diff options
Diffstat (limited to 'drivers/video/atmel_lcdfb.c')
-rw-r--r-- | drivers/video/atmel_lcdfb.c | 345 |
1 files changed, 68 insertions, 277 deletions
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index cd96162..088511a 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c @@ -20,55 +20,12 @@ #include <linux/gfp.h> #include <linux/module.h> #include <linux/platform_data/atmel.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/of_gpio.h> -#include <video/of_display_timing.h> -#include <video/videomode.h> #include <mach/cpu.h> #include <asm/gpio.h> #include <video/atmel_lcdc.h> -struct atmel_lcdfb_config { - bool have_alt_pixclock; - bool have_hozval; - bool have_intensity_bit; -}; - - /* LCD Controller info data structure, stored in device platform_data */ -struct atmel_lcdfb_info { - spinlock_t lock; - struct fb_info *info; - void __iomem *mmio; - int irq_base; - struct work_struct task; - - unsigned int smem_len; - struct platform_device *pdev; - struct clk *bus_clk; - struct clk *lcdc_clk; - - struct backlight_device *backlight; - u8 bl_power; - u8 saved_lcdcon; - - u32 pseudo_palette[16]; - bool have_intensity_bit; - - struct atmel_lcdfb_pdata pdata; - - struct atmel_lcdfb_config *config; -}; - -struct atmel_lcdfb_power_ctrl_gpio { - int gpio; - int active_low; - - struct list_head list; -}; - #define lcdc_readl(sinfo, reg) __raw_readl((sinfo)->mmio+(reg)) #define lcdc_writel(sinfo, reg, val) __raw_writel((val), (sinfo)->mmio+(reg)) @@ -77,6 +34,12 @@ struct atmel_lcdfb_power_ctrl_gpio { #define ATMEL_LCDC_DMA_BURST_LEN 8 /* words */ #define ATMEL_LCDC_FIFO_SIZE 512 /* words */ +struct atmel_lcdfb_config { + bool have_alt_pixclock; + bool have_hozval; + bool have_intensity_bit; +}; + static struct atmel_lcdfb_config at91sam9261_config = { .have_hozval = true, .have_intensity_bit = true, @@ -131,7 +94,6 @@ static const struct platform_device_id atmel_lcdfb_devtypes[] = { /* terminator */ } }; -MODULE_DEVICE_TABLE(platform, atmel_lcdfb_devtypes); static struct atmel_lcdfb_config * atmel_lcdfb_get_config(struct platform_device *pdev) @@ -286,27 +248,18 @@ static void exit_backlight(struct atmel_lcdfb_info *sinfo) static void init_contrast(struct atmel_lcdfb_info *sinfo) { - struct atmel_lcdfb_pdata *pdata = &sinfo->pdata; - /* contrast pwm can be 'inverted' */ - if (pdata->lcdcon_pol_negative) + if (sinfo->lcdcon_pol_negative) contrast_ctr &= ~(ATMEL_LCDC_POL_POSITIVE); /* have some default contrast/backlight settings */ lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, contrast_ctr); lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CVAL_DEFAULT); - if (pdata->lcdcon_is_backlight) + if (sinfo->lcdcon_is_backlight) init_backlight(sinfo); } -static inline void atmel_lcdfb_power_control(struct atmel_lcdfb_info *sinfo, int on) -{ - struct atmel_lcdfb_pdata *pdata = &sinfo->pdata; - - if (pdata->atmel_lcdfb_power_control) - pdata->atmel_lcdfb_power_control(pdata, on); -} static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = { .type = FB_TYPE_PACKED_PIXELS, @@ -346,11 +299,9 @@ static unsigned long compute_hozval(struct atmel_lcdfb_info *sinfo, static void atmel_lcdfb_stop_nowait(struct atmel_lcdfb_info *sinfo) { - struct atmel_lcdfb_pdata *pdata = &sinfo->pdata; - /* Turn off the LCD controller and the DMA controller */ lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, - pdata->guard_time << ATMEL_LCDC_GUARDT_OFFSET); + sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET); /* Wait for the LCDC core to become idle */ while (lcdc_readl(sinfo, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY) @@ -370,11 +321,9 @@ static void atmel_lcdfb_stop(struct atmel_lcdfb_info *sinfo) static void atmel_lcdfb_start(struct atmel_lcdfb_info *sinfo) { - struct atmel_lcdfb_pdata *pdata = &sinfo->pdata; - - lcdc_writel(sinfo, ATMEL_LCDC_DMACON, pdata->default_dmacon); + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, sinfo->default_dmacon); lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, - (pdata->guard_time << ATMEL_LCDC_GUARDT_OFFSET) + (sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR); } @@ -475,7 +424,6 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, { struct device *dev = info->device; struct atmel_lcdfb_info *sinfo = info->par; - struct atmel_lcdfb_pdata *pdata = &sinfo->pdata; unsigned long clk_value_khz; clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; @@ -562,7 +510,7 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, else var->green.length = 6; - if (pdata->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) { + if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) { /* RGB:5X5 mode */ var->red.offset = var->green.length + 5; var->blue.offset = 0; @@ -579,7 +527,7 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, var->transp.length = 8; /* fall through */ case 24: - if (pdata->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) { + if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) { /* RGB:888 mode */ var->red.offset = 16; var->blue.offset = 0; @@ -628,7 +576,6 @@ static void atmel_lcdfb_reset(struct atmel_lcdfb_info *sinfo) static int atmel_lcdfb_set_par(struct fb_info *info) { struct atmel_lcdfb_info *sinfo = info->par; - struct atmel_lcdfb_pdata *pdata = &sinfo->pdata; unsigned long hozval_linesz; unsigned long value; unsigned long clk_value_khz; @@ -690,7 +637,7 @@ static int atmel_lcdfb_set_par(struct fb_info *info) /* Initialize control register 2 */ - value = pdata->default_lcdcon2; + value = sinfo->default_lcdcon2; if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT)) value |= ATMEL_LCDC_INVLINE_INVERTED; @@ -794,7 +741,6 @@ static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int transp, struct fb_info *info) { struct atmel_lcdfb_info *sinfo = info->par; - struct atmel_lcdfb_pdata *pdata = &sinfo->pdata; unsigned int val; u32 *pal; int ret = 1; @@ -831,7 +777,8 @@ static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red, */ } else { /* new style BGR:565 / RGB:565 */ - if (pdata->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) { + if (sinfo->lcd_wiring_mode == + ATMEL_LCDC_WIRING_RGB) { val = ((blue >> 11) & 0x001f); val |= ((red >> 0) & 0xf800); } else { @@ -965,187 +912,16 @@ static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo) clk_disable_unprepare(sinfo->lcdc_clk); } -#ifdef CONFIG_OF -static const struct of_device_id atmel_lcdfb_dt_ids[] = { - { .compatible = "atmel,at91sam9261-lcdc" , .data = &at91sam9261_config, }, - { .compatible = "atmel,at91sam9263-lcdc" , .data = &at91sam9263_config, }, - { .compatible = "atmel,at91sam9g10-lcdc" , .data = &at91sam9g10_config, }, - { .compatible = "atmel,at91sam9g45-lcdc" , .data = &at91sam9g45_config, }, - { .compatible = "atmel,at91sam9g45es-lcdc" , .data = &at91sam9g45es_config, }, - { .compatible = "atmel,at91sam9rl-lcdc" , .data = &at91sam9rl_config, }, - { .compatible = "atmel,at32ap-lcdc" , .data = &at32ap_config, }, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(of, atmel_lcdfb_dt_ids); - -static const char *atmel_lcdfb_wiring_modes[] = { - [ATMEL_LCDC_WIRING_BGR] = "BRG", - [ATMEL_LCDC_WIRING_RGB] = "RGB", -}; - -const int atmel_lcdfb_get_of_wiring_modes(struct device_node *np) -{ - const char *mode; - int err, i; - - err = of_property_read_string(np, "atmel,lcd-wiring-mode", &mode); - if (err < 0) - return ATMEL_LCDC_WIRING_BGR; - - for (i = 0; i < ARRAY_SIZE(atmel_lcdfb_wiring_modes); i++) - if (!strcasecmp(mode, atmel_lcdfb_wiring_modes[i])) - return i; - - return -ENODEV; -} - -static void atmel_lcdfb_power_control_gpio(struct atmel_lcdfb_pdata *pdata, int on) -{ - struct atmel_lcdfb_power_ctrl_gpio *og; - - list_for_each_entry(og, &pdata->pwr_gpios, list) - gpio_set_value(og->gpio, on); -} - -static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo) -{ - struct fb_info *info = sinfo->info; - struct atmel_lcdfb_pdata *pdata = &sinfo->pdata; - struct fb_var_screeninfo *var = &info->var; - struct device *dev = &sinfo->pdev->dev; - struct device_node *np =dev->of_node; - struct device_node *display_np; - struct device_node *timings_np; - struct display_timings *timings; - enum of_gpio_flags flags; - struct atmel_lcdfb_power_ctrl_gpio *og; - bool is_gpio_power = false; - int ret = -ENOENT; - int i, gpio; - - sinfo->config = (struct atmel_lcdfb_config*) - of_match_device(atmel_lcdfb_dt_ids, dev)->data; - - display_np = of_parse_phandle(np, "display", 0); - if (!display_np) { - dev_err(dev, "failed to find display phandle\n"); - return -ENOENT; - } - - ret = of_property_read_u32(display_np, "bits-per-pixel", &var->bits_per_pixel); - if (ret < 0) { - dev_err(dev, "failed to get property bits-per-pixel\n"); - goto put_display_node; - } - - ret = of_property_read_u32(display_np, "atmel,guard-time", &pdata->guard_time); - if (ret < 0) { - dev_err(dev, "failed to get property atmel,guard-time\n"); - goto put_display_node; - } - - ret = of_property_read_u32(display_np, "atmel,lcdcon2", &pdata->default_lcdcon2); - if (ret < 0) { - dev_err(dev, "failed to get property atmel,lcdcon2\n"); - goto put_display_node; - } - - ret = of_property_read_u32(display_np, "atmel,dmacon", &pdata->default_dmacon); - if (ret < 0) { - dev_err(dev, "failed to get property bits-per-pixel\n"); - goto put_display_node; - } - - ret = -ENOMEM; - for (i = 0; i < of_gpio_named_count(display_np, "atmel,power-control-gpio"); i++) { - gpio = of_get_named_gpio_flags(display_np, "atmel,power-control-gpio", - i, &flags); - if (gpio < 0) - continue; - - og = devm_kzalloc(dev, sizeof(*og), GFP_KERNEL); - if (!og) - goto put_display_node; - - og->gpio = gpio; - og->active_low = flags & OF_GPIO_ACTIVE_LOW; - is_gpio_power = true; - ret = devm_gpio_request(dev, gpio, "lcd-power-control-gpio"); - if (ret) { - dev_err(dev, "request gpio %d failed\n", gpio); - goto put_display_node; - } - - ret = gpio_direction_output(gpio, og->active_low); - if (ret) { - dev_err(dev, "set direction output gpio %d failed\n", gpio); - goto put_display_node; - } - } - - if (is_gpio_power) - pdata->atmel_lcdfb_power_control = atmel_lcdfb_power_control_gpio; - - ret = atmel_lcdfb_get_of_wiring_modes(display_np); - if (ret < 0) { - dev_err(dev, "invalid atmel,lcd-wiring-mode\n"); - goto put_display_node; - } - pdata->lcd_wiring_mode = ret; - - pdata->lcdcon_is_backlight = of_property_read_bool(display_np, "atmel,lcdcon-backlight"); - - timings = of_get_display_timings(display_np); - if (!timings) { - dev_err(dev, "failed to get display timings\n"); - goto put_display_node; - } - - timings_np = of_find_node_by_name(display_np, "display-timings"); - if (!timings_np) { - dev_err(dev, "failed to find display-timings node\n"); - goto put_display_node; - } - - for (i = 0; i < of_get_child_count(timings_np); i++) { - struct videomode vm; - struct fb_videomode fb_vm; - - ret = videomode_from_timings(timings, &vm, i); - if (ret < 0) - goto put_timings_node; - ret = fb_videomode_from_videomode(&vm, &fb_vm); - if (ret < 0) - goto put_timings_node; - - fb_add_videomode(&fb_vm, &info->modelist); - } - - return 0; - -put_timings_node: - of_node_put(timings_np); -put_display_node: - of_node_put(display_np); - return ret; -} -#else -static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo) -{ - return 0; -} -#endif static int __init atmel_lcdfb_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct fb_info *info; struct atmel_lcdfb_info *sinfo; - struct atmel_lcdfb_pdata *pdata = NULL; + struct atmel_lcdfb_info *pdata_sinfo; + struct fb_videomode fbmode; struct resource *regs = NULL; struct resource *map = NULL; - struct fb_modelist *modelist; int ret; dev_dbg(dev, "%s BEGIN\n", __func__); @@ -1158,35 +934,26 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) } sinfo = info->par; - sinfo->pdev = pdev; - sinfo->info = info; - - INIT_LIST_HEAD(&info->modelist); - if (pdev->dev.of_node) { - ret = atmel_lcdfb_of_init(sinfo); - if (ret) - goto free_info; - } else if (dev_get_platdata(dev)) { - struct fb_monspecs *monspecs; - int i; - - pdata = dev_get_platdata(dev); - monspecs = pdata->default_monspecs; - sinfo->pdata = *pdata; - - for (i = 0; i < monspecs->modedb_len; i++) - fb_add_videomode(&monspecs->modedb[i], &info->modelist); - - sinfo->config = atmel_lcdfb_get_config(pdev); - - info->var.bits_per_pixel = pdata->default_bpp ? pdata->default_bpp : 16; - memcpy(&info->monspecs, pdata->default_monspecs, sizeof(info->monspecs)); + if (dev->platform_data) { + pdata_sinfo = (struct atmel_lcdfb_info *)dev->platform_data; + sinfo->default_bpp = pdata_sinfo->default_bpp; + sinfo->default_dmacon = pdata_sinfo->default_dmacon; + sinfo->default_lcdcon2 = pdata_sinfo->default_lcdcon2; + sinfo->default_monspecs = pdata_sinfo->default_monspecs; + sinfo->atmel_lcdfb_power_control = pdata_sinfo->atmel_lcdfb_power_control; + sinfo->guard_time = pdata_sinfo->guard_time; + sinfo->smem_len = pdata_sinfo->smem_len; + sinfo->lcdcon_is_backlight = pdata_sinfo->lcdcon_is_backlight; + sinfo->lcdcon_pol_negative = pdata_sinfo->lcdcon_pol_negative; + sinfo->lcd_wiring_mode = pdata_sinfo->lcd_wiring_mode; } else { dev_err(dev, "cannot get default configuration\n"); goto free_info; } - + sinfo->info = info; + sinfo->pdev = pdev; + sinfo->config = atmel_lcdfb_get_config(pdev); if (!sinfo->config) goto free_info; @@ -1195,6 +962,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) info->pseudo_palette = sinfo->pseudo_palette; info->fbops = &atmel_lcdfb_ops; + memcpy(&info->monspecs, sinfo->default_monspecs, sizeof(info->monspecs)); info->fix = atmel_lcdfb_fix; /* Enable LCDC Clocks */ @@ -1210,11 +978,14 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) } atmel_lcdfb_start_clock(sinfo); - modelist = list_first_entry(&info->modelist, - struct fb_modelist, list); - fb_videomode_to_var(&info->var, &modelist->mode); + ret = fb_find_mode(&info->var, info, NULL, info->monspecs.modedb, + info->monspecs.modedb_len, info->monspecs.modedb, + sinfo->default_bpp); + if (!ret) { + dev_err(dev, "no suitable video mode found\n"); + goto stop_clk; + } - atmel_lcdfb_check_var(&info->var, info); regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) { @@ -1298,6 +1069,18 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) goto unregister_irqs; } + /* + * This makes sure that our colour bitfield + * descriptors are correctly initialised. + */ + atmel_lcdfb_check_var(&info->var, info); + + ret = fb_set_var(info, &info->var); + if (ret) { + dev_warn(dev, "unable to set display parameters\n"); + goto free_cmap; + } + dev_set_drvdata(dev, info); /* @@ -1309,8 +1092,13 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) goto reset_drvdata; } + /* add selected videomode to modelist */ + fb_var_to_videomode(&fbmode, &info->var); + fb_add_videomode(&fbmode, &info->modelist); + /* Power up the LCDC screen */ - atmel_lcdfb_power_control(sinfo, 1); + if (sinfo->atmel_lcdfb_power_control) + sinfo->atmel_lcdfb_power_control(1); dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %d\n", info->node, info->fix.mmio_start, sinfo->mmio, sinfo->irq_base); @@ -1319,6 +1107,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) reset_drvdata: dev_set_drvdata(dev, NULL); +free_cmap: fb_dealloc_cmap(&info->cmap); unregister_irqs: cancel_work_sync(&sinfo->task); @@ -1354,16 +1143,15 @@ static int __exit atmel_lcdfb_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; struct fb_info *info = dev_get_drvdata(dev); struct atmel_lcdfb_info *sinfo; - struct atmel_lcdfb_pdata *pdata; if (!info || !info->par) return 0; sinfo = info->par; - pdata = &sinfo->pdata; cancel_work_sync(&sinfo->task); exit_backlight(sinfo); - atmel_lcdfb_power_control(sinfo, 0); + if (sinfo->atmel_lcdfb_power_control) + sinfo->atmel_lcdfb_power_control(0); unregister_framebuffer(info); atmel_lcdfb_stop_clock(sinfo); clk_put(sinfo->lcdc_clk); @@ -1379,6 +1167,7 @@ static int __exit atmel_lcdfb_remove(struct platform_device *pdev) atmel_lcdfb_free_video_memory(sinfo); } + dev_set_drvdata(dev, NULL); framebuffer_release(info); return 0; @@ -1399,7 +1188,9 @@ static int atmel_lcdfb_suspend(struct platform_device *pdev, pm_message_t mesg) sinfo->saved_lcdcon = lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_CTR); lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, 0); - atmel_lcdfb_power_control(sinfo, 0); + if (sinfo->atmel_lcdfb_power_control) + sinfo->atmel_lcdfb_power_control(0); + atmel_lcdfb_stop(sinfo); atmel_lcdfb_stop_clock(sinfo); @@ -1413,7 +1204,8 @@ static int atmel_lcdfb_resume(struct platform_device *pdev) atmel_lcdfb_start_clock(sinfo); atmel_lcdfb_start(sinfo); - atmel_lcdfb_power_control(sinfo, 1); + if (sinfo->atmel_lcdfb_power_control) + sinfo->atmel_lcdfb_power_control(1); lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, sinfo->saved_lcdcon); /* Enable FIFO & DMA errors */ @@ -1436,7 +1228,6 @@ static struct platform_driver atmel_lcdfb_driver = { .driver = { .name = "atmel_lcdfb", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(atmel_lcdfb_dt_ids), }, }; |