aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-omap2/vp.c65
-rw-r--r--arch/arm/mach-omap2/vp.h2
-rw-r--r--drivers/video/hdmi_ti_4xxx_ip.c123
-rw-r--r--drivers/video/hdmi_ti_4xxx_ip.h5
-rw-r--r--drivers/video/omap2/Kconfig1
-rw-r--r--drivers/video/omap2/Makefile1
-rw-r--r--drivers/video/omap2/dss/dss.h6
-rw-r--r--drivers/video/omap2/dss/hdmi.c63
-rw-r--r--drivers/video/omap2/dss/hdmi_panel.c1
-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.c837
-rw-r--r--drivers/video/omap2/hdcp/hdcp_top.c1045
-rw-r--r--include/video/hdmi_ti_4xxx_ip.h10
-rw-r--r--sound/soc/codecs/omap-hdmi-codec.c141
18 files changed, 3076 insertions, 61 deletions
diff --git a/arch/arm/mach-omap2/vp.c b/arch/arm/mach-omap2/vp.c
index 4677921..4202701 100644
--- a/arch/arm/mach-omap2/vp.c
+++ b/arch/arm/mach-omap2/vp.c
@@ -1,5 +1,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/ratelimit.h>
#include <plat/common.h>
@@ -117,6 +118,21 @@ int omap_vp_update_errorgain(struct voltagedomain *voltdm,
return 0;
}
+#define _MAX_COUNT_ERR 10
+static u8 __vp_debug_error_message_count = _MAX_COUNT_ERR;
+/* Dump with stack the first few messages, tone down severity for the rest */
+#define _vp_controlled_err(ARGS...) \
+{ \
+ if (__vp_debug_error_message_count) { \
+ pr_err(ARGS); \
+ dump_stack(); \
+ __vp_debug_error_message_count--; \
+ } else { \
+ pr_err_ratelimited(ARGS); \
+ } \
+}
+
+
/* VP force update method of voltage scaling */
int omap_vp_forceupdate_scale(struct voltagedomain *voltdm,
unsigned long target_volt)
@@ -126,6 +142,18 @@ int omap_vp_forceupdate_scale(struct voltagedomain *voltdm,
u8 target_vsel, current_vsel;
int ret, timeout = 0;
+ /*
+ * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us
+ * This is an additional allowance to ensure we are in proper state
+ * to enter into forceupdate state transition.
+ */
+ omap_test_timeout((voltdm->read(vp->vstatus)), VP_IDLE_TIMEOUT,
+ timeout);
+
+ if (timeout >= VP_IDLE_TIMEOUT)
+ _vp_controlled_err("%s:vdd_%s idletimdout forceupdate(v=%ld)\n",
+ __func__, voltdm->name, target_volt);
+
ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, &current_vsel);
if (ret)
return ret;
@@ -141,8 +169,11 @@ int omap_vp_forceupdate_scale(struct voltagedomain *voltdm,
udelay(1);
}
if (timeout >= VP_TRANXDONE_TIMEOUT) {
- pr_warning("%s: vdd_%s TRANXDONE timeout exceeded."
- "Voltage change aborted", __func__, voltdm->name);
+ _vp_controlled_err("%s: vdd_%s TRANXDONE timeout exceeded."
+ "Voltage change aborted target volt=%ld,"
+ "target vsel=0x%02x, current_vsel=0x%02x\n",
+ __func__, voltdm->name, target_volt,
+ target_vsel, current_vsel);
return -ETIMEDOUT;
}
@@ -171,9 +202,12 @@ int omap_vp_forceupdate_scale(struct voltagedomain *voltdm,
omap_test_timeout(vp->common->ops->check_txdone(vp->id),
VP_TRANXDONE_TIMEOUT, timeout);
if (timeout >= VP_TRANXDONE_TIMEOUT)
- pr_err("%s: vdd_%s TRANXDONE timeout exceeded."
- "TRANXDONE never got set after the voltage update\n",
- __func__, voltdm->name);
+ _vp_controlled_err("%s: vdd_%s TRANXDONE timeout exceeded. "
+ "TRANXDONE never got set after the voltage update. "
+ "target volt=%ld, target vsel=0x%02x, "
+ "current_vsel=0x%02x\n",
+ __func__, voltdm->name, target_volt,
+ target_vsel, current_vsel);
omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel);
@@ -190,9 +224,11 @@ int omap_vp_forceupdate_scale(struct voltagedomain *voltdm,
}
if (timeout >= VP_TRANXDONE_TIMEOUT)
- pr_warning("%s: vdd_%s TRANXDONE timeout exceeded while trying"
- "to clear the TRANXDONE status\n",
- __func__, voltdm->name);
+ _vp_controlled_err("%s: vdd_%s TRANXDONE timeout exceeded while"
+ "trying to clear the TRANXDONE status. target volt=%ld,"
+ "target vsel=0x%02x, current_vsel=0x%02x\n",
+ __func__, voltdm->name, target_volt,
+ target_vsel, current_vsel);
vpconfig = voltdm->read(vp->vpconfig);
/* Clear initVDD copy trigger bit */
@@ -308,6 +344,17 @@ void omap_vp_disable(struct voltagedomain *voltdm)
return;
}
+ /*
+ * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us
+ * Depending on if we catch VP in the middle of an SR operation.
+ */
+ omap_test_timeout((voltdm->read(vp->vstatus)),
+ VP_IDLE_TIMEOUT, timeout);
+
+ if (timeout >= VP_IDLE_TIMEOUT)
+ pr_warning("%s: vdd_%s idle timedout before disable\n",
+ __func__, voltdm->name);
+
/* Disable VP */
vpconfig = voltdm->read(vp->vpconfig);
vpconfig &= ~vp->common->vpconfig_vpenable;
@@ -320,7 +367,7 @@ void omap_vp_disable(struct voltagedomain *voltdm)
VP_IDLE_TIMEOUT, timeout);
if (timeout >= VP_IDLE_TIMEOUT)
- pr_warning("%s: vdd_%s idle timedout\n",
+ pr_warning("%s: vdd_%s idle timedout after disable\n",
__func__, voltdm->name);
vp->enabled = false;
diff --git a/arch/arm/mach-omap2/vp.h b/arch/arm/mach-omap2/vp.h
index 57a4d44..ea3f9ee 100644
--- a/arch/arm/mach-omap2/vp.h
+++ b/arch/arm/mach-omap2/vp.h
@@ -22,7 +22,7 @@
struct voltagedomain;
/* XXX document */
-#define VP_IDLE_TIMEOUT 200
+#define VP_IDLE_TIMEOUT 500
#define VP_TRANXDONE_TIMEOUT 300
/**
diff --git a/drivers/video/hdmi_ti_4xxx_ip.c b/drivers/video/hdmi_ti_4xxx_ip.c
index c105442..3cbcbe3 100644
--- a/drivers/video/hdmi_ti_4xxx_ip.c
+++ b/drivers/video/hdmi_ti_4xxx_ip.c
@@ -148,10 +148,30 @@ static int hdmi_pll_init(struct hdmi_ip_data *ip_data,
return 0;
}
+static int hdmi_wait_for_audio_stop(struct hdmi_ip_data *ip_data)
+{
+ int count = 0;
+ /* wait for audio to stop before powering off the phy*/
+ while (REG_GET(hdmi_wp_base(ip_data),
+ HDMI_WP_AUDIO_CTRL, 31, 31) != 0) {
+ msleep(100);
+ if (count++ > 100) {
+ pr_err("Audio is not turned off "
+ "even after 10 seconds\n");
+ return -ETIMEDOUT;
+ }
+ }
+ return 0;
+}
/* PHY_PWR_CMD */
-static int hdmi_set_phy_pwr(struct hdmi_ip_data *ip_data, enum hdmi_phy_pwr val)
+static int hdmi_set_phy_pwr(struct hdmi_ip_data *ip_data,
+ enum hdmi_phy_pwr val, bool set_mode)
{
+ /* FIXME audio driver should have already stopped, but not yet */
+ if (val == HDMI_PHYPWRCMD_OFF && !set_mode)
+ hdmi_wait_for_audio_stop(ip_data);
+
/* Command for power control of HDMI PHY */
REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, val, 7, 6);
@@ -228,11 +248,11 @@ int hdmi_ti_4xxx_phy_init(struct hdmi_ip_data *ip_data)
{
u16 r = 0;
- r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
+ r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON, false);
if (r)
return r;
- r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON);
+ r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON, false);
if (r)
return r;
@@ -259,9 +279,9 @@ int hdmi_ti_4xxx_phy_init(struct hdmi_ip_data *ip_data)
return 0;
}
-void hdmi_ti_4xxx_phy_off(struct hdmi_ip_data *ip_data)
+void hdmi_ti_4xxx_phy_off(struct hdmi_ip_data *ip_data, bool set_mode)
{
- hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
+ hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF, set_mode);
}
EXPORT_SYMBOL(hdmi_ti_4xxx_phy_init);
EXPORT_SYMBOL(hdmi_ti_4xxx_phy_off);
@@ -281,7 +301,7 @@ static int hdmi_core_ddc_edid(struct hdmi_ip_data *ip_data,
* right shifted values( The behavior is not consistent and seen only
* with some TV's)
*/
- usleep_range(800, 1000);
+ msleep(300);
if (!ext) {
/* Clk SCL Devices */
@@ -500,6 +520,8 @@ static void hdmi_core_video_config(struct hdmi_ip_data *ip_data,
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);
+ /* PD bit has to be written to recieve the interrupts */
+ r = FLD_MOD(r, HDMI_CORE_CTRL1_POWER_DOWN, 0, 0);
hdmi_write_reg(hdmi_core_sys_base(ip_data), HDMI_CORE_CTRL1, r);
REG_FLD_MOD(hdmi_core_sys_base(ip_data),
@@ -654,6 +676,36 @@ void hdmi_ti_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start)
}
EXPORT_SYMBOL(hdmi_ti_4xxx_wp_video_start);
+int hdmi_ti_4xxx_wp_get_video_state(struct hdmi_ip_data *ip_data)
+{
+ u32 status = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG);
+
+ return (status & 0x80000000) ? 1 : 0;
+}
+
+int hdmi_ti_4xxx_set_wait_soft_reset(struct hdmi_ip_data *ip_data)
+{
+ u8 count = 0;
+
+ /* reset W1 */
+ REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_SYSCONFIG, 0x1, 0, 0);
+
+ /* wait till SOFTRESET == 0 */
+ while (hdmi_wait_for_bit_change(hdmi_wp_base(ip_data),
+ HDMI_WP_SYSCONFIG, 0, 0, 0) != 0) {
+ if (count++ > 10) {
+ pr_err("SYSCONFIG[SOFTRESET] bit not set to 0\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ /* Make madule smart and wakeup capable*/
+ REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_SYSCONFIG, 0x3, 3, 2);
+
+ return 0;
+}
+
+
static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt,
struct omap_video_timings *timings, struct hdmi_config *param)
{
@@ -711,6 +763,16 @@ static void hdmi_wp_video_config_timing(struct hdmi_ip_data *ip_data,
hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_TIMING_V, timing_v);
}
+static void hdmi_wp_core_interrupt_set(struct hdmi_ip_data *ip_data, u32 val)
+{
+ u32 irqStatus;
+ irqStatus = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQENABLE_SET);
+ pr_debug("[HDMI] WP_IRQENABLE_SET..currently reads as:%x\n", irqStatus);
+ irqStatus = irqStatus | val;
+ hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQENABLE_SET, irqStatus);
+ pr_debug("[HDMI]WP_IRQENABLE_SET..changed to :%x\n", irqStatus);
+}
+
void hdmi_ti_4xxx_basic_configure(struct hdmi_ip_data *ip_data,
struct hdmi_config *cfg)
{
@@ -730,6 +792,8 @@ void hdmi_ti_4xxx_basic_configure(struct hdmi_ip_data *ip_data,
&avi_cfg,
&repeat_cfg);
+ hdmi_wp_core_interrupt_set(ip_data, HDMI_WP_IRQENABLE_CORE);
+
hdmi_wp_video_init_format(&video_format, &video_timing, cfg);
hdmi_wp_video_config_timing(ip_data, &video_timing);
@@ -804,6 +868,53 @@ void hdmi_ti_4xxx_basic_configure(struct hdmi_ip_data *ip_data,
}
EXPORT_SYMBOL(hdmi_ti_4xxx_basic_configure);
+u32 hdmi_ti_4xxx_irq_handler(struct hdmi_ip_data *ip_data)
+{
+ u32 val, sys_stat = 0, core_state = 0;
+ u32 intr2 = 0, intr3 = 0, r = 0;
+ void __iomem *wp_base = hdmi_wp_base(ip_data);
+ void __iomem *core_base = hdmi_core_sys_base(ip_data);
+
+ pr_debug("Enter hdmi_ti_4xxx_irq_handler\n");
+
+ val = hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
+ if (val & HDMI_WP_IRQSTATUS_CORE) {
+ core_state = hdmi_read_reg(core_base, HDMI_CORE_SYS_INTR_STATE);
+ if (core_state & 0x1) {
+ sys_stat = hdmi_read_reg(core_base,
+ HDMI_CORE_SYS_SYS_STAT);
+ intr2 = hdmi_read_reg(core_base, HDMI_CORE_SYS_INTR2);
+ intr3 = hdmi_read_reg(core_base, HDMI_CORE_SYS_INTR3);
+
+ pr_debug("HDMI_CORE_SYS_SYS_STAT = 0x%x\n", sys_stat);
+ pr_debug("HDMI_CORE_SYS_INTR2 = 0x%x\n", intr2);
+ pr_debug("HDMI_CORE_SYS_INTR3 = 0x%x\n", intr3);
+
+ hdmi_write_reg(core_base, HDMI_CORE_SYS_INTR2, intr2);
+ hdmi_write_reg(core_base, HDMI_CORE_SYS_INTR3, intr3);
+
+ hdmi_read_reg(core_base, HDMI_CORE_SYS_INTR2);
+ hdmi_read_reg(core_base, HDMI_CORE_SYS_INTR3);
+ }
+ }
+
+ pr_debug("HDMI_WP_IRQSTATUS = 0x%x\n", val);
+ pr_debug("HDMI_CORE_SYS_INTR_STATE = 0x%x\n", core_state);
+
+ if (intr2 & HDMI_CORE_SYSTEM_INTR2__BCAP)
+ r |= HDMI_BCAP;
+
+ if (intr3 & HDMI_CORE_SYSTEM_INTR3__RI_ERR)
+ r |= HDMI_RI_ERR;
+
+ /* Ack other interrupts if any */
+ hdmi_write_reg(wp_base, HDMI_WP_IRQSTATUS, val);
+ /* flush posted write */
+ hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
+ return r;
+}
+EXPORT_SYMBOL(hdmi_ti_4xxx_irq_handler);
+
void hdmi_ti_4xxx_dump_regs(struct hdmi_ip_data *ip_data, struct seq_file *s)
{
#define DUMPREG(g, r) seq_printf(s, "%-35s %08x\n", #r, hdmi_read_reg(g, r))
diff --git a/drivers/video/hdmi_ti_4xxx_ip.h b/drivers/video/hdmi_ti_4xxx_ip.h
index 6c31e70..4572d66 100644
--- a/drivers/video/hdmi_ti_4xxx_ip.h
+++ b/drivers/video/hdmi_ti_4xxx_ip.h
@@ -47,6 +47,8 @@ struct hdmi_reg { u16 idx; };
#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)
+#define HDMI_WP_IRQSTATUS_CORE 0x1
+#define HDMI_WP_IRQENABLE_CORE 0x1
/* HDMI IP Core System */
#define HDMI_CORE_SYS_REG(idx) HDMI_REG(idx)
@@ -78,6 +80,9 @@ struct hdmi_reg { u16 idx; };
#define HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC 0x1
#define HDMI_CORE_CTRL1_BSEL_24BITBUS 0x1
#define HDMI_CORE_CTRL1_EDGE_RISINGEDGE 0x1
+#define HDMI_CORE_CTRL1_POWER_DOWN 0x1
+#define HDMI_CORE_SYSTEM_INTR2__BCAP 0x80
+#define HDMI_CORE_SYSTEM_INTR3__RI_ERR 0xF0
/* HDMI DDC E-DID */
#define HDMI_CORE_DDC_CMD HDMI_CORE_SYS_REG(0x3CC)
diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig
index 97a5bc4..c4ec41a 100644
--- a/drivers/video/omap2/Kconfig
+++ b/drivers/video/omap2/Kconfig
@@ -8,3 +8,4 @@ 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 2d5a205..ceb1dd9 100644
--- a/drivers/video/omap2/Makefile
+++ b/drivers/video/omap2/Makefile
@@ -3,5 +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/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 0dc6b5c..e508e05 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -515,6 +515,7 @@ 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);
@@ -523,11 +524,14 @@ 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 *);
+void hdmi_load_hdcp_keys(struct omap_dss_device *dssdev);
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),
+ void (*hdmi_power_on_cb)(void));
int omap_dss_ovl_set_info(struct omap_overlay *ovl,
struct omap_overlay_info *info);
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index 2ab0368..c244634 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -76,11 +76,17 @@ static struct {
struct hdmi_config cfg;
struct regulator *hdmi_reg;
+ int hdmi_irq;
struct clk *sys_clk;
struct clk *hdmi_clk;
int runtime_count;
int enabled;
+ bool set_mode;
+
+ void (*hdmi_start_frame_cb)(void);
+ void (*hdmi_irq_cb)(int);
+ void (*hdmi_power_on_cb)(void);
} hdmi;
static const u8 edid_header[8] = {0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0};
@@ -415,6 +421,9 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
hdmi_ti_4xxx_wp_video_start(&hdmi.hdmi_data, 1);
+ if (hdmi.hdmi_start_frame_cb && hdmi.custom_set)
+ (*hdmi.hdmi_start_frame_cb)();
+
return 0;
err:
hdmi_runtime_put();
@@ -426,12 +435,21 @@ static void hdmi_power_off(struct omap_dss_device *dssdev)
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_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;
}
+void hdmi_load_hdcp_keys(struct omap_dss_device *dssdev)
+{
+ /* load the keys and reset the wrapper to populate the AKSV registers*/
+ if (hdmi.hdmi_power_on_cb) {
+ hdmi.hdmi_power_on_cb();
+ hdmi_ti_4xxx_set_wait_soft_reset(&hdmi.hdmi_data);
+ }
+}
+
int omapdss_hdmi_get_pixel_clock(void)
{
return PICOS2KHZ(hdmi.cfg.timings.pixclock);
@@ -442,6 +460,18 @@ 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),
+ void (*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;
@@ -462,11 +492,27 @@ static irqreturn_t hpd_irq_handler(int irq, void *ptr)
int hpd = hdmi_get_current_hpd();
pr_info("hpd %d\n", hpd);
+ if (!hpd && hdmi.hdmi_irq_cb)
+ hdmi.hdmi_irq_cb(HDMI_HPD_LOW);
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)
{
@@ -489,12 +535,14 @@ int omapdss_hdmi_display_set_mode(struct omap_dss_device *dssdev,
{
int r1, r2;
/* turn the hdmi off and on to get new timings to use */
- omapdss_hdmi_display_disable(dssdev);
+ 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 = omapdss_hdmi_display_enable(dssdev);
+ r2 = dssdev->driver->enable(dssdev);
return r1 ? : r2;
}
@@ -692,6 +740,15 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
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;
+ }
+
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;
diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c
index 0fa5dea..c17fad5 100644
--- a/drivers/video/omap2/dss/hdmi_panel.c
+++ b/drivers/video/omap2/dss/hdmi_panel.c
@@ -224,6 +224,7 @@ static void hdmi_hotplug_detect_worker(struct work_struct *work)
dssdev->panel.monspecs.max_x * 10000;
dssdev->panel.height_in_um =
dssdev->panel.monspecs.max_y * 10000;
+ hdmi_load_hdcp_keys(dssdev);
switch_set_state(&hdmi.hpd_switch, 1);
goto done;
} else if (state == HPD_STATE_EDID_TRYLAST){
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..5b92aff
--- /dev/null
+++ b/drivers/video/omap2/hdcp/hdcp_lib.c
@@ -0,0 +1,837 @@
+/*
+ * 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,
+ 0x4, 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,
+ 0x4, 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..f23605a
--- /dev/null
+++ b/drivers/video/omap2/hdcp/hdcp_top.c
@@ -0,0 +1,1045 @@
+/*
+ * 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) {
+ 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);
+
+ 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 0 /* cancel_work_sync is only exported to GPL code
+ * as HDCP is not GPL, will not wait for work to finish
+ * but avoid freeing the work which is on-going
+ */
+ if (ret != 1) {
+ ret = cancel_work_sync(&((*work)->work));
+ printk(KERN_INFO "Canceling work failed - "
+ "cancel_work_sync done %d\n", ret);
+ }
+#endif
+ kfree(*work);
+ *work = 0;
+ }
+}
+
+
+/******************************************************************************
+ * HDCP callbacks
+ *****************************************************************************/
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_3des_cb
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_3des_cb(void)
+{
+ DBG("hdcp_3des_cb() %u", jiffies_to_msecs(jiffies));
+
+ if (!hdcp.hdcp_keys_loaded) {
+ DBG("%s: hdcp_keys not loaded = %d",
+ __func__, hdcp.hdcp_keys_loaded);
+ return;
+ }
+
+ /* Load 3DES key */
+ if (hdcp_3des_load_key(hdcp.en_ctrl->key) != HDCP_OK) {
+ printk(KERN_ERR "Error Loading HDCP keys\n");
+ return;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * 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 */
+ hdcp_cancel_work(&hdcp.pending_start);
+ 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/include/video/hdmi_ti_4xxx_ip.h b/include/video/hdmi_ti_4xxx_ip.h
index 0d5b4fd..992c704 100644
--- a/include/video/hdmi_ti_4xxx_ip.h
+++ b/include/video/hdmi_ti_4xxx_ip.h
@@ -21,6 +21,10 @@
#ifndef _HDMI_TI_4xxx_
#define _HDMI_TI_4xxx_
+#define HDMI_HPD_LOW 0x10
+#define HDMI_HPD_HIGH 0x20
+#define HDMI_BCAP 0x40
+#define HDMI_RI_ERR 0x80
enum hdmi_pll_pwr {
HDMI_PLLPWRCMD_ALLOFF = 0,
HDMI_PLLPWRCMD_PLLONLY = 1,
@@ -353,7 +357,7 @@ enum hdmi_core_infoframe {
int hdmi_ti_4xxx_phy_init(struct hdmi_ip_data *ip_data);
-void hdmi_ti_4xxx_phy_off(struct hdmi_ip_data *ip_data);
+void hdmi_ti_4xxx_phy_off(struct hdmi_ip_data *ip_data, bool set_mode);
int read_ti_4xxx_edid(struct hdmi_ip_data *ip_data, u8 *pedid, u16 max_length);
void hdmi_ti_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start);
int hdmi_ti_4xxx_pll_program(struct hdmi_ip_data *ip_data,
@@ -361,6 +365,9 @@ int hdmi_ti_4xxx_pll_program(struct hdmi_ip_data *ip_data,
int hdmi_ti_4xxx_set_pll_pwr(struct hdmi_ip_data *ip_data, enum hdmi_pll_pwr val);
void hdmi_ti_4xxx_basic_configure(struct hdmi_ip_data *ip_data,
struct hdmi_config *cfg);
+int hdmi_ti_4xxx_rxdet(struct hdmi_ip_data *ip_data);
+int hdmi_ti_4xxx_wp_get_video_state(struct hdmi_ip_data *ip_data);
+u32 hdmi_ti_4xxx_irq_handler(struct hdmi_ip_data *ip_data);
void hdmi_ti_4xxx_dump_regs(struct hdmi_ip_data *ip_data, struct seq_file *s);
int hdmi_ti_4xxx_config_audio_acr(struct hdmi_ip_data *ip_data,
u32 sample_freq, u32 *n, u32 *cts, u32 pclk);
@@ -374,4 +381,5 @@ void hdmi_ti_4xxx_core_audio_config(struct hdmi_ip_data *ip_data,
void hdmi_ti_4xxx_core_audio_infoframe_config(struct hdmi_ip_data *ip_data,
struct hdmi_core_infoframe_audio *info_aud);
void hdmi_ti_4xxx_audio_enable(struct hdmi_ip_data *ip_data, bool idle);
+int hdmi_ti_4xxx_set_wait_soft_reset(struct hdmi_ip_data *ip_data);
#endif
diff --git a/sound/soc/codecs/omap-hdmi-codec.c b/sound/soc/codecs/omap-hdmi-codec.c
index 8d89eed..c735700 100644
--- a/sound/soc/codecs/omap-hdmi-codec.c
+++ b/sound/soc/codecs/omap-hdmi-codec.c
@@ -48,34 +48,40 @@
#define HDMI_PLLCTRL 0x200
#define HDMI_PHY 0x300
+/* hdmi configuration params */
+struct hdmi_params {
+ int format;
+ int sample_freq;
+ int channels_nr;
+};
+
+
/* codec private data */
-struct hdmi_data {
+struct hdmi_codec_data {
struct hdmi_audio_format audio_fmt;
struct hdmi_audio_dma audio_dma;
struct hdmi_core_audio_config audio_core_cfg;
struct hdmi_core_infoframe_audio aud_if_cfg;
struct hdmi_ip_data ip_data;
struct omap_hwmod *oh;
-};
+ struct omap_dss_device *dssdev;
+ struct notifier_block notifier;
+ struct hdmi_params params;
+ int active;
+} hdmi_data;
-static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+
+static int hdmi_audio_set_configuration(struct hdmi_codec_data *priv)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_codec *codec = rtd->codec;
- struct platform_device *pdev = to_platform_device(codec->dev);
- struct hdmi_data *priv = snd_soc_codec_get_drvdata(codec);
struct hdmi_audio_format *audio_format = &priv->audio_fmt;
struct hdmi_audio_dma *audio_dma = &priv->audio_dma;
struct hdmi_core_audio_config *core_cfg = &priv->audio_core_cfg;
struct hdmi_core_infoframe_audio *aud_if_cfg = &priv->aud_if_cfg;
- int err, n, cts, channels_nr, channel_alloc;
+ int err, n, cts, channel_alloc;
enum hdmi_core_audio_sample_freq sample_freq;
u32 pclk = omapdss_hdmi_get_pixel_clock();
-
- switch (params_format(params)) {
+ switch (priv->params.format) {
case SNDRV_PCM_FORMAT_S16_LE:
core_cfg->i2s_cfg.word_max_length =
HDMI_AUDIO_I2S_MAX_WORD_20BITS;
@@ -107,7 +113,7 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
}
- switch (params_rate(params)) {
+ switch (priv->params.sample_freq) {
case 32000:
sample_freq = HDMI_AUDIO_FS_32000;
break;
@@ -121,8 +127,8 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- err = hdmi_ti_4xxx_config_audio_acr(&priv->ip_data, params_rate(params),
- &n, &cts, pclk);
+ err = hdmi_ti_4xxx_config_audio_acr(&priv->ip_data,
+ priv->params.sample_freq, &n, &cts, pclk);
if (err < 0)
return err;
@@ -179,9 +185,8 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
core_cfg->en_parallel_aud_input = true;
/* Number of channels */
- channels_nr = params_channels(params);
- switch (channels_nr) {
+ switch (priv->params.channels_nr) {
case 2:
core_cfg->layout = HDMI_AUDIO_LAYOUT_2CH;
channel_alloc = 0x0;
@@ -211,7 +216,7 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
HDMI_AUDIO_I2S_SD3_EN;
break;
default:
- dev_err(&pdev->dev, "Unsupported number of channels\n");
+ pr_err("Unsupported number of channels\n");
return -EINVAL;
}
@@ -223,7 +228,7 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
* 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 = channels_nr;
+ aud_if_cfg->db1_channel_count = priv->params.channels_nr;
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 = channel_alloc;
@@ -232,6 +237,42 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
hdmi_ti_4xxx_core_audio_infoframe_config(&priv->ip_data, aud_if_cfg);
return 0;
+
+}
+
+int hdmi_audio_notifier_callback(struct notifier_block *nb,
+ unsigned long arg, void *ptr)
+{
+ enum omap_dss_display_state state = arg;
+
+ if (state == OMAP_DSS_DISPLAY_ACTIVE) {
+ /* this happens just after hdmi_power_on */
+ if (hdmi_data.active)
+ hdmi_ti_4xxx_audio_enable(&hdmi_data.ip_data, 0);
+ hdmi_audio_set_configuration(&hdmi_data);
+ if (hdmi_data.active)
+ hdmi_ti_4xxx_audio_enable(&hdmi_data.ip_data, 1);
+ }
+ return 0;
+}
+
+int hdmi_audio_match(struct omap_dss_device *dssdev, void *arg)
+{
+ return sysfs_streq(dssdev->name , "hdmi");
+}
+
+static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct hdmi_codec_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->params.format = params_format(params);
+ priv->params.sample_freq = params_rate(params);
+ priv->params.channels_nr = params_channels(params);
+ return hdmi_audio_set_configuration(priv);
}
static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd,
@@ -239,7 +280,7 @@ static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
- struct hdmi_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct hdmi_codec_data *priv = snd_soc_codec_get_drvdata(codec);
int err = 0;
switch (cmd) {
@@ -253,11 +294,12 @@ static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd,
omap_hwmod_set_slave_idlemode(priv->oh,
HWMOD_IDLEMODE_NO);
hdmi_ti_4xxx_audio_enable(&priv->ip_data, 1);
+ priv->active = 1;
break;
-
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ priv->active = 0;
hdmi_ti_4xxx_audio_enable(&priv->ip_data, 0);
/*
* switch back to smart-idle & wakeup capable
@@ -283,52 +325,71 @@ static int hdmi_audio_startup(struct snd_pcm_substream *substream,
}
static int hdmi_probe(struct snd_soc_codec *codec)
{
- struct hdmi_data *priv;
struct platform_device *pdev = to_platform_device(codec->dev);
struct resource *hdmi_rsrc;
+ int ret = 0;
- priv = kzalloc(sizeof(struct hdmi_data), GFP_KERNEL);
- if (priv == NULL)
- return -ENOMEM;
-
- snd_soc_codec_set_drvdata(codec, priv);
-
+ snd_soc_codec_set_drvdata(codec, &hdmi_data);
hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!hdmi_rsrc) {
dev_err(&pdev->dev, "Cannot obtain IORESOURCE_MEM HDMI\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto res_err;
}
- priv->oh = omap_hwmod_lookup("dss_hdmi");
+ hdmi_data.oh = omap_hwmod_lookup("dss_hdmi");
- if (!priv->oh) {
+ if (!hdmi_data.oh) {
dev_err(&pdev->dev, "can't find omap_hwmod for hdmi\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto res_err;
}
/* Base address taken from platform */
- priv->ip_data.base_wp = ioremap(hdmi_rsrc->start,
+ hdmi_data.ip_data.base_wp = ioremap(hdmi_rsrc->start,
resource_size(hdmi_rsrc));
- if (!priv->ip_data.base_wp) {
+ if (!hdmi_data.ip_data.base_wp) {
dev_err(&pdev->dev, "can't ioremap WP\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto res_err;
+ }
+
+ hdmi_data.ip_data.hdmi_core_sys_offset = HDMI_CORE_SYS;
+ hdmi_data.ip_data.hdmi_core_av_offset = HDMI_CORE_AV;
+ hdmi_data.ip_data.hdmi_pll_offset = HDMI_PLLCTRL;
+ hdmi_data.ip_data.hdmi_phy_offset = HDMI_PHY;
+
+ hdmi_data.dssdev = omap_dss_find_device(NULL, hdmi_audio_match);
+
+ if (!hdmi_data.dssdev) {
+ dev_err(&pdev->dev, "can't find HDMI device\n");
+ ret = -ENODEV;
+ goto dssdev_err;
}
- priv->ip_data.hdmi_core_sys_offset = HDMI_CORE_SYS;
- priv->ip_data.hdmi_core_av_offset = HDMI_CORE_AV;
- priv->ip_data.hdmi_pll_offset = HDMI_PLLCTRL;
- priv->ip_data.hdmi_phy_offset = HDMI_PHY;
+ hdmi_data.notifier.notifier_call = hdmi_audio_notifier_callback;
+ blocking_notifier_chain_register(&hdmi_data.dssdev->state_notifiers,
+ &hdmi_data.notifier);
return 0;
+
+dssdev_err:
+ iounmap(hdmi_data.ip_data.base_wp);
+res_err:
+ return ret;
+
}
static int hdmi_remove(struct snd_soc_codec *codec)
{
- struct hdmi_data *priv = snd_soc_codec_get_drvdata(codec);
+ struct hdmi_codec_data *priv = snd_soc_codec_get_drvdata(codec);
+
+ blocking_notifier_chain_unregister(&priv->dssdev->state_notifiers,
+ &priv->notifier);
iounmap(priv->ip_data.base_wp);
kfree(priv);
return 0;