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