linux/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2013 Red Hat
   3 * Author: Rob Clark <robdclark@gmail.com>
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms of the GNU General Public License version 2 as published by
   7 * the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful, but WITHOUT
  10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  12 * more details.
  13 *
  14 * You should have received a copy of the GNU General Public License along with
  15 * this program.  If not, see <http://www.gnu.org/licenses/>.
  16 */
  17
  18#include "hdmi.h"
  19
  20struct hdmi_bridge {
  21        struct drm_bridge base;
  22        struct hdmi *hdmi;
  23};
  24#define to_hdmi_bridge(x) container_of(x, struct hdmi_bridge, base)
  25
  26void msm_hdmi_bridge_destroy(struct drm_bridge *bridge)
  27{
  28}
  29
  30static void msm_hdmi_power_on(struct drm_bridge *bridge)
  31{
  32        struct drm_device *dev = bridge->dev;
  33        struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
  34        struct hdmi *hdmi = hdmi_bridge->hdmi;
  35        const struct hdmi_platform_config *config = hdmi->config;
  36        int i, ret;
  37
  38        pm_runtime_get_sync(&hdmi->pdev->dev);
  39
  40        for (i = 0; i < config->pwr_reg_cnt; i++) {
  41                ret = regulator_enable(hdmi->pwr_regs[i]);
  42                if (ret) {
  43                        dev_err(dev->dev, "failed to enable pwr regulator: %s (%d)\n",
  44                                        config->pwr_reg_names[i], ret);
  45                }
  46        }
  47
  48        if (config->pwr_clk_cnt > 0) {
  49                DBG("pixclock: %lu", hdmi->pixclock);
  50                ret = clk_set_rate(hdmi->pwr_clks[0], hdmi->pixclock);
  51                if (ret) {
  52                        dev_err(dev->dev, "failed to set pixel clk: %s (%d)\n",
  53                                        config->pwr_clk_names[0], ret);
  54                }
  55        }
  56
  57        for (i = 0; i < config->pwr_clk_cnt; i++) {
  58                ret = clk_prepare_enable(hdmi->pwr_clks[i]);
  59                if (ret) {
  60                        dev_err(dev->dev, "failed to enable pwr clk: %s (%d)\n",
  61                                        config->pwr_clk_names[i], ret);
  62                }
  63        }
  64}
  65
  66static void power_off(struct drm_bridge *bridge)
  67{
  68        struct drm_device *dev = bridge->dev;
  69        struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
  70        struct hdmi *hdmi = hdmi_bridge->hdmi;
  71        const struct hdmi_platform_config *config = hdmi->config;
  72        int i, ret;
  73
  74        /* TODO do we need to wait for final vblank somewhere before
  75         * cutting the clocks?
  76         */
  77        mdelay(16 + 4);
  78
  79        for (i = 0; i < config->pwr_clk_cnt; i++)
  80                clk_disable_unprepare(hdmi->pwr_clks[i]);
  81
  82        for (i = 0; i < config->pwr_reg_cnt; i++) {
  83                ret = regulator_disable(hdmi->pwr_regs[i]);
  84                if (ret) {
  85                        dev_err(dev->dev, "failed to disable pwr regulator: %s (%d)\n",
  86                                        config->pwr_reg_names[i], ret);
  87                }
  88        }
  89
  90        pm_runtime_put_autosuspend(&hdmi->pdev->dev);
  91}
  92
  93#define AVI_IFRAME_LINE_NUMBER 1
  94
  95static void msm_hdmi_config_avi_infoframe(struct hdmi *hdmi)
  96{
  97        struct drm_crtc *crtc = hdmi->encoder->crtc;
  98        const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
  99        union hdmi_infoframe frame;
 100        u8 buffer[HDMI_INFOFRAME_SIZE(AVI)];
 101        u32 val;
 102        int len;
 103
 104        drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode, false);
 105
 106        len = hdmi_infoframe_pack(&frame, buffer, sizeof(buffer));
 107        if (len < 0) {
 108                dev_err(&hdmi->pdev->dev,
 109                        "failed to configure avi infoframe\n");
 110                return;
 111        }
 112
 113        /*
 114         * the AVI_INFOx registers don't map exactly to how the AVI infoframes
 115         * are packed according to the spec. The checksum from the header is
 116         * written to the LSB byte of AVI_INFO0 and the version is written to
 117         * the third byte from the LSB of AVI_INFO3
 118         */
 119        hdmi_write(hdmi, REG_HDMI_AVI_INFO(0),
 120                   buffer[3] |
 121                   buffer[4] << 8 |
 122                   buffer[5] << 16 |
 123                   buffer[6] << 24);
 124
 125        hdmi_write(hdmi, REG_HDMI_AVI_INFO(1),
 126                   buffer[7] |
 127                   buffer[8] << 8 |
 128                   buffer[9] << 16 |
 129                   buffer[10] << 24);
 130
 131        hdmi_write(hdmi, REG_HDMI_AVI_INFO(2),
 132                   buffer[11] |
 133                   buffer[12] << 8 |
 134                   buffer[13] << 16 |
 135                   buffer[14] << 24);
 136
 137        hdmi_write(hdmi, REG_HDMI_AVI_INFO(3),
 138                   buffer[15] |
 139                   buffer[16] << 8 |
 140                   buffer[1] << 24);
 141
 142        hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0,
 143                   HDMI_INFOFRAME_CTRL0_AVI_SEND |
 144                   HDMI_INFOFRAME_CTRL0_AVI_CONT);
 145
 146        val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1);
 147        val &= ~HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE__MASK;
 148        val |= HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE(AVI_IFRAME_LINE_NUMBER);
 149        hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val);
 150}
 151
 152static void msm_hdmi_bridge_pre_enable(struct drm_bridge *bridge)
 153{
 154        struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
 155        struct hdmi *hdmi = hdmi_bridge->hdmi;
 156        struct hdmi_phy *phy = hdmi->phy;
 157
 158        DBG("power up");
 159
 160        if (!hdmi->power_on) {
 161                msm_hdmi_phy_resource_enable(phy);
 162                msm_hdmi_power_on(bridge);
 163                hdmi->power_on = true;
 164                if (hdmi->hdmi_mode) {
 165                        msm_hdmi_config_avi_infoframe(hdmi);
 166                        msm_hdmi_audio_update(hdmi);
 167                }
 168        }
 169
 170        msm_hdmi_phy_powerup(phy, hdmi->pixclock);
 171
 172        msm_hdmi_set_mode(hdmi, true);
 173
 174        if (hdmi->hdcp_ctrl)
 175                msm_hdmi_hdcp_on(hdmi->hdcp_ctrl);
 176}
 177
 178static void msm_hdmi_bridge_enable(struct drm_bridge *bridge)
 179{
 180}
 181
 182static void msm_hdmi_bridge_disable(struct drm_bridge *bridge)
 183{
 184}
 185
 186static void msm_hdmi_bridge_post_disable(struct drm_bridge *bridge)
 187{
 188        struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
 189        struct hdmi *hdmi = hdmi_bridge->hdmi;
 190        struct hdmi_phy *phy = hdmi->phy;
 191
 192        if (hdmi->hdcp_ctrl)
 193                msm_hdmi_hdcp_off(hdmi->hdcp_ctrl);
 194
 195        DBG("power down");
 196        msm_hdmi_set_mode(hdmi, false);
 197
 198        msm_hdmi_phy_powerdown(phy);
 199
 200        if (hdmi->power_on) {
 201                power_off(bridge);
 202                hdmi->power_on = false;
 203                if (hdmi->hdmi_mode)
 204                        msm_hdmi_audio_update(hdmi);
 205                msm_hdmi_phy_resource_disable(phy);
 206        }
 207}
 208
 209static void msm_hdmi_bridge_mode_set(struct drm_bridge *bridge,
 210                 struct drm_display_mode *mode,
 211                 struct drm_display_mode *adjusted_mode)
 212{
 213        struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
 214        struct hdmi *hdmi = hdmi_bridge->hdmi;
 215        int hstart, hend, vstart, vend;
 216        uint32_t frame_ctrl;
 217
 218        mode = adjusted_mode;
 219
 220        hdmi->pixclock = mode->clock * 1000;
 221
 222        hstart = mode->htotal - mode->hsync_start;
 223        hend   = mode->htotal - mode->hsync_start + mode->hdisplay;
 224
 225        vstart = mode->vtotal - mode->vsync_start - 1;
 226        vend   = mode->vtotal - mode->vsync_start + mode->vdisplay - 1;
 227
 228        DBG("htotal=%d, vtotal=%d, hstart=%d, hend=%d, vstart=%d, vend=%d",
 229                        mode->htotal, mode->vtotal, hstart, hend, vstart, vend);
 230
 231        hdmi_write(hdmi, REG_HDMI_TOTAL,
 232                        HDMI_TOTAL_H_TOTAL(mode->htotal - 1) |
 233                        HDMI_TOTAL_V_TOTAL(mode->vtotal - 1));
 234
 235        hdmi_write(hdmi, REG_HDMI_ACTIVE_HSYNC,
 236                        HDMI_ACTIVE_HSYNC_START(hstart) |
 237                        HDMI_ACTIVE_HSYNC_END(hend));
 238        hdmi_write(hdmi, REG_HDMI_ACTIVE_VSYNC,
 239                        HDMI_ACTIVE_VSYNC_START(vstart) |
 240                        HDMI_ACTIVE_VSYNC_END(vend));
 241
 242        if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
 243                hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,
 244                                HDMI_VSYNC_TOTAL_F2_V_TOTAL(mode->vtotal));
 245                hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,
 246                                HDMI_VSYNC_ACTIVE_F2_START(vstart + 1) |
 247                                HDMI_VSYNC_ACTIVE_F2_END(vend + 1));
 248        } else {
 249                hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,
 250                                HDMI_VSYNC_TOTAL_F2_V_TOTAL(0));
 251                hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,
 252                                HDMI_VSYNC_ACTIVE_F2_START(0) |
 253                                HDMI_VSYNC_ACTIVE_F2_END(0));
 254        }
 255
 256        frame_ctrl = 0;
 257        if (mode->flags & DRM_MODE_FLAG_NHSYNC)
 258                frame_ctrl |= HDMI_FRAME_CTRL_HSYNC_LOW;
 259        if (mode->flags & DRM_MODE_FLAG_NVSYNC)
 260                frame_ctrl |= HDMI_FRAME_CTRL_VSYNC_LOW;
 261        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
 262                frame_ctrl |= HDMI_FRAME_CTRL_INTERLACED_EN;
 263        DBG("frame_ctrl=%08x", frame_ctrl);
 264        hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl);
 265
 266        if (hdmi->hdmi_mode)
 267                msm_hdmi_audio_update(hdmi);
 268}
 269
 270static const struct drm_bridge_funcs msm_hdmi_bridge_funcs = {
 271                .pre_enable = msm_hdmi_bridge_pre_enable,
 272                .enable = msm_hdmi_bridge_enable,
 273                .disable = msm_hdmi_bridge_disable,
 274                .post_disable = msm_hdmi_bridge_post_disable,
 275                .mode_set = msm_hdmi_bridge_mode_set,
 276};
 277
 278
 279/* initialize bridge */
 280struct drm_bridge *msm_hdmi_bridge_init(struct hdmi *hdmi)
 281{
 282        struct drm_bridge *bridge = NULL;
 283        struct hdmi_bridge *hdmi_bridge;
 284        int ret;
 285
 286        hdmi_bridge = devm_kzalloc(hdmi->dev->dev,
 287                        sizeof(*hdmi_bridge), GFP_KERNEL);
 288        if (!hdmi_bridge) {
 289                ret = -ENOMEM;
 290                goto fail;
 291        }
 292
 293        hdmi_bridge->hdmi = hdmi;
 294
 295        bridge = &hdmi_bridge->base;
 296        bridge->funcs = &msm_hdmi_bridge_funcs;
 297
 298        ret = drm_bridge_attach(hdmi->encoder, bridge, NULL);
 299        if (ret)
 300                goto fail;
 301
 302        return bridge;
 303
 304fail:
 305        if (bridge)
 306                msm_hdmi_bridge_destroy(bridge);
 307
 308        return ERR_PTR(ret);
 309}
 310