aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/video/Kconfig1
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c170
-rw-r--r--include/video/sh_mobile_lcdc.h1
3 files changed, 150 insertions, 22 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index dd483bfe3951..d0c821992a99 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1893,6 +1893,7 @@ config FB_SH_MOBILE_LCDC
1893 select FB_SYS_COPYAREA 1893 select FB_SYS_COPYAREA
1894 select FB_SYS_IMAGEBLIT 1894 select FB_SYS_IMAGEBLIT
1895 select FB_SYS_FOPS 1895 select FB_SYS_FOPS
1896 select FB_DEFERRED_IO
1896 ---help--- 1897 ---help---
1897 Frame buffer driver for the on-chip SH-Mobile LCD controller. 1898 Frame buffer driver for the on-chip SH-Mobile LCD controller.
1898 1899
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index e339d829183c..0e2b8fd24df1 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -16,7 +16,9 @@
16#include <linux/clk.h> 16#include <linux/clk.h>
17#include <linux/platform_device.h> 17#include <linux/platform_device.h>
18#include <linux/dma-mapping.h> 18#include <linux/dma-mapping.h>
19#include <linux/interrupt.h>
19#include <video/sh_mobile_lcdc.h> 20#include <video/sh_mobile_lcdc.h>
21#include <asm/atomic.h>
20 22
21#define PALETTE_NR 16 23#define PALETTE_NR 16
22 24
@@ -30,11 +32,14 @@ struct sh_mobile_lcdc_chan {
30 u32 pseudo_palette[PALETTE_NR]; 32 u32 pseudo_palette[PALETTE_NR];
31 struct fb_info info; 33 struct fb_info info;
32 dma_addr_t dma_handle; 34 dma_addr_t dma_handle;
35 struct fb_deferred_io defio;
33}; 36};
34 37
35struct sh_mobile_lcdc_priv { 38struct sh_mobile_lcdc_priv {
36 void __iomem *base; 39 void __iomem *base;
40 int irq;
37#ifdef CONFIG_HAVE_CLK 41#ifdef CONFIG_HAVE_CLK
42 atomic_t clk_usecnt;
38 struct clk *dot_clk; 43 struct clk *dot_clk;
39 struct clk *clk; 44 struct clk *clk;
40#endif 45#endif
@@ -57,7 +62,7 @@ struct sh_mobile_lcdc_priv {
57 62
58/* per-channel registers */ 63/* per-channel registers */
59enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, 64enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
60 LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR }; 65 LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };
61 66
62static unsigned long lcdc_offs_mainlcd[] = { 67static unsigned long lcdc_offs_mainlcd[] = {
63 [LDDCKPAT1R] = 0x400, 68 [LDDCKPAT1R] = 0x400,
@@ -67,6 +72,7 @@ static unsigned long lcdc_offs_mainlcd[] = {
67 [LDMT3R] = 0x420, 72 [LDMT3R] = 0x420,
68 [LDDFR] = 0x424, 73 [LDDFR] = 0x424,
69 [LDSM1R] = 0x428, 74 [LDSM1R] = 0x428,
75 [LDSM2R] = 0x42c,
70 [LDSA1R] = 0x430, 76 [LDSA1R] = 0x430,
71 [LDMLSR] = 0x438, 77 [LDMLSR] = 0x438,
72 [LDHCNR] = 0x448, 78 [LDHCNR] = 0x448,
@@ -84,6 +90,7 @@ static unsigned long lcdc_offs_sublcd[] = {
84 [LDMT3R] = 0x608, 90 [LDMT3R] = 0x608,
85 [LDDFR] = 0x60c, 91 [LDDFR] = 0x60c,
86 [LDSM1R] = 0x610, 92 [LDSM1R] = 0x610,
93 [LDSM2R] = 0x614,
87 [LDSA1R] = 0x618, 94 [LDSA1R] = 0x618,
88 [LDMLSR] = 0x620, 95 [LDMLSR] = 0x620,
89 [LDHCNR] = 0x624, 96 [LDHCNR] = 0x624,
@@ -97,6 +104,8 @@ static unsigned long lcdc_offs_sublcd[] = {
97#define LCDC_RESET 0x00000100 104#define LCDC_RESET 0x00000100
98#define DISPLAY_BEU 0x00000008 105#define DISPLAY_BEU 0x00000008
99#define LCDC_ENABLE 0x00000001 106#define LCDC_ENABLE 0x00000001
107#define LDINTR_FE 0x00000400
108#define LDINTR_FS 0x00000004
100 109
101static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, 110static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
102 int reg_nr, unsigned long data) 111 int reg_nr, unsigned long data)
@@ -171,6 +180,65 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
171 lcdc_sys_read_data, 180 lcdc_sys_read_data,
172}; 181};
173 182
183#ifdef CONFIG_HAVE_CLK
184static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
185{
186 if (atomic_inc_and_test(&priv->clk_usecnt)) {
187 clk_enable(priv->clk);
188 if (priv->dot_clk)
189 clk_enable(priv->dot_clk);
190 }
191}
192
193static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
194{
195 if (atomic_sub_return(1, &priv->clk_usecnt) == -1) {
196 if (priv->dot_clk)
197 clk_disable(priv->dot_clk);
198 clk_disable(priv->clk);
199 }
200}
201#else
202static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) {}
203static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) {}
204#endif
205
206static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
207 struct list_head *pagelist)
208{
209 struct sh_mobile_lcdc_chan *ch = info->par;
210
211 /* enable clocks before accessing hardware */
212 sh_mobile_lcdc_clk_on(ch->lcdc);
213
214 /* trigger panel update */
215 lcdc_write_chan(ch, LDSM2R, 1);
216}
217
218static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)
219{
220 struct fb_deferred_io *fbdefio = info->fbdefio;
221
222 if (fbdefio)
223 schedule_delayed_work(&info->deferred_work, fbdefio->delay);
224}
225
226static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
227{
228 struct sh_mobile_lcdc_priv *priv = data;
229 unsigned long tmp;
230
231 /* acknowledge interrupt */
232 tmp = lcdc_read(priv, _LDINTR);
233 tmp &= 0xffffff00; /* mask in high 24 bits */
234 tmp |= 0x000000ff ^ LDINTR_FS; /* status in low 8 */
235 lcdc_write(priv, _LDINTR, tmp);
236
237 /* disable clocks */
238 sh_mobile_lcdc_clk_off(priv);
239 return IRQ_HANDLED;
240}
241
174static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, 242static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
175 int start) 243 int start)
176{ 244{
@@ -208,11 +276,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
208 int k, m; 276 int k, m;
209 int ret = 0; 277 int ret = 0;
210 278
211#ifdef CONFIG_HAVE_CLK 279 /* enable clocks before accessing the hardware */
212 clk_enable(priv->clk); 280 for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
213 if (priv->dot_clk) 281 if (priv->ch[k].enabled)
214 clk_enable(priv->dot_clk); 282 sh_mobile_lcdc_clk_on(priv);
215#endif 283
216 /* reset */ 284 /* reset */
217 lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET); 285 lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
218 lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0); 286 lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);
@@ -255,7 +323,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
255 lcdc_write(priv, _LDDCKSTPR, 0); 323 lcdc_write(priv, _LDDCKSTPR, 0);
256 lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0); 324 lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);
257 325
258 /* interrupts are disabled */ 326 /* interrupts are disabled to begin with */
259 lcdc_write(priv, _LDINTR, 0); 327 lcdc_write(priv, _LDINTR, 0);
260 328
261 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 329 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
@@ -316,9 +384,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
316 return ret; 384 return ret;
317 } 385 }
318 386
319 /* --- display_lcdc_data() --- */
320 lcdc_write(priv, _LDINTR, 0x00000f00);
321
322 /* word and long word swap */ 387 /* word and long word swap */
323 lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6); 388 lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);
324 389
@@ -340,8 +405,24 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
340 /* set line size */ 405 /* set line size */
341 lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length); 406 lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length);
342 407
343 /* continuous read mode */ 408 /* setup deferred io if SYS bus */
344 lcdc_write_chan(ch, LDSM1R, 0); 409 tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
410 if (ch->ldmt1r_value & (1 << 12) && tmp) {
411 ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
412 ch->defio.delay = msecs_to_jiffies(tmp);
413 ch->info.fbdefio = &ch->defio;
414 fb_deferred_io_init(&ch->info);
415
416 /* one-shot mode */
417 lcdc_write_chan(ch, LDSM1R, 1);
418
419 /* enable "Frame End Interrupt Enable" bit */
420 lcdc_write(priv, _LDINTR, LDINTR_FE);
421
422 } else {
423 /* continuous read mode */
424 lcdc_write_chan(ch, LDSM1R, 0);
425 }
345 } 426 }
346 427
347 /* display output */ 428 /* display output */
@@ -365,6 +446,7 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
365{ 446{
366 struct sh_mobile_lcdc_chan *ch; 447 struct sh_mobile_lcdc_chan *ch;
367 struct sh_mobile_lcdc_board_cfg *board_cfg; 448 struct sh_mobile_lcdc_board_cfg *board_cfg;
449 unsigned long tmp;
368 int k; 450 int k;
369 451
370 /* tell the board code to disable the panel */ 452 /* tell the board code to disable the panel */
@@ -373,16 +455,22 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
373 board_cfg = &ch->cfg.board_cfg; 455 board_cfg = &ch->cfg.board_cfg;
374 if (board_cfg->display_off) 456 if (board_cfg->display_off)
375 board_cfg->display_off(board_cfg->board_data); 457 board_cfg->display_off(board_cfg->board_data);
458
459 /* cleanup deferred io if SYS bus */
460 tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
461 if (ch->ldmt1r_value & (1 << 12) && tmp) {
462 fb_deferred_io_cleanup(&ch->info);
463 ch->info.fbdefio = NULL;
464 }
376 } 465 }
377 466
378 /* stop the lcdc */ 467 /* stop the lcdc */
379 sh_mobile_lcdc_start_stop(priv, 0); 468 sh_mobile_lcdc_start_stop(priv, 0);
380 469
381#ifdef CONFIG_HAVE_CLK 470 /* stop clocks */
382 if (priv->dot_clk) 471 for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
383 clk_disable(priv->dot_clk); 472 if (priv->ch[k].enabled)
384 clk_disable(priv->clk); 473 sh_mobile_lcdc_clk_off(priv);
385#endif
386} 474}
387 475
388static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) 476static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
@@ -446,6 +534,7 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
446 priv->lddckr = icksel << 16; 534 priv->lddckr = icksel << 16;
447 535
448#ifdef CONFIG_HAVE_CLK 536#ifdef CONFIG_HAVE_CLK
537 atomic_set(&priv->clk_usecnt, -1);
449 snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id); 538 snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id);
450 priv->clk = clk_get(&pdev->dev, clk_name); 539 priv->clk = clk_get(&pdev->dev, clk_name);
451 if (IS_ERR(priv->clk)) { 540 if (IS_ERR(priv->clk)) {
@@ -497,13 +586,34 @@ static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
497 .accel = FB_ACCEL_NONE, 586 .accel = FB_ACCEL_NONE,
498}; 587};
499 588
589static void sh_mobile_lcdc_fillrect(struct fb_info *info,
590 const struct fb_fillrect *rect)
591{
592 sys_fillrect(info, rect);
593 sh_mobile_lcdc_deferred_io_touch(info);
594}
595
596static void sh_mobile_lcdc_copyarea(struct fb_info *info,
597 const struct fb_copyarea *area)
598{
599 sys_copyarea(info, area);
600 sh_mobile_lcdc_deferred_io_touch(info);
601}
602
603static void sh_mobile_lcdc_imageblit(struct fb_info *info,
604 const struct fb_image *image)
605{
606 sys_imageblit(info, image);
607 sh_mobile_lcdc_deferred_io_touch(info);
608}
609
500static struct fb_ops sh_mobile_lcdc_ops = { 610static struct fb_ops sh_mobile_lcdc_ops = {
501 .fb_setcolreg = sh_mobile_lcdc_setcolreg, 611 .fb_setcolreg = sh_mobile_lcdc_setcolreg,
502 .fb_read = fb_sys_read, 612 .fb_read = fb_sys_read,
503 .fb_write = fb_sys_write, 613 .fb_write = fb_sys_write,
504 .fb_fillrect = sys_fillrect, 614 .fb_fillrect = sh_mobile_lcdc_fillrect,
505 .fb_copyarea = sys_copyarea, 615 .fb_copyarea = sh_mobile_lcdc_copyarea,
506 .fb_imageblit = sys_imageblit, 616 .fb_imageblit = sh_mobile_lcdc_imageblit,
507}; 617};
508 618
509static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) 619static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
@@ -564,8 +674,9 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
564 } 674 }
565 675
566 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 676 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
567 if (res == NULL) { 677 i = platform_get_irq(pdev, 0);
568 dev_err(&pdev->dev, "cannot find IO resource\n"); 678 if (!res || i < 0) {
679 dev_err(&pdev->dev, "cannot get platform resources\n");
569 error = -ENOENT; 680 error = -ENOENT;
570 goto err0; 681 goto err0;
571 } 682 }
@@ -577,6 +688,14 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
577 goto err0; 688 goto err0;
578 } 689 }
579 690
691 error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED,
692 pdev->dev.bus_id, priv);
693 if (error) {
694 dev_err(&pdev->dev, "unable to request irq\n");
695 goto err1;
696 }
697
698 priv->irq = i;
580 platform_set_drvdata(pdev, priv); 699 platform_set_drvdata(pdev, priv);
581 pdata = pdev->dev.platform_data; 700 pdata = pdev->dev.platform_data;
582 701
@@ -660,6 +779,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
660 info->fix.smem_start = priv->ch[i].dma_handle; 779 info->fix.smem_start = priv->ch[i].dma_handle;
661 info->screen_base = buf; 780 info->screen_base = buf;
662 info->device = &pdev->dev; 781 info->device = &pdev->dev;
782 info->par = &priv->ch[i];
663 } 783 }
664 784
665 if (error) 785 if (error)
@@ -687,6 +807,10 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
687 (int) priv->ch[i].cfg.lcd_cfg.xres, 807 (int) priv->ch[i].cfg.lcd_cfg.xres,
688 (int) priv->ch[i].cfg.lcd_cfg.yres, 808 (int) priv->ch[i].cfg.lcd_cfg.yres,
689 priv->ch[i].cfg.bpp); 809 priv->ch[i].cfg.bpp);
810
811 /* deferred io mode: disable clock to save power */
812 if (info->fbdefio)
813 sh_mobile_lcdc_clk_off(priv);
690 } 814 }
691 815
692 return 0; 816 return 0;
@@ -728,6 +852,8 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
728 if (priv->base) 852 if (priv->base)
729 iounmap(priv->base); 853 iounmap(priv->base);
730 854
855 if (priv->irq)
856 free_irq(priv->irq, priv);
731 kfree(priv); 857 kfree(priv);
732 return 0; 858 return 0;
733} 859}
diff --git a/include/video/sh_mobile_lcdc.h b/include/video/sh_mobile_lcdc.h
index 1a4bc6ada606..25144ab22b95 100644
--- a/include/video/sh_mobile_lcdc.h
+++ b/include/video/sh_mobile_lcdc.h
@@ -37,6 +37,7 @@ enum { LCDC_CLK_BUS, LCDC_CLK_PERIPHERAL, LCDC_CLK_EXTERNAL };
37struct sh_mobile_lcdc_sys_bus_cfg { 37struct sh_mobile_lcdc_sys_bus_cfg {
38 unsigned long ldmt2r; 38 unsigned long ldmt2r;
39 unsigned long ldmt3r; 39 unsigned long ldmt3r;
40 unsigned long deferred_io_msec;
40}; 41};
41 42
42struct sh_mobile_lcdc_sys_bus_ops { 43struct sh_mobile_lcdc_sys_bus_ops {