summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 9ff395f)
raw | patch | inline | side by side (parent: 9ff395f)
author | Lajos Molnar <molnar@ti.com> | |
Wed, 27 Mar 2013 17:44:05 +0000 (12:44 -0500) | ||
committer | Praneeth Bajjuri <praneeth@ti.com> | |
Fri, 12 Jul 2013 22:41:41 +0000 (17:41 -0500) |
This patch implements a new DSS composition module.
DSSCOMP allows specifying a whole composition for a DSS display:
1. set overlay information for all overlays on a manager
2. reroute the overlays to the manager (overlay must be disabled
just as when using sysfs)
3. set manager information
4. optionally call manager->apply() that programs the DISPC
4. optionally do an update (after a sync call)
DSS already implements coordinated updates by separating overlay
information setting from the applicaion of those settings (which
happens only in manager->apply()). However current clients of DSS
call manager->apply() for each change, which make coordinated
change impossible.
This API also implements auto cropping of all layers to the display
region. This makes switching displays and handling display resolution
changes easier (without getting "failed to setup overlay" messages.)
DSSCOMP operates on 3 levels.
base.c contains the basic DSS operations, such as setting DSS overlay
and managers using DSSCOMP's setting structures. Theoretically,
DSSCOMP could be used via only these operations.
queue.c contains the queuing mechanism. This module maintains
compositions queued to each overlay manager (the basic DSS composition
entity). Queueing operations consist of creating a composition,
setting/getting manager/overlay information for the composition,
applying the composition to the display (which also displays it
on manually updated panels), waiting on various states of a composition.
For now the basic queuing mechanism of DSSCOMP is "queue and forget".
Therefore, it is not necessary to dequeue each frame queued. A
consequence of this methodology is that if one applies a composition
to a display, any prior unapplied compositions will be dropped.
Compositions are applied asynchronously (delayed apply) so that
they can be queued from irq context.
The queuing interface tracks which overlay is assigned to which
manager. This is done at the DSS programming level, as that is the
most reliable place to monitor overlay ownership. Nonetheless,
the device interface uses overlay information to verify overlay
ownership - which may be slightly out of sync. The user of
DSSCOMP should maintain overlay ownership to ensure flawless
sharing of overlays between managers. (E.g. should not use an
overlay on a new manager, until the overlay has been disabled on
the previous manager, and that composition has been programmed.)
Buffers must be displayable (e.g. TILER2D or physically contiguous).
device.c contains the device hooks to operating system, and the file
interface (via /dev/dsscomp's ioctls). /dev/dsscomp works on top of
the queueing mechanism.
There are 3 levels of header files.
linux/dsscomp.h: basic dsscomp structures and ioctls
plat/dsscomp.h: kernel dsscomp interface (on top of linux/dsscomp.h)
local dsscomp.h: common implementation structures and shared methods
An additional Android adaptation layer is also provided. This
sits on top of DSSCOMP and allows specifying all overlays on all
managers in a single operation as required by Android's rendering
model. Here the checking of overlays is slightly relaxed as
Android makes sure that an overlay is detached from a display
before it is attached to another one. This interface also
uses ION/TILER to remap non-contiguous buffers into contiguous
TILER1D space - so they can be displayed by DISPC.
At the Android level buffers can be specified with a set of
pages, using offsets into FBMEM (contiguous), or by referencing
prior overlays (e.g. cloning).
Note: plat/dsscomp.h defines a handle typedef that causes a
checkpatch warning. I feel that the creation of a handle typedef
is warranted.
Limitations:
- no WB support
- unsure whether to call sync on non-manual update panels
- cannot get overlay/manager information on a composition without
first having set it
Change-Id: I9e9c0fc6e5ed0bf4f077af9f755cee35d436990b
Signed-off-by: Arthur Philpott <arthur.philpott@ti.com>
Signed-off-by: Dandawate Saket <dsaket@ti.com>
DSSCOMP allows specifying a whole composition for a DSS display:
1. set overlay information for all overlays on a manager
2. reroute the overlays to the manager (overlay must be disabled
just as when using sysfs)
3. set manager information
4. optionally call manager->apply() that programs the DISPC
4. optionally do an update (after a sync call)
DSS already implements coordinated updates by separating overlay
information setting from the applicaion of those settings (which
happens only in manager->apply()). However current clients of DSS
call manager->apply() for each change, which make coordinated
change impossible.
This API also implements auto cropping of all layers to the display
region. This makes switching displays and handling display resolution
changes easier (without getting "failed to setup overlay" messages.)
DSSCOMP operates on 3 levels.
base.c contains the basic DSS operations, such as setting DSS overlay
and managers using DSSCOMP's setting structures. Theoretically,
DSSCOMP could be used via only these operations.
queue.c contains the queuing mechanism. This module maintains
compositions queued to each overlay manager (the basic DSS composition
entity). Queueing operations consist of creating a composition,
setting/getting manager/overlay information for the composition,
applying the composition to the display (which also displays it
on manually updated panels), waiting on various states of a composition.
For now the basic queuing mechanism of DSSCOMP is "queue and forget".
Therefore, it is not necessary to dequeue each frame queued. A
consequence of this methodology is that if one applies a composition
to a display, any prior unapplied compositions will be dropped.
Compositions are applied asynchronously (delayed apply) so that
they can be queued from irq context.
The queuing interface tracks which overlay is assigned to which
manager. This is done at the DSS programming level, as that is the
most reliable place to monitor overlay ownership. Nonetheless,
the device interface uses overlay information to verify overlay
ownership - which may be slightly out of sync. The user of
DSSCOMP should maintain overlay ownership to ensure flawless
sharing of overlays between managers. (E.g. should not use an
overlay on a new manager, until the overlay has been disabled on
the previous manager, and that composition has been programmed.)
Buffers must be displayable (e.g. TILER2D or physically contiguous).
device.c contains the device hooks to operating system, and the file
interface (via /dev/dsscomp's ioctls). /dev/dsscomp works on top of
the queueing mechanism.
There are 3 levels of header files.
linux/dsscomp.h: basic dsscomp structures and ioctls
plat/dsscomp.h: kernel dsscomp interface (on top of linux/dsscomp.h)
local dsscomp.h: common implementation structures and shared methods
An additional Android adaptation layer is also provided. This
sits on top of DSSCOMP and allows specifying all overlays on all
managers in a single operation as required by Android's rendering
model. Here the checking of overlays is slightly relaxed as
Android makes sure that an overlay is detached from a display
before it is attached to another one. This interface also
uses ION/TILER to remap non-contiguous buffers into contiguous
TILER1D space - so they can be displayed by DISPC.
At the Android level buffers can be specified with a set of
pages, using offsets into FBMEM (contiguous), or by referencing
prior overlays (e.g. cloning).
Note: plat/dsscomp.h defines a handle typedef that causes a
checkpatch warning. I feel that the creation of a handle typedef
is warranted.
Limitations:
- no WB support
- unsure whether to call sync on non-manual update panels
- cannot get overlay/manager information on a composition without
first having set it
Change-Id: I9e9c0fc6e5ed0bf4f077af9f755cee35d436990b
Signed-off-by: Arthur Philpott <arthur.philpott@ti.com>
Signed-off-by: Dandawate Saket <dsaket@ti.com>
13 files changed:
arch/arm/plat-omap/include/plat/dsscomp.h | [new file with mode: 0644] | patch | blob |
drivers/video/omap2/Kconfig | patch | blob | history | |
drivers/video/omap2/Makefile | patch | blob | history | |
drivers/video/omap2/dsscomp/Kconfig | [new file with mode: 0644] | patch | blob |
drivers/video/omap2/dsscomp/Makefile | [new file with mode: 0644] | patch | blob |
drivers/video/omap2/dsscomp/base.c | [new file with mode: 0755] | patch | blob |
drivers/video/omap2/dsscomp/device.c | [new file with mode: 0755] | patch | blob |
drivers/video/omap2/dsscomp/dsscomp.h | [new file with mode: 0644] | patch | blob |
drivers/video/omap2/dsscomp/gralloc.c | [new file with mode: 0755] | patch | blob |
drivers/video/omap2/dsscomp/queue.c | [new file with mode: 0755] | patch | blob |
drivers/video/omap2/dsscomp/tiler-utils.c | [new file with mode: 0644] | patch | blob |
drivers/video/omap2/dsscomp/tiler-utils.h | [new file with mode: 0644] | patch | blob |
include/video/dsscomp.h | [new file with mode: 0644] | patch | blob |
diff --git a/arch/arm/plat-omap/include/plat/dsscomp.h b/arch/arm/plat-omap/include/plat/dsscomp.h
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * arch/arm/plat-omap/include/plat/dsscomp.h
+ *
+ * DSS Composition platform specific header
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc
+ * Author: Lajos Molnar <molnar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _ARCH_ARM_PLAT_OMAP_DSSCOMP_H
+#define _ARCH_ARM_PLAT_OMAP_DSSCOMP_H
+
+#include <video/omapdss.h>
+
+/* queuing operations */
+struct dsscomp;
+struct dsscomp *dsscomp_new(struct omap_overlay_manager *mgr);
+u32 dsscomp_get_ovls(struct dsscomp *comp);
+int dsscomp_set_ovl(struct dsscomp *comp, struct dss2_ovl_info *ovl);
+int dsscomp_get_ovl(struct dsscomp *comp, u32 ix, struct dss2_ovl_info *ovl);
+int dsscomp_set_mgr(struct dsscomp *comp, struct dss2_mgr_info *mgr);
+int dsscomp_get_mgr(struct dsscomp *comp, struct dss2_mgr_info *mgr);
+int dsscomp_setup(struct dsscomp *comp, enum dsscomp_setup_mode mode,
+ struct dss2_rect_t win);
+int dsscomp_delayed_apply(struct dsscomp *comp);
+void dsscomp_drop(struct dsscomp *c);
+
+struct tiler_pa_info;
+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);
+#endif
index b07b2b042e7e242e6ff515ef0348912106f8cccb..313b80fd7d345a29dbc41fe16b72c12ac68d49f7 100644 (file)
source "drivers/video/omap2/dss/Kconfig"
source "drivers/video/omap2/omapfb/Kconfig"
source "drivers/video/omap2/displays/Kconfig"
+source "drivers/video/omap2/dsscomp/Kconfig"
endif
index 5ea7cb9aed17b9d666bfdba808b889d745fa8ffb..f70f06626cd27ebdc646dfa8ce71bd6e2c6846e2 100644 (file)
obj-$(CONFIG_OMAP2_DSS) += dss/
obj-$(CONFIG_FB_OMAP2) += omapfb/
obj-y += displays/
+obj-y += dsscomp/
diff --git a/drivers/video/omap2/dsscomp/Kconfig b/drivers/video/omap2/dsscomp/Kconfig
--- /dev/null
@@ -0,0 +1,21 @@
+menuconfig DSSCOMP
+ tristate "OMAP DSS Composition support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && OMAP2_DSS && DRM_OMAP_DMM_TILER
+ default n
+
+ help
+ Frame composition driver using OMAP DSS2. Allows using
+ all DSS2 resources in a unified configuration. Should not
+ be used together with other DSS2 devices, such as V4L2
+ or OMAPFB.
+
+config DSSCOMP_DEBUG_LOG
+ bool "Log event timestamps in debugfs"
+ default n
+ depends on DEBUG_FS && DSSCOMP
+
+ help
+ Takes timestamp for each callback and state transition, and
+ logs the last 128 entries (last few frames' worth) in a
+ log buffer. This is a separate menuconfig in case this is
+ deemed an overhead.
diff --git a/drivers/video/omap2/dsscomp/Makefile b/drivers/video/omap2/dsscomp/Makefile
--- /dev/null
@@ -0,0 +1,4 @@
+obj-$(CONFIG_DSSCOMP) += dsscomp.o
+dsscomp-y := device.o base.o queue.o
+dsscomp-y += gralloc.o
+dsscomp-y += tiler-utils.o
diff --git a/drivers/video/omap2/dsscomp/base.c b/drivers/video/omap2/dsscomp/base.c
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * linux/drivers/video/omap2/dsscomp/base.c
+ *
+ * DSS Composition basic operation support
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc
+ * Author: Lajos Molnar <molnar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+
+#include <linux/notifier.h>
+#include "../../../drivers/gpu/drm/omapdrm/omap_dmm_tiler.h"
+
+#include <video/omapdss.h>
+#include <video/dsscomp.h>
+#include <plat/dsscomp.h>
+
+#include "dsscomp.h"
+
+int debug;
+module_param(debug, int, 0644);
+
+/* color formats supported - bitfield info is used for truncation logic */
+static const struct color_info {
+ int a_ix, a_bt; /* bitfields */
+ int r_ix, r_bt;
+ int g_ix, g_bt;
+ int b_ix, b_bt;
+ int x_bt;
+ enum omap_color_mode mode;
+ const char *name;
+} fmts[2][16] = { {
+ { 0, 0, 0, 0, 0, 0, 0, 0, 1, OMAP_DSS_COLOR_CLUT1, "BITMAP1" },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 2, OMAP_DSS_COLOR_CLUT2, "BITMAP2" },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 4, OMAP_DSS_COLOR_CLUT4, "BITMAP4" },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 8, OMAP_DSS_COLOR_CLUT8, "BITMAP8" },
+ { 0, 0, 8, 4, 4, 4, 0, 4, 4, OMAP_DSS_COLOR_RGB12U, "xRGB12-4444" },
+ { 12, 4, 8, 4, 4, 4, 0, 4, 0, OMAP_DSS_COLOR_ARGB16, "ARGB16-4444" },
+ { 0, 0, 11, 5, 5, 6, 0, 5, 0, OMAP_DSS_COLOR_RGB16, "RGB16-565" },
+ { 15, 1, 10, 5, 5, 5, 0, 5, 0, OMAP_DSS_COLOR_ARGB16_1555,
+ "ARGB16-1555" },
+ { 0, 0, 16, 8, 8, 8, 0, 8, 8, OMAP_DSS_COLOR_RGB24U, "xRGB24-8888" },
+ { 0, 0, 16, 8, 8, 8, 0, 8, 0, OMAP_DSS_COLOR_RGB24P, "RGB24-888" },
+ { 0, 0, 12, 4, 8, 4, 4, 4, 4, OMAP_DSS_COLOR_RGBX16, "RGBx12-4444" },
+ { 0, 4, 12, 4, 8, 4, 4, 4, 0, OMAP_DSS_COLOR_RGBA16, "RGBA16-4444" },
+ { 24, 8, 16, 8, 8, 8, 0, 8, 0, OMAP_DSS_COLOR_ARGB32, "ARGB32-8888" },
+ { 0, 8, 24, 8, 16, 8, 8, 8, 0, OMAP_DSS_COLOR_RGBA32, "RGBA32-8888" },
+ { 0, 0, 24, 8, 16, 8, 8, 8, 8, OMAP_DSS_COLOR_RGBX32, "RGBx24-8888" },
+ { 0, 0, 10, 5, 5, 5, 0, 5, 1, OMAP_DSS_COLOR_XRGB16_1555,
+ "xRGB15-1555" },
+}, {
+ { 0, 0, 0, 0, 0, 0, 0, 0, 12, OMAP_DSS_COLOR_NV12, "NV12" },
+ { 0, 0, 12, 4, 8, 4, 4, 4, 4, OMAP_DSS_COLOR_RGBX16, "RGBx12-4444" },
+ { 0, 4, 12, 4, 8, 4, 4, 4, 0, OMAP_DSS_COLOR_RGBA16, "RGBA16-4444" },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "invalid" },
+ { 0, 0, 8, 4, 4, 4, 0, 4, 4, OMAP_DSS_COLOR_RGB12U, "xRGB12-4444" },
+ { 12, 4, 8, 4, 4, 4, 0, 4, 0, OMAP_DSS_COLOR_ARGB16, "ARGB16-4444" },
+ { 0, 0, 11, 5, 5, 6, 0, 5, 0, OMAP_DSS_COLOR_RGB16, "RGB16-565" },
+ { 15, 1, 10, 5, 5, 5, 0, 5, 0, OMAP_DSS_COLOR_ARGB16_1555,
+ "ARGB16-1555" },
+ { 0, 0, 16, 8, 8, 8, 0, 8, 8, OMAP_DSS_COLOR_RGB24U, "xRGB24-8888" },
+ { 0, 0, 16, 8, 8, 8, 0, 8, 0, OMAP_DSS_COLOR_RGB24P, "RGB24-888" },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 16, OMAP_DSS_COLOR_YUV2, "YUYV" },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 16, OMAP_DSS_COLOR_UYVY, "UYVY" },
+ { 24, 8, 16, 8, 8, 8, 0, 8, 0, OMAP_DSS_COLOR_ARGB32, "ARGB32-8888" },
+ { 0, 8, 24, 8, 16, 8, 8, 8, 0, OMAP_DSS_COLOR_RGBA32, "RGBA32-8888" },
+ { 0, 0, 24, 8, 16, 8, 8, 8, 8, OMAP_DSS_COLOR_RGBX32, "RGBx24-8888" },
+ { 0, 0, 10, 5, 5, 5, 0, 5, 1, OMAP_DSS_COLOR_XRGB16_1555,
+ "xRGB15-1555" },
+} };
+
+static const struct color_info *get_color_info(enum omap_color_mode mode)
+{
+ int i;
+ for (i = 0; i < sizeof(fmts) / sizeof(fmts[0][0]); i++)
+ if (fmts[0][i].mode == mode)
+ return fmts[0] + i;
+ return NULL;
+}
+
+static int color_mode_to_bpp(enum omap_color_mode color_mode)
+{
+ const struct color_info *ci = get_color_info(color_mode);
+ if (!ci)
+ return 0;
+
+ return ci->a_bt + ci->r_bt + ci->g_bt + ci->b_bt + ci->x_bt;
+}
+
+#ifdef CONFIG_DEBUG_FS
+const char *dsscomp_get_color_name(enum omap_color_mode m)
+{
+ const struct color_info *ci = get_color_info(m);
+ return ci ? ci->name : NULL;
+}
+#endif
+
+union rect {
+ struct {
+ s32 x;
+ s32 y;
+ s32 w;
+ s32 h;
+ };
+ struct {
+ s32 xy[2];
+ s32 wh[2];
+ };
+ struct dss2_rect_t r;
+};
+
+static int crop_to_rect(union rect *crop, union rect *win, union rect *vis,
+ int rotation, int mirror)
+{
+ int c, swap = rotation & 1;
+
+ /* align crop window with display coordinates */
+ if (swap)
+ crop->y -= (crop->h = -crop->h);
+ if (rotation & 2)
+ crop->xy[!swap] -= (crop->wh[!swap] = -crop->wh[!swap]);
+ if ((!mirror) ^ !(rotation & 2))
+ crop->xy[swap] -= (crop->wh[swap] = -crop->wh[swap]);
+
+ for (c = 0; c < 2; c++) {
+ /* see if complete buffer is outside the vis or it is
+ fully cropped or scaled to 0 */
+ if (win->wh[c] <= 0 || vis->wh[c] <= 0 ||
+ win->xy[c] + win->wh[c] <= vis->xy[c] ||
+ win->xy[c] >= vis->xy[c] + vis->wh[c] ||
+ !crop->wh[c ^ swap])
+ return -ENOENT;
+
+ /* crop left/top */
+ if (win->xy[c] < vis->xy[c]) {
+ /* correction term */
+ int a = (vis->xy[c] - win->xy[c]) *
+ crop->wh[c ^ swap] / win->wh[c];
+ crop->xy[c ^ swap] += a;
+ crop->wh[c ^ swap] -= a;
+ win->wh[c] -= vis->xy[c] - win->xy[c];
+ win->xy[c] = vis->xy[c];
+ }
+ /* crop right/bottom */
+ if (win->xy[c] + win->wh[c] > vis->xy[c] + vis->wh[c]) {
+ crop->wh[c ^ swap] = crop->wh[c ^ swap] *
+ (vis->xy[c] + vis->wh[c] - win->xy[c]) /
+ win->wh[c];
+ win->wh[c] = vis->xy[c] + vis->wh[c] - win->xy[c];
+ }
+
+ if (!crop->wh[c ^ swap] || !win->wh[c])
+ return -ENOENT;
+ }
+
+ /* realign crop window to buffer coordinates */
+ if (rotation & 2)
+ crop->xy[!swap] -= (crop->wh[!swap] = -crop->wh[!swap]);
+ if ((!mirror) ^ !(rotation & 2))
+ crop->xy[swap] -= (crop->wh[swap] = -crop->wh[swap]);
+ if (swap)
+ crop->y -= (crop->h = -crop->h);
+ return 0;
+}
+
+int set_dss_ovl_info(struct dss2_ovl_info *oi)
+{
+ struct omap_overlay_info info;
+ struct omap_overlay *ovl;
+ struct dss2_ovl_cfg *cfg;
+ union rect crop, win, vis;
+ int c;
+ enum tiler_fmt fmt;
+
+ /* check overlay number */
+ if (!oi || oi->cfg.ix >= omap_dss_get_num_overlays())
+ return -EINVAL;
+ cfg = &oi->cfg;
+ ovl = omap_dss_get_overlay(cfg->ix);
+
+ /* just in case there are new fields, we get the current info */
+ ovl->get_overlay_info(ovl, &info);
+
+ if (!cfg->enabled)
+ goto done;
+
+ /* copied params */
+ info.zorder = cfg->zorder;
+
+ if (cfg->zonly)
+ goto done;
+
+ info.global_alpha = cfg->global_alpha;
+ info.pre_mult_alpha = cfg->pre_mult_alpha;
+ info.rotation = cfg->rotation;
+ info.mirror = cfg->mirror;
+ info.color_mode = cfg->color_mode;
+
+ /* crop to screen */
+ crop.r = cfg->crop;
+ win.r = cfg->win;
+ vis.x = vis.y = 0;
+ vis.w = ovl->manager->output->device->panel.timings.x_res;
+ vis.h = ovl->manager->output->device->panel.timings.y_res;
+
+ if (crop_to_rect(&crop, &win, &vis, cfg->rotation, cfg->mirror) ||
+ vis.w < 2)
+ goto done;
+
+ /* adjust crop to UV pixel boundaries */
+ for (c = 0; c < (cfg->color_mode == OMAP_DSS_COLOR_NV12 ? 2 :
+ (cfg->color_mode &
+ (OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY)) ? 1 : 0); c++) {
+ /* keep the output window to avoid trembling edges */
+ crop.wh[c] += crop.xy[c] & 1; /* round down start */
+ crop.xy[c] &= ~1;
+ crop.wh[c] += crop.wh[c] & 1; /* round up end */
+
+ /*
+ * Buffer is aligned on UV pixel boundaries, so no
+ * worries about extending crop region.
+ */
+ }
+
+ info.width = crop.w;
+ info.height = crop.h;
+ if (cfg->rotation & 1)
+ /* DISPC uses swapped height/width for 90/270 degrees */
+ swap(info.width, info.height);
+ info.pos_x = win.x;
+ info.pos_y = win.y;
+ info.out_width = win.w;
+ info.out_height = win.h;
+
+ /* calculate addresses and cropping */
+ info.paddr = oi->ba;
+ info.p_uv_addr = (info.color_mode == OMAP_DSS_COLOR_NV12) ? oi->uv : 0;
+
+ /* check for TILER 2D buffer */
+ if (tiler_get_fmt(info.paddr, &fmt) && fmt >= TILFMT_8BIT &&
+ fmt <= TILFMT_32BIT) {
+ int bpp = 1 << (fmt - TILFMT_8BIT);
+ struct tiler_view_t t;
+
+ /* crop to top-left */
+
+ /*
+ * DSS supports YUV422 on 32-bit mode, but its technically
+ * 2 bytes-per-pixel.
+ * Also RGB24-888 is 3 bytes-per-pixel even though no
+ * tiler pixel format matches this.
+ */
+ if (cfg->color_mode &
+ (OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY))
+ bpp = 2;
+ else if (cfg->color_mode == OMAP_DSS_COLOR_RGB24P)
+ bpp = 3;
+
+ tilview_create(&t, info.paddr, cfg->width, cfg->height);
+ info.paddr -= t.tsptr;
+ tilview_crop(&t, 0, crop.y, cfg->width, crop.h);
+ info.paddr += t.tsptr + bpp * crop.x;
+
+ info.rotation_type = OMAP_DSS_ROT_TILER;
+ info.screen_width = 0;
+
+ /* for NV12 format also crop NV12 */
+ if (info.color_mode == OMAP_DSS_COLOR_NV12) {
+ tilview_create(&t, info.p_uv_addr,
+ cfg->width >> 1, cfg->height >> 1);
+ info.p_uv_addr -= t.tsptr;
+ tilview_crop(&t, 0, crop.y >> 1, cfg->width >> 1,
+ crop.h >> 1);
+ info.p_uv_addr += t.tsptr + bpp * crop.x;
+ }
+ } else {
+ /* program tiler 1D as SDMA */
+
+ int bpp = color_mode_to_bpp(cfg->color_mode);
+ if (!bpp) {
+ pr_warn("invalid color format %u for ovl%d\n",
+ cfg->color_mode, cfg->ix);
+ goto done;
+ }
+
+ info.screen_width = cfg->stride * 8 / (bpp == 12 ? 8 : bpp);
+ info.paddr += crop.x * (bpp / 8) + crop.y * cfg->stride;
+
+ /* for NV12 format also crop NV12 */
+ if (info.color_mode == OMAP_DSS_COLOR_NV12)
+ info.p_uv_addr += crop.x * (bpp / 8) +
+ (crop.y >> 1) * cfg->stride;
+
+ /* no rotation on DMA buffer */
+ if (cfg->rotation & 3 || cfg->mirror)
+ return -EINVAL;
+
+ info.rotation_type = OMAP_DSS_ROT_DMA;
+ }
+
+ info.max_x_decim = cfg->decim.max_x ? : 255;
+ info.max_y_decim = cfg->decim.max_y ? : 255;
+ info.min_x_decim = cfg->decim.min_x ? : 1;
+ info.min_y_decim = cfg->decim.min_y ? : 1;
+#if 0
+ info.pic_height = cfg->height;
+
+ info.field = 0;
+ if (cfg->ilace & OMAP_DSS_ILACE_SEQ)
+ info.field |= OMAP_FLAG_IBUF;
+ if (cfg->ilace & OMAP_DSS_ILACE_SWAP)
+ info.field |= OMAP_FLAG_ISWAP;
+ /*
+ * Ignore OMAP_DSS_ILACE as there is no real support yet for
+ * interlaced interleaved vs progressive buffers
+ */
+ if (ovl->manager &&
+ ovl->manager->device &&
+ !strcmp(ovl->manager->device->name, "hdmi") &&
+ is_hdmi_interlaced())
+ info.field |= OMAP_FLAG_IDEV;
+
+ info.out_wb = 0;
+#endif
+
+ info.cconv = cfg->cconv;
+
+done:
+ pr_debug("ovl%d: en=%d %x/%x ", ovl->id, ovl->is_enabled(ovl),
+ info.paddr, info.p_uv_addr);
+ pr_debug("(%dx%d|%d) => ", info.width, info.height, info.screen_width);
+ pr_debug("(%dx%d) @ ", info.out_width, info.out_height);
+ pr_debug("(%d,%d) rot=%d ", info.pos_x, info.pos_y, info.rotation);
+ pr_debug("mir=%d col=%x z=%d ", info.mirror, info.color_mode,
+ info.zorder);
+ pr_debug("al=%02x prem=%d\n", info.global_alpha, info.pre_mult_alpha);
+ /* set overlay info */
+ return ovl->set_overlay_info(ovl, &info);
+}
+
+void swap_rb_in_ovl_info(struct dss2_ovl_info *oi)
+{
+ /* we need to swap YUV color matrix if we are swapping R and B */
+ if (oi->cfg.color_mode &
+ (OMAP_DSS_COLOR_NV12 | OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY)) {
+ swap(oi->cfg.cconv.ry, oi->cfg.cconv.by);
+ swap(oi->cfg.cconv.rcr, oi->cfg.cconv.bcr);
+ swap(oi->cfg.cconv.rcb, oi->cfg.cconv.bcb);
+ }
+}
+
+struct omap_overlay_manager *find_dss_mgr(int display_ix)
+{
+ struct omap_overlay_manager *mgr;
+ char name[32];
+ int i;
+
+ sprintf(name, "display%d", display_ix);
+
+ for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
+ mgr = omap_dss_get_overlay_manager(i);
+ if (mgr->output->device && !strcmp(name, dev_name(&mgr->output->device->dev)))
+ return mgr;
+ }
+ return NULL;
+}
+
+int set_dss_mgr_info(struct dss2_mgr_info *mi, struct omapdss_ovl_cb *cb)
+{
+ struct omap_overlay_manager_info info;
+ struct omap_overlay_manager *mgr;
+
+ if (!mi)
+ return -EINVAL;
+ mgr = find_dss_mgr(mi->ix);
+ if (!mgr)
+ return -EINVAL;
+
+ /* just in case there are new fields, we get the current info */
+ mgr->get_manager_info(mgr, &info);
+
+ /* we support alpha-enabled only if we have free zorder */
+ /* :FIXME: for now DSS has this as an ovl cap */
+ if (alpha_only) {
+ if (!mi->alpha_blending)
+ return -EINVAL;
+ info.partial_alpha_enabled = false;
+ } else {
+ info.partial_alpha_enabled = mi->alpha_blending;
+ }
+
+ info.default_color = mi->default_color;
+ info.trans_enabled = mi->trans_enabled && !mi->alpha_blending;
+ info.trans_key = mi->trans_key;
+ info.trans_key_type = mi->trans_key_type;
+
+ info.cpr_coefs = mi->cpr_coefs;
+ info.cpr_enable = mi->cpr_enabled;
+ info.cb = *cb;
+
+ return mgr->set_manager_info(mgr, &info);
+}
+
+void swap_rb_in_mgr_info(struct dss2_mgr_info *mi)
+{
+ const struct omap_dss_cpr_coefs c = { 256, 0, 0, 0, 256, 0, 0, 0, 256 };
+
+ /* set default CPR */
+ if (!mi->cpr_enabled)
+ mi->cpr_coefs = c;
+ mi->cpr_enabled = true;
+
+ /* swap red and blue */
+ swap(mi->cpr_coefs.rr, mi->cpr_coefs.br);
+ swap(mi->cpr_coefs.rg, mi->cpr_coefs.bg);
+ swap(mi->cpr_coefs.rb, mi->cpr_coefs.bb);
+}
+
+/*
+ * ===========================================================================
+ * DEBUG METHODS
+ * ===========================================================================
+ */
+void dump_ovl_info(struct dsscomp_dev *cdev, struct dss2_ovl_info *oi)
+{
+ struct dss2_ovl_cfg *c = &oi->cfg;
+ const struct color_info *ci;
+
+ if (!(debug & DEBUG_OVERLAYS) ||
+ !(debug & DEBUG_COMPOSITIONS))
+ return;
+
+ ci = get_color_info(c->color_mode);
+ if (c->zonly) {
+ dev_info(DEV(cdev), "ovl%d(%s z%d)\n", c->ix, c->enabled ?
+ "ON" : "off", c->zorder);
+ return;
+ }
+ dev_info(DEV(cdev), "ovl%d(%s ", c->ix, c->enabled ? "ON" : "off");
+ dev_info(DEV(cdev), "z%d %s", c->zorder, ci ? (ci->name ? :
+ "(none)") : "(invalid)");
+ dev_info(DEV(cdev), "%s", c->pre_mult_alpha ? " premult" : "");
+ dev_info(DEV(cdev), "*%d%%", (c->global_alpha * 100 + 128) / 255);
+ dev_info(DEV(cdev), "%d*%d:%d,%d+", c->width, c->height, c->crop.x,
+ c->crop.y);
+ dev_info(DEV(cdev), "%d,%d rot%d", c->crop.w, c->crop.h, c->rotation);
+ dev_info(DEV(cdev), "%s => %d,", c->mirror ? "+mir" : "", c->win.x);
+ dev_info(DEV(cdev), "%d+%d,%d ", c->win.y, c->win.w, c->win.h);
+ dev_info(DEV(cdev), "%p/%p|%d)\n", (void *)oi->ba, (void *)oi->uv,
+ c->stride);
+}
+
+static void print_mgr_info(struct dsscomp_dev *cdev,
+ struct dss2_mgr_info *mi)
+{
+ pr_cont("(dis%d(%s) alpha=%d col=%08x ilace=%d) ",
+ mi->ix,
+ (mi->ix < cdev->num_displays && cdev->displays[mi->ix]) ?
+ cdev->displays[mi->ix]->name : "NONE",
+ mi->alpha_blending, mi->default_color,
+ mi->interlaced);
+}
+
+void dump_comp_info(struct dsscomp_dev *cdev, struct dsscomp_setup_mgr_data *d,
+ const char *phase)
+{
+ if (!(debug & DEBUG_COMPOSITIONS))
+ return;
+
+ dev_info(DEV(cdev), "[%p] %s: %c%c%c ",
+ *phase == 'q' ? (void *)d->sync_id : d, phase,
+ (d->mode & DSSCOMP_SETUP_MODE_APPLY) ? 'A' : '-',
+ (d->mode & DSSCOMP_SETUP_MODE_DISPLAY) ? 'D' : '-',
+ (d->mode & DSSCOMP_SETUP_MODE_CAPTURE) ? 'C' : '-');
+ print_mgr_info(cdev, &d->mgr);
+ pr_cont("n=%d\n", d->num_ovls);
+}
+
+void dump_total_comp_info(struct dsscomp_dev *cdev,
+ struct dsscomp_setup_dispc_data *d,
+ const char *phase)
+{
+ int i;
+
+ if (!(debug & DEBUG_COMPOSITIONS))
+ return;
+
+ dev_info(DEV(cdev), "[%p] ", *phase == 'q' ? (void *)d->sync_id : d);
+ dev_info(DEV(cdev), "%s: %c", phase,
+ (d->mode & DSSCOMP_SETUP_MODE_APPLY) ? 'A' : '-');
+ dev_info(DEV(cdev), "%c",
+ (d->mode & DSSCOMP_SETUP_MODE_DISPLAY) ? 'D' : '-');
+ dev_info(DEV(cdev), "%c ",
+ (d->mode & DSSCOMP_SETUP_MODE_CAPTURE) ? 'C' : '-');
+
+ for (i = 0; i < d->num_mgrs && i < ARRAY_SIZE(d->mgrs); i++)
+ print_mgr_info(cdev, d->mgrs + i);
+ pr_cont("n=%d\n", d->num_ovls);
+}
diff --git a/drivers/video/omap2/dsscomp/device.c b/drivers/video/omap2/dsscomp/device.c
--- /dev/null
@@ -0,0 +1,732 @@
+/*
+ * linux/drivers/video/omap2/dsscomp/device.c
+ *
+ * DSS Composition file device and ioctl support
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc
+ * Author: Lajos Molnar <molnar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DEBUG
+
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/anon_inodes.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+
+#define MODULE_NAME_DSSCOMP "dsscomp"
+
+#include <video/omapdss.h>
+#include <video/dsscomp.h>
+#include <plat/dsscomp.h>
+#include "../../../drivers/gpu/drm/omapdrm/omap_dmm_tiler.h"
+#include "dsscomp.h"
+
+#include <linux/debugfs.h>
+
+static DECLARE_WAIT_QUEUE_HEAD(waitq);
+static DEFINE_MUTEX(wait_mtx);
+bool alpha_only = true;
+
+static u32 hwc_virt_to_phys(u32 arg)
+{
+ pmd_t *pmd;
+ pte_t *ptep;
+
+ pgd_t *pgd = pgd_offset(current->mm, arg);
+ if (pgd_none(*pgd) || pgd_bad(*pgd))
+ return 0;
+
+ pmd = pmd_offset((pud_t *)pgd, arg);
+ if (pmd_none(*pmd) || pmd_bad(*pmd))
+ return 0;
+
+ ptep = pte_offset_map(pmd, arg);
+ if (ptep && pte_present(*ptep))
+ return (PAGE_MASK & *ptep) | (~PAGE_MASK & arg);
+
+ return 0;
+}
+
+/*
+ * ===========================================================================
+ * WAIT OPERATIONS
+ * ===========================================================================
+ */
+
+static void sync_drop(struct dsscomp_sync_obj *sync)
+{
+ if (sync && atomic_dec_and_test(&sync->refs)) {
+ if (debug & DEBUG_WAITS)
+ pr_info("free sync [%p]\n", sync);
+
+ kfree(sync);
+ }
+}
+
+static int sync_setup(const char *name, const struct file_operations *fops,
+ struct dsscomp_sync_obj *sync, int flags)
+{
+ if (!sync)
+ return -ENOMEM;
+
+ sync->refs.counter = 1;
+ sync->fd = anon_inode_getfd(name, fops, sync, flags);
+ return sync->fd < 0 ? sync->fd : 0;
+}
+
+static int sync_finalize(struct dsscomp_sync_obj *sync, int r)
+{
+ if (sync) {
+ if (r < 0)
+ /* delete sync object on failure */
+ sys_close(sync->fd);
+ else
+ /* return file descriptor on success */
+ r = sync->fd;
+ }
+ return r;
+}
+
+/* wait for programming or release of a composition */
+int dsscomp_wait(struct dsscomp_sync_obj *sync, enum dsscomp_wait_phase phase,
+ int timeout)
+{
+ mutex_lock(&wait_mtx);
+ if (debug & DEBUG_WAITS) {
+ pr_info("wait %s on ", phase == DSSCOMP_WAIT_DISPLAYED ?
+ "display" : phase == DSSCOMP_WAIT_PROGRAMMED ?
+ "program" : "release");
+ pr_info("[%p]\n", sync);
+ }
+
+ if (sync->state < phase) {
+ mutex_unlock(&wait_mtx);
+
+ timeout = wait_event_interruptible_timeout(waitq,
+ sync->state >= phase, timeout);
+ if (debug & DEBUG_WAITS) {
+ pr_info("wait over [%p]: ", sync);
+ pr_info("%s", timeout < 0 ? "signal" : timeout > 0 ?
+ "ok" : "timeout");
+ pr_info("%d\n", timeout);
+ }
+ if (timeout <= 0)
+ return timeout ? : -ETIME;
+
+ mutex_lock(&wait_mtx);
+ }
+ mutex_unlock(&wait_mtx);
+
+ return 0;
+}
+EXPORT_SYMBOL(dsscomp_wait);
+
+static void dsscomp_queue_cb(void *data, int status)
+{
+ struct dsscomp_sync_obj *sync = data;
+ enum dsscomp_wait_phase phase =
+ status == DSS_COMPLETION_PROGRAMMED ? DSSCOMP_WAIT_PROGRAMMED :
+ status == DSS_COMPLETION_DISPLAYED ? DSSCOMP_WAIT_DISPLAYED :
+ DSSCOMP_WAIT_RELEASED, old_phase;
+
+ mutex_lock(&wait_mtx);
+ old_phase = sync->state;
+ if (old_phase < phase)
+ sync->state = phase;
+ mutex_unlock(&wait_mtx);
+
+ if (status & DSS_COMPLETION_RELEASED)
+ sync_drop(sync);
+ if (old_phase < phase)
+ wake_up_interruptible_sync(&waitq);
+}
+
+static int sync_release(struct inode *inode, struct file *filp)
+{
+ struct dsscomp_sync_obj *sync = filp->private_data;
+ sync_drop(sync);
+ return 0;
+}
+
+static long sync_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int r = 0;
+ struct dsscomp_sync_obj *sync = filp->private_data;
+ void __user *ptr = (void __user *)arg;
+
+ switch (cmd) {
+ case DSSCIOC_WAIT:
+ {
+ struct dsscomp_wait_data wd;
+ r = copy_from_user(&wd, ptr, sizeof(wd)) ? :
+ dsscomp_wait(sync, wd.phase,
+ usecs_to_jiffies(wd.timeout_us));
+ break;
+ }
+ default:
+ r = -EINVAL;
+ }
+ return r;
+}
+
+static const struct file_operations sync_fops = {
+ .owner = THIS_MODULE,
+ .release = sync_release,
+ .unlocked_ioctl = sync_ioctl,
+};
+
+static long setup_mgr(struct dsscomp_dev *cdev,
+ struct dsscomp_setup_mgr_data *d)
+{
+ int i, r;
+ struct omap_dss_device *dev;
+ struct omap_overlay_manager *mgr;
+ struct dsscomp *comp;
+ struct dsscomp_sync_obj *sync = NULL;
+
+ dump_comp_info(cdev, d, "queue");
+ for (i = 0; i < d->num_ovls; i++)
+ dump_ovl_info(cdev, d->ovls + i);
+
+ /* verify display is valid and connected */
+ if (d->mgr.ix >= cdev->num_displays)
+ return -EINVAL;
+ dev = cdev->displays[d->mgr.ix];
+ if (!dev)
+ return -EINVAL;
+ mgr = dev->output->manager;
+ if (!mgr)
+ return -ENODEV;
+
+ comp = dsscomp_new(mgr);
+ if (IS_ERR(comp))
+ return PTR_ERR(comp);
+
+ /* swap red & blue if requested */
+ if (d->mgr.swap_rb) {
+ swap_rb_in_mgr_info(&d->mgr);
+ for (i = 0; i < d->num_ovls; i++)
+ swap_rb_in_ovl_info(d->ovls + i);
+ }
+
+ r = dsscomp_set_mgr(comp, &d->mgr);
+
+ for (i = 0; i < d->num_ovls; i++) {
+ struct dss2_ovl_info *oi = d->ovls + i;
+ u32 addr = (u32) oi->address;
+
+ if (oi->addressing != OMAP_DSS_BUFADDR_DIRECT)
+ return -EINVAL;
+
+ /* convert addresses to user space */
+ if (oi->cfg.color_mode == OMAP_DSS_COLOR_NV12) {
+ if (oi->uv_address)
+ oi->uv = hwc_virt_to_phys((u32) oi->uv_address);
+ else
+ oi->uv = hwc_virt_to_phys(addr +
+ oi->cfg.height * oi->cfg.stride);
+ }
+ oi->ba = hwc_virt_to_phys(addr);
+
+ r = r ? : dsscomp_set_ovl(comp, oi);
+ }
+
+ r = r ? : dsscomp_setup(comp, d->mode, d->win);
+
+ /* create sync object */
+ if (d->get_sync_obj) {
+ sync = kzalloc(sizeof(*sync), GFP_KERNEL);
+ r = sync_setup("dsscomp_sync", &sync_fops, sync, O_RDONLY);
+ if (sync && (debug & DEBUG_WAITS))
+ dev_info(DEV(cdev), "new sync [%p] on #%d\n", sync,
+ sync->fd);
+ if (r)
+ sync_drop(sync);
+ }
+
+ /* drop composition if failed to create */
+ if (r) {
+ dsscomp_drop(comp);
+ return r;
+ }
+
+ if (sync) {
+ sync->refs.counter++;
+ comp->extra_cb = dsscomp_queue_cb;
+ comp->extra_cb_data = sync;
+ }
+ if (d->mode & DSSCOMP_SETUP_APPLY)
+ r = dsscomp_delayed_apply(comp);
+
+ /* delete sync object if failed to apply or create file */
+ if (sync) {
+ r = sync_finalize(sync, r);
+ if (r < 0)
+ sync_drop(sync);
+ }
+ return r;
+}
+
+static long query_display(struct dsscomp_dev *cdev,
+ struct dsscomp_display_info *dis)
+{
+ struct omap_dss_device *dev;
+ struct omap_overlay_manager *mgr;
+ struct omap_overlay_manager_info info;
+ int i;
+
+ /* get display */
+ if (dis->ix >= cdev->num_displays)
+ return -EINVAL;
+ dev = cdev->displays[dis->ix];
+ if (!dev)
+ return -EINVAL;
+ mgr = dev->output->manager;
+
+ /* fill out display information */
+ dis->channel = dev->channel;
+ dis->enabled = (dev->state == OMAP_DSS_DISPLAY_SUSPENDED) ?
+ dev->activate_after_resume :
+ (dev->state == OMAP_DSS_DISPLAY_ACTIVE);
+ dis->overlays_available = 0;
+ dis->overlays_owned = 0;
+#if 0
+ dis->s3d_info = dev->panel.s3d_info;
+#endif
+ dis->state = dev->state;
+ dis->timings = dev->panel.timings;
+
+ dis->width_in_mm = DIV_ROUND_CLOSEST(dev->panel.width_in_um, 1000);
+ dis->height_in_mm = DIV_ROUND_CLOSEST(dev->panel.height_in_um, 1000);
+
+ /* find all overlays available for/owned by this display */
+ for (i = 0; i < cdev->num_ovls && dis->enabled; i++) {
+ if (cdev->ovls[i]->manager == mgr)
+ dis->overlays_owned |= 1 << i;
+ else if (!cdev->ovls[i]->is_enabled(cdev->ovls[i]))
+ dis->overlays_available |= 1 << i;
+ }
+ dis->overlays_available |= dis->overlays_owned;
+
+ /* fill out manager information */
+ if (mgr) {
+ mgr->get_manager_info(mgr, &info);
+ dis->mgr.alpha_blending =
+ alpha_only || info.partial_alpha_enabled;
+ dis->mgr.default_color = info.default_color;
+#if 0
+ dis->mgr.interlaced = !strcmp(dev->name, "hdmi") &&
+ is_hdmi_interlaced()
+#else
+ dis->mgr.interlaced = 0;
+#endif
+ dis->mgr.trans_enabled = info.trans_enabled;
+ dis->mgr.trans_key = info.trans_key;
+ dis->mgr.trans_key_type = info.trans_key_type;
+ } else {
+ /* display is disabled if it has no manager */
+ memset(&dis->mgr, 0, sizeof(dis->mgr));
+ }
+ dis->mgr.ix = dis->ix;
+
+#ifdef HDMI_ENABLED
+ if (dev->driver && dis->modedb_len && dev->driver->get_modedb)
+ dis->modedb_len = dev->driver->get_modedb(dev,
+ (struct fb_videomode *)dis->modedb, dis->modedb_len);
+#endif
+ return 0;
+}
+
+static long check_ovl(struct dsscomp_dev *cdev,
+ struct dsscomp_check_ovl_data *chk)
+{
+ u16 x_decim, y_decim;
+ bool five_taps;
+ struct omap_dss_device *dev;
+ struct omap_overlay_manager *mgr;
+ int i;
+ long allowed = 0;
+ bool checked_vid = false, scale_ok = false;
+ struct dss2_ovl_cfg *c = &chk->ovl.cfg;
+ enum tiler_fmt fmt;
+
+ /* get display */
+ if (chk->mgr.ix >= cdev->num_displays)
+ return -EINVAL;
+
+ dev = cdev->displays[chk->mgr.ix];
+ if (!dev)
+ return -EINVAL;
+ mgr = dev->output->manager;
+
+ /* we support alpha-enabled only if we have free zorder */
+ /* :FIXME: for now DSS has this as an ovl cap */
+ if (alpha_only && !chk->mgr.alpha_blending)
+ return -EINVAL;
+
+ /* normalize decimation */
+ if (!c->decim.min_x)
+ c->decim.min_x = 1;
+ if (!c->decim.min_y)
+ c->decim.min_y = 1;
+ if (!c->decim.max_x)
+ c->decim.max_x = 255;
+ if (!c->decim.max_y)
+ c->decim.max_y = 255;
+
+ /* check scaling support */
+ for (i = 0; i < cdev->num_ovls; i++) {
+ /* verify color format support */
+ if (c->color_mode & ~cdev->ovls[i]->supported_modes)
+ continue;
+
+ /* verify scaling on GFX and VID pipes */
+ if (!i || !checked_vid) {
+ struct omap_overlay_info info = {
+ .out_width = c->win.w,
+ .out_height = c->win.h,
+ .width = c->crop.w,
+ .height = c->crop.h,
+ .color_mode = c->color_mode,
+ .rotation = c->rotation,
+ .min_x_decim = c->decim.min_x,
+ .max_x_decim = c->decim.max_x,
+ .min_y_decim = c->decim.min_y,
+ .max_y_decim = c->decim.max_y,
+ };
+ u32 ba = (unsigned int) &chk->ovl.address;
+
+ ba = hwc_virt_to_phys(ba);
+ /* check for valid tiler container */
+ if (tiler_get_fmt(ba, &fmt) && fmt >= TILFMT_8BIT &&
+ fmt <= TILFMT_32BIT)
+ info.rotation_type = OMAP_DSS_ROT_TILER;
+ else
+ info.rotation_type = OMAP_DSS_ROT_DMA;
+
+/* scale_ok = !dispc_scaling_decision(i, &info, mgr->id,
+ &x_decim, &y_decim, &five_taps);
+*/
+ /* update minimum decimation needs to support ovl */
+ if (scale_ok) {
+ if (x_decim > c->decim.min_x)
+ c->decim.min_x = x_decim;
+ if (y_decim > c->decim.min_y)
+ c->decim.min_y = y_decim;
+ }
+ }
+ checked_vid = i;
+ if (scale_ok)
+ allowed |= 1 << i;
+ }
+
+ return allowed;
+}
+
+static long setup_display(struct dsscomp_dev *cdev,
+ struct dsscomp_setup_display_data *dis)
+{
+ struct omap_dss_device *dev;
+
+ /* get display */
+ if (dis->ix >= cdev->num_displays)
+ return -EINVAL;
+ dev = cdev->displays[dis->ix];
+ if (!dev)
+ return -EINVAL;
+
+#ifdef HDMI_ENABLED
+ if (dev->driver->set_mode)
+ return dev->driver->set_mode(dev,
+ (struct fb_videomode *)&dis->mode);
+ else
+#endif
+ return 0;
+}
+
+static void fill_cache(struct dsscomp_dev *cdev)
+{
+ unsigned long i;
+ struct omap_dss_device *dssdev = NULL;
+
+ cdev->num_ovls = min(omap_dss_get_num_overlays(), MAX_OVERLAYS);
+ for (i = 0; i < cdev->num_ovls; i++)
+ cdev->ovls[i] = omap_dss_get_overlay(i);
+
+ cdev->num_mgrs = min(omap_dss_get_num_overlay_managers(), MAX_MANAGERS);
+ for (i = 0; i < cdev->num_mgrs; i++)
+ cdev->mgrs[i] = omap_dss_get_overlay_manager(i);
+
+ for_each_dss_dev(dssdev) {
+ const char *name = dev_name(&dssdev->dev);
+ if (strncmp(name, "display", 7) ||
+ strict_strtoul(name + 7, 10, &i) ||
+ i >= MAX_DISPLAYS)
+ continue;
+
+ if (cdev->num_displays <= i)
+ cdev->num_displays = i + 1;
+
+ cdev->displays[i] = dssdev;
+ dev_dbg(DEV(cdev), "display%lu=%s\n", i, dssdev->driver_name);
+
+ cdev->state_notifiers[i].notifier_call = dsscomp_state_notifier;
+ blocking_notifier_chain_register(&dssdev->state_notifiers,
+ cdev->state_notifiers + i);
+ }
+ dev_info(DEV(cdev), "found %d displays and %d overlays\n",
+ cdev->num_displays, cdev->num_ovls);
+
+ /*
+ * :FIXME: for now DSS has this as an ovl cap, even though it relates
+ * to the manager. For now we store this globally so we can access
+ * this.
+ */
+ alpha_only = cdev->num_ovls &&
+ (cdev->ovls[0]->caps & OMAP_DSS_OVL_CAP_ZORDER);
+}
+
+static long comp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int r = 0;
+ struct miscdevice *dev = filp->private_data;
+ struct dsscomp_dev *cdev = container_of(dev, struct dsscomp_dev, dev);
+ void __user *ptr = (void __user *)arg;
+
+ union {
+ struct {
+ struct dsscomp_setup_mgr_data set;
+ struct dss2_ovl_info ovl[MAX_OVERLAYS];
+ } m;
+ struct dsscomp_setup_dispc_data dispc;
+ struct dsscomp_display_info dis;
+ struct dsscomp_check_ovl_data chk;
+ struct dsscomp_setup_display_data sdis;
+ } u;
+
+ dsscomp_gralloc_init(cdev);
+
+ switch (cmd) {
+ case DSSCIOC_SETUP_MGR:
+ {
+ r = copy_from_user(&u.m.set, ptr, sizeof(u.m.set)) ? :
+ u.m.set.num_ovls > ARRAY_SIZE(u.m.ovl) ? -EINVAL :
+ copy_from_user(&u.m.ovl,
+ (void __user *)arg + sizeof(u.m.set),
+ sizeof(*u.m.ovl) * u.m.set.num_ovls) ? :
+ setup_mgr(cdev, &u.m.set);
+ break;
+ }
+ case DSSCIOC_SETUP_DISPC:
+ {
+ r = copy_from_user(&u.dispc, ptr, sizeof(u.dispc)) ? :
+ dsscomp_gralloc_queue_ioctl(&u.dispc);
+ break;
+ }
+ case DSSCIOC_QUERY_DISPLAY:
+ {
+ struct dsscomp_display_info *dis = NULL;
+ r = copy_from_user(&u.dis, ptr, sizeof(u.dis));
+ if (!r)
+ dis = kzalloc(sizeof(*dis->modedb) * u.dis.modedb_len +
+ sizeof(*dis), GFP_KERNEL);
+ if (dis) {
+ *dis = u.dis;
+ r = query_display(cdev, dis) ? :
+ copy_to_user(ptr, dis, sizeof(*dis) +
+ sizeof(*dis->modedb) * dis->modedb_len);
+ kfree(dis);
+ } else {
+ r = r ? : -ENOMEM;
+ }
+ break;
+ }
+ case DSSCIOC_CHECK_OVL:
+ {
+ r = copy_from_user(&u.chk, ptr, sizeof(u.chk)) ? :
+ check_ovl(cdev, &u.chk);
+ break;
+ }
+ case DSSCIOC_SETUP_DISPLAY:
+ {
+ r = copy_from_user(&u.sdis, ptr, sizeof(u.sdis)) ? :
+ setup_display(cdev, &u.sdis);
+ }
+ default:
+ r = -EINVAL;
+ }
+ return r;
+}
+
+/* must implement open for filp->private_data to be filled */
+static int comp_open(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static const struct file_operations comp_fops = {
+ .owner = THIS_MODULE,
+ .open = comp_open,
+ .unlocked_ioctl = comp_ioctl,
+};
+
+static int dsscomp_debug_show(struct seq_file *s, void *unused)
+{
+ void (*fn)(struct seq_file *s) = s->private;
+ fn(s);
+ return 0;
+}
+
+static int dsscomp_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dsscomp_debug_show, inode->i_private);
+}
+
+static const struct file_operations dsscomp_debug_fops = {
+ .open = dsscomp_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int dsscomp_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct dsscomp_dev *cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ if (!cdev) {
+ pr_err("dsscomp: failed to allocate device.\n");
+ return -ENOMEM;
+ }
+ cdev->dev.minor = MISC_DYNAMIC_MINOR;
+ cdev->dev.name = "dsscomp";
+ cdev->dev.mode = 0666;
+ cdev->dev.fops = &comp_fops;
+
+ ret = misc_register(&cdev->dev);
+ if (ret) {
+ pr_err("dsscomp: failed to register misc device.\n");
+ return ret;
+ }
+ cdev->dbgfs = debugfs_create_dir("dsscomp", NULL);
+ if (IS_ERR_OR_NULL(cdev->dbgfs)) {
+ dev_warn(DEV(cdev), "failed to create debug files.\n");
+ } else {
+ debugfs_create_file("comps", S_IRUGO,
+ cdev->dbgfs, dsscomp_dbg_comps, &dsscomp_debug_fops);
+ debugfs_create_file("gralloc", S_IRUGO,
+ cdev->dbgfs, dsscomp_dbg_gralloc, &dsscomp_debug_fops);
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+ debugfs_create_file("log", S_IRUGO,
+ cdev->dbgfs, dsscomp_dbg_events, &dsscomp_debug_fops);
+#endif
+ }
+
+ platform_set_drvdata(pdev, cdev);
+
+ pr_info("dsscomp: initializing.\n");
+
+ fill_cache(cdev);
+
+ /* initialize queues */
+ dsscomp_queue_init(cdev);
+ dsscomp_gralloc_init(cdev);
+
+ return 0;
+}
+
+static int dsscomp_remove(struct platform_device *pdev)
+{
+ struct dsscomp_dev *cdev = platform_get_drvdata(pdev);
+ misc_deregister(&cdev->dev);
+ debugfs_remove_recursive(cdev->dbgfs);
+ dsscomp_queue_exit();
+ dsscomp_gralloc_exit();
+ kfree(cdev);
+
+ return 0;
+}
+
+static struct platform_driver dsscomp_pdriver = {
+ .probe = dsscomp_probe,
+ .remove = dsscomp_remove,
+ .driver = { .name = MODULE_NAME_DSSCOMP, .owner = THIS_MODULE }
+};
+
+static struct platform_device dsscomp_pdev = {
+ .name = MODULE_NAME_DSSCOMP,
+ .id = -1
+};
+
+static int __init dsscomp_init(void)
+{
+ int err = platform_driver_register(&dsscomp_pdriver);
+ if (err)
+ return err;
+
+ err = platform_device_register(&dsscomp_pdev);
+ if (err)
+ platform_driver_unregister(&dsscomp_pdriver);
+ return err;
+}
+
+static void __exit dsscomp_exit(void)
+{
+ platform_device_unregister(&dsscomp_pdev);
+ platform_driver_unregister(&dsscomp_pdriver);
+}
+
+#define DUMP_CHUNK 256
+static char dump_buf[64 * 1024];
+static void dsscomp_kdump(void)
+{
+ struct seq_file s = {
+ .buf = dump_buf,
+ .size = sizeof(dump_buf) - 1,
+ };
+ int i;
+
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+ dsscomp_dbg_events(&s);
+#endif
+ dsscomp_dbg_comps(&s);
+ dsscomp_dbg_gralloc(&s);
+
+ for (i = 0; i < s.count; i += DUMP_CHUNK) {
+ if ((s.count - i) > DUMP_CHUNK) {
+ char c = s.buf[i + DUMP_CHUNK];
+ s.buf[i + DUMP_CHUNK] = 0;
+ pr_cont("%s", s.buf + i);
+ s.buf[i + DUMP_CHUNK] = c;
+ } else {
+ s.buf[s.count] = 0;
+ pr_cont("%s", s.buf + i);
+ }
+ }
+}
+EXPORT_SYMBOL(dsscomp_kdump);
+
+MODULE_LICENSE("GPL v2");
+module_init(dsscomp_init);
+module_exit(dsscomp_exit);
diff --git a/drivers/video/omap2/dsscomp/dsscomp.h b/drivers/video/omap2/dsscomp/dsscomp.h
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * linux/drivers/video/omap2/dsscomp/base.c
+ *
+ * DSS Composition basic operation support
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc
+ * Author: Lajos Molnar <molnar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _DSSCOMP_H
+#define _DSSCOMP_H
+
+#include <linux/miscdevice.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+#include <linux/hrtimer.h>
+#endif
+
+#define MAX_OVERLAYS 5
+#define MAX_MANAGERS 3
+#define MAX_DISPLAYS 4
+
+#define DEBUG_OVERLAYS (1 << 0)
+#define DEBUG_COMPOSITIONS (1 << 1)
+#define DEBUG_PHASES (1 << 2)
+#define DEBUG_WAITS (1 << 3)
+#define DEBUG_GRALLOC_PHASES (1 << 4)
+
+/*
+ * Utility macros
+ */
+#define ZERO(c) memset(&c, 0, sizeof(c))
+#define ZEROn(c, n) memset(c, 0, sizeof(*c) * n)
+#define DEV(c) (c->dev.this_device)
+
+/**
+ * DSS Composition Device Driver
+ *
+ * @dev: misc device base
+ * @dbgfs: debugfs hook
+ */
+struct dsscomp_dev {
+ struct miscdevice dev;
+ struct dentry *dbgfs;
+
+ /* cached DSS objects */
+ u32 num_ovls;
+ struct omap_overlay *ovls[MAX_OVERLAYS];
+ u32 num_mgrs;
+ struct omap_overlay_manager *mgrs[MAX_MANAGERS];
+ u32 num_displays;
+ struct omap_dss_device *displays[MAX_DISPLAYS];
+ struct notifier_block state_notifiers[MAX_DISPLAYS];
+};
+
+extern int debug;
+
+#ifdef CONFIG_DEBUG_FS
+extern struct mutex dbg_mtx;
+extern struct list_head dbg_comps;
+#define DO_IF_DEBUG_FS(cmd) { \
+ mutex_lock(&dbg_mtx); \
+ cmd; \
+ mutex_unlock(&dbg_mtx); \
+}
+#else
+#define DO_IF_DEBUG_FS(cmd)
+#endif
+
+enum dsscomp_state {
+ DSSCOMP_STATE_ACTIVE = 0xAC54156E,
+ DSSCOMP_STATE_APPLYING = 0xB554C591,
+ DSSCOMP_STATE_APPLIED = 0xB60504C1,
+ DSSCOMP_STATE_PROGRAMMED = 0xC0520652,
+ DSSCOMP_STATE_DISPLAYED = 0xD15504CA,
+};
+
+struct dsscomp {
+ enum dsscomp_state state;
+ /*
+ * :TRICKY: before applying, overlays used in a composition are stored
+ * in ovl_mask and the other masks are empty. Once composition is
+ * applied, blank is set to see if all overlays are to be disabled on
+ * this composition, any disabled overlays in the composition are set in
+ * ovl_dmask, and ovl_mask is updated to include ALL overlays that are
+ * actually on the display - even if they are not part of the
+ * composition. The reason: we use ovl_mask to see if an overlay is used
+ * or planned to be used on a manager. We update ovl_mask when
+ * composition is programmed (removing the disabled overlays).
+ */
+ bool blank; /* true if all overlays are to be disabled */
+ u32 ovl_mask; /* overlays used on this frame */
+ u32 ovl_dmask; /* overlays disabled on this frame */
+ u32 ix; /* manager index that this frame is on */
+ struct dsscomp_setup_mgr_data frm;
+ struct dss2_ovl_info ovls[5];
+ void (*extra_cb)(void *data, int status);
+ void *extra_cb_data;
+ bool must_apply; /* whether composition must be applied */
+
+#ifdef CONFIG_DEBUG_FS
+ struct list_head dbg_q;
+ u32 dbg_used;
+ struct {
+ u32 t, state;
+ } dbg_log[8];
+#endif
+};
+
+struct dsscomp_sync_obj {
+ int state;
+ int fd;
+ atomic_t refs;
+};
+
+/*
+ * Kernel interface
+ */
+int dsscomp_queue_init(struct dsscomp_dev *cdev);
+void dsscomp_queue_exit(void);
+void dsscomp_gralloc_init(struct dsscomp_dev *cdev);
+void dsscomp_gralloc_exit(void);
+int dsscomp_gralloc_queue_ioctl(struct dsscomp_setup_dispc_data *d);
+int dsscomp_wait(struct dsscomp_sync_obj *sync, enum dsscomp_wait_phase phase,
+ int timeout);
+int dsscomp_state_notifier(struct notifier_block *nb,
+ unsigned long arg, void *ptr);
+
+/* basic operation - if not using queues */
+int set_dss_ovl_info(struct dss2_ovl_info *oi);
+int set_dss_mgr_info(struct dss2_mgr_info *mi, struct omapdss_ovl_cb *cb);
+struct omap_overlay_manager *find_dss_mgr(int display_ix);
+void swap_rb_in_ovl_info(struct dss2_ovl_info *oi);
+void swap_rb_in_mgr_info(struct dss2_mgr_info *mi);
+
+/*
+ * Debug functions
+ */
+void dump_ovl_info(struct dsscomp_dev *cdev, struct dss2_ovl_info *oi);
+void dump_comp_info(struct dsscomp_dev *cdev, struct dsscomp_setup_mgr_data *d,
+ const char *phase);
+void dump_total_comp_info(struct dsscomp_dev *cdev,
+ struct dsscomp_setup_dispc_data *d,
+ const char *phase);
+const char *dsscomp_get_color_name(enum omap_color_mode m);
+
+void dsscomp_dbg_comps(struct seq_file *s);
+void dsscomp_dbg_gralloc(struct seq_file *s);
+
+#define log_state_str(s) (\
+ (s) == DSSCOMP_STATE_ACTIVE ? "ACTIVE" : \
+ (s) == DSSCOMP_STATE_APPLYING ? "APPLY'N" : \
+ (s) == DSSCOMP_STATE_APPLIED ? "APPLIED" : \
+ (s) == DSSCOMP_STATE_PROGRAMMED ? "PROGR'D" : \
+ (s) == DSSCOMP_STATE_DISPLAYED ? "DISPL'D" : "INVALID")
+
+#define log_status_str(ev) ( \
+ ((ev) & DSS_COMPLETION_CHANGED) ? "CHANGED" : \
+ (ev) == DSS_COMPLETION_DISPLAYED ? "DISPLAYED" : \
+ (ev) == DSS_COMPLETION_PROGRAMMED ? "PROGRAMMED" : \
+ (ev) == DSS_COMPLETION_TORN ? "TORN" : \
+ (ev) == DSS_COMPLETION_RELEASED ? "RELEASED" : \
+ ((ev) & DSS_COMPLETION_RELEASED) ? "ECLIPSED" : "???")
+
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+extern struct dbg_event_t {
+ u32 ms, a1, a2, ix;
+ void *data;
+ const char *fmt;
+} dbg_events[128];
+extern u32 dbg_event_ix;
+extern bool alpha_only;
+
+void dsscomp_dbg_events(struct seq_file *s);
+#endif
+
+static inline
+void __log_event(u32 ix, u32 ms, void *data, const char *fmt, u32 a1, u32 a2)
+{
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+ if (!ms)
+ ms = ktime_to_ms(ktime_get());
+ dbg_events[dbg_event_ix].ms = ms;
+ dbg_events[dbg_event_ix].data = data;
+ dbg_events[dbg_event_ix].fmt = fmt;
+ dbg_events[dbg_event_ix].a1 = a1;
+ dbg_events[dbg_event_ix].a2 = a2;
+ dbg_events[dbg_event_ix].ix = ix;
+ dbg_event_ix = (dbg_event_ix + 1) % ARRAY_SIZE(dbg_events);
+#endif
+}
+
+#define log_event(ix, ms, data, fmt, a1, a2) \
+ DO_IF_DEBUG_FS(__log_event(ix, ms, data, fmt, a1, a2))
+
+#endif
diff --git a/drivers/video/omap2/dsscomp/gralloc.c b/drivers/video/omap2/dsscomp/gralloc.c
--- /dev/null
@@ -0,0 +1,637 @@
+/*
+ * drivers/video/omap2/dsscomp/gralloc.c
+ *
+ * DSS Composition gralloc file
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc
+ * Author: Lajos Molnar <molnar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include "../../../drivers/gpu/drm/omapdrm/omap_dmm_tiler.h"
+#include <video/dsscomp.h>
+#include <plat/dsscomp.h>
+#include "dsscomp.h"
+#include "tiler-utils.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;
+ struct tiler_block *block_handle;
+ 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(slot->block_handle);
+ 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;
+
+ /* 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;
+
+ /* only supporting DIRECT buffer types */
+
+ /* 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 && !is_tiler_addr(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);
+ struct dsscomp *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_t(u16, d->num_mgrs, ARRAY_SIZE(d->mgrs));
+ d->num_ovls = min_t(u16, d->num_ovls, 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->output->manager;
+ if (!mgr) {
+ dev_warn(DEV(cdev), "no manager for display%d\n",
+ d->mgrs[i].ix);
+ continue;
+ }
+ ch = mgr->id;
+ channels[i] = ch;
+ 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_uv = registered_fb[fb_ix];
+ fbi = fbi_uv;
+ 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;
+ } else if (oi->addressing != OMAP_DSS_BUFADDR_DIRECT) {
+ goto skip_buffer;
+ }
+
+ /* 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);
+ if (oi->cfg.color_mode == OMAP_DSS_COLOR_NV12)
+ oi->uv = oi->ba + oi->cfg.stride * oi->cfg.height;
+ 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_phys(slot->block_handle, 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;
+ struct dsscomp *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++) {
+ struct tiler_block *block_handle =
+ tiler_reserve_1d(TILER1D_SLOT_SIZE);
+ if (IS_ERR_OR_NULL(block_handle)) {
+ pr_err("could not allocate tiler block\n");
+ break;
+ }
+ slots[i].block_handle = block_handle;
+ slots[i].phys = tiler_ssptr(block_handle);
+ slots[i].size = TILER1D_SLOT_SIZE >> PAGE_SHIFT;
+ slots[i].page_map = vmalloc(sizeof(*slots[i].page_map) *
+ slots[i].size);
+ if (!slots[i].page_map) {
+ pr_err("could not allocate page_map\n");
+ tiler_unpin(block_handle);
+ 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) {
+ vfree(slot->page_map);
+ tiler_unpin(slot->block_handle);
+ }
+ INIT_LIST_HEAD(&free_slots);
+}
diff --git a/drivers/video/omap2/dsscomp/queue.c b/drivers/video/omap2/dsscomp/queue.c
--- /dev/null
@@ -0,0 +1,812 @@
+/*
+ * linux/drivers/video/omap2/dsscomp/queue.c
+ *
+ * DSS Composition queueing support
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc
+ * Author: Lajos Molnar <molnar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ratelimit.h>
+
+#include <video/omapdss.h>
+#include <video/dsscomp.h>
+#include <plat/dsscomp.h>
+
+#include <linux/debugfs.h>
+
+#include "dsscomp.h"
+/* queue state */
+
+static DEFINE_MUTEX(mtx);
+
+/* free overlay structs */
+struct maskref {
+ u32 mask;
+ u32 refs[MAX_OVERLAYS];
+};
+
+static struct {
+ struct workqueue_struct *apply_workq;
+
+ u32 ovl_mask; /* overlays used on this display */
+ struct maskref ovl_qmask; /* overlays queued to this display */
+ bool blanking;
+} mgrq[MAX_MANAGERS];
+
+static struct workqueue_struct *cb_wkq; /* callback work queue */
+static struct dsscomp_dev *cdev;
+
+#ifdef CONFIG_DEBUG_FS
+LIST_HEAD(dbg_comps);
+DEFINE_MUTEX(dbg_mtx); /* Mutex for debug operations */
+#endif
+
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+struct dbg_event_t dbg_events[128];
+u32 dbg_event_ix;
+#endif
+
+static inline void __log_state(struct dsscomp *c, void *fn, u32 ev)
+{
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+ if (c->dbg_used < ARRAY_SIZE(c->dbg_log)) {
+ u32 t = (u32) ktime_to_ms(ktime_get());
+ c->dbg_log[c->dbg_used].t = t;
+ c->dbg_log[c->dbg_used++].state = c->state;
+ __log_event(20 * c->ix + 20, t, c, ev ? "%pf on %s" : "%pf",
+ (u32) fn, (u32) log_status_str(ev));
+ }
+#endif
+}
+#define log_state(c, fn, ev) DO_IF_DEBUG_FS(__log_state(c, fn, ev))
+
+static inline void maskref_incbit(struct maskref *om, u32 ix)
+{
+ om->refs[ix]++;
+ om->mask |= 1 << ix;
+}
+
+static void maskref_decmask(struct maskref *om, u32 mask)
+{
+ while (mask) {
+ u32 ix = fls(mask) - 1, m = 1 << ix;
+ if (!--om->refs[ix])
+ om->mask &= ~m;
+ mask &= ~m;
+ }
+}
+
+/*
+ * ===========================================================================
+ * EXIT
+ * ===========================================================================
+ */
+
+/* Initialize queue structures, and set up state of the displays */
+int dsscomp_queue_init(struct dsscomp_dev *cdev_)
+{
+ u32 i, j;
+ cdev = cdev_;
+
+ if (ARRAY_SIZE(mgrq) < cdev->num_mgrs)
+ return -EINVAL;
+
+ ZERO(mgrq);
+ for (i = 0; i < cdev->num_mgrs; i++) {
+ struct omap_overlay_manager *mgr;
+ mgrq[i].apply_workq =
+ create_singlethread_workqueue("dsscomp_apply");
+ if (!mgrq[i].apply_workq)
+ goto error;
+
+ /* record overlays on this display */
+ mgr = cdev->mgrs[i];
+ for (j = 0; j < cdev->num_ovls; j++) {
+ if (cdev->ovls[j]->is_enabled(cdev->ovls[j]) &&
+ mgr &&
+ cdev->ovls[j]->manager == mgr)
+ mgrq[i].ovl_mask |= 1 << j;
+ }
+ }
+
+ cb_wkq = create_singlethread_workqueue("dsscomp_cb");
+ if (!cb_wkq)
+ goto error;
+
+ return 0;
+error:
+ while (i--)
+ destroy_workqueue(mgrq[i].apply_workq);
+ return -ENOMEM;
+}
+
+/* get display index from manager */
+static u32 get_display_ix(struct omap_overlay_manager *mgr)
+{
+ u32 i;
+
+ /* handle if manager is not attached to a display */
+ if (!mgr || !mgr->output->device)
+ return cdev->num_displays;
+
+ /* find manager's display */
+ for (i = 0; i < cdev->num_displays; i++)
+ if (cdev->displays[i] == mgr->output->device)
+ break;
+
+ return i;
+}
+
+/*
+ * ===========================================================================
+ * QUEUING SETUP OPERATIONS
+ * ===========================================================================
+ */
+
+/* create a new composition for a display */
+struct dsscomp *dsscomp_new(struct omap_overlay_manager *mgr)
+{
+ struct dsscomp *comp = NULL;
+ u32 display_ix = get_display_ix(mgr);
+
+ /* check manager */
+ u32 ix = mgr ? mgr->id : cdev->num_mgrs;
+ if (ix >= cdev->num_mgrs || display_ix >= cdev->num_displays)
+ return ERR_PTR(-EINVAL);
+
+ /* allocate composition */
+ comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return NULL;
+
+ /* initialize new composition */
+ comp->ix = ix; /* save where this composition came from */
+ comp->ovl_mask = 0;
+ comp->ovl_dmask = 0;
+ comp->frm.sync_id = 0;
+ comp->frm.mgr.ix = display_ix;
+ comp->state = DSSCOMP_STATE_ACTIVE;
+
+ DO_IF_DEBUG_FS({
+ __log_state(comp, dsscomp_new, 0);
+ list_add(&comp->dbg_q, &dbg_comps);
+ });
+
+ return comp;
+}
+EXPORT_SYMBOL(dsscomp_new);
+
+/* returns overlays used in a composition */
+u32 dsscomp_get_ovls(struct dsscomp *comp)
+{
+ u32 mask;
+
+ mutex_lock(&mtx);
+ BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
+ mask = comp->ovl_mask;
+ mutex_unlock(&mtx);
+
+ return mask;
+}
+EXPORT_SYMBOL(dsscomp_get_ovls);
+
+/* set overlay info */
+int dsscomp_set_ovl(struct dsscomp *comp, struct dss2_ovl_info *ovl)
+{
+ int r = -EBUSY;
+ u32 i, mask, oix, ix;
+ struct omap_overlay *o;
+
+ mutex_lock(&mtx);
+
+ BUG_ON(!ovl);
+ BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
+
+ ix = comp->ix;
+
+ if (ovl->cfg.ix >= cdev->num_ovls) {
+ r = -EINVAL;
+ goto done;
+ }
+
+ /* if overlay is already part of the composition */
+ mask = 1 << ovl->cfg.ix;
+ if (mask & comp->ovl_mask) {
+ /* look up overlay */
+ for (oix = 0; oix < comp->frm.num_ovls; oix++) {
+ if (comp->ovls[oix].cfg.ix == ovl->cfg.ix)
+ break;
+ }
+ BUG_ON(oix == comp->frm.num_ovls);
+ } else {
+ /* check if ovl is free to use */
+ if (comp->frm.num_ovls >= ARRAY_SIZE(comp->ovls))
+ goto done;
+
+ /* not in any other displays queue */
+ if (mask & ~mgrq[ix].ovl_qmask.mask) {
+ for (i = 0; i < cdev->num_mgrs; i++) {
+ if (i == ix)
+ continue;
+ if (mgrq[i].ovl_qmask.mask & mask)
+ goto done;
+ }
+ }
+
+ /* and disabled (unless forced) if on another manager */
+ o = cdev->ovls[ovl->cfg.ix];
+ if (o->is_enabled(o) && (!o->manager || o->manager->id != ix))
+ goto done;
+
+ /* add overlay to composition & display */
+ comp->ovl_mask |= mask;
+ oix = comp->frm.num_ovls++;
+ maskref_incbit(&mgrq[ix].ovl_qmask, ovl->cfg.ix);
+ }
+
+ comp->ovls[oix] = *ovl;
+ r = 0;
+done:
+ mutex_unlock(&mtx);
+
+ return r;
+}
+EXPORT_SYMBOL(dsscomp_set_ovl);
+
+/* get overlay info */
+int dsscomp_get_ovl(struct dsscomp *comp, u32 ix, struct dss2_ovl_info *ovl)
+{
+ int r;
+ u32 oix;
+
+ mutex_lock(&mtx);
+
+ BUG_ON(!ovl);
+ BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
+
+ if (ix >= cdev->num_ovls) {
+ r = -EINVAL;
+ } else if (comp->ovl_mask & (1 << ix)) {
+ r = 0;
+ for (oix = 0; oix < comp->frm.num_ovls; oix++)
+ if (comp->ovls[oix].cfg.ix == ovl->cfg.ix) {
+ *ovl = comp->ovls[oix];
+ break;
+ }
+ BUG_ON(oix == comp->frm.num_ovls);
+ } else {
+ r = -ENOENT;
+ }
+
+ mutex_unlock(&mtx);
+
+ return r;
+}
+EXPORT_SYMBOL(dsscomp_get_ovl);
+
+/* set manager info */
+int dsscomp_set_mgr(struct dsscomp *comp, struct dss2_mgr_info *mgr)
+{
+ mutex_lock(&mtx);
+
+ BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
+ BUG_ON(mgr->ix != comp->frm.mgr.ix);
+
+ comp->frm.mgr = *mgr;
+
+ mutex_unlock(&mtx);
+
+ return 0;
+}
+EXPORT_SYMBOL(dsscomp_set_mgr);
+
+/* get manager info */
+int dsscomp_get_mgr(struct dsscomp *comp, struct dss2_mgr_info *mgr)
+{
+ mutex_lock(&mtx);
+
+ BUG_ON(!mgr);
+ BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
+
+ *mgr = comp->frm.mgr;
+
+ mutex_unlock(&mtx);
+
+ return 0;
+}
+EXPORT_SYMBOL(dsscomp_get_mgr);
+
+/* get manager info */
+int dsscomp_setup(struct dsscomp *comp, enum dsscomp_setup_mode mode,
+ struct dss2_rect_t win)
+{
+ mutex_lock(&mtx);
+
+ BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
+
+ comp->frm.mode = mode;
+ comp->frm.win = win;
+
+ mutex_unlock(&mtx);
+
+ return 0;
+}
+EXPORT_SYMBOL(dsscomp_setup);
+
+/*
+ * ===========================================================================
+ * QUEUING COMMITTING OPERATIONS
+ * ===========================================================================
+ */
+void dsscomp_drop(struct dsscomp *comp)
+{
+ /* decrement unprogrammed references */
+ if (comp->state < DSSCOMP_STATE_PROGRAMMED)
+ maskref_decmask(&mgrq[comp->ix].ovl_qmask, comp->ovl_mask);
+ comp->state = 0;
+
+ if (debug & DEBUG_COMPOSITIONS)
+ dev_info(DEV(cdev), "[%p] released\n", comp);
+
+ DO_IF_DEBUG_FS(list_del(&comp->dbg_q));
+
+ kfree(comp);
+}
+EXPORT_SYMBOL(dsscomp_drop);
+
+struct dsscomp_cb_work {
+ struct work_struct work;
+ struct dsscomp *comp;
+ int status;
+};
+
+static void dsscomp_mgr_delayed_cb(struct work_struct *work)
+{
+ struct dsscomp_cb_work *wk = container_of(work, typeof(*wk), work);
+ struct dsscomp *comp = wk->comp;
+ int status = wk->status;
+ u32 ix;
+
+ kfree(work);
+
+ mutex_lock(&mtx);
+
+ BUG_ON(comp->state == DSSCOMP_STATE_ACTIVE);
+ ix = comp->ix;
+
+ /* call extra callbacks if requested */
+ if (comp->extra_cb)
+ comp->extra_cb(comp->extra_cb_data, status);
+
+ /* handle programming & release */
+ if (status == DSS_COMPLETION_PROGRAMMED) {
+ comp->state = DSSCOMP_STATE_PROGRAMMED;
+ log_state(comp, dsscomp_mgr_delayed_cb, status);
+
+ /* update used overlay mask */
+ mgrq[ix].ovl_mask = comp->ovl_mask & ~comp->ovl_dmask;
+ maskref_decmask(&mgrq[ix].ovl_qmask, comp->ovl_mask);
+
+ if (debug & DEBUG_PHASES)
+ dev_info(DEV(cdev), "[%p] programmed\n", comp);
+ } else if ((status == DSS_COMPLETION_DISPLAYED) &&
+ comp->state == DSSCOMP_STATE_PROGRAMMED) {
+ /* composition is 1st displayed */
+ comp->state = DSSCOMP_STATE_DISPLAYED;
+ log_state(comp, dsscomp_mgr_delayed_cb, status);
+ if (debug & DEBUG_PHASES)
+ dev_info(DEV(cdev), "[%p] displayed\n", comp);
+ } else if (status & DSS_COMPLETION_RELEASED) {
+ /* composition is no longer displayed */
+ log_event(20 * comp->ix + 20, 0, comp, "%pf on %s",
+ (u32)dsscomp_mgr_delayed_cb,
+ (u32)log_status_str(status));
+ dsscomp_drop(comp);
+ }
+ mutex_unlock(&mtx);
+}
+
+static u32 dsscomp_mgr_callback(void *data, int id, int status)
+{
+ struct dsscomp *comp = data;
+
+ if (status == DSS_COMPLETION_PROGRAMMED ||
+ (status == DSS_COMPLETION_DISPLAYED &&
+ comp->state != DSSCOMP_STATE_DISPLAYED) ||
+ (status & DSS_COMPLETION_RELEASED)) {
+ struct dsscomp_cb_work *wk = kzalloc(sizeof(*wk), GFP_ATOMIC);
+ wk->comp = comp;
+ wk->status = status;
+ INIT_WORK(&wk->work, dsscomp_mgr_delayed_cb);
+ queue_work(cb_wkq, &wk->work);
+ }
+
+ /* get each callback only once */
+ return ~status;
+}
+
+static inline bool dssdev_manually_updated(struct omap_dss_device *dev)
+{
+ return dev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+}
+
+/* apply composition */
+/* at this point the composition is not on any queue */
+static int dsscomp_apply(struct dsscomp *comp)
+{
+ int i, r = -EFAULT;
+ u32 dmask, display_ix;
+ struct omap_dss_device *dssdev;
+ struct omap_dss_driver *drv;
+ struct omap_overlay_manager *mgr;
+ struct omap_overlay *ovl;
+ struct dsscomp_setup_mgr_data *d;
+ u32 oix;
+ bool cb_programmed = false;
+
+ struct omapdss_ovl_cb cb = {
+ .fn = dsscomp_mgr_callback,
+ .data = comp,
+ .mask = DSS_COMPLETION_DISPLAYED |
+ DSS_COMPLETION_PROGRAMMED | DSS_COMPLETION_RELEASED,
+ };
+
+ BUG_ON(comp->state != DSSCOMP_STATE_APPLYING);
+
+ /* check if the display is valid and used */
+ r = -ENODEV;
+ d = &comp->frm;
+ display_ix = d->mgr.ix;
+ if (display_ix >= cdev->num_displays)
+ goto done;
+ dssdev = cdev->displays[display_ix];
+ if (!dssdev)
+ goto done;
+
+ drv = dssdev->driver;
+ mgr = dssdev->output->manager;
+ if (!mgr || !drv || mgr->id >= cdev->num_mgrs)
+ goto done;
+
+ dump_comp_info(cdev, d, "apply");
+
+ r = 0;
+ dmask = 0;
+ for (oix = 0; oix < comp->frm.num_ovls; oix++) {
+ struct dss2_ovl_info *oi = comp->ovls + oix;
+
+ /* keep track of disabled overlays */
+ if (!oi->cfg.enabled)
+ dmask |= 1 << oi->cfg.ix;
+
+ if (r && !comp->must_apply)
+ continue;
+
+ dump_ovl_info(cdev, oi);
+
+ if (oi->cfg.ix >= cdev->num_ovls) {
+ r = -EINVAL;
+ continue;
+ }
+ ovl = cdev->ovls[oi->cfg.ix];
+
+ /* set overlays' manager & info */
+ if (ovl->is_enabled(ovl) && ovl->manager != mgr) {
+ r = -EBUSY;
+ goto skip_ovl_set;
+ }
+ if (ovl->manager != mgr) {
+ /* :NOTE: ignore error from unset */
+ ovl->unset_manager(ovl);
+ r = ovl->set_manager(ovl, mgr);
+ if (r)
+ goto skip_ovl_set;
+ }
+
+ r = set_dss_ovl_info(oi);
+skip_ovl_set:
+ if (r && comp->must_apply) {
+ dev_err(DEV(cdev), "[%p] set ovl%d failed %d",
+ comp, oi->cfg.ix, r);
+ oi->cfg.enabled = false;
+ dmask |= 1 << oi->cfg.ix;
+ set_dss_ovl_info(oi);
+ }
+ }
+
+ /*
+ * set manager's info - this also sets the completion callback,
+ * so if it succeeds, we will use the callback to complete the
+ * composition. Otherwise, we can skip the composition now.
+ */
+ if (!r || comp->must_apply) {
+ r = set_dss_mgr_info(&d->mgr, &cb);
+ cb_programmed = r == 0;
+ }
+
+ if (r && !comp->must_apply) {
+ dev_err(DEV(cdev), "[%p] set failed %d\n", comp, r);
+ goto done;
+ } else {
+ if (r)
+ dev_warn(DEV(cdev), "[%p] ignoring set failure %d\n",
+ comp, r);
+ comp->blank = dmask == comp->ovl_mask;
+ comp->ovl_dmask = dmask;
+
+ /*
+ * Check other overlays that may also use this display.
+ * NOTE: This is only needed in case someone changes
+ * overlays via sysfs. We use comp->ovl_mask to refresh
+ * the overlays actually used on a manager when the
+ * composition is programmed.
+ */
+ for (i = 0; i < cdev->num_ovls; i++) {
+ u32 mask = 1 << i;
+ if ((~comp->ovl_mask & mask) &&
+ cdev->ovls[i]->is_enabled(cdev->ovls[i]) &&
+ cdev->ovls[i]->manager == mgr) {
+ mutex_lock(&mtx);
+ comp->ovl_mask |= mask;
+ maskref_incbit(&mgrq[comp->ix].ovl_qmask, i);
+ mutex_unlock(&mtx);
+ }
+ }
+ }
+
+ /* apply changes and call update on manual panels */
+ /* no need for mutex as no callbacks are scheduled yet */
+ comp->state = DSSCOMP_STATE_APPLIED;
+ log_state(comp, dsscomp_apply, 0);
+
+ if (!d->win.w && !d->win.x)
+ d->win.w = dssdev->panel.timings.x_res - d->win.x;
+ if (!d->win.h && !d->win.y)
+ d->win.h = dssdev->panel.timings.y_res - d->win.y;
+
+ mutex_lock(&mtx);
+ if (mgrq[comp->ix].blanking) {
+ pr_info_ratelimited("ignoring apply mgr(%s) while blanking\n",
+ mgr->name);
+ r = -ENODEV;
+ } else {
+ r = mgr->apply(mgr);
+ if (r)
+ dev_err(DEV(cdev), "failed while applying %d", r);
+ /* keep error if set_mgr_info failed */
+ if (!r && !cb_programmed)
+ r = -EINVAL;
+ }
+ mutex_unlock(&mtx);
+
+ /*
+ * TRICKY: try to unregister callback to see if callbacks have
+ * been applied (moved into DSS2 pipeline). Unregistering also
+ * avoids having to unnecessarily kick out compositions (which
+ * would result in screen blinking). If callbacks failed to apply,
+ * (e.g. could not set them or apply them) we will need to call
+ * them ourselves (we note this by returning an error).
+ */
+ if (cb_programmed && r) {
+ /* clear error if callback already registered */
+ if (omap_dss_manager_unregister_callback(mgr, &cb))
+ r = 0;
+ }
+ /* if failed to apply, kick out prior composition */
+ if (comp->must_apply && r)
+ mgr->blank(mgr, true);
+
+ if (!r && (d->mode & DSSCOMP_SETUP_MODE_DISPLAY)) {
+ /* cannot handle update errors, so ignore them */
+ if (dssdev_manually_updated(dssdev) && drv->sync)
+ drv->update(dssdev, d->win.x, d->win.y, d->win.w,
+ d->win.h);
+ else
+ /* wait for sync to do smooth animations */
+ mgr->wait_for_vsync(mgr);
+ }
+
+done:
+ return r;
+}
+
+struct dsscomp_apply_work {
+ struct work_struct work;
+ struct dsscomp *comp;
+};
+
+int dsscomp_state_notifier(struct notifier_block *nb,
+ unsigned long arg, void *ptr)
+{
+ struct omap_dss_device *dssdev = ptr;
+ enum omap_dss_display_state state = arg;
+ struct omap_overlay_manager *mgr = dssdev->output->manager;
+ if (mgr) {
+ mutex_lock(&mtx);
+ if (state == OMAP_DSS_DISPLAY_DISABLED) {
+ mgr->blank(mgr, true);
+ mgrq[mgr->id].blanking = true;
+ } else if (state == OMAP_DSS_DISPLAY_ACTIVE) {
+ mgrq[mgr->id].blanking = false;
+ }
+ mutex_unlock(&mtx);
+ }
+ return 0;
+}
+
+
+static void dsscomp_do_apply(struct work_struct *work)
+{
+ struct dsscomp_apply_work *wk = container_of(work, typeof(*wk), work);
+ /* complete compositions that failed to apply */
+ if (dsscomp_apply(wk->comp))
+ dsscomp_mgr_callback(wk->comp, -1, DSS_COMPLETION_ECLIPSED_SET);
+ kfree(wk);
+}
+
+int dsscomp_delayed_apply(struct dsscomp *comp)
+{
+ /* don't block in case we are called from interrupt context */
+ struct dsscomp_apply_work *wk = kzalloc(sizeof(*wk), GFP_NOWAIT);
+ if (!wk)
+ return -ENOMEM;
+
+ mutex_lock(&mtx);
+
+ BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
+ comp->state = DSSCOMP_STATE_APPLYING;
+ log_state(comp, dsscomp_delayed_apply, 0);
+
+ if (debug & DEBUG_PHASES)
+ dev_info(DEV(cdev), "[%p] applying\n", comp);
+ mutex_unlock(&mtx);
+
+ wk->comp = comp;
+ INIT_WORK(&wk->work, dsscomp_do_apply);
+ return queue_work(mgrq[comp->ix].apply_workq, &wk->work) ? 0 : -EBUSY;
+}
+EXPORT_SYMBOL(dsscomp_delayed_apply);
+
+/*
+ * ===========================================================================
+ * DEBUGFS
+ * ===========================================================================
+ */
+
+#ifdef CONFIG_DEBUG_FS
+static void seq_print_comp(struct seq_file *s, struct dsscomp *c)
+{
+ struct dsscomp_setup_mgr_data *d = &c->frm;
+ int i;
+
+ seq_printf(s, " [%p]: %s%s\n", c, c->blank ? "blank " : "",
+ c->state == DSSCOMP_STATE_ACTIVE ? "ACTIVE" :
+ c->state == DSSCOMP_STATE_APPLYING ? "APPLYING" :
+ c->state == DSSCOMP_STATE_APPLIED ? "APPLIED" :
+ c->state == DSSCOMP_STATE_PROGRAMMED ? "PROGRAMMED" :
+ c->state == DSSCOMP_STATE_DISPLAYED ? "DISPLAYED" :
+ "???");
+ seq_printf(s, " sync_id=%x, flags=%c%c%c\n",
+ d->sync_id,
+ (d->mode & DSSCOMP_SETUP_MODE_APPLY) ? 'A' : '-',
+ (d->mode & DSSCOMP_SETUP_MODE_DISPLAY) ? 'D' : '-',
+ (d->mode & DSSCOMP_SETUP_MODE_CAPTURE) ? 'C' : '-');
+ for (i = 0; i < d->num_ovls; i++) {
+ struct dss2_ovl_info *oi;
+ struct dss2_ovl_cfg *g;
+ oi = d->ovls + i;
+ g = &oi->cfg;
+ if (g->zonly) {
+ seq_printf(s, " ovl%d={%s z%d}\n", g->ix,
+ g->enabled ? "ON" : "off",
+ g->zorder);
+ } else {
+ seq_printf(s, " ovl%d={%s ", g->ix,
+ g->enabled ? "ON" : "off");
+ seq_printf(s, "z%d %s", g->zorder,
+ dsscomp_get_color_name(g->color_mode) ?
+ : "N/A");
+ seq_printf(s, "%s ", g->pre_mult_alpha ?
+ " premult" : "");
+ seq_printf(s, "*%d%%", (g->global_alpha * 100 + 128) /
+ 255);
+ seq_printf(s, "%d*%d:%d,", g->width, g->height,
+ g->crop.x);
+ seq_printf(s, "%d+%d,%d ", g->crop.y, g->crop.w,
+ g->crop.h);
+ seq_printf(s, "rot%d%s => ", g->rotation, g->mirror ?
+ "+mir" : "");
+ seq_printf(s, "%d,%d+%d, ", g->win.x, g->win.y,
+ g->win.w);
+ seq_printf(s, "%d %p/%p|%d}\n", g->win.h,
+ (void *)oi->ba, (void *)oi->uv,
+ g->stride);
+ }
+ }
+ if (c->extra_cb)
+ seq_printf(s, " gsync=[%p] %pf\n\n", c->extra_cb_data,
+ c->extra_cb);
+ else
+ seq_printf(s, " gsync=[%p] (called)\n\n", c->extra_cb_data);
+}
+#endif
+
+void dsscomp_dbg_comps(struct seq_file *s)
+{
+#ifdef CONFIG_DEBUG_FS
+ struct dsscomp *c;
+ u32 i;
+
+ mutex_lock(&dbg_mtx);
+ for (i = 0; i < cdev->num_mgrs; i++) {
+ struct omap_overlay_manager *mgr = cdev->mgrs[i];
+ seq_printf(s, "ACTIVE COMPOSITIONS on %s\n\n", mgr->name);
+ list_for_each_entry(c, &dbg_comps, dbg_q) {
+ struct dss2_mgr_info *mi = &c->frm.mgr;
+ if (mi->ix < cdev->num_displays &&
+ cdev->displays[mi->ix]->output->manager == mgr)
+ seq_print_comp(s, c);
+ }
+
+ /* print manager cache */
+ mgr->dump_cb(mgr, s);
+ }
+ mutex_unlock(&dbg_mtx);
+#endif
+}
+
+void dsscomp_dbg_events(struct seq_file *s)
+{
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+ u32 i;
+ struct dbg_event_t *d;
+
+ mutex_lock(&dbg_mtx);
+ for (i = dbg_event_ix; i < dbg_event_ix + ARRAY_SIZE(dbg_events); i++) {
+ d = dbg_events + (i % ARRAY_SIZE(dbg_events));
+ if (!d->ms)
+ continue;
+ seq_printf(s, "[% 5d.%03d] %*s[%08x] ",
+ d->ms / 1000, d->ms % 1000,
+ d->ix + ((u32) d->data) % 7,
+ "", (u32) d->data);
+ seq_printf(s, d->fmt, d->a1, d->a2);
+ seq_printf(s, "\n");
+ }
+ mutex_unlock(&dbg_mtx);
+#endif
+}
+
+/*
+ * ===========================================================================
+ * EXIT
+ * ===========================================================================
+ */
+void dsscomp_queue_exit(void)
+{
+ if (cdev) {
+ int i;
+ for (i = 0; i < cdev->num_displays; i++)
+ destroy_workqueue(mgrq[i].apply_workq);
+ destroy_workqueue(cb_wkq);
+ cdev = NULL;
+ }
+}
+EXPORT_SYMBOL(dsscomp_queue_exit);
diff --git a/drivers/video/omap2/dsscomp/tiler-utils.c b/drivers/video/omap2/dsscomp/tiler-utils.c
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * drivers/video/omap2/dsscomp/tiler-utils.c
+ *
+ * Tiler Utility APIs source file
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc
+ * Author: Sreenidhi Koti <sreenidhi@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/shmem_fs.h>
+
+#include "tiler-utils.h"
+/* ==========================================================================
+ * Kernel APIs
+ * ==========================================================================
+ */
+
+u32 tiler_virt2phys(u32 usr)
+{
+ pmd_t *pmd;
+ pte_t *ptep;
+ pgd_t *pgd = pgd_offset(current->mm, usr);
+
+ if (pgd_none(*pgd) || pgd_bad(*pgd))
+ return 0;
+
+ pmd = pmd_offset((pud_t *)pgd, usr);
+ if (pmd_none(*pmd) || pmd_bad(*pmd))
+ return 0;
+
+ ptep = pte_offset_map(pmd, usr);
+ if (ptep && pte_present(*ptep))
+ return (*ptep & PAGE_MASK) | (~PAGE_MASK & usr);
+
+ return 0;
+}
+
+void tiler_pa_free(struct tiler_pa_info *pa)
+{
+ if (pa)
+ kfree(pa->mem);
+ kfree(pa);
+}
+
+/* get physical pages of a user block */
+struct tiler_pa_info *user_block_to_pa(u32 usr_addr, u32 num_pg)
+{
+ struct task_struct *curr_task = current;
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma = NULL;
+
+ struct tiler_pa_info *pa = NULL;
+ struct page **pages = NULL;
+ u32 *mem = NULL, write, i;
+ int usr_count;
+
+ pa = kzalloc(sizeof(*pa), GFP_KERNEL);
+ if (!pa)
+ return NULL;
+
+ mem = kzalloc(num_pg * sizeof(*mem), GFP_KERNEL);
+ if (!mem) {
+ kfree(pa);
+ return NULL;
+ }
+
+ pages = kmalloc(num_pg * sizeof(*pages), GFP_KERNEL);
+ if (!pages) {
+ kfree(mem);
+ kfree(pa);
+ return NULL;
+ }
+
+ /*
+ * Important Note: usr_addr is mapped from user
+ * application process to current process - it must lie
+ * completely within the current virtual memory address
+ * space in order to be of use to us here.
+ */
+ down_read(&mm->mmap_sem);
+ vma = find_vma(mm, usr_addr + (num_pg << PAGE_SHIFT) - 1);
+
+ if (!vma || (usr_addr < vma->vm_start)) {
+ kfree(mem);
+ kfree(pa);
+ kfree(pages);
+ up_read(&mm->mmap_sem);
+ printk(KERN_ERR "Address is outside VMA: ");
+ printk(KERN_ERR "address start = %08x", usr_addr);
+ printk(KERN_ERR "user end = %08x\n", (usr_addr +
+ (num_pg << PAGE_SHIFT)));
+ return ERR_PTR(-EFAULT);
+ }
+
+ if (vma->vm_flags & (VM_WRITE | VM_MAYWRITE))
+ write = 1;
+
+ usr_count = get_user_pages(curr_task, mm, usr_addr, num_pg, write, 1,
+ pages, NULL);
+
+ if (usr_count > 0) {
+ /* process user allocated buffer */
+ if (usr_count != num_pg) {
+ /* release the pages we did get */
+ for (i = 0; i < usr_count; i++)
+ page_cache_release(pages[i]);
+ } else {
+ /* fill in the physical address information */
+ for (i = 0; i < num_pg; i++) {
+ mem[i] = page_to_phys(pages[i]);
+ BUG_ON(pages[i] != phys_to_page(mem[i]));
+ }
+ }
+ } else {
+ /* fallback for kernel allocated buffers */
+ for (i = 0; i < num_pg; i++) {
+ mem[i] = tiler_virt2phys(usr_addr);
+
+ if (!mem[i]) {
+ printk(KERN_ERR "VMA not in page table\n");
+ break;
+ }
+
+ usr_addr += PAGE_SIZE;
+ }
+ }
+
+ up_read(&mm->mmap_sem);
+
+ kfree(pages);
+
+ /* if failed to map all pages */
+ if (i < num_pg) {
+ kfree(mem);
+ kfree(pa);
+ return ERR_PTR(-EFAULT);
+ }
+
+ pa->mem = mem;
+ pa->memtype = usr_count > 0 ? TILER_MEM_GOT_PAGES : TILER_MEM_USING;
+ pa->num_pg = num_pg;
+ return pa;
+}
diff --git a/drivers/video/omap2/dsscomp/tiler-utils.h b/drivers/video/omap2/dsscomp/tiler-utils.h
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * drivers/video/omap2/dsscomp/tiler-utils.h
+ *
+ * Tiler Utility APIs header file
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc
+ * Author: Sreenidhi Koti <sreenidhi@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* type of tiler memory */
+enum tiler_memtype {
+ TILER_MEM_ALLOCED, /* tiler allocated the memory */
+ TILER_MEM_GOT_PAGES, /* tiler used get_user_pages */
+ TILER_MEM_USING, /* tiler is using the pages */
+};
+
+/* physical pages to pin - mem must be kmalloced */
+struct tiler_pa_info {
+ u32 num_pg; /* number of pages in page-list */
+ u32 *mem; /* list of phys page addresses */
+ enum tiler_memtype memtype; /* how we got physical pages */
+};
+
+/**
+ * Frees a tiler_pa_info structure and associated memory
+ *
+ * @param pa Ptr to tiler_pa_info structure
+ *
+ */
+void tiler_pa_free(struct tiler_pa_info *pa);
+
+/**
+ * Creates and returns a tiler_pa_info structure from a user address
+ *
+ * @param usr_addr User Address
+ * @param num_pg Number of pages
+ *
+ * @return Ptr to new tiler_pa_info structure
+ */
+struct tiler_pa_info *user_block_to_pa(u32 usr_addr, u32 num_pg);
+/**
+ * Get the physical address for a given user va.
+ *
+ * @param usr user virtual address
+ *
+ * @return valid pa or 0 for error
+ */
+u32 tiler_virt2phys(u32 usr);
diff --git a/include/video/dsscomp.h b/include/video/dsscomp.h
--- /dev/null
+++ b/include/video/dsscomp.h
@@ -0,0 +1,666 @@
+/*
+ * include/video/dsscomp.h
+ *
+ * DSS Composition header file
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc
+ * Author: Lajos Molnar <molnar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LINUX_DSSCOMP_H
+#define _LINUX_DSSCOMP_H
+
+#ifdef __KERNEL__
+#include <video/omapdss.h>
+#else
+
+/* exporting enumerations from arch/arm/plat-omap/include/plat/display.h */
+enum omap_plane {
+ OMAP_DSS_GFX = 0,
+ OMAP_DSS_VIDEO1 = 1,
+ OMAP_DSS_VIDEO2 = 2,
+ OMAP_DSS_VIDEO3 = 3,
+ OMAP_DSS_WB = 4,
+};
+
+enum omap_channel {
+ OMAP_DSS_CHANNEL_LCD = 0,
+ OMAP_DSS_CHANNEL_DIGIT = 1,
+ OMAP_DSS_CHANNEL_LCD2 = 2,
+};
+
+enum omap_color_mode {
+ OMAP_DSS_COLOR_CLUT1 = 1 << 0, /* BITMAP 1 */
+ OMAP_DSS_COLOR_CLUT2 = 1 << 1, /* BITMAP 2 */
+ OMAP_DSS_COLOR_CLUT4 = 1 << 2, /* BITMAP 4 */
+ OMAP_DSS_COLOR_CLUT8 = 1 << 3, /* BITMAP 8 */
+
+ /* also referred to as RGB 12-BPP, 16-bit container */
+ OMAP_DSS_COLOR_RGB12U = 1 << 4, /* xRGB12-4444 */
+ OMAP_DSS_COLOR_ARGB16 = 1 << 5, /* ARGB16-4444 */
+ OMAP_DSS_COLOR_RGB16 = 1 << 6, /* RGB16-565 */
+
+ /* also referred to as RGB 24-BPP, 32-bit container */
+ OMAP_DSS_COLOR_RGB24U = 1 << 7, /* xRGB24-8888 */
+ OMAP_DSS_COLOR_RGB24P = 1 << 8, /* RGB24-888 */
+ OMAP_DSS_COLOR_YUV2 = 1 << 9, /* YUV2 4:2:2 co-sited */
+ OMAP_DSS_COLOR_UYVY = 1 << 10, /* UYVY 4:2:2 co-sited */
+ OMAP_DSS_COLOR_ARGB32 = 1 << 11, /* ARGB32-8888 */
+ OMAP_DSS_COLOR_RGBA32 = 1 << 12, /* RGBA32-8888 */
+
+ /* also referred to as RGBx 32 in TRM */
+ OMAP_DSS_COLOR_RGBX24 = 1 << 13, /* RGBx32-8888 */
+ OMAP_DSS_COLOR_RGBX32 = 1 << 13, /* RGBx32-8888 */
+ OMAP_DSS_COLOR_NV12 = 1 << 14, /* NV12 format: YUV 4:2:0 */
+
+ /* also referred to as RGBA12-4444 in TRM */
+ OMAP_DSS_COLOR_RGBA16 = 1 << 15, /* RGBA16-4444 */
+
+ OMAP_DSS_COLOR_RGBX12 = 1 << 16, /* RGBx16-4444 */
+ OMAP_DSS_COLOR_RGBX16 = 1 << 16, /* RGBx16-4444 */
+ OMAP_DSS_COLOR_ARGB16_1555 = 1 << 17, /* ARGB16-1555 */
+
+ /* also referred to as xRGB16-555 in TRM */
+ OMAP_DSS_COLOR_XRGB15 = 1 << 18, /* xRGB16-1555 */
+ OMAP_DSS_COLOR_XRGB16_1555 = 1 << 18, /* xRGB16-1555 */
+};
+
+enum omap_dss_trans_key_type {
+ OMAP_DSS_COLOR_KEY_GFX_DST = 0,
+ OMAP_DSS_COLOR_KEY_VID_SRC = 1,
+};
+
+enum omap_dss_display_state {
+ OMAP_DSS_DISPLAY_DISABLED = 0,
+ OMAP_DSS_DISPLAY_ACTIVE,
+ OMAP_DSS_DISPLAY_SUSPENDED,
+ OMAP_DSS_DISPLAY_TRANSITION,
+};
+
+struct omap_video_timings {
+ /* Unit: pixels */
+ __u16 x_res;
+ /* Unit: pixels */
+ __u16 y_res;
+ /* Unit: KHz */
+ __u32 pixel_clock;
+ /* Unit: pixel clocks */
+ __u16 hsw; /* Horizontal synchronization pulse width */
+ /* Unit: pixel clocks */
+ __u16 hfp; /* Horizontal front porch */
+ /* Unit: pixel clocks */
+ __u16 hbp; /* Horizontal back porch */
+ /* Unit: line clocks */
+ __u16 vsw; /* Vertical synchronization pulse width */
+ /* Unit: line clocks */
+ __u16 vfp; /* Vertical front porch */
+ /* Unit: line clocks */
+ __u16 vbp; /* Vertical back porch */
+};
+
+/* YUV to RGB color conversion info */
+struct omap_dss_cconv_coefs {
+ __s16 ry, rcr, rcb;
+ __s16 gy, gcr, gcb;
+ __s16 by, bcr, bcb;
+
+ /* Y is 16..235, UV is 16..240 if not fullrange. Otherwise 0..255 */
+ __u16 full_range;
+} __aligned(4);
+
+struct omap_dss_cpr_coefs {
+ __s16 rr, rg, rb;
+ __s16 gr, gg, gb;
+ __s16 br, bg, bb;
+};
+
+/*
+ * Stereoscopic Panel types
+ * row, column, overunder, sidebyside options
+ * are with respect to native scan order
+ */
+enum s3d_disp_type {
+ S3D_DISP_NONE = 0,
+ S3D_DISP_FRAME_SEQ,
+ S3D_DISP_ROW_IL,
+ S3D_DISP_COL_IL,
+ S3D_DISP_PIX_IL,
+ S3D_DISP_CHECKB,
+ S3D_DISP_OVERUNDER,
+ S3D_DISP_SIDEBYSIDE,
+};
+
+/* Subsampling direction is based on native panel scan order.*/
+enum s3d_disp_sub_sampling {
+ S3D_DISP_SUB_SAMPLE_NONE = 0,
+ S3D_DISP_SUB_SAMPLE_V,
+ S3D_DISP_SUB_SAMPLE_H,
+};
+
+/*
+ * Indicates if display expects left view first followed by right or viceversa
+ * For row interlaved displays, defines first row view
+ * For column interleaved displays, defines first column view
+ * For checkerboard, defines first pixel view
+ * For overunder, defines top view
+ * For sidebyside, defines west view
+ */
+enum s3d_disp_order {
+ S3D_DISP_ORDER_L = 0,
+ S3D_DISP_ORDER_R = 1,
+};
+
+/*
+ * Indicates current view
+ * Used mainly for displays that need to trigger a sync signal
+ */
+enum s3d_disp_view {
+ S3D_DISP_VIEW_L = 0,
+ S3D_DISP_VIEW_R,
+};
+
+struct s3d_disp_info {
+ enum s3d_disp_type type;
+ enum s3d_disp_sub_sampling sub_samp;
+ enum s3d_disp_order order;
+ /*
+ * Gap between left and right views
+ * For over/under units are lines
+ * For sidebyside units are pixels
+ * For other types ignored
+ */
+ unsigned int gap;
+};
+
+#endif
+
+/* copy of fb_videomode */
+struct dsscomp_videomode {
+ const char *name; /* optional */
+ __u32 refresh; /* optional */
+ __u32 xres;
+ __u32 yres;
+ __u32 pixclock;
+ __u32 left_margin;
+ __u32 right_margin;
+ __u32 upper_margin;
+ __u32 lower_margin;
+ __u32 hsync_len;
+ __u32 vsync_len;
+ __u32 sync;
+ __u32 vmode;
+ __u32 flag;
+};
+
+enum omap_dss_ilace_mode {
+ OMAP_DSS_ILACE = (1 << 0), /* interlaced vs. progressive */
+ OMAP_DSS_ILACE_SEQ = (1 << 1), /* sequential vs interleaved */
+ OMAP_DSS_ILACE_SWAP = (1 << 2), /* swap fields, e.g. TB=>BT */
+
+ OMAP_DSS_ILACE_NONE = 0,
+ OMAP_DSS_ILACE_IL_TB = OMAP_DSS_ILACE,
+ OMAP_DSS_ILACE_IL_BT = OMAP_DSS_ILACE | OMAP_DSS_ILACE_SWAP,
+ OMAP_DSS_ILACE_SEQ_TB = OMAP_DSS_ILACE_IL_TB | OMAP_DSS_ILACE_SEQ,
+ OMAP_DSS_ILACE_SEQ_BT = OMAP_DSS_ILACE_IL_BT | OMAP_DSS_ILACE_SEQ,
+};
+
+/* YUV VC1 range mapping info */
+struct dss2_vc1_range_map_info {
+ __u8 enable; /* bool */
+
+ __u8 range_y; /* 0..7 */
+ __u8 range_uv; /* 0..7 */
+} __aligned(4);
+
+/* standard rectangle */
+struct dss2_rect_t {
+ __s32 x; /* left */
+ __s32 y; /* top */
+ __u32 w; /* width */
+ __u32 h; /* height */
+} __aligned(4);
+
+/* decimation constraints */
+struct dss2_decim {
+ __u8 min_x;
+ __u8 max_x; /* 0 is same as 255 */
+ __u8 min_y;
+ __u8 max_y; /* 0 is same as 255 */
+} __aligned(4);
+
+/*
+ * A somewhat more user friendly interface to the DSS2. This is a
+ * direct interface to the DSS2 overlay and overlay_manager modules.
+ * User-space APIs are provided for HW-specific control of DSS in
+ * contrast with V4L2/FB that are more generic, but in this process
+ * omit HW-specific features.
+ *
+ * For now managers are specified by display index as opposed to manager
+ * type, so that display0 is always the default display (e.g. HDMI on
+ * panda, and LCD blaze.) For now you would need to query the displays
+ * or use sysfs to find a specific display.
+ *
+ * Userspace operations are as follows:
+ *
+ * 1) check if DSS supports an overlay configuration, use DSSCIOC_CHECK_OVL
+ * ioctl with the manager, overlay, and setup-mode information filled out.
+ * All fields should be filled out as it may influence whether DSS can
+ * display/render the overlay.
+ *
+ * If proper address information is not available, it may be possible to
+ * use a type-of-address enumeration instead for luma/rgb and chroma (if
+ * applicable) frames.
+ *
+ * Do this for each overlay before attempting to configure DSS.
+ *
+ * 2) configure DSS pipelines for display/manager using DSSCIOC_SETUP_MANAGER
+ * ioctl. You can delay applying the settings until an dss2_manager_apply()
+ * is called for the internal composition object, if the APPLY bit of setup mode
+ * is not set. However the CAPTURE/DISPLAY bits of the setup mode settings will
+ * determine if at this time a capture will take place (in case of capture
+ * only mode). You may also set up additional pipelines with
+ * dss2_overlay_setup() before this.
+ *
+ * 3) On OMAP4/5 you can use the DSS WB pipeline to copy (and convert) a buffer
+ * using DSS. Use the DSSCIOC_WB_COPY ioctl for this. This is a blocking
+ * call, and it may possibly fail if an ongoing WB capture mode has been
+ * scheduled (which is outside of the current scope of the DSS2 interface.)
+ *
+ * There is also a one-shot configuration API (DSSCIOC_SETUP_DISPC). This
+ * allows you to set-up all overlays on all managers in one call. This call
+ * performs additional functionality:
+ *
+ * - it maps userspace 1D buffers into TILER 1D for the duration of the display
+ * - it disables all overlays that were specified before, but are no longer
+ * specified
+ *
+ */
+
+/*
+ * DSS2 overlay information. This structure contains all information
+ * needed to set up the overlay for a particular buffer to be displayed
+ * at a particular orientation.
+ *
+ * The following information is deemed to be set globally, so it is not
+ * included:
+ * - whether to enable zorder (always enabled)
+ * - whether to replicate/truncate color fields (it is decided per the
+ * whole manager/overlay settings, and is enabled unless overlay is
+ * directed to WB.)
+ *
+ * There is also no support for CLUT formats
+ *
+ * Requirements:
+ *
+ * 1) 0 <= crop.x <= crop.x + crop.w <= width
+ * 2) 0 <= crop.y <= crop.y + crop.h <= height
+ * 3) win.x <= win.x + win.w and win.w >= 0
+ * 4) win.y <= win.y + win.h and win.h >= 0
+ *
+ * 5) color_mode is supported by overlay
+ * 6) requested scaling is supported by overlay and functional clocks
+ *
+ * Notes:
+ *
+ * 1) Any portions of X:[pos_x, pos_x + out_width] and
+ * Y:[pos_y, pos_y + out_height] outside of the screen
+ * X:[0, screen.width], Y:[0, screen.height] will be cropped
+ * automatically without changing the scaling ratio.
+ *
+ * 2) Crop region will be adjusted to the pixel granularity:
+ * (2-by-1) for YUV422, (2-by-2) for YUV420. This will
+ * not modify the output region. Crop region is for the
+ * original (unrotated) buffer, so it does not change with
+ * rotation.
+ *
+ * 3) Rotation will not modify the output region, specifically
+ * its height and width. Also the coordinate system of the
+ * display is always (0,0) = top left.
+ *
+ * 4) cconv and vc1 only needs to be filled for YUV color modes.
+ *
+ * 5) vc1.range_y and vc1.range_uv only needs to be filled if
+ * vc1.enable is true.
+ */
+struct dss2_ovl_cfg {
+ __u16 width; /* buffer width */
+ __u16 height; /* buffer height */
+ __u32 stride; /* buffer stride */
+
+ enum omap_color_mode color_mode;
+ __u8 pre_mult_alpha; /* bool */
+ __u8 global_alpha; /* 0..255 */
+ __u8 rotation; /* 0..3 (*90 degrees clockwise) */
+ __u8 mirror; /* left-to-right: mirroring is applied after rotation */
+
+ enum omap_dss_ilace_mode ilace; /* interlace mode */
+
+ struct dss2_rect_t win; /* output window - on display */
+ struct dss2_rect_t crop; /* crop window - in source buffer */
+
+ struct dss2_decim decim; /* predecimation limits */
+
+ struct omap_dss_cconv_coefs cconv;
+ struct dss2_vc1_range_map_info vc1;
+
+ __u8 ix; /* ovl index same as sysfs/overlay# */
+ __u8 zorder; /* 0..3 */
+ __u8 enabled; /* bool */
+ __u8 zonly; /* only set zorder and enabled bit */
+ __u8 mgr_ix; /* mgr index */
+} __aligned(4);
+
+enum omapdss_buffer_type {
+ OMAP_DSS_BUFTYPE_SDMA,
+ OMAP_DSS_BUFTYPE_TILER_8BIT,
+ OMAP_DSS_BUFTYPE_TILER_16BIT,
+ OMAP_DSS_BUFTYPE_TILER_32BIT,
+ OMAP_DSS_BUFTYPE_TILER_PAGE,
+};
+
+enum omapdss_buffer_addressing_type {
+ OMAP_DSS_BUFADDR_DIRECT, /* using direct addresses */
+ OMAP_DSS_BUFADDR_BYTYPE, /* using buffer types */
+ OMAP_DSS_BUFADDR_ION, /* using ion handle(s) */
+ OMAP_DSS_BUFADDR_GRALLOC, /* using gralloc handle */
+ OMAP_DSS_BUFADDR_OVL_IX, /* using a prior overlay */
+ OMAP_DSS_BUFADDR_LAYER_IX, /* using a Post2 layer */
+ OMAP_DSS_BUFADDR_FB, /* using framebuffer memory */
+};
+
+struct dss2_ovl_info {
+ struct dss2_ovl_cfg cfg;
+
+ enum omapdss_buffer_addressing_type addressing;
+
+ union {
+ /* user-space interfaces */
+ struct {
+ void *address; /* main buffer address */
+ void *uv_address; /* uv buffer */
+ };
+
+ /*
+ * For DSSCIOC_CHECK_OVL we allow specifying just the
+ * type of each buffer. This is used if we need to
+ * check whether DSS will be able to display a buffer
+ * if using a particular memory type before spending
+ * time to map/copy the buffer into that type of
+ * memory.
+ */
+ struct {
+ enum omapdss_buffer_type ba_type;
+ enum omapdss_buffer_type uv_type;
+ };
+
+ /* kernel-space interfaces */
+
+ /*
+ * for fbmem, highest 4-bits of address is fb index,
+ * rest of the bits are the offset
+ */
+ struct {
+ __u32 ba; /* base address or index */
+ __u32 uv; /* uv address */
+ };
+ };
+};
+
+/*
+ * DSS2 manager information.
+ *
+ * The following information is deemed to be set globally, so it is not
+ * included:
+ * gamma correction
+ * whether to enable zorder (always enabled)
+ * whether to replicate/truncate color fields (it is decided per the
+ * whole manager/overlay settings, and is enabled unless overlay is
+ * directed to WB.)
+ * Notes:
+ *
+ * 1) trans_key_type and trans_enabled only need to be filled if
+ * trans_enabled is true, and alpha_blending is false.
+ */
+struct dss2_mgr_info {
+ __u32 ix; /* display index same as sysfs/display# */
+
+ __u32 default_color;
+
+ enum omap_dss_trans_key_type trans_key_type;
+ __u32 trans_key;
+ struct omap_dss_cpr_coefs cpr_coefs;
+
+ __u8 trans_enabled; /* bool */
+
+ __u8 interlaced; /* bool */
+ __u8 alpha_blending; /* bool - overrides trans_enabled */
+ __u8 cpr_enabled; /* bool */
+ __u8 swap_rb; /* bool - swap red and blue */
+} __aligned(4);
+
+/*
+ * ioctl: DSSCIOC_SETUP_MGR, struct dsscomp_setup_mgr_data
+ *
+ * 1. sets manager of each ovl in composition to the display
+ * 2. calls set_dss_ovl_info() for each ovl to set up the
+ * overlay staging structures (this is a wrapper around ovl->set_info())
+ * 3. calls set_dss_mgr_info() for mgr to set up the manager
+ * staging structures (this is a wrapper around mgr->set_info())
+ * 4. if update is true:
+ * calls manager->apply()
+ * calls driver->update() in a non-blocking fashion
+ * this will program the DSS synchronously
+ *
+ * Notes:
+ *
+ * 1) x, y, w, h only needs to be set if update is true.
+ *
+ * All non-specified pipelines that currently are on the same display
+ * will remain the same as on the previous frame. You may want to
+ * disable unused pipelines to avoid surprises.
+ *
+ * If get_sync_obj is false, it returns 0 on success, <0 error value
+ * on failure.
+ *
+ * If get_sync_obj is true, it returns fd on success, or a negative value
+ * on failure. You can use the fd to wait on (using DSSCIOC_WAIT ioctl()).
+ *
+ * Note: frames do not get eclipsed when the display turns off. Queue a
+ * blank frame to eclipse old frames. Blank frames get eclipsed when
+ * programmed into DSS.
+ *
+ * (A blank frame is queued to the display automatically in Android before
+ * the display is turned off.)
+ *
+ * All overlays to be used on the frame must be listed. There is no way
+ * to add another overlay to a defined frame.
+ */
+enum dsscomp_setup_mode {
+ DSSCOMP_SETUP_MODE_APPLY = (1 << 0), /* applies changes to cache */
+ DSSCOMP_SETUP_MODE_DISPLAY = (1 << 1), /* calls display update */
+ DSSCOMP_SETUP_MODE_CAPTURE = (1 << 2), /* capture to WB */
+
+ /* just apply changes for next vsync/update */
+ DSSCOMP_SETUP_APPLY = DSSCOMP_SETUP_MODE_APPLY,
+ /* trigger an update (wait for vsync) */
+ DSSCOMP_SETUP_DISPLAY =
+ DSSCOMP_SETUP_MODE_APPLY | DSSCOMP_SETUP_MODE_DISPLAY,
+ /* capture to WB - WB must be configured */
+ DSSCOMP_SETUP_CAPTURE =
+ DSSCOMP_SETUP_MODE_APPLY | DSSCOMP_SETUP_MODE_CAPTURE,
+ /* display and capture to WB - WB must be configured */
+ DSSCOMP_SETUP_DISPLAY_CAPTURE =
+ DSSCOMP_SETUP_DISPLAY | DSSCOMP_SETUP_CAPTURE,
+};
+
+struct dsscomp_setup_mgr_data {
+ __u32 sync_id; /* synchronization ID - for debugging */
+
+ struct dss2_rect_t win; /* update region, set w/h to 0 for fullscreen */
+ enum dsscomp_setup_mode mode;
+ __u16 num_ovls; /* # of overlays used in the composition */
+ __u16 get_sync_obj; /* ioctl should return a sync object */
+
+ struct dss2_mgr_info mgr;
+ struct dss2_ovl_info ovls[0]; /* up to 5 overlays to set up */
+};
+
+/*
+ * ioctl: DSSCIOC_CHECK_OVL, struct dsscomp_check_ovl_data
+ *
+ * DISPLAY and/or CAPTURE bits must be filled for the mode field
+ * correctly to be able to decide correctly if DSS can properly
+ * render the overlay.
+ *
+ * ovl.ix is ignored.
+ *
+ * Returns a positive bitmask regarding which overlay of DSS can
+ * render the overlay as it is configured for the display/display's
+ * manager. NOTE: that overlays that are assigned to other displays
+ * may be returned. If there is an invalid configuration (negative
+ * sizes, etc.), a negative error value is returned.
+ *
+ * ovl->decim's min values will be modified to the smallest decimation that
+ * DSS can use to support the overlay configuration.
+ *
+ * Assumptions:
+ * - zorder will be distinct from other pipelines on that manager
+ * - overlay will be enabled and routed to the display specified
+ */
+struct dsscomp_check_ovl_data {
+ enum dsscomp_setup_mode mode;
+ struct dss2_mgr_info mgr;
+ struct dss2_ovl_info ovl;
+};
+
+/*
+ * This structure is used to set up the entire DISPC (all managers),
+ * and is analogous to dsscomp_setup_mgr_data.
+ *
+ * Additional features:
+ * - all overlays that were specified in a prior use of this
+ * structure, and are no longer specified, will be disabled.
+ * - 1D buffers under 4M will be mapped into TILER1D.
+ *
+ * Limitations:
+ * - only DISPLAY mode is supported (DISPLAY and APPLY bits will
+ * automatically be set)
+ * - getting a sync object is not supported.
+ */
+struct dsscomp_setup_dispc_data {
+ __u32 sync_id; /* synchronization ID - for debugging */
+
+ enum dsscomp_setup_mode mode;
+ __u16 num_ovls; /* # of overlays used in the composition */
+ __u16 num_mgrs; /* # of managers used in the composition */
+ __u16 get_sync_obj; /* ioctl should return a sync object */
+
+ struct dss2_mgr_info mgrs[3];
+ struct dss2_ovl_info ovls[5]; /* up to 5 overlays to set up */
+};
+
+/*
+ * ioctl: DSSCIOC_WB_COPY, struct dsscomp_wb_copy_data
+ *
+ * Requirements:
+ * wb.ix must be OMAP_DSS_WB.
+ *
+ * Returns 0 on success (copy is completed), non-0 on failure.
+ */
+struct dsscomp_wb_copy_data {
+ struct dss2_ovl_info ovl, wb;
+};
+
+/*
+ * ioctl: DSSCIOC_QUERY_DISPLAY, struct dsscomp_display_info
+ *
+ * Gets informations about the display. Fill in ix and modedb_len before
+ * calling ioctl, and rest of the fields are filled in by ioctl. Up to
+ * modedb_len timings are retrieved in the order of display preference.
+ *
+ * Returns: 0 on success, non-0 error value on failure.
+ */
+struct dsscomp_display_info {
+ __u32 ix; /* display index (sysfs/display#) */
+ __u32 overlays_available; /* bitmask of available overlays */
+ __u32 overlays_owned; /* bitmask of owned overlays */
+ enum omap_channel channel;
+ enum omap_dss_display_state state;
+ __u8 enabled; /* bool: resume-state if suspended */
+ struct omap_video_timings timings;
+ struct s3d_disp_info s3d_info; /* any S3D specific information */
+ struct dss2_mgr_info mgr; /* manager information */
+ __u16 width_in_mm; /* screen dimensions */
+ __u16 height_in_mm;
+
+ __u32 modedb_len; /* number of video timings */
+ struct dsscomp_videomode modedb[]; /* display supported timings */
+};
+
+/*
+ * ioctl: DSSCIOC_SETUP_DISPLAY, struct dsscomp_setup_display_data
+ *
+ * Gets informations about the display. Fill in ix before calling
+ * ioctl, and rest of the fields are filled in by ioctl.
+ *
+ * Returns: 0 on success, non-0 error value on failure.
+ */
+struct dsscomp_setup_display_data {
+ __u32 ix; /* display index (sysfs/display#) */
+ struct dsscomp_videomode mode; /* video timings */
+};
+
+/*
+ * ioctl: DSSCIOC_WAIT, struct dsscomp_wait_data
+ *
+ * Use this ioctl to wait for one of the following events:
+ *
+ * A) the moment a composition is programmed into DSS
+ * B) the moment a composition is first displayed (or captured)
+ * C) the moment when a composition is no longer queued or displayed on a
+ * display (it is released). (A composition is assumed to be superceded
+ * when another composition has been programmed into DSS, even if that
+ * subsequent composition does not update/specify all overlays used by
+ * the prior composition; moreover, even if it uses the same buffers.)
+ *
+ * Set timeout to desired timeout value in microseconds.
+ *
+ * This ioctl must be used on the sync object returned by the
+ * DSSCIOC_SETUP_MGR or DSSCIOC_SETUP_DISPC ioctls.
+ *
+ * Returns: >=0 on success, <0 error value on failure (e.g. -ETIME).
+ */
+enum dsscomp_wait_phase {
+ DSSCOMP_WAIT_PROGRAMMED = 1,
+ DSSCOMP_WAIT_DISPLAYED,
+ DSSCOMP_WAIT_RELEASED,
+};
+
+struct dsscomp_wait_data {
+ __u32 timeout_us; /* timeout in microseconds */
+ enum dsscomp_wait_phase phase; /* phase to wait for */
+};
+
+/* IOCTLS */
+#define DSSCIOC_SETUP_MGR _IOW('O', 128, struct dsscomp_setup_mgr_data)
+#define DSSCIOC_CHECK_OVL _IOWR('O', 129, struct dsscomp_check_ovl_data)
+#define DSSCIOC_WB_COPY _IOW('O', 130, struct dsscomp_wb_copy_data)
+#define DSSCIOC_QUERY_DISPLAY _IOWR('O', 131, struct dsscomp_display_info)
+#define DSSCIOC_WAIT _IOW('O', 132, struct dsscomp_wait_data)
+
+#define DSSCIOC_SETUP_DISPC _IOW('O', 133, struct dsscomp_setup_dispc_data)
+#define DSSCIOC_SETUP_DISPLAY \
+ _IOW('O', 134, struct dsscomp_setup_display_data)
+#endif