Linux3.X下的Samsung MFC(Multi Format Codec) Firmware

作者:咕唧咕唧liukun321

来自:http://blog.csdn.net/liukun321


三星系列处理器,从S3c6410开始SOC上就集成了一个MFC(Multi Format Codec)多格式媒体编解码器硬件模块。并且三星的linux BSP开源了对它的驱动。看过MFC驱动的朋友,肯定发现了,在驱动初始化过程中,内核需要加载一个Samsung MFC 固件。没有它MFC是用不了的。下面我们就看看,这个固件是怎么和内核联系起来的。从哪能获得固件。

先看MFC固件的加载,这个过程如下,以exynos4412 Linux 3.5 的BSP为例,在linux-3.5\drivers\media\video\s5p-mfc\s5p_mfc.c:


[cpp] view plain copy
  1. static int s5p_mfc_open(struct file *file)  
  2. {  
  3.          structs5p_mfc_dev *dev = video_drvdata(file);  
  4.          structs5p_mfc_ctx *ctx = NULL;  
  5.          structvb2_queue *q;  
  6.          unsignedlong flags;  
  7.          intret = 0;  
  8.          exynos_cpufreq_lock_freq(1,MAX_CPU_FREQ);  
  9. #ifdef CONFIG_BUSFREQ_OPP  
  10.          dev_lock(dev->bus_dev,dev->device, BUSFREQ_400MHZ);  
  11. #endif  
  12.    
  13.          mfc_debug_enter();  
  14.          dev->num_inst++;   /* It is guarded by mfc_mutex in vfd */  
  15.          /*Allocate memory for context */  
  16.          ctx= kzalloc(sizeof *ctx, GFP_KERNEL);  
  17.          if(!ctx) {  
  18.                    mfc_err("Notenough memory\n");  
  19.                    ret= -ENOMEM;  
  20.                    gotoerr_alloc;  
  21.          }  
  22.          v4l2_fh_init(&ctx->fh,video_devdata(file));  
  23.          file->private_data= &ctx->fh;  
  24.          v4l2_fh_add(&ctx->fh);  
  25.          ctx->dev= dev;  
  26.          INIT_LIST_HEAD(&ctx->src_queue);  
  27.          INIT_LIST_HEAD(&ctx->dst_queue);  
  28.          ctx->src_queue_cnt= 0;  
  29.          ctx->dst_queue_cnt= 0;  
  30.          /*Get context number */  
  31.          ctx->num= 0;  
  32.          while(dev->ctx[ctx->num]) {  
  33.                    ctx->num++;  
  34.                    if(ctx->num >= MFC_NUM_CONTEXTS) {  
  35.                             mfc_err("Toomany open contexts\n");  
  36.                             ret= -EBUSY;  
  37.                             gotoerr_no_ctx;  
  38.                    }  
  39.          }  
  40.          /*Mark context as idle */  
  41.          spin_lock_irqsave(&dev->condlock,flags);  
  42.          clear_bit(ctx->num,&dev->ctx_work_bits);  
  43.          spin_unlock_irqrestore(&dev->condlock,flags);  
  44.          dev->ctx[ctx->num]= ctx;  
  45.          if(s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {  
  46.                    ctx->type= MFCINST_DECODER;  
  47.                    ctx->c_ops= get_dec_codec_ops();  
  48.                    /*Setup ctrl handler */  
  49.                    ret= s5p_mfc_dec_ctrls_setup(ctx);  
  50.                    if(ret) {  
  51.                             mfc_err("Failedto setup mfc controls\n");  
  52.                             gotoerr_ctrls_setup;  
  53.                    }  
  54.          }else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {  
  55.                    ctx->type= MFCINST_ENCODER;  
  56.                    ctx->c_ops= get_enc_codec_ops();  
  57.                    /*only for encoder */  
  58.                    INIT_LIST_HEAD(&ctx->ref_queue);  
  59.                    ctx->ref_queue_cnt= 0;  
  60.                    /*Setup ctrl handler */  
  61.                    ret= s5p_mfc_enc_ctrls_setup(ctx);  
  62.                    if(ret) {  
  63.                             mfc_err("Failedto setup mfc controls\n");  
  64.                             gotoerr_ctrls_setup;  
  65.                    }  
  66.          }else {  
  67.                    ret= -ENOENT;  
  68.                    gotoerr_bad_node;  
  69.          }  
  70.          ctx->fh.ctrl_handler= &ctx->ctrl_handler;  
  71.          ctx->inst_no= -1;  
  72.          /*Load firmware if this is the first instance */  
  73.          if(dev->num_inst == 1) {  
  74.                    dev->watchdog_timer.expires= jiffies +  
  75.                                                msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);  
  76.                    add_timer(&dev->watchdog_timer);  
  77.                    ret= s5p_mfc_power_on();  
  78.                    if(ret < 0) {  
  79.                             mfc_err("poweron failed\n");  
  80.                             gotoerr_pwr_enable;  
  81.                    }  
  82.                    s5p_mfc_clock_on();  
  83.                    ret = s5p_mfc_alloc_and_load_firmware(dev);  
  84.                    if(ret)  
  85.                             gotoerr_alloc_fw;  
  86.                    /*Init the FW */  
  87.                    ret= s5p_mfc_init_hw(dev);  
  88.                    if(ret)  
  89.                             gotoerr_init_hw;  
  90.                    s5p_mfc_clock_off();  
  91.          }  
  92.          /*Init videobuf2 queue for CAPTURE */  
  93.          q= &ctx->vq_dst;  
  94.          q->type= V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;  
  95.          q->drv_priv= &ctx->fh;  
  96.          if(s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {  
  97.                    q->io_modes= VB2_MMAP | VB2_DMABUF;  
  98.                    q->ops= get_dec_queue_ops();  
  99.          }else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {  
  100.                    q->io_modes= VB2_MMAP | VB2_USERPTR | VB2_DMABUF;  
  101.                    q->ops= get_enc_queue_ops();  
  102.          }else {  
  103.                    ret= -ENOENT;  
  104.                    gotoerr_queue_init;  
  105.          }  
  106.          q->mem_ops= (struct vb2_mem_ops *)&vb2_dma_contig_memops;  
  107.          ret= vb2_queue_init(q);  
  108.          if(ret) {  
  109.                    mfc_err("Failedto initialize videobuf2 queue(capture)\n");  
  110.                    gotoerr_queue_init;  
  111.          }  
  112.          /*Init videobuf2 queue for OUTPUT */  
  113.          q= &ctx->vq_src;  
  114.          q->type= V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;  
  115.          q->io_modes= VB2_MMAP;  
  116.          q->drv_priv= &ctx->fh;  
  117.          if(s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {  
  118.                    q->io_modes= VB2_MMAP | VB2_DMABUF;  
  119.                    q->ops= get_dec_queue_ops();  
  120.          }else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {  
  121.                    q->io_modes= VB2_MMAP | VB2_USERPTR | VB2_DMABUF;  
  122.                    q->ops= get_enc_queue_ops();  
  123.          }else {  
  124.                    ret= -ENOENT;  
  125.                    gotoerr_queue_init;  
  126.          }  
  127.          q->mem_ops= (struct vb2_mem_ops *)&vb2_dma_contig_memops;  
  128.          ret= vb2_queue_init(q);  
  129.          if(ret) {  
  130.                    mfc_err("Failedto initialize videobuf2 queue(output)\n");  
  131.                    gotoerr_queue_init;  
  132.          }  
  133.          init_waitqueue_head(&ctx->queue);  
  134.          mfc_debug_leave();  
  135.          returnret;  
  136.          /*Deinit when failure occured */  
  137. err_queue_init:  
  138. err_init_hw:  
  139.          s5p_mfc_release_firmware(dev);  
  140. err_alloc_fw:  
  141.          dev->ctx[ctx->num]= NULL;  
  142.          del_timer_sync(&dev->watchdog_timer);  
  143.          s5p_mfc_clock_off();  
  144. err_pwr_enable:  
  145.          if(dev->num_inst == 1) {  
  146.                    if(s5p_mfc_power_off() < 0)  
  147.                             mfc_err("poweroff failed\n");  
  148.                    s5p_mfc_release_firmware(dev);  
  149.          }  
  150. err_ctrls_setup:  
  151.          s5p_mfc_dec_ctrls_delete(ctx);  
  152. err_bad_node:  
  153. err_no_ctx:  
  154.          v4l2_fh_del(&ctx->fh);  
  155.          v4l2_fh_exit(&ctx->fh);  
  156.          kfree(ctx);  
  157. err_alloc:  
  158.          dev->num_inst--;  
  159.          mfc_debug_leave();  
  160.          returnret;  
  161. }  

展开第83行函数ret = s5p_mfc_alloc_and_load_firmware(dev);


[cpp] view plain copy
  1. int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)  
  2. {  
  3.     struct firmware *fw_blob;  
  4.     size_t bank2_base_phys;  
  5.     void *b_base;  
  6.     int err;  
  7.   
  8.     /* Firmare has to be present as a separate file or compiled 
  9.      * into kernel. */  
  10.     mfc_debug_enter();  
  11.     err = request_firmware((const struct firmware **)&fw_blob,  
  12.                      "s5p-mfc.fw", dev->v4l2_dev.dev);  
  13.     if (err != 0) {  
  14.         mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");  
  15.         return -EINVAL;  
  16.     }  
  17.     dev->fw_size = ALIGN(fw_blob->size, FIRMWARE_ALIGN);  
  18.     if (s5p_mfc_bitproc_buf) {  
  19.         mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n");  
  20.         release_firmware(fw_blob);  
  21.         return -ENOMEM;  
  22.     }  
  23.     s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc(  
  24.         dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size);  
  25.     if (IS_ERR(s5p_mfc_bitproc_buf)) {  
  26.         s5p_mfc_bitproc_buf = NULL;  
  27.         mfc_err("Allocating bitprocessor buffer failed\n");  
  28.         release_firmware(fw_blob);  
  29.         return -ENOMEM;  
  30.     }  


只看关键部分,分析一下request_firmware((const struct firmware**)&fw_blob, "s5p-mfc.fw", dev->v4l2_dev.dev); 从函数名看,不难联想其功能:加载固件。 其实就是把固件中的数据搬到fw_blob 的一个成员指向的内存中,具体怎么搬,看后面分析。 说到这里,我们有必要再看一下struct firmware 这个结构的定义

struct firmware {

         size_t size;

         const u8*data;

         struct page**pages;

};

该说说固件加载的过程了,request_firmware执行过程中,会为实例化的struct firmware结构分配内存,并且函数将在 /sys/class/firmware 下创建一个以设备名为目录名的新目录,其中包含3 个属性:

loading :这个属性应当被加载固件的用户空间进程设置为 1。当加载完毕, 它将被设为 0。被设为 -1 时,将中止固件加载。
data :一个用来接收固件数据的二进制属性。在设置 loading 1, 用户空间进程将固件写入这个属性。
device :一个链接到 /sys/devices 下相关入口项的符号链接。

一旦创建了 sysfs 入口项, 内核将为设备产生一个热插拔事件,并传递包括变量FIRMWARE的环境变量给处理热插拔的用户空间程序。FIRMWARE被设置为提供给 request_firmware 的固件文件名。

用户空间程序定位固件文件, 并将其拷贝到内核提供的二进制属性;若无法定位文件, 用户空间程序设置 loading 属性为 -1。

 

若固件请求在 10 秒内没有被服务, 内核就放弃并返回一个失败状态给驱动。超时周期可通过 sysfs 属性 /sys/class/firmware/timeout 属性改变。

 

说了这么多,其实还落了个重点。其实在执行request_firmware时还有这样一个过程:判断固件是否被编译进内核,如果在内核编译过程就加入了固件,上面说的那个过程就白扯了---用不到。看下面代码:

fw_priv =_request_firmware_prepare(firmware_p, name, device, true,false);

if (IS_ERR_OR_NULL(fw_priv))

         returnPTR_RET(fw_priv);

 

这是request_firmware函数开头的一小段代码,_request_firmware_prepare 函数执行过程,判断固件若被编译入内核,则从内核全局的固件地址(指针)处往目标地址进行拷贝,也就是拷贝到structfirmware data成员指向的内存处(最终完成立了固件的加载),然后_request_firmware_prepare返回空指针,进而request_firmware函数也返回了。此过程代码实现如下:

[cpp] view plain copy
  1. static struct firmware_priv *  
  2. _request_firmware_prepare(const struct firmware **firmware_p, const char *name,  
  3.               struct device *device, bool uevent, bool nowait)  
  4. {  
  5.     struct firmware *firmware;  
  6.     struct firmware_priv *fw_priv;  
  7.   
  8.     if (!firmware_p)  
  9.         return ERR_PTR(-EINVAL);  
  10.   
  11.     *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);  
  12.     if (!firmware) {  
  13.         dev_err(device, "%s: kmalloc(struct firmware) failed\n",  
  14.             __func__);  
  15.         return ERR_PTR(-ENOMEM);  
  16.     }  
  17.   
  18.     if (fw_get_builtin_firmware(firmware, name)) {  
  19.         dev_dbg(device, "firmware: using built-in firmware %s\n", name);  
  20.         return NULL;  
  21.     }  
  22. 。。。。。  
  23. }  

上面扯了一堆固件加载过程的东西,其实不止是MFC,几乎所有的固件加载都是遵循上面的过程。

再来点实用的东西。

我们该从哪里获得固件呢?

1、找三星技术支持。

2、找第三方开发板售后。

3、从网上Down

前两条就当是废话,三星不会搭理我们这些小用户。开发板售后?他们只会说:自己找一下,光盘里呢!光盘里是有,可我想要高版本的哪去搞?

这个也不难,google一下流行的发行版的linux内核分支,固件嘛好在是应用于全平台的。立马就有了。

下面附上我拿到的固件:

Samsung MFC 固件下载
共包含以下文件:
 * MFCv5 firmware (s5p-mfc.fw)
 * MFCv6 firmware, version 1 (s5p-mfc-v6.fw)
 * MFCv6 firmware, version 2 (s5p-mfc-v6-v2.fw)
 * MFCv7 firmware (s5p-mfc-v7.fw)
 * MFCv8 firmware (s5p-mfc-v8.fw)

在我的内核包里,用的是s5p-mfc.fw,也就是MFCv5。

另外还找到了一些关于MFC固件版本与处理器对应关系的信息:

s5p-mfc-v7.fw: Used in exynos 5420

s5p-mfc-v8.fw: Used in exynos 5800

 

本文的内容就这么多了,下篇文章希望能够接上篇,分析Live555服务器流程。Live555用的时间短,文字码出来,总感觉质量不行。好事多磨吧。

你可能感兴趣的:(Linux3.X下的Samsung MFC(Multi Format Codec) Firmware)