summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/msm_hdcp/msm_hdcp.txt14
-rw-r--r--Documentation/devicetree/bindings/regulator/max20010.txt77
-rw-r--r--arch/arm/boot/dts/qcom/msm8998.dtsi4
-rw-r--r--arch/arm64/configs/sdm660-perf_defconfig2
-rw-r--r--arch/arm64/configs/sdm660_defconfig2
-rw-r--r--drivers/base/firmware_class.c1
-rw-r--r--drivers/char/diag/diagchar_core.c17
-rw-r--r--drivers/clk/msm/clock-osm.c174
-rw-r--r--drivers/gpu/drm/msm/Makefile1
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c50
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h2
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c82
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c994
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c461
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h103
-rw-r--r--drivers/gpu/drm/msm/sde_hdcp.h7
-rw-r--r--drivers/gpu/drm/msm/sde_hdcp_1x.c7
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c4
-rw-r--r--drivers/media/platform/msm/camera_v2/msm.c91
-rw-r--r--drivers/media/platform/msm/camera_v2/msm.h5
-rw-r--r--drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c146
-rw-r--r--drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h3
-rw-r--r--drivers/misc/hdcp.c299
-rw-r--r--drivers/mmc/host/sdhci-msm.c8
-rw-r--r--drivers/mmc/host/sdhci-msm.h2
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-v3.h6
-rw-r--r--drivers/platform/msm/mhi/mhi_bhi.c9
-rw-r--r--drivers/platform/msm/mhi/mhi_macros.h3
-rw-r--r--drivers/platform/msm/mhi/mhi_pm.c12
-rw-r--r--drivers/regulator/Kconfig9
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/max20010-regulator.c490
-rw-r--r--drivers/soc/qcom/memshare/msm_memshare.c5
-rw-r--r--include/dt-bindings/regulator/max20010.h20
-rw-r--r--include/linux/hdcp_qseecom.h6
35 files changed, 2898 insertions, 219 deletions
diff --git a/Documentation/devicetree/bindings/msm_hdcp/msm_hdcp.txt b/Documentation/devicetree/bindings/msm_hdcp/msm_hdcp.txt
new file mode 100644
index 000000000000..8d5f55d7a8ca
--- /dev/null
+++ b/Documentation/devicetree/bindings/msm_hdcp/msm_hdcp.txt
@@ -0,0 +1,14 @@
+MSM HDCP driver
+
+Standalone driver managing HDCP related communications
+between TZ and HLOS for MSM chipset.
+
+Required properties:
+
+compatible = "qcom,msm-hdcp";
+
+Example:
+
+qcom_msmhdcp: qcom,msm_hdcp {
+ compatible = "qcom,msm-hdcp";
+};
diff --git a/Documentation/devicetree/bindings/regulator/max20010.txt b/Documentation/devicetree/bindings/regulator/max20010.txt
new file mode 100644
index 000000000000..3dd8f6d1cf19
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/max20010.txt
@@ -0,0 +1,77 @@
+Binding for Maxim MAX20010 regulator
+
+MAX20010 is a synchronous step-down converter. It is able to deliver upto 6A
+with 2 different programmable output voltages from 0.5V to 1.27V in 10mV steps
+and from 0.625V to 1.5875V in 12.5mV steps. It supports synchronous
+rectification and automatic PWM/PFM transitions.
+
+The MAX20010 interface is via I2C bus.
+
+=======================
+Supported Properties
+=======================
+
+- compatible
+ Usage: required
+ Value type: <string>
+ Definition: should be "maxim,max20010".
+
+- reg
+ Usage: required
+ Value type: <u32>
+ Definition: The device 8-bit I2C address.
+
+- vin-supply
+ Usage: optional
+ Value type: <phandle>
+ Definition: This is the phandle for the parent regulator. Typically used
+ for EN pin control of the buck.
+
+- regulator-initial-mode
+ Usage: optional
+ Value type: <u32>
+ Definition: The regulator operating mode. Should be either
+ "MAX20010_OPMODE_SYNC" or "MAX20010_OPMODE_FPWM".
+ These constants are defined in file
+ include/dt-bindings/regulator/max20010.h
+
+- maxim,vrange-sel
+ Usage: optional
+ Value type: <u32>
+ Definition: Integer value specifies the voltage range to be used.
+ Supported values are 0 or 1.
+ Value 0 supports voltage range from 0.5V to 1.27V in 10mV
+ steps. Value 1 supports voltage range from 0.625V to 1.5875V
+ in 12.5mV steps.
+
+- maxim,soft-start-slew-rate
+ Usage: optional
+ Value type: <u32>
+ Definition: An integer value specifies the slew rate in uV/uS to be used
+ for soft-start operation of the buck. Supported values are
+ 5500, 11000, 22000 and 44000.
+
+- maxim,dvs-slew-rate
+ Usage: optional
+ Value type: <u32>
+ Definition: An integer value specifies the slew rate in uV/uS to be used
+ for buck dynamic voltage scaling operations. Supported
+ values are 5500, 11000, 22000 and 44000.
+
+=======
+Example
+=======
+
+ i2c_0 {
+ max20010-regulator@74 {
+ compatible = "maxim,max20010";
+ reg = <0x74>;
+ vin-supply = <&parent_reg>;
+ regulator-min-microvolt = <600000>;
+ regulator-max-microvolt = <1270000>;
+ regulator-initial-mode = <MAX20010_OPMODE_SYNC>;
+ maxim,vrange-sel = <0>;
+ maxim,soft-start-slew-rate = <5500>;
+ maxim,dvs-slew-rate = <5500>;
+ }
+ }
diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi
index 5218a1d86e6d..fc546512992d 100644
--- a/arch/arm/boot/dts/qcom/msm8998.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998.dtsi
@@ -2366,6 +2366,10 @@
hyplog-size-offset = <0x414>; /* 0x066BFB34 */
};
+ qcom_msmhdcp: qcom,msm_hdcp {
+ compatible = "qcom,msm-hdcp";
+ };
+
qcom_crypto: qcrypto@1DE0000 {
compatible = "qcom,qcrypto";
reg = <0x1DE0000 0x20000>,
diff --git a/arch/arm64/configs/sdm660-perf_defconfig b/arch/arm64/configs/sdm660-perf_defconfig
index 939b34f7d6dd..6d6fd23095d5 100644
--- a/arch/arm64/configs/sdm660-perf_defconfig
+++ b/arch/arm64/configs/sdm660-perf_defconfig
@@ -7,6 +7,8 @@ CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
diff --git a/arch/arm64/configs/sdm660_defconfig b/arch/arm64/configs/sdm660_defconfig
index 5084a5505111..25566e45c46f 100644
--- a/arch/arm64/configs/sdm660_defconfig
+++ b/arch/arm64/configs/sdm660_defconfig
@@ -9,6 +9,8 @@ CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_EXPERT=y
CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 212ca2eee257..68561696f31b 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -295,6 +295,7 @@ static void fw_free_buf(struct firmware_buf *buf)
{
struct firmware_cache *fwc = buf->fwc;
if (!fwc) {
+ kfree_const(buf->fw_id);
kfree(buf);
return;
}
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index d8fcfe291e6e..0bc23199b92e 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -481,20 +481,21 @@ static void diag_close_logging_process(const int pid)
params.req_mode = USB_MODE;
params.mode_param = 0;
+ params.pd_mask = 0;
params.peripheral_mask =
diag_translate_kernel_to_user_mask(session_mask);
- for (i = UPD_WLAN; i < NUM_MD_SESSIONS; i++) {
- if (session_mask &
- MD_PERIPHERAL_MASK(i)) {
+ if (driver->num_pd_session > 0) {
+ for (i = UPD_WLAN; ((i < NUM_MD_SESSIONS) &&
+ (session_mask & MD_PERIPHERAL_MASK(i)));
+ i++) {
j = i - UPD_WLAN;
driver->pd_session_clear[j] = 1;
driver->pd_logging_mode[j] = 0;
driver->num_pd_session -= 1;
params.pd_mask =
diag_translate_kernel_to_user_mask(session_mask);
- } else
- params.pd_mask = 0;
+ }
}
diag_switch_logging(&params);
@@ -1612,7 +1613,7 @@ static uint32_t diag_translate_mask(uint32_t peripheral_mask)
static int diag_switch_logging(struct diag_logging_mode_param_t *param)
{
- int new_mode, i;
+ int new_mode, i = 0;
int curr_mode;
int err = 0;
uint8_t do_switch = 1;
@@ -1653,6 +1654,8 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param)
diag_mux->mux_mask)) {
DIAG_LOG(DIAG_DEBUG_USERSPACE,
"diag_fr: User PD is already logging onto active peripheral logging\n");
+ i = upd - UPD_WLAN;
+ driver->pd_session_clear[i] = 0;
return -EINVAL;
}
peripheral_mask =
@@ -1662,8 +1665,8 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param)
if (!driver->pd_session_clear[i]) {
driver->pd_logging_mode[i] = 1;
driver->num_pd_session += 1;
- driver->pd_session_clear[i] = 0;
}
+ driver->pd_session_clear[i] = 0;
} else {
peripheral_mask =
diag_translate_mask(param->peripheral_mask);
diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c
index 7cc1c56a2090..72a75873b810 100644
--- a/drivers/clk/msm/clock-osm.c
+++ b/drivers/clk/msm/clock-osm.c
@@ -606,6 +606,83 @@ static int clk_osm_acd_auto_local_write_reg(struct clk_osm *c, u32 mask)
return 0;
}
+static int clk_osm_acd_init(struct clk_osm *c)
+{
+
+ int rc = 0;
+ u32 auto_xfer_mask = 0;
+
+ if (!c->acd_init)
+ return 0;
+
+ c->acd_debugfs_addr = ACD_HW_VERSION;
+
+ /* Program ACD tunable-length delay register */
+ clk_osm_acd_master_write_reg(c, c->acd_td, ACDTD);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDTD);
+
+ /* Program ACD control register */
+ clk_osm_acd_master_write_reg(c, c->acd_cr, ACDCR);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDCR);
+
+ /* Program ACD soft start control register */
+ clk_osm_acd_master_write_reg(c, c->acd_sscr, ACDSSCR);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDSSCR);
+
+ /* Program initial ACD external interface configuration register */
+ clk_osm_acd_master_write_reg(c, c->acd_extint0_cfg, ACD_EXTINT_CFG);
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_EXTINT_CFG);
+
+ /* Program ACD auto-register transfer control register */
+ clk_osm_acd_master_write_reg(c, c->acd_autoxfer_ctl, ACD_AUTOXFER_CTL);
+
+ /* Ensure writes complete before transfers to local copy */
+ clk_osm_acd_mb(c);
+
+ /* Transfer master copies */
+ rc = clk_osm_acd_auto_local_write_reg(c, auto_xfer_mask);
+ if (rc)
+ return rc;
+
+ /* Switch CPUSS clock source to ACD clock */
+ rc = clk_osm_acd_master_write_through_reg(c, ACD_GFMUX_CFG_SELECT,
+ ACD_GFMUX_CFG);
+ if (rc)
+ return rc;
+
+ /* Program ACD_DCVS_SW */
+ rc = clk_osm_acd_master_write_through_reg(c,
+ ACD_DCVS_SW_DCVS_IN_PRGR_SET,
+ ACD_DCVS_SW);
+ if (rc)
+ return rc;
+
+ rc = clk_osm_acd_master_write_through_reg(c,
+ ACD_DCVS_SW_DCVS_IN_PRGR_CLEAR,
+ ACD_DCVS_SW);
+ if (rc)
+ return rc;
+
+ udelay(1);
+
+ /* Program final ACD external interface configuration register */
+ rc = clk_osm_acd_master_write_through_reg(c, c->acd_extint1_cfg,
+ ACD_EXTINT_CFG);
+ if (rc)
+ return rc;
+
+ /*
+ * ACDCR, ACDTD, ACDSSCR, ACD_EXTINT_CFG, ACD_GFMUX_CFG
+ * must be copied from master to local copy on PC exit.
+ */
+ auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_GFMUX_CFG);
+ clk_osm_acd_master_write_reg(c, auto_xfer_mask, ACD_AUTOXFER_CFG);
+
+ /* ACD has been initialized and enabled for this cluster */
+ c->acd_init = false;
+ return 0;
+}
+
static inline int clk_osm_count_ns(struct clk_osm *c, u64 nsec)
{
u64 temp;
@@ -729,6 +806,17 @@ static int clk_osm_set_rate(struct clk *c, unsigned long rate)
static int clk_osm_enable(struct clk *c)
{
struct clk_osm *cpuclk = to_clk_osm(c);
+ int rc;
+
+ rc = clk_osm_acd_init(cpuclk);
+ if (rc) {
+ pr_err("Failed to initialize ACD for cluster %d, rc=%d\n",
+ cpuclk->cluster_num, rc);
+ return rc;
+ }
+
+ /* Wait for 5 usecs before enabling OSM */
+ udelay(5);
clk_osm_write_reg(cpuclk, 1, ENABLE_REG);
@@ -3105,81 +3193,6 @@ static int clk_osm_panic_callback(struct notifier_block *nfb,
return NOTIFY_OK;
}
-static int clk_osm_acd_init(struct clk_osm *c)
-{
-
- int rc = 0;
- u32 auto_xfer_mask = 0;
-
- if (!c->acd_init)
- return 0;
-
- c->acd_debugfs_addr = ACD_HW_VERSION;
-
- /* Program ACD tunable-length delay register */
- clk_osm_acd_master_write_reg(c, c->acd_td, ACDTD);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDTD);
-
- /* Program ACD control register */
- clk_osm_acd_master_write_reg(c, c->acd_cr, ACDCR);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDCR);
-
- /* Program ACD soft start control register */
- clk_osm_acd_master_write_reg(c, c->acd_sscr, ACDSSCR);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDSSCR);
-
- /* Program initial ACD external interface configuration register */
- clk_osm_acd_master_write_reg(c, c->acd_extint0_cfg, ACD_EXTINT_CFG);
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_EXTINT_CFG);
-
- /* Program ACD auto-register transfer control register */
- clk_osm_acd_master_write_reg(c, c->acd_autoxfer_ctl, ACD_AUTOXFER_CTL);
-
- /* Ensure writes complete before transfers to local copy */
- clk_osm_acd_mb(c);
-
- /* Transfer master copies */
- rc = clk_osm_acd_auto_local_write_reg(c, auto_xfer_mask);
- if (rc)
- return rc;
-
- /* Switch CPUSS clock source to ACD clock */
- rc = clk_osm_acd_master_write_through_reg(c, ACD_GFMUX_CFG_SELECT,
- ACD_GFMUX_CFG);
- if (rc)
- return rc;
-
- /* Program ACD_DCVS_SW */
- rc = clk_osm_acd_master_write_through_reg(c,
- ACD_DCVS_SW_DCVS_IN_PRGR_SET,
- ACD_DCVS_SW);
- if (rc)
- return rc;
-
- rc = clk_osm_acd_master_write_through_reg(c,
- ACD_DCVS_SW_DCVS_IN_PRGR_CLEAR,
- ACD_DCVS_SW);
- if (rc)
- return rc;
-
- udelay(1);
-
- /* Program final ACD external interface configuration register */
- rc = clk_osm_acd_master_write_through_reg(c, c->acd_extint1_cfg,
- ACD_EXTINT_CFG);
- if (rc)
- return rc;
-
- /*
- * ACDCR, ACDTD, ACDSSCR, ACD_EXTINT_CFG, ACD_GFMUX_CFG
- * must be copied from master to local copy on PC exit.
- */
- auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_GFMUX_CFG);
- clk_osm_acd_master_write_reg(c, auto_xfer_mask, ACD_AUTOXFER_CFG);
-
- return 0;
-}
-
static unsigned long init_rate = 300000000;
static unsigned long osm_clk_init_rate = 200000000;
@@ -3362,17 +3375,6 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev)
clk_osm_setup_cluster_pll(&perfcl_clk);
}
- rc = clk_osm_acd_init(&pwrcl_clk);
- if (rc) {
- pr_err("failed to initialize ACD for pwrcl, rc=%d\n", rc);
- return rc;
- }
- rc = clk_osm_acd_init(&perfcl_clk);
- if (rc) {
- pr_err("failed to initialize ACD for perfcl, rc=%d\n", rc);
- return rc;
- }
-
spin_lock_init(&pwrcl_clk.lock);
spin_lock_init(&perfcl_clk.lock);
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index a0ac535d50d1..999d5e45e5c5 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -107,6 +107,7 @@ msm_drm-$(CONFIG_DRM_SDE_HDMI) += \
hdmi-staging/sde_hdmi.o \
hdmi-staging/sde_hdmi_bridge.o \
hdmi-staging/sde_hdmi_audio.o \
+ hdmi-staging/sde_hdmi_hdcp2p2.o \
msm_drm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \
dsi/pll/dsi_pll_28nm.o
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
index 443e0111516c..47bee42c59fa 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
@@ -1039,6 +1039,7 @@ static void _sde_hdmi_cec_update_phys_addr(struct sde_hdmi *display)
static void _sde_hdmi_init_ddc(struct sde_hdmi *display, struct hdmi *hdmi)
{
display->ddc_ctrl.io = &display->io[HDMI_TX_CORE_IO];
+ init_completion(&display->ddc_ctrl.rx_status_done);
}
static void _sde_hdmi_map_regs(struct sde_hdmi *display, struct hdmi *hdmi)
@@ -1127,32 +1128,39 @@ static void _sde_hdmi_cec_irq(struct sde_hdmi *sde_hdmi)
static irqreturn_t _sde_hdmi_irq(int irq, void *dev_id)
{
- struct sde_hdmi *sde_hdmi = dev_id;
+ struct sde_hdmi *display = dev_id;
struct hdmi *hdmi;
- if (!sde_hdmi || !sde_hdmi->ctrl.ctrl) {
- SDE_ERROR("sde_hdmi=%p or hdmi is NULL\n", sde_hdmi);
+ if (!display || !display->ctrl.ctrl) {
+ SDE_ERROR("sde_hdmi=%pK or hdmi is NULL\n", display);
return IRQ_NONE;
}
- hdmi = sde_hdmi->ctrl.ctrl;
+
+ hdmi = display->ctrl.ctrl;
/* Process HPD: */
- _sde_hdmi_connector_irq(sde_hdmi);
+ _sde_hdmi_connector_irq(display);
+
+ /* Process Scrambling ISR */
+ sde_hdmi_ddc_scrambling_isr((void *)display);
+
+ /* Process DDC2 */
+ sde_hdmi_ddc_hdcp2p2_isr((void *)display);
/* Process DDC: */
hdmi_i2c_irq(hdmi->i2c);
/* Process HDCP: */
- if (sde_hdmi->hdcp_ops && sde_hdmi->hdcp_data) {
- if (sde_hdmi->hdcp_ops->isr) {
- if (sde_hdmi->hdcp_ops->isr(
- sde_hdmi->hdcp_data))
+ if (display->hdcp_ops && display->hdcp_data) {
+ if (display->hdcp_ops->isr) {
+ if (display->hdcp_ops->isr(
+ display->hdcp_data))
DEV_ERR("%s: hdcp_1x_isr failed\n",
__func__);
}
}
/* Process CEC: */
- _sde_hdmi_cec_irq(sde_hdmi);
+ _sde_hdmi_cec_irq(display);
return IRQ_HANDLED;
}
@@ -1717,11 +1725,22 @@ static int _sde_hdmi_init_hdcp(struct sde_hdmi *hdmi_ctrl)
kfree(hdcp_data);
goto end;
} else {
- hdmi_ctrl->hdcp_feature_data[SDE_HDCP_1x] = hdcp_data;
+ hdmi_ctrl->hdcp_feat_data[SDE_HDCP_1x] = hdcp_data;
SDE_HDMI_DEBUG("%s: HDCP 1.4 initialized\n", __func__);
}
}
+ hdcp_data = sde_hdmi_hdcp2p2_init(&hdcp_init_data);
+
+ if (IS_ERR_OR_NULL(hdcp_data)) {
+ DEV_ERR("%s: hdcp 2.2 init failed\n", __func__);
+ rc = -EINVAL;
+ goto end;
+ } else {
+ hdmi_ctrl->hdcp_feat_data[SDE_HDCP_2P2] = hdcp_data;
+ SDE_HDMI_DEBUG("%s: HDCP 2.2 initialized\n", __func__);
+ }
+
end:
return rc;
}
@@ -1905,8 +1924,11 @@ int sde_hdmi_dev_deinit(struct sde_hdmi *display)
SDE_ERROR("Invalid params\n");
return -EINVAL;
}
- if (display->hdcp_feature_data[SDE_HDCP_1x])
- sde_hdcp_1x_deinit(display->hdcp_feature_data[SDE_HDCP_1x]);
+ if (display->hdcp_feat_data[SDE_HDCP_1x])
+ sde_hdcp_1x_deinit(display->hdcp_feat_data[SDE_HDCP_1x]);
+
+ if (display->hdcp_feat_data[SDE_HDCP_2P2])
+ sde_hdmi_hdcp2p2_deinit(display->hdcp_feat_data[SDE_HDCP_2P2]);
return 0;
}
@@ -1993,6 +2015,8 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data)
_sde_hdmi_map_regs(display, priv->hdmi);
_sde_hdmi_init_ddc(display, priv->hdmi);
+ display->enc_lvl = HDCP_STATE_AUTH_ENC_NONE;
+
INIT_DELAYED_WORK(&display->hdcp_cb_work,
sde_hdmi_tx_hdcp_cb_work);
mutex_init(&display->hdcp_mutex);
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
index dd61fa794526..6b6518287028 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
@@ -147,7 +147,7 @@ struct sde_hdmi {
*/
void *hdcp_data;
/*hold hdcp init data*/
- void *hdcp_feature_data[2];
+ void *hdcp_feat_data[2];
struct sde_hdcp_ops *hdcp_ops;
struct sde_hdmi_tx_ddc_ctrl ddc_ctrl;
struct work_struct hpd_work;
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
index a9673d152cb7..6b01d02930f8 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c
@@ -32,8 +32,7 @@ struct sde_hdmi_bridge {
#define HDMI_TX_SCRAMBLER_MIN_TX_VERSION 0x04
#define HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ 340000
#define HDMI_TX_SCRAMBLER_TIMEOUT_MSEC 200
-/* default hsyncs for 4k@60 for 200ms */
-#define HDMI_DEFAULT_TIMEOUT_HSYNC 28571
+
/* for AVI program */
#define HDMI_AVI_INFOFRAME_BUFFER_SIZE \
@@ -177,39 +176,22 @@ static int _sde_hdmi_bridge_scrambler_ddc_check_status(struct hdmi *hdmi)
return rc;
}
-static void _sde_hdmi_bridge_scrambler_ddc_reset(struct hdmi *hdmi)
-{
- u32 reg_val;
-
- /* clear ack and disable interrupts */
- reg_val = BIT(14) | BIT(9) | BIT(5) | BIT(1);
- hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL2, reg_val);
-
- /* Reset DDC timers */
- reg_val = BIT(0) | hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL);
- hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
-
- reg_val = hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL);
- reg_val &= ~BIT(0);
- hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
-}
-
-static void _sde_hdmi_bridge_scrambler_ddc_disable(struct hdmi *hdmi)
-{
- u32 reg_val;
-
- _sde_hdmi_bridge_scrambler_ddc_reset(hdmi);
- /* Disable HW DDC access to RxStatus register */
- reg_val = hdmi_read(hdmi, REG_HDMI_HW_DDC_CTRL);
- reg_val &= ~(BIT(8) | BIT(9));
- hdmi_write(hdmi, REG_HDMI_HW_DDC_CTRL, reg_val);
-}
-
static int _sde_hdmi_bridge_scrambler_status_timer_setup(struct hdmi *hdmi,
u32 timeout_hsync)
{
u32 reg_val;
int rc;
+ struct sde_connector *c_conn;
+ struct drm_connector *connector = NULL;
+ struct sde_hdmi *display;
+
+ if (!hdmi) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+ connector = hdmi->connector;
+ c_conn = to_sde_connector(hdmi->connector);
+ display = (struct sde_hdmi *)c_conn->display;
_sde_hdmi_bridge_ddc_clear_irq(hdmi, "scrambler");
@@ -243,7 +225,7 @@ static int _sde_hdmi_bridge_scrambler_status_timer_setup(struct hdmi *hdmi,
if (rc)
SDE_ERROR("scrambling ddc error %d\n", rc);
- _sde_hdmi_bridge_scrambler_ddc_disable(hdmi);
+ _sde_hdmi_scrambler_ddc_disable((void *)display);
return rc;
}
@@ -269,20 +251,6 @@ static int _sde_hdmi_bridge_setup_ddc_timers(struct hdmi *hdmi,
return 0;
}
-static inline int _sde_hdmi_bridge_get_timeout_in_hysnc(
- struct drm_display_mode *mode, u32 timeout_ms)
-{
- /*
- * pixel clock = h_total * v_total * fps
- * 1 sec = pixel clock number of pixels are transmitted.
- * time taken by one line (h_total) = 1s / (v_total * fps).
- * lines for give time = (time_ms * 1000) / (1000000 / (v_total * fps))
- * = (time_ms * clock) / h_total
- */
-
- return (timeout_ms * mode->clock / mode->htotal);
-}
-
static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
struct drm_display_mode *mode)
{
@@ -291,14 +259,17 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
u32 reg_val = 0;
u32 tmds_clock_ratio = 0;
bool scrambler_on = false;
-
+ struct sde_connector *c_conn;
struct drm_connector *connector = NULL;
+ struct sde_hdmi *display;
if (!hdmi || !mode) {
SDE_ERROR("invalid input\n");
return -EINVAL;
}
connector = hdmi->connector;
+ c_conn = to_sde_connector(hdmi->connector);
+ display = (struct sde_hdmi *)c_conn->display;
/* Read HDMI version */
reg_val = hdmi_read(hdmi, REG_HDMI_VERSION);
@@ -344,9 +315,10 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi,
* status bit on the sink. Sink should set this bit
* with in 200ms after scrambler is enabled.
*/
- timeout_hsync = _sde_hdmi_bridge_get_timeout_in_hysnc(
- mode,
+ timeout_hsync = _sde_hdmi_get_timeout_in_hysnc(
+ (void *)display,
HDMI_TX_SCRAMBLER_TIMEOUT_MSEC);
+
if (timeout_hsync <= 0) {
SDE_ERROR("err in timeout hsync calc\n");
timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC;
@@ -408,13 +380,23 @@ static void sde_hdmi_update_hdcp_info(struct drm_connector *connector)
return;
}
+ /* check first if hdcp2p2 is supported */
+ fd = display->hdcp_feat_data[SDE_HDCP_2P2];
+ if (fd)
+ ops = sde_hdmi_hdcp2p2_start(fd);
+
+ if (ops && ops->feature_supported)
+ display->hdcp22_present = ops->feature_supported(fd);
+ else
+ display->hdcp22_present = false;
+
if (!display->hdcp22_present) {
if (display->hdcp1_use_sw_keys) {
display->hdcp14_present =
hdcp1_check_if_supported_load_app();
}
if (display->hdcp14_present) {
- fd = display->hdcp_feature_data[SDE_HDCP_1x];
+ fd = display->hdcp_feat_data[SDE_HDCP_1x];
if (fd)
ops = sde_hdcp_1x_start(fd);
}
@@ -666,9 +648,9 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
_sde_hdmi_bridge_set_spd_infoframe(hdmi, mode);
DRM_DEBUG("hdmi setup info frame\n");
}
- _sde_hdmi_bridge_setup_scrambler(hdmi, mode);
_sde_hdmi_save_mode(hdmi, mode);
+ _sde_hdmi_bridge_setup_scrambler(hdmi, mode);
}
static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = {
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c
new file mode 100644
index 000000000000..1e673440f399
--- /dev/null
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c
@@ -0,0 +1,994 @@
+/* Copyright (c) 2017, 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/types.h>
+#include <linux/kthread.h>
+
+#include <linux/hdcp_qseecom.h>
+#include "sde_hdcp.h"
+#include "video/msm_hdmi_hdcp_mgr.h"
+#include "sde_hdmi_util.h"
+
+/*
+ * Defined addresses and offsets of standard HDCP 2.2 sink registers
+ * for DDC, as defined in HDCP 2.2 spec section 2.14 table 2.7
+ */
+#define HDCP_SINK_DDC_SLAVE_ADDR 0x74 /* Sink DDC slave address */
+#define HDCP_SINK_DDC_HDCP2_VERSION 0x50 /* Does sink support HDCP2.2 */
+#define HDCP_SINK_DDC_HDCP2_WRITE_MESSAGE 0x60 /* HDCP Tx writes here */
+#define HDCP_SINK_DDC_HDCP2_RXSTATUS 0x70 /* RxStatus, 2 bytes */
+#define HDCP_SINK_DDC_HDCP2_READ_MESSAGE 0x80 /* HDCP Tx reads here */
+
+#define HDCP2P2_DEFAULT_TIMEOUT 500
+
+/*
+ * HDCP 2.2 encryption requires the data encryption block that is present in
+ * HDMI controller version 4.0.0 and above
+ */
+#define MIN_HDMI_TX_MAJOR_VERSION 4
+
+enum sde_hdmi_hdcp2p2_sink_status {
+ SINK_DISCONNECTED,
+ SINK_CONNECTED
+};
+
+enum sde_hdmi_auth_status {
+ HDMI_HDCP_AUTH_STATUS_FAILURE,
+ HDMI_HDCP_AUTH_STATUS_SUCCESS
+};
+
+struct sde_hdmi_hdcp2p2_ctrl {
+ atomic_t auth_state;
+ enum sde_hdmi_hdcp2p2_sink_status sink_status; /* Is sink connected */
+ struct sde_hdcp_init_data init_data; /* Feature data from HDMI drv */
+ struct mutex mutex; /* mutex to protect access to ctrl */
+ struct mutex msg_lock; /* mutex to protect access to msg buffer */
+ struct mutex wakeup_mutex; /* mutex to protect access to wakeup call*/
+ struct sde_hdcp_ops *ops;
+ void *lib_ctx; /* Handle to HDCP 2.2 Trustzone library */
+ struct hdcp_txmtr_ops *lib; /* Ops for driver to call into TZ */
+
+ enum hdmi_hdcp_wakeup_cmd wakeup_cmd;
+ enum sde_hdmi_auth_status auth_status;
+ char *send_msg_buf;
+ uint32_t send_msg_len;
+ uint32_t timeout;
+ uint32_t timeout_left;
+
+ struct task_struct *thread;
+ struct kthread_worker worker;
+ struct kthread_work status;
+ struct kthread_work auth;
+ struct kthread_work send_msg;
+ struct kthread_work recv_msg;
+ struct kthread_work link;
+ struct kthread_work poll;
+};
+
+static int sde_hdmi_hdcp2p2_auth(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+static void sde_hdmi_hdcp2p2_send_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+static void sde_hdmi_hdcp2p2_recv_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+static void sde_hdmi_hdcp2p2_auth_status(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+static int sde_hdmi_hdcp2p2_link_check(struct sde_hdmi_hdcp2p2_ctrl *ctrl);
+
+static bool sde_hdcp2p2_is_valid_state(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_AUTHENTICATE)
+ return true;
+
+ if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE)
+ return true;
+
+ return false;
+}
+
+static int sde_hdmi_hdcp2p2_copy_buf(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ struct hdmi_hdcp_wakeup_data *data)
+{
+ mutex_lock(&ctrl->msg_lock);
+
+ if (!data->send_msg_len) {
+ mutex_unlock(&ctrl->msg_lock);
+ return 0;
+ }
+
+ ctrl->send_msg_len = data->send_msg_len;
+
+ kzfree(ctrl->send_msg_buf);
+
+ ctrl->send_msg_buf = kzalloc(data->send_msg_len, GFP_KERNEL);
+
+ if (!ctrl->send_msg_buf) {
+ mutex_unlock(&ctrl->msg_lock);
+ return -ENOMEM;
+ }
+
+ memcpy(ctrl->send_msg_buf, data->send_msg_buf, ctrl->send_msg_len);
+
+ mutex_unlock(&ctrl->msg_lock);
+
+ return 0;
+}
+
+static int sde_hdmi_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+
+ if (!data) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ ctrl = data->context;
+ if (!ctrl) {
+ SDE_ERROR("invalid ctrl\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&ctrl->wakeup_mutex);
+
+ SDE_HDCP_DEBUG("cmd: %s, timeout %dms\n",
+ hdmi_hdcp_cmd_to_str(data->cmd),
+ data->timeout);
+
+ ctrl->wakeup_cmd = data->cmd;
+
+ if (data->timeout)
+ ctrl->timeout = data->timeout * 2;
+ else
+ ctrl->timeout = HDCP2P2_DEFAULT_TIMEOUT;
+
+ if (!sde_hdcp2p2_is_valid_state(ctrl)) {
+ SDE_ERROR("invalid state\n");
+ goto exit;
+ }
+
+ if (sde_hdmi_hdcp2p2_copy_buf(ctrl, data))
+ goto exit;
+
+ if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS)
+ ctrl->auth_status = HDMI_HDCP_AUTH_STATUS_SUCCESS;
+ else if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_FAILED)
+ ctrl->auth_status = HDMI_HDCP_AUTH_STATUS_FAILURE;
+
+ switch (ctrl->wakeup_cmd) {
+ case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE:
+ queue_kthread_work(&ctrl->worker, &ctrl->send_msg);
+ break;
+ case HDMI_HDCP_WKUP_CMD_RECV_MESSAGE:
+ queue_kthread_work(&ctrl->worker, &ctrl->recv_msg);
+ break;
+ case HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS:
+ case HDMI_HDCP_WKUP_CMD_STATUS_FAILED:
+ queue_kthread_work(&ctrl->worker, &ctrl->status);
+ break;
+ case HDMI_HDCP_WKUP_CMD_LINK_POLL:
+ queue_kthread_work(&ctrl->worker, &ctrl->poll);
+ break;
+ case HDMI_HDCP_WKUP_CMD_AUTHENTICATE:
+ queue_kthread_work(&ctrl->worker, &ctrl->auth);
+ break;
+ default:
+ SDE_ERROR("invalid wakeup command %d\n", ctrl->wakeup_cmd);
+ }
+exit:
+ mutex_unlock(&ctrl->wakeup_mutex);
+ return 0;
+}
+
+static int sde_hdmi_hdcp2p2_wakeup_lib(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ struct hdcp_lib_wakeup_data *data)
+{
+ int rc = 0;
+
+ if (ctrl && ctrl->lib && ctrl->lib->wakeup &&
+ data && (data->cmd != HDCP_LIB_WKUP_CMD_INVALID)) {
+ rc = ctrl->lib->wakeup(data);
+ if (rc)
+ SDE_ERROR("error sending %s to lib\n",
+ hdcp_lib_cmd_to_str(data->cmd));
+ }
+
+ return rc;
+}
+
+static void sde_hdmi_hdcp2p2_reset(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ ctrl->sink_status = SINK_DISCONNECTED;
+ atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE);
+}
+
+static void sde_hdmi_hdcp2p2_off(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+ struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_AUTHENTICATE};
+
+ ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ sde_hdmi_hdcp2p2_reset(ctrl);
+
+ flush_kthread_worker(&ctrl->worker);
+
+ sde_hdmi_hdcp2p2_ddc_disable((void *)ctrl->init_data.cb_data);
+
+ cdata.context = input;
+ sde_hdmi_hdcp2p2_wakeup(&cdata);
+}
+
+static int sde_hdmi_hdcp2p2_authenticate(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = input;
+ struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_AUTHENTICATE};
+ u32 regval;
+ int rc = 0;
+
+ /* Enable authentication success interrupt */
+ regval = DSS_REG_R(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2);
+ regval |= BIT(1) | BIT(2);
+
+ DSS_REG_W(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2, regval);
+
+ flush_kthread_worker(&ctrl->worker);
+
+ ctrl->sink_status = SINK_CONNECTED;
+ atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATING);
+
+ /* make sure ddc is idle before starting hdcp 2.2 authentication */
+ _sde_hdmi_scrambler_ddc_disable((void *)ctrl->init_data.cb_data);
+ sde_hdmi_hdcp2p2_ddc_disable((void *)ctrl->init_data.cb_data);
+
+ cdata.context = input;
+ sde_hdmi_hdcp2p2_wakeup(&cdata);
+
+ return rc;
+}
+
+static int sde_hdmi_hdcp2p2_reauthenticate(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+
+ ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ sde_hdmi_hdcp2p2_reset(ctrl);
+
+ return sde_hdmi_hdcp2p2_authenticate(input);
+}
+
+static void sde_hdmi_hdcp2p2_min_level_change(void *client_ctx,
+int min_enc_lvl)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl =
+ (struct sde_hdmi_hdcp2p2_ctrl *)client_ctx;
+ struct hdcp_lib_wakeup_data cdata = {
+ HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE};
+ bool enc_notify = true;
+ enum sde_hdcp_states enc_lvl;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ switch (min_enc_lvl) {
+ case 0:
+ enc_lvl = HDCP_STATE_AUTH_ENC_NONE;
+ break;
+ case 1:
+ enc_lvl = HDCP_STATE_AUTH_ENC_1X;
+ break;
+ case 2:
+ enc_lvl = HDCP_STATE_AUTH_ENC_2P2;
+ break;
+ default:
+ enc_notify = false;
+ }
+
+ SDE_HDCP_DEBUG("enc level changed %d\n", min_enc_lvl);
+
+ cdata.context = ctrl->lib_ctx;
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+
+ if (enc_notify && ctrl->init_data.notify_status)
+ ctrl->init_data.notify_status(ctrl->init_data.cb_data, enc_lvl);
+}
+
+static void sde_hdmi_hdcp2p2_auth_failed(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL);
+
+ sde_hdmi_hdcp2p2_ddc_disable(ctrl->init_data.cb_data);
+
+ /* notify hdmi tx about HDCP failure */
+ ctrl->init_data.notify_status(ctrl->init_data.cb_data,
+ HDCP_STATE_AUTH_FAIL);
+}
+
+static int sde_hdmi_hdcp2p2_ddc_rd_message(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ u8 *buf, int size, u32 timeout)
+{
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+
+ int rc;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid ctrl\n");
+ return -EINVAL;
+ }
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid ddc data\n");
+ return -EINVAL;
+ }
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+ SDE_ERROR("hdcp is off\n");
+ return -EINVAL;
+ }
+
+ memset(ddc_data, 0, sizeof(*ddc_data));
+ ddc_data->dev_addr = HDCP_SINK_DDC_SLAVE_ADDR;
+ ddc_data->offset = HDCP_SINK_DDC_HDCP2_READ_MESSAGE;
+ ddc_data->data_buf = buf;
+ ddc_data->data_len = size;
+ ddc_data->request_len = size;
+ ddc_data->retry = 0;
+ ddc_data->hard_timeout = timeout;
+ ddc_data->what = "HDCP2ReadMessage";
+
+ rc = sde_hdmi_ddc_read(ctrl->init_data.cb_data);
+ if (rc)
+ SDE_ERROR("Cannot read HDCP message register\n");
+
+ ctrl->timeout_left = ddc_data->timeout_left;
+
+ return rc;
+}
+
+static int sde_hdmi_hdcp2p2_ddc_wt_message(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ u8 *buf, size_t size)
+{
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+
+ int rc;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid ctrl\n");
+ return -EINVAL;
+ }
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid ddc data\n");
+ return -EINVAL;
+ }
+
+ memset(ddc_data, 0, sizeof(*ddc_data));
+ ddc_data->dev_addr = HDCP_SINK_DDC_SLAVE_ADDR;
+ ddc_data->offset = HDCP_SINK_DDC_HDCP2_WRITE_MESSAGE;
+ ddc_data->data_buf = buf;
+ ddc_data->data_len = size;
+ ddc_data->hard_timeout = ctrl->timeout;
+ ddc_data->what = "HDCP2WriteMessage";
+
+ rc = sde_hdmi_ddc_write((void *)ctrl->init_data.cb_data);
+ if (rc)
+ SDE_ERROR("Cannot write HDCP message register\n");
+
+ ctrl->timeout_left = ddc_data->timeout_left;
+
+ return rc;
+}
+
+static int sde_hdmi_hdcp2p2_read_version(struct sde_hdmi_hdcp2p2_ctrl *ctrl,
+ u8 *hdcp2version)
+{
+ struct sde_hdmi_tx_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ int rc;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid ctrl\n");
+ return -EINVAL;
+ }
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ ddc_data = &ddc_ctrl->ddc_data;
+
+ if (!ddc_data) {
+ SDE_ERROR("invalid ddc data\n");
+ return -EINVAL;
+ }
+ memset(ddc_data, 0, sizeof(*ddc_data));
+ ddc_data->dev_addr = HDCP_SINK_DDC_SLAVE_ADDR;
+ ddc_data->offset = HDCP_SINK_DDC_HDCP2_VERSION;
+ ddc_data->data_buf = hdcp2version;
+ ddc_data->data_len = 1;
+ ddc_data->request_len = 1;
+ ddc_data->retry = 1;
+ ddc_data->what = "HDCP2Version";
+
+ rc = sde_hdmi_ddc_read((void *)ctrl->init_data.cb_data);
+ if (rc) {
+ SDE_ERROR("Cannot read HDCP2Version register");
+ return rc;
+ }
+
+ SDE_HDCP_DEBUG("Read HDCP2Version as %u\n", *hdcp2version);
+ return rc;
+}
+
+static bool sde_hdmi_hdcp2p2_feature_supported(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = input;
+ struct hdcp_txmtr_ops *lib = NULL;
+ bool supported = false;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ goto end;
+ }
+
+ lib = ctrl->lib;
+ if (!lib) {
+ SDE_ERROR("invalid lib ops data\n");
+ goto end;
+ }
+
+ if (lib->feature_supported) {
+ supported = lib->feature_supported(
+ ctrl->lib_ctx);
+ }
+
+end:
+ return supported;
+}
+
+static void sde_hdmi_hdcp2p2_send_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ int rc = 0;
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+ uint32_t msglen;
+ char *msg = NULL;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ cdata.context = ctrl->lib_ctx;
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+ SDE_ERROR("hdcp is off\n");
+ goto exit;
+ }
+
+ mutex_lock(&ctrl->msg_lock);
+ msglen = ctrl->send_msg_len;
+
+ if (!msglen) {
+ mutex_unlock(&ctrl->msg_lock);
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ msg = kzalloc(msglen, GFP_KERNEL);
+ if (!msg) {
+ mutex_unlock(&ctrl->msg_lock);
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ memcpy(msg, ctrl->send_msg_buf, msglen);
+ mutex_unlock(&ctrl->msg_lock);
+
+ /* Forward the message to the sink */
+ rc = sde_hdmi_hdcp2p2_ddc_wt_message(ctrl,
+ msg, (size_t)msglen);
+ if (rc) {
+ SDE_ERROR("Error sending msg to sink %d\n", rc);
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED;
+ } else {
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS;
+ cdata.timeout = ctrl->timeout_left;
+ }
+exit:
+ kfree(msg);
+
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+}
+
+static void sde_hdmi_hdcp2p2_send_msg_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, send_msg);
+
+ sde_hdmi_hdcp2p2_send_msg(ctrl);
+}
+
+static void sde_hdmi_hdcp2p2_link_cb(void *data)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = data;
+
+ if (!ctrl) {
+ SDE_HDCP_DEBUG("invalid input\n");
+ return;
+ }
+
+ if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE)
+ queue_kthread_work(&ctrl->worker, &ctrl->link);
+}
+
+static void sde_hdmi_hdcp2p2_recv_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ int timeout_hsync = 0, rc = 0;
+ char *recvd_msg_buf = NULL;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ cdata.context = ctrl->lib_ctx;
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+ SDE_ERROR("hdcp is off\n");
+ goto exit;
+ }
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ if (!ddc_ctrl) {
+ pr_err("invalid ddc ctrl\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ ddc_data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+ memset(ddc_data, 0, sizeof(*ddc_data));
+
+ timeout_hsync = _sde_hdmi_get_timeout_in_hysnc(
+ (void *)ctrl->init_data.cb_data, ctrl->timeout);
+
+ if (timeout_hsync <= 0) {
+ SDE_ERROR("err in timeout hsync calc\n");
+ timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC;
+ }
+
+ SDE_HDCP_DEBUG("timeout for rxstatus %dms, %d hsync\n",
+ ctrl->timeout, timeout_hsync);
+
+ ddc_data->intr_mask = RXSTATUS_MESSAGE_SIZE | RXSTATUS_REAUTH_REQ;
+ ddc_data->timeout_ms = ctrl->timeout;
+ ddc_data->timeout_hsync = timeout_hsync;
+ ddc_data->periodic_timer_hsync = timeout_hsync / 20;
+ ddc_data->read_method = HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER;
+ ddc_data->wait = true;
+
+ rc = sde_hdmi_hdcp2p2_read_rxstatus(ctrl->init_data.cb_data);
+ if (rc) {
+ SDE_ERROR("error reading rxstatus %d\n", rc);
+ goto exit;
+ }
+
+ if (ddc_data->reauth_req) {
+ ddc_data->reauth_req = false;
+
+ SDE_HDCP_DEBUG("reauth triggered by sink\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ ctrl->timeout_left = ddc_data->timeout_left;
+
+ SDE_HDCP_DEBUG("timeout left after rxstatus %dms, msg size %d\n",
+ ctrl->timeout_left, ddc_data->message_size);
+
+ if (!ddc_data->message_size) {
+ SDE_ERROR("recvd invalid message size\n");
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ recvd_msg_buf = kzalloc(ddc_data->message_size, GFP_KERNEL);
+ if (!recvd_msg_buf) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ rc = sde_hdmi_hdcp2p2_ddc_rd_message(ctrl, recvd_msg_buf,
+ ddc_data->message_size, ctrl->timeout_left);
+ if (rc) {
+ SDE_ERROR("error reading message %d\n", rc);
+ goto exit;
+ }
+
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS;
+ cdata.recvd_msg_buf = recvd_msg_buf;
+ cdata.recvd_msg_len = ddc_data->message_size;
+ cdata.timeout = ctrl->timeout_left;
+exit:
+ if (rc == -ETIMEDOUT)
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT;
+ else if (rc)
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED;
+
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+ kfree(recvd_msg_buf);
+}
+
+static void sde_hdmi_hdcp2p2_recv_msg_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, recv_msg);
+
+ sde_hdmi_hdcp2p2_recv_msg(ctrl);
+}
+
+static int sde_hdmi_hdcp2p2_link_check(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *ddc_data;
+ int timeout_hsync;
+ int ret;
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+
+ if (!ddc_ctrl)
+ return -EINVAL;
+
+ sde_hdmi_ddc_config(ctrl->init_data.cb_data);
+
+ ddc_data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+
+ memset(ddc_data, 0, sizeof(*ddc_data));
+
+ timeout_hsync = _sde_hdmi_get_timeout_in_hysnc(
+ (void *)ctrl->init_data.cb_data,
+ jiffies_to_msecs(HZ / 2));
+
+ if (timeout_hsync <= 0) {
+ SDE_ERROR("err in timeout hsync calc\n");
+ timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC;
+ }
+ SDE_HDCP_DEBUG("timeout for rxstatus %d hsyncs\n", timeout_hsync);
+
+ ddc_data->intr_mask = RXSTATUS_READY | RXSTATUS_MESSAGE_SIZE |
+ RXSTATUS_REAUTH_REQ;
+ ddc_data->timeout_hsync = timeout_hsync;
+ ddc_data->periodic_timer_hsync = timeout_hsync;
+ ddc_data->read_method = HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER;
+ ddc_data->link_cb = sde_hdmi_hdcp2p2_link_cb;
+ ddc_data->link_data = ctrl;
+
+ ret = sde_hdmi_hdcp2p2_read_rxstatus((void *)ctrl->init_data.cb_data);
+ return ret;
+}
+
+static void sde_hdmi_hdcp2p2_poll_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, poll);
+
+ sde_hdmi_hdcp2p2_link_check(ctrl);
+}
+
+static void sde_hdmi_hdcp2p2_auth_status(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
+ SDE_ERROR("hdcp is off\n");
+ return;
+ }
+
+ if (ctrl->auth_status == HDMI_HDCP_AUTH_STATUS_SUCCESS) {
+ ctrl->init_data.notify_status(ctrl->init_data.cb_data,
+ HDCP_STATE_AUTHENTICATED);
+
+ atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATED);
+ } else {
+ sde_hdmi_hdcp2p2_auth_failed(ctrl);
+ }
+}
+
+static void sde_hdmi_hdcp2p2_auth_status_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, status);
+
+ sde_hdmi_hdcp2p2_auth_status(ctrl);
+}
+
+static void sde_hdmi_hdcp2p2_link_work(struct kthread_work *work)
+{
+ int rc = 0;
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, link);
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+ char *recvd_msg_buf = NULL;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *ddc_data;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ cdata.context = ctrl->lib_ctx;
+
+ ddc_ctrl = ctrl->init_data.ddc_ctrl;
+ if (!ddc_ctrl) {
+ rc = -EINVAL;
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ goto exit;
+ }
+
+ ddc_data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+
+ if (ddc_data->reauth_req) {
+ SDE_HDCP_DEBUG("reauth triggered by sink\n");
+
+ ddc_data->reauth_req = false;
+ rc = -ENOLINK;
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ goto exit;
+ }
+
+ if (ddc_data->ready && ddc_data->message_size) {
+ SDE_HDCP_DEBUG("topology changed. rxstatus msg size %d\n",
+ ddc_data->message_size);
+
+ ddc_data->ready = false;
+
+ recvd_msg_buf = kzalloc(ddc_data->message_size, GFP_KERNEL);
+ if (!recvd_msg_buf) {
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ goto exit;
+ }
+
+ rc = sde_hdmi_hdcp2p2_ddc_rd_message(ctrl, recvd_msg_buf,
+ ddc_data->message_size, HDCP2P2_DEFAULT_TIMEOUT);
+ if (rc) {
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ SDE_ERROR("error reading message %d\n", rc);
+ } else {
+ cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS;
+ cdata.recvd_msg_buf = recvd_msg_buf;
+ cdata.recvd_msg_len = ddc_data->message_size;
+ }
+
+ ddc_data->message_size = 0;
+ }
+exit:
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+ kfree(recvd_msg_buf);
+
+ if (rc) {
+ sde_hdmi_hdcp2p2_auth_failed(ctrl);
+ return;
+ }
+}
+
+static int sde_hdmi_hdcp2p2_auth(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+ int rc = 0;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ cdata.context = ctrl->lib_ctx;
+
+ if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING)
+ cdata.cmd = HDCP_LIB_WKUP_CMD_START;
+ else
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+
+ rc = sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+ if (rc)
+ sde_hdmi_hdcp2p2_auth_failed(ctrl);
+
+ return rc;
+}
+
+static void sde_hdmi_hdcp2p2_auth_work(struct kthread_work *work)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work,
+ struct sde_hdmi_hdcp2p2_ctrl, auth);
+
+ sde_hdmi_hdcp2p2_auth(ctrl);
+}
+
+void sde_hdmi_hdcp2p2_deinit(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+ struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
+
+ ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input;
+
+ if (!ctrl) {
+ SDE_ERROR("invalid input\n");
+ return;
+ }
+
+ cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
+ cdata.context = ctrl->lib_ctx;
+ sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata);
+
+ kthread_stop(ctrl->thread);
+
+ mutex_destroy(&ctrl->mutex);
+ mutex_destroy(&ctrl->msg_lock);
+ mutex_destroy(&ctrl->wakeup_mutex);
+ kfree(ctrl);
+}
+
+void *sde_hdmi_hdcp2p2_init(struct sde_hdcp_init_data *init_data)
+{
+ int rc;
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+ static struct sde_hdcp_ops ops = {
+ .reauthenticate = sde_hdmi_hdcp2p2_reauthenticate,
+ .authenticate = sde_hdmi_hdcp2p2_authenticate,
+ .feature_supported = sde_hdmi_hdcp2p2_feature_supported,
+ .off = sde_hdmi_hdcp2p2_off
+ };
+
+ static struct hdcp_client_ops client_ops = {
+ .wakeup = sde_hdmi_hdcp2p2_wakeup,
+ .notify_lvl_change = sde_hdmi_hdcp2p2_min_level_change,
+ };
+
+ static struct hdcp_txmtr_ops txmtr_ops;
+ struct hdcp_register_data register_data;
+
+ SDE_HDCP_DEBUG("HDCP2P2 feature initialization\n");
+
+ if (!init_data || !init_data->core_io || !init_data->mutex ||
+ !init_data->ddc_ctrl || !init_data->notify_status ||
+ !init_data->workq || !init_data->cb_data) {
+ SDE_ERROR("invalid input\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (init_data->hdmi_tx_ver < MIN_HDMI_TX_MAJOR_VERSION) {
+ SDE_ERROR("HDMI Tx does not support HDCP 2.2\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return ERR_PTR(-ENOMEM);
+
+ ctrl->init_data = *init_data;
+ ctrl->lib = &txmtr_ops;
+
+ ctrl->sink_status = SINK_DISCONNECTED;
+
+ atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE);
+
+ ctrl->ops = &ops;
+ mutex_init(&ctrl->mutex);
+ mutex_init(&ctrl->msg_lock);
+ mutex_init(&ctrl->wakeup_mutex);
+
+ register_data.hdcp_ctx = &ctrl->lib_ctx;
+ register_data.client_ops = &client_ops;
+ register_data.txmtr_ops = &txmtr_ops;
+ register_data.device_type = HDCP_TXMTR_HDMI;
+ register_data.client_ctx = ctrl;
+
+ rc = hdcp_library_register(&register_data);
+ if (rc) {
+ SDE_ERROR("Unable to register with HDCP 2.2 library\n");
+ goto error;
+ }
+
+ init_kthread_worker(&ctrl->worker);
+
+ init_kthread_work(&ctrl->auth, sde_hdmi_hdcp2p2_auth_work);
+ init_kthread_work(&ctrl->send_msg, sde_hdmi_hdcp2p2_send_msg_work);
+ init_kthread_work(&ctrl->recv_msg, sde_hdmi_hdcp2p2_recv_msg_work);
+ init_kthread_work(&ctrl->status, sde_hdmi_hdcp2p2_auth_status_work);
+ init_kthread_work(&ctrl->link, sde_hdmi_hdcp2p2_link_work);
+ init_kthread_work(&ctrl->poll, sde_hdmi_hdcp2p2_poll_work);
+
+ ctrl->thread = kthread_run(kthread_worker_fn,
+ &ctrl->worker, "hdmi_hdcp2p2");
+
+ if (IS_ERR(ctrl->thread)) {
+ SDE_ERROR("unable to start hdcp2p2 thread\n");
+ rc = PTR_ERR(ctrl->thread);
+ ctrl->thread = NULL;
+ goto error;
+ }
+
+ return ctrl;
+error:
+ kfree(ctrl);
+ return ERR_PTR(rc);
+}
+
+static bool sde_hdmi_hdcp2p2_supported(struct sde_hdmi_hdcp2p2_ctrl *ctrl)
+{
+ u8 hdcp2version = 0;
+ int rc = sde_hdmi_hdcp2p2_read_version(ctrl, &hdcp2version);
+
+ if (rc)
+ goto error;
+
+ if (hdcp2version & BIT(2)) {
+ SDE_HDCP_DEBUG("Sink is HDCP 2.2 capable\n");
+ return true;
+ }
+
+error:
+ SDE_HDCP_DEBUG("Sink is not HDCP 2.2 capable\n");
+ return false;
+}
+
+struct sde_hdcp_ops *sde_hdmi_hdcp2p2_start(void *input)
+{
+ struct sde_hdmi_hdcp2p2_ctrl *ctrl;
+
+ ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input;
+
+ SDE_HDCP_DEBUG("Checking sink capability\n");
+ if (sde_hdmi_hdcp2p2_supported(ctrl))
+ return ctrl->ops;
+ else
+ return NULL;
+
+}
+
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c
index 10fc89545440..35ee5ae56d6a 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c
@@ -26,6 +26,237 @@
#include "sde_hdmi_regs.h"
#include "hdmi.h"
+#define HDMI_SEC_TO_MS 1000
+#define HDMI_MS_TO_US 1000
+#define HDMI_SEC_TO_US (HDMI_SEC_TO_MS * HDMI_MS_TO_US)
+#define HDMI_KHZ_TO_HZ 1000
+#define HDMI_BUSY_WAIT_DELAY_US 100
+
+static void sde_hdmi_hdcp2p2_ddc_clear_status(struct sde_hdmi *display)
+{
+ u32 reg_val;
+ struct hdmi *hdmi;
+
+ if (!display) {
+ pr_err("invalid ddc ctrl\n");
+ return;
+ }
+ hdmi = display->ctrl.ctrl;
+ /* check for errors and clear status */
+ reg_val = hdmi_read(hdmi, HDMI_HDCP2P2_DDC_STATUS);
+
+ if (reg_val & BIT(4)) {
+ pr_debug("ddc aborted\n");
+ reg_val |= BIT(5);
+ }
+
+ if (reg_val & BIT(8)) {
+ pr_debug("timed out\n");
+ reg_val |= BIT(9);
+ }
+
+ if (reg_val & BIT(12)) {
+ pr_debug("NACK0\n");
+ reg_val |= BIT(13);
+ }
+
+ if (reg_val & BIT(14)) {
+ pr_debug("NACK1\n");
+ reg_val |= BIT(15);
+ }
+
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_STATUS, reg_val);
+}
+
+int sde_hdmi_ddc_hdcp2p2_isr(void *hdmi_display)
+{
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *data;
+ u32 intr0, intr2, intr5;
+ u32 msg_size;
+ int rc = 0;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct hdmi *hdmi;
+
+ ddc_ctrl = &display->ddc_ctrl;
+ data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+ hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ intr0 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL0);
+ intr2 = hdmi_read(hdmi, HDMI_HDCP_INT_CTRL2);
+ intr5 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL5);
+
+ pr_debug("intr0: 0x%x, intr2: 0x%x, intr5: 0x%x\n",
+ intr0, intr2, intr5);
+
+ /* check if encryption is enabled */
+ if (intr2 & BIT(0)) {
+ /*
+ * ack encryption ready interrupt.
+ * disable encryption ready interrupt.
+ * enable encryption not ready interrupt.
+ */
+ intr2 &= ~BIT(2);
+ intr2 |= BIT(1) | BIT(6);
+
+ pr_info("HDCP 2.2 Encryption enabled\n");
+ data->encryption_ready = true;
+ }
+
+ /* check if encryption is disabled */
+ if (intr2 & BIT(4)) {
+ /*
+ * ack encryption not ready interrupt.
+ * disable encryption not ready interrupt.
+ * enable encryption ready interrupt.
+ */
+ intr2 &= ~BIT(6);
+ intr2 |= BIT(5) | BIT(2);
+
+ pr_info("HDCP 2.2 Encryption disabled\n");
+ data->encryption_ready = false;
+ }
+
+ hdmi_write(hdmi, HDMI_HDCP_INT_CTRL2, intr2);
+
+ /* get the message size bits 29:20 */
+ msg_size = (intr0 & (0x3FF << 20)) >> 20;
+
+ if (msg_size) {
+ /* ack and disable message size interrupt */
+ intr0 |= BIT(30);
+ intr0 &= ~BIT(31);
+
+ data->message_size = msg_size;
+ }
+
+ /* check and disable ready interrupt */
+ if (intr0 & BIT(16)) {
+ /* ack ready/not ready interrupt */
+ intr0 |= BIT(17);
+ intr0 &= ~BIT(18);
+ pr_debug("got ready interrupt\n");
+ data->ready = true;
+ }
+
+ /* check for reauth req interrupt */
+ if (intr0 & BIT(12)) {
+ /* ack and disable reauth req interrupt */
+ intr0 |= BIT(13);
+ intr0 &= ~BIT(14);
+ pr_err("got reauth interrupt\n");
+ data->reauth_req = true;
+ }
+
+ /* check for ddc fail interrupt */
+ if (intr0 & BIT(8)) {
+ /* ack ddc fail interrupt */
+ intr0 |= BIT(9);
+ pr_err("got ddc fail interrupt\n");
+ data->ddc_max_retries_fail = true;
+ }
+
+ /* check for ddc done interrupt */
+ if (intr0 & BIT(4)) {
+ /* ack ddc done interrupt */
+ intr0 |= BIT(5);
+ pr_debug("got ddc done interrupt\n");
+ data->ddc_done = true;
+ }
+
+ /* check for ddc read req interrupt */
+ if (intr0 & BIT(0)) {
+ /* ack read req interrupt */
+ intr0 |= BIT(1);
+
+ data->ddc_read_req = true;
+ }
+
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL0, intr0);
+
+ if (intr5 & BIT(0)) {
+ pr_err("RXSTATUS_DDC_REQ_TIMEOUT\n");
+
+ /* ack and disable timeout interrupt */
+ intr5 |= BIT(1);
+ intr5 &= ~BIT(2);
+
+ data->ddc_timeout = true;
+ }
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL5, intr5);
+
+ if (data->message_size || data->ready || data->reauth_req) {
+ if (data->wait) {
+ complete(&ddc_ctrl->rx_status_done);
+ } else if (data->link_cb && data->link_data) {
+ data->link_cb(data->link_data);
+ } else {
+ pr_err("new msg/reauth not handled\n");
+ rc = -EINVAL;
+ }
+ }
+
+ sde_hdmi_hdcp2p2_ddc_clear_status(display);
+
+ return rc;
+}
+
+int sde_hdmi_ddc_scrambling_isr(void *hdmi_display)
+{
+
+ bool scrambler_timer_off = false;
+ u32 intr2, intr5;
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct hdmi *hdmi;
+
+
+ hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ intr2 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL2);
+ intr5 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL5);
+
+ pr_debug("intr2: 0x%x, intr5: 0x%x\n", intr2, intr5);
+
+ if (intr2 & BIT(12)) {
+ pr_err("SCRAMBLER_STATUS_NOT\n");
+
+ intr2 |= BIT(14);
+ scrambler_timer_off = true;
+ }
+
+ if (intr2 & BIT(8)) {
+ pr_err("SCRAMBLER_STATUS_DDC_FAILED\n");
+
+ intr2 |= BIT(9);
+
+ scrambler_timer_off = true;
+ }
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL2, intr2);
+
+ if (intr5 & BIT(8)) {
+ pr_err("SCRAMBLER_STATUS_DDC_REQ_TIMEOUT\n");
+ intr5 |= BIT(9);
+ intr5 &= ~BIT(10);
+ scrambler_timer_off = true;
+ }
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL5, intr5);
+
+ if (scrambler_timer_off)
+ _sde_hdmi_scrambler_ddc_disable((void *)display);
+
+ return 0;
+}
+
static int sde_hdmi_ddc_read_retry(struct sde_hdmi *display)
{
int status;
@@ -311,3 +542,233 @@ int sde_hdmi_config_avmute(struct hdmi *hdmi, bool set)
return 0;
}
+
+int _sde_hdmi_get_timeout_in_hysnc(void *hdmi_display, u32 timeout_ms)
+{
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct drm_display_mode mode = display->mode;
+ /*
+ * pixel clock = h_total * v_total * fps
+ * 1 sec = pixel clock number of pixels are transmitted.
+ * time taken by one line (h_total) = 1s / (v_total * fps).
+ * lines for give time = (time_ms * 1000) / (1000000 / (v_total * fps))
+ * = (time_ms * clock) / h_total
+ */
+
+ return (timeout_ms * mode.clock / mode.htotal);
+}
+
+static void sde_hdmi_hdcp2p2_ddc_reset(struct sde_hdmi *hdmi_ctrl)
+{
+ u32 reg_val;
+ struct hdmi *hdmi = hdmi_ctrl->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("Invalid parameters\n");
+ return;
+ }
+
+ /*
+ * Clear acks for DDC_REQ, DDC_DONE, DDC_FAILED, RXSTATUS_READY,
+ * RXSTATUS_MSG_SIZE
+ */
+ reg_val = BIT(30) | BIT(17) | BIT(13) | BIT(9) | BIT(5) | BIT(1);
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL0, reg_val);
+ /* Reset DDC timers */
+ reg_val = BIT(0) | hdmi_read(hdmi, HDMI_HDCP2P2_DDC_CTRL);
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_CTRL, reg_val);
+ reg_val = hdmi_read(hdmi, HDMI_HDCP2P2_DDC_CTRL);
+ reg_val &= ~BIT(0);
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_CTRL, reg_val);
+}
+
+void sde_hdmi_hdcp2p2_ddc_disable(void *hdmi_display)
+{
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ u32 reg_val;
+ struct hdmi *hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("Invalid parameters\n");
+ return;
+ }
+
+ sde_hdmi_hdcp2p2_ddc_reset(display);
+
+ /* Disable HW DDC access to RxStatus register */
+ reg_val = hdmi_read(hdmi, HDMI_HW_DDC_CTRL);
+ reg_val &= ~(BIT(1) | BIT(0));
+
+ hdmi_write(hdmi, HDMI_HW_DDC_CTRL, reg_val);
+}
+
+static void _sde_hdmi_scrambler_ddc_reset(struct hdmi *hdmi)
+{
+ u32 reg_val;
+
+ /* clear ack and disable interrupts */
+ reg_val = BIT(14) | BIT(9) | BIT(5) | BIT(1);
+ hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL2, reg_val);
+
+ /* Reset DDC timers */
+ reg_val = BIT(0) | hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL);
+ hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
+
+ reg_val = hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL);
+ reg_val &= ~BIT(0);
+ hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val);
+}
+
+void _sde_hdmi_scrambler_ddc_disable(void *hdmi_display)
+{
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ u32 reg_val;
+
+ struct hdmi *hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("Invalid parameters\n");
+ return;
+ }
+
+ _sde_hdmi_scrambler_ddc_reset(hdmi);
+ /* Disable HW DDC access to RxStatus register */
+ reg_val = hdmi_read(hdmi, REG_HDMI_HW_DDC_CTRL);
+ reg_val &= ~(BIT(8) | BIT(9));
+ hdmi_write(hdmi, REG_HDMI_HW_DDC_CTRL, reg_val);
+}
+
+void sde_hdmi_ddc_config(void *hdmi_display)
+{
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct hdmi *hdmi = display->ctrl.ctrl;
+
+ if (!hdmi) {
+ pr_err("Invalid parameters\n");
+ return;
+ }
+ hdmi_write(hdmi, REG_HDMI_DDC_SPEED,
+ HDMI_DDC_SPEED_THRESHOLD(2) |
+ HDMI_DDC_SPEED_PRESCALE(10));
+
+ hdmi_write(hdmi, REG_HDMI_DDC_SETUP,
+ HDMI_DDC_SETUP_TIMEOUT(0xff));
+
+ /* enable reference timer for 19us */
+ hdmi_write(hdmi, REG_HDMI_DDC_REF,
+ HDMI_DDC_REF_REFTIMER_ENABLE |
+ HDMI_DDC_REF_REFTIMER(19));
+}
+
+int sde_hdmi_hdcp2p2_read_rxstatus(void *hdmi_display)
+{
+ u32 reg_val;
+ u32 intr_en_mask;
+ u32 timeout;
+ u32 timer;
+ int rc = 0;
+ int busy_wait_us;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data *data;
+ struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display;
+ struct hdmi *hdmi = display->ctrl.ctrl;
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ u32 rem;
+
+ if (!hdmi) {
+ pr_err("Invalid ddc data\n");
+ return -EINVAL;
+ }
+
+ ddc_ctrl = &display->ddc_ctrl;
+ data = &ddc_ctrl->sde_hdcp2p2_ddc_data;
+ if (!data) {
+ pr_err("Invalid ddc data\n");
+ return -EINVAL;
+ }
+
+ rc = ddc_clear_irq(hdmi);
+ if (rc) {
+ pr_err("DDC clear irq failed\n");
+ return rc;
+ }
+ intr_en_mask = data->intr_mask;
+ intr_en_mask |= BIT(HDCP2P2_RXSTATUS_DDC_FAILED_INTR_MASK);
+
+ /* Disable short read for now, sinks don't support it */
+ reg_val = hdmi_read(hdmi, HDMI_HDCP2P2_DDC_CTRL);
+ reg_val |= BIT(4);
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_CTRL, reg_val);
+ /*
+ * Setup the DDC timers for HDMI_HDCP2P2_DDC_TIMER_CTRL1 and
+ * HDMI_HDCP2P2_DDC_TIMER_CTRL2.
+ * Following are the timers:
+ * 1. DDC_REQUEST_TIMER: Timeout in hsyncs in which to wait for the
+ * HDCP 2.2 sink to respond to an RxStatus request
+ * 2. DDC_URGENT_TIMER: Time period in hsyncs to issue an urgent flag
+ * when an RxStatus DDC request is made but not accepted by I2C
+ * engine
+ * 3. DDC_TIMEOUT_TIMER: Timeout in hsyncs which starts counting when
+ * a request is made and stops when it is accepted by DDC arbiter
+ */
+
+ timeout = data->timeout_hsync;
+ timer = data->periodic_timer_hsync;
+
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_TIMER_CTRL, timer);
+ /* Set both urgent and hw-timeout fields to the same value */
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_TIMER_CTRL2,
+ (timeout << 16 | timeout));
+ /* enable interrupts */
+ reg_val = intr_en_mask;
+ /* Clear interrupt status bits */
+ reg_val |= intr_en_mask >> 1;
+
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL0, reg_val);
+ reg_val = hdmi_read(hdmi, HDMI_DDC_INT_CTRL5);
+ /* clear and enable RxStatus read timeout */
+ reg_val |= BIT(2) | BIT(1);
+
+ hdmi_write(hdmi, HDMI_DDC_INT_CTRL5, reg_val);
+ /*
+ * Enable hardware DDC access to RxStatus register
+ *
+ * HDMI_HW_DDC_CTRL:Bits 1:0 (RXSTATUS_DDC_ENABLE) read like this:
+ *
+ * 0 = disable HW controlled DDC access to RxStatus
+ * 1 = automatic on when HDCP 2.2 is authenticated and loop based on
+ * request timer (i.e. the hardware will loop automatically)
+ * 2 = force on and loop based on request timer (hardware will loop)
+ * 3 = enable by sw trigger and loop until interrupt is generated for
+ * RxStatus.reauth_req, RxStatus.ready or RxStatus.message_Size.
+ *
+ * Depending on the value of ddc_data::poll_sink, we make the decision
+ * to use either SW_TRIGGER(3) (poll_sink = false) which means that the
+ * hardware will poll sink and generate interrupt when sink responds,
+ * or use AUTOMATIC_LOOP(1) (poll_sink = true) which will poll the sink
+ * based on request timer
+ */
+
+ reg_val = hdmi_read(hdmi, HDMI_HW_DDC_CTRL);
+ reg_val &= ~(BIT(1) | BIT(0));
+
+ busy_wait_us = data->timeout_ms * HDMI_MS_TO_US;
+
+ /* read method: HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER */
+ reg_val |= BIT(1) | BIT(0);
+ hdmi_write(hdmi, HDMI_HW_DDC_CTRL, reg_val);
+
+ hdmi_write(hdmi, HDMI_HDCP2P2_DDC_SW_TRIGGER, 1);
+ if (data->wait) {
+ reinit_completion(&ddc_ctrl->rx_status_done);
+ rem = wait_for_completion_timeout(&ddc_ctrl->rx_status_done,
+ HZ);
+ data->timeout_left = jiffies_to_msecs(rem);
+
+ if (!data->timeout_left) {
+ pr_err("sw ddc rxstatus timeout\n");
+ rc = -ETIMEDOUT;
+ }
+ sde_hdmi_hdcp2p2_ddc_disable((void *)display);
+ }
+ return rc;
+}
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h
index 6becf1efc9d8..697cb0c40754 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h
@@ -37,6 +37,72 @@
#define HDMI_UTIL_ERROR(fmt, args...) SDE_ERROR(fmt, ##args)
+/*
+ * Offsets in HDMI_DDC_INT_CTRL0 register
+ *
+ * The HDMI_DDC_INT_CTRL0 register is intended for HDCP 2.2 RxStatus
+ * register manipulation. It reads like this:
+ *
+ * Bit 31: RXSTATUS_MESSAGE_SIZE_MASK (1 = generate interrupt when size > 0)
+ * Bit 30: RXSTATUS_MESSAGE_SIZE_ACK (1 = Acknowledge message size intr)
+ * Bits 29-20: RXSTATUS_MESSAGE_SIZE (Actual size of message available)
+ * Bits 19-18: RXSTATUS_READY_MASK (1 = generate interrupt when ready = 1
+ * 2 = generate interrupt when ready = 0)
+ * Bit 17: RXSTATUS_READY_ACK (1 = Acknowledge ready bit interrupt)
+ * Bit 16: RXSTATUS_READY (1 = Rxstatus ready bit read is 1)
+ * Bit 15: RXSTATUS_READY_NOT (1 = Rxstatus ready bit read is 0)
+ * Bit 14: RXSTATUS_REAUTH_REQ_MASK (1 = generate interrupt when reauth is
+ * requested by sink)
+ * Bit 13: RXSTATUS_REAUTH_REQ_ACK (1 = Acknowledge Reauth req interrupt)
+ * Bit 12: RXSTATUS_REAUTH_REQ (1 = Rxstatus reauth req bit read is 1)
+ * Bit 10: RXSTATUS_DDC_FAILED_MASK (1 = generate interrupt when DDC
+ * tranasaction fails)
+ * Bit 9: RXSTATUS_DDC_FAILED_ACK (1 = Acknowledge ddc failure interrupt)
+ * Bit 8: RXSTATUS_DDC_FAILED (1 = DDC transaction failed)
+ * Bit 6: RXSTATUS_DDC_DONE_MASK (1 = generate interrupt when DDC
+ * transaction completes)
+ * Bit 5: RXSTATUS_DDC_DONE_ACK (1 = Acknowledge ddc done interrupt)
+ * Bit 4: RXSTATUS_DDC_DONE (1 = DDC transaction is done)
+ * Bit 2: RXSTATUS_DDC_REQ_MASK (1 = generate interrupt when DDC Read
+ * request for RXstatus is made)
+ * Bit 1: RXSTATUS_DDC_REQ_ACK (1 = Acknowledge Rxstatus read interrupt)
+ * Bit 0: RXSTATUS_DDC_REQ (1 = RXStatus DDC read request is made)
+ *
+ */
+
+#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_SHIFT 20
+#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_MASK 0x3ff00000
+#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_ACK_SHIFT 30
+#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_INTR_SHIFT 31
+
+#define HDCP2P2_RXSTATUS_REAUTH_REQ_SHIFT 12
+#define HDCP2P2_RXSTATUS_REAUTH_REQ_MASK 1
+#define HDCP2P2_RXSTATUS_REAUTH_REQ_ACK_SHIFT 13
+#define HDCP2P2_RXSTATUS_REAUTH_REQ_INTR_SHIFT 14
+
+#define HDCP2P2_RXSTATUS_READY_SHIFT 16
+#define HDCP2P2_RXSTATUS_READY_MASK 1
+#define HDCP2P2_RXSTATUS_READY_ACK_SHIFT 17
+#define HDCP2P2_RXSTATUS_READY_INTR_SHIFT 18
+#define HDCP2P2_RXSTATUS_READY_INTR_MASK 18
+
+#define HDCP2P2_RXSTATUS_DDC_FAILED_SHIFT 8
+#define HDCP2P2_RXSTATUS_DDC_FAILED_ACKSHIFT 9
+#define HDCP2P2_RXSTATUS_DDC_FAILED_INTR_MASK 10
+#define HDCP2P2_RXSTATUS_DDC_DONE 6
+
+/* default hsyncs for 4k@60 for 200ms */
+#define HDMI_DEFAULT_TIMEOUT_HSYNC 28571
+
+/*
+ * Bits 1:0 in HDMI_HW_DDC_CTRL that dictate how the HDCP 2.2 RxStatus will be
+ * read by the hardware
+ */
+#define HDCP2P2_RXSTATUS_HW_DDC_DISABLE 0
+#define HDCP2P2_RXSTATUS_HW_DDC_AUTOMATIC_LOOP 1
+#define HDCP2P2_RXSTATUS_HW_DDC_FORCE_LOOP 2
+#define HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER 3
+
struct sde_hdmi_tx_ddc_data {
char *what;
u8 *data_buf;
@@ -50,14 +116,49 @@ struct sde_hdmi_tx_ddc_data {
int retry;
};
+enum sde_hdmi_tx_hdcp2p2_rxstatus_intr_mask {
+ RXSTATUS_MESSAGE_SIZE = BIT(31),
+ RXSTATUS_READY = BIT(18),
+ RXSTATUS_REAUTH_REQ = BIT(14),
+};
+
+struct sde_hdmi_tx_hdcp2p2_ddc_data {
+ enum sde_hdmi_tx_hdcp2p2_rxstatus_intr_mask intr_mask;
+ u32 timeout_ms;
+ u32 timeout_hsync;
+ u32 periodic_timer_hsync;
+ u32 timeout_left;
+ u32 read_method;
+ u32 message_size;
+ bool encryption_ready;
+ bool ready;
+ bool reauth_req;
+ bool ddc_max_retries_fail;
+ bool ddc_done;
+ bool ddc_read_req;
+ bool ddc_timeout;
+ bool wait;
+ int irq_wait_count;
+ void (*link_cb)(void *data);
+ void *link_data;
+};
+
struct sde_hdmi_tx_ddc_ctrl {
- atomic_t rxstatus_busy_wait_done;
+ struct completion rx_status_done;
struct dss_io_data *io;
struct sde_hdmi_tx_ddc_data ddc_data;
+ struct sde_hdmi_tx_hdcp2p2_ddc_data sde_hdcp2p2_ddc_data;
};
/* DDC */
int sde_hdmi_ddc_write(void *cb_data);
int sde_hdmi_ddc_read(void *cb_data);
+int sde_hdmi_ddc_scrambling_isr(void *hdmi_display);
+int _sde_hdmi_get_timeout_in_hysnc(void *hdmi_display, u32 timeout_ms);
+void _sde_hdmi_scrambler_ddc_disable(void *hdmi_display);
+void sde_hdmi_hdcp2p2_ddc_disable(void *hdmi_display);
+int sde_hdmi_hdcp2p2_read_rxstatus(void *hdmi_display);
+void sde_hdmi_ddc_config(void *hdmi_display);
+int sde_hdmi_ddc_hdcp2p2_isr(void *hdmi_display);
#endif /* _SDE_HDMI_UTIL_H_ */
diff --git a/drivers/gpu/drm/msm/sde_hdcp.h b/drivers/gpu/drm/msm/sde_hdcp.h
index 443bd5790551..49cca9399cb0 100644
--- a/drivers/gpu/drm/msm/sde_hdcp.h
+++ b/drivers/gpu/drm/msm/sde_hdcp.h
@@ -25,6 +25,7 @@
#include <drm/drm_edid.h>
#include "hdmi.h"
#include "sde_kms.h"
+#include "sde_hdmi_util.h"
#ifdef SDE_HDCP_DEBUG_ENABLE
#define SDE_HDCP_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args)
@@ -75,9 +76,9 @@ struct sde_hdcp_ops {
void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data);
void sde_hdcp_1x_deinit(void *input);
-
struct sde_hdcp_ops *sde_hdcp_1x_start(void *input);
-
+void *sde_hdmi_hdcp2p2_init(struct sde_hdcp_init_data *init_data);
+void sde_hdmi_hdcp2p2_deinit(void *input);
const char *sde_hdcp_state_name(enum sde_hdcp_states hdcp_state);
-
+struct sde_hdcp_ops *sde_hdmi_hdcp2p2_start(void *input);
#endif /* __SDE_HDCP_H__ */
diff --git a/drivers/gpu/drm/msm/sde_hdcp_1x.c b/drivers/gpu/drm/msm/sde_hdcp_1x.c
index e650098ec96d..3aba9e307732 100644
--- a/drivers/gpu/drm/msm/sde_hdcp_1x.c
+++ b/drivers/gpu/drm/msm/sde_hdcp_1x.c
@@ -1216,18 +1216,19 @@ static void sde_hdcp_1x_cache_topology(struct sde_hdcp_1x *hdcp)
memcpy((void *)&hdcp->cached_tp,
(void *) &hdcp->current_tp,
sizeof(hdcp->cached_tp));
+ hdcp1_cache_repeater_topology((void *)&hdcp->cached_tp);
}
-static void sde_hdcp_1x_notify_topology(struct sde_hdcp_1x *hdcp)
+static void sde_hdcp_1x_notify_topology(void)
{
- /* TO DO : something here for HDCP 1x*/
+ hdcp1_notify_topology();
}
static void sde_hdcp_1x_update_auth_status(struct sde_hdcp_1x *hdcp)
{
if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATED)) {
sde_hdcp_1x_cache_topology(hdcp);
- sde_hdcp_1x_notify_topology(hdcp);
+ sde_hdcp_1x_notify_topology();
}
if (hdcp->init_data.notify_status &&
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
index 648249916be4..f0831e64f250 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c
@@ -267,7 +267,9 @@ static int32_t msm_isp_stats_configure(struct vfe_device *vfe_dev,
int result = 0;
memset(&buf_event, 0, sizeof(struct msm_isp_event_data));
- buf_event.timestamp = ts->buf_time;
+ buf_event.timestamp = ts->event_time;
+ buf_event.mono_timestamp = ts->buf_time;
+
buf_event.frame_id = vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id;
pingpong_status = vfe_dev->hw_info->
vfe_ops.stats_ops.get_pingpong_status(vfe_dev);
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index f95cc37f5c2c..9cb7d5299ef8 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -32,7 +32,6 @@
#include "cam_hw_ops.h"
#include <media/msmb_generic_buf_mgr.h>
-
static struct v4l2_device *msm_v4l2_dev;
static struct list_head ordered_sd_list;
@@ -149,7 +148,7 @@ typedef int (*msm_queue_find_func)(void *d1, void *d2);
#define msm_queue_find(queue, type, member, func, data) ({\
unsigned long flags; \
struct msm_queue_head *__q = (queue); \
- type *node = 0; \
+ type *node = NULL; \
typeof(node) __ret = NULL; \
msm_queue_find_func __f = (func); \
spin_lock_irqsave(&__q->lock, flags); \
@@ -279,22 +278,47 @@ void msm_delete_stream(unsigned int session_id, unsigned int stream_id)
struct msm_session *session = NULL;
struct msm_stream *stream = NULL;
unsigned long flags;
+ int try_count = 0;
session = msm_queue_find(msm_session_q, struct msm_session,
list, __msm_queue_find_session, &session_id);
+
if (!session)
return;
- stream = msm_queue_find(&session->stream_q, struct msm_stream,
- list, __msm_queue_find_stream, &stream_id);
- if (!stream)
- return;
- spin_lock_irqsave(&(session->stream_q.lock), flags);
- list_del_init(&stream->list);
- session->stream_q.len--;
- kfree(stream);
- stream = NULL;
- spin_unlock_irqrestore(&(session->stream_q.lock), flags);
+ while (1) {
+
+ if (try_count > 5) {
+ pr_err("%s : not able to delete stream %d\n",
+ __func__, __LINE__);
+ break;
+ }
+
+ write_lock(&session->stream_rwlock);
+ try_count++;
+ stream = msm_queue_find(&session->stream_q, struct msm_stream,
+ list, __msm_queue_find_stream, &stream_id);
+
+ if (!stream) {
+ write_unlock(&session->stream_rwlock);
+ return;
+ }
+
+ if (msm_vb2_get_stream_state(stream) != 1) {
+ write_unlock(&session->stream_rwlock);
+ continue;
+ }
+
+ spin_lock_irqsave(&(session->stream_q.lock), flags);
+ list_del_init(&stream->list);
+ session->stream_q.len--;
+ kfree(stream);
+ stream = NULL;
+ spin_unlock_irqrestore(&(session->stream_q.lock), flags);
+ write_unlock(&session->stream_rwlock);
+ break;
+ }
+
}
EXPORT_SYMBOL(msm_delete_stream);
@@ -444,6 +468,7 @@ int msm_create_session(unsigned int session_id, struct video_device *vdev)
mutex_init(&session->lock);
mutex_init(&session->lock_q);
mutex_init(&session->close_lock);
+ rwlock_init(&session->stream_rwlock);
if (gpu_limit) {
session->sysfs_pwr_limit = kgsl_pwr_limits_add(KGSL_DEVICE_3D0);
@@ -1048,17 +1073,25 @@ static struct v4l2_file_operations msm_fops = {
#endif
};
-struct msm_stream *msm_get_stream(unsigned int session_id,
- unsigned int stream_id)
+struct msm_session *msm_get_session(unsigned int session_id)
{
struct msm_session *session;
- struct msm_stream *stream;
session = msm_queue_find(msm_session_q, struct msm_session,
list, __msm_queue_find_session, &session_id);
if (!session)
return ERR_PTR(-EINVAL);
+ return session;
+}
+EXPORT_SYMBOL(msm_get_session);
+
+
+struct msm_stream *msm_get_stream(struct msm_session *session,
+ unsigned int stream_id)
+{
+ struct msm_stream *stream;
+
stream = msm_queue_find(&session->stream_q, struct msm_stream,
list, __msm_queue_find_stream, &stream_id);
@@ -1115,6 +1148,34 @@ struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q)
}
EXPORT_SYMBOL(msm_get_stream_from_vb2q);
+struct msm_session *msm_get_session_from_vb2q(struct vb2_queue *q)
+{
+ struct msm_session *session;
+ struct msm_stream *stream;
+ unsigned long flags1;
+ unsigned long flags2;
+
+ spin_lock_irqsave(&msm_session_q->lock, flags1);
+ list_for_each_entry(session, &(msm_session_q->list), list) {
+ spin_lock_irqsave(&(session->stream_q.lock), flags2);
+ list_for_each_entry(
+ stream, &(session->stream_q.list), list) {
+ if (stream->vb2_q == q) {
+ spin_unlock_irqrestore
+ (&(session->stream_q.lock), flags2);
+ spin_unlock_irqrestore
+ (&msm_session_q->lock, flags1);
+ return session;
+ }
+ }
+ spin_unlock_irqrestore(&(session->stream_q.lock), flags2);
+ }
+ spin_unlock_irqrestore(&msm_session_q->lock, flags1);
+ return NULL;
+}
+EXPORT_SYMBOL(msm_get_session_from_vb2q);
+
+
#ifdef CONFIG_COMPAT
long msm_copy_camera_private_ioctl_args(unsigned long arg,
struct msm_camera_private_ioctl_arg *k_ioctl,
diff --git a/drivers/media/platform/msm/camera_v2/msm.h b/drivers/media/platform/msm/camera_v2/msm.h
index 7474cb119147..dce47bc7249c 100644
--- a/drivers/media/platform/msm/camera_v2/msm.h
+++ b/drivers/media/platform/msm/camera_v2/msm.h
@@ -111,6 +111,7 @@ struct msm_session {
struct mutex lock;
struct mutex lock_q;
struct mutex close_lock;
+ rwlock_t stream_rwlock;
struct kgsl_pwr_limit *sysfs_pwr_limit;
};
@@ -129,11 +130,13 @@ int msm_create_stream(unsigned int session_id,
void msm_delete_stream(unsigned int session_id, unsigned int stream_id);
int msm_create_command_ack_q(unsigned int session_id, unsigned int stream_id);
void msm_delete_command_ack_q(unsigned int session_id, unsigned int stream_id);
-struct msm_stream *msm_get_stream(unsigned int session_id,
+struct msm_session *msm_get_session(unsigned int session_id);
+struct msm_stream *msm_get_stream(struct msm_session *session,
unsigned int stream_id);
struct vb2_queue *msm_get_stream_vb2q(unsigned int session_id,
unsigned int stream_id);
struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q);
+struct msm_session *msm_get_session_from_vb2q(struct vb2_queue *q);
struct msm_session *msm_session_find(unsigned int session_id);
#ifdef CONFIG_COMPAT
long msm_copy_camera_private_ioctl_args(unsigned long arg,
diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
index c779ee46c19a..ba9b4df6bf22 100644
--- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, 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
@@ -44,17 +44,25 @@ static int msm_vb2_queue_setup(struct vb2_queue *q,
int msm_vb2_buf_init(struct vb2_buffer *vb)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct msm_vb2_buffer *msm_vb2_buf;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ session = msm_get_session_from_vb2q(vb->vb2_queue);
+ if (IS_ERR_OR_NULL(session))
+ return -EINVAL;
+
+ read_lock(&session->stream_rwlock);
+
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
if (!stream) {
pr_err("%s: Couldn't find stream\n", __func__);
+ read_unlock(&session->stream_rwlock);
return -EINVAL;
}
msm_vb2_buf = container_of(vbuf, struct msm_vb2_buffer, vb2_v4l2_buf);
msm_vb2_buf->in_freeq = 0;
-
+ read_unlock(&session->stream_rwlock);
return 0;
}
@@ -62,6 +70,7 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb)
{
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
+ struct msm_session *session;
unsigned long flags;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
@@ -71,21 +80,30 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb)
return;
}
+ session = msm_get_session_from_vb2q(vb->vb2_queue);
+ if (IS_ERR_OR_NULL(session))
+ return;
+
+ read_lock(&session->stream_rwlock);
+
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
if (!stream) {
pr_err("%s:%d] NULL stream", __func__, __LINE__);
+ read_unlock(&session->stream_rwlock);
return;
}
spin_lock_irqsave(&stream->stream_lock, flags);
list_add_tail(&msm_vb2->list, &stream->queued_list);
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
}
static void msm_vb2_buf_finish(struct vb2_buffer *vb)
{
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
+ struct msm_session *session;
unsigned long flags;
struct msm_vb2_buffer *msm_vb2_entry, *temp;
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
@@ -96,9 +114,16 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb)
return;
}
+ session = msm_get_session_from_vb2q(vb->vb2_queue);
+ if (IS_ERR_OR_NULL(session))
+ return;
+
+ read_lock(&session->stream_rwlock);
+
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
if (!stream) {
pr_err("%s:%d] NULL stream", __func__, __LINE__);
+ read_unlock(&session->stream_rwlock);
return;
}
@@ -111,6 +136,7 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb)
}
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return;
}
@@ -118,12 +144,20 @@ static void msm_vb2_stop_stream(struct vb2_queue *q)
{
struct msm_vb2_buffer *msm_vb2, *temp;
struct msm_stream *stream;
+ struct msm_session *session;
unsigned long flags;
struct vb2_v4l2_buffer *vb2_v4l2_buf;
+ session = msm_get_session_from_vb2q(q);
+ if (IS_ERR_OR_NULL(session))
+ return;
+
+ read_lock(&session->stream_rwlock);
+
stream = msm_get_stream_from_vb2q(q);
if (!stream) {
pr_err_ratelimited("%s:%d] NULL stream", __func__, __LINE__);
+ read_unlock(&session->stream_rwlock);
return;
}
@@ -143,7 +177,27 @@ static void msm_vb2_stop_stream(struct vb2_queue *q)
msm_vb2->in_freeq = 0;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
+}
+
+int msm_vb2_get_stream_state(struct msm_stream *stream)
+{
+ struct msm_vb2_buffer *msm_vb2, *temp;
+ unsigned long flags;
+ int rc = 1;
+
+ spin_lock_irqsave(&stream->stream_lock, flags);
+ list_for_each_entry_safe(msm_vb2, temp, &(stream->queued_list), list) {
+ if (msm_vb2->in_freeq != 0) {
+ rc = 0;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&stream->stream_lock, flags);
+ return rc;
}
+EXPORT_SYMBOL(msm_vb2_get_stream_state);
+
static struct vb2_ops msm_vb2_get_q_op = {
.queue_setup = msm_vb2_queue_setup,
@@ -198,14 +252,23 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id,
unsigned int stream_id)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
struct msm_vb2_buffer *msm_vb2 = NULL;
unsigned long flags;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
return NULL;
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
+ return NULL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
if (!stream->vb2_q) {
@@ -228,6 +291,7 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id,
vb2_v4l2_buf = NULL;
end:
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return vb2_v4l2_buf;
}
@@ -235,13 +299,23 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id,
unsigned int stream_id, uint32_t index)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
struct msm_vb2_buffer *msm_vb2 = NULL;
unsigned long flags;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
+ return NULL;
+
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
return NULL;
+ }
spin_lock_irqsave(&stream->stream_lock, flags);
@@ -263,6 +337,7 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id,
vb2_v4l2_buf = NULL;
end:
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return vb2_v4l2_buf;
}
@@ -270,14 +345,24 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id,
unsigned int stream_id)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct msm_vb2_buffer *msm_vb2;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
int rc = 0;
unsigned long flags;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
return -EINVAL;
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
+ return -EINVAL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
if (vb) {
list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
@@ -305,6 +390,7 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id,
rc = -EINVAL;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return rc;
}
@@ -315,12 +401,22 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id,
unsigned long flags;
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
int rc = 0;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
return -EINVAL;
+
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
+ return -EINVAL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
if (vb) {
list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
@@ -352,6 +448,7 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id,
rc = -EINVAL;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return rc;
}
@@ -359,15 +456,24 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id,
uint32_t index)
{
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
struct msm_vb2_buffer *msm_vb2 = NULL;
unsigned long flags;
long rc = -EINVAL;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
return rc;
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
+ return -EINVAL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
if (!stream->vb2_q) {
@@ -393,6 +499,7 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id,
end:
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return rc;
}
EXPORT_SYMBOL(msm_vb2_return_buf_by_idx);
@@ -402,11 +509,21 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id)
unsigned long flags;
struct msm_vb2_buffer *msm_vb2;
struct msm_stream *stream;
+ struct msm_session *session;
struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL;
- stream = msm_get_stream(session_id, stream_id);
- if (IS_ERR_OR_NULL(stream))
+ session = msm_get_session(session_id);
+ if (IS_ERR_OR_NULL(session))
+ return -EINVAL;
+
+ read_lock(&session->stream_rwlock);
+
+ stream = msm_get_stream(session, stream_id);
+ if (IS_ERR_OR_NULL(stream)) {
+ read_unlock(&session->stream_rwlock);
return -EINVAL;
+ }
+
spin_lock_irqsave(&stream->stream_lock, flags);
list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf);
@@ -415,6 +532,7 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id)
msm_vb2->in_freeq = 0;
}
spin_unlock_irqrestore(&stream->stream_lock, flags);
+ read_unlock(&session->stream_rwlock);
return 0;
}
diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h
index 53511d5416d7..c65cb58128d9 100644
--- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h
+++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, 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
@@ -67,5 +67,6 @@ struct vb2_mem_ops *msm_vb2_get_q_mem_ops(void);
int msm_vb2_request_cb(struct msm_sd_req_vb2_q *req_sd);
long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id,
uint32_t index);
+int msm_vb2_get_stream_state(struct msm_stream *stream);
#endif /*_MSM_VB_H */
diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c
index 6dabb92bc8ce..c6f2dbfe573d 100644
--- a/drivers/misc/hdcp.c
+++ b/drivers/misc/hdcp.c
@@ -12,10 +12,13 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
+#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/list.h>
@@ -30,6 +33,8 @@
#include <linux/errno.h>
#include <linux/hdcp_qseecom.h>
#include <linux/kthread.h>
+#include <linux/of.h>
+#include <video/msm_hdmi_hdcp_mgr.h>
#include "qseecom_kernel.h"
@@ -542,6 +547,24 @@ struct hdcp_lib_message_map {
const char *msg_name;
};
+struct msm_hdcp_mgr {
+ struct platform_device *pdev;
+ dev_t dev_num;
+ struct cdev cdev;
+ struct class *class;
+ struct device *device;
+ struct HDCP_V2V1_MSG_TOPOLOGY cached_tp;
+ u32 tp_msgid;
+ void *client_ctx;
+ struct hdcp_lib_handle *handle;
+};
+
+#define CLASS_NAME "hdcp"
+#define DRIVER_NAME "msm_hdcp"
+
+static struct msm_hdcp_mgr *hdcp_drv_mgr;
+static struct hdcp_lib_handle *drv_client_handle;
+
static void hdcp_lib_clean(struct hdcp_lib_handle *handle);
static void hdcp_lib_init(struct hdcp_lib_handle *handle);
static void hdcp_lib_msg_sent(struct hdcp_lib_handle *handle);
@@ -2393,7 +2416,13 @@ int hdcp_library_register(struct hdcp_register_data *data)
}
*data->hdcp_ctx = handle;
+ /* Cache the client ctx to be used later
+ * HDCP driver probe happens earlier than
+ * SDE driver probe hence caching it to
+ * be used later.
+ */
+ drv_client_handle = handle;
handle->thread = kthread_run(kthread_worker_fn,
&handle->worker, "hdcp_tz_lib");
@@ -2433,3 +2462,273 @@ void hdcp_library_deregister(void *phdcpcontext)
kzfree(handle);
}
EXPORT_SYMBOL(hdcp_library_deregister);
+
+void hdcp1_notify_topology(void)
+{
+ char *envp[4];
+ char *a;
+ char *b;
+
+ a = kzalloc(SZ_16, GFP_KERNEL);
+
+ if (!a)
+ return;
+
+ b = kzalloc(SZ_16, GFP_KERNEL);
+
+ if (!b) {
+ kfree(a);
+ return;
+ }
+
+ envp[0] = "HDCP_MGR_EVENT=MSG_READY";
+ envp[1] = a;
+ envp[2] = b;
+ envp[3] = NULL;
+
+ snprintf(envp[1], 16, "%d", (int)DOWN_CHECK_TOPOLOGY);
+ snprintf(envp[2], 16, "%d", (int)HDCP_V1_TX);
+
+ kobject_uevent_env(&hdcp_drv_mgr->device->kobj, KOBJ_CHANGE, envp);
+ kfree(a);
+ kfree(b);
+}
+
+static ssize_t msm_hdcp_1x_sysfs_rda_tp(struct device *dev,
+struct device_attribute *attr, char *buf)
+{
+ ssize_t ret = 0;
+
+ if (!hdcp_drv_mgr) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ switch (hdcp_drv_mgr->tp_msgid) {
+ case DOWN_CHECK_TOPOLOGY:
+ case DOWN_REQUEST_TOPOLOGY:
+ buf[MSG_ID_IDX] = hdcp_drv_mgr->tp_msgid;
+ buf[RET_CODE_IDX] = HDCP_AUTHED;
+ ret = HEADER_LEN;
+
+ memcpy(buf + HEADER_LEN, &hdcp_drv_mgr->cached_tp,
+ sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
+
+ ret += sizeof(struct HDCP_V2V1_MSG_TOPOLOGY);
+
+ /* clear the flag once data is read back to user space*/
+ hdcp_drv_mgr->tp_msgid = -1;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+} /* hdcp_1x_sysfs_rda_tp*/
+
+static ssize_t msm_hdcp_1x_sysfs_wta_tp(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int msgid = 0;
+ ssize_t ret = count;
+
+ if (!hdcp_drv_mgr || !buf) {
+ pr_err("invalid input\n");
+ return -EINVAL;
+ }
+
+ msgid = buf[0];
+
+ switch (msgid) {
+ case DOWN_CHECK_TOPOLOGY:
+ case DOWN_REQUEST_TOPOLOGY:
+ hdcp_drv_mgr->tp_msgid = msgid;
+ break;
+ /* more cases added here */
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+} /* hdmi_tx_sysfs_wta_hpd */
+
+static ssize_t hdmi_hdcp2p2_sysfs_wta_min_level_change(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc;
+ int min_enc_lvl;
+ struct hdcp_lib_handle *handle;
+ ssize_t ret = count;
+
+ handle = hdcp_drv_mgr->handle;
+
+ rc = kstrtoint(buf, 10, &min_enc_lvl);
+ if (rc) {
+ pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc);
+ return -EINVAL;
+ }
+
+ if (handle && handle->client_ops->notify_lvl_change) {
+ handle->client_ops->notify_lvl_change(handle->client_ctx,
+ min_enc_lvl);
+ }
+
+ return ret;
+}
+
+static DEVICE_ATTR(tp, S_IRUGO | S_IWUSR, msm_hdcp_1x_sysfs_rda_tp,
+msm_hdcp_1x_sysfs_wta_tp);
+
+static DEVICE_ATTR(min_level_change, S_IWUSR, NULL,
+hdmi_hdcp2p2_sysfs_wta_min_level_change);
+
+void hdcp1_cache_repeater_topology(void *hdcp1_cached_tp)
+{
+ memcpy((void *)&hdcp_drv_mgr->cached_tp,
+ hdcp1_cached_tp,
+ sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
+}
+
+static struct attribute *msm_hdcp_fs_attrs[] = {
+ &dev_attr_tp.attr,
+ &dev_attr_min_level_change.attr,
+ NULL
+};
+
+static struct attribute_group msm_hdcp_fs_attr_group = {
+ .attrs = msm_hdcp_fs_attrs
+};
+
+static int msm_hdcp_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int msm_hdcp_close(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations msm_hdcp_fops = {
+ .owner = THIS_MODULE,
+ .open = msm_hdcp_open,
+ .release = msm_hdcp_close,
+};
+
+static const struct of_device_id msm_hdcp_dt_match[] = {
+ { .compatible = "qcom,msm-hdcp",},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, msm_hdcp_dt_match);
+
+static int msm_hdcp_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ hdcp_drv_mgr = devm_kzalloc(&pdev->dev, sizeof(struct msm_hdcp_mgr),
+ GFP_KERNEL);
+ if (!hdcp_drv_mgr)
+ return -ENOMEM;
+
+ hdcp_drv_mgr->pdev = pdev;
+
+ platform_set_drvdata(pdev, hdcp_drv_mgr);
+
+ ret = alloc_chrdev_region(&hdcp_drv_mgr->dev_num, 0, 1, DRIVER_NAME);
+ if (ret < 0) {
+ pr_err("alloc_chrdev_region failed ret = %d\n", ret);
+ goto error_get_dev_num;
+ }
+
+ hdcp_drv_mgr->class = class_create(THIS_MODULE, CLASS_NAME);
+ if (IS_ERR(hdcp_drv_mgr->class)) {
+ ret = PTR_ERR(hdcp_drv_mgr->class);
+ pr_err("couldn't create class rc = %d\n", ret);
+ goto error_class_create;
+ }
+
+ hdcp_drv_mgr->device = device_create(hdcp_drv_mgr->class, NULL,
+ hdcp_drv_mgr->dev_num, NULL, DRIVER_NAME);
+ if (IS_ERR(hdcp_drv_mgr->device)) {
+ ret = PTR_ERR(hdcp_drv_mgr->device);
+ pr_err("device_create failed %d\n", ret);
+ goto error_class_device_create;
+ }
+
+ cdev_init(&hdcp_drv_mgr->cdev, &msm_hdcp_fops);
+ ret = cdev_add(&hdcp_drv_mgr->cdev,
+ MKDEV(MAJOR(hdcp_drv_mgr->dev_num), 0), 1);
+ if (ret < 0) {
+ pr_err("cdev_add failed %d\n", ret);
+ goto error_cdev_add;
+ }
+
+ ret = sysfs_create_group(&hdcp_drv_mgr->device->kobj,
+ &msm_hdcp_fs_attr_group);
+ if (ret)
+ pr_err("unable to register rotator sysfs nodes\n");
+
+ /* Store the handle in the hdcp drv mgr
+ * to be used for the sysfs notifications
+ */
+ hdcp_drv_mgr->handle = drv_client_handle;
+
+ return 0;
+error_cdev_add:
+ device_destroy(hdcp_drv_mgr->class, hdcp_drv_mgr->dev_num);
+error_class_device_create:
+ class_destroy(hdcp_drv_mgr->class);
+error_class_create:
+ unregister_chrdev_region(hdcp_drv_mgr->dev_num, 1);
+error_get_dev_num:
+ devm_kfree(&pdev->dev, hdcp_drv_mgr);
+ hdcp_drv_mgr = NULL;
+ return ret;
+}
+
+static int msm_hdcp_remove(struct platform_device *pdev)
+{
+ struct msm_hdcp_mgr *mgr;
+
+ mgr = (struct msm_hdcp_mgr *)platform_get_drvdata(pdev);
+ if (!mgr)
+ return -ENODEV;
+
+ sysfs_remove_group(&hdcp_drv_mgr->device->kobj,
+ &msm_hdcp_fs_attr_group);
+ cdev_del(&hdcp_drv_mgr->cdev);
+ device_destroy(hdcp_drv_mgr->class, hdcp_drv_mgr->dev_num);
+ class_destroy(hdcp_drv_mgr->class);
+ unregister_chrdev_region(hdcp_drv_mgr->dev_num, 1);
+
+ devm_kfree(&pdev->dev, hdcp_drv_mgr);
+ hdcp_drv_mgr = NULL;
+ return 0;
+}
+
+static struct platform_driver msm_hdcp_driver = {
+ .probe = msm_hdcp_probe,
+ .remove = msm_hdcp_remove,
+ .driver = {
+ .name = "msm_hdcp",
+ .of_match_table = msm_hdcp_dt_match,
+ .pm = NULL,
+ }
+};
+
+static int __init msm_hdcp_init(void)
+{
+ return platform_driver_register(&msm_hdcp_driver);
+}
+
+static void __exit msm_hdcp_exit(void)
+{
+ return platform_driver_unregister(&msm_hdcp_driver);
+}
+
+module_init(msm_hdcp_init);
+module_exit(msm_hdcp_exit);
+
+MODULE_DESCRIPTION("MSM HDCP driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 288d79f4defb..feb9cbe1f068 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -1958,7 +1958,7 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
sdhci_msm_pm_qos_parse(dev, pdata);
if (of_get_property(np, "qcom,core_3_0v_support", NULL))
- pdata->core_3_0v_support = true;
+ msm_host->core_3_0v_support = true;
pdata->sdr104_wa = of_property_read_bool(np, "qcom,sdr104-wa");
@@ -2639,7 +2639,9 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
*/
mb();
- if ((io_level & REQ_IO_HIGH) && (msm_host->caps_0 & CORE_3_0V_SUPPORT))
+ if ((io_level & REQ_IO_HIGH) &&
+ (msm_host->caps_0 & CORE_3_0V_SUPPORT) &&
+ !msm_host->core_3_0v_support)
writel_relaxed((readl_relaxed(host->ioaddr +
msm_host_offset->CORE_VENDOR_SPEC) &
~CORE_IO_PAD_PWR_SWITCH), host->ioaddr +
@@ -4133,7 +4135,7 @@ static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host,
msm_host->use_14lpp_dll = true;
/* Fake 3.0V support for SDIO devices which requires such voltage */
- if (msm_host->pdata->core_3_0v_support) {
+ if (msm_host->core_3_0v_support) {
caps |= CORE_3_0V_SUPPORT;
writel_relaxed((readl_relaxed(host->ioaddr +
SDHCI_CAPABILITIES) | caps), host->ioaddr +
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index 92f61708001e..b63b4df3ded3 100644
--- a/drivers/mmc/host/sdhci-msm.h
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -152,7 +152,6 @@ struct sdhci_msm_pltfm_data {
u32 ice_clk_max;
u32 ice_clk_min;
struct sdhci_msm_pm_qos_data pm_qos_data;
- bool core_3_0v_support;
bool sdr104_wa;
};
@@ -226,6 +225,7 @@ struct sdhci_msm_host {
bool tuning_in_progress;
bool mci_removed;
const struct sdhci_msm_offset *offset;
+ bool core_3_0v_support;
};
extern char *saved_command_line;
diff --git a/drivers/phy/phy-qcom-ufs-qmp-v3.h b/drivers/phy/phy-qcom-ufs-qmp-v3.h
index 0c9c3e7896bf..8cb4b0eeb866 100644
--- a/drivers/phy/phy-qcom-ufs-qmp-v3.h
+++ b/drivers/phy/phy-qcom-ufs-qmp-v3.h
@@ -259,7 +259,7 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_0_0[] = {
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x4B),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0xF1),
+ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0x81),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW, 0x80),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6E),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A),
@@ -320,7 +320,7 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_1_0[] = {
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x4B),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0xF1),
+ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0x81),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW, 0x80),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_LANE_MODE_1, 0x06),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_SIGDET_LVL, 0x24),
@@ -336,7 +336,7 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_1_0[] = {
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN, 0x04),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SO_SATURATION_AND_ENABLE, 0x4B),
- UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_PI_CONTROLS, 0xF1),
+ UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_PI_CONTROLS, 0x81),
UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FASTLOCK_COUNT_LOW, 0x80),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_MULTI_LANE_CTRL1, 0x02),
UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6E),
diff --git a/drivers/platform/msm/mhi/mhi_bhi.c b/drivers/platform/msm/mhi/mhi_bhi.c
index 4354b2600472..68ef2595f3c3 100644
--- a/drivers/platform/msm/mhi/mhi_bhi.c
+++ b/drivers/platform/msm/mhi/mhi_bhi.c
@@ -537,11 +537,12 @@ void bhi_firmware_download(struct work_struct *work)
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enter\n");
- wait_event_interruptible(*mhi_dev_ctxt->mhi_ev_wq.bhi_event,
+ ret = wait_event_interruptible_timeout(
+ *mhi_dev_ctxt->mhi_ev_wq.bhi_event,
mhi_dev_ctxt->mhi_state == MHI_STATE_BHI ||
- mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT);
- if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT ||
- mhi_dev_ctxt->mhi_state != MHI_STATE_BHI) {
+ mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
+ msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT));
+ if (!ret || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"MHI is not in valid state for firmware download\n");
return;
diff --git a/drivers/platform/msm/mhi/mhi_macros.h b/drivers/platform/msm/mhi/mhi_macros.h
index 04ecf13991b3..ee0b9b759e0a 100644
--- a/drivers/platform/msm/mhi/mhi_macros.h
+++ b/drivers/platform/msm/mhi/mhi_macros.h
@@ -27,8 +27,7 @@
#define CMD_EL_PER_RING 128
#define ELEMENT_GAP 1
#define MHI_EPID 4
-#define MHI_MAX_RESUME_TIMEOUT 5000
-#define MHI_MAX_SUSPEND_TIMEOUT 5000
+#define MHI_MAX_STATE_TRANSITION_TIMEOUT 5000
#define MHI_MAX_CMD_TIMEOUT 500
#define MHI_RPM_AUTOSUSPEND_TMR_VAL_MS 1000
#define MAX_BUF_SIZE 32
diff --git a/drivers/platform/msm/mhi/mhi_pm.c b/drivers/platform/msm/mhi/mhi_pm.c
index ad9a6fd6b278..49db99100311 100644
--- a/drivers/platform/msm/mhi/mhi_pm.c
+++ b/drivers/platform/msm/mhi/mhi_pm.c
@@ -116,7 +116,7 @@ static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt,
mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
mhi_dev_ctxt->mhi_state == MHI_STATE_M1 ||
mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
- msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
+ msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT));
if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get M0||M1 event or LD pm_state:0x%x state:%s\n",
@@ -142,7 +142,7 @@ static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt,
r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m3_event,
mhi_dev_ctxt->mhi_state == MHI_STATE_M3 ||
mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
- msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT));
+ msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT));
if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get M3 event, timeout, current state:%s\n",
@@ -180,7 +180,7 @@ static int mhi_pm_initiate_m0(struct mhi_device_ctxt *mhi_dev_ctxt)
mhi_dev_ctxt->mhi_state == MHI_STATE_M0 ||
mhi_dev_ctxt->mhi_state == MHI_STATE_M1 ||
mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT,
- msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT));
+ msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT));
if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) {
mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR,
"Failed to get M0 event, timeout or LD\n");
@@ -322,9 +322,6 @@ static int mhi_pm_slave_mode_power_on(struct mhi_device_ctxt *mhi_dev_ctxt)
else
ret_val = 0;
- /* wait for firmware download to complete */
- flush_work(&mhi_dev_ctxt->bhi_ctxt.fw_load_work);
-
if (ret_val) {
read_lock_irq(&mhi_dev_ctxt->pm_xfer_lock);
mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt);
@@ -333,6 +330,9 @@ static int mhi_pm_slave_mode_power_on(struct mhi_device_ctxt *mhi_dev_ctxt)
unlock_pm_lock:
+ /* wait for firmware download to complete */
+ flush_work(&mhi_dev_ctxt->bhi_ctxt.fw_load_work);
+
mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit with ret:%d\n", ret_val);
mutex_unlock(&mhi_dev_ctxt->pm_lock);
return ret_val;
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 88956d3ba674..068c7ddfb739 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -344,6 +344,15 @@ config REGULATOR_MAX1586
regulator via I2C bus. The provided regulator is suitable
for PXA27x chips to control VCC_CORE and VCC_USIM voltages.
+config REGULATOR_MAX20010
+ tristate "Maxim MAX20010 regulator support"
+ depends on I2C
+ help
+ This driver supports the Maxim MAX20010 switching voltage regulator
+ (buck converter). The regulator is controlled using an I2C interface
+ and supports 2 programmable voltage ranges from 0.5V to 1.27V in 10mV
+ steps and 0.625V to 1.5875V in 12.5mV steps.
+
config REGULATOR_MAX8649
tristate "Maxim 8649 voltage regulator"
depends on I2C
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index e345f10f94af..856ebe2b5c1b 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o
obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o
obj-$(CONFIG_REGULATOR_MAX14577) += max14577.o
obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
+obj-$(CONFIG_REGULATOR_MAX20010) += max20010-regulator.o
obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o
obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
obj-$(CONFIG_REGULATOR_MAX8907) += max8907-regulator.o
diff --git a/drivers/regulator/max20010-regulator.c b/drivers/regulator/max20010-regulator.c
new file mode 100644
index 000000000000..a914ca70ccb7
--- /dev/null
+++ b/drivers/regulator/max20010-regulator.c
@@ -0,0 +1,490 @@
+/* Copyright (c) 2017 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/param.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+
+struct voltage_range {
+ int vrange_sel;
+ int min_uV;
+ int max_uV;
+ int step_uV;
+};
+
+struct max20010_slew_rate {
+ int slew_sel;
+ int soft_start;
+ int dvs;
+};
+
+struct max20010_device_info {
+ struct device *dev;
+ struct regulator_dev *rdev;
+ struct regulator_init_data *init_data;
+ struct regmap *regmap;
+ const struct voltage_range *range;
+ const struct max20010_slew_rate *slew_rate;
+ unsigned vout_sel;
+ bool enabled;
+};
+
+#define MAX20010_ID_REG 0x00
+
+#define MAX20010_VMAX_REG 0x02
+#define MAX20010_VMAX_MASK GENMASK(6, 0)
+
+#define MAX20010_CONFIG_REG 0x05
+#define MAX20010_CONFIG_SYNC_IO_MASK GENMASK(1, 0)
+#define MAX20010_CONFIG_MODE_MASK BIT(3)
+#define MAX20010_CONFIG_MODE_SYNC 0
+#define MAX20010_CONFIG_MODE_FPWM 8
+#define MAX20010_CONFIG_VSTEP_MASK BIT(7)
+#define MAX20010_CONFIG_VSTEP_SHIFT 7
+
+#define MAX20010_SLEW_REG 0x06
+#define MAX20010_SLEW_MASK GENMASK(3, 0)
+
+#define MAX20010_VSET_REG 0x07
+#define MAX20010_VSET_MASK GENMASK(6, 0)
+
+static const struct max20010_slew_rate slew_rates[] = {
+ {0, 22000, 22000},
+ {1, 11000, 22000},
+ {2, 5500, 22000},
+ {3, 11000, 11000},
+ {4, 5500, 11000},
+ {5, 44000, 44000},
+ {6, 22000, 44000},
+ {7, 11000, 44000},
+ {8, 5500, 44000},
+ {9, 5500, 5500},
+};
+
+static const struct voltage_range max20010_range0 = {0, 500000, 1270000, 10000};
+static const struct voltage_range max20010_range1 = {1, 625000, 1587500, 12500};
+
+static const struct regmap_config max20010_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX20010_VSET_REG,
+};
+
+static int max20010_set_voltage_sel(struct regulator_dev *rdev, unsigned sel)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ /* Set the voltage only if the regulator was enabled earlier */
+ if (info->enabled) {
+ rc = regulator_set_voltage_sel_regmap(rdev, sel);
+ if (rc) {
+ dev_err(info->dev,
+ "regulator set voltage failed for selector = 0x%2x, rc=%d\n",
+ sel, rc);
+ return rc;
+ }
+ }
+
+ info->vout_sel = sel;
+ return rc;
+}
+
+static int max20010_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+
+ return (info->enabled == true) ? 1 : 0;
+}
+
+static int max20010_regulator_enable(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ rc = regulator_set_voltage_sel_regmap(rdev, info->vout_sel);
+ if (rc) {
+ dev_err(info->dev, "regulator enable failed, rc=%d\n", rc);
+ return rc;
+ }
+ info->enabled = true;
+
+ return rc;
+}
+
+static int max20010_regulator_disable(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ rc = regulator_set_voltage_sel_regmap(rdev, 0x0);
+ if (rc) {
+ dev_err(info->dev, "regulator disable failed, rc=%d\n", rc);
+ return rc;
+ }
+ info->enabled = false;
+
+ return rc;
+}
+
+static inline unsigned int max20010_map_mode(unsigned int mode)
+{
+ return (mode == MAX20010_CONFIG_MODE_FPWM) ?
+ REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE;
+}
+
+static int max20010_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ rc = regmap_update_bits(info->regmap, MAX20010_CONFIG_REG,
+ MAX20010_CONFIG_MODE_MASK,
+ MAX20010_CONFIG_MODE_FPWM);
+ break;
+ case REGULATOR_MODE_IDLE:
+ rc = regmap_update_bits(info->regmap, MAX20010_CONFIG_REG,
+ (MAX20010_CONFIG_MODE_MASK
+ | MAX20010_CONFIG_SYNC_IO_MASK),
+ MAX20010_CONFIG_MODE_SYNC);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rc)
+ dev_err(info->dev, "failed to set %s mode, rc=%d\n",
+ mode == REGULATOR_MODE_NORMAL ? "Force PWM" : "SYNC",
+ rc);
+ return rc;
+}
+
+static unsigned int max20010_get_mode(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ unsigned int val;
+ int rc = 0;
+
+ rc = regmap_read(info->regmap, MAX20010_CONFIG_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "failed to read mode configuration, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return max20010_map_mode(val & MAX20010_CONFIG_MODE_MASK);
+}
+
+static int max20010_enable_time(struct regulator_dev *rdev)
+{
+ struct max20010_device_info *info = rdev_get_drvdata(rdev);
+ int volt_uV;
+
+ volt_uV = regulator_list_voltage_linear(rdev, info->vout_sel);
+ return DIV_ROUND_UP(volt_uV, info->slew_rate->soft_start);
+}
+
+static struct regulator_ops max20010_regulator_ops = {
+ .set_voltage_sel = max20010_set_voltage_sel,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .map_voltage = regulator_map_voltage_linear,
+ .list_voltage = regulator_list_voltage_linear,
+ .is_enabled = max20010_regulator_is_enabled,
+ .enable = max20010_regulator_enable,
+ .disable = max20010_regulator_disable,
+ .set_mode = max20010_set_mode,
+ .get_mode = max20010_get_mode,
+ .enable_time = max20010_enable_time,
+};
+
+static struct regulator_desc rdesc = {
+ .name = "max20010-reg",
+ .supply_name = "vin",
+ .owner = THIS_MODULE,
+ .ops = &max20010_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_min_sel = 1,
+ .vsel_reg = MAX20010_VSET_REG,
+ .vsel_mask = MAX20010_VSET_MASK,
+ .of_map_mode = max20010_map_mode,
+};
+
+static int max20010_device_setup(struct max20010_device_info *info)
+{
+ int max_uV, rc = 0;
+ unsigned int val;
+
+ rc = regmap_update_bits(info->regmap, MAX20010_CONFIG_REG,
+ MAX20010_CONFIG_VSTEP_MASK,
+ (info->range->vrange_sel
+ << MAX20010_CONFIG_VSTEP_SHIFT));
+ if (rc) {
+ dev_err(info->dev, "failed to update vstep configuration, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ max_uV = min(info->init_data->constraints.max_uV, info->range->max_uV);
+ val = DIV_ROUND_UP(max_uV - info->range->min_uV,
+ info->range->step_uV) + 1;
+ rc = regmap_update_bits(info->regmap, MAX20010_VMAX_REG,
+ MAX20010_VMAX_MASK, val);
+ if (rc) {
+ dev_err(info->dev, "failed to write VMAX configuration, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = regmap_update_bits(info->regmap, MAX20010_SLEW_REG,
+ MAX20010_SLEW_MASK, info->slew_rate->slew_sel);
+ if (rc) {
+ dev_err(info->dev, "failed to write slew configuration, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Store default voltage register value */
+ rc = regmap_read(info->regmap, MAX20010_VSET_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "failed to read voltage register, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ info->vout_sel = val & MAX20010_VSET_MASK;
+ info->enabled = (info->vout_sel != 0x0) ? true : false;
+
+ return rc;
+}
+
+static int max20010_parse_init_data(struct max20010_device_info *info)
+{
+ struct device_node *of_node = info->dev->of_node;
+ int i, slew_index, ss_slew_rate, dvs_slew_rate, rc = 0;
+ unsigned int val;
+
+ if (of_find_property(of_node, "maxim,vrange-sel", NULL)) {
+ rc = of_property_read_u32(of_node, "maxim,vrange-sel", &val);
+ if (rc) {
+ dev_err(info->dev, "maxim,vrange-sel property read failed, rc=%d\n",
+ rc);
+ return rc;
+ } else if (val > 1) {
+ dev_err(info->dev, "unsupported vrange-sel value = %d, should be either 0 or 1\n",
+ val);
+ return -EINVAL;
+ }
+ } else {
+ /* Read default voltage range value */
+ rc = regmap_read(info->regmap, MAX20010_CONFIG_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "failed to read config register, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = (val & MAX20010_CONFIG_VSTEP_MASK)
+ >> MAX20010_CONFIG_VSTEP_SHIFT;
+ }
+
+ info->range = (val == 0) ? &max20010_range0 : &max20010_range1;
+
+ /*
+ * Verify the min and max constraints specified through regulator device
+ * properties are fit with in that of the selected voltage range of the
+ * device.
+ */
+ if (info->init_data->constraints.min_uV < info->range->min_uV ||
+ info->init_data->constraints.max_uV > info->range->max_uV) {
+ dev_err(info->dev,
+ "Regulator min/max constraints are not fit with in the device min/max constraints\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Read soft-start and dvs slew rates from device node. Use default
+ * values if not specified.
+ *
+ * Read the register default values and modify them with the slew-rates
+ * defined through device node.
+ */
+ rc = regmap_read(info->regmap, MAX20010_SLEW_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "failed to read slew register, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ slew_index = val & MAX20010_SLEW_MASK;
+
+ if (slew_index >= ARRAY_SIZE(slew_rates)) {
+ dev_err(info->dev, "unsupported default slew configuration\n");
+ return -EINVAL;
+ }
+
+ ss_slew_rate = slew_rates[slew_index].soft_start;
+ dvs_slew_rate = slew_rates[slew_index].dvs;
+
+ if (of_find_property(of_node, "maxim,soft-start-slew-rate", NULL)) {
+ rc = of_property_read_u32(of_node, "maxim,soft-start-slew-rate",
+ &val);
+ if (rc) {
+ dev_err(info->dev, "maxim,soft-start-slew-rate read failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ ss_slew_rate = val;
+ }
+
+ if (of_find_property(of_node, "maxim,dvs-slew-rate", NULL)) {
+ rc = of_property_read_u32(of_node, "maxim,dvs-slew-rate",
+ &val);
+ if (rc) {
+ dev_err(info->dev, "maxim,dvs-slew-rate read failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ dvs_slew_rate = val;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(slew_rates); i++) {
+ if (ss_slew_rate == slew_rates[i].soft_start
+ && dvs_slew_rate == slew_rates[i].dvs) {
+ info->slew_rate = &slew_rates[i];
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(slew_rates)) {
+ dev_err(info->dev, "invalid slew-rate values are specified.\n");
+ return -EINVAL;
+ }
+
+ return rc;
+}
+
+static int max20010_regulator_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max20010_device_info *info;
+ struct regulator_config config = { };
+ int val, rc = 0;
+
+ info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = &client->dev;
+ info->init_data = of_get_regulator_init_data(info->dev,
+ info->dev->of_node, &rdesc);
+ if (!info->init_data) {
+ dev_err(info->dev, "regulator init_data is missing\n");
+ return -ENODEV;
+ }
+
+ info->init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE;
+ info->init_data->constraints.valid_modes_mask
+ = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
+
+ info->regmap = devm_regmap_init_i2c(client, &max20010_regmap_config);
+ if (IS_ERR(info->regmap)) {
+ dev_err(info->dev, "Error in allocating regmap\n");
+ return PTR_ERR(info->regmap);
+ }
+
+ i2c_set_clientdata(client, info);
+
+ /* Get chip Id */
+ rc = regmap_read(info->regmap, MAX20010_ID_REG, &val);
+ if (rc) {
+ dev_err(info->dev, "Failed to get chip ID!\n");
+ return rc;
+ }
+
+ rc = max20010_parse_init_data(info);
+ if (rc) {
+ dev_err(info->dev, "max20010 init data parsing failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = max20010_device_setup(info);
+ if (rc) {
+ dev_err(info->dev, "Failed to setup device, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ config.dev = info->dev;
+ config.init_data = info->init_data;
+ config.regmap = info->regmap;
+ config.driver_data = info;
+ config.of_node = client->dev.of_node;
+
+ rdesc.min_uV = info->range->min_uV;
+ rdesc.uV_step = info->range->step_uV;
+ rdesc.n_voltages = DIV_ROUND_UP((info->range->max_uV
+ - info->range->min_uV),
+ info->range->step_uV);
+ rdesc.ramp_delay = info->slew_rate->dvs;
+
+ info->rdev = devm_regulator_register(info->dev, &rdesc, &config);
+ if (IS_ERR(info->rdev)) {
+ dev_err(info->dev, "Failed to register regulator, rc=%d\n", rc);
+ return PTR_ERR(info->rdev);
+ }
+
+ dev_info(info->dev, "Detected regulator MAX20010 PID = %d : voltage-range(%d) : (%d - %d) uV, step = %d uV\n",
+ val, info->range->vrange_sel, info->range->min_uV,
+ info->range->max_uV, info->range->step_uV);
+
+ return rc;
+}
+
+static const struct of_device_id max20010_match_table[] = {
+ {.compatible = "maxim,max20010", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max20010_match_table);
+
+static const struct i2c_device_id max20010_id[] = {
+ {"max20010", -1},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, max20010_id);
+
+static struct i2c_driver max20010_regulator_driver = {
+ .driver = {
+ .name = "max20010-regulator",
+ .owner = THIS_MODULE,
+ .of_match_table = max20010_match_table,
+ },
+ .probe = max20010_regulator_probe,
+ .id_table = max20010_id,
+};
+module_i2c_driver(max20010_regulator_driver);
+
+MODULE_DESCRIPTION("MAX20010 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c
index 7406dba44320..6dd4b06bf377 100644
--- a/drivers/soc/qcom/memshare/msm_memshare.c
+++ b/drivers/soc/qcom/memshare/msm_memshare.c
@@ -968,8 +968,8 @@ static int memshare_child_probe(struct platform_device *pdev)
/*
* Memshare allocation for guaranteed clients
*/
- if (memblock[num_clients].guarantee) {
- if (client_id == 1 && size > 0)
+ if (memblock[num_clients].guarantee && size > 0) {
+ if (client_id == 1)
size += MEMSHARE_GUARD_BYTES;
rc = memshare_alloc(memsh_child->dev,
size,
@@ -980,6 +980,7 @@ static int memshare_child_probe(struct platform_device *pdev)
return rc;
}
memblock[num_clients].alloted = 1;
+ shared_hyp_mapping(num_clients);
}
/*
diff --git a/include/dt-bindings/regulator/max20010.h b/include/dt-bindings/regulator/max20010.h
new file mode 100644
index 000000000000..492e7287216f
--- /dev/null
+++ b/include/dt-bindings/regulator/max20010.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2017, 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 _DT_BINDINGS_REGULATOR_MAX20010_H
+#define _DT_BINDINGS_REGULATOR_MAX20010_H
+
+/* Regulator operating modes */
+#define MAX20010_OPMODE_SYNC 0
+#define MAX20010_OPMODE_FPWM 8
+
+#endif /* _DT_BINDINGS_REGULATOR_MAX20010_H */
diff --git a/include/linux/hdcp_qseecom.h b/include/linux/hdcp_qseecom.h
index 68f2dd993170..dc513fbab580 100644
--- a/include/linux/hdcp_qseecom.h
+++ b/include/linux/hdcp_qseecom.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -125,6 +125,7 @@ struct hdcp_txmtr_ops {
struct hdcp_client_ops {
int (*wakeup)(struct hdmi_hdcp_wakeup_data *data);
+ void (*notify_lvl_change)(void *client_ctx, int min_lvl);
};
enum hdcp_device_type {
@@ -146,5 +147,6 @@ void hdcp_library_deregister(void *phdcpcontext);
bool hdcp1_check_if_supported_load_app(void);
int hdcp1_set_keys(uint32_t *aksv_msb, uint32_t *aksv_lsb);
int hdcp1_set_enc(bool enable);
-
+void hdcp1_cache_repeater_topology(void *hdcp1_cached_tp);
+void hdcp1_notify_topology(void);
#endif /* __HDCP_QSEECOM_H */