1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include <linux/device.h>
16#include <linux/gpio/consumer.h>
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/platform_device.h>
20#include <linux/xilinx-v4l2-controls.h>
21
22#include <media/v4l2-async.h>
23#include <media/v4l2-ctrls.h>
24#include <media/v4l2-subdev.h>
25
26#include "xilinx-hls-common.h"
27#include "xilinx-vip.h"
28#include "xilinx-vtc.h"
29
30#define XTPG_CTRL_STATUS_SLAVE_ERROR (1 << 16)
31#define XTPG_CTRL_IRQ_SLAVE_ERROR (1 << 16)
32
33#define XTPG_PATTERN_CONTROL 0x0100
34#define XTPG_PATTERN_MASK (0xf << 0)
35#define XTPG_PATTERN_CONTROL_CROSS_HAIRS (1 << 4)
36#define XTPG_PATTERN_CONTROL_MOVING_BOX (1 << 5)
37#define XTPG_PATTERN_CONTROL_COLOR_MASK_SHIFT 6
38#define XTPG_PATTERN_CONTROL_COLOR_MASK_MASK (0xf << 6)
39#define XTPG_PATTERN_CONTROL_STUCK_PIXEL (1 << 9)
40#define XTPG_PATTERN_CONTROL_NOISE (1 << 10)
41#define XTPG_PATTERN_CONTROL_MOTION (1 << 12)
42#define XTPG_MOTION_SPEED 0x0104
43#define XTPG_CROSS_HAIRS 0x0108
44#define XTPG_CROSS_HAIRS_ROW_SHIFT 0
45#define XTPG_CROSS_HAIRS_ROW_MASK (0xfff << 0)
46#define XTPG_CROSS_HAIRS_COLUMN_SHIFT 16
47#define XTPG_CROSS_HAIRS_COLUMN_MASK (0xfff << 16)
48#define XTPG_ZPLATE_HOR_CONTROL 0x010c
49#define XTPG_ZPLATE_VER_CONTROL 0x0110
50#define XTPG_ZPLATE_START_SHIFT 0
51#define XTPG_ZPLATE_START_MASK (0xffff << 0)
52#define XTPG_ZPLATE_SPEED_SHIFT 16
53#define XTPG_ZPLATE_SPEED_MASK (0xffff << 16)
54#define XTPG_BOX_SIZE 0x0114
55#define XTPG_BOX_COLOR 0x0118
56#define XTPG_STUCK_PIXEL_THRESH 0x011c
57#define XTPG_NOISE_GAIN 0x0120
58#define XTPG_BAYER_PHASE 0x0124
59#define XTPG_BAYER_PHASE_RGGB 0
60#define XTPG_BAYER_PHASE_GRBG 1
61#define XTPG_BAYER_PHASE_GBRG 2
62#define XTPG_BAYER_PHASE_BGGR 3
63#define XTPG_BAYER_PHASE_OFF 4
64
65
66
67
68#define XTPG_HLS_BG_PATTERN 0x0020
69#define XTPG_HLS_FG_PATTERN 0x0028
70#define XTPG_HLS_FG_PATTERN_CROSS_HAIR (1 << 1)
71#define XTPG_HLS_MASK_ID 0x0030
72#define XTPG_HLS_MOTION_SPEED 0x0038
73#define XTPG_HLS_COLOR_FORMAT 0x0040
74#define XTPG_HLS_COLOR_FORMAT_RGB 0
75#define XTPG_HLS_COLOR_FORMAT_YUV_444 1
76#define XTPG_HLS_COLOR_FORMAT_YUV_422 2
77#define XTPG_HLS_COLOR_FORMAT_YUV_420 3
78#define XTPG_HLS_CROSS_HAIR_HOR 0x0048
79#define XTPG_HLS_CROSS_HAIR_VER 0x0050
80#define XTPG_HLS_ZPLATE_HOR_CNTL_START 0x0058
81#define XTPG_HLS_ZPLATE_HOR_CNTL_DELTA 0x0060
82#define XTPG_HLS_ZPLATE_VER_CNTL_START 0x0068
83#define XTPG_HLS_ZPLATE_VER_CNTL_DELTA 0x0070
84#define XTPG_HLS_BOX_SIZE 0x0078
85#define XTPG_HLS_BOX_COLOR_RED_CB 0x0080
86#define XTPG_HLS_BOX_COLOR_GREEN_CR 0x0088
87#define XTPG_HLS_BOX_COLOR_BLUE_Y 0x0090
88#define XTPG_HLS_ENABLE_INPUT 0x0098
89#define XTPG_HLS_USE_INPUT_VID_STREAM (1 << 0)
90#define XTPG_HLS_PASS_THRU_START_X 0x00a0
91#define XTPG_HLS_PASS_THRU_START_Y 0x00a8
92#define XTPG_HLS_PASS_THRU_END_X 0x00b0
93#define XTPG_HLS_PASS_THRU_END_Y 0x00b8
94
95
96
97
98
99#define XTPG_MIN_HBLANK 3
100#define XTPG_MAX_HBLANK (XVTC_MAX_HSIZE - XVIP_MIN_WIDTH)
101#define XTPG_MIN_VBLANK 3
102#define XTPG_MAX_VBLANK (XVTC_MAX_VSIZE - XVIP_MIN_HEIGHT)
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124struct xtpg_device {
125 struct xvip_device xvip;
126
127 struct media_pad pads[2];
128 unsigned int npads;
129 bool has_input;
130
131 struct v4l2_mbus_framefmt formats[2];
132 struct v4l2_mbus_framefmt default_format;
133 const struct xvip_video_format *vip_format;
134 bool bayer;
135
136 struct v4l2_ctrl_handler ctrl_handler;
137 struct v4l2_ctrl *hblank;
138 struct v4l2_ctrl *vblank;
139 struct v4l2_ctrl *pattern;
140 bool streaming;
141 bool is_hls;
142
143 struct xvtc_device *vtc;
144 struct gpio_desc *vtmux_gpio;
145 struct gpio_desc *rst_gpio;
146};
147
148static inline struct xtpg_device *to_tpg(struct v4l2_subdev *subdev)
149{
150 return container_of(subdev, struct xtpg_device, xvip.subdev);
151}
152
153static u32 xtpg_get_bayer_phase(unsigned int code)
154{
155 switch (code) {
156 case MEDIA_BUS_FMT_SRGGB8_1X8:
157 return XTPG_BAYER_PHASE_RGGB;
158 case MEDIA_BUS_FMT_SGRBG8_1X8:
159 return XTPG_BAYER_PHASE_GRBG;
160 case MEDIA_BUS_FMT_SGBRG8_1X8:
161 return XTPG_BAYER_PHASE_GBRG;
162 case MEDIA_BUS_FMT_SBGGR8_1X8:
163 return XTPG_BAYER_PHASE_BGGR;
164 default:
165 return XTPG_BAYER_PHASE_OFF;
166 }
167}
168
169static void xtpg_config_vtc(struct xtpg_device *xtpg, int width, int height)
170{
171
172 struct xvtc_config config = {
173 .hblank_start = width,
174 .hsync_start = width + 1,
175 .vblank_start = height,
176 .vsync_start = height + 1,
177 };
178 unsigned int htotal;
179 unsigned int vtotal;
180
181 htotal = min_t(unsigned int, XVTC_MAX_HSIZE,
182 v4l2_ctrl_g_ctrl(xtpg->hblank) + width);
183 vtotal = min_t(unsigned int, XVTC_MAX_VSIZE,
184 v4l2_ctrl_g_ctrl(xtpg->vblank) + height);
185
186 config.hsync_end = htotal - 1;
187 config.hsize = htotal;
188 config.vsync_end = vtotal - 1;
189 config.vsize = vtotal;
190
191 xvtc_generator_start(xtpg->vtc, &config);
192}
193
194static void __xtpg_update_pattern_control(struct xtpg_device *xtpg,
195 bool passthrough, bool pattern)
196{
197 u32 pattern_mask = (1 << (xtpg->pattern->maximum + 1)) - 1;
198
199
200
201
202
203 if (xtpg->npads == 1 || !xtpg->has_input)
204 passthrough = false;
205
206
207 if (passthrough)
208 pattern_mask &= ~1;
209
210
211 if (pattern)
212 pattern_mask &= 1;
213
214 __v4l2_ctrl_modify_range(xtpg->pattern, 0, xtpg->pattern->maximum,
215 pattern_mask, pattern ? 9 : 0);
216}
217
218static void xtpg_update_pattern_control(struct xtpg_device *xtpg,
219 bool passthrough, bool pattern)
220{
221 mutex_lock(xtpg->ctrl_handler.lock);
222 __xtpg_update_pattern_control(xtpg, passthrough, pattern);
223 mutex_unlock(xtpg->ctrl_handler.lock);
224}
225
226
227
228
229
230static int xtpg_s_stream(struct v4l2_subdev *subdev, int enable)
231{
232 struct xtpg_device *xtpg = to_tpg(subdev);
233 unsigned int width = xtpg->formats[0].width;
234 unsigned int height = xtpg->formats[0].height;
235 bool passthrough;
236 u32 bayer_phase;
237
238 if (!enable) {
239 if (!xtpg->is_hls) {
240 xvip_stop(&xtpg->xvip);
241 } else {
242
243
244
245
246
247
248 gpiod_set_value_cansleep(xtpg->rst_gpio, 0x1);
249 gpiod_set_value_cansleep(xtpg->rst_gpio, 0x0);
250 v4l2_ctrl_handler_setup(&xtpg->ctrl_handler);
251 }
252
253 if (xtpg->vtc)
254 xvtc_generator_stop(xtpg->vtc);
255
256 xtpg_update_pattern_control(xtpg, true, true);
257 xtpg->streaming = false;
258 return 0;
259 }
260
261 if (xtpg->is_hls) {
262 u32 fmt = 0;
263
264 switch (xtpg->vip_format->code) {
265 case MEDIA_BUS_FMT_VYYUYY8_1X24:
266 fmt = XTPG_HLS_COLOR_FORMAT_YUV_420;
267 break;
268 case MEDIA_BUS_FMT_UYVY8_1X16:
269 fmt = XTPG_HLS_COLOR_FORMAT_YUV_422;
270 break;
271 case MEDIA_BUS_FMT_VUY8_1X24:
272 fmt = XTPG_HLS_COLOR_FORMAT_YUV_444;
273 break;
274 case MEDIA_BUS_FMT_RBG888_1X24:
275 fmt = XTPG_HLS_COLOR_FORMAT_RGB;
276 break;
277 }
278 xvip_write(&xtpg->xvip, XTPG_HLS_COLOR_FORMAT, fmt);
279 xvip_write(&xtpg->xvip, XHLS_REG_COLS, width);
280 xvip_write(&xtpg->xvip, XHLS_REG_ROWS, height);
281 } else {
282 xvip_set_frame_size(&xtpg->xvip, &xtpg->formats[0]);
283 }
284
285 if (xtpg->vtc)
286 xtpg_config_vtc(xtpg, width, height);
287
288
289
290
291
292
293 mutex_lock(xtpg->ctrl_handler.lock);
294
295 if (xtpg->is_hls)
296 xvip_write(&xtpg->xvip, XTPG_HLS_BG_PATTERN,
297 xtpg->pattern->cur.val);
298 else
299 xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
300 XTPG_PATTERN_MASK, xtpg->pattern->cur.val);
301
302
303
304
305
306 passthrough = xtpg->pattern->cur.val == 0;
307 __xtpg_update_pattern_control(xtpg, passthrough, !passthrough);
308
309 xtpg->streaming = true;
310
311 mutex_unlock(xtpg->ctrl_handler.lock);
312
313 if (xtpg->vtmux_gpio)
314 gpiod_set_value_cansleep(xtpg->vtmux_gpio, !passthrough);
315
316 if (xtpg->is_hls) {
317 xvip_set(&xtpg->xvip, XTPG_HLS_ENABLE_INPUT,
318 XTPG_HLS_USE_INPUT_VID_STREAM);
319 xvip_set(&xtpg->xvip, XVIP_CTRL_CONTROL,
320 XHLS_REG_CTRL_AUTO_RESTART |
321 XVIP_CTRL_CONTROL_SW_ENABLE);
322 } else {
323
324
325
326
327
328 bayer_phase = passthrough ? XTPG_BAYER_PHASE_OFF
329 : xtpg_get_bayer_phase(xtpg->formats[0].code);
330 xvip_write(&xtpg->xvip, XTPG_BAYER_PHASE, bayer_phase);
331 xvip_start(&xtpg->xvip);
332 }
333
334 return 0;
335}
336
337
338
339
340
341static struct v4l2_mbus_framefmt *
342__xtpg_get_pad_format(struct xtpg_device *xtpg,
343 struct v4l2_subdev_pad_config *cfg,
344 unsigned int pad, u32 which)
345{
346 switch (which) {
347 case V4L2_SUBDEV_FORMAT_TRY:
348 return v4l2_subdev_get_try_format(&xtpg->xvip.subdev, cfg, pad);
349 case V4L2_SUBDEV_FORMAT_ACTIVE:
350 return &xtpg->formats[pad];
351 default:
352 return NULL;
353 }
354}
355
356static int xtpg_get_format(struct v4l2_subdev *subdev,
357 struct v4l2_subdev_pad_config *cfg,
358 struct v4l2_subdev_format *fmt)
359{
360 struct xtpg_device *xtpg = to_tpg(subdev);
361
362 fmt->format = *__xtpg_get_pad_format(xtpg, cfg, fmt->pad, fmt->which);
363
364 return 0;
365}
366
367static int xtpg_set_format(struct v4l2_subdev *subdev,
368 struct v4l2_subdev_pad_config *cfg,
369 struct v4l2_subdev_format *fmt)
370{
371 struct xtpg_device *xtpg = to_tpg(subdev);
372 struct v4l2_mbus_framefmt *__format;
373 u32 bayer_phase;
374
375 __format = __xtpg_get_pad_format(xtpg, cfg, fmt->pad, fmt->which);
376
377
378
379
380 if (xtpg->npads == 2 && fmt->pad == 1) {
381 fmt->format = *__format;
382 return 0;
383 }
384
385
386 if (xtpg->bayer) {
387 bayer_phase = xtpg_get_bayer_phase(fmt->format.code);
388 if (bayer_phase != XTPG_BAYER_PHASE_OFF)
389 __format->code = fmt->format.code;
390 }
391
392 xvip_set_format_size(__format, fmt);
393
394 fmt->format = *__format;
395
396
397 if (xtpg->npads == 2) {
398 __format = __xtpg_get_pad_format(xtpg, cfg, 1, fmt->which);
399 *__format = fmt->format;
400 }
401
402 return 0;
403}
404
405
406
407
408
409static int xtpg_enum_frame_size(struct v4l2_subdev *subdev,
410 struct v4l2_subdev_pad_config *cfg,
411 struct v4l2_subdev_frame_size_enum *fse)
412{
413 struct v4l2_mbus_framefmt *format;
414
415 format = v4l2_subdev_get_try_format(subdev, cfg, fse->pad);
416
417 if (fse->index || fse->code != format->code)
418 return -EINVAL;
419
420
421
422
423
424 if (fse->pad == 0) {
425 fse->min_width = XVIP_MIN_WIDTH;
426 fse->max_width = XVIP_MAX_WIDTH;
427 fse->min_height = XVIP_MIN_HEIGHT;
428 fse->max_height = XVIP_MAX_HEIGHT;
429 } else {
430 fse->min_width = format->width;
431 fse->max_width = format->width;
432 fse->min_height = format->height;
433 fse->max_height = format->height;
434 }
435
436 return 0;
437}
438
439static int xtpg_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
440{
441 struct xtpg_device *xtpg = to_tpg(subdev);
442 struct v4l2_mbus_framefmt *format;
443
444 format = v4l2_subdev_get_try_format(subdev, fh->pad, 0);
445 *format = xtpg->default_format;
446
447 if (xtpg->npads == 2) {
448 format = v4l2_subdev_get_try_format(subdev, fh->pad, 1);
449 *format = xtpg->default_format;
450 }
451
452 return 0;
453}
454
455static int xtpg_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
456{
457 return 0;
458}
459
460static int xtpg_s_ctrl(struct v4l2_ctrl *ctrl)
461{
462 struct xtpg_device *xtpg = container_of(ctrl->handler,
463 struct xtpg_device,
464 ctrl_handler);
465 switch (ctrl->id) {
466 case V4L2_CID_TEST_PATTERN:
467 if (xtpg->is_hls)
468 xvip_write(&xtpg->xvip, XTPG_HLS_BG_PATTERN,
469 ctrl->val);
470 else
471 xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
472 XTPG_PATTERN_MASK, ctrl->val);
473 return 0;
474 case V4L2_CID_XILINX_TPG_CROSS_HAIRS:
475 xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
476 XTPG_PATTERN_CONTROL_CROSS_HAIRS, ctrl->val);
477 return 0;
478 case V4L2_CID_XILINX_TPG_MOVING_BOX:
479 xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
480 XTPG_PATTERN_CONTROL_MOVING_BOX, ctrl->val);
481 return 0;
482 case V4L2_CID_XILINX_TPG_COLOR_MASK:
483 if (xtpg->is_hls)
484 xvip_write(&xtpg->xvip, XTPG_HLS_MASK_ID, ctrl->val);
485 else
486 xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
487 XTPG_PATTERN_CONTROL_COLOR_MASK_MASK,
488 ctrl->val <<
489 XTPG_PATTERN_CONTROL_COLOR_MASK_SHIFT);
490 return 0;
491 case V4L2_CID_XILINX_TPG_STUCK_PIXEL:
492 xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
493 XTPG_PATTERN_CONTROL_STUCK_PIXEL, ctrl->val);
494 return 0;
495 case V4L2_CID_XILINX_TPG_NOISE:
496 xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
497 XTPG_PATTERN_CONTROL_NOISE, ctrl->val);
498 return 0;
499 case V4L2_CID_XILINX_TPG_MOTION:
500 xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL,
501 XTPG_PATTERN_CONTROL_MOTION, ctrl->val);
502 return 0;
503 case V4L2_CID_XILINX_TPG_MOTION_SPEED:
504 if (xtpg->is_hls)
505 xvip_write(&xtpg->xvip, XTPG_HLS_MOTION_SPEED,
506 ctrl->val);
507 else
508 xvip_write(&xtpg->xvip, XTPG_MOTION_SPEED, ctrl->val);
509 return 0;
510 case V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW:
511 if (xtpg->is_hls)
512 xvip_write(&xtpg->xvip, XTPG_HLS_CROSS_HAIR_HOR,
513 ctrl->val);
514 else
515 xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS,
516 XTPG_CROSS_HAIRS_ROW_MASK,
517 ctrl->val <<
518 XTPG_CROSS_HAIRS_ROW_SHIFT);
519 return 0;
520 case V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN:
521 if (xtpg->is_hls)
522 xvip_write(&xtpg->xvip, XTPG_HLS_CROSS_HAIR_VER,
523 ctrl->val);
524 else
525 xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS,
526 XTPG_CROSS_HAIRS_COLUMN_MASK,
527 ctrl->val <<
528 XTPG_CROSS_HAIRS_COLUMN_SHIFT);
529 return 0;
530 case V4L2_CID_XILINX_TPG_ZPLATE_HOR_START:
531 if (xtpg->is_hls)
532 xvip_write(&xtpg->xvip, XTPG_HLS_ZPLATE_HOR_CNTL_START,
533 ctrl->val);
534 else
535 xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL,
536 XTPG_ZPLATE_START_MASK,
537 ctrl->val << XTPG_ZPLATE_START_SHIFT);
538 return 0;
539 case V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED:
540 if (xtpg->is_hls)
541 xvip_write(&xtpg->xvip, XTPG_HLS_ZPLATE_HOR_CNTL_DELTA,
542 ctrl->val);
543 else
544 xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL,
545 XTPG_ZPLATE_SPEED_MASK,
546 ctrl->val << XTPG_ZPLATE_SPEED_SHIFT);
547 return 0;
548 case V4L2_CID_XILINX_TPG_ZPLATE_VER_START:
549 if (xtpg->is_hls)
550 xvip_write(&xtpg->xvip, XTPG_HLS_ZPLATE_VER_CNTL_START,
551 ctrl->val);
552 else
553 xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL,
554 XTPG_ZPLATE_START_MASK,
555 ctrl->val << XTPG_ZPLATE_START_SHIFT);
556 return 0;
557 case V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED:
558 if (xtpg->is_hls)
559 xvip_write(&xtpg->xvip, XTPG_HLS_ZPLATE_VER_CNTL_DELTA,
560 ctrl->val);
561 else
562 xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL,
563 XTPG_ZPLATE_SPEED_MASK,
564 ctrl->val << XTPG_ZPLATE_SPEED_SHIFT);
565 return 0;
566 case V4L2_CID_XILINX_TPG_BOX_SIZE:
567 if (xtpg->is_hls)
568 xvip_write(&xtpg->xvip, XTPG_HLS_BOX_SIZE, ctrl->val);
569 else
570 xvip_write(&xtpg->xvip, XTPG_BOX_SIZE, ctrl->val);
571 return 0;
572 case V4L2_CID_XILINX_TPG_BOX_COLOR:
573 if (xtpg->is_hls) {
574 xvip_write(&xtpg->xvip, XTPG_HLS_BOX_COLOR_RED_CB,
575 ctrl->val >> 16);
576 xvip_write(&xtpg->xvip, XTPG_HLS_BOX_COLOR_GREEN_CR,
577 ctrl->val >> 8);
578 xvip_write(&xtpg->xvip, XTPG_HLS_BOX_COLOR_BLUE_Y,
579 ctrl->val);
580 } else {
581 xvip_write(&xtpg->xvip, XTPG_BOX_COLOR, ctrl->val);
582 }
583 return 0;
584 case V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH:
585 xvip_write(&xtpg->xvip, XTPG_STUCK_PIXEL_THRESH, ctrl->val);
586 return 0;
587 case V4L2_CID_XILINX_TPG_NOISE_GAIN:
588 xvip_write(&xtpg->xvip, XTPG_NOISE_GAIN, ctrl->val);
589 return 0;
590 case V4L2_CID_XILINX_TPG_HLS_FG_PATTERN:
591 xvip_write(&xtpg->xvip, XTPG_HLS_FG_PATTERN, ctrl->val);
592 return 0;
593 }
594
595 return 0;
596}
597
598static const struct v4l2_ctrl_ops xtpg_ctrl_ops = {
599 .s_ctrl = xtpg_s_ctrl,
600};
601
602static const struct v4l2_subdev_core_ops xtpg_core_ops = {
603};
604
605static const struct v4l2_subdev_video_ops xtpg_video_ops = {
606 .s_stream = xtpg_s_stream,
607};
608
609static const struct v4l2_subdev_pad_ops xtpg_pad_ops = {
610 .enum_mbus_code = xvip_enum_mbus_code,
611 .enum_frame_size = xtpg_enum_frame_size,
612 .get_fmt = xtpg_get_format,
613 .set_fmt = xtpg_set_format,
614};
615
616static const struct v4l2_subdev_ops xtpg_ops = {
617 .core = &xtpg_core_ops,
618 .video = &xtpg_video_ops,
619 .pad = &xtpg_pad_ops,
620};
621
622static const struct v4l2_subdev_internal_ops xtpg_internal_ops = {
623 .open = xtpg_open,
624 .close = xtpg_close,
625};
626
627
628
629
630
631static const char *const xtpg_pattern_strings[] = {
632 "Passthrough",
633 "Horizontal Ramp",
634 "Vertical Ramp",
635 "Temporal Ramp",
636 "Solid Red",
637 "Solid Green",
638 "Solid Blue",
639 "Solid Black",
640 "Solid White",
641 "Color Bars",
642 "Zone Plate",
643 "Tartan Color Bars",
644 "Cross Hatch",
645 "None",
646 "Vertical/Horizontal Ramps",
647 "Black/White Checker Board",
648};
649
650static const char *const xtpg_hls_pattern_strings[] = {
651 "Passthrough",
652 "Horizontal Ramp",
653 "Vertical Ramp",
654 "Temporal Ramp",
655 "Solid Red",
656 "Solid Green",
657 "Solid Blue",
658 "Solid Black",
659 "Solid White",
660 "Color Bars",
661 "Zone Plate",
662 "Tartan Color Bars",
663 "Cross Hatch",
664 "Color Sweep",
665 "Vertical/Horizontal Ramps",
666 "Black/White Checker Board",
667 "PseudoRandom",
668};
669
670static const char *const xtpg_hls_fg_strings[] = {
671 "No Overlay",
672 "Moving Box",
673 "Cross Hairs",
674};
675
676static const struct v4l2_ctrl_config xtpg_hls_fg_ctrl = {
677 .ops = &xtpg_ctrl_ops,
678 .id = V4L2_CID_XILINX_TPG_HLS_FG_PATTERN,
679 .name = "Test Pattern: Foreground Pattern",
680 .type = V4L2_CTRL_TYPE_MENU,
681 .min = 0,
682 .max = ARRAY_SIZE(xtpg_hls_fg_strings) - 1,
683 .qmenu = xtpg_hls_fg_strings,
684};
685
686static struct v4l2_ctrl_config xtpg_common_ctrls[] = {
687 {
688 .ops = &xtpg_ctrl_ops,
689 .id = V4L2_CID_XILINX_TPG_COLOR_MASK,
690 .name = "Test Pattern: Color Mask",
691 .type = V4L2_CTRL_TYPE_BITMASK,
692 .min = 0,
693 .max = 0x7,
694 .def = 0,
695 }, {
696 .ops = &xtpg_ctrl_ops,
697 .id = V4L2_CID_XILINX_TPG_MOTION_SPEED,
698 .name = "Test Pattern: Motion Speed",
699 .type = V4L2_CTRL_TYPE_INTEGER,
700 .min = 0,
701 .max = (1 << 8) - 1,
702 .step = 1,
703 .def = 4,
704 .flags = V4L2_CTRL_FLAG_SLIDER,
705 }, {
706 .ops = &xtpg_ctrl_ops,
707 .id = V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW,
708 .name = "Test Pattern: Cross Hairs Row",
709 .type = V4L2_CTRL_TYPE_INTEGER,
710 .min = 0,
711 .max = (1 << 12) - 1,
712 .step = 1,
713 .def = 0x64,
714 .flags = V4L2_CTRL_FLAG_SLIDER,
715 }, {
716 .ops = &xtpg_ctrl_ops,
717 .id = V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN,
718 .name = "Test Pattern: Cross Hairs Column",
719 .type = V4L2_CTRL_TYPE_INTEGER,
720 .min = 0,
721 .max = (1 << 12) - 1,
722 .step = 1,
723 .def = 0x64,
724 .flags = V4L2_CTRL_FLAG_SLIDER,
725 }, {
726 .ops = &xtpg_ctrl_ops,
727 .id = V4L2_CID_XILINX_TPG_ZPLATE_HOR_START,
728 .name = "Test Pattern: Zplate Horizontal Start Pos",
729 .type = V4L2_CTRL_TYPE_INTEGER,
730 .min = 0,
731 .max = (1 << 16) - 1,
732 .step = 1,
733 .def = 0x1e,
734 .flags = V4L2_CTRL_FLAG_SLIDER,
735 }, {
736 .ops = &xtpg_ctrl_ops,
737 .id = V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED,
738 .name = "Test Pattern: Zplate Horizontal Speed",
739 .type = V4L2_CTRL_TYPE_INTEGER,
740 .min = 0,
741 .max = (1 << 16) - 1,
742 .step = 1,
743 .def = 0,
744 .flags = V4L2_CTRL_FLAG_SLIDER,
745 }, {
746 .ops = &xtpg_ctrl_ops,
747 .id = V4L2_CID_XILINX_TPG_ZPLATE_VER_START,
748 .name = "Test Pattern: Zplate Vertical Start Pos",
749 .type = V4L2_CTRL_TYPE_INTEGER,
750 .min = 0,
751 .max = (1 << 16) - 1,
752 .step = 1,
753 .def = 1,
754 .flags = V4L2_CTRL_FLAG_SLIDER,
755 }, {
756 .ops = &xtpg_ctrl_ops,
757 .id = V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED,
758 .name = "Test Pattern: Zplate Vertical Speed",
759 .type = V4L2_CTRL_TYPE_INTEGER,
760 .min = 0,
761 .max = (1 << 16) - 1,
762 .step = 1,
763 .def = 0,
764 .flags = V4L2_CTRL_FLAG_SLIDER,
765 }, {
766 .ops = &xtpg_ctrl_ops,
767 .id = V4L2_CID_XILINX_TPG_BOX_SIZE,
768 .name = "Test Pattern: Box Size",
769 .type = V4L2_CTRL_TYPE_INTEGER,
770 .min = 0,
771 .max = (1 << 12) - 1,
772 .step = 1,
773 .def = 0x32,
774 .flags = V4L2_CTRL_FLAG_SLIDER,
775 }, {
776 .ops = &xtpg_ctrl_ops,
777 .id = V4L2_CID_XILINX_TPG_BOX_COLOR,
778 .name = "Test Pattern: Box Color(RGB/YCbCr)",
779 .type = V4L2_CTRL_TYPE_INTEGER,
780 .min = 0,
781 .max = (1 << 24) - 1,
782 .step = 1,
783 .def = 0,
784 },
785};
786
787static struct v4l2_ctrl_config xtpg_ctrls[] = {
788 {
789 .ops = &xtpg_ctrl_ops,
790 .id = V4L2_CID_XILINX_TPG_CROSS_HAIRS,
791 .name = "Test Pattern: Cross Hairs",
792 .type = V4L2_CTRL_TYPE_BOOLEAN,
793 .min = false,
794 .max = true,
795 .step = 1,
796 .def = 0,
797 }, {
798 .ops = &xtpg_ctrl_ops,
799 .id = V4L2_CID_XILINX_TPG_MOVING_BOX,
800 .name = "Test Pattern: Moving Box",
801 .type = V4L2_CTRL_TYPE_BOOLEAN,
802 .min = false,
803 .max = true,
804 .step = 1,
805 .def = 0,
806 }, {
807 .ops = &xtpg_ctrl_ops,
808 .id = V4L2_CID_XILINX_TPG_STUCK_PIXEL,
809 .name = "Test Pattern: Stuck Pixel",
810 .type = V4L2_CTRL_TYPE_BOOLEAN,
811 .min = false,
812 .max = true,
813 .step = 1,
814 .def = 0,
815 }, {
816 .ops = &xtpg_ctrl_ops,
817 .id = V4L2_CID_XILINX_TPG_NOISE,
818 .name = "Test Pattern: Noise",
819 .type = V4L2_CTRL_TYPE_BOOLEAN,
820 .min = false,
821 .max = true,
822 .step = 1,
823 .def = 0,
824 }, {
825 .ops = &xtpg_ctrl_ops,
826 .id = V4L2_CID_XILINX_TPG_MOTION,
827 .name = "Test Pattern: Motion",
828 .type = V4L2_CTRL_TYPE_BOOLEAN,
829 .min = false,
830 .max = true,
831 .step = 1,
832 .def = 0,
833 }, {
834 .ops = &xtpg_ctrl_ops,
835 .id = V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH,
836 .name = "Test Pattern: Stuck Pixel threshold",
837 .type = V4L2_CTRL_TYPE_INTEGER,
838 .min = 0,
839 .max = (1 << 16) - 1,
840 .step = 1,
841 .def = 0,
842 .flags = V4L2_CTRL_FLAG_SLIDER,
843 }, {
844 .ops = &xtpg_ctrl_ops,
845 .id = V4L2_CID_XILINX_TPG_NOISE_GAIN,
846 .name = "Test Pattern: Noise Gain",
847 .type = V4L2_CTRL_TYPE_INTEGER,
848 .min = 0,
849 .max = (1 << 8) - 1,
850 .step = 1,
851 .def = 0,
852 .flags = V4L2_CTRL_FLAG_SLIDER,
853 },
854};
855
856
857
858
859
860static const struct media_entity_operations xtpg_media_ops = {
861 .link_validate = v4l2_subdev_link_validate,
862};
863
864
865
866
867
868static int __maybe_unused xtpg_pm_suspend(struct device *dev)
869{
870 struct xtpg_device *xtpg = dev_get_drvdata(dev);
871
872 xvip_suspend(&xtpg->xvip);
873
874 return 0;
875}
876
877static int __maybe_unused xtpg_pm_resume(struct device *dev)
878{
879 struct xtpg_device *xtpg = dev_get_drvdata(dev);
880
881 xvip_resume(&xtpg->xvip);
882
883 return 0;
884}
885
886
887
888
889
890static int xtpg_parse_of(struct xtpg_device *xtpg)
891{
892 struct device *dev = xtpg->xvip.dev;
893 struct device_node *node = xtpg->xvip.dev->of_node;
894 struct device_node *ports;
895 struct device_node *port;
896 unsigned int nports = 0;
897 bool has_endpoint = false;
898
899 if (of_device_is_compatible(dev->of_node, "xlnx,v-tpg-7.0"))
900 xtpg->is_hls = true;
901
902 ports = of_get_child_by_name(node, "ports");
903 if (ports == NULL)
904 ports = node;
905
906 for_each_child_of_node(ports, port) {
907 const struct xvip_video_format *format;
908 struct device_node *endpoint;
909
910 if (!port->name || of_node_cmp(port->name, "port"))
911 continue;
912
913 format = xvip_of_get_format(port);
914 if (IS_ERR(format)) {
915 dev_err(dev, "invalid format in DT");
916 of_node_put(port);
917 return PTR_ERR(format);
918 }
919
920
921 if (!xtpg->vip_format) {
922 xtpg->vip_format = format;
923 } else if (xtpg->vip_format != format) {
924 dev_err(dev, "in/out format mismatch in DT");
925 of_node_put(port);
926 return -EINVAL;
927 }
928
929 if (nports == 0) {
930 endpoint = of_get_next_child(port, NULL);
931 if (endpoint)
932 has_endpoint = true;
933 of_node_put(endpoint);
934 }
935
936
937 nports++;
938 }
939
940 if (nports != 1 && nports != 2) {
941 dev_err(dev, "invalid number of ports %u\n", nports);
942 return -EINVAL;
943 }
944
945 xtpg->npads = nports;
946 if (nports == 2 && has_endpoint)
947 xtpg->has_input = true;
948
949 return 0;
950}
951
952static int xtpg_probe(struct platform_device *pdev)
953{
954 struct v4l2_subdev *subdev;
955 struct xtpg_device *xtpg;
956 u32 i, bayer_phase;
957 u32 npatterns;
958 int ret;
959
960 xtpg = devm_kzalloc(&pdev->dev, sizeof(*xtpg), GFP_KERNEL);
961 if (!xtpg)
962 return -ENOMEM;
963
964 xtpg->xvip.dev = &pdev->dev;
965
966 ret = xtpg_parse_of(xtpg);
967 if (ret < 0)
968 return ret;
969
970 ret = xvip_init_resources(&xtpg->xvip);
971 if (ret < 0)
972 return ret;
973
974 xtpg->vtmux_gpio = devm_gpiod_get_optional(&pdev->dev, "timing",
975 GPIOD_OUT_HIGH);
976 if (IS_ERR(xtpg->vtmux_gpio)) {
977 ret = PTR_ERR(xtpg->vtmux_gpio);
978 goto error_resource;
979 }
980
981 if (xtpg->is_hls) {
982 xtpg->rst_gpio = devm_gpiod_get(&pdev->dev, "reset",
983 GPIOD_OUT_HIGH);
984 if (IS_ERR(xtpg->rst_gpio)) {
985 ret = PTR_ERR(xtpg->rst_gpio);
986 goto error_resource;
987 }
988 }
989
990 xtpg->vtc = xvtc_of_get(pdev->dev.of_node);
991 if (IS_ERR(xtpg->vtc)) {
992 ret = PTR_ERR(xtpg->vtc);
993 goto error_resource;
994 }
995
996
997
998
999
1000 if (xtpg->is_hls)
1001 gpiod_set_value_cansleep(xtpg->rst_gpio, 0x0);
1002 else
1003 xvip_reset(&xtpg->xvip);
1004
1005
1006
1007
1008 if (xtpg->npads == 2) {
1009 xtpg->pads[0].flags = MEDIA_PAD_FL_SINK;
1010 xtpg->pads[1].flags = MEDIA_PAD_FL_SOURCE;
1011 } else {
1012 xtpg->pads[0].flags = MEDIA_PAD_FL_SOURCE;
1013 }
1014
1015
1016 xtpg->default_format.code = xtpg->vip_format->code;
1017 xtpg->default_format.field = V4L2_FIELD_NONE;
1018 xtpg->default_format.colorspace = V4L2_COLORSPACE_SRGB;
1019
1020 if (xtpg->is_hls) {
1021 npatterns = ARRAY_SIZE(xtpg_hls_pattern_strings);
1022 xtpg->default_format.width = xvip_read(&xtpg->xvip,
1023 XHLS_REG_COLS);
1024 xtpg->default_format.height = xvip_read(&xtpg->xvip,
1025 XHLS_REG_ROWS);
1026 } else {
1027 npatterns = ARRAY_SIZE(xtpg_pattern_strings);
1028 xvip_get_frame_size(&xtpg->xvip, &xtpg->default_format);
1029 }
1030
1031 if (!xtpg->is_hls) {
1032 bayer_phase = xtpg_get_bayer_phase(xtpg->vip_format->code);
1033 if (bayer_phase != XTPG_BAYER_PHASE_OFF)
1034 xtpg->bayer = true;
1035 }
1036
1037 xtpg->formats[0] = xtpg->default_format;
1038 if (xtpg->npads == 2)
1039 xtpg->formats[1] = xtpg->default_format;
1040
1041
1042 subdev = &xtpg->xvip.subdev;
1043 v4l2_subdev_init(subdev, &xtpg_ops);
1044 subdev->dev = &pdev->dev;
1045 subdev->internal_ops = &xtpg_internal_ops;
1046 strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
1047 v4l2_set_subdevdata(subdev, xtpg);
1048 subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
1049 subdev->entity.ops = &xtpg_media_ops;
1050
1051 ret = media_entity_pads_init(&subdev->entity, xtpg->npads, xtpg->pads);
1052 if (ret < 0)
1053 goto error;
1054
1055 if (xtpg->is_hls)
1056 v4l2_ctrl_handler_init(&xtpg->ctrl_handler, 4 +
1057 ARRAY_SIZE(xtpg_common_ctrls));
1058 else
1059 v4l2_ctrl_handler_init(&xtpg->ctrl_handler, 3 +
1060 ARRAY_SIZE(xtpg_common_ctrls) +
1061 ARRAY_SIZE(xtpg_ctrls));
1062
1063 xtpg->vblank = v4l2_ctrl_new_std(&xtpg->ctrl_handler, &xtpg_ctrl_ops,
1064 V4L2_CID_VBLANK, XTPG_MIN_VBLANK,
1065 XTPG_MAX_VBLANK, 1, 100);
1066 xtpg->hblank = v4l2_ctrl_new_std(&xtpg->ctrl_handler, &xtpg_ctrl_ops,
1067 V4L2_CID_HBLANK, XTPG_MIN_HBLANK,
1068 XTPG_MAX_HBLANK, 1, 100);
1069
1070 if (xtpg->is_hls) {
1071 xtpg->pattern =
1072 v4l2_ctrl_new_std_menu_items(&xtpg->ctrl_handler,
1073 &xtpg_ctrl_ops,
1074 V4L2_CID_TEST_PATTERN,
1075 npatterns - 1,
1076 1, 9,
1077 xtpg_hls_pattern_strings);
1078 v4l2_ctrl_new_custom(&xtpg->ctrl_handler,
1079 &xtpg_hls_fg_ctrl, NULL);
1080 } else {
1081 xtpg->pattern =
1082 v4l2_ctrl_new_std_menu_items(&xtpg->ctrl_handler,
1083 &xtpg_ctrl_ops,
1084 V4L2_CID_TEST_PATTERN,
1085 npatterns - 1,
1086 1, 9,
1087 xtpg_pattern_strings);
1088
1089 for (i = 0; i < ARRAY_SIZE(xtpg_ctrls); i++)
1090 v4l2_ctrl_new_custom(&xtpg->ctrl_handler,
1091 &xtpg_ctrls[i], NULL);
1092 }
1093
1094 for (i = 0; i < ARRAY_SIZE(xtpg_common_ctrls); i++)
1095 v4l2_ctrl_new_custom(&xtpg->ctrl_handler,
1096 &xtpg_common_ctrls[i], NULL);
1097
1098 if (xtpg->ctrl_handler.error) {
1099 dev_err(&pdev->dev, "failed to add controls\n");
1100 ret = xtpg->ctrl_handler.error;
1101 goto error;
1102 }
1103
1104 subdev->ctrl_handler = &xtpg->ctrl_handler;
1105
1106 xtpg_update_pattern_control(xtpg, true, true);
1107
1108 ret = v4l2_ctrl_handler_setup(&xtpg->ctrl_handler);
1109 if (ret < 0) {
1110 dev_err(&pdev->dev, "failed to set controls\n");
1111 goto error;
1112 }
1113
1114 platform_set_drvdata(pdev, xtpg);
1115
1116 xvip_print_version(&xtpg->xvip);
1117
1118 ret = v4l2_async_register_subdev(subdev);
1119 if (ret < 0) {
1120 dev_err(&pdev->dev, "failed to register subdev\n");
1121 goto error;
1122 }
1123
1124 return 0;
1125
1126error:
1127 v4l2_ctrl_handler_free(&xtpg->ctrl_handler);
1128 media_entity_cleanup(&subdev->entity);
1129 xvtc_put(xtpg->vtc);
1130error_resource:
1131 xvip_cleanup_resources(&xtpg->xvip);
1132 return ret;
1133}
1134
1135static int xtpg_remove(struct platform_device *pdev)
1136{
1137 struct xtpg_device *xtpg = platform_get_drvdata(pdev);
1138 struct v4l2_subdev *subdev = &xtpg->xvip.subdev;
1139
1140 v4l2_async_unregister_subdev(subdev);
1141 v4l2_ctrl_handler_free(&xtpg->ctrl_handler);
1142 media_entity_cleanup(&subdev->entity);
1143
1144 xvip_cleanup_resources(&xtpg->xvip);
1145
1146 return 0;
1147}
1148
1149static SIMPLE_DEV_PM_OPS(xtpg_pm_ops, xtpg_pm_suspend, xtpg_pm_resume);
1150
1151static const struct of_device_id xtpg_of_id_table[] = {
1152 { .compatible = "xlnx,v-tpg-5.0" },
1153 { .compatible = "xlnx,v-tpg-7.0" },
1154 { }
1155};
1156MODULE_DEVICE_TABLE(of, xtpg_of_id_table);
1157
1158static struct platform_driver xtpg_driver = {
1159 .driver = {
1160 .name = "xilinx-tpg",
1161 .pm = &xtpg_pm_ops,
1162 .of_match_table = xtpg_of_id_table,
1163 },
1164 .probe = xtpg_probe,
1165 .remove = xtpg_remove,
1166};
1167
1168module_platform_driver(xtpg_driver);
1169
1170MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
1171MODULE_DESCRIPTION("Xilinx Test Pattern Generator Driver");
1172MODULE_LICENSE("GPL v2");
1173