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 + -
显示快捷键?