aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMagnus Damm <damm@igel.co.jp>2009-08-14 06:49:08 -0400
committerPaul Mundt <lethal@linux-sh.org>2009-08-23 05:03:19 -0400
commit0246c4712c40294bd5e8335f0c15a38c8e52709f (patch)
tree375b68994d72931b9ecd6e7b2e1ccb9716c75d73
parentf1a3b994f9dfd12111dc034402aed256fac66dfe (diff)
video: Runtime PM for SuperH Mobile LCDC
This patch modifies the SuperH Mobile LCDC framebuffer driver to support Runtime PM. The driver is using the functions - pm_runtime_get_sync() - pm_runtime_put_sync() to inform the bus code if the hardware is idle or not. If the hardware is idle then the bus code may call the runtime dev_pm_ops callbacks to save and restore state. pm_runtime_resume() is used to allow the driver to access the hardware from probe(). Signed-off-by: Magnus Damm <damm@igel.co.jp> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c156
1 files changed, 110 insertions, 46 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index fc3f9662cea..1cb5213c1a0 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -14,6 +14,7 @@
14#include <linux/mm.h> 14#include <linux/mm.h>
15#include <linux/fb.h> 15#include <linux/fb.h>
16#include <linux/clk.h> 16#include <linux/clk.h>
17#include <linux/pm_runtime.h>
17#include <linux/platform_device.h> 18#include <linux/platform_device.h>
18#include <linux/dma-mapping.h> 19#include <linux/dma-mapping.h>
19#include <linux/interrupt.h> 20#include <linux/interrupt.h>
@@ -23,33 +24,6 @@
23 24
24#define PALETTE_NR 16 25#define PALETTE_NR 16
25 26
26struct sh_mobile_lcdc_priv;
27struct sh_mobile_lcdc_chan {
28 struct sh_mobile_lcdc_priv *lcdc;
29 unsigned long *reg_offs;
30 unsigned long ldmt1r_value;
31 unsigned long enabled; /* ME and SE in LDCNT2R */
32 struct sh_mobile_lcdc_chan_cfg cfg;
33 u32 pseudo_palette[PALETTE_NR];
34 struct fb_info *info;
35 dma_addr_t dma_handle;
36 struct fb_deferred_io defio;
37 struct scatterlist *sglist;
38 unsigned long frame_end;
39 wait_queue_head_t frame_end_wait;
40};
41
42struct sh_mobile_lcdc_priv {
43 void __iomem *base;
44 int irq;
45 atomic_t clk_usecnt;
46 struct clk *dot_clk;
47 struct clk *clk;
48 unsigned long lddckr;
49 struct sh_mobile_lcdc_chan ch[2];
50 int started;
51};
52
53/* shared registers */ 27/* shared registers */
54#define _LDDCKR 0x410 28#define _LDDCKR 0x410
55#define _LDDCKSTPR 0x414 29#define _LDDCKSTPR 0x414
@@ -63,11 +37,23 @@ struct sh_mobile_lcdc_priv {
63#define _LDDWAR 0x900 37#define _LDDWAR 0x900
64#define _LDDRAR 0x904 38#define _LDDRAR 0x904
65 39
40/* shared registers and their order for context save/restore */
41static int lcdc_shared_regs[] = {
42 _LDDCKR,
43 _LDDCKSTPR,
44 _LDINTR,
45 _LDDDSR,
46 _LDCNT1R,
47 _LDCNT2R,
48};
49#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs)
50
66/* per-channel registers */ 51/* per-channel registers */
67enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, 52enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
68 LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR }; 53 LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR,
54 NR_CH_REGS };
69 55
70static unsigned long lcdc_offs_mainlcd[] = { 56static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
71 [LDDCKPAT1R] = 0x400, 57 [LDDCKPAT1R] = 0x400,
72 [LDDCKPAT2R] = 0x404, 58 [LDDCKPAT2R] = 0x404,
73 [LDMT1R] = 0x418, 59 [LDMT1R] = 0x418,
@@ -85,7 +71,7 @@ static unsigned long lcdc_offs_mainlcd[] = {
85 [LDPMR] = 0x460, 71 [LDPMR] = 0x460,
86}; 72};
87 73
88static unsigned long lcdc_offs_sublcd[] = { 74static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
89 [LDDCKPAT1R] = 0x408, 75 [LDDCKPAT1R] = 0x408,
90 [LDDCKPAT2R] = 0x40c, 76 [LDDCKPAT2R] = 0x40c,
91 [LDMT1R] = 0x600, 77 [LDMT1R] = 0x600,
@@ -110,6 +96,35 @@ static unsigned long lcdc_offs_sublcd[] = {
110#define LDINTR_FE 0x00000400 96#define LDINTR_FE 0x00000400
111#define LDINTR_FS 0x00000004 97#define LDINTR_FS 0x00000004
112 98
99struct sh_mobile_lcdc_priv;
100struct sh_mobile_lcdc_chan {
101 struct sh_mobile_lcdc_priv *lcdc;
102 unsigned long *reg_offs;
103 unsigned long ldmt1r_value;
104 unsigned long enabled; /* ME and SE in LDCNT2R */
105 struct sh_mobile_lcdc_chan_cfg cfg;
106 u32 pseudo_palette[PALETTE_NR];
107 unsigned long saved_ch_regs[NR_CH_REGS];
108 struct fb_info *info;
109 dma_addr_t dma_handle;
110 struct fb_deferred_io defio;
111 struct scatterlist *sglist;
112 unsigned long frame_end;
113 wait_queue_head_t frame_end_wait;
114};
115
116struct sh_mobile_lcdc_priv {
117 void __iomem *base;
118 int irq;
119 atomic_t hw_usecnt;
120 struct device *dev;
121 struct clk *dot_clk;
122 unsigned long lddckr;
123 struct sh_mobile_lcdc_chan ch[2];
124 unsigned long saved_shared_regs[NR_SHARED_REGS];
125 int started;
126};
127
113static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, 128static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
114 int reg_nr, unsigned long data) 129 int reg_nr, unsigned long data)
115{ 130{
@@ -188,8 +203,8 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
188 203
189static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) 204static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
190{ 205{
191 if (atomic_inc_and_test(&priv->clk_usecnt)) { 206 if (atomic_inc_and_test(&priv->hw_usecnt)) {
192 clk_enable(priv->clk); 207 pm_runtime_get_sync(priv->dev);
193 if (priv->dot_clk) 208 if (priv->dot_clk)
194 clk_enable(priv->dot_clk); 209 clk_enable(priv->dot_clk);
195 } 210 }
@@ -197,10 +212,10 @@ static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
197 212
198static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) 213static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
199{ 214{
200 if (atomic_sub_return(1, &priv->clk_usecnt) == -1) { 215 if (atomic_sub_return(1, &priv->hw_usecnt) == -1) {
201 if (priv->dot_clk) 216 if (priv->dot_clk)
202 clk_disable(priv->dot_clk); 217 clk_disable(priv->dot_clk);
203 clk_disable(priv->clk); 218 pm_runtime_put(priv->dev);
204 } 219 }
205} 220}
206 221
@@ -574,7 +589,6 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
574 int clock_source, 589 int clock_source,
575 struct sh_mobile_lcdc_priv *priv) 590 struct sh_mobile_lcdc_priv *priv)
576{ 591{
577 char clk_name[8];
578 char *str; 592 char *str;
579 int icksel; 593 int icksel;
580 594
@@ -588,23 +602,21 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
588 602
589 priv->lddckr = icksel << 16; 603 priv->lddckr = icksel << 16;
590 604
591 atomic_set(&priv->clk_usecnt, -1);
592 snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id);
593 priv->clk = clk_get(&pdev->dev, clk_name);
594 if (IS_ERR(priv->clk)) {
595 dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
596 return PTR_ERR(priv->clk);
597 }
598
599 if (str) { 605 if (str) {
600 priv->dot_clk = clk_get(&pdev->dev, str); 606 priv->dot_clk = clk_get(&pdev->dev, str);
601 if (IS_ERR(priv->dot_clk)) { 607 if (IS_ERR(priv->dot_clk)) {
602 dev_err(&pdev->dev, "cannot get dot clock %s\n", str); 608 dev_err(&pdev->dev, "cannot get dot clock %s\n", str);
603 clk_put(priv->clk);
604 return PTR_ERR(priv->dot_clk); 609 return PTR_ERR(priv->dot_clk);
605 } 610 }
606 } 611 }
607 612 atomic_set(&priv->hw_usecnt, -1);
613
614 /* Runtime PM support involves two step for this driver:
615 * 1) Enable Runtime PM
616 * 2) Force Runtime PM Resume since hardware is accessed from probe()
617 */
618 pm_runtime_enable(priv->dev);
619 pm_runtime_resume(priv->dev);
608 return 0; 620 return 0;
609} 621}
610 622
@@ -722,9 +734,59 @@ static int sh_mobile_lcdc_resume(struct device *dev)
722 return sh_mobile_lcdc_start(platform_get_drvdata(pdev)); 734 return sh_mobile_lcdc_start(platform_get_drvdata(pdev));
723} 735}
724 736
737static int sh_mobile_lcdc_runtime_suspend(struct device *dev)
738{
739 struct platform_device *pdev = to_platform_device(dev);
740 struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev);
741 struct sh_mobile_lcdc_chan *ch;
742 int k, n;
743
744 /* save per-channel registers */
745 for (k = 0; k < ARRAY_SIZE(p->ch); k++) {
746 ch = &p->ch[k];
747 if (!ch->enabled)
748 continue;
749 for (n = 0; n < NR_CH_REGS; n++)
750 ch->saved_ch_regs[n] = lcdc_read_chan(ch, n);
751 }
752
753 /* save shared registers */
754 for (n = 0; n < NR_SHARED_REGS; n++)
755 p->saved_shared_regs[n] = lcdc_read(p, lcdc_shared_regs[n]);
756
757 /* turn off LCDC hardware */
758 lcdc_write(p, _LDCNT1R, 0);
759 return 0;
760}
761
762static int sh_mobile_lcdc_runtime_resume(struct device *dev)
763{
764 struct platform_device *pdev = to_platform_device(dev);
765 struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev);
766 struct sh_mobile_lcdc_chan *ch;
767 int k, n;
768
769 /* restore per-channel registers */
770 for (k = 0; k < ARRAY_SIZE(p->ch); k++) {
771 ch = &p->ch[k];
772 if (!ch->enabled)
773 continue;
774 for (n = 0; n < NR_CH_REGS; n++)
775 lcdc_write_chan(ch, n, ch->saved_ch_regs[n]);
776 }
777
778 /* restore shared registers */
779 for (n = 0; n < NR_SHARED_REGS; n++)
780 lcdc_write(p, lcdc_shared_regs[n], p->saved_shared_regs[n]);
781
782 return 0;
783}
784
725static struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { 785static struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
726 .suspend = sh_mobile_lcdc_suspend, 786 .suspend = sh_mobile_lcdc_suspend,
727 .resume = sh_mobile_lcdc_resume, 787 .resume = sh_mobile_lcdc_resume,
788 .runtime_suspend = sh_mobile_lcdc_runtime_suspend,
789 .runtime_resume = sh_mobile_lcdc_runtime_resume,
728}; 790};
729 791
730static int sh_mobile_lcdc_remove(struct platform_device *pdev); 792static int sh_mobile_lcdc_remove(struct platform_device *pdev);
@@ -769,6 +831,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
769 } 831 }
770 832
771 priv->irq = i; 833 priv->irq = i;
834 priv->dev = &pdev->dev;
772 platform_set_drvdata(pdev, priv); 835 platform_set_drvdata(pdev, priv);
773 pdata = pdev->dev.platform_data; 836 pdata = pdev->dev.platform_data;
774 837
@@ -940,7 +1003,8 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
940 1003
941 if (priv->dot_clk) 1004 if (priv->dot_clk)
942 clk_put(priv->dot_clk); 1005 clk_put(priv->dot_clk);
943 clk_put(priv->clk); 1006
1007 pm_runtime_disable(priv->dev);
944 1008
945 if (priv->base) 1009 if (priv->base)
946 iounmap(priv->base); 1010 iounmap(priv->base);