|
@@ -16,6 +16,7 @@
|
|
#include <linux/init.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/module.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/i2c.h>
|
|
|
|
+#include <linux/clk.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/gpio.h>
|
|
@@ -24,8 +25,7 @@
|
|
#include <linux/v4l2-mediabus.h>
|
|
#include <linux/v4l2-mediabus.h>
|
|
#include <linux/videodev2.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-subdev.h>
|
|
#include <media/v4l2-ctrls.h>
|
|
#include <media/v4l2-ctrls.h>
|
|
#include <media/v4l2-image-sizes.h>
|
|
#include <media/v4l2-image-sizes.h>
|
|
@@ -106,6 +106,10 @@
|
|
#define CTRL1_AWB_GAIN 0x04
|
|
#define CTRL1_AWB_GAIN 0x04
|
|
#define CTRL1_LENC 0x02
|
|
#define CTRL1_LENC 0x02
|
|
#define CTRL1_PRE 0x01
|
|
#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 0xD3 /* DVP output speed control */
|
|
#define R_DVP_SP_AUTO_MODE 0x80
|
|
#define R_DVP_SP_AUTO_MODE 0x80
|
|
#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0);
|
|
#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_ZOOM_EN 0x04 /* Enable Zoom mode */
|
|
#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */
|
|
#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */
|
|
#define COM8 0x13 /* Common control 8 */
|
|
#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_BNDF_EN 0x20 /* Banding filter ON/OFF */
|
|
#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */
|
|
#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */
|
|
#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */
|
|
#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */
|
|
@@ -248,8 +252,19 @@
|
|
#define ZOOMS 0x49 /* Zoom: Vertical start point */
|
|
#define ZOOMS 0x49 /* Zoom: Vertical start point */
|
|
#define COM22 0x4B /* Flash light control */
|
|
#define COM22 0x4B /* Flash light control */
|
|
#define COM25 0x4E /* For Banding operations */
|
|
#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 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 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 REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */
|
|
#define REG5E 0x5E /* AVGsel[15:8], 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 */
|
|
#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */
|
|
@@ -282,12 +297,14 @@ struct ov2640_win_size {
|
|
|
|
|
|
struct ov2640_priv {
|
|
struct ov2640_priv {
|
|
struct v4l2_subdev subdev;
|
|
struct v4l2_subdev subdev;
|
|
|
|
+#if defined(CONFIG_MEDIA_CONTROLLER)
|
|
|
|
+ struct media_pad pad;
|
|
|
|
+#endif
|
|
struct v4l2_ctrl_handler hdl;
|
|
struct v4l2_ctrl_handler hdl;
|
|
u32 cfmt_code;
|
|
u32 cfmt_code;
|
|
- struct v4l2_clk *clk;
|
|
|
|
|
|
+ struct clk *clk;
|
|
const struct ov2640_win_size *win;
|
|
const struct ov2640_win_size *win;
|
|
|
|
|
|
- struct soc_camera_subdev_desc ssdd_dt;
|
|
|
|
struct gpio_desc *resetb_gpio;
|
|
struct gpio_desc *resetb_gpio;
|
|
struct gpio_desc *pwdn_gpio;
|
|
struct gpio_desc *pwdn_gpio;
|
|
};
|
|
};
|
|
@@ -304,11 +321,11 @@ static const struct regval_list ov2640_init_regs[] = {
|
|
{ 0x2e, 0xdf },
|
|
{ 0x2e, 0xdf },
|
|
{ BANK_SEL, BANK_SEL_SENS },
|
|
{ BANK_SEL, BANK_SEL_SENS },
|
|
{ 0x3c, 0x32 },
|
|
{ 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 },
|
|
{ 0x2c, 0x0c },
|
|
{ 0x33, 0x78 },
|
|
{ 0x33, 0x78 },
|
|
{ 0x3a, 0x33 },
|
|
{ 0x3a, 0x33 },
|
|
@@ -353,25 +370,28 @@ static const struct regval_list ov2640_init_regs[] = {
|
|
{ 0x71, 0x94 },
|
|
{ 0x71, 0x94 },
|
|
{ 0x73, 0xc1 },
|
|
{ 0x73, 0xc1 },
|
|
{ 0x3d, 0x34 },
|
|
{ 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 },
|
|
{ 0xe5, 0x7f },
|
|
- { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL },
|
|
|
|
|
|
+ { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL },
|
|
{ 0x41, 0x24 },
|
|
{ 0x41, 0x24 },
|
|
- { RESET, RESET_JPEG | RESET_DVP },
|
|
|
|
|
|
+ { RESET, RESET_JPEG | RESET_DVP },
|
|
{ 0x76, 0xff },
|
|
{ 0x76, 0xff },
|
|
{ 0x33, 0xa0 },
|
|
{ 0x33, 0xa0 },
|
|
{ 0x42, 0x20 },
|
|
{ 0x42, 0x20 },
|
|
{ 0x43, 0x18 },
|
|
{ 0x43, 0x18 },
|
|
{ 0x4c, 0x00 },
|
|
{ 0x4c, 0x00 },
|
|
- { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 },
|
|
|
|
|
|
+ { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 },
|
|
{ 0x88, 0x3f },
|
|
{ 0x88, 0x3f },
|
|
{ 0xd7, 0x03 },
|
|
{ 0xd7, 0x03 },
|
|
{ 0xd9, 0x10 },
|
|
{ 0xd9, 0x10 },
|
|
- { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 },
|
|
|
|
|
|
+ { R_DVP_SP, R_DVP_SP_AUTO_MODE | 0x2 },
|
|
{ 0xc8, 0x08 },
|
|
{ 0xc8, 0x08 },
|
|
{ 0xc9, 0x80 },
|
|
{ 0xc9, 0x80 },
|
|
{ BPADDR, 0x00 },
|
|
{ BPADDR, 0x00 },
|
|
@@ -433,7 +453,7 @@ static const struct regval_list ov2640_init_regs[] = {
|
|
{ 0xc5, 0x11 },
|
|
{ 0xc5, 0x11 },
|
|
{ 0xc6, 0x51 },
|
|
{ 0xc6, 0x51 },
|
|
{ 0xbf, 0x80 },
|
|
{ 0xbf, 0x80 },
|
|
- { 0xc7, 0x10 },
|
|
|
|
|
|
+ { 0xc7, 0x10 }, /* simple AWB */
|
|
{ 0xb6, 0x66 },
|
|
{ 0xb6, 0x66 },
|
|
{ 0xb8, 0xA5 },
|
|
{ 0xb8, 0xA5 },
|
|
{ 0xb7, 0x64 },
|
|
{ 0xb7, 0x64 },
|
|
@@ -480,6 +500,9 @@ static const struct regval_list ov2640_init_regs[] = {
|
|
static const struct regval_list ov2640_size_change_preamble_regs[] = {
|
|
static const struct regval_list ov2640_size_change_preamble_regs[] = {
|
|
{ BANK_SEL, BANK_SEL_DSP },
|
|
{ BANK_SEL, BANK_SEL_DSP },
|
|
{ RESET, RESET_DVP },
|
|
{ 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) },
|
|
{ HSIZE8, HSIZE8_SET(UXGA_WIDTH) },
|
|
{ VSIZE8, VSIZE8_SET(UXGA_HEIGHT) },
|
|
{ VSIZE8, VSIZE8_SET(UXGA_HEIGHT) },
|
|
{ CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN |
|
|
{ CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN |
|
|
@@ -611,6 +634,8 @@ static const struct regval_list ov2640_rgb565_le_regs[] = {
|
|
static u32 ov2640_codes[] = {
|
|
static u32 ov2640_codes[] = {
|
|
MEDIA_BUS_FMT_YUYV8_2X8,
|
|
MEDIA_BUS_FMT_YUYV8_2X8,
|
|
MEDIA_BUS_FMT_UYVY8_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_BE,
|
|
MEDIA_BUS_FMT_RGB565_2X8_LE,
|
|
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)
|
|
static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
{
|
|
{
|
|
struct v4l2_subdev *sd =
|
|
struct v4l2_subdev *sd =
|
|
@@ -698,8 +718,10 @@ static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
|
|
|
|
switch (ctrl->id) {
|
|
switch (ctrl->id) {
|
|
case V4L2_CID_VFLIP:
|
|
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:
|
|
case V4L2_CID_HFLIP:
|
|
val = ctrl->val ? REG04_HFLIP_IMG : 0x00;
|
|
val = ctrl->val ? REG04_HFLIP_IMG : 0x00;
|
|
return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val);
|
|
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)
|
|
static int ov2640_s_power(struct v4l2_subdev *sd, int on)
|
|
{
|
|
{
|
|
|
|
+#ifdef CONFIG_GPIOLIB
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
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);
|
|
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 */
|
|
/* 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;
|
|
int i, default_size = ARRAY_SIZE(ov2640_supported_win_sizes) - 1;
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ov2640_supported_win_sizes); i++) {
|
|
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];
|
|
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];
|
|
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);
|
|
struct ov2640_priv *priv = to_ov2640(client);
|
|
const struct regval_list *selected_cfmt_regs;
|
|
const struct regval_list *selected_cfmt_regs;
|
|
|
|
+ u8 val;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
/* select win */
|
|
/* select win */
|
|
- priv->win = ov2640_select_win(width, height);
|
|
|
|
|
|
+ priv->win = win;
|
|
|
|
|
|
/* select format */
|
|
/* select format */
|
|
priv->cfmt_code = 0;
|
|
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__);
|
|
dev_dbg(&client->dev, "%s: Selected cfmt YUYV (YUV422)", __func__);
|
|
selected_cfmt_regs = ov2640_yuyv_regs;
|
|
selected_cfmt_regs = ov2640_yuyv_regs;
|
|
break;
|
|
break;
|
|
- default:
|
|
|
|
case MEDIA_BUS_FMT_UYVY8_2X8:
|
|
case MEDIA_BUS_FMT_UYVY8_2X8:
|
|
|
|
+ default:
|
|
dev_dbg(&client->dev, "%s: Selected cfmt UYVY", __func__);
|
|
dev_dbg(&client->dev, "%s: Selected cfmt UYVY", __func__);
|
|
selected_cfmt_regs = ov2640_uyvy_regs;
|
|
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 */
|
|
/* reset hardware */
|
|
@@ -828,12 +864,15 @@ static int ov2640_set_params(struct i2c_client *client, u32 *width, u32 *height,
|
|
|
|
|
|
/* set cfmt */
|
|
/* set cfmt */
|
|
ret = ov2640_write_array(client, selected_cfmt_regs);
|
|
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)
|
|
if (ret < 0)
|
|
goto err;
|
|
goto err;
|
|
|
|
|
|
priv->cfmt_code = code;
|
|
priv->cfmt_code = code;
|
|
- *width = priv->win->width;
|
|
|
|
- *height = priv->win->height;
|
|
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
@@ -857,25 +896,14 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd,
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
if (!priv->win) {
|
|
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;
|
|
priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8;
|
|
}
|
|
}
|
|
|
|
|
|
mf->width = priv->win->width;
|
|
mf->width = priv->win->width;
|
|
mf->height = priv->win->height;
|
|
mf->height = priv->win->height;
|
|
mf->code = priv->cfmt_code;
|
|
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;
|
|
mf->field = V4L2_FIELD_NONE;
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
@@ -887,32 +915,34 @@ static int ov2640_set_fmt(struct v4l2_subdev *sd,
|
|
{
|
|
{
|
|
struct v4l2_mbus_framefmt *mf = &format->format;
|
|
struct v4l2_mbus_framefmt *mf = &format->format;
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
|
+ const struct ov2640_win_size *win;
|
|
|
|
|
|
if (format->pad)
|
|
if (format->pad)
|
|
return -EINVAL;
|
|
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->field = V4L2_FIELD_NONE;
|
|
|
|
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
|
|
|
|
|
|
switch (mf->code) {
|
|
switch (mf->code) {
|
|
case MEDIA_BUS_FMT_RGB565_2X8_BE:
|
|
case MEDIA_BUS_FMT_RGB565_2X8_BE:
|
|
case MEDIA_BUS_FMT_RGB565_2X8_LE:
|
|
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;
|
|
break;
|
|
default:
|
|
default:
|
|
mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
|
|
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)
|
|
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;
|
|
cfg->try_fmt = *mf;
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -995,7 +1025,7 @@ static const struct v4l2_ctrl_ops ov2640_ctrl_ops = {
|
|
.s_ctrl = ov2640_s_ctrl,
|
|
.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
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
.g_register = ov2640_g_register,
|
|
.g_register = ov2640_g_register,
|
|
.s_register = ov2640_s_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,
|
|
.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 = {
|
|
static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = {
|
|
.enum_mbus_code = ov2640_enum_mbus_code,
|
|
.enum_mbus_code = ov2640_enum_mbus_code,
|
|
.get_selection = ov2640_get_selection,
|
|
.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,
|
|
.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,
|
|
.core = &ov2640_subdev_core_ops,
|
|
- .video = &ov2640_subdev_video_ops,
|
|
|
|
.pad = &ov2640_subdev_pad_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,
|
|
static int ov2640_probe_dt(struct i2c_client *client,
|
|
struct ov2640_priv *priv)
|
|
struct ov2640_priv *priv)
|
|
{
|
|
{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
/* Request the reset GPIO deasserted */
|
|
/* Request the reset GPIO deasserted */
|
|
priv->resetb_gpio = devm_gpiod_get_optional(&client->dev, "resetb",
|
|
priv->resetb_gpio = devm_gpiod_get_optional(&client->dev, "resetb",
|
|
GPIOD_OUT_LOW);
|
|
GPIOD_OUT_LOW);
|
|
|
|
+
|
|
if (!priv->resetb_gpio)
|
|
if (!priv->resetb_gpio)
|
|
dev_dbg(&client->dev, "resetb gpio is not assigned!\n");
|
|
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 */
|
|
/* Request the power down GPIO asserted */
|
|
priv->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "pwdn",
|
|
priv->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "pwdn",
|
|
GPIOD_OUT_HIGH);
|
|
GPIOD_OUT_HIGH);
|
|
|
|
+
|
|
if (!priv->pwdn_gpio)
|
|
if (!priv->pwdn_gpio)
|
|
dev_dbg(&client->dev, "pwdn gpio is not assigned!\n");
|
|
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;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -1100,7 +1088,6 @@ static int ov2640_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *did)
|
|
const struct i2c_device_id *did)
|
|
{
|
|
{
|
|
struct ov2640_priv *priv;
|
|
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);
|
|
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
@@ -1117,23 +1104,19 @@ static int ov2640_probe(struct i2c_client *client,
|
|
return -ENOMEM;
|
|
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);
|
|
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_handler_init(&priv->hdl, 2);
|
|
v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
|
|
v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
|
|
V4L2_CID_VFLIP, 0, 1, 1, 0);
|
|
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;
|
|
priv->subdev.ctrl_handler = &priv->hdl;
|
|
if (priv->hdl.error) {
|
|
if (priv->hdl.error) {
|
|
ret = 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);
|
|
ret = ov2640_video_probe(client);
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
@@ -1158,9 +1148,13 @@ static int ov2640_probe(struct i2c_client *client,
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
err_videoprobe:
|
|
err_videoprobe:
|
|
|
|
+#if defined(CONFIG_MEDIA_CONTROLLER)
|
|
|
|
+ media_entity_cleanup(&priv->subdev.entity);
|
|
|
|
+#endif
|
|
|
|
+err_hdl:
|
|
v4l2_ctrl_handler_free(&priv->hdl);
|
|
v4l2_ctrl_handler_free(&priv->hdl);
|
|
err_clk:
|
|
err_clk:
|
|
- v4l2_clk_put(priv->clk);
|
|
|
|
|
|
+ clk_disable_unprepare(priv->clk);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1169,9 +1163,12 @@ static int ov2640_remove(struct i2c_client *client)
|
|
struct ov2640_priv *priv = to_ov2640(client);
|
|
struct ov2640_priv *priv = to_ov2640(client);
|
|
|
|
|
|
v4l2_async_unregister_subdev(&priv->subdev);
|
|
v4l2_async_unregister_subdev(&priv->subdev);
|
|
- v4l2_clk_put(priv->clk);
|
|
|
|
- v4l2_device_unregister_subdev(&priv->subdev);
|
|
|
|
v4l2_ctrl_handler_free(&priv->hdl);
|
|
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;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1199,6 +1196,6 @@ static struct i2c_driver ov2640_i2c_driver = {
|
|
|
|
|
|
module_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_AUTHOR("Alberto Panizzo");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_LICENSE("GPL v2");
|