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        for (i = 0; i < config->pwr_reg_cnt; i++) {
  39                ret = regulator_enable(hdmi->pwr_regs[i]);
  40                if (ret) {
  41                        dev_err(dev->dev, "failed to enable pwr regulator: %s (%d)\n",
  42                                        config->pwr_reg_names[i], ret);
  43                }
  44        }
  45
  46        if (config->pwr_clk_cnt > 0) {
  47                DBG("pixclock: %lu", hdmi->pixclock);
  48                ret = clk_set_rate(hdmi->pwr_clks[0], hdmi->pixclock);
  49                if (ret) {
  50                        dev_err(dev->dev, "failed to set pixel clk: %s (%d)\n",
  51                                        config->pwr_clk_names[0], ret);
  52                }
  53        }
  54
  55        for (i = 0; i < config->pwr_clk_cnt; i++) {
  56                ret = clk_prepare_enable(hdmi->pwr_clks[i]);
  57                if (ret) {
  58                        dev_err(dev->dev, "failed to enable pwr clk: %s (%d)\n",
  59                                        config->pwr_clk_names[i], ret);
  60                }
  61        }
  62}
  63
  64static void power_off(struct drm_bridge *bridge)
  65{
  66        struct drm_device *dev = bridge->dev;
  67        struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
  68        struct hdmi *hdmi = hdmi_bridge->hdmi;
  69        const struct hdmi_platform_config *config = hdmi->config;
  70        int i, ret;
  71
  72        /* TODO do we need to wait for final vblank somewhere before
  73         * cutting the clocks?
  74         */
  75        mdelay(16 + 4);
  76
  77        for (i = 0; i < config->pwr_clk_cnt; i++)
  78                clk_disable_unprepare(hdmi->pwr_clks[i]);
  79
  80        for (i = 0; i < config->pwr_reg_cnt; i++) {
  81                ret = regulator_disable(hdmi->pwr_regs[i]);
  82                if (ret) {
  83                        dev_err(dev->dev, "failed to disable pwr regulator: %s (%d)\n",
  84                                        config->pwr_reg_names[i], ret);
  85                }
  86        }
  87}
  88
  89static void msm_hdmi_bridge_pre_enable(struct drm_bridge *bridge)
  90{
  91        struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
  92        struct hdmi *hdmi = hdmi_bridge->hdmi;
  93        struct hdmi_phy *phy = hdmi->phy;
  94
  95        DBG("power up");
  96
  97        if (!hdmi->power_on) {
  98                msm_hdmi_phy_resource_enable(phy);
  99                msm_hdmi_power_on(bridge);
 100                hdmi->power_on = true;
 101                msm_hdmi_audio_update(hdmi);
 102        }
 103
 104        msm_hdmi_phy_powerup(phy, hdmi->pixclock);
 105
 106        msm_hdmi_set_mode(hdmi, true);
 107
 108        if (hdmi->hdcp_ctrl)
 109                msm_hdmi_hdcp_on(hdmi->hdcp_ctrl);
 110}
 111
 112static void msm_hdmi_bridge_enable(struct drm_bridge *bridge)
 113{
 114}
 115
 116static void msm_hdmi_bridge_disable(struct drm_bridge *bridge)
 117{
 118}
 119
 120static void msm_hdmi_bridge_post_disable(struct drm_bridge *bridge)
 121{
 122        struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
 123        struct hdmi *hdmi = hdmi_bridge->hdmi;
 124        struct hdmi_phy *phy = hdmi->phy;
 125
 126        if (hdmi->hdcp_ctrl)
 127                msm_hdmi_hdcp_off(hdmi->hdcp_ctrl);
 128
 129        DBG("power down");
 130        msm_hdmi_set_mode(hdmi, false);
 131
 132        msm_hdmi_phy_powerdown(phy);
 133
 134        if (hdmi->power_on) {
 135                power_off(bridge);
 136                hdmi->power_on = false;
 137                msm_hdmi_audio_update(hdmi);
 138                msm_hdmi_phy_resource_disable(phy);
 139        }
 140}
 141
 142static void msm_hdmi_bridge_mode_set(struct drm_bridge *bridge,
 143                 struct drm_display_mode *mode,
 144                 struct drm_display_mode *adjusted_mode)
 145{
 146        struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
 147        struct hdmi *hdmi = hdmi_bridge->hdmi;
 148        int hstart, hend, vstart, vend;
 149        uint32_t frame_ctrl;
 150
 151        mode = adjusted_mode;
 152
 153        hdmi->pixclock = mode->clock * 1000;
 154
 155        hstart = mode->htotal - mode->hsync_start;
 156        hend   = mode->htotal - mode->hsync_start + mode->hdisplay;
 157
 158        vstart = mode->vtotal - mode->vsync_start - 1;
 159        vend   = mode->vtotal - mode->vsync_start + mode->vdisplay - 1;
 160
 161        DBG("htotal=%d, vtotal=%d, hstart=%d, hend=%d, vstart=%d, vend=%d",
 162                        mode->htotal, mode->vtotal, hstart, hend, vstart, vend);
 163
 164        hdmi_write(hdmi, REG_HDMI_TOTAL,
 165                        HDMI_TOTAL_H_TOTAL(mode->htotal - 1) |
 166                        HDMI_TOTAL_V_TOTAL(mode->vtotal - 1));
 167
 168        hdmi_write(hdmi, REG_HDMI_ACTIVE_HSYNC,
 169                        HDMI_ACTIVE_HSYNC_START(hstart) |
 170                        HDMI_ACTIVE_HSYNC_END(hend));
 171        hdmi_write(hdmi, REG_HDMI_ACTIVE_VSYNC,
 172                        HDMI_ACTIVE_VSYNC_START(vstart) |
 173                        HDMI_ACTIVE_VSYNC_END(vend));
 174
 175        if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
 176                hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,
 177                                HDMI_VSYNC_TOTAL_F2_V_TOTAL(mode->vtotal));
 178                hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,
 179                                HDMI_VSYNC_ACTIVE_F2_START(vstart + 1) |
 180                                HDMI_VSYNC_ACTIVE_F2_END(vend + 1));
 181        } else {
 182                hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,
 183                                HDMI_VSYNC_TOTAL_F2_V_TOTAL(0));
 184                hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,
 185                                HDMI_VSYNC_ACTIVE_F2_START(0) |
 186                                HDMI_VSYNC_ACTIVE_F2_END(0));
 187        }
 188
 189        frame_ctrl = 0;
 190        if (mode->flags & DRM_MODE_FLAG_NHSYNC)
 191                frame_ctrl |= HDMI_FRAME_CTRL_HSYNC_LOW;
 192        if (mode->flags & DRM_MODE_FLAG_NVSYNC)
 193                frame_ctrl |= HDMI_FRAME_CTRL_VSYNC_LOW;
 194        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
 195                frame_ctrl |= HDMI_FRAME_CTRL_INTERLACED_EN;
 196        DBG("frame_ctrl=%08x", frame_ctrl);
 197        hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl);
 198
 199        msm_hdmi_audio_update(hdmi);
 200}
 201
 202static const struct drm_bridge_funcs msm_hdmi_bridge_funcs = {
 203                .pre_enable = msm_hdmi_bridge_pre_enable,
 204                .enable = msm_hdmi_bridge_enable,
 205                .disable = msm_hdmi_bridge_disable,
 206                .post_disable = msm_hdmi_bridge_post_disable,
 207                .mode_set = msm_hdmi_bridge_mode_set,
 208};
 209
 210
 211/* initialize bridge */
 212struct drm_bridge *msm_hdmi_bridge_init(struct hdmi *hdmi)
 213{
 214        struct drm_bridge *bridge = NULL;
 215        struct hdmi_bridge *hdmi_bridge;
 216        int ret;
 217
 218        hdmi_bridge = devm_kzalloc(hdmi->dev->dev,
 219                        sizeof(*hdmi_bridge), GFP_KERNEL);
 220        if (!hdmi_bridge) {
 221                ret = -ENOMEM;
 222                goto fail;
 223        }
 224
 225        hdmi_bridge->hdmi = hdmi;
 226
 227        bridge = &hdmi_bridge->base;
 228        bridge->funcs = &msm_hdmi_bridge_funcs;
 229
 230        ret = drm_bridge_attach(hdmi->dev, bridge);
 231        if (ret)
 232                goto fail;
 233
 234        return bridge;
 235
 236fail:
 237        if (bridge)
 238                msm_hdmi_bridge_destroy(bridge);
 239
 240        return ERR_PTR(ret);
 241}
 242