blob: dfce82e11a367ef97daac3cb1be458856cf9a48c [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 * +--------------------+
41 * | dss_cache |
42 * +--------------------+
43 * v
44 * configure()
45 * v
46 * +--------------------+
47 * | shadow registers |
48 * +--------------------+
49 * v
50 * VFP or lcd/digit_enable
51 * v
52 * +--------------------+
53 * | registers |
54 * +--------------------+
55 */
56
57struct overlay_cache_data {
58 /* 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
76struct manager_cache_data {
77 /* 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 {
92 spinlock_t lock;
93 struct overlay_cache_data overlay_cache[MAX_DSS_OVERLAYS];
94 struct manager_cache_data manager_cache[MAX_DSS_MANAGERS];
95
96 bool irq_enabled;
97} dss_cache;
98
99void dss_apply_init(void)
100{
101 spin_lock_init(&dss_cache.lock);
102}
103
104static bool ovl_manual_update(struct omap_overlay *ovl)
105{
106 return ovl->manager->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
107}
108
109static bool mgr_manual_update(struct omap_overlay_manager *mgr)
110{
111 return mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
112}
113
114static int overlay_enabled(struct omap_overlay *ovl)
115{
116 return ovl->info.enabled && ovl->manager && ovl->manager->device;
117}
118
119int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
120{
121 unsigned long timeout = msecs_to_jiffies(500);
122 struct manager_cache_data *mc;
123 u32 irq;
124 int r;
125 int i;
126 struct omap_dss_device *dssdev = mgr->device;
127
128 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
129 return 0;
130
131 if (mgr_manual_update(mgr))
132 return 0;
133
134 if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
135 || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
136 irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
137 } else {
138 irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
139 DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2;
140 }
141
142 mc = &dss_cache.manager_cache[mgr->id];
143 i = 0;
144 while (1) {
145 unsigned long flags;
146 bool shadow_dirty, dirty;
147
148 spin_lock_irqsave(&dss_cache.lock, flags);
149 dirty = mc->dirty;
150 shadow_dirty = mc->shadow_dirty;
151 spin_unlock_irqrestore(&dss_cache.lock, flags);
152
153 if (!dirty && !shadow_dirty) {
154 r = 0;
155 break;
156 }
157
158 /* 4 iterations is the worst case:
159 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
160 * 2 - first VSYNC, dirty = true
161 * 3 - dirty = false, shadow_dirty = true
162 * 4 - shadow_dirty = false */
163 if (i++ == 3) {
164 DSSERR("mgr(%d)->wait_for_go() not finishing\n",
165 mgr->id);
166 r = 0;
167 break;
168 }
169
170 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
171 if (r == -ERESTARTSYS)
172 break;
173
174 if (r) {
175 DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
176 break;
177 }
178 }
179
180 return r;
181}
182
183int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
184{
185 unsigned long timeout = msecs_to_jiffies(500);
186 struct overlay_cache_data *oc;
187 struct omap_dss_device *dssdev;
188 u32 irq;
189 int r;
190 int i;
191
192 if (!ovl->manager)
193 return 0;
194
195 dssdev = ovl->manager->device;
196
197 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
198 return 0;
199
200 if (ovl_manual_update(ovl))
201 return 0;
202
203 if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
204 || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
205 irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
206 } else {
207 irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
208 DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2;
209 }
210
211 oc = &dss_cache.overlay_cache[ovl->id];
212 i = 0;
213 while (1) {
214 unsigned long flags;
215 bool shadow_dirty, dirty;
216
217 spin_lock_irqsave(&dss_cache.lock, flags);
218 dirty = oc->dirty;
219 shadow_dirty = oc->shadow_dirty;
220 spin_unlock_irqrestore(&dss_cache.lock, flags);
221
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
252static int configure_overlay(enum omap_plane plane)
253{
254 struct omap_overlay *ovl;
255 struct overlay_cache_data *c;
256 struct omap_overlay_info *oi;
257 bool ilace, replication;
258 int r;
259
260 DSSDBGF("%d", plane);
261
262 c = &dss_cache.overlay_cache[plane];
263 oi = &c->info;
264
265 if (!c->enabled) {
266 dispc_ovl_enable(plane, 0);
267 return 0;
268 }
269
270 ovl = omap_dss_get_overlay(plane);
271
272 replication = dss_use_replication(ovl->manager->device, oi->color_mode);
273
274 ilace = ovl->manager->device->type == OMAP_DISPLAY_TYPE_VENC;
275
276 dispc_ovl_set_channel_out(plane, c->channel);
277
278 r = dispc_ovl_setup(plane, oi, ilace, replication);
279 if (r) {
280 /* this shouldn't happen */
281 DSSERR("dispc_ovl_setup failed for ovl %d\n", plane);
282 dispc_ovl_enable(plane, 0);
283 return r;
284 }
285
286 dispc_ovl_set_fifo_threshold(plane, c->fifo_low, c->fifo_high);
287
288 dispc_ovl_enable(plane, 1);
289
290 return 0;
291}
292
293static void configure_manager(enum omap_channel channel)
294{
295 struct omap_overlay_manager_info *mi;
296
297 DSSDBGF("%d", channel);
298
299 /* picking info from the cache */
300 mi = &dss_cache.manager_cache[channel].info;
301
302 dispc_mgr_setup(channel, mi);
303}
304
305/* configure_dispc() tries to write values from cache to shadow registers.
306 * It writes only to those managers/overlays that are not busy.
307 * returns 0 if everything could be written to shadow registers.
308 * returns 1 if not everything could be written to shadow registers. */
309static int configure_dispc(void)
310{
311 struct overlay_cache_data *oc;
312 struct manager_cache_data *mc;
313 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) {
331 oc = &dss_cache.overlay_cache[i];
332 mc = &dss_cache.manager_cache[oc->channel];
333
334 if (!oc->dirty)
335 continue;
336
337 if (mc->manual_update && !mc->do_manual_update)
338 continue;
339
340 if (mgr_busy[oc->channel]) {
341 busy = true;
342 continue;
343 }
344
345 r = configure_overlay(i);
346 if (r)
347 DSSERR("configure_overlay %d failed\n", i);
348
349 oc->dirty = false;
350 oc->shadow_dirty = true;
351 mgr_go[oc->channel] = true;
352 }
353
354 /* Commit manager settings */
355 for (i = 0; i < num_mgrs; ++i) {
356 mc = &dss_cache.manager_cache[i];
357
358 if (!mc->dirty)
359 continue;
360
361 if (mc->manual_update && !mc->do_manual_update)
362 continue;
363
364 if (mgr_busy[i]) {
365 busy = true;
366 continue;
367 }
368
369 configure_manager(i);
370 mc->dirty = false;
371 mc->shadow_dirty = true;
372 mgr_go[i] = true;
373 }
374
375 /* set GO */
376 for (i = 0; i < num_mgrs; ++i) {
377 mc = &dss_cache.manager_cache[i];
378
379 if (!mgr_go[i])
380 continue;
381
382 /* We don't need GO with manual update display. LCD iface will
383 * always be turned off after frame, and new settings will be
384 * taken in to use at next update */
385 if (!mc->manual_update)
386 dispc_mgr_go(i);
387 }
388
389 if (busy)
390 r = 1;
391 else
392 r = 0;
393
394 return r;
395}
396
397void dss_mgr_start_update(struct omap_overlay_manager *mgr)
398{
399 struct manager_cache_data *mc;
400 struct overlay_cache_data *oc;
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200401 struct omap_overlay *ovl;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200402
403 mc = &dss_cache.manager_cache[mgr->id];
404
405 mc->do_manual_update = true;
406 configure_dispc();
407 mc->do_manual_update = false;
408
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200409 list_for_each_entry(ovl, &mgr->overlays, list) {
410 oc = &dss_cache.overlay_cache[ovl->id];
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200411 oc->shadow_dirty = false;
412 }
413
Tomi Valkeinen6e53ca92011-11-01 13:58:50 +0200414 mc = &dss_cache.manager_cache[mgr->id];
415 mc->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200416
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200417 dispc_mgr_enable(mgr->id, true);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200418}
419
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200420static void dss_apply_irq_handler(void *data, u32 mask);
421
422static void dss_register_vsync_isr(void)
423{
424 u32 mask;
425 int r;
426
427 mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
428 DISPC_IRQ_EVSYNC_EVEN;
429 if (dss_has_feature(FEAT_MGR_LCD2))
430 mask |= DISPC_IRQ_VSYNC2;
431
432 r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
433 WARN_ON(r);
434
435 dss_cache.irq_enabled = true;
436}
437
438static void dss_unregister_vsync_isr(void)
439{
440 u32 mask;
441 int r;
442
443 mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
444 DISPC_IRQ_EVSYNC_EVEN;
445 if (dss_has_feature(FEAT_MGR_LCD2))
446 mask |= DISPC_IRQ_VSYNC2;
447
448 r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask);
449 WARN_ON(r);
450
451 dss_cache.irq_enabled = false;
452}
453
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200454static void dss_apply_irq_handler(void *data, u32 mask)
455{
456 struct manager_cache_data *mc;
457 struct overlay_cache_data *oc;
458 const int num_ovls = dss_feat_get_num_ovls();
459 const int num_mgrs = dss_feat_get_num_mgrs();
460 int i, r;
461 bool mgr_busy[MAX_DSS_MANAGERS];
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200462
463 for (i = 0; i < num_mgrs; i++)
464 mgr_busy[i] = dispc_mgr_go_busy(i);
465
466 spin_lock(&dss_cache.lock);
467
468 for (i = 0; i < num_ovls; ++i) {
469 oc = &dss_cache.overlay_cache[i];
470 if (!mgr_busy[oc->channel])
471 oc->shadow_dirty = false;
472 }
473
474 for (i = 0; i < num_mgrs; ++i) {
475 mc = &dss_cache.manager_cache[i];
476 if (!mgr_busy[i])
477 mc->shadow_dirty = false;
478 }
479
480 r = configure_dispc();
481 if (r == 1)
482 goto end;
483
484 /* re-read busy flags */
485 for (i = 0; i < num_mgrs; i++)
486 mgr_busy[i] = dispc_mgr_go_busy(i);
487
488 /* keep running as long as there are busy managers, so that
489 * we can collect overlay-applied information */
490 for (i = 0; i < num_mgrs; ++i) {
491 if (mgr_busy[i])
492 goto end;
493 }
494
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200495 dss_unregister_vsync_isr();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200496
497end:
498 spin_unlock(&dss_cache.lock);
499}
500
501static int omap_dss_mgr_apply_ovl(struct omap_overlay *ovl)
502{
503 struct overlay_cache_data *oc;
504 struct omap_dss_device *dssdev;
505
506 oc = &dss_cache.overlay_cache[ovl->id];
507
508 if (ovl->manager_changed) {
509 ovl->manager_changed = false;
510 ovl->info_dirty = true;
511 }
512
513 if (!overlay_enabled(ovl)) {
514 if (oc->enabled) {
515 oc->enabled = false;
516 oc->dirty = true;
517 }
518 return 0;
519 }
520
521 if (!ovl->info_dirty)
522 return 0;
523
524 dssdev = ovl->manager->device;
525
526 if (dss_check_overlay(ovl, dssdev)) {
527 if (oc->enabled) {
528 oc->enabled = false;
529 oc->dirty = true;
530 }
531 return -EINVAL;
532 }
533
534 ovl->info_dirty = false;
535 oc->dirty = true;
536 oc->info = ovl->info;
537
538 oc->channel = ovl->manager->id;
539
540 oc->enabled = true;
541
542 return 0;
543}
544
545static void omap_dss_mgr_apply_mgr(struct omap_overlay_manager *mgr)
546{
547 struct manager_cache_data *mc;
548
549 mc = &dss_cache.manager_cache[mgr->id];
550
551 if (mgr->device_changed) {
552 mgr->device_changed = false;
553 mgr->info_dirty = true;
554 }
555
556 if (!mgr->info_dirty)
557 return;
558
559 if (!mgr->device)
560 return;
561
562 mgr->info_dirty = false;
563 mc->dirty = true;
564 mc->info = mgr->info;
565
566 mc->manual_update = mgr_manual_update(mgr);
567}
568
569static void omap_dss_mgr_apply_ovl_fifos(struct omap_overlay *ovl)
570{
571 struct overlay_cache_data *oc;
572 struct omap_dss_device *dssdev;
573 u32 size, burst_size;
574
575 oc = &dss_cache.overlay_cache[ovl->id];
576
577 if (!oc->enabled)
578 return;
579
580 dssdev = ovl->manager->device;
581
582 size = dispc_ovl_get_fifo_size(ovl->id);
583
584 burst_size = dispc_ovl_get_burst_size(ovl->id);
585
586 switch (dssdev->type) {
587 case OMAP_DISPLAY_TYPE_DPI:
588 case OMAP_DISPLAY_TYPE_DBI:
589 case OMAP_DISPLAY_TYPE_SDI:
590 case OMAP_DISPLAY_TYPE_VENC:
591 case OMAP_DISPLAY_TYPE_HDMI:
592 default_get_overlay_fifo_thresholds(ovl->id, size,
593 burst_size, &oc->fifo_low,
594 &oc->fifo_high);
595 break;
596#ifdef CONFIG_OMAP2_DSS_DSI
597 case OMAP_DISPLAY_TYPE_DSI:
598 dsi_get_overlay_fifo_thresholds(ovl->id, size,
599 burst_size, &oc->fifo_low,
600 &oc->fifo_high);
601 break;
602#endif
603 default:
604 BUG();
605 }
606}
607
608int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
609{
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200610 int r;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200611 unsigned long flags;
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200612 struct omap_overlay *ovl;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200613
614 DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
615
616 r = dispc_runtime_get();
617 if (r)
618 return r;
619
620 spin_lock_irqsave(&dss_cache.lock, flags);
621
622 /* Configure overlays */
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200623 list_for_each_entry(ovl, &mgr->overlays, list)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200624 omap_dss_mgr_apply_ovl(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200625
626 /* Configure manager */
627 omap_dss_mgr_apply_mgr(mgr);
628
629 /* Configure overlay fifos */
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200630 list_for_each_entry(ovl, &mgr->overlays, list)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200631 omap_dss_mgr_apply_ovl_fifos(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200632
633 r = 0;
Tomi Valkeinen04f66432011-11-07 15:04:01 +0200634 if (mgr->enabled && !mgr_manual_update(mgr)) {
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200635 if (!dss_cache.irq_enabled)
636 dss_register_vsync_isr();
Tomi Valkeinen18135ea2011-11-04 09:35:59 +0200637
638 configure_dispc();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200639 }
640
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200641 spin_unlock_irqrestore(&dss_cache.lock, flags);
642
643 dispc_runtime_put();
644
645 return r;
646}
647
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200648void dss_mgr_enable(struct omap_overlay_manager *mgr)
649{
650 dispc_mgr_enable(mgr->id, true);
Tomi Valkeinenbe729172011-11-04 10:30:47 +0200651 mgr->enabled = true;
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200652}
653
654void dss_mgr_disable(struct omap_overlay_manager *mgr)
655{
656 dispc_mgr_enable(mgr->id, false);
Tomi Valkeinenbe729172011-11-04 10:30:47 +0200657 mgr->enabled = false;
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200658}
659