au1200fb.c

来自「Linux环境下视频显示卡设备的驱动程序源代码」· C语言 代码 · 共 1,922 行 · 第 1/4 页

C
1,922
字号
	pdata->pixel_order = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_PO) >> 22;	pdata->xsize = ((lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZX) >> 11) + 1;	pdata->ysize = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZY) + 1;	/* Window control register 2 */	pdata->colorkey_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_CKMODE) >> 24;	pdata->double_buffer_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_DBM) >> 23;	pdata->ram_array_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_RAM) >> 21;	pdata->enable = (lcd->winenable >> plane) & 1;	au_sync();}static int au1200fb_ioctl(struct fb_info *info, unsigned int cmd,                          unsigned long arg){	int plane;	int val;#ifdef CONFIG_PM	au1xxx_pm_access(LCD_pm_dev);#endif	plane = fbinfo2index(info);	print_dbg("au1200fb: ioctl %d on plane %d\n", cmd, plane);	if (cmd == AU1200_LCD_FB_IOCTL) {		struct au1200_lcd_iodata_t iodata;		if (copy_from_user(&iodata, (void __user *) arg, sizeof(iodata)))			return -EFAULT;		print_dbg("FB IOCTL called\n");		switch (iodata.subcmd) {		case AU1200_LCD_SET_SCREEN:			print_dbg("AU1200_LCD_SET_SCREEN\n");			set_global(cmd, &iodata.global);			break;		case AU1200_LCD_GET_SCREEN:			print_dbg("AU1200_LCD_GET_SCREEN\n");			get_global(cmd, &iodata.global);			break;		case AU1200_LCD_SET_WINDOW:			print_dbg("AU1200_LCD_SET_WINDOW\n");			set_window(plane, &iodata.window);			break;		case AU1200_LCD_GET_WINDOW:			print_dbg("AU1200_LCD_GET_WINDOW\n");			get_window(plane, &iodata.window);			break;		case AU1200_LCD_SET_PANEL:			print_dbg("AU1200_LCD_SET_PANEL\n");			if ((iodata.global.panel_choice >= 0) &&					(iodata.global.panel_choice <					 NUM_PANELS))			{				struct panel_settings *newpanel;				panel_index = iodata.global.panel_choice;				newpanel = &known_lcd_panels[panel_index];				au1200_setpanel(newpanel);			}			break;		case AU1200_LCD_GET_PANEL:			print_dbg("AU1200_LCD_GET_PANEL\n");			iodata.global.panel_choice = panel_index;			break;		default:			return -EINVAL;		}		val = copy_to_user((void __user *) arg, &iodata, sizeof(iodata));		if (val) {			print_dbg("error: could not copy %d bytes\n", val);			return -EFAULT;		}	}	return 0;}static struct fb_ops au1200fb_fb_ops = {	.owner		= THIS_MODULE,	.fb_check_var	= au1200fb_fb_check_var,	.fb_set_par	= au1200fb_fb_set_par,	.fb_setcolreg	= au1200fb_fb_setcolreg,	.fb_blank	= au1200fb_fb_blank,	.fb_fillrect	= cfb_fillrect,	.fb_copyarea	= cfb_copyarea,	.fb_imageblit	= cfb_imageblit,	.fb_sync	= NULL,	.fb_ioctl	= au1200fb_ioctl,	.fb_mmap	= au1200fb_fb_mmap,};/*-------------------------------------------------------------------------*/static irqreturn_t au1200fb_handle_irq(int irq, void* dev_id){	/* Nothing to do for now, just clear any pending interrupt */	lcd->intstatus = lcd->intstatus;	au_sync();	return IRQ_HANDLED;}/*-------------------------------------------------------------------------*//* AU1200 LCD device probe helpers */static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev){	struct fb_info *fbi = &fbdev->fb_info;	int bpp;	memset(fbi, 0, sizeof(struct fb_info));	fbi->fbops = &au1200fb_fb_ops;	bpp = winbpp(win->w[fbdev->plane].mode_winctrl1);	/* Copy monitor specs from panel data */	/* fixme: we're setting up LCD controller windows, so these dont give a	damn as to what the monitor specs are (the panel itself does, but that	isnt done here...so maybe need a generic catchall monitor setting??? */	memcpy(&fbi->monspecs, &panel->monspecs, sizeof(struct fb_monspecs));	/* We first try the user mode passed in argument. If that failed,	 * or if no one has been specified, we default to the first mode of the	 * panel list. Note that after this call, var data will be set */	if (!fb_find_mode(&fbi->var,			  fbi,			  NULL, /* drv_info.opt_mode, */			  fbi->monspecs.modedb,			  fbi->monspecs.modedb_len,			  fbi->monspecs.modedb,			  bpp)) {		print_err("Cannot find valid mode for panel %s", panel->name);		return -EFAULT;	}	fbi->pseudo_palette = kcalloc(16, sizeof(u32), GFP_KERNEL);	if (!fbi->pseudo_palette) {		return -ENOMEM;	}	if (fb_alloc_cmap(&fbi->cmap, AU1200_LCD_NBR_PALETTE_ENTRIES, 0) < 0) {		print_err("Fail to allocate colormap (%d entries)",			   AU1200_LCD_NBR_PALETTE_ENTRIES);		kfree(fbi->pseudo_palette);		return -EFAULT;	}	strncpy(fbi->fix.id, "AU1200", sizeof(fbi->fix.id));	fbi->fix.smem_start = fbdev->fb_phys;	fbi->fix.smem_len = fbdev->fb_len;	fbi->fix.type = FB_TYPE_PACKED_PIXELS;	fbi->fix.xpanstep = 0;	fbi->fix.ypanstep = 0;	fbi->fix.mmio_start = 0;	fbi->fix.mmio_len = 0;	fbi->fix.accel = FB_ACCEL_NONE;	fbi->screen_base = (char __iomem *) fbdev->fb_mem;	au1200fb_update_fbinfo(fbi);	return 0;}/*-------------------------------------------------------------------------*//* AU1200 LCD controller device driver */static int au1200fb_drv_probe(struct device *dev){	struct au1200fb_device *fbdev;	unsigned long page;	int bpp, plane, ret;	if (!dev)		return -EINVAL;	for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) {		bpp = winbpp(win->w[plane].mode_winctrl1);		if (win->w[plane].xres == 0)			win->w[plane].xres = panel->Xres;		if (win->w[plane].yres == 0)			win->w[plane].yres = panel->Yres;		fbdev = &_au1200fb_devices[plane];		memset(fbdev, 0, sizeof(struct au1200fb_device));		fbdev->plane = plane;		/* Allocate the framebuffer to the maximum screen size */		fbdev->fb_len = (win->w[plane].xres * win->w[plane].yres * bpp) / 8;		fbdev->fb_mem = dma_alloc_noncoherent(dev,				PAGE_ALIGN(fbdev->fb_len),				&fbdev->fb_phys, GFP_KERNEL);		if (!fbdev->fb_mem) {			print_err("fail to allocate frambuffer (size: %dK))",				  fbdev->fb_len / 1024);			return -ENOMEM;		}		/*		 * Set page reserved so that mmap will work. This is necessary		 * since we'll be remapping normal memory.		 */		for (page = (unsigned long)fbdev->fb_phys;		     page < PAGE_ALIGN((unsigned long)fbdev->fb_phys +			     fbdev->fb_len);		     page += PAGE_SIZE) {			SetPageReserved(pfn_to_page(page >> PAGE_SHIFT)); /* LCD DMA is NOT coherent on Au1200 */		}		print_dbg("Framebuffer memory map at %p", fbdev->fb_mem);		print_dbg("phys=0x%08x, size=%dK", fbdev->fb_phys, fbdev->fb_len / 1024);		/* Init FB data */		if ((ret = au1200fb_init_fbinfo(fbdev)) < 0)			goto failed;		/* Register new framebuffer */		if ((ret = register_framebuffer(&fbdev->fb_info)) < 0) {			print_err("cannot register new framebuffer");			goto failed;		}		au1200fb_fb_set_par(&fbdev->fb_info);#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)		if (plane == 0)			if (fb_prepare_logo(&fbdev->fb_info, FB_ROTATE_UR)) {				/* Start display and show logo on boot */				fb_set_cmap(&fbdev->fb_info.cmap,						&fbdev->fb_info);				fb_show_logo(&fbdev->fb_info, FB_ROTATE_UR);			}#endif	}	/* Now hook interrupt too */	if ((ret = request_irq(AU1200_LCD_INT, au1200fb_handle_irq,		 	  IRQF_DISABLED | IRQF_SHARED, "lcd", (void *)dev)) < 0) {		print_err("fail to request interrupt line %d (err: %d)",			  AU1200_LCD_INT, ret);		goto failed;	}	return 0;failed:	/* NOTE: This only does the current plane/window that failed; others are still active */	if (fbdev->fb_mem)		dma_free_noncoherent(dev, PAGE_ALIGN(fbdev->fb_len),				fbdev->fb_mem, fbdev->fb_phys);	if (fbdev->fb_info.cmap.len != 0)		fb_dealloc_cmap(&fbdev->fb_info.cmap);	if (fbdev->fb_info.pseudo_palette)		kfree(fbdev->fb_info.pseudo_palette);	if (plane == 0)		free_irq(AU1200_LCD_INT, (void*)dev);	return ret;}static int au1200fb_drv_remove(struct device *dev){	struct au1200fb_device *fbdev;	int plane;	if (!dev)		return -ENODEV;	/* Turn off the panel */	au1200_setpanel(NULL);	for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane)	{		fbdev = &_au1200fb_devices[plane];		/* Clean up all probe data */		unregister_framebuffer(&fbdev->fb_info);		if (fbdev->fb_mem)			dma_free_noncoherent(dev, PAGE_ALIGN(fbdev->fb_len),					fbdev->fb_mem, fbdev->fb_phys);		if (fbdev->fb_info.cmap.len != 0)			fb_dealloc_cmap(&fbdev->fb_info.cmap);		if (fbdev->fb_info.pseudo_palette)			kfree(fbdev->fb_info.pseudo_palette);	}	free_irq(AU1200_LCD_INT, (void *)dev);	return 0;}#ifdef CONFIG_PMstatic int au1200fb_drv_suspend(struct device *dev, u32 state, u32 level){	/* TODO */	return 0;}static int au1200fb_drv_resume(struct device *dev, u32 level){	/* TODO */	return 0;}#endif /* CONFIG_PM */static struct device_driver au1200fb_driver = {	.name		= "au1200-lcd",	.bus		= &platform_bus_type,	.probe		= au1200fb_drv_probe,	.remove		= au1200fb_drv_remove,#ifdef CONFIG_PM	.suspend	= au1200fb_drv_suspend,	.resume		= au1200fb_drv_resume,#endif};/*-------------------------------------------------------------------------*//* Kernel driver */static void au1200fb_setup(void){	char* options = NULL;	char* this_opt;	int num_panels = ARRAY_SIZE(known_lcd_panels);	int panel_idx = -1;	fb_get_options(DRIVER_NAME, &options);	if (options) {		while ((this_opt = strsep(&options,",")) != NULL) {			/* Panel option - can be panel name,			 * "bs" for board-switch, or number/index */			if (!strncmp(this_opt, "panel:", 6)) {				int i;				long int li;				char *endptr;				this_opt += 6;				/* First check for index, which allows				 * to short circuit this mess */				li = simple_strtol(this_opt, &endptr, 0);				if (*endptr == '\0') {					panel_idx = (int)li;				}				else if (strcmp(this_opt, "bs") == 0) {					extern int board_au1200fb_panel(void);					panel_idx = board_au1200fb_panel();				}				else				for (i = 0; i < num_panels; i++) {					if (!strcmp(this_opt, known_lcd_panels[i].name)) {						panel_idx = i;						break;					}				}				if ((panel_idx < 0) || (panel_idx >= num_panels)) {						print_warn("Panel %s not supported!", this_opt);				}				else					panel_index = panel_idx;			}			else if (strncmp(this_opt, "nohwcursor", 10) == 0) {				nohwcursor = 1;			}			/* Unsupported option */			else {				print_warn("Unsupported option \"%s\"", this_opt);			}		}	}}#ifdef CONFIG_PMstatic int au1200fb_pm_callback(au1xxx_power_dev_t *dev,		au1xxx_request_t request, void *data) {	int retval = -1;	unsigned int d = 0;	unsigned int brightness = 0;	if (request == AU1XXX_PM_SLEEP) {		board_au1200fb_panel_shutdown();	}	else if (request == AU1XXX_PM_WAKEUP) {		if(dev->prev_state == SLEEP_STATE)		{			int plane;			au1200_setpanel(panel);			for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) 	{				struct au1200fb_device *fbdev;				fbdev = &_au1200fb_devices[plane];				au1200fb_fb_set_par(&fbdev->fb_info);			}		}		d = *((unsigned int*)data);		if(d <=10) brightness = 26;		else if(d<=20) brightness = 51;		else if(d<=30) brightness = 77;		else if(d<=40) brightness = 102;		else if(d<=50) brightness = 128;		else if(d<=60) brightness = 153;		else if(d<=70) brightness = 179;		else if(d<=80) brightness = 204;		else if(d<=90) brightness = 230;		else brightness = 255;		set_brightness(brightness);	} else if (request == AU1XXX_PM_GETSTATUS) {		return dev->cur_state;	} else if (request == AU1XXX_PM_ACCESS) {		if (dev->cur_state != SLEEP_STATE)			return retval;		else {			au1200_setpanel(panel);		}	} else if (request == AU1XXX_PM_IDLE) {	} else if (request == AU1XXX_PM_CLEANUP) {	}	return retval;}#endifstatic int __init au1200fb_init(void){	print_info("" DRIVER_DESC "");	/* Setup driver with options */	au1200fb_setup();	/* Point to the panel selected */	panel = &known_lcd_panels[panel_index];	win = &windows[window_index];	printk(DRIVER_NAME ": Panel %d %s\n", panel_index, panel->name);	printk(DRIVER_NAME ": Win %d %s\n", window_index, win->name);	/* Kickstart the panel, the framebuffers/windows come soon enough */	au1200_setpanel(panel);	#ifdef CONFIG_PM	LCD_pm_dev = new_au1xxx_power_device("LCD", &au1200fb_pm_callback, NULL);	if ( LCD_pm_dev == NULL)		printk(KERN_INFO "Unable to create a power management device entry for the au1200fb.\n");	else		printk(KERN_INFO "Power management device entry for the au1200fb loaded.\n");	#endif	return driver_register(&au1200fb_driver);}static void __exit au1200fb_cleanup(void){	driver_unregister(&au1200fb_driver);}module_init(au1200fb_init);module_exit(au1200fb_cleanup);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?