blob: 5bde08f199201e5c73b56c15115e498083d55e34 [file] [log] [blame]
Tomi Valkeinen58f255482011-11-04 09:48:54 +02001/*
2 * Copyright (C) 2011 Texas Instruments
3 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#define DSS_SUBSYS_NAME "APPLY"
19
20#include <linux/kernel.h>
21#include <linux/slab.h>
22#include <linux/spinlock.h>
23#include <linux/jiffies.h>
24
25#include <video/omapdss.h>
26
27#include "dss.h"
28#include "dss_features.h"
29
30/*
31 * We have 4 levels of cache for the dispc settings. First two are in SW and
32 * the latter two in HW.
33 *
34 * +--------------------+
35 * |overlay/manager_info|
36 * +--------------------+
37 * v
38 * apply()
39 * v
40 * +--------------------+
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +020041 * | info |
Tomi Valkeinen58f255482011-11-04 09:48:54 +020042 * +--------------------+
43 * v
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +020044 * write_regs()
Tomi Valkeinen58f255482011-11-04 09:48:54 +020045 * v
46 * +--------------------+
47 * | shadow registers |
48 * +--------------------+
49 * v
50 * VFP or lcd/digit_enable
51 * v
52 * +--------------------+
53 * | registers |
54 * +--------------------+
55 */
56
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +020057struct ovl_priv_data {
Tomi Valkeinen58f255482011-11-04 09:48:54 +020058 /* If true, cache changed, but not written to shadow registers. Set
59 * in apply(), cleared when registers written. */
60 bool dirty;
61 /* If true, shadow registers contain changed values not yet in real
62 * registers. Set when writing to shadow registers, cleared at
63 * VSYNC/EVSYNC */
64 bool shadow_dirty;
65
66 bool enabled;
67
68 struct omap_overlay_info info;
69
70 enum omap_channel channel;
71
72 u32 fifo_low;
73 u32 fifo_high;
74};
75
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +020076struct mgr_priv_data {
Tomi Valkeinen58f255482011-11-04 09:48:54 +020077 /* If true, cache changed, but not written to shadow registers. Set
78 * in apply(), cleared when registers written. */
79 bool dirty;
80 /* If true, shadow registers contain changed values not yet in real
81 * registers. Set when writing to shadow registers, cleared at
82 * VSYNC/EVSYNC */
83 bool shadow_dirty;
84
85 struct omap_overlay_manager_info info;
86
87 bool manual_update;
88 bool do_manual_update;
Tomi Valkeinenbf213522011-11-15 14:43:53 +020089
90 /* If true, a display is enabled using this manager */
91 bool enabled;
Tomi Valkeinen58f255482011-11-04 09:48:54 +020092};
93
94static struct {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +020095 struct ovl_priv_data ovl_priv_data_array[MAX_DSS_OVERLAYS];
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +020096 struct mgr_priv_data mgr_priv_data_array[MAX_DSS_MANAGERS];
Tomi Valkeinen58f255482011-11-04 09:48:54 +020097
98 bool irq_enabled;
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +020099} dss_data;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200100
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200101/* protects dss_data */
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200102static spinlock_t data_lock;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200103/* lock for blocking functions */
104static DEFINE_MUTEX(apply_lock);
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200105
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200106static struct ovl_priv_data *get_ovl_priv(struct omap_overlay *ovl)
107{
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200108 return &dss_data.ovl_priv_data_array[ovl->id];
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200109}
110
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200111static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr)
112{
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200113 return &dss_data.mgr_priv_data_array[mgr->id];
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200114}
115
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200116void dss_apply_init(void)
117{
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200118 spin_lock_init(&data_lock);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200119}
120
121static bool ovl_manual_update(struct omap_overlay *ovl)
122{
123 return ovl->manager->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
124}
125
126static bool mgr_manual_update(struct omap_overlay_manager *mgr)
127{
128 return mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
129}
130
131static int overlay_enabled(struct omap_overlay *ovl)
132{
133 return ovl->info.enabled && ovl->manager && ovl->manager->device;
134}
135
136int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
137{
138 unsigned long timeout = msecs_to_jiffies(500);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200139 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200140 u32 irq;
141 int r;
142 int i;
143 struct omap_dss_device *dssdev = mgr->device;
144
145 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
146 return 0;
147
148 if (mgr_manual_update(mgr))
149 return 0;
150
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200151 irq = dispc_mgr_get_vsync_irq(mgr->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200152
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200153 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200154 i = 0;
155 while (1) {
156 unsigned long flags;
157 bool shadow_dirty, dirty;
158
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200159 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200160 dirty = mp->dirty;
161 shadow_dirty = mp->shadow_dirty;
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200162 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200163
164 if (!dirty && !shadow_dirty) {
165 r = 0;
166 break;
167 }
168
169 /* 4 iterations is the worst case:
170 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
171 * 2 - first VSYNC, dirty = true
172 * 3 - dirty = false, shadow_dirty = true
173 * 4 - shadow_dirty = false */
174 if (i++ == 3) {
175 DSSERR("mgr(%d)->wait_for_go() not finishing\n",
176 mgr->id);
177 r = 0;
178 break;
179 }
180
181 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
182 if (r == -ERESTARTSYS)
183 break;
184
185 if (r) {
186 DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
187 break;
188 }
189 }
190
191 return r;
192}
193
194int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
195{
196 unsigned long timeout = msecs_to_jiffies(500);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200197 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200198 struct omap_dss_device *dssdev;
199 u32 irq;
200 int r;
201 int i;
202
203 if (!ovl->manager)
204 return 0;
205
206 dssdev = ovl->manager->device;
207
208 if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
209 return 0;
210
211 if (ovl_manual_update(ovl))
212 return 0;
213
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200214 irq = dispc_mgr_get_vsync_irq(ovl->manager->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200215
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200216 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200217 i = 0;
218 while (1) {
219 unsigned long flags;
220 bool shadow_dirty, dirty;
221
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200222 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200223 dirty = op->dirty;
224 shadow_dirty = op->shadow_dirty;
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200225 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200226
227 if (!dirty && !shadow_dirty) {
228 r = 0;
229 break;
230 }
231
232 /* 4 iterations is the worst case:
233 * 1 - initial iteration, dirty = true (between VFP and VSYNC)
234 * 2 - first VSYNC, dirty = true
235 * 3 - dirty = false, shadow_dirty = true
236 * 4 - shadow_dirty = false */
237 if (i++ == 3) {
238 DSSERR("ovl(%d)->wait_for_go() not finishing\n",
239 ovl->id);
240 r = 0;
241 break;
242 }
243
244 r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
245 if (r == -ERESTARTSYS)
246 break;
247
248 if (r) {
249 DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id);
250 break;
251 }
252 }
253
254 return r;
255}
256
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200257static int dss_ovl_write_regs(struct omap_overlay *ovl)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200258{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200259 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200260 struct omap_overlay_info *oi;
261 bool ilace, replication;
262 int r;
263
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200264 DSSDBGF("%d", ovl->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200265
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200266 op = get_ovl_priv(ovl);
267 oi = &op->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200268
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200269 if (!op->enabled) {
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200270 dispc_ovl_enable(ovl->id, 0);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200271 return 0;
272 }
273
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200274 replication = dss_use_replication(ovl->manager->device, oi->color_mode);
275
276 ilace = ovl->manager->device->type == OMAP_DISPLAY_TYPE_VENC;
277
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200278 dispc_ovl_set_channel_out(ovl->id, op->channel);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200279
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200280 r = dispc_ovl_setup(ovl->id, oi, ilace, replication);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200281 if (r) {
282 /* this shouldn't happen */
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200283 DSSERR("dispc_ovl_setup failed for ovl %d\n", ovl->id);
284 dispc_ovl_enable(ovl->id, 0);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200285 return r;
286 }
287
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200288 dispc_ovl_set_fifo_threshold(ovl->id, op->fifo_low, op->fifo_high);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200289
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200290 dispc_ovl_enable(ovl->id, 1);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200291
292 return 0;
293}
294
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200295static void dss_mgr_write_regs(struct omap_overlay_manager *mgr)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200296{
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200297 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200298 struct omap_overlay_manager_info *mi;
299
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200300 DSSDBGF("%d", mgr->id);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200301
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200302 mp = get_mgr_priv(mgr);
303 mi = &mp->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200304
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200305 dispc_mgr_setup(mgr->id, mi);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200306}
307
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200308/* dss_write_regs() tries to write values from cache to shadow registers.
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200309 * It writes only to those managers/overlays that are not busy.
310 * returns 0 if everything could be written to shadow registers.
311 * returns 1 if not everything could be written to shadow registers. */
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200312static int dss_write_regs(void)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200313{
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200314 struct omap_overlay *ovl;
315 struct omap_overlay_manager *mgr;
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200316 struct ovl_priv_data *op;
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200317 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200318 const int num_ovls = dss_feat_get_num_ovls();
319 const int num_mgrs = dss_feat_get_num_mgrs();
320 int i;
321 int r;
322 bool mgr_busy[MAX_DSS_MANAGERS];
323 bool mgr_go[MAX_DSS_MANAGERS];
324 bool busy;
325
326 r = 0;
327 busy = false;
328
329 for (i = 0; i < num_mgrs; i++) {
330 mgr_busy[i] = dispc_mgr_go_busy(i);
331 mgr_go[i] = false;
332 }
333
334 /* Commit overlay settings */
335 for (i = 0; i < num_ovls; ++i) {
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200336 ovl = omap_dss_get_overlay(i);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200337 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200338
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200339 if (!op->dirty)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200340 continue;
341
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200342 mp = get_mgr_priv(ovl->manager);
343
344 if (mp->manual_update && !mp->do_manual_update)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200345 continue;
346
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200347 if (mgr_busy[op->channel]) {
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200348 busy = true;
349 continue;
350 }
351
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200352 r = dss_ovl_write_regs(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200353 if (r)
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200354 DSSERR("dss_ovl_write_regs %d failed\n", i);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200355
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200356 op->dirty = false;
357 op->shadow_dirty = true;
358 mgr_go[op->channel] = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200359 }
360
361 /* Commit manager settings */
362 for (i = 0; i < num_mgrs; ++i) {
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200363 mgr = omap_dss_get_overlay_manager(i);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200364 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200365
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200366 if (!mp->dirty)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200367 continue;
368
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200369 if (mp->manual_update && !mp->do_manual_update)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200370 continue;
371
372 if (mgr_busy[i]) {
373 busy = true;
374 continue;
375 }
376
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200377 dss_mgr_write_regs(mgr);
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200378 mp->dirty = false;
379 mp->shadow_dirty = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200380 mgr_go[i] = true;
381 }
382
383 /* set GO */
384 for (i = 0; i < num_mgrs; ++i) {
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200385 mgr = omap_dss_get_overlay_manager(i);
386 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200387
388 if (!mgr_go[i])
389 continue;
390
391 /* We don't need GO with manual update display. LCD iface will
392 * always be turned off after frame, and new settings will be
393 * taken in to use at next update */
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200394 if (!mp->manual_update)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200395 dispc_mgr_go(i);
396 }
397
398 if (busy)
399 r = 1;
400 else
401 r = 0;
402
403 return r;
404}
405
406void dss_mgr_start_update(struct omap_overlay_manager *mgr)
407{
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200408 struct mgr_priv_data *mp = get_mgr_priv(mgr);
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200409 struct ovl_priv_data *op;
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200410 struct omap_overlay *ovl;
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200411 unsigned long flags;
412
413 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200414
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200415 mp->do_manual_update = true;
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200416 dss_write_regs();
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200417 mp->do_manual_update = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200418
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200419 list_for_each_entry(ovl, &mgr->overlays, list) {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200420 op = get_ovl_priv(ovl);
421 op->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200422 }
423
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200424 mp->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200425
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200426 dispc_mgr_enable(mgr->id, true);
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200427
428 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200429}
430
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200431static void dss_apply_irq_handler(void *data, u32 mask);
432
433static void dss_register_vsync_isr(void)
434{
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200435 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200436 u32 mask;
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200437 int r, i;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200438
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200439 mask = 0;
440 for (i = 0; i < num_mgrs; ++i)
441 mask |= dispc_mgr_get_vsync_irq(i);
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200442
443 r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
444 WARN_ON(r);
445
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200446 dss_data.irq_enabled = true;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200447}
448
449static void dss_unregister_vsync_isr(void)
450{
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200451 const int num_mgrs = dss_feat_get_num_mgrs();
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200452 u32 mask;
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200453 int r, i;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200454
Tomi Valkeinenbc1a9512011-11-15 11:20:13 +0200455 mask = 0;
456 for (i = 0; i < num_mgrs; ++i)
457 mask |= dispc_mgr_get_vsync_irq(i);
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200458
459 r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask);
460 WARN_ON(r);
461
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200462 dss_data.irq_enabled = false;
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200463}
464
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200465static void dss_apply_irq_handler(void *data, u32 mask)
466{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200467 struct omap_overlay *ovl;
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200468 struct omap_overlay_manager *mgr;
469 struct mgr_priv_data *mp;
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200470 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200471 const int num_ovls = dss_feat_get_num_ovls();
472 const int num_mgrs = dss_feat_get_num_mgrs();
473 int i, r;
474 bool mgr_busy[MAX_DSS_MANAGERS];
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200475
476 for (i = 0; i < num_mgrs; i++)
477 mgr_busy[i] = dispc_mgr_go_busy(i);
478
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200479 spin_lock(&data_lock);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200480
481 for (i = 0; i < num_ovls; ++i) {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200482 ovl = omap_dss_get_overlay(i);
483 op = get_ovl_priv(ovl);
484 if (!mgr_busy[op->channel])
485 op->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200486 }
487
488 for (i = 0; i < num_mgrs; ++i) {
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200489 mgr = omap_dss_get_overlay_manager(i);
490 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200491 if (!mgr_busy[i])
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200492 mp->shadow_dirty = false;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200493 }
494
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200495 r = dss_write_regs();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200496 if (r == 1)
497 goto end;
498
499 /* re-read busy flags */
500 for (i = 0; i < num_mgrs; i++)
501 mgr_busy[i] = dispc_mgr_go_busy(i);
502
503 /* keep running as long as there are busy managers, so that
504 * we can collect overlay-applied information */
505 for (i = 0; i < num_mgrs; ++i) {
506 if (mgr_busy[i])
507 goto end;
508 }
509
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200510 dss_unregister_vsync_isr();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200511
512end:
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200513 spin_unlock(&data_lock);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200514}
515
Tomi Valkeinen5738b632011-11-15 13:37:33 +0200516static void omap_dss_mgr_apply_ovl(struct omap_overlay *ovl)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200517{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200518 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200519
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200520 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200521
522 if (ovl->manager_changed) {
523 ovl->manager_changed = false;
524 ovl->info_dirty = true;
525 }
526
527 if (!overlay_enabled(ovl)) {
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200528 if (op->enabled) {
529 op->enabled = false;
530 op->dirty = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200531 }
Tomi Valkeinen5738b632011-11-15 13:37:33 +0200532 return;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200533 }
534
535 if (!ovl->info_dirty)
Tomi Valkeinen5738b632011-11-15 13:37:33 +0200536 return;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200537
538 ovl->info_dirty = false;
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200539 op->dirty = true;
540 op->info = ovl->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200541
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200542 op->channel = ovl->manager->id;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200543
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200544 op->enabled = true;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200545}
546
547static void omap_dss_mgr_apply_mgr(struct omap_overlay_manager *mgr)
548{
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200549 struct mgr_priv_data *mp;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200550
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200551 mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200552
553 if (mgr->device_changed) {
554 mgr->device_changed = false;
555 mgr->info_dirty = true;
556 }
557
558 if (!mgr->info_dirty)
559 return;
560
561 if (!mgr->device)
562 return;
563
564 mgr->info_dirty = false;
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200565 mp->dirty = true;
566 mp->info = mgr->info;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200567
Tomi Valkeinenaf3d64b2011-11-15 12:02:03 +0200568 mp->manual_update = mgr_manual_update(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200569}
570
571static void omap_dss_mgr_apply_ovl_fifos(struct omap_overlay *ovl)
572{
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200573 struct ovl_priv_data *op;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200574 struct omap_dss_device *dssdev;
575 u32 size, burst_size;
576
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200577 op = get_ovl_priv(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200578
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200579 if (!op->enabled)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200580 return;
581
582 dssdev = ovl->manager->device;
583
584 size = dispc_ovl_get_fifo_size(ovl->id);
585
586 burst_size = dispc_ovl_get_burst_size(ovl->id);
587
588 switch (dssdev->type) {
589 case OMAP_DISPLAY_TYPE_DPI:
590 case OMAP_DISPLAY_TYPE_DBI:
591 case OMAP_DISPLAY_TYPE_SDI:
592 case OMAP_DISPLAY_TYPE_VENC:
593 case OMAP_DISPLAY_TYPE_HDMI:
594 default_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200595 burst_size, &op->fifo_low,
596 &op->fifo_high);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200597 break;
598#ifdef CONFIG_OMAP2_DSS_DSI
599 case OMAP_DISPLAY_TYPE_DSI:
600 dsi_get_overlay_fifo_thresholds(ovl->id, size,
Tomi Valkeinenc10c6f02011-11-15 11:56:57 +0200601 burst_size, &op->fifo_low,
602 &op->fifo_high);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200603 break;
604#endif
605 default:
606 BUG();
607 }
608}
609
610int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
611{
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200612 int r;
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200613 unsigned long flags;
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200614 struct omap_overlay *ovl;
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200615 struct mgr_priv_data *mp = get_mgr_priv(mgr);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200616
617 DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
618
619 r = dispc_runtime_get();
620 if (r)
621 return r;
622
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200623 spin_lock_irqsave(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200624
625 /* Configure overlays */
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200626 list_for_each_entry(ovl, &mgr->overlays, list)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200627 omap_dss_mgr_apply_ovl(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200628
629 /* Configure manager */
630 omap_dss_mgr_apply_mgr(mgr);
631
632 /* Configure overlay fifos */
Tomi Valkeinen07e327c2011-11-05 10:59:59 +0200633 list_for_each_entry(ovl, &mgr->overlays, list)
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200634 omap_dss_mgr_apply_ovl_fifos(ovl);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200635
636 r = 0;
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200637 if (mp->enabled && !mgr_manual_update(mgr)) {
Tomi Valkeinend09c7aa2011-11-15 12:04:43 +0200638 if (!dss_data.irq_enabled)
Tomi Valkeinendbce0162011-11-15 11:18:12 +0200639 dss_register_vsync_isr();
Tomi Valkeinen18135ea2011-11-04 09:35:59 +0200640
Tomi Valkeinenf6a5e082011-11-15 11:47:39 +0200641 dss_write_regs();
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200642 }
643
Tomi Valkeinen063fd702011-11-15 12:04:10 +0200644 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200645
646 dispc_runtime_put();
647
648 return r;
649}
650
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200651void dss_mgr_enable(struct omap_overlay_manager *mgr)
652{
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200653 struct mgr_priv_data *mp = get_mgr_priv(mgr);
654 unsigned long flags;
655
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200656 mutex_lock(&apply_lock);
657
Tomi Valkeinen9a147a62011-11-09 15:30:11 +0200658 if (!mgr_manual_update(mgr))
659 dispc_mgr_enable(mgr->id, true);
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200660
661 spin_lock_irqsave(&data_lock, flags);
662
663 mp->enabled = true;
664
665 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200666
667 mutex_unlock(&apply_lock);
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200668}
669
670void dss_mgr_disable(struct omap_overlay_manager *mgr)
671{
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200672 struct mgr_priv_data *mp = get_mgr_priv(mgr);
673 unsigned long flags;
674
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200675 mutex_lock(&apply_lock);
676
Tomi Valkeinen9a147a62011-11-09 15:30:11 +0200677 if (!mgr_manual_update(mgr))
678 dispc_mgr_enable(mgr->id, false);
Tomi Valkeinenbf213522011-11-15 14:43:53 +0200679
680 spin_lock_irqsave(&data_lock, flags);
681
682 mp->enabled = false;
683
684 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200685
686 mutex_unlock(&apply_lock);
Tomi Valkeinen7797c6d2011-11-04 10:22:46 +0200687}
688
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200689int dss_mgr_set_info(struct omap_overlay_manager *mgr,
690 struct omap_overlay_manager_info *info)
691{
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200692 unsigned long flags;
693
694 spin_lock_irqsave(&data_lock, flags);
695
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200696 mgr->info = *info;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200697 mgr->info_dirty = true;
698
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200699 spin_unlock_irqrestore(&data_lock, flags);
700
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200701 return 0;
702}
703
704void dss_mgr_get_info(struct omap_overlay_manager *mgr,
705 struct omap_overlay_manager_info *info)
706{
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200707 unsigned long flags;
708
709 spin_lock_irqsave(&data_lock, flags);
710
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200711 *info = mgr->info;
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200712
713 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200714}
715
716int dss_mgr_set_device(struct omap_overlay_manager *mgr,
717 struct omap_dss_device *dssdev)
718{
719 int r;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200720
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200721 mutex_lock(&apply_lock);
722
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200723 if (dssdev->manager) {
724 DSSERR("display '%s' already has a manager '%s'\n",
725 dssdev->name, dssdev->manager->name);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200726 r = -EINVAL;
727 goto err;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200728 }
729
730 if ((mgr->supported_displays & dssdev->type) == 0) {
731 DSSERR("display '%s' does not support manager '%s'\n",
732 dssdev->name, mgr->name);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200733 r = -EINVAL;
734 goto err;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200735 }
736
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200737 dssdev->manager = mgr;
738 mgr->device = dssdev;
739 mgr->device_changed = true;
740
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200741 mutex_unlock(&apply_lock);
742
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200743 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200744err:
745 mutex_unlock(&apply_lock);
746 return r;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200747}
748
749int dss_mgr_unset_device(struct omap_overlay_manager *mgr)
750{
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200751 int r;
752
753 mutex_lock(&apply_lock);
754
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200755 if (!mgr->device) {
756 DSSERR("failed to unset display, display not set.\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200757 r = -EINVAL;
758 goto err;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200759 }
760
761 /*
762 * Don't allow currently enabled displays to have the overlay manager
763 * pulled out from underneath them
764 */
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200765 if (mgr->device->state != OMAP_DSS_DISPLAY_DISABLED) {
766 r = -EINVAL;
767 goto err;
768 }
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200769
770 mgr->device->manager = NULL;
771 mgr->device = NULL;
772 mgr->device_changed = true;
773
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200774 mutex_unlock(&apply_lock);
775
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200776 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200777err:
778 mutex_unlock(&apply_lock);
779 return r;
Tomi Valkeineneb70d732011-11-15 12:15:18 +0200780}
781
782
783
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200784int dss_ovl_set_info(struct omap_overlay *ovl,
785 struct omap_overlay_info *info)
786{
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200787 unsigned long flags;
788
789 spin_lock_irqsave(&data_lock, flags);
790
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200791 ovl->info = *info;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200792 ovl->info_dirty = true;
793
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200794 spin_unlock_irqrestore(&data_lock, flags);
795
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200796 return 0;
797}
798
799void dss_ovl_get_info(struct omap_overlay *ovl,
800 struct omap_overlay_info *info)
801{
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200802 unsigned long flags;
803
804 spin_lock_irqsave(&data_lock, flags);
805
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200806 *info = ovl->info;
Tomi Valkeinene0a2aa5b2011-11-15 14:32:57 +0200807
808 spin_unlock_irqrestore(&data_lock, flags);
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200809}
810
811int dss_ovl_set_manager(struct omap_overlay *ovl,
812 struct omap_overlay_manager *mgr)
813{
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200814 int r;
815
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200816 if (!mgr)
817 return -EINVAL;
818
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200819 mutex_lock(&apply_lock);
820
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200821 if (ovl->manager) {
822 DSSERR("overlay '%s' already has a manager '%s'\n",
823 ovl->name, ovl->manager->name);
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200824 r = -EINVAL;
825 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200826 }
827
828 if (ovl->info.enabled) {
829 DSSERR("overlay has to be disabled to change the manager\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200830 r = -EINVAL;
831 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200832 }
833
834 ovl->manager = mgr;
835 list_add_tail(&ovl->list, &mgr->overlays);
836 ovl->manager_changed = true;
837
838 /* XXX: When there is an overlay on a DSI manual update display, and
839 * the overlay is first disabled, then moved to tv, and enabled, we
840 * seem to get SYNC_LOST_DIGIT error.
841 *
842 * Waiting doesn't seem to help, but updating the manual update display
843 * after disabling the overlay seems to fix this. This hints that the
844 * overlay is perhaps somehow tied to the LCD output until the output
845 * is updated.
846 *
847 * Userspace workaround for this is to update the LCD after disabling
848 * the overlay, but before moving the overlay to TV.
849 */
850
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200851 mutex_unlock(&apply_lock);
852
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200853 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200854err:
855 mutex_unlock(&apply_lock);
856 return r;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200857}
858
859int dss_ovl_unset_manager(struct omap_overlay *ovl)
860{
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200861 int r;
862
863 mutex_lock(&apply_lock);
864
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200865 if (!ovl->manager) {
866 DSSERR("failed to detach overlay: manager not set\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200867 r = -EINVAL;
868 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200869 }
870
871 if (ovl->info.enabled) {
872 DSSERR("overlay has to be disabled to unset the manager\n");
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200873 r = -EINVAL;
874 goto err;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200875 }
876
877 ovl->manager = NULL;
878 list_del(&ovl->list);
879 ovl->manager_changed = true;
880
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200881 mutex_unlock(&apply_lock);
882
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200883 return 0;
Tomi Valkeinen5558db32011-11-15 14:28:48 +0200884err:
885 mutex_unlock(&apply_lock);
886 return r;
Tomi Valkeinenf77b3072011-11-15 12:11:11 +0200887}
888