S5PV210 camera 驅動分析(android)

一、Camera 工作原理介紹
1. 結構 .

一般來說,camera 主要是由 lens 和 sensor IC 兩部分組成,其中有的 sensor IC 集成 了 DSP,有的沒有集成,但也需要外部 DSP 處理。細分的來講,camera 設備由下邊幾部 分構成:

 1) lens(鏡頭) 一般 camera 的鏡頭結構是有幾片透鏡組成,分有塑膠透鏡(Plastic)和玻璃透 鏡(Glass) ,通常鏡頭結構有:1P,2P,1G1P,1G3P,2G2P,4G 等。

 2) sensor(圖像傳感器) Senor 是一種半導體芯片,有兩種類型:CCD 和 CMOS。Sensor 將從 lens 上傳導過來的光線轉換爲電信號, 再通過內部的 AD 轉換爲數字信號。 由於 Sensor 的每個 pixel 只能感光 R 光或者 B 光或者 G 光, 因此每個像素此時存貯的是單色的, 我們稱之爲 RAW DATA 數據。 要想將每個像素的 RAW DATA 數據還原成三基色,就需要 ISP 來處理。 

3)ISP(圖像信號處理) 主要完成數字圖像的處理工作,把 sensor 採集到的原始數據轉換爲顯示支持 的格式。 

4)CAMIF(camera 控制器) 芯片上的 camera 接口電路,對設備進行控制,接收 sensor 採集的數據交給 CPU,並送入 LCD 進行顯示。


2. 工作原理 .
外部光線穿過 lens 後, 經過 color filter 濾波後照射到 Sensor 面上, Sensor 將從 lens 上傳導過來的光線轉換爲電信號,再通過內部的 AD 轉換爲數字信號。如果 Sensor 沒有集 成 DSP,則通過 DVP 的方式傳輸到 baseband,此時的數據格式是 RAW DATA。如果集成 了 DSP, RAW DATA 數據經過 AWB、 則 color matrix、 lens shading、 gamma、 sharpness、 AE 和 de-noise 處理,後輸出 YUV 或者 RGB 格式的數據。
最後會由 CPU 送到 framebuffer 中進行顯示,這樣我們就看到 camera 拍攝到的景象 了。

3. YUV 與 YCbCr .
YUV 和 RGB 一樣,是色彩空間中常用的色彩模型之一,兩者可以相互轉換。YUV 中 得 Y 表示亮度,U 和 V 表示色度。與 RGB 相比,它的優點在於佔用更少的空間。 YCbCr 則是在世界數字組織視頻標準研製過程中作爲 ITU - R BT601 建議的一部分, 其實是 YUV 經過縮放和偏移的翻版。 其中 Y 與 YUV 中的 Y 含義一致, Cb , Cr 同樣都指色 彩, 只是在表示方法上不同而已。在 YUV 家族中, YCbCr 是在計算機系統中應用最多的成 員, 其應用領域很廣泛,JPEG、 MPEG 均採用此格式。 一般人們所講的 YUV 大多是指 YCbCr。 YCbCr 有許多取樣格式, 如 4∶4∶4 , 4∶2∶2 , 4∶1∶1 和 4∶2∶0。

二、Camera 硬件
1. CAMIF .

如下是 S5PV210 的 camera 系統的結構圖:



S5PV210 的 camera 接口控制器叫 FIMC4.3,它支持 ITU R BT-601/656、AXI 和 MIPI(CSI)三種接口方式,最大輸入像素是 8192*8192。S5PV210 有三組 camera 接口。

主要特性: 支持多種輸入接口類型:



ITU-R BT 601/656 模式 DMA(AXI 64 位)模式 MIPI(CSI)模式 Direct FIFO 模式 支持多種輸出接口:DMA 模式/Direct FIFO 模式 支持數碼調焦(DZI) 最大輸入像素 8192*8192 支持圖像翻轉、旋轉等處理效果 生成多種圖片格式 支持採集幀控制

2. 接口信號 .

FIMC 信號定義如下所示(YCbCr 模式)

Signal

VSYNC HREF PCLK DATA[7:0] FIELD CAM_MCLK I I I I


I/O
幀同步信號 行同步信號 像素時鐘 像素數據 FIELD 信號

Description

Type

Muxed

O O

系統時鐘信號



通過 CAM_MCLK 給攝像頭提供時鐘,RST 是復位線,PWDN 在攝像頭工作時應該始終 爲低。HREF 是行參考信號,PCLK 是像素時鐘,VSYNC 是場同步信號。一旦給攝像頭提供了 時鐘,並且復位攝像頭,攝像頭就開始工作了,通過 HREF,PCLK 和 VSYNC 同步傳輸數字圖 像信號。數據是通過 DATA0~DATA7 這八根數據線並行送出的。

3. 工作時序 .
FIMC43 支持如下兩種視頻數據:

ITU-R BT 601 輸入時序圖 這種方式下行和幀同步信號獨立於視頻數據,因此需要同步信號。



ITU-R BT 656 輸入時序圖



這種方式下同步信號已經內嵌到視頻數據中了,因此不需要額外的行和幀同步信號。

(ITU-R BT 601: 16 位數據傳輸;21 芯;Y、U、V 信號同時傳輸。 ITU-R BT 656: 9 芯,不需要同步信號;8 位數據傳輸;串行視頻傳輸;傳輸速率是 601 的 2 倍;先傳 Y, 後傳 UV。 )

同步信號的時延參數 t1:表示 VSYNC 前、後插入週期 t2:表示 HREF 前插入週期 t3:表示 HREF 寬度 t4:表示 HREF 後插入週期



4. 外部接口 . 外部接口
硬件原理圖的 CAM A 部分:

CAM B 部分

5. Camera 內部結構圖 .
下圖是 camera 內部結構框圖,以 OV sensor 爲例:

三、Camera 驅動
1. V4L2 .

1)簡介 ) 在 Linux 中,攝像頭方面的標準化程度比較高,這個標準就是 V4L2 驅動程序,這也是 業界比較公認的方式。 V4L 全稱是 Video for Linux,是 Linux 內核中標準的關於視頻驅動程序,目前使用比 較多的版本是 Video for Linux 2, 簡稱 V4L2。 它爲 Linux 下的視頻驅動提供了統一的接口, 使得應用程序可以使用統一的 API 操作不同的視頻設備。從內核空間到用戶空間,主要的 數據流和控制類均由 V4L2 驅動程序的框架來定義。 V4L2 驅動程序一般只提供 Video 數據的獲得,而如何實現視頻預覽,如何向上層發送 數據,如何把純視頻流和取景器、視頻錄製等實際業務組織起來,都是 camera 的硬件抽象 層需要負責的工作。

 V4L2 驅動核心實現爲如下文件:drivers/media/video/v4l2-dev.c。 V4l2-dev.h 中定義的 video_device 是 V4L2 驅動程序的核心數據結構,它爲具體的攝 像頭 sensor 驅動提供了接口調用。 V4l2 的採集過程(應用程序):

1)打開設備,獲得文件描述符; 

2) 設置圖片格式;

3) 分配緩衝區;

4)啓動採集過程,讀取數據;

5) 停止採集,關閉設備。


2)數據結構 )

 V4L2 的主要數據結構是 video_device,定義在 v4l2_dev.h 中:

struct video_device { 

/* device ops */ 

const struct v4l2_file_operations *fops; /*接口函數指針*/


/* sysfs */

 struct device dev; struct cdev *cdev; /* v4l 設備結構 */ 

/* 字符設備結構*/


/* Set either parent or v4l2_dev if your driver uses v4l2_device */ 

struct device *parent; struct v4l2_device *v4l2_dev; /* 設備父指針 */

/* v4l2 設備指針*/


/* device info */ 

char name[32]; 

int vfl_type; /* 'minor' is set to -1 if the registration failed */ 

int minor; u16 num; /* use bitops to set/clear/test flags */ 

unsigned long flags; /* attribute to differentiate multiple indices on one physical device */ 

int index; /*次設備號*/ 

/*設備名稱*/


/* V4L2 file handles */ 

spinlock_t fh_lock; /* Lock for all v4l2_fhs */


struct list_head fh_list; /* List of struct v4l2_fh */

int debug;

/* debug 級別*/

/* Video 標準變量 */ 

v4l2_std_id tvnorms; /* Supported tv norms */


v4l2_std_id current_norm; /* Current tvnorm */

/* 回調函數 */ 

void (*release)(struct video_device *vdev);


/* ioctl 回調函數 */ 

const struct v4l2_ioctl_ops *ioctl_ops; 

};


主要接口函數有: int video_register_device(struct video_device *vdev, int type, int nr);

static int v4l2_ioctl(struct inode *inode, struct file *filp, long arg);

unsigned int cmd, unsigned

2. FIMC
1)簡介 ) FIMC 這個模塊不僅僅是一個攝像頭的控制接口,它還承擔着 V4L2 的 output 功能和 overlay 的功能。 FIMC 的驅動在內核中的位置:drivers/media/video/samsung/fimc 它包含下邊的文件:
fimc_regs.c fimc_capture.c fimc_dev.c fimc_output.c fimc_overlay.c fimc_v4l2.c

它們的組織關係如下:

可以看到,FIMC 的驅動實現了 v4l2 所有的接口,可以分爲 v4l2-input 設備接口, v4l2-output 設備接口以及 v4l2-overlay 設備接口。這裏我們主要關注 v4l2-input 設備接口, 因爲攝像頭屬於視頻輸入設備。 fimc_v4l2.c 裏面註冊了很多的回調函數,都是用於實現 v4l2 的標準接口的,但是這些 回調函數基本上都不是在 fimc_v4l2.c 裏面實現的,而是有相應的.c 分別去實現。比如:

v4l2-input 設備的操作實現: fimc_capture.c v4l2-output 

設備的操作實現: fimc_output.c v4l2-overlay 

設備的操作實現: fimc_overlay.c


這些代碼其實都是和具體硬件操作無關的, 這個驅動把所有操作硬件寄存器的代碼都寫 到一個文件裏面了,就是 fimc40_regs.c。這樣把硬件相關的代碼和硬件無關的代碼分開來 實現是非常好的方式,可以最大限度的實現代碼複用。

 2) 數據結構 ) FIMC 的主要數據結構 fimc_control,定義在 fimc.h 中:


struct fimc_control {
        int                             id;             /* controller id */
        char                            name[16];
        atomic_t                        in_use;
        void __iomem                    *regs;          /* register i/o */
        struct clk                      *clk;           /* interface clock */
        struct regulator        *regulator;             /* pd regulator */
        struct fimc_meminfo             mem;            /* for reserved mem */


        /* kernel helpers */
        struct mutex                    lock;           /* controller lock */
        struct mutex                    alloc_lock;
        struct mutex                    v4l2_lock;
        wait_queue_head_t               wq;
        struct device                   *dev;
        int                             irq;


        /* v4l2 related */
        struct video_device             *vd;
        struct v4l2_device              v4l2_dev;


        /* fimc specific */
        struct fimc_limit               *limit;         /* H/W limitation */
        struct s3c_platform_camera      *cam;           /* activated camera */
        struct fimc_capinfo             *cap;           /* capture dev info */
        struct fimc_outinfo             *out;           /* output dev info */
        struct fimc_fbinfo              fb;             /* fimd info */
        struct fimc_scaler              sc;             /* scaler info */
        struct fimc_effect              fe;             /* fimc effect info */


        enum fimc_status                status;
        enum fimc_log                   log;


        u32                             ctx_busy[FIMC_MAX_CTXS];
};


因爲 FIMC 一共有三套一樣的控制器(fimc0, fimc1, fimc2) ,所以驅動裏使用了一個數組來描述:: 

struct video_device fimc_video_device[FIMC_DEVICES] = { 

[0] = { .fops = &fimc_fops, .ioctl_ops = &fimc_v4l2_ops, .release = fimc_vdev_release, }, 

[1] = { .fops = &fimc_fops, .ioctl_ops = &fimc_v4l2_ops, .release = fimc_vdev_release, }, 

[2] = { .fops = &fimc_fops, .ioctl_ops = &fimc_v4l2_ops, .release = fimc_vdev_release, }, 

};


fb_ops 結構體是針對 v4l2 設備的基本操作,定義如下:

static const struct v4l2_file_operations fimc_fops = { 

            .owner  = THIS_MODULE,

             .open = fimc_open, 

             .release  = fimc_release,

             .ioctl = video_ioctl2,

             .read = fimc_read,

             .write = fimc_write,

             .mmap = fimc_mmap,

             .poll = fimc_poll,

}; 

3)FIMC 初始設置 

) 在 S5PV210 中, FIMC 初始設置代碼在 /drivers/ arch/arm/mach-s5pv210/mach-t34h.c 中:

static struct s3c_platform_fimc fimc_plat_lsi = {
        .srclk_name     = "mout_mpll",
        .clk_name       = "sclk_fimc",
        .lclk_name      = "sclk_fimc_lclk",
        .clk_rate       = 166750000,
#if defined(CONFIG_VIDEO_S5K4EA)
        .default_cam    = CAMERA_CSI_C,
#else
#ifdef CAM_ITU_CH_A
        .default_cam    = CAMERA_PAR_A,
#else
        .default_cam    = CAMERA_PAR_B,
#endif
#endif
        .camera         = {
#ifdef CONFIG_VIDEO_S5K4ECGX
                        &s5k4ecgx,
#endif
#ifdef CONFIG_VIDEO_S5KA3DFX
                        &s5ka3dfx,
#endif
#ifdef CONFIG_VIDEO_S5K4BA
                        &s5k4ba,
#endif
#ifdef CONFIG_VIDEO_S5K4EA
                        &s5k4ea,
#endif
#ifdef CONFIG_VIDEO_HM2055
                        &hm2055,
#endif
#ifdef CONFIG_VIDEO_GC0308
                        &gc0308,
#endif
#ifdef CONFIG_VIDEO_HIMAX2055
                        &himax2055,
#endif
#ifdef CONFIG_VIDEO_ADV7181
                        &adv7181,
#endif
        },
        .hw_ver         = 0x43,
};



對於 GPIO 的配置代碼在 /drivers/ arch/arm/mach-s5pv210/setup-fimc0.c 中:

void s3c_fimc0_cfg_gpio(struct platform_device *pdev) { 

int i = 0;


/* CAM A port(b0010) : PCLK, VSYNC, HREF, DATA[0-4] */ 

for (i = 0; i < 8; i++) { 

s3c_gpio_cfgpin(S5PV210_GPE0(i), S3C_GPIO_SFN(2)); 

s3c_gpio_setpull(S5PV210_GPE0(i), S3C_GPIO_PULL_NONE);

 } 

/* CAM A port(b0010) : DATA[5-7], CLKOUT(MIPI CAM also), FIELD */ 


for (i = 0; i < 5; i++) { 

s3c_gpio_cfgpin(S5PV210_GPE1(i), S3C_GPIO_SFN(2)); 

s3c_gpio_setpull(S5PV210_GPE1(i), S3C_GPIO_PULL_NONE); 

} /* CAM B port(b0011) : DATA[0-7] */ 


for (i = 0; i < 8; i++) { 

s3c_gpio_cfgpin(S5PV210_GPJ0(i), S3C_GPIO_SFN(3));

 s3c_gpio_setpull(S5PV210_GPJ0(i), S3C_GPIO_PULL_NONE); 

} /* CAM B port(b0011) : PCLK, VSYNC, HREF, FIELD, CLCKOUT */ 


for (i = 0; i < 5; i++) { 

s3c_gpio_cfgpin(S5PV210_GPJ1(i), S3C_GPIO_SFN(3)); 

s3c_gpio_setpull(S5PV210_GPJ1(i), S3C_GPIO_PULL_NONE); 

}


4)接口函數 ) 

FIMC 的主要回調函數如下,實現在 fimc_v4l2.c 中:


