diff options
Diffstat (limited to 'drivers/media/i2c')
27 files changed, 2355 insertions, 316 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index cee1dae6e014..fd181c99ce11 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -209,7 +209,6 @@ config VIDEO_ADV7604 depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API depends on GPIOLIB || COMPILE_TEST select HDMI - select MEDIA_CEC_EDID ---help--- Support for the Analog Devices ADV7604 video decoder. @@ -221,7 +220,7 @@ config VIDEO_ADV7604 config VIDEO_ADV7604_CEC bool "Enable Analog Devices ADV7604 CEC support" - depends on VIDEO_ADV7604 && MEDIA_CEC_SUPPORT + depends on VIDEO_ADV7604 && CEC_CORE ---help--- When selected the adv7604 will support the optional HDMI CEC feature. @@ -230,7 +229,6 @@ config VIDEO_ADV7842 tristate "Analog Devices ADV7842 decoder" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API select HDMI - select MEDIA_CEC_EDID ---help--- Support for the Analog Devices ADV7842 video decoder. @@ -242,7 +240,7 @@ config VIDEO_ADV7842 config VIDEO_ADV7842_CEC bool "Enable Analog Devices ADV7842 CEC support" - depends on VIDEO_ADV7842 && MEDIA_CEC_SUPPORT + depends on VIDEO_ADV7842 && CEC_CORE ---help--- When selected the adv7842 will support the optional HDMI CEC feature. @@ -470,7 +468,6 @@ config VIDEO_ADV7511 tristate "Analog Devices ADV7511 encoder" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API select HDMI - select MEDIA_CEC_EDID ---help--- Support for the Analog Devices ADV7511 video encoder. @@ -481,7 +478,7 @@ config VIDEO_ADV7511 config VIDEO_ADV7511_CEC bool "Enable Analog Devices ADV7511 CEC support" - depends on VIDEO_ADV7511 && MEDIA_CEC_SUPPORT + depends on VIDEO_ADV7511 && CEC_CORE ---help--- When selected the adv7511 will support the optional HDMI CEC feature. @@ -520,6 +517,17 @@ config VIDEO_APTINA_PLL config VIDEO_SMIAPP_PLL tristate +config VIDEO_OV2640 + tristate "OmniVision OV2640 sensor support" + depends on VIDEO_V4L2 && I2C + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor-level driver for the OmniVision + OV2640 camera. + + To compile this driver as a module, choose M here: the + module will be called ov2640. + config VIDEO_OV2659 tristate "OmniVision OV2659 sensor support" depends on VIDEO_V4L2 && I2C @@ -531,6 +539,29 @@ config VIDEO_OV2659 To compile this driver as a module, choose M here: the module will be called ov2659. +config VIDEO_OV5645 + tristate "OmniVision OV5645 sensor support" + depends on OF + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the OmniVision + OV5645 camera. + + To compile this driver as a module, choose M here: the + module will be called ov5645. + +config VIDEO_OV5647 + tristate "OmniVision OV5647 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + ---help--- + This is a Video4Linux2 sensor-level driver for the OmniVision + OV5647 camera. + + To compile this driver as a module, choose M here: the + module will be called ov5647. + config VIDEO_OV7640 tristate "OmniVision OV7640 sensor support" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 5bc7bbeb5499..62323ec66be8 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -57,6 +57,9 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o +obj-$(CONFIG_VIDEO_OV2640) += ov2640.o +obj-$(CONFIG_VIDEO_OV5645) += ov5645.o +obj-$(CONFIG_VIDEO_OV5647) += ov5647.o obj-$(CONFIG_VIDEO_OV7640) += ov7640.o obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_OV9650) += ov9650.o diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index a9026a91855e..3d2a3c6b67d8 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -336,7 +336,7 @@ cleanup: return ret; } -static int __exit ad5820_remove(struct i2c_client *client) +static int ad5820_remove(struct i2c_client *client) { struct v4l2_subdev *subdev = i2c_get_clientdata(client); struct ad5820_device *coil = to_ad5820_device(subdev); @@ -362,7 +362,7 @@ static struct i2c_driver ad5820_i2c_driver = { .pm = &ad5820_pm, }, .probe = ad5820_probe, - .remove = __exit_p(ad5820_remove), + .remove = ad5820_remove, .id_table = ad5820_id_table, }; diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 8c9e28949ab1..ccc478605643 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -734,7 +734,7 @@ static int adv7511_s_power(struct v4l2_subdev *sd, int on) #if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC) static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) { - struct adv7511_state *state = adap->priv; + struct adv7511_state *state = cec_get_drvdata(adap); struct v4l2_subdev *sd = &state->sd; if (state->i2c_cec == NULL) @@ -769,7 +769,7 @@ static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) { - struct adv7511_state *state = adap->priv; + struct adv7511_state *state = cec_get_drvdata(adap); struct v4l2_subdev *sd = &state->sd; unsigned int i, free_idx = ADV7511_MAX_ADDRS; @@ -824,7 +824,7 @@ static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, u32 signal_free_time, struct cec_msg *msg) { - struct adv7511_state *state = adap->priv; + struct adv7511_state *state = cec_get_drvdata(adap); struct v4l2_subdev *sd = &state->sd; u8 len = msg->len; unsigned int i; diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index d8bf435db86d..f1fa9cec489f 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2050,7 +2050,7 @@ static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled) static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable) { - struct adv76xx_state *state = adap->priv; + struct adv76xx_state *state = cec_get_drvdata(adap); struct v4l2_subdev *sd = &state->sd; if (!state->cec_enabled_adap && enable) { @@ -2080,7 +2080,7 @@ static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable) static int adv76xx_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) { - struct adv76xx_state *state = adap->priv; + struct adv76xx_state *state = cec_get_drvdata(adap); struct v4l2_subdev *sd = &state->sd; unsigned int i, free_idx = ADV76XX_MAX_ADDRS; @@ -2135,7 +2135,7 @@ static int adv76xx_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) static int adv76xx_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, u32 signal_free_time, struct cec_msg *msg) { - struct adv76xx_state *state = adap->priv; + struct adv76xx_state *state = cec_get_drvdata(adap); struct v4l2_subdev *sd = &state->sd; u8 len = msg->len; unsigned int i; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 2d61f0cc2b5b..303effda1a2e 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -2250,7 +2250,7 @@ static void adv7842_cec_isr(struct v4l2_subdev *sd, bool *handled) static int adv7842_cec_adap_enable(struct cec_adapter *adap, bool enable) { - struct adv7842_state *state = adap->priv; + struct adv7842_state *state = cec_get_drvdata(adap); struct v4l2_subdev *sd = &state->sd; if (!state->cec_enabled_adap && enable) { @@ -2279,7 +2279,7 @@ static int adv7842_cec_adap_enable(struct cec_adapter *adap, bool enable) static int adv7842_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) { - struct adv7842_state *state = adap->priv; + struct adv7842_state *state = cec_get_drvdata(adap); struct v4l2_subdev *sd = &state->sd; unsigned int i, free_idx = ADV7842_MAX_ADDRS; @@ -2334,7 +2334,7 @@ static int adv7842_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) static int adv7842_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, u32 signal_free_time, struct cec_msg *msg) { - struct adv7842_state *state = adap->priv; + struct adv7842_state *state = cec_get_drvdata(adap); struct v4l2_subdev *sd = &state->sd; u8 len = msg->len; unsigned int i; diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index bec4a563a09c..6e313d5243a0 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -1485,6 +1485,7 @@ static const struct of_device_id et8ek8_of_table[] = { { .compatible = "toshiba,et8ek8" }, { }, }; +MODULE_DEVICE_TABLE(of, et8ek8_of_table); static const struct i2c_device_id et8ek8_id_table[] = { { ET8EK8_NAME, 0 }, @@ -1495,6 +1496,7 @@ MODULE_DEVICE_TABLE(i2c, et8ek8_id_table); static const struct dev_pm_ops et8ek8_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(et8ek8_suspend, et8ek8_resume) }; +MODULE_DEVICE_TABLE(of, et8ek8_of_table); static struct i2c_driver et8ek8_i2c_driver = { .driver = { diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/ov2640.c index 56de18263359..e6d0c1f64f0b 100644 --- a/drivers/media/i2c/soc_camera/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/i2c.h> +#include <linux/clk.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/gpio.h> @@ -24,8 +25,7 @@ #include <linux/v4l2-mediabus.h> #include <linux/videodev2.h> -#include <media/soc_camera.h> -#include <media/v4l2-clk.h> +#include <media/v4l2-device.h> #include <media/v4l2-subdev.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-image-sizes.h> @@ -106,6 +106,10 @@ #define CTRL1_AWB_GAIN 0x04 #define CTRL1_LENC 0x02 #define CTRL1_PRE 0x01 +/* REG 0xC7 (unknown name): affects Auto White Balance (AWB) + * AWB_OFF 0x40 + * AWB_SIMPLE 0x10 + * AWB_ON 0x00 (Advanced AWB ?) */ #define R_DVP_SP 0xD3 /* DVP output speed control */ #define R_DVP_SP_AUTO_MODE 0x80 #define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0); @@ -199,7 +203,7 @@ #define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */ #define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */ #define COM8 0x13 /* Common control 8 */ -#define COM8_DEF 0xC0 /* Banding filter ON/OFF */ +#define COM8_DEF 0xC0 #define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */ #define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */ #define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */ @@ -248,8 +252,19 @@ #define ZOOMS 0x49 /* Zoom: Vertical start point */ #define COM22 0x4B /* Flash light control */ #define COM25 0x4E /* For Banding operations */ +#define COM25_50HZ_BANDING_AEC_MSBS_MASK 0xC0 /* 50Hz Bd. AEC 2 MSBs */ +#define COM25_60HZ_BANDING_AEC_MSBS_MASK 0x30 /* 60Hz Bd. AEC 2 MSBs */ +#define COM25_50HZ_BANDING_AEC_MSBS_SET(x) VAL_SET(x, 0x3, 8, 6) +#define COM25_60HZ_BANDING_AEC_MSBS_SET(x) VAL_SET(x, 0x3, 8, 4) #define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */ +#define BD50_50HZ_BANDING_AEC_LSBS_SET(x) VAL_SET(x, 0xFF, 0, 0) #define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */ +#define BD60_60HZ_BANDING_AEC_LSBS_SET(x) VAL_SET(x, 0xFF, 0, 0) +#define REG5A 0x5A /* 50/60Hz Banding Maximum AEC Step */ +#define BD50_MAX_AEC_STEP_MASK 0xF0 /* 50Hz Banding Max. AEC Step */ +#define BD60_MAX_AEC_STEP_MASK 0x0F /* 60Hz Banding Max. AEC Step */ +#define BD50_MAX_AEC_STEP_SET(x) VAL_SET((x - 1), 0x0F, 0, 4) +#define BD60_MAX_AEC_STEP_SET(x) VAL_SET((x - 1), 0x0F, 0, 0) #define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */ #define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */ #define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */ @@ -282,12 +297,14 @@ struct ov2640_win_size { struct ov2640_priv { struct v4l2_subdev subdev; +#if defined(CONFIG_MEDIA_CONTROLLER) + struct media_pad pad; +#endif struct v4l2_ctrl_handler hdl; u32 cfmt_code; - struct v4l2_clk *clk; + struct clk *clk; const struct ov2640_win_size *win; - struct soc_camera_subdev_desc ssdd_dt; struct gpio_desc *resetb_gpio; struct gpio_desc *pwdn_gpio; }; @@ -304,11 +321,11 @@ static const struct regval_list ov2640_init_regs[] = { { 0x2e, 0xdf }, { BANK_SEL, BANK_SEL_SENS }, { 0x3c, 0x32 }, - { CLKRC, CLKRC_DIV_SET(1) }, - { COM2, COM2_OCAP_Nx_SET(3) }, - { REG04, REG04_DEF | REG04_HREF_EN }, - { COM8, COM8_DEF | COM8_BNDF_EN | COM8_AGC_EN | COM8_AEC_EN }, - { COM9, COM9_AGC_GAIN_8x | 0x08}, + { CLKRC, CLKRC_DIV_SET(1) }, + { COM2, COM2_OCAP_Nx_SET(3) }, + { REG04, REG04_DEF | REG04_HREF_EN }, + { COM8, COM8_DEF | COM8_BNDF_EN | COM8_AGC_EN | COM8_AEC_EN }, + { COM9, COM9_AGC_GAIN_8x | 0x08}, { 0x2c, 0x0c }, { 0x33, 0x78 }, { 0x3a, 0x33 }, @@ -353,25 +370,28 @@ static const struct regval_list ov2640_init_regs[] = { { 0x71, 0x94 }, { 0x73, 0xc1 }, { 0x3d, 0x34 }, - { COM7, COM7_RES_UXGA | COM7_ZOOM_EN }, - { 0x5a, 0x57 }, - { BD50, 0xbb }, - { BD60, 0x9c }, - { BANK_SEL, BANK_SEL_DSP }, + { COM7, COM7_RES_UXGA | COM7_ZOOM_EN }, + { REG5A, BD50_MAX_AEC_STEP_SET(6) + | BD60_MAX_AEC_STEP_SET(8) }, /* 0x57 */ + { COM25, COM25_50HZ_BANDING_AEC_MSBS_SET(0x0bb) + | COM25_60HZ_BANDING_AEC_MSBS_SET(0x09c) }, /* 0x00 */ + { BD50, BD50_50HZ_BANDING_AEC_LSBS_SET(0x0bb) }, /* 0xbb */ + { BD60, BD60_60HZ_BANDING_AEC_LSBS_SET(0x09c) }, /* 0x9c */ + { BANK_SEL, BANK_SEL_DSP }, { 0xe5, 0x7f }, - { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL }, + { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL }, { 0x41, 0x24 }, - { RESET, RESET_JPEG | RESET_DVP }, + { RESET, RESET_JPEG | RESET_DVP }, { 0x76, 0xff }, { 0x33, 0xa0 }, { 0x42, 0x20 }, { 0x43, 0x18 }, { 0x4c, 0x00 }, - { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 }, + { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 }, { 0x88, 0x3f }, { 0xd7, 0x03 }, { 0xd9, 0x10 }, - { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 }, + { R_DVP_SP, R_DVP_SP_AUTO_MODE | 0x2 }, { 0xc8, 0x08 }, { 0xc9, 0x80 }, { BPADDR, 0x00 }, @@ -433,7 +453,7 @@ static const struct regval_list ov2640_init_regs[] = { { 0xc5, 0x11 }, { 0xc6, 0x51 }, { 0xbf, 0x80 }, - { 0xc7, 0x10 }, + { 0xc7, 0x10 }, /* simple AWB */ { 0xb6, 0x66 }, { 0xb8, 0xA5 }, { 0xb7, 0x64 }, @@ -480,6 +500,9 @@ static const struct regval_list ov2640_init_regs[] = { static const struct regval_list ov2640_size_change_preamble_regs[] = { { BANK_SEL, BANK_SEL_DSP }, { RESET, RESET_DVP }, + { SIZEL, SIZEL_HSIZE8_11_SET(UXGA_WIDTH) | + SIZEL_HSIZE8_SET(UXGA_WIDTH) | + SIZEL_VSIZE8_SET(UXGA_HEIGHT) }, { HSIZE8, HSIZE8_SET(UXGA_WIDTH) }, { VSIZE8, VSIZE8_SET(UXGA_HEIGHT) }, { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | @@ -611,6 +634,8 @@ static const struct regval_list ov2640_rgb565_le_regs[] = { static u32 ov2640_codes[] = { MEDIA_BUS_FMT_YUYV8_2X8, MEDIA_BUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_YVYU8_2X8, + MEDIA_BUS_FMT_VYUY8_2X8, MEDIA_BUS_FMT_RGB565_2X8_BE, MEDIA_BUS_FMT_RGB565_2X8_LE, }; @@ -677,13 +702,8 @@ err: } /* - * soc_camera_ops functions + * functions */ -static int ov2640_s_stream(struct v4l2_subdev *sd, int enable) -{ - return 0; -} - static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = @@ -698,8 +718,10 @@ static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_VFLIP: - val = ctrl->val ? REG04_VFLIP_IMG : 0x00; - return ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val); + val = ctrl->val ? REG04_VFLIP_IMG | REG04_VREF_EN : 0x00; + return ov2640_mask_set(client, REG04, + REG04_VFLIP_IMG | REG04_VREF_EN, val); + /* NOTE: REG04_VREF_EN: 1 line shift / even/odd line swap */ case V4L2_CID_HFLIP: val = ctrl->val ? REG04_HFLIP_IMG : 0x00; return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val); @@ -743,41 +765,46 @@ static int ov2640_s_register(struct v4l2_subdev *sd, static int ov2640_s_power(struct v4l2_subdev *sd, int on) { +#ifdef CONFIG_GPIOLIB struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct ov2640_priv *priv = to_ov2640(client); - return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); + if (priv->pwdn_gpio) + gpiod_direction_output(priv->pwdn_gpio, !on); + if (on && priv->resetb_gpio) { + /* Active the resetb pin to perform a reset pulse */ + gpiod_direction_output(priv->resetb_gpio, 1); + usleep_range(3000, 5000); + gpiod_set_value(priv->resetb_gpio, 0); + } +#endif + return 0; } /* Select the nearest higher resolution for capture */ -static const struct ov2640_win_size *ov2640_select_win(u32 *width, u32 *height) +static const struct ov2640_win_size *ov2640_select_win(u32 width, u32 height) { int i, default_size = ARRAY_SIZE(ov2640_supported_win_sizes) - 1; for (i = 0; i < ARRAY_SIZE(ov2640_supported_win_sizes); i++) { - if (ov2640_supported_win_sizes[i].width >= *width && - ov2640_supported_win_sizes[i].height >= *height) { - *width = ov2640_supported_win_sizes[i].width; - *height = ov2640_supported_win_sizes[i].height; + if (ov2640_supported_win_sizes[i].width >= width && + ov2640_supported_win_sizes[i].height >= height) return &ov2640_supported_win_sizes[i]; - } } - *width = ov2640_supported_win_sizes[default_size].width; - *height = ov2640_supported_win_sizes[default_size].height; return &ov2640_supported_win_sizes[default_size]; } -static int ov2640_set_params(struct i2c_client *client, u32 *width, u32 *height, - u32 code) +static int ov2640_set_params(struct i2c_client *client, + const struct ov2640_win_size *win, u32 code) { struct ov2640_priv *priv = to_ov2640(client); const struct regval_list *selected_cfmt_regs; + u8 val; int ret; /* select win */ - priv->win = ov2640_select_win(width, height); + priv->win = win; /* select format */ priv->cfmt_code = 0; @@ -794,10 +821,19 @@ static int ov2640_set_params(struct i2c_client *client, u32 *width, u32 *height, dev_dbg(&client->dev, "%s: Selected cfmt YUYV (YUV422)", __func__); selected_cfmt_regs = ov2640_yuyv_regs; break; - default: case MEDIA_BUS_FMT_UYVY8_2X8: + default: dev_dbg(&client->dev, "%s: Selected cfmt UYVY", __func__); selected_cfmt_regs = ov2640_uyvy_regs; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + dev_dbg(&client->dev, "%s: Selected cfmt YVYU", __func__); + selected_cfmt_regs = ov2640_yuyv_regs; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + dev_dbg(&client->dev, "%s: Selected cfmt VYUY", __func__); + selected_cfmt_regs = ov2640_uyvy_regs; + break; } /* reset hardware */ @@ -830,10 +866,13 @@ static int ov2640_set_params(struct i2c_client *client, u32 *width, u32 *height, ret = ov2640_write_array(client, selected_cfmt_regs); if (ret < 0) goto err; + val = (code == MEDIA_BUS_FMT_YVYU8_2X8) + || (code == MEDIA_BUS_FMT_VYUY8_2X8) ? CTRL0_VFIRST : 0x00; + ret = ov2640_mask_set(client, CTRL0, CTRL0_VFIRST, val); + if (ret < 0) + goto err; priv->cfmt_code = code; - *width = priv->win->width; - *height = priv->win->height; return 0; @@ -857,25 +896,14 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd, return -EINVAL; if (!priv->win) { - u32 width = SVGA_WIDTH, height = SVGA_HEIGHT; - priv->win = ov2640_select_win(&width, &height); + priv->win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT); priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8; } mf->width = priv->win->width; mf->height = priv->win->height; mf->code = priv->cfmt_code; - - switch (mf->code) { - case MEDIA_BUS_FMT_RGB565_2X8_BE: - case MEDIA_BUS_FMT_RGB565_2X8_LE: - mf->colorspace = V4L2_COLORSPACE_SRGB; - break; - default: - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_UYVY8_2X8: - mf->colorspace = V4L2_COLORSPACE_JPEG; - } + mf->colorspace = V4L2_COLORSPACE_SRGB; mf->field = V4L2_FIELD_NONE; return 0; @@ -887,32 +915,34 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd, { struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); + const struct ov2640_win_size *win; if (format->pad) return -EINVAL; - /* - * select suitable win, but don't store it - */ - ov2640_select_win(&mf->width, &mf->height); + /* select suitable win */ + win = ov2640_select_win(mf->width, mf->height); + mf->width = win->width; + mf->height = win->height; mf->field = V4L2_FIELD_NONE; + mf->colorspace = V4L2_COLORSPACE_SRGB; switch (mf->code) { case MEDIA_BUS_FMT_RGB565_2X8_BE: case MEDIA_BUS_FMT_RGB565_2X8_LE: - mf->colorspace = V4L2_COLORSPACE_SRGB; + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_UYVY8_2X8: + case MEDIA_BUS_FMT_YVYU8_2X8: + case MEDIA_BUS_FMT_VYUY8_2X8: break; default: mf->code = MEDIA_BUS_FMT_UYVY8_2X8; - case MEDIA_BUS_FMT_YUYV8_2X8: - case MEDIA_BUS_FMT_UYVY8_2X8: - mf->colorspace = V4L2_COLORSPACE_JPEG; + break; } if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return ov2640_set_params(client, &mf->width, - &mf->height, mf->code); + return ov2640_set_params(client, win, mf->code); cfg->try_fmt = *mf; return 0; } @@ -995,7 +1025,7 @@ static const struct v4l2_ctrl_ops ov2640_ctrl_ops = { .s_ctrl = ov2640_s_ctrl, }; -static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { +static const struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov2640_g_register, .s_register = ov2640_s_register, @@ -1003,26 +1033,6 @@ static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { .s_power = ov2640_s_power, }; -static int ov2640_g_mbus_config(struct v4l2_subdev *sd, - struct v4l2_mbus_config *cfg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); - - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; - cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); - - return 0; -} - -static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = { - .s_stream = ov2640_s_stream, - .g_mbus_config = ov2640_g_mbus_config, -}; - static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = { .enum_mbus_code = ov2640_enum_mbus_code, .get_selection = ov2640_get_selection, @@ -1030,65 +1040,43 @@ static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = { .set_fmt = ov2640_set_fmt, }; -static struct v4l2_subdev_ops ov2640_subdev_ops = { +static const struct v4l2_subdev_ops ov2640_subdev_ops = { .core = &ov2640_subdev_core_ops, - .video = &ov2640_subdev_video_ops, .pad = &ov2640_subdev_pad_ops, }; -/* OF probe functions */ -static int ov2640_hw_power(struct device *dev, int on) -{ - struct i2c_client *client = to_i2c_client(dev); - struct ov2640_priv *priv = to_ov2640(client); - - dev_dbg(&client->dev, "%s: %s the camera\n", - __func__, on ? "ENABLE" : "DISABLE"); - - if (priv->pwdn_gpio) - gpiod_direction_output(priv->pwdn_gpio, !on); - - return 0; -} - -static int ov2640_hw_reset(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct ov2640_priv *priv = to_ov2640(client); - - if (priv->resetb_gpio) { - /* Active the resetb pin to perform a reset pulse */ - gpiod_direction_output(priv->resetb_gpio, 1); - usleep_range(3000, 5000); - gpiod_direction_output(priv->resetb_gpio, 0); - } - - return 0; -} - static int ov2640_probe_dt(struct i2c_client *client, struct ov2640_priv *priv) { + int ret; + /* Request the reset GPIO deasserted */ priv->resetb_gpio = devm_gpiod_get_optional(&client->dev, "resetb", GPIOD_OUT_LOW); + if (!priv->resetb_gpio) dev_dbg(&client->dev, "resetb gpio is not assigned!\n"); - else if (IS_ERR(priv->resetb_gpio)) - return PTR_ERR(priv->resetb_gpio); + + ret = PTR_ERR_OR_ZERO(priv->resetb_gpio); + if (ret && ret != -ENOSYS) { + dev_dbg(&client->dev, + "Error %d while getting resetb gpio\n", ret); + return ret; + } /* Request the power down GPIO asserted */ priv->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "pwdn", GPIOD_OUT_HIGH); + if (!priv->pwdn_gpio) dev_dbg(&client->dev, "pwdn gpio is not assigned!\n"); - else if (IS_ERR(priv->pwdn_gpio)) - return PTR_ERR(priv->pwdn_gpio); - /* Initialize the soc_camera_subdev_desc */ - priv->ssdd_dt.power = ov2640_hw_power; - priv->ssdd_dt.reset = ov2640_hw_reset; - client->dev.platform_data = &priv->ssdd_dt; + ret = PTR_ERR_OR_ZERO(priv->pwdn_gpio); + if (ret && ret != -ENOSYS) { + dev_dbg(&client->dev, + "Error %d while getting pwdn gpio\n", ret); + return ret; + } return 0; } @@ -1100,7 +1088,6 @@ static int ov2640_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov2640_priv *priv; - struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int ret; @@ -1117,23 +1104,19 @@ static int ov2640_probe(struct i2c_client *client, return -ENOMEM; } - priv->clk = v4l2_clk_get(&client->dev, "xvclk"); - if (IS_ERR(priv->clk)) - return -EPROBE_DEFER; - - if (!ssdd && !client->dev.of_node) { - dev_err(&client->dev, "Missing platform_data for driver\n"); - ret = -EINVAL; - goto err_clk; + if (client->dev.of_node) { + priv->clk = devm_clk_get(&client->dev, "xvclk"); + if (IS_ERR(priv->clk)) + return -EPROBE_DEFER; + clk_prepare_enable(priv->clk); } - if (!ssdd) { - ret = ov2640_probe_dt(client, priv); - if (ret) - goto err_clk; - } + ret = ov2640_probe_dt(client, priv); + if (ret) + goto err_clk; v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); + priv->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; v4l2_ctrl_handler_init(&priv->hdl, 2); v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); @@ -1142,8 +1125,15 @@ static int ov2640_probe(struct i2c_client *client, priv->subdev.ctrl_handler = &priv->hdl; if (priv->hdl.error) { ret = priv->hdl.error; - goto err_clk; + goto err_hdl; } +#if defined(CONFIG_MEDIA_CONTROLLER) + priv->pad.flags = MEDIA_PAD_FL_SOURCE; + priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&priv->subdev.entity, 1, &priv->pad); + if (ret < 0) + goto err_hdl; +#endif ret = ov2640_video_probe(client); if (ret < 0) @@ -1158,9 +1148,13 @@ static int ov2640_probe(struct i2c_client *client, return 0; err_videoprobe: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&priv->subdev.entity); +#endif +err_hdl: v4l2_ctrl_handler_free(&priv->hdl); err_clk: - v4l2_clk_put(priv->clk); + clk_disable_unprepare(priv->clk); return ret; } @@ -1169,9 +1163,12 @@ static int ov2640_remove(struct i2c_client *client) struct ov2640_priv *priv = to_ov2640(client); v4l2_async_unregister_subdev(&priv->subdev); - v4l2_clk_put(priv->clk); - v4l2_device_unregister_subdev(&priv->subdev); v4l2_ctrl_handler_free(&priv->hdl); +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&priv->subdev.entity); +#endif + v4l2_device_unregister_subdev(&priv->subdev); + clk_disable_unprepare(priv->clk); return 0; } @@ -1199,6 +1196,6 @@ static struct i2c_driver ov2640_i2c_driver = { module_i2c_driver(ov2640_i2c_driver); -MODULE_DESCRIPTION("SoC Camera driver for Omni Vision 2640 sensor"); +MODULE_DESCRIPTION("Driver for Omni Vision 2640 sensor"); MODULE_AUTHOR("Alberto Panizzo"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c new file mode 100644 index 000000000000..57bd591ea54b --- /dev/null +++ b/drivers/media/i2c/ov5645.c @@ -0,0 +1,1345 @@ +/* + * Driver for the OV5645 camera sensor. + * + * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015 By Tech Design S.L. All Rights Reserved. + * Copyright (C) 2012-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * Based on: + * - the OV5645 driver from QC msm-3.10 kernel on codeaurora.org: + * https://us.codeaurora.org/cgit/quic/la/kernel/msm-3.10/tree/drivers/ + * media/platform/msm/camera_v2/sensor/ov5645.c?h=LA.BR.1.2.4_rb1.41 + * - the OV5640 driver posted on linux-media: + * https://www.mail-archive.com/linux-media%40vger.kernel.org/msg92671.html + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-of.h> +#include <media/v4l2-subdev.h> + +#define OV5645_VOLTAGE_ANALOG 2800000 +#define OV5645_VOLTAGE_DIGITAL_CORE 1500000 +#define OV5645_VOLTAGE_DIGITAL_IO 1800000 + +#define OV5645_SYSTEM_CTRL0 0x3008 +#define OV5645_SYSTEM_CTRL0_START 0x02 +#define OV5645_SYSTEM_CTRL0_STOP 0x42 +#define OV5645_CHIP_ID_HIGH 0x300a +#define OV5645_CHIP_ID_HIGH_BYTE 0x56 +#define OV5645_CHIP_ID_LOW 0x300b +#define OV5645_CHIP_ID_LOW_BYTE 0x45 +#define OV5645_AWB_MANUAL_CONTROL 0x3406 +#define OV5645_AWB_MANUAL_ENABLE BIT(0) +#define OV5645_AEC_PK_MANUAL 0x3503 +#define OV5645_AEC_MANUAL_ENABLE BIT(0) +#define OV5645_AGC_MANUAL_ENABLE BIT(1) +#define OV5645_TIMING_TC_REG20 0x3820 +#define OV5645_SENSOR_VFLIP BIT(1) +#define OV5645_ISP_VFLIP BIT(2) +#define OV5645_TIMING_TC_REG21 0x3821 +#define OV5645_SENSOR_MIRROR BIT(1) +#define OV5645_PRE_ISP_TEST_SETTING_1 0x503d +#define OV5645_TEST_PATTERN_MASK 0x3 +#define OV5645_SET_TEST_PATTERN(x) ((x) & OV5645_TEST_PATTERN_MASK) +#define OV5645_TEST_PATTERN_ENABLE BIT(7) +#define OV5645_SDE_SAT_U 0x5583 +#define OV5645_SDE_SAT_V 0x5584 + +struct reg_value { + u16 reg; + u8 val; +}; + +struct ov5645_mode_info { + u32 width; + u32 height; + const struct reg_value *data; + u32 data_size; +}; + +struct ov5645 { + struct i2c_client *i2c_client; + struct device *dev; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_of_endpoint ep; + struct v4l2_mbus_framefmt fmt; + struct v4l2_rect crop; + struct clk *xclk; + + struct regulator *io_regulator; + struct regulator *core_regulator; + struct regulator *analog_regulator; + + const struct ov5645_mode_info *current_mode; + + struct v4l2_ctrl_handler ctrls; + + /* Cached register values */ + u8 aec_pk_manual; + u8 timing_tc_reg20; + u8 timing_tc_reg21; + + struct mutex power_lock; /* lock to protect power state */ + int power_count; + + struct gpio_desc *enable_gpio; + struct gpio_desc *rst_gpio; +}; + +static inline struct ov5645 *to_ov5645(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov5645, sd); +} + +static const struct reg_value ov5645_global_init_setting[] = { + { 0x3103, 0x11 }, + { 0x3008, 0x82 }, + { 0x3008, 0x42 }, + { 0x3103, 0x03 }, + { 0x3503, 0x07 }, + { 0x3002, 0x1c }, + { 0x3006, 0xc3 }, + { 0x300e, 0x45 }, + { 0x3017, 0x00 }, + { 0x3018, 0x00 }, + { 0x302e, 0x0b }, + { 0x3037, 0x13 }, + { 0x3108, 0x01 }, + { 0x3611, 0x06 }, + { 0x3500, 0x00 }, + { 0x3501, 0x01 }, + { 0x3502, 0x00 }, + { 0x350a, 0x00 }, + { 0x350b, 0x3f }, + { 0x3620, 0x33 }, + { 0x3621, 0xe0 }, + { 0x3622, 0x01 }, + { 0x3630, 0x2e }, + { 0x3631, 0x00 }, + { 0x3632, 0x32 }, + { 0x3633, 0x52 }, + { 0x3634, 0x70 }, + { 0x3635, 0x13 }, + { 0x3636, 0x03 }, + { 0x3703, 0x5a }, + { 0x3704, 0xa0 }, + { 0x3705, 0x1a }, + { 0x3709, 0x12 }, + { 0x370b, 0x61 }, + { 0x370f, 0x10 }, + { 0x3715, 0x78 }, + { 0x3717, 0x01 }, + { 0x371b, 0x20 }, + { 0x3731, 0x12 }, + { 0x3901, 0x0a }, + { 0x3905, 0x02 }, + { 0x3906, 0x10 }, + { 0x3719, 0x86 }, + { 0x3810, 0x00 }, + { 0x3811, 0x10 }, + { 0x3812, 0x00 }, + { 0x3821, 0x01 }, + { 0x3824, 0x01 }, + { 0x3826, 0x03 }, + { 0x3828, 0x08 }, + { 0x3a19, 0xf8 }, + { 0x3c01, 0x34 }, + { 0x3c04, 0x28 }, + { 0x3c05, 0x98 }, + { 0x3c07, 0x07 }, + { 0x3c09, 0xc2 }, + { 0x3c0a, 0x9c }, + { 0x3c0b, 0x40 }, + { 0x3c01, 0x34 }, + { 0x4001, 0x02 }, + { 0x4514, 0x00 }, + { 0x4520, 0xb0 }, + { 0x460b, 0x37 }, + { 0x460c, 0x20 }, + { 0x4818, 0x01 }, + { 0x481d, 0xf0 }, + { 0x481f, 0x50 }, + { 0x4823, 0x70 }, + { 0x4831, 0x14 }, + { 0x5000, 0xa7 }, + { 0x5001, 0x83 }, + { 0x501d, 0x00 }, + { 0x501f, 0x00 }, + { 0x503d, 0x00 }, + { 0x505c, 0x30 }, + { 0x5181, 0x59 }, + { 0x5183, 0x00 }, + { 0x5191, 0xf0 }, + { 0x5192, 0x03 }, + { 0x5684, 0x10 }, + { 0x5685, 0xa0 }, + { 0x5686, 0x0c }, + { 0x5687, 0x78 }, + { 0x5a00, 0x08 }, + { 0x5a21, 0x00 }, + { 0x5a24, 0x00 }, + { 0x3008, 0x02 }, + { 0x3503, 0x00 }, + { 0x5180, 0xff }, + { 0x5181, 0xf2 }, + { 0x5182, 0x00 }, + { 0x5183, 0x14 }, + { 0x5184, 0x25 }, + { 0x5185, 0x24 }, + { 0x5186, 0x09 }, + { 0x5187, 0x09 }, + { 0x5188, 0x0a }, + { 0x5189, 0x75 }, + { 0x518a, 0x52 }, + { 0x518b, 0xea }, + { 0x518c, 0xa8 }, + { 0x518d, 0x42 }, + { 0x518e, 0x38 }, + { 0x518f, 0x56 }, + { 0x5190, 0x42 }, + { 0x5191, 0xf8 }, + { 0x5192, 0x04 }, + { 0x5193, 0x70 }, + { 0x5194, 0xf0 }, + { 0x5195, 0xf0 }, + { 0x5196, 0x03 }, + { 0x5197, 0x01 }, + { 0x5198, 0x04 }, + { 0x5199, 0x12 }, + { 0x519a, 0x04 }, + { 0x519b, 0x00 }, + { 0x519c, 0x06 }, + { 0x519d, 0x82 }, + { 0x519e, 0x38 }, + { 0x5381, 0x1e }, + { 0x5382, 0x5b }, + { 0x5383, 0x08 }, + { 0x5384, 0x0a }, + { 0x5385, 0x7e }, + { 0x5386, 0x88 }, + { 0x5387, 0x7c }, + { 0x5388, 0x6c }, + { 0x5389, 0x10 }, + { 0x538a, 0x01 }, + { 0x538b, 0x98 }, + { 0x5300, 0x08 }, + { 0x5301, 0x30 }, + { 0x5302, 0x10 }, + { 0x5303, 0x00 }, + { 0x5304, 0x08 }, + { 0x5305, 0x30 }, + { 0x5306, 0x08 }, + { 0x5307, 0x16 }, + { 0x5309, 0x08 }, + { 0x530a, 0x30 }, + { 0x530b, 0x04 }, + { 0x530c, 0x06 }, + { 0x5480, 0x01 }, + { 0x5481, 0x08 }, + { 0x5482, 0x14 }, + { 0x5483, 0x28 }, + { 0x5484, 0x51 }, + { 0x5485, 0x65 }, + { 0x5486, 0x71 }, + { 0x5487, 0x7d }, + { 0x5488, 0x87 }, + { 0x5489, 0x91 }, + { 0x548a, 0x9a }, + { 0x548b, 0xaa }, + { 0x548c, 0xb8 }, + { 0x548d, 0xcd }, + { 0x548e, 0xdd }, + { 0x548f, 0xea }, + { 0x5490, 0x1d }, + { 0x5580, 0x02 }, + { 0x5583, 0x40 }, + { 0x5584, 0x10 }, + { 0x5589, 0x10 }, + { 0x558a, 0x00 }, + { 0x558b, 0xf8 }, + { 0x5800, 0x3f }, + { 0x5801, 0x16 }, + { 0x5802, 0x0e }, + { 0x5803, 0x0d }, + { 0x5804, 0x17 }, + { 0x5805, 0x3f }, + { 0x5806, 0x0b }, + { 0x5807, 0x06 }, + { 0x5808, 0x04 }, + { 0x5809, 0x04 }, + { 0x580a, 0x06 }, + { 0x580b, 0x0b }, + { 0x580c, 0x09 }, + { 0x580d, 0x03 }, + { 0x580e, 0x00 }, + { 0x580f, 0x00 }, + { 0x5810, 0x03 }, + { 0x5811, 0x08 }, + { 0x5812, 0x0a }, + { 0x5813, 0x03 }, + { 0x5814, 0x00 }, + { 0x5815, 0x00 }, + { 0x5816, 0x04 }, + { 0x5817, 0x09 }, + { 0x5818, 0x0f }, + { 0x5819, 0x08 }, + { 0x581a, 0x06 }, + { 0x581b, 0x06 }, + { 0x581c, 0x08 }, + { 0x581d, 0x0c }, + { 0x581e, 0x3f }, + { 0x581f, 0x1e }, + { 0x5820, 0x12 }, + { 0x5821, 0x13 }, + { 0x5822, 0x21 }, + { 0x5823, 0x3f }, + { 0x5824, 0x68 }, + { 0x5825, 0x28 }, + { 0x5826, 0x2c }, + { 0x5827, 0x28 }, + { 0x5828, 0x08 }, + { 0x5829, 0x48 }, + { 0x582a, 0x64 }, + { 0x582b, 0x62 }, + { 0x582c, 0x64 }, + { 0x582d, 0x28 }, + { 0x582e, 0x46 }, + { 0x582f, 0x62 }, + { 0x5830, 0x60 }, + { 0x5831, 0x62 }, + { 0x5832, 0x26 }, + { 0x5833, 0x48 }, + { 0x5834, 0x66 }, + { 0x5835, 0x44 }, + { 0x5836, 0x64 }, + { 0x5837, 0x28 }, + { 0x5838, 0x66 }, + { 0x5839, 0x48 }, + { 0x583a, 0x2c }, + { 0x583b, 0x28 }, + { 0x583c, 0x26 }, + { 0x583d, 0xae }, + { 0x5025, 0x00 }, + { 0x3a0f, 0x30 }, + { 0x3a10, 0x28 }, + { 0x3a1b, 0x30 }, + { 0x3a1e, 0x26 }, + { 0x3a11, 0x60 }, + { 0x3a1f, 0x14 }, + { 0x0601, 0x02 }, + { 0x3008, 0x42 }, + { 0x3008, 0x02 } +}; + +static const struct reg_value ov5645_setting_sxga[] = { + { 0x3612, 0xa9 }, + { 0x3614, 0x50 }, + { 0x3618, 0x00 }, + { 0x3034, 0x18 }, + { 0x3035, 0x21 }, + { 0x3036, 0x70 }, + { 0x3600, 0x09 }, + { 0x3601, 0x43 }, + { 0x3708, 0x66 }, + { 0x370c, 0xc3 }, + { 0x3800, 0x00 }, + { 0x3801, 0x00 }, + { 0x3802, 0x00 }, + { 0x3803, 0x06 }, + { 0x3804, 0x0a }, + { 0x3805, 0x3f }, + { 0x3806, 0x07 }, + { 0x3807, 0x9d }, + { 0x3808, 0x05 }, + { 0x3809, 0x00 }, + { 0x380a, 0x03 }, + { 0x380b, 0xc0 }, + { 0x380c, 0x07 }, + { 0x380d, 0x68 }, + { 0x380e, 0x03 }, + { 0x380f, 0xd8 }, + { 0x3813, 0x06 }, + { 0x3814, 0x31 }, + { 0x3815, 0x31 }, + { 0x3820, 0x47 }, + { 0x3a02, 0x03 }, + { 0x3a03, 0xd8 }, + { 0x3a08, 0x01 }, + { 0x3a09, 0xf8 }, + { 0x3a0a, 0x01 }, + { 0x3a0b, 0xa4 }, + { 0x3a0e, 0x02 }, + { 0x3a0d, 0x02 }, + { 0x3a14, 0x03 }, + { 0x3a15, 0xd8 }, + { 0x3a18, 0x00 }, + { 0x4004, 0x02 }, + { 0x4005, 0x18 }, + { 0x4300, 0x32 }, + { 0x4202, 0x00 } +}; + +static const struct reg_value ov5645_setting_1080p[] = { + { 0x3612, 0xab }, + { 0x3614, 0x50 }, + { 0x3618, 0x04 }, + { 0x3034, 0x18 }, + { 0x3035, 0x11 }, + { 0x3036, 0x54 }, + { 0x3600, 0x08 }, + { 0x3601, 0x33 }, + { 0x3708, 0x63 }, + { 0x370c, 0xc0 }, + { 0x3800, 0x01 }, + { 0x3801, 0x50 }, + { 0x3802, 0x01 }, + { 0x3803, 0xb2 }, + { 0x3804, 0x08 }, + { 0x3805, 0xef }, + { 0x3806, 0x05 }, + { 0x3807, 0xf1 }, + { 0x3808, 0x07 }, + { 0x3809, 0x80 }, + { 0x380a, 0x04 }, + { 0x380b, 0x38 }, + { 0x380c, 0x09 }, + { 0x380d, 0xc4 }, + { 0x380e, 0x04 }, + { 0x380f, 0x60 }, + { 0x3813, 0x04 }, + { 0x3814, 0x11 }, + { 0x3815, 0x11 }, + { 0x3820, 0x47 }, + { 0x4514, 0x88 }, + { 0x3a02, 0x04 }, + { 0x3a03, 0x60 }, + { 0x3a08, 0x01 }, + { 0x3a09, 0x50 }, + { 0x3a0a, 0x01 }, + { 0x3a0b, 0x18 }, + { 0x3a0e, 0x03 }, + { 0x3a0d, 0x04 }, + { 0x3a14, 0x04 }, + { 0x3a15, 0x60 }, + { 0x3a18, 0x00 }, + { 0x4004, 0x06 }, + { 0x4005, 0x18 }, + { 0x4300, 0x32 }, + { 0x4202, 0x00 }, + { 0x4837, 0x0b } +}; + +static const struct reg_value ov5645_setting_full[] = { + { 0x3612, 0xab }, + { 0x3614, 0x50 }, + { 0x3618, 0x04 }, + { 0x3034, 0x18 }, + { 0x3035, 0x11 }, + { 0x3036, 0x54 }, + { 0x3600, 0x08 }, + { 0x3601, 0x33 }, + { 0x3708, 0x63 }, + { 0x370c, 0xc0 }, + { 0x3800, 0x00 }, + { 0x3801, 0x00 }, + { 0x3802, 0x00 }, + { 0x3803, 0x00 }, + { 0x3804, 0x0a }, + { 0x3805, 0x3f }, + { 0x3806, 0x07 }, + { 0x3807, 0x9f }, + { 0x3808, 0x0a }, + { 0x3809, 0x20 }, + { 0x380a, 0x07 }, + { 0x380b, 0x98 }, + { 0x380c, 0x0b }, + { 0x380d, 0x1c }, + { 0x380e, 0x07 }, + { 0x380f, 0xb0 }, + { 0x3813, 0x06 }, + { 0x3814, 0x11 }, + { 0x3815, 0x11 }, + { 0x3820, 0x47 }, + { 0x4514, 0x88 }, + { 0x3a02, 0x07 }, + { 0x3a03, 0xb0 }, + { 0x3a08, 0x01 }, + { 0x3a09, 0x27 }, + { 0x3a0a, 0x00 }, + { 0x3a0b, 0xf6 }, + { 0x3a0e, 0x06 }, + { 0x3a0d, 0x08 }, + { 0x3a14, 0x07 }, + { 0x3a15, 0xb0 }, + { 0x3a18, 0x01 }, + { 0x4004, 0x06 }, + { 0x4005, 0x18 }, + { 0x4300, 0x32 }, + { 0x4837, 0x0b }, + { 0x4202, 0x00 } +}; + +static const struct ov5645_mode_info ov5645_mode_info_data[] = { + { + .width = 1280, + .height = 960, + .data = ov5645_setting_sxga, + .data_size = ARRAY_SIZE(ov5645_setting_sxga) + }, + { + .width = 1920, + .height = 1080, + .data = ov5645_setting_1080p, + .data_size = ARRAY_SIZE(ov5645_setting_1080p) + }, + { + .width = 2592, + .height = 1944, + .data = ov5645_setting_full, + .data_size = ARRAY_SIZE(ov5645_setting_full) + }, +}; + +static int ov5645_regulators_enable(struct ov5645 *ov5645) +{ + int ret; + + ret = regulator_enable(ov5645->io_regulator); + if (ret < 0) { + dev_err(ov5645->dev, "set io voltage failed\n"); + return ret; + } + + ret = regulator_enable(ov5645->analog_regulator); + if (ret) { + dev_err(ov5645->dev, "set analog voltage failed\n"); + goto err_disable_io; + } + + ret = regulator_enable(ov5645->core_regulator); + if (ret) { + dev_err(ov5645->dev, "set core voltage failed\n"); + goto err_disable_analog; + } + + return 0; + +err_disable_analog: + regulator_disable(ov5645->analog_regulator); +err_disable_io: + regulator_disable(ov5645->io_regulator); + + return ret; +} + +static void ov5645_regulators_disable(struct ov5645 *ov5645) +{ + int ret; + + ret = regulator_disable(ov5645->core_regulator); + if (ret < 0) + dev_err(ov5645->dev, "core regulator disable failed\n"); + + ret = regulator_disable(ov5645->analog_regulator); + if (ret < 0) + dev_err(ov5645->dev, "analog regulator disable failed\n"); + + ret = regulator_disable(ov5645->io_regulator); + if (ret < 0) + dev_err(ov5645->dev, "io regulator disable failed\n"); +} + +static int ov5645_write_reg(struct ov5645 *ov5645, u16 reg, u8 val) +{ + u8 regbuf[3]; + int ret; + + regbuf[0] = reg >> 8; + regbuf[1] = reg & 0xff; + regbuf[2] = val; + + ret = i2c_master_send(ov5645->i2c_client, regbuf, 3); + if (ret < 0) + dev_err(ov5645->dev, "%s: write reg error %d: reg=%x, val=%x\n", + __func__, ret, reg, val); + + return ret; +} + +static int ov5645_read_reg(struct ov5645 *ov5645, u16 reg, u8 *val) +{ + u8 regbuf[2]; + int ret; + + regbuf[0] = reg >> 8; + regbuf[1] = reg & 0xff; + + ret = i2c_master_send(ov5645->i2c_client, regbuf, 2); + if (ret < 0) { + dev_err(ov5645->dev, "%s: write reg error %d: reg=%x\n", + __func__, ret, reg); + return ret; + } + + ret = i2c_master_recv(ov5645->i2c_client, val, 1); + if (ret < 0) { + dev_err(ov5645->dev, "%s: read reg error %d: reg=%x\n", + __func__, ret, reg); + return ret; + } + + return 0; +} + +static int ov5645_set_aec_mode(struct ov5645 *ov5645, u32 mode) +{ + u8 val = ov5645->aec_pk_manual; + int ret; + + if (mode == V4L2_EXPOSURE_AUTO) + val &= ~OV5645_AEC_MANUAL_ENABLE; + else /* V4L2_EXPOSURE_MANUAL */ + val |= OV5645_AEC_MANUAL_ENABLE; + + ret = ov5645_write_reg(ov5645, OV5645_AEC_PK_MANUAL, val); + if (!ret) + ov5645->aec_pk_manual = val; + + return ret; +} + +static int ov5645_set_agc_mode(struct ov5645 *ov5645, u32 enable) +{ + u8 val = ov5645->aec_pk_manual; + int ret; + + if (enable) + val &= ~OV5645_AGC_MANUAL_ENABLE; + else + val |= OV5645_AGC_MANUAL_ENABLE; + + ret = ov5645_write_reg(ov5645, OV5645_AEC_PK_MANUAL, val); + if (!ret) + ov5645->aec_pk_manual = val; + + return ret; +} + +static int ov5645_set_register_array(struct ov5645 *ov5645, + const struct reg_value *settings, + unsigned int num_settings) +{ + unsigned int i; + int ret; + + for (i = 0; i < num_settings; ++i, ++settings) { + ret = ov5645_write_reg(ov5645, settings->reg, settings->val); + if (ret < 0) + return ret; + } + + return 0; +} + +static int ov5645_set_power_on(struct ov5645 *ov5645) +{ + int ret; + + ret = ov5645_regulators_enable(ov5645); + if (ret < 0) { + return ret; + } + + ret = clk_prepare_enable(ov5645->xclk); + if (ret < 0) { + dev_err(ov5645->dev, "clk prepare enable failed\n"); + ov5645_regulators_disable(ov5645); + return ret; + } + + usleep_range(5000, 15000); + gpiod_set_value_cansleep(ov5645->enable_gpio, 1); + + usleep_range(1000, 2000); + gpiod_set_value_cansleep(ov5645->rst_gpio, 0); + + msleep(20); + + return 0; +} + +static void ov5645_set_power_off(struct ov5645 *ov5645) +{ + gpiod_set_value_cansleep(ov5645->rst_gpio, 1); + gpiod_set_value_cansleep(ov5645->enable_gpio, 0); + clk_disable_unprepare(ov5645->xclk); + ov5645_regulators_disable(ov5645); +} + +static int ov5645_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov5645 *ov5645 = to_ov5645(sd); + int ret = 0; + + mutex_lock(&ov5645->power_lock); + + /* If the power count is modified from 0 to != 0 or from != 0 to 0, + * update the power state. + */ + if (ov5645->power_count == !on) { + if (on) { + ret = ov5645_set_power_on(ov5645); + if (ret < 0) + goto exit; + + ret = ov5645_set_register_array(ov5645, + ov5645_global_init_setting, + ARRAY_SIZE(ov5645_global_init_setting)); + if (ret < 0) { + dev_err(ov5645->dev, + "could not set init registers\n"); + ov5645_set_power_off(ov5645); + goto exit; + } + + ret = ov5645_write_reg(ov5645, OV5645_SYSTEM_CTRL0, + OV5645_SYSTEM_CTRL0_STOP); + if (ret < 0) { + ov5645_set_power_off(ov5645); + goto exit; + } + } else { + ov5645_set_power_off(ov5645); + } + } + + /* Update the power count. */ + ov5645->power_count += on ? 1 : -1; + WARN_ON(ov5645->power_count < 0); + +exit: + mutex_unlock(&ov5645->power_lock); + + return ret; +} + +static int ov5645_set_saturation(struct ov5645 *ov5645, s32 value) +{ + u32 reg_value = (value * 0x10) + 0x40; + int ret; + + ret = ov5645_write_reg(ov5645, OV5645_SDE_SAT_U, reg_value); + if (ret < 0) + return ret; + + return ov5645_write_reg(ov5645, OV5645_SDE_SAT_V, reg_value); +} + +static int ov5645_set_hflip(struct ov5645 *ov5645, s32 value) +{ + u8 val = ov5645->timing_tc_reg21; + int ret; + + if (value == 0) + val &= ~(OV5645_SENSOR_MIRROR); + else + val |= (OV5645_SENSOR_MIRROR); + + ret = ov5645_write_reg(ov5645, OV5645_TIMING_TC_REG21, val); + if (!ret) + ov5645->timing_tc_reg21 = val; + + return ret; +} + +static int ov5645_set_vflip(struct ov5645 *ov5645, s32 value) +{ + u8 val = ov5645->timing_tc_reg20; + int ret; + + if (value == 0) + val |= (OV5645_SENSOR_VFLIP | OV5645_ISP_VFLIP); + else + val &= ~(OV5645_SENSOR_VFLIP | OV5645_ISP_VFLIP); + + ret = ov5645_write_reg(ov5645, OV5645_TIMING_TC_REG20, val); + if (!ret) + ov5645->timing_tc_reg20 = val; + + return ret; +} + +static int ov5645_set_test_pattern(struct ov5645 *ov5645, s32 value) +{ + u8 val = 0; + + if (value) { + val = OV5645_SET_TEST_PATTERN(value - 1); + val |= OV5645_TEST_PATTERN_ENABLE; + } + + return ov5645_write_reg(ov5645, OV5645_PRE_ISP_TEST_SETTING_1, val); +} + +static const char * const ov5645_test_pattern_menu[] = { + "Disabled", + "Vertical Color Bars", + "Pseudo-Random Data", + "Color Square", + "Black Image", +}; + +static int ov5645_set_awb(struct ov5645 *ov5645, s32 enable_auto) +{ + u8 val = 0; + + if (!enable_auto) + val = OV5645_AWB_MANUAL_ENABLE; + + return ov5645_write_reg(ov5645, OV5645_AWB_MANUAL_CONTROL, val); +} + +static int ov5645_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ov5645 *ov5645 = container_of(ctrl->handler, + struct ov5645, ctrls); + int ret; + + mutex_lock(&ov5645->power_lock); + if (!ov5645->power_count) { + mutex_unlock(&ov5645->power_lock); + return 0; + } + + switch (ctrl->id) { + case V4L2_CID_SATURATION: + ret = ov5645_set_saturation(ov5645, ctrl->val); + break; + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = ov5645_set_awb(ov5645, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + ret = ov5645_set_agc_mode(ov5645, ctrl->val); + break; + case V4L2_CID_EXPOSURE_AUTO: + ret = ov5645_set_aec_mode(ov5645, ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = ov5645_set_test_pattern(ov5645, ctrl->val); + break; + case V4L2_CID_HFLIP: + ret = ov5645_set_hflip(ov5645, ctrl->val); + break; + case V4L2_CID_VFLIP: + ret = ov5645_set_vflip(ov5645, ctrl->val); + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&ov5645->power_lock); + + return ret; +} + +static struct v4l2_ctrl_ops ov5645_ctrl_ops = { + .s_ctrl = ov5645_s_ctrl, +}; + +static int ov5645_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_UYVY8_2X8; + + return 0; +} + +static int ov5645_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->code != MEDIA_BUS_FMT_UYVY8_2X8) + return -EINVAL; + + if (fse->index >= ARRAY_SIZE(ov5645_mode_info_data)) + return -EINVAL; + + fse->min_width = ov5645_mode_info_data[fse->index].width; + fse->max_width = ov5645_mode_info_data[fse->index].width; + fse->min_height = ov5645_mode_info_data[fse->index].height; + fse->max_height = ov5645_mode_info_data[fse->index].height; + + return 0; +} + +static struct v4l2_mbus_framefmt * +__ov5645_get_pad_format(struct ov5645 *ov5645, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&ov5645->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &ov5645->fmt; + default: + return NULL; + } +} + +static int ov5645_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov5645 *ov5645 = to_ov5645(sd); + + format->format = *__ov5645_get_pad_format(ov5645, cfg, format->pad, + format->which); + return 0; +} + +static struct v4l2_rect * +__ov5645_get_pad_crop(struct ov5645 *ov5645, struct v4l2_subdev_pad_config *cfg, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&ov5645->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &ov5645->crop; + default: + return NULL; + } +} + +static const struct ov5645_mode_info * +ov5645_find_nearest_mode(unsigned int width, unsigned int height) +{ + int i; + + for (i = ARRAY_SIZE(ov5645_mode_info_data) - 1; i >= 0; i--) { + if (ov5645_mode_info_data[i].width <= width && + ov5645_mode_info_data[i].height <= height) + break; + } + + if (i < 0) + i = 0; + + return &ov5645_mode_info_data[i]; +} + +static int ov5645_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct ov5645 *ov5645 = to_ov5645(sd); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + const struct ov5645_mode_info *new_mode; + + __crop = __ov5645_get_pad_crop(ov5645, cfg, format->pad, + format->which); + + new_mode = ov5645_find_nearest_mode(format->format.width, + format->format.height); + __crop->width = new_mode->width; + __crop->height = new_mode->height; + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ov5645->current_mode = new_mode; + + __format = __ov5645_get_pad_format(ov5645, cfg, format->pad, + format->which); + __format->width = __crop->width; + __format->height = __crop->height; + __format->code = MEDIA_BUS_FMT_UYVY8_2X8; + __format->field = V4L2_FIELD_NONE; + __format->colorspace = V4L2_COLORSPACE_SRGB; + + format->format = *__format; + + return 0; +} + +static int ov5645_entity_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_subdev_format fmt = { 0 }; + + fmt.which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.format.width = 1920; + fmt.format.height = 1080; + + ov5645_set_format(subdev, cfg, &fmt); + + return 0; +} + +static int ov5645_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct ov5645 *ov5645 = to_ov5645(sd); + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + sel->r = *__ov5645_get_pad_crop(ov5645, cfg, sel->pad, + sel->which); + return 0; +} + +static int ov5645_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct ov5645 *ov5645 = to_ov5645(subdev); + int ret; + + if (enable) { + ret = ov5645_set_register_array(ov5645, + ov5645->current_mode->data, + ov5645->current_mode->data_size); + if (ret < 0) { + dev_err(ov5645->dev, "could not set mode %dx%d\n", + ov5645->current_mode->width, + ov5645->current_mode->height); + return ret; + } + ret = v4l2_ctrl_handler_setup(&ov5645->ctrls); + if (ret < 0) { + dev_err(ov5645->dev, "could not sync v4l2 controls\n"); + return ret; + } + ret = ov5645_write_reg(ov5645, OV5645_SYSTEM_CTRL0, + OV5645_SYSTEM_CTRL0_START); + if (ret < 0) + return ret; + } else { + ret = ov5645_write_reg(ov5645, OV5645_SYSTEM_CTRL0, + OV5645_SYSTEM_CTRL0_STOP); + if (ret < 0) + return ret; + } + + return 0; +} + +static const struct v4l2_subdev_core_ops ov5645_core_ops = { + .s_power = ov5645_s_power, +}; + +static const struct v4l2_subdev_video_ops ov5645_video_ops = { + .s_stream = ov5645_s_stream, +}; + +static const struct v4l2_subdev_pad_ops ov5645_subdev_pad_ops = { + .init_cfg = ov5645_entity_init_cfg, + .enum_mbus_code = ov5645_enum_mbus_code, + .enum_frame_size = ov5645_enum_frame_size, + .get_fmt = ov5645_get_format, + .set_fmt = ov5645_set_format, + .get_selection = ov5645_get_selection, +}; + +static const struct v4l2_subdev_ops ov5645_subdev_ops = { + .core = &ov5645_core_ops, + .video = &ov5645_video_ops, + .pad = &ov5645_subdev_pad_ops, +}; + +static int ov5645_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *endpoint; + struct ov5645 *ov5645; + u8 chip_id_high, chip_id_low; + u32 xclk_freq; + int ret; + + ov5645 = devm_kzalloc(dev, sizeof(struct ov5645), GFP_KERNEL); + if (!ov5645) + return -ENOMEM; + + ov5645->i2c_client = client; + ov5645->dev = dev; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + ret = v4l2_of_parse_endpoint(endpoint, &ov5645->ep); + if (ret < 0) { + dev_err(dev, "parsing endpoint node failed\n"); + return ret; + } + + of_node_put(endpoint); + + if (ov5645->ep.bus_type != V4L2_MBUS_CSI2) { + dev_err(dev, "invalid bus type, must be CSI2\n"); + return -EINVAL; + } + + /* get system clock (xclk) */ + ov5645->xclk = devm_clk_get(dev, "xclk"); + if (IS_ERR(ov5645->xclk)) { + dev_err(dev, "could not get xclk"); + return PTR_ERR(ov5645->xclk); + } + + ret = of_property_read_u32(dev->of_node, "clock-frequency", &xclk_freq); + if (ret) { + dev_err(dev, "could not get xclk frequency\n"); + return ret; + } + + if (xclk_freq != 23880000) { + dev_err(dev, "external clock frequency %u is not supported\n", + xclk_freq); + return -EINVAL; + } + + ret = clk_set_rate(ov5645->xclk, xclk_freq); + if (ret) { + dev_err(dev, "could not set xclk frequency\n"); + return ret; + } + + ov5645->io_regulator = devm_regulator_get(dev, "vdddo"); + if (IS_ERR(ov5645->io_regulator)) { + dev_err(dev, "cannot get io regulator\n"); + return PTR_ERR(ov5645->io_regulator); + } + + ret = regulator_set_voltage(ov5645->io_regulator, + OV5645_VOLTAGE_DIGITAL_IO, + OV5645_VOLTAGE_DIGITAL_IO); + if (ret < 0) { + dev_err(dev, "cannot set io voltage\n"); + return ret; + } + + ov5645->core_regulator = devm_regulator_get(dev, "vddd"); + if (IS_ERR(ov5645->core_regulator)) { + dev_err(dev, "cannot get core regulator\n"); + return PTR_ERR(ov5645->core_regulator); + } + + ret = regulator_set_voltage(ov5645->core_regulator, + OV5645_VOLTAGE_DIGITAL_CORE, + OV5645_VOLTAGE_DIGITAL_CORE); + if (ret < 0) { + dev_err(dev, "cannot set core voltage\n"); + return ret; + } + + ov5645->analog_regulator = devm_regulator_get(dev, "vdda"); + if (IS_ERR(ov5645->analog_regulator)) { + dev_err(dev, "cannot get analog regulator\n"); + return PTR_ERR(ov5645->analog_regulator); + } + + ret = regulator_set_voltage(ov5645->analog_regulator, + OV5645_VOLTAGE_ANALOG, + OV5645_VOLTAGE_ANALOG); + if (ret < 0) { + dev_err(dev, "cannot set analog voltage\n"); + return ret; + } + + ov5645->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH); + if (IS_ERR(ov5645->enable_gpio)) { + dev_err(dev, "cannot get enable gpio\n"); + return PTR_ERR(ov5645->enable_gpio); + } + + ov5645->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ov5645->rst_gpio)) { + dev_err(dev, "cannot get reset gpio\n"); + return PTR_ERR(ov5645->rst_gpio); + } + + mutex_init(&ov5645->power_lock); + + v4l2_ctrl_handler_init(&ov5645->ctrls, 7); + v4l2_ctrl_new_std(&ov5645->ctrls, &ov5645_ctrl_ops, + V4L2_CID_SATURATION, -4, 4, 1, 0); + v4l2_ctrl_new_std(&ov5645->ctrls, &ov5645_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&ov5645->ctrls, &ov5645_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(&ov5645->ctrls, &ov5645_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + v4l2_ctrl_new_std(&ov5645->ctrls, &ov5645_ctrl_ops, + V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + v4l2_ctrl_new_std_menu(&ov5645->ctrls, &ov5645_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, + 0, V4L2_EXPOSURE_AUTO); + v4l2_ctrl_new_std_menu_items(&ov5645->ctrls, &ov5645_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov5645_test_pattern_menu) - 1, + 0, 0, ov5645_test_pattern_menu); + + ov5645->sd.ctrl_handler = &ov5645->ctrls; + + if (ov5645->ctrls.error) { + dev_err(dev, "%s: control initialization error %d\n", + __func__, ov5645->ctrls.error); + ret = ov5645->ctrls.error; + goto free_ctrl; + } + + v4l2_i2c_subdev_init(&ov5645->sd, client, &ov5645_subdev_ops); + ov5645->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ov5645->pad.flags = MEDIA_PAD_FL_SOURCE; + ov5645->sd.dev = &client->dev; + + ret = media_entity_pads_init(&ov5645->sd.entity, 1, &ov5645->pad); + if (ret < 0) { + dev_err(dev, "could not register media entity\n"); + goto free_ctrl; + } + + ret = ov5645_s_power(&ov5645->sd, true); + if (ret < 0) { + dev_err(dev, "could not power up OV5645\n"); + goto free_entity; + } + + ret = ov5645_read_reg(ov5645, OV5645_CHIP_ID_HIGH, &chip_id_high); + if (ret < 0 || chip_id_high != OV5645_CHIP_ID_HIGH_BYTE) { + dev_err(dev, "could not read ID high\n"); + ret = -ENODEV; + goto power_down; + } + ret = ov5645_read_reg(ov5645, OV5645_CHIP_ID_LOW, &chip_id_low); + if (ret < 0 || chip_id_low != OV5645_CHIP_ID_LOW_BYTE) { + dev_err(dev, "could not read ID low\n"); + ret = -ENODEV; + goto power_down; + } + + dev_info(dev, "OV5645 detected at address 0x%02x\n", client->addr); + + ret = ov5645_read_reg(ov5645, OV5645_AEC_PK_MANUAL, + &ov5645->aec_pk_manual); + if (ret < 0) { + dev_err(dev, "could not read AEC/AGC mode\n"); + ret = -ENODEV; + goto power_down; + } + + ret = ov5645_read_reg(ov5645, OV5645_TIMING_TC_REG20, + &ov5645->timing_tc_reg20); + if (ret < 0) { + dev_err(dev, "could not read vflip value\n"); + ret = -ENODEV; + goto power_down; + } + + ret = ov5645_read_reg(ov5645, OV5645_TIMING_TC_REG21, + &ov5645->timing_tc_reg21); + if (ret < 0) { + dev_err(dev, "could not read hflip value\n"); + ret = -ENODEV; + goto power_down; + } + + ov5645_s_power(&ov5645->sd, false); + + ret = v4l2_async_register_subdev(&ov5645->sd); + if (ret < 0) { + dev_err(dev, "could not register v4l2 device\n"); + goto free_entity; + } + + ov5645_entity_init_cfg(&ov5645->sd, NULL); + + return 0; + +power_down: + ov5645_s_power(&ov5645->sd, false); +free_entity: + media_entity_cleanup(&ov5645->sd.entity); +free_ctrl: + v4l2_ctrl_handler_free(&ov5645->ctrls); + mutex_destroy(&ov5645->power_lock); + + return ret; +} + +static int ov5645_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5645 *ov5645 = to_ov5645(sd); + + v4l2_async_unregister_subdev(&ov5645->sd); + media_entity_cleanup(&ov5645->sd.entity); + v4l2_ctrl_handler_free(&ov5645->ctrls); + mutex_destroy(&ov5645->power_lock); + + return 0; +} + +static const struct i2c_device_id ov5645_id[] = { + { "ov5645", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ov5645_id); + +static const struct of_device_id ov5645_of_match[] = { + { .compatible = "ovti,ov5645" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ov5645_of_match); + +static struct i2c_driver ov5645_i2c_driver = { + .driver = { + .of_match_table = of_match_ptr(ov5645_of_match), + .name = "ov5645", + }, + .probe = ov5645_probe, + .remove = ov5645_remove, + .id_table = ov5645_id, +}; + +module_i2c_driver(ov5645_i2c_driver); + +MODULE_DESCRIPTION("Omnivision OV5645 Camera Driver"); +MODULE_AUTHOR("Todor Tomov <todor.tomov@linaro.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c new file mode 100644 index 000000000000..f57a0b354cf6 --- /dev/null +++ b/drivers/media/i2c/ov5647.c @@ -0,0 +1,634 @@ +/* + * A V4L2 driver for OmniVision OV5647 cameras. + * + * Based on Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor driver + * Copyright (C) 2011 Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * Based on Omnivision OV7670 Camera Driver + * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.net> + * + * Copyright (C) 2016, Synopsys, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-image-sizes.h> +#include <media/v4l2-mediabus.h> +#include <media/v4l2-of.h> + +#define SENSOR_NAME "ov5647" + +#define OV5647_SW_RESET 0x0103 +#define OV5647_REG_CHIPID_H 0x300A +#define OV5647_REG_CHIPID_L 0x300B + +#define REG_TERM 0xfffe +#define VAL_TERM 0xfe +#define REG_DLY 0xffff + +#define OV5647_ROW_START 0x01 +#define OV5647_ROW_START_MIN 0 +#define OV5647_ROW_START_MAX 2004 +#define OV5647_ROW_START_DEF 54 + +#define OV5647_COLUMN_START 0x02 +#define OV5647_COLUMN_START_MIN 0 +#define OV5647_COLUMN_START_MAX 2750 +#define OV5647_COLUMN_START_DEF 16 + +#define OV5647_WINDOW_HEIGHT 0x03 +#define OV5647_WINDOW_HEIGHT_MIN 2 +#define OV5647_WINDOW_HEIGHT_MAX 2006 +#define OV5647_WINDOW_HEIGHT_DEF 1944 + +#define OV5647_WINDOW_WIDTH 0x04 +#define OV5647_WINDOW_WIDTH_MIN 2 +#define OV5647_WINDOW_WIDTH_MAX 2752 +#define OV5647_WINDOW_WIDTH_DEF 2592 + +struct regval_list { + u16 addr; + u8 data; +}; + +struct ov5647 { + struct v4l2_subdev sd; + struct media_pad pad; + struct mutex lock; + struct v4l2_mbus_framefmt format; + unsigned int width; + unsigned int height; + int power_count; + struct clk *xclk; +}; + +static inline struct ov5647 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov5647, sd); +} + +static struct regval_list sensor_oe_disable_regs[] = { + {0x3000, 0x00}, + {0x3001, 0x00}, + {0x3002, 0x00}, +}; + +static struct regval_list sensor_oe_enable_regs[] = { + {0x3000, 0x0f}, + {0x3001, 0xff}, + {0x3002, 0xe4}, +}; + +static struct regval_list ov5647_640x480[] = { + {0x0100, 0x00}, + {0x0103, 0x01}, + {0x3034, 0x08}, + {0x3035, 0x21}, + {0x3036, 0x46}, + {0x303c, 0x11}, + {0x3106, 0xf5}, + {0x3821, 0x07}, + {0x3820, 0x41}, + {0x3827, 0xec}, + {0x370c, 0x0f}, + {0x3612, 0x59}, + {0x3618, 0x00}, + {0x5000, 0x06}, + {0x5001, 0x01}, + {0x5002, 0x41}, + {0x5003, 0x08}, + {0x5a00, 0x08}, + {0x3000, 0x00}, + {0x3001, 0x00}, + {0x3002, 0x00}, + {0x3016, 0x08}, + {0x3017, 0xe0}, + {0x3018, 0x44}, + {0x301c, 0xf8}, + {0x301d, 0xf0}, + {0x3a18, 0x00}, + {0x3a19, 0xf8}, + {0x3c01, 0x80}, + {0x3b07, 0x0c}, + {0x380c, 0x07}, + {0x380d, 0x68}, + {0x380e, 0x03}, + {0x380f, 0xd8}, + {0x3814, 0x31}, + {0x3815, 0x31}, + {0x3708, 0x64}, + {0x3709, 0x52}, + {0x3808, 0x02}, + {0x3809, 0x80}, + {0x380a, 0x01}, + {0x380b, 0xE0}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x00}, + {0x3804, 0x0a}, + {0x3805, 0x3f}, + {0x3806, 0x07}, + {0x3807, 0xa1}, + {0x3811, 0x08}, + {0x3813, 0x02}, + {0x3630, 0x2e}, + {0x3632, 0xe2}, + {0x3633, 0x23}, + {0x3634, 0x44}, + {0x3636, 0x06}, + {0x3620, 0x64}, + {0x3621, 0xe0}, + {0x3600, 0x37}, + {0x3704, 0xa0}, + {0x3703, 0x5a}, + {0x3715, 0x78}, + {0x3717, 0x01}, + {0x3731, 0x02}, + {0x370b, 0x60}, + {0x3705, 0x1a}, + {0x3f05, 0x02}, + {0x3f06, 0x10}, + {0x3f01, 0x0a}, + {0x3a08, 0x01}, + {0x3a09, 0x27}, + {0x3a0a, 0x00}, + {0x3a0b, 0xf6}, + {0x3a0d, 0x04}, + {0x3a0e, 0x03}, + {0x3a0f, 0x58}, + {0x3a10, 0x50}, + {0x3a1b, 0x58}, + {0x3a1e, 0x50}, + {0x3a11, 0x60}, + {0x3a1f, 0x28}, + {0x4001, 0x02}, + {0x4004, 0x02}, + {0x4000, 0x09}, + {0x4837, 0x24}, + {0x4050, 0x6e}, + {0x4051, 0x8f}, + {0x0100, 0x01}, +}; + +static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) +{ + int ret; + unsigned char data[3] = { reg >> 8, reg & 0xff, val}; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + ret = i2c_master_send(client, data, 3); + if (ret < 0) + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + + return ret; +} + +static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val) +{ + int ret; + unsigned char data_w[2] = { reg >> 8, reg & 0xff }; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + ret = i2c_master_send(client, data_w, 2); + if (ret < 0) { + dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", + __func__, reg); + return ret; + } + + ret = i2c_master_recv(client, val, 1); + if (ret < 0) + dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n", + __func__, reg); + + return ret; +} + +static int ov5647_write_array(struct v4l2_subdev *sd, + struct regval_list *regs, int array_size) +{ + int i, ret; + + for (i = 0; i < array_size; i++) { + ret = ov5647_write(sd, regs[i].addr, regs[i].data); + if (ret < 0) + return ret; + } + + return 0; +} + +static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel) +{ + u8 channel_id; + int ret; + + ret = ov5647_read(sd, 0x4814, &channel_id); + if (ret < 0) + return ret; + + channel_id &= ~(3 << 6); + return ov5647_write(sd, 0x4814, channel_id | (channel << 6)); +} + +static int ov5647_stream_on(struct v4l2_subdev *sd) +{ + int ret; + + ret = ov5647_write(sd, 0x4202, 0x00); + if (ret < 0) + return ret; + + return ov5647_write(sd, 0x300D, 0x00); +} + +static int ov5647_stream_off(struct v4l2_subdev *sd) +{ + int ret; + + ret = ov5647_write(sd, 0x4202, 0x0f); + if (ret < 0) + return ret; + + return ov5647_write(sd, 0x300D, 0x01); +} + +static int set_sw_standby(struct v4l2_subdev *sd, bool standby) +{ + int ret; + u8 rdval; + + ret = ov5647_read(sd, 0x0100, &rdval); + if (ret < 0) + return ret; + + if (standby) + rdval &= ~0x01; + else + rdval |= 0x01; + + return ov5647_write(sd, 0x0100, rdval); +} + +static int __sensor_init(struct v4l2_subdev *sd) +{ + int ret; + u8 resetval, rdval; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + ret = ov5647_read(sd, 0x0100, &rdval); + if (ret < 0) + return ret; + + ret = ov5647_write_array(sd, ov5647_640x480, + ARRAY_SIZE(ov5647_640x480)); + if (ret < 0) { + dev_err(&client->dev, "write sensor default regs error\n"); + return ret; + } + + ret = ov5647_set_virtual_channel(sd, 0); + if (ret < 0) + return ret; + + ret = ov5647_read(sd, 0x0100, &resetval); + if (ret < 0) + return ret; + + if (!(resetval & 0x01)) { + dev_err(&client->dev, "Device was in SW standby"); + ret = ov5647_write(sd, 0x0100, 0x01); + if (ret < 0) + return ret; + } + + return ov5647_write(sd, 0x4800, 0x04); +} + +static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) +{ + int ret = 0; + struct ov5647 *ov5647 = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + mutex_lock(&ov5647->lock); + + if (on && !ov5647->power_count) { + dev_dbg(&client->dev, "OV5647 power on\n"); + + ret = clk_prepare_enable(ov5647->xclk); + if (ret < 0) { + dev_err(&client->dev, "clk prepare enable failed\n"); + goto out; + } + + ret = ov5647_write_array(sd, sensor_oe_enable_regs, + ARRAY_SIZE(sensor_oe_enable_regs)); + if (ret < 0) { + clk_disable_unprepare(ov5647->xclk); + dev_err(&client->dev, + "write sensor_oe_enable_regs error\n"); + goto out; + } + + ret = __sensor_init(sd); + if (ret < 0) { + clk_disable_unprepare(ov5647->xclk); + dev_err(&client->dev, + "Camera not available, check Power\n"); + goto out; + } + } else if (!on && ov5647->power_count == 1) { + dev_dbg(&client->dev, "OV5647 power off\n"); + + ret = ov5647_write_array(sd, sensor_oe_disable_regs, + ARRAY_SIZE(sensor_oe_disable_regs)); + + if (ret < 0) + dev_dbg(&client->dev, "disable oe failed\n"); + + ret = set_sw_standby(sd, true); + + if (ret < 0) + dev_dbg(&client->dev, "soft stby failed\n"); + + clk_disable_unprepare(ov5647->xclk); + } + + /* Update the power count. */ + ov5647->power_count += on ? 1 : -1; + WARN_ON(ov5647->power_count < 0); + +out: + mutex_unlock(&ov5647->lock); + + return ret; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov5647_sensor_get_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + u8 val; + int ret; + + ret = ov5647_read(sd, reg->reg & 0xff, &val); + if (ret < 0) + return ret; + + reg->val = val; + reg->size = 1; + + return 0; +} + +static int ov5647_sensor_set_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + return ov5647_write(sd, reg->reg & 0xff, reg->val & 0xff); +} +#endif + +/** + * @short Subdev core operations registration + */ +static const struct v4l2_subdev_core_ops ov5647_subdev_core_ops = { + .s_power = ov5647_sensor_power, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov5647_sensor_get_register, + .s_register = ov5647_sensor_set_register, +#endif +}; + +static int ov5647_s_stream(struct v4l2_subdev *sd, int enable) +{ + if (enable) + return ov5647_stream_on(sd); + else + return ov5647_stream_off(sd); +} + +static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = { + .s_stream = ov5647_s_stream, +}; + +static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = MEDIA_BUS_FMT_SBGGR8_1X8; + + return 0; +} + +static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = { + .enum_mbus_code = ov5647_enum_mbus_code, +}; + +static const struct v4l2_subdev_ops ov5647_subdev_ops = { + .core = &ov5647_subdev_core_ops, + .video = &ov5647_subdev_video_ops, + .pad = &ov5647_subdev_pad_ops, +}; + +static int ov5647_detect(struct v4l2_subdev *sd) +{ + u8 read; + int ret; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + ret = ov5647_write(sd, OV5647_SW_RESET, 0x01); + if (ret < 0) + return ret; + + ret = ov5647_read(sd, OV5647_REG_CHIPID_H, &read); + if (ret < 0) + return ret; + + if (read != 0x56) { + dev_err(&client->dev, "ID High expected 0x56 got %x", read); + return -ENODEV; + } + + ret = ov5647_read(sd, OV5647_REG_CHIPID_L, &read); + if (ret < 0) + return ret; + + if (read != 0x47) { + dev_err(&client->dev, "ID Low expected 0x47 got %x", read); + return -ENODEV; + } + + return ov5647_write(sd, OV5647_SW_RESET, 0x00); +} + +static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + struct v4l2_rect *crop = + v4l2_subdev_get_try_crop(sd, fh->pad, 0); + + crop->left = OV5647_COLUMN_START_DEF; + crop->top = OV5647_ROW_START_DEF; + crop->width = OV5647_WINDOW_WIDTH_DEF; + crop->height = OV5647_WINDOW_HEIGHT_DEF; + + format->code = MEDIA_BUS_FMT_SBGGR8_1X8; + + format->width = OV5647_WINDOW_WIDTH_DEF; + format->height = OV5647_WINDOW_HEIGHT_DEF; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = { + .open = ov5647_open, +}; + +static int ov5647_parse_dt(struct device_node *np) +{ + struct v4l2_of_endpoint bus_cfg; + struct device_node *ep; + + int ret; + + ep = of_graph_get_next_endpoint(np, NULL); + if (!ep) + return -EINVAL; + + ret = v4l2_of_parse_endpoint(ep, &bus_cfg); + + of_node_put(ep); + return ret; +} + +static int ov5647_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct ov5647 *sensor; + int ret; + struct v4l2_subdev *sd; + struct device_node *np = client->dev.of_node; + u32 xclk_freq; + + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return -ENOMEM; + + if (IS_ENABLED(CONFIG_OF) && np) { + ret = ov5647_parse_dt(np); + if (ret) { + dev_err(dev, "DT parsing error: %d\n", ret); + return ret; + } + } + + /* get system clock (xclk) */ + sensor->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(sensor->xclk)) { + dev_err(dev, "could not get xclk"); + return PTR_ERR(sensor->xclk); + } + + xclk_freq = clk_get_rate(sensor->xclk); + if (xclk_freq != 25000000) { + dev_err(dev, "Unsupported clock frequency: %u\n", xclk_freq); + return -EINVAL; + } + + mutex_init(&sensor->lock); + + sd = &sensor->sd; + v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops); + sensor->sd.internal_ops = &ov5647_subdev_internal_ops; + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad); + if (ret < 0) + goto mutex_remove; + + ret = ov5647_detect(sd); + if (ret < 0) + goto error; + + ret = v4l2_async_register_subdev(sd); + if (ret < 0) + goto error; + + dev_dbg(dev, "OmniVision OV5647 camera driver probed\n"); + return 0; +error: + media_entity_cleanup(&sd->entity); +mutex_remove: + mutex_destroy(&sensor->lock); + return ret; +} + +static int ov5647_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov5647 *ov5647 = to_state(sd); + + v4l2_async_unregister_subdev(&ov5647->sd); + media_entity_cleanup(&ov5647->sd.entity); + v4l2_device_unregister_subdev(sd); + mutex_destroy(&ov5647->lock); + + return 0; +} + +static const struct i2c_device_id ov5647_id[] = { + { "ov5647", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov5647_id); + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov5647_of_match[] = { + { .compatible = "ovti,ov5647" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ov5647_of_match); +#endif + +static struct i2c_driver ov5647_driver = { + .driver = { + .of_match_table = of_match_ptr(ov5647_of_match), + .name = SENSOR_NAME, + }, + .probe = ov5647_probe, + .remove = ov5647_remove, + .id_table = ov5647_id, +}; + +module_i2c_driver(ov5647_driver); + +MODULE_AUTHOR("Ramiro Oliveira <roliveir@synopsys.com>"); +MODULE_DESCRIPTION("A low-level driver for OmniVision ov5647 sensors"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 56cfb5ca9c95..7270c68ed18a 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -10,12 +10,15 @@ * This file may be distributed under the terms of the GNU General * Public License, version 2. */ +#include <linux/clk.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/i2c.h> #include <linux/delay.h> #include <linux/videodev2.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-mediabus.h> @@ -227,6 +230,9 @@ struct ov7670_info { struct v4l2_ctrl *hue; }; struct ov7670_format_struct *fmt; /* Current format */ + struct clk *clk; + struct gpio_desc *resetb_gpio; + struct gpio_desc *pwdn_gpio; int min_width; /* Filter out smaller sizes */ int min_height; /* Filter out smaller sizes */ int clock_speed; /* External clock speed (MHz) */ @@ -589,8 +595,6 @@ static int ov7670_init(struct v4l2_subdev *sd, u32 val) return ov7670_write_array(sd, ov7670_default_regs); } - - static int ov7670_detect(struct v4l2_subdev *sd) { unsigned char v; @@ -1046,7 +1050,6 @@ static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - memset(cp, 0, sizeof(struct v4l2_captureparm)); cp->capability = V4L2_CAP_TIMEPERFRAME; info->devtype->get_framerate(sd, &cp->timeperframe); @@ -1061,9 +1064,8 @@ static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - if (cp->extendedmode != 0) - return -EINVAL; + cp->capability = V4L2_CAP_TIMEPERFRAME; return info->devtype->set_framerate(sd, tpf); } @@ -1549,6 +1551,27 @@ static const struct ov7670_devtype ov7670_devdata[] = { }, }; +static int ov7670_init_gpio(struct i2c_client *client, struct ov7670_info *info) +{ + info->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown", + GPIOD_OUT_LOW); + if (IS_ERR(info->pwdn_gpio)) { + dev_info(&client->dev, "can't get %s GPIO\n", "powerdown"); + return PTR_ERR(info->pwdn_gpio); + } + + info->resetb_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(info->resetb_gpio)) { + dev_info(&client->dev, "can't get %s GPIO\n", "reset"); + return PTR_ERR(info->resetb_gpio); + } + + usleep_range(3000, 5000); + + return 0; +} + static int ov7670_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1589,13 +1612,28 @@ static int ov7670_probe(struct i2c_client *client, info->pclk_hb_disable = true; } + info->clk = devm_clk_get(&client->dev, "xclk"); + if (IS_ERR(info->clk)) + return -EPROBE_DEFER; + clk_prepare_enable(info->clk); + + ret = ov7670_init_gpio(client, info); + if (ret) + goto clk_disable; + + info->clock_speed = clk_get_rate(info->clk) / 1000000; + if (info->clock_speed < 10 || info->clock_speed > 48) { + ret = -EINVAL; + goto clk_disable; + } + /* Make sure it's an ov7670 */ ret = ov7670_detect(sd); if (ret) { v4l_dbg(1, debug, client, "chip found @ 0x%x (%s) is not an ov7670 chip.\n", client->addr << 1, client->adapter->name); - return ret; + goto clk_disable; } v4l_info(client, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); @@ -1636,10 +1674,9 @@ static int ov7670_probe(struct i2c_client *client, V4L2_EXPOSURE_AUTO); sd->ctrl_handler = &info->hdl; if (info->hdl.error) { - int err = info->hdl.error; + ret = info->hdl.error; - v4l2_ctrl_handler_free(&info->hdl); - return err; + goto hdl_free; } /* * We have checked empirically that hw allows to read back the gain @@ -1651,7 +1688,17 @@ static int ov7670_probe(struct i2c_client *client, v4l2_ctrl_cluster(2, &info->saturation); v4l2_ctrl_handler_setup(&info->hdl); + ret = v4l2_async_register_subdev(&info->sd); + if (ret < 0) + goto hdl_free; + return 0; + +hdl_free: + v4l2_ctrl_handler_free(&info->hdl); +clk_disable: + clk_disable_unprepare(info->clk); + return ret; } @@ -1662,6 +1709,7 @@ static int ov7670_remove(struct i2c_client *client) v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(&info->hdl); + clk_disable_unprepare(info->clk); return 0; } @@ -1672,9 +1720,18 @@ static const struct i2c_device_id ov7670_id[] = { }; MODULE_DEVICE_TABLE(i2c, ov7670_id); +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov7670_of_match[] = { + { .compatible = "ovti,ov7670", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ov7670_of_match); +#endif + static struct i2c_driver ov7670_driver = { .driver = { .name = "ov7670", + .of_match_table = of_match_ptr(ov7670_of_match), }, .probe = ov7670_probe, .remove = ov7670_remove, diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig index 7704bcf5cc25..96859f37cb1c 100644 --- a/drivers/media/i2c/soc_camera/Kconfig +++ b/drivers/media/i2c/soc_camera/Kconfig @@ -41,12 +41,6 @@ config SOC_CAMERA_MT9V022 help This driver supports MT9V022 cameras from Micron -config SOC_CAMERA_OV2640 - tristate "ov2640 camera support" - depends on SOC_CAMERA && I2C - help - This is a ov2640 camera driver - config SOC_CAMERA_OV5642 tristate "ov5642 camera support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index 6f994f9353a0..974bdb721dbe 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -3,7 +3,6 @@ obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o -obj-$(CONFIG_SOC_CAMERA_OV2640) += ov2640.o obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.o obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c index 05b55cfe8147..77f1e0243d6e 100644 --- a/drivers/media/i2c/soc_camera/imx074.c +++ b/drivers/media/i2c/soc_camera/imx074.c @@ -180,7 +180,7 @@ static int imx074_set_fmt(struct v4l2_subdev *sd, mf->field = V4L2_FIELD_NONE; if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - priv->fmt = imx074_find_datafmt(mf->code); + priv->fmt = fmt; else cfg->try_fmt = *mf; @@ -271,12 +271,12 @@ static int imx074_g_mbus_config(struct v4l2_subdev *sd, return 0; } -static struct v4l2_subdev_video_ops imx074_subdev_video_ops = { +static const struct v4l2_subdev_video_ops imx074_subdev_video_ops = { .s_stream = imx074_s_stream, .g_mbus_config = imx074_g_mbus_config, }; -static struct v4l2_subdev_core_ops imx074_subdev_core_ops = { +static const struct v4l2_subdev_core_ops imx074_subdev_core_ops = { .s_power = imx074_s_power, }; @@ -287,7 +287,7 @@ static const struct v4l2_subdev_pad_ops imx074_subdev_pad_ops = { .set_fmt = imx074_set_fmt, }; -static struct v4l2_subdev_ops imx074_subdev_ops = { +static const struct v4l2_subdev_ops imx074_subdev_ops = { .core = &imx074_subdev_core_ops, .video = &imx074_subdev_video_ops, .pad = &imx074_subdev_pad_ops, diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c index 3d6378d4491c..1bfb0d53059e 100644 --- a/drivers/media/i2c/soc_camera/mt9m001.c +++ b/drivers/media/i2c/soc_camera/mt9m001.c @@ -278,6 +278,7 @@ static int mt9m001_get_fmt(struct v4l2_subdev *sd, } static int mt9m001_s_fmt(struct v4l2_subdev *sd, + const struct mt9m001_datafmt *fmt, struct v4l2_mbus_framefmt *mf) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -297,9 +298,8 @@ static int mt9m001_s_fmt(struct v4l2_subdev *sd, if (!ret) { mf->width = mt9m001->rect.width; mf->height = mt9m001->rect.height; - mt9m001->fmt = mt9m001_find_datafmt(mf->code, - mt9m001->fmts, mt9m001->num_fmts); - mf->colorspace = mt9m001->fmt->colorspace; + mt9m001->fmt = fmt; + mf->colorspace = fmt->colorspace; } return ret; @@ -335,7 +335,7 @@ static int mt9m001_set_fmt(struct v4l2_subdev *sd, mf->colorspace = fmt->colorspace; if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return mt9m001_s_fmt(sd, mf); + return mt9m001_s_fmt(sd, fmt, mf); cfg->try_fmt = *mf; return 0; } @@ -574,7 +574,7 @@ static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = { .s_ctrl = mt9m001_s_ctrl, }; -static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { +static const struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9m001_g_register, .s_register = mt9m001_s_register, @@ -630,7 +630,7 @@ static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, return bps == 10 ? 0 : -EINVAL; } -static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { +static const struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { .s_stream = mt9m001_s_stream, .g_mbus_config = mt9m001_g_mbus_config, .s_mbus_config = mt9m001_s_mbus_config, @@ -648,7 +648,7 @@ static const struct v4l2_subdev_pad_ops mt9m001_subdev_pad_ops = { .set_fmt = mt9m001_set_fmt, }; -static struct v4l2_subdev_ops mt9m001_subdev_ops = { +static const struct v4l2_subdev_ops mt9m001_subdev_ops = { .core = &mt9m001_subdev_core_ops, .video = &mt9m001_subdev_video_ops, .sensor = &mt9m001_subdev_sensor_ops, diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c index 3aa5569065ad..714fb3555b34 100644 --- a/drivers/media/i2c/soc_camera/mt9t031.c +++ b/drivers/media/i2c/soc_camera/mt9t031.c @@ -679,7 +679,7 @@ static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = { .s_ctrl = mt9t031_s_ctrl, }; -static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { +static const struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { .s_power = mt9t031_s_power, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9t031_g_register, @@ -726,7 +726,7 @@ static int mt9t031_s_mbus_config(struct v4l2_subdev *sd, return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); } -static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { +static const struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { .s_stream = mt9t031_s_stream, .g_mbus_config = mt9t031_g_mbus_config, .s_mbus_config = mt9t031_s_mbus_config, @@ -744,7 +744,7 @@ static const struct v4l2_subdev_pad_ops mt9t031_subdev_pad_ops = { .set_fmt = mt9t031_set_fmt, }; -static struct v4l2_subdev_ops mt9t031_subdev_ops = { +static const struct v4l2_subdev_ops mt9t031_subdev_ops = { .core = &mt9t031_subdev_core_ops, .video = &mt9t031_subdev_video_ops, .sensor = &mt9t031_subdev_sensor_ops, diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c index 2ef22241ec14..297d22ebcb18 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/mt9t112.c @@ -773,7 +773,7 @@ static int mt9t112_s_power(struct v4l2_subdev *sd, int on) return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); } -static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = { +static const struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9t112_g_register, .s_register = mt9t112_s_register, @@ -1031,7 +1031,7 @@ static int mt9t112_s_mbus_config(struct v4l2_subdev *sd, return 0; } -static struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = { +static const struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = { .s_stream = mt9t112_s_stream, .g_mbus_config = mt9t112_g_mbus_config, .s_mbus_config = mt9t112_s_mbus_config, @@ -1048,7 +1048,7 @@ static const struct v4l2_subdev_pad_ops mt9t112_subdev_pad_ops = { /************************************************************************ i2c driver ************************************************************************/ -static struct v4l2_subdev_ops mt9t112_subdev_ops = { +static const struct v4l2_subdev_ops mt9t112_subdev_ops = { .core = &mt9t112_subdev_core_ops, .video = &mt9t112_subdev_video_ops, .pad = &mt9t112_subdev_pad_ops, diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index 6a14ab5e4f2d..762f06919329 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -403,6 +403,7 @@ static int mt9v022_get_fmt(struct v4l2_subdev *sd, } static int mt9v022_s_fmt(struct v4l2_subdev *sd, + const struct mt9v022_datafmt *fmt, struct v4l2_mbus_framefmt *mf) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -441,9 +442,8 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd, if (!ret) { mf->width = mt9v022->rect.width; mf->height = mt9v022->rect.height; - mt9v022->fmt = mt9v022_find_datafmt(mf->code, - mt9v022->fmts, mt9v022->num_fmts); - mf->colorspace = mt9v022->fmt->colorspace; + mt9v022->fmt = fmt; + mf->colorspace = fmt->colorspace; } return ret; @@ -478,7 +478,7 @@ static int mt9v022_set_fmt(struct v4l2_subdev *sd, mf->colorspace = fmt->colorspace; if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return mt9v022_s_fmt(sd, mf); + return mt9v022_s_fmt(sd, fmt, mf); cfg->try_fmt = *mf; return 0; } @@ -770,7 +770,7 @@ static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = { .s_ctrl = mt9v022_s_ctrl, }; -static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { +static const struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9v022_g_register, .s_register = mt9v022_s_register, @@ -858,7 +858,7 @@ static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, return 0; } -static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { +static const struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { .s_stream = mt9v022_s_stream, .g_mbus_config = mt9v022_g_mbus_config, .s_mbus_config = mt9v022_s_mbus_config, @@ -876,7 +876,7 @@ static const struct v4l2_subdev_pad_ops mt9v022_subdev_pad_ops = { .set_fmt = mt9v022_set_fmt, }; -static struct v4l2_subdev_ops mt9v022_subdev_ops = { +static const struct v4l2_subdev_ops mt9v022_subdev_ops = { .core = &mt9v022_subdev_core_ops, .video = &mt9v022_subdev_video_ops, .sensor = &mt9v022_subdev_sensor_ops, diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c index 3d185bd622a3..39f420db9c70 100644 --- a/drivers/media/i2c/soc_camera/ov5642.c +++ b/drivers/media/i2c/soc_camera/ov5642.c @@ -811,7 +811,7 @@ static int ov5642_set_fmt(struct v4l2_subdev *sd, mf->field = V4L2_FIELD_NONE; if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - priv->fmt = ov5642_find_datafmt(mf->code); + priv->fmt = fmt; else cfg->try_fmt = *mf; return 0; @@ -943,7 +943,7 @@ static int ov5642_s_power(struct v4l2_subdev *sd, int on) return ret; } -static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = { +static const struct v4l2_subdev_video_ops ov5642_subdev_video_ops = { .g_mbus_config = ov5642_g_mbus_config, }; @@ -955,7 +955,7 @@ static const struct v4l2_subdev_pad_ops ov5642_subdev_pad_ops = { .set_fmt = ov5642_set_fmt, }; -static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { +static const struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { .s_power = ov5642_s_power, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov5642_get_register, @@ -963,7 +963,7 @@ static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { #endif }; -static struct v4l2_subdev_ops ov5642_subdev_ops = { +static const struct v4l2_subdev_ops ov5642_subdev_ops = { .core = &ov5642_subdev_core_ops, .video = &ov5642_subdev_video_ops, .pad = &ov5642_subdev_pad_ops, @@ -1063,9 +1063,18 @@ static const struct i2c_device_id ov5642_id[] = { }; MODULE_DEVICE_TABLE(i2c, ov5642_id); +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id ov5642_of_match[] = { + { .compatible = "ovti,ov5642" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ov5642_of_match); +#endif + static struct i2c_driver ov5642_i2c_driver = { .driver = { .name = "ov5642", + .of_match_table = of_match_ptr(ov5642_of_match), }, .probe = ov5642_probe, .remove = ov5642_remove, diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c index 4bf2995e1cb8..dbd6d92c589f 100644 --- a/drivers/media/i2c/soc_camera/ov6650.c +++ b/drivers/media/i2c/soc_camera/ov6650.c @@ -885,7 +885,7 @@ static const struct v4l2_ctrl_ops ov6550_ctrl_ops = { .s_ctrl = ov6550_s_ctrl, }; -static struct v4l2_subdev_core_ops ov6650_core_ops = { +static const struct v4l2_subdev_core_ops ov6650_core_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov6650_get_register, .s_register = ov6650_set_register, @@ -942,7 +942,7 @@ static int ov6650_s_mbus_config(struct v4l2_subdev *sd, return ret; } -static struct v4l2_subdev_video_ops ov6650_video_ops = { +static const struct v4l2_subdev_video_ops ov6650_video_ops = { .s_stream = ov6650_s_stream, .g_parm = ov6650_g_parm, .s_parm = ov6650_s_parm, @@ -958,7 +958,7 @@ static const struct v4l2_subdev_pad_ops ov6650_pad_ops = { .set_fmt = ov6650_set_fmt, }; -static struct v4l2_subdev_ops ov6650_subdev_ops = { +static const struct v4l2_subdev_ops ov6650_subdev_ops = { .core = &ov6650_core_ops, .video = &ov6650_video_ops, .pad = &ov6650_pad_ops, @@ -1033,7 +1033,7 @@ static int ov6650_probe(struct i2c_client *client, priv->code = MEDIA_BUS_FMT_YUYV8_2X8; priv->colorspace = V4L2_COLORSPACE_JPEG; - priv->clk = v4l2_clk_get(&client->dev, "mclk"); + priv->clk = v4l2_clk_get(&client->dev, NULL); if (IS_ERR(priv->clk)) { ret = PTR_ERR(priv->clk); goto eclkget; diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c index 985a3672b243..0f7b9d1b9c57 100644 --- a/drivers/media/i2c/soc_camera/ov772x.c +++ b/drivers/media/i2c/soc_camera/ov772x.c @@ -894,38 +894,15 @@ static int ov772x_get_fmt(struct v4l2_subdev *sd, return 0; } -static int ov772x_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) -{ - struct ov772x_priv *priv = to_ov772x(sd); - const struct ov772x_color_format *cfmt; - const struct ov772x_win_size *win; - int ret; - - ov772x_select_params(mf, &cfmt, &win); - - ret = ov772x_set_params(priv, cfmt, win); - if (ret < 0) - return ret; - - priv->win = win; - priv->cfmt = cfmt; - - mf->code = cfmt->code; - mf->width = win->rect.width; - mf->height = win->rect.height; - mf->field = V4L2_FIELD_NONE; - mf->colorspace = cfmt->colorspace; - - return 0; -} - static int ov772x_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { + struct ov772x_priv *priv = to_ov772x(sd); struct v4l2_mbus_framefmt *mf = &format->format; const struct ov772x_color_format *cfmt; const struct ov772x_win_size *win; + int ret; if (format->pad) return -EINVAL; @@ -938,9 +915,17 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd, mf->field = V4L2_FIELD_NONE; mf->colorspace = cfmt->colorspace; - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - return ov772x_s_fmt(sd, mf); - cfg->try_fmt = *mf; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + cfg->try_fmt = *mf; + return 0; + } + + ret = ov772x_set_params(priv, cfmt, win); + if (ret < 0) + return ret; + + priv->win = win; + priv->cfmt = cfmt; return 0; } @@ -993,7 +978,7 @@ static const struct v4l2_ctrl_ops ov772x_ctrl_ops = { .s_ctrl = ov772x_s_ctrl, }; -static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = { +static const struct v4l2_subdev_core_ops ov772x_subdev_core_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov772x_g_register, .s_register = ov772x_s_register, @@ -1027,7 +1012,7 @@ static int ov772x_g_mbus_config(struct v4l2_subdev *sd, return 0; } -static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { +static const struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { .s_stream = ov772x_s_stream, .g_mbus_config = ov772x_g_mbus_config, }; @@ -1039,7 +1024,7 @@ static const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = { .set_fmt = ov772x_set_fmt, }; -static struct v4l2_subdev_ops ov772x_subdev_ops = { +static const struct v4l2_subdev_ops ov772x_subdev_ops = { .core = &ov772x_subdev_core_ops, .video = &ov772x_subdev_video_ops, .pad = &ov772x_subdev_pad_ops, diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c index 65085a235128..0146d1f7aacb 100644 --- a/drivers/media/i2c/soc_camera/ov9640.c +++ b/drivers/media/i2c/soc_camera/ov9640.c @@ -486,11 +486,8 @@ static int ov9640_s_fmt(struct v4l2_subdev *sd, { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov9640_reg_alt alts = {0}; - enum v4l2_colorspace cspace; - u32 code = mf->code; int ret; - ov9640_res_roundup(&mf->width, &mf->height); ov9640_alter_regs(mf->code, &alts); ov9640_reset(client); @@ -499,24 +496,7 @@ static int ov9640_s_fmt(struct v4l2_subdev *sd, if (ret) return ret; - switch (code) { - case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: - case MEDIA_BUS_FMT_RGB565_2X8_LE: - cspace = V4L2_COLORSPACE_SRGB; - break; - default: - code = MEDIA_BUS_FMT_UYVY8_2X8; - case MEDIA_BUS_FMT_UYVY8_2X8: - cspace = V4L2_COLORSPACE_JPEG; - } - - ret = ov9640_write_regs(client, mf->width, code, &alts); - if (!ret) { - mf->code = code; - mf->colorspace = cspace; - } - - return ret; + return ov9640_write_regs(client, mf->width, mf->code, &alts); } static int ov9640_set_fmt(struct v4l2_subdev *sd, @@ -539,8 +519,10 @@ static int ov9640_set_fmt(struct v4l2_subdev *sd, break; default: mf->code = MEDIA_BUS_FMT_UYVY8_2X8; + /* fall through */ case MEDIA_BUS_FMT_UYVY8_2X8: mf->colorspace = V4L2_COLORSPACE_JPEG; + break; } if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) @@ -637,7 +619,7 @@ static const struct v4l2_ctrl_ops ov9640_ctrl_ops = { .s_ctrl = ov9640_s_ctrl, }; -static struct v4l2_subdev_core_ops ov9640_core_ops = { +static const struct v4l2_subdev_core_ops ov9640_core_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov9640_get_register, .s_register = ov9640_set_register, @@ -661,7 +643,7 @@ static int ov9640_g_mbus_config(struct v4l2_subdev *sd, return 0; } -static struct v4l2_subdev_video_ops ov9640_video_ops = { +static const struct v4l2_subdev_video_ops ov9640_video_ops = { .s_stream = ov9640_s_stream, .g_mbus_config = ov9640_g_mbus_config, }; @@ -672,7 +654,7 @@ static const struct v4l2_subdev_pad_ops ov9640_pad_ops = { .set_fmt = ov9640_set_fmt, }; -static struct v4l2_subdev_ops ov9640_subdev_ops = { +static const struct v4l2_subdev_ops ov9640_subdev_ops = { .core = &ov9640_core_ops, .video = &ov9640_video_ops, .pad = &ov9640_pad_ops, diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c index f11f76cdacad..cc07b7ae5407 100644 --- a/drivers/media/i2c/soc_camera/ov9740.c +++ b/drivers/media/i2c/soc_camera/ov9740.c @@ -673,20 +673,8 @@ static int ov9740_s_fmt(struct v4l2_subdev *sd, { struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov9740_priv *priv = to_ov9740(sd); - enum v4l2_colorspace cspace; - u32 code = mf->code; int ret; - ov9740_res_roundup(&mf->width, &mf->height); - - switch (code) { - case MEDIA_BUS_FMT_YUYV8_2X8: - cspace = V4L2_COLORSPACE_SRGB; - break; - default: - return -EINVAL; - } - ret = ov9740_reg_write_array(client, ov9740_defaults, ARRAY_SIZE(ov9740_defaults)); if (ret < 0) @@ -696,11 +684,7 @@ static int ov9740_s_fmt(struct v4l2_subdev *sd, if (ret < 0) return ret; - mf->code = code; - mf->colorspace = cspace; - - memcpy(&priv->current_mf, mf, sizeof(struct v4l2_mbus_framefmt)); - + priv->current_mf = *mf; return ret; } @@ -907,12 +891,12 @@ static int ov9740_g_mbus_config(struct v4l2_subdev *sd, return 0; } -static struct v4l2_subdev_video_ops ov9740_video_ops = { +static const struct v4l2_subdev_video_ops ov9740_video_ops = { .s_stream = ov9740_s_stream, .g_mbus_config = ov9740_g_mbus_config, }; -static struct v4l2_subdev_core_ops ov9740_core_ops = { +static const struct v4l2_subdev_core_ops ov9740_core_ops = { .s_power = ov9740_s_power, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov9740_get_register, @@ -926,7 +910,7 @@ static const struct v4l2_subdev_pad_ops ov9740_pad_ops = { .set_fmt = ov9740_set_fmt, }; -static struct v4l2_subdev_ops ov9740_subdev_ops = { +static const struct v4l2_subdev_ops ov9740_subdev_ops = { .core = &ov9740_core_ops, .video = &ov9740_video_ops, .pad = &ov9740_pad_ops, diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c index bc8ec59a3fbd..02398d0bc649 100644 --- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c +++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c @@ -1213,7 +1213,7 @@ static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = { .s_ctrl = rj54n1_s_ctrl, }; -static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = { +static const struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = rj54n1_g_register, .s_register = rj54n1_s_register, @@ -1251,7 +1251,7 @@ static int rj54n1_s_mbus_config(struct v4l2_subdev *sd, return reg_write(client, RJ54N1_OUT_SIGPO, 0); } -static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { +static const struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { .s_stream = rj54n1_s_stream, .g_mbus_config = rj54n1_g_mbus_config, .s_mbus_config = rj54n1_s_mbus_config, @@ -1265,7 +1265,7 @@ static const struct v4l2_subdev_pad_ops rj54n1_subdev_pad_ops = { .set_fmt = rj54n1_set_fmt, }; -static struct v4l2_subdev_ops rj54n1_subdev_ops = { +static const struct v4l2_subdev_ops rj54n1_subdev_ops = { .core = &rj54n1_subdev_core_ops, .video = &rj54n1_subdev_video_ops, .pad = &rj54n1_subdev_pad_ops, diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c index c9c49ed707b8..bdb5e0a431e9 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/tw9910.c @@ -837,7 +837,7 @@ done: return ret; } -static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { +static const struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = tw9910_g_register, .s_register = tw9910_s_register, @@ -901,7 +901,7 @@ static int tw9910_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm) return 0; } -static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { +static const struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { .s_std = tw9910_s_std, .g_std = tw9910_g_std, .s_stream = tw9910_s_stream, @@ -917,7 +917,7 @@ static const struct v4l2_subdev_pad_ops tw9910_subdev_pad_ops = { .set_fmt = tw9910_set_fmt, }; -static struct v4l2_subdev_ops tw9910_subdev_ops = { +static const struct v4l2_subdev_ops tw9910_subdev_ops = { .core = &tw9910_subdev_core_ops, .video = &tw9910_subdev_video_ops, .pad = &tw9910_subdev_pad_ops, diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index f569a05fe105..acef4eca269f 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -194,57 +194,61 @@ static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n) } } -static u8 i2c_rd8(struct v4l2_subdev *sd, u16 reg) +static noinline u32 i2c_rdreg(struct v4l2_subdev *sd, u16 reg, u32 n) { - u8 val; + __le32 val = 0; + + i2c_rd(sd, reg, (u8 __force *)&val, n); - i2c_rd(sd, reg, &val, 1); + return le32_to_cpu(val); +} + +static noinline void i2c_wrreg(struct v4l2_subdev *sd, u16 reg, u32 val, u32 n) +{ + __le32 raw = cpu_to_le32(val); - return val; + i2c_wr(sd, reg, (u8 __force *)&raw, n); +} + +static u8 i2c_rd8(struct v4l2_subdev *sd, u16 reg) +{ + return i2c_rdreg(sd, reg, 1); } static void i2c_wr8(struct v4l2_subdev *sd, u16 reg, u8 val) { - i2c_wr(sd, reg, &val, 1); + i2c_wrreg(sd, reg, val, 1); } static void i2c_wr8_and_or(struct v4l2_subdev *sd, u16 reg, u8 mask, u8 val) { - i2c_wr8(sd, reg, (i2c_rd8(sd, reg) & mask) | val); + i2c_wrreg(sd, reg, (i2c_rdreg(sd, reg, 2) & mask) | val, 2); } static u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg) { - u16 val; - - i2c_rd(sd, reg, (u8 *)&val, 2); - - return val; + return i2c_rdreg(sd, reg, 2); } static void i2c_wr16(struct v4l2_subdev *sd, u16 reg, u16 val) { - i2c_wr(sd, reg, (u8 *)&val, 2); + i2c_wrreg(sd, reg, val, 2); } static void i2c_wr16_and_or(struct v4l2_subdev *sd, u16 reg, u16 mask, u16 val) { - i2c_wr16(sd, reg, (i2c_rd16(sd, reg) & mask) | val); + i2c_wrreg(sd, reg, (i2c_rdreg(sd, reg, 2) & mask) | val, 2); } static u32 i2c_rd32(struct v4l2_subdev *sd, u16 reg) { - u32 val; - - i2c_rd(sd, reg, (u8 *)&val, 4); - - return val; + return i2c_rdreg(sd, reg, 4); } static void i2c_wr32(struct v4l2_subdev *sd, u16 reg, u32 val) { - i2c_wr(sd, reg, (u8 *)&val, 4); + i2c_wrreg(sd, reg, val, 4); } /* --------------- STATUS --------------- */ @@ -1227,7 +1231,7 @@ static int tc358743_g_register(struct v4l2_subdev *sd, reg->size = tc358743_get_reg_size(reg->reg); - i2c_rd(sd, reg->reg, (u8 *)®->val, reg->size); + reg->val = i2c_rdreg(sd, reg->reg, reg->size); return 0; } @@ -1253,7 +1257,7 @@ static int tc358743_s_register(struct v4l2_subdev *sd, reg->reg == BCAPS) return 0; - i2c_wr(sd, (u16)reg->reg, (u8 *)®->val, + i2c_wrreg(sd, (u16)reg->reg, reg->val, tc358743_get_reg_size(reg->reg)); return 0; @@ -1459,6 +1463,10 @@ static int tc358743_g_mbus_config(struct v4l2_subdev *sd, static int tc358743_s_stream(struct v4l2_subdev *sd, int enable) { enable_stream(sd, enable); + if (!enable) { + /* Put all lanes in PL-11 state (STOPSTATE) */ + tc358743_set_csi(sd); + } return 0; } @@ -1951,9 +1959,18 @@ static struct i2c_device_id tc358743_id[] = { MODULE_DEVICE_TABLE(i2c, tc358743_id); +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id tc358743_of_match[] = { + { .compatible = "toshiba,tc358743" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tc358743_of_match); +#endif + static struct i2c_driver tc358743_driver = { .driver = { .name = "tc358743", + .of_match_table = of_match_ptr(tc358743_of_match), }, .probe = tc358743_probe, .remove = tc358743_remove, diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 48646a7f3fb0..04e96b3057bb 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -865,13 +865,13 @@ static int tvp5150_fill_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f; struct tvp5150 *decoder = to_tvp5150(sd); - if (!format || format->pad) + if (!format || (format->pad != DEMOD_PAD_VID_OUT)) return -EINVAL; f = &format->format; f->width = decoder->rect.width; - f->height = decoder->rect.height / 2; + f->height = decoder->rect.height; f->code = MEDIA_BUS_FMT_UYVY8_2X8; f->field = V4L2_FIELD_ALTERNATE; |