• 摄像头驱动学习


    \drivers\media\video\s3c2440camif.c

    \drivers\media\video\s3c2440_ov9650.c

    \drivers\media\video\sccb.c

    硬件连接

    I2C总线连接

    clip_image002clip_image004

    I2CSCL ——GPE14

    I2CSDA——GPE15

    Sccb.h

    #define SIO_C S3C2410_GPE14

    #define SIO_D S3C2410_GPE15

    #define State(x) s3c2410_gpio_getpin(x)

    #define High(x) do{s3c2410_gpio_setpin(x,1); smp_mb();}while(0)

    #define Low(x) do{s3c2410_gpio_setpin(x,0); smp_mb();}while(0)

    #define WAIT_STABLE() do{udelay(10);}while(0)

    #define WAIT_CYCLE() do{udelay(90);}while(0)

    #define CFG_READ(x) do{s3c2410_gpio_cfgpin(x,S3C2410_GPIO_INPUT);smp_mb();}while(0)

    #define CFG_WRITE(x) do{s3c2410_gpio_cfgpin(x,S3C2410_GPIO_OUTPUT);smp_mb();}while(0)

    void sccb_write(u8 IdAddr, u8 SubAddr, u8 data);

    u8 sccb_read(u8 IdAddr, u8 SubAddr);

    CFG_WRITE(x)把GPIO设置为输出

    CFG_READ(x)把GPIO设置为输入

    High(x) 把GPIO设置为高电平

    Low(x) 把GPIO设置为低电平

    Sccb.c

    clip_image006

    把SCL和Data都拉高,即为默认初始化状态电平

    int sccb_init(void)

    {

    CFG_WRITE(SIO_C);

    CFG_WRITE(SIO_D);

    High(SIO_C);

    High(SIO_D);

    WAIT_STABLE();

    return 0;

    }

    由上面的图可以看出,CLK高电平时,DATA拉低,即为START

    static void __inline__ sccb_start(void)

    {

    CFG_WRITE(SIO_D);

    Low(SIO_D);

    WAIT_STABLE();

    }

    使用到一个信号量

    static DECLARE_MUTEX(bus_lock);

    void sccb_write(u8 IdAddr, u8 SubAddr, u8 data)

    {

    down(&bus_lock);

    sccb_start();

    sccb_write_byte(IdAddr);

    sccb_write_byte(SubAddr);

    sccb_write_byte(data);

    sccb_stop();

    up (&bus_lock);

    }

    首先把信号量数值降低,表示自己使用,如果此时信号量不大于0,表示bus正在使用,驱动会在此等待,知道信号量大于0,然后,将其减1,此时bus对外界不可用。最后再把信号量加1,表示bus可用。

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    摄像头接口初始化:

    /*

    * camif_init()

    */

    static int __init camif_init(void)

    {

    配置CAMIF的GPIO接口功能

    /* set gpio-j to camera mode. */

    s3c2410_gpio_cfgpin(S3C2440_GPJ0, S3C2440_GPJ0_CAMDATA0);

    s3c2410_gpio_cfgpin(S3C2440_GPJ1, S3C2440_GPJ1_CAMDATA1);

    s3c2410_gpio_cfgpin(S3C2440_GPJ2, S3C2440_GPJ2_CAMDATA2);

    s3c2410_gpio_cfgpin(S3C2440_GPJ3, S3C2440_GPJ3_CAMDATA3);

    s3c2410_gpio_cfgpin(S3C2440_GPJ4, S3C2440_GPJ4_CAMDATA4);

    s3c2410_gpio_cfgpin(S3C2440_GPJ5, S3C2440_GPJ5_CAMDATA5);

    s3c2410_gpio_cfgpin(S3C2440_GPJ6, S3C2440_GPJ6_CAMDATA6);

    s3c2410_gpio_cfgpin(S3C2440_GPJ7, S3C2440_GPJ7_CAMDATA7);

    s3c2410_gpio_cfgpin(S3C2440_GPJ8, S3C2440_GPJ8_CAMPCLK);

    s3c2410_gpio_cfgpin(S3C2440_GPJ9, S3C2440_GPJ9_CAMVSYNC);

    s3c2410_gpio_cfgpin(S3C2440_GPJ10, S3C2440_GPJ10_CAMHREF);

    s3c2410_gpio_cfgpin(S3C2440_GPJ11, S3C2440_GPJ11_CAMCLKOUT);

    s3c2410_gpio_cfgpin(S3C2440_GPJ12, S3C2440_GPJ12_CAMRESET);

    申请CAMIF的寄存器区域,由于OS的系统统一管理,所以,需要确定,该寄存器区域没有被别的进程获取,所以要申请。

    /* init camera's virtual memory. */

    if (!request_mem_region((unsigned long)S3C2440_PA_CAMIF, S3C2440_SZ_CAMIF, CARD_NAME))

    {

    ret = -EBUSY;

    goto error1;

    }

    申请到了寄存器区域,相当于物理地址可用,然后,再将该物理地址映射到虚拟地址。

    /* remap the virtual memory. */

    camif_base_addr = (unsigned long)ioremap_nocache((unsigned long)S3C2440_PA_CAMIF, S3C2440_SZ_CAMIF);

    if (camif_base_addr == (unsigned long)NULL)

    {

    ret = -EBUSY;

    goto error2;

    }

    获取CAMIF的时钟,使用24M

    /* init camera clock. */

    pdev->clk = clk_get(NULL, "camif");

    if (IS_ERR(pdev->clk))

    {

    ret = -ENOENT;

    goto error3;

    }

    clk_enable(pdev->clk);

    camif_upll_clk = clk_get(NULL, "camif-upll");

    clk_set_rate(camif_upll_clk, 24000000);

    mdelay(100);

    初始化CAMIF的状态

    /* init camif state and its lock. */

    pdev->state = CAMIF_STATE_FREE;

    CAMIF DEV的状态:

    /* for s3c2440camif_dev->state field. */

    enum

    {

    CAMIF_STATE_FREE = 0, // not openned

    CAMIF_STATE_READY = 1, // openned, but standby

    CAMIF_STATE_PREVIEWING = 2, // in previewing

    CAMIF_STATE_CODECING = 3 // in capturing

    };

    注册杂项设备:

    /* register to videodev layer. */

    if (misc_register(&misc) < 0)

    {

    ret = -EBUSY;

    goto error4;

    }

    printk(KERN_ALERT"s3c2440 camif init done\n");

    初始化SCCB接口,Serial Camera Control Bus,其实是I2C接口

    sccb_init();

    硬件复位CAMIF

    hw_reset_camif();

    其实是软件复位,对CIGCTRL寄存器配置软件复位

    /* software reset camera interface. */

    static void __inline__ hw_reset_camif(void)

    {

    u32 cigctrl;

    cigctrl = (1<<30)|(1<<29);

    iowrite32(cigctrl, S3C244X_CIGCTRL);

    mdelay(10);

    cigctrl = (1<<29);

    iowrite32(cigctrl, S3C244X_CIGCTRL);

    mdelay(10);

    }

    clip_image008

    检测是否存在OV9650,GPG4连的是LCD_PWR,不知道为什么?

    has_ov9650 = s3c2440_ov9650_init() >= 0;

    s3c2410_gpio_setpin(S3C2410_GPG4, 1);

    clip_image010

    OV9650的初始化:

    int s3c2440_ov9650_init(void)

    {

    printk(KERN_ALERT"Loading OV9650 driver.........\n");

    /* power on. */

    ov9650_poweron();

    mdelay(100);

    /* check device. */

    if (ov9650_check() == 0 && ov9650_check() == 0)

    {

    printk(KERN_ERR"No OV9650 found!!!\n");

    return -ENODEV;

    }

    show_ov9650_product_id();

    ov9650_init_regs();

    printk("ov9650 init done!\n");

    return 0;

    }

    OV9650上电,这里对GPG12设置为输出,这里使用的虽然是中断引脚,但似乎没有用中断,这是一个电源控制引脚,PWDN0power on1power down

    static void __inline__ ov9650_poweron(void)

    {

    s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_OUTP);

    s3c2410_gpio_setpin(S3C2410_GPG12, 0);

    mdelay(20);

    }

    clip_image012

    clip_image014

    OV9650检测,读取OV9650Manu ID

    static int __inline__ ov9650_check(void)

    {

    u32 mid;

    mid = sccb_read(OV9650_SCCB_ADDR, 0x1c)<<8;

    mid |= sccb_read(OV9650_SCCB_ADDR, 0x1d);

    printk("SCCB address 0x%02X, manufacture ID 0x%04X, expect 0x%04X\n", OV9650_SCCB_ADDR, mid, OV9650_MANUFACT_ID);

    return (mid==OV9650_MANUFACT_ID)?1:0;

    }

    #define OV9650_SCCB_ADDR 0x60

    #define OV9650_MANUFACT_ID 0x7FA2

    #define OV9650_PRODUCT_ID 0x9650

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    摄像头接口打开:

    /*

    * camif_open()

    */

    static int camif_open(struct inode *inode, struct file *file)

    {

    首先判断是否存在OV9650 camera是文件开始的一个static变量。

    struct s3c2440camif_dev *pdev;

    struct s3c2440camif_fh *fh;

    int ret;

    if (!has_ov9650) {

    return -ENODEV;

    }

    pdev = &camera;

    为file handle分配内存

    fh = kzalloc(sizeof(*fh),GFP_KERNEL); // alloc memory for filehandle

    设置设备状态为open and standby,初始化CAMIF的配置。

    pdev->state = CAMIF_STATE_READY;

    init_camif_config(fh);

    对CAMIF进行配置,最后更新一下配置

    /* config camif when master-open camera.*/

    static void init_camif_config(struct s3c2440camif_fh * fh)

    {

    struct s3c2440camif_dev * pdev;

    pdev = fh->dev;

    pdev->input = 0; // FIXME, the default input image format, see inputs[] for detail.

    /* the source image size (input from external camera). */

    pdev->srcHsize = 1280; // FIXME, the OV9650's horizontal output pixels.

    pdev->srcVsize = 1024; // FIXME, the OV9650's verical output pixels.

    /* the windowed image size. */

    pdev->wndHsize = 1280;

    pdev->wndVsize = 1024;

    /* codec-path target(output) image size. */

    pdev->coTargetHsize = pdev->wndHsize;

    pdev->coTargetVsize = pdev->wndVsize;

    /* preview-path target(preview) image size. */

    pdev->preTargetHsize = 640;

    pdev->preTargetVsize = 512;

    update_camif_config(fh, CAMIF_CMD_STOP);

    }

    由于设备状态是CAMIF_STATE_READY所以,直接更新寄存器。

    /* update camera interface with the new config. */

    static void update_camif_config (struct s3c2440camif_fh * fh, u32 cmdcode)

    {

    struct s3c2440camif_dev * pdev;

    pdev = fh->dev;

    switch (pdev->state)

    {

    case CAMIF_STATE_READY:

    update_camif_regs(fh->dev); // config the regs directly.

    break;

    case CAMIF_STATE_PREVIEWING:

    /* camif is previewing image. */

    disable_irq(IRQ_S3C2440_CAM_P); // disable cam-preview irq.

    /* source image format. */

    if (cmdcode & CAMIF_CMD_SFMT)

    {

    // ignore it, nothing to do now.

    }

    /* target image format. */

    if (cmdcode & CAMIF_CMD_TFMT)

    {

    /* change target image format only. */

    pdev->cmdcode |= CAMIF_CMD_TFMT;

    }

    不知为什么要等待VSYNCL?然后对寄存器进行配置。

    /* update camif registers, called only when camif ready, or ISR. */

    static void __inline__ update_camif_regs(struct s3c2440camif_dev * pdev)

    {

    if (!in_irq())

    {

    while(1) // wait until VSYNC is 'L'

    {

    barrier();

    if ((ioread32(S3C244X_CICOSTATUS)&(1<<28)) == 0)

    break;

    }

    }

    /* WARNING: don't change the statement sort below!!! */

    update_source_fmt_regs(pdev);

    update_target_wnd_regs(pdev);

    update_target_fmt_regs(pdev);

    update_target_zoom_regs(pdev);

    }

    初始化CAMIF的DMA内存。

    ret = init_image_buffer(); // init image buffer.

    这里为DMA分配内存,按页分配,内存管理的知识需要学习?

    /* init image buffer (only when the camif is first open). */

    static int __inline__ init_image_buffer(void)

    {

    int size1, size2;

    unsigned long size;

    unsigned int order;

    /* size1 is the max image size of codec path. */

    size1 = MAX_C_WIDTH * MAX_C_HEIGHT * 16 / 8;

    /* size2 is the max image size of preview path. */

    size2 = MAX_P_WIDTH * MAX_P_HEIGHT * 16 / 8;

    size = (size1 > size2)?size1:size2;

    order = get_order(size); //获取需要分配字节的2的阶数,内存按页分配

    img_buff[0].order = order;

    img_buff[0].virt_base = __get_free_pages(GFP_KERNEL|GFP_DMA, img_buff[0].order);

    if (img_buff[0].virt_base == (unsigned long)NULL)

    {

    goto error0;

    }

    img_buff[0].phy_base = img_buff[0].virt_base - PAGE_OFFSET + PHYS_OFFSET; // the DMA address.

    申请中断,分别为Codec的中断和Preview的中断

    request_irq(IRQ_S3C2440_CAM_C, on_camif_irq_c, IRQF_DISABLED, "CAM_C", pdev);

    request_irq(IRQ_S3C2440_CAM_P, on_camif_irq_p, IRQF_DISABLED, "CAM_P", pdev);

    使能时钟,软件复位,更新CAMIF的配置。

    clk_enable(pdev->clk); // and enable camif clock.

    soft_reset_camif();

    file->private_data = fh;

    fh->dev = pdev;

    update_camif_config(fh, 0);

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    摄像头接口读取函数:

    static ssize_t camif_read(struct file *file, char __user *data, size_t count, loff_t *ppos)

    {

    int i;

    struct s3c2440camif_fh * fh;

    struct s3c2440camif_dev * pdev;

    fh = file->private_data;

    pdev = fh->dev;

    if (start_capture(pdev, 0) != 0) //此处是捕获一张图片,所以会阻塞在此,直至中断发生。

    {

    return -ERESTARTSYS;

    }

    //中断已经发生,数据已经更新。

    disable_irq(IRQ_S3C2440_CAM_C);

    disable_irq(IRQ_S3C2440_CAM_P);

    for (i = 0; i < 4; i++)

    {

    if (img_buff[i].state != CAMIF_BUFF_INVALID) //如果数据已经更新,移动数据

    {

    copy_to_user(data, (void *)img_buff[i].virt_base, count);

    img_buff[i].state = CAMIF_BUFF_INVALID; //设置数据无效

    }

    }

    enable_irq(IRQ_S3C2440_CAM_P); //重新使能中断

    enable_irq(IRQ_S3C2440_CAM_C);

    return count;

    }

    开始捕获函数,设置window offset

    /* start image capture.

    *

    * param 'stream' means capture pictures streamly or capture only one picture.

    */

    static int start_capture(struct s3c2440camif_dev * pdev, int stream)

    {

    int ret;

    u32 ciwdofst;

    u32 ciprscctrl;

    u32 ciimgcpt;

    ciwdofst = ioread32(S3C244X_CIWDOFST); //window offset

    ciwdofst |= (1<<30) // Clear the overflow indication flag of input CODEC FIFO Y

    |(1<<15) // Clear the overflow indication flag of input CODEC FIFO Cb

    |(1<<14) // Clear the overflow indication flag of input CODEC FIFO Cr

    |(1<<13) // Clear the overflow indication flag of input PREVIEW FIFO Cb

    |(1<<12); // Clear the overflow indication flag of input PREVIEW FIFO Cr

    iowrite32(ciwdofst, S3C244X_CIWDOFST);

    ciprscctrl = ioread32(S3C244X_CIPRSCCTRL); //Preview main scale control开始prev

    ciprscctrl |= 1<<15; // preview scaler start

    iowrite32(ciprscctrl, S3C244X_CIPRSCCTRL);

    pdev->state = CAMIF_STATE_PREVIEWING;

    ciimgcpt = (1<<31) // camera interface global capture enable

    |(1<<29); // capture enable for preview scaler.

    iowrite32(ciimgcpt, S3C244X_CIIMGCPT); //Image capture enable开始捕获

    //如果是捕获视频的话,会一直循环这样,只是数据到什么地方了呢?

    ret = 0;

    if (stream == 0)

    {

    pdev->cmdcode = CAMIF_CMD_STOP;

    //如果是捕获图片的话,那么只会中断一次,然后STOP,在此阻塞,等待中断发生

    ret = wait_event_interruptible(pdev->cmdqueue, pdev->cmdcode == CAMIF_CMD_NONE);

    }

    return ret;

    }

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    摄像头接口中断服务P path函数:

    /*

    * ISR: service for P-path interrupt.

    */

    static irqreturn_t on_camif_irq_p(int irq, void * dev)

    {

    u32 ciprstatus;

    u32 frame;

    struct s3c2440camif_dev * pdev;

    ciprstatus = ioread32(S3C244X_CIPRSTATUS); 检查状态

    if ((ciprstatus & (1<<21))== 0)

    {

    return IRQ_RETVAL(IRQ_NONE);

    }

    pdev = (struct s3c2440camif_dev *)dev;

    /* valid img_buff[x] just DMAed. */

    frame = (ciprstatus&(3<<26))>>26; 计算帧数?

    frame = (frame+4-1)%4;

    img_buff[frame].state = CAMIF_BUFF_RGB565;

    if (pdev->cmdcode & CAMIF_CMD_STOP) 查看有没有CMD

    {

    stop_capture(pdev);

    pdev->state = CAMIF_STATE_READY;

    }

    else

    {

    if (pdev->cmdcode & CAMIF_CMD_P2C)

    {

    camif_c2p(pdev);

    }

    if (pdev->cmdcode & CAMIF_CMD_WND)

    {

    update_target_wnd_regs(pdev);

    }

    if (pdev->cmdcode & CAMIF_CMD_TFMT)

    {

    update_target_fmt_regs(pdev);

    }

    if (pdev->cmdcode & CAMIF_CMD_ZOOM)

    {

    update_target_zoom_regs(pdev);

    }

    invalid_image_buffer();

    }

    pdev->cmdcode = CAMIF_CMD_NONE; 最后将CMD置为NONE,然后唤醒Read中的queue

    wake_up(&pdev->cmdqueue);

    return IRQ_RETVAL(IRQ_HANDLED);

    }

    clip_image016

    配置目标格式寄存器

    /* update registers:

    * PREVIEW path:

    * CIPRCLRSA1 ~ CIPRCLRSA4

    * CIPRTRGFMT

    * CIPRCTRL

    * CIPRSCCTRL

    * CIPRTAREA

    * CODEC path:

    * CICOYSA1 ~ CICOYSA4

    * CICOCBSA1 ~ CICOCBSA4

    * CICOCRSA1 ~ CICOCRSA4

    * CICOTRGFMT

    * CICOCTRL

    * CICOTAREA

    */

    static void __inline__ update_target_fmt_regs(struct s3c2440camif_dev * pdev)

    {

    u32 ciprtrgfmt;

    u32 ciprctrl;

    u32 ciprscctrl;

    u32 mainBurstSize, remainedBurstSize;

    /* CIPRCLRSA1 ~ CIPRCLRSA4. */

    iowrite32(img_buff[0].phy_base, S3C244X_CIPRCLRSA1); DMA的地址

    iowrite32(img_buff[1].phy_base, S3C244X_CIPRCLRSA2);

    iowrite32(img_buff[2].phy_base, S3C244X_CIPRCLRSA3);

    iowrite32(img_buff[3].phy_base, S3C244X_CIPRCLRSA4);

    /* CIPRTRGFMT. */

    ciprtrgfmt = (pdev->preTargetHsize<<16) // horizontal pixel number of target image

    |(0<<14) // don't mirror or rotation.

    |(pdev->preTargetVsize<<0); // vertical pixel number of target image

    iowrite32(ciprtrgfmt, S3C244X_CIPRTRGFMT);

    /* CIPRCTRL. */

    calc_burst_size(2, pdev->preTargetHsize, &mainBurstSize, &remainedBurstSize);

    ciprctrl = (mainBurstSize<<19)|(remainedBurstSize<<14);

    iowrite32(ciprctrl, S3C244X_CIPRCTRL);

    /* CIPRSCCTRL. */

    ciprscctrl = ioread32(S3C244X_CIPRSCCTRL);

    ciprscctrl &= 1<<15; // clear all other info except 'preview scaler start'.

    ciprscctrl |= 0<<30; // 16-bits RGB

    iowrite32(ciprscctrl, S3C244X_CIPRSCCTRL); // 16-bit RGB

    /* CIPRTAREA. */

    iowrite32(pdev->preTargetHsize * pdev->preTargetVsize, S3C244X_CIPRTAREA);

    }

  • 相关阅读:
    网站前台性能优化教程
    解决Jboss打开run.bat时闪退不能启动的方法
    如何讲解自己开发的程序
    数据库调优教程汇总
    数据库调优教程(十三) MySQL数据库其他优化方法
    数据库调优教程(十二) 优化sql语句
    数据库调优教程(十一) 设计一张漂亮的表
    数据库调优教程(十) 【精华章节】解决like ’%str’ 时索引不被使用的4种方法
    数据库调优教程(九) 添加了索引但不被使用的几种常见可能
    Redis Cluster 实践
  • 原文地址:https://www.cnblogs.com/yanhc/p/2175224.html
Copyright © 2020-2023  润新知