1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Driver for Analog Devices ADV748X HDMI receiver and Component Processor (CP) |
4 | * |
5 | * Copyright (C) 2017 Renesas Electronics Corp. |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/mutex.h> |
10 | |
11 | #include <media/v4l2-ctrls.h> |
12 | #include <media/v4l2-device.h> |
13 | #include <media/v4l2-dv-timings.h> |
14 | #include <media/v4l2-ioctl.h> |
15 | |
16 | #include <uapi/linux/v4l2-dv-timings.h> |
17 | |
18 | #include "adv748x.h" |
19 | |
20 | /* ----------------------------------------------------------------------------- |
21 | * HDMI and CP |
22 | */ |
23 | |
24 | #define ADV748X_HDMI_MIN_WIDTH 640 |
25 | #define ADV748X_HDMI_MAX_WIDTH 1920 |
26 | #define ADV748X_HDMI_MIN_HEIGHT 480 |
27 | #define ADV748X_HDMI_MAX_HEIGHT 1200 |
28 | |
29 | /* V4L2_DV_BT_CEA_720X480I59_94 - 0.5 MHz */ |
30 | #define ADV748X_HDMI_MIN_PIXELCLOCK 13000000 |
31 | /* V4L2_DV_BT_DMT_1600X1200P60 */ |
32 | #define ADV748X_HDMI_MAX_PIXELCLOCK 162000000 |
33 | |
34 | static const struct v4l2_dv_timings_cap adv748x_hdmi_timings_cap = { |
35 | .type = V4L2_DV_BT_656_1120, |
36 | /* keep this initialization for compatibility with GCC < 4.4.6 */ |
37 | .reserved = { 0 }, |
38 | |
39 | V4L2_INIT_BT_TIMINGS(ADV748X_HDMI_MIN_WIDTH, ADV748X_HDMI_MAX_WIDTH, |
40 | ADV748X_HDMI_MIN_HEIGHT, ADV748X_HDMI_MAX_HEIGHT, |
41 | ADV748X_HDMI_MIN_PIXELCLOCK, |
42 | ADV748X_HDMI_MAX_PIXELCLOCK, |
43 | V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT, |
44 | V4L2_DV_BT_CAP_PROGRESSIVE) |
45 | }; |
46 | |
47 | struct adv748x_hdmi_video_standards { |
48 | struct v4l2_dv_timings timings; |
49 | u8 vid_std; |
50 | u8 v_freq; |
51 | }; |
52 | |
53 | static const struct adv748x_hdmi_video_standards |
54 | adv748x_hdmi_video_standards[] = { |
55 | { V4L2_DV_BT_CEA_720X480P59_94, 0x4a, 0x00 }, |
56 | { V4L2_DV_BT_CEA_720X576P50, 0x4b, 0x00 }, |
57 | { V4L2_DV_BT_CEA_1280X720P60, 0x53, 0x00 }, |
58 | { V4L2_DV_BT_CEA_1280X720P50, 0x53, 0x01 }, |
59 | { V4L2_DV_BT_CEA_1280X720P30, 0x53, 0x02 }, |
60 | { V4L2_DV_BT_CEA_1280X720P25, 0x53, 0x03 }, |
61 | { V4L2_DV_BT_CEA_1280X720P24, 0x53, 0x04 }, |
62 | { V4L2_DV_BT_CEA_1920X1080P60, 0x5e, 0x00 }, |
63 | { V4L2_DV_BT_CEA_1920X1080P50, 0x5e, 0x01 }, |
64 | { V4L2_DV_BT_CEA_1920X1080P30, 0x5e, 0x02 }, |
65 | { V4L2_DV_BT_CEA_1920X1080P25, 0x5e, 0x03 }, |
66 | { V4L2_DV_BT_CEA_1920X1080P24, 0x5e, 0x04 }, |
67 | /* SVGA */ |
68 | { V4L2_DV_BT_DMT_800X600P56, 0x80, 0x00 }, |
69 | { V4L2_DV_BT_DMT_800X600P60, 0x81, 0x00 }, |
70 | { V4L2_DV_BT_DMT_800X600P72, 0x82, 0x00 }, |
71 | { V4L2_DV_BT_DMT_800X600P75, 0x83, 0x00 }, |
72 | { V4L2_DV_BT_DMT_800X600P85, 0x84, 0x00 }, |
73 | /* SXGA */ |
74 | { V4L2_DV_BT_DMT_1280X1024P60, 0x85, 0x00 }, |
75 | { V4L2_DV_BT_DMT_1280X1024P75, 0x86, 0x00 }, |
76 | /* VGA */ |
77 | { V4L2_DV_BT_DMT_640X480P60, 0x88, 0x00 }, |
78 | { V4L2_DV_BT_DMT_640X480P72, 0x89, 0x00 }, |
79 | { V4L2_DV_BT_DMT_640X480P75, 0x8a, 0x00 }, |
80 | { V4L2_DV_BT_DMT_640X480P85, 0x8b, 0x00 }, |
81 | /* XGA */ |
82 | { V4L2_DV_BT_DMT_1024X768P60, 0x8c, 0x00 }, |
83 | { V4L2_DV_BT_DMT_1024X768P70, 0x8d, 0x00 }, |
84 | { V4L2_DV_BT_DMT_1024X768P75, 0x8e, 0x00 }, |
85 | { V4L2_DV_BT_DMT_1024X768P85, 0x8f, 0x00 }, |
86 | /* UXGA */ |
87 | { V4L2_DV_BT_DMT_1600X1200P60, 0x96, 0x00 }, |
88 | }; |
89 | |
90 | static void adv748x_hdmi_fill_format(struct adv748x_hdmi *hdmi, |
91 | struct v4l2_mbus_framefmt *fmt) |
92 | { |
93 | memset(fmt, 0, sizeof(*fmt)); |
94 | |
95 | fmt->code = MEDIA_BUS_FMT_RGB888_1X24; |
96 | fmt->field = hdmi->timings.bt.interlaced ? |
97 | V4L2_FIELD_ALTERNATE : V4L2_FIELD_NONE; |
98 | |
99 | /* TODO: The colorspace depends on the AVI InfoFrame contents */ |
100 | fmt->colorspace = V4L2_COLORSPACE_SRGB; |
101 | |
102 | fmt->width = hdmi->timings.bt.width; |
103 | fmt->height = hdmi->timings.bt.height; |
104 | |
105 | if (fmt->field == V4L2_FIELD_ALTERNATE) |
106 | fmt->height /= 2; |
107 | } |
108 | |
109 | static void adv748x_fill_optional_dv_timings(struct v4l2_dv_timings *timings) |
110 | { |
111 | v4l2_find_dv_timings_cap(t: timings, cap: &adv748x_hdmi_timings_cap, |
112 | pclock_delta: 250000, NULL, NULL); |
113 | } |
114 | |
115 | static bool adv748x_hdmi_has_signal(struct adv748x_state *state) |
116 | { |
117 | int val; |
118 | |
119 | /* Check that VERT_FILTER and DE_REGEN is locked */ |
120 | val = hdmi_read(state, ADV748X_HDMI_LW1); |
121 | return (val & ADV748X_HDMI_LW1_VERT_FILTER) && |
122 | (val & ADV748X_HDMI_LW1_DE_REGEN); |
123 | } |
124 | |
125 | static int adv748x_hdmi_read_pixelclock(struct adv748x_state *state) |
126 | { |
127 | int a, b; |
128 | |
129 | a = hdmi_read(state, ADV748X_HDMI_TMDS_1); |
130 | b = hdmi_read(state, ADV748X_HDMI_TMDS_2); |
131 | if (a < 0 || b < 0) |
132 | return -ENODATA; |
133 | |
134 | /* |
135 | * The high 9 bits store TMDS frequency measurement in MHz |
136 | * The low 7 bits of TMDS_2 store the 7-bit TMDS fractional frequency |
137 | * measurement in 1/128 MHz |
138 | */ |
139 | return ((a << 1) | (b >> 7)) * 1000000 + (b & 0x7f) * 1000000 / 128; |
140 | } |
141 | |
142 | /* |
143 | * adv748x_hdmi_set_de_timings: Adjust horizontal picture offset through DE |
144 | * |
145 | * HDMI CP uses a Data Enable synchronisation timing reference |
146 | * |
147 | * Vary the leading and trailing edge position of the DE signal output by the CP |
148 | * core. Values are stored as signed-twos-complement in one-pixel-clock units |
149 | * |
150 | * The start and end are shifted equally by the 10-bit shift value. |
151 | */ |
152 | static void adv748x_hdmi_set_de_timings(struct adv748x_state *state, int shift) |
153 | { |
154 | u8 high, low; |
155 | |
156 | /* POS_HIGH stores bits 8 and 9 of both the start and end */ |
157 | high = ADV748X_CP_DE_POS_HIGH_SET; |
158 | high |= (shift & 0x300) >> 8; |
159 | low = shift & 0xff; |
160 | |
161 | /* The sequence of the writes is important and must be followed */ |
162 | cp_write(state, ADV748X_CP_DE_POS_HIGH, high); |
163 | cp_write(state, ADV748X_CP_DE_POS_END_LOW, low); |
164 | |
165 | high |= (shift & 0x300) >> 6; |
166 | |
167 | cp_write(state, ADV748X_CP_DE_POS_HIGH, high); |
168 | cp_write(state, ADV748X_CP_DE_POS_START_LOW, low); |
169 | } |
170 | |
171 | static int adv748x_hdmi_set_video_timings(struct adv748x_state *state, |
172 | const struct v4l2_dv_timings *timings) |
173 | { |
174 | const struct adv748x_hdmi_video_standards *stds = |
175 | adv748x_hdmi_video_standards; |
176 | unsigned int i; |
177 | |
178 | for (i = 0; i < ARRAY_SIZE(adv748x_hdmi_video_standards); i++) { |
179 | if (v4l2_match_dv_timings(measured: timings, standard: &stds[i].timings, pclock_delta: 250000, |
180 | match_reduced_fps: false)) |
181 | break; |
182 | } |
183 | |
184 | if (i >= ARRAY_SIZE(adv748x_hdmi_video_standards)) |
185 | return -EINVAL; |
186 | |
187 | /* |
188 | * When setting cp_vid_std to either 720p, 1080i, or 1080p, the video |
189 | * will get shifted horizontally to the left in active video mode. |
190 | * The de_h_start and de_h_end controls are used to centre the picture |
191 | * correctly |
192 | */ |
193 | switch (stds[i].vid_std) { |
194 | case 0x53: /* 720p */ |
195 | adv748x_hdmi_set_de_timings(state, shift: -40); |
196 | break; |
197 | case 0x54: /* 1080i */ |
198 | case 0x5e: /* 1080p */ |
199 | adv748x_hdmi_set_de_timings(state, shift: -44); |
200 | break; |
201 | default: |
202 | adv748x_hdmi_set_de_timings(state, shift: 0); |
203 | break; |
204 | } |
205 | |
206 | io_write(state, ADV748X_IO_VID_STD, stds[i].vid_std); |
207 | io_clrset(state, ADV748X_IO_DATAPATH, ADV748X_IO_DATAPATH_VFREQ_M, |
208 | stds[i].v_freq << ADV748X_IO_DATAPATH_VFREQ_SHIFT); |
209 | |
210 | return 0; |
211 | } |
212 | |
213 | /* ----------------------------------------------------------------------------- |
214 | * v4l2_subdev_video_ops |
215 | */ |
216 | |
217 | static int adv748x_hdmi_s_dv_timings(struct v4l2_subdev *sd, |
218 | struct v4l2_dv_timings *timings) |
219 | { |
220 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); |
221 | struct adv748x_state *state = adv748x_hdmi_to_state(hdmi); |
222 | int ret; |
223 | |
224 | if (!timings) |
225 | return -EINVAL; |
226 | |
227 | if (v4l2_match_dv_timings(measured: &hdmi->timings, standard: timings, pclock_delta: 0, match_reduced_fps: false)) |
228 | return 0; |
229 | |
230 | if (!v4l2_valid_dv_timings(t: timings, cap: &adv748x_hdmi_timings_cap, |
231 | NULL, NULL)) |
232 | return -ERANGE; |
233 | |
234 | adv748x_fill_optional_dv_timings(timings); |
235 | |
236 | mutex_lock(&state->mutex); |
237 | |
238 | ret = adv748x_hdmi_set_video_timings(state, timings); |
239 | if (ret) |
240 | goto error; |
241 | |
242 | hdmi->timings = *timings; |
243 | |
244 | cp_clrset(state, ADV748X_CP_VID_ADJ_2, ADV748X_CP_VID_ADJ_2_INTERLACED, |
245 | timings->bt.interlaced ? |
246 | ADV748X_CP_VID_ADJ_2_INTERLACED : 0); |
247 | |
248 | mutex_unlock(lock: &state->mutex); |
249 | |
250 | return 0; |
251 | |
252 | error: |
253 | mutex_unlock(lock: &state->mutex); |
254 | return ret; |
255 | } |
256 | |
257 | static int adv748x_hdmi_g_dv_timings(struct v4l2_subdev *sd, |
258 | struct v4l2_dv_timings *timings) |
259 | { |
260 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); |
261 | struct adv748x_state *state = adv748x_hdmi_to_state(hdmi); |
262 | |
263 | mutex_lock(&state->mutex); |
264 | |
265 | *timings = hdmi->timings; |
266 | |
267 | mutex_unlock(lock: &state->mutex); |
268 | |
269 | return 0; |
270 | } |
271 | |
272 | static int adv748x_hdmi_query_dv_timings(struct v4l2_subdev *sd, |
273 | struct v4l2_dv_timings *timings) |
274 | { |
275 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); |
276 | struct adv748x_state *state = adv748x_hdmi_to_state(hdmi); |
277 | struct v4l2_bt_timings *bt = &timings->bt; |
278 | int pixelclock; |
279 | int polarity; |
280 | |
281 | if (!timings) |
282 | return -EINVAL; |
283 | |
284 | memset(timings, 0, sizeof(struct v4l2_dv_timings)); |
285 | |
286 | /* |
287 | * If the pattern generator is enabled the device shall not be queried |
288 | * for timings. Instead the timings programmed shall be reported as they |
289 | * are the ones being used to generate the pattern. |
290 | */ |
291 | if (cp_read(state, ADV748X_CP_PAT_GEN) & ADV748X_CP_PAT_GEN_EN) { |
292 | *timings = hdmi->timings; |
293 | return 0; |
294 | } |
295 | |
296 | if (!adv748x_hdmi_has_signal(state)) |
297 | return -ENOLINK; |
298 | |
299 | pixelclock = adv748x_hdmi_read_pixelclock(state); |
300 | if (pixelclock < 0) |
301 | return -ENODATA; |
302 | |
303 | timings->type = V4L2_DV_BT_656_1120; |
304 | |
305 | bt->pixelclock = pixelclock; |
306 | bt->interlaced = hdmi_read(state, ADV748X_HDMI_F1H1) & |
307 | ADV748X_HDMI_F1H1_INTERLACED ? |
308 | V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; |
309 | bt->width = hdmi_read16(state, ADV748X_HDMI_LW1, |
310 | ADV748X_HDMI_LW1_WIDTH_MASK); |
311 | bt->height = hdmi_read16(state, ADV748X_HDMI_F0H1, |
312 | ADV748X_HDMI_F0H1_HEIGHT_MASK); |
313 | bt->hfrontporch = hdmi_read16(state, ADV748X_HDMI_HFRONT_PORCH, |
314 | ADV748X_HDMI_HFRONT_PORCH_MASK); |
315 | bt->hsync = hdmi_read16(state, ADV748X_HDMI_HSYNC_WIDTH, |
316 | ADV748X_HDMI_HSYNC_WIDTH_MASK); |
317 | bt->hbackporch = hdmi_read16(state, ADV748X_HDMI_HBACK_PORCH, |
318 | ADV748X_HDMI_HBACK_PORCH_MASK); |
319 | bt->vfrontporch = hdmi_read16(state, ADV748X_HDMI_VFRONT_PORCH, |
320 | ADV748X_HDMI_VFRONT_PORCH_MASK) / 2; |
321 | bt->vsync = hdmi_read16(state, ADV748X_HDMI_VSYNC_WIDTH, |
322 | ADV748X_HDMI_VSYNC_WIDTH_MASK) / 2; |
323 | bt->vbackporch = hdmi_read16(state, ADV748X_HDMI_VBACK_PORCH, |
324 | ADV748X_HDMI_VBACK_PORCH_MASK) / 2; |
325 | |
326 | polarity = hdmi_read(state, 0x05); |
327 | bt->polarities = (polarity & BIT(4) ? V4L2_DV_VSYNC_POS_POL : 0) | |
328 | (polarity & BIT(5) ? V4L2_DV_HSYNC_POS_POL : 0); |
329 | |
330 | if (bt->interlaced == V4L2_DV_INTERLACED) { |
331 | bt->height += hdmi_read16(state, 0x0b, 0x1fff); |
332 | bt->il_vfrontporch = hdmi_read16(state, 0x2c, 0x3fff) / 2; |
333 | bt->il_vsync = hdmi_read16(state, 0x30, 0x3fff) / 2; |
334 | bt->il_vbackporch = hdmi_read16(state, 0x34, 0x3fff) / 2; |
335 | } |
336 | |
337 | adv748x_fill_optional_dv_timings(timings); |
338 | |
339 | /* |
340 | * No interrupt handling is implemented yet. |
341 | * There should be an IRQ when a cable is plugged and the new timings |
342 | * should be figured out and stored to state. |
343 | */ |
344 | hdmi->timings = *timings; |
345 | |
346 | return 0; |
347 | } |
348 | |
349 | static int adv748x_hdmi_g_input_status(struct v4l2_subdev *sd, u32 *status) |
350 | { |
351 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); |
352 | struct adv748x_state *state = adv748x_hdmi_to_state(hdmi); |
353 | |
354 | mutex_lock(&state->mutex); |
355 | |
356 | *status = adv748x_hdmi_has_signal(state) ? 0 : V4L2_IN_ST_NO_SIGNAL; |
357 | |
358 | mutex_unlock(lock: &state->mutex); |
359 | |
360 | return 0; |
361 | } |
362 | |
363 | static int adv748x_hdmi_s_stream(struct v4l2_subdev *sd, int enable) |
364 | { |
365 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); |
366 | struct adv748x_state *state = adv748x_hdmi_to_state(hdmi); |
367 | int ret; |
368 | |
369 | mutex_lock(&state->mutex); |
370 | |
371 | ret = adv748x_tx_power(tx: hdmi->tx, on: enable); |
372 | if (ret) |
373 | goto done; |
374 | |
375 | if (adv748x_hdmi_has_signal(state)) |
376 | adv_dbg(state, "Detected HDMI signal\n" ); |
377 | else |
378 | adv_dbg(state, "Couldn't detect HDMI video signal\n" ); |
379 | |
380 | done: |
381 | mutex_unlock(lock: &state->mutex); |
382 | return ret; |
383 | } |
384 | |
385 | static int adv748x_hdmi_g_pixelaspect(struct v4l2_subdev *sd, |
386 | struct v4l2_fract *aspect) |
387 | { |
388 | aspect->numerator = 1; |
389 | aspect->denominator = 1; |
390 | |
391 | return 0; |
392 | } |
393 | |
394 | static const struct v4l2_subdev_video_ops adv748x_video_ops_hdmi = { |
395 | .s_dv_timings = adv748x_hdmi_s_dv_timings, |
396 | .g_dv_timings = adv748x_hdmi_g_dv_timings, |
397 | .query_dv_timings = adv748x_hdmi_query_dv_timings, |
398 | .g_input_status = adv748x_hdmi_g_input_status, |
399 | .s_stream = adv748x_hdmi_s_stream, |
400 | .g_pixelaspect = adv748x_hdmi_g_pixelaspect, |
401 | }; |
402 | |
403 | /* ----------------------------------------------------------------------------- |
404 | * v4l2_subdev_pad_ops |
405 | */ |
406 | |
407 | static int adv748x_hdmi_propagate_pixelrate(struct adv748x_hdmi *hdmi) |
408 | { |
409 | struct v4l2_subdev *tx; |
410 | struct v4l2_dv_timings timings; |
411 | |
412 | tx = adv748x_get_remote_sd(pad: &hdmi->pads[ADV748X_HDMI_SOURCE]); |
413 | if (!tx) |
414 | return -ENOLINK; |
415 | |
416 | adv748x_hdmi_query_dv_timings(sd: &hdmi->sd, timings: &timings); |
417 | |
418 | return adv748x_csi2_set_pixelrate(sd: tx, rate: timings.bt.pixelclock); |
419 | } |
420 | |
421 | static int adv748x_hdmi_enum_mbus_code(struct v4l2_subdev *sd, |
422 | struct v4l2_subdev_state *sd_state, |
423 | struct v4l2_subdev_mbus_code_enum *code) |
424 | { |
425 | if (code->index != 0) |
426 | return -EINVAL; |
427 | |
428 | code->code = MEDIA_BUS_FMT_RGB888_1X24; |
429 | |
430 | return 0; |
431 | } |
432 | |
433 | static int adv748x_hdmi_get_format(struct v4l2_subdev *sd, |
434 | struct v4l2_subdev_state *sd_state, |
435 | struct v4l2_subdev_format *sdformat) |
436 | { |
437 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); |
438 | struct v4l2_mbus_framefmt *mbusformat; |
439 | |
440 | if (sdformat->pad != ADV748X_HDMI_SOURCE) |
441 | return -EINVAL; |
442 | |
443 | if (sdformat->which == V4L2_SUBDEV_FORMAT_TRY) { |
444 | mbusformat = v4l2_subdev_state_get_format(sd_state, |
445 | sdformat->pad); |
446 | sdformat->format = *mbusformat; |
447 | } else { |
448 | adv748x_hdmi_fill_format(hdmi, fmt: &sdformat->format); |
449 | adv748x_hdmi_propagate_pixelrate(hdmi); |
450 | } |
451 | |
452 | return 0; |
453 | } |
454 | |
455 | static int adv748x_hdmi_set_format(struct v4l2_subdev *sd, |
456 | struct v4l2_subdev_state *sd_state, |
457 | struct v4l2_subdev_format *sdformat) |
458 | { |
459 | struct v4l2_mbus_framefmt *mbusformat; |
460 | |
461 | if (sdformat->pad != ADV748X_HDMI_SOURCE) |
462 | return -EINVAL; |
463 | |
464 | if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) |
465 | return adv748x_hdmi_get_format(sd, sd_state, sdformat); |
466 | |
467 | mbusformat = v4l2_subdev_state_get_format(sd_state, sdformat->pad); |
468 | *mbusformat = sdformat->format; |
469 | |
470 | return 0; |
471 | } |
472 | |
473 | static int adv748x_hdmi_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) |
474 | { |
475 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); |
476 | |
477 | memset(edid->reserved, 0, sizeof(edid->reserved)); |
478 | |
479 | if (!hdmi->edid.present) |
480 | return -ENODATA; |
481 | |
482 | if (edid->start_block == 0 && edid->blocks == 0) { |
483 | edid->blocks = hdmi->edid.blocks; |
484 | return 0; |
485 | } |
486 | |
487 | if (edid->start_block >= hdmi->edid.blocks) |
488 | return -EINVAL; |
489 | |
490 | if (edid->start_block + edid->blocks > hdmi->edid.blocks) |
491 | edid->blocks = hdmi->edid.blocks - edid->start_block; |
492 | |
493 | memcpy(edid->edid, hdmi->edid.edid + edid->start_block * 128, |
494 | edid->blocks * 128); |
495 | |
496 | return 0; |
497 | } |
498 | |
499 | static inline int adv748x_hdmi_edid_write_block(struct adv748x_hdmi *hdmi, |
500 | unsigned int total_len, const u8 *val) |
501 | { |
502 | struct adv748x_state *state = adv748x_hdmi_to_state(hdmi); |
503 | int err = 0; |
504 | int i = 0; |
505 | int len = 0; |
506 | |
507 | adv_dbg(state, "%s: write EDID block (%d byte)\n" , |
508 | __func__, total_len); |
509 | |
510 | while (!err && i < total_len) { |
511 | len = (total_len - i) > I2C_SMBUS_BLOCK_MAX ? |
512 | I2C_SMBUS_BLOCK_MAX : |
513 | (total_len - i); |
514 | |
515 | err = adv748x_write_block(state, client_page: ADV748X_PAGE_EDID, |
516 | init_reg: i, val: val + i, val_len: len); |
517 | i += len; |
518 | } |
519 | |
520 | return err; |
521 | } |
522 | |
523 | static int adv748x_hdmi_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) |
524 | { |
525 | struct adv748x_hdmi *hdmi = adv748x_sd_to_hdmi(sd); |
526 | struct adv748x_state *state = adv748x_hdmi_to_state(hdmi); |
527 | int err; |
528 | |
529 | memset(edid->reserved, 0, sizeof(edid->reserved)); |
530 | |
531 | if (edid->start_block != 0) |
532 | return -EINVAL; |
533 | |
534 | if (edid->blocks == 0) { |
535 | hdmi->edid.blocks = 0; |
536 | hdmi->edid.present = 0; |
537 | |
538 | /* Fall back to a 16:9 aspect ratio */ |
539 | hdmi->aspect_ratio.numerator = 16; |
540 | hdmi->aspect_ratio.denominator = 9; |
541 | |
542 | /* Disable the EDID */ |
543 | repeater_write(state, ADV748X_REPEATER_EDID_SZ, |
544 | edid->blocks << ADV748X_REPEATER_EDID_SZ_SHIFT); |
545 | |
546 | repeater_write(state, ADV748X_REPEATER_EDID_CTL, 0); |
547 | |
548 | return 0; |
549 | } |
550 | |
551 | if (edid->blocks > 4) { |
552 | edid->blocks = 4; |
553 | return -E2BIG; |
554 | } |
555 | |
556 | memcpy(hdmi->edid.edid, edid->edid, 128 * edid->blocks); |
557 | hdmi->edid.blocks = edid->blocks; |
558 | hdmi->edid.present = true; |
559 | |
560 | hdmi->aspect_ratio = v4l2_calc_aspect_ratio(hor_landscape: edid->edid[0x15], |
561 | vert_portrait: edid->edid[0x16]); |
562 | |
563 | err = adv748x_hdmi_edid_write_block(hdmi, total_len: 128 * edid->blocks, |
564 | val: hdmi->edid.edid); |
565 | if (err < 0) { |
566 | v4l2_err(sd, "error %d writing edid pad %d\n" , err, edid->pad); |
567 | return err; |
568 | } |
569 | |
570 | repeater_write(state, ADV748X_REPEATER_EDID_SZ, |
571 | edid->blocks << ADV748X_REPEATER_EDID_SZ_SHIFT); |
572 | |
573 | repeater_write(state, ADV748X_REPEATER_EDID_CTL, |
574 | ADV748X_REPEATER_EDID_CTL_EN); |
575 | |
576 | return 0; |
577 | } |
578 | |
579 | static bool adv748x_hdmi_check_dv_timings(const struct v4l2_dv_timings *timings, |
580 | void *hdl) |
581 | { |
582 | const struct adv748x_hdmi_video_standards *stds = |
583 | adv748x_hdmi_video_standards; |
584 | unsigned int i; |
585 | |
586 | for (i = 0; stds[i].timings.bt.width; i++) |
587 | if (v4l2_match_dv_timings(measured: timings, standard: &stds[i].timings, pclock_delta: 0, match_reduced_fps: false)) |
588 | return true; |
589 | |
590 | return false; |
591 | } |
592 | |
593 | static int adv748x_hdmi_enum_dv_timings(struct v4l2_subdev *sd, |
594 | struct v4l2_enum_dv_timings *timings) |
595 | { |
596 | return v4l2_enum_dv_timings_cap(t: timings, cap: &adv748x_hdmi_timings_cap, |
597 | fnc: adv748x_hdmi_check_dv_timings, NULL); |
598 | } |
599 | |
600 | static int adv748x_hdmi_dv_timings_cap(struct v4l2_subdev *sd, |
601 | struct v4l2_dv_timings_cap *cap) |
602 | { |
603 | *cap = adv748x_hdmi_timings_cap; |
604 | return 0; |
605 | } |
606 | |
607 | static const struct v4l2_subdev_pad_ops adv748x_pad_ops_hdmi = { |
608 | .enum_mbus_code = adv748x_hdmi_enum_mbus_code, |
609 | .set_fmt = adv748x_hdmi_set_format, |
610 | .get_fmt = adv748x_hdmi_get_format, |
611 | .get_edid = adv748x_hdmi_get_edid, |
612 | .set_edid = adv748x_hdmi_set_edid, |
613 | .dv_timings_cap = adv748x_hdmi_dv_timings_cap, |
614 | .enum_dv_timings = adv748x_hdmi_enum_dv_timings, |
615 | }; |
616 | |
617 | /* ----------------------------------------------------------------------------- |
618 | * v4l2_subdev_ops |
619 | */ |
620 | |
621 | static const struct v4l2_subdev_ops adv748x_ops_hdmi = { |
622 | .video = &adv748x_video_ops_hdmi, |
623 | .pad = &adv748x_pad_ops_hdmi, |
624 | }; |
625 | |
626 | /* ----------------------------------------------------------------------------- |
627 | * Controls |
628 | */ |
629 | |
630 | static const char * const [] = { |
631 | "Disabled" , |
632 | "Solid Color" , |
633 | "Color Bars" , |
634 | "Ramp Grey" , |
635 | "Ramp Blue" , |
636 | "Ramp Red" , |
637 | "Checkered" |
638 | }; |
639 | |
640 | static int adv748x_hdmi_s_ctrl(struct v4l2_ctrl *ctrl) |
641 | { |
642 | struct adv748x_hdmi *hdmi = adv748x_ctrl_to_hdmi(ctrl); |
643 | struct adv748x_state *state = adv748x_hdmi_to_state(hdmi); |
644 | int ret; |
645 | u8 pattern; |
646 | |
647 | /* Enable video adjustment first */ |
648 | ret = cp_clrset(state, ADV748X_CP_VID_ADJ, |
649 | ADV748X_CP_VID_ADJ_ENABLE, |
650 | ADV748X_CP_VID_ADJ_ENABLE); |
651 | if (ret < 0) |
652 | return ret; |
653 | |
654 | switch (ctrl->id) { |
655 | case V4L2_CID_BRIGHTNESS: |
656 | ret = cp_write(state, ADV748X_CP_BRI, ctrl->val); |
657 | break; |
658 | case V4L2_CID_HUE: |
659 | ret = cp_write(state, ADV748X_CP_HUE, ctrl->val); |
660 | break; |
661 | case V4L2_CID_CONTRAST: |
662 | ret = cp_write(state, ADV748X_CP_CON, ctrl->val); |
663 | break; |
664 | case V4L2_CID_SATURATION: |
665 | ret = cp_write(state, ADV748X_CP_SAT, ctrl->val); |
666 | break; |
667 | case V4L2_CID_TEST_PATTERN: |
668 | pattern = ctrl->val; |
669 | |
670 | /* Pattern is 0-indexed. Ctrl Menu is 1-indexed */ |
671 | if (pattern) { |
672 | pattern--; |
673 | pattern |= ADV748X_CP_PAT_GEN_EN; |
674 | } |
675 | |
676 | ret = cp_write(state, ADV748X_CP_PAT_GEN, pattern); |
677 | |
678 | break; |
679 | default: |
680 | return -EINVAL; |
681 | } |
682 | |
683 | return ret; |
684 | } |
685 | |
686 | static const struct v4l2_ctrl_ops adv748x_hdmi_ctrl_ops = { |
687 | .s_ctrl = adv748x_hdmi_s_ctrl, |
688 | }; |
689 | |
690 | static int adv748x_hdmi_init_controls(struct adv748x_hdmi *hdmi) |
691 | { |
692 | struct adv748x_state *state = adv748x_hdmi_to_state(hdmi); |
693 | |
694 | v4l2_ctrl_handler_init(&hdmi->ctrl_hdl, 5); |
695 | |
696 | /* Use our mutex for the controls */ |
697 | hdmi->ctrl_hdl.lock = &state->mutex; |
698 | |
699 | v4l2_ctrl_new_std(hdl: &hdmi->ctrl_hdl, ops: &adv748x_hdmi_ctrl_ops, |
700 | V4L2_CID_BRIGHTNESS, ADV748X_CP_BRI_MIN, |
701 | ADV748X_CP_BRI_MAX, step: 1, ADV748X_CP_BRI_DEF); |
702 | v4l2_ctrl_new_std(hdl: &hdmi->ctrl_hdl, ops: &adv748x_hdmi_ctrl_ops, |
703 | V4L2_CID_CONTRAST, ADV748X_CP_CON_MIN, |
704 | ADV748X_CP_CON_MAX, step: 1, ADV748X_CP_CON_DEF); |
705 | v4l2_ctrl_new_std(hdl: &hdmi->ctrl_hdl, ops: &adv748x_hdmi_ctrl_ops, |
706 | V4L2_CID_SATURATION, ADV748X_CP_SAT_MIN, |
707 | ADV748X_CP_SAT_MAX, step: 1, ADV748X_CP_SAT_DEF); |
708 | v4l2_ctrl_new_std(hdl: &hdmi->ctrl_hdl, ops: &adv748x_hdmi_ctrl_ops, |
709 | V4L2_CID_HUE, ADV748X_CP_HUE_MIN, |
710 | ADV748X_CP_HUE_MAX, step: 1, ADV748X_CP_HUE_DEF); |
711 | |
712 | /* |
713 | * Todo: V4L2_CID_DV_RX_POWER_PRESENT should also be supported when |
714 | * interrupts are handled correctly |
715 | */ |
716 | |
717 | v4l2_ctrl_new_std_menu_items(hdl: &hdmi->ctrl_hdl, ops: &adv748x_hdmi_ctrl_ops, |
718 | V4L2_CID_TEST_PATTERN, |
719 | ARRAY_SIZE(hdmi_ctrl_patgen_menu) - 1, |
720 | mask: 0, def: 0, qmenu: hdmi_ctrl_patgen_menu); |
721 | |
722 | hdmi->sd.ctrl_handler = &hdmi->ctrl_hdl; |
723 | if (hdmi->ctrl_hdl.error) { |
724 | v4l2_ctrl_handler_free(hdl: &hdmi->ctrl_hdl); |
725 | return hdmi->ctrl_hdl.error; |
726 | } |
727 | |
728 | return v4l2_ctrl_handler_setup(hdl: &hdmi->ctrl_hdl); |
729 | } |
730 | |
731 | int adv748x_hdmi_init(struct adv748x_hdmi *hdmi) |
732 | { |
733 | struct adv748x_state *state = adv748x_hdmi_to_state(hdmi); |
734 | struct v4l2_dv_timings cea1280x720 = V4L2_DV_BT_CEA_1280X720P30; |
735 | int ret; |
736 | |
737 | adv748x_hdmi_s_dv_timings(sd: &hdmi->sd, timings: &cea1280x720); |
738 | |
739 | /* Initialise a default 16:9 aspect ratio */ |
740 | hdmi->aspect_ratio.numerator = 16; |
741 | hdmi->aspect_ratio.denominator = 9; |
742 | |
743 | adv748x_subdev_init(sd: &hdmi->sd, state, ops: &adv748x_ops_hdmi, |
744 | MEDIA_ENT_F_IO_DTV, ident: "hdmi" ); |
745 | |
746 | hdmi->pads[ADV748X_HDMI_SINK].flags = MEDIA_PAD_FL_SINK; |
747 | hdmi->pads[ADV748X_HDMI_SOURCE].flags = MEDIA_PAD_FL_SOURCE; |
748 | |
749 | ret = media_entity_pads_init(entity: &hdmi->sd.entity, |
750 | num_pads: ADV748X_HDMI_NR_PADS, pads: hdmi->pads); |
751 | if (ret) |
752 | return ret; |
753 | |
754 | ret = adv748x_hdmi_init_controls(hdmi); |
755 | if (ret) |
756 | goto err_free_media; |
757 | |
758 | return 0; |
759 | |
760 | err_free_media: |
761 | media_entity_cleanup(entity: &hdmi->sd.entity); |
762 | |
763 | return ret; |
764 | } |
765 | |
766 | void adv748x_hdmi_cleanup(struct adv748x_hdmi *hdmi) |
767 | { |
768 | v4l2_device_unregister_subdev(sd: &hdmi->sd); |
769 | media_entity_cleanup(entity: &hdmi->sd.entity); |
770 | v4l2_ctrl_handler_free(hdl: &hdmi->ctrl_hdl); |
771 | } |
772 | |