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