单片机
返回首页

s5pv210 fimc 之 fimc-dev.c

2024-12-16 来源:cnblogs

fimc-dev.c 是Samsung FIMC 设备的V4L2 驱动。上层应用直接操作这个设备,进行capture,图片处理,以及overlay输出


http://blog.csdn.net/cxw3506/article/details/8476263


43 int fimc_dma_alloc(struct fimc_control *ctrl, struct fimc_buf_set *bs,  

 44                             int i, int align)  

 45 {  

 46     dma_addr_t end, *curr;  

 47  

 48     mutex_lock(&ctrl->alloc_lock);  

 49  

 50     end = ctrl->mem.base + ctrl->mem.size;  

 51     curr = &ctrl->mem.curr;  

 52  

 53     if (!bs->length[i])  

 54         return -EINVAL;  

 55  

 56     if (!align) {  

 57         if (*curr + bs->length[i] > end) {  

 58             goto overflow;  

 59         } else {  

 60             bs->base[i] = *curr;  

 61             bs->garbage[i] = 0;  

 62             *curr += bs->length[i];  

 63         }  

 64     } else {  

 65         if (ALIGN(*curr, align) + bs->length[i] > end)  

 66             goto overflow;  

 67         else {  

 68             bs->base[i] = ALIGN(*curr, align);  

 69             bs->garbage[i] = ALIGN(*curr, align) - *curr;  

 70             *curr += (bs->length[i] + bs->garbage[i]);  

 71         }  

 72     }  

 73  

 74     mutex_unlock(&ctrl->alloc_lock);  

 75  

 76     return 0;  

 77  

 78 overflow:  

 79     bs->base[i] = 0;  

 80     bs->length[i] = 0;  

 81     bs->garbage[i] = 0;  

 82  

 83     mutex_unlock(&ctrl->alloc_lock);  

 84  

 85     return -ENOMEM;  

 86 }  

这个函数很简单,之所以提出来说下,是因为我在DMA对齐问题上卡了一个多星期


FIMC使用预分配的物理内存来申请DMA buffer,参数中的align指明申请buffer的对齐方式,对于FIMC capture来说,似乎output DMA要求4k对齐(尽管我没有在datasheet中找到),如果给定的DMA地址没有4K对齐,FIMC DMA控制器会很聪明的从4K对齐的地址开始传送数据,这会导致了帧数据偏移。


@i 参数指定了plane数,FIMC 输出支持很多种格式,有单层的比如YUYV,两层的V4L2_PIX_FMT_NV12,还有三层的V4L2_PIX_FMT_NV12T


单层格式输出申请一个buffer,两层格式输出申请两个buffer,三层则需申请三个buffer。


 


 88 void fimc_dma_free(struct fimc_control *ctrl, struct fimc_buf_set *bs, int i)  

 89 {  

 90     int total = bs->length[i] + bs->garbage[i];  

 91     mutex_lock(&ctrl->alloc_lock);  

 92   

 93     if (bs->base[i]) {  

 94         if (ctrl->mem.curr - total >= ctrl->mem.base)  

 95             ctrl->mem.curr -= total;  

 96   

 97         bs->base[i] = 0;  

 98         bs->length[i] = 0;  

 99         bs->garbage[i] = 0;  

100     }  

101   

102     mutex_unlock(&ctrl->alloc_lock);  

103 }  


这个函数有问题,93 ~ 95 成立的条件是bs->base[i]所占的地址必须在ctrl->mem.base最后面,这就要求释放顺序必须从bs的最后一个节点向前释放。


 


655 static inline int fimc_mmap_cap(struct file *filp, struct vm_area_struct *vma)  

656 {  

657     struct fimc_prv_data *prv_data =  

658                 (struct fimc_prv_data *)filp->private_data;  

659     struct fimc_control *ctrl = prv_data->ctrl;  

660     u32 size = vma->vm_end - vma->vm_start;  

661     u32 pfn, idx = vma->vm_pgoff;  

662   

663     vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);  

664     vma->vm_flags |= VM_RESERVED;  

665   

666     /* 

667      * page frame number of the address for a source frame 

668      * to be stored at. 

669      */  

670     pfn = __phys_to_pfn(ctrl->cap->bufs[idx].base[0]);  

671   