const struct v4l2_ioctl_ops fimc_v4l2_ops = {
        .vidioc_querycap                = fimc_querycap,
        .vidioc_reqbufs                 = fimc_reqbufs,
        .vidioc_querybuf                = fimc_querybuf,
        .vidioc_g_ctrl                  = fimc_g_ctrl,
        .vidioc_s_ctrl                  = fimc_s_ctrl,
        .vidioc_s_ext_ctrls             = fimc_s_ext_ctrls,
        .vidioc_cropcap                 = fimc_cropcap,
        .vidioc_g_crop                  = fimc_g_crop,
        .vidioc_s_crop                  = fimc_s_crop,
        .vidioc_streamon                = fimc_streamon,
        .vidioc_streamoff               = fimc_streamoff,
        .vidioc_qbuf                    = fimc_qbuf,
        .vidioc_dqbuf                   = fimc_dqbuf,
        .vidioc_enum_fmt_vid_cap        = fimc_enum_fmt_vid_capture,
        .vidioc_g_fmt_vid_cap           = fimc_g_fmt_vid_capture,
        .vidioc_s_fmt_vid_cap           = fimc_s_fmt_vid_capture,
        .vidioc_try_fmt_vid_cap         = fimc_try_fmt_vid_capture,
        .vidioc_enum_input              = fimc_enum_input,
        .vidioc_g_input                 = fimc_g_input,
        .vidioc_s_input                 = fimc_s_input,
        .vidioc_g_parm                  = fimc_g_parm,
        .vidioc_s_parm                  = fimc_s_parm,
        .vidioc_queryctrl               = fimc_queryctrl,
        .vidioc_querymenu               = fimc_querymenu,
        .vidioc_g_fmt_vid_out           = fimc_g_fmt_vid_out,
        .vidioc_s_fmt_vid_out           = fimc_s_fmt_vid_out,
        .vidioc_try_fmt_vid_out         = fimc_try_fmt_vid_out,
        .vidioc_g_fbuf                  = fimc_g_fbuf,
        .vidioc_s_fbuf                  = fimc_s_fbuf,
        .vidioc_try_fmt_vid_overlay     = fimc_try_fmt_overlay,
        .vidioc_g_fmt_vid_overlay       = fimc_g_fmt_vid_overlay,
        .vidioc_s_fmt_vid_overlay       = fimc_s_fmt_vid_overlay,
};
                                                              


5)寄存器操作(fimc_regs.c) )寄存器操作( )

對於寄存器的操作,實現都在 fimc_regs.c 文件中,如

int fimc_hwset_camera_source(struct fimc_control *ctrl) {


 struct s3c_platform_camera *cam = ctrl->cam; u32 cfg = 0;


cfg |= S3C_CISRCFMT_ITU601_8BIT; cfg |= cam->order422;

if (cam->type == CAM_TYPE_ITU)

cfg |= cam->fmt;

cfg |= S3C_CISRCFMT_SOURCEHSIZE(cam->width); 

cfg |= S3C_CISRCFMT_SOURCEVSIZE(cam->height);


writel(cfg, ctrl->regs + S3C_CISRCFMT);

return 0; 

}


int fimc_hwset_enable_irq(struct fimc_control *ctrl, int overflow, int level) { 

u32 cfg = readl(ctrl->regs + S3C_CIGCTRL);


cfg &= ~(S3C_CIGCTRL_IRQ_OVFEN | S3C_CIGCTRL_IRQ_LEVEL); 

cfg |= S3C_CIGCTRL_IRQ_ENABLE;


if (overflow) cfg |= S3C_CIGCTRL_IRQ_OVFEN;

if (level) cfg |= S3C_CIGCTRL_IRQ_LEVEL;

writel(cfg, ctrl->regs + S3C_CIGCTRL);

return 0; 

}


3. Sensor 驅動

1)簡介, 

)簡介 本方案中使用了兩個攝像頭模組: MT9P111 和 S5K6AAFX。 其中 MT9P111 是 APTINA 公司推出的 1/4 英寸光學格式 5M 單芯片傳感器,用作後攝像頭;S5K6AAFX 是三星出的 1.3M CMOS 高清圖像傳感器,用作前攝像頭。 

2)參數設置 

)參數設置 MT9P111 的參數設置

#ifdef MT9P111_ENABLED 

static struct mt9p111_platform_data mt9p111_plat = { 

.default_width = 1024, 

.default_height = 600, 

.pixelformat = V4L2_PIX_FMT_UYVY, 

.freq = 24000000, 

.is_mipi = 0, 

}; 


static struct i2c_board_info mt9p111_i2c_info = { 

I2C_BOARD_INFO("MT9P111", 0x3D),//0x7a,0x7b .

platform_data = &mt9p111_plat, 

}; 


