diff options
| author | Casey Piper <cpiper@codeaurora.org> | 2015-07-02 10:39:36 -0700 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:43:42 -0700 |
| commit | bbdb824e975ddee32e19b5d397c08d3cee1b44ba (patch) | |
| tree | f9386dab88cc621aee5b87d9c15b57e4836353ea | |
| parent | 00ebebb0e9d7282166bfd1be0bf5580a27c71b78 (diff) | |
msm: mdss: hdmi: add hardware ddc rxstatus support
Support reading rxstatus through hardware interrupts.
Reading rxstatus through hardware allows locality
deadlines to be enforced.
Change-Id: Ic56b3e5c27f2410c7b060a6d5c7c88e0770dc16b
Signed-off-by: Alhad Purnapatre <alhadp@codeaurora.org>
Signed-off-by: Casey Piper <cpiper@codeaurora.org>
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_hdcp.h | 2 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_hdcp2p2.c | 89 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_tx.c | 1 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_util.c | 236 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_util.h | 86 |
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__ */ |