672     if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) {  

673         fimc_err('%s: writable mapping must be sharedn', __func__);  

674         return -EINVAL;  

675     }  

676   

677     if (remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot)) {  

678         fimc_err('%s: mmap failn', __func__);  

679         return -EINVAL;  

680     }  

681   

682     return 0;  

683 }  

fimc capture 设备的mmap实现,ctrl->cap->bufs[idx]是fimc capture设备申请的buffer,mmap就是把这个buffer映射到应用程序空间


661 vma->vm_pgoff 表示vm_file内以PAGE_SIZE为单位的偏移,但是在这里应用层和内核使用另外一种约定的含义,buffer ID, 应用层调用mmap接口对fimc capture设备的buffer进行映射


 


700 static u32 fimc_poll(struct file *filp, poll_table *wait)  

701 {  

702     struct fimc_prv_data *prv_data =  

703                 (struct fimc_prv_data *)filp->private_data;  

704     struct fimc_control *ctrl = prv_data->ctrl;  

705     struct fimc_capinfo *cap = ctrl->cap;  

706     u32 mask = 0;  

707   

708     if (cap) {  

709         if (cap->irq || (ctrl->status != FIMC_STREAMON)) {  

710             mask = POLLIN | POLLRDNORM;  

711             cap->irq = 0;  

712         } else {  

713             poll_wait(filp, &ctrl->wq, wait);  

714         }  

715     }  

716   

717     return mask;  

718 }  


fimc_poll向上层应用提供了等待机制,应用程序poll fimc设备并阻塞,直到cap或者output中断处理函数唤醒


 


732 u32 fimc_mapping_rot_flip(u32 rot, u32 flip)  

733 {  

734     u32 ret = 0;  

735   

736     switch (rot) {  

737     case 0:  

738         if (flip & FIMC_XFLIP)  

739             ret |= FIMC_XFLIP;  

740   

741         if (flip & FIMC_YFLIP)  

742             ret |= FIMC_YFLIP;  

743         break;  

744   

745     case 90:  

746         ret = FIMC_ROT;  

747         if (flip & FIMC_XFLIP)  

748             ret |= FIMC_XFLIP;  

749   

750         if (flip & FIMC_YFLIP)  

751             ret |= FIMC_YFLIP;  

752         break;  

753   

754     case 180:  

755         ret = (FIMC_XFLIP | FIMC_YFLIP);  

756         if (flip & FIMC_XFLIP)  

757             ret &= ~FIMC_XFLIP;  

758   

759         if (flip & FIMC_YFLIP)  

760             ret &= ~FIMC_YFLIP;  

761         break;  

762   

763     case 270:  

764         ret = (FIMC_XFLIP | FIMC_YFLIP | FIMC_ROT);  

765         if (flip & FIMC_XFLIP)  

766             ret &= ~FIMC_XFLIP;  

767   

768         if (flip & FIMC_YFLIP)  

769             ret &= ~FIMC_YFLIP;  

770         break;  

771     }  

772   

773     return ret;  

774 }  


rot会影响flip的结果,该函数映射(合并)rot和 flip操作


 


static int fimc_open(struct file *filp)  

