camera驅動框架分析(上)


前言

  camera驅動框架涉及到的知識點比較多,特別是camera本身的接口就有很多,有些是直接連接到soc的camif口上的,有些是通過usb接口導出的,如usb camera。我這里主要討論前者,也就是與soc直連的。我認為凡是涉及到usb的,都不是一兩句話可以說明白的!如有錯誤,歡迎指正,謝謝!!!

環境說明

涉及到的基礎知識點:
字符設備驅動
設備模型
平台設備驅動
v4l2框架
i2c驅動框架

涉及到的術語:
camera : 指的是整個camera,包括它本身的硬件連接方式及支持i2c控制的i2c設備
sensor : 指的是支持i2c控制的i2c設備,它屬於camera的一部分,在內核實現里也能體現出來
camera host: 指的是與camera相連接的,一般內嵌在soc里面的控制器

涉及到的文件夾:
drivers/media/platform/soc_camera/ 主要存放camera host驅動,通用的camera驅動也存放在此
drivers/media/i2c/soc_camera/ 主要存放sensor驅動

分析所采用的內核版本:

VERSION = 3                                                                                                                                                   
PATCHLEVEL = 15                                                                 
SUBLEVEL = 0                                                                    
EXTRAVERSION =                                                                  
NAME = Shuffling Zombie Juror    

camera的驅動包括通用camera的驅動、camera host的驅動以及sensor的驅動,下面一個個來分析

這里先插一張圖,來自:http://blog.csdn.net/kickxxx/article/details/8484498(該圖片及圖片后的文字是在我寫完這篇博文后發現的,我認為對理解camera驅動會有幫助,所以就摘抄了_)
soc camera 子系統 系統架構圖

Soc camera sub-system對應着drivers/media/video/下的soc_camera.c soc_camera_platform.c

Soc camera host 是host端實現,是由平台廠商實現的,向上實現soc_camera_host_ops接口,向下操作Camera host硬件以及通過平台特定的接口操作Soc camera device

Soc camera device 是平台的camera device(同時也是subdev),由驅動開發者來實現v4l2_subdev_call調用的subdev 接口,同時還要為soc camera host實現平台特定的操作接口;向下操作camera sensor或者video AD芯片。

Camera host hardware是平台硬件相關的,不同的平台有不同的host硬件,比如imx51的ipu,三星s5pv210的fimc控制器等。

soc_camera_host,soc_camera_device,v4l2_device,v4l2_subdev關系如下:
    理論上系統內可以有多個soc_camera_host,物理上soc_camera_host就是系統的camera處理模塊驅動
    一個soc_camera_host可以對應多個soc_camera_device,物理上soc_camera_device是一個camera接口,每個soc_camera_host對應一個v4l2_dev
    每個soc_camera_device,系統會為他們創建設備節點/dev/videoX。
    每個soc_camera_device有多個v4l2_subdev,物理上v4l2_subdev可以是sensor,video AD芯片
    v4l2_subdev可以通過i2c掛接到v4l2_device,也可以通過soc_camera_link提供的add_device來增加,這依賴於sensor和video AD芯片掛接到MCU camera接口的方式。

通用camera驅動

對應文件drivers/media/platform/soc_camera/soc_camera.c

static struct platform_driver __refdata soc_camera_pdrv = {
	.probe = soc_camera_pdrv_probe,
	.remove  = soc_camera_pdrv_remove,
	.driver  = {
		.name	= "soc-camera-pdrv",
		.owner	= THIS_MODULE,
	},
};

module_platform_driver(soc_camera_pdrv);

從這里可以看出,我們要使該驅動probe得到調用,先得注冊一個平台設備,且名字為soc-camera-pdrv。通用camera的驅動就是定義了一套數據結構,然后告訴大家,你如果想用通用的camera驅動,那就照着數據結構填好,然后用soc-camera-pdrv的名字通過平台總線注冊上來就可以了。平台設備的注冊可以通過兩種方式來實現,一種是通過設備樹,它是最新的一種機制,通過dts文件來描述硬件信息,使得內核里面不會再硬編碼一堆和用於描述硬件信息的代碼。對應到這里的硬件信息就是camera sensor硬件信息以及camera硬件布線信息。另一種就是以前采用的方式,直接用代碼在板子相關的啟動文件里來描述那些信息並通過平台設備的注冊。soc_camera_pdrv里面沒有設備樹的相關支持,說明這類設備的添加還是采用后面那種方式,通過下面的命令輸出也可以證實這一點:

我用命令(grep -rns soc-camera-pdrv arch/arm*/)搜索一下,就可以得到以下結果:

arch/arm/mach-shmobile/board-lager.c:394:	platform_device_register_data(&platform_bus, "soc-camera-pdrv", 1,
arch/arm/mach-shmobile/board-bockw.c:606:	platform_device_register_data(&platform_bus, "soc-camera-pdrv", 0,
arch/arm/mach-shmobile/board-bockw.c:609:	platform_device_register_data(&platform_bus, "soc-camera-pdrv", 1,
arch/arm/mach-shmobile/board-mackerel.c:1224:	.name	= "soc-camera-pdrv",
arch/arm/mach-shmobile/board-armadillo800eva.c:910:	.name	= "soc-camera-pdrv",
arch/arm/mach-shmobile/board-marzen.c:299:	.name	= "soc-camera-pdrv",				\
arch/arm/mach-at91/board-sam9m10g45ek.c:241:	.name	= "soc-camera-pdrv",
arch/arm/mach-omap1/board-ams-delta.c:435:	.name   = "soc-camera-pdrv",
arch/arm/mach-pxa/ezx.c:788:	.name   = "soc-camera-pdrv",
arch/arm/mach-pxa/ezx.c:1062:	.name   = "soc-camera-pdrv",
arch/arm/mach-pxa/em-x270.c:1034:	.name	= "soc-camera-pdrv",
arch/arm/mach-pxa/palmz72.c:339:	.name	= "soc-camera-pdrv",
arch/arm/mach-pxa/pcm990-baseboard.c:507:		.name	= "soc-camera-pdrv",
arch/arm/mach-pxa/pcm990-baseboard.c:513:		.name	= "soc-camera-pdrv",
arch/arm/mach-pxa/mioa701.c:682:MIO_SIMPLE_DEV(mioa701_camera,	  "soc-camera-pdrv",&iclink);
arch/arm/mach-imx/mach-imx27_visstrim_m10.c:572:	platform_device_register_resndata(NULL, "soc-camera-pdrv", 0, NULL, 0,
arch/arm/mach-imx/mach-mx31_3ds.c:248:	.name	= "soc-camera-pdrv",
arch/arm/mach-imx/mach-mx31_3ds.c:412:	REGULATOR_SUPPLY("cmos_2v8", "soc-camera-pdrv.0"),
arch/arm/mach-imx/mach-mx31_3ds.c:444:	REGULATOR_SUPPLY("cmos_vcore", "soc-camera-pdrv.0"),
arch/arm/mach-imx/mach-mx35_3ds.c:305:	.name	= "soc-camera-pdrv",
arch/arm/mach-imx/mach-mx35_3ds.c:324:	REGULATOR_SUPPLY("cmos_vio", "soc-camera-pdrv.0"),
arch/arm/mach-imx/mach-mx27_3ds.c:272:	REGULATOR_SUPPLY("cmos_2v8", "soc-camera-pdrv.0"),
arch/arm/mach-imx/mach-mx27_3ds.c:302:	REGULATOR_SUPPLY("cmos_vcore", "soc-camera-pdrv.0"),
arch/arm/mach-imx/mach-mx27_3ds.c:410:	.name	= "soc-camera-pdrv",
arch/arm/mach-imx/mx31moboard-smartbot.c:91:		.name	= "soc-camera-pdrv",
arch/arm/mach-imx/mx31moboard-marxbot.c:181:		.name	= "soc-camera-pdrv",
arch/arm/mach-imx/mach-pcm037.c:329:	.name	= "soc-camera-pdrv",
arch/arm/mach-imx/mach-pcm037.c:337:	.name	= "soc-camera-pdrv",

我選一個稍微簡單的mach來進行后面的分析,at91平台(arch/arm/mach-at91/board-sam9m10g45ek.c),我把相關的代碼截取出來:

   * soc-camera OV2640                                                            
   */                                                                             
  #if defined(CONFIG_SOC_CAMERA_OV2640) || \                                      
      defined(CONFIG_SOC_CAMERA_OV2640_MODULE)                                    
  static unsigned long isi_camera_query_bus_param(struct soc_camera_link *link)   
  {                                                                               
      /* ISI board for ek using default 8-bits connection */                      
      return SOCAM_DATAWIDTH_8;                                                   
  }                                                                               
                                                                                  
  static int i2c_camera_power(struct device *dev, int on)                         
  {                                                                               
      /* enable or disable the camera */                                          
      pr_debug("%s: %s the camera\n", __func__, on ? "ENABLE" : "DISABLE");       
      at91_set_gpio_output(AT91_PIN_PD13, !on);                                   
                                                                                  
      if (!on)                                                                    
          goto out;                                                               
                                                                                  
      /* If enabled, give a reset impulse */                                      
      at91_set_gpio_output(AT91_PIN_PD12, 0);                                     
      msleep(20);                                                                 
      at91_set_gpio_output(AT91_PIN_PD12, 1);                                     
      msleep(100);                                                                
                                                                                  
  out:                                                                            
      return 0;                                                                   
  }                                                                               
                                                                                  
  static struct i2c_board_info i2c_camera = {                                     
      I2C_BOARD_INFO("ov2640", 0x30),                                             
  };                                                                              
                                                                                  
  static struct soc_camera_link iclink_ov2640 = {                                 
      .bus_id         = 0,                                                        
      .board_info     = &i2c_camera,                                              
      .i2c_adapter_id     = 0,                                                    
      .power          = i2c_camera_power,                                         
      .query_bus_param    = isi_camera_query_bus_param,                           
  };                                                                              
                                                                                  
  static struct platform_device isi_ov2640 = {                                    
      .name   = "soc-camera-pdrv",                                                
      .id = 0,                                                                    
      .dev    = {                                                                 
          .platform_data = &iclink_ov2640,                                        
      },                                                                          
  };                                                                              
  #endif   

最重要的結構就是soc_camera_link,它是所有camera這類設備都需要用到的結構體。bus_id用來描述它是連接到哪條soc camera host總線上,后面會再講這個。board_info用來描述i2c設備的信息,比如它的型號名稱,它的i2c地址,相信研究過i2c驅動的人都比較熟悉。i2c_adapter_id用來描述i2c設備掛載哪條i2c總線上。sensor的控制一般通過i2c來實現,所以這里才會有i2c設備的描述,因為需要對應的i2c驅動來驅動它啊。power一般指sensor的電源模塊的開啟和關閉,一般是單獨通過一個gpio來控制的。query_bus_param這個成員先不看吧,用到的時候再看。

總之,通過上面的信息以及后面的平台設備注冊后,就將soc-camera-pdrv平台設備添加到平台總線了。也就是說只要這段代碼編譯進入了內核並調用了這段代碼,那么soc_camera_pdrv_probe就一定會執行了。下面繼續分析前面列出來的soc_camera_pdrv_probe吧!

soc_camera_pdrv_probe的實現很短,為了方面說明,也貼出來吧:

static int soc_camera_pdrv_probe(struct platform_device *pdev)
{
	struct soc_camera_desc *sdesc = pdev->dev.platform_data;
	struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc;
	struct soc_camera_device *icd;
	int ret;

	if (!sdesc)
		return -EINVAL;

	icd = devm_kzalloc(&pdev->dev, sizeof(*icd), GFP_KERNEL);
	if (!icd)
		return -ENOMEM;

	/*
	 * In the asynchronous case ssdd->num_regulators == 0 yet, so, the below
	 * regulator allocation is a dummy. They are actually requested by the
	 * subdevice driver, using soc_camera_power_init(). Also note, that in
	 * that case regulators are attached to the I2C device and not to the
	 * camera platform device.
	 */
	ret = devm_regulator_bulk_get(&pdev->dev, ssdd->sd_pdata.num_regulators,
				      ssdd->sd_pdata.regulators);
	if (ret < 0)
		return ret;

	icd->iface = sdesc->host_desc.bus_id;
	icd->sdesc = sdesc;
	icd->pdev = &pdev->dev;
	platform_set_drvdata(pdev, icd);

	icd->user_width		= DEFAULT_WIDTH;
	icd->user_height	= DEFAULT_HEIGHT;

	return soc_camera_device_register(icd);
}

這里我們會開始接觸第二個重要的數據結構soc_camera_device,它在內核里代表的就是一個camera sensor設備。有一點需要提前說明下,我們之前談到數據結構soc_camera_link,對應到驅動使用的時候,將其拆分成兩個結構體了,我想也是為了代碼更清晰吧!對應的結構如下:

struct soc_camera_desc {
	struct soc_camera_subdev_desc subdev_desc;
	struct soc_camera_host_desc host_desc;
};

因此,soc_camera_pdrv_probe里面的icd->iface = sdesc->host_desc.bus_id其實就是上面我說過的bus_id,用來描述它是連接到哪條soc camera host線上。soc_camera_pdrv_probe主要是創建對象 soc_camera_device,它代表着一個camera sensor設備。當然可以有多個這樣的設備同時存在,且都由該驅動負責創建。並將platform設備傳過來的各種數據放到soc_camera_device里面,最終調用soc_camera_device_register將該camera sensor注冊。

soc_camera_device_register的代碼就不貼了,它其實主要就做了一件事情,將代表着camera sensor的對象soc_camera_device放到了全局鏈表devices中,其他的就是做參數檢查等等。

好了,到這里,我們的系統里的devices全局鏈表里已經有一個用於代表camera sensor的設備了,它就在這里靜靜的等待着負責它的驅動的到來,我們應該可以想象到,負責它的就是camera host咯。順便說一下,如果我們僅僅需要寫一個sensor驅動,那么到這里,就算完成了一小半了,剩下的就是完成我們camera sensor里對應的i2c設備的驅動(參考drivers/media/i2c/soc_camera/,里面有一些已經實現了的i2c sensor驅動),至於camera host驅動,一般對應的soc的sdk都會實現啦。

未完,待續!
2015年6月


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM