diff options
author | zack.zeng <zack.zeng@rock-chips.com> | 2018-12-25 18:00:15 +0800 |
---|---|---|
committer | Tao Huang <huangtao@rock-chips.com> | 2019-01-24 14:47:38 +0800 |
commit | 7b1fb3919b69301a90d5abf62ae415758cbb2674 (patch) | |
tree | 68d3f4ff7f686d1c5a5c23bd89a6da1d99d5efce | |
parent | f944fd08f6c5c5116538faec997583b9e59b8f21 (diff) |
media: soc_camera: add mono sensor sc031gs
Change-Id: I6b3e376d905895ad9fef4364184b201da5f873cc
Signed-off-by: Yiqing Zeng <zack.zeng@rock-chips.com>
5 files changed, 2377 insertions, 0 deletions
diff --git a/drivers/media/i2c/soc_camera/rockchip/Kconfig b/drivers/media/i2c/soc_camera/rockchip/Kconfig index ba48a89fcc81..f5e8a121f213 100644 --- a/drivers/media/i2c/soc_camera/rockchip/Kconfig +++ b/drivers/media/i2c/soc_camera/rockchip/Kconfig @@ -89,3 +89,10 @@ config VIDEO_ov5640 default n help This is ov5640 camera driver adapt to rockchip cif isp platform. + +config VIDEO_SC031GS + tristate "sc031gs driver adapt to rockchip cif isp platform" + depends on VIDEO_V4L2 && VIDEO_RK_CIF_ISP10 && I2C + default n + help + This is sc031gs camera driver adapt to rockchip cif isp platform. diff --git a/drivers/media/i2c/soc_camera/rockchip/Makefile b/drivers/media/i2c/soc_camera/rockchip/Makefile index 55fd7ea478d8..e764f60ccb8a 100644 --- a/drivers/media/i2c/soc_camera/rockchip/Makefile +++ b/drivers/media/i2c/soc_camera/rockchip/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_VIDEO_OV13850) += ov_camera_module.o rk_camera_module.o ov13850_v4l obj-$(CONFIG_VIDEO_OV9281) += ov_camera_module.o rk_camera_module.o ov9281_v4l2-i2c-subdev.o obj-$(CONFIG_VIDEO_OV9750) += ov_camera_module.o rk_camera_module.o ov9750_v4l2-i2c-subdev.o obj-$(CONFIG_VIDEO_ov5640) += ov_camera_module.o rk_camera_module.o ov5640_v4l2-i2c-subdev.o +obj-$(CONFIG_VIDEO_SC031GS) += sc_camera_module.o rk_camera_module.o sc031gs_v4l2-i2c-subdev.o diff --git a/drivers/media/i2c/soc_camera/rockchip/sc031gs_v4l2-i2c-subdev.c b/drivers/media/i2c/soc_camera/rockchip/sc031gs_v4l2-i2c-subdev.c new file mode 100644 index 000000000000..c9ca8b4abe62 --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/sc031gs_v4l2-i2c-subdev.c @@ -0,0 +1,787 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sc031gs sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + * Note: + * + *v0.1.0: + *1. Initialize version; + * + */ + +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf-core.h> +#include <linux/slab.h> +#include <media/v4l2-controls_rockchip.h> +#include "sc_camera_module.h" + +#define SC031GS_DRIVER_NAME "sc031gs" + +#define SC031GS_AEC_PK_LONG_GAIN_HIGH_REG 0x3e08 +#define SC031GS_AEC_PK_LONG_GAIN_LOW_REG 0x3e09 + +#define SC031GS_AEC_PK_LONG_EXPO_HIGH_REG 0x3e01 +#define SC031GS_AEC_PK_LONG_EXPO_LOW_REG 0x3e02 + +#define SC031GS_AEC_GROUP_UPDATE_ADDRESS 0x3812 +#define SC031GS_AEC_GROUP_UPDATE_START_DATA 0x00 +#define SC031GS_AEC_GROUP_UPDATE_END_DATA 0x30 + +#define SC031GS_PIDH_ADDR 0x3107 +#define SC031GS_PIDL_ADDR 0x3108 + +/* High byte of product ID */ +#define SC031GS_PIDH_MAGIC 0x00 +/* Low byte of product ID */ +#define SC031GS_PIDL_MAGIC 0x31 + +#define SC031GS_EXT_CLK 24000000 +#define SC031GS_TIMING_VTS_HIGH_REG 0x320e +#define SC031GS_TIMING_VTS_LOW_REG 0x320f +#define SC031GS_TIMING_HTS_HIGH_REG 0x320c +#define SC031GS_TIMING_HTS_LOW_REG 0x320d +#define SC031GS_FINE_INTG_TIME_MIN 0 +#define SC031GS_FINE_INTG_TIME_MAX_MARGIN 0 +#define SC031GS_COARSE_INTG_TIME_MIN 1 +#define SC031GS_COARSE_INTG_TIME_MAX_MARGIN 4 +#define SC031GS_HORIZONTAL_OUTPUT_SIZE_HIGH_REG 0x3208 +#define SC031GS_HORIZONTAL_OUTPUT_SIZE_LOW_REG 0x3209 +#define SC031GS_VERTICAL_OUTPUT_SIZE_HIGH_REG 0x320a +#define SC031GS_VERTICAL_OUTPUT_SIZE_LOW_REG 0x320b + +#define SC031GS_MIRROR_FILP 0x3221 + +static struct sc_camera_module sc031gs; + +/* ======================================================================== */ +/* Base sensor configs */ +/* ======================================================================== */ +/* MCLK:24MHz 640x480 50fps mipi 1lane 10bit 720Mbps/lane */ +static struct sc_camera_module_reg sc031gs_init_tab_640_480_50fps[] = { +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x0100, 0x00}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3000, 0x00}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3001, 0x00}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x300f, 0x0f}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3018, 0x13}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3019, 0xfe}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x301c, 0x78}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3031, 0x0a}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3037, 0x20}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x303f, 0x01}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3208, 0x02}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3209, 0x80}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x320a, 0x01}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x320b, 0xe0}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x320c, 0x03}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x320d, 0x6e}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x320e, 0x06}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x320f, 0x67}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3250, 0xc0}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3251, 0x02}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3252, 0x02}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3253, 0xa6}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3254, 0x02}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3255, 0x07}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3304, 0x48}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3306, 0x38}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3309, 0x68}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x330b, 0xe0}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x330c, 0x18}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x330f, 0x20}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3310, 0x10}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3314, 0x3a}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3315, 0x38}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3316, 0x48}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3317, 0x20}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3329, 0x3c}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x332d, 0x3c}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x332f, 0x40}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3335, 0x44}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3344, 0x44}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x335b, 0x80}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x335f, 0x80}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3366, 0x06}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3385, 0x31}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3387, 0x51}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3389, 0x01}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x33b1, 0x03}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x33b2, 0x06}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3621, 0xa4}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3622, 0x05}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3630, 0x46}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3631, 0x48}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3633, 0x52}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3636, 0x25}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3637, 0x89}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3638, 0x0f}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3639, 0x08}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x363a, 0x00}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x363b, 0x48}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x363c, 0x06}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x363d, 0x00}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x363e, 0xf8}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3640, 0x00}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3641, 0x01}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x36e9, 0x00}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x36ea, 0x3b}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x36eb, 0x0e}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x36ec, 0x0e}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x36ed, 0x33}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x36f9, 0x00}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x36fa, 0x3a}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x36fc, 0x01}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3908, 0x91}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3d08, 0x01}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3e01, 0x2a}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3e02, 0x00}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3e03, 0x0b}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x3e06, 0x0c}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x4500, 0x59}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x4501, 0xc4}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x4603, 0x00}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x5011, 0x00}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x4418, 0x08}, +{SC_CAMERA_MODULE_REG_TYPE_DATA, 0x4419, 0x8a}, +}; + +static struct sc_camera_module_config sc031gs_configs[] = { + { + .name = "640x480_50fps", + .frm_fmt = { + .width = 640, + .height = 480, + .code = MEDIA_BUS_FMT_Y10_1X10 + }, + .frm_intrvl = { + .interval = { + .numerator = 1, + .denominator = 50 + } + }, + .auto_exp_enabled = false, + .auto_gain_enabled = false, + .auto_wb_enabled = false, + .reg_table = (void *)sc031gs_init_tab_640_480_50fps, + .reg_table_num_entries = + ARRAY_SIZE(sc031gs_init_tab_640_480_50fps), + .v_blanking_time_us = 3078, + .max_exp_gain_h = 16, + .max_exp_gain_l = 0, + PLTFRM_CAM_ITF_MIPI_CFG(0, 1, 720, SC031GS_EXT_CLK) + } +}; + +static int sc031gs_set_flip(struct sc_camera_module *cam_mod, + struct pltfrm_camera_module_reg reglist[], + int len) +{ + int i, mode = 0; + u16 orientation = 0; + + mode = sc_camera_module_get_flip_mirror(cam_mod); + + if (mode == -1) { + sc_camera_module_pr_debug(cam_mod, + "dts don't set flip, return!\n"); + return 0; + } + + if (!IS_ERR_OR_NULL(cam_mod->active_config)) { + if (mode == SC_MIRROR_BIT_MASK) + orientation |= 0x06; + if (mode == SC_FLIP_BIT_MASK) + orientation |= 0x60; + for (i = 0; i < len; i++) { + if (reglist[i].reg == SC031GS_MIRROR_FILP) + reglist[i].val = orientation; + } + } + return 0; +} + +static int sc031gs_g_vts(struct sc_camera_module *cam_mod, u32 *vts) +{ + u32 msb, lsb; + int ret; + + ret = sc_camera_module_read_reg_table(cam_mod, + SC031GS_TIMING_VTS_HIGH_REG, + &msb); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = sc_camera_module_read_reg_table(cam_mod, + SC031GS_TIMING_VTS_LOW_REG, + &lsb); + if (IS_ERR_VALUE(ret)) + goto err; + + *vts = (msb << 8) | lsb; + + return 0; +err: + sc_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +static int sc031gs_auto_adjust_fps(struct sc_camera_module *cam_mod, + u32 exp_time) +{ + int ret; + u32 vts; + + if ((exp_time + SC031GS_COARSE_INTG_TIME_MAX_MARGIN) > + cam_mod->vts_min) + vts = (exp_time + SC031GS_COARSE_INTG_TIME_MAX_MARGIN); + else + vts = cam_mod->vts_min; + + ret = sc_camera_module_write_reg(cam_mod, + SC031GS_TIMING_VTS_LOW_REG, + vts & 0xFF); + ret |= sc_camera_module_write_reg(cam_mod, + SC031GS_TIMING_VTS_HIGH_REG, + (vts >> 8) & 0xFF); + + if (IS_ERR_VALUE(ret)) { + sc_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + } else { + sc_camera_module_pr_debug(cam_mod, + "updated vts = 0x%x,vts_min=0x%x\n", + vts, cam_mod->vts_min); + cam_mod->vts_cur = vts; + } + + return ret; +} + +static int sc031gs_set_vts(struct sc_camera_module *cam_mod, + u32 vts) +{ + int ret = 0; + + if (vts <= cam_mod->vts_min) + return ret; + + ret = sc_camera_module_write_reg(cam_mod, + SC031GS_TIMING_VTS_LOW_REG, + vts & 0xFF); + ret |= sc_camera_module_write_reg(cam_mod, + SC031GS_TIMING_VTS_HIGH_REG, + (vts >> 8) & 0xFF); + + if (IS_ERR_VALUE(ret)) { + sc_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + } else { + sc_camera_module_pr_debug(cam_mod, + "updated vts = 0x%x,vts_min=0x%x\n", + vts, cam_mod->vts_min); + cam_mod->vts_cur = vts; + } + + return ret; +} + +static int sc031gs_write_aec(struct sc_camera_module *cam_mod) +{ + int ret = 0; + + sc_camera_module_pr_debug(cam_mod, + "exp_time = %d lines, gain = %d, flash_mode = %d\n", + cam_mod->exp_config.exp_time, + cam_mod->exp_config.gain, + cam_mod->exp_config.flash_mode); + + /* + * if the sensor is already streaming, write to shadow registers, + * if the sensor is in SW standby, write to active registers, + * if the sensor is off/registers are not writeable, do nothing + */ + if (cam_mod->state == SC_CAMERA_MODULE_SW_STANDBY || + cam_mod->state == SC_CAMERA_MODULE_STREAMING) { + u32 a_gain = cam_mod->exp_config.gain; + u32 exp_time = cam_mod->exp_config.exp_time; + u32 vts = cam_mod->vts_min; + u32 coarse_again, fine_again, fine_again_reg, coarse_again_reg; + + a_gain = a_gain * cam_mod->exp_config.gain_percent / 100; + + /* hold reg start */ + mutex_lock(&cam_mod->lock); + ret = sc_camera_module_write_reg(cam_mod, + SC031GS_AEC_GROUP_UPDATE_ADDRESS, + SC031GS_AEC_GROUP_UPDATE_START_DATA); + + if (!IS_ERR_VALUE(ret) && cam_mod->auto_adjust_fps) + ret |= sc031gs_auto_adjust_fps(cam_mod, + cam_mod->exp_config.exp_time); + if (exp_time > vts - 6) + exp_time = vts - 6; + + if (a_gain < 0x20) { /*1x ~ 2x*/ + fine_again = a_gain - 16; + coarse_again = 0x03; + fine_again_reg = ((0x01 << 4) & 0x10) | + (fine_again & 0x0f); + coarse_again_reg = coarse_again & 0x1F; + } else if (a_gain < 0x40) { /*2x ~ 4x*/ + fine_again = (a_gain >> 1) - 16; + coarse_again = 0x7; + fine_again_reg = ((0x01 << 4) & 0x10) | + (fine_again & 0x0f); + coarse_again_reg = coarse_again & 0x1F; + } else if (a_gain < 0x80) { /*4x ~ 8x*/ + fine_again = (a_gain >> 2) - 16; + coarse_again = 0xf; + fine_again_reg = ((0x01 << 4) & 0x10) | + (fine_again & 0x0f); + coarse_again_reg = coarse_again & 0x1F; + } else { /*8x ~ 16x*/ + fine_again = (a_gain >> 3) - 16; + coarse_again = 0x1f; + fine_again_reg = ((0x01 << 4) & 0x10) | + (fine_again & 0x0f); + coarse_again_reg = coarse_again & 0x1F; + } + + if (a_gain < 0x20) { + ret |= sc_camera_module_write_reg(cam_mod, + 0x3314, 0x3a); + ret |= sc_camera_module_write_reg(cam_mod, + 0x3317, 0x20); + } else { + ret |= sc_camera_module_write_reg(cam_mod, + 0x3314, 0x44); + ret |= sc_camera_module_write_reg(cam_mod, + 0x3317, 0x0f); + } + + ret |= sc_camera_module_write_reg(cam_mod, + SC031GS_AEC_PK_LONG_GAIN_HIGH_REG, + coarse_again_reg); + ret |= sc_camera_module_write_reg(cam_mod, + SC031GS_AEC_PK_LONG_GAIN_LOW_REG, + fine_again_reg); + + ret |= sc_camera_module_write_reg(cam_mod, + SC031GS_AEC_PK_LONG_EXPO_HIGH_REG, + (exp_time >> 4) & 0xff); + ret |= sc_camera_module_write_reg(cam_mod, + SC031GS_AEC_PK_LONG_EXPO_LOW_REG, + (exp_time & 0xff) << 4); + + if (!cam_mod->auto_adjust_fps) + ret |= sc031gs_set_vts(cam_mod, + cam_mod->exp_config.vts_value); + + /* hold reg end */ + ret |= sc_camera_module_write_reg(cam_mod, + SC031GS_AEC_GROUP_UPDATE_ADDRESS, + SC031GS_AEC_GROUP_UPDATE_END_DATA); + mutex_unlock(&cam_mod->lock); + } + + if (IS_ERR_VALUE(ret)) + sc_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +static int sc031gs_g_ctrl(struct sc_camera_module *cam_mod, u32 ctrl_id) +{ + int ret = 0; + + sc_camera_module_pr_debug(cam_mod, "\n"); + + switch (ctrl_id) { + case V4L2_CID_GAIN: + case V4L2_CID_EXPOSURE: + case V4L2_CID_FLASH_LED_MODE: + /* nothing to be done here */ + break; + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + sc_camera_module_pr_debug(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +static int sc031gs_filltimings(struct sc_camera_module_custom_config *custom) +{ + u32 i, j; + u32 win_h_off = 0, win_v_off = 0; + struct sc_camera_module_config *config; + struct sc_camera_module_timings *timings; + struct sc_camera_module_reg *reg_table; + u32 reg_table_num_entries; + + for (i = 0; i < custom->num_configs; i++) { + config = &custom->configs[i]; + reg_table = config->reg_table; + reg_table_num_entries = config->reg_table_num_entries; + timings = &config->timings; + + memset(timings, 0x00, sizeof(*timings)); + for (j = 0; j < reg_table_num_entries; j++) { + switch (reg_table[j].reg) { + case SC031GS_TIMING_VTS_HIGH_REG: + if (timings->frame_length_lines & 0xff00) + timings->frame_length_lines = 0; + timings->frame_length_lines = + ((reg_table[j].val << 8) | + (timings->frame_length_lines & 0xff)); + break; + case SC031GS_TIMING_VTS_LOW_REG: + timings->frame_length_lines = + (reg_table[j].val | + (timings->frame_length_lines & 0xff00)); + break; + case SC031GS_TIMING_HTS_HIGH_REG: + if (timings->line_length_pck & 0xff00) + timings->line_length_pck = 0; + timings->line_length_pck = + ((reg_table[j].val << 8) | + timings->line_length_pck); + break; + case SC031GS_TIMING_HTS_LOW_REG: + timings->line_length_pck = + (reg_table[j].val | + (timings->line_length_pck & 0xff00)); + break; + case SC031GS_HORIZONTAL_OUTPUT_SIZE_HIGH_REG: + timings->sensor_output_width = + ((reg_table[j].val << 8) | + (timings->sensor_output_width & 0xff)); + break; + case SC031GS_HORIZONTAL_OUTPUT_SIZE_LOW_REG: + timings->sensor_output_width = + (reg_table[j].val | + (timings->sensor_output_width & 0xff00)); + break; + case SC031GS_VERTICAL_OUTPUT_SIZE_HIGH_REG: + timings->sensor_output_height = + ((reg_table[j].val << 8) | + (timings->sensor_output_height & 0xff)); + break; + case SC031GS_VERTICAL_OUTPUT_SIZE_LOW_REG: + timings->sensor_output_height = + (reg_table[j].val | + (timings->sensor_output_height & 0xff00)); + break; + } + } + + timings->crop_horizontal_start += win_h_off; + timings->crop_horizontal_end -= win_h_off; + timings->crop_vertical_start += win_v_off; + timings->crop_vertical_end -= win_v_off; + timings->exp_time = timings->exp_time >> 4; + + timings->vt_pix_clk_freq_hz = + config->frm_intrvl.interval.denominator + * timings->frame_length_lines + * timings->line_length_pck; + + timings->coarse_integration_time_min = + SC031GS_COARSE_INTG_TIME_MIN; + timings->coarse_integration_time_max_margin = + SC031GS_COARSE_INTG_TIME_MAX_MARGIN; + + /* OV Sensor do not use fine integration time. */ + timings->fine_integration_time_min = + SC031GS_FINE_INTG_TIME_MIN; + timings->fine_integration_time_max_margin = + SC031GS_FINE_INTG_TIME_MAX_MARGIN; + } + + return 0; +} + +static int sc031gs_g_timings(struct sc_camera_module *cam_mod, + struct sc_camera_module_timings *timings) +{ + int ret = 0; + unsigned int vts; + + if (IS_ERR_OR_NULL(cam_mod->active_config)) + goto err; + + *timings = cam_mod->active_config->timings; + + vts = (!cam_mod->vts_cur) ? + timings->frame_length_lines : + cam_mod->vts_cur; + + if (cam_mod->frm_intrvl_valid) + timings->vt_pix_clk_freq_hz = + cam_mod->frm_intrvl.interval.denominator + * vts + * timings->line_length_pck; + else + timings->vt_pix_clk_freq_hz = + cam_mod->active_config->frm_intrvl.interval.denominator + * vts + * timings->line_length_pck; + + timings->frame_length_lines = vts; + + return ret; +err: + sc_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +static int sc031gs_s_ctrl(struct sc_camera_module *cam_mod, u32 ctrl_id) +{ + int ret = 0; + + sc_camera_module_pr_debug(cam_mod, "\n"); + + switch (ctrl_id) { + case V4L2_CID_GAIN: + case V4L2_CID_EXPOSURE: + ret = sc031gs_write_aec(cam_mod); + break; + case V4L2_CID_FLASH_LED_MODE: + /* nothing to be done here */ + break; + case V4L2_CID_FOCUS_ABSOLUTE: + /* todo*/ + break; + /* + *case RK_V4L2_CID_AUTO_FPS: + * if (cam_mod->auto_adjust_fps) + * ret = sc031gs_auto_adjust_fps( + *cam_mod, + *cam_mod->exp_config.exp_time); + *break; + */ + default: + ret = -EINVAL; + break; + } + + if (IS_ERR_VALUE(ret)) + sc_camera_module_pr_err(cam_mod, + "failed with error (%d)\n", ret); + return ret; +} + +static int sc031gs_s_ext_ctrls(struct sc_camera_module *cam_mod, + struct sc_camera_module_ext_ctrls *ctrls) +{ + int ret = 0; + + if ((ctrls->ctrls[0].id == V4L2_CID_GAIN || + ctrls->ctrls[0].id == V4L2_CID_EXPOSURE)) + ret = sc031gs_write_aec(cam_mod); + else + ret = -EINVAL; + + if (IS_ERR_VALUE(ret)) + sc_camera_module_pr_debug(cam_mod, + "failed with error (%d)\n", ret); + + return ret; +} + +static int sc031gs_start_streaming(struct sc_camera_module *cam_mod) +{ + int ret = 0; + + sc_camera_module_pr_info(cam_mod, "active config=%s\n", + cam_mod->active_config->name); + + ret = sc031gs_g_vts(cam_mod, &cam_mod->vts_min); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = sc_camera_module_write_reg(cam_mod, 0x0100, 0x01); + if (IS_ERR_VALUE(ret)) + goto err; + + return 0; +err: + sc_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +static int sc031gs_stop_streaming(struct sc_camera_module *cam_mod) +{ + int ret = 0; + + sc_camera_module_pr_info(cam_mod, "\n"); + + ret = sc_camera_module_write_reg(cam_mod, 0x0100, 0x00); + + if (IS_ERR_VALUE(ret)) + goto err; + + return 0; +err: + sc_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +static int sc031gs_check_camera_id(struct sc_camera_module *cam_mod) +{ + u32 pidh, pidl; + int ret = 0; + + sc_camera_module_pr_debug(cam_mod, "\n"); + + ret |= sc_camera_module_read_reg(cam_mod, 1, SC031GS_PIDH_ADDR, &pidh); + ret |= sc_camera_module_read_reg(cam_mod, 1, SC031GS_PIDL_ADDR, &pidl); + + if (IS_ERR_VALUE(ret)) { + sc_camera_module_pr_err(cam_mod, + "register read failed, camera module powered off?\n"); + goto err; + } + + if (pidh == SC031GS_PIDH_MAGIC && pidl == SC031GS_PIDL_MAGIC) { + sc_camera_module_pr_info(cam_mod, + "successfully detected camera ID 0x%02x%02x\n", + pidh, pidl); + } else { + sc_camera_module_pr_err(cam_mod, + "wrong camera ID, expected 0x%02x%02x, detected 0x%02x%02x\n", + SC031GS_PIDH_MAGIC, SC031GS_PIDL_MAGIC, pidh, pidl); + ret = -EINVAL; + goto err; + } + + return 0; +err: + sc_camera_module_pr_err(cam_mod, "failed with error (%d)\n", ret); + return ret; +} + +/* ======================================================================== */ +/* This part is platform dependent */ +/* ======================================================================== */ + +static struct v4l2_subdev_core_ops sc031gs_camera_module_core_ops = { + .g_ctrl = sc_camera_module_g_ctrl, + .s_ctrl = sc_camera_module_s_ctrl, + .s_ext_ctrls = sc_camera_module_s_ext_ctrls, + .s_power = sc_camera_module_s_power, + .ioctl = sc_camera_module_ioctl +}; + +static struct v4l2_subdev_video_ops sc031gs_camera_module_video_ops = { + .s_frame_interval = sc_camera_module_s_frame_interval, + .g_frame_interval = sc_camera_module_g_frame_interval, + .s_stream = sc_camera_module_s_stream +}; + +static struct v4l2_subdev_pad_ops sc031gs_camera_module_pad_ops = { + .enum_frame_interval = sc_camera_module_enum_frameintervals, + .get_fmt = sc_camera_module_g_fmt, + .set_fmt = sc_camera_module_s_fmt, +}; + +static struct v4l2_subdev_ops sc031gs_camera_module_ops = { + .core = &sc031gs_camera_module_core_ops, + .video = &sc031gs_camera_module_video_ops, + .pad = &sc031gs_camera_module_pad_ops, +}; + +static struct sc_camera_module_custom_config sc031gs_custom_config = { + .start_streaming = sc031gs_start_streaming, + .stop_streaming = sc031gs_stop_streaming, + .s_ctrl = sc031gs_s_ctrl, + .g_ctrl = sc031gs_g_ctrl, + .s_ext_ctrls = sc031gs_s_ext_ctrls, + .g_timings = sc031gs_g_timings, + .set_flip = sc031gs_set_flip, + .s_vts = sc031gs_auto_adjust_fps, + .check_camera_id = sc031gs_check_camera_id, + .configs = sc031gs_configs, + .num_configs = ARRAY_SIZE(sc031gs_configs), + .power_up_delays_ms = {5, 30, 30}, + /* + *0: Exposure time valid fields; + *1: Exposure gain valid fields; + *(2 fields == 1 frames) + */ + .exposure_valid_frame = {4, 4} +}; + +static int sc031gs_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + dev_info(&client->dev, "probing...\n"); + sc031gs_filltimings(&sc031gs_custom_config); + + v4l2_i2c_subdev_init(&sc031gs.sd, client, + &sc031gs_camera_module_ops); + sc031gs.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sc031gs.custom = sc031gs_custom_config; + mutex_init(&sc031gs.lock); + dev_info(&client->dev, "probing successful\n"); + + return 0; +} + +static int sc031gs_remove(struct i2c_client *client) +{ + struct sc_camera_module *cam_mod = i2c_get_clientdata(client); + + dev_info(&client->dev, "removing device...\n"); + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + mutex_destroy(&cam_mod->lock); + sc_camera_module_release(cam_mod); + + dev_info(&client->dev, "removed\n"); + return 0; +} + +static const struct i2c_device_id sc031gs_id[] = { + { SC031GS_DRIVER_NAME, 0 }, + { } +}; + +static const struct of_device_id sc031gs_of_match[] = { + {.compatible = "smartsens,sc031gs-v4l2-i2c-subdev"}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, sc031gs_id); + +static struct i2c_driver sc031gs_i2c_driver = { + .driver = { + .name = SC031GS_DRIVER_NAME, + .of_match_table = sc031gs_of_match + }, + .probe = sc031gs_probe, + .remove = sc031gs_remove, + .id_table = sc031gs_id, +}; + +module_i2c_driver(sc031gs_i2c_driver); + +MODULE_DESCRIPTION("SoC Camera driver for sc031gs"); +MODULE_AUTHOR("zack.zeng"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/i2c/soc_camera/rockchip/sc_camera_module.c b/drivers/media/i2c/soc_camera/rockchip/sc_camera_module.c new file mode 100644 index 000000000000..00bb5bcbb500 --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/sc_camera_module.c @@ -0,0 +1,1301 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sc_camera_module.c + * + * Generic omnivision sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + */ + +#include <linux/delay.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-device.h> +#include <media/videobuf-core.h> +#include <linux/slab.h> +#include <linux/gcd.h> +#include <media/v4l2-controls_rockchip.h> + +#include "sc_camera_module.h" + +static struct sc_camera_module *to_sc_camera_module(struct v4l2_subdev *sd) +{ + return container_of(sd, struct sc_camera_module, sd); +} + +/* ======================================================================== */ + +static void sc_camera_module_reset(struct sc_camera_module *cam_mod) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + cam_mod->inited = false; + cam_mod->active_config = NULL; + cam_mod->update_config = true; + cam_mod->frm_fmt_valid = false; + cam_mod->frm_intrvl_valid = false; + cam_mod->exp_config.auto_exp = false; + cam_mod->exp_config.auto_gain = false; + cam_mod->wb_config.auto_wb = false; + cam_mod->auto_adjust_fps = true; + cam_mod->rotation = 0; + cam_mod->ctrl_updt = 0; + cam_mod->state = SC_CAMERA_MODULE_POWER_OFF; + cam_mod->state_before_suspend = SC_CAMERA_MODULE_POWER_OFF; + cam_mod->exp_config.exp_time = 0; + cam_mod->exp_config.gain = 0; + cam_mod->vts_cur = 0; +} + +/* ======================================================================== */ + +static void sc_camera_module_set_active_config(struct sc_camera_module + *cam_mod, struct sc_camera_module_config *new_config) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (IS_ERR_OR_NULL(new_config)) { + cam_mod->active_config = new_config; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "no active config\n"); + } else { + cam_mod->ctrl_updt &= SC_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP | + SC_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN | + SC_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + if (new_config->auto_exp_enabled != + cam_mod->exp_config.auto_exp) { + cam_mod->ctrl_updt |= + SC_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP; + cam_mod->exp_config.auto_exp = + new_config->auto_exp_enabled; + } + if (new_config->auto_gain_enabled != + cam_mod->exp_config.auto_gain) { + cam_mod->ctrl_updt |= + SC_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN; + cam_mod->exp_config.auto_gain = + new_config->auto_gain_enabled; + } + if (new_config->auto_wb_enabled != + cam_mod->wb_config.auto_wb) { + cam_mod->ctrl_updt |= + SC_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + cam_mod->wb_config.auto_wb = + new_config->auto_wb_enabled; + } + if (new_config != cam_mod->active_config) { + cam_mod->update_config = true; + cam_mod->active_config = new_config; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "activating config '%s'\n", + cam_mod->active_config->name); + } + } +} + +/* ======================================================================== */ + +static struct sc_camera_module_config *sc_camera_module_find_config( + struct sc_camera_module *cam_mod, + struct v4l2_mbus_framefmt *fmt, + struct v4l2_subdev_frame_interval *frm_intrvl) +{ + u32 i; + unsigned long gcdiv; + struct v4l2_subdev_frame_interval norm_interval; + + if (!IS_ERR_OR_NULL(fmt)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (!IS_ERR_OR_NULL(frm_intrvl)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "frame interval %d/%d\n", + frm_intrvl->interval.numerator, + frm_intrvl->interval.denominator); + + for (i = 0; i < cam_mod->custom.num_configs; i++) { + if (!IS_ERR_OR_NULL(frm_intrvl)) { + gcdiv = gcd(cam_mod->custom.configs[i].frm_intrvl.interval.numerator, + cam_mod->custom.configs[i].frm_intrvl.interval.denominator); + norm_interval.interval.numerator = + cam_mod->custom.configs[i].frm_intrvl.interval.numerator / + gcdiv; + norm_interval.interval.denominator = + cam_mod->custom.configs[i].frm_intrvl.interval.denominator / + gcdiv; + if ((frm_intrvl->interval.numerator != + norm_interval.interval.numerator) || + (frm_intrvl->interval.denominator != + norm_interval.interval.denominator)) + continue; + } + if (!IS_ERR_OR_NULL(fmt)) { + if ((cam_mod->custom.configs[i].frm_fmt.width != + fmt->width) || + (cam_mod->custom.configs[i].frm_fmt.height != + fmt->height) || + (cam_mod->custom.configs[i].frm_fmt.code != + fmt->code)) { + continue; + } + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "found matching config %s\n", + cam_mod->custom.configs[i].name); + return &cam_mod->custom.configs[i]; + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "no matching config found\n"); + + return ERR_PTR(-EINVAL); +} + +/* ======================================================================== */ + +static int sc_camera_module_write_config(struct sc_camera_module *cam_mod) +{ + int ret = 0; + struct sc_camera_module_reg *reg_table; + u32 reg_table_num_entries; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active sensor configuration"); + ret = -EFAULT; + goto err; + } + + if (!cam_mod->inited) { + cam_mod->active_config->soft_reset = true; + reg_table = cam_mod->active_config->reg_table; + reg_table_num_entries = + cam_mod->active_config->reg_table_num_entries; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "write config %s\n", + cam_mod->active_config->name); + } else { + if (cam_mod->active_config->reg_diff_table && + cam_mod->active_config->reg_diff_table_num_entries) { + cam_mod->active_config->soft_reset = false; + reg_table = cam_mod->active_config->reg_diff_table; + reg_table_num_entries = + cam_mod->active_config->reg_diff_table_num_entries; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "write config %s%s\n", + cam_mod->active_config->name, "_diff"); + } else { + cam_mod->active_config->soft_reset = true; + reg_table = cam_mod->active_config->reg_table; + reg_table_num_entries = + cam_mod->active_config->reg_table_num_entries; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "write config %s\n", + cam_mod->active_config->name); + } + } + + if (!IS_ERR_OR_NULL(cam_mod->custom.set_flip)) + cam_mod->custom.set_flip(cam_mod, + reg_table, reg_table_num_entries); + + ret = pltfrm_camera_module_write_reglist(&cam_mod->sd, + reg_table, reg_table_num_entries); + if (IS_ERR_VALUE(ret)) + goto err; + ret = pltfrm_camera_module_patch_config(&cam_mod->sd, + &cam_mod->frm_fmt, + &cam_mod->frm_intrvl); + if (IS_ERR_VALUE(ret)) + goto err; + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +static int sc_camera_module_attach(struct sc_camera_module *cam_mod) +{ + int ret = 0; + struct sc_camera_module_custom_config *custom; + + custom = &cam_mod->custom; + + if (custom->check_camera_id) { + sc_camera_module_s_power(&cam_mod->sd, 1); + ret = custom->check_camera_id(cam_mod); + sc_camera_module_s_power(&cam_mod->sd, 0); + if (ret != 0) + goto err; + } + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + sc_camera_module_release(cam_mod); + return ret; +} + +/* ======================================================================== */ + +int sc_camera_module_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct sc_camera_module *cam_mod = to_sc_camera_module(sd); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (IS_ERR_OR_NULL(sc_camera_module_find_config(cam_mod, fmt, NULL))) { + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "format not supported\n"); + return -EINVAL; + } + pltfrm_camera_module_pr_debug(&cam_mod->sd, "format supported\n"); + + return 0; +} + +/* ======================================================================== */ + +int sc_camera_module_s_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct sc_camera_module *cam_mod = to_sc_camera_module(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%dx%d, fmt code 0x%04x\n", + fmt->width, fmt->height, fmt->code); + + if (IS_ERR_OR_NULL(sc_camera_module_find_config(cam_mod, fmt, NULL))) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "format %dx%d, code 0x%04x, not supported\n", + fmt->width, fmt->height, fmt->code); + ret = -EINVAL; + goto err; + } + cam_mod->frm_fmt_valid = true; + cam_mod->frm_fmt = *fmt; + if (cam_mod->frm_intrvl_valid) { + sc_camera_module_set_active_config(cam_mod, + sc_camera_module_find_config(cam_mod, + fmt, &cam_mod->frm_intrvl)); + } else { + sc_camera_module_set_active_config(cam_mod, + sc_camera_module_find_config(cam_mod, + fmt, NULL)); + } + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int sc_camera_module_g_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct sc_camera_module *cam_mod = to_sc_camera_module(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (cam_mod->active_config) { + fmt->code = cam_mod->active_config->frm_fmt.code; + fmt->width = cam_mod->active_config->frm_fmt.width; + fmt->height = cam_mod->active_config->frm_fmt.height; + return 0; + } + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "no active config\n"); + + return -1; +} + +/* ======================================================================== */ + +int sc_camera_module_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval) +{ + struct sc_camera_module *cam_mod = to_sc_camera_module(sd); + unsigned long gcdiv; + struct v4l2_subdev_frame_interval norm_interval; + struct sc_camera_module_config *config; + unsigned int vts; + int ret = 0; + + if (interval->interval.denominator == 0 || + interval->interval.numerator == 0) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "invalid frame interval %d/%d\n", + interval->interval.numerator, + interval->interval.denominator); + ret = -EINVAL; + goto err; + } + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d/%d (%dfps)\n", + interval->interval.numerator, interval->interval.denominator, + (interval->interval.denominator + + (interval->interval.numerator >> 1)) / + interval->interval.numerator); + + /* normalize interval */ + gcdiv = gcd(interval->interval.numerator, + interval->interval.denominator); + norm_interval.interval.numerator = + interval->interval.numerator / gcdiv; + norm_interval.interval.denominator = + interval->interval.denominator / gcdiv; + + if (!cam_mod->frm_fmt_valid) + goto end; + config = sc_camera_module_find_config(cam_mod, + &cam_mod->active_config->frm_fmt, + &norm_interval); + + if (!IS_ERR_OR_NULL(config) && config != cam_mod->active_config) { + sc_camera_module_set_active_config(cam_mod, config); + if (cam_mod->state == SC_CAMERA_MODULE_STREAMING) { + cam_mod->custom.stop_streaming(cam_mod); + sc_camera_module_write_config(cam_mod); + cam_mod->custom.start_streaming(cam_mod); + } + } else { + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active sensor configuration"); + ret = -EFAULT; + goto err; + } + if (cam_mod->active_config->frm_intrvl.interval.denominator < + norm_interval.interval.denominator) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "%dx%d@%dfps isn't support!", + cam_mod->active_config->frm_fmt.width, + cam_mod->active_config->frm_fmt.height, + norm_interval.interval.denominator); + ret = -EFAULT; + goto err; + } + if (!cam_mod->custom.s_vts) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "custom.s_vts isn't support!"); + ret = -EFAULT; + goto err; + } + + vts = cam_mod->active_config->timings.frame_length_lines; + vts *= cam_mod->active_config->frm_intrvl.interval.denominator; + vts /= norm_interval.interval.denominator; + cam_mod->vts_cur = vts; + + if (cam_mod->state != SC_CAMERA_MODULE_STREAMING) + goto end; + + cam_mod->custom.s_vts(cam_mod, vts); + } + +end: + cam_mod->frm_intrvl_valid = true; + cam_mod->frm_intrvl = norm_interval; + cam_mod->auto_adjust_fps = false; + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +int sc_camera_module_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval) +{ + struct sc_camera_module *cam_mod = to_sc_camera_module(sd); + + if (cam_mod->active_config) { + if (cam_mod->state == SC_CAMERA_MODULE_STREAMING) { + if (cam_mod->frm_intrvl_valid) { + *interval = cam_mod->frm_intrvl; + return 0; + } else { + *interval = cam_mod->active_config->frm_intrvl; + return 0; + } + } + } + + return -EFAULT; +} + +/* ======================================================================== */ + +int sc_camera_module_s_stream(struct v4l2_subdev *sd, int enable) +{ + int ret = 0; + struct sc_camera_module *cam_mod = to_sc_camera_module(sd); + unsigned int vts; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", enable); + + if (enable) { + if (cam_mod->state == SC_CAMERA_MODULE_STREAMING) + return 0; + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active sensor configuration, cannot start streaming\n"); + ret = -EFAULT; + goto err; + } + if (cam_mod->state != SC_CAMERA_MODULE_SW_STANDBY) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "sensor is not powered on (in state %d), cannot start streaming\n", + cam_mod->state); + ret = -EINVAL; + goto err; + } + if (cam_mod->update_config) { + ret = sc_camera_module_write_config(cam_mod); + if (IS_ERR_VALUE(ret)) + goto err; + } + + ret = cam_mod->custom.start_streaming(cam_mod); + if (IS_ERR_VALUE(ret)) + goto err; + + if (cam_mod->frm_intrvl_valid) { + if ((cam_mod->frm_intrvl.interval.numerator != + cam_mod->active_config->frm_intrvl.interval.numerator) || + (cam_mod->frm_intrvl.interval.denominator != + cam_mod->active_config->frm_intrvl.interval.denominator)) { + if (cam_mod->frm_intrvl.interval.denominator > + cam_mod->active_config->frm_intrvl.interval.denominator) { + pltfrm_camera_module_pr_warn(&cam_mod->sd, + "sensor is not support stream: %dx%d@(%d/%d)fps!\n", + cam_mod->active_config->frm_fmt.width, + cam_mod->active_config->frm_fmt.height, + cam_mod->frm_intrvl.interval.denominator, + cam_mod->frm_intrvl.interval.numerator); + goto end; + } + vts = cam_mod->active_config->timings.frame_length_lines; + vts *= cam_mod->active_config->frm_intrvl.interval.denominator; + vts /= cam_mod->frm_intrvl.interval.denominator; + cam_mod->custom.s_vts(cam_mod, vts); + } + } + + if (!cam_mod->inited && cam_mod->update_config) + cam_mod->inited = true; + cam_mod->update_config = false; + cam_mod->ctrl_updt = 0; + mdelay(cam_mod->custom.power_up_delays_ms[2]); + cam_mod->state = SC_CAMERA_MODULE_STREAMING; + } else { + int pclk; + int wait_ms; + struct isp_supplemental_sensor_mode_data timings; + + if (cam_mod->state != SC_CAMERA_MODULE_STREAMING) + return 0; + ret = cam_mod->custom.stop_streaming(cam_mod); + if (IS_ERR_VALUE(ret)) + goto err; + + ret = sc_camera_module_ioctl(sd, + RK_VIDIOC_SENSOR_MODE_DATA, + &timings); + + cam_mod->state = SC_CAMERA_MODULE_SW_STANDBY; + + if (IS_ERR_VALUE(ret)) + goto err; + + pclk = timings.vt_pix_clk_freq_hz / 1000; + + if (!pclk) + goto err; + + wait_ms = + (timings.line_length_pck * + timings.frame_length_lines) / + pclk; + + /* + * wait for a frame period to make sure that there is + * no pending frame left. + */ + + msleep(wait_ms + 1); + } + +end: + cam_mod->state_before_suspend = cam_mod->state; + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int sc_camera_module_s_power(struct v4l2_subdev *sd, int on) +{ + int ret = 0; + struct sc_camera_module *cam_mod = to_sc_camera_module(sd); + struct v4l2_subdev *af_ctrl; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", on); + + if (on) { + if (cam_mod->state == SC_CAMERA_MODULE_POWER_OFF) { + ret = pltfrm_camera_module_s_power(&cam_mod->sd, 1); + if (!IS_ERR_VALUE(ret)) { + mdelay(cam_mod->custom.power_up_delays_ms[0]); + cam_mod->state = SC_CAMERA_MODULE_HW_STANDBY; + } + } + if (cam_mod->state == SC_CAMERA_MODULE_HW_STANDBY) { + ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + if (!IS_ERR_VALUE(ret)) { + mdelay(cam_mod->custom.power_up_delays_ms[1]); + cam_mod->state = SC_CAMERA_MODULE_SW_STANDBY; + if (!IS_ERR_OR_NULL(cam_mod->custom.init_common) && + cam_mod->custom.init_common(cam_mod)) + usleep_range(1000, 1500); + + af_ctrl = pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + v4l2_subdev_call(af_ctrl, + core, init, 0); + } + } + } + if (cam_mod->update_config) { + sc_camera_module_write_config(cam_mod); + cam_mod->update_config = false; + } + } else { + if (cam_mod->state == SC_CAMERA_MODULE_STREAMING) { + ret = sc_camera_module_s_stream(sd, 0); + if (!IS_ERR_VALUE(ret)) + cam_mod->state = SC_CAMERA_MODULE_SW_STANDBY; + } + if (cam_mod->state == SC_CAMERA_MODULE_SW_STANDBY) { + ret = pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + + if (!IS_ERR_VALUE(ret)) + cam_mod->state = SC_CAMERA_MODULE_HW_STANDBY; + } + if (cam_mod->state == SC_CAMERA_MODULE_HW_STANDBY) { + ret = pltfrm_camera_module_s_power(&cam_mod->sd, 0); + if (!IS_ERR_VALUE(ret)) { + cam_mod->state = SC_CAMERA_MODULE_POWER_OFF; + sc_camera_module_reset(cam_mod); + } + } + } + + cam_mod->state_before_suspend = cam_mod->state; + + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "%s failed, camera left in state %d\n", + on ? "on" : "off", cam_mod->state); + goto err; + } else { + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "camera powered %s\n", on ? "on" : "off"); + } + + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +/* ======================================================================== */ + +int sc_camera_module_g_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct sc_camera_module *cam_mod = to_sc_camera_module(sd); + int ret; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, " id 0x%x\n", ctrl->id); + + if (ctrl->id == V4L2_CID_FLASH_LED_MODE) { + ctrl->value = cam_mod->exp_config.flash_mode; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FLASH_LED_MODE %d\n", + ctrl->value); + return 0; + } + + if (ctrl->id == V4L2_CID_HFLIP) { + ctrl->value = cam_mod->hflip; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_HFLIP %d\n", + ctrl->value); + return 0; + } + + if (ctrl->id == V4L2_CID_VFLIP) { + ctrl->value = cam_mod->vflip; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_VFLIP %d\n", + ctrl->value); + return 0; + } + + if (IS_ERR_OR_NULL(cam_mod->active_config)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "no active configuration\n"); + return -EFAULT; + } + + if (ctrl->id == RK_V4L2_CID_VBLANKING) { + ctrl->value = cam_mod->active_config->v_blanking_time_us; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "RK_V4L2_CID_VBLANKING %d\n", + ctrl->value); + return 0; + } + + if (cam_mod->state != SC_CAMERA_MODULE_SW_STANDBY && + cam_mod->state != SC_CAMERA_MODULE_STREAMING) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "cannot get controls when camera is off\n"); + return -EFAULT; + } + + if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) { + struct v4l2_subdev *af_ctrl; + + af_ctrl = pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + ret = v4l2_subdev_call(af_ctrl, core, g_ctrl, ctrl); + return ret; + } + } + + if (!IS_ERR_OR_NULL(cam_mod->custom.g_ctrl)) { + ret = cam_mod->custom.g_ctrl(cam_mod, ctrl->id); + if (IS_ERR_VALUE(ret)) + return ret; + } + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl->value = cam_mod->exp_config.gain; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_GAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE: + ctrl->value = cam_mod->exp_config.exp_time; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE %d\n", + ctrl->value); + break; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + ctrl->value = cam_mod->wb_config.temperature; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ctrl->value = cam_mod->wb_config.preset_id; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + ctrl->value = cam_mod->exp_config.auto_gain; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTOGAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl->value = cam_mod->exp_config.auto_exp; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE_AUTO %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl->value = cam_mod->wb_config.auto_wb; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_WHITE_BALANCE %d\n", + ctrl->value); + break; + case V4L2_CID_FOCUS_ABSOLUTE: + ctrl->value = cam_mod->af_config.abs_pos; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FOCUS_ABSOLUTE %d\n", + ctrl->value); + break; + default: + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "failed, unknown ctrl %d\n", ctrl->id); + return -EINVAL; + } + + return 0; +} + +static int flash_light_ctrl(struct v4l2_subdev *sd, + struct sc_camera_module *cam_mod, + int value) +{ + return 0; +} + +/* ======================================================================== */ + +int sc_camera_module_s_ext_ctrls(struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls) +{ + u32 i; + u32 ctrl_cnt = 0; + struct sc_camera_module *cam_mod = to_sc_camera_module(sd); + int ret = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + if (ctrls->count == 0) + return -EINVAL; + + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl; + u32 ctrl_updt = 0; + + ctrl = &ctrls->controls[i]; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl_updt = SC_CAMERA_MODULE_CTRL_UPDT_GAIN; + cam_mod->exp_config.gain = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_GAIN %d\n", + ctrl->value); + break; + case RK_V4L2_CID_GAIN_PERCENT: + ctrl_updt = SC_CAMERA_MODULE_CTRL_UPDT_GAIN; + cam_mod->exp_config.gain_percent = ctrl->value; + break; + case V4L2_CID_FLASH_LED_MODE: + ret = flash_light_ctrl(sd, cam_mod, ctrl->value); + if (ret == 0) { + cam_mod->exp_config.flash_mode = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FLASH_LED_MODE %d\n", + ctrl->value); + } + break; + case V4L2_CID_EXPOSURE: + ctrl_updt = SC_CAMERA_MODULE_CTRL_UPDT_EXP_TIME; + cam_mod->exp_config.exp_time = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE %d\n", + ctrl->value); + break; + case RK_V4L2_CID_VTS: + ctrl_updt = SC_CAMERA_MODULE_CTRL_UPDT_VTS_VALUE; + cam_mod->exp_config.vts_value = ctrl->value; + break; + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + ctrl_updt = SC_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE; + cam_mod->wb_config.temperature = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_WHITE_BALANCE_TEMPERATURE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ctrl_updt = SC_CAMERA_MODULE_CTRL_UPDT_PRESET_WB; + cam_mod->wb_config.preset_id = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE %d\n", + ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + ctrl_updt = SC_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN; + cam_mod->exp_config.auto_gain = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTOGAIN %d\n", + ctrl->value); + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl_updt = SC_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP; + cam_mod->exp_config.auto_exp = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_EXPOSURE_AUTO %d\n", + ctrl->value); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl_updt = SC_CAMERA_MODULE_CTRL_UPDT_AUTO_WB; + cam_mod->wb_config.auto_wb = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_AUTO_WHITE_BALANCE %d\n", + ctrl->value); + break; + case RK_V4L2_CID_AUTO_FPS: + cam_mod->auto_adjust_fps = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "RK_V4L2_CID_AUTO_FPS %d\n", + ctrl->value); + break; + case V4L2_CID_FOCUS_ABSOLUTE: + { + struct v4l2_subdev *af_ctrl; + + af_ctrl = pltfrm_camera_module_get_af_ctrl(sd); + if (!IS_ERR_OR_NULL(af_ctrl)) { + struct v4l2_control single_ctrl; + + single_ctrl.id = + V4L2_CID_FOCUS_ABSOLUTE; + single_ctrl.value = ctrl->value; + ret = v4l2_subdev_call(af_ctrl, + core, s_ctrl, &single_ctrl); + return ret; + } + } + ctrl_updt = + SC_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE; + cam_mod->af_config.abs_pos = ctrl->value; + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "V4L2_CID_FOCUS_ABSOLUTE %d\n", + ctrl->value); + break; + case V4L2_CID_HFLIP: + if (ctrl->value) + cam_mod->hflip = true; + else + cam_mod->hflip = false; + cam_mod->flip_flg = true; + break; + case V4L2_CID_VFLIP: + if (ctrl->value) + cam_mod->vflip = true; + else + cam_mod->vflip = false; + cam_mod->flip_flg = true; + break; + default: + pltfrm_camera_module_pr_warn(&cam_mod->sd, + "ignoring unknown ctrl 0x%x\n", ctrl->id); + break; + } + + if (cam_mod->state != SC_CAMERA_MODULE_SW_STANDBY && + cam_mod->state != SC_CAMERA_MODULE_STREAMING) + cam_mod->ctrl_updt |= ctrl_updt; + else if (ctrl_updt) + ctrl_cnt++; + } + + /* if camera module is already streaming, write through */ + if (ctrl_cnt && + (cam_mod->state == SC_CAMERA_MODULE_STREAMING || + cam_mod->state == SC_CAMERA_MODULE_SW_STANDBY)) { + struct sc_camera_module_ext_ctrls sc_ctrls; + + sc_ctrls.ctrls = kmalloc_array(ctrls->count, + sizeof(*sc_ctrls.ctrls), + GFP_KERNEL); + + if (sc_ctrls.ctrls) { + for (i = 0; i < ctrl_cnt; i++) { + sc_ctrls.ctrls[i].id = ctrls->controls[i].id; + sc_ctrls.ctrls[i].value = + ctrls->controls[i].value; + } + + sc_ctrls.count = ctrl_cnt; + + ret = cam_mod->custom.s_ext_ctrls(cam_mod, &sc_ctrls); + + kfree(sc_ctrls.ctrls); + } else { + ret = -ENOMEM; + } + if (IS_ERR_VALUE(ret)) + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "failed with error %d\n", ret); + } + + return ret; +} + +/* ======================================================================== */ + +int sc_camera_module_s_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl) +{ + struct sc_camera_module *cam_mod = to_sc_camera_module(sd); + struct v4l2_ext_control ext_ctrl[1]; + struct v4l2_ext_controls ext_ctrls; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, + "0x%x 0x%x\n", ctrl->id, ctrl->value); + + ext_ctrl[0].id = ctrl->id; + ext_ctrl[0].value = ctrl->value; + + ext_ctrls.count = 1; + ext_ctrls.controls = ext_ctrl; + + return sc_camera_module_s_ext_ctrls(sd, &ext_ctrls); +} + +/* ======================================================================== */ + +long sc_camera_module_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg) +{ + struct sc_camera_module *cam_mod = to_sc_camera_module(sd); + unsigned int ret, i; + unsigned int flag = 0, val; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "cmd: 0x%x\n", cmd); + + if (cmd == RK_VIDIOC_SENSOR_MODE_DATA) { + struct sc_camera_module_timings sc_timings; + struct isp_supplemental_sensor_mode_data *timings = + (struct isp_supplemental_sensor_mode_data *)arg; + + if (cam_mod->custom.g_timings) + ret = cam_mod->custom.g_timings(cam_mod, &sc_timings); + else + ret = -EPERM; + + if (IS_ERR_VALUE(ret)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; + } + + timings->sensor_output_width = sc_timings.sensor_output_width; + timings->sensor_output_height = sc_timings.sensor_output_height; + timings->crop_horizontal_start = + sc_timings.crop_horizontal_start; + timings->crop_vertical_start = sc_timings.crop_vertical_start; + timings->crop_horizontal_end = sc_timings.crop_horizontal_end; + timings->crop_vertical_end = sc_timings.crop_vertical_end; + timings->line_length_pck = sc_timings.line_length_pck; + timings->frame_length_lines = sc_timings.frame_length_lines; + timings->vt_pix_clk_freq_hz = sc_timings.vt_pix_clk_freq_hz; + timings->binning_factor_x = sc_timings.binning_factor_x; + timings->binning_factor_y = sc_timings.binning_factor_y; + timings->coarse_integration_time_max_margin = + sc_timings.coarse_integration_time_max_margin; + timings->coarse_integration_time_min = + sc_timings.coarse_integration_time_min; + timings->fine_integration_time_max_margin = + sc_timings.fine_integration_time_max_margin; + timings->fine_integration_time_min = + sc_timings.fine_integration_time_min; + + timings->exposure_valid_frame[0] = + cam_mod->custom.exposure_valid_frame[0]; + timings->exposure_valid_frame[1] = + cam_mod->custom.exposure_valid_frame[1]; + if (cam_mod->exp_config.exp_time) + timings->exp_time = cam_mod->exp_config.exp_time; + else + timings->exp_time = sc_timings.exp_time; + + if (cam_mod->exp_config.gain) + timings->gain = cam_mod->exp_config.gain; + else + timings->gain = sc_timings.gain; + + if (cam_mod->active_config) { + timings->max_exp_gain_h = + cam_mod->active_config->max_exp_gain_h; + timings->max_exp_gain_l = + cam_mod->active_config->max_exp_gain_l; + } else { + timings->max_exp_gain_h = + cam_mod->custom.configs[0].max_exp_gain_h; + timings->max_exp_gain_l = + cam_mod->custom.configs[0].max_exp_gain_l; + } + return ret; + } else if (cmd == RK_VIDIOC_SENSOR_CONFIGINFO) { + struct sensor_config_info_s *sensor_config = + (struct sensor_config_info_s *)arg; + + sensor_config->config_num = cam_mod->custom.num_configs; + for (i = 0; i < cam_mod->custom.num_configs; i++) { + if (i >= SENSOR_CONFIG_NUM) + break; + sensor_config->sensor_fmt[i] = + pltfrm_camera_module_pix_fmt2csi2_dt(cam_mod->custom.configs[i].frm_fmt.code); + sensor_config->reso[i].width = + cam_mod->custom.configs[i].frm_fmt.width; + sensor_config->reso[i].height = + cam_mod->custom.configs[i].frm_fmt.height; + } + return 0; + } else if (cmd == RK_VIDIOC_SENSOR_REG_ACCESS) { + struct sensor_reg_rw_s *sensor_rw = + (struct sensor_reg_rw_s *)arg; + + if (sensor_rw->reg_access_mode == SENSOR_READ_MODE) { + for (i = 0; i < cam_mod->custom.configs[0].reg_table_num_entries; i++) { + flag = + cam_mod->custom.configs[0].reg_table[i].flag; + if (flag != + PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT) + break; + } + if (flag == PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "Can not get sensor reg type.\n"); + return -EINVAL; + } + sensor_rw->reg_addr_len = + PLTFRM_CAMERA_MODULE_REG_LEN(flag); + sensor_rw->reg_data_len = + PLTFRM_CAMERA_MODULE_DATA_LEN(flag); + pltfrm_camera_module_read_reg_ex(&cam_mod->sd, + 1, flag, sensor_rw->addr, &val); + sensor_rw->data = val; + } else { + flag = (sensor_rw->reg_addr_len << + PLTFRM_CAMERA_MODULE_REG_LEN_BIT); + flag |= (sensor_rw->reg_data_len << + PLTFRM_CAMERA_MODULE_DATA_LEN_BIT); + pltfrm_camera_module_write_reg_ex(&cam_mod->sd, + flag, sensor_rw->addr, sensor_rw->data); + } + return 0; + } else if (cmd == PLTFRM_CIFCAM_G_ITF_CFG) { + struct pltfrm_cam_itf *itf_cfg = (struct pltfrm_cam_itf *)arg; + struct sc_camera_module_config *config; + + if (cam_mod->custom.num_configs <= 0) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "cam_mod->custom.num_configs is NULL, Get interface config failed!\n"); + return -EINVAL; + } + + if (IS_ERR_OR_NULL(cam_mod->active_config)) + config = &cam_mod->custom.configs[0]; + else + config = cam_mod->active_config; + + *itf_cfg = config->itf_cfg; + + pltfrm_camera_module_ioctl(sd, PLTFRM_CIFCAM_G_ITF_CFG, arg); + return 0; + } else if (cmd == PLTFRM_CIFCAM_ATTACH) { + ret = sc_camera_module_init(cam_mod, &cam_mod->custom); + if (!IS_ERR_VALUE(ret)) { + pltfrm_camera_module_ioctl(sd, cmd, arg); + return sc_camera_module_attach(cam_mod); + } else { + sc_camera_module_release(cam_mod); + return ret; + } + } + + ret = pltfrm_camera_module_ioctl(sd, cmd, arg); + return ret; +} + +/* ======================================================================== */ + +int sc_camera_module_get_flip_mirror(struct sc_camera_module *cam_mod) +{ + int mode = 0; + + if (!cam_mod->flip_flg) + return -1; + + if (cam_mod->hflip) + mode |= SC_MIRROR_BIT_MASK; + else + mode &= ~SC_MIRROR_BIT_MASK; + + if (cam_mod->vflip) + mode |= SC_FLIP_BIT_MASK; + else + mode &= ~SC_FLIP_BIT_MASK; + + return mode; +} + +/* ======================================================================== */ + +int sc_camera_module_enum_frameintervals(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct sc_camera_module *cam_mod = to_sc_camera_module(sd); + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "%d\n", fie->index); + + if (fie->index >= cam_mod->custom.num_configs) + return -EINVAL; + fie->code = + cam_mod->custom.configs[fie->index].frm_fmt.code; + fie->width = + cam_mod->custom.configs[fie->index].frm_fmt.width; + fie->height = + cam_mod->custom.configs[fie->index].frm_fmt.height; + fie->interval.numerator = + cam_mod->custom.configs[fie->index].frm_intrvl.interval.numerator; + fie->interval.denominator = + cam_mod->custom.configs[fie->index].frm_intrvl.interval.denominator; + return 0; +} + +/* ======================================================================== */ + +int sc_camera_module_write_reglist(struct sc_camera_module *cam_mod, + const struct sc_camera_module_reg reglist[], + int len) +{ + return pltfrm_camera_module_write_reglist(&cam_mod->sd, reglist, len); +} + +/* ======================================================================== */ + +int sc_camera_module_write_reg(struct sc_camera_module *cam_mod, + u16 reg, + u8 val) +{ + return pltfrm_camera_module_write_reg(&cam_mod->sd, reg, val); +} + +/* ======================================================================== */ + +int sc_camera_module_read_reg(struct sc_camera_module *cam_mod, + u16 data_length, + u16 reg, + u32 *val) +{ + return pltfrm_camera_module_read_reg(&cam_mod->sd, + data_length, reg, val); +} + +/* ======================================================================== */ + +int sc_camera_module_read_reg_table(struct sc_camera_module *cam_mod, + u16 reg, + u32 *val) +{ + int i; + + if (cam_mod->state == SC_CAMERA_MODULE_STREAMING) + return pltfrm_camera_module_read_reg(&cam_mod->sd, + 1, reg, val); + + if (!IS_ERR_OR_NULL(cam_mod->active_config)) { + for (i = cam_mod->active_config->reg_table_num_entries - 1; + i > 0; + i--) { + if (cam_mod->active_config->reg_table[i].reg == reg) { + *val = cam_mod->active_config->reg_table[i].val; + return 0; + } + } + } + + if (cam_mod->state == SC_CAMERA_MODULE_SW_STANDBY) + return pltfrm_camera_module_read_reg(&cam_mod->sd, + 1, reg, val); + + return -EFAULT; +} + +int sc_camera_module_init(struct sc_camera_module *cam_mod, + struct sc_camera_module_custom_config *custom) +{ + int ret = 0; + int mode = 0; + + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + cam_mod->hflip = false; + cam_mod->vflip = false; + cam_mod->flip_flg = false; + sc_camera_module_reset(cam_mod); + + if (IS_ERR_OR_NULL(custom->start_streaming) || + IS_ERR_OR_NULL(custom->stop_streaming) || + IS_ERR_OR_NULL(custom->s_ctrl) || + IS_ERR_OR_NULL(custom->g_ctrl)) { + pltfrm_camera_module_pr_err(&cam_mod->sd, + "mandatory callback function is missing\n"); + ret = -EINVAL; + goto err; + } + + ret = pltfrm_camera_module_init(&cam_mod->sd, &cam_mod->pltfm_data); + if (IS_ERR_VALUE(ret)) + goto err; + + ret |= pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_PD, + PLTFRM_CAMERA_MODULE_PIN_STATE_INACTIVE); + ret |= pltfrm_camera_module_set_pin_state(&cam_mod->sd, + PLTFRM_CAMERA_MODULE_PIN_RESET, + PLTFRM_CAMERA_MODULE_PIN_STATE_ACTIVE); + if (IS_ERR_VALUE(ret)) { + sc_camera_module_release(cam_mod); + goto err; + } + + mode = pltfrm_camera_module_get_flip_mirror(&cam_mod->sd); + if (mode != -1) { + cam_mod->hflip = mode & SC_MIRROR_BIT_MASK ? true : false; + cam_mod->vflip = mode & SC_FLIP_BIT_MASK ? true : false; + cam_mod->flip_flg = true; + } + return 0; +err: + pltfrm_camera_module_pr_err(&cam_mod->sd, + "failed with error %d\n", ret); + return ret; +} + +void sc_camera_module_release(struct sc_camera_module *cam_mod) +{ + pltfrm_camera_module_pr_debug(&cam_mod->sd, "\n"); + + if (cam_mod->otp_work.wq) { + flush_workqueue(cam_mod->otp_work.wq); + destroy_workqueue(cam_mod->otp_work.wq); + cam_mod->otp_work.wq = NULL; + } + + cam_mod->custom.configs = NULL; + + pltfrm_camera_module_release(&cam_mod->sd); + v4l2_device_unregister_subdev(&cam_mod->sd); +} diff --git a/drivers/media/i2c/soc_camera/rockchip/sc_camera_module.h b/drivers/media/i2c/soc_camera/rockchip/sc_camera_module.h new file mode 100644 index 000000000000..a0cf581522bd --- /dev/null +++ b/drivers/media/i2c/soc_camera/rockchip/sc_camera_module.h @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * sc_camera_module.h + * + * Generic omnivision sensor driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd. + * + * Copyright (C) 2012-2014 Intel Mobile Communications GmbH + * + * Copyright (C) 2008 Texas Instruments. + * + */ + +#ifndef SC_CAMERA_MODULE_H +#define SC_CAMERA_MODULE_H +#include <linux/workqueue.h> +#include <linux/platform_data/rk_isp10_platform_camera_module.h> +#include <linux/platform_data/rk_isp10_platform.h> + +/* + * TODO: references to v4l2 should be reomved from here and go into a + * platform dependent wrapper + */ + +#define SC_CAMERA_MODULE_REG_TYPE_DATA PLTFRM_CAMERA_MODULE_REG_TYPE_DATA +#define SC_CAMERA_MODULE_REG_TYPE_TIMEOUT PLTFRM_CAMERA_MODULE_REG_TYPE_TIMEOUT +#define sc_camera_module_csi_config +#define sc_camera_module_reg pltfrm_camera_module_reg +#define SC_FLIP_BIT_MASK (1 << PLTFRM_CAMERA_MODULE_FLIP_BIT) +#define SC_MIRROR_BIT_MASK (1 << PLTFRM_CAMERA_MODULE_MIRROR_BIT) + +#define SC_CAMERA_MODULE_CTRL_UPDT_GAIN 0x01 +#define SC_CAMERA_MODULE_CTRL_UPDT_EXP_TIME 0x02 +#define SC_CAMERA_MODULE_CTRL_UPDT_WB_TEMPERATURE 0x04 +#define SC_CAMERA_MODULE_CTRL_UPDT_AUTO_WB 0x08 +#define SC_CAMERA_MODULE_CTRL_UPDT_AUTO_GAIN 0x10 +#define SC_CAMERA_MODULE_CTRL_UPDT_AUTO_EXP 0x20 +#define SC_CAMERA_MODULE_CTRL_UPDT_FOCUS_ABSOLUTE 0x40 +#define SC_CAMERA_MODULE_CTRL_UPDT_PRESET_WB 0x80 +#define SC_CAMERA_MODULE_CTRL_UPDT_VTS_VALUE 0x100 + +enum sc_camera_module_state { + SC_CAMERA_MODULE_POWER_OFF = 0, + SC_CAMERA_MODULE_HW_STANDBY = 1, + SC_CAMERA_MODULE_SW_STANDBY = 2, + SC_CAMERA_MODULE_STREAMING = 3 +}; + +struct sc_camera_module; + +struct sc_camera_module_timings { + /* public */ + u32 coarse_integration_time_min; + u32 coarse_integration_time_max_margin; + u32 fine_integration_time_min; + u32 fine_integration_time_max_margin; + u32 frame_length_lines; + u32 line_length_pck; + u32 vt_pix_clk_freq_hz; + u32 sensor_output_width; + u32 sensor_output_height; + u32 crop_horizontal_start; /* Sensor crop start cord. (x0,y0) */ + u32 crop_vertical_start; + u32 crop_horizontal_end; /* Sensor crop end cord. (x1,y1) */ + u32 crop_vertical_end; + u8 binning_factor_x; + u8 binning_factor_y; + u32 exp_time; + u32 gain; +}; + +struct sc_camera_module_config { + const char *name; + struct v4l2_mbus_framefmt frm_fmt; + struct v4l2_subdev_frame_interval frm_intrvl; + u8 auto_exp_enabled; + u8 auto_gain_enabled; + u8 auto_wb_enabled; + struct sc_camera_module_reg *reg_table; + u32 reg_table_num_entries; + struct sc_camera_module_reg *reg_diff_table; + u32 reg_diff_table_num_entries; + u32 v_blanking_time_us; + u32 line_length_pck; + u32 frame_length_lines; + struct sc_camera_module_timings timings; + u8 soft_reset; + u8 ignore_measurement_check; + u8 max_exp_gain_h; + u8 max_exp_gain_l; + struct pltfrm_cam_itf itf_cfg; +}; + +struct sc_camera_module_exp_config { + s32 exp_time; + u8 auto_exp; + u16 gain; + u16 gain_percent; + u8 auto_gain; + enum v4l2_flash_led_mode flash_mode; + u32 vts_value; +}; + +struct sc_camera_module_wb_config { + u32 temperature; + u32 preset_id; + u8 auto_wb; +}; + +struct sc_camera_module_af_config { + u32 abs_pos; + u32 rel_pos; +}; + +struct sc_camera_module_ext_ctrl { + /* public */ + u32 id; + u32 value; + __u32 reserved2[1]; +}; + +struct sc_camera_module_ext_ctrls { + /* public */ + u32 count; + struct sc_camera_module_ext_ctrl *ctrls; +}; + +/* + * start_streaming: (mandatory) will be called when sensor should be + * put into streaming mode right after the base config has been + * written to the sensor. After a successful call of this function + * the sensor should start delivering frame data. + * + * stop_streaming: (mandatory) will be called when sensor should stop + * delivering data. After a successful call of this function the + * sensor should not deliver any more frame data. + * + * check_camera_id: (optional) will be called when the sensor is + * powered on. If provided should check the sensor ID/version + * required by the custom driver. Register access should be + * possible when this function is invoked. + * + * s_ctrl: (mandatory) will be called at the successful end of + * sc_camera_module_s_ctrl with the ctrl_id as argument. + * + * priv: (optional) for private data used by the custom driver. + */ +struct sc_camera_module_custom_config { + int (*start_streaming)(struct sc_camera_module *cam_mod); + int (*stop_streaming)(struct sc_camera_module *cam_mod); + int (*check_camera_id)(struct sc_camera_module *cam_mod); + int (*s_ctrl)(struct sc_camera_module *cam_mod, u32 ctrl_id); + int (*g_ctrl)(struct sc_camera_module *cam_mod, u32 ctrl_id); + int (*g_timings)(struct sc_camera_module *cam_mod, + struct sc_camera_module_timings *timings); + int (*s_vts)(struct sc_camera_module *cam_mod, + u32 vts); + int (*s_ext_ctrls)(struct sc_camera_module *cam_mod, + struct sc_camera_module_ext_ctrls *ctrls); + int (*set_flip)(struct sc_camera_module *cam_mod, + struct pltfrm_camera_module_reg reglist[], + int len); + int (*init_common)(struct sc_camera_module *cam_mod); + int (*read_otp)(struct sc_camera_module *cam_mod); + struct sc_camera_module_config *configs; + u32 num_configs; + u32 power_up_delays_ms[3]; + unsigned short exposure_valid_frame[2]; + void *priv; +}; + +struct sc_camera_module_otp_work { + struct work_struct work; + struct workqueue_struct *wq; + void *cam_mod; +}; + +struct sc_camera_module { + /* public */ + struct v4l2_subdev sd; + struct v4l2_mbus_framefmt frm_fmt; + struct v4l2_subdev_frame_interval frm_intrvl; + struct sc_camera_module_exp_config exp_config; + struct sc_camera_module_wb_config wb_config; + struct sc_camera_module_af_config af_config; + struct sc_camera_module_custom_config custom; + enum sc_camera_module_state state; + enum sc_camera_module_state state_before_suspend; + struct sc_camera_module_config *active_config; + struct sc_camera_module_otp_work otp_work; + u32 ctrl_updt; + u32 vts_cur; + u32 vts_min; + u8 auto_adjust_fps; + u8 update_config; + u8 frm_fmt_valid; + u8 frm_intrvl_valid; + u8 hflip; + u8 vflip; + u8 flip_flg; + u32 rotation; + void *pltfm_data; + u8 inited; + int as_master; + struct mutex lock; +}; + +#define sc_camera_module_pr_info(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_info(&(cam_mod)->sd, fmt, ## arg) +#define sc_camera_module_pr_debug(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_debug(&(cam_mod)->sd, fmt, ## arg) +#define sc_camera_module_pr_warn(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_warn(&(cam_mod)->sd, fmt, ## arg) +#define sc_camera_module_pr_err(cam_mod, fmt, arg...) \ + pltfrm_camera_module_pr_err(&(cam_mod)->sd, fmt, ## arg) + +int sc_camera_module_write_reglist(struct sc_camera_module *cam_mod, + const struct sc_camera_module_reg reglist[], + int len); + +int sc_camera_module_write_reg(struct sc_camera_module *cam_mod, + u16 reg, + u8 val); + +int sc_camera_module_read_reg(struct sc_camera_module *cam_mod, + u16 data_length, + u16 reg, + u32 *val); + +int sc_camera_module_read_reg_table(struct sc_camera_module *cam_mod, + u16 reg, + u32 *val); + +int sc_camera_module_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt); + +int sc_camera_module_s_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format); + +int sc_camera_module_g_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format); + +int sc_camera_module_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval); + +int sc_camera_module_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *interval); + +int sc_camera_module_s_stream(struct v4l2_subdev *sd, + int enable); + +int sc_camera_module_s_power(struct v4l2_subdev *sd, + int on); + +int sc_camera_module_g_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl); + +int sc_camera_module_s_ctrl(struct v4l2_subdev *sd, + struct v4l2_control *ctrl); + +int sc_camera_module_s_ext_ctrls(struct v4l2_subdev *sd, + struct v4l2_ext_controls *ctrls); + +int sc_camera_module_enum_frameintervals(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie); + +int sc_camera_module_init(struct sc_camera_module *cam_mod, + struct sc_camera_module_custom_config *custom); + +void sc_camera_module_release(struct sc_camera_module *cam_mod); + +long sc_camera_module_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg); + +int sc_camera_module_get_flip_mirror(struct sc_camera_module *cam_mod); +#endif |