aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux-omap-pm/dss2/0063-VRFB-add-suspend-resume-functionality.patch
blob: d2b9346a93d43212648a6e5880496a4b709aaa06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
From c26aa6e5aab9d145b343a2a71243508378e59621 Mon Sep 17 00:00:00 2001
From: Imre Deak <imre.deak@nokia.com>
Date: Tue, 5 May 2009 11:16:13 +0200
Subject: [PATCH 063/146] VRFB: add suspend/resume functionality

At the moment the VRFB context is restored at each core power domain
OFF->ON transition. This is not optimal since the VRFB might be unused
temporarily for example when the screen is blanked. Add a suspend /
resume function to mark these unused periods during which we'll avoid
thea the context restore.

Use atomic bitops for ctx_map for consistency.

Signed-off-by: Imre Deak <imre.deak@nokia.com>
---
 arch/arm/plat-omap/include/mach/vrfb.h   |    2 +
 arch/arm/plat-omap/vrfb.c                |   75 ++++++++++++++++++++++++-----
 drivers/video/omap2/omapfb/omapfb-main.c |   28 +++++++++++
 3 files changed, 92 insertions(+), 13 deletions(-)

diff --git a/arch/arm/plat-omap/include/mach/vrfb.h b/arch/arm/plat-omap/include/mach/vrfb.h
index ee6c062..9647d82 100644
--- a/arch/arm/plat-omap/include/mach/vrfb.h
+++ b/arch/arm/plat-omap/include/mach/vrfb.h
@@ -39,6 +39,8 @@ struct vrfb
 
 extern int omap_vrfb_request_ctx(struct vrfb *vrfb);
 extern void omap_vrfb_release_ctx(struct vrfb *vrfb);
+extern void omap_vrfb_suspend_ctx(struct vrfb *vrfb);
+extern void omap_vrfb_resume_ctx(struct vrfb *vrfb);
 extern void omap_vrfb_adjust_size(u16 *width, u16 *height,
 		u8 bytespp);
 extern void omap_vrfb_setup(struct vrfb *vrfb, unsigned long paddr,
diff --git a/arch/arm/plat-omap/vrfb.c b/arch/arm/plat-omap/vrfb.c
index 289fc8a..29f04e2 100644
--- a/arch/arm/plat-omap/vrfb.c
+++ b/arch/arm/plat-omap/vrfb.c
@@ -1,7 +1,9 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/ioport.h>
+
 #include <asm/io.h>
+#include <asm/bitops.h>
 
 #include <mach/io.h>
 #include <mach/vrfb.h>
@@ -37,7 +39,9 @@
 
 #define VRFB_NUM_CTXS 12
 /* bitmap of reserved contexts */
-static unsigned ctx_map;
+static unsigned long ctx_map;
+/* bitmap of contexts for which we have to keep the HW context valid */
+static unsigned long ctx_map_active;
 
 /*
  * Access to this happens from client drivers or the PM core after wake-up.
@@ -51,18 +55,23 @@ struct {
 	u32 size;
 } vrfb_hw_context[VRFB_NUM_CTXS];
 
+static void inline restore_hw_context(int ctx)
+{
+	omap_writel(vrfb_hw_context[ctx].control, SMS_ROT_CONTROL(ctx));
+	omap_writel(vrfb_hw_context[ctx].size, SMS_ROT_SIZE(ctx));
+	omap_writel(vrfb_hw_context[ctx].physical_ba, SMS_ROT_PHYSICAL_BA(ctx));
+}
+
 void omap_vrfb_restore_context(void)
 {
 	int i;
+	unsigned long map = ctx_map_active;
 
-	for (i = 0; i < VRFB_NUM_CTXS; i++) {
-		/* Restore only the active contexts */
-		if (!(ctx_map & (1 << i)))
-			continue;
-		omap_writel(vrfb_hw_context[i].control, SMS_ROT_CONTROL(i));
-		omap_writel(vrfb_hw_context[i].size, SMS_ROT_SIZE(i));
-		omap_writel(vrfb_hw_context[i].physical_ba,
-			    SMS_ROT_PHYSICAL_BA(i));
+	for (i = ffs(map); i; i = ffs(map)) {
+		/* i=1..32 */
+		i--;
+		map &= ~(1 << i);
+		restore_hw_context(i);
 	}
 }
 
@@ -156,13 +165,20 @@ EXPORT_SYMBOL(omap_vrfb_setup);
 void omap_vrfb_release_ctx(struct vrfb *vrfb)
 {
 	int rot;
+	int ctx = vrfb->context;
 
-	if (vrfb->context == 0xff)
+	if (ctx == 0xff)
 		return;
 
-	DBG("release ctx %d\n", vrfb->context);
+	DBG("release ctx %d\n", ctx);
 
-	ctx_map &= ~(1 << vrfb->context);
+	if (!(ctx_map & (1 << ctx))) {
+		BUG();
+		return;
+	}
+	WARN_ON(!(ctx_map_active & (1 << ctx)));
+	clear_bit(ctx, &ctx_map_active);
+	clear_bit(ctx, &ctx_map);
 
 	for (rot = 0; rot < 4; ++rot) {
 		if(vrfb->paddr[rot]) {
@@ -194,7 +210,9 @@ int omap_vrfb_request_ctx(struct vrfb *vrfb)
 
 	DBG("found free ctx %d\n", ctx);
 
-	ctx_map |= 1 << ctx;
+	set_bit(ctx, &ctx_map);
+	WARN_ON(ctx_map_active & (1 << ctx));
+	set_bit(ctx, &ctx_map_active);
 
 	memset(vrfb, 0, sizeof(*vrfb));
 
@@ -219,3 +237,34 @@ int omap_vrfb_request_ctx(struct vrfb *vrfb)
 }
 EXPORT_SYMBOL(omap_vrfb_request_ctx);
 
+void omap_vrfb_suspend_ctx(struct vrfb *vrfb)
+{
+	DBG("suspend ctx %d\n", vrfb->context);
+	if (vrfb->context >= VRFB_NUM_CTXS ||
+	    (!(1 << vrfb->context) & ctx_map_active)) {
+		BUG();
+		return;
+	}
+	clear_bit(vrfb->context, &ctx_map_active);
+}
+EXPORT_SYMBOL(omap_vrfb_suspend_ctx);
+
+void omap_vrfb_resume_ctx(struct vrfb *vrfb)
+{
+	DBG("resume ctx %d\n", vrfb->context);
+	if (vrfb->context >= VRFB_NUM_CTXS ||
+	    ((1 << vrfb->context) & ctx_map_active)) {
+		BUG();
+		return;
+	}
+	/*
+	 * omap_vrfb_restore_context is normally called by the core domain
+	 * save / restore logic, but since this VRFB context was suspended
+	 * those calls didn't actually restore the context and now we might
+	 * have an invalid context. Do an explicit restore here.
+	 */
+	restore_hw_context(vrfb->context);
+	set_bit(vrfb->context, &ctx_map_active);
+}
+EXPORT_SYMBOL(omap_vrfb_resume_ctx);
+
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index 76e7c6c..4bb74b7 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -1036,6 +1036,30 @@ static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
 	return 0;
 }
 
+static void omapfb_vrfb_suspend_all(struct omapfb2_device *fbdev)
+{
+	int i;
+
+	for (i = 0; i < fbdev->num_fbs; i++) {
+		struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
+
+		if (ofbi->region.vrfb.vaddr[0])
+			omap_vrfb_suspend_ctx(&ofbi->region.vrfb);
+	}
+}
+
+static void omapfb_vrfb_resume_all(struct omapfb2_device *fbdev)
+{
+	int i;
+
+	for (i = 0; i < fbdev->num_fbs; i++) {
+		struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);
+
+		if (ofbi->region.vrfb.vaddr[0])
+			omap_vrfb_resume_ctx(&ofbi->region.vrfb);
+	}
+}
+
 static int omapfb_blank(int blank, struct fb_info *fbi)
 {
 	struct omapfb_info *ofbi = FB2OFB(fbi);
@@ -1051,6 +1075,8 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
 		if (display->state != OMAP_DSS_DISPLAY_SUSPENDED)
 			goto exit;
 
+		omapfb_vrfb_resume_all(fbdev);
+
 		if (display->resume)
 			r = display->resume(display);
 
@@ -1073,6 +1099,8 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
 		if (display->suspend)
 			r = display->suspend(display);
 
+		omapfb_vrfb_suspend_all(fbdev);
+
 		break;
 
 	default:
-- 
1.6.2.4