⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pxafb.c

📁 Linux环境下视频显示卡设备的驱动程序源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	lcd_writel(fbi, FDADR6, 0);	fbi->n_smart_cmds = 0;	return ret;}int pxafb_smart_queue(struct fb_info *info, uint16_t *cmds, int n_cmds){	int i;	struct pxafb_info *fbi = container_of(info, struct pxafb_info, fb);	for (i = 0; i < n_cmds; i++, cmds++) {		/* if it is a software delay, flush and delay */		if ((*cmds & 0xff00) == SMART_CMD_DELAY) {			pxafb_smart_flush(info);			mdelay(*cmds & 0xff);			continue;		}		/* leave 2 commands for INTERRUPT and WAIT_FOR_SYNC */		if (fbi->n_smart_cmds == CMD_BUFF_SIZE - 8)			pxafb_smart_flush(info);		fbi->smart_cmds[fbi->n_smart_cmds++] = *cmds;	}	return 0;}static unsigned int __smart_timing(unsigned time_ns, unsigned long lcd_clk){	unsigned int t = (time_ns * (lcd_clk / 1000000) / 1000);	return (t == 0) ? 1 : t;}static void setup_smart_timing(struct pxafb_info *fbi,				struct fb_var_screeninfo *var){	struct pxafb_mach_info *inf = fbi->dev->platform_data;	struct pxafb_mode_info *mode = &inf->modes[0];	unsigned long lclk = clk_get_rate(fbi->clk);	unsigned t1, t2, t3, t4;	t1 = max(mode->a0csrd_set_hld, mode->a0cswr_set_hld);	t2 = max(mode->rd_pulse_width, mode->wr_pulse_width);	t3 = mode->op_hold_time;	t4 = mode->cmd_inh_time;	fbi->reg_lccr1 =		LCCR1_DisWdth(var->xres) |		LCCR1_BegLnDel(__smart_timing(t1, lclk)) |		LCCR1_EndLnDel(__smart_timing(t2, lclk)) |		LCCR1_HorSnchWdth(__smart_timing(t3, lclk));	fbi->reg_lccr2 = LCCR2_DisHght(var->yres);	fbi->reg_lccr3 = fbi->lccr3 | LCCR3_PixClkDiv(__smart_timing(t4, lclk));	fbi->reg_lccr3 |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? LCCR3_HSP : 0;	fbi->reg_lccr3 |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? LCCR3_VSP : 0;	/* FIXME: make this configurable */	fbi->reg_cmdcr = 1;}static int pxafb_smart_thread(void *arg){	struct pxafb_info *fbi = arg;	struct pxafb_mach_info *inf = fbi->dev->platform_data;	if (!fbi || !inf->smart_update) {		pr_err("%s: not properly initialized, thread terminated\n",				__func__);		return -EINVAL;	}	pr_debug("%s(): task starting\n", __func__);	set_freezable();	while (!kthread_should_stop()) {		if (try_to_freeze())			continue;		mutex_lock(&fbi->ctrlr_lock);		if (fbi->state == C_ENABLE) {			inf->smart_update(&fbi->fb);			complete(&fbi->refresh_done);		}		mutex_unlock(&fbi->ctrlr_lock);		set_current_state(TASK_INTERRUPTIBLE);		schedule_timeout(30 * HZ / 1000);	}	pr_debug("%s(): task ending\n", __func__);	return 0;}static int pxafb_smart_init(struct pxafb_info *fbi){	if (!(fbi->lccr0 & LCCR0_LCDT))		return 0;	fbi->smart_cmds = (uint16_t *) fbi->dma_buff->cmd_buff;	fbi->n_smart_cmds = 0;	init_completion(&fbi->command_done);	init_completion(&fbi->refresh_done);	fbi->smart_thread = kthread_run(pxafb_smart_thread, fbi,					"lcd_refresh");	if (IS_ERR(fbi->smart_thread)) {		pr_err("%s: unable to create kernel thread\n", __func__);		return PTR_ERR(fbi->smart_thread);	}	return 0;}#elseint pxafb_smart_queue(struct fb_info *info, uint16_t *cmds, int n_cmds){	return 0;}int pxafb_smart_flush(struct fb_info *info){	return 0;}static inline int pxafb_smart_init(struct pxafb_info *fbi) { return 0; }#endif /* CONFIG_FB_PXA_SMARTPANEL */static void setup_parallel_timing(struct pxafb_info *fbi,				  struct fb_var_screeninfo *var){	unsigned int lines_per_panel, pcd = get_pcd(fbi, var->pixclock);	fbi->reg_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;	fbi->reg_lccr2 =		LCCR2_DisHght(lines_per_panel) +		LCCR2_VrtSnchWdth(var->vsync_len) +		LCCR2_BegFrmDel(var->upper_margin) +		LCCR2_EndFrmDel(var->lower_margin);	fbi->reg_lccr3 = fbi->lccr3 |		(var->sync & FB_SYNC_HOR_HIGH_ACT ?		 LCCR3_HorSnchH : LCCR3_HorSnchL) |		(var->sync & FB_SYNC_VERT_HIGH_ACT ?		 LCCR3_VrtSnchH : LCCR3_VrtSnchL);	if (pcd) {		fbi->reg_lccr3 |= LCCR3_PixClkDiv(pcd);		set_hsync_time(fbi, pcd);	}}/* * 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){	u_long flags;	/* Update shadow copy atomically */	local_irq_save(flags);#ifdef CONFIG_FB_PXA_SMARTPANEL	if (fbi->lccr0 & LCCR0_LCDT)		setup_smart_timing(fbi, var);	else#endif		setup_parallel_timing(fbi, var);	setup_base_frame(fbi, 0);	fbi->reg_lccr0 = fbi->lccr0 |		(LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM |		 LCCR0_QDM | LCCR0_BM  | LCCR0_OUM);	fbi->reg_lccr3 |= pxafb_var_to_lccr3(var);	fbi->reg_lccr4 = lcd_readl(fbi, LCCR4) & ~LCCR4_PAL_FOR_MASK;	fbi->reg_lccr4 |= (fbi->lccr4 & LCCR4_PAL_FOR_MASK);	local_irq_restore(flags);	/*	 * Only update the registers if the controller is enabled	 * and something has changed.	 */	if ((lcd_readl(fbi, LCCR0) != fbi->reg_lccr0) ||	    (lcd_readl(fbi, LCCR1) != fbi->reg_lccr1) ||	    (lcd_readl(fbi, LCCR2) != fbi->reg_lccr2) ||	    (lcd_readl(fbi, LCCR3) != fbi->reg_lccr3) ||	    (lcd_readl(fbi, LCCR4) != fbi->reg_lccr4) ||	    (lcd_readl(fbi, FDADR0) != fbi->fdadr[0]) ||	    (lcd_readl(fbi, FDADR1) != fbi->fdadr[1]))		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 (fbi->backlight_power)		fbi->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 (fbi->lcd_power)		fbi->lcd_power(on, &fbi->fb.var);}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->fdadr[0]);	pr_debug("fdadr1 0x%08x\n", (unsigned int) fbi->fdadr[1]);	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);	if (fbi->lccr0 & LCCR0_LCDT)		return;	/* Sequence from 11.7.10 */	lcd_writel(fbi, LCCR4, fbi->reg_lccr4);	lcd_writel(fbi, LCCR3, fbi->reg_lccr3);	lcd_writel(fbi, LCCR2, fbi->reg_lccr2);	lcd_writel(fbi, LCCR1, fbi->reg_lccr1);	lcd_writel(fbi, LCCR0, fbi->reg_lccr0 & ~LCCR0_ENB);	lcd_writel(fbi, FDADR0, fbi->fdadr[0]);	lcd_writel(fbi, FDADR1, fbi->fdadr[1]);	lcd_writel(fbi, LCCR0, fbi->reg_lccr0 | LCCR0_ENB);}static void pxafb_disable_controller(struct pxafb_info *fbi){	uint32_t lccr0;#ifdef CONFIG_FB_PXA_SMARTPANEL	if (fbi->lccr0 & LCCR0_LCDT) {		wait_for_completion_timeout(&fbi->refresh_done,				200 * HZ / 1000);		return;	}#endif	/* Clear LCD Status Register */	lcd_writel(fbi, LCSR, 0xffffffff);	lccr0 = lcd_readl(fbi, LCCR0) & ~LCCR0_LDM;	lcd_writel(fbi, LCCR0, lccr0);	lcd_writel(fbi, LCCR0, lccr0 | LCCR0_DIS);	wait_for_completion_timeout(&fbi->disable_done, 200 * HZ / 1000);	/* 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 lccr0, lcsr, lcsr1;	lcsr = lcd_readl(fbi, LCSR);	if (lcsr & LCSR_LDD) {		lccr0 = lcd_readl(fbi, LCCR0);		lcd_writel(fbi, LCCR0, lccr0 | LCCR0_LDM);		complete(&fbi->disable_done);	}#ifdef CONFIG_FB_PXA_SMARTPANEL	if (lcsr & LCSR_CMD_INT)		complete(&fbi->command_done);#endif	lcd_writel(fbi, LCSR, lcsr);#ifdef CONFIG_FB_PXA_OVERLAY	lcsr1 = lcd_readl(fbi, LCSR1);	if (lcsr1 & LCSR1_BS(1))		complete(&fbi->overlay[0].branch_done);	if (lcsr1 & LCSR1_BS(2))		complete(&fbi->overlay[1].branch_done);	lcd_writel(fbi, LCSR1, lcsr1);#endif	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;	mutex_lock(&fbi->ctrlr_lock);	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_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_enable_controller(fbi);			__pxafb_lcd_power(fbi, 1);			__pxafb_backlight_power(fbi, 1);		}		break;	}	mutex_unlock(&fbi->ctrlr_lock);}/* * 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. * * 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:		pr_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;	}	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#endifstatic int __devinit pxafb_init_video_memory(struct pxafb_info *fbi){	int size = PAGE_ALIGN(fbi->video_mem_size);	fbi->video_mem = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);	if (fbi->video_mem == NULL)		return -ENOMEM;	fbi->video_mem_phys = virt_to_phys(fbi->video_mem);	fbi->video_mem_size = size;	fbi->fb.fix.smem_start	= fbi->video_mem_phys;	fbi->fb.fix.smem_len	= fbi->video_mem_size;	fbi->fb.screen_base	= fbi->video_mem;	return fbi->video_mem ? 0 : -ENOMEM;}static void pxafb_decode_mach_info(struct pxafb_info *fbi,				   struct pxafb_mach_info *inf){	unsigned int lcd_conn = inf->lcd_conn;	struct pxafb_mode_info *m;	int i;	fbi->cmap_inverse	= inf->cmap_inverse;	fbi->cmap_static	= inf->cmap_static;	fbi->lccr4 		= inf->lccr4;	switch (lcd_conn & LCD_TYPE_MASK) {	case LCD_TYPE_MONO_STN:		fbi->lccr0 = LCCR0_CMS;		break;	case LCD_TYPE_MONO_DSTN:		fbi->lccr0 = LCCR0_CMS | LCCR0_SDS;		break;	case LCD_TYPE_COLOR_STN:		fbi->lccr0 = 0;		break;	case LCD_TYPE_COLOR_DSTN:		fbi->lccr0 = LCCR0_SDS;		break;	case LCD_TYPE_COLOR_TFT:		fbi->lccr0 = LCCR0_PAS;		break;	case LCD_TYPE_SMART_PANEL:		fbi->lccr0 = LCCR0_LCDT | LCCR0_PAS;		break;	default:		/* fall back to backward compatibility way */		fbi->lccr0 = inf->lccr0;		fbi->lccr3 = inf->lccr3;

⌨️ 快捷键说明

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