LINUX framebuffer from linux2.6.28

         framebuffer 为linux系统为显示设备提供的一个接口将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用在图形模式下直接对显示缓冲区直接操作。framebuffer为一个字符设备,主设备号29,lframebuffer的显示缓冲区在linux的内核太的地址空间,在linux中,应用程序都有自己的虚拟地址空间,应用程序不能直接访问物理缓冲区地址的,在fileoperations结构中提供了mmap()函数,可将文件的内容映射到用户空间。对于帧缓冲通过将屏幕缓冲区(Framebuffer)的物理地址映射到用户空间的一段虚拟地址中。

     LCD显示原理,framebuffer驱动就是分配一块内存显示,然后设置LCD的控制寄存器,LCD显示器不断地从显存中获取数据并在LCD显示,实现这些操作的方法是 :填充struct  fb_info结构体,并调用register_framebuffer(fbinfo)向内核注册结构体struct fb_info,其结构中最主要的的是fs_ops成员。

<span style="font-size:14px;">struct <strong>fb_var_screeninfo </strong>{
	__u32 xres;			/* visible resolution		*/
	__u32 yres;
	__u32 xres_virtual;		/* virtual resolution		*/
	__u32 yres_virtual;
	__u32 xoffset;			/* offset from virtual to visible */
	__u32 yoffset;			/* resolution			*/


	__u32 bits_per_pixel;		/* guess what			*/
	__u32 grayscale;		/* != 0 Graylevels instead of colors */


	struct fb_bitfield red;		/* bitfield in fb mem if true color, */
	struct fb_bitfield green;	/* else only length is significant */
	struct fb_bitfield blue;
	struct fb_bitfield transp;	/* transparency			*/	


	__u32 nonstd;			/* != 0 Non standard pixel format */


	__u32 activate;			/* see FB_ACTIVATE_*		*/


	__u32 height;			/* height of picture in mm    */
	__u32 width;			/* width of picture in mm     */


	__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;		/* time from sync to picture	*/
	__u32 right_margin;		/* time from picture to sync	*/
	__u32 upper_margin;		/* time from sync to picture	*/
	__u32 lower_margin;
	__u32 hsync_len;		/* length of horizontal sync	*/
	__u32 vsync_len;		/* length of vertical sync	*/
	__u32 sync;			/* see FB_SYNC_*		*/
	__u32 vmode;			/* see FB_VMODE_*		*/
	__u32 rotate;			/* angle we rotate counter clockwise */
	__u32 reserved[5];		/* Reserved for future compatibility */
};
struct <strong>fb_fix_screeninfo</strong> {
	char id[16];			/* identification string eg "TT Builtin" */
	unsigned long smem_start;	/* Start of frame buffer mem */
					/* (physical address) */
	__u32 smem_len;			/* Length of frame buffer mem */
	__u32 type;			/* see FB_TYPE_*		*/
	__u32 type_aux;			/* Interleave for interleaved Planes */
	__u32 visual;			/* see FB_VISUAL_*		*/ 
	__u16 xpanstep;			/* zero if no hardware panning  */
	__u16 ypanstep;			/* zero if no hardware panning  */
	__u16 ywrapstep;		/* zero if no hardware ywrap    */
	__u32 line_length;		/* length of a line in bytes    */
	unsigned long mmio_start;	/* Start of Memory Mapped I/O   */
					/* (physical address) */
	__u32 mmio_len;			/* Length of Memory Mapped I/O  */
	__u32 accel;			/* Indicate to driver which	*/
					/*  specific chip/card we have	*/
	__u16 reserved[3];		/* Reserved for future compatibility */
};
  <strong>struct fb_info</strong> {
	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;	/* Current var<span style="color:#3366ff;"><strong>当前缓冲区可变参数,即当前视频信息</strong></span> */
	struct fb_fix_screeninfo fix;	/* Current fix <span style="color:#3366ff;"><strong>当前缓冲区的固定参数</strong></span>*/
	struct fb_monspecs monspecs;	/* Current Monitor specs */
	struct work_struct queue;	/*<strong> Framebuffer event queue</strong> */
	struct fb_pixmap pixmap;	/* Image hardware mapper */
	struct fb_pixmap sprite;	/* Cursor hardware mapper */
	struct fb_cmap cmap;		/* <strong>Current cmap</strong> */
	struct list_head modelist;      /* mode list */
	struct fb_videomode *mode;	/* current mode */


#ifdef CONFIG_FB_BACKLIGHT
	/* <strong>assigned backlight device</strong> */
	/* set before framebuffer registration, 
	   remove after unregister */
	struct backlight_device *bl_dev;


	/* Backlight level curve */
	struct mutex bl_curve_mutex;	
	u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif
#ifdef CONFIG_FB_DEFERRED_IO
	struct delayed_work deferred_work;
	struct fb_deferred_io *fbdefio;
#endif


	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 */
#ifdef CONFIG_FB_TILEBLITTING
	struct fb_tile_ops *tileops;    /* Tile Blitting */
#endif
	char __iomem *screen_base;	/* Virtual address */
	unsigned long screen_size;	/* Amount of ioremapped VRAM or 0 */ 
	void *pseudo_palette;		/* Fake palette of 16 colors */ 
#define FBINFO_STATE_RUNNING	0
#define FBINFO_STATE_SUSPENDED	1
	u32 state;			/* Hardware state i.e suspend */
	void *fbcon_par;                /* fbcon use-only private area */
	/* From here on everything is device dependent */
	void *par;
	/* we need the PCI or similiar aperture base/size not
	   smem_start/size as smem_start may just be an object
	   allocated inside the aperture so may not actually overlap */
	struct apertures_struct {
		unsigned int count;
		struct aperture {
			resource_size_t base;
			resource_size_t size;
		} ranges[0];
	} *apertures;
};</span>

