aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/omap2/dsscomp/gralloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/omap2/dsscomp/gralloc.c')
-rw-r--r--drivers/video/omap2/dsscomp/gralloc.c607
1 files changed, 607 insertions, 0 deletions
diff --git a/drivers/video/omap2/dsscomp/gralloc.c b/drivers/video/omap2/dsscomp/gralloc.c
new file mode 100644
index 0000000..b75dfd9
--- /dev/null
+++ b/drivers/video/omap2/dsscomp/gralloc.c
@@ -0,0 +1,607 @@
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <mach/tiler.h>
+#include <video/dsscomp.h>
+#include <plat/dsscomp.h>
+#include "dsscomp.h"
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+static bool blanked;
+
+#define NUM_TILER1D_SLOTS 2
+#define TILER1D_SLOT_SIZE (16 << 20)
+
+static struct tiler1d_slot {
+ struct list_head q;
+ tiler_blk_handle slot;
+ u32 phys;
+ u32 size;
+ u32 *page_map;
+} slots[NUM_TILER1D_SLOTS];
+static struct list_head free_slots;
+static struct dsscomp_dev *cdev;
+static DEFINE_MUTEX(mtx);
+static struct semaphore free_slots_sem =
+ __SEMAPHORE_INITIALIZER(free_slots_sem, 0);
+
+/* gralloc composition sync object */
+struct dsscomp_gralloc_t {
+ void (*cb_fn)(void *, int);
+ void *cb_arg;
+ struct list_head q;
+ struct list_head slots;
+ atomic_t refs;
+ bool early_callback;
+ bool programmed;
+};
+
+/* queued gralloc compositions */
+static LIST_HEAD(flip_queue);
+
+static u32 ovl_use_mask[MAX_MANAGERS];
+
+static void unpin_tiler_blocks(struct list_head *slots)
+{
+ struct tiler1d_slot *slot;
+
+ /* unpin any tiler memory */
+ list_for_each_entry(slot, slots, q) {
+ tiler_unpin_block(slot->slot);
+ up(&free_slots_sem);
+ }
+
+ /* free tiler slots */
+ list_splice_init(slots, &free_slots);
+}
+
+static void dsscomp_gralloc_cb(void *data, int status)
+{
+ struct dsscomp_gralloc_t *gsync = data, *gsync_;
+ bool early_cbs = true;
+ LIST_HEAD(done);
+
+ mutex_lock(&mtx);
+ if (gsync->early_callback && status == DSS_COMPLETION_PROGRAMMED)
+ gsync->programmed = true;
+
+ if (status & DSS_COMPLETION_RELEASED) {
+ if (atomic_dec_and_test(&gsync->refs))
+ unpin_tiler_blocks(&gsync->slots);
+
+ log_event(0, 0, gsync, "--refs=%d on %s",
+ atomic_read(&gsync->refs),
+ (u32) log_status_str(status));
+ }
+
+ /* get completed list items in order, if any */
+ list_for_each_entry_safe(gsync, gsync_, &flip_queue, q) {
+ if (gsync->cb_fn) {
+ early_cbs &= gsync->early_callback && gsync->programmed;
+ if (early_cbs) {
+ gsync->cb_fn(gsync->cb_arg, 1);
+ gsync->cb_fn = NULL;
+ }
+ }
+ if (gsync->refs.counter && gsync->cb_fn)
+ break;
+ if (gsync->refs.counter == 0)
+ list_move_tail(&gsync->q, &done);
+ }
+ mutex_unlock(&mtx);
+
+ /* call back for completed composition with mutex unlocked */
+ list_for_each_entry_safe(gsync, gsync_, &done, q) {
+ if (debug & DEBUG_GRALLOC_PHASES)
+ dev_info(DEV(cdev), "[%p] completed flip\n", gsync);
+
+ log_event(0, 0, gsync, "calling %pf [%p]",
+ (u32) gsync->cb_fn, (u32) gsync->cb_arg);
+
+ if (gsync->cb_fn)
+ gsync->cb_fn(gsync->cb_arg, 1);
+ kfree(gsync);
+ }
+}
+
+/* This is just test code for now that does the setup + apply.
+ It still uses userspace virtual addresses, but maps non
+ TILER buffers into 1D */
+int dsscomp_gralloc_queue_ioctl(struct dsscomp_setup_dispc_data *d)
+{
+ struct tiler_pa_info *pas[MAX_OVERLAYS];
+ s32 ret;
+ u32 i;
+
+ if (d->num_ovls > MAX_OVERLAYS)
+ return -EINVAL;
+
+ /* convert virtual addresses to physical and get tiler pa infos */
+ for (i = 0; i < d->num_ovls; i++) {
+ struct dss2_ovl_info *oi = d->ovls + i;
+ u32 addr = (u32) oi->address;
+
+ pas[i] = NULL;
+
+ /* assume virtual NV12 for now */
+ if (oi->cfg.color_mode == OMAP_DSS_COLOR_NV12)
+ oi->uv = tiler_virt2phys(addr +
+ oi->cfg.height * oi->cfg.stride);
+ else
+ oi->uv = 0;
+ oi->ba = tiler_virt2phys(addr);
+
+ /* map non-TILER buffers to 1D */
+ if ((oi->ba < 0x60000000 || oi->ba >= 0x80000000) && oi->ba)
+ pas[i] = user_block_to_pa(addr & PAGE_MASK,
+ PAGE_ALIGN(oi->cfg.height * oi->cfg.stride +
+ (addr & ~PAGE_MASK)) >> PAGE_SHIFT);
+ }
+ ret = dsscomp_gralloc_queue(d, pas, false, NULL, NULL);
+ for (i = 0; i < d->num_ovls; i++)
+ tiler_pa_free(pas[i]);
+ return ret;
+}
+
+int dsscomp_gralloc_queue(struct dsscomp_setup_dispc_data *d,
+ struct tiler_pa_info **pas,
+ bool early_callback,
+ void (*cb_fn)(void *, int), void *cb_arg)
+{
+ u32 i;
+ int r = 0;
+ struct omap_dss_device *dev;
+ struct omap_overlay_manager *mgr;
+ static DEFINE_MUTEX(local_mtx);
+ dsscomp_t comp[MAX_MANAGERS];
+ u32 ovl_new_use_mask[MAX_MANAGERS];
+ u32 mgr_set_mask = 0;
+ u32 ovl_set_mask = 0;
+ struct tiler1d_slot *slot = NULL;
+ u32 slot_used = 0;
+#ifdef CONFIG_DEBUG_FS
+ u32 ms = ktime_to_ms(ktime_get());
+#endif
+ u32 channels[ARRAY_SIZE(d->mgrs)], ch;
+ int skip;
+ struct dsscomp_gralloc_t *gsync;
+ struct dss2_rect_t win = { .w = 0 };
+
+ /* reserve tiler areas if not already done so */
+ dsscomp_gralloc_init(cdev);
+
+ dump_total_comp_info(cdev, d, "queue");
+ for (i = 0; i < d->num_ovls; i++)
+ dump_ovl_info(cdev, d->ovls + i);
+
+ mutex_lock(&local_mtx);
+
+ mutex_lock(&mtx);
+
+ /* create sync object with 1 temporary ref */
+ gsync = kzalloc(sizeof(*gsync), GFP_KERNEL);
+ gsync->cb_arg = cb_arg;
+ gsync->cb_fn = cb_fn;
+ gsync->refs.counter = 1;
+ gsync->early_callback = early_callback;
+ INIT_LIST_HEAD(&gsync->slots);
+ list_add_tail(&gsync->q, &flip_queue);
+ if (debug & DEBUG_GRALLOC_PHASES)
+ dev_info(DEV(cdev), "[%p] queuing flip\n", gsync);
+
+ log_event(0, ms, gsync, "new in %pf (refs=1)",
+ (u32) dsscomp_gralloc_queue, 0);
+
+ /* ignore frames while we are blanked */
+ skip = blanked;
+ if (skip && (debug & DEBUG_PHASES))
+ dev_info(DEV(cdev), "[%p,%08x] ignored\n", gsync, d->sync_id);
+
+ /* mark blank frame by NULL tiler pa pointer */
+ if (!skip && pas == NULL)
+ blanked = true;
+
+ mutex_unlock(&mtx);
+
+ d->num_mgrs = min(d->num_mgrs, (u16) ARRAY_SIZE(d->mgrs));
+ d->num_ovls = min(d->num_ovls, (u16) ARRAY_SIZE(d->ovls));
+
+ memset(comp, 0, sizeof(comp));
+ memset(ovl_new_use_mask, 0, sizeof(ovl_new_use_mask));
+
+ if (skip)
+ goto skip_comp;
+
+ d->mode = DSSCOMP_SETUP_DISPLAY;
+
+ /* mark managers we are using */
+ for (i = 0; i < d->num_mgrs; i++) {
+ /* verify display is valid & connected, ignore if not */
+ if (d->mgrs[i].ix >= cdev->num_displays)
+ continue;
+ dev = cdev->displays[d->mgrs[i].ix];
+ if (!dev) {
+ dev_warn(DEV(cdev), "failed to get display%d\n",
+ d->mgrs[i].ix);
+ continue;
+ }
+ mgr = dev->manager;
+ if (!mgr) {
+ dev_warn(DEV(cdev), "no manager for display%d\n",
+ d->mgrs[i].ix);
+ continue;
+ }
+ channels[i] = ch = mgr->id;
+ mgr_set_mask |= 1 << ch;
+
+ /* swap red & blue if requested */
+ if (d->mgrs[i].swap_rb)
+ swap_rb_in_mgr_info(d->mgrs + i);
+ }
+
+ /* create dsscomp objects for set managers (including active ones) */
+ for (ch = 0; ch < MAX_MANAGERS; ch++) {
+ if (!(mgr_set_mask & (1 << ch)) && !ovl_use_mask[ch])
+ continue;
+
+ mgr = cdev->mgrs[ch];
+
+ comp[ch] = dsscomp_new(mgr);
+ if (IS_ERR(comp[ch])) {
+ comp[ch] = NULL;
+ dev_warn(DEV(cdev), "failed to get composition on %s\n",
+ mgr->name);
+ continue;
+ }
+
+ /* set basic manager information for blanked managers */
+ if (!(mgr_set_mask & (1 << ch))) {
+ struct dss2_mgr_info mi = {
+ .alpha_blending = true,
+ .ix = comp[ch]->frm.mgr.ix,
+ };
+ dsscomp_set_mgr(comp[ch], &mi);
+ }
+
+ comp[ch]->must_apply = true;
+ r = dsscomp_setup(comp[ch], d->mode, win);
+ if (r)
+ dev_err(DEV(cdev), "failed to setup comp (%d)\n", r);
+ }
+
+ /* configure manager data from gralloc composition */
+ for (i = 0; i < d->num_mgrs; i++) {
+ ch = channels[i];
+ r = dsscomp_set_mgr(comp[ch], d->mgrs + i);
+ if (r)
+ dev_err(DEV(cdev), "failed to set mgr%d (%d)\n", ch, r);
+ }
+
+ /* NOTE: none of the dsscomp sets should fail as composition is new */
+ for (i = 0; i < d->num_ovls; i++) {
+ struct dss2_ovl_info *oi = d->ovls + i;
+ u32 mgr_ix = oi->cfg.mgr_ix;
+ u32 size;
+
+ /* verify manager index */
+ if (mgr_ix >= d->num_mgrs) {
+ dev_err(DEV(cdev), "invalid manager for ovl%d\n",
+ oi->cfg.ix);
+ continue;
+ }
+ ch = channels[mgr_ix];
+
+ /* skip overlays on compositions we could not create */
+ if (!comp[ch])
+ continue;
+
+ /* swap red & blue if requested */
+ if (d->mgrs[mgr_ix].swap_rb)
+ swap_rb_in_ovl_info(d->ovls + i);
+
+ /* copy prior overlay to avoid mapping layers twice to 1D */
+ if (oi->addressing == OMAP_DSS_BUFADDR_OVL_IX) {
+ unsigned int j = oi->ba;
+ if (j >= i) {
+ WARN(1, "Invalid clone layer (%u)", j);
+ goto skip_buffer;
+ }
+
+ oi->ba = d->ovls[j].ba;
+ oi->uv = d->ovls[j].uv;
+ goto skip_map1d;
+ } else if (oi->addressing == OMAP_DSS_BUFADDR_FB) {
+ /* get fb */
+ int fb_ix = (oi->ba >> 28);
+ int fb_uv_ix = (oi->uv >> 28);
+ struct fb_info *fbi = NULL, *fbi_uv = NULL;
+ size_t size = oi->cfg.height * oi->cfg.stride;
+ if (fb_ix >= num_registered_fb ||
+ (oi->cfg.color_mode == OMAP_DSS_COLOR_NV12 &&
+ fb_uv_ix >= num_registered_fb)) {
+ WARN(1, "display has no framebuffer");
+ goto skip_buffer;
+ }
+
+ fbi = fbi_uv = registered_fb[fb_ix];
+ if (oi->cfg.color_mode == OMAP_DSS_COLOR_NV12)
+ fbi_uv = registered_fb[fb_uv_ix];
+
+ if (size + oi->ba > fbi->fix.smem_len ||
+ (oi->cfg.color_mode == OMAP_DSS_COLOR_NV12 &&
+ (size >> 1) + oi->uv > fbi_uv->fix.smem_len)) {
+ WARN(1, "image outside of framebuffer memory");
+ goto skip_buffer;
+ }
+
+ oi->ba += fbi->fix.smem_start;
+ oi->uv += fbi_uv->fix.smem_start;
+ goto skip_map1d;
+ }
+
+ /* map non-TILER buffers to 1D */
+
+ /* skip 2D and disabled layers */
+ if (!pas[i] || !oi->cfg.enabled)
+ goto skip_map1d;
+
+ if (!slot) {
+ if (down_timeout(&free_slots_sem,
+ msecs_to_jiffies(100))) {
+ dev_warn(DEV(cdev), "could not obtain tiler slot");
+ goto skip_buffer;
+ }
+ mutex_lock(&mtx);
+ slot = list_first_entry(&free_slots, typeof(*slot), q);
+ list_move(&slot->q, &gsync->slots);
+ mutex_unlock(&mtx);
+ }
+
+ size = oi->cfg.stride * oi->cfg.height;
+ if (oi->cfg.color_mode == OMAP_DSS_COLOR_NV12)
+ size += size >> 2;
+ size = DIV_ROUND_UP(size, PAGE_SIZE);
+
+ if (slot_used + size > slot->size) {
+ dev_err(DEV(cdev), "tiler slot not big enough for frame %d + %d > %d",
+ slot_used, size, slot->size);
+ goto skip_buffer;
+ }
+
+ /* "map" into TILER 1D - will happen after loop */
+ oi->ba = slot->phys + (slot_used << PAGE_SHIFT) +
+ (oi->ba & ~PAGE_MASK);
+ memcpy(slot->page_map + slot_used, pas[i]->mem,
+ sizeof(*slot->page_map) * size);
+ slot_used += size;
+ goto skip_map1d;
+
+skip_buffer:
+ oi->cfg.enabled = false;
+skip_map1d:
+
+ if (oi->cfg.enabled)
+ ovl_new_use_mask[ch] |= 1 << oi->cfg.ix;
+
+ r = dsscomp_set_ovl(comp[ch], oi);
+ if (r)
+ dev_err(DEV(cdev), "failed to set ovl%d (%d)\n",
+ oi->cfg.ix, r);
+ else
+ ovl_set_mask |= 1 << oi->cfg.ix;
+ }
+
+ if (slot && slot_used) {
+ r = tiler_pin_block(slot->slot, slot->page_map,
+ slot_used);
+ if (r)
+ dev_err(DEV(cdev), "failed to pin %d pages into"
+ " %d-pg slots (%d)\n", slot_used,
+ TILER1D_SLOT_SIZE >> PAGE_SHIFT, r);
+ }
+
+ for (ch = 0; ch < MAX_MANAGERS; ch++) {
+ /* disable all overlays not specifically set from prior frame */
+ u32 mask = ovl_use_mask[ch] & ~ovl_set_mask;
+
+ if (!comp[ch])
+ continue;
+
+ while (mask) {
+ struct dss2_ovl_info oi = {
+ .cfg.zonly = true,
+ .cfg.enabled = false,
+ .cfg.ix = fls(mask) - 1,
+ };
+ dsscomp_set_ovl(comp[ch], &oi);
+ mask &= ~(1 << oi.cfg.ix);
+ }
+
+ /* associate dsscomp objects with this gralloc composition */
+ comp[ch]->extra_cb = dsscomp_gralloc_cb;
+ comp[ch]->extra_cb_data = gsync;
+ atomic_inc(&gsync->refs);
+ log_event(0, ms, gsync, "++refs=%d for [%p]",
+ atomic_read(&gsync->refs), (u32) comp[ch]);
+
+ r = dsscomp_delayed_apply(comp[ch]);
+ if (r)
+ dev_err(DEV(cdev), "failed to apply comp (%d)\n", r);
+ else
+ ovl_use_mask[ch] = ovl_new_use_mask[ch];
+ }
+skip_comp:
+ /* release sync object ref - this completes unapplied compositions */
+ dsscomp_gralloc_cb(gsync, DSS_COMPLETION_RELEASED);
+
+ mutex_unlock(&local_mtx);
+
+ return r;
+}
+
+#ifdef CONFIG_EARLYSUSPEND
+static int blank_complete;
+static DECLARE_WAIT_QUEUE_HEAD(early_suspend_wq);
+
+static void dsscomp_early_suspend_cb(void *data, int status)
+{
+ blank_complete = true;
+ wake_up(&early_suspend_wq);
+}
+
+static void dsscomp_early_suspend(struct early_suspend *h)
+{
+ struct dsscomp_setup_dispc_data d = {
+ .num_mgrs = 0,
+ };
+ int err;
+
+ pr_info("DSSCOMP: %s\n", __func__);
+
+ /* use gralloc queue as we need to blank all screens */
+ blank_complete = false;
+ dsscomp_gralloc_queue(&d, NULL, false, dsscomp_early_suspend_cb, NULL);
+
+ /* wait until composition is displayed */
+ err = wait_event_timeout(early_suspend_wq, blank_complete,
+ msecs_to_jiffies(500));
+ if (err == 0)
+ pr_warn("DSSCOMP: timeout blanking screen\n");
+ else
+ pr_info("DSSCOMP: blanked screen\n");
+}
+
+static void dsscomp_late_resume(struct early_suspend *h)
+{
+ pr_info("DSSCOMP: %s\n", __func__);
+ blanked = false;
+}
+
+static struct early_suspend early_suspend_info = {
+ .suspend = dsscomp_early_suspend,
+ .resume = dsscomp_late_resume,
+ .level = EARLY_SUSPEND_LEVEL_DISABLE_FB,
+};
+#endif
+
+void dsscomp_dbg_gralloc(struct seq_file *s)
+{
+#ifdef CONFIG_DEBUG_FS
+ struct dsscomp_gralloc_t *g;
+ struct tiler1d_slot *t;
+ dsscomp_t c;
+ int i;
+
+ mutex_lock(&dbg_mtx);
+ seq_printf(s, "ACTIVE GRALLOC FLIPS\n\n");
+ list_for_each_entry(g, &flip_queue, q) {
+ char *sep = "";
+ seq_printf(s, " [%p] (refs=%d)\n"
+ " slots=[", g, atomic_read(&g->refs));
+ list_for_each_entry(t, &g->slots, q) {
+ seq_printf(s, "%s%08x", sep, t->phys);
+ sep = ", ";
+ }
+ seq_printf(s, "]\n cmdcb=[%08x] ", (u32) g->cb_arg);
+ if (g->cb_fn)
+ seq_printf(s, "%pf\n\n ", g->cb_fn);
+ else
+ seq_printf(s, "(called)\n\n ");
+
+ list_for_each_entry(c, &dbg_comps, dbg_q) {
+ if (c->extra_cb && c->extra_cb_data == g)
+ seq_printf(s, "| %8s ",
+ cdev->mgrs[c->ix]->name);
+ }
+ seq_printf(s, "\n ");
+ list_for_each_entry(c, &dbg_comps, dbg_q) {
+ if (c->extra_cb && c->extra_cb_data == g)
+ seq_printf(s, "| [%08x] %7s ", (u32) c,
+ log_state_str(c->state));
+ }
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+ for (i = 0; i < ARRAY_SIZE(c->dbg_log); i++) {
+ int go = false;
+ seq_printf(s, "\n ");
+ list_for_each_entry(c, &dbg_comps, dbg_q) {
+ if (!c->extra_cb || c->extra_cb_data != g)
+ continue;
+ if (i < c->dbg_used) {
+ u32 t = c->dbg_log[i].t;
+ u32 state = c->dbg_log[i].state;
+ seq_printf(s, "| % 6d.%03d %7s ",
+ t / 1000, t % 1000,
+ log_state_str(state));
+ go |= c->dbg_used > i + 1;
+ } else {
+ seq_printf(s, "%-21s", "|");
+ }
+ }
+ if (!go)
+ break;
+ }
+#endif
+ seq_printf(s, "\n\n");
+ }
+ seq_printf(s, "\n");
+ mutex_unlock(&dbg_mtx);
+#endif
+}
+
+void dsscomp_gralloc_init(struct dsscomp_dev *cdev_)
+{
+ int i;
+
+ /* save at least cdev pointer */
+ if (!cdev && cdev_) {
+ cdev = cdev_;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ register_early_suspend(&early_suspend_info);
+#endif
+ }
+
+ if (!free_slots.next) {
+ INIT_LIST_HEAD(&free_slots);
+ for (i = 0; i < NUM_TILER1D_SLOTS; i++) {
+ u32 phys;
+ tiler_blk_handle slot =
+ tiler_alloc_block_area(TILFMT_PAGE,
+ TILER1D_SLOT_SIZE, 1, &phys, NULL);
+ if (IS_ERR_OR_NULL(slot)) {
+ pr_err("could not allocate slot");
+ break;
+ }
+ slots[i].slot = slot;
+ slots[i].phys = phys;
+ slots[i].size = TILER1D_SLOT_SIZE >> PAGE_SHIFT;
+ slots[i].page_map = kmalloc(sizeof(*slots[i].page_map) *
+ slots[i].size, GFP_KERNEL);
+ if (!slots[i].page_map) {
+ pr_err("could not allocate page_map");
+ tiler_free_block_area(slot);
+ break;
+ }
+ list_add(&slots[i].q, &free_slots);
+ up(&free_slots_sem);
+ }
+ /* reset free_slots if no TILER memory could be reserved */
+ if (!i)
+ ZERO(free_slots);
+ }
+}
+
+void dsscomp_gralloc_exit(void)
+{
+ struct tiler1d_slot *slot;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&early_suspend_info);
+#endif
+
+ list_for_each_entry(slot, &free_slots, q)
+ tiler_free_block_area(slot->slot);
+ INIT_LIST_HEAD(&free_slots);
+}