Re: question for soc-camera driver
- Date: Fri, 11 Apr 2008 09:54:34 +0800
- From: "冯鑫" <fengxin215@xxxxxxxxx>
- Subject: Re: question for soc-camera driver
2008/4/10 Guennadi Liakhovetski <g.liakhovetski@xxxxxx>:
> > Thanks,I test it already. if I request 4 buffers,wrong frames will
> > appear sometimes and get the partially inverted frame sequence too.
>
> can you describe more precisely what you mean by "wrong frames?" Is it the
> same problem as what I'm seeing here: misplaced start of frame, i.e., your
> frame looks divided into four rectangles?
The wrong frame is divided two portion,top and bottom. the top is
right of the bottom.
> > I request 2 buffers,there is not wrong frames.But some frames will be
> > lost,like 1,2,3,4,7,8,9,10,14,...
>
> This is good. This means those frames had buffer overruns and have been
> dropped. Above you mean, that frames 5, 6, 11, 12, and 13 have been
> dropped, not that all frames you listed have been dropped?
yes, the frames 5, 6, 11, 12, and 13 have been dropped. The frames I
listed is right.
> I will see if I can further improve the algorithm and identify why the
> frame sequence gets inverted with 4 buffers, but I don't know when I will
> get time for this.
I have a driver got frome MontaVista Software. Now this driver can
work well on my pxa270.This driver work on linux-2.6.20.I will give
you this driver, and I hope this driver can help you.there is 4
files,mainstone.c,mt9v022.c,mainstone.h,mt9v022.h
1、-----------------This is mainstone.c-----------------
/*
* linux/drivers/media/video/mainstone.c
*
* Driver for Intel Mainstone Camera
*
* Author: Aleskey Makarov <amakarov@xxxxxxxxxxxxx>
* (ci_xxx routines : Intel Corporation)
*
* 2003 (c) Intel Corporation
* 2003-2005 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#undef CONFIG_FB_PXA
//#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/videodev.h>
#include <linux/fs.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/stringify.h>
#include <linux/delay.h>
#include <linux/rwsem.h>
#if CONFIG_FB_PXA
# include <linux/fb.h>
#endif
#include <media/video-buf.h>
#include <asm/io.h>
#include <asm/scatterlist.h>
#include <asm/semaphore.h>
#include <asm/arch/hardware.h>
#include <asm/arch/dma.h>
#include <asm/arch/mainstone.h>
#include <media/v4l2-dev.h>
#include <linux/platform_device.h>
#include <asm/arch/pxa-regs.h>
#include <linux/version.h>
#include "mt9v022.h"
#include "mainstone.h"
#define F(string) __stringify(KBUILD_BASENAME) ":" \
__stringify(__LINE__) " (%s) : " string "\n", __FUNCTION__
#define F0 F("@")
/* for registration */
#define DEVICE_NAME "mainstone_video_device"
#define DRIVER_NAME "mainstone_video"
#define VIDEOBUF_BUFFER(x) list_entry( x, struct videobuf_buffer, queue )
#define VIDEOBUF_INDEX(x) ( ((x) == mainstone_queue.read_buf) ? -1 : (x)->i )
#define CAMERA_WIDTH_DEFAULT 752
#define CAMERA_HEIGHT_DEFAULT 480
#define CAMERA_WIDTH_MAX 752
#define CAMERA_HEIGHT_MAX 480
#define CAMERA_WIDTH_MIN 88
#define CAMERA_HEIGHT_MIN 72
#define CTRL_CAMERA_WIDTH (V4L2_CID_PRIVATE_BASE + 0)
#define CTRL_CAMERA_HEIGHT (V4L2_CID_PRIVATE_BASE + 1)
/* BEGIN capture interface */
static unsigned long ci_regs_base;
static struct platform_device mainstone_device;
static void ci_set_image_format(int input_format, int output_format)
{
unsigned int value = 0, tbit = 0, rgbt_conv = 0, rgb_conv = 0;
unsigned int rgb_f = 0, ycbcr_f = 0, rgb_bpp = 0, raw_bpp = 0;
unsigned int cspace = 0;
value = CICR1;
value &= ((CI_CICR1_PPL_SMASK << CI_CICR1_PPL_SHIFT) |
(CI_CICR1_DW_SMASK << CI_CICR1_DW_SHIFT));
switch (input_format) {
case CI_RAW8:
cspace = 0;
raw_bpp = 0;
break;
case CI_RAW9:
cspace = 0;
raw_bpp = 1;
break;
case CI_RAW10:
cspace = 0;
raw_bpp = 2;
break;
case CI_YCBCR422:
case CI_YCBCR422_PLANAR:
cspace = 2;
if (output_format == CI_YCBCR422_PLANAR) {
ycbcr_f = 1;
}
break;
case CI_RGB444:
cspace = 1;
rgb_bpp = 0;
break;
case CI_RGB555:
cspace = 1;
rgb_bpp = 1;
if (output_format == CI_RGBT555_0) {
rgbt_conv = 2;
tbit = 0;
} else if (output_format == CI_RGBT555_1) {
rgbt_conv = 2;
tbit = 1;
}
break;
case CI_RGB565:
cspace = 1;
rgb_bpp = 2;
rgb_f = 1;
break;
case CI_RGB666:
cspace = 1;
rgb_bpp = 3;
if (output_format == CI_RGB666_PACKED) {
rgb_f = 1;
}
break;
case CI_RGB888:
case CI_RGB888_PACKED:
cspace = 1;
rgb_bpp = 4;
switch (output_format) {
case CI_RGB888_PACKED:
rgb_f = 1;
break;
case CI_RGBT888_0:
rgbt_conv = 1;
tbit = 0;
break;
case CI_RGBT888_1:
rgbt_conv = 1;
tbit = 1;
break;
case CI_RGB666:
rgb_conv = 1;
break;
case CI_RGB666_PACKED:
rgb_conv = 1;
rgb_f = 1;
break;
case CI_RGB565:
rgb_conv = 2;
break;
case CI_RGB555:
rgb_conv = 3;
break;
case CI_RGB444:
rgb_conv = 4;
break;
default:
break;
}
break;
default:
break;
}
value |= (tbit == 1) ? CI_CICR1_TBIT : 0;
value |= rgbt_conv << CI_CICR1_RGBT_CONV_SHIFT;
value |= rgb_conv << CI_CICR1_RGB_CONV_SHIFT;
value |= (rgb_f == 1) ? CI_CICR1_RBG_F : 0;
value |= (ycbcr_f == 1) ? CI_CICR1_YCBCR_F : 0;
value |= rgb_bpp << CI_CICR1_RGB_BPP_SHIFT;
value |= raw_bpp << CI_CICR1_RAW_BPP_SHIFT;
value |= cspace << CI_CICR1_COLOR_SP_SHIFT;
CICR1 = value;
}
static void ci_configure_mp(unsigned int ppl, unsigned int lpf,
unsigned int bfw, unsigned int blw)
{
unsigned int value;
/* write ppl field in cicr1 (pixel per line) */
value = CICR1;
value &= ~(CI_CICR1_PPL_SMASK << CI_CICR1_PPL_SHIFT);
value |= (ppl & CI_CICR1_PPL_SMASK) << CI_CICR1_PPL_SHIFT;
CICR1 = value;
/* write BLW, ELW in cicr2 (Beginning/End of Line)) */
value = CICR2;
value &=
~(CI_CICR2_BLW_SMASK << CI_CICR2_BLW_SHIFT | CI_CICR2_ELW_SMASK <<
CI_CICR2_ELW_SHIFT);
value |= (blw & CI_CICR2_BLW_SMASK) << CI_CICR2_BLW_SHIFT;
CICR2 = value;
/* write BFW, LPF in cicr3 (Beginning of Frame, Lines per Frame) */
value = CICR3;
value &=
~(CI_CICR3_BFW_SMASK << CI_CICR3_BFW_SHIFT | CI_CICR3_LPF_SMASK <<
CI_CICR3_LPF_SHIFT);
value |= (bfw & CI_CICR3_BFW_SMASK) << CI_CICR3_BFW_SHIFT;
value |= (lpf & CI_CICR3_LPF_SMASK) << CI_CICR3_LPF_SHIFT;
CICR3 = value;
}
static void camera_gpio_init(void)
{
pxa_gpio_mode(27 | GPIO_ALT_FN_3_IN); /* CIF_DD[0] */
pxa_gpio_mode(114 | GPIO_ALT_FN_1_IN); /* CIF_DD[1] */
pxa_gpio_mode(116 | GPIO_ALT_FN_1_IN); /* CIF_DD[2] */
pxa_gpio_mode(115 | GPIO_ALT_FN_2_IN); /* CIF_DD[3] */
pxa_gpio_mode( 90 | GPIO_OUT); /*general purpose Out */
pxa_gpio_mode( 91 | GPIO_OUT); /*general purpose Out */
pxa_gpio_mode( 52 | GPIO_ALT_FN_1_IN); /* CIF_DD[4] */
pxa_gpio_mode( 82 | GPIO_ALT_FN_3_IN); /* CIF_DD[5] */
pxa_gpio_mode(17 | GPIO_ALT_FN_2_IN); /* CIF_DD[6] */
pxa_gpio_mode(12 | GPIO_ALT_FN_2_IN); /* CIF_DD[7] */
pxa_gpio_mode(23 | GPIO_ALT_FN_1_OUT); /* CIF_MCLK */
pxa_gpio_mode(26 | GPIO_ALT_FN_2_IN); /* CIF_PCLK */
pxa_gpio_mode(25 | GPIO_ALT_FN_1_IN); /* CIF_LV */
pxa_gpio_mode(24 | GPIO_ALT_FN_1_IN); /* CIF_FV */
return;
}
static int ci_init(void)
{
ci_regs_base = (unsigned long)ioremap(CI_REGS_PHYS, CI_REG_SIZE);
if (!ci_regs_base) {
pr_debug(F("can't remap I/O registers at %x"), CI_REGS_PHYS);
return -1;
}
/* clear all CI registers */
CICR0 = 0x3FF; /* disable all interrupts */
CICR1 = 0;
CICR2 = 0;
CICR3 = 0;
CICR4 = 0;
CISR = ~0;
CIFR = 0;
CITOR = 0;
/* enable CI clock */
CKEN |= CKEN24_CAMERA;
return 0;
}
static int ci_disable_quick(void)
{
int i;
CICR0 &= ~CI_CICR0_ENB;
for (i = 0; 1; i++) {
if (CISR & CI_CISR_CQD) {
CISR |= CI_CISR_CQD;
return 0;
}
if (i >= 500) {
pr_debug(F("timeout; giving up"));
return -EIO;
}
msleep(10);
}
}
/* END capture interface */
/* protects mainstone_xxxing, pix_format */
static DECLARE_MUTEX(mainstone_video_sema);
/*
* mainstone_xxxing != 0 iff this descriptor is used to
* read, stream or overlay videodata.
* - reading is triggered when user read() or select for first time;
* - streaming mode is selected when user does REQBUFS ioctl and
* is triggered in STREAMON ioctl
* - overlay mode is triggered in OVERLAY ioctl
*/
#if CONFIG_FB_PXA
static struct file *mainstone_previewing;
#else
# define mainstone_previewing 0
#endif
static struct file *mainstone_streaming;
static struct file *mainstone_reading;
static int mainstone_streaming_streamon;
static struct v4l2_pix_format pix_format;
static int video_nr;
static struct videobuf_queue mainstone_queue;
static unsigned int mclk_khz = CI_MCLK_DEFT;
static unsigned int camera_width = CAMERA_WIDTH_DEFAULT;
static unsigned int camera_height = CAMERA_HEIGHT_DEFAULT;
static void camera_find_size(unsigned int *width, unsigned int *height)
{
pr_debug(F("size requested : %dx%d"), *width, *height);
/* dma chunk size must be a multiple of 8 */
*width -= *width & 7;
if (!*width)
*width = 8;
if (*width > CAMERA_WIDTH_MAX)
*width = CAMERA_WIDTH_MAX;
if (!*height)
*height = 8;
if (*height > CAMERA_HEIGHT_MAX)
*height = CAMERA_HEIGHT_MAX;
pr_debug(F("size granted : %dx%d"), *width, *height);
}
/*
* list of image formats
*/
/* *INDENT-OFF* */
static const struct {
/* the same as in struct v4l2_pix_format */
__u32 pixelformat;
__u8 description[32];
enum v4l2_colorspace colorspace;
/* for packed formats, how many pixels are in one packet */
unsigned int pixels;
/* how many bytes takes one pixel (for packed formats -- one packet) */
unsigned int bytes;
/* value to be programmed into camera's control register */
//unsigned int capture_format;
/* values programmed into ci control registers */
unsigned int ci_in_format;
unsigned int ci_out_format;
} camera_formats[] = {
{
/* first entry is default */
.pixelformat = V4L2_PIX_FMT_RGB565,
.description = "565 RGB",
.colorspace = V4L2_COLORSPACE_SRGB,
.pixels = 1,
.bytes = 2,
//.capture_format = ADCM2650_O_FORMAT_888RGB,
.ci_in_format = CI_RGB888_PACKED,
.ci_out_format = CI_RGB565,
}, {
.pixelformat = V4L2_PIX_FMT_RGB24,
.description = "888 RGB Packed",
.colorspace = V4L2_COLORSPACE_SRGB,
.pixels = 2,
.bytes = 6,
//.capture_format = ADCM2650_O_FORMAT_888RGB,
.ci_in_format = CI_RGB888_PACKED,
.ci_out_format = CI_RGB888_PACKED,
}, {
.pixelformat = V4L2_PIX_FMT_RGB32,
.description = "888 RGB",
.colorspace = V4L2_COLORSPACE_SRGB,
.pixels = 1,
.bytes = 4,
//.capture_format = ADCM2650_O_FORMAT_888RGB,
.ci_in_format = CI_RGB888_PACKED,
.ci_out_format = CI_RGB888,
}, {
.pixelformat = V4L2_PIX_FMT_RGB555,
.description = "555 RGB",
.colorspace = V4L2_COLORSPACE_SRGB,
.pixels = 1,
.bytes = 2,
//.capture_format = ADCM2650_O_FORMAT_888RGB,
.ci_in_format = CI_RGB888_PACKED,
.ci_out_format = CI_RGB555,
}, {
.pixelformat = V4L2_PIX_FMT_YUYV,
.description = "4:2:2 YCbCr",
.colorspace = V4L2_COLORSPACE_JPEG,
.pixels = 2,
.bytes = 4,
//.capture_format = ADCM2650_O_FORMAT_422_A_YCbYCr,
.ci_in_format = CI_YCBCR422,
.ci_out_format = CI_YCBCR422,
}, {
.pixelformat = V4L2_PIX_FMT_GREY,
.description = "GREY",
.colorspace = V4L2_COLORSPACE_SRGB,
.pixels = 1,
.bytes = 1,
//.capture_format = ADCM2650_O_FORMAT_888RGB,
.ci_in_format = CI_RAW8,
.ci_out_format = CI_RAW8,
}
};
const struct v4l2_queryctrl mt9v022_controls[] = {
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Flip Vertically",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0,
}, {
.id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Flip Horizontally",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 0,
}, {
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Analog Gain",
.minimum = 64,
.maximum = 127,
.step = 1,
.default_value = 64,
.flags = V4L2_CTRL_FLAG_SLIDER,
}, {
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Exposure",
.minimum = 1,
.maximum = 255,
.step = 1,
.default_value = 255,
.flags = V4L2_CTRL_FLAG_SLIDER,
}, {
.id = V4L2_CID_AUTOGAIN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Automatic Gain",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1,
}, {
.id = V4L2_CID_EXPOSURE_AUTO,
.type = V4L2_CTRL_TYPE_BOOLEAN,
.name = "Automatic Exposure",
.minimum = 0,
.maximum = 1,
.step = 1,
.default_value = 1,
}
};
static struct v4l2_queryctrl const *soc_camera_find_qctrl(int id)
{
int i;
for (i = 0; i < ARRAY_SIZE(mt9v022_controls); i++)
{
if (mt9v022_controls[i].id == id)
return &mt9v022_controls[i];
}
return NULL;
}
/* *INDENT-ON* */
static int camera_find_format(unsigned int pixelformat)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(camera_formats); i++) {
if (pixelformat == camera_formats[i].pixelformat)
break;
}
if (i == ARRAY_SIZE(camera_formats)) {
i = 0;
}
return i;
}
static void try_pix_format(struct v4l2_pix_format *format)
{
int i;
camera_find_size(&format->width, &format->height);
i = camera_find_format(format->pixelformat);
format->pixelformat = camera_formats[i].pixelformat;
format->field = V4L2_FIELD_NONE;
format->bytesperline =
(format->width / camera_formats[i].pixels) *
camera_formats[i].bytes;
format->sizeimage = format->bytesperline * format->height;
format->colorspace = camera_formats[i].colorspace;
format->priv = 0;
}
/* BEGIN void units */
/*
* these units (dma chains for length of one image) are used
* when there are no buffers to fill got from user
*/
/* how many void units must be allocated */
#define VOID_UNITS_NUMBER 3
/* how many bytes can be transferred by one descriptor */
#define BYTES_PER_DMA_DESCRIPTOR_TRANSFER ((unsigned int)(8 * 1024 - 32))
struct void_unit {
struct pxa_dma_desc *begin;
struct pxa_dma_desc *end;
dma_addr_t begin_phys;
dma_addr_t end_phys;
};
/* registry of all the free units */
static struct void_unit void_units[VOID_UNITS_NUMBER];
static unsigned int void_unit_descriptors_per_unit;
static unsigned int void_unit_descriptors_memory_length;
static unsigned int void_unit_next_free;
/*
* Allocate VOID_UNITS_NUMBER number of void units
* Each to thansfer 'pix_format.sizeimage' bytes
*/
static int void_unit_allocate(void)
{
unsigned int i;
unsigned int j;
struct pxa_dma_desc *chain;
dma_addr_t chain_phys;
dma_addr_t dev_null_phys;
unsigned int length_remained;
unsigned int descriptor_length;
unsigned int length = pix_format.sizeimage;
pr_debug(F0);
void_unit_descriptors_per_unit =
(length + BYTES_PER_DMA_DESCRIPTOR_TRANSFER - 1) /
BYTES_PER_DMA_DESCRIPTOR_TRANSFER;
void_unit_descriptors_memory_length =
void_unit_descriptors_per_unit * sizeof(pxa_dma_desc) *
VOID_UNITS_NUMBER + sizeof(unsigned int);
/*
* allocate VOID_UNITS_NUMBER void units each 'length'
* descriptors length plus a place where data will go
* (analog of /dev/null)
*/
void_units[0].begin =
dma_alloc_coherent(0, void_unit_descriptors_memory_length,
&void_units[0].begin_phys, GFP_KERNEL | GFP_DMA);
if (!void_units[0].begin) {
pr_debug(F("dma_alloc_coherent()"));
return -ENOMEM;
}
dev_null_phys = void_units[0].begin_phys +
void_unit_descriptors_per_unit *
sizeof(pxa_dma_desc) * VOID_UNITS_NUMBER;
for (i = 0; i < VOID_UNITS_NUMBER; i++) {
void_units[i].begin = void_units[0].begin +
void_unit_descriptors_per_unit * i;
void_units[i].end = void_units[0].begin +
void_unit_descriptors_per_unit * (i + 1) - 1;
void_units[i].begin_phys = void_units[0].begin_phys +
void_unit_descriptors_per_unit * i *
sizeof(struct pxa_dma_desc);
void_units[i].end_phys = void_units[0].begin_phys +
(void_unit_descriptors_per_unit * (i + 1) - 1) *
sizeof(struct pxa_dma_desc);
chain = void_units[i].begin;
chain_phys = void_units[i].begin_phys;
length_remained = length;
for (j = 0; j < void_unit_descriptors_per_unit; j++) {
descriptor_length =
min(length_remained,
BYTES_PER_DMA_DESCRIPTOR_TRANSFER);
chain->ddadr = chain_phys + sizeof(struct pxa_dma_desc);
chain->dsadr = CIBR0_PHY;
chain->dtadr = dev_null_phys;
chain->dcmd =
descriptor_length | DCMD_FLOWSRC | DCMD_BURST32;
chain += 1;
chain_phys += sizeof(struct pxa_dma_desc);
length_remained -= descriptor_length;
}
void_units[i].end->dcmd |= DCMD_ENDIRQEN;
}
void_unit_next_free = 0;
return 0;
}
/*
* Free all the void units allocated by void_units_allocate()
*/
static void void_unit_free(void)
{
pr_debug(F0);
if (void_units[0].begin) {
dma_free_coherent(0, void_unit_descriptors_memory_length,
void_units[0].begin,
void_units[0].begin_phys);
void_units[0].begin = 0;
}
}
/* END void units */
/* BEGIN videobuffer units */
struct videobuffer {
/* this must be the first entry */
struct videobuf_buffer buffer;
/*
* A unit of dma descriptors for videobuffer
* allocated in prepare() callback
*/
struct pxa_dma_desc *begin;
struct pxa_dma_desc *end;
dma_addr_t begin_phys;
};
static int videobuffer_unit_allocate(struct videobuffer *vb)
{
unsigned int i;
unsigned int unit_length = pix_format.sizeimage;
pr_debug(F("buffer : %d"), VIDEOBUF_INDEX(&vb->buffer));
/* already allocated */
BUG_ON(vb->begin);
/*
* dma.pages : memory from user-space pointers,
* allocated in userland and splitted to series of pages
* in videobuf_dma_init_user() (using get_user_pages())
* and then by videobuf_pages_to_sg() each page
* becomes an element of scatterlist.
*
* dma.vmalloc : allocated in kernelspace,
* in videobuf_dma_init_kernel() using vmalloc()
* and splitted to pages in videobuf_vmalloc_to_sg()
*
* so, in either case each element of dma.sglist
* has length <= PAGE_SIZE and vb->dma.sglen == nr_pages
* It depends on the current implementation of
* videobuffer
*
* dma.bus_addr : memory comes from framebuffer,
* scatterlist built in videobuf_dma_pci_map()
* and consists of exactly 1 element that describes all
* the continuous buffer.
*/
BUG_ON(!(vb->buffer.dma.pages || vb->buffer.dma.vmalloc) ||
vb->buffer.dma.bus_addr);
BUG_ON(vb->buffer.dma.sglen != vb->buffer.dma.nr_pages);
BUG_ON(mainstone_previewing);
vb->begin =
dma_alloc_coherent(0,
vb->buffer.dma.nr_pages * sizeof(pxa_dma_desc),
&vb->begin_phys, GFP_KERNEL | GFP_DMA);
if (!vb->begin) {
pr_debug(F("dma_alloc_coherent()"));
return -ENOMEM;
}
vb->end = vb->begin + vb->buffer.dma.nr_pages - 1;
for (i = 0; i < vb->buffer.dma.sglen; i++) {
struct pxa_dma_desc *dr = vb->begin + i;
struct scatterlist *sl = vb->buffer.dma.sglist + i;
unsigned int burst_size;
unsigned int chunk_length =
min((unsigned int)(PAGE_SIZE - sl->offset), unit_length);
if (!(sl->offset & 31))
burst_size = DCMD_BURST32;
else if (!(sl->offset & 15))
burst_size = DCMD_BURST16;
else if (!(sl->offset & 7))
burst_size = DCMD_BURST8;
else {
/*
* we've checked in QBUF ioctl that
* userpointer is aligned
*/
burst_size = 0; /* to prevent warning */
BUG();
}
dr->ddadr =
vb->begin_phys + (i + 1) * sizeof(struct pxa_dma_desc);
dr->dsadr = CIBR0_PHY;
dr->dtadr = sl->dma_address;
dr->dcmd =
chunk_length | DCMD_FLOWSRC | DCMD_INCTRGADDR | burst_size;
unit_length -= chunk_length;
}
vb->end->dcmd |= DCMD_ENDIRQEN;
return 0;
}
static void videobuffer_unit_free(struct videobuffer *vb)
{
pr_debug(F("buffer : %d"), VIDEOBUF_INDEX(&vb->buffer));
if (!vb->begin) {
pr_debug(F("already free"));
return;
}
dma_free_coherent(0, vb->buffer.dma.nr_pages * sizeof(pxa_dma_desc),
vb->begin, vb->begin_phys);
vb->begin = 0;
}
/* END videobuffer units */
/* BEGIN units */
static int dma_channel_number = -1;
static __u32 sequence;
static DEFINE_SPINLOCK(mainstone_irq_lock);
static LIST_HEAD(queued_buffers);
/*
* Any time we have three 'units' ready to dma.
* Each of the 'units' is a part of running dma chain,
* and each 'unit' is next in this chain to the other.
* Each of the 'units' represent either a buffer
* for one frame where pixel data goes or 'void chain'.
*
* Interrupt routine is triggered when the first unit
* completely ends. At this time the second unit
* becomes the first, third becomes the second and
* the routine makes a new third unit either
* from queued_buffers or, if queued_buffers is empty,
* from void chains.
*/
struct unit {
struct pxa_dma_desc *end;
struct videobuf_buffer *videobuffer;
};
#define UNITS_LENGTH 3
static struct unit units[UNITS_LENGTH];
static unsigned int last_unit;
/*
* All buffers that are in units array also go here.
* This list is needed because videobuffer in
* videobuf_queue_cancel() calls list_del(&q->bufs[i]->queue);
* for each buffer with STATE_QUEUED.
*/
static LIST_HEAD(units_buffers);
#define UNIT_1 units[(last_unit - 2) % UNITS_LENGTH]
#define UNIT_2 units[(last_unit - 1) % UNITS_LENGTH]
#define UNIT_3 units[(last_unit - 0) % UNITS_LENGTH]
/* waitqueue to wait dma stop */
static DECLARE_WAIT_QUEUE_HEAD(dma_stop_waitqueue);
/*
* Adds new unit to the dma chain.
* Returns phys address of first descriptor of added unit
* Call under mainstone_irq_lock held
*/
static dma_addr_t add_unit3(void)
{
dma_addr_t retval;
if (list_empty(&queued_buffers)) {
unsigned int void_unit_index =
void_unit_next_free++ % VOID_UNITS_NUMBER;
pr_debug(F("void"));
UNIT_3.end = void_units[void_unit_index].end;
UNIT_3.videobuffer = 0;
retval = void_units[void_unit_index].begin_phys;
} else {
struct videobuffer *vb =
(struct videobuffer *)VIDEOBUF_BUFFER(queued_buffers.next);
pr_debug(F("buffer : %d"), VIDEOBUF_INDEX(&vb->buffer));
BUG_ON(!vb->begin);
list_del(&vb->buffer.queue);
list_add(&vb->buffer.queue, &units_buffers);
UNIT_3.end = vb->end;
UNIT_3.videobuffer = &vb->buffer;
retval = vb->begin_phys;
}
return retval;
}
/*
* Set up units from queued_buffers and void chains and run dma.
*/
static void units_start(void)
{
unsigned int flags;
pr_debug(F0);
spin_lock_irqsave(&mainstone_irq_lock, flags);
DCSR(dma_channel_number) = 0;
DDADR(dma_channel_number) = add_unit3();
last_unit++;
UNIT_2.end->ddadr = add_unit3();
last_unit++;
UNIT_2.end->ddadr = add_unit3();
DCSR(dma_channel_number) &= ~DCSR_STOPIRQEN;
DCSR(dma_channel_number) |= DCSR_RUN;
spin_unlock_irqrestore(&mainstone_irq_lock, flags);
}
static struct pxa_dma_desc *stop_descriptor;
static u32 stop_descriptor_ddadr;
/*
* Stop dma and wait while the current unit ends.
*/
static void units_stop(void)
{
DECLARE_WAITQUEUE(wait, current);
unsigned int flags;
pr_debug(F0);
spin_lock_irqsave(&mainstone_irq_lock, flags);
DCSR(dma_channel_number) |= DCSR_STOPIRQEN;
stop_descriptor = UNIT_3.end;
stop_descriptor_ddadr = UNIT_3.end->ddadr;
UNIT_3.end->ddadr = DDADR_STOP;
spin_unlock_irqrestore(&mainstone_irq_lock, flags);
/* wait when dma controller stops */
add_wait_queue(&dma_stop_waitqueue, &wait);
set_current_state(TASK_UNINTERRUPTIBLE);
while (1) {
if (DCSR(dma_channel_number) & DCSR_STOPSTATE)
break;
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&dma_stop_waitqueue, &wait);
}
static void dma_irq(int channel, void *data, struct pt_regs *regs)
{
unsigned long flags;
unsigned int dcsr;
spin_lock_irqsave(&mainstone_irq_lock, flags);
dcsr = DCSR(dma_channel_number);
pr_debug(F("dcsr : %08x"), dcsr);
if (dcsr & DCSR_ENDINTR) {
/* reset interrupt */
DCSR(dma_channel_number) = dcsr | DCSR_ENDINTR;
if (UNIT_1.videobuffer) {
struct videobuf_buffer *vb = UNIT_1.videobuffer;
pr_debug(F("buffer : %d"), VIDEOBUF_INDEX(vb));
list_del(&vb->queue);
do_gettimeofday(&vb->ts);
vb->field_count = sequence++;
vb->state = STATE_DONE;
wake_up(&vb->done);
} else {
pr_debug(F("void"));
}
last_unit++;
if (UNIT_2.end->ddadr != DDADR_STOP) {
UNIT_2.end->ddadr = add_unit3();
} else {
UNIT_3.videobuffer = 0;
/*
* Let the new last descriptor has
* ddadr == DDADR_STOP just not to forget
* on the next irq that we are shutting down
*/
UNIT_3.end = UNIT_2.end;
}
}
if (dcsr & DCSR_STOPSTATE) {
pr_debug(F("stop (%08x)"), dcsr);
if (stop_descriptor_ddadr) {
stop_descriptor->ddadr = stop_descriptor_ddadr;
stop_descriptor_ddadr = 0;
}
DCSR(dma_channel_number) = (dcsr & ~DCSR_STOPIRQEN);
wake_up_all(&dma_stop_waitqueue);
}
spin_unlock_irqrestore(&mainstone_irq_lock, flags);
}
/* END units */
#ifdef CONFIG_FB_PXA
/* BEGIN overlay */
static struct v4l2_window window_format;
static struct v4l2_framebuffer framebuffer_format;
static struct pxa_dma_desc *overlay_descr_begin;
static dma_addr_t overlay_descr_begin_phys;
static unsigned int overlay_descr_number;
/* Round to the nearest 8-aligned signed integer */
static inline void align_8(__s32 * v)
{
__s32 shift = *v & 7;
if (shift >= 4)
shift = shift - 8;
*v -= shift;
}
/*
* Height and width must be one of the standard values
* Offset of upper left corner must be an integer multiple of 8
* No chroma-key, clips etc.
*/
static void try_window_format(struct v4l2_window *format)
{
unsigned int width;
unsigned int height;
pr_debug(F0);
if (format->w.width > 0)
width = format->w.width;
else
width = -format->w.width;
if (format->w.height > 0)
height = format->w.height;
else
height = -format->w.height;
camera_find_size(&width, &height);
align_8(&format->w.left);
format->w.width = width;
format->w.height = height;
format->field = V4L2_FIELD_NONE;
format->chromakey = 0;
format->clips = 0;
format->clipcount = 0;
format->bitmap = 0;
}
static void try_framebuffer_format(struct v4l2_framebuffer *format)
{
format->capability = 0;
format->flags = V4L2_FBUF_FLAG_PRIMARY;
/* agree with user's base and fmt -- it's superuser */
}
static inline int aligned_8(unsigned int v)
{
return !(v & 7);
}
static int overlay_allocate(void)
{
/* in pixels */
unsigned int left_shaded = 0;
unsigned int right_shaded = 0;
unsigned int top_shaded = 0;
unsigned int bottom_shaded = 0;
unsigned int view_width;
unsigned int view_height;
struct pxa_dma_desc *descr;
dma_addr_t descr_phys;
/* in bytes */
unsigned int descr_length;
dma_addr_t fb_address;
unsigned int i;
dma_addr_t dev_null_phys;
unsigned int pixels;
unsigned int bytes;
pr_debug(F0);
if (!framebuffer_format.base) {
int i;
pr_debug(F
("framebuffer is not configured, trying to initialize by default
value..."));
for (i = 0; i < FB_MAX; i++) {
if (!registered_fb[i])
continue;
if (!strcmp(registered_fb[i]->fix.id, "PXA"))
break;
}
if (i == FB_MAX) {
pr_debug(F("could not find pxa framebuffer"));
return -EINVAL;
}
/*
* This depends on the current implementation
* of pxa framebuffer, namely
*
* - the id of pxa framebuffer is "PXA"
* - it is initialized at the beginning
* - memory allocated at initialization is never freed
* - the framebuffer geometry never changes
* - pixelformat is rgb565 and never changes
*/
framebuffer_format.base =
(void *)registered_fb[i]->fix.smem_start;
framebuffer_format.fmt.width = registered_fb[i]->var.xres;
framebuffer_format.fmt.height = registered_fb[i]->var.yres;
framebuffer_format.fmt.pixelformat = V4L2_PIX_FMT_RGB565;
framebuffer_format.fmt.field = V4L2_FIELD_NONE;
framebuffer_format.fmt.bytesperline =
registered_fb[i]->var.xres * 2;
framebuffer_format.fmt.sizeimage =
registered_fb[i]->var.xres * registered_fb[i]->var.xres * 2;
framebuffer_format.fmt.colorspace = V4L2_COLORSPACE_SRGB;
}
{
unsigned int format_index =
camera_find_format(framebuffer_format.fmt.pixelformat);
if (camera_formats[format_index].pixelformat !=
framebuffer_format.fmt.pixelformat) {
pr_debug(F
("the format of framebuffer is not supported"));
return -EINVAL;
}
pixels = camera_formats[format_index].pixels;
bytes = camera_formats[format_index].bytes;
}
if (window_format.w.left < 0)
left_shaded = -window_format.w.left;
if ((window_format.w.left + window_format.w.width) >
framebuffer_format.fmt.width)
right_shaded =
window_format.w.left + window_format.w.width -
framebuffer_format.fmt.width;
if (window_format.w.top < 0)
top_shaded = -window_format.w.top;
if ((window_format.w.top + window_format.w.height) >
framebuffer_format.fmt.height)
bottom_shaded =
window_format.w.top + window_format.w.height -
framebuffer_format.fmt.height;
if (left_shaded >= window_format.w.width ||
right_shaded >= window_format.w.width ||
top_shaded >= window_format.w.height ||
bottom_shaded >= window_format.w.height) {
pr_debug(F("nothing to show"));
return -EINVAL;
}
view_width = window_format.w.width - left_shaded - right_shaded;
view_height = window_format.w.height - top_shaded - bottom_shaded;
overlay_descr_number =
top_shaded + bottom_shaded +
((left_shaded ? 1 : 0) + 1 + (right_shaded ? 1 : 0))
* view_height;
/* we must have checked this in ioctls */
BUG_ON(!aligned_8(left_shaded) || !aligned_8(right_shaded) ||
!aligned_8(view_width));
pr_debug(F
("left_shaded:%u; right_shaded:%u; top_shaded:%u; bottom_shaded:%u;"),
left_shaded, right_shaded, top_shaded, bottom_shaded);
pr_debug(F("view_width:%u; view_height:%u; pixels:%u; bytes:%u;"),
view_width, view_height, pixels, bytes);
overlay_descr_begin =
dma_alloc_coherent(0,
overlay_descr_number *
sizeof(struct pxa_dma_desc)
+ sizeof(unsigned int),
&overlay_descr_begin_phys, GFP_KERNEL | GFP_DMA);
if (!overlay_descr_begin) {
pr_debug(F("dma_alloc_coherent()"));
return -ENOMEM;
}
descr = overlay_descr_begin;
descr_phys = overlay_descr_begin_phys;
fb_address = (unsigned int)framebuffer_format.base;
if (window_format.w.top > 0) {
fb_address +=
window_format.w.top * framebuffer_format.fmt.bytesperline;
}
if (window_format.w.left > 0) {
fb_address += (window_format.w.left / pixels) * bytes;
}
dev_null_phys = descr_phys +
overlay_descr_number * sizeof(struct pxa_dma_desc);
descr_length = (window_format.w.width / pixels) * bytes;
for (i = 0; i < top_shaded; i++) {
descr->ddadr = descr_phys + sizeof(struct pxa_dma_desc);
descr->dsadr = CIBR0_PHY;
descr->dtadr = dev_null_phys;
descr->dcmd =
descr_length | DCMD_FLOWSRC | DCMD_INCTRGADDR | DCMD_BURST8;
descr += 1;
descr_phys += sizeof(struct pxa_dma_desc);
}
for (i = 0; i < view_height; i++) {
if (left_shaded) {
descr_length = (left_shaded / pixels) * bytes;
descr->ddadr = descr_phys + sizeof(struct pxa_dma_desc);
descr->dsadr = CIBR0_PHY;
descr->dtadr = dev_null_phys;
descr->dcmd =
descr_length | DCMD_FLOWSRC | DCMD_INCTRGADDR |
DCMD_BURST8;
descr += 1;
descr_phys += sizeof(struct pxa_dma_desc);
}
descr_length = (view_width / pixels) * bytes;
descr->ddadr = descr_phys + sizeof(struct pxa_dma_desc);
descr->dsadr = CIBR0_PHY;
descr->dtadr = fb_address;
descr->dcmd =
descr_length | DCMD_FLOWSRC | DCMD_INCTRGADDR | DCMD_BURST8;
descr += 1;
descr_phys += sizeof(struct pxa_dma_desc);
fb_address += framebuffer_format.fmt.bytesperline;
if (right_shaded) {
descr_length = (right_shaded / pixels) * bytes;
descr->ddadr = descr_phys + sizeof(struct pxa_dma_desc);
descr->dsadr = CIBR0_PHY;
descr->dtadr = dev_null_phys;
descr->dcmd =
descr_length | DCMD_FLOWSRC | DCMD_INCTRGADDR |
DCMD_BURST8;
descr += 1;
descr_phys += sizeof(struct pxa_dma_desc);
}
}
descr_length = (window_format.w.width / pixels) * bytes;
for (i = 0; i < bottom_shaded; i++) {
descr->ddadr = descr_phys + sizeof(struct pxa_dma_desc);
descr->dsadr = CIBR0_PHY;
descr->dtadr = dev_null_phys;
descr->dcmd =
descr_length | DCMD_FLOWSRC | DCMD_INCTRGADDR | DCMD_BURST8;
descr += 1;
descr_phys += sizeof(struct pxa_dma_desc);
}
descr -= 1;
descr->ddadr = overlay_descr_begin_phys;
return 0;
}
static void overlay_free(void)
{
pr_debug(F0);
if (!overlay_descr_begin)
return;
dma_free_coherent(0, overlay_descr_number * sizeof(struct pxa_dma_desc)
+ sizeof(unsigned int), overlay_descr_begin,
overlay_descr_begin_phys);
overlay_descr_begin = 0;
}
static void dma_start_overlay(void)
{
pr_debug(F0);
DCSR(dma_channel_number) = 0;
DDADR(dma_channel_number) = overlay_descr_begin_phys;
DCSR(dma_channel_number) &= ~DCSR_STOPIRQEN;
DCSR(dma_channel_number) |= DCSR_RUN;
}
static inline void dma_stop_overlay(void)
{
DECLARE_WAITQUEUE(wait, current);
pr_debug(F0);
/* stop dma controller */
DCSR(dma_channel_number) |= DCSR_STOPIRQEN;
DCSR(dma_channel_number) &= ~DCSR_RUN;
/* wait when dma controller stops */
add_wait_queue(&dma_stop_waitqueue, &wait);
set_current_state(TASK_UNINTERRUPTIBLE);
while (1) {
if (DCSR(dma_channel_number) & DCSR_STOPSTATE)
break;
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&dma_stop_waitqueue, &wait);
}
#else
static inline void dma_start_overlay(void)
{
}
static inline void dma_stop_overlay(void)
{
}
static inline int overlay_allocate(void)
{
return 0;
}
static inline void overlay_free(void)
{
}
#endif
/* END overlay */
/* BEGIN dma */
static int dma_start_x(int resume)
{
unsigned int format_index = camera_find_format(pix_format.pixelformat);
unsigned int width = pix_format.width;
unsigned int height = pix_format.height;
int err;
pr_debug(F("%s"), resume ? "resume" : "");
#if CONFIG_FB_PXA
if (mainstone_previewing) {
format_index =
camera_find_format(framebuffer_format.fmt.pixelformat);
if (camera_formats[format_index].pixelformat !=
framebuffer_format.fmt.pixelformat) {
pr_debug(F
("the format of framebuffer is not supported"));
return -EINVAL;
}
width = window_format.w.width;
height = window_format.w.height;
}
#endif
if (camera_width < width || camera_height < height) {
/*
* we could possibly check that when setting capture size
* or camera 'input' size (camera_[height,width]) but that
* would restrict the order in which user set these parameters
*/
pr_info
("Camera input size must be greater than image format.\n");
return -EINVAL;
}
sequence = 0;
/* ci: reset fifo */
CIFR |= CI_CIFR_RESETF;
/* ci: clear int status */
CISR = ~0;
/* set up dma */
if (!mainstone_previewing) {
if (!resume) {
err = void_unit_allocate();
if (err)
return err;
}
units_start();
} else {
if (!resume) {
/* allocate and initialize dma descriptors for overlay */
err = overlay_allocate();
if (err)
return err;
}
/* run overlay dma */
dma_start_overlay();
}
/* program capture interface with video fromat */
ci_set_image_format(camera_formats[format_index].ci_in_format,
camera_formats[format_index].ci_out_format);
ci_configure_mp(width - 1, height - 1, 0, 0);
/* ci: enable interface with dma */
CICR0 |= (CI_CICR0_ENB | CI_CICR0_DMA_EN);
if(reg_write(MT9V022_WINDOW_WIDTH, width)!=0)
{
printk("set width error\n");
}
if(reg_write(MT9V022_WINDOW_HEIGHT, height)!=0)
{
printk("set height error\n");
}
return 0;
}
static inline int dma_start(void)
{
return dma_start_x(0);
}
static inline int dma_resume(void)
{
return dma_start_x(1);
}
static void dma_stop_x(int suspend)
{
pr_debug(F("%s"), suspend ? "suspend" : "");
/* stop dma and wait for it */
if (!mainstone_previewing) {
units_stop();
if (!suspend) {
void_unit_free();
}
} else {
dma_stop_overlay();
if (!suspend) {
overlay_free();
}
}
ci_disable_quick();
}
static inline void dma_stop(void)
{
dma_stop_x(0);
}
static inline void dma_suspend(void)
{
dma_stop_x(1);
}
/* END dma */
/* BEGIN video buffer callbacks */
static void mainstone_vbq_release(struct videobuf_queue *q,
struct videobuf_buffer *vb);
static int mainstone_vbq_setup(struct videobuf_queue *q, unsigned int *count,
unsigned int *size)
{
pr_debug(F0);
if (*count <= 0)
*count = VIDEO_MAX_FRAME;
if (*count > VIDEO_MAX_FRAME)
*count = VIDEO_MAX_FRAME;
*size = pix_format.sizeimage;
pr_debug(F("count : %u; size : %u"), *count, *size);
return 0;
}
static int mainstone_vbq_prepare(struct videobuf_queue *q,
struct videobuf_buffer *vb,
enum v4l2_field field)
{
int err = 0;
pr_debug(F("@ buffer : %d"), VIDEOBUF_INDEX(vb));
if (vb->baddr && (pix_format.sizeimage > vb->bsize)) {
/* This is a userspace buffer and it isn't big enough. */
err = -EINVAL;
}
vb->size = pix_format.sizeimage;
vb->width = pix_format.width;
vb->height = pix_format.height;
vb->field = field;
if (err)
return err;
if (vb->state == STATE_NEEDS_INIT) {
err = videobuf_iolock(q, vb, NULL);
if (!err)
err =
videobuffer_unit_allocate((struct videobuffer *)vb);
}
if (!err)
vb->state = STATE_PREPARED;
else
mainstone_vbq_release(q, vb);
return err;
}
static void mainstone_vbq_queue(struct videobuf_queue *q,
struct videobuf_buffer *vb)
{
pr_debug(F("@ buffer : %d"), VIDEOBUF_INDEX(vb));
vb->state = STATE_QUEUED;
list_add_tail(&vb->queue, &queued_buffers);
}
static void mainstone_vbq_release(struct videobuf_queue *q,
struct videobuf_buffer *vb)
{
pr_debug(F("@ buffer : %d"), VIDEOBUF_INDEX(vb));
videobuf_waiton(vb, 0, 0);
videobuf_pci_dma_unmap(NULL, &vb->dma);
videobuf_dma_free(&vb->dma);
videobuffer_unit_free((struct videobuffer *)vb);
vb->state = STATE_NEEDS_INIT;
}
struct videobuf_queue_ops mainstone_videobuf_queue_ops = {
.buf_setup = mainstone_vbq_setup,
.buf_prepare = mainstone_vbq_prepare,
.buf_queue = mainstone_vbq_queue,
.buf_release = mainstone_vbq_release,
};
/* END video buffer callbacks */
/* BEGIN file operations */
#ifdef CONFIG_PM
static DECLARE_RWSEM(suspend_rwsem);
#endif
static int
mainstone_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
void *arg)
{
int err = 0;
#ifdef CONFIG_PM
down_read(&suspend_rwsem);
#endif
switch (cmd) {
case VIDIOC_ENUMINPUT:
{
struct v4l2_input *input = (struct v4l2_input *)arg;
int index = input->index;
pr_debug(F("VIDIOC_ENUMINPUT"));
if (index != 0) {
err = -EINVAL;
break;
}
memset(input, 0, sizeof(*input));
input->index = index;
strlcpy(input->name, "camera", sizeof(input->name));
input->type = V4L2_INPUT_TYPE_CAMERA;
break;
}
case VIDIOC_G_INPUT:
{
unsigned int *input = arg;
pr_debug(F("VIDIOC_G_INPUT"));
*input = 0;
break;
}
case VIDIOC_S_INPUT:
{
unsigned int *input = arg;
pr_debug(F("VIDIOC_S_INPUT"));
if (*input > 0)
err = -EINVAL;
break;
}
case VIDIOC_QUERYCAP:
{
struct v4l2_capability *cap =
(struct v4l2_capability *)arg;
pr_debug(F("VIDIOC_QUERYCAP"));
memset(cap, 0, sizeof(*cap));
strlcpy(cap->driver, DRIVER_NAME, sizeof(cap->driver));
strlcpy(cap->card, DEVICE_NAME, sizeof(cap->card));
cap->bus_info[0] = '\0';
cap->version = KERNEL_VERSION(0, 0, 0);
cap->capabilities =
#if CONFIG_FB_PXA
V4L2_CAP_VIDEO_OVERLAY |
#endif
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
break;
}
case VIDIOC_G_FMT:
{
struct v4l2_format *f = (struct v4l2_format *)arg;
pr_debug(F("VIDIOC_G_FMT"));
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
struct v4l2_pix_format *pix = &f->fmt.pix;
down(&mainstone_video_sema);
*pix = pix_format;
/*
* in videobuffer, the size of videobuffer is aligned to pagesize,
* and userspace buffer length is compared to this aligned length.
* I don't know why.
*/
pix->sizeimage = PAGE_ALIGN(pix->sizeimage);
up(&mainstone_video_sema);
#if CONFIG_FB_PXA
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) {
struct v4l2_window *win = &f->fmt.win;
down(&mainstone_video_sema);
*win = window_format;
up(&mainstone_video_sema);
#endif
} else {
err = -EINVAL;
}
break;
}
case VIDIOC_TRY_FMT:
{
struct v4l2_format *f = (struct v4l2_format *)arg;
pr_debug(F("VIDIOC_TRY_FMT"));
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
try_pix_format(&f->fmt.pix);
#if CONFIG_FB_PXA
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) {
try_window_format(&f->fmt.win);
#endif
} else {
err = -EINVAL;
}
break;
}
case VIDIOC_S_FMT:
{
struct v4l2_format *f = (struct v4l2_format *)arg;
pr_debug(F("VIDIOC_S_FMT"));
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
struct v4l2_pix_format *pix = &f->fmt.pix;
down(&mainstone_video_sema);
/* cannot change format on the fly */
if (mainstone_previewing || mainstone_streaming
|| mainstone_reading) {
up(&mainstone_video_sema);
err = -EBUSY;
break;
}
try_pix_format(pix);
pix_format = *pix;
/* see above */
pix->sizeimage = PAGE_ALIGN(pix->sizeimage);
up(&mainstone_video_sema);
#if CONFIG_FB_PXA
} else if (f->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) {
struct v4l2_window *win = &f->fmt.win;
down(&mainstone_video_sema);
/* cannot change format on the fly */
if (mainstone_previewing || mainstone_streaming
|| mainstone_reading) {
up(&mainstone_video_sema);
err = -EBUSY;
break;
}
try_window_format(win);
window_format = *win;
up(&mainstone_video_sema);
#endif
} else {
err = -EINVAL;
}
break;
}
case VIDIOC_ENUM_FMT:
{
struct v4l2_fmtdesc *fmt = (struct v4l2_fmtdesc *)arg;
int index = fmt->index;
enum v4l2_buf_type type = fmt->type;
pr_debug(F("VIDIOC_ENUM_FMT"));
if (index >= ARRAY_SIZE(camera_formats) ||
type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
err = -EINVAL;
break;
}
memset(fmt, 0, sizeof(*fmt));
fmt->index = index;
fmt->type = type;
fmt->flags = 0;
strlcpy(fmt->description,
camera_formats[index].description,
sizeof(fmt->description)
);
fmt->pixelformat = camera_formats[index].pixelformat;
break;
}
#if CONFIG_FB_PXA
case VIDIOC_G_FBUF:
{
struct v4l2_framebuffer *fb =
(struct v4l2_framebuffer *)arg;
pr_debug(F("VIDIOC_G_FBUF"));
down(&mainstone_video_sema);
*fb = framebuffer_format;
up(&mainstone_video_sema);
break;
}
case VIDIOC_S_FBUF:
{
struct v4l2_framebuffer *fb =
(struct v4l2_framebuffer *)arg;
pr_debug(F("VIDIOC_S_FBUF"));
if (current->uid && current->euid
&& !capable(CAP_SYS_ADMIN)) {
err = -EPERM;
break;
}
down(&mainstone_video_sema);
/* cannot change format on the fly */
if (mainstone_previewing || mainstone_streaming
|| mainstone_reading) {
up(&mainstone_video_sema);
err = -EBUSY;
break;
}
try_framebuffer_format(fb);
framebuffer_format = *fb;
up(&mainstone_video_sema);
break;
}
case VIDIOC_OVERLAY:
pr_debug(F("VIDIOC_OVERLAY %d"), *(int *)arg);
down(&mainstone_video_sema);
if (*(int *)arg) {
if (mainstone_previewing || mainstone_streaming
|| mainstone_reading) {
err = -EBUSY;
} else {
/* overlay on */
mainstone_previewing = file;
err = dma_start();
if (err)
mainstone_previewing = 0;
}
} else {
if (mainstone_previewing != file) {
err = -EBUSY;
} else {
/* overlay off */
dma_stop();
mainstone_previewing = 0;
}
}
up(&mainstone_video_sema);
break;
#else
case VIDIOC_G_FBUF:
case VIDIOC_S_FBUF:
case VIDIOC_OVERLAY:
err = -EINVAL;
break;
#endif
case VIDIOC_CROPCAP:
{
struct v4l2_cropcap *cropcap =
(struct v4l2_cropcap *)arg;
enum v4l2_buf_type type = cropcap->type;
pr_debug(F("VIDIOC_CROPCAP"));
if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
err = -EINVAL;
break;
}
memset(cropcap, 0, sizeof(*cropcap));
cropcap->type = type;
/*
* We're required to support the CROPCAP
* ioctl even though the G_CROP/S_CROP
* ioctls are optional. We don't support
* cropping of captured images.
*/
down(&mainstone_video_sema);
cropcap->bounds.width = pix_format.width;
cropcap->bounds.height = pix_format.height;
up(&mainstone_video_sema);
cropcap->defrect.width = cropcap->bounds.width;
cropcap->defrect.height = cropcap->bounds.height;
cropcap->pixelaspect.numerator = 1;
cropcap->pixelaspect.denominator = 1;
break;
}
case VIDIOC_G_CROP:
case VIDIOC_S_CROP:
pr_debug(F("VIDIOC_x_CROP"));
err = -EINVAL;
break;
case VIDIOC_G_PARM:
case VIDIOC_S_PARM:
pr_debug(F("VIDIOC_x_PARM"));
err = -EINVAL;
break;
case VIDIOC_REQBUFS:
pr_debug(F("VIDIOC_REQBUFS"));
down(&mainstone_video_sema);
if (mainstone_previewing || mainstone_streaming
|| mainstone_reading) {
up(&mainstone_video_sema);
err = -EBUSY;
break;
}
mainstone_streaming = file;
mainstone_streaming_streamon = 0;
videobuf_queue_init(&mainstone_queue,
&mainstone_videobuf_queue_ops, 0,
&mainstone_irq_lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_NONE, sizeof(struct videobuffer),
0);
err = videobuf_reqbufs(&mainstone_queue, arg);
up(&mainstone_video_sema);
break;
case VIDIOC_QUERYBUF:
pr_debug(F("VIDIOC_QUERYBUF"));
down(&mainstone_video_sema);
if (mainstone_streaming != file) {
up(&mainstone_video_sema);
err = -EBUSY;
break;
}
err = videobuf_querybuf(&mainstone_queue, arg);
up(&mainstone_video_sema);
break;
case VIDIOC_QBUF:
pr_debug(F("VIDIOC_QBUF"));
down(&mainstone_video_sema);
if (mainstone_streaming != file) {
up(&mainstone_video_sema);
err = -EBUSY;
break;
}
/*
* This driver is not completely v4l2 compliant
* --it requires the user pointer for streaming
* are 8-byte aligned. This is because of dma controller
* can transfer data from capture interface only in
* chunks that have length of integer multiple of 8 byte
*/
if ((((struct v4l2_buffer *)arg)->memory == V4L2_MEMORY_USERPTR)
&& (((struct v4l2_buffer *)arg)->m.userptr & 7)
) {
up(&mainstone_video_sema);
pr_debug(F("not aligned"));
err = -EINVAL;
break;
}
err = videobuf_qbuf(&mainstone_queue, arg);
up(&mainstone_video_sema);
break;
case VIDIOC_DQBUF:
pr_debug(F("VIDIOC_DQBUF"));
down(&mainstone_video_sema);
if (mainstone_streaming != file) {
up(&mainstone_video_sema);
err = -EBUSY;
break;
}
err =
videobuf_dqbuf(&mainstone_queue, arg,
file->f_flags & O_NONBLOCK);
up(&mainstone_video_sema);
break;
case VIDIOC_STREAMON:
pr_debug(F("VIDIOC_STREAMON"));
if (*(int *)arg != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
err = -EINVAL;
break;
}
down(&mainstone_video_sema);
if (mainstone_streaming != file) {
up(&mainstone_video_sema);
err = -EBUSY;
break;
}
if (mainstone_streaming_streamon) {
/* already on */
up(&mainstone_video_sema);
err = -EBUSY;
break;
}
err = videobuf_streamon(&mainstone_queue);
if (!err) {
err = dma_start();
if (err) {
videobuf_streamoff(&mainstone_queue);
up(&mainstone_video_sema);
break;
}
mainstone_streaming_streamon = 1;
}
up(&mainstone_video_sema);
break;
case VIDIOC_STREAMOFF:
pr_debug(F("VIDIOC_STREAMOFF"));
if (*(int *)arg != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
err = -EINVAL;
break;
}
down(&mainstone_video_sema);
if (mainstone_streaming != file) {
up(&mainstone_video_sema);
err = -EBUSY;
break;
}
if (!mainstone_streaming_streamon) {
/* already off */
up(&mainstone_video_sema);
err = -EBUSY;
break;
}
mainstone_streaming_streamon = 0;
dma_stop();
err = videobuf_streamoff(&mainstone_queue);
up(&mainstone_video_sema);
break;
case VIDIOC_G_CTRL:
{
struct v4l2_control *ctrl = (struct v4l2_control *)arg;
int data;
pr_debug(F("VIDIOC_G_CTRL"));
switch(ctrl->id)
{
case V4L2_CID_VFLIP:
data = reg_read(MT9V022_READ_MODE);
if (data < 0)
{
err = -EIO;
break;
}
ctrl->value = !!(data & 0x10);
break;
case V4L2_CID_HFLIP:
data = reg_read(MT9V022_READ_MODE);
if (data < 0)
{
err = -EIO;
break;
}
ctrl->value = !!(data & 0x20);
break;
case V4L2_CID_EXPOSURE_AUTO:
data = reg_read(MT9V022_AEC_AGC_ENABLE);
if (data < 0)
{
err = -EIO;
break;
}
ctrl->value = !!(data & 0x1);
break;
case V4L2_CID_AUTOGAIN:
data = reg_read(MT9V022_AEC_AGC_ENABLE);
if (data < 0)
{
err = -EIO;
break;
}
ctrl->value = !!(data & 0x2);
break;
default :
err = -EIO;
break;
}
break;
}
case VIDIOC_S_CTRL:
{
struct v4l2_control *ctrl = (struct v4l2_control *)arg;
const struct v4l2_queryctrl *qctrl;
int data;
pr_debug(F("VIDIOC_S_CTRL"));
qctrl = soc_camera_find_qctrl(ctrl->id);
if (!qctrl)
{
err = -EINVAL;
break;
}
down(&mainstone_video_sema);
if (mainstone_previewing || mainstone_streaming
|| mainstone_reading) {
err = -EBUSY;
goto error;
}
switch(ctrl->id)
{
case V4L2_CID_VFLIP:
if (ctrl->value)
data = reg_set(MT9V022_READ_MODE, 0x10);//overturn
else
data = reg_clear(MT9V022_READ_MODE, 0x10);//not overtrun
if (data < 0)
err = -EIO;
break;
case V4L2_CID_HFLIP:
if (ctrl->value)
data = reg_set(MT9V022_READ_MODE, 0x20);//overturn
else
data = reg_clear(MT9V022_READ_MODE, 0x20);//not overtrun
if (data < 0)
err = -EIO;
break;
case V4L2_CID_GAIN:
/* mt9v022 has minimum == default */
if (ctrl->value > qctrl->maximum|| ctrl->value < qctrl->minimum)
{
err = -EINVAL;
break;
}
else
{
unsigned long range = qctrl->maximum - qctrl->minimum;
/* Datasheet says 16 to 64. autogain only works properly
* after setting gain to maximum 14. Larger values
* produce "white fly" noise effect. On the whole,
* manually setting analog gain does no good. */
unsigned long gain = ((ctrl->value - qctrl->minimum) *
10 + range / 2) / range + 4;
if (gain >= 32)
gain &= ~1;
/* The user wants to set gain manually, hope, she
* knows, what she's doing... Switch AGC off. */
if (reg_clear(MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
{
err = -EIO;
break;
}
printk("Setting gain from %d to %lu\n",
reg_read(MT9V022_ANALOG_GAIN), gain);
if (reg_write(MT9V022_ANALOG_GAIN, gain) < 0)
{
err = -EIO;
break;
}
//icd->gain = ctrl->value;
}
break;
case V4L2_CID_EXPOSURE:
/* mt9v022 has maximum == default */
if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum)
{
err = -EINVAL;
break;
}
else
{
unsigned long range = qctrl->maximum - qctrl->minimum;
unsigned long shutter = ((ctrl->value - qctrl->minimum) *
479 + range / 2) / range + 1;
/* The user wants to set shutter width manually, hope,
* she knows, what she's doing... Switch AEC off. */
if (reg_clear(MT9V022_AEC_AGC_ENABLE, 0x1) < 0)
{
err = -EIO;
break;
}
printk("Shutter width from %d to %lu\n",
reg_read(MT9V022_TOTAL_SHUTTER_WIDTH),
shutter);
if (reg_write(MT9V022_TOTAL_SHUTTER_WIDTH,
shutter) < 0)
{
return -EIO;
break;
}
//icd->exposure = ctrl->value;
}
break;
case V4L2_CID_AUTOGAIN:
if (ctrl->value)
data = reg_set(MT9V022_AEC_AGC_ENABLE, 0x2);//auto
else
data = reg_clear(MT9V022_AEC_AGC_ENABLE, 0x2);//manual
if (data < 0)
err = -EIO;
break;
case V4L2_CID_EXPOSURE_AUTO:
if (ctrl->value)
data = reg_set(MT9V022_AEC_AGC_ENABLE, 0x1);//auto
else
data = reg_clear(MT9V022_AEC_AGC_ENABLE, 0x1);//manual
if (data < 0)
err = -EIO;
break;
default :
err = -EIO;
break;
}
error:
up(&mainstone_video_sema);
break;
}
case VIDIOC_QUERYCTRL:
{
const struct v4l2_queryctrl *ctrl;
struct v4l2_queryctrl *c = arg;
ctrl = soc_camera_find_qctrl(c->id);
if( NULL == ctrl )
{
err = -EINVAL;
break;
}
*c = *ctrl;
break;
}
case VIDIOC_QUERYMENU:
pr_debug(F("VIDIOC_QUERYMENU"));
err = -EINVAL;
break;
case VIDIOC_ENUMSTD:
case VIDIOC_G_STD:
case VIDIOC_S_STD:
case VIDIOC_QUERYSTD:
/*
* Digital cameras don't have an analog video standard,
* so we don't need to implement these ioctls.
*/
pr_debug(F("VIDIOC_xSTD"));
err = -EINVAL;
break;
case VIDIOC_ENUMAUDIO:
case VIDIOC_ENUMAUDOUT:
case VIDIOC_G_AUDIO:
case VIDIOC_S_AUDIO:
case VIDIOC_G_AUDOUT:
case VIDIOC_S_AUDOUT:
/* we don't have any audio inputs or outputs */
pr_debug(F("VIDIOC_xAUDIO"));
err = -EINVAL;
break;
case VIDIOC_G_JPEGCOMP:
case VIDIOC_S_JPEGCOMP:
/* JPEG compression is not supported */
pr_debug(F("VIDIOC_x_JPEGCOMP"));
err = -EINVAL;
break;
case VIDIOC_G_TUNER:
case VIDIOC_S_TUNER:
case VIDIOC_G_MODULATOR:
case VIDIOC_S_MODULATOR:
case VIDIOC_G_FREQUENCY:
case VIDIOC_S_FREQUENCY:
/* we don't have a tuner or modulator */
pr_debug(F("VIDIOC_x_[TUNER,MODULATOR,FREQUENCY]"));
err = -EINVAL;
break;
case VIDIOC_ENUMOUTPUT:
case VIDIOC_G_OUTPUT:
case VIDIOC_S_OUTPUT:
/* we don't have any video outputs */
pr_debug(F("VIDIOC_xOUTPUT"));
err = -EINVAL;
break;
case VIDIOC_G_PRIORITY:
case VIDIOC_S_PRIORITY:
pr_debug(F("VIDIOC_x_PRIORITY"));
err = -EINVAL;
break;
default:
/* unrecognized ioctl */
pr_debug(F("???"));
err = -ENOIOCTLCMD;
break;
}
#ifdef CONFIG_PM
up_read(&suspend_rwsem);
#endif
return err;
}
static unsigned int
mainstone_poll(struct file *file, struct poll_table_struct *wait)
{
int err = 0;
pr_debug(F0);
#ifdef CONFIG_PM
down_read(&suspend_rwsem);
#endif
if (down_interruptible(&mainstone_video_sema)) {
err = -ERESTARTSYS;
goto exit1;
}
if (mainstone_previewing) {
err = -EBUSY;
goto exit;
}
if (!mainstone_streaming) {
if (mainstone_reading == 0) {
videobuf_queue_init(&mainstone_queue,
&mainstone_videobuf_queue_ops, 0,
&mainstone_irq_lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_NONE,
sizeof(struct videobuffer), 0);
err = dma_start();
if (err)
goto exit;
mainstone_reading = file;
} else if (mainstone_reading != file) {
err = -EBUSY;
goto exit;
}
}
err = videobuf_poll_stream(file, &mainstone_queue, wait);
exit:
up(&mainstone_video_sema);
exit1:
#ifdef CONFIG_PM
up_read(&suspend_rwsem);
#endif
return err;
}
static ssize_t
mainstone_read(struct file *file, char *data, size_t count, loff_t * ppos)
{
int err = 0;
pr_debug(F0);
#ifdef CONFIG_PM
down_read(&suspend_rwsem);
#endif
if (down_interruptible(&mainstone_video_sema)) {
err = -ERESTARTSYS;
goto exit1;
}
if (mainstone_streaming || mainstone_previewing) {
err = -EBUSY;
goto exit;
}
if (mainstone_reading == 0) {
videobuf_queue_init(&mainstone_queue,
&mainstone_videobuf_queue_ops, 0,
&mainstone_irq_lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_NONE, sizeof(struct videobuffer),
0);
err = dma_start();
if (err)
goto exit;
mainstone_reading = file;
} else if (mainstone_reading != file) {
err = -EBUSY;
goto exit;
}
err =
videobuf_read_one(&mainstone_queue, data, count, ppos,
file->f_flags & O_NONBLOCK);
exit:
up(&mainstone_video_sema);
exit1:
#ifdef CONFIG_PM
up_read(&suspend_rwsem);
#endif
return err;
}
static int mainstone_mmap(struct file *file, struct vm_area_struct *vma)
{
int err = 0;
pr_debug(F0);
#ifdef CONFIG_PM
down_read(&suspend_rwsem);
#endif
err = videobuf_mmap_mapper(&mainstone_queue, vma);
#ifdef CONFIG_PM
up_read(&suspend_rwsem);
#endif
return err;
}
static int
mainstone_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
return video_usercopy(inode, file, cmd, arg, mainstone_do_ioctl);
}
static int mainstone_release(struct inode *inode, struct file *file)
{
int err = 0;
pr_debug(F0);
#ifdef CONFIG_PM
down_read(&suspend_rwsem);
#endif
if (down_interruptible(&mainstone_video_sema)) {
err = -ERESTARTSYS;
goto exit1;
}
#if CONFIG_FB_PXA
if (mainstone_previewing == file) {
dma_stop();
mainstone_previewing = 0;
}
#endif
if (mainstone_streaming == file) {
if (mainstone_streaming_streamon) {
dma_stop();
mainstone_streaming_streamon = 0;
}
mainstone_streaming = 0;
videobuf_streamoff(&mainstone_queue);
videobuf_mmap_free(&mainstone_queue);
}
if (mainstone_reading) {
dma_stop();
mainstone_reading = 0;
if (mainstone_queue.read_buf) {
mainstone_vbq_release(&mainstone_queue,
mainstone_queue.read_buf);
kfree(mainstone_queue.read_buf);
mainstone_queue.read_buf = 0;
}
}
up(&mainstone_video_sema);
exit1:
#ifdef CONFIG_PM
up_read(&suspend_rwsem);
#endif
return err;
}
static struct video_device mainstone_video_device;
static int mainstone_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
pr_debug(F0);
if (mainstone_video_device.minor != minor)
return -ENODEV;
file->private_data = 0;
return 0;
}
static struct file_operations mainstone_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = mainstone_read,
.poll = mainstone_poll,
.ioctl = mainstone_ioctl,
.mmap = mainstone_mmap,
.open = mainstone_open,
.release = mainstone_release,
};
/* END file operations */
/*
* We need this, see drivers/media/video/videodev.c
* video_register_device()
*/
static void mainstone_video_device_release(struct video_device *device)
{
}
static struct video_device mainstone_video_device = {
.dev = 0,
.name = DEVICE_NAME,
.type = VID_TYPE_CAPTURE,
.hardware = 0,
.minor = -1,
.fops = &mainstone_fops,
.release = &mainstone_video_device_release,
};
/**
* Initialize Mainstone camera (ADCM2650) using I2C (SMBus) protocol
* and PXA27x QuickCapture interface using memory mapped registers.
* The dma_channel_number should be alredy valid before calling this
* function.
*/
static int hardware_init(int first_time){
unsigned int v;
if (dma_channel_number < 0) {
pr_debug(F("internal error dma_channel_number"
" is not allocated yet"));
return -ENXIO;
}
/* enable hardware */
camera_gpio_init();
/* This routes QuickCapture DMA requests to
* the selected DMA channel (2800002.pdf 5.5.1 and Table 5-22),
* so that DMA transfer of video may work.
* The camera interface must be powered on yet.
*/
DRCMR68 = dma_channel_number | DRCMR_MAPVLD;
/* initialize capture interface */
ci_init();
/* set master parallel mode */
v = CICR0;
v &= ~(CI_CICR0_SIM_SMASK << CI_CICR0_SIM_SHIFT);
v |= CI_CICR0_SIM_MODE_MP << CI_CICR0_SIM_SHIFT;
CICR0 = v;
/* set 8 data pins */
v = CICR1;
v &= ~(CI_CICR1_DW_SMASK << CI_CICR1_DW_SHIFT);
v |= CI_CICR1_DW8 << CI_CICR1_DW_SHIFT;
CICR1 = v;
/* enable pixel clock(sensor will provide pclock) and master clock */
{
unsigned int ciclk, div, cccr_l, K;
/* determine the LCLK frequency programmed into the CCCR. */
cccr_l = (CCCR & 0x0000001F);
if (cccr_l < 8)
K = 1;
else if (cccr_l < 17)
K = 2;
else
K = 3;
/* in kHz */
ciclk = (13 * cccr_l * 1000) / K;
for (div = 1; 1; div++) {
mclk_khz = ciclk / (2 * (div + 1));
if (mclk_khz <= CI_MCLK_DEFT)
break;
}
pr_debug(F("ciclk : %d; div : %d; mclk_khz : %d"),
ciclk, div, mclk_khz);
v = CICR4;
v &= ~(CI_CICR4_DIV_SMASK << CI_CICR4_DIV_SHIFT);
v |= CI_CICR4_PCLK_EN | CI_CICR4_MCLK_EN | (div <<
CI_CICR4_DIV_SHIFT);
CICR4 = v;
}
/* data sample on rising and vsync active high */
CICR4 &= ~(CI_CICR4_PCP | CI_CICR4_HSP | CI_CICR4_VSP);
/* setup fifo (don't enable fifo1 and fifo2), 32 byte threshold */
v = CIFR;
v &= ~(CI_CIFR_FEN1 | CI_CIFR_FEN2 | CI_CIFR_THL_SMASK <<
CI_CIFR_THL_SHIFT);
v |= CI_CIFR_THL_32 << CI_CIFR_THL_SHIFT;
v |= CI_CIFR_RESETF | CI_CIFR_FEN0;
CIFR = v;
/* set timeout = 0 */
CITOR = 0;
/* Camera interface power on (Mainstone II User Guide 3.2.2.5),
* camera select MUX control. After this the camera may be
* set up through I2C interface.
*/
MST_MSCWR1 |= (MST_MSCWR1_CAMERA_ON | MST_MSCWR1_CAMERA_SEL);
msleep(50);
/*
* Register adcm2650 I2C device, if not yet registered only.
* For probbing the device while registering it all
* register setup which done above should be completed
* successfully.
*/
if(first_time){
mt9v022_init();
}
return 0;
}
/**
* Reverts what is done by hardware_init(), including
* deregistering of the I2C device.
*/
void hardware_done(void){
DRCMR68 = 0;
mt9v022_exit();
/* cut down at Board Level */
MST_MSCWR1 &= ~(MST_MSCWR1_CAMERA_ON | MST_MSCWR1_CAMERA_SEL);
CKEN &= ~CKEN24_CAMERA;
}
static int mainstone_initialize(struct device *dev)
{
int err;
pr_debug(F0);
/* initialize dma */
err = -ENXIO;
dma_channel_number =
pxa_request_dma("capture_interface", DMA_PRIO_HIGH, dma_irq, 0);
if (dma_channel_number < 0) {
return err;
}
DCSR(dma_channel_number) = 0;
/* initialise pixel format by default values */
pix_format.width = camera_width;
pix_format.height = camera_height;
pix_format.pixelformat = camera_formats[0].pixelformat;
try_pix_format(&pix_format);
#if CONFIG_FB_PXA
window_format.w.left = 0;
window_format.w.top = 0;
window_format.w.width = camera_width;
window_format.w.height = camera_height;
try_window_format(&window_format);
memset(&framebuffer_format, 0, sizeof(framebuffer_format));
try_framebuffer_format(&framebuffer_format);
#endif
/* Initialize QuickCapture and camera*/
err = hardware_init(1);
if(err!=0){
pr_debug(F(" error initialising QuickCapture and ADCM2650 camera"));
return err;
}
video_set_drvdata(&mainstone_video_device, 0);
err =
video_register_device(&mainstone_video_device, VFL_TYPE_GRABBER,
video_nr);
if (err) {
goto error;
}
err = reg_write(MT9V022_READ_MODE, 0x300);
/* All defaults */
if (err >= 0)
/* AEC, AGC on */
err = reg_set(MT9V022_AEC_AGC_ENABLE, 0x3);
if (err >= 0)
err = reg_write(MT9V022_MAX_TOTAL_SHUTTER_WIDTH, 480);
if (err >= 0)
/* default - auto */
err = reg_clear(MT9V022_BLACK_LEVEL_CALIB_CTRL, 1);
if (err >= 0)
err = reg_write(MT9V022_DIGITAL_TEST_PATTERN, 0);
pr_info("registered device video%d [v4l2]\n",
mainstone_video_device.minor);
return 0;
error:
hardware_done();
pxa_free_dma(dma_channel_number);
return err;
}
static int mainstone_cleanup(struct device *dev)
{
pr_debug(F0);
video_unregister_device(&mainstone_video_device);
hardware_done();
if (dma_channel_number >= 0)
pxa_free_dma(dma_channel_number);
/* may be mapped in int ci_init() */
if (ci_regs_base)
iounmap((unsigned long *)ci_regs_base);
return 0;
}
static int mainstone_suspend(struct device *dev, u32 state, u32 level)
{
pr_debug(F("state : %u; level : %u"), state, level);
//if (level != SUSPEND_DISABLE){
if (level != 2) {//SUSPEND_DISABLE not define
return 0;
}
#ifdef CONFIG_PM
switch (level) {
case SUSPEND_POWER_DOWN:
pr_debug(F("suspend"));
down_write(&suspend_rwsem);
if (mainstone_previewing || mainstone_streaming
|| mainstone_reading)
dma_suspend();
break;
}
#endif
return 0;
}
static int mainstone_resume(struct device *dev, u32 level)
{
//if (level != RESUME_ENABLE) {
if (level != 2) {//RESUME_ENABLE not define
return 0;
}
hardware_init(0);
#ifdef CONFIG_PM
switch (level) {
case RESUME_POWER_ON:
pr_debug(F("resume"));
if (mainstone_previewing || mainstone_streaming
|| mainstone_reading)
dma_resume();
up_write(&suspend_rwsem);
break;
}
#endif
return 0;
}
/*
* We need this, see drivers/base/core.c, device_release()
*/
static void mainstone_platform_release(struct device *device)
{
}
static struct device_driver mainstone_driver = {
.name = DRIVER_NAME,
.bus = &platform_bus_type,
.probe = mainstone_initialize,
.remove = mainstone_cleanup,
.suspend = mainstone_suspend,
.resume = mainstone_resume,
};
static struct platform_device mainstone_device = {
.name = DRIVER_NAME,
.id = 0,
.dev = {
.release = mainstone_platform_release,
}
};
static int __init mainstone_init(void)
{
int err;
pr_info("Mainstone camera\n");
err = driver_register(&mainstone_driver);
if (err)
return err;
err = platform_device_register(&mainstone_device);
if (err) {
driver_unregister(&mainstone_driver);
return err;
}
return 0;
}
#ifdef MODULE
static void __exit mainstone_exit(void)
{
pr_info("Mainstone camera exit\n");
platform_device_unregister(&mainstone_device);
driver_unregister(&mainstone_driver);
}
module_exit(mainstone_exit);
#endif
module_init(mainstone_init);
MODULE_AUTHOR("MontaVista Software Inc.");
MODULE_DESCRIPTION("Intel PXA27x Mainstone camera driver");
MODULE_LICENSE("GPL");
static int video_nr = -1;
module_param(video_nr, int, 0);
MODULE_PARM_DESC(video_nr,
"Minor number for video device (-1 ==> auto assign)");
2、-----------------This is mt9v022.c-----------------
/*
* drivers/media/video/mt9v022.c
*
* mt9v022 Camera Module driver.
*
* Author: Intel Corporation
*
* 2003 (c) Intel Corporation
* 2003-2005 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/types.h>
#include <asm/mach-types.h>
#include <asm/io.h>
#include <asm/semaphore.h>
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/dma.h>
#include <asm/irq.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include "mt9v022.h"
#define LINE_STRING __stringify(KBUILD_BASENAME) ":" __stringify(__LINE__) " : "
int mt9v022_client_id = -1;
static struct i2c_driver mt9v022_driver;
static DECLARE_MUTEX(i2c_lock);
/*
* support only one client (= exemplar of the sensor)
* per whole system so far
*/
struct i2c_client mt9v022_client = {
.flags = 0x01,
.driver = &mt9v022_driver,
.name = "mt9v022-0",
};
int reg_read(const u8 reg)
{
s32 data = i2c_smbus_read_word_data(&mt9v022_client, reg);
return data < 0 ? data : swab16(data);
}
int reg_write(const u8 reg,
const u16 data)
{
return i2c_smbus_write_word_data(&mt9v022_client, reg, swab16(data));
}
int reg_set(const u8 reg,
const u16 data)
{
int ret;
ret = reg_read(reg);
if (ret < 0)
return ret;
return reg_write(reg, ret | data);
}
int reg_clear(const u8 reg,
const u16 data)
{
int ret;
ret = reg_read(reg);
if (ret < 0)
return ret;
return reg_write(reg, ret & ~data);
}
static int i2c_mt9v022_detect_client(struct i2c_adapter *adapter, int address,
int kind)
{
int err;
pr_debug(LINE_STRING "\n");
pr_info("ADCM2650: i2c-bus:%s; address:0x%x... ",
adapter->name, address);
if (mt9v022_client_id != -1) {
pr_info("the sensor is already initialized.\n");
/*
* anyway, return 0 or else
* the detection cycle will be broken
*/
return 0;
}
/* returns true if the functionality is supported */
err = i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA);
if (!err) {
pr_info("word operations is not permited.\n");
return 0;
}
mt9v022_client.addr = address;
mt9v022_client.adapter = adapter;
/*
* don't pay attention to kind argument --
* we make the lists of addresses ourselves
*/
err = reg_read(MT9V022_CHIP_VERSION);
if (err != 0x1313) {
pr_info("failed.\n");
return 0;
} else {
pr_info("detected.\n");
}
mt9v022_client_id = 0;
err = i2c_attach_client(&mt9v022_client);
if (err) {
pr_debug(LINE_STRING "i2c_attach_client()\n");
mt9v022_client_id = -1;
}
return 0;
}
static unsigned short normal_i2c[] = {MT9V022_SENSOR_SLAVE_ADDR,
I2C_CLIENT_END};
static unsigned short probe = I2C_CLIENT_END;
static unsigned short ignore = I2C_CLIENT_END;
//static unsigned short forces = I2C_CLIENT_END;
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_i2c,
.probe = &probe,
.ignore = &ignore,
// .forces = forces,
};
static int i2c_mt9v022_probe(struct i2c_adapter *adap)
{
pr_debug(LINE_STRING "\n");
return i2c_probe(adap, &addr_data, i2c_mt9v022_detect_client);
}
static int i2c_mt9v022_detach(struct i2c_client *client)
{
int err;
pr_debug(LINE_STRING "\n");
err = i2c_detach_client(client);
if (err) {
pr_debug(LINE_STRING "i2c_detach_client()\n");
return err;
}
return 0;
}
#define I2C_DRIVERID_ADCM2650 I2C_DRIVERID_EXP1
static struct i2c_driver mt9v022_driver = {
.driver = {
.name = "mt9v022",
},
.id = MT9V022_SENSOR_SLAVE_ADDR,
.attach_adapter = &i2c_mt9v022_probe,
.detach_client = &i2c_mt9v022_detach,
};
int mt9v022_init(void)
{
pr_debug(LINE_STRING "\n");
return i2c_add_driver(&mt9v022_driver);
}
void mt9v022_exit(void)
{
pr_debug(LINE_STRING "\n");
i2c_del_driver(&mt9v022_driver);
}
3、-----------------This is mainstone.h-----------------
/*
* linux/drivers/media/video/mainstone.h
*
* Driver for Intel Mainstone Camera
*
* Author: Aleskey Makarov <amakarov@xxxxxxxxxxxxx>
*
* 2003 (c) Intel Corporation
* 2003-2005 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#ifndef _MAINSTONE_CAMERA_H__
#define _MAINSTONE_CAMERA_H__
/* MCLK, kHz */
#define CI_MCLK_DEFT 13000
/* phys addreses of ci registers CIBR */
#define CIBR0_PHY (0x50000000 + 0x28)
#define CIBR1_PHY (0x50000000 + 0x30)
#define CIBR2_PHY (0x50000000 + 0x38)
#define CI_REG_SIZE 0x40 /* 0x5000_0000 --- 0x5000_0038 * 64K */
#define CI_REGS_PHYS 0x50000000
/* CICR0 */
#define CI_CICR0_FOM (1 << 0)
#define CI_CICR0_EOFM (1 << 1)
#define CI_CICR0_SOFM (1 << 2)
#define CI_CICR0_CDM (1 << 3)
#define CI_CICR0_QDM (1 << 4)
#define CI_CICR0_PERRM (1 << 5)
#define CI_CICR0_EOLM (1 << 6)
#define CI_CICR0_FEM (1 << 7)
#define CI_CICR0_RDAVM (1 << 8)
#define CI_CICR0_TOM (1 << 9)
#define CI_CICR0_SIM_SHIFT 24
#define CI_CICR0_SIM_SMASK 0x7
#define CI_CICR0_SIM_MODE_MP 0 /* Master-Parallel */
#define CI_CICR0_SIM_MODE_SP 1 /* Slave-Parallel */
#define CI_CICR0_SIM_MODE_MS 2 /* Master-Serial */
#define CI_CICR0_SIM_MODE_EP 3 /* Embedded-Parallel */
#define CI_CICR0_SIM_MODE_ES 4 /* Embedded-Serial */
#define CI_CICR0_DIS (1 << 27)
#define CI_CICR0_ENB (1 << 28)
#define CI_CICR0_SL_CAP_EN (1 << 29)
#define CI_CICR0_PAR_EN (1 << 30)
#define CI_CICR0_DMA_EN (1 << 31)
#define CI_CICR0_INTERRUPT_MASK 0x3FF
/* CICR1 */
#define CI_CICR1_TBIT (1 << 31)
#define CI_CICR1_RGBT_CONV_SHIFT 29
#define CI_CICR1_RGBT_CONV_SMASK 0x3
#define CI_CICR1_PPL_SHIFT 15
#define CI_CICR1_PPL_SMASK 0x7FF
#define CI_CICR1_RGB_CONV_SHIFT 12
#define CI_CICR1_RGB_CONV_SMASK 0x7
#define CI_CICR1_RBG_F (1 << 11)
#define CI_CICR1_YCBCR_F (1 << 10)
#define CI_CICR1_RGB_BPP_SHIFT 7
#define CI_CICR1_RGB_BPP_SMASK 0x7
#define CI_CICR1_RAW_BPP_SHIFT 5
#define CI_CICR1_RAW_BPP_SMASK 0x3
#define CI_CICR1_COLOR_SP_SHIFT 3
#define CI_CICR1_COLOR_SP_SMASK 0x3
#define CI_CICR1_DW_SHIFT 0
#define CI_CICR1_DW_SMASK 0x7
#define CI_CICR1_DW4 0x0
#define CI_CICR1_DW5 0x1
#define CI_CICR1_DW8 0x2
#define CI_CICR1_DW9 0x3,
#define CI_CICR1_DW10 0x4
/* CICR2 */
#define CI_CICR2_FSW_SHIFT 0
#define CI_CICR2_FSW_SMASK 0x3
#define CI_CICR2_BFPW_SHIFT 3
#define CI_CICR2_BFPW_SMASK 0x3F
#define CI_CICR2_HSW_SHIFT 10
#define CI_CICR2_HSW_SMASK 0x3F
#define CI_CICR2_ELW_SHIFT 16
#define CI_CICR2_ELW_SMASK 0xFF
#define CI_CICR2_BLW_SHIFT 24
#define CI_CICR2_BLW_SMASK 0xFF
/* CICR3 */
#define CI_CICR3_LPF_SHIFT 0
#define CI_CICR3_LPF_SMASK 0x7FF
#define CI_CICR3_VSW_SHIFT 11
#define CI_CICR3_VSW_SMASK 0x1F
#define CI_CICR3_EFW_SHIFT 16
#define CI_CICR3_EFW_SMASK 0xFF
#define CI_CICR3_BFW_SHIFT 24
#define CI_CICR3_BFW_SMASK 0xFF
/* CICR4 */
#define CI_CICR4_DIV_SHIFT 0
#define CI_CICR4_DIV_SMASK 0xFF
#define CI_CICR4_FR_RATE_SHIFT 8
#define CI_CICR4_FR_RATE_SMASK 0x7
#define CI_CICR4_MCLK_EN (1 << 19)
#define CI_CICR4_VSP (1 << 20)
#define CI_CICR4_HSP (1 << 21)
#define CI_CICR4_PCP (1 << 22)
#define CI_CICR4_PCLK_EN (1 << 23)
/* CISR */
#define CI_CISR_IFO_0 (1 << 0)
#define CI_CISR_IFO_1 (1 << 1)
#define CI_CISR_IFO_2 (1 << 2)
#define CI_CISR_EOF (1 << 3)
#define CI_CISR_SOF (1 << 4)
#define CI_CISR_CDD (1 << 5)
#define CI_CISR_CQD (1 << 6)
#define CI_CISR_PAR_ERR (1 << 7)
#define CI_CISR_EOL (1 << 8)
#define CI_CISR_FEMPTY_0 (1 << 9)
#define CI_CISR_FEMPTY_1 (1 << 10)
#define CI_CISR_FEMPTY_2 (1 << 11)
#define CI_CISR_RDAV_0 (1 << 12)
#define CI_CISR_RDAV_1 (1 << 13)
#define CI_CISR_RDAV_2 (1 << 14)
#define CI_CISR_FTO (1 << 15)
/* CIFR */
#define CI_CIFR_FEN0 (1 << 0)
#define CI_CIFR_FEN1 (1 << 1)
#define CI_CIFR_FEN2 (1 << 2)
#define CI_CIFR_RESETF (1 << 3)
#define CI_CIFR_THL_SHIFT 4
#define CI_CIFR_THL_SMASK 0x3
#define CI_CIFR_THL_32 0
#define CI_CIFR_THL_64 1
#define CI_CIFR_THL_96 2
#define CI_CIFR_FLVL0_SHIFT 8
#define CI_CIFR_FLVL0_SMASK 0xFF
#define CI_CIFR_FLVL1_SHIFT 16
#define CI_CIFR_FLVL1_SMASK 0x7F
#define CI_CIFR_FLVL2_SHIFT 23
#define CI_CIFR_FLVL2_SMASK 0x7F
/* formats */
#define CI_RAW8 0 /* RAW */
#define CI_RAW9 1
#define CI_RAW10 2
#define CI_YCBCR422 3 /* YCBCR */
#define CI_YCBCR422_PLANAR 4 /* YCBCR Planaried */
#define CI_RGB444 5 /* RGB */
#define CI_RGB555 6
#define CI_RGB565 7
#define CI_RGB666 8
#define CI_RGB888 9
#define CI_RGBT555_0 10 /* RGB+Transparent bit 0 */
#define CI_RGBT888_0 11
#define CI_RGBT555_1 12 /* RGB+Transparent bit 1 */
#define CI_RGBT888_1 13
#define CI_RGB666_PACKED 14 /* RGB Packed */
#define CI_RGB888_PACKED 15
#define CI_INVALID_FORMAT 0xFF
#endif
4、-----------------This is mt9v022.h-----------------
/*
* drivers/media/video/mt9v022.h
*
* mt9v022 Camera Module driver.
*
* Author: Intel Corporation
*
* 2003 (c) Intel Corporation
* 2003-2005 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#define MT9V022_SENSOR_SLAVE_ADDR 0x48
/* mt9v022 selected register addresses */
#define MT9V022_CHIP_VERSION 0x00
#define MT9V022_COLUMN_START 0x01
#define MT9V022_ROW_START 0x02
#define MT9V022_WINDOW_HEIGHT 0x03
#define MT9V022_WINDOW_WIDTH 0x04
#define MT9V022_HORIZONTAL_BLANKING 0x05
#define MT9V022_VERTICAL_BLANKING 0x06
#define MT9V022_CHIP_CONTROL 0x07
#define MT9V022_SHUTTER_WIDTH1 0x08
#define MT9V022_SHUTTER_WIDTH2 0x09
#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a
#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b
#define MT9V022_RESET 0x0c
#define MT9V022_READ_MODE 0x0d
#define MT9V022_MONITOR_MODE 0x0e
#define MT9V022_PIXEL_OPERATION_MODE 0x0f
#define MT9V022_LED_OUT_CONTROL 0x1b
#define MT9V022_ADC_MODE_CONTROL 0x1c
#define MT9V022_ANALOG_GAIN 0x34
#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47
#define MT9V022_PIXCLK_FV_LV 0x74
#define MT9V022_DIGITAL_TEST_PATTERN 0x7f
#define MT9V022_AEC_AGC_ENABLE 0xAF
#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD
#define MT9V022_CAPTURE_MODE 0X20
/* Progressive scan, master, defaults */
#define MT9V022_CHIP_CONTROL_DEFAULT 0x188
extern int mt9v022_init(void);
extern void mt9v022_exit(void);
/* Read and Write from/to Image Pipeline Registers */
extern int reg_read(const u8 reg);
extern int reg_write(const u8 reg, const u16 data);
extern int reg_set(const u8 reg, const u16 data);
extern int reg_clear(const u8 reg, const u16 data);
> In general, for your real application, you should really consider doing
> something like what mplayer is doing - an own thread for video data
> read-out, preferably with a real-time priority.
Thanks maybe I will try.
Thanks
fengxin
--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request@xxxxxxxxxx?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list