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