PageRenderTime 49ms CodeModel.GetById 18ms app.highlight 25ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/video/omap2/dsscomp/queue.c

https://bitbucket.org/slukk/jb-tsm-kernel-4.2
C | 807 lines | 567 code | 124 blank | 116 comment | 144 complexity | 0c995781fe14bc13eaeb49080eae0339 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
  1/*
  2 * linux/drivers/video/omap2/dsscomp/queue.c
  3 *
  4 * DSS Composition queueing support
  5 *
  6 * Copyright (C) 2011 Texas Instruments, Inc
  7 * Author: Lajos Molnar <molnar@ti.com>
  8 *
  9 * This program is free software; you can redistribute it and/or modify it
 10 * under the terms of the GNU General Public License version 2 as published by
 11 * the Free Software Foundation.
 12 *
 13 * This program is distributed in the hope that it will be useful, but WITHOUT
 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 15 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 16 * more details.
 17 *
 18 * You should have received a copy of the GNU General Public License along with
 19 * this program.  If not, see <http://www.gnu.org/licenses/>.
 20 */
 21
 22#include <linux/kernel.h>
 23#include <linux/vmalloc.h>
 24#include <linux/sched.h>
 25#include <linux/slab.h>
 26#include <linux/ratelimit.h>
 27
 28#include <video/omapdss.h>
 29#include <video/dsscomp.h>
 30#include <plat/dsscomp.h>
 31
 32#include <linux/debugfs.h>
 33
 34#include "dsscomp.h"
 35/* queue state */
 36
 37static DEFINE_MUTEX(mtx);
 38
 39/* free overlay structs */
 40struct maskref {
 41	u32 mask;
 42	u32 refs[MAX_OVERLAYS];
 43};
 44
 45static struct {
 46	struct workqueue_struct *apply_workq;
 47
 48	u32 ovl_mask;		/* overlays used on this display */
 49	struct maskref ovl_qmask;		/* overlays queued to this display */
 50	bool blanking;
 51} mgrq[MAX_MANAGERS];
 52
 53static struct workqueue_struct *cb_wkq;		/* callback work queue */
 54static struct dsscomp_dev *cdev;
 55
 56#ifdef CONFIG_DEBUG_FS
 57LIST_HEAD(dbg_comps);
 58DEFINE_MUTEX(dbg_mtx);
 59#endif
 60
 61#ifdef CONFIG_DSSCOMP_DEBUG_LOG
 62struct dbg_event_t dbg_events[128];
 63u32 dbg_event_ix;
 64#endif
 65
 66static inline void __log_state(dsscomp_t c, void *fn, u32 ev)
 67{
 68#ifdef CONFIG_DSSCOMP_DEBUG_LOG
 69	if (c->dbg_used < ARRAY_SIZE(c->dbg_log)) {
 70		u32 t = (u32) ktime_to_ms(ktime_get());
 71		c->dbg_log[c->dbg_used].t = t;
 72		c->dbg_log[c->dbg_used++].state = c->state;
 73		__log_event(20 * c->ix + 20, t, c, ev ? "%pf on %s" : "%pf",
 74				(u32) fn, (u32) log_status_str(ev));
 75	}
 76#endif
 77}
 78#define log_state(c, fn, ev) DO_IF_DEBUG_FS(__log_state(c, fn, ev))
 79
 80static inline void maskref_incbit(struct maskref *om, u32 ix)
 81{
 82	om->refs[ix]++;
 83	om->mask |= 1 << ix;
 84}
 85
 86static void maskref_decmask(struct maskref *om, u32 mask)
 87{
 88	while (mask) {
 89		u32 ix = fls(mask) - 1, m = 1 << ix;
 90		if (!--om->refs[ix])
 91			om->mask &= ~m;
 92		mask &= ~m;
 93	}
 94}
 95
 96/*
 97 * ===========================================================================
 98 *		EXIT
 99 * ===========================================================================
100 */
101
102/* Initialize queue structures, and set up state of the displays */
103int dsscomp_queue_init(struct dsscomp_dev *cdev_)
104{
105	u32 i, j;
106	cdev = cdev_;
107
108	if (ARRAY_SIZE(mgrq) < cdev->num_mgrs)
109		return -EINVAL;
110
111	ZERO(mgrq);
112	for (i = 0; i < cdev->num_mgrs; i++) {
113		struct omap_overlay_manager *mgr;
114		mgrq[i].apply_workq = create_singlethread_workqueue("dsscomp_apply");
115		if (!mgrq[i].apply_workq)
116			goto error;
117
118		/* record overlays on this display */
119		mgr = cdev->mgrs[i];
120		for (j = 0; j < cdev->num_ovls; j++) {
121			if (cdev->ovls[j]->info.enabled &&
122			    mgr &&
123			    cdev->ovls[j]->manager == mgr)
124				mgrq[i].ovl_mask |= 1 << j;
125		}
126	}
127
128	cb_wkq = create_singlethread_workqueue("dsscomp_cb");
129	if (!cb_wkq)
130		goto error;
131
132	return 0;
133error:
134	while (i--)
135		destroy_workqueue(mgrq[i].apply_workq);
136	return -ENOMEM;
137}
138
139/* get display index from manager */
140static u32 get_display_ix(struct omap_overlay_manager *mgr)
141{
142	u32 i;
143
144	/* handle if manager is not attached to a display */
145	if (!mgr || !mgr->device)
146		return cdev->num_displays;
147
148	/* find manager's display */
149	for (i = 0; i < cdev->num_displays; i++)
150		if (cdev->displays[i] == mgr->device)
151			break;
152
153	return i;
154}
155
156/*
157 * ===========================================================================
158 *		QUEUING SETUP OPERATIONS
159 * ===========================================================================
160 */
161
162/* create a new composition for a display */
163dsscomp_t dsscomp_new(struct omap_overlay_manager *mgr)
164{
165	struct dsscomp_data *comp = NULL;
166	u32 display_ix = get_display_ix(mgr);
167
168	/* check manager */
169	u32 ix = mgr ? mgr->id : cdev->num_mgrs;
170	if (ix >= cdev->num_mgrs || display_ix >= cdev->num_displays)
171		return ERR_PTR(-EINVAL);
172
173	/* allocate composition */
174	comp = kzalloc(sizeof(*comp), GFP_KERNEL);
175	if (!comp)
176		return NULL;
177
178	/* initialize new composition */
179	comp->ix = ix;	/* save where this composition came from */
180	comp->ovl_mask = comp->ovl_dmask = 0;
181	comp->frm.sync_id = 0;
182	comp->frm.mgr.ix = display_ix;
183	comp->state = DSSCOMP_STATE_ACTIVE;
184
185	DO_IF_DEBUG_FS({
186		__log_state(comp, dsscomp_new, 0);
187		list_add(&comp->dbg_q, &dbg_comps);
188	});
189
190	return comp;
191}
192EXPORT_SYMBOL(dsscomp_new);
193
194/* returns overlays used in a composition */
195u32 dsscomp_get_ovls(dsscomp_t comp)
196{
197	u32 mask;
198
199	mutex_lock(&mtx);
200	BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
201	mask = comp->ovl_mask;
202	mutex_unlock(&mtx);
203
204	return mask;
205}
206EXPORT_SYMBOL(dsscomp_get_ovls);
207
208/* set overlay info */
209int dsscomp_set_ovl(dsscomp_t comp, struct dss2_ovl_info *ovl)
210{
211	int r = -EBUSY;
212	u32 i, mask, oix, ix;
213	struct omap_overlay *o;
214
215	mutex_lock(&mtx);
216
217	BUG_ON(!ovl);
218	BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
219
220	ix = comp->ix;
221
222	if (ovl->cfg.ix >= cdev->num_ovls) {
223		r = -EINVAL;
224		goto done;
225	}
226
227	/* if overlay is already part of the composition */
228	mask = 1 << ovl->cfg.ix;
229	if (mask & comp->ovl_mask) {
230		/* look up overlay */
231		for (oix = 0; oix < comp->frm.num_ovls; oix++) {
232			if (comp->ovls[oix].cfg.ix == ovl->cfg.ix)
233				break;
234		}
235		BUG_ON(oix == comp->frm.num_ovls);
236	} else {
237		/* check if ovl is free to use */
238		if (comp->frm.num_ovls >= ARRAY_SIZE(comp->ovls))
239			goto done;
240
241		/* not in any other displays queue */
242		if (mask & ~mgrq[ix].ovl_qmask.mask) {
243			for (i = 0; i < cdev->num_mgrs; i++) {
244				if (i == ix)
245					continue;
246				if (mgrq[i].ovl_qmask.mask & mask)
247					goto done;
248			}
249		}
250
251		/* and disabled (unless forced) if on another manager */
252		o = cdev->ovls[ovl->cfg.ix];
253		if (o->info.enabled && (!o->manager || o->manager->id != ix))
254			goto done;
255
256		/* add overlay to composition & display */
257		comp->ovl_mask |= mask;
258		oix = comp->frm.num_ovls++;
259		maskref_incbit(&mgrq[ix].ovl_qmask, ovl->cfg.ix);
260	}
261
262	comp->ovls[oix] = *ovl;
263	r = 0;
264done:
265	mutex_unlock(&mtx);
266
267	return r;
268}
269EXPORT_SYMBOL(dsscomp_set_ovl);
270
271/* get overlay info */
272int dsscomp_get_ovl(dsscomp_t comp, u32 ix, struct dss2_ovl_info *ovl)
273{
274	int r;
275	u32 oix;
276
277	mutex_lock(&mtx);
278
279	BUG_ON(!ovl);
280	BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
281
282	if (ix >= cdev->num_ovls) {
283		r = -EINVAL;
284	} else if (comp->ovl_mask & (1 << ix)) {
285		r = 0;
286		for (oix = 0; oix < comp->frm.num_ovls; oix++)
287			if (comp->ovls[oix].cfg.ix == ovl->cfg.ix) {
288				*ovl = comp->ovls[oix];
289				break;
290			}
291		BUG_ON(oix == comp->frm.num_ovls);
292	} else {
293		r = -ENOENT;
294	}
295
296	mutex_unlock(&mtx);
297
298	return r;
299}
300EXPORT_SYMBOL(dsscomp_get_ovl);
301
302/* set manager info */
303int dsscomp_set_mgr(dsscomp_t comp, struct dss2_mgr_info *mgr)
304{
305	mutex_lock(&mtx);
306
307	BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
308	BUG_ON(mgr->ix != comp->frm.mgr.ix);
309
310	comp->frm.mgr = *mgr;
311
312	mutex_unlock(&mtx);
313
314	return 0;
315}
316EXPORT_SYMBOL(dsscomp_set_mgr);
317
318/* get manager info */
319int dsscomp_get_mgr(dsscomp_t comp, struct dss2_mgr_info *mgr)
320{
321	mutex_lock(&mtx);
322
323	BUG_ON(!mgr);
324	BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
325
326	*mgr = comp->frm.mgr;
327
328	mutex_unlock(&mtx);
329
330	return 0;
331}
332EXPORT_SYMBOL(dsscomp_get_mgr);
333
334/* get manager info */
335int dsscomp_setup(dsscomp_t comp, enum dsscomp_setup_mode mode,
336			struct dss2_rect_t win)
337{
338	mutex_lock(&mtx);
339
340	BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
341
342	comp->frm.mode = mode;
343	comp->frm.win = win;
344
345	mutex_unlock(&mtx);
346
347	return 0;
348}
349EXPORT_SYMBOL(dsscomp_setup);
350
351/*
352 * ===========================================================================
353 *		QUEUING COMMITTING OPERATIONS
354 * ===========================================================================
355 */
356void dsscomp_drop(dsscomp_t comp)
357{
358	/* decrement unprogrammed references */
359	if (comp->state < DSSCOMP_STATE_PROGRAMMED)
360		maskref_decmask(&mgrq[comp->ix].ovl_qmask, comp->ovl_mask);
361	comp->state = 0;
362
363	if (debug & DEBUG_COMPOSITIONS)
364		dev_info(DEV(cdev), "[%p] released\n", comp);
365
366	DO_IF_DEBUG_FS(list_del(&comp->dbg_q));
367
368	kfree(comp);
369}
370EXPORT_SYMBOL(dsscomp_drop);
371
372struct dsscomp_cb_work {
373	struct work_struct work;
374	struct dsscomp_data *comp;
375	int status;
376};
377
378static void dsscomp_mgr_delayed_cb(struct work_struct *work)
379{
380	struct dsscomp_cb_work *wk = container_of(work, typeof(*wk), work);
381	struct dsscomp_data *comp = wk->comp;
382	int status = wk->status;
383	u32 ix;
384
385	kfree(work);
386
387	mutex_lock(&mtx);
388
389	BUG_ON(comp->state == DSSCOMP_STATE_ACTIVE);
390	ix = comp->ix;
391
392	/* call extra callbacks if requested */
393	if (comp->extra_cb)
394		comp->extra_cb(comp->extra_cb_data, status);
395
396	/* handle programming & release */
397	if (status == DSS_COMPLETION_PROGRAMMED) {
398		comp->state = DSSCOMP_STATE_PROGRAMMED;
399		log_state(comp, dsscomp_mgr_delayed_cb, status);
400
401		/* update used overlay mask */
402		mgrq[ix].ovl_mask = comp->ovl_mask & ~comp->ovl_dmask;
403		maskref_decmask(&mgrq[ix].ovl_qmask, comp->ovl_mask);
404
405		if (debug & DEBUG_PHASES)
406			dev_info(DEV(cdev), "[%p] programmed\n", comp);
407	} else if ((status == DSS_COMPLETION_DISPLAYED) &&
408		   comp->state == DSSCOMP_STATE_PROGRAMMED) {
409		/* composition is 1st displayed */
410		comp->state = DSSCOMP_STATE_DISPLAYED;
411		log_state(comp, dsscomp_mgr_delayed_cb, status);
412		if (debug & DEBUG_PHASES)
413			dev_info(DEV(cdev), "[%p] displayed\n", comp);
414	} else if (status & DSS_COMPLETION_RELEASED) {
415		/* composition is no longer displayed */
416		log_event(20 * comp->ix + 20, 0, comp, "%pf on %s",
417				(u32) dsscomp_mgr_delayed_cb,
418				(u32) log_status_str(status));
419		dsscomp_drop(comp);
420	}
421	mutex_unlock(&mtx);
422}
423
424static u32 dsscomp_mgr_callback(void *data, int id, int status)
425{
426	struct dsscomp_data *comp = data;
427
428	if (status == DSS_COMPLETION_PROGRAMMED ||
429	    (status == DSS_COMPLETION_DISPLAYED &&
430	     comp->state != DSSCOMP_STATE_DISPLAYED) ||
431	    (status & DSS_COMPLETION_RELEASED)) {
432		struct dsscomp_cb_work *wk = kzalloc(sizeof(*wk), GFP_ATOMIC);
433		wk->comp = comp;
434		wk->status = status;
435		INIT_WORK(&wk->work, dsscomp_mgr_delayed_cb);
436		queue_work(cb_wkq, &wk->work);
437	}
438
439	/* get each callback only once */
440	return ~status;
441}
442
443static inline bool dssdev_manually_updated(struct omap_dss_device *dev)
444{
445	return dev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
446		dev->driver->get_update_mode(dev) != OMAP_DSS_UPDATE_AUTO;
447}
448
449/* apply composition */
450/* at this point the composition is not on any queue */
451static int dsscomp_apply(dsscomp_t comp)
452{
453	int i, r = -EFAULT;
454	u32 dmask, display_ix;
455	struct omap_dss_device *dssdev;
456	struct omap_dss_driver *drv;
457	struct omap_overlay_manager *mgr;
458	struct omap_overlay *ovl;
459	struct dsscomp_setup_mgr_data *d;
460	u32 oix;
461	bool cb_programmed = false;
462
463	struct omapdss_ovl_cb cb = {
464		.fn = dsscomp_mgr_callback,
465		.data = comp,
466		.mask = DSS_COMPLETION_DISPLAYED |
467		DSS_COMPLETION_PROGRAMMED | DSS_COMPLETION_RELEASED,
468	};
469
470	BUG_ON(comp->state != DSSCOMP_STATE_APPLYING);
471
472	/* check if the display is valid and used */
473	r = -ENODEV;
474	d = &comp->frm;
475	display_ix = d->mgr.ix;
476	if (display_ix >= cdev->num_displays)
477		goto done;
478	dssdev = cdev->displays[display_ix];
479	if (!dssdev)
480		goto done;
481
482	drv = dssdev->driver;
483	mgr = dssdev->manager;
484	if (!mgr || !drv || mgr->id >= cdev->num_mgrs)
485		goto done;
486
487	dump_comp_info(cdev, d, "apply");
488
489	r = 0;
490	dmask = 0;
491	for (oix = 0; oix < comp->frm.num_ovls; oix++) {
492		struct dss2_ovl_info *oi = comp->ovls + oix;
493
494		/* keep track of disabled overlays */
495		if (!oi->cfg.enabled)
496			dmask |= 1 << oi->cfg.ix;
497
498		if (r && !comp->must_apply)
499			continue;
500
501		dump_ovl_info(cdev, oi);
502
503		if (oi->cfg.ix >= cdev->num_ovls) {
504			r = -EINVAL;
505			continue;
506		}
507		ovl = cdev->ovls[oi->cfg.ix];
508
509		/* set overlays' manager & info */
510		if (ovl->info.enabled && ovl->manager != mgr) {
511			r = -EBUSY;
512			goto skip_ovl_set;
513		}
514		if (ovl->manager != mgr) {
515			/*
516			 * Ideally, we should call ovl->unset_manager(ovl),
517			 * but it may block on go even though the disabling
518			 * of the overlay already went through.  So instead,
519			 * we are just clearing the manager.
520			 */
521			ovl->manager = NULL;
522			r = ovl->set_manager(ovl, mgr);
523			if (r)
524				goto skip_ovl_set;
525		}
526
527		r = set_dss_ovl_info(oi);
528skip_ovl_set:
529		if (r && comp->must_apply) {
530			dev_err(DEV(cdev), "[%p] set ovl%d failed %d", comp,
531								oi->cfg.ix, r);
532			oi->cfg.enabled = false;
533			dmask |= 1 << oi->cfg.ix;
534			set_dss_ovl_info(oi);
535		}
536	}
537
538	/*
539	 * set manager's info - this also sets the completion callback,
540	 * so if it succeeds, we will use the callback to complete the
541	 * composition.  Otherwise, we can skip the composition now.
542	 */
543	if (!r || comp->must_apply) {
544		r = set_dss_mgr_info(&d->mgr, &cb);
545		cb_programmed = r == 0;
546	}
547
548	if (r && !comp->must_apply) {
549		dev_err(DEV(cdev), "[%p] set failed %d\n", comp, r);
550		goto done;
551	} else {
552		if (r)
553			dev_warn(DEV(cdev), "[%p] ignoring set failure %d\n",
554								comp, r);
555		comp->blank = dmask == comp->ovl_mask;
556		comp->ovl_dmask = dmask;
557
558		/*
559		 * Check other overlays that may also use this display.
560		 * NOTE: This is only needed in case someone changes
561		 * overlays via sysfs.  We use comp->ovl_mask to refresh
562		 * the overlays actually used on a manager when the
563		 * composition is programmed.
564		 */
565		for (i = 0; i < cdev->num_ovls; i++) {
566			u32 mask = 1 << i;
567			if ((~comp->ovl_mask & mask) &&
568			    cdev->ovls[i]->info.enabled &&
569			    cdev->ovls[i]->manager == mgr) {
570				mutex_lock(&mtx);
571				comp->ovl_mask |= mask;
572				maskref_incbit(&mgrq[comp->ix].ovl_qmask, i);
573				mutex_unlock(&mtx);
574			}
575		}
576	}
577
578	/* apply changes and call update on manual panels */
579	/* no need for mutex as no callbacks are scheduled yet */
580	comp->state = DSSCOMP_STATE_APPLIED;
581	log_state(comp, dsscomp_apply, 0);
582
583	if (!d->win.w && !d->win.x)
584		d->win.w = dssdev->panel.timings.x_res - d->win.x;
585	if (!d->win.h && !d->win.y)
586		d->win.h = dssdev->panel.timings.y_res - d->win.y;
587
588	mutex_lock(&mtx);
589	if (mgrq[comp->ix].blanking) {
590		pr_info_ratelimited("ignoring apply mgr(%s) while blanking\n",
591				    mgr->name);
592		r = -ENODEV;
593	} else {
594		r = mgr->apply(mgr);
595		if (r)
596			dev_err(DEV(cdev), "failed while applying %d", r);
597		/* keep error if set_mgr_info failed */
598		if (!r && !cb_programmed)
599			r = -EINVAL;
600	}
601	mutex_unlock(&mtx);
602
603	/*
604	 * TRICKY: try to unregister callback to see if callbacks have
605	 * been applied (moved into DSS2 pipeline).  Unregistering also
606	 * avoids having to unnecessarily kick out compositions (which
607	 * would result in screen blinking).  If callbacks failed to apply,
608	 * (e.g. could not set them or apply them) we will need to call
609	 * them ourselves (we note this by returning an error).
610	 */
611	if (cb_programmed && r) {
612		/* clear error if callback already registered */
613		if (omap_dss_manager_unregister_callback(mgr, &cb))
614			r = 0;
615	}
616	/* if failed to apply, kick out prior composition */
617	if (comp->must_apply && r)
618		mgr->blank(mgr, true);
619
620	if (!r && (d->mode & DSSCOMP_SETUP_MODE_DISPLAY)) {
621		/* cannot handle update errors, so ignore them */
622		if (dssdev_manually_updated(dssdev) && drv->update)
623			drv->update(dssdev, d->win.x,
624					d->win.y, d->win.w, d->win.h);
625		else
626			/* wait for sync to do smooth animations */
627			mgr->wait_for_vsync(mgr);
628	}
629
630done:
631	return r;
632}
633
634struct dsscomp_apply_work {
635	struct work_struct work;
636	dsscomp_t comp;
637};
638
639int dsscomp_state_notifier(struct notifier_block *nb,
640						unsigned long arg, void *ptr)
641{
642	struct omap_dss_device *dssdev = ptr;
643	enum omap_dss_display_state state = arg;
644	struct omap_overlay_manager *mgr = dssdev->manager;
645	if (mgr) {
646		mutex_lock(&mtx);
647		if (state == OMAP_DSS_DISPLAY_DISABLED) {
648			mgr->blank(mgr, true);
649			mgrq[mgr->id].blanking = true;
650		} else if (state == OMAP_DSS_DISPLAY_ACTIVE) {
651			mgrq[mgr->id].blanking = false;
652		}
653		mutex_unlock(&mtx);
654	}
655	return 0;
656}
657
658
659static void dsscomp_do_apply(struct work_struct *work)
660{
661	struct dsscomp_apply_work *wk = container_of(work, typeof(*wk), work);
662	/* complete compositions that failed to apply */
663	if (dsscomp_apply(wk->comp))
664		dsscomp_mgr_callback(wk->comp, -1, DSS_COMPLETION_ECLIPSED_SET);
665	kfree(wk);
666}
667
668int dsscomp_delayed_apply(dsscomp_t comp)
669{
670	/* don't block in case we are called from interrupt context */
671	struct dsscomp_apply_work *wk = kzalloc(sizeof(*wk), GFP_NOWAIT);
672	if (!wk)
673		return -ENOMEM;
674
675	mutex_lock(&mtx);
676
677	BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
678	comp->state = DSSCOMP_STATE_APPLYING;
679	log_state(comp, dsscomp_delayed_apply, 0);
680
681	if (debug & DEBUG_PHASES)
682		dev_info(DEV(cdev), "[%p] applying\n", comp);
683	mutex_unlock(&mtx);
684
685	wk->comp = comp;
686	INIT_WORK(&wk->work, dsscomp_do_apply);
687	return queue_work(mgrq[comp->ix].apply_workq, &wk->work) ? 0 : -EBUSY;
688}
689EXPORT_SYMBOL(dsscomp_delayed_apply);
690
691/*
692 * ===========================================================================
693 *		DEBUGFS
694 * ===========================================================================
695 */
696
697#ifdef CONFIG_DEBUG_FS
698void seq_print_comp(struct seq_file *s, dsscomp_t c)
699{
700	struct dsscomp_setup_mgr_data *d = &c->frm;
701	int i;
702
703	seq_printf(s, "  [%p]: %s%s\n", c, c->blank ? "blank " : "",
704		   c->state == DSSCOMP_STATE_ACTIVE ? "ACTIVE" :
705		   c->state == DSSCOMP_STATE_APPLYING ? "APPLYING" :
706		   c->state == DSSCOMP_STATE_APPLIED ? "APPLIED" :
707		   c->state == DSSCOMP_STATE_PROGRAMMED ? "PROGRAMMED" :
708		   c->state == DSSCOMP_STATE_DISPLAYED ? "DISPLAYED" :
709		   "???");
710	seq_printf(s, "    sync_id=%x, flags=%c%c%c\n",
711		   d->sync_id,
712		   (d->mode & DSSCOMP_SETUP_MODE_APPLY) ? 'A' : '-',
713		   (d->mode & DSSCOMP_SETUP_MODE_DISPLAY) ? 'D' : '-',
714		   (d->mode & DSSCOMP_SETUP_MODE_CAPTURE) ? 'C' : '-');
715	for (i = 0; i < d->num_ovls; i++) {
716		struct dss2_ovl_info *oi;
717		struct dss2_ovl_cfg *g;
718		oi = d->ovls + i;
719		g = &oi->cfg;
720		if (g->zonly) {
721			seq_printf(s, "    ovl%d={%s z%d}\n",
722				   g->ix, g->enabled ? "ON" : "off", g->zorder);
723		} else {
724			seq_printf(s, "    ovl%d={%s z%d %s%s *%d%%"
725						" %d*%d:%d,%d+%d,%d rot%d%s"
726						" => %d,%d+%d,%d %p/%p|%d}\n",
727				g->ix, g->enabled ? "ON" : "off", g->zorder,
728				dsscomp_get_color_name(g->color_mode) ? : "N/A",
729				g->pre_mult_alpha ? " premult" : "",
730				(g->global_alpha * 100 + 128) / 255,
731				g->width, g->height, g->crop.x, g->crop.y,
732				g->crop.w, g->crop.h,
733				g->rotation, g->mirror ? "+mir" : "",
734				g->win.x, g->win.y, g->win.w, g->win.h,
735				(void *) oi->ba, (void *) oi->uv, g->stride);
736		}
737	}
738	if (c->extra_cb)
739		seq_printf(s, "    gsync=[%p] %pf\n\n", c->extra_cb_data,
740								c->extra_cb);
741	else
742		seq_printf(s, "    gsync=[%p] (called)\n\n", c->extra_cb_data);
743}
744#endif
745
746void dsscomp_dbg_comps(struct seq_file *s)
747{
748#ifdef CONFIG_DEBUG_FS
749	dsscomp_t c;
750	u32 i;
751
752	mutex_lock(&dbg_mtx);
753	for (i = 0; i < cdev->num_mgrs; i++) {
754		struct omap_overlay_manager *mgr = cdev->mgrs[i];
755		seq_printf(s, "ACTIVE COMPOSITIONS on %s\n\n", mgr->name);
756		list_for_each_entry(c, &dbg_comps, dbg_q) {
757			struct dss2_mgr_info *mi = &c->frm.mgr;
758			if (mi->ix < cdev->num_displays &&
759			    cdev->displays[mi->ix]->manager == mgr)
760				seq_print_comp(s, c);
761		}
762
763		/* print manager cache */
764		mgr->dump_cb(mgr, s);
765	}
766	mutex_unlock(&dbg_mtx);
767#endif
768}
769
770void dsscomp_dbg_events(struct seq_file *s)
771{
772#ifdef CONFIG_DSSCOMP_DEBUG_LOG
773	u32 i;
774	struct dbg_event_t *d;
775
776	mutex_lock(&dbg_mtx);
777	for (i = dbg_event_ix; i < dbg_event_ix + ARRAY_SIZE(dbg_events); i++) {
778		d = dbg_events + (i % ARRAY_SIZE(dbg_events));
779		if (!d->ms)
780			continue;
781		seq_printf(s, "[% 5d.%03d] %*s[%08x] ",
782			   d->ms / 1000, d->ms % 1000,
783			   d->ix + ((u32) d->data) % 7,
784			   "", (u32) d->data);
785		seq_printf(s, d->fmt, d->a1, d->a2);
786		seq_printf(s, "\n");
787	}
788	mutex_unlock(&dbg_mtx);
789#endif
790}
791
792/*
793 * ===========================================================================
794 *		EXIT
795 * ===========================================================================
796 */
797void dsscomp_queue_exit(void)
798{
799	if (cdev) {
800		int i;
801		for (i = 0; i < cdev->num_displays; i++)
802			destroy_workqueue(mgrq[i].apply_workq);
803		destroy_workqueue(cb_wkq);
804		cdev = NULL;
805	}
806}
807EXPORT_SYMBOL(dsscomp_queue_exit);