fb_ops结构体用来实现对帧缓冲的设备操作:

static const struct file_operations fb_fops = {
	.owner =	THIS_MODULE,
	.read =		fb_read,
	.write =	fb_write,
	.unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = fb_compat_ioctl,
#endif
	.mmap =		fb_mmap,
	.open =		fb_open,
	.release =	fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
	.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
	.fsync =	fb_deferred_io_fsync,
#endif
	.llseek =	default_llseek,
};


LCD驱动分析:

/* LCD Controller  LCD设备控制信息 */

static struct resource s3c_lcd_resource[] = {
[0] = {
.start = S3C24XX_PA_LCD,
.end   = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_LCD,
.end   = IRQ_LCD,
.flags = IORESOURCE_IRQ,
}


};
static u64 s3c_device_lcd_dmamask = 0xffffffffUL;
struct platform_device s3c_device_lcd = {
.name  = "s3c2410-lcd",
.id  = -1,//表示只有一个设备
.num_resources  = ARRAY_SIZE(s3c_lcd_resource),
.resource  = s3c_lcd_resource,
.dev              = {
.dma_mask = &s3c_device_lcd_dmamask,
.coherent_dma_mask = 0xffffffffUL
}

};

static struct s3c2410fb_display gt2440_lcd_cfg __initdata = {
.lcdcon5 = S3C2410_LCDCON5_FRM565 |
 S3C2410_LCDCON5_INVVCLK |
 S3C2410_LCDCON5_PWREN |
 S3C2410_LCDCON5_HWSWP,
.type = S3C2410_LCDCON1_TFT,

.width = 320,
.height = 240,


.pixclock = 120000, /* HCLK 100 MHz, divisor 3 */
// .setclkval = 0x3,
.xres = 320,
.yres = 240,
.bpp = 16,
.left_margin = 10, /* for HFPD*/
.right_margin = 69, /* for HBPD*/
.hsync_len = 5, /* for HSPW*/
.upper_margin = 12, /* for VFPD*/
.lower_margin = 5, /* for VBPD*/
.vsync_len = 5, /* for VSPW*/
};

static struct s3c2410fb_mach_info gt2440_fb_info __initdata = {
.displays = &gt2440_lcd_cfg,
.num_displays = 1,
.default_display = 0,

}

