brintos

brintos / linux-shallow public Read only

0
0
Text · 9.5 KiB · 4a5b511 Raw
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