📄 camera_sensor_if.c
字号:
/*
* linux/drivers/media/video/omap/camera_sensor_if.c
*
* Copyright (C) 2004 Texas Instruments 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; 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.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/videodev.h>
#include "camera_sensor_if.h"
#include "camera_core.h"
#include "ov9640.h"
extern int ov9640_configure(enum image_size, enum pixel_format, unsigned char);
extern int ov9640_read_mask(u8, u8);
extern int ov9640_write_mask(u8, u8, u8);
extern int ov9640_init (void);
#define DEF_GAIN 31
#define DEF_AUTOGAIN 1
#define DEF_EXPOSURE 154
#define DEF_AEC 1
#define DEF_FREEZE_AGCAEC 0
#define DEF_BLUE 153
#define DEF_RED (255 - DEF_BLUE)
#define DEF_AWB 1
#define DEF_HFLIP 0
#define DEF_VFLIP 0
/* Our own specific controls */
#define V4L2_CID_FREEZE_AGCAEC V4L2_CID_PRIVATE_BASE+0
#define V4L2_CID_AUTOEXPOSURE V4L2_CID_PRIVATE_BASE+1
#define V4L2_CID_LAST_PRIV V4L2_CID_AUTOEXPOSURE
/* Video controls */
static struct vcontrol {
struct v4l2_queryctrl qc;
int current_value;
u8 reg;
u8 mask;
u8 start_bit;
} control[] = {
{ { V4L2_CID_GAIN, V4L2_CTRL_TYPE_INTEGER, "Gain", 0, 63, 1,
DEF_GAIN },
0, OV9640_GAIN, 0x3f, 0 },
{ { V4L2_CID_AUTOGAIN, V4L2_CTRL_TYPE_BOOLEAN, "Auto Gain", 0, 1, 0,
DEF_AUTOGAIN },
0, OV9640_COM8, 0x04, 2 },
{ { V4L2_CID_EXPOSURE, V4L2_CTRL_TYPE_INTEGER, "Exposure", 0, 255, 1,
DEF_EXPOSURE },
0, OV9640_AECH, 0xff, 0 },
{ { V4L2_CID_AUTOEXPOSURE, V4L2_CTRL_TYPE_BOOLEAN, "Auto Exposure", 0, 1, 0,
DEF_AEC },
0, OV9640_COM8, 0x01, 0 },
{ { V4L2_CID_FREEZE_AGCAEC, V4L2_CTRL_TYPE_BOOLEAN, "Freeze AGC/AEC", 0,1,0,
DEF_FREEZE_AGCAEC },
0, OV9640_COM9, 0x01, 0 },
{ { V4L2_CID_RED_BALANCE, V4L2_CTRL_TYPE_INTEGER, "Red Balance", 0, 255, 1,
DEF_RED },
0, OV9640_RED, 0xff, 0 },
{ { V4L2_CID_BLUE_BALANCE, V4L2_CTRL_TYPE_INTEGER, "Blue Balance", 0, 255, 1,
DEF_BLUE },
0, OV9640_BLUE, 0xff, 0 },
{ { V4L2_CID_AUTO_WHITE_BALANCE, V4L2_CTRL_TYPE_BOOLEAN, "Auto White Balance", 0,1,0,
DEF_AWB },
0, OV9640_COM8, 0x02, 1 },
{ { V4L2_CID_HFLIP, V4L2_CTRL_TYPE_BOOLEAN, "Mirror Image", 0, 1, 0,
DEF_HFLIP },
0, OV9640_MVFP, 0x20, 5 },
{ { V4L2_CID_VFLIP, V4L2_CTRL_TYPE_BOOLEAN, "Vertical Flip", 0, 1, 0,
DEF_VFLIP },
0, OV9640_MVFP, 0x10, 4 },
};
#define NUM_CONTROLS (sizeof(control)/sizeof(control[0]))
/* Convert a Video for Linux fourcc pixelformat to an enum pixel_format value.
*/
static enum pixel_format
image_find_format(unsigned long pixelformat)
{ switch (pixelformat) {
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_UYVY:
default:
return YUV;
case V4L2_PIX_FMT_RGB565:
case V4L2_PIX_FMT_RGB565X:
return RGB565;
case V4L2_PIX_FMT_RGB555:
case V4L2_PIX_FMT_RGB555X:
return RGB555;
}
}
/* Returns the index of the requested ID from the control structure array */
static int
find_vctrl(int id)
{
int i;
if (id < V4L2_CID_BASE)
return -EDOM;
for (i = NUM_CONTROLS - 1; i >= 0; i--)
if (control[i].qc.id == id)
break;
if (i < 0)
i = -EINVAL;
return i;
}
/* Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This
* ioctl is used to negotiate the image capture size and pixel format
* without actually making it take effect.
*/
static int/*void*/
sensor_try_fmt(struct v4l2_pix_format *pix)
{
enum image_size isize;
int ifmt;
isize = image_find_size(pix->width, pix->height);
pix->width = image_sizes[isize].width;
pix->height = image_sizes[isize].height;
for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) {
if (pix->pixelformat == image_formats[ifmt].pixelformat)
break;
}
if (ifmt == NUM_CAPTURE_FORMATS)
ifmt = 0;
pix->pixelformat = image_formats[ifmt].pixelformat;
pix->field = V4L2_FIELD_NONE;
pix->bytesperline = pix->width*2;
pix->sizeimage = pix->bytesperline*pix->height;
pix->priv = 0;
switch (pix->pixelformat) {
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_UYVY:
default:
pix->colorspace = V4L2_COLORSPACE_JPEG;
break;
case V4L2_PIX_FMT_RGB565:
case V4L2_PIX_FMT_RGB565X:
case V4L2_PIX_FMT_RGB555:
case V4L2_PIX_FMT_RGB555X:
pix->colorspace = V4L2_COLORSPACE_SRGB;
break;
}
return 0;
}
/* Calculate the internal clock divisor (value of the CLKRC register) of the
* OV9640 given the image size, the frequency (in Hz) of its XCLK input and a
* desired frame period (in seconds). The frame period 'fper' is expressed as
* a fraction. The frame period is an input/output parameter.
* Returns the value of the OV9640 CLKRC register that will yield the frame
* period returned in 'fper' at the specified xclk frequency. The
* returned period will be as close to the requested period as possible.
*/
unsigned char
sensor_clkrc(enum image_size isize, unsigned long xclk, struct v4l2_fract *fper)
{
unsigned long fpm, fpm_max; /* frames per minute */
unsigned long divisor;
const unsigned long divisor_max = 64;
const static unsigned long clks_per_frame[] =
{ 200000, 200000, 200000, 200000, 400000, 800000, 3200000 };
/* QQCIF QQVGA QCIF QVGA CIF VGA SXGA
*actually 199680,400000, 199680, 400000, 399360, 800000, 3200000
*/
if (fper->numerator > 0)
fpm = (fper->denominator*60)/fper->numerator;
else
fpm = 0xffffffff;
fpm_max = (xclk*60)/clks_per_frame[isize];
if (fpm_max == 0)
fpm_max = 1;
if (fpm > fpm_max)
fpm = fpm_max;
if (fpm == 0)
fpm = 1;
divisor = fpm_max/fpm;
if (divisor > divisor_max)
divisor = divisor_max;
fper->numerator = divisor*60;
fper->denominator = fpm_max;
/* try to reduce the fraction */
while (!(fper->denominator % 5) && !(fper->numerator % 5)) {
fper->numerator /= 5;
fper->denominator /= 5;
}
while (!(fper->denominator % 3) && !(fper->numerator % 3)) {
fper->numerator /= 3;
fper->denominator /= 3;
}
while (!(fper->denominator % 2) && !(fper->numerator % 2)) {
fper->numerator /= 2;
fper->denominator /= 2;
}
if (fper->numerator < fper->denominator) {
if (!(fper->denominator % fper->numerator)) {
fper->denominator /= fper->numerator;
fper->numerator = 1;
}
}
else {
if (!(fper->numerator % fper->denominator)) {
fper->numerator /= fper->denominator;
fper->denominator = 1;
}
}
return (0x80 | (divisor - 1));
}
int camera_sensor_enum_pixformat(struct v4l2_fmtdesc *fmt)
{
int index = fmt->index;
enum v4l2_buf_type type = fmt->type;
memset(fmt, 0, sizeof(*fmt));
fmt->index = index;
fmt->type = type;
switch (fmt->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (index >= NUM_CAPTURE_FORMATS)
return -EINVAL;
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
if (index >= NUM_OVERLAY_FORMATS)
return -EINVAL;
break;
default:
return -EINVAL;
}
fmt->flags = image_formats[index].flags;
strlcpy(fmt->description, image_formats[index].description, sizeof(fmt->description));
fmt->pixelformat = image_formats[index].pixelformat;
return 0;
}
int camera_sensor_set_format( struct v4l2_pix_format* fmt, unsigned long xclk, struct v4l2_fract *fper)
{
enum image_size isize;
enum pixel_format pfmt;
unsigned char clkrc;
int err = 0;
isize = image_find_size(fmt->width, fmt->height);
pfmt = image_find_format(fmt->pixelformat);
clkrc = sensor_clkrc (isize, xclk, fper);
err = ov9640_configure(isize, pfmt, clkrc);
return err;
}
int camera_sensor_query_control(struct v4l2_queryctrl *qc)
{
int i;
i = find_vctrl (qc->id);
if (i == -EINVAL) {
qc->flags = V4L2_CTRL_FLAG_DISABLED;
return 0;
}
if (i < 0)
return -EINVAL;
*qc = control[i].qc;
return 0;
}
int camera_sensor_get_control(struct v4l2_control *vc)
{
int i, val;
struct vcontrol * lvc;
i = find_vctrl(vc->id);
if (i < 0)
return -EINVAL;
lvc = &control[i];
val = ov9640_read_mask(lvc->reg, lvc->mask) >> lvc->start_bit;
if (val >= 0) {
vc->value = lvc->current_value = val;
return 0;
} else
return val;
}
int camera_sensor_set_control(struct v4l2_control *vc)
{
struct vcontrol * lvc;
int val = vc->value;
int i;
i = find_vctrl(vc->id);
if (i < 0)
return -EINVAL;
lvc = &control[i];
val = ov9640_write_mask(lvc->reg,
val << lvc->start_bit,
lvc->mask) >> lvc->start_bit;
if (val >= 0) {
lvc->current_value = val;
return 0;
} else
return val;
}
/* Initialize the OV9640 sensor.
* This routine allocates and initializes the data structure for the sensor,
* powers up the sensor, registers the I2C driver, and sets a default image
* capture format in cam->pix. The capture format is not actually programmed
* into the OV9640 sensor by this routine.
* This function must set cam->sensor to a non-NULL value to indicate that
* initialization is successful. cam->sensor must be set to NULL to indicate
* failure.
*/
static int
camera_sensor_init(struct v4l2_pix_format *pix)
{
/* Call Ov9640 init function */
ov9640_init ();
/* Make the default capture format QVGA YUYV */
pix->width = image_sizes[QCIF].width;
pix->height = image_sizes[QCIF].height;
pix->bytesperline = pix->width*2;
pix->sizeimage = pix->bytesperline*pix->height;
pix->pixelformat = V4L2_PIX_FMT_RGB565;
sensor_try_fmt(pix);
return 0;
}
/* Function pointers exported to the core module */
struct camera_sensor camera_sensor_if = {
version: 0x01,
init: camera_sensor_init,
enum_pixformat: camera_sensor_enum_pixformat,
try_format: sensor_try_fmt,
set_format: camera_sensor_set_format,
query_control: camera_sensor_query_control,
get_control: camera_sensor_get_control,
set_control: camera_sensor_set_control
};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -