summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_hdcp.h2
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c89
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.c1
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_util.c236
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_util.h86
5 files changed, 380 insertions, 34 deletions
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h
index 86d010da18d7..901aaefac387 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h
@@ -14,6 +14,7 @@
#define __MDSS_HDMI_HDCP_H__
#include "mdss_hdmi_util.h"
+#include <video/msm_hdmi_modes.h>
#include <soc/qcom/scm.h>
enum hdmi_hdcp_state {
@@ -35,6 +36,7 @@ struct hdmi_hdcp_init_data {
struct hdmi_tx_ddc_ctrl *ddc_ctrl;
u32 phy_addr;
u32 hdmi_tx_ver;
+ struct msm_hdmi_mode_timing_info *timing;
};
struct hdmi_hdcp_ops {
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c
index 82081af9f805..5176bc521599 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c
@@ -88,6 +88,7 @@ struct hdcp2p2_ctrl {
bool hdcp_txmtr_init; /* Has the HDCP TZ transmitter been initialized */
struct hdcp_txmtr_ops *txmtr_ops; /* Ops for driver to call into TZ */
struct hdcp_client_ops *client_ops; /* Ops for driver to export to TZ */
+ struct completion rxstatus_completion; /* Rx status interrupt */
};
static int hdcp2p2_authenticate(void *input);
@@ -324,50 +325,51 @@ static int hdcp2p2_read_version(struct hdcp2p2_ctrl *hdcp2p2_ctrl,
/**
* Work item that polls the sink for an incoming message.
- * Since messages from sink during auth are always in response to messages
- * sent by TX, we know when to expect sink messages, and thus polling is
- * required by the HDCP 2.2 spec
*/
static void hdcp2p2_sink_message_work(struct work_struct *work)
{
struct hdcp2p2_ctrl *hdcp2p2_ctrl = container_of(work,
struct hdcp2p2_ctrl, hdcp_sink_message_work);
- struct hdmi_tx_ddc_data ddc_data;
+ struct hdmi_tx_hdcp2p2_ddc_data hdcp2p2_ddc_data;
int rc;
- u16 rx_status;
int msg_size;
- int wait_cycles = 50; /* 50 20 ms cycles of wait = 1 second total */
-
- do {
- memset(&ddc_data, 0, sizeof(ddc_data));
- ddc_data.dev_addr = HDCP_SINK_DDC_SLAVE_ADDR;
- ddc_data.offset = HDCP_SINK_DDC_HDCP2_RXSTATUS;
- ddc_data.data_buf = (u8 *)&rx_status;
- ddc_data.data_len = 2;
- ddc_data.request_len = 2;
- ddc_data.retry = 1;
- ddc_data.what = "HDCP2RxStatus";
-
- rc = hdmi_ddc_read(hdcp2p2_ctrl->init_data.ddc_ctrl, &ddc_data);
- if (rc) {
- DEV_ERR("%s: Error %d in read HDCP RX status register",
- __func__, rc);
- return;
- }
+ u64 mult;
+ u64 div;
+ struct msm_hdmi_mode_timing_info *timing;
- msg_size = rx_status & 0x3FF;
- if (msg_size) {
- DEV_DBG("%s: Message available at sink, size %d\n",
- __func__, msg_size);
- break;
- }
+ if (!hdcp2p2_ctrl) {
+ DEV_ERR("%s: invalid hdcp2p2 data\n", __func__);
+ return;
+ }
- msleep(20);
+ timing = hdcp2p2_ctrl->init_data.timing;
- } while (--wait_cycles);
+ /* calculate number of lines sent in 10ms */
+ mult = 10000 * ((u64)timing->pixel_freq * 1000);
+ div = hdmi_tx_get_v_total(timing) * 1000000;
+ if (div)
+ do_div(mult, div);
+ else
+ mult = 0;
+
+ memset(&hdcp2p2_ddc_data, 0, sizeof(hdcp2p2_ddc_data));
+ hdcp2p2_ddc_data.ddc_data.what = "HDCP2RxStatus";
+ hdcp2p2_ddc_data.ddc_data.data_buf = (u8 *)&msg_size;
+ hdcp2p2_ddc_data.ddc_data.data_len = sizeof(msg_size);
+ hdcp2p2_ddc_data.rxstatus_field = RXSTATUS_MESSAGE_SIZE;
+ hdcp2p2_ddc_data.timer_delay_lines = (u32)mult;
+ hdcp2p2_ddc_data.irq_wait_count = 100;
+ hdcp2p2_ddc_data.poll_sink = false;
+
+ hdmi_ddc_config(hdcp2p2_ctrl->init_data.ddc_ctrl);
+ DEV_DBG("%s: Reading rxstatus, timer delay lines %u\n", __func__,
+ (u32)mult);
+ rc = hdmi_hdcp2p2_ddc_read_rxstatus(hdcp2p2_ctrl->init_data.ddc_ctrl,
+ &hdcp2p2_ddc_data,
+ &hdcp2p2_ctrl->rxstatus_completion);
- if (!wait_cycles) {
- DEV_ERR("%s: Timeout in waiting for sink\n", __func__);
+ if (rc) {
+ DEV_ERR("%s: Could not read rxstatus from sink\n", __func__);
goto error;
} else {
/* Read message from sink now */
@@ -378,6 +380,7 @@ static void hdcp2p2_sink_message_work(struct work_struct *work)
DEV_ERR("%s: Could not allocate memory\n", __func__);
goto error;
}
+
message->message_bytes = kmalloc(msg_size, GFP_KERNEL);
if (!message->message_bytes) {
DEV_ERR("%s: Could not allocate memory\n", __func__);
@@ -409,6 +412,11 @@ static void hdcp2p2_tz_message_work(struct work_struct *work)
struct list_head *prev, *next;
struct hdcp2p2_message *msg;
+ if (!hdcp2p2_ctrl) {
+ DEV_ERR("%s: invalid hdcp2p2 data\n", __func__);
+ return;
+ }
+
mutex_lock(&hdcp2p2_ctrl->mutex);
if (list_empty(&hdcp2p2_ctrl->hdcp_sink_messages)) {
DEV_ERR("%s: No message is available from sink\n", __func__);
@@ -497,6 +505,8 @@ static int hdcp2p2_isr(void *input)
return -EINVAL;
}
+ DEV_DBG("%s\n INT_CTRL0 is 0x%x\n", __func__,
+ DSS_REG_R(hdcp2p2_ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0));
reg_val = DSS_REG_R(hdcp2p2_ctrl->init_data.core_io,
HDMI_HDCP_INT_CTRL2);
if (reg_val & BIT(0)) {
@@ -505,6 +515,15 @@ static int hdcp2p2_isr(void *input)
DSS_REG_W(hdcp2p2_ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2,
reg_val);
}
+
+ reg_val = DSS_REG_R(hdcp2p2_ctrl->init_data.core_io,
+ HDMI_DDC_INT_CTRL0);
+ if (reg_val & HDCP2P2_RXSTATUS_MESSAGE_SIZE_MASK) {
+ DSS_REG_W(hdcp2p2_ctrl->init_data.core_io, HDMI_DDC_INT_CTRL0,
+ reg_val & ~(BIT(31)));
+ complete(&hdcp2p2_ctrl->rxstatus_completion);
+ }
+
return 0;
}
@@ -630,6 +649,7 @@ static int hdcp2p2_send_message_to_sink(void *client_ctx, void *hdcp_handle,
return rc;
}
+ DEV_DBG("%s: Polling sink for next message\n", __func__);
/* Start polling sink for the next expected message in the protocol */
if (!authenticated)
schedule_work(&hdcp2p2_ctrl->hdcp_sink_message_work);
@@ -739,7 +759,7 @@ void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data)
}
if (init_data->hdmi_tx_ver < MIN_HDMI_TX_MAJOR_VERSION) {
- DEV_DBG("%s: HDMI Tx does not support HDCP 2.2\n", __func__);
+ DEV_ERR("%s: HDMI Tx does not support HDCP 2.2\n", __func__);
return ERR_PTR(-ENODEV);
}
@@ -765,6 +785,7 @@ void *hdmi_hdcp2p2_init(struct hdmi_hdcp_init_data *init_data)
INIT_WORK(&hdcp2p2_ctrl->hdcp_tz_message_work,
hdcp2p2_tz_message_work);
INIT_LIST_HEAD(&hdcp2p2_ctrl->hdcp_sink_messages);
+ init_completion(&hdcp2p2_ctrl->rxstatus_completion);
hdcp2p2_ctrl->sink_status = SINK_DISCONNECTED;
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
index c8dc5eb1b5ae..96772003a0b4 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
@@ -1285,6 +1285,7 @@ static int hdmi_tx_init_features(struct hdmi_tx_ctrl *hdmi_ctrl)
hdcp_init_data.notify_status = hdmi_tx_hdcp_cb;
hdcp_init_data.cb_data = (void *)hdmi_ctrl;
hdcp_init_data.hdmi_tx_ver = hdmi_ctrl->hdmi_tx_ver;
+ hdcp_init_data.timing = &hdmi_ctrl->vid_cfg.timing;
/*
* Try to initialize both HDCP 1.4 and 2.2 features, decide which one
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c
index 2f90bf63bc8b..5f24a76e5ce3 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_util.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c
@@ -1345,3 +1345,239 @@ int hdmi_setup_ddc_timers(struct hdmi_tx_ddc_ctrl *ctrl,
return 0;
}
+
+void hdmi_hdcp2p2_ddc_reset(struct hdmi_tx_ddc_ctrl *ctrl)
+{
+ u32 reg_val;
+
+ if (!ctrl) {
+ DEV_ERR("%s: Invalid parameters\n", __func__);
+ 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);
+ DSS_REG_W(ctrl->io, HDMI_DDC_INT_CTRL0, reg_val);
+
+ /* Reset DDC timers */
+ reg_val = BIT(0) | DSS_REG_R(ctrl->io, HDMI_HDCP2P2_DDC_CTRL);
+ DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_CTRL, reg_val);
+ reg_val = DSS_REG_R(ctrl->io, HDMI_HDCP2P2_DDC_CTRL);
+ reg_val &= ~BIT(0);
+ DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_CTRL, reg_val);
+}
+
+void hdmi_hdcp2p2_ddc_disable(struct hdmi_tx_ddc_ctrl *ctrl)
+{
+ u32 reg_val;
+ u32 retry_read = 100;
+ bool ddc_hw_not_ready;
+
+ if (!ctrl) {
+ DEV_ERR("%s: Invalid parameters\n", __func__);
+ return;
+ }
+
+ /* Clear RXSTATUS_DDC_DONE interrupt */
+ DSS_REG_W(ctrl->io, HDMI_DDC_INT_CTRL0, BIT(5));
+ ddc_hw_not_ready = true;
+
+ /* Make sure the interrupt is clear */
+ do {
+ reg_val = DSS_REG_R(ctrl->io, HDMI_DDC_INT_CTRL0);
+ ddc_hw_not_ready = reg_val & BIT(5);
+ if (ddc_hw_not_ready)
+ msleep(20);
+ } while (ddc_hw_not_ready && --retry_read);
+
+ /* Disable HW DDC access to RxStatus register */
+ reg_val = DSS_REG_R(ctrl->io, HDMI_HW_DDC_CTRL);
+ reg_val &= ~(BIT(1) | BIT(0));
+ DSS_REG_W(ctrl->io, HDMI_HW_DDC_CTRL, reg_val);
+}
+
+int hdmi_hdcp2p2_ddc_read_rxstatus(struct hdmi_tx_ddc_ctrl *ctrl,
+ struct hdmi_tx_hdcp2p2_ddc_data *hdcp2p2_ddc_data,
+ struct completion *rxstatus_completion)
+{
+ u32 reg_val;
+ u32 reg_field_shift;
+ u32 reg_field_mask;
+ u32 reg_intr_ack_shift;
+ u32 reg_intr_mask_shift;
+ u32 timeout;
+ u32 rxstatus_read_method;
+ u32 rxstatus_bytes;
+ bool poll_sink;
+
+ if (!hdcp2p2_ddc_data)
+ return -EINVAL;
+
+ /* We return a u32 for all RxStatus bits being read */
+ if (hdcp2p2_ddc_data->ddc_data.data_len < sizeof(u32))
+ return -EINVAL;
+
+ poll_sink = hdcp2p2_ddc_data->poll_sink;
+
+ /*
+ * Setup shifts and masks based on which RxStatus bits we are dealing
+ * with in this call
+ */
+ switch (hdcp2p2_ddc_data->rxstatus_field) {
+ case RXSTATUS_MESSAGE_SIZE:
+ reg_field_shift = HDCP2P2_RXSTATUS_MESSAGE_SIZE_SHIFT;
+ reg_field_mask = HDCP2P2_RXSTATUS_MESSAGE_SIZE_MASK;
+ reg_intr_ack_shift =
+ HDCP2P2_RXSTATUS_MESSAGE_SIZE_ACK_SHIFT;
+ reg_intr_mask_shift =
+ HDCP2P2_RXSTATUS_MESSAGE_SIZE_INTR_SHIFT;
+ break;
+ case RXSTATUS_REAUTH_REQ:
+ reg_field_shift = HDCP2P2_RXSTATUS_REAUTH_REQ_SHIFT;
+ reg_field_mask = HDCP2P2_RXSTATUS_REAUTH_REQ_MASK;
+ reg_intr_ack_shift =
+ HDCP2P2_RXSTATUS_REAUTH_REQ_ACK_SHIFT;
+ reg_intr_mask_shift =
+ HDCP2P2_RXSTATUS_REAUTH_REQ_INTR_SHIFT;
+ break;
+ case RXSTATUS_READY:
+ reg_field_shift = HDCP2P2_RXSTATUS_READY_SHIFT;
+ reg_field_mask = HDCP2P2_RXSTATUS_READY_MASK;
+ reg_intr_ack_shift =
+ HDCP2P2_RXSTATUS_READY_ACK_SHIFT;
+ reg_intr_mask_shift =
+ HDCP2P2_RXSTATUS_READY_INTR_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ DEV_DBG("%s: Requested msg size, shift %u mask %u ack %u mask %u\n",
+ __func__, reg_field_shift, reg_field_mask,
+ reg_intr_ack_shift, reg_intr_mask_shift);
+ /* Disable short read for now, sinks don't support it */
+ reg_val = DSS_REG_R(ctrl->io, HDMI_HDCP2P2_DDC_CTRL);
+ reg_val |= BIT(4);
+ DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_CTRL, reg_val);
+
+ /* Clear interrupt status bits */
+ reg_val = DSS_REG_R(ctrl->io, HDMI_DDC_INT_CTRL0);
+ reg_val &= ~BIT(reg_intr_ack_shift);
+ DSS_REG_W(ctrl->io, HDMI_DDC_INT_CTRL0, 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 = hdcp2p2_ddc_data->timer_delay_lines & 0xffff;
+ DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_TIMER_CTRL, timeout);
+ /* Set both urgent and hw-timeout fields to the same value */
+ DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_TIMER_CTRL2,
+ (timeout << 16 | timeout));
+
+ /* Enable the interrupt for the requested field, and enable timeouts */
+ reg_val = DSS_REG_R(ctrl->io, HDMI_DDC_INT_CTRL0);
+ reg_val |= BIT(reg_intr_mask_shift);
+ reg_val |= BIT(HDCP2P2_RXSTATUS_DDC_FAILED_INTR_MASK);
+ DEV_DBG("%s: Enabling interrupts for req field\n", __func__);
+ DSS_REG_W(ctrl->io, HDMI_DDC_INT_CTRL0, 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 = DSS_REG_R(ctrl->io, HDMI_HW_DDC_CTRL);
+ reg_val &= ~(BIT(1) | BIT(0));
+
+ rxstatus_read_method =
+ poll_sink ? HDCP2P2_RXSTATUS_HW_DDC_AUTOMATIC_LOOP
+ : HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER;
+
+ if (poll_sink)
+ reg_val |= BIT(0);
+ else
+ reg_val |= (BIT(1) | BIT(0));
+
+ DEV_DBG("%s: Enabling hardware access to rxstatus\n", __func__);
+ DSS_REG_W(ctrl->io, HDMI_HW_DDC_CTRL, reg_val);
+
+ if (rxstatus_read_method == HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER) {
+ /* If we are using SW_TRIGGER, then go ahead and trigger it */
+ DSS_REG_W(ctrl->io, HDMI_HDCP2P2_DDC_SW_TRIGGER, 1);
+
+ /*
+ * Now wait for interrupt bits to be set to indicate that the
+ * register is available to read
+ */
+ DEV_DBG("%s: HDMI_DDC_INT_CTRL0 is 0x%x, waiting for ISR\n",
+ __func__, DSS_REG_R(ctrl->io, HDMI_DDC_INT_CTRL0));
+ if (!wait_for_completion_timeout(rxstatus_completion,
+ msecs_to_jiffies(200))) {
+ DEV_ERR("%s: Timeout in waiting for interrupt\n",
+ __func__);
+ return -ETIMEDOUT;
+ }
+ }
+
+ /* Make sure no errors occurred during DDC transaction */
+ reg_val = DSS_REG_R(ctrl->io, HDMI_HDCP2P2_DDC_STATUS);
+
+ /* Check for NACK0, NACK1, TIMEOUT, ABORT bits */
+ reg_val &= (BIT(12) | BIT(14) | BIT(4) | BIT(8));
+
+ if (reg_val) {
+ DEV_ERR("%s: DDC transaction error\n", __func__);
+ return -EIO;
+ }
+
+ /* Read the RxStatus field that was requested */
+ reg_val = DSS_REG_R(ctrl->io, HDMI_DDC_INT_CTRL0);
+ rxstatus_bytes = reg_val;
+ rxstatus_bytes &= reg_field_mask;
+ rxstatus_bytes >>= reg_field_shift;
+ memcpy(hdcp2p2_ddc_data->ddc_data.data_buf,
+ &rxstatus_bytes, sizeof(rxstatus_bytes));
+
+ /* Read the RxStatus field that was requested */
+ /* Write the interrupt ack back */
+ reg_val |= BIT(reg_intr_ack_shift);
+ DSS_REG_W(ctrl->io, HDMI_DDC_INT_CTRL0, reg_val);
+
+ /* Clear the ack bits and the DDC_FAILED bit next */
+ reg_val = DSS_REG_R(ctrl->io, HDMI_DDC_INT_CTRL0);
+ reg_val &= ~BIT(reg_intr_ack_shift);
+ reg_val &= ~BIT(HDCP2P2_RXSTATUS_DDC_FAILED_INTR_MASK);
+ DSS_REG_W(ctrl->io, HDMI_DDC_INT_CTRL0, reg_val);
+
+ /* Disable hardware access to RxStatus register */
+ reg_val = DSS_REG_R(ctrl->io, HDMI_HW_DDC_CTRL);
+ reg_val &= ~(BIT(1) | BIT(0));
+ DSS_REG_W(ctrl->io, HDMI_HW_DDC_CTRL, reg_val);
+
+ return 0;
+}
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.h b/drivers/video/fbdev/msm/mdss_hdmi_util.h
index d222e2d83430..56ebfdb4e808 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_util.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_util.h
@@ -220,6 +220,10 @@
#define HDMI_DDC_INT_CTRL3 (0x0000043C)
#define HDMI_DDC_INT_CTRL4 (0x00000440)
#define HDMI_DDC_INT_CTRL5 (0x00000444)
+#define HDMI_HDCP2P2_DDC_CTRL (0x0000044C)
+#define HDMI_HDCP2P2_DDC_TIMER_CTRL (0x00000450)
+#define HDMI_HDCP2P2_DDC_TIMER_CTRL2 (0x00000454)
+#define HDMI_HDCP2P2_DDC_STATUS (0x00000458)
#define HDMI_SCRAMBLER_STATUS_DDC_CTRL (0x00000464)
#define HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL (0x00000468)
#define HDMI_SCRAMBLER_STATUS_DDC_TIMER_CTRL2 (0x0000046C)
@@ -227,6 +231,7 @@
#define HDMI_SCRAMBLER_STATUS_DDC_TIMER_STATUS (0x00000474)
#define HDMI_SCRAMBLER_STATUS_DDC_TIMER_STATUS2 (0x00000478)
#define HDMI_HW_DDC_CTRL (0x000004CC)
+#define HDMI_HDCP2P2_DDC_SW_TRIGGER (0x000004D0)
#define HDMI_HDCP_STATUS (0x00000500)
#define HDMI_HDCP_INT_CTRL2 (0x00000504)
@@ -297,6 +302,67 @@
#define HDCP_SEC_TZ_HV_HLOS_HDCP_SHA_CTRL (0x00000024)
#define HDCP_SEC_TZ_HV_HLOS_HDCP_SHA_DATA (0x00000028)
+/*
+ * 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
+
+/*
+ * 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
+
enum hdmi_tx_feature_type {
HDMI_TX_FEAT_EDID,
HDMI_TX_FEAT_HDCP,
@@ -345,6 +411,21 @@ struct hdmi_tx_ddc_data {
int retry;
};
+enum hdmi_tx_hdcp2p2_rxstatus_field {
+ RXSTATUS_MESSAGE_SIZE,
+ RXSTATUS_REAUTH_REQ,
+ RXSTATUS_READY,
+};
+
+struct hdmi_tx_hdcp2p2_ddc_data {
+ struct hdmi_tx_ddc_data ddc_data;
+ enum hdmi_tx_hdcp2p2_rxstatus_field rxstatus_field;
+ u32 timer_delay_lines;
+ bool poll_sink;
+ int irq_wait_count;
+};
+
+
struct hdmi_util_ds_data {
bool ds_registered;
u32 ds_max_clk;
@@ -396,5 +477,10 @@ int hdmi_scdc_read(struct hdmi_tx_ddc_ctrl *ctrl, u32 data_type, u32 *val);
int hdmi_scdc_write(struct hdmi_tx_ddc_ctrl *ctrl, u32 data_type, u32 val);
int hdmi_setup_ddc_timers(struct hdmi_tx_ddc_ctrl *ctrl,
u32 type, u32 to_in_num_lines);
+void hdmi_hdcp2p2_ddc_reset(struct hdmi_tx_ddc_ctrl *ctrl);
+void hdmi_hdcp2p2_ddc_disable(struct hdmi_tx_ddc_ctrl *ctrl);
+int hdmi_hdcp2p2_ddc_read_rxstatus(struct hdmi_tx_ddc_ctrl *ctrl,
+ struct hdmi_tx_hdcp2p2_ddc_data *hdcp2p2_ddc_data,
+ struct completion *rxstatus_completion);
#endif /* __HDMI_UTIL_H__ */