一、驱动框架图
二、内核kernel层(Framebuffer)
2.1、入口出口函数(init exit)
/*linux/drivers/video/fbmem.c*/
主设备号: 29,
提供了读写相关接口函数
#define FB_MAJOR 29 /* /dev/fb* framebuffers */
static const struct file_operations fb_fops = {
、、、、、、、、、
.read = fb_read,
.write = fb_write,
.unlocked_ioctl = fb_ioctl,
.mmap = fb_mmap,
.open = fb_open,
.release = fb_release,
、、、、、、、
};static int __init fbmem_init(void)
{
、、、、、、、ret = register_chrdev(FB_MAJOR, "fb", &fb_fops);
、、、、、、、
}
2.2、fb_open
/*linux/drivers/video/fbmem.c*/
static struct fb_info *get_fb_info(unsigned int idx)
{
struct fb_info *fb_info;
、、、、、、、、fb_info = registered_fb[idx];
、、、、、、、
return fb_info;
}fb_open(struct inode *inode, struct file *file)
__acquires(&info->lock)
__releases(&info->lock)
{
int fbidx = iminor(inode);
、、、、、、、
info = get_fb_info(fbidx); //根据从fb_info结构体数组中取出一个成员
、、、、、、、、
}
2.3、fb_read
/*linux/drivers/video/fbmem.c*/
又是操作 struct fb_info *registered_fb[FB_MAX]; 数组
static struct fb_info *file_fb_info(struct file *file)
{
、、、、、、
struct fb_info *info = registered_fb[fbidx];、、、、
}static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
struct fb_info *info = file_fb_info(file); //从registered_fb数组中取出一个成员
u8 *buffer, *dst;
u8 __iomem *src;
int c, cnt = 0, err = 0;
unsigned long total_size;
if (info->fbops->fb_read)
return info->fbops->fb_read(info, buf, count, ppos);
、、、、、、buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
GFP_KERNEL);
、、、、src = (u8 __iomem *) (info->screen_base + p);
、、、、、、
while (count) {
c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
dst = buffer;
fb_memcpy_fromfb(dst, src, c);
dst += c;
src += c;if (copy_to_user(buf, buffer, c)) {
err = -EFAULT;
break;
}
*ppos += c;
buf += c;
cnt += c;
count -= c;
}、、、、、、、、、
return (err) ? err : cnt;
}
2.4、registered_fb[FB_MAX] 初始化(设置)register_framebuffer
int register_framebuffer(struct fb_info *fb_info)
{
int ret;mutex_lock(®istration_lock);
ret = do_register_framebuffer(fb_info);
mutex_unlock(®istration_lock);return ret;
}
2.5、struct fb_info结构体解析
struct fb_info {
atomic_t count;
int node;
int flags;
struct mutex lock; /* Lock for open/release/ioctl funcs */
struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields */
struct fb_var_screeninfo var; /*LCD屏的可变参数 */
struct fb_fix_screeninfo fix; /* LCD屏固定参数*/
struct fb_monspecs monspecs; /* Current Monitor specs */
struct work_struct queue; /* Framebuffer event queue */
struct fb_pixmap pixmap; /* Image hardware mapper */
struct fb_pixmap sprite; /* Cursor hardware mapper */
struct fb_cmap cmap; /* Current cmap */
struct list_head modelist; /* mode list */
struct fb_videomode *mode; /* current mode */、、、、、、、
struct fb_ops *fbops; /*底层操作函数,大部分用内核提供的,不需要实现*/
struct device *device; /* This is the parent */
struct device *dev; /* This is this fb device */
int class_flag; /* private sysfs flags */
、、、、、、
union {
char __iomem *screen_base; /* Virtual address LCD显存虚拟地址*/
char *screen_buffer;
};
unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */
void *pseudo_palette; /* Fake palette of 16 colors */
、、、、、、、
/* From here on everything is device dependent */
void *par; //私有数据,可以存放自己的私有数据
、、、、、、、、bool skip_vt_switch; /* no VT switch on suspend/resume required */
};
2.6、struct fb_var_screeninfo var; 结构体
struct fb_var_screeninfo { ///显示屏信息
__u32 xres; /* visible resolution*//可视区域,一行有多少个像素点
__u32 yres; ///可视区域,一列有多少个像素点
__u32 xres_virtual; /* virtual resolution*//虚拟区域,一行有多少个像素点,简单的意思就是内存中定义的区间是比较大的
__u32 yres_virtual;////虚拟区域,一列有多少个像素点
__u32 xoffset; //虚拟到可见屏幕之间的行偏移
__u32 yoffset; /* resolution *//虚拟到可见屏幕之间的列偏移__u32 bits_per_pixel; /* guess what*/ 每个像素的 bit 数,这个参数不需要自己配置,而是通过上层在调用 checkvar 函数传递 bpp 的时候赋值的
__u32 grayscale; /* 0 = color, 1 = grayscale,*////等于零就成黑白 (灰度)
/* >1 = FOURCC */
// 通过 pixel per bpp 来设定 red green 和 blue 的位置; pixel per bpp 可以通过 ioctl 设定
struct fb_bitfield red; //fb缓存的R位域
struct fb_bitfield green; /* else only length is significant *//fb缓存的G位域
struct fb_bitfield blue; //fb缓存的B位域
struct fb_bitfield transp; /* transparency *//透明度__u32 nonstd; /* != 0 Non standard pixel format *///如果nonstd 不等于0,非标准的像素格式
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; //内存中的图像高度
__u32 width; //内存中的图像宽度__u32 accel_flags; /* (OBSOLETE) see fb_info.flags *////加速标志
/* Timing: All values in pixclocks, except pixclock (of course) */
///时序,这些部分就是显示器的显示方法了,和具体的液晶显示屏有关,在驱动中一般放在 具体液晶屏的配置文件
__u32 pixclock; /* pixel clock in ps (pico seconds) *///像素时钟
__u32 left_margin; //行切换(HBPD),从同步到绘图之间的延迟 左边框,驱动程序一般不设置
__u32 right_margin; /* (HFPD) *///行切换,从绘图到同步之间的延迟 右边框,驱动程序一般不设置
__u32 upper_margin; /* VBPD *///帧切换,从同步到绘图之间的延迟 上边框,驱动程序一般不设置
__u32 lower_margin; //(VFPD) //帧切换,从绘图到同步之间的延迟 底边框,驱动程序一般不设置
__u32 hsync_len; /* length of horizontal sync */ //水平同步的长度
__u32 vsync_len; /* length of vertical sync */ //垂直同步的长度
__u32 sync; /* see FB_SYNC_* *////---->看 FB_SYNC_*
__u32 vmode; /* see FB_VMODE_* *////---->看 FB_VMODE_*
__u32 rotate; /* angle we rotate counter clockwise */
__u32 colorspace; /* colorspace for FOURCC-based modes */
__u32 reserved[4]; /* Reserved for future compatibility */
};
2.7、struct fb_fix_screeninfo
struct fb_fix_screeninfo {
char id[16]; //字符串形式的标识符
unsigned long smem_start; //显存物理起始地址,注意,是物理址,不是虚拟地址
__u32 smem_len; /* Length of frame buffer mem *///fb 显存的长度
__u32 type; /* see FB_TYPE_* *////看FB_TYPE_* -->
__u32 type_aux; /* Interleave for interleaved Planes *///分界
__u32 visual; ///看FB_VISUAL_* -->
__u16 xpanstep; //如果没有硬件panning就赋值为0
__u16 ypanstep; //如果没有硬件panning就赋值为0
__u16 ywrapstep; //如果没有硬件ywrap就赋值为0
__u32 line_length; //一行的字节数
unsigned long mmio_start; //内存映射 IO的开始位置
/* (physical address) */
__u32 mmio_len; //内存映射 IO的长度
__u32 accel; /* Indicate to driver which */
/* specific chip/card we have */
__u16 capabilities; /* see FB_CAP_* *///功能 ---FB_CAP_FOURCC--- Device supports FOURCC-based formats
__u16 reserved[2]; //为以后的兼容性保留
};
三、平台设备驱动层
3.1、 s3c_fb_driver
static const struct platform_device_id s3c_fb_driver_ids[] = {
{
.name = "s3c-fb",
.driver_data = (unsigned long)&s3c_fb_data_64xx,
}, {
.name = "s5pv210-fb",
.driver_data = (unsigned long)&s3c_fb_data_s5pv210,
}, {
.name = "exynos4-fb",
.driver_data = (unsigned long)&s3c_fb_data_exynos4,
}, {
.name = "exynos5-fb",
.driver_data = (unsigned long)&s3c_fb_data_exynos5,
}, {
.name = "s3c2443-fb",
.driver_data = (unsigned long)&s3c_fb_data_s3c2443,
},
{},
};static struct platform_driver s3c_fb_driver = {
.probe = s3c_fb_probe, //重点分析
.remove = s3c_fb_remove,
.id_table = s3c_fb_driver_ids,
.driver = {
.name = "s3c-fb",
.pm = &s3cfb_pm_ops,
},
};
3.2、s3c_fb_probe分析
static int s3c_fb_probe(struct platform_device *pdev)
{
const struct platform_device_id *platid;
struct s3c_fb_driverdata *fbdrv;
struct device *dev = &pdev->dev;
struct s3c_fb_platdata *pd;
struct s3c_fb *sfb;
struct resource *res;
int win;
int ret = 0;
u32 reg;platid = platform_get_device_id(pdev);
fbdrv = (struct s3c_fb_driverdata *)platid->driver_data;if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) {
dev_err(dev, "too many windows, cannot attach\n");
return -EINVAL;
}pd = dev_get_platdata(&pdev->dev);
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);
sfb->dev = dev;
sfb->pdata = pd;
sfb->variant = fbdrv->variant;spin_lock_init(&sfb->slock);
sfb->bus_clk = devm_clk_get(dev, "lcd");
if (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);
if (!sfb->variant.has_clksel) {
sfb->lcd_clk = devm_clk_get(dev, "sclk_fimd");
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);
sfb->regs = devm_ioremap_resource(dev, res);
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);
pm_runtime_get_sync(sfb->dev);/* setup gpio and output polarity controls */
pd->setup_gpio();
writel(pd->vidcon1, sfb->regs + VIDCON1);
/* set video clock running at under-run */
if (sfb->variant.has_fixvclk) {
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);/* 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);
/* 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]);
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]);
goto err_pm_runtime;
}
}platform_set_drvdata(pdev, 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;
}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;
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);
if (!fbinfo) {
dev_err(sfb->dev, "failed to allocate framebuffer\n");
return -ENOENT;
}windata = sfb->pdata->win[win_no];
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;
var = &fbinfo->var;
win->variant = *variant;
win->fbinfo = fbinfo;
win->parent = sfb;
win->windata = windata;
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 */
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;
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 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);
else
dev_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;
}