pxafb.c

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

C
1,520
字号
 *                2 ( PCD + 1 ) * *   PCD =      LCLK *         ------------- - 1 *         2(PixelClock) * * Where: *   LCLK = LCD/Memory Clock *   PCD = LCCR3[7:0] * * PixelClock here is in Hz while the pixclock argument given is the * period in picoseconds. Hence PixelClock = 1 / ( pixclock * 10^-12 ) * * The function get_lclk_frequency_10khz returns LCLK in units of * 10khz. Calling the result of this function lclk gives us the * following * *    PCD = (lclk * 10^4 ) * ( pixclock * 10^-12 ) *          -------------------------------------- - 1 *                          2 * * Factoring the 10^4 and 10^-12 out gives 10^-8 == 1 / 100000000 as used below. */static inline unsigned int get_pcd(struct pxafb_info *fbi, unsigned int pixclock){	unsigned long long pcd;	/* FIXME: Need to take into account Double Pixel Clock mode	 * (DPC) bit? or perhaps set it based on the various clock	 * speeds */	pcd = (unsigned long long)(clk_get_rate(fbi->clk) / 10000);	pcd *= pixclock;	do_div(pcd, 100000000 * 2);	/* no need for this, since we should subtract 1 anyway. they cancel */	/* pcd += 1; */ /* make up for integer math truncations */	return (unsigned int)pcd;}/* * Some touchscreens need hsync information from the video driver to * function correctly. We export it here.  Note that 'hsync_time' and * the value returned from pxafb_get_hsync_time() is the *reciprocal* * of the hsync period in seconds. */static inline void set_hsync_time(struct pxafb_info *fbi, unsigned int pcd){	unsigned long htime;	if ((pcd == 0) || (fbi->fb.var.hsync_len == 0)) {		fbi->hsync_time=0;		return;	}	htime = clk_get_rate(fbi->clk) / (pcd * fbi->fb.var.hsync_len);	fbi->hsync_time = htime;}unsigned long pxafb_get_hsync_time(struct device *dev){	struct pxafb_info *fbi = dev_get_drvdata(dev);	/* If display is blanked/suspended, hsync isn't active */	if (!fbi || (fbi->state != C_ENABLE))		return 0;	return fbi->hsync_time;}EXPORT_SYMBOL(pxafb_get_hsync_time);/* * pxafb_activate_var(): *	Configures LCD Controller based on entries in var parameter.  Settings are *	only written to the controller if changes were made. */static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *fbi){	struct pxafb_lcd_reg new_regs;	u_long flags;	u_int lines_per_panel, pcd = get_pcd(fbi, var->pixclock);	pr_debug("pxafb: Configuring PXA LCD\n");	pr_debug("var: xres=%d hslen=%d lm=%d rm=%d\n",		 var->xres, var->hsync_len,		 var->left_margin, var->right_margin);	pr_debug("var: yres=%d vslen=%d um=%d bm=%d\n",		 var->yres, var->vsync_len,		 var->upper_margin, var->lower_margin);	pr_debug("var: pixclock=%d pcd=%d\n", var->pixclock, pcd);#if DEBUG_VAR	if (var->xres < 16        || var->xres > 1024)		printk(KERN_ERR "%s: invalid xres %d\n",			fbi->fb.fix.id, var->xres);	switch(var->bits_per_pixel) {	case 1:	case 2:	case 4:	case 8:	case 16:		break;	default:		printk(KERN_ERR "%s: invalid bit depth %d\n",		       fbi->fb.fix.id, var->bits_per_pixel);		break;	}	if (var->hsync_len < 1    || var->hsync_len > 64)		printk(KERN_ERR "%s: invalid hsync_len %d\n",			fbi->fb.fix.id, var->hsync_len);	if (var->left_margin < 1  || var->left_margin > 255)		printk(KERN_ERR "%s: invalid left_margin %d\n",			fbi->fb.fix.id, var->left_margin);	if (var->right_margin < 1 || var->right_margin > 255)		printk(KERN_ERR "%s: invalid right_margin %d\n",			fbi->fb.fix.id, var->right_margin);	if (var->yres < 1         || var->yres > 1024)		printk(KERN_ERR "%s: invalid yres %d\n",			fbi->fb.fix.id, var->yres);	if (var->vsync_len < 1    || var->vsync_len > 64)		printk(KERN_ERR "%s: invalid vsync_len %d\n",			fbi->fb.fix.id, var->vsync_len);	if (var->upper_margin < 0 || var->upper_margin > 255)		printk(KERN_ERR "%s: invalid upper_margin %d\n",			fbi->fb.fix.id, var->upper_margin);	if (var->lower_margin < 0 || var->lower_margin > 255)		printk(KERN_ERR "%s: invalid lower_margin %d\n",			fbi->fb.fix.id, var->lower_margin);#endif	new_regs.lccr0 = fbi->lccr0 |		(LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM |                 LCCR0_QDM | LCCR0_BM  | LCCR0_OUM);	new_regs.lccr1 =		LCCR1_DisWdth(var->xres) +		LCCR1_HorSnchWdth(var->hsync_len) +		LCCR1_BegLnDel(var->left_margin) +		LCCR1_EndLnDel(var->right_margin);	/*	 * If we have a dual scan LCD, we need to halve	 * the YRES parameter.	 */	lines_per_panel = var->yres;	if ((fbi->lccr0 & LCCR0_SDS) == LCCR0_Dual)		lines_per_panel /= 2;	new_regs.lccr2 =		LCCR2_DisHght(lines_per_panel) +		LCCR2_VrtSnchWdth(var->vsync_len) +		LCCR2_BegFrmDel(var->upper_margin) +		LCCR2_EndFrmDel(var->lower_margin);	new_regs.lccr3 = fbi->lccr3 |		pxafb_bpp_to_lccr3(var) |		(var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) |		(var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL);	if (pcd)		new_regs.lccr3 |= LCCR3_PixClkDiv(pcd);	pr_debug("nlccr0 = 0x%08x\n", new_regs.lccr0);	pr_debug("nlccr1 = 0x%08x\n", new_regs.lccr1);	pr_debug("nlccr2 = 0x%08x\n", new_regs.lccr2);	pr_debug("nlccr3 = 0x%08x\n", new_regs.lccr3);	/* Update shadow copy atomically */	local_irq_save(flags);	/* setup dma descriptors */	fbi->dmadesc_fblow_cpu = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette_cpu - 3*16);	fbi->dmadesc_fbhigh_cpu = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette_cpu - 2*16);	fbi->dmadesc_palette_cpu = (struct pxafb_dma_descriptor *)((unsigned int)fbi->palette_cpu - 1*16);	fbi->dmadesc_fblow_dma = fbi->palette_dma - 3*16;	fbi->dmadesc_fbhigh_dma = fbi->palette_dma - 2*16;	fbi->dmadesc_palette_dma = fbi->palette_dma - 1*16;#define BYTES_PER_PANEL (lines_per_panel * fbi->fb.fix.line_length)	/* populate descriptors */	fbi->dmadesc_fblow_cpu->fdadr = fbi->dmadesc_fblow_dma;	fbi->dmadesc_fblow_cpu->fsadr = fbi->screen_dma + BYTES_PER_PANEL;	fbi->dmadesc_fblow_cpu->fidr  = 0;	fbi->dmadesc_fblow_cpu->ldcmd = BYTES_PER_PANEL;	fbi->fdadr1 = fbi->dmadesc_fblow_dma; /* only used in dual-panel mode */	fbi->dmadesc_fbhigh_cpu->fsadr = fbi->screen_dma;	fbi->dmadesc_fbhigh_cpu->fidr = 0;	fbi->dmadesc_fbhigh_cpu->ldcmd = BYTES_PER_PANEL;	fbi->dmadesc_palette_cpu->fsadr = fbi->palette_dma;	fbi->dmadesc_palette_cpu->fidr  = 0;	if ((fbi->lccr4 & LCCR4_PAL_FOR_MASK) == LCCR4_PAL_FOR_0)		fbi->dmadesc_palette_cpu->ldcmd = fbi->palette_size *							sizeof(u16);	else		fbi->dmadesc_palette_cpu->ldcmd = fbi->palette_size *							sizeof(u32);	fbi->dmadesc_palette_cpu->ldcmd |= LDCMD_PAL;	if (var->bits_per_pixel == 16) {		/* palette shouldn't be loaded in true-color mode */		fbi->dmadesc_fbhigh_cpu->fdadr = fbi->dmadesc_fbhigh_dma;		fbi->fdadr0 = fbi->dmadesc_fbhigh_dma; /* no pal just fbhigh */		/* init it to something, even though we won't be using it */		fbi->dmadesc_palette_cpu->fdadr = fbi->dmadesc_palette_dma;	} else {		fbi->dmadesc_palette_cpu->fdadr = fbi->dmadesc_fbhigh_dma;		fbi->dmadesc_fbhigh_cpu->fdadr = fbi->dmadesc_palette_dma;		fbi->fdadr0 = fbi->dmadesc_palette_dma; /* flips back and forth between pal and fbhigh */	}#if 0	pr_debug("fbi->dmadesc_fblow_cpu = 0x%p\n", fbi->dmadesc_fblow_cpu);	pr_debug("fbi->dmadesc_fbhigh_cpu = 0x%p\n", fbi->dmadesc_fbhigh_cpu);	pr_debug("fbi->dmadesc_palette_cpu = 0x%p\n", fbi->dmadesc_palette_cpu);	pr_debug("fbi->dmadesc_fblow_dma = 0x%x\n", fbi->dmadesc_fblow_dma);	pr_debug("fbi->dmadesc_fbhigh_dma = 0x%x\n", fbi->dmadesc_fbhigh_dma);	pr_debug("fbi->dmadesc_palette_dma = 0x%x\n", fbi->dmadesc_palette_dma);	pr_debug("fbi->dmadesc_fblow_cpu->fdadr = 0x%x\n", fbi->dmadesc_fblow_cpu->fdadr);	pr_debug("fbi->dmadesc_fbhigh_cpu->fdadr = 0x%x\n", fbi->dmadesc_fbhigh_cpu->fdadr);	pr_debug("fbi->dmadesc_palette_cpu->fdadr = 0x%x\n", fbi->dmadesc_palette_cpu->fdadr);	pr_debug("fbi->dmadesc_fblow_cpu->fsadr = 0x%x\n", fbi->dmadesc_fblow_cpu->fsadr);	pr_debug("fbi->dmadesc_fbhigh_cpu->fsadr = 0x%x\n", fbi->dmadesc_fbhigh_cpu->fsadr);	pr_debug("fbi->dmadesc_palette_cpu->fsadr = 0x%x\n", fbi->dmadesc_palette_cpu->fsadr);	pr_debug("fbi->dmadesc_fblow_cpu->ldcmd = 0x%x\n", fbi->dmadesc_fblow_cpu->ldcmd);	pr_debug("fbi->dmadesc_fbhigh_cpu->ldcmd = 0x%x\n", fbi->dmadesc_fbhigh_cpu->ldcmd);	pr_debug("fbi->dmadesc_palette_cpu->ldcmd = 0x%x\n", fbi->dmadesc_palette_cpu->ldcmd);#endif	fbi->reg_lccr0 = new_regs.lccr0;	fbi->reg_lccr1 = new_regs.lccr1;	fbi->reg_lccr2 = new_regs.lccr2;	fbi->reg_lccr3 = new_regs.lccr3;	fbi->reg_lccr4 = LCCR4 & (~LCCR4_PAL_FOR_MASK);	fbi->reg_lccr4 |= (fbi->lccr4 & LCCR4_PAL_FOR_MASK);	set_hsync_time(fbi, pcd);	local_irq_restore(flags);	/*	 * Only update the registers if the controller is enabled	 * and something has changed.	 */	if ((LCCR0  != fbi->reg_lccr0) || (LCCR1  != fbi->reg_lccr1) ||	    (LCCR2  != fbi->reg_lccr2) || (LCCR3  != fbi->reg_lccr3) ||	    (FDADR0 != fbi->fdadr0)    || (FDADR1 != fbi->fdadr1))		pxafb_schedule_work(fbi, C_REENABLE);	return 0;}/* * NOTE!  The following functions are purely helpers for set_ctrlr_state. * Do not call them directly; set_ctrlr_state does the correct serialisation * to ensure that things happen in the right way 100% of time time. *	-- rmk */static inline void __pxafb_backlight_power(struct pxafb_info *fbi, int on){	pr_debug("pxafb: backlight o%s\n", on ? "n" : "ff"); 	if (pxafb_backlight_power) 		pxafb_backlight_power(on);}static inline void __pxafb_lcd_power(struct pxafb_info *fbi, int on){	pr_debug("pxafb: LCD power o%s\n", on ? "n" : "ff");	if (pxafb_lcd_power)		pxafb_lcd_power(on, &fbi->fb.var);}static void pxafb_setup_gpio(struct pxafb_info *fbi){	int gpio, ldd_bits;        unsigned int lccr0 = fbi->lccr0;	/*	 * setup is based on type of panel supported        */	/* 4 bit interface */	if ((lccr0 & LCCR0_CMS) == LCCR0_Mono &&	    (lccr0 & LCCR0_SDS) == LCCR0_Sngl &&	    (lccr0 & LCCR0_DPD) == LCCR0_4PixMono)		ldd_bits = 4;	/* 8 bit interface */        else if (((lccr0 & LCCR0_CMS) == LCCR0_Mono &&		  ((lccr0 & LCCR0_SDS) == LCCR0_Dual || (lccr0 & LCCR0_DPD) == LCCR0_8PixMono)) ||                 ((lccr0 & LCCR0_CMS) == LCCR0_Color &&		  (lccr0 & LCCR0_PAS) == LCCR0_Pas && (lccr0 & LCCR0_SDS) == LCCR0_Sngl))		ldd_bits = 8;	/* 16 bit interface */	else if ((lccr0 & LCCR0_CMS) == LCCR0_Color &&		 ((lccr0 & LCCR0_SDS) == LCCR0_Dual || (lccr0 & LCCR0_PAS) == LCCR0_Act))		ldd_bits = 16;	else {	        printk(KERN_ERR "pxafb_setup_gpio: unable to determine bits per pixel\n");		return;        }	for (gpio = 58; ldd_bits; gpio++, ldd_bits--)		pxa_gpio_mode(gpio | GPIO_ALT_FN_2_OUT);	pxa_gpio_mode(GPIO74_LCD_FCLK_MD);	pxa_gpio_mode(GPIO75_LCD_LCLK_MD);	pxa_gpio_mode(GPIO76_LCD_PCLK_MD);	pxa_gpio_mode(GPIO77_LCD_ACBIAS_MD);}static void pxafb_enable_controller(struct pxafb_info *fbi){	pr_debug("pxafb: Enabling LCD controller\n");	pr_debug("fdadr0 0x%08x\n", (unsigned int) fbi->fdadr0);	pr_debug("fdadr1 0x%08x\n", (unsigned int) fbi->fdadr1);	pr_debug("reg_lccr0 0x%08x\n", (unsigned int) fbi->reg_lccr0);	pr_debug("reg_lccr1 0x%08x\n", (unsigned int) fbi->reg_lccr1);	pr_debug("reg_lccr2 0x%08x\n", (unsigned int) fbi->reg_lccr2);	pr_debug("reg_lccr3 0x%08x\n", (unsigned int) fbi->reg_lccr3);	/* enable LCD controller clock */	clk_enable(fbi->clk);	/* Sequence from 11.7.10 */	LCCR3 = fbi->reg_lccr3;	LCCR2 = fbi->reg_lccr2;	LCCR1 = fbi->reg_lccr1;	LCCR0 = fbi->reg_lccr0 & ~LCCR0_ENB;	FDADR0 = fbi->fdadr0;	FDADR1 = fbi->fdadr1;	LCCR0 |= LCCR0_ENB;	pr_debug("FDADR0 0x%08x\n", (unsigned int) FDADR0);	pr_debug("FDADR1 0x%08x\n", (unsigned int) FDADR1);	pr_debug("LCCR0 0x%08x\n", (unsigned int) LCCR0);	pr_debug("LCCR1 0x%08x\n", (unsigned int) LCCR1);	pr_debug("LCCR2 0x%08x\n", (unsigned int) LCCR2);	pr_debug("LCCR3 0x%08x\n", (unsigned int) LCCR3);	pr_debug("LCCR4 0x%08x\n", (unsigned int) LCCR4);}static void pxafb_disable_controller(struct pxafb_info *fbi){	DECLARE_WAITQUEUE(wait, current);	pr_debug("pxafb: disabling LCD controller\n");	set_current_state(TASK_UNINTERRUPTIBLE);	add_wait_queue(&fbi->ctrlr_wait, &wait);	LCSR = 0xffffffff;	/* Clear LCD Status Register */	LCCR0 &= ~LCCR0_LDM;	/* Enable LCD Disable Done Interrupt */	LCCR0 |= LCCR0_DIS;	/* Disable LCD Controller */	schedule_timeout(200 * HZ / 1000);	remove_wait_queue(&fbi->ctrlr_wait, &wait);	/* disable LCD controller clock */	clk_disable(fbi->clk);}/* *  pxafb_handle_irq: Handle 'LCD DONE' interrupts. */static irqreturn_t pxafb_handle_irq(int irq, void *dev_id){	struct pxafb_info *fbi = dev_id;	unsigned int lcsr = LCSR;	if (lcsr & LCSR_LDD) {		LCCR0 |= LCCR0_LDM;		wake_up(&fbi->ctrlr_wait);	}	LCSR = lcsr;	return IRQ_HANDLED;}/* * This function must be called from task context only, since it will * sleep when disabling the LCD controller, or if we get two contending * processes trying to alter state. */static void set_ctrlr_state(struct pxafb_info *fbi, u_int state){	u_int old_state;	down(&fbi->ctrlr_sem);	old_state = fbi->state;	/*	 * Hack around fbcon initialisation.	 */	if (old_state == C_STARTUP && state == C_REENABLE)		state = C_ENABLE;	switch (state) {	case C_DISABLE_CLKCHANGE:		/*		 * Disable controller for clock change.  If the		 * controller is already disabled, then do nothing.		 */		if (old_state != C_DISABLE && old_state != C_DISABLE_PM) {			fbi->state = state;			//TODO __pxafb_lcd_power(fbi, 0);			pxafb_disable_controller(fbi);		}		break;	case C_DISABLE_PM:	case C_DISABLE:		/*		 * Disable controller		 */		if (old_state != C_DISABLE) {			fbi->state = state;			__pxafb_backlight_power(fbi, 0);			__pxafb_lcd_power(fbi, 0);			if (old_state != C_DISABLE_CLKCHANGE)				pxafb_disable_controller(fbi);		}		break;	case C_ENABLE_CLKCHANGE:		/*		 * Enable the controller after clock change.  Only		 * do this if we were disabled for the clock change.		 */		if (old_state == C_DISABLE_CLKCHANGE) {			fbi->state = C_ENABLE;			pxafb_enable_controller(fbi);			//TODO __pxafb_lcd_power(fbi, 1);		}		break;	case C_REENABLE:		/*		 * Re-enable the controller only if it was already		 * enabled.  This is so we reprogram the control		 * registers.		 */		if (old_state == C_ENABLE) {			__pxafb_lcd_power(fbi, 0);			pxafb_disable_controller(fbi);			pxafb_setup_gpio(fbi);			pxafb_enable_controller(fbi);			__pxafb_lcd_power(fbi, 1);		}		break;	case C_ENABLE_PM:		/*		 * Re-enable the controller after PM.  This is not		 * perfect - think about the case where we were doing		 * a clock change, and we suspended half-way through.		 */		if (old_state != C_DISABLE_PM)			break;		/* fall through */	case C_ENABLE:		/*		 * Power up the LCD screen, enable controller, and		 * turn on the backlight.		 */		if (old_state != C_ENABLE) {			fbi->state = C_ENABLE;			pxafb_setup_gpio(fbi);			pxafb_enable_controller(fbi);			__pxafb_lcd_power(fbi, 1);			__pxafb_backlight_power(fbi, 1);		}		break;	}	up(&fbi->ctrlr_sem);}/* * Our LCD controller task (which is called when we blank or unblank) * via keventd. */static void pxafb_task(struct work_struct *work){	struct pxafb_info *fbi =		container_of(work, struct pxafb_info, task);	u_int state = xchg(&fbi->task_state, -1);	set_ctrlr_state(fbi, state);}#ifdef CONFIG_CPU_FREQ/* * CPU clock speed change handler.  We need to adjust the LCD timing * parameters when the CPU clock is adjusted by the power management * subsystem. *

⌨️ 快捷键说明

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