FIMC這個名字應該是從S5PC1x0開始出現的。在s5pv210里面的定義是攝像頭接口。可是它相同具有圖像數據顏色空間轉換的作用。
而exynos4412對它的定義看起來更清楚些,攝像頭接口被定義為FIMC-LITE 。顏色空間轉換的硬件結構被定義為FIMC-IS。
不多說了,我們先來看看Linux3.0.8 三星的BSP包中與fimc驅動相關的文件。
上面的源代碼文件組成了整個fimc的驅動框架。
通過.c文件的命名也大致能夠推測到FIMC的幾個用途:
1、Capture ,Camera Interface 用于控制Camera,及m2m操作
2、Output,這個用途能夠簡單看成:僅僅使用了FIMC的m2m功能。這里fimc實際上就成了一個帶有顏色空間轉換功能的快速DMA。
3、Overlay,比方Android 的Overlay就依賴了FIMC的這個功能,能夠簡單把它看作是個m2fb,當然實質上還是m2m。
清楚FIMC的大致用途了。再來說說。每一個C文件在FIMC驅動框架中扮演了何種角色:
csis.c文件,用于MIPI 接口的攝像頭設備,這里不多說什么了。
fimc_dev.c 是驅動中對FIMC硬件設備最高層的抽象,這在后面會具體介紹。
fimc_v4l2.c linux驅動中 。將fimc 設備的功能操作接口(Capture,output,Overlay)。用v4l2框架封裝。在應用層用過攝像頭設備,或在應用層使用FMIC設備完畢過m2m操作的朋友應該都清楚,fimc經層層封裝后終于暴露給用戶空間的是v4l2 標準接口的設備文件 videoX。 這里面也引出了一個我們應該關注的問題:Fimc設備在軟件層上是怎樣同攝像頭設備關聯的。
fimc_capture.c 實現對camera Interface 的控制操作。它實現的基礎依賴硬件相關的攝像頭驅動(eg.ov965X.c / ov5642.c 等)。
而且提供以下函數接口,由fimc_v4l2.c文件進一步封裝
int fimc_g_parm(struct file *file, void*fh, struct v4l2_streamparm *a)
int fimc_s_parm(struct file *file, void*fh, struct v4l2_streamparm *a)
intfimc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc)
intfimc_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qm)
intfimc_enum_input(struct file *file, void *fh, struct v4l2_input *inp)
intfimc_g_input(struct file *file, void *fh, unsigned int *i)
intfimc_release_subdev(struct fimc_control *ctrl)
intfimc_s_input(struct file *file, void *fh, unsigned int i)
intfimc_enum_fmt_vid_capture(struct file *file, void *fh,struct v4l2_fmtdesc *f)
intfimc_g_fmt_vid_capture(struct file *file, void *fh, struct v4l2_format *f)
intfimc_s_fmt_vid_capture(struct file *file, void *fh, struct v4l2_format *f)
intfimc_try_fmt_vid_capture(struct file *file, void *fh, struct v4l2_format *f)
intfimc_reqbufs_capture(void *fh, struct v4l2_requestbuffers *b)
intfimc_querybuf_capture(void *fh, struct v4l2_buffer *b)
intfimc_g_ctrl_capture(void *fh, struct v4l2_control *c)
intfimc_s_ctrl_capture(void *fh, struct v4l2_control *c)
intfimc_s_ext_ctrls_capture(void *fh, struct v4l2_ext_controls *c)
intfimc_cropcap_capture(void *fh, struct v4l2_cropcap *a)
intfimc_g_crop_capture(void *fh, struct v4l2_crop *a)
intfimc_s_crop_capture(void *fh, struct v4l2_crop *a)
intfimc_start_capture(struct fimc_control *ctrl)
intfimc_stop_capture(struct fimc_control *ctrl)
intfimc_streamon_capture(void *fh)
intfimc_streamoff_capture(void *fh)
intfimc_qbuf_capture(void *fh, struct v4l2_buffer *b)
intfimc_dqbuf_capture(void *fh, struct v4l2_buffer *b)
fimc_output.c 實現fimc m2m操作,須要用FIMC實現硬件顏色空間轉換的時候,這個文件中的函數就派上作用了,另外在fimc 用于Capture 和 overlay 過程本質上也包括m2m操作。因此除了提供功能函數接口,由fimc_v4l2.c文件進一步封裝。另外還提供了一些功能函數供fimc_dev.c調用。比方用于設置一個m2m過程的srcAddr(源地址) 和 dstAddr(目的地址)。
這部分接口太多就不貼出來了。
fimc_overlay.c 實現fimc overlay操作。
相同提供函數接口。由fimc_v4l2.c文件進一步封裝。
fimc_regs.c Fimc硬件相關操作,基本寄存器配置等。這個文件提供函數接口供fimc_capture.c、fimc_output.c、fimc_overlay.c調用。
通過剛才的分析。能夠總結出以下的源代碼結構圖:
好了,框架有了。再來看源代碼就輕松多了
接下來,先來看看FIMC設備的注冊過程。
以FIMC-0為例。說說/dev/video0 這個設備文件是怎么出來的。
先看幾個關鍵結構:
首先是 s3c_platform_fimcfimc_plat_lsi;也就是抽象fimc模塊的數據結構,fimc_plat_lsi還包括了一個.camera成員。該結構初始化例如以下
static struct s3c_platform_fimc fimc_plat_lsi = {
.srclk_name = 'mout_mpll',
.clk_name = 'sclk_fimc',
.lclk_name = 'fimc',
.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_TVP5150
&tvp5150,
#endif
#ifdef CONFIG_VIDEO_OV9650
&ov9650,
#endif
},
.hw_ver = 0x43,
};
能夠看到在s3c_platform_fimc中有一個camera成員。
這里重點看一下ov9650.展開ov9650
static struct s3c_platform_camera ov9650 = {
#ifdef CAM_ITU_CH_A
.id = CAMERA_PAR_A,
#else
.id = CAMERA_PAR_B,
#endif
.type = CAM_TYPE_ITU,
.fmt = ITU_601_YCBCR422_8BIT,
.order422 = CAM_ORDER422_8BIT_YCBYCR,
.i2c_busnum = 0,
.info = &ov9650_i2c_info,
.pixelformat = V4L2_PIX_FMT_YUYV,
.srclk_name = 'mout_mpll',
/* .srclk_name = 'xusbxti', */
.clk_name = 'sclk_cam1',
.clk_rate = 40000000,
.line_length = 1920,
.width = 1280,
.height = 1024,
.window = {
.left = 0,
.top = 0,
.width = 1280,
.height = 1024,
},
/* Polarity */
.inv_pclk = 1,
.inv_vsync = 1,
.inv_href = 0,
.inv_hsync = 0,
.initialized = 0,
.cam_power = ov9650_power_en,
};
這個結構體。實現了對ov9650攝像頭硬件結構的抽象。定義了攝像頭的關鍵參數和基本特性。
由于fimc設備在linux3.0.8內核中作為一個平臺設備載入。而上面提到的s3c_platform_fimcfimc_plat_lsi僅是fimc的抽象數據而非設備。
這就須要將抽象fimc的結構體作為fimc platform_device 的一個私有數據。
所以就有了以下的過程。s3c_platform_fimcfimc_plat_lsi 結構在板級設備初始化XXX_machine_init(void) 過程作為s3c_fimc0_set_platdata 的實參傳入。
之后fimc_plat_lsi就成為了fimc設備的platform_data。
s3c_fimc0_set_platdata(&fimc_plat_lsi);
s3c_fimc1_set_platdata(&fimc_plat_lsi);
s3c_fimc2_set_platdata(&fimc_plat_lsi);
以s3c_fimc0_set_platdata為例展開
void __init s3c_fimc0_set_platdata(struct s3c_platform_fimc *pd)
{
struct s3c_platform_fimc *npd;
if (!pd)
pd = &default_fimc0_data;
npd = kmemdup(pd, sizeof(struct s3c_platform_fimc), GFP_KERNEL);
if (!npd)
printk(KERN_ERR '%s: no memory for platform datan', __func__);
else {
if (!npd->cfg_gpio)
npd->cfg_gpio = s3c_fimc0_cfg_gpio;
if (!npd->clk_on)
npd->clk_on = s3c_fimc_clk_on;
if (!npd->clk_off)
npd->clk_off = s3c_fimc_clk_off;
npd->hw_ver = 0x45;
/* starting physical address of memory region */
npd->pmem_start = s5p_get_media_memory_bank(S5P_MDEV_FIMC0, 1);
/* size of memory region */
npd->pmem_size = s5p_get_media_memsize_bank(S5P_MDEV_FIMC0, 1);
s3c_device_fimc0.dev.platform_data = npd;
}
}
最后一句是關鍵 s3c_device_fimc0.dev.platform_data = npd;
看一下s3c_device_fimc0定義:
struct platform_device s3c_device_fimc0 = {
.name = 's3c-fimc',
.id = 0,
.num_resources = ARRAY_SIZE(s3c_fimc0_resource),
.resource = s3c_fimc0_resource,
};
fimc的抽象數據。則作為它的私有數據被包括進了s3c_device_fimc0這個結構中。到這里才完畢了FIMC平臺設備的終于定義。這個平臺設備的定義s3c_device_fimc0又被加入到了整個硬件平臺的 platform_device 列表中,終于在XXX_machine_init(void) 函數中調用platform_add_devices(mini210_devices, ARRAY_SIZE(mini210_devices)); 完畢全部platform_device 的注冊:
static struct platform_device *mini210_devices[] __initdata = {
&s3c_device_adc,
&s3c_device_cfcon,
&s3c_device_nand,
。。。
&s3c_device_fb,
&mini210_lcd_dev,
#ifdef CONFIG_VIDEO_FIMC
&s3c_device_fimc0,
&s3c_device_fimc1,
&s3c_device_fimc2,
}
platform_add_devices(mini210_devices, ARRAY_SIZE(mini210_devices));
platform_device 被載入后,等待與之匹配的platform_driver。
若此時fimc driver 的驅動模塊被載入。
這個時候,fimc_dev.c文件中的static int __devinit fimc_probe(structplatform_device *pdev) 函數上場了。
static int __devinit fimc_probe(struct platform_device *pdev)
{
struct s3c_platform_fimc *pdata;
struct fimc_control *ctrl;
struct clk *srclk;
int ret;
if (!fimc_dev) {
fimc_dev = kzalloc(sizeof(*fimc_dev), GFP_KERNEL);
if (!fimc_dev) {
dev_err(&pdev->dev, '%s: not enough memoryn',
__func__);
return -ENOMEM;
}
}
ctrl = fimc_register_controller(pdev);
if (!ctrl) {
printk(KERN_ERR '%s: cannot register fimcn', __func__);
goto err_alloc;
}
pdata = to_fimc_plat(&pdev->dev);
if (pdata->cfg_gpio)
pdata->cfg_gpio(pdev);
#ifdef REGULATOR_FIMC
/* Get fimc power domain regulator */
ctrl->regulator = regulator_get(&pdev->dev, 'pd');
if (IS_ERR(ctrl->regulator)) {
fimc_err('%s: failed to get resource %sn',
__func__, 's3c-fimc');
return PTR_ERR(ctrl->regulator);
}
#endif //REGULATOR_FIMC
/* fimc source clock */
srclk = clk_get(&pdev->dev, pdata->srclk_name);
if (IS_ERR(srclk)) {
fimc_err('%s: failed to get source clock of fimcn',
__func__);
goto err_v4l2;
}
/* fimc clock */
ctrl->clk = clk_get(&pdev->dev, pdata->clk_name);
if (IS_ERR(ctrl->clk)) {
fimc_err('%s: failed to get fimc clock sourcen',
__func__);
goto err_v4l2;
}
/* set parent for mclk */
clk_set_parent(ctrl->clk, srclk);
/* set rate for mclk */
clk_set_rate(ctrl->clk, pdata->clk_rate);
/* V4L2 device-subdev registration */
ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);
if (ret) {
fimc_err('%s: v4l2 device register failedn', __func__);
goto err_fimc;
}
/* things to initialize once */
if (!fimc_dev->initialized) {
ret = fimc_init_global(pdev);
if (ret)
goto err_v4l2;
}
/* video device register */
ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);
if (ret) {
fimc_err('%s: cannot register video drivern', __func__);
goto err_v4l2;
}
video_set_drvdata(ctrl->vd, ctrl);
ret = device_create_file(&(pdev->dev), &dev_attr_log_level);
if (ret < 0) {
fimc_err('failed to add sysfs entriesn');
goto err_global;
}
printk(KERN_INFO 'FIMC%d registered successfullyn', ctrl->id);
return 0;
err_global:
video_unregister_device(ctrl->vd);
err_v4l2:
v4l2_device_unregister(&ctrl->v4l2_dev);
err_fimc:
fimc_unregister_controller(pdev);
err_alloc:
kfree(fimc_dev);
return -EINVAL;
}
在fimc_probe函數中有這么一段
if(!fimc_dev->initialized) {
ret = fimc_init_global(pdev);
if (ret)
goto err_v4l2;
}
這段代碼運行過程:首先推斷fimc是否已經被初始化完畢(此時FIMC是忙狀態的),假設沒有被初始化,則運行fimc_init_global(pdev);函數,它的作用是先推斷平臺數據中是否初始化了攝像頭結構(即前面提到的.camera成員),從平臺數據中獲得攝像頭的時鐘頻率并將平臺數據中內嵌的s3c_platform_camera結構數據保存到該驅動模塊全局的fimc_dev中,感興趣的朋友能夠展開這個函數看一下,這里就不再貼出來了。
緊接著這段代碼還運行了兩個很關鍵的過程:
ret= v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);
if (ret) {
fimc_err('%s: v4l2device register failedn', __func__);
goto err_fimc;
}
這個函數里的核心完畢了對v4l2_dev->subdev鏈表頭的初始化,并將ctrl->v4l2_dev關聯到pdev->dev結構的私有數據的driver_data成員中(即完畢了pdev->dev->p->driver_data= ctrl->v4l2_dev; )。也就是實現了v4l2_dev向內核結構注冊的過程。
上一篇:kernel 2.6.35.7向S5PV210移植
下一篇:s5pv210 uboot-2012-10移植(二) 之能夠啟動進入控制臺
推薦閱讀最新更新時間:2025-04-23 15:30