void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
{
struct s3c2410fb_mach_info *npd;


npd = kmemdup(pd, sizeof(*npd), GFP_KERNEL);
if (npd) {
s3c_device_lcd.dev.platform_data = npd;
npd->displays = kmemdup(pd->displays,
sizeof(struct s3c2410fb_display) * npd->num_displays,
GFP_KERNEL);
if (!npd->displays)
printk(KERN_ERR "no memory for LCD display data\n");
} else {
printk(KERN_ERR "no memory for LCD platform data\n");
}
}

LCDdriver分析:

static int __devinit s3c24xxfb_probe(struct platform_device *pdev,
 enum s3c_drv_type drv_type)
{
struct s3c2410fb_info *info;
struct s3c2410fb_display *display;
struct fb_info *fbinfo;
struct s3c2410fb_mach_info *mach_info;
struct resource *res;
int ret;
int irq;
int i;
int size;
u32 lcdcon1;

mach_info = pdev->dev.platform_data;
if (mach_info == NULL) {
dev_err(&pdev->dev,
"no platform data for lcd, cannot attach\n");
return -EINVAL;
}


if (mach_info->default_display >= mach_info->num_displays) {
dev_err(&pdev->dev, "default is %d but only %d displays\n",
mach_info->default_display, mach_info->num_displays);
return -EINVAL;
}


display = mach_info->displays + mach_info->default_display;


irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq for device\n");
return -ENOENT;
}


fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
if (!fbinfo)
return -ENOMEM;


platform_set_drvdata(pdev, fbinfo);


info = fbinfo->par;
info->dev = &pdev->dev;
info->drv_type = drv_type;


res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get memory registers\n");
ret = -ENXIO;
goto dealloc_fb;
}


size = (res->end - res->start) + 1;
info->mem = request_mem_region(res->start, size, pdev->name);
if (info->mem == NULL) {
dev_err(&pdev->dev, "failed to get memory region\n");
ret = -ENOENT;
goto dealloc_fb;
}


info->io = ioremap(res->start, size);
if (info->io == NULL) {
dev_err(&pdev->dev, "ioremap() of registers failed\n");
ret = -ENXIO;
goto release_mem;
}


info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);


dprintk("devinit\n");


strcpy(fbinfo->fix.id, driver_name);


/* Stop the video */
lcdcon1 = readl(info->io + S3C2410_LCDCON1);
writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);


fbinfo->fix.type    = FB_TYPE_PACKED_PIXELS;
fbinfo->fix.type_aux    = 0;
fbinfo->fix.xpanstep    = 0;
fbinfo->fix.ypanstep    = 0;
fbinfo->fix.ywrapstep    = 0;
fbinfo->fix.accel    = FB_ACCEL_NONE;


fbinfo->var.nonstd    = 0;
fbinfo->var.activate    = FB_ACTIVATE_NOW;
fbinfo->var.accel_flags     = 0;
fbinfo->var.vmode    = FB_VMODE_NONINTERLACED;


fbinfo->fbops    = &s3c2410fb_ops;
fbinfo->flags    = FBINFO_FLAG_DEFAULT;
fbinfo->pseudo_palette      = &info->pseudo_pal;


for (i = 0; i < 256; i++)
info->palette_buffer[i] = PALETTE_BUFF_CLEAR;


ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);
if (ret) {
dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
ret = -EBUSY;
goto release_regs;
}


info->clk = clk_get(NULL, "lcd");
if (IS_ERR(info->clk)) {
printk(KERN_ERR "failed to get lcd clock source\n");
ret = PTR_ERR(info->clk);
goto release_irq;
}


clk_enable(info->clk);
dprintk("got and enabled clock\n");


msleep(1);


info->clk_rate = clk_get_rate(info->clk);


/* find maximum required memory size for display */
for (i = 0; i < mach_info->num_displays; i++) {
unsigned long smem_len = mach_info->displays[i].xres;


smem_len *= mach_info->displays[i].yres;
smem_len *= mach_info->displays[i].bpp;
smem_len >>= 3;
if (fbinfo->fix.smem_len < smem_len)
fbinfo->fix.smem_len = smem_len;
}


/* Initialize video memory */
ret = s3c2410fb_map_video_memory(fbinfo);
//分配DRAM内存给framerbuffer,并初始化这段内存

if (ret) {
printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);
ret = -ENOMEM;
goto release_clock;
}


dprintk("got video memory\n");

fbinfo->var.xres = display->xres;
fbinfo->var.yres = display->yres;
fbinfo->var.bits_per_pixel = display->bpp;


s3c2410fb_init_registers(fbinfo);//初始化LCD控制器的相关寄存器,可参考s3c2410处理器芯片手册;
       

s3c2410fb_check_var(&fbinfo->var, fbinfo);//检测framerbuffer的相关参数


ret = s3c2410fb_cpufreq_register(info);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register cpufreq\n");
goto free_video_memory;
}
ret = register_framebuffer(fbinfo);//注册帧缓冲设备fb_info到系统中
if (ret < 0) {
printk(KERN_ERR "Failed to register framebuffer device: %d\n",
ret);
goto free_cpufreq;
}
       /* create device files */
ret = device_create_file(&pdev->dev, &dev_attr_debug);
if (ret) {
printk(KERN_ERR "failed to add debug attribute\n");
}


printk(KERN_INFO "fb%d: %s frame buffer device\n",
fbinfo->node, fbinfo->fix.id);


return 0;
}

<span style="font-size:18px;color:#3333ff;"> /* s3c2410fb_map_video_memory():
 *	Allocates the DRAM memory for the frame buffer.  This buffer is
 *	remapped into a non-cached, non-buffered, memory region to
 *	allow palette and pixel writes to occur without flushing the
 *	cache.  Once this area is remapped, all virtual memory
 *	access to the video memory should occur at the new region.
 */
static int __devinit s3c2410fb_map_video_memory(struct fb_info *info)
{
	struct s3c2410fb_info *fbi = info->par;
	dma_addr_t map_dma;
	unsigned map_size = PAGE_ALIGN(info->fix.smem_len);

	dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);

	info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
						   &map_dma, GFP_KERNEL);

	if (info->screen_base) {
		/* prevent initial garbage on screen */
		dprintk("map_video_memory: clear %p:%08x\n",
			info->screen_base, map_size);
		memset(info->screen_base, 0x00, map_size);

		info->fix.smem_start = map_dma;

		dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",
			info->fix.smem_start, info->screen_base, map_size);
	}

	return info->screen_base ? 0 : -ENOMEM;
}</span>


int
register_framebuffer(struct fb_info *fb_info)
{
int i;
struct fb_event event;
struct fb_videomode mode;


if (num_registered_fb == FB_MAX)
return -ENXIO;


if (fb_check_foreignness(fb_info))
return -ENOSYS;


remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
fb_is_primary_device(fb_info));


num_registered_fb++;
for (i = 0 ; i < FB_MAX; i++)
if (!registered_fb[i])
break;
fb_info->node = i;
mutex_init(&fb_info->lock);
mutex_init(&fb_info->mm_lock);


fb_info->dev = device_create(fb_class, fb_info->device,
    MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
if (IS_ERR(fb_info->dev)) {
/* Not fatal */
printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
fb_info->dev = NULL;
} else
fb_init_device(fb_info);


if (fb_info->pixmap.addr == NULL) {
fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
if (fb_info->pixmap.addr) {
fb_info->pixmap.size = FBPIXMAPSIZE;
fb_info->pixmap.buf_align = 1;
fb_info->pixmap.scan_align = 1;
fb_info->pixmap.access_align = 32;
fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
}
}
fb_info->pixmap.offset = 0;


if (!fb_info->pixmap.blit_x)
fb_info->pixmap.blit_x = ~(u32)0;


if (!fb_info->pixmap.blit_y)
fb_info->pixmap.blit_y = ~(u32)0;


if (!fb_info->modelist.prev || !fb_info->modelist.next)
INIT_LIST_HEAD(&fb_info->modelist);


fb_var_to_videomode(&mode, &fb_info->var);
fb_add_videomode(&mode, &fb_info->modelist);
registered_fb[i] = fb_info;


event.info = fb_info;
if (!lock_fb_info(fb_info))
return -ENODEV;
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
unlock_fb_info(fb_info);
return 0;
}


你可能感兴趣的:(linux,LCD,framebuffer)