aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c')
-rw-r--r--drivers/media/i2c/Kconfig43
-rw-r--r--drivers/media/i2c/Makefile3
-rw-r--r--drivers/media/i2c/ad5820.c4
-rw-r--r--drivers/media/i2c/adv7511.c6
-rw-r--r--drivers/media/i2c/adv7604.c6
-rw-r--r--drivers/media/i2c/adv7842.c6
-rw-r--r--drivers/media/i2c/et8ek8/et8ek8_driver.c2
-rw-r--r--drivers/media/i2c/ov2640.c (renamed from drivers/media/i2c/soc_camera/ov2640.c)291
-rw-r--r--drivers/media/i2c/ov5645.c1345
-rw-r--r--drivers/media/i2c/ov5647.c634
-rw-r--r--drivers/media/i2c/ov7670.c75
-rw-r--r--drivers/media/i2c/soc_camera/Kconfig6
-rw-r--r--drivers/media/i2c/soc_camera/Makefile1
-rw-r--r--drivers/media/i2c/soc_camera/imx074.c8
-rw-r--r--drivers/media/i2c/soc_camera/mt9m001.c14
-rw-r--r--drivers/media/i2c/soc_camera/mt9t031.c6
-rw-r--r--drivers/media/i2c/soc_camera/mt9t112.c6
-rw-r--r--drivers/media/i2c/soc_camera/mt9v022.c14
-rw-r--r--drivers/media/i2c/soc_camera/ov5642.c17
-rw-r--r--drivers/media/i2c/soc_camera/ov6650.c8
-rw-r--r--drivers/media/i2c/soc_camera/ov772x.c47
-rw-r--r--drivers/media/i2c/soc_camera/ov9640.c30
-rw-r--r--drivers/media/i2c/soc_camera/ov9740.c24
-rw-r--r--drivers/media/i2c/soc_camera/rj54n1cb0c.c6
-rw-r--r--drivers/media/i2c/soc_camera/tw9910.c6
-rw-r--r--drivers/media/i2c/tc358743.c59
-rw-r--r--drivers/media/i2c/tvp5150.c4
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 *)&reg->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 *)&reg->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;