static struct s3c_platform_camera mt9p111 = { 

.id = CAMERA_PAR_A,

.type = CAM_TYPE_ITU,

.fmt =  = ITU_601_YCBCR422_8BIT,


.order422 = CAM_ORDER422_8BIT_CBYCRY, 

.i2c_busnum 

.info = 7,


= &mt9p111_i2c_info, = V4L2_PIX_FMT_UYVY, = "xusbxti",

.pixelformat .srclk_name

.clk_name = "sclk_cam0",

.clk_rate = 24000000,

.line_length 

.width 

.height

.window 

.left = 0, 

.top = 0, 

.width 

.height 

}, = 1024, = 600, = 1920, = 1024, = 600, ={


/* Polarity */ .inv_pclk = 0, .inv_vsync .inv_href = 0, .inv_hsync = 0, = 0,

.initialized = 0, .cam_power }; #endif = smdkv210_cam0_power,

S5K6AAFX 的參數設置

#ifdef S5K6AAFX_ENABLED 

static struct s5k6aafx_platform_data s5k6aafx_plat = { 

.default_width = 800, 

.default_height = 600,

 .pixelformat = V4L2_PIX_FMT_YUYV, 

.freq = 24000000,


.is_mipi = 0, 

}; 

static struct i2c_board_info s5k6aafx_i2c_info = {


I2C_BOARD_INFO("s5k6aafx", 0x3c), 

.platform_data = &s5k6aafx_plat,

 }; 


static struct s3c_platform_camera s5k6aafx = { 

.id 

.type 

.fmt = CAMERA_PAR_B, = CAM_TYPE_ITU, = ITU_601_YCBCR422_8BIT,


.order422 = CAM_ORDER422_8BIT_YCBYCR, .i2c_busnum .info = 4,

= &s5k6aafx_i2c_info, = V4L2_PIX_FMT_YUYV, = "xusbxti",

.pixelformat .srclk_name

.clk_name = "sclk_cam1", .clk_rate = 24000000, .line_length = 1280,

/* default resol for preview kind of thing */ 

.width .height 

.window .left = 0, 

.top = 0, .width 

.height }, /* Polarity */

 .inv_pclk = 0, 

.inv_vsync = 0, = 800, = 600, = 800, = 600, ={


.inv_href = 0, 

.inv_hsync = 0,


.initialized = 0, 

.cam_power }; 

#endif = smdkv210_cam1_power,


3)數據結構 )

(未註釋)

struct v4l2_subdev { 

struct list_head list; 

struct module *owner; 

u32 flags; 

struct v4l2_device *v4l2_dev; 

const struct v4l2_subdev_ops *ops; /* name must be unique */ 

char name[V4L2_SUBDEV_NAME_SIZE]; /* can be used to group similar subdevs, value is driver-specific */ 

u32 grp_id; /* pointer to private data */ 

void *priv;

 }; 

#endif


4)接口函數 )

(未註釋)

static const struct v4l2_subdev_core_ops mt9p111_core_ops = { 

.init = mt9p111_init, /* initializing API */


.s_config = mt9p111_s_config, /* Fetch platform data */ 

.queryctrl = mt9p111_queryctrl, 

.querymenu = mt9p111_querymenu,

 .g_ctrl = mt9p111_g_ctrl, 

.s_ctrl = mt9p111_s_ctrl,

 };


static const struct v4l2_subdev_video_ops mt9p111_video_ops = { 

// .s_crystal_freq = mt9p111_s_crystal_freq, 

.g_fmt = mt9p111_g_fmt, 

.s_fmt = mt9p111_s_fmt,

 .enum_framesizes = mt9p111_enum_framesizes, // //

 .enum_frameintervals = mt9p111_enum_frameintervals, 

.enum_fmt = mt9p111_enum_fmt, 

.try_fmt = mt9p111_try_fmt, 

.g_parm = mt9p111_g_parm, 

.s_parm = mt9p111_s_parm, 

.s_stream = mt9p111_s_stream, };


static const struct v4l2_subdev_ops mt9p111_ops = { 

.core = &mt9p111_core_ops, 

.video = &mt9p111_video_ops, 

};

 #endif

發佈了39 篇原創文章 · 獲贊 22 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章