diff options
author | Beata Michalska <b.michalska@samsung.com> | 2014-07-25 16:45:58 +0200 |
---|---|---|
committer | Sylwester Nawrocki <s.nawrocki@samsung.com> | 2014-11-27 03:43:14 -0800 |
commit | 06975230d3a600d1e3a21747aef004d6583decc1 (patch) | |
tree | e8cb49b0fbd9e7319e49f63f795420da4c216a71 | |
parent | cd6e2e406309601c9452f5132b5b8e544aa2a37c (diff) | |
download | linux-3.10-06975230d3a600d1e3a21747aef004d6583decc1.tar.gz linux-3.10-06975230d3a600d1e3a21747aef004d6583decc1.tar.bz2 linux-3.10-06975230d3a600d1e3a21747aef004d6583decc1.zip |
exynos: fimc-is: Support for V4L2 controls
Add support for FIMC ISP and SCALERC v4l2 controls
Change-Id: I360597cbaed60a24f3184df3c5617f868fd02749
Signed-off-by: Beata Michalska <b.michalska@samsung.com>
-rw-r--r-- | drivers/media/platform/exynos5-is/fimc-is-fw.c | 127 | ||||
-rw-r--r-- | drivers/media/platform/exynos5-is/fimc-is-fw.h | 34 | ||||
-rw-r--r-- | drivers/media/platform/exynos5-is/fimc-is-isp.c | 273 | ||||
-rw-r--r-- | drivers/media/platform/exynos5-is/fimc-is-isp.h | 29 | ||||
-rw-r--r-- | drivers/media/platform/exynos5-is/fimc-is-param.h | 4 | ||||
-rw-r--r-- | drivers/media/platform/exynos5-is/fimc-is-pipeline.c | 68 | ||||
-rw-r--r-- | drivers/media/platform/exynos5-is/fimc-is-pipeline.h | 5 | ||||
-rw-r--r-- | drivers/media/platform/exynos5-is/fimc-is-scaler.c | 187 | ||||
-rw-r--r-- | drivers/media/platform/exynos5-is/fimc-is-scaler.h | 14 |
9 files changed, 669 insertions, 72 deletions
diff --git a/drivers/media/platform/exynos5-is/fimc-is-fw.c b/drivers/media/platform/exynos5-is/fimc-is-fw.c index acd5a781a14..a76bc050235 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-fw.c +++ b/drivers/media/platform/exynos5-is/fimc-is-fw.c @@ -297,6 +297,25 @@ static const struct fimc_is_fw_mem fimc_is_fw_130_mem = { }; /** + * @brief FIMC-IS FW supported cam controls + */ +static const unsigned long metadata2_ctrl[] = { + /* FIMC_IS_CID_BRIGHTNESS */ 0x00a8, + /* FIMC_IS_CID_CONTRAST */ 0x00ac, + /* FIMC_IS_CID_SATURATION */ 0x00a4, + /* FIMC_IS_CID_HUE */ 0x00a0, + /* FIMC_IS_CID_AWB */ 0x0458, + /* FIMC_IS_CID_EXPOSURE] */ 0x0038, + /* FIMC_IS_CID_FOCUS_MODE */ 0x0470, + /* FIMC_IS_CID_FOCUS_RANGE */ 0x0020, + /* FIMC_IS_CID_AWB_MODE */ 0x0458, + /* FIMC_IS_CID_ISO */ 0x0048, + /* FIMC_IS_CID_ISO_MODE */ 0x3818, + /* FIMC_IS_CID_SCENE */ 0x0424, + /* FIMC_IS_CID_COLOR_MODE */ 0x0078, +}; + +/** * @brief Get the memory layout for given FIMC-IS firmware version */ static int fimc_is_fw_130_mem_cfg(struct fimc_is_fw_mem *memcfg) @@ -388,65 +407,67 @@ static int fimc_is_fw_130_tweak_params(unsigned int type, void* base_addr, void */ static int fimc_is_fw_130_config_shot(void * addr) { - struct camera2_shot *shot = (struct camera2_shot*)addr; - if (!addr) - return -EINVAL; - /* CAM CONTROL */ - /* REQUEST */ - shot->ctl.request.base.id = 1; - shot->ctl.request.base.metadatamode = METADATA_MODE_FULL; - shot->ctl.request.base.framecount = 0; - /* LENS */ - shot->ctl.lens.optical_stabilization_mode = OPTICAL_STABILIZATION_MODE_ON; - shot->ctl.lens.aperture = 1; - /* SENSOR */ - shot->ctl.sensor.exposure_time = 150000000; - shot->ctl.sensor.sensitivity = 100; - /* FLASH */ - /* HOTPIXEL */ - shot->ctl.hotpixel.mode = PROCESSING_MODE_HIGH_QUALITY; - /* DEMOSAIC*/ - shot->ctl.demosaic.mode = PROCESSING_MODE_HIGH_QUALITY; - /* NOISE */ - shot->ctl.noise.mode = PROCESSING_MODE_HIGH_QUALITY; - shot->ctl.noise.strength = 1; + struct camera2_shot *shot = (struct camera2_shot *)addr; + + if (!addr) + return -EINVAL; + /* REQUEST */ + shot->ctl.request.base.id = 1; + shot->ctl.request.base.metadatamode = METADATA_MODE_FULL; + shot->ctl.request.base.framecount = 0; + /* LENS */ + shot->ctl.lens.optical_stabilization_mode = OPTICAL_STABILIZATION_MODE_ON; + shot->ctl.lens.aperture = 1; + /* HOTPIXEL */ + shot->ctl.hotpixel.mode = PROCESSING_MODE_HIGH_QUALITY; + /* DEMOSAIC*/ + shot->ctl.demosaic.mode = PROCESSING_MODE_HIGH_QUALITY; + /* NOISE */ + shot->ctl.noise.mode = PROCESSING_MODE_HIGH_QUALITY; + shot->ctl.noise.strength = 1; /* SHADING */ - shot->ctl.shading.mode = PROCESSING_MODE_HIGH_QUALITY; - /* GEOMETRIC */ - /* COLOR */ - shot->ctl.color.base.mode = COLOR_CORRECTION_MODE_HIGH_QUALITY; - shot->ctl.color.base.saturation = 1; - /* TONEMAP */ - /* EDGE */ - /* SCALER */ - /* JPEG */ - /* STATS */ - /* AA */ - shot->ctl.aa.capture_intent = AA_CAPTURE_INTENT_PREVIEW; - shot->ctl.aa.mode = AA_CONTROL_AUTO; - shot->ctl.aa.ae_mode = AA_AEMODE_ON; - - shot->dm.aa.iso_mode = AA_ISOMODE_MANUAL; - shot->dm.aa.iso_value = 100; - shot->ctl.aa.ae_anti_banding_mode = AA_AE_ANTIBANDING_AUTO; - shot->ctl.aa.ae_flash_mode = AA_FLASHMODE_ON; - shot->ctl.aa.awb_mode = AA_AWBMODE_WB_AUTO; - shot->ctl.aa.af_mode = AA_AFMODE_OFF; - shot->ctl.aa.ae_target_fps_range[0] = 15; - shot->ctl.aa.ae_target_fps_range[1] = 30; - /* USER CONTROL */ - shot->uctl.u_update_bitmap = 1 /* SENSOR */ | (1<<1) /* FLASH*/; - shot->uctl.flash_ud.ctl.flash_mode = CAM2_FLASH_MODE_SINGLE; + shot->ctl.shading.mode = PROCESSING_MODE_HIGH_QUALITY; + /* GEOMETRIC */ + /* COLOR */ + shot->ctl.color.base.mode = COLOR_CORRECTION_MODE_HIGH_QUALITY; + shot->ctl.color.base.saturation = 1; + /* AA */ + shot->ctl.aa.capture_intent = AA_CAPTURE_INTENT_PREVIEW; + shot->ctl.aa.mode = AA_CONTROL_AUTO; + shot->ctl.aa.ae_mode = AA_AEMODE_ON; + + shot->ctl.aa.ae_anti_banding_mode = AA_AE_ANTIBANDING_AUTO; + + shot->ctl.aa.ae_flash_mode = AA_FLASHMODE_ON; + shot->ctl.aa.ae_target_fps_range[0] = 15; + shot->ctl.aa.ae_target_fps_range[1] = 30; + /* USER CONTROL */ + shot->uctl.u_update_bitmap = 1 /* SENSOR */ | (1<<1) /* FLASH*/; + shot->uctl.flash_ud.ctl.flash_mode = CAM2_FLASH_MODE_SINGLE; + + shot->magicnumber = FIMC_IS_MAGIC_NUMBER; + + return 0; +} - shot->magicnumber = FIMC_IS_MAGIC_NUMBER; +static int fimc_is_fw_130_set_control(void *base_addr, unsigned int id, + unsigned long val) +{ + unsigned long *ctrl; - return 0; + if (!base_addr || id >= FIMC_IS_CID_MAX) + return -EINVAL; + + ctrl = (unsigned long *)((char *)base_addr + metadata2_ctrl[id]); + *ctrl = val; + return 0; } static const struct fimc_is_fw_ops fimc_is_fw_130_ops = { - .mem_config = fimc_is_fw_130_mem_cfg, - .tweak_param = fimc_is_fw_130_tweak_params, - .config_shot = fimc_is_fw_130_config_shot, + .mem_config = fimc_is_fw_130_mem_cfg, + .tweak_param = fimc_is_fw_130_tweak_params, + .config_shot = fimc_is_fw_130_config_shot, + .set_control = fimc_is_fw_130_set_control, }; /** @} */ /* End of FIMC_IS_FW_V130_SUPPORT */ diff --git a/drivers/media/platform/exynos5-is/fimc-is-fw.h b/drivers/media/platform/exynos5-is/fimc-is-fw.h index fb289d32f46..7f81da5ade9 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-fw.h +++ b/drivers/media/platform/exynos5-is/fimc-is-fw.h @@ -43,16 +43,38 @@ struct fimc_is_fw_mem { unsigned int meta_type; }; +/* + * Set of supported controls + */ +enum { + FIMC_IS_CID_BRIGHTNESS, /* [0..5] */ + FIMC_IS_CID_CONTRAST, /* [0..5] */ + FIMC_IS_CID_SATURATION, /* [0..5] */ + FIMC_IS_CID_HUE, /* [0..5] */ + FIMC_IS_CID_AWB, /* [ON/OFF] */ + FIMC_IS_CID_EXPOSURE, /* Absolute value */ + FIMC_IS_CID_FOCUS_MODE, /* Auto [ON/OFF] */ + FIMC_IS_CID_FOCUS_RANGE,/* [MACRO/NORMAL] */ + FIMC_IS_CID_AWB_MODE, /* @see: V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE */ + FIMC_IS_CID_ISO, /* [80/100/200/400/800] */ + FIMC_IS_CID_ISO_MODE, /* [AUTO/MANUAL */ + FIMC_IS_CID_SCENE, /* @see: V4L2_CID_SCENE_MODE */ + FIMC_IS_CID_COLOR_MODE, + FIMC_IS_CID_MAX +}; + /** * @brief FIMC-IS supported firmware-specific operations */ struct fimc_is_fw_ops { - /* Get firmware specific memory layout information */ - int (*mem_config) (struct fimc_is_fw_mem*); - /* Tweak FIMC-IS subblock parameters specific for given firmware */ - int (*tweak_param) (unsigned int, void *, void*); - /* Setup initial shot configuration */ - int (*config_shot) (void*); + /* Get firmware specific memory layout information */ + int (*mem_config)(struct fimc_is_fw_mem *); + /* Tweak FIMC-IS subblock parameters specific for given firmware */ + int (*tweak_param)(unsigned int, void *, void *); + /* Setup initial shot configuration */ + int (*config_shot)(void *); + /* Set individual controls for camera shot */ + int (*set_control)(void *, unsigned int, unsigned long); }; extern const struct fimc_is_fw_ops *fimc_is_fw_ops; diff --git a/drivers/media/platform/exynos5-is/fimc-is-isp.c b/drivers/media/platform/exynos5-is/fimc-is-isp.c index 082fe7db53b..9c637f78eba 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-isp.c +++ b/drivers/media/platform/exynos5-is/fimc-is-isp.c @@ -13,6 +13,7 @@ #include <media/videobuf2-dma-contig.h> #include "fimc-is.h" +#include "fimc-is-metadata.h" #define ISP_DRV_NAME "fimc-is-isp" @@ -38,6 +39,8 @@ static const struct fimc_is_fmt formats[] = { }; #define NUM_FORMATS ARRAY_SIZE(formats) +#define ISP_VIDEO_NUM_CTRL FIMC_IS_CID_MAX + static const struct fimc_is_fmt *find_format(struct v4l2_format *f) { unsigned int i; @@ -356,6 +359,7 @@ static int isp_subdev_registered(struct v4l2_subdev *sd) int ret; mutex_init(&isp->video_lock); + mutex_init(&isp->lock); memset(vfd, 0, sizeof(*vfd)); snprintf(vfd->name, sizeof(vfd->name), "fimc-is-isp.output"); @@ -394,7 +398,6 @@ static int isp_subdev_registered(struct v4l2_subdev *sd) media_entity_cleanup(&vfd->entity); return ret; } - v4l2_info(sd->v4l2_dev, "Registered %s as /dev/%s\n", vfd->name, video_device_node_name(vfd)); return 0; @@ -446,8 +449,14 @@ static int isp_s_power(struct v4l2_subdev *sd, int on) sensor = fimc_is_get_sensor(is, sdata->id); ret = fimc_is_pipeline_open(isp->pipeline, sensor); - if (ret) + if (ret) { v4l2_err(&isp->subdev, "Pipeline open failed\n"); + } else { + ret = v4l2_ctrl_handler_setup(&isp->ctrls.handler); + if (ret) + v4l2_err(&isp->subdev, "Failed to setup controls\n"); + } + } else { ret = fimc_is_pipeline_close(isp->pipeline); if (ret) @@ -523,6 +532,257 @@ static const struct v4l2_subdev_video_ops isp_video_ops = { .s_stream = isp_s_stream, }; +static const s64 isp_video_iso_qmenu[] = { + 80, 100, 200, 400, 800, +}; + +static inline int isp_video_set_color_ctrl(struct fimc_is_isp *isp, + unsigned int id, + unsigned long val) +{ + /* + * @TODO: Change it to queueing the requestes and setting in at one go + * instead of making function call per each control + */ + return fimc_is_pipeline_update_shot_ctrl(isp->pipeline, + id, val); +} + +static inline int isp_video_set_sensor_ctrl(struct fimc_is_isp *isp, + unsigned int id, + unsigned long val) +{ + /* @TODO: Make some reasonable adjustment between the settings */ + switch (id) { + case FIMC_IS_CID_EXPOSURE: + val *= 1000000; + break; + case FIMC_IS_CID_ISO: + val = isp_video_iso_qmenu[val]; + break; + case FIMC_IS_CID_ISO_MODE: + val = val ? AA_ISOMODE_AUTO : AA_ISOMODE_MANUAL; + break; + } + return fimc_is_pipeline_update_shot_ctrl(isp->pipeline, + id, val); +} + +static const unsigned int awb_mode_map[] = { + [V4L2_WHITE_BALANCE_MANUAL] = AA_AWBMODE_OFF, + [V4L2_WHITE_BALANCE_AUTO] = AA_AWBMODE_WB_AUTO, + [V4L2_WHITE_BALANCE_INCANDESCENT] = AA_AWBMODE_WB_INCANDESCENT, + [V4L2_WHITE_BALANCE_FLUORESCENT] = AA_AWBMODE_WB_FLUORESCENT, + [V4L2_WHITE_BALANCE_FLUORESCENT_H] = AA_AWBMODE_WB_WARM_FLUORESCENT, + [V4L2_WHITE_BALANCE_DAYLIGHT] = AA_AWBMODE_WB_DAYLIGHT, + [V4L2_WHITE_BALANCE_CLOUDY] = AA_AWBMODE_WB_CLOUDY_DAYLIGHT, + [V4L2_WHITE_BALANCE_SHADE] = AA_AWBMODE_WB_SHADE, +}; + +static inline int isp_video_set_awb_ctrl(struct fimc_is_isp *isp, + unsigned int id, + unsigned long val) +{ + if (id == FIMC_IS_CID_AWB) + val = val ? AA_AWBMODE_WB_AUTO : AA_AWBMODE_OFF; + else { + /* Boundary check should be performed by v4l2 */ + if (val >= ARRAY_SIZE(awb_mode_map)) + return -EINVAL; + val = awb_mode_map[val]; + if (!val) + val = AA_AWBMODE_WB_AUTO; + } + + return fimc_is_pipeline_update_shot_ctrl(isp->pipeline, + id, val); +} + +static inline int isp_video_set_focus_ctrl(struct fimc_is_isp *isp, + unsigned int id, + unsigned long val) +{ + if (id == FIMC_IS_CID_FOCUS_MODE) + val = val ? AA_AFMODE_AUTO : AA_AFMODE_OFF; + + else + val = (val == V4L2_AUTO_FOCUS_RANGE_NORMAL) + ? AA_AFMODE_CONTINUOUS_PICTURE + : AA_AFMODE_MACRO; + + return fimc_is_pipeline_update_shot_ctrl(isp->pipeline, + id, val); + +} + +static const unsigned int scene_map[] = { + [V4L2_SCENE_MODE_NONE] = AA_SCENE_MODE_UNSUPPORTED, + [V4L2_SCENE_MODE_BEACH_SNOW] = AA_SCENE_MODE_BEACH, + [V4L2_SCENE_MODE_CANDLE_LIGHT] = AA_SCENE_MODE_CANDLELIGHT, + [V4L2_SCENE_MODE_FIREWORKS] = AA_SCENE_MODE_FIREWORKS, + [V4L2_SCENE_MODE_LANDSCAPE] = AA_SCENE_MODE_LANDSCAPE, + [V4L2_SCENE_MODE_NIGHT] = AA_SCENE_MODE_NIGHT, + [V4L2_SCENE_MODE_PARTY_INDOOR] = AA_SCENE_MODE_PARTY, + [V4L2_SCENE_MODE_PORTRAIT] = AA_SCENE_MODE_PORTRAIT, + [V4L2_SCENE_MODE_SPORTS] = AA_SCENE_MODE_SPORTS, + [V4L2_SCENE_MODE_SUNSET] = AA_SCENE_MODE_SUNSET, +}; + +#define ISP_SCENE_MODE_SKIP_MASK \ + (1 << (V4L2_SCENE_MODE_BACKLIGHT) | \ + 1 << (V4L2_SCENE_MODE_DAWN_DUSK) | \ + 1 << (V4L2_SCENE_MODE_FALL_COLORS) | \ + 1 << (V4L2_SCENE_MODE_TEXT )) + +static inline int isp_video_set_scene_ctrl(struct fimc_is_isp *isp, + unsigned long val) +{ + /* Boundary check should be performed by v4l2 */ + if (val >= ARRAY_SIZE(scene_map)) + return -EINVAL; + + val = scene_map[val]; + if (!val) + val = AA_SCENE_MODE_UNSUPPORTED; + /* @TODO: Set remaining controls based on scene chosen*/ + return fimc_is_pipeline_update_shot_ctrl(isp->pipeline, + FIMC_IS_CID_SCENE, val); + +} + +static int isp_video_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct fimc_is_isp *isp = container_of(ctrl->handler, + struct fimc_is_isp, + ctrls.handler); + int ret = 0; + + mutex_lock(&isp->lock); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + isp_video_set_color_ctrl(isp, FIMC_IS_CID_BRIGHTNESS, + ctrl->val); + break; + case V4L2_CID_CONTRAST: + isp_video_set_color_ctrl(isp, FIMC_IS_CID_CONTRAST, + ctrl->val); + break; + case V4L2_CID_SATURATION: + isp_video_set_color_ctrl(isp, FIMC_IS_CID_SATURATION, + ctrl->val); + break; + case V4L2_CID_HUE: + isp_video_set_color_ctrl(isp, FIMC_IS_CID_HUE, + ctrl->val); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + isp_video_set_awb_ctrl(isp, FIMC_IS_CID_AWB, + ctrl->val); + break; + case V4L2_CID_EXPOSURE_ABSOLUTE: + isp_video_set_sensor_ctrl(isp, FIMC_IS_CID_EXPOSURE, + ctrl->val); + break; + case V4L2_CID_FOCUS_AUTO: + isp_video_set_focus_ctrl(isp, FIMC_IS_CID_FOCUS_MODE, + ctrl->val); + break; + case V4L2_CID_AUTO_FOCUS_RANGE: + isp_video_set_focus_ctrl(isp, FIMC_IS_CID_FOCUS_RANGE, + ctrl->val); + break; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + isp_video_set_awb_ctrl(isp, FIMC_IS_CID_AWB_MODE, + ctrl->val); + break; + case V4L2_CID_ISO_SENSITIVITY: + isp_video_set_sensor_ctrl(isp, FIMC_IS_CID_ISO, + ctrl->val); + break; + case V4L2_CID_ISO_SENSITIVITY_AUTO: + isp_video_set_sensor_ctrl(isp, FIMC_IS_CID_ISO_MODE, + ctrl->val); + break; + case V4L2_CID_SCENE_MODE: + isp_video_set_scene_ctrl(isp, ctrl->val); + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&isp->lock); + + return ret; +} + +static const struct v4l2_ctrl_ops isp_video_ctrl_ops = { + .s_ctrl = isp_video_s_ctrl, +}; + +static int isp_video_create_controls(struct fimc_is_isp *isp) +{ + + struct v4l2_ctrl_handler *handler = &isp->ctrls.handler; + const struct v4l2_ctrl_ops *ops = &isp_video_ctrl_ops; + struct fimc_is_isp_ctrls *ctrls = &isp->ctrls; + + /* Color controls cluster: brightness */ + ctrls->brightness = v4l2_ctrl_new_std(handler, ops, + V4L2_CID_BRIGHTNESS, 0, 5, 1, 0); + /* Color controls cluster: contrast */ + ctrls->contrast = v4l2_ctrl_new_std(handler, ops, + V4L2_CID_CONTRAST, 0, 5, 1, 3); + /* Color controls cluster : saturation */ + ctrls->saturation = v4l2_ctrl_new_std(handler, ops, + V4L2_CID_SATURATION, 0, 5, 1, 1); + /* Color controls cluster: hue */ + ctrls->hue = v4l2_ctrl_new_std(handler, ops, + V4L2_CID_HUE, 0, 5, 1, 0); + /* White balance control cluster: state */ + ctrls->awb = v4l2_ctrl_new_std(handler, ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + /* Exposure absolute time control */ + ctrls->exposure = v4l2_ctrl_new_std(handler, ops, + V4L2_CID_EXPOSURE_ABSOLUTE, 4, 1800, 1, 200); + /* Focus controls cluster: focus mode */ + ctrls->focus = v4l2_ctrl_new_std(handler, ops, + V4L2_CID_FOCUS_AUTO, 0, 1, 1, 0); + /* Focus controls cluster: focus range */ + ctrls->focus_range = v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_AUTO_FOCUS_RANGE, + V4L2_AUTO_FOCUS_RANGE_MACRO, + ~(1 << V4L2_AUTO_FOCUS_RANGE_NORMAL | + 1 << V4L2_AUTO_FOCUS_RANGE_MACRO), + V4L2_AUTO_FOCUS_RANGE_NORMAL); + /* White balance control cluster : mode */ + ctrls->awb_mode = v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + V4L2_WHITE_BALANCE_SHADE, + 1 << V4L2_WHITE_BALANCE_HORIZON | + 1 << V4L2_WHITE_BALANCE_FLASH, + V4L2_WHITE_BALANCE_AUTO); + /* ISO controls cluster: value */ + ctrls->iso = v4l2_ctrl_new_int_menu(handler, ops, + V4L2_CID_ISO_SENSITIVITY, + ARRAY_SIZE(isp_video_iso_qmenu) - 1, + ARRAY_SIZE(isp_video_iso_qmenu)/2 - 1, + isp_video_iso_qmenu); + /* ISO controls cluster: mode */ + ctrls->iso_mode = v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_ISO_SENSITIVITY_AUTO, + 0, 0, 0); + + /* Scene mode control */ + ctrls->scene_mode = v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_SCENE_MODE, + V4L2_SCENE_MODE_SUNSET, + ISP_SCENE_MODE_SKIP_MASK, + V4L2_SCENE_MODE_NONE); + return handler->error; +} + static struct v4l2_subdev_ops isp_subdev_ops = { .core = &isp_core_ops, .video = &isp_video_ops, @@ -532,7 +792,7 @@ int fimc_is_isp_subdev_create(struct fimc_is_isp *isp, struct vb2_alloc_ctx *alloc_ctx, struct fimc_is_pipeline *pipeline) { - struct v4l2_ctrl_handler *handler = &isp->ctrl_handler; + struct v4l2_ctrl_handler *handler = &isp->ctrls.handler; struct v4l2_subdev *sd = &isp->subdev; int ret; @@ -557,9 +817,12 @@ int fimc_is_isp_subdev_create(struct fimc_is_isp *isp, if (ret < 0) return ret; - ret = v4l2_ctrl_handler_init(handler, 1); + ret = v4l2_ctrl_handler_init(handler, ISP_VIDEO_NUM_CTRL); if (handler->error) goto err_ctrl; + ret = isp_video_create_controls(isp); + if (ret) + goto err_ctrl; sd->ctrl_handler = handler; sd->internal_ops = &isp_subdev_internal_ops; @@ -579,6 +842,6 @@ void fimc_is_isp_subdev_destroy(struct fimc_is_isp *isp) v4l2_device_unregister_subdev(sd); media_entity_cleanup(&sd->entity); - v4l2_ctrl_handler_free(&isp->ctrl_handler); + v4l2_ctrl_handler_free(&isp->ctrls.handler); v4l2_set_subdevdata(sd, NULL); } diff --git a/drivers/media/platform/exynos5-is/fimc-is-isp.h b/drivers/media/platform/exynos5-is/fimc-is-isp.h index d7156872f7c..f435a20bf9b 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-isp.h +++ b/drivers/media/platform/exynos5-is/fimc-is-isp.h @@ -31,6 +31,32 @@ #define ISP_MAX_BUFS 2 /** + * struct fimc_is_isp_ctrls - FIMC ISP controls + */ +struct fimc_is_isp_ctrls { + struct v4l2_ctrl_handler handler; + + /* Color controls cluster */ + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *hue; + /* White balance controls cluster */ + struct v4l2_ctrl *awb; + struct v4l2_ctrl *awb_mode; + /* Sensitivity controls cluster */ + struct v4l2_ctrl *iso; + struct v4l2_ctrl *iso_mode; + /* Exposure time control */ + struct v4l2_ctrl *exposure; + /* Focus control */ + struct v4l2_ctrl *focus; + struct v4l2_ctrl *focus_range; + /* Scene mode control */ + struct v4l2_ctrl *scene_mode; +}; + +/** * struct fimc_is_isp - ISP context * @vfd: video device node * @fh: v4l2 file handle @@ -62,8 +88,9 @@ struct fimc_is_isp { struct v4l2_subdev subdev; struct media_pad vd_pad; struct media_pad subdev_pads[ISP_SD_PADS_NUM]; - struct v4l2_ctrl_handler ctrl_handler; + struct fimc_is_isp_ctrls ctrls; struct mutex video_lock; + struct mutex lock; struct v4l2_subdev *sensor_sd; struct fimc_is_pipeline *pipeline; diff --git a/drivers/media/platform/exynos5-is/fimc-is-param.h b/drivers/media/platform/exynos5-is/fimc-is-param.h index 3aaea6caec1..728d14cc6f1 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-param.h +++ b/drivers/media/platform/exynos5-is/fimc-is-param.h @@ -528,8 +528,8 @@ enum scaler_scaling_error { }; enum scaler_rotation_command { - SCALER_ROTATION_COMMAND_DISABLE = 0, - SCALER_ROTATION_COMMAND_CLOCKWISE90 = 1 + SCALER_ROTATION_CMD_DISABLE = 0, + SCALER_ROTATION_CMD_CLOCKWISE90 = 1 }; enum scaler_rotation_error { diff --git a/drivers/media/platform/exynos5-is/fimc-is-pipeline.c b/drivers/media/platform/exynos5-is/fimc-is-pipeline.c index be9623644f8..b7882923ef4 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-pipeline.c +++ b/drivers/media/platform/exynos5-is/fimc-is-pipeline.c @@ -1311,6 +1311,60 @@ static void fimc_is_pipeline_scalerc_validate(struct fimc_is_pipeline* pipeline, } } +int fimc_is_pipeline_set_scaler_effect(struct fimc_is_pipeline *pipeline, + unsigned int rotation) +{ + struct fimc_is *is = pipeline->is; + struct scalerp_param *scaler_param = + &pipeline->is_region->parameter.scalerp; + unsigned long index[2] = {0}; + unsigned int paused = 0; + int ret = 0; + + if (!test_bit(PIPELINE_OPEN, &pipeline->state) + || !test_bit(PIPELINE_PREPARE, &pipeline->state)) { + dev_err(pipeline->dev, "Pipeline not opened.\n"); + return -EINVAL; + } + + mutex_lock(&pipeline->pipe_scl_lock); + + if (test_bit(PIPELINE_START, &pipeline->state)) { + ret = fimc_is_pipeline_stop(pipeline, 0); + if (ret) { + dev_err(pipeline->dev, "Not able to stop pipeline\n"); + goto leave; + } + paused = 1; + } + + if (SCALER_ROTATE &rotation ) { + scaler_param->rotation.cmd = SCALER_ROTATION_CMD_CLOCKWISE90; + __set_bit(PARAM_SCALERP_ROTATION, index); + } + + if ((SCALER_FLIP_X | SCALER_FLIP_Y) & rotation) + __set_bit(PARAM_SCALERP_FLIP, index); + + if (SCALER_FLIP_X & rotation ) + scaler_param->flip.cmd = SCALER_FLIP_COMMAND_X_MIRROR; + if (SCALER_FLIP_Y & rotation ) + scaler_param->flip.cmd |= SCALER_FLIP_COMMAND_Y_MIRROR; + + ret = fimc_is_itf_set_param(&is->interface, pipeline->instance, + ISS_PREVIEW_STILL, index[0], index[1]); + + if (paused) { + ret = fimc_is_pipeline_start(pipeline, 0); + if (ret) + dev_err(pipeline->dev, + "Failed to restart the pipeline.\n"); + } +leave: + mutex_unlock(&pipeline->pipe_scl_lock); + return ret; +} + int fimc_is_pipeline_scaler_start(struct fimc_is_pipeline *pipeline, enum fimc_is_scaler_id scaler_id, unsigned int num_bufs, @@ -1516,6 +1570,19 @@ void fimc_is_pipeline_config_shot(struct fimc_is_pipeline *pipeline) FIMC_IS_FW_CALL_OP(config_shot, pipeline->minfo->shot.vaddr); } +int fimc_is_pipeline_update_shot_ctrl(struct fimc_is_pipeline *pipeline, + unsigned int config_id, + unsigned long val) +{ + /* @TODO: Exynos5 support (ISP configuration)*/ + if (!test_bit(PIPELINE_OPEN, &pipeline->state)) + return -EAGAIN; + + return FIMC_IS_FW_CALL_OP(set_control, + pipeline->minfo->shot.vaddr, + config_id, val); +} + int fimc_is_pipeline_shot_safe(struct fimc_is_pipeline *pipeline) { int ret; @@ -1690,7 +1757,6 @@ int fimc_is_pipeline_shot(struct fimc_is_pipeline *pipeline) scaler_ud->scp_target_address[i] = 0; } fimc_is_pipeline_buf_unlock(pipeline); - /* Send shot command */ pipeline->fcount++; rcount = pipeline->fcount; diff --git a/drivers/media/platform/exynos5-is/fimc-is-pipeline.h b/drivers/media/platform/exynos5-is/fimc-is-pipeline.h index 7c75df25534..8335fb16cae 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-pipeline.h +++ b/drivers/media/platform/exynos5-is/fimc-is-pipeline.h @@ -132,7 +132,12 @@ int fimc_is_pipeline_scaler_start(struct fimc_is_pipeline *pipeline, unsigned int num_planes); int fimc_is_pipeline_scaler_stop(struct fimc_is_pipeline *pipeline, enum fimc_is_scaler_id scaler_id); +int fimc_is_pipeline_set_scaler_effect(struct fimc_is_pipeline *pipeline, + unsigned int rotation); void fimc_is_pipeline_config_shot(struct fimc_is_pipeline *pipeline); +int fimc_is_pipeline_update_shot_ctrl(struct fimc_is_pipeline *pipeline, + unsigned int id, + unsigned long val); int fimc_is_pipeline_shot_safe(struct fimc_is_pipeline *pipeline); int fimc_is_pipeline_shot(struct fimc_is_pipeline *pipeline); int fimc_is_pipeline_start(struct fimc_is_pipeline *pipeline, int streamon); diff --git a/drivers/media/platform/exynos5-is/fimc-is-scaler.c b/drivers/media/platform/exynos5-is/fimc-is-scaler.c index 966ccf0fdf6..09d7d567132 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-scaler.c +++ b/drivers/media/platform/exynos5-is/fimc-is-scaler.c @@ -13,9 +13,20 @@ #include <media/videobuf2-dma-contig.h> #include "fimc-is.h" +#include "fimc-is-metadata.h" #define IS_SCALER_DRV_NAME "fimc-is-scaler" +#define FLIP_X_AXIS 0 +#define FLIP_Y_AXIS 1 + + +#define SCALER_ROTATE_90_CW 90 +#define SCALER_ROTATE_180_CW 180 +#define SCALER_ROTATE_270_CW 270 + +#define SCALER_CTRL_NUM 4 + static const struct fimc_is_fmt formats[] = { { .name = "YUV 4:2:0 3p MultiPlanar", @@ -64,6 +75,12 @@ static int scaler_video_capture_start_streaming(struct vb2_queue *vq, return -EINVAL; } + ret = v4l2_ctrl_handler_setup(&ctx->ctrls.handler); + if (ret) { + v4l2_err(&ctx->subdev, "Failed to setup scaler controls.\n"); + return -EINVAL; + } + set_bit(STATE_RUNNING, &ctx->capture_state); return 0; } @@ -176,6 +193,7 @@ static void scaler_video_capture_buffer_cleanup(struct vb2_buffer *vb) struct fimc_is_scaler *ctx = vb2_get_drv_priv(vq); struct fimc_is_buf *fbuf = container_of(vb, struct fimc_is_buf, vb); + /* * Skip the cleanup case the buffer * is no longer accessible through any of the queues @@ -392,6 +410,164 @@ static const struct v4l2_ioctl_ops scaler_video_capture_ioctl_ops = { .vidioc_streamoff = vb2_ioctl_streamoff, }; +static int scaler_video_set_flip(struct fimc_is_scaler *ctx, + unsigned int val, + unsigned int axis) +{ + if (ctx->scaler_id != SCALER_SCP) + return -EINVAL; + + if (axis == FLIP_X_AXIS) { + if (val) + ctx->ctrls.rotation |= SCALER_FLIP_X; + else + ctx->ctrls.rotation &= ~SCALER_FLIP_X; + } else { + if (val) + ctx->ctrls.rotation |= SCALER_FLIP_Y; + else + ctx->ctrls.rotation &= ~SCALER_FLIP_Y; + } + + return fimc_is_pipeline_set_scaler_effect(ctx->pipeline, + ctx->ctrls.rotation); + return 0; +} + +static int scler_video_set_rotate(struct fimc_is_scaler *ctx, + unsigned int val) +{ + int ret = 0; + + if (ctx->scaler_id != SCALER_SCP) + return -EINVAL; + + if (!val) { + ctx->ctrls.rotation &= ~SCALER_ROTATE; + } else { + switch (val) { + case SCALER_ROTATE_90_CW: + ctx->ctrls.rotation |= SCALER_ROTATE; + break; + + case SCALER_ROTATE_180_CW: + ctx->ctrls.rotation |= SCALER_FLIP_X | SCALER_FLIP_Y; + break; + + case SCALER_ROTATE_270_CW: + ctx->ctrls.rotation = SCALER_ROTATE | + SCALER_FLIP_X | SCALER_FLIP_Y; + break; + } + } + ret = fimc_is_pipeline_set_scaler_effect(ctx->pipeline, + ctx->ctrls.rotation); + return ret; +} + +static const unsigned int color_mode_map[] = { + [V4L2_COLORFX_NONE] = V4L2_COLORFX_NONE, + [V4L2_COLORFX_BW] = COLOR_CORRECTION_MODE_EFFECT_MONO, + [V4L2_COLORFX_SEPIA] = COLOR_CORRECTION_MODE_EFFECT_SEPIA, + [V4L2_COLORFX_NEGATIVE] = COLOR_CORRECTION_MODE_EFFECT_NEGATIVE, + [V4L2_COLORFX_EMBOSS] = COLORCORRECTION_MODE_EFFECT_EMBOSS, + [V4L2_COLORFX_SKETCH] = COLORCORRECTION_MODE_EFFECT_SKETCH, + [V4L2_COLORFX_AQUA] = COLOR_CORRECTION_MODE_EFFECT_AQUA, + [V4L2_COLORFX_ART_FREEZE] = COLOR_CORRECTION_MODE_EFFECT_POSTERIZE, + [V4L2_COLORFX_SOLARIZATION] = COLOR_CORRECTION_MODE_EFFECT_SOLARIZE, + [V4L2_COLORFX_ANTIQUE] = COLORCORRECTION_MODE_EFFECT_WARM_VINTAGE, +}; + +/* Unsupported color modes */ +#define SCALER_COLOR_MODE_SKIP_MASK (1 << (V4L2_COLORFX_GRASS_GREEN) | \ + 1 << (V4L2_COLORFX_SKIN_WHITEN) | \ + 1 << (V4L2_COLORFX_SKY_BLUE) | \ + 1 << (V4L2_COLORFX_SILHOUETTE) | \ + 1 << (V4L2_COLORFX_VIVID) | \ + 1 << (V4L2_COLORFX_SET_CBCR)) + +static int scaler_video_set_color_mode(struct fimc_is_scaler *ctx, + unsigned int val) +{ + unsigned int mode; + int ret = 0; + + if (val >= ARRAY_SIZE(color_mode_map) || + ctx->scaler_id != SCALER_SCC) + return -EINVAL; + + mode = color_mode_map[val]; + + if (!mode && val != V4L2_COLORFX_NONE) + return -EINVAL; + + if (!ret) + ret = fimc_is_pipeline_update_shot_ctrl(ctx->pipeline, + FIMC_IS_CID_COLOR_MODE, mode); + return ret; +} + +static int scaler_video_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct fimc_is_scaler *ctx = container_of(ctrl->handler, + struct fimc_is_scaler, + ctrls.handler); + int ret = 0; + + mutex_lock(&ctx->lock); + switch (ctrl->id) { + case V4L2_CID_HFLIP: + scaler_video_set_flip(ctx, ctrl->val, FLIP_X_AXIS); + break; + case V4L2_CID_VFLIP: + scaler_video_set_flip(ctx, ctrl->val, FLIP_Y_AXIS); + break; + case V4L2_CID_COLORFX: + scaler_video_set_color_mode(ctx, ctrl->val); + break; + case V4L2_CID_ROTATE: + scler_video_set_rotate(ctx, ctrl->val); + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&ctx->lock); + return ret; +} + +static const struct v4l2_ctrl_ops scaler_video_ctrl_ops = { + .s_ctrl = scaler_video_s_ctrl, +}; + +static int scaler_video_capture_create_controls(struct fimc_is_scaler *ctx) +{ + struct v4l2_ctrl_handler *handler = &ctx->ctrls.handler; + const struct v4l2_ctrl_ops *ops = &scaler_video_ctrl_ops; + + /* Flip */ + if (ctx->scaler_id == SCALER_SCP) { + v4l2_ctrl_new_std(handler, ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + v4l2_ctrl_new_std(handler, ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + /* Rotate */ + v4l2_ctrl_new_std(handler, ops, + V4L2_CID_ROTATE, + 0, 270, 90, 0); + } + /* Color effect */ + if (ctx->scaler_id == SCALER_SCC) + v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_COLORFX, + V4L2_COLORFX_ANTIQUE, + SCALER_COLOR_MODE_SKIP_MASK, + V4L2_COLORFX_NONE); + + return handler->error; +} + static int scaler_subdev_registered(struct v4l2_subdev *sd) { struct fimc_is_scaler *ctx = v4l2_get_subdevdata(sd); @@ -469,10 +645,12 @@ int fimc_is_scaler_subdev_create(struct fimc_is_scaler *ctx, struct vb2_alloc_ctx *alloc_ctx, struct fimc_is_pipeline *pipeline) { - struct v4l2_ctrl_handler *handler = &ctx->ctrl_handler; + struct v4l2_ctrl_handler *handler = &ctx->ctrls.handler; struct v4l2_subdev *sd = &ctx->subdev; int ret; + mutex_init(&ctx->lock); + ctx->scaler_id = scaler_id; ctx->alloc_ctx = alloc_ctx; ctx->pipeline = pipeline; @@ -500,9 +678,12 @@ int fimc_is_scaler_subdev_create(struct fimc_is_scaler *ctx, if (ret < 0) return ret; - ret = v4l2_ctrl_handler_init(handler, 1); + ret = v4l2_ctrl_handler_init(handler, SCALER_CTRL_NUM); if (handler->error) goto err_ctrl; + ret = scaler_video_capture_create_controls(ctx); + if (ret) + goto err_ctrl; sd->ctrl_handler = handler; sd->internal_ops = &scaler_subdev_internal_ops; @@ -521,6 +702,6 @@ void fimc_is_scaler_subdev_destroy(struct fimc_is_scaler *ctx) v4l2_device_unregister_subdev(sd); media_entity_cleanup(&sd->entity); - v4l2_ctrl_handler_free(&ctx->ctrl_handler); + v4l2_ctrl_handler_free(&ctx->ctrls.handler); v4l2_set_subdevdata(sd, NULL); } diff --git a/drivers/media/platform/exynos5-is/fimc-is-scaler.h b/drivers/media/platform/exynos5-is/fimc-is-scaler.h index 97a9d0d72d3..d54c46450fb 100644 --- a/drivers/media/platform/exynos5-is/fimc-is-scaler.h +++ b/drivers/media/platform/exynos5-is/fimc-is-scaler.h @@ -45,6 +45,16 @@ #define SCALER_MIN_WIDTH 32 #define SCALER_MIN_HEIGHT 32 +#define SCALER_ROTATE (1 << 2) +#define SCALER_FLIP_X (1 << 0) +#define SCALER_FLIP_Y (1 << 1) + +struct fimc_is_scaler_ctrl { + struct v4l2_ctrl_handler handler; + unsigned int rotation; + unsigned int color_mode; + unsigned int status; +}; /** * struct fimc_is_scaler - fimc-is scaler structure * @vfd: video device node @@ -55,6 +65,7 @@ * @subdev_pads: the subdev media pads * @ctrl_handler: v4l2 control handler * @video_lock: video lock mutex + * @lock: internal locking * @event_q: notifies scaler events * @pipeline: pipeline instance for this scaler context * @scaler_id: distinguishes scaler preview or scaler codec @@ -77,9 +88,10 @@ struct fimc_is_scaler { struct media_pad vd_pad; struct media_pad subdev_pads[SCALER_SD_PADS_NUM]; struct v4l2_mbus_framefmt subdev_fmt; - struct v4l2_ctrl_handler ctrl_handler; + struct fimc_is_scaler_ctrl ctrls; struct mutex video_lock; + struct mutex lock; wait_queue_head_t event_q; struct fimc_is_pipeline *pipeline; |