{  

    struct fimc_control *ctrl;  

    struct s3c_platform_fimc *pdata;  

    struct fimc_prv_data *prv_data;  

    int in_use;  

    int ret;  

  

    ctrl = video_get_drvdata(video_devdata(filp));  

    pdata = to_fimc_plat(ctrl->dev);  

  

    mutex_lock(&ctrl->lock);  

  

    in_use = atomic_read(&ctrl->in_use);  

    if (in_use >= FIMC_MAX_CTXS || (in_use && 1 != ctrl->id)) {  

        fimc_err('%s: Device busy.n', __func__);  

        ret = -EBUSY;  

        goto resource_busy;  

    } else {  

        atomic_inc(&ctrl->in_use);  

    }  

    in_use = atomic_read(&ctrl->in_use);  

  

    prv_data = kzalloc(sizeof(struct fimc_prv_data), GFP_KERNEL);  

    if (!prv_data) {  

        fimc_err('%s: not enough memoryn', __func__);  

        ret = -ENOMEM;  

        goto kzalloc_err;  

    }  

  

    prv_data->ctx_id = fimc_get_free_ctx(ctrl);  

    if (prv_data->ctx_id < 0) {  

        fimc_err('%s: Context busy flag not reset.n', __func__);  

        ret = -EBUSY;  

        goto ctx_err;  

    }  

    prv_data->ctrl = ctrl;  

    filp->private_data = prv_data;  

  

    if (in_use == 1) {  

        fimc_clk_en(ctrl, true);  

  

        if (pdata->hw_ver == 0x40)  

            fimc_hw_reset_camera(ctrl);  

  

        /* Apply things to interface register */  

        fimc_hwset_reset(ctrl);  

  

        if (num_registered_fb > 0) {  

            struct fb_info *fbinfo = registered_fb[0];  

            ctrl->fb.lcd_hres = (int)fbinfo->var.xres;  

            ctrl->fb.lcd_vres = (int)fbinfo->var.yres;  

            fimc_info1('%s: fd.lcd_hres=%d fd.lcd_vres=%dn',  

                    __func__, ctrl->fb.lcd_hres,  

                    ctrl->fb.lcd_vres);  

        }  

  

        ctrl->mem.curr = ctrl->mem.base;  

        ctrl->status = FIMC_STREAMOFF;  

  

        if (0 != ctrl->id)  

            fimc_clk_en(ctrl, false);  

    }  

  

    mutex_unlock(&ctrl->lock);  

  

    fimc_info1('%s opened.n', ctrl->name);  

  

    return 0;  

  

ctx_err:  

    kfree(prv_data);  

  

kzalloc_err:  

    atomic_dec(&ctrl->in_use);  

  

resource_busy:  

    mutex_unlock(&ctrl->lock);  

    return ret;  

}  

932 如果是第一次打开,那么需要使能mclk  


 


939 调用fimc_hwset_reset 进行fimc控制器的reset过程951 FIMC会为每一个控制器预分配一段物理内存, mem.base 指向物理内存的起始地址,FIMC在执行DMA之前,需要为DMA分配物理内存,FIMC直接从这个预保留的空间进行分配


mem.curr记录着当前可用空间的起始位置在arch/arm/mach-s5pv210/mach-smdkc110.c中


 


//#define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC0 (6144 * SZ_1K)  

#define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC0 (24576 * SZ_1K)  

#define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC1 (9900 * SZ_1K)  

//#define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC2 (6144 * SZ_1K)  

#define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC2 (24576 * SZ_1K)  

#define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC0 (36864 * SZ_1K)  

#define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC1 (36864 * SZ_1K)  

#define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMD (S5PV210_LCD_WIDTH *   

                         S5PV210_LCD_HEIGHT * 4 *   

                         CONFIG_FB_S3C_NR_BUFFERS)  

#define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_JPEG (8192 * SZ_1K)  

定义了media device需要的reserved 物理内存大小,物理内存的实际使用可能达不到定义的大小,   

物理内存的使用大小 和图片大小以及queue buffer数量成正比的,也就是说图片分辨率Width x Height越高,queue buffer数量越多,实际使用的物理内存越大  

所以这个物理内存大小要根据项目情况具体调整,当然你也可以不调,就是浪费点内存。  

  

1133 struct video_device fimc_video_device[FIMC_DEVICES] = {    

1134     [0] = {    

1135         .fops = &fimc_fops,    

1136         .ioctl_ops = &fimc_v4l2_ops,    

1137         .release = fimc_vdev_release,    

1138     },    

1139     [1] = {    

1140         .fops = &fimc_fops,    

1141         .ioctl_ops = &fimc_v4l2_ops,    

1142         .release = fimc_vdev_release,    

1143     },    

1144     [2] = {    

1145         .fops = &fimc_fops,    

1146         .ioctl_ops = &fimc_v4l2_ops,    

1147         .release = fimc_vdev_release,    

1148     },    

1149 };  

FIMC_DEVICES 一共有三个fimc0, fimc1, fimc2设备,    

    

fops定义了设备节点的文件操作函数; ioctl_ops定义了fimc向V4L2提供的所有ioctl操作集合

1310 static int __devinit fimc_probe(struct platform_device *pdev)    

