pxafb.c

来自「linux 内核源代码」· C语言 代码 · 共 1,520 行 · 第 1/3 页

C
1,520
字号
 * TODO: Determine why f->new != 10*get_lclk_frequency_10khz() */static intpxafb_freq_transition(struct notifier_block *nb, unsigned long val, void *data){	struct pxafb_info *fbi = TO_INF(nb, freq_transition);	//TODO struct cpufreq_freqs *f = data;	u_int pcd;	switch (val) {	case CPUFREQ_PRECHANGE:		set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE);		break;	case CPUFREQ_POSTCHANGE:		pcd = get_pcd(fbi, fbi->fb.var.pixclock);		set_hsync_time(fbi, pcd);		fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd);		set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE);		break;	}	return 0;}static intpxafb_freq_policy(struct notifier_block *nb, unsigned long val, void *data){	struct pxafb_info *fbi = TO_INF(nb, freq_policy);	struct fb_var_screeninfo *var = &fbi->fb.var;	struct cpufreq_policy *policy = data;	switch (val) {	case CPUFREQ_ADJUST:	case CPUFREQ_INCOMPATIBLE:		printk(KERN_DEBUG "min dma period: %d ps, "			"new clock %d kHz\n", pxafb_display_dma_period(var),			policy->max);		// TODO: fill in min/max values		break;#if 0	case CPUFREQ_NOTIFY:		printk(KERN_ERR "%s: got CPUFREQ_NOTIFY\n", __FUNCTION__);		do {} while(0);		/* todo: panic if min/max values aren't fulfilled		 * [can't really happen unless there's a bug in the		 * CPU policy verification process *		 */		break;#endif	}	return 0;}#endif#ifdef CONFIG_PM/* * Power management hooks.  Note that we won't be called from IRQ context, * unlike the blank functions above, so we may sleep. */static int pxafb_suspend(struct platform_device *dev, pm_message_t state){	struct pxafb_info *fbi = platform_get_drvdata(dev);	set_ctrlr_state(fbi, C_DISABLE_PM);	return 0;}static int pxafb_resume(struct platform_device *dev){	struct pxafb_info *fbi = platform_get_drvdata(dev);	set_ctrlr_state(fbi, C_ENABLE_PM);	return 0;}#else#define pxafb_suspend	NULL#define pxafb_resume	NULL#endif/* * pxafb_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 __init pxafb_map_video_memory(struct pxafb_info *fbi){	u_long palette_mem_size;	/*	 * We reserve one page for the palette, plus the size	 * of the framebuffer.	 */	fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE);	fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size,					      &fbi->map_dma, GFP_KERNEL);	if (fbi->map_cpu) {		/* prevent initial garbage on screen */		memset(fbi->map_cpu, 0, fbi->map_size);		fbi->fb.screen_base = fbi->map_cpu + PAGE_SIZE;		fbi->screen_dma = fbi->map_dma + PAGE_SIZE;		/*		 * FIXME: this is actually the wrong thing to place in		 * smem_start.  But fbdev suffers from the problem that		 * it needs an API which doesn't exist (in this case,		 * dma_writecombine_mmap)		 */		fbi->fb.fix.smem_start = fbi->screen_dma;		fbi->palette_size = fbi->fb.var.bits_per_pixel == 8 ? 256 : 16;		if ((fbi->lccr4 & LCCR4_PAL_FOR_MASK) == LCCR4_PAL_FOR_0)			palette_mem_size = fbi->palette_size * sizeof(u16);		else			palette_mem_size = fbi->palette_size * sizeof(u32);		pr_debug("pxafb: palette_mem_size = 0x%08lx\n", palette_mem_size);		fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size);		fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size;	}	return fbi->map_cpu ? 0 : -ENOMEM;}static struct pxafb_info * __init pxafb_init_fbinfo(struct device *dev){	struct pxafb_info *fbi;	void *addr;	struct pxafb_mach_info *inf = dev->platform_data;	struct pxafb_mode_info *mode = inf->modes;	int i, smemlen;	/* Alloc the pxafb_info and pseudo_palette in one step */	fbi = kmalloc(sizeof(struct pxafb_info) + sizeof(u32) * 16, GFP_KERNEL);	if (!fbi)		return NULL;	memset(fbi, 0, sizeof(struct pxafb_info));	fbi->dev = dev;	fbi->clk = clk_get(dev, "LCDCLK");	if (IS_ERR(fbi->clk)) {		kfree(fbi);		return NULL;	}	strcpy(fbi->fb.fix.id, PXA_NAME);	fbi->fb.fix.type	= FB_TYPE_PACKED_PIXELS;	fbi->fb.fix.type_aux	= 0;	fbi->fb.fix.xpanstep	= 0;	fbi->fb.fix.ypanstep	= 0;	fbi->fb.fix.ywrapstep	= 0;	fbi->fb.fix.accel	= FB_ACCEL_NONE;	fbi->fb.var.nonstd	= 0;	fbi->fb.var.activate	= FB_ACTIVATE_NOW;	fbi->fb.var.height	= -1;	fbi->fb.var.width	= -1;	fbi->fb.var.accel_flags	= 0;	fbi->fb.var.vmode	= FB_VMODE_NONINTERLACED;	fbi->fb.fbops		= &pxafb_ops;	fbi->fb.flags		= FBINFO_DEFAULT;	fbi->fb.node		= -1;	addr = fbi;	addr = addr + sizeof(struct pxafb_info);	fbi->fb.pseudo_palette	= addr;	pxafb_setmode(&fbi->fb.var, mode);	fbi->cmap_inverse		= inf->cmap_inverse;	fbi->cmap_static		= inf->cmap_static;	fbi->lccr0			= inf->lccr0;	fbi->lccr3			= inf->lccr3;	fbi->lccr4			= inf->lccr4;	fbi->state			= C_STARTUP;	fbi->task_state			= (u_char)-1;	for (i = 0; i < inf->num_modes; i++) {		smemlen = mode[i].xres * mode[i].yres * mode[i].bpp / 8;		if (smemlen > fbi->fb.fix.smem_len)			fbi->fb.fix.smem_len = smemlen;	}	init_waitqueue_head(&fbi->ctrlr_wait);	INIT_WORK(&fbi->task, pxafb_task);	init_MUTEX(&fbi->ctrlr_sem);	return fbi;}#ifdef CONFIG_FB_PXA_PARAMETERSstatic int __init pxafb_parse_options(struct device *dev, char *options){	struct pxafb_mach_info *inf = dev->platform_data;	char *this_opt;        if (!options || !*options)                return 0;	dev_dbg(dev, "options are \"%s\"\n", options ? options : "null");	/* could be made table driven or similar?... */        while ((this_opt = strsep(&options, ",")) != NULL) {                if (!strncmp(this_opt, "mode:", 5)) {			const char *name = this_opt+5;			unsigned int namelen = strlen(name);			int res_specified = 0, bpp_specified = 0;			unsigned int xres = 0, yres = 0, bpp = 0;			int yres_specified = 0;			int i;			for (i = namelen-1; i >= 0; i--) {				switch (name[i]) {				case '-':					namelen = i;					if (!bpp_specified && !yres_specified) {						bpp = simple_strtoul(&name[i+1], NULL, 0);						bpp_specified = 1;					} else						goto done;					break;				case 'x':					if (!yres_specified) {						yres = simple_strtoul(&name[i+1], NULL, 0);						yres_specified = 1;					} else						goto done;					break;				case '0' ... '9':					break;				default:					goto done;				}			}			if (i < 0 && yres_specified) {				xres = simple_strtoul(name, NULL, 0);				res_specified = 1;			}		done:			if (res_specified) {				dev_info(dev, "overriding resolution: %dx%d\n", xres, yres);				inf->modes[0].xres = xres; inf->modes[0].yres = yres;			}			if (bpp_specified)				switch (bpp) {				case 1:				case 2:				case 4:				case 8:				case 16:					inf->modes[0].bpp = bpp;					dev_info(dev, "overriding bit depth: %d\n", bpp);					break;				default:					dev_err(dev, "Depth %d is not valid\n", bpp);				}                } else if (!strncmp(this_opt, "pixclock:", 9)) {                        inf->modes[0].pixclock = simple_strtoul(this_opt+9, NULL, 0);			dev_info(dev, "override pixclock: %ld\n", inf->modes[0].pixclock);                } else if (!strncmp(this_opt, "left:", 5)) {                        inf->modes[0].left_margin = simple_strtoul(this_opt+5, NULL, 0);			dev_info(dev, "override left: %u\n", inf->modes[0].left_margin);                } else if (!strncmp(this_opt, "right:", 6)) {                        inf->modes[0].right_margin = simple_strtoul(this_opt+6, NULL, 0);			dev_info(dev, "override right: %u\n", inf->modes[0].right_margin);                } else if (!strncmp(this_opt, "upper:", 6)) {                        inf->modes[0].upper_margin = simple_strtoul(this_opt+6, NULL, 0);			dev_info(dev, "override upper: %u\n", inf->modes[0].upper_margin);                } else if (!strncmp(this_opt, "lower:", 6)) {                        inf->modes[0].lower_margin = simple_strtoul(this_opt+6, NULL, 0);			dev_info(dev, "override lower: %u\n", inf->modes[0].lower_margin);                } else if (!strncmp(this_opt, "hsynclen:", 9)) {                        inf->modes[0].hsync_len = simple_strtoul(this_opt+9, NULL, 0);			dev_info(dev, "override hsynclen: %u\n", inf->modes[0].hsync_len);                } else if (!strncmp(this_opt, "vsynclen:", 9)) {                        inf->modes[0].vsync_len = simple_strtoul(this_opt+9, NULL, 0);			dev_info(dev, "override vsynclen: %u\n", inf->modes[0].vsync_len);                } else if (!strncmp(this_opt, "hsync:", 6)) {                        if (simple_strtoul(this_opt+6, NULL, 0) == 0) {				dev_info(dev, "override hsync: Active Low\n");				inf->modes[0].sync &= ~FB_SYNC_HOR_HIGH_ACT;			} else {				dev_info(dev, "override hsync: Active High\n");				inf->modes[0].sync |= FB_SYNC_HOR_HIGH_ACT;			}                } else if (!strncmp(this_opt, "vsync:", 6)) {                        if (simple_strtoul(this_opt+6, NULL, 0) == 0) {				dev_info(dev, "override vsync: Active Low\n");				inf->modes[0].sync &= ~FB_SYNC_VERT_HIGH_ACT;			} else {				dev_info(dev, "override vsync: Active High\n");				inf->modes[0].sync |= FB_SYNC_VERT_HIGH_ACT;			}                } else if (!strncmp(this_opt, "dpc:", 4)) {                        if (simple_strtoul(this_opt+4, NULL, 0) == 0) {				dev_info(dev, "override double pixel clock: false\n");				inf->lccr3 &= ~LCCR3_DPC;			} else {				dev_info(dev, "override double pixel clock: true\n");				inf->lccr3 |= LCCR3_DPC;			}                } else if (!strncmp(this_opt, "outputen:", 9)) {                        if (simple_strtoul(this_opt+9, NULL, 0) == 0) {				dev_info(dev, "override output enable: active low\n");				inf->lccr3 = (inf->lccr3 & ~LCCR3_OEP) | LCCR3_OutEnL;			} else {				dev_info(dev, "override output enable: active high\n");				inf->lccr3 = (inf->lccr3 & ~LCCR3_OEP) | LCCR3_OutEnH;			}                } else if (!strncmp(this_opt, "pixclockpol:", 12)) {                        if (simple_strtoul(this_opt+12, NULL, 0) == 0) {				dev_info(dev, "override pixel clock polarity: falling edge\n");				inf->lccr3 = (inf->lccr3 & ~LCCR3_PCP) | LCCR3_PixFlEdg;			} else {				dev_info(dev, "override pixel clock polarity: rising edge\n");				inf->lccr3 = (inf->lccr3 & ~LCCR3_PCP) | LCCR3_PixRsEdg;			}                } else if (!strncmp(this_opt, "color", 5)) {			inf->lccr0 = (inf->lccr0 & ~LCCR0_CMS) | LCCR0_Color;                } else if (!strncmp(this_opt, "mono", 4)) {			inf->lccr0 = (inf->lccr0 & ~LCCR0_CMS) | LCCR0_Mono;                } else if (!strncmp(this_opt, "active", 6)) {			inf->lccr0 = (inf->lccr0 & ~LCCR0_PAS) | LCCR0_Act;                } else if (!strncmp(this_opt, "passive", 7)) {			inf->lccr0 = (inf->lccr0 & ~LCCR0_PAS) | LCCR0_Pas;                } else if (!strncmp(this_opt, "single", 6)) {			inf->lccr0 = (inf->lccr0 & ~LCCR0_SDS) | LCCR0_Sngl;                } else if (!strncmp(this_opt, "dual", 4)) {			inf->lccr0 = (inf->lccr0 & ~LCCR0_SDS) | LCCR0_Dual;                } else if (!strncmp(this_opt, "4pix", 4)) {			inf->lccr0 = (inf->lccr0 & ~LCCR0_DPD) | LCCR0_4PixMono;                } else if (!strncmp(this_opt, "8pix", 4)) {			inf->lccr0 = (inf->lccr0 & ~LCCR0_DPD) | LCCR0_8PixMono;		} else {			dev_err(dev, "unknown option: %s\n", this_opt);			return -EINVAL;		}        }        return 0;}#endifint __init pxafb_probe(struct platform_device *dev){	struct pxafb_info *fbi;	struct pxafb_mach_info *inf;	int ret;	dev_dbg(&dev->dev, "pxafb_probe\n");	inf = dev->dev.platform_data;	ret = -ENOMEM;	fbi = NULL;	if (!inf)		goto failed;#ifdef CONFIG_FB_PXA_PARAMETERS	ret = pxafb_parse_options(&dev->dev, g_options);	if (ret < 0)		goto failed;#endif#ifdef DEBUG_VAR        /* Check for various illegal bit-combinations. Currently only	 * a warning is given. */        if (inf->lccr0 & LCCR0_INVALID_CONFIG_MASK)                dev_warn(&dev->dev, "machine LCCR0 setting contains illegal bits: %08x\n",                        inf->lccr0 & LCCR0_INVALID_CONFIG_MASK);        if (inf->lccr3 & LCCR3_INVALID_CONFIG_MASK)                dev_warn(&dev->dev, "machine LCCR3 setting contains illegal bits: %08x\n",                        inf->lccr3 & LCCR3_INVALID_CONFIG_MASK);        if (inf->lccr0 & LCCR0_DPD &&	    ((inf->lccr0 & LCCR0_PAS) != LCCR0_Pas ||	     (inf->lccr0 & LCCR0_SDS) != LCCR0_Sngl ||	     (inf->lccr0 & LCCR0_CMS) != LCCR0_Mono))                dev_warn(&dev->dev, "Double Pixel Data (DPD) mode is only valid in passive mono"			 " single panel mode\n");        if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Act &&	    (inf->lccr0 & LCCR0_SDS) == LCCR0_Dual)                dev_warn(&dev->dev, "Dual panel only valid in passive mode\n");        if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Pas &&             (inf->modes->upper_margin || inf->modes->lower_margin))                dev_warn(&dev->dev, "Upper and lower margins must be 0 in passive mode\n");#endif	dev_dbg(&dev->dev, "got a %dx%dx%d LCD\n",inf->modes->xres, inf->modes->yres, inf->modes->bpp);	if (inf->modes->xres == 0 || inf->modes->yres == 0 || inf->modes->bpp == 0) {		dev_err(&dev->dev, "Invalid resolution or bit depth\n");		ret = -EINVAL;		goto failed;	}	pxafb_backlight_power = inf->pxafb_backlight_power;	pxafb_lcd_power = inf->pxafb_lcd_power;	fbi = pxafb_init_fbinfo(&dev->dev);	if (!fbi) {		dev_err(&dev->dev, "Failed to initialize framebuffer device\n");		ret = -ENOMEM; // only reason for pxafb_init_fbinfo to fail is kmalloc		goto failed;	}	/* Initialize video memory */	ret = pxafb_map_video_memory(fbi);	if (ret) {		dev_err(&dev->dev, "Failed to allocate video RAM: %d\n", ret);		ret = -ENOMEM;		goto failed;	}	ret = request_irq(IRQ_LCD, pxafb_handle_irq, IRQF_DISABLED, "LCD", fbi);	if (ret) {		dev_err(&dev->dev, "request_irq failed: %d\n", ret);		ret = -EBUSY;		goto failed;	}	/*	 * This makes sure that our colour bitfield	 * descriptors are correctly initialised.	 */	pxafb_check_var(&fbi->fb.var, &fbi->fb);	pxafb_set_par(&fbi->fb);	platform_set_drvdata(dev, fbi);	ret = register_framebuffer(&fbi->fb);	if (ret < 0) {		dev_err(&dev->dev, "Failed to register framebuffer device: %d\n", ret);		goto failed;	}#ifdef CONFIG_PM	// TODO#endif#ifdef CONFIG_CPU_FREQ	fbi->freq_transition.notifier_call = pxafb_freq_transition;	fbi->freq_policy.notifier_call = pxafb_freq_policy;	cpufreq_register_notifier(&fbi->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);	cpufreq_register_notifier(&fbi->freq_policy, CPUFREQ_POLICY_NOTIFIER);#endif	/*	 * Ok, now enable the LCD controller	 */	set_ctrlr_state(fbi, C_ENABLE);	return 0;failed:	platform_set_drvdata(dev, NULL);	kfree(fbi);	return ret;}static struct platform_driver pxafb_driver = {	.probe		= pxafb_probe,#ifdef CONFIG_PM	.suspend	= pxafb_suspend,	.resume		= pxafb_resume,#endif	.driver		= {		.name	= "pxa2xx-fb",	},};#ifndef MODULEint __devinit pxafb_setup(char *options){# ifdef CONFIG_FB_PXA_PARAMETERS	if (options)		strlcpy(g_options, options, sizeof(g_options));# endif	return 0;}#else# ifdef CONFIG_FB_PXA_PARAMETERSmodule_param_string(options, g_options, sizeof(g_options), 0);MODULE_PARM_DESC(options, "LCD parameters (see Documentation/fb/pxafb.txt)");# endif#endifint __devinit pxafb_init(void){#ifndef MODULE	char *option = NULL;	if (fb_get_options("pxafb", &option))		return -ENODEV;	pxafb_setup(option);#endif	return platform_driver_register(&pxafb_driver);}module_init(pxafb_init);MODULE_DESCRIPTION("loadable framebuffer driver for PXA");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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