blob: 6ad77fdc04ad8d5e8bf24dcc52b2f683c05c8d62 [file] [log] [blame]
Helen Fornazier5ba0ae42017-06-19 14:00:11 -03001/*
2 * vimc-common.c Virtual Media Controller Driver
3 *
4 * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18#include "vimc-common.h"
19
20static const struct vimc_pix_map vimc_pix_map_list[] = {
21 /* TODO: add all missing formats */
22
23 /* RGB formats */
24 {
25 .code = MEDIA_BUS_FMT_BGR888_1X24,
26 .pixelformat = V4L2_PIX_FMT_BGR24,
27 .bpp = 3,
28 },
29 {
30 .code = MEDIA_BUS_FMT_RGB888_1X24,
31 .pixelformat = V4L2_PIX_FMT_RGB24,
32 .bpp = 3,
33 },
34 {
35 .code = MEDIA_BUS_FMT_ARGB8888_1X32,
36 .pixelformat = V4L2_PIX_FMT_ARGB32,
37 .bpp = 4,
38 },
39
40 /* Bayer formats */
41 {
42 .code = MEDIA_BUS_FMT_SBGGR8_1X8,
43 .pixelformat = V4L2_PIX_FMT_SBGGR8,
44 .bpp = 1,
45 },
46 {
47 .code = MEDIA_BUS_FMT_SGBRG8_1X8,
48 .pixelformat = V4L2_PIX_FMT_SGBRG8,
49 .bpp = 1,
50 },
51 {
52 .code = MEDIA_BUS_FMT_SGRBG8_1X8,
53 .pixelformat = V4L2_PIX_FMT_SGRBG8,
54 .bpp = 1,
55 },
56 {
57 .code = MEDIA_BUS_FMT_SRGGB8_1X8,
58 .pixelformat = V4L2_PIX_FMT_SRGGB8,
59 .bpp = 1,
60 },
61 {
62 .code = MEDIA_BUS_FMT_SBGGR10_1X10,
63 .pixelformat = V4L2_PIX_FMT_SBGGR10,
64 .bpp = 2,
65 },
66 {
67 .code = MEDIA_BUS_FMT_SGBRG10_1X10,
68 .pixelformat = V4L2_PIX_FMT_SGBRG10,
69 .bpp = 2,
70 },
71 {
72 .code = MEDIA_BUS_FMT_SGRBG10_1X10,
73 .pixelformat = V4L2_PIX_FMT_SGRBG10,
74 .bpp = 2,
75 },
76 {
77 .code = MEDIA_BUS_FMT_SRGGB10_1X10,
78 .pixelformat = V4L2_PIX_FMT_SRGGB10,
79 .bpp = 2,
80 },
81
82 /* 10bit raw bayer a-law compressed to 8 bits */
83 {
84 .code = MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8,
85 .pixelformat = V4L2_PIX_FMT_SBGGR10ALAW8,
86 .bpp = 1,
87 },
88 {
89 .code = MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8,
90 .pixelformat = V4L2_PIX_FMT_SGBRG10ALAW8,
91 .bpp = 1,
92 },
93 {
94 .code = MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8,
95 .pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8,
96 .bpp = 1,
97 },
98 {
99 .code = MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8,
100 .pixelformat = V4L2_PIX_FMT_SRGGB10ALAW8,
101 .bpp = 1,
102 },
103
104 /* 10bit raw bayer DPCM compressed to 8 bits */
105 {
106 .code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
107 .pixelformat = V4L2_PIX_FMT_SBGGR10DPCM8,
108 .bpp = 1,
109 },
110 {
111 .code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
112 .pixelformat = V4L2_PIX_FMT_SGBRG10DPCM8,
113 .bpp = 1,
114 },
115 {
116 .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
117 .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
118 .bpp = 1,
119 },
120 {
121 .code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
122 .pixelformat = V4L2_PIX_FMT_SRGGB10DPCM8,
123 .bpp = 1,
124 },
125 {
126 .code = MEDIA_BUS_FMT_SBGGR12_1X12,
127 .pixelformat = V4L2_PIX_FMT_SBGGR12,
128 .bpp = 2,
129 },
130 {
131 .code = MEDIA_BUS_FMT_SGBRG12_1X12,
132 .pixelformat = V4L2_PIX_FMT_SGBRG12,
133 .bpp = 2,
134 },
135 {
136 .code = MEDIA_BUS_FMT_SGRBG12_1X12,
137 .pixelformat = V4L2_PIX_FMT_SGRBG12,
138 .bpp = 2,
139 },
140 {
141 .code = MEDIA_BUS_FMT_SRGGB12_1X12,
142 .pixelformat = V4L2_PIX_FMT_SRGGB12,
143 .bpp = 2,
144 },
145};
146
147const struct vimc_pix_map *vimc_pix_map_by_code(u32 code)
148{
149 unsigned int i;
150
151 for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
152 if (vimc_pix_map_list[i].code == code)
153 return &vimc_pix_map_list[i];
154 }
155 return NULL;
156}
157
158const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
159{
160 unsigned int i;
161
162 for (i = 0; i < ARRAY_SIZE(vimc_pix_map_list); i++) {
163 if (vimc_pix_map_list[i].pixelformat == pixelformat)
164 return &vimc_pix_map_list[i];
165 }
166 return NULL;
167}
168
169int vimc_propagate_frame(struct media_pad *src, const void *frame)
170{
171 struct media_link *link;
172
173 if (!(src->flags & MEDIA_PAD_FL_SOURCE))
174 return -EINVAL;
175
176 /* Send this frame to all sink pads that are direct linked */
177 list_for_each_entry(link, &src->entity->links, list) {
178 if (link->source == src &&
179 (link->flags & MEDIA_LNK_FL_ENABLED)) {
180 struct vimc_ent_device *ved = NULL;
181 struct media_entity *entity = link->sink->entity;
182
183 if (is_media_entity_v4l2_subdev(entity)) {
184 struct v4l2_subdev *sd =
185 container_of(entity, struct v4l2_subdev,
186 entity);
187 ved = v4l2_get_subdevdata(sd);
188 } else if (is_media_entity_v4l2_video_device(entity)) {
189 struct video_device *vdev =
190 container_of(entity,
191 struct video_device,
192 entity);
193 ved = video_get_drvdata(vdev);
194 }
195 if (ved && ved->process_frame)
196 ved->process_frame(ved, link->sink, frame);
197 }
198 }
199
200 return 0;
201}
202
203/* Helper function to allocate and initialize pads */
204struct media_pad *vimc_pads_init(u16 num_pads, const unsigned long *pads_flag)
205{
206 struct media_pad *pads;
207 unsigned int i;
208
209 /* Allocate memory for the pads */
210 pads = kcalloc(num_pads, sizeof(*pads), GFP_KERNEL);
211 if (!pads)
212 return ERR_PTR(-ENOMEM);
213
214 /* Initialize the pads */
215 for (i = 0; i < num_pads; i++) {
216 pads[i].index = i;
217 pads[i].flags = pads_flag[i];
218 }
219
220 return pads;
221}
Helen Fornazierc1495432017-06-19 14:00:12 -0300222
Helen Fornazierbf5fb952017-06-19 14:00:13 -0300223int vimc_pipeline_s_stream(struct media_entity *ent, int enable)
224{
225 struct v4l2_subdev *sd;
226 struct media_pad *pad;
227 unsigned int i;
228 int ret;
229
230 for (i = 0; i < ent->num_pads; i++) {
231 if (ent->pads[i].flags & MEDIA_PAD_FL_SOURCE)
232 continue;
233
234 /* Start the stream in the subdevice direct connected */
235 pad = media_entity_remote_pad(&ent->pads[i]);
236
237 /*
238 * if this is a raw node from vimc-core, then there is
239 * nothing to activate
240 * TODO: remove this when there are no more raw nodes in the
241 * core and return error instead
242 */
243 if (pad->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
244 continue;
245
246 sd = media_entity_to_v4l2_subdev(pad->entity);
247 ret = v4l2_subdev_call(sd, video, s_stream, enable);
248 if (ret && ret != -ENOIOCTLCMD)
249 return ret;
250 }
251
252 return 0;
253}
254
Helen Fornazier288a22d2017-06-19 14:00:14 -0300255static int vimc_get_mbus_format(struct media_pad *pad,
256 struct v4l2_subdev_format *fmt)
257{
258 if (is_media_entity_v4l2_subdev(pad->entity)) {
259 struct v4l2_subdev *sd =
260 media_entity_to_v4l2_subdev(pad->entity);
261 int ret;
262
263 fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
264 fmt->pad = pad->index;
265
266 ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt);
267 if (ret)
268 return ret;
269
270 } else if (is_media_entity_v4l2_video_device(pad->entity)) {
271 struct video_device *vdev = container_of(pad->entity,
272 struct video_device,
273 entity);
274 struct vimc_ent_device *ved = video_get_drvdata(vdev);
275 const struct vimc_pix_map *vpix;
276 struct v4l2_pix_format vdev_fmt;
277
278 if (!ved->vdev_get_format)
279 return -ENOIOCTLCMD;
280
281 ved->vdev_get_format(ved, &vdev_fmt);
282 vpix = vimc_pix_map_by_pixelformat(vdev_fmt.pixelformat);
283 v4l2_fill_mbus_format(&fmt->format, &vdev_fmt, vpix->code);
284 } else {
285 return -EINVAL;
286 }
287
288 return 0;
289}
290
291int vimc_link_validate(struct media_link *link)
292{
293 struct v4l2_subdev_format source_fmt, sink_fmt;
294 int ret;
295
296 /*
297 * if it is a raw node from vimc-core, ignore the link for now
298 * TODO: remove this when there are no more raw nodes in the
299 * core and return error instead
300 */
301 if (link->source->entity->obj_type == MEDIA_ENTITY_TYPE_BASE)
302 return 0;
303
304 ret = vimc_get_mbus_format(link->source, &source_fmt);
305 if (ret)
306 return ret;
307
308 ret = vimc_get_mbus_format(link->sink, &sink_fmt);
309 if (ret)
310 return ret;
311
312 pr_info("vimc link validate: "
313 "%s:src:%dx%d (0x%x, %d, %d, %d, %d) "
314 "%s:snk:%dx%d (0x%x, %d, %d, %d, %d)\n",
315 /* src */
316 link->source->entity->name,
317 source_fmt.format.width, source_fmt.format.height,
318 source_fmt.format.code, source_fmt.format.colorspace,
319 source_fmt.format.quantization, source_fmt.format.xfer_func,
320 source_fmt.format.ycbcr_enc,
321 /* sink */
322 link->sink->entity->name,
323 sink_fmt.format.width, sink_fmt.format.height,
324 sink_fmt.format.code, sink_fmt.format.colorspace,
325 sink_fmt.format.quantization, sink_fmt.format.xfer_func,
326 sink_fmt.format.ycbcr_enc);
327
328 /* The width, height and code must match. */
329 if (source_fmt.format.width != sink_fmt.format.width
330 || source_fmt.format.height != sink_fmt.format.height
331 || source_fmt.format.code != sink_fmt.format.code)
332 return -EPIPE;
333
334 /*
335 * The field order must match, or the sink field order must be NONE
336 * to support interlaced hardware connected to bridges that support
337 * progressive formats only.
338 */
339 if (source_fmt.format.field != sink_fmt.format.field &&
340 sink_fmt.format.field != V4L2_FIELD_NONE)
341 return -EPIPE;
342
343 /*
344 * If colorspace is DEFAULT, then assume all the colorimetry is also
345 * DEFAULT, return 0 to skip comparing the other colorimetry parameters
346 */
347 if (source_fmt.format.colorspace == V4L2_COLORSPACE_DEFAULT
348 || sink_fmt.format.colorspace == V4L2_COLORSPACE_DEFAULT)
349 return 0;
350
351 /* Colorspace must match. */
352 if (source_fmt.format.colorspace != sink_fmt.format.colorspace)
353 return -EPIPE;
354
355 /* Colorimetry must match if they are not set to DEFAULT */
356 if (source_fmt.format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT
357 && sink_fmt.format.ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT
358 && source_fmt.format.ycbcr_enc != sink_fmt.format.ycbcr_enc)
359 return -EPIPE;
360
361 if (source_fmt.format.quantization != V4L2_QUANTIZATION_DEFAULT
362 && sink_fmt.format.quantization != V4L2_QUANTIZATION_DEFAULT
363 && source_fmt.format.quantization != sink_fmt.format.quantization)
364 return -EPIPE;
365
366 if (source_fmt.format.xfer_func != V4L2_XFER_FUNC_DEFAULT
367 && sink_fmt.format.xfer_func != V4L2_XFER_FUNC_DEFAULT
368 && source_fmt.format.xfer_func != sink_fmt.format.xfer_func)
369 return -EPIPE;
370
371 return 0;
372}
373
Helen Fornazierc1495432017-06-19 14:00:12 -0300374static const struct media_entity_operations vimc_ent_sd_mops = {
Helen Fornazier288a22d2017-06-19 14:00:14 -0300375 .link_validate = vimc_link_validate,
Helen Fornazierc1495432017-06-19 14:00:12 -0300376};
377
378int vimc_ent_sd_register(struct vimc_ent_device *ved,
379 struct v4l2_subdev *sd,
380 struct v4l2_device *v4l2_dev,
381 const char *const name,
382 u32 function,
383 u16 num_pads,
384 const unsigned long *pads_flag,
385 const struct v4l2_subdev_ops *sd_ops,
386 void (*sd_destroy)(struct vimc_ent_device *))
387{
388 int ret;
389
390 /* Allocate the pads */
391 ved->pads = vimc_pads_init(num_pads, pads_flag);
392 if (IS_ERR(ved->pads))
393 return PTR_ERR(ved->pads);
394
395 /* Fill the vimc_ent_device struct */
396 ved->destroy = sd_destroy;
397 ved->ent = &sd->entity;
398
399 /* Initialize the subdev */
400 v4l2_subdev_init(sd, sd_ops);
401 sd->entity.function = function;
402 sd->entity.ops = &vimc_ent_sd_mops;
403 sd->owner = THIS_MODULE;
404 strlcpy(sd->name, name, sizeof(sd->name));
405 v4l2_set_subdevdata(sd, ved);
406
407 /* Expose this subdev to user space */
408 sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
409
410 /* Initialize the media entity */
411 ret = media_entity_pads_init(&sd->entity, num_pads, ved->pads);
412 if (ret)
413 goto err_clean_pads;
414
415 /* Register the subdev with the v4l2 and the media framework */
416 ret = v4l2_device_register_subdev(v4l2_dev, sd);
417 if (ret) {
418 dev_err(v4l2_dev->dev,
419 "%s: subdev register failed (err=%d)\n",
420 name, ret);
421 goto err_clean_m_ent;
422 }
423
424 return 0;
425
426err_clean_m_ent:
427 media_entity_cleanup(&sd->entity);
428err_clean_pads:
429 vimc_pads_cleanup(ved->pads);
430 return ret;
431}
432
433void vimc_ent_sd_unregister(struct vimc_ent_device *ved, struct v4l2_subdev *sd)
434{
435 v4l2_device_unregister_subdev(sd);
436 media_entity_cleanup(ved->ent);
437 vimc_pads_cleanup(ved->pads);
438}