1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2009 Nokia Corporation |
4 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> |
5 | * |
6 | * VENC settings from TI's DSS driver |
7 | */ |
8 | |
9 | #define DSS_SUBSYS_NAME "VENC" |
10 | |
11 | #include <linux/kernel.h> |
12 | #include <linux/module.h> |
13 | #include <linux/clk.h> |
14 | #include <linux/err.h> |
15 | #include <linux/io.h> |
16 | #include <linux/completion.h> |
17 | #include <linux/delay.h> |
18 | #include <linux/string.h> |
19 | #include <linux/seq_file.h> |
20 | #include <linux/platform_device.h> |
21 | #include <linux/regulator/consumer.h> |
22 | #include <linux/pm_runtime.h> |
23 | #include <linux/of.h> |
24 | #include <linux/of_graph.h> |
25 | #include <linux/component.h> |
26 | #include <linux/sys_soc.h> |
27 | |
28 | #include <drm/drm_bridge.h> |
29 | |
30 | #include "omapdss.h" |
31 | #include "dss.h" |
32 | |
33 | /* Venc registers */ |
34 | #define VENC_REV_ID 0x00 |
35 | #define VENC_STATUS 0x04 |
36 | #define VENC_F_CONTROL 0x08 |
37 | #define VENC_VIDOUT_CTRL 0x10 |
38 | #define VENC_SYNC_CTRL 0x14 |
39 | #define VENC_LLEN 0x1C |
40 | #define VENC_FLENS 0x20 |
41 | #define VENC_HFLTR_CTRL 0x24 |
42 | #define VENC_CC_CARR_WSS_CARR 0x28 |
43 | #define VENC_C_PHASE 0x2C |
44 | #define VENC_GAIN_U 0x30 |
45 | #define VENC_GAIN_V 0x34 |
46 | #define VENC_GAIN_Y 0x38 |
47 | #define VENC_BLACK_LEVEL 0x3C |
48 | #define VENC_BLANK_LEVEL 0x40 |
49 | #define VENC_X_COLOR 0x44 |
50 | #define VENC_M_CONTROL 0x48 |
51 | #define VENC_BSTAMP_WSS_DATA 0x4C |
52 | #define VENC_S_CARR 0x50 |
53 | #define VENC_LINE21 0x54 |
54 | #define VENC_LN_SEL 0x58 |
55 | #define VENC_L21__WC_CTL 0x5C |
56 | #define VENC_HTRIGGER_VTRIGGER 0x60 |
57 | #define VENC_SAVID__EAVID 0x64 |
58 | #define VENC_FLEN__FAL 0x68 |
59 | #define VENC_LAL__PHASE_RESET 0x6C |
60 | #define VENC_HS_INT_START_STOP_X 0x70 |
61 | #define VENC_HS_EXT_START_STOP_X 0x74 |
62 | #define VENC_VS_INT_START_X 0x78 |
63 | #define VENC_VS_INT_STOP_X__VS_INT_START_Y 0x7C |
64 | #define VENC_VS_INT_STOP_Y__VS_EXT_START_X 0x80 |
65 | #define VENC_VS_EXT_STOP_X__VS_EXT_START_Y 0x84 |
66 | #define VENC_VS_EXT_STOP_Y 0x88 |
67 | #define VENC_AVID_START_STOP_X 0x90 |
68 | #define VENC_AVID_START_STOP_Y 0x94 |
69 | #define VENC_FID_INT_START_X__FID_INT_START_Y 0xA0 |
70 | #define VENC_FID_INT_OFFSET_Y__FID_EXT_START_X 0xA4 |
71 | #define VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y 0xA8 |
72 | #define VENC_TVDETGP_INT_START_STOP_X 0xB0 |
73 | #define VENC_TVDETGP_INT_START_STOP_Y 0xB4 |
74 | #define VENC_GEN_CTRL 0xB8 |
75 | #define VENC_OUTPUT_CONTROL 0xC4 |
76 | #define VENC_OUTPUT_TEST 0xC8 |
77 | #define VENC_DAC_B__DAC_C 0xC8 |
78 | |
79 | struct venc_config { |
80 | u32 f_control; |
81 | u32 vidout_ctrl; |
82 | u32 sync_ctrl; |
83 | u32 llen; |
84 | u32 flens; |
85 | u32 hfltr_ctrl; |
86 | u32 cc_carr_wss_carr; |
87 | u32 c_phase; |
88 | u32 gain_u; |
89 | u32 gain_v; |
90 | u32 gain_y; |
91 | u32 black_level; |
92 | u32 blank_level; |
93 | u32 x_color; |
94 | u32 m_control; |
95 | u32 bstamp_wss_data; |
96 | u32 s_carr; |
97 | u32 line21; |
98 | u32 ln_sel; |
99 | u32 l21__wc_ctl; |
100 | u32 htrigger_vtrigger; |
101 | u32 savid__eavid; |
102 | u32 flen__fal; |
103 | u32 lal__phase_reset; |
104 | u32 hs_int_start_stop_x; |
105 | u32 hs_ext_start_stop_x; |
106 | u32 vs_int_start_x; |
107 | u32 vs_int_stop_x__vs_int_start_y; |
108 | u32 vs_int_stop_y__vs_ext_start_x; |
109 | u32 vs_ext_stop_x__vs_ext_start_y; |
110 | u32 vs_ext_stop_y; |
111 | u32 avid_start_stop_x; |
112 | u32 avid_start_stop_y; |
113 | u32 fid_int_start_x__fid_int_start_y; |
114 | u32 fid_int_offset_y__fid_ext_start_x; |
115 | u32 fid_ext_start_y__fid_ext_offset_y; |
116 | u32 tvdetgp_int_start_stop_x; |
117 | u32 tvdetgp_int_start_stop_y; |
118 | u32 gen_ctrl; |
119 | }; |
120 | |
121 | /* from TRM */ |
122 | static const struct venc_config venc_config_pal_trm = { |
123 | .f_control = 0, |
124 | .vidout_ctrl = 1, |
125 | .sync_ctrl = 0x40, |
126 | .llen = 0x35F, /* 863 */ |
127 | .flens = 0x270, /* 624 */ |
128 | .hfltr_ctrl = 0, |
129 | .cc_carr_wss_carr = 0x2F7225ED, |
130 | .c_phase = 0, |
131 | .gain_u = 0x111, |
132 | .gain_v = 0x181, |
133 | .gain_y = 0x140, |
134 | .black_level = 0x3B, |
135 | .blank_level = 0x3B, |
136 | .x_color = 0x7, |
137 | .m_control = 0x2, |
138 | .bstamp_wss_data = 0x3F, |
139 | .s_carr = 0x2A098ACB, |
140 | .line21 = 0, |
141 | .ln_sel = 0x01290015, |
142 | .l21__wc_ctl = 0x0000F603, |
143 | .htrigger_vtrigger = 0, |
144 | |
145 | .savid__eavid = 0x06A70108, |
146 | .flen__fal = 0x00180270, |
147 | .lal__phase_reset = 0x00040135, |
148 | .hs_int_start_stop_x = 0x00880358, |
149 | .hs_ext_start_stop_x = 0x000F035F, |
150 | .vs_int_start_x = 0x01A70000, |
151 | .vs_int_stop_x__vs_int_start_y = 0x000001A7, |
152 | .vs_int_stop_y__vs_ext_start_x = 0x01AF0000, |
153 | .vs_ext_stop_x__vs_ext_start_y = 0x000101AF, |
154 | .vs_ext_stop_y = 0x00000025, |
155 | .avid_start_stop_x = 0x03530083, |
156 | .avid_start_stop_y = 0x026C002E, |
157 | .fid_int_start_x__fid_int_start_y = 0x0001008A, |
158 | .fid_int_offset_y__fid_ext_start_x = 0x002E0138, |
159 | .fid_ext_start_y__fid_ext_offset_y = 0x01380001, |
160 | |
161 | .tvdetgp_int_start_stop_x = 0x00140001, |
162 | .tvdetgp_int_start_stop_y = 0x00010001, |
163 | .gen_ctrl = 0x00FF0000, |
164 | }; |
165 | |
166 | /* from TRM */ |
167 | static const struct venc_config venc_config_ntsc_trm = { |
168 | .f_control = 0, |
169 | .vidout_ctrl = 1, |
170 | .sync_ctrl = 0x8040, |
171 | .llen = 0x359, |
172 | .flens = 0x20C, |
173 | .hfltr_ctrl = 0, |
174 | .cc_carr_wss_carr = 0x043F2631, |
175 | .c_phase = 0, |
176 | .gain_u = 0x102, |
177 | .gain_v = 0x16C, |
178 | .gain_y = 0x12F, |
179 | .black_level = 0x43, |
180 | .blank_level = 0x38, |
181 | .x_color = 0x7, |
182 | .m_control = 0x1, |
183 | .bstamp_wss_data = 0x38, |
184 | .s_carr = 0x21F07C1F, |
185 | .line21 = 0, |
186 | .ln_sel = 0x01310011, |
187 | .l21__wc_ctl = 0x0000F003, |
188 | .htrigger_vtrigger = 0, |
189 | |
190 | .savid__eavid = 0x069300F4, |
191 | .flen__fal = 0x0016020C, |
192 | .lal__phase_reset = 0x00060107, |
193 | .hs_int_start_stop_x = 0x008E0350, |
194 | .hs_ext_start_stop_x = 0x000F0359, |
195 | .vs_int_start_x = 0x01A00000, |
196 | .vs_int_stop_x__vs_int_start_y = 0x020701A0, |
197 | .vs_int_stop_y__vs_ext_start_x = 0x01AC0024, |
198 | .vs_ext_stop_x__vs_ext_start_y = 0x020D01AC, |
199 | .vs_ext_stop_y = 0x00000006, |
200 | .avid_start_stop_x = 0x03480078, |
201 | .avid_start_stop_y = 0x02060024, |
202 | .fid_int_start_x__fid_int_start_y = 0x0001008A, |
203 | .fid_int_offset_y__fid_ext_start_x = 0x01AC0106, |
204 | .fid_ext_start_y__fid_ext_offset_y = 0x01060006, |
205 | |
206 | .tvdetgp_int_start_stop_x = 0x00140001, |
207 | .tvdetgp_int_start_stop_y = 0x00010001, |
208 | .gen_ctrl = 0x00F90000, |
209 | }; |
210 | |
211 | enum venc_videomode { |
212 | VENC_MODE_UNKNOWN, |
213 | VENC_MODE_PAL, |
214 | VENC_MODE_NTSC, |
215 | }; |
216 | |
217 | static const struct drm_display_mode omap_dss_pal_mode = { |
218 | .hdisplay = 720, |
219 | .hsync_start = 732, |
220 | .hsync_end = 796, |
221 | .htotal = 864, |
222 | .vdisplay = 574, |
223 | .vsync_start = 579, |
224 | .vsync_end = 584, |
225 | .vtotal = 625, |
226 | .clock = 13500, |
227 | |
228 | .flags = DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_NHSYNC | |
229 | DRM_MODE_FLAG_NVSYNC, |
230 | }; |
231 | |
232 | static const struct drm_display_mode omap_dss_ntsc_mode = { |
233 | .hdisplay = 720, |
234 | .hsync_start = 736, |
235 | .hsync_end = 800, |
236 | .htotal = 858, |
237 | .vdisplay = 482, |
238 | .vsync_start = 488, |
239 | .vsync_end = 494, |
240 | .vtotal = 525, |
241 | .clock = 13500, |
242 | |
243 | .flags = DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_NHSYNC | |
244 | DRM_MODE_FLAG_NVSYNC, |
245 | }; |
246 | |
247 | struct venc_device { |
248 | struct platform_device *pdev; |
249 | void __iomem *base; |
250 | struct regulator *vdda_dac_reg; |
251 | struct dss_device *dss; |
252 | |
253 | struct dss_debugfs_entry *debugfs; |
254 | |
255 | struct clk *tv_dac_clk; |
256 | |
257 | const struct venc_config *config; |
258 | enum omap_dss_venc_type type; |
259 | bool invert_polarity; |
260 | bool requires_tv_dac_clk; |
261 | |
262 | struct omap_dss_device output; |
263 | struct drm_bridge bridge; |
264 | }; |
265 | |
266 | #define drm_bridge_to_venc(b) container_of(b, struct venc_device, bridge) |
267 | |
268 | static inline void venc_write_reg(struct venc_device *venc, int idx, u32 val) |
269 | { |
270 | __raw_writel(val, addr: venc->base + idx); |
271 | } |
272 | |
273 | static inline u32 venc_read_reg(struct venc_device *venc, int idx) |
274 | { |
275 | u32 l = __raw_readl(addr: venc->base + idx); |
276 | return l; |
277 | } |
278 | |
279 | static void venc_write_config(struct venc_device *venc, |
280 | const struct venc_config *config) |
281 | { |
282 | DSSDBG("write venc conf\n" ); |
283 | |
284 | venc_write_reg(venc, VENC_LLEN, val: config->llen); |
285 | venc_write_reg(venc, VENC_FLENS, val: config->flens); |
286 | venc_write_reg(venc, VENC_CC_CARR_WSS_CARR, val: config->cc_carr_wss_carr); |
287 | venc_write_reg(venc, VENC_C_PHASE, val: config->c_phase); |
288 | venc_write_reg(venc, VENC_GAIN_U, val: config->gain_u); |
289 | venc_write_reg(venc, VENC_GAIN_V, val: config->gain_v); |
290 | venc_write_reg(venc, VENC_GAIN_Y, val: config->gain_y); |
291 | venc_write_reg(venc, VENC_BLACK_LEVEL, val: config->black_level); |
292 | venc_write_reg(venc, VENC_BLANK_LEVEL, val: config->blank_level); |
293 | venc_write_reg(venc, VENC_M_CONTROL, val: config->m_control); |
294 | venc_write_reg(venc, VENC_BSTAMP_WSS_DATA, val: config->bstamp_wss_data); |
295 | venc_write_reg(venc, VENC_S_CARR, val: config->s_carr); |
296 | venc_write_reg(venc, VENC_L21__WC_CTL, val: config->l21__wc_ctl); |
297 | venc_write_reg(venc, VENC_SAVID__EAVID, val: config->savid__eavid); |
298 | venc_write_reg(venc, VENC_FLEN__FAL, val: config->flen__fal); |
299 | venc_write_reg(venc, VENC_LAL__PHASE_RESET, val: config->lal__phase_reset); |
300 | venc_write_reg(venc, VENC_HS_INT_START_STOP_X, |
301 | val: config->hs_int_start_stop_x); |
302 | venc_write_reg(venc, VENC_HS_EXT_START_STOP_X, |
303 | val: config->hs_ext_start_stop_x); |
304 | venc_write_reg(venc, VENC_VS_INT_START_X, val: config->vs_int_start_x); |
305 | venc_write_reg(venc, VENC_VS_INT_STOP_X__VS_INT_START_Y, |
306 | val: config->vs_int_stop_x__vs_int_start_y); |
307 | venc_write_reg(venc, VENC_VS_INT_STOP_Y__VS_EXT_START_X, |
308 | val: config->vs_int_stop_y__vs_ext_start_x); |
309 | venc_write_reg(venc, VENC_VS_EXT_STOP_X__VS_EXT_START_Y, |
310 | val: config->vs_ext_stop_x__vs_ext_start_y); |
311 | venc_write_reg(venc, VENC_VS_EXT_STOP_Y, val: config->vs_ext_stop_y); |
312 | venc_write_reg(venc, VENC_AVID_START_STOP_X, val: config->avid_start_stop_x); |
313 | venc_write_reg(venc, VENC_AVID_START_STOP_Y, val: config->avid_start_stop_y); |
314 | venc_write_reg(venc, VENC_FID_INT_START_X__FID_INT_START_Y, |
315 | val: config->fid_int_start_x__fid_int_start_y); |
316 | venc_write_reg(venc, VENC_FID_INT_OFFSET_Y__FID_EXT_START_X, |
317 | val: config->fid_int_offset_y__fid_ext_start_x); |
318 | venc_write_reg(venc, VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y, |
319 | val: config->fid_ext_start_y__fid_ext_offset_y); |
320 | |
321 | venc_write_reg(venc, VENC_DAC_B__DAC_C, |
322 | val: venc_read_reg(venc, VENC_DAC_B__DAC_C)); |
323 | venc_write_reg(venc, VENC_VIDOUT_CTRL, val: config->vidout_ctrl); |
324 | venc_write_reg(venc, VENC_HFLTR_CTRL, val: config->hfltr_ctrl); |
325 | venc_write_reg(venc, VENC_X_COLOR, val: config->x_color); |
326 | venc_write_reg(venc, VENC_LINE21, val: config->line21); |
327 | venc_write_reg(venc, VENC_LN_SEL, val: config->ln_sel); |
328 | venc_write_reg(venc, VENC_HTRIGGER_VTRIGGER, val: config->htrigger_vtrigger); |
329 | venc_write_reg(venc, VENC_TVDETGP_INT_START_STOP_X, |
330 | val: config->tvdetgp_int_start_stop_x); |
331 | venc_write_reg(venc, VENC_TVDETGP_INT_START_STOP_Y, |
332 | val: config->tvdetgp_int_start_stop_y); |
333 | venc_write_reg(venc, VENC_GEN_CTRL, val: config->gen_ctrl); |
334 | venc_write_reg(venc, VENC_F_CONTROL, val: config->f_control); |
335 | venc_write_reg(venc, VENC_SYNC_CTRL, val: config->sync_ctrl); |
336 | } |
337 | |
338 | static void venc_reset(struct venc_device *venc) |
339 | { |
340 | int t = 1000; |
341 | |
342 | venc_write_reg(venc, VENC_F_CONTROL, val: 1<<8); |
343 | while (venc_read_reg(venc, VENC_F_CONTROL) & (1<<8)) { |
344 | if (--t == 0) { |
345 | DSSERR("Failed to reset venc\n" ); |
346 | return; |
347 | } |
348 | } |
349 | |
350 | #ifdef CONFIG_OMAP2_DSS_SLEEP_AFTER_VENC_RESET |
351 | /* the magical sleep that makes things work */ |
352 | /* XXX more info? What bug this circumvents? */ |
353 | msleep(20); |
354 | #endif |
355 | } |
356 | |
357 | static int venc_runtime_get(struct venc_device *venc) |
358 | { |
359 | int r; |
360 | |
361 | DSSDBG("venc_runtime_get\n" ); |
362 | |
363 | r = pm_runtime_get_sync(dev: &venc->pdev->dev); |
364 | if (WARN_ON(r < 0)) { |
365 | pm_runtime_put_noidle(dev: &venc->pdev->dev); |
366 | return r; |
367 | } |
368 | return 0; |
369 | } |
370 | |
371 | static void venc_runtime_put(struct venc_device *venc) |
372 | { |
373 | int r; |
374 | |
375 | DSSDBG("venc_runtime_put\n" ); |
376 | |
377 | r = pm_runtime_put_sync(dev: &venc->pdev->dev); |
378 | WARN_ON(r < 0 && r != -ENOSYS); |
379 | } |
380 | |
381 | static int venc_power_on(struct venc_device *venc) |
382 | { |
383 | u32 l; |
384 | int r; |
385 | |
386 | r = venc_runtime_get(venc); |
387 | if (r) |
388 | goto err0; |
389 | |
390 | venc_reset(venc); |
391 | venc_write_config(venc, config: venc->config); |
392 | |
393 | dss_set_venc_output(dss: venc->dss, type: venc->type); |
394 | dss_set_dac_pwrdn_bgz(dss: venc->dss, enable: 1); |
395 | |
396 | l = 0; |
397 | |
398 | if (venc->type == OMAP_DSS_VENC_TYPE_COMPOSITE) |
399 | l |= 1 << 1; |
400 | else /* S-Video */ |
401 | l |= (1 << 0) | (1 << 2); |
402 | |
403 | if (venc->invert_polarity == false) |
404 | l |= 1 << 3; |
405 | |
406 | venc_write_reg(venc, VENC_OUTPUT_CONTROL, val: l); |
407 | |
408 | r = regulator_enable(regulator: venc->vdda_dac_reg); |
409 | if (r) |
410 | goto err1; |
411 | |
412 | r = dss_mgr_enable(dssdev: &venc->output); |
413 | if (r) |
414 | goto err2; |
415 | |
416 | return 0; |
417 | |
418 | err2: |
419 | regulator_disable(regulator: venc->vdda_dac_reg); |
420 | err1: |
421 | venc_write_reg(venc, VENC_OUTPUT_CONTROL, val: 0); |
422 | dss_set_dac_pwrdn_bgz(dss: venc->dss, enable: 0); |
423 | |
424 | venc_runtime_put(venc); |
425 | err0: |
426 | return r; |
427 | } |
428 | |
429 | static void venc_power_off(struct venc_device *venc) |
430 | { |
431 | venc_write_reg(venc, VENC_OUTPUT_CONTROL, val: 0); |
432 | dss_set_dac_pwrdn_bgz(dss: venc->dss, enable: 0); |
433 | |
434 | dss_mgr_disable(dssdev: &venc->output); |
435 | |
436 | regulator_disable(regulator: venc->vdda_dac_reg); |
437 | |
438 | venc_runtime_put(venc); |
439 | } |
440 | |
441 | static enum venc_videomode venc_get_videomode(const struct drm_display_mode *mode) |
442 | { |
443 | if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) |
444 | return VENC_MODE_UNKNOWN; |
445 | |
446 | if (mode->clock == omap_dss_pal_mode.clock && |
447 | mode->hdisplay == omap_dss_pal_mode.hdisplay && |
448 | mode->vdisplay == omap_dss_pal_mode.vdisplay) |
449 | return VENC_MODE_PAL; |
450 | |
451 | if (mode->clock == omap_dss_ntsc_mode.clock && |
452 | mode->hdisplay == omap_dss_ntsc_mode.hdisplay && |
453 | mode->vdisplay == omap_dss_ntsc_mode.vdisplay) |
454 | return VENC_MODE_NTSC; |
455 | |
456 | return VENC_MODE_UNKNOWN; |
457 | } |
458 | |
459 | static int venc_dump_regs(struct seq_file *s, void *p) |
460 | { |
461 | struct venc_device *venc = s->private; |
462 | |
463 | #define DUMPREG(venc, r) \ |
464 | seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(venc, r)) |
465 | |
466 | if (venc_runtime_get(venc)) |
467 | return 0; |
468 | |
469 | DUMPREG(venc, VENC_F_CONTROL); |
470 | DUMPREG(venc, VENC_VIDOUT_CTRL); |
471 | DUMPREG(venc, VENC_SYNC_CTRL); |
472 | DUMPREG(venc, VENC_LLEN); |
473 | DUMPREG(venc, VENC_FLENS); |
474 | DUMPREG(venc, VENC_HFLTR_CTRL); |
475 | DUMPREG(venc, VENC_CC_CARR_WSS_CARR); |
476 | DUMPREG(venc, VENC_C_PHASE); |
477 | DUMPREG(venc, VENC_GAIN_U); |
478 | DUMPREG(venc, VENC_GAIN_V); |
479 | DUMPREG(venc, VENC_GAIN_Y); |
480 | DUMPREG(venc, VENC_BLACK_LEVEL); |
481 | DUMPREG(venc, VENC_BLANK_LEVEL); |
482 | DUMPREG(venc, VENC_X_COLOR); |
483 | DUMPREG(venc, VENC_M_CONTROL); |
484 | DUMPREG(venc, VENC_BSTAMP_WSS_DATA); |
485 | DUMPREG(venc, VENC_S_CARR); |
486 | DUMPREG(venc, VENC_LINE21); |
487 | DUMPREG(venc, VENC_LN_SEL); |
488 | DUMPREG(venc, VENC_L21__WC_CTL); |
489 | DUMPREG(venc, VENC_HTRIGGER_VTRIGGER); |
490 | DUMPREG(venc, VENC_SAVID__EAVID); |
491 | DUMPREG(venc, VENC_FLEN__FAL); |
492 | DUMPREG(venc, VENC_LAL__PHASE_RESET); |
493 | DUMPREG(venc, VENC_HS_INT_START_STOP_X); |
494 | DUMPREG(venc, VENC_HS_EXT_START_STOP_X); |
495 | DUMPREG(venc, VENC_VS_INT_START_X); |
496 | DUMPREG(venc, VENC_VS_INT_STOP_X__VS_INT_START_Y); |
497 | DUMPREG(venc, VENC_VS_INT_STOP_Y__VS_EXT_START_X); |
498 | DUMPREG(venc, VENC_VS_EXT_STOP_X__VS_EXT_START_Y); |
499 | DUMPREG(venc, VENC_VS_EXT_STOP_Y); |
500 | DUMPREG(venc, VENC_AVID_START_STOP_X); |
501 | DUMPREG(venc, VENC_AVID_START_STOP_Y); |
502 | DUMPREG(venc, VENC_FID_INT_START_X__FID_INT_START_Y); |
503 | DUMPREG(venc, VENC_FID_INT_OFFSET_Y__FID_EXT_START_X); |
504 | DUMPREG(venc, VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y); |
505 | DUMPREG(venc, VENC_TVDETGP_INT_START_STOP_X); |
506 | DUMPREG(venc, VENC_TVDETGP_INT_START_STOP_Y); |
507 | DUMPREG(venc, VENC_GEN_CTRL); |
508 | DUMPREG(venc, VENC_OUTPUT_CONTROL); |
509 | DUMPREG(venc, VENC_OUTPUT_TEST); |
510 | |
511 | venc_runtime_put(venc); |
512 | |
513 | #undef DUMPREG |
514 | return 0; |
515 | } |
516 | |
517 | static int venc_get_clocks(struct venc_device *venc) |
518 | { |
519 | struct clk *clk; |
520 | |
521 | if (venc->requires_tv_dac_clk) { |
522 | clk = devm_clk_get(dev: &venc->pdev->dev, id: "tv_dac_clk" ); |
523 | if (IS_ERR(ptr: clk)) { |
524 | DSSERR("can't get tv_dac_clk\n" ); |
525 | return PTR_ERR(ptr: clk); |
526 | } |
527 | } else { |
528 | clk = NULL; |
529 | } |
530 | |
531 | venc->tv_dac_clk = clk; |
532 | |
533 | return 0; |
534 | } |
535 | |
536 | /* ----------------------------------------------------------------------------- |
537 | * DRM Bridge Operations |
538 | */ |
539 | |
540 | static int venc_bridge_attach(struct drm_bridge *bridge, |
541 | enum drm_bridge_attach_flags flags) |
542 | { |
543 | struct venc_device *venc = drm_bridge_to_venc(bridge); |
544 | |
545 | if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) |
546 | return -EINVAL; |
547 | |
548 | return drm_bridge_attach(encoder: bridge->encoder, bridge: venc->output.next_bridge, |
549 | previous: bridge, flags); |
550 | } |
551 | |
552 | static enum drm_mode_status |
553 | venc_bridge_mode_valid(struct drm_bridge *bridge, |
554 | const struct drm_display_info *info, |
555 | const struct drm_display_mode *mode) |
556 | { |
557 | switch (venc_get_videomode(mode)) { |
558 | case VENC_MODE_PAL: |
559 | case VENC_MODE_NTSC: |
560 | return MODE_OK; |
561 | |
562 | default: |
563 | return MODE_BAD; |
564 | } |
565 | } |
566 | |
567 | static bool venc_bridge_mode_fixup(struct drm_bridge *bridge, |
568 | const struct drm_display_mode *mode, |
569 | struct drm_display_mode *adjusted_mode) |
570 | { |
571 | const struct drm_display_mode *venc_mode; |
572 | |
573 | switch (venc_get_videomode(mode: adjusted_mode)) { |
574 | case VENC_MODE_PAL: |
575 | venc_mode = &omap_dss_pal_mode; |
576 | break; |
577 | |
578 | case VENC_MODE_NTSC: |
579 | venc_mode = &omap_dss_ntsc_mode; |
580 | break; |
581 | |
582 | default: |
583 | return false; |
584 | } |
585 | |
586 | drm_mode_copy(dst: adjusted_mode, src: venc_mode); |
587 | drm_mode_set_crtcinfo(p: adjusted_mode, CRTC_INTERLACE_HALVE_V); |
588 | drm_mode_set_name(mode: adjusted_mode); |
589 | |
590 | return true; |
591 | } |
592 | |
593 | static void venc_bridge_mode_set(struct drm_bridge *bridge, |
594 | const struct drm_display_mode *mode, |
595 | const struct drm_display_mode *adjusted_mode) |
596 | { |
597 | struct venc_device *venc = drm_bridge_to_venc(bridge); |
598 | enum venc_videomode venc_mode = venc_get_videomode(mode: adjusted_mode); |
599 | |
600 | switch (venc_mode) { |
601 | default: |
602 | WARN_ON_ONCE(1); |
603 | fallthrough; |
604 | case VENC_MODE_PAL: |
605 | venc->config = &venc_config_pal_trm; |
606 | break; |
607 | |
608 | case VENC_MODE_NTSC: |
609 | venc->config = &venc_config_ntsc_trm; |
610 | break; |
611 | } |
612 | |
613 | dispc_set_tv_pclk(dispc: venc->dss->dispc, pclk: 13500000); |
614 | } |
615 | |
616 | static void venc_bridge_enable(struct drm_bridge *bridge) |
617 | { |
618 | struct venc_device *venc = drm_bridge_to_venc(bridge); |
619 | |
620 | venc_power_on(venc); |
621 | } |
622 | |
623 | static void venc_bridge_disable(struct drm_bridge *bridge) |
624 | { |
625 | struct venc_device *venc = drm_bridge_to_venc(bridge); |
626 | |
627 | venc_power_off(venc); |
628 | } |
629 | |
630 | static int venc_bridge_get_modes(struct drm_bridge *bridge, |
631 | struct drm_connector *connector) |
632 | { |
633 | static const struct drm_display_mode *modes[] = { |
634 | &omap_dss_pal_mode, |
635 | &omap_dss_ntsc_mode, |
636 | }; |
637 | unsigned int i; |
638 | |
639 | for (i = 0; i < ARRAY_SIZE(modes); ++i) { |
640 | struct drm_display_mode *mode; |
641 | |
642 | mode = drm_mode_duplicate(dev: connector->dev, mode: modes[i]); |
643 | if (!mode) |
644 | return i; |
645 | |
646 | mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; |
647 | drm_mode_set_name(mode); |
648 | drm_mode_probed_add(connector, mode); |
649 | } |
650 | |
651 | return ARRAY_SIZE(modes); |
652 | } |
653 | |
654 | static const struct drm_bridge_funcs venc_bridge_funcs = { |
655 | .attach = venc_bridge_attach, |
656 | .mode_valid = venc_bridge_mode_valid, |
657 | .mode_fixup = venc_bridge_mode_fixup, |
658 | .mode_set = venc_bridge_mode_set, |
659 | .enable = venc_bridge_enable, |
660 | .disable = venc_bridge_disable, |
661 | .get_modes = venc_bridge_get_modes, |
662 | }; |
663 | |
664 | static void venc_bridge_init(struct venc_device *venc) |
665 | { |
666 | venc->bridge.funcs = &venc_bridge_funcs; |
667 | venc->bridge.of_node = venc->pdev->dev.of_node; |
668 | venc->bridge.ops = DRM_BRIDGE_OP_MODES; |
669 | venc->bridge.type = DRM_MODE_CONNECTOR_SVIDEO; |
670 | venc->bridge.interlace_allowed = true; |
671 | |
672 | drm_bridge_add(bridge: &venc->bridge); |
673 | } |
674 | |
675 | static void venc_bridge_cleanup(struct venc_device *venc) |
676 | { |
677 | drm_bridge_remove(bridge: &venc->bridge); |
678 | } |
679 | |
680 | /* ----------------------------------------------------------------------------- |
681 | * Component Bind & Unbind |
682 | */ |
683 | |
684 | static int venc_bind(struct device *dev, struct device *master, void *data) |
685 | { |
686 | struct dss_device *dss = dss_get_device(dev: master); |
687 | struct venc_device *venc = dev_get_drvdata(dev); |
688 | u8 rev_id; |
689 | int r; |
690 | |
691 | venc->dss = dss; |
692 | |
693 | r = venc_runtime_get(venc); |
694 | if (r) |
695 | return r; |
696 | |
697 | rev_id = (u8)(venc_read_reg(venc, VENC_REV_ID) & 0xff); |
698 | dev_dbg(dev, "OMAP VENC rev %d\n" , rev_id); |
699 | |
700 | venc_runtime_put(venc); |
701 | |
702 | venc->debugfs = dss_debugfs_create_file(dss, name: "venc" , show_fn: venc_dump_regs, |
703 | data: venc); |
704 | |
705 | return 0; |
706 | } |
707 | |
708 | static void venc_unbind(struct device *dev, struct device *master, void *data) |
709 | { |
710 | struct venc_device *venc = dev_get_drvdata(dev); |
711 | |
712 | dss_debugfs_remove_file(entry: venc->debugfs); |
713 | } |
714 | |
715 | static const struct component_ops venc_component_ops = { |
716 | .bind = venc_bind, |
717 | .unbind = venc_unbind, |
718 | }; |
719 | |
720 | /* ----------------------------------------------------------------------------- |
721 | * Probe & Remove, Suspend & Resume |
722 | */ |
723 | |
724 | static int venc_init_output(struct venc_device *venc) |
725 | { |
726 | struct omap_dss_device *out = &venc->output; |
727 | int r; |
728 | |
729 | venc_bridge_init(venc); |
730 | |
731 | out->dev = &venc->pdev->dev; |
732 | out->id = OMAP_DSS_OUTPUT_VENC; |
733 | out->type = OMAP_DISPLAY_TYPE_VENC; |
734 | out->name = "venc.0" ; |
735 | out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; |
736 | out->of_port = 0; |
737 | |
738 | r = omapdss_device_init_output(out, local_bridge: &venc->bridge); |
739 | if (r < 0) { |
740 | venc_bridge_cleanup(venc); |
741 | return r; |
742 | } |
743 | |
744 | omapdss_device_register(dssdev: out); |
745 | |
746 | return 0; |
747 | } |
748 | |
749 | static void venc_uninit_output(struct venc_device *venc) |
750 | { |
751 | omapdss_device_unregister(dssdev: &venc->output); |
752 | omapdss_device_cleanup_output(out: &venc->output); |
753 | |
754 | venc_bridge_cleanup(venc); |
755 | } |
756 | |
757 | static int venc_probe_of(struct venc_device *venc) |
758 | { |
759 | struct device_node *node = venc->pdev->dev.of_node; |
760 | struct device_node *ep; |
761 | u32 channels; |
762 | int r; |
763 | |
764 | ep = of_graph_get_endpoint_by_regs(parent: node, port_reg: 0, reg: 0); |
765 | if (!ep) |
766 | return 0; |
767 | |
768 | venc->invert_polarity = of_property_read_bool(np: ep, propname: "ti,invert-polarity" ); |
769 | |
770 | r = of_property_read_u32(np: ep, propname: "ti,channels" , out_value: &channels); |
771 | if (r) { |
772 | dev_err(&venc->pdev->dev, |
773 | "failed to read property 'ti,channels': %d\n" , r); |
774 | goto err; |
775 | } |
776 | |
777 | switch (channels) { |
778 | case 1: |
779 | venc->type = OMAP_DSS_VENC_TYPE_COMPOSITE; |
780 | break; |
781 | case 2: |
782 | venc->type = OMAP_DSS_VENC_TYPE_SVIDEO; |
783 | break; |
784 | default: |
785 | dev_err(&venc->pdev->dev, "bad channel property '%d'\n" , |
786 | channels); |
787 | r = -EINVAL; |
788 | goto err; |
789 | } |
790 | |
791 | of_node_put(node: ep); |
792 | |
793 | return 0; |
794 | |
795 | err: |
796 | of_node_put(node: ep); |
797 | return r; |
798 | } |
799 | |
800 | static const struct soc_device_attribute venc_soc_devices[] = { |
801 | { .machine = "OMAP3[45]*" }, |
802 | { .machine = "AM35*" }, |
803 | { /* sentinel */ } |
804 | }; |
805 | |
806 | static int venc_probe(struct platform_device *pdev) |
807 | { |
808 | struct venc_device *venc; |
809 | int r; |
810 | |
811 | venc = kzalloc(size: sizeof(*venc), GFP_KERNEL); |
812 | if (!venc) |
813 | return -ENOMEM; |
814 | |
815 | venc->pdev = pdev; |
816 | |
817 | platform_set_drvdata(pdev, data: venc); |
818 | |
819 | /* The OMAP34xx, OMAP35xx and AM35xx VENC require the TV DAC clock. */ |
820 | if (soc_device_match(matches: venc_soc_devices)) |
821 | venc->requires_tv_dac_clk = true; |
822 | |
823 | venc->config = &venc_config_pal_trm; |
824 | |
825 | venc->base = devm_platform_ioremap_resource(pdev, index: 0); |
826 | if (IS_ERR(ptr: venc->base)) { |
827 | r = PTR_ERR(ptr: venc->base); |
828 | goto err_free; |
829 | } |
830 | |
831 | venc->vdda_dac_reg = devm_regulator_get(dev: &pdev->dev, id: "vdda" ); |
832 | if (IS_ERR(ptr: venc->vdda_dac_reg)) { |
833 | r = PTR_ERR(ptr: venc->vdda_dac_reg); |
834 | if (r != -EPROBE_DEFER) |
835 | DSSERR("can't get VDDA_DAC regulator\n" ); |
836 | goto err_free; |
837 | } |
838 | |
839 | r = venc_get_clocks(venc); |
840 | if (r) |
841 | goto err_free; |
842 | |
843 | r = venc_probe_of(venc); |
844 | if (r) |
845 | goto err_free; |
846 | |
847 | pm_runtime_enable(dev: &pdev->dev); |
848 | |
849 | r = venc_init_output(venc); |
850 | if (r) |
851 | goto err_pm_disable; |
852 | |
853 | r = component_add(&pdev->dev, &venc_component_ops); |
854 | if (r) |
855 | goto err_uninit_output; |
856 | |
857 | return 0; |
858 | |
859 | err_uninit_output: |
860 | venc_uninit_output(venc); |
861 | err_pm_disable: |
862 | pm_runtime_disable(dev: &pdev->dev); |
863 | err_free: |
864 | kfree(objp: venc); |
865 | return r; |
866 | } |
867 | |
868 | static void venc_remove(struct platform_device *pdev) |
869 | { |
870 | struct venc_device *venc = platform_get_drvdata(pdev); |
871 | |
872 | component_del(&pdev->dev, &venc_component_ops); |
873 | |
874 | venc_uninit_output(venc); |
875 | |
876 | pm_runtime_disable(dev: &pdev->dev); |
877 | |
878 | kfree(objp: venc); |
879 | } |
880 | |
881 | static __maybe_unused int venc_runtime_suspend(struct device *dev) |
882 | { |
883 | struct venc_device *venc = dev_get_drvdata(dev); |
884 | |
885 | if (venc->tv_dac_clk) |
886 | clk_disable_unprepare(clk: venc->tv_dac_clk); |
887 | |
888 | return 0; |
889 | } |
890 | |
891 | static __maybe_unused int venc_runtime_resume(struct device *dev) |
892 | { |
893 | struct venc_device *venc = dev_get_drvdata(dev); |
894 | |
895 | if (venc->tv_dac_clk) |
896 | clk_prepare_enable(clk: venc->tv_dac_clk); |
897 | |
898 | return 0; |
899 | } |
900 | |
901 | static const struct dev_pm_ops venc_pm_ops = { |
902 | SET_RUNTIME_PM_OPS(venc_runtime_suspend, venc_runtime_resume, NULL) |
903 | SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) |
904 | }; |
905 | |
906 | static const struct of_device_id venc_of_match[] = { |
907 | { .compatible = "ti,omap2-venc" , }, |
908 | { .compatible = "ti,omap3-venc" , }, |
909 | { .compatible = "ti,omap4-venc" , }, |
910 | {}, |
911 | }; |
912 | |
913 | struct platform_driver omap_venchw_driver = { |
914 | .probe = venc_probe, |
915 | .remove_new = venc_remove, |
916 | .driver = { |
917 | .name = "omapdss_venc" , |
918 | .pm = &venc_pm_ops, |
919 | .of_match_table = venc_of_match, |
920 | .suppress_bind_attrs = true, |
921 | }, |
922 | }; |
923 | |