diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2013-05-15 10:36:56 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@osg.samsung.com> | 2015-04-03 00:04:18 -0400 |
commit | a5562f65b1371a0988b707c10c44fcc2bba56990 (patch) | |
tree | 4c5638cbbab1f8f298c77be9192066afccf69e7b | |
parent | 40ac9b196d07813132e2d5a14aae40d5812f131e (diff) |
[media] v4l: xilinx: Add Test Pattern Generator driver
The TPG generates multiple static or dynamic test patterns. The driver
currently hardcodes the pattern to the moving box pattern.
Signed-off-by: Christian Kohn <christian.kohn@xilinx.com>
Signed-off-by: Hyun Kwon <hyun.kwon@xilinx.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
-rw-r--r-- | Documentation/devicetree/bindings/media/xilinx/xlnx,v-tpg.txt | 71 | ||||
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | drivers/media/platform/xilinx/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/platform/xilinx/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/platform/xilinx/xilinx-tpg.c | 931 | ||||
-rw-r--r-- | include/uapi/linux/Kbuild | 1 | ||||
-rw-r--r-- | include/uapi/linux/xilinx-v4l2-controls.h | 73 |
7 files changed, 1085 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,v-tpg.txt b/Documentation/devicetree/bindings/media/xilinx/xlnx,v-tpg.txt new file mode 100644 index 000000000000..9dd86b3db937 --- /dev/null +++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,v-tpg.txt | |||
@@ -0,0 +1,71 @@ | |||
1 | Xilinx Video Test Pattern Generator (TPG) | ||
2 | ----------------------------------------- | ||
3 | |||
4 | Required properties: | ||
5 | |||
6 | - compatible: Must contain at least one of | ||
7 | |||
8 | "xlnx,v-tpg-5.0" (TPG version 5.0) | ||
9 | "xlnx,v-tpg-6.0" (TPG version 6.0) | ||
10 | |||
11 | TPG versions backward-compatible with previous versions should list all | ||
12 | compatible versions in the newer to older order. | ||
13 | |||
14 | - reg: Physical base address and length of the registers set for the device. | ||
15 | |||
16 | - clocks: Reference to the video core clock. | ||
17 | |||
18 | - xlnx,video-format, xlnx,video-width: Video format and width, as defined in | ||
19 | video.txt. | ||
20 | |||
21 | - port: Video port, using the DT bindings defined in ../video-interfaces.txt. | ||
22 | The TPG has a single output port numbered 0. | ||
23 | |||
24 | Optional properties: | ||
25 | |||
26 | - xlnx,vtc: A phandle referencing the Video Timing Controller that generates | ||
27 | video timings for the TPG test patterns. | ||
28 | |||
29 | - timing-gpios: Specifier for a GPIO that controls the timing mux at the TPG | ||
30 | input. The GPIO active level corresponds to the selection of VTC-generated | ||
31 | video timings. | ||
32 | |||
33 | The xlnx,vtc and timing-gpios properties are mandatory when the TPG is | ||
34 | synthesized with two ports and forbidden when synthesized with one port. | ||
35 | |||
36 | Example: | ||
37 | |||
38 | tpg_0: tpg@40050000 { | ||
39 | compatible = "xlnx,v-tpg-6.0", "xlnx,v-tpg-5.0"; | ||
40 | reg = <0x40050000 0x10000>; | ||
41 | clocks = <&clkc 15>; | ||
42 | |||
43 | xlnx,vtc = <&vtc_3>; | ||
44 | timing-gpios = <&ps7_gpio_0 55 GPIO_ACTIVE_LOW>; | ||
45 | |||
46 | ports { | ||
47 | #address-cells = <1>; | ||
48 | #size-cells = <0>; | ||
49 | |||
50 | port@0 { | ||
51 | reg = <0>; | ||
52 | |||
53 | xlnx,video-format = <XVIP_VF_YUV_422>; | ||
54 | xlnx,video-width = <8>; | ||
55 | |||
56 | tpg_in: endpoint { | ||
57 | remote-endpoint = <&adv7611_out>; | ||
58 | }; | ||
59 | }; | ||
60 | port@1 { | ||
61 | reg = <1>; | ||
62 | |||
63 | xlnx,video-format = <XVIP_VF_YUV_422>; | ||
64 | xlnx,video-width = <8>; | ||
65 | |||
66 | tpg1_out: endpoint { | ||
67 | remote-endpoint = <&switch_in0>; | ||
68 | }; | ||
69 | }: | ||
70 | }; | ||
71 | }; | ||
diff --git a/MAINTAINERS b/MAINTAINERS index ca3b2638fcc6..30e7e38ccad0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -10826,6 +10826,7 @@ T: git git://linuxtv.org/media_tree.git | |||
10826 | S: Supported | 10826 | S: Supported |
10827 | F: Documentation/devicetree/bindings/media/xilinx/ | 10827 | F: Documentation/devicetree/bindings/media/xilinx/ |
10828 | F: drivers/media/platform/xilinx/ | 10828 | F: drivers/media/platform/xilinx/ |
10829 | F: include/uapi/linux/xilinx-v4l2-controls.h | ||
10829 | 10830 | ||
10830 | XILLYBUS DRIVER | 10831 | XILLYBUS DRIVER |
10831 | M: Eli Billauer <eli.billauer@gmail.com> | 10832 | M: Eli Billauer <eli.billauer@gmail.com> |
diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig index 19db82343bb8..d7324c726fc2 100644 --- a/drivers/media/platform/xilinx/Kconfig +++ b/drivers/media/platform/xilinx/Kconfig | |||
@@ -7,6 +7,13 @@ config VIDEO_XILINX | |||
7 | 7 | ||
8 | if VIDEO_XILINX | 8 | if VIDEO_XILINX |
9 | 9 | ||
10 | config VIDEO_XILINX_TPG | ||
11 | tristate "Xilinx Video Test Pattern Generator" | ||
12 | depends on VIDEO_XILINX | ||
13 | select VIDEO_XILINX_VTC | ||
14 | ---help--- | ||
15 | Driver for the Xilinx Video Test Pattern Generator | ||
16 | |||
10 | config VIDEO_XILINX_VTC | 17 | config VIDEO_XILINX_VTC |
11 | tristate "Xilinx Video Timing Controller" | 18 | tristate "Xilinx Video Timing Controller" |
12 | depends on VIDEO_XILINX | 19 | depends on VIDEO_XILINX |
diff --git a/drivers/media/platform/xilinx/Makefile b/drivers/media/platform/xilinx/Makefile index 6611e3228e3c..e8a0f2a9f733 100644 --- a/drivers/media/platform/xilinx/Makefile +++ b/drivers/media/platform/xilinx/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o | 1 | xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o |
2 | 2 | ||
3 | obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o | 3 | obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o |
4 | obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o | ||
4 | obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o | 5 | obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o |
diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c new file mode 100644 index 000000000000..b5f7d5ecb7f6 --- /dev/null +++ b/drivers/media/platform/xilinx/xilinx-tpg.c | |||
@@ -0,0 +1,931 @@ | |||
1 | /* | ||
2 | * Xilinx Test Pattern Generator | ||
3 | * | ||
4 | * Copyright (C) 2013-2015 Ideas on Board | ||
5 | * Copyright (C) 2013-2015 Xilinx, Inc. | ||
6 | * | ||
7 | * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> | ||
8 | * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
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-vip.h" | ||
27 | #include "xilinx-vtc.h" | ||
28 | |||
29 | #define XTPG_CTRL_STATUS_SLAVE_ERROR (1 << 16) | ||
30 | #define XTPG_CTRL_IRQ_SLAVE_ERROR (1 << 16) | ||
31 | |||
32 | #define XTPG_PATTERN_CONTROL 0x0100 | ||
33 | #define XTPG_PATTERN_MASK (0xf << 0) | ||
34 | #define XTPG_PATTERN_CONTROL_CROSS_HAIRS (1 << 4) | ||
35 | #define XTPG_PATTERN_CONTROL_MOVING_BOX (1 << 5) | ||
36 | #define XTPG_PATTERN_CONTROL_COLOR_MASK_SHIFT 6 | ||
37 | #define XTPG_PATTERN_CONTROL_COLOR_MASK_MASK (0xf << 6) | ||
38 | #define XTPG_PATTERN_CONTROL_STUCK_PIXEL (1 << 9) | ||
39 | #define XTPG_PATTERN_CONTROL_NOISE (1 << 10) | ||
40 | #define XTPG_PATTERN_CONTROL_MOTION (1 << 12) | ||
41 | #define XTPG_MOTION_SPEED 0x0104 | ||
42 | #define XTPG_CROSS_HAIRS 0x0108 | ||
43 | #define XTPG_CROSS_HAIRS_ROW_SHIFT 0 | ||
44 | #define XTPG_CROSS_HAIRS_ROW_MASK (0xfff << 0) | ||
45 | #define XTPG_CROSS_HAIRS_COLUMN_SHIFT 16 | ||
46 | #define XTPG_CROSS_HAIRS_COLUMN_MASK (0xfff << 16) | ||
47 | #define XTPG_ZPLATE_HOR_CONTROL 0x010c | ||
48 | #define XTPG_ZPLATE_VER_CONTROL 0x0110 | ||
49 | #define XTPG_ZPLATE_START_SHIFT 0 | ||
50 | #define XTPG_ZPLATE_START_MASK (0xffff << 0) | ||
51 | #define XTPG_ZPLATE_SPEED_SHIFT 16 | ||
52 | #define XTPG_ZPLATE_SPEED_MASK (0xffff << 16) | ||
53 | #define XTPG_BOX_SIZE 0x0114 | ||
54 | #define XTPG_BOX_COLOR 0x0118 | ||
55 | #define XTPG_STUCK_PIXEL_THRESH 0x011c | ||
56 | #define XTPG_NOISE_GAIN 0x0120 | ||
57 | #define XTPG_BAYER_PHASE 0x0124 | ||
58 | #define XTPG_BAYER_PHASE_RGGB 0 | ||
59 | #define XTPG_BAYER_PHASE_GRBG 1 | ||
60 | #define XTPG_BAYER_PHASE_GBRG 2 | ||
61 | #define XTPG_BAYER_PHASE_BGGR 3 | ||
62 | #define XTPG_BAYER_PHASE_OFF 4 | ||
63 | |||
64 | /* | ||
65 | * The minimum blanking value is one clock cycle for the front porch, one clock | ||
66 | * cycle for the sync pulse and one clock cycle for the back porch. | ||
67 | */ | ||
68 | #define XTPG_MIN_HBLANK 3 | ||
69 | #define XTPG_MAX_HBLANK (XVTC_MAX_HSIZE - XVIP_MIN_WIDTH) | ||
70 | #define XTPG_MIN_VBLANK 3 | ||
71 | #define XTPG_MAX_VBLANK (XVTC_MAX_VSIZE - XVIP_MIN_HEIGHT) | ||
72 | |||
73 | /** | ||
74 | * struct xtpg_device - Xilinx Test Pattern Generator device structure | ||
75 | * @xvip: Xilinx Video IP device | ||
76 | * @pads: media pads | ||
77 | * @npads: number of pads (1 or 2) | ||
78 | * @has_input: whether an input is connected to the sink pad | ||
79 | * @formats: active V4L2 media bus format for each pad | ||
80 | * @default_format: default V4L2 media bus format | ||
81 | * @vip_format: format information corresponding to the active format | ||
82 | * @bayer: boolean flag if TPG is set to any bayer format | ||
83 | * @ctrl_handler: control handler | ||
84 | * @hblank: horizontal blanking control | ||
85 | * @vblank: vertical blanking control | ||
86 | * @pattern: test pattern control | ||
87 | * @streaming: is the video stream active | ||
88 | * @vtc: video timing controller | ||
89 | * @vtmux_gpio: video timing mux GPIO | ||
90 | */ | ||
91 | struct xtpg_device { | ||
92 | struct xvip_device xvip; | ||
93 | |||
94 | struct media_pad pads[2]; | ||
95 | unsigned int npads; | ||
96 | bool has_input; | ||
97 | |||
98 | struct v4l2_mbus_framefmt formats[2]; | ||
99 | struct v4l2_mbus_framefmt default_format; | ||
100 | const struct xvip_video_format *vip_format; | ||
101 | bool bayer; | ||
102 | |||
103 | struct v4l2_ctrl_handler ctrl_handler; | ||
104 | struct v4l2_ctrl *hblank; | ||
105 | struct v4l2_ctrl *vblank; | ||
106 | struct v4l2_ctrl *pattern; | ||
107 | bool streaming; | ||
108 | |||
109 | struct xvtc_device *vtc; | ||
110 | struct gpio_desc *vtmux_gpio; | ||
111 | }; | ||
112 | |||
113 | static inline struct xtpg_device *to_tpg(struct v4l2_subdev *subdev) | ||
114 | { | ||
115 | return container_of(subdev, struct xtpg_device, xvip.subdev); | ||
116 | } | ||
117 | |||
118 | static u32 xtpg_get_bayer_phase(unsigned int code) | ||
119 | { | ||
120 | switch (code) { | ||
121 | case MEDIA_BUS_FMT_SRGGB8_1X8: | ||
122 | return XTPG_BAYER_PHASE_RGGB; | ||
123 | case MEDIA_BUS_FMT_SGRBG8_1X8: | ||
124 | return XTPG_BAYER_PHASE_GRBG; | ||
125 | case MEDIA_BUS_FMT_SGBRG8_1X8: | ||
126 | return XTPG_BAYER_PHASE_GBRG; | ||
127 | case MEDIA_BUS_FMT_SBGGR8_1X8: | ||
128 | return XTPG_BAYER_PHASE_BGGR; | ||
129 | default: | ||
130 | return XTPG_BAYER_PHASE_OFF; | ||
131 | } | ||
132 | } | ||
133 | |||
134 | static void __xtpg_update_pattern_control(struct xtpg_device *xtpg, | ||
135 | bool passthrough, bool pattern) | ||
136 | { | ||
137 | u32 pattern_mask = (1 << (xtpg->pattern->maximum + 1)) - 1; | ||
138 | |||
139 | /* | ||
140 | * If the TPG has no sink pad or no input connected to its sink pad | ||
141 | * passthrough mode can't be enabled. | ||
142 | */ | ||
143 | if (xtpg->npads == 1 || !xtpg->has_input) | ||
144 | passthrough = false; | ||
145 | |||
146 | /* If passthrough mode is allowed unmask bit 0. */ | ||
147 | if (passthrough) | ||
148 | pattern_mask &= ~1; | ||
149 | |||
150 | /* If test pattern mode is allowed unmask all other bits. */ | ||
151 | if (pattern) | ||
152 | pattern_mask &= 1; | ||
153 | |||
154 | __v4l2_ctrl_modify_range(xtpg->pattern, 0, xtpg->pattern->maximum, | ||
155 | pattern_mask, pattern ? 9 : 0); | ||
156 | } | ||
157 | |||
158 | static void xtpg_update_pattern_control(struct xtpg_device *xtpg, | ||
159 | bool passthrough, bool pattern) | ||
160 | { | ||
161 | mutex_lock(xtpg->ctrl_handler.lock); | ||
162 | __xtpg_update_pattern_control(xtpg, passthrough, pattern); | ||
163 | mutex_unlock(xtpg->ctrl_handler.lock); | ||
164 | } | ||
165 | |||
166 | /* ----------------------------------------------------------------------------- | ||
167 | * V4L2 Subdevice Video Operations | ||
168 | */ | ||
169 | |||
170 | static int xtpg_s_stream(struct v4l2_subdev *subdev, int enable) | ||
171 | { | ||
172 | struct xtpg_device *xtpg = to_tpg(subdev); | ||
173 | unsigned int width = xtpg->formats[0].width; | ||
174 | unsigned int height = xtpg->formats[0].height; | ||
175 | bool passthrough; | ||
176 | u32 bayer_phase; | ||
177 | |||
178 | if (!enable) { | ||
179 | xvip_stop(&xtpg->xvip); | ||
180 | if (xtpg->vtc) | ||
181 | xvtc_generator_stop(xtpg->vtc); | ||
182 | |||
183 | xtpg_update_pattern_control(xtpg, true, true); | ||
184 | xtpg->streaming = false; | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | xvip_set_frame_size(&xtpg->xvip, &xtpg->formats[0]); | ||
189 | |||
190 | if (xtpg->vtc) { | ||
191 | struct xvtc_config config = { | ||
192 | .hblank_start = width, | ||
193 | .hsync_start = width + 1, | ||
194 | .vblank_start = height, | ||
195 | .vsync_start = height + 1, | ||
196 | }; | ||
197 | unsigned int htotal; | ||
198 | unsigned int vtotal; | ||
199 | |||
200 | htotal = min_t(unsigned int, XVTC_MAX_HSIZE, | ||
201 | v4l2_ctrl_g_ctrl(xtpg->hblank) + width); | ||
202 | vtotal = min_t(unsigned int, XVTC_MAX_VSIZE, | ||
203 | v4l2_ctrl_g_ctrl(xtpg->vblank) + height); | ||
204 | |||
205 | config.hsync_end = htotal - 1; | ||
206 | config.hsize = htotal; | ||
207 | config.vsync_end = vtotal - 1; | ||
208 | config.vsize = vtotal; | ||
209 | |||
210 | xvtc_generator_start(xtpg->vtc, &config); | ||
211 | } | ||
212 | |||
213 | /* | ||
214 | * Configure the bayer phase and video timing mux based on the | ||
215 | * operation mode (passthrough or test pattern generation). The test | ||
216 | * pattern can be modified by the control set handler, we thus need to | ||
217 | * take the control lock here to avoid races. | ||
218 | */ | ||
219 | mutex_lock(xtpg->ctrl_handler.lock); | ||
220 | |||
221 | xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, | ||
222 | XTPG_PATTERN_MASK, xtpg->pattern->cur.val); | ||
223 | |||
224 | /* | ||
225 | * Switching between passthrough and test pattern generation modes isn't | ||
226 | * allowed during streaming, update the control range accordingly. | ||
227 | */ | ||
228 | passthrough = xtpg->pattern->cur.val == 0; | ||
229 | __xtpg_update_pattern_control(xtpg, passthrough, !passthrough); | ||
230 | |||
231 | xtpg->streaming = true; | ||
232 | |||
233 | mutex_unlock(xtpg->ctrl_handler.lock); | ||
234 | |||
235 | /* | ||
236 | * For TPG v5.0, the bayer phase needs to be off for the pass through | ||
237 | * mode, otherwise the external input would be subsampled. | ||
238 | */ | ||
239 | bayer_phase = passthrough ? XTPG_BAYER_PHASE_OFF | ||
240 | : xtpg_get_bayer_phase(xtpg->formats[0].code); | ||
241 | xvip_write(&xtpg->xvip, XTPG_BAYER_PHASE, bayer_phase); | ||
242 | |||
243 | if (xtpg->vtmux_gpio) | ||
244 | gpiod_set_value_cansleep(xtpg->vtmux_gpio, !passthrough); | ||
245 | |||
246 | xvip_start(&xtpg->xvip); | ||
247 | |||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | /* ----------------------------------------------------------------------------- | ||
252 | * V4L2 Subdevice Pad Operations | ||
253 | */ | ||
254 | |||
255 | static struct v4l2_mbus_framefmt * | ||
256 | __xtpg_get_pad_format(struct xtpg_device *xtpg, | ||
257 | struct v4l2_subdev_pad_config *cfg, | ||
258 | unsigned int pad, u32 which) | ||
259 | { | ||
260 | switch (which) { | ||
261 | case V4L2_SUBDEV_FORMAT_TRY: | ||
262 | return v4l2_subdev_get_try_format(&xtpg->xvip.subdev, cfg, pad); | ||
263 | case V4L2_SUBDEV_FORMAT_ACTIVE: | ||
264 | return &xtpg->formats[pad]; | ||
265 | default: | ||
266 | return NULL; | ||
267 | } | ||
268 | } | ||
269 | |||
270 | static int xtpg_get_format(struct v4l2_subdev *subdev, | ||
271 | struct v4l2_subdev_pad_config *cfg, | ||
272 | struct v4l2_subdev_format *fmt) | ||
273 | { | ||
274 | struct xtpg_device *xtpg = to_tpg(subdev); | ||
275 | |||
276 | fmt->format = *__xtpg_get_pad_format(xtpg, cfg, fmt->pad, fmt->which); | ||
277 | |||
278 | return 0; | ||
279 | } | ||
280 | |||
281 | static int xtpg_set_format(struct v4l2_subdev *subdev, | ||
282 | struct v4l2_subdev_pad_config *cfg, | ||
283 | struct v4l2_subdev_format *fmt) | ||
284 | { | ||
285 | struct xtpg_device *xtpg = to_tpg(subdev); | ||
286 | struct v4l2_mbus_framefmt *__format; | ||
287 | u32 bayer_phase; | ||
288 | |||
289 | __format = __xtpg_get_pad_format(xtpg, cfg, fmt->pad, fmt->which); | ||
290 | |||
291 | /* In two pads mode the source pad format is always identical to the | ||
292 | * sink pad format. | ||
293 | */ | ||
294 | if (xtpg->npads == 2 && fmt->pad == 1) { | ||
295 | fmt->format = *__format; | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | /* Bayer phase is configurable at runtime */ | ||
300 | if (xtpg->bayer) { | ||
301 | bayer_phase = xtpg_get_bayer_phase(fmt->format.code); | ||
302 | if (bayer_phase != XTPG_BAYER_PHASE_OFF) | ||
303 | __format->code = fmt->format.code; | ||
304 | } | ||
305 | |||
306 | xvip_set_format_size(__format, fmt); | ||
307 | |||
308 | fmt->format = *__format; | ||
309 | |||
310 | /* Propagate the format to the source pad. */ | ||
311 | if (xtpg->npads == 2) { | ||
312 | __format = __xtpg_get_pad_format(xtpg, cfg, 1, 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_pad_config *cfg, | ||
325 | struct v4l2_subdev_frame_size_enum *fse) | ||
326 | { | ||
327 | struct v4l2_mbus_framefmt *format; | ||
328 | |||
329 | format = v4l2_subdev_get_try_format(subdev, cfg, 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_get_try_format(subdev, fh->pad, 0); | ||
358 | *format = xtpg->default_format; | ||
359 | |||
360 | if (xtpg->npads == 2) { | ||
361 | format = v4l2_subdev_get_try_format(subdev, fh->pad, 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(&xtpg->xvip, XTPG_PATTERN_CONTROL, | ||
381 | XTPG_PATTERN_MASK, ctrl->val); | ||
382 | return 0; | ||
383 | case V4L2_CID_XILINX_TPG_CROSS_HAIRS: | ||
384 | xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, | ||
385 | XTPG_PATTERN_CONTROL_CROSS_HAIRS, ctrl->val); | ||
386 | return 0; | ||
387 | case V4L2_CID_XILINX_TPG_MOVING_BOX: | ||
388 | xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, | ||
389 | XTPG_PATTERN_CONTROL_MOVING_BOX, ctrl->val); | ||
390 | return 0; | ||
391 | case V4L2_CID_XILINX_TPG_COLOR_MASK: | ||
392 | xvip_clr_and_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, | ||
393 | XTPG_PATTERN_CONTROL_COLOR_MASK_MASK, | ||
394 | 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(&xtpg->xvip, XTPG_PATTERN_CONTROL, | ||
399 | XTPG_PATTERN_CONTROL_STUCK_PIXEL, ctrl->val); | ||
400 | return 0; | ||
401 | case V4L2_CID_XILINX_TPG_NOISE: | ||
402 | xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, | ||
403 | XTPG_PATTERN_CONTROL_NOISE, ctrl->val); | ||
404 | return 0; | ||
405 | case V4L2_CID_XILINX_TPG_MOTION: | ||
406 | xvip_clr_or_set(&xtpg->xvip, XTPG_PATTERN_CONTROL, | ||
407 | XTPG_PATTERN_CONTROL_MOTION, ctrl->val); | ||
408 | return 0; | ||
409 | case V4L2_CID_XILINX_TPG_MOTION_SPEED: | ||
410 | xvip_write(&xtpg->xvip, XTPG_MOTION_SPEED, ctrl->val); | ||
411 | return 0; | ||
412 | case V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW: | ||
413 | xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS, | ||
414 | XTPG_CROSS_HAIRS_ROW_MASK, | ||
415 | ctrl->val << XTPG_CROSS_HAIRS_ROW_SHIFT); | ||
416 | return 0; | ||
417 | case V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN: | ||
418 | xvip_clr_and_set(&xtpg->xvip, XTPG_CROSS_HAIRS, | ||
419 | XTPG_CROSS_HAIRS_COLUMN_MASK, | ||
420 | ctrl->val << XTPG_CROSS_HAIRS_COLUMN_SHIFT); | ||
421 | return 0; | ||
422 | case V4L2_CID_XILINX_TPG_ZPLATE_HOR_START: | ||
423 | xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL, | ||
424 | XTPG_ZPLATE_START_MASK, | ||
425 | ctrl->val << XTPG_ZPLATE_START_SHIFT); | ||
426 | return 0; | ||
427 | case V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED: | ||
428 | xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_HOR_CONTROL, | ||
429 | XTPG_ZPLATE_SPEED_MASK, | ||
430 | ctrl->val << XTPG_ZPLATE_SPEED_SHIFT); | ||
431 | return 0; | ||
432 | case V4L2_CID_XILINX_TPG_ZPLATE_VER_START: | ||
433 | xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL, | ||
434 | XTPG_ZPLATE_START_MASK, | ||
435 | ctrl->val << XTPG_ZPLATE_START_SHIFT); | ||
436 | return 0; | ||
437 | case V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED: | ||
438 | xvip_clr_and_set(&xtpg->xvip, XTPG_ZPLATE_VER_CONTROL, | ||
439 | XTPG_ZPLATE_SPEED_MASK, | ||
440 | ctrl->val << XTPG_ZPLATE_SPEED_SHIFT); | ||
441 | return 0; | ||
442 | case V4L2_CID_XILINX_TPG_BOX_SIZE: | ||
443 | xvip_write(&xtpg->xvip, XTPG_BOX_SIZE, ctrl->val); | ||
444 | return 0; | ||
445 | case V4L2_CID_XILINX_TPG_BOX_COLOR: | ||
446 | xvip_write(&xtpg->xvip, XTPG_BOX_COLOR, ctrl->val); | ||
447 | return 0; | ||
448 | case V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH: | ||
449 | xvip_write(&xtpg->xvip, XTPG_STUCK_PIXEL_THRESH, ctrl->val); | ||
450 | return 0; | ||
451 | case V4L2_CID_XILINX_TPG_NOISE_GAIN: | ||
452 | xvip_write(&xtpg->xvip, XTPG_NOISE_GAIN, 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 struct v4l2_subdev_core_ops xtpg_core_ops = { | ||
464 | }; | ||
465 | |||
466 | static struct v4l2_subdev_video_ops xtpg_video_ops = { | ||
467 | .s_stream = xtpg_s_stream, | ||
468 | }; | ||
469 | |||
470 | static 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 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(&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(&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 | struct device_node *ports; | ||
716 | struct device_node *port; | ||
717 | unsigned int nports = 0; | ||
718 | bool has_endpoint = false; | ||
719 | |||
720 | ports = of_get_child_by_name(node, "ports"); | ||
721 | if (ports == NULL) | ||
722 | ports = node; | ||
723 | |||
724 | for_each_child_of_node(ports, port) { | ||
725 | const struct xvip_video_format *format; | ||
726 | struct device_node *endpoint; | ||
727 | |||
728 | if (!port->name || of_node_cmp(port->name, "port")) | ||
729 | continue; | ||
730 | |||
731 | format = xvip_of_get_format(port); | ||
732 | if (IS_ERR(format)) { | ||
733 | dev_err(dev, "invalid format in DT"); | ||
734 | return PTR_ERR(format); | ||
735 | } | ||
736 | |||
737 | /* Get and check the format description */ | ||
738 | if (!xtpg->vip_format) { | ||
739 | xtpg->vip_format = format; | ||
740 | } else if (xtpg->vip_format != format) { | ||
741 | dev_err(dev, "in/out format mismatch in DT"); | ||
742 | return -EINVAL; | ||
743 | } | ||
744 | |||
745 | if (nports == 0) { | ||
746 | endpoint = of_get_next_child(port, NULL); | ||
747 | if (endpoint) | ||
748 | has_endpoint = true; | ||
749 | of_node_put(endpoint); | ||
750 | } | ||
751 | |||
752 | /* Count the number of ports. */ | ||
753 | nports++; | ||
754 | } | ||
755 | |||
756 | if (nports != 1 && nports != 2) { | ||
757 | dev_err(dev, "invalid number of ports %u\n", nports); | ||
758 | return -EINVAL; | ||
759 | } | ||
760 | |||
761 | xtpg->npads = nports; | ||
762 | if (nports == 2 && has_endpoint) | ||
763 | xtpg->has_input = true; | ||
764 | |||
765 | return 0; | ||
766 | } | ||
767 | |||
768 | static int xtpg_probe(struct platform_device *pdev) | ||
769 | { | ||
770 | struct v4l2_subdev *subdev; | ||
771 | struct xtpg_device *xtpg; | ||
772 | u32 i, bayer_phase; | ||
773 | int ret; | ||
774 | |||
775 | xtpg = devm_kzalloc(&pdev->dev, sizeof(*xtpg), GFP_KERNEL); | ||
776 | if (!xtpg) | ||
777 | return -ENOMEM; | ||
778 | |||
779 | xtpg->xvip.dev = &pdev->dev; | ||
780 | |||
781 | ret = xtpg_parse_of(xtpg); | ||
782 | if (ret < 0) | ||
783 | return ret; | ||
784 | |||
785 | ret = xvip_init_resources(&xtpg->xvip); | ||
786 | if (ret < 0) | ||
787 | return ret; | ||
788 | |||
789 | xtpg->vtmux_gpio = devm_gpiod_get_optional(&pdev->dev, "timing", | ||
790 | GPIOD_OUT_HIGH); | ||
791 | if (IS_ERR(xtpg->vtmux_gpio)) { | ||
792 | ret = PTR_ERR(xtpg->vtmux_gpio); | ||
793 | goto error_resource; | ||
794 | } | ||
795 | |||
796 | xtpg->vtc = xvtc_of_get(pdev->dev.of_node); | ||
797 | if (IS_ERR(xtpg->vtc)) { | ||
798 | ret = PTR_ERR(xtpg->vtc); | ||
799 | goto error_resource; | ||
800 | } | ||
801 | |||
802 | /* Reset and initialize the core */ | ||
803 | xvip_reset(&xtpg->xvip); | ||
804 | |||
805 | /* Initialize V4L2 subdevice and media entity. Pad numbers depend on the | ||
806 | * number of pads. | ||
807 | */ | ||
808 | if (xtpg->npads == 2) { | ||
809 | xtpg->pads[0].flags = MEDIA_PAD_FL_SINK; | ||
810 | xtpg->pads[1].flags = MEDIA_PAD_FL_SOURCE; | ||
811 | } else { | ||
812 | xtpg->pads[0].flags = MEDIA_PAD_FL_SOURCE; | ||
813 | } | ||
814 | |||
815 | /* Initialize the default format */ | ||
816 | xtpg->default_format.code = xtpg->vip_format->code; | ||
817 | xtpg->default_format.field = V4L2_FIELD_NONE; | ||
818 | xtpg->default_format.colorspace = V4L2_COLORSPACE_SRGB; | ||
819 | xvip_get_frame_size(&xtpg->xvip, &xtpg->default_format); | ||
820 | |||
821 | bayer_phase = xtpg_get_bayer_phase(xtpg->vip_format->code); | ||
822 | if (bayer_phase != XTPG_BAYER_PHASE_OFF) | ||
823 | xtpg->bayer = true; | ||
824 | |||
825 | xtpg->formats[0] = xtpg->default_format; | ||
826 | if (xtpg->npads == 2) | ||
827 | xtpg->formats[1] = xtpg->default_format; | ||
828 | |||
829 | /* Initialize V4L2 subdevice and media entity */ | ||
830 | subdev = &xtpg->xvip.subdev; | ||
831 | v4l2_subdev_init(subdev, &xtpg_ops); | ||
832 | subdev->dev = &pdev->dev; | ||
833 | subdev->internal_ops = &xtpg_internal_ops; | ||
834 | strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); | ||
835 | v4l2_set_subdevdata(subdev, xtpg); | ||
836 | subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
837 | subdev->entity.ops = &xtpg_media_ops; | ||
838 | |||
839 | ret = media_entity_init(&subdev->entity, xtpg->npads, xtpg->pads, 0); | ||
840 | if (ret < 0) | ||
841 | goto error; | ||
842 | |||
843 | v4l2_ctrl_handler_init(&xtpg->ctrl_handler, 3 + ARRAY_SIZE(xtpg_ctrls)); | ||
844 | |||
845 | xtpg->vblank = v4l2_ctrl_new_std(&xtpg->ctrl_handler, &xtpg_ctrl_ops, | ||
846 | V4L2_CID_VBLANK, XTPG_MIN_VBLANK, | ||
847 | XTPG_MAX_VBLANK, 1, 100); | ||
848 | xtpg->hblank = v4l2_ctrl_new_std(&xtpg->ctrl_handler, &xtpg_ctrl_ops, | ||
849 | V4L2_CID_HBLANK, XTPG_MIN_HBLANK, | ||
850 | XTPG_MAX_HBLANK, 1, 100); | ||
851 | xtpg->pattern = v4l2_ctrl_new_std_menu_items(&xtpg->ctrl_handler, | ||
852 | &xtpg_ctrl_ops, V4L2_CID_TEST_PATTERN, | ||
853 | ARRAY_SIZE(xtpg_pattern_strings) - 1, | ||
854 | 1, 9, xtpg_pattern_strings); | ||
855 | |||
856 | for (i = 0; i < ARRAY_SIZE(xtpg_ctrls); i++) | ||
857 | v4l2_ctrl_new_custom(&xtpg->ctrl_handler, &xtpg_ctrls[i], NULL); | ||
858 | |||
859 | if (xtpg->ctrl_handler.error) { | ||
860 | dev_err(&pdev->dev, "failed to add controls\n"); | ||
861 | ret = xtpg->ctrl_handler.error; | ||
862 | goto error; | ||
863 | } | ||
864 | subdev->ctrl_handler = &xtpg->ctrl_handler; | ||
865 | |||
866 | xtpg_update_pattern_control(xtpg, true, true); | ||
867 | |||
868 | ret = v4l2_ctrl_handler_setup(&xtpg->ctrl_handler); | ||
869 | if (ret < 0) { | ||
870 | dev_err(&pdev->dev, "failed to set controls\n"); | ||
871 | goto error; | ||
872 | } | ||
873 | |||
874 | platform_set_drvdata(pdev, xtpg); | ||
875 | |||
876 | xvip_print_version(&xtpg->xvip); | ||
877 | |||
878 | ret = v4l2_async_register_subdev(subdev); | ||
879 | if (ret < 0) { | ||
880 | dev_err(&pdev->dev, "failed to register subdev\n"); | ||
881 | goto error; | ||
882 | } | ||
883 | |||
884 | return 0; | ||
885 | |||
886 | error: | ||
887 | v4l2_ctrl_handler_free(&xtpg->ctrl_handler); | ||
888 | media_entity_cleanup(&subdev->entity); | ||
889 | xvtc_put(xtpg->vtc); | ||
890 | error_resource: | ||
891 | xvip_cleanup_resources(&xtpg->xvip); | ||
892 | return ret; | ||
893 | } | ||
894 | |||
895 | static int xtpg_remove(struct platform_device *pdev) | ||
896 | { | ||
897 | struct xtpg_device *xtpg = platform_get_drvdata(pdev); | ||
898 | struct v4l2_subdev *subdev = &xtpg->xvip.subdev; | ||
899 | |||
900 | v4l2_async_unregister_subdev(subdev); | ||
901 | v4l2_ctrl_handler_free(&xtpg->ctrl_handler); | ||
902 | media_entity_cleanup(&subdev->entity); | ||
903 | |||
904 | xvip_cleanup_resources(&xtpg->xvip); | ||
905 | |||
906 | return 0; | ||
907 | } | ||
908 | |||
909 | static SIMPLE_DEV_PM_OPS(xtpg_pm_ops, xtpg_pm_suspend, xtpg_pm_resume); | ||
910 | |||
911 | static const struct of_device_id xtpg_of_id_table[] = { | ||
912 | { .compatible = "xlnx,v-tpg-5.0" }, | ||
913 | { } | ||
914 | }; | ||
915 | MODULE_DEVICE_TABLE(of, xtpg_of_id_table); | ||
916 | |||
917 | static struct platform_driver xtpg_driver = { | ||
918 | .driver = { | ||
919 | .name = "xilinx-tpg", | ||
920 | .pm = &xtpg_pm_ops, | ||
921 | .of_match_table = xtpg_of_id_table, | ||
922 | }, | ||
923 | .probe = xtpg_probe, | ||
924 | .remove = xtpg_remove, | ||
925 | }; | ||
926 | |||
927 | module_platform_driver(xtpg_driver); | ||
928 | |||
929 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); | ||
930 | MODULE_DESCRIPTION("Xilinx Test Pattern Generator Driver"); | ||
931 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 68ceb97c458c..d4cfc17cc414 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild | |||
@@ -446,5 +446,6 @@ header-y += wireless.h | |||
446 | header-y += x25.h | 446 | header-y += x25.h |
447 | header-y += xattr.h | 447 | header-y += xattr.h |
448 | header-y += xfrm.h | 448 | header-y += xfrm.h |
449 | header-y += xilinx-v4l2-controls.h | ||
449 | header-y += zorro.h | 450 | header-y += zorro.h |
450 | header-y += zorro_ids.h | 451 | header-y += zorro_ids.h |
diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h new file mode 100644 index 000000000000..fb495b91e800 --- /dev/null +++ b/include/uapi/linux/xilinx-v4l2-controls.h | |||
@@ -0,0 +1,73 @@ | |||
1 | /* | ||
2 | * Xilinx Controls Header | ||
3 | * | ||
4 | * Copyright (C) 2013-2015 Ideas on Board | ||
5 | * Copyright (C) 2013-2015 Xilinx, Inc. | ||
6 | * | ||
7 | * Contacts: Hyun Kwon <hyun.kwon@xilinx.com> | ||
8 | * Laurent Pinchart <laurent.pinchart@ideasonboard.com> | ||
9 | * | ||
10 | * This software is licensed under the terms of the GNU General Public | ||
11 | * License version 2, as published by the Free Software Foundation, and | ||
12 | * may be copied, distributed, and modified under those terms. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | */ | ||
19 | |||
20 | #ifndef __UAPI_XILINX_V4L2_CONTROLS_H__ | ||
21 | #define __UAPI_XILINX_V4L2_CONTROLS_H__ | ||
22 | |||
23 | #include <linux/v4l2-controls.h> | ||
24 | |||
25 | #define V4L2_CID_XILINX_OFFSET 0xc000 | ||
26 | #define V4L2_CID_XILINX_BASE (V4L2_CID_USER_BASE + V4L2_CID_XILINX_OFFSET) | ||
27 | |||
28 | /* | ||
29 | * Private Controls for Xilinx Video IPs | ||
30 | */ | ||
31 | |||
32 | /* | ||
33 | * Xilinx TPG Video IP | ||
34 | */ | ||
35 | |||
36 | #define V4L2_CID_XILINX_TPG (V4L2_CID_USER_BASE + 0xc000) | ||
37 | |||
38 | /* Draw cross hairs */ | ||
39 | #define V4L2_CID_XILINX_TPG_CROSS_HAIRS (V4L2_CID_XILINX_TPG + 1) | ||
40 | /* Enable a moving box */ | ||
41 | #define V4L2_CID_XILINX_TPG_MOVING_BOX (V4L2_CID_XILINX_TPG + 2) | ||
42 | /* Mask out a color component */ | ||
43 | #define V4L2_CID_XILINX_TPG_COLOR_MASK (V4L2_CID_XILINX_TPG + 3) | ||
44 | /* Enable a stuck pixel feature */ | ||
45 | #define V4L2_CID_XILINX_TPG_STUCK_PIXEL (V4L2_CID_XILINX_TPG + 4) | ||
46 | /* Enable a noisy output */ | ||
47 | #define V4L2_CID_XILINX_TPG_NOISE (V4L2_CID_XILINX_TPG + 5) | ||
48 | /* Enable the motion feature */ | ||
49 | #define V4L2_CID_XILINX_TPG_MOTION (V4L2_CID_XILINX_TPG + 6) | ||
50 | /* Configure the motion speed of moving patterns */ | ||
51 | #define V4L2_CID_XILINX_TPG_MOTION_SPEED (V4L2_CID_XILINX_TPG + 7) | ||
52 | /* The row of horizontal cross hair location */ | ||
53 | #define V4L2_CID_XILINX_TPG_CROSS_HAIR_ROW (V4L2_CID_XILINX_TPG + 8) | ||
54 | /* The colum of vertical cross hair location */ | ||
55 | #define V4L2_CID_XILINX_TPG_CROSS_HAIR_COLUMN (V4L2_CID_XILINX_TPG + 9) | ||
56 | /* Set starting point of sine wave for horizontal component */ | ||
57 | #define V4L2_CID_XILINX_TPG_ZPLATE_HOR_START (V4L2_CID_XILINX_TPG + 10) | ||
58 | /* Set speed of the horizontal component */ | ||
59 | #define V4L2_CID_XILINX_TPG_ZPLATE_HOR_SPEED (V4L2_CID_XILINX_TPG + 11) | ||
60 | /* Set starting point of sine wave for vertical component */ | ||
61 | #define V4L2_CID_XILINX_TPG_ZPLATE_VER_START (V4L2_CID_XILINX_TPG + 12) | ||
62 | /* Set speed of the vertical component */ | ||
63 | #define V4L2_CID_XILINX_TPG_ZPLATE_VER_SPEED (V4L2_CID_XILINX_TPG + 13) | ||
64 | /* Moving box size */ | ||
65 | #define V4L2_CID_XILINX_TPG_BOX_SIZE (V4L2_CID_XILINX_TPG + 14) | ||
66 | /* Moving box color */ | ||
67 | #define V4L2_CID_XILINX_TPG_BOX_COLOR (V4L2_CID_XILINX_TPG + 15) | ||
68 | /* Upper limit count of generated stuck pixels */ | ||
69 | #define V4L2_CID_XILINX_TPG_STUCK_PIXEL_THRESH (V4L2_CID_XILINX_TPG + 16) | ||
70 | /* Noise level */ | ||
71 | #define V4L2_CID_XILINX_TPG_NOISE_GAIN (V4L2_CID_XILINX_TPG + 17) | ||
72 | |||
73 | #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */ | ||