blob: 242cb1c983c062fdd34661733f87ae02904cb5ca [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 *
Tomi Valkeinen0b53f172011-11-16 14:31:58 +020034 * set_info()
35 * v
Tomi Valkeinen58f255482011-11-04 09:48:54 +020036 * +--------------------+
Tomi Valkeinen0b53f172011-11-16 14:31:58 +020037 * | user_info |
Tomi Valkeinen58f255482011-11-04 09:48:54 +020038 * +--------------------+
39 * v
40 * apply()
41 * v
42 * +--------------------+
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +020043 * | info |
Tomi Valkeinen58f255482011-11-04 09:48:54 +020044 * +--------------------+
45 * v
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +020046 * write_regs()
Tomi Valkeinen58f255482011-11-04 09:48:54 +020047 * v
48 * +--------------------+
49 * | shadow registers |
50 * +--------------------+
51 * v
52 * VFP or lcd/digit_enable
53 * v
54 * +--------------------+
55 * | registers |
56 * +--------------------+
57 */
58
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +020059struct ovl_priv_data {
Tomi Valkeinenc1a9feb2011-11-16 14:11:56 +020060
61 bool user_info_dirty;
62 struct omap_overlay_info user_info;
63
Tomi Valkeinen0b53f172011-11-16 14:31:58 +020064 bool info_dirty;
Tomi Valkeinen58f255482011-11-04 09:48:54 +020065 struct omap_overlay_info info;
66
Tomi Valkeinen0b53f172011-11-16 14:31:58 +020067 bool shadow_info_dirty;
68
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +020069 bool extra_info_dirty;
70 bool shadow_extra_info_dirty;
71
72 bool enabled;
Tomi Valkeinen5d5a97a2011-11-16 14:17:54 +020073 enum omap_channel channel;
Tomi Valkeinen6dc802e2011-11-16 14:28:12 +020074 u32 fifo_low, fifo_high;
Tomi Valkeinen58f255482011-11-04 09:48:54 +020075};
76
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +020077struct mgr_priv_data {
Tomi Valkeinen388c4c62011-11-16 13:58:07 +020078
79 bool user_info_dirty;
80 struct omap_overlay_manager_info user_info;
81
Tomi Valkeinen0b53f172011-11-16 14:31:58 +020082 bool info_dirty;
Tomi Valkeinen58f255482011-11-04 09:48:54 +020083 struct omap_overlay_manager_info info;
84
Tomi Valkeinen0b53f172011-11-16 14:31:58 +020085 bool shadow_info_dirty;
86
Tomi Valkeinen43a972d2011-11-15 15:04:25 +020087 /* If true, GO bit is up and shadow registers cannot be written.
88 * Never true for manual update displays */
89 bool busy;
90
Tomi Valkeinen34861372011-11-18 15:43:29 +020091 /* If true, dispc output is enabled */
92 bool updating;
93
Tomi Valkeinenbf213522011-11-15 14:43:53 +020094 /* If true, a display is enabled using this manager */
95 bool enabled;
Tomi Valkeinen58f255482011-11-04 09:48:54 +020096};
97
98static struct {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +020099 struct ovl_priv_data ovl_priv_data_array[MAX_DSS_OVERLAYS];
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200100 struct mgr_priv_data mgr_priv_data_array[MAX_DSS_MANAGERS];
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200101
102 bool irq_enabled;
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200103} dss_data;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200104
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200105/* protects dss_data */
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200106static spinlock_t data_lock;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200107/* lock for blocking functions */
108static DEFINE_MUTEX(apply_lock);
Tomi Valkeinenf1577ce2011-11-16 14:37:48 +0200109static DECLARE_COMPLETION(extra_updated_completion);
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200110
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200111static void dss_register_vsync_isr(void);
112
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200113static struct ovl_priv_data *get_ovl_priv(struct omap_overlay *ovl)
114{
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200115 return &dss_data.ovl_priv_data_array[ovl->id];
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200116}
117
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200118static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr)
119{
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200120 return &dss_data.mgr_priv_data_array[mgr->id];
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200121}
122
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200123void dss_apply_init(void)
124{
Tomi Valkeinenc1a9feb2011-11-16 14:11:56 +0200125 const int num_ovls = dss_feat_get_num_ovls();
126 int i;
127
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200128 spin_lock_init(&data_lock);
Tomi Valkeinenc1a9feb2011-11-16 14:11:56 +0200129
130 for (i = 0; i < num_ovls; ++i) {
131 struct ovl_priv_data *op;
132
133 op = &dss_data.ovl_priv_data_array[i];
134
135 op->info.global_alpha = 255;
136
137 switch (i) {
138 case 0:
139 op->info.zorder = 0;
140 break;
141 case 1:
142 op->info.zorder =
143 dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 3 : 0;
144 break;
145 case 2:
146 op->info.zorder =
147 dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 2 : 0;
148 break;
149 case 3:
150 op->info.zorder =
151 dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 1 : 0;
152 break;
153 }
154
155 op->user_info = op->info;
156 }
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200157}
158
159static bool ovl_manual_update(struct omap_overlay *ovl)
160{
161 return ovl->manager->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
162}
163
164static bool mgr_manual_update(struct omap_overlay_manager *mgr)
165{
166 return mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
167}
168
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200169static bool need_isr(void)
170{
171 const int num_mgrs = dss_feat_get_num_mgrs();
172 int i;
173
174 for (i = 0; i < num_mgrs; ++i) {
175 struct omap_overlay_manager *mgr;
176 struct mgr_priv_data *mp;
177 struct omap_overlay *ovl;
178
179 mgr = omap_dss_get_overlay_manager(i);
180 mp = get_mgr_priv(mgr);
181
182 if (!mp->enabled)
183 continue;
184
Tomi Valkeinen34861372011-11-18 15:43:29 +0200185 if (mgr_manual_update(mgr)) {
186 /* to catch FRAMEDONE */
187 if (mp->updating)
188 return true;
189 } else {
190 /* to catch GO bit going down */
191 if (mp->busy)
192 return true;
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200193
194 /* to write new values to registers */
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200195 if (mp->info_dirty)
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200196 return true;
Tomi Valkeinen34861372011-11-18 15:43:29 +0200197
198 list_for_each_entry(ovl, &mgr->overlays, list) {
199 struct ovl_priv_data *op;
200
201 op = get_ovl_priv(ovl);
202
203 if (!op->enabled)
204 continue;
205
206 /* to write new values to registers */
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200207 if (op->info_dirty || op->extra_info_dirty)
Tomi Valkeinen34861372011-11-18 15:43:29 +0200208 return true;
209 }
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200210 }
211 }
212
213 return false;
214}
215
216static bool need_go(struct omap_overlay_manager *mgr)
217{
218 struct omap_overlay *ovl;
219 struct mgr_priv_data *mp;
220 struct ovl_priv_data *op;
221
222 mp = get_mgr_priv(mgr);
223
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200224 if (mp->shadow_info_dirty)
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200225 return true;
226
227 list_for_each_entry(ovl, &mgr->overlays, list) {
228 op = get_ovl_priv(ovl);
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200229 if (op->shadow_info_dirty || op->shadow_extra_info_dirty)
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200230 return true;
231 }
232
233 return false;
234}
235
Tomi Valkeinenf1577ce2011-11-16 14:37:48 +0200236/* returns true if an extra_info field is currently being updated */
237static bool extra_info_update_ongoing(void)
238{
239 const int num_ovls = omap_dss_get_num_overlays();
240 struct ovl_priv_data *op;
241 struct omap_overlay *ovl;
242 struct mgr_priv_data *mp;
243 int i;
244 bool eid;
245
246 for (i = 0; i < num_ovls; ++i) {
247 ovl = omap_dss_get_overlay(i);
248 op = get_ovl_priv(ovl);
249
250 if (!op->enabled)
251 continue;
252
253 mp = get_mgr_priv(ovl->manager);
254
255 if (!mp->enabled)
256 continue;
257
258 eid = op->extra_info_dirty || op->shadow_extra_info_dirty;
259
260 if (!eid)
261 continue;
262
263 if (ovl_manual_update(ovl) && !mp->updating)
264 continue;
265
266 return true;
267 }
268
269 return false;
270}
271
272/* wait until no extra_info updates are pending */
273static void wait_pending_extra_info_updates(void)
274{
275 bool updating;
276 unsigned long flags;
277 unsigned long t;
278
279 spin_lock_irqsave(&data_lock, flags);
280
281 updating = extra_info_update_ongoing();
282
283 if (!updating) {
284 spin_unlock_irqrestore(&data_lock, flags);
285 return;
286 }
287
288 init_completion(&extra_updated_completion);
289
290 spin_unlock_irqrestore(&data_lock, flags);
291
292 t = msecs_to_jiffies(500);
293 wait_for_completion_timeout(&extra_updated_completion, t);
294
295 updating = extra_info_update_ongoing();
296
297 WARN_ON(updating);
298}
299
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200300int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
301{
302 unsigned long timeout = msecs_to_jiffies(500);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200303 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200304 u32 irq;
305 int r;
306 int i;
307 struct omap_dss_device *dssdev = mgr->device;
308
309 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
310 return 0;
311
312 if (mgr_manual_update(mgr))
313 return 0;
314
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200315 irq = dispc_mgr_get_vsync_irq(mgr->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200316
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200317 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200318 i = 0;
319 while (1) {
320 unsigned long flags;
321 bool shadow_dirty, dirty;
322
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200323 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200324 dirty = mp->info_dirty;
325 shadow_dirty = mp->shadow_info_dirty;
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200326 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200327
328 if (!dirty && !shadow_dirty) {
329 r = 0;
330 break;
331 }
332
333 /* 4 iterations is the worst case:
334 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
335 * 2 - first VSYNC, dirty = true
336 * 3 - dirty = false, shadow_dirty = true
337 * 4 - shadow_dirty = false */
338 if (i++ == 3) {
339 DSSERR("mgr(%d)->wait_for_go() not finishing\n",
340 mgr->id);
341 r = 0;
342 break;
343 }
344
345 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
346 if (r == -ERESTARTSYS)
347 break;
348
349 if (r) {
350 DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
351 break;
352 }
353 }
354
355 return r;
356}
357
358int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
359{
360 unsigned long timeout = msecs_to_jiffies(500);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200361 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200362 struct omap_dss_device *dssdev;
363 u32 irq;
364 int r;
365 int i;
366
367 if (!ovl->manager)
368 return 0;
369
370 dssdev = ovl->manager->device;
371
372 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
373 return 0;
374
375 if (ovl_manual_update(ovl))
376 return 0;
377
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200378 irq = dispc_mgr_get_vsync_irq(ovl->manager->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200379
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200380 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200381 i = 0;
382 while (1) {
383 unsigned long flags;
384 bool shadow_dirty, dirty;
385
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200386 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200387 dirty = op->info_dirty;
388 shadow_dirty = op->shadow_info_dirty;
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200389 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200390
391 if (!dirty && !shadow_dirty) {
392 r = 0;
393 break;
394 }
395
396 /* 4 iterations is the worst case:
397 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
398 * 2 - first VSYNC, dirty = true
399 * 3 - dirty = false, shadow_dirty = true
400 * 4 - shadow_dirty = false */
401 if (i++ == 3) {
402 DSSERR("ovl(%d)->wait_for_go() not finishing\n",
403 ovl->id);
404 r = 0;
405 break;
406 }
407
408 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
409 if (r == -ERESTARTSYS)
410 break;
411
412 if (r) {
413 DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id);
414 break;
415 }
416 }
417
418 return r;
419}
420
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200421static void dss_ovl_write_regs(struct omap_overlay *ovl)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200422{
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200423 struct ovl_priv_data *op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200424 struct omap_overlay_info *oi;
425 bool ilace, replication;
Tomi Valkeinen34861372011-11-18 15:43:29 +0200426 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200427 int r;
428
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200429 DSSDBGF("%d", ovl->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200430
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200431 if (!op->enabled || !op->info_dirty)
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200432 return;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200433
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200434 oi = &op->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200435
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200436 replication = dss_use_replication(ovl->manager->device, oi->color_mode);
437
438 ilace = ovl->manager->device->type == OMAP_DISPLAY_TYPE_VENC;
439
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200440 r = dispc_ovl_setup(ovl->id, oi, ilace, replication);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200441 if (r) {
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200442 /*
443 * We can't do much here, as this function can be called from
444 * vsync interrupt.
445 */
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200446 DSSERR("dispc_ovl_setup failed for ovl %d\n", ovl->id);
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200447
448 /* This will leave fifo configurations in a nonoptimal state */
449 op->enabled = false;
450 dispc_ovl_enable(ovl->id, false);
451 return;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200452 }
453
Tomi Valkeinen34861372011-11-18 15:43:29 +0200454 mp = get_mgr_priv(ovl->manager);
455
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200456 op->info_dirty = false;
Tomi Valkeinen34861372011-11-18 15:43:29 +0200457 if (mp->updating)
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200458 op->shadow_info_dirty = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200459}
460
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +0200461static void dss_ovl_write_regs_extra(struct omap_overlay *ovl)
462{
463 struct ovl_priv_data *op = get_ovl_priv(ovl);
Tomi Valkeinen34861372011-11-18 15:43:29 +0200464 struct mgr_priv_data *mp;
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +0200465
466 DSSDBGF("%d", ovl->id);
467
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200468 if (!op->extra_info_dirty)
469 return;
470
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +0200471 /* note: write also when op->enabled == false, so that the ovl gets
472 * disabled */
473
474 dispc_ovl_enable(ovl->id, op->enabled);
Tomi Valkeinen5d5a97a2011-11-16 14:17:54 +0200475 dispc_ovl_set_channel_out(ovl->id, op->channel);
Tomi Valkeinen6dc802e2011-11-16 14:28:12 +0200476 dispc_ovl_set_fifo_threshold(ovl->id, op->fifo_low, op->fifo_high);
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200477
Tomi Valkeinen34861372011-11-18 15:43:29 +0200478 mp = get_mgr_priv(ovl->manager);
479
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200480 op->extra_info_dirty = false;
Tomi Valkeinen34861372011-11-18 15:43:29 +0200481 if (mp->updating)
482 op->shadow_extra_info_dirty = true;
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +0200483}
484
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200485static void dss_mgr_write_regs(struct omap_overlay_manager *mgr)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200486{
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200487 struct mgr_priv_data *mp = get_mgr_priv(mgr);
488 struct omap_overlay *ovl;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200489
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200490 DSSDBGF("%d", mgr->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200491
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200492 if (!mp->enabled)
493 return;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200494
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200495 WARN_ON(mp->busy);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200496
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200497 /* Commit overlay settings */
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200498 list_for_each_entry(ovl, &mgr->overlays, list) {
499 dss_ovl_write_regs(ovl);
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +0200500 dss_ovl_write_regs_extra(ovl);
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +0200501 }
502
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200503 if (mp->info_dirty) {
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200504 dispc_mgr_setup(mgr->id, &mp->info);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200505
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200506 mp->info_dirty = false;
Tomi Valkeinen34861372011-11-18 15:43:29 +0200507 if (mp->updating)
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200508 mp->shadow_info_dirty = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200509 }
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200510}
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200511
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200512static void dss_write_regs(void)
513{
514 const int num_mgrs = omap_dss_get_num_overlay_managers();
515 int i;
516
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200517 for (i = 0; i < num_mgrs; ++i) {
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200518 struct omap_overlay_manager *mgr;
519 struct mgr_priv_data *mp;
520
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200521 mgr = omap_dss_get_overlay_manager(i);
522 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200523
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200524 if (!mp->enabled || mgr_manual_update(mgr) || mp->busy)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200525 continue;
526
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200527 dss_mgr_write_regs(mgr);
528
529 if (need_go(mgr)) {
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200530 mp->busy = true;
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200531
532 if (!dss_data.irq_enabled && need_isr())
533 dss_register_vsync_isr();
534
535 dispc_mgr_go(mgr->id);
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200536 }
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200537 }
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200538}
539
540void dss_mgr_start_update(struct omap_overlay_manager *mgr)
541{
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200542 struct mgr_priv_data *mp = get_mgr_priv(mgr);
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200543 unsigned long flags;
544
545 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200546
Tomi Valkeinen34861372011-11-18 15:43:29 +0200547 WARN_ON(mp->updating);
548
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200549 dss_mgr_write_regs(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200550
Tomi Valkeinen34861372011-11-18 15:43:29 +0200551 mp->updating = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200552
Tomi Valkeinen34861372011-11-18 15:43:29 +0200553 if (!dss_data.irq_enabled && need_isr())
554 dss_register_vsync_isr();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200555
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200556 dispc_mgr_enable(mgr->id, true);
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200557
558 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200559}
560
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200561static void dss_apply_irq_handler(void *data, u32 mask);
562
563static void dss_register_vsync_isr(void)
564{
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200565 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200566 u32 mask;
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200567 int r, i;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200568
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200569 mask = 0;
570 for (i = 0; i < num_mgrs; ++i)
571 mask |= dispc_mgr_get_vsync_irq(i);
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200572
Tomi Valkeinen34861372011-11-18 15:43:29 +0200573 for (i = 0; i < num_mgrs; ++i)
574 mask |= dispc_mgr_get_framedone_irq(i);
575
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200576 r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
577 WARN_ON(r);
578
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200579 dss_data.irq_enabled = true;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200580}
581
582static void dss_unregister_vsync_isr(void)
583{
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200584 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200585 u32 mask;
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200586 int r, i;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200587
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200588 mask = 0;
589 for (i = 0; i < num_mgrs; ++i)
590 mask |= dispc_mgr_get_vsync_irq(i);
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200591
Tomi Valkeinen34861372011-11-18 15:43:29 +0200592 for (i = 0; i < num_mgrs; ++i)
593 mask |= dispc_mgr_get_framedone_irq(i);
594
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200595 r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask);
596 WARN_ON(r);
597
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200598 dss_data.irq_enabled = false;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200599}
600
Tomi Valkeinen76098932011-11-16 12:03:22 +0200601static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200602{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200603 struct omap_overlay *ovl;
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200604 struct mgr_priv_data *mp;
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200605 struct ovl_priv_data *op;
Tomi Valkeinen76098932011-11-16 12:03:22 +0200606
607 mp = get_mgr_priv(mgr);
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200608 mp->shadow_info_dirty = false;
Tomi Valkeinen76098932011-11-16 12:03:22 +0200609
610 list_for_each_entry(ovl, &mgr->overlays, list) {
611 op = get_ovl_priv(ovl);
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200612 op->shadow_info_dirty = false;
Tomi Valkeinen76098932011-11-16 12:03:22 +0200613 op->shadow_extra_info_dirty = false;
614 }
615}
616
617static void dss_apply_irq_handler(void *data, u32 mask)
618{
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200619 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200620 int i;
Tomi Valkeinenf1577ce2011-11-16 14:37:48 +0200621 bool extra_updating;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200622
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200623 spin_lock(&data_lock);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200624
Tomi Valkeinen76098932011-11-16 12:03:22 +0200625 /* clear busy, updating flags, shadow_dirty flags */
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200626 for (i = 0; i < num_mgrs; i++) {
Tomi Valkeinen76098932011-11-16 12:03:22 +0200627 struct omap_overlay_manager *mgr;
628 struct mgr_priv_data *mp;
629
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200630 mgr = omap_dss_get_overlay_manager(i);
631 mp = get_mgr_priv(mgr);
632
Tomi Valkeinen76098932011-11-16 12:03:22 +0200633 if (!mp->enabled)
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200634 continue;
635
Tomi Valkeinen76098932011-11-16 12:03:22 +0200636 mp->updating = dispc_mgr_is_enabled(i);
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200637
Tomi Valkeinen76098932011-11-16 12:03:22 +0200638 if (!mgr_manual_update(mgr)) {
639 mp->busy = dispc_mgr_go_busy(i);
640
641 if (!mp->busy)
642 mgr_clear_shadow_dirty(mgr);
643 } else {
644 if (!mp->updating)
645 mgr_clear_shadow_dirty(mgr);
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +0200646 }
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200647 }
648
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200649 dss_write_regs();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200650
Tomi Valkeinenf1577ce2011-11-16 14:37:48 +0200651 extra_updating = extra_info_update_ongoing();
652 if (!extra_updating)
653 complete_all(&extra_updated_completion);
654
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200655 if (!need_isr())
656 dss_unregister_vsync_isr();
Tomi Valkeinen43a972d2011-11-15 15:04:25 +0200657
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200658 spin_unlock(&data_lock);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200659}
660
Tomi Valkeinen5738b632011-11-15 13:37:33 +0200661static void omap_dss_mgr_apply_ovl(struct omap_overlay *ovl)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200662{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200663 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200664
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200665 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200666
Tomi Valkeinenc1a9feb2011-11-16 14:11:56 +0200667 if (!op->user_info_dirty)
Tomi Valkeinen5738b632011-11-15 13:37:33 +0200668 return;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200669
Tomi Valkeinenc1a9feb2011-11-16 14:11:56 +0200670 op->user_info_dirty = false;
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200671 op->info_dirty = true;
Tomi Valkeinenc1a9feb2011-11-16 14:11:56 +0200672 op->info = op->user_info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200673}
674
675static void omap_dss_mgr_apply_mgr(struct omap_overlay_manager *mgr)
676{
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200677 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200678
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200679 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200680
Tomi Valkeinen388c4c62011-11-16 13:58:07 +0200681 if (!mp->user_info_dirty)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200682 return;
683
Tomi Valkeinen388c4c62011-11-16 13:58:07 +0200684 mp->user_info_dirty = false;
Tomi Valkeinen0b53f172011-11-16 14:31:58 +0200685 mp->info_dirty = true;
Tomi Valkeinen388c4c62011-11-16 13:58:07 +0200686 mp->info = mp->user_info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200687}
688
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200689int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
690{
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200691 unsigned long flags;
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200692 struct omap_overlay *ovl;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200693
694 DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
695
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200696 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200697
698 /* Configure overlays */
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200699 list_for_each_entry(ovl, &mgr->overlays, list)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200700 omap_dss_mgr_apply_ovl(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200701
702 /* Configure manager */
703 omap_dss_mgr_apply_mgr(mgr);
704
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200705 dss_write_regs();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200706
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200707 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200708
Tomi Valkeinene70f98a2011-11-16 16:53:44 +0200709 return 0;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200710}
711
Tomi Valkeinen841c09c2011-11-16 15:25:53 +0200712static void dss_apply_ovl_enable(struct omap_overlay *ovl, bool enable)
713{
714 struct ovl_priv_data *op;
715
716 op = get_ovl_priv(ovl);
717
718 if (op->enabled == enable)
719 return;
720
721 op->enabled = enable;
722 op->extra_info_dirty = true;
723}
724
Tomi Valkeinen6dc802e2011-11-16 14:28:12 +0200725static void dss_ovl_setup_fifo(struct omap_overlay *ovl)
726{
727 struct ovl_priv_data *op = get_ovl_priv(ovl);
728 struct omap_dss_device *dssdev;
729 u32 size, burst_size;
730 u32 fifo_low, fifo_high;
731
732 dssdev = ovl->manager->device;
733
734 size = dispc_ovl_get_fifo_size(ovl->id);
735
736 burst_size = dispc_ovl_get_burst_size(ovl->id);
737
738 switch (dssdev->type) {
739 case OMAP_DISPLAY_TYPE_DPI:
740 case OMAP_DISPLAY_TYPE_DBI:
741 case OMAP_DISPLAY_TYPE_SDI:
742 case OMAP_DISPLAY_TYPE_VENC:
743 case OMAP_DISPLAY_TYPE_HDMI:
744 default_get_overlay_fifo_thresholds(ovl->id, size,
745 burst_size, &fifo_low, &fifo_high);
746 break;
747#ifdef CONFIG_OMAP2_DSS_DSI
748 case OMAP_DISPLAY_TYPE_DSI:
749 dsi_get_overlay_fifo_thresholds(ovl->id, size,
750 burst_size, &fifo_low, &fifo_high);
751 break;
752#endif
753 default:
754 BUG();
755 }
756
757 op->fifo_low = fifo_low;
758 op->fifo_high = fifo_high;
759 op->extra_info_dirty = true;
760}
761
762static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr)
763{
764 struct omap_overlay *ovl;
765 struct ovl_priv_data *op;
766 struct mgr_priv_data *mp;
767
768 mp = get_mgr_priv(mgr);
769
770 if (!mp->enabled)
771 return;
772
773 list_for_each_entry(ovl, &mgr->overlays, list) {
774 op = get_ovl_priv(ovl);
775
776 if (!op->enabled)
777 continue;
778
779 dss_ovl_setup_fifo(ovl);
780 }
781}
782
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200783void dss_mgr_enable(struct omap_overlay_manager *mgr)
784{
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200785 struct mgr_priv_data *mp = get_mgr_priv(mgr);
786 unsigned long flags;
787
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200788 mutex_lock(&apply_lock);
789
Tomi Valkeinene4f7ad72011-11-16 16:01:33 +0200790 if (mp->enabled)
791 goto out;
792
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200793 spin_lock_irqsave(&data_lock, flags);
794
795 mp->enabled = true;
796
Tomi Valkeinen6dc802e2011-11-16 14:28:12 +0200797 dss_mgr_setup_fifos(mgr);
798
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200799 dss_write_regs();
800
Tomi Valkeinen34861372011-11-18 15:43:29 +0200801 if (!mgr_manual_update(mgr))
802 mp->updating = true;
803
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200804 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200805
Tomi Valkeinen75c94962011-11-15 18:25:23 +0200806 if (!mgr_manual_update(mgr))
807 dispc_mgr_enable(mgr->id, true);
808
Tomi Valkeinene4f7ad72011-11-16 16:01:33 +0200809out:
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200810 mutex_unlock(&apply_lock);
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200811}
812
813void dss_mgr_disable(struct omap_overlay_manager *mgr)
814{
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200815 struct mgr_priv_data *mp = get_mgr_priv(mgr);
816 unsigned long flags;
817
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200818 mutex_lock(&apply_lock);
819
Tomi Valkeinene4f7ad72011-11-16 16:01:33 +0200820 if (!mp->enabled)
821 goto out;
822
Tomi Valkeinen9a147a62011-11-09 15:30:11 +0200823 if (!mgr_manual_update(mgr))
824 dispc_mgr_enable(mgr->id, false);
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200825
826 spin_lock_irqsave(&data_lock, flags);
827
Tomi Valkeinen34861372011-11-18 15:43:29 +0200828 mp->updating = false;
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200829 mp->enabled = false;
830
831 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200832
Tomi Valkeinene4f7ad72011-11-16 16:01:33 +0200833out:
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200834 mutex_unlock(&apply_lock);
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200835}
836
Tomi Valkeinenf17d04f2011-11-17 14:31:09 +0200837static int dss_mgr_simple_check(struct omap_overlay_manager *mgr,
838 const struct omap_overlay_manager_info *info)
839{
840 if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) {
841 /*
842 * OMAP3 supports only graphics source transparency color key
843 * and alpha blending simultaneously. See TRM 15.4.2.4.2.2
844 * Alpha Mode.
845 */
846 if (info->partial_alpha_enabled && info->trans_enabled
847 && info->trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) {
848 DSSERR("check_manager: illegal transparency key\n");
849 return -EINVAL;
850 }
851 }
852
853 return 0;
854}
855
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200856int dss_mgr_set_info(struct omap_overlay_manager *mgr,
857 struct omap_overlay_manager_info *info)
858{
Tomi Valkeinen388c4c62011-11-16 13:58:07 +0200859 struct mgr_priv_data *mp = get_mgr_priv(mgr);
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200860 unsigned long flags;
Tomi Valkeinenf17d04f2011-11-17 14:31:09 +0200861 int r;
862
863 r = dss_mgr_simple_check(mgr, info);
864 if (r)
865 return r;
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200866
867 spin_lock_irqsave(&data_lock, flags);
868
Tomi Valkeinen388c4c62011-11-16 13:58:07 +0200869 mp->user_info = *info;
870 mp->user_info_dirty = true;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200871
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200872 spin_unlock_irqrestore(&data_lock, flags);
873
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200874 return 0;
875}
876
877void dss_mgr_get_info(struct omap_overlay_manager *mgr,
878 struct omap_overlay_manager_info *info)
879{
Tomi Valkeinen388c4c62011-11-16 13:58:07 +0200880 struct mgr_priv_data *mp = get_mgr_priv(mgr);
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200881 unsigned long flags;
882
883 spin_lock_irqsave(&data_lock, flags);
884
Tomi Valkeinen388c4c62011-11-16 13:58:07 +0200885 *info = mp->user_info;
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200886
887 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200888}
889
890int dss_mgr_set_device(struct omap_overlay_manager *mgr,
891 struct omap_dss_device *dssdev)
892{
893 int r;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200894
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200895 mutex_lock(&apply_lock);
896
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200897 if (dssdev->manager) {
898 DSSERR("display '%s' already has a manager '%s'\n",
899 dssdev->name, dssdev->manager->name);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200900 r = -EINVAL;
901 goto err;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200902 }
903
904 if ((mgr->supported_displays & dssdev->type) == 0) {
905 DSSERR("display '%s' does not support manager '%s'\n",
906 dssdev->name, mgr->name);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200907 r = -EINVAL;
908 goto err;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200909 }
910
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200911 dssdev->manager = mgr;
912 mgr->device = dssdev;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200913
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200914 mutex_unlock(&apply_lock);
915
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200916 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200917err:
918 mutex_unlock(&apply_lock);
919 return r;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200920}
921
922int dss_mgr_unset_device(struct omap_overlay_manager *mgr)
923{
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200924 int r;
925
926 mutex_lock(&apply_lock);
927
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200928 if (!mgr->device) {
929 DSSERR("failed to unset display, display not set.\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200930 r = -EINVAL;
931 goto err;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200932 }
933
934 /*
935 * Don't allow currently enabled displays to have the overlay manager
936 * pulled out from underneath them
937 */
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200938 if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED) {
939 r = -EINVAL;
940 goto err;
941 }
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200942
943 mgr->device->manager = NULL;
944 mgr->device = NULL;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200945
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200946 mutex_unlock(&apply_lock);
947
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200948 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200949err:
950 mutex_unlock(&apply_lock);
951 return r;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200952}
953
954
Tomi Valkeinenfcc764d2011-11-17 14:26:48 +0200955static int dss_ovl_simple_check(struct omap_overlay *ovl,
956 const struct omap_overlay_info *info)
957{
958 if (info->paddr == 0) {
959 DSSERR("check_overlay: paddr cannot be 0\n");
960 return -EINVAL;
961 }
962
963 if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
964 if (info->out_width != 0 && info->width != info->out_width) {
965 DSSERR("check_overlay: overlay %d doesn't support "
966 "scaling\n", ovl->id);
967 return -EINVAL;
968 }
969
970 if (info->out_height != 0 && info->height != info->out_height) {
971 DSSERR("check_overlay: overlay %d doesn't support "
972 "scaling\n", ovl->id);
973 return -EINVAL;
974 }
975 }
976
977 if ((ovl->supported_modes & info->color_mode) == 0) {
978 DSSERR("check_overlay: overlay %d doesn't support mode %d\n",
979 ovl->id, info->color_mode);
980 return -EINVAL;
981 }
982
983 if (info->zorder >= omap_dss_get_num_overlays()) {
984 DSSERR("check_overlay: zorder %d too high\n", info->zorder);
985 return -EINVAL;
986 }
987
988 return 0;
989}
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200990
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200991int dss_ovl_set_info(struct omap_overlay *ovl,
992 struct omap_overlay_info *info)
993{
Tomi Valkeinenc1a9feb2011-11-16 14:11:56 +0200994 struct ovl_priv_data *op = get_ovl_priv(ovl);
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200995 unsigned long flags;
Tomi Valkeinenfcc764d2011-11-17 14:26:48 +0200996 int r;
997
998 r = dss_ovl_simple_check(ovl, info);
999 if (r)
1000 return r;
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +02001001
1002 spin_lock_irqsave(&data_lock, flags);
1003
Tomi Valkeinenc1a9feb2011-11-16 14:11:56 +02001004 op->user_info = *info;
1005 op->user_info_dirty = true;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001006
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +02001007 spin_unlock_irqrestore(&data_lock, flags);
1008
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001009 return 0;
1010}
1011
1012void dss_ovl_get_info(struct omap_overlay *ovl,
1013 struct omap_overlay_info *info)
1014{
Tomi Valkeinenc1a9feb2011-11-16 14:11:56 +02001015 struct ovl_priv_data *op = get_ovl_priv(ovl);
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +02001016 unsigned long flags;
1017
1018 spin_lock_irqsave(&data_lock, flags);
1019
Tomi Valkeinenc1a9feb2011-11-16 14:11:56 +02001020 *info = op->user_info;
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +02001021
1022 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001023}
1024
1025int dss_ovl_set_manager(struct omap_overlay *ovl,
1026 struct omap_overlay_manager *mgr)
1027{
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +02001028 struct ovl_priv_data *op = get_ovl_priv(ovl);
1029 unsigned long flags;
Tomi Valkeinen5558db32011-11-15 14:28:48 +02001030 int r;
1031
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001032 if (!mgr)
1033 return -EINVAL;
1034
Tomi Valkeinen5558db32011-11-15 14:28:48 +02001035 mutex_lock(&apply_lock);
1036
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001037 if (ovl->manager) {
1038 DSSERR("overlay '%s' already has a manager '%s'\n",
1039 ovl->name, ovl->manager->name);
Tomi Valkeinen5558db32011-11-15 14:28:48 +02001040 r = -EINVAL;
1041 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001042 }
1043
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +02001044 spin_lock_irqsave(&data_lock, flags);
1045
1046 if (op->enabled) {
1047 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001048 DSSERR("overlay has to be disabled to change the manager\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +02001049 r = -EINVAL;
1050 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001051 }
1052
Tomi Valkeinen5d5a97a2011-11-16 14:17:54 +02001053 op->channel = mgr->id;
1054 op->extra_info_dirty = true;
1055
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001056 ovl->manager = mgr;
1057 list_add_tail(&ovl->list, &mgr->overlays);
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001058
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +02001059 spin_unlock_irqrestore(&data_lock, flags);
1060
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001061 /* XXX: When there is an overlay on a DSI manual update display, and
1062 * the overlay is first disabled, then moved to tv, and enabled, we
1063 * seem to get SYNC_LOST_DIGIT error.
1064 *
1065 * Waiting doesn't seem to help, but updating the manual update display
1066 * after disabling the overlay seems to fix this. This hints that the
1067 * overlay is perhaps somehow tied to the LCD output until the output
1068 * is updated.
1069 *
1070 * Userspace workaround for this is to update the LCD after disabling
1071 * the overlay, but before moving the overlay to TV.
1072 */
1073
Tomi Valkeinen5558db32011-11-15 14:28:48 +02001074 mutex_unlock(&apply_lock);
1075
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001076 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +02001077err:
1078 mutex_unlock(&apply_lock);
1079 return r;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001080}
1081
1082int dss_ovl_unset_manager(struct omap_overlay *ovl)
1083{
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +02001084 struct ovl_priv_data *op = get_ovl_priv(ovl);
1085 unsigned long flags;
Tomi Valkeinen5558db32011-11-15 14:28:48 +02001086 int r;
1087
1088 mutex_lock(&apply_lock);
1089
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001090 if (!ovl->manager) {
1091 DSSERR("failed to detach overlay: manager not set\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +02001092 r = -EINVAL;
1093 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001094 }
1095
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +02001096 spin_lock_irqsave(&data_lock, flags);
1097
1098 if (op->enabled) {
1099 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001100 DSSERR("overlay has to be disabled to unset the manager\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +02001101 r = -EINVAL;
1102 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001103 }
1104
Tomi Valkeinen5d5a97a2011-11-16 14:17:54 +02001105 op->channel = -1;
1106
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001107 ovl->manager = NULL;
1108 list_del(&ovl->list);
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001109
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +02001110 spin_unlock_irqrestore(&data_lock, flags);
1111
Tomi Valkeinen5558db32011-11-15 14:28:48 +02001112 mutex_unlock(&apply_lock);
1113
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001114 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +02001115err:
1116 mutex_unlock(&apply_lock);
1117 return r;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +02001118}
1119
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +02001120bool dss_ovl_is_enabled(struct omap_overlay *ovl)
1121{
1122 struct ovl_priv_data *op = get_ovl_priv(ovl);
1123 unsigned long flags;
1124 bool e;
1125
1126 spin_lock_irqsave(&data_lock, flags);
1127
1128 e = op->enabled;
1129
1130 spin_unlock_irqrestore(&data_lock, flags);
1131
1132 return e;
1133}
1134
1135int dss_ovl_enable(struct omap_overlay *ovl)
1136{
1137 struct ovl_priv_data *op = get_ovl_priv(ovl);
1138 unsigned long flags;
1139 int r;
1140
1141 mutex_lock(&apply_lock);
1142
Tomi Valkeinene4f7ad72011-11-16 16:01:33 +02001143 if (op->enabled) {
1144 r = 0;
1145 goto err;
1146 }
1147
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +02001148 if (ovl->manager == NULL || ovl->manager->device == NULL) {
1149 r = -EINVAL;
1150 goto err;
1151 }
1152
1153 spin_lock_irqsave(&data_lock, flags);
1154
Tomi Valkeinen841c09c2011-11-16 15:25:53 +02001155 dss_apply_ovl_enable(ovl, true);
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +02001156
Tomi Valkeinen6dc802e2011-11-16 14:28:12 +02001157 dss_ovl_setup_fifo(ovl);
1158
Tomi Valkeinen75c94962011-11-15 18:25:23 +02001159 dss_write_regs();
1160
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +02001161 spin_unlock_irqrestore(&data_lock, flags);
1162
1163 mutex_unlock(&apply_lock);
1164
1165 return 0;
1166err:
1167 mutex_unlock(&apply_lock);
1168 return r;
1169}
1170
1171int dss_ovl_disable(struct omap_overlay *ovl)
1172{
1173 struct ovl_priv_data *op = get_ovl_priv(ovl);
1174 unsigned long flags;
1175 int r;
1176
1177 mutex_lock(&apply_lock);
1178
Tomi Valkeinene4f7ad72011-11-16 16:01:33 +02001179 if (!op->enabled) {
1180 r = 0;
1181 goto err;
1182 }
1183
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +02001184 if (ovl->manager == NULL || ovl->manager->device == NULL) {
1185 r = -EINVAL;
1186 goto err;
1187 }
1188
1189 spin_lock_irqsave(&data_lock, flags);
1190
Tomi Valkeinen841c09c2011-11-16 15:25:53 +02001191 dss_apply_ovl_enable(ovl, false);
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +02001192
Tomi Valkeinen75c94962011-11-15 18:25:23 +02001193 dss_write_regs();
1194
Tomi Valkeinenaaa874a2011-11-15 16:37:53 +02001195 spin_unlock_irqrestore(&data_lock, flags);
1196
1197 mutex_unlock(&apply_lock);
1198
1199 return 0;
1200
1201err:
1202 mutex_unlock(&apply_lock);
1203 return r;
1204}
1205