summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeata Michalska <b.michalska@samsung.com>2014-07-25 16:45:58 +0200
committerSylwester Nawrocki <s.nawrocki@samsung.com>2014-11-27 03:43:14 -0800
commit06975230d3a600d1e3a21747aef004d6583decc1 (patch)
treee8cb49b0fbd9e7319e49f63f795420da4c216a71
parentcd6e2e406309601c9452f5132b5b8e544aa2a37c (diff)
downloadlinux-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.c127
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-fw.h34
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-isp.c273
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-isp.h29
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-param.h4
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-pipeline.c68
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-pipeline.h5
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-scaler.c187
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-scaler.h14
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;