aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/omap2
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/omap2')
-rw-r--r--drivers/video/omap2/Kconfig2
-rw-r--r--drivers/video/omap2/Makefile2
-rw-r--r--drivers/video/omap2/displays/panel-generic-dpi.c24
-rw-r--r--drivers/video/omap2/displays/panel-taal.c33
-rw-r--r--drivers/video/omap2/dss/Kconfig13
-rw-r--r--drivers/video/omap2/dss/Makefile4
-rw-r--r--drivers/video/omap2/dss/core.c40
-rw-r--r--drivers/video/omap2/dss/dispc.c1935
-rw-r--r--drivers/video/omap2/dss/dispc.h60
-rw-r--r--drivers/video/omap2/dss/display.c14
-rw-r--r--drivers/video/omap2/dss/dpi.c73
-rw-r--r--drivers/video/omap2/dss/dsi.c621
-rw-r--r--drivers/video/omap2/dss/dss.c565
-rw-r--r--drivers/video/omap2/dss/dss.h105
-rw-r--r--drivers/video/omap2/dss/dss_features.c33
-rw-r--r--drivers/video/omap2/dss/dss_features.h9
-rw-r--r--drivers/video/omap2/dss/fifothreshold.c422
-rw-r--r--drivers/video/omap2/dss/hdmi.c1964
-rw-r--r--drivers/video/omap2/dss/hdmi.h631
-rw-r--r--drivers/video/omap2/dss/hdmi_panel.c (renamed from drivers/video/omap2/dss/hdmi_omap4_panel.c)147
-rw-r--r--drivers/video/omap2/dss/manager.c766
-rw-r--r--drivers/video/omap2/dss/overlay.c210
-rw-r--r--drivers/video/omap2/dss/rfbi.c98
-rw-r--r--drivers/video/omap2/dss/sdi.c40
-rw-r--r--drivers/video/omap2/dss/venc.c171
-rw-r--r--drivers/video/omap2/dsscomp/Kconfig20
-rw-r--r--drivers/video/omap2/dsscomp/Makefile3
-rw-r--r--drivers/video/omap2/dsscomp/base.c504
-rw-r--r--drivers/video/omap2/dsscomp/device.c634
-rw-r--r--drivers/video/omap2/dsscomp/dsscomp.h208
-rw-r--r--drivers/video/omap2/dsscomp/gralloc.c607
-rw-r--r--drivers/video/omap2/dsscomp/queue.c807
-rw-r--r--drivers/video/omap2/hdcp/Kconfig14
-rw-r--r--drivers/video/omap2/hdcp/Makefile8
-rw-r--r--drivers/video/omap2/hdcp/hdcp.h394
-rw-r--r--drivers/video/omap2/hdcp/hdcp_ddc.c310
-rw-r--r--drivers/video/omap2/hdcp/hdcp_ddc.h111
-rw-r--r--drivers/video/omap2/hdcp/hdcp_lib.c838
-rw-r--r--drivers/video/omap2/hdcp/hdcp_top.c1050
-rw-r--r--drivers/video/omap2/omapfb/Kconfig1
-rw-r--r--drivers/video/omap2/omapfb/omapfb-ioctl.c31
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c121
-rw-r--r--drivers/video/omap2/omapfb/omapfb.h7
43 files changed, 9897 insertions, 3753 deletions
diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig
index d877c36..c4ec41a 100644
--- a/drivers/video/omap2/Kconfig
+++ b/drivers/video/omap2/Kconfig
@@ -7,3 +7,5 @@ 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"
+source "drivers/video/omap2/hdcp/Kconfig"
diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile
index 5ddef12..ceb1dd9 100644
--- a/drivers/video/omap2/Makefile
+++ b/drivers/video/omap2/Makefile
@@ -3,4 +3,6 @@ obj-$(CONFIG_OMAP2_VRFB) += vrfb.o
obj-$(CONFIG_OMAP2_DSS) += dss/
obj-$(CONFIG_FB_OMAP2) += omapfb/
+obj-$(CONFIG_OMAP4_HDCP) += hdcp/
obj-y += displays/
+obj-y += dsscomp/
diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c
index 9c90f75..611be7e 100644
--- a/drivers/video/omap2/displays/panel-generic-dpi.c
+++ b/drivers/video/omap2/displays/panel-generic-dpi.c
@@ -82,6 +82,30 @@ static struct panel_config generic_dpi_panels[] = {
.name = "generic",
},
+ /* generic 720p */
+ {
+ {
+ .x_res = 1280,
+ .y_res = 720,
+
+ .pixel_clock = 74250,
+
+ .hfp = 110,
+ .hsw = 40,
+ .hbp = 20,
+
+ .vfp = 5,
+ .vsw = 5,
+ .vbp = 20,
+ },
+ .acbi = 0x0,
+ .acb = 0x0,
+ .config = OMAP_DSS_LCD_TFT,
+ .power_on_delay = 0,
+ .power_off_delay = 0,
+ .name = "generic_720p",
+ },
+
/* Sharp LQ043T1DG01 */
{
{
diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c
index fdd5d4ae..b82bcc1 100644
--- a/drivers/video/omap2/displays/panel-taal.c
+++ b/drivers/video/omap2/displays/panel-taal.c
@@ -504,14 +504,18 @@ static int taal_exit_ulps(struct omap_dss_device *dssdev)
return 0;
r = omapdss_dsi_display_enable(dssdev);
- if (r)
- goto err;
+ if (r) {
+ dev_err(&dssdev->dev, "failed to enable DSI\n");
+ goto err1;
+ }
omapdss_dsi_vc_enable_hs(dssdev, td->channel, true);
r = _taal_enable_te(dssdev, true);
- if (r)
- goto err;
+ if (r) {
+ dev_err(&dssdev->dev, "failed to re-enable TE");
+ goto err2;
+ }
enable_irq(gpio_to_irq(panel_data->ext_te_gpio));
@@ -521,13 +525,15 @@ static int taal_exit_ulps(struct omap_dss_device *dssdev)
return 0;
-err:
- dev_err(&dssdev->dev, "exit ULPS failed");
- r = taal_panel_reset(dssdev);
-
- enable_irq(gpio_to_irq(panel_data->ext_te_gpio));
- td->ulps_enabled = false;
+err2:
+ dev_err(&dssdev->dev, "failed to exit ULPS");
+ r = taal_panel_reset(dssdev);
+ if (!r) {
+ enable_irq(gpio_to_irq(panel_data->ext_te_gpio));
+ td->ulps_enabled = false;
+ }
+err1:
taal_queue_ulps_work(dssdev);
return r;
@@ -1317,8 +1323,11 @@ static void taal_disable(struct omap_dss_device *dssdev)
dsi_bus_lock(dssdev);
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
- taal_wake_up(dssdev);
- taal_power_off(dssdev);
+ int r;
+
+ r = taal_wake_up(dssdev);
+ if (!r)
+ taal_power_off(dssdev);
}
dsi_bus_unlock(dssdev);
diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig
index 6b3e2da..8f9420e 100644
--- a/drivers/video/omap2/dss/Kconfig
+++ b/drivers/video/omap2/dss/Kconfig
@@ -63,6 +63,7 @@ config OMAP2_DSS_VENC
config OMAP4_DSS_HDMI
bool "HDMI support"
depends on ARCH_OMAP4
+ select HDMI_TI_4XXX_IP
default y
help
HDMI Interface. This adds the High Definition Multimedia Interface.
@@ -117,18 +118,6 @@ config OMAP2_DSS_MIN_FCK_PER_PCK
Max FCK is 173MHz, so this doesn't work if your PCK
is very high.
-config OMAP2_DSS_SLEEP_BEFORE_RESET
- bool "Sleep 50ms before DSS reset"
- default y
- help
- For some unknown reason we may get SYNC_LOST errors from the display
- subsystem at initialization time if we don't sleep before resetting
- the DSS. See the source (dss.c) for more comments.
-
- However, 50ms is quite long time to sleep, and with some
- configurations the SYNC_LOST may never happen, so the sleep can
- be disabled here.
-
config OMAP2_DSS_SLEEP_AFTER_VENC_RESET
bool "Sleep 20ms after VENC reset"
default y
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
index 10d9d3b..e67db6e 100644
--- a/drivers/video/omap2/dss/Makefile
+++ b/drivers/video/omap2/dss/Makefile
@@ -1,9 +1,9 @@
obj-$(CONFIG_OMAP2_DSS) += omapdss.o
-omapdss-y := core.o dss.o dss_features.o dispc.o display.o manager.o overlay.o
+omapdss-y := core.o dss.o dss_features.o dispc.o display.o manager.o overlay.o fifothreshold.o
omapdss-$(CONFIG_OMAP2_DSS_DPI) += dpi.o
omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o
omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o \
- hdmi_omap4_panel.o
+ hdmi_panel.o
diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c
index 3da4267..2a1e6ed 100644
--- a/drivers/video/omap2/dss/core.c
+++ b/drivers/video/omap2/dss/core.c
@@ -145,6 +145,8 @@ static int dss_initialize_debugfs(void)
debugfs_create_file("venc", S_IRUGO, dss_debugfs_dir,
&venc_dump_regs, &dss_debug_fops);
#endif
+ debugfs_create_file("hdmi", S_IRUGO, dss_debugfs_dir,
+ &hdmi_dump_regs, &dss_debug_fops);
return 0;
}
@@ -183,8 +185,11 @@ static int omap_dss_probe(struct platform_device *pdev)
goto err_dss;
}
- /* keep clocks enabled to prevent context saves/restores during init */
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ r = dispc_init_platform_driver();
+ if (r) {
+ DSSERR("Failed to initialize dispc platform driver\n");
+ goto err_dispc;
+ }
r = rfbi_init_platform_driver();
if (r) {
@@ -192,12 +197,6 @@ static int omap_dss_probe(struct platform_device *pdev)
goto err_rfbi;
}
- r = dispc_init_platform_driver();
- if (r) {
- DSSERR("Failed to initialize dispc platform driver\n");
- goto err_dispc;
- }
-
r = venc_init_platform_driver();
if (r) {
DSSERR("Failed to initialize venc platform driver\n");
@@ -238,8 +237,6 @@ static int omap_dss_probe(struct platform_device *pdev)
pdata->default_device = dssdev;
}
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
-
return 0;
err_register:
@@ -424,6 +421,24 @@ static int dss_driver_remove(struct device *dev)
return 0;
}
+static void omap_dss_driver_disable(struct omap_dss_device *dssdev)
+{
+ if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)
+ blocking_notifier_call_chain(&dssdev->state_notifiers,
+ OMAP_DSS_DISPLAY_DISABLED, dssdev);
+ dssdev->driver->disable_orig(dssdev);
+ dssdev->first_vsync = false;
+}
+
+static int omap_dss_driver_enable(struct omap_dss_device *dssdev)
+{
+ int r = dssdev->driver->enable_orig(dssdev);
+ if (!r && dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ blocking_notifier_call_chain(&dssdev->state_notifiers,
+ OMAP_DSS_DISPLAY_ACTIVE, dssdev);
+ return r;
+}
+
int omap_dss_register_driver(struct omap_dss_driver *dssdriver)
{
dssdriver->driver.bus = &dss_bus_type;
@@ -436,6 +451,11 @@ int omap_dss_register_driver(struct omap_dss_driver *dssdriver)
dssdriver->get_recommended_bpp =
omapdss_default_get_recommended_bpp;
+ dssdriver->disable_orig = dssdriver->disable;
+ dssdriver->disable = omap_dss_driver_disable;
+ dssdriver->enable_orig = dssdriver->enable;
+ dssdriver->enable = omap_dss_driver_enable;
+
return driver_register(&dssdriver->driver);
}
EXPORT_SYMBOL(omap_dss_register_driver);
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index 7a9a2e7..c5eaf90 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -33,12 +33,17 @@
#include <linux/workqueue.h>
#include <linux/hardirq.h>
#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/ratelimit.h>
#include <plat/sram.h>
#include <plat/clock.h>
-
+#include <mach/tiler.h>
+#include <plat/omap-pm.h>
#include <video/omapdss.h>
+#include "../clockdomain.h"
#include "dss.h"
#include "dss_features.h"
#include "dispc.h"
@@ -55,26 +60,20 @@
#define DISPC_MAX_NR_ISRS 8
+static struct clockdomain *l3_1_clkdm, *l3_2_clkdm;
+
struct omap_dispc_isr_data {
omap_dispc_isr_t isr;
void *arg;
u32 mask;
};
-struct dispc_h_coef {
- s8 hc4;
- s8 hc3;
- u8 hc2;
- s8 hc1;
- s8 hc0;
-};
-
-struct dispc_v_coef {
- s8 vc22;
- s8 vc2;
- u8 vc1;
- s8 vc0;
- s8 vc00;
+struct dispc_hv_coef {
+ s8 hc0_vc00;
+ s8 hc1_vc0;
+ u8 hc2_vc1;
+ s8 hc3_vc2;
+ s8 hc4_vc22;
};
#define REG_GET(idx, start, end) \
@@ -92,9 +91,17 @@ struct dispc_irq_stats {
static struct {
struct platform_device *pdev;
void __iomem *base;
+
+ int ctx_loss_cnt;
+ struct mutex runtime_lock;
+ int runtime_count;
+
int irq;
+ struct clk *dss_clk;
+
+ u32 fifo_size[MAX_DSS_OVERLAYS];
- u32 fifo_size[3];
+ u32 channel_irq[3]; /* Max channels hardcoded to 3*/
spinlock_t irq_lock;
u32 irq_error_mask;
@@ -102,6 +109,7 @@ static struct {
u32 error_irqs;
struct work_struct error_work;
+ bool ctx_valid;
u32 ctx[DISPC_SZ_REGS / sizeof(u32)];
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
@@ -134,18 +142,34 @@ static inline u32 dispc_read_reg(const u16 idx)
return __raw_readl(dispc.base + idx);
}
+static int dispc_get_ctx_loss_count(void)
+{
+ struct device *dev = &dispc.pdev->dev;
+ struct omap_display_platform_data *pdata = dev->platform_data;
+ struct omap_dss_board_info *board_data = pdata->board_data;
+ int cnt;
+
+ if (!board_data->get_context_loss_count)
+ return -ENOENT;
+
+ cnt = board_data->get_context_loss_count(dev);
+
+ WARN_ONCE(cnt < 0, "get_context_loss_count failed: %d\n", cnt);
+
+ return cnt;
+}
+
#define SR(reg) \
dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg)
#define RR(reg) \
dispc_write_reg(DISPC_##reg, dispc.ctx[DISPC_##reg / sizeof(u32)])
-void dispc_save_context(void)
+static void dispc_save_context(void)
{
- int i;
- if (cpu_is_omap24xx())
- return;
+ int i, o;
+
+ DSSDBG("dispc_save_context\n");
- SR(SYSCONFIG);
SR(IRQENABLE);
SR(CONTROL);
SR(CONFIG);
@@ -158,7 +182,8 @@ void dispc_save_context(void)
SR(TIMING_V(OMAP_DSS_CHANNEL_LCD));
SR(POL_FREQ(OMAP_DSS_CHANNEL_LCD));
SR(DIVISORo(OMAP_DSS_CHANNEL_LCD));
- SR(GLOBAL_ALPHA);
+ if (dss_has_feature(FEAT_GLOBAL_ALPHA))
+ SR(GLOBAL_ALPHA);
SR(SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT));
SR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD));
if (dss_has_feature(FEAT_MGR_LCD2)) {
@@ -188,123 +213,108 @@ void dispc_save_context(void)
SR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD));
SR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD));
- SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
- SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
- SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
+ if (dss_has_feature(FEAT_CPR)) {
+ SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
+ SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
+ SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
+ }
if (dss_has_feature(FEAT_MGR_LCD2)) {
- SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
- SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
- SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
+ if (dss_has_feature(FEAT_CPR)) {
+ SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
+ SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
+ SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
+ }
SR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2));
SR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2));
SR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2));
}
- SR(OVL_PRELOAD(OMAP_DSS_GFX));
+ 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));
+ /* 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++)
- SR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, i));
+ 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_HV(OMAP_DSS_VIDEO1, i));
+ for (i = 0; i < 8; i++)
+ SR(OVL_FIR_COEF_H(o, i));
- for (i = 0; i < 5; i++)
- SR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i));
+ for (i = 0; i < 8; i++)
+ SR(OVL_FIR_COEF_HV(o, i));
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i));
+ for (i = 0; i < 5; i++)
+ SR(OVL_CONV_COEF(o, i));
- 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));
+ if (dss_has_feature(FEAT_FIR_COEF_V)) {
+ for (i = 0; i < 8; i++)
+ SR(OVL_FIR_COEF_V(o, i));
+ }
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, i));
+ 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_HV2(OMAP_DSS_VIDEO1, i));
+ for (i = 0; i < 8; i++)
+ SR(OVL_FIR_COEF_H2(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 < 8; i++)
+ SR(OVL_FIR_COEF_HV2(o, i));
- 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_V2(o, i));
+ }
+ if (dss_has_feature(FEAT_ATTR2))
+ SR(OVL_ATTRIBUTES2(o));
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, i));
+ if (dss_has_feature(FEAT_PRELOAD))
+ SR(OVL_PRELOAD(o));
+ }
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, i));
+ if (dss_has_feature(FEAT_CORE_CLK_DIV))
+ SR(DIVISOR);
- for (i = 0; i < 5; i++)
- SR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i));
+ dispc.ctx_loss_cnt = dispc_get_ctx_loss_count();
+ dispc.ctx_valid = true;
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i));
+ DSSDBG("context saved, ctx_loss_count %d\n", dispc.ctx_loss_cnt);
+}
- 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));
+static void dispc_restore_context(void)
+{
+ struct device *dev = &dispc.pdev->dev;
+ int i, o, ctx;
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, i));
+ DSSDBG("dispc_restore_context\n");
- for (i = 0; i < 8; i++)
- SR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, i));
+ if (!dispc.ctx_valid)
+ return;
- 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));
+ ctx = dispc_get_ctx_loss_count();
- SR(OVL_PRELOAD(OMAP_DSS_VIDEO2));
+ if (!omap_pm_was_context_lost(dev))
+ return;
- if (dss_has_feature(FEAT_CORE_CLK_DIV))
- SR(DIVISOR);
-}
+ DSSDBG("ctx_loss_count: saved %d, current %d\n",
+ dispc.ctx_loss_cnt, ctx);
-void dispc_restore_context(void)
-{
- int i;
- RR(SYSCONFIG);
/*RR(IRQENABLE);*/
/*RR(CONTROL);*/
RR(CONFIG);
@@ -317,7 +327,8 @@ void dispc_restore_context(void)
RR(TIMING_V(OMAP_DSS_CHANNEL_LCD));
RR(POL_FREQ(OMAP_DSS_CHANNEL_LCD));
RR(DIVISORo(OMAP_DSS_CHANNEL_LCD));
- RR(GLOBAL_ALPHA);
+ if (dss_has_feature(FEAT_GLOBAL_ALPHA))
+ RR(GLOBAL_ALPHA);
RR(SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT));
RR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD));
if (dss_has_feature(FEAT_MGR_LCD2)) {
@@ -347,114 +358,81 @@ void dispc_restore_context(void)
RR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD));
RR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD));
- RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
- RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
- RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
+ if (dss_has_feature(FEAT_CPR)) {
+ RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
+ RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
+ RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
+ }
if (dss_has_feature(FEAT_MGR_LCD2)) {
RR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2));
RR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2));
RR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2));
- RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
- RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
- RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
+ if (dss_has_feature(FEAT_CPR)) {
+ RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
+ RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
+ RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
+ }
}
- 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));
+ if (dss_has_feature(FEAT_PRELOAD))
+ RR(OVL_PRELOAD(OMAP_DSS_GFX));
- for (i = 0; i < 5; i++)
- RR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i));
- 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_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));
+ RR(OVL_FIR_COEF_HV(o, i));
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, i));
+ for (i = 0; i < 5; i++)
+ RR(OVL_CONV_COEF(o, 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(o, i));
+ }
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_V(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));
- 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));
+ for (i = 0; i < 8; i++)
+ RR(OVL_FIR_COEF_H2(o, i));
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, i));
+ for (i = 0; i < 8; i++)
+ RR(OVL_FIR_COEF_HV2(o, i));
- 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_V2(o, i));
+ }
+ if (dss_has_feature(FEAT_ATTR2))
+ RR(OVL_ATTRIBUTES2(o));
- for (i = 0; i < 8; i++)
- RR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, i));
+ if (dss_has_feature(FEAT_PRELOAD))
+ RR(OVL_PRELOAD(o));
}
- if (dss_has_feature(FEAT_ATTR2))
- RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2));
-
- RR(OVL_PRELOAD(OMAP_DSS_VIDEO2));
if (dss_has_feature(FEAT_CORE_CLK_DIV))
RR(DIVISOR);
@@ -471,19 +449,176 @@ void dispc_restore_context(void)
* the context is fully restored
*/
RR(IRQENABLE);
+
+ DSSDBG("context restored\n");
}
#undef SR
#undef RR
-static inline void enable_clocks(bool enable)
+static u32 dispc_calculate_threshold(enum omap_plane plane, u32 paddr,
+ u32 puv_addr, u16 width, u16 height,
+ s32 row_inc, s32 pix_inc)
{
- if (enable)
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
- else
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ int shift;
+ u32 channel_no = plane;
+ u32 val, burstsize, doublestride;
+ u32 rotation, bursttype, color_mode;
+ struct dispc_config dispc_reg_config;
+
+ if (width >= 1920)
+ return 1500;
+
+ /* Get the burst size */
+ shift = (plane == OMAP_DSS_GFX) ? 6 : 14;
+ val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
+ burstsize = FLD_GET(val, shift + 1, shift);
+ doublestride = FLD_GET(val, 22, 22);
+ rotation = FLD_GET(val, 13, 12);
+ bursttype = FLD_GET(val, 29, 29);
+ color_mode = FLD_GET(val, 4, 1);
+
+ /* base address for frame (Luma frame in case of YUV420) */
+ dispc_reg_config.ba = paddr;
+ /* base address for Chroma frame in case of YUV420 */
+ dispc_reg_config.bacbcr = puv_addr;
+ /* OrgSizeX for frame */
+ dispc_reg_config.sizex = width - 1;
+ /* OrgSizeY for frame */
+ dispc_reg_config.sizey = height - 1;
+ /* burst size */
+ dispc_reg_config.burstsize = burstsize;
+ /* pixel increment */
+ dispc_reg_config.pixelinc = pix_inc;
+ /* row increment */
+ dispc_reg_config.rowinc = row_inc;
+ /* burst type: 1D/2D */
+ dispc_reg_config.bursttype = bursttype;
+ /* chroma DoubleStride when in YUV420 format */
+ dispc_reg_config.doublestride = doublestride;
+ /* Pixcel format of the frame.*/
+ dispc_reg_config.format = color_mode;
+ /* Rotation of frame */
+ dispc_reg_config.rotation = rotation;
+
+ /* DMA buffer allications - assuming reset values */
+ dispc_reg_config.gfx_top_buffer = 0;
+ dispc_reg_config.gfx_bottom_buffer = 0;
+ dispc_reg_config.vid1_top_buffer = 1;
+ dispc_reg_config.vid1_bottom_buffer = 1;
+ dispc_reg_config.vid2_top_buffer = 2;
+ dispc_reg_config.vid2_bottom_buffer = 2;
+ dispc_reg_config.vid3_top_buffer = 3;
+ dispc_reg_config.vid3_bottom_buffer = 3;
+ dispc_reg_config.wb_top_buffer = 4;
+ dispc_reg_config.wb_bottom_buffer = 4;
+
+ /* antiFlicker is off */
+ dispc_reg_config.antiflicker = 0;
+
+ return sa_calc_wrap(&dispc_reg_config, channel_no);
}
+int dispc_runtime_get(void)
+{
+ int r;
+
+ mutex_lock(&dispc.runtime_lock);
+
+ if (dispc.runtime_count++ == 0) {
+ DSSDBG("dispc_runtime_get\n");
+
+ /*
+ * OMAP4 ERRATUM xxxx: Mstandby and disconnect protocol issue
+ * Impacts: all OMAP4 devices
+ * Simplfied Description:
+ * issue #1: The handshake between IP modules on L3_1 and L3_2
+ * peripherals with PRCM has a limitation in a certain time
+ * window of L4 clock cycle. Due to the fact that a wrong
+ * variant of stall signal was used in circuit of PRCM, the
+ * intitator-interconnect protocol is broken when the time
+ * window is hit where the PRCM requires the interconnect to go
+ * to idle while intitator asks to wakeup.
+ * Issue #2: DISPC asserts a sub-mstandby signal for a short
+ * period. In this time interval, IP block requests
+ * disconnection of Master port, and results in Mstandby and
+ * wait request to PRCM. In parallel, if mstandby is de-asserted
+ * by DISPC simultaneously, interconnect requests for a
+ * reconnect for one cycle alone resulting in a disconnect
+ * protocol violation and a deadlock of the system.
+ *
+ * Workaround:
+ * L3_1 clock domain must not be programmed in HW_AUTO if
+ * Static dependency with DSS is enabled and DSS clock domain
+ * is ON. Same for L3_2.
+ */
+ if (cpu_is_omap44xx()) {
+ clkdm_deny_idle(l3_1_clkdm);
+ clkdm_deny_idle(l3_2_clkdm);
+ }
+
+ r = dss_runtime_get();
+ if (r)
+ goto err_dss_get;
+
+ /* XXX dispc fclk can also come from DSI PLL */
+ clk_enable(dispc.dss_clk);
+
+ r = pm_runtime_get_sync(&dispc.pdev->dev);
+ WARN_ON(r);
+ if (r < 0)
+ goto err_runtime_get;
+
+ dispc_restore_context();
+ }
+
+ mutex_unlock(&dispc.runtime_lock);
+
+ return 0;
+
+err_runtime_get:
+ clk_disable(dispc.dss_clk);
+ dss_runtime_put();
+err_dss_get:
+ mutex_unlock(&dispc.runtime_lock);
+
+ return r;
+}
+
+void dispc_runtime_put(void)
+{
+ mutex_lock(&dispc.runtime_lock);
+
+ if (--dispc.runtime_count == 0) {
+ int r;
+
+ DSSDBG("dispc_runtime_put\n");
+
+ dispc_save_context();
+
+ r = pm_runtime_put_sync(&dispc.pdev->dev);
+ WARN_ON(r);
+
+ clk_disable(dispc.dss_clk);
+
+ dss_runtime_put();
+
+ /*
+ * OMAP4 ERRATUM xxxx: Mstandby and disconnect protocol issue
+ * Workaround:
+ * Restore L3_1 amd L3_2 CD to HW_AUTO, when DSS module idles.
+ */
+ if (cpu_is_omap44xx()) {
+ clkdm_allow_idle(l3_1_clkdm);
+ clkdm_allow_idle(l3_2_clkdm);
+ }
+
+ }
+
+ mutex_unlock(&dispc.runtime_lock);
+}
+
+
bool dispc_go_busy(enum omap_channel channel)
{
int bit;
@@ -505,8 +640,6 @@ void dispc_go(enum omap_channel channel)
int bit;
bool enable_bit, go_bit;
- enable_clocks(1);
-
if (channel == OMAP_DSS_CHANNEL_LCD ||
channel == OMAP_DSS_CHANNEL_LCD2)
bit = 0; /* LCDENABLE */
@@ -520,7 +653,7 @@ void dispc_go(enum omap_channel channel)
enable_bit = REG_GET(DISPC_CONTROL, bit, bit) == 1;
if (!enable_bit)
- goto end;
+ return;
if (channel == OMAP_DSS_CHANNEL_LCD ||
channel == OMAP_DSS_CHANNEL_LCD2)
@@ -535,7 +668,7 @@ void dispc_go(enum omap_channel channel)
if (go_bit) {
DSSERR("GO bit not down for channel %d\n", channel);
- goto end;
+ return;
}
DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" :
@@ -545,8 +678,6 @@ void dispc_go(enum omap_channel channel)
REG_FLD_MOD(DISPC_CONTROL2, 1, bit, bit);
else
REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit);
-end:
- enable_clocks(0);
}
static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value)
@@ -585,105 +716,227 @@ static void _dispc_write_firv2_reg(enum omap_plane plane, int reg, u32 value)
dispc_write_reg(DISPC_OVL_FIR_COEF_V2(plane, reg), value);
}
-static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup,
- int vscaleup, int five_taps,
- enum omap_color_component color_comp)
+static const struct dispc_hv_coef *
+dispc_get_scaling_coef(u32 inc, bool five_taps)
{
- /* Coefficients for horizontal up-sampling */
- static const struct dispc_h_coef coef_hup[8] = {
- { 0, 0, 128, 0, 0 },
- { -1, 13, 124, -8, 0 },
- { -2, 30, 112, -11, -1 },
- { -5, 51, 95, -11, -2 },
- { 0, -9, 73, 73, -9 },
- { -2, -11, 95, 51, -5 },
- { -1, -11, 112, 30, -2 },
- { 0, -8, 124, 13, -1 },
+ static const struct dispc_hv_coef coef3_M8[8] = {
+ { 0, 0, 128, 0, 0 },
+ { 0, 2, 123, 3, 0 },
+ { 0, 5, 111, 12, 0 },
+ { 0, 7, 89, 32, 0 },
+ { 0, 64, 64, 0, 0 },
+ { 0, 32, 89, 7, 0 },
+ { 0, 12, 111, 5, 0 },
+ { 0, 3, 123, 2, 0 },
};
- /* Coefficients for vertical up-sampling */
- static const struct dispc_v_coef coef_vup_3tap[8] = {
- { 0, 0, 128, 0, 0 },
- { 0, 3, 123, 2, 0 },
- { 0, 12, 111, 5, 0 },
- { 0, 32, 89, 7, 0 },
- { 0, 0, 64, 64, 0 },
- { 0, 7, 89, 32, 0 },
- { 0, 5, 111, 12, 0 },
- { 0, 2, 123, 3, 0 },
+ static const struct dispc_hv_coef coef3_M16[8] = {
+ { 0, 36, 56, 36, 0 },
+ { 0, 31, 57, 40, 0 },
+ { 0, 27, 56, 45, 0 },
+ { 0, 23, 55, 50, 0 },
+ { 0, 55, 55, 18, 0 },
+ { 0, 50, 55, 23, 0 },
+ { 0, 45, 56, 27, 0 },
+ { 0, 40, 57, 31, 0 },
};
- static const struct dispc_v_coef coef_vup_5tap[8] = {
- { 0, 0, 128, 0, 0 },
- { -1, 13, 124, -8, 0 },
- { -2, 30, 112, -11, -1 },
- { -5, 51, 95, -11, -2 },
- { 0, -9, 73, 73, -9 },
- { -2, -11, 95, 51, -5 },
- { -1, -11, 112, 30, -2 },
- { 0, -8, 124, 13, -1 },
+ static const struct dispc_hv_coef coef_M8[8] = {
+ { 0, 0, 128, 0, 0 },
+ { 0, -8, 124, 13, -1 },
+ { -1, -11, 112, 30, -2 },
+ { -2, -11, 95, 51, -5 },
+ { -9, 73, 73, -9, 0 },
+ { -5, 51, 95, -11, -2 },
+ { -2, 30, 112, -11, -1 },
+ { -1, 13, 124, -8, 0 },
};
- /* Coefficients for horizontal down-sampling */
- static const struct dispc_h_coef coef_hdown[8] = {
- { 0, 36, 56, 36, 0 },
- { 4, 40, 55, 31, -2 },
- { 8, 44, 54, 27, -5 },
- { 12, 48, 53, 22, -7 },
- { -9, 17, 52, 51, 17 },
- { -7, 22, 53, 48, 12 },
- { -5, 27, 54, 44, 8 },
- { -2, 31, 55, 40, 4 },
+ static const struct dispc_hv_coef coef_M9[8] = {
+ { 8, -8, 128, -8, 8 },
+ { 14, -21, 126, 8, 1 },
+ { 17, -27, 117, 30, -9 },
+ { 17, -30, 103, 56, -18 },
+ { -26, 83, 83, -26, 14 },
+ { -18, 56, 103, -30, 17 },
+ { -9, 30, 117, -27, 17 },
+ { 1, 8, 126, -21, 14 },
};
- /* Coefficients for vertical down-sampling */
- static const struct dispc_v_coef coef_vdown_3tap[8] = {
- { 0, 36, 56, 36, 0 },
- { 0, 40, 57, 31, 0 },
- { 0, 45, 56, 27, 0 },
- { 0, 50, 55, 23, 0 },
- { 0, 18, 55, 55, 0 },
- { 0, 23, 55, 50, 0 },
- { 0, 27, 56, 45, 0 },
- { 0, 31, 57, 40, 0 },
+ static const struct dispc_hv_coef coef_M10[8] = {
+ { -2, 2, 128, 2, -2 },
+ { 5, -12, 125, 20, -10 },
+ { 11, -22, 116, 41, -18 },
+ { 15, -27, 102, 62, -24 },
+ { -28, 83, 83, -28, 18 },
+ { -24, 62, 102, -27, 15 },
+ { -18, 41, 116, -22, 11 },
+ { -10, 20, 125, -12, 5 },
};
- static const struct dispc_v_coef coef_vdown_5tap[8] = {
- { 0, 36, 56, 36, 0 },
- { 4, 40, 55, 31, -2 },
- { 8, 44, 54, 27, -5 },
- { 12, 48, 53, 22, -7 },
- { -9, 17, 52, 51, 17 },
- { -7, 22, 53, 48, 12 },
- { -5, 27, 54, 44, 8 },
- { -2, 31, 55, 40, 4 },
+ static const struct dispc_hv_coef coef_M11[8] = {
+ { -12, 12, 128, 12, -12 },
+ { -4, -3, 124, 30, -19 },
+ { 3, -15, 115, 49, -24 },
+ { 9, -22, 101, 67, -27 },
+ { -26, 83, 83, -26, 14 },
+ { -27, 67, 101, -22, 9 },
+ { -24, 49, 115, -15, 3 },
+ { -19, 30, 124, -3, -4 },
};
- const struct dispc_h_coef *h_coef;
- const struct dispc_v_coef *v_coef;
- int i;
+ static const struct dispc_hv_coef coef_M12[8] = {
+ { -19, 21, 124, 21, -19 },
+ { -12, 6, 120, 38, -24 },
+ { -6, -7, 112, 55, -26 },
+ { 1, -16, 98, 70, -25 },
+ { -21, 82, 82, -21, 6 },
+ { -25, 70, 98, -16, 1 },
+ { -26, 55, 112, -7, -6 },
+ { -24, 38, 120, 6, -12 },
+ };
- if (hscaleup)
- h_coef = coef_hup;
- else
- h_coef = coef_hdown;
+ static const struct dispc_hv_coef coef_M13[8] = {
+ { -22, 27, 118, 27, -22 },
+ { -18, 13, 115, 43, -25 },
+ { -12, 0, 107, 58, -25 },
+ { -6, -10, 95, 71, -22 },
+ { -17, 81, 81, -17, 0 },
+ { -22, 71, 95, -10, -6 },
+ { -25, 58, 107, 0, -12 },
+ { -25, 43, 115, 13, -18 },
+ };
- if (vscaleup)
- v_coef = five_taps ? coef_vup_5tap : coef_vup_3tap;
- else
- v_coef = five_taps ? coef_vdown_5tap : coef_vdown_3tap;
+ static const struct dispc_hv_coef coef_M14[8] = {
+ { -23, 32, 110, 32, -23 },
+ { -20, 18, 108, 46, -24 },
+ { -16, 6, 101, 59, -22 },
+ { -11, -4, 91, 70, -18 },
+ { -11, 78, 78, -11, -6 },
+ { -18, 70, 91, -4, -11 },
+ { -22, 59, 101, 6, -16 },
+ { -24, 46, 108, 18, -20 },
+ };
+
+ static const struct dispc_hv_coef coef_M16[8] = {
+ { -20, 37, 94, 37, -20 },
+ { -21, 26, 93, 48, -18 },
+ { -19, 15, 88, 58, -14 },
+ { -17, 6, 82, 66, -9 },
+ { -2, 73, 73, -2, -14 },
+ { -9, 66, 82, 6, -17 },
+ { -14, 58, 88, 15, -19 },
+ { -18, 48, 93, 26, -21 },
+ };
+
+ static const struct dispc_hv_coef coef_M19[8] = {
+ { -12, 38, 76, 38, -12 },
+ { -14, 31, 72, 47, -8 },
+ { -16, 22, 73, 53, -4 },
+ { -16, 15, 69, 59, 1 },
+ { 8, 64, 64, 8, -16 },
+ { 1, 59, 69, 15, -16 },
+ { -4, 53, 73, 22, -16 },
+ { -9, 47, 72, 31, -13 },
+ };
+
+ static const struct dispc_hv_coef coef_M22[8] = {
+ { -6, 37, 66, 37, -6 },
+ { -8, 32, 61, 44, -1 },
+ { -11, 25, 63, 48, 3 },
+ { -13, 19, 61, 53, 8 },
+ { 13, 58, 58, 13, -14 },
+ { 8, 53, 61, 19, -13 },
+ { 3, 48, 63, 25, -11 },
+ { -2, 44, 61, 32, -7 },
+ };
+
+ static const struct dispc_hv_coef coef_M26[8] = {
+ { 1, 36, 54, 36, 1 },
+ { -2, 31, 55, 40, 4 },
+ { -5, 27, 54, 44, 8 },
+ { -8, 22, 53, 48, 13 },
+ { 18, 51, 51, 18, -10 },
+ { 13, 48, 53, 22, -8 },
+ { 8, 44, 54, 27, -5 },
+ { 4, 40, 55, 31, -2 },
+ };
+
+ static const struct dispc_hv_coef coef_M32[8] = {
+ { 7, 34, 46, 34, 7 },
+ { 4, 31, 46, 37, 10 },
+ { 1, 27, 46, 39, 14 },
+ { -1, 24, 46, 42, 17 },
+ { 21, 45, 45, 21, -4 },
+ { 17, 42, 46, 24, -1 },
+ { 14, 39, 46, 28, 1 },
+ { 10, 37, 46, 31, 4 },
+ };
+
+ inc >>= 7; /* /= 128 */
+ if (five_taps) {
+ if (inc > 26)
+ return coef_M32;
+ if (inc > 22)
+ return coef_M26;
+ if (inc > 19)
+ return coef_M22;
+ if (inc > 16)
+ return coef_M19;
+ if (inc > 14)
+ return coef_M16;
+ if (inc > 13)
+ return coef_M14;
+ if (inc > 12)
+ return coef_M13;
+ if (inc > 11)
+ return coef_M12;
+ if (inc > 10)
+ return coef_M11;
+ if (inc > 9)
+ return coef_M10;
+ if (inc > 8)
+ return coef_M9;
+ /* reduce blockiness when upscaling much */
+ if (inc > 3)
+ return coef_M8;
+ if (inc > 2)
+ return coef_M11;
+ if (inc > 1)
+ return coef_M16;
+ return coef_M19;
+ } else {
+ if (inc > 14)
+ return coef3_M16;
+ /* reduce blockiness when upscaling much */
+ if (inc > 3)
+ return coef3_M8;
+ return coef3_M16;
+ }
+}
+
+static void _dispc_set_scale_coef(enum omap_plane plane, int hinc,
+ int vinc, bool five_taps,
+ enum omap_color_component color_comp)
+{
+ const struct dispc_hv_coef *h_coef;
+ const struct dispc_hv_coef *v_coef;
+ int i;
+
+ h_coef = dispc_get_scaling_coef(hinc, true);
+ v_coef = dispc_get_scaling_coef(vinc, five_taps);
for (i = 0; i < 8; i++) {
u32 h, hv;
- h = FLD_VAL(h_coef[i].hc0, 7, 0)
- | FLD_VAL(h_coef[i].hc1, 15, 8)
- | FLD_VAL(h_coef[i].hc2, 23, 16)
- | FLD_VAL(h_coef[i].hc3, 31, 24);
- hv = FLD_VAL(h_coef[i].hc4, 7, 0)
- | FLD_VAL(v_coef[i].vc0, 15, 8)
- | FLD_VAL(v_coef[i].vc1, 23, 16)
- | FLD_VAL(v_coef[i].vc2, 31, 24);
+ h = FLD_VAL(h_coef[i].hc0_vc00, 7, 0)
+ | FLD_VAL(h_coef[i].hc1_vc0, 15, 8)
+ | FLD_VAL(h_coef[i].hc2_vc1, 23, 16)
+ | FLD_VAL(h_coef[i].hc3_vc2, 31, 24);
+ hv = FLD_VAL(h_coef[i].hc4_vc22, 7, 0)
+ | FLD_VAL(v_coef[i].hc1_vc0, 15, 8)
+ | FLD_VAL(v_coef[i].hc2_vc1, 23, 16)
+ | FLD_VAL(v_coef[i].hc3_vc2, 31, 24);
if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) {
_dispc_write_firh_reg(plane, i, h);
@@ -692,14 +945,13 @@ static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup,
_dispc_write_firh2_reg(plane, i, h);
_dispc_write_firhv2_reg(plane, i, hv);
}
-
}
if (five_taps) {
for (i = 0; i < 8; i++) {
u32 v;
- v = FLD_VAL(v_coef[i].vc00, 7, 0)
- | FLD_VAL(v_coef[i].vc22, 15, 8);
+ v = FLD_VAL(v_coef[i].hc0_vc00, 7, 0)
+ | FLD_VAL(v_coef[i].hc4_vc22, 15, 8);
if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y)
_dispc_write_firv_reg(plane, i, v);
else
@@ -708,49 +960,22 @@ static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup,
}
}
-static void _dispc_setup_color_conv_coef(void)
+void _dispc_setup_color_conv_coef(enum omap_plane plane,
+ const struct omap_dss_cconv_coefs *ct)
{
- const struct color_conv_coef {
- int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb;
- int full_range;
- } ctbl_bt601_5 = {
- 298, 409, 0, 298, -208, -100, 298, 0, 517, 0,
- };
-
- const struct color_conv_coef *ct;
+ BUG_ON(plane < OMAP_DSS_VIDEO1 || plane > OMAP_DSS_VIDEO3);
#define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0))
- ct = &ctbl_bt601_5;
-
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 0),
- CVAL(ct->rcr, ct->ry));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 1),
- CVAL(ct->gy, ct->rcb));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 2),
- CVAL(ct->gcb, ct->gcr));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 3),
- CVAL(ct->bcr, ct->by));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 4),
- CVAL(0, ct->bcb));
-
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 0),
- CVAL(ct->rcr, ct->ry));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 1),
- CVAL(ct->gy, ct->rcb));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 2),
- CVAL(ct->gcb, ct->gcr));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 3),
- CVAL(ct->bcr, ct->by));
- dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 4),
- CVAL(0, ct->bcb));
+ dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 0), CVAL(ct->rcr, ct->ry));
+ dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 1), CVAL(ct->gy, ct->rcb));
+ dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 2), CVAL(ct->gcb, ct->gcr));
+ dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 3), CVAL(ct->bcr, ct->by));
+ dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 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(plane), ct->full_range, 11, 11);
}
@@ -825,8 +1050,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)
@@ -847,11 +1076,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;
@@ -901,8 +1130,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;
@@ -920,7 +1151,7 @@ static void _dispc_set_color_mode(enum omap_plane plane,
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1);
}
-static void _dispc_set_channel_out(enum omap_plane plane,
+void dispc_set_channel_out(enum omap_plane plane,
enum omap_channel channel)
{
int shift;
@@ -933,6 +1164,7 @@ static 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:
@@ -973,14 +1205,13 @@ void dispc_set_burst_size(enum omap_plane plane,
int shift;
u32 val;
- enable_clocks(1);
-
switch (plane) {
case OMAP_DSS_GFX:
shift = 6;
break;
case OMAP_DSS_VIDEO1:
case OMAP_DSS_VIDEO2:
+ case OMAP_DSS_VIDEO3:
shift = 14;
break;
default:
@@ -991,8 +1222,6 @@ void dispc_set_burst_size(enum omap_plane plane,
val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
val = FLD_MOD(val, burst_size, shift+1, shift);
dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);
-
- enable_clocks(0);
}
void dispc_enable_gamma_table(bool enable)
@@ -1009,6 +1238,63 @@ 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);
+}
+
+void dispc_enable_cpr(enum omap_channel channel, bool enable)
+{
+ u16 reg;
+
+ if (channel == OMAP_DSS_CHANNEL_LCD)
+ reg = DISPC_CONFIG;
+ else if (channel == OMAP_DSS_CHANNEL_LCD2)
+ reg = DISPC_CONFIG2;
+ else
+ return;
+
+ REG_FLD_MOD(reg, enable, 15, 15);
+}
+
+void dispc_set_cpr_coef(enum omap_channel channel,
+ struct omap_dss_cpr_coefs *coefs)
+{
+ u32 coef_r, coef_g, coef_b;
+
+ if (channel != OMAP_DSS_CHANNEL_LCD && channel != OMAP_DSS_CHANNEL_LCD2)
+ return;
+
+ coef_r = FLD_VAL(coefs->rr, 31, 22) | FLD_VAL(coefs->rg, 20, 11) |
+ FLD_VAL(coefs->rb, 9, 0);
+ coef_g = FLD_VAL(coefs->gr, 31, 22) | FLD_VAL(coefs->gg, 20, 11) |
+ FLD_VAL(coefs->gb, 9, 0);
+ coef_b = FLD_VAL(coefs->br, 31, 22) | FLD_VAL(coefs->bg, 20, 11) |
+ FLD_VAL(coefs->bb, 9, 0);
+
+ dispc_write_reg(DISPC_CPR_COEF_R(channel), coef_r);
+ dispc_write_reg(DISPC_CPR_COEF_G(channel), coef_g);
+ dispc_write_reg(DISPC_CPR_COEF_B(channel), coef_b);
+}
+
static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable)
{
u32 val;
@@ -1029,9 +1315,7 @@ void dispc_enable_replication(enum omap_plane plane, bool enable)
else
bit = 10;
- enable_clocks(1);
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, bit, bit);
- enable_clocks(0);
}
void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height)
@@ -1039,9 +1323,7 @@ void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height)
u32 val;
BUG_ON((width > (1 << 11)) || (height > (1 << 11)));
val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
- enable_clocks(1);
dispc_write_reg(DISPC_SIZE_MGR(channel), val);
- enable_clocks(0);
}
void dispc_set_digit_size(u16 width, u16 height)
@@ -1049,9 +1331,7 @@ void dispc_set_digit_size(u16 width, u16 height)
u32 val;
BUG_ON((width > (1 << 11)) || (height > (1 << 11)));
val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
- enable_clocks(1);
dispc_write_reg(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT), val);
- enable_clocks(0);
}
static void dispc_read_plane_fifo_sizes(void)
@@ -1060,8 +1340,6 @@ static void dispc_read_plane_fifo_sizes(void)
int plane;
u8 start, end;
- enable_clocks(1);
-
dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end);
for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) {
@@ -1069,8 +1347,6 @@ static void dispc_read_plane_fifo_sizes(void)
start, end);
dispc.fifo_size[plane] = size;
}
-
- enable_clocks(0);
}
u32 dispc_get_plane_fifo_size(enum omap_plane plane)
@@ -1085,8 +1361,6 @@ void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high)
dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end);
dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end);
- enable_clocks(1);
-
DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n",
plane,
REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane),
@@ -1095,21 +1369,18 @@ void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high)
hi_start, hi_end),
low, high);
+ /* preload to high threshold to avoid FIFO underflow */
+ dispc_write_reg(DISPC_OVL_PRELOAD(plane), min(high, 0xfffu));
+
dispc_write_reg(DISPC_OVL_FIFO_THRESHOLD(plane),
FLD_VAL(high, hi_start, hi_end) |
FLD_VAL(low, lo_start, lo_end));
-
- enable_clocks(0);
}
void dispc_enable_fifomerge(bool enable)
{
- enable_clocks(1);
-
DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled");
REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14);
-
- enable_clocks(0);
}
static void _dispc_set_fir(enum omap_plane plane,
@@ -1209,8 +1480,9 @@ static void _dispc_set_scaling_common(enum omap_plane plane,
int accu0 = 0;
int accu1 = 0;
u32 l;
+ u16 y_adjust = color_mode == OMAP_DSS_COLOR_NV12 ? 2 : 0;
- _dispc_set_scale_param(plane, orig_width, orig_height,
+ _dispc_set_scale_param(plane, orig_width, orig_height - y_adjust,
out_width, out_height, five_taps,
rotation, DISPC_COLOR_COMPONENT_RGB_Y);
l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
@@ -1262,6 +1534,7 @@ static void _dispc_set_scaling_uv(enum omap_plane plane,
{
int scale_x = out_width != orig_width;
int scale_y = out_height != orig_height;
+ u16 y_adjust = 0;
if (!dss_has_feature(FEAT_HANDLE_UV_SEPARATE))
return;
@@ -1278,6 +1551,7 @@ static void _dispc_set_scaling_uv(enum omap_plane plane,
orig_height >>= 1;
/* UV is subsampled by 2 horz.*/
orig_width >>= 1;
+ y_adjust = 1;
break;
case OMAP_DSS_COLOR_YUV2:
case OMAP_DSS_COLOR_UYVY:
@@ -1301,7 +1575,7 @@ static void _dispc_set_scaling_uv(enum omap_plane plane,
if (out_height != orig_height)
scale_y = true;
- _dispc_set_scale_param(plane, orig_width, orig_height,
+ _dispc_set_scale_param(plane, orig_width, orig_height - y_adjust,
out_width, out_height, five_taps,
rotation, DISPC_COLOR_COMPONENT_UV);
@@ -1341,7 +1615,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;
@@ -1391,6 +1666,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)
@@ -1439,6 +1724,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,
@@ -1529,7 +1836,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;
@@ -1542,6 +1849,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;
@@ -1571,10 +1887,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;
@@ -1671,6 +1987,18 @@ static unsigned long calc_fclk_five_taps(enum omap_channel channel, u16 width,
/* FIXME venc pclk? */
u64 tmp, pclk = dispc_pclk_rate(channel);
+ if (cpu_is_omap44xx()) {
+ /* do conservative TRM value on OMAP4 ES1.0 */
+ if (omap_rev() == OMAP4430_REV_ES1_0)
+ return pclk * DIV_ROUND_UP(width, out_width) *
+ DIV_ROUND_UP(height, out_height);
+
+ /* since 4430 ES2.0, fclk requirement only depends on width */
+ pclk *= max(width, out_width);
+ do_div(pclk, out_width);
+ return pclk;
+ }
+
if (height > out_height) {
/* FIXME get real display PPL */
unsigned int ppl = 800;
@@ -1706,6 +2034,11 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width,
{
unsigned int hf, vf;
+ /* on OMAP4 three-tap and five-tap clock requirements are the same */
+ if (cpu_is_omap44xx())
+ return calc_fclk_five_taps(channel, width, height, out_width,
+ out_height, 0);
+
/*
* FIXME how to determine the 'A' factor
* for the no downscaling case ?
@@ -1729,27 +2062,188 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width,
return dispc_pclk_rate(channel) * vf * hf;
}
-void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel_out)
+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,
+ enum omap_dss_rotation_type type,
+ 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)
{
- enable_clocks(1);
- _dispc_set_channel_out(plane, channel_out);
- enable_clocks(0);
+ 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();
+ u16 y_decim_limit = type == OMAP_DSS_ROT_TILER ? 2 : 16;
+
+ /* 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;
+ } else {
+ if (max_y_decim > y_decim_limit)
+ max_y_decim = y_decim_limit;
+ }
+
+ /*
+ * 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 * maxdownscale < in_width ||
+ out_height * maxdownscale < in_height)
+ 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;
+
+ /*
+ * 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);
+
+ /* 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 %u*%u to %u*%u, "
+ "required fclk rate = %lu Hz, "
+ "current = %lu Hz\n",
+ width, height, out_width, out_height,
+ 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;
}
-static int _dispc_setup_plane(enum omap_plane plane,
+int dispc_setup_plane(enum omap_plane plane,
u32 paddr, u16 screen_width,
u16 pos_x, u16 pos_y,
u16 width, u16 height,
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, int mirror,
+ 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;
@@ -1757,6 +2251,18 @@ static 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;
+ u32 fifo_high, fifo_low;
+
+ 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, x_decim, height, y_decim,
+ out_width, out_height,
+ ilace, color_mode,
+ rotation, mirror, channel, five_taps ? 5 : 3);
if (paddr == 0)
return -EINVAL;
@@ -1778,59 +2284,41 @@ static 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) {
@@ -1851,17 +2339,69 @@ static 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);
@@ -1879,8 +2419,8 @@ static 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);
@@ -1895,17 +2435,30 @@ static 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);
+ if (cpu_is_omap44xx()) {
+ fifo_low = dispc_calculate_threshold(plane, paddr + offset0,
+ puv_addr + offset0, width, height,
+ row_inc, pix_inc);
+ fifo_high = dispc_get_plane_fifo_size(plane) - 1;
+ dispc_setup_plane_fifo(plane, fifo_low, fifo_high);
+ }
+
return 0;
}
-static void _dispc_enable_plane(enum omap_plane plane, bool enable)
+int dispc_enable_plane(enum omap_plane plane, bool enable)
{
+ DSSDBG("dispc_enable_plane %d, %d\n", plane, enable);
+
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 0, 0);
+
+ return 0;
}
static void dispc_disable_isr(void *data, u32 mask)
@@ -1922,6 +2475,17 @@ static void _enable_lcd_out(enum omap_channel channel, bool enable)
REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0);
}
+void omap_dispc_set_irq_type(int channel, enum omap_dispc_irq_type type)
+{
+ if (type == OMAP_DISPC_IRQ_TYPE_VSYNC) {
+ dispc.channel_irq[channel] = channel == OMAP_DSS_CHANNEL_LCD2 ?
+ DISPC_IRQ_VSYNC2 : DISPC_IRQ_VSYNC;
+ } else {
+ dispc.channel_irq[channel] = channel == OMAP_DSS_CHANNEL_LCD2 ?
+ DISPC_IRQ_FRAMEDONE2 : DISPC_IRQ_FRAMEDONE;
+ }
+}
+
static void dispc_enable_lcd_out(enum omap_channel channel, bool enable)
{
struct completion frame_done_completion;
@@ -1929,8 +2493,6 @@ static void dispc_enable_lcd_out(enum omap_channel channel, bool enable)
int r;
u32 irq;
- enable_clocks(1);
-
/* When we disable LCD output, we need to wait until frame is done.
* Otherwise the DSS is still working, and turning off the clocks
* prevents DSS from going to OFF mode */
@@ -1938,8 +2500,7 @@ static void dispc_enable_lcd_out(enum omap_channel channel, bool enable)
REG_GET(DISPC_CONTROL2, 0, 0) :
REG_GET(DISPC_CONTROL, 0, 0);
- irq = channel == OMAP_DSS_CHANNEL_LCD2 ? DISPC_IRQ_FRAMEDONE2 :
- DISPC_IRQ_FRAMEDONE;
+ irq = dispc.channel_irq[channel];
if (!enable && is_on) {
init_completion(&frame_done_completion);
@@ -1964,8 +2525,6 @@ static void dispc_enable_lcd_out(enum omap_channel channel, bool enable)
if (r)
DSSERR("failed to unregister FRAMEDONE isr\n");
}
-
- enable_clocks(0);
}
static void _enable_digit_out(bool enable)
@@ -1973,17 +2532,13 @@ static void _enable_digit_out(bool enable)
REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1);
}
-static void dispc_enable_digit_out(bool enable)
+static void dispc_enable_digit_out(enum omap_display_type type, bool enable)
{
struct completion frame_done_completion;
int r;
- enable_clocks(1);
-
- if (REG_GET(DISPC_CONTROL, 1, 1) == enable) {
- enable_clocks(0);
+ if (REG_GET(DISPC_CONTROL, 1, 1) == enable)
return;
- }
if (enable) {
unsigned long flags;
@@ -2002,7 +2557,8 @@ static void dispc_enable_digit_out(bool enable)
init_completion(&frame_done_completion);
r = omap_dispc_register_isr(dispc_disable_isr, &frame_done_completion,
- DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD);
+ DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
+ | DISPC_IRQ_FRAMEDONETV);
if (r)
DSSERR("failed to register EVSYNC isr\n");
@@ -2015,13 +2571,17 @@ static void dispc_enable_digit_out(bool enable)
msecs_to_jiffies(100)))
DSSERR("timeout waiting for EVSYNC\n");
- if (!wait_for_completion_timeout(&frame_done_completion,
- msecs_to_jiffies(100)))
- DSSERR("timeout waiting for EVSYNC\n");
+ /* Don't wait for the odd field in the case of HDMI */
+ if (type != OMAP_DISPLAY_TYPE_HDMI) {
+ if (!wait_for_completion_timeout(&frame_done_completion,
+ msecs_to_jiffies(100)))
+ DSSERR("timeout waiting for EVSYNC\n");
+ }
r = omap_dispc_unregister_isr(dispc_disable_isr,
&frame_done_completion,
- DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD);
+ DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
+ | DISPC_IRQ_FRAMEDONETV);
if (r)
DSSERR("failed to unregister EVSYNC isr\n");
@@ -2031,12 +2591,12 @@ 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);
}
-
- enable_clocks(0);
}
bool dispc_is_channel_enabled(enum omap_channel channel)
@@ -2051,13 +2611,14 @@ bool dispc_is_channel_enabled(enum omap_channel channel)
BUG();
}
-void dispc_enable_channel(enum omap_channel channel, bool enable)
+void dispc_enable_channel(enum omap_channel channel,
+ enum omap_display_type type, bool enable)
{
if (channel == OMAP_DSS_CHANNEL_LCD ||
channel == OMAP_DSS_CHANNEL_LCD2)
dispc_enable_lcd_out(channel, enable);
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
- dispc_enable_digit_out(enable);
+ dispc_enable_digit_out(type, enable);
else
BUG();
}
@@ -2067,9 +2628,7 @@ void dispc_lcd_enable_signal_polarity(bool act_high)
if (!dss_has_feature(FEAT_LCDENABLEPOL))
return;
- enable_clocks(1);
REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29);
- enable_clocks(0);
}
void dispc_lcd_enable_signal(bool enable)
@@ -2077,9 +2636,7 @@ void dispc_lcd_enable_signal(bool enable)
if (!dss_has_feature(FEAT_LCDENABLESIGNAL))
return;
- enable_clocks(1);
REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28);
- enable_clocks(0);
}
void dispc_pck_free_enable(bool enable)
@@ -2087,19 +2644,15 @@ void dispc_pck_free_enable(bool enable)
if (!dss_has_feature(FEAT_PCKFREEENABLE))
return;
- enable_clocks(1);
REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27);
- enable_clocks(0);
}
void dispc_enable_fifohandcheck(enum omap_channel channel, bool enable)
{
- enable_clocks(1);
if (channel == OMAP_DSS_CHANNEL_LCD2)
REG_FLD_MOD(DISPC_CONFIG2, enable ? 1 : 0, 16, 16);
else
REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16);
- enable_clocks(0);
}
@@ -2122,27 +2675,21 @@ void dispc_set_lcd_display_type(enum omap_channel channel,
return;
}
- enable_clocks(1);
if (channel == OMAP_DSS_CHANNEL_LCD2)
REG_FLD_MOD(DISPC_CONTROL2, mode, 3, 3);
else
REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3);
- enable_clocks(0);
}
void dispc_set_loadmode(enum omap_dss_load_mode mode)
{
- enable_clocks(1);
REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1);
- enable_clocks(0);
}
void dispc_set_default_color(enum omap_channel channel, u32 color)
{
- enable_clocks(1);
dispc_write_reg(DISPC_DEFAULT_COLOR(channel), color);
- enable_clocks(0);
}
u32 dispc_get_default_color(enum omap_channel channel)
@@ -2153,9 +2700,7 @@ u32 dispc_get_default_color(enum omap_channel channel)
channel != OMAP_DSS_CHANNEL_LCD &&
channel != OMAP_DSS_CHANNEL_LCD2);
- enable_clocks(1);
l = dispc_read_reg(DISPC_DEFAULT_COLOR(channel));
- enable_clocks(0);
return l;
}
@@ -2164,7 +2709,6 @@ void dispc_set_trans_key(enum omap_channel ch,
enum omap_dss_trans_key_type type,
u32 trans_key)
{
- enable_clocks(1);
if (ch == OMAP_DSS_CHANNEL_LCD)
REG_FLD_MOD(DISPC_CONFIG, type, 11, 11);
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
@@ -2173,14 +2717,12 @@ void dispc_set_trans_key(enum omap_channel ch,
REG_FLD_MOD(DISPC_CONFIG2, type, 11, 11);
dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key);
- enable_clocks(0);
}
void dispc_get_trans_key(enum omap_channel ch,
enum omap_dss_trans_key_type *type,
u32 *trans_key)
{
- enable_clocks(1);
if (type) {
if (ch == OMAP_DSS_CHANNEL_LCD)
*type = REG_GET(DISPC_CONFIG, 11, 11);
@@ -2194,33 +2736,27 @@ void dispc_get_trans_key(enum omap_channel ch,
if (trans_key)
*trans_key = dispc_read_reg(DISPC_TRANS_COLOR(ch));
- enable_clocks(0);
}
void dispc_enable_trans_key(enum omap_channel ch, bool enable)
{
- enable_clocks(1);
if (ch == OMAP_DSS_CHANNEL_LCD)
REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10);
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12);
else /* OMAP_DSS_CHANNEL_LCD2 */
REG_FLD_MOD(DISPC_CONFIG2, enable, 10, 10);
- enable_clocks(0);
}
void dispc_enable_alpha_blending(enum omap_channel ch, bool enable)
{
if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
return;
- enable_clocks(1);
+ /* :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);
- enable_clocks(0);
}
bool dispc_alpha_blending_enabled(enum omap_channel ch)
{
@@ -2229,16 +2765,14 @@ bool dispc_alpha_blending_enabled(enum omap_channel ch)
if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
return false;
- enable_clocks(1);
if (ch == OMAP_DSS_CHANNEL_LCD)
enabled = REG_GET(DISPC_CONFIG, 18, 18);
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();
- enable_clocks(0);
return enabled;
}
@@ -2248,7 +2782,6 @@ bool dispc_trans_key_enabled(enum omap_channel ch)
{
bool enabled;
- enable_clocks(1);
if (ch == OMAP_DSS_CHANNEL_LCD)
enabled = REG_GET(DISPC_CONFIG, 10, 10);
else if (ch == OMAP_DSS_CHANNEL_DIGIT)
@@ -2257,7 +2790,6 @@ bool dispc_trans_key_enabled(enum omap_channel ch)
enabled = REG_GET(DISPC_CONFIG2, 10, 10);
else
BUG();
- enable_clocks(0);
return enabled;
}
@@ -2285,12 +2817,10 @@ void dispc_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
return;
}
- enable_clocks(1);
if (channel == OMAP_DSS_CHANNEL_LCD2)
REG_FLD_MOD(DISPC_CONTROL2, code, 9, 8);
else
REG_FLD_MOD(DISPC_CONTROL, code, 9, 8);
- enable_clocks(0);
}
void dispc_set_parallel_interface_mode(enum omap_channel channel,
@@ -2322,8 +2852,6 @@ void dispc_set_parallel_interface_mode(enum omap_channel channel,
return;
}
- enable_clocks(1);
-
if (channel == OMAP_DSS_CHANNEL_LCD2) {
l = dispc_read_reg(DISPC_CONTROL2);
l = FLD_MOD(l, stallmode, 11, 11);
@@ -2335,8 +2863,6 @@ void dispc_set_parallel_interface_mode(enum omap_channel channel,
l = FLD_MOD(l, gpout1, 16, 16);
dispc_write_reg(DISPC_CONTROL, l);
}
-
- enable_clocks(0);
}
static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp,
@@ -2389,10 +2915,8 @@ static void _dispc_set_lcd_timings(enum omap_channel channel, int hsw,
FLD_VAL(vbp, 31, 20);
}
- enable_clocks(1);
dispc_write_reg(DISPC_TIMING_H(channel), timing_h);
dispc_write_reg(DISPC_TIMING_V(channel), timing_v);
- enable_clocks(0);
}
/* change name to mode? */
@@ -2435,10 +2959,8 @@ static void dispc_set_lcd_divisor(enum omap_channel channel, u16 lck_div,
BUG_ON(lck_div < 1);
BUG_ON(pck_div < 2);
- enable_clocks(1);
dispc_write_reg(DISPC_DIVISORo(channel),
FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0));
- enable_clocks(0);
}
static void dispc_get_lcd_divisor(enum omap_channel channel, int *lck_div,
@@ -2457,7 +2979,7 @@ unsigned long dispc_fclk_rate(void)
switch (dss_get_dispc_clk_source()) {
case OMAP_DSS_CLK_SRC_FCK:
- r = dss_clk_get_rate(DSS_CLK_FCK);
+ r = clk_get_rate(dispc.dss_clk);
break;
case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:
dsidev = dsi_get_dsidev_from_id(0);
@@ -2487,7 +3009,7 @@ unsigned long dispc_lclk_rate(enum omap_channel channel)
switch (dss_get_lcd_clk_source(channel)) {
case OMAP_DSS_CLK_SRC_FCK:
- r = dss_clk_get_rate(DSS_CLK_FCK);
+ r = clk_get_rate(dispc.dss_clk);
break;
case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:
dsidev = dsi_get_dsidev_from_id(0);
@@ -2510,13 +3032,23 @@ unsigned long dispc_pclk_rate(enum omap_channel channel)
unsigned long r;
u32 l;
- l = dispc_read_reg(DISPC_DIVISORo(channel));
+ if (channel == OMAP_DSS_CHANNEL_LCD ||
+ channel == OMAP_DSS_CHANNEL_LCD2) {
+ l = dispc_read_reg(DISPC_DIVISORo(channel));
+
+ pcd = FLD_GET(l, 7, 0);
- pcd = FLD_GET(l, 7, 0);
+ r = dispc_lclk_rate(channel);
- r = dispc_lclk_rate(channel);
+ return r / pcd;
+ } else {
+ struct omap_overlay_manager *mgr;
+ mgr = omap_dss_get_overlay_manager(channel);
+ if (!mgr || !mgr->device)
+ return 0;
- return r / pcd;
+ return mgr->device->panel.timings.pixel_clock * 1000;
+ }
}
void dispc_dump_clocks(struct seq_file *s)
@@ -2526,7 +3058,8 @@ void dispc_dump_clocks(struct seq_file *s)
enum omap_dss_clk_source dispc_clk_src = dss_get_dispc_clk_source();
enum omap_dss_clk_source lcd_clk_src;
- enable_clocks(1);
+ if (dispc_runtime_get())
+ return;
seq_printf(s, "- DISPC -\n");
@@ -2574,7 +3107,8 @@ void dispc_dump_clocks(struct seq_file *s)
seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD2), pcd);
}
- enable_clocks(0);
+
+ dispc_runtime_put();
}
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
@@ -2612,6 +3146,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);
@@ -2627,9 +3165,11 @@ 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))
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ if (dispc_runtime_get())
+ return;
DUMPREG(DISPC_REVISION);
DUMPREG(DISPC_SYSCONFIG);
@@ -2649,7 +3189,8 @@ void dispc_dump_regs(struct seq_file *s)
DUMPREG(DISPC_TIMING_V(OMAP_DSS_CHANNEL_LCD));
DUMPREG(DISPC_POL_FREQ(OMAP_DSS_CHANNEL_LCD));
DUMPREG(DISPC_DIVISORo(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_GLOBAL_ALPHA);
+ if (dss_has_feature(FEAT_GLOBAL_ALPHA))
+ DUMPREG(DISPC_GLOBAL_ALPHA);
DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT));
DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_LCD));
if (dss_has_feature(FEAT_MGR_LCD2)) {
@@ -2680,188 +3221,82 @@ void dispc_dump_regs(struct seq_file *s)
DUMPREG(DISPC_DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD));
DUMPREG(DISPC_DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
- DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
+ if (dss_has_feature(FEAT_CPR)) {
+ DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD));
+ DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD));
+ DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD));
+ }
if (dss_has_feature(FEAT_MGR_LCD2)) {
DUMPREG(DISPC_DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2));
DUMPREG(DISPC_DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2));
DUMPREG(DISPC_DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
- DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
- }
-
- 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));
- 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));
+ if (dss_has_feature(FEAT_CPR)) {
+ DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2));
+ DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2));
+ DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2));
+ }
+ }
- 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));
- 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));
+ if (dss_has_feature(FEAT_PRELOAD))
+ DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_GFX));
- 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));
-
- DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO1));
- DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO2));
-
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ for (o = OMAP_DSS_VIDEO1; o <= OMAP_DSS_VIDEO3; o++) {
+ if (o == OMAP_DSS_VIDEO3 && !dss_has_feature(FEAT_OVL_VID3))
+ continue;
+
+ 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));
+
+ 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();
#undef DUMPREG
}
@@ -2882,9 +3317,7 @@ static void _dispc_set_pol_freq(enum omap_channel channel, bool onoff, bool rf,
l |= FLD_VAL(acbi, 11, 8);
l |= FLD_VAL(acb, 7, 0);
- enable_clocks(1);
dispc_write_reg(DISPC_POL_FREQ(channel), l);
- enable_clocks(0);
}
void dispc_set_pol_freq(enum omap_channel channel,
@@ -3005,15 +3438,11 @@ static void _omap_dispc_set_irqs(void)
mask |= isr_data->mask;
}
- enable_clocks(1);
-
old_mask = dispc_read_reg(DISPC_IRQENABLE);
/* clear the irqstatus for newly enabled irqs */
dispc_write_reg(DISPC_IRQSTATUS, (mask ^ old_mask) & mask);
dispc_write_reg(DISPC_IRQENABLE, mask);
-
- enable_clocks(0);
}
int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
@@ -3119,6 +3548,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))
@@ -3218,6 +3649,8 @@ static void dispc_error_worker(struct work_struct *work)
dispc.error_irqs = 0;
spin_unlock_irqrestore(&dispc.irq_lock, flags);
+ dispc_runtime_get();
+
if (errors & DISPC_IRQ_GFX_FIFO_UNDERFLOW) {
DSSERR("GFX_FIFO_UNDERFLOW, disabling GFX\n");
for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
@@ -3272,6 +3705,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;
@@ -3283,6 +3734,11 @@ static void dispc_error_worker(struct work_struct *work)
mgr = omap_dss_get_overlay_manager(i);
if (mgr->id == OMAP_DSS_CHANNEL_LCD) {
+ if(!mgr->device->first_vsync){
+ DSSERR("First SYNC_LOST.. ignoring \n");
+ break;
+ }
+
manager = mgr;
enable = mgr->device->state ==
OMAP_DSS_DISPLAY_ACTIVE;
@@ -3315,17 +3771,23 @@ static void dispc_error_worker(struct work_struct *work)
struct omap_overlay_manager *manager = NULL;
bool enable = false;
- DSSERR("SYNC_LOST_DIGIT, disabling TV\n");
+ pr_err_ratelimited("SYNC_LOST_DIGIT, disabling TV\n");
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
struct omap_overlay_manager *mgr;
mgr = omap_dss_get_overlay_manager(i);
if (mgr->id == OMAP_DSS_CHANNEL_DIGIT) {
+ if(!mgr->device->first_vsync){
+ DSSERR("First SYNC_LOST..TV ignoring\n");
+ }
+
manager = mgr;
enable = mgr->device->state ==
OMAP_DSS_DISPLAY_ACTIVE;
+ mgr->device->sync_lost_error = 1;
mgr->device->driver->disable(mgr->device);
+ mgr->device->sync_lost_error = 0;
break;
}
}
@@ -3361,6 +3823,11 @@ static void dispc_error_worker(struct work_struct *work)
mgr = omap_dss_get_overlay_manager(i);
if (mgr->id == OMAP_DSS_CHANNEL_LCD2) {
+ if(!mgr->device->first_vsync){
+ DSSERR("First SYNC_LOST.. ignoring \n");
+ break;
+ }
+
manager = mgr;
enable = mgr->device->state ==
OMAP_DSS_DISPLAY_ACTIVE;
@@ -3404,6 +3871,8 @@ static void dispc_error_worker(struct work_struct *work)
dispc.irq_error_mask |= errors;
_omap_dispc_set_irqs();
spin_unlock_irqrestore(&dispc.irq_lock, flags);
+
+ dispc_runtime_put();
}
int omap_dispc_wait_for_irq_timeout(u32 irqmask, unsigned long timeout)
@@ -3446,11 +3915,15 @@ int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
int r;
DECLARE_COMPLETION_ONSTACK(completion);
+ r = dispc_runtime_get();
+ if (r)
+ return r;
+
r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion,
irqmask);
if (r)
- return r;
+ goto done;
timeout = wait_for_completion_interruptible_timeout(&completion,
timeout);
@@ -3458,12 +3931,14 @@ int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask);
if (timeout == 0)
- return -ETIMEDOUT;
+ r = -ETIMEDOUT;
+ else if (timeout == -ERESTARTSYS)
+ r = timeout;
- if (timeout == -ERESTARTSYS)
- return -ERESTARTSYS;
+done:
+ dispc_runtime_put();
- return 0;
+ return r;
}
#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC
@@ -3498,7 +3973,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));
@@ -3522,13 +3998,6 @@ static void _omap_dispc_initial_config(void)
{
u32 l;
- l = dispc_read_reg(DISPC_SYSCONFIG);
- l = FLD_MOD(l, 2, 13, 12); /* MIDLEMODE: smart standby */
- l = FLD_MOD(l, 2, 4, 3); /* SIDLEMODE: smart idle */
- l = FLD_MOD(l, 1, 2, 2); /* ENWAKEUP */
- l = FLD_MOD(l, 1, 0, 0); /* AUTOIDLE */
- dispc_write_reg(DISPC_SYSCONFIG, l);
-
/* Exclusively enable DISPC_CORE_CLK and set divider to 1 */
if (dss_has_feature(FEAT_CORE_CLK_DIV)) {
l = dispc_read_reg(DISPC_DIVISOR);
@@ -3538,83 +4007,47 @@ static void _omap_dispc_initial_config(void)
dispc_write_reg(DISPC_DIVISOR, l);
}
+ /* for OMAP4 ERRATUM xxxx: Mstandby and disconnect protocol issue */
+ if (cpu_is_omap44xx()) {
+ l3_1_clkdm = clkdm_lookup("l3_1_clkdm");
+ l3_2_clkdm = clkdm_lookup("l3_2_clkdm");
+ }
+
/* FUNCGATED */
if (dss_has_feature(FEAT_FUNCGATED))
REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9);
+ REG_FLD_MOD(DISPC_CONFIG, 1, 17, 17);
+
/* L3 firewall setting: enable access to OCM RAM */
/* XXX this should be somewhere in plat-omap */
if (cpu_is_omap24xx())
__raw_writel(0x402000b0, OMAP2_L3_IO_ADDRESS(0x680050a0));
- _dispc_setup_color_conv_coef();
-
dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY);
dispc_read_plane_fifo_sizes();
}
-int dispc_enable_plane(enum omap_plane plane, bool enable)
-{
- DSSDBG("dispc_enable_plane %d, %d\n", plane, enable);
-
- enable_clocks(1);
- _dispc_enable_plane(plane, enable);
- enable_clocks(0);
-
- return 0;
-}
-
-int dispc_setup_plane(enum omap_plane plane,
- u32 paddr, u16 screen_width,
- u16 pos_x, u16 pos_y,
- u16 width, u16 height,
- u16 out_width, u16 out_height,
- enum omap_color_mode color_mode,
- bool ilace,
- 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 r = 0;
-
- 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",
- plane, paddr, screen_width, pos_x, pos_y,
- width, height,
- out_width, out_height,
- ilace, color_mode,
- rotation, mirror, channel);
-
- enable_clocks(1);
-
- r = _dispc_setup_plane(plane,
- paddr, screen_width,
- pos_x, pos_y,
- width, height,
- out_width, out_height,
- color_mode, ilace,
- rotation_type,
- rotation, mirror,
- global_alpha,
- pre_mult_alpha,
- channel, puv_addr);
-
- enable_clocks(0);
-
- return r;
-}
-
/* DISPC HW IP initialisation */
static int omap_dispchw_probe(struct platform_device *pdev)
{
u32 rev;
int r = 0;
struct resource *dispc_mem;
+ struct clk *clk;
dispc.pdev = pdev;
+ clk = clk_get(&pdev->dev, "dss_clk");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get dss_clk\n");
+ r = PTR_ERR(clk);
+ goto err_get_clk;
+ }
+
+ dispc.dss_clk = clk;
+
spin_lock_init(&dispc.irq_lock);
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
@@ -3628,51 +4061,65 @@ static int omap_dispchw_probe(struct platform_device *pdev)
if (!dispc_mem) {
DSSERR("can't get IORESOURCE_MEM DISPC\n");
r = -EINVAL;
- goto fail0;
+ goto err_ioremap;
}
dispc.base = ioremap(dispc_mem->start, resource_size(dispc_mem));
if (!dispc.base) {
DSSERR("can't ioremap DISPC\n");
r = -ENOMEM;
- goto fail0;
+ goto err_ioremap;
}
dispc.irq = platform_get_irq(dispc.pdev, 0);
if (dispc.irq < 0) {
DSSERR("platform_get_irq failed\n");
r = -ENODEV;
- goto fail1;
+ goto err_irq;
}
r = request_irq(dispc.irq, omap_dispc_irq_handler, IRQF_SHARED,
"OMAP DISPC", dispc.pdev);
if (r < 0) {
DSSERR("request_irq failed\n");
- goto fail1;
+ goto err_irq;
}
- enable_clocks(1);
+ mutex_init(&dispc.runtime_lock);
+
+ pm_runtime_enable(&pdev->dev);
+
+ r = dispc_runtime_get();
+ if (r)
+ goto err_runtime_get;
_omap_dispc_initial_config();
_omap_dispc_initialize_irq();
- dispc_save_context();
-
rev = dispc_read_reg(DISPC_REVISION);
dev_dbg(&pdev->dev, "OMAP DISPC rev %d.%d\n",
FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
- enable_clocks(0);
+ dispc_runtime_put();
return 0;
-fail1:
+
+err_runtime_get:
+ pm_runtime_disable(&pdev->dev);
+ free_irq(dispc.irq, dispc.pdev);
+err_irq:
iounmap(dispc.base);
-fail0:
+err_ioremap:
+ clk_put(dispc.dss_clk);
+err_get_clk:
return r;
}
static int omap_dispchw_remove(struct platform_device *pdev)
{
+ pm_runtime_disable(&pdev->dev);
+
+ clk_put(dispc.dss_clk);
+
free_irq(dispc.irq, dispc.pdev);
iounmap(dispc.base);
return 0;
diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h
index 6c9ee0a..ee08b69 100644
--- a/drivers/video/omap2/dss/dispc.h
+++ b/drivers/video/omap2/dss/dispc.h
@@ -174,7 +174,8 @@ static inline u16 DISPC_DIVISORo(enum omap_channel channel)
case OMAP_DSS_CHANNEL_LCD:
return 0x0070;
case OMAP_DSS_CHANNEL_DIGIT:
- BUG();
+ /* FIXME venc pclk? */
+ return 0x0070;
case OMAP_DSS_CHANNEL_LCD2:
return 0x040C;
default:
@@ -291,6 +292,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 +307,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 +321,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 +337,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 +353,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 +367,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 +381,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 +396,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 +412,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 +427,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 +442,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 +457,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 +472,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 +486,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 +500,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 +515,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 +531,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 +546,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 +562,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 +578,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 +593,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 +609,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 +625,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 +642,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 +658,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 +675,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 +690,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 +707,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 +724,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 +740,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/display.c b/drivers/video/omap2/dss/display.c
index c2dfc8c..8b3b360 100644
--- a/drivers/video/omap2/dss/display.c
+++ b/drivers/video/omap2/dss/display.c
@@ -339,6 +339,18 @@ void default_get_overlay_fifo_thresholds(enum omap_plane plane,
*fifo_low = fifo_size - burst_size_bytes;
}
+void omapdss_display_get_dimensions(struct omap_dss_device *dssdev,
+ u32 *width_in_um, u32 *height_in_um)
+{
+ if (dssdev->driver->get_dimensions) {
+ dssdev->driver->get_dimensions(dssdev,
+ width_in_um, width_in_um);
+ } else {
+ *width_in_um = dssdev->panel.width_in_um;
+ *height_in_um = dssdev->panel.height_in_um;
+ }
+}
+
int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev)
{
switch (dssdev->type) {
@@ -446,6 +458,8 @@ void dss_init_device(struct platform_device *pdev,
return;
}
+ BLOCKING_INIT_NOTIFIER_HEAD(&dssdev->state_notifiers);
+
/* create device sysfs files */
i = 0;
while ((attr = display_sysfs_attrs[i++]) != NULL) {
diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c
index ff6bd30..f053b18 100644
--- a/drivers/video/omap2/dss/dpi.c
+++ b/drivers/video/omap2/dss/dpi.c
@@ -23,7 +23,6 @@
#define DSS_SUBSYS_NAME "DPI"
#include <linux/kernel.h>
-#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/errno.h>
@@ -130,8 +129,6 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)
bool is_tft;
int r = 0;
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
-
dispc_set_pol_freq(dssdev->manager->id, dssdev->panel.config,
dssdev->panel.acbi, dssdev->panel.acb);
@@ -144,7 +141,7 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)
r = dpi_set_dispc_clk(dssdev, is_tft, t->pixel_clock * 1000,
&fck, &lck_div, &pck_div);
if (r)
- goto err0;
+ return r;
pck = fck / lck_div / pck_div / 1000;
@@ -158,12 +155,10 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)
dispc_set_lcd_timings(dssdev->manager->id, t);
-err0:
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
- return r;
+ return 0;
}
-static int dpi_basic_init(struct omap_dss_device *dssdev)
+static void dpi_basic_init(struct omap_dss_device *dssdev)
{
bool is_tft;
@@ -175,8 +170,6 @@ static int dpi_basic_init(struct omap_dss_device *dssdev)
OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN);
dispc_set_tft_data_lines(dssdev->manager->id,
dssdev->phy.dpi.data_lines);
-
- return 0;
}
int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
@@ -186,31 +179,38 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");
- goto err0;
+ goto err_start_dev;
}
if (cpu_is_omap34xx()) {
r = regulator_enable(dpi.vdds_dsi_reg);
if (r)
- goto err1;
+ goto err_reg_enable;
}
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ r = dss_runtime_get();
+ if (r)
+ goto err_get_dss;
- r = dpi_basic_init(dssdev);
+ r = dispc_runtime_get();
if (r)
- goto err2;
+ goto err_get_dispc;
+
+ dpi_basic_init(dssdev);
if (dpi_use_dsi_pll(dssdev)) {
- dss_clk_enable(DSS_CLK_SYSCK);
+ r = dsi_runtime_get(dpi.dsidev);
+ if (r)
+ goto err_get_dsi;
+
r = dsi_pll_init(dpi.dsidev, 0, 1);
if (r)
- goto err3;
+ goto err_dsi_pll_init;
}
r = dpi_set_mode(dssdev);
if (r)
- goto err4;
+ goto err_set_mode;
mdelay(2);
@@ -218,19 +218,22 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
return 0;
-err4:
+err_set_mode:
if (dpi_use_dsi_pll(dssdev))
dsi_pll_uninit(dpi.dsidev, true);
-err3:
+err_dsi_pll_init:
if (dpi_use_dsi_pll(dssdev))
- dss_clk_disable(DSS_CLK_SYSCK);
-err2:
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ dsi_runtime_put(dpi.dsidev);
+err_get_dsi:
+ dispc_runtime_put();
+err_get_dispc:
+ dss_runtime_put();
+err_get_dss:
if (cpu_is_omap34xx())
regulator_disable(dpi.vdds_dsi_reg);
-err1:
+err_reg_enable:
omap_dss_stop_device(dssdev);
-err0:
+err_start_dev:
return r;
}
EXPORT_SYMBOL(omapdss_dpi_display_enable);
@@ -242,10 +245,11 @@ void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)
if (dpi_use_dsi_pll(dssdev)) {
dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
dsi_pll_uninit(dpi.dsidev, true);
- dss_clk_disable(DSS_CLK_SYSCK);
+ dsi_runtime_put(dpi.dsidev);
}
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ dispc_runtime_put();
+ dss_runtime_put();
if (cpu_is_omap34xx())
regulator_disable(dpi.vdds_dsi_reg);
@@ -257,11 +261,26 @@ EXPORT_SYMBOL(omapdss_dpi_display_disable);
void dpi_set_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
+ int r;
+
DSSDBG("dpi_set_timings\n");
dssdev->panel.timings = *timings;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
+ r = dss_runtime_get();
+ if (r)
+ return;
+
+ r = dispc_runtime_get();
+ if (r) {
+ dss_runtime_put();
+ return;
+ }
+
dpi_set_mode(dssdev);
dispc_go(dssdev->manager->id);
+
+ dispc_runtime_put();
+ dss_runtime_put();
}
}
EXPORT_SYMBOL(dpi_set_timings);
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index 345757c..93b52f6 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -36,6 +36,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
#include <video/omapdss.h>
#include <plat/clock.h>
@@ -205,6 +206,7 @@ struct dsi_reg { u16 idx; };
#define DSI_DT_DCS_LONG_WRITE 0x39
#define DSI_DT_RX_ACK_WITH_ERR 0x02
+#define DSI_DT_RX_LONG_READ 0x1a
#define DSI_DT_RX_DCS_LONG_READ 0x1c
#define DSI_DT_RX_SHORT_READ_1 0x21
#define DSI_DT_RX_SHORT_READ_2 0x22
@@ -267,8 +269,15 @@ struct dsi_isr_tables {
struct dsi_data {
struct platform_device *pdev;
void __iomem *base;
+
+ struct mutex runtime_lock;
+ int runtime_count;
+
int irq;
+ struct clk *dss_clk;
+ struct clk *sys_clk;
+
void (*dsi_mux_pads)(bool enable);
struct dsi_clock_info current_cinfo;
@@ -389,15 +398,6 @@ static inline u32 dsi_read_reg(struct platform_device *dsidev,
return __raw_readl(dsi->base + idx.idx);
}
-
-void dsi_save_context(void)
-{
-}
-
-void dsi_restore_context(void)
-{
-}
-
void dsi_bus_lock(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
@@ -493,9 +493,18 @@ static void dsi_perf_show(struct platform_device *dsidev, const char *name)
total_bytes * 1000 / total_us);
}
#else
-#define dsi_perf_mark_setup(x)
-#define dsi_perf_mark_start(x)
-#define dsi_perf_show(x, y)
+static inline void dsi_perf_mark_setup(struct platform_device *dsidev)
+{
+}
+
+static inline void dsi_perf_mark_start(struct platform_device *dsidev)
+{
+}
+
+static inline void dsi_perf_show(struct platform_device *dsidev,
+ const char *name)
+{
+}
#endif
static void print_irq_status(u32 status)
@@ -1039,13 +1048,69 @@ static u32 dsi_get_errors(struct platform_device *dsidev)
return e;
}
-/* DSI func clock. this could also be dsi_pll_hsdiv_dsi_clk */
-static inline void enable_clocks(bool enable)
+int dsi_runtime_get(struct platform_device *dsidev)
{
- if (enable)
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
- else
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ int r;
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+
+ mutex_lock(&dsi->runtime_lock);
+
+ if (dsi->runtime_count++ == 0) {
+ DSSDBG("dsi_runtime_get\n");
+
+ r = dss_runtime_get();
+ if (r)
+ goto err_get_dss;
+
+ r = dispc_runtime_get();
+ if (r)
+ goto err_get_dispc;
+
+ /* XXX dsi fclk can also come from DSI PLL */
+ clk_enable(dsi->dss_clk);
+
+ r = pm_runtime_get_sync(&dsi->pdev->dev);
+ WARN_ON(r);
+ if (r < 0)
+ goto err_runtime_get;
+ }
+
+ mutex_unlock(&dsi->runtime_lock);
+
+ return 0;
+
+err_runtime_get:
+ clk_disable(dsi->dss_clk);
+ dispc_runtime_put();
+err_get_dispc:
+ dss_runtime_put();
+err_get_dss:
+ mutex_unlock(&dsi->runtime_lock);
+
+ return r;
+}
+
+void dsi_runtime_put(struct platform_device *dsidev)
+{
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+
+ mutex_lock(&dsi->runtime_lock);
+
+ if (--dsi->runtime_count == 0) {
+ int r;
+
+ DSSDBG("dsi_runtime_put\n");
+
+ r = pm_runtime_put_sync(&dsi->pdev->dev);
+ WARN_ON(r);
+
+ clk_disable(dsi->dss_clk);
+
+ dispc_runtime_put();
+ dss_runtime_put();
+ }
+
+ mutex_unlock(&dsi->runtime_lock);
}
/* source clock for DSI PLL. this could also be PCLKFREE */
@@ -1055,9 +1120,9 @@ static inline void dsi_enable_pll_clock(struct platform_device *dsidev,
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
if (enable)
- dss_clk_enable(DSS_CLK_SYSCK);
+ clk_enable(dsi->sys_clk);
else
- dss_clk_disable(DSS_CLK_SYSCK);
+ clk_disable(dsi->sys_clk);
if (enable && dsi->pll_locked) {
if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1)
@@ -1150,10 +1215,11 @@ static unsigned long dsi_fclk_rate(struct platform_device *dsidev)
{
unsigned long r;
int dsi_module = dsi_get_dsidev_id(dsidev);
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
if (dss_get_dsi_clk_source(dsi_module) == OMAP_DSS_CLK_SRC_FCK) {
/* DSI FCLK source is DSS_CLK_FCK */
- r = dss_clk_get_rate(DSS_CLK_FCK);
+ r = clk_get_rate(dsi->dss_clk);
} else {
/* DSI FCLK source is dsi_pll_hsdiv_dsi_clk */
r = dsi_get_pll_hsdiv_dsi_rate(dsidev);
@@ -1262,7 +1328,7 @@ static int dsi_calc_clock_rates(struct omap_dss_device *dssdev,
return -EINVAL;
if (cinfo->use_sys_clk) {
- cinfo->clkin = dss_clk_get_rate(DSS_CLK_SYSCK);
+ cinfo->clkin = clk_get_rate(dsi->sys_clk);
/* XXX it is unclear if highfreq should be used
* with DSS_SYS_CLK source also */
cinfo->highfreq = 0;
@@ -1311,7 +1377,7 @@ int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft,
int match = 0;
unsigned long dss_sys_clk, max_dss_fck;
- dss_sys_clk = dss_clk_get_rate(DSS_CLK_SYSCK);
+ dss_sys_clk = clk_get_rate(dsi->sys_clk);
max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);
@@ -1539,6 +1605,9 @@ int dsi_pll_set_clock_div(struct platform_device *dsidev,
l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */
l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */
l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */
+
+ if (cpu_is_omap44xx())
+ l = FLD_MOD(l, 3, 22, 21); /* DSI_REF_SEL */
dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION2, l);
REG_FLD_MOD(dsidev, DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */
@@ -1601,7 +1670,6 @@ int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk,
dsi->vdds_dsi_reg = vdds_dsi;
}
- enable_clocks(1);
dsi_enable_pll_clock(dsidev, 1);
/*
* Note: SCP CLK is not required on OMAP3, but it is required on OMAP4.
@@ -1653,7 +1721,6 @@ err1:
}
err0:
dsi_disable_scp_clk(dsidev);
- enable_clocks(0);
dsi_enable_pll_clock(dsidev, 0);
return r;
}
@@ -1671,7 +1738,6 @@ void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes)
}
dsi_disable_scp_clk(dsidev);
- enable_clocks(0);
dsi_enable_pll_clock(dsidev, 0);
DSSDBG("PLL uninit done\n");
@@ -1688,7 +1754,8 @@ static void dsi_dump_dsidev_clocks(struct platform_device *dsidev,
dispc_clk_src = dss_get_dispc_clk_source();
dsi_clk_src = dss_get_dsi_clk_source(dsi_module);
- enable_clocks(1);
+ if (dsi_runtime_get(dsidev))
+ return;
seq_printf(s, "- DSI%d PLL -\n", dsi_module + 1);
@@ -1731,7 +1798,7 @@ static void dsi_dump_dsidev_clocks(struct platform_device *dsidev,
seq_printf(s, "LP_CLK\t\t%lu\n", cinfo->lp_clk);
- enable_clocks(0);
+ dsi_runtime_put(dsidev);
}
void dsi_dump_clocks(struct seq_file *s)
@@ -1873,7 +1940,8 @@ static void dsi_dump_dsidev_regs(struct platform_device *dsidev,
{
#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(dsidev, r))
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ if (dsi_runtime_get(dsidev))
+ return;
dsi_enable_scp_clk(dsidev);
DUMPREG(DSI_REVISION);
@@ -1947,7 +2015,7 @@ static void dsi_dump_dsidev_regs(struct platform_device *dsidev,
DUMPREG(DSI_PLL_CONFIGURATION2);
dsi_disable_scp_clk(dsidev);
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ dsi_runtime_put(dsidev);
#undef DUMPREG
}
@@ -1994,6 +2062,10 @@ static int dsi_cio_power(struct platform_device *dsidev,
/* PWR_CMD */
REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG1, state, 28, 27);
+ if (cpu_is_omap44xx())
+ /*bit 30 has to be set to 1 to GO in omap4*/
+ REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG1, 1, 30, 30);
+
/* PWR_STATUS */
while (FLD_GET(dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1),
26, 25) != state) {
@@ -2354,6 +2426,13 @@ static int dsi_cio_init(struct omap_dss_device *dssdev)
if (dsi->dsi_mux_pads)
dsi->dsi_mux_pads(true);
+ if (cpu_is_omap44xx()) {
+ /* DDR_CLK_ALWAYS_ON */
+ REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 13, 13);
+ /* HS_AUTO_STOP_ENABLE */
+ REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 18, 18);
+ }
+
dsi_enable_scp_clk(dsidev);
/* A dummy read using the SCP interface to any DSIPHY register is
@@ -2463,28 +2542,6 @@ static void dsi_cio_uninit(struct platform_device *dsidev)
dsi->dsi_mux_pads(false);
}
-static int _dsi_wait_reset(struct platform_device *dsidev)
-{
- int t = 0;
-
- while (REG_GET(dsidev, DSI_SYSSTATUS, 0, 0) == 0) {
- if (++t > 5) {
- DSSERR("soft reset failed\n");
- return -ENODEV;
- }
- udelay(1);
- }
-
- return 0;
-}
-
-static int _dsi_reset(struct platform_device *dsidev)
-{
- /* Soft reset */
- REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 1, 1, 1);
- return _dsi_wait_reset(dsidev);
-}
-
static void dsi_config_tx_fifo(struct platform_device *dsidev,
enum fifo_size size1, enum fifo_size size2,
enum fifo_size size3, enum fifo_size size4)
@@ -2722,6 +2779,8 @@ static void dsi_vc_initial_config(struct platform_device *dsidev, int channel)
r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */
if (dss_has_feature(FEAT_DSI_VC_OCP_WIDTH))
r = FLD_MOD(r, 3, 11, 10); /* OCP_WIDTH = 32 bit */
+ if (channel == 0)
+ r = FLD_MOD(r, 1, 11, 10); /* OCP_WIDTH = 32 bit */
r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */
r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */
@@ -2885,6 +2944,10 @@ static u16 dsi_vc_flush_receive_data(struct platform_device *dsidev,
} else if (dt == DSI_DT_RX_SHORT_READ_2) {
DSSERR("\tDCS short response, 2 byte: %#x\n",
FLD_GET(val, 23, 8));
+ } else if (dt == DSI_DT_RX_LONG_READ) {
+ DSSERR("\tlong response, len %d\n",
+ FLD_GET(val, 23, 8));
+ dsi_vc_flush_long_data(dsidev, channel);
} else if (dt == DSI_DT_RX_DCS_LONG_READ) {
DSSERR("\tDCS long response, len %d\n",
FLD_GET(val, 23, 8));
@@ -3229,7 +3292,7 @@ int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd,
buf[1] = (data >> 8) & 0xff;
return 2;
- } else if (dt == DSI_DT_RX_DCS_LONG_READ) {
+ } else if (dt == DSI_DT_RX_DCS_LONG_READ || dt == DSI_DT_RX_LONG_READ) {
int w;
int len = FLD_GET(val, 23, 8);
if (dsi->debug_read)
@@ -3386,6 +3449,10 @@ static int dsi_enter_ulps(struct platform_device *dsidev)
dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion,
DSI_CIO_IRQ_ULPSACTIVENOT_ALL0);
+ /* Reset LANEx_ULPS_SIG2 */
+ REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, (0 << 0) | (0 << 1) | (0 << 2),
+ 7, 5);
+
dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ULPS);
dsi_if_enable(dsidev, false);
@@ -3401,7 +3468,7 @@ err:
}
static void dsi_set_lp_rx_timeout(struct platform_device *dsidev,
- unsigned ticks, bool x4, bool x16)
+ unsigned ticks, bool x4, bool x16, bool to)
{
unsigned long fck;
unsigned long total_ticks;
@@ -3413,7 +3480,7 @@ static void dsi_set_lp_rx_timeout(struct platform_device *dsidev,
fck = dsi_fclk_rate(dsidev);
r = dsi_read_reg(dsidev, DSI_TIMING2);
- r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */
+ r = FLD_MOD(r, to ? 1 : 0, 15, 15); /* LP_RX_TO */
r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* LP_RX_TO_X16 */
r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* LP_RX_TO_X4 */
r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */
@@ -3428,7 +3495,7 @@ static void dsi_set_lp_rx_timeout(struct platform_device *dsidev,
}
static void dsi_set_ta_timeout(struct platform_device *dsidev, unsigned ticks,
- bool x8, bool x16)
+ bool x8, bool x16, bool to)
{
unsigned long fck;
unsigned long total_ticks;
@@ -3440,7 +3507,7 @@ static void dsi_set_ta_timeout(struct platform_device *dsidev, unsigned ticks,
fck = dsi_fclk_rate(dsidev);
r = dsi_read_reg(dsidev, DSI_TIMING1);
- r = FLD_MOD(r, 1, 31, 31); /* TA_TO */
+ r = FLD_MOD(r, to ? 1 : 0, 31, 31); /* TA_TO */
r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* TA_TO_X16 */
r = FLD_MOD(r, x8 ? 1 : 0, 29, 29); /* TA_TO_X8 */
r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */
@@ -3455,7 +3522,8 @@ static void dsi_set_ta_timeout(struct platform_device *dsidev, unsigned ticks,
}
static void dsi_set_stop_state_counter(struct platform_device *dsidev,
- unsigned ticks, bool x4, bool x16)
+ unsigned ticks, bool x4, bool x16,
+ bool stop_mode)
{
unsigned long fck;
unsigned long total_ticks;
@@ -3467,7 +3535,7 @@ static void dsi_set_stop_state_counter(struct platform_device *dsidev,
fck = dsi_fclk_rate(dsidev);
r = dsi_read_reg(dsidev, DSI_TIMING1);
- r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
+ r = FLD_MOD(r, stop_mode ? 1 : 0, 15, 15); /* FORCE_TX_STOP_MODE_IO */
r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* STOP_STATE_X16_IO */
r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* STOP_STATE_X4_IO */
r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */
@@ -3482,7 +3550,7 @@ static void dsi_set_stop_state_counter(struct platform_device *dsidev,
}
static void dsi_set_hs_tx_timeout(struct platform_device *dsidev,
- unsigned ticks, bool x4, bool x16)
+ unsigned ticks, bool x4, bool x16, bool to)
{
unsigned long fck;
unsigned long total_ticks;
@@ -3494,7 +3562,7 @@ static void dsi_set_hs_tx_timeout(struct platform_device *dsidev,
fck = dsi_get_txbyteclkhs(dsidev);
r = dsi_read_reg(dsidev, DSI_TIMING2);
- r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */
+ r = FLD_MOD(r, to ? 1 : 0, 31, 31); /* HS_TX_TO */
r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* HS_TX_TO_X16 */
r = FLD_MOD(r, x4 ? 1 : 0, 29, 29); /* HS_TX_TO_X8 (4 really) */
r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */
@@ -3507,7 +3575,8 @@ static void dsi_set_hs_tx_timeout(struct platform_device *dsidev,
ticks, x4 ? " x4" : "", x16 ? " x16" : "",
(total_ticks * 1000) / (fck / 1000 / 1000));
}
-static int dsi_proto_config(struct omap_dss_device *dssdev)
+
+static int dsi_cmd_proto_config(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
u32 r;
@@ -3524,10 +3593,10 @@ static int dsi_proto_config(struct omap_dss_device *dssdev)
DSI_FIFO_SIZE_32);
/* XXX what values for the timeouts? */
- dsi_set_stop_state_counter(dsidev, 0x1000, false, false);
- dsi_set_ta_timeout(dsidev, 0x1fff, true, true);
- dsi_set_lp_rx_timeout(dsidev, 0x1fff, true, true);
- dsi_set_hs_tx_timeout(dsidev, 0x1fff, true, true);
+ dsi_set_stop_state_counter(dsidev, 0x1000, false, false, true);
+ dsi_set_ta_timeout(dsidev, 0x1fff, true, true, true);
+ dsi_set_lp_rx_timeout(dsidev, 0x1fff, true, true, true);
+ dsi_set_hs_tx_timeout(dsidev, 0x1fff, true, true, true);
switch (dssdev->ctrl.pixel_size) {
case 16:
@@ -3569,6 +3638,165 @@ static int dsi_proto_config(struct omap_dss_device *dssdev)
return 0;
}
+static int dispc_to_dsi_clock(int val, int bytes_per_pixel, int lanes)
+{
+ return (val * bytes_per_pixel + lanes / 2) / lanes;
+}
+static int dsi_video_proto_config(struct omap_dss_device *dssdev)
+{
+ struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
+ struct omap_video_timings *timings = &dssdev->panel.timings;
+ int buswidth = 0;
+ u32 r;
+ int bytes_per_pixel;
+ int hbp, hfp, hsa, tl;
+
+ dsi_config_tx_fifo(dsidev, DSI_FIFO_SIZE_32,
+ DSI_FIFO_SIZE_32,
+ DSI_FIFO_SIZE_32,
+ DSI_FIFO_SIZE_32);
+
+ dsi_config_rx_fifo(dsidev, DSI_FIFO_SIZE_32,
+ DSI_FIFO_SIZE_32,
+ DSI_FIFO_SIZE_32,
+ DSI_FIFO_SIZE_32);
+
+ dsi_set_stop_state_counter(dsidev, 0x1fff, true, true, false);
+ dsi_set_ta_timeout(dsidev, 0x1fff, true, true, true);
+ dsi_set_lp_rx_timeout(dsidev, 0x1fff, true, true, false);
+ dsi_set_hs_tx_timeout(dsidev, 0x1fff, true, true, true);
+
+ switch (dssdev->ctrl.pixel_size) {
+ case 16:
+ buswidth = 0;
+ bytes_per_pixel = 2;
+ break;
+ case 18:
+ buswidth = 1;
+ bytes_per_pixel = 3;
+ break;
+ case 24:
+ buswidth = 2;
+ bytes_per_pixel = 3;
+ break;
+ default:
+ BUG();
+ }
+
+ r = dsi_read_reg(dsidev, DSI_CTRL);
+ r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */
+ r = FLD_MOD(r, 1, 4, 4); /* VP_CLK_RATIO, always 1, see errata*/
+ r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */
+ r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */
+ r = FLD_MOD(r, 1, 9, 9); /* VP_DE_POL */
+ r = FLD_MOD(r, 0, 10, 10); /* VP_HSYNC_POL */
+ r = FLD_MOD(r, 1, 11, 11); /* VP_VSYNC_POL */
+ r = FLD_MOD(r, 2, 13, 12); /* LINE_BUFFER */
+ r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */
+ r = FLD_MOD(r, 1, 15, 15); /* VP_VSYNC_START */
+ r = FLD_MOD(r, 1, 17, 17); /* VP_HSYNC_START */
+ r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */
+ r = FLD_MOD(r, 1, 20, 20); /* BLANKING_MODE */
+ r = FLD_MOD(r, 1, 21, 21); /* HFP_BLANKING */
+ r = FLD_MOD(r, 1, 22, 22); /* HBP_BLANKING */
+ r = FLD_MOD(r, 1, 23, 23); /* HSA_BLANKING */
+ dsi_write_reg(dsidev, DSI_CTRL, r);
+
+ if(!dssdev->skip_init){
+ dsi_vc_initial_config(dsidev, 0);
+ dsi_vc_initial_config(dsidev, 1);
+ dsi_vc_initial_config(dsidev, 2);
+ dsi_vc_initial_config(dsidev, 3);
+ }
+
+ hbp = dispc_to_dsi_clock(timings->hbp, bytes_per_pixel, 4);
+ hfp = dispc_to_dsi_clock(timings->hfp, bytes_per_pixel, 4);
+ hsa = dispc_to_dsi_clock(timings->hsw, bytes_per_pixel, 4);
+ tl = hbp + hfp + hsa +
+ dispc_to_dsi_clock(timings->x_res, bytes_per_pixel, 4);
+
+ r = dsi_read_reg(dsidev, DSI_VM_TIMING1);
+ r = FLD_MOD(r, hbp - 1, 11, 0); /* HBP */
+ r = FLD_MOD(r, hfp - 1, 23, 12); /* HFP */
+ r = FLD_MOD(r, hsa - 1, 31, 24); /* HSA */
+ dsi_write_reg(dsidev, DSI_VM_TIMING1, r);
+
+ r = dsi_read_reg(dsidev, DSI_VM_TIMING2);
+ r = FLD_MOD(r, timings->vbp, 7, 0); /* VBP */
+ r = FLD_MOD(r, timings->vfp, 15, 8); /* VFP */
+ r = FLD_MOD(r, timings->vsw, 23, 16); /* VSA */
+ r = FLD_MOD(r, 4, 27, 24); /* WINDOW_SYNC */
+ dsi_write_reg(dsidev, DSI_VM_TIMING2, r);
+
+ r = dsi_read_reg(dsidev, DSI_VM_TIMING3);
+ r = FLD_MOD(r, timings->y_res, 14, 0);
+ r = FLD_MOD(r, tl - 1, 31, 16);
+ dsi_write_reg(dsidev, DSI_VM_TIMING3, r);
+
+ /* TODO: either calculate these values or make them configurable */
+ r = FLD_VAL(72, 23, 16) | /* HSA_HS_INTERLEAVING */
+ FLD_VAL(114, 15, 8) | /* HFB_HS_INTERLEAVING */
+ FLD_VAL(150, 7, 0); /* HbB_HS_INTERLEAVING */
+ dsi_write_reg(dsidev, DSI_VM_TIMING4, r);
+
+ r = FLD_VAL(130, 23, 16) | /* HSA_LP_INTERLEAVING */
+ FLD_VAL(223, 15, 8) | /* HFB_LP_INTERLEAVING */
+ FLD_VAL(59, 7, 0); /* HBB_LP_INTERLEAVING */
+ dsi_write_reg(dsidev, DSI_VM_TIMING5, r);
+
+ r = FLD_VAL(0x7A67, 31, 16) | /* BL_HS_INTERLEAVING */
+ FLD_VAL(0x31D1, 15, 0); /* BL_LP_INTERLEAVING */
+ dsi_write_reg(dsidev, DSI_VM_TIMING6, r);
+
+ r = FLD_VAL(18, 31, 16) | /* ENTER_HS_MODE_LATENCY */
+ FLD_VAL(15, 15, 0); /* EXIT_HS_MODE_LATENCY */
+ dsi_write_reg(dsidev, DSI_VM_TIMING7, r);
+
+ return 0;
+}
+
+int dsi_video_mode_enable(struct omap_dss_device *dssdev, u8 data_type)
+{
+ struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
+ u16 word_count;
+ u32 r;
+ u32 header;
+
+ dsi_if_enable(dsidev, 0);
+ dsi_vc_enable(dsidev, 1, 0);
+ dsi_vc_enable(dsidev, 0, 0);
+
+ r = dsi_read_reg(dsidev, DSI_TIMING1);
+ r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */
+ dsi_write_reg(dsidev, DSI_TIMING1, r);
+
+ if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 15, 0) != 0)
+ BUG();
+
+ r = dsi_read_reg(dsidev, DSI_VC_CTRL(0));
+ r = FLD_MOD(r, 1, 4, 4);
+ r = FLD_MOD(r, 1, 9, 9);
+ dsi_write_reg(dsidev, DSI_VC_CTRL(0), r);
+
+ r = dsi_read_reg(dsidev, DSI_VC_CTRL(1));
+ r = FLD_MOD(r, 0, 4, 4);
+ r = FLD_MOD(r, 1, 9, 9);
+ dsi_write_reg(dsidev, DSI_VC_CTRL(1), r);
+
+ word_count = dssdev->panel.timings.x_res * 3;
+ header = FLD_VAL(0, 31, 24) | /* ECC */
+ FLD_VAL(word_count, 23, 8) | /* WORD_COUNT */
+ FLD_VAL(0, 7, 6) | /* VC_ID */
+ FLD_VAL(data_type, 5, 0);
+ dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_HEADER(0), header);
+
+ dsi_vc_enable(dsidev, 1, 1);
+ dsi_vc_enable(dsidev, 0, 1);
+ dsi_if_enable(dsidev, 1);
+
+ return 0;
+}
+
static void dsi_proto_timings(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
@@ -3579,6 +3807,7 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev)
unsigned ddr_clk_pre, ddr_clk_post;
unsigned enter_hs_mode_lat, exit_hs_mode_lat;
unsigned ths_eot;
+ unsigned offset_ddr_clk;
u32 r;
r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0);
@@ -3603,9 +3832,13 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev)
ths_eot = DIV_ROUND_UP(4, dsi_get_num_data_lanes_dssdev(dssdev));
+ /* DDR PRE & DDR POST increased to keep LP-11 under 10 usec */
+ offset_ddr_clk = dssdev->clocks.dsi.offset_ddr_clk;
+
ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare,
- 4);
- ddr_clk_post = DIV_ROUND_UP(tclk_post + ths_trail, 4) + ths_eot;
+ 4) + offset_ddr_clk;
+ ddr_clk_post = DIV_ROUND_UP(tclk_post + ths_trail, 4) + ths_eot
+ + offset_ddr_clk;
BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255);
BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255);
@@ -3807,6 +4040,11 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev,
DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n",
x, y, w, h);
+ if (dssdev->phy.dsi.type == OMAP_DSS_DSI_TYPE_VIDEO_MODE) {
+ dss_start_update(dssdev);
+ return;
+ }
+
dsi_vc_config_vp(dsidev, channel);
bytespp = dssdev->ctrl.pixel_size / 8;
@@ -4017,23 +4255,30 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ?
DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2;
- r = omap_dispc_register_isr(dsi_framedone_irq_callback, (void *) dssdev,
- irq);
- if (r) {
- DSSERR("can't get FRAMEDONE irq\n");
- return r;
+ if (dssdev->phy.dsi.type == OMAP_DSS_DSI_TYPE_CMD_MODE) {
+ r = omap_dispc_register_isr(dsi_framedone_irq_callback, (void *) dssdev,
+ irq);
+ if (r) {
+ DSSERR("can't get FRAMEDONE irq\n");
+ return r;
+ }
+
+ dispc_set_parallel_interface_mode(dssdev->manager->id,
+ OMAP_DSS_PARALLELMODE_DSI);
+ dispc_enable_fifohandcheck(dssdev->manager->id, 1);
+ } else {
+ dispc_set_parallel_interface_mode(dssdev->manager->id,
+ OMAP_DSS_PARALLELMODE_BYPASS);
+ dispc_enable_fifohandcheck(dssdev->manager->id, 0);
}
dispc_set_lcd_display_type(dssdev->manager->id,
OMAP_DSS_LCD_DISPLAY_TFT);
- dispc_set_parallel_interface_mode(dssdev->manager->id,
- OMAP_DSS_PARALLELMODE_DSI);
- dispc_enable_fifohandcheck(dssdev->manager->id, 1);
dispc_set_tft_data_lines(dssdev->manager->id, dssdev->ctrl.pixel_size);
- {
+ if(dssdev->phy.dsi.type == OMAP_DSS_DSI_TYPE_CMD_MODE) {
struct omap_video_timings timings = {
.hsw = 1,
.hfp = 1,
@@ -4044,6 +4289,9 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
};
dispc_set_lcd_timings(dssdev->manager->id, &timings);
+ } else {
+ dispc_set_lcd_timings(dssdev->manager->id,
+ &dssdev->panel.timings);
}
return 0;
@@ -4056,8 +4304,9 @@ static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev)
irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ?
DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2;
- omap_dispc_unregister_isr(dsi_framedone_irq_callback, (void *) dssdev,
- irq);
+ if(dssdev->phy.dsi.type == OMAP_DSS_DSI_TYPE_CMD_MODE)
+ omap_dispc_unregister_isr(dsi_framedone_irq_callback, (void *) dssdev,
+ irq);
}
static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev)
@@ -4120,13 +4369,20 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
int dsi_module = dsi_get_dsidev_id(dsidev);
int r;
+ /* The SCPClk is required for PLL and complexio registers on OMAP4 */
+ if (cpu_is_omap44xx())
+ REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 14, 14);
+
r = dsi_pll_init(dsidev, true, true);
+
if (r)
goto err0;
- r = dsi_configure_dsi_clocks(dssdev);
- if (r)
- goto err1;
+ if(!dssdev->skip_init){
+ r = dsi_configure_dsi_clocks(dssdev);
+ if (r)
+ goto err1;
+ }
dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src);
dss_select_dsi_clk_source(dsi_module, dssdev->clocks.dsi.dsi_fclk_src);
@@ -4135,13 +4391,19 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
DSSDBG("PLL OK\n");
- r = dsi_configure_dispc_clocks(dssdev);
- if (r)
- goto err2;
+ if(!dssdev->skip_init){
+ r = dsi_configure_dispc_clocks(dssdev);
+ if (r)
+ goto err2;
+ }
- r = dsi_cio_init(dssdev);
- if (r)
- goto err2;
+ if(!dssdev->skip_init){
+ r = dsi_cio_init(dssdev);
+ if (r)
+ goto err2;
+ }
+ else
+ dsi_enable_scp_clk(dsidev);
_dsi_print_reset_status(dsidev);
@@ -4151,17 +4413,23 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
if (1)
_dsi_print_reset_status(dsidev);
- r = dsi_proto_config(dssdev);
+ if(dssdev->phy.dsi.type == OMAP_DSS_DSI_TYPE_CMD_MODE)
+ r = dsi_cmd_proto_config(dssdev);
+ else
+ r = dsi_video_proto_config(dssdev);
+
if (r)
goto err3;
/* enable interface */
- dsi_vc_enable(dsidev, 0, 1);
- dsi_vc_enable(dsidev, 1, 1);
- dsi_vc_enable(dsidev, 2, 1);
- dsi_vc_enable(dsidev, 3, 1);
- dsi_if_enable(dsidev, 1);
- dsi_force_tx_stop_mode_io(dsidev);
+ if(!dssdev->skip_init){
+ dsi_vc_enable(dsidev, 0, 1);
+ dsi_vc_enable(dsidev, 1, 1);
+ dsi_vc_enable(dsidev, 2, 1);
+ dsi_vc_enable(dsidev, 3, 1);
+ dsi_if_enable(dsidev, 1);
+ dsi_force_tx_stop_mode_io(dsidev);
+ }
return 0;
err3:
@@ -4198,22 +4466,6 @@ static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev,
dsi_pll_uninit(dsidev, disconnect_lanes);
}
-static int dsi_core_init(struct platform_device *dsidev)
-{
- /* Autoidle */
- REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 1, 0, 0);
-
- /* ENWAKEUP */
- REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 1, 2, 2);
-
- /* SIDLEMODE smart-idle */
- REG_FLD_MOD(dsidev, DSI_SYSCONFIG, 2, 4, 3);
-
- _dsi_initialize_irq(dsidev);
-
- return 0;
-}
-
int omapdss_dsi_display_enable(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
@@ -4229,37 +4481,40 @@ int omapdss_dsi_display_enable(struct omap_dss_device *dssdev)
r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");
- goto err0;
+ goto err_start_dev;
}
- enable_clocks(1);
- dsi_enable_pll_clock(dsidev, 1);
-
- r = _dsi_reset(dsidev);
+ r = dsi_runtime_get(dsidev);
if (r)
- goto err1;
+ goto err_get_dsi;
- dsi_core_init(dsidev);
+ if(!dssdev->skip_init)
+ dsi_enable_pll_clock(dsidev, 1);
- r = dsi_display_init_dispc(dssdev);
- if (r)
- goto err1;
+ _dsi_initialize_irq(dsidev);
+
+ if(!dssdev->skip_init){
+ r = dsi_display_init_dispc(dssdev);
+ if (r)
+ goto err_init_dispc;
+ }
r = dsi_display_init_dsi(dssdev);
if (r)
- goto err2;
+ goto err_init_dsi;
mutex_unlock(&dsi->lock);
return 0;
-err2:
+err_init_dsi:
dsi_display_uninit_dispc(dssdev);
-err1:
- enable_clocks(0);
+err_init_dispc:
dsi_enable_pll_clock(dsidev, 0);
+ dsi_runtime_put(dsidev);
+err_get_dsi:
omap_dss_stop_device(dssdev);
-err0:
+err_start_dev:
mutex_unlock(&dsi->lock);
DSSDBG("dsi_display_enable FAILED\n");
return r;
@@ -4282,7 +4537,7 @@ void omapdss_dsi_display_disable(struct omap_dss_device *dssdev,
dsi_display_uninit_dsi(dssdev, disconnect_lanes, enter_ulps);
- enable_clocks(0);
+ dsi_runtime_put(dsidev);
dsi_enable_pll_clock(dsidev, 0);
omap_dss_stop_device(dssdev);
@@ -4322,9 +4577,12 @@ int dsi_init_display(struct omap_dss_device *dssdev)
DSSDBG("DSI init\n");
- /* XXX these should be figured out dynamically */
- dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
+ if(dssdev->phy.dsi.type == OMAP_DSS_DSI_TYPE_CMD_MODE) {
+ dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
+ } else {
+ dssdev->caps = 0;
+ }
if (dsi->vdds_dsi_reg == NULL) {
struct regulator *vdds_dsi;
@@ -4437,7 +4695,44 @@ static void dsi_calc_clock_param_ranges(struct platform_device *dsidev)
dsi->lpdiv_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_LPDIV);
}
-static int dsi_init(struct platform_device *dsidev)
+static int dsi_get_clocks(struct platform_device *dsidev)
+{
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ struct clk *clk;
+
+ clk = clk_get(&dsidev->dev, "dss_clk");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get dss_clk\n");
+ return PTR_ERR(clk);
+ }
+
+ dsi->dss_clk = clk;
+
+ clk = clk_get(&dsidev->dev, "sys_clk");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get sys_clk\n");
+ clk_put(dsi->dss_clk);
+ dsi->dss_clk = NULL;
+ return PTR_ERR(clk);
+ }
+
+ dsi->sys_clk = clk;
+
+ return 0;
+}
+
+static void dsi_put_clocks(struct platform_device *dsidev)
+{
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+
+ if (dsi->dss_clk)
+ clk_put(dsi->dss_clk);
+ if (dsi->sys_clk)
+ clk_put(dsi->sys_clk);
+}
+
+/* DSI1 HW IP initialisation */
+static int omap_dsi1hw_probe(struct platform_device *dsidev)
{
struct omap_display_platform_data *dss_plat_data;
struct omap_dss_board_info *board_info;
@@ -4449,7 +4744,7 @@ static int dsi_init(struct platform_device *dsidev)
dsi = kzalloc(sizeof(*dsi), GFP_KERNEL);
if (!dsi) {
r = -ENOMEM;
- goto err0;
+ goto err_alloc;
}
dsi->pdev = dsidev;
@@ -4472,6 +4767,14 @@ static int dsi_init(struct platform_device *dsidev)
mutex_init(&dsi->lock);
sema_init(&dsi->bus_lock, 1);
+ r = dsi_get_clocks(dsidev);
+ if (r)
+ goto err_get_clk;
+
+ mutex_init(&dsi->runtime_lock);
+
+ pm_runtime_enable(&dsidev->dev);
+
INIT_DELAYED_WORK_DEFERRABLE(&dsi->framedone_timeout_work,
dsi_framedone_timeout_work_callback);
@@ -4484,26 +4787,26 @@ static int dsi_init(struct platform_device *dsidev)
if (!dsi_mem) {
DSSERR("can't get IORESOURCE_MEM DSI\n");
r = -EINVAL;
- goto err1;
+ goto err_ioremap;
}
dsi->base = ioremap(dsi_mem->start, resource_size(dsi_mem));
if (!dsi->base) {
DSSERR("can't ioremap DSI\n");
r = -ENOMEM;
- goto err1;
+ goto err_ioremap;
}
dsi->irq = platform_get_irq(dsi->pdev, 0);
if (dsi->irq < 0) {
DSSERR("platform_get_irq failed\n");
r = -ENODEV;
- goto err2;
+ goto err_get_irq;
}
r = request_irq(dsi->irq, omap_dsi_irq_handler, IRQF_SHARED,
dev_name(&dsidev->dev), dsi->pdev);
if (r < 0) {
DSSERR("request_irq failed\n");
- goto err2;
+ goto err_get_irq;
}
/* DSI VCs initialization */
@@ -4515,7 +4818,9 @@ static int dsi_init(struct platform_device *dsidev)
dsi_calc_clock_param_ranges(dsidev);
- enable_clocks(1);
+ r = dsi_runtime_get(dsidev);
+ if (r)
+ goto err_get_dsi;
rev = dsi_read_reg(dsidev, DSI_REVISION);
dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n",
@@ -4523,21 +4828,32 @@ static int dsi_init(struct platform_device *dsidev)
dsi->num_data_lanes = dsi_get_num_data_lanes(dsidev);
- enable_clocks(0);
+ dsi_runtime_put(dsidev);
return 0;
-err2:
+
+err_get_dsi:
+ free_irq(dsi->irq, dsi->pdev);
+err_get_irq:
iounmap(dsi->base);
-err1:
+err_ioremap:
+ pm_runtime_disable(&dsidev->dev);
+err_get_clk:
kfree(dsi);
-err0:
+err_alloc:
return r;
}
-static void dsi_exit(struct platform_device *dsidev)
+static int omap_dsi1hw_remove(struct platform_device *dsidev)
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ WARN_ON(dsi->scp_clk_refcount > 0);
+
+ pm_runtime_disable(&dsidev->dev);
+
+ dsi_put_clocks(dsidev);
+
if (dsi->vdds_dsi_reg != NULL) {
if (dsi->vdds_dsi_enabled) {
regulator_disable(dsi->vdds_dsi_reg);
@@ -4553,29 +4869,6 @@ static void dsi_exit(struct platform_device *dsidev)
kfree(dsi);
- DSSDBG("omap_dsi_exit\n");
-}
-
-/* DSI1 HW IP initialisation */
-static int omap_dsi1hw_probe(struct platform_device *dsidev)
-{
- int r;
-
- r = dsi_init(dsidev);
- if (r) {
- DSSERR("Failed to initialize DSI\n");
- goto err_dsi;
- }
-err_dsi:
- return r;
-}
-
-static int omap_dsi1hw_remove(struct platform_device *dsidev)
-{
- struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
-
- dsi_exit(dsidev);
- WARN_ON(dsi->scp_clk_refcount > 0);
return 0;
}
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
index d9489d5..964eb08 100644
--- a/drivers/video/omap2/dss/dss.c
+++ b/drivers/video/omap2/dss/dss.c
@@ -28,6 +28,8 @@
#include <linux/delay.h>
#include <linux/seq_file.h>
#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <video/omapdss.h>
#include <plat/clock.h>
@@ -59,15 +61,12 @@ struct dss_reg {
static struct {
struct platform_device *pdev;
void __iomem *base;
- int ctx_id;
+
+ struct mutex runtime_lock;
+ int runtime_count;
struct clk *dpll4_m4_ck;
- struct clk *dss_ick;
- struct clk *dss_fck;
- struct clk *dss_sys_clk;
- struct clk *dss_tv_fck;
- struct clk *dss_video_fck;
- unsigned num_clks_enabled;
+ struct clk *dss_clk;
unsigned long cache_req_pck;
unsigned long cache_prate;
@@ -78,6 +77,7 @@ static struct {
enum omap_dss_clk_source dispc_clk_source;
enum omap_dss_clk_source lcd_clk_source[MAX_DSS_LCD_MANAGERS];
+ bool ctx_valid;
u32 ctx[DSS_SZ_REGS / sizeof(u32)];
} dss;
@@ -87,13 +87,6 @@ static const char * const dss_generic_clk_source_names[] = {
[OMAP_DSS_CLK_SRC_FCK] = "DSS_FCK",
};
-static void dss_clk_enable_all_no_ctx(void);
-static void dss_clk_disable_all_no_ctx(void);
-static void dss_clk_enable_no_ctx(enum dss_clock clks);
-static void dss_clk_disable_no_ctx(enum dss_clock clks);
-
-static int _omap_dss_wait_reset(void);
-
static inline void dss_write_reg(const struct dss_reg idx, u32 val)
{
__raw_writel(val, dss.base + idx.idx);
@@ -109,12 +102,10 @@ static inline u32 dss_read_reg(const struct dss_reg idx)
#define RR(reg) \
dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)])
-void dss_save_context(void)
+static void dss_save_context(void)
{
- if (cpu_is_omap24xx())
- return;
+ DSSDBG("dss_save_context\n");
- SR(SYSCONFIG);
SR(CONTROL);
if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) &
@@ -122,14 +113,19 @@ void dss_save_context(void)
SR(SDI_CONTROL);
SR(PLL_CONTROL);
}
+
+ dss.ctx_valid = true;
+
+ DSSDBG("context saved\n");
}
-void dss_restore_context(void)
+static void dss_restore_context(void)
{
- if (_omap_dss_wait_reset())
- DSSERR("DSS not coming out of reset after sleep\n");
+ DSSDBG("dss_restore_context\n");
+
+ if (!dss.ctx_valid)
+ return;
- RR(SYSCONFIG);
RR(CONTROL);
if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) &
@@ -137,6 +133,8 @@ void dss_restore_context(void)
RR(SDI_CONTROL);
RR(PLL_CONTROL);
}
+
+ DSSDBG("context restored\n");
}
#undef SR
@@ -234,6 +232,7 @@ const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src)
return dss_generic_clk_source_names[clk_src];
}
+
void dss_dump_clocks(struct seq_file *s)
{
unsigned long dpll4_ck_rate;
@@ -241,13 +240,14 @@ void dss_dump_clocks(struct seq_file *s)
const char *fclk_name, *fclk_real_name;
unsigned long fclk_rate;
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ if (dss_runtime_get())
+ return;
seq_printf(s, "- DSS -\n");
fclk_name = dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_FCK);
fclk_real_name = dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_FCK);
- fclk_rate = dss_clk_get_rate(DSS_CLK_FCK);
+ fclk_rate = clk_get_rate(dss.dss_clk);
if (dss.dpll4_m4_ck) {
dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck));
@@ -273,14 +273,15 @@ void dss_dump_clocks(struct seq_file *s)
fclk_rate);
}
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ dss_runtime_put();
}
void dss_dump_regs(struct seq_file *s)
{
#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r))
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ if (dss_runtime_get())
+ return;
DUMPREG(DSS_REVISION);
DUMPREG(DSS_SYSCONFIG);
@@ -294,7 +295,7 @@ void dss_dump_regs(struct seq_file *s)
DUMPREG(DSS_SDI_STATUS);
}
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ dss_runtime_put();
#undef DUMPREG
}
@@ -437,7 +438,7 @@ int dss_calc_clock_rates(struct dss_clock_info *cinfo)
} else {
if (cinfo->fck_div != 0)
return -EINVAL;
- cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK);
+ cinfo->fck = clk_get_rate(dss.dss_clk);
}
return 0;
@@ -467,7 +468,7 @@ int dss_set_clock_div(struct dss_clock_info *cinfo)
int dss_get_clock_div(struct dss_clock_info *cinfo)
{
- cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK);
+ cinfo->fck = clk_get_rate(dss.dss_clk);
if (dss.dpll4_m4_ck) {
unsigned long prate;
@@ -512,7 +513,7 @@ int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);
- fck = dss_clk_get_rate(DSS_CLK_FCK);
+ fck = clk_get_rate(dss.dss_clk);
if (req_pck == dss.cache_req_pck &&
((cpu_is_omap34xx() && prate == dss.cache_prate) ||
dss.cache_dss_cinfo.fck == fck)) {
@@ -539,7 +540,7 @@ retry:
if (dss.dpll4_m4_ck == NULL) {
struct dispc_clock_info cur_dispc;
/* XXX can we change the clock on omap2? */
- fck = dss_clk_get_rate(DSS_CLK_FCK);
+ fck = clk_get_rate(dss.dss_clk);
fck_div = 1;
dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
@@ -616,28 +617,6 @@ found:
return 0;
}
-static int _omap_dss_wait_reset(void)
-{
- int t = 0;
-
- while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) {
- if (++t > 1000) {
- DSSERR("soft reset failed\n");
- return -ENODEV;
- }
- udelay(1);
- }
-
- return 0;
-}
-
-static int _omap_dss_reset(void)
-{
- /* Soft reset */
- REG_FLD_MOD(DSS_SYSCONFIG, 1, 1, 1);
- return _omap_dss_wait_reset();
-}
-
void dss_set_venc_output(enum omap_dss_venc_type type)
{
int l = 0;
@@ -663,424 +642,111 @@ void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select hdmi)
REG_FLD_MOD(DSS_CONTROL, hdmi, 15, 15); /* VENC_HDMI_SWITCH */
}
-static int dss_init(void)
+static int dss_get_clocks(void)
{
+ struct clk *clk;
int r;
- u32 rev;
- struct resource *dss_mem;
- struct clk *dpll4_m4_ck;
- dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0);
- if (!dss_mem) {
- DSSERR("can't get IORESOURCE_MEM DSS\n");
- r = -EINVAL;
- goto fail0;
- }
- dss.base = ioremap(dss_mem->start, resource_size(dss_mem));
- if (!dss.base) {
- DSSERR("can't ioremap DSS\n");
- r = -ENOMEM;
- goto fail0;
+ clk = clk_get(&dss.pdev->dev, "dss_clk");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get clock dss_clk\n");
+ r = PTR_ERR(clk);
+ goto err;
}
- /* disable LCD and DIGIT output. This seems to fix the synclost
- * problem that we get, if the bootloader starts the DSS and
- * the kernel resets it */
- omap_writel(omap_readl(0x48050440) & ~0x3, 0x48050440);
-
-#ifdef CONFIG_OMAP2_DSS_SLEEP_BEFORE_RESET
- /* We need to wait here a bit, otherwise we sometimes start to
- * get synclost errors, and after that only power cycle will
- * restore DSS functionality. I have no idea why this happens.
- * And we have to wait _before_ resetting the DSS, but after
- * enabling clocks.
- *
- * This bug was at least present on OMAP3430. It's unknown
- * if it happens on OMAP2 or OMAP3630.
- */
- msleep(50);
-#endif
-
- _omap_dss_reset();
-
- /* autoidle */
- REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0);
+ dss.dss_clk = clk;
- /* Select DPLL */
- REG_FLD_MOD(DSS_CONTROL, 0, 0, 0);
-
-#ifdef CONFIG_OMAP2_DSS_VENC
- REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */
- REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */
- REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */
-#endif
if (cpu_is_omap34xx()) {
- dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck");
- if (IS_ERR(dpll4_m4_ck)) {
+ clk = clk_get(NULL, "dpll4_m4_ck");
+ if (IS_ERR(clk)) {
DSSERR("Failed to get dpll4_m4_ck\n");
- r = PTR_ERR(dpll4_m4_ck);
- goto fail1;
+ r = PTR_ERR(clk);
+ goto err;
}
} else if (cpu_is_omap44xx()) {
- dpll4_m4_ck = clk_get(NULL, "dpll_per_m5x2_ck");
- if (IS_ERR(dpll4_m4_ck)) {
- DSSERR("Failed to get dpll4_m4_ck\n");
- r = PTR_ERR(dpll4_m4_ck);
- goto fail1;
+ clk = clk_get(NULL, "dpll_per_m5x2_ck");
+ if (IS_ERR(clk)) {
+ DSSERR("Failed to get dpll_per_m5x2_ck\n");
+ r = PTR_ERR(clk);
+ goto err;
}
} else { /* omap24xx */
- dpll4_m4_ck = NULL;
+ clk = NULL;
}
- dss.dpll4_m4_ck = dpll4_m4_ck;
-
- dss.dsi_clk_source[0] = OMAP_DSS_CLK_SRC_FCK;
- dss.dsi_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;
- dss.dispc_clk_source = OMAP_DSS_CLK_SRC_FCK;
- dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK;
- dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;
-
- dss_save_context();
-
- rev = dss_read_reg(DSS_REVISION);
- printk(KERN_INFO "OMAP DSS rev %d.%d\n",
- FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
+ dss.dpll4_m4_ck = clk;
return 0;
-fail1:
- iounmap(dss.base);
-fail0:
- return r;
-}
-
-static void dss_exit(void)
-{
+err:
+ if (dss.dss_clk)
+ clk_put(dss.dss_clk);
if (dss.dpll4_m4_ck)
clk_put(dss.dpll4_m4_ck);
- iounmap(dss.base);
-}
-
-/* CONTEXT */
-static int dss_get_ctx_id(void)
-{
- struct omap_display_platform_data *pdata = dss.pdev->dev.platform_data;
- int r;
-
- if (!pdata->board_data->get_last_off_on_transaction_id)
- return 0;
- r = pdata->board_data->get_last_off_on_transaction_id(&dss.pdev->dev);
- if (r < 0) {
- dev_err(&dss.pdev->dev, "getting transaction ID failed, "
- "will force context restore\n");
- r = -1;
- }
return r;
}
-int dss_need_ctx_restore(void)
-{
- int id = dss_get_ctx_id();
-
- if (id < 0 || id != dss.ctx_id) {
- DSSDBG("ctx id %d -> id %d\n",
- dss.ctx_id, id);
- dss.ctx_id = id;
- return 1;
- } else {
- return 0;
- }
-}
-
-static void save_all_ctx(void)
-{
- DSSDBG("save context\n");
-
- dss_clk_enable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK);
-
- dss_save_context();
- dispc_save_context();
-#ifdef CONFIG_OMAP2_DSS_DSI
- dsi_save_context();
-#endif
-
- dss_clk_disable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK);
-}
-
-static void restore_all_ctx(void)
-{
- DSSDBG("restore context\n");
-
- dss_clk_enable_all_no_ctx();
-
- dss_restore_context();
- dispc_restore_context();
-#ifdef CONFIG_OMAP2_DSS_DSI
- dsi_restore_context();
-#endif
-
- dss_clk_disable_all_no_ctx();
-}
-
-static int dss_get_clock(struct clk **clock, const char *clk_name)
+static void dss_put_clocks(void)
{
- struct clk *clk;
-
- clk = clk_get(&dss.pdev->dev, clk_name);
-
- if (IS_ERR(clk)) {
- DSSERR("can't get clock %s", clk_name);
- return PTR_ERR(clk);
- }
-
- *clock = clk;
-
- DSSDBG("clk %s, rate %ld\n", clk_name, clk_get_rate(clk));
-
- return 0;
+ if (dss.dpll4_m4_ck)
+ clk_put(dss.dpll4_m4_ck);
+ clk_put(dss.dss_clk);
}
-static int dss_get_clocks(void)
+int dss_runtime_get(void)
{
int r;
- struct omap_display_platform_data *pdata = dss.pdev->dev.platform_data;
- dss.dss_ick = NULL;
- dss.dss_fck = NULL;
- dss.dss_sys_clk = NULL;
- dss.dss_tv_fck = NULL;
- dss.dss_video_fck = NULL;
-
- r = dss_get_clock(&dss.dss_ick, "ick");
- if (r)
- goto err;
+ mutex_lock(&dss.runtime_lock);
- r = dss_get_clock(&dss.dss_fck, "fck");
- if (r)
- goto err;
+ if (dss.runtime_count++ == 0) {
+ DSSDBG("dss_runtime_get\n");
- if (!pdata->opt_clock_available) {
- r = -ENODEV;
- goto err;
- }
+ clk_enable(dss.dss_clk);
- if (pdata->opt_clock_available("sys_clk")) {
- r = dss_get_clock(&dss.dss_sys_clk, "sys_clk");
- if (r)
+ r = pm_runtime_get_sync(&dss.pdev->dev);
+ WARN_ON(r);
+ if (r < 0)
goto err;
- }
- if (pdata->opt_clock_available("tv_clk")) {
- r = dss_get_clock(&dss.dss_tv_fck, "tv_clk");
- if (r)
- goto err;
+ dss_restore_context();
}
- if (pdata->opt_clock_available("video_clk")) {
- r = dss_get_clock(&dss.dss_video_fck, "video_clk");
- if (r)
- goto err;
- }
+ mutex_unlock(&dss.runtime_lock);
return 0;
err:
- if (dss.dss_ick)
- clk_put(dss.dss_ick);
- if (dss.dss_fck)
- clk_put(dss.dss_fck);
- if (dss.dss_sys_clk)
- clk_put(dss.dss_sys_clk);
- if (dss.dss_tv_fck)
- clk_put(dss.dss_tv_fck);
- if (dss.dss_video_fck)
- clk_put(dss.dss_video_fck);
-
+ clk_disable(dss.dss_clk);
+ mutex_unlock(&dss.runtime_lock);
return r;
}
-static void dss_put_clocks(void)
+void dss_runtime_put(void)
{
- if (dss.dss_video_fck)
- clk_put(dss.dss_video_fck);
- if (dss.dss_tv_fck)
- clk_put(dss.dss_tv_fck);
- if (dss.dss_sys_clk)
- clk_put(dss.dss_sys_clk);
- clk_put(dss.dss_fck);
- clk_put(dss.dss_ick);
-}
-
-unsigned long dss_clk_get_rate(enum dss_clock clk)
-{
- switch (clk) {
- case DSS_CLK_ICK:
- return clk_get_rate(dss.dss_ick);
- case DSS_CLK_FCK:
- return clk_get_rate(dss.dss_fck);
- case DSS_CLK_SYSCK:
- return clk_get_rate(dss.dss_sys_clk);
- case DSS_CLK_TVFCK:
- return clk_get_rate(dss.dss_tv_fck);
- case DSS_CLK_VIDFCK:
- return clk_get_rate(dss.dss_video_fck);
- }
-
- BUG();
- return 0;
-}
-
-static unsigned count_clk_bits(enum dss_clock clks)
-{
- unsigned num_clks = 0;
-
- if (clks & DSS_CLK_ICK)
- ++num_clks;
- if (clks & DSS_CLK_FCK)
- ++num_clks;
- if (clks & DSS_CLK_SYSCK)
- ++num_clks;
- if (clks & DSS_CLK_TVFCK)
- ++num_clks;
- if (clks & DSS_CLK_VIDFCK)
- ++num_clks;
-
- return num_clks;
-}
-
-static void dss_clk_enable_no_ctx(enum dss_clock clks)
-{
- unsigned num_clks = count_clk_bits(clks);
-
- if (clks & DSS_CLK_ICK)
- clk_enable(dss.dss_ick);
- if (clks & DSS_CLK_FCK)
- clk_enable(dss.dss_fck);
- if ((clks & DSS_CLK_SYSCK) && dss.dss_sys_clk)
- clk_enable(dss.dss_sys_clk);
- if ((clks & DSS_CLK_TVFCK) && dss.dss_tv_fck)
- clk_enable(dss.dss_tv_fck);
- if ((clks & DSS_CLK_VIDFCK) && dss.dss_video_fck)
- clk_enable(dss.dss_video_fck);
-
- dss.num_clks_enabled += num_clks;
-}
+ mutex_lock(&dss.runtime_lock);
-void dss_clk_enable(enum dss_clock clks)
-{
- bool check_ctx = dss.num_clks_enabled == 0;
-
- dss_clk_enable_no_ctx(clks);
-
- /*
- * HACK: On omap4 the registers may not be accessible right after
- * enabling the clocks. At some point this will be handled by
- * pm_runtime, but for the time begin this should make things work.
- */
- if (cpu_is_omap44xx() && check_ctx)
- udelay(10);
-
- if (check_ctx && cpu_is_omap34xx() && dss_need_ctx_restore())
- restore_all_ctx();
-}
+ if (--dss.runtime_count == 0) {
+ int r;
-static void dss_clk_disable_no_ctx(enum dss_clock clks)
-{
- unsigned num_clks = count_clk_bits(clks);
-
- if (clks & DSS_CLK_ICK)
- clk_disable(dss.dss_ick);
- if (clks & DSS_CLK_FCK)
- clk_disable(dss.dss_fck);
- if ((clks & DSS_CLK_SYSCK) && dss.dss_sys_clk)
- clk_disable(dss.dss_sys_clk);
- if ((clks & DSS_CLK_TVFCK) && dss.dss_tv_fck)
- clk_disable(dss.dss_tv_fck);
- if ((clks & DSS_CLK_VIDFCK) && dss.dss_video_fck)
- clk_disable(dss.dss_video_fck);
-
- dss.num_clks_enabled -= num_clks;
-}
+ DSSDBG("dss_runtime_put\n");
-void dss_clk_disable(enum dss_clock clks)
-{
- if (cpu_is_omap34xx()) {
- unsigned num_clks = count_clk_bits(clks);
+ dss_save_context();
- BUG_ON(dss.num_clks_enabled < num_clks);
+ r = pm_runtime_put_sync(&dss.pdev->dev);
+ WARN_ON(r);
- if (dss.num_clks_enabled == num_clks)
- save_all_ctx();
+ clk_disable(dss.dss_clk);
}
- dss_clk_disable_no_ctx(clks);
-}
-
-static void dss_clk_enable_all_no_ctx(void)
-{
- enum dss_clock clks;
-
- clks = DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_SYSCK | DSS_CLK_TVFCK;
- if (cpu_is_omap34xx())
- clks |= DSS_CLK_VIDFCK;
- dss_clk_enable_no_ctx(clks);
+ mutex_unlock(&dss.runtime_lock);
}
-static void dss_clk_disable_all_no_ctx(void)
-{
- enum dss_clock clks;
-
- clks = DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_SYSCK | DSS_CLK_TVFCK;
- if (cpu_is_omap34xx())
- clks |= DSS_CLK_VIDFCK;
- dss_clk_disable_no_ctx(clks);
-}
-
-#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
-/* CLOCKS */
-static void core_dump_clocks(struct seq_file *s)
-{
- int i;
- struct clk *clocks[5] = {
- dss.dss_ick,
- dss.dss_fck,
- dss.dss_sys_clk,
- dss.dss_tv_fck,
- dss.dss_video_fck
- };
-
- const char *names[5] = {
- "ick",
- "fck",
- "sys_clk",
- "tv_fck",
- "video_fck"
- };
-
- seq_printf(s, "- CORE -\n");
-
- seq_printf(s, "internal clk count\t\t%u\n", dss.num_clks_enabled);
-
- for (i = 0; i < 5; i++) {
- if (!clocks[i])
- continue;
- seq_printf(s, "%s (%s)%*s\t%lu\t%d\n",
- names[i],
- clocks[i]->name,
- 24 - strlen(names[i]) - strlen(clocks[i]->name),
- "",
- clk_get_rate(clocks[i]),
- clocks[i]->usecount);
- }
-}
-#endif /* defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) */
-
/* DEBUGFS */
#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
void dss_debug_dump_clocks(struct seq_file *s)
{
- core_dump_clocks(s);
dss_dump_clocks(s);
dispc_dump_clocks(s);
#ifdef CONFIG_OMAP2_DSS_DSI
@@ -1089,28 +755,53 @@ void dss_debug_dump_clocks(struct seq_file *s)
}
#endif
-
/* DSS HW IP initialisation */
static int omap_dsshw_probe(struct platform_device *pdev)
{
+ struct resource *dss_mem;
+ u32 rev;
int r;
dss.pdev = pdev;
+ dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0);
+ if (!dss_mem) {
+ DSSERR("can't get IORESOURCE_MEM DSS\n");
+ r = -EINVAL;
+ goto err_ioremap;
+ }
+ dss.base = ioremap(dss_mem->start, resource_size(dss_mem));
+ if (!dss.base) {
+ DSSERR("can't ioremap DSS\n");
+ r = -ENOMEM;
+ goto err_ioremap;
+ }
+
r = dss_get_clocks();
if (r)
goto err_clocks;
- dss_clk_enable_all_no_ctx();
+ mutex_init(&dss.runtime_lock);
- dss.ctx_id = dss_get_ctx_id();
- DSSDBG("initial ctx id %u\n", dss.ctx_id);
+ pm_runtime_enable(&pdev->dev);
- r = dss_init();
- if (r) {
- DSSERR("Failed to initialize DSS\n");
- goto err_dss;
- }
+ r = dss_runtime_get();
+ if (r)
+ goto err_runtime_get;
+
+ /* Select DPLL */
+ REG_FLD_MOD(DSS_CONTROL, 0, 0, 0);
+
+#ifdef CONFIG_OMAP2_DSS_VENC
+ REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */
+ REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */
+ REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */
+#endif
+ dss.dsi_clk_source[0] = OMAP_DSS_CLK_SRC_FCK;
+ dss.dsi_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;
+ dss.dispc_clk_source = OMAP_DSS_CLK_SRC_FCK;
+ dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK;
+ dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;
r = dpi_init();
if (r) {
@@ -1124,33 +815,37 @@ static int omap_dsshw_probe(struct platform_device *pdev)
goto err_sdi;
}
- dss_clk_disable_all_no_ctx();
+ rev = dss_read_reg(DSS_REVISION);
+ printk(KERN_INFO "OMAP DSS rev %d.%d\n",
+ FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
+
+ dss_runtime_put();
+
return 0;
err_sdi:
dpi_exit();
err_dpi:
- dss_exit();
-err_dss:
- dss_clk_disable_all_no_ctx();
+ dss_runtime_put();
+err_runtime_get:
+ pm_runtime_disable(&pdev->dev);
dss_put_clocks();
err_clocks:
+ iounmap(dss.base);
+err_ioremap:
return r;
}
static int omap_dsshw_remove(struct platform_device *pdev)
{
+ dpi_exit();
+ sdi_exit();
- dss_exit();
+ iounmap(dss.base);
- /*
- * As part of hwmod changes, DSS is not the only controller of dss
- * clocks; hwmod framework itself will also enable clocks during hwmod
- * init for dss, and autoidle is set in h/w for DSS. Hence, there's no
- * need to disable clocks if their usecounts > 1.
- */
- WARN_ON(dss.num_clks_enabled > 0);
+ pm_runtime_disable(&pdev->dev);
dss_put_clocks();
+
return 0;
}
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 8ab6d43..22cd979 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -109,14 +109,6 @@ enum omap_parallel_interface_mode {
OMAP_DSS_PARALLELMODE_DSI,
};
-enum dss_clock {
- DSS_CLK_ICK = 1 << 0, /* DSS_L3_ICLK and DSS_L4_ICLK */
- DSS_CLK_FCK = 1 << 1, /* DSS1_ALWON_FCLK */
- DSS_CLK_SYSCK = 1 << 2, /* DSS2_ALWON_FCLK */
- DSS_CLK_TVFCK = 1 << 3, /* DSS_TV_FCLK */
- DSS_CLK_VIDFCK = 1 << 4, /* DSS_96M_FCLK*/
-};
-
enum dss_hdmi_venc_clk_source_select {
DSS_VENC_TV_CLK = 0,
DSS_HDMI_M_PCLK = 1,
@@ -164,14 +156,28 @@ struct dsi_clock_info {
bool use_sys_clk;
};
-/* HDMI PLL structure */
-struct hdmi_pll_info {
- u16 regn;
- u16 regm;
- u32 regmf;
- u16 regm2;
- u16 regsd;
- u16 dcofreq;
+struct dispc_config {
+ u32 sizex, sizey;
+ u32 burstsize;
+ u32 pixelinc;
+ u32 rowinc;
+ u32 bursttype;
+ u32 antiflicker;
+ u32 doublestride;
+ u32 ba;
+ u32 bacbcr;
+ u32 format;
+ u32 rotation;
+ u32 gfx_top_buffer;
+ u32 gfx_bottom_buffer;
+ u32 vid1_top_buffer;
+ u32 vid1_bottom_buffer;
+ u32 vid2_top_buffer;
+ u32 vid2_bottom_buffer;
+ u32 vid3_top_buffer;
+ u32 vid3_bottom_buffer;
+ u32 wb_top_buffer;
+ u32 wb_bottom_buffer;
};
struct seq_file;
@@ -220,13 +226,10 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force);
int dss_init_platform_driver(void);
void dss_uninit_platform_driver(void);
+int dss_runtime_get(void);
+void dss_runtime_put(void);
+
void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select);
-void dss_save_context(void);
-void dss_restore_context(void);
-void dss_clk_enable(enum dss_clock clks);
-void dss_clk_disable(enum dss_clock clks);
-unsigned long dss_clk_get_rate(enum dss_clock clk);
-int dss_need_ctx_restore(void);
const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src);
void dss_dump_clocks(struct seq_file *s);
@@ -283,15 +286,15 @@ struct file_operations;
int dsi_init_platform_driver(void);
void dsi_uninit_platform_driver(void);
+int dsi_runtime_get(struct platform_device *dsidev);
+void dsi_runtime_put(struct platform_device *dsidev);
+
void dsi_dump_clocks(struct seq_file *s);
void dsi_create_debugfs_files_irq(struct dentry *debugfs_dir,
const struct file_operations *debug_fops);
void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir,
const struct file_operations *debug_fops);
-void dsi_save_context(void);
-void dsi_restore_context(void);
-
int dsi_init_display(struct omap_dss_device *display);
void dsi_irq_handler(void);
unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev);
@@ -317,6 +320,13 @@ static inline int dsi_init_platform_driver(void)
static inline void dsi_uninit_platform_driver(void)
{
}
+static inline int dsi_runtime_get(struct platform_device *dsidev)
+{
+ return 0;
+}
+static inline void dsi_runtime_put(struct platform_device *dsidev)
+{
+}
static inline unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev)
{
WARN("%s: DSI not compiled in, returning rate as 0\n", __func__);
@@ -384,8 +394,8 @@ void dispc_dump_regs(struct seq_file *s);
void dispc_irq_handler(void);
void dispc_fake_vsync_irq(void);
-void dispc_save_context(void);
-void dispc_restore_context(void);
+int dispc_runtime_get(void);
+void dispc_runtime_put(void);
void dispc_enable_sidle(void);
void dispc_disable_sidle(void);
@@ -402,6 +412,14 @@ 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_enable_cpr(enum omap_channel channel, bool enable);
+void dispc_set_cpr_coef(enum omap_channel channel,
+ struct omap_dss_cpr_coefs *coefs);
+void _dispc_setup_color_conv_coef(enum omap_plane plane,
+ const struct omap_dss_cconv_coefs *ct);
void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr);
void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr);
@@ -418,15 +436,26 @@ 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,
+ enum omap_dss_rotation_type type,
+ 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);
-void dispc_enable_channel(enum omap_channel channel, bool enable);
+void dispc_enable_channel(enum omap_channel channel,
+ enum omap_display_type type, bool enable);
bool dispc_is_channel_enabled(enum omap_channel channel);
int dispc_enable_plane(enum omap_plane plane, bool enable);
void dispc_enable_replication(enum omap_plane plane, bool enable);
@@ -467,7 +496,7 @@ int dispc_set_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo);
int dispc_get_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo);
-
+u32 sa_calc_wrap(struct dispc_config *dispc_reg_config, u32 channel_no);
/* VENC */
#ifdef CONFIG_OMAP2_DSS_VENC
@@ -508,8 +537,26 @@ void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev);
void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev);
int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev,
struct omap_video_timings *timings);
+int omapdss_hdmi_display_set_mode(struct omap_dss_device *dssdev,
+ struct fb_videomode *mode);
+void omapdss_hdmi_restart(void);
+int hdmi_panel_hpd_handler(int hpd);
+int omapdss_hdmi_get_pixel_clock(void);
+int omapdss_hdmi_get_mode(void);
+int omapdss_hdmi_get_deepcolor(void);
+void omapdss_hdmi_set_deepcolor(int val);
+int hdmi_get_current_hpd(void);
+void hdmi_get_monspecs(struct fb_monspecs *specs);
+u8 *hdmi_read_edid(struct omap_video_timings *);
+
int hdmi_panel_init(void);
void hdmi_panel_exit(void);
+void hdmi_dump_regs(struct seq_file *s);
+int omapdss_hdmi_register_hdcp_callbacks(void (*hdmi_start_frame_cb)(void),
+ void (*hdmi_irq_cb)(int status),
+ bool (*hdmi_power_on_cb)(void));
+int omap_dss_ovl_set_info(struct omap_overlay *ovl,
+ struct omap_overlay_info *info);
/* RFBI */
#ifdef CONFIG_OMAP2_DSS_RFBI
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c
index 1c18888..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 |
@@ -286,7 +296,9 @@ static const struct omap_dss_features omap3430_dss_features = {
FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE |
FEAT_FUNCGATED | FEAT_ROWREPEATENABLE |
FEAT_LINEBUFFERSPLIT | FEAT_RESIZECONF |
- FEAT_DSI_PLL_FREQSEL | FEAT_DSI_REVERSE_TXCLKESC,
+ FEAT_DSI_PLL_FREQSEL | FEAT_DSI_REVERSE_TXCLKESC |
+ FEAT_VENC_REQUIRES_TV_DAC_CLK | FEAT_CPR | FEAT_PRELOAD |
+ FEAT_FIR_COEF_V,
.num_mgrs = 2,
.num_ovls = 3,
@@ -306,7 +318,8 @@ static const struct omap_dss_features omap3630_dss_features = {
FEAT_PRE_MULT_ALPHA | FEAT_FUNCGATED |
FEAT_ROWREPEATENABLE | FEAT_LINEBUFFERSPLIT |
FEAT_RESIZECONF | FEAT_DSI_PLL_PWR_BUG |
- FEAT_DSI_PLL_FREQSEL,
+ FEAT_DSI_PLL_FREQSEL | FEAT_CPR | FEAT_PRELOAD |
+ FEAT_FIR_COEF_V,
.num_mgrs = 2,
.num_ovls = 3,
@@ -327,10 +340,13 @@ static const struct omap_dss_features omap4430_es1_0_dss_features = {
FEAT_MGR_LCD2 | FEAT_GLOBAL_ALPHA_VID1 |
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_DSI_GNQ | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 |
+ 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,
@@ -348,10 +364,13 @@ static const struct omap_dss_features omap4_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_HDMI_CTS_SWMODE |
- FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2,
+ FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | 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,
diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h
index 07b346f..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
@@ -51,6 +51,13 @@ enum dss_feat_id {
FEAT_HDMI_CTS_SWMODE = 1 << 19,
FEAT_HANDLE_UV_SEPARATE = 1 << 20,
FEAT_ATTR2 = 1 << 21,
+ FEAT_VENC_REQUIRES_TV_DAC_CLK = 1 << 22,
+ 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/fifothreshold.c b/drivers/video/omap2/dss/fifothreshold.c
new file mode 100644
index 0000000..431f4c1
--- /dev/null
+++ b/drivers/video/omap2/dss/fifothreshold.c
@@ -0,0 +1,422 @@
+/*
+ * linux/drivers/video/omap2/dss/fifothreshold.c
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * 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 <video/omapdss.h>
+#include "dss.h"
+
+#define YUV422_UYVY 10
+#define YUV422_YUV2 11
+
+struct sa_struct {
+ u32 min_sa;
+ u32 max_lt;
+ u32 min_lt;
+};
+
+struct ddma_config {
+ u16 twomode;
+ u16 antifckr;
+ u16 double_stride;
+ u16 bpp;
+ u16 bitmap;
+ u16 pixel_inc;
+ u16 max_burst;
+ u16 gballoc;
+ u16 vballoc;
+ u16 yuv420;
+ u32 rowincr;
+ u32 ba;
+ u32 size_x;
+ u32 size_y;
+};
+
+/*
+ * bitpk : used to return partial bit vectors of bigger
+ * bit vector, as and when required in algorithm.
+ * Ex: bitpk(BaseAddress,28,27) = BaseAddress[28:27]
+ */
+static inline u32 bitpk(unsigned long a, u32 left, u32 right)
+{
+ return (a >> right) & ((2 << (left - right)) - 1);
+}
+
+/*
+ * dispc_reg_to_ddma converts the DISPC register values into information
+ * used by the DDMA. Ex: format=15 => BytesPerPixel = 3
+ * dispcRegConfig :: Dispc register information
+ * ChannelNo :: ChannelNo
+ * y_nuv :: 1->Luma frame parameters, calculation ;
+ * 0->Chroma frame parameters and calculation
+ * bh_config :: Output struct having information useful for the algorithm
+ */
+static void dispc_reg_to_ddma(struct dispc_config *dispc_reg_config,
+ u32 channel_no, u32 y_nuv, struct ddma_config *bh_config)
+{
+ u16 i;
+ /* GFX pipe specific conversions */
+ if (channel_no == 0) {
+ /*
+ * For bitmap formats the pixcel information is stored in bits.
+ * This needs to be divided by 8 to convert into bytes.
+ */
+ bh_config->bitmap = (dispc_reg_config->format <= 2) ? 8 : 1;
+ /*
+ * In case of GFX there is no YUV420 mode:
+ * yuv420: 1->nonYUV420 2-> YUV420
+ */
+ bh_config->yuv420 = 1;
+ bh_config->pixel_inc = dispc_reg_config->pixelinc;
+ switch (dispc_reg_config->format) {
+ /* LUT for format<-->Bytesper pixel */
+ case 0:
+ case 3:
+ i = 1;
+ break;
+ case 1:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 10:
+ case 11:
+ case 15:
+ i = 2;
+ break;
+ case 9:
+ i = 3;
+ break;
+ default:
+ i = 4;
+ break;
+ }
+ bh_config->bpp = i;
+ i = 0;
+ /*
+ * Chroma double_stride value of DISPC registers is invalid
+ * for GFX where there is np YUV420 format.
+ */
+ bh_config->double_stride = 0;
+ bh_config->antifckr = dispc_reg_config->antiflicker;
+ bh_config->ba = dispc_reg_config->ba;
+ bh_config->size_x = dispc_reg_config->sizex;
+ } else {
+ /*
+ * For all Video channels
+ *
+ * In Video there is no bitmap format, All format pixcel is
+ * stored in multiples of bytes.
+ */
+ bh_config->bitmap = 1;
+ /* No antiflicker mode for Video channels */
+ bh_config->antifckr = 0;
+ /*
+ * 1->nonYUV420 2-> YUV420 : Used in breaking up the buffer
+ * allocation:: Top:Luma, Bottom:Chroma
+ */
+ bh_config->yuv420 = (dispc_reg_config->format == 0) ? 2 : 1;
+
+ switch (dispc_reg_config->format) {
+ /* LUT for format<-->Bytesper pixel */
+ /* bpp:1 for Luma bpp:2 for Chroma */
+ case 0:
+ i = (y_nuv ? 1 : 2);
+ break;
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 15:
+ i = 2;
+ break;
+ case 9:
+ i = 3;
+ break;
+ case 10:
+ case 11:
+ i = (dispc_reg_config->rotation == 1 ||
+ dispc_reg_config->rotation == 3) ? 4 : 2;
+ break;
+
+ /* Format 10,11 => YUV422. YUV422+No rotation : bpp =bpp/2 */
+ default:
+ i = 4;
+ break;
+ }
+
+ /*
+ * PixcelIncrement = numberOfPixcelsInterleaving*BytesPerPixcel
+ * + 1. For Chroma pixcelincrement should be doubled to leave
+ * same number of Chroma pixels and Luma.
+ */
+ bh_config->pixel_inc =
+ (dispc_reg_config->format == 0 && y_nuv == 0) ?
+ (dispc_reg_config->pixelinc - 1) * 2 + 1 :
+ dispc_reg_config->pixelinc;
+
+ /*
+ * for YUV422+No rotation : bpp =bpp/2:: To correct Pixcel
+ * increment accordingly use in stride calculation.
+ */
+ bh_config->pixel_inc =
+ ((dispc_reg_config->format == YUV422_UYVY ||
+ dispc_reg_config->format == YUV422_YUV2) &&
+ (dispc_reg_config->rotation == 0 ||
+ dispc_reg_config->rotation == 2)) ?
+ (dispc_reg_config->pixelinc - 1) / 2 + 1 :
+ bh_config->pixel_inc;
+ bh_config->bpp = i;
+ bh_config->double_stride =
+ (dispc_reg_config->format == 0 && y_nuv == 0) ?
+ dispc_reg_config->doublestride : 0;
+
+ /* Conditions in which SizeY is halfed = i; */
+ i = (((dispc_reg_config->rotation == 1 ||
+ dispc_reg_config->rotation == 3) &&
+ (dispc_reg_config->format == YUV422_UYVY ||
+ dispc_reg_config->format == YUV422_YUV2)) ||
+ ((dispc_reg_config->rotation == 1 ||
+ dispc_reg_config->rotation == 3 ||
+ bh_config->double_stride == 1) &&
+ dispc_reg_config->format == 0 && y_nuv == 0)) ? 1 : 0;
+
+ /* Choosing between BA_Y and BA_CbCr */
+ bh_config->ba =
+ (dispc_reg_config->format == 0 && y_nuv == 0) ?
+ dispc_reg_config->bacbcr :
+ dispc_reg_config->ba;
+
+ /* SizeX halfed for Chroma frame */
+ bh_config->size_x =
+ (dispc_reg_config->format == 0 && y_nuv == 0) ?
+ (dispc_reg_config->sizex + 1) / 2 - 1 :
+ dispc_reg_config->sizex;
+ }
+
+ bh_config->twomode = dispc_reg_config->bursttype;
+ bh_config->size_y = ((dispc_reg_config->sizey + 1) >> i) - 1;
+ bh_config->rowincr = dispc_reg_config->rowinc;
+ bh_config->max_burst = 1 << (dispc_reg_config->burstsize + 1);
+
+ /* Decoding the burstSize to be used in BH calculation algorithm */
+ bh_config->gballoc =
+ (dispc_reg_config->gfx_bottom_buffer == channel_no) +
+ (dispc_reg_config->gfx_top_buffer == channel_no);
+ bh_config->vballoc =
+ (dispc_reg_config->vid1_bottom_buffer == channel_no) +
+ (dispc_reg_config->vid1_top_buffer == channel_no) +
+ (dispc_reg_config->vid2_bottom_buffer == channel_no) +
+ (dispc_reg_config->vid2_top_buffer == channel_no) +
+ (dispc_reg_config->vid3_bottom_buffer == channel_no) +
+ (dispc_reg_config->vid3_top_buffer == channel_no) +
+ (dispc_reg_config->wb_bottom_buffer == channel_no) +
+ (dispc_reg_config->wb_top_buffer == channel_no);
+}
+
+/*
+ * sa_calc calculates SA and LT values for one set of DISPC reg inputs
+ * dispc_reg_config :: Dispc register information
+ * channel_no :: channel_no
+ * y_nuv :: 1->Luma frame parameters, calculation
+ * 0->Chroma frame parameters and calculation
+ * sa_info :: Output struct having information of SA and LT values
+ */
+static void sa_calc(struct dispc_config *dispc_reg_config, u32 channel_no,
+ u32 y_nuv, struct sa_struct *sa_info)
+{
+ u32 Sorientation, mode, mode_0, mode_1;
+ int blkh_opt;
+ int pagemode;
+ long int sizeX_nopred, sizeX_pred;
+ u32 pict_16word;
+ long int pict_16word_ceil;
+ long int stride;
+ int stride_8k;
+ int stride_16k;
+ int stride_32k;
+ int stride_64k;
+ int stride_ok, stride_val;
+ int linesRem;
+ u32 BA_bhbit, bh_max;
+ int burstHeight;
+ int i;
+ int bh2d_cond;
+ int C1, c1flag, C2;
+ long int Tot_mem;
+ struct ddma_config bh_config;
+
+ dispc_reg_to_ddma(dispc_reg_config, channel_no, y_nuv, &bh_config);
+
+ mode = bitpk(bh_config.ba, 28, 27);
+ mode_1 = bitpk(bh_config.ba, 28, 28);
+ mode_0 = bitpk(bh_config.ba, 27, 27);
+ Sorientation = bitpk(bh_config.ba, 31, 31);
+
+ pagemode = (mode == 3);
+ blkh_opt = mode_1 ? 2 : 4;
+
+ bh_config.double_stride = (bh_config.double_stride == 1
+ && bh_config.twomode == 1) ? 2 : 1;
+
+ /* SizeX in frame = number of pixels * BytesPerPixel */
+ sizeX_nopred = ((bh_config.size_x + 1) * bh_config.bpp) /
+ bh_config.bitmap;
+ /* Size including skipped pixels */
+ sizeX_pred = ((bh_config.size_x + 1) *
+ (bh_config.pixel_inc - 1 + bh_config.bpp)) / bh_config.bitmap;
+ stride = ((bh_config.rowincr - 1) + sizeX_pred) *
+ bh_config.double_stride;
+ stride_8k = stride == 8192 && mode_1 == 0 && Sorientation;
+ stride_16k = stride == 16384 && !(mode_0 != mode_1 && !Sorientation);
+ stride_32k = stride == 32768 && (mode_1 == 1 || !Sorientation);
+ stride_64k = stride == 65536 && !(mode_0 == mode_1) && !Sorientation;
+ stride_ok = (stride_8k || stride_16k || stride_32k || stride_64k);
+ stride_val = stride_64k ? 16 : stride_32k ? 15 : stride_16k ? 14 : 13;
+
+ linesRem = bh_config.size_y + 1;
+
+ /* Condition than enables 2D fetch of OCP */
+ bh2d_cond = (bh_config.twomode && (pagemode == 0)
+ && stride_ok && (linesRem > 0));
+
+ /*
+ * BH calculation algorithm depending on stride,NumberofLinesInFrame,
+ * other parameters of Tiler alignment of base address.
+ */
+ C1 = C2 = c1flag = 0;
+ for (i = 1; i <= 5 && linesRem > 0 && c1flag == 0; i++) {
+ if (bh2d_cond) {
+ /* 2D transfer */
+ BA_bhbit = bitpk(bh_config.ba,
+ stride_val + (mode_1 == 0),
+ stride_val);
+ bh_max = blkh_opt - BA_bhbit;
+
+ burstHeight = min(linesRem,
+ min((int) bh_config.max_burst, (int) bh_max));
+
+ if (burstHeight == 3 ||
+ (burstHeight == 4 && bh_config.antifckr == 1))
+ burstHeight = 2;
+ } else {
+ burstHeight = 1;
+ }
+ if ((C1 + burstHeight) <= 4 && c1flag == 0) {
+ /*
+ * C1 incremented until its >= 4. ensures howmany
+ * full lines are requested just before SA reaches
+ */
+ C1 += burstHeight;
+ } else {
+ if (c1flag == 0)
+ /*
+ * After C1 saturated to 4, next burstHeight
+ * decides C2: the number of partially filled
+ * lines when SA condition is reached
+ */
+ C2 = burstHeight;
+ c1flag = 1;
+ }
+ linesRem -= burstHeight;
+ bh_config.ba += stride * burstHeight;
+ }
+
+ /*
+ * Total line buffer memory GFXBuffers+Vid/WB bufers allocated in terms
+ * of 16Byte Word locations
+ */
+ Tot_mem = (640 * bh_config.gballoc + 1024 * bh_config.vballoc) /
+ (4 * bh_config.yuv420);
+ /*
+ * Ceil(rounded to higher integer) of Number of 16Byte Word locations
+ * used by single line of frame.
+ */
+ pict_16word_ceil = DIV_ROUND_UP(sizeX_nopred, 16);
+
+ /* Exact Number of 16Byte Word locations used by single line of frame */
+ pict_16word = sizeX_nopred / 16;
+
+ /*
+ * Number of sets of 4 lines that can fully fit into the memory
+ * buffers allocated.
+ */
+ i = Tot_mem / pict_16word_ceil;
+
+ if (i == 0) {
+ /* LineSize > MemoryLineBufferSize (Valid only for 1D) */
+ sa_info->min_sa = Tot_mem - 8;
+ } else if (i == 1) {
+ /*
+ * When MemoryLineBufferSize > LineSize >
+ * (MemoryLineBufferSize/2)
+ */
+ sa_info->min_sa = pict_16word + C2 * (Tot_mem -
+ pict_16word_ceil - 8);
+ } else {
+ /* All other cases */
+ sa_info->min_sa = i * pict_16word + C1 * pict_16word + C2 *
+ (Tot_mem - (pict_16word_ceil * i) - 8);
+ }
+
+ /* C2=0:: no partialy filed lines:: Then minLT = 0 */
+ if (C2 == 0) {
+ sa_info->min_lt = 0;
+ } else if (bh_config.antifckr == 1) {
+ if (C1 == 3)
+ sa_info->min_lt = 3 * pict_16word_ceil + C2 * (Tot_mem -
+ (pict_16word_ceil*i));
+ else if (C1 == 4)
+ sa_info->min_lt = 2 * pict_16word_ceil + C2 * (Tot_mem -
+ (pict_16word_ceil*i));
+ } else {
+ sa_info->min_lt = C2 * (Tot_mem - (pict_16word_ceil*i));
+ }
+
+ sa_info->max_lt = max(sa_info->min_sa - 8, sa_info->min_lt + 1);
+}
+
+/*
+ * sa_calc calculates SA and LT values for one set of DISPC reg inputs
+ * This takes care of calling the actual sa_calc function once/twice
+ * as per nonYUV420/YUV420 format and gives final value of output
+ * dispc_reg_config :: Dispc register information
+ * channel_no :: channel_no
+ * y_nuv :: 1->Luma frame parameters, calculation;
+ * 0->Chroma frame parameters and calculation
+ * sa_info :: Output struct having information of SA and LT values
+ */
+u32 sa_calc_wrap(struct dispc_config *dispc_reg_config, u32 channel_no)
+{
+ struct sa_struct sa_info_y;
+ struct sa_struct sa_info_uv;
+
+ /* SA values calculated for Luma frame */
+ sa_calc(dispc_reg_config, channel_no, 1, &sa_info_y);
+
+ /* Going into this looop only for YUV420 Format and Channel != GFX */
+ if (dispc_reg_config->format == 0 && channel_no > 0) {
+ /* SA values calculated for Chroma Frame */
+ sa_calc(dispc_reg_config, channel_no, 0, &sa_info_uv);
+ return 2 * max(max(sa_info_y.min_sa - 8, sa_info_y.min_lt + 1),
+ max(sa_info_uv.min_sa - 8, sa_info_uv.min_lt + 1));
+ } else {
+ return sa_info_y.max_lt;
+ }
+}
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index fadd6a0..cfb82b5 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -26,1099 +26,268 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/interrupt.h>
+#include <linux/seq_file.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/string.h>
-#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
#include <video/omapdss.h>
-#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
- defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-#endif
+#include <video/hdmi_ti_4xxx_ip.h>
+#include <linux/gpio.h>
+#include <linux/fb.h>
+#include <linux/omapfb.h>
#include "dss.h"
-#include "hdmi.h"
#include "dss_features.h"
-#define HDMI_DEFAULT_REGN 15
-#define HDMI_DEFAULT_REGM2 1
+#define HDMI_WP 0x0
+#define HDMI_CORE_SYS 0x400
+#define HDMI_CORE_AV 0x900
+#define HDMI_PLLCTRL 0x200
+#define HDMI_PHY 0x300
+
+/* HDMI EDID Length move this */
+#define HDMI_EDID_MAX_LENGTH 512
+#define EDID_TIMING_DESCRIPTOR_SIZE 0x12
+#define EDID_DESCRIPTOR_BLOCK0_ADDRESS 0x36
+#define EDID_DESCRIPTOR_BLOCK1_ADDRESS 0x80
+#define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4
+#define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4
+
+#define OMAP_HDMI_TIMINGS_NB 34
static struct {
struct mutex lock;
struct omap_display_platform_data *pdata;
struct platform_device *pdev;
- void __iomem *base_wp; /* HDMI wrapper */
+ struct omap_dss_device *dssdev;
+ struct hdmi_ip_data hdmi_data;
int code;
int mode;
u8 edid[HDMI_EDID_MAX_LENGTH];
u8 edid_set;
+ bool can_do_hdmi;
+
bool custom_set;
+ enum hdmi_deep_color_mode deep_color;
struct hdmi_config cfg;
+ struct regulator *hdmi_reg;
- int hpd_gpio;
- bool phy_tx_enabled;
-} hdmi;
+ int hdmi_irq;
+ struct clk *sys_clk;
+ struct clk *hdmi_clk;
-/*
- * Logic for the below structure :
- * user enters the CEA or VESA timings by specifying the HDMI/DVI code.
- * There is a correspondence between CEA/VESA timing and code, please
- * refer to section 6.3 in HDMI 1.3 specification for timing code.
- *
- * In the below structure, cea_vesa_timings corresponds to all OMAP4
- * supported CEA and VESA timing values.code_cea corresponds to the CEA
- * code, It is used to get the timing from cea_vesa_timing array.Similarly
- * with code_vesa. Code_index is used for back mapping, that is once EDID
- * is read from the TV, EDID is parsed to find the timing values and then
- * map it to corresponding CEA or VESA index.
- */
-
-static const struct hdmi_timings cea_vesa_timings[OMAP_HDMI_TIMINGS_NB] = {
- { {640, 480, 25200, 96, 16, 48, 2, 10, 33} , 0 , 0},
- { {1280, 720, 74250, 40, 440, 220, 5, 5, 20}, 1, 1},
- { {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, 1, 1},
- { {720, 480, 27027, 62, 16, 60, 6, 9, 30}, 0, 0},
- { {2880, 576, 108000, 256, 48, 272, 5, 5, 39}, 0, 0},
- { {1440, 240, 27027, 124, 38, 114, 3, 4, 15}, 0, 0},
- { {1440, 288, 27000, 126, 24, 138, 3, 2, 19}, 0, 0},
- { {1920, 540, 74250, 44, 528, 148, 5, 2, 15}, 1, 1},
- { {1920, 540, 74250, 44, 88, 148, 5, 2, 15}, 1, 1},
- { {1920, 1080, 148500, 44, 88, 148, 5, 4, 36}, 1, 1},
- { {720, 576, 27000, 64, 12, 68, 5, 5, 39}, 0, 0},
- { {1440, 576, 54000, 128, 24, 136, 5, 5, 39}, 0, 0},
- { {1920, 1080, 148500, 44, 528, 148, 5, 4, 36}, 1, 1},
- { {2880, 480, 108108, 248, 64, 240, 6, 9, 30}, 0, 0},
- { {1920, 1080, 74250, 44, 638, 148, 5, 4, 36}, 1, 1},
- /* VESA From Here */
- { {640, 480, 25175, 96, 16, 48, 2 , 11, 31}, 0, 0},
- { {800, 600, 40000, 128, 40, 88, 4 , 1, 23}, 1, 1},
- { {848, 480, 33750, 112, 16, 112, 8 , 6, 23}, 1, 1},
- { {1280, 768, 79500, 128, 64, 192, 7 , 3, 20}, 1, 0},
- { {1280, 800, 83500, 128, 72, 200, 6 , 3, 22}, 1, 0},
- { {1360, 768, 85500, 112, 64, 256, 6 , 3, 18}, 1, 1},
- { {1280, 960, 108000, 112, 96, 312, 3 , 1, 36}, 1, 1},
- { {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38}, 1, 1},
- { {1024, 768, 65000, 136, 24, 160, 6, 3, 29}, 0, 0},
- { {1400, 1050, 121750, 144, 88, 232, 4, 3, 32}, 1, 0},
- { {1440, 900, 106500, 152, 80, 232, 6, 3, 25}, 1, 0},
- { {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30}, 1, 0},
- { {1366, 768, 85500, 143, 70, 213, 3, 3, 24}, 1, 1},
- { {1920, 1080, 148500, 44, 148, 80, 5, 4, 36}, 1, 1},
- { {1280, 768, 68250, 32, 48, 80, 7, 3, 12}, 0, 1},
- { {1400, 1050, 101000, 32, 48, 80, 4, 3, 23}, 0, 1},
- { {1680, 1050, 119000, 32, 48, 80, 6, 3, 21}, 0, 1},
- { {1280, 800, 79500, 32, 48, 80, 6, 3, 14}, 0, 1},
- { {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, 1, 1}
-};
-
-/*
- * This is a static mapping array which maps the timing values
- * with corresponding CEA / VESA code
- */
-static const int code_index[OMAP_HDMI_TIMINGS_NB] = {
- 1, 19, 4, 2, 37, 6, 21, 20, 5, 16, 17, 29, 31, 35, 32,
- /* <--15 CEA 17--> vesa*/
- 4, 9, 0xE, 0x17, 0x1C, 0x27, 0x20, 0x23, 0x10, 0x2A,
- 0X2F, 0x3A, 0X51, 0X52, 0x16, 0x29, 0x39, 0x1B
-};
+ int runtime_count;
+ int enabled;
+ bool set_mode;
+ bool wp_reset_done;
-/*
- * This is reverse static mapping which maps the CEA / VESA code
- * to the corresponding timing values
- */
-static const int code_cea[39] = {
- -1, 0, 3, 3, 2, 8, 5, 5, -1, -1,
- -1, -1, -1, -1, -1, -1, 9, 10, 10, 1,
- 7, 6, 6, -1, -1, -1, -1, -1, -1, 11,
- 11, 12, 14, -1, -1, 13, 13, 4, 4
-};
-
-static const int code_vesa[85] = {
- -1, -1, -1, -1, 15, -1, -1, -1, -1, 16,
- -1, -1, -1, -1, 17, -1, 23, -1, -1, -1,
- -1, -1, 29, 18, -1, -1, -1, 32, 19, -1,
- -1, -1, 21, -1, -1, 22, -1, -1, -1, 20,
- -1, 30, 24, -1, -1, -1, -1, 25, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 31, 26, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 27, 28, -1, 33};
+ void (*hdmi_start_frame_cb)(void);
+ void (*hdmi_irq_cb)(int);
+ bool (*hdmi_power_on_cb)(void);
+} hdmi;
static const u8 edid_header[8] = {0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0};
-static inline void hdmi_write_reg(const struct hdmi_reg idx, u32 val)
-{
- __raw_writel(val, hdmi.base_wp + idx.idx);
-}
-
-static inline u32 hdmi_read_reg(const struct hdmi_reg idx)
-{
- return __raw_readl(hdmi.base_wp + idx.idx);
-}
-
-static inline int hdmi_wait_for_bit_change(const struct hdmi_reg idx,
- int b2, int b1, u32 val)
-{
- u32 t = 0;
- while (val != REG_GET(idx, b2, b1)) {
- udelay(1);
- if (t++ > 10000)
- return !val;
- }
- return val;
-}
-
-int hdmi_init_display(struct omap_dss_device *dssdev)
-{
- DSSDBG("init_display\n");
-
- return 0;
-}
-
-static int hdmi_pll_init(enum hdmi_clk_refsel refsel, int dcofreq,
- struct hdmi_pll_info *fmt, u16 sd)
-{
- u32 r;
-
- /* PLL start always use manual mode */
- REG_FLD_MOD(PLLCTRL_PLL_CONTROL, 0x0, 0, 0);
-
- r = hdmi_read_reg(PLLCTRL_CFG1);
- r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */
- r = FLD_MOD(r, fmt->regn, 8, 1); /* CFG1_PLL_REGN */
-
- hdmi_write_reg(PLLCTRL_CFG1, r);
-
- r = hdmi_read_reg(PLLCTRL_CFG2);
-
- r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */
- r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */
- r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */
-
- if (dcofreq) {
- /* divider programming for frequency beyond 1000Mhz */
- REG_FLD_MOD(PLLCTRL_CFG3, sd, 17, 10);
- r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */
- } else {
- r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */
- }
-
- hdmi_write_reg(PLLCTRL_CFG2, r);
-
- r = hdmi_read_reg(PLLCTRL_CFG4);
- r = FLD_MOD(r, fmt->regm2, 24, 18);
- r = FLD_MOD(r, fmt->regmf, 17, 0);
-
- hdmi_write_reg(PLLCTRL_CFG4, r);
-
- /* go now */
- REG_FLD_MOD(PLLCTRL_PLL_GO, 0x1, 0, 0);
-
- /* wait for bit change */
- if (hdmi_wait_for_bit_change(PLLCTRL_PLL_GO, 0, 0, 1) != 1) {
- DSSERR("PLL GO bit not set\n");
- return -ETIMEDOUT;
- }
-
- /* Wait till the lock bit is set in PLL status */
- if (hdmi_wait_for_bit_change(PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) {
- DSSWARN("cannot lock PLL\n");
- DSSWARN("CFG1 0x%x\n",
- hdmi_read_reg(PLLCTRL_CFG1));
- DSSWARN("CFG2 0x%x\n",
- hdmi_read_reg(PLLCTRL_CFG2));
- DSSWARN("CFG4 0x%x\n",
- hdmi_read_reg(PLLCTRL_CFG4));
- return -ETIMEDOUT;
- }
-
- DSSDBG("PLL locked!\n");
-
- return 0;
-}
-
-/* PHY_PWR_CMD */
-static int hdmi_set_phy_pwr(enum hdmi_phy_pwr val)
+static int hdmi_runtime_get(void)
{
- /* Command for power control of HDMI PHY */
- REG_FLD_MOD(HDMI_WP_PWR_CTRL, val, 7, 6);
+ int r;
- /* Status of the power control of HDMI PHY */
- if (hdmi_wait_for_bit_change(HDMI_WP_PWR_CTRL, 5, 4, val) != val) {
- DSSERR("Failed to set PHY power mode to %d\n", val);
- return -ETIMEDOUT;
- }
+ DSSDBG("hdmi_runtime_get\n");
- return 0;
-}
+ if (hdmi.runtime_count++ == 0) {
+ r = dss_runtime_get();
+ if (r)
+ goto err_get_dss;
-/* PLL_PWR_CMD */
-static int hdmi_set_pll_pwr(enum hdmi_pll_pwr val)
-{
- /* Command for power control of HDMI PLL */
- REG_FLD_MOD(HDMI_WP_PWR_CTRL, val, 3, 2);
+ r = dispc_runtime_get();
+ if (r)
+ goto err_get_dispc;
- /* wait till PHY_PWR_STATUS is set */
- if (hdmi_wait_for_bit_change(HDMI_WP_PWR_CTRL, 1, 0, val) != val) {
- DSSERR("Failed to set PHY_PWR_STATUS\n");
- return -ETIMEDOUT;
- }
+ clk_enable(hdmi.sys_clk);
+ clk_enable(hdmi.hdmi_clk);
- return 0;
-}
-
-static int hdmi_pll_reset(void)
-{
- /* SYSRESET controlled by power FSM */
- REG_FLD_MOD(PLLCTRL_PLL_CONTROL, 0x0, 3, 3);
-
- /* READ 0x0 reset is in progress */
- if (hdmi_wait_for_bit_change(PLLCTRL_PLL_STATUS, 0, 0, 1) != 1) {
- DSSERR("Failed to sysreset PLL\n");
- return -ETIMEDOUT;
+ r = pm_runtime_get_sync(&hdmi.pdev->dev);
+ WARN_ON(r);
+ if (r < 0)
+ goto err_runtime_get;
}
return 0;
-}
-
-static int hdmi_check_hpd_state(void)
-{
- unsigned long flags;
- bool hpd;
- int r;
- /* this should be in ti_hdmi_4xxx_ip private data */
- static DEFINE_SPINLOCK(phy_tx_lock);
- spin_lock_irqsave(&phy_tx_lock, flags);
-
- hpd = gpio_get_value(hdmi.hpd_gpio);
-
- if (hpd == hdmi.phy_tx_enabled) {
- spin_unlock_irqrestore(&phy_tx_lock, flags);
- return 0;
- }
-
- if (hpd)
- r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_TXON);
- else
- r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_LDOON);
-
- if (r) {
- DSSERR("Failed to %s PHY TX power\n",
- hpd ? "enable" : "disable");
- goto err;
- }
-
- hdmi.phy_tx_enabled = hpd;
-err:
- spin_unlock_irqrestore(&phy_tx_lock, flags);
+err_runtime_get:
+ clk_disable(hdmi.sys_clk);
+ clk_disable(hdmi.hdmi_clk);
+ dispc_runtime_put();
+err_get_dispc:
+ dss_runtime_put();
+err_get_dss:
return r;
}
-static irqreturn_t hpd_irq_handler(int irq, void *data)
+static void hdmi_runtime_put(void)
{
- hdmi_check_hpd_state();
-
- return IRQ_HANDLED;
-}
-
-static int hdmi_phy_init(void)
-{
- u16 r = 0;
-
- r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_LDOON);
- if (r)
- return r;
-
- /*
- * Read address 0 in order to get the SCP reset done completed
- * Dummy access performed to make sure reset is done
- */
- hdmi_read_reg(HDMI_TXPHY_TX_CTRL);
-
- /*
- * Write to phy address 0 to configure the clock
- * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field
- */
- REG_FLD_MOD(HDMI_TXPHY_TX_CTRL, 0x1, 31, 30);
-
- /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */
- hdmi_write_reg(HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000);
-
- /* Setup max LDO voltage */
- REG_FLD_MOD(HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0);
-
- /* Write to phy address 3 to change the polarity control */
- REG_FLD_MOD(HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27);
-
- r = request_threaded_irq(gpio_to_irq(hdmi.hpd_gpio),
- NULL, hpd_irq_handler,
- IRQF_DISABLED | IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING, "hpd", NULL);
- if (r) {
- DSSERR("HPD IRQ request failed\n");
- hdmi_set_phy_pwr(HDMI_PHYPWRCMD_OFF);
- return r;
- }
+ int r;
- r = hdmi_check_hpd_state();
- if (r) {
- free_irq(gpio_to_irq(hdmi.hpd_gpio), NULL);
- hdmi_set_phy_pwr(HDMI_PHYPWRCMD_OFF);
- return r;
- }
+ DSSDBG("hdmi_runtime_put\n");
- return 0;
-}
+ if (--hdmi.runtime_count == 0) {
+ r = pm_runtime_put_sync(&hdmi.pdev->dev);
+ WARN_ON(r);
-static int hdmi_wait_softreset(void)
-{
- /* reset W1 */
- REG_FLD_MOD(HDMI_WP_SYSCONFIG, 0x1, 0, 0);
+ clk_disable(hdmi.sys_clk);
+ clk_disable(hdmi.hdmi_clk);
- /* wait till SOFTRESET == 0 */
- if (hdmi_wait_for_bit_change(HDMI_WP_SYSCONFIG, 0, 0, 0) != 0) {
- DSSERR("sysconfig reset failed\n");
- return -ETIMEDOUT;
+ dispc_runtime_put();
+ dss_runtime_put();
}
-
- return 0;
}
-static int hdmi_pll_program(struct hdmi_pll_info *fmt)
+int hdmi_init_display(struct omap_dss_device *dssdev)
{
- u16 r = 0;
- enum hdmi_clk_refsel refsel;
-
- /* wait for wrapper reset */
- r = hdmi_wait_softreset();
- if (r)
- return r;
-
- r = hdmi_set_pll_pwr(HDMI_PLLPWRCMD_ALLOFF);
- if (r)
- return r;
-
- r = hdmi_set_pll_pwr(HDMI_PLLPWRCMD_BOTHON_ALLCLKS);
- if (r)
- return r;
-
- r = hdmi_pll_reset();
- if (r)
- return r;
-
- refsel = HDMI_REFSEL_SYSCLK;
-
- r = hdmi_pll_init(refsel, fmt->dcofreq, fmt, fmt->regsd);
- if (r)
- return r;
+ DSSDBG("init_display\n");
return 0;
}
-static void hdmi_phy_off(void)
+static int relaxed_fb_mode_is_equal(const struct fb_videomode *mode1,
+ const struct fb_videomode *mode2)
{
- free_irq(gpio_to_irq(hdmi.hpd_gpio), NULL);
- hdmi_set_phy_pwr(HDMI_PHYPWRCMD_OFF);
- hdmi.phy_tx_enabled = false;
-}
-
-static int hdmi_core_ddc_edid(u8 *pedid, int ext)
-{
- u32 i, j;
- char checksum = 0;
- u32 offset = 0;
-
- /* Turn on CLK for DDC */
- REG_FLD_MOD(HDMI_CORE_AV_DPD, 0x7, 2, 0);
-
- /*
- * SW HACK : Without the Delay DDC(i2c bus) reads 0 values /
- * right shifted values( The behavior is not consistent and seen only
- * with some TV's)
- */
- usleep_range(800, 1000);
-
- if (!ext) {
- /* Clk SCL Devices */
- REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0xA, 3, 0);
-
- /* HDMI_CORE_DDC_STATUS_IN_PROG */
- if (hdmi_wait_for_bit_change(HDMI_CORE_DDC_STATUS,
- 4, 4, 0) != 0) {
- DSSERR("Failed to program DDC\n");
- return -ETIMEDOUT;
- }
-
- /* Clear FIFO */
- REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0x9, 3, 0);
-
- /* HDMI_CORE_DDC_STATUS_IN_PROG */
- if (hdmi_wait_for_bit_change(HDMI_CORE_DDC_STATUS,
- 4, 4, 0) != 0) {
- DSSERR("Failed to program DDC\n");
- return -ETIMEDOUT;
- }
-
- } else {
- if (ext % 2 != 0)
- offset = 0x80;
- }
-
- /* Load Segment Address Register */
- REG_FLD_MOD(HDMI_CORE_DDC_SEGM, ext/2, 7, 0);
-
- /* Load Slave Address Register */
- REG_FLD_MOD(HDMI_CORE_DDC_ADDR, 0xA0 >> 1, 7, 1);
-
- /* Load Offset Address Register */
- REG_FLD_MOD(HDMI_CORE_DDC_OFFSET, offset, 7, 0);
-
- /* Load Byte Count */
- REG_FLD_MOD(HDMI_CORE_DDC_COUNT1, 0x80, 7, 0);
- REG_FLD_MOD(HDMI_CORE_DDC_COUNT2, 0x0, 1, 0);
-
- /* Set DDC_CMD */
- if (ext)
- REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0x4, 3, 0);
- else
- REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0x2, 3, 0);
-
- /* HDMI_CORE_DDC_STATUS_BUS_LOW */
- if (REG_GET(HDMI_CORE_DDC_STATUS, 6, 6) == 1) {
- DSSWARN("I2C Bus Low?\n");
- return -EIO;
- }
- /* HDMI_CORE_DDC_STATUS_NO_ACK */
- if (REG_GET(HDMI_CORE_DDC_STATUS, 5, 5) == 1) {
- DSSWARN("I2C No Ack\n");
- return -EIO;
- }
-
- i = ext * 128;
- j = 0;
- while (((REG_GET(HDMI_CORE_DDC_STATUS, 4, 4) == 1) ||
- (REG_GET(HDMI_CORE_DDC_STATUS, 2, 2) == 0)) &&
- j < 128) {
-
- if (REG_GET(HDMI_CORE_DDC_STATUS, 2, 2) == 0) {
- /* FIFO not empty */
- pedid[i++] = REG_GET(HDMI_CORE_DDC_DATA, 7, 0);
- j++;
- }
- }
+ u32 ratio1 = mode1->flag & (FB_FLAG_RATIO_4_3 | FB_FLAG_RATIO_16_9);
+ u32 ratio2 = mode2->flag & (FB_FLAG_RATIO_4_3 | FB_FLAG_RATIO_16_9);
- for (j = 0; j < 128; j++)
- checksum += pedid[j];
-
- if (checksum != 0) {
- DSSERR("E-EDID checksum failed!!\n");
- return -EIO;
- }
-
- return 0;
+ return (mode1->xres == mode2->xres &&
+ mode1->yres == mode2->yres &&
+ mode1->pixclock <= mode2->pixclock * 201 / 200 &&
+ mode1->pixclock >= mode2->pixclock * 200 / 201 &&
+ mode1->hsync_len + mode1->left_margin + mode1->right_margin ==
+ mode2->hsync_len + mode2->left_margin + mode2->right_margin &&
+ mode1->vsync_len + mode1->upper_margin + mode1->lower_margin ==
+ mode2->vsync_len + mode2->upper_margin + mode2->lower_margin &&
+ (!ratio1 || !ratio2 || ratio1 == ratio2) &&
+ (mode1->vmode & FB_VMODE_INTERLACED) ==
+ (mode2->vmode & FB_VMODE_INTERLACED));
}
-static int read_edid(u8 *pedid, u16 max_length)
+static int hdmi_set_timings(struct fb_videomode *vm, bool check_only)
{
- int r = 0, n = 0, i = 0;
- int max_ext_blocks = (max_length / 128) - 1;
+ int i = 0;
+ DSSDBG("hdmi_get_code\n");
- r = hdmi_core_ddc_edid(pedid, 0);
- if (r) {
- return r;
- } else {
- n = pedid[0x7e];
-
- /*
- * README: need to comply with max_length set by the caller.
- * Better implementation should be to allocate necessary
- * memory to store EDID according to nb_block field found
- * in first block
- */
- if (n > max_ext_blocks)
- n = max_ext_blocks;
-
- for (i = 1; i <= n; i++) {
- r = hdmi_core_ddc_edid(pedid, i);
- if (r)
- return r;
+ if (!vm->xres || !vm->yres || !vm->pixclock)
+ goto fail;
+
+ for (i = 0; i < CEA_MODEDB_SIZE; i++) {
+ if (relaxed_fb_mode_is_equal(cea_modes + i, vm)) {
+ *vm = cea_modes[i];
+ if (check_only)
+ return 1;
+ hdmi.cfg.cm.code = i;
+ hdmi.cfg.cm.mode = HDMI_HDMI;
+ hdmi.cfg.timings = cea_modes[hdmi.cfg.cm.code];
+ goto done;
}
}
- return 0;
-}
-
-static int get_timings_index(void)
-{
- int code;
-
- if (hdmi.mode == 0)
- code = code_vesa[hdmi.code];
- else
- code = code_cea[hdmi.code];
-
- if (code == -1) {
- /* HDMI code 4 corresponds to 640 * 480 VGA */
- hdmi.code = 4;
- /* DVI mode 1 corresponds to HDMI 0 to DVI */
- hdmi.mode = HDMI_DVI;
- code = code_vesa[hdmi.code];
- }
- return code;
-}
-
-static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing)
-{
- int i = 0, code = -1, temp_vsync = 0, temp_hsync = 0;
- int timing_vsync = 0, timing_hsync = 0;
- struct omap_video_timings temp;
- struct hdmi_cm cm = {-1};
- DSSDBG("hdmi_get_code\n");
-
- for (i = 0; i < OMAP_HDMI_TIMINGS_NB; i++) {
- temp = cea_vesa_timings[i].timings;
- if ((temp.pixel_clock == timing->pixel_clock) &&
- (temp.x_res == timing->x_res) &&
- (temp.y_res == timing->y_res)) {
-
- temp_hsync = temp.hfp + temp.hsw + temp.hbp;
- timing_hsync = timing->hfp + timing->hsw + timing->hbp;
- temp_vsync = temp.vfp + temp.vsw + temp.vbp;
- timing_vsync = timing->vfp + timing->vsw + timing->vbp;
-
- DSSDBG("temp_hsync = %d , temp_vsync = %d"
- "timing_hsync = %d, timing_vsync = %d\n",
- temp_hsync, temp_hsync,
- timing_hsync, timing_vsync);
-
- if ((temp_hsync == timing_hsync) &&
- (temp_vsync == timing_vsync)) {
- code = i;
- cm.code = code_index[i];
- if (code < 14)
- cm.mode = HDMI_HDMI;
- else
- cm.mode = HDMI_DVI;
- DSSDBG("Hdmi_code = %d mode = %d\n",
- cm.code, cm.mode);
- break;
- }
+ for (i = 0; i < VESA_MODEDB_SIZE; i++) {
+ if (relaxed_fb_mode_is_equal(vesa_modes + i, vm)) {
+ *vm = vesa_modes[i];
+ if (check_only)
+ return 1;
+ hdmi.cfg.cm.code = i;
+ hdmi.cfg.cm.mode = HDMI_DVI;
+ hdmi.cfg.timings = vesa_modes[hdmi.cfg.cm.code];
+ goto done;
}
}
- return cm;
-}
+fail:
+ if (check_only)
+ return 0;
+ hdmi.cfg.cm.code = 1;
+ hdmi.cfg.cm.mode = HDMI_HDMI;
+ hdmi.cfg.timings = cea_modes[hdmi.cfg.cm.code];
-static void get_horz_vert_timing_info(int current_descriptor_addrs, u8 *edid ,
- struct omap_video_timings *timings)
-{
- /* X and Y resolution */
- timings->x_res = (((edid[current_descriptor_addrs + 4] & 0xF0) << 4) |
- edid[current_descriptor_addrs + 2]);
- timings->y_res = (((edid[current_descriptor_addrs + 7] & 0xF0) << 4) |
- edid[current_descriptor_addrs + 5]);
-
- timings->pixel_clock = ((edid[current_descriptor_addrs + 1] << 8) |
- edid[current_descriptor_addrs]);
-
- timings->pixel_clock = 10 * timings->pixel_clock;
-
- /* HORIZONTAL FRONT PORCH */
- timings->hfp = edid[current_descriptor_addrs + 8] |
- ((edid[current_descriptor_addrs + 11] & 0xc0) << 2);
- /* HORIZONTAL SYNC WIDTH */
- timings->hsw = edid[current_descriptor_addrs + 9] |
- ((edid[current_descriptor_addrs + 11] & 0x30) << 4);
- /* HORIZONTAL BACK PORCH */
- timings->hbp = (((edid[current_descriptor_addrs + 4] & 0x0F) << 8) |
- edid[current_descriptor_addrs + 3]) -
- (timings->hfp + timings->hsw);
- /* VERTICAL FRONT PORCH */
- timings->vfp = ((edid[current_descriptor_addrs + 10] & 0xF0) >> 4) |
- ((edid[current_descriptor_addrs + 11] & 0x0f) << 2);
- /* VERTICAL SYNC WIDTH */
- timings->vsw = (edid[current_descriptor_addrs + 10] & 0x0F) |
- ((edid[current_descriptor_addrs + 11] & 0x03) << 4);
- /* VERTICAL BACK PORCH */
- timings->vbp = (((edid[current_descriptor_addrs + 7] & 0x0F) << 8) |
- edid[current_descriptor_addrs + 6]) -
- (timings->vfp + timings->vsw);
+ i = -1;
+done:
+ DSSDBG("%s-%d\n", hdmi.cfg.cm.mode ? "CEA" : "VESA", hdmi.cfg.cm.code);
+ return i >= 0;
}
-/* Description : This function gets the resolution information from EDID */
-static void get_edid_timing_data(u8 *edid)
+void hdmi_get_monspecs(struct fb_monspecs *specs)
{
- u8 count;
- u16 current_descriptor_addrs;
- struct hdmi_cm cm;
- struct omap_video_timings edid_timings;
-
- /* search block 0, there are 4 DTDs arranged in priority order */
- for (count = 0; count < EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR; count++) {
- current_descriptor_addrs =
- EDID_DESCRIPTOR_BLOCK0_ADDRESS +
- count * EDID_TIMING_DESCRIPTOR_SIZE;
- get_horz_vert_timing_info(current_descriptor_addrs,
- edid, &edid_timings);
- cm = hdmi_get_code(&edid_timings);
- DSSDBG("Block0[%d] value matches code = %d , mode = %d\n",
- count, cm.code, cm.mode);
- if (cm.code == -1) {
- continue;
- } else {
- hdmi.code = cm.code;
- hdmi.mode = cm.mode;
- DSSDBG("code = %d , mode = %d\n",
- hdmi.code, hdmi.mode);
- return;
- }
- }
- if (edid[0x7e] != 0x00) {
- for (count = 0; count < EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR;
- count++) {
- current_descriptor_addrs =
- EDID_DESCRIPTOR_BLOCK1_ADDRESS +
- count * EDID_TIMING_DESCRIPTOR_SIZE;
- get_horz_vert_timing_info(current_descriptor_addrs,
- edid, &edid_timings);
- cm = hdmi_get_code(&edid_timings);
- DSSDBG("Block1[%d] value matches code = %d, mode = %d",
- count, cm.code, cm.mode);
- if (cm.code == -1) {
- continue;
- } else {
- hdmi.code = cm.code;
- hdmi.mode = cm.mode;
- DSSDBG("code = %d , mode = %d\n",
- hdmi.code, hdmi.mode);
- return;
- }
- }
- }
-
- DSSINFO("no valid timing found , falling back to VGA\n");
- hdmi.code = 4; /* setting default value of 640 480 VGA */
- hdmi.mode = HDMI_DVI;
-}
-
-static void hdmi_read_edid(struct omap_video_timings *dp)
-{
- int ret = 0, code;
-
- memset(hdmi.edid, 0, HDMI_EDID_MAX_LENGTH);
+ int i, j;
+ char *edid = (char *) hdmi.edid;
+ memset(specs, 0x0, sizeof(*specs));
if (!hdmi.edid_set)
- ret = read_edid(hdmi.edid, HDMI_EDID_MAX_LENGTH);
+ return;
- if (!ret) {
- if (!memcmp(hdmi.edid, edid_header, sizeof(edid_header))) {
- /* search for timings of default resolution */
- get_edid_timing_data(hdmi.edid);
- hdmi.edid_set = true;
- }
- } else {
- DSSWARN("failed to read E-EDID\n");
- }
+ fb_edid_to_monspecs(edid, specs);
+ if (specs->modedb == NULL)
+ return;
- if (!hdmi.edid_set) {
- DSSINFO("fallback to VGA\n");
- hdmi.code = 4; /* setting default value of 640 480 VGA */
- hdmi.mode = HDMI_DVI;
+ for (i = 1; i <= edid[0x7e] && i * 128 < HDMI_EDID_MAX_LENGTH; i++) {
+ if (edid[i * 128] == 0x2)
+ fb_edid_add_monspecs(edid + i * 128, specs);
}
- code = get_timings_index();
-
- *dp = cea_vesa_timings[code].timings;
-}
-
-static void hdmi_core_init(struct hdmi_core_video_config *video_cfg,
- struct hdmi_core_infoframe_avi *avi_cfg,
- struct hdmi_core_packet_enable_repeat *repeat_cfg)
-{
- DSSDBG("Enter hdmi_core_init\n");
-
- /* video core */
- video_cfg->ip_bus_width = HDMI_INPUT_8BIT;
- video_cfg->op_dither_truc = HDMI_OUTPUTTRUNCATION_8BIT;
- video_cfg->deep_color_pkt = HDMI_DEEPCOLORPACKECTDISABLE;
- video_cfg->pkt_mode = HDMI_PACKETMODERESERVEDVALUE;
- video_cfg->hdmi_dvi = HDMI_DVI;
- video_cfg->tclk_sel_clkmult = HDMI_FPLL10IDCK;
-
- /* info frame */
- avi_cfg->db1_format = 0;
- avi_cfg->db1_active_info = 0;
- avi_cfg->db1_bar_info_dv = 0;
- avi_cfg->db1_scan_info = 0;
- avi_cfg->db2_colorimetry = 0;
- avi_cfg->db2_aspect_ratio = 0;
- avi_cfg->db2_active_fmt_ar = 0;
- avi_cfg->db3_itc = 0;
- avi_cfg->db3_ec = 0;
- avi_cfg->db3_q_range = 0;
- avi_cfg->db3_nup_scaling = 0;
- avi_cfg->db4_videocode = 0;
- avi_cfg->db5_pixel_repeat = 0;
- avi_cfg->db6_7_line_eoftop = 0 ;
- avi_cfg->db8_9_line_sofbottom = 0;
- avi_cfg->db10_11_pixel_eofleft = 0;
- avi_cfg->db12_13_pixel_sofright = 0;
-
- /* packet enable and repeat */
- repeat_cfg->audio_pkt = 0;
- repeat_cfg->audio_pkt_repeat = 0;
- repeat_cfg->avi_infoframe = 0;
- repeat_cfg->avi_infoframe_repeat = 0;
- repeat_cfg->gen_cntrl_pkt = 0;
- repeat_cfg->gen_cntrl_pkt_repeat = 0;
- repeat_cfg->generic_pkt = 0;
- repeat_cfg->generic_pkt_repeat = 0;
-}
+ hdmi.can_do_hdmi = specs->misc & FB_MISC_HDMI;
-static void hdmi_core_powerdown_disable(void)
-{
- DSSDBG("Enter hdmi_core_powerdown_disable\n");
- REG_FLD_MOD(HDMI_CORE_CTRL1, 0x0, 0, 0);
-}
+ /* filter out resolutions we don't support */
+ for (i = j = 0; i < specs->modedb_len; i++) {
+ u32 max_pclk = hdmi.dssdev->clocks.hdmi.max_pixclk_khz;
+ if (!hdmi_set_timings(&specs->modedb[i], true))
+ continue;
-static void hdmi_core_swreset_release(void)
-{
- DSSDBG("Enter hdmi_core_swreset_release\n");
- REG_FLD_MOD(HDMI_CORE_SYS_SRST, 0x0, 0, 0);
-}
+ if (max_pclk &&
+ max_pclk < PICOS2KHZ(specs->modedb[i].pixclock))
+ continue;
-static void hdmi_core_swreset_assert(void)
-{
- DSSDBG("Enter hdmi_core_swreset_assert\n");
- REG_FLD_MOD(HDMI_CORE_SYS_SRST, 0x1, 0, 0);
-}
+ if (specs->modedb[i].flag & FB_FLAG_PIXEL_REPEAT)
+ continue;
-/* DSS_HDMI_CORE_VIDEO_CONFIG */
-static void hdmi_core_video_config(struct hdmi_core_video_config *cfg)
-{
- u32 r = 0;
-
- /* sys_ctrl1 default configuration not tunable */
- r = hdmi_read_reg(HDMI_CORE_CTRL1);
- r = FLD_MOD(r, HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC, 5, 5);
- r = FLD_MOD(r, HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC, 4, 4);
- r = FLD_MOD(r, HDMI_CORE_CTRL1_BSEL_24BITBUS, 2, 2);
- r = FLD_MOD(r, HDMI_CORE_CTRL1_EDGE_RISINGEDGE, 1, 1);
- hdmi_write_reg(HDMI_CORE_CTRL1, r);
-
- REG_FLD_MOD(HDMI_CORE_SYS_VID_ACEN, cfg->ip_bus_width, 7, 6);
-
- /* Vid_Mode */
- r = hdmi_read_reg(HDMI_CORE_SYS_VID_MODE);
-
- /* dither truncation configuration */
- if (cfg->op_dither_truc > HDMI_OUTPUTTRUNCATION_12BIT) {
- r = FLD_MOD(r, cfg->op_dither_truc - 3, 7, 6);
- r = FLD_MOD(r, 1, 5, 5);
- } else {
- r = FLD_MOD(r, cfg->op_dither_truc, 7, 6);
- r = FLD_MOD(r, 0, 5, 5);
+ specs->modedb[j++] = specs->modedb[i];
}
- hdmi_write_reg(HDMI_CORE_SYS_VID_MODE, r);
-
- /* HDMI_Ctrl */
- r = hdmi_read_reg(HDMI_CORE_AV_HDMI_CTRL);
- r = FLD_MOD(r, cfg->deep_color_pkt, 6, 6);
- r = FLD_MOD(r, cfg->pkt_mode, 5, 3);
- r = FLD_MOD(r, cfg->hdmi_dvi, 0, 0);
- hdmi_write_reg(HDMI_CORE_AV_HDMI_CTRL, r);
-
- /* TMDS_CTRL */
- REG_FLD_MOD(HDMI_CORE_SYS_TMDS_CTRL,
- cfg->tclk_sel_clkmult, 6, 5);
-}
-
-static void hdmi_core_aux_infoframe_avi_config(
- struct hdmi_core_infoframe_avi info_avi)
-{
- u32 val;
- char sum = 0, checksum = 0;
-
- sum += 0x82 + 0x002 + 0x00D;
- hdmi_write_reg(HDMI_CORE_AV_AVI_TYPE, 0x082);
- hdmi_write_reg(HDMI_CORE_AV_AVI_VERS, 0x002);
- hdmi_write_reg(HDMI_CORE_AV_AVI_LEN, 0x00D);
-
- val = (info_avi.db1_format << 5) |
- (info_avi.db1_active_info << 4) |
- (info_avi.db1_bar_info_dv << 2) |
- (info_avi.db1_scan_info);
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(0), val);
- sum += val;
-
- val = (info_avi.db2_colorimetry << 6) |
- (info_avi.db2_aspect_ratio << 4) |
- (info_avi.db2_active_fmt_ar);
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(1), val);
- sum += val;
-
- val = (info_avi.db3_itc << 7) |
- (info_avi.db3_ec << 4) |
- (info_avi.db3_q_range << 2) |
- (info_avi.db3_nup_scaling);
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(2), val);
- sum += val;
-
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(3), info_avi.db4_videocode);
- sum += info_avi.db4_videocode;
-
- val = info_avi.db5_pixel_repeat;
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(4), val);
- sum += val;
-
- val = info_avi.db6_7_line_eoftop & 0x00FF;
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(5), val);
- sum += val;
-
- val = ((info_avi.db6_7_line_eoftop >> 8) & 0x00FF);
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(6), val);
- sum += val;
-
- val = info_avi.db8_9_line_sofbottom & 0x00FF;
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(7), val);
- sum += val;
-
- val = ((info_avi.db8_9_line_sofbottom >> 8) & 0x00FF);
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(8), val);
- sum += val;
-
- val = info_avi.db10_11_pixel_eofleft & 0x00FF;
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(9), val);
- sum += val;
-
- val = ((info_avi.db10_11_pixel_eofleft >> 8) & 0x00FF);
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(10), val);
- sum += val;
-
- val = info_avi.db12_13_pixel_sofright & 0x00FF;
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(11), val);
- sum += val;
-
- val = ((info_avi.db12_13_pixel_sofright >> 8) & 0x00FF);
- hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(12), val);
- sum += val;
-
- checksum = 0x100 - sum;
- hdmi_write_reg(HDMI_CORE_AV_AVI_CHSUM, checksum);
-}
-
-static void hdmi_core_av_packet_config(
- struct hdmi_core_packet_enable_repeat repeat_cfg)
-{
- /* enable/repeat the infoframe */
- hdmi_write_reg(HDMI_CORE_AV_PB_CTRL1,
- (repeat_cfg.audio_pkt << 5) |
- (repeat_cfg.audio_pkt_repeat << 4) |
- (repeat_cfg.avi_infoframe << 1) |
- (repeat_cfg.avi_infoframe_repeat));
-
- /* enable/repeat the packet */
- hdmi_write_reg(HDMI_CORE_AV_PB_CTRL2,
- (repeat_cfg.gen_cntrl_pkt << 3) |
- (repeat_cfg.gen_cntrl_pkt_repeat << 2) |
- (repeat_cfg.generic_pkt << 1) |
- (repeat_cfg.generic_pkt_repeat));
-}
-
-static void hdmi_wp_init(struct omap_video_timings *timings,
- struct hdmi_video_format *video_fmt,
- struct hdmi_video_interface *video_int)
-{
- DSSDBG("Enter hdmi_wp_init\n");
-
- timings->hbp = 0;
- timings->hfp = 0;
- timings->hsw = 0;
- timings->vbp = 0;
- timings->vfp = 0;
- timings->vsw = 0;
-
- video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444;
- video_fmt->y_res = 0;
- video_fmt->x_res = 0;
-
- video_int->vsp = 0;
- video_int->hsp = 0;
-
- video_int->interlacing = 0;
- video_int->tm = 0; /* HDMI_TIMING_SLAVE */
-
+ specs->modedb_len = j;
}
-static void hdmi_wp_video_start(bool start)
+u8 *hdmi_read_edid(struct omap_video_timings *dp)
{
- REG_FLD_MOD(HDMI_WP_VIDEO_CFG, start, 31, 31);
-}
+ int ret = 0, i;
-static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt,
- struct omap_video_timings *timings, struct hdmi_config *param)
-{
- DSSDBG("Enter hdmi_wp_video_init_format\n");
-
- video_fmt->y_res = param->timings.timings.y_res;
- video_fmt->x_res = param->timings.timings.x_res;
+ if (hdmi.edid_set)
+ return hdmi.edid;
- timings->hbp = param->timings.timings.hbp;
- timings->hfp = param->timings.timings.hfp;
- timings->hsw = param->timings.timings.hsw;
- timings->vbp = param->timings.timings.vbp;
- timings->vfp = param->timings.timings.vfp;
- timings->vsw = param->timings.timings.vsw;
-}
-
-static void hdmi_wp_video_config_format(
- struct hdmi_video_format *video_fmt)
-{
- u32 l = 0;
-
- REG_FLD_MOD(HDMI_WP_VIDEO_CFG, video_fmt->packing_mode, 10, 8);
-
- l |= FLD_VAL(video_fmt->y_res, 31, 16);
- l |= FLD_VAL(video_fmt->x_res, 15, 0);
- hdmi_write_reg(HDMI_WP_VIDEO_SIZE, l);
-}
-
-static void hdmi_wp_video_config_interface(
- struct hdmi_video_interface *video_int)
-{
- u32 r;
- DSSDBG("Enter hdmi_wp_video_config_interface\n");
-
- r = hdmi_read_reg(HDMI_WP_VIDEO_CFG);
- r = FLD_MOD(r, video_int->vsp, 7, 7);
- r = FLD_MOD(r, video_int->hsp, 6, 6);
- r = FLD_MOD(r, video_int->interlacing, 3, 3);
- r = FLD_MOD(r, video_int->tm, 1, 0);
- hdmi_write_reg(HDMI_WP_VIDEO_CFG, r);
-}
-
-static void hdmi_wp_video_config_timing(
- struct omap_video_timings *timings)
-{
- u32 timing_h = 0;
- u32 timing_v = 0;
-
- DSSDBG("Enter hdmi_wp_video_config_timing\n");
-
- timing_h |= FLD_VAL(timings->hbp, 31, 20);
- timing_h |= FLD_VAL(timings->hfp, 19, 8);
- timing_h |= FLD_VAL(timings->hsw, 7, 0);
- hdmi_write_reg(HDMI_WP_VIDEO_TIMING_H, timing_h);
-
- timing_v |= FLD_VAL(timings->vbp, 31, 20);
- timing_v |= FLD_VAL(timings->vfp, 19, 8);
- timing_v |= FLD_VAL(timings->vsw, 7, 0);
- hdmi_write_reg(HDMI_WP_VIDEO_TIMING_V, timing_v);
-}
-
-static void hdmi_basic_configure(struct hdmi_config *cfg)
-{
- /* HDMI */
- struct omap_video_timings video_timing;
- struct hdmi_video_format video_format;
- struct hdmi_video_interface video_interface;
- /* HDMI core */
- struct hdmi_core_infoframe_avi avi_cfg;
- struct hdmi_core_video_config v_core_cfg;
- struct hdmi_core_packet_enable_repeat repeat_cfg;
-
- hdmi_wp_init(&video_timing, &video_format,
- &video_interface);
-
- hdmi_core_init(&v_core_cfg,
- &avi_cfg,
- &repeat_cfg);
-
- hdmi_wp_video_init_format(&video_format,
- &video_timing, cfg);
-
- hdmi_wp_video_config_timing(&video_timing);
-
- /* video config */
- video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422;
-
- hdmi_wp_video_config_format(&video_format);
-
- video_interface.vsp = cfg->timings.vsync_pol;
- video_interface.hsp = cfg->timings.hsync_pol;
- video_interface.interlacing = cfg->interlace;
- video_interface.tm = 1 ; /* HDMI_TIMING_MASTER_24BIT */
-
- hdmi_wp_video_config_interface(&video_interface);
-
- /*
- * configure core video part
- * set software reset in the core
- */
- hdmi_core_swreset_assert();
-
- /* power down off */
- hdmi_core_powerdown_disable();
+ memset(hdmi.edid, 0, HDMI_EDID_MAX_LENGTH);
- v_core_cfg.pkt_mode = HDMI_PACKETMODE24BITPERPIXEL;
- v_core_cfg.hdmi_dvi = cfg->cm.mode;
+ ret = read_ti_4xxx_edid(&hdmi.hdmi_data, hdmi.edid,
+ HDMI_EDID_MAX_LENGTH);
- hdmi_core_video_config(&v_core_cfg);
+ for (i = 0; i < HDMI_EDID_MAX_LENGTH; i += 16)
+ pr_info("edid[%03x] = %02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n", i,
+ hdmi.edid[i], hdmi.edid[i + 1], hdmi.edid[i + 2],
+ hdmi.edid[i + 3], hdmi.edid[i + 4], hdmi.edid[i + 5],
+ hdmi.edid[i + 6], hdmi.edid[i + 7], hdmi.edid[i + 8],
+ hdmi.edid[i + 9], hdmi.edid[i + 10], hdmi.edid[i + 11],
+ hdmi.edid[i + 12], hdmi.edid[i + 13], hdmi.edid[i + 14],
+ hdmi.edid[i + 15]);
- /* release software reset in the core */
- hdmi_core_swreset_release();
+ if (ret) {
+ DSSWARN("failed to read E-EDID\n");
+ return NULL;
+ }
- /*
- * configure packet
- * info frame video see doc CEA861-D page 65
- */
- avi_cfg.db1_format = HDMI_INFOFRAME_AVI_DB1Y_RGB;
- avi_cfg.db1_active_info =
- HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF;
- avi_cfg.db1_bar_info_dv = HDMI_INFOFRAME_AVI_DB1B_NO;
- avi_cfg.db1_scan_info = HDMI_INFOFRAME_AVI_DB1S_0;
- avi_cfg.db2_colorimetry = HDMI_INFOFRAME_AVI_DB2C_NO;
- avi_cfg.db2_aspect_ratio = HDMI_INFOFRAME_AVI_DB2M_NO;
- avi_cfg.db2_active_fmt_ar = HDMI_INFOFRAME_AVI_DB2R_SAME;
- avi_cfg.db3_itc = HDMI_INFOFRAME_AVI_DB3ITC_NO;
- avi_cfg.db3_ec = HDMI_INFOFRAME_AVI_DB3EC_XVYUV601;
- avi_cfg.db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_DEFAULT;
- avi_cfg.db3_nup_scaling = HDMI_INFOFRAME_AVI_DB3SC_NO;
- avi_cfg.db4_videocode = cfg->cm.code;
- avi_cfg.db5_pixel_repeat = HDMI_INFOFRAME_AVI_DB5PR_NO;
- avi_cfg.db6_7_line_eoftop = 0;
- avi_cfg.db8_9_line_sofbottom = 0;
- avi_cfg.db10_11_pixel_eofleft = 0;
- avi_cfg.db12_13_pixel_sofright = 0;
-
- hdmi_core_aux_infoframe_avi_config(avi_cfg);
-
- /* enable/repeat the infoframe */
- repeat_cfg.avi_infoframe = HDMI_PACKETENABLE;
- repeat_cfg.avi_infoframe_repeat = HDMI_PACKETREPEATON;
- /* wakeup */
- repeat_cfg.audio_pkt = HDMI_PACKETENABLE;
- repeat_cfg.audio_pkt_repeat = HDMI_PACKETREPEATON;
- hdmi_core_av_packet_config(repeat_cfg);
-}
+ if (memcmp(hdmi.edid, edid_header, sizeof(edid_header)))
+ return NULL;
-static void update_hdmi_timings(struct hdmi_config *cfg,
- struct omap_video_timings *timings, int code)
-{
- cfg->timings.timings.x_res = timings->x_res;
- cfg->timings.timings.y_res = timings->y_res;
- cfg->timings.timings.hbp = timings->hbp;
- cfg->timings.timings.hfp = timings->hfp;
- cfg->timings.timings.hsw = timings->hsw;
- cfg->timings.timings.vbp = timings->vbp;
- cfg->timings.timings.vfp = timings->vfp;
- cfg->timings.timings.vsw = timings->vsw;
- cfg->timings.timings.pixel_clock = timings->pixel_clock;
- cfg->timings.vsync_pol = cea_vesa_timings[code].vsync_pol;
- cfg->timings.hsync_pol = cea_vesa_timings[code].hsync_pol;
+ hdmi.edid_set = true;
+ return hdmi.edid;
}
static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
@@ -1127,16 +296,12 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
unsigned long clkin, refclk;
u32 mf;
- clkin = dss_clk_get_rate(DSS_CLK_SYSCK) / 10000;
+ clkin = clk_get_rate(hdmi.sys_clk) / 10000;
/*
* Input clock is predivided by N + 1
* out put of which is reference clk
*/
- if (dssdev->clocks.hdmi.regn == 0)
- pi->regn = HDMI_DEFAULT_REGN;
- else
- pi->regn = dssdev->clocks.hdmi.regn;
-
+ pi->regn = dssdev->clocks.hdmi.regn;
refclk = clkin / (pi->regn + 1);
/*
@@ -1144,11 +309,7 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
* Multiplying by 100 to avoid fractional part removal
*/
pi->regm = (phy * 100 / (refclk)) / 100;
-
- if (dssdev->clocks.hdmi.regm2 == 0)
- pi->regm2 = HDMI_DEFAULT_REGM2;
- else
- pi->regm2 = dssdev->clocks.hdmi.regm2;
+ pi->regm2 = dssdev->clocks.hdmi.regm2;
/*
* fractional multiplier is remainder of the difference between
@@ -1169,26 +330,59 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd);
}
-static void hdmi_enable_clocks(int enable)
-{
- if (enable)
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK |
- DSS_CLK_SYSCK | DSS_CLK_VIDFCK);
- else
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK |
- DSS_CLK_SYSCK | DSS_CLK_VIDFCK);
+static void hdmi_load_hdcp_keys(struct omap_dss_device *dssdev)
+{
+ int aksv;
+ int retries = 5;
+ DSSDBG("hdmi_load_hdcp_keys\n");
+ /* load the keys and reset the wrapper to populate the AKSV registers*/
+ if (hdmi.hdmi_power_on_cb) {
+ aksv = hdmi_ti_4xx_check_aksv_data(&hdmi.hdmi_data);
+ if ((aksv == HDMI_AKSV_ZERO) &&
+ hdmi.custom_set &&
+ hdmi.hdmi_power_on_cb()) {
+ hdmi_ti_4xxx_set_wait_soft_reset(&hdmi.hdmi_data);
+
+ while (retries) {
+ aksv = hdmi_ti_4xx_check_aksv_data(&hdmi.hdmi_data);
+ if (aksv == HDMI_AKSV_VALID)
+ break;
+ msleep(50);
+ retries--;
+ }
+
+ hdmi.wp_reset_done = (aksv == HDMI_AKSV_VALID) ?
+ true : false;
+ DSSINFO("HDMI_WRAPPER RESET DONE\n");
+ } else if (aksv == HDMI_AKSV_VALID)
+ hdmi.wp_reset_done = true;
+ else if (aksv == HDMI_AKSV_ERROR)
+ hdmi.wp_reset_done = false;
+
+ if (!hdmi.wp_reset_done)
+ DSSERR("*** INVALID AKSV: "
+ "Do not perform HDCP AUTHENTICATION\n");
+ }
+
}
static int hdmi_power_on(struct omap_dss_device *dssdev)
{
- int r, code = 0;
+ int r;
struct hdmi_pll_info pll_data;
struct omap_video_timings *p;
unsigned long phy;
- hdmi_enable_clocks(1);
+ r = hdmi_runtime_get();
+ if (r)
+ return r;
+
+ /* Load the HDCP keys if not already loaded*/
+ hdmi_load_hdcp_keys(dssdev);
- dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 0);
+ hdmi_ti_4xxx_wp_video_start(&hdmi.hdmi_data, 0);
+
+ dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, dssdev->type, 0);
p = &dssdev->panel.timings;
@@ -1197,35 +391,53 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
dssdev->panel.timings.y_res);
if (!hdmi.custom_set) {
- DSSDBG("Read EDID as no EDID is not set on poweron\n");
- hdmi_read_edid(p);
+ struct fb_videomode vesa_vga = vesa_modes[4];
+ hdmi_set_timings(&vesa_vga, false);
}
- code = get_timings_index();
- dssdev->panel.timings = cea_vesa_timings[code].timings;
- update_hdmi_timings(&hdmi.cfg, p, code);
+
+ omapfb_fb2dss_timings(&hdmi.cfg.timings, &dssdev->panel.timings);
phy = p->pixel_clock;
- hdmi_compute_pll(dssdev, phy, &pll_data);
+ switch (hdmi.deep_color) {
+ case HDMI_DEEP_COLOR_30BIT:
+ phy = (p->pixel_clock * 125) / 100 ;
+ hdmi.cfg.deep_color = HDMI_DEEP_COLOR_30BIT;
+ break;
+ case HDMI_DEEP_COLOR_36BIT:
+ if (p->pixel_clock == 148500) {
+ printk(KERN_ERR "36 bit deep color not supported");
+ goto err;
+ }
- hdmi_wp_video_start(0);
+ phy = (p->pixel_clock * 150) / 100;
+ hdmi.cfg.deep_color = HDMI_DEEP_COLOR_36BIT;
+ break;
+ case HDMI_DEEP_COLOR_24BIT:
+ default:
+ phy = p->pixel_clock;
+ hdmi.cfg.deep_color = HDMI_DEEP_COLOR_24BIT;
+ break;
+ }
- /* config the PLL and PHY first */
- r = hdmi_pll_program(&pll_data);
+ hdmi_compute_pll(dssdev, phy, &pll_data);
+
+ /* config the PLL and PHY hdmi_set_pll_pwrfirst */
+ r = hdmi_ti_4xxx_pll_program(&hdmi.hdmi_data, &pll_data);
if (r) {
DSSDBG("Failed to lock PLL\n");
goto err;
}
- r = hdmi_phy_init();
+ r = hdmi_ti_4xxx_phy_init(&hdmi.hdmi_data);
if (r) {
DSSDBG("Failed to start PHY\n");
goto err;
}
- hdmi.cfg.cm.mode = hdmi.mode;
+ hdmi.cfg.cm.mode = hdmi.can_do_hdmi ? hdmi.mode : HDMI_DVI;
hdmi.cfg.cm.code = hdmi.code;
- hdmi_basic_configure(&hdmi.cfg);
+ hdmi_ti_4xxx_basic_configure(&hdmi.hdmi_data, &hdmi.cfg);
/* Make selection of HDMI in DSS */
dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK);
@@ -1245,65 +457,154 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
dispc_set_digit_size(dssdev->panel.timings.x_res,
dssdev->panel.timings.y_res);
- dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 1);
+ dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, dssdev->type, 1);
+
+ hdmi_ti_4xxx_wp_video_start(&hdmi.hdmi_data, 1);
- hdmi_wp_video_start(1);
+ if (hdmi.hdmi_start_frame_cb &&
+ hdmi.custom_set &&
+ hdmi.wp_reset_done)
+ (*hdmi.hdmi_start_frame_cb)();
return 0;
err:
- hdmi_enable_clocks(0);
+ hdmi_runtime_put();
return -EIO;
}
static void hdmi_power_off(struct omap_dss_device *dssdev)
{
- dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 0);
+ if (hdmi.hdmi_irq_cb)
+ hdmi.hdmi_irq_cb(HDMI_HPD_LOW);
+
+ hdmi_ti_4xxx_wp_video_start(&hdmi.hdmi_data, 0);
+
+ dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, dssdev->type, 0);
+ hdmi_ti_4xxx_phy_off(&hdmi.hdmi_data, hdmi.set_mode);
+ hdmi_ti_4xxx_set_pll_pwr(&hdmi.hdmi_data, HDMI_PLLPWRCMD_ALLOFF);
+ hdmi_runtime_put();
+ hdmi.deep_color = HDMI_DEEP_COLOR_24BIT;
+}
- hdmi_wp_video_start(0);
- hdmi_phy_off();
- hdmi_set_pll_pwr(HDMI_PLLPWRCMD_ALLOFF);
- hdmi_enable_clocks(0);
+int omapdss_hdmi_get_pixel_clock(void)
+{
+ return PICOS2KHZ(hdmi.cfg.timings.pixclock);
+}
- hdmi.edid_set = 0;
+int omapdss_hdmi_get_mode(void)
+{
+ return hdmi.mode;
+}
+
+int omapdss_hdmi_register_hdcp_callbacks(void (*hdmi_start_frame_cb)(void),
+ void (*hdmi_irq_cb)(int status),
+ bool (*hdmi_power_on_cb)(void))
+{
+ hdmi.hdmi_start_frame_cb = hdmi_start_frame_cb;
+ hdmi.hdmi_irq_cb = hdmi_irq_cb;
+ hdmi.hdmi_power_on_cb = hdmi_power_on_cb;
+
+ return hdmi_ti_4xxx_wp_get_video_state(&hdmi.hdmi_data);
+}
+EXPORT_SYMBOL(omapdss_hdmi_register_hdcp_callbacks);
+
+void omapdss_hdmi_set_deepcolor(int val)
+{
+ hdmi.deep_color = val;
+}
+
+int omapdss_hdmi_get_deepcolor(void)
+{
+ return hdmi.deep_color;
+}
+
+int hdmi_get_current_hpd()
+{
+ return gpio_get_value(hdmi.dssdev->hpd_gpio);
+}
+
+static irqreturn_t hpd_irq_handler(int irq, void *ptr)
+{
+ int hpd = hdmi_get_current_hpd();
+ pr_info("hpd %d\n", hpd);
+
+ hdmi_panel_hpd_handler(hpd);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t hdmi_irq_handler(int irq, void *arg)
+{
+ int r = 0;
+
+ r = hdmi_ti_4xxx_irq_handler(&hdmi.hdmi_data);
+
+ DSSDBG("Received HDMI IRQ = %08x\n", r);
+
+ if (hdmi.hdmi_irq_cb)
+ hdmi.hdmi_irq_cb(r);
+
+ return IRQ_HANDLED;
}
int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
- struct hdmi_cm cm;
+ struct fb_videomode t;
- cm = hdmi_get_code(timings);
- if (cm.code == -1) {
- DSSERR("Invalid timing entered\n");
- return -EINVAL;
- }
+ omapfb_dss2fb_timings(timings, &t);
+ /* also check interlaced timings */
+ if (!hdmi_set_timings(&t, true)) {
+ t.yres *= 2;
+ t.vmode |= FB_VMODE_INTERLACED;
+ }
+ if (!hdmi_set_timings(&t, true))
+ return -EINVAL;
return 0;
+}
+int omapdss_hdmi_display_set_mode(struct omap_dss_device *dssdev,
+ struct fb_videomode *vm)
+{
+ int r1, r2;
+ DSSINFO("Enter omapdss_hdmi_display_set_mode\n");
+ /* turn the hdmi off and on to get new timings to use */
+ hdmi.set_mode = true;
+ dssdev->driver->disable(dssdev);
+ hdmi.set_mode = false;
+ r1 = hdmi_set_timings(vm, false) ? 0 : -EINVAL;
+ hdmi.custom_set = 1;
+ hdmi.code = hdmi.cfg.cm.code;
+ hdmi.mode = hdmi.cfg.cm.mode;
+ r2 = dssdev->driver->enable(dssdev);
+ return r1 ? : r2;
}
void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev)
{
- struct hdmi_cm cm;
+ struct fb_videomode t;
- hdmi.custom_set = 1;
- cm = hdmi_get_code(&dssdev->panel.timings);
- hdmi.code = cm.code;
- hdmi.mode = cm.mode;
- omapdss_hdmi_display_enable(dssdev);
- hdmi.custom_set = 0;
+ omapfb_dss2fb_timings(&dssdev->panel.timings, &t);
+ /* also check interlaced timings */
+ if (!hdmi_set_timings(&t, true)) {
+ t.yres *= 2;
+ t.vmode |= FB_VMODE_INTERLACED;
+ }
+
+ omapdss_hdmi_display_set_mode(dssdev, &t);
}
int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev)
{
- struct omap_dss_hdmi_data *priv = dssdev->data;
int r = 0;
- DSSDBG("ENTER hdmi_display_enable\n");
+ DSSINFO("ENTER hdmi_display_enable\n");
mutex_lock(&hdmi.lock);
- hdmi.hpd_gpio = priv->hpd_gpio;
+ if (hdmi.enabled)
+ goto err0;
r = omap_dss_start_device(dssdev);
if (r) {
@@ -1319,15 +620,34 @@ int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev)
}
}
+ hdmi.hdmi_reg = regulator_get(NULL, "hdmi_vref");
+ if (IS_ERR_OR_NULL(hdmi.hdmi_reg)) {
+ DSSERR("Failed to get hdmi_vref regulator\n");
+ r = PTR_ERR(hdmi.hdmi_reg) ? : -ENODEV;
+ goto err2;
+ }
+
+ r = regulator_enable(hdmi.hdmi_reg);
+ if (r) {
+ DSSERR("failed to enable hdmi_vref regulator\n");
+ goto err3;
+ }
+
r = hdmi_power_on(dssdev);
if (r) {
DSSERR("failed to power on device\n");
- goto err2;
+ goto err4;
}
+ hdmi.enabled = true;
+
mutex_unlock(&hdmi.lock);
return 0;
+err4:
+ regulator_disable(hdmi.hdmi_reg);
+err3:
+ regulator_put(hdmi.hdmi_reg);
err2:
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
@@ -1340,440 +660,91 @@ err0:
void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev)
{
- DSSDBG("Enter hdmi_display_disable\n");
+ DSSINFO("Enter hdmi_display_disable\n");
mutex_lock(&hdmi.lock);
+ if (!hdmi.enabled)
+ goto done;
+
+ hdmi.enabled = false;
+ hdmi.wp_reset_done = false;
+
hdmi_power_off(dssdev);
+ if (dssdev->sync_lost_error == 0)
+ if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
+ /* clear EDID and mode on disable only */
+ hdmi.edid_set = false;
+ hdmi.custom_set = 0;
+ pr_info("hdmi: clearing EDID info\n");
+ }
+ regulator_disable(hdmi.hdmi_reg);
+
+ regulator_put(hdmi.hdmi_reg);
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
omap_dss_stop_device(dssdev);
-
+done:
mutex_unlock(&hdmi.lock);
}
-#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
- defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
-static void hdmi_wp_audio_config_format(
- struct hdmi_audio_format *aud_fmt)
-{
- u32 r;
-
- DSSDBG("Enter hdmi_wp_audio_config_format\n");
-
- r = hdmi_read_reg(HDMI_WP_AUDIO_CFG);
- r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24);
- r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16);
- r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5);
- r = FLD_MOD(r, aud_fmt->type, 4, 4);
- r = FLD_MOD(r, aud_fmt->justification, 3, 3);
- r = FLD_MOD(r, aud_fmt->sample_order, 2, 2);
- r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1);
- r = FLD_MOD(r, aud_fmt->sample_size, 0, 0);
- hdmi_write_reg(HDMI_WP_AUDIO_CFG, r);
-}
-
-static void hdmi_wp_audio_config_dma(struct hdmi_audio_dma *aud_dma)
+static int hdmi_get_clocks(struct platform_device *pdev)
{
- u32 r;
-
- DSSDBG("Enter hdmi_wp_audio_config_dma\n");
-
- r = hdmi_read_reg(HDMI_WP_AUDIO_CFG2);
- r = FLD_MOD(r, aud_dma->transfer_size, 15, 8);
- r = FLD_MOD(r, aud_dma->block_size, 7, 0);
- hdmi_write_reg(HDMI_WP_AUDIO_CFG2, r);
+ struct clk *clk;
- r = hdmi_read_reg(HDMI_WP_AUDIO_CTRL);
- r = FLD_MOD(r, aud_dma->mode, 9, 9);
- r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0);
- hdmi_write_reg(HDMI_WP_AUDIO_CTRL, r);
-}
-
-static void hdmi_core_audio_config(struct hdmi_core_audio_config *cfg)
-{
- u32 r;
-
- /* audio clock recovery parameters */
- r = hdmi_read_reg(HDMI_CORE_AV_ACR_CTRL);
- r = FLD_MOD(r, cfg->use_mclk, 2, 2);
- r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1);
- r = FLD_MOD(r, cfg->cts_mode, 0, 0);
- hdmi_write_reg(HDMI_CORE_AV_ACR_CTRL, r);
-
- REG_FLD_MOD(HDMI_CORE_AV_N_SVAL1, cfg->n, 7, 0);
- REG_FLD_MOD(HDMI_CORE_AV_N_SVAL2, cfg->n >> 8, 7, 0);
- REG_FLD_MOD(HDMI_CORE_AV_N_SVAL3, cfg->n >> 16, 7, 0);
-
- if (cfg->cts_mode == HDMI_AUDIO_CTS_MODE_SW) {
- REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL1, cfg->cts, 7, 0);
- REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL2, cfg->cts >> 8, 7, 0);
- REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL3, cfg->cts >> 16, 7, 0);
- } else {
- /*
- * HDMI IP uses this configuration to divide the MCLK to
- * update CTS value.
- */
- REG_FLD_MOD(HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0);
-
- /* Configure clock for audio packets */
- REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_1,
- cfg->aud_par_busclk, 7, 0);
- REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_2,
- (cfg->aud_par_busclk >> 8), 7, 0);
- REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_3,
- (cfg->aud_par_busclk >> 16), 7, 0);
+ clk = clk_get(&pdev->dev, "sys_clk");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get sys_clk\n");
+ return PTR_ERR(clk);
}
- /* Override of SPDIF sample frequency with value in I2S_CHST4 */
- REG_FLD_MOD(HDMI_CORE_AV_SPDIF_CTRL, cfg->fs_override, 1, 1);
-
- /* I2S parameters */
- REG_FLD_MOD(HDMI_CORE_AV_I2S_CHST4, cfg->freq_sample, 3, 0);
-
- r = hdmi_read_reg(HDMI_CORE_AV_I2S_IN_CTRL);
- r = FLD_MOD(r, cfg->i2s_cfg.en_high_bitrate_aud, 7, 7);
- r = FLD_MOD(r, cfg->i2s_cfg.sck_edge_mode, 6, 6);
- r = FLD_MOD(r, cfg->i2s_cfg.cbit_order, 5, 5);
- r = FLD_MOD(r, cfg->i2s_cfg.vbit, 4, 4);
- r = FLD_MOD(r, cfg->i2s_cfg.ws_polarity, 3, 3);
- r = FLD_MOD(r, cfg->i2s_cfg.justification, 2, 2);
- r = FLD_MOD(r, cfg->i2s_cfg.direction, 1, 1);
- r = FLD_MOD(r, cfg->i2s_cfg.shift, 0, 0);
- hdmi_write_reg(HDMI_CORE_AV_I2S_IN_CTRL, r);
-
- r = hdmi_read_reg(HDMI_CORE_AV_I2S_CHST5);
- r = FLD_MOD(r, cfg->freq_sample, 7, 4);
- r = FLD_MOD(r, cfg->i2s_cfg.word_length, 3, 1);
- r = FLD_MOD(r, cfg->i2s_cfg.word_max_length, 0, 0);
- hdmi_write_reg(HDMI_CORE_AV_I2S_CHST5, r);
-
- REG_FLD_MOD(HDMI_CORE_AV_I2S_IN_LEN, cfg->i2s_cfg.in_length_bits, 3, 0);
-
- /* Audio channels and mode parameters */
- REG_FLD_MOD(HDMI_CORE_AV_HDMI_CTRL, cfg->layout, 2, 1);
- r = hdmi_read_reg(HDMI_CORE_AV_AUD_MODE);
- r = FLD_MOD(r, cfg->i2s_cfg.active_sds, 7, 4);
- r = FLD_MOD(r, cfg->en_dsd_audio, 3, 3);
- r = FLD_MOD(r, cfg->en_parallel_aud_input, 2, 2);
- r = FLD_MOD(r, cfg->en_spdif, 1, 1);
- hdmi_write_reg(HDMI_CORE_AV_AUD_MODE, r);
-}
-
-static void hdmi_core_audio_infoframe_config(
- struct hdmi_core_infoframe_audio *info_aud)
-{
- u8 val;
- u8 sum = 0, checksum = 0;
-
- /*
- * Set audio info frame type, version and length as
- * described in HDMI 1.4a Section 8.2.2 specification.
- * Checksum calculation is defined in Section 5.3.5.
- */
- hdmi_write_reg(HDMI_CORE_AV_AUDIO_TYPE, 0x84);
- hdmi_write_reg(HDMI_CORE_AV_AUDIO_VERS, 0x01);
- hdmi_write_reg(HDMI_CORE_AV_AUDIO_LEN, 0x0a);
- sum += 0x84 + 0x001 + 0x00a;
-
- val = (info_aud->db1_coding_type << 4)
- | (info_aud->db1_channel_count - 1);
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(0), val);
- sum += val;
-
- val = (info_aud->db2_sample_freq << 2) | info_aud->db2_sample_size;
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(1), val);
- sum += val;
-
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(2), 0x00);
-
- val = info_aud->db4_channel_alloc;
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(3), val);
- sum += val;
-
- val = (info_aud->db5_downmix_inh << 7) | (info_aud->db5_lsv << 3);
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(4), val);
- sum += val;
-
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(5), 0x00);
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(6), 0x00);
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(7), 0x00);
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(8), 0x00);
- hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(9), 0x00);
-
- checksum = 0x100 - sum;
- hdmi_write_reg(HDMI_CORE_AV_AUDIO_CHSUM, checksum);
-
- /*
- * TODO: Add MPEG and SPD enable and repeat cfg when EDID parsing
- * is available.
- */
-}
-
-static int hdmi_config_audio_acr(u32 sample_freq, u32 *n, u32 *cts)
-{
- u32 r;
- u32 deep_color = 0;
- u32 pclk = hdmi.cfg.timings.timings.pixel_clock;
-
- if (n == NULL || cts == NULL)
- return -EINVAL;
- /*
- * Obtain current deep color configuration. This needed
- * to calculate the TMDS clock based on the pixel clock.
- */
- r = REG_GET(HDMI_WP_VIDEO_CFG, 1, 0);
- switch (r) {
- case 1: /* No deep color selected */
- deep_color = 100;
- break;
- case 2: /* 10-bit deep color selected */
- deep_color = 125;
- break;
- case 3: /* 12-bit deep color selected */
- deep_color = 150;
- break;
- default:
- return -EINVAL;
- }
-
- switch (sample_freq) {
- case 32000:
- if ((deep_color == 125) && ((pclk == 54054)
- || (pclk == 74250)))
- *n = 8192;
- else
- *n = 4096;
- break;
- case 44100:
- *n = 6272;
- break;
- case 48000:
- if ((deep_color == 125) && ((pclk == 54054)
- || (pclk == 74250)))
- *n = 8192;
- else
- *n = 6144;
- break;
- default:
- *n = 0;
- return -EINVAL;
- }
-
- /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */
- *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10);
-
- return 0;
-}
-
-static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct hdmi_audio_format audio_format;
- struct hdmi_audio_dma audio_dma;
- struct hdmi_core_audio_config core_cfg;
- struct hdmi_core_infoframe_audio aud_if_cfg;
- int err, n, cts;
- enum hdmi_core_audio_sample_freq sample_freq;
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- core_cfg.i2s_cfg.word_max_length =
- HDMI_AUDIO_I2S_MAX_WORD_20BITS;
- core_cfg.i2s_cfg.word_length = HDMI_AUDIO_I2S_CHST_WORD_16_BITS;
- core_cfg.i2s_cfg.in_length_bits =
- HDMI_AUDIO_I2S_INPUT_LENGTH_16;
- core_cfg.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT;
- audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES;
- audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS;
- audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT;
- audio_dma.transfer_size = 0x10;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- core_cfg.i2s_cfg.word_max_length =
- HDMI_AUDIO_I2S_MAX_WORD_24BITS;
- core_cfg.i2s_cfg.word_length = HDMI_AUDIO_I2S_CHST_WORD_24_BITS;
- core_cfg.i2s_cfg.in_length_bits =
- HDMI_AUDIO_I2S_INPUT_LENGTH_24;
- audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_ONESAMPLE;
- audio_format.sample_size = HDMI_AUDIO_SAMPLE_24BITS;
- audio_format.justification = HDMI_AUDIO_JUSTIFY_RIGHT;
- core_cfg.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT;
- audio_dma.transfer_size = 0x20;
- break;
- default:
- return -EINVAL;
- }
+ hdmi.sys_clk = clk;
- switch (params_rate(params)) {
- case 32000:
- sample_freq = HDMI_AUDIO_FS_32000;
- break;
- case 44100:
- sample_freq = HDMI_AUDIO_FS_44100;
- break;
- case 48000:
- sample_freq = HDMI_AUDIO_FS_48000;
- break;
- default:
- return -EINVAL;
+ clk = clk_get(&pdev->dev, "hdmi_clk");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get hdmi_clk\n");
+ clk_put(hdmi.sys_clk);
+ return PTR_ERR(clk);
}
- err = hdmi_config_audio_acr(params_rate(params), &n, &cts);
- if (err < 0)
- return err;
-
- /* Audio wrapper config */
- audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL;
- audio_format.active_chnnls_msk = 0x03;
- audio_format.type = HDMI_AUDIO_TYPE_LPCM;
- audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST;
- /* Disable start/stop signals of IEC 60958 blocks */
- audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF;
-
- audio_dma.block_size = 0xC0;
- audio_dma.mode = HDMI_AUDIO_TRANSF_DMA;
- audio_dma.fifo_threshold = 0x20; /* in number of samples */
+ hdmi.hdmi_clk = clk;
- hdmi_wp_audio_config_dma(&audio_dma);
- hdmi_wp_audio_config_format(&audio_format);
-
- /*
- * I2S config
- */
- core_cfg.i2s_cfg.en_high_bitrate_aud = false;
- /* Only used with high bitrate audio */
- core_cfg.i2s_cfg.cbit_order = false;
- /* Serial data and word select should change on sck rising edge */
- core_cfg.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING;
- core_cfg.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM;
- /* Set I2S word select polarity */
- core_cfg.i2s_cfg.ws_polarity = HDMI_AUDIO_I2S_WS_POLARITY_LOW_IS_LEFT;
- core_cfg.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST;
- /* Set serial data to word select shift. See Phillips spec. */
- core_cfg.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT;
- /* Enable one of the four available serial data channels */
- core_cfg.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN;
-
- /* Core audio config */
- core_cfg.freq_sample = sample_freq;
- core_cfg.n = n;
- core_cfg.cts = cts;
- if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) {
- core_cfg.aud_par_busclk = 0;
- core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_SW;
- core_cfg.use_mclk = false;
- } else {
- core_cfg.aud_par_busclk = (((128 * 31) - 1) << 8);
- core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_HW;
- core_cfg.use_mclk = true;
- core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS;
- }
- core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH;
- core_cfg.en_spdif = false;
- /* Use sample frequency from channel status word */
- core_cfg.fs_override = true;
- /* Enable ACR packets */
- core_cfg.en_acr_pkt = true;
- /* Disable direct streaming digital audio */
- core_cfg.en_dsd_audio = false;
- /* Use parallel audio interface */
- core_cfg.en_parallel_aud_input = true;
-
- hdmi_core_audio_config(&core_cfg);
-
- /*
- * Configure packet
- * info frame audio see doc CEA861-D page 74
- */
- aud_if_cfg.db1_coding_type = HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM;
- aud_if_cfg.db1_channel_count = 2;
- aud_if_cfg.db2_sample_freq = HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM;
- aud_if_cfg.db2_sample_size = HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM;
- aud_if_cfg.db4_channel_alloc = 0x00;
- aud_if_cfg.db5_downmix_inh = false;
- aud_if_cfg.db5_lsv = 0;
-
- hdmi_core_audio_infoframe_config(&aud_if_cfg);
return 0;
}
-static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
+static void hdmi_put_clocks(void)
{
- int err = 0;
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- REG_FLD_MOD(HDMI_CORE_AV_AUD_MODE, 1, 0, 0);
- REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 1, 31, 31);
- REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 1, 30, 30);
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- REG_FLD_MOD(HDMI_CORE_AV_AUD_MODE, 0, 0, 0);
- REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 0, 30, 30);
- REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 0, 31, 31);
- break;
- default:
- err = -EINVAL;
- }
- return err;
-}
-
-static int hdmi_audio_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- if (!hdmi.mode) {
- pr_err("Current video settings do not support audio.\n");
- return -EIO;
- }
- return 0;
+ if (hdmi.sys_clk)
+ clk_put(hdmi.sys_clk);
+ if (hdmi.hdmi_clk)
+ clk_put(hdmi.hdmi_clk);
}
-static struct snd_soc_codec_driver hdmi_audio_codec_drv = {
-};
-
-static struct snd_soc_dai_ops hdmi_audio_codec_ops = {
- .hw_params = hdmi_audio_hw_params,
- .trigger = hdmi_audio_trigger,
- .startup = hdmi_audio_startup,
-};
-
-static struct snd_soc_dai_driver hdmi_codec_dai_drv = {
- .name = "hdmi-audio-codec",
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_32000 |
- SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- },
- .ops = &hdmi_audio_codec_ops,
-};
-#endif
-
/* HDMI HW IP initialisation */
static int omapdss_hdmihw_probe(struct platform_device *pdev)
{
struct resource *hdmi_mem;
-#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
- defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
- int ret;
-#endif
+ struct omap_dss_board_info *board_data;
+ int r;
hdmi.pdata = pdev->dev.platform_data;
hdmi.pdev = pdev;
mutex_init(&hdmi.lock);
+ /* save reference to HDMI device */
+ board_data = hdmi.pdata->board_data;
+ for (r = 0; r < board_data->num_devices; r++) {
+ if (board_data->devices[r]->type == OMAP_DISPLAY_TYPE_HDMI)
+ hdmi.dssdev = board_data->devices[r];
+ }
+ if (!hdmi.dssdev) {
+ DSSERR("can't get HDMI device\n");
+ return -EINVAL;
+ }
+
hdmi_mem = platform_get_resource(hdmi.pdev, IORESOURCE_MEM, 0);
if (!hdmi_mem) {
DSSERR("can't get IORESOURCE_MEM HDMI\n");
@@ -1781,25 +752,47 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
}
/* Base address taken from platform */
- hdmi.base_wp = ioremap(hdmi_mem->start, resource_size(hdmi_mem));
- if (!hdmi.base_wp) {
+ hdmi.hdmi_data.base_wp = ioremap(hdmi_mem->start,
+ resource_size(hdmi_mem));
+ if (!hdmi.hdmi_data.base_wp) {
DSSERR("can't ioremap WP\n");
return -ENOMEM;
}
- hdmi_panel_init();
+ r = hdmi_get_clocks(pdev);
+ if (r) {
+ iounmap(hdmi.hdmi_data.base_wp);
+ return r;
+ }
-#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
- defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
+ pm_runtime_enable(&pdev->dev);
- /* Register ASoC codec DAI */
- ret = snd_soc_register_codec(&pdev->dev, &hdmi_audio_codec_drv,
- &hdmi_codec_dai_drv, 1);
- if (ret) {
- DSSERR("can't register ASoC HDMI audio codec\n");
- return ret;
+ r = request_irq(gpio_to_irq(hdmi.dssdev->hpd_gpio), hpd_irq_handler,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "hpd", NULL);
+ if (r < 0) {
+ pr_err("hdmi: request_irq %d failed\n",
+ gpio_to_irq(hdmi.dssdev->hpd_gpio));
+ return -EINVAL;
+ }
+
+ hdmi.hdmi_irq = platform_get_irq(pdev, 0);
+
+ r = request_irq(hdmi.hdmi_irq, hdmi_irq_handler, 0, "OMAP HDMI", NULL);
+ if (r < 0) {
+ pr_err("hdmi: request_irq %s failed\n",
+ pdev->name);
+ return -EINVAL;
}
-#endif
+
+ hdmi.hdmi_data.hdmi_core_sys_offset = HDMI_CORE_SYS;
+ hdmi.hdmi_data.hdmi_core_av_offset = HDMI_CORE_AV;
+ hdmi.hdmi_data.hdmi_pll_offset = HDMI_PLLCTRL;
+ hdmi.hdmi_data.hdmi_phy_offset = HDMI_PHY;
+ hdmi.wp_reset_done = false;
+
+ hdmi_panel_init();
+
return 0;
}
@@ -1807,12 +800,15 @@ static int omapdss_hdmihw_remove(struct platform_device *pdev)
{
hdmi_panel_exit();
-#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
- defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
- snd_soc_unregister_codec(&pdev->dev);
-#endif
+ if (hdmi.dssdev)
+ free_irq(gpio_to_irq(hdmi.dssdev->hpd_gpio), hpd_irq_handler);
+ hdmi.dssdev = NULL;
- iounmap(hdmi.base_wp);
+ pm_runtime_disable(&pdev->dev);
+
+ hdmi_put_clocks();
+
+ iounmap(hdmi.hdmi_data.base_wp);
return 0;
}
@@ -1835,3 +831,13 @@ void hdmi_uninit_platform_driver(void)
{
return platform_driver_unregister(&omapdss_hdmihw_driver);
}
+
+void hdmi_dump_regs(struct seq_file *s)
+{
+ if (hdmi_runtime_get())
+ return;
+
+ hdmi_ti_4xxx_dump_regs(&hdmi.hdmi_data, s);
+
+ hdmi_runtime_put();
+}
diff --git a/drivers/video/omap2/dss/hdmi.h b/drivers/video/omap2/dss/hdmi.h
deleted file mode 100644
index c885f9c..0000000
--- a/drivers/video/omap2/dss/hdmi.h
+++ /dev/null
@@ -1,631 +0,0 @@
-/*
- * hdmi.h
- *
- * HDMI driver definition for TI OMAP4 processors.
- *
- * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.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 _OMAP4_DSS_HDMI_H_
-#define _OMAP4_DSS_HDMI_H_
-
-#include <linux/string.h>
-#include <video/omapdss.h>
-
-#define HDMI_WP 0x0
-#define HDMI_CORE_SYS 0x400
-#define HDMI_CORE_AV 0x900
-#define HDMI_PLLCTRL 0x200
-#define HDMI_PHY 0x300
-
-struct hdmi_reg { u16 idx; };
-
-#define HDMI_REG(idx) ((const struct hdmi_reg) { idx })
-
-/* HDMI Wrapper */
-#define HDMI_WP_REG(idx) HDMI_REG(HDMI_WP + idx)
-
-#define HDMI_WP_REVISION HDMI_WP_REG(0x0)
-#define HDMI_WP_SYSCONFIG HDMI_WP_REG(0x10)
-#define HDMI_WP_IRQSTATUS_RAW HDMI_WP_REG(0x24)
-#define HDMI_WP_IRQSTATUS HDMI_WP_REG(0x28)
-#define HDMI_WP_PWR_CTRL HDMI_WP_REG(0x40)
-#define HDMI_WP_IRQENABLE_SET HDMI_WP_REG(0x2C)
-#define HDMI_WP_VIDEO_CFG HDMI_WP_REG(0x50)
-#define HDMI_WP_VIDEO_SIZE HDMI_WP_REG(0x60)
-#define HDMI_WP_VIDEO_TIMING_H HDMI_WP_REG(0x68)
-#define HDMI_WP_VIDEO_TIMING_V HDMI_WP_REG(0x6C)
-#define HDMI_WP_WP_CLK HDMI_WP_REG(0x70)
-#define HDMI_WP_AUDIO_CFG HDMI_WP_REG(0x80)
-#define HDMI_WP_AUDIO_CFG2 HDMI_WP_REG(0x84)
-#define HDMI_WP_AUDIO_CTRL HDMI_WP_REG(0x88)
-#define HDMI_WP_AUDIO_DATA HDMI_WP_REG(0x8C)
-
-/* HDMI IP Core System */
-#define HDMI_CORE_SYS_REG(idx) HDMI_REG(HDMI_CORE_SYS + idx)
-
-#define HDMI_CORE_SYS_VND_IDL HDMI_CORE_SYS_REG(0x0)
-#define HDMI_CORE_SYS_DEV_IDL HDMI_CORE_SYS_REG(0x8)
-#define HDMI_CORE_SYS_DEV_IDH HDMI_CORE_SYS_REG(0xC)
-#define HDMI_CORE_SYS_DEV_REV HDMI_CORE_SYS_REG(0x10)
-#define HDMI_CORE_SYS_SRST HDMI_CORE_SYS_REG(0x14)
-#define HDMI_CORE_CTRL1 HDMI_CORE_SYS_REG(0x20)
-#define HDMI_CORE_SYS_SYS_STAT HDMI_CORE_SYS_REG(0x24)
-#define HDMI_CORE_SYS_VID_ACEN HDMI_CORE_SYS_REG(0x124)
-#define HDMI_CORE_SYS_VID_MODE HDMI_CORE_SYS_REG(0x128)
-#define HDMI_CORE_SYS_INTR_STATE HDMI_CORE_SYS_REG(0x1C0)
-#define HDMI_CORE_SYS_INTR1 HDMI_CORE_SYS_REG(0x1C4)
-#define HDMI_CORE_SYS_INTR2 HDMI_CORE_SYS_REG(0x1C8)
-#define HDMI_CORE_SYS_INTR3 HDMI_CORE_SYS_REG(0x1CC)
-#define HDMI_CORE_SYS_INTR4 HDMI_CORE_SYS_REG(0x1D0)
-#define HDMI_CORE_SYS_UMASK1 HDMI_CORE_SYS_REG(0x1D4)
-#define HDMI_CORE_SYS_TMDS_CTRL HDMI_CORE_SYS_REG(0x208)
-#define HDMI_CORE_SYS_DE_DLY HDMI_CORE_SYS_REG(0xC8)
-#define HDMI_CORE_SYS_DE_CTRL HDMI_CORE_SYS_REG(0xCC)
-#define HDMI_CORE_SYS_DE_TOP HDMI_CORE_SYS_REG(0xD0)
-#define HDMI_CORE_SYS_DE_CNTL HDMI_CORE_SYS_REG(0xD8)
-#define HDMI_CORE_SYS_DE_CNTH HDMI_CORE_SYS_REG(0xDC)
-#define HDMI_CORE_SYS_DE_LINL HDMI_CORE_SYS_REG(0xE0)
-#define HDMI_CORE_SYS_DE_LINH_1 HDMI_CORE_SYS_REG(0xE4)
-#define HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC 0x1
-#define HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC 0x1
-#define HDMI_CORE_CTRL1_BSEL_24BITBUS 0x1
-#define HDMI_CORE_CTRL1_EDGE_RISINGEDGE 0x1
-
-/* HDMI DDC E-DID */
-#define HDMI_CORE_DDC_CMD HDMI_CORE_SYS_REG(0x3CC)
-#define HDMI_CORE_DDC_STATUS HDMI_CORE_SYS_REG(0x3C8)
-#define HDMI_CORE_DDC_ADDR HDMI_CORE_SYS_REG(0x3B4)
-#define HDMI_CORE_DDC_OFFSET HDMI_CORE_SYS_REG(0x3BC)
-#define HDMI_CORE_DDC_COUNT1 HDMI_CORE_SYS_REG(0x3C0)
-#define HDMI_CORE_DDC_COUNT2 HDMI_CORE_SYS_REG(0x3C4)
-#define HDMI_CORE_DDC_DATA HDMI_CORE_SYS_REG(0x3D0)
-#define HDMI_CORE_DDC_SEGM HDMI_CORE_SYS_REG(0x3B8)
-
-/* HDMI IP Core Audio Video */
-#define HDMI_CORE_AV_REG(idx) HDMI_REG(HDMI_CORE_AV + idx)
-
-#define HDMI_CORE_AV_HDMI_CTRL HDMI_CORE_AV_REG(0xBC)
-#define HDMI_CORE_AV_DPD HDMI_CORE_AV_REG(0xF4)
-#define HDMI_CORE_AV_PB_CTRL1 HDMI_CORE_AV_REG(0xF8)
-#define HDMI_CORE_AV_PB_CTRL2 HDMI_CORE_AV_REG(0xFC)
-#define HDMI_CORE_AV_AVI_TYPE HDMI_CORE_AV_REG(0x100)
-#define HDMI_CORE_AV_AVI_VERS HDMI_CORE_AV_REG(0x104)
-#define HDMI_CORE_AV_AVI_LEN HDMI_CORE_AV_REG(0x108)
-#define HDMI_CORE_AV_AVI_CHSUM HDMI_CORE_AV_REG(0x10C)
-#define HDMI_CORE_AV_AVI_DBYTE(n) HDMI_CORE_AV_REG(n * 4 + 0x110)
-#define HDMI_CORE_AV_AVI_DBYTE_NELEMS HDMI_CORE_AV_REG(15)
-#define HDMI_CORE_AV_SPD_DBYTE HDMI_CORE_AV_REG(0x190)
-#define HDMI_CORE_AV_SPD_DBYTE_NELEMS HDMI_CORE_AV_REG(27)
-#define HDMI_CORE_AV_AUD_DBYTE(n) HDMI_CORE_AV_REG(n * 4 + 0x210)
-#define HDMI_CORE_AV_AUD_DBYTE_NELEMS HDMI_CORE_AV_REG(10)
-#define HDMI_CORE_AV_MPEG_DBYTE HDMI_CORE_AV_REG(0x290)
-#define HDMI_CORE_AV_MPEG_DBYTE_NELEMS HDMI_CORE_AV_REG(27)
-#define HDMI_CORE_AV_GEN_DBYTE HDMI_CORE_AV_REG(0x300)
-#define HDMI_CORE_AV_GEN_DBYTE_NELEMS HDMI_CORE_AV_REG(31)
-#define HDMI_CORE_AV_GEN2_DBYTE HDMI_CORE_AV_REG(0x380)
-#define HDMI_CORE_AV_GEN2_DBYTE_NELEMS HDMI_CORE_AV_REG(31)
-#define HDMI_CORE_AV_ACR_CTRL HDMI_CORE_AV_REG(0x4)
-#define HDMI_CORE_AV_FREQ_SVAL HDMI_CORE_AV_REG(0x8)
-#define HDMI_CORE_AV_N_SVAL1 HDMI_CORE_AV_REG(0xC)
-#define HDMI_CORE_AV_N_SVAL2 HDMI_CORE_AV_REG(0x10)
-#define HDMI_CORE_AV_N_SVAL3 HDMI_CORE_AV_REG(0x14)
-#define HDMI_CORE_AV_CTS_SVAL1 HDMI_CORE_AV_REG(0x18)
-#define HDMI_CORE_AV_CTS_SVAL2 HDMI_CORE_AV_REG(0x1C)
-#define HDMI_CORE_AV_CTS_SVAL3 HDMI_CORE_AV_REG(0x20)
-#define HDMI_CORE_AV_CTS_HVAL1 HDMI_CORE_AV_REG(0x24)
-#define HDMI_CORE_AV_CTS_HVAL2 HDMI_CORE_AV_REG(0x28)
-#define HDMI_CORE_AV_CTS_HVAL3 HDMI_CORE_AV_REG(0x2C)
-#define HDMI_CORE_AV_AUD_MODE HDMI_CORE_AV_REG(0x50)
-#define HDMI_CORE_AV_SPDIF_CTRL HDMI_CORE_AV_REG(0x54)
-#define HDMI_CORE_AV_HW_SPDIF_FS HDMI_CORE_AV_REG(0x60)
-#define HDMI_CORE_AV_SWAP_I2S HDMI_CORE_AV_REG(0x64)
-#define HDMI_CORE_AV_SPDIF_ERTH HDMI_CORE_AV_REG(0x6C)
-#define HDMI_CORE_AV_I2S_IN_MAP HDMI_CORE_AV_REG(0x70)
-#define HDMI_CORE_AV_I2S_IN_CTRL HDMI_CORE_AV_REG(0x74)
-#define HDMI_CORE_AV_I2S_CHST0 HDMI_CORE_AV_REG(0x78)
-#define HDMI_CORE_AV_I2S_CHST1 HDMI_CORE_AV_REG(0x7C)
-#define HDMI_CORE_AV_I2S_CHST2 HDMI_CORE_AV_REG(0x80)
-#define HDMI_CORE_AV_I2S_CHST4 HDMI_CORE_AV_REG(0x84)
-#define HDMI_CORE_AV_I2S_CHST5 HDMI_CORE_AV_REG(0x88)
-#define HDMI_CORE_AV_ASRC HDMI_CORE_AV_REG(0x8C)
-#define HDMI_CORE_AV_I2S_IN_LEN HDMI_CORE_AV_REG(0x90)
-#define HDMI_CORE_AV_HDMI_CTRL HDMI_CORE_AV_REG(0xBC)
-#define HDMI_CORE_AV_AUDO_TXSTAT HDMI_CORE_AV_REG(0xC0)
-#define HDMI_CORE_AV_AUD_PAR_BUSCLK_1 HDMI_CORE_AV_REG(0xCC)
-#define HDMI_CORE_AV_AUD_PAR_BUSCLK_2 HDMI_CORE_AV_REG(0xD0)
-#define HDMI_CORE_AV_AUD_PAR_BUSCLK_3 HDMI_CORE_AV_REG(0xD4)
-#define HDMI_CORE_AV_TEST_TXCTRL HDMI_CORE_AV_REG(0xF0)
-#define HDMI_CORE_AV_DPD HDMI_CORE_AV_REG(0xF4)
-#define HDMI_CORE_AV_PB_CTRL1 HDMI_CORE_AV_REG(0xF8)
-#define HDMI_CORE_AV_PB_CTRL2 HDMI_CORE_AV_REG(0xFC)
-#define HDMI_CORE_AV_AVI_TYPE HDMI_CORE_AV_REG(0x100)
-#define HDMI_CORE_AV_AVI_VERS HDMI_CORE_AV_REG(0x104)
-#define HDMI_CORE_AV_AVI_LEN HDMI_CORE_AV_REG(0x108)
-#define HDMI_CORE_AV_AVI_CHSUM HDMI_CORE_AV_REG(0x10C)
-#define HDMI_CORE_AV_SPD_TYPE HDMI_CORE_AV_REG(0x180)
-#define HDMI_CORE_AV_SPD_VERS HDMI_CORE_AV_REG(0x184)
-#define HDMI_CORE_AV_SPD_LEN HDMI_CORE_AV_REG(0x188)
-#define HDMI_CORE_AV_SPD_CHSUM HDMI_CORE_AV_REG(0x18C)
-#define HDMI_CORE_AV_AUDIO_TYPE HDMI_CORE_AV_REG(0x200)
-#define HDMI_CORE_AV_AUDIO_VERS HDMI_CORE_AV_REG(0x204)
-#define HDMI_CORE_AV_AUDIO_LEN HDMI_CORE_AV_REG(0x208)
-#define HDMI_CORE_AV_AUDIO_CHSUM HDMI_CORE_AV_REG(0x20C)
-#define HDMI_CORE_AV_MPEG_TYPE HDMI_CORE_AV_REG(0x280)
-#define HDMI_CORE_AV_MPEG_VERS HDMI_CORE_AV_REG(0x284)
-#define HDMI_CORE_AV_MPEG_LEN HDMI_CORE_AV_REG(0x288)
-#define HDMI_CORE_AV_MPEG_CHSUM HDMI_CORE_AV_REG(0x28C)
-#define HDMI_CORE_AV_CP_BYTE1 HDMI_CORE_AV_REG(0x37C)
-#define HDMI_CORE_AV_CEC_ADDR_ID HDMI_CORE_AV_REG(0x3FC)
-#define HDMI_CORE_AV_SPD_DBYTE_ELSIZE 0x4
-#define HDMI_CORE_AV_GEN2_DBYTE_ELSIZE 0x4
-#define HDMI_CORE_AV_MPEG_DBYTE_ELSIZE 0x4
-#define HDMI_CORE_AV_GEN_DBYTE_ELSIZE 0x4
-
-/* PLL */
-#define HDMI_PLL_REG(idx) HDMI_REG(HDMI_PLLCTRL + idx)
-
-#define PLLCTRL_PLL_CONTROL HDMI_PLL_REG(0x0)
-#define PLLCTRL_PLL_STATUS HDMI_PLL_REG(0x4)
-#define PLLCTRL_PLL_GO HDMI_PLL_REG(0x8)
-#define PLLCTRL_CFG1 HDMI_PLL_REG(0xC)
-#define PLLCTRL_CFG2 HDMI_PLL_REG(0x10)
-#define PLLCTRL_CFG3 HDMI_PLL_REG(0x14)
-#define PLLCTRL_CFG4 HDMI_PLL_REG(0x20)
-
-/* HDMI PHY */
-#define HDMI_PHY_REG(idx) HDMI_REG(HDMI_PHY + idx)
-
-#define HDMI_TXPHY_TX_CTRL HDMI_PHY_REG(0x0)
-#define HDMI_TXPHY_DIGITAL_CTRL HDMI_PHY_REG(0x4)
-#define HDMI_TXPHY_POWER_CTRL HDMI_PHY_REG(0x8)
-#define HDMI_TXPHY_PAD_CFG_CTRL HDMI_PHY_REG(0xC)
-
-/* HDMI EDID Length */
-#define HDMI_EDID_MAX_LENGTH 256
-#define EDID_TIMING_DESCRIPTOR_SIZE 0x12
-#define EDID_DESCRIPTOR_BLOCK0_ADDRESS 0x36
-#define EDID_DESCRIPTOR_BLOCK1_ADDRESS 0x80
-#define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4
-#define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4
-
-#define OMAP_HDMI_TIMINGS_NB 34
-
-#define REG_FLD_MOD(idx, val, start, end) \
- hdmi_write_reg(idx, FLD_MOD(hdmi_read_reg(idx), val, start, end))
-#define REG_GET(idx, start, end) \
- FLD_GET(hdmi_read_reg(idx), start, end)
-
-/* HDMI timing structure */
-struct hdmi_timings {
- struct omap_video_timings timings;
- int vsync_pol;
- int hsync_pol;
-};
-
-enum hdmi_phy_pwr {
- HDMI_PHYPWRCMD_OFF = 0,
- HDMI_PHYPWRCMD_LDOON = 1,
- HDMI_PHYPWRCMD_TXON = 2
-};
-
-enum hdmi_pll_pwr {
- HDMI_PLLPWRCMD_ALLOFF = 0,
- HDMI_PLLPWRCMD_PLLONLY = 1,
- HDMI_PLLPWRCMD_BOTHON_ALLCLKS = 2,
- HDMI_PLLPWRCMD_BOTHON_NOPHYCLK = 3
-};
-
-enum hdmi_clk_refsel {
- HDMI_REFSEL_PCLK = 0,
- HDMI_REFSEL_REF1 = 1,
- HDMI_REFSEL_REF2 = 2,
- HDMI_REFSEL_SYSCLK = 3
-};
-
-enum hdmi_core_inputbus_width {
- HDMI_INPUT_8BIT = 0,
- HDMI_INPUT_10BIT = 1,
- HDMI_INPUT_12BIT = 2
-};
-
-enum hdmi_core_dither_trunc {
- HDMI_OUTPUTTRUNCATION_8BIT = 0,
- HDMI_OUTPUTTRUNCATION_10BIT = 1,
- HDMI_OUTPUTTRUNCATION_12BIT = 2,
- HDMI_OUTPUTDITHER_8BIT = 3,
- HDMI_OUTPUTDITHER_10BIT = 4,
- HDMI_OUTPUTDITHER_12BIT = 5
-};
-
-enum hdmi_core_deepcolor_ed {
- HDMI_DEEPCOLORPACKECTDISABLE = 0,
- HDMI_DEEPCOLORPACKECTENABLE = 1
-};
-
-enum hdmi_core_packet_mode {
- HDMI_PACKETMODERESERVEDVALUE = 0,
- HDMI_PACKETMODE24BITPERPIXEL = 4,
- HDMI_PACKETMODE30BITPERPIXEL = 5,
- HDMI_PACKETMODE36BITPERPIXEL = 6,
- HDMI_PACKETMODE48BITPERPIXEL = 7
-};
-
-enum hdmi_core_hdmi_dvi {
- HDMI_DVI = 0,
- HDMI_HDMI = 1
-};
-
-enum hdmi_core_tclkselclkmult {
- HDMI_FPLL05IDCK = 0,
- HDMI_FPLL10IDCK = 1,
- HDMI_FPLL20IDCK = 2,
- HDMI_FPLL40IDCK = 3
-};
-
-enum hdmi_core_packet_ctrl {
- HDMI_PACKETENABLE = 1,
- HDMI_PACKETDISABLE = 0,
- HDMI_PACKETREPEATON = 1,
- HDMI_PACKETREPEATOFF = 0
-};
-
-/* INFOFRAME_AVI_ and INFOFRAME_AUDIO_ definitions */
-enum hdmi_core_infoframe {
- HDMI_INFOFRAME_AVI_DB1Y_RGB = 0,
- HDMI_INFOFRAME_AVI_DB1Y_YUV422 = 1,
- HDMI_INFOFRAME_AVI_DB1Y_YUV444 = 2,
- HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF = 0,
- HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_ON = 1,
- HDMI_INFOFRAME_AVI_DB1B_NO = 0,
- HDMI_INFOFRAME_AVI_DB1B_VERT = 1,
- HDMI_INFOFRAME_AVI_DB1B_HORI = 2,
- HDMI_INFOFRAME_AVI_DB1B_VERTHORI = 3,
- HDMI_INFOFRAME_AVI_DB1S_0 = 0,
- HDMI_INFOFRAME_AVI_DB1S_1 = 1,
- HDMI_INFOFRAME_AVI_DB1S_2 = 2,
- HDMI_INFOFRAME_AVI_DB2C_NO = 0,
- HDMI_INFOFRAME_AVI_DB2C_ITU601 = 1,
- HDMI_INFOFRAME_AVI_DB2C_ITU709 = 2,
- HDMI_INFOFRAME_AVI_DB2C_EC_EXTENDED = 3,
- HDMI_INFOFRAME_AVI_DB2M_NO = 0,
- HDMI_INFOFRAME_AVI_DB2M_43 = 1,
- HDMI_INFOFRAME_AVI_DB2M_169 = 2,
- HDMI_INFOFRAME_AVI_DB2R_SAME = 8,
- HDMI_INFOFRAME_AVI_DB2R_43 = 9,
- HDMI_INFOFRAME_AVI_DB2R_169 = 10,
- HDMI_INFOFRAME_AVI_DB2R_149 = 11,
- HDMI_INFOFRAME_AVI_DB3ITC_NO = 0,
- HDMI_INFOFRAME_AVI_DB3ITC_YES = 1,
- HDMI_INFOFRAME_AVI_DB3EC_XVYUV601 = 0,
- HDMI_INFOFRAME_AVI_DB3EC_XVYUV709 = 1,
- HDMI_INFOFRAME_AVI_DB3Q_DEFAULT = 0,
- HDMI_INFOFRAME_AVI_DB3Q_LR = 1,
- HDMI_INFOFRAME_AVI_DB3Q_FR = 2,
- HDMI_INFOFRAME_AVI_DB3SC_NO = 0,
- HDMI_INFOFRAME_AVI_DB3SC_HORI = 1,
- HDMI_INFOFRAME_AVI_DB3SC_VERT = 2,
- HDMI_INFOFRAME_AVI_DB3SC_HORIVERT = 3,
- HDMI_INFOFRAME_AVI_DB5PR_NO = 0,
- HDMI_INFOFRAME_AVI_DB5PR_2 = 1,
- HDMI_INFOFRAME_AVI_DB5PR_3 = 2,
- HDMI_INFOFRAME_AVI_DB5PR_4 = 3,
- HDMI_INFOFRAME_AVI_DB5PR_5 = 4,
- HDMI_INFOFRAME_AVI_DB5PR_6 = 5,
- HDMI_INFOFRAME_AVI_DB5PR_7 = 6,
- HDMI_INFOFRAME_AVI_DB5PR_8 = 7,
- HDMI_INFOFRAME_AVI_DB5PR_9 = 8,
- HDMI_INFOFRAME_AVI_DB5PR_10 = 9,
- HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM = 0,
- HDMI_INFOFRAME_AUDIO_DB1CT_IEC60958 = 1,
- HDMI_INFOFRAME_AUDIO_DB1CT_AC3 = 2,
- HDMI_INFOFRAME_AUDIO_DB1CT_MPEG1 = 3,
- HDMI_INFOFRAME_AUDIO_DB1CT_MP3 = 4,
- HDMI_INFOFRAME_AUDIO_DB1CT_MPEG2_MULTICH = 5,
- HDMI_INFOFRAME_AUDIO_DB1CT_AAC = 6,
- HDMI_INFOFRAME_AUDIO_DB1CT_DTS = 7,
- HDMI_INFOFRAME_AUDIO_DB1CT_ATRAC = 8,
- HDMI_INFOFRAME_AUDIO_DB1CT_ONEBIT = 9,
- HDMI_INFOFRAME_AUDIO_DB1CT_DOLBY_DIGITAL_PLUS = 10,
- HDMI_INFOFRAME_AUDIO_DB1CT_DTS_HD = 11,
- HDMI_INFOFRAME_AUDIO_DB1CT_MAT = 12,
- HDMI_INFOFRAME_AUDIO_DB1CT_DST = 13,
- HDMI_INFOFRAME_AUDIO_DB1CT_WMA_PRO = 14,
- HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM = 0,
- HDMI_INFOFRAME_AUDIO_DB2SF_32000 = 1,
- HDMI_INFOFRAME_AUDIO_DB2SF_44100 = 2,
- HDMI_INFOFRAME_AUDIO_DB2SF_48000 = 3,
- HDMI_INFOFRAME_AUDIO_DB2SF_88200 = 4,
- HDMI_INFOFRAME_AUDIO_DB2SF_96000 = 5,
- HDMI_INFOFRAME_AUDIO_DB2SF_176400 = 6,
- HDMI_INFOFRAME_AUDIO_DB2SF_192000 = 7,
- HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM = 0,
- HDMI_INFOFRAME_AUDIO_DB2SS_16BIT = 1,
- HDMI_INFOFRAME_AUDIO_DB2SS_20BIT = 2,
- HDMI_INFOFRAME_AUDIO_DB2SS_24BIT = 3,
- HDMI_INFOFRAME_AUDIO_DB5_DM_INH_PERMITTED = 0,
- HDMI_INFOFRAME_AUDIO_DB5_DM_INH_PROHIBITED = 1
-};
-
-enum hdmi_packing_mode {
- HDMI_PACK_10b_RGB_YUV444 = 0,
- HDMI_PACK_24b_RGB_YUV444_YUV422 = 1,
- HDMI_PACK_20b_YUV422 = 2,
- HDMI_PACK_ALREADYPACKED = 7
-};
-
-enum hdmi_core_audio_sample_freq {
- HDMI_AUDIO_FS_32000 = 0x3,
- HDMI_AUDIO_FS_44100 = 0x0,
- HDMI_AUDIO_FS_48000 = 0x2,
- HDMI_AUDIO_FS_88200 = 0x8,
- HDMI_AUDIO_FS_96000 = 0xA,
- HDMI_AUDIO_FS_176400 = 0xC,
- HDMI_AUDIO_FS_192000 = 0xE,
- HDMI_AUDIO_FS_NOT_INDICATED = 0x1
-};
-
-enum hdmi_core_audio_layout {
- HDMI_AUDIO_LAYOUT_2CH = 0,
- HDMI_AUDIO_LAYOUT_8CH = 1
-};
-
-enum hdmi_core_cts_mode {
- HDMI_AUDIO_CTS_MODE_HW = 0,
- HDMI_AUDIO_CTS_MODE_SW = 1
-};
-
-enum hdmi_stereo_channels {
- HDMI_AUDIO_STEREO_NOCHANNELS = 0,
- HDMI_AUDIO_STEREO_ONECHANNEL = 1,
- HDMI_AUDIO_STEREO_TWOCHANNELS = 2,
- HDMI_AUDIO_STEREO_THREECHANNELS = 3,
- HDMI_AUDIO_STEREO_FOURCHANNELS = 4
-};
-
-enum hdmi_audio_type {
- HDMI_AUDIO_TYPE_LPCM = 0,
- HDMI_AUDIO_TYPE_IEC = 1
-};
-
-enum hdmi_audio_justify {
- HDMI_AUDIO_JUSTIFY_LEFT = 0,
- HDMI_AUDIO_JUSTIFY_RIGHT = 1
-};
-
-enum hdmi_audio_sample_order {
- HDMI_AUDIO_SAMPLE_RIGHT_FIRST = 0,
- HDMI_AUDIO_SAMPLE_LEFT_FIRST = 1
-};
-
-enum hdmi_audio_samples_perword {
- HDMI_AUDIO_ONEWORD_ONESAMPLE = 0,
- HDMI_AUDIO_ONEWORD_TWOSAMPLES = 1
-};
-
-enum hdmi_audio_sample_size {
- HDMI_AUDIO_SAMPLE_16BITS = 0,
- HDMI_AUDIO_SAMPLE_24BITS = 1
-};
-
-enum hdmi_audio_transf_mode {
- HDMI_AUDIO_TRANSF_DMA = 0,
- HDMI_AUDIO_TRANSF_IRQ = 1
-};
-
-enum hdmi_audio_blk_strt_end_sig {
- HDMI_AUDIO_BLOCK_SIG_STARTEND_ON = 0,
- HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF = 1
-};
-
-enum hdmi_audio_i2s_config {
- HDMI_AUDIO_I2S_WS_POLARITY_LOW_IS_LEFT = 0,
- HDMI_AUDIO_I2S_WS_POLARIT_YLOW_IS_RIGHT = 1,
- HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST = 0,
- HDMI_AUDIO_I2S_LSB_SHIFTED_FIRST = 1,
- HDMI_AUDIO_I2S_MAX_WORD_20BITS = 0,
- HDMI_AUDIO_I2S_MAX_WORD_24BITS = 1,
- HDMI_AUDIO_I2S_CHST_WORD_NOT_SPECIFIED = 0,
- HDMI_AUDIO_I2S_CHST_WORD_16_BITS = 1,
- HDMI_AUDIO_I2S_CHST_WORD_17_BITS = 6,
- HDMI_AUDIO_I2S_CHST_WORD_18_BITS = 2,
- HDMI_AUDIO_I2S_CHST_WORD_19_BITS = 4,
- HDMI_AUDIO_I2S_CHST_WORD_20_BITS_20MAX = 5,
- HDMI_AUDIO_I2S_CHST_WORD_20_BITS_24MAX = 1,
- HDMI_AUDIO_I2S_CHST_WORD_21_BITS = 6,
- HDMI_AUDIO_I2S_CHST_WORD_22_BITS = 2,
- HDMI_AUDIO_I2S_CHST_WORD_23_BITS = 4,
- HDMI_AUDIO_I2S_CHST_WORD_24_BITS = 5,
- HDMI_AUDIO_I2S_SCK_EDGE_FALLING = 0,
- HDMI_AUDIO_I2S_SCK_EDGE_RISING = 1,
- HDMI_AUDIO_I2S_VBIT_FOR_PCM = 0,
- HDMI_AUDIO_I2S_VBIT_FOR_COMPRESSED = 1,
- HDMI_AUDIO_I2S_INPUT_LENGTH_NA = 0,
- HDMI_AUDIO_I2S_INPUT_LENGTH_16 = 2,
- HDMI_AUDIO_I2S_INPUT_LENGTH_17 = 12,
- HDMI_AUDIO_I2S_INPUT_LENGTH_18 = 4,
- HDMI_AUDIO_I2S_INPUT_LENGTH_19 = 8,
- HDMI_AUDIO_I2S_INPUT_LENGTH_20 = 10,
- HDMI_AUDIO_I2S_INPUT_LENGTH_21 = 13,
- HDMI_AUDIO_I2S_INPUT_LENGTH_22 = 5,
- HDMI_AUDIO_I2S_INPUT_LENGTH_23 = 9,
- HDMI_AUDIO_I2S_INPUT_LENGTH_24 = 11,
- HDMI_AUDIO_I2S_FIRST_BIT_SHIFT = 0,
- HDMI_AUDIO_I2S_FIRST_BIT_NO_SHIFT = 1,
- HDMI_AUDIO_I2S_SD0_EN = 1,
- HDMI_AUDIO_I2S_SD1_EN = 1 << 1,
- HDMI_AUDIO_I2S_SD2_EN = 1 << 2,
- HDMI_AUDIO_I2S_SD3_EN = 1 << 3,
-};
-
-enum hdmi_audio_mclk_mode {
- HDMI_AUDIO_MCLK_128FS = 0,
- HDMI_AUDIO_MCLK_256FS = 1,
- HDMI_AUDIO_MCLK_384FS = 2,
- HDMI_AUDIO_MCLK_512FS = 3,
- HDMI_AUDIO_MCLK_768FS = 4,
- HDMI_AUDIO_MCLK_1024FS = 5,
- HDMI_AUDIO_MCLK_1152FS = 6,
- HDMI_AUDIO_MCLK_192FS = 7
-};
-
-struct hdmi_core_video_config {
- enum hdmi_core_inputbus_width ip_bus_width;
- enum hdmi_core_dither_trunc op_dither_truc;
- enum hdmi_core_deepcolor_ed deep_color_pkt;
- enum hdmi_core_packet_mode pkt_mode;
- enum hdmi_core_hdmi_dvi hdmi_dvi;
- enum hdmi_core_tclkselclkmult tclk_sel_clkmult;
-};
-
-/*
- * Refer to section 8.2 in HDMI 1.3 specification for
- * details about infoframe databytes
- */
-struct hdmi_core_infoframe_avi {
- u8 db1_format;
- /* Y0, Y1 rgb,yCbCr */
- u8 db1_active_info;
- /* A0 Active information Present */
- u8 db1_bar_info_dv;
- /* B0, B1 Bar info data valid */
- u8 db1_scan_info;
- /* S0, S1 scan information */
- u8 db2_colorimetry;
- /* C0, C1 colorimetry */
- u8 db2_aspect_ratio;
- /* M0, M1 Aspect ratio (4:3, 16:9) */
- u8 db2_active_fmt_ar;
- /* R0...R3 Active format aspect ratio */
- u8 db3_itc;
- /* ITC IT content. */
- u8 db3_ec;
- /* EC0, EC1, EC2 Extended colorimetry */
- u8 db3_q_range;
- /* Q1, Q0 Quantization range */
- u8 db3_nup_scaling;
- /* SC1, SC0 Non-uniform picture scaling */
- u8 db4_videocode;
- /* VIC0..6 Video format identification */
- u8 db5_pixel_repeat;
- /* PR0..PR3 Pixel repetition factor */
- u16 db6_7_line_eoftop;
- /* Line number end of top bar */
- u16 db8_9_line_sofbottom;
- /* Line number start of bottom bar */
- u16 db10_11_pixel_eofleft;
- /* Pixel number end of left bar */
- u16 db12_13_pixel_sofright;
- /* Pixel number start of right bar */
-};
-/*
- * Refer to section 8.2 in HDMI 1.3 specification for
- * details about infoframe databytes
- */
-struct hdmi_core_infoframe_audio {
- u8 db1_coding_type;
- u8 db1_channel_count;
- u8 db2_sample_freq;
- u8 db2_sample_size;
- u8 db4_channel_alloc;
- bool db5_downmix_inh;
- u8 db5_lsv; /* Level shift values for downmix */
-};
-
-struct hdmi_core_packet_enable_repeat {
- u32 audio_pkt;
- u32 audio_pkt_repeat;
- u32 avi_infoframe;
- u32 avi_infoframe_repeat;
- u32 gen_cntrl_pkt;
- u32 gen_cntrl_pkt_repeat;
- u32 generic_pkt;
- u32 generic_pkt_repeat;
-};
-
-struct hdmi_video_format {
- enum hdmi_packing_mode packing_mode;
- u32 y_res; /* Line per panel */
- u32 x_res; /* pixel per line */
-};
-
-struct hdmi_video_interface {
- int vsp; /* Vsync polarity */
- int hsp; /* Hsync polarity */
- int interlacing;
- int tm; /* Timing mode */
-};
-
-struct hdmi_cm {
- int code;
- int mode;
-};
-
-struct hdmi_config {
- struct hdmi_timings timings;
- u16 interlace;
- struct hdmi_cm cm;
-};
-
-struct hdmi_audio_format {
- enum hdmi_stereo_channels stereo_channels;
- u8 active_chnnls_msk;
- enum hdmi_audio_type type;
- enum hdmi_audio_justify justification;
- enum hdmi_audio_sample_order sample_order;
- enum hdmi_audio_samples_perword samples_per_word;
- enum hdmi_audio_sample_size sample_size;
- enum hdmi_audio_blk_strt_end_sig en_sig_blk_strt_end;
-};
-
-struct hdmi_audio_dma {
- u8 transfer_size;
- u8 block_size;
- enum hdmi_audio_transf_mode mode;
- u16 fifo_threshold;
-};
-
-struct hdmi_core_audio_i2s_config {
- u8 word_max_length;
- u8 word_length;
- u8 in_length_bits;
- u8 justification;
- u8 en_high_bitrate_aud;
- u8 sck_edge_mode;
- u8 cbit_order;
- u8 vbit;
- u8 ws_polarity;
- u8 direction;
- u8 shift;
- u8 active_sds;
-};
-
-struct hdmi_core_audio_config {
- struct hdmi_core_audio_i2s_config i2s_cfg;
- enum hdmi_core_audio_sample_freq freq_sample;
- bool fs_override;
- u32 n;
- u32 cts;
- u32 aud_par_busclk;
- enum hdmi_core_audio_layout layout;
- enum hdmi_core_cts_mode cts_mode;
- bool use_mclk;
- enum hdmi_audio_mclk_mode mclk_mode;
- bool en_acr_pkt;
- bool en_dsd_audio;
- bool en_parallel_aud_input;
- bool en_spdif;
-};
-#endif
diff --git a/drivers/video/omap2/dss/hdmi_omap4_panel.c b/drivers/video/omap2/dss/hdmi_panel.c
index 7d4f2bd..0fa5dea 100644
--- a/drivers/video/omap2/dss/hdmi_omap4_panel.c
+++ b/drivers/video/omap2/dss/hdmi_panel.c
@@ -1,5 +1,5 @@
/*
- * hdmi_omap4_panel.c
+ * hdmi_panel.c
*
* HDMI library support functions for TI OMAP4 processors.
*
@@ -25,13 +25,39 @@
#include <linux/mutex.h>
#include <linux/module.h>
#include <video/omapdss.h>
+#include <linux/switch.h>
#include "dss.h"
+#include <video/hdmi_ti_4xxx_ip.h>
+
static struct {
struct mutex hdmi_lock;
+ struct switch_dev hpd_switch;
} hdmi;
+static ssize_t hdmi_deepcolor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int r;
+ r = omapdss_hdmi_get_deepcolor();
+ return snprintf(buf, PAGE_SIZE, "%d\n", r);
+}
+
+static ssize_t hdmi_deepcolor_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long deep_color;
+ int r = kstrtoul(buf, 0, &deep_color);
+ if (r || deep_color > 2)
+ return -EINVAL;
+ omapdss_hdmi_set_deepcolor(deep_color);
+ return size;
+}
+
+static DEVICE_ATTR(deepcolor, S_IRUGO | S_IWUSR, hdmi_deepcolor_show,
+ hdmi_deepcolor_store);
static int hdmi_panel_probe(struct omap_dss_device *dssdev)
{
@@ -48,6 +74,10 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev)
dssdev->panel.timings.x_res = 640;
dssdev->panel.timings.y_res = 480;
+ /* sysfs entry to provide user space control to set deepcolor mode */
+ if (device_create_file(&dssdev->dev, &dev_attr_deepcolor))
+ DSSERR("failed to create sysfs file\n");
+
DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n",
dssdev->panel.timings.x_res,
dssdev->panel.timings.y_res);
@@ -56,7 +86,7 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev)
static void hdmi_panel_remove(struct omap_dss_device *dssdev)
{
-
+ device_remove_file(&dssdev->dev, &dev_attr_deepcolor);
}
static int hdmi_panel_enable(struct omap_dss_device *dssdev)
@@ -78,7 +108,6 @@ static int hdmi_panel_enable(struct omap_dss_device *dssdev)
}
dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
err:
mutex_unlock(&hdmi.hdmi_lock);
@@ -110,8 +139,9 @@ static int hdmi_panel_suspend(struct omap_dss_device *dssdev)
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
- omapdss_hdmi_display_disable(dssdev);
+ hdmi_panel_hpd_handler(0);
+ omapdss_hdmi_display_disable(dssdev);
err:
mutex_unlock(&hdmi.hdmi_lock);
@@ -129,18 +159,90 @@ static int hdmi_panel_resume(struct omap_dss_device *dssdev)
goto err;
}
- r = omapdss_hdmi_display_enable(dssdev);
- if (r) {
- DSSERR("failed to power on\n");
- goto err;
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+err:
+ mutex_unlock(&hdmi.hdmi_lock);
+
+ hdmi_panel_hpd_handler(hdmi_get_current_hpd());
+
+ return r;
+}
+
+enum {
+ HPD_STATE_OFF,
+ HPD_STATE_START,
+ HPD_STATE_EDID_TRYLAST = HPD_STATE_START + 5,
+};
+
+static struct hpd_worker_data {
+ struct delayed_work dwork;
+ atomic_t state;
+} hpd_work;
+static struct workqueue_struct *my_workq;
+
+static void hdmi_hotplug_detect_worker(struct work_struct *work)
+{
+ struct hpd_worker_data *d = container_of(work, typeof(*d), dwork.work);
+ struct omap_dss_device *dssdev = NULL;
+ int state = atomic_read(&d->state);
+
+ int match(struct omap_dss_device *dssdev, void *arg)
+ {
+ return sysfs_streq(dssdev->name , "hdmi");
}
+ dssdev = omap_dss_find_device(NULL, match);
- dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+ pr_err("in hpd work %d, state=%d\n", state, dssdev->state);
+ if (dssdev == NULL)
+ return;
-err:
+ mutex_lock(&hdmi.hdmi_lock);
+ if (state == HPD_STATE_OFF) {
+ switch_set_state(&hdmi.hpd_switch, 0);
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
+ mutex_unlock(&hdmi.hdmi_lock);
+ dssdev->driver->disable(dssdev);
+ mutex_lock(&hdmi.hdmi_lock);
+ }
+ goto done;
+ } else {
+ if (state == HPD_STATE_START) {
+ mutex_unlock(&hdmi.hdmi_lock);
+ dssdev->driver->enable(dssdev);
+ mutex_lock(&hdmi.hdmi_lock);
+ } else if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE ||
+ hdmi.hpd_switch.state) {
+ /* powered down after enable - skip EDID read */
+ goto done;
+ } else if (hdmi_read_edid(&dssdev->panel.timings)) {
+ /* get monspecs from edid */
+ hdmi_get_monspecs(&dssdev->panel.monspecs);
+ pr_info("panel size %d by %d\n",
+ dssdev->panel.monspecs.max_x,
+ dssdev->panel.monspecs.max_y);
+ dssdev->panel.width_in_um =
+ dssdev->panel.monspecs.max_x * 10000;
+ dssdev->panel.height_in_um =
+ dssdev->panel.monspecs.max_y * 10000;
+ switch_set_state(&hdmi.hpd_switch, 1);
+ goto done;
+ } else if (state == HPD_STATE_EDID_TRYLAST){
+ pr_info("Failed to read EDID after %d times. Giving up.", state - HPD_STATE_START);
+ goto done;
+ }
+ if (atomic_add_unless(&d->state, 1, HPD_STATE_OFF))
+ queue_delayed_work(my_workq, &d->dwork, msecs_to_jiffies(60));
+ }
+done:
mutex_unlock(&hdmi.hdmi_lock);
+}
- return r;
+int hdmi_panel_hpd_handler(int hpd)
+{
+ __cancel_delayed_work(&hpd_work.dwork);
+ atomic_set(&hpd_work.state, hpd ? HPD_STATE_START : HPD_STATE_OFF);
+ queue_delayed_work(my_workq, &hpd_work.dwork, msecs_to_jiffies(hpd ? 40 : 30));
+ return 0;
}
static void hdmi_get_timings(struct omap_dss_device *dssdev,
@@ -162,11 +264,8 @@ static void hdmi_set_timings(struct omap_dss_device *dssdev,
dssdev->panel.timings = *timings;
- if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
- /* turn the hdmi off and on to get new timings to use */
- omapdss_hdmi_display_disable(dssdev);
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
omapdss_hdmi_display_set_timing(dssdev);
- }
mutex_unlock(&hdmi.hdmi_lock);
}
@@ -190,6 +289,16 @@ err:
return r;
}
+static int hdmi_get_modedb(struct omap_dss_device *dssdev,
+ struct fb_videomode *modedb, int modedb_len)
+{
+ struct fb_monspecs *specs = &dssdev->panel.monspecs;
+ if (specs->modedb_len < modedb_len)
+ modedb_len = specs->modedb_len;
+ memcpy(modedb, specs->modedb, sizeof(*modedb) * modedb_len);
+ return modedb_len;
+}
+
static struct omap_dss_driver hdmi_driver = {
.probe = hdmi_panel_probe,
.remove = hdmi_panel_remove,
@@ -200,6 +309,8 @@ static struct omap_dss_driver hdmi_driver = {
.get_timings = hdmi_get_timings,
.set_timings = hdmi_set_timings,
.check_timings = hdmi_check_timings,
+ .get_modedb = hdmi_get_modedb,
+ .set_mode = omapdss_hdmi_display_set_mode,
.driver = {
.name = "hdmi_panel",
.owner = THIS_MODULE,
@@ -209,7 +320,11 @@ static struct omap_dss_driver hdmi_driver = {
int hdmi_panel_init(void)
{
mutex_init(&hdmi.hdmi_lock);
+ hdmi.hpd_switch.name = "hdmi";
+ switch_dev_register(&hdmi.hpd_switch);
+ my_workq = create_singlethread_workqueue("hdmi_hotplug");
+ INIT_DELAYED_WORK(&hpd_work.dwork, hdmi_hotplug_detect_worker);
omap_dss_register_driver(&hdmi_driver);
return 0;
@@ -217,6 +332,8 @@ int hdmi_panel_init(void)
void hdmi_panel_exit(void)
{
+ destroy_workqueue(my_workq);
omap_dss_unregister_driver(&hdmi_driver);
+ switch_dev_unregister(&hdmi.hpd_switch);
}
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
index 9aeea50..5888688 100644
--- a/drivers/video/omap2/dss/manager.c
+++ b/drivers/video/omap2/dss/manager.c
@@ -28,6 +28,8 @@
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>
+#include <linux/ratelimit.h>
+#include <linux/seq_file.h>
#include <video/omapdss.h>
#include <plat/cpu.h>
@@ -37,6 +39,7 @@
static int num_managers;
static struct list_head manager_list;
+static struct omap_overlay_manager *mgrs[MAX_DSS_MANAGERS];
static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf)
{
@@ -260,6 +263,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;
@@ -275,6 +282,108 @@ static ssize_t manager_alpha_blending_enabled_store(
return size;
}
+static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.cpr_enable);
+}
+
+static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr,
+ const char *buf, size_t size)
+{
+ struct omap_overlay_manager_info info;
+ int v;
+ int r;
+ bool enable;
+
+ if (!dss_has_feature(FEAT_CPR))
+ return -ENODEV;
+
+ r = kstrtoint(buf, 0, &v);
+ if (r)
+ return r;
+
+ enable = !!v;
+
+ mgr->get_manager_info(mgr, &info);
+
+ if (info.cpr_enable == enable)
+ return size;
+
+ info.cpr_enable = enable;
+
+ r = mgr->set_manager_info(mgr, &info);
+ if (r)
+ return r;
+
+ r = mgr->apply(mgr);
+ if (r)
+ return r;
+
+ return size;
+}
+
+static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr,
+ char *buf)
+{
+ struct omap_overlay_manager_info info;
+
+ mgr->get_manager_info(mgr, &info);
+
+ return snprintf(buf, PAGE_SIZE,
+ "%d %d %d %d %d %d %d %d %d\n",
+ info.cpr_coefs.rr,
+ info.cpr_coefs.rg,
+ info.cpr_coefs.rb,
+ info.cpr_coefs.gr,
+ info.cpr_coefs.gg,
+ info.cpr_coefs.gb,
+ info.cpr_coefs.br,
+ info.cpr_coefs.bg,
+ info.cpr_coefs.bb);
+}
+
+static ssize_t manager_cpr_coef_store(struct omap_overlay_manager *mgr,
+ const char *buf, size_t size)
+{
+ struct omap_overlay_manager_info info;
+ struct omap_dss_cpr_coefs coefs;
+ int r, i;
+ s16 *arr;
+
+ if (!dss_has_feature(FEAT_CPR))
+ return -ENODEV;
+
+ if (sscanf(buf, "%hd %hd %hd %hd %hd %hd %hd %hd %hd",
+ &coefs.rr, &coefs.rg, &coefs.rb,
+ &coefs.gr, &coefs.gg, &coefs.gb,
+ &coefs.br, &coefs.bg, &coefs.bb) != 9)
+ return -EINVAL;
+
+ arr = (s16[]){ coefs.rr, coefs.rg, coefs.rb,
+ coefs.gr, coefs.gg, coefs.gb,
+ coefs.br, coefs.bg, coefs.bb };
+
+ for (i = 0; i < 9; ++i) {
+ if (arr[i] < -512 || arr[i] > 511)
+ return -EINVAL;
+ }
+
+ mgr->get_manager_info(mgr, &info);
+
+ info.cpr_coefs = coefs;
+
+ r = mgr->set_manager_info(mgr, &info);
+ if (r)
+ return r;
+
+ r = mgr->apply(mgr);
+ if (r)
+ return r;
+
+ return size;
+}
+
struct manager_attribute {
struct attribute attr;
ssize_t (*show)(struct omap_overlay_manager *, char *);
@@ -300,6 +409,12 @@ static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR,
static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR,
manager_alpha_blending_enabled_show,
manager_alpha_blending_enabled_store);
+static MANAGER_ATTR(cpr_enable, S_IRUGO|S_IWUSR,
+ manager_cpr_enable_show,
+ manager_cpr_enable_store);
+static MANAGER_ATTR(cpr_coef, S_IRUGO|S_IWUSR,
+ manager_cpr_coef_show,
+ manager_cpr_coef_store);
static struct attribute *manager_sysfs_attrs[] = {
@@ -310,6 +425,8 @@ static struct attribute *manager_sysfs_attrs[] = {
&manager_attr_trans_key_value.attr,
&manager_attr_trans_key_enabled.attr,
&manager_attr_alpha_blending_enabled.attr,
+ &manager_attr_cpr_enable.attr,
+ &manager_attr_cpr_coef.attr,
NULL
};
@@ -353,6 +470,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 +540,21 @@ 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 omap_dss_cconv_coefs cconv;
};
struct manager_cache_data {
@@ -447,6 +584,12 @@ 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 */
+
+ bool cpr_enable;
+ struct omap_dss_cpr_coefs cpr_coefs;
+ bool skip_init;
};
static struct {
@@ -455,9 +598,43 @@ static struct {
struct manager_cache_data manager_cache[MAX_DSS_MANAGERS];
bool irq_enabled;
+ u32 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)
@@ -492,6 +669,12 @@ static int omap_dss_set_device(struct omap_overlay_manager *mgr,
mgr->device = dssdev;
mgr->device_changed = true;
+ if (dssdev->type == OMAP_DISPLAY_TYPE_DSI &&
+ !(dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE))
+ omap_dispc_set_irq_type(mgr->id, OMAP_DISPC_IRQ_TYPE_VSYNC);
+ else
+ omap_dispc_set_irq_type(mgr->id, OMAP_DISPC_IRQ_TYPE_FRAMEDONE);
+
return 0;
}
@@ -513,6 +696,7 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
{
unsigned long timeout = msecs_to_jiffies(500);
u32 irq;
+ int r;
if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) {
irq = DISPC_IRQ_EVSYNC_ODD;
@@ -524,7 +708,11 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
else
irq = DISPC_IRQ_VSYNC2;
}
- return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
+ r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
+ if (!r)
+ mgr->device->first_vsync = true;
+
+ return r;
}
static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
@@ -541,7 +729,8 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
|| dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
- irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
+ irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN
+ | DISPC_IRQ_FRAMEDONETV;
} else {
if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
enum omap_dss_update_mode mode;
@@ -588,6 +777,8 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
}
r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
+ if (!r)
+ mgr->device->first_vsync = true;
if (r == -ERESTARTSYS)
break;
@@ -619,7 +810,8 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
if (dssdev->type == OMAP_DISPLAY_TYPE_VENC
|| dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
- irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
+ irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN
+ | DISPC_IRQ_FRAMEDONETV;
} else {
if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
enum omap_dss_update_mode mode;
@@ -737,6 +929,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);
@@ -779,11 +973,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:
@@ -849,14 +1049,20 @@ 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->rotation_type,
+ 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,
@@ -875,7 +1081,12 @@ static int configure_overlay(enum omap_plane plane)
dispc_enable_replication(plane, c->replication);
dispc_set_burst_size(plane, c->burst_size);
- dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high);
+ dispc_set_zorder(plane, c->zorder);
+ dispc_enable_zorder(plane, 1);
+ if (!cpu_is_omap44xx())
+ dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high);
+ if (plane != OMAP_DSS_GFX)
+ _dispc_setup_color_conv_coef(plane, &c->cconv);
dispc_enable_plane(plane, 1);
@@ -893,7 +1104,19 @@ 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);
+ }
+ if (dss_has_feature(FEAT_CPR)) {
+ dispc_enable_cpr(channel, c->cpr_enable);
+ dispc_set_cpr_coef(channel, &c->cpr_coefs);
+ }
}
/* configure_dispc() tries to write values from cache to shadow registers.
@@ -908,6 +1131,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;
@@ -940,6 +1164,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;
@@ -960,7 +1186,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;
@@ -976,8 +1211,12 @@ static int configure_dispc(void)
/* We don't need GO with manual update display. LCD iface will
* always be turned off after frame, and new settings will be
* taken in to use at next update */
- if (!mc->manual_upd_display)
- dispc_go(i);
+ if (!mc->manual_upd_display){
+ if(mc->skip_init)
+ mc->skip_init = false;
+ else
+ dispc_go(i);
+ }
}
if (busy)
@@ -1139,6 +1378,89 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev,
*hi = h;
}
+static void schedule_completion_irq(void);
+
+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_FRAMEDONETV | DISPC_IRQ_EVSYNC_EVEN |
+ DISPC_IRQ_EVSYNC_ODD
+ };
+ int i;
+
+ spin_lock(&dss_cache.lock);
+
+ for (i = 0; i < num_mgrs; i++) {
+ mc = &dss_cache.manager_cache[i];
+ if (mask & masks[i]) {
+ if (mgrs[i] && mgrs[i]->device)
+ mgrs[i]->device->first_vsync = true;
+ 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 (mask & masks[oc->channel]) {
+ dss_ovl_cb(&oc->cb.dispc, i, DSS_COMPLETION_DISPLAYED);
+ oc->cb.dispc_displayed = true;
+ }
+ }
+
+ schedule_completion_irq();
+
+ spin_unlock(&dss_cache.lock);
+}
+
+static void schedule_completion_irq(void)
+{
+ 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_FRAMEDONETV | DISPC_IRQ_EVSYNC_EVEN |
+ DISPC_IRQ_EVSYNC_ODD
+ };
+ u32 mask = 0;
+ int i;
+
+ for (i = 0; i < num_mgrs; i++) {
+ mc = &dss_cache.manager_cache[i];
+ if (mc->cb.dispc.fn &&
+ (mc->cb.dispc.mask & DSS_COMPLETION_DISPLAYED))
+ mask |= masks[i];
+ }
+
+ /* notify all overlays on that manager */
+ for (i = 0; i < num_ovls; i++) {
+ oc = &dss_cache.overlay_cache[i];
+ if (oc->cb.dispc.fn && oc->enabled &&
+ (oc->cb.dispc.mask & DSS_COMPLETION_DISPLAYED))
+ mask |= masks[oc->channel];
+ }
+
+ if (mask != dss_cache.comp_irq_enabled) {
+ if (dss_cache.comp_irq_enabled)
+ omap_dispc_unregister_isr(dss_completion_irq_handler,
+ NULL, dss_cache.comp_irq_enabled);
+ if (mask)
+ omap_dispc_register_isr(dss_completion_irq_handler,
+ NULL, mask);
+ dss_cache.comp_irq_enabled = mask;
+ }
+}
+
void dss_start_update(struct omap_dss_device *dssdev)
{
struct manager_cache_data *mc;
@@ -1147,14 +1469,20 @@ 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;
+ unsigned long flags;
mgr = dssdev->manager;
+ spin_lock_irqsave(&dss_cache.lock, flags);
for (i = 0; i < num_ovls; ++i) {
oc = &dss_cache.overlay_cache[i];
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;
}
@@ -1163,9 +1491,14 @@ 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;
}
+ schedule_completion_irq();
+ spin_unlock_irqrestore(&dss_cache.lock, flags);
+
dssdev->manager->enable(dssdev->manager);
}
@@ -1179,30 +1512,52 @@ static void dss_apply_irq_handler(void *data, u32 mask)
bool mgr_busy[MAX_DSS_MANAGERS];
u32 irq_mask;
+ spin_lock(&dss_cache.lock);
+
for (i = 0; i < num_mgrs; i++)
mgr_busy[i] = dispc_go_busy(i);
- spin_lock(&dss_cache.lock);
-
for (i = 0; i < num_ovls; ++i) {
oc = &dss_cache.overlay_cache[i];
- if (!mgr_busy[oc->channel])
+ 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) {
+ if (mgrs[i] && mgrs[i]->device)
+ mgrs[i]->device->first_vsync = true;
+ dss_ovl_program_cb(&mc->cb, i);
mc->shadow_dirty = false;
+ }
}
+ schedule_completion_irq();
+
r = configure_dispc();
if (r == 1)
goto end;
+ /*
+ * FIXME Sometimes when handling an interrupt for a manager, the
+ * manager is still busy at the beginning of the interrupt handler.
+ * Later it becomes idle, so we unregister the interrupt. This
+ * leaves the shadow_dirty flag in an incorrect true state, and also
+ * misses the 'programmed' callback.
+ *
+ * For now, we do not unregister the interrupt if any manager
+ * was busy at the first read of the GO bits. A better fix would be
+ * to keep the first read busy state in the cache, so we do not operate
+ * on instantaneous reads of the GO bit.
+ */
+
/* re-read busy flags */
for (i = 0; i < num_mgrs; i++)
- mgr_busy[i] = dispc_go_busy(i);
+ mgr_busy[i] |= dispc_go_busy(i);
/* keep running as long as there are busy managers, so that
* we can collect overlay-applied information */
@@ -1223,10 +1578,133 @@ end:
spin_unlock(&dss_cache.lock);
}
+static int omap_dss_mgr_blank(struct omap_overlay_manager *mgr,
+ bool wait_for_go)
+{
+ struct overlay_cache_data *oc;
+ struct manager_cache_data *mc;
+ unsigned long flags;
+ int r, r_get, i;
+
+ DSSDBG("omap_dss_mgr_blank(%s,wait=%d)\n", mgr->name, wait_for_go);
+
+ r_get = r = dispc_runtime_get();
+ /* still clear cache even if failed to get clocks, just don't config */
+
+ spin_lock_irqsave(&dss_cache.lock, flags);
+
+ /* disable overlays in overlay info structs and in cache */
+ for (i = 0; i < omap_dss_get_num_overlays(); i++) {
+ struct omap_overlay_info oi = { .enabled = false };
+ struct omap_overlay *ovl;
+
+ ovl = omap_dss_get_overlay(i);
+
+ if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC) ||
+ ovl->manager != mgr)
+ continue;
+
+ oc = &dss_cache.overlay_cache[ovl->id];
+
+ /* complete unconfigured info in cache */
+ dss_ovl_cb(&oc->cb.cache, i, DSS_COMPLETION_ECLIPSED_CACHE);
+ oc->cb.cache.fn = NULL;
+
+ ovl->info = oi;
+ ovl->info_dirty = false;
+ oc->dirty = true;
+ oc->enabled = false;
+ }
+
+ /* dirty manager */
+ mc = &dss_cache.manager_cache[mgr->id];
+ dss_ovl_cb(&mc->cb.cache, i, DSS_COMPLETION_ECLIPSED_CACHE);
+ mc->cb.cache.fn = NULL;
+ mgr->info.cb.fn = NULL;
+ mc->dirty = true;
+ mgr->info_dirty = false;
+
+ /*
+ * TRICKY: Enable apply irq even if not waiting for vsync, so that
+ * DISPC programming takes place in case GO bit was on.
+ */
+ if (!dss_cache.irq_enabled) {
+ u32 mask;
+
+ mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD |
+ DISPC_IRQ_EVSYNC_EVEN;
+ if (dss_has_feature(FEAT_MGR_LCD2))
+ mask |= DISPC_IRQ_VSYNC2;
+
+ r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
+ dss_cache.irq_enabled = true;
+ }
+
+ if (!r_get) {
+ r = configure_dispc();
+ if (r)
+ pr_info("mgr_blank while GO is set");
+ }
+
+ if (r_get || !wait_for_go) {
+ /* pretend that programming has happened */
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
+ oc = &dss_cache.overlay_cache[i];
+ if (oc->channel != mgr->id)
+ continue;
+ if (r && oc->dirty)
+ dss_ovl_configure_cb(&oc->cb, i, false);
+ if (oc->shadow_dirty) {
+ dss_ovl_program_cb(&oc->cb, i);
+ oc->dispc_channel = oc->channel;
+ oc->shadow_dirty = false;
+ } else {
+ pr_warn("ovl%d-shadow is not dirty\n", i);
+ }
+ }
+
+ if (r && mc->dirty)
+ dss_ovl_configure_cb(&mc->cb, i, false);
+ if (mc->shadow_dirty) {
+ dss_ovl_program_cb(&mc->cb, i);
+ mc->shadow_dirty = false;
+ } else {
+ pr_warn("mgr%d-shadow is not dirty\n", mgr->id);
+ }
+ }
+
+ spin_unlock_irqrestore(&dss_cache.lock, flags);
+
+ if (wait_for_go)
+ mgr->wait_for_go(mgr);
+
+ if (!r_get)
+ dispc_runtime_put();
+
+ return r;
+}
+
+int omap_dss_manager_unregister_callback(struct omap_overlay_manager *mgr,
+ struct omapdss_ovl_cb *cb)
+{
+ unsigned long flags;
+ int r = 0;
+ spin_lock_irqsave(&dss_cache.lock, flags);
+ if (mgr->info_dirty &&
+ mgr->info.cb.fn == cb->fn &&
+ mgr->info.cb.data == cb->data)
+ mgr->info.cb.fn = NULL;
+ else
+ r = -EPERM;
+ spin_unlock_irqrestore(&dss_cache.lock, flags);
+ return r;
+}
+
static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
{
struct overlay_cache_data *oc;
struct manager_cache_data *mc;
+ struct omap_dss_device *dssdev;
int i;
struct omap_overlay *ovl;
int num_planes_enabled = 0;
@@ -1236,8 +1714,19 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
+ r = dispc_runtime_get();
+ if (r)
+ return r;
+
spin_lock_irqsave(&dss_cache.lock, flags);
+ if (!mgr->device || mgr->device->state != OMAP_DSS_DISPLAY_ACTIVE) {
+ pr_info_ratelimited("cannot apply mgr(%s) on inactive device\n",
+ mgr->name);
+ r = -ENODEV;
+ goto done;
+ }
+
/* Configure overlays */
for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
struct omap_dss_device *dssdev;
@@ -1247,34 +1736,39 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC))
continue;
- oc = &dss_cache.overlay_cache[ovl->id];
-
- if (!overlay_enabled(ovl)) {
- if (oc->enabled) {
- oc->enabled = false;
- oc->dirty = true;
- }
+ if (ovl->manager != mgr)
continue;
- }
- if (!ovl->info_dirty) {
+ oc = &dss_cache.overlay_cache[ovl->id];
+ dssdev = mgr->device;
+
+ if (!overlay_enabled(ovl) || !dssdev) {
+ ovl->info.enabled = false;
+ } else if (!ovl->info_dirty) {
if (oc->enabled)
++num_planes_enabled;
continue;
+ } else if (dss_check_overlay(ovl, dssdev)) {
+ ovl->info.enabled = false;
}
- dssdev = ovl->manager->device;
-
- if (dss_check_overlay(ovl, dssdev)) {
- if (oc->enabled) {
- oc->enabled = false;
- oc->dirty = true;
- }
- 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;
+ if (ovl->info.enabled || oc->enabled)
+ oc->dirty = true;
+ oc->enabled = ovl->info.enabled;
+ if (!oc->enabled)
+ continue;
oc->paddr = ovl->info.paddr;
oc->vaddr = ovl->info.vaddr;
@@ -1292,15 +1786,19 @@ 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->cconv = ovl->info.cconv;
oc->replication =
dss_use_replication(dssdev, ovl->info.color_mode);
oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC;
- oc->channel = ovl->manager->id;
-
- oc->enabled = true;
+ oc->channel = mgr->id;
oc->manual_update =
dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
@@ -1310,45 +1808,58 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
++num_planes_enabled;
}
- /* Configure managers */
- list_for_each_entry(mgr, &manager_list, list) {
- struct omap_dss_device *dssdev;
+ /* configure manager */
+ if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC))
+ goto skip_mgr;
- if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC))
- continue;
+ mc = &dss_cache.manager_cache[mgr->id];
- mc = &dss_cache.manager_cache[mgr->id];
+ if (mgr->device_changed) {
+ mgr->device_changed = false;
+ mgr->info_dirty = true;
+ }
- if (mgr->device_changed) {
- mgr->device_changed = false;
- mgr->info_dirty = true;
- }
+ if (!mgr->info_dirty)
+ goto skip_mgr;
- if (!mgr->info_dirty)
- continue;
+ if (!mgr->device)
+ goto skip_mgr;
- if (!mgr->device)
- continue;
+ dssdev = mgr->device;
- 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;
+ mgr->info_dirty = false;
+ mc->dirty = true;
- mc->default_color = mgr->info.default_color;
- mc->trans_key_type = mgr->info.trans_key_type;
- mc->trans_key = mgr->info.trans_key;
- mc->trans_enabled = mgr->info.trans_enabled;
- mc->alpha_enabled = mgr->info.alpha_enabled;
+ mc->default_color = mgr->info.default_color;
+ mc->trans_key_type = mgr->info.trans_key_type;
+ mc->trans_key = mgr->info.trans_key;
+ mc->trans_enabled = mgr->info.trans_enabled;
+ mc->alpha_enabled = mgr->info.alpha_enabled;
+ mc->cpr_coefs = mgr->info.cpr_coefs;
+ mc->cpr_enable = mgr->info.cpr_enable;
- mc->manual_upd_display =
- dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+ mc->manual_upd_display =
+ dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
- mc->manual_update =
- dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
- dssdev->driver->get_update_mode(dssdev) !=
- OMAP_DSS_UPDATE_AUTO;
- }
+ mc->manual_update =
+ dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
+ dssdev->driver->get_update_mode(dssdev) !=
+ OMAP_DSS_UPDATE_AUTO;
+
+ mc->skip_init = dssdev->skip_init;
+
+skip_mgr:
/* XXX TODO: Try to get fifomerge working. The problem is that it
* affects both managers, not individually but at the same time. This
@@ -1381,6 +1892,8 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
continue;
dssdev = ovl->manager->device;
+ if (!dssdev)
+ continue;
size = dispc_get_plane_fifo_size(ovl->id);
if (use_fifomerge)
@@ -1409,7 +1922,6 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
}
r = 0;
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
if (!dss_cache.irq_enabled) {
u32 mask;
@@ -1422,41 +1934,139 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
dss_cache.irq_enabled = true;
}
configure_dispc();
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+done:
spin_unlock_irqrestore(&dss_cache.lock, flags);
+ dispc_runtime_put();
+
return r;
}
-static int dss_check_manager(struct omap_overlay_manager *mgr)
+#ifdef CONFIG_DEBUG_FS
+static void seq_print_cb(struct seq_file *s, struct omapdss_ovl_cb *cb)
{
- /* OMAP supports only graphics source transparency color key and alpha
- * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */
+ if (!cb->fn) {
+ seq_printf(s, "(none)\n");
+ return;
+ }
+
+ seq_printf(s, "mask=%c%c%c%c [%p] %pf\n",
+ (cb->mask & DSS_COMPLETION_CHANGED) ? 'C' : '-',
+ (cb->mask & DSS_COMPLETION_PROGRAMMED) ? 'P' : '-',
+ (cb->mask & DSS_COMPLETION_DISPLAYED) ? 'D' : '-',
+ (cb->mask & DSS_COMPLETION_RELEASED) ? 'R' : '-',
+ cb->data,
+ cb->fn);
+}
+#endif
+
+static void seq_print_cbs(struct omap_overlay_manager *mgr, struct seq_file *s)
+{
+#ifdef CONFIG_DEBUG_FS
+ struct manager_cache_data *mc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dss_cache.lock, flags);
+
+ mc = &dss_cache.manager_cache[mgr->id];
+
+ seq_printf(s, " DISPC pipeline:\n\n"
+ " info:%13s ", mgr->info_dirty ? "DIRTY" : "clean");
+ seq_print_cb(s, &mgr->info.cb);
+ seq_printf(s, " cache:%12s ", mc->dirty ? "DIRTY" : "clean");
+ seq_print_cb(s, &mc->cb.cache);
+ seq_printf(s, " shadow: %s %s ",
+ mc->cb.shadow_enabled ? "ACT" : "off",
+ mc->shadow_dirty ? "DIRTY" : "clean");
+ seq_print_cb(s, &mc->cb.shadow);
+ seq_printf(s, " dispc:%12s ",
+ mc->cb.dispc_displayed ? "DISPLAYED" : "");
+ seq_print_cb(s, &mc->cb.dispc);
+ seq_printf(s, "\n");
+
+ spin_unlock_irqrestore(&dss_cache.lock, flags);
+#endif
+}
- if (mgr->info.alpha_enabled && mgr->info.trans_enabled &&
+static int dss_check_manager(struct omap_overlay_manager *mgr)
+{
+ /* 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;
+}
+
+int omap_dss_ovl_set_info(struct omap_overlay *ovl,
+ struct omap_overlay_info *info)
+{
+ int r;
+ struct omap_overlay_info old_info;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dss_cache.lock, flags);
+ old_info = ovl->info;
+ ovl->info = *info;
+
+ if (ovl->manager) {
+ r = dss_check_overlay(ovl, ovl->manager->device);
+ if (r) {
+ ovl->info = old_info;
+ spin_unlock_irqrestore(&dss_cache.lock, flags);
+ return r;
+ }
+ }
+
+ /* 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;
+ spin_unlock_irqrestore(&dss_cache.lock, flags);
return 0;
}
+
static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info)
{
int r;
struct omap_overlay_manager_info old_info;
+ unsigned long flags;
+ spin_lock_irqsave(&dss_cache.lock, flags);
old_info = mgr->info;
mgr->info = *info;
r = dss_check_manager(mgr);
if (r) {
mgr->info = old_info;
+ spin_unlock_irqrestore(&dss_cache.lock, flags);
return r;
}
+ if (mgr->info_dirty)
+ dss_ovl_cb(&old_info.cb, mgr->id, DSS_COMPLETION_ECLIPSED_SET);
+
mgr->info_dirty = true;
+ spin_unlock_irqrestore(&dss_cache.lock, flags);
return 0;
}
@@ -1469,13 +2079,13 @@ static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr,
static int dss_mgr_enable(struct omap_overlay_manager *mgr)
{
- dispc_enable_channel(mgr->id, 1);
+ dispc_enable_channel(mgr->id, mgr->device->type, 1);
return 0;
}
static int dss_mgr_disable(struct omap_overlay_manager *mgr)
{
- dispc_enable_channel(mgr->id, 0);
+ dispc_enable_channel(mgr->id, mgr->device->type, 0);
return 0;
}
@@ -1483,6 +2093,8 @@ static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager)
{
++num_managers;
list_add_tail(&manager->list, &manager_list);
+ if (manager->id < ARRAY_SIZE(mgrs))
+ mgrs[manager->id] = manager;
}
int dss_init_overlay_managers(struct platform_device *pdev)
@@ -1501,6 +2113,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";
@@ -1523,6 +2139,8 @@ int dss_init_overlay_managers(struct platform_device *pdev)
mgr->get_manager_info = &omap_dss_mgr_get_info;
mgr->wait_for_go = &dss_mgr_wait_for_go;
mgr->wait_for_vsync = &dss_mgr_wait_for_vsync;
+ mgr->blank = &omap_dss_mgr_blank;
+ mgr->dump_cb = &seq_print_cbs;
mgr->enable = &dss_mgr_enable;
mgr->disable = &dss_mgr_disable;
diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c
index 0f08025..e9d31c2 100644
--- a/drivers/video/omap2/dss/overlay.c
+++ b/drivers/video/omap2/dss/overlay.c
@@ -84,32 +84,42 @@ static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf,
old_mgr = ovl->manager;
+ r = dispc_runtime_get();
+ if (r)
+ return r;
+
/* detach old manager */
if (old_mgr) {
r = ovl->unset_manager(ovl);
if (r) {
DSSERR("detach failed\n");
- return r;
+ goto err;
}
r = old_mgr->apply(old_mgr);
if (r)
- return r;
+ goto err;
}
if (mgr) {
r = ovl->set_manager(ovl, mgr);
if (r) {
DSSERR("Failed to attach overlay\n");
- return r;
+ goto err;
}
r = mgr->apply(mgr);
if (r)
- return r;
+ goto err;
}
+ dispc_runtime_put();
+
return size;
+
+err:
+ dispc_runtime_put();
+ return r;
}
static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf)
@@ -238,6 +248,9 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl,
u8 alpha;
struct omap_overlay_info info;
+ if (!dss_has_feature(FEAT_GLOBAL_ALPHA))
+ return -ENODEV;
+
r = kstrtou8(buf, 0, &alpha);
if (r)
return r;
@@ -308,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 *);
@@ -334,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,
@@ -345,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
};
@@ -449,28 +583,12 @@ int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev)
return -EINVAL;
}
- return 0;
-}
-
-static int dss_ovl_set_overlay_info(struct omap_overlay *ovl,
- struct omap_overlay_info *info)
-{
- int r;
- struct omap_overlay_info old_info;
-
- old_info = ovl->info;
- ovl->info = *info;
-
- if (ovl->manager) {
- r = dss_check_overlay(ovl, ovl->manager->device);
- if (r) {
- ovl->info = old_info;
- return r;
- }
+ 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;
}
- ovl->info_dirty = true;
-
return 0;
}
@@ -504,7 +622,6 @@ static int omap_dss_set_manager(struct omap_overlay *ovl,
ovl->manager = mgr;
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
/* XXX: When there is an overlay on a DSI manual update display, and
* the overlay is first disabled, then moved to tv, and enabled, we
* seem to get SYNC_LOST_DIGIT error.
@@ -518,7 +635,6 @@ static int omap_dss_set_manager(struct omap_overlay *ovl,
* the overlay, but before moving the overlay to TV.
*/
dispc_set_channel_out(ovl->id, mgr->id);
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
return 0;
}
@@ -592,6 +708,9 @@ void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr)
void dss_init_overlays(struct platform_device *pdev)
{
int i, r;
+ const struct omap_dss_cconv_coefs ctbl_bt601_5 = {
+ 298, 409, 0, 298, -208, -100, 298, 0, 517, 0,
+ };
INIT_LIST_HEAD(&overlay_list);
@@ -609,6 +728,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";
@@ -616,6 +736,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";
@@ -623,12 +746,31 @@ 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->info.cconv = ctbl_bt601_5;
+
ovl->set_manager = &omap_dss_set_manager;
ovl->unset_manager = &omap_dss_unset_manager;
- ovl->set_overlay_info = &dss_ovl_set_overlay_info;
+ ovl->set_overlay_info = &omap_dss_ovl_set_info;
ovl->get_overlay_info = &dss_ovl_get_overlay_info;
ovl->wait_for_go = &dss_ovl_wait_for_go;
@@ -718,16 +860,18 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)
}
}
- if (mgr) {
+ if (mgr && force) {
+ dispc_runtime_get();
+
for (i = 0; i < dss_feat_get_num_ovls(); i++) {
struct omap_overlay *ovl;
ovl = omap_dss_get_overlay(i);
- if (!ovl->manager || force) {
- if (ovl->manager)
- omap_dss_unset_manager(ovl);
- omap_dss_set_manager(ovl, mgr);
- }
+ if (ovl->manager)
+ omap_dss_unset_manager(ovl);
+ omap_dss_set_manager(ovl, mgr);
}
+
+ dispc_runtime_put();
}
}
diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c
index c06fbe0..3e2f5cd 100644
--- a/drivers/video/omap2/dss/rfbi.c
+++ b/drivers/video/omap2/dss/rfbi.c
@@ -33,6 +33,8 @@
#include <linux/hrtimer.h>
#include <linux/seq_file.h>
#include <linux/semaphore.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <video/omapdss.h>
#include "dss.h"
@@ -120,12 +122,46 @@ static inline u32 rfbi_read_reg(const struct rfbi_reg idx)
return __raw_readl(rfbi.base + idx.idx);
}
-static void rfbi_enable_clocks(bool enable)
+static int rfbi_runtime_get(void)
{
- if (enable)
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
- else
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ int r;
+
+ DSSDBG("rfbi_runtime_get\n");
+
+ r = dss_runtime_get();
+ if (r)
+ goto err_get_dss;
+
+ r = dispc_runtime_get();
+ if (r)
+ goto err_get_dispc;
+
+ r = pm_runtime_get_sync(&rfbi.pdev->dev);
+ WARN_ON(r);
+ if (r < 0)
+ goto err_runtime_get;
+
+ return 0;
+
+err_runtime_get:
+ dispc_runtime_put();
+err_get_dispc:
+ dss_runtime_put();
+err_get_dss:
+ return r;
+}
+
+static void rfbi_runtime_put(void)
+{
+ int r;
+
+ DSSDBG("rfbi_runtime_put\n");
+
+ r = pm_runtime_put_sync(&rfbi.pdev->dev);
+ WARN_ON(r);
+
+ dispc_runtime_put();
+ dss_runtime_put();
}
void rfbi_bus_lock(void)
@@ -805,7 +841,8 @@ void rfbi_dump_regs(struct seq_file *s)
{
#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, rfbi_read_reg(r))
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ if (rfbi_runtime_get())
+ return;
DUMPREG(RFBI_REVISION);
DUMPREG(RFBI_SYSCONFIG);
@@ -836,7 +873,7 @@ void rfbi_dump_regs(struct seq_file *s)
DUMPREG(RFBI_VSYNC_WIDTH);
DUMPREG(RFBI_HSYNC_WIDTH);
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ rfbi_runtime_put();
#undef DUMPREG
}
@@ -844,7 +881,9 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
{
int r;
- rfbi_enable_clocks(1);
+ r = rfbi_runtime_get();
+ if (r)
+ return r;
r = omap_dss_start_device(dssdev);
if (r) {
@@ -879,6 +918,7 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
err1:
omap_dss_stop_device(dssdev);
err0:
+ rfbi_runtime_put();
return r;
}
EXPORT_SYMBOL(omapdss_rfbi_display_enable);
@@ -889,7 +929,7 @@ void omapdss_rfbi_display_disable(struct omap_dss_device *dssdev)
DISPC_IRQ_FRAMEDONE);
omap_dss_stop_device(dssdev);
- rfbi_enable_clocks(0);
+ rfbi_runtime_put();
}
EXPORT_SYMBOL(omapdss_rfbi_display_disable);
@@ -904,8 +944,9 @@ int rfbi_init_display(struct omap_dss_device *dssdev)
static int omap_rfbihw_probe(struct platform_device *pdev)
{
u32 rev;
- u32 l;
struct resource *rfbi_mem;
+ struct clk *clk;
+ int r;
rfbi.pdev = pdev;
@@ -914,36 +955,55 @@ static int omap_rfbihw_probe(struct platform_device *pdev)
rfbi_mem = platform_get_resource(rfbi.pdev, IORESOURCE_MEM, 0);
if (!rfbi_mem) {
DSSERR("can't get IORESOURCE_MEM RFBI\n");
- return -EINVAL;
+ r = -EINVAL;
+ goto err_ioremap;
}
rfbi.base = ioremap(rfbi_mem->start, resource_size(rfbi_mem));
if (!rfbi.base) {
DSSERR("can't ioremap RFBI\n");
- return -ENOMEM;
+ r = -ENOMEM;
+ goto err_ioremap;
}
- rfbi_enable_clocks(1);
+ pm_runtime_enable(&pdev->dev);
+
+ r = rfbi_runtime_get();
+ if (r)
+ goto err_get_rfbi;
msleep(10);
- rfbi.l4_khz = dss_clk_get_rate(DSS_CLK_ICK) / 1000;
+ clk = clk_get(&pdev->dev, "rfbi_iclk");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get rfbi_iclk\n");
+ r = PTR_ERR(clk);
+ goto err_get_ick;
+ }
+
+ rfbi.l4_khz = clk_get_rate(clk) / 1000;
- /* Enable autoidle and smart-idle */
- l = rfbi_read_reg(RFBI_SYSCONFIG);
- l |= (1 << 0) | (2 << 3);
- rfbi_write_reg(RFBI_SYSCONFIG, l);
+ clk_put(clk);
rev = rfbi_read_reg(RFBI_REVISION);
dev_dbg(&pdev->dev, "OMAP RFBI rev %d.%d\n",
FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
- rfbi_enable_clocks(0);
+ rfbi_runtime_put();
return 0;
+
+err_get_ick:
+ rfbi_runtime_put();
+err_get_rfbi:
+ pm_runtime_disable(&pdev->dev);
+ iounmap(rfbi.base);
+err_ioremap:
+ return r;
}
static int omap_rfbihw_remove(struct platform_device *pdev)
{
+ pm_runtime_disable(&pdev->dev);
iounmap(rfbi.base);
return 0;
}
diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c
index 0bd4b03..3a688c8 100644
--- a/drivers/video/omap2/dss/sdi.c
+++ b/drivers/video/omap2/dss/sdi.c
@@ -20,13 +20,11 @@
#define DSS_SUBSYS_NAME "SDI"
#include <linux/kernel.h>
-#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/regulator/consumer.h>
#include <video/omapdss.h>
-#include <plat/cpu.h>
#include "dss.h"
static struct {
@@ -60,14 +58,20 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");
- goto err0;
+ goto err_start_dev;
}
r = regulator_enable(sdi.vdds_sdi_reg);
if (r)
- goto err1;
+ goto err_reg_enable;
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK);
+ r = dss_runtime_get();
+ if (r)
+ goto err_get_dss;
+
+ r = dispc_runtime_get();
+ if (r)
+ goto err_get_dispc;
sdi_basic_init(dssdev);
@@ -80,7 +84,7 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
r = dss_calc_clock_div(1, t->pixel_clock * 1000,
&dss_cinfo, &dispc_cinfo);
if (r)
- goto err2;
+ goto err_calc_clock_div;
fck = dss_cinfo.fck;
lck_div = dispc_cinfo.lck_div;
@@ -101,27 +105,34 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
r = dss_set_clock_div(&dss_cinfo);
if (r)
- goto err2;
+ goto err_set_dss_clock_div;
r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo);
if (r)
- goto err2;
+ goto err_set_dispc_clock_div;
dss_sdi_init(dssdev->phy.sdi.datapairs);
r = dss_sdi_enable();
if (r)
- goto err1;
+ goto err_sdi_enable;
mdelay(2);
dssdev->manager->enable(dssdev->manager);
return 0;
-err2:
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+
+err_sdi_enable:
+err_set_dispc_clock_div:
+err_set_dss_clock_div:
+err_calc_clock_div:
+ dispc_runtime_put();
+err_get_dispc:
+ dss_runtime_put();
+err_get_dss:
regulator_disable(sdi.vdds_sdi_reg);
-err1:
+err_reg_enable:
omap_dss_stop_device(dssdev);
-err0:
+err_start_dev:
return r;
}
EXPORT_SYMBOL(omapdss_sdi_display_enable);
@@ -132,7 +143,8 @@ void omapdss_sdi_display_disable(struct omap_dss_device *dssdev)
dss_sdi_disable();
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK);
+ dispc_runtime_put();
+ dss_runtime_put();
regulator_disable(sdi.vdds_sdi_reg);
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c
index 980f919..4491983 100644
--- a/drivers/video/omap2/dss/venc.c
+++ b/drivers/video/omap2/dss/venc.c
@@ -33,11 +33,13 @@
#include <linux/seq_file.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
#include <video/omapdss.h>
#include <plat/cpu.h>
#include "dss.h"
+#include "dss_features.h"
/* Venc registers */
#define VENC_REV_ID 0x00
@@ -292,6 +294,12 @@ static struct {
struct mutex venc_lock;
u32 wss_data;
struct regulator *vdda_dac_reg;
+
+ struct mutex runtime_lock;
+ int runtime_count;
+
+ struct clk *tv_clk;
+ struct clk *tv_dac_clk;
} venc;
static inline void venc_write_reg(int idx, u32 val)
@@ -380,14 +388,71 @@ static void venc_reset(void)
#endif
}
-static void venc_enable_clocks(int enable)
+static int venc_runtime_get(void)
+{
+ int r;
+
+ mutex_lock(&venc.runtime_lock);
+
+ if (venc.runtime_count++ == 0) {
+ DSSDBG("venc_runtime_get\n");
+
+ r = dss_runtime_get();
+ if (r)
+ goto err_get_dss;
+
+ r = dispc_runtime_get();
+ if (r)
+ goto err_get_dispc;
+
+ clk_enable(venc.tv_clk);
+ if (venc.tv_dac_clk)
+ clk_enable(venc.tv_dac_clk);
+
+ r = pm_runtime_get_sync(&venc.pdev->dev);
+ WARN_ON(r);
+ if (r < 0)
+ goto err_runtime_get;
+ }
+
+ mutex_unlock(&venc.runtime_lock);
+
+ return 0;
+
+err_runtime_get:
+ clk_disable(venc.tv_clk);
+ if (venc.tv_dac_clk)
+ clk_disable(venc.tv_dac_clk);
+ dispc_runtime_put();
+err_get_dispc:
+ dss_runtime_put();
+err_get_dss:
+ mutex_unlock(&venc.runtime_lock);
+
+ return r;
+}
+
+static void venc_runtime_put(void)
{
- if (enable)
- dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_TVFCK |
- DSS_CLK_VIDFCK);
- else
- dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_TVFCK |
- DSS_CLK_VIDFCK);
+ mutex_lock(&venc.runtime_lock);
+
+ if (--venc.runtime_count == 0) {
+ int r;
+
+ DSSDBG("venc_runtime_put\n");
+
+ r = pm_runtime_put_sync(&venc.pdev->dev);
+ WARN_ON(r);
+
+ clk_disable(venc.tv_clk);
+ if (venc.tv_dac_clk)
+ clk_disable(venc.tv_dac_clk);
+
+ dispc_runtime_put();
+ dss_runtime_put();
+ }
+
+ mutex_unlock(&venc.runtime_lock);
}
static const struct venc_config *venc_timings_to_config(
@@ -406,8 +471,6 @@ static void venc_power_on(struct omap_dss_device *dssdev)
{
u32 l;
- venc_enable_clocks(1);
-
venc_reset();
venc_write_config(venc_timings_to_config(&dssdev->panel.timings));
@@ -448,8 +511,6 @@ static void venc_power_off(struct omap_dss_device *dssdev)
dssdev->platform_disable(dssdev);
regulator_disable(venc.vdda_dac_reg);
-
- venc_enable_clocks(0);
}
@@ -487,6 +548,10 @@ static int venc_panel_enable(struct omap_dss_device *dssdev)
goto err1;
}
+ r = venc_runtime_get();
+ if (r)
+ goto err1;
+
venc_power_on(dssdev);
venc.wss_data = 0;
@@ -520,6 +585,8 @@ static void venc_panel_disable(struct omap_dss_device *dssdev)
venc_power_off(dssdev);
+ venc_runtime_put();
+
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
omap_dss_stop_device(dssdev);
@@ -598,6 +665,7 @@ static u32 venc_get_wss(struct omap_dss_device *dssdev)
static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss)
{
const struct venc_config *config;
+ int r;
DSSDBG("venc_set_wss\n");
@@ -608,16 +676,19 @@ static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss)
/* Invert due to VENC_L21_WC_CTL:INV=1 */
venc.wss_data = (wss ^ 0xfffff) << 8;
- venc_enable_clocks(1);
+ r = venc_runtime_get();
+ if (r)
+ goto err;
venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data |
venc.wss_data);
- venc_enable_clocks(0);
+ venc_runtime_put();
+err:
mutex_unlock(&venc.venc_lock);
- return 0;
+ return r;
}
static struct omap_dss_driver venc_driver = {
@@ -673,7 +744,8 @@ void venc_dump_regs(struct seq_file *s)
{
#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r))
- venc_enable_clocks(1);
+ if (venc_runtime_get())
+ return;
DUMPREG(VENC_F_CONTROL);
DUMPREG(VENC_VIDOUT_CTRL);
@@ -717,16 +789,53 @@ void venc_dump_regs(struct seq_file *s)
DUMPREG(VENC_OUTPUT_CONTROL);
DUMPREG(VENC_OUTPUT_TEST);
- venc_enable_clocks(0);
+ venc_runtime_put();
#undef DUMPREG
}
+static int venc_get_clocks(struct platform_device *pdev)
+{
+ struct clk *clk;
+
+ clk = clk_get(&pdev->dev, "tv_clk");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get tv_clk\n");
+ return PTR_ERR(clk);
+ }
+
+ venc.tv_clk = clk;
+
+ if (dss_has_feature(FEAT_VENC_REQUIRES_TV_DAC_CLK)) {
+ clk = clk_get(&pdev->dev, "tv_dac_clk");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get tv_dac_clk\n");
+ clk_put(venc.tv_clk);
+ return PTR_ERR(clk);
+ }
+ } else {
+ clk = NULL;
+ }
+
+ venc.tv_dac_clk = clk;
+
+ return 0;
+}
+
+static void venc_put_clocks(void)
+{
+ if (venc.tv_clk)
+ clk_put(venc.tv_clk);
+ if (venc.tv_dac_clk)
+ clk_put(venc.tv_dac_clk);
+}
+
/* VENC HW IP initialisation */
static int omap_venchw_probe(struct platform_device *pdev)
{
u8 rev_id;
struct resource *venc_mem;
+ int r;
venc.pdev = pdev;
@@ -737,22 +846,41 @@ static int omap_venchw_probe(struct platform_device *pdev)
venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0);
if (!venc_mem) {
DSSERR("can't get IORESOURCE_MEM VENC\n");
- return -EINVAL;
+ r = -EINVAL;
+ goto err_ioremap;
}
venc.base = ioremap(venc_mem->start, resource_size(venc_mem));
if (!venc.base) {
DSSERR("can't ioremap VENC\n");
- return -ENOMEM;
+ r = -ENOMEM;
+ goto err_ioremap;
}
- venc_enable_clocks(1);
+ r = venc_get_clocks(pdev);
+ if (r)
+ goto err_get_clk;
+
+ mutex_init(&venc.runtime_lock);
+ pm_runtime_enable(&pdev->dev);
+
+ r = venc_runtime_get();
+ if (r)
+ goto err_get_venc;
rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff);
dev_dbg(&pdev->dev, "OMAP VENC rev %d\n", rev_id);
- venc_enable_clocks(0);
+ venc_runtime_put();
return omap_dss_register_driver(&venc_driver);
+
+err_get_venc:
+ pm_runtime_disable(&pdev->dev);
+ venc_put_clocks();
+err_get_clk:
+ iounmap(venc.base);
+err_ioremap:
+ return r;
}
static int omap_venchw_remove(struct platform_device *pdev)
@@ -763,6 +891,9 @@ static int omap_venchw_remove(struct platform_device *pdev)
}
omap_dss_unregister_driver(&venc_driver);
+ pm_runtime_disable(&pdev->dev);
+ venc_put_clocks();
+
iounmap(venc.base);
return 0;
}
diff --git a/drivers/video/omap2/dsscomp/Kconfig b/drivers/video/omap2/dsscomp/Kconfig
new file mode 100644
index 0000000..0c2a749
--- /dev/null
+++ b/drivers/video/omap2/dsscomp/Kconfig
@@ -0,0 +1,20 @@
+menuconfig DSSCOMP
+ tristate "OMAP DSS Composition support (EXPERIMENTAL)"
+ depends on OMAP2_DSS && PVR_SGX
+ 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.
+
+config DSSCOMP_DEBUG_LOG
+ bool "Log event timestamps in debugfs"
+ default y
+ depends on DEBUG_FS
+
+ help
+ Takes timestamp for each callback and state transition, and
+ logs the last 128 entries (last few frames' worth) in a
+ log buffer. This is a separate menuconfig in case this is
+ deemed an overhead.
diff --git a/drivers/video/omap2/dsscomp/Makefile b/drivers/video/omap2/dsscomp/Makefile
new file mode 100644
index 0000000..8a67933
--- /dev/null
+++ b/drivers/video/omap2/dsscomp/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DSSCOMP) += dsscomp.o
+dsscomp-y := device.o base.o queue.o
+dsscomp-y += gralloc.o
diff --git a/drivers/video/omap2/dsscomp/base.c b/drivers/video/omap2/dsscomp/base.c
new file mode 100644
index 0000000..ad7ade6
--- /dev/null
+++ b/drivers/video/omap2/dsscomp/base.c
@@ -0,0 +1,504 @@
+/*
+ * 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;
+}
+
+#ifdef CONFIG_DEBUG_FS
+const char *dsscomp_get_color_name(enum omap_color_mode m)
+{
+ const struct color_info *ci = get_color_info(m);
+ return ci ? ci->name : NULL;
+}
+#endif
+
+union rect {
+ struct {
+ s32 x;
+ s32 y;
+ s32 w;
+ s32 h;
+ };
+ struct {
+ s32 xy[2];
+ s32 wh[2];
+ };
+ struct dss2_rect_t r;
+};
+
+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, cfg->width, cfg->height);
+ info.paddr -= t.tsptr;
+ tilview_crop(&t, 0, crop.y, cfg->width, crop.h);
+ info.paddr += t.tsptr + bpp * crop.x;
+
+ info.rotation_type = OMAP_DSS_ROT_TILER;
+ info.screen_width = 0;
+
+ /* for NV12 format also crop NV12 */
+ if (info.color_mode == OMAP_DSS_COLOR_NV12) {
+ tilview_create(&t, info.p_uv_addr,
+ cfg->width >> 1, cfg->height >> 1);
+ info.p_uv_addr -= t.tsptr;
+ tilview_crop(&t, 0, crop.y >> 1, cfg->width >> 1,
+ crop.h >> 1);
+ info.p_uv_addr += t.tsptr + bpp * crop.x;
+ }
+ } else {
+ /* program tiler 1D as SDMA */
+
+ int bpp = color_mode_to_bpp(cfg->color_mode);
+ info.screen_width = cfg->stride * 8 / (bpp == 12 ? 8 : bpp);
+ info.paddr += crop.x * (bpp / 8) + crop.y * cfg->stride;
+
+ /* for NV12 format also crop NV12 */
+ if (info.color_mode == OMAP_DSS_COLOR_NV12)
+ info.p_uv_addr += crop.x * (bpp / 8) +
+ (crop.y >> 1) * cfg->stride;
+
+ /* no rotation on DMA buffer */
+ if (cfg->rotation & 3 || cfg->mirror)
+ return -EINVAL;
+
+ info.rotation_type = OMAP_DSS_ROT_DMA;
+ }
+
+ info.max_x_decim = cfg->decim.max_x ? : 255;
+ info.max_y_decim = cfg->decim.max_y ? : 255;
+ info.min_x_decim = cfg->decim.min_x ? : 1;
+ info.min_y_decim = cfg->decim.min_y ? : 1;
+#if 0
+ info.pic_height = cfg->height;
+
+ info.field = 0;
+ if (cfg->ilace & OMAP_DSS_ILACE_SEQ)
+ info.field |= OMAP_FLAG_IBUF;
+ if (cfg->ilace & OMAP_DSS_ILACE_SWAP)
+ info.field |= OMAP_FLAG_ISWAP;
+ /*
+ * Ignore OMAP_DSS_ILACE as there is no real support yet for
+ * interlaced interleaved vs progressive buffers
+ */
+ if (ovl->manager &&
+ ovl->manager->device &&
+ !strcmp(ovl->manager->device->name, "hdmi") &&
+ is_hdmi_interlaced())
+ info.field |= OMAP_FLAG_IDEV;
+
+ info.out_wb = 0;
+#endif
+
+ info.cconv = cfg->cconv;
+
+done:
+#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);
+}
+
+void swap_rb_in_ovl_info(struct dss2_ovl_info *oi)
+{
+ /* we need to swap YUV color matrix if we are swapping R and B */
+ if (oi->cfg.color_mode &
+ (OMAP_DSS_COLOR_NV12 | OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY)) {
+ swap(oi->cfg.cconv.ry, oi->cfg.cconv.by);
+ swap(oi->cfg.cconv.rcr, oi->cfg.cconv.bcr);
+ swap(oi->cfg.cconv.rcb, oi->cfg.cconv.bcb);
+ }
+}
+
+struct omap_overlay_manager *find_dss_mgr(int display_ix)
+{
+ struct omap_overlay_manager *mgr;
+ char name[32];
+ int i;
+
+ sprintf(name, "display%d", display_ix);
+
+ for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
+ mgr = omap_dss_get_overlay_manager(i);
+ if (mgr->device && !strcmp(name, dev_name(&mgr->device->dev)))
+ return mgr;
+ }
+ return NULL;
+}
+
+int set_dss_mgr_info(struct dss2_mgr_info *mi, struct omapdss_ovl_cb *cb)
+{
+ struct omap_overlay_manager_info info;
+ struct omap_overlay_manager *mgr;
+
+ if (!mi)
+ return -EINVAL;
+ mgr = find_dss_mgr(mi->ix);
+ if (!mgr)
+ return -EINVAL;
+
+ /* just in case there are new fields, we get the current info */
+ mgr->get_manager_info(mgr, &info);
+
+ 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;
+
+ info.cpr_coefs = mi->cpr_coefs;
+ info.cpr_enable = mi->cpr_enabled;
+ info.cb = *cb;
+
+ return mgr->set_manager_info(mgr, &info);
+}
+
+void swap_rb_in_mgr_info(struct dss2_mgr_info *mi)
+{
+ const struct omap_dss_cpr_coefs c = { 256, 0, 0, 0, 256, 0, 0, 0, 256 };
+
+ /* set default CPR */
+ if (!mi->cpr_enabled)
+ mi->cpr_coefs = c;
+ mi->cpr_enabled = true;
+
+ /* swap red and blue */
+ swap(mi->cpr_coefs.rr, mi->cpr_coefs.br);
+ swap(mi->cpr_coefs.rg, mi->cpr_coefs.bg);
+ swap(mi->cpr_coefs.rb, mi->cpr_coefs.bb);
+}
+
+/*
+ * ===========================================================================
+ * DEBUG METHODS
+ * ===========================================================================
+ */
+void dump_ovl_info(struct dsscomp_dev *cdev, struct dss2_ovl_info *oi)
+{
+ struct dss2_ovl_cfg *c = &oi->cfg;
+ const struct color_info *ci;
+
+ if (!(debug & DEBUG_OVERLAYS) ||
+ !(debug & DEBUG_COMPOSITIONS))
+ return;
+
+ ci = get_color_info(c->color_mode);
+ if (c->zonly) {
+ dev_info(DEV(cdev), "ovl%d(%s z%d)\n",
+ c->ix, c->enabled ? "ON" : "off", c->zorder);
+ return;
+ }
+ dev_info(DEV(cdev), "ovl%d(%s 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);
+}
+
+static void print_mgr_info(struct dsscomp_dev *cdev,
+ struct dss2_mgr_info *mi)
+{
+ printk("(dis%d(%s) alpha=%d col=%08x ilace=%d) ",
+ mi->ix,
+ (mi->ix < cdev->num_displays && cdev->displays[mi->ix]) ?
+ cdev->displays[mi->ix]->name : "NONE",
+ mi->alpha_blending, mi->default_color,
+ mi->interlaced);
+}
+
+void dump_comp_info(struct dsscomp_dev *cdev, struct dsscomp_setup_mgr_data *d,
+ const char *phase)
+{
+ if (!(debug & DEBUG_COMPOSITIONS))
+ return;
+
+ dev_info(DEV(cdev), "[%p] %s: %c%c%c ",
+ *phase == 'q' ? (void *) d->sync_id : d, phase,
+ (d->mode & DSSCOMP_SETUP_MODE_APPLY) ? 'A' : '-',
+ (d->mode & DSSCOMP_SETUP_MODE_DISPLAY) ? 'D' : '-',
+ (d->mode & DSSCOMP_SETUP_MODE_CAPTURE) ? 'C' : '-');
+ print_mgr_info(cdev, &d->mgr);
+ printk("n=%d\n", d->num_ovls);
+}
+
+void dump_total_comp_info(struct dsscomp_dev *cdev,
+ struct dsscomp_setup_dispc_data *d,
+ const char *phase)
+{
+ int i;
+
+ if (!(debug & DEBUG_COMPOSITIONS))
+ return;
+
+ dev_info(DEV(cdev), "[%p] %s: %c%c%c ",
+ *phase == 'q' ? (void *) d->sync_id : d, phase,
+ (d->mode & DSSCOMP_SETUP_MODE_APPLY) ? 'A' : '-',
+ (d->mode & DSSCOMP_SETUP_MODE_DISPLAY) ? 'D' : '-',
+ (d->mode & DSSCOMP_SETUP_MODE_CAPTURE) ? 'C' : '-');
+
+ for (i = 0; i < d->num_mgrs && i < ARRAY_SIZE(d->mgrs); i++)
+ print_mgr_info(cdev, d->mgrs + i);
+ printk("n=%d\n", 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..80cc21b
--- /dev/null
+++ b/drivers/video/omap2/dsscomp/device.c
@@ -0,0 +1,634 @@
+/*
+ * linux/drivers/video/omap2/dsscomp/device.c
+ *
+ * DSS Composition file device and ioctl support
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc
+ * Author: Lajos Molnar <molnar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DEBUG
+
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/anon_inodes.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+
+#define MODULE_NAME "dsscomp"
+
+#include <video/omapdss.h>
+#include <video/dsscomp.h>
+#include <plat/dsscomp.h>
+#include "dsscomp.h"
+
+#include <linux/debugfs.h>
+
+static DECLARE_WAIT_QUEUE_HEAD(waitq);
+static DEFINE_MUTEX(wait_mtx);
+
+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;
+}
+
+/*
+ * ===========================================================================
+ * WAIT OPERATIONS
+ * ===========================================================================
+ */
+
+static void sync_drop(struct dsscomp_sync_obj *sync)
+{
+ if (sync && atomic_dec_and_test(&sync->refs)) {
+ if (debug & DEBUG_WAITS)
+ pr_info("free sync [%p]\n", sync);
+
+ kfree(sync);
+ }
+}
+
+static int sync_setup(const char *name, const struct file_operations *fops,
+ struct dsscomp_sync_obj *sync, int flags)
+{
+ if (!sync)
+ return -ENOMEM;
+
+ sync->refs.counter = 1;
+ sync->fd = anon_inode_getfd(name, fops, sync, flags);
+ return sync->fd < 0 ? sync->fd : 0;
+}
+
+static int sync_finalize(struct dsscomp_sync_obj *sync, int r)
+{
+ if (sync) {
+ if (r < 0)
+ /* delete sync object on failure */
+ sys_close(sync->fd);
+ else
+ /* return file descriptor on success */
+ r = sync->fd;
+ }
+ return r;
+}
+
+/* wait for programming or release of a composition */
+int dsscomp_wait(struct dsscomp_sync_obj *sync, enum dsscomp_wait_phase phase,
+ int timeout)
+{
+ mutex_lock(&wait_mtx);
+ if (debug & DEBUG_WAITS)
+ pr_info("wait %s on [%p]\n",
+ phase == DSSCOMP_WAIT_DISPLAYED ? "display" :
+ phase == DSSCOMP_WAIT_PROGRAMMED ? "program" :
+ "release", sync);
+
+ if (sync->state < phase) {
+ mutex_unlock(&wait_mtx);
+
+ timeout = wait_event_interruptible_timeout(waitq,
+ sync->state >= phase, timeout);
+ if (debug & DEBUG_WAITS)
+ pr_info("wait over [%p]: %s %d\n", sync,
+ timeout < 0 ? "signal" :
+ timeout > 0 ? "ok" : "timeout",
+ timeout);
+ if (timeout <= 0)
+ return timeout ? : -ETIME;
+
+ mutex_lock(&wait_mtx);
+ }
+ mutex_unlock(&wait_mtx);
+
+ return 0;
+}
+EXPORT_SYMBOL(dsscomp_wait);
+
+static void dsscomp_queue_cb(void *data, int status)
+{
+ struct dsscomp_sync_obj *sync = data;
+ enum dsscomp_wait_phase phase =
+ status == DSS_COMPLETION_PROGRAMMED ? DSSCOMP_WAIT_PROGRAMMED :
+ status == DSS_COMPLETION_DISPLAYED ? DSSCOMP_WAIT_DISPLAYED :
+ DSSCOMP_WAIT_RELEASED, old_phase;
+
+ mutex_lock(&wait_mtx);
+ old_phase = sync->state;
+ if (old_phase < phase)
+ sync->state = phase;
+ mutex_unlock(&wait_mtx);
+
+ if (status & DSS_COMPLETION_RELEASED)
+ sync_drop(sync);
+ if (old_phase < phase)
+ wake_up_interruptible_sync(&waitq);
+}
+
+static int sync_release(struct inode *inode, struct file *filp)
+{
+ struct dsscomp_sync_obj *sync = filp->private_data;
+ sync_drop(sync);
+ return 0;
+}
+
+static long sync_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int r = 0;
+ struct dsscomp_sync_obj *sync = filp->private_data;
+ void __user *ptr = (void __user *)arg;
+
+ switch (cmd) {
+ case DSSCIOC_WAIT:
+ {
+ struct dsscomp_wait_data wd;
+ r = copy_from_user(&wd, ptr, sizeof(wd)) ? :
+ dsscomp_wait(sync, wd.phase,
+ usecs_to_jiffies(wd.timeout_us));
+ break;
+ }
+ default:
+ r = -EINVAL;
+ }
+ return r;
+}
+
+static const struct file_operations sync_fops = {
+ .owner = THIS_MODULE,
+ .release = sync_release,
+ .unlocked_ioctl = sync_ioctl,
+};
+
+static long setup_mgr(struct dsscomp_dev *cdev,
+ struct dsscomp_setup_mgr_data *d)
+{
+ int i, r;
+ struct omap_dss_device *dev;
+ struct omap_overlay_manager *mgr;
+ dsscomp_t comp;
+ struct dsscomp_sync_obj *sync = NULL;
+
+ dump_comp_info(cdev, d, "queue");
+ for (i = 0; i < d->num_ovls; i++)
+ dump_ovl_info(cdev, d->ovls + i);
+
+ /* verify display is valid and connected */
+ if (d->mgr.ix >= cdev->num_displays)
+ return -EINVAL;
+ dev = cdev->displays[d->mgr.ix];
+ if (!dev)
+ return -EINVAL;
+ mgr = dev->manager;
+ if (!mgr)
+ return -ENODEV;
+
+ comp = dsscomp_new(mgr);
+ if (IS_ERR(comp))
+ return PTR_ERR(comp);
+
+ /* swap red & blue if requested */
+ if (d->mgr.swap_rb) {
+ swap_rb_in_mgr_info(&d->mgr);
+ for (i = 0; i < d->num_ovls; i++)
+ swap_rb_in_ovl_info(d->ovls + i);
+ }
+
+ r = dsscomp_set_mgr(comp, &d->mgr);
+
+ for (i = 0; i < d->num_ovls; i++) {
+ struct dss2_ovl_info *oi = d->ovls + i;
+ u32 addr = (u32) oi->address;
+
+ /* 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);
+
+ /* create sync object */
+ if (d->get_sync_obj) {
+ sync = kzalloc(sizeof(*sync), GFP_KERNEL);
+ r = sync_setup("dsscomp_sync", &sync_fops, sync, O_RDONLY);
+ if (sync && (debug & DEBUG_WAITS))
+ dev_info(DEV(cdev), "new sync [%p] on #%d\n", sync,
+ sync->fd);
+ if (r)
+ sync_drop(sync);
+ }
+
+ /* drop composition if failed to create */
+ if (r) {
+ dsscomp_drop(comp);
+ return r;
+ }
+
+ if (sync) {
+ sync->refs.counter++;
+ comp->extra_cb = dsscomp_queue_cb;
+ comp->extra_cb_data = sync;
+ }
+ if (d->mode & DSSCOMP_SETUP_APPLY)
+ r = dsscomp_delayed_apply(comp);
+
+ /* delete sync object if failed to apply or create file */
+ if (sync) {
+ r = sync_finalize(sync, r);
+ if (r < 0)
+ sync_drop(sync);
+ }
+ return r;
+}
+
+static long query_display(struct dsscomp_dev *cdev,
+ struct dsscomp_display_info *dis)
+{
+ struct omap_dss_device *dev;
+ struct omap_overlay_manager *mgr;
+ 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;
+
+ dis->width_in_mm = DIV_ROUND_CLOSEST(dev->panel.width_in_um, 1000);
+ dis->height_in_mm = DIV_ROUND_CLOSEST(dev->panel.height_in_um, 1000);
+
+ /* find all overlays available for/owned by this display */
+ for (i = 0; i < cdev->num_ovls && dis->enabled; i++) {
+ if (cdev->ovls[i]->manager == mgr)
+ dis->overlays_owned |= 1 << i;
+ else if (!cdev->ovls[i]->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;
+
+ if (dis->modedb_len && dev->driver->get_modedb)
+ dis->modedb_len = dev->driver->get_modedb(dev,
+ (struct fb_videomode *) dis->modedb, dis->modedb_len);
+ 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 setup_display(struct dsscomp_dev *cdev,
+ struct dsscomp_setup_display_data *dis)
+{
+ struct omap_dss_device *dev;
+
+ /* get display */
+ if (dis->ix >= cdev->num_displays)
+ return -EINVAL;
+ dev = cdev->displays[dis->ix];
+ if (!dev)
+ return -EINVAL;
+
+ if (dev->driver->set_mode)
+ return dev->driver->set_mode(dev,
+ (struct fb_videomode *) &dis->mode);
+ else
+ return 0;
+}
+
+static void fill_cache(struct dsscomp_dev *cdev)
+{
+ unsigned long i;
+ struct omap_dss_device *dssdev = NULL;
+
+ cdev->num_ovls = min(omap_dss_get_num_overlays(), MAX_OVERLAYS);
+ for (i = 0; i < cdev->num_ovls; i++)
+ cdev->ovls[i] = omap_dss_get_overlay(i);
+
+ cdev->num_mgrs = min(omap_dss_get_num_overlay_managers(), MAX_MANAGERS);
+ for (i = 0; i < cdev->num_mgrs; i++)
+ cdev->mgrs[i] = omap_dss_get_overlay_manager(i);
+
+ for_each_dss_dev(dssdev) {
+ const char *name = dev_name(&dssdev->dev);
+ if (strncmp(name, "display", 7) ||
+ strict_strtoul(name + 7, 10, &i) ||
+ i >= MAX_DISPLAYS)
+ continue;
+
+ if (cdev->num_displays <= i)
+ cdev->num_displays = i + 1;
+
+ cdev->displays[i] = dssdev;
+ dev_dbg(DEV(cdev), "display%lu=%s\n", i, dssdev->driver_name);
+
+ cdev->state_notifiers[i].notifier_call = dsscomp_state_notifier;
+ blocking_notifier_chain_register(&dssdev->state_notifiers,
+ cdev->state_notifiers + i);
+ }
+ dev_info(DEV(cdev), "found %d displays and %d overlays\n",
+ cdev->num_displays, cdev->num_ovls);
+}
+
+static long comp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int r = 0;
+ struct miscdevice *dev = filp->private_data;
+ struct dsscomp_dev *cdev = container_of(dev, struct dsscomp_dev, dev);
+ void __user *ptr = (void __user *)arg;
+
+ union {
+ struct {
+ struct dsscomp_setup_mgr_data set;
+ struct dss2_ovl_info ovl[MAX_OVERLAYS];
+ } m;
+ struct dsscomp_setup_dispc_data dispc;
+ struct dsscomp_display_info dis;
+ struct dsscomp_check_ovl_data chk;
+ struct dsscomp_setup_display_data sdis;
+ } u;
+
+ dsscomp_gralloc_init(cdev);
+
+ switch (cmd) {
+ case DSSCIOC_SETUP_MGR:
+ {
+ r = copy_from_user(&u.m.set, ptr, sizeof(u.m.set)) ? :
+ u.m.set.num_ovls >= ARRAY_SIZE(u.m.ovl) ? -EINVAL :
+ copy_from_user(&u.m.ovl,
+ (void __user *)arg + sizeof(u.m.set),
+ sizeof(*u.m.ovl) * u.m.set.num_ovls) ? :
+ setup_mgr(cdev, &u.m.set);
+ break;
+ }
+ case DSSCIOC_SETUP_DISPC:
+ {
+ r = copy_from_user(&u.dispc, ptr, sizeof(u.dispc)) ? :
+ dsscomp_gralloc_queue_ioctl(&u.dispc);
+ break;
+ }
+ case DSSCIOC_QUERY_DISPLAY:
+ {
+ struct dsscomp_display_info *dis = NULL;
+ r = copy_from_user(&u.dis, ptr, sizeof(u.dis));
+ if (!r) {
+ /* impose a safe limit on modedb_len to prevent
+ * wrap around/overflow calculation of the alloced
+ * size that would make it smaller than
+ * struct dsscomp_display_info and cause heap
+ * corruption.
+ */
+ u.dis.modedb_len = clamp_val(u.dis.modedb_len, 0, 256);
+
+ dis = kzalloc(sizeof(*dis->modedb) * u.dis.modedb_len +
+ sizeof(*dis), GFP_KERNEL);
+ }
+ if (dis) {
+ *dis = u.dis;
+ r = query_display(cdev, dis) ? :
+ copy_to_user(ptr, dis, sizeof(*dis) +
+ sizeof(*dis->modedb) * dis->modedb_len);
+ kfree(dis);
+ } else {
+ r = r ? : -ENOMEM;
+ }
+ break;
+ }
+ case DSSCIOC_CHECK_OVL:
+ {
+ r = copy_from_user(&u.chk, ptr, sizeof(u.chk)) ? :
+ check_ovl(cdev, &u.chk);
+ break;
+ }
+ case DSSCIOC_SETUP_DISPLAY:
+ {
+ r = copy_from_user(&u.sdis, ptr, sizeof(u.sdis)) ? :
+ setup_display(cdev, &u.sdis);
+ }
+ default:
+ r = -EINVAL;
+ }
+ return r;
+}
+
+/* must implement open for filp->private_data to be filled */
+static int comp_open(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static const struct file_operations comp_fops = {
+ .owner = THIS_MODULE,
+ .open = comp_open,
+ .unlocked_ioctl = comp_ioctl,
+};
+
+static int dsscomp_debug_show(struct seq_file *s, void *unused)
+{
+ void (*fn)(struct seq_file *s) = s->private;
+ fn(s);
+ return 0;
+}
+
+static int dsscomp_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dsscomp_debug_show, inode->i_private);
+}
+
+static const struct file_operations dsscomp_debug_fops = {
+ .open = dsscomp_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int dsscomp_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct dsscomp_dev *cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ if (!cdev) {
+ pr_err("dsscomp: failed to allocate device.\n");
+ return -ENOMEM;
+ }
+ cdev->dev.minor = MISC_DYNAMIC_MINOR;
+ cdev->dev.name = "dsscomp";
+ cdev->dev.mode = 0666;
+ cdev->dev.fops = &comp_fops;
+
+ ret = misc_register(&cdev->dev);
+ if (ret) {
+ pr_err("dsscomp: failed to register misc device.\n");
+ return ret;
+ }
+ cdev->dbgfs = debugfs_create_dir("dsscomp", NULL);
+ if (IS_ERR_OR_NULL(cdev->dbgfs))
+ dev_warn(DEV(cdev), "failed to create debug files.\n");
+ else {
+ debugfs_create_file("comps", S_IRUGO,
+ cdev->dbgfs, dsscomp_dbg_comps, &dsscomp_debug_fops);
+ debugfs_create_file("gralloc", S_IRUGO,
+ cdev->dbgfs, dsscomp_dbg_gralloc, &dsscomp_debug_fops);
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+ debugfs_create_file("log", S_IRUGO,
+ cdev->dbgfs, dsscomp_dbg_events, &dsscomp_debug_fops);
+#endif
+ }
+
+ platform_set_drvdata(pdev, cdev);
+
+ pr_info("dsscomp: initializing.\n");
+
+ fill_cache(cdev);
+
+ /* initialize queues */
+ dsscomp_queue_init(cdev);
+ dsscomp_gralloc_init(cdev);
+
+ return 0;
+}
+
+static int dsscomp_remove(struct platform_device *pdev)
+{
+ struct dsscomp_dev *cdev = platform_get_drvdata(pdev);
+ misc_deregister(&cdev->dev);
+ debugfs_remove_recursive(cdev->dbgfs);
+ dsscomp_queue_exit();
+ dsscomp_gralloc_exit();
+ kfree(cdev);
+
+ return 0;
+}
+
+static struct platform_driver dsscomp_pdriver = {
+ .probe = dsscomp_probe,
+ .remove = dsscomp_remove,
+ .driver = { .name = MODULE_NAME, .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);
+}
+
+#define DUMP_CHUNK 256
+static char dump_buf[64 * 1024];
+void dsscomp_kdump(void)
+{
+ struct seq_file s = {
+ .buf = dump_buf,
+ .size = sizeof(dump_buf) - 1,
+ };
+ int i;
+
+ dsscomp_dbg_events(&s);
+ dsscomp_dbg_comps(&s);
+ dsscomp_dbg_gralloc(&s);
+
+ for (i = 0; i < s.count; i += DUMP_CHUNK) {
+ if ((s.count - i) > DUMP_CHUNK) {
+ char c = s.buf[i + DUMP_CHUNK];
+ s.buf[i + DUMP_CHUNK] = 0;
+ pr_cont("%s", s.buf + i);
+ s.buf[i + DUMP_CHUNK] = c;
+ } else {
+ s.buf[s.count] = 0;
+ pr_cont("%s", s.buf + i);
+ }
+ }
+}
+EXPORT_SYMBOL(dsscomp_kdump);
+
+MODULE_LICENSE("GPL v2");
+module_init(dsscomp_init);
+module_exit(dsscomp_exit);
diff --git a/drivers/video/omap2/dsscomp/dsscomp.h b/drivers/video/omap2/dsscomp/dsscomp.h
new file mode 100644
index 0000000..8edfaa7
--- /dev/null
+++ b/drivers/video/omap2/dsscomp/dsscomp.h
@@ -0,0 +1,208 @@
+/*
+ * linux/drivers/video/omap2/dsscomp/base.c
+ *
+ * DSS Composition basic operation support
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc
+ * Author: Lajos Molnar <molnar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _DSSCOMP_H
+#define _DSSCOMP_H
+
+#include <linux/miscdevice.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+#include <linux/hrtimer.h>
+#endif
+
+#define MAX_OVERLAYS 5
+#define MAX_MANAGERS 3
+#define MAX_DISPLAYS 4
+
+#define DEBUG_OVERLAYS (1 << 0)
+#define DEBUG_COMPOSITIONS (1 << 1)
+#define DEBUG_PHASES (1 << 2)
+#define DEBUG_WAITS (1 << 3)
+#define DEBUG_GRALLOC_PHASES (1 << 4)
+
+/*
+ * Utility macros
+ */
+#define ZERO(c) memset(&c, 0, sizeof(c))
+#define ZEROn(c, n) memset(c, 0, sizeof(*c) * n)
+#define DEV(c) (c->dev.this_device)
+
+/**
+ * DSS Composition Device Driver
+ *
+ * @dev: misc device base
+ * @dbgfs: debugfs hook
+ */
+struct dsscomp_dev {
+ struct miscdevice dev;
+ struct dentry *dbgfs;
+
+ /* cached DSS objects */
+ u32 num_ovls;
+ struct omap_overlay *ovls[MAX_OVERLAYS];
+ u32 num_mgrs;
+ struct omap_overlay_manager *mgrs[MAX_MANAGERS];
+ u32 num_displays;
+ struct omap_dss_device *displays[MAX_DISPLAYS];
+ struct notifier_block state_notifiers[MAX_DISPLAYS];
+};
+
+extern int debug;
+
+#ifdef CONFIG_DEBUG_FS
+extern struct mutex dbg_mtx;
+extern struct list_head dbg_comps;
+#define DO_IF_DEBUG_FS(cmd) { \
+ mutex_lock(&dbg_mtx); \
+ cmd; \
+ mutex_unlock(&dbg_mtx); \
+}
+#else
+#define DO_IF_DEBUG_FS(cmd)
+#endif
+
+enum dsscomp_state {
+ DSSCOMP_STATE_ACTIVE = 0xAC54156E,
+ DSSCOMP_STATE_APPLYING = 0xB554C591,
+ DSSCOMP_STATE_APPLIED = 0xB60504C1,
+ DSSCOMP_STATE_PROGRAMMED = 0xC0520652,
+ DSSCOMP_STATE_DISPLAYED = 0xD15504CA,
+};
+
+struct dsscomp_data {
+ enum dsscomp_state state;
+ /*
+ * :TRICKY: before applying, overlays used in a composition are stored
+ * in ovl_mask and the other masks are empty. Once composition is
+ * applied, blank is set to see if all overlays are to be disabled on
+ * this composition, any disabled overlays in the composition are set in
+ * ovl_dmask, and ovl_mask is updated to include ALL overlays that are
+ * actually on the display - even if they are not part of the
+ * composition. The reason: we use ovl_mask to see if an overlay is used
+ * or planned to be used on a manager. We update ovl_mask when
+ * composition is programmed (removing the disabled overlays).
+ */
+ bool blank; /* true if all overlays are to be disabled */
+ u32 ovl_mask; /* overlays used on this frame */
+ u32 ovl_dmask; /* overlays disabled on this frame */
+ u32 ix; /* manager index that this frame is on */
+ struct dsscomp_setup_mgr_data frm;
+ struct dss2_ovl_info ovls[5];
+ void (*extra_cb)(void *data, int status);
+ void *extra_cb_data;
+ bool must_apply; /* whether composition must be applied */
+
+#ifdef CONFIG_DEBUG_FS
+ struct list_head dbg_q;
+ u32 dbg_used;
+ struct {
+ u32 t, state;
+ } dbg_log[8];
+#endif
+};
+
+struct dsscomp_sync_obj {
+ int state;
+ int fd;
+ atomic_t refs;
+};
+
+/*
+ * Kernel interface
+ */
+int dsscomp_queue_init(struct dsscomp_dev *cdev);
+void dsscomp_queue_exit(void);
+void dsscomp_gralloc_init(struct dsscomp_dev *cdev);
+void dsscomp_gralloc_exit(void);
+int dsscomp_gralloc_queue_ioctl(struct dsscomp_setup_dispc_data *d);
+int dsscomp_wait(struct dsscomp_sync_obj *sync, enum dsscomp_wait_phase phase,
+ int timeout);
+int dsscomp_state_notifier(struct notifier_block *nb,
+ unsigned long arg, void *ptr);
+
+/* basic operation - if not using queues */
+int set_dss_ovl_info(struct dss2_ovl_info *oi);
+int set_dss_mgr_info(struct dss2_mgr_info *mi, struct omapdss_ovl_cb *cb);
+struct omap_overlay_manager *find_dss_mgr(int display_ix);
+void swap_rb_in_ovl_info(struct dss2_ovl_info *oi);
+void swap_rb_in_mgr_info(struct dss2_mgr_info *mi);
+
+/*
+ * Debug functions
+ */
+void dump_ovl_info(struct dsscomp_dev *cdev, struct dss2_ovl_info *oi);
+void dump_comp_info(struct dsscomp_dev *cdev, struct dsscomp_setup_mgr_data *d,
+ const char *phase);
+void dump_total_comp_info(struct dsscomp_dev *cdev,
+ struct dsscomp_setup_dispc_data *d,
+ const char *phase);
+const char *dsscomp_get_color_name(enum omap_color_mode m);
+
+void dsscomp_dbg_comps(struct seq_file *s);
+void dsscomp_dbg_gralloc(struct seq_file *s);
+
+#define log_state_str(s) (\
+ (s) == DSSCOMP_STATE_ACTIVE ? "ACTIVE" : \
+ (s) == DSSCOMP_STATE_APPLYING ? "APPLY'N" : \
+ (s) == DSSCOMP_STATE_APPLIED ? "APPLIED" : \
+ (s) == DSSCOMP_STATE_PROGRAMMED ? "PROGR'D" : \
+ (s) == DSSCOMP_STATE_DISPLAYED ? "DISPL'D" : "INVALID")
+
+#define log_status_str(ev) ( \
+ ((ev) & DSS_COMPLETION_CHANGED) ? "CHANGED" : \
+ (ev) == DSS_COMPLETION_DISPLAYED ? "DISPLAYED" : \
+ (ev) == DSS_COMPLETION_PROGRAMMED ? "PROGRAMMED" : \
+ (ev) == DSS_COMPLETION_TORN ? "TORN" : \
+ (ev) == DSS_COMPLETION_RELEASED ? "RELEASED" : \
+ ((ev) & DSS_COMPLETION_RELEASED) ? "ECLIPSED" : "???")
+
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+extern struct dbg_event_t {
+ u32 ms, a1, a2, ix;
+ void *data;
+ const char *fmt;
+} dbg_events[128];
+extern u32 dbg_event_ix;
+
+void dsscomp_dbg_events(struct seq_file *s);
+#endif
+
+static inline
+void __log_event(u32 ix, u32 ms, void *data, const char *fmt, u32 a1, u32 a2)
+{
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+ if (!ms)
+ ms = ktime_to_ms(ktime_get());
+ dbg_events[dbg_event_ix].ms = ms;
+ dbg_events[dbg_event_ix].data = data;
+ dbg_events[dbg_event_ix].fmt = fmt;
+ dbg_events[dbg_event_ix].a1 = a1;
+ dbg_events[dbg_event_ix].a2 = a2;
+ dbg_events[dbg_event_ix].ix = ix;
+ dbg_event_ix = (dbg_event_ix + 1) % ARRAY_SIZE(dbg_events);
+#endif
+}
+
+#define log_event(ix, ms, data, fmt, a1, a2) \
+ DO_IF_DEBUG_FS(__log_event(ix, ms, data, fmt, a1, a2))
+
+#endif
diff --git a/drivers/video/omap2/dsscomp/gralloc.c b/drivers/video/omap2/dsscomp/gralloc.c
new file mode 100644
index 0000000..b75dfd9
--- /dev/null
+++ b/drivers/video/omap2/dsscomp/gralloc.c
@@ -0,0 +1,607 @@
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <mach/tiler.h>
+#include <video/dsscomp.h>
+#include <plat/dsscomp.h>
+#include "dsscomp.h"
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+static bool blanked;
+
+#define NUM_TILER1D_SLOTS 2
+#define TILER1D_SLOT_SIZE (16 << 20)
+
+static struct tiler1d_slot {
+ struct list_head q;
+ tiler_blk_handle slot;
+ u32 phys;
+ u32 size;
+ u32 *page_map;
+} slots[NUM_TILER1D_SLOTS];
+static struct list_head free_slots;
+static struct dsscomp_dev *cdev;
+static DEFINE_MUTEX(mtx);
+static struct semaphore free_slots_sem =
+ __SEMAPHORE_INITIALIZER(free_slots_sem, 0);
+
+/* gralloc composition sync object */
+struct dsscomp_gralloc_t {
+ void (*cb_fn)(void *, int);
+ void *cb_arg;
+ struct list_head q;
+ struct list_head slots;
+ atomic_t refs;
+ bool early_callback;
+ bool programmed;
+};
+
+/* queued gralloc compositions */
+static LIST_HEAD(flip_queue);
+
+static u32 ovl_use_mask[MAX_MANAGERS];
+
+static void unpin_tiler_blocks(struct list_head *slots)
+{
+ struct tiler1d_slot *slot;
+
+ /* unpin any tiler memory */
+ list_for_each_entry(slot, slots, q) {
+ tiler_unpin_block(slot->slot);
+ up(&free_slots_sem);
+ }
+
+ /* free tiler slots */
+ list_splice_init(slots, &free_slots);
+}
+
+static void dsscomp_gralloc_cb(void *data, int status)
+{
+ struct dsscomp_gralloc_t *gsync = data, *gsync_;
+ bool early_cbs = true;
+ LIST_HEAD(done);
+
+ mutex_lock(&mtx);
+ if (gsync->early_callback && status == DSS_COMPLETION_PROGRAMMED)
+ gsync->programmed = true;
+
+ if (status & DSS_COMPLETION_RELEASED) {
+ if (atomic_dec_and_test(&gsync->refs))
+ unpin_tiler_blocks(&gsync->slots);
+
+ log_event(0, 0, gsync, "--refs=%d on %s",
+ atomic_read(&gsync->refs),
+ (u32) log_status_str(status));
+ }
+
+ /* get completed list items in order, if any */
+ list_for_each_entry_safe(gsync, gsync_, &flip_queue, q) {
+ if (gsync->cb_fn) {
+ early_cbs &= gsync->early_callback && gsync->programmed;
+ if (early_cbs) {
+ gsync->cb_fn(gsync->cb_arg, 1);
+ gsync->cb_fn = NULL;
+ }
+ }
+ if (gsync->refs.counter && gsync->cb_fn)
+ break;
+ if (gsync->refs.counter == 0)
+ list_move_tail(&gsync->q, &done);
+ }
+ mutex_unlock(&mtx);
+
+ /* call back for completed composition with mutex unlocked */
+ list_for_each_entry_safe(gsync, gsync_, &done, q) {
+ if (debug & DEBUG_GRALLOC_PHASES)
+ dev_info(DEV(cdev), "[%p] completed flip\n", gsync);
+
+ log_event(0, 0, gsync, "calling %pf [%p]",
+ (u32) gsync->cb_fn, (u32) gsync->cb_arg);
+
+ if (gsync->cb_fn)
+ gsync->cb_fn(gsync->cb_arg, 1);
+ kfree(gsync);
+ }
+}
+
+/* This is just test code for now that does the setup + apply.
+ It still uses userspace virtual addresses, but maps non
+ TILER buffers into 1D */
+int dsscomp_gralloc_queue_ioctl(struct dsscomp_setup_dispc_data *d)
+{
+ struct tiler_pa_info *pas[MAX_OVERLAYS];
+ s32 ret;
+ u32 i;
+
+ if (d->num_ovls > MAX_OVERLAYS)
+ return -EINVAL;
+
+ /* convert virtual addresses to physical and get tiler pa infos */
+ for (i = 0; i < d->num_ovls; i++) {
+ struct dss2_ovl_info *oi = d->ovls + i;
+ u32 addr = (u32) oi->address;
+
+ pas[i] = NULL;
+
+ /* assume virtual NV12 for now */
+ if (oi->cfg.color_mode == OMAP_DSS_COLOR_NV12)
+ oi->uv = tiler_virt2phys(addr +
+ oi->cfg.height * oi->cfg.stride);
+ else
+ oi->uv = 0;
+ oi->ba = tiler_virt2phys(addr);
+
+ /* map non-TILER buffers to 1D */
+ if ((oi->ba < 0x60000000 || oi->ba >= 0x80000000) && oi->ba)
+ pas[i] = user_block_to_pa(addr & PAGE_MASK,
+ PAGE_ALIGN(oi->cfg.height * oi->cfg.stride +
+ (addr & ~PAGE_MASK)) >> PAGE_SHIFT);
+ }
+ ret = dsscomp_gralloc_queue(d, pas, false, NULL, NULL);
+ for (i = 0; i < d->num_ovls; i++)
+ tiler_pa_free(pas[i]);
+ return ret;
+}
+
+int dsscomp_gralloc_queue(struct dsscomp_setup_dispc_data *d,
+ struct tiler_pa_info **pas,
+ bool early_callback,
+ void (*cb_fn)(void *, int), void *cb_arg)
+{
+ u32 i;
+ int r = 0;
+ struct omap_dss_device *dev;
+ struct omap_overlay_manager *mgr;
+ static DEFINE_MUTEX(local_mtx);
+ dsscomp_t comp[MAX_MANAGERS];
+ u32 ovl_new_use_mask[MAX_MANAGERS];
+ u32 mgr_set_mask = 0;
+ u32 ovl_set_mask = 0;
+ struct tiler1d_slot *slot = NULL;
+ u32 slot_used = 0;
+#ifdef CONFIG_DEBUG_FS
+ u32 ms = ktime_to_ms(ktime_get());
+#endif
+ u32 channels[ARRAY_SIZE(d->mgrs)], ch;
+ int skip;
+ struct dsscomp_gralloc_t *gsync;
+ struct dss2_rect_t win = { .w = 0 };
+
+ /* reserve tiler areas if not already done so */
+ dsscomp_gralloc_init(cdev);
+
+ dump_total_comp_info(cdev, d, "queue");
+ for (i = 0; i < d->num_ovls; i++)
+ dump_ovl_info(cdev, d->ovls + i);
+
+ mutex_lock(&local_mtx);
+
+ mutex_lock(&mtx);
+
+ /* create sync object with 1 temporary ref */
+ gsync = kzalloc(sizeof(*gsync), GFP_KERNEL);
+ gsync->cb_arg = cb_arg;
+ gsync->cb_fn = cb_fn;
+ gsync->refs.counter = 1;
+ gsync->early_callback = early_callback;
+ INIT_LIST_HEAD(&gsync->slots);
+ list_add_tail(&gsync->q, &flip_queue);
+ if (debug & DEBUG_GRALLOC_PHASES)
+ dev_info(DEV(cdev), "[%p] queuing flip\n", gsync);
+
+ log_event(0, ms, gsync, "new in %pf (refs=1)",
+ (u32) dsscomp_gralloc_queue, 0);
+
+ /* ignore frames while we are blanked */
+ skip = blanked;
+ if (skip && (debug & DEBUG_PHASES))
+ dev_info(DEV(cdev), "[%p,%08x] ignored\n", gsync, d->sync_id);
+
+ /* mark blank frame by NULL tiler pa pointer */
+ if (!skip && pas == NULL)
+ blanked = true;
+
+ mutex_unlock(&mtx);
+
+ d->num_mgrs = min(d->num_mgrs, (u16) ARRAY_SIZE(d->mgrs));
+ d->num_ovls = min(d->num_ovls, (u16) ARRAY_SIZE(d->ovls));
+
+ memset(comp, 0, sizeof(comp));
+ memset(ovl_new_use_mask, 0, sizeof(ovl_new_use_mask));
+
+ if (skip)
+ goto skip_comp;
+
+ d->mode = DSSCOMP_SETUP_DISPLAY;
+
+ /* mark managers we are using */
+ for (i = 0; i < d->num_mgrs; i++) {
+ /* verify display is valid & connected, ignore if not */
+ if (d->mgrs[i].ix >= cdev->num_displays)
+ continue;
+ dev = cdev->displays[d->mgrs[i].ix];
+ if (!dev) {
+ dev_warn(DEV(cdev), "failed to get display%d\n",
+ d->mgrs[i].ix);
+ continue;
+ }
+ mgr = dev->manager;
+ if (!mgr) {
+ dev_warn(DEV(cdev), "no manager for display%d\n",
+ d->mgrs[i].ix);
+ continue;
+ }
+ channels[i] = ch = mgr->id;
+ mgr_set_mask |= 1 << ch;
+
+ /* swap red & blue if requested */
+ if (d->mgrs[i].swap_rb)
+ swap_rb_in_mgr_info(d->mgrs + i);
+ }
+
+ /* create dsscomp objects for set managers (including active ones) */
+ for (ch = 0; ch < MAX_MANAGERS; ch++) {
+ if (!(mgr_set_mask & (1 << ch)) && !ovl_use_mask[ch])
+ continue;
+
+ mgr = cdev->mgrs[ch];
+
+ comp[ch] = dsscomp_new(mgr);
+ if (IS_ERR(comp[ch])) {
+ comp[ch] = NULL;
+ dev_warn(DEV(cdev), "failed to get composition on %s\n",
+ mgr->name);
+ continue;
+ }
+
+ /* set basic manager information for blanked managers */
+ if (!(mgr_set_mask & (1 << ch))) {
+ struct dss2_mgr_info mi = {
+ .alpha_blending = true,
+ .ix = comp[ch]->frm.mgr.ix,
+ };
+ dsscomp_set_mgr(comp[ch], &mi);
+ }
+
+ comp[ch]->must_apply = true;
+ r = dsscomp_setup(comp[ch], d->mode, win);
+ if (r)
+ dev_err(DEV(cdev), "failed to setup comp (%d)\n", r);
+ }
+
+ /* configure manager data from gralloc composition */
+ for (i = 0; i < d->num_mgrs; i++) {
+ ch = channels[i];
+ r = dsscomp_set_mgr(comp[ch], d->mgrs + i);
+ if (r)
+ dev_err(DEV(cdev), "failed to set mgr%d (%d)\n", ch, r);
+ }
+
+ /* NOTE: none of the dsscomp sets should fail as composition is new */
+ for (i = 0; i < d->num_ovls; i++) {
+ struct dss2_ovl_info *oi = d->ovls + i;
+ u32 mgr_ix = oi->cfg.mgr_ix;
+ u32 size;
+
+ /* verify manager index */
+ if (mgr_ix >= d->num_mgrs) {
+ dev_err(DEV(cdev), "invalid manager for ovl%d\n",
+ oi->cfg.ix);
+ continue;
+ }
+ ch = channels[mgr_ix];
+
+ /* skip overlays on compositions we could not create */
+ if (!comp[ch])
+ continue;
+
+ /* swap red & blue if requested */
+ if (d->mgrs[mgr_ix].swap_rb)
+ swap_rb_in_ovl_info(d->ovls + i);
+
+ /* copy prior overlay to avoid mapping layers twice to 1D */
+ if (oi->addressing == OMAP_DSS_BUFADDR_OVL_IX) {
+ unsigned int j = oi->ba;
+ if (j >= i) {
+ WARN(1, "Invalid clone layer (%u)", j);
+ goto skip_buffer;
+ }
+
+ oi->ba = d->ovls[j].ba;
+ oi->uv = d->ovls[j].uv;
+ goto skip_map1d;
+ } else if (oi->addressing == OMAP_DSS_BUFADDR_FB) {
+ /* get fb */
+ int fb_ix = (oi->ba >> 28);
+ int fb_uv_ix = (oi->uv >> 28);
+ struct fb_info *fbi = NULL, *fbi_uv = NULL;
+ size_t size = oi->cfg.height * oi->cfg.stride;
+ if (fb_ix >= num_registered_fb ||
+ (oi->cfg.color_mode == OMAP_DSS_COLOR_NV12 &&
+ fb_uv_ix >= num_registered_fb)) {
+ WARN(1, "display has no framebuffer");
+ goto skip_buffer;
+ }
+
+ fbi = fbi_uv = registered_fb[fb_ix];
+ if (oi->cfg.color_mode == OMAP_DSS_COLOR_NV12)
+ fbi_uv = registered_fb[fb_uv_ix];
+
+ if (size + oi->ba > fbi->fix.smem_len ||
+ (oi->cfg.color_mode == OMAP_DSS_COLOR_NV12 &&
+ (size >> 1) + oi->uv > fbi_uv->fix.smem_len)) {
+ WARN(1, "image outside of framebuffer memory");
+ goto skip_buffer;
+ }
+
+ oi->ba += fbi->fix.smem_start;
+ oi->uv += fbi_uv->fix.smem_start;
+ goto skip_map1d;
+ }
+
+ /* map non-TILER buffers to 1D */
+
+ /* skip 2D and disabled layers */
+ if (!pas[i] || !oi->cfg.enabled)
+ goto skip_map1d;
+
+ if (!slot) {
+ if (down_timeout(&free_slots_sem,
+ msecs_to_jiffies(100))) {
+ dev_warn(DEV(cdev), "could not obtain tiler slot");
+ goto skip_buffer;
+ }
+ mutex_lock(&mtx);
+ slot = list_first_entry(&free_slots, typeof(*slot), q);
+ list_move(&slot->q, &gsync->slots);
+ mutex_unlock(&mtx);
+ }
+
+ size = oi->cfg.stride * oi->cfg.height;
+ if (oi->cfg.color_mode == OMAP_DSS_COLOR_NV12)
+ size += size >> 2;
+ size = DIV_ROUND_UP(size, PAGE_SIZE);
+
+ if (slot_used + size > slot->size) {
+ dev_err(DEV(cdev), "tiler slot not big enough for frame %d + %d > %d",
+ slot_used, size, slot->size);
+ goto skip_buffer;
+ }
+
+ /* "map" into TILER 1D - will happen after loop */
+ oi->ba = slot->phys + (slot_used << PAGE_SHIFT) +
+ (oi->ba & ~PAGE_MASK);
+ memcpy(slot->page_map + slot_used, pas[i]->mem,
+ sizeof(*slot->page_map) * size);
+ slot_used += size;
+ goto skip_map1d;
+
+skip_buffer:
+ oi->cfg.enabled = false;
+skip_map1d:
+
+ if (oi->cfg.enabled)
+ ovl_new_use_mask[ch] |= 1 << oi->cfg.ix;
+
+ r = dsscomp_set_ovl(comp[ch], oi);
+ if (r)
+ dev_err(DEV(cdev), "failed to set ovl%d (%d)\n",
+ oi->cfg.ix, r);
+ else
+ ovl_set_mask |= 1 << oi->cfg.ix;
+ }
+
+ if (slot && slot_used) {
+ r = tiler_pin_block(slot->slot, slot->page_map,
+ slot_used);
+ if (r)
+ dev_err(DEV(cdev), "failed to pin %d pages into"
+ " %d-pg slots (%d)\n", slot_used,
+ TILER1D_SLOT_SIZE >> PAGE_SHIFT, r);
+ }
+
+ for (ch = 0; ch < MAX_MANAGERS; ch++) {
+ /* disable all overlays not specifically set from prior frame */
+ u32 mask = ovl_use_mask[ch] & ~ovl_set_mask;
+
+ if (!comp[ch])
+ continue;
+
+ while (mask) {
+ struct dss2_ovl_info oi = {
+ .cfg.zonly = true,
+ .cfg.enabled = false,
+ .cfg.ix = fls(mask) - 1,
+ };
+ dsscomp_set_ovl(comp[ch], &oi);
+ mask &= ~(1 << oi.cfg.ix);
+ }
+
+ /* associate dsscomp objects with this gralloc composition */
+ comp[ch]->extra_cb = dsscomp_gralloc_cb;
+ comp[ch]->extra_cb_data = gsync;
+ atomic_inc(&gsync->refs);
+ log_event(0, ms, gsync, "++refs=%d for [%p]",
+ atomic_read(&gsync->refs), (u32) comp[ch]);
+
+ r = dsscomp_delayed_apply(comp[ch]);
+ if (r)
+ dev_err(DEV(cdev), "failed to apply comp (%d)\n", r);
+ else
+ ovl_use_mask[ch] = ovl_new_use_mask[ch];
+ }
+skip_comp:
+ /* release sync object ref - this completes unapplied compositions */
+ dsscomp_gralloc_cb(gsync, DSS_COMPLETION_RELEASED);
+
+ mutex_unlock(&local_mtx);
+
+ return r;
+}
+
+#ifdef CONFIG_EARLYSUSPEND
+static int blank_complete;
+static DECLARE_WAIT_QUEUE_HEAD(early_suspend_wq);
+
+static void dsscomp_early_suspend_cb(void *data, int status)
+{
+ blank_complete = true;
+ wake_up(&early_suspend_wq);
+}
+
+static void dsscomp_early_suspend(struct early_suspend *h)
+{
+ struct dsscomp_setup_dispc_data d = {
+ .num_mgrs = 0,
+ };
+ int err;
+
+ pr_info("DSSCOMP: %s\n", __func__);
+
+ /* use gralloc queue as we need to blank all screens */
+ blank_complete = false;
+ dsscomp_gralloc_queue(&d, NULL, false, dsscomp_early_suspend_cb, NULL);
+
+ /* wait until composition is displayed */
+ err = wait_event_timeout(early_suspend_wq, blank_complete,
+ msecs_to_jiffies(500));
+ if (err == 0)
+ pr_warn("DSSCOMP: timeout blanking screen\n");
+ else
+ pr_info("DSSCOMP: blanked screen\n");
+}
+
+static void dsscomp_late_resume(struct early_suspend *h)
+{
+ pr_info("DSSCOMP: %s\n", __func__);
+ blanked = false;
+}
+
+static struct early_suspend early_suspend_info = {
+ .suspend = dsscomp_early_suspend,
+ .resume = dsscomp_late_resume,
+ .level = EARLY_SUSPEND_LEVEL_DISABLE_FB,
+};
+#endif
+
+void dsscomp_dbg_gralloc(struct seq_file *s)
+{
+#ifdef CONFIG_DEBUG_FS
+ struct dsscomp_gralloc_t *g;
+ struct tiler1d_slot *t;
+ dsscomp_t c;
+ int i;
+
+ mutex_lock(&dbg_mtx);
+ seq_printf(s, "ACTIVE GRALLOC FLIPS\n\n");
+ list_for_each_entry(g, &flip_queue, q) {
+ char *sep = "";
+ seq_printf(s, " [%p] (refs=%d)\n"
+ " slots=[", g, atomic_read(&g->refs));
+ list_for_each_entry(t, &g->slots, q) {
+ seq_printf(s, "%s%08x", sep, t->phys);
+ sep = ", ";
+ }
+ seq_printf(s, "]\n cmdcb=[%08x] ", (u32) g->cb_arg);
+ if (g->cb_fn)
+ seq_printf(s, "%pf\n\n ", g->cb_fn);
+ else
+ seq_printf(s, "(called)\n\n ");
+
+ list_for_each_entry(c, &dbg_comps, dbg_q) {
+ if (c->extra_cb && c->extra_cb_data == g)
+ seq_printf(s, "| %8s ",
+ cdev->mgrs[c->ix]->name);
+ }
+ seq_printf(s, "\n ");
+ list_for_each_entry(c, &dbg_comps, dbg_q) {
+ if (c->extra_cb && c->extra_cb_data == g)
+ seq_printf(s, "| [%08x] %7s ", (u32) c,
+ log_state_str(c->state));
+ }
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+ for (i = 0; i < ARRAY_SIZE(c->dbg_log); i++) {
+ int go = false;
+ seq_printf(s, "\n ");
+ list_for_each_entry(c, &dbg_comps, dbg_q) {
+ if (!c->extra_cb || c->extra_cb_data != g)
+ continue;
+ if (i < c->dbg_used) {
+ u32 t = c->dbg_log[i].t;
+ u32 state = c->dbg_log[i].state;
+ seq_printf(s, "| % 6d.%03d %7s ",
+ t / 1000, t % 1000,
+ log_state_str(state));
+ go |= c->dbg_used > i + 1;
+ } else {
+ seq_printf(s, "%-21s", "|");
+ }
+ }
+ if (!go)
+ break;
+ }
+#endif
+ seq_printf(s, "\n\n");
+ }
+ seq_printf(s, "\n");
+ mutex_unlock(&dbg_mtx);
+#endif
+}
+
+void dsscomp_gralloc_init(struct dsscomp_dev *cdev_)
+{
+ int i;
+
+ /* save at least cdev pointer */
+ if (!cdev && cdev_) {
+ cdev = cdev_;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ register_early_suspend(&early_suspend_info);
+#endif
+ }
+
+ if (!free_slots.next) {
+ INIT_LIST_HEAD(&free_slots);
+ for (i = 0; i < NUM_TILER1D_SLOTS; i++) {
+ u32 phys;
+ tiler_blk_handle slot =
+ tiler_alloc_block_area(TILFMT_PAGE,
+ TILER1D_SLOT_SIZE, 1, &phys, NULL);
+ if (IS_ERR_OR_NULL(slot)) {
+ pr_err("could not allocate slot");
+ break;
+ }
+ slots[i].slot = slot;
+ slots[i].phys = phys;
+ slots[i].size = TILER1D_SLOT_SIZE >> PAGE_SHIFT;
+ slots[i].page_map = kmalloc(sizeof(*slots[i].page_map) *
+ slots[i].size, GFP_KERNEL);
+ if (!slots[i].page_map) {
+ pr_err("could not allocate page_map");
+ tiler_free_block_area(slot);
+ break;
+ }
+ list_add(&slots[i].q, &free_slots);
+ up(&free_slots_sem);
+ }
+ /* reset free_slots if no TILER memory could be reserved */
+ if (!i)
+ ZERO(free_slots);
+ }
+}
+
+void dsscomp_gralloc_exit(void)
+{
+ struct tiler1d_slot *slot;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&early_suspend_info);
+#endif
+
+ list_for_each_entry(slot, &free_slots, q)
+ tiler_free_block_area(slot->slot);
+ INIT_LIST_HEAD(&free_slots);
+}
diff --git a/drivers/video/omap2/dsscomp/queue.c b/drivers/video/omap2/dsscomp/queue.c
new file mode 100644
index 0000000..c25d655
--- /dev/null
+++ b/drivers/video/omap2/dsscomp/queue.c
@@ -0,0 +1,807 @@
+/*
+ * linux/drivers/video/omap2/dsscomp/queue.c
+ *
+ * DSS Composition queueing support
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc
+ * Author: Lajos Molnar <molnar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ratelimit.h>
+
+#include <video/omapdss.h>
+#include <video/dsscomp.h>
+#include <plat/dsscomp.h>
+
+#include <linux/debugfs.h>
+
+#include "dsscomp.h"
+/* queue state */
+
+static DEFINE_MUTEX(mtx);
+
+/* free overlay structs */
+struct maskref {
+ u32 mask;
+ u32 refs[MAX_OVERLAYS];
+};
+
+static struct {
+ struct workqueue_struct *apply_workq;
+
+ u32 ovl_mask; /* overlays used on this display */
+ struct maskref ovl_qmask; /* overlays queued to this display */
+ bool blanking;
+} mgrq[MAX_MANAGERS];
+
+static struct workqueue_struct *cb_wkq; /* callback work queue */
+static struct dsscomp_dev *cdev;
+
+#ifdef CONFIG_DEBUG_FS
+LIST_HEAD(dbg_comps);
+DEFINE_MUTEX(dbg_mtx);
+#endif
+
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+struct dbg_event_t dbg_events[128];
+u32 dbg_event_ix;
+#endif
+
+static inline void __log_state(dsscomp_t c, void *fn, u32 ev)
+{
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+ if (c->dbg_used < ARRAY_SIZE(c->dbg_log)) {
+ u32 t = (u32) ktime_to_ms(ktime_get());
+ c->dbg_log[c->dbg_used].t = t;
+ c->dbg_log[c->dbg_used++].state = c->state;
+ __log_event(20 * c->ix + 20, t, c, ev ? "%pf on %s" : "%pf",
+ (u32) fn, (u32) log_status_str(ev));
+ }
+#endif
+}
+#define log_state(c, fn, ev) DO_IF_DEBUG_FS(__log_state(c, fn, ev))
+
+static inline void maskref_incbit(struct maskref *om, u32 ix)
+{
+ om->refs[ix]++;
+ om->mask |= 1 << ix;
+}
+
+static void maskref_decmask(struct maskref *om, u32 mask)
+{
+ while (mask) {
+ u32 ix = fls(mask) - 1, m = 1 << ix;
+ if (!--om->refs[ix])
+ om->mask &= ~m;
+ mask &= ~m;
+ }
+}
+
+/*
+ * ===========================================================================
+ * EXIT
+ * ===========================================================================
+ */
+
+/* Initialize queue structures, and set up state of the displays */
+int dsscomp_queue_init(struct dsscomp_dev *cdev_)
+{
+ u32 i, j;
+ cdev = cdev_;
+
+ if (ARRAY_SIZE(mgrq) < cdev->num_mgrs)
+ return -EINVAL;
+
+ ZERO(mgrq);
+ for (i = 0; i < cdev->num_mgrs; i++) {
+ struct omap_overlay_manager *mgr;
+ mgrq[i].apply_workq = create_singlethread_workqueue("dsscomp_apply");
+ if (!mgrq[i].apply_workq)
+ goto error;
+
+ /* record overlays on this display */
+ mgr = cdev->mgrs[i];
+ for (j = 0; j < cdev->num_ovls; j++) {
+ if (cdev->ovls[j]->info.enabled &&
+ mgr &&
+ cdev->ovls[j]->manager == mgr)
+ mgrq[i].ovl_mask |= 1 << j;
+ }
+ }
+
+ cb_wkq = create_singlethread_workqueue("dsscomp_cb");
+ if (!cb_wkq)
+ goto error;
+
+ return 0;
+error:
+ while (i--)
+ destroy_workqueue(mgrq[i].apply_workq);
+ return -ENOMEM;
+}
+
+/* get display index from manager */
+static u32 get_display_ix(struct omap_overlay_manager *mgr)
+{
+ u32 i;
+
+ /* handle if manager is not attached to a display */
+ if (!mgr || !mgr->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
+ * ===========================================================================
+ */
+
+/* create a new composition for a display */
+dsscomp_t dsscomp_new(struct omap_overlay_manager *mgr)
+{
+ struct dsscomp_data *comp = NULL;
+ u32 display_ix = get_display_ix(mgr);
+
+ /* check manager */
+ u32 ix = mgr ? mgr->id : cdev->num_mgrs;
+ if (ix >= cdev->num_mgrs || display_ix >= cdev->num_displays)
+ return ERR_PTR(-EINVAL);
+
+ /* allocate composition */
+ comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return NULL;
+
+ /* initialize new composition */
+ comp->ix = ix; /* save where this composition came from */
+ comp->ovl_mask = comp->ovl_dmask = 0;
+ comp->frm.sync_id = 0;
+ comp->frm.mgr.ix = display_ix;
+ comp->state = DSSCOMP_STATE_ACTIVE;
+
+ DO_IF_DEBUG_FS({
+ __log_state(comp, dsscomp_new, 0);
+ list_add(&comp->dbg_q, &dbg_comps);
+ });
+
+ return comp;
+}
+EXPORT_SYMBOL(dsscomp_new);
+
+/* returns overlays used in a composition */
+u32 dsscomp_get_ovls(dsscomp_t comp)
+{
+ u32 mask;
+
+ mutex_lock(&mtx);
+ BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
+ mask = comp->ovl_mask;
+ mutex_unlock(&mtx);
+
+ return mask;
+}
+EXPORT_SYMBOL(dsscomp_get_ovls);
+
+/* set overlay info */
+int dsscomp_set_ovl(dsscomp_t comp, struct dss2_ovl_info *ovl)
+{
+ int r = -EBUSY;
+ u32 i, mask, oix, ix;
+ struct omap_overlay *o;
+
+ mutex_lock(&mtx);
+
+ BUG_ON(!ovl);
+ BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
+
+ ix = comp->ix;
+
+ if (ovl->cfg.ix >= cdev->num_ovls) {
+ r = -EINVAL;
+ goto done;
+ }
+
+ /* if overlay is already part of the composition */
+ mask = 1 << ovl->cfg.ix;
+ if (mask & comp->ovl_mask) {
+ /* look up overlay */
+ for (oix = 0; oix < comp->frm.num_ovls; oix++) {
+ if (comp->ovls[oix].cfg.ix == ovl->cfg.ix)
+ break;
+ }
+ BUG_ON(oix == comp->frm.num_ovls);
+ } else {
+ /* check if ovl is free to use */
+ if (comp->frm.num_ovls >= ARRAY_SIZE(comp->ovls))
+ goto done;
+
+ /* not in any other displays queue */
+ if (mask & ~mgrq[ix].ovl_qmask.mask) {
+ for (i = 0; i < cdev->num_mgrs; i++) {
+ if (i == ix)
+ continue;
+ if (mgrq[i].ovl_qmask.mask & mask)
+ goto done;
+ }
+ }
+
+ /* and disabled (unless forced) if on another manager */
+ o = cdev->ovls[ovl->cfg.ix];
+ if (o->info.enabled && (!o->manager || o->manager->id != ix))
+ goto done;
+
+ /* add overlay to composition & display */
+ comp->ovl_mask |= mask;
+ oix = comp->frm.num_ovls++;
+ maskref_incbit(&mgrq[ix].ovl_qmask, ovl->cfg.ix);
+ }
+
+ comp->ovls[oix] = *ovl;
+ r = 0;
+done:
+ mutex_unlock(&mtx);
+
+ return r;
+}
+EXPORT_SYMBOL(dsscomp_set_ovl);
+
+/* get overlay info */
+int dsscomp_get_ovl(dsscomp_t comp, u32 ix, struct dss2_ovl_info *ovl)
+{
+ int r;
+ u32 oix;
+
+ mutex_lock(&mtx);
+
+ BUG_ON(!ovl);
+ BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
+
+ if (ix >= cdev->num_ovls) {
+ r = -EINVAL;
+ } else if (comp->ovl_mask & (1 << ix)) {
+ r = 0;
+ for (oix = 0; oix < comp->frm.num_ovls; oix++)
+ if (comp->ovls[oix].cfg.ix == ovl->cfg.ix) {
+ *ovl = comp->ovls[oix];
+ break;
+ }
+ BUG_ON(oix == comp->frm.num_ovls);
+ } else {
+ r = -ENOENT;
+ }
+
+ mutex_unlock(&mtx);
+
+ return r;
+}
+EXPORT_SYMBOL(dsscomp_get_ovl);
+
+/* set manager info */
+int dsscomp_set_mgr(dsscomp_t comp, struct dss2_mgr_info *mgr)
+{
+ mutex_lock(&mtx);
+
+ BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
+ BUG_ON(mgr->ix != comp->frm.mgr.ix);
+
+ comp->frm.mgr = *mgr;
+
+ mutex_unlock(&mtx);
+
+ return 0;
+}
+EXPORT_SYMBOL(dsscomp_set_mgr);
+
+/* get manager info */
+int dsscomp_get_mgr(dsscomp_t comp, struct dss2_mgr_info *mgr)
+{
+ mutex_lock(&mtx);
+
+ BUG_ON(!mgr);
+ BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
+
+ *mgr = comp->frm.mgr;
+
+ mutex_unlock(&mtx);
+
+ return 0;
+}
+EXPORT_SYMBOL(dsscomp_get_mgr);
+
+/* get manager info */
+int dsscomp_setup(dsscomp_t comp, enum dsscomp_setup_mode mode,
+ struct dss2_rect_t win)
+{
+ mutex_lock(&mtx);
+
+ BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
+
+ comp->frm.mode = mode;
+ comp->frm.win = win;
+
+ mutex_unlock(&mtx);
+
+ return 0;
+}
+EXPORT_SYMBOL(dsscomp_setup);
+
+/*
+ * ===========================================================================
+ * QUEUING COMMITTING OPERATIONS
+ * ===========================================================================
+ */
+void dsscomp_drop(dsscomp_t comp)
+{
+ /* decrement unprogrammed references */
+ if (comp->state < DSSCOMP_STATE_PROGRAMMED)
+ maskref_decmask(&mgrq[comp->ix].ovl_qmask, comp->ovl_mask);
+ comp->state = 0;
+
+ if (debug & DEBUG_COMPOSITIONS)
+ dev_info(DEV(cdev), "[%p] released\n", comp);
+
+ DO_IF_DEBUG_FS(list_del(&comp->dbg_q));
+
+ kfree(comp);
+}
+EXPORT_SYMBOL(dsscomp_drop);
+
+struct dsscomp_cb_work {
+ struct work_struct work;
+ struct dsscomp_data *comp;
+ int status;
+};
+
+static void dsscomp_mgr_delayed_cb(struct work_struct *work)
+{
+ struct dsscomp_cb_work *wk = container_of(work, typeof(*wk), work);
+ struct dsscomp_data *comp = wk->comp;
+ int status = wk->status;
+ u32 ix;
+
+ kfree(work);
+
+ mutex_lock(&mtx);
+
+ BUG_ON(comp->state == DSSCOMP_STATE_ACTIVE);
+ ix = comp->ix;
+
+ /* call extra callbacks if requested */
+ if (comp->extra_cb)
+ comp->extra_cb(comp->extra_cb_data, status);
+
+ /* handle programming & release */
+ if (status == DSS_COMPLETION_PROGRAMMED) {
+ comp->state = DSSCOMP_STATE_PROGRAMMED;
+ log_state(comp, dsscomp_mgr_delayed_cb, status);
+
+ /* update used overlay mask */
+ mgrq[ix].ovl_mask = comp->ovl_mask & ~comp->ovl_dmask;
+ maskref_decmask(&mgrq[ix].ovl_qmask, comp->ovl_mask);
+
+ if (debug & DEBUG_PHASES)
+ dev_info(DEV(cdev), "[%p] programmed\n", comp);
+ } else if ((status == DSS_COMPLETION_DISPLAYED) &&
+ comp->state == DSSCOMP_STATE_PROGRAMMED) {
+ /* composition is 1st displayed */
+ comp->state = DSSCOMP_STATE_DISPLAYED;
+ log_state(comp, dsscomp_mgr_delayed_cb, status);
+ if (debug & DEBUG_PHASES)
+ dev_info(DEV(cdev), "[%p] displayed\n", comp);
+ } else if (status & DSS_COMPLETION_RELEASED) {
+ /* composition is no longer displayed */
+ log_event(20 * comp->ix + 20, 0, comp, "%pf on %s",
+ (u32) dsscomp_mgr_delayed_cb,
+ (u32) log_status_str(status));
+ dsscomp_drop(comp);
+ }
+ mutex_unlock(&mtx);
+}
+
+static u32 dsscomp_mgr_callback(void *data, int id, int status)
+{
+ struct dsscomp_data *comp = data;
+
+ if (status == DSS_COMPLETION_PROGRAMMED ||
+ (status == DSS_COMPLETION_DISPLAYED &&
+ comp->state != DSSCOMP_STATE_DISPLAYED) ||
+ (status & DSS_COMPLETION_RELEASED)) {
+ struct dsscomp_cb_work *wk = kzalloc(sizeof(*wk), GFP_ATOMIC);
+ wk->comp = comp;
+ wk->status = status;
+ INIT_WORK(&wk->work, dsscomp_mgr_delayed_cb);
+ queue_work(cb_wkq, &wk->work);
+ }
+
+ /* get each callback only once */
+ return ~status;
+}
+
+static inline bool dssdev_manually_updated(struct omap_dss_device *dev)
+{
+ return dev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE &&
+ dev->driver->get_update_mode(dev) != OMAP_DSS_UPDATE_AUTO;
+}
+
+/* apply composition */
+/* at this point the composition is not on any queue */
+static 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;
+ u32 oix;
+ bool cb_programmed = false;
+
+ struct omapdss_ovl_cb cb = {
+ .fn = dsscomp_mgr_callback,
+ .data = comp,
+ .mask = DSS_COMPLETION_DISPLAYED |
+ DSS_COMPLETION_PROGRAMMED | DSS_COMPLETION_RELEASED,
+ };
+
+ BUG_ON(comp->state != DSSCOMP_STATE_APPLYING);
+
+ /* check if the display is valid and used */
+ r = -ENODEV;
+ d = &comp->frm;
+ display_ix = d->mgr.ix;
+ if (display_ix >= cdev->num_displays)
+ goto done;
+ dssdev = cdev->displays[display_ix];
+ if (!dssdev)
+ goto done;
+
+ drv = dssdev->driver;
+ mgr = dssdev->manager;
+ if (!mgr || !drv || mgr->id >= cdev->num_mgrs)
+ goto done;
+
+ dump_comp_info(cdev, d, "apply");
+
+ r = 0;
+ dmask = 0;
+ for (oix = 0; oix < comp->frm.num_ovls; oix++) {
+ struct dss2_ovl_info *oi = comp->ovls + oix;
+
+ /* keep track of disabled overlays */
+ if (!oi->cfg.enabled)
+ dmask |= 1 << oi->cfg.ix;
+
+ if (r && !comp->must_apply)
+ continue;
+
+ dump_ovl_info(cdev, oi);
+
+ if (oi->cfg.ix >= cdev->num_ovls) {
+ r = -EINVAL;
+ continue;
+ }
+ ovl = cdev->ovls[oi->cfg.ix];
+
+ /* set overlays' manager & info */
+ if (ovl->info.enabled && ovl->manager != mgr) {
+ r = -EBUSY;
+ goto skip_ovl_set;
+ }
+ 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 skip_ovl_set;
+ }
+
+ r = set_dss_ovl_info(oi);
+skip_ovl_set:
+ if (r && comp->must_apply) {
+ dev_err(DEV(cdev), "[%p] set ovl%d failed %d", comp,
+ oi->cfg.ix, r);
+ oi->cfg.enabled = false;
+ dmask |= 1 << oi->cfg.ix;
+ set_dss_ovl_info(oi);
+ }
+ }
+
+ /*
+ * set manager's info - this also sets the completion callback,
+ * so if it succeeds, we will use the callback to complete the
+ * composition. Otherwise, we can skip the composition now.
+ */
+ if (!r || comp->must_apply) {
+ r = set_dss_mgr_info(&d->mgr, &cb);
+ cb_programmed = r == 0;
+ }
+
+ if (r && !comp->must_apply) {
+ dev_err(DEV(cdev), "[%p] set failed %d\n", comp, r);
+ goto done;
+ } else {
+ if (r)
+ dev_warn(DEV(cdev), "[%p] ignoring set failure %d\n",
+ comp, r);
+ comp->blank = dmask == comp->ovl_mask;
+ comp->ovl_dmask = dmask;
+
+ /*
+ * Check other overlays that may also use this display.
+ * NOTE: This is only needed in case someone changes
+ * overlays via sysfs. We use comp->ovl_mask to refresh
+ * the overlays actually used on a manager when the
+ * composition is programmed.
+ */
+ for (i = 0; i < cdev->num_ovls; i++) {
+ u32 mask = 1 << i;
+ if ((~comp->ovl_mask & mask) &&
+ cdev->ovls[i]->info.enabled &&
+ cdev->ovls[i]->manager == mgr) {
+ mutex_lock(&mtx);
+ comp->ovl_mask |= mask;
+ maskref_incbit(&mgrq[comp->ix].ovl_qmask, i);
+ mutex_unlock(&mtx);
+ }
+ }
+ }
+
+ /* apply changes and call update on manual panels */
+ /* no need for mutex as no callbacks are scheduled yet */
+ comp->state = DSSCOMP_STATE_APPLIED;
+ log_state(comp, dsscomp_apply, 0);
+
+ if (!d->win.w && !d->win.x)
+ d->win.w = dssdev->panel.timings.x_res - d->win.x;
+ if (!d->win.h && !d->win.y)
+ d->win.h = dssdev->panel.timings.y_res - d->win.y;
+
+ mutex_lock(&mtx);
+ if (mgrq[comp->ix].blanking) {
+ pr_info_ratelimited("ignoring apply mgr(%s) while blanking\n",
+ mgr->name);
+ r = -ENODEV;
+ } else {
+ r = mgr->apply(mgr);
+ if (r)
+ dev_err(DEV(cdev), "failed while applying %d", r);
+ /* keep error if set_mgr_info failed */
+ if (!r && !cb_programmed)
+ r = -EINVAL;
+ }
+ mutex_unlock(&mtx);
+
+ /*
+ * TRICKY: try to unregister callback to see if callbacks have
+ * been applied (moved into DSS2 pipeline). Unregistering also
+ * avoids having to unnecessarily kick out compositions (which
+ * would result in screen blinking). If callbacks failed to apply,
+ * (e.g. could not set them or apply them) we will need to call
+ * them ourselves (we note this by returning an error).
+ */
+ if (cb_programmed && r) {
+ /* clear error if callback already registered */
+ if (omap_dss_manager_unregister_callback(mgr, &cb))
+ r = 0;
+ }
+ /* if failed to apply, kick out prior composition */
+ if (comp->must_apply && r)
+ mgr->blank(mgr, true);
+
+ if (!r && (d->mode & DSSCOMP_SETUP_MODE_DISPLAY)) {
+ /* cannot handle update errors, so ignore them */
+ if (dssdev_manually_updated(dssdev) && drv->update)
+ drv->update(dssdev, d->win.x,
+ d->win.y, d->win.w, d->win.h);
+ else
+ /* wait for sync to do smooth animations */
+ mgr->wait_for_vsync(mgr);
+ }
+
+done:
+ return r;
+}
+
+struct dsscomp_apply_work {
+ struct work_struct work;
+ dsscomp_t comp;
+};
+
+int dsscomp_state_notifier(struct notifier_block *nb,
+ unsigned long arg, void *ptr)
+{
+ struct omap_dss_device *dssdev = ptr;
+ enum omap_dss_display_state state = arg;
+ struct omap_overlay_manager *mgr = dssdev->manager;
+ if (mgr) {
+ mutex_lock(&mtx);
+ if (state == OMAP_DSS_DISPLAY_DISABLED) {
+ mgr->blank(mgr, true);
+ mgrq[mgr->id].blanking = true;
+ } else if (state == OMAP_DSS_DISPLAY_ACTIVE) {
+ mgrq[mgr->id].blanking = false;
+ }
+ mutex_unlock(&mtx);
+ }
+ return 0;
+}
+
+
+static void dsscomp_do_apply(struct work_struct *work)
+{
+ struct dsscomp_apply_work *wk = container_of(work, typeof(*wk), work);
+ /* complete compositions that failed to apply */
+ if (dsscomp_apply(wk->comp))
+ dsscomp_mgr_callback(wk->comp, -1, DSS_COMPLETION_ECLIPSED_SET);
+ kfree(wk);
+}
+
+int dsscomp_delayed_apply(dsscomp_t comp)
+{
+ /* don't block in case we are called from interrupt context */
+ struct dsscomp_apply_work *wk = kzalloc(sizeof(*wk), GFP_NOWAIT);
+ if (!wk)
+ return -ENOMEM;
+
+ mutex_lock(&mtx);
+
+ BUG_ON(comp->state != DSSCOMP_STATE_ACTIVE);
+ comp->state = DSSCOMP_STATE_APPLYING;
+ log_state(comp, dsscomp_delayed_apply, 0);
+
+ if (debug & DEBUG_PHASES)
+ dev_info(DEV(cdev), "[%p] applying\n", comp);
+ mutex_unlock(&mtx);
+
+ wk->comp = comp;
+ INIT_WORK(&wk->work, dsscomp_do_apply);
+ return queue_work(mgrq[comp->ix].apply_workq, &wk->work) ? 0 : -EBUSY;
+}
+EXPORT_SYMBOL(dsscomp_delayed_apply);
+
+/*
+ * ===========================================================================
+ * DEBUGFS
+ * ===========================================================================
+ */
+
+#ifdef CONFIG_DEBUG_FS
+void seq_print_comp(struct seq_file *s, dsscomp_t c)
+{
+ struct dsscomp_setup_mgr_data *d = &c->frm;
+ int i;
+
+ seq_printf(s, " [%p]: %s%s\n", c, c->blank ? "blank " : "",
+ c->state == DSSCOMP_STATE_ACTIVE ? "ACTIVE" :
+ c->state == DSSCOMP_STATE_APPLYING ? "APPLYING" :
+ c->state == DSSCOMP_STATE_APPLIED ? "APPLIED" :
+ c->state == DSSCOMP_STATE_PROGRAMMED ? "PROGRAMMED" :
+ c->state == DSSCOMP_STATE_DISPLAYED ? "DISPLAYED" :
+ "???");
+ seq_printf(s, " sync_id=%x, flags=%c%c%c\n",
+ d->sync_id,
+ (d->mode & DSSCOMP_SETUP_MODE_APPLY) ? 'A' : '-',
+ (d->mode & DSSCOMP_SETUP_MODE_DISPLAY) ? 'D' : '-',
+ (d->mode & DSSCOMP_SETUP_MODE_CAPTURE) ? 'C' : '-');
+ for (i = 0; i < d->num_ovls; i++) {
+ struct dss2_ovl_info *oi;
+ struct dss2_ovl_cfg *g;
+ oi = d->ovls + i;
+ g = &oi->cfg;
+ if (g->zonly) {
+ seq_printf(s, " ovl%d={%s z%d}\n",
+ g->ix, g->enabled ? "ON" : "off", g->zorder);
+ } else {
+ seq_printf(s, " ovl%d={%s z%d %s%s *%d%%"
+ " %d*%d:%d,%d+%d,%d rot%d%s"
+ " => %d,%d+%d,%d %p/%p|%d}\n",
+ g->ix, g->enabled ? "ON" : "off", g->zorder,
+ dsscomp_get_color_name(g->color_mode) ? : "N/A",
+ g->pre_mult_alpha ? " premult" : "",
+ (g->global_alpha * 100 + 128) / 255,
+ g->width, g->height, g->crop.x, g->crop.y,
+ g->crop.w, g->crop.h,
+ g->rotation, g->mirror ? "+mir" : "",
+ g->win.x, g->win.y, g->win.w, g->win.h,
+ (void *) oi->ba, (void *) oi->uv, g->stride);
+ }
+ }
+ if (c->extra_cb)
+ seq_printf(s, " gsync=[%p] %pf\n\n", c->extra_cb_data,
+ c->extra_cb);
+ else
+ seq_printf(s, " gsync=[%p] (called)\n\n", c->extra_cb_data);
+}
+#endif
+
+void dsscomp_dbg_comps(struct seq_file *s)
+{
+#ifdef CONFIG_DEBUG_FS
+ dsscomp_t c;
+ u32 i;
+
+ mutex_lock(&dbg_mtx);
+ for (i = 0; i < cdev->num_mgrs; i++) {
+ struct omap_overlay_manager *mgr = cdev->mgrs[i];
+ seq_printf(s, "ACTIVE COMPOSITIONS on %s\n\n", mgr->name);
+ list_for_each_entry(c, &dbg_comps, dbg_q) {
+ struct dss2_mgr_info *mi = &c->frm.mgr;
+ if (mi->ix < cdev->num_displays &&
+ cdev->displays[mi->ix]->manager == mgr)
+ seq_print_comp(s, c);
+ }
+
+ /* print manager cache */
+ mgr->dump_cb(mgr, s);
+ }
+ mutex_unlock(&dbg_mtx);
+#endif
+}
+
+void dsscomp_dbg_events(struct seq_file *s)
+{
+#ifdef CONFIG_DSSCOMP_DEBUG_LOG
+ u32 i;
+ struct dbg_event_t *d;
+
+ mutex_lock(&dbg_mtx);
+ for (i = dbg_event_ix; i < dbg_event_ix + ARRAY_SIZE(dbg_events); i++) {
+ d = dbg_events + (i % ARRAY_SIZE(dbg_events));
+ if (!d->ms)
+ continue;
+ seq_printf(s, "[% 5d.%03d] %*s[%08x] ",
+ d->ms / 1000, d->ms % 1000,
+ d->ix + ((u32) d->data) % 7,
+ "", (u32) d->data);
+ seq_printf(s, d->fmt, d->a1, d->a2);
+ seq_printf(s, "\n");
+ }
+ mutex_unlock(&dbg_mtx);
+#endif
+}
+
+/*
+ * ===========================================================================
+ * EXIT
+ * ===========================================================================
+ */
+void dsscomp_queue_exit(void)
+{
+ if (cdev) {
+ int i;
+ for (i = 0; i < cdev->num_displays; i++)
+ destroy_workqueue(mgrq[i].apply_workq);
+ destroy_workqueue(cb_wkq);
+ cdev = NULL;
+ }
+}
+EXPORT_SYMBOL(dsscomp_queue_exit);
diff --git a/drivers/video/omap2/hdcp/Kconfig b/drivers/video/omap2/hdcp/Kconfig
new file mode 100644
index 0000000..a18f183
--- /dev/null
+++ b/drivers/video/omap2/hdcp/Kconfig
@@ -0,0 +1,14 @@
+menuconfig OMAP4_HDCP
+ bool "OMAP4 HDCP support"
+ depends on OMAP2_DSS && OMAP4_DSS_HDMI
+ default n
+ help
+ HDCP Interface. This adds the High Definition Content Protection Interface.
+ See http://www.digital-cp.com/ for HDCP specification.
+
+config OMAP4_HDCP_DEBUG
+ bool "OMAP4 HDCP Debugging"
+ depends on OMAP4_HDCP
+ default n
+ help
+ Enableds verbose debugging the the HDCP drivers
diff --git a/drivers/video/omap2/hdcp/Makefile b/drivers/video/omap2/hdcp/Makefile
new file mode 100644
index 0000000..80b0c0c
--- /dev/null
+++ b/drivers/video/omap2/hdcp/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for HDCP linux kernel module.
+#
+
+ccflags-$(CONFIG_OMAP4_HDCP_DEBUG) = -DDEBUG -DHDCP_DEBUG
+
+obj-$(CONFIG_OMAP4_HDCP) += hdcp.o
+hdcp-y := hdcp_top.o hdcp_lib.o hdcp_ddc.o
diff --git a/drivers/video/omap2/hdcp/hdcp.h b/drivers/video/omap2/hdcp/hdcp.h
new file mode 100644
index 0000000..e22a588
--- /dev/null
+++ b/drivers/video/omap2/hdcp/hdcp.h
@@ -0,0 +1,394 @@
+/*
+ * hdcp.h
+ *
+ * HDCP interface DSS driver setting for TI's OMAP4 family of processor.
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Authors: Fabrice Olivero
+ * Fabrice Olivero <f-olivero@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 _HDCP_H_
+#define _HDCP_H_
+
+
+/********************************/
+/* Structures related to ioctl */
+/********************************/
+
+/* HDCP key size in 32-bit words */
+#define DESHDCP_KEY_SIZE 160
+
+/* HDCP ioctl */
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+struct hdcp_encrypt_control {
+ uint32_t in_key[DESHDCP_KEY_SIZE];
+ uint32_t *out_key;
+};
+
+struct hdcp_enable_control {
+ uint32_t key[DESHDCP_KEY_SIZE];
+ int nb_retry;
+};
+
+#define MAX_SHA_DATA_SIZE 645
+#define MAX_SHA_VPRIME_SIZE 20
+
+struct hdcp_sha_in {
+ uint8_t data[MAX_SHA_DATA_SIZE];
+ uint32_t byte_counter;
+ uint8_t vprime[MAX_SHA_VPRIME_SIZE];
+};
+
+struct hdcp_wait_control {
+ uint32_t event;
+ struct hdcp_sha_in *data;
+};
+
+/* HDCP ioctl */
+#define HDCP_IOCTL_MAGIC 'h'
+#define HDCP_ENABLE _IOW(HDCP_IOCTL_MAGIC, 0, \
+ struct hdcp_enable_control)
+#define HDCP_DISABLE _IO(HDCP_IOCTL_MAGIC, 1)
+#define HDCP_ENCRYPT_KEY _IOWR(HDCP_IOCTL_MAGIC, 2, \
+ struct hdcp_encrypt_control)
+#define HDCP_QUERY_STATUS _IOWR(HDCP_IOCTL_MAGIC, 3, uint32_t)
+#define HDCP_WAIT_EVENT _IOWR(HDCP_IOCTL_MAGIC, 4, \
+ struct hdcp_wait_control)
+#define HDCP_DONE _IOW(HDCP_IOCTL_MAGIC, 5, uint32_t)
+
+/* HDCP state */
+#define HDCP_STATE_DISABLED 0
+#define HDCP_STATE_INIT 1
+#define HDCP_STATE_AUTH_1ST_STEP 2
+#define HDCP_STATE_AUTH_2ND_STEP 3
+#define HDCP_STATE_AUTH_3RD_STEP 4
+#define HDCP_STATE_AUTH_FAIL_RESTARTING 5
+#define HDCP_STATE_AUTH_FAILURE 6
+
+/* HDCP events */
+#define HDCP_EVENT_STEP1 (1 << 0x0)
+#define HDCP_EVENT_STEP2 (1 << 0x1)
+#define HDCP_EVENT_EXIT (1 << 0x2)
+
+/* HDCP user space status */
+#define HDCP_US_NO_ERR (0 << 8)
+#define HDCP_US_FAILURE (1 << 8)
+
+#ifdef __KERNEL__
+
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+
+#define _9032_AUTO_RI_ /* Auto Ri mode */
+#define _9032_BCAP_ /* BCAP polling */
+#undef _9032_AN_STOP_FIX_
+
+#ifdef DEBUG
+#define DDC_DBG /* Log DDC data */
+#undef POWER_TRANSITION_DBG /* Add wait loops to allow testing DSS power
+ * transition during HDCP */
+#endif
+
+/***************************/
+/* HW specific definitions */
+/***************************/
+
+/* DESHDCP base address */
+/*----------------------*/
+
+#define DSS_SS_FROM_L3__DESHDCP 0x58007000
+
+/* DESHDCP registers */
+#define DESHDCP__DHDCP_CTRL 0x020
+#define DESHDCP__DHDCP_DATA_L 0x024
+#define DESHDCP__DHDCP_DATA_H 0x028
+
+/* DESHDCP CTRL bits */
+#define DESHDCP__DHDCP_CTRL__DIRECTION_POS_F 2
+#define DESHDCP__DHDCP_CTRL__DIRECTION_POS_L 2
+
+#define DESHDCP__DHDCP_CTRL__OUTPUT_READY_POS_F 0
+#define DESHDCP__DHDCP_CTRL__OUTPUT_READY_POS_L 0
+
+/* HDMI WP base address */
+/*----------------------*/
+#define HDMI_WP 0x58006000
+
+/* HDMI CORE SYSTEM base address */
+/*-------------------------------*/
+
+#define HDMI_IP_CORE_SYSTEM 0x400
+
+/* HDMI CORE registers */
+#define HDMI_IP_CORE_SYSTEM__DCTL 0x034
+
+#define HDMI_IP_CORE_SYSTEM__HDCP_CTRL 0x03C
+
+#define HDMI_IP_CORE_SYSTEM__BKSV0 0x040
+
+#define HDMI_IP_CORE_SYSTEM__AN0 0x054
+
+#define HDMI_IP_CORE_SYSTEM__AKSV0 0x074
+
+#define HDMI_IP_CORE_SYSTEM__R1 0x088
+#define HDMI_IP_CORE_SYSTEM__R2 0x08C
+
+#define HDMI_IP_CORE_SYSTEM__RI_CMD 0x09C
+#define HDMI_IP_CORE_SYSTEM__RI_STAT 0x098
+
+#define HDMI_IP_CORE_SYSTEM__INTR2 0x1C8
+#define HDMI_IP_CORE_SYSTEM__INTR3 0x1CC
+
+#define HDMI_IP_CORE_SYSTEM__INT_UNMASK2 0x1D8
+#define HDMI_IP_CORE_SYSTEM__INT_UNMASK3 0x1DC
+
+#define HDMI_IP_CORE_SYSTEM__SHA_CTRL 0x330
+
+#define HDMI_IP_CORE_SYSTEM__INTR2__BCAP 0x80
+#define HDMI_IP_CORE_SYSTEM__INTR3__RI_ERR 0xF0
+
+enum hdcp_repeater {
+ HDCP_RECEIVER = 0,
+ HDCP_REPEATER = 1
+};
+
+enum encryption_state {
+ HDCP_ENC_OFF = 0x0,
+ HDCP_ENC_ON = 0x1
+};
+
+/* HDMI CORE AV base address */
+/*---------------------------*/
+
+#define HDMI_CORE_AV_BASE 0x900
+#ifndef HDMI_CORE_AV_HDMI_CTRL
+#define HDMI_CORE_AV_HDMI_CTRL 0x0BC
+#define HDMI_CORE_AV_PB_CTRL2 0x0FC
+#define HDMI_CORE_AV_CP_BYTE1 0x37C
+#endif
+
+#define HDMI_CORE_AV_HDMI_CTRL__HDMI_MODE 0x01
+
+enum av_mute {
+ AV_MUTE_SET = 0x01,
+ AV_MUTE_CLEAR = 0x10
+};
+/***********************/
+/* HDCP DDC addresses */
+/***********************/
+
+#define DDC_BKSV_ADDR 0x00
+#define DDC_Ri_ADDR 0x08
+#define DDC_AKSV_ADDR 0x10
+#define DDC_AN_ADDR 0x18
+#define DDC_V_ADDR 0x20
+#define DDC_BCAPS_ADDR 0x40
+#define DDC_BSTATUS_ADDR 0x41
+#define DDC_KSV_FIFO_ADDR 0x43
+
+#define DDC_BKSV_LEN 5
+#define DDC_Ri_LEN 2
+#define DDC_AKSV_LEN 5
+#define DDC_AN_LEN 8
+#define DDC_V_LEN 20
+#define DDC_BCAPS_LEN 1
+#define DDC_BSTATUS_LEN 2
+
+#define DDC_BIT_REPEATER 6
+
+#define DDC_BSTATUS0_MAX_DEVS 0x80
+#define DDC_BSTATUS0_DEV_COUNT 0x7F
+#define DDC_BSTATUS1_MAX_CASC 0x08
+
+/***************************/
+/* Definitions */
+/***************************/
+
+/* Status / error codes */
+#define HDCP_OK 0
+#define HDCP_DDC_ERROR 1
+#define HDCP_AUTH_FAILURE 2
+#define HDCP_AKSV_ERROR 3
+#define HDCP_3DES_ERROR 4
+#define HDCP_SHA1_ERROR 5
+#define HDCP_DRIVER_ERROR 6
+#define HDCP_CANCELLED_AUTH 7
+
+#define HDCP_INFINITE_REAUTH 0x100
+#define HDCP_MAX_DDC_ERR 5
+
+/* FIXME: should be 300ms delay between HDMI start frame event and HDCP enable
+ * (to respect 7 VSYNC delay in 24 Hz)
+ */
+#define HDCP_ENABLE_DELAY 300
+#define HDCP_R0_DELAY 110
+#define HDCP_KSV_TIMEOUT_DELAY 5000
+#define HDCP_REAUTH_DELAY 100
+
+/* DDC access timeout in ms */
+#define HDCP_DDC_TIMEOUT 500
+#define HDCP_STOP_FRAME_BLOCKING_TIMEOUT (2*HDCP_DDC_TIMEOUT)
+
+/* Event source */
+#define HDCP_SRC_SHIFT 8
+#define HDCP_IOCTL_SRC (0x1 << HDCP_SRC_SHIFT)
+#define HDCP_HDMI_SRC (0x2 << HDCP_SRC_SHIFT)
+#define HDCP_IRQ_SRC (0x4 << HDCP_SRC_SHIFT)
+#define HDCP_WORKQUEUE_SRC (0x8 << HDCP_SRC_SHIFT)
+
+/* Workqueue events */
+/* Note: HDCP_ENABLE_CTL, HDCP_R0_EXP_EVENT, HDCP_KSV_TIMEOUT_EVENT and
+ * HDCP_AUTH_REATT_EVENT can be cancelled by HDCP disabling
+ */
+#define HDCP_ENABLE_CTL (HDCP_IOCTL_SRC | 0)
+#define HDCP_DISABLE_CTL (HDCP_IOCTL_SRC | 1)
+#define HDCP_START_FRAME_EVENT (HDCP_HDMI_SRC | 2)
+#define HDCP_STOP_FRAME_EVENT (HDCP_HDMI_SRC | 3)
+#define HDCP_HPD_LOW_EVENT (HDCP_IRQ_SRC | 4)
+#define HDCP_RI_FAIL_EVENT (HDCP_IRQ_SRC | 5)
+#define HDCP_KSV_LIST_RDY_EVENT (HDCP_IRQ_SRC | 6)
+#define HDCP_R0_EXP_EVENT (HDCP_WORKQUEUE_SRC | 7)
+#define HDCP_KSV_TIMEOUT_EVENT (HDCP_WORKQUEUE_SRC | 8)
+#define HDCP_AUTH_REATT_EVENT (HDCP_WORKQUEUE_SRC | 9)
+
+/* IRQ status */
+#define HDCP_IRQ_RI_FAIL 0x01
+#define HDCP_IRQ_KSV_RDY 0x02
+
+enum hdcp_states {
+ HDCP_DISABLED,
+ HDCP_ENABLE_PENDING,
+ HDCP_AUTHENTICATION_START,
+ HDCP_WAIT_R0_DELAY,
+ HDCP_WAIT_KSV_LIST,
+ HDCP_LINK_INTEGRITY_CHECK,
+ HDCP_KEY_ENCRYPTION_ONGOING
+};
+
+enum hdmi_states {
+ HDMI_STOPPED,
+ HDMI_STARTED
+};
+
+struct hdcp_delayed_work {
+ struct delayed_work work;
+ int event;
+};
+
+struct hdcp {
+ void __iomem *hdmi_wp_base_addr;
+ void __iomem *deshdcp_base_addr;
+ struct mutex lock;
+ struct hdcp_enable_control *en_ctrl;
+ dev_t dev_id;
+ struct class *hdcp_class;
+ enum hdmi_states hdmi_state;
+ enum hdcp_states hdcp_state;
+ int auth_state;
+ struct delayed_work *pending_start;
+ /* Following variable should store works submitted from workqueue
+ * context
+ * WARNING: only ONE work at a time can be stored (no conflict
+ * should happen). It is used to allow cancelled pending works when
+ * disabling HDCP
+ */
+ struct delayed_work *pending_wq_event;
+ int retry_cnt;
+ int dss_state;
+ int pending_disable;
+ int hdmi_restart;
+ int hpd_low;
+ spinlock_t spinlock;
+ struct workqueue_struct *workqueue;
+ int hdcp_up_event;
+ int hdcp_down_event;
+ bool hdcp_keys_loaded;
+};
+
+extern struct hdcp hdcp;
+extern struct hdcp_sha_in sha_input;
+
+
+/***************************/
+/* Macros for accessing HW */
+/***************************/
+
+#define WR_REG_32(base, offset, val) __raw_writel(val, base + offset)
+#define RD_REG_32(base, offset) __raw_readl(base + offset)
+
+
+#undef FLD_MASK
+#define FLD_MASK(start, end) (((1 << (start - end + 1)) - 1) << (end))
+#undef FLD_VAL
+#define FLD_VAL(val, start, end) (((val) << end) & FLD_MASK(start, end))
+#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end))
+#define FLD_MOD(orig, val, start, end) \
+ (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end))
+
+#define WR_FIELD_32(base, offset, start, end, val) \
+ WR_REG_32(base, offset, FLD_MOD(RD_REG_32(base, offset), val, \
+ start, end))
+
+#define RD_FIELD_32(base, offset, start, end) \
+ ((RD_REG_32(base, offset) & FLD_MASK(start, end)) >> (end))
+
+
+#undef DBG
+
+#ifdef HDCP_DEBUG
+#define DBG(format, ...) \
+ printk(KERN_DEBUG "HDCP: " format "\n", ## __VA_ARGS__)
+#else
+#define DBG(format, ...)
+#endif
+
+/***************************/
+/* Function prototypes */
+/***************************/
+
+int hdcp_user_space_task(int flags);
+
+/* 3DES */
+int hdcp_3des_load_key(uint32_t *deshdcp_encrypted_key);
+void hdcp_3des_encrypt_key(struct hdcp_encrypt_control *enc_ctrl,
+ uint32_t out_key[DESHDCP_KEY_SIZE]);
+
+/* IP control */
+int hdcp_lib_disable(void);
+int hdcp_lib_step1_start(void);
+int hdcp_lib_step1_r0_check(void);
+int hdcp_lib_step2(void);
+int hdcp_lib_irq(void);
+void hdcp_lib_auto_ri_check(bool state);
+void hdcp_lib_auto_bcaps_rdy_check(bool state);
+void hdcp_lib_set_av_mute(enum av_mute av_mute_state);
+void hdcp_lib_set_encryption(enum encryption_state enc_state);
+u8 hdcp_lib_check_repeater_bit_in_tx(void);
+
+/* DDC */
+int hdcp_ddc_read(u16 no_bytes, u8 addr, u8 *pdata);
+int hdcp_ddc_write(u16 no_bytes, u8 addr, u8 *pdata);
+void hdcp_ddc_abort(void);
+
+#endif /* __KERNEL__ */
+
+#endif /* _HDCP_H_ */
diff --git a/drivers/video/omap2/hdcp/hdcp_ddc.c b/drivers/video/omap2/hdcp/hdcp_ddc.c
new file mode 100644
index 0000000..e5104fa
--- /dev/null
+++ b/drivers/video/omap2/hdcp/hdcp_ddc.c
@@ -0,0 +1,310 @@
+/*
+ * hdcp_ddc.c
+ *
+ * HDCP interface DSS driver setting for TI's OMAP4 family of processor.
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Authors: Fabrice Olivero
+ * Fabrice Olivero <f-olivero@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/delay.h>
+#include "hdcp.h"
+#include "hdcp_ddc.h"
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_suspend_resume_auto_ri
+ *-----------------------------------------------------------------------------
+ */
+static int hdcp_suspend_resume_auto_ri(enum ri_suspend_resume state)
+{
+ static u8 OldRiStat, OldRiCommand;
+ u8 TimeOut = 10;
+
+ /* Suspend Auto Ri in order to allow FW access MDDC bus.
+ * Poll 0x72:0x26[0] for MDDC bus availability or timeout
+ */
+
+ DBG("hdcp_suspend_resume_auto_ri() state=%s",
+ state == AUTO_RI_SUSPEND ? "SUSPEND" : "RESUME");
+
+ if (state == AUTO_RI_SUSPEND) {
+ /* Save original Auto Ri state */
+ OldRiCommand = RD_FIELD_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__RI_CMD, 0, 0);
+
+ /* Disable Auto Ri */
+ hdcp_lib_auto_ri_check(false);
+
+ /* Wait for HW to release MDDC bus */
+ /* TODO: while loop / timeout to be enhanced */
+ while (--TimeOut) {
+ if (!RD_FIELD_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__RI_STAT, 0, 0))
+ break;
+ }
+
+ /* MDDC bus not relinquished */
+ if (!TimeOut) {
+ printk(KERN_ERR "HDCP: Suspending Auto Ri failed !\n");
+ return -HDCP_DDC_ERROR;
+ }
+
+ OldRiStat = RD_FIELD_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__RI_STAT, 0, 0);
+ } else {
+ /* If Auto Ri was enabled before it was suspended */
+ if ((OldRiStat) && (OldRiCommand))
+ /* Re-enable Auto Ri */
+ hdcp_lib_auto_ri_check(false);
+ }
+
+ return HDCP_OK;
+}
+
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_start_ddc_transfer
+ *-----------------------------------------------------------------------------
+ */
+static int hdcp_start_ddc_transfer(mddc_type *mddc_cmd, u8 operation)
+{
+ u8 *cmd = (u8 *)mddc_cmd;
+ struct timeval t0, t1, t2;
+ u32 time_elapsed_ms = 0;
+ u32 i, size;
+ unsigned long flags;
+
+#ifdef _9032_AUTO_RI_
+ if (hdcp_suspend_resume_auto_ri(AUTO_RI_SUSPEND))
+ return -HDCP_DDC_ERROR;
+#endif
+
+ spin_lock_irqsave(&hdcp.spinlock, flags);
+
+ /* Abort Master DDC operation and Clear FIFO pointer */
+ WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_CMD, MASTER_CMD_CLEAR_FIFO);
+
+ /* Read to flush */
+ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_CMD);
+
+ /* Sending DDC header, it'll clear DDC Status register too */
+ for (i = 0; i < 7; i++) {
+ WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_ADDR + i * sizeof(uint32_t),
+ cmd[i]);
+
+ /* Read to flush */
+ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_ADDR +
+ i * sizeof(uint32_t));
+ }
+
+ spin_unlock_irqrestore(&hdcp.spinlock, flags);
+
+ do_gettimeofday(&t1);
+ memcpy(&t0, &t1, sizeof(t0));
+
+ i = 0;
+ size = mddc_cmd->nbytes_lsb + (mddc_cmd->nbytes_msb << 8);
+
+ while ((i < size) && (hdcp.pending_disable == 0)) {
+ if (operation == DDC_WRITE) {
+ /* Write data to DDC FIFO as long as it is NOT full */
+ if (RD_FIELD_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_STATUS, 3, 3)
+ == 0) {
+ WR_REG_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_DATA,
+ mddc_cmd->pdata[i++]);
+ do_gettimeofday(&t1);
+ }
+ } else if (operation == DDC_READ) {
+ /* Read from DDC FIFO as long as it is NOT empty */
+ if (RD_FIELD_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_STATUS, 2, 2)
+ == 0) {
+ mddc_cmd->pdata[i++] =
+ RD_REG_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_DATA);
+ do_gettimeofday(&t1);
+ }
+ }
+
+ do_gettimeofday(&t2);
+ time_elapsed_ms = (t2.tv_sec - t1.tv_sec) * 1000 +
+ (t2.tv_usec - t1.tv_usec) / 1000;
+
+ if (time_elapsed_ms > HDCP_DDC_TIMEOUT) {
+ DBG("DDC timeout - no data during %d ms - "
+ "status=%02x %u",
+ HDCP_DDC_TIMEOUT,
+ RD_REG_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_STATUS),
+ jiffies_to_msecs(jiffies));
+ goto ddc_error;
+ }
+ }
+
+ if (hdcp.pending_disable)
+ goto ddc_abort;
+
+ /* Wait for the FIFO to be empty (end of transfer) */
+ while ((RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_STATUS) != 0x4) &&
+ (hdcp.pending_disable == 0)) {
+ do_gettimeofday(&t2);
+ time_elapsed_ms = (t2.tv_sec - t1.tv_sec) * 1000 +
+ (t2.tv_usec - t1.tv_usec) / 1000;
+
+ if (time_elapsed_ms > HDCP_DDC_TIMEOUT) {
+ DBG("DDC timeout - FIFO not getting empty - "
+ "status=%02x",
+ RD_REG_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_STATUS));
+ goto ddc_error;
+ }
+ }
+
+ if (hdcp.pending_disable)
+ goto ddc_abort;
+
+ DBG("DDC transfer: bytes: %d time_us: %lu status: %x",
+ i,
+ (t2.tv_sec - t0.tv_sec) * 1000000 + (t2.tv_usec - t0.tv_usec),
+ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_STATUS));
+
+#ifdef DDC_DBG
+ {
+ int k;
+ for (k = 0; k < i; k++)
+ printk(KERN_DEBUG "%02x ", mddc_cmd->pdata[k]);
+ printk(KERN_DEBUG "\n");
+ }
+#endif
+
+#ifdef _9032_AUTO_RI_
+ /* Re-enable Auto Ri */
+ if (hdcp_suspend_resume_auto_ri(AUTO_RI_RESUME))
+ return -HDCP_DDC_ERROR;
+#endif
+
+ return HDCP_OK;
+
+ddc_error:
+ hdcp_ddc_abort();
+ return -HDCP_DDC_ERROR;
+
+ddc_abort:
+ DBG("DDC transfer aborted - status=%02x",
+ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_STATUS));
+
+ return HDCP_OK;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_ddc_operation
+ *-----------------------------------------------------------------------------
+ */
+static int hdcp_ddc_operation(u16 no_bytes, u8 addr, u8 *pdata,
+ enum ddc_operation operation)
+{
+ mddc_type mddc;
+
+ mddc.slaveAddr = HDCPRX_SLV;
+ mddc.offset = 0;
+ mddc.regAddr = addr;
+ mddc.nbytes_lsb = no_bytes & 0xFF;
+ mddc.nbytes_msb = (no_bytes & 0x300) >> 8;
+ mddc.dummy = 0;
+ mddc.pdata = pdata;
+
+ if (operation == DDC_READ)
+ mddc.cmd = MASTER_CMD_SEQ_RD;
+ else
+ mddc.cmd = MASTER_CMD_SEQ_WR;
+
+ DBG("DDC %s: offset=%02x len=%d %u", operation == DDC_READ ?
+ "READ" : "WRITE",
+ addr, no_bytes,
+ jiffies_to_msecs(jiffies));
+
+ return hdcp_start_ddc_transfer(&mddc, operation);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_ddc_read
+ *-----------------------------------------------------------------------------
+ */
+int hdcp_ddc_read(u16 no_bytes, u8 addr, u8 *pdata)
+{
+ return hdcp_ddc_operation(no_bytes, addr, pdata, DDC_READ);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_ddc_write
+ *-----------------------------------------------------------------------------
+ */
+int hdcp_ddc_write(u16 no_bytes, u8 addr, u8 *pdata)
+{
+ return hdcp_ddc_operation(no_bytes, addr, pdata, DDC_WRITE);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_ddc_abort
+ *-----------------------------------------------------------------------------
+ */
+void hdcp_ddc_abort(void)
+{
+ unsigned long flags;
+
+ /* In case of I2C_NO_ACK error, do not abort DDC to avoid
+ * DDC lockup
+ */
+ if (RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_STATUS) & 0x20)
+ return;
+
+ spin_lock_irqsave(&hdcp.spinlock, flags);
+
+ /* Abort Master DDC operation and Clear FIFO pointer */
+ WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_CMD, MASTER_CMD_ABORT);
+
+ /* Read to flush */
+ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_CMD);
+
+ WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_CMD, MASTER_CMD_CLEAR_FIFO);
+
+ /* Read to flush */
+ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__DDC_CMD);
+
+ spin_unlock_irqrestore(&hdcp.spinlock, flags);
+}
diff --git a/drivers/video/omap2/hdcp/hdcp_ddc.h b/drivers/video/omap2/hdcp/hdcp_ddc.h
new file mode 100644
index 0000000..83bae23
--- /dev/null
+++ b/drivers/video/omap2/hdcp/hdcp_ddc.h
@@ -0,0 +1,111 @@
+/*
+ * hdcp_ddc.h
+ *
+ * HDCP interface DSS driver setting for TI's OMAP4 family of processor.
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Authors: Fabrice Olivero
+ * Fabrice Olivero <f-olivero@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 HDCPRX_SLV 0x74
+
+#define MASTER_BASE 0xEC
+#define MDDC_MANUAL_ADDR 0xEC
+#define MDDC_SLAVE_ADDR 0xED
+#define MDDC_SEGMENT_ADDR 0xEE
+#define MDDC_OFFSET_ADDR 0xEF
+#define MDDC_DIN_CNT_LSB_ADDR 0xF0
+#define MDDC_DIN_CNT_MSB_ADDR 0xF1
+#define MDDC_STATUS_ADDR 0xF2
+#define MDDC_COMMAND_ADDR 0xF3
+#define MDDC_FIFO_ADDR 0xF4
+#define MDDC_FIFO_CNT_ADDR 0xF5
+
+#define BIT_MDDC_ST_IN_PROGR 0x10
+#define BIT_MDDC_ST_I2C_LOW 0x40
+#define BIT_MDDC_ST_NO_ACK 0x20
+
+/* DDC Command[3:0]:
+ *
+ * 1111 - Abort transaction
+ * 1001 - Clear FIFO
+ * 1010 - Clock SCL
+ * 0000 - Current address read with no ACK on last byte
+ * 0001 - Current address read with ACK on last byte
+ * 0010 - Sequential read with no ACK on last byte
+ * 0011 - Sequential read with ACK on last byte
+ * 0100 - Enhanced DDC read with no ACK on last byte
+ * 0101 - Enhanced DDC read with ACK on last byte
+ * 0110 - Sequential write ignoring ACK on last byte
+ * 0111 - Sequential write requiring ACK on last byte
+ */
+
+#define MASTER_CMD_ABORT 0x0f
+#define MASTER_CMD_CLEAR_FIFO 0x09
+#define MASTER_CMD_CLOCK 0x0a
+#define MASTER_CMD_CUR_RD 0x00
+#define MASTER_CMD_SEQ_RD 0x02
+#define MASTER_CMD_ENH_RD 0x04
+#define MASTER_CMD_SEQ_WR 0x06
+
+#define MASTER_FIFO_WR_USE 0x01
+#define MASTER_FIFO_RD_USE 0x02
+#define MASTER_FIFO_EMPTY 0x04
+#define MASTER_FIFO_FULL 0x08
+#define MASTER_DDC_BUSY 0x10
+#define MASTER_DDC_NOACK 0x20
+#define MASTER_DDC_STUCK 0x40
+#define MASTER_DDC_RSVD 0x80
+
+/* OMAP 4 HDMI TRM: */
+#define HDMI_IP_CORE_SYSTEM__DDC_MAN 0x3B0
+#define HDMI_IP_CORE_SYSTEM__DDC_ADDR 0x3B4
+#define HDMI_IP_CORE_SYSTEM__DDC_SEGM 0x3B8
+#define HDMI_IP_CORE_SYSTEM__DDC_OFFSET 0x3BC
+#define HDMI_IP_CORE_SYSTEM__DDC_COUNT1 0x3C0
+#define HDMI_IP_CORE_SYSTEM__DDC_COUNT2 0x3C4
+#define HDMI_IP_CORE_SYSTEM__DDC_STATUS 0x3C8
+#define HDMI_IP_CORE_SYSTEM__DDC_CMD 0x3CC
+#define HDMI_IP_CORE_SYSTEM__DDC_DATA 0x3D0
+#define HDMI_IP_CORE_SYSTEM__DDC_FIFOCNT 0x3D4
+
+#define IIC_OK 0
+#define _IIC_CAPTURED 1
+#define _IIC_NOACK 2
+#define _MDDC_CAPTURED 3
+#define _MDDC_NOACK 4
+#define _MDDC_FIFO_FULL 5
+
+typedef struct {
+ u8 slaveAddr;
+ u8 offset; /* "offset = DDC_SEGM register" */
+ u8 regAddr;
+ u8 nbytes_lsb;
+ u8 nbytes_msb;
+ u8 dummy;
+ u8 cmd;
+ u8 *pdata;
+ u8 data[6];
+} mddc_type;
+
+enum ddc_operation {
+ DDC_READ,
+ DDC_WRITE
+};
+
+enum ri_suspend_resume {
+ AUTO_RI_SUSPEND,
+ AUTO_RI_RESUME
+};
diff --git a/drivers/video/omap2/hdcp/hdcp_lib.c b/drivers/video/omap2/hdcp/hdcp_lib.c
new file mode 100644
index 0000000..6e7850c
--- /dev/null
+++ b/drivers/video/omap2/hdcp/hdcp_lib.c
@@ -0,0 +1,838 @@
+/*
+ * hdcp_lib.c
+ *
+ * HDCP interface DSS driver setting for TI's OMAP4 family of processor.
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Authors: Fabrice Olivero
+ * Fabrice Olivero <f-olivero@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/delay.h>
+#include <mach/omap4-common.h>
+#include <linux/dma-mapping.h>
+#include "hdcp.h"
+
+static void hdcp_lib_read_an(u8 *an);
+static void hdcp_lib_read_aksv(u8 *ksv_data);
+static void hdcp_lib_write_bksv(u8 *ksv_data);
+static void hdcp_lib_generate_an(u8 *an);
+static int hdcp_lib_r0_check(void);
+static int hdcp_lib_sha_bstatus(struct hdcp_sha_in *sha);
+static void hdcp_lib_set_repeater_bit_in_tx(enum hdcp_repeater rx_mode);
+static void hdcp_lib_toggle_repeater_bit_in_tx(void);
+static int hdcp_lib_initiate_step1(void);
+static int hdcp_lib_check_ksv(uint8_t ksv[5]);
+
+#define PPA_SERVICE_HDCP_READ_M0 0x30
+#define PPA_SERVICE_HDCP_CHECK_V 0x31
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_read_an
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_lib_read_an(u8 *an)
+{
+ u8 i;
+
+ for (i = 0; i < 8; i++) {
+ an[i] = (RD_REG_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__AN0 +
+ i * sizeof(uint32_t))) & 0xFF;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_read_aksv
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_lib_read_aksv(u8 *ksv_data)
+{
+ u8 i;
+ for (i = 0; i < 5; i++) {
+ ksv_data[i] = RD_REG_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__AKSV0 +
+ i * sizeof(uint32_t));
+
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_write_bksv
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_lib_write_bksv(u8 *ksv_data)
+{
+ u8 i;
+ for (i = 0; i < 5; i++) {
+ WR_REG_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__BKSV0 +
+ i * sizeof(uint32_t), ksv_data[i]);
+ }
+}
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_generate_an
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_lib_generate_an(u8 *an)
+{
+ /* Generate An using HDCP HW */
+ DBG("hdcp_lib_generate_an()");
+
+ /* Start AN Gen */
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 3, 3, 0);
+
+ /* Delay of 10 ms */
+ mdelay(10);
+
+ /* Stop AN Gen */
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 3, 3, 1);
+
+ /* Must set 0x72:0x0F[3] twice to guarantee that takes effect */
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 3, 3, 1);
+
+ hdcp_lib_read_an(an);
+
+ DBG("AN: %x %x %x %x %x %x %x %x", an[0], an[1], an[2], an[3],
+ an[4], an[5], an[6], an[7]);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_r0_check
+ *-----------------------------------------------------------------------------
+ */
+static int hdcp_lib_r0_check(void)
+{
+ u8 ro_rx[2], ro_tx[2];
+
+ DBG("hdcp_lib_r0_check()");
+
+ /* DDC: Read Ri' from RX */
+ if (hdcp_ddc_read(DDC_Ri_LEN, DDC_Ri_ADDR , (u8 *)&ro_rx))
+ return -HDCP_DDC_ERROR;
+
+ /* Read Ri in HDCP IP */
+ ro_tx[0] = RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__R1) & 0xFF;
+
+ ro_tx[1] = RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__R2) & 0xFF;
+
+ /* Compare values */
+ DBG("ROTX: %x%x RORX:%x%x", ro_tx[0], ro_tx[1], ro_rx[0], ro_rx[1]);
+
+ if ((ro_rx[0] == ro_tx[0]) && (ro_rx[1] == ro_tx[1]))
+ return HDCP_OK;
+ else
+ return -HDCP_AUTH_FAILURE;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_sha_bstatus
+ *-----------------------------------------------------------------------------
+ */
+static int hdcp_lib_sha_bstatus(struct hdcp_sha_in *sha)
+{
+ u8 data[2];
+
+ if (hdcp_ddc_read(DDC_BSTATUS_LEN, DDC_BSTATUS_ADDR, data))
+ return -HDCP_DDC_ERROR;
+
+ sha->data[sha->byte_counter++] = data[0];
+ sha->data[sha->byte_counter++] = data[1];
+
+ return HDCP_OK;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_set_repeater_bit_in_tx
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_lib_set_repeater_bit_in_tx(enum hdcp_repeater rx_mode)
+{
+ DBG("hdcp_lib_set_repeater_bit_in_tx() value=%d", rx_mode);
+
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 4, 4, rx_mode);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_toggle_repeater_bit_in_tx
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_lib_toggle_repeater_bit_in_tx(void)
+{
+ if (hdcp_lib_check_repeater_bit_in_tx())
+ hdcp_lib_set_repeater_bit_in_tx(HDCP_RECEIVER);
+ else
+ hdcp_lib_set_repeater_bit_in_tx(HDCP_REPEATER);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_initiate_step1
+ *-----------------------------------------------------------------------------
+ */
+static int hdcp_lib_initiate_step1(void)
+{
+ /* HDCP authentication steps:
+ * 1) Read Bksv - check validity (is HDMI Rx supporting HDCP ?)
+ * 2) Initializes HDCP (CP reset release)
+ * 3) Read Bcaps - is HDMI Rx a repeater ?
+ * *** First part authentication ***
+ * 4) Read Bksv - check validity (is HDMI Rx supporting HDCP ?)
+ * 5) Generates An
+ * 6) DDC: Writes An, Aksv
+ * 7) DDC: Write Bksv
+ */
+ uint8_t an_ksv_data[8], an_bksv_data[8];
+ uint8_t rx_type;
+
+ DBG("hdcp_lib_initiate_step1()\n");
+
+ /* DDC: Read BKSV from RX */
+ if (hdcp_ddc_read(DDC_BKSV_LEN, DDC_BKSV_ADDR , an_ksv_data))
+ return -HDCP_DDC_ERROR;
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+
+ DBG("BKSV: %02x %02x %02x %02x %02x", an_ksv_data[0], an_ksv_data[1],
+ an_ksv_data[2], an_ksv_data[3],
+ an_ksv_data[4]);
+
+ if (hdcp_lib_check_ksv(an_ksv_data)) {
+ DBG("BKSV error (number of 0 and 1)");
+ return -HDCP_AUTH_FAILURE;
+ }
+
+ /* TODO: Need to confirm it is required */
+#ifndef _9032_AN_STOP_FIX_
+ hdcp_lib_toggle_repeater_bit_in_tx();
+#endif
+
+ /* Release CP reset bit */
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 2, 2, 1);
+
+ /* Read BCAPS to determine if HDCP RX is a repeater */
+ if (hdcp_ddc_read(DDC_BCAPS_LEN, DDC_BCAPS_ADDR, &rx_type))
+ return -HDCP_DDC_ERROR;
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+
+ rx_type = FLD_GET(rx_type, DDC_BIT_REPEATER, DDC_BIT_REPEATER);
+
+ /* Set repeater bit in HDCP CTRL */
+ if (rx_type == 1) {
+ hdcp_lib_set_repeater_bit_in_tx(HDCP_REPEATER);
+ DBG("HDCP RX is a repeater");
+ } else {
+ hdcp_lib_set_repeater_bit_in_tx(HDCP_RECEIVER);
+ DBG("HDCP RX is a receiver");
+ }
+
+/* Power debug code */
+#ifdef POWER_TRANSITION_DBG
+ printk(KERN_INFO "\n**************************\n"
+ "AUTHENTICATION: WAIT FOR DSS TRANSITION\n"
+ "*************************\n");
+ mdelay(10000);
+ printk(KERN_INFO "\n**************************\n"
+ "DONE\n"
+ "*************************\n");
+#endif
+ /* DDC: Read BKSV from RX */
+ if (hdcp_ddc_read(DDC_BKSV_LEN, DDC_BKSV_ADDR , an_bksv_data))
+ return -HDCP_DDC_ERROR;
+
+ /* Generate An */
+ hdcp_lib_generate_an(an_ksv_data);
+
+ /* Authentication 1st step initiated HERE */
+
+ /* DDC: Write An */
+ if (hdcp_ddc_write(DDC_AN_LEN, DDC_AN_ADDR , an_ksv_data))
+ return -HDCP_DDC_ERROR;
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+
+ /* Read AKSV from IP: (HDCP AKSV register) */
+ hdcp_lib_read_aksv(an_ksv_data);
+
+ DBG("AKSV: %02x %02x %02x %02x %02x", an_ksv_data[0], an_ksv_data[1],
+ an_ksv_data[2], an_ksv_data[3],
+ an_ksv_data[4]);
+
+ if (hdcp_lib_check_ksv(an_ksv_data)) {
+ printk(KERN_INFO "HDCP: AKSV error (number of 0 and 1)\n");
+ return -HDCP_AKSV_ERROR;
+ }
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+
+ /* DDC: Write AKSV */
+ if (hdcp_ddc_write(DDC_AKSV_LEN, DDC_AKSV_ADDR, an_ksv_data))
+ return -HDCP_DDC_ERROR;
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+
+ /* Write Bksv to IP */
+ hdcp_lib_write_bksv(an_bksv_data);
+
+ /* Check IP BKSV error */
+ if (RD_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 5, 5))
+ return -HDCP_AUTH_FAILURE;
+
+ /* Here BSKV should be checked against revokation list */
+
+ return HDCP_OK;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_check_ksv
+ *-----------------------------------------------------------------------------
+ */
+static int hdcp_lib_check_ksv(uint8_t ksv[5])
+{
+ int i, j;
+ int zero = 0, one = 0;
+
+ for (i = 0; i < 5; i++) {
+ /* Count number of zero / one */
+ for (j = 0; j < 8; j++) {
+ if (ksv[i] & (0x01 << j))
+ one++;
+ else
+ zero++;
+ }
+ }
+
+ if (one == zero)
+ return 0;
+ else
+ return -1;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_3des_load_key
+ *-----------------------------------------------------------------------------
+ */
+int hdcp_3des_load_key(uint32_t *deshdcp_encrypted_key)
+{
+ int counter = 0, status = HDCP_OK;
+
+ DBG("Loading HDCP keys...");
+
+ /* Set decryption mode in DES control register */
+ WR_FIELD_32(hdcp.deshdcp_base_addr,
+ DESHDCP__DHDCP_CTRL,
+ DESHDCP__DHDCP_CTRL__DIRECTION_POS_F,
+ DESHDCP__DHDCP_CTRL__DIRECTION_POS_L,
+ 0x0);
+
+ /* Write encrypted data */
+ while (counter < DESHDCP_KEY_SIZE) {
+ /* Fill Data registers */
+ WR_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_L,
+ deshdcp_encrypted_key[counter]);
+ WR_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_H,
+ deshdcp_encrypted_key[counter + 1]);
+
+ /* Wait for output bit at '1' */
+ while (RD_FIELD_32(hdcp.deshdcp_base_addr,
+ DESHDCP__DHDCP_CTRL,
+ DESHDCP__DHDCP_CTRL__OUTPUT_READY_POS_F,
+ DESHDCP__DHDCP_CTRL__OUTPUT_READY_POS_L
+ ) != 0x1)
+ ;
+
+ /* Dummy read (indeed data are transfered directly into
+ * key memory)
+ */
+ if (RD_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_L) !=
+ 0x0) {
+ status = -HDCP_3DES_ERROR;
+ printk(KERN_ERR "HDCP: DESHDCP dummy read error\n");
+ }
+ if (RD_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_H) !=
+ 0x0) {
+ status = -HDCP_3DES_ERROR;
+ printk(KERN_ERR "HDCP: DESHDCP dummy read error\n");
+ }
+
+ counter += 2;
+ }
+
+ return status;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_3des_encrypt_key
+ *-----------------------------------------------------------------------------
+ */
+void hdcp_3des_encrypt_key(struct hdcp_encrypt_control *enc_ctrl,
+ uint32_t out_key[DESHDCP_KEY_SIZE])
+{
+ int counter = 0;
+
+ DBG("Encrypting HDCP keys...");
+
+ /* Reset encrypted key array */
+ for (counter = 0; counter < DESHDCP_KEY_SIZE; counter++)
+ out_key[counter] = 0;
+
+ /* Set encryption mode in DES control register */
+ WR_FIELD_32(hdcp.deshdcp_base_addr,
+ DESHDCP__DHDCP_CTRL,
+ DESHDCP__DHDCP_CTRL__DIRECTION_POS_F,
+ DESHDCP__DHDCP_CTRL__DIRECTION_POS_L,
+ 0x1);
+
+ /* Write raw data and read encrypted data */
+ counter = 0;
+
+#ifdef POWER_TRANSITION_DBG
+ printk(KERN_ERR "\n**************************\n"
+ "ENCRYPTION: WAIT FOR DSS TRANSITION\n"
+ "*************************\n");
+ mdelay(10000);
+ printk(KER_INFO "\n**************************\n"
+ "DONE\n"
+ "*************************\n");
+#endif
+
+ while (counter < DESHDCP_KEY_SIZE) {
+ /* Fill Data registers */
+ WR_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_L,
+ enc_ctrl->in_key[counter]);
+ WR_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_H,
+ enc_ctrl->in_key[counter + 1]);
+
+ /* Wait for output bit at '1' */
+ while (RD_FIELD_32(hdcp.deshdcp_base_addr,
+ DESHDCP__DHDCP_CTRL,
+ DESHDCP__DHDCP_CTRL__OUTPUT_READY_POS_F,
+ DESHDCP__DHDCP_CTRL__OUTPUT_READY_POS_L
+ ) != 0x1)
+ ;
+
+ /* Read enrypted data */
+ out_key[counter] = RD_REG_32(hdcp.deshdcp_base_addr,
+ DESHDCP__DHDCP_DATA_L);
+ out_key[counter + 1] = RD_REG_32(hdcp.deshdcp_base_addr,
+ DESHDCP__DHDCP_DATA_H);
+
+ counter += 2;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_disable
+ *-----------------------------------------------------------------------------
+ */
+int hdcp_lib_disable()
+{
+ DBG("hdcp_lib_disable() %u", jiffies_to_msecs(jiffies));
+
+ /* CP reset */
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 2, 2, 0);
+
+ /* Clear AV mute in case it was set */
+ hdcp_lib_set_av_mute(AV_MUTE_CLEAR);
+
+ return HDCP_OK;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_set_encryption
+ *-----------------------------------------------------------------------------
+ */
+void hdcp_lib_set_encryption(enum encryption_state enc_state)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hdcp.spinlock, flags);
+
+ /* HDCP_CTRL::ENC_EN set/clear */
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 0, 0, enc_state);
+
+ /* Read to flush */
+ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL);
+
+ spin_unlock_irqrestore(&hdcp.spinlock, flags);
+
+ pr_info("HDCP: Encryption state changed: %s hdcp_ctrl: %02x",
+ enc_state == HDCP_ENC_OFF ? "OFF" : "ON",
+ RD_REG_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL));
+
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_set_av_mute
+ *-----------------------------------------------------------------------------
+ */
+void hdcp_lib_set_av_mute(enum av_mute av_mute_state)
+{
+ unsigned long flags;
+
+ DBG("hdcp_lib_set_av_mute() av_mute=%d", av_mute_state);
+
+
+ spin_lock_irqsave(&hdcp.spinlock, flags);
+
+ {
+ u8 RegVal, TimeOutCount = 64;
+
+ RegVal = RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_CORE_AV_BASE,
+ HDMI_CORE_AV_PB_CTRL2);
+
+ /* PRguide-GPC: To change the content of the CP_BYTE1 register,
+ * CP_EN must be zero
+ * set PB_CTRL2 :: CP_RPT = 0
+ */
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_CORE_AV_BASE,
+ HDMI_CORE_AV_PB_CTRL2, 2, 2, 0);
+
+ /* Set/clear AV mute state */
+ WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_CORE_AV_BASE,
+ HDMI_CORE_AV_CP_BYTE1, av_mute_state);
+
+ /* FIXME: This loop should be removed */
+ while (TimeOutCount--) {
+ /* Continue in this loop till CP_EN becomes 0,
+ * prior to TimeOutCount becoming 0 */
+ if (!RD_FIELD_32(hdcp.hdmi_wp_base_addr +
+ HDMI_CORE_AV_BASE,
+ HDMI_CORE_AV_PB_CTRL2, 3, 3))
+ break;
+ }
+
+ DBG(" timeoutcount=%d", TimeOutCount);
+
+ /* FIXME: why is this if condition required?, according to prg,
+ * this shall be unconditioanlly */
+ if (TimeOutCount) {
+ /* set PB_CTRL2 :: CP_EN = 1 & CP_RPT = 1 */
+ RegVal = FLD_MOD(RegVal, 0x3, 3, 2);
+
+ WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_CORE_AV_BASE,
+ HDMI_CORE_AV_PB_CTRL2, RegVal);
+ }
+ }
+
+ spin_unlock_irqrestore(&hdcp.spinlock, flags);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_check_repeater_bit_in_tx
+ *-----------------------------------------------------------------------------
+ */
+u8 hdcp_lib_check_repeater_bit_in_tx(void)
+{
+ return RD_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 4, 4);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_auto_ri_check
+ *-----------------------------------------------------------------------------
+ */
+void hdcp_lib_auto_ri_check(bool state)
+{
+ u8 reg_val;
+ unsigned long flags;
+
+ DBG("hdcp_lib_auto_ri_check() state=%s",
+ state == true ? "ON" : "OFF");
+
+ spin_lock_irqsave(&hdcp.spinlock, flags);
+
+ reg_val = RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__INT_UNMASK3);
+
+ reg_val = (state == true) ? (reg_val | 0xB0) : (reg_val & ~0xB0);
+
+ /* Turn on/off the following Auto Ri interrupts */
+ WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__INT_UNMASK3, reg_val);
+
+ /* Enable/Disable Ri */
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__RI_CMD, 0, 0,
+ ((state == true) ? 1 : 0));
+
+ /* Read to flush */
+ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__RI_CMD);
+
+ spin_unlock_irqrestore(&hdcp.spinlock, flags);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_auto_bcaps_rdy_check
+ *-----------------------------------------------------------------------------
+ */
+void hdcp_lib_auto_bcaps_rdy_check(bool state)
+{
+ u8 reg_val;
+ unsigned long flags;
+
+ DBG("hdcp_lib_auto_bcaps_rdy_check() state=%s",
+ state == true ? "ON" : "OFF");
+
+ spin_lock_irqsave(&hdcp.spinlock, flags);
+
+ /* Enable KSV_READY / BACP_DONE interrupt */
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__INT_UNMASK2, 7, 7,
+ ((state == true) ? 1 : 0));
+
+ /* Enable/Disable Ri & Bcap */
+ reg_val = RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__RI_CMD);
+
+ /* Enable RI_EN & BCAP_EN OR disable BCAP_EN */
+ reg_val = (state == true) ? (reg_val | 0x3) : (reg_val & ~0x2);
+
+ WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__RI_CMD, reg_val);
+
+ /* Read to flush */
+ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__RI_CMD);
+
+ spin_unlock_irqrestore(&hdcp.spinlock, flags);
+
+ DBG("hdcp_lib_auto_bcaps_rdy_check() Done\n");
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_step1_start
+ *-----------------------------------------------------------------------------
+ */
+int hdcp_lib_step1_start(void)
+{
+ u8 hdmi_mode;
+ int status;
+
+ DBG("hdcp_lib_step1_start() %u", jiffies_to_msecs(jiffies));
+
+ /* Check if mode is HDMI or DVI */
+ hdmi_mode = RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_CORE_AV_BASE,
+ HDMI_CORE_AV_HDMI_CTRL) &
+ HDMI_CORE_AV_HDMI_CTRL__HDMI_MODE;
+
+ DBG("RX mode: %s", hdmi_mode ? "HDMI" : "DVI");
+
+ /* Set AV Mute */
+ hdcp_lib_set_av_mute(AV_MUTE_SET);
+
+ /* Must turn encryption off when AVMUTE */
+ hdcp_lib_set_encryption(HDCP_ENC_OFF);
+
+ status = hdcp_lib_initiate_step1();
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+ else
+ return status;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_step1_r0_check
+ *-----------------------------------------------------------------------------
+ */
+int hdcp_lib_step1_r0_check(void)
+{
+ int status = HDCP_OK;
+
+ /* HDCP authentication steps:
+ * 1) DDC: Read M0'
+ * 2) Compare M0 and M0'
+ * if Rx is a receiver: switch to authentication step 3
+ * 3) Enable encryption / auto Ri check / disable AV mute
+ * if Rx is a repeater: switch to authentication step 2
+ * 3) Get M0 from HDMI IP and store it for further processing (V)
+ * 4) Enable encryption / auto Ri check / auto BCAPS RDY polling
+ * Disable AV mute
+ */
+
+ DBG("hdcp_lib_step1_r0_check() %u", jiffies_to_msecs(jiffies));
+
+ status = hdcp_lib_r0_check();
+ if (status < 0)
+ return status;
+
+ /* Authentication 1st step done */
+
+ /* Now prepare 2nd step authentication in case of RX repeater and
+ * enable encryption / Ri check
+ */
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+
+ if (hdcp_lib_check_repeater_bit_in_tx()) {
+ status = omap4_secure_dispatcher(PPA_SERVICE_HDCP_READ_M0,
+ FLAG_START_CRITICAL,
+ 0, 0, 0, 0, 0);
+ /* Wait for user space */
+ if (status) {
+ printk(KERN_ERR "HDCP: omap4_secure_dispatcher M0 error "
+ "%d\n", status);
+ return -HDCP_AUTH_FAILURE;
+ }
+
+ DBG("hdcp_lib_set_encryption() %u", jiffies_to_msecs(jiffies));
+
+ /* Enable encryption */
+ hdcp_lib_set_encryption(HDCP_ENC_ON);
+
+#ifdef _9032_AUTO_RI_
+ /* Enable Auto Ri */
+ hdcp_lib_auto_ri_check(true);
+#endif
+
+#ifdef _9032_BCAP_
+ /* Enable automatic BCAPS polling */
+ hdcp_lib_auto_bcaps_rdy_check(true);
+#endif
+
+ /* Now, IP waiting for BCAPS ready bit */
+ } else {
+ /* Receiver: enable encryption and auto Ri check */
+ hdcp_lib_set_encryption(HDCP_ENC_ON);
+
+#ifdef _9032_AUTO_RI_
+ /* Enable Auto Ri */
+ hdcp_lib_auto_ri_check(true);
+#endif
+
+ }
+
+ /* Clear AV mute */
+ hdcp_lib_set_av_mute(AV_MUTE_CLEAR);
+
+ return HDCP_OK;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_step2
+ *-----------------------------------------------------------------------------
+ */
+int hdcp_lib_step2(void)
+{
+ /* HDCP authentication steps:
+ * 1) Disable auto Ri check
+ * 2) DDC: read BStatus (nb of devices, MAX_DEV
+ */
+
+ u8 bstatus[2];
+ int status = HDCP_OK;
+
+ DBG("hdcp_lib_step2() %u", jiffies_to_msecs(jiffies));
+
+#ifdef _9032_AUTO_RI_
+ /* Disable Auto Ri */
+ hdcp_lib_auto_ri_check(false);
+#endif
+
+ /* DDC: Read Bstatus (1st byte) from Rx */
+ if (hdcp_ddc_read(DDC_BSTATUS_LEN, DDC_BSTATUS_ADDR, bstatus))
+ return -HDCP_DDC_ERROR;
+
+ /* Get KSV list size */
+ DBG("KSV list size: %d", bstatus[0] & DDC_BSTATUS0_DEV_COUNT);
+ sha_input.byte_counter = (bstatus[0] & DDC_BSTATUS0_DEV_COUNT) * 5;
+
+ /* Check BStatus topology errors */
+ if (bstatus[0] & DDC_BSTATUS0_MAX_DEVS) {
+ DBG("MAX_DEV_EXCEEDED set");
+ return -HDCP_AUTH_FAILURE;
+ }
+
+ if (bstatus[1] & DDC_BSTATUS1_MAX_CASC) {
+ DBG("MAX_CASCADE_EXCEEDED set");
+ return -HDCP_AUTH_FAILURE;
+ }
+
+ DBG("Retrieving KSV list...");
+
+ /* Clear all SHA input data */
+ /* TODO: should be done earlier at HDCP init */
+ memset(sha_input.data, 0, MAX_SHA_DATA_SIZE);
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+
+ /* DDC: read KSV list */
+ if (sha_input.byte_counter) {
+ if (hdcp_ddc_read(sha_input.byte_counter, DDC_KSV_FIFO_ADDR,
+ (u8 *)&sha_input.data))
+ return -HDCP_DDC_ERROR;
+ }
+
+ /* Read and add Bstatus */
+ if (hdcp_lib_sha_bstatus(&sha_input))
+ return -HDCP_DDC_ERROR;
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+
+ /* Read V' */
+ if (hdcp_ddc_read(DDC_V_LEN, DDC_V_ADDR, sha_input.vprime))
+ return -HDCP_DDC_ERROR;
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+
+ /* clear sha_input values in cache*/
+ dma_sync_single_for_device(NULL,
+ __pa((u32)(&sha_input)),
+ sizeof(struct hdcp_sha_in),
+ DMA_TO_DEVICE);
+
+ status = omap4_secure_dispatcher(PPA_SERVICE_HDCP_CHECK_V,
+ FLAG_START_CRITICAL,
+ 1, __pa((u32)&sha_input), 0, 0, 0);
+ /* Wait for user space */
+ if (status) {
+ printk(KERN_ERR "HDCP: omap4_secure_dispatcher CHECH_V error "
+ "%d\n", status);
+ return -HDCP_AUTH_FAILURE;
+ }
+
+ if (status == HDCP_OK) {
+ /* Re-enable Ri check */
+#ifdef _9032_AUTO_RI_
+ hdcp_lib_auto_ri_check(true);
+#endif
+ }
+
+ return status;
+}
diff --git a/drivers/video/omap2/hdcp/hdcp_top.c b/drivers/video/omap2/hdcp/hdcp_top.c
new file mode 100644
index 0000000..1bb32f4
--- /dev/null
+++ b/drivers/video/omap2/hdcp/hdcp_top.c
@@ -0,0 +1,1050 @@
+/*
+ * hdcp_top.c
+ *
+ * HDCP interface DSS driver setting for TI's OMAP4 family of processor.
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Authors: Fabrice Olivero
+ * Fabrice Olivero <f-olivero@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/module.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/completion.h>
+#include <linux/miscdevice.h>
+#include <linux/firmware.h>
+#include "../../hdmi_ti_4xxx_ip.h"
+#include "../dss/dss.h"
+#include "hdcp.h"
+
+struct hdcp hdcp;
+struct hdcp_sha_in sha_input;
+
+/* State machine / workqueue */
+static void hdcp_wq_disable(void);
+static void hdcp_wq_start_authentication(void);
+static void hdcp_wq_check_r0(void);
+static void hdcp_wq_step2_authentication(void);
+static void hdcp_wq_authentication_failure(void);
+static void hdcp_work_queue(struct work_struct *work);
+static struct delayed_work *hdcp_submit_work(int event, int delay);
+static void hdcp_cancel_work(struct delayed_work **work);
+
+/* Callbacks */
+static void hdcp_start_frame_cb(void);
+static void hdcp_irq_cb(int hpd_low);
+
+/* Control */
+static long hdcp_enable_ctl(void __user *argp);
+static long hdcp_disable_ctl(void);
+static long hdcp_query_status_ctl(void __user *argp);
+static long hdcp_encrypt_key_ctl(void __user *argp);
+
+/* Driver */
+static int __init hdcp_init(void);
+static void __exit hdcp_exit(void);
+
+struct completion hdcp_comp;
+static DECLARE_WAIT_QUEUE_HEAD(hdcp_up_wait_queue);
+static DECLARE_WAIT_QUEUE_HEAD(hdcp_down_wait_queue);
+
+#define DSS_POWER
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_request_dss
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_request_dss(void)
+{
+#ifdef DSS_POWER
+ hdcp.dss_state = dss_runtime_get();
+#endif
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_user_space_task
+ *-----------------------------------------------------------------------------
+ */
+int hdcp_user_space_task(int flags)
+{
+ int ret;
+
+ DBG("Wait for user space task %x\n", flags);
+ hdcp.hdcp_up_event = flags & 0xFF;
+ hdcp.hdcp_down_event = flags & 0xFF;
+ wake_up_interruptible(&hdcp_up_wait_queue);
+ wait_event_interruptible(hdcp_down_wait_queue,
+ (hdcp.hdcp_down_event & 0xFF) == 0);
+ ret = (hdcp.hdcp_down_event & 0xFF00) >> 8;
+
+ DBG("User space task done %x\n", hdcp.hdcp_down_event);
+ hdcp.hdcp_down_event = 0;
+
+ return ret;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_release_dss
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_release_dss(void)
+{
+#ifdef DSS_POWER
+ if (hdcp.dss_state == 0)
+ dss_runtime_put();
+#endif
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_wq_disable
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_wq_disable(void)
+{
+ printk(KERN_INFO "HDCP: disabled\n");
+
+ hdcp_cancel_work(&hdcp.pending_wq_event);
+ hdcp_lib_disable();
+ hdcp.pending_disable = 0;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_wq_start_authentication
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_wq_start_authentication(void)
+{
+ int status = HDCP_OK;
+
+ hdcp.hdcp_state = HDCP_AUTHENTICATION_START;
+
+ printk(KERN_INFO "HDCP: authentication start\n");
+
+ /* Step 1 part 1 (until R0 calc delay) */
+ status = hdcp_lib_step1_start();
+
+ if (status == -HDCP_AKSV_ERROR) {
+ hdcp_wq_authentication_failure();
+ } else if (status == -HDCP_CANCELLED_AUTH) {
+ DBG("Authentication step 1 cancelled.");
+ return;
+ } else if (status != HDCP_OK) {
+ hdcp_wq_authentication_failure();
+ } else {
+ hdcp.hdcp_state = HDCP_WAIT_R0_DELAY;
+ hdcp.auth_state = HDCP_STATE_AUTH_1ST_STEP;
+ hdcp.pending_wq_event = hdcp_submit_work(HDCP_R0_EXP_EVENT,
+ HDCP_R0_DELAY);
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_wq_check_r0
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_wq_check_r0(void)
+{
+ int status = hdcp_lib_step1_r0_check();
+
+ if (status == -HDCP_CANCELLED_AUTH) {
+ DBG("Authentication step 1/R0 cancelled.");
+ return;
+ } else if (status < 0)
+ hdcp_wq_authentication_failure();
+ else {
+ if (hdcp_lib_check_repeater_bit_in_tx()) {
+ /* Repeater */
+ printk(KERN_INFO "HDCP: authentication step 1 "
+ "successful - Repeater\n");
+
+ hdcp.hdcp_state = HDCP_WAIT_KSV_LIST;
+ hdcp.auth_state = HDCP_STATE_AUTH_2ND_STEP;
+
+ hdcp.pending_wq_event =
+ hdcp_submit_work(HDCP_KSV_TIMEOUT_EVENT,
+ HDCP_KSV_TIMEOUT_DELAY);
+ } else {
+ /* Receiver */
+ printk(KERN_INFO "HDCP: authentication step 1 "
+ "successful - Receiver\n");
+
+ hdcp.hdcp_state = HDCP_LINK_INTEGRITY_CHECK;
+ hdcp.auth_state = HDCP_STATE_AUTH_3RD_STEP;
+
+ /* Restore retry counter */
+ if (hdcp.en_ctrl->nb_retry == 0)
+ hdcp.retry_cnt = HDCP_INFINITE_REAUTH;
+ else
+ hdcp.retry_cnt = hdcp.en_ctrl->nb_retry;
+ }
+ }
+}
+
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_wq_step2_authentication
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_wq_step2_authentication(void)
+{
+ int status = HDCP_OK;
+
+ /* KSV list timeout is running and should be canceled */
+ hdcp_cancel_work(&hdcp.pending_wq_event);
+
+ status = hdcp_lib_step2();
+
+ if (status == -HDCP_CANCELLED_AUTH) {
+ DBG("Authentication step 2 cancelled.");
+ return;
+ } else if (status < 0)
+ hdcp_wq_authentication_failure();
+ else {
+ printk(KERN_INFO "HDCP: (Repeater) authentication step 2 "
+ "successful\n");
+
+ hdcp.hdcp_state = HDCP_LINK_INTEGRITY_CHECK;
+ hdcp.auth_state = HDCP_STATE_AUTH_3RD_STEP;
+
+ /* Restore retry counter */
+ if (hdcp.en_ctrl->nb_retry == 0)
+ hdcp.retry_cnt = HDCP_INFINITE_REAUTH;
+ else
+ hdcp.retry_cnt = hdcp.en_ctrl->nb_retry;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_wq_authentication_failure
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_wq_authentication_failure(void)
+{
+ if (hdcp.hdmi_state == HDMI_STOPPED) {
+ hdcp.auth_state = HDCP_STATE_AUTH_FAILURE;
+ return;
+ }
+
+ hdcp_lib_auto_ri_check(false);
+ hdcp_lib_auto_bcaps_rdy_check(false);
+ hdcp_lib_set_av_mute(AV_MUTE_SET);
+ hdcp_lib_set_encryption(HDCP_ENC_OFF);
+
+ hdcp_cancel_work(&hdcp.pending_wq_event);
+
+ hdcp_lib_disable();
+ hdcp.pending_disable = 0;
+
+ if (hdcp.retry_cnt && (hdcp.hdmi_state != HDMI_STOPPED)) {
+ if (hdcp.retry_cnt < HDCP_INFINITE_REAUTH) {
+ hdcp.retry_cnt--;
+ printk(KERN_INFO "HDCP: authentication failed - "
+ "retrying, attempts=%d\n",
+ hdcp.retry_cnt);
+ } else
+ printk(KERN_INFO "HDCP: authentication failed - "
+ "retrying\n");
+
+ hdcp.hdcp_state = HDCP_AUTHENTICATION_START;
+ hdcp.auth_state = HDCP_STATE_AUTH_FAIL_RESTARTING;
+
+ hdcp.pending_wq_event = hdcp_submit_work(HDCP_AUTH_REATT_EVENT,
+ HDCP_REAUTH_DELAY);
+ } else {
+ printk(KERN_INFO "HDCP: authentication failed - "
+ "HDCP disabled\n");
+ hdcp.hdcp_state = HDCP_ENABLE_PENDING;
+ hdcp.auth_state = HDCP_STATE_AUTH_FAILURE;
+ }
+
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_work_queue
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_work_queue(struct work_struct *work)
+{
+ struct hdcp_delayed_work *hdcp_w =
+ container_of(work, struct hdcp_delayed_work, work.work);
+ int event = hdcp_w->event;
+
+ mutex_lock(&hdcp.lock);
+
+ DBG("hdcp_work_queue() - START - %u hdmi=%d hdcp=%d auth=%d evt= %x %d"
+ " hdcp_ctrl=%02x",
+ jiffies_to_msecs(jiffies),
+ hdcp.hdmi_state,
+ hdcp.hdcp_state,
+ hdcp.auth_state,
+ (event & 0xFF00) >> 8,
+ event & 0xFF,
+ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL));
+
+ /* Clear pending_wq_event
+ * In case a delayed work is scheduled from the state machine
+ * "pending_wq_event" is used to memorize pointer on the event to be
+ * able to cancel any pending work in case HDCP is disabled
+ */
+ if (event & HDCP_WORKQUEUE_SRC)
+ hdcp.pending_wq_event = 0;
+
+ /* First handle HDMI state */
+ if (event == HDCP_START_FRAME_EVENT) {
+ hdcp.pending_start = 0;
+ hdcp.hdmi_state = HDMI_STARTED;
+ }
+ /**********************/
+ /* HDCP state machine */
+ /**********************/
+ switch (hdcp.hdcp_state) {
+
+ /* State */
+ /*********/
+ case HDCP_DISABLED:
+ /* HDCP enable control or re-authentication event */
+ if (event == HDCP_ENABLE_CTL) {
+ if (hdcp.en_ctrl->nb_retry == 0)
+ hdcp.retry_cnt = HDCP_INFINITE_REAUTH;
+ else
+ hdcp.retry_cnt = hdcp.en_ctrl->nb_retry;
+
+ if (hdcp.hdmi_state == HDMI_STARTED)
+ hdcp_wq_start_authentication();
+ else
+ hdcp.hdcp_state = HDCP_ENABLE_PENDING;
+ }
+
+ break;
+
+ /* State */
+ /*********/
+ case HDCP_ENABLE_PENDING:
+ /* HDMI start frame event */
+ if (event == HDCP_START_FRAME_EVENT)
+ hdcp_wq_start_authentication();
+
+ break;
+
+ /* State */
+ /*********/
+ case HDCP_AUTHENTICATION_START:
+ /* Re-authentication */
+ if (event == HDCP_AUTH_REATT_EVENT)
+ hdcp_wq_start_authentication();
+
+ break;
+
+ /* State */
+ /*********/
+ case HDCP_WAIT_R0_DELAY:
+ /* R0 timer elapsed */
+ if (event == HDCP_R0_EXP_EVENT)
+ hdcp_wq_check_r0();
+
+ break;
+
+ /* State */
+ /*********/
+ case HDCP_WAIT_KSV_LIST:
+ /* Ri failure */
+ if (event == HDCP_RI_FAIL_EVENT) {
+ printk(KERN_INFO "HDCP: Ri check failure\n");
+
+ hdcp_wq_authentication_failure();
+ }
+ /* KSV list ready event */
+ else if (event == HDCP_KSV_LIST_RDY_EVENT)
+ hdcp_wq_step2_authentication();
+ /* Timeout */
+ else if (event == HDCP_KSV_TIMEOUT_EVENT) {
+ printk(KERN_INFO "HDCP: BCAPS polling timeout\n");
+ hdcp_wq_authentication_failure();
+ }
+ break;
+
+ /* State */
+ /*********/
+ case HDCP_LINK_INTEGRITY_CHECK:
+ /* Ri failure */
+ if (event == HDCP_RI_FAIL_EVENT) {
+ printk(KERN_INFO "HDCP: Ri check failure\n");
+ hdcp_wq_authentication_failure();
+ }
+ break;
+
+ default:
+ printk(KERN_WARNING "HDCP: error - unknow HDCP state\n");
+ break;
+ }
+
+ kfree(hdcp_w);
+ hdcp_w = 0;
+ if (event == HDCP_START_FRAME_EVENT)
+ hdcp.pending_start = 0;
+ if (event == HDCP_KSV_LIST_RDY_EVENT ||
+ event == HDCP_R0_EXP_EVENT) {
+ hdcp.pending_wq_event = 0;
+ }
+
+ DBG("hdcp_work_queue() - END - %u hdmi=%d hdcp=%d auth=%d evt=%x %d ",
+ jiffies_to_msecs(jiffies),
+ hdcp.hdmi_state,
+ hdcp.hdcp_state,
+ hdcp.auth_state,
+ (event & 0xFF00) >> 8,
+ event & 0xFF);
+
+ mutex_unlock(&hdcp.lock);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_submit_work
+ *-----------------------------------------------------------------------------
+ */
+static struct delayed_work *hdcp_submit_work(int event, int delay)
+{
+ struct hdcp_delayed_work *work;
+
+ work = kmalloc(sizeof(struct hdcp_delayed_work), GFP_ATOMIC);
+
+ if (work) {
+ INIT_DELAYED_WORK(&work->work, hdcp_work_queue);
+ work->event = event;
+ queue_delayed_work(hdcp.workqueue,
+ &work->work,
+ msecs_to_jiffies(delay));
+ } else {
+ printk(KERN_WARNING "HDCP: Cannot allocate memory to "
+ "create work\n");
+ return 0;
+ }
+
+ return &work->work;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_cancel_work
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_cancel_work(struct delayed_work **work)
+{
+ int ret = 0;
+
+ if (*work) {
+ ret = cancel_delayed_work(*work);
+ if (ret != 1) {
+ ret = cancel_work_sync(&((*work)->work));
+ printk(KERN_INFO "Canceling work failed - "
+ "cancel_work_sync done %d\n", ret);
+ }
+ kfree(*work);
+ *work = 0;
+ }
+}
+
+
+/******************************************************************************
+ * HDCP callbacks
+ *****************************************************************************/
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_3des_cb
+ *-----------------------------------------------------------------------------
+ */
+static bool hdcp_3des_cb(void)
+{
+ DBG("hdcp_3des_cb() %u", jiffies_to_msecs(jiffies));
+
+ if (!hdcp.hdcp_keys_loaded) {
+ printk(KERN_ERR "%s: hdcp_keys not loaded = %d",
+ __func__, hdcp.hdcp_keys_loaded);
+ return false;
+ }
+
+ /* Load 3DES key */
+ if (hdcp_3des_load_key(hdcp.en_ctrl->key) != HDCP_OK) {
+ printk(KERN_ERR "Error Loading HDCP keys\n");
+ return false;
+ }
+ return true;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_start_frame_cb
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_start_frame_cb(void)
+{
+ DBG("hdcp_start_frame_cb() %u", jiffies_to_msecs(jiffies));
+
+ if (!hdcp.hdcp_keys_loaded) {
+ DBG("%s: hdcp_keys not loaded = %d",
+ __func__, hdcp.hdcp_keys_loaded);
+ return;
+ }
+
+ /* Cancel any pending work */
+ if (hdcp.pending_start)
+ hdcp_cancel_work(&hdcp.pending_start);
+ if (hdcp.pending_wq_event)
+ hdcp_cancel_work(&hdcp.pending_wq_event);
+
+ hdcp.hpd_low = 0;
+ hdcp.pending_disable = 0;
+ hdcp.retry_cnt = hdcp.en_ctrl->nb_retry;
+ hdcp.pending_start = hdcp_submit_work(HDCP_START_FRAME_EVENT,
+ HDCP_ENABLE_DELAY);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_irq_cb
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_irq_cb(int status)
+{
+ DBG("hdcp_irq_cb() status=%x", status);
+
+ if (!hdcp.hdcp_keys_loaded) {
+ DBG("%s: hdcp_keys not loaded = %d",
+ __func__, hdcp.hdcp_keys_loaded);
+ return;
+ }
+
+ /* Disable auto Ri/BCAPS immediately */
+ if (((status & HDMI_RI_ERR) ||
+ (status & HDMI_BCAP) ||
+ (status & HDMI_HPD_LOW)) &&
+ (hdcp.hdcp_state != HDCP_ENABLE_PENDING)) {
+ hdcp_lib_auto_ri_check(false);
+ hdcp_lib_auto_bcaps_rdy_check(false);
+ }
+
+ /* Work queue execution not required if HDCP is disabled */
+ /* TODO: ignore interrupts if they are masked (cannnot access UMASK
+ * here so should use global variable
+ */
+ if ((hdcp.hdcp_state != HDCP_DISABLED) &&
+ (hdcp.hdcp_state != HDCP_ENABLE_PENDING)) {
+ if (status & HDMI_HPD_LOW) {
+ hdcp_lib_set_encryption(HDCP_ENC_OFF);
+ hdcp_ddc_abort();
+ }
+
+ if (status & HDMI_RI_ERR) {
+ hdcp_lib_set_av_mute(AV_MUTE_SET);
+ hdcp_lib_set_encryption(HDCP_ENC_OFF);
+ hdcp_submit_work(HDCP_RI_FAIL_EVENT, 0);
+ }
+ /* RI error takes precedence over BCAP */
+ else if (status & HDMI_BCAP)
+ hdcp_submit_work(HDCP_KSV_LIST_RDY_EVENT, 0);
+ }
+
+ if (status & HDMI_HPD_LOW) {
+ hdcp.pending_disable = 1; /* Used to exit on-going HDCP
+ * work */
+ hdcp.hpd_low = 0; /* Used to cancel HDCP works */
+ hdcp_lib_disable();
+ /* In case of HDCP_STOP_FRAME_EVENT, HDCP stop
+ * frame callback is blocked and waiting for
+ * HDCP driver to finish accessing the HW
+ * before returning
+ * Reason is to avoid HDMI driver to shutdown
+ * DSS/HDMI power before HDCP work is finished
+ */
+ hdcp.hdmi_state = HDMI_STOPPED;
+ hdcp.hdcp_state = HDCP_ENABLE_PENDING;
+ hdcp.auth_state = HDCP_STATE_DISABLED;
+ }
+}
+
+/******************************************************************************
+ * HDCP control from ioctl
+ *****************************************************************************/
+
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_enable_ctl
+ *-----------------------------------------------------------------------------
+ */
+static long hdcp_enable_ctl(void __user *argp)
+{
+ DBG("hdcp_ioctl() - ENABLE %u", jiffies_to_msecs(jiffies));
+
+ if (hdcp.en_ctrl == 0) {
+ hdcp.en_ctrl =
+ kmalloc(sizeof(struct hdcp_enable_control),
+ GFP_KERNEL);
+
+ if (hdcp.en_ctrl == 0) {
+ printk(KERN_WARNING
+ "HDCP: Cannot allocate memory for HDCP"
+ " enable control struct\n");
+ return -EFAULT;
+ }
+ }
+
+ if (copy_from_user(hdcp.en_ctrl, argp,
+ sizeof(struct hdcp_enable_control))) {
+ printk(KERN_WARNING "HDCP: Error copying from user space "
+ "- enable ioctl\n");
+ return -EFAULT;
+ }
+
+ /* Post event to workqueue */
+ if (hdcp_submit_work(HDCP_ENABLE_CTL, 0) == 0)
+ return -EFAULT;
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_disable_ctl
+ *-----------------------------------------------------------------------------
+ */
+static long hdcp_disable_ctl(void)
+{
+ DBG("hdcp_ioctl() - DISABLE %u", jiffies_to_msecs(jiffies));
+
+ hdcp_cancel_work(&hdcp.pending_start);
+ hdcp_cancel_work(&hdcp.pending_wq_event);
+
+ hdcp.pending_disable = 1;
+ /* Post event to workqueue */
+ if (hdcp_submit_work(HDCP_DISABLE_CTL, 0) == 0)
+ return -EFAULT;
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_query_status_ctl
+ *-----------------------------------------------------------------------------
+ */
+static long hdcp_query_status_ctl(void __user *argp)
+{
+ uint32_t *status = (uint32_t *)argp;
+
+ DBG("hdcp_ioctl() - QUERY %u", jiffies_to_msecs(jiffies));
+
+ *status = hdcp.auth_state;
+
+ return 0;
+}
+
+static int hdcp_wait_re_entrance;
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_wait_event_ctl
+ *-----------------------------------------------------------------------------
+ */
+static long hdcp_wait_event_ctl(void __user *argp)
+{
+ struct hdcp_wait_control ctrl;
+
+ DBG("hdcp_ioctl() - WAIT %u %d", jiffies_to_msecs(jiffies),
+ hdcp.hdcp_up_event);
+
+ if (copy_from_user(&ctrl, argp,
+ sizeof(struct hdcp_wait_control))) {
+ printk(KERN_WARNING "HDCP: Error copying from user space"
+ " - wait ioctl");
+ return -EFAULT;
+ }
+
+ if (hdcp_wait_re_entrance == 0) {
+ hdcp_wait_re_entrance = 1;
+ wait_event_interruptible(hdcp_up_wait_queue,
+ (hdcp.hdcp_up_event & 0xFF) != 0);
+
+ ctrl.event = hdcp.hdcp_up_event;
+
+ if ((ctrl.event & 0xFF) == HDCP_EVENT_STEP2) {
+ if (copy_to_user(ctrl.data, &sha_input,
+ sizeof(struct hdcp_sha_in))) {
+ printk(KERN_WARNING "HDCP: Error copying to "
+ "user space - wait ioctl");
+ return -EFAULT;
+ }
+ }
+
+ hdcp.hdcp_up_event = 0;
+ hdcp_wait_re_entrance = 0;
+ } else
+ ctrl.event = HDCP_EVENT_EXIT;
+
+ /* Store output data to output pointer */
+ if (copy_to_user(argp, &ctrl,
+ sizeof(struct hdcp_wait_control))) {
+ printk(KERN_WARNING "HDCP: Error copying to user space -"
+ " wait ioctl");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_done_ctl
+ *-----------------------------------------------------------------------------
+ */
+static long hdcp_done_ctl(void __user *argp)
+{
+ uint32_t *status = (uint32_t *)argp;
+
+ DBG("hdcp_ioctl() - DONE %u %d", jiffies_to_msecs(jiffies), *status);
+
+ hdcp.hdcp_down_event &= ~(*status & 0xFF);
+ hdcp.hdcp_down_event |= *status & 0xFF00;
+
+ wake_up_interruptible(&hdcp_down_wait_queue);
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_encrypt_key_ctl
+ *-----------------------------------------------------------------------------
+ */
+static long hdcp_encrypt_key_ctl(void __user *argp)
+{
+ struct hdcp_encrypt_control *ctrl;
+ uint32_t *out_key;
+
+ DBG("hdcp_ioctl() - ENCRYPT KEY %u", jiffies_to_msecs(jiffies));
+
+ mutex_lock(&hdcp.lock);
+
+ if (hdcp.hdcp_state != HDCP_DISABLED) {
+ printk(KERN_INFO "HDCP: Cannot encrypt keys while HDCP "
+ "is enabled\n");
+ mutex_unlock(&hdcp.lock);
+ return -EFAULT;
+ }
+
+ hdcp.hdcp_state = HDCP_KEY_ENCRYPTION_ONGOING;
+
+ /* Encryption happens in ioctl / user context */
+ ctrl = kmalloc(sizeof(struct hdcp_encrypt_control),
+ GFP_KERNEL);
+
+ if (ctrl == 0) {
+ printk(KERN_WARNING "HDCP: Cannot allocate memory for HDCP"
+ " encryption control struct\n");
+ mutex_unlock(&hdcp.lock);
+ return -EFAULT;
+ }
+
+ out_key = kmalloc(sizeof(uint32_t) *
+ DESHDCP_KEY_SIZE, GFP_KERNEL);
+
+ if (out_key == 0) {
+ printk(KERN_WARNING "HDCP: Cannot allocate memory for HDCP "
+ "encryption output key\n");
+ kfree(ctrl);
+ mutex_unlock(&hdcp.lock);
+ return -EFAULT;
+ }
+
+ if (copy_from_user(ctrl, argp,
+ sizeof(struct hdcp_encrypt_control))) {
+ printk(KERN_WARNING "HDCP: Error copying from user space"
+ " - encrypt ioctl\n");
+ kfree(ctrl);
+ kfree(out_key);
+ mutex_unlock(&hdcp.lock);
+ return -EFAULT;
+ }
+
+ hdcp_request_dss();
+
+ /* Call encrypt function */
+ hdcp_3des_encrypt_key(ctrl, out_key);
+
+ hdcp_release_dss();
+
+ hdcp.hdcp_state = HDCP_DISABLED;
+ mutex_unlock(&hdcp.lock);
+
+ /* Store output data to output pointer */
+ if (copy_to_user(ctrl->out_key, out_key,
+ sizeof(uint32_t)*DESHDCP_KEY_SIZE)) {
+ printk(KERN_WARNING "HDCP: Error copying to user space -"
+ " encrypt ioctl\n");
+ kfree(ctrl);
+ kfree(out_key);
+ return -EFAULT;
+ }
+
+ kfree(ctrl);
+ kfree(out_key);
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_ioctl
+ *-----------------------------------------------------------------------------
+ */
+long hdcp_ioctl(struct file *fd, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+
+ switch (cmd) {
+ case HDCP_ENABLE:
+ return hdcp_enable_ctl(argp);
+
+ case HDCP_DISABLE:
+ return hdcp_disable_ctl();
+
+ case HDCP_ENCRYPT_KEY:
+ return hdcp_encrypt_key_ctl(argp);
+
+ case HDCP_QUERY_STATUS:
+ return hdcp_query_status_ctl(argp);
+
+ case HDCP_WAIT_EVENT:
+ return hdcp_wait_event_ctl(argp);
+
+ case HDCP_DONE:
+ return hdcp_done_ctl(argp);
+
+ default:
+ return -ENOTTY;
+ } /* End switch */
+}
+
+
+/******************************************************************************
+ * HDCP driver init/exit
+ *****************************************************************************/
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_mmap
+ *-----------------------------------------------------------------------------
+ */
+static int hdcp_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ int status;
+
+ DBG("hdcp_mmap() %lx %lx %lx\n", vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start);
+
+ vma->vm_flags |= VM_IO | VM_RESERVED;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ status = remap_pfn_range(vma, vma->vm_start,
+ HDMI_WP >> PAGE_SHIFT,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot);
+ if (status) {
+ DBG("mmap error %d\n", status);
+ return -EAGAIN;
+ }
+
+ DBG("mmap succesfull\n");
+ return 0;
+}
+
+static struct file_operations hdcp_fops = {
+ .owner = THIS_MODULE,
+ .mmap = hdcp_mmap,
+ .unlocked_ioctl = hdcp_ioctl,
+};
+
+struct miscdevice mdev;
+
+static void hdcp_load_keys_cb(const struct firmware *fw, void *context)
+{
+ struct hdcp_enable_control *en_ctrl;
+
+ if (!fw) {
+ pr_err("HDCP: failed to load keys\n");
+ return;
+ }
+
+ if (fw->size != sizeof(en_ctrl->key)) {
+ pr_err("HDCP: encrypted key file wrong size %d\n", fw->size);
+ return;
+ }
+
+ en_ctrl = kmalloc(sizeof(*en_ctrl), GFP_KERNEL);
+ if (!en_ctrl) {
+ pr_err("HDCP: can't allocated space for keys\n");
+ return;
+ }
+
+ memcpy(en_ctrl->key, fw->data, sizeof(en_ctrl->key));
+ en_ctrl->nb_retry = 20;
+
+ hdcp.en_ctrl = en_ctrl;
+ hdcp.retry_cnt = hdcp.en_ctrl->nb_retry;
+ hdcp.hdcp_state = HDCP_ENABLE_PENDING;
+ hdcp.hdcp_keys_loaded = true;
+ pr_info("HDCP: loaded keys\n");
+}
+
+static int hdcp_load_keys(void)
+{
+ int ret;
+
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ "hdcp.keys", mdev.this_device, GFP_KERNEL,
+ &hdcp, hdcp_load_keys_cb);
+ if (ret < 0) {
+ pr_err("HDCP: request_firmware_nowait failed: %d\n", ret);
+ hdcp.hdcp_keys_loaded = false;
+ return ret;
+ }
+
+ return 0;
+}
+
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_init
+ *-----------------------------------------------------------------------------
+ */
+static int __init hdcp_init(void)
+{
+ DBG("hdcp_init() %u", jiffies_to_msecs(jiffies));
+
+ /* Map HDMI WP address */
+ hdcp.hdmi_wp_base_addr = ioremap(HDMI_WP, 0x1000);
+
+ if (!hdcp.hdmi_wp_base_addr) {
+ printk(KERN_ERR "HDCP: HDMI WP IOremap error\n");
+ return -EFAULT;
+ }
+
+ /* Map DESHDCP in kernel address space */
+ hdcp.deshdcp_base_addr = ioremap(DSS_SS_FROM_L3__DESHDCP, 0x34);
+
+ if (!hdcp.deshdcp_base_addr) {
+ printk(KERN_ERR "HDCP: DESHDCP IOremap error\n");
+ goto err_map_deshdcp;
+ }
+
+ mutex_init(&hdcp.lock);
+
+ mdev.minor = MISC_DYNAMIC_MINOR;
+ mdev.name = "hdcp";
+ mdev.mode = 0666;
+ mdev.fops = &hdcp_fops;
+
+ if (misc_register(&mdev)) {
+ printk(KERN_ERR "HDCP: Could not add character driver\n");
+ goto err_register;
+ }
+
+ mutex_lock(&hdcp.lock);
+
+ /* Variable init */
+ hdcp.en_ctrl = 0;
+ hdcp.hdcp_state = HDCP_DISABLED;
+ hdcp.pending_start = 0;
+ hdcp.pending_wq_event = 0;
+ hdcp.retry_cnt = 0;
+ hdcp.auth_state = HDCP_STATE_DISABLED;
+ hdcp.pending_disable = 0;
+ hdcp.hdcp_up_event = 0;
+ hdcp.hdcp_down_event = 0;
+ hdcp_wait_re_entrance = 0;
+ hdcp.hpd_low = 0;
+
+ spin_lock_init(&hdcp.spinlock);
+
+ init_completion(&hdcp_comp);
+
+ hdcp.workqueue = create_singlethread_workqueue("hdcp");
+ if (hdcp.workqueue == NULL)
+ goto err_add_driver;
+
+ hdcp_request_dss();
+
+ /* Register HDCP callbacks to HDMI library */
+ if (omapdss_hdmi_register_hdcp_callbacks(&hdcp_start_frame_cb,
+ &hdcp_irq_cb,
+ &hdcp_3des_cb))
+ hdcp.hdmi_state = HDMI_STARTED;
+ else
+ hdcp.hdmi_state = HDMI_STOPPED;
+
+ hdcp_release_dss();
+
+ mutex_unlock(&hdcp.lock);
+
+ hdcp_load_keys();
+
+ return 0;
+
+err_add_driver:
+ misc_deregister(&mdev);
+
+err_register:
+ mutex_destroy(&hdcp.lock);
+
+ iounmap(hdcp.deshdcp_base_addr);
+
+err_map_deshdcp:
+ iounmap(hdcp.hdmi_wp_base_addr);
+
+ return -EFAULT;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_exit
+ *-----------------------------------------------------------------------------
+ */
+static void __exit hdcp_exit(void)
+{
+ DBG("hdcp_exit() %u", jiffies_to_msecs(jiffies));
+
+ mutex_lock(&hdcp.lock);
+
+ kfree(hdcp.en_ctrl);
+
+ hdcp_request_dss();
+
+ /* Un-register HDCP callbacks to HDMI library */
+ omapdss_hdmi_register_hdcp_callbacks(0, 0, 0);
+
+ hdcp_release_dss();
+
+ misc_deregister(&mdev);
+
+ /* Unmap HDMI WP / DESHDCP */
+ iounmap(hdcp.hdmi_wp_base_addr);
+ iounmap(hdcp.deshdcp_base_addr);
+
+ destroy_workqueue(hdcp.workqueue);
+
+ mutex_unlock(&hdcp.lock);
+
+ mutex_destroy(&hdcp.lock);
+}
+
+/*-----------------------------------------------------------------------------
+ *-----------------------------------------------------------------------------
+ */
+module_init(hdcp_init);
+module_exit(hdcp_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("OMAP HDCP kernel module");
+MODULE_AUTHOR("Fabrice Olivero");
diff --git a/drivers/video/omap2/omapfb/Kconfig b/drivers/video/omap2/omapfb/Kconfig
index aa33386..d15486e 100644
--- a/drivers/video/omap2/omapfb/Kconfig
+++ b/drivers/video/omap2/omapfb/Kconfig
@@ -7,6 +7,7 @@ menuconfig FB_OMAP2
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
+ select FB_MODE_HELPERS
help
Frame buffer driver for OMAP2+ based boards.
diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c
index cff4503..8188e92 100644
--- a/drivers/video/omap2/omapfb/omapfb-ioctl.c
+++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c
@@ -883,6 +883,7 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
case OMAPFB_GET_DISPLAY_INFO: {
u16 xres, yres;
+ u32 w, h;
DBG("ioctl GET_DISPLAY_INFO\n");
@@ -896,15 +897,9 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
p.display_info.xres = xres;
p.display_info.yres = yres;
- if (display->driver->get_dimensions) {
- u32 w, h;
- display->driver->get_dimensions(display, &w, &h);
- p.display_info.width = w;
- p.display_info.height = h;
- } else {
- p.display_info.width = 0;
- p.display_info.height = 0;
- }
+ omapdss_display_get_dimensions(display, &w, &h);
+ p.display_info.width = w;
+ p.display_info.height = h;
if (copy_to_user((void __user *)arg, &p.display_info,
sizeof(p.display_info)))
@@ -912,6 +907,24 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
break;
}
+ case OMAPFB_ENABLEVSYNC:
+ if (get_user(p.crt, (__u32 __user *)arg)) {
+ r = -EFAULT;
+ break;
+ }
+
+ omapfb_lock(fbdev);
+ fbdev->vsync_active = !!p.crt;
+
+ if (display->state == OMAP_DSS_DISPLAY_ACTIVE) {
+ if (p.crt)
+ omapfb_enable_vsync(fbdev);
+ else
+ omapfb_disable_vsync(fbdev);
+ }
+ omapfb_unlock(fbdev);
+ break;
+
default:
dev_err(fbdev->dev, "Unknown ioctl 0x%x\n", cmd);
r = -EINVAL;
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index 505bc12..319c2ac 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -29,6 +29,7 @@
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/omapfb.h>
+#include <linux/wait.h>
#include <video/omapdss.h>
#include <plat/vram.h>
@@ -303,7 +304,7 @@ static void assign_colormode_to_var(struct fb_var_screeninfo *var,
var->transp = color->transp;
}
-static int fb_mode_to_dss_mode(struct fb_var_screeninfo *var,
+int omapfb_mode_to_dss_mode(struct fb_var_screeninfo *var,
enum omap_color_mode *mode)
{
enum omap_color_mode dssmode;
@@ -358,7 +359,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;
@@ -375,6 +376,7 @@ static int fb_mode_to_dss_mode(struct fb_var_screeninfo *var,
return -EINVAL;
}
+EXPORT_SYMBOL(omapfb_mode_to_dss_mode);
static int check_fb_res_bounds(struct fb_var_screeninfo *var)
{
@@ -512,7 +514,7 @@ static int setup_vrfb_rotation(struct fb_info *fbi)
DBG("setup_vrfb_rotation\n");
- r = fb_mode_to_dss_mode(var, &mode);
+ r = omapfb_mode_to_dss_mode(var, &mode);
if (r)
return r;
@@ -622,8 +624,9 @@ void set_fb_fix(struct fb_info *fbi)
fix->smem_len = var->yres_virtual * fix->line_length;
} else {
- fix->line_length =
- (var->xres_virtual * var->bits_per_pixel) >> 3;
+ /* SGX requires stride to be a multiple of 32 pixels */
+ int xres_align = ALIGN(var->xres_virtual, 32);
+ fix->line_length = (xres_align * var->bits_per_pixel) >> 3;
fix->smem_len = rg->size;
}
@@ -665,12 +668,13 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
enum omap_color_mode mode = 0;
int i;
int r;
+ u32 w = 0, h = 0;
DBG("check_fb_var %d\n", ofbi->id);
WARN_ON(!atomic_read(&ofbi->region->lock_count));
- r = fb_mode_to_dss_mode(var, &mode);
+ r = omapfb_mode_to_dss_mode(var, &mode);
if (r) {
DBG("cannot convert var to omap dss mode\n");
return r;
@@ -702,9 +706,10 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
var->xres, var->yres,
var->xres_virtual, var->yres_virtual);
- if (display && display->driver->get_dimensions) {
- u32 w, h;
- display->driver->get_dimensions(display, &w, &h);
+ if (display)
+ omapdss_display_get_dimensions(display, &w, &h);
+
+ if (w && h) {
var->width = DIV_ROUND_CLOSEST(w, 1000);
var->height = DIV_ROUND_CLOSEST(h, 1000);
} else {
@@ -757,6 +762,11 @@ static int omapfb_open(struct fb_info *fbi, int user)
static int omapfb_release(struct fb_info *fbi, int user)
{
+ struct omapfb_info *ofbi = FB2OFB(fbi);
+ struct omapfb2_device *fbdev = ofbi->fbdev;
+
+ omapfb_disable_vsync(fbdev);
+
return 0;
}
@@ -879,9 +889,9 @@ int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
omapfb_calc_addr(ofbi, var, fix, rotation,
&data_start_p, &data_start_v);
- r = fb_mode_to_dss_mode(var, &mode);
+ r = omapfb_mode_to_dss_mode(var, &mode);
if (r) {
- DBG("fb_mode_to_dss_mode failed");
+ DBG("omapfb_mode_to_dss_mode failed");
goto err;
}
@@ -1018,6 +1028,41 @@ static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
return r;
}
+void omapfb_fb2dss_timings(struct fb_videomode *fb_timings,
+ struct omap_video_timings *dss_timings)
+{
+ dss_timings->x_res = fb_timings->xres;
+ dss_timings->y_res = fb_timings->yres;
+ if (fb_timings->vmode & FB_VMODE_INTERLACED)
+ dss_timings->y_res /= 2;
+ dss_timings->pixel_clock = fb_timings->pixclock ?
+ PICOS2KHZ(fb_timings->pixclock) : 0;
+ dss_timings->hfp = fb_timings->right_margin;
+ dss_timings->hbp = fb_timings->left_margin;
+ dss_timings->hsw = fb_timings->hsync_len;
+ dss_timings->vfp = fb_timings->lower_margin;
+ dss_timings->vbp = fb_timings->upper_margin;
+ dss_timings->vsw = fb_timings->vsync_len;
+}
+EXPORT_SYMBOL(omapfb_fb2dss_timings);
+
+void omapfb_dss2fb_timings(struct omap_video_timings *dss_timings,
+ struct fb_videomode *fb_timings)
+{
+ memset(fb_timings, 0, sizeof(*fb_timings));
+ fb_timings->xres = dss_timings->x_res;
+ fb_timings->yres = dss_timings->y_res;
+ fb_timings->pixclock = dss_timings->pixel_clock ?
+ KHZ2PICOS(dss_timings->pixel_clock) : 0;
+ fb_timings->right_margin = dss_timings->hfp;
+ fb_timings->left_margin = dss_timings->hbp;
+ fb_timings->hsync_len = dss_timings->hsw;
+ fb_timings->lower_margin = dss_timings->vfp;
+ fb_timings->upper_margin = dss_timings->vbp;
+ fb_timings->vsync_len = dss_timings->vsw;
+}
+EXPORT_SYMBOL(omapfb_dss2fb_timings);
+
/* set the video mode according to info->var */
static int omapfb_set_par(struct fb_info *fbi)
{
@@ -1251,11 +1296,16 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
switch (blank) {
case FB_BLANK_UNBLANK:
- if (display->state != OMAP_DSS_DISPLAY_SUSPENDED)
- goto exit;
+ if (display->state == OMAP_DSS_DISPLAY_SUSPENDED) {
+ if (display->driver->resume)
+ r = display->driver->resume(display);
+ } else if (display->state == OMAP_DSS_DISPLAY_DISABLED) {
+ if (display->driver->enable)
+ r = display->driver->enable(display);
+ }
- if (display->driver->resume)
- r = display->driver->resume(display);
+ if (fbdev->vsync_active)
+ omapfb_enable_vsync(fbdev);
break;
@@ -1265,11 +1315,17 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
case FB_BLANK_VSYNC_SUSPEND:
case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_POWERDOWN:
+
+ if (fbdev->vsync_active)
+ omapfb_disable_vsync(fbdev);
+
if (display->state != OMAP_DSS_DISPLAY_ACTIVE)
goto exit;
if (display->driver->suspend)
r = display->driver->suspend(display);
+ else if (display->driver->disable)
+ display->driver->disable(display);
break;
@@ -2233,6 +2289,39 @@ static int omapfb_init_display(struct omapfb2_device *fbdev,
return 0;
}
+static void omapfb_send_vsync_work(struct work_struct *work)
+{
+ struct omapfb2_device *fbdev =
+ container_of(work, typeof(*fbdev), vsync_work);
+ char buf[64];
+ char *envp[2];
+
+ snprintf(buf, sizeof(buf), "VSYNC=%llu",
+ ktime_to_ns(fbdev->vsync_timestamp));
+ envp[0] = buf;
+ envp[1] = NULL;
+ kobject_uevent_env(&fbdev->dev->kobj, KOBJ_CHANGE, envp);
+}
+static void omapfb_vsync_isr(void *data, u32 mask)
+{
+ struct omapfb2_device *fbdev = data;
+ fbdev->vsync_timestamp = ktime_get();
+ schedule_work(&fbdev->vsync_work);
+}
+
+int omapfb_enable_vsync(struct omapfb2_device *fbdev)
+{
+ int r;
+ /* TODO: should determine correct IRQ like dss_mgr_wait_for_vsync does*/
+ r = omap_dispc_register_isr(omapfb_vsync_isr, fbdev, DISPC_IRQ_VSYNC);
+ return r;
+}
+
+void omapfb_disable_vsync(struct omapfb2_device *fbdev)
+{
+ omap_dispc_unregister_isr(omapfb_vsync_isr, fbdev, DISPC_IRQ_VSYNC);
+}
+
static int omapfb_probe(struct platform_device *pdev)
{
struct omapfb2_device *fbdev = NULL;
@@ -2348,6 +2437,7 @@ static int omapfb_probe(struct platform_device *pdev)
goto cleanup;
}
+ INIT_WORK(&fbdev->vsync_work, omapfb_send_vsync_work);
return 0;
cleanup:
@@ -2362,6 +2452,7 @@ static int omapfb_remove(struct platform_device *pdev)
struct omapfb2_device *fbdev = platform_get_drvdata(pdev);
/* FIXME: wait till completion of pending events */
+ /* TODO: terminate vsync thread */
omapfb_remove_sysfs(fbdev);
diff --git a/drivers/video/omap2/omapfb/omapfb.h b/drivers/video/omap2/omapfb/omapfb.h
index aa1b1d9..649388f 100644
--- a/drivers/video/omap2/omapfb/omapfb.h
+++ b/drivers/video/omap2/omapfb/omapfb.h
@@ -97,6 +97,10 @@ struct omapfb2_device {
struct omap_dss_device *dssdev;
u8 bpp;
} bpp_overrides[10];
+
+ bool vsync_active;
+ ktime_t vsync_timestamp;
+ struct work_struct vsync_work;
};
struct omapfb_colormode {
@@ -128,6 +132,9 @@ int dss_mode_to_fb_mode(enum omap_color_mode dssmode,
int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
u16 posx, u16 posy, u16 outw, u16 outh);
+int omapfb_enable_vsync(struct omapfb2_device *fbdev);
+void omapfb_disable_vsync(struct omapfb2_device *fbdev);
+
/* find the display connected to this fb, if any */
static inline struct omap_dss_device *fb2display(struct fb_info *fbi)
{