1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * vimc-common.c Virtual Media Controller Driver
5 * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
8 #include <linux/init.h>
9 #include <linux/module.h>
11 #include "vimc-common.h"
14 * NOTE: non-bayer formats need to come first (necessary for enum_mbus_code
17 static const struct vimc_pix_map vimc_pix_map_list
[] = {
18 /* TODO: add all missing formats */
23 MEDIA_BUS_FMT_BGR888_1X24
,
24 MEDIA_BUS_FMT_BGR888_3X8
26 .pixelformat
= V4L2_PIX_FMT_BGR24
,
32 MEDIA_BUS_FMT_RGB888_1X24
,
33 MEDIA_BUS_FMT_RGB888_2X12_BE
,
34 MEDIA_BUS_FMT_RGB888_2X12_LE
,
35 MEDIA_BUS_FMT_RGB888_3X8
,
36 MEDIA_BUS_FMT_RGB888_1X7X4_SPWG
,
37 MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA
,
38 MEDIA_BUS_FMT_RGB888_1X32_PADHI
,
39 MEDIA_BUS_FMT_GBR888_1X24
41 .pixelformat
= V4L2_PIX_FMT_RGB24
,
46 .code
= { MEDIA_BUS_FMT_ARGB8888_1X32
},
47 .pixelformat
= V4L2_PIX_FMT_ARGB32
,
54 .code
= { MEDIA_BUS_FMT_SBGGR8_1X8
},
55 .pixelformat
= V4L2_PIX_FMT_SBGGR8
,
60 .code
= { MEDIA_BUS_FMT_SGBRG8_1X8
},
61 .pixelformat
= V4L2_PIX_FMT_SGBRG8
,
66 .code
= { MEDIA_BUS_FMT_SGRBG8_1X8
},
67 .pixelformat
= V4L2_PIX_FMT_SGRBG8
,
72 .code
= { MEDIA_BUS_FMT_SRGGB8_1X8
},
73 .pixelformat
= V4L2_PIX_FMT_SRGGB8
,
78 .code
= { MEDIA_BUS_FMT_SBGGR10_1X10
},
79 .pixelformat
= V4L2_PIX_FMT_SBGGR10
,
84 .code
= { MEDIA_BUS_FMT_SGBRG10_1X10
},
85 .pixelformat
= V4L2_PIX_FMT_SGBRG10
,
90 .code
= { MEDIA_BUS_FMT_SGRBG10_1X10
},
91 .pixelformat
= V4L2_PIX_FMT_SGRBG10
,
96 .code
= { MEDIA_BUS_FMT_SRGGB10_1X10
},
97 .pixelformat
= V4L2_PIX_FMT_SRGGB10
,
102 /* 10bit raw bayer a-law compressed to 8 bits */
104 .code
= { MEDIA_BUS_FMT_SBGGR10_ALAW8_1X8
},
105 .pixelformat
= V4L2_PIX_FMT_SBGGR10ALAW8
,
110 .code
= { MEDIA_BUS_FMT_SGBRG10_ALAW8_1X8
},
111 .pixelformat
= V4L2_PIX_FMT_SGBRG10ALAW8
,
116 .code
= { MEDIA_BUS_FMT_SGRBG10_ALAW8_1X8
},
117 .pixelformat
= V4L2_PIX_FMT_SGRBG10ALAW8
,
122 .code
= { MEDIA_BUS_FMT_SRGGB10_ALAW8_1X8
},
123 .pixelformat
= V4L2_PIX_FMT_SRGGB10ALAW8
,
128 /* 10bit raw bayer DPCM compressed to 8 bits */
130 .code
= { MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8
},
131 .pixelformat
= V4L2_PIX_FMT_SBGGR10DPCM8
,
136 .code
= { MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8
},
137 .pixelformat
= V4L2_PIX_FMT_SGBRG10DPCM8
,
142 .code
= { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8
},
143 .pixelformat
= V4L2_PIX_FMT_SGRBG10DPCM8
,
148 .code
= { MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8
},
149 .pixelformat
= V4L2_PIX_FMT_SRGGB10DPCM8
,
154 .code
= { MEDIA_BUS_FMT_SBGGR12_1X12
},
155 .pixelformat
= V4L2_PIX_FMT_SBGGR12
,
160 .code
= { MEDIA_BUS_FMT_SGBRG12_1X12
},
161 .pixelformat
= V4L2_PIX_FMT_SGBRG12
,
166 .code
= { MEDIA_BUS_FMT_SGRBG12_1X12
},
167 .pixelformat
= V4L2_PIX_FMT_SGRBG12
,
172 .code
= { MEDIA_BUS_FMT_SRGGB12_1X12
},
173 .pixelformat
= V4L2_PIX_FMT_SRGGB12
,
179 bool vimc_is_source(struct media_entity
*ent
)
183 for (i
= 0; i
< ent
->num_pads
; i
++)
184 if (ent
->pads
[i
].flags
& MEDIA_PAD_FL_SINK
)
189 const struct vimc_pix_map
*vimc_pix_map_by_index(unsigned int i
)
191 if (i
>= ARRAY_SIZE(vimc_pix_map_list
))
194 return &vimc_pix_map_list
[i
];
197 u32
vimc_mbus_code_by_index(unsigned int index
)
201 for (i
= 0; i
< ARRAY_SIZE(vimc_pix_map_list
); i
++) {
202 for (j
= 0; j
< ARRAY_SIZE(vimc_pix_map_list
[i
].code
); j
++) {
203 if (!vimc_pix_map_list
[i
].code
[j
])
207 return vimc_pix_map_list
[i
].code
[j
];
214 const struct vimc_pix_map
*vimc_pix_map_by_code(u32 code
)
218 for (i
= 0; i
< ARRAY_SIZE(vimc_pix_map_list
); i
++) {
219 for (j
= 0; j
< ARRAY_SIZE(vimc_pix_map_list
[i
].code
); j
++) {
220 if (vimc_pix_map_list
[i
].code
[j
] == code
)
221 return &vimc_pix_map_list
[i
];
227 const struct vimc_pix_map
*vimc_pix_map_by_pixelformat(u32 pixelformat
)
231 for (i
= 0; i
< ARRAY_SIZE(vimc_pix_map_list
); i
++) {
232 if (vimc_pix_map_list
[i
].pixelformat
== pixelformat
)
233 return &vimc_pix_map_list
[i
];
238 static int vimc_get_pix_format(struct media_pad
*pad
,
239 struct v4l2_pix_format
*fmt
)
241 if (is_media_entity_v4l2_subdev(pad
->entity
)) {
242 struct v4l2_subdev
*sd
=
243 media_entity_to_v4l2_subdev(pad
->entity
);
244 struct v4l2_subdev_format sd_fmt
;
245 const struct vimc_pix_map
*pix_map
;
248 sd_fmt
.which
= V4L2_SUBDEV_FORMAT_ACTIVE
;
249 sd_fmt
.pad
= pad
->index
;
251 ret
= v4l2_subdev_call(sd
, pad
, get_fmt
, NULL
, &sd_fmt
);
255 v4l2_fill_pix_format(fmt
, &sd_fmt
.format
);
256 pix_map
= vimc_pix_map_by_code(sd_fmt
.format
.code
);
257 fmt
->pixelformat
= pix_map
->pixelformat
;
258 } else if (is_media_entity_v4l2_video_device(pad
->entity
)) {
259 struct video_device
*vdev
= container_of(pad
->entity
,
262 struct vimc_ent_device
*ved
= video_get_drvdata(vdev
);
264 if (!ved
->vdev_get_format
)
267 ved
->vdev_get_format(ved
, fmt
);
275 int vimc_vdev_link_validate(struct media_link
*link
)
277 struct v4l2_pix_format source_fmt
, sink_fmt
;
280 ret
= vimc_get_pix_format(link
->source
, &source_fmt
);
284 ret
= vimc_get_pix_format(link
->sink
, &sink_fmt
);
288 pr_info("vimc link validate: "
289 "%s:src:%dx%d (0x%x, %d, %d, %d, %d) "
290 "%s:snk:%dx%d (0x%x, %d, %d, %d, %d)\n",
292 link
->source
->entity
->name
,
293 source_fmt
.width
, source_fmt
.height
,
294 source_fmt
.pixelformat
, source_fmt
.colorspace
,
295 source_fmt
.quantization
, source_fmt
.xfer_func
,
296 source_fmt
.ycbcr_enc
,
298 link
->sink
->entity
->name
,
299 sink_fmt
.width
, sink_fmt
.height
,
300 sink_fmt
.pixelformat
, sink_fmt
.colorspace
,
301 sink_fmt
.quantization
, sink_fmt
.xfer_func
,
304 /* The width, height and pixelformat must match. */
305 if (source_fmt
.width
!= sink_fmt
.width
||
306 source_fmt
.height
!= sink_fmt
.height
||
307 source_fmt
.pixelformat
!= sink_fmt
.pixelformat
)
311 * The field order must match, or the sink field order must be NONE
312 * to support interlaced hardware connected to bridges that support
313 * progressive formats only.
315 if (source_fmt
.field
!= sink_fmt
.field
&&
316 sink_fmt
.field
!= V4L2_FIELD_NONE
)
320 * If colorspace is DEFAULT, then assume all the colorimetry is also
321 * DEFAULT, return 0 to skip comparing the other colorimetry parameters
323 if (source_fmt
.colorspace
== V4L2_COLORSPACE_DEFAULT
||
324 sink_fmt
.colorspace
== V4L2_COLORSPACE_DEFAULT
)
327 /* Colorspace must match. */
328 if (source_fmt
.colorspace
!= sink_fmt
.colorspace
)
331 /* Colorimetry must match if they are not set to DEFAULT */
332 if (source_fmt
.ycbcr_enc
!= V4L2_YCBCR_ENC_DEFAULT
&&
333 sink_fmt
.ycbcr_enc
!= V4L2_YCBCR_ENC_DEFAULT
&&
334 source_fmt
.ycbcr_enc
!= sink_fmt
.ycbcr_enc
)
337 if (source_fmt
.quantization
!= V4L2_QUANTIZATION_DEFAULT
&&
338 sink_fmt
.quantization
!= V4L2_QUANTIZATION_DEFAULT
&&
339 source_fmt
.quantization
!= sink_fmt
.quantization
)
342 if (source_fmt
.xfer_func
!= V4L2_XFER_FUNC_DEFAULT
&&
343 sink_fmt
.xfer_func
!= V4L2_XFER_FUNC_DEFAULT
&&
344 source_fmt
.xfer_func
!= sink_fmt
.xfer_func
)
350 static const struct media_entity_operations vimc_ent_sd_mops
= {
351 .link_validate
= v4l2_subdev_link_validate
,
354 int vimc_ent_sd_register(struct vimc_ent_device
*ved
,
355 struct v4l2_subdev
*sd
,
356 struct v4l2_device
*v4l2_dev
,
357 const char *const name
,
360 struct media_pad
*pads
,
361 const struct v4l2_subdev_ops
*sd_ops
)
365 /* Fill the vimc_ent_device struct */
366 ved
->ent
= &sd
->entity
;
368 /* Initialize the subdev */
369 v4l2_subdev_init(sd
, sd_ops
);
370 sd
->entity
.function
= function
;
371 sd
->entity
.ops
= &vimc_ent_sd_mops
;
372 sd
->owner
= THIS_MODULE
;
373 strscpy(sd
->name
, name
, sizeof(sd
->name
));
374 v4l2_set_subdevdata(sd
, ved
);
376 /* Expose this subdev to user space */
377 sd
->flags
|= V4L2_SUBDEV_FL_HAS_DEVNODE
;
378 if (sd
->ctrl_handler
)
379 sd
->flags
|= V4L2_SUBDEV_FL_HAS_EVENTS
;
381 /* Initialize the media entity */
382 ret
= media_entity_pads_init(&sd
->entity
, num_pads
, pads
);
386 /* Register the subdev with the v4l2 and the media framework */
387 ret
= v4l2_device_register_subdev(v4l2_dev
, sd
);
389 dev_err(v4l2_dev
->dev
,
390 "%s: subdev register failed (err=%d)\n",
392 goto err_clean_m_ent
;
398 media_entity_cleanup(&sd
->entity
);