xref: /linux/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c (revision 52338415)
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 
11 struct 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 
17 void msm_hdmi_bridge_destroy(struct drm_bridge *bridge)
18 {
19 }
20 
21 static 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 
57 static 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 
86 static 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 
144 static 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 
170 static void msm_hdmi_bridge_enable(struct drm_bridge *bridge)
171 {
172 }
173 
174 static void msm_hdmi_bridge_disable(struct drm_bridge *bridge)
175 {
176 }
177 
178 static 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 
201 static 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 
262 static 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 */
272 struct 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);
291 	if (ret)
292 		goto fail;
293 
294 	return bridge;
295 
296 fail:
297 	if (bridge)
298 		msm_hdmi_bridge_destroy(bridge);
299 
300 	return ERR_PTR(ret);
301 }
302