LCD(六)显示控制器、framebuffer驱动、s3c-fb.c中probe函数分析
????????? 三星SOC芯片上集成了顯示控制器(有稱作fimd的,Fully Interactive Mobile Display)。它的驅動分為mainline版本的和legacy版本的,mainline版本的是通用的,由Ben Dooks <ben@simtec.co.uk> 管理著的,代碼比較規(guī)范。下面我們來分析一下這個驅動,這個驅動在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部分.vtiming = &smdkv210_lcd_timing, //時序部分.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的driver data.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_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 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 datastruct device *dev = &pdev->dev; struct s3c_fb_platdata *pd; //platform datastruct s3c_fb *sfb; //一個重要的數據結構,它代表了一個顯示控制器,顯示控制器的所有東西都//放在這里了,但是這里將其作為一個局部變量了struct resource *res; //資源int win;int ret = 0;u32 reg;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中定義,主要是一定設置參數if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) {dev_err(dev, "too many windows, cannot attach\n");return -EINVAL;}pd = pdev->dev.platform_data; //獲取platform data,它在板級文件中定義,//這個data里 包含了顯示控制器的數據,也包含了win的數據if (!pd) {dev_err(dev, "no platform data specified\n");return -EINVAL;}sfb = devm_kzalloc(dev, sizeof(struct s3c_fb), GFP_KERNEL); //分配內存空間if (!sfb) {dev_err(dev, "no memory for framebuffers\n");return -ENOMEM;}dev_dbg(dev, "allocate new framebuffer %p\n", sfb);printk(KERN_ERR "Here I am: %s:%i\n", __FILE__, __LINE__);sfb->dev = dev; //向sfb填入顯示控制器的device結構體sfb->pdata = pd; //向sfb填入顯示控制器的platform data結構體sfb->variant = fbdrv->variant; //driver data結構體里有variant成員,//具體variant可以看下面s3c_fb_variant結構spin_lock_init(&sfb->slock);sfb->bus_clk = devm_clk_get(dev, "lcd"); //通過"lcd"這個名字,去clock文件中找到自己的bus clockif (IS_ERR(sfb->bus_clk)) {dev_err(dev, "failed to get bus clock\n");return PTR_ERR(sfb->bus_clk);}clk_prepare_enable(sfb->bus_clk); //bus_clk具體用途待定if (!sfb->variant.has_clksel) {sfb->lcd_clk = devm_clk_get(dev, "sclk_fimd"); //如果driver data里沒定義 源時鐘,//就用"sclk_fimd",此名字去clock文件中找到自己的源時鐘if (IS_ERR(sfb->lcd_clk)) {dev_err(dev, "failed to get lcd clock\n");ret = PTR_ERR(sfb->lcd_clk);goto err_bus_clk;}clk_prepare_enable(sfb->lcd_clk);}pm_runtime_enable(sfb->dev);res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //獲取資源的物理起始地址、//終地址、大小、類型等,放在res結構中,實際上是寄存器門的物理起始地址sfb->regs = devm_ioremap_resource(dev, res); //內存映射,將寄存器的訪問地址映射到//剛才分配的內存上,sfb->regs為起始地址if (IS_ERR(sfb->regs)) {ret = PTR_ERR(sfb->regs);goto err_lcd_clk;}res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); //獲取設備中斷資源if (!res) {dev_err(dev, "failed to acquire irq resource\n");ret = -ENOENT;goto err_lcd_clk;}sfb->irq_no = res->start;ret = devm_request_irq(dev, sfb->irq_no, s3c_fb_irq,0, "s3c_fb", sfb);if (ret) {dev_err(dev, "irq request failed\n");goto err_lcd_clk;}dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs);platform_set_drvdata(pdev, sfb);//將sfb填入pdev->dev->p->driverdata結構體中pm_runtime_get_sync(sfb->dev);/* setup gpio and output polarity controls */pd->setup_gpio(); //執(zhí)行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 vclkreg = readl(sfb->regs + VIDCON1);reg &= ~VIDCON1_VCLK_MASK;reg |= VIDCON1_VCLK_RUN;writel(reg, sfb->regs + VIDCON1);}/* zero all windows before we do anything */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);}s3c_fb_set_rgb_timing(sfb); //設置LCD時序/* we have the register setup, start allocating framebuffers */for (win = 0; win < fbdrv->variant.nr_windows; win++) {if (!pd->win[win])continue;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]);//注冊不成功的話就釋放之前注冊成功過的windowgoto err_pm_runtime;}}platform_set_drvdata(pdev, sfb);//再一次將sfb填入pdev->dev->p->driverdata結構體中,//之前曾經這樣操作過一次,現在再來一次,是因為sfb里的數據更新了很多pm_runtime_put_sync(sfb->dev);return 0;err_pm_runtime:pm_runtime_put_sync(sfb->dev);err_lcd_clk:pm_runtime_disable(sfb->dev);if (!sfb->variant.has_clksel)clk_disable_unprepare(sfb->lcd_clk);err_bus_clk:clk_disable_unprepare(sfb->bus_clk);return ret; }************************************************ s3c_fb_probe_win() *************************************************************
/*** s3c_fb_probe_win() - register an hardware window* @sfb: The base resources for the hardware* @variant: The variant information for this window.* @res: Pointer to where to place the resultant window.** Allocate and do the basic initialisation for one of the hardware's graphics* windows.*//*將顯示控制器的結構體作為參數傳遞進來,它有寄存器起始地址等豐富信息,還將win的號碼也作為參數傳遞進來variant --> driver data中win的部分,它是由fbdrv->win[win]作為參數傳過來的res --> per window private data for each framebuffer,它里面含有指向FBI(fb_info)結構體的針指*/ static int s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,struct s3c_fb_win_variant *variant,struct s3c_fb_win **res) {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;dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant);init_waitqueue_head(&sfb->vsync_info.wait); //初始化等待隊列頭palette_size = variant->palette_sz * 4; //調色板大小fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +palette_size * sizeof(u32), sfb->dev);//分配fb_info結構體,返回一個fb_info結構體地址,這個結構體現在沒什么內容,//只賦值了par(win的起始地址)和device (父設備)兩個變量if (!fbinfo) {dev_err(sfb->dev, "failed to allocate framebuffer\n");return -ENOENT;}windata = sfb->pdata->win[win_no]; //windata指向 platform data中win的部份 initmode = *sfb->pdata->vtiming;WARN_ON(windata->max_bpp == 0);WARN_ON(windata->xres == 0);WARN_ON(windata->yres == 0);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);if (ret) {dev_err(sfb->dev, "failed to allocate display memory\n");return ret;}/* 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 *///給FBI填上各個參數,此函數詳見appendixinitmode.xres = windata->xres;initmode.yres = windata->yres;fb_videomode_to_var(&fbinfo->var, &initmode);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->fbops = &s3c_fb_ops; //對framebuffer的操作,詳見appendixfbinfo->flags = FBINFO_FLAG_DEFAULT;fbinfo->pseudo_palette = &win->pseudo_palette;/* prepare to actually start the framebuffer */ret = s3c_fb_check_var(&fbinfo->var, fbinfo); //檢查可變參數if (ret < 0) {dev_err(sfb->dev, "check_var failed on initial video params\n");return ret;}/* create initial colour map */ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1);if (ret == 0)fb_set_cmap(&fbinfo->cmap, fbinfo);elsedev_err(sfb->dev, "failed to allocate fb cmap\n");s3c_fb_set_par(fbinfo);dev_dbg(sfb->dev, "about to register framebuffer\n");/* run the check_var and set_par on our configuration. */ret = register_framebuffer(fbinfo);if (ret < 0) {dev_err(sfb->dev, "failed to register framebuffer\n");return ret;}dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id);return 0; }************************************************ s3c_fb_alloc_memory() ************************************************
/*** s3c_fb_alloc_memory() - allocate display memory for framebuffer window* @sfb: The base resources for the hardware.* @win: The window to initialise memory for.** Allocate memory for the given framebuffer.*/ static int 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;dev_dbg(sfb->dev, "allocating memory for display\n");real_size = windata->xres * windata->yres;virt_size = windata->virtual_x * windata->virtual_y; //虛擬sizedev_dbg(sfb->dev, "real_size=%u (%u.%u), virt_size=%u (%u.%u)\n",real_size, windata->xres, windata->yres,virt_size, windata->virtual_x, windata->virtual_y);//一張framebuffer的大小,是按虛擬分辨率和實際分辨率兩者中較大的來算的size = (real_size > virt_size) ? real_size : virt_size;size *= (windata->max_bpp > 16) ? 32 : windata->max_bpp;size /= 8;fbi->fix.smem_len = size; //要分配的內存大小size = PAGE_ALIGN(size); //頁大小dev_dbg(sfb->dev, "want %u bytes for window\n", size);fbi->screen_base = dma_alloc_writecombine(sfb->dev, size,&map_dma, GFP_KERNEL); //分配framebuffer的內存if (!fbi->screen_base)return -ENOMEM;dev_dbg(sfb->dev, "mapped %x to %p\n",(unsigned int)map_dma, fbi->screen_base);memset(fbi->screen_base, 0x0, size); //將framebuffer的內存清空為0fbi->fix.smem_start = map_dma;return 0; }*********************************************************Appendix******************************************************
/*** struct s3c_fb_variant - fb variant information* @is_2443: Set if S3C2443/S3C2416 style hardware.* @nr_windows: The number of windows.* @vidtcon: The base for the VIDTCONx registers* @wincon: The base for the WINxCON registers.* @winmap: The base for the WINxMAP registers.* @keycon: The abse for the WxKEYCON registers.* @buf_start: Offset of buffer start registers.* @buf_size: Offset of buffer size registers.* @buf_end: Offset of buffer end registers.* @osd: The base for the OSD registers.* @palette: Address of palette memory, or 0 if none.* @has_prtcon: Set if has PRTCON register.* @has_shadowcon: Set if has SHADOWCON register.* @has_blendcon: Set if has BLENDCON register.* @has_clksel: Set if VIDCON0 register has CLKSEL bit.* @has_fixvclk: Set if VIDCON1 register has FIXVCLK bits.*/ struct s3c_fb_variant {unsigned int is_2443:1;unsigned short nr_windows;unsigned int vidtcon;unsigned short wincon;unsigned short winmap;unsigned short keycon;unsigned short buf_start;unsigned short buf_end;unsigned short buf_size;unsigned short osd;unsigned short osd_stride;unsigned short palette[S3C_FB_MAX_WIN];unsigned int has_prtcon:1;unsigned int has_shadowcon:1;unsigned int has_blendcon: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_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_palette palette;struct s3c_fb_win_variant variant;u32 *palette_buffer;u32 pseudo_palette[16];unsigned int index; };?
總結
以上是生活随笔為你收集整理的LCD(六)显示控制器、framebuffer驱动、s3c-fb.c中probe函数分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 淘宝迈入智能时代 技术就是要实打实解决商
- 下一篇: 刚安装完成的Jmeter5打开之后没有工