diff options
| author | Abhinav Kumar <abhinavk@codeaurora.org> | 2017-05-17 16:24:38 -0700 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-06-15 21:18:35 -0700 |
| commit | 9342afef9fa19aaa6bba1bf0072d155345b02224 (patch) | |
| tree | 9df7dbe6d22b738892b6b93c54d530bcce037e50 | |
| parent | 85e5cecb6f102ceaa498cb8137182f9ba499f40b (diff) | |
drm/msm : add HDCP 2.2 helper functions to HDMI utils
HDCP 2.2 needs additional helper functions for handling
DDC transactions and interface specific calculations.
Add the necessary utility APIs and also move the necessary
ones to the HDMI utils file for better maintenance.
Change-Id: I215f04991c6ff71ec3e1db7674024e85fbc922e8
Signed-off-by: Abhinav Kumar <abhinavk@codeaurora.org>
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c | 68 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c | 456 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h | 101 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde_hdcp.h | 2 |
4 files changed, 578 insertions, 49 deletions
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..c5e219cb7441 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; 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..3b87500835a1 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,235 @@ #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_debug("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_debug("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); + data->ready = true; + } + + /* check for reauth req interrupt */ + if (intr0 & BIT(12)) { + /* ack and disable reauth req interrupt */ + intr0 |= BIT(13); + intr0 &= ~BIT(14); + + data->reauth_req = true; + } + + /* check for ddc fail interrupt */ + if (intr0 & BIT(8)) { + /* ack ddc fail interrupt */ + intr0 |= BIT(9); + data->ddc_max_retries_fail = true; + } + + /* check for ddc done interrupt */ + if (intr0 & BIT(4)) { + /* ack ddc done interrupt */ + intr0 |= BIT(5); + + 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) { + atomic_set(&ddc_ctrl->rxstatus_busy_wait_done, 1); + } 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 +540,230 @@ 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; + } + init_ddc(hdmi); +} + +int sde_hdmi_hdcp2p2_ddc_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; + + 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; + pr_debug("timeout: %d hsyncs, timer %d hsync\n", timeout, timer); + + 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; + + pr_debug("writing HDMI_DDC_INT_CTRL0 0x%x\n", reg_val); + 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; + atomic_set(&ddc_ctrl->rxstatus_busy_wait_done, 0); + + /* 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) { + while (busy_wait_us > 0 && + !atomic_read(&ddc_ctrl->rxstatus_busy_wait_done)) { + udelay(HDMI_BUSY_WAIT_DELAY_US); + busy_wait_us -= HDMI_BUSY_WAIT_DELAY_US; + }; + + if (busy_wait_us < 0) + busy_wait_us = 0; + + data->timeout_left = busy_wait_us / HDMI_MS_TO_US; + + 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..11a45936b7b3 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 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_ddc_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..6371edf6206d 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,7 +76,6 @@ 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); const char *sde_hdcp_state_name(enum sde_hdcp_states hdcp_state); |
