1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd |
4 | * Zheng Yang <zhengyang@rock-chips.com> |
5 | */ |
6 | |
7 | #include <drm/drm_atomic.h> |
8 | #include <drm/drm_edid.h> |
9 | #include <drm/drm_of.h> |
10 | #include <drm/drm_probe_helper.h> |
11 | #include <drm/drm_simple_kms_helper.h> |
12 | |
13 | #include <linux/clk.h> |
14 | #include <linux/mfd/syscon.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/regmap.h> |
17 | |
18 | #include "rk3066_hdmi.h" |
19 | |
20 | #include "rockchip_drm_drv.h" |
21 | |
22 | #define DEFAULT_PLLA_RATE 30000000 |
23 | |
24 | struct hdmi_data_info { |
25 | int vic; /* The CEA Video ID (VIC) of the current drm display mode. */ |
26 | unsigned int enc_out_format; |
27 | unsigned int colorimetry; |
28 | }; |
29 | |
30 | struct rk3066_hdmi_i2c { |
31 | struct i2c_adapter adap; |
32 | |
33 | u8 ddc_addr; |
34 | u8 segment_addr; |
35 | u8 stat; |
36 | |
37 | struct mutex i2c_lock; /* For i2c operation. */ |
38 | struct completion cmpltn; |
39 | }; |
40 | |
41 | struct rk3066_hdmi { |
42 | struct device *dev; |
43 | struct drm_device *drm_dev; |
44 | struct regmap *grf_regmap; |
45 | int irq; |
46 | struct clk *hclk; |
47 | void __iomem *regs; |
48 | |
49 | struct drm_connector connector; |
50 | struct rockchip_encoder encoder; |
51 | |
52 | struct rk3066_hdmi_i2c *i2c; |
53 | struct i2c_adapter *ddc; |
54 | |
55 | unsigned int tmdsclk; |
56 | |
57 | struct hdmi_data_info hdmi_data; |
58 | }; |
59 | |
60 | static struct rk3066_hdmi *encoder_to_rk3066_hdmi(struct drm_encoder *encoder) |
61 | { |
62 | struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); |
63 | |
64 | return container_of(rkencoder, struct rk3066_hdmi, encoder); |
65 | } |
66 | |
67 | static struct rk3066_hdmi *connector_to_rk3066_hdmi(struct drm_connector *connector) |
68 | { |
69 | return container_of(connector, struct rk3066_hdmi, connector); |
70 | } |
71 | |
72 | static inline u8 hdmi_readb(struct rk3066_hdmi *hdmi, u16 offset) |
73 | { |
74 | return readl_relaxed(hdmi->regs + offset); |
75 | } |
76 | |
77 | static inline void hdmi_writeb(struct rk3066_hdmi *hdmi, u16 offset, u32 val) |
78 | { |
79 | writel_relaxed(val, hdmi->regs + offset); |
80 | } |
81 | |
82 | static inline void hdmi_modb(struct rk3066_hdmi *hdmi, u16 offset, |
83 | u32 msk, u32 val) |
84 | { |
85 | u8 temp = hdmi_readb(hdmi, offset) & ~msk; |
86 | |
87 | temp |= val & msk; |
88 | hdmi_writeb(hdmi, offset, val: temp); |
89 | } |
90 | |
91 | static void rk3066_hdmi_i2c_init(struct rk3066_hdmi *hdmi) |
92 | { |
93 | int ddc_bus_freq; |
94 | |
95 | ddc_bus_freq = (hdmi->tmdsclk >> 2) / HDMI_SCL_RATE; |
96 | |
97 | hdmi_writeb(hdmi, HDMI_DDC_BUS_FREQ_L, val: ddc_bus_freq & 0xFF); |
98 | hdmi_writeb(hdmi, HDMI_DDC_BUS_FREQ_H, val: (ddc_bus_freq >> 8) & 0xFF); |
99 | |
100 | /* Clear the EDID interrupt flag and mute the interrupt. */ |
101 | hdmi_modb(hdmi, HDMI_INTR_MASK1, msk: HDMI_INTR_EDID_MASK, val: 0); |
102 | hdmi_writeb(hdmi, HDMI_INTR_STATUS1, val: HDMI_INTR_EDID_MASK); |
103 | } |
104 | |
105 | static inline u8 rk3066_hdmi_get_power_mode(struct rk3066_hdmi *hdmi) |
106 | { |
107 | return hdmi_readb(hdmi, HDMI_SYS_CTRL) & HDMI_SYS_POWER_MODE_MASK; |
108 | } |
109 | |
110 | static void rk3066_hdmi_set_power_mode(struct rk3066_hdmi *hdmi, int mode) |
111 | { |
112 | u8 current_mode, next_mode; |
113 | u8 i = 0; |
114 | |
115 | current_mode = rk3066_hdmi_get_power_mode(hdmi); |
116 | |
117 | DRM_DEV_DEBUG(hdmi->dev, "mode :%d\n" , mode); |
118 | DRM_DEV_DEBUG(hdmi->dev, "current_mode :%d\n" , current_mode); |
119 | |
120 | if (current_mode == mode) |
121 | return; |
122 | |
123 | do { |
124 | if (current_mode > mode) { |
125 | next_mode = current_mode / 2; |
126 | } else { |
127 | if (current_mode < HDMI_SYS_POWER_MODE_A) |
128 | next_mode = HDMI_SYS_POWER_MODE_A; |
129 | else |
130 | next_mode = current_mode * 2; |
131 | } |
132 | |
133 | DRM_DEV_DEBUG(hdmi->dev, "%d: next_mode :%d\n" , i, next_mode); |
134 | |
135 | if (next_mode != HDMI_SYS_POWER_MODE_D) { |
136 | hdmi_modb(hdmi, HDMI_SYS_CTRL, |
137 | msk: HDMI_SYS_POWER_MODE_MASK, val: next_mode); |
138 | } else { |
139 | hdmi_writeb(hdmi, HDMI_SYS_CTRL, |
140 | val: HDMI_SYS_POWER_MODE_D | |
141 | HDMI_SYS_PLL_RESET_MASK); |
142 | usleep_range(min: 90, max: 100); |
143 | hdmi_writeb(hdmi, HDMI_SYS_CTRL, |
144 | val: HDMI_SYS_POWER_MODE_D | |
145 | HDMI_SYS_PLLB_RESET); |
146 | usleep_range(min: 90, max: 100); |
147 | hdmi_writeb(hdmi, HDMI_SYS_CTRL, |
148 | val: HDMI_SYS_POWER_MODE_D); |
149 | } |
150 | current_mode = next_mode; |
151 | i = i + 1; |
152 | } while ((next_mode != mode) && (i < 5)); |
153 | |
154 | /* |
155 | * When the IP controller isn't configured with accurate video timing, |
156 | * DDC_CLK should be equal to the PLLA frequency, which is 30MHz, |
157 | * so we need to init the TMDS rate to the PCLK rate and reconfigure |
158 | * the DDC clock. |
159 | */ |
160 | if (mode < HDMI_SYS_POWER_MODE_D) |
161 | hdmi->tmdsclk = DEFAULT_PLLA_RATE; |
162 | } |
163 | |
164 | static int |
165 | rk3066_hdmi_upload_frame(struct rk3066_hdmi *hdmi, int setup_rc, |
166 | union hdmi_infoframe *frame, u32 frame_index, |
167 | u32 mask, u32 disable, u32 enable) |
168 | { |
169 | if (mask) |
170 | hdmi_modb(hdmi, HDMI_CP_AUTO_SEND_CTRL, msk: mask, val: disable); |
171 | |
172 | hdmi_writeb(hdmi, HDMI_CP_BUF_INDEX, val: frame_index); |
173 | |
174 | if (setup_rc >= 0) { |
175 | u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE]; |
176 | ssize_t rc, i; |
177 | |
178 | rc = hdmi_infoframe_pack(frame, buffer: packed_frame, |
179 | size: sizeof(packed_frame)); |
180 | if (rc < 0) |
181 | return rc; |
182 | |
183 | for (i = 0; i < rc; i++) |
184 | hdmi_writeb(hdmi, HDMI_CP_BUF_ACC_HB0 + i * 4, |
185 | val: packed_frame[i]); |
186 | |
187 | if (mask) |
188 | hdmi_modb(hdmi, HDMI_CP_AUTO_SEND_CTRL, msk: mask, val: enable); |
189 | } |
190 | |
191 | return setup_rc; |
192 | } |
193 | |
194 | static int rk3066_hdmi_config_avi(struct rk3066_hdmi *hdmi, |
195 | struct drm_display_mode *mode) |
196 | { |
197 | union hdmi_infoframe frame; |
198 | int rc; |
199 | |
200 | rc = drm_hdmi_avi_infoframe_from_display_mode(frame: &frame.avi, |
201 | connector: &hdmi->connector, mode); |
202 | |
203 | if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444) |
204 | frame.avi.colorspace = HDMI_COLORSPACE_YUV444; |
205 | else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422) |
206 | frame.avi.colorspace = HDMI_COLORSPACE_YUV422; |
207 | else |
208 | frame.avi.colorspace = HDMI_COLORSPACE_RGB; |
209 | |
210 | frame.avi.colorimetry = hdmi->hdmi_data.colorimetry; |
211 | frame.avi.scan_mode = HDMI_SCAN_MODE_NONE; |
212 | |
213 | return rk3066_hdmi_upload_frame(hdmi, setup_rc: rc, frame: &frame, |
214 | frame_index: HDMI_INFOFRAME_AVI, mask: 0, disable: 0, enable: 0); |
215 | } |
216 | |
217 | static int rk3066_hdmi_config_video_timing(struct rk3066_hdmi *hdmi, |
218 | struct drm_display_mode *mode) |
219 | { |
220 | int value, vsync_offset; |
221 | |
222 | /* Set the details for the external polarity and interlace mode. */ |
223 | value = HDMI_EXT_VIDEO_SET_EN; |
224 | value |= mode->flags & DRM_MODE_FLAG_PHSYNC ? |
225 | HDMI_VIDEO_HSYNC_ACTIVE_HIGH : HDMI_VIDEO_HSYNC_ACTIVE_LOW; |
226 | value |= mode->flags & DRM_MODE_FLAG_PVSYNC ? |
227 | HDMI_VIDEO_VSYNC_ACTIVE_HIGH : HDMI_VIDEO_VSYNC_ACTIVE_LOW; |
228 | value |= mode->flags & DRM_MODE_FLAG_INTERLACE ? |
229 | HDMI_VIDEO_MODE_INTERLACE : HDMI_VIDEO_MODE_PROGRESSIVE; |
230 | |
231 | if (hdmi->hdmi_data.vic == 2 || hdmi->hdmi_data.vic == 3) |
232 | vsync_offset = 6; |
233 | else |
234 | vsync_offset = 0; |
235 | |
236 | value |= vsync_offset << HDMI_VIDEO_VSYNC_OFFSET_SHIFT; |
237 | hdmi_writeb(hdmi, HDMI_EXT_VIDEO_PARA, val: value); |
238 | |
239 | /* Set the details for the external video timing. */ |
240 | value = mode->htotal; |
241 | hdmi_writeb(hdmi, HDMI_EXT_HTOTAL_L, val: value & 0xFF); |
242 | hdmi_writeb(hdmi, HDMI_EXT_HTOTAL_H, val: (value >> 8) & 0xFF); |
243 | |
244 | value = mode->htotal - mode->hdisplay; |
245 | hdmi_writeb(hdmi, HDMI_EXT_HBLANK_L, val: value & 0xFF); |
246 | hdmi_writeb(hdmi, HDMI_EXT_HBLANK_H, val: (value >> 8) & 0xFF); |
247 | |
248 | value = mode->htotal - mode->hsync_start; |
249 | hdmi_writeb(hdmi, HDMI_EXT_HDELAY_L, val: value & 0xFF); |
250 | hdmi_writeb(hdmi, HDMI_EXT_HDELAY_H, val: (value >> 8) & 0xFF); |
251 | |
252 | value = mode->hsync_end - mode->hsync_start; |
253 | hdmi_writeb(hdmi, HDMI_EXT_HDURATION_L, val: value & 0xFF); |
254 | hdmi_writeb(hdmi, HDMI_EXT_HDURATION_H, val: (value >> 8) & 0xFF); |
255 | |
256 | value = mode->vtotal; |
257 | hdmi_writeb(hdmi, HDMI_EXT_VTOTAL_L, val: value & 0xFF); |
258 | hdmi_writeb(hdmi, HDMI_EXT_VTOTAL_H, val: (value >> 8) & 0xFF); |
259 | |
260 | value = mode->vtotal - mode->vdisplay; |
261 | hdmi_writeb(hdmi, HDMI_EXT_VBLANK_L, val: value & 0xFF); |
262 | |
263 | value = mode->vtotal - mode->vsync_start + vsync_offset; |
264 | hdmi_writeb(hdmi, HDMI_EXT_VDELAY, val: value & 0xFF); |
265 | |
266 | value = mode->vsync_end - mode->vsync_start; |
267 | hdmi_writeb(hdmi, HDMI_EXT_VDURATION, val: value & 0xFF); |
268 | |
269 | return 0; |
270 | } |
271 | |
272 | static void |
273 | rk3066_hdmi_phy_write(struct rk3066_hdmi *hdmi, u16 offset, u8 value) |
274 | { |
275 | hdmi_writeb(hdmi, offset, val: value); |
276 | hdmi_modb(hdmi, HDMI_SYS_CTRL, |
277 | msk: HDMI_SYS_PLL_RESET_MASK, val: HDMI_SYS_PLL_RESET); |
278 | usleep_range(min: 90, max: 100); |
279 | hdmi_modb(hdmi, HDMI_SYS_CTRL, msk: HDMI_SYS_PLL_RESET_MASK, val: 0); |
280 | usleep_range(min: 900, max: 1000); |
281 | } |
282 | |
283 | static void rk3066_hdmi_config_phy(struct rk3066_hdmi *hdmi) |
284 | { |
285 | /* TMDS uses the same frequency as dclk. */ |
286 | hdmi_writeb(hdmi, HDMI_DEEP_COLOR_MODE, val: 0x22); |
287 | |
288 | /* |
289 | * The semi-public documentation does not describe the hdmi registers |
290 | * used by the function rk3066_hdmi_phy_write(), so we keep using |
291 | * these magic values for now. |
292 | */ |
293 | if (hdmi->tmdsclk > 100000000) { |
294 | rk3066_hdmi_phy_write(hdmi, offset: 0x158, value: 0x0E); |
295 | rk3066_hdmi_phy_write(hdmi, offset: 0x15c, value: 0x00); |
296 | rk3066_hdmi_phy_write(hdmi, offset: 0x160, value: 0x60); |
297 | rk3066_hdmi_phy_write(hdmi, offset: 0x164, value: 0x00); |
298 | rk3066_hdmi_phy_write(hdmi, offset: 0x168, value: 0xDA); |
299 | rk3066_hdmi_phy_write(hdmi, offset: 0x16c, value: 0xA1); |
300 | rk3066_hdmi_phy_write(hdmi, offset: 0x170, value: 0x0e); |
301 | rk3066_hdmi_phy_write(hdmi, offset: 0x174, value: 0x22); |
302 | rk3066_hdmi_phy_write(hdmi, offset: 0x178, value: 0x00); |
303 | } else if (hdmi->tmdsclk > 50000000) { |
304 | rk3066_hdmi_phy_write(hdmi, offset: 0x158, value: 0x06); |
305 | rk3066_hdmi_phy_write(hdmi, offset: 0x15c, value: 0x00); |
306 | rk3066_hdmi_phy_write(hdmi, offset: 0x160, value: 0x60); |
307 | rk3066_hdmi_phy_write(hdmi, offset: 0x164, value: 0x00); |
308 | rk3066_hdmi_phy_write(hdmi, offset: 0x168, value: 0xCA); |
309 | rk3066_hdmi_phy_write(hdmi, offset: 0x16c, value: 0xA3); |
310 | rk3066_hdmi_phy_write(hdmi, offset: 0x170, value: 0x0e); |
311 | rk3066_hdmi_phy_write(hdmi, offset: 0x174, value: 0x20); |
312 | rk3066_hdmi_phy_write(hdmi, offset: 0x178, value: 0x00); |
313 | } else { |
314 | rk3066_hdmi_phy_write(hdmi, offset: 0x158, value: 0x02); |
315 | rk3066_hdmi_phy_write(hdmi, offset: 0x15c, value: 0x00); |
316 | rk3066_hdmi_phy_write(hdmi, offset: 0x160, value: 0x60); |
317 | rk3066_hdmi_phy_write(hdmi, offset: 0x164, value: 0x00); |
318 | rk3066_hdmi_phy_write(hdmi, offset: 0x168, value: 0xC2); |
319 | rk3066_hdmi_phy_write(hdmi, offset: 0x16c, value: 0xA2); |
320 | rk3066_hdmi_phy_write(hdmi, offset: 0x170, value: 0x0e); |
321 | rk3066_hdmi_phy_write(hdmi, offset: 0x174, value: 0x20); |
322 | rk3066_hdmi_phy_write(hdmi, offset: 0x178, value: 0x00); |
323 | } |
324 | } |
325 | |
326 | static int rk3066_hdmi_setup(struct rk3066_hdmi *hdmi, |
327 | struct drm_display_mode *mode) |
328 | { |
329 | struct drm_display_info *display = &hdmi->connector.display_info; |
330 | |
331 | hdmi->hdmi_data.vic = drm_match_cea_mode(to_match: mode); |
332 | hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB; |
333 | |
334 | if (hdmi->hdmi_data.vic == 6 || hdmi->hdmi_data.vic == 7 || |
335 | hdmi->hdmi_data.vic == 21 || hdmi->hdmi_data.vic == 22 || |
336 | hdmi->hdmi_data.vic == 2 || hdmi->hdmi_data.vic == 3 || |
337 | hdmi->hdmi_data.vic == 17 || hdmi->hdmi_data.vic == 18) |
338 | hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601; |
339 | else |
340 | hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709; |
341 | |
342 | hdmi->tmdsclk = mode->clock * 1000; |
343 | |
344 | /* Mute video and audio output. */ |
345 | hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, msk: HDMI_VIDEO_AUDIO_DISABLE_MASK, |
346 | val: HDMI_AUDIO_DISABLE | HDMI_VIDEO_DISABLE); |
347 | |
348 | /* Set power state to mode B. */ |
349 | if (rk3066_hdmi_get_power_mode(hdmi) != HDMI_SYS_POWER_MODE_B) |
350 | rk3066_hdmi_set_power_mode(hdmi, mode: HDMI_SYS_POWER_MODE_B); |
351 | |
352 | /* Input video mode is RGB 24 bit. Use external data enable signal. */ |
353 | hdmi_modb(hdmi, HDMI_AV_CTRL1, |
354 | msk: HDMI_VIDEO_DE_MASK, val: HDMI_VIDEO_EXTERNAL_DE); |
355 | hdmi_writeb(hdmi, HDMI_VIDEO_CTRL1, |
356 | val: HDMI_VIDEO_OUTPUT_RGB444 | |
357 | HDMI_VIDEO_INPUT_DATA_DEPTH_8BIT | |
358 | HDMI_VIDEO_INPUT_COLOR_RGB); |
359 | hdmi_writeb(hdmi, HDMI_DEEP_COLOR_MODE, val: 0x20); |
360 | |
361 | rk3066_hdmi_config_video_timing(hdmi, mode); |
362 | |
363 | if (display->is_hdmi) { |
364 | hdmi_modb(hdmi, HDMI_HDCP_CTRL, msk: HDMI_VIDEO_MODE_MASK, |
365 | val: HDMI_VIDEO_MODE_HDMI); |
366 | rk3066_hdmi_config_avi(hdmi, mode); |
367 | } else { |
368 | hdmi_modb(hdmi, HDMI_HDCP_CTRL, msk: HDMI_VIDEO_MODE_MASK, val: 0); |
369 | } |
370 | |
371 | rk3066_hdmi_config_phy(hdmi); |
372 | |
373 | rk3066_hdmi_set_power_mode(hdmi, mode: HDMI_SYS_POWER_MODE_E); |
374 | |
375 | /* |
376 | * When the IP controller is configured with accurate video |
377 | * timing, the TMDS clock source should be switched to |
378 | * DCLK_LCDC, so we need to init the TMDS rate to the pixel mode |
379 | * clock rate and reconfigure the DDC clock. |
380 | */ |
381 | rk3066_hdmi_i2c_init(hdmi); |
382 | |
383 | /* Unmute video output. */ |
384 | hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, |
385 | msk: HDMI_VIDEO_AUDIO_DISABLE_MASK, val: HDMI_AUDIO_DISABLE); |
386 | return 0; |
387 | } |
388 | |
389 | static void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder, |
390 | struct drm_atomic_state *state) |
391 | { |
392 | struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); |
393 | struct drm_connector_state *conn_state; |
394 | struct drm_crtc_state *crtc_state; |
395 | int mux, val; |
396 | |
397 | conn_state = drm_atomic_get_new_connector_state(state, connector: &hdmi->connector); |
398 | if (WARN_ON(!conn_state)) |
399 | return; |
400 | |
401 | crtc_state = drm_atomic_get_new_crtc_state(state, crtc: conn_state->crtc); |
402 | if (WARN_ON(!crtc_state)) |
403 | return; |
404 | |
405 | mux = drm_of_encoder_active_endpoint_id(node: hdmi->dev->of_node, encoder); |
406 | if (mux) |
407 | val = (HDMI_VIDEO_SEL << 16) | HDMI_VIDEO_SEL; |
408 | else |
409 | val = HDMI_VIDEO_SEL << 16; |
410 | |
411 | regmap_write(map: hdmi->grf_regmap, GRF_SOC_CON0, val); |
412 | |
413 | DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder enable select: vop%s\n" , |
414 | (mux) ? "1" : "0" ); |
415 | |
416 | rk3066_hdmi_setup(hdmi, mode: &crtc_state->adjusted_mode); |
417 | } |
418 | |
419 | static void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder, |
420 | struct drm_atomic_state *state) |
421 | { |
422 | struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); |
423 | |
424 | DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder disable\n" ); |
425 | |
426 | if (rk3066_hdmi_get_power_mode(hdmi) == HDMI_SYS_POWER_MODE_E) { |
427 | hdmi_writeb(hdmi, HDMI_VIDEO_CTRL2, |
428 | val: HDMI_VIDEO_AUDIO_DISABLE_MASK); |
429 | hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, |
430 | msk: HDMI_AUDIO_CP_LOGIC_RESET_MASK, |
431 | val: HDMI_AUDIO_CP_LOGIC_RESET); |
432 | usleep_range(min: 500, max: 510); |
433 | } |
434 | rk3066_hdmi_set_power_mode(hdmi, mode: HDMI_SYS_POWER_MODE_A); |
435 | } |
436 | |
437 | static int |
438 | rk3066_hdmi_encoder_atomic_check(struct drm_encoder *encoder, |
439 | struct drm_crtc_state *crtc_state, |
440 | struct drm_connector_state *conn_state) |
441 | { |
442 | struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); |
443 | |
444 | s->output_mode = ROCKCHIP_OUT_MODE_P888; |
445 | s->output_type = DRM_MODE_CONNECTOR_HDMIA; |
446 | |
447 | return 0; |
448 | } |
449 | |
450 | static const |
451 | struct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs = { |
452 | .atomic_check = rk3066_hdmi_encoder_atomic_check, |
453 | .atomic_enable = rk3066_hdmi_encoder_enable, |
454 | .atomic_disable = rk3066_hdmi_encoder_disable, |
455 | }; |
456 | |
457 | static enum drm_connector_status |
458 | rk3066_hdmi_connector_detect(struct drm_connector *connector, bool force) |
459 | { |
460 | struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector); |
461 | |
462 | return (hdmi_readb(hdmi, HDMI_HPG_MENS_STA) & HDMI_HPG_IN_STATUS_HIGH) ? |
463 | connector_status_connected : connector_status_disconnected; |
464 | } |
465 | |
466 | static int rk3066_hdmi_connector_get_modes(struct drm_connector *connector) |
467 | { |
468 | struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector); |
469 | struct edid *edid; |
470 | int ret = 0; |
471 | |
472 | if (!hdmi->ddc) |
473 | return 0; |
474 | |
475 | edid = drm_get_edid(connector, adapter: hdmi->ddc); |
476 | if (edid) { |
477 | drm_connector_update_edid_property(connector, edid); |
478 | ret = drm_add_edid_modes(connector, edid); |
479 | kfree(objp: edid); |
480 | } |
481 | |
482 | return ret; |
483 | } |
484 | |
485 | static enum drm_mode_status |
486 | rk3066_hdmi_connector_mode_valid(struct drm_connector *connector, |
487 | struct drm_display_mode *mode) |
488 | { |
489 | u32 vic = drm_match_cea_mode(to_match: mode); |
490 | |
491 | if (vic > 1) |
492 | return MODE_OK; |
493 | else |
494 | return MODE_BAD; |
495 | } |
496 | |
497 | static struct drm_encoder * |
498 | rk3066_hdmi_connector_best_encoder(struct drm_connector *connector) |
499 | { |
500 | struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector); |
501 | |
502 | return &hdmi->encoder.encoder; |
503 | } |
504 | |
505 | static int |
506 | rk3066_hdmi_probe_single_connector_modes(struct drm_connector *connector, |
507 | uint32_t maxX, uint32_t maxY) |
508 | { |
509 | if (maxX > 1920) |
510 | maxX = 1920; |
511 | if (maxY > 1080) |
512 | maxY = 1080; |
513 | |
514 | return drm_helper_probe_single_connector_modes(connector, maxX, maxY); |
515 | } |
516 | |
517 | static void rk3066_hdmi_connector_destroy(struct drm_connector *connector) |
518 | { |
519 | drm_connector_unregister(connector); |
520 | drm_connector_cleanup(connector); |
521 | } |
522 | |
523 | static const struct drm_connector_funcs rk3066_hdmi_connector_funcs = { |
524 | .fill_modes = rk3066_hdmi_probe_single_connector_modes, |
525 | .detect = rk3066_hdmi_connector_detect, |
526 | .destroy = rk3066_hdmi_connector_destroy, |
527 | .reset = drm_atomic_helper_connector_reset, |
528 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
529 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
530 | }; |
531 | |
532 | static const |
533 | struct drm_connector_helper_funcs rk3066_hdmi_connector_helper_funcs = { |
534 | .get_modes = rk3066_hdmi_connector_get_modes, |
535 | .mode_valid = rk3066_hdmi_connector_mode_valid, |
536 | .best_encoder = rk3066_hdmi_connector_best_encoder, |
537 | }; |
538 | |
539 | static int |
540 | rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi) |
541 | { |
542 | struct drm_encoder *encoder = &hdmi->encoder.encoder; |
543 | struct device *dev = hdmi->dev; |
544 | |
545 | encoder->possible_crtcs = |
546 | drm_of_find_possible_crtcs(dev: drm, port: dev->of_node); |
547 | |
548 | /* |
549 | * If we failed to find the CRTC(s) which this encoder is |
550 | * supposed to be connected to, it's because the CRTC has |
551 | * not been registered yet. Defer probing, and hope that |
552 | * the required CRTC is added later. |
553 | */ |
554 | if (encoder->possible_crtcs == 0) |
555 | return -EPROBE_DEFER; |
556 | |
557 | drm_encoder_helper_add(encoder, funcs: &rk3066_hdmi_encoder_helper_funcs); |
558 | drm_simple_encoder_init(dev: drm, encoder, DRM_MODE_ENCODER_TMDS); |
559 | |
560 | hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; |
561 | |
562 | drm_connector_helper_add(connector: &hdmi->connector, |
563 | funcs: &rk3066_hdmi_connector_helper_funcs); |
564 | drm_connector_init_with_ddc(dev: drm, connector: &hdmi->connector, |
565 | funcs: &rk3066_hdmi_connector_funcs, |
566 | DRM_MODE_CONNECTOR_HDMIA, |
567 | ddc: hdmi->ddc); |
568 | |
569 | drm_connector_attach_encoder(connector: &hdmi->connector, encoder); |
570 | |
571 | return 0; |
572 | } |
573 | |
574 | static irqreturn_t rk3066_hdmi_hardirq(int irq, void *dev_id) |
575 | { |
576 | struct rk3066_hdmi *hdmi = dev_id; |
577 | irqreturn_t ret = IRQ_NONE; |
578 | u8 interrupt; |
579 | |
580 | if (rk3066_hdmi_get_power_mode(hdmi) == HDMI_SYS_POWER_MODE_A) |
581 | hdmi_writeb(hdmi, HDMI_SYS_CTRL, val: HDMI_SYS_POWER_MODE_B); |
582 | |
583 | interrupt = hdmi_readb(hdmi, HDMI_INTR_STATUS1); |
584 | if (interrupt) |
585 | hdmi_writeb(hdmi, HDMI_INTR_STATUS1, val: interrupt); |
586 | |
587 | if (interrupt & HDMI_INTR_EDID_MASK) { |
588 | hdmi->i2c->stat = interrupt; |
589 | complete(&hdmi->i2c->cmpltn); |
590 | } |
591 | |
592 | if (interrupt & (HDMI_INTR_HOTPLUG | HDMI_INTR_MSENS)) |
593 | ret = IRQ_WAKE_THREAD; |
594 | |
595 | return ret; |
596 | } |
597 | |
598 | static irqreturn_t rk3066_hdmi_irq(int irq, void *dev_id) |
599 | { |
600 | struct rk3066_hdmi *hdmi = dev_id; |
601 | |
602 | drm_helper_hpd_irq_event(dev: hdmi->connector.dev); |
603 | |
604 | return IRQ_HANDLED; |
605 | } |
606 | |
607 | static int rk3066_hdmi_i2c_read(struct rk3066_hdmi *hdmi, struct i2c_msg *msgs) |
608 | { |
609 | int length = msgs->len; |
610 | u8 *buf = msgs->buf; |
611 | int ret; |
612 | |
613 | ret = wait_for_completion_timeout(x: &hdmi->i2c->cmpltn, HZ / 10); |
614 | if (!ret || hdmi->i2c->stat & HDMI_INTR_EDID_ERR) |
615 | return -EAGAIN; |
616 | |
617 | while (length--) |
618 | *buf++ = hdmi_readb(hdmi, HDMI_DDC_READ_FIFO_ADDR); |
619 | |
620 | return 0; |
621 | } |
622 | |
623 | static int rk3066_hdmi_i2c_write(struct rk3066_hdmi *hdmi, struct i2c_msg *msgs) |
624 | { |
625 | /* |
626 | * The DDC module only supports read EDID message, so |
627 | * we assume that each word write to this i2c adapter |
628 | * should be the offset of the EDID word address. |
629 | */ |
630 | if (msgs->len != 1 || |
631 | (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR)) |
632 | return -EINVAL; |
633 | |
634 | reinit_completion(x: &hdmi->i2c->cmpltn); |
635 | |
636 | if (msgs->addr == DDC_SEGMENT_ADDR) |
637 | hdmi->i2c->segment_addr = msgs->buf[0]; |
638 | if (msgs->addr == DDC_ADDR) |
639 | hdmi->i2c->ddc_addr = msgs->buf[0]; |
640 | |
641 | /* Set edid fifo first address. */ |
642 | hdmi_writeb(hdmi, HDMI_EDID_FIFO_ADDR, val: 0x00); |
643 | |
644 | /* Set edid word address 0x00/0x80. */ |
645 | hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, val: hdmi->i2c->ddc_addr); |
646 | |
647 | /* Set edid segment pointer. */ |
648 | hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, val: hdmi->i2c->segment_addr); |
649 | |
650 | return 0; |
651 | } |
652 | |
653 | static int rk3066_hdmi_i2c_xfer(struct i2c_adapter *adap, |
654 | struct i2c_msg *msgs, int num) |
655 | { |
656 | struct rk3066_hdmi *hdmi = i2c_get_adapdata(adap); |
657 | struct rk3066_hdmi_i2c *i2c = hdmi->i2c; |
658 | int i, ret = 0; |
659 | |
660 | mutex_lock(&i2c->i2c_lock); |
661 | |
662 | rk3066_hdmi_i2c_init(hdmi); |
663 | |
664 | /* Unmute HDMI EDID interrupt. */ |
665 | hdmi_modb(hdmi, HDMI_INTR_MASK1, |
666 | msk: HDMI_INTR_EDID_MASK, val: HDMI_INTR_EDID_MASK); |
667 | i2c->stat = 0; |
668 | |
669 | for (i = 0; i < num; i++) { |
670 | DRM_DEV_DEBUG(hdmi->dev, |
671 | "xfer: num: %d/%d, len: %d, flags: %#x\n" , |
672 | i + 1, num, msgs[i].len, msgs[i].flags); |
673 | |
674 | if (msgs[i].flags & I2C_M_RD) |
675 | ret = rk3066_hdmi_i2c_read(hdmi, msgs: &msgs[i]); |
676 | else |
677 | ret = rk3066_hdmi_i2c_write(hdmi, msgs: &msgs[i]); |
678 | |
679 | if (ret < 0) |
680 | break; |
681 | } |
682 | |
683 | if (!ret) |
684 | ret = num; |
685 | |
686 | /* Mute HDMI EDID interrupt. */ |
687 | hdmi_modb(hdmi, HDMI_INTR_MASK1, msk: HDMI_INTR_EDID_MASK, val: 0); |
688 | |
689 | mutex_unlock(lock: &i2c->i2c_lock); |
690 | |
691 | return ret; |
692 | } |
693 | |
694 | static u32 rk3066_hdmi_i2c_func(struct i2c_adapter *adapter) |
695 | { |
696 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; |
697 | } |
698 | |
699 | static const struct i2c_algorithm rk3066_hdmi_algorithm = { |
700 | .master_xfer = rk3066_hdmi_i2c_xfer, |
701 | .functionality = rk3066_hdmi_i2c_func, |
702 | }; |
703 | |
704 | static struct i2c_adapter *rk3066_hdmi_i2c_adapter(struct rk3066_hdmi *hdmi) |
705 | { |
706 | struct i2c_adapter *adap; |
707 | struct rk3066_hdmi_i2c *i2c; |
708 | int ret; |
709 | |
710 | i2c = devm_kzalloc(dev: hdmi->dev, size: sizeof(*i2c), GFP_KERNEL); |
711 | if (!i2c) |
712 | return ERR_PTR(error: -ENOMEM); |
713 | |
714 | mutex_init(&i2c->i2c_lock); |
715 | init_completion(x: &i2c->cmpltn); |
716 | |
717 | adap = &i2c->adap; |
718 | adap->owner = THIS_MODULE; |
719 | adap->dev.parent = hdmi->dev; |
720 | adap->dev.of_node = hdmi->dev->of_node; |
721 | adap->algo = &rk3066_hdmi_algorithm; |
722 | strscpy(adap->name, "RK3066 HDMI" , sizeof(adap->name)); |
723 | i2c_set_adapdata(adap, data: hdmi); |
724 | |
725 | ret = i2c_add_adapter(adap); |
726 | if (ret) { |
727 | DRM_DEV_ERROR(hdmi->dev, "cannot add %s I2C adapter\n" , |
728 | adap->name); |
729 | devm_kfree(dev: hdmi->dev, p: i2c); |
730 | return ERR_PTR(error: ret); |
731 | } |
732 | |
733 | hdmi->i2c = i2c; |
734 | |
735 | DRM_DEV_DEBUG(hdmi->dev, "registered %s I2C bus driver\n" , adap->name); |
736 | |
737 | return adap; |
738 | } |
739 | |
740 | static int rk3066_hdmi_bind(struct device *dev, struct device *master, |
741 | void *data) |
742 | { |
743 | struct platform_device *pdev = to_platform_device(dev); |
744 | struct drm_device *drm = data; |
745 | struct rk3066_hdmi *hdmi; |
746 | int irq; |
747 | int ret; |
748 | |
749 | hdmi = devm_kzalloc(dev, size: sizeof(*hdmi), GFP_KERNEL); |
750 | if (!hdmi) |
751 | return -ENOMEM; |
752 | |
753 | hdmi->dev = dev; |
754 | hdmi->drm_dev = drm; |
755 | hdmi->regs = devm_platform_ioremap_resource(pdev, index: 0); |
756 | if (IS_ERR(ptr: hdmi->regs)) |
757 | return PTR_ERR(ptr: hdmi->regs); |
758 | |
759 | irq = platform_get_irq(pdev, 0); |
760 | if (irq < 0) |
761 | return irq; |
762 | |
763 | hdmi->hclk = devm_clk_get(dev, id: "hclk" ); |
764 | if (IS_ERR(ptr: hdmi->hclk)) { |
765 | DRM_DEV_ERROR(dev, "unable to get HDMI hclk clock\n" ); |
766 | return PTR_ERR(ptr: hdmi->hclk); |
767 | } |
768 | |
769 | ret = clk_prepare_enable(clk: hdmi->hclk); |
770 | if (ret) { |
771 | DRM_DEV_ERROR(dev, "cannot enable HDMI hclk clock: %d\n" , ret); |
772 | return ret; |
773 | } |
774 | |
775 | hdmi->grf_regmap = syscon_regmap_lookup_by_phandle(np: dev->of_node, |
776 | property: "rockchip,grf" ); |
777 | if (IS_ERR(ptr: hdmi->grf_regmap)) { |
778 | DRM_DEV_ERROR(dev, "unable to get rockchip,grf\n" ); |
779 | ret = PTR_ERR(ptr: hdmi->grf_regmap); |
780 | goto err_disable_hclk; |
781 | } |
782 | |
783 | /* internal hclk = hdmi_hclk / 25 */ |
784 | hdmi_writeb(hdmi, HDMI_INTERNAL_CLK_DIVIDER, val: 25); |
785 | |
786 | hdmi->ddc = rk3066_hdmi_i2c_adapter(hdmi); |
787 | if (IS_ERR(ptr: hdmi->ddc)) { |
788 | ret = PTR_ERR(ptr: hdmi->ddc); |
789 | hdmi->ddc = NULL; |
790 | goto err_disable_hclk; |
791 | } |
792 | |
793 | rk3066_hdmi_set_power_mode(hdmi, mode: HDMI_SYS_POWER_MODE_B); |
794 | usleep_range(min: 999, max: 1000); |
795 | hdmi_writeb(hdmi, HDMI_INTR_MASK1, val: HDMI_INTR_HOTPLUG); |
796 | hdmi_writeb(hdmi, HDMI_INTR_MASK2, val: 0); |
797 | hdmi_writeb(hdmi, HDMI_INTR_MASK3, val: 0); |
798 | hdmi_writeb(hdmi, HDMI_INTR_MASK4, val: 0); |
799 | rk3066_hdmi_set_power_mode(hdmi, mode: HDMI_SYS_POWER_MODE_A); |
800 | |
801 | ret = rk3066_hdmi_register(drm, hdmi); |
802 | if (ret) |
803 | goto err_disable_i2c; |
804 | |
805 | dev_set_drvdata(dev, data: hdmi); |
806 | |
807 | ret = devm_request_threaded_irq(dev, irq, handler: rk3066_hdmi_hardirq, |
808 | thread_fn: rk3066_hdmi_irq, IRQF_SHARED, |
809 | devname: dev_name(dev), dev_id: hdmi); |
810 | if (ret) { |
811 | DRM_DEV_ERROR(dev, "failed to request hdmi irq: %d\n" , ret); |
812 | goto err_cleanup_hdmi; |
813 | } |
814 | |
815 | return 0; |
816 | |
817 | err_cleanup_hdmi: |
818 | hdmi->connector.funcs->destroy(&hdmi->connector); |
819 | hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder); |
820 | err_disable_i2c: |
821 | i2c_put_adapter(adap: hdmi->ddc); |
822 | err_disable_hclk: |
823 | clk_disable_unprepare(clk: hdmi->hclk); |
824 | |
825 | return ret; |
826 | } |
827 | |
828 | static void rk3066_hdmi_unbind(struct device *dev, struct device *master, |
829 | void *data) |
830 | { |
831 | struct rk3066_hdmi *hdmi = dev_get_drvdata(dev); |
832 | |
833 | hdmi->connector.funcs->destroy(&hdmi->connector); |
834 | hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder); |
835 | |
836 | i2c_put_adapter(adap: hdmi->ddc); |
837 | clk_disable_unprepare(clk: hdmi->hclk); |
838 | } |
839 | |
840 | static const struct component_ops rk3066_hdmi_ops = { |
841 | .bind = rk3066_hdmi_bind, |
842 | .unbind = rk3066_hdmi_unbind, |
843 | }; |
844 | |
845 | static int rk3066_hdmi_probe(struct platform_device *pdev) |
846 | { |
847 | return component_add(&pdev->dev, &rk3066_hdmi_ops); |
848 | } |
849 | |
850 | static void rk3066_hdmi_remove(struct platform_device *pdev) |
851 | { |
852 | component_del(&pdev->dev, &rk3066_hdmi_ops); |
853 | } |
854 | |
855 | static const struct of_device_id rk3066_hdmi_dt_ids[] = { |
856 | { .compatible = "rockchip,rk3066-hdmi" }, |
857 | { /* sentinel */ }, |
858 | }; |
859 | MODULE_DEVICE_TABLE(of, rk3066_hdmi_dt_ids); |
860 | |
861 | struct platform_driver rk3066_hdmi_driver = { |
862 | .probe = rk3066_hdmi_probe, |
863 | .remove_new = rk3066_hdmi_remove, |
864 | .driver = { |
865 | .name = "rockchip-rk3066-hdmi" , |
866 | .of_match_table = rk3066_hdmi_dt_ids, |
867 | }, |
868 | }; |
869 | |