代码拉取完成,页面将自动刷新
/*
* /linux-3.0.8/drivers/media/video/gt2440_ov9650.c
*/
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/media.h>
#include <linux/module.h>
#include <linux/ratelimit.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-mediabus.h>
#include "gt2440_ov9650.h"
/* help data structure */
struct gt2440_ov9650_reg gt2440_ov9650_regs[] = {
//{ REG_COM7, 0x80 }, /* SCCB reset, output format */
{ REG_COM2, 0x10 }, /* Set soft sleep mode */
{ REG_OFCON, 0x43 }, /* Power down register */
{ REG_ACOM, 0x12 }, /* reserved */
{ REG_ADC, 0x91 }, /* reserved */
{ REG_COM5, 0x00 }, /* System clock options */
//{ REG_MVFP, 0x0c }, /* Mirror / vflip */
//{ REG_BLUE, 0x80 }, /* blue gain */
//{ REG_RED, 0x80 }, /* red gain */
//{ REG_GAIN, 0x00 }, /* Gain lower 8 bits (rest in vref) */
//{ REG_AEC, 0xf0 }, /* More bits of AEC value */
//{ REG_COM1, 0x00 }, /* Control 1 */
{ REG_COM3, 0x04 }, /* Output byte swap, signals reassign */
{ REG_COM4, 0x80 }, /* Vario Pixels */
{ REG_CLKRC, 0x83 }, /* Clock control, try to run at 24Mhz */
{ REG_COM7, 0x04 }, /* SCCB reset, output format: VGA */
{ REG_COM2, 0x01 }, /* Output drive, soft sleep mode */
{ REG_COM9, 0x2e }, /* Control 9 - gain ceiling */
{ REG_COM10, 0x00 }, /* Slave mode, HREF vs HSYNC, signals negate */
{ REG_HSTART, 0x1d + 8 }, /* Horiz start high bits */
{ REG_HSTOP, 0xbd + 8 }, /* Horiz stop high bits */
{ REG_HREF, 0xbf }, /* HREF pieces */
{ REG_VSTART, 0x00 }, /* Vert start high bits */
{ REG_VSTOP, 0x80 }, /* Vert stop high bits */
{ REG_VREF, 0x12 }, /* Pieces of GAIN, VSTART, VSTOP */
{ REG_EDGE, 0xa6 }, /* Edge enhancement factor */
{ REG_COM16, 0x02 }, /* Color matrix coeff double option */
{ REG_COM17, 0x08 }, /* Single Frame out, Banding filter */
{ REG_PSHFT, 0x00 }, /* Pixel delay after HREF */
{ 0x16, 0x06 },
{ REG_CHLF, 0xc0 }, /* reserved */
{ 0x34, 0xbf },
{ 0xa8, 0x80 },
{ 0x96, 0x04 },
{ REG_TSLB, 0x00 }, /* YUYV format */
{ 0x8e, 0x00 },
{ REG_COM12, 0x77 }, /* HREF option, UV average */
{ 0x8b, 0x06 },
{ 0x35, 0x91 },
{ 0x94, 0x88 },
{ 0x95, 0x88 },
{ REG_COM15, 0xc1 }, /* Output range, RGB 555 vs 565 option */
{ REG_GRCOM, 0x3f }, /* Analog BLC & regulator */
{ REG_COM6, 0x42 | 1 }, /* HREF & ADBLC options */
{ REG_COM8, 0xe5 }, /* AGC/AEC options */
{ REG_COM13, 0x90 }, /* Gamma selection, Color matrix enable, UV delay */
{ REG_HV, 0x80 }, /* Manual banding filter MSB */
{ 0x5c, 0x96 }, /* reserved up to 0xa5 */
{ 0x5d, 0x96 },
{ 0x5e, 0x10 },
{ 0x59, 0xeb },
{ 0x5a, 0x9c },
{ 0x5b, 0x55 },
{ 0x43, 0xf0 },
{ 0x44, 0x10 },
{ 0x45, 0x55 },
{ 0x46, 0x86 },
{ 0x47, 0x64 },
{ 0x48, 0x86 },
{ 0x5f, 0xe0 },
{ 0x60, 0x8c },
{ 0x61, 0x20 },
{ 0xa5, 0xd9 },
{ 0xa4, 0x74 }, /* reserved */
{ REG_COM23, 0x02 }, /* Color bar test, color gain */
{ REG_COM8, (1 << 5) | 0x7 }, /* AGC/AEC options */
{ REG_COM22, 0x23 }, /* Edge enhancement, denoising */
{ REG_COM14, 0x02 }, /* Edge enhancement options */
{ 0xa9, 0xb8 },
{ 0xaa, 0x92 },
{ 0xab, 0x0a },
{ REG_DBLC1, 0xdf }, /* Digital BLC */
{ REG_DBLC_B, 0x00 }, /* Digital BLC B chan offset */
{ REG_DBLC_R, 0x00 }, /* Digital BLC R chan offset */
{ REG_DBLC_GB, 0x00 }, /* Digital BLC GB chan offset */
{ REG_TSLB, 0x00 }, /* YUYV format */
{ REG_AEW, 0x70 }, /* AGC upper limit */
{ REG_AEB, 0x64 }, /* AGC lower limit */
{ REG_VPT, 0xc3 }, /* AGC/AEC fast mode op region */
// { REG_EXHCH, 0x00 }, /* Dummy pixel insert MSB */
// { REG_EXHCL, 0x00 }, /* Dummy pixel insert LSB */
{ REG_COM11, (1 << 7) | (3 << 5)}, /* Night mode, banding filter enable */
{ REG_MBD, 0x41 }, /* Manual banding filter value, when COM11[0] is high */
{ REG_LCC(5), 0x00 }, /* Lens Correction Option 5 */
{ REG_COM14, 0x00 }, /* Edge enhancement options */
{ REG_EDGE, 0xa4 } /* Edge enhancement factor */
};
static const struct gt2440_ov9650_framesize gt2440_ov9650_framesizes[] = {
{ 640, 480, 0x40 }, /* VGA */
{ 352, 288, 0x20 }, /* CIF */
{ 320, 240, 0x10 }, /* QVGA */
{ 176, 144, 0x08 }, /* QCIF */
};
static const struct gt2440_ov9650_pixelcode gt2440_ov9650_pixelcodes[] = {
{ V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG },
};
/* data structure */
static const struct v4l2_subdev_core_ops gt2440_ov9650_core_ops = {
.s_power = gt2440_ov9650_s_power,
.log_status = gt2440_ov9650_log_status,
};
static const struct v4l2_subdev_video_ops gt2440_ov9650_video_ops = {
.s_stream = gt2440_ov9650_s_stream,
};
static const struct v4l2_subdev_pad_ops gt2440_ov9650_pad_ops = {
.enum_mbus_code = gt2440_ov9650_enum_mbus_code,
.enum_frame_size = gt2440_ov9650_enum_frame_size,
.get_fmt = gt2440_ov9650_get_fmt,
.set_fmt = gt2440_ov9650_set_fmt,
};
static const struct v4l2_subdev_ops gt2440_ov9650_subdev_ops = {
.core = >2440_ov9650_core_ops,
.video = >2440_ov9650_video_ops,
.pad = >2440_ov9650_pad_ops,
};
static const struct v4l2_subdev_internal_ops gt2440_ov9650_internal_ops = {
.registered = gt2440_ov9650_registered,
.open = gt2440_ov9650_open,
};
static const struct v4l2_ctrl_ops gt2440_ov9650_ctrl_ops = {
.s_ctrl = gt2440_ov9650_s_ctrl,
};
/***** help function *****/
/***** help function *****/
static void __gt2440_ov9650_set_power(struct gt2440_ov9650_info *info, int on)
{
struct gt2440_ov9650_platdata *pdata = info->pdata;
pdata->set_power(pdata, on);
info->streaming = 0;
}
static int gt2440_ov9650_write_array(struct i2c_client *client,
const struct gt2440_ov9650_reg *regs,
unsigned int len)
{
int i;
for (i = 0; i < len; i++) {
int ret = gt2440_ov9650_write_reg(client, regs[i].addr, regs[i].value);
if (ret < 0)
return ret;
}
return 0;
}
static int gt2440_ov9650_set_gamma_curve(struct gt2440_ov9650_info *info)
{
struct i2c_client *client = v4l2_get_subdevdata(&info->v4l2_sub);
static const u8 gamma_curve[] = {
/* Values taken from OV application note. */
0x40, 0x30, 0x4b, 0x60, 0x70, 0x70, 0x70, 0x70,
0x60, 0x60, 0x50, 0x48, 0x3a, 0x2e, 0x28, 0x22,
0x04, 0x07, 0x10, 0x28, 0x36, 0x44, 0x52, 0x60,
0x6c, 0x78, 0x8c, 0x9e, 0xbb, 0xd2, 0xe6
};
u8 addr = REG_GSP;
unsigned int i;
int ret = 0;
for (i = 0; i < ARRAY_SIZE(gamma_curve) && !ret; i++) {
ret = gt2440_ov9650_write_reg(client, addr, gamma_curve[i]);
addr++;
}
return ret;
};
static int gt2440_ov9650_set_color_matrix(struct gt2440_ov9650_info *info)
{
struct i2c_client *client = v4l2_get_subdevdata(&info->v4l2_sub);
static const u8 mtx[] = {
/* MTX1..MTX9, MTXS */
0x3a, 0x3d, 0x03, 0x12, 0x26, 0x38, 0x40, 0x40, 0x40, 0x0d
};
u8 addr = REG_MTX(1);
unsigned int i;
for (i = 0; i < ARRAY_SIZE(mtx); i++) {
int ret = gt2440_ov9650_write_reg(client, addr, mtx[i]);
if (ret < 0)
return ret;;
addr++;
}
return 0;
}
static int __gt2440_ov9650_set_params(struct gt2440_ov9650_info *info)
{
struct i2c_client *client = v4l2_get_subdevdata(&info->v4l2_sub);
int ret;
ret = gt2440_ov9650_write_array(client, gt2440_ov9650_regs,
ARRAY_SIZE(gt2440_ov9650_regs));
if (ret < 0)
return ret;
ret = gt2440_ov9650_set_gamma_curve(info);
if (ret < 0)
return ret;
return gt2440_ov9650_set_color_matrix(info);
}
static int gt2440_ov9650_set_framesize(struct v4l2_mbus_framefmt *framefmt,
const struct gt2440_ov9650_framesize **framesizes)
{
unsigned int min_err = ~0;
int i = ARRAY_SIZE(gt2440_ov9650_framesizes);
const struct gt2440_ov9650_framesize *framesize = >2440_ov9650_framesizes[0],
*match = NULL;
while (i--) {
int err = abs(framesize->width - framefmt->width)
+ abs(framesize->height - framefmt->height);
if (err < min_err) {
min_err = err;
match = framesize;
}
framesize++;
}
if (match) {
framefmt->width = match->width;
framefmt->height = match->height;
if (framesizes)
*framesizes = match;
return 0;
}
return -EINVAL;
}
static void gt2440_ov9650_set_format(struct gt2440_ov9650_info *info,
struct v4l2_mbus_framefmt *framefmt)
{
const struct gt2440_ov9650_framesize *framesize = NULL;
unsigned int index;
gt2440_ov9650_set_framesize(framefmt, &framesize);
if (framesize)
info->com7_reg = framesize->com7_reg;
index = 0; /* ov965x_get_pixfmt_index(ov965x, mf); */
framefmt->colorspace = V4L2_COLORSPACE_JPEG;
framefmt->code = gt2440_ov9650_pixelcodes[index].pixelcode;
framefmt->field = V4L2_FIELD_NONE;
}
/***** v4l2_subdev_ops function *****/
static int gt2440_ov9650_s_power(struct v4l2_subdev *v4l2_sub, int on)
{ dprintk("gt2440_ov9650_s_power()\n");
struct gt2440_ov9650_info *info =
container_of(v4l2_sub, struct gt2440_ov9650_info, v4l2_sub);;
mutex_lock(&info->lock);
if (info->power == !on) {
__gt2440_ov9650_set_power(info, on);
info->apply_fmt = 1;
}
info->power += on ? 1 : -1;
WARN_ON(info->power < 0);
mutex_unlock(&info->lock);
return 0;
}
static int gt2440_ov9650_log_status(struct v4l2_subdev *v4l2_sub)
{ dprintk("gt2440_ov9650_log_status()\n");
v4l2_ctrl_handler_log_status(v4l2_sub->ctrl_handler, v4l2_sub->name);
return 0;
}
static int gt2440_ov9650_s_stream(struct v4l2_subdev *v4l2_sub, int on)
{ dprintk("gt2440_ov9650_s_stream()\n");
struct i2c_client *client = v4l2_get_subdevdata(v4l2_sub);
struct gt2440_ov9650_info *info =
container_of(v4l2_sub, struct gt2440_ov9650_info, v4l2_sub);;
int ret = 0;
mutex_lock(&info->lock);
if (info->streaming == !on) {
if (on) {
ret = __gt2440_ov9650_set_params(info);
if (!ret && info->apply_fmt) {
ret = gt2440_ov9650_write_reg(client, REG_COM7,
info->com7_reg);
gt2440_ov9650_write_reg(client, REG_COM2, 0x01);
/* ov965x->apply_fmt = 0; */
}
} else {
ret = gt2440_ov9650_write_reg(client, REG_COM2, 0x11);
}
}
if (!ret)
info->streaming += on ? 1 : -1;
WARN_ON(info->streaming < 0);
mutex_unlock(&info->lock);
return ret;
}
static int gt2440_ov9650_enum_mbus_code(struct v4l2_subdev *v4l2_sub,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code_enum)
{ dprintk("gt2440_ov9650_enum_mbus_code()\n");
if (code_enum->index >= ARRAY_SIZE(gt2440_ov9650_pixelcodes))
return -EINVAL;
code_enum->code = gt2440_ov9650_pixelcodes[code_enum->index].pixelcode;
return 0;
}
static int gt2440_ov9650_enum_frame_size(struct v4l2_subdev *v4l2_sub,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *frame_size_enum)
{ dprintk("gt2440_ov9650_enum_frame_size()\n");
int i = ARRAY_SIZE(gt2440_ov9650_pixelcodes);
if (frame_size_enum->index > ARRAY_SIZE(gt2440_ov9650_pixelcodes))
return -EINVAL;
while (--i)
if (frame_size_enum->code == gt2440_ov9650_pixelcodes[i].pixelcode) break;
frame_size_enum->code = gt2440_ov9650_pixelcodes[i].pixelcode;
frame_size_enum->min_width = gt2440_ov9650_framesizes[frame_size_enum->index].width;
frame_size_enum->max_width = frame_size_enum->min_width;
frame_size_enum->max_height = gt2440_ov9650_framesizes[frame_size_enum->index].height;
frame_size_enum->min_height = frame_size_enum->max_height;
return 0;
}
static int gt2440_ov9650_get_fmt(struct v4l2_subdev *v4l2_sub, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *format)
{ dprintk("gt2440_ov9650_get_fmt()\n");
struct gt2440_ov9650_info *info =
container_of(v4l2_sub, struct gt2440_ov9650_info, v4l2_sub);;
struct v4l2_mbus_framefmt *framefmt;
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
framefmt = v4l2_subdev_get_try_format(fh, 0);
format->format = *framefmt;
return 0;
}
mutex_lock(&info->lock);
format->format = info->framefmt;
mutex_unlock(&info->lock);
return 0;
}
static int gt2440_ov9650_set_fmt(struct v4l2_subdev *v4l2_sub, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *format )
{ dprintk("gt2440_ov9650_set_fmt()\n");
struct gt2440_ov9650_info *info =
container_of(v4l2_sub, struct gt2440_ov9650_info, v4l2_sub);;
struct v4l2_mbus_framefmt *framefmt = NULL;
int ret = 0;
gt2440_ov9650_set_format(info, &format ->format);
mutex_lock(&info->lock);
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
if (fh != NULL) {
framefmt = v4l2_subdev_get_try_format(fh, format->pad);
*framefmt = format->format;
}
} else {
if (info->streaming) {
ret = -EBUSY;
} else {
info->framefmt = format->format;
info->apply_fmt = 1;
}
}
mutex_unlock(&info->lock);
return ret;
}
/***** v4l2_subdev_internal_ops function *****/
static int gt2440_ov9650_registered(struct v4l2_subdev *v4l2_sub)
{ dprintk("gt2440_ov9650_registered()\n");
struct i2c_client *client = v4l2_get_subdevdata(v4l2_sub);
struct gt2440_ov9650_info *info =
container_of(v4l2_sub, struct gt2440_ov9650_info, v4l2_sub);
u8 pid, ver;
int ret;
mutex_lock(&info->lock);
__gt2440_ov9650_set_power(info, 1);
usleep_range(25000, 26000);
ret = gt2440_ov9650_read_reg(client, REG_PID, &pid);
if (!ret)
ret = gt2440_ov9650_read_reg(client, REG_VER, &ver);
if (!ret) {
if (pid == OV965X_PID_MSB &&
(ver == OV9650_PID_LSB || ver == OV9652_PID_LSB))
v4l2_info(v4l2_sub, "success to find OV96%02X Sensor\n", ver);
else
v4l2_err(v4l2_sub, "failed to find OV9650 Sensor: 0x%02x, 0x%02x\n", pid, ver);
}
__gt2440_ov9650_set_power(info, 0);
mutex_unlock(&info->lock);
return ret;
}
static int gt2440_ov9650_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *subdev_fh)
{ dprintk("gt2440_ov9650_open()\n");
struct v4l2_mbus_framefmt *framefmt = v4l2_subdev_get_try_format(subdev_fh, 0);
framefmt->width = gt2440_ov9650_framesizes[0].width;
framefmt->height = gt2440_ov9650_framesizes[0].height;
framefmt->colorspace = gt2440_ov9650_pixelcodes[0].colorspace;
framefmt->code = gt2440_ov9650_pixelcodes[0].pixelcode;
framefmt->field = V4L2_FIELD_NONE;
v4l2_info(subdev, "%s:%d\n", __func__, __LINE__);
return 0;
}
static int gt2440_ov9650_s_ctrl(struct v4l2_ctrl *ctrl)
{ dprintk("gt2440_ov9650_s_ctrl()\n");
struct v4l2_subdev *v4l2_sub =
&container_of(ctrl->handler, struct gt2440_ov9650_info, ctrl.handler)->v4l2_sub;
struct gt2440_ov9650_info *info =
container_of(v4l2_sub, struct gt2440_ov9650_info, v4l2_sub);
struct i2c_client *client = v4l2_get_subdevdata(v4l2_sub);
int ret = 0;
mutex_lock(&info->lock);
if (info->power == 0)
goto unlock;
switch (ctrl->id) {
case V4L2_CID_AUTO_WHITE_BALANCE:
break;
case V4L2_CID_BRIGHTNESS:
/* ret = gt2440_ov9650_write_reg(client, REG_USER_BRIGHTNESS, ctrl->val); */
break;
case V4L2_CID_CONTRAST:
/* ret = gt2440_ov9650_write_reg(client, REG_USER_CONTRAST, ctrl->val); */
break;
case V4L2_CID_HFLIP:
break;
case V4L2_CID_EXPOSURE_AUTO:
break;
case V4L2_CID_POWER_LINE_FREQUENCY:
ret = -EINVAL; //s5k6aa_set_anti_banding(ov965x, ctrl->val);
break;
case V4L2_CID_SATURATION:
/* ret = gt2440_ov9650_write_reg(client, REG_USER_SATURATION, ctrl->val); */
break;
}
unlock:
mutex_unlock(&info->lock);
return ret;
}
/***** probe and remove function *****/
static int gt2440_ov9650_probe(struct i2c_client *client, const struct i2c_device_id *id)
{ dprintk("gt2440_ov9650_probe():\n");
dprintk("i2c_client: name = %s, addr = 0x%2x\n", client->name, client->addr);
dprintk("i2c_device_id: name = %s \n", id->name);
struct gt2440_ov9650_platdata *pdata;
struct gt2440_ov9650_info *info;
int ret;
int i;
pdata = (struct gt2440_ov9650_platdata *)client->dev.platform_data;
if (pdata == NULL) {
dev_err(&client->dev,"failed to get platform data for ov9650\n");
return -EINVAL;
}
info = kzalloc(sizeof(struct gt2440_ov9650_info), GFP_KERNEL);
if (info == NULL){
dev_err(&client->dev,"failed to allocate struct gt2440_ov9650_info{}\n");
return -ENOMEM;
}
info->dev = &client->dev;
info->pdata = pdata;
info->mclk_frequency = pdata->mclk_frequency;
info->com7_reg = gt2440_ov9650_framesizes[0].com7_reg;
mutex_init(&info->lock);
if(pdata->init_gpio){
ret = pdata->init_gpio(pdata);
if(ret){
dev_err(info->dev,"failed to init gpio\n");
goto exit_1;
}
}else{
dev_err(info->dev,"failed to get gpio\n");
goto exit_1;
}
info->framefmt.width = gt2440_ov9650_framesizes[0].width;
info->framefmt.height = gt2440_ov9650_framesizes[0].height;
info->framefmt.colorspace = gt2440_ov9650_pixelcodes[0].colorspace;
info->framefmt.code = gt2440_ov9650_pixelcodes[0].pixelcode;
info->framefmt.field = V4L2_FIELD_NONE;
info->v4l2_sub.internal_ops = >2440_ov9650_internal_ops;
info->v4l2_sub.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
v4l2_i2c_subdev_init(&info->v4l2_sub, client, >2440_ov9650_subdev_ops);
ret = v4l2_ctrl_handler_init(&info->ctrl.handler, 16);
if (ret){
dev_err(info->dev,"failed to init v4l2_ctrl_handler\n");
goto exit_2;
}
info->ctrl.auto_wb = v4l2_ctrl_new_std(&info->ctrl.handler, >2440_ov9650_ctrl_ops,
V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
info->ctrl.auto_exp = v4l2_ctrl_new_std_menu(&info->ctrl.handler, >2440_ov9650_ctrl_ops,
V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL,
0, V4L2_EXPOSURE_AUTO);
info->ctrl.exposure = v4l2_ctrl_new_std(&info->ctrl.handler, >2440_ov9650_ctrl_ops,
V4L2_CID_EXPOSURE, 0, 6000000U, 1, 100000U);
info->ctrl.hflip = v4l2_ctrl_new_std(&info->ctrl.handler, >2440_ov9650_ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, 0);
info->ctrl.vflip = v4l2_ctrl_new_std(&info->ctrl.handler, >2440_ov9650_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
v4l2_ctrl_cluster(2, &info->ctrl.hflip);
v4l2_ctrl_new_std(&info->ctrl.handler, >2440_ov9650_ctrl_ops,
V4L2_CID_SATURATION, -127, 127, 1, 0);
v4l2_ctrl_new_std(&info->ctrl.handler, >2440_ov9650_ctrl_ops,
V4L2_CID_BRIGHTNESS, -127, 127, 1, 0);
v4l2_ctrl_new_std(&info->ctrl.handler, >2440_ov9650_ctrl_ops,
V4L2_CID_CONTRAST, -127, 127, 1, 0);
if (info->ctrl.handler.error) {
dev_err(info->dev,"failed to set v4l2_ctrl_handler: error = %d\n",
info->ctrl.handler.error);
ret = info->ctrl.handler.error;
goto exit_3;
}
info->v4l2_sub.ctrl_handler = &info->ctrl.handler;
return 0;
exit_3:
v4l2_ctrl_handler_free( &info->ctrl.handler);
info->v4l2_sub.ctrl_handler = NULL;
exit_2:
if(pdata->exit_gpio)
ret = pdata->exit_gpio(pdata);
exit_1:
kfree(info);
i2c_set_clientdata(client, NULL);
return ret;
}
static int gt2440_ov9650_remove(struct i2c_client *client)
{ dprintk("gt2440_ov9650_remove():\n");
dprintk("i2c_client: name = %s, i2c_client: addr = 0x%2x\n",
client->name, client->addr);
struct v4l2_subdev *v4l2_sub = i2c_get_clientdata(client);
struct gt2440_ov9650_info *info;
struct gt2440_ov9650_platdata*pdata;
int i, ret;
if(v4l2_sub == NULL)
return 0;
info = container_of(v4l2_sub, struct gt2440_ov9650_info, v4l2_sub);
pdata = info->pdata;
v4l2_ctrl_handler_free( &info->ctrl.handler);
info->v4l2_sub.ctrl_handler = NULL;
v4l2_set_subdevdata(v4l2_sub, NULL);
if(pdata->exit_gpio)
ret = pdata->exit_gpio(pdata);
kfree(info);
i2c_set_clientdata(client, NULL);
return 0;
}
/***** gt2440 board probe and remove function *****/
static const struct i2c_device_id gt2440_ov9650_id_table[] = {
{ "OV9650", 0 },
{ "OV9652", 0 },
};
static struct i2c_driver gt2440_ov9650_driver = {
.driver = {
.name = "gt2440-ov9650",
.owner = THIS_MODULE,
},
.probe = gt2440_ov9650_probe,
.remove = gt2440_ov9650_remove,
.id_table = gt2440_ov9650_id_table,
};
static int __init gt2440_ov9650_init(void)
{ dprintk("gt2440_ov9650_init()");
return i2c_add_driver(>2440_ov9650_driver);
}
static void __exit gt2440_ov9650_exit(void)
{ dprintk("gt2440_ov9650_exit()");
i2c_del_driver(>2440_ov9650_driver);
}
module_init(gt2440_ov9650_init);
module_exit(gt2440_ov9650_exit);
MODULE_DESCRIPTION("GT2440 OV9650 Camera Sensor Device Driver");
MODULE_AUTHOR("Liguang13579<1659890447@qq.com>");
MODULE_LICENSE("GPL v2");
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。