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