]>
Commit | Line | Data |
---|---|---|
c942fddf | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
5ba0ae43 HF |
2 | /* |
3 | * vimc-common.c Virtual Media Controller Driver | |
4 | * | |
5 | * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> | |
5ba0ae43 HF |
6 | */ |
7 | ||
4a29b709 HF |
8 | #include <linux/init.h> |
9 | #include <linux/module.h> | |
10 | ||
5ba0ae43 HF |
11 | #include "vimc-common.h" |
12 | ||
b6c61a6c HF |
13 | static const __u32 vimc_mbus_list[] = { |
14 | MEDIA_BUS_FMT_FIXED, | |
15 | MEDIA_BUS_FMT_RGB444_1X12, | |
16 | MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE, | |
17 | MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE, | |
18 | MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, | |
19 | MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, | |
20 | MEDIA_BUS_FMT_RGB565_1X16, | |
21 | MEDIA_BUS_FMT_BGR565_2X8_BE, | |
22 | MEDIA_BUS_FMT_BGR565_2X8_LE, | |
23 | MEDIA_BUS_FMT_RGB565_2X8_BE, | |
24 | MEDIA_BUS_FMT_RGB565_2X8_LE, | |
25 | MEDIA_BUS_FMT_RGB666_1X18, | |
26 | MEDIA_BUS_FMT_RBG888_1X24, | |
27 | MEDIA_BUS_FMT_RGB666_1X24_CPADHI, | |
28 | MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, | |
29 | MEDIA_BUS_FMT_BGR888_1X24, | |
30 | MEDIA_BUS_FMT_GBR888_1X24, | |
31 | MEDIA_BUS_FMT_RGB888_1X24, | |
32 | MEDIA_BUS_FMT_RGB888_2X12_BE, | |
33 | MEDIA_BUS_FMT_RGB888_2X12_LE, | |
34 | MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, | |
35 | MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, | |
36 | MEDIA_BUS_FMT_ARGB8888_1X32, | |
37 | MEDIA_BUS_FMT_RGB888_1X32_PADHI, | |
38 | MEDIA_BUS_FMT_RGB101010_1X30, | |
39 | MEDIA_BUS_FMT_RGB121212_1X36, | |
40 | MEDIA_BUS_FMT_RGB161616_1X48, | |
41 | MEDIA_BUS_FMT_Y8_1X8, | |
42 | MEDIA_BUS_FMT_UV8_1X8, | |
43 | MEDIA_BUS_FMT_UYVY8_1_5X8, | |
44 | MEDIA_BUS_FMT_VYUY8_1_5X8, | |
45 | MEDIA_BUS_FMT_YUYV8_1_5X8, | |
46 | MEDIA_BUS_FMT_YVYU8_1_5X8, | |
47 | MEDIA_BUS_FMT_UYVY8_2X8, | |
48 | MEDIA_BUS_FMT_VYUY8_2X8, | |
49 | MEDIA_BUS_FMT_YUYV8_2X8, | |
50 | MEDIA_BUS_FMT_YVYU8_2X8, | |
51 | MEDIA_BUS_FMT_Y10_1X10, | |
52 | MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, | |
53 | MEDIA_BUS_FMT_UYVY10_2X10, | |
54 | MEDIA_BUS_FMT_VYUY10_2X10, | |
55 | MEDIA_BUS_FMT_YUYV10_2X10, | |
56 | MEDIA_BUS_FMT_YVYU10_2X10, | |
57 | MEDIA_BUS_FMT_Y12_1X12, | |
58 | MEDIA_BUS_FMT_UYVY12_2X12, | |
59 | MEDIA_BUS_FMT_VYUY12_2X12, | |
60 | MEDIA_BUS_FMT_YUYV12_2X12, | |
61 | MEDIA_BUS_FMT_YVYU12_2X12, | |
62 | MEDIA_BUS_FMT_UYVY8_1X16, | |
63 | MEDIA_BUS_FMT_VYUY8_1X16, | |
64 | MEDIA_BUS_FMT_YUYV8_1X16, | |
65 | MEDIA_BUS_FMT_YVYU8_1X16, | |
66 | MEDIA_BUS_FMT_YDYUYDYV8_1X16, | |
67 | MEDIA_BUS_FMT_UYVY10_1X20, | |
68 | MEDIA_BUS_FMT_VYUY10_1X20, | |
69 | MEDIA_BUS_FMT_YUYV10_1X20, | |
70 | MEDIA_BUS_FMT_YVYU10_1X20, | |
71 | MEDIA_BUS_FMT_VUY8_1X24, | |
72 | MEDIA_BUS_FMT_YUV8_1X24, | |
73 | MEDIA_BUS_FMT_UYYVYY8_0_5X24, | |
74 | MEDIA_BUS_FMT_UYVY12_1X24, | |
75 | MEDIA_BUS_FMT_VYUY12_1X24, | |
76 | MEDIA_BUS_FMT_YUYV12_1X24, | |
77 | MEDIA_BUS_FMT_YVYU12_1X24, | |
78 | MEDIA_BUS_FMT_YUV10_1X30, | |
79 | MEDIA_BUS_FMT_UYYVYY10_0_5X30, | |
80 | MEDIA_BUS_FMT_AYUV8_1X32, | |
81 | MEDIA_BUS_FMT_UYYVYY12_0_5X36, | |
82 | MEDIA_BUS_FMT_YUV12_1X36, | |
83 | MEDIA_BUS_FMT_YUV16_1X48, | |
84 | MEDIA_BUS_FMT_UYYVYY16_0_5X48, | |
85 | MEDIA_BUS_FMT_SBGGR8_1X8, | |
86 | MEDIA_BUS_FMT_SGBRG8_1X8, | |
87 | MEDIA_BUS_FMT_SGRBG8_1X8, | |
88 | MEDIA_BUS_FMT_SRGGB8_1X8, | |
89 | MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8, | |
90 | MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8, | |
91 | MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8, | |
92 | MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8, | |
93 | MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, | |
94 | MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, | |
95 | MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, | |
96 | MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, | |
97 | MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE, | |
98 | MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, | |
99 | MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_BE, | |
100 | MEDIA_BUS_FMT_SBGGR10_2X8_PADLO_LE, | |
101 | MEDIA_BUS_FMT_SBGGR10_1X10, | |
102 | MEDIA_BUS_FMT_SGBRG10_1X10, | |
103 | MEDIA_BUS_FMT_SGRBG10_1X10, | |
104 | MEDIA_BUS_FMT_SRGGB10_1X10, | |
105 | MEDIA_BUS_FMT_SBGGR12_1X12, | |
106 | MEDIA_BUS_FMT_SGBRG12_1X12, | |
107 | MEDIA_BUS_FMT_SGRBG12_1X12, | |
108 | MEDIA_BUS_FMT_SRGGB12_1X12, | |
109 | MEDIA_BUS_FMT_SBGGR14_1X14, | |
110 | MEDIA_BUS_FMT_SGBRG14_1X14, | |
111 | MEDIA_BUS_FMT_SGRBG14_1X14, | |
112 | MEDIA_BUS_FMT_SRGGB14_1X14, | |
113 | MEDIA_BUS_FMT_SBGGR16_1X16, | |
114 | MEDIA_BUS_FMT_SGBRG16_1X16, | |
115 | MEDIA_BUS_FMT_SGRBG16_1X16, | |
116 | MEDIA_BUS_FMT_SRGGB16_1X16, | |
117 | MEDIA_BUS_FMT_JPEG_1X8, | |
118 | MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8, | |
119 | MEDIA_BUS_FMT_AHSV8888_1X32, | |
5ba0ae43 HF |
120 | }; |
121 | ||
b6c61a6c HF |
122 | /* Helper function to check mbus codes */ |
123 | bool vimc_mbus_code_supported(__u32 code) | |
5ba0ae43 HF |
124 | { |
125 | unsigned int i; | |
126 | ||
b6c61a6c HF |
127 | for (i = 0; i < ARRAY_SIZE(vimc_mbus_list); i++) |
128 | if (code == vimc_mbus_list[i]) | |
129 | return true; | |
130 | return false; | |
5ba0ae43 | 131 | } |
b6c61a6c | 132 | EXPORT_SYMBOL_GPL(vimc_mbus_code_supported); |
5ba0ae43 | 133 | |
b6c61a6c HF |
134 | /* Helper function to enumerate mbus codes */ |
135 | int vimc_enum_mbus_code(struct v4l2_subdev *sd, | |
136 | struct v4l2_subdev_pad_config *cfg, | |
137 | struct v4l2_subdev_mbus_code_enum *code) | |
5ba0ae43 | 138 | { |
b6c61a6c HF |
139 | if (code->index >= ARRAY_SIZE(vimc_mbus_list)) |
140 | return -EINVAL; | |
5ba0ae43 | 141 | |
b6c61a6c HF |
142 | code->code = vimc_mbus_list[code->index]; |
143 | return 0; | |
5ba0ae43 | 144 | } |
b6c61a6c | 145 | EXPORT_SYMBOL_GPL(vimc_enum_mbus_code); |
5ba0ae43 | 146 | |
5ba0ae43 HF |
147 | /* Helper function to allocate and initialize pads */ |
148 | struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag) | |
149 | { | |
150 | struct media_pad *pads; | |
151 | unsigned int i; | |
152 | ||
153 | /* Allocate memory for the pads */ | |
154 | pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL); | |
155 | if (!pads) | |
156 | return ERR_PTR(-ENOMEM); | |
157 | ||
158 | /* Initialize the pads */ | |
159 | for (i = 0; i < num_pads; i++) { | |
160 | pads[i].index = i; | |
161 | pads[i].flags = pads_flag[i]; | |
162 | } | |
163 | ||
164 | return pads; | |
165 | } | |
4a29b709 | 166 | EXPORT_SYMBOL_GPL(vimc_pads_init); |
c149543e | 167 | |
bf5fb95c HF |
168 | int vimc_pipeline_s_stream(struct media_entity *ent, int enable) |
169 | { | |
170 | struct v4l2_subdev *sd; | |
171 | struct media_pad *pad; | |
172 | unsigned int i; | |
173 | int ret; | |
174 | ||
175 | for (i = 0; i < ent->num_pads; i++) { | |
176 | if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE) | |
177 | continue; | |
178 | ||
179 | /* Start the stream in the subdevice direct connected */ | |
180 | pad = media_entity_remote_pad(&ent->pads[i]); | |
e159b607 HF |
181 | if (!pad) |
182 | continue; | |
bf5fb95c | 183 | |
4a29b709 HF |
184 | if (!is_media_entity_v4l2_subdev(pad->entity)) |
185 | return -EINVAL; | |
bf5fb95c HF |
186 | |
187 | sd = media_entity_to_v4l2_subdev(pad->entity); | |
188 | ret = v4l2_subdev_call(sd, video, s_stream, enable); | |
189 | if (ret && ret != -ENOIOCTLCMD) | |
190 | return ret; | |
191 | } | |
192 | ||
193 | return 0; | |
194 | } | |
4a29b709 | 195 | EXPORT_SYMBOL_GPL(vimc_pipeline_s_stream); |
bf5fb95c | 196 | |
288a22d4 HF |
197 | static int vimc_get_mbus_format(struct media_pad *pad, |
198 | struct v4l2_subdev_format *fmt) | |
199 | { | |
200 | if (is_media_entity_v4l2_subdev(pad->entity)) { | |
201 | struct v4l2_subdev *sd = | |
202 | media_entity_to_v4l2_subdev(pad->entity); | |
203 | int ret; | |
204 | ||
205 | fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; | |
206 | fmt->pad = pad->index; | |
207 | ||
208 | ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); | |
209 | if (ret) | |
210 | return ret; | |
211 | ||
212 | } else if (is_media_entity_v4l2_video_device(pad->entity)) { | |
213 | struct video_device *vdev = container_of(pad->entity, | |
214 | struct video_device, | |
215 | entity); | |
216 | struct vimc_ent_device *ved = video_get_drvdata(vdev); | |
288a22d4 HF |
217 | struct v4l2_pix_format vdev_fmt; |
218 | ||
219 | if (!ved->vdev_get_format) | |
220 | return -ENOIOCTLCMD; | |
221 | ||
222 | ved->vdev_get_format(ved, &vdev_fmt); | |
b6c61a6c | 223 | v4l2_fill_mbus_format(&fmt->format, &vdev_fmt, 0); |
288a22d4 HF |
224 | } else { |
225 | return -EINVAL; | |
226 | } | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
231 | int vimc_link_validate(struct media_link *link) | |
232 | { | |
233 | struct v4l2_subdev_format source_fmt, sink_fmt; | |
234 | int ret; | |
235 | ||
288a22d4 HF |
236 | ret = vimc_get_mbus_format(link->source, &source_fmt); |
237 | if (ret) | |
238 | return ret; | |
239 | ||
240 | ret = vimc_get_mbus_format(link->sink, &sink_fmt); | |
241 | if (ret) | |
242 | return ret; | |
243 | ||
244 | pr_info("vimc link validate: " | |
245 | "%s:src:%dx%d (0x%x, %d, %d, %d, %d) " | |
246 | "%s:snk:%dx%d (0x%x, %d, %d, %d, %d)\n", | |
247 | /* src */ | |
248 | link->source->entity->name, | |
249 | source_fmt.format.width, source_fmt.format.height, | |
250 | source_fmt.format.code, source_fmt.format.colorspace, | |
251 | source_fmt.format.quantization, source_fmt.format.xfer_func, | |
252 | source_fmt.format.ycbcr_enc, | |
253 | /* sink */ | |
254 | link->sink->entity->name, | |
255 | sink_fmt.format.width, sink_fmt.format.height, | |
256 | sink_fmt.format.code, sink_fmt.format.colorspace, | |
257 | sink_fmt.format.quantization, sink_fmt.format.xfer_func, | |
258 | sink_fmt.format.ycbcr_enc); | |
259 | ||
260 | /* The width, height and code must match. */ | |
261 | if (source_fmt.format.width != sink_fmt.format.width | |
262 | || source_fmt.format.height != sink_fmt.format.height | |
b6c61a6c HF |
263 | || (source_fmt.format.code && sink_fmt.format.code && |
264 | source_fmt.format.code != sink_fmt.format.code)) { | |
265 | pr_err("vimc: format doesn't match in link %s->%s\n", | |
266 | link->source->entity->name, link->sink->entity->name); | |
288a22d4 | 267 | return -EPIPE; |
b6c61a6c | 268 | } |
288a22d4 HF |
269 | |
270 | /* | |
271 | * The field order must match, or the sink field order must be NONE | |
272 | * to support interlaced hardware connected to bridges that support | |
273 | * progressive formats only. | |
274 | */ | |
275 | if (source_fmt.format.field != sink_fmt.format.field && | |
276 | sink_fmt.format.field != V4L2_FIELD_NONE) | |
277 | return -EPIPE; | |
278 | ||
279 | /* | |
280 | * If colorspace is DEFAULT, then assume all the colorimetry is also | |
281 | * DEFAULT, return 0 to skip comparing the other colorimetry parameters | |
282 | */ | |
283 | if (source_fmt.format.colorspace == V4L2_COLORSPACE_DEFAULT | |
284 | || sink_fmt.format.colorspace == V4L2_COLORSPACE_DEFAULT) | |
285 | return 0; | |
286 | ||
287 | /* Colorspace must match. */ | |
288 | if (source_fmt.format.colorspace != sink_fmt.format.colorspace) | |
289 | return -EPIPE; | |
290 | ||
291 | /* Colorimetry must match if they are not set to DEFAULT */ | |
292 | if (source_fmt.format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT | |
293 | && sink_fmt.format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT | |
294 | && source_fmt.format.ycbcr_enc != sink_fmt.format.ycbcr_enc) | |
295 | return -EPIPE; | |
296 | ||
297 | if (source_fmt.format.quantization != V4L2_QUANTIZATION_DEFAULT | |
298 | && sink_fmt.format.quantization != V4L2_QUANTIZATION_DEFAULT | |
299 | && source_fmt.format.quantization != sink_fmt.format.quantization) | |
300 | return -EPIPE; | |
301 | ||
302 | if (source_fmt.format.xfer_func != V4L2_XFER_FUNC_DEFAULT | |
303 | && sink_fmt.format.xfer_func != V4L2_XFER_FUNC_DEFAULT | |
304 | && source_fmt.format.xfer_func != sink_fmt.format.xfer_func) | |
305 | return -EPIPE; | |
306 | ||
307 | return 0; | |
308 | } | |
4a29b709 | 309 | EXPORT_SYMBOL_GPL(vimc_link_validate); |
288a22d4 | 310 | |
c149543e | 311 | static const struct media_entity_operations vimc_ent_sd_mops = { |
288a22d4 | 312 | .link_validate = vimc_link_validate, |
c149543e HF |
313 | }; |
314 | ||
315 | int vimc_ent_sd_register(struct vimc_ent_device *ved, | |
316 | struct v4l2_subdev *sd, | |
317 | struct v4l2_device *v4l2_dev, | |
318 | const char *const name, | |
319 | u32 function, | |
320 | u16 num_pads, | |
321 | const unsigned long *pads_flag, | |
2b177f28 | 322 | const struct v4l2_subdev_internal_ops *sd_int_ops, |
4a29b709 | 323 | const struct v4l2_subdev_ops *sd_ops) |
c149543e HF |
324 | { |
325 | int ret; | |
326 | ||
327 | /* Allocate the pads */ | |
328 | ved->pads = vimc_pads_init(num_pads, pads_flag); | |
329 | if (IS_ERR(ved->pads)) | |
330 | return PTR_ERR(ved->pads); | |
331 | ||
332 | /* Fill the vimc_ent_device struct */ | |
c149543e HF |
333 | ved->ent = &sd->entity; |
334 | ||
335 | /* Initialize the subdev */ | |
336 | v4l2_subdev_init(sd, sd_ops); | |
2b177f28 | 337 | sd->internal_ops = sd_int_ops; |
c149543e HF |
338 | sd->entity.function = function; |
339 | sd->entity.ops = &vimc_ent_sd_mops; | |
340 | sd->owner = THIS_MODULE; | |
c0decac1 | 341 | strscpy(sd->name, name, sizeof(sd->name)); |
c149543e HF |
342 | v4l2_set_subdevdata(sd, ved); |
343 | ||
344 | /* Expose this subdev to user space */ | |
3da7ee94 HV |
345 | sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; |
346 | if (sd->ctrl_handler) | |
347 | sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS; | |
c149543e HF |
348 | |
349 | /* Initialize the media entity */ | |
350 | ret = media_entity_pads_init(&sd->entity, num_pads, ved->pads); | |
351 | if (ret) | |
352 | goto err_clean_pads; | |
353 | ||
354 | /* Register the subdev with the v4l2 and the media framework */ | |
355 | ret = v4l2_device_register_subdev(v4l2_dev, sd); | |
356 | if (ret) { | |
357 | dev_err(v4l2_dev->dev, | |
358 | "%s: subdev register failed (err=%d)\n", | |
359 | name, ret); | |
360 | goto err_clean_m_ent; | |
361 | } | |
362 | ||
363 | return 0; | |
364 | ||
365 | err_clean_m_ent: | |
366 | media_entity_cleanup(&sd->entity); | |
367 | err_clean_pads: | |
368 | vimc_pads_cleanup(ved->pads); | |
369 | return ret; | |
370 | } | |
4a29b709 | 371 | EXPORT_SYMBOL_GPL(vimc_ent_sd_register); |
c149543e HF |
372 | |
373 | void vimc_ent_sd_unregister(struct vimc_ent_device *ved, struct v4l2_subdev *sd) | |
374 | { | |
c149543e HF |
375 | media_entity_cleanup(ved->ent); |
376 | vimc_pads_cleanup(ved->pads); | |
2b177f28 | 377 | v4l2_device_unregister_subdev(sd); |
c149543e | 378 | } |
4a29b709 HF |
379 | EXPORT_SYMBOL_GPL(vimc_ent_sd_unregister); |
380 | ||
381 | MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Common"); | |
382 | MODULE_AUTHOR("Helen Koike <helen.fornazier@gmail.com>"); | |
383 | MODULE_LICENSE("GPL"); |