diff options
Diffstat (limited to 'drivers/video/da8xx-fb.c')
-rw-r--r-- | drivers/video/da8xx-fb.c | 176 |
1 files changed, 155 insertions, 21 deletions
diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index ea1fd3f47511..8d244ba0f601 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c | |||
@@ -28,6 +28,9 @@ | |||
28 | #include <linux/uaccess.h> | 28 | #include <linux/uaccess.h> |
29 | #include <linux/interrupt.h> | 29 | #include <linux/interrupt.h> |
30 | #include <linux/clk.h> | 30 | #include <linux/clk.h> |
31 | #include <linux/cpufreq.h> | ||
32 | #include <linux/console.h> | ||
33 | #include <linux/slab.h> | ||
31 | #include <video/da8xx-fb.h> | 34 | #include <video/da8xx-fb.h> |
32 | 35 | ||
33 | #define DRIVER_NAME "da8xx_lcdc" | 36 | #define DRIVER_NAME "da8xx_lcdc" |
@@ -113,6 +116,12 @@ struct da8xx_fb_par { | |||
113 | unsigned short pseudo_palette[16]; | 116 | unsigned short pseudo_palette[16]; |
114 | unsigned int databuf_sz; | 117 | unsigned int databuf_sz; |
115 | unsigned int palette_sz; | 118 | unsigned int palette_sz; |
119 | unsigned int pxl_clk; | ||
120 | int blank; | ||
121 | #ifdef CONFIG_CPU_FREQ | ||
122 | struct notifier_block freq_transition; | ||
123 | #endif | ||
124 | void (*panel_power_ctrl)(int); | ||
116 | }; | 125 | }; |
117 | 126 | ||
118 | /* Variable Screen Information */ | 127 | /* Variable Screen Information */ |
@@ -155,7 +164,7 @@ struct da8xx_panel { | |||
155 | int vfp; /* Vertical front porch */ | 164 | int vfp; /* Vertical front porch */ |
156 | int vbp; /* Vertical back porch */ | 165 | int vbp; /* Vertical back porch */ |
157 | int vsw; /* Vertical Sync Pulse Width */ | 166 | int vsw; /* Vertical Sync Pulse Width */ |
158 | int pxl_clk; /* Pixel clock */ | 167 | unsigned int pxl_clk; /* Pixel clock */ |
159 | unsigned char invert_pxl_clk; /* Invert Pixel clock */ | 168 | unsigned char invert_pxl_clk; /* Invert Pixel clock */ |
160 | }; | 169 | }; |
161 | 170 | ||
@@ -171,7 +180,7 @@ static struct da8xx_panel known_lcd_panels[] = { | |||
171 | .vfp = 2, | 180 | .vfp = 2, |
172 | .vbp = 2, | 181 | .vbp = 2, |
173 | .vsw = 0, | 182 | .vsw = 0, |
174 | .pxl_clk = 0x10, | 183 | .pxl_clk = 4608000, |
175 | .invert_pxl_clk = 1, | 184 | .invert_pxl_clk = 1, |
176 | }, | 185 | }, |
177 | /* Sharp LK043T1DG01 */ | 186 | /* Sharp LK043T1DG01 */ |
@@ -185,13 +194,23 @@ static struct da8xx_panel known_lcd_panels[] = { | |||
185 | .vfp = 2, | 194 | .vfp = 2, |
186 | .vbp = 2, | 195 | .vbp = 2, |
187 | .vsw = 10, | 196 | .vsw = 10, |
188 | .pxl_clk = 0x12, | 197 | .pxl_clk = 7833600, |
189 | .invert_pxl_clk = 0, | 198 | .invert_pxl_clk = 0, |
190 | }, | 199 | }, |
191 | }; | 200 | }; |
192 | 201 | ||
202 | /* Enable the Raster Engine of the LCD Controller */ | ||
203 | static inline void lcd_enable_raster(void) | ||
204 | { | ||
205 | u32 reg; | ||
206 | |||
207 | reg = lcdc_read(LCD_RASTER_CTRL_REG); | ||
208 | if (!(reg & LCD_RASTER_ENABLE)) | ||
209 | lcdc_write(reg | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); | ||
210 | } | ||
211 | |||
193 | /* Disable the Raster Engine of the LCD Controller */ | 212 | /* Disable the Raster Engine of the LCD Controller */ |
194 | static void lcd_disable_raster(struct da8xx_fb_par *par) | 213 | static inline void lcd_disable_raster(void) |
195 | { | 214 | { |
196 | u32 reg; | 215 | u32 reg; |
197 | 216 | ||
@@ -443,14 +462,25 @@ static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, | |||
443 | static void lcd_reset(struct da8xx_fb_par *par) | 462 | static void lcd_reset(struct da8xx_fb_par *par) |
444 | { | 463 | { |
445 | /* Disable the Raster if previously Enabled */ | 464 | /* Disable the Raster if previously Enabled */ |
446 | if (lcdc_read(LCD_RASTER_CTRL_REG) & LCD_RASTER_ENABLE) | 465 | lcd_disable_raster(); |
447 | lcd_disable_raster(par); | ||
448 | 466 | ||
449 | /* DMA has to be disabled */ | 467 | /* DMA has to be disabled */ |
450 | lcdc_write(0, LCD_DMA_CTRL_REG); | 468 | lcdc_write(0, LCD_DMA_CTRL_REG); |
451 | lcdc_write(0, LCD_RASTER_CTRL_REG); | 469 | lcdc_write(0, LCD_RASTER_CTRL_REG); |
452 | } | 470 | } |
453 | 471 | ||
472 | static void lcd_calc_clk_divider(struct da8xx_fb_par *par) | ||
473 | { | ||
474 | unsigned int lcd_clk, div; | ||
475 | |||
476 | lcd_clk = clk_get_rate(par->lcdc_clk); | ||
477 | div = lcd_clk / par->pxl_clk; | ||
478 | |||
479 | /* Configure the LCD clock divisor. */ | ||
480 | lcdc_write(LCD_CLK_DIVISOR(div) | | ||
481 | (LCD_RASTER_MODE & 0x1), LCD_CTRL_REG); | ||
482 | } | ||
483 | |||
454 | static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, | 484 | static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, |
455 | struct da8xx_panel *panel) | 485 | struct da8xx_panel *panel) |
456 | { | 486 | { |
@@ -459,9 +489,8 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, | |||
459 | 489 | ||
460 | lcd_reset(par); | 490 | lcd_reset(par); |
461 | 491 | ||
462 | /* Configure the LCD clock divisor. */ | 492 | /* Calculate the divider */ |
463 | lcdc_write(LCD_CLK_DIVISOR(panel->pxl_clk) | | 493 | lcd_calc_clk_divider(par); |
464 | (LCD_RASTER_MODE & 0x1), LCD_CTRL_REG); | ||
465 | 494 | ||
466 | if (panel->invert_pxl_clk) | 495 | if (panel->invert_pxl_clk) |
467 | lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) | | 496 | lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) | |
@@ -513,13 +542,11 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, | |||
513 | static irqreturn_t lcdc_irq_handler(int irq, void *arg) | 542 | static irqreturn_t lcdc_irq_handler(int irq, void *arg) |
514 | { | 543 | { |
515 | u32 stat = lcdc_read(LCD_STAT_REG); | 544 | u32 stat = lcdc_read(LCD_STAT_REG); |
516 | u32 reg; | ||
517 | 545 | ||
518 | if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) { | 546 | if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) { |
519 | reg = lcdc_read(LCD_RASTER_CTRL_REG); | 547 | lcd_disable_raster(); |
520 | lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); | ||
521 | lcdc_write(stat, LCD_STAT_REG); | 548 | lcdc_write(stat, LCD_STAT_REG); |
522 | lcdc_write(reg | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); | 549 | lcd_enable_raster(); |
523 | } else | 550 | } else |
524 | lcdc_write(stat, LCD_STAT_REG); | 551 | lcdc_write(stat, LCD_STAT_REG); |
525 | 552 | ||
@@ -574,6 +601,38 @@ static int fb_check_var(struct fb_var_screeninfo *var, | |||
574 | return err; | 601 | return err; |
575 | } | 602 | } |
576 | 603 | ||
604 | #ifdef CONFIG_CPU_FREQ | ||
605 | static int lcd_da8xx_cpufreq_transition(struct notifier_block *nb, | ||
606 | unsigned long val, void *data) | ||
607 | { | ||
608 | struct da8xx_fb_par *par; | ||
609 | |||
610 | par = container_of(nb, struct da8xx_fb_par, freq_transition); | ||
611 | if (val == CPUFREQ_PRECHANGE) { | ||
612 | lcd_disable_raster(); | ||
613 | } else if (val == CPUFREQ_POSTCHANGE) { | ||
614 | lcd_calc_clk_divider(par); | ||
615 | lcd_enable_raster(); | ||
616 | } | ||
617 | |||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | static inline int lcd_da8xx_cpufreq_register(struct da8xx_fb_par *par) | ||
622 | { | ||
623 | par->freq_transition.notifier_call = lcd_da8xx_cpufreq_transition; | ||
624 | |||
625 | return cpufreq_register_notifier(&par->freq_transition, | ||
626 | CPUFREQ_TRANSITION_NOTIFIER); | ||
627 | } | ||
628 | |||
629 | static inline void lcd_da8xx_cpufreq_deregister(struct da8xx_fb_par *par) | ||
630 | { | ||
631 | cpufreq_unregister_notifier(&par->freq_transition, | ||
632 | CPUFREQ_TRANSITION_NOTIFIER); | ||
633 | } | ||
634 | #endif | ||
635 | |||
577 | static int __devexit fb_remove(struct platform_device *dev) | 636 | static int __devexit fb_remove(struct platform_device *dev) |
578 | { | 637 | { |
579 | struct fb_info *info = dev_get_drvdata(&dev->dev); | 638 | struct fb_info *info = dev_get_drvdata(&dev->dev); |
@@ -581,8 +640,13 @@ static int __devexit fb_remove(struct platform_device *dev) | |||
581 | if (info) { | 640 | if (info) { |
582 | struct da8xx_fb_par *par = info->par; | 641 | struct da8xx_fb_par *par = info->par; |
583 | 642 | ||
584 | if (lcdc_read(LCD_RASTER_CTRL_REG) & LCD_RASTER_ENABLE) | 643 | #ifdef CONFIG_CPU_FREQ |
585 | lcd_disable_raster(par); | 644 | lcd_da8xx_cpufreq_deregister(par); |
645 | #endif | ||
646 | if (par->panel_power_ctrl) | ||
647 | par->panel_power_ctrl(0); | ||
648 | |||
649 | lcd_disable_raster(); | ||
586 | lcdc_write(0, LCD_RASTER_CTRL_REG); | 650 | lcdc_write(0, LCD_RASTER_CTRL_REG); |
587 | 651 | ||
588 | /* disable DMA */ | 652 | /* disable DMA */ |
@@ -639,6 +703,35 @@ static int fb_ioctl(struct fb_info *info, unsigned int cmd, | |||
639 | return 0; | 703 | return 0; |
640 | } | 704 | } |
641 | 705 | ||
706 | static int cfb_blank(int blank, struct fb_info *info) | ||
707 | { | ||
708 | struct da8xx_fb_par *par = info->par; | ||
709 | int ret = 0; | ||
710 | |||
711 | if (par->blank == blank) | ||
712 | return 0; | ||
713 | |||
714 | par->blank = blank; | ||
715 | switch (blank) { | ||
716 | case FB_BLANK_UNBLANK: | ||
717 | if (par->panel_power_ctrl) | ||
718 | par->panel_power_ctrl(1); | ||
719 | |||
720 | lcd_enable_raster(); | ||
721 | break; | ||
722 | case FB_BLANK_POWERDOWN: | ||
723 | if (par->panel_power_ctrl) | ||
724 | par->panel_power_ctrl(0); | ||
725 | |||
726 | lcd_disable_raster(); | ||
727 | break; | ||
728 | default: | ||
729 | ret = -EINVAL; | ||
730 | } | ||
731 | |||
732 | return ret; | ||
733 | } | ||
734 | |||
642 | static struct fb_ops da8xx_fb_ops = { | 735 | static struct fb_ops da8xx_fb_ops = { |
643 | .owner = THIS_MODULE, | 736 | .owner = THIS_MODULE, |
644 | .fb_check_var = fb_check_var, | 737 | .fb_check_var = fb_check_var, |
@@ -647,6 +740,7 @@ static struct fb_ops da8xx_fb_ops = { | |||
647 | .fb_fillrect = cfb_fillrect, | 740 | .fb_fillrect = cfb_fillrect, |
648 | .fb_copyarea = cfb_copyarea, | 741 | .fb_copyarea = cfb_copyarea, |
649 | .fb_imageblit = cfb_imageblit, | 742 | .fb_imageblit = cfb_imageblit, |
743 | .fb_blank = cfb_blank, | ||
650 | }; | 744 | }; |
651 | 745 | ||
652 | static int __init fb_probe(struct platform_device *device) | 746 | static int __init fb_probe(struct platform_device *device) |
@@ -721,6 +815,12 @@ static int __init fb_probe(struct platform_device *device) | |||
721 | } | 815 | } |
722 | 816 | ||
723 | par = da8xx_fb_info->par; | 817 | par = da8xx_fb_info->par; |
818 | par->lcdc_clk = fb_clk; | ||
819 | par->pxl_clk = lcdc_info->pxl_clk; | ||
820 | if (fb_pdata->panel_power_ctrl) { | ||
821 | par->panel_power_ctrl = fb_pdata->panel_power_ctrl; | ||
822 | par->panel_power_ctrl(1); | ||
823 | } | ||
724 | 824 | ||
725 | if (lcd_init(par, lcd_cfg, lcdc_info) < 0) { | 825 | if (lcd_init(par, lcd_cfg, lcdc_info) < 0) { |
726 | dev_err(&device->dev, "lcd_init failed\n"); | 826 | dev_err(&device->dev, "lcd_init failed\n"); |
@@ -754,8 +854,6 @@ static int __init fb_probe(struct platform_device *device) | |||
754 | da8xx_fb_fix.smem_len = par->databuf_sz - par->palette_sz; | 854 | da8xx_fb_fix.smem_len = par->databuf_sz - par->palette_sz; |
755 | da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8; | 855 | da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8; |
756 | 856 | ||
757 | par->lcdc_clk = fb_clk; | ||
758 | |||
759 | par->irq = platform_get_irq(device, 0); | 857 | par->irq = platform_get_irq(device, 0); |
760 | if (par->irq < 0) { | 858 | if (par->irq < 0) { |
761 | ret = -ENOENT; | 859 | ret = -ENOENT; |
@@ -814,12 +912,24 @@ static int __init fb_probe(struct platform_device *device) | |||
814 | goto err_dealloc_cmap; | 912 | goto err_dealloc_cmap; |
815 | } | 913 | } |
816 | 914 | ||
915 | #ifdef CONFIG_CPU_FREQ | ||
916 | ret = lcd_da8xx_cpufreq_register(par); | ||
917 | if (ret) { | ||
918 | dev_err(&device->dev, "failed to register cpufreq\n"); | ||
919 | goto err_cpu_freq; | ||
920 | } | ||
921 | #endif | ||
922 | |||
817 | /* enable raster engine */ | 923 | /* enable raster engine */ |
818 | lcdc_write(lcdc_read(LCD_RASTER_CTRL_REG) | | 924 | lcd_enable_raster(); |
819 | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); | ||
820 | 925 | ||
821 | return 0; | 926 | return 0; |
822 | 927 | ||
928 | #ifdef CONFIG_CPU_FREQ | ||
929 | err_cpu_freq: | ||
930 | unregister_framebuffer(da8xx_fb_info); | ||
931 | #endif | ||
932 | |||
823 | err_dealloc_cmap: | 933 | err_dealloc_cmap: |
824 | fb_dealloc_cmap(&da8xx_fb_info->cmap); | 934 | fb_dealloc_cmap(&da8xx_fb_info->cmap); |
825 | 935 | ||
@@ -852,11 +962,35 @@ err_request_mem: | |||
852 | #ifdef CONFIG_PM | 962 | #ifdef CONFIG_PM |
853 | static int fb_suspend(struct platform_device *dev, pm_message_t state) | 963 | static int fb_suspend(struct platform_device *dev, pm_message_t state) |
854 | { | 964 | { |
855 | return -EBUSY; | 965 | struct fb_info *info = platform_get_drvdata(dev); |
966 | struct da8xx_fb_par *par = info->par; | ||
967 | |||
968 | acquire_console_sem(); | ||
969 | if (par->panel_power_ctrl) | ||
970 | par->panel_power_ctrl(0); | ||
971 | |||
972 | fb_set_suspend(info, 1); | ||
973 | lcd_disable_raster(); | ||
974 | clk_disable(par->lcdc_clk); | ||
975 | release_console_sem(); | ||
976 | |||
977 | return 0; | ||
856 | } | 978 | } |
857 | static int fb_resume(struct platform_device *dev) | 979 | static int fb_resume(struct platform_device *dev) |
858 | { | 980 | { |
859 | return -EBUSY; | 981 | struct fb_info *info = platform_get_drvdata(dev); |
982 | struct da8xx_fb_par *par = info->par; | ||
983 | |||
984 | acquire_console_sem(); | ||
985 | if (par->panel_power_ctrl) | ||
986 | par->panel_power_ctrl(1); | ||
987 | |||
988 | clk_enable(par->lcdc_clk); | ||
989 | lcd_enable_raster(); | ||
990 | fb_set_suspend(info, 0); | ||
991 | release_console_sem(); | ||
992 | |||
993 | return 0; | ||
860 | } | 994 | } |
861 | #else | 995 | #else |
862 | #define fb_suspend NULL | 996 | #define fb_suspend NULL |