355 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013 Red Hat4 * Author: Rob Clark <robdclark@gmail.com>5 */6 7#include <linux/delay.h>8#include <drm/drm_bridge_connector.h>9#include <drm/drm_edid.h>10 11#include "msm_kms.h"12#include "hdmi.h"13 14static void msm_hdmi_power_on(struct drm_bridge *bridge)15{16 struct drm_device *dev = bridge->dev;17 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);18 struct hdmi *hdmi = hdmi_bridge->hdmi;19 const struct hdmi_platform_config *config = hdmi->config;20 int i, ret;21 22 pm_runtime_get_sync(&hdmi->pdev->dev);23 24 ret = regulator_bulk_enable(config->pwr_reg_cnt, hdmi->pwr_regs);25 if (ret)26 DRM_DEV_ERROR(dev->dev, "failed to enable pwr regulator: %d\n", ret);27 28 if (config->pwr_clk_cnt > 0) {29 DBG("pixclock: %lu", hdmi->pixclock);30 ret = clk_set_rate(hdmi->pwr_clks[0], hdmi->pixclock);31 if (ret) {32 DRM_DEV_ERROR(dev->dev, "failed to set pixel clk: %s (%d)\n",33 config->pwr_clk_names[0], ret);34 }35 }36 37 for (i = 0; i < config->pwr_clk_cnt; i++) {38 ret = clk_prepare_enable(hdmi->pwr_clks[i]);39 if (ret) {40 DRM_DEV_ERROR(dev->dev, "failed to enable pwr clk: %s (%d)\n",41 config->pwr_clk_names[i], ret);42 }43 }44}45 46static void power_off(struct drm_bridge *bridge)47{48 struct drm_device *dev = bridge->dev;49 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);50 struct hdmi *hdmi = hdmi_bridge->hdmi;51 const struct hdmi_platform_config *config = hdmi->config;52 int i, ret;53 54 /* TODO do we need to wait for final vblank somewhere before55 * cutting the clocks?56 */57 mdelay(16 + 4);58 59 for (i = 0; i < config->pwr_clk_cnt; i++)60 clk_disable_unprepare(hdmi->pwr_clks[i]);61 62 ret = regulator_bulk_disable(config->pwr_reg_cnt, hdmi->pwr_regs);63 if (ret)64 DRM_DEV_ERROR(dev->dev, "failed to disable pwr regulator: %d\n", ret);65 66 pm_runtime_put(&hdmi->pdev->dev);67}68 69#define AVI_IFRAME_LINE_NUMBER 170 71static void msm_hdmi_config_avi_infoframe(struct hdmi *hdmi)72{73 struct drm_crtc *crtc = hdmi->encoder->crtc;74 const struct drm_display_mode *mode = &crtc->state->adjusted_mode;75 union hdmi_infoframe frame;76 u8 buffer[HDMI_INFOFRAME_SIZE(AVI)];77 u32 val;78 int len;79 80 drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,81 hdmi->connector, mode);82 83 len = hdmi_infoframe_pack(&frame, buffer, sizeof(buffer));84 if (len < 0) {85 DRM_DEV_ERROR(&hdmi->pdev->dev,86 "failed to configure avi infoframe\n");87 return;88 }89 90 /*91 * the AVI_INFOx registers don't map exactly to how the AVI infoframes92 * are packed according to the spec. The checksum from the header is93 * written to the LSB byte of AVI_INFO0 and the version is written to94 * the third byte from the LSB of AVI_INFO395 */96 hdmi_write(hdmi, REG_HDMI_AVI_INFO(0),97 buffer[3] |98 buffer[4] << 8 |99 buffer[5] << 16 |100 buffer[6] << 24);101 102 hdmi_write(hdmi, REG_HDMI_AVI_INFO(1),103 buffer[7] |104 buffer[8] << 8 |105 buffer[9] << 16 |106 buffer[10] << 24);107 108 hdmi_write(hdmi, REG_HDMI_AVI_INFO(2),109 buffer[11] |110 buffer[12] << 8 |111 buffer[13] << 16 |112 buffer[14] << 24);113 114 hdmi_write(hdmi, REG_HDMI_AVI_INFO(3),115 buffer[15] |116 buffer[16] << 8 |117 buffer[1] << 24);118 119 hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0,120 HDMI_INFOFRAME_CTRL0_AVI_SEND |121 HDMI_INFOFRAME_CTRL0_AVI_CONT);122 123 val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1);124 val &= ~HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE__MASK;125 val |= HDMI_INFOFRAME_CTRL1_AVI_INFO_LINE(AVI_IFRAME_LINE_NUMBER);126 hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, val);127}128 129static void msm_hdmi_bridge_pre_enable(struct drm_bridge *bridge)130{131 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);132 struct hdmi *hdmi = hdmi_bridge->hdmi;133 struct hdmi_phy *phy = hdmi->phy;134 135 DBG("power up");136 137 if (!hdmi->power_on) {138 msm_hdmi_phy_resource_enable(phy);139 msm_hdmi_power_on(bridge);140 hdmi->power_on = true;141 if (hdmi->hdmi_mode) {142 msm_hdmi_config_avi_infoframe(hdmi);143 msm_hdmi_audio_update(hdmi);144 }145 }146 147 msm_hdmi_phy_powerup(phy, hdmi->pixclock);148 149 msm_hdmi_set_mode(hdmi, true);150 151 if (hdmi->hdcp_ctrl)152 msm_hdmi_hdcp_on(hdmi->hdcp_ctrl);153}154 155static void msm_hdmi_bridge_post_disable(struct drm_bridge *bridge)156{157 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);158 struct hdmi *hdmi = hdmi_bridge->hdmi;159 struct hdmi_phy *phy = hdmi->phy;160 161 if (hdmi->hdcp_ctrl)162 msm_hdmi_hdcp_off(hdmi->hdcp_ctrl);163 164 DBG("power down");165 msm_hdmi_set_mode(hdmi, false);166 167 msm_hdmi_phy_powerdown(phy);168 169 if (hdmi->power_on) {170 power_off(bridge);171 hdmi->power_on = false;172 if (hdmi->hdmi_mode)173 msm_hdmi_audio_update(hdmi);174 msm_hdmi_phy_resource_disable(phy);175 }176}177 178static void msm_hdmi_bridge_mode_set(struct drm_bridge *bridge,179 const struct drm_display_mode *mode,180 const struct drm_display_mode *adjusted_mode)181{182 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);183 struct hdmi *hdmi = hdmi_bridge->hdmi;184 int hstart, hend, vstart, vend;185 uint32_t frame_ctrl;186 187 mode = adjusted_mode;188 189 hdmi->pixclock = mode->clock * 1000;190 191 hstart = mode->htotal - mode->hsync_start;192 hend = mode->htotal - mode->hsync_start + mode->hdisplay;193 194 vstart = mode->vtotal - mode->vsync_start - 1;195 vend = mode->vtotal - mode->vsync_start + mode->vdisplay - 1;196 197 DBG("htotal=%d, vtotal=%d, hstart=%d, hend=%d, vstart=%d, vend=%d",198 mode->htotal, mode->vtotal, hstart, hend, vstart, vend);199 200 hdmi_write(hdmi, REG_HDMI_TOTAL,201 HDMI_TOTAL_H_TOTAL(mode->htotal - 1) |202 HDMI_TOTAL_V_TOTAL(mode->vtotal - 1));203 204 hdmi_write(hdmi, REG_HDMI_ACTIVE_HSYNC,205 HDMI_ACTIVE_HSYNC_START(hstart) |206 HDMI_ACTIVE_HSYNC_END(hend));207 hdmi_write(hdmi, REG_HDMI_ACTIVE_VSYNC,208 HDMI_ACTIVE_VSYNC_START(vstart) |209 HDMI_ACTIVE_VSYNC_END(vend));210 211 if (mode->flags & DRM_MODE_FLAG_INTERLACE) {212 hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,213 HDMI_VSYNC_TOTAL_F2_V_TOTAL(mode->vtotal));214 hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,215 HDMI_VSYNC_ACTIVE_F2_START(vstart + 1) |216 HDMI_VSYNC_ACTIVE_F2_END(vend + 1));217 } else {218 hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,219 HDMI_VSYNC_TOTAL_F2_V_TOTAL(0));220 hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,221 HDMI_VSYNC_ACTIVE_F2_START(0) |222 HDMI_VSYNC_ACTIVE_F2_END(0));223 }224 225 frame_ctrl = 0;226 if (mode->flags & DRM_MODE_FLAG_NHSYNC)227 frame_ctrl |= HDMI_FRAME_CTRL_HSYNC_LOW;228 if (mode->flags & DRM_MODE_FLAG_NVSYNC)229 frame_ctrl |= HDMI_FRAME_CTRL_VSYNC_LOW;230 if (mode->flags & DRM_MODE_FLAG_INTERLACE)231 frame_ctrl |= HDMI_FRAME_CTRL_INTERLACED_EN;232 DBG("frame_ctrl=%08x", frame_ctrl);233 hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl);234 235 if (hdmi->hdmi_mode)236 msm_hdmi_audio_update(hdmi);237}238 239static const struct drm_edid *msm_hdmi_bridge_edid_read(struct drm_bridge *bridge,240 struct drm_connector *connector)241{242 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);243 struct hdmi *hdmi = hdmi_bridge->hdmi;244 const struct drm_edid *drm_edid;245 uint32_t hdmi_ctrl;246 247 hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL);248 hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE);249 250 drm_edid = drm_edid_read_ddc(connector, hdmi->i2c);251 252 hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl);253 254 if (drm_edid) {255 /*256 * FIXME: This should use connector->display_info.is_hdmi from a257 * path that has read the EDID and called258 * drm_edid_connector_update().259 */260 const struct edid *edid = drm_edid_raw(drm_edid);261 262 hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid);263 }264 265 return drm_edid;266}267 268static enum drm_mode_status msm_hdmi_bridge_mode_valid(struct drm_bridge *bridge,269 const struct drm_display_info *info,270 const struct drm_display_mode *mode)271{272 struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);273 struct hdmi *hdmi = hdmi_bridge->hdmi;274 const struct hdmi_platform_config *config = hdmi->config;275 struct msm_drm_private *priv = bridge->dev->dev_private;276 struct msm_kms *kms = priv->kms;277 long actual, requested;278 279 requested = 1000 * mode->clock;280 281 /* for mdp5/apq8074, we manage our own pixel clk (as opposed to282 * mdp4/dtv stuff where pixel clk is assigned to mdp/encoder283 * instead):284 */285 if (kms->funcs->round_pixclk)286 actual = kms->funcs->round_pixclk(kms,287 requested, hdmi_bridge->hdmi->encoder);288 else if (config->pwr_clk_cnt > 0)289 actual = clk_round_rate(hdmi->pwr_clks[0], requested);290 else291 actual = requested;292 293 DBG("requested=%ld, actual=%ld", requested, actual);294 295 if (actual != requested)296 return MODE_CLOCK_RANGE;297 298 return 0;299}300 301static const struct drm_bridge_funcs msm_hdmi_bridge_funcs = {302 .pre_enable = msm_hdmi_bridge_pre_enable,303 .post_disable = msm_hdmi_bridge_post_disable,304 .mode_set = msm_hdmi_bridge_mode_set,305 .mode_valid = msm_hdmi_bridge_mode_valid,306 .edid_read = msm_hdmi_bridge_edid_read,307 .detect = msm_hdmi_bridge_detect,308};309 310static void311msm_hdmi_hotplug_work(struct work_struct *work)312{313 struct hdmi_bridge *hdmi_bridge =314 container_of(work, struct hdmi_bridge, hpd_work);315 struct drm_bridge *bridge = &hdmi_bridge->base;316 317 drm_bridge_hpd_notify(bridge, drm_bridge_detect(bridge));318}319 320/* initialize bridge */321int msm_hdmi_bridge_init(struct hdmi *hdmi)322{323 struct drm_bridge *bridge = NULL;324 struct hdmi_bridge *hdmi_bridge;325 int ret;326 327 hdmi_bridge = devm_kzalloc(hdmi->dev->dev,328 sizeof(*hdmi_bridge), GFP_KERNEL);329 if (!hdmi_bridge)330 return -ENOMEM;331 332 hdmi_bridge->hdmi = hdmi;333 INIT_WORK(&hdmi_bridge->hpd_work, msm_hdmi_hotplug_work);334 335 bridge = &hdmi_bridge->base;336 bridge->funcs = &msm_hdmi_bridge_funcs;337 bridge->ddc = hdmi->i2c;338 bridge->type = DRM_MODE_CONNECTOR_HDMIA;339 bridge->ops = DRM_BRIDGE_OP_HPD |340 DRM_BRIDGE_OP_DETECT |341 DRM_BRIDGE_OP_EDID;342 343 ret = devm_drm_bridge_add(hdmi->dev->dev, bridge);344 if (ret)345 return ret;346 347 ret = drm_bridge_attach(hdmi->encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);348 if (ret)349 return ret;350 351 hdmi->bridge = bridge;352 353 return 0;354}355