Merge "hwmon: epm_adc: Add psoc voltage scaling"
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index ddba4c3..6fee4e6 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -87,6 +87,7 @@
android_usb@fc42b0c8 {
compatible = "qcom,android-usb";
reg = <0xfc42b0c8 0xc8>;
+ qcom,android-usb-swfi-latency = <100>;
};
hsic@f9a15000 {
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index e5fd0d5..c95472f 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -89,6 +89,7 @@
CONFIG_MSM_EBI_ERP=y
CONFIG_MSM_CACHE_ERP=y
CONFIG_MSM_L1_ERR_PANIC=y
+CONFIG_MSM_L1_RECOV_ERR_PANIC=y
CONFIG_MSM_L1_ERR_LOG=y
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
CONFIG_MSM_DCVS=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index f9d7800..1842b6e 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -90,6 +90,7 @@
CONFIG_MSM_EBI_ERP=y
CONFIG_MSM_CACHE_ERP=y
CONFIG_MSM_L1_ERR_PANIC=y
+CONFIG_MSM_L1_RECOV_ERR_PANIC=y
CONFIG_MSM_L1_ERR_LOG=y
CONFIG_MSM_L2_ERP_PRINT_ACCESS_ERRORS=y
CONFIG_MSM_L2_ERP_1BIT_PANIC=y
diff --git a/arch/arm/configs/msm8974-perf_defconfig b/arch/arm/configs/msm8974-perf_defconfig
index 263160d..758e05e 100644
--- a/arch/arm/configs/msm8974-perf_defconfig
+++ b/arch/arm/configs/msm8974-perf_defconfig
@@ -71,6 +71,7 @@
CONFIG_MSM_RTB_SEPARATE_CPUS=y
CONFIG_MSM_CACHE_ERP=y
CONFIG_MSM_L1_ERR_PANIC=y
+CONFIG_MSM_L1_RECOV_ERR_PANIC=y
CONFIG_MSM_L1_ERR_LOG=y
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
CONFIG_MSM_ENABLE_WDOG_DEBUG_CONTROL=y
diff --git a/arch/arm/configs/msm8974_defconfig b/arch/arm/configs/msm8974_defconfig
index 00e4c8a..553dd52 100644
--- a/arch/arm/configs/msm8974_defconfig
+++ b/arch/arm/configs/msm8974_defconfig
@@ -70,6 +70,7 @@
CONFIG_MSM_RTB_SEPARATE_CPUS=y
CONFIG_MSM_CACHE_ERP=y
CONFIG_MSM_L1_ERR_PANIC=y
+CONFIG_MSM_L1_RECOV_ERR_PANIC=y
CONFIG_MSM_L1_ERR_LOG=y
CONFIG_MSM_L2_ERP_PRINT_ACCESS_ERRORS=y
CONFIG_MSM_L2_ERP_2BIT_PANIC=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index a81b8a3..f1b2936 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -237,6 +237,7 @@
CONFIG_IP_NF_TARGET_ULOG=y
CONFIG_NF_NAT=y
CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NATTYPE_MODULE=y
CONFIG_IP_NF_TARGET_NETMAP=y
CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_NF_MANGLE=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 372d0ac..8dd8579 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -2539,6 +2539,21 @@
For production builds, you should probably say 'N' here.
+config MSM_L1_RECOV_ERR_PANIC
+ bool "Panic on recoverable L1 cache errors"
+ depends on MSM_CACHE_ERP && MSM_L1_ERR_PANIC
+ help
+ Certain CPU designs may be able to automatically recover from certain
+ kinds of L1 cache errors, even though the L1 cache itself may not
+ support error correction. These errors should not result in any kind
+ of corruption, but their presence is nevertheless an indication of
+ poor system health. To cause the kernel to panic whenever a
+ recoverable L1 cache error is detected, say 'Y' here. This may be
+ useful as a debugging technique if general system instability is
+ suspected.
+
+ For production builds, you should definitely say 'N' here.
+
config MSM_L1_ERR_LOG
bool "Log CPU ERP events to system memory"
depends on MSM_CACHE_ERP
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 3320d27..4ae4f7b 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -57,7 +57,7 @@
endif
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
-obj-$(CONFIG_MSM_AVS_HW) += avs_hw.o
+obj-$(CONFIG_MSM_AVS_HW) += avs.o
obj-$(CONFIG_CPU_V6) += idle-v6.o
obj-$(CONFIG_CPU_V7) += idle-v7.o
obj-$(CONFIG_MSM_JTAG) += jtag.o
diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c
new file mode 100644
index 0000000..aa257ef
--- /dev/null
+++ b/arch/arm/mach-msm/avs.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <asm/mach-types.h>
+#include <asm/cputype.h>
+#include "avs.h"
+
+u32 avs_get_avscsr(void)
+{
+ u32 val = 0;
+
+ asm volatile ("mrc p15, 7, %[avscsr], c15, c1, 7\n\t"
+ : [avscsr]"=r" (val)
+ );
+
+ return val;
+}
+EXPORT_SYMBOL(avs_get_avscsr);
+
+void avs_set_avscsr(u32 avscsr)
+{
+ asm volatile ("mcr p15, 7, %[avscsr], c15, c1, 7\n\t"
+ "isb\n\t"
+ :
+ : [avscsr]"r" (avscsr)
+ );
+}
+EXPORT_SYMBOL(avs_set_avscsr);
+
+u32 avs_get_avsdscr(void)
+{
+ u32 val = 0;
+
+ asm volatile ("mrc p15, 7, %[avsdscr], c15, c0, 6\n\t"
+ : [avsdscr]"=r" (val)
+ );
+
+ return val;
+}
+EXPORT_SYMBOL(avs_get_avsdscr);
+
+void avs_set_avsdscr(u32 avsdscr)
+{
+ asm volatile("mcr p15, 7, %[avsdscr], c15, c0, 6\n\t"
+ "isb\n\t"
+ :
+ : [avsdscr]"r" (avsdscr)
+ );
+}
+EXPORT_SYMBOL(avs_set_avsdscr);
+
+static void avs_enable_local(void *data)
+{
+ u32 avsdscr = (u32) data;
+ u32 avscsr_enable = 0x61;
+
+ avs_set_avsdscr(avsdscr);
+ avs_set_avscsr(avscsr_enable);
+}
+
+static void avs_disable_local(void *data)
+{
+ avs_set_avscsr(0);
+}
+
+void avs_enable(int cpu, u32 avsdscr)
+{
+ int ret;
+
+ ret = smp_call_function_single(cpu, avs_enable_local,
+ (void *)avsdscr, true);
+ WARN_ON(ret);
+}
+EXPORT_SYMBOL(avs_enable);
+
+void avs_disable(int cpu)
+{
+ int ret;
+
+ ret = smp_call_function_single(cpu, avs_disable_local,
+ (void *) 0, true);
+ WARN_ON(ret);
+}
+EXPORT_SYMBOL(avs_disable);
diff --git a/arch/arm/mach-msm/avs.h b/arch/arm/mach-msm/avs.h
index 556603a..f8b311c 100644
--- a/arch/arm/mach-msm/avs.h
+++ b/arch/arm/mach-msm/avs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009,2012, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -15,42 +15,24 @@
#define AVS_H
#ifdef CONFIG_MSM_AVS_HW
-u32 avs_reset_delays(u32 avsdscr);
u32 avs_get_avscsr(void);
+void avs_set_avscsr(u32 avscsr);
u32 avs_get_avsdscr(void);
-u32 avs_get_tscsr(void);
-void avs_set_tscsr(u32 to_tscsr);
-u32 avs_disable(void);
-void avs_enable(u32 avscsr);
+void avs_set_avsdscr(u32 avsdscr);
+void avs_disable(int cpu);
+void avs_enable(int cpu, u32 avsdscr);
#else
-static inline u32 avs_reset_delays(u32 avsdscr)
-{ return 0; }
static inline u32 avs_get_avscsr(void)
{ return 0; }
+static inline void avs_set_avscsr(u32 avscsr) {}
static inline u32 avs_get_avsdscr(void)
{ return 0; }
-static inline u32 avs_get_tscsr(void)
-{ return 0; }
-static inline void avs_set_tscsr(u32 to_tscsr) {}
-static inline u32 avs_disable(void)
-{return 0; }
-static inline void avs_enable(u32 avscsr) {}
+static inline void avs_set_avsdscr(u32 avsdscr) {}
+static inline void avs_disable(int cpu) {}
+static inline void avs_enable(int cpu, u32 avsdscr) {}
#endif
-#define AVS_DISABLE(cpu) do { \
- if (get_cpu() == (cpu)) \
- avs_disable(); \
- put_cpu(); \
- } while (0);
+#define AVS_DISABLE(cpu) avs_disable(cpu)
+#define AVS_ENABLE(cpu, x) avs_enable(cpu, x)
-/* AVSCSR(0x61) to enable CPU, V and L2 AVS module */
-
-#define AVS_ENABLE(cpu, x) do { \
- if (get_cpu() == (cpu)) { \
- avs_reset_delays((x)); \
- avs_enable(0x61); \
- } \
- put_cpu(); \
- } while (0);
-
-#endif /* AVS_H */
+#endif
diff --git a/arch/arm/mach-msm/avs_hw.S b/arch/arm/mach-msm/avs_hw.S
deleted file mode 100644
index 6fad8bd..0000000
--- a/arch/arm/mach-msm/avs_hw.S
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (c) 2009, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only 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.
- */
-
- .text
-
- .global avs_get_avscsr
-/* Read r0=AVSCSR to get status from CPU, VFP, and L2 ring oscillators */
-
-avs_get_avscsr:
- mrc p15, 7, r0, c15, c1, 7
- bx lr
-
- .global avs_get_avsdscr
-/* Read r0=AVSDSCR to get the AVS Delay Synthesizer control settings */
-
-avs_get_avsdscr:
- mrc p15, 7, r0, c15, c0, 6
- bx lr
-
-
-
-
- .global avs_get_tscsr
-/* Read r0=TSCSR to get temperature sensor control and status */
-
-avs_get_tscsr:
- mrc p15, 7, r0, c15, c1, 0
- bx lr
-
- .global avs_set_tscsr
-/* Write TSCSR=r0 to set temperature sensor control and status */
-
-avs_set_tscsr:
- mcr p15, 7, r0, c15, c1, 0
- bx lr
-
-
-
-
-
- .global avs_reset_delays
-avs_reset_delays:
-
-/* AVSDSCR(dly) to program delay */
- mcr p15, 7, r0, c15, c0, 6
-
-/* Read r0=AVSDSCR */
- mrc p15, 7, r0, c15, c0, 6
- bx lr
-
- .global avs_enable
-avs_enable:
-/* Restore the avs_scr register */
- mcr p15, 7, r0, c15, c1, 7
- bx lr
-
- .global avs_disable
-avs_disable:
-
-/* Get the AVSCSR value */
- mrc p15, 7, r0, c15, c1, 7
-/* Clear AVSCSR */
- mov r1, #0
-/* Write AVSCSR */
- mcr p15, 7, r1, c15, c1, 7
-
- bx lr
-
- .end
-
-
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 72d926f..f52a7ca 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -3354,13 +3354,6 @@
{
int i;
- /* Reset the AVS registers until we have support for AVS */
- for (i = 0; i < ARRAY_SIZE(msm_spm_data); i++) {
- struct msm_spm_platform_data *pdata = &msm_spm_data[i];
- pdata->reg_init_values[MSM_SPM_REG_SAW2_AVS_CTL] = 0;
- pdata->reg_init_values[MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0;
- }
-
/* Update the SPM sequences for SPC and PC */
for (i = 0; i < ARRAY_SIZE(msm_spm_data); i++) {
int j;
diff --git a/arch/arm/mach-msm/cache_erp.c b/arch/arm/mach-msm/cache_erp.c
index 8a73c84..6b8f58b 100644
--- a/arch/arm/mach-msm/cache_erp.c
+++ b/arch/arm/mach-msm/cache_erp.c
@@ -20,6 +20,7 @@
#include <linux/io.h>
#include <mach/msm-krait-l2-accessors.h>
#include <mach/msm_iomap.h>
+#include <mach/socinfo.h>
#include <asm/cputype.h>
#include "acpuclock.h"
@@ -32,6 +33,8 @@
#define CESR_TLBMH BIT(16)
#define CESR_I_MASK 0x000000CC
+#define CESR_VALID_MASK 0x000100FF
+
/* Print a message for everything but TLB MH events */
#define CESR_PRINT_MASK 0x000000FF
@@ -64,6 +67,12 @@
#define ERP_L1_ERR(a) do { } while (0)
#endif
+#ifdef CONFIG_MSM_L1_RECOV_ERR_PANIC
+#define ERP_L1_RECOV_ERR(a) panic(a)
+#else
+#define ERP_L1_RECOV_ERR(a) do { } while (0)
+#endif
+
#ifdef CONFIG_MSM_L2_ERP_PORT_PANIC
#define ERP_PORT_ERR(a) panic(a)
#else
@@ -319,8 +328,13 @@
/* Clear the interrupt bits we processed */
write_cesr(cesr);
- if (print_regs)
- ERP_L1_ERR("L1 cache error detected");
+ if (print_regs) {
+ if ((cesr & (~CESR_I_MASK & CESR_VALID_MASK)) ||
+ cpu_is_krait_v1() || cpu_is_krait_v2())
+ ERP_L1_ERR("L1 nonrecoverable cache error detected");
+ else
+ ERP_L1_RECOV_ERR("L1 recoverable error detected\n");
+ }
return IRQ_HANDLED;
}
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index 550bb56..b42ad94 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -558,15 +558,18 @@
static bool msm_pm_power_collapse_standalone(bool from_idle)
{
unsigned int cpu = smp_processor_id();
- unsigned int avsdscr_setting;
- unsigned int avscsr_enable;
+ unsigned int avsdscr;
+ unsigned int avscsr;
bool collapsed;
- avsdscr_setting = avs_get_avsdscr();
- avscsr_enable = avs_disable();
+ avsdscr = avs_get_avsdscr();
+ avscsr = avs_get_avscsr();
+ avs_set_avscsr(0); /* Disable AVS */
+
collapsed = msm_pm_spm_power_collapse(cpu, from_idle, false);
- avs_enable(avscsr_enable);
- avs_reset_delays(avsdscr_setting);
+
+ avs_set_avsdscr(avsdscr);
+ avs_set_avscsr(avscsr);
return collapsed;
}
@@ -574,8 +577,8 @@
{
unsigned int cpu = smp_processor_id();
unsigned long saved_acpuclk_rate;
- unsigned int avsdscr_setting;
- unsigned int avscsr_enable;
+ unsigned int avsdscr;
+ unsigned int avscsr;
bool collapsed;
if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
@@ -586,8 +589,9 @@
if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
pr_info("CPU%u: %s: pre power down\n", cpu, __func__);
- avsdscr_setting = avs_get_avsdscr();
- avscsr_enable = avs_disable();
+ avsdscr = avs_get_avsdscr();
+ avscsr = avs_get_avscsr();
+ avs_set_avscsr(0); /* Disable AVS */
if (cpu_online(cpu))
saved_acpuclk_rate = acpuclk_power_collapse();
@@ -629,8 +633,8 @@
}
- avs_enable(avscsr_enable);
- avs_reset_delays(avsdscr_setting);
+ avs_set_avsdscr(avsdscr);
+ avs_set_avscsr(avscsr);
msm_pm_config_hw_after_power_up();
if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
pr_info("CPU%u: %s: post power up\n", cpu, __func__);
diff --git a/drivers/gpu/ion/ion_iommu_heap.c b/drivers/gpu/ion/ion_iommu_heap.c
index 0b691f3..5483054 100644
--- a/drivers/gpu/ion/ion_iommu_heap.c
+++ b/drivers/gpu/ion/ion_iommu_heap.c
@@ -76,7 +76,8 @@
goto err2;
for_each_sg(table->sgl, sg, table->nents, i) {
- data->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ data->pages[i] = alloc_page(
+ GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
if (!data->pages[i])
goto err3;
diff --git a/drivers/input/misc/lis3dh_acc.c b/drivers/input/misc/lis3dh_acc.c
index cc4ee9f..ea1b079 100644
--- a/drivers/input/misc/lis3dh_acc.c
+++ b/drivers/input/misc/lis3dh_acc.c
@@ -274,6 +274,7 @@
rc = PTR_ERR(lis3dh_acc_vreg[i].vreg);
pr_err("%s:regulator get failed rc=%d\n",
__func__, rc);
+ lis3dh_acc_vreg[i].vreg = NULL;
goto error_vdd;
}
@@ -287,6 +288,7 @@
pr_err("%s: set voltage failed rc=%d\n",
__func__, rc);
regulator_put(lis3dh_acc_vreg[i].vreg);
+ lis3dh_acc_vreg[i].vreg = NULL;
goto error_vdd;
}
}
@@ -302,6 +304,7 @@
lis3dh_acc_vreg[i].max_uV);
}
regulator_put(lis3dh_acc_vreg[i].vreg);
+ lis3dh_acc_vreg[i].vreg = NULL;
goto error_vdd;
}
}
@@ -312,12 +315,16 @@
error_vdd:
while (--i >= 0) {
- if (regulator_count_voltages(lis3dh_acc_vreg[i].vreg) > 0) {
- regulator_set_voltage(lis3dh_acc_vreg[i].vreg, 0,
- lis3dh_acc_vreg[i].max_uV);
+ if (!IS_ERR_OR_NULL(lis3dh_acc_vreg[i].vreg)) {
+ if (regulator_count_voltages(
+ lis3dh_acc_vreg[i].vreg) > 0) {
+ regulator_set_voltage(lis3dh_acc_vreg[i].vreg,
+ 0, lis3dh_acc_vreg[i].max_uV);
+ }
+ regulator_disable(lis3dh_acc_vreg[i].vreg);
+ regulator_put(lis3dh_acc_vreg[i].vreg);
+ lis3dh_acc_vreg[i].vreg = NULL;
}
- regulator_disable(lis3dh_acc_vreg[i].vreg);
- regulator_put(lis3dh_acc_vreg[i].vreg);
}
return rc;
}
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
index db6f93c..6c64a57 100644
--- a/drivers/input/misc/mpu3050.c
+++ b/drivers/input/misc/mpu3050.c
@@ -184,6 +184,7 @@
rc = PTR_ERR(mpu_vreg[i].vreg);
pr_err("%s:regulator get failed rc=%d\n",
__func__, rc);
+ mpu_vreg[i].vreg = NULL;
goto error_vdd;
}
@@ -194,6 +195,7 @@
pr_err("%s:set_voltage failed rc=%d\n",
__func__, rc);
regulator_put(mpu_vreg[i].vreg);
+ mpu_vreg[i].vreg = NULL;
goto error_vdd;
}
}
@@ -210,6 +212,7 @@
0, mpu_vreg[i].max_uV);
}
regulator_put(mpu_vreg[i].vreg);
+ mpu_vreg[i].vreg = NULL;
goto error_vdd;
}
}
@@ -219,12 +222,16 @@
}
error_vdd:
while (--i >= 0) {
- if (regulator_count_voltages(mpu_vreg[i].vreg) > 0) {
- regulator_set_voltage(mpu_vreg[i].vreg, 0,
+ if (!IS_ERR_OR_NULL(mpu_vreg[i].vreg)) {
+ if (regulator_count_voltages(
+ mpu_vreg[i].vreg) > 0) {
+ regulator_set_voltage(mpu_vreg[i].vreg, 0,
mpu_vreg[i].max_uV);
+ }
+ regulator_disable(mpu_vreg[i].vreg);
+ regulator_put(mpu_vreg[i].vreg);
+ mpu_vreg[i].vreg = NULL;
}
- regulator_disable(mpu_vreg[i].vreg);
- regulator_put(mpu_vreg[i].vreg);
}
return rc;
}
diff --git a/drivers/iommu/msm_iommu_sec.c b/drivers/iommu/msm_iommu_sec.c
index 72ec4a6..a6483b9 100644
--- a/drivers/iommu/msm_iommu_sec.c
+++ b/drivers/iommu/msm_iommu_sec.c
@@ -74,9 +74,9 @@
unsigned int spare;
} pinit;
unsigned int *buf;
- int psize[2] = {0};
+ int psize[2] = {0, 0};
unsigned int spare;
- int ret, ptbl_ret;
+ int ret, ptbl_ret = 0;
for_each_compatible_node(np, NULL, "qcom,msm-smmu-v2")
if (of_find_property(np, "qcom,iommu-secure-id", NULL))
@@ -134,7 +134,7 @@
unsigned int id;
unsigned int spare;
} cfg;
- int ret, scm_ret;
+ int ret, scm_ret = 0;
cfg.id = sec_id;
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index 38e8de1..0979e99 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -92,11 +92,10 @@
.src = MSM_BUS_MASTER_VIDEO_P0_OCMEM,
.dst = MSM_BUS_SLAVE_OCMEM,
.ab = 3760000000U,
- .ib = 3910400000U,
+ .ib = 4888000000ULL,
},
};
-
static struct msm_bus_vectors dec_ocmem_init_vectors[] = {
{
.src = MSM_BUS_MASTER_VIDEO_P0_OCMEM,
@@ -147,7 +146,7 @@
.src = MSM_BUS_MASTER_VIDEO_P0_OCMEM,
.dst = MSM_BUS_SLAVE_OCMEM,
.ab = 2767360000U,
- .ib = 3113280000U,
+ .ib = 4981248000ULL,
},
};
@@ -156,7 +155,7 @@
.src = MSM_BUS_MASTER_VIDEO_P0_OCMEM,
.dst = MSM_BUS_SLAVE_OCMEM,
.ab = 3459200000U,
- .ib = 3459200000U,
+ .ib = 6226560000ULL,
},
};
@@ -295,7 +294,7 @@
.src = MSM_BUS_MASTER_VIDEO_P0,
.dst = MSM_BUS_SLAVE_EBI_CH0,
.ab = 161200000,
- .ib = 2659800000U,
+ .ib = 6400000000ULL,
},
};
@@ -358,7 +357,7 @@
.src = MSM_BUS_MASTER_VIDEO_P0,
.dst = MSM_BUS_SLAVE_EBI_CH0,
.ab = 2020000000U,
- .ib = 3636000000U,
+ .ib = 6400000000ULL,
},
};
diff --git a/drivers/media/video/msm_vidc/msm_vdec.c b/drivers/media/video/msm_vidc/msm_vdec.c
index 48e6a93..440c704 100644
--- a/drivers/media/video/msm_vidc/msm_vdec.c
+++ b/drivers/media/video/msm_vidc/msm_vdec.c
@@ -22,8 +22,8 @@
#define MSM_VDEC_DVC_NAME "msm_vdec_8974"
#define DEFAULT_HEIGHT 720
#define DEFAULT_WIDTH 1280
-#define MAX_SUPPORTED_WIDTH 1920
-#define MAX_SUPPORTED_HEIGHT 1088
+#define MAX_SUPPORTED_WIDTH 3820
+#define MAX_SUPPORTED_HEIGHT 2160
#define MIN_NUM_OUTPUT_BUFFERS 4
#define MAX_NUM_OUTPUT_BUFFERS 6
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index cb1da06..327f39c 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -1810,7 +1810,8 @@
ret = qpnp_adc_tm_is_ready();
if (ret == -EPROBE_DEFER) {
- queue_delayed_work(system_nrt_wq, to_delayed_work(w), 100);
+ queue_delayed_work(system_nrt_wq, to_delayed_work(w),
+ msecs_to_jiffies(100));
return;
}
diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile
index a0d707e..4deaa8c 100644
--- a/drivers/video/msm/mdss/Makefile
+++ b/drivers/video/msm/mdss/Makefile
@@ -20,5 +20,6 @@
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_util.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_edid.o
obj-$(CONFIG_FB_MSM_MDSS_HDMI_MHL_8334) += mhl_sii8334.o
+obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_hdcp.o
obj-$(CONFIG_FB_MSM_MDSS_WRITEBACK) += mdss_wb.o
diff --git a/drivers/video/msm/mdss/mdss_hdmi_hdcp.c b/drivers/video/msm/mdss/mdss_hdmi_hdcp.c
new file mode 100644
index 0000000..e361510
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_hdmi_hdcp.c
@@ -0,0 +1,1180 @@
+/* Copyright (c) 2010-2012 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "mdss_hdmi_hdcp.h"
+
+#define HDCP_STATE_NAME (hdcp_state_name(hdcp_ctrl->hdcp_state))
+
+/* HDCP Keys state based on HDMI_HDCP_LINK0_STATUS:KEYS_STATE */
+#define HDCP_KEYS_STATE_NO_KEYS 0
+#define HDCP_KEYS_STATE_NOT_CHECKED 1
+#define HDCP_KEYS_STATE_CHECKING 2
+#define HDCP_KEYS_STATE_VALID 3
+#define HDCP_KEYS_STATE_AKSV_NOT_VALID 4
+#define HDCP_KEYS_STATE_CHKSUM_MISMATCH 5
+#define HDCP_KEYS_STATE_PROD_AKSV 6
+#define HDCP_KEYS_STATE_RESERVED 7
+
+struct hdmi_hdcp_ctrl {
+ enum hdmi_hdcp_state hdcp_state;
+ struct work_struct hdcp_auth_work;
+ struct work_struct hdcp_int_work;
+ struct completion r0_checked;
+ struct hdmi_hdcp_init_data init_data;
+ struct timer_list hdcp_timer;
+};
+
+const char *hdcp_state_name(enum hdmi_hdcp_state hdcp_state)
+{
+ switch (hdcp_state) {
+ case HDCP_STATE_INACTIVE: return "HDCP_STATE_INACTIVE";
+ case HDCP_STATE_AUTHENTICATING: return "HDCP_STATE_AUTHENTICATING";
+ case HDCP_STATE_AUTHENTICATED: return "HDCP_STATE_AUTHENTICATED";
+ case HDCP_STATE_AUTH_FAIL: return "HDCP_STATE_AUTH_FAIL";
+ default: return "???";
+ }
+} /* hdcp_state_name */
+
+static int hdmi_hdcp_count_one(u8 *array, u8 len)
+{
+ int i, j, count = 0;
+ for (i = 0; i < len; i++)
+ for (j = 0; j < 8; j++)
+ count += (((array[i] >> j) & 0x1) ? 1 : 0);
+ return count;
+} /* hdmi_hdcp_count_one */
+
+static void reset_hdcp_ddc_failures(struct hdmi_hdcp_ctrl *hdcp_ctrl)
+{
+ int hdcp_ddc_ctrl1_reg;
+ int hdcp_ddc_status;
+ int failure;
+ int nack0;
+ struct dss_io_data *io;
+
+ if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ io = hdcp_ctrl->init_data.core_io;
+
+ /* Check for any DDC transfer failures */
+ hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS);
+ failure = (hdcp_ddc_status >> 16) & 0x1;
+ nack0 = (hdcp_ddc_status >> 14) & 0x1;
+ DEV_DBG("%s: %s: On Entry: HDCP_DDC_STATUS=0x%x, FAIL=%d, NACK0=%d\n",
+ __func__, HDCP_STATE_NAME, hdcp_ddc_status, failure, nack0);
+
+ if (failure == 0x1) {
+ /*
+ * Indicates that the last HDCP HW DDC transfer failed.
+ * This occurs when a transfer is attempted with HDCP DDC
+ * disabled (HDCP_DDC_DISABLE=1) or the number of retries
+ * matches HDCP_DDC_RETRY_CNT.
+ * Failure occured, let's clear it.
+ */
+ DEV_DBG("%s: %s: DDC failure detected.HDCP_DDC_STATUS=0x%08x\n",
+ __func__, HDCP_STATE_NAME, hdcp_ddc_status);
+
+ /* First, Disable DDC */
+ DSS_REG_W(io, HDMI_HDCP_DDC_CTRL_0, BIT(0));
+
+ /* ACK the Failure to Clear it */
+ hdcp_ddc_ctrl1_reg = DSS_REG_R(io, HDMI_HDCP_DDC_CTRL_1);
+ DSS_REG_W(io, HDMI_HDCP_DDC_CTRL_1,
+ hdcp_ddc_ctrl1_reg | BIT(0));
+
+ /* Check if the FAILURE got Cleared */
+ hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS);
+ hdcp_ddc_status = (hdcp_ddc_status >> 16) & BIT(0);
+ if (hdcp_ddc_status == 0x0)
+ DEV_DBG("%s: %s: HDCP DDC Failure cleared\n", __func__,
+ HDCP_STATE_NAME);
+ else
+ DEV_WARN("%s: %s: Unable to clear HDCP DDC Failure",
+ __func__, HDCP_STATE_NAME);
+
+ /* Re-Enable HDCP DDC */
+ DSS_REG_W(io, HDMI_HDCP_DDC_CTRL_0, 0);
+ }
+
+ if (nack0 == 0x1) {
+ DEV_DBG("%s: %s: Before: HDMI_DDC_SW_STATUS=0x%08x\n", __func__,
+ HDCP_STATE_NAME, DSS_REG_R(io, HDMI_DDC_SW_STATUS));
+ /* Reset HDMI DDC software status */
+ DSS_REG_W_ND(io, HDMI_DDC_CTRL,
+ DSS_REG_R(io, HDMI_DDC_CTRL) | BIT(3));
+ msleep(20);
+ DSS_REG_W_ND(io, HDMI_DDC_CTRL,
+ DSS_REG_R(io, HDMI_DDC_CTRL) & ~(BIT(3)));
+
+ /* Reset HDMI DDC Controller */
+ DSS_REG_W_ND(io, HDMI_DDC_CTRL,
+ DSS_REG_R(io, HDMI_DDC_CTRL) | BIT(1));
+ msleep(20);
+ DSS_REG_W_ND(io, HDMI_DDC_CTRL,
+ DSS_REG_R(io, HDMI_DDC_CTRL) & ~BIT(1));
+ DEV_DBG("%s: %s: After: HDMI_DDC_SW_STATUS=0x%08x\n", __func__,
+ HDCP_STATE_NAME, DSS_REG_R(io, HDMI_DDC_SW_STATUS));
+ }
+
+ hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS);
+
+ failure = (hdcp_ddc_status >> 16) & BIT(0);
+ nack0 = (hdcp_ddc_status >> 14) & BIT(0);
+ DEV_DBG("%s: %s: On Exit: HDCP_DDC_STATUS=0x%x, FAIL=%d, NACK0=%d\n",
+ __func__, HDCP_STATE_NAME, hdcp_ddc_status, failure, nack0);
+} /* reset_hdcp_ddc_failures */
+
+static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
+{
+ int rc;
+ u32 qfprom_aksv_lsb, qfprom_aksv_msb;
+ u32 link0_aksv_0, link0_aksv_1;
+ u32 link0_bksv_0, link0_bksv_1;
+ u32 link0_an_0, link0_an_1;
+ u32 timeout_count;
+ bool is_match;
+ bool stale_an = false;
+ struct dss_io_data *io;
+ u8 aksv[5], bksv[5];
+ u8 an[8];
+ u8 bcaps;
+ struct hdmi_tx_ddc_data ddc_data;
+ u32 link0_status, an_ready, keys_state;
+ u8 buf[0xFF];
+
+ if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io ||
+ !hdcp_ctrl->init_data.qfprom_io) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (HDCP_STATE_AUTHENTICATING != hdcp_ctrl->hdcp_state) {
+ DEV_DBG("%s: %s: invalid state. returning\n", __func__,
+ HDCP_STATE_NAME);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ io = hdcp_ctrl->init_data.core_io;
+
+ /* Fetch aksv from QFPROM, this info should be public. */
+ qfprom_aksv_lsb = DSS_REG_R(hdcp_ctrl->init_data.qfprom_io,
+ HDCP_KSV_LSB);
+ qfprom_aksv_msb = DSS_REG_R(hdcp_ctrl->init_data.qfprom_io,
+ HDCP_KSV_MSB);
+
+ aksv[0] = qfprom_aksv_lsb & 0xFF;
+ aksv[1] = (qfprom_aksv_lsb >> 8) & 0xFF;
+ aksv[2] = (qfprom_aksv_lsb >> 16) & 0xFF;
+ aksv[3] = (qfprom_aksv_lsb >> 24) & 0xFF;
+ aksv[4] = qfprom_aksv_msb & 0xFF;
+
+ /* check there are 20 ones in AKSV */
+ if (hdmi_hdcp_count_one(aksv, 5) != 20) {
+ DEV_ERR("%s: %s: AKSV QFPROM doesn't have 20 1's, 20 0's\n",
+ __func__, HDCP_STATE_NAME);
+ DEV_ERR("%s: %s: QFPROM AKSV chk failed (AKSV=%02x%08x)\n",
+ __func__, HDCP_STATE_NAME, qfprom_aksv_msb,
+ qfprom_aksv_lsb);
+ rc = -EINVAL;
+ goto error;
+ }
+ DEV_DBG("%s: %s: AKSV=%02x%08x\n", __func__, HDCP_STATE_NAME,
+ qfprom_aksv_msb, qfprom_aksv_lsb);
+
+ /*
+ * Write AKSV read from QFPROM to the HDCP registers.
+ * This step is needed for HDCP authentication and must be
+ * written before enabling HDCP.
+ */
+ DSS_REG_W(io, HDMI_HDCP_SW_LOWER_AKSV, qfprom_aksv_lsb);
+ DSS_REG_W(io, HDMI_HDCP_SW_UPPER_AKSV, qfprom_aksv_msb);
+
+ /* Check to see if link0_Status has stale values for An ready bit */
+ link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS);
+ DEV_DBG("%s: %s: Before enabling cipher Link0_status=0x%08x\n",
+ __func__, HDCP_STATE_NAME, link0_status);
+ if (link0_status & (BIT(8) | BIT(9))) {
+ DEV_DBG("%s: %s: An ready even before enabling HDCP\n",
+ __func__, HDCP_STATE_NAME);
+ stale_an = true;
+ }
+
+ /*
+ * Read BCAPS
+ * We need to first try to read an HDCP register on the sink to see if
+ * the sink is ready for HDCP authentication
+ */
+ memset(&ddc_data, 0, sizeof(ddc_data));
+ ddc_data.dev_addr = 0x74;
+ ddc_data.offset = 0x40;
+ ddc_data.data_buf = &bcaps;
+ ddc_data.data_len = 1;
+ ddc_data.request_len = 1;
+ ddc_data.retry = 5;
+ ddc_data.what = "Bcaps";
+ ddc_data.no_align = true;
+ rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);
+ if (rc) {
+ DEV_ERR("%s: %s: BCAPS read failed\n", __func__,
+ HDCP_STATE_NAME);
+ goto error;
+ }
+ DEV_DBG("%s: %s: BCAPS=%02x\n", __func__, HDCP_STATE_NAME, bcaps);
+
+ /*
+ * HDCP setup prior to enabling HDCP_CTRL.
+ * Setup seed values for random number An.
+ */
+ DSS_REG_W(io, HDMI_HDCP_ENTROPY_CTRL0, 0xB1FFB0FF);
+ DSS_REG_W(io, HDMI_HDCP_ENTROPY_CTRL1, 0xF00DFACE);
+
+ /* Disable the RngCipher state */
+ DSS_REG_W(io, HDMI_HDCP_DEBUG_CTRL,
+ DSS_REG_R(io, HDMI_HDCP_DEBUG_CTRL) & ~(BIT(2)));
+ DEV_DBG("%s: %s: HDCP_DEBUG_CTRL=0x%08x\n", __func__, HDCP_STATE_NAME,
+ DSS_REG_R(io, HDMI_HDCP_DEBUG_CTRL));
+
+ /* Ensure that all register writes are completed before
+ * enabling HDCP cipher
+ */
+ wmb();
+
+ /*
+ * Enable HDCP
+ * This needs to be done as early as possible in order for the
+ * hardware to make An available to read
+ */
+ DSS_REG_W(io, HDMI_HDCP_CTRL, BIT(0));
+
+ /* Clear any DDC failures from previous tries */
+ reset_hdcp_ddc_failures(hdcp_ctrl);
+
+ /* Write BCAPS to the hardware */
+ DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA12, bcaps);
+
+ /*
+ * If we had stale values for the An ready bit, it should most
+ * likely be cleared now after enabling HDCP cipher
+ */
+ link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS);
+ DEV_DBG("%s: %s: After enabling HDCP Link0_Status=0x%08x\n",
+ __func__, HDCP_STATE_NAME, link0_status);
+ if (!(link0_status & (BIT(8) | BIT(9)))) {
+ DEV_DBG("%s: %s: An not ready after enabling HDCP\n",
+ __func__, HDCP_STATE_NAME);
+ stale_an = false;
+ }
+
+ /* Wait for HDCP keys to be checked and validated */
+ timeout_count = 100;
+ keys_state = (link0_status >> 28) & 0x7;
+ while ((keys_state != HDCP_KEYS_STATE_VALID) &&
+ timeout_count--) {
+ link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS);
+ keys_state = (link0_status >> 28) & 0x7;
+ DEV_DBG("%s: %s: Keys not ready(%d). s=%d\n, l0=%0x08x",
+ __func__, HDCP_STATE_NAME, timeout_count,
+ keys_state, link0_status);
+ msleep(20);
+ }
+
+ if (!timeout_count) {
+ DEV_ERR("%s: %s: Invalid Keys State: %d\n", __func__,
+ HDCP_STATE_NAME, keys_state);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ /*
+ * 1.1_Features turned off by default.
+ * No need to write AInfo since 1.1_Features is disabled.
+ */
+ DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA4, 0);
+
+ /* Wait for An0 and An1 bit to be ready */
+ timeout_count = 100;
+ do {
+ link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS);
+ an_ready = (link0_status & BIT(8)) && (link0_status & BIT(9));
+ if (!an_ready) {
+ DEV_DBG("%s: %s: An not ready(%d). l0_status=0x%08x\n",
+ __func__, HDCP_STATE_NAME, timeout_count,
+ link0_status);
+ msleep(20);
+ }
+ } while (!an_ready && timeout_count--);
+
+ if (!timeout_count) {
+ rc = -ETIMEDOUT;
+ DEV_ERR("%s: %s: timedout, An0=%ld, An1=%ld\n", __func__,
+ HDCP_STATE_NAME, (link0_status & BIT(8)) >> 8,
+ (link0_status & BIT(9)) >> 9);
+ goto error;
+ }
+
+ /*
+ * In cases where An_ready bits had stale values, it would be
+ * better to delay reading of An to avoid any potential of this
+ * read being blocked
+ */
+ if (stale_an) {
+ msleep(200);
+ stale_an = false;
+ }
+
+ /* Read An0 and An1 */
+ link0_an_0 = DSS_REG_R(io, HDMI_HDCP_RCVPORT_DATA5);
+ link0_an_1 = DSS_REG_R(io, HDMI_HDCP_RCVPORT_DATA6);
+
+ /* Read AKSV */
+ link0_aksv_0 = DSS_REG_R(io, HDMI_HDCP_RCVPORT_DATA3);
+ link0_aksv_1 = DSS_REG_R(io, HDMI_HDCP_RCVPORT_DATA4);
+
+ /* Copy An and AKSV to byte arrays for transmission */
+ aksv[0] = link0_aksv_0 & 0xFF;
+ aksv[1] = (link0_aksv_0 >> 8) & 0xFF;
+ aksv[2] = (link0_aksv_0 >> 16) & 0xFF;
+ aksv[3] = (link0_aksv_0 >> 24) & 0xFF;
+ aksv[4] = link0_aksv_1 & 0xFF;
+
+ an[0] = link0_an_0 & 0xFF;
+ an[1] = (link0_an_0 >> 8) & 0xFF;
+ an[2] = (link0_an_0 >> 16) & 0xFF;
+ an[3] = (link0_an_0 >> 24) & 0xFF;
+ an[4] = link0_an_1 & 0xFF;
+ an[5] = (link0_an_1 >> 8) & 0xFF;
+ an[6] = (link0_an_1 >> 16) & 0xFF;
+ an[7] = (link0_an_1 >> 24) & 0xFF;
+
+ /* Write An to offset 0x18 */
+ memset(&ddc_data, 0, sizeof(ddc_data));
+ ddc_data.dev_addr = 0x74;
+ ddc_data.offset = 0x18;
+ ddc_data.data_buf = an;
+ ddc_data.data_len = 8;
+ ddc_data.what = "An";
+ rc = hdmi_ddc_write(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);
+ if (rc) {
+ DEV_ERR("%s: %s: An write failed\n", __func__, HDCP_STATE_NAME);
+ goto error;
+ }
+
+ /* Write AKSV to offset 0x10 */
+ memset(&ddc_data, 0, sizeof(ddc_data));
+ ddc_data.dev_addr = 0x74;
+ ddc_data.offset = 0x10;
+ ddc_data.data_buf = aksv;
+ ddc_data.data_len = 5;
+ ddc_data.what = "Aksv";
+ rc = hdmi_ddc_write(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);
+ if (rc) {
+ DEV_ERR("%s: %s: AKSV write failed\n", __func__,
+ HDCP_STATE_NAME);
+ goto error;
+ }
+ DEV_DBG("%s: %s: Link0-AKSV=%02x%08x\n", __func__,
+ HDCP_STATE_NAME, link0_aksv_1 & 0xFF, link0_aksv_0);
+
+ /* Read BKSV at offset 0x00 */
+ memset(&ddc_data, 0, sizeof(ddc_data));
+ ddc_data.dev_addr = 0x74;
+ ddc_data.offset = 0x00;
+ ddc_data.data_buf = bksv;
+ ddc_data.data_len = 5;
+ ddc_data.request_len = 5;
+ ddc_data.retry = 5;
+ ddc_data.what = "Bksv";
+ ddc_data.no_align = true;
+ rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);
+ if (rc) {
+ DEV_ERR("%s: %s: BKSV read failed\n", __func__,
+ HDCP_STATE_NAME);
+ goto error;
+ }
+
+ /* check there are 20 ones in BKSV */
+ if (hdmi_hdcp_count_one(bksv, 5) != 20) {
+ DEV_ERR("%s: %s: BKSV doesn't have 20 1's and 20 0's\n",
+ __func__, HDCP_STATE_NAME);
+ DEV_ERR("%s: %s: BKSV chk fail. BKSV=%02x%02x%02x%02x%02x\n",
+ __func__, HDCP_STATE_NAME, bksv[4], bksv[3], bksv[2],
+ bksv[1], bksv[0]);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ link0_bksv_0 = bksv[3];
+ link0_bksv_0 = (link0_bksv_0 << 8) | bksv[2];
+ link0_bksv_0 = (link0_bksv_0 << 8) | bksv[1];
+ link0_bksv_0 = (link0_bksv_0 << 8) | bksv[0];
+ link0_bksv_1 = bksv[4];
+ DEV_DBG("%s: %s: BKSV=%02x%08x\n", __func__, HDCP_STATE_NAME,
+ link0_bksv_1, link0_bksv_0);
+
+ /* Write BKSV read from sink to HDCP registers */
+ DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA0, link0_bksv_0);
+ DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA1, link0_bksv_1);
+
+ /* Enable HDCP interrupts and ack/clear any stale interrupts */
+ DSS_REG_W(io, HDMI_HDCP_INT_CTRL, 0xE6);
+
+ /*
+ * HDCP Compliace Test case 1A-01:
+ * Wait here at least 100ms before reading R0'
+ */
+ msleep(125);
+
+ /* Read R0' at offset 0x08 */
+ memset(buf, 0, sizeof(buf));
+ memset(&ddc_data, 0, sizeof(ddc_data));
+ ddc_data.dev_addr = 0x74;
+ ddc_data.offset = 0x08;
+ ddc_data.data_buf = buf;
+ ddc_data.data_len = 2;
+ ddc_data.request_len = 2;
+ ddc_data.retry = 5;
+ ddc_data.what = "R0'";
+ ddc_data.no_align = true;
+ rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);
+ if (rc) {
+ DEV_ERR("%s: %s: R0' read failed\n", __func__, HDCP_STATE_NAME);
+ goto error;
+ }
+ DEV_DBG("%s: %s: R0'=%02x%02x\n", __func__, HDCP_STATE_NAME,
+ buf[1], buf[0]);
+
+ /* Write R0' to HDCP registers and check to see if it is a match */
+ INIT_COMPLETION(hdcp_ctrl->r0_checked);
+ DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA2_0, (((u32)buf[1]) << 8) | buf[0]);
+ timeout_count = wait_for_completion_interruptible_timeout(
+ &hdcp_ctrl->r0_checked, HZ*2);
+ link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS);
+ is_match = link0_status & BIT(12);
+ if (!is_match) {
+ DEV_DBG("%s: %s: Link0_Status=0x%08x\n", __func__,
+ HDCP_STATE_NAME, link0_status);
+ if (!timeout_count) {
+ DEV_ERR("%s: %s: Timeout. No R0 mtch. R0'=%02x%02x\n",
+ __func__, HDCP_STATE_NAME, buf[1], buf[0]);
+ rc = -ETIMEDOUT;
+ goto error;
+ } else {
+ DEV_ERR("%s: %s: R0 mismatch. R0'=%02x%02x\n", __func__,
+ HDCP_STATE_NAME, buf[1], buf[0]);
+ rc = -EINVAL;
+ goto error;
+ }
+ } else {
+ DEV_DBG("%s: %s: R0 matches\n", __func__, HDCP_STATE_NAME);
+ }
+
+error:
+ if (rc) {
+ DEV_ERR("%s: %s: Authentication Part I failed\n", __func__,
+ HDCP_STATE_NAME);
+ } else {
+ /* Enable HDCP Encryption */
+ DSS_REG_W(io, HDMI_HDCP_CTRL, BIT(0) | BIT(8));
+ DEV_INFO("%s: %s: Authentication Part I successful\n",
+ __func__, HDCP_STATE_NAME);
+ }
+ return rc;
+} /* hdmi_hdcp_authentication_part1 */
+
+#define READ_WRITE_V_H(off, name, reg) \
+do { \
+ ddc_data.offset = (off); \
+ memset(what, 0, sizeof(what)); \
+ snprintf(what, sizeof(what), (name)); \
+ rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data); \
+ if (rc) { \
+ DEV_ERR("%s: %s: Read %s failed\n", __func__, HDCP_STATE_NAME, \
+ what); \
+ goto error; \
+ } \
+ DEV_DBG("%s: %s: %s: buf[0]=%x, buf[1]=%x, buf[2]=%x, buf[3]=%x\n", \
+ __func__, HDCP_STATE_NAME, what, buf[0], buf[1], \
+ buf[2], buf[3]); \
+ DSS_REG_W(io, (reg), \
+ (buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0])); \
+} while (0);
+
+static int hdmi_hdcp_transfer_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl)
+{
+ char what[20];
+ int rc = 0;
+ u8 buf[4];
+ struct hdmi_tx_ddc_data ddc_data;
+ struct dss_io_data *io;
+
+ if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ io = hdcp_ctrl->init_data.core_io;
+ memset(&ddc_data, 0, sizeof(ddc_data));
+ ddc_data.dev_addr = 0x74;
+ ddc_data.data_buf = buf;
+ ddc_data.data_len = 4;
+ ddc_data.request_len = 4;
+ ddc_data.retry = 5;
+ ddc_data.what = what;
+ ddc_data.no_align = true;
+
+ /* Read V'.HO 4 Byte at offset 0x20 */
+ READ_WRITE_V_H(0x20, "V' H0", HDMI_HDCP_RCVPORT_DATA7);
+
+ /* Read V'.H1 4 Byte at offset 0x24 */
+ READ_WRITE_V_H(0x24, "V' H1", HDMI_HDCP_RCVPORT_DATA8);
+
+ /* Read V'.H2 4 Byte at offset 0x28 */
+ READ_WRITE_V_H(0x28, "V' H2", HDMI_HDCP_RCVPORT_DATA9);
+
+ /* Read V'.H3 4 Byte at offset 0x2C */
+ READ_WRITE_V_H(0x2C, "V' H3", HDMI_HDCP_RCVPORT_DATA10);
+
+ /* Read V'.H4 4 Byte at offset 0x30 */
+ READ_WRITE_V_H(0x30, "V' H4", HDMI_HDCP_RCVPORT_DATA11);
+
+error:
+ return rc;
+}
+
+static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl)
+{
+ int rc, cnt, i;
+ struct hdmi_tx_ddc_data ddc_data;
+ u32 timeout_count, down_stream_devices;
+ u8 buf[0xFF];
+ u8 ksv_fifo[5 * 127];
+ u8 bcaps;
+ u16 bstatus, max_devs_exceeded, max_cascade_exceeded;
+ u32 link0_status;
+ u32 ksv_bytes;
+ struct dss_io_data *io;
+
+ if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (HDCP_STATE_AUTHENTICATING != hdcp_ctrl->hdcp_state) {
+ DEV_DBG("%s: %s: invalid state. returning\n", __func__,
+ HDCP_STATE_NAME);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ io = hdcp_ctrl->init_data.core_io;
+
+ memset(buf, 0, sizeof(buf));
+ memset(ksv_fifo, 0, sizeof(ksv_fifo));
+
+ /* Read BCAPS at offset 0x40 */
+ memset(&ddc_data, 0, sizeof(ddc_data));
+ ddc_data.dev_addr = 0x74;
+ ddc_data.offset = 0x40;
+ ddc_data.data_buf = &bcaps;
+ ddc_data.data_len = 1;
+ ddc_data.request_len = 1;
+ ddc_data.retry = 5;
+ ddc_data.what = "Bcaps";
+ ddc_data.no_align = false;
+ rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);
+ if (rc) {
+ DEV_ERR("%s: %s: BCAPS read failed\n", __func__,
+ HDCP_STATE_NAME);
+ goto error;
+ }
+ DEV_DBG("%s: %s: BCAPS=%02x (%s)\n", __func__, HDCP_STATE_NAME, bcaps,
+ (bcaps & BIT(6)) ? "repeater" : "no repeater");
+
+ /* if REPEATER (Bit 6), perform Part2 Authentication */
+ if (!(bcaps & BIT(6))) {
+ DEV_INFO("%s: %s: auth part II skipped, no repeater\n",
+ __func__, HDCP_STATE_NAME);
+ return 0;
+ }
+
+ /* Wait until READY bit is set in BCAPS */
+ timeout_count = 50;
+ while (!(bcaps && BIT(5)) && timeout_count) {
+ msleep(100);
+ timeout_count--;
+ /* Read BCAPS at offset 0x40 */
+ memset(&ddc_data, 0, sizeof(ddc_data));
+ ddc_data.dev_addr = 0x74;
+ ddc_data.offset = 0x40;
+ ddc_data.data_buf = &bcaps;
+ ddc_data.data_len = 1;
+ ddc_data.request_len = 1;
+ ddc_data.retry = 5;
+ ddc_data.what = "Bcaps";
+ ddc_data.no_align = false;
+ rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);
+ if (rc) {
+ DEV_ERR("%s: %s: BCAPS read failed\n", __func__,
+ HDCP_STATE_NAME);
+ goto error;
+ }
+ }
+
+ /* Read BSTATUS at offset 0x41 */
+ memset(&ddc_data, 0, sizeof(ddc_data));
+ ddc_data.dev_addr = 0x74;
+ ddc_data.offset = 0x41;
+ ddc_data.data_buf = buf;
+ ddc_data.data_len = 2;
+ ddc_data.request_len = 2;
+ ddc_data.retry = 5;
+ ddc_data.what = "Bstatuss";
+ ddc_data.no_align = false;
+ rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);
+ if (rc) {
+ DEV_ERR("%s: %s: BSTATUS read failed\n", __func__,
+ HDCP_STATE_NAME);
+ goto error;
+ }
+ bstatus = buf[1];
+ bstatus = (bstatus << 8) | buf[0];
+
+ /* Write BSTATUS and BCAPS to HDCP registers */
+ DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA12, bcaps | (bstatus << 8));
+
+ down_stream_devices = bstatus & 0x7F;
+ if (down_stream_devices == 0) {
+ /*
+ * If no downstream devices are attached to the repeater
+ * then part II fails.
+ * todo: The other approach would be to continue PART II.
+ */
+ DEV_ERR("%s: %s: No downstream devices\n", __func__,
+ HDCP_STATE_NAME);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ /*
+ * HDCP Compliance 1B-05:
+ * Check if no. of devices connected to repeater
+ * exceed max_devices_connected from bit 7 of Bstatus.
+ */
+ max_devs_exceeded = (bstatus & BIT(7)) >> 7;
+ if (max_devs_exceeded == 0x01) {
+ DEV_ERR("%s: %s: no. of devs connected exceeds max allowed",
+ __func__, HDCP_STATE_NAME);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ /*
+ * HDCP Compliance 1B-06:
+ * Check if no. of cascade connected to repeater
+ * exceed max_cascade_connected from bit 11 of Bstatus.
+ */
+ max_cascade_exceeded = (bstatus & BIT(11)) >> 11;
+ if (max_cascade_exceeded == 0x01) {
+ DEV_ERR("%s: %s: no. of cascade conn exceeds max allowed",
+ __func__, HDCP_STATE_NAME);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ /*
+ * Read KSV FIFO over DDC
+ * Key Slection vector FIFO Used to pull downstream KSVs
+ * from HDCP Repeaters.
+ * All bytes (DEVICE_COUNT * 5) must be read in a single,
+ * auto incrementing access.
+ * All bytes read as 0x00 for HDCP Receivers that are not
+ * HDCP Repeaters (REPEATER == 0).
+ */
+ ksv_bytes = 5 * down_stream_devices;
+ memset(&ddc_data, 0, sizeof(ddc_data));
+ ddc_data.dev_addr = 0x74;
+ ddc_data.offset = 0x43;
+ ddc_data.data_buf = ksv_fifo;
+ ddc_data.data_len = ksv_bytes;
+ ddc_data.request_len = ksv_bytes;
+ ddc_data.retry = 5;
+ ddc_data.what = "KSV FIFO";
+ ddc_data.no_align = true;
+ cnt = 0;
+ do {
+ rc = hdmi_ddc_read(hdcp_ctrl->init_data.ddc_ctrl, &ddc_data);
+ if (rc) {
+ DEV_ERR("%s: %s: KSV FIFO read failed\n", __func__,
+ HDCP_STATE_NAME);
+ /*
+ * HDCP Compliace Test case 1B-01:
+ * Wait here until all the ksv bytes have been
+ * read from the KSV FIFO register.
+ */
+ msleep(25);
+ } else {
+ break;
+ }
+ cnt++;
+ } while (cnt != 20);
+
+ if (cnt == 20)
+ goto error;
+
+ rc = hdmi_hdcp_transfer_v_h(hdcp_ctrl);
+ if (rc)
+ goto error;
+
+ /*
+ * Write KSV FIFO to HDCP_SHA_DATA.
+ * This is done 1 byte at time starting with the LSB.
+ * On the very last byte write, the HDCP_SHA_DATA_DONE bit[0]
+ */
+
+ /* First, reset SHA engine */
+ DSS_REG_W(io, HDMI_HDCP_SHA_CTRL, 1);
+ /* Next, enable SHA engine, SEL=DIGA_HDCP */
+ DSS_REG_W(io, HDMI_HDCP_SHA_CTRL, 0);
+
+ for (i = 0; i < ksv_bytes - 1; i++) {
+ /* Write KSV byte and do not set DONE bit[0] */
+ DSS_REG_W_ND(io, HDMI_HDCP_SHA_DATA, ksv_fifo[i] << 16);
+
+ /*
+ * Once 64 bytes have been written, we need to poll for
+ * HDCP_SHA_BLOCK_DONE before writing any further
+ */
+ if (i && !((i + 1) % 64)) {
+ timeout_count = 100;
+ while (!(DSS_REG_R(io, HDMI_HDCP_SHA_STATUS) & BIT(0))
+ && (--timeout_count)) {
+ DEV_DBG("%s: %s: Wrote 64 bytes KVS FIFO\n",
+ __func__, HDCP_STATE_NAME);
+ DEV_DBG("%s: %s: HDCP_SHA_STATUS=%08x\n",
+ __func__, HDCP_STATE_NAME,
+ DSS_REG_R(io, HDMI_HDCP_SHA_STATUS));
+ msleep(20);
+ }
+ if (!timeout_count) {
+ rc = -ETIMEDOUT;
+ DEV_ERR("%s: %s: Write KSV FIFO timedout",
+ __func__, HDCP_STATE_NAME);
+ goto error;
+ }
+ }
+
+ }
+
+ /* Write l to DONE bit[0] */
+ DSS_REG_W_ND(io, HDMI_HDCP_SHA_DATA,
+ (ksv_fifo[ksv_bytes - 1] << 16) | 0x1);
+
+ /* Now wait for HDCP_SHA_COMP_DONE */
+ timeout_count = 100;
+ while ((0x10 != (DSS_REG_R(io, HDMI_HDCP_SHA_STATUS)
+ & 0xFFFFFF10)) && --timeout_count)
+ msleep(20);
+ if (!timeout_count) {
+ rc = -ETIMEDOUT;
+ DEV_ERR("%s: %s: SHA computation timedout", __func__,
+ HDCP_STATE_NAME);
+ goto error;
+ }
+
+ /* Wait for V_MATCHES */
+ timeout_count = 100;
+ link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS);
+ while (((link0_status & BIT(20)) != BIT(20)) && --timeout_count) {
+ DEV_DBG("%s: %s: Waiting for V_MATCHES(%d). l0_status=0x%08x\n",
+ __func__, HDCP_STATE_NAME, timeout_count, link0_status);
+ msleep(20);
+ link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS);
+ }
+ if (!timeout_count) {
+ rc = -ETIMEDOUT;
+ DEV_ERR("%s: %s: HDCP V Match timedout", __func__,
+ HDCP_STATE_NAME);
+ goto error;
+ }
+
+error:
+ if (rc)
+ DEV_ERR("%s: %s: Authentication Part II failed\n", __func__,
+ HDCP_STATE_NAME);
+ else
+ DEV_INFO("%s: %s: Authentication Part II successful\n",
+ __func__, HDCP_STATE_NAME);
+ return rc;
+} /* hdmi_hdcp_authentication_part2 */
+
+static void hdmi_hdcp_timer(unsigned long data)
+{
+ struct hdmi_hdcp_ctrl *hdcp_ctrl = (struct hdmi_hdcp_ctrl *)data;
+
+ if (!hdcp_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ if (HDCP_STATE_AUTHENTICATING == hdcp_ctrl->hdcp_state) {
+ DEV_DBG("%s: %s: Queuing work to start HDCP authentication",
+ __func__, HDCP_STATE_NAME);
+ queue_work(hdcp_ctrl->init_data.workq,
+ &hdcp_ctrl->hdcp_auth_work);
+ } else {
+ DEV_DBG("%s: %s: Invalid state\n", __func__, HDCP_STATE_NAME);
+ }
+} /* hdmi_hdcp_timer */
+
+static void hdmi_hdcp_int_work(struct work_struct *work)
+{
+ struct hdmi_hdcp_ctrl *hdcp_ctrl = container_of(work,
+ struct hdmi_hdcp_ctrl, hdcp_int_work);
+
+ if (!hdcp_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ mutex_lock(hdcp_ctrl->init_data.mutex);
+ hdcp_ctrl->hdcp_state = HDCP_STATE_AUTH_FAIL;
+ mutex_unlock(hdcp_ctrl->init_data.mutex);
+
+ if (hdcp_ctrl->init_data.notify_status) {
+ hdcp_ctrl->init_data.notify_status(
+ hdcp_ctrl->init_data.cb_data,
+ hdcp_ctrl->hdcp_state);
+ }
+} /* hdmi_hdcp_int_work */
+
+static void hdmi_hdcp_auth_work(struct work_struct *work)
+{
+ int rc;
+ struct hdmi_hdcp_ctrl *hdcp_ctrl = container_of(work,
+ struct hdmi_hdcp_ctrl, hdcp_auth_work);
+
+ if (!hdcp_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ if (HDCP_STATE_AUTHENTICATING != hdcp_ctrl->hdcp_state) {
+ DEV_DBG("%s: %s: invalid state. returning\n", __func__,
+ HDCP_STATE_NAME);
+ return;
+ }
+
+ rc = hdmi_hdcp_authentication_part1(hdcp_ctrl);
+ if (rc) {
+ DEV_DBG("%s: %s: HDCP Auth Part I failed\n", __func__,
+ HDCP_STATE_NAME);
+ goto error;
+ }
+
+ rc = hdmi_hdcp_authentication_part2(hdcp_ctrl);
+ if (rc) {
+ DEV_DBG("%s: %s: HDCP Auth Part II failed\n", __func__,
+ HDCP_STATE_NAME);
+ goto error;
+ }
+
+error:
+ /*
+ * Ensure that the state did not change during authentication.
+ * If it did, it means that deauthenticate/reauthenticate was
+ * called. In that case, this function need not notify HDMI Tx
+ * of the result
+ */
+ mutex_lock(hdcp_ctrl->init_data.mutex);
+ if (HDCP_STATE_AUTHENTICATING == hdcp_ctrl->hdcp_state) {
+ if (rc)
+ hdcp_ctrl->hdcp_state = HDCP_STATE_AUTH_FAIL;
+ else
+ hdcp_ctrl->hdcp_state = HDCP_STATE_AUTHENTICATED;
+ mutex_unlock(hdcp_ctrl->init_data.mutex);
+
+ /* Notify HDMI Tx controller of the result */
+ DEV_DBG("%s: %s: Notifying HDMI Tx of auth result\n",
+ __func__, HDCP_STATE_NAME);
+ if (hdcp_ctrl->init_data.notify_status) {
+ hdcp_ctrl->init_data.notify_status(
+ hdcp_ctrl->init_data.cb_data,
+ hdcp_ctrl->hdcp_state);
+ }
+ } else {
+ DEV_DBG("%s: %s: HDCP state changed during authentication\n",
+ __func__, HDCP_STATE_NAME);
+ mutex_unlock(hdcp_ctrl->init_data.mutex);
+ }
+ return;
+} /* hdmi_hdcp_auth_work */
+
+int hdmi_hdcp_authenticate(void *input)
+{
+ struct hdmi_hdcp_ctrl *hdcp_ctrl = (struct hdmi_hdcp_ctrl *)input;
+
+ if (!hdcp_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ if (HDCP_STATE_INACTIVE != hdcp_ctrl->hdcp_state) {
+ DEV_DBG("%s: %s: already active or activating. returning\n",
+ __func__, HDCP_STATE_NAME);
+ return 0;
+ }
+
+ DEV_DBG("%s: %s: Queuing work to start HDCP authentication", __func__,
+ HDCP_STATE_NAME);
+ mutex_lock(hdcp_ctrl->init_data.mutex);
+ hdcp_ctrl->hdcp_state = HDCP_STATE_AUTHENTICATING;
+ mutex_unlock(hdcp_ctrl->init_data.mutex);
+ queue_work(hdcp_ctrl->init_data.workq, &hdcp_ctrl->hdcp_auth_work);
+
+ return 0;
+} /* hdmi_hdcp_authenticate */
+
+int hdmi_hdcp_reauthenticate(void *input)
+{
+ struct hdmi_hdcp_ctrl *hdcp_ctrl = (struct hdmi_hdcp_ctrl *)input;
+ struct dss_io_data *io;
+
+ if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ io = hdcp_ctrl->init_data.core_io;
+
+
+ if (HDCP_STATE_AUTH_FAIL != hdcp_ctrl->hdcp_state) {
+ DEV_DBG("%s: %s: invalid state. returning\n", __func__,
+ HDCP_STATE_NAME);
+ return 0;
+ }
+
+ /*
+ * Disable HPD circuitry.
+ * This is needed to reset the HDCP cipher engine so that when we
+ * attempt a re-authentication, HW would clear the AN0_READY and
+ * AN1_READY bits in HDMI_HDCP_LINK0_STATUS register
+ */
+ DSS_REG_W(io, HDMI_HPD_CTRL, DSS_REG_R(hdcp_ctrl->init_data.core_io,
+ HDMI_HPD_CTRL) & ~BIT(28));
+
+ /* Disable HDCP interrupts */
+ DSS_REG_W(io, HDMI_HDCP_INT_CTRL, 0);
+
+ DSS_REG_W(io, HDMI_HDCP_RESET, BIT(0));
+
+ /* Disable encryption and disable the HDCP block */
+ DSS_REG_W(io, HDMI_HDCP_CTRL, 0);
+
+ /* Enable HPD circuitry */
+ DSS_REG_W(hdcp_ctrl->init_data.core_io, HDMI_HPD_CTRL,
+ DSS_REG_R(hdcp_ctrl->init_data.core_io,
+ HDMI_HPD_CTRL) | BIT(28));
+
+ /* Restart authentication attempt */
+ DEV_DBG("%s: %s: Scheduling timer to start HDCP authentication",
+ __func__, HDCP_STATE_NAME);
+ mutex_lock(hdcp_ctrl->init_data.mutex);
+ hdcp_ctrl->hdcp_state = HDCP_STATE_AUTHENTICATING;
+ mutex_unlock(hdcp_ctrl->init_data.mutex);
+ mod_timer(&hdcp_ctrl->hdcp_timer, jiffies + HZ/2);
+
+ return 0;
+} /* hdmi_hdcp_reauthenticate */
+
+void hdmi_hdcp_off(void *input)
+{
+ struct hdmi_hdcp_ctrl *hdcp_ctrl = (struct hdmi_hdcp_ctrl *)input;
+ struct dss_io_data *io;
+ int rc = 0;
+
+ if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ io = hdcp_ctrl->init_data.core_io;
+
+ if (HDCP_STATE_INACTIVE == hdcp_ctrl->hdcp_state) {
+ DEV_DBG("%s: %s: inactive. returning\n", __func__,
+ HDCP_STATE_NAME);
+ return;
+ }
+
+ /*
+ * Need to set the state to inactive here so that any ongoing
+ * reauth works will know that the HDCP session has been turned off
+ */
+ mutex_lock(hdcp_ctrl->init_data.mutex);
+ hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE;
+ mutex_unlock(hdcp_ctrl->init_data.mutex);
+
+ /* Disable HDCP interrupts */
+ DSS_REG_W(io, HDMI_HDCP_INT_CTRL, 0);
+
+ /*
+ * Cancel any pending auth/reauth attempts.
+ * If one is ongoing, this will wait for it to finish.
+ * No more reauthentiaction attempts will be scheduled since we
+ * set the currect state to inactive.
+ */
+ rc = del_timer_sync(&hdcp_ctrl->hdcp_timer);
+ if (rc)
+ DEV_DBG("%s: %s: Deleted hdcp reauth timer\n", __func__,
+ HDCP_STATE_NAME);
+ rc = cancel_work_sync(&hdcp_ctrl->hdcp_auth_work);
+ if (rc)
+ DEV_DBG("%s: %s: Deleted hdcp auth work\n", __func__,
+ HDCP_STATE_NAME);
+ rc = cancel_work_sync(&hdcp_ctrl->hdcp_int_work);
+ if (rc)
+ DEV_DBG("%s: %s: Deleted hdcp int work\n", __func__,
+ HDCP_STATE_NAME);
+
+ DSS_REG_W(io, HDMI_HDCP_RESET, BIT(0));
+
+ /* Disable encryption and disable the HDCP block */
+ DSS_REG_W(io, HDMI_HDCP_CTRL, 0);
+
+ DEV_DBG("%s: %s: HDCP: Off\n", __func__, HDCP_STATE_NAME);
+} /* hdmi_hdcp_off */
+
+int hdmi_hdcp_isr(void *input)
+{
+ struct hdmi_hdcp_ctrl *hdcp_ctrl = (struct hdmi_hdcp_ctrl *)input;
+ int rc = 0;
+ struct dss_io_data *io;
+ u32 hdcp_int_val;
+
+ if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ io = hdcp_ctrl->init_data.core_io;
+
+ /* Ignore HDCP interrupts if HDCP is disabled */
+ if (HDCP_STATE_INACTIVE == hdcp_ctrl->hdcp_state)
+ return 0;
+
+ hdcp_int_val = DSS_REG_R(io, HDMI_HDCP_INT_CTRL);
+ if (hdcp_int_val & BIT(0)) {
+ /* AUTH_SUCCESS_INT */
+ DSS_REG_W(io, HDMI_HDCP_INT_CTRL, (hdcp_int_val | BIT(1)));
+ DEV_INFO("%s: %s: AUTH_SUCCESS_INT received\n", __func__,
+ HDCP_STATE_NAME);
+ if (HDCP_STATE_AUTHENTICATING == hdcp_ctrl->hdcp_state)
+ complete_all(&hdcp_ctrl->r0_checked);
+ }
+
+ if (hdcp_int_val & BIT(4)) {
+ /* AUTH_FAIL_INT */
+ u32 link_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS);
+ DSS_REG_W(io, HDMI_HDCP_INT_CTRL, (hdcp_int_val | BIT(5)));
+ DEV_INFO("%s: %s: AUTH_FAIL_INT rcvd, LINK0_STATUS=0x%08x\n",
+ __func__, HDCP_STATE_NAME, link_status);
+ if (HDCP_STATE_AUTHENTICATED == hdcp_ctrl->hdcp_state) {
+ /* Inform HDMI Tx of the failure */
+ queue_work(hdcp_ctrl->init_data.workq,
+ &hdcp_ctrl->hdcp_int_work);
+ /* todo: print debug log with auth fail reason */
+ } else if (HDCP_STATE_AUTHENTICATING == hdcp_ctrl->hdcp_state) {
+ complete_all(&hdcp_ctrl->r0_checked);
+ }
+
+ /* Clear AUTH_FAIL_INFO as well */
+ DSS_REG_W(io, HDMI_HDCP_INT_CTRL, (hdcp_int_val | BIT(7)));
+ }
+
+ if (hdcp_int_val & BIT(8)) {
+ /* DDC_XFER_REQ_INT */
+ DSS_REG_W(io, HDMI_HDCP_INT_CTRL, (hdcp_int_val | BIT(9)));
+ DEV_INFO("%s: %s: DDC_XFER_REQ_INT received\n", __func__,
+ HDCP_STATE_NAME);
+ }
+
+ if (hdcp_int_val & BIT(12)) {
+ /* DDC_XFER_DONE_INT */
+ DSS_REG_W(io, HDMI_HDCP_INT_CTRL, (hdcp_int_val | BIT(13)));
+ DEV_INFO("%s: %s: DDC_XFER_DONE received\n", __func__,
+ HDCP_STATE_NAME);
+ }
+
+error:
+ return rc;
+} /* hdmi_hdcp_isr */
+
+void hdmi_hdcp_deinit(void *input)
+{
+ struct hdmi_hdcp_ctrl *hdcp_ctrl = (struct hdmi_hdcp_ctrl *)input;
+
+ if (!hdcp_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ del_timer_sync(&hdcp_ctrl->hdcp_timer);
+ kfree(hdcp_ctrl);
+} /* hdmi_hdcp_deinit */
+
+void *hdmi_hdcp_init(struct hdmi_hdcp_init_data *init_data)
+{
+ struct hdmi_hdcp_ctrl *hdcp_ctrl = NULL;
+
+ if (!init_data || !init_data->core_io || !init_data->qfprom_io ||
+ !init_data->mutex || !init_data->ddc_ctrl ||
+ !init_data->notify_status || !init_data->workq ||
+ !init_data->cb_data) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ goto error;
+ }
+
+ hdcp_ctrl = kzalloc(sizeof(*hdcp_ctrl), GFP_KERNEL);
+ if (!hdcp_ctrl) {
+ DEV_ERR("%s: Out of memory\n", __func__);
+ goto error;
+ }
+
+ hdcp_ctrl->init_data = *init_data;
+
+ INIT_WORK(&hdcp_ctrl->hdcp_auth_work, hdmi_hdcp_auth_work);
+ INIT_WORK(&hdcp_ctrl->hdcp_int_work, hdmi_hdcp_int_work);
+
+ init_timer(&hdcp_ctrl->hdcp_timer);
+ hdcp_ctrl->hdcp_timer.function = hdmi_hdcp_timer;
+ hdcp_ctrl->hdcp_timer.data = (u32)hdcp_ctrl;
+ hdcp_ctrl->hdcp_timer.expires = 0xffffffffL;
+
+ hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE;
+ init_completion(&hdcp_ctrl->r0_checked);
+ DEV_DBG("%s: HDCP module initialized. HDCP_STATE=%s", __func__,
+ HDCP_STATE_NAME);
+
+error:
+ return (void *)hdcp_ctrl;
+} /* hdmi_hdcp_init */
diff --git a/drivers/video/msm/mdss/mdss_hdmi_hdcp.h b/drivers/video/msm/mdss/mdss_hdmi_hdcp.h
new file mode 100644
index 0000000..d35b2a9
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_hdmi_hdcp.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2012 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+
+#ifndef __MDSS_HDMI_HDCP_H__
+#define __MDSS_HDMI_HDCP_H__
+
+#include "mdss_hdmi_util.h"
+
+enum hdmi_hdcp_state {
+ HDCP_STATE_INACTIVE,
+ HDCP_STATE_AUTHENTICATING,
+ HDCP_STATE_AUTHENTICATED,
+ HDCP_STATE_AUTH_FAIL
+};
+
+struct hdmi_hdcp_init_data {
+ struct dss_io_data *core_io;
+ struct dss_io_data *qfprom_io;
+ struct mutex *mutex;
+ struct workqueue_struct *workq;
+ void *cb_data;
+ void (*notify_status)(void *cb_data, enum hdmi_hdcp_state status);
+
+ struct hdmi_tx_ddc_ctrl *ddc_ctrl;
+};
+
+const char *hdcp_state_name(enum hdmi_hdcp_state hdcp_state);
+void *hdmi_hdcp_init(struct hdmi_hdcp_init_data *init_data);
+void hdmi_hdcp_deinit(void *input);
+int hdmi_hdcp_isr(void *ptr);
+int hdmi_hdcp_reauthenticate(void *input);
+int hdmi_hdcp_authenticate(void *hdcp_ctrl);
+void hdmi_hdcp_off(void *hdcp_ctrl);
+#endif /* __MDSS_HDMI_HDCP_H__ */
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.c b/drivers/video/msm/mdss/mdss_hdmi_tx.c
index 9235856..8434129 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.c
@@ -26,6 +26,7 @@
#include "mdss_fb.h"
#include "mdss_hdmi_tx.h"
#include "mdss_hdmi_edid.h"
+#include "mdss_hdmi_hdcp.h"
#include "mdss.h"
#include "mdss_panel.h"
@@ -45,6 +46,9 @@
((d & 0xff) + ((d >> 8) & 0xff) + \
((d >> 16) & 0xff) + ((d >> 24) & 0xff))
+/* Enable HDCP by default */
+static bool hdcp_feature_on = true;
+
/* parameters for clock regeneration */
struct hdmi_tx_audio_acr {
u32 n;
@@ -421,10 +425,75 @@
hdmi_ctrl->kobj = NULL;
} /* hdmi_tx_sysfs_remove */
+static inline u32 hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl)
+{
+ return hdmi_edid_get_sink_mode(
+ hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID]) ? 0 : 1;
+} /* hdmi_tx_is_dvi_mode */
+
+static inline void hdmi_tx_set_audio_switch_node(struct hdmi_tx_ctrl *hdmi_ctrl,
+ int val, bool force)
+{
+ if (!hdmi_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ if (!hdmi_tx_is_dvi_mode(hdmi_ctrl) &&
+ (force || (hdmi_ctrl->audio_sdev.state != val))) {
+ switch_set_state(&hdmi_ctrl->audio_sdev, val);
+ DEV_INFO("%s: hdmi_audio state switched to %d\n", __func__,
+ hdmi_ctrl->audio_sdev.state);
+ }
+} /* hdmi_tx_set_audio_switch_node */
+
+void hdmi_tx_hdcp_cb(void *ptr, enum hdmi_hdcp_state status)
+{
+ int rc = 0;
+ struct hdmi_tx_ctrl *hdmi_ctrl = (struct hdmi_tx_ctrl *)ptr;
+
+ if (!hdmi_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ DEV_DBG("%s: HDCP status=%s hpd_state=%d\n", __func__,
+ hdcp_state_name(status), hdmi_ctrl->hpd_state);
+
+ switch (status) {
+ case HDCP_STATE_AUTHENTICATED:
+ if (hdmi_ctrl->hpd_state)
+ hdmi_tx_set_audio_switch_node(hdmi_ctrl, 1, false);
+ break;
+ case HDCP_STATE_AUTH_FAIL:
+ hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0, false);
+
+ if (hdmi_ctrl->hpd_state) {
+ DEV_DBG("%s: Reauthenticating\n", __func__);
+ rc = hdmi_hdcp_reauthenticate(
+ hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP]);
+ if (rc)
+ DEV_ERR("%s: HDCP reauth failed. rc=%d\n",
+ __func__, rc);
+ } else {
+ DEV_DBG("%s: Not reauthenticating. Cable not conn\n",
+ __func__);
+ }
+
+ break;
+ case HDCP_STATE_AUTHENTICATING:
+ case HDCP_STATE_INACTIVE:
+ default:
+ break;
+ /* do nothing */
+ }
+}
+
/* Enable HDMI features */
static int hdmi_tx_init_features(struct hdmi_tx_ctrl *hdmi_ctrl)
{
struct hdmi_edid_init_data edid_init_data;
+ struct hdmi_hdcp_init_data hdcp_init_data;
if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -446,6 +515,29 @@
hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID],
hdmi_ctrl->video_resolution);
+ /* Initialize HDCP feature */
+ if (hdmi_ctrl->present_hdcp) {
+ hdcp_init_data.core_io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
+ hdcp_init_data.qfprom_io =
+ &hdmi_ctrl->pdata.io[HDMI_TX_QFPROM_IO];
+ hdcp_init_data.mutex = &hdmi_ctrl->mutex;
+ hdcp_init_data.ddc_ctrl = &hdmi_ctrl->ddc_ctrl;
+ hdcp_init_data.workq = hdmi_ctrl->workq;
+ hdcp_init_data.notify_status = hdmi_tx_hdcp_cb;
+ hdcp_init_data.cb_data = (void *)hdmi_ctrl;
+
+ hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP] =
+ hdmi_hdcp_init(&hdcp_init_data);
+ if (!hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP]) {
+ DEV_ERR("%s: hdmi_hdcp_init failed\n", __func__);
+ hdmi_edid_deinit(hdmi_ctrl->feature_data[
+ HDMI_TX_FEAT_EDID]);
+ hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID] = NULL;
+ return -EPERM;
+ }
+
+ DEV_DBG("%s: HDCP feature initialized\n", __func__);
+ }
return 0;
} /* hdmi_tx_init_features */
@@ -455,12 +547,6 @@
return DSS_REG_R_ND(io, HDMI_CTRL) & BIT(0);
} /* hdmi_tx_is_controller_on */
-static inline u32 hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl)
-{
- return hdmi_edid_get_sink_mode(
- hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID]) ? 0 : 1;
-} /* hdmi_tx_is_dvi_mode */
-
static int hdmi_tx_init_panel_info(uint32_t resolution,
struct mdss_panel_info *pinfo)
{
@@ -556,7 +642,6 @@
static void hdmi_tx_hpd_int_work(struct work_struct *work)
{
struct hdmi_tx_ctrl *hdmi_ctrl = NULL;
- struct dss_io_data *io = NULL;
hdmi_ctrl = container_of(work, struct hdmi_tx_ctrl, hpd_int_work);
if (!hdmi_ctrl || !hdmi_ctrl->hpd_initialized) {
@@ -564,16 +649,8 @@
return;
}
- io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
- if (!io->base) {
- DEV_ERR("%s: Core io is not initialized\n", __func__);
- return;
- }
-
DEV_DBG("%s: Got HPD interrupt\n", __func__);
- hdmi_ctrl->hpd_state =
- (DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(1)) >> 1;
if (hdmi_ctrl->hpd_state) {
hdmi_tx_read_sink_info(hdmi_ctrl);
DEV_INFO("HDMI HPD: sense CONNECTED: send ONLINE\n");
@@ -593,15 +670,22 @@
complete_all(&hdmi_ctrl->hpd_done);
} /* hdmi_tx_hpd_int_work */
-static int hdmi_tx_check_capability(struct dss_io_data *io)
+static int hdmi_tx_check_capability(struct hdmi_tx_ctrl *hdmi_ctrl)
{
u32 hdmi_disabled, hdcp_disabled;
+ struct dss_io_data *io = NULL;
- if (!io) {
+ if (!hdmi_ctrl) {
DEV_ERR("%s: invalid input\n", __func__);
return -EINVAL;
}
+ io = &hdmi_ctrl->pdata.io[HDMI_TX_QFPROM_IO];
+ if (!io->base) {
+ DEV_ERR("%s: QFPROM io is not initialized\n", __func__);
+ return -EINVAL;
+ }
+
hdcp_disabled = DSS_REG_R_ND(io,
QFPROM_RAW_FEAT_CONFIG_ROW0_LSB) & BIT(31);
@@ -616,8 +700,13 @@
return -ENODEV;
}
- if (hdcp_disabled)
+ if (hdcp_disabled) {
+ hdmi_ctrl->present_hdcp = 0;
DEV_WARN("%s: HDCP disabled\n", __func__);
+ } else {
+ hdmi_ctrl->present_hdcp = 1;
+ DEV_DBG("%s: Device is HDCP enabled\n", __func__);
+ }
return 0;
} /* hdmi_tx_check_capability */
@@ -1096,12 +1185,14 @@
reg_val |= BIT(0); /* Enable the block */
if (hdmi_edid_get_sink_mode(
hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID]) == 0) {
- if (hdmi_ctrl->present_hdcp)
+ if (hdmi_ctrl->hdcp_feature_on &&
+ hdmi_ctrl->present_hdcp)
/* HDMI Encryption */
reg_val |= BIT(2);
reg_val |= BIT(1);
} else {
- if (hdmi_ctrl->present_hdcp)
+ if (hdmi_ctrl->hdcp_feature_on &&
+ hdmi_ctrl->present_hdcp)
/* HDMI_Encryption_ON */
reg_val |= BIT(1) | BIT(2);
else
@@ -1689,7 +1780,7 @@
DEV_ERR("%s: audio still on after %d sec. try again\n",
__func__, i+1);
- switch_set_state(&hdmi_ctrl->audio_sdev, 0);
+ hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0, true);
continue;
}
break;
@@ -1744,16 +1835,15 @@
return rc;
}
- switch_set_state(&hdmi_ctrl->audio_sdev, 1);
- DEV_INFO("%s: hdmi_audio state switch to %d\n", __func__,
- hdmi_ctrl->audio_sdev.state);
+ if (!hdmi_ctrl->hdcp_feature_on || !hdmi_ctrl->present_hdcp)
+ hdmi_tx_set_audio_switch_node(hdmi_ctrl, 1, false);
hdmi_tx_set_avi_infoframe(hdmi_ctrl);
hdmi_tx_set_vendor_specific_infoframe(hdmi_ctrl);
hdmi_tx_set_spd_infoframe(hdmi_ctrl);
}
- /* todo: HDCP/CEC */
+ /* todo: CEC */
DEV_INFO("%s: HDMI Core: Initialized\n", __func__);
@@ -1812,11 +1902,13 @@
return;
}
- if (!hdmi_tx_is_dvi_mode(hdmi_ctrl)) {
- switch_set_state(&hdmi_ctrl->audio_sdev, 0);
- DEV_INFO("%s: hdmi_audio state switch to %d\n", __func__,
- hdmi_ctrl->audio_sdev.state);
+ if (hdmi_ctrl->hdcp_feature_on && hdmi_ctrl->present_hdcp) {
+ DEV_DBG("%s: Turning off HDCP\n", __func__);
+ hdmi_hdcp_off(hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP]);
+ }
+ if (!hdmi_tx_is_dvi_mode(hdmi_ctrl)) {
+ hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0, false);
hdmi_tx_audio_off(hdmi_ctrl);
}
@@ -1895,6 +1987,8 @@
return rc;
}
+ hdmi_ctrl->hdcp_feature_on = hdcp_feature_on;
+
DEV_INFO("power: ON (%s)\n", hdmi_get_video_fmt_2string(
hdmi_ctrl->video_resolution));
@@ -1917,7 +2011,6 @@
hdmi_tx_power_off(panel_data);
return rc;
}
- /* todo: HDCP */
} else {
mutex_unlock(&hdmi_ctrl->mutex);
}
@@ -2060,6 +2153,9 @@
}
if (DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(0)) {
+ hdmi_ctrl->hpd_state =
+ (DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(1)) >> 1;
+
/*
* Ack the current hpd interrupt and stop listening to
* new hpd interrupt.
@@ -2071,6 +2167,11 @@
if (hdmi_ddc_isr(&hdmi_ctrl->ddc_ctrl) < 0)
DEV_ERR("%s: hdmi_ddc_isr failed\n", __func__);
+ if (hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP])
+ if (hdmi_hdcp_isr(
+ hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP]) < 0)
+ DEV_ERR("%s: hdmi_hdcp_isr failed\n", __func__);
+
return IRQ_HANDLED;
} /* hdmi_tx_isr */
@@ -2081,6 +2182,11 @@
return;
}
+ if (hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP]) {
+ hdmi_hdcp_deinit(hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP]);
+ hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP] = NULL;
+ }
+
if (hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID])
hdmi_edid_deinit(hdmi_ctrl->feature_data[HDMI_TX_FEAT_EDID]);
@@ -2105,7 +2211,7 @@
pdata = &hdmi_ctrl->pdata;
- rc = hdmi_tx_check_capability(&pdata->io[HDMI_TX_QFPROM_IO]);
+ rc = hdmi_tx_check_capability(hdmi_ctrl);
if (rc) {
DEV_ERR("%s: no HDMI device\n", __func__);
goto fail_no_hdmi;
@@ -2242,6 +2348,14 @@
break;
case MDSS_EVENT_TIMEGEN_ON:
+ if (hdmi_ctrl->hdcp_feature_on && hdmi_ctrl->present_hdcp) {
+ DEV_DBG("%s: Starting HDCP authentication\n", __func__);
+ rc = hdmi_hdcp_authenticate(
+ hdmi_ctrl->feature_data[HDMI_TX_FEAT_HDCP]);
+ if (rc)
+ DEV_ERR("%s: hdcp auth failed. rc=%d\n",
+ __func__, rc);
+ }
break;
case MDSS_EVENT_SUSPEND:
@@ -2978,6 +3092,26 @@
platform_driver_unregister(&this_driver);
} /* hdmi_tx_drv_exit */
+static int set_hdcp_feature_on(const char *val, const struct kernel_param *kp)
+{
+ int rc = 0;
+
+ rc = param_set_bool(val, kp);
+ if (!rc)
+ pr_debug("%s: HDCP feature = %d\n", __func__, hdcp_feature_on);
+
+ return rc;
+}
+
+static struct kernel_param_ops hdcp_feature_on_param_ops = {
+ .set = set_hdcp_feature_on,
+ .get = param_get_bool,
+};
+
+module_param_cb(hdcp, &hdcp_feature_on_param_ops, &hdcp_feature_on,
+ S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(hdcp, "Enable or Disable HDCP");
+
module_init(hdmi_tx_drv_init);
module_exit(hdmi_tx_drv_exit);
diff --git a/drivers/video/msm/mdss/mdss_hdmi_tx.h b/drivers/video/msm/mdss/mdss_hdmi_tx.h
index 5f8094f..f78ce9f 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_tx.h
+++ b/drivers/video/msm/mdss/mdss_hdmi_tx.h
@@ -63,6 +63,7 @@
struct work_struct power_off_work;
+ bool hdcp_feature_on;
u32 present_hdcp;
u8 spd_vendor_name[8];
diff --git a/drivers/video/msm/mdss/mdss_hdmi_util.h b/drivers/video/msm/mdss/mdss_hdmi_util.h
index cd7b7dd..d79b6e7 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_util.h
+++ b/drivers/video/msm/mdss/mdss_hdmi_util.h
@@ -215,6 +215,8 @@
/* QFPROM Registers for HDMI/HDCP */
#define QFPROM_RAW_FEAT_CONFIG_ROW0_LSB (0x000000F8)
#define QFPROM_RAW_FEAT_CONFIG_ROW0_MSB (0x000000FC)
+#define HDCP_KSV_LSB (0x000060D8)
+#define HDCP_KSV_MSB (0x000060DC)
/* all video formats defined by EIA CEA-861-E */
#define HDMI_VFRMT_640x480p60_4_3 0
diff --git a/include/media/Kbuild b/include/media/Kbuild
index 70f6334..fc764eb 100644
--- a/include/media/Kbuild
+++ b/include/media/Kbuild
@@ -8,3 +8,4 @@
header-y += msm_mercury.h
header-y += msm_jpeg.h
header-y += msm_media_info.h
+header-y += msm_vidc.h
diff --git a/include/media/msm_vidc.h b/include/media/msm_vidc.h
index 0fd11a3..4261d34 100644
--- a/include/media/msm_vidc.h
+++ b/include/media/msm_vidc.h
@@ -1,19 +1,8 @@
-/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only 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.
- *
- */
-
#ifndef _MSM_VIDC_H_
#define _MSM_VIDC_H_
+#ifdef __KERNEL__
+
#include <linux/poll.h>
#include <linux/videodev2.h>
@@ -71,3 +60,59 @@
int msm_vidc_wait(void *instance);
int msm_vidc_s_parm(void *instance, struct v4l2_streamparm *a);
#endif
+struct msm_vidc_interlace_payload {
+ unsigned int format;
+};
+struct msm_vidc_framerate_payload {
+ unsigned int frame_rate;
+};
+struct msm_vidc_ts_payload {
+ unsigned int timestamp_hi;
+ unsigned int timestamp_lo;
+};
+struct msm_vidc_concealmb_payload {
+ unsigned int num_mbs;
+};
+struct msm_vidc_recoverysei_payload {
+ unsigned int flags;
+};
+struct msm_vidc_panscan_window {
+ unsigned int panscan_height_offset;
+ unsigned int panscan_width_offset;
+ unsigned int panscan_window_width;
+ unsigned int panscan_window_height;
+};
+struct msm_vidc_panscan_window_payload {
+ unsigned int num_panscan_windows;
+ struct msm_vidc_panscan_window wnd[1];
+};
+enum msm_vidc_extradata_type {
+ EXTRADATA_NONE = 0x00000000,
+ EXTRADATA_MB_QUANTIZATION = 0x00000001,
+ EXTRADATA_INTERLACE_VIDEO = 0x00000002,
+ EXTRADATA_VC1_FRAMEDISP = 0x00000003,
+ EXTRADATA_VC1_SEQDISP = 0x00000004,
+ EXTRADATA_TIMESTAMP = 0x00000005,
+ EXTRADATA_S3D_FRAME_PACKING = 0x00000006,
+ EXTRADATA_FRAME_RATE = 0x00000007,
+ EXTRADATA_PANSCAN_WINDOW = 0x00000008,
+ EXTRADATA_RECOVERY_POINT_SEI = 0x00000009,
+ EXTRADATA_MULTISLICE_INFO = 0x7F100000,
+ EXTRADATA_NUM_CONCEALED_MB = 0x7F100001,
+ EXTRADATA_INDEX = 0x7F100002,
+ EXTRADATA_METADATA_FILLER = 0x7FE00002,
+};
+enum msm_vidc_interlace_type {
+ INTERLACE_FRAME_PROGRESSIVE = 0x01,
+ INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST = 0x02,
+ INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST = 0x04,
+ INTERLACE_FRAME_TOPFIELDFIRST = 0x08,
+ INTERLACE_FRAME_BOTTOMFIELDFIRST = 0x10,
+};
+enum msm_vidc_recovery_sei {
+ FRAME_RECONSTRUCTION_INCORRECT = 0x0,
+ FRAME_RECONSTRUCTION_CORRECT = 0x01,
+ FRAME_RECONSTRUCTION_APPROXIMATELY_CORRECT = 0x02,
+};
+
+#endif
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 9669d4a..8cec741 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -397,8 +397,10 @@
if (sco_pi(sk)->conn) {
sk->sk_state = BT_DISCONN;
sco_sock_set_timer(sk, SCO_DISCONN_TIMEOUT);
- hci_conn_put(sco_pi(sk)->conn->hcon);
- sco_pi(sk)->conn->hcon = NULL;
+ if (sco_pi(sk)->conn->hcon != NULL) {
+ hci_conn_put(sco_pi(sk)->conn->hcon);
+ sco_pi(sk)->conn->hcon = NULL;
+ }
} else
sco_chan_del(sk, ECONNRESET);
break;