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>
This commit is contained in:
parent
00ebebb0e9
commit
bbdb824e97
5 changed files with 378 additions and 32 deletions
|
@ -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 {
|
||||
|
|
|
@ -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 */
|
||||
u64 mult;
|
||||
u64 div;
|
||||
struct msm_hdmi_mode_timing_info *timing;
|
||||
|
||||
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";
|
||||
if (!hdcp2p2_ctrl) {
|
||||
DEV_ERR("%s: invalid hdcp2p2 data\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
timing = hdcp2p2_ctrl->init_data.timing;
|
||||
|
||||
msg_size = rx_status & 0x3FF;
|
||||
if (msg_size) {
|
||||
DEV_DBG("%s: Message available at sink, size %d\n",
|
||||
__func__, msg_size);
|
||||
break;
|
||||
}
|
||||
/* 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;
|
||||
|
||||
msleep(20);
|
||||
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;
|
||||
|
||||
} while (--wait_cycles);
|
||||
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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
Loading…
Add table
Reference in a new issue