三星SOC的显示控制器及framebuffer驱动s3c-fb.c中probe函数分析
三星SOC芯片上集成了顯示控制器(有稱作fimd的,Fully Interactive Mobile Display)。它的驅動分為mainline版本的和legacy版本的,mainline版本的是通用的,由Ben Dooks <ben@simtec.co.uk> 管理著的,代碼比較規范。下面我們來分析一下這個驅動,這個驅動在driver/video/里,文件名是s3c-fb.c 與其相關的,主要還有arch/arm/plat-s5p/dev-fimd0.c 和arch/arm/mach-XXXX/setup-fimd0.c 。 前者是定義platform device的,后者是定義一些比較depend on 具體芯片的東西,例如GPIO。
我們這里主要分析s3c-fb.c中的probe函數, probe函數里最重要的是s3c_fb_probe_win函數,一會也一并分析
首先解釋一個概念,很多書中有framebuffer這個概念,但是在三星的顯示控制器文檔或代碼中,常出現win或window的概念,顯示控制器可以控制0~5個windows,代碼中分給它們分別編號win0, win1,win2......這里一張win或window就對應一個framebuffer, 每個framebuffer有自己的一個FBI(fb_info)結構,
代碼中, 顯示控制器是s3c_fb結構體, window是s3c_fb_win結構體。
代碼中有兩種data,一種是platform data(在板文件中定義),另一種是driver data(在驅動文件中定義),在它們各自的結構體里面,又可以分為兩部份,一是用于sfb的data, 另一是用于win的data。
framebuffer是fb_info結構體,里面主要存儲設置參數的數據結構有兩個,fb_var_screeninfo和fb_fix_screeninfo結構體。
************************************************ platform data***************************************************
static struct s3c_fb_platdata smdkv210_lcd0_pdata __initdata = {
.win[0] = &smdkv210_fb_win0, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? -------->用于win的部分,下文稱作“platform data中win的部分”
.vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, ? ? ?-------->下面開始是sfb的部分,
.vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
.setup_gpio = s5pv210_fb_gpio_setup_24bpp,
};
************************************************ driver data***************************************************
static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = {
.variant = { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ---------->用于sfb的driverdata
.nr_windows = 5,
.vidtcon = VIDTCON0,
.wincon = WINCON(0),
.winmap = WINxMAP(0),
.keycon = WKEYCON,
.osd = VIDOSD_BASE,
.osd_stride = 16,
.buf_start = VIDW_BUF_START(0),
.buf_size = VIDW_BUF_SIZE(0),
.buf_end = VIDW_BUF_END(0),
.palette = {
[0] = 0x2400,
[1] = 0x2800,
[2] = 0x2c00,
[3] = 0x3000,
[4] = 0x3400,
},
.has_shadowcon= 1,
.has_blendcon = 1,
.has_alphacon = 1,
.has_clksel = 1,
.has_fixvclk = 1,
},
.win[0] = &s3c_fb_data_s5p_wins[0], ? ? ? ? ? ? ? ? ? ? ? ? ? ?---------->用于各個win的部分,下文稱作“driver data中win的部份”
.win[1] = &s3c_fb_data_s5p_wins[1],
.win[2] = &s3c_fb_data_s5p_wins[2],
.win[3] = &s3c_fb_data_s5p_wins[3],
.win[4] = &s3c_fb_data_s5p_wins[4],
};
************************************************ s3c_fb_probe() ***************************************************
static int __devinit s3c_fb_probe(struct platform_device *pdev)
{
const struct platform_device_id *platid; ? ? ? ? ? ? ? ? ? ?-------> 因為一個驅動要適合很多版本的設備,每個版本的設備的設置 參數都不一樣,所以要用到platid來選擇哪個版本的設備,像“s5pv210-fb”, "s3c2443-fb"..., 這些就是platid(也在s3c-fb.c中定義了), 也表明了,這個驅動能適合于這些設備。
struct s3c_fb_driverdata *fbdrv; ? ? ? ? ? ? ? ? ? ? ? ? ----> driver data
struct device *dev = &pdev->dev;
struct s3c_fb_platdata *pd; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ----> platform data
struct s3c_fb *sfb; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? -----> 一個最重要的數據結據, 它代表了一個顯示控制器,顯示控制器的所有東東都放在這里了。但這里把它做成一個局部變量了。
struct resource *res; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?-----> 資源
int win;
int default_win;
...
platid = platform_get_device_id(pdev);??----> 從platform device 里 的id_entry 變量中獲取platid,由一個宏實現
fbdrv = (struct s3c_fb_driverdata *)platid->driver_data;? ? ---> 獲取platid對應的driver data,driver data在s3c-fb.c中定義,主要是一定設置參數
...
pd = pdev->dev.platform_data; ?--> 獲取platform data,它在板文件中定義,這個data里包含了關于顯示控制器的數據,也包含了win的數據。
...
sfb = kzalloc(sizeof(struct s3c_fb), GFP_KERNEL); ? ? ? ? ------> 給這個最重要的結構分配內存空間
...
sfb->dev = dev; ? ? ? ? ? ? ? ? ? ? ?---> 向sfb 填入 顯示控制器的 device 結構體
sfb->pdata = pd; ? ? ? ? ? ? ? ? ? --->?向sfb 填入 顯示控制器的 platform data 結構體
sfb->variant = fbdrv->variant; ? ? ?---> driver data結構體里 有 variant成員, 具體variant可以看下面s3c_fb_variant結構。
...
sfb->bus_clk = clk_get(dev, "lcd"); -->用"lcd"這個名字,去clock文件中找到自己的bus clock
...
clk_enable(sfb->bus_clk); ---> bus clock有什么用,我還不太清楚
...
if (!sfb->variant.has_clksel) {
sfb->lcd_clk = clk_get(dev, "sclk_fimd"); --> 如果driver data里沒定義 源時鐘, 就用“sclk_fimd”此名字去clock文件找到自己的源時鐘
...
clk_enable(sfb->lcd_clk);
....
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); --> 獲取資源的物理起始地址,終地址,大小,類型等,放在res結構中。實際上是寄存器們的物理起,終地址。
...
sfb->regs_res = request_mem_region(res->start, resource_size(res), dev_name(dev)); ---> 分配內存
...
sfb->regs = ioremap(res->start, resource_size(res)); ----> 內存映射, 將寄存器的訪問地址映射到剛才分配的內存上, sfb->regs為起始地址。
... // 中斷的請求略
platform_set_drvdata(pdev, sfb); ---> 將sfb 填入pdev->dev->p->driverdata 結構體中
...
pd->setup_gpio(); ? ? ? ? ? ? ? ? ? ? ?--> 執行setup_gpio函數,此函數在上面說的 setup_fimd0.c 中定義了。用來配置GPIO端口給FIMD使用。
...
writel(pd->vidcon1, sfb->regs + VIDCON1); ? ? ? ? ? ? -->設置VIDCON1 寄存器
/* set video clock running at under-run */ ? ??
if (sfb->variant.has_fixvclk) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?--> run vclk
reg = readl(sfb->regs + VIDCON1);
reg &= ~VIDCON1_VCLK_MASK;
reg |= VIDCON1_VCLK_RUN;
writel(reg, sfb->regs + VIDCON1);
}
for (win = 0; win < fbdrv->variant.nr_windows; win++)
s3c_fb_clear_win(sfb, win); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?---> 將各個window的wincon寄存器清0,VIDOSDxA,?VIDOSDxB,?VIDOSDxC清0,禁止update各個window的shadow
/* initialise colour key controls */
for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) {
void __iomem *regs = sfb->regs + sfb->variant.keycon;
regs += (win * 8);
writel(0xffffff, regs + WKEYCON0);
writel(0xffffff, regs + WKEYCON1);
}
/* we have the register setup, start allocating framebuffers */
default_win = sfb->pdata->default_win;? ? ? ? ? ? ? ---> platform data 在板文件中定義了
for (i = 0; i < fbdrv->variant.nr_windows; i++) {
win = i;
if (i == 0)
win = default_win;
if (i == default_win)
win = 0;
if (!pd->win[win])
continue;
if (!pd->win[win]->win_mode.pixclock)-----> 像素時鐘
s3c_fb_missing_pixclock(&pd->win[win]->win_mode);----->如果像素時鐘沒預先定義,則由預先設定的刷新率和LCD參數來計算像素時鐘
ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win],?&sfb->windows[win]); ? ? ? ----->分配 及 注冊framebuffer的重要函數,下面會詳細說
if (ret < 0) {
dev_err(dev, "failed to create window %d\n", win);
for (; win >= 0; win--)
s3c_fb_release_win(sfb, sfb->windows[win]); ? ? ? ? ? ? ? ? ? ? ----> 注冊不成功的話就釋放之前注冊成功過的window
goto err_irq;
}
}
}
...
platform_set_drvdata(pdev, sfb);---> 再一次將sfb 填入pdev->dev->p->driverdata 結構體中,之前曾經這樣操作過一次,現在再來一次,是因為sfb里的數據更新了很多
? ? ? ?...
#ifdef CONFIG_HAS_EARLYSUSPEND--> 如果有定義earlysuspend的話, 則注冊early suspend函數
sfb->early_suspend.suspend = s3c_fb_early_suspend;
sfb->early_suspend.resume = s3c_fb_late_resume;
sfb->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
register_early_suspend(&sfb->early_suspend);
#endif
return 0;
?...
還有一些錯訓處理在此略過
}??---- > probe函數完成
************************************************ s3c_fb_probe_win() *************************************************************
static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, ? ? ? ? ? --->將顯示控制器的結構體作為參數傳進來,它有寄存器起始地址等豐富信息;還將win的號碼也作為參數傳進來
? ? ?struct s3c_fb_win_variant *variant, ? ? ? ? ? ?----> driver data中win的部份, 它是由fbdrv->win[win]作參數傳過來的。
? ? ?struct s3c_fb_win **res) ? ? ? ---> per window private data for each framebuffer,它里面含有指向FBI(fb_info)結構體的針指
{
struct fb_var_screeninfo *var;
struct fb_videomode *initmode;
struct s3c_fb_pd_win *windata; ? ?-->per window setup data, ?也就是platform data中win的部份
struct s3c_fb_win *win;
struct fb_info *fbinfo;
int palette_size;
int ret;
init_waitqueue_head(&sfb->vsync_info.wait);??---> 初始化等待隊列頭
palette_size = variant->palette_sz * 4; ? ? ? ? ---> 調色板大小 , 這方面內容我還不了解,為什么乘以4?
fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +? palette_size * sizeof(u32), sfb->dev);? ?---> 重頭戲來了, 分配fb_info結構體,返回一個fb_info結構體地址,這個結構體現在沒什么內容,只賦值了par(win的起始地址)和device (父設備)兩個變量,
windata = sfb->pdata->win[win_no];?? ? ? ? --->windata指向 platform data中win的部份?
initmode = &windata->win_mode;
...
win = fbinfo->par;
*res = win;?? ? ? ? --->par就是win的起始地址,現在把起始地址給*res,那么*res就是指向s3c_fb_win的指針?
var = &fbinfo->var;?? ? ? ? ---> 現在fbinfo->var還是空的, 只是將地址給var而已?
win->variant = *variant;?? ? ? ? --->將win的參數填進win->variant里?
win->fbinfo = fbinfo;?? ? ? ? ---讓win->fbinfo指向這個FBI結構實體?
win->parent = sfb;?? ? ? ? ---win的parent是顯示控制器,所以它指向sfb結構體>?
win->windata = windata;? ? ? ? --->讓win->windata指向 platform data中win的部分?
win->index = win_no; ? ? ?
win->palette_buffer = (u32 *)(win + 1); ? ? ? --->這個也不太理解?
ret = s3c_fb_alloc_memory(sfb, win);? ? ----> 下面詳解
...
/* setup the r/b/g positions for the window's palette */ ? ? ? ? ? ? ? ?--->設置調色板, 未理解
if (win->variant.palette_16bpp) {
/* Set RGB 5:6:5 as default */
win->palette.r.offset = 11;
win->palette.r.length = 5;
win->palette.g.offset = 5;
win->palette.g.length = 6;
win->palette.b.offset = 0;
win->palette.b.length = 5;
} else {
/* Set 8bpp or 8bpp and 1bit alpha */
win->palette.r.offset = 16;
win->palette.r.length = 8;
win->palette.g.offset = 8;
win->palette.g.length = 8;
win->palette.b.offset = 0;
win->palette.b.length = 8;
}
/* setup the initial video mode from the window */
fb_videomode_to_var(&fbinfo->var, initmode); ? ? ?--> 給FBI填上各個參數,此函數詳見appendix
fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
fbinfo->fix.accel= FB_ACCEL_NONE;
fbinfo->var.activate= FB_ACTIVATE_NOW;
fbinfo->var.vmode= FB_VMODE_NONINTERLACED;
fbinfo->var.bits_per_pixel = windata->default_bpp;
fbinfo->var.width= windata->width;
fbinfo->var.height= windata->height;
fbinfo->fbops = &s3c_fb_ops;?? ?---->對framebuffer的操作, 詳見appendix
fbinfo->flags = FBINFO_FLAG_DEFAULT;
fbinfo->pseudo_palette ?= &win->pseudo_palette;?
/* prepare to actually start the framebuffer */
ret = s3c_fb_check_var(&fbinfo->var, fbinfo); ? ?-->檢查可變參數?Framebuffer layer call to verify the given information and allow us to?update various information depending on the hardware capabilities.
...
ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1);
if (ret == 0)
fb_set_cmap(&fbinfo->cmap, fbinfo);
else
dev_err(sfb->dev, "failed to allocate fb cmap\n");
s3c_fb_set_par(fbinfo);
...
ret = register_framebuffer(fbinfo);
...
return 0;
} s3c_fb_probe_win結束
************************************************ s3c_fb_alloc_memory() ************************************************
static int __devinit s3c_fb_alloc_memory(struct s3c_fb *sfb,
struct s3c_fb_win *win)
{
struct s3c_fb_pd_win *windata = win->windata; ?---> platform data中win的部分
unsigned int real_size, virt_size, size;
struct fb_info *fbi = win->fbinfo; ? ? ---> 讓fbi指向FBI結構體
dma_addr_t map_dma;
? ? ...
real_size = windata->win_mode.xres * windata->win_mode.yres;
virt_size = windata->virtual_x * windata->virtual_y;? ? ---> 虛擬size
size = (real_size > virt_size) ? real_size : virt_size; 一張framebuffer的大小,是按虛擬分辨率和實際分辨率兩者中較大的來算的
size *= (windata->max_bpp > 16) ? 32 : windata->max_bpp;
size /= 8;?
fbi->fix.smem_len = size;-----> 要分配的內存大小,
size = PAGE_ALIGN(size); ? ?---- 頁對齊
... 關于CMA 與ION部分省略
fbi->screen_base = dma_alloc_writecombine(sfb->dev, size, ?&map_dma, GFP_KERNEL);? ?-->分配framebuffer的內存
memset(fbi->screen_base, 0x0, size); ? ?-->將framebuffer的內存清空為0
fbi->fix.smem_start = map_dma;
return 0;
} ?---->結束
*********************************************************Appendix******************************************************
struct s3c_fb_variant {
unsigned int is_2443:1;
unsigned shortnr_windows;
unsigned int vidtcon;
unsigned shortwincon;
unsigned shortwinmap;
unsigned shortkeycon;
unsigned shortbuf_start;
unsigned shortbuf_end;
unsigned shortbuf_size;
unsigned shortosd;
unsigned shortosd_stride;
unsigned shortpalette[S3C_FB_MAX_WIN];
unsigned int has_prtcon:1;
unsigned int has_shadowcon:1;
unsigned int has_blendcon:1;
unsigned int has_alphacon:1;
unsigned int has_clksel:1;
unsigned int has_fixvclk:1;
}
/**
?* fb_videomode_to_var - convert fb_videomode to fb_var_screeninfo
?* @var: pointer to struct fb_var_screeninfo
?* @mode: pointer to struct fb_videomode
?*/
void fb_videomode_to_var(struct fb_var_screeninfo *var,
const struct fb_videomode *mode)
{
var->xres = mode->xres;
var->yres = mode->yres;
var->xres_virtual = mode->xres;
var->yres_virtual = mode->yres;
var->xoffset = 0;
var->yoffset = 0;
var->pixclock = mode->pixclock;
var->left_margin = mode->left_margin;
var->right_margin = mode->right_margin;
var->upper_margin = mode->upper_margin;
var->lower_margin = mode->lower_margin;
var->hsync_len = mode->hsync_len;
var->vsync_len = mode->vsync_len;
var->sync = mode->sync;
var->vmode = mode->vmode & FB_VMODE_MASK;
}
static struct fb_ops s3c_fb_ops = {
.owner = THIS_MODULE,
.fb_open = s3c_fb_open,
.fb_release = s3c_fb_release,
.fb_check_var = s3c_fb_check_var,
.fb_set_par = s3c_fb_set_par,
.fb_blank = s3c_fb_blank,
.fb_setcolreg = s3c_fb_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_pan_display= s3c_fb_pan_display,
.fb_ioctl = s3c_fb_ioctl,
};
/**
?* struct s3c_fb_win - per window private data for each framebuffer.
?* @windata: The platform data supplied for the window configuration.
?* @parent: The hardware that this window is part of.
?* @fbinfo: Pointer pack to the framebuffer info for this window.
?* @varint: The variant information for this window.
?* @palette_buffer: Buffer/cache to hold palette entries.
?* @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/
?* @index: The window number of this window.
?* @palette: The bitfields for changing r/g/b into a hardware palette entry.
?*/
struct s3c_fb_win {
struct s3c_fb_pd_win*windata;
struct s3c_fb *parent;
struct fb_info*fbinfo;
struct s3c_fb_palettepalette;
struct s3c_fb_win_variant variant;
u32 *palette_buffer;
u32 pseudo_palette[16];
unsigned int index;
#ifdef CONFIG_ION_EXYNOS
struct ion_handle *fb_ion_handle;
#endif
};
總結
以上是生活随笔為你收集整理的三星SOC的显示控制器及framebuffer驱动s3c-fb.c中probe函数分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 英语学习/词典app行业top5简要分析
- 下一篇: 用高德地图实现点击地图添加标记点,获取该