1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | /* |
3 | * Xilinx Test Pattern Generator |
4 | * |
5 | * Copyright (C) 2013-2015 Ideas on Board |
6 | * Copyright (C) 2013-2015 Xilinx, Inc. |
7 | * |
8 | * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> |
9 | * Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
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/of_graph.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/xilinx-v4l2-controls.h> |
19 | |
20 | #include <media/v4l2-async.h> |
21 | #include <media/v4l2-ctrls.h> |
22 | #include <media/v4l2-subdev.h> |
23 | |
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 | * The minimum blanking value is one clock cycle for the front porch, one clock |
64 | * cycle for the sync pulse and one clock cycle for the back porch. |
65 | */ |
66 | #define XTPG_MIN_HBLANK 3 |
67 | #define XTPG_MAX_HBLANK (XVTC_MAX_HSIZE - XVIP_MIN_WIDTH) |
68 | #define XTPG_MIN_VBLANK 3 |
69 | #define XTPG_MAX_VBLANK (XVTC_MAX_VSIZE - XVIP_MIN_HEIGHT) |
70 | |
71 | /** |
72 | * struct xtpg_device - Xilinx Test Pattern Generator device structure |
73 | * @xvip: Xilinx Video IP device |
74 | * @pads: media pads |
75 | * @npads: number of pads (1 or 2) |
76 | * @has_input: whether an input is connected to the sink pad |
77 | * @formats: active V4L2 media bus format for each pad |
78 | * @default_format: default V4L2 media bus format |
79 | * @vip_format: format information corresponding to the active format |
80 | * @bayer: boolean flag if TPG is set to any bayer format |
81 | * @ctrl_handler: control handler |
82 | * @hblank: horizontal blanking control |
83 | * @vblank: vertical blanking control |
84 | * @pattern: test pattern control |
85 | * @streaming: is the video stream active |
86 | * @vtc: video timing controller |
87 | * @vtmux_gpio: video timing mux GPIO |
88 | */ |
89 | struct xtpg_device { |
90 | struct xvip_device xvip; |
91 | |
92 | struct media_pad pads[2]; |
93 | unsigned int npads; |
94 | bool has_input; |
95 | |
96 | struct v4l2_mbus_framefmt formats[2]; |
97 | struct v4l2_mbus_framefmt default_format; |
98 | const struct xvip_video_format *vip_format; |
99 | bool bayer; |
100 | |
101 | struct v4l2_ctrl_handler ctrl_handler; |
102 | struct v4l2_ctrl *hblank; |
103 | struct v4l2_ctrl *vblank; |
104 | struct v4l2_ctrl *pattern; |
105 | bool streaming; |
106 | |
107 | struct xvtc_device *vtc; |
108 | struct gpio_desc *vtmux_gpio; |
109 | }; |
110 | |
111 | static inline struct xtpg_device *to_tpg(struct v4l2_subdev *subdev) |
112 | { |
113 | return container_of(subdev, struct xtpg_device, xvip.subdev); |
114 | } |
115 | |
116 | static u32 xtpg_get_bayer_phase(unsigned int code) |
117 | { |
118 | switch (code) { |
119 | case MEDIA_BUS_FMT_SRGGB8_1X8: |
120 | return XTPG_BAYER_PHASE_RGGB; |
121 | case MEDIA_BUS_FMT_SGRBG8_1X8: |
122 | return XTPG_BAYER_PHASE_GRBG; |
123 | case MEDIA_BUS_FMT_SGBRG8_1X8: |
124 | return XTPG_BAYER_PHASE_GBRG; |
125 | case MEDIA_BUS_FMT_SBGGR8_1X8: |
126 | return XTPG_BAYER_PHASE_BGGR; |
127 | default: |
128 | return XTPG_BAYER_PHASE_OFF; |
129 | } |
130 | } |
131 | |
132 | static void __xtpg_update_pattern_control(struct xtpg_device *xtpg, |
133 | bool passthrough, bool pattern) |
134 | { |
135 | u32 pattern_mask = (1 << (xtpg->pattern->maximum + 1)) - 1; |
136 | |
137 | /* |
138 | * If the TPG has no sink pad or no input connected to its sink pad |
139 | * passthrough mode can't be enabled. |
140 | */ |
141 | if (xtpg->npads == 1 || !xtpg->has_input) |
142 | passthrough = false; |
143 | |
144 | /* If passthrough mode is allowed unmask bit 0. */ |
145 | if (passthrough) |
146 | pattern_mask &= ~1; |
147 | |
148 | /* If test pattern mode is allowed unmask all other bits. */ |
149 | if (pattern) |
150 | pattern_mask &= 1; |
151 | |
152 | __v4l2_ctrl_modify_range(ctrl: xtpg->pattern, min: 0, max: xtpg->pattern->maximum, |
153 | step: pattern_mask, def: pattern ? 9 : 0); |
154 | } |
155 | |
156 | static void xtpg_update_pattern_control(struct xtpg_device *xtpg, |
157 | bool passthrough, bool pattern) |
158 | { |
159 | mutex_lock(xtpg->ctrl_handler.lock); |
160 | __xtpg_update_pattern_control(xtpg, passthrough, pattern); |
161 | mutex_unlock(lock: xtpg->ctrl_handler.lock); |
162 | } |
163 | |
164 | /* ----------------------------------------------------------------------------- |
165 | * V4L2 Subdevice Video Operations |
166 | */ |
167 | |
168 | static int xtpg_s_stream(struct v4l2_subdev *subdev, int enable) |
169 | { |
170 | struct xtpg_device *xtpg = to_tpg(subdev); |
171 | unsigned int width = xtpg->formats[0].width; |
172 | unsigned int height = xtpg->formats[0].height; |
173 | bool passthrough; |
174 | u32 bayer_phase; |
175 | |
176 | if (!enable) { |
177 | xvip_stop(xvip: &xtpg->xvip); |
178 | if (xtpg->vtc) |
179 | xvtc_generator_stop(xvtc: xtpg->vtc); |
180 | |
181 | xtpg_update_pattern_control(xtpg, passthrough: true, pattern: true); |
182 | xtpg->streaming = false; |
183 | return 0; |
184 | } |
185 | |
186 | xvip_set_frame_size(xvip: &xtpg->xvip, format: &xtpg->formats[0]); |
187 | |
188 | if (xtpg->vtc) { |
189 | struct xvtc_config config = { |
190 | .hblank_start = width, |
191 | .hsync_start = width + 1, |
192 | .vblank_start = height, |
193 | .vsync_start = height + 1, |
194 | }; |
195 | unsigned int htotal; |
196 | unsigned int vtotal; |
197 | |
198 | htotal = min_t(unsigned int, XVTC_MAX_HSIZE, |
199 | v4l2_ctrl_g_ctrl(xtpg->hblank) + width); |
200 | vtotal = min_t(unsigned int, XVTC_MAX_VSIZE, |
201 | v4l2_ctrl_g_ctrl(xtpg->vblank) + height); |
202 | |
203 | config.hsync_end = htotal - 1; |
204 | config.hsize = htotal; |
205 | config.vsync_end = vtotal - 1; |
206 | config.vsize = vtotal; |
207 | |
208 | xvtc_generator_start(xvtc: xtpg->vtc, config: &config); |
209 | } |
210 | |
211 | /* |
212 | * Configure the bayer phase and video timing mux based on the |
213 | * operation mode (passthrough or test pattern generation). The test |
214 | * pattern can be modified by the control set handler, we thus need to |
215 | * take the control lock here to avoid races. |
216 | */ |
217 | mutex_lock(xtpg->ctrl_handler.lock); |
218 | |
219 | xvip_clr_and_set(xvip: &xtpg->xvip, XTPG_PATTERN_CONTROL, |
220 | XTPG_PATTERN_MASK, set: xtpg->pattern->cur.val); |
221 | |
222 | /* |
223 | * Switching between passthrough and test pattern generation modes isn't |
224 | * allowed during streaming, update the control range accordingly. |
225 | */ |
226 | passthrough = xtpg->pattern->cur.val == 0; |
227 | __xtpg_update_pattern_control(xtpg, passthrough, pattern: !passthrough); |
228 | |
229 | xtpg->streaming = true; |
230 | |
231 | mutex_unlock(lock: xtpg->ctrl_handler.lock); |
232 | |
233 | /* |
234 | * For TPG v5.0, the bayer phase needs to be off for the pass through |
235 | * mode, otherwise the external input would be subsampled. |
236 | */ |
237 | bayer_phase = passthrough ? XTPG_BAYER_PHASE_OFF |
238 | : xtpg_get_bayer_phase(code: xtpg->formats[0].code); |
239 | xvip_write(xvip: &xtpg->xvip, XTPG_BAYER_PHASE, value: bayer_phase); |
240 | |
241 | if (xtpg->vtmux_gpio) |
242 | gpiod_set_value_cansleep(desc: xtpg->vtmux_gpio, value: !passthrough); |
243 | |
244 | xvip_start(xvip: &xtpg->xvip); |
245 | |
246 | return 0; |
247 | } |
248 | |
249 | /* ----------------------------------------------------------------------------- |
250 | * V4L2 Subdevice Pad Operations |
251 | */ |
252 | |
253 | static struct v4l2_mbus_framefmt * |
254 | __xtpg_get_pad_format(struct xtpg_device *xtpg, |
255 | struct v4l2_subdev_state *sd_state, |
256 | unsigned int pad, u32 which) |
257 | { |
258 | switch (which) { |
259 | case V4L2_SUBDEV_FORMAT_TRY: |
260 | return v4l2_subdev_state_get_format(sd_state, pad); |
261 | case V4L2_SUBDEV_FORMAT_ACTIVE: |
262 | return &xtpg->formats[pad]; |
263 | default: |
264 | return NULL; |
265 | } |
266 | } |
267 | |
268 | static int xtpg_get_format(struct v4l2_subdev *subdev, |
269 | struct v4l2_subdev_state *sd_state, |
270 | struct v4l2_subdev_format *fmt) |
271 | { |
272 | struct xtpg_device *xtpg = to_tpg(subdev); |
273 | |
274 | fmt->format = *__xtpg_get_pad_format(xtpg, sd_state, pad: fmt->pad, |
275 | which: fmt->which); |
276 | |
277 | return 0; |
278 | } |
279 | |
280 | static int xtpg_set_format(struct v4l2_subdev *subdev, |
281 | struct v4l2_subdev_state *sd_state, |
282 | struct v4l2_subdev_format *fmt) |
283 | { |
284 | struct xtpg_device *xtpg = to_tpg(subdev); |
285 | struct v4l2_mbus_framefmt *__format; |
286 | u32 bayer_phase; |
287 | |
288 | __format = __xtpg_get_pad_format(xtpg, sd_state, pad: fmt->pad, which: fmt->which); |
289 | |
290 | /* In two pads mode the source pad format is always identical to the |
291 | * sink pad format. |
292 | */ |
293 | if (xtpg->npads == 2 && fmt->pad == 1) { |
294 | fmt->format = *__format; |
295 | return 0; |
296 | } |
297 | |
298 | /* Bayer phase is configurable at runtime */ |
299 | if (xtpg->bayer) { |
300 | bayer_phase = xtpg_get_bayer_phase(code: fmt->format.code); |
301 | if (bayer_phase != XTPG_BAYER_PHASE_OFF) |
302 | __format->code = fmt->format.code; |
303 | } |
304 | |
305 | xvip_set_format_size(format: __format, fmt); |
306 | |
307 | fmt->format = *__format; |
308 | |
309 | /* Propagate the format to the source pad. */ |
310 | if (xtpg->npads == 2) { |
311 | __format = __xtpg_get_pad_format(xtpg, sd_state, pad: 1, |
312 | which: fmt->which); |
313 | *__format = fmt->format; |
314 | } |
315 | |
316 | return 0; |
317 | } |
318 | |
319 | /* ----------------------------------------------------------------------------- |
320 | * V4L2 Subdevice Operations |
321 | */ |
322 | |
323 | static int xtpg_enum_frame_size(struct v4l2_subdev *subdev, |
324 | struct v4l2_subdev_state *sd_state, |
325 | struct v4l2_subdev_frame_size_enum *fse) |
326 | { |
327 | struct v4l2_mbus_framefmt *format; |
328 | |
329 | format = v4l2_subdev_state_get_format(sd_state, fse->pad); |
330 | |
331 | if (fse->index || fse->code != format->code) |
332 | return -EINVAL; |
333 | |
334 | /* Min / max values for pad 0 is always fixed in both one and two pads |
335 | * modes. In two pads mode, the source pad(= 1) size is identical to |
336 | * the sink pad size */ |
337 | if (fse->pad == 0) { |
338 | fse->min_width = XVIP_MIN_WIDTH; |
339 | fse->max_width = XVIP_MAX_WIDTH; |
340 | fse->min_height = XVIP_MIN_HEIGHT; |
341 | fse->max_height = XVIP_MAX_HEIGHT; |
342 | } else { |
343 | fse->min_width = format->width; |
344 | fse->max_width = format->width; |
345 | fse->min_height = format->height; |
346 | fse->max_height = format->height; |
347 | } |
348 | |
349 | return 0; |
350 | } |
351 | |
352 | static int xtpg_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) |
353 | { |
354 | struct xtpg_device *xtpg = to_tpg(subdev); |
355 | struct v4l2_mbus_framefmt *format; |
356 | |
357 | format = v4l2_subdev_state_get_format(fh->state, 0); |
358 | *format = xtpg->default_format; |
359 | |
360 | if (xtpg->npads == 2) { |
361 | format = v4l2_subdev_state_get_format(fh->state, 1); |
362 | *format = xtpg->default_format; |
363 | } |
364 | |
365 | return 0; |
366 | } |
367 | |
368 | static int xtpg_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) |
369 | { |
370 | return 0; |
371 | } |
372 | |
373 | static int xtpg_s_ctrl(struct v4l2_ctrl *ctrl) |
374 | { |
375 | struct xtpg_device *xtpg = container_of(ctrl->handler, |
376 | struct xtpg_device, |
377 | ctrl_handler); |
378 | switch (ctrl->id) { |
379 | case V4L2_CID_TEST_PATTERN: |
380 | xvip_clr_and_set(xvip: &xtpg->xvip, XTPG_PATTERN_CONTROL, |
381 | XTPG_PATTERN_MASK, set: ctrl->val); |
382 | return 0; |
383 | case V4L2_CID_XILINX_TPG_CROSS_HAIRS: |
384 | xvip_clr_or_set(xvip: &xtpg->xvip, XTPG_PATTERN_CONTROL, |
385 | XTPG_PATTERN_CONTROL_CROSS_HAIRS, set: ctrl->val); |
386 | return 0; |
387 | case V4L2_CID_XILINX_TPG_MOVING_BOX: |
388 | xvip_clr_or_set(xvip: &xtpg->xvip, XTPG_PATTERN_CONTROL, |
389 | XTPG_PATTERN_CONTROL_MOVING_BOX, set: ctrl->val); |
390 | return 0; |
391 | case V4L2_CID_XILINX_TPG_COLOR_MASK: |
392 | xvip_clr_and_set(xvip: &xtpg->xvip, XTPG_PATTERN_CONTROL, |
393 | XTPG_PATTERN_CONTROL_COLOR_MASK_MASK, |
394 | set: ctrl->val << |
395 | XTPG_PATTERN_CONTROL_COLOR_MASK_SHIFT); |
396 | return 0; |
397 | case V4L2_CID_XILINX_TPG_STUCK_PIXEL: |
398 | xvip_clr_or_set(xvip: &xtpg->xvip, XTPG_PATTERN_CONTROL, |
399 | XTPG_PATTERN_CONTROL_STUCK_PIXEL, set: ctrl->val); |
400 | return 0; |
401 | case V4L2_CID_XILINX_TPG_NOISE: |
402 | xvip_clr_or_set(xvip: &xtpg->xvip, XTPG_PATTERN_CONTROL, |
403 | XTPG_PATTERN_CONTROL_NOISE, set: ctrl->val); |
404 | return 0; |
405 | case V4L2_CID_XILINX_TPG_MOTION: |
406 | xvip_clr_or_set(xvip: &xtpg->xvip, XTPG_PATTERN_CONTROL, |
407 | XTPG_PATTERN_CONTROL_MOTION, set: ctrl->val); |
408 | return 0; |
409 | case V4L2_CID_XILINX_TPG_MOTION_SPEED: |
410 | xvip_write(xvip: &xtpg->xvip, XTPG_MOTION_SPEED, value: ctrl->val); |
411 | return 0; |
412 | case V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW: |
413 | xvip_clr_and_set(xvip: &xtpg->xvip, XTPG_CROSS_HAIRS, |
414 | XTPG_CROSS_HAIRS_ROW_MASK, |
415 | set: ctrl->val << XTPG_CROSS_HAIRS_ROW_SHIFT); |
416 | return 0; |
417 | case V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN: |
418 | xvip_clr_and_set(xvip: &xtpg->xvip, XTPG_CROSS_HAIRS, |
419 | XTPG_CROSS_HAIRS_COLUMN_MASK, |
420 | set: ctrl->val << XTPG_CROSS_HAIRS_COLUMN_SHIFT); |
421 | return 0; |
422 | case V4L2_CID_XILINX_TPG_ZPLATE_HOR_START: |
423 | xvip_clr_and_set(xvip: &xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL, |
424 | XTPG_ZPLATE_START_MASK, |
425 | set: ctrl->val << XTPG_ZPLATE_START_SHIFT); |
426 | return 0; |
427 | case V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED: |
428 | xvip_clr_and_set(xvip: &xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL, |
429 | XTPG_ZPLATE_SPEED_MASK, |
430 | set: ctrl->val << XTPG_ZPLATE_SPEED_SHIFT); |
431 | return 0; |
432 | case V4L2_CID_XILINX_TPG_ZPLATE_VER_START: |
433 | xvip_clr_and_set(xvip: &xtpg->xvip, XTPG_ZPLATE_VER_CONTROL, |
434 | XTPG_ZPLATE_START_MASK, |
435 | set: ctrl->val << XTPG_ZPLATE_START_SHIFT); |
436 | return 0; |
437 | case V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED: |
438 | xvip_clr_and_set(xvip: &xtpg->xvip, XTPG_ZPLATE_VER_CONTROL, |
439 | XTPG_ZPLATE_SPEED_MASK, |
440 | set: ctrl->val << XTPG_ZPLATE_SPEED_SHIFT); |
441 | return 0; |
442 | case V4L2_CID_XILINX_TPG_BOX_SIZE: |
443 | xvip_write(xvip: &xtpg->xvip, XTPG_BOX_SIZE, value: ctrl->val); |
444 | return 0; |
445 | case V4L2_CID_XILINX_TPG_BOX_COLOR: |
446 | xvip_write(xvip: &xtpg->xvip, XTPG_BOX_COLOR, value: ctrl->val); |
447 | return 0; |
448 | case V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH: |
449 | xvip_write(xvip: &xtpg->xvip, XTPG_STUCK_PIXEL_THRESH, value: ctrl->val); |
450 | return 0; |
451 | case V4L2_CID_XILINX_TPG_NOISE_GAIN: |
452 | xvip_write(xvip: &xtpg->xvip, XTPG_NOISE_GAIN, value: ctrl->val); |
453 | return 0; |
454 | } |
455 | |
456 | return 0; |
457 | } |
458 | |
459 | static const struct v4l2_ctrl_ops xtpg_ctrl_ops = { |
460 | .s_ctrl = xtpg_s_ctrl, |
461 | }; |
462 | |
463 | static const struct v4l2_subdev_core_ops xtpg_core_ops = { |
464 | }; |
465 | |
466 | static const struct v4l2_subdev_video_ops xtpg_video_ops = { |
467 | .s_stream = xtpg_s_stream, |
468 | }; |
469 | |
470 | static const struct v4l2_subdev_pad_ops xtpg_pad_ops = { |
471 | .enum_mbus_code = xvip_enum_mbus_code, |
472 | .enum_frame_size = xtpg_enum_frame_size, |
473 | .get_fmt = xtpg_get_format, |
474 | .set_fmt = xtpg_set_format, |
475 | }; |
476 | |
477 | static const struct v4l2_subdev_ops xtpg_ops = { |
478 | .core = &xtpg_core_ops, |
479 | .video = &xtpg_video_ops, |
480 | .pad = &xtpg_pad_ops, |
481 | }; |
482 | |
483 | static const struct v4l2_subdev_internal_ops xtpg_internal_ops = { |
484 | .open = xtpg_open, |
485 | .close = xtpg_close, |
486 | }; |
487 | |
488 | /* |
489 | * Control Config |
490 | */ |
491 | |
492 | static const char *const xtpg_pattern_strings[] = { |
493 | "Passthrough", |
494 | "Horizontal Ramp", |
495 | "Vertical Ramp", |
496 | "Temporal Ramp", |
497 | "Solid Red", |
498 | "Solid Green", |
499 | "Solid Blue", |
500 | "Solid Black", |
501 | "Solid White", |
502 | "Color Bars", |
503 | "Zone Plate", |
504 | "Tartan Color Bars", |
505 | "Cross Hatch", |
506 | "None", |
507 | "Vertical/Horizontal Ramps", |
508 | "Black/White Checker Board", |
509 | }; |
510 | |
511 | static struct v4l2_ctrl_config xtpg_ctrls[] = { |
512 | { |
513 | .ops = &xtpg_ctrl_ops, |
514 | .id = V4L2_CID_XILINX_TPG_CROSS_HAIRS, |
515 | .name = "Test Pattern: Cross Hairs", |
516 | .type = V4L2_CTRL_TYPE_BOOLEAN, |
517 | .min = false, |
518 | .max = true, |
519 | .step = 1, |
520 | .def = 0, |
521 | }, { |
522 | .ops = &xtpg_ctrl_ops, |
523 | .id = V4L2_CID_XILINX_TPG_MOVING_BOX, |
524 | .name = "Test Pattern: Moving Box", |
525 | .type = V4L2_CTRL_TYPE_BOOLEAN, |
526 | .min = false, |
527 | .max = true, |
528 | .step = 1, |
529 | .def = 0, |
530 | }, { |
531 | .ops = &xtpg_ctrl_ops, |
532 | .id = V4L2_CID_XILINX_TPG_COLOR_MASK, |
533 | .name = "Test Pattern: Color Mask", |
534 | .type = V4L2_CTRL_TYPE_BITMASK, |
535 | .min = 0, |
536 | .max = 0xf, |
537 | .def = 0, |
538 | }, { |
539 | .ops = &xtpg_ctrl_ops, |
540 | .id = V4L2_CID_XILINX_TPG_STUCK_PIXEL, |
541 | .name = "Test Pattern: Stuck Pixel", |
542 | .type = V4L2_CTRL_TYPE_BOOLEAN, |
543 | .min = false, |
544 | .max = true, |
545 | .step = 1, |
546 | .def = 0, |
547 | }, { |
548 | .ops = &xtpg_ctrl_ops, |
549 | .id = V4L2_CID_XILINX_TPG_NOISE, |
550 | .name = "Test Pattern: Noise", |
551 | .type = V4L2_CTRL_TYPE_BOOLEAN, |
552 | .min = false, |
553 | .max = true, |
554 | .step = 1, |
555 | .def = 0, |
556 | }, { |
557 | .ops = &xtpg_ctrl_ops, |
558 | .id = V4L2_CID_XILINX_TPG_MOTION, |
559 | .name = "Test Pattern: Motion", |
560 | .type = V4L2_CTRL_TYPE_BOOLEAN, |
561 | .min = false, |
562 | .max = true, |
563 | .step = 1, |
564 | .def = 0, |
565 | }, { |
566 | .ops = &xtpg_ctrl_ops, |
567 | .id = V4L2_CID_XILINX_TPG_MOTION_SPEED, |
568 | .name = "Test Pattern: Motion Speed", |
569 | .type = V4L2_CTRL_TYPE_INTEGER, |
570 | .min = 0, |
571 | .max = (1 << 8) - 1, |
572 | .step = 1, |
573 | .def = 4, |
574 | .flags = V4L2_CTRL_FLAG_SLIDER, |
575 | }, { |
576 | .ops = &xtpg_ctrl_ops, |
577 | .id = V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW, |
578 | .name = "Test Pattern: Cross Hairs Row", |
579 | .type = V4L2_CTRL_TYPE_INTEGER, |
580 | .min = 0, |
581 | .max = (1 << 12) - 1, |
582 | .step = 1, |
583 | .def = 0x64, |
584 | .flags = V4L2_CTRL_FLAG_SLIDER, |
585 | }, { |
586 | .ops = &xtpg_ctrl_ops, |
587 | .id = V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN, |
588 | .name = "Test Pattern: Cross Hairs Column", |
589 | .type = V4L2_CTRL_TYPE_INTEGER, |
590 | .min = 0, |
591 | .max = (1 << 12) - 1, |
592 | .step = 1, |
593 | .def = 0x64, |
594 | .flags = V4L2_CTRL_FLAG_SLIDER, |
595 | }, { |
596 | .ops = &xtpg_ctrl_ops, |
597 | .id = V4L2_CID_XILINX_TPG_ZPLATE_HOR_START, |
598 | .name = "Test Pattern: Zplate Horizontal Start Pos", |
599 | .type = V4L2_CTRL_TYPE_INTEGER, |
600 | .min = 0, |
601 | .max = (1 << 16) - 1, |
602 | .step = 1, |
603 | .def = 0x1e, |
604 | .flags = V4L2_CTRL_FLAG_SLIDER, |
605 | }, { |
606 | .ops = &xtpg_ctrl_ops, |
607 | .id = V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED, |
608 | .name = "Test Pattern: Zplate Horizontal Speed", |
609 | .type = V4L2_CTRL_TYPE_INTEGER, |
610 | .min = 0, |
611 | .max = (1 << 16) - 1, |
612 | .step = 1, |
613 | .def = 0, |
614 | .flags = V4L2_CTRL_FLAG_SLIDER, |
615 | }, { |
616 | .ops = &xtpg_ctrl_ops, |
617 | .id = V4L2_CID_XILINX_TPG_ZPLATE_VER_START, |
618 | .name = "Test Pattern: Zplate Vertical Start Pos", |
619 | .type = V4L2_CTRL_TYPE_INTEGER, |
620 | .min = 0, |
621 | .max = (1 << 16) - 1, |
622 | .step = 1, |
623 | .def = 1, |
624 | .flags = V4L2_CTRL_FLAG_SLIDER, |
625 | }, { |
626 | .ops = &xtpg_ctrl_ops, |
627 | .id = V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED, |
628 | .name = "Test Pattern: Zplate Vertical Speed", |
629 | .type = V4L2_CTRL_TYPE_INTEGER, |
630 | .min = 0, |
631 | .max = (1 << 16) - 1, |
632 | .step = 1, |
633 | .def = 0, |
634 | .flags = V4L2_CTRL_FLAG_SLIDER, |
635 | }, { |
636 | .ops = &xtpg_ctrl_ops, |
637 | .id = V4L2_CID_XILINX_TPG_BOX_SIZE, |
638 | .name = "Test Pattern: Box Size", |
639 | .type = V4L2_CTRL_TYPE_INTEGER, |
640 | .min = 0, |
641 | .max = (1 << 12) - 1, |
642 | .step = 1, |
643 | .def = 0x32, |
644 | .flags = V4L2_CTRL_FLAG_SLIDER, |
645 | }, { |
646 | .ops = &xtpg_ctrl_ops, |
647 | .id = V4L2_CID_XILINX_TPG_BOX_COLOR, |
648 | .name = "Test Pattern: Box Color(RGB)", |
649 | .type = V4L2_CTRL_TYPE_INTEGER, |
650 | .min = 0, |
651 | .max = (1 << 24) - 1, |
652 | .step = 1, |
653 | .def = 0, |
654 | }, { |
655 | .ops = &xtpg_ctrl_ops, |
656 | .id = V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH, |
657 | .name = "Test Pattern: Stuck Pixel threshold", |
658 | .type = V4L2_CTRL_TYPE_INTEGER, |
659 | .min = 0, |
660 | .max = (1 << 16) - 1, |
661 | .step = 1, |
662 | .def = 0, |
663 | .flags = V4L2_CTRL_FLAG_SLIDER, |
664 | }, { |
665 | .ops = &xtpg_ctrl_ops, |
666 | .id = V4L2_CID_XILINX_TPG_NOISE_GAIN, |
667 | .name = "Test Pattern: Noise Gain", |
668 | .type = V4L2_CTRL_TYPE_INTEGER, |
669 | .min = 0, |
670 | .max = (1 << 8) - 1, |
671 | .step = 1, |
672 | .def = 0, |
673 | .flags = V4L2_CTRL_FLAG_SLIDER, |
674 | }, |
675 | }; |
676 | |
677 | /* ----------------------------------------------------------------------------- |
678 | * Media Operations |
679 | */ |
680 | |
681 | static const struct media_entity_operations xtpg_media_ops = { |
682 | .link_validate = v4l2_subdev_link_validate, |
683 | }; |
684 | |
685 | /* ----------------------------------------------------------------------------- |
686 | * Power Management |
687 | */ |
688 | |
689 | static int __maybe_unused xtpg_pm_suspend(struct device *dev) |
690 | { |
691 | struct xtpg_device *xtpg = dev_get_drvdata(dev); |
692 | |
693 | xvip_suspend(xvip: &xtpg->xvip); |
694 | |
695 | return 0; |
696 | } |
697 | |
698 | static int __maybe_unused xtpg_pm_resume(struct device *dev) |
699 | { |
700 | struct xtpg_device *xtpg = dev_get_drvdata(dev); |
701 | |
702 | xvip_resume(xvip: &xtpg->xvip); |
703 | |
704 | return 0; |
705 | } |
706 | |
707 | /* ----------------------------------------------------------------------------- |
708 | * Platform Device Driver |
709 | */ |
710 | |
711 | static int xtpg_parse_of(struct xtpg_device *xtpg) |
712 | { |
713 | struct device *dev = xtpg->xvip.dev; |
714 | struct device_node *node = xtpg->xvip.dev->of_node; |
715 | unsigned int nports = 0; |
716 | bool has_endpoint = false; |
717 | |
718 | for_each_of_graph_port(node, port) { |
719 | const struct xvip_video_format *format; |
720 | struct device_node *endpoint; |
721 | |
722 | format = xvip_of_get_format(node: port); |
723 | if (IS_ERR(ptr: format)) { |
724 | dev_err(dev, "invalid format in DT"); |
725 | return PTR_ERR(ptr: format); |
726 | } |
727 | |
728 | /* Get and check the format description */ |
729 | if (!xtpg->vip_format) { |
730 | xtpg->vip_format = format; |
731 | } else if (xtpg->vip_format != format) { |
732 | dev_err(dev, "in/out format mismatch in DT"); |
733 | return -EINVAL; |
734 | } |
735 | |
736 | if (nports == 0) { |
737 | endpoint = of_graph_get_next_port_endpoint(port, NULL); |
738 | if (endpoint) |
739 | has_endpoint = true; |
740 | of_node_put(node: endpoint); |
741 | } |
742 | |
743 | /* Count the number of ports. */ |
744 | nports++; |
745 | } |
746 | |
747 | if (nports != 1 && nports != 2) { |
748 | dev_err(dev, "invalid number of ports %u\n", nports); |
749 | return -EINVAL; |
750 | } |
751 | |
752 | xtpg->npads = nports; |
753 | if (nports == 2 && has_endpoint) |
754 | xtpg->has_input = true; |
755 | |
756 | return 0; |
757 | } |
758 | |
759 | static int xtpg_probe(struct platform_device *pdev) |
760 | { |
761 | struct v4l2_subdev *subdev; |
762 | struct xtpg_device *xtpg; |
763 | u32 i, bayer_phase; |
764 | int ret; |
765 | |
766 | xtpg = devm_kzalloc(dev: &pdev->dev, size: sizeof(*xtpg), GFP_KERNEL); |
767 | if (!xtpg) |
768 | return -ENOMEM; |
769 | |
770 | xtpg->xvip.dev = &pdev->dev; |
771 | |
772 | ret = xtpg_parse_of(xtpg); |
773 | if (ret < 0) |
774 | return ret; |
775 | |
776 | ret = xvip_init_resources(xvip: &xtpg->xvip); |
777 | if (ret < 0) |
778 | return ret; |
779 | |
780 | xtpg->vtmux_gpio = devm_gpiod_get_optional(dev: &pdev->dev, con_id: "timing", |
781 | flags: GPIOD_OUT_HIGH); |
782 | if (IS_ERR(ptr: xtpg->vtmux_gpio)) { |
783 | ret = PTR_ERR(ptr: xtpg->vtmux_gpio); |
784 | goto error_resource; |
785 | } |
786 | |
787 | xtpg->vtc = xvtc_of_get(np: pdev->dev.of_node); |
788 | if (IS_ERR(ptr: xtpg->vtc)) { |
789 | ret = PTR_ERR(ptr: xtpg->vtc); |
790 | goto error_resource; |
791 | } |
792 | |
793 | /* Reset and initialize the core */ |
794 | xvip_reset(xvip: &xtpg->xvip); |
795 | |
796 | /* Initialize V4L2 subdevice and media entity. Pad numbers depend on the |
797 | * number of pads. |
798 | */ |
799 | if (xtpg->npads == 2) { |
800 | xtpg->pads[0].flags = MEDIA_PAD_FL_SINK; |
801 | xtpg->pads[1].flags = MEDIA_PAD_FL_SOURCE; |
802 | } else { |
803 | xtpg->pads[0].flags = MEDIA_PAD_FL_SOURCE; |
804 | } |
805 | |
806 | /* Initialize the default format */ |
807 | xtpg->default_format.code = xtpg->vip_format->code; |
808 | xtpg->default_format.field = V4L2_FIELD_NONE; |
809 | xtpg->default_format.colorspace = V4L2_COLORSPACE_SRGB; |
810 | xvip_get_frame_size(xvip: &xtpg->xvip, format: &xtpg->default_format); |
811 | |
812 | bayer_phase = xtpg_get_bayer_phase(code: xtpg->vip_format->code); |
813 | if (bayer_phase != XTPG_BAYER_PHASE_OFF) |
814 | xtpg->bayer = true; |
815 | |
816 | xtpg->formats[0] = xtpg->default_format; |
817 | if (xtpg->npads == 2) |
818 | xtpg->formats[1] = xtpg->default_format; |
819 | |
820 | /* Initialize V4L2 subdevice and media entity */ |
821 | subdev = &xtpg->xvip.subdev; |
822 | v4l2_subdev_init(sd: subdev, ops: &xtpg_ops); |
823 | subdev->dev = &pdev->dev; |
824 | subdev->internal_ops = &xtpg_internal_ops; |
825 | strscpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); |
826 | v4l2_set_subdevdata(sd: subdev, p: xtpg); |
827 | subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; |
828 | subdev->entity.ops = &xtpg_media_ops; |
829 | |
830 | ret = media_entity_pads_init(entity: &subdev->entity, num_pads: xtpg->npads, pads: xtpg->pads); |
831 | if (ret < 0) |
832 | goto error; |
833 | |
834 | v4l2_ctrl_handler_init(&xtpg->ctrl_handler, 3 + ARRAY_SIZE(xtpg_ctrls)); |
835 | |
836 | xtpg->vblank = v4l2_ctrl_new_std(hdl: &xtpg->ctrl_handler, ops: &xtpg_ctrl_ops, |
837 | V4L2_CID_VBLANK, XTPG_MIN_VBLANK, |
838 | XTPG_MAX_VBLANK, step: 1, def: 100); |
839 | xtpg->hblank = v4l2_ctrl_new_std(hdl: &xtpg->ctrl_handler, ops: &xtpg_ctrl_ops, |
840 | V4L2_CID_HBLANK, XTPG_MIN_HBLANK, |
841 | XTPG_MAX_HBLANK, step: 1, def: 100); |
842 | xtpg->pattern = v4l2_ctrl_new_std_menu_items(hdl: &xtpg->ctrl_handler, |
843 | ops: &xtpg_ctrl_ops, V4L2_CID_TEST_PATTERN, |
844 | ARRAY_SIZE(xtpg_pattern_strings) - 1, |
845 | mask: 1, def: 9, qmenu: xtpg_pattern_strings); |
846 | |
847 | for (i = 0; i < ARRAY_SIZE(xtpg_ctrls); i++) |
848 | v4l2_ctrl_new_custom(hdl: &xtpg->ctrl_handler, cfg: &xtpg_ctrls[i], NULL); |
849 | |
850 | if (xtpg->ctrl_handler.error) { |
851 | dev_err(&pdev->dev, "failed to add controls\n"); |
852 | ret = xtpg->ctrl_handler.error; |
853 | goto error; |
854 | } |
855 | subdev->ctrl_handler = &xtpg->ctrl_handler; |
856 | |
857 | xtpg_update_pattern_control(xtpg, passthrough: true, pattern: true); |
858 | |
859 | ret = v4l2_ctrl_handler_setup(hdl: &xtpg->ctrl_handler); |
860 | if (ret < 0) { |
861 | dev_err(&pdev->dev, "failed to set controls\n"); |
862 | goto error; |
863 | } |
864 | |
865 | platform_set_drvdata(pdev, data: xtpg); |
866 | |
867 | xvip_print_version(xvip: &xtpg->xvip); |
868 | |
869 | ret = v4l2_async_register_subdev(subdev); |
870 | if (ret < 0) { |
871 | dev_err(&pdev->dev, "failed to register subdev\n"); |
872 | goto error; |
873 | } |
874 | |
875 | return 0; |
876 | |
877 | error: |
878 | v4l2_ctrl_handler_free(hdl: &xtpg->ctrl_handler); |
879 | media_entity_cleanup(entity: &subdev->entity); |
880 | xvtc_put(xvtc: xtpg->vtc); |
881 | error_resource: |
882 | xvip_cleanup_resources(xvip: &xtpg->xvip); |
883 | return ret; |
884 | } |
885 | |
886 | static void xtpg_remove(struct platform_device *pdev) |
887 | { |
888 | struct xtpg_device *xtpg = platform_get_drvdata(pdev); |
889 | struct v4l2_subdev *subdev = &xtpg->xvip.subdev; |
890 | |
891 | v4l2_async_unregister_subdev(sd: subdev); |
892 | v4l2_ctrl_handler_free(hdl: &xtpg->ctrl_handler); |
893 | media_entity_cleanup(entity: &subdev->entity); |
894 | |
895 | xvip_cleanup_resources(xvip: &xtpg->xvip); |
896 | } |
897 | |
898 | static SIMPLE_DEV_PM_OPS(xtpg_pm_ops, xtpg_pm_suspend, xtpg_pm_resume); |
899 | |
900 | static const struct of_device_id xtpg_of_id_table[] = { |
901 | { .compatible = "xlnx,v-tpg-5.0"}, |
902 | { } |
903 | }; |
904 | MODULE_DEVICE_TABLE(of, xtpg_of_id_table); |
905 | |
906 | static struct platform_driver xtpg_driver = { |
907 | .driver = { |
908 | .name = "xilinx-tpg", |
909 | .pm = &xtpg_pm_ops, |
910 | .of_match_table = xtpg_of_id_table, |
911 | }, |
912 | .probe = xtpg_probe, |
913 | .remove = xtpg_remove, |
914 | }; |
915 | |
916 | module_platform_driver(xtpg_driver); |
917 | |
918 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); |
919 | MODULE_DESCRIPTION("Xilinx Test Pattern Generator Driver"); |
920 | MODULE_LICENSE("GPL v2"); |
921 |
Definitions
- xtpg_device
- to_tpg
- xtpg_get_bayer_phase
- __xtpg_update_pattern_control
- xtpg_update_pattern_control
- xtpg_s_stream
- __xtpg_get_pad_format
- xtpg_get_format
- xtpg_set_format
- xtpg_enum_frame_size
- xtpg_open
- xtpg_close
- xtpg_s_ctrl
- xtpg_ctrl_ops
- xtpg_core_ops
- xtpg_video_ops
- xtpg_pad_ops
- xtpg_ops
- xtpg_internal_ops
- xtpg_pattern_strings
- xtpg_ctrls
- xtpg_media_ops
- xtpg_pm_suspend
- xtpg_pm_resume
- xtpg_parse_of
- xtpg_probe
- xtpg_remove
- xtpg_pm_ops
- xtpg_of_id_table
Improve your Profiling and Debugging skills
Find out more