blob: 1be5f47a21149b22bece283a2f92e5bc86f7e2d0 [file] [log] [blame]
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001/*
2 * linux/drivers/video/omap2/dss/manager.c
3 *
4 * Copyright (C) 2009 Nokia Corporation
5 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
6 *
7 * Some code and ideas taken from drivers/video/omap/ driver
8 * by Imre Deak.
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License version 2 as published by
12 * the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#define DSS_SUBSYS_NAME "MANAGER"
24
25#include <linux/kernel.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090026#include <linux/slab.h>
Tomi Valkeineneed07e02009-08-07 13:43:20 +030027#include <linux/module.h>
28#include <linux/platform_device.h>
29#include <linux/spinlock.h>
30#include <linux/jiffies.h>
31
Tomi Valkeinena0b38cc2011-05-11 14:05:07 +030032#include <video/omapdss.h>
Tomi Valkeineneed07e02009-08-07 13:43:20 +030033#include <plat/cpu.h>
34
35#include "dss.h"
Archit Tanejaa0acb552010-09-15 19:20:00 +053036#include "dss_features.h"
Tomi Valkeineneed07e02009-08-07 13:43:20 +030037
38static int num_managers;
39static struct list_head manager_list;
40
41static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf)
42{
43 return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name);
44}
45
46static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf)
47{
48 return snprintf(buf, PAGE_SIZE, "%s\n",
49 mgr->device ? mgr->device->name : "<none>");
50}
51
52static ssize_t manager_display_store(struct omap_overlay_manager *mgr,
53 const char *buf, size_t size)
54{
55 int r = 0;
56 size_t len = size;
57 struct omap_dss_device *dssdev = NULL;
58
59 int match(struct omap_dss_device *dssdev, void *data)
60 {
61 const char *str = data;
62 return sysfs_streq(dssdev->name, str);
63 }
64
65 if (buf[size-1] == '\n')
66 --len;
67
68 if (len > 0)
69 dssdev = omap_dss_find_device((void *)buf, match);
70
71 if (len > 0 && dssdev == NULL)
72 return -EINVAL;
73
74 if (dssdev)
75 DSSDBG("display %s found\n", dssdev->name);
76
77 if (mgr->device) {
78 r = mgr->unset_device(mgr);
79 if (r) {
80 DSSERR("failed to unset display\n");
81 goto put_device;
82 }
83 }
84
85 if (dssdev) {
86 r = mgr->set_device(mgr, dssdev);
87 if (r) {
88 DSSERR("failed to set manager\n");
89 goto put_device;
90 }
91
92 r = mgr->apply(mgr);
93 if (r) {
94 DSSERR("failed to apply dispc config\n");
95 goto put_device;
96 }
97 }
98
99put_device:
100 if (dssdev)
101 omap_dss_put_device(dssdev);
102
103 return r ? r : size;
104}
105
106static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr,
107 char *buf)
108{
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300109 return snprintf(buf, PAGE_SIZE, "%#x\n", mgr->info.default_color);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300110}
111
112static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr,
113 const char *buf, size_t size)
114{
115 struct omap_overlay_manager_info info;
116 u32 color;
117 int r;
118
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300119 r = kstrtouint(buf, 0, &color);
120 if (r)
121 return r;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300122
123 mgr->get_manager_info(mgr, &info);
124
125 info.default_color = color;
126
127 r = mgr->set_manager_info(mgr, &info);
128 if (r)
129 return r;
130
131 r = mgr->apply(mgr);
132 if (r)
133 return r;
134
135 return size;
136}
137
138static const char *trans_key_type_str[] = {
139 "gfx-destination",
140 "video-source",
141};
142
143static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr,
144 char *buf)
145{
146 enum omap_dss_trans_key_type key_type;
147
148 key_type = mgr->info.trans_key_type;
149 BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str));
150
151 return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]);
152}
153
154static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr,
155 const char *buf, size_t size)
156{
157 enum omap_dss_trans_key_type key_type;
158 struct omap_overlay_manager_info info;
159 int r;
160
161 for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
162 key_type < ARRAY_SIZE(trans_key_type_str); key_type++) {
163 if (sysfs_streq(buf, trans_key_type_str[key_type]))
164 break;
165 }
166
167 if (key_type == ARRAY_SIZE(trans_key_type_str))
168 return -EINVAL;
169
170 mgr->get_manager_info(mgr, &info);
171
172 info.trans_key_type = key_type;
173
174 r = mgr->set_manager_info(mgr, &info);
175 if (r)
176 return r;
177
178 r = mgr->apply(mgr);
179 if (r)
180 return r;
181
182 return size;
183}
184
185static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr,
186 char *buf)
187{
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300188 return snprintf(buf, PAGE_SIZE, "%#x\n", mgr->info.trans_key);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300189}
190
191static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr,
192 const char *buf, size_t size)
193{
194 struct omap_overlay_manager_info info;
195 u32 key_value;
196 int r;
197
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300198 r = kstrtouint(buf, 0, &key_value);
199 if (r)
200 return r;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300201
202 mgr->get_manager_info(mgr, &info);
203
204 info.trans_key = key_value;
205
206 r = mgr->set_manager_info(mgr, &info);
207 if (r)
208 return r;
209
210 r = mgr->apply(mgr);
211 if (r)
212 return r;
213
214 return size;
215}
216
217static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr,
218 char *buf)
219{
220 return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_enabled);
221}
222
223static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr,
224 const char *buf, size_t size)
225{
226 struct omap_overlay_manager_info info;
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300227 bool enable;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300228 int r;
229
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300230 r = strtobool(buf, &enable);
231 if (r)
232 return r;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300233
234 mgr->get_manager_info(mgr, &info);
235
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300236 info.trans_enabled = enable;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300237
238 r = mgr->set_manager_info(mgr, &info);
239 if (r)
240 return r;
241
242 r = mgr->apply(mgr);
243 if (r)
244 return r;
245
246 return size;
247}
248
249static ssize_t manager_alpha_blending_enabled_show(
250 struct omap_overlay_manager *mgr, char *buf)
251{
Archit Taneja11354dd2011-09-26 11:47:29 +0530252 WARN_ON(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER));
253
254 return snprintf(buf, PAGE_SIZE, "%d\n",
255 mgr->info.partial_alpha_enabled);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300256}
257
258static ssize_t manager_alpha_blending_enabled_store(
259 struct omap_overlay_manager *mgr,
260 const char *buf, size_t size)
261{
262 struct omap_overlay_manager_info info;
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300263 bool enable;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300264 int r;
265
Archit Taneja11354dd2011-09-26 11:47:29 +0530266 WARN_ON(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER));
267
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300268 r = strtobool(buf, &enable);
269 if (r)
270 return r;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300271
272 mgr->get_manager_info(mgr, &info);
273
Archit Taneja11354dd2011-09-26 11:47:29 +0530274 info.partial_alpha_enabled = enable;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300275
276 r = mgr->set_manager_info(mgr, &info);
277 if (r)
278 return r;
279
280 r = mgr->apply(mgr);
281 if (r)
282 return r;
283
284 return size;
285}
286
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300287static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr,
288 char *buf)
289{
290 return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.cpr_enable);
291}
292
293static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr,
294 const char *buf, size_t size)
295{
296 struct omap_overlay_manager_info info;
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300297 int r;
298 bool enable;
299
300 if (!dss_has_feature(FEAT_CPR))
301 return -ENODEV;
302
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300303 r = strtobool(buf, &enable);
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300304 if (r)
305 return r;
306
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300307 mgr->get_manager_info(mgr, &info);
308
309 if (info.cpr_enable == enable)
310 return size;
311
312 info.cpr_enable = enable;
313
314 r = mgr->set_manager_info(mgr, &info);
315 if (r)
316 return r;
317
318 r = mgr->apply(mgr);
319 if (r)
320 return r;
321
322 return size;
323}
324
325static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr,
326 char *buf)
327{
328 struct omap_overlay_manager_info info;
329
330 mgr->get_manager_info(mgr, &info);
331
332 return snprintf(buf, PAGE_SIZE,
333 "%d %d %d %d %d %d %d %d %d\n",
334 info.cpr_coefs.rr,
335 info.cpr_coefs.rg,
336 info.cpr_coefs.rb,
337 info.cpr_coefs.gr,
338 info.cpr_coefs.gg,
339 info.cpr_coefs.gb,
340 info.cpr_coefs.br,
341 info.cpr_coefs.bg,
342 info.cpr_coefs.bb);
343}
344
345static ssize_t manager_cpr_coef_store(struct omap_overlay_manager *mgr,
346 const char *buf, size_t size)
347{
348 struct omap_overlay_manager_info info;
349 struct omap_dss_cpr_coefs coefs;
350 int r, i;
351 s16 *arr;
352
353 if (!dss_has_feature(FEAT_CPR))
354 return -ENODEV;
355
356 if (sscanf(buf, "%hd %hd %hd %hd %hd %hd %hd %hd %hd",
357 &coefs.rr, &coefs.rg, &coefs.rb,
358 &coefs.gr, &coefs.gg, &coefs.gb,
359 &coefs.br, &coefs.bg, &coefs.bb) != 9)
360 return -EINVAL;
361
362 arr = (s16[]){ coefs.rr, coefs.rg, coefs.rb,
363 coefs.gr, coefs.gg, coefs.gb,
364 coefs.br, coefs.bg, coefs.bb };
365
366 for (i = 0; i < 9; ++i) {
367 if (arr[i] < -512 || arr[i] > 511)
368 return -EINVAL;
369 }
370
371 mgr->get_manager_info(mgr, &info);
372
373 info.cpr_coefs = coefs;
374
375 r = mgr->set_manager_info(mgr, &info);
376 if (r)
377 return r;
378
379 r = mgr->apply(mgr);
380 if (r)
381 return r;
382
383 return size;
384}
385
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300386struct manager_attribute {
387 struct attribute attr;
388 ssize_t (*show)(struct omap_overlay_manager *, char *);
389 ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t);
390};
391
392#define MANAGER_ATTR(_name, _mode, _show, _store) \
393 struct manager_attribute manager_attr_##_name = \
394 __ATTR(_name, _mode, _show, _store)
395
396static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL);
397static MANAGER_ATTR(display, S_IRUGO|S_IWUSR,
398 manager_display_show, manager_display_store);
399static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR,
400 manager_default_color_show, manager_default_color_store);
401static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR,
402 manager_trans_key_type_show, manager_trans_key_type_store);
403static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR,
404 manager_trans_key_value_show, manager_trans_key_value_store);
405static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR,
406 manager_trans_key_enabled_show,
407 manager_trans_key_enabled_store);
408static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR,
409 manager_alpha_blending_enabled_show,
410 manager_alpha_blending_enabled_store);
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300411static MANAGER_ATTR(cpr_enable, S_IRUGO|S_IWUSR,
412 manager_cpr_enable_show,
413 manager_cpr_enable_store);
414static MANAGER_ATTR(cpr_coef, S_IRUGO|S_IWUSR,
415 manager_cpr_coef_show,
416 manager_cpr_coef_store);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300417
418
419static struct attribute *manager_sysfs_attrs[] = {
420 &manager_attr_name.attr,
421 &manager_attr_display.attr,
422 &manager_attr_default_color.attr,
423 &manager_attr_trans_key_type.attr,
424 &manager_attr_trans_key_value.attr,
425 &manager_attr_trans_key_enabled.attr,
426 &manager_attr_alpha_blending_enabled.attr,
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300427 &manager_attr_cpr_enable.attr,
428 &manager_attr_cpr_coef.attr,
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300429 NULL
430};
431
432static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr,
433 char *buf)
434{
435 struct omap_overlay_manager *manager;
436 struct manager_attribute *manager_attr;
437
438 manager = container_of(kobj, struct omap_overlay_manager, kobj);
439 manager_attr = container_of(attr, struct manager_attribute, attr);
440
441 if (!manager_attr->show)
442 return -ENOENT;
443
444 return manager_attr->show(manager, buf);
445}
446
447static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr,
448 const char *buf, size_t size)
449{
450 struct omap_overlay_manager *manager;
451 struct manager_attribute *manager_attr;
452
453 manager = container_of(kobj, struct omap_overlay_manager, kobj);
454 manager_attr = container_of(attr, struct manager_attribute, attr);
455
456 if (!manager_attr->store)
457 return -ENOENT;
458
459 return manager_attr->store(manager, buf, size);
460}
461
Emese Revfy52cf25d2010-01-19 02:58:23 +0100462static const struct sysfs_ops manager_sysfs_ops = {
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300463 .show = manager_attr_show,
464 .store = manager_attr_store,
465};
466
467static struct kobj_type manager_ktype = {
468 .sysfs_ops = &manager_sysfs_ops,
469 .default_attrs = manager_sysfs_attrs,
470};
471
472/*
473 * We have 4 levels of cache for the dispc settings. First two are in SW and
474 * the latter two in HW.
475 *
476 * +--------------------+
477 * |overlay/manager_info|
478 * +--------------------+
479 * v
480 * apply()
481 * v
482 * +--------------------+
483 * | dss_cache |
484 * +--------------------+
485 * v
486 * configure()
487 * v
488 * +--------------------+
489 * | shadow registers |
490 * +--------------------+
491 * v
492 * VFP or lcd/digit_enable
493 * v
494 * +--------------------+
495 * | registers |
496 * +--------------------+
497 */
498
499struct overlay_cache_data {
500 /* If true, cache changed, but not written to shadow registers. Set
501 * in apply(), cleared when registers written. */
502 bool dirty;
503 /* If true, shadow registers contain changed values not yet in real
504 * registers. Set when writing to shadow registers, cleared at
505 * VSYNC/EVSYNC */
506 bool shadow_dirty;
507
508 bool enabled;
509
Nishant Kamat4df9d102011-06-20 20:39:28 +0530510 struct omap_overlay_info info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300511
512 enum omap_channel channel;
513 bool replication;
514 bool ilace;
515
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300516 u32 fifo_low;
517 u32 fifo_high;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300518};
519
520struct manager_cache_data {
521 /* If true, cache changed, but not written to shadow registers. Set
522 * in apply(), cleared when registers written. */
523 bool dirty;
524 /* If true, shadow registers contain changed values not yet in real
525 * registers. Set when writing to shadow registers, cleared at
526 * VSYNC/EVSYNC */
527 bool shadow_dirty;
528
Nishant Kamat4df9d102011-06-20 20:39:28 +0530529 struct omap_overlay_manager_info info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300530
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300531 bool manual_update;
532 bool do_manual_update;
533
534 /* manual update region */
535 u16 x, y, w, h;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300536
537 /* enlarge the update area if the update area contains scaled
538 * overlays */
539 bool enlarge_update_area;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300540};
541
542static struct {
543 spinlock_t lock;
Archit Tanejaa0acb552010-09-15 19:20:00 +0530544 struct overlay_cache_data overlay_cache[MAX_DSS_OVERLAYS];
545 struct manager_cache_data manager_cache[MAX_DSS_MANAGERS];
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300546
547 bool irq_enabled;
548} dss_cache;
549
550
551
552static int omap_dss_set_device(struct omap_overlay_manager *mgr,
553 struct omap_dss_device *dssdev)
554{
555 int i;
556 int r;
557
558 if (dssdev->manager) {
559 DSSERR("display '%s' already has a manager '%s'\n",
560 dssdev->name, dssdev->manager->name);
561 return -EINVAL;
562 }
563
564 if ((mgr->supported_displays & dssdev->type) == 0) {
565 DSSERR("display '%s' does not support manager '%s'\n",
566 dssdev->name, mgr->name);
567 return -EINVAL;
568 }
569
570 for (i = 0; i < mgr->num_overlays; i++) {
571 struct omap_overlay *ovl = mgr->overlays[i];
572
573 if (ovl->manager != mgr || !ovl->info.enabled)
574 continue;
575
576 r = dss_check_overlay(ovl, dssdev);
577 if (r)
578 return r;
579 }
580
581 dssdev->manager = mgr;
582 mgr->device = dssdev;
583 mgr->device_changed = true;
584
585 return 0;
586}
587
588static int omap_dss_unset_device(struct omap_overlay_manager *mgr)
589{
590 if (!mgr->device) {
591 DSSERR("failed to unset display, display not set.\n");
592 return -EINVAL;
593 }
594
Daniel Morsing0f770b42011-08-03 22:10:51 +0200595 /*
596 * Don't allow currently enabled displays to have the overlay manager
597 * pulled out from underneath them
598 */
599 if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED)
600 return -EINVAL;
601
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300602 mgr->device->manager = NULL;
603 mgr->device = NULL;
604 mgr->device_changed = true;
605
606 return 0;
607}
608
Tomi Valkeinen3f71cbe2010-01-08 17:06:04 +0200609static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
610{
611 unsigned long timeout = msecs_to_jiffies(500);
612 u32 irq;
613
Sumit Semwal18faa1b2010-12-02 11:27:14 +0000614 if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) {
Tomi Valkeinen3f71cbe2010-01-08 17:06:04 +0200615 irq = DISPC_IRQ_EVSYNC_ODD;
Mythri P Kb1196012011-03-08 17:15:54 +0530616 } else if (mgr->device->type == OMAP_DISPLAY_TYPE_HDMI) {
617 irq = DISPC_IRQ_EVSYNC_EVEN;
Sumit Semwal18faa1b2010-12-02 11:27:14 +0000618 } else {
619 if (mgr->id == OMAP_DSS_CHANNEL_LCD)
620 irq = DISPC_IRQ_VSYNC;
621 else
622 irq = DISPC_IRQ_VSYNC2;
623 }
Tomi Valkeinen3f71cbe2010-01-08 17:06:04 +0200624 return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
625}
626
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300627static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
628{
629 unsigned long timeout = msecs_to_jiffies(500);
630 struct manager_cache_data *mc;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300631 u32 irq;
632 int r;
633 int i;
Tomi Valkeinen446f7bf2010-01-11 16:12:31 +0200634 struct omap_dss_device *dssdev = mgr->device;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300635
Ville Syrjäläa74b2602010-03-04 16:03:56 +0200636 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300637 return 0;
638
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300639 if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
640 return 0;
641
Mythri P Kb1196012011-03-08 17:15:54 +0530642 if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
643 || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300644 irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300645 } else {
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300646 irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
647 DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300648 }
649
650 mc = &dss_cache.manager_cache[mgr->id];
651 i = 0;
652 while (1) {
653 unsigned long flags;
654 bool shadow_dirty, dirty;
655
656 spin_lock_irqsave(&dss_cache.lock, flags);
657 dirty = mc->dirty;
658 shadow_dirty = mc->shadow_dirty;
659 spin_unlock_irqrestore(&dss_cache.lock, flags);
660
661 if (!dirty && !shadow_dirty) {
662 r = 0;
663 break;
664 }
665
666 /* 4 iterations is the worst case:
667 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
668 * 2 - first VSYNC, dirty = true
669 * 3 - dirty = false, shadow_dirty = true
670 * 4 - shadow_dirty = false */
671 if (i++ == 3) {
672 DSSERR("mgr(%d)->wait_for_go() not finishing\n",
673 mgr->id);
674 r = 0;
675 break;
676 }
677
678 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
679 if (r == -ERESTARTSYS)
680 break;
681
682 if (r) {
683 DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
684 break;
685 }
686 }
687
688 return r;
689}
690
691int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
692{
693 unsigned long timeout = msecs_to_jiffies(500);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300694 struct overlay_cache_data *oc;
695 struct omap_dss_device *dssdev;
696 u32 irq;
697 int r;
698 int i;
699
Ville Syrjäläa74b2602010-03-04 16:03:56 +0200700 if (!ovl->manager)
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300701 return 0;
702
703 dssdev = ovl->manager->device;
704
Ville Syrjäläa74b2602010-03-04 16:03:56 +0200705 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
706 return 0;
707
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300708 if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
709 return 0;
710
Mythri P Kb1196012011-03-08 17:15:54 +0530711 if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
712 || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300713 irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300714 } else {
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300715 irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
716 DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300717 }
718
719 oc = &dss_cache.overlay_cache[ovl->id];
720 i = 0;
721 while (1) {
722 unsigned long flags;
723 bool shadow_dirty, dirty;
724
725 spin_lock_irqsave(&dss_cache.lock, flags);
726 dirty = oc->dirty;
727 shadow_dirty = oc->shadow_dirty;
728 spin_unlock_irqrestore(&dss_cache.lock, flags);
729
730 if (!dirty && !shadow_dirty) {
731 r = 0;
732 break;
733 }
734
735 /* 4 iterations is the worst case:
736 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
737 * 2 - first VSYNC, dirty = true
738 * 3 - dirty = false, shadow_dirty = true
739 * 4 - shadow_dirty = false */
740 if (i++ == 3) {
741 DSSERR("ovl(%d)->wait_for_go() not finishing\n",
742 ovl->id);
743 r = 0;
744 break;
745 }
746
747 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
748 if (r == -ERESTARTSYS)
749 break;
750
751 if (r) {
752 DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id);
753 break;
754 }
755 }
756
757 return r;
758}
759
760static int overlay_enabled(struct omap_overlay *ovl)
761{
762 return ovl->info.enabled && ovl->manager && ovl->manager->device;
763}
764
765/* Is rect1 a subset of rect2? */
766static bool rectangle_subset(int x1, int y1, int w1, int h1,
767 int x2, int y2, int w2, int h2)
768{
769 if (x1 < x2 || y1 < y2)
770 return false;
771
772 if (x1 + w1 > x2 + w2)
773 return false;
774
775 if (y1 + h1 > y2 + h2)
776 return false;
777
778 return true;
779}
780
781/* Do rect1 and rect2 overlap? */
782static bool rectangle_intersects(int x1, int y1, int w1, int h1,
783 int x2, int y2, int w2, int h2)
784{
785 if (x1 >= x2 + w2)
786 return false;
787
788 if (x2 >= x1 + w1)
789 return false;
790
791 if (y1 >= y2 + h2)
792 return false;
793
794 if (y2 >= y1 + h1)
795 return false;
796
797 return true;
798}
799
800static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc)
801{
Nishant Kamat4df9d102011-06-20 20:39:28 +0530802 struct omap_overlay_info *oi = &oc->info;
803
804 if (oi->out_width != 0 && oi->width != oi->out_width)
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300805 return true;
806
Nishant Kamat4df9d102011-06-20 20:39:28 +0530807 if (oi->out_height != 0 && oi->height != oi->out_height)
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300808 return true;
809
810 return false;
811}
812
813static int configure_overlay(enum omap_plane plane)
814{
815 struct overlay_cache_data *c;
816 struct manager_cache_data *mc;
Archit Tanejaa4273b72011-09-14 11:10:10 +0530817 struct omap_overlay_info *oi, new_oi;
Nishant Kamat4df9d102011-06-20 20:39:28 +0530818 struct omap_overlay_manager_info *mi;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300819 u16 outw, outh;
820 u16 x, y, w, h;
821 u32 paddr;
822 int r;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300823 u16 orig_w, orig_h, orig_outw, orig_outh;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300824
825 DSSDBGF("%d", plane);
826
827 c = &dss_cache.overlay_cache[plane];
Nishant Kamat4df9d102011-06-20 20:39:28 +0530828 oi = &c->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300829
830 if (!c->enabled) {
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +0300831 dispc_ovl_enable(plane, 0);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300832 return 0;
833 }
834
835 mc = &dss_cache.manager_cache[c->channel];
Nishant Kamat4df9d102011-06-20 20:39:28 +0530836 mi = &mc->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300837
Nishant Kamat4df9d102011-06-20 20:39:28 +0530838 x = oi->pos_x;
839 y = oi->pos_y;
840 w = oi->width;
841 h = oi->height;
842 outw = oi->out_width == 0 ? oi->width : oi->out_width;
843 outh = oi->out_height == 0 ? oi->height : oi->out_height;
844 paddr = oi->paddr;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300845
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300846 orig_w = w;
847 orig_h = h;
848 orig_outw = outw;
849 orig_outh = outh;
850
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300851 if (mc->manual_update && mc->do_manual_update) {
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300852 unsigned bpp;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300853 unsigned scale_x_m = w, scale_x_d = outw;
854 unsigned scale_y_m = h, scale_y_d = outh;
855
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300856 /* If the overlay is outside the update region, disable it */
857 if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h,
858 x, y, outw, outh)) {
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +0300859 dispc_ovl_enable(plane, 0);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300860 return 0;
861 }
862
Nishant Kamat4df9d102011-06-20 20:39:28 +0530863 switch (oi->color_mode) {
Amber Jainf20e4222011-05-19 19:47:50 +0530864 case OMAP_DSS_COLOR_NV12:
865 bpp = 8;
866 break;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300867 case OMAP_DSS_COLOR_RGB16:
868 case OMAP_DSS_COLOR_ARGB16:
869 case OMAP_DSS_COLOR_YUV2:
870 case OMAP_DSS_COLOR_UYVY:
Amber Jainf20e4222011-05-19 19:47:50 +0530871 case OMAP_DSS_COLOR_RGBA16:
872 case OMAP_DSS_COLOR_RGBX16:
873 case OMAP_DSS_COLOR_ARGB16_1555:
874 case OMAP_DSS_COLOR_XRGB16_1555:
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300875 bpp = 16;
876 break;
877
878 case OMAP_DSS_COLOR_RGB24P:
879 bpp = 24;
880 break;
881
882 case OMAP_DSS_COLOR_RGB24U:
883 case OMAP_DSS_COLOR_ARGB32:
884 case OMAP_DSS_COLOR_RGBA32:
885 case OMAP_DSS_COLOR_RGBX32:
886 bpp = 32;
887 break;
888
889 default:
890 BUG();
891 }
892
Nishant Kamat4df9d102011-06-20 20:39:28 +0530893 if (mc->x > oi->pos_x) {
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300894 x = 0;
Nishant Kamat4df9d102011-06-20 20:39:28 +0530895 outw -= (mc->x - oi->pos_x);
896 paddr += (mc->x - oi->pos_x) *
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300897 scale_x_m / scale_x_d * bpp / 8;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300898 } else {
Nishant Kamat4df9d102011-06-20 20:39:28 +0530899 x = oi->pos_x - mc->x;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300900 }
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300901
Nishant Kamat4df9d102011-06-20 20:39:28 +0530902 if (mc->y > oi->pos_y) {
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300903 y = 0;
Nishant Kamat4df9d102011-06-20 20:39:28 +0530904 outh -= (mc->y - oi->pos_y);
905 paddr += (mc->y - oi->pos_y) *
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300906 scale_y_m / scale_y_d *
Nishant Kamat4df9d102011-06-20 20:39:28 +0530907 oi->screen_width * bpp / 8;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300908 } else {
Nishant Kamat4df9d102011-06-20 20:39:28 +0530909 y = oi->pos_y - mc->y;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300910 }
911
912 if (mc->w < (x + outw))
913 outw -= (x + outw) - (mc->w);
914
915 if (mc->h < (y + outh))
916 outh -= (y + outh) - (mc->h);
917
918 w = w * outw / orig_outw;
919 h = h * outh / orig_outh;
Tomi Valkeinenf55fdcf2010-06-03 16:27:46 +0300920
921 /* YUV mode overlay's input width has to be even and the
922 * algorithm above may adjust the width to be odd.
923 *
924 * Here we adjust the width if needed, preferring to increase
925 * the width if the original width was bigger.
926 */
927 if ((w & 1) &&
Nishant Kamat4df9d102011-06-20 20:39:28 +0530928 (oi->color_mode == OMAP_DSS_COLOR_YUV2 ||
929 oi->color_mode == OMAP_DSS_COLOR_UYVY)) {
Tomi Valkeinenf55fdcf2010-06-03 16:27:46 +0300930 if (orig_w > w)
931 w += 1;
932 else
933 w -= 1;
934 }
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300935 }
936
Archit Tanejaa4273b72011-09-14 11:10:10 +0530937 new_oi = *oi;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300938
Archit Tanejaa4273b72011-09-14 11:10:10 +0530939 /* update new_oi members which could have been possibly updated */
940 new_oi.pos_x = x;
941 new_oi.pos_y = y;
942 new_oi.width = w;
943 new_oi.height = h;
944 new_oi.out_width = outw;
945 new_oi.out_height = outh;
946 new_oi.paddr = paddr;
947
Archit Tanejac3d925292011-09-14 11:52:54 +0530948 r = dispc_ovl_setup(plane, &new_oi, c->ilace, c->channel,
949 c->replication, c->fifo_low, c->fifo_high);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300950 if (r) {
951 /* this shouldn't happen */
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +0300952 DSSERR("dispc_ovl_setup failed for ovl %d\n", plane);
953 dispc_ovl_enable(plane, 0);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300954 return r;
955 }
956
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +0300957 dispc_ovl_enable(plane, 1);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300958
959 return 0;
960}
961
962static void configure_manager(enum omap_channel channel)
963{
Nishant Kamat4df9d102011-06-20 20:39:28 +0530964 struct omap_overlay_manager_info *mi;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300965
966 DSSDBGF("%d", channel);
967
Nishant Kamat4df9d102011-06-20 20:39:28 +0530968 /* picking info from the cache */
969 mi = &dss_cache.manager_cache[channel].info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300970
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +0300971 dispc_mgr_set_default_color(channel, mi->default_color);
972 dispc_mgr_set_trans_key(channel, mi->trans_key_type, mi->trans_key);
973 dispc_mgr_enable_trans_key(channel, mi->trans_enabled);
Archit Taneja11354dd2011-09-26 11:47:29 +0530974 dispc_mgr_enable_alpha_fixed_zorder(channel, mi->partial_alpha_enabled);
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300975 if (dss_has_feature(FEAT_CPR)) {
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +0300976 dispc_mgr_enable_cpr(channel, mi->cpr_enable);
977 dispc_mgr_set_cpr_coef(channel, &mi->cpr_coefs);
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300978 }
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300979}
980
981/* configure_dispc() tries to write values from cache to shadow registers.
982 * It writes only to those managers/overlays that are not busy.
983 * returns 0 if everything could be written to shadow registers.
984 * returns 1 if not everything could be written to shadow registers. */
985static int configure_dispc(void)
986{
987 struct overlay_cache_data *oc;
988 struct manager_cache_data *mc;
Archit Tanejaa0acb552010-09-15 19:20:00 +0530989 const int num_ovls = dss_feat_get_num_ovls();
990 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300991 int i;
992 int r;
Archit Tanejaa0acb552010-09-15 19:20:00 +0530993 bool mgr_busy[MAX_DSS_MANAGERS];
994 bool mgr_go[MAX_DSS_MANAGERS];
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300995 bool busy;
996
997 r = 0;
998 busy = false;
999
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001000 for (i = 0; i < num_mgrs; i++) {
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001001 mgr_busy[i] = dispc_mgr_go_busy(i);
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001002 mgr_go[i] = false;
1003 }
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001004
1005 /* Commit overlay settings */
1006 for (i = 0; i < num_ovls; ++i) {
1007 oc = &dss_cache.overlay_cache[i];
1008 mc = &dss_cache.manager_cache[oc->channel];
1009
1010 if (!oc->dirty)
1011 continue;
1012
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +03001013 if (mc->manual_update && !mc->do_manual_update)
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001014 continue;
1015
1016 if (mgr_busy[oc->channel]) {
1017 busy = true;
1018 continue;
1019 }
1020
1021 r = configure_overlay(i);
1022 if (r)
1023 DSSERR("configure_overlay %d failed\n", i);
1024
1025 oc->dirty = false;
1026 oc->shadow_dirty = true;
1027 mgr_go[oc->channel] = true;
1028 }
1029
1030 /* Commit manager settings */
1031 for (i = 0; i < num_mgrs; ++i) {
1032 mc = &dss_cache.manager_cache[i];
1033
1034 if (!mc->dirty)
1035 continue;
1036
1037 if (mc->manual_update && !mc->do_manual_update)
1038 continue;
1039
1040 if (mgr_busy[i]) {
1041 busy = true;
1042 continue;
1043 }
1044
1045 configure_manager(i);
1046 mc->dirty = false;
1047 mc->shadow_dirty = true;
1048 mgr_go[i] = true;
1049 }
1050
1051 /* set GO */
1052 for (i = 0; i < num_mgrs; ++i) {
1053 mc = &dss_cache.manager_cache[i];
1054
1055 if (!mgr_go[i])
1056 continue;
1057
1058 /* We don't need GO with manual update display. LCD iface will
1059 * always be turned off after frame, and new settings will be
1060 * taken in to use at next update */
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +03001061 if (!mc->manual_update)
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001062 dispc_mgr_go(i);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001063 }
1064
1065 if (busy)
1066 r = 1;
1067 else
1068 r = 0;
1069
1070 return r;
1071}
1072
Tomi Valkeinenf49a9512010-03-26 16:26:37 +02001073/* Make the coordinates even. There are some strange problems with OMAP and
1074 * partial DSI update when the update widths are odd. */
1075static void make_even(u16 *x, u16 *w)
1076{
1077 u16 x1, x2;
1078
1079 x1 = *x;
1080 x2 = *x + *w;
1081
1082 x1 &= ~1;
1083 x2 = ALIGN(x2, 2);
1084
1085 *x = x1;
1086 *w = x2 - x1;
1087}
1088
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001089/* Configure dispc for partial update. Return possibly modified update
1090 * area */
1091void dss_setup_partial_planes(struct omap_dss_device *dssdev,
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001092 u16 *xi, u16 *yi, u16 *wi, u16 *hi, bool enlarge_update_area)
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001093{
1094 struct overlay_cache_data *oc;
1095 struct manager_cache_data *mc;
Nishant Kamat4df9d102011-06-20 20:39:28 +05301096 struct omap_overlay_info *oi;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301097 const int num_ovls = dss_feat_get_num_ovls();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001098 struct omap_overlay_manager *mgr;
1099 int i;
1100 u16 x, y, w, h;
1101 unsigned long flags;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001102 bool area_changed;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001103
1104 x = *xi;
1105 y = *yi;
1106 w = *wi;
1107 h = *hi;
1108
1109 DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n",
1110 *xi, *yi, *wi, *hi);
1111
1112 mgr = dssdev->manager;
1113
1114 if (!mgr) {
1115 DSSDBG("no manager\n");
1116 return;
1117 }
1118
Tomi Valkeinenf49a9512010-03-26 16:26:37 +02001119 make_even(&x, &w);
1120
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001121 spin_lock_irqsave(&dss_cache.lock, flags);
1122
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001123 /*
1124 * Execute the outer loop until the inner loop has completed
1125 * once without increasing the update area. This will ensure that
1126 * all scaled overlays end up completely within the update area.
1127 */
1128 do {
1129 area_changed = false;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001130
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001131 /* We need to show the whole overlay if it is scaled. So look
1132 * for those, and make the update area larger if found.
1133 * Also mark the overlay cache dirty */
1134 for (i = 0; i < num_ovls; ++i) {
1135 unsigned x1, y1, x2, y2;
1136 unsigned outw, outh;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001137
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001138 oc = &dss_cache.overlay_cache[i];
Nishant Kamat4df9d102011-06-20 20:39:28 +05301139 oi = &oc->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001140
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001141 if (oc->channel != mgr->id)
1142 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001143
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001144 oc->dirty = true;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001145
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001146 if (!enlarge_update_area)
1147 continue;
1148
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001149 if (!oc->enabled)
1150 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001151
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001152 if (!dispc_is_overlay_scaled(oc))
1153 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001154
Nishant Kamat4df9d102011-06-20 20:39:28 +05301155 outw = oi->out_width == 0 ?
1156 oi->width : oi->out_width;
1157 outh = oi->out_height == 0 ?
1158 oi->height : oi->out_height;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001159
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001160 /* is the overlay outside the update region? */
1161 if (!rectangle_intersects(x, y, w, h,
Nishant Kamat4df9d102011-06-20 20:39:28 +05301162 oi->pos_x, oi->pos_y,
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001163 outw, outh))
1164 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001165
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001166 /* if the overlay totally inside the update region? */
Nishant Kamat4df9d102011-06-20 20:39:28 +05301167 if (rectangle_subset(oi->pos_x, oi->pos_y, outw, outh,
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001168 x, y, w, h))
1169 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001170
Nishant Kamat4df9d102011-06-20 20:39:28 +05301171 if (x > oi->pos_x)
1172 x1 = oi->pos_x;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001173 else
1174 x1 = x;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001175
Nishant Kamat4df9d102011-06-20 20:39:28 +05301176 if (y > oi->pos_y)
1177 y1 = oi->pos_y;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001178 else
1179 y1 = y;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001180
Nishant Kamat4df9d102011-06-20 20:39:28 +05301181 if ((x + w) < (oi->pos_x + outw))
1182 x2 = oi->pos_x + outw;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001183 else
1184 x2 = x + w;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001185
Nishant Kamat4df9d102011-06-20 20:39:28 +05301186 if ((y + h) < (oi->pos_y + outh))
1187 y2 = oi->pos_y + outh;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001188 else
1189 y2 = y + h;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001190
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001191 x = x1;
1192 y = y1;
1193 w = x2 - x1;
1194 h = y2 - y1;
Tomi Valkeinenf49a9512010-03-26 16:26:37 +02001195
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001196 make_even(&x, &w);
1197
1198 DSSDBG("changing upd area due to ovl(%d) "
1199 "scaling %d,%d %dx%d\n",
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001200 i, x, y, w, h);
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001201
1202 area_changed = true;
1203 }
1204 } while (area_changed);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001205
1206 mc = &dss_cache.manager_cache[mgr->id];
1207 mc->do_manual_update = true;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001208 mc->enlarge_update_area = enlarge_update_area;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001209 mc->x = x;
1210 mc->y = y;
1211 mc->w = w;
1212 mc->h = h;
1213
1214 configure_dispc();
1215
1216 mc->do_manual_update = false;
1217
1218 spin_unlock_irqrestore(&dss_cache.lock, flags);
1219
1220 *xi = x;
1221 *yi = y;
1222 *wi = w;
1223 *hi = h;
1224}
1225
1226void dss_start_update(struct omap_dss_device *dssdev)
1227{
1228 struct manager_cache_data *mc;
1229 struct overlay_cache_data *oc;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301230 const int num_ovls = dss_feat_get_num_ovls();
1231 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001232 struct omap_overlay_manager *mgr;
1233 int i;
1234
1235 mgr = dssdev->manager;
1236
1237 for (i = 0; i < num_ovls; ++i) {
1238 oc = &dss_cache.overlay_cache[i];
1239 if (oc->channel != mgr->id)
1240 continue;
1241
1242 oc->shadow_dirty = false;
1243 }
1244
1245 for (i = 0; i < num_mgrs; ++i) {
1246 mc = &dss_cache.manager_cache[i];
1247 if (mgr->id != i)
1248 continue;
1249
1250 mc->shadow_dirty = false;
1251 }
1252
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001253 dssdev->manager->enable(dssdev->manager);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001254}
1255
1256static void dss_apply_irq_handler(void *data, u32 mask)
1257{
1258 struct manager_cache_data *mc;
1259 struct overlay_cache_data *oc;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301260 const int num_ovls = dss_feat_get_num_ovls();
1261 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001262 int i, r;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301263 bool mgr_busy[MAX_DSS_MANAGERS];
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001264 u32 irq_mask;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001265
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001266 for (i = 0; i < num_mgrs; i++)
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001267 mgr_busy[i] = dispc_mgr_go_busy(i);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001268
1269 spin_lock(&dss_cache.lock);
1270
1271 for (i = 0; i < num_ovls; ++i) {
1272 oc = &dss_cache.overlay_cache[i];
1273 if (!mgr_busy[oc->channel])
1274 oc->shadow_dirty = false;
1275 }
1276
1277 for (i = 0; i < num_mgrs; ++i) {
1278 mc = &dss_cache.manager_cache[i];
1279 if (!mgr_busy[i])
1280 mc->shadow_dirty = false;
1281 }
1282
1283 r = configure_dispc();
1284 if (r == 1)
1285 goto end;
1286
1287 /* re-read busy flags */
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001288 for (i = 0; i < num_mgrs; i++)
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001289 mgr_busy[i] = dispc_mgr_go_busy(i);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001290
1291 /* keep running as long as there are busy managers, so that
1292 * we can collect overlay-applied information */
1293 for (i = 0; i < num_mgrs; ++i) {
1294 if (mgr_busy[i])
1295 goto end;
1296 }
1297
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001298 irq_mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
1299 DISPC_IRQ_EVSYNC_EVEN;
1300 if (dss_has_feature(FEAT_MGR_LCD2))
1301 irq_mask |= DISPC_IRQ_VSYNC2;
1302
1303 omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, irq_mask);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001304 dss_cache.irq_enabled = false;
1305
1306end:
1307 spin_unlock(&dss_cache.lock);
1308}
1309
1310static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
1311{
1312 struct overlay_cache_data *oc;
1313 struct manager_cache_data *mc;
1314 int i;
1315 struct omap_overlay *ovl;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001316 unsigned long flags;
1317 int r;
1318
1319 DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
1320
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001321 r = dispc_runtime_get();
1322 if (r)
1323 return r;
1324
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001325 spin_lock_irqsave(&dss_cache.lock, flags);
1326
1327 /* Configure overlays */
1328 for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
1329 struct omap_dss_device *dssdev;
1330
1331 ovl = omap_dss_get_overlay(i);
1332
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001333 oc = &dss_cache.overlay_cache[ovl->id];
1334
Tomi Valkeinen8fa80312011-08-16 12:56:19 +03001335 if (ovl->manager_changed) {
1336 ovl->manager_changed = false;
1337 ovl->info_dirty = true;
1338 }
1339
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001340 if (!overlay_enabled(ovl)) {
1341 if (oc->enabled) {
1342 oc->enabled = false;
1343 oc->dirty = true;
1344 }
1345 continue;
1346 }
1347
Tomi Valkeinen714ad3a2011-10-27 13:11:31 +03001348 if (!ovl->info_dirty)
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001349 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001350
1351 dssdev = ovl->manager->device;
1352
1353 if (dss_check_overlay(ovl, dssdev)) {
1354 if (oc->enabled) {
1355 oc->enabled = false;
1356 oc->dirty = true;
1357 }
1358 continue;
1359 }
1360
1361 ovl->info_dirty = false;
1362 oc->dirty = true;
Nishant Kamat4df9d102011-06-20 20:39:28 +05301363 oc->info = ovl->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001364
1365 oc->replication =
1366 dss_use_replication(dssdev, ovl->info.color_mode);
1367
1368 oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC;
1369
1370 oc->channel = ovl->manager->id;
1371
1372 oc->enabled = true;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001373 }
1374
1375 /* Configure managers */
1376 list_for_each_entry(mgr, &manager_list, list) {
1377 struct omap_dss_device *dssdev;
1378
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001379 mc = &dss_cache.manager_cache[mgr->id];
1380
1381 if (mgr->device_changed) {
1382 mgr->device_changed = false;
1383 mgr->info_dirty = true;
1384 }
1385
1386 if (!mgr->info_dirty)
1387 continue;
1388
1389 if (!mgr->device)
1390 continue;
1391
1392 dssdev = mgr->device;
1393
1394 mgr->info_dirty = false;
1395 mc->dirty = true;
Nishant Kamat4df9d102011-06-20 20:39:28 +05301396 mc->info = mgr->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001397
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001398 mc->manual_update =
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +03001399 dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001400 }
1401
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001402 /* Configure overlay fifos */
1403 for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
1404 struct omap_dss_device *dssdev;
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001405 u32 size, burst_size;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001406
1407 ovl = omap_dss_get_overlay(i);
1408
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001409 oc = &dss_cache.overlay_cache[ovl->id];
1410
1411 if (!oc->enabled)
1412 continue;
1413
1414 dssdev = ovl->manager->device;
1415
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +03001416 size = dispc_ovl_get_fifo_size(ovl->id);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001417
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +03001418 burst_size = dispc_ovl_get_burst_size(ovl->id);
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001419
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001420 switch (dssdev->type) {
1421 case OMAP_DISPLAY_TYPE_DPI:
1422 case OMAP_DISPLAY_TYPE_DBI:
1423 case OMAP_DISPLAY_TYPE_SDI:
1424 case OMAP_DISPLAY_TYPE_VENC:
Mythri P Kb1196012011-03-08 17:15:54 +05301425 case OMAP_DISPLAY_TYPE_HDMI:
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001426 default_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001427 burst_size, &oc->fifo_low,
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001428 &oc->fifo_high);
1429 break;
1430#ifdef CONFIG_OMAP2_DSS_DSI
1431 case OMAP_DISPLAY_TYPE_DSI:
1432 dsi_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001433 burst_size, &oc->fifo_low,
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001434 &oc->fifo_high);
1435 break;
1436#endif
1437 default:
1438 BUG();
1439 }
1440 }
1441
1442 r = 0;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001443 if (!dss_cache.irq_enabled) {
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001444 u32 mask;
1445
1446 mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
1447 DISPC_IRQ_EVSYNC_EVEN;
1448 if (dss_has_feature(FEAT_MGR_LCD2))
1449 mask |= DISPC_IRQ_VSYNC2;
1450
1451 r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001452 dss_cache.irq_enabled = true;
1453 }
1454 configure_dispc();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001455
1456 spin_unlock_irqrestore(&dss_cache.lock, flags);
1457
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001458 dispc_runtime_put();
1459
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001460 return r;
1461}
1462
1463static int dss_check_manager(struct omap_overlay_manager *mgr)
1464{
Archit Taneja11354dd2011-09-26 11:47:29 +05301465 if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) {
1466 /*
1467 * OMAP3 supports only graphics source transparency color key
1468 * and alpha blending simultaneously. See TRM 15.4.2.4.2.2
1469 * Alpha Mode
1470 */
1471 if (mgr->info.partial_alpha_enabled && mgr->info.trans_enabled
1472 && mgr->info.trans_key_type !=
1473 OMAP_DSS_COLOR_KEY_GFX_DST)
1474 return -EINVAL;
1475 }
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001476
1477 return 0;
1478}
1479
1480static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr,
1481 struct omap_overlay_manager_info *info)
1482{
1483 int r;
1484 struct omap_overlay_manager_info old_info;
1485
1486 old_info = mgr->info;
1487 mgr->info = *info;
1488
1489 r = dss_check_manager(mgr);
1490 if (r) {
1491 mgr->info = old_info;
1492 return r;
1493 }
1494
1495 mgr->info_dirty = true;
1496
1497 return 0;
1498}
1499
1500static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr,
1501 struct omap_overlay_manager_info *info)
1502{
1503 *info = mgr->info;
1504}
1505
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001506static int dss_mgr_enable(struct omap_overlay_manager *mgr)
1507{
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001508 dispc_mgr_enable(mgr->id, 1);
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001509 return 0;
1510}
1511
1512static int dss_mgr_disable(struct omap_overlay_manager *mgr)
1513{
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001514 dispc_mgr_enable(mgr->id, 0);
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001515 return 0;
1516}
1517
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001518static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager)
1519{
1520 ++num_managers;
1521 list_add_tail(&manager->list, &manager_list);
1522}
1523
1524int dss_init_overlay_managers(struct platform_device *pdev)
1525{
1526 int i, r;
1527
1528 spin_lock_init(&dss_cache.lock);
1529
1530 INIT_LIST_HEAD(&manager_list);
1531
1532 num_managers = 0;
1533
Archit Tanejaa0acb552010-09-15 19:20:00 +05301534 for (i = 0; i < dss_feat_get_num_mgrs(); ++i) {
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001535 struct omap_overlay_manager *mgr;
1536 mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
1537
1538 BUG_ON(mgr == NULL);
1539
1540 switch (i) {
1541 case 0:
1542 mgr->name = "lcd";
1543 mgr->id = OMAP_DSS_CHANNEL_LCD;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001544 break;
1545 case 1:
1546 mgr->name = "tv";
1547 mgr->id = OMAP_DSS_CHANNEL_DIGIT;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001548 break;
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001549 case 2:
1550 mgr->name = "lcd2";
1551 mgr->id = OMAP_DSS_CHANNEL_LCD2;
1552 break;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001553 }
1554
1555 mgr->set_device = &omap_dss_set_device;
1556 mgr->unset_device = &omap_dss_unset_device;
1557 mgr->apply = &omap_dss_mgr_apply;
1558 mgr->set_manager_info = &omap_dss_mgr_set_info;
1559 mgr->get_manager_info = &omap_dss_mgr_get_info;
1560 mgr->wait_for_go = &dss_mgr_wait_for_go;
Tomi Valkeinen3f71cbe2010-01-08 17:06:04 +02001561 mgr->wait_for_vsync = &dss_mgr_wait_for_vsync;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001562
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001563 mgr->enable = &dss_mgr_enable;
1564 mgr->disable = &dss_mgr_disable;
1565
Tomi Valkeinen4a9e78a2011-08-15 11:22:21 +03001566 mgr->caps = 0;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301567 mgr->supported_displays =
1568 dss_feat_get_supported_displays(mgr->id);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001569
1570 dss_overlay_setup_dispc_manager(mgr);
1571
1572 omap_dss_add_overlay_manager(mgr);
1573
1574 r = kobject_init_and_add(&mgr->kobj, &manager_ktype,
1575 &pdev->dev.kobj, "manager%d", i);
1576
1577 if (r) {
1578 DSSERR("failed to create sysfs file\n");
1579 continue;
1580 }
1581 }
1582
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001583 return 0;
1584}
1585
1586void dss_uninit_overlay_managers(struct platform_device *pdev)
1587{
1588 struct omap_overlay_manager *mgr;
1589
1590 while (!list_empty(&manager_list)) {
1591 mgr = list_first_entry(&manager_list,
1592 struct omap_overlay_manager, list);
1593 list_del(&mgr->list);
1594 kobject_del(&mgr->kobj);
1595 kobject_put(&mgr->kobj);
1596 kfree(mgr);
1597 }
1598
1599 num_managers = 0;
1600}
1601
1602int omap_dss_get_num_overlay_managers(void)
1603{
1604 return num_managers;
1605}
1606EXPORT_SYMBOL(omap_dss_get_num_overlay_managers);
1607
1608struct omap_overlay_manager *omap_dss_get_overlay_manager(int num)
1609{
1610 int i = 0;
1611 struct omap_overlay_manager *mgr;
1612
1613 list_for_each_entry(mgr, &manager_list, list) {
1614 if (i++ == num)
1615 return mgr;
1616 }
1617
1618 return NULL;
1619}
1620EXPORT_SYMBOL(omap_dss_get_overlay_manager);
1621