1311 {    

1312     struct s3c_platform_fimc *pdata;    

1313     struct fimc_control *ctrl;    

1314     struct clk *srclk;    

1315     int ret;    

1316    

1317     if (!fimc_dev) {    

1318         fimc_dev = kzalloc(sizeof(*fimc_dev), GFP_KERNEL);    

1319         if (!fimc_dev) {    

1320             dev_err(&pdev->dev, '%s: not enough memoryn',    

1321                 __func__);    

1322             return -ENOMEM;    

1323         }    

1324     }    

1325    

1326     ctrl = fimc_register_controller(pdev);    

1327     if (!ctrl) {    

1328         printk(KERN_ERR '%s: cannot register fimcn', __func__);    

1329         goto err_alloc;    

1330     }    

1331    

1332     pdata = to_fimc_plat(&pdev->dev);    

1333     if (pdata->cfg_gpio)    

1334         pdata->cfg_gpio(pdev);    

1335    

1336     /* Get fimc power domain regulator */    

1337     ctrl->regulator = regulator_get(&pdev->dev, 'pd');    

1338     if (IS_ERR(ctrl->regulator)) {    

1339         fimc_err('%s: failed to get resource %sn',    

1340                 __func__, 's3c-fimc');    

1341         return PTR_ERR(ctrl->regulator);    

1342     }    

1343    

1344     /* fimc source clock */    

1345     srclk = clk_get(&pdev->dev, pdata->srclk_name);    

1346     if (IS_ERR(srclk)) {    

1347         fimc_err('%s: failed to get source clock of fimcn',    

1348                 __func__);    

1349         goto err_v4l2;    

1350     }    

1351     

1352     /* fimc clock */    

1353     ctrl->clk = clk_get(&pdev->dev, pdata->clk_name);    

1354     if (IS_ERR(ctrl->clk)) {    

1355         fimc_err('%s: failed to get fimc clock sourcen',    

1356             __func__);    

1357         goto err_v4l2;    

1358     }    

1359     

1360     /* set parent for mclk */    

1361     clk_set_parent(ctrl->clk, srclk);    

1362     

1363     /* set rate for mclk */    

1364     clk_set_rate(ctrl->clk, pdata->clk_rate);    

1365     

1366     /* V4L2 device-subdev registration */    

1367     ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);    

1368     if (ret) {    

1369         fimc_err('%s: v4l2 device register failedn', __func__);    

1370         goto err_fimc;    

1371     }    

1372     

1373     /* things to initialize once */    

1374     if (!fimc_dev->initialized) {    

1375         ret = fimc_init_global(pdev);    

1376         if (ret)    

1377             goto err_v4l2;    

1378     }    

1379     

1380     /* video device register */    

1381     ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);    

1382     if (ret) {    

1383         fimc_err('%s: cannot register video drivern', __func__);    

1384         goto err_v4l2;    

1385     }    

1386     

1387     video_set_drvdata(ctrl->vd, ctrl);    

1388     

1389     ret = device_create_file(&(pdev->dev), &dev_attr_log_level);    

1390     if (ret < 0) {    

1391         fimc_err('failed to add sysfs entriesn');    

1392         goto err_global;    

1393     }    

1394     printk(KERN_INFO 'FIMC%d registered successfullyn', ctrl->id);    

1395     

1396     return 0;    

1397     

1398 err_global:    

1399     video_unregister_device(ctrl->vd);    

1400     

1401 err_v4l2:    

1402     v4l2_device_unregister(&ctrl->v4l2_dev);    

1403     

1404 err_fimc:    

1405     fimc_unregister_controller(pdev);    

1406     

1407 err_alloc:    

1408     kfree(fimc_dev);    

1409     return -EINVAL;    

1410     

1411 }    

    

1333 ~ 1334 调用平台的gpio设置函数,一般来说,这个用来设置external CameraA/CameraB的输入输出  

    

1344 ~ 1364 设置mclk,mclk的频率由sensor的输出图像尺寸, 如果外围sensor自身有晶振,那么CPU不需要对外提供mclk  

    

1381 ~ 1385 注册一个video device,会生成设备节点/dev/videoX  

 


  


  

  

  

  


  

  


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦线性调频增强器

  • 家用电器遥控器

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • DS1669数字电位器

  • HA1377 桥式放大器 BCL 电容 17W(汽车音频)

    相关电子头条文章