Skip to content

Instantly share code, notes, and snippets.

@crouchggj
Created October 14, 2013 06:44
Show Gist options
  • Save crouchggj/6971724 to your computer and use it in GitHub Desktop.
Save crouchggj/6971724 to your computer and use it in GitHub Desktop.
sep6200a v4l2 camera jpeg driver(inculde poll)
/*
*
* 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