]> Git Repo - linux.git/blob - drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
Merge tag 'for-linus-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml
[linux.git] / drivers / gpu / drm / msm / hdmi / hdmi_bridge.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2013 Red Hat
4  * Author: Rob Clark <[email protected]>
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, 0);
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 }
This page took 0.052223 seconds and 4 git commands to generate.