diff options
Diffstat (limited to 'drivers/media/video/m5mols/m5mols_controls.c')
-rw-r--r-- | drivers/media/video/m5mols/m5mols_controls.c | 369 |
1 files changed, 234 insertions, 135 deletions
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c index 3e08018..d135d20 100644 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -1,5 +1,5 @@ /* - * Controls for M5MOLS 8M Pixel camera sensor with ISP + * Controls for M-5MOLS 8M Pixel camera sensor with ISP * * Copyright (C) 2011 Samsung Electronics Co., Ltd. * Author: HeungJun Kim <riverful.kim@samsung.com> @@ -14,186 +14,285 @@ */ #include <linux/i2c.h> +#include <linux/delay.h> #include <linux/videodev2.h> #include <media/v4l2-ctrls.h> #include "m5mols.h" #include "m5mols_reg.h" -static int m5mols_wb_mode(struct m5mols_info *info, struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = &info->sd; - int ret; - - if (info->is_awb_lock) { - ret = m5mols_set_awb_lock(info, false); - if (!ret) - return ret; - } - - /* 0x01 : Auto Whitebalance, 0x02 : Manual Whitebalance. */ - return i2c_w8_wb(sd, CAT6_AWB_MODE, (ctrl->val) ? 0x1 : 0x2); +static struct m5mols_scenemode m5mols_default_scenemode[] = { + [REG_SCENE_NORMAL] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF, + 5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_PORTRAIT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 4, + REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_LANDSCAPE] = { + REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 4, REG_EDGE_ON, 6, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_SPORTS] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_PARTY_INDOOR] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 4, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_BEACH_SNOW] = { + REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 4, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_SUNSET] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, + REG_AWB_DAYLIGHT, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_DAWN_DUSK] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, + REG_AWB_FLUORESCENT_1, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_FALL] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 5, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_NIGHT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_AGAINST_LIGHT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_FIRE] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, + }, + [REG_SCENE_TEXT] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 7, + REG_AF_MACRO, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON, + }, + [REG_SCENE_CANDLE] = { + REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, + REG_CHROMA_ON, 3, REG_EDGE_ON, 5, + REG_AF_NORMAL, REG_FD_OFF, + REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, + 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, + }, +}; -} - -static int m5mols_exposure_mode(struct m5mols_info *info, - struct v4l2_ctrl *ctrl) +/** + * m5mols_do_scenemode() - Change current scenemode + * @mode: Desired mode of the scenemode + * + * WARNING: The execution order is important. Do not change the order. + */ +int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) { struct v4l2_subdev *sd = &info->sd; + struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode]; int ret; - if (info->is_ae_lock) { - ret = m5mols_set_ae_lock(info, false); - if (ret) - return ret; - } - - /* 0x01 : Auto Exposure, 0x0 : Manual Exposure. */ - return i2c_w8_ae(sd, CAT3_AE_MODE, - (ctrl->val == V4L2_EXPOSURE_AUTO) ? 0x1 : 0x0); -} - -static int m5mols_exposure(struct m5mols_info *info) -{ - struct v4l2_subdev *sd = &info->sd; - - return i2c_w16_ae(sd, CAT3_MANUAL_GAIN_MON, info->exposure->val); -} - -static int m5mols_zoom(struct m5mols_info *info, struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = &info->sd; - - return i2c_w8_mon(sd, CAT2_ZOOM, ctrl->val); -} - -static int m5mols_focus_mode(struct m5mols_info *info, - struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = &info->sd; - u32 reg; - int ret; + if (mode > REG_SCENE_CANDLE) + return -EINVAL; - ret = m5mols_set_mode(sd, MODE_MONITOR); + ret = m5mols_lock_3a(info, false); + if (!ret) + ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode); + if (!ret) + ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode); + if (!ret) + ret = m5mols_write(sd, AE_MODE, scenemode.metering); + if (!ret) + ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias); + if (!ret) + ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode); + if (!ret) + ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset); + if (!ret) + ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en); + if (!ret) + ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl); + if (!ret) + ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en); + if (!ret) + ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl); + if (!ret && is_available_af(info)) + ret = m5mols_write(sd, AF_MODE, scenemode.af_range); + if (!ret && is_available_af(info)) + ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode); + if (!ret) + ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone); + if (!ret) + ret = m5mols_write(sd, AE_ISO, scenemode.iso); if (!ret) - ret = i2c_r8_system(sd, CAT0_INT_FACTOR, ®); + ret = m5mols_mode(info, REG_CAPTURE); if (!ret) - ret = i2c_w8_system(sd, CAT0_INT_ENABLE, (1 << INT_BIT_AF)); + ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr); if (!ret) - /* must be excuted in the monitor mode */ - ret = i2c_w8_lens(sd, CATA_INIT_AF_FUNC, 0x1); + ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc); + if (!ret) + ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light); + if (!ret) + ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash); + if (!ret) + ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode); + if (!ret) + ret = m5mols_mode(info, REG_MONITOR); - /* 0x0 : Normal AF mode - * 0x1 : Macro AF mode - * 0x2 : Continuous AF mode (Not working) */ - return i2c_w8_lens(sd, CATA_AF_MODE, 0x0); + return ret; } -static int m5mols_focus(struct m5mols_info *info, struct v4l2_ctrl *ctrl) +static int m5mols_lock_ae(struct m5mols_info *info, bool lock) { - struct v4l2_subdev *sd = &info->sd; + int ret = 0; - /* 0x0: Stop, - 0x1: Excute AF, - 0x02: Excutre MF rel(Not tested), - 0x03: Excute MF absol(Not tested) */ - return i2c_w8_lens(sd, CATA_AF_EXCUTE, ctrl->val); + if (info->lock_ae != lock) + ret = m5mols_write(&info->sd, AE_LOCK, + lock ? REG_AE_LOCK : REG_AE_UNLOCK); + if (!ret) + info->lock_ae = lock; + + return ret; } -static int m5mols_set_saturation(struct m5mols_info *info, - struct v4l2_ctrl *ctrl) +static int m5mols_lock_awb(struct m5mols_info *info, bool lock) { - struct v4l2_subdev *sd = &info->sd; - static u8 m5mols_chroma_lvl[] = { - 0x1c, 0x3e, 0x5f, 0x80, 0xa1, 0xc2, 0xe4, - }; - int ret; + int ret = 0; - ret = i2c_w8_mon(sd, CAT2_CHROMA_LVL, m5mols_chroma_lvl[ctrl->val]); + if (info->lock_awb != lock) + ret = m5mols_write(&info->sd, AWB_LOCK, + lock ? REG_AWB_LOCK : REG_AWB_UNLOCK); if (!ret) - ret = i2c_w8_mon(sd, CAT2_CHROMA_EN, true); + info->lock_awb = lock; return ret; } -static int m5mols_set_colorfx(struct m5mols_info *info, struct v4l2_ctrl *ctrl) +/* m5mols_lock_3a() - Lock 3A(Auto Exposure, Auto Whitebalance, Auto Focus) */ +int m5mols_lock_3a(struct m5mols_info *info, bool lock) { - struct v4l2_subdev *sd = &info->sd; - static u8 m5mols_effects_gamma[] = { /* cat 1: Effects */ - [V4L2_COLORFX_NEGATIVE] = 0x01, - [V4L2_COLORFX_EMBOSS] = 0x06, - [V4L2_COLORFX_SKETCH] = 0x07, - }; - static u8 m5mols_cfixb_chroma[] = { /* cat 2: Cr for effect */ - [V4L2_COLORFX_BW] = 0x0, - [V4L2_COLORFX_SEPIA] = 0xd8, - [V4L2_COLORFX_SKY_BLUE] = 0x40, - [V4L2_COLORFX_GRASS_GREEN] = 0xe0, - }; - static u8 m5mols_cfixr_chroma[] = { /* cat 2: Cb for effect */ - [V4L2_COLORFX_BW] = 0x0, - [V4L2_COLORFX_SEPIA] = 0x18, - [V4L2_COLORFX_SKY_BLUE] = 0x00, - [V4L2_COLORFX_GRASS_GREEN] = 0xe0, - }; - int ret = -EINVAL; - - switch (ctrl->val) { - case V4L2_COLORFX_NONE: - return i2c_w8_mon(sd, CAT2_COLOR_EFFECT, false); - case V4L2_COLORFX_BW: /* chroma: Gray */ - case V4L2_COLORFX_SEPIA: /* chroma: Sepia */ - case V4L2_COLORFX_SKY_BLUE: /* chroma: Blue */ - case V4L2_COLORFX_GRASS_GREEN: /* chroma: Green */ - ret = i2c_w8_mon(sd, CAT2_CFIXB, - m5mols_cfixb_chroma[ctrl->val]); - if (!ret) - ret = i2c_w8_mon(sd, CAT2_CFIXR, - m5mols_cfixr_chroma[ctrl->val]); - if (!ret) - ret = i2c_w8_mon(sd, CAT2_COLOR_EFFECT, true); - return ret; - case V4L2_COLORFX_NEGATIVE: /* gamma: Negative */ - case V4L2_COLORFX_EMBOSS: /* gamma: Emboss */ - case V4L2_COLORFX_SKETCH: /* gamma: Outline */ - ret = i2c_w8_param(sd, CAT1_EFFECT, - m5mols_effects_gamma[ctrl->val]); - if (!ret) - ret = i2c_w8_mon(sd, CAT2_COLOR_EFFECT, true); - return ret; - } + int ret; + + ret = m5mols_lock_ae(info, lock); + if (!ret) + ret = m5mols_lock_awb(info, lock); + /* Don't need to handle unlocking AF */ + if (!ret && is_available_af(info) && lock) + ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); return ret; } +/* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */ int m5mols_set_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); struct m5mols_info *info = to_m5mols(sd); - int ret = 0; + int ret; switch (ctrl->id) { case V4L2_CID_ZOOM_ABSOLUTE: - return m5mols_zoom(info, ctrl); - case V4L2_CID_FOCUS_AUTO: - if (ctrl->val != 0) - ret = m5mols_focus_mode(info, ctrl); - if (!ret) - ret = m5mols_focus(info, ctrl); - return ret; + return m5mols_write(sd, MON_ZOOM, ctrl->val); + case V4L2_CID_EXPOSURE_AUTO: - if (!ctrl->is_new) - ctrl->val = V4L2_EXPOSURE_MANUAL; - ret = m5mols_exposure_mode(info, ctrl); - if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) - ret = m5mols_exposure(info); + ret = m5mols_lock_ae(info, + ctrl->val == V4L2_EXPOSURE_AUTO ? false : true); + if (!ret && ctrl->val == V4L2_EXPOSURE_AUTO) + ret = m5mols_write(sd, AE_MODE, REG_AE_ALL); + if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) { + int val = info->exposure->val; + ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); + if (!ret) + ret = m5mols_write(sd, AE_MAN_GAIN_MON, val); + if (!ret) + ret = m5mols_write(sd, AE_MAN_GAIN_CAP, val); + } return ret; + case V4L2_CID_AUTO_WHITE_BALANCE: - return m5mols_wb_mode(info, ctrl); + ret = m5mols_lock_awb(info, ctrl->val ? false : true); + if (!ret) + ret = m5mols_write(sd, AWB_MODE, ctrl->val ? + REG_AWB_AUTO : REG_AWB_PRESET); + return ret; + case V4L2_CID_SATURATION: - return m5mols_set_saturation(info, ctrl); + ret = m5mols_write(sd, MON_CHROMA_LVL, ctrl->val); + if (!ret) + ret = m5mols_write(sd, MON_CHROMA_EN, REG_CHROMA_ON); + return ret; + case V4L2_CID_COLORFX: - return m5mols_set_colorfx(info, ctrl); + /* + * This control uses two kinds of registers: normal & color. + * The normal effect belongs to category 1, while the color + * one belongs to category 2. + * + * The normal effect uses one register: CAT1_EFFECT. + * The color effect uses three registers: + * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB. + */ + ret = m5mols_write(sd, PARM_EFFECT, + ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA : + ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS : + REG_EFFECT_OFF); + if (!ret) + ret = m5mols_write(sd, MON_EFFECT, + ctrl->val == V4L2_COLORFX_SEPIA ? + REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF); + if (!ret) + ret = m5mols_write(sd, MON_CFIXR, + ctrl->val == V4L2_COLORFX_SEPIA ? + REG_CFIXR_SEPIA : 0); + if (!ret) + ret = m5mols_write(sd, MON_CFIXB, + ctrl->val == V4L2_COLORFX_SEPIA ? + REG_CFIXB_SEPIA : 0); + return ret; } return -EINVAL; |