blob: fdbbeebcd75cb5df1e24f28f6147ddcc87346d53 [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;
Archit Tanejaa4273b72011-09-14 11:10:10 +0530812 struct omap_overlay_info *oi, new_oi;
Nishant Kamat4df9d102011-06-20 20:39:28 +0530813 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) {
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +0300826 dispc_ovl_enable(plane, 0);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300827 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)) {
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +0300854 dispc_ovl_enable(plane, 0);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300855 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
Archit Tanejaa4273b72011-09-14 11:10:10 +0530932 new_oi = *oi;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300933
Archit Tanejaa4273b72011-09-14 11:10:10 +0530934 /* update new_oi members which could have been possibly updated */
935 new_oi.pos_x = x;
936 new_oi.pos_y = y;
937 new_oi.width = w;
938 new_oi.height = h;
939 new_oi.out_width = outw;
940 new_oi.out_height = outh;
941 new_oi.paddr = paddr;
942
Archit Tanejac3d925292011-09-14 11:52:54 +0530943 r = dispc_ovl_setup(plane, &new_oi, c->ilace, c->channel,
944 c->replication, c->fifo_low, c->fifo_high);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300945 if (r) {
946 /* this shouldn't happen */
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +0300947 DSSERR("dispc_ovl_setup failed for ovl %d\n", plane);
948 dispc_ovl_enable(plane, 0);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300949 return r;
950 }
951
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +0300952 dispc_ovl_enable(plane, 1);
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300953
954 return 0;
955}
956
957static void configure_manager(enum omap_channel channel)
958{
Nishant Kamat4df9d102011-06-20 20:39:28 +0530959 struct omap_overlay_manager_info *mi;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300960
961 DSSDBGF("%d", channel);
962
Nishant Kamat4df9d102011-06-20 20:39:28 +0530963 /* picking info from the cache */
964 mi = &dss_cache.manager_cache[channel].info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300965
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +0300966 dispc_mgr_set_default_color(channel, mi->default_color);
967 dispc_mgr_set_trans_key(channel, mi->trans_key_type, mi->trans_key);
968 dispc_mgr_enable_trans_key(channel, mi->trans_enabled);
969 dispc_mgr_enable_alpha_blending(channel, mi->alpha_enabled);
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300970 if (dss_has_feature(FEAT_CPR)) {
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +0300971 dispc_mgr_enable_cpr(channel, mi->cpr_enable);
972 dispc_mgr_set_cpr_coef(channel, &mi->cpr_coefs);
Tomi Valkeinen3c07cae2011-06-21 09:34:30 +0300973 }
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300974}
975
976/* configure_dispc() tries to write values from cache to shadow registers.
977 * It writes only to those managers/overlays that are not busy.
978 * returns 0 if everything could be written to shadow registers.
979 * returns 1 if not everything could be written to shadow registers. */
980static int configure_dispc(void)
981{
982 struct overlay_cache_data *oc;
983 struct manager_cache_data *mc;
Archit Tanejaa0acb552010-09-15 19:20:00 +0530984 const int num_ovls = dss_feat_get_num_ovls();
985 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300986 int i;
987 int r;
Archit Tanejaa0acb552010-09-15 19:20:00 +0530988 bool mgr_busy[MAX_DSS_MANAGERS];
989 bool mgr_go[MAX_DSS_MANAGERS];
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300990 bool busy;
991
992 r = 0;
993 busy = false;
994
Sumit Semwal18faa1b2010-12-02 11:27:14 +0000995 for (i = 0; i < num_mgrs; i++) {
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +0300996 mgr_busy[i] = dispc_mgr_go_busy(i);
Sumit Semwal18faa1b2010-12-02 11:27:14 +0000997 mgr_go[i] = false;
998 }
Tomi Valkeineneed07e02009-08-07 13:43:20 +0300999
1000 /* Commit overlay settings */
1001 for (i = 0; i < num_ovls; ++i) {
1002 oc = &dss_cache.overlay_cache[i];
1003 mc = &dss_cache.manager_cache[oc->channel];
1004
1005 if (!oc->dirty)
1006 continue;
1007
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +03001008 if (mc->manual_update && !mc->do_manual_update)
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001009 continue;
1010
1011 if (mgr_busy[oc->channel]) {
1012 busy = true;
1013 continue;
1014 }
1015
1016 r = configure_overlay(i);
1017 if (r)
1018 DSSERR("configure_overlay %d failed\n", i);
1019
1020 oc->dirty = false;
1021 oc->shadow_dirty = true;
1022 mgr_go[oc->channel] = true;
1023 }
1024
1025 /* Commit manager settings */
1026 for (i = 0; i < num_mgrs; ++i) {
1027 mc = &dss_cache.manager_cache[i];
1028
1029 if (!mc->dirty)
1030 continue;
1031
1032 if (mc->manual_update && !mc->do_manual_update)
1033 continue;
1034
1035 if (mgr_busy[i]) {
1036 busy = true;
1037 continue;
1038 }
1039
1040 configure_manager(i);
1041 mc->dirty = false;
1042 mc->shadow_dirty = true;
1043 mgr_go[i] = true;
1044 }
1045
1046 /* set GO */
1047 for (i = 0; i < num_mgrs; ++i) {
1048 mc = &dss_cache.manager_cache[i];
1049
1050 if (!mgr_go[i])
1051 continue;
1052
1053 /* We don't need GO with manual update display. LCD iface will
1054 * always be turned off after frame, and new settings will be
1055 * taken in to use at next update */
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +03001056 if (!mc->manual_update)
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001057 dispc_mgr_go(i);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001058 }
1059
1060 if (busy)
1061 r = 1;
1062 else
1063 r = 0;
1064
1065 return r;
1066}
1067
Tomi Valkeinenf49a9512010-03-26 16:26:37 +02001068/* Make the coordinates even. There are some strange problems with OMAP and
1069 * partial DSI update when the update widths are odd. */
1070static void make_even(u16 *x, u16 *w)
1071{
1072 u16 x1, x2;
1073
1074 x1 = *x;
1075 x2 = *x + *w;
1076
1077 x1 &= ~1;
1078 x2 = ALIGN(x2, 2);
1079
1080 *x = x1;
1081 *w = x2 - x1;
1082}
1083
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001084/* Configure dispc for partial update. Return possibly modified update
1085 * area */
1086void dss_setup_partial_planes(struct omap_dss_device *dssdev,
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001087 u16 *xi, u16 *yi, u16 *wi, u16 *hi, bool enlarge_update_area)
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001088{
1089 struct overlay_cache_data *oc;
1090 struct manager_cache_data *mc;
Nishant Kamat4df9d102011-06-20 20:39:28 +05301091 struct omap_overlay_info *oi;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301092 const int num_ovls = dss_feat_get_num_ovls();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001093 struct omap_overlay_manager *mgr;
1094 int i;
1095 u16 x, y, w, h;
1096 unsigned long flags;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001097 bool area_changed;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001098
1099 x = *xi;
1100 y = *yi;
1101 w = *wi;
1102 h = *hi;
1103
1104 DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n",
1105 *xi, *yi, *wi, *hi);
1106
1107 mgr = dssdev->manager;
1108
1109 if (!mgr) {
1110 DSSDBG("no manager\n");
1111 return;
1112 }
1113
Tomi Valkeinenf49a9512010-03-26 16:26:37 +02001114 make_even(&x, &w);
1115
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001116 spin_lock_irqsave(&dss_cache.lock, flags);
1117
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001118 /*
1119 * Execute the outer loop until the inner loop has completed
1120 * once without increasing the update area. This will ensure that
1121 * all scaled overlays end up completely within the update area.
1122 */
1123 do {
1124 area_changed = false;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001125
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001126 /* We need to show the whole overlay if it is scaled. So look
1127 * for those, and make the update area larger if found.
1128 * Also mark the overlay cache dirty */
1129 for (i = 0; i < num_ovls; ++i) {
1130 unsigned x1, y1, x2, y2;
1131 unsigned outw, outh;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001132
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001133 oc = &dss_cache.overlay_cache[i];
Nishant Kamat4df9d102011-06-20 20:39:28 +05301134 oi = &oc->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001135
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001136 if (oc->channel != mgr->id)
1137 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001138
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001139 oc->dirty = true;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001140
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001141 if (!enlarge_update_area)
1142 continue;
1143
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001144 if (!oc->enabled)
1145 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001146
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001147 if (!dispc_is_overlay_scaled(oc))
1148 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001149
Nishant Kamat4df9d102011-06-20 20:39:28 +05301150 outw = oi->out_width == 0 ?
1151 oi->width : oi->out_width;
1152 outh = oi->out_height == 0 ?
1153 oi->height : oi->out_height;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001154
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001155 /* is the overlay outside the update region? */
1156 if (!rectangle_intersects(x, y, w, h,
Nishant Kamat4df9d102011-06-20 20:39:28 +05301157 oi->pos_x, oi->pos_y,
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001158 outw, outh))
1159 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001160
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001161 /* if the overlay totally inside the update region? */
Nishant Kamat4df9d102011-06-20 20:39:28 +05301162 if (rectangle_subset(oi->pos_x, oi->pos_y, outw, outh,
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001163 x, y, w, h))
1164 continue;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001165
Nishant Kamat4df9d102011-06-20 20:39:28 +05301166 if (x > oi->pos_x)
1167 x1 = oi->pos_x;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001168 else
1169 x1 = x;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001170
Nishant Kamat4df9d102011-06-20 20:39:28 +05301171 if (y > oi->pos_y)
1172 y1 = oi->pos_y;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001173 else
1174 y1 = y;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001175
Nishant Kamat4df9d102011-06-20 20:39:28 +05301176 if ((x + w) < (oi->pos_x + outw))
1177 x2 = oi->pos_x + outw;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001178 else
1179 x2 = x + w;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001180
Nishant Kamat4df9d102011-06-20 20:39:28 +05301181 if ((y + h) < (oi->pos_y + outh))
1182 y2 = oi->pos_y + outh;
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001183 else
1184 y2 = y + h;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001185
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001186 x = x1;
1187 y = y1;
1188 w = x2 - x1;
1189 h = y2 - y1;
Tomi Valkeinenf49a9512010-03-26 16:26:37 +02001190
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001191 make_even(&x, &w);
1192
1193 DSSDBG("changing upd area due to ovl(%d) "
1194 "scaling %d,%d %dx%d\n",
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001195 i, x, y, w, h);
Tomi Valkeinen8cab90f2010-06-09 15:09:30 +03001196
1197 area_changed = true;
1198 }
1199 } while (area_changed);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001200
1201 mc = &dss_cache.manager_cache[mgr->id];
1202 mc->do_manual_update = true;
Tomi Valkeinen26a8c252010-06-09 15:31:34 +03001203 mc->enlarge_update_area = enlarge_update_area;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001204 mc->x = x;
1205 mc->y = y;
1206 mc->w = w;
1207 mc->h = h;
1208
1209 configure_dispc();
1210
1211 mc->do_manual_update = false;
1212
1213 spin_unlock_irqrestore(&dss_cache.lock, flags);
1214
1215 *xi = x;
1216 *yi = y;
1217 *wi = w;
1218 *hi = h;
1219}
1220
1221void dss_start_update(struct omap_dss_device *dssdev)
1222{
1223 struct manager_cache_data *mc;
1224 struct overlay_cache_data *oc;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301225 const int num_ovls = dss_feat_get_num_ovls();
1226 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001227 struct omap_overlay_manager *mgr;
1228 int i;
1229
1230 mgr = dssdev->manager;
1231
1232 for (i = 0; i < num_ovls; ++i) {
1233 oc = &dss_cache.overlay_cache[i];
1234 if (oc->channel != mgr->id)
1235 continue;
1236
1237 oc->shadow_dirty = false;
1238 }
1239
1240 for (i = 0; i < num_mgrs; ++i) {
1241 mc = &dss_cache.manager_cache[i];
1242 if (mgr->id != i)
1243 continue;
1244
1245 mc->shadow_dirty = false;
1246 }
1247
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001248 dssdev->manager->enable(dssdev->manager);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001249}
1250
1251static void dss_apply_irq_handler(void *data, u32 mask)
1252{
1253 struct manager_cache_data *mc;
1254 struct overlay_cache_data *oc;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301255 const int num_ovls = dss_feat_get_num_ovls();
1256 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001257 int i, r;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301258 bool mgr_busy[MAX_DSS_MANAGERS];
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001259 u32 irq_mask;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001260
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001261 for (i = 0; i < num_mgrs; i++)
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001262 mgr_busy[i] = dispc_mgr_go_busy(i);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001263
1264 spin_lock(&dss_cache.lock);
1265
1266 for (i = 0; i < num_ovls; ++i) {
1267 oc = &dss_cache.overlay_cache[i];
1268 if (!mgr_busy[oc->channel])
1269 oc->shadow_dirty = false;
1270 }
1271
1272 for (i = 0; i < num_mgrs; ++i) {
1273 mc = &dss_cache.manager_cache[i];
1274 if (!mgr_busy[i])
1275 mc->shadow_dirty = false;
1276 }
1277
1278 r = configure_dispc();
1279 if (r == 1)
1280 goto end;
1281
1282 /* re-read busy flags */
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001283 for (i = 0; i < num_mgrs; i++)
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001284 mgr_busy[i] = dispc_mgr_go_busy(i);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001285
1286 /* keep running as long as there are busy managers, so that
1287 * we can collect overlay-applied information */
1288 for (i = 0; i < num_mgrs; ++i) {
1289 if (mgr_busy[i])
1290 goto end;
1291 }
1292
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001293 irq_mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
1294 DISPC_IRQ_EVSYNC_EVEN;
1295 if (dss_has_feature(FEAT_MGR_LCD2))
1296 irq_mask |= DISPC_IRQ_VSYNC2;
1297
1298 omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, irq_mask);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001299 dss_cache.irq_enabled = false;
1300
1301end:
1302 spin_unlock(&dss_cache.lock);
1303}
1304
1305static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
1306{
1307 struct overlay_cache_data *oc;
1308 struct manager_cache_data *mc;
1309 int i;
1310 struct omap_overlay *ovl;
1311 int num_planes_enabled = 0;
1312 bool use_fifomerge;
1313 unsigned long flags;
1314 int r;
1315
1316 DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
1317
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001318 r = dispc_runtime_get();
1319 if (r)
1320 return r;
1321
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001322 spin_lock_irqsave(&dss_cache.lock, flags);
1323
1324 /* Configure overlays */
1325 for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
1326 struct omap_dss_device *dssdev;
1327
1328 ovl = omap_dss_get_overlay(i);
1329
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001330 oc = &dss_cache.overlay_cache[ovl->id];
1331
Tomi Valkeinen8fa80312011-08-16 12:56:19 +03001332 if (ovl->manager_changed) {
1333 ovl->manager_changed = false;
1334 ovl->info_dirty = true;
1335 }
1336
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001337 if (!overlay_enabled(ovl)) {
1338 if (oc->enabled) {
1339 oc->enabled = false;
1340 oc->dirty = true;
1341 }
1342 continue;
1343 }
1344
1345 if (!ovl->info_dirty) {
1346 if (oc->enabled)
1347 ++num_planes_enabled;
1348 continue;
1349 }
1350
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;
1373
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001374 ++num_planes_enabled;
1375 }
1376
1377 /* Configure managers */
1378 list_for_each_entry(mgr, &manager_list, list) {
1379 struct omap_dss_device *dssdev;
1380
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001381 mc = &dss_cache.manager_cache[mgr->id];
1382
1383 if (mgr->device_changed) {
1384 mgr->device_changed = false;
1385 mgr->info_dirty = true;
1386 }
1387
1388 if (!mgr->info_dirty)
1389 continue;
1390
1391 if (!mgr->device)
1392 continue;
1393
1394 dssdev = mgr->device;
1395
1396 mgr->info_dirty = false;
1397 mc->dirty = true;
Nishant Kamat4df9d102011-06-20 20:39:28 +05301398 mc->info = mgr->info;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001399
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001400 mc->manual_update =
Tomi Valkeinen8cff88c2011-04-30 14:09:53 +03001401 dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001402 }
1403
1404 /* XXX TODO: Try to get fifomerge working. The problem is that it
1405 * affects both managers, not individually but at the same time. This
1406 * means the change has to be well synchronized. I guess the proper way
1407 * is to have a two step process for fifo merge:
1408 * fifomerge enable:
1409 * 1. disable other planes, leaving one plane enabled
1410 * 2. wait until the planes are disabled on HW
1411 * 3. config merged fifo thresholds, enable fifomerge
1412 * fifomerge disable:
1413 * 1. config unmerged fifo thresholds, disable fifomerge
1414 * 2. wait until fifo changes are in HW
1415 * 3. enable planes
1416 */
1417 use_fifomerge = false;
1418
1419 /* Configure overlay fifos */
1420 for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
1421 struct omap_dss_device *dssdev;
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001422 u32 size, burst_size;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001423
1424 ovl = omap_dss_get_overlay(i);
1425
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001426 oc = &dss_cache.overlay_cache[ovl->id];
1427
1428 if (!oc->enabled)
1429 continue;
1430
1431 dssdev = ovl->manager->device;
1432
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +03001433 size = dispc_ovl_get_fifo_size(ovl->id);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001434 if (use_fifomerge)
1435 size *= 3;
1436
Tomi Valkeinenf0e5caa2011-08-16 13:25:00 +03001437 burst_size = dispc_ovl_get_burst_size(ovl->id);
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001438
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001439 switch (dssdev->type) {
1440 case OMAP_DISPLAY_TYPE_DPI:
1441 case OMAP_DISPLAY_TYPE_DBI:
1442 case OMAP_DISPLAY_TYPE_SDI:
1443 case OMAP_DISPLAY_TYPE_VENC:
Mythri P Kb1196012011-03-08 17:15:54 +05301444 case OMAP_DISPLAY_TYPE_HDMI:
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001445 default_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001446 burst_size, &oc->fifo_low,
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001447 &oc->fifo_high);
1448 break;
1449#ifdef CONFIG_OMAP2_DSS_DSI
1450 case OMAP_DISPLAY_TYPE_DSI:
1451 dsi_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinen5ed8cf52011-06-21 09:35:36 +03001452 burst_size, &oc->fifo_low,
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001453 &oc->fifo_high);
1454 break;
1455#endif
1456 default:
1457 BUG();
1458 }
1459 }
1460
1461 r = 0;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001462 if (!dss_cache.irq_enabled) {
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001463 u32 mask;
1464
1465 mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
1466 DISPC_IRQ_EVSYNC_EVEN;
1467 if (dss_has_feature(FEAT_MGR_LCD2))
1468 mask |= DISPC_IRQ_VSYNC2;
1469
1470 r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001471 dss_cache.irq_enabled = true;
1472 }
1473 configure_dispc();
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001474
1475 spin_unlock_irqrestore(&dss_cache.lock, flags);
1476
Tomi Valkeinen4fbafaf2011-05-27 10:52:19 +03001477 dispc_runtime_put();
1478
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001479 return r;
1480}
1481
1482static int dss_check_manager(struct omap_overlay_manager *mgr)
1483{
1484 /* OMAP supports only graphics source transparency color key and alpha
1485 * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */
1486
1487 if (mgr->info.alpha_enabled && mgr->info.trans_enabled &&
1488 mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST)
1489 return -EINVAL;
1490
1491 return 0;
1492}
1493
1494static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr,
1495 struct omap_overlay_manager_info *info)
1496{
1497 int r;
1498 struct omap_overlay_manager_info old_info;
1499
1500 old_info = mgr->info;
1501 mgr->info = *info;
1502
1503 r = dss_check_manager(mgr);
1504 if (r) {
1505 mgr->info = old_info;
1506 return r;
1507 }
1508
1509 mgr->info_dirty = true;
1510
1511 return 0;
1512}
1513
1514static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr,
1515 struct omap_overlay_manager_info *info)
1516{
1517 *info = mgr->info;
1518}
1519
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001520static int dss_mgr_enable(struct omap_overlay_manager *mgr)
1521{
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001522 dispc_mgr_enable(mgr->id, 1);
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001523 return 0;
1524}
1525
1526static int dss_mgr_disable(struct omap_overlay_manager *mgr)
1527{
Tomi Valkeinen26d9dd02011-08-16 13:45:15 +03001528 dispc_mgr_enable(mgr->id, 0);
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001529 return 0;
1530}
1531
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001532static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager)
1533{
1534 ++num_managers;
1535 list_add_tail(&manager->list, &manager_list);
1536}
1537
1538int dss_init_overlay_managers(struct platform_device *pdev)
1539{
1540 int i, r;
1541
1542 spin_lock_init(&dss_cache.lock);
1543
1544 INIT_LIST_HEAD(&manager_list);
1545
1546 num_managers = 0;
1547
Archit Tanejaa0acb552010-09-15 19:20:00 +05301548 for (i = 0; i < dss_feat_get_num_mgrs(); ++i) {
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001549 struct omap_overlay_manager *mgr;
1550 mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
1551
1552 BUG_ON(mgr == NULL);
1553
1554 switch (i) {
1555 case 0:
1556 mgr->name = "lcd";
1557 mgr->id = OMAP_DSS_CHANNEL_LCD;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001558 break;
1559 case 1:
1560 mgr->name = "tv";
1561 mgr->id = OMAP_DSS_CHANNEL_DIGIT;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001562 break;
Sumit Semwal18faa1b2010-12-02 11:27:14 +00001563 case 2:
1564 mgr->name = "lcd2";
1565 mgr->id = OMAP_DSS_CHANNEL_LCD2;
1566 break;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001567 }
1568
1569 mgr->set_device = &omap_dss_set_device;
1570 mgr->unset_device = &omap_dss_unset_device;
1571 mgr->apply = &omap_dss_mgr_apply;
1572 mgr->set_manager_info = &omap_dss_mgr_set_info;
1573 mgr->get_manager_info = &omap_dss_mgr_get_info;
1574 mgr->wait_for_go = &dss_mgr_wait_for_go;
Tomi Valkeinen3f71cbe2010-01-08 17:06:04 +02001575 mgr->wait_for_vsync = &dss_mgr_wait_for_vsync;
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001576
Tomi Valkeinena2faee82010-01-08 17:14:53 +02001577 mgr->enable = &dss_mgr_enable;
1578 mgr->disable = &dss_mgr_disable;
1579
Tomi Valkeinen4a9e78a2011-08-15 11:22:21 +03001580 mgr->caps = 0;
Archit Tanejaa0acb552010-09-15 19:20:00 +05301581 mgr->supported_displays =
1582 dss_feat_get_supported_displays(mgr->id);
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001583
1584 dss_overlay_setup_dispc_manager(mgr);
1585
1586 omap_dss_add_overlay_manager(mgr);
1587
1588 r = kobject_init_and_add(&mgr->kobj, &manager_ktype,
1589 &pdev->dev.kobj, "manager%d", i);
1590
1591 if (r) {
1592 DSSERR("failed to create sysfs file\n");
1593 continue;
1594 }
1595 }
1596
Tomi Valkeineneed07e02009-08-07 13:43:20 +03001597 return 0;
1598}
1599
1600void dss_uninit_overlay_managers(struct platform_device *pdev)
1601{
1602 struct omap_overlay_manager *mgr;
1603
1604 while (!list_empty(&manager_list)) {
1605 mgr = list_first_entry(&manager_list,
1606 struct omap_overlay_manager, list);
1607 list_del(&mgr->list);
1608 kobject_del(&mgr->kobj);
1609 kobject_put(&mgr->kobj);
1610 kfree(mgr);
1611 }
1612
1613 num_managers = 0;
1614}
1615
1616int omap_dss_get_num_overlay_managers(void)
1617{
1618 return num_managers;
1619}
1620EXPORT_SYMBOL(omap_dss_get_num_overlay_managers);
1621
1622struct omap_overlay_manager *omap_dss_get_overlay_manager(int num)
1623{
1624 int i = 0;
1625 struct omap_overlay_manager *mgr;
1626
1627 list_for_each_entry(mgr, &manager_list, list) {
1628 if (i++ == num)
1629 return mgr;
1630 }
1631
1632 return NULL;
1633}
1634EXPORT_SYMBOL(omap_dss_get_overlay_manager);
1635