X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fvideo%2Fpxafb.c;h=b4947c810706c14c95d9406a51cebad10d20c47e;hb=6ae7232084ffea750968320241a0276d2211d736;hp=efd9333b05c24001b13a8212be03a91215dbcdaf;hpb=d052d1beff706920e82c5d55006b08e256b5df09;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index efd9333..b4947c8 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -22,7 +22,6 @@ * */ -#include #include #include #include @@ -60,7 +59,7 @@ #define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP|LCCR3_VSP|LCCR3_PCD|LCCR3_BPP) static void (*pxafb_backlight_power)(int); -static void (*pxafb_lcd_power)(int); +static void (*pxafb_lcd_power)(int, struct fb_var_screeninfo *); static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); @@ -215,6 +214,48 @@ extern unsigned int get_clk_frequency_khz(int info); #endif /* + * Select the smallest mode that allows the desired resolution to be + * displayed. If desired parameters can be rounded up. + */ +static struct pxafb_mode_info *pxafb_getmode(struct pxafb_mach_info *mach, struct fb_var_screeninfo *var) +{ + struct pxafb_mode_info *mode = NULL; + struct pxafb_mode_info *modelist = mach->modes; + unsigned int best_x = 0xffffffff, best_y = 0xffffffff; + unsigned int i; + + for (i = 0 ; i < mach->num_modes ; i++) { + if (modelist[i].xres >= var->xres && modelist[i].yres >= var->yres && + modelist[i].xres < best_x && modelist[i].yres < best_y && + modelist[i].bpp >= var->bits_per_pixel ) { + best_x = modelist[i].xres; + best_y = modelist[i].yres; + mode = &modelist[i]; + } + } + + return mode; +} + +static void pxafb_setmode(struct fb_var_screeninfo *var, struct pxafb_mode_info *mode) +{ + var->xres = mode->xres; + var->yres = mode->yres; + var->bits_per_pixel = mode->bpp; + var->pixclock = mode->pixclock; + var->hsync_len = mode->hsync_len; + var->left_margin = mode->left_margin; + var->right_margin = mode->right_margin; + var->vsync_len = mode->vsync_len; + var->upper_margin = mode->upper_margin; + var->lower_margin = mode->lower_margin; + var->sync = mode->sync; + var->grayscale = mode->cmap_greyscale; + var->xres_virtual = var->xres; + var->yres_virtual = var->yres; +} + +/* * pxafb_check_var(): * Get the video params out of 'var'. If a value doesn't fit, round it up, * if it's too big, return -EINVAL. @@ -226,15 +267,29 @@ extern unsigned int get_clk_frequency_khz(int info); static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct pxafb_info *fbi = (struct pxafb_info *)info; + struct pxafb_mach_info *inf = fbi->dev->platform_data; if (var->xres < MIN_XRES) var->xres = MIN_XRES; if (var->yres < MIN_YRES) var->yres = MIN_YRES; - if (var->xres > fbi->max_xres) - var->xres = fbi->max_xres; - if (var->yres > fbi->max_yres) - var->yres = fbi->max_yres; + + if (inf->fixed_modes) { + struct pxafb_mode_info *mode; + + mode = pxafb_getmode(inf, var); + if (!mode) + return -EINVAL; + pxafb_setmode(var, mode); + } else { + if (var->xres > inf->modes->xres) + return -EINVAL; + if (var->yres > inf->modes->yres) + return -EINVAL; + if (var->bits_per_pixel > inf->modes->bpp) + return -EINVAL; + } + var->xres_virtual = max(var->xres_virtual, var->xres); var->yres_virtual = @@ -395,7 +450,7 @@ static int pxafb_blank(int blank, struct fb_info *info) return 0; } -static int pxafb_mmap(struct fb_info *info, struct file *file, +static int pxafb_mmap(struct fb_info *info, struct vm_area_struct *vma) { struct pxafb_info *fbi = (struct pxafb_info *)info; @@ -418,7 +473,6 @@ static struct fb_ops pxafb_ops = { .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, .fb_blank = pxafb_blank, - .fb_cursor = soft_cursor, .fb_mmap = pxafb_mmap, }; @@ -695,7 +749,7 @@ 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); + pxafb_lcd_power(on, &fbi->fb.var); } static void pxafb_setup_gpio(struct pxafb_info *fbi) @@ -782,7 +836,7 @@ static void pxafb_disable_controller(struct pxafb_info *fbi) LCCR0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */ LCCR0 |= LCCR0_DIS; /* Disable LCD Controller */ - schedule_timeout(20 * HZ / 1000); + schedule_timeout(200 * HZ / 1000); remove_wait_queue(&fbi->ctrlr_wait, &wait); /* disable LCD controller clock */ @@ -792,7 +846,7 @@ static void pxafb_disable_controller(struct pxafb_info *fbi) /* * pxafb_handle_irq: Handle 'LCD DONE' interrupts. */ -static irqreturn_t pxafb_handle_irq(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t pxafb_handle_irq(int irq, void *dev_id) { struct pxafb_info *fbi = dev_id; unsigned int lcsr = LCSR; @@ -871,9 +925,11 @@ static void set_ctrlr_state(struct pxafb_info *fbi, u_int state) * 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; @@ -908,9 +964,10 @@ static void set_ctrlr_state(struct pxafb_info *fbi, u_int state) * Our LCD controller task (which is called when we blank or unblank) * via keventd. */ -static void pxafb_task(void *dummy) +static void pxafb_task(struct work_struct *work) { - struct pxafb_info *fbi = dummy; + struct pxafb_info *fbi = + container_of(work, struct pxafb_info, task); u_int state = xchg(&fbi->task_state, -1); set_ctrlr_state(fbi, state); @@ -981,17 +1038,17 @@ pxafb_freq_policy(struct notifier_block *nb, unsigned long val, void *data) * 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 device *dev, pm_message_t state) +static int pxafb_suspend(struct platform_device *dev, pm_message_t state) { - struct pxafb_info *fbi = dev_get_drvdata(dev); + struct pxafb_info *fbi = platform_get_drvdata(dev); set_ctrlr_state(fbi, C_DISABLE_PM); return 0; } -static int pxafb_resume(struct device *dev) +static int pxafb_resume(struct platform_device *dev) { - struct pxafb_info *fbi = dev_get_drvdata(dev); + struct pxafb_info *fbi = platform_get_drvdata(dev); set_ctrlr_state(fbi, C_ENABLE_PM); return 0; @@ -1051,6 +1108,8 @@ 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); @@ -1084,34 +1143,24 @@ static struct pxafb_info * __init pxafb_init_fbinfo(struct device *dev) addr = addr + sizeof(struct pxafb_info); fbi->fb.pseudo_palette = addr; - fbi->max_xres = inf->xres; - fbi->fb.var.xres = inf->xres; - fbi->fb.var.xres_virtual = inf->xres; - fbi->max_yres = inf->yres; - fbi->fb.var.yres = inf->yres; - fbi->fb.var.yres_virtual = inf->yres; - fbi->max_bpp = inf->bpp; - fbi->fb.var.bits_per_pixel = inf->bpp; - fbi->fb.var.pixclock = inf->pixclock; - fbi->fb.var.hsync_len = inf->hsync_len; - fbi->fb.var.left_margin = inf->left_margin; - fbi->fb.var.right_margin = inf->right_margin; - fbi->fb.var.vsync_len = inf->vsync_len; - fbi->fb.var.upper_margin = inf->upper_margin; - fbi->fb.var.lower_margin = inf->lower_margin; - fbi->fb.var.sync = inf->sync; - fbi->fb.var.grayscale = inf->cmap_greyscale; + 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->state = C_STARTUP; fbi->task_state = (u_char)-1; - fbi->fb.fix.smem_len = fbi->max_xres * fbi->max_yres * - fbi->max_bpp / 8; + + 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, fbi); + INIT_WORK(&fbi->task, pxafb_task); init_MUTEX(&fbi->ctrlr_sem); return fbi; @@ -1167,7 +1216,7 @@ static int __init pxafb_parse_options(struct device *dev, char *options) done: if (res_specified) { dev_info(dev, "overriding resolution: %dx%d\n", xres, yres); - inf->xres = xres; inf->yres = yres; + inf->modes[0].xres = xres; inf->modes[0].yres = yres; } if (bpp_specified) switch (bpp) { @@ -1176,48 +1225,48 @@ static int __init pxafb_parse_options(struct device *dev, char *options) case 4: case 8: case 16: - inf->bpp = bpp; + 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->pixclock = simple_strtoul(this_opt+9, NULL, 0); - dev_info(dev, "override pixclock: %ld\n", inf->pixclock); + 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->left_margin = simple_strtoul(this_opt+5, NULL, 0); - dev_info(dev, "override left: %u\n", inf->left_margin); + 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->right_margin = simple_strtoul(this_opt+6, NULL, 0); - dev_info(dev, "override right: %u\n", inf->right_margin); + 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->upper_margin = simple_strtoul(this_opt+6, NULL, 0); - dev_info(dev, "override upper: %u\n", inf->upper_margin); + 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->lower_margin = simple_strtoul(this_opt+6, NULL, 0); - dev_info(dev, "override lower: %u\n", inf->lower_margin); + 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->hsync_len = simple_strtoul(this_opt+9, NULL, 0); - dev_info(dev, "override hsynclen: %u\n", inf->hsync_len); + 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->vsync_len = simple_strtoul(this_opt+9, NULL, 0); - dev_info(dev, "override vsynclen: %u\n", inf->vsync_len); + 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->sync &= ~FB_SYNC_HOR_HIGH_ACT; + inf->modes[0].sync &= ~FB_SYNC_HOR_HIGH_ACT; } else { dev_info(dev, "override hsync: Active High\n"); - inf->sync |= FB_SYNC_HOR_HIGH_ACT; + 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->sync &= ~FB_SYNC_VERT_HIGH_ACT; + inf->modes[0].sync &= ~FB_SYNC_VERT_HIGH_ACT; } else { dev_info(dev, "override vsync: Active High\n"); - inf->sync |= FB_SYNC_VERT_HIGH_ACT; + 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) { @@ -1269,22 +1318,22 @@ static int __init pxafb_parse_options(struct device *dev, char *options) } #endif -int __init pxafb_probe(struct device *dev) +int __init pxafb_probe(struct platform_device *dev) { struct pxafb_info *fbi; struct pxafb_mach_info *inf; int ret; - dev_dbg(dev, "pxafb_probe\n"); + dev_dbg(&dev->dev, "pxafb_probe\n"); - inf = dev->platform_data; + inf = dev->dev.platform_data; ret = -ENOMEM; fbi = NULL; if (!inf) goto failed; #ifdef CONFIG_FB_PXA_PARAMETERS - ret = pxafb_parse_options(dev, g_options); + ret = pxafb_parse_options(&dev->dev, g_options); if (ret < 0) goto failed; #endif @@ -1294,36 +1343,36 @@ int __init pxafb_probe(struct device *dev) * a warning is given. */ if (inf->lccr0 & LCCR0_INVALID_CONFIG_MASK) - dev_warn(dev, "machine LCCR0 setting contains illegal bits: %08x\n", + 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, "machine LCCR3 setting contains illegal bits: %08x\n", + 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, "Double Pixel Data (DPD) mode is only valid in passive 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, "Dual panel only valid in passive mode\n"); + dev_warn(&dev->dev, "Dual panel only valid in passive mode\n"); if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Pas && - (inf->upper_margin || inf->lower_margin)) - dev_warn(dev, "Upper and lower margins must be 0 in passive mode\n"); + (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, "got a %dx%dx%d LCD\n",inf->xres, inf->yres, inf->bpp); - if (inf->xres == 0 || inf->yres == 0 || inf->bpp == 0) { - dev_err(dev, "Invalid resolution or bit depth\n"); + 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); + fbi = pxafb_init_fbinfo(&dev->dev); if (!fbi) { - dev_err(dev, "Failed to initialize framebuffer device\n"); + dev_err(&dev->dev, "Failed to initialize framebuffer device\n"); ret = -ENOMEM; // only reason for pxafb_init_fbinfo to fail is kmalloc goto failed; } @@ -1331,14 +1380,14 @@ int __init pxafb_probe(struct device *dev) /* Initialize video memory */ ret = pxafb_map_video_memory(fbi); if (ret) { - dev_err(dev, "Failed to allocate video RAM: %d\n", 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, SA_INTERRUPT, "LCD", fbi); + ret = request_irq(IRQ_LCD, pxafb_handle_irq, IRQF_DISABLED, "LCD", fbi); if (ret) { - dev_err(dev, "request_irq failed: %d\n", ret); + dev_err(&dev->dev, "request_irq failed: %d\n", ret); ret = -EBUSY; goto failed; } @@ -1350,11 +1399,11 @@ int __init pxafb_probe(struct device *dev) pxafb_check_var(&fbi->fb.var, &fbi->fb); pxafb_set_par(&fbi->fb); - dev_set_drvdata(dev, fbi); + platform_set_drvdata(dev, fbi); ret = register_framebuffer(&fbi->fb); if (ret < 0) { - dev_err(dev, "Failed to register framebuffer device: %d\n", ret); + dev_err(&dev->dev, "Failed to register framebuffer device: %d\n", ret); goto failed; } @@ -1377,26 +1426,28 @@ int __init pxafb_probe(struct device *dev) return 0; failed: - dev_set_drvdata(dev, NULL); + platform_set_drvdata(dev, NULL); kfree(fbi); return ret; } -static struct device_driver pxafb_driver = { - .name = "pxa2xx-fb", - .bus = &platform_bus_type, +static struct platform_driver pxafb_driver = { .probe = pxafb_probe, #ifdef CONFIG_PM .suspend = pxafb_suspend, .resume = pxafb_resume, #endif + .driver = { + .name = "pxa2xx-fb", + }, }; #ifndef MODULE int __devinit pxafb_setup(char *options) { # ifdef CONFIG_FB_PXA_PARAMETERS - strlcpy(g_options, options, sizeof(g_options)); + if (options) + strlcpy(g_options, options, sizeof(g_options)); # endif return 0; } @@ -1416,7 +1467,7 @@ int __devinit pxafb_init(void) return -ENODEV; pxafb_setup(option); #endif - return driver_register(&pxafb_driver); + return platform_driver_register(&pxafb_driver); } module_init(pxafb_init);