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