blob: b5a542fbd8187b97006c800e902ca13921b16701 [file] [log] [blame]
Tomi Valkeinen58f255482011-11-04 09:48:54 +02001/*
2 * Copyright (C) 2011 Texas Instruments
3 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#define DSS_SUBSYS_NAME "APPLY"
19
20#include <linux/kernel.h>
21#include <linux/slab.h>
22#include <linux/spinlock.h>
23#include <linux/jiffies.h>
24
25#include <video/omapdss.h>
26
27#include "dss.h"
28#include "dss_features.h"
29
30/*
31 * We have 4 levels of cache for the dispc settings. First two are in SW and
32 * the latter two in HW.
33 *
34 * +--------------------+
35 * |overlay/manager_info|
36 * +--------------------+
37 * v
38 * apply()
39 * v
40 * +--------------------+
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +020041 * | info |
Tomi Valkeinen58f255482011-11-04 09:48:54 +020042 * +--------------------+
43 * v
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +020044 * write_regs()
Tomi Valkeinen58f255482011-11-04 09:48:54 +020045 * v
46 * +--------------------+
47 * | shadow registers |
48 * +--------------------+
49 * v
50 * VFP or lcd/digit_enable
51 * v
52 * +--------------------+
53 * | registers |
54 * +--------------------+
55 */
56
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +020057struct ovl_priv_data {
Tomi Valkeinen58f255482011-11-04 09:48:54 +020058 /* If true, cache changed, but not written to shadow registers. Set
59 * in apply(), cleared when registers written. */
60 bool dirty;
61 /* If true, shadow registers contain changed values not yet in real
62 * registers. Set when writing to shadow registers, cleared at
63 * VSYNC/EVSYNC */
64 bool shadow_dirty;
65
66 bool enabled;
67
68 struct omap_overlay_info info;
69
70 enum omap_channel channel;
71
72 u32 fifo_low;
73 u32 fifo_high;
74};
75
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +020076struct mgr_priv_data {
Tomi Valkeinen58f255482011-11-04 09:48:54 +020077 /* If true, cache changed, but not written to shadow registers. Set
78 * in apply(), cleared when registers written. */
79 bool dirty;
80 /* If true, shadow registers contain changed values not yet in real
81 * registers. Set when writing to shadow registers, cleared at
82 * VSYNC/EVSYNC */
83 bool shadow_dirty;
84
85 struct omap_overlay_manager_info info;
86
87 bool manual_update;
88 bool do_manual_update;
89};
90
91static struct {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +020092 struct ovl_priv_data ovl_priv_data_array[MAX_DSS_OVERLAYS];
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +020093 struct mgr_priv_data mgr_priv_data_array[MAX_DSS_MANAGERS];
Tomi Valkeinen58f255482011-11-04 09:48:54 +020094
95 bool irq_enabled;
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +020096} dss_data;
Tomi Valkeinen58f255482011-11-04 09:48:54 +020097
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +020098/* protects dss_data */
Tomi Valkeinen063fd702011-11-15 12:04:10 +020099static spinlock_t data_lock;
100
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200101static struct ovl_priv_data *get_ovl_priv(struct omap_overlay *ovl)
102{
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200103 return &dss_data.ovl_priv_data_array[ovl->id];
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200104}
105
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200106static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr)
107{
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200108 return &dss_data.mgr_priv_data_array[mgr->id];
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200109}
110
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200111void dss_apply_init(void)
112{
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200113 spin_lock_init(&data_lock);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200114}
115
116static bool ovl_manual_update(struct omap_overlay *ovl)
117{
118 return ovl->manager->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
119}
120
121static bool mgr_manual_update(struct omap_overlay_manager *mgr)
122{
123 return mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
124}
125
126static int overlay_enabled(struct omap_overlay *ovl)
127{
128 return ovl->info.enabled && ovl->manager && ovl->manager->device;
129}
130
131int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
132{
133 unsigned long timeout = msecs_to_jiffies(500);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200134 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200135 u32 irq;
136 int r;
137 int i;
138 struct omap_dss_device *dssdev = mgr->device;
139
140 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
141 return 0;
142
143 if (mgr_manual_update(mgr))
144 return 0;
145
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200146 irq = dispc_mgr_get_vsync_irq(mgr->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200147
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200148 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200149 i = 0;
150 while (1) {
151 unsigned long flags;
152 bool shadow_dirty, dirty;
153
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200154 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200155 dirty = mp->dirty;
156 shadow_dirty = mp->shadow_dirty;
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200157 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200158
159 if (!dirty && !shadow_dirty) {
160 r = 0;
161 break;
162 }
163
164 /* 4 iterations is the worst case:
165 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
166 * 2 - first VSYNC, dirty = true
167 * 3 - dirty = false, shadow_dirty = true
168 * 4 - shadow_dirty = false */
169 if (i++ == 3) {
170 DSSERR("mgr(%d)->wait_for_go() not finishing\n",
171 mgr->id);
172 r = 0;
173 break;
174 }
175
176 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
177 if (r == -ERESTARTSYS)
178 break;
179
180 if (r) {
181 DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
182 break;
183 }
184 }
185
186 return r;
187}
188
189int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
190{
191 unsigned long timeout = msecs_to_jiffies(500);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200192 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200193 struct omap_dss_device *dssdev;
194 u32 irq;
195 int r;
196 int i;
197
198 if (!ovl->manager)
199 return 0;
200
201 dssdev = ovl->manager->device;
202
203 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
204 return 0;
205
206 if (ovl_manual_update(ovl))
207 return 0;
208
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200209 irq = dispc_mgr_get_vsync_irq(ovl->manager->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200210
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200211 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200212 i = 0;
213 while (1) {
214 unsigned long flags;
215 bool shadow_dirty, dirty;
216
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200217 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200218 dirty = op->dirty;
219 shadow_dirty = op->shadow_dirty;
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200220 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200221
222 if (!dirty && !shadow_dirty) {
223 r = 0;
224 break;
225 }
226
227 /* 4 iterations is the worst case:
228 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
229 * 2 - first VSYNC, dirty = true
230 * 3 - dirty = false, shadow_dirty = true
231 * 4 - shadow_dirty = false */
232 if (i++ == 3) {
233 DSSERR("ovl(%d)->wait_for_go() not finishing\n",
234 ovl->id);
235 r = 0;
236 break;
237 }
238
239 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
240 if (r == -ERESTARTSYS)
241 break;
242
243 if (r) {
244 DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id);
245 break;
246 }
247 }
248
249 return r;
250}
251
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200252static int dss_ovl_write_regs(struct omap_overlay *ovl)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200253{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200254 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200255 struct omap_overlay_info *oi;
256 bool ilace, replication;
257 int r;
258
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200259 DSSDBGF("%d", ovl->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200260
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200261 op = get_ovl_priv(ovl);
262 oi = &op->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200263
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200264 if (!op->enabled) {
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200265 dispc_ovl_enable(ovl->id, 0);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200266 return 0;
267 }
268
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200269 replication = dss_use_replication(ovl->manager->device, oi->color_mode);
270
271 ilace = ovl->manager->device->type == OMAP_DISPLAY_TYPE_VENC;
272
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200273 dispc_ovl_set_channel_out(ovl->id, op->channel);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200274
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200275 r = dispc_ovl_setup(ovl->id, oi, ilace, replication);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200276 if (r) {
277 /* this shouldn't happen */
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200278 DSSERR("dispc_ovl_setup failed for ovl %d\n", ovl->id);
279 dispc_ovl_enable(ovl->id, 0);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200280 return r;
281 }
282
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200283 dispc_ovl_set_fifo_threshold(ovl->id, op->fifo_low, op->fifo_high);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200284
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200285 dispc_ovl_enable(ovl->id, 1);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200286
287 return 0;
288}
289
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200290static void dss_mgr_write_regs(struct omap_overlay_manager *mgr)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200291{
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200292 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200293 struct omap_overlay_manager_info *mi;
294
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200295 DSSDBGF("%d", mgr->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200296
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200297 mp = get_mgr_priv(mgr);
298 mi = &mp->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200299
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200300 dispc_mgr_setup(mgr->id, mi);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200301}
302
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200303/* dss_write_regs() tries to write values from cache to shadow registers.
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200304 * It writes only to those managers/overlays that are not busy.
305 * returns 0 if everything could be written to shadow registers.
306 * returns 1 if not everything could be written to shadow registers. */
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200307static int dss_write_regs(void)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200308{
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200309 struct omap_overlay *ovl;
310 struct omap_overlay_manager *mgr;
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200311 struct ovl_priv_data *op;
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200312 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200313 const int num_ovls = dss_feat_get_num_ovls();
314 const int num_mgrs = dss_feat_get_num_mgrs();
315 int i;
316 int r;
317 bool mgr_busy[MAX_DSS_MANAGERS];
318 bool mgr_go[MAX_DSS_MANAGERS];
319 bool busy;
320
321 r = 0;
322 busy = false;
323
324 for (i = 0; i < num_mgrs; i++) {
325 mgr_busy[i] = dispc_mgr_go_busy(i);
326 mgr_go[i] = false;
327 }
328
329 /* Commit overlay settings */
330 for (i = 0; i < num_ovls; ++i) {
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200331 ovl = omap_dss_get_overlay(i);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200332 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200333
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200334 if (!op->dirty)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200335 continue;
336
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200337 mp = get_mgr_priv(ovl->manager);
338
339 if (mp->manual_update && !mp->do_manual_update)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200340 continue;
341
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200342 if (mgr_busy[op->channel]) {
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200343 busy = true;
344 continue;
345 }
346
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200347 r = dss_ovl_write_regs(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200348 if (r)
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200349 DSSERR("dss_ovl_write_regs %d failed\n", i);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200350
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200351 op->dirty = false;
352 op->shadow_dirty = true;
353 mgr_go[op->channel] = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200354 }
355
356 /* Commit manager settings */
357 for (i = 0; i < num_mgrs; ++i) {
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200358 mgr = omap_dss_get_overlay_manager(i);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200359 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200360
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200361 if (!mp->dirty)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200362 continue;
363
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200364 if (mp->manual_update && !mp->do_manual_update)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200365 continue;
366
367 if (mgr_busy[i]) {
368 busy = true;
369 continue;
370 }
371
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200372 dss_mgr_write_regs(mgr);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200373 mp->dirty = false;
374 mp->shadow_dirty = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200375 mgr_go[i] = true;
376 }
377
378 /* set GO */
379 for (i = 0; i < num_mgrs; ++i) {
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200380 mgr = omap_dss_get_overlay_manager(i);
381 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200382
383 if (!mgr_go[i])
384 continue;
385
386 /* We don't need GO with manual update display. LCD iface will
387 * always be turned off after frame, and new settings will be
388 * taken in to use at next update */
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200389 if (!mp->manual_update)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200390 dispc_mgr_go(i);
391 }
392
393 if (busy)
394 r = 1;
395 else
396 r = 0;
397
398 return r;
399}
400
401void dss_mgr_start_update(struct omap_overlay_manager *mgr)
402{
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200403 struct mgr_priv_data *mp = get_mgr_priv(mgr);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200404 struct ovl_priv_data *op;
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200405 struct omap_overlay *ovl;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200406
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200407 mp->do_manual_update = true;
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200408 dss_write_regs();
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200409 mp->do_manual_update = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200410
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200411 list_for_each_entry(ovl, &mgr->overlays, list) {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200412 op = get_ovl_priv(ovl);
413 op->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200414 }
415
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200416 mp->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200417
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200418 dispc_mgr_enable(mgr->id, true);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200419}
420
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200421static void dss_apply_irq_handler(void *data, u32 mask);
422
423static void dss_register_vsync_isr(void)
424{
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200425 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200426 u32 mask;
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200427 int r, i;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200428
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200429 mask = 0;
430 for (i = 0; i < num_mgrs; ++i)
431 mask |= dispc_mgr_get_vsync_irq(i);
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200432
433 r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
434 WARN_ON(r);
435
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200436 dss_data.irq_enabled = true;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200437}
438
439static void dss_unregister_vsync_isr(void)
440{
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200441 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200442 u32 mask;
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200443 int r, i;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200444
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200445 mask = 0;
446 for (i = 0; i < num_mgrs; ++i)
447 mask |= dispc_mgr_get_vsync_irq(i);
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200448
449 r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask);
450 WARN_ON(r);
451
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200452 dss_data.irq_enabled = false;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200453}
454
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200455static void dss_apply_irq_handler(void *data, u32 mask)
456{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200457 struct omap_overlay *ovl;
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200458 struct omap_overlay_manager *mgr;
459 struct mgr_priv_data *mp;
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200460 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200461 const int num_ovls = dss_feat_get_num_ovls();
462 const int num_mgrs = dss_feat_get_num_mgrs();
463 int i, r;
464 bool mgr_busy[MAX_DSS_MANAGERS];
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200465
466 for (i = 0; i < num_mgrs; i++)
467 mgr_busy[i] = dispc_mgr_go_busy(i);
468
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200469 spin_lock(&data_lock);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200470
471 for (i = 0; i < num_ovls; ++i) {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200472 ovl = omap_dss_get_overlay(i);
473 op = get_ovl_priv(ovl);
474 if (!mgr_busy[op->channel])
475 op->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200476 }
477
478 for (i = 0; i < num_mgrs; ++i) {
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200479 mgr = omap_dss_get_overlay_manager(i);
480 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200481 if (!mgr_busy[i])
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200482 mp->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200483 }
484
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200485 r = dss_write_regs();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200486 if (r == 1)
487 goto end;
488
489 /* re-read busy flags */
490 for (i = 0; i < num_mgrs; i++)
491 mgr_busy[i] = dispc_mgr_go_busy(i);
492
493 /* keep running as long as there are busy managers, so that
494 * we can collect overlay-applied information */
495 for (i = 0; i < num_mgrs; ++i) {
496 if (mgr_busy[i])
497 goto end;
498 }
499
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200500 dss_unregister_vsync_isr();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200501
502end:
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200503 spin_unlock(&data_lock);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200504}
505
Tomi Valkeinen5738b632011-11-15 13:37:33 +0200506static void omap_dss_mgr_apply_ovl(struct omap_overlay *ovl)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200507{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200508 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200509
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200510 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200511
512 if (ovl->manager_changed) {
513 ovl->manager_changed = false;
514 ovl->info_dirty = true;
515 }
516
517 if (!overlay_enabled(ovl)) {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200518 if (op->enabled) {
519 op->enabled = false;
520 op->dirty = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200521 }
Tomi Valkeinen5738b632011-11-15 13:37:33 +0200522 return;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200523 }
524
525 if (!ovl->info_dirty)
Tomi Valkeinen5738b632011-11-15 13:37:33 +0200526 return;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200527
528 ovl->info_dirty = false;
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200529 op->dirty = true;
530 op->info = ovl->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200531
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200532 op->channel = ovl->manager->id;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200533
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200534 op->enabled = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200535}
536
537static void omap_dss_mgr_apply_mgr(struct omap_overlay_manager *mgr)
538{
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200539 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200540
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200541 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200542
543 if (mgr->device_changed) {
544 mgr->device_changed = false;
545 mgr->info_dirty = true;
546 }
547
548 if (!mgr->info_dirty)
549 return;
550
551 if (!mgr->device)
552 return;
553
554 mgr->info_dirty = false;
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200555 mp->dirty = true;
556 mp->info = mgr->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200557
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200558 mp->manual_update = mgr_manual_update(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200559}
560
561static void omap_dss_mgr_apply_ovl_fifos(struct omap_overlay *ovl)
562{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200563 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200564 struct omap_dss_device *dssdev;
565 u32 size, burst_size;
566
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200567 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200568
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200569 if (!op->enabled)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200570 return;
571
572 dssdev = ovl->manager->device;
573
574 size = dispc_ovl_get_fifo_size(ovl->id);
575
576 burst_size = dispc_ovl_get_burst_size(ovl->id);
577
578 switch (dssdev->type) {
579 case OMAP_DISPLAY_TYPE_DPI:
580 case OMAP_DISPLAY_TYPE_DBI:
581 case OMAP_DISPLAY_TYPE_SDI:
582 case OMAP_DISPLAY_TYPE_VENC:
583 case OMAP_DISPLAY_TYPE_HDMI:
584 default_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200585 burst_size, &op->fifo_low,
586 &op->fifo_high);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200587 break;
588#ifdef CONFIG_OMAP2_DSS_DSI
589 case OMAP_DISPLAY_TYPE_DSI:
590 dsi_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200591 burst_size, &op->fifo_low,
592 &op->fifo_high);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200593 break;
594#endif
595 default:
596 BUG();
597 }
598}
599
600int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
601{
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200602 int r;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200603 unsigned long flags;
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200604 struct omap_overlay *ovl;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200605
606 DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
607
608 r = dispc_runtime_get();
609 if (r)
610 return r;
611
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200612 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200613
614 /* Configure overlays */
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200615 list_for_each_entry(ovl, &mgr->overlays, list)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200616 omap_dss_mgr_apply_ovl(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200617
618 /* Configure manager */
619 omap_dss_mgr_apply_mgr(mgr);
620
621 /* Configure overlay fifos */
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200622 list_for_each_entry(ovl, &mgr->overlays, list)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200623 omap_dss_mgr_apply_ovl_fifos(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200624
625 r = 0;
Tomi Valkeinen04f66432011-11-07 15:04:01 +0200626 if (mgr->enabled && !mgr_manual_update(mgr)) {
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200627 if (!dss_data.irq_enabled)
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200628 dss_register_vsync_isr();
Tomi Valkeinen18135ea2011-11-04 09:35:59 +0200629
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200630 dss_write_regs();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200631 }
632
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200633 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200634
635 dispc_runtime_put();
636
637 return r;
638}
639
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200640void dss_mgr_enable(struct omap_overlay_manager *mgr)
641{
642 dispc_mgr_enable(mgr->id, true);
Tomi Valkeinenbe729172011-11-04 10:30:47 +0200643 mgr->enabled = true;
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200644}
645
646void dss_mgr_disable(struct omap_overlay_manager *mgr)
647{
648 dispc_mgr_enable(mgr->id, false);
Tomi Valkeinenbe729172011-11-04 10:30:47 +0200649 mgr->enabled = false;
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200650}
651
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200652int dss_mgr_set_info(struct omap_overlay_manager *mgr,
653 struct omap_overlay_manager_info *info)
654{
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200655 mgr->info = *info;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200656 mgr->info_dirty = true;
657
658 return 0;
659}
660
661void dss_mgr_get_info(struct omap_overlay_manager *mgr,
662 struct omap_overlay_manager_info *info)
663{
664 *info = mgr->info;
665}
666
667int dss_mgr_set_device(struct omap_overlay_manager *mgr,
668 struct omap_dss_device *dssdev)
669{
670 int r;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200671
672 if (dssdev->manager) {
673 DSSERR("display '%s' already has a manager '%s'\n",
674 dssdev->name, dssdev->manager->name);
675 return -EINVAL;
676 }
677
678 if ((mgr->supported_displays & dssdev->type) == 0) {
679 DSSERR("display '%s' does not support manager '%s'\n",
680 dssdev->name, mgr->name);
681 return -EINVAL;
682 }
683
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200684 dssdev->manager = mgr;
685 mgr->device = dssdev;
686 mgr->device_changed = true;
687
688 return 0;
689}
690
691int dss_mgr_unset_device(struct omap_overlay_manager *mgr)
692{
693 if (!mgr->device) {
694 DSSERR("failed to unset display, display not set.\n");
695 return -EINVAL;
696 }
697
698 /*
699 * Don't allow currently enabled displays to have the overlay manager
700 * pulled out from underneath them
701 */
702 if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED)
703 return -EINVAL;
704
705 mgr->device->manager = NULL;
706 mgr->device = NULL;
707 mgr->device_changed = true;
708
709 return 0;
710}
711
712
713
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200714int dss_ovl_set_info(struct omap_overlay *ovl,
715 struct omap_overlay_info *info)
716{
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200717 ovl->info = *info;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200718 ovl->info_dirty = true;
719
720 return 0;
721}
722
723void dss_ovl_get_info(struct omap_overlay *ovl,
724 struct omap_overlay_info *info)
725{
726 *info = ovl->info;
727}
728
729int dss_ovl_set_manager(struct omap_overlay *ovl,
730 struct omap_overlay_manager *mgr)
731{
732 if (!mgr)
733 return -EINVAL;
734
735 if (ovl->manager) {
736 DSSERR("overlay '%s' already has a manager '%s'\n",
737 ovl->name, ovl->manager->name);
738 return -EINVAL;
739 }
740
741 if (ovl->info.enabled) {
742 DSSERR("overlay has to be disabled to change the manager\n");
743 return -EINVAL;
744 }
745
746 ovl->manager = mgr;
747 list_add_tail(&ovl->list, &mgr->overlays);
748 ovl->manager_changed = true;
749
750 /* XXX: When there is an overlay on a DSI manual update display, and
751 * the overlay is first disabled, then moved to tv, and enabled, we
752 * seem to get SYNC_LOST_DIGIT error.
753 *
754 * Waiting doesn't seem to help, but updating the manual update display
755 * after disabling the overlay seems to fix this. This hints that the
756 * overlay is perhaps somehow tied to the LCD output until the output
757 * is updated.
758 *
759 * Userspace workaround for this is to update the LCD after disabling
760 * the overlay, but before moving the overlay to TV.
761 */
762
763 return 0;
764}
765
766int dss_ovl_unset_manager(struct omap_overlay *ovl)
767{
768 if (!ovl->manager) {
769 DSSERR("failed to detach overlay: manager not set\n");
770 return -EINVAL;
771 }
772
773 if (ovl->info.enabled) {
774 DSSERR("overlay has to be disabled to unset the manager\n");
775 return -EINVAL;
776 }
777
778 ovl->manager = NULL;
779 list_del(&ovl->list);
780 ovl->manager_changed = true;
781
782 return 0;
783}
784