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