我是靠谱客的博主 疯狂糖豆,这篇文章主要介绍LCD(六)显示控制器、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.carch/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***************************************************

复制代码
1
2
3
4
5
6
7
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***************************************************

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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() ***************************************************

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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 data struct device *dev = &pdev->dev; struct s3c_fb_platdata *pd; //platform data struct 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 attachn"); return -EINVAL; } pd = pdev->dev.platform_data; //获取platform data,它在板级文件中定义, //这个data里 包含了显示控制器的数据,也包含了win的数据 if (!pd) { dev_err(dev, "no platform data specifiedn"); return -EINVAL; } sfb = devm_kzalloc(dev, sizeof(struct s3c_fb), GFP_KERNEL); //分配内存空间 if (!sfb) { dev_err(dev, "no memory for framebuffersn"); return -ENOMEM; } dev_dbg(dev, "allocate new framebuffer %pn", sfb); printk(KERN_ERR "Here I am: %s:%in", __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 clock if (IS_ERR(sfb->bus_clk)) { dev_err(dev, "failed to get bus clockn"); 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 clockn"); 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 resourcen"); 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 failedn"); goto err_lcd_clk; } dev_dbg(dev, "got resources (regs %p), probing windowsn", 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(); //执行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); } /* 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 %dn", win); for (; win >= 0; win--) s3c_fb_release_win(sfb, sfb->windows[win]); //注册不成功的话就释放之前注册成功过的window goto 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() *************************************************************

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/** * 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 %pn", 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 framebuffern"); 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 memoryn"); 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填上各个参数,此函数详见appendix initmode.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的操作,详见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); //检查可变参数 if (ret < 0) { dev_err(sfb->dev, "check_var failed on initial video paramsn"); 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); else dev_err(sfb->dev, "failed to allocate fb cmapn"); s3c_fb_set_par(fbinfo); dev_dbg(sfb->dev, "about to register framebuffern"); /* run the check_var and set_par on our configuration. */ ret = register_framebuffer(fbinfo); if (ret < 0) { dev_err(sfb->dev, "failed to register framebuffern"); return ret; } dev_info(sfb->dev, "window %d: fb %sn", win_no, fbinfo->fix.id); return 0; }

************************************************ s3c_fb_alloc_memory() ************************************************

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/** * 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 displayn"); real_size = windata->xres * windata->yres; virt_size = windata->virtual_x * windata->virtual_y; //虚拟size dev_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 windown", 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 %pn", (unsigned int)map_dma, fbi->screen_base); memset(fbi->screen_base, 0x0, size); //将framebuffer的内存清空为0 fbi->fix.smem_start = map_dma; return 0; }

*********************************************************Appendix******************************************************

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/** * 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; };
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/** * 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; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
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, };
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/** * 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函数分析的全部内容,更多相关LCD(六)显示控制器、framebuffer驱动、s3c-fb内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(81)

评论列表共有 0 条评论

立即
投稿
返回
顶部