aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLajos Molnar <molnar@ti.com>2011-05-25 22:45:26 -0500
committerIliyan Malchev <malchev@google.com>2011-06-23 18:08:47 -0700
commita7e96985ea15176a46ed3d767c58baf3c36d510e (patch)
tree2e5a94050ef6605616c562b9f4767e514b8b7bb6
parent104985357a76abff70f761757f504c78c9096873 (diff)
downloadkernel_samsung_tuna-a7e96985ea15176a46ed3d767c58baf3c36d510e.zip
kernel_samsung_tuna-a7e96985ea15176a46ed3d767c58baf3c36d510e.tar.gz
kernel_samsung_tuna-a7e96985ea15176a46ed3d767c58baf3c36d510e.tar.bz2
(TEMP) OMAP: DSS: enable video decoding
Add global alpha settings for video 1 overlay in _dispc_setup_global_alpha. On OMAP4 VID1 has global alpha. Change-Id: Iaafaae6283c322a6962d24c0fefa253acd1505f0 Signed-off-by: Lajos Molnar <molnar@ti.com> OMAP4: DSS: Fix incorrect OMAP3-alpha compatibility setting Alpha blending is always enabled on OMAP4. The meaning of the alpha enabled bits in DISPC_CONFIG registers now enabled OMAP3 compatibility mode which disables ZORDER settings. This is not what we want when we set alpha blending. This patch sets alpha blending always to true on OMAP4. It also turns off OMAP3 compatibility mode for now, and reports the compatibility mode results when querying dispc_alpha_blending. Change-Id: Ie7af6835c0415681ae09bc220522e7c692c03e61 Signed-off-by: Lajos Molnar <molnar@ti.com> OMAP: DSS2: Add DISPC VIDEO3 pipeline support VID3 and Writeback pipeline registers have a linear relation, introduce these registers and access these registers in DISPC functions. Add a dss_feature for VIDEO3 pipeline. Configure color conversion and global alpha for this new pipeline. Also, create overlay object for the new video pipe. Signed-off-by: Archit Taneja <archit@ti.com> Signed-off-by: Lajos Molnar <molnar@ti.com> OMAP: DSS2: Add zorder support on OMAP4 Add zorder Support on OMAP4, this feature allows deciding the visibility order of the overlays based on the zorder value provided as a sysfs attribute of the overlay object. Since this feature is not supported on OMAP3 and OMAP2, the sysfs attibutes always return zorder as 0 and do not write to dispc registers for non OMAP4 architectures. Signed-off-by: Sumit Semwal <sumit.semwal@ti.com> Signed-off-by: Archit Taneja <archit@ti.com> Signed-off-by: Mark Tyler <mark.tyler@ti.com> Signed-off-by: Lajos Molnar <molnar@ti.com> OMAP4: DSS: Making the deault color format of framebuffer ARGB32 Earlier the default color format,frmamebuffer has,was xRGB24U but SGX is producing data in ARGB32. Also,SGX,the FB driver user,doesnt negotiates with FB driver for the format and thus FB needs to provide ARGB32 to align with SGX format. http://git.omapzoom.org/?p=kernel/omap.git;a=commit;h=c75a7e1b83d446650b0f393746f8a810242a8739 Signed-off-by: Mark Tyler <mark.tyler@ti.com> OMAP4:DSS: Added TILER support for DSS Added support for displaying buffers located in the TILER 2D containers. Buffers in TILER 1D containers can be displayed using OMAP_DSS_ROT_DMA. Change-Id: I86b231ed5775e410acf31bb49deb817c0989c16b Signed-off-by: Lajos Molnar <molnar@ti.com> OMAP: DSS2: Add callback for tracking overlay/manager changes This patch allows tracking when a particular overlay or manager change has taken place, and when it is eclipsed (no longer used). In DSS2 overlay/manager information travels through 4 stages: 1: (software) overlay/manager info 2: (software) cache (on manager->apply()) 3: (hardware) shadow registers (on configure()) 4: (hardware) DISPC (on vsync or go/enable) Callback information for each settings can be passed as part of the info structure (info->cb), which is the following struct: struct omapdss_ovl_cb { void (*fn)(void *data, int id, int status); void *data; }; id contains to the overlay/manager index. If fn is NULL, no callback will take place. Otherwise, callbacks will be generated on programming (when settings get into the DISPC), when the settings are displayed, and on eclipse (when settings are overwritten by newer settings). Depending on the stage where the eclipse happens, a different callback is generated. Status is one of the DSS_COMPLETION_... enum values specifying the event. DSS_COMPLETION_ECLIPSED_SET - info was overwritten at stage 1 DSS_COMPLETION_ECLIPSED_CACHE - info was overwritten at stage 2 DSS_COMPLETION_ECLIPSED_SHADOW - info was overwritten at stage 3 DSS_COMPLETION_RELEASED - info was overwritten at stage 4 (after successfully being displayed) DSS_COMPLETION_TORN - info was overwritten at stage 4 (before it was successfully displayed) DSS_COMPLETION_PROGRAMMED - info moved from stage 3 to stage 4 (this follows the prior info's callback of DSS_COMPLETION_RELEASED/TORN) DSS_COMPLETION_DISPLAYED - info in stage 4 has been successfully displayed. This callback is received on every frame refresh. If only the first display is required, it needs to be filtered out in the callback. You can use the DSS_COMPLETION_RELEASED flag to see if an info has been eclipsed (so you don't have to check for all 5 values). There is a fundamental issue with tracking DSS settings in the current DSS2 framework. Pipeline/manager settings are programmed first into an info structure that stays around. These settins can be modified piece-by-piece - as they are done using the sysfs framework. Theoretically, these cause the old settings to be eclipsed by the new settings. However, sysfs interface is used to augment the other DSS2 users that would register for these callbacks. Therefore, we need to treat these partial updates specially. For now - if the callback function and data are the same - a different status is used: DSS_COMPLETION_CHANGED_SET. Now these auxiliary interfaces also apply the changes automatically, so a similar method needs to be used at level 2. (DSS_COMPLETION_CHANGED_CACHE) This, however, causes callback info to stay around at the level 1 interface making it not useful for tracking the status. We cannot guarantee that the information is tracked from setting to release/eclipse if future settings may end up reusing (ignorantly) the same callback info. For now the callback info is cleared at level 1 when transferred to level 2. If other - callback unaware - DSS2 users modify overlay/manager settings, (e.g. using sysfs controls in any way), they will not reset the callback. We err on the side of sysfs, and we will not treat a settings application (transferring level 1 info to level 2) as eclipsed if the level-1 callback method is empty. This works for sysfs changes, but we will be a missed callback if V4L2 or FB is changing the base address, and we are using the callbacks to track buffer usage. The other issue is using sysfs to enable/disable an overlay. We may get a RELEASED event on disable, but no usage callback is done on subsequent enable because sysfs will not request a callback. Change-Id: I0b7fdbeefe71e40dc86ec8401dbc7e82374eaeb5 Signed-off-by: Lajos Molnar <molnar@ti.com> Signed-off-by: Mark Tyler <mark.tyler@ti.com> OMAP:DSS:DSSCOMP: New composition module 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 users 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). Each composition is referred to by a unique sync-id. 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. 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.) 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 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 - not fully operational - still debugging some unit test issues Change-Id: I5826a409f4fd258c8ce86e782c5e6bdd4bf9bcf4 Signed-off-by: Lajos Molnar <molnar@ti.com> OMAP:DSS:DSSCOMP: Add DSS composition module to build/config system This patch links in the DSS composition (dsscomp) module into the build and configuration system. DSS composition is enabled by default. Change-Id: I5b480dc10b17c5e37f5611baf96a20217cb6ec3e Signed-off-by: Lajos Molnar <molnar@ti.com> Signed-off-by: Mark Tyler <mark.tyler@ti.com> OMAP:OMAPLFB: *HACK* Adapted to DSSCOMP OMAPLFB must call DSSCOMP's apply so that the related overlays are also programmed into DSS. Change-Id: Ia8c1c181028fb7f241677e9657a1d402c8724ee8 Signed-off-by: Mark Tyler <mark.tyler@ti.com> Signed-off-by: Lajos Molnar <molnar@ti.com> OMAP4:DSS: Jumbo patch to enable predecimation Includes the following fixes: - fixed color format modes for non-GFX pipelines - fixed NV12 support in 1D mode buffer - added all color formats to configure_overlay() Predecimation - added predecimation support for NV12/YUV/rotated/SDMA buffers - added scaling decision (predecimation) - added conservative fclk calculation - added sysfs controls - tied into DSSCOMP Signed-off-by: Lajos Molnar <molnar@ti.com> OMAP4:DSS: Fix maxdownscale limits for OMAP4 in DISPC setup plane. Signed-off-by: Mark Tyler <mark.tyler@ti.com>
-rw-r--r--arch/arm/plat-omap/include/plat/dsscomp.h23
-rw-r--r--drivers/gpu/pvr/omaplfb/omaplfb_linux.c20
-rw-r--r--drivers/video/omap2/Kconfig1
-rw-r--r--drivers/video/omap2/Makefile1
-rw-r--r--drivers/video/omap2/dss/dispc.c932
-rw-r--r--drivers/video/omap2/dss/dispc.h57
-rw-r--r--drivers/video/omap2/dss/dss.h19
-rw-r--r--drivers/video/omap2/dss/dss_features.c24
-rw-r--r--drivers/video/omap2/dss/dss_features.h5
-rw-r--r--drivers/video/omap2/dss/manager.c259
-rw-r--r--drivers/video/omap2/dss/overlay.c157
-rw-r--r--drivers/video/omap2/dsscomp/Kconfig9
-rw-r--r--drivers/video/omap2/dsscomp/Makefile2
-rw-r--r--drivers/video/omap2/dsscomp/base.c442
-rw-r--r--drivers/video/omap2/dsscomp/device.c369
-rw-r--r--drivers/video/omap2/dsscomp/dsscomp.h83
-rw-r--r--drivers/video/omap2/dsscomp/queue.c887
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c2
-rw-r--r--include/video/dsscomp.h557
-rw-r--r--include/video/omapdss.h37
20 files changed, 3461 insertions, 425 deletions
diff --git a/arch/arm/plat-omap/include/plat/dsscomp.h b/arch/arm/plat-omap/include/plat/dsscomp.h
new file mode 100644
index 0000000..acafe0f
--- /dev/null
+++ b/arch/arm/plat-omap/include/plat/dsscomp.h
@@ -0,0 +1,23 @@
+#ifndef _ARCH_ARM_PLAT_OMAP_DSSCOMP_H
+#define _ARCH_ARM_PLAT_OMAP_DSSCOMP_H
+
+#include <video/omapdss.h>
+
+/* queuing operations */
+typedef struct dsscomp_data *dsscomp_t; /* handle */
+
+dsscomp_t dsscomp_new_sync_id(struct omap_overlay_manager *mgr, u32 sync_id);
+u32 dsscomp_first_sync_id(struct omap_overlay_manager *mgr);
+dsscomp_t dsscomp_find(struct omap_overlay_manager *mgr, u32 sync_id);
+u32 dsscomp_get_ovls(dsscomp_t comp);
+int dsscomp_set_ovl(dsscomp_t comp, struct dss2_ovl_info *ovl);
+int dsscomp_get_ovl(dsscomp_t comp, u32 ix, struct dss2_ovl_info *ovl);
+int dsscomp_set_mgr(dsscomp_t comp, struct dss2_mgr_info *mgr);
+int dsscomp_get_mgr(dsscomp_t comp, struct dss2_mgr_info *mgr);
+int dsscomp_setup(dsscomp_t comp, enum dsscomp_setup_mode mode,
+ struct dss2_rect_t win);
+int dsscomp_apply(dsscomp_t comp);
+int dsscomp_wait(dsscomp_t comp, enum dsscomp_wait_phase phase, int timeout);
+void dsscomp_drop(dsscomp_t c);
+
+#endif
diff --git a/drivers/gpu/pvr/omaplfb/omaplfb_linux.c b/drivers/gpu/pvr/omaplfb/omaplfb_linux.c
index 8aae8f5..07576c8 100644
--- a/drivers/gpu/pvr/omaplfb/omaplfb_linux.c
+++ b/drivers/gpu/pvr/omaplfb/omaplfb_linux.c
@@ -277,6 +277,26 @@ void OMAPLFBFlip(OMAPLFB_DEVINFO *psDevInfo, OMAPLFB_BUFFER *psBuffer)
}
}
#endif
+
+ {
+ #include <video/dsscomp.h>
+ #include <plat/dsscomp.h>
+
+ struct omapfb_info *ofbi = FB2OFB(psDevInfo->psLINFBInfo);
+ struct omap_overlay_manager *manager;
+ struct omap_overlay *overlay;
+
+ dsscomp_t comp;
+ u32 sync_id;
+
+ overlay = ofbi->overlays[OMAP_DSS_GFX];
+ manager = overlay->manager;
+
+ sync_id = dsscomp_first_sync_id(manager);
+ comp = dsscomp_find(manager, sync_id);
+ dsscomp_apply(comp);
+ }
+
console_unlock();
}
diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig
index d877c36..97a5bc4 100644
--- a/drivers/video/omap2/Kconfig
+++ b/drivers/video/omap2/Kconfig
@@ -7,3 +7,4 @@ config OMAP2_VRFB
source "drivers/video/omap2/dss/Kconfig"
source "drivers/video/omap2/omapfb/Kconfig"
source "drivers/video/omap2/displays/Kconfig"
+source "drivers/video/omap2/dsscomp/Kconfig"
diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile
index 5ddef12..2d5a205 100644
--- a/drivers/video/omap2/Makefile
+++ b/drivers/video/omap2/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
obj-$(CONFIG_OMAP2_DSS) += dss/
obj-$(CONFIG_FB_OMAP2) += omapfb/
obj-y += displays/
+obj-y += dsscomp/
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index 62aa77c..988d440 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -38,6 +38,7 @@
#include <plat/sram.h>
#include <plat/clock.h>
+#include <mach/tiler.h>
#include <video/omapdss.h>
@@ -102,7 +103,7 @@ static struct {
int irq;
struct clk *dss_clk;
- u32 fifo_size[3];
+ u32 fifo_size[MAX_DSS_OVERLAYS];
u32 channel_irq[3]; /* Max channels hardcoded to 3*/
@@ -169,7 +170,7 @@ static int dispc_get_ctx_loss_count(void)
static void dispc_save_context(void)
{
- int i;
+ int i, o;
DSSDBG("dispc_save_context\n");
@@ -236,105 +237,60 @@ static void dispc_save_context(void)
if (dss_has_feature(FEAT_PRELOAD))
SR(OVL_PRELOAD(OMAP_DSS_GFX));
- /* VID1 */
- SR(OVL_BA0(OMAP_DSS_VIDEO1));
- SR(OVL_BA1(OMAP_DSS_VIDEO1));
- SR(OVL_POSITION(OMAP_DSS_VIDEO1));
- SR(OVL_SIZE(OMAP_DSS_VIDEO1));
- SR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO1));
- SR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1));
- SR(OVL_ROW_INC(OMAP_DSS_VIDEO1));
- SR(OVL_PIXEL_INC(OMAP_DSS_VIDEO1));
- SR(OVL_FIR(OMAP_DSS_VIDEO1));
- SR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1));
- SR(OVL_ACCU0(OMAP_DSS_VIDEO1));
- SR(OVL_ACCU1(OMAP_DSS_VIDEO1));
-
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, i));
-
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, i));
-
- for (i = 0; i < 5; i++)
- SR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i));
-
- if (dss_has_feature(FEAT_FIR_COEF_V)) {
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i));
- }
+ /* VID1-3 */
+ for (o = OMAP_DSS_VIDEO1; o <= OMAP_DSS_VIDEO3; o++) {
+ if (o == OMAP_DSS_VIDEO3 && !dss_has_feature(FEAT_OVL_VID3))
+ continue;
- if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
- SR(OVL_BA0_UV(OMAP_DSS_VIDEO1));
- SR(OVL_BA1_UV(OMAP_DSS_VIDEO1));
- SR(OVL_FIR2(OMAP_DSS_VIDEO1));
- SR(OVL_ACCU2_0(OMAP_DSS_VIDEO1));
- SR(OVL_ACCU2_1(OMAP_DSS_VIDEO1));
+ SR(OVL_BA0(o));
+ SR(OVL_BA1(o));
+ SR(OVL_POSITION(o));
+ SR(OVL_SIZE(o));
+ SR(OVL_ATTRIBUTES(o));
+ SR(OVL_FIFO_THRESHOLD(o));
+ SR(OVL_ROW_INC(o));
+ SR(OVL_PIXEL_INC(o));
+ SR(OVL_FIR(o));
+ SR(OVL_PICTURE_SIZE(o));
+ SR(OVL_ACCU0(o));
+ SR(OVL_ACCU1(o));
for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, i));
+ SR(OVL_FIR_COEF_H(o, i));
for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, i));
+ SR(OVL_FIR_COEF_HV(o, i));
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, i));
- }
- if (dss_has_feature(FEAT_ATTR2))
- SR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1));
+ for (i = 0; i < 5; i++)
+ SR(OVL_CONV_COEF(o, i));
- if (dss_has_feature(FEAT_PRELOAD))
- SR(OVL_PRELOAD(OMAP_DSS_VIDEO1));
-
- /* VID2 */
- SR(OVL_BA0(OMAP_DSS_VIDEO2));
- SR(OVL_BA1(OMAP_DSS_VIDEO2));
- SR(OVL_POSITION(OMAP_DSS_VIDEO2));
- SR(OVL_SIZE(OMAP_DSS_VIDEO2));
- SR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO2));
- SR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2));
- SR(OVL_ROW_INC(OMAP_DSS_VIDEO2));
- SR(OVL_PIXEL_INC(OMAP_DSS_VIDEO2));
- SR(OVL_FIR(OMAP_DSS_VIDEO2));
- SR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2));
- SR(OVL_ACCU0(OMAP_DSS_VIDEO2));
- SR(OVL_ACCU1(OMAP_DSS_VIDEO2));
-
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, i));
-
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, i));
-
- for (i = 0; i < 5; i++)
- SR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i));
-
- if (dss_has_feature(FEAT_FIR_COEF_V)) {
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i));
- }
+ if (dss_has_feature(FEAT_FIR_COEF_V)) {
+ for (i = 0; i < 8; i++)
+ SR(OVL_FIR_COEF_V(o, i));
+ }
- if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
- SR(OVL_BA0_UV(OMAP_DSS_VIDEO2));
- SR(OVL_BA1_UV(OMAP_DSS_VIDEO2));
- SR(OVL_FIR2(OMAP_DSS_VIDEO2));
- SR(OVL_ACCU2_0(OMAP_DSS_VIDEO2));
- SR(OVL_ACCU2_1(OMAP_DSS_VIDEO2));
+ if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
+ SR(OVL_BA0_UV(o));
+ SR(OVL_BA1_UV(o));
+ SR(OVL_FIR2(o));
+ SR(OVL_ACCU2_0(o));
+ SR(OVL_ACCU2_1(o));
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, i));
+ for (i = 0; i < 8; i++)
+ SR(OVL_FIR_COEF_H2(o, i));
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, i));
+ for (i = 0; i < 8; i++)
+ SR(OVL_FIR_COEF_HV2(o, i));
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, i));
- }
- if (dss_has_feature(FEAT_ATTR2))
- SR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2));
+ for (i = 0; i < 8; i++)
+ SR(OVL_FIR_COEF_V2(o, i));
+ }
+ if (dss_has_feature(FEAT_ATTR2))
+ SR(OVL_ATTRIBUTES2(o));
- if (dss_has_feature(FEAT_PRELOAD))
- SR(OVL_PRELOAD(OMAP_DSS_VIDEO2));
+ if (dss_has_feature(FEAT_PRELOAD))
+ SR(OVL_PRELOAD(o));
+ }
if (dss_has_feature(FEAT_CORE_CLK_DIV))
SR(DIVISOR);
@@ -347,7 +303,7 @@ static void dispc_save_context(void)
static void dispc_restore_context(void)
{
- int i, ctx;
+ int i, o, ctx;
DSSDBG("dispc_restore_context\n");
@@ -425,105 +381,61 @@ static void dispc_restore_context(void)
if (dss_has_feature(FEAT_PRELOAD))
RR(OVL_PRELOAD(OMAP_DSS_GFX));
- /* VID1 */
- RR(OVL_BA0(OMAP_DSS_VIDEO1));
- RR(OVL_BA1(OMAP_DSS_VIDEO1));
- RR(OVL_POSITION(OMAP_DSS_VIDEO1));
- RR(OVL_SIZE(OMAP_DSS_VIDEO1));
- RR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO1));
- RR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1));
- RR(OVL_ROW_INC(OMAP_DSS_VIDEO1));
- RR(OVL_PIXEL_INC(OMAP_DSS_VIDEO1));
- RR(OVL_FIR(OMAP_DSS_VIDEO1));
- RR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1));
- RR(OVL_ACCU0(OMAP_DSS_VIDEO1));
- RR(OVL_ACCU1(OMAP_DSS_VIDEO1));
-
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, i));
-
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, i));
-
- for (i = 0; i < 5; i++)
- RR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i));
-
- if (dss_has_feature(FEAT_FIR_COEF_V)) {
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i));
- }
- if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
- RR(OVL_BA0_UV(OMAP_DSS_VIDEO1));
- RR(OVL_BA1_UV(OMAP_DSS_VIDEO1));
- RR(OVL_FIR2(OMAP_DSS_VIDEO1));
- RR(OVL_ACCU2_0(OMAP_DSS_VIDEO1));
- RR(OVL_ACCU2_1(OMAP_DSS_VIDEO1));
+ /* VID1-3 */
+ for (o = OMAP_DSS_VIDEO1; o <= OMAP_DSS_VIDEO3; o++) {
+ if (o == OMAP_DSS_VIDEO3 && !dss_has_feature(FEAT_OVL_VID3))
+ continue;
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, i));
+ RR(OVL_BA0(o));
+ RR(OVL_BA1(o));
+ RR(OVL_POSITION(o));
+ RR(OVL_SIZE(o));
+ RR(OVL_ATTRIBUTES(o));
+ RR(OVL_FIFO_THRESHOLD(o));
+ RR(OVL_ROW_INC(o));
+ RR(OVL_PIXEL_INC(o));
+ RR(OVL_FIR(o));
+ RR(OVL_PICTURE_SIZE(o));
+ RR(OVL_ACCU0(o));
+ RR(OVL_ACCU1(o));
for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, i));
+ RR(OVL_FIR_COEF_H(o, i));
for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, i));
- }
- if (dss_has_feature(FEAT_ATTR2))
- RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1));
+ RR(OVL_FIR_COEF_HV(o, i));
- if (dss_has_feature(FEAT_PRELOAD))
- RR(OVL_PRELOAD(OMAP_DSS_VIDEO1));
-
- /* VID2 */
- RR(OVL_BA0(OMAP_DSS_VIDEO2));
- RR(OVL_BA1(OMAP_DSS_VIDEO2));
- RR(OVL_POSITION(OMAP_DSS_VIDEO2));
- RR(OVL_SIZE(OMAP_DSS_VIDEO2));
- RR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO2));
- RR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2));
- RR(OVL_ROW_INC(OMAP_DSS_VIDEO2));
- RR(OVL_PIXEL_INC(OMAP_DSS_VIDEO2));
- RR(OVL_FIR(OMAP_DSS_VIDEO2));
- RR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2));
- RR(OVL_ACCU0(OMAP_DSS_VIDEO2));
- RR(OVL_ACCU1(OMAP_DSS_VIDEO2));
-
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, i));
-
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, i));
-
- for (i = 0; i < 5; i++)
- RR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i));
-
- if (dss_has_feature(FEAT_FIR_COEF_V)) {
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i));
- }
+ for (i = 0; i < 5; i++)
+ RR(OVL_CONV_COEF(o, i));
- if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
- RR(OVL_BA0_UV(OMAP_DSS_VIDEO2));
- RR(OVL_BA1_UV(OMAP_DSS_VIDEO2));
- RR(OVL_FIR2(OMAP_DSS_VIDEO2));
- RR(OVL_ACCU2_0(OMAP_DSS_VIDEO2));
- RR(OVL_ACCU2_1(OMAP_DSS_VIDEO2));
+ if (dss_has_feature(FEAT_FIR_COEF_V)) {
+ for (i = 0; i < 8; i++)
+ RR(OVL_FIR_COEF_V(o, i));
+ }
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, i));
+ if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
+ RR(OVL_BA0_UV(o));
+ RR(OVL_BA1_UV(o));
+ RR(OVL_FIR2(o));
+ RR(OVL_ACCU2_0(o));
+ RR(OVL_ACCU2_1(o));
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, i));
+ for (i = 0; i < 8; i++)
+ RR(OVL_FIR_COEF_H2(o, i));
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, i));
- }
- if (dss_has_feature(FEAT_ATTR2))
- RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2));
+ for (i = 0; i < 8; i++)
+ RR(OVL_FIR_COEF_HV2(o, i));
- if (dss_has_feature(FEAT_PRELOAD))
- RR(OVL_PRELOAD(OMAP_DSS_VIDEO2));
+ for (i = 0; i < 8; i++)
+ RR(OVL_FIR_COEF_V2(o, i));
+ }
+ if (dss_has_feature(FEAT_ATTR2))
+ RR(OVL_ATTRIBUTES2(o));
+
+ if (dss_has_feature(FEAT_PRELOAD))
+ RR(OVL_PRELOAD(o));
+ }
if (dss_has_feature(FEAT_CORE_CLK_DIV))
RR(DIVISOR);
@@ -864,12 +776,25 @@ static void _dispc_setup_color_conv_coef(void)
dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 4),
CVAL(0, ct->bcb));
+ dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO3, 0),
+ CVAL(ct->rcr, ct->ry));
+ dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO3, 1),
+ CVAL(ct->gy, ct->rcb));
+ dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO3, 2),
+ CVAL(ct->gcb, ct->gcr));
+ dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO3, 3),
+ CVAL(ct->bcr, ct->by));
+ dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO3, 4),
+ CVAL(0, ct->bcb));
+
#undef CVAL
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO1),
ct->full_range, 11, 11);
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO2),
ct->full_range, 11, 11);
+ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO3),
+ ct->full_range, 11, 11);
}
@@ -944,8 +869,12 @@ static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha)
if (plane == OMAP_DSS_GFX)
REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 7, 0);
+ else if (plane == OMAP_DSS_VIDEO1)
+ REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 15, 8);
else if (plane == OMAP_DSS_VIDEO2)
REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 23, 16);
+ else if (plane == OMAP_DSS_VIDEO3)
+ REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 31, 24);
}
static void _dispc_set_pix_inc(enum omap_plane plane, s32 inc)
@@ -966,11 +895,11 @@ static void _dispc_set_color_mode(enum omap_plane plane,
switch (color_mode) {
case OMAP_DSS_COLOR_NV12:
m = 0x0; break;
- case OMAP_DSS_COLOR_RGB12U:
+ case OMAP_DSS_COLOR_RGBX16:
m = 0x1; break;
case OMAP_DSS_COLOR_RGBA16:
m = 0x2; break;
- case OMAP_DSS_COLOR_RGBX16:
+ case OMAP_DSS_COLOR_RGB12U:
m = 0x4; break;
case OMAP_DSS_COLOR_ARGB16:
m = 0x5; break;
@@ -1020,8 +949,10 @@ static void _dispc_set_color_mode(enum omap_plane plane,
case OMAP_DSS_COLOR_RGB24P:
m = 0x9; break;
case OMAP_DSS_COLOR_YUV2:
+ case OMAP_DSS_COLOR_RGBX16:
m = 0xa; break;
case OMAP_DSS_COLOR_UYVY:
+ case OMAP_DSS_COLOR_RGBA16:
m = 0xb; break;
case OMAP_DSS_COLOR_ARGB32:
m = 0xc; break;
@@ -1052,6 +983,7 @@ void dispc_set_channel_out(enum omap_plane plane,
break;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
+ case OMAP_DSS_VIDEO3:
shift = 16;
break;
default:
@@ -1098,6 +1030,7 @@ void dispc_set_burst_size(enum omap_plane plane,
break;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
+ case OMAP_DSS_VIDEO3:
shift = 14;
break;
default:
@@ -1124,6 +1057,29 @@ void dispc_enable_gamma_table(bool enable)
REG_FLD_MOD(DISPC_CONFIG, enable, 9, 9);
}
+void dispc_set_zorder(enum omap_plane plane,
+ enum omap_overlay_zorder zorder)
+{
+ u32 val;
+
+ if (!dss_has_feature(FEAT_OVL_ZORDER))
+ return;
+ val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
+ val = FLD_MOD(val, zorder, 27, 26);
+ dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);
+}
+
+void dispc_enable_zorder(enum omap_plane plane, bool enable)
+{
+ u32 val;
+
+ if (!dss_has_feature(FEAT_OVL_ZORDER))
+ return;
+ val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
+ val = FLD_MOD(val, enable, 25, 25);
+ dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);
+}
+
static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable)
{
u32 val;
@@ -1438,7 +1394,8 @@ static void _dispc_set_scaling(enum omap_plane plane,
}
static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation,
- bool mirroring, enum omap_color_mode color_mode)
+ bool mirroring, enum omap_color_mode color_mode,
+ enum omap_dss_rotation_type type)
{
bool row_repeat = false;
int vidrot = 0;
@@ -1488,6 +1445,16 @@ static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation,
if (dss_has_feature(FEAT_ROWREPEATENABLE))
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane),
row_repeat ? 1 : 0, 18, 18);
+
+ if (color_mode == OMAP_DSS_COLOR_NV12) {
+ /* this will never happen for GFX */
+ /* 1D NV12 buffer is always non-rotated or vert. mirrored */
+ bool doublestride = (rotation == OMAP_DSS_ROT_0 ||
+ rotation == OMAP_DSS_ROT_180) &&
+ type == OMAP_DSS_ROT_TILER;
+ /* DOUBLESTRIDE */
+ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), doublestride, 22, 22);
+ }
}
static int color_mode_to_bpp(enum omap_color_mode color_mode)
@@ -1536,6 +1503,28 @@ static s32 pixinc(int pixels, u8 ps)
BUG();
}
+static void calc_tiler_row_rotation(struct tiler_view_t *view,
+ u16 width, int bpp, int y_decim,
+ s32 *row_inc, unsigned *offset1, bool ilace)
+{
+ /* assume TB. We worry about swapping top/bottom outside of this call */
+
+ if (ilace) {
+ /* even and odd frames are interleaved */
+
+ /* offset1 is always at an odd line */
+ *offset1 = view->v_inc * (y_decim | 1);
+ y_decim *= 2;
+ }
+ *row_inc = view->v_inc * y_decim + 1 - width * bpp;
+
+ DSSDBG(" ps: %d/%d, width: %d/%d, offset1: %d,"
+ " height: %d, row_inc:%d\n", view->bpp, bpp,
+ view->width, width, *offset1, view->height, *row_inc);
+
+ return;
+}
+
static void calc_vrfb_rotation_offset(u8 rotation, bool mirror,
u16 screen_width,
u16 width, u16 height,
@@ -1626,7 +1615,7 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror,
enum omap_color_mode color_mode, bool fieldmode,
unsigned int field_offset,
unsigned *offset0, unsigned *offset1,
- s32 *row_inc, s32 *pix_inc)
+ s32 *row_inc, s32 *pix_inc, int x_decim, int y_decim)
{
u8 ps;
u16 fbw, fbh;
@@ -1639,6 +1628,15 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror,
case OMAP_DSS_COLOR_CLUT8:
BUG();
return;
+ case OMAP_DSS_COLOR_YUV2:
+ case OMAP_DSS_COLOR_UYVY:
+ if (cpu_is_omap44xx()) {
+ /* on OMAP4 YUYV is handled as 32-bit data */
+ ps = 4;
+ screen_width /= 2;
+ break;
+ }
+ /* fall through */
default:
ps = color_mode_to_bpp(color_mode) / 8;
break;
@@ -1668,10 +1666,10 @@ static void calc_dma_rotation_offset(u8 rotation, bool mirror,
*offset0 = *offset1 + field_offset * screen_width * ps;
else
*offset0 = *offset1;
- *row_inc = pixinc(1 + (screen_width - fbw) +
+ *row_inc = pixinc(1 + (y_decim * screen_width - fbw * x_decim) +
(fieldmode ? screen_width : 0),
ps);
- *pix_inc = pixinc(1, ps);
+ *pix_inc = pixinc(x_decim, ps);
break;
case OMAP_DSS_ROT_90:
*offset1 = screen_width * (fbh - 1) * ps;
@@ -1768,6 +1766,11 @@ static unsigned long calc_fclk_five_taps(enum omap_channel channel, u16 width,
/* FIXME venc pclk? */
u64 tmp, pclk = dispc_pclk_rate(channel);
+ /* do conservative guess on OMAP4 until better formula is available */
+ if (cpu_is_omap44xx())
+ return pclk * DIV_ROUND_UP(width, out_width) *
+ DIV_ROUND_UP(height, out_height);
+
if (height > out_height) {
/* FIXME get real display PPL */
unsigned int ppl = 800;
@@ -1826,6 +1829,174 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width,
return dispc_pclk_rate(channel) * vf * hf;
}
+int dispc_scaling_decision(u16 width, u16 height,
+ u16 out_width, u16 out_height,
+ enum omap_plane plane,
+ enum omap_color_mode color_mode,
+ enum omap_channel channel, u8 rotation,
+ u16 min_x_decim, u16 max_x_decim,
+ u16 min_y_decim, u16 max_y_decim,
+ u16 *x_decim, u16 *y_decim, bool *five_taps)
+{
+ int maxdownscale = cpu_is_omap24xx() ? 2 : 4;
+ int bpp = color_mode_to_bpp(color_mode);
+
+ /*
+ * For now only whole byte formats on OMAP4 can be predecimated.
+ * Later SDMA decimation support may be added
+ */
+ bool can_decimate_x = cpu_is_omap44xx() && !(bpp & 7);
+ bool can_decimate_y = can_decimate_x;
+
+ bool can_scale = plane != OMAP_DSS_GFX;
+
+ u16 in_width, in_height;
+ unsigned long fclk = 0, fclk5 = 0;
+ int min_factor, max_factor; /* decimation search limits */
+ int x, y; /* decimation search variables */
+ unsigned long fclk_max = dispc_fclk_rate();
+
+ /* No decimation for bitmap formats */
+ if (color_mode == OMAP_DSS_COLOR_CLUT1 ||
+ color_mode == OMAP_DSS_COLOR_CLUT2 ||
+ color_mode == OMAP_DSS_COLOR_CLUT4 ||
+ color_mode == OMAP_DSS_COLOR_CLUT8) {
+ *x_decim = 1;
+ *y_decim = 1;
+ *five_taps = false;
+ return 0;
+ }
+
+ /* restrict search region based on whether we can decimate */
+ if (!can_decimate_x) {
+ if (min_x_decim > 1)
+ return -EINVAL;
+ min_x_decim = max_x_decim = 1;
+ } else {
+ if (max_x_decim > 16)
+ max_x_decim = 16;
+ }
+
+ if (!can_decimate_y) {
+ if (min_y_decim > 1)
+ return -EINVAL;
+ min_y_decim = max_y_decim = 1;
+ }
+
+ /*
+ * Find best supported quality. In the search algorithm, we make use
+ * of the fact, that increased decimation in either direction will have
+ * lower quality. However, we do not differentiate horizontal and
+ * vertical decimation even though they may affect quality differently
+ * given the exact geometry involved.
+ *
+ * Also, since the clock calculations are abstracted, we cannot make
+ * assumptions on how decimation affects the clock rates in our search.
+ *
+ * We search the whole search region in increasing layers from
+ * min_factor to max_factor. In each layer we search in increasing
+ * factors alternating between x and y axis:
+ *
+ * x: 1 2 3
+ * y:
+ * 1 1st | 3rd | 6th |
+ * ----+ | |
+ * 2 2nd 4th | 8th |
+ * ------------+ |
+ * 3 5th 7th 9th |
+ * --------------------+
+ */
+ min_factor = min(min_x_decim, min_y_decim);
+ max_factor = max(max_x_decim, max_y_decim);
+ x = min_x_decim;
+ y = min_y_decim;
+ while (1) {
+ if (x < min_x_decim || x > max_x_decim ||
+ y < min_y_decim || y > max_y_decim)
+ goto loop;
+
+ in_width = DIV_ROUND_UP(width, x);
+ in_height = DIV_ROUND_UP(height, y);
+
+ if (in_width == out_width && in_height == out_height)
+ break;
+
+ if (!can_scale)
+ goto loop;
+
+ if (out_width < in_width / maxdownscale ||
+ out_height < in_height / maxdownscale)
+ goto loop;
+
+ /* Use 5-tap filter unless must use 3-tap */
+ if (!cpu_is_omap44xx())
+ *five_taps = in_width <= 1024;
+ else if (omap_rev() == OMAP4430_REV_ES1_0)
+ *five_taps = in_width <= 1280;
+ else
+ *five_taps = true;
+
+ /* Also use 3-tap if downscaling by 2 or less */
+ *five_taps &= out_height * 2 < in_height;
+
+ /*
+ * Predecimation on OMAP4 still fetches the whole lines
+ * :TODO: How does it affect the required clock speed?
+ */
+ fclk = calc_fclk(channel, in_width, in_height,
+ out_width, out_height);
+ fclk5 = *five_taps ?
+ calc_fclk_five_taps(channel, in_width, in_height,
+ out_width, out_height, color_mode) : 0;
+
+ DSSDBG("%d*%d,%d*%d->%d,%d requires %lu(3T), %lu(5T) Hz\n",
+ in_width, x, in_height, y, out_width, out_height,
+ fclk, fclk5);
+
+ /* Use 3-tap if 5-tap clock requirement is too high */
+ *five_taps &= fclk5 <= fclk_max;
+
+ /* for now we always use 5-tap unless 3-tap is required */
+ if (*five_taps)
+ fclk = fclk5;
+
+ /* OMAP2/3 has a scaler size limitation */
+ if (!cpu_is_omap44xx() && in_width > (1024 << !*five_taps))
+ goto loop;
+
+ DSSDBG("required fclk rate = %lu Hz\n", fclk);
+ DSSDBG("current fclk rate = %lu Hz\n", fclk_max);
+
+ if (fclk > fclk_max)
+ goto loop;
+ break;
+
+loop:
+ /* err if exhausted search region */
+ if (x == max_x_decim && y == max_y_decim) {
+ DSSERR("failed to set up scaling, "
+ "required fclk rate = %lu Hz, "
+ "current fclk rate = %lu Hz\n",
+ fclk, fclk_max);
+ return -EINVAL;
+ }
+
+ /* get to next factor */
+ if (x == y) {
+ x = min_factor;
+ y++;
+ } else {
+ swap(x, y);
+ if (x < y)
+ x++;
+ }
+ }
+
+ *x_decim = x;
+ *y_decim = y;
+ return 0;
+}
+
int dispc_setup_plane(enum omap_plane plane,
u32 paddr, u16 screen_width,
u16 pos_x, u16 pos_y,
@@ -1833,13 +2004,13 @@ int dispc_setup_plane(enum omap_plane plane,
u16 out_width, u16 out_height,
enum omap_color_mode color_mode,
bool ilace,
+ int x_decim, int y_decim, bool five_taps,
enum omap_dss_rotation_type rotation_type,
u8 rotation, bool mirror,
u8 global_alpha, u8 pre_mult_alpha,
enum omap_channel channel, u32 puv_addr)
{
- const int maxdownscale = cpu_is_omap34xx() ? 4 : 2;
- bool five_taps = 0;
+ const int maxdownscale = cpu_is_omap24xx() ? 2 : 4;
bool fieldmode = 0;
int cconv = 0;
unsigned offset0, offset1;
@@ -1847,14 +2018,17 @@ int dispc_setup_plane(enum omap_plane plane,
s32 pix_inc;
u16 frame_height = height;
unsigned int field_offset = 0;
+ int pixpg = (color_mode &
+ (OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY)) ? 2 : 1;
+ unsigned long tiler_width, tiler_height;
- DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> "
- "%dx%d, ilace %d, cmode %x, rot %d, mir %d chan %d\n",
+ DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %d/%dx%d/%d -> "
+ "%dx%d, ilace %d, cmode %x, rot %d, mir %d chan %d %dtap\n",
plane, paddr, screen_width, pos_x, pos_y,
- width, height,
+ width, x_decim, height, y_decim,
out_width, out_height,
ilace, color_mode,
- rotation, mirror, channel);
+ rotation, mirror, channel, five_taps ? 5 : 3);
if (paddr == 0)
return -EINVAL;
@@ -1876,59 +2050,41 @@ int dispc_setup_plane(enum omap_plane plane,
if (!dss_feat_color_mode_supported(plane, color_mode))
return -EINVAL;
+ /* predecimate */
+
+ /* adjust for group-of-pixels*/
+ if (rotation & 1)
+ height /= pixpg;
+ else
+ width /= pixpg;
+
+ /* remember tiler block's size as we are reconstructing it */
+ tiler_width = width;
+ tiler_height = height;
+
+ width = DIV_ROUND_UP(width, x_decim);
+ height = DIV_ROUND_UP(height, y_decim);
+
+ /* NV12 width has to be even (height apparently does not) */
+ if (color_mode == OMAP_DSS_COLOR_NV12)
+ width &= ~1;
+
if (plane == OMAP_DSS_GFX) {
if (width != out_width || height != out_height)
return -EINVAL;
} else {
/* video plane */
- unsigned long fclk = 0;
-
- if (out_width < width / maxdownscale ||
- out_width > width * 8)
+ if (out_width < width / maxdownscale)
return -EINVAL;
- if (out_height < height / maxdownscale ||
- out_height > height * 8)
+ if (out_height < height / maxdownscale)
return -EINVAL;
if (color_mode == OMAP_DSS_COLOR_YUV2 ||
color_mode == OMAP_DSS_COLOR_UYVY ||
color_mode == OMAP_DSS_COLOR_NV12)
cconv = 1;
-
- /* Must use 5-tap filter? */
- five_taps = height > out_height * 2;
-
- if (!five_taps) {
- fclk = calc_fclk(channel, width, height, out_width,
- out_height);
-
- /* Try 5-tap filter if 3-tap fclk is too high */
- if (cpu_is_omap34xx() && height > out_height &&
- fclk > dispc_fclk_rate())
- five_taps = true;
- }
-
- if (width > (2048 >> five_taps)) {
- DSSERR("failed to set up scaling, fclk too low\n");
- return -EINVAL;
- }
-
- if (five_taps)
- fclk = calc_fclk_five_taps(channel, width, height,
- out_width, out_height, color_mode);
-
- DSSDBG("required fclk rate = %lu Hz\n", fclk);
- DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate());
-
- if (!fclk || fclk > dispc_fclk_rate()) {
- DSSERR("failed to set up scaling, "
- "required fclk rate = %lu Hz, "
- "current fclk rate = %lu Hz\n",
- fclk, dispc_fclk_rate());
- return -EINVAL;
- }
}
if (ilace && !fieldmode) {
@@ -1949,17 +2105,69 @@ int dispc_setup_plane(enum omap_plane plane,
if (fieldmode)
field_offset = 1;
- if (rotation_type == OMAP_DSS_ROT_DMA)
+ /* default values */
+ row_inc = pix_inc = 0x1;
+ offset0 = offset1 = 0x0;
+
+ /*
+ * :HACK: we piggy back on UV separate feature for TILER to avoid
+ * having to keep rebase our FEAT_ enum until they add TILER.
+ */
+ if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
+ /* set BURSTTYPE */
+ bool use_tiler = rotation_type == OMAP_DSS_ROT_TILER;
+ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), use_tiler, 29, 29);
+ }
+
+ if (rotation_type == OMAP_DSS_ROT_TILER) {
+ struct tiler_view_t view = {0};
+ int bpp = color_mode_to_bpp(color_mode) / 8;
+ /* tiler needs 0-degree width & height */
+ if (rotation & 1)
+ swap(tiler_width, tiler_height);
+
+ if (color_mode == OMAP_DSS_COLOR_YUV2 ||
+ color_mode == OMAP_DSS_COLOR_UYVY)
+ tiler_width /= 2;
+
+ tilview_create(&view, paddr, tiler_width, tiler_height);
+ tilview_rotate(&view, rotation * 90);
+ tilview_flip(&view, mirror, false);
+ paddr = view.tsptr;
+
+ /* we cannot do TB field interlaced in rotated view */
+ pix_inc = 1 + (x_decim - 1) * bpp * pixpg;
+ calc_tiler_row_rotation(&view, width * x_decim, bpp * pixpg,
+ y_decim, &row_inc, &offset1, ilace);
+
+ DSSDBG("w, h = %ld %ld\n", tiler_width, tiler_height);
+
+ if (puv_addr) {
+ tilview_create(&view, puv_addr, tiler_width / 2,
+ tiler_height / 2);
+ tilview_rotate(&view, rotation * 90);
+ tilview_flip(&view, mirror, false);
+ puv_addr = view.tsptr;
+ }
+
+ } else if (rotation_type == OMAP_DSS_ROT_DMA) {
calc_dma_rotation_offset(rotation, mirror,
screen_width, width, frame_height, color_mode,
fieldmode, field_offset,
- &offset0, &offset1, &row_inc, &pix_inc);
- else
+ &offset0, &offset1, &row_inc, &pix_inc,
+ x_decim, y_decim);
+ } else {
calc_vrfb_rotation_offset(rotation, mirror,
screen_width, width, frame_height, color_mode,
fieldmode, field_offset,
&offset0, &offset1, &row_inc, &pix_inc);
+ }
+ /* adjust back to pixels */
+ if (rotation & 1)
+ height *= pixpg;
+ else
+ width *= pixpg;
DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n",
offset0, offset1, row_inc, pix_inc);
@@ -1977,8 +2185,8 @@ int dispc_setup_plane(enum omap_plane plane,
_dispc_set_row_inc(plane, row_inc);
_dispc_set_pix_inc(plane, pix_inc);
- DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, width, height,
- out_width, out_height);
+ DSSDBG("%d,%d %d*%dx%d*%d -> %dx%d\n", pos_x, pos_y, width, x_decim,
+ height, y_decim, out_width, out_height);
_dispc_set_plane_pos(plane, pos_x, pos_y);
@@ -1993,7 +2201,8 @@ int dispc_setup_plane(enum omap_plane plane,
_dispc_set_vid_color_conv(plane, cconv);
}
- _dispc_set_rotation_attrs(plane, rotation, mirror, color_mode);
+ _dispc_set_rotation_attrs(plane, rotation, mirror, color_mode,
+ rotation_type);
_dispc_set_pre_mult_alpha(plane, pre_mult_alpha);
_dispc_setup_global_alpha(plane, global_alpha);
@@ -2135,6 +2344,8 @@ static void dispc_enable_digit_out(bool enable)
dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR;
if (dss_has_feature(FEAT_MGR_LCD2))
dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST2;
+ if (dss_has_feature(FEAT_OVL_VID3))
+ dispc.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW;
dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT);
_omap_dispc_set_irqs();
spin_unlock_irqrestore(&dispc.irq_lock, flags);
@@ -2293,12 +2504,11 @@ void dispc_enable_alpha_blending(enum omap_channel ch, bool enable)
if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
return;
+ /* :NOTE: compatibility mode is not supported on LCD2 */
if (ch == OMAP_DSS_CHANNEL_LCD)
REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18);
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19);
- else /* OMAP_DSS_CHANNEL_LCD2 */
- REG_FLD_MOD(DISPC_CONFIG2, enable, 18, 18);
}
bool dispc_alpha_blending_enabled(enum omap_channel ch)
{
@@ -2312,7 +2522,7 @@ bool dispc_alpha_blending_enabled(enum omap_channel ch)
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
enabled = REG_GET(DISPC_CONFIG, 19, 19);
else if (ch == OMAP_DSS_CHANNEL_LCD2)
- enabled = REG_GET(DISPC_CONFIG2, 18, 18);
+ enabled = false;
else
BUG();
@@ -2678,6 +2888,10 @@ void dispc_dump_irqs(struct seq_file *s)
PIS(VID1_END_WIN);
PIS(VID2_FIFO_UNDERFLOW);
PIS(VID2_END_WIN);
+ if (dss_has_feature(FEAT_OVL_VID3)) {
+ PIS(VID3_FIFO_UNDERFLOW);
+ PIS(VID3_END_WIN);
+ }
PIS(SYNC_LOST);
PIS(SYNC_LOST_DIGIT);
PIS(WAKEUP);
@@ -2693,6 +2907,7 @@ void dispc_dump_irqs(struct seq_file *s)
void dispc_dump_regs(struct seq_file *s)
{
+ int i, o;
#define DUMPREG(r) seq_printf(s, "%-50s %08x\n", #r, dispc_read_reg(r))
if (dispc_runtime_get())
@@ -2768,177 +2983,59 @@ void dispc_dump_regs(struct seq_file *s)
if (dss_has_feature(FEAT_PRELOAD))
DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_GFX));
- DUMPREG(DISPC_OVL_BA0(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_BA1(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_POSITION(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_SIZE(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_ROW_INC(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_PIXEL_INC(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_FIR(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_ACCU0(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_ACCU1(OMAP_DSS_VIDEO1));
-
- DUMPREG(DISPC_OVL_BA0(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_BA1(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_POSITION(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_SIZE(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_ROW_INC(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_PIXEL_INC(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_FIR(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_ACCU0(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_ACCU1(OMAP_DSS_VIDEO2));
-
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 7));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 7));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 4));
- if (dss_has_feature(FEAT_FIR_COEF_V)) {
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 7));
- }
+ for (o = OMAP_DSS_VIDEO1; o <= OMAP_DSS_VIDEO3; o++) {
+ if (o == OMAP_DSS_VIDEO3 && !dss_has_feature(FEAT_OVL_VID3))
+ continue;
- if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
- DUMPREG(DISPC_OVL_BA0_UV(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_BA1_UV(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_FIR2(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_ACCU2_0(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_ACCU2_1(OMAP_DSS_VIDEO1));
-
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 7));
-
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 7));
-
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 7));
- }
- if (dss_has_feature(FEAT_ATTR2))
- DUMPREG(DISPC_OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1));
-
-
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 7));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 7));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 4));
-
- if (dss_has_feature(FEAT_FIR_COEF_V)) {
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 7));
- }
+ DUMPREG(DISPC_OVL_BA0(o));
+ DUMPREG(DISPC_OVL_BA1(o));
+ DUMPREG(DISPC_OVL_POSITION(o));
+ DUMPREG(DISPC_OVL_SIZE(o));
+ DUMPREG(DISPC_OVL_ATTRIBUTES(o));
+ DUMPREG(DISPC_OVL_FIFO_THRESHOLD(o));
+ DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(o));
+ DUMPREG(DISPC_OVL_ROW_INC(o));
+ DUMPREG(DISPC_OVL_PIXEL_INC(o));
+ DUMPREG(DISPC_OVL_FIR(o));
+ DUMPREG(DISPC_OVL_PICTURE_SIZE(o));
+ DUMPREG(DISPC_OVL_ACCU0(o));
+ DUMPREG(DISPC_OVL_ACCU1(o));
- if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
- DUMPREG(DISPC_OVL_BA0_UV(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_BA1_UV(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_FIR2(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_ACCU2_0(OMAP_DSS_VIDEO2));
- DUMPREG(DISPC_OVL_ACCU2_1(OMAP_DSS_VIDEO2));
-
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 7));
-
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 7));
-
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 0));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 1));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 2));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 3));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 4));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 5));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 6));
- DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 7));
- }
- if (dss_has_feature(FEAT_ATTR2))
- DUMPREG(DISPC_OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2));
-
- if (dss_has_feature(FEAT_PRELOAD)) {
- DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO2));
+ for (i = 0; i < 8; i++)
+ DUMPREG(DISPC_OVL_FIR_COEF_H(o, i));
+
+ for (i = 0; i < 8; i++)
+ DUMPREG(DISPC_OVL_FIR_COEF_HV(o, i));
+
+ for (i = 0; i < 5; i++)
+ DUMPREG(DISPC_OVL_CONV_COEF(o, i));
+
+ if (dss_has_feature(FEAT_FIR_COEF_V)) {
+ for (i = 0; i < 8; i++)
+ DUMPREG(DISPC_OVL_FIR_COEF_V(o, i));
+ }
+
+ if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
+ DUMPREG(DISPC_OVL_BA0_UV(o));
+ DUMPREG(DISPC_OVL_BA1_UV(o));
+ DUMPREG(DISPC_OVL_FIR2(o));
+ DUMPREG(DISPC_OVL_ACCU2_0(o));
+ DUMPREG(DISPC_OVL_ACCU2_1(o));
+
+ for (i = 0; i < 8; i++)
+ DUMPREG(DISPC_OVL_FIR_COEF_H2(o, i));
+
+ for (i = 0; i < 8; i++)
+ DUMPREG(DISPC_OVL_FIR_COEF_HV2(o, i));
+
+ for (i = 0; i < 8; i++)
+ DUMPREG(DISPC_OVL_FIR_COEF_V2(o, i));
+ }
+ if (dss_has_feature(FEAT_ATTR2))
+ DUMPREG(DISPC_OVL_ATTRIBUTES2(o));
+
+ if (dss_has_feature(FEAT_PRELOAD))
+ DUMPREG(DISPC_OVL_PRELOAD(o));
}
dispc_runtime_put();
@@ -3193,6 +3290,8 @@ static void print_irq_status(u32 status)
PIS(OCP_ERR);
PIS(VID1_FIFO_UNDERFLOW);
PIS(VID2_FIFO_UNDERFLOW);
+ if (dss_has_feature(FEAT_OVL_VID3))
+ PIS(VID3_FIFO_UNDERFLOW);
PIS(SYNC_LOST);
PIS(SYNC_LOST_DIGIT);
if (dss_has_feature(FEAT_MGR_LCD2))
@@ -3346,6 +3445,24 @@ static void dispc_error_worker(struct work_struct *work)
}
}
+ if (errors & DISPC_IRQ_VID3_FIFO_UNDERFLOW) {
+ DSSERR("VID3_FIFO_UNDERFLOW, disabling VID3\n");
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+ struct omap_overlay *ovl;
+ ovl = omap_dss_get_overlay(i);
+
+ if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
+ continue;
+
+ if (ovl->id == 3) {
+ dispc_enable_plane(ovl->id, 0);
+ dispc_go(ovl->manager->id);
+ mdelay(50);
+ break;
+ }
+ }
+ }
+
if (errors & DISPC_IRQ_SYNC_LOST) {
struct omap_overlay_manager *manager = NULL;
bool enable = false;
@@ -3572,7 +3689,8 @@ static void _omap_dispc_initialize_irq(void)
dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR;
if (dss_has_feature(FEAT_MGR_LCD2))
dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST2;
-
+ if (dss_has_feature(FEAT_OVL_VID3))
+ dispc.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW;
/* there's SYNC_LOST_DIGIT waiting after enabling the DSS,
* so clear it */
dispc_write_reg(DISPC_IRQSTATUS, dispc_read_reg(DISPC_IRQSTATUS));
diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h
index 6c9ee0a..c06efc3 100644
--- a/drivers/video/omap2/dss/dispc.h
+++ b/drivers/video/omap2/dss/dispc.h
@@ -291,6 +291,8 @@ static inline u16 DISPC_OVL_BASE(enum omap_plane plane)
return 0x00BC;
case OMAP_DSS_VIDEO2:
return 0x014C;
+ case OMAP_DSS_VIDEO3:
+ return 0x0300;
default:
BUG();
}
@@ -304,6 +306,8 @@ static inline u16 DISPC_BA0_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0000;
+ case OMAP_DSS_VIDEO3:
+ return 0x0008;
default:
BUG();
}
@@ -316,6 +320,8 @@ static inline u16 DISPC_BA1_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0004;
+ case OMAP_DSS_VIDEO3:
+ return 0x000C;
default:
BUG();
}
@@ -330,6 +336,8 @@ static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane)
return 0x0544;
case OMAP_DSS_VIDEO2:
return 0x04BC;
+ case OMAP_DSS_VIDEO3:
+ return 0x0310;
default:
BUG();
}
@@ -344,6 +352,8 @@ static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane)
return 0x0548;
case OMAP_DSS_VIDEO2:
return 0x04C0;
+ case OMAP_DSS_VIDEO3:
+ return 0x0314;
default:
BUG();
}
@@ -356,6 +366,8 @@ static inline u16 DISPC_POS_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0008;
+ case OMAP_DSS_VIDEO3:
+ return 0x009C;
default:
BUG();
}
@@ -368,6 +380,8 @@ static inline u16 DISPC_SIZE_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x000C;
+ case OMAP_DSS_VIDEO3:
+ return 0x00A8;
default:
BUG();
}
@@ -381,6 +395,8 @@ static inline u16 DISPC_ATTR_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0010;
+ case OMAP_DSS_VIDEO3:
+ return 0x0070;
default:
BUG();
}
@@ -395,6 +411,8 @@ static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane)
return 0x0568;
case OMAP_DSS_VIDEO2:
return 0x04DC;
+ case OMAP_DSS_VIDEO3:
+ return 0x032C;
default:
BUG();
}
@@ -408,6 +426,8 @@ static inline u16 DISPC_FIFO_THRESH_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0014;
+ case OMAP_DSS_VIDEO3:
+ return 0x008C;
default:
BUG();
}
@@ -421,6 +441,8 @@ static inline u16 DISPC_FIFO_SIZE_STATUS_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0018;
+ case OMAP_DSS_VIDEO3:
+ return 0x0088;
default:
BUG();
}
@@ -434,6 +456,8 @@ static inline u16 DISPC_ROW_INC_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x001C;
+ case OMAP_DSS_VIDEO3:
+ return 0x00A4;
default:
BUG();
}
@@ -447,6 +471,8 @@ static inline u16 DISPC_PIX_INC_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0020;
+ case OMAP_DSS_VIDEO3:
+ return 0x0098;
default:
BUG();
}
@@ -459,6 +485,7 @@ static inline u16 DISPC_WINDOW_SKIP_OFFSET(enum omap_plane plane)
return 0x0034;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
+ case OMAP_DSS_VIDEO3:
BUG();
default:
BUG();
@@ -472,6 +499,7 @@ static inline u16 DISPC_TABLE_BA_OFFSET(enum omap_plane plane)
return 0x0038;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
+ case OMAP_DSS_VIDEO3:
BUG();
default:
BUG();
@@ -486,6 +514,8 @@ static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0024;
+ case OMAP_DSS_VIDEO3:
+ return 0x0090;
default:
BUG();
}
@@ -500,6 +530,8 @@ static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane)
return 0x0580;
case OMAP_DSS_VIDEO2:
return 0x055C;
+ case OMAP_DSS_VIDEO3:
+ return 0x0424;
default:
BUG();
}
@@ -513,6 +545,8 @@ static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0028;
+ case OMAP_DSS_VIDEO3:
+ return 0x0094;
default:
BUG();
}
@@ -527,6 +561,8 @@ static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x002C;
+ case OMAP_DSS_VIDEO3:
+ return 0x0000;
default:
BUG();
}
@@ -541,6 +577,8 @@ static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane)
return 0x0584;
case OMAP_DSS_VIDEO2:
return 0x0560;
+ case OMAP_DSS_VIDEO3:
+ return 0x0428;
default:
BUG();
}
@@ -554,6 +592,8 @@ static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0030;
+ case OMAP_DSS_VIDEO3:
+ return 0x0004;
default:
BUG();
}
@@ -568,6 +608,8 @@ static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane)
return 0x0588;
case OMAP_DSS_VIDEO2:
return 0x0564;
+ case OMAP_DSS_VIDEO3:
+ return 0x042C;
default:
BUG();
}
@@ -582,6 +624,8 @@ static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0034 + i * 0x8;
+ case OMAP_DSS_VIDEO3:
+ return 0x0010 + i * 0x8;
default:
BUG();
}
@@ -597,6 +641,8 @@ static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i)
return 0x058C + i * 0x8;
case OMAP_DSS_VIDEO2:
return 0x0568 + i * 0x8;
+ case OMAP_DSS_VIDEO3:
+ return 0x0430 + i * 0x8;
default:
BUG();
}
@@ -611,6 +657,8 @@ static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i)
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
return 0x0038 + i * 0x8;
+ case OMAP_DSS_VIDEO3:
+ return 0x0014 + i * 0x8;
default:
BUG();
}
@@ -626,6 +674,8 @@ static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i)
return 0x0590 + i * 8;
case OMAP_DSS_VIDEO2:
return 0x056C + i * 0x8;
+ case OMAP_DSS_VIDEO3:
+ return 0x0434 + i * 0x8;
default:
BUG();
}
@@ -639,6 +689,7 @@ static inline u16 DISPC_CONV_COEF_OFFSET(enum omap_plane plane, u16 i)
BUG();
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
+ case OMAP_DSS_VIDEO3:
return 0x0074 + i * 0x4;
default:
BUG();
@@ -655,6 +706,8 @@ static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i)
return 0x0124 + i * 0x4;
case OMAP_DSS_VIDEO2:
return 0x00B4 + i * 0x4;
+ case OMAP_DSS_VIDEO3:
+ return 0x0050 + i * 0x4;
default:
BUG();
}
@@ -670,6 +723,8 @@ static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i)
return 0x05CC + i * 0x4;
case OMAP_DSS_VIDEO2:
return 0x05A8 + i * 0x4;
+ case OMAP_DSS_VIDEO3:
+ return 0x0470 + i * 0x4;
default:
BUG();
}
@@ -684,6 +739,8 @@ static inline u16 DISPC_PRELOAD_OFFSET(enum omap_plane plane)
return 0x0174;
case OMAP_DSS_VIDEO2:
return 0x00E8;
+ case OMAP_DSS_VIDEO3:
+ return 0x00A0;
default:
BUG();
}
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index ce17a61..6d59b26 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -398,6 +398,9 @@ void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high);
void dispc_enable_fifomerge(bool enable);
void dispc_set_burst_size(enum omap_plane plane,
enum omap_burst_size burst_size);
+void dispc_set_zorder(enum omap_plane plane,
+ enum omap_overlay_zorder zorder);
+void dispc_enable_zorder(enum omap_plane plane, bool enable);
void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr);
void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr);
@@ -414,11 +417,20 @@ int dispc_setup_plane(enum omap_plane plane,
u16 out_width, u16 out_height,
enum omap_color_mode color_mode,
bool ilace,
+ int x_decim, int y_decim, bool five_taps,
enum omap_dss_rotation_type rotation_type,
u8 rotation, bool mirror,
u8 global_alpha, u8 pre_mult_alpha,
enum omap_channel channel,
u32 puv_addr);
+int dispc_scaling_decision(u16 width, u16 height,
+ u16 out_width, u16 out_height,
+ enum omap_plane plane,
+ enum omap_color_mode color_mode,
+ enum omap_channel channel, u8 rotation,
+ u16 min_x_decim, u16 max_x_decim,
+ u16 min_y_decim, u16 max_y_decim,
+ u16 *x_decim, u16 *y_decim, bool *three_tap);
bool dispc_go_busy(enum omap_channel channel);
void dispc_go(enum omap_channel channel);
@@ -535,4 +547,11 @@ static inline void dss_collect_irq_stats(u32 irqstatus, unsigned *irq_arr)
}
#endif
+/* callback is optional */
+static inline void dss_ovl_cb(struct omapdss_ovl_cb *cb, int id, int status)
+{
+ if (cb->fn)
+ cb->fn(cb->data, id, status);
+}
+
#endif
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c
index bd420f9..aba2250 100644
--- a/drivers/video/omap2/dss/dss_features.c
+++ b/drivers/video/omap2/dss/dss_features.c
@@ -197,7 +197,17 @@ static const enum omap_color_mode omap4_dss_supported_color_modes[] = {
OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 |
OMAP_DSS_COLOR_RGBX32,
- /* OMAP_DSS_VIDEO2 */
+ /* OMAP_DSS_VIDEO2 */
+ OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U |
+ OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 |
+ OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 |
+ OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U |
+ OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY |
+ OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 |
+ OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 |
+ OMAP_DSS_COLOR_RGBX32,
+
+ /* OMAP_DSS_VIDEO3 */
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U |
OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 |
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 |
@@ -331,10 +341,12 @@ static const struct omap_dss_features omap4430_es1_0_dss_features = {
FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC |
FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH |
FEAT_DSI_GNQ | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 |
- FEAT_CPR | FEAT_PRELOAD | FEAT_FIR_COEF_V,
+ FEAT_CPR | FEAT_PRELOAD | FEAT_FIR_COEF_V |
+ FEAT_ALPHA_OMAP3_COMPAT | FEAT_OVL_VID3 |
+ FEAT_OVL_ZORDER,
.num_mgrs = 3,
- .num_ovls = 3,
+ .num_ovls = 4,
.supported_displays = omap4_dss_supported_displays,
.supported_color_modes = omap4_dss_supported_color_modes,
.clksrc_names = omap4_dss_clk_source_names,
@@ -353,10 +365,12 @@ static const struct omap_dss_features omap4_dss_features = {
FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH |
FEAT_DSI_GNQ | FEAT_HDMI_CTS_SWMODE |
FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | FEAT_CPR |
- FEAT_PRELOAD | FEAT_FIR_COEF_V,
+ FEAT_PRELOAD | FEAT_FIR_COEF_V |
+ FEAT_ALPHA_OMAP3_COMPAT | FEAT_OVL_VID3 |
+ FEAT_OVL_ZORDER,
.num_mgrs = 3,
- .num_ovls = 3,
+ .num_ovls = 4,
.supported_displays = omap4_dss_supported_displays,
.supported_color_modes = omap4_dss_supported_color_modes,
.clksrc_names = omap4_dss_clk_source_names,
diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h
index 5be8103..28f44ed 100644
--- a/drivers/video/omap2/dss/dss_features.h
+++ b/drivers/video/omap2/dss/dss_features.h
@@ -21,7 +21,7 @@
#define __OMAP2_DSS_FEATURES_H
#define MAX_DSS_MANAGERS 3
-#define MAX_DSS_OVERLAYS 3
+#define MAX_DSS_OVERLAYS 4
#define MAX_DSS_LCD_MANAGERS 2
#define MAX_NUM_DSI 2
@@ -55,6 +55,9 @@ enum dss_feat_id {
FEAT_CPR = 1 << 23,
FEAT_PRELOAD = 1 << 24,
FEAT_FIR_COEF_V = 1 << 25,
+ FEAT_ALPHA_OMAP3_COMPAT = 1 << 26,
+ FEAT_OVL_VID3 = 1 << 27,
+ FEAT_OVL_ZORDER = 1 << 28,
};
/* DSS register field id */
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
index b1126b6..106767d 100644
--- a/drivers/video/omap2/dss/manager.c
+++ b/drivers/video/omap2/dss/manager.c
@@ -260,6 +260,10 @@ static ssize_t manager_alpha_blending_enabled_store(
if (sscanf(buf, "%d", &enable) != 1)
return -EINVAL;
+ /* if we have OMAP3 alpha compatibility, alpha blending is always on */
+ if (dss_has_feature(FEAT_ALPHA_OMAP3_COMPAT) && !enable)
+ return -EINVAL;
+
mgr->get_manager_info(mgr, &info);
info.alpha_enabled = enable ? true : false;
@@ -353,6 +357,20 @@ static struct kobj_type manager_ktype = {
.default_attrs = manager_sysfs_attrs,
};
+struct callback_states {
+ /*
+ * Keep track of callbacks at the last 3 levels of pipeline:
+ * cache, shadow registers and in DISPC registers.
+ *
+ * Note: We zero the function pointer when moving from one level to
+ * another to avoid checking for dirty and shadow_dirty fields that
+ * are not common between overlay and manager cache structures.
+ */
+ struct omapdss_ovl_cb cache, shadow, dispc;
+ bool dispc_displayed;
+ bool shadow_enabled;
+};
+
/*
* We have 4 levels of cache for the dispc settings. First two are in SW and
* the latter two in HW.
@@ -409,15 +427,20 @@ struct overlay_cache_data {
u8 global_alpha;
u8 pre_mult_alpha;
+ struct callback_states cb; /* callback data for the last 3 states */
+ int dispc_channel; /* overlay's channel in DISPC */
+
enum omap_channel channel;
bool replication;
bool ilace;
+ u16 min_x_decim, max_x_decim, min_y_decim, max_y_decim;
enum omap_burst_size burst_size;
u32 fifo_low;
u32 fifo_high;
bool manual_update;
+ enum omap_overlay_zorder zorder;
};
struct manager_cache_data {
@@ -447,6 +470,8 @@ struct manager_cache_data {
/* enlarge the update area if the update area contains scaled
* overlays */
bool enlarge_update_area;
+
+ struct callback_states cb; /* callback data for the last 3 states */
};
static struct {
@@ -455,9 +480,43 @@ static struct {
struct manager_cache_data manager_cache[MAX_DSS_MANAGERS];
bool irq_enabled;
+ bool comp_irq_enabled;
} dss_cache;
+/* propagating callback info between states */
+static inline void
+dss_ovl_configure_cb(struct callback_states *st, int i, bool enabled)
+{
+ /* complete info in shadow */
+ dss_ovl_cb(&st->shadow, i, DSS_COMPLETION_ECLIPSED_SHADOW);
+ /* propagate cache to shadow */
+ st->shadow = st->cache;
+ st->shadow_enabled = enabled;
+ st->cache.fn = NULL; /* info traveled to shadow */
+}
+
+static inline void
+dss_ovl_program_cb(struct callback_states *st, int i)
+{
+ /* mark previous programming as completed */
+ dss_ovl_cb(&st->dispc, i, st->dispc_displayed ?
+ DSS_COMPLETION_RELEASED : DSS_COMPLETION_TORN);
+
+ /* mark shadow info as programmed, not yet displayed */
+ dss_ovl_cb(&st->shadow, i, DSS_COMPLETION_PROGRAMMED);
+
+ /* if overlay/manager is not enabled, we are done now */
+ if (!st->shadow_enabled) {
+ dss_ovl_cb(&st->shadow, i, DSS_COMPLETION_RELEASED);
+ st->shadow.fn = NULL;
+ }
+
+ /* propagate shadow to dispc */
+ st->dispc = st->shadow;
+ st->shadow.fn = NULL;
+ st->dispc_displayed = false;
+}
static int omap_dss_set_device(struct omap_overlay_manager *mgr,
struct omap_dss_device *dssdev)
@@ -743,6 +802,8 @@ static int configure_overlay(enum omap_plane plane)
u16 x, y, w, h;
u32 paddr;
int r;
+ u16 x_decim, y_decim;
+ bool five_taps;
u16 orig_w, orig_h, orig_outw, orig_outh;
DSSDBGF("%d", plane);
@@ -785,11 +846,17 @@ static int configure_overlay(enum omap_plane plane)
case OMAP_DSS_COLOR_NV12:
bpp = 8;
break;
+
+ case OMAP_DSS_COLOR_CLUT1:
+ case OMAP_DSS_COLOR_CLUT2:
+ case OMAP_DSS_COLOR_CLUT4:
+ case OMAP_DSS_COLOR_CLUT8:
case OMAP_DSS_COLOR_RGB16:
case OMAP_DSS_COLOR_ARGB16:
case OMAP_DSS_COLOR_YUV2:
case OMAP_DSS_COLOR_UYVY:
case OMAP_DSS_COLOR_RGBA16:
+ case OMAP_DSS_COLOR_RGB12U:
case OMAP_DSS_COLOR_RGBX16:
case OMAP_DSS_COLOR_ARGB16_1555:
case OMAP_DSS_COLOR_XRGB16_1555:
@@ -855,14 +922,19 @@ static int configure_overlay(enum omap_plane plane)
}
}
- r = dispc_setup_plane(plane,
+ r = dispc_scaling_decision(w, h, outw, outh,
+ plane, c->color_mode, c->channel,
+ c->rotation, c->min_x_decim, c->max_x_decim,
+ c->min_y_decim, c->max_y_decim,
+ &x_decim, &y_decim, &five_taps);
+ r = r ? : dispc_setup_plane(plane,
paddr,
c->screen_width,
x, y,
w, h,
outw, outh,
c->color_mode,
- c->ilace,
+ c->ilace, x_decim, y_decim, five_taps,
c->rotation_type,
c->rotation,
c->mirror,
@@ -881,6 +953,8 @@ static int configure_overlay(enum omap_plane plane)
dispc_enable_replication(plane, c->replication);
dispc_set_burst_size(plane, c->burst_size);
+ dispc_set_zorder(plane, c->zorder);
+ dispc_enable_zorder(plane, 1);
dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high);
dispc_enable_plane(plane, 1);
@@ -899,7 +973,15 @@ static void configure_manager(enum omap_channel channel)
dispc_set_default_color(channel, c->default_color);
dispc_set_trans_key(channel, c->trans_key_type, c->trans_key);
dispc_enable_trans_key(channel, c->trans_enabled);
- dispc_enable_alpha_blending(channel, c->alpha_enabled);
+
+ /* if we have OMAP3 alpha compatibility, alpha blending is always on */
+ if (dss_has_feature(FEAT_ALPHA_OMAP3_COMPAT)) {
+ /* and alpha_blending bit enables OMAP3 compatibility mode */
+ dispc_enable_alpha_blending(channel, false);
+ c->alpha_enabled = true;
+ } else {
+ dispc_enable_alpha_blending(channel, c->alpha_enabled);
+ }
}
/* configure_dispc() tries to write values from cache to shadow registers.
@@ -914,6 +996,7 @@ static int configure_dispc(void)
const int num_mgrs = dss_feat_get_num_mgrs();
int i;
int r;
+ int used_ovls, j;
bool mgr_busy[MAX_DSS_MANAGERS];
bool mgr_go[MAX_DSS_MANAGERS];
bool busy;
@@ -946,6 +1029,8 @@ static int configure_dispc(void)
if (r)
DSSERR("configure_overlay %d failed\n", i);
+ dss_ovl_configure_cb(&oc->cb, i, oc->enabled);
+
oc->dirty = false;
oc->shadow_dirty = true;
mgr_go[oc->channel] = true;
@@ -966,7 +1051,16 @@ static int configure_dispc(void)
continue;
}
+ for (j = used_ovls = 0; j < num_ovls; j++) {
+ oc = &dss_cache.overlay_cache[j];
+ if (oc->channel == i && oc->enabled)
+ used_ovls++;
+ }
+
configure_manager(i);
+
+ dss_ovl_configure_cb(&mc->cb, i, used_ovls);
+
mc->dirty = false;
mc->shadow_dirty = true;
mgr_go[i] = true;
@@ -1145,6 +1239,57 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev,
*hi = h;
}
+static void dss_completion_irq_handler(void *data, u32 mask)
+{
+ struct manager_cache_data *mc;
+ struct overlay_cache_data *oc;
+ const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache);
+ const int num_mgrs = MAX_DSS_MANAGERS;
+ const u32 masks[] = {
+ DISPC_IRQ_FRAMEDONE | DISPC_IRQ_VSYNC,
+ DISPC_IRQ_FRAMEDONE2 | DISPC_IRQ_VSYNC2,
+ /*DISPC_IRQ_FRAMEDONE_DIG |*/ DISPC_IRQ_EVSYNC_EVEN |
+ DISPC_IRQ_EVSYNC_ODD
+ };
+ int i;
+ bool notify = false;
+
+ spin_lock(&dss_cache.lock);
+
+ for (i = 0; i < num_mgrs; i++) {
+ mc = &dss_cache.manager_cache[i];
+ if (!(mask & masks[i]))
+ continue;
+
+ dss_ovl_cb(&mc->cb.dispc, i, DSS_COMPLETION_DISPLAYED);
+ mc->cb.dispc_displayed = true;
+ }
+
+ /* notify all overlays on that manager */
+ for (i = 0; i < num_ovls; i++) {
+ oc = &dss_cache.overlay_cache[i];
+ if (oc->enabled)
+ notify = true;
+
+ if (!(mask & masks[oc->channel]))
+ continue;
+
+ dss_ovl_cb(&oc->cb.dispc, i, DSS_COMPLETION_DISPLAYED);
+ oc->cb.dispc_displayed = true;
+ }
+
+ if (!notify) {
+ omap_dispc_unregister_isr(dss_completion_irq_handler, NULL,
+ DISPC_IRQ_FRAMEDONE | DISPC_IRQ_VSYNC |
+ DISPC_IRQ_FRAMEDONE2 | DISPC_IRQ_VSYNC2 |
+ /*DISPC_IRQ_FRAMEDONE_DIG |*/ DISPC_IRQ_EVSYNC_EVEN |
+ DISPC_IRQ_EVSYNC_ODD);
+ dss_cache.comp_irq_enabled = false;
+ }
+
+ spin_unlock(&dss_cache.lock);
+}
+
void dss_start_update(struct omap_dss_device *dssdev)
{
struct manager_cache_data *mc;
@@ -1153,14 +1298,22 @@ void dss_start_update(struct omap_dss_device *dssdev)
const int num_mgrs = dss_feat_get_num_mgrs();
struct omap_overlay_manager *mgr;
int i;
+ bool notify = false;
+ unsigned long flags;
mgr = dssdev->manager;
for (i = 0; i < num_ovls; ++i) {
oc = &dss_cache.overlay_cache[i];
+ notify |= oc->enabled;
+
if (oc->channel != mgr->id)
continue;
+ if (oc->shadow_dirty) {
+ dss_ovl_program_cb(&oc->cb, i);
+ oc->dispc_channel = oc->channel;
+ }
oc->shadow_dirty = false;
}
@@ -1169,9 +1322,29 @@ void dss_start_update(struct omap_dss_device *dssdev)
if (mgr->id != i)
continue;
+ if (mc->shadow_dirty)
+ dss_ovl_program_cb(&mc->cb, i);
mc->shadow_dirty = false;
}
+ spin_lock_irqsave(&dss_cache.lock, flags);
+ if (!dss_cache.comp_irq_enabled && notify) {
+ omap_dispc_register_isr(dss_completion_irq_handler, NULL,
+ DISPC_IRQ_FRAMEDONE | DISPC_IRQ_VSYNC |
+ DISPC_IRQ_FRAMEDONE2 | DISPC_IRQ_VSYNC2 |
+ /*DISPC_IRQ_FRAMEDONE_DIG |*/ DISPC_IRQ_EVSYNC_EVEN |
+ DISPC_IRQ_EVSYNC_ODD);
+ dss_cache.comp_irq_enabled = true;
+ } else if (dss_cache.comp_irq_enabled && !notify) {
+ omap_dispc_unregister_isr(dss_completion_irq_handler, NULL,
+ DISPC_IRQ_FRAMEDONE | DISPC_IRQ_VSYNC |
+ DISPC_IRQ_FRAMEDONE2 | DISPC_IRQ_VSYNC2 |
+ /*DISPC_IRQ_FRAMEDONE_DIG |*/ DISPC_IRQ_EVSYNC_EVEN |
+ DISPC_IRQ_EVSYNC_ODD);
+ dss_cache.comp_irq_enabled = false;
+ }
+ spin_unlock_irqrestore(&dss_cache.lock, flags);
+
dssdev->manager->enable(dssdev->manager);
}
@@ -1183,6 +1356,7 @@ static void dss_apply_irq_handler(void *data, u32 mask)
const int num_mgrs = dss_feat_get_num_mgrs();
int i, r;
bool mgr_busy[MAX_DSS_MANAGERS];
+ bool notify = false;
u32 irq_mask;
for (i = 0; i < num_mgrs; i++)
@@ -1192,14 +1366,37 @@ static void dss_apply_irq_handler(void *data, u32 mask)
for (i = 0; i < num_ovls; ++i) {
oc = &dss_cache.overlay_cache[i];
- if (!mgr_busy[oc->channel])
+ notify |= oc->enabled;
+
+ if (!mgr_busy[oc->channel] && oc->shadow_dirty) {
+ dss_ovl_program_cb(&oc->cb, i);
+ oc->dispc_channel = oc->channel;
oc->shadow_dirty = false;
+ }
}
for (i = 0; i < num_mgrs; ++i) {
mc = &dss_cache.manager_cache[i];
- if (!mgr_busy[i])
+ if (!mgr_busy[i] && mc->shadow_dirty) {
+ dss_ovl_program_cb(&mc->cb, i);
mc->shadow_dirty = false;
+ }
+ }
+
+ if (!dss_cache.comp_irq_enabled && notify) {
+ r = omap_dispc_register_isr(dss_completion_irq_handler, NULL,
+ DISPC_IRQ_FRAMEDONE | DISPC_IRQ_VSYNC |
+ DISPC_IRQ_FRAMEDONE2 | DISPC_IRQ_VSYNC2 |
+ /*DISPC_IRQ_FRAMEDONE_DIG |*/ DISPC_IRQ_EVSYNC_EVEN |
+ DISPC_IRQ_EVSYNC_ODD);
+ dss_cache.comp_irq_enabled = true;
+ } else if (dss_cache.comp_irq_enabled && !notify) {
+ omap_dispc_unregister_isr(dss_completion_irq_handler, NULL,
+ DISPC_IRQ_FRAMEDONE | DISPC_IRQ_VSYNC |
+ DISPC_IRQ_FRAMEDONE2 | DISPC_IRQ_VSYNC2 |
+ /*DISPC_IRQ_FRAMEDONE_DIG |*/ DISPC_IRQ_EVSYNC_EVEN |
+ DISPC_IRQ_EVSYNC_ODD);
+ dss_cache.comp_irq_enabled = false;
}
r = configure_dispc();
@@ -1283,6 +1480,17 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
continue;
}
+ /* complete unconfigured info in cache */
+ dss_ovl_cb(&oc->cb.cache, i,
+#if 0
+ (oc->cb.cache.fn == ovl->info.cb.fn &&
+ oc->cb.cache.data == ovl->info.cb.data) ?
+ DSS_COMPLETION_CHANGED_CACHE :
+#endif
+ DSS_COMPLETION_ECLIPSED_CACHE);
+ oc->cb.cache = ovl->info.cb;
+ ovl->info.cb.fn = NULL;
+
ovl->info_dirty = false;
oc->dirty = true;
@@ -1302,6 +1510,11 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
oc->out_height = ovl->info.out_height;
oc->global_alpha = ovl->info.global_alpha;
oc->pre_mult_alpha = ovl->info.pre_mult_alpha;
+ oc->zorder = ovl->info.zorder;
+ oc->min_x_decim = ovl->info.min_x_decim;
+ oc->max_x_decim = ovl->info.max_x_decim;
+ oc->min_y_decim = ovl->info.min_y_decim;
+ oc->max_y_decim = ovl->info.max_y_decim;
oc->replication =
dss_use_replication(dssdev, ovl->info.color_mode);
@@ -1342,6 +1555,17 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
dssdev = mgr->device;
+ /* complete unconfigured info in cache */
+ dss_ovl_cb(&mc->cb.cache, mgr->id,
+#if 0
+ (mc->cb.cache.fn == mgr->info.cb.fn &&
+ mc->cb.cache.data == mgr->info.cb.data) ?
+ DSS_COMPLETION_CHANGED_CACHE :
+#endif
+ DSS_COMPLETION_ECLIPSED_CACHE);
+ mc->cb.cache = mgr->info.cb;
+ mgr->info.cb.fn = NULL;
+
mgr->info_dirty = false;
mc->dirty = true;
@@ -1441,12 +1665,20 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
static int dss_check_manager(struct omap_overlay_manager *mgr)
{
- /* OMAP supports only graphics source transparency color key and alpha
- * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */
-
- if (mgr->info.alpha_enabled && mgr->info.trans_enabled &&
+ /* if we have OMAP3 alpha compatibility, alpha blending is always on */
+ if (dss_has_feature(FEAT_ALPHA_OMAP3_COMPAT)) {
+ if (!mgr->info.alpha_enabled)
+ return -EINVAL;
+ } else {
+ /*
+ * OMAP3- supports only graphics destination transparency
+ * color key and alpha blending simultaneously.
+ * See TRM 15.4.2.4.2.2 Alpha Mode.
+ */
+ if (mgr->info.alpha_enabled && mgr->info.trans_enabled &&
mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST)
- return -EINVAL;
+ return -EINVAL;
+ }
return 0;
}
@@ -1466,6 +1698,9 @@ static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr,
return r;
}
+ if (mgr->info_dirty)
+ dss_ovl_cb(&old_info.cb, mgr->id, DSS_COMPLETION_ECLIPSED_SET);
+
mgr->info_dirty = true;
return 0;
@@ -1511,6 +1746,10 @@ int dss_init_overlay_managers(struct platform_device *pdev)
BUG_ON(mgr == NULL);
+ /* alpha blending always on with OMAP3 alpha compatibility */
+ if (dss_has_feature(FEAT_ALPHA_OMAP3_COMPAT))
+ mgr->info.alpha_enabled = true;
+
switch (i) {
case 0:
mgr->name = "lcd";
diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c
index c84380c..2a5ce97 100644
--- a/drivers/video/omap2/dss/overlay.c
+++ b/drivers/video/omap2/dss/overlay.c
@@ -321,6 +321,118 @@ static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl,
return size;
}
+static ssize_t overlay_zorder_show(struct omap_overlay *ovl, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ ovl->info.zorder);
+}
+
+static ssize_t overlay_zorder_store(struct omap_overlay *ovl,
+ const char *buf, size_t size)
+{
+ int r;
+ struct omap_overlay_info info;
+
+ if (!dss_has_feature(FEAT_OVL_ZORDER))
+ return size;
+
+ ovl->get_overlay_info(ovl, &info);
+
+ info.zorder = simple_strtoul(buf, NULL, 10);
+
+ r = ovl->set_overlay_info(ovl, &info);
+ if (r)
+ return r;
+
+ if (ovl->manager) {
+ r = ovl->manager->apply(ovl->manager);
+ if (r)
+ return r;
+ }
+
+ return size;
+}
+
+static ssize_t overlay_decim_show(u16 min, u16 max, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d..%d\n", min, max);
+}
+
+static ssize_t overlay_x_decim_show(struct omap_overlay *ovl, char *buf)
+{
+ return overlay_decim_show(ovl->info.min_x_decim, ovl->info.max_x_decim,
+ buf);
+}
+
+static ssize_t overlay_y_decim_show(struct omap_overlay *ovl, char *buf)
+{
+ return overlay_decim_show(ovl->info.min_y_decim, ovl->info.max_y_decim,
+ buf);
+}
+
+static ssize_t overlay_decim_store(u16 *min, u16 *max,
+ const char *buf, size_t size)
+{
+ char *last;
+
+ *min = *max = simple_strtoul(buf, &last, 10);
+ if (last < buf + size && *last == '.') {
+ /* check for .. separator */
+ if (last + 2 >= buf + size || last[1] != '.')
+ return -EINVAL;
+
+ *max = simple_strtoul(last + 2, &last, 10);
+
+ /* fix order */
+ if (*max < *min)
+ swap(*min, *max);
+ }
+
+ /* decimation must be positive */
+ if (*min == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t overlay_x_decim_store(struct omap_overlay *ovl,
+ const char *buf, size_t size)
+{
+ int r;
+ struct omap_overlay_info info;
+
+ ovl->get_overlay_info(ovl, &info);
+
+ r = overlay_decim_store(&info.min_x_decim, &info.max_x_decim,
+ buf, size);
+
+ r = r ? : ovl->set_overlay_info(ovl, &info);
+
+ if (!r && ovl->manager)
+ r = ovl->manager->apply(ovl->manager);
+
+ return r ? : size;
+}
+
+static ssize_t overlay_y_decim_store(struct omap_overlay *ovl,
+ const char *buf, size_t size)
+{
+ int r;
+ struct omap_overlay_info info;
+
+ ovl->get_overlay_info(ovl, &info);
+
+ r = overlay_decim_store(&info.min_y_decim, &info.max_y_decim,
+ buf, size);
+
+ r = r ? : ovl->set_overlay_info(ovl, &info);
+
+ if (!r && ovl->manager)
+ r = ovl->manager->apply(ovl->manager);
+
+ return r ? : size;
+}
+
struct overlay_attribute {
struct attribute attr;
ssize_t (*show)(struct omap_overlay *, char *);
@@ -347,6 +459,12 @@ static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR,
static OVERLAY_ATTR(pre_mult_alpha, S_IRUGO|S_IWUSR,
overlay_pre_mult_alpha_show,
overlay_pre_mult_alpha_store);
+static OVERLAY_ATTR(x_decim, S_IRUGO|S_IWUSR,
+ overlay_x_decim_show, overlay_x_decim_store);
+static OVERLAY_ATTR(y_decim, S_IRUGO|S_IWUSR,
+ overlay_y_decim_show, overlay_y_decim_store);
+static OVERLAY_ATTR(zorder, S_IRUGO|S_IWUSR,
+ overlay_zorder_show, overlay_zorder_store);
static struct attribute *overlay_sysfs_attrs[] = {
&overlay_attr_name.attr,
@@ -358,6 +476,9 @@ static struct attribute *overlay_sysfs_attrs[] = {
&overlay_attr_enabled.attr,
&overlay_attr_global_alpha.attr,
&overlay_attr_pre_mult_alpha.attr,
+ &overlay_attr_zorder.attr,
+ &overlay_attr_x_decim.attr,
+ &overlay_attr_y_decim.attr,
NULL
};
@@ -462,6 +583,12 @@ int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev)
return -EINVAL;
}
+ if ((info->zorder < OMAP_DSS_OVL_ZORDER_0) ||
+ (info->zorder > OMAP_DSS_OVL_ZORDER_3)) {
+ DSSERR("overlay doesn't support zorder %d\n", info->zorder);
+ return -EINVAL;
+ }
+
return 0;
}
@@ -482,6 +609,14 @@ static int dss_ovl_set_overlay_info(struct omap_overlay *ovl,
}
}
+ /* complete previous settings */
+ if (ovl->info_dirty)
+ dss_ovl_cb(&old_info.cb, ovl->id,
+ (info->cb.fn == old_info.cb.fn &&
+ info->cb.data == old_info.cb.data) ?
+ DSS_COMPLETION_CHANGED_SET :
+ DSS_COMPLETION_ECLIPSED_SET);
+
ovl->info_dirty = true;
return 0;
@@ -620,6 +755,7 @@ void dss_init_overlays(struct platform_device *pdev)
ovl->id = OMAP_DSS_GFX;
ovl->caps = OMAP_DSS_OVL_CAP_DISPC;
ovl->info.global_alpha = 255;
+ ovl->info.zorder = OMAP_DSS_OVL_ZORDER_0;
break;
case 1:
ovl->name = "vid1";
@@ -627,6 +763,9 @@ void dss_init_overlays(struct platform_device *pdev)
ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
OMAP_DSS_OVL_CAP_DISPC;
ovl->info.global_alpha = 255;
+ ovl->info.zorder = dss_has_feature(FEAT_OVL_ZORDER) ?
+ OMAP_DSS_OVL_ZORDER_3 :
+ OMAP_DSS_OVL_ZORDER_0;
break;
case 2:
ovl->name = "vid2";
@@ -634,9 +773,27 @@ void dss_init_overlays(struct platform_device *pdev)
ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
OMAP_DSS_OVL_CAP_DISPC;
ovl->info.global_alpha = 255;
+ ovl->info.zorder = dss_has_feature(FEAT_OVL_ZORDER) ?
+ OMAP_DSS_OVL_ZORDER_2 :
+ OMAP_DSS_OVL_ZORDER_0;
+ break;
+ case 3:
+ ovl->name = "vid3";
+ ovl->id = OMAP_DSS_VIDEO3;
+ ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
+ OMAP_DSS_OVL_CAP_DISPC;
+ ovl->info.global_alpha = 255;
+ ovl->info.zorder = dss_has_feature(FEAT_OVL_ZORDER) ?
+ OMAP_DSS_OVL_ZORDER_1 :
+ OMAP_DSS_OVL_ZORDER_0;
break;
+
}
+ ovl->info.min_x_decim = ovl->info.min_y_decim = 1;
+ ovl->info.max_x_decim = ovl->info.max_y_decim =
+ cpu_is_omap44xx() ? 16 : 1;
+
ovl->set_manager = &omap_dss_set_manager;
ovl->unset_manager = &omap_dss_unset_manager;
ovl->set_overlay_info = &dss_ovl_set_overlay_info;
diff --git a/drivers/video/omap2/dsscomp/Kconfig b/drivers/video/omap2/dsscomp/Kconfig
new file mode 100644
index 0000000..f3b6f64
--- /dev/null
+++ b/drivers/video/omap2/dsscomp/Kconfig
@@ -0,0 +1,9 @@
+menuconfig DSSCOMP
+ tristate "OMAP DSS Composition support (EXPERIMENTAL)"
+ depends on OMAP2_DSS
+ default y
+
+ 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 framebuffer.
diff --git a/drivers/video/omap2/dsscomp/Makefile b/drivers/video/omap2/dsscomp/Makefile
new file mode 100644
index 0000000..705e36e
--- /dev/null
+++ b/drivers/video/omap2/dsscomp/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_DSSCOMP) += dsscomp.o
+dsscomp-y := device.o base.o queue.o
diff --git a/drivers/video/omap2/dsscomp/base.c b/drivers/video/omap2/dsscomp/base.c
new file mode 100644
index 0000000..0d0e616
--- /dev/null
+++ b/drivers/video/omap2/dsscomp/base.c
@@ -0,0 +1,442 @@
+/*
+ * 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 <mach/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);
+ BUG_ON(!ci);
+
+ return ci->a_bt + ci->r_bt + ci->g_bt + ci->b_bt + ci->x_bt;
+}
+
+union rect {
+ struct {
+ s32 x;
+ s32 y;
+ s32 w;
+ s32 h;
+ };
+ struct {
+ s32 xy[2];
+ s32 wh[2];
+ };
+ struct dss2_rect_t r;
+};
+
+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;
+
+ /* 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);
+
+ info.enabled = cfg->enabled;
+ 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->device->panel.timings.x_res;
+ vis.h = ovl->manager->device->panel.timings.y_res;
+
+ if (crop_to_rect(&crop, &win, &vis, cfg->rotation, cfg->mirror) ||
+ vis.w < 2) {
+ info.enabled = false;
+ 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;
+ info.vaddr = NULL;
+
+ /* check for TILER 2D buffer */
+ if (info.paddr >= 0x60000000 && info.paddr < 0x78000000) {
+ int bpp = 1 << ((info.paddr >> 27) & 3);
+ 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, info.width, info.height);
+ info.paddr -= t.tsptr;
+ tilview_crop(&t, 0, crop.y, info.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,
+ info.width >> 1, info.height >> 1);
+ info.p_uv_addr -= t.tsptr;
+ tilview_crop(&t, 0, crop.y >> 1, info.width,
+ 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);
+ 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
+
+ /* :TODO: copy color conversion - this needs ovl support */
+
+done:
+#if 0
+ pr_debug("ovl%d: en=%d %x/%x (%dx%d|%d) => (%dx%d) @ (%d,%d) rot=%d "
+ "mir=%d col=%x z=%d al=%02x prem=%d pich=%d ilace=%d\n",
+ ovl->id, info.enabled, info.paddr, info.p_uv_addr, info.width,
+ info.height, info.screen_width, info.out_width, info.out_height,
+ info.pos_x, info.pos_y, info.rotation, info.mirror,
+ info.color_mode, info.zorder, info.global_alpha,
+ info.pre_mult_alpha, info.pic_height, info.field);
+#else
+ pr_debug("ovl%d: en=%d %x/%x (%dx%d|%d) => (%dx%d) @ (%d,%d) rot=%d "
+ "mir=%d col=%x z=%d al=%02x prem=%d\n",
+ ovl->id, info.enabled, info.paddr, info.p_uv_addr, info.width,
+ info.height, info.screen_width, info.out_width, info.out_height,
+ info.pos_x, info.pos_y, info.rotation, info.mirror,
+ info.color_mode, info.zorder, info.global_alpha,
+ info.pre_mult_alpha);
+#endif
+ /* set overlay info */
+ return ovl->set_overlay_info(ovl, &info);
+}
+
+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->device && !strcmp(name, dev_name(&mgr->device->dev)))
+ return mgr;
+ }
+ return NULL;
+}
+
+int set_dss_mgr_info(struct dss2_mgr_info *mi)
+{
+ 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);
+
+ info.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;
+
+ return mgr->set_manager_info(mgr, &info);
+}
+
+/*
+ * ===========================================================================
+ * 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 z%d %s%s *%d%% %d*%d:%d,%d+%d,%d rot%d%s"
+ " => %d,%d+%d,%d %p/%p|%d)\n",
+ c->ix, c->enabled ? "ON" : "off", c->zorder,
+ ci->name ? : "(none)",
+ c->pre_mult_alpha ? " premult" : "",
+ (c->global_alpha * 100 + 128) / 255,
+ c->width, c->height, c->crop.x, c->crop.y,
+ c->crop.w, c->crop.h,
+ c->rotation, c->mirror ? "+mir" : "",
+ c->win.x, c->win.y, c->win.w, c->win.h,
+ (void *) oi->ba, (void *) oi->uv, c->stride);
+}
+
+void dump_comp_info(struct dsscomp_dev *cdev, struct dsscomp_setup_mgr_data *d,
+ const char *phase)
+{
+ struct dss2_mgr_info *mi = &d->mgr;
+
+ if (!(debug & DEBUG_COMPOSITIONS))
+ return;
+
+ dev_info(DEV(cdev), "[%08x] %s: %c%c%c"
+ "(dis%d(%s) alpha=%d col=%08x ilace=%d n=%d)\n",
+ d->sync_id, phase,
+ (d->mode & DSSCOMP_SETUP_MODE_APPLY) ? 'A' : '-',
+ (d->mode & DSSCOMP_SETUP_MODE_DISPLAY) ? 'D' : '-',
+ (d->mode & DSSCOMP_SETUP_MODE_CAPTURE) ? 'C' : '-',
+ 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,
+ d->num_ovls);
+}
diff --git a/drivers/video/omap2/dsscomp/device.c b/drivers/video/omap2/dsscomp/device.c
new file mode 100644
index 0000000..20235a8
--- /dev/null
+++ b/drivers/video/omap2/dsscomp/device.c
@@ -0,0 +1,369 @@
+/*
+ * 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/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+
+#define MODULE_NAME "dsscomp"
+
+#include <video/omapdss.h>
+#include <video/dsscomp.h>
+#include <plat/dsscomp.h>
+#include "dsscomp.h"
+
+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(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;
+}
+
+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;
+ dsscomp_t comp;
+
+ 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->manager;
+ if (!mgr)
+ return -ENODEV;
+
+ comp = dsscomp_new_sync_id(mgr, d->sync_id);
+ if (IS_ERR(comp))
+ return PTR_ERR(comp);
+
+ 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;
+
+ /* convert addresses to user space */
+ if (oi->cfg.color_mode == OMAP_DSS_COLOR_NV12)
+ 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);
+ if (r)
+ dsscomp_drop(comp);
+ else if (d->mode & DSSCOMP_SETUP_APPLY)
+ r = dsscomp_apply(comp);
+
+ 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;
+ int i;
+
+ /* get display */
+ if (dis->ix >= cdev->num_displays)
+ return -EINVAL;
+ dev = cdev->displays[dis->ix];
+ if (!dev)
+ return -EINVAL;
+ mgr = dev->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;
+
+ /* 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]->info.enabled)
+ dis->overlays_available |= 1 << i;
+ }
+ dis->overlays_available |= dis->overlays_owned;
+
+ /* fill out manager information */
+ if (mgr) {
+ dis->mgr.alpha_blending = mgr->info.alpha_enabled;
+ dis->mgr.default_color = mgr->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 = mgr->info.trans_enabled;
+ dis->mgr.trans_key = mgr->info.trans_key;
+ dis->mgr.trans_key_type = mgr->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;
+
+ return 0;
+}
+
+static long check_ovl(struct dsscomp_dev *cdev,
+ struct dsscomp_check_ovl_data *chk)
+{
+ /* for now return all overlays as possible */
+ return (1 << cdev->num_ovls) - 1;
+}
+
+static long wait(struct dsscomp_dev *cdev, struct dsscomp_wait_data *wd)
+{
+ struct omap_overlay_manager *mgr;
+ dsscomp_t comp;
+
+ /* get manager */
+ if (wd->ix >= cdev->num_displays || !cdev->displays[wd->ix])
+ return -EINVAL;
+ mgr = cdev->displays[wd->ix]->manager;
+ if (!mgr)
+ return -ENODEV;
+
+ /* get composition */
+ comp = dsscomp_find(mgr, wd->sync_id);
+ if (IS_ERR(comp))
+ return 0;
+
+ return dsscomp_wait(comp, wd->phase, usecs_to_jiffies(wd->timeout_us));
+}
+
+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);
+ }
+ dev_info(DEV(cdev), "found %d displays and %d overlays\n",
+ cdev->num_displays, cdev->num_ovls);
+}
+
+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;
+
+ switch (cmd) {
+ case DSSCOMP_SETUP_MGR:
+ {
+ struct {
+ struct dsscomp_setup_mgr_data set;
+ struct dss2_ovl_info ovl[MAX_OVERLAYS];
+ } p;
+
+ r = copy_from_user(&p.set, ptr, sizeof(p.set)) ? :
+ p.set.num_ovls >= ARRAY_SIZE(p.ovl) ? -EINVAL :
+ copy_from_user(&p.ovl, (void __user *)arg + sizeof(p.set),
+ sizeof(*p.ovl) * p.set.num_ovls) ? :
+ setup_mgr(cdev, &p.set);
+ break;
+ }
+ case DSSCOMP_QUERY_DISPLAY:
+ {
+ struct dsscomp_display_info dis;
+ r = copy_from_user(&dis, ptr, sizeof(dis)) ? :
+ query_display(cdev, &dis) ? :
+ copy_to_user(ptr, &dis, sizeof(dis));
+ break;
+ }
+ case DSSCOMP_CHECK_OVL:
+ {
+ struct dsscomp_check_ovl_data chk;
+ r = copy_from_user(&chk, ptr, sizeof(chk)) ? :
+ check_ovl(cdev, &chk);
+ break;
+ }
+ case DSSCOMP_WAIT:
+ {
+ struct dsscomp_wait_data wd;
+ r = copy_from_user(&wd, ptr, sizeof(wd)) ? :
+ wait(cdev, &wd);
+ break;
+ }
+ 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_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");
+
+ platform_set_drvdata(pdev, cdev);
+
+ fill_cache(cdev);
+
+ /* initialize queues */
+ dsscomp_queue_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();
+ kfree(cdev);
+
+ return 0;
+}
+
+static struct platform_driver dsscomp_pdriver = {
+ .probe = dsscomp_probe,
+ .remove = dsscomp_remove,
+ .driver = { .name = MODULE_NAME, .owner = THIS_MODULE }
+};
+
+static struct platform_device dsscomp_pdev = {
+ .name = MODULE_NAME,
+ .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);
+}
+
+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
new file mode 100644
index 0000000..baf8034
--- /dev/null
+++ b/drivers/video/omap2/dsscomp/dsscomp.h
@@ -0,0 +1,83 @@
+/*
+ * 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>
+
+#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)
+
+/*
+ * 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];
+};
+
+extern int debug;
+
+/*
+ * Kernel interface
+ */
+int dsscomp_queue_init(struct dsscomp_dev *cdev);
+void dsscomp_queue_exit(void);
+
+/* 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 omap_overlay_manager *find_dss_mgr(int display_ix);
+
+/*
+ * 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);
+
+#endif
diff --git a/drivers/video/omap2/dsscomp/queue.c b/drivers/video/omap2/dsscomp/queue.c
new file mode 100644
index 0000000..ade17a0
--- /dev/null
+++ b/drivers/video/omap2/dsscomp/queue.c
@@ -0,0 +1,887 @@
+/*
+ * 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 <video/omapdss.h>
+#include <video/dsscomp.h>
+#include <plat/dsscomp.h>
+
+#include "dsscomp.h"
+
+/* queue state */
+static DEFINE_MUTEX(mtx);
+
+/* free overlay structs */
+static LIST_HEAD(free_ois);
+
+#define QUEUE_SIZE 3
+
+#define MUTEXED(exp) ({ typeof(exp) __r;\
+ mutex_lock(&mtx); __r = (exp); mutex_unlock(&mtx); __r; })
+
+#undef STRICT_CHECK
+
+#define MAGIC_ACTIVE 0xAC54156E
+#define MAGIC_APPLIED 0xA50504C1
+#define MAGIC_PROGRAMMED 0x50520652
+#define MAGIC_DISPLAYED 0xD15504CA
+
+static struct dsscomp_data {
+ u32 magic;
+ struct dsscomp_setup_mgr_data frm;
+ /*
+ * :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 list_head q;
+ struct list_head ois;
+ struct omapdss_ovl_cb cb;
+} *cis;
+
+static struct dss2_overlay {
+ struct dss2_ovl_info ovl;
+ struct list_head q;
+} *ois;
+
+static struct {
+ struct list_head q_ci; /* compositions */
+ struct list_head free_cis; /* free composition structs */
+
+ wait_queue_head_t wq;
+
+ u32 ovl_mask; /* overlays used on this display */
+ u32 ovl_qmask; /* overlays queued to this display */
+} mgrq[MAX_MANAGERS];
+
+static struct dsscomp_dev *cdev;
+
+/*
+ * ===========================================================================
+ * EXIT
+ * ===========================================================================
+ */
+
+/* Initialize queue structures, and set up state of the displays */
+int dsscomp_queue_init(struct dsscomp_dev *cdev_)
+{
+ u32 i, j, ncis, nois;
+
+ cdev = cdev_;
+ ncis = QUEUE_SIZE * cdev->num_mgrs;
+ nois = QUEUE_SIZE * cdev->num_ovls;
+
+ INIT_LIST_HEAD(&free_ois);
+
+ cis = vmalloc(sizeof(*cis) * ncis);
+ ois = vmalloc(sizeof(*ois) * nois);
+
+ if (cis && ois && ARRAY_SIZE(mgrq) >= cdev->num_mgrs) {
+ ZERO(mgrq);
+ ZEROn(cis, ncis);
+ ZEROn(ois, nois);
+
+ for (i = 0; i < nois; i++)
+ list_add(&ois[i].q, &free_ois);
+ for (i = 0; i < cdev->num_mgrs; i++) {
+ struct omap_overlay_manager *mgr;
+ INIT_LIST_HEAD(&mgrq[i].q_ci);
+ INIT_LIST_HEAD(&mgrq[i].free_cis);
+ init_waitqueue_head(&mgrq[i].wq);
+
+ for (j = 0; j < QUEUE_SIZE; j++)
+ list_add(&cis[j + QUEUE_SIZE * i].q,
+ &mgrq[i].free_cis);
+
+ /* record overlays on this display */
+ mgr = cdev->mgrs[i];
+ for (j = 0; j < cdev->num_ovls; j++) {
+ if (cdev->ovls[j]->info.enabled &&
+ mgr &&
+ cdev->ovls[j]->manager == mgr)
+ mgrq[i].ovl_mask |= 1 << j;
+ }
+ }
+ return 0;
+ } else {
+ vfree(cis);
+ vfree(ois);
+ return -ENOMEM;
+ }
+}
+
+/* returns if composition is valid and active */
+static inline struct dsscomp_data *validate(struct dsscomp_data *comp)
+{
+#ifdef STRICT_CHECK
+ u32 ix, q_ix;
+ struct dsscomp_data *c;
+ struct omap_overlay_manager *mgr;
+
+ if (!comp)
+ return ERR_PTR(-EFAULT);
+
+ ix = comp->frm.mgr.ix;
+ if (ix >= cdev->num_displays || !cdev->displays[ix])
+ return ERR_PTR(-EINVAL);
+ mgr = cdev->displays[ix]->manager;
+ if (!mgr || mgr->id >= cdev->num_mgrs)
+ return ERR_PTR(-ENODEV);
+
+ /* check if composition is active */
+ list_for_each_entry(c, &mgrq[mgr->id].q_ci, q)
+ if (c == comp)
+ return c;
+ return ERR_PTR(-ESRCH);
+#else
+ if (!comp)
+ return ERR_PTR(-EFAULT);
+
+ return (comp->magic == MAGIC_PROGRAMMED ||
+ comp->magic == MAGIC_DISPLAYED ||
+ comp->magic == MAGIC_APPLIED ||
+ comp->magic == MAGIC_ACTIVE) ? comp : ERR_PTR(-ESRCH);
+#endif
+}
+
+
+/* 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->device)
+ return cdev->num_displays;
+
+ /* find manager's display */
+ for (i = 0; i < cdev->num_displays; i++)
+ if (cdev->displays[i] == mgr->device)
+ break;
+
+ return i;
+}
+
+/*
+ * ===========================================================================
+ * QUEUING SETUP OPERATIONS
+ * ===========================================================================
+ */
+
+/* get composition by sync_id */
+static dsscomp_t dsscomp_get(struct omap_overlay_manager *mgr, u32 sync_id)
+{
+ struct dsscomp_data *comp;
+
+ /* get display index */
+ u32 ix = mgr ? mgr->id : cdev->num_mgrs;
+ if (ix >= cdev->num_mgrs)
+ return NULL;
+
+ /* find composition with sync id on manager */
+ list_for_each_entry(comp, &mgrq[ix].q_ci, q)
+ if (comp->frm.sync_id == sync_id)
+ return comp;
+
+ return NULL;
+}
+
+/* create a new composition for a display */
+dsscomp_t dsscomp_new_sync_id(struct omap_overlay_manager *mgr, u32 sync_id)
+{
+ struct dsscomp_data *comp = NULL;
+ int r;
+ 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);
+
+ /* see if sync_id exists */
+ mutex_lock(&mtx);
+ if (dsscomp_get(mgr, sync_id)) {
+ r = -EEXIST;
+ goto done;
+ }
+
+ /* check if there is space on the queue */
+ if (list_empty(&mgrq[ix].free_cis)) {
+ /* discard earliest unapplied frame */
+ list_for_each_entry(comp, &mgrq[ix].q_ci, q) {
+ if (comp->magic == MAGIC_ACTIVE)
+ break;
+ }
+ /* fail if we have not found one */
+ if (&comp->q == &mgrq[ix].q_ci) {
+ r = -EBUSY;
+ goto done;
+ }
+ dsscomp_drop(comp);
+ }
+
+ /* initialize new composition */
+ comp = list_first_entry(&mgrq[ix].free_cis, typeof(*comp), q);
+ list_move_tail(&comp->q, &mgrq[ix].q_ci);
+ comp->ix = ix; /* save where this composition came from */
+ ZERO(comp->frm);
+ INIT_LIST_HEAD(&comp->ois);
+ comp->ovl_mask = comp->ovl_dmask = 0;
+ comp->frm.sync_id = sync_id;
+ comp->frm.mgr.ix = display_ix;
+ ZERO(comp->cb);
+
+ /* :TODO: retrieve last manager configuration */
+
+ comp->magic = MAGIC_ACTIVE;
+ r = 0;
+ done:
+ mutex_unlock(&mtx);
+ return r ? ERR_PTR(r) : comp;
+}
+EXPORT_SYMBOL(dsscomp_new_sync_id);
+
+dsscomp_t dsscomp_find(struct omap_overlay_manager *mgr, u32 sync_id)
+{
+ struct dsscomp_data *comp;
+
+ /* check manager */
+ if (!mgr || mgr->id >= cdev->num_mgrs)
+ return ERR_PTR(-EINVAL);
+
+ /* see if sync_id exists */
+ mutex_lock(&mtx);
+ comp = dsscomp_get(mgr, sync_id);
+ if (IS_ERR(comp))
+ comp = NULL;
+ mutex_unlock(&mtx);
+ return comp;
+}
+EXPORT_SYMBOL(dsscomp_find);
+
+/* find first unapplied sync_id or 0 if none found */
+u32 dsscomp_first_sync_id(struct omap_overlay_manager *mgr)
+{
+ struct dsscomp_data *comp;
+ u32 sync_id = 0;
+
+ /* get display index */
+ u32 ix = mgr ? mgr->id : cdev->num_mgrs;
+ if (ix >= cdev->num_mgrs)
+ return 0;
+
+ /* find first unapplied composition */
+ mutex_lock(&mtx);
+ list_for_each_entry(comp, &mgrq[ix].q_ci, q) {
+ if (comp->magic == MAGIC_ACTIVE &&
+ (DSSCOMP_SETUP_MODE_APPLY & ~comp->frm.mode)) {
+ sync_id = comp->frm.sync_id;
+ break;
+ }
+ }
+ mutex_unlock(&mtx);
+
+ return sync_id;
+}
+EXPORT_SYMBOL(dsscomp_first_sync_id);
+
+/* returns overlays used in a composition */
+u32 dsscomp_get_ovls(dsscomp_t comp)
+{
+ BUG_ON(MUTEXED(IS_ERR(validate(comp))));
+
+ return comp->ovl_mask;
+}
+EXPORT_SYMBOL(dsscomp_get_ovls);
+
+/* set overlay info */
+int dsscomp_set_ovl(dsscomp_t comp, struct dss2_ovl_info *ovl)
+{
+ int r = -EFAULT;
+
+ if (comp && ovl) {
+ u32 i, mask = 1 << ovl->cfg.ix;
+ struct omap_overlay *o;
+ struct dss2_overlay *oi;
+ u32 ix = comp->frm.mgr.ix;
+ if (ix < cdev->num_displays &&
+ cdev->displays[ix] &&
+ cdev->displays[ix]->manager)
+ ix = cdev->displays[ix]->manager->id;
+ else
+ ix = cdev->num_mgrs;
+ if (ix >= cdev->num_mgrs)
+ return -ENODEV;
+
+ mutex_lock(&mtx);
+
+ /* check if composition is active */
+ comp = validate(comp);
+ if (IS_ERR(comp)) {
+ r = PTR_ERR(comp);
+ goto done;
+ }
+
+ if (comp->magic != MAGIC_ACTIVE) {
+ r = -EACCES;
+ goto done;
+ }
+
+ if (ovl->cfg.ix >= cdev->num_ovls) {
+ r = -EINVAL;
+ goto done;
+ }
+
+ /* if overlay is already part of the composition */
+ if (mask & comp->ovl_mask) {
+ /* look up overlay */
+ list_for_each_entry(oi, &comp->ois, q)
+ if (oi->ovl.cfg.ix == ovl->cfg.ix)
+ break;
+ BUG_ON(&oi->q == &comp->ois);
+ } else {
+ /* check if ovl is free to use */
+ r = -EBUSY;
+ if (list_empty(&free_ois))
+ goto done;
+
+ /* not in any other displays queue */
+ if (mask & ~mgrq[ix].ovl_qmask) {
+ for (i = 0; i < cdev->num_mgrs; i++) {
+
+ if (i == ix)
+ continue;
+ if (mgrq[i].ovl_qmask & mask)
+ goto done;
+ }
+ }
+
+ /* and disabled (unless forced) if on another manager */
+ o = cdev->ovls[ovl->cfg.ix];
+ if (o->info.enabled &&
+ (!o->manager || o->manager->id != ix))
+ goto done;
+
+ /* add overlay to composition & display */
+ comp->ovl_mask |= mask;
+ comp->frm.num_ovls++;
+ mgrq[ix].ovl_qmask |= mask;
+
+ oi = list_first_entry(&free_ois, typeof(*oi), q);
+ list_move(&oi->q, &comp->ois);
+ }
+
+ oi->ovl = *ovl;
+ r = 0;
+ done:
+ mutex_unlock(&mtx);
+ }
+
+ return r;
+}
+EXPORT_SYMBOL(dsscomp_set_ovl);
+
+/* get overlay info */
+int dsscomp_get_ovl(dsscomp_t comp, u32 ix, struct dss2_ovl_info *ovl)
+{
+ int r = -EFAULT;
+ struct dss2_overlay *oi;
+
+ if (comp && ovl) {
+ mutex_lock(&mtx);
+
+ /* check if composition is active */
+ comp = validate(comp);
+ if (IS_ERR(comp)) {
+ r = PTR_ERR(comp);
+ } else if (comp->magic != MAGIC_ACTIVE) {
+ r = -EACCES;
+ } else {
+ if (ix >= cdev->num_ovls) {
+ r = -EINVAL;
+ } else if (comp->ovl_mask & (1 << ix)) {
+ r = 0;
+ list_for_each_entry(oi, &comp->ois, q) {
+ if (oi->ovl.cfg.ix == ix) {
+ *ovl = oi->ovl;
+ break;
+ }
+ }
+ BUG_ON(&oi->q == &comp->ois);
+ } else {
+ /* :TODO: get past overlay info */
+ r = -ENOENT;
+ }
+ }
+
+ mutex_unlock(&mtx);
+ }
+
+ return r;
+}
+EXPORT_SYMBOL(dsscomp_get_ovl);
+
+/* set manager info */
+int dsscomp_set_mgr(dsscomp_t comp, struct dss2_mgr_info *mgr)
+{
+ int r = -EFAULT;
+
+ if (comp && mgr) {
+ mutex_lock(&mtx);
+
+ /* check if composition is active */
+ comp = validate(comp);
+ if (IS_ERR(comp)) {
+ r = PTR_ERR(comp);
+ goto done;
+ }
+
+ if (comp->magic != MAGIC_ACTIVE) {
+ r = -EACCES;
+ goto done;
+ }
+
+ /* set display index in manager info */
+ mgr->ix = comp->frm.mgr.ix;
+ comp->frm.mgr = *mgr;
+ r = 0;
+ done:
+ mutex_unlock(&mtx);
+ }
+
+ return r;
+}
+EXPORT_SYMBOL(dsscomp_set_mgr);
+
+/* get manager info */
+int dsscomp_get_mgr(dsscomp_t comp, struct dss2_mgr_info *mgr)
+{
+ int r = -EFAULT;
+
+ if (comp && mgr) {
+ mutex_lock(&mtx);
+
+ /* check if composition is active */
+ comp = validate(comp);
+ if (IS_ERR(comp)) {
+ r = PTR_ERR(comp);
+ } else if (comp->magic != MAGIC_ACTIVE) {
+ r = -EACCES;
+ } else {
+ r = 0;
+ *mgr = comp->frm.mgr;
+ }
+
+ mutex_unlock(&mtx);
+ }
+
+ return r;
+}
+EXPORT_SYMBOL(dsscomp_get_mgr);
+
+/* get manager info */
+int dsscomp_setup(dsscomp_t comp, enum dsscomp_setup_mode mode,
+ struct dss2_rect_t win)
+{
+ int r = -EFAULT;
+
+ if (comp) {
+ mutex_lock(&mtx);
+
+ /* check if composition is active */
+ comp = validate(comp);
+ if (IS_ERR(comp)) {
+ r = PTR_ERR(comp);
+ } else if (comp->magic != MAGIC_ACTIVE) {
+ r = -EACCES;
+ } else {
+ r = 0;
+ comp->frm.mode = mode;
+ comp->frm.win = win;
+ }
+
+ mutex_unlock(&mtx);
+ }
+
+ return r;
+}
+EXPORT_SYMBOL(dsscomp_setup);
+
+/*
+ * ===========================================================================
+ * QUEUING COMMITTING OPERATIONS
+ * ===========================================================================
+ */
+
+static void refresh_masks(u32 ix)
+{
+ struct dsscomp_data *c;
+
+ mgrq[ix].ovl_qmask = mgrq[ix].ovl_mask;
+ list_for_each_entry(c, &mgrq[ix].q_ci, q) {
+ if (c->magic != MAGIC_PROGRAMMED)
+ mgrq[ix].ovl_qmask |= c->ovl_mask;
+ }
+
+ wake_up_interruptible_sync(&mgrq[ix].wq);
+}
+
+void dsscomp_drop(dsscomp_t c)
+{
+ struct dss2_overlay *o, *o2;
+
+ if (debug & DEBUG_COMPOSITIONS)
+ dev_info(DEV(cdev), "[%08x] released\n", c->frm.sync_id);
+
+ list_for_each_entry_safe(o, o2, &c->ois, q)
+ list_move(&o->q, &free_ois);
+ list_move(&c->q, &mgrq[c->ix].free_cis);
+ c->magic = 0;
+}
+EXPORT_SYMBOL(dsscomp_drop);
+
+static void dsscomp_mgr_callback(void *data, int id, int status)
+{
+ struct dsscomp_data *comp = data;
+ u32 ix;
+
+ /* do any other callbacks */
+ if (comp->cb.fn)
+ comp->cb.fn(comp->cb.data, id, status);
+
+ /* verify validity */
+ comp = validate(comp);
+ if (IS_ERR(comp))
+ return;
+
+ ix = comp->frm.mgr.ix;
+ if (ix >= cdev->num_displays ||
+ !cdev->displays[ix] ||
+ !cdev->displays[ix]->manager)
+ return;
+ ix = cdev->displays[ix]->manager->id;
+ if (ix >= cdev->num_mgrs)
+ return;
+
+ /* handle programming & release */
+ if (status == DSS_COMPLETION_PROGRAMMED) {
+ comp->magic = MAGIC_PROGRAMMED;
+ if (debug & DEBUG_PHASES)
+ dev_info(DEV(cdev),
+ "[%08x] programmed\n", comp->frm.sync_id);
+
+ /* update used overlay mask */
+ mgrq[ix].ovl_mask = comp->ovl_mask & ~comp->ovl_dmask;
+
+ /* if all overlays were disabled, the composition is complete */
+ if (comp->blank)
+ dsscomp_drop(comp);
+ refresh_masks(ix);
+ } else if ((status & DSS_COMPLETION_DISPLAYED) &&
+ comp->magic == MAGIC_PROGRAMMED) {
+ /* composition is 1st displayed */
+ comp->magic = MAGIC_DISPLAYED;
+ if (debug & DEBUG_PHASES)
+ dev_info(DEV(cdev),
+ "[%08x] displayed\n", comp->frm.sync_id);
+ wake_up_interruptible_sync(&mgrq[ix].wq);
+ } else if (status & DSS_COMPLETION_RELEASED) {
+ /* composition is no longer displayed */
+ dsscomp_drop(comp);
+ refresh_masks(ix);
+ }
+}
+
+static inline bool dssdev_manually_updated(struct omap_dss_device *dev)
+{
+ return dev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
+ dev->driver->get_update_mode(dev) != OMAP_DSS_UPDATE_AUTO;
+}
+
+/* get manager info */
+int dsscomp_apply(dsscomp_t 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;
+ struct dsscomp_data *c, *c2;
+ struct dss2_overlay *o, *o2;
+ bool change = false;
+
+ mutex_lock(&mtx);
+
+ /* check if composition is active */
+ comp = validate(comp);
+ if (IS_ERR(comp)) {
+ r = PTR_ERR(comp);
+ goto done;
+ }
+
+ if (comp->magic != MAGIC_ACTIVE) {
+ r = -EACCES;
+ goto done;
+ }
+
+ /* 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->manager;
+ if (!mgr || !drv || mgr->id >= cdev->num_mgrs)
+ goto done;
+
+ /* skip all unapplied prior compositions */
+ list_for_each_entry_safe(c, c2, &mgrq[mgr->id].q_ci, q) {
+ if (c == comp)
+ break;
+
+ /* keep applied compositions, as callback has been scheduled */
+ if (c->magic == MAGIC_ACTIVE) {
+ dsscomp_drop(c);
+ change = true;
+ }
+ }
+
+ dump_comp_info(cdev, d, "apply");
+
+ r = 0;
+ dmask = 0;
+ list_for_each_entry_safe(o, o2, &comp->ois, q) {
+ struct dss2_ovl_info *oi = &o->ovl;
+
+ /* keep track of disabled overlays */
+ if (!oi->cfg.enabled)
+ dmask |= 1 << oi->cfg.ix;
+
+ if (r)
+ goto done_ovl;
+
+ dump_ovl_info(cdev, oi);
+
+ if (oi->cfg.ix >= cdev->num_ovls) {
+ r = -EINVAL;
+ goto done_ovl;
+ }
+ ovl = cdev->ovls[oi->cfg.ix];
+
+ /* set overlays' manager & info */
+ if (ovl->info.enabled && ovl->manager != mgr) {
+ r = -EBUSY;
+ goto done_ovl;
+ }
+ if (ovl->manager != mgr) {
+ /*
+ * Ideally, we should call ovl->unset_manager(ovl),
+ * but it may block on go even though the disabling
+ * of the overlay already went through. So instead,
+ * we are just clearing the manager.
+ */
+ ovl->manager = NULL;
+ r = ovl->set_manager(ovl, mgr);
+ if (r)
+ goto done_ovl;
+ }
+
+ r = set_dss_ovl_info(oi);
+done_ovl:
+ /* we no longer have use for the overlay info structs */
+ list_move(&o->q, &free_ois);
+ }
+
+ /*
+ * 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.
+ */
+ r = r ? : set_dss_mgr_info(&d->mgr);
+ if (r) {
+ dev_err(DEV(cdev), "[%08x] set failed %d\n", d->sync_id, r);
+ dsscomp_drop(comp);
+ change = true;
+ goto done;
+ } else {
+ /* override manager's callback to avoid eclipsed cb */
+ comp->blank = dmask == comp->ovl_mask;
+ comp->ovl_dmask = dmask;
+ comp->cb = mgr->info.cb;
+ mgr->info.cb.fn = dsscomp_mgr_callback;
+ mgr->info.cb.data = comp;
+
+ /*
+ * Check other overlays that may also use this display.
+ * NOTE: This is only needed in case someone changes
+ * overlays via sysfs.
+ */
+ for (i = 0; i < cdev->num_ovls; i++) {
+ u32 mask = 1 << i;
+ if ((~comp->ovl_mask & mask) &&
+ cdev->ovls[i]->info.enabled &&
+ cdev->ovls[i]->manager == mgr)
+ comp->ovl_mask |= mask;
+ }
+ }
+
+ /* apply changes and call update on manual panels */
+ comp->magic = MAGIC_APPLIED;
+
+ if (dssdev_manually_updated(dssdev)) {
+ 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;
+
+ /* sync to prevent frame loss */
+ r = drv->sync(dssdev) ? : mgr->apply(mgr);
+
+ if (!r && (d->mode & DSSCOMP_SETUP_MODE_DISPLAY)) {
+#if 0
+ /* schedule update if supported */
+ if (drv->sched_update)
+ r = drv->sched_update(dssdev, d->win.x,
+ d->win.y, d->win.w, d->win.h);
+ else if (drv->update)
+#else
+ if (drv->update)
+#endif
+ r = drv->update(dssdev, d->win.x,
+ d->win.y, d->win.w, d->win.h);
+ }
+ } else {
+ /* wait for sync to avoid tear */
+ r = mgr->wait_for_vsync(mgr) ? : mgr->apply(mgr);
+ }
+done:
+ if (change)
+ refresh_masks(display_ix);
+
+ mutex_unlock(&mtx);
+
+ return r;
+}
+EXPORT_SYMBOL(dsscomp_apply);
+
+/*
+ * ===========================================================================
+ * WAIT OPERATIONS
+ * ===========================================================================
+ */
+
+/* return true iff composition phase has passed */
+static bool is_wait_over(dsscomp_t comp, enum dsscomp_wait_phase phase)
+{
+ comp = validate(comp);
+ return IS_ERR(comp) ||
+ (phase == DSSCOMP_WAIT_PROGRAMMED &&
+ (comp->magic == MAGIC_PROGRAMMED ||
+ comp->magic == MAGIC_DISPLAYED)) ||
+ (phase == DSSCOMP_WAIT_DISPLAYED &&
+ comp->magic == MAGIC_DISPLAYED);
+}
+
+/* wait for programming or release of a composition */
+int dsscomp_wait(dsscomp_t comp, enum dsscomp_wait_phase phase, int timeout)
+{
+ u32 id;
+
+ mutex_lock(&mtx);
+
+ comp = validate(comp);
+ id = IS_ERR(comp) ? 0 : comp->frm.sync_id;
+ if (debug & DEBUG_WAITS)
+ dev_info(DEV(cdev), "wait %s on [%08x]\n",
+ phase == DSSCOMP_WAIT_DISPLAYED ? "display" :
+ phase == DSSCOMP_WAIT_PROGRAMMED ? "program" :
+ "release", id);
+
+ if (!IS_ERR(comp) && !is_wait_over(comp, phase)) {
+ u32 ix = comp->frm.mgr.ix;
+
+ mutex_unlock(&mtx);
+
+ /*
+ * we can check being active without mutex because we will
+ * also check it while holding the mutex before returning
+ */
+ timeout = wait_event_interruptible_timeout(mgrq[ix].wq,
+ is_wait_over(comp, phase), timeout);
+ if (debug & DEBUG_WAITS)
+ dev_info(DEV(cdev), "wait over [%08x]: %s %d\n", id,
+ timeout < 0 ? "signal" :
+ timeout > 0 ? "ok" : "timeout",
+ timeout);
+ if (timeout <= 0)
+ return timeout ? : -ETIME;
+
+ mutex_lock(&mtx);
+ }
+
+ mutex_unlock(&mtx);
+
+ return 0;
+}
+EXPORT_SYMBOL(dsscomp_wait);
+
+/*
+ * ===========================================================================
+ * EXIT
+ * ===========================================================================
+ */
+void dsscomp_queue_exit(void)
+{
+ struct dsscomp_data *c, *c2;
+ if (cis && ois && cdev) {
+ int i;
+ for (i = 0; i < cdev->num_displays; i++) {
+ list_for_each_entry_safe(c, c2, &mgrq[i].q_ci, q)
+ dsscomp_drop(c);
+ }
+ vfree(cis);
+ vfree(ois);
+ cis = NULL;
+ ois = NULL;
+ cdev = NULL;
+ }
+}
+EXPORT_SYMBOL(dsscomp_queue_exit);
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index 0a3e400..51da678 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -358,7 +358,7 @@ static int fb_mode_to_dss_mode(struct fb_var_screeninfo *var,
dssmode = OMAP_DSS_COLOR_RGB24P;
break;
case 32:
- dssmode = OMAP_DSS_COLOR_RGB24U;
+ dssmode = OMAP_DSS_COLOR_ARGB32;
break;
default:
return -EINVAL;
diff --git a/include/video/dsscomp.h b/include/video/dsscomp.h
new file mode 100644
index 0000000..0900f7a
--- /dev/null
+++ b/include/video/dsscomp.h
@@ -0,0 +1,557 @@
+#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 */
+};
+
+#endif
+
+/*
+ * 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;
+};
+
+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 to RGB color conversion info */
+struct dss2_color_conv_info {
+ __s16 r_y, r_cr, r_cb;
+ __s16 g_y, g_cr, g_cb;
+ __s16 b_y, b_cr, b_cb;
+
+ /* Y is 16..235, UV is 16..240 if not fullrange. Otherwise 0..255 */
+ __u16 fullrange; /* bool */
+} __attribute__ ((aligned(4)));
+
+/* YUV VC1 range mapping info */
+struct dss2_vc1_range_map_info {
+ __u8 enable; /* bool */
+
+ __u8 range_y; /* 0..7 */
+ __u8 range_uv; /* 0..7 */
+} __attribute__ ((aligned(4)));
+
+/* standard rectangle */
+struct dss2_rect_t {
+ __s32 x; /* left */
+ __s32 y; /* top */
+ __u32 w; /* width */
+ __u32 h; /* height */
+} __attribute__ ((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 */
+} __attribute__ ((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 DSSCOMP_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 DSSCOMP_SETUP_MANAGER
+ * ioctl. You can delay applying the settings until an dss2_manager_apply()
+ * with the same sync_id is called 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 DSSCOMP_WB_COPY ioctl for this. This is a blocking
+ * call, and it may possibly fail if an ongoing WB capture mode has been
+ * schedule (which is outside of the current scope of the DSS2 interface.)
+ *
+ */
+
+/*
+ * 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 dss2_color_conv_info 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 */
+} __attribute__ ((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,
+};
+
+struct dss2_ovl_info {
+ struct dss2_ovl_cfg cfg;
+
+ union {
+ /* user-space interfaces */
+ struct {
+ void *address; /* main buffer address */
+
+ /*
+ * For DSSCOMP_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. Default value of 0 uses the address to
+ * determine the type.
+ */
+ __u16 ba_type;
+ __u16 uv_type;
+ };
+
+ /* kernel-space interfaces */
+ struct {
+ __u32 ba; /* base address */
+ __u32 uv; /* uv address */
+ };
+ };
+};
+
+/*
+ * DSS2 manager information.
+ *
+ * The following information is deemed to be set globally, so it is not
+ * included:
+ * - gamma correction
+ * - color phase 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;
+ __u8 trans_enabled; /* bool */
+
+ __u8 interlaced; /* bool */
+ __u8 alpha_blending; /* bool - overrides trans_enabled */
+} __attribute__ ((aligned(4)));
+
+/*
+ * ioctl: DSSCOMP_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 poll()). It gets
+ * ready when frame has been eclipsed by another frame.
+ *
+ * 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.
+ *
+ * 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 */
+
+ 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: DSSCOMP_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;
+};
+
+/*
+ * ioctl: DSSCOMP_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: DSSCOMP_QUERY_DISPLAY, struct dsscomp_display_info
+ *
+ * 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_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 */
+};
+
+/*
+ * ioctl: DSSCOMP_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.)
+ *
+ * Non-existing sync IDs are assumed to have been programmed, displayed and
+ * released. (They are assumed to be no-longer existing sync IDs.)
+ *
+ * Set timeout to desired timeout value in microseconds. Set timeout
+ * to 0 if you want to return a sync object (file descriptor) instead of
+ * waiting for the event. You can then poll on this sync object to
+ * wait for the specified event.
+ *
+ * Returns: >=0 on success, <0 error value on failure (e.g. -ETIME).
+ */
+enum dsscomp_wait_phase {
+ DSSCOMP_WAIT_RELEASED,
+ DSSCOMP_WAIT_PROGRAMMED,
+ DSSCOMP_WAIT_DISPLAYED,
+};
+
+struct dsscomp_wait_data {
+ __u32 ix; /* display index */
+ __u32 sync_id;
+ __u32 timeout_us; /* timeout in microseconds */
+ enum dsscomp_wait_phase phase; /* phase to wait for */
+};
+
+/*
+ * ioctl: DSSCOMP_LAST_RELEASED, struct dsscomp_wait_data
+ *
+ * Non-blocking sync.
+ *
+ * Fills in the sync_id of the last released frame on a
+ * display specified by ix.
+ *
+ * Returns 0 on success, non-0 error value on failure.
+ */
+
+/* IOCTLS */
+#define DSSCOMP_SETUP_MGR _IOW('O', 128, struct dsscomp_setup_mgr_data)
+#define DSSCOMP_CHECK_OVL _IOWR('O', 129, struct dsscomp_check_ovl_data)
+#define DSSCOMP_WB_COPY _IOW('O', 130, struct dsscomp_wb_copy_data)
+#define DSSCOMP_QUERY_DISPLAY _IOWR('O', 131, struct dsscomp_display_info)
+#define DSSCOMP_WAIT _IOW('O', 132, struct dsscomp_wait_data)
+
+#endif
diff --git a/include/video/omapdss.h b/include/video/omapdss.h
index 25fc1f3..565a49b 100644
--- a/include/video/omapdss.h
+++ b/include/video/omapdss.h
@@ -41,6 +41,8 @@
#define DISPC_IRQ_WAKEUP (1 << 16)
#define DISPC_IRQ_SYNC_LOST2 (1 << 17)
#define DISPC_IRQ_VSYNC2 (1 << 18)
+#define DISPC_IRQ_VID3_END_WIN (1 << 19)
+#define DISPC_IRQ_VID3_FIFO_UNDERFLOW (1 << 20)
#define DISPC_IRQ_ACBIAS_COUNT_STAT2 (1 << 21)
#define DISPC_IRQ_FRAMEDONE2 (1 << 22)
@@ -60,7 +62,8 @@ enum omap_display_type {
enum omap_plane {
OMAP_DSS_GFX = 0,
OMAP_DSS_VIDEO1 = 1,
- OMAP_DSS_VIDEO2 = 2
+ OMAP_DSS_VIDEO2 = 2,
+ OMAP_DSS_VIDEO3 = 3,
};
enum omap_channel {
@@ -166,6 +169,7 @@ enum omap_dss_overlay_managers {
enum omap_dss_rotation_type {
OMAP_DSS_ROT_DMA = 0,
OMAP_DSS_ROT_VRFB = 1,
+ OMAP_DSS_ROT_TILER = 2,
};
/* clockwise rotation angle */
@@ -196,6 +200,13 @@ enum omap_dss_clk_source {
OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI, /* OMAP4: PLL2_CLK2 */
};
+enum omap_overlay_zorder {
+ OMAP_DSS_OVL_ZORDER_0 = 0,
+ OMAP_DSS_OVL_ZORDER_1 = 1,
+ OMAP_DSS_OVL_ZORDER_2 = 2,
+ OMAP_DSS_OVL_ZORDER_3 = 3,
+};
+
/* RFBI */
struct rfbi_timings {
@@ -308,6 +319,24 @@ extern const struct omap_video_timings omap_dss_pal_timings;
extern const struct omap_video_timings omap_dss_ntsc_timings;
#endif
+enum omapdss_completion_status {
+ DSS_COMPLETION_PROGRAMMED = 0,
+ DSS_COMPLETION_DISPLAYED = 4,
+ DSS_COMPLETION_CHANGED_SET,
+ DSS_COMPLETION_CHANGED_CACHE,
+ DSS_COMPLETION_RELEASED = 8,
+ DSS_COMPLETION_ECLIPSED_SET,
+ DSS_COMPLETION_ECLIPSED_CACHE,
+ DSS_COMPLETION_ECLIPSED_SHADOW,
+ DSS_COMPLETION_TORN,
+};
+
+struct omapdss_ovl_cb {
+ /* optional callback method */
+ void (*fn)(void *data, int id, int status);
+ void *data;
+};
+
struct omap_overlay_info {
bool enabled;
@@ -328,6 +357,10 @@ struct omap_overlay_info {
u16 out_height; /* if 0, out_height == height */
u8 global_alpha;
u8 pre_mult_alpha;
+ enum omap_overlay_zorder zorder;
+ u16 min_x_decim, max_x_decim, min_y_decim, max_y_decim;
+
+ struct omapdss_ovl_cb cb;
};
struct omap_overlay {
@@ -367,6 +400,8 @@ struct omap_overlay_manager_info {
bool trans_enabled;
bool alpha_enabled;
+
+ struct omapdss_ovl_cb cb;
};
struct omap_overlay_manager {