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