📄 pxafb.c
字号:
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 + -