Skip to content

Add support for user supplied ov7740 register settings. #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 181 additions & 9 deletions drivers/media/i2c/soc_camera/ov7740.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>

#include <linux/ov7740.h> /* OV7740 specific IOCTL's */

#define VAL_SET(x, mask, rshift, lshift) \
((((x) >> rshift) & mask) << lshift)

Expand Down Expand Up @@ -55,17 +57,22 @@ struct regval_list {
/* Supported resolutions */
enum ov7740_width {
W_VGA = 640,
W_QVGA = 320,
};

enum ov7740_height {
H_VGA = 480,
H_QVGA = 240,
};

struct ov7740_win_size {
char *name;
enum ov7740_width width;
enum ov7740_height height;
const struct regval_list *regs;
// It seems there are some registers that will give a NAK error when written to.
// The ignore_errors flags tells the driver to ignore this issue.
int ignore_errors;
};


Expand All @@ -87,7 +94,7 @@ struct ov7740_priv {

#define ENDMARKER { 0xff, 0xff }

static const struct regval_list ov7740_init_regs[] = {
static struct regval_list ov7740_vga_regs[VIDIOC_OV7740_MAX_REGS + 1] = {
{0x55 ,0x40},
{0x11 ,0x02},

Expand Down Expand Up @@ -208,15 +215,19 @@ static const struct regval_list ov7740_init_regs[] = {
ENDMARKER,
};

static const struct regval_list ov7740_vga_regs[] = {
/* Initial registers is for vga format, no setting needed */
static struct regval_list ov7740_qvga_regs[VIDIOC_OV7740_MAX_REGS + 1] = {
/* These are only used if a user program supplies QVGA register
* settings for the OV7740.
*/
ENDMARKER,
};

#define OV7740_SIZE(n, w, h, r) \
{.name = n, .width = w , .height = h, .regs = r }
{.name = n, .width = w , .height = h, .regs = r, .ignore_errors = 0 }

static const struct ov7740_win_size ov7740_supported_win_sizes[] = {
#define QVGA_INDEX 0 // Index 0 can be replaced by user supplied QVGA register values
static struct ov7740_win_size ov7740_supported_win_sizes[] = {
OV7740_SIZE("VGA", W_VGA, H_VGA, ov7740_vga_regs),
OV7740_SIZE("VGA", W_VGA, H_VGA, ov7740_vga_regs),
};

Expand All @@ -234,7 +245,8 @@ static struct ov7740_priv *to_ov7740(const struct i2c_client *client)
}

static int ov7740_write_array(struct i2c_client *client,
const struct regval_list *vals)
const struct regval_list *vals,
int ignore_errors)
{
int ret;

Expand All @@ -244,7 +256,7 @@ static int ov7740_write_array(struct i2c_client *client,
dev_vdbg(&client->dev, "array: 0x%02x, 0x%02x",
vals->reg_num, vals->value);

if (ret < 0) {
if (!ignore_errors && ret < 0) {
dev_err(&client->dev, "array: 0x%02x, 0x%02x write failed",
vals->reg_num, vals->value);
return ret;
Expand Down Expand Up @@ -277,7 +289,7 @@ static int ov7740_reset(struct i2c_client *client)
ENDMARKER,
};

ret = ov7740_write_array(client, reset_seq);
ret = ov7740_write_array(client, reset_seq, 0);
if (ret)
goto err;

Expand Down Expand Up @@ -356,6 +368,160 @@ static int ov7740_s_power(struct v4l2_subdev *sd, int on)
return soc_camera_set_power(&client->dev, ssdd, priv->clk, on);
}

static long ov7740_set_regs(struct i2c_client *c,
struct v4l2_ov7740_register_group *g,
struct regval_list *r)
{
int i;
long ret = 0;
if (g->numregs == VIDIOC_OV7740_AUTOSIZE_FLAG) {
for (i = 0;
((i < VIDIOC_OV7740_MAX_REGS) &&
!(g->regs[i].reg == 0xFF && g->regs[i].val == 0xFF));
i++) {
r[i].reg_num = g->regs[i].reg;
r[i].value = g->regs[i].val;
}
} else if (g->numregs >= 0 && g->numregs < VIDIOC_OV7740_MAX_REGS) {
for (i = 0; i < g->numregs; i++) {
r[i].reg_num = g->regs[i].reg;
r[i].value = g->regs[i].val;
}
} else {
ret = -EINVAL;
goto done;
}
// ENDMARKER
r[i].reg_num = 0xFF;
r[i].value = 0xFF;
done:
return 0;
}

static long ov7740_get_regs(struct i2c_client *c,
struct v4l2_ov7740_register_group *g,
const struct regval_list *r)
{
int count = 0;
while (count < VIDIOC_OV7740_MAX_REGS &&
!(r[count].reg_num == 0xFF && r[count].value == 0xFF)) {
g->regs[count].reg = r[count].reg_num;
g->regs[count].val = r[count].value;
count++;
}
g->numregs = count;
return 0;
}

static inline long ov7740_set_vga_regs(struct i2c_client *c,
struct v4l2_ov7740_register_group *g)
{
return ov7740_set_regs(c, g, ov7740_vga_regs);
}

static inline long ov7740_get_vga_regs(struct i2c_client *c,
struct v4l2_ov7740_register_group *g)
{
return ov7740_get_regs(c, g, ov7740_vga_regs);
}

static inline long ov7740_set_qvga_regs(struct i2c_client *c,
struct v4l2_ov7740_register_group *g)
{
long ret = ov7740_set_regs(c, g, ov7740_qvga_regs);
if (ret < 0)
return ret;
// We assume (and require) that the user supplied register values do, in fact,
// configure the device for QVGA mode.
ov7740_supported_win_sizes[QVGA_INDEX].name = "QVGA";
ov7740_supported_win_sizes[QVGA_INDEX].width = W_QVGA;
ov7740_supported_win_sizes[QVGA_INDEX].height = H_QVGA;
ov7740_supported_win_sizes[QVGA_INDEX].regs = ov7740_qvga_regs;
ov7740_supported_win_sizes[QVGA_INDEX].ignore_errors = g->ignore_errors;
return 0;
}

static inline long ov7740_get_qvga_regs(struct i2c_client *c,
struct v4l2_ov7740_register_group *g)
{
return ov7740_get_regs(c, g, ov7740_qvga_regs);
}

static long ov7740_get_register_group(struct i2c_client *c,
struct v4l2_ov7740_register_group *g)
{
long ret;
int i;

for (i = 0; i < g->numregs; i++) {
if (g->regs[i].reg > 0xff)
return -EINVAL;
ret = i2c_smbus_read_byte_data(c, g->regs[i].reg);
if (ret < 0)
return ret;
g->regs[i].val = ret;
}
return 0;
}

static long ov7740_set_register_group(struct i2c_client *c,
struct v4l2_ov7740_register_group *g)
{
long ret;
int i;

for (i = 0; i < g->numregs; i++) {
if (g->regs[i].reg > 0xff ||
g->regs[i].val > 0xff)
return -EINVAL;
ret = i2c_smbus_write_byte_data(c, g->regs[i].reg, g->regs[i].val);
if (ret < 0)
return ret;
g->regs[i].val = ret;
}
return 0;
}

static long ov7740_ioctl(struct v4l2_subdev *sd,
unsigned int cmd,
void *arg)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct v4l2_ov7740_register_group *g = arg;
long ret;

switch (cmd) {
case VIDIOC_OV7740_S_VGA_REGS:
ret = ov7740_set_vga_regs(client, g);
break;

case VIDIOC_OV7740_G_VGA_REGS:
ret = ov7740_get_vga_regs(client, g);
break;

case VIDIOC_OV7740_S_QVGA_REGS:
ret = ov7740_set_qvga_regs(client, g);
break;

case VIDIOC_OV7740_G_QVGA_REGS:
ret = ov7740_get_qvga_regs(client, g);
break;

case VIDIOC_OV7740_G_REGISTER_GROUP:
ret = ov7740_get_register_group(client, g);
break;

case VIDIOC_OV7740_S_REGISTER_GROUP:
ret = ov7740_set_register_group(client, g);
break;

default:
ret = -ENOTTY;
}

return ret;
}

/* Select the nearest higher resolution for capture */
static const struct ov7740_win_size *ov7740_select_win(u32 *width, u32 *height)
{
Expand Down Expand Up @@ -397,7 +563,7 @@ static int ov7740_set_params(struct i2c_client *client, u32 *width, u32 *height,

/* initialize the sensor with default data */
dev_dbg(&client->dev, "%s: Init default", __func__);
ret = ov7740_write_array(client, ov7740_init_regs);
ret = ov7740_write_array(client, priv->win->regs, priv->win->ignore_errors);
if (ret < 0)
goto err;

Expand Down Expand Up @@ -550,6 +716,7 @@ static struct v4l2_subdev_core_ops ov7740_subdev_core_ops = {
.s_register = ov7740_s_register,
#endif
.s_power = ov7740_s_power,
.ioctl = ov7740_ioctl,
};

static int ov7740_g_mbus_config(struct v4l2_subdev *sd,
Expand Down Expand Up @@ -691,6 +858,7 @@ static int ov7740_probe(struct i2c_client *client,
}

v4l2_i2c_subdev_init(&priv->subdev, client, &ov7740_subdev_ops);
priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
v4l2_ctrl_handler_init(&priv->hdl, 2);
v4l2_ctrl_new_std(&priv->hdl, &ov7740_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
Expand All @@ -710,6 +878,10 @@ static int ov7740_probe(struct i2c_client *client,
if (ret < 0)
goto err_handler;

ret = v4l2_device_register_subdev_nodes(priv->subdev.v4l2_dev);
if (ret < 0)
goto err_handler;

dev_info(&adapter->dev, "OV7740 Probed\n");
return 0;

Expand Down
70 changes: 70 additions & 0 deletions include/uapi/linux/ov7740.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* ov7740.h
*
* OmniVision ov7740 Image Sensor - User-space API
*
* Copyright (C) 2015 iRobot
*
* Contact: Patrick Doyle <pdoyle@irobot.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/

#ifndef _UAPI_OV7740_H
#define _UAPI_OV7740_H

#include <linux/types.h>
#include <linux/videodev2.h>

#define VIDIOC_OV7740_MAX_REGS 256 // Nobody should ever need more than 640Kbytes of RAM
#define VIDIOC_OV7740_AUTOSIZE_FLAG -1
#define VIDIOC_OV7740_ENDMARKER { 0xff, 0xff }

struct v4l2_ov7740_register_group {
__s32 numregs;
// Note: Set the ignore_errors flag to ignore all errors when writing registers.
// It seems there are some registers that will give a NAK error when written to.
__u32 ignore_errors;
struct {
__u8 reg;
__u8 val;
} regs[VIDIOC_OV7740_MAX_REGS + 1];
} __attribute__ ((packed));

/*
* Private IOCTLs
*
* VIDIOC_OV7740_S_VGA_REGS: Configure register settings for VGA mode
* VIDIOC_OV7740_G_VGA_REGS: Retrieve register settings for VGA mode
* VIDIOC_OV7740_S_QVGA_REGS: Configure register settings for QVGA mode
* VIDIOC_OV7740_G_QVGA_REGS: Retrieve register settings for QVGA mode
* VIDIOC_OV7740_S_REGISTER_GROUP: Write a group of registers on the device
* VIDIOC_OV7740_G_REGISTER_GROUP: Read a group of registers on the device
*/

#define VIDIOC_OV7740_S_VGA_REGS \
_IOW ('V', BASE_VIDIOC_PRIVATE + 0, struct v4l2_ov7740_register_group)
#define VIDIOC_OV7740_G_VGA_REGS \
_IOWR('V', BASE_VIDIOC_PRIVATE + 1, struct v4l2_ov7740_register_group)
#define VIDIOC_OV7740_S_QVGA_REGS \
_IOW ('V', BASE_VIDIOC_PRIVATE + 2, struct v4l2_ov7740_register_group)
#define VIDIOC_OV7740_G_QVGA_REGS \
_IOWR('V', BASE_VIDIOC_PRIVATE + 3, struct v4l2_ov7740_register_group)
#define VIDIOC_OV7740_S_REGISTER_GROUP \
_IOW ('V', BASE_VIDIOC_PRIVATE + 4, struct v4l2_ov7740_register_group)
#define VIDIOC_OV7740_G_REGISTER_GROUP \
_IOWR('V', BASE_VIDIOC_PRIVATE + 5, struct v4l2_ov7740_register_group)

#endif /* _UAPI_OV7740_H */