aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/sh_mobile_lcdcfb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/sh_mobile_lcdcfb.c')
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c198
1 files changed, 173 insertions, 25 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index efff672fd7b8..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,15 @@ 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;
43 struct clk *dot_clk;
38 struct clk *clk; 44 struct clk *clk;
39#endif 45#endif
40 unsigned long lddckr; 46 unsigned long lddckr;
@@ -56,7 +62,7 @@ struct sh_mobile_lcdc_priv {
56 62
57/* per-channel registers */ 63/* per-channel registers */
58enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, 64enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
59 LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR }; 65 LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };
60 66
61static unsigned long lcdc_offs_mainlcd[] = { 67static unsigned long lcdc_offs_mainlcd[] = {
62 [LDDCKPAT1R] = 0x400, 68 [LDDCKPAT1R] = 0x400,
@@ -66,6 +72,7 @@ static unsigned long lcdc_offs_mainlcd[] = {
66 [LDMT3R] = 0x420, 72 [LDMT3R] = 0x420,
67 [LDDFR] = 0x424, 73 [LDDFR] = 0x424,
68 [LDSM1R] = 0x428, 74 [LDSM1R] = 0x428,
75 [LDSM2R] = 0x42c,
69 [LDSA1R] = 0x430, 76 [LDSA1R] = 0x430,
70 [LDMLSR] = 0x438, 77 [LDMLSR] = 0x438,
71 [LDHCNR] = 0x448, 78 [LDHCNR] = 0x448,
@@ -83,6 +90,7 @@ static unsigned long lcdc_offs_sublcd[] = {
83 [LDMT3R] = 0x608, 90 [LDMT3R] = 0x608,
84 [LDDFR] = 0x60c, 91 [LDDFR] = 0x60c,
85 [LDSM1R] = 0x610, 92 [LDSM1R] = 0x610,
93 [LDSM2R] = 0x614,
86 [LDSA1R] = 0x618, 94 [LDSA1R] = 0x618,
87 [LDMLSR] = 0x620, 95 [LDMLSR] = 0x620,
88 [LDHCNR] = 0x624, 96 [LDHCNR] = 0x624,
@@ -96,6 +104,8 @@ static unsigned long lcdc_offs_sublcd[] = {
96#define LCDC_RESET 0x00000100 104#define LCDC_RESET 0x00000100
97#define DISPLAY_BEU 0x00000008 105#define DISPLAY_BEU 0x00000008
98#define LCDC_ENABLE 0x00000001 106#define LCDC_ENABLE 0x00000001
107#define LDINTR_FE 0x00000400
108#define LDINTR_FS 0x00000004
99 109
100static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, 110static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
101 int reg_nr, unsigned long data) 111 int reg_nr, unsigned long data)
@@ -170,6 +180,65 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
170 lcdc_sys_read_data, 180 lcdc_sys_read_data,
171}; 181};
172 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
173static 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,
174 int start) 243 int start)
175{ 244{
@@ -207,6 +276,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
207 int k, m; 276 int k, m;
208 int ret = 0; 277 int ret = 0;
209 278
279 /* enable clocks before accessing the hardware */
280 for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
281 if (priv->ch[k].enabled)
282 sh_mobile_lcdc_clk_on(priv);
283
210 /* reset */ 284 /* reset */
211 lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET); 285 lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
212 lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0); 286 lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);
@@ -249,7 +323,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
249 lcdc_write(priv, _LDDCKSTPR, 0); 323 lcdc_write(priv, _LDDCKSTPR, 0);
250 lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0); 324 lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);
251 325
252 /* interrupts are disabled */ 326 /* interrupts are disabled to begin with */
253 lcdc_write(priv, _LDINTR, 0); 327 lcdc_write(priv, _LDINTR, 0);
254 328
255 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 329 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
@@ -310,9 +384,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
310 return ret; 384 return ret;
311 } 385 }
312 386
313 /* --- display_lcdc_data() --- */
314 lcdc_write(priv, _LDINTR, 0x00000f00);
315
316 /* word and long word swap */ 387 /* word and long word swap */
317 lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6); 388 lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);
318 389
@@ -334,8 +405,24 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
334 /* set line size */ 405 /* set line size */
335 lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length); 406 lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length);
336 407
337 /* continuous read mode */ 408 /* setup deferred io if SYS bus */
338 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 }
339 } 426 }
340 427
341 /* display output */ 428 /* display output */
@@ -359,6 +446,7 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
359{ 446{
360 struct sh_mobile_lcdc_chan *ch; 447 struct sh_mobile_lcdc_chan *ch;
361 struct sh_mobile_lcdc_board_cfg *board_cfg; 448 struct sh_mobile_lcdc_board_cfg *board_cfg;
449 unsigned long tmp;
362 int k; 450 int k;
363 451
364 /* tell the board code to disable the panel */ 452 /* tell the board code to disable the panel */
@@ -367,10 +455,22 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
367 board_cfg = &ch->cfg.board_cfg; 455 board_cfg = &ch->cfg.board_cfg;
368 if (board_cfg->display_off) 456 if (board_cfg->display_off)
369 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 }
370 } 465 }
371 466
372 /* stop the lcdc */ 467 /* stop the lcdc */
373 sh_mobile_lcdc_start_stop(priv, 0); 468 sh_mobile_lcdc_start_stop(priv, 0);
469
470 /* stop clocks */
471 for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
472 if (priv->ch[k].enabled)
473 sh_mobile_lcdc_clk_off(priv);
374} 474}
375 475
376static 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)
@@ -413,9 +513,13 @@ static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
413 return -EINVAL; 513 return -EINVAL;
414} 514}
415 515
416static int sh_mobile_lcdc_setup_clocks(struct device *dev, int clock_source, 516static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
517 int clock_source,
417 struct sh_mobile_lcdc_priv *priv) 518 struct sh_mobile_lcdc_priv *priv)
418{ 519{
520#ifdef CONFIG_HAVE_CLK
521 char clk_name[8];
522#endif
419 char *str; 523 char *str;
420 int icksel; 524 int icksel;
421 525
@@ -430,14 +534,21 @@ static int sh_mobile_lcdc_setup_clocks(struct device *dev, int clock_source,
430 priv->lddckr = icksel << 16; 534 priv->lddckr = icksel << 16;
431 535
432#ifdef CONFIG_HAVE_CLK 536#ifdef CONFIG_HAVE_CLK
537 atomic_set(&priv->clk_usecnt, -1);
538 snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id);
539 priv->clk = clk_get(&pdev->dev, clk_name);
540 if (IS_ERR(priv->clk)) {
541 dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
542 return PTR_ERR(priv->clk);
543 }
544
433 if (str) { 545 if (str) {
434 priv->clk = clk_get(dev, str); 546 priv->dot_clk = clk_get(&pdev->dev, str);
435 if (IS_ERR(priv->clk)) { 547 if (IS_ERR(priv->dot_clk)) {
436 dev_err(dev, "cannot get clock %s\n", str); 548 dev_err(&pdev->dev, "cannot get dot clock %s\n", str);
437 return PTR_ERR(priv->clk); 549 clk_put(priv->clk);
550 return PTR_ERR(priv->dot_clk);
438 } 551 }
439
440 clk_enable(priv->clk);
441 } 552 }
442#endif 553#endif
443 554
@@ -475,11 +586,34 @@ static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
475 .accel = FB_ACCEL_NONE, 586 .accel = FB_ACCEL_NONE,
476}; 587};
477 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
478static struct fb_ops sh_mobile_lcdc_ops = { 610static struct fb_ops sh_mobile_lcdc_ops = {
479 .fb_setcolreg = sh_mobile_lcdc_setcolreg, 611 .fb_setcolreg = sh_mobile_lcdc_setcolreg,
480 .fb_fillrect = cfb_fillrect, 612 .fb_read = fb_sys_read,
481 .fb_copyarea = cfb_copyarea, 613 .fb_write = fb_sys_write,
482 .fb_imageblit = cfb_imageblit, 614 .fb_fillrect = sh_mobile_lcdc_fillrect,
615 .fb_copyarea = sh_mobile_lcdc_copyarea,
616 .fb_imageblit = sh_mobile_lcdc_imageblit,
483}; 617};
484 618
485static 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)
@@ -540,8 +674,9 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
540 } 674 }
541 675
542 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 676 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
543 if (res == NULL) { 677 i = platform_get_irq(pdev, 0);
544 dev_err(&pdev->dev, "cannot find IO resource\n"); 678 if (!res || i < 0) {
679 dev_err(&pdev->dev, "cannot get platform resources\n");
545 error = -ENOENT; 680 error = -ENOENT;
546 goto err0; 681 goto err0;
547 } 682 }
@@ -553,6 +688,14 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
553 goto err0; 688 goto err0;
554 } 689 }
555 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;
556 platform_set_drvdata(pdev, priv); 699 platform_set_drvdata(pdev, priv);
557 pdata = pdev->dev.platform_data; 700 pdata = pdev->dev.platform_data;
558 701
@@ -587,8 +730,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
587 goto err1; 730 goto err1;
588 } 731 }
589 732
590 error = sh_mobile_lcdc_setup_clocks(&pdev->dev, 733 error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv);
591 pdata->clock_source, priv);
592 if (error) { 734 if (error) {
593 dev_err(&pdev->dev, "unable to setup clocks\n"); 735 dev_err(&pdev->dev, "unable to setup clocks\n");
594 goto err1; 736 goto err1;
@@ -637,6 +779,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
637 info->fix.smem_start = priv->ch[i].dma_handle; 779 info->fix.smem_start = priv->ch[i].dma_handle;
638 info->screen_base = buf; 780 info->screen_base = buf;
639 info->device = &pdev->dev; 781 info->device = &pdev->dev;
782 info->par = &priv->ch[i];
640 } 783 }
641 784
642 if (error) 785 if (error)
@@ -664,6 +807,10 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
664 (int) priv->ch[i].cfg.lcd_cfg.xres, 807 (int) priv->ch[i].cfg.lcd_cfg.xres,
665 (int) priv->ch[i].cfg.lcd_cfg.yres, 808 (int) priv->ch[i].cfg.lcd_cfg.yres,
666 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);
667 } 814 }
668 815
669 return 0; 816 return 0;
@@ -697,15 +844,16 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
697 } 844 }
698 845
699#ifdef CONFIG_HAVE_CLK 846#ifdef CONFIG_HAVE_CLK
700 if (priv->clk) { 847 if (priv->dot_clk)
701 clk_disable(priv->clk); 848 clk_put(priv->dot_clk);
702 clk_put(priv->clk); 849 clk_put(priv->clk);
703 }
704#endif 850#endif
705 851
706 if (priv->base) 852 if (priv->base)
707 iounmap(priv->base); 853 iounmap(priv->base);
708 854
855 if (priv->irq)
856 free_irq(priv->irq, priv);
709 kfree(priv); 857 kfree(priv);
710 return 0; 858 return 0;
711} 859}