Created
October 14, 2013 06:44
-
-
Save crouchggj/6971724 to your computer and use it in GitHub Desktop.
sep6200a v4l2 camera jpeg driver(inculde poll)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* | |
* SEUIC CAMERA&JPEG Module | |
* | |
*kernel/driver/jpeg/sep6500_jpeg.c | |
*init this version | |
*08-17-2012 jyb my_member@163.com | |
*06-03-2013 Guanguojin guanguojing1989@126.com | |
*10-14-2013 Guanguojin guanguojing1989@126.com | |
*/ | |
#include <linux/module.h> | |
#include <linux/errno.h> | |
#include <linux/kernel.h> | |
#include <linux/fs.h> | |
#include <linux/miscdevice.h> | |
#include <linux/cdev.h> | |
#include <linux/device.h> | |
#include <linux/init.h> | |
#include <linux/delay.h> | |
#include <linux/moduleparam.h> | |
#include <linux/platform_device.h> | |
#include <linux/init.h> | |
#include <linux/types.h> | |
#include <linux/interrupt.h> | |
#include <asm/uaccess.h> | |
#include <asm/io.h> | |
#include <mach/hardware.h> | |
#include <mach/gpio.h> | |
#include <mach/irqs.h> | |
#include <board/board.h> | |
#include <linux/dma-mapping.h> | |
#include <linux/spinlock.h> | |
#include <linux/videodev2.h> | |
#ifdef CONFIG_VIDEO_V4L1_COMPAT | |
#include <linux/videodev.h> | |
#endif | |
#include <media/v4l2-ioctl.h> | |
#include <media/v4l2-common.h> | |
#include <linux/sched.h> | |
#include <linux/vmalloc.h> | |
#include <linux/mm.h> | |
#include <linux/list.h> | |
#include <linux/slab.h> | |
#include <linux/jiffies.h> | |
#include <asm/uaccess.h> | |
#if defined(CONFIG_CAM_OV7725) | |
#include "ov7725.h" | |
#elif defined(CONFIG_CAM_GC0308) | |
#include "gc0308.h" | |
#endif | |
#include "sep6500_typedef.h" | |
#include "sep6500_jpeg.h" | |
#if defined(CONFIG_CAM_OV7725) | |
extern int ov7725_write_reg(unsigned int reg,unsigned int value); | |
#endif | |
static dma_addr_t dma_jpeg_buffer; | |
static u_char *map_jpeg_buffer,*use_map_addr; | |
static sepcamif_dev cam_dev; | |
static int video_nr = -1; | |
#define MAGIC_NUM (90) | |
#define BUFFER_OFFSET (19) //512 * 1024 | |
char tmp_buffers1[4] = {'0','0','0','0'}; | |
struct list_node{ | |
unsigned int offset; | |
unsigned long length; | |
}; | |
static struct list_node show_buffer = {MAGIC_NUM, 0}; | |
struct list_node empty_buffer_list[3]; | |
struct list_node ready_buffer_list[3]; | |
void Init_JpegBuffer_List(void) | |
{ | |
unsigned int i; | |
for(i=0; i<3; i++){ | |
empty_buffer_list[i].offset = i; | |
empty_buffer_list[i].length = 0; | |
ready_buffer_list[i].offset = MAGIC_NUM; //magic num | |
ready_buffer_list[i].length = 0; | |
} | |
} | |
int Get_EmptyBuffer(void) | |
{ | |
int nRet = -1; | |
int i; | |
for(i = 0; i < 3; i++) | |
{ | |
if(empty_buffer_list[i].offset != MAGIC_NUM) | |
{ | |
nRet = empty_buffer_list[i].offset; | |
empty_buffer_list[i].offset = MAGIC_NUM; | |
camera_trace("empty[%d]=%d\n",i,nRet); | |
break; | |
} | |
} | |
return nRet; | |
} | |
void Fill_ReadyBuffer(int offset,unsigned long length) | |
{ | |
int i; | |
for(i = 0;i < 3; i++) | |
{ | |
if(ready_buffer_list[i].offset == MAGIC_NUM) | |
{ | |
ready_buffer_list[i].offset = offset; | |
ready_buffer_list[i].length = length; | |
camera_trace("fill[%d]=%d\n",i,offset); | |
return; | |
} | |
} | |
} | |
void Rebuild_ReadyBuffer(void) | |
{ | |
int i; | |
for(i = 1; i < 3;i++) | |
{ | |
if(ready_buffer_list[i].offset == MAGIC_NUM){ | |
ready_buffer_list[i - 1].offset = MAGIC_NUM; | |
ready_buffer_list[i - 1].length = 0; | |
break; | |
}else{ | |
ready_buffer_list[i-1] = ready_buffer_list[i]; | |
} | |
} | |
} | |
void Get_ReadyBuffer(struct list_node *nRet) | |
{ | |
int i; | |
nRet->offset = MAGIC_NUM; | |
if(ready_buffer_list[0].offset != MAGIC_NUM ) | |
{ | |
//printk("%s, has ReadyBuffer \n",__func__); | |
nRet->offset = ready_buffer_list[0].offset; | |
nRet->length = ready_buffer_list[0].length; | |
camera_trace("ready[%d]=%d\n",0,nRet->offset); | |
Rebuild_ReadyBuffer(); | |
} | |
return ; | |
} | |
void Free_ReadyBuffer(struct list_node* ReadyBuffer) | |
{ | |
int i; | |
for(i = 0 ;i < 3;i++) | |
{ | |
if(empty_buffer_list[i].offset == MAGIC_NUM) | |
{ | |
camera_trace("free[%d]=%d\n",i,ReadyBuffer->offset); | |
empty_buffer_list[i].offset = ReadyBuffer->offset; | |
empty_buffer_list[i].length = 0; | |
ReadyBuffer->offset = MAGIC_NUM; | |
break; | |
} | |
} | |
return ; | |
} | |
static int sep6500_jpeg_open(struct inode *inode, struct file *file) | |
{ | |
int offset; | |
int LINE_PIXEL = 640; | |
int FRAME_PIXEL = 16; | |
printk("%s\n",__func__); | |
int Y1,U1,V1; | |
Init_JpegBuffer_List(); | |
show_buffer.offset = MAGIC_NUM; | |
write_reg(GPIO_PORTB_SEL_V, ((read_reg(GPIO_PORTB_SEL_V)) & 0x0)); | |
write_reg(GPIO_PORTB_2FUNC_V, ((read_reg(GPIO_PORTB_2FUNC_V)) | 0xffffffff)); | |
write_reg(CAM_EN_V , 0); //禁能 | |
write_reg(CAM_CLKDIV_V, CLOCK_DIV_RATE);//有时钟输出2分频//在这里camif时钟是15M | |
write_reg(CAM_INTC_V, 0x2c3);//camera中断设置 | |
write_reg(CAM_CON_V, 0xE5);//camera功能设置 | |
write_reg(RAM_MODE_V,RAM_MODE_ESRAM);//JPEG编码采用ESRAM加速模式 ESRAM模式,启动一次编16行数据直到一帧数据编码结束 | |
write_reg(Y_ADDR_BASE_1_V,ESRAM_BASE);//JPEG在ESRAM中YUV分量首地址0xb0000000 | |
write_reg(CAM_HAY1_V,ESRAM_BASE); //CAMIF在ESRAM中YUV分量首地址 0xb0000000 | |
Y1 = LINE_PIXEL * FRAME_PIXEL ; | |
U1 = LINE_PIXEL * FRAME_PIXEL / 4; | |
V1 = LINE_PIXEL * FRAME_PIXEL / 4; | |
write_reg(CAM_HAY2_V, (ESRAM_BASE + Y1) ); | |
write_reg(CAM_HAU1_V, (ESRAM_BASE + Y1 + Y1)); | |
write_reg(CAM_HAU2_V, (ESRAM_BASE + Y1 + Y1+ U1)); | |
write_reg(CAM_HAV1_V, (ESRAM_BASE + Y1 + Y1+ U1 + U1) ); | |
write_reg(CAM_HAV2_V, (ESRAM_BASE + Y1 + Y1+ U1 + U1+V1)); | |
offset = Get_EmptyBuffer(); | |
if(offset != -1) | |
write_reg(JPEG_EN_ADDR1_V,dma_jpeg_buffer + (offset << BUFFER_OFFSET) );//初始化JPEG编码后的首地址.offset is 1 | |
else{ | |
printk("Erro,has no Buffer!\n"); | |
return -1; | |
} | |
offset = Get_EmptyBuffer(); | |
if(offset != -1) | |
write_reg(JPEG_EN_ADDR2_V,((U32 )dma_jpeg_buffer+ (offset << BUFFER_OFFSET )));//初始化JPEG编码后的第二个首地址.offset is 2 | |
else{ | |
printk("Erro,has no Buffer!\n"); | |
return -1; | |
} | |
write_reg(ENCODE_RATE_V,RATIO);//配置压缩比,在sep6500中标准压缩比为0x2 | |
write_reg(MODE_SEL_V,0xd);//连续帧模式,编码输出数据帧Ping Pong存放 | |
write_reg(SOFT_ENABLE_V,JPEG_SOFT_EN);//使能JPEG MASTER模块,使能JPEG CODEC寄存器配置 | |
write_reg(Y_PARAM_V,PARAM_Y);//水平垂直采样系数配置Y、 U、 V | |
write_reg(U_PARAM_V,PARAM_U);//水平垂直采样系数配置Y、 U、 V | |
write_reg(V_PARAM_V,PARAM_V);//水平垂直采样系数配置Y、 U、 V | |
return 0; | |
} | |
static int sep6500_jpeg_release(struct inode *inode, struct file *file) | |
{ | |
printk("sep6500_jpeg_release\n"); | |
write_reg(CAM_EN_V,ENERGY);//CAMIF禁能工作 | |
write_reg(SOFT_ENABLE_V,ENERGY);//禁能JPEG MASTER模块,JPEGC CODEC复位 | |
write_reg(START_CODEC_V,ENERGY);//JPEG停止编码 | |
printk("sep6500_jpeg_close\n"); | |
return 0; | |
} | |
static int sep6500_jpeg_ioctl(struct file *file, unsigned int cmd, unsigned long *arg) | |
{ | |
int i; | |
switch (cmd) | |
{ | |
case VIDIOC_QUERYCAP://获得设备各个属性 | |
{ | |
break; | |
} | |
case VIDIOC_CROPCAP://查询驱动修剪能力 | |
{ | |
break; | |
} | |
case VIDIOC_S_CROP://设置视频信号的矩形边框 | |
{ | |
break; | |
} | |
case VIDIOC_ENUM_FMT://获取当前驱动支持的视频格式 | |
{ | |
break; | |
} | |
case VIDIOC_G_FMT://读取当前驱动的视频捕获格式 | |
{ | |
break; | |
} | |
case VIDIOC_S_FMT://设置当前驱动的视频捕获格式 | |
{ | |
int Y1,U1,V1,LINE_PIXEL,FRAME_PIXEL; | |
struct v4l2_format *fmt=kmalloc(sizeof(struct v4l2_format), GFP_KERNEL); | |
copy_from_user(fmt, arg, sizeof(struct v4l2_format)); | |
if((320*240) == ((fmt->fmt.pix.width)*(fmt->fmt.pix.height))) | |
{ | |
LINE_PIXEL = 320; | |
FRAME_PIXEL = 16; | |
write_reg(IMAGE_FORMAT_V,QVGA_LINE);//分别率240 JPEG头格式使能编码使能加入重启标志 | |
write_reg(NMCU_V,QVGA_NMCU);//MCU个数 | |
write_reg(PIXEL_PARAM_V,QVGA_LIST);//分别率320 | |
write_reg(CAM_HRES_V, LINE_PIXEL_QVGA);//设置列分辨率 | |
write_reg(CAM_VRES_V, 0x10);//设中行分辨 | |
write_reg(CAM_PIECE_NUM_V, 0x0f); | |
#if defined(CONFIG_CAM_OV7725) | |
ov7725_QVGA_Init(); | |
#endif | |
camera_trace("VIDIOC_S_FMT: QVGA(320*240)\n"); | |
} | |
else if((640*480) == ((fmt->fmt.pix.width)*(fmt->fmt.pix.height))) | |
{ | |
write_reg(IMAGE_FORMAT_V,VGA_LINE);//分别率480 JPEG头格式使能编码使能加入重启标志 | |
write_reg(NMCU_V,VGA_NMCU);//MCU个数 | |
write_reg(PIXEL_PARAM_V,VGA_LIST);//分别率640 | |
write_reg(CAM_HRES_V, LINE_PIXEL_VGA);//设置列分辨率 | |
write_reg(CAM_VRES_V, 0x10);//设中行分辨 | |
write_reg(CAM_PIECE_NUM_V, 0x1e); | |
#if defined(CONFIG_CAM_OV7725) | |
ov7725_VGA_Init(); | |
#elif defined(CONFIG_CAM_GC0308) | |
gc0308_VGA_Init(); | |
#endif | |
camera_trace("VIDIOC_S_FMT: VGA(640*480)\n"); | |
} | |
else if((176*144) == ((fmt->fmt.pix.width)*(fmt->fmt.pix.height))) | |
{ | |
write_reg(IMAGE_FORMAT_V,QCIF_LINE);//分别率176 JPEG头格式使能编码使能加入重启标志 | |
write_reg(NMCU_V,QCIF_NMCU);//MCU个数 | |
write_reg(PIXEL_PARAM_V,QCIF_LIST);//分别率144 | |
write_reg(CAM_HRES_V, LINE_PIXEL_QCIF);//设置列分辨率 | |
write_reg(CAM_VRES_V, LIST_PIXEL_QCIF);//设中行分辨 | |
#if defined(CONFIG_CAM_OV7725) | |
ov7725_QCIF_Init(); | |
#endif | |
camera_trace("VIDIOC_S_FMT: QCIF(176*144)\n"); | |
} | |
else | |
{ | |
write_reg(IMAGE_FORMAT_V,QVGA_LINE);//分别率240 JPEG头格式使能编码使能加入重启标志 | |
write_reg(NMCU_V,QVGA_NMCU);//MCU个数 | |
write_reg(PIXEL_PARAM_V,QVGA_LIST);//分别率320 | |
write_reg(CAM_HRES_V, LINE_PIXEL_QVGA);//设置列分辨率 | |
write_reg(CAM_VRES_V, LIST_PIXEL_QVGA);//设置行分辨 | |
#if defined(CONFIG_CAM_OV7725) | |
ov7725_QVGA_Init(); | |
printk("VIDIOC_S_FMT: Default QVGA(320*240)!\n"); | |
#elif defined(CONFIG_CAM_GC0308) | |
gc0308_VGA_Init(); | |
printk("VIDIOC_S_FMT: Default VGA(640*480)!\n"); | |
#endif | |
} | |
kfree(fmt); | |
break; | |
} | |
case VIDIOC_S_PARM://设置帧率 | |
{ | |
struct v4l2_streamparm *setfps=kmalloc(sizeof(struct v4l2_streamparm), GFP_KERNEL); | |
copy_from_user(setfps,arg,sizeof(struct v4l2_streamparm)); | |
if( 30 == setfps->parm.capture.timeperframe.denominator ) | |
{ | |
#if defined(CONFIG_CAM_OV7725) | |
ov7725_write_reg(0x33, 0x00);//30zhen 30M QVGA | |
//ov7725_write_reg(0x33, 0x80);//30zhen 30M QVGA_LINE | |
camera_trace("VIDIOC_S_PARM Max fps\n"); | |
#endif | |
} | |
if( 25 == setfps->parm.capture.timeperframe.denominator ) | |
{ | |
#if defined(CONFIG_CAM_OV7725) | |
ov7725_write_reg(0x33, 0x00);//20zhen 30M | |
ov7725_write_reg(0x34, 0x01);//25zhen 30M | |
camera_trace("VIDIOC_S_PARM Low fps\n"); | |
#endif | |
} | |
kfree(setfps); | |
break; | |
} | |
case VIDIOC_REQBUFS://分配内存 | |
{ | |
break; | |
} | |
case VIDIOC_QUERYBUF://把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址 | |
{ | |
break; | |
} | |
case VIDIOC_QBUF://放一个空的视频缓冲区到缓冲队列中,向缓冲区放一帧数据 | |
if(show_buffer.offset != MAGIC_NUM){ | |
Free_ReadyBuffer(&show_buffer); | |
} | |
Get_ReadyBuffer(&show_buffer); | |
if(show_buffer.offset != MAGIC_NUM) | |
{ | |
cam_dev.flag = 1; | |
wake_up_interruptible(&cam_dev.cmdqueue); | |
} | |
Restult: | |
break; | |
case VIDIOC_STREAMON://打开视频流,启动视频采集 | |
{ | |
write_reg(START_CODEC_V,JPEG_BM_EN);//JPEG 编码使能 | |
write_reg(CAM_EN_V,CAM_ENABLE); | |
camera_trace("VIDIOC_STREAMON\n"); | |
break; | |
} | |
case VIDIOC_DQBUF://获取一帧视频数据 | |
{ | |
if(copy_to_user((struct list_node *)arg,&show_buffer,sizeof(struct list_node))) | |
{ | |
printk("copy to user is erro buff!\n"); | |
return -EINVAL; | |
} | |
cam_dev.flag = 0; | |
break; | |
} | |
case VIDIOC_STREAMOFF://关闭视频流 | |
{ | |
write_reg(CAM_EN_V,ENERGY);//CAMIF禁能工作 | |
write_reg(START_CODEC_V,ENERGY);//JPEG停止编码 | |
camera_trace("VIDIOC_STREAMOFF\n"); | |
break; | |
} | |
default: | |
return -EINVAL; | |
} | |
return 0; | |
} | |
static irqreturn_t sep6500_jpeg_irqhandler(int irq , void *param) | |
{ | |
int buffer_index; | |
unsigned src_addr,offset; | |
unsigned long end_of_ready_addr; | |
unsigned long jpeg_int_status; | |
SEP0611_INT_DISABLE(INTSRC_JPEG); | |
jpeg_int_status = read_reg(INT_STATE_V); | |
write_reg(INT_STATE_V,((read_reg(INT_STATE_V)) ));//清除帧中断 | |
buffer_index = Get_EmptyBuffer(); | |
if(buffer_index == -1) | |
goto END; | |
end_of_ready_addr = read_reg(DATA_READY_V); | |
if(0x42 == jpeg_int_status) | |
{ | |
src_addr = read_reg(JPEG_EN_ADDR1_V); | |
if(src_addr == 0){ | |
goto END; | |
} | |
write_reg(JPEG_EN_ADDR1_V,dma_jpeg_buffer + (buffer_index << BUFFER_OFFSET) ); | |
} | |
else | |
{ | |
src_addr = read_reg(JPEG_EN_ADDR2_V); | |
if(src_addr == 0){ | |
goto END; | |
} | |
write_reg(JPEG_EN_ADDR2_V,dma_jpeg_buffer + (buffer_index << BUFFER_OFFSET) ); | |
} | |
offset = (src_addr - dma_jpeg_buffer) >> BUFFER_OFFSET; | |
if(offset == show_buffer.offset) | |
printk("===> show_buffer Bug show!\n"); | |
if(offset == buffer_index) | |
printk("===> fill buffer Bug show!\n"); | |
int length = end_of_ready_addr - src_addr; | |
*((char *) end_of_ready_addr + 10) = 'L'; | |
*((char *) end_of_ready_addr + 11) = length&0xff; | |
*((char *) end_of_ready_addr + 12) = (length >> 8)&0xff; | |
*((char *) end_of_ready_addr + 13) = (length >> 16)&0xff; | |
*((char *) end_of_ready_addr + 14) = (length >> 24 )&0xff; | |
Fill_ReadyBuffer(offset,(end_of_ready_addr - src_addr + 15)); | |
END: | |
SEP0611_INT_ENABLE(INTSRC_JPEG); | |
return IRQ_HANDLED; | |
} | |
/********************************************************************************************/ | |
static int sep6500_mmap(struct file *file, struct vm_area_struct *vma) | |
{ | |
unsigned long startt, size; | |
int ret = 0; | |
startt =(unsigned long) vma->vm_start; | |
size =(unsigned long) (vma->vm_end - vma->vm_start); | |
if(remap_pfn_range(vma,startt,dma_jpeg_buffer >> PAGE_SHIFT,size,PAGE_SHARED)) | |
{ | |
printk("SEP6500 JPEG MEM Mmap Failed \n"); | |
return -1; | |
} | |
return ret; | |
} | |
static int sepcamif_s_fmt_vid_cap(struct file *file, void *priv,struct v4l2_format *f) | |
{ | |
struct video_device *dev = video_devdata(file); | |
int ret=0; | |
printk("!!! %s \n",__func__); | |
//TODO: start capture | |
return ret; | |
} | |
/*----------------------------------------------------------------------------- | |
* | |
* poll function | |
* return status POLLIN | POLLRDNORM | |
* | |
*-----------------------------------------------------------------------------*/ | |
static unsigned int sep6500_poll(struct file *file, poll_table *wait) | |
{ | |
int mask = 0; | |
struct video_device *dev = video_devdata(file); | |
sepcamif_dev *pcam = video_get_drvdata(dev); | |
poll_wait(file, &cam_dev.cmdqueue, wait); | |
if(pcam->flag == 1) | |
mask=POLLIN|POLLRDNORM; | |
else | |
mask = POLLRDNORM; | |
return mask; | |
} | |
/*******************************************************************************************/ | |
static struct v4l2_file_operations sepcamif_fops ={ | |
.owner = THIS_MODULE, | |
.open = sep6500_jpeg_open, | |
.release = sep6500_jpeg_release, | |
.ioctl = sep6500_jpeg_ioctl, | |
/*.ioctl = video_ioctl2,*/ | |
.mmap = sep6500_mmap, | |
.poll = sep6500_poll, | |
}; | |
static const struct v4l2_ioctl_ops sepcamif_ioctl_ops = { | |
/*.vidioc_querycap = s3c2410camif_querycap,*/ | |
/*.vidioc_enum_fmt_vid_cap = s3c2410camif_enum_fmt_vid_cap,*/ | |
/*.vidioc_g_fmt_vid_cap = s3c2410camif_g_fmt_vid_cap,*/ | |
.vidioc_s_fmt_vid_cap = sepcamif_s_fmt_vid_cap, | |
/*.vidioc_g_ctrl = s3c2410camif_vidioc_g_ctrl,*/ | |
/*.vidioc_s_ctrl = s3c2410camif_vidioc_s_ctrl, */ | |
/*.vidioc_enum_input = s3c2410camif_vidioc_enum_input,*/ | |
/*.vidioc_queryctrl = s3c2410camif_vidioc_queryctrl,*/ | |
/*//Buffer handlers*/ | |
/*.vidioc_reqbufs = s3c2410camif_vidioc_reqbufs,*/ | |
/*.vidioc_querybuf = s3c2410camif_vidioc_querybuf,*/ | |
/*.vidioc_qbuf = s3c2410camif_vidioc_qbuf,*/ | |
/*.vidioc_dqbuf = s3c2410camif_vidioc_dqbuf,*/ | |
/*//Stream on/off*/ | |
/*.vidioc_streamon = s3c2410camif_vidioc_streamon,*/ | |
/*.vidioc_streamoff = s3c2410camif_vidioc_streamoff,*/ | |
}; | |
static struct video_device sep6200a_v4l_template = { | |
.name = "sep6200a Camera Interface", | |
.fops = &sepcamif_fops, | |
/*.ioctl_ops = & sepcamif_ioctl_ops,*/ | |
.release = video_device_release, | |
}; | |
static void camera_platform_release(struct device *device) | |
{ | |
} | |
static struct platform_device sepcamif_v4l2_devices = { | |
.name = "sepcamif_v4l2", | |
.dev = { | |
.release = camera_platform_release, | |
}, | |
.id = 0, | |
}; | |
/* | |
* This structure contains pointers to the power management callback functions. | |
*/ | |
static struct platform_driver sepcamif_v4l2_driver = { | |
.driver = { | |
.name = "s3c2440camif Platform Driver", | |
}, | |
.probe = NULL, | |
.remove = NULL, | |
.suspend = NULL, | |
.resume = NULL, | |
.shutdown = NULL, | |
}; | |
static int init_sepcamif_struct(sepcamif_dev *cam) | |
{ | |
cam->video_dev = video_device_alloc(); | |
if(cam->video_dev == NULL) | |
return -ENOMEM; | |
*(cam->video_dev) = sep6200a_v4l_template; | |
video_set_drvdata(cam->video_dev, cam); | |
dev_set_drvdata(&sepcamif_v4l2_devices.dev, (void *)cam); | |
cam->video_dev->minor = -1; | |
init_waitqueue_head(&cam->cmdqueue); | |
cam->flag = 0; | |
return 0; | |
} | |
static int __init sep6500_jpeg_init(void) | |
{ | |
int ret = 0; | |
int err; | |
printk("=======================================\n"); | |
printk("SEP0611C Camera MJPEG Driver v2.0\n"); | |
sepcamif_dev *cam; | |
cam = &cam_dev; | |
if((ret = init_sepcamif_struct(cam)) < 0 ) | |
return ret; | |
/*----------------------------------------------------------------------------- | |
* register I2C device | |
*-----------------------------------------------------------------------------*/ | |
err = platform_device_register(&sepcamif_v4l2_devices); | |
if (err != 0) { | |
printk("camif_init: platform_device_register failed.\n"); | |
platform_driver_unregister(&sepcamif_v4l2_driver); | |
return err; | |
} | |
/*----------------------------------------------------------------------------- | |
* register the device driver structure | |
*-----------------------------------------------------------------------------*/ | |
err = platform_driver_register(&sepcamif_v4l2_driver); | |
if (err != 0) { | |
platform_device_unregister(&sepcamif_v4l2_devices); | |
printk("camif_init: driver_register failed.\n"); | |
video_device_release(cam->video_dev); | |
return err; | |
} | |
/*----------------------------------------------------------------------------- | |
* register v4l device | |
*-----------------------------------------------------------------------------*/ | |
if (video_register_device(cam->video_dev, VFL_TYPE_GRABBER, video_nr) == -1) | |
{ | |
platform_driver_unregister(&sepcamif_v4l2_driver); | |
platform_device_unregister(&sepcamif_v4l2_devices); | |
video_device_release(cam->video_dev); | |
printk("video_register_device failed\n"); | |
return -1; | |
} | |
if(request_irq(INTSRC_JPEG, sep6500_jpeg_irqhandler, IRQF_ONESHOT, "sep6200a_jpeg", NULL)){ | |
printk("failed to request INTSRC_JPEG IRQ,IRQF_IRQPOLL,IRQF_ONESHOT,IRQF_SHARED %d!\n",INTSRC_JPEG ); | |
return -1; | |
} | |
SEP0611_INT_ENABLE(INTSRC_JPEG); | |
map_jpeg_buffer=dma_alloc_writecombine(NULL, 3 * 512 *1024, &dma_jpeg_buffer, GFP_KERNEL); | |
camera_trace("Init Camera MCLK \n"); | |
write_reg(CAM_CLKDIV_V,CLOCK_DIV_RATE);//有时钟输出2分频 | |
printk("===== SEP0611C Camera MJPEG Init Success\n"); | |
printk("=======================================\n"); | |
return ret; | |
} | |
static void __exit sep6500_jpeg_exit(void) | |
{ | |
sepcamif_dev *pcam=&cam_dev; | |
video_unregister_device(pcam->video_dev); | |
platform_driver_unregister(&sepcamif_v4l2_driver); | |
platform_device_unregister(&sepcamif_v4l2_devices); | |
printk("Exit jpeg!\n"); | |
} | |
module_init(sep6500_jpeg_init); | |
module_exit(sep6500_jpeg_exit); | |
MODULE_AUTHOR("Guanguojin"); | |
MODULE_DESCRIPTION("Sep6200A Camera MJPEG Driver"); | |
MODULE_LICENSE("GPL"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment