Tool/software: Linux
Hi all,
I am using latest linux processor sdk am57xx with ov10635 camera module.
I did some changes in driver code similar to mt9t11x.c.
Probing is done. The driver is registered. But while using yavta command, it only gives-
Device /dev/video1 opened.
Device `vip' on `platform:vip' is a video output (without mplanes) device.
Video format set: YUYV (56595559) 640x480 (stride 1280) field none buffer size 614400
Video format: YUYV (56595559) 640x480 (stride 1280) field none buffer size 614400
8 buffers requested.
length: 614400 offset: 0 timestamp type/source: mono/EoF
Buffer 0/0 mapped at address 0xb6e27000.
length: 614400 offset: 614400 timestamp type/source: mono/EoF
Buffer 1/0 mapped at address 0xb6d91000.
length: 614400 offset: 1228800 timestamp type/source: mono/EoF
Buffer 2/0 mapped at address 0xb6cfb000.
length: 614400 offset: 1843200 timestamp type/source: mono/EoF
Buffer 3/0 mapped at address 0xb6c65000.
length: 614400 offset: 2457600 timestamp type/source: mono/EoF
Buffer 4/0 mapped at address 0xb6bcf000.
length: 614400 offset: 3072000 timestamp type/source: mono/EoF
Buffer 5/0 mapped at address 0xb6b39000.
length: 614400 offset: 3686400 timestamp type/source: mono/EoF
Buffer 6/0 mapped at address 0xb6aa3000.
length: 614400 offset: 4300800 timestamp type/source: mono/EoF
Buffer 7/0 mapped at address 0xb6a0d000.
I also enable all logs but there are no error displayed.
Please find the driver code if there is any requirement for checking my changes.
/*
* OmniVision OV1063X Camera Driver
*
* Based on the original driver written by Phil Edworthy.
* Copyright (C) 2013 Phil Edworthy
* Copyright (C) 2013 Renesas Electronics
*
* This driver has been tested at QVGA, VGA and 720p, and 1280x800 at up to
* 30fps and it should work at any resolution in between and any frame rate
* up to 30fps.
*
* FIXME:
* Horizontal flip (mirroring) does not work correctly. The image is flipped,
* but the colors are wrong.
*
* 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.
*
*/
#define DEBUG
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/pm_runtime.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
#include <linux/v4l2-mediabus.h>
#include <media/media-entity.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-image-sizes.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-common.h>
#include <media/v4l2-of.h>
#include "ov1063x_regs.h"
/* Register definitions */
#define OV1063X_VFLIP 0x381c
#define OV1063X_VFLIP_ON (0x3 << 6)
#define OV1063X_VFLIP_SUBSAMPLE 0x1
#define OV1063X_HMIRROR 0x381d
#define OV1063X_HMIRROR_ON 0x3
#define OV1063X_PID 0x300a
#define OV1063X_VER 0x300b
#define OV1063X_FORMAT_CTRL00 0x4300
#define OV1063X_FORMAT_YUYV 0x38
#define OV1063X_FORMAT_YYYU 0x39
#define OV1063X_FORMAT_UYVY 0x3A
#define OV1063X_FORMAT_VYUY 0x3B
/* IDs */
#define OV10633_VERSION_REG 0xa630
#define OV10635_VERSION_REG 0xa635
#define OV1063X_VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xff))
enum ov1063x_model {
SENSOR_OV10633,
SENSOR_OV10635,
};
#define OV1063X_SENSOR_WIDTH 1312
#define OV1063X_SENSOR_HEIGHT 814
#define OV1063X_MAX_WIDTH 1280
#define OV1063X_MAX_HEIGHT 800
#define MAX_NUM_GPIOS 20
struct ov1063x_color_format {
u32 code;
u32 colorspace;
};
struct ov1063x_framesize {
u16 width;
u16 height;
};
struct ov1063x_priv {
struct v4l2_subdev subdev;
struct v4l2_async_subdev asd;
struct v4l2_ctrl_handler hdl;
int model;
int revision;
int xvclk;
/* Protects the struct fields below */
struct mutex lock;
int fps_numerator;
int fps_denominator;
int power;
int streaming;
struct v4l2_mbus_framefmt format;
int width;
int height;
struct gpio_desc *powerdown_gpio;
struct gpio mux_gpios[MAX_NUM_GPIOS];
int num_gpios;
};
static int ov1063x_init_gpios(struct i2c_client *client);
static const struct ov1063x_framesize ov1063x_framesizes[] = {
{
.width = 1280,
.height = 800,
}, {
.width = 1280,
.height = 720,
}, {
.width = 752,
.height = 480,
}, {
.width = 640,
.height = 480,
}, {
.width = 600,
.height = 400,
}, {
.width = 352,
.height = 288,
}, {
.width = 320,
.height = 240,
},
};
/*
* supported color format list
*/
static const struct ov1063x_color_format ov1063x_cfmts[] = {
// {
// .code = MEDIA_BUS_FMT_YUYV8_2X8,
// .colorspace = V4L2_COLORSPACE_SMPTE170M,
// },
{
.code = MEDIA_BUS_FMT_UYVY8_2X8,
.colorspace = V4L2_COLORSPACE_SMPTE170M,
},
// {
// .code = MEDIA_BUS_FMT_VYUY8_2X8,
// .colorspace = V4L2_COLORSPACE_SMPTE170M,
// },
// {
// .code = MEDIA_BUS_FMT_YVYU8_2X8,
// .colorspace = V4L2_COLORSPACE_SMPTE170M,
// },
// {
// .code = MEDIA_BUS_FMT_YUYV10_2X10,
// .colorspace = V4L2_COLORSPACE_SMPTE170M,
// },
};
static struct ov1063x_priv *to_ov1063x(const struct i2c_client *client)
{
return container_of(i2c_get_clientdata(client), struct ov1063x_priv,
subdev);
}
/* read a register */
static int ov1063x_reg_read(struct i2c_client *client, u16 reg, u8 *val)
{
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
int ret;
u8 data[2] = { reg >> 8, reg & 0xff };
struct i2c_msg msg = {
.addr = client->addr,
.flags = 0,
.len = 2,
.buf = data,
};
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0)
goto err;
msg.flags = I2C_M_RD;
msg.len = 1;
msg.buf = &data[0];
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0)
goto err;
*val = data[0];
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return 0;
err:
dev_err(&client->dev, "Failed reading register 0x%04x!\n", reg);
return ret;
}
/* write a register */
static int ov1063x_reg_write(struct i2c_client *client, u16 reg, u8 val)
{
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
int ret;
u8 data[3] = { reg >> 8, reg & 0xff, val };
struct i2c_msg msg = {
.addr = client->addr,
.flags = 0,
.len = 3,
.buf = data,
};
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0) {
dev_err(&client->dev, "Failed writing register 0x%04x!\n", reg);
return ret;
}
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return 0;
}
static int ov1063x_reg_write16(struct i2c_client *client, u16 reg, u16 val)
{
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
int ret;
ret = ov1063x_reg_write(client, reg, val >> 8);
if (ret)
return ret;
ret = ov1063x_reg_write(client, reg + 1, val & 0xff);
if (ret)
return ret;
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return 0;
}
/* Read a register, alter its bits, write it back */
static int ov1063x_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset)
{
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
u8 val;
int ret;
ret = ov1063x_reg_read(client, reg, &val);
if (ret) {
dev_err(&client->dev,
"[Read]-Modify-Write of register %04x failed!\n", reg);
return ret;
}
val |= set;
val &= ~unset;
ret = ov1063x_reg_write(client, reg, val);
if (ret)
dev_err(&client->dev,
"Read-Modify-[Write] of register %04x failed!\n", reg);
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return ret;
}
//changes
static void __ov1063x_set_power(struct i2c_client *client, int on)
{
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
int gpio_read;
//struct i2c_client *client = v4l2_get_subdevdata(priv->subdev);
struct ov1063x_priv *priv = i2c_get_clientdata(client);
dev_dbg(&client->dev, "%s: on: %d\n", __func__, on);
on = (on) ? 1 : 0;
if (priv->power == on){
printk("priv_power_on_returning\n");
return;}
if (on) {
if (priv->powerdown_gpio){
gpiod_set_value_cansleep(priv->powerdown_gpio, 1);
gpio_read = gpiod_get_raw_value(priv->powerdown_gpio);
dev_err(&client->dev, "POWERDOWN_GPIO_After_IN_ON %d", gpio_read);
}
usleep_range(25000, 26000);
} else {
if (priv->powerdown_gpio){
gpiod_set_value_cansleep(priv->powerdown_gpio, 0);
gpio_read = gpiod_get_raw_value(priv->powerdown_gpio);
dev_err(&client->dev, "POWERDOWN_GPIO_After_IN_OFF %d", gpio_read);
}
}
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
priv->power = on;
}
/* Start/Stop streaming from the device */
static int ov1063x_s_stream(struct v4l2_subdev *sd, int enable)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
//change
struct ov1063x_priv *priv = to_ov1063x(client);
//change
int ret;
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
mutex_lock(&priv->lock);
//if (priv->streaming == enable) {
// mutex_unlock(&priv->lock);
// return 0;
//}
printk("Enable_stream\n");printk("%d\n",enable);
//if (!enable) {
/* Stop Streaming Sequence */
//__ov1063x_set_power(client, 0);
// priv->streaming = enable;
// mutex_unlock(&priv->lock);printk("Enable_stream_return\n");
// return 0;
//}
//changes
__ov1063x_set_power(client, 1);
ret = ov1063x_init_gpios(client);
if (ret) {
dev_err(&client->dev, "Failed to request gpios");
return ret;
}
ov1063x_reg_write(client, 0x0100, enable);
if (enable){
ov1063x_reg_write(client, 0x301c, 0xf0);
//printk("enable_ov_reg_write\n");
}
else{
ov1063x_reg_write(client, 0x301c, 0x70);
//printk("disable_ov_reg_write\n");
}
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
//priv->streaming = enable;
mutex_unlock(&priv->lock);
return 0;
}
/* Set status of additional camera capabilities */
static int ov1063x_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov1063x_priv *priv = container_of(ctrl->handler,
struct ov1063x_priv, hdl);
struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
// mutex_lock(&priv->lock);
/*
* If the device is not powered up now, postpone applying control's
* value to the hardware, until it is ready to accept commands.
*/
// if (priv->power == 0) {
// printk("priv->power\n");
// mutex_unlock(&priv->lock);
// return 0;
// }
switch (ctrl->id) {
case V4L2_CID_VFLIP:
if (ctrl->val)
return ov1063x_reg_rmw(client, OV1063X_VFLIP,
OV1063X_VFLIP_ON, 0);
else
return ov1063x_reg_rmw(client, OV1063X_VFLIP,
0, OV1063X_VFLIP_ON);
break;
case V4L2_CID_HFLIP:
if (ctrl->val)
return ov1063x_reg_rmw(client, OV1063X_HMIRROR,
OV1063X_HMIRROR_ON, 0);
else
return ov1063x_reg_rmw(client, OV1063X_HMIRROR,
0, OV1063X_HMIRROR_ON);
break;
}
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
// mutex_unlock(&priv->lock);
return -EINVAL;
}
/*
* Get the best pixel clock (pclk) that meets minimum hts/vts requirements.
* xvclk => pre-divider => clk1 => multiplier => clk2 => post-divider => pclk
* We try all valid combinations of settings for the 3 blocks to get the pixel
* clock, and from that calculate the actual hts/vts to use. The vts is
* extended so as to achieve the required frame rate. The function also returns
* the PLL register contents needed to set the pixel clock.
*/
static int ov1063x_get_pclk(int xvclk, int *htsmin, int *vtsmin,
int fps_numerator, int fps_denominator,
u8 *r3003, u8 *r3004)
{
int pre_divs[] = { 2, 3, 4, 6, 8, 10, 12, 14 };
int pclk;
int best_pclk = INT_MAX;
int best_hts = 0;
int i, j, k;
int best_i = 0, best_j = 0, best_k = 0;
int clk1, clk2;
int hts;
//printk("PS-SE Enter, ov1063x_get_pclk\n");
/* Pre-div, reg 0x3004, bits 6:4 */
for (i = 0; i < ARRAY_SIZE(pre_divs); i++) {
clk1 = (xvclk / pre_divs[i]) * 2;
if ((clk1 < 3000000) || (clk1 > 27000000))
continue;
/* Mult = reg 0x3003, bits 5:0 */
for (j = 1; j < 32; j++) {
clk2 = (clk1 * j);
if ((clk2 < 200000000) || (clk2 > 500000000))
continue;
/* Post-div, reg 0x3004, bits 2:0 */
for (k = 0; k < 8; k++) {
pclk = clk2 / (2 * (k + 1));
if (pclk > 96000000)
continue;
hts = *htsmin + 200 + pclk / 300000;
/* 2 clock cycles for every YUV422 pixel */
if (pclk < (((hts * *vtsmin) / fps_denominator)
* fps_numerator * 2))
continue;
if (pclk < best_pclk) {
best_pclk = pclk;
best_hts = hts;
best_i = i;
best_j = j;
best_k = k;
}
}
}
}
/* register contents */
*r3003 = (u8)best_j;
*r3004 = ((u8)best_i << 4) | (u8)best_k;
/* Did we get a valid PCLK? */
if (best_pclk == INT_MAX)
return -1;
*htsmin = best_hts;
/* Adjust vts to get as close to the desired frame rate as we can */
*vtsmin = best_pclk / ((best_hts / fps_denominator) *
fps_numerator * 2);
//printk("PS-SE Exit, ov1063x_get_pclk\n");
return best_pclk;
}
static int ov1063x_set_regs(struct i2c_client *client,
const struct ov1063x_reg *regs, int nr_regs)
{
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
int i, ret;
u8 val;
for (i = 0; i < nr_regs; i++) {
if (regs[i].reg == 0x300c) {
val = ((client->addr * 2) | 0x1);
//printk("val\n");//printk("%d\n",val);
ret = ov1063x_reg_write(client, regs[i].reg, val);
//printk("ov1063x_reg_write ret value\n");//printk("%d\n", ret);
if (ret)
return ret;
} else {
ret = ov1063x_reg_write(client, regs[i].reg,
regs[i].val);
//printk("ov1063x_reg_write in else\n");//printk("%d\n",ret);
if (ret)
return ret;
}
}
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return 0;
}
/* Setup registers according to resolution and color encoding */
static int ov1063x_set_params(struct i2c_client *client, u32 width, u32 height)
{
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
struct ov1063x_priv *priv = to_ov1063x(client);
int ret = -EINVAL;
int pclk;
int hts, vts;
u8 r3003, r3004, r4300;
int tmp;
u32 height_pre_subsample;
u32 width_pre_subsample;
u8 horiz_crop_mode;
int nr_isp_pixels;
int vert_sub_sample = 0;
int horiz_sub_sample = 0;
int sensor_width;
int tmp_array_size;
if ((width > OV1063X_MAX_WIDTH) || (height > OV1063X_MAX_HEIGHT))
return ret;
priv->width = width;
priv->height = height;
/* Vertical sub-sampling? */
height_pre_subsample = priv->height;
if (priv->height <= 400) {
vert_sub_sample = 1;
height_pre_subsample <<= 1;
}
/* Horizontal sub-sampling? */
width_pre_subsample = priv->width;
if (priv->width <= 640) {
horiz_sub_sample = 1;
width_pre_subsample <<= 1;
}
/* Horizontal cropping */
if (width_pre_subsample > 768) {
sensor_width = OV1063X_SENSOR_WIDTH;
horiz_crop_mode = 0x63;
} else if (width_pre_subsample > 656) {
sensor_width = 768;
horiz_crop_mode = 0x6b;
} else {
sensor_width = 656;
horiz_crop_mode = 0x73;
}
/* minimum values for hts and vts */
hts = sensor_width;
vts = height_pre_subsample + 50;
dev_dbg(&client->dev, "fps=(%d/%d), hts=%d, vts=%d\n",
priv->fps_numerator, priv->fps_denominator, hts, vts);
/* Get the best PCLK & adjust hts,vts accordingly */
pclk = ov1063x_get_pclk(priv->xvclk, &hts, &vts, priv->fps_numerator,
priv->fps_denominator, &r3003, &r3004);
if (pclk < 0)
return ret;
dev_dbg(&client->dev, "pclk=%d, hts=%d, vts=%d\n", pclk, hts, vts);
dev_dbg(&client->dev, "r3003=0x%X r3004=0x%X\n", r3003, r3004);
/* Disable ISP & program all registers that we might modify */
ret = ov1063x_set_regs(client, ov1063x_regs_change_mode,
ARRAY_SIZE(ov1063x_regs_change_mode));
if (ret){
//printk("ov1063x_set_regs_ret_set\n");
return ret;}
/* Set to 1280x720 */
ret = ov1063x_reg_write(client, 0x380f, 0x80);
if (ret)
return ret;
/* Set PLL */
ret = ov1063x_reg_write(client, 0x3003, r3003);
if (ret)
return ret;
ret = ov1063x_reg_write(client, 0x3004, r3004);
if (ret)
return ret;
/* Set HSYNC */
ret = ov1063x_reg_write(client, 0x4700, 0x00);
if (ret)
return ret;
switch (priv->format.code) {
case MEDIA_BUS_FMT_UYVY8_2X8:
r4300 = OV1063X_FORMAT_UYVY;
break;
case MEDIA_BUS_FMT_VYUY8_2X8:
r4300 = OV1063X_FORMAT_VYUY;
break;
case MEDIA_BUS_FMT_YUYV8_2X8:
r4300 = OV1063X_FORMAT_YUYV;
break;
case MEDIA_BUS_FMT_YVYU8_2X8:
r4300 = OV1063X_FORMAT_YYYU;
break;
default:
r4300 = OV1063X_FORMAT_UYVY;
break;
}
/* Set format to UYVY */
ret = ov1063x_reg_write(client, OV1063X_FORMAT_CTRL00, r4300);
if (ret)
return ret;
dev_dbg(&client->dev, "r4300=0x%X\n", r4300);
/* Set output to 8-bit yuv */
ret = ov1063x_reg_write(client, 0x4605, 0x08);
if (ret)
return ret;
/* Horizontal cropping */
ret = ov1063x_reg_write(client, 0x3621, horiz_crop_mode);
if (ret)
return ret;
ret = ov1063x_reg_write(client, 0x3702, (pclk + 1500000) / 3000000);
if (ret)
return ret;
ret = ov1063x_reg_write(client, 0x3703, (pclk + 666666) / 1333333);
if (ret)
return ret;
ret = ov1063x_reg_write(client, 0x3704, (pclk + 961500) / 1923000);
if (ret)
return ret;
/* Vertical cropping */
tmp = ((OV1063X_SENSOR_HEIGHT - height_pre_subsample) / 2) & ~0x1;
ret = ov1063x_reg_write16(client, 0x3802, tmp);
if (ret)
return ret;
tmp = tmp + height_pre_subsample + 3;
ret = ov1063x_reg_write16(client, 0x3806, tmp);
if (ret)
return ret;
dev_dbg(&client->dev, "width x height = %x x %x\n",
priv->width, priv->height);
/* Output size */
ret = ov1063x_reg_write16(client, 0x3808, priv->width);
if (ret)
return ret;
ret = ov1063x_reg_write16(client, 0x380a, priv->height);
if (ret)
return ret;
dev_dbg(&client->dev, "hts x vts = %x x %x\n", hts, vts);
ret = ov1063x_reg_write16(client, 0x380c, hts);
if (ret)
return ret;
ret = ov1063x_reg_write16(client, 0x380e, vts);
if (ret)
return ret;
if (vert_sub_sample) {
ret = ov1063x_reg_rmw(client, OV1063X_VFLIP,
OV1063X_VFLIP_SUBSAMPLE, 0);
if (ret)
return ret;
tmp_array_size = ARRAY_SIZE(ov1063x_regs_vert_sub_sample);
ret = ov1063x_set_regs(client, ov1063x_regs_vert_sub_sample,
tmp_array_size);
if (ret)
return ret;
}
ret = ov1063x_reg_write16(client, 0x4606, 2 * hts);
if (ret)
return ret;
ret = ov1063x_reg_write16(client, 0x460a,
2 * (hts - width_pre_subsample));
if (ret)
return ret;
tmp = (vts - 8) * 16;
ret = ov1063x_reg_write16(client, 0xc488, tmp);
if (ret)
return ret;
ret = ov1063x_reg_write16(client, 0xc48a, tmp);
if (ret)
return ret;
nr_isp_pixels = sensor_width * (priv->height + 4);
ret = ov1063x_reg_write16(client, 0xc4cc, nr_isp_pixels / 256);
if (ret)
return ret;
ret = ov1063x_reg_write16(client, 0xc4ce, nr_isp_pixels / 256);
if (ret)
return ret;
ret = ov1063x_reg_write16(client, 0xc512, nr_isp_pixels / 16);
if (ret)
return ret;
/* Horizontal sub-sampling */
if (horiz_sub_sample) {
ret = ov1063x_reg_write(client, 0x5005, 0x9);
if (ret)
return ret;
ret = ov1063x_reg_write(client, 0x3007, 0x2);
if (ret)
return ret;
}
ret = ov1063x_reg_write16(client, 0xc518, vts);
if (ret)
return ret;
ret = ov1063x_reg_write16(client, 0xc51a, hts);
if (ret)
return ret;
/* Enable ISP blocks */
//printk("ov1063x_set_enter5\n");
ret = ov1063x_set_regs(client, ov1063x_regs_enable,
ARRAY_SIZE(ov1063x_regs_enable));
//printk("ov1063x_set_exit5\n");
if (ret){
printk("ov1063x_set_regs_ret_set\n");
return ret;}
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return 0;
}
/*
* V4L2 subdev video and pad level operations
*/
static void ov1063x_get_default_format(struct v4l2_mbus_framefmt *mf)
{
//printk("Enter ov1063x_get_default_format\n");
mf->width = ov1063x_framesizes[0].width;
mf->height = ov1063x_framesizes[0].height;
mf->colorspace = ov1063x_cfmts[0].colorspace;
mf->code = ov1063x_cfmts[0].code;
mf->field = V4L2_FIELD_NONE;
//printk("Exit ov1063x_get_default_format\n");
}
static int ov1063x_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov1063x_priv *priv = to_ov1063x(client);
struct v4l2_mbus_framefmt *mf;
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
mf = v4l2_subdev_get_try_format(sd, cfg, 0);
mutex_lock(&priv->lock);
fmt->format = *mf;
mutex_unlock(&priv->lock);
return 0;
}
mutex_lock(&priv->lock);
fmt->format = priv->format;
mutex_unlock(&priv->lock);
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return 0;
}
static void __ov1063x_try_frame_size(struct v4l2_mbus_framefmt *mf)
{
//printk("Enter __ov1063x_try_frame_size\n");
const struct ov1063x_framesize *fsize = &ov1063x_framesizes[0];
const struct ov1063x_framesize *match = NULL;
int i = ARRAY_SIZE(ov1063x_framesizes);
unsigned int min_err = UINT_MAX;
while (i--) {
int err = abs(fsize->width - mf->width)
+ abs(fsize->height - mf->height);
if (err < min_err) {
min_err = err;
match = fsize;
}
fsize++;
}
if (!match)
match = &ov1063x_framesizes[0];
mf->width = match->width;
mf->height = match->height;
//printk("Exit __ov1063x_try_frame_size\n");
}
static int ov1063x_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *fmt)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
unsigned int index = ARRAY_SIZE(ov1063x_cfmts);
struct ov1063x_priv *priv = to_ov1063x(client);
struct v4l2_mbus_framefmt *mf = &fmt->format;
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
int ret = 0;
__ov1063x_try_frame_size(mf);
while (--index >= 0)
if (ov1063x_cfmts[index].code == mf->code)
break;
if (index < 0)
return -EINVAL;
mf->colorspace = ov1063x_cfmts[index].colorspace;
mf->code = ov1063x_cfmts[index].code;
mf->field = V4L2_FIELD_NONE;
mutex_lock(&priv->lock);
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
*mf = fmt->format;
} else {
priv->format = fmt->format;
ret = ov1063x_set_params(client, mf->width, mf->height);
}
mutex_unlock(&priv->lock);
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return ret;
}
static int ov1063x_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
//printk("Enter ov1063x_enum_mbus_code\n");
if (code->index >= ARRAY_SIZE(ov1063x_cfmts))
return -EINVAL;
code->code = ov1063x_cfmts[code->index].code;
//printk("Exit ov1063x_enum_mbus_code\n");
return 0;
}
static int ov1063x_enum_frame_sizes(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
int i = ARRAY_SIZE(ov1063x_cfmts);
//printk("Enter ov1063x_enum_frame_sizes\n");
if (fse->index >= ARRAY_SIZE(ov1063x_framesizes))
return -EINVAL;
while (--i)
if (ov1063x_cfmts[i].code == fse->code)
break;
fse->code = ov1063x_cfmts[i].code;
fse->min_width = ov1063x_framesizes[fse->index].width;
fse->max_width = fse->min_width;
fse->max_height = ov1063x_framesizes[fse->index].height;
fse->min_height = fse->max_height;
//printk("Exit ov1063x_enum_frame_sizes\n");
return 0;
}
static int ov1063x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov1063x_priv *priv = to_ov1063x(client);
struct v4l2_captureparm *cp = &parms->parm.capture;
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
memset(cp, 0, sizeof(struct v4l2_captureparm));
cp->capability = V4L2_CAP_TIMEPERFRAME;
cp->timeperframe.denominator = priv->fps_numerator;
cp->timeperframe.numerator = priv->fps_denominator;
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return 0;
}
static int ov1063x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov1063x_priv *priv = to_ov1063x(client);
struct v4l2_captureparm *cp = &parms->parm.capture;
int ret;
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (cp->extendedmode != 0)
return -EINVAL;
/* FIXME Check we can handle the requested framerate */
priv->fps_denominator = cp->timeperframe.numerator;
priv->fps_numerator = cp->timeperframe.denominator;
ret = ov1063x_set_params(client, priv->width, priv->height);
if (ret < 0)
return ret;
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return 0;
}
static int ov1063x_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
{
struct v4l2_crop a_writable = *a;
struct v4l2_rect *rect = &a_writable.c;
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov1063x_priv *priv = to_ov1063x(client);
int ret = -EINVAL;
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
ret = ov1063x_set_params(client, rect->width, rect->height);
if (!ret)
return -EINVAL;
rect->width = priv->width;
rect->height = priv->height;
rect->left = 0;
rect->top = 0;
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return ret;
}
static int ov1063x_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov1063x_priv *priv = to_ov1063x(client);
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
if (priv) {
a->c.width = priv->width;
a->c.height = priv->height;
} else {
a->c.width = OV1063X_MAX_WIDTH;
a->c.height = OV1063X_MAX_HEIGHT;
}
a->c.left = 0;
a->c.top = 0;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return 0;
}
static int ov1063x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
//printk("Enter ov1063x_cropcap\n");
a->bounds.left = 0;
a->bounds.top = 0;
a->bounds.width = OV1063X_MAX_WIDTH;
a->bounds.height = OV1063X_MAX_HEIGHT;
a->defrect = a->bounds;
a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
a->pixelaspect.numerator = 1;
a->pixelaspect.denominator = 1;
//printk("Exit ov1063x_cropcap\n");
return 0;
}
static int ov1063x_init_gpios(struct i2c_client *client)
{
struct ov1063x_priv *priv = to_ov1063x(client);
int ret = 0;
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
ret = gpio_request_array(priv->mux_gpios, priv->num_gpios);
//printk("gpio_request_array_ret\n");
//printk("%d\n",ret);
if (ret)
goto done;
gpio_free_array(priv->mux_gpios, priv->num_gpios);
done:
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return ret;
}
//changes
static int ov1063x_video_probe(struct i2c_client *client)
{
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
struct ov1063x_priv *priv = to_ov1063x(client);
u8 pid, ver;
u16 id;
int ret=0, value;
__ov1063x_set_power(client, 1);
ret = ov1063x_set_regs(client, ov1063x_regs_default,
ARRAY_SIZE(ov1063x_regs_default));
if (ret)
return ret;
usleep_range(500, 510);
/* check and show product ID and manufacturer ID */
ret = ov1063x_reg_read(client, OV1063X_PID, &pid);
if (ret)
return ret;
id = pid << 8;
ret = ov1063x_reg_read(client, OV1063X_VER, &ver);
if (ret)
return ret;
id |= ver;
if (OV1063X_VERSION(pid, ver) == OV10635_VERSION_REG) {
priv->model = SENSOR_OV10635;
priv->revision = 1;
} else if (OV1063X_VERSION(pid, ver) == OV10633_VERSION_REG) {
priv->model = SENSOR_OV10633;
priv->revision = 1;
} else {
dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver);
return -ENODEV;
//changes
goto done;
//changes
}
dev_info(&client->dev, "ov1063x Product ID %x Manufacturer ID %x\n",
pid, ver);
/* Program all the 'standard' registers */
//changes
done:
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
value = v4l2_ctrl_handler_setup(&priv->hdl);
printk("Value-\n");printk("%d\n",value);
//__ov1063x_set_power(client, 0); //pwerdown pin high
return value;
}
/*
* V4L2 subdev internal operations
*/
static int ov1063x_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf;
dev_dbg(&client->dev, "%s:\n", __func__);
mf = v4l2_subdev_get_try_format(sd, fh->pad, 0);
ov1063x_get_default_format(mf);
return 0;
}
static const struct v4l2_ctrl_ops ov1063x_ctrl_ops = {
.s_ctrl = ov1063x_s_ctrl,
};
static struct v4l2_subdev_video_ops ov1063x_subdev_video_ops = {
.s_stream = ov1063x_s_stream,
.cropcap = ov1063x_cropcap,
.g_crop = ov1063x_g_crop,
.s_crop = ov1063x_s_crop,
.g_parm = ov1063x_g_parm,
.s_parm = ov1063x_s_parm,
};
static const struct v4l2_subdev_internal_ops ov1063x_sd_internal_ops = {
.open = ov1063x_open,
};
static const struct v4l2_subdev_core_ops ov1063x_subdev_core_ops = {
.log_status = v4l2_ctrl_subdev_log_status,
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
};
static const struct v4l2_subdev_pad_ops ov1063x_subdev_pad_ops = {
.enum_mbus_code = ov1063x_enum_mbus_code,
.enum_frame_size = ov1063x_enum_frame_sizes,
.get_fmt = ov1063x_get_fmt,
.set_fmt = ov1063x_set_fmt,
};
static struct v4l2_subdev_ops ov1063x_subdev_ops = {
.core = &ov1063x_subdev_core_ops,
.video = &ov1063x_subdev_video_ops,
.pad = &ov1063x_subdev_pad_ops,
};
/*
* i2c_driver function
*/
static int ov1063x_of_probe(struct i2c_client *client,
struct device_node *node)
{
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
struct ov1063x_priv *priv = to_ov1063x(client);
struct gpio *gpios = &priv->mux_gpios[0];
unsigned int flags;
int i, gpio;
/*
* Iterate over all the gpios in the device tree
* ENOENT is returned when trying to access last + 1 gpio
*/
for (i = 0; i < MAX_NUM_GPIOS; i++) {
gpio = of_get_named_gpio_flags(node, "mux-gpios", i, &flags);
if (gpio_is_valid(gpio)) {
gpios[i].gpio = gpio;
gpios[i].flags = (flags & OF_GPIO_ACTIVE_LOW) ?
GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH;
gpios[i].label = client->name;
} else {
if (gpio == -ENOENT)
break;
return gpio;
}
}
priv->num_gpios = i;
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return 0;
}
static int ov1063x_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
struct device_node *node = client->dev.of_node;
struct ov1063x_priv *priv;
struct v4l2_subdev *sd;
int ret = 0;
int gpio_read;
//changes
struct gpio_desc *gpio;
//changes
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
////printk("devm_kzalloc\n");
if (!priv)
return -ENOMEM;
gpio = devm_gpiod_get(&client->dev, "powerdown", GPIOD_OUT_LOW);
if (IS_ERR(gpio)) {
if (PTR_ERR(gpio) != -ENOENT)
return PTR_ERR(gpio);
gpio = NULL;
}
gpio_read = gpiod_get_raw_value(priv->powerdown_gpio);
dev_err(&client->dev, "POWERDOWN_GPIO\n %d", gpio_read);
priv->powerdown_gpio = gpio;
i2c_set_clientdata(client, priv);
//printk("i2c_set_clientdata\n");
ret = ov1063x_of_probe(client, node);
if (ret){//printk("ov1063x_of_probe ret error\n");
goto err;}
/* TODO External XVCLK is board specific */
priv->xvclk = 24000000; //initially 24000000
/* Default framerate */
priv->fps_numerator = 30;
priv->fps_denominator = 1;
ov1063x_get_default_format(&priv->format);
priv->width = priv->format.width;
priv->height = priv->format.height;
sd = &priv->subdev;
v4l2_i2c_subdev_init(sd, client, &ov1063x_subdev_ops);
sd->internal_ops = &ov1063x_sd_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
V4L2_SUBDEV_FL_HAS_EVENTS;
v4l2_ctrl_handler_init(&priv->hdl, 2);
v4l2_ctrl_new_std(&priv->hdl, &ov1063x_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(&priv->hdl, &ov1063x_ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, 0);
priv->subdev.ctrl_handler = &priv->hdl;
if (priv->hdl.error) {
ret = priv->hdl.error;
goto err;
}
mutex_init(&priv->lock);
ret = ov1063x_init_gpios(client);
//printk("ov1063x_init_gpios exit\n");
if (ret) {
dev_err(&client->dev, "Failed to request gpios");
goto err;
}
ret = ov1063x_video_probe(client);
if (ret) {
v4l2_ctrl_handler_free(&priv->hdl);
goto err;
}
sd->dev = &client->dev;
ret = v4l2_async_register_subdev(sd);
if (ret){
v4l2_ctrl_handler_free(&priv->hdl);
goto err;
}
dev_info(&client->dev, "%s sensor driver registered !!\n", sd->name);
pm_runtime_enable(&client->dev);
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
err:
return ret;
}
static int ov1063x_remove(struct i2c_client *client)
{
//dev_err(&client->dev, "PS-SE Enter %s:", __func__);
struct ov1063x_priv *priv = i2c_get_clientdata(client);
v4l2_device_unregister_subdev(&priv->subdev);
v4l2_async_unregister_subdev(&priv->subdev);
v4l2_ctrl_handler_free(&priv->hdl);
pm_runtime_disable(&client->dev);
//dev_err(&client->dev, "PS-SE Exit %s:\n", __func__);
return 0;
}
static const struct i2c_device_id ov1063x_id[] = {
{ "ov10635", 0 },
{ "ov10633", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ov1063x_id);
#if defined(CONFIG_OF)
static const struct of_device_id ov1063x_dt_id[] = {
{
.compatible = "ovti,ov10635", .data = "ov10635"
},
{
.compatible = "ovti,ov10633", .data = "ov10633"
},
{
}
};
#endif
static struct i2c_driver ov1063x_i2c_driver = {
.driver = {
.name = "ov1063x",
.of_match_table = of_match_ptr(ov1063x_dt_id),
},
.probe = ov1063x_probe,
.remove = ov1063x_remove,
.id_table = ov1063x_id,
};
module_i2c_driver(ov1063x_i2c_driver);
MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV1063X");
MODULE_AUTHOR("Phil Edworthy <phil.edworthy@renesas.com>");
MODULE_LICENSE("GPL v2");
Any suggestion.