1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * vimc-sensor.c Virtual Media Controller Driver
5 * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com>
8 #include <linux/component.h>
9 #include <linux/module.h>
10 #include <linux/mod_devicetable.h>
11 #include <linux/platform_device.h>
12 #include <linux/v4l2-mediabus.h>
13 #include <linux/vmalloc.h>
14 #include <media/v4l2-ctrls.h>
15 #include <media/v4l2-event.h>
16 #include <media/v4l2-subdev.h>
17 #include <media/tpg/v4l2-tpg.h>
19 #include "vimc-common.h"
21 #define VIMC_SEN_DRV_NAME "vimc-sensor"
23 struct vimc_sen_device
{
24 struct vimc_ent_device ved
;
25 struct v4l2_subdev sd
;
28 struct task_struct
*kthread_sen
;
30 /* The active format */
31 struct v4l2_mbus_framefmt mbus_format
;
32 struct v4l2_ctrl_handler hdl
;
35 static const struct v4l2_mbus_framefmt fmt_default
= {
38 .code
= MEDIA_BUS_FMT_RGB888_1X24
,
39 .field
= V4L2_FIELD_NONE
,
40 .colorspace
= V4L2_COLORSPACE_DEFAULT
,
43 static int vimc_sen_init_cfg(struct v4l2_subdev
*sd
,
44 struct v4l2_subdev_pad_config
*cfg
)
48 for (i
= 0; i
< sd
->entity
.num_pads
; i
++) {
49 struct v4l2_mbus_framefmt
*mf
;
51 mf
= v4l2_subdev_get_try_format(sd
, cfg
, i
);
58 static int vimc_sen_enum_frame_size(struct v4l2_subdev
*sd
,
59 struct v4l2_subdev_pad_config
*cfg
,
60 struct v4l2_subdev_frame_size_enum
*fse
)
65 fse
->min_width
= VIMC_FRAME_MIN_WIDTH
;
66 fse
->max_width
= VIMC_FRAME_MAX_WIDTH
;
67 fse
->min_height
= VIMC_FRAME_MIN_HEIGHT
;
68 fse
->max_height
= VIMC_FRAME_MAX_HEIGHT
;
73 static int vimc_sen_get_fmt(struct v4l2_subdev
*sd
,
74 struct v4l2_subdev_pad_config
*cfg
,
75 struct v4l2_subdev_format
*fmt
)
77 struct vimc_sen_device
*vsen
=
78 container_of(sd
, struct vimc_sen_device
, sd
);
80 fmt
->format
= fmt
->which
== V4L2_SUBDEV_FORMAT_TRY
?
81 *v4l2_subdev_get_try_format(sd
, cfg
, fmt
->pad
) :
87 static void vimc_sen_tpg_s_format(struct vimc_sen_device
*vsen
)
89 u32 pixelformat
= vsen
->ved
.stream
->producer_pixfmt
;
90 const struct v4l2_format_info
*pix_info
;
92 pix_info
= v4l2_format_info(pixelformat
);
94 tpg_reset_source(&vsen
->tpg
, vsen
->mbus_format
.width
,
95 vsen
->mbus_format
.height
, vsen
->mbus_format
.field
);
96 tpg_s_bytesperline(&vsen
->tpg
, 0,
97 vsen
->mbus_format
.width
* pix_info
->bpp
[0]);
98 tpg_s_buf_height(&vsen
->tpg
, vsen
->mbus_format
.height
);
99 tpg_s_fourcc(&vsen
->tpg
, pixelformat
);
100 /* TODO: add support for V4L2_FIELD_ALTERNATE */
101 tpg_s_field(&vsen
->tpg
, vsen
->mbus_format
.field
, false);
102 tpg_s_colorspace(&vsen
->tpg
, vsen
->mbus_format
.colorspace
);
103 tpg_s_ycbcr_enc(&vsen
->tpg
, vsen
->mbus_format
.ycbcr_enc
);
104 tpg_s_quantization(&vsen
->tpg
, vsen
->mbus_format
.quantization
);
105 tpg_s_xfer_func(&vsen
->tpg
, vsen
->mbus_format
.xfer_func
);
108 static void vimc_sen_adjust_fmt(struct v4l2_mbus_framefmt
*fmt
)
110 fmt
->width
= clamp_t(u32
, fmt
->width
, VIMC_FRAME_MIN_WIDTH
,
111 VIMC_FRAME_MAX_WIDTH
) & ~1;
112 fmt
->height
= clamp_t(u32
, fmt
->height
, VIMC_FRAME_MIN_HEIGHT
,
113 VIMC_FRAME_MAX_HEIGHT
) & ~1;
115 /* TODO: add support for V4L2_FIELD_ALTERNATE */
116 if (fmt
->field
== V4L2_FIELD_ANY
|| fmt
->field
== V4L2_FIELD_ALTERNATE
)
117 fmt
->field
= fmt_default
.field
;
119 vimc_colorimetry_clamp(fmt
);
122 static int vimc_sen_set_fmt(struct v4l2_subdev
*sd
,
123 struct v4l2_subdev_pad_config
*cfg
,
124 struct v4l2_subdev_format
*fmt
)
126 struct vimc_sen_device
*vsen
= v4l2_get_subdevdata(sd
);
127 struct v4l2_mbus_framefmt
*mf
;
129 if (!vimc_mbus_code_supported(fmt
->format
.code
))
130 fmt
->format
.code
= fmt_default
.code
;
132 if (fmt
->which
== V4L2_SUBDEV_FORMAT_ACTIVE
) {
133 /* Do not change the format while stream is on */
137 mf
= &vsen
->mbus_format
;
139 mf
= v4l2_subdev_get_try_format(sd
, cfg
, fmt
->pad
);
142 /* Set the new format */
143 vimc_sen_adjust_fmt(&fmt
->format
);
145 dev_dbg(vsen
->dev
, "%s: format update: "
146 "old:%dx%d (0x%x, %d, %d, %d, %d) "
147 "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsen
->sd
.name
,
149 mf
->width
, mf
->height
, mf
->code
,
150 mf
->colorspace
, mf
->quantization
,
151 mf
->xfer_func
, mf
->ycbcr_enc
,
153 fmt
->format
.width
, fmt
->format
.height
, fmt
->format
.code
,
154 fmt
->format
.colorspace
, fmt
->format
.quantization
,
155 fmt
->format
.xfer_func
, fmt
->format
.ycbcr_enc
);
162 static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops
= {
163 .init_cfg
= vimc_sen_init_cfg
,
164 .enum_mbus_code
= vimc_enum_mbus_code
,
165 .enum_frame_size
= vimc_sen_enum_frame_size
,
166 .get_fmt
= vimc_sen_get_fmt
,
167 .set_fmt
= vimc_sen_set_fmt
,
170 static void *vimc_sen_process_frame(struct vimc_ent_device
*ved
,
171 const void *sink_frame
)
173 struct vimc_sen_device
*vsen
= container_of(ved
, struct vimc_sen_device
,
176 tpg_fill_plane_buffer(&vsen
->tpg
, 0, 0, vsen
->frame
);
180 static int vimc_sen_s_stream(struct v4l2_subdev
*sd
, int enable
)
182 struct vimc_sen_device
*vsen
=
183 container_of(sd
, struct vimc_sen_device
, sd
);
186 u32 pixelformat
= vsen
->ved
.stream
->producer_pixfmt
;
187 const struct v4l2_format_info
*pix_info
;
188 unsigned int frame_size
;
190 if (vsen
->kthread_sen
)
191 /* tpg is already executing */
194 /* Calculate the frame size */
195 pix_info
= v4l2_format_info(pixelformat
);
196 frame_size
= vsen
->mbus_format
.width
* pix_info
->bpp
[0] *
197 vsen
->mbus_format
.height
;
200 * Allocate the frame buffer. Use vmalloc to be able to
201 * allocate a large amount of memory
203 vsen
->frame
= vmalloc(frame_size
);
207 /* configure the test pattern generator */
208 vimc_sen_tpg_s_format(vsen
);
220 static const struct v4l2_subdev_core_ops vimc_sen_core_ops
= {
221 .log_status
= v4l2_ctrl_subdev_log_status
,
222 .subscribe_event
= v4l2_ctrl_subdev_subscribe_event
,
223 .unsubscribe_event
= v4l2_event_subdev_unsubscribe
,
226 static const struct v4l2_subdev_video_ops vimc_sen_video_ops
= {
227 .s_stream
= vimc_sen_s_stream
,
230 static const struct v4l2_subdev_ops vimc_sen_ops
= {
231 .core
= &vimc_sen_core_ops
,
232 .pad
= &vimc_sen_pad_ops
,
233 .video
= &vimc_sen_video_ops
,
236 static int vimc_sen_s_ctrl(struct v4l2_ctrl
*ctrl
)
238 struct vimc_sen_device
*vsen
=
239 container_of(ctrl
->handler
, struct vimc_sen_device
, hdl
);
242 case VIMC_CID_TEST_PATTERN
:
243 tpg_s_pattern(&vsen
->tpg
, ctrl
->val
);
246 tpg_s_hflip(&vsen
->tpg
, ctrl
->val
);
249 tpg_s_vflip(&vsen
->tpg
, ctrl
->val
);
251 case V4L2_CID_BRIGHTNESS
:
252 tpg_s_brightness(&vsen
->tpg
, ctrl
->val
);
254 case V4L2_CID_CONTRAST
:
255 tpg_s_contrast(&vsen
->tpg
, ctrl
->val
);
258 tpg_s_hue(&vsen
->tpg
, ctrl
->val
);
260 case V4L2_CID_SATURATION
:
261 tpg_s_saturation(&vsen
->tpg
, ctrl
->val
);
269 static const struct v4l2_ctrl_ops vimc_sen_ctrl_ops
= {
270 .s_ctrl
= vimc_sen_s_ctrl
,
273 static void vimc_sen_release(struct v4l2_subdev
*sd
)
275 struct vimc_sen_device
*vsen
=
276 container_of(sd
, struct vimc_sen_device
, sd
);
278 v4l2_ctrl_handler_free(&vsen
->hdl
);
279 tpg_free(&vsen
->tpg
);
283 static const struct v4l2_subdev_internal_ops vimc_sen_int_ops
= {
284 .release
= vimc_sen_release
,
287 static void vimc_sen_comp_unbind(struct device
*comp
, struct device
*master
,
290 struct vimc_ent_device
*ved
= dev_get_drvdata(comp
);
291 struct vimc_sen_device
*vsen
=
292 container_of(ved
, struct vimc_sen_device
, ved
);
294 vimc_ent_sd_unregister(ved
, &vsen
->sd
);
297 /* Image Processing Controls */
298 static const struct v4l2_ctrl_config vimc_sen_ctrl_class
= {
299 .flags
= V4L2_CTRL_FLAG_READ_ONLY
| V4L2_CTRL_FLAG_WRITE_ONLY
,
300 .id
= VIMC_CID_VIMC_CLASS
,
301 .name
= "VIMC Controls",
302 .type
= V4L2_CTRL_TYPE_CTRL_CLASS
,
305 static const struct v4l2_ctrl_config vimc_sen_ctrl_test_pattern
= {
306 .ops
= &vimc_sen_ctrl_ops
,
307 .id
= VIMC_CID_TEST_PATTERN
,
308 .name
= "Test Pattern",
309 .type
= V4L2_CTRL_TYPE_MENU
,
310 .max
= TPG_PAT_NOISE
,
311 .qmenu
= tpg_pattern_strings
,
314 static int vimc_sen_comp_bind(struct device
*comp
, struct device
*master
,
317 struct v4l2_device
*v4l2_dev
= master_data
;
318 struct vimc_platform_data
*pdata
= comp
->platform_data
;
319 struct vimc_sen_device
*vsen
;
322 /* Allocate the vsen struct */
323 vsen
= kzalloc(sizeof(*vsen
), GFP_KERNEL
);
327 v4l2_ctrl_handler_init(&vsen
->hdl
, 4);
329 v4l2_ctrl_new_custom(&vsen
->hdl
, &vimc_sen_ctrl_class
, NULL
);
330 v4l2_ctrl_new_custom(&vsen
->hdl
, &vimc_sen_ctrl_test_pattern
, NULL
);
331 v4l2_ctrl_new_std(&vsen
->hdl
, &vimc_sen_ctrl_ops
,
332 V4L2_CID_VFLIP
, 0, 1, 1, 0);
333 v4l2_ctrl_new_std(&vsen
->hdl
, &vimc_sen_ctrl_ops
,
334 V4L2_CID_HFLIP
, 0, 1, 1, 0);
335 v4l2_ctrl_new_std(&vsen
->hdl
, &vimc_sen_ctrl_ops
,
336 V4L2_CID_BRIGHTNESS
, 0, 255, 1, 128);
337 v4l2_ctrl_new_std(&vsen
->hdl
, &vimc_sen_ctrl_ops
,
338 V4L2_CID_CONTRAST
, 0, 255, 1, 128);
339 v4l2_ctrl_new_std(&vsen
->hdl
, &vimc_sen_ctrl_ops
,
340 V4L2_CID_HUE
, -128, 127, 1, 0);
341 v4l2_ctrl_new_std(&vsen
->hdl
, &vimc_sen_ctrl_ops
,
342 V4L2_CID_SATURATION
, 0, 255, 1, 128);
343 vsen
->sd
.ctrl_handler
= &vsen
->hdl
;
344 if (vsen
->hdl
.error
) {
345 ret
= vsen
->hdl
.error
;
349 /* Initialize ved and sd */
350 ret
= vimc_ent_sd_register(&vsen
->ved
, &vsen
->sd
, v4l2_dev
,
352 MEDIA_ENT_F_CAM_SENSOR
, 1,
353 (const unsigned long[1]) {MEDIA_PAD_FL_SOURCE
},
354 &vimc_sen_int_ops
, &vimc_sen_ops
);
358 vsen
->ved
.process_frame
= vimc_sen_process_frame
;
359 dev_set_drvdata(comp
, &vsen
->ved
);
362 /* Initialize the frame format */
363 vsen
->mbus_format
= fmt_default
;
365 /* Initialize the test pattern generator */
366 tpg_init(&vsen
->tpg
, vsen
->mbus_format
.width
,
367 vsen
->mbus_format
.height
);
368 ret
= tpg_alloc(&vsen
->tpg
, VIMC_FRAME_MAX_WIDTH
);
370 goto err_unregister_ent_sd
;
374 err_unregister_ent_sd
:
375 vimc_ent_sd_unregister(&vsen
->ved
, &vsen
->sd
);
377 v4l2_ctrl_handler_free(&vsen
->hdl
);
384 static const struct component_ops vimc_sen_comp_ops
= {
385 .bind
= vimc_sen_comp_bind
,
386 .unbind
= vimc_sen_comp_unbind
,
389 static int vimc_sen_probe(struct platform_device
*pdev
)
391 return component_add(&pdev
->dev
, &vimc_sen_comp_ops
);
394 static int vimc_sen_remove(struct platform_device
*pdev
)
396 component_del(&pdev
->dev
, &vimc_sen_comp_ops
);
401 static const struct platform_device_id vimc_sen_driver_ids
[] = {
403 .name
= VIMC_SEN_DRV_NAME
,
408 static struct platform_driver vimc_sen_pdrv
= {
409 .probe
= vimc_sen_probe
,
410 .remove
= vimc_sen_remove
,
411 .id_table
= vimc_sen_driver_ids
,
413 .name
= VIMC_SEN_DRV_NAME
,
417 module_platform_driver(vimc_sen_pdrv
);
419 MODULE_DEVICE_TABLE(platform
, vimc_sen_driver_ids
);
421 MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Sensor");
422 MODULE_AUTHOR("Helen Mae Koike Fornazier <helen.fornazier@gmail.com>");
423 MODULE_LICENSE("GPL");