blob: eb7833ecaf32b7f719b5341ffb6abe8acbbb43b8 [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{
252 return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.alpha_enabled);
253}
254
255static ssize_t manager_alpha_blending_enabled_store(
256 struct omap_overlay_manager *mgr,
257 const char *buf, size_t size)
258{
259 struct omap_overlay_manager_info info;
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300260 bool enable;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300261 int r;
262
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300263 r = strtobool(buf, &enable);
264 if (r)
265 return r;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300266
267 mgr->get_manager_info(mgr, &info);
268
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300269 info.alpha_enabled = enable;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300270
271 r = mgr->set_manager_info(mgr, &info);
272 if (r)
273 return r;
274
275 r = mgr->apply(mgr);
276 if (r)
277 return r;
278
279 return size;
280}
281
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300282static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr,
283 char *buf)
284{
285 return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.cpr_enable);
286}
287
288static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr,
289 const char *buf, size_t size)
290{
291 struct omap_overlay_manager_info info;
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300292 int r;
293 bool enable;
294
295 if (!dss_has_feature(FEAT_CPR))
296 return -ENODEV;
297
Tomi Valkeinene3a26ae2011-08-15 15:55:55 +0300298 r = strtobool(buf, &enable);
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300299 if (r)
300 return r;
301
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300302 mgr->get_manager_info(mgr, &info);
303
304 if (info.cpr_enable == enable)
305 return size;
306
307 info.cpr_enable = enable;
308
309 r = mgr->set_manager_info(mgr, &info);
310 if (r)
311 return r;
312
313 r = mgr->apply(mgr);
314 if (r)
315 return r;
316
317 return size;
318}
319
320static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr,
321 char *buf)
322{
323 struct omap_overlay_manager_info info;
324
325 mgr->get_manager_info(mgr, &info);
326
327 return snprintf(buf, PAGE_SIZE,
328 "%d %d %d %d %d %d %d %d %d\n",
329 info.cpr_coefs.rr,
330 info.cpr_coefs.rg,
331 info.cpr_coefs.rb,
332 info.cpr_coefs.gr,
333 info.cpr_coefs.gg,
334 info.cpr_coefs.gb,
335 info.cpr_coefs.br,
336 info.cpr_coefs.bg,
337 info.cpr_coefs.bb);
338}
339
340static ssize_t manager_cpr_coef_store(struct omap_overlay_manager *mgr,
341 const char *buf, size_t size)
342{
343 struct omap_overlay_manager_info info;
344 struct omap_dss_cpr_coefs coefs;
345 int r, i;
346 s16 *arr;
347
348 if (!dss_has_feature(FEAT_CPR))
349 return -ENODEV;
350
351 if (sscanf(buf, "%hd %hd %hd %hd %hd %hd %hd %hd %hd",
352 &coefs.rr, &coefs.rg, &coefs.rb,
353 &coefs.gr, &coefs.gg, &coefs.gb,
354 &coefs.br, &coefs.bg, &coefs.bb) != 9)
355 return -EINVAL;
356
357 arr = (s16[]){ coefs.rr, coefs.rg, coefs.rb,
358 coefs.gr, coefs.gg, coefs.gb,
359 coefs.br, coefs.bg, coefs.bb };
360
361 for (i = 0; i < 9; ++i) {
362 if (arr[i] < -512 || arr[i] > 511)
363 return -EINVAL;
364 }
365
366 mgr->get_manager_info(mgr, &info);
367
368 info.cpr_coefs = coefs;
369
370 r = mgr->set_manager_info(mgr, &info);
371 if (r)
372 return r;
373
374 r = mgr->apply(mgr);
375 if (r)
376 return r;
377
378 return size;
379}
380
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300381struct manager_attribute {
382 struct attribute attr;
383 ssize_t (*show)(struct omap_overlay_manager *, char *);
384 ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t);
385};
386
387#define MANAGER_ATTR(_name, _mode, _show, _store) \
388 struct manager_attribute manager_attr_##_name = \
389 __ATTR(_name, _mode, _show, _store)
390
391static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL);
392static MANAGER_ATTR(display, S_IRUGO|S_IWUSR,
393 manager_display_show, manager_display_store);
394static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR,
395 manager_default_color_show, manager_default_color_store);
396static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR,
397 manager_trans_key_type_show, manager_trans_key_type_store);
398static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR,
399 manager_trans_key_value_show, manager_trans_key_value_store);
400static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR,
401 manager_trans_key_enabled_show,
402 manager_trans_key_enabled_store);
403static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR,
404 manager_alpha_blending_enabled_show,
405 manager_alpha_blending_enabled_store);
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300406static MANAGER_ATTR(cpr_enable, S_IRUGO|S_IWUSR,
407 manager_cpr_enable_show,
408 manager_cpr_enable_store);
409static MANAGER_ATTR(cpr_coef, S_IRUGO|S_IWUSR,
410 manager_cpr_coef_show,
411 manager_cpr_coef_store);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300412
413
414static struct attribute *manager_sysfs_attrs[] = {
415 &manager_attr_name.attr,
416 &manager_attr_display.attr,
417 &manager_attr_default_color.attr,
418 &manager_attr_trans_key_type.attr,
419 &manager_attr_trans_key_value.attr,
420 &manager_attr_trans_key_enabled.attr,
421 &manager_attr_alpha_blending_enabled.attr,
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300422 &manager_attr_cpr_enable.attr,
423 &manager_attr_cpr_coef.attr,
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300424 NULL
425};
426
427static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr,
428 char *buf)
429{
430 struct omap_overlay_manager *manager;
431 struct manager_attribute *manager_attr;
432
433 manager = container_of(kobj, struct omap_overlay_manager, kobj);
434 manager_attr = container_of(attr, struct manager_attribute, attr);
435
436 if (!manager_attr->show)
437 return -ENOENT;
438
439 return manager_attr->show(manager, buf);
440}
441
442static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr,
443 const char *buf, size_t size)
444{
445 struct omap_overlay_manager *manager;
446 struct manager_attribute *manager_attr;
447
448 manager = container_of(kobj, struct omap_overlay_manager, kobj);
449 manager_attr = container_of(attr, struct manager_attribute, attr);
450
451 if (!manager_attr->store)
452 return -ENOENT;
453
454 return manager_attr->store(manager, buf, size);
455}
456
Emese Revfy52cf25d2010-01-19 02:58:23 +0100457static const struct sysfs_ops manager_sysfs_ops = {
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300458 .show = manager_attr_show,
459 .store = manager_attr_store,
460};
461
462static struct kobj_type manager_ktype = {
463 .sysfs_ops = &manager_sysfs_ops,
464 .default_attrs = manager_sysfs_attrs,
465};
466
467/*
468 * We have 4 levels of cache for the dispc settings. First two are in SW and
469 * the latter two in HW.
470 *
471 * +--------------------+
472 * |overlay/manager_info|
473 * +--------------------+
474 * v
475 * apply()
476 * v
477 * +--------------------+
478 * | dss_cache |
479 * +--------------------+
480 * v
481 * configure()
482 * v
483 * +--------------------+
484 * | shadow registers |
485 * +--------------------+
486 * v
487 * VFP or lcd/digit_enable
488 * v
489 * +--------------------+
490 * | registers |
491 * +--------------------+
492 */
493
494struct overlay_cache_data {
495 /* If true, cache changed, but not written to shadow registers. Set
496 * in apply(), cleared when registers written. */
497 bool dirty;
498 /* If true, shadow registers contain changed values not yet in real
499 * registers. Set when writing to shadow registers, cleared at
500 * VSYNC/EVSYNC */
501 bool shadow_dirty;
502
503 bool enabled;
504
Nishant Kamat4df9d102011-06-20 20:39:28 +0530505 struct omap_overlay_info info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300506
507 enum omap_channel channel;
508 bool replication;
509 bool ilace;
510
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300511 u32 fifo_low;
512 u32 fifo_high;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300513};
514
515struct manager_cache_data {
516 /* If true, cache changed, but not written to shadow registers. Set
517 * in apply(), cleared when registers written. */
518 bool dirty;
519 /* If true, shadow registers contain changed values not yet in real
520 * registers. Set when writing to shadow registers, cleared at
521 * VSYNC/EVSYNC */
522 bool shadow_dirty;
523
Nishant Kamat4df9d102011-06-20 20:39:28 +0530524 struct omap_overlay_manager_info info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300525
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300526 bool manual_update;
527 bool do_manual_update;
528
529 /* manual update region */
530 u16 x, y, w, h;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300531
532 /* enlarge the update area if the update area contains scaled
533 * overlays */
534 bool enlarge_update_area;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300535};
536
537static struct {
538 spinlock_t lock;
Archit Tanejaa0acb552010-09-15 19:20:00 +0530539 struct overlay_cache_data overlay_cache[MAX_DSS_OVERLAYS];
540 struct manager_cache_data manager_cache[MAX_DSS_MANAGERS];
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300541
542 bool irq_enabled;
543} dss_cache;
544
545
546
547static int omap_dss_set_device(struct omap_overlay_manager *mgr,
548 struct omap_dss_device *dssdev)
549{
550 int i;
551 int r;
552
553 if (dssdev->manager) {
554 DSSERR("display '%s' already has a manager '%s'\n",
555 dssdev->name, dssdev->manager->name);
556 return -EINVAL;
557 }
558
559 if ((mgr->supported_displays & dssdev->type) == 0) {
560 DSSERR("display '%s' does not support manager '%s'\n",
561 dssdev->name, mgr->name);
562 return -EINVAL;
563 }
564
565 for (i = 0; i < mgr->num_overlays; i++) {
566 struct omap_overlay *ovl = mgr->overlays[i];
567
568 if (ovl->manager != mgr || !ovl->info.enabled)
569 continue;
570
571 r = dss_check_overlay(ovl, dssdev);
572 if (r)
573 return r;
574 }
575
576 dssdev->manager = mgr;
577 mgr->device = dssdev;
578 mgr->device_changed = true;
579
580 return 0;
581}
582
583static int omap_dss_unset_device(struct omap_overlay_manager *mgr)
584{
585 if (!mgr->device) {
586 DSSERR("failed to unset display, display not set.\n");
587 return -EINVAL;
588 }
589
Daniel Morsing0f770b42011-08-03 22:10:51 +0200590 /*
591 * Don't allow currently enabled displays to have the overlay manager
592 * pulled out from underneath them
593 */
594 if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED)
595 return -EINVAL;
596
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300597 mgr->device->manager = NULL;
598 mgr->device = NULL;
599 mgr->device_changed = true;
600
601 return 0;
602}
603
Tomi Valkeinen3f71cbe2010-01-08 17:06:04 +0200604static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
605{
606 unsigned long timeout = msecs_to_jiffies(500);
607 u32 irq;
608
Sumit Semwal18faa1b2010-12-02 11:27:14 +0000609 if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) {
Tomi Valkeinen3f71cbe2010-01-08 17:06:04 +0200610 irq = DISPC_IRQ_EVSYNC_ODD;
Mythri P Kb1196012011-03-08 17:15:54 +0530611 } else if (mgr->device->type == OMAP_DISPLAY_TYPE_HDMI) {
612 irq = DISPC_IRQ_EVSYNC_EVEN;
Sumit Semwal18faa1b2010-12-02 11:27:14 +0000613 } else {
614 if (mgr->id == OMAP_DSS_CHANNEL_LCD)
615 irq = DISPC_IRQ_VSYNC;
616 else
617 irq = DISPC_IRQ_VSYNC2;
618 }
Tomi Valkeinen3f71cbe2010-01-08 17:06:04 +0200619 return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
620}
621
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300622static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
623{
624 unsigned long timeout = msecs_to_jiffies(500);
625 struct manager_cache_data *mc;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300626 u32 irq;
627 int r;
628 int i;
Tomi Valkeinen446f7bf2010-01-11 16:12:31 +0200629 struct omap_dss_device *dssdev = mgr->device;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300630
Ville Syrjäläa74b2602010-03-04 16:03:56 +0200631 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300632 return 0;
633
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300634 if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
635 return 0;
636
Mythri P Kb1196012011-03-08 17:15:54 +0530637 if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
638 || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300639 irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300640 } else {
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300641 irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
642 DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300643 }
644
645 mc = &dss_cache.manager_cache[mgr->id];
646 i = 0;
647 while (1) {
648 unsigned long flags;
649 bool shadow_dirty, dirty;
650
651 spin_lock_irqsave(&dss_cache.lock, flags);
652 dirty = mc->dirty;
653 shadow_dirty = mc->shadow_dirty;
654 spin_unlock_irqrestore(&dss_cache.lock, flags);
655
656 if (!dirty && !shadow_dirty) {
657 r = 0;
658 break;
659 }
660
661 /* 4 iterations is the worst case:
662 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
663 * 2 - first VSYNC, dirty = true
664 * 3 - dirty = false, shadow_dirty = true
665 * 4 - shadow_dirty = false */
666 if (i++ == 3) {
667 DSSERR("mgr(%d)->wait_for_go() not finishing\n",
668 mgr->id);
669 r = 0;
670 break;
671 }
672
673 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
674 if (r == -ERESTARTSYS)
675 break;
676
677 if (r) {
678 DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
679 break;
680 }
681 }
682
683 return r;
684}
685
686int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
687{
688 unsigned long timeout = msecs_to_jiffies(500);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300689 struct overlay_cache_data *oc;
690 struct omap_dss_device *dssdev;
691 u32 irq;
692 int r;
693 int i;
694
Ville Syrjäläa74b2602010-03-04 16:03:56 +0200695 if (!ovl->manager)
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300696 return 0;
697
698 dssdev = ovl->manager->device;
699
Ville Syrjäläa74b2602010-03-04 16:03:56 +0200700 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
701 return 0;
702
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300703 if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
704 return 0;
705
Mythri P Kb1196012011-03-08 17:15:54 +0530706 if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
707 || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300708 irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300709 } else {
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300710 irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ?
711 DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300712 }
713
714 oc = &dss_cache.overlay_cache[ovl->id];
715 i = 0;
716 while (1) {
717 unsigned long flags;
718 bool shadow_dirty, dirty;
719
720 spin_lock_irqsave(&dss_cache.lock, flags);
721 dirty = oc->dirty;
722 shadow_dirty = oc->shadow_dirty;
723 spin_unlock_irqrestore(&dss_cache.lock, flags);
724
725 if (!dirty && !shadow_dirty) {
726 r = 0;
727 break;
728 }
729
730 /* 4 iterations is the worst case:
731 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
732 * 2 - first VSYNC, dirty = true
733 * 3 - dirty = false, shadow_dirty = true
734 * 4 - shadow_dirty = false */
735 if (i++ == 3) {
736 DSSERR("ovl(%d)->wait_for_go() not finishing\n",
737 ovl->id);
738 r = 0;
739 break;
740 }
741
742 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
743 if (r == -ERESTARTSYS)
744 break;
745
746 if (r) {
747 DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id);
748 break;
749 }
750 }
751
752 return r;
753}
754
755static int overlay_enabled(struct omap_overlay *ovl)
756{
757 return ovl->info.enabled && ovl->manager && ovl->manager->device;
758}
759
760/* Is rect1 a subset of rect2? */
761static bool rectangle_subset(int x1, int y1, int w1, int h1,
762 int x2, int y2, int w2, int h2)
763{
764 if (x1 < x2 || y1 < y2)
765 return false;
766
767 if (x1 + w1 > x2 + w2)
768 return false;
769
770 if (y1 + h1 > y2 + h2)
771 return false;
772
773 return true;
774}
775
776/* Do rect1 and rect2 overlap? */
777static bool rectangle_intersects(int x1, int y1, int w1, int h1,
778 int x2, int y2, int w2, int h2)
779{
780 if (x1 >= x2 + w2)
781 return false;
782
783 if (x2 >= x1 + w1)
784 return false;
785
786 if (y1 >= y2 + h2)
787 return false;
788
789 if (y2 >= y1 + h1)
790 return false;
791
792 return true;
793}
794
795static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc)
796{
Nishant Kamat4df9d102011-06-20 20:39:28 +0530797 struct omap_overlay_info *oi = &oc->info;
798
799 if (oi->out_width != 0 && oi->width != oi->out_width)
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300800 return true;
801
Nishant Kamat4df9d102011-06-20 20:39:28 +0530802 if (oi->out_height != 0 && oi->height != oi->out_height)
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300803 return true;
804
805 return false;
806}
807
808static int configure_overlay(enum omap_plane plane)
809{
810 struct overlay_cache_data *c;
811 struct manager_cache_data *mc;
Nishant Kamat4df9d102011-06-20 20:39:28 +0530812 struct omap_overlay_info *oi;
813 struct omap_overlay_manager_info *mi;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300814 u16 outw, outh;
815 u16 x, y, w, h;
816 u32 paddr;
817 int r;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300818 u16 orig_w, orig_h, orig_outw, orig_outh;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300819
820 DSSDBGF("%d", plane);
821
822 c = &dss_cache.overlay_cache[plane];
Nishant Kamat4df9d102011-06-20 20:39:28 +0530823 oi = &c->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300824
825 if (!c->enabled) {
826 dispc_enable_plane(plane, 0);
827 return 0;
828 }
829
830 mc = &dss_cache.manager_cache[c->channel];
Nishant Kamat4df9d102011-06-20 20:39:28 +0530831 mi = &mc->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300832
Nishant Kamat4df9d102011-06-20 20:39:28 +0530833 x = oi->pos_x;
834 y = oi->pos_y;
835 w = oi->width;
836 h = oi->height;
837 outw = oi->out_width == 0 ? oi->width : oi->out_width;
838 outh = oi->out_height == 0 ? oi->height : oi->out_height;
839 paddr = oi->paddr;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300840
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300841 orig_w = w;
842 orig_h = h;
843 orig_outw = outw;
844 orig_outh = outh;
845
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +0300846 if (mc->manual_update && mc->do_manual_update) {
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300847 unsigned bpp;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300848 unsigned scale_x_m = w, scale_x_d = outw;
849 unsigned scale_y_m = h, scale_y_d = outh;
850
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300851 /* If the overlay is outside the update region, disable it */
852 if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h,
853 x, y, outw, outh)) {
854 dispc_enable_plane(plane, 0);
855 return 0;
856 }
857
Nishant Kamat4df9d102011-06-20 20:39:28 +0530858 switch (oi->color_mode) {
Amber Jainf20e4222011-05-19 19:47:50 +0530859 case OMAP_DSS_COLOR_NV12:
860 bpp = 8;
861 break;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300862 case OMAP_DSS_COLOR_RGB16:
863 case OMAP_DSS_COLOR_ARGB16:
864 case OMAP_DSS_COLOR_YUV2:
865 case OMAP_DSS_COLOR_UYVY:
Amber Jainf20e4222011-05-19 19:47:50 +0530866 case OMAP_DSS_COLOR_RGBA16:
867 case OMAP_DSS_COLOR_RGBX16:
868 case OMAP_DSS_COLOR_ARGB16_1555:
869 case OMAP_DSS_COLOR_XRGB16_1555:
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300870 bpp = 16;
871 break;
872
873 case OMAP_DSS_COLOR_RGB24P:
874 bpp = 24;
875 break;
876
877 case OMAP_DSS_COLOR_RGB24U:
878 case OMAP_DSS_COLOR_ARGB32:
879 case OMAP_DSS_COLOR_RGBA32:
880 case OMAP_DSS_COLOR_RGBX32:
881 bpp = 32;
882 break;
883
884 default:
885 BUG();
886 }
887
Nishant Kamat4df9d102011-06-20 20:39:28 +0530888 if (mc->x > oi->pos_x) {
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300889 x = 0;
Nishant Kamat4df9d102011-06-20 20:39:28 +0530890 outw -= (mc->x - oi->pos_x);
891 paddr += (mc->x - oi->pos_x) *
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300892 scale_x_m / scale_x_d * bpp / 8;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300893 } else {
Nishant Kamat4df9d102011-06-20 20:39:28 +0530894 x = oi->pos_x - mc->x;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300895 }
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300896
Nishant Kamat4df9d102011-06-20 20:39:28 +0530897 if (mc->y > oi->pos_y) {
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300898 y = 0;
Nishant Kamat4df9d102011-06-20 20:39:28 +0530899 outh -= (mc->y - oi->pos_y);
900 paddr += (mc->y - oi->pos_y) *
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300901 scale_y_m / scale_y_d *
Nishant Kamat4df9d102011-06-20 20:39:28 +0530902 oi->screen_width * bpp / 8;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300903 } else {
Nishant Kamat4df9d102011-06-20 20:39:28 +0530904 y = oi->pos_y - mc->y;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +0300905 }
906
907 if (mc->w < (x + outw))
908 outw -= (x + outw) - (mc->w);
909
910 if (mc->h < (y + outh))
911 outh -= (y + outh) - (mc->h);
912
913 w = w * outw / orig_outw;
914 h = h * outh / orig_outh;
Tomi Valkeinenf55fdcf2010-06-03 16:27:46 +0300915
916 /* YUV mode overlay's input width has to be even and the
917 * algorithm above may adjust the width to be odd.
918 *
919 * Here we adjust the width if needed, preferring to increase
920 * the width if the original width was bigger.
921 */
922 if ((w & 1) &&
Nishant Kamat4df9d102011-06-20 20:39:28 +0530923 (oi->color_mode == OMAP_DSS_COLOR_YUV2 ||
924 oi->color_mode == OMAP_DSS_COLOR_UYVY)) {
Tomi Valkeinenf55fdcf2010-06-03 16:27:46 +0300925 if (orig_w > w)
926 w += 1;
927 else
928 w -= 1;
929 }
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300930 }
931
932 r = dispc_setup_plane(plane,
933 paddr,
Nishant Kamat4df9d102011-06-20 20:39:28 +0530934 oi->screen_width,
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300935 x, y,
936 w, h,
937 outw, outh,
Nishant Kamat4df9d102011-06-20 20:39:28 +0530938 oi->color_mode,
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300939 c->ilace,
Nishant Kamat4df9d102011-06-20 20:39:28 +0530940 oi->rotation_type,
941 oi->rotation,
942 oi->mirror,
943 oi->global_alpha,
944 oi->pre_mult_alpha,
Amber Jain0d66cbb2011-05-19 19:47:54 +0530945 c->channel,
Nishant Kamat4df9d102011-06-20 20:39:28 +0530946 oi->p_uv_addr);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300947
948 if (r) {
949 /* this shouldn't happen */
950 DSSERR("dispc_setup_plane failed for ovl %d\n", plane);
951 dispc_enable_plane(plane, 0);
952 return r;
953 }
954
955 dispc_enable_replication(plane, c->replication);
956
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +0300957 dispc_set_fifo_threshold(plane, c->fifo_low, c->fifo_high);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300958
959 dispc_enable_plane(plane, 1);
960
961 return 0;
962}
963
964static void configure_manager(enum omap_channel channel)
965{
Nishant Kamat4df9d102011-06-20 20:39:28 +0530966 struct omap_overlay_manager_info *mi;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300967
968 DSSDBGF("%d", channel);
969
Nishant Kamat4df9d102011-06-20 20:39:28 +0530970 /* picking info from the cache */
971 mi = &dss_cache.manager_cache[channel].info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300972
Nishant Kamat4df9d102011-06-20 20:39:28 +0530973 dispc_set_default_color(channel, mi->default_color);
974 dispc_set_trans_key(channel, mi->trans_key_type, mi->trans_key);
975 dispc_enable_trans_key(channel, mi->trans_enabled);
976 dispc_enable_alpha_blending(channel, mi->alpha_enabled);
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300977 if (dss_has_feature(FEAT_CPR)) {
978 dispc_enable_cpr(channel, mi->cpr_enable);
979 dispc_set_cpr_coef(channel, &mi->cpr_coefs);
980 }
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300981}
982
983/* configure_dispc() tries to write values from cache to shadow registers.
984 * It writes only to those managers/overlays that are not busy.
985 * returns 0 if everything could be written to shadow registers.
986 * returns 1 if not everything could be written to shadow registers. */
987static int configure_dispc(void)
988{
989 struct overlay_cache_data *oc;
990 struct manager_cache_data *mc;
Archit Tanejaa0acb552010-09-15 19:20:00 +0530991 const int num_ovls = dss_feat_get_num_ovls();
992 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300993 int i;
994 int r;
Archit Tanejaa0acb552010-09-15 19:20:00 +0530995 bool mgr_busy[MAX_DSS_MANAGERS];
996 bool mgr_go[MAX_DSS_MANAGERS];
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300997 bool busy;
998
999 r = 0;
1000 busy = false;
1001
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001002 for (i = 0; i < num_mgrs; i++) {
1003 mgr_busy[i] = dispc_go_busy(i);
1004 mgr_go[i] = false;
1005 }
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001006
1007 /* Commit overlay settings */
1008 for (i = 0; i < num_ovls; ++i) {
1009 oc = &dss_cache.overlay_cache[i];
1010 mc = &dss_cache.manager_cache[oc->channel];
1011
1012 if (!oc->dirty)
1013 continue;
1014
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +03001015 if (mc->manual_update && !mc->do_manual_update)
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001016 continue;
1017
1018 if (mgr_busy[oc->channel]) {
1019 busy = true;
1020 continue;
1021 }
1022
1023 r = configure_overlay(i);
1024 if (r)
1025 DSSERR("configure_overlay %d failed\n", i);
1026
1027 oc->dirty = false;
1028 oc->shadow_dirty = true;
1029 mgr_go[oc->channel] = true;
1030 }
1031
1032 /* Commit manager settings */
1033 for (i = 0; i < num_mgrs; ++i) {
1034 mc = &dss_cache.manager_cache[i];
1035
1036 if (!mc->dirty)
1037 continue;
1038
1039 if (mc->manual_update && !mc->do_manual_update)
1040 continue;
1041
1042 if (mgr_busy[i]) {
1043 busy = true;
1044 continue;
1045 }
1046
1047 configure_manager(i);
1048 mc->dirty = false;
1049 mc->shadow_dirty = true;
1050 mgr_go[i] = true;
1051 }
1052
1053 /* set GO */
1054 for (i = 0; i < num_mgrs; ++i) {
1055 mc = &dss_cache.manager_cache[i];
1056
1057 if (!mgr_go[i])
1058 continue;
1059
1060 /* We don't need GO with manual update display. LCD iface will
1061 * always be turned off after frame, and new settings will be
1062 * taken in to use at next update */
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +03001063 if (!mc->manual_update)
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001064 dispc_go(i);
1065 }
1066
1067 if (busy)
1068 r = 1;
1069 else
1070 r = 0;
1071
1072 return r;
1073}
1074
Tomi Valkeinenf49a9512010-03-26 16:26:37 +02001075/* Make the coordinates even. There are some strange problems with OMAP and
1076 * partial DSI update when the update widths are odd. */
1077static void make_even(u16 *x, u16 *w)
1078{
1079 u16 x1, x2;
1080
1081 x1 = *x;
1082 x2 = *x + *w;
1083
1084 x1 &= ~1;
1085 x2 = ALIGN(x2, 2);
1086
1087 *x = x1;
1088 *w = x2 - x1;
1089}
1090
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001091/* Configure dispc for partial update. Return possibly modified update
1092 * area */
1093void dss_setup_partial_planes(struct omap_dss_device *dssdev,
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001094 u16 *xi, u16 *yi, u16 *wi, u16 *hi, bool enlarge_update_area)
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001095{
1096 struct overlay_cache_data *oc;
1097 struct manager_cache_data *mc;
Nishant Kamat4df9d102011-06-20 20:39:28 +05301098 struct omap_overlay_info *oi;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301099 const int num_ovls = dss_feat_get_num_ovls();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001100 struct omap_overlay_manager *mgr;
1101 int i;
1102 u16 x, y, w, h;
1103 unsigned long flags;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001104 bool area_changed;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001105
1106 x = *xi;
1107 y = *yi;
1108 w = *wi;
1109 h = *hi;
1110
1111 DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n",
1112 *xi, *yi, *wi, *hi);
1113
1114 mgr = dssdev->manager;
1115
1116 if (!mgr) {
1117 DSSDBG("no manager\n");
1118 return;
1119 }
1120
Tomi Valkeinenf49a9512010-03-26 16:26:37 +02001121 make_even(&x, &w);
1122
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001123 spin_lock_irqsave(&dss_cache.lock, flags);
1124
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001125 /*
1126 * Execute the outer loop until the inner loop has completed
1127 * once without increasing the update area. This will ensure that
1128 * all scaled overlays end up completely within the update area.
1129 */
1130 do {
1131 area_changed = false;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001132
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001133 /* We need to show the whole overlay if it is scaled. So look
1134 * for those, and make the update area larger if found.
1135 * Also mark the overlay cache dirty */
1136 for (i = 0; i < num_ovls; ++i) {
1137 unsigned x1, y1, x2, y2;
1138 unsigned outw, outh;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001139
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001140 oc = &dss_cache.overlay_cache[i];
Nishant Kamat4df9d102011-06-20 20:39:28 +05301141 oi = &oc->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001142
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001143 if (oc->channel != mgr->id)
1144 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001145
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001146 oc->dirty = true;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001147
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001148 if (!enlarge_update_area)
1149 continue;
1150
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001151 if (!oc->enabled)
1152 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001153
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001154 if (!dispc_is_overlay_scaled(oc))
1155 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001156
Nishant Kamat4df9d102011-06-20 20:39:28 +05301157 outw = oi->out_width == 0 ?
1158 oi->width : oi->out_width;
1159 outh = oi->out_height == 0 ?
1160 oi->height : oi->out_height;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001161
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001162 /* is the overlay outside the update region? */
1163 if (!rectangle_intersects(x, y, w, h,
Nishant Kamat4df9d102011-06-20 20:39:28 +05301164 oi->pos_x, oi->pos_y,
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001165 outw, outh))
1166 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001167
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001168 /* if the overlay totally inside the update region? */
Nishant Kamat4df9d102011-06-20 20:39:28 +05301169 if (rectangle_subset(oi->pos_x, oi->pos_y, outw, outh,
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001170 x, y, w, h))
1171 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001172
Nishant Kamat4df9d102011-06-20 20:39:28 +05301173 if (x > oi->pos_x)
1174 x1 = oi->pos_x;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001175 else
1176 x1 = x;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001177
Nishant Kamat4df9d102011-06-20 20:39:28 +05301178 if (y > oi->pos_y)
1179 y1 = oi->pos_y;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001180 else
1181 y1 = y;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001182
Nishant Kamat4df9d102011-06-20 20:39:28 +05301183 if ((x + w) < (oi->pos_x + outw))
1184 x2 = oi->pos_x + outw;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001185 else
1186 x2 = x + w;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001187
Nishant Kamat4df9d102011-06-20 20:39:28 +05301188 if ((y + h) < (oi->pos_y + outh))
1189 y2 = oi->pos_y + outh;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001190 else
1191 y2 = y + h;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001192
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001193 x = x1;
1194 y = y1;
1195 w = x2 - x1;
1196 h = y2 - y1;
Tomi Valkeinenf49a9512010-03-26 16:26:37 +02001197
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001198 make_even(&x, &w);
1199
1200 DSSDBG("changing upd area due to ovl(%d) "
1201 "scaling %d,%d %dx%d\n",
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001202 i, x, y, w, h);
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001203
1204 area_changed = true;
1205 }
1206 } while (area_changed);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001207
1208 mc = &dss_cache.manager_cache[mgr->id];
1209 mc->do_manual_update = true;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001210 mc->enlarge_update_area = enlarge_update_area;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001211 mc->x = x;
1212 mc->y = y;
1213 mc->w = w;
1214 mc->h = h;
1215
1216 configure_dispc();
1217
1218 mc->do_manual_update = false;
1219
1220 spin_unlock_irqrestore(&dss_cache.lock, flags);
1221
1222 *xi = x;
1223 *yi = y;
1224 *wi = w;
1225 *hi = h;
1226}
1227
1228void dss_start_update(struct omap_dss_device *dssdev)
1229{
1230 struct manager_cache_data *mc;
1231 struct overlay_cache_data *oc;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301232 const int num_ovls = dss_feat_get_num_ovls();
1233 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001234 struct omap_overlay_manager *mgr;
1235 int i;
1236
1237 mgr = dssdev->manager;
1238
1239 for (i = 0; i < num_ovls; ++i) {
1240 oc = &dss_cache.overlay_cache[i];
1241 if (oc->channel != mgr->id)
1242 continue;
1243
1244 oc->shadow_dirty = false;
1245 }
1246
1247 for (i = 0; i < num_mgrs; ++i) {
1248 mc = &dss_cache.manager_cache[i];
1249 if (mgr->id != i)
1250 continue;
1251
1252 mc->shadow_dirty = false;
1253 }
1254
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001255 dssdev->manager->enable(dssdev->manager);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001256}
1257
1258static void dss_apply_irq_handler(void *data, u32 mask)
1259{
1260 struct manager_cache_data *mc;
1261 struct overlay_cache_data *oc;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301262 const int num_ovls = dss_feat_get_num_ovls();
1263 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001264 int i, r;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301265 bool mgr_busy[MAX_DSS_MANAGERS];
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001266 u32 irq_mask;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001267
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001268 for (i = 0; i < num_mgrs; i++)
1269 mgr_busy[i] = dispc_go_busy(i);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001270
1271 spin_lock(&dss_cache.lock);
1272
1273 for (i = 0; i < num_ovls; ++i) {
1274 oc = &dss_cache.overlay_cache[i];
1275 if (!mgr_busy[oc->channel])
1276 oc->shadow_dirty = false;
1277 }
1278
1279 for (i = 0; i < num_mgrs; ++i) {
1280 mc = &dss_cache.manager_cache[i];
1281 if (!mgr_busy[i])
1282 mc->shadow_dirty = false;
1283 }
1284
1285 r = configure_dispc();
1286 if (r == 1)
1287 goto end;
1288
1289 /* re-read busy flags */
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001290 for (i = 0; i < num_mgrs; i++)
1291 mgr_busy[i] = dispc_go_busy(i);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001292
1293 /* keep running as long as there are busy managers, so that
1294 * we can collect overlay-applied information */
1295 for (i = 0; i < num_mgrs; ++i) {
1296 if (mgr_busy[i])
1297 goto end;
1298 }
1299
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001300 irq_mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
1301 DISPC_IRQ_EVSYNC_EVEN;
1302 if (dss_has_feature(FEAT_MGR_LCD2))
1303 irq_mask |= DISPC_IRQ_VSYNC2;
1304
1305 omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, irq_mask);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001306 dss_cache.irq_enabled = false;
1307
1308end:
1309 spin_unlock(&dss_cache.lock);
1310}
1311
1312static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
1313{
1314 struct overlay_cache_data *oc;
1315 struct manager_cache_data *mc;
1316 int i;
1317 struct omap_overlay *ovl;
1318 int num_planes_enabled = 0;
1319 bool use_fifomerge;
1320 unsigned long flags;
1321 int r;
1322
1323 DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
1324
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001325 r = dispc_runtime_get();
1326 if (r)
1327 return r;
1328
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001329 spin_lock_irqsave(&dss_cache.lock, flags);
1330
1331 /* Configure overlays */
1332 for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
1333 struct omap_dss_device *dssdev;
1334
1335 ovl = omap_dss_get_overlay(i);
1336
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001337 oc = &dss_cache.overlay_cache[ovl->id];
1338
Tomi Valkeinen8fa80312011-08-16 12:56:19 +03001339 if (ovl->manager_changed) {
1340 ovl->manager_changed = false;
1341 ovl->info_dirty = true;
1342 }
1343
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001344 if (!overlay_enabled(ovl)) {
1345 if (oc->enabled) {
1346 oc->enabled = false;
1347 oc->dirty = true;
1348 }
1349 continue;
1350 }
1351
1352 if (!ovl->info_dirty) {
1353 if (oc->enabled)
1354 ++num_planes_enabled;
1355 continue;
1356 }
1357
1358 dssdev = ovl->manager->device;
1359
1360 if (dss_check_overlay(ovl, dssdev)) {
1361 if (oc->enabled) {
1362 oc->enabled = false;
1363 oc->dirty = true;
1364 }
1365 continue;
1366 }
1367
1368 ovl->info_dirty = false;
1369 oc->dirty = true;
Nishant Kamat4df9d102011-06-20 20:39:28 +05301370 oc->info = ovl->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001371
1372 oc->replication =
1373 dss_use_replication(dssdev, ovl->info.color_mode);
1374
1375 oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC;
1376
1377 oc->channel = ovl->manager->id;
1378
1379 oc->enabled = true;
1380
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001381 ++num_planes_enabled;
1382 }
1383
1384 /* Configure managers */
1385 list_for_each_entry(mgr, &manager_list, list) {
1386 struct omap_dss_device *dssdev;
1387
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001388 mc = &dss_cache.manager_cache[mgr->id];
1389
1390 if (mgr->device_changed) {
1391 mgr->device_changed = false;
1392 mgr->info_dirty = true;
1393 }
1394
1395 if (!mgr->info_dirty)
1396 continue;
1397
1398 if (!mgr->device)
1399 continue;
1400
1401 dssdev = mgr->device;
1402
1403 mgr->info_dirty = false;
1404 mc->dirty = true;
Nishant Kamat4df9d102011-06-20 20:39:28 +05301405 mc->info = mgr->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001406
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001407 mc->manual_update =
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +03001408 dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001409 }
1410
1411 /* XXX TODO: Try to get fifomerge working. The problem is that it
1412 * affects both managers, not individually but at the same time. This
1413 * means the change has to be well synchronized. I guess the proper way
1414 * is to have a two step process for fifo merge:
1415 * fifomerge enable:
1416 * 1. disable other planes, leaving one plane enabled
1417 * 2. wait until the planes are disabled on HW
1418 * 3. config merged fifo thresholds, enable fifomerge
1419 * fifomerge disable:
1420 * 1. config unmerged fifo thresholds, disable fifomerge
1421 * 2. wait until fifo changes are in HW
1422 * 3. enable planes
1423 */
1424 use_fifomerge = false;
1425
1426 /* Configure overlay fifos */
1427 for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
1428 struct omap_dss_device *dssdev;
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001429 u32 size, burst_size;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001430
1431 ovl = omap_dss_get_overlay(i);
1432
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001433 oc = &dss_cache.overlay_cache[ovl->id];
1434
1435 if (!oc->enabled)
1436 continue;
1437
1438 dssdev = ovl->manager->device;
1439
1440 size = dispc_get_plane_fifo_size(ovl->id);
1441 if (use_fifomerge)
1442 size *= 3;
1443
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001444 burst_size = dispc_get_burst_size(ovl->id);
1445
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001446 switch (dssdev->type) {
1447 case OMAP_DISPLAY_TYPE_DPI:
1448 case OMAP_DISPLAY_TYPE_DBI:
1449 case OMAP_DISPLAY_TYPE_SDI:
1450 case OMAP_DISPLAY_TYPE_VENC:
Mythri P Kb1196012011-03-08 17:15:54 +05301451 case OMAP_DISPLAY_TYPE_HDMI:
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001452 default_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001453 burst_size, &oc->fifo_low,
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001454 &oc->fifo_high);
1455 break;
1456#ifdef CONFIG_OMAP2_DSS_DSI
1457 case OMAP_DISPLAY_TYPE_DSI:
1458 dsi_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001459 burst_size, &oc->fifo_low,
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001460 &oc->fifo_high);
1461 break;
1462#endif
1463 default:
1464 BUG();
1465 }
1466 }
1467
1468 r = 0;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001469 if (!dss_cache.irq_enabled) {
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001470 u32 mask;
1471
1472 mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
1473 DISPC_IRQ_EVSYNC_EVEN;
1474 if (dss_has_feature(FEAT_MGR_LCD2))
1475 mask |= DISPC_IRQ_VSYNC2;
1476
1477 r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001478 dss_cache.irq_enabled = true;
1479 }
1480 configure_dispc();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001481
1482 spin_unlock_irqrestore(&dss_cache.lock, flags);
1483
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001484 dispc_runtime_put();
1485
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001486 return r;
1487}
1488
1489static int dss_check_manager(struct omap_overlay_manager *mgr)
1490{
1491 /* OMAP supports only graphics source transparency color key and alpha
1492 * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */
1493
1494 if (mgr->info.alpha_enabled && mgr->info.trans_enabled &&
1495 mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST)
1496 return -EINVAL;
1497
1498 return 0;
1499}
1500
1501static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr,
1502 struct omap_overlay_manager_info *info)
1503{
1504 int r;
1505 struct omap_overlay_manager_info old_info;
1506
1507 old_info = mgr->info;
1508 mgr->info = *info;
1509
1510 r = dss_check_manager(mgr);
1511 if (r) {
1512 mgr->info = old_info;
1513 return r;
1514 }
1515
1516 mgr->info_dirty = true;
1517
1518 return 0;
1519}
1520
1521static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr,
1522 struct omap_overlay_manager_info *info)
1523{
1524 *info = mgr->info;
1525}
1526
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001527static int dss_mgr_enable(struct omap_overlay_manager *mgr)
1528{
1529 dispc_enable_channel(mgr->id, 1);
1530 return 0;
1531}
1532
1533static int dss_mgr_disable(struct omap_overlay_manager *mgr)
1534{
1535 dispc_enable_channel(mgr->id, 0);
1536 return 0;
1537}
1538
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001539static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager)
1540{
1541 ++num_managers;
1542 list_add_tail(&manager->list, &manager_list);
1543}
1544
1545int dss_init_overlay_managers(struct platform_device *pdev)
1546{
1547 int i, r;
1548
1549 spin_lock_init(&dss_cache.lock);
1550
1551 INIT_LIST_HEAD(&manager_list);
1552
1553 num_managers = 0;
1554
Archit Tanejaa0acb552010-09-15 19:20:00 +05301555 for (i = 0; i < dss_feat_get_num_mgrs(); ++i) {
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001556 struct omap_overlay_manager *mgr;
1557 mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
1558
1559 BUG_ON(mgr == NULL);
1560
1561 switch (i) {
1562 case 0:
1563 mgr->name = "lcd";
1564 mgr->id = OMAP_DSS_CHANNEL_LCD;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001565 break;
1566 case 1:
1567 mgr->name = "tv";
1568 mgr->id = OMAP_DSS_CHANNEL_DIGIT;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001569 break;
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001570 case 2:
1571 mgr->name = "lcd2";
1572 mgr->id = OMAP_DSS_CHANNEL_LCD2;
1573 break;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001574 }
1575
1576 mgr->set_device = &omap_dss_set_device;
1577 mgr->unset_device = &omap_dss_unset_device;
1578 mgr->apply = &omap_dss_mgr_apply;
1579 mgr->set_manager_info = &omap_dss_mgr_set_info;
1580 mgr->get_manager_info = &omap_dss_mgr_get_info;
1581 mgr->wait_for_go = &dss_mgr_wait_for_go;
Tomi Valkeinen3f71cbe2010-01-08 17:06:04 +02001582 mgr->wait_for_vsync = &dss_mgr_wait_for_vsync;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001583
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001584 mgr->enable = &dss_mgr_enable;
1585 mgr->disable = &dss_mgr_disable;
1586
Tomi Valkeinen4a9e78a2011-08-15 11:22:21 +03001587 mgr->caps = 0;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301588 mgr->supported_displays =
1589 dss_feat_get_supported_displays(mgr->id);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001590
1591 dss_overlay_setup_dispc_manager(mgr);
1592
1593 omap_dss_add_overlay_manager(mgr);
1594
1595 r = kobject_init_and_add(&mgr->kobj, &manager_ktype,
1596 &pdev->dev.kobj, "manager%d", i);
1597
1598 if (r) {
1599 DSSERR("failed to create sysfs file\n");
1600 continue;
1601 }
1602 }
1603
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001604 return 0;
1605}
1606
1607void dss_uninit_overlay_managers(struct platform_device *pdev)
1608{
1609 struct omap_overlay_manager *mgr;
1610
1611 while (!list_empty(&manager_list)) {
1612 mgr = list_first_entry(&manager_list,
1613 struct omap_overlay_manager, list);
1614 list_del(&mgr->list);
1615 kobject_del(&mgr->kobj);
1616 kobject_put(&mgr->kobj);
1617 kfree(mgr);
1618 }
1619
1620 num_managers = 0;
1621}
1622
1623int omap_dss_get_num_overlay_managers(void)
1624{
1625 return num_managers;
1626}
1627EXPORT_SYMBOL(omap_dss_get_num_overlay_managers);
1628
1629struct omap_overlay_manager *omap_dss_get_overlay_manager(int num)
1630{
1631 int i = 0;
1632 struct omap_overlay_manager *mgr;
1633
1634 list_for_each_entry(mgr, &manager_list, list) {
1635 if (i++ == num)
1636 return mgr;
1637 }
1638
1639 return NULL;
1640}
1641EXPORT_SYMBOL(omap_dss_get_overlay_manager);
1642