162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * vimc-sensor.c Virtual Media Controller Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015-2017 Helen Koike <helen.fornazier@gmail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/v4l2-mediabus.h> 962306a36Sopenharmony_ci#include <linux/vmalloc.h> 1062306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 1162306a36Sopenharmony_ci#include <media/v4l2-event.h> 1262306a36Sopenharmony_ci#include <media/v4l2-subdev.h> 1362306a36Sopenharmony_ci#include <media/tpg/v4l2-tpg.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "vimc-common.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cienum vimc_sensor_osd_mode { 1862306a36Sopenharmony_ci VIMC_SENSOR_OSD_SHOW_ALL = 0, 1962306a36Sopenharmony_ci VIMC_SENSOR_OSD_SHOW_COUNTERS = 1, 2062306a36Sopenharmony_ci VIMC_SENSOR_OSD_SHOW_NONE = 2 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct vimc_sensor_device { 2462306a36Sopenharmony_ci struct vimc_ent_device ved; 2562306a36Sopenharmony_ci struct v4l2_subdev sd; 2662306a36Sopenharmony_ci struct tpg_data tpg; 2762306a36Sopenharmony_ci u8 *frame; 2862306a36Sopenharmony_ci enum vimc_sensor_osd_mode osd_value; 2962306a36Sopenharmony_ci u64 start_stream_ts; 3062306a36Sopenharmony_ci /* The active format */ 3162306a36Sopenharmony_ci struct v4l2_mbus_framefmt mbus_format; 3262306a36Sopenharmony_ci struct v4l2_ctrl_handler hdl; 3362306a36Sopenharmony_ci struct media_pad pad; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic const struct v4l2_mbus_framefmt fmt_default = { 3762306a36Sopenharmony_ci .width = 640, 3862306a36Sopenharmony_ci .height = 480, 3962306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB888_1X24, 4062306a36Sopenharmony_ci .field = V4L2_FIELD_NONE, 4162306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic int vimc_sensor_init_cfg(struct v4l2_subdev *sd, 4562306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci unsigned int i; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci for (i = 0; i < sd->entity.num_pads; i++) { 5062306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci mf = v4l2_subdev_get_try_format(sd, sd_state, i); 5362306a36Sopenharmony_ci *mf = fmt_default; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic int vimc_sensor_enum_mbus_code(struct v4l2_subdev *sd, 6062306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 6162306a36Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci u32 mbus_code = vimc_mbus_code_by_index(code->index); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (!mbus_code) 6662306a36Sopenharmony_ci return -EINVAL; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci code->code = mbus_code; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return 0; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int vimc_sensor_enum_frame_size(struct v4l2_subdev *sd, 7462306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 7562306a36Sopenharmony_ci struct v4l2_subdev_frame_size_enum *fse) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci const struct vimc_pix_map *vpix; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (fse->index) 8062306a36Sopenharmony_ci return -EINVAL; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* Only accept code in the pix map table */ 8362306a36Sopenharmony_ci vpix = vimc_pix_map_by_code(fse->code); 8462306a36Sopenharmony_ci if (!vpix) 8562306a36Sopenharmony_ci return -EINVAL; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci fse->min_width = VIMC_FRAME_MIN_WIDTH; 8862306a36Sopenharmony_ci fse->max_width = VIMC_FRAME_MAX_WIDTH; 8962306a36Sopenharmony_ci fse->min_height = VIMC_FRAME_MIN_HEIGHT; 9062306a36Sopenharmony_ci fse->max_height = VIMC_FRAME_MAX_HEIGHT; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int vimc_sensor_get_fmt(struct v4l2_subdev *sd, 9662306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 9762306a36Sopenharmony_ci struct v4l2_subdev_format *fmt) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct vimc_sensor_device *vsensor = 10062306a36Sopenharmony_ci container_of(sd, struct vimc_sensor_device, sd); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci fmt->format = fmt->which == V4L2_SUBDEV_FORMAT_TRY ? 10362306a36Sopenharmony_ci *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) : 10462306a36Sopenharmony_ci vsensor->mbus_format; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void vimc_sensor_tpg_s_format(struct vimc_sensor_device *vsensor) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci const struct vimc_pix_map *vpix = 11262306a36Sopenharmony_ci vimc_pix_map_by_code(vsensor->mbus_format.code); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci tpg_reset_source(&vsensor->tpg, vsensor->mbus_format.width, 11562306a36Sopenharmony_ci vsensor->mbus_format.height, vsensor->mbus_format.field); 11662306a36Sopenharmony_ci tpg_s_bytesperline(&vsensor->tpg, 0, vsensor->mbus_format.width * vpix->bpp); 11762306a36Sopenharmony_ci tpg_s_buf_height(&vsensor->tpg, vsensor->mbus_format.height); 11862306a36Sopenharmony_ci tpg_s_fourcc(&vsensor->tpg, vpix->pixelformat); 11962306a36Sopenharmony_ci /* TODO: add support for V4L2_FIELD_ALTERNATE */ 12062306a36Sopenharmony_ci tpg_s_field(&vsensor->tpg, vsensor->mbus_format.field, false); 12162306a36Sopenharmony_ci tpg_s_colorspace(&vsensor->tpg, vsensor->mbus_format.colorspace); 12262306a36Sopenharmony_ci tpg_s_ycbcr_enc(&vsensor->tpg, vsensor->mbus_format.ycbcr_enc); 12362306a36Sopenharmony_ci tpg_s_quantization(&vsensor->tpg, vsensor->mbus_format.quantization); 12462306a36Sopenharmony_ci tpg_s_xfer_func(&vsensor->tpg, vsensor->mbus_format.xfer_func); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic void vimc_sensor_adjust_fmt(struct v4l2_mbus_framefmt *fmt) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci const struct vimc_pix_map *vpix; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* Only accept code in the pix map table */ 13262306a36Sopenharmony_ci vpix = vimc_pix_map_by_code(fmt->code); 13362306a36Sopenharmony_ci if (!vpix) 13462306a36Sopenharmony_ci fmt->code = fmt_default.code; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci fmt->width = clamp_t(u32, fmt->width, VIMC_FRAME_MIN_WIDTH, 13762306a36Sopenharmony_ci VIMC_FRAME_MAX_WIDTH) & ~1; 13862306a36Sopenharmony_ci fmt->height = clamp_t(u32, fmt->height, VIMC_FRAME_MIN_HEIGHT, 13962306a36Sopenharmony_ci VIMC_FRAME_MAX_HEIGHT) & ~1; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* TODO: add support for V4L2_FIELD_ALTERNATE */ 14262306a36Sopenharmony_ci if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE) 14362306a36Sopenharmony_ci fmt->field = fmt_default.field; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci vimc_colorimetry_clamp(fmt); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int vimc_sensor_set_fmt(struct v4l2_subdev *sd, 14962306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 15062306a36Sopenharmony_ci struct v4l2_subdev_format *fmt) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct vimc_sensor_device *vsensor = v4l2_get_subdevdata(sd); 15362306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { 15662306a36Sopenharmony_ci /* Do not change the format while stream is on */ 15762306a36Sopenharmony_ci if (vsensor->frame) 15862306a36Sopenharmony_ci return -EBUSY; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci mf = &vsensor->mbus_format; 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Set the new format */ 16662306a36Sopenharmony_ci vimc_sensor_adjust_fmt(&fmt->format); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci dev_dbg(vsensor->ved.dev, "%s: format update: " 16962306a36Sopenharmony_ci "old:%dx%d (0x%x, %d, %d, %d, %d) " 17062306a36Sopenharmony_ci "new:%dx%d (0x%x, %d, %d, %d, %d)\n", vsensor->sd.name, 17162306a36Sopenharmony_ci /* old */ 17262306a36Sopenharmony_ci mf->width, mf->height, mf->code, 17362306a36Sopenharmony_ci mf->colorspace, mf->quantization, 17462306a36Sopenharmony_ci mf->xfer_func, mf->ycbcr_enc, 17562306a36Sopenharmony_ci /* new */ 17662306a36Sopenharmony_ci fmt->format.width, fmt->format.height, fmt->format.code, 17762306a36Sopenharmony_ci fmt->format.colorspace, fmt->format.quantization, 17862306a36Sopenharmony_ci fmt->format.xfer_func, fmt->format.ycbcr_enc); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci *mf = fmt->format; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops vimc_sensor_pad_ops = { 18662306a36Sopenharmony_ci .init_cfg = vimc_sensor_init_cfg, 18762306a36Sopenharmony_ci .enum_mbus_code = vimc_sensor_enum_mbus_code, 18862306a36Sopenharmony_ci .enum_frame_size = vimc_sensor_enum_frame_size, 18962306a36Sopenharmony_ci .get_fmt = vimc_sensor_get_fmt, 19062306a36Sopenharmony_ci .set_fmt = vimc_sensor_set_fmt, 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic void *vimc_sensor_process_frame(struct vimc_ent_device *ved, 19462306a36Sopenharmony_ci const void *sink_frame) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct vimc_sensor_device *vsensor = 19762306a36Sopenharmony_ci container_of(ved, struct vimc_sensor_device, ved); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci const unsigned int line_height = 16; 20062306a36Sopenharmony_ci u8 *basep[TPG_MAX_PLANES][2]; 20162306a36Sopenharmony_ci unsigned int line = 1; 20262306a36Sopenharmony_ci char str[100]; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci tpg_fill_plane_buffer(&vsensor->tpg, 0, 0, vsensor->frame); 20562306a36Sopenharmony_ci tpg_calc_text_basep(&vsensor->tpg, basep, 0, vsensor->frame); 20662306a36Sopenharmony_ci switch (vsensor->osd_value) { 20762306a36Sopenharmony_ci case VIMC_SENSOR_OSD_SHOW_ALL: { 20862306a36Sopenharmony_ci const char *order = tpg_g_color_order(&vsensor->tpg); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 21162306a36Sopenharmony_ci 16, order); 21262306a36Sopenharmony_ci snprintf(str, sizeof(str), 21362306a36Sopenharmony_ci "brightness %3d, contrast %3d, saturation %3d, hue %d ", 21462306a36Sopenharmony_ci vsensor->tpg.brightness, 21562306a36Sopenharmony_ci vsensor->tpg.contrast, 21662306a36Sopenharmony_ci vsensor->tpg.saturation, 21762306a36Sopenharmony_ci vsensor->tpg.hue); 21862306a36Sopenharmony_ci tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, str); 21962306a36Sopenharmony_ci snprintf(str, sizeof(str), "sensor size: %dx%d", 22062306a36Sopenharmony_ci vsensor->mbus_format.width, 22162306a36Sopenharmony_ci vsensor->mbus_format.height); 22262306a36Sopenharmony_ci tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, str); 22362306a36Sopenharmony_ci fallthrough; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci case VIMC_SENSOR_OSD_SHOW_COUNTERS: { 22662306a36Sopenharmony_ci unsigned int ms; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci ms = div_u64(ktime_get_ns() - vsensor->start_stream_ts, 1000000); 22962306a36Sopenharmony_ci snprintf(str, sizeof(str), "%02d:%02d:%02d:%03d", 23062306a36Sopenharmony_ci (ms / (60 * 60 * 1000)) % 24, 23162306a36Sopenharmony_ci (ms / (60 * 1000)) % 60, 23262306a36Sopenharmony_ci (ms / 1000) % 60, 23362306a36Sopenharmony_ci ms % 1000); 23462306a36Sopenharmony_ci tpg_gen_text(&vsensor->tpg, basep, line++ * line_height, 16, str); 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci case VIMC_SENSOR_OSD_SHOW_NONE: 23862306a36Sopenharmony_ci default: 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return vsensor->frame; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic int vimc_sensor_s_stream(struct v4l2_subdev *sd, int enable) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct vimc_sensor_device *vsensor = 24862306a36Sopenharmony_ci container_of(sd, struct vimc_sensor_device, sd); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (enable) { 25162306a36Sopenharmony_ci const struct vimc_pix_map *vpix; 25262306a36Sopenharmony_ci unsigned int frame_size; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci vsensor->start_stream_ts = ktime_get_ns(); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* Calculate the frame size */ 25762306a36Sopenharmony_ci vpix = vimc_pix_map_by_code(vsensor->mbus_format.code); 25862306a36Sopenharmony_ci frame_size = vsensor->mbus_format.width * vpix->bpp * 25962306a36Sopenharmony_ci vsensor->mbus_format.height; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* 26262306a36Sopenharmony_ci * Allocate the frame buffer. Use vmalloc to be able to 26362306a36Sopenharmony_ci * allocate a large amount of memory 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ci vsensor->frame = vmalloc(frame_size); 26662306a36Sopenharmony_ci if (!vsensor->frame) 26762306a36Sopenharmony_ci return -ENOMEM; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* configure the test pattern generator */ 27062306a36Sopenharmony_ci vimc_sensor_tpg_s_format(vsensor); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci } else { 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci vfree(vsensor->frame); 27562306a36Sopenharmony_ci vsensor->frame = NULL; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic const struct v4l2_subdev_core_ops vimc_sensor_core_ops = { 28262306a36Sopenharmony_ci .log_status = v4l2_ctrl_subdev_log_status, 28362306a36Sopenharmony_ci .subscribe_event = v4l2_ctrl_subdev_subscribe_event, 28462306a36Sopenharmony_ci .unsubscribe_event = v4l2_event_subdev_unsubscribe, 28562306a36Sopenharmony_ci}; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops vimc_sensor_video_ops = { 28862306a36Sopenharmony_ci .s_stream = vimc_sensor_s_stream, 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic const struct v4l2_subdev_ops vimc_sensor_ops = { 29262306a36Sopenharmony_ci .core = &vimc_sensor_core_ops, 29362306a36Sopenharmony_ci .pad = &vimc_sensor_pad_ops, 29462306a36Sopenharmony_ci .video = &vimc_sensor_video_ops, 29562306a36Sopenharmony_ci}; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic int vimc_sensor_s_ctrl(struct v4l2_ctrl *ctrl) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct vimc_sensor_device *vsensor = 30062306a36Sopenharmony_ci container_of(ctrl->handler, struct vimc_sensor_device, hdl); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci switch (ctrl->id) { 30362306a36Sopenharmony_ci case VIMC_CID_TEST_PATTERN: 30462306a36Sopenharmony_ci tpg_s_pattern(&vsensor->tpg, ctrl->val); 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci case V4L2_CID_HFLIP: 30762306a36Sopenharmony_ci tpg_s_hflip(&vsensor->tpg, ctrl->val); 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci case V4L2_CID_VFLIP: 31062306a36Sopenharmony_ci tpg_s_vflip(&vsensor->tpg, ctrl->val); 31162306a36Sopenharmony_ci break; 31262306a36Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 31362306a36Sopenharmony_ci tpg_s_brightness(&vsensor->tpg, ctrl->val); 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci case V4L2_CID_CONTRAST: 31662306a36Sopenharmony_ci tpg_s_contrast(&vsensor->tpg, ctrl->val); 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci case V4L2_CID_HUE: 31962306a36Sopenharmony_ci tpg_s_hue(&vsensor->tpg, ctrl->val); 32062306a36Sopenharmony_ci break; 32162306a36Sopenharmony_ci case V4L2_CID_SATURATION: 32262306a36Sopenharmony_ci tpg_s_saturation(&vsensor->tpg, ctrl->val); 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci case VIMC_CID_OSD_TEXT_MODE: 32562306a36Sopenharmony_ci vsensor->osd_value = ctrl->val; 32662306a36Sopenharmony_ci break; 32762306a36Sopenharmony_ci default: 32862306a36Sopenharmony_ci return -EINVAL; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops vimc_sensor_ctrl_ops = { 33462306a36Sopenharmony_ci .s_ctrl = vimc_sensor_s_ctrl, 33562306a36Sopenharmony_ci}; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void vimc_sensor_release(struct vimc_ent_device *ved) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct vimc_sensor_device *vsensor = 34062306a36Sopenharmony_ci container_of(ved, struct vimc_sensor_device, ved); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci v4l2_ctrl_handler_free(&vsensor->hdl); 34362306a36Sopenharmony_ci tpg_free(&vsensor->tpg); 34462306a36Sopenharmony_ci media_entity_cleanup(vsensor->ved.ent); 34562306a36Sopenharmony_ci kfree(vsensor); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/* Image Processing Controls */ 34962306a36Sopenharmony_cistatic const struct v4l2_ctrl_config vimc_sensor_ctrl_class = { 35062306a36Sopenharmony_ci .flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY, 35162306a36Sopenharmony_ci .id = VIMC_CID_VIMC_CLASS, 35262306a36Sopenharmony_ci .name = "VIMC Controls", 35362306a36Sopenharmony_ci .type = V4L2_CTRL_TYPE_CTRL_CLASS, 35462306a36Sopenharmony_ci}; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic const struct v4l2_ctrl_config vimc_sensor_ctrl_test_pattern = { 35762306a36Sopenharmony_ci .ops = &vimc_sensor_ctrl_ops, 35862306a36Sopenharmony_ci .id = VIMC_CID_TEST_PATTERN, 35962306a36Sopenharmony_ci .name = "Test Pattern", 36062306a36Sopenharmony_ci .type = V4L2_CTRL_TYPE_MENU, 36162306a36Sopenharmony_ci .max = TPG_PAT_NOISE, 36262306a36Sopenharmony_ci .qmenu = tpg_pattern_strings, 36362306a36Sopenharmony_ci}; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic const char * const vimc_ctrl_osd_mode_strings[] = { 36662306a36Sopenharmony_ci "All", 36762306a36Sopenharmony_ci "Counters Only", 36862306a36Sopenharmony_ci "None", 36962306a36Sopenharmony_ci NULL, 37062306a36Sopenharmony_ci}; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic const struct v4l2_ctrl_config vimc_sensor_ctrl_osd_mode = { 37362306a36Sopenharmony_ci .ops = &vimc_sensor_ctrl_ops, 37462306a36Sopenharmony_ci .id = VIMC_CID_OSD_TEXT_MODE, 37562306a36Sopenharmony_ci .name = "Show Information", 37662306a36Sopenharmony_ci .type = V4L2_CTRL_TYPE_MENU, 37762306a36Sopenharmony_ci .max = ARRAY_SIZE(vimc_ctrl_osd_mode_strings) - 2, 37862306a36Sopenharmony_ci .qmenu = vimc_ctrl_osd_mode_strings, 37962306a36Sopenharmony_ci}; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc, 38262306a36Sopenharmony_ci const char *vcfg_name) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct v4l2_device *v4l2_dev = &vimc->v4l2_dev; 38562306a36Sopenharmony_ci struct vimc_sensor_device *vsensor; 38662306a36Sopenharmony_ci int ret; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Allocate the vsensor struct */ 38962306a36Sopenharmony_ci vsensor = kzalloc(sizeof(*vsensor), GFP_KERNEL); 39062306a36Sopenharmony_ci if (!vsensor) 39162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci v4l2_ctrl_handler_init(&vsensor->hdl, 4); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci v4l2_ctrl_new_custom(&vsensor->hdl, &vimc_sensor_ctrl_class, NULL); 39662306a36Sopenharmony_ci v4l2_ctrl_new_custom(&vsensor->hdl, &vimc_sensor_ctrl_test_pattern, NULL); 39762306a36Sopenharmony_ci v4l2_ctrl_new_custom(&vsensor->hdl, &vimc_sensor_ctrl_osd_mode, NULL); 39862306a36Sopenharmony_ci v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, 39962306a36Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 0); 40062306a36Sopenharmony_ci v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, 40162306a36Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 0); 40262306a36Sopenharmony_ci v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, 40362306a36Sopenharmony_ci V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); 40462306a36Sopenharmony_ci v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, 40562306a36Sopenharmony_ci V4L2_CID_CONTRAST, 0, 255, 1, 128); 40662306a36Sopenharmony_ci v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, 40762306a36Sopenharmony_ci V4L2_CID_HUE, -128, 127, 1, 0); 40862306a36Sopenharmony_ci v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops, 40962306a36Sopenharmony_ci V4L2_CID_SATURATION, 0, 255, 1, 128); 41062306a36Sopenharmony_ci vsensor->sd.ctrl_handler = &vsensor->hdl; 41162306a36Sopenharmony_ci if (vsensor->hdl.error) { 41262306a36Sopenharmony_ci ret = vsensor->hdl.error; 41362306a36Sopenharmony_ci goto err_free_vsensor; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* Initialize the test pattern generator */ 41762306a36Sopenharmony_ci tpg_init(&vsensor->tpg, vsensor->mbus_format.width, 41862306a36Sopenharmony_ci vsensor->mbus_format.height); 41962306a36Sopenharmony_ci ret = tpg_alloc(&vsensor->tpg, VIMC_FRAME_MAX_WIDTH); 42062306a36Sopenharmony_ci if (ret) 42162306a36Sopenharmony_ci goto err_free_hdl; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* Initialize ved and sd */ 42462306a36Sopenharmony_ci vsensor->pad.flags = MEDIA_PAD_FL_SOURCE; 42562306a36Sopenharmony_ci ret = vimc_ent_sd_register(&vsensor->ved, &vsensor->sd, v4l2_dev, 42662306a36Sopenharmony_ci vcfg_name, 42762306a36Sopenharmony_ci MEDIA_ENT_F_CAM_SENSOR, 1, &vsensor->pad, 42862306a36Sopenharmony_ci &vimc_sensor_ops); 42962306a36Sopenharmony_ci if (ret) 43062306a36Sopenharmony_ci goto err_free_tpg; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci vsensor->ved.process_frame = vimc_sensor_process_frame; 43362306a36Sopenharmony_ci vsensor->ved.dev = vimc->mdev.dev; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* Initialize the frame format */ 43662306a36Sopenharmony_ci vsensor->mbus_format = fmt_default; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return &vsensor->ved; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cierr_free_tpg: 44162306a36Sopenharmony_ci tpg_free(&vsensor->tpg); 44262306a36Sopenharmony_cierr_free_hdl: 44362306a36Sopenharmony_ci v4l2_ctrl_handler_free(&vsensor->hdl); 44462306a36Sopenharmony_cierr_free_vsensor: 44562306a36Sopenharmony_ci kfree(vsensor); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return ERR_PTR(ret); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistruct vimc_ent_type vimc_sensor_type = { 45162306a36Sopenharmony_ci .add = vimc_sensor_add, 45262306a36Sopenharmony_ci .release = vimc_sensor_release 45362306a36Sopenharmony_ci}; 454