at v4.16 12 kB view raw
1/* 2 * vimc-sensor.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 <linux/component.h> 19#include <linux/freezer.h> 20#include <linux/kthread.h> 21#include <linux/module.h> 22#include <linux/platform_device.h> 23#include <linux/v4l2-mediabus.h> 24#include <linux/vmalloc.h> 25#include <media/v4l2-ctrls.h> 26#include <media/v4l2-subdev.h> 27#include <media/tpg/v4l2-tpg.h> 28 29#include "vimc-common.h" 30 31#define VIMC_SEN_DRV_NAME "vimc-sensor" 32 33struct vimc_sen_device { 34 struct vimc_ent_device ved; 35 struct v4l2_subdev sd; 36 struct device *dev; 37 struct tpg_data tpg; 38 struct task_struct *kthread_sen; 39 u8 *frame; 40 /* The active format */ 41 struct v4l2_mbus_framefmt mbus_format; 42 struct v4l2_ctrl_handler hdl; 43}; 44 45static const struct v4l2_mbus_framefmt fmt_default = { 46 .width = 640, 47 .height = 480, 48 .code = MEDIA_BUS_FMT_RGB888_1X24, 49 .field = V4L2_FIELD_NONE, 50 .colorspace = V4L2_COLORSPACE_DEFAULT, 51}; 52 53static int vimc_sen_init_cfg(struct v4l2_subdev *sd, 54 struct v4l2_subdev_pad_config *cfg) 55{ 56 unsigned int i; 57 58 for (i = 0; i < sd->entity.num_pads; i++) { 59 struct v4l2_mbus_framefmt *mf; 60 61 mf = v4l2_subdev_get_try_format(sd, cfg, i); 62 *mf = fmt_default; 63 } 64 65 return 0; 66} 67 68static int vimc_sen_enum_mbus_code(struct v4l2_subdev *sd, 69 struct v4l2_subdev_pad_config *cfg, 70 struct v4l2_subdev_mbus_code_enum *code) 71{ 72 const struct vimc_pix_map *vpix = vimc_pix_map_by_index(code->index); 73 74 if (!vpix) 75 return -EINVAL; 76 77 code->code = vpix->code; 78 79 return 0; 80} 81 82static int vimc_sen_enum_frame_size(struct v4l2_subdev *sd, 83 struct v4l2_subdev_pad_config *cfg, 84 struct v4l2_subdev_frame_size_enum *fse) 85{ 86 const struct vimc_pix_map *vpix; 87 88 if (fse->index) 89 return -EINVAL; 90 91 /* Only accept code in the pix map table */ 92 vpix = vimc_pix_map_by_code(fse->code); 93 if (!vpix) 94 return -EINVAL; 95 96 fse->min_width = VIMC_FRAME_MIN_WIDTH; 97 fse->max_width = VIMC_FRAME_MAX_WIDTH; 98 fse->min_height = VIMC_FRAME_MIN_HEIGHT; 99 fse->max_height = VIMC_FRAME_MAX_HEIGHT; 100 101 return 0; 102} 103 104static int vimc_sen_get_fmt(struct v4l2_subdev *sd, 105 struct v4l2_subdev_pad_config *cfg, 106 struct v4l2_subdev_format *fmt) 107{ 108 struct vimc_sen_device *vsen = 109 container_of(sd, struct vimc_sen_device, sd); 110 111 fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? 112 *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) : 113 vsen->mbus_format; 114 115 return 0; 116} 117 118static void vimc_sen_tpg_s_format(struct vimc_sen_device *vsen) 119{ 120 const struct vimc_pix_map *vpix = 121 vimc_pix_map_by_code(vsen->mbus_format.code); 122 123 tpg_reset_source(&vsen->tpg, vsen->mbus_format.width, 124 vsen->mbus_format.height, vsen->mbus_format.field); 125 tpg_s_bytesperline(&vsen->tpg, 0, vsen->mbus_format.width * vpix->bpp); 126 tpg_s_buf_height(&vsen->tpg, vsen->mbus_format.height); 127 tpg_s_fourcc(&vsen->tpg, vpix->pixelformat); 128 /* TODO: add support for V4L2_FIELD_ALTERNATE */ 129 tpg_s_field(&vsen->tpg, vsen->mbus_format.field, false); 130 tpg_s_colorspace(&vsen->tpg, vsen->mbus_format.colorspace); 131 tpg_s_ycbcr_enc(&vsen->tpg, vsen->mbus_format.ycbcr_enc); 132 tpg_s_quantization(&vsen->tpg, vsen->mbus_format.quantization); 133 tpg_s_xfer_func(&vsen->tpg, vsen->mbus_format.xfer_func); 134} 135 136static void vimc_sen_adjust_fmt(struct v4l2_mbus_framefmt *fmt) 137{ 138 const struct vimc_pix_map *vpix; 139 140 /* Only accept code in the pix map table */ 141 vpix = vimc_pix_map_by_code(fmt->code); 142 if (!vpix) 143 fmt->code = fmt_default.code; 144 145 fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH, 146 VIMC_FRAME_MAX_WIDTH) & ~1; 147 fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT, 148 VIMC_FRAME_MAX_HEIGHT) & ~1; 149 150 /* TODO: add support for V4L2_FIELD_ALTERNATE */ 151 if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE) 152 fmt->field = fmt_default.field; 153 154 vimc_colorimetry_clamp(fmt); 155} 156 157static int vimc_sen_set_fmt(struct v4l2_subdev *sd, 158 struct v4l2_subdev_pad_config *cfg, 159 struct v4l2_subdev_format *fmt) 160{ 161 struct vimc_sen_device *vsen = v4l2_get_subdevdata(sd); 162 struct v4l2_mbus_framefmt *mf; 163 164 if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { 165 /* Do not change the format while stream is on */ 166 if (vsen->frame) 167 return -EBUSY; 168 169 mf = &vsen->mbus_format; 170 } else { 171 mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); 172 } 173 174 /* Set the new format */ 175 vimc_sen_adjust_fmt(&fmt->format); 176 177 dev_dbg(vsen->dev, "%s: format update: " 178 "old:%dx%d (0x%x, %d, %d, %d, %d) " 179 "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsen->sd.name, 180 /* old */ 181 mf->width, mf->height, mf->code, 182 mf->colorspace, mf->quantization, 183 mf->xfer_func, mf->ycbcr_enc, 184 /* new */ 185 fmt->format.width, fmt->format.height, fmt->format.code, 186 fmt->format.colorspace, fmt->format.quantization, 187 fmt->format.xfer_func, fmt->format.ycbcr_enc); 188 189 *mf = fmt->format; 190 191 return 0; 192} 193 194static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = { 195 .init_cfg = vimc_sen_init_cfg, 196 .enum_mbus_code = vimc_sen_enum_mbus_code, 197 .enum_frame_size = vimc_sen_enum_frame_size, 198 .get_fmt = vimc_sen_get_fmt, 199 .set_fmt = vimc_sen_set_fmt, 200}; 201 202static int vimc_sen_tpg_thread(void *data) 203{ 204 struct vimc_sen_device *vsen = data; 205 unsigned int i; 206 207 set_freezable(); 208 set_current_state(TASK_UNINTERRUPTIBLE); 209 210 for (;;) { 211 try_to_freeze(); 212 if (kthread_should_stop()) 213 break; 214 215 tpg_fill_plane_buffer(&vsen->tpg, 0, 0, vsen->frame); 216 217 /* Send the frame to all source pads */ 218 for (i = 0; i < vsen->sd.entity.num_pads; i++) 219 vimc_propagate_frame(&vsen->sd.entity.pads[i], 220 vsen->frame); 221 222 /* 60 frames per second */ 223 schedule_timeout(HZ/60); 224 } 225 226 return 0; 227} 228 229static int vimc_sen_s_stream(struct v4l2_subdev *sd, int enable) 230{ 231 struct vimc_sen_device *vsen = 232 container_of(sd, struct vimc_sen_device, sd); 233 int ret; 234 235 if (enable) { 236 const struct vimc_pix_map *vpix; 237 unsigned int frame_size; 238 239 if (vsen->kthread_sen) 240 /* tpg is already executing */ 241 return 0; 242 243 /* Calculate the frame size */ 244 vpix = vimc_pix_map_by_code(vsen->mbus_format.code); 245 frame_size = vsen->mbus_format.width * vpix->bpp * 246 vsen->mbus_format.height; 247 248 /* 249 * Allocate the frame buffer. Use vmalloc to be able to 250 * allocate a large amount of memory 251 */ 252 vsen->frame = vmalloc(frame_size); 253 if (!vsen->frame) 254 return -ENOMEM; 255 256 /* configure the test pattern generator */ 257 vimc_sen_tpg_s_format(vsen); 258 259 /* Initialize the image generator thread */ 260 vsen->kthread_sen = kthread_run(vimc_sen_tpg_thread, vsen, 261 "%s-sen", vsen->sd.v4l2_dev->name); 262 if (IS_ERR(vsen->kthread_sen)) { 263 dev_err(vsen->dev, "%s: kernel_thread() failed\n", 264 vsen->sd.name); 265 vfree(vsen->frame); 266 vsen->frame = NULL; 267 return PTR_ERR(vsen->kthread_sen); 268 } 269 } else { 270 if (!vsen->kthread_sen) 271 return 0; 272 273 /* Stop image generator */ 274 ret = kthread_stop(vsen->kthread_sen); 275 if (ret) 276 return ret; 277 278 vsen->kthread_sen = NULL; 279 vfree(vsen->frame); 280 vsen->frame = NULL; 281 return 0; 282 } 283 284 return 0; 285} 286 287static const struct v4l2_subdev_video_ops vimc_sen_video_ops = { 288 .s_stream = vimc_sen_s_stream, 289}; 290 291static const struct v4l2_subdev_ops vimc_sen_ops = { 292 .pad = &vimc_sen_pad_ops, 293 .video = &vimc_sen_video_ops, 294}; 295 296static int vimc_sen_s_ctrl(struct v4l2_ctrl *ctrl) 297{ 298 struct vimc_sen_device *vsen = 299 container_of(ctrl->handler, struct vimc_sen_device, hdl); 300 301 switch (ctrl->id) { 302 case VIMC_CID_TEST_PATTERN: 303 tpg_s_pattern(&vsen->tpg, ctrl->val); 304 break; 305 case V4L2_CID_HFLIP: 306 tpg_s_hflip(&vsen->tpg, ctrl->val); 307 break; 308 case V4L2_CID_VFLIP: 309 tpg_s_vflip(&vsen->tpg, ctrl->val); 310 break; 311 default: 312 return -EINVAL; 313 } 314 return 0; 315} 316 317static const struct v4l2_ctrl_ops vimc_sen_ctrl_ops = { 318 .s_ctrl = vimc_sen_s_ctrl, 319}; 320 321static void vimc_sen_comp_unbind(struct device *comp, struct device *master, 322 void *master_data) 323{ 324 struct vimc_ent_device *ved = dev_get_drvdata(comp); 325 struct vimc_sen_device *vsen = 326 container_of(ved, struct vimc_sen_device, ved); 327 328 vimc_ent_sd_unregister(ved, &vsen->sd); 329 v4l2_ctrl_handler_free(&vsen->hdl); 330 tpg_free(&vsen->tpg); 331 kfree(vsen); 332} 333 334/* Image Processing Controls */ 335static const struct v4l2_ctrl_config vimc_sen_ctrl_class = { 336 .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY, 337 .id = VIMC_CID_VIMC_CLASS, 338 .name = "VIMC Controls", 339 .type = V4L2_CTRL_TYPE_CTRL_CLASS, 340}; 341 342static const struct v4l2_ctrl_config vimc_sen_ctrl_test_pattern = { 343 .ops = &vimc_sen_ctrl_ops, 344 .id = VIMC_CID_TEST_PATTERN, 345 .name = "Test Pattern", 346 .type = V4L2_CTRL_TYPE_MENU, 347 .max = TPG_PAT_NOISE, 348 .qmenu = tpg_pattern_strings, 349}; 350 351static int vimc_sen_comp_bind(struct device *comp, struct device *master, 352 void *master_data) 353{ 354 struct v4l2_device *v4l2_dev = master_data; 355 struct vimc_platform_data *pdata = comp->platform_data; 356 struct vimc_sen_device *vsen; 357 int ret; 358 359 /* Allocate the vsen struct */ 360 vsen = kzalloc(sizeof(*vsen), GFP_KERNEL); 361 if (!vsen) 362 return -ENOMEM; 363 364 v4l2_ctrl_handler_init(&vsen->hdl, 4); 365 366 v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_class, NULL); 367 v4l2_ctrl_new_custom(&vsen->hdl, &vimc_sen_ctrl_test_pattern, NULL); 368 v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, 369 V4L2_CID_VFLIP, 0, 1, 1, 0); 370 v4l2_ctrl_new_std(&vsen->hdl, &vimc_sen_ctrl_ops, 371 V4L2_CID_HFLIP, 0, 1, 1, 0); 372 vsen->sd.ctrl_handler = &vsen->hdl; 373 if (vsen->hdl.error) { 374 ret = vsen->hdl.error; 375 goto err_free_vsen; 376 } 377 378 /* Initialize ved and sd */ 379 ret = vimc_ent_sd_register(&vsen->ved, &vsen->sd, v4l2_dev, 380 pdata->entity_name, 381 MEDIA_ENT_F_ATV_DECODER, 1, 382 (const unsigned long[1]) {MEDIA_PAD_FL_SOURCE}, 383 &vimc_sen_ops); 384 if (ret) 385 goto err_free_hdl; 386 387 dev_set_drvdata(comp, &vsen->ved); 388 vsen->dev = comp; 389 390 /* Initialize the frame format */ 391 vsen->mbus_format = fmt_default; 392 393 /* Initialize the test pattern generator */ 394 tpg_init(&vsen->tpg, vsen->mbus_format.width, 395 vsen->mbus_format.height); 396 ret = tpg_alloc(&vsen->tpg, VIMC_FRAME_MAX_WIDTH); 397 if (ret) 398 goto err_unregister_ent_sd; 399 400 return 0; 401 402err_unregister_ent_sd: 403 vimc_ent_sd_unregister(&vsen->ved, &vsen->sd); 404err_free_hdl: 405 v4l2_ctrl_handler_free(&vsen->hdl); 406err_free_vsen: 407 kfree(vsen); 408 409 return ret; 410} 411 412static const struct component_ops vimc_sen_comp_ops = { 413 .bind = vimc_sen_comp_bind, 414 .unbind = vimc_sen_comp_unbind, 415}; 416 417static int vimc_sen_probe(struct platform_device *pdev) 418{ 419 return component_add(&pdev->dev, &vimc_sen_comp_ops); 420} 421 422static int vimc_sen_remove(struct platform_device *pdev) 423{ 424 component_del(&pdev->dev, &vimc_sen_comp_ops); 425 426 return 0; 427} 428 429static const struct platform_device_id vimc_sen_driver_ids[] = { 430 { 431 .name = VIMC_SEN_DRV_NAME, 432 }, 433 { } 434}; 435 436static struct platform_driver vimc_sen_pdrv = { 437 .probe = vimc_sen_probe, 438 .remove = vimc_sen_remove, 439 .id_table = vimc_sen_driver_ids, 440 .driver = { 441 .name = VIMC_SEN_DRV_NAME, 442 }, 443}; 444 445module_platform_driver(vimc_sen_pdrv); 446 447MODULE_DEVICE_TABLE(platform, vimc_sen_driver_ids); 448 449MODULE_DESCRIPTION("Virtual Media Controller Driver (VIMC) Sensor"); 450MODULE_AUTHOR("Helen Mae Koike Fornazier <helen.fornazier@gmail.com>"); 451MODULE_LICENSE("GPL");