Hey Parshuram, This patch looks good to me. Reviewed-by: Robert Foss <robert.foss@xxxxxxxxxx> On Fri, 26 Feb 2021 at 17:18, Parshuram Thombare <pthombar@xxxxxxxxxxx> wrote: > > This patch enable HDCP in MHDP driver. > > Signed-off-by: Parshuram Thombare <pthombar@xxxxxxxxxxx> > --- > drivers/gpu/drm/bridge/cadence/Makefile | 2 +- > .../drm/bridge/cadence/cdns-mhdp8546-core.c | 105 +++- > .../drm/bridge/cadence/cdns-mhdp8546-core.h | 21 + > .../drm/bridge/cadence/cdns-mhdp8546-hdcp.c | 578 ++++++++++++++++++ > .../drm/bridge/cadence/cdns-mhdp8546-hdcp.h | 89 +++ > 5 files changed, 782 insertions(+), 13 deletions(-) > create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c > create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.h > > diff --git a/drivers/gpu/drm/bridge/cadence/Makefile b/drivers/gpu/drm/bridge/cadence/Makefile > index 8f647991b374..4d2db8df1bc6 100644 > --- a/drivers/gpu/drm/bridge/cadence/Makefile > +++ b/drivers/gpu/drm/bridge/cadence/Makefile > @@ -1,4 +1,4 @@ > # SPDX-License-Identifier: GPL-2.0-only > obj-$(CONFIG_DRM_CDNS_MHDP8546) += cdns-mhdp8546.o > -cdns-mhdp8546-y := cdns-mhdp8546-core.o > +cdns-mhdp8546-y := cdns-mhdp8546-core.o cdns-mhdp8546-hdcp.o > cdns-mhdp8546-$(CONFIG_DRM_CDNS_MHDP8546_J721E) += cdns-mhdp8546-j721e.o > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c > index d0c65610ebb5..e2d75fc596f6 100644 > --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c > @@ -42,6 +42,7 @@ > #include <drm/drm_connector.h> > #include <drm/drm_crtc_helper.h> > #include <drm/drm_dp_helper.h> > +#include <drm/drm_hdcp.h> > #include <drm/drm_modeset_helper_vtables.h> > #include <drm/drm_print.h> > #include <drm/drm_probe_helper.h> > @@ -49,7 +50,7 @@ > #include <asm/unaligned.h> > > #include "cdns-mhdp8546-core.h" > - > +#include "cdns-mhdp8546-hdcp.h" > #include "cdns-mhdp8546-j721e.h" > > static int cdns_mhdp_mailbox_read(struct cdns_mhdp_device *mhdp) > @@ -1614,10 +1615,40 @@ enum drm_mode_status cdns_mhdp_mode_valid(struct drm_connector *conn, > return MODE_OK; > } > > +static int cdns_mhdp_connector_atomic_check(struct drm_connector *conn, > + struct drm_atomic_state *state) > +{ > + struct drm_connector_state *old_state, *new_state; > + struct drm_crtc_state *crtc_state; > + u64 old_cp, new_cp; > + > + old_state = drm_atomic_get_old_connector_state(state, conn); > + new_state = drm_atomic_get_new_connector_state(state, conn); > + old_cp = old_state->content_protection; > + new_cp = new_state->content_protection; > + > + if (!new_state->crtc) { > + if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED) > + new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; > + return 0; > + } > + > + if (old_cp == new_cp || > + (old_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED && > + new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)) > + return 0; > + > + crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc); > + crtc_state->mode_changed = true; > + > + return 0; > +} > + > static const struct drm_connector_helper_funcs cdns_mhdp_conn_helper_funcs = { > .detect_ctx = cdns_mhdp_connector_detect, > .get_modes = cdns_mhdp_get_modes, > .mode_valid = cdns_mhdp_mode_valid, > + .atomic_check = cdns_mhdp_connector_atomic_check, > }; > > static const struct drm_connector_funcs cdns_mhdp_conn_funcs = { > @@ -1662,7 +1693,7 @@ static int cdns_mhdp_connector_init(struct cdns_mhdp_device *mhdp) > return ret; > } > > - return 0; > + return drm_connector_attach_content_protection_property(conn, true); > } > > static int cdns_mhdp_attach(struct drm_bridge *bridge, > @@ -1956,6 +1987,13 @@ static void cdns_mhdp_atomic_enable(struct drm_bridge *bridge, > conn_state = drm_atomic_get_new_connector_state(state, connector); > if (WARN_ON(!conn_state)) > goto out; > + if (conn_state->content_protection == > + DRM_MODE_CONTENT_PROTECTION_DESIRED && > + mhdp->hw_state == MHDP_HW_READY) { > + mutex_unlock(&mhdp->link_mutex); > + cdns_mhdp_hdcp_enable(mhdp); > + mutex_lock(&mhdp->link_mutex); > + } > > crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); > if (WARN_ON(!crtc_state)) > @@ -2000,6 +2038,7 @@ static void cdns_mhdp_atomic_disable(struct drm_bridge *bridge, > > mutex_lock(&mhdp->link_mutex); > > + cdns_mhdp_hdcp_disable(mhdp); > mhdp->bridge_enabled = false; > cdns_mhdp_reg_read(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, &resp); > resp &= ~CDNS_DP_FRAMER_EN; > @@ -2288,7 +2327,6 @@ static irqreturn_t cdns_mhdp_irq_handler(int irq, void *data) > struct cdns_mhdp_device *mhdp = data; > u32 apb_stat, sw_ev0; > bool bridge_attached; > - int ret; > > apb_stat = readl(mhdp->regs + CDNS_APB_INT_STATUS); > if (!(apb_stat & CDNS_APB_INT_MASK_SW_EVENT_INT)) > @@ -2307,20 +2345,54 @@ static irqreturn_t cdns_mhdp_irq_handler(int irq, void *data) > spin_unlock(&mhdp->start_lock); > > if (bridge_attached && (sw_ev0 & CDNS_DPTX_HPD)) { > - ret = cdns_mhdp_update_link_status(mhdp); > - if (mhdp->connector.dev) { > - if (ret < 0) > - schedule_work(&mhdp->modeset_retry_work); > - else > - drm_kms_helper_hotplug_event(mhdp->bridge.dev); > - } else { > - drm_bridge_hpd_notify(&mhdp->bridge, cdns_mhdp_detect(mhdp)); > - } > + schedule_work(&mhdp->hpd_work); > + } > + > + if (sw_ev0 & ~CDNS_DPTX_HPD) { > + mhdp->sw_events |= (sw_ev0 & ~CDNS_DPTX_HPD); > + wake_up(&mhdp->sw_events_wq); > } > > return IRQ_HANDLED; > } > > +u32 cdns_mhdp_wait_for_sw_event(struct cdns_mhdp_device *mhdp, u32 event) > +{ > + u32 ret; > + > + ret = wait_event_timeout(mhdp->sw_events_wq, > + mhdp->sw_events & event, > + msecs_to_jiffies(500)); > + if (!ret) { > + dev_dbg(mhdp->dev, "SW event 0x%x timeout\n", event); > + goto sw_event_out; > + } > + > + ret = mhdp->sw_events; > + mhdp->sw_events &= ~event; > + > +sw_event_out: > + return ret; > +} > + > +static void cdns_mhdp_hpd_work(struct work_struct *work) > +{ > + struct cdns_mhdp_device *mhdp = container_of(work, > + struct cdns_mhdp_device, > + hpd_work); > + int ret; > + > + ret = cdns_mhdp_update_link_status(mhdp); > + if (mhdp->connector.dev) { > + if (ret < 0) > + schedule_work(&mhdp->modeset_retry_work); > + else > + drm_kms_helper_hotplug_event(mhdp->bridge.dev); > + } else { > + drm_bridge_hpd_notify(&mhdp->bridge, cdns_mhdp_detect(mhdp)); > + } > +} > + > static int cdns_mhdp_probe(struct platform_device *pdev) > { > struct device *dev = &pdev->dev; > @@ -2356,6 +2428,12 @@ static int cdns_mhdp_probe(struct platform_device *pdev) > return PTR_ERR(mhdp->regs); > } > > + mhdp->sapb_regs = devm_platform_ioremap_resource(pdev, 2); > + if (IS_ERR(mhdp->sapb_regs)) { > + dev_err(dev, "Failed to get SAPB memory resource\n"); > + return PTR_ERR(mhdp->sapb_regs); > + } > + > mhdp->phy = devm_of_phy_get_by_index(dev, pdev->dev.of_node, 0); > if (IS_ERR(mhdp->phy)) { > dev_err(dev, "no PHY configured\n"); > @@ -2430,13 +2508,16 @@ static int cdns_mhdp_probe(struct platform_device *pdev) > > /* Initialize the work for modeset in case of link train failure */ > INIT_WORK(&mhdp->modeset_retry_work, cdns_mhdp_modeset_retry_fn); > + INIT_WORK(&mhdp->hpd_work, cdns_mhdp_hpd_work); > > init_waitqueue_head(&mhdp->fw_load_wq); > + init_waitqueue_head(&mhdp->sw_events_wq); > > ret = cdns_mhdp_load_firmware(mhdp); > if (ret) > goto phy_exit; > > + cdns_mhdp_hdcp_init(mhdp, pdev->dev.of_node); > drm_bridge_add(&mhdp->bridge); > > return 0; > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h > index 5897a85e3159..0d652569e487 100644 > --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h > @@ -47,6 +47,10 @@ struct phy; > > #define CDNS_SW_EVENT0 0x00044 > #define CDNS_DPTX_HPD BIT(0) > +#define CDNS_HDCP_TX_STATUS BIT(4) > +#define CDNS_HDCP2_TX_IS_KM_STORED BIT(5) > +#define CDNS_HDCP2_TX_STORE_KM BIT(6) > +#define CDNS_HDCP_TX_IS_RCVR_ID_VALID BIT(7) > > #define CDNS_SW_EVENT1 0x00048 > #define CDNS_SW_EVENT2 0x0004c > @@ -339,8 +343,17 @@ struct cdns_mhdp_platform_info { > #define to_cdns_mhdp_bridge_state(s) \ > container_of(s, struct cdns_mhdp_bridge_state, base) > > +struct cdns_mhdp_hdcp { > + struct delayed_work check_work; > + struct work_struct prop_work; > + struct mutex mutex; /* mutex to protect hdcp.value */ > + u32 value; > + u8 config; > +}; > + > struct cdns_mhdp_device { > void __iomem *regs; > + void __iomem *sapb_regs; > void __iomem *j721e_regs; > > struct device *dev; > @@ -392,9 +405,17 @@ struct cdns_mhdp_device { > > /* Work struct to schedule a uevent on link train failure */ > struct work_struct modeset_retry_work; > + struct work_struct hpd_work; > + > + wait_queue_head_t sw_events_wq; > + u32 sw_events; > + > + struct cdns_mhdp_hdcp hdcp; > }; > > #define connector_to_mhdp(x) container_of(x, struct cdns_mhdp_device, connector) > #define bridge_to_mhdp(x) container_of(x, struct cdns_mhdp_device, bridge) > > +u32 cdns_mhdp_wait_for_sw_event(struct cdns_mhdp_device *mhdp, uint32_t event); > + > #endif > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c > new file mode 100644 > index 000000000000..c0ee8235ffa9 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c > @@ -0,0 +1,578 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Cadence MHDP8546 DP bridge driver. > + * > + * Copyright (C) 2020 Cadence Design Systems, Inc. > + * > + */ > + > +#include <linux/io.h> > +#include <linux/iopoll.h> > + > +#include <asm/unaligned.h> > + > +#include "cdns-mhdp8546-hdcp.h" > + > +static int cdns_mhdp_secure_mailbox_read(struct cdns_mhdp_device *mhdp) > +{ > + int ret, empty; > + > + WARN_ON(!mutex_is_locked(&mhdp->mbox_mutex)); > + > + ret = readx_poll_timeout(readl, mhdp->sapb_regs + CDNS_MAILBOX_EMPTY, > + empty, !empty, MAILBOX_RETRY_US, > + MAILBOX_TIMEOUT_US); > + if (ret < 0) > + return ret; > + > + return readl(mhdp->sapb_regs + CDNS_MAILBOX_RX_DATA) & 0xff; > +} > + > +static int cdns_mhdp_secure_mailbox_write(struct cdns_mhdp_device *mhdp, > + u8 val) > +{ > + int ret, full; > + > + WARN_ON(!mutex_is_locked(&mhdp->mbox_mutex)); > + > + ret = readx_poll_timeout(readl, mhdp->sapb_regs + CDNS_MAILBOX_FULL, > + full, !full, MAILBOX_RETRY_US, > + MAILBOX_TIMEOUT_US); > + if (ret < 0) > + return ret; > + > + writel(val, mhdp->sapb_regs + CDNS_MAILBOX_TX_DATA); > + > + return 0; > +} > + > +static int cdns_mhdp_secure_mailbox_recv_header(struct cdns_mhdp_device *mhdp, > + u8 module_id, > + u8 opcode, > + u16 req_size) > +{ > + u32 mbox_size, i; > + u8 header[4]; > + int ret; > + > + /* read the header of the message */ > + for (i = 0; i < sizeof(header); i++) { > + ret = cdns_mhdp_secure_mailbox_read(mhdp); > + if (ret < 0) > + return ret; > + > + header[i] = ret; > + } > + > + mbox_size = get_unaligned_be16(header + 2); > + > + if (opcode != header[0] || module_id != header[1] || > + (opcode != HDCP_TRAN_IS_REC_ID_VALID && req_size != mbox_size)) { > + for (i = 0; i < mbox_size; i++) > + if (cdns_mhdp_secure_mailbox_read(mhdp) < 0) > + break; > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int cdns_mhdp_secure_mailbox_recv_data(struct cdns_mhdp_device *mhdp, > + u8 *buff, u16 buff_size) > +{ > + int ret; > + u32 i; > + > + for (i = 0; i < buff_size; i++) { > + ret = cdns_mhdp_secure_mailbox_read(mhdp); > + if (ret < 0) > + return ret; > + > + buff[i] = ret; > + } > + > + return 0; > +} > + > +static int cdns_mhdp_secure_mailbox_send(struct cdns_mhdp_device *mhdp, > + u8 module_id, > + u8 opcode, > + u16 size, > + u8 *message) > +{ > + u8 header[4]; > + int ret; > + u32 i; > + > + header[0] = opcode; > + header[1] = module_id; > + put_unaligned_be16(size, header + 2); > + > + for (i = 0; i < sizeof(header); i++) { > + ret = cdns_mhdp_secure_mailbox_write(mhdp, header[i]); > + if (ret) > + return ret; > + } > + > + for (i = 0; i < size; i++) { > + ret = cdns_mhdp_secure_mailbox_write(mhdp, message[i]); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > +static int cdns_mhdp_hdcp_get_status(struct cdns_mhdp_device *mhdp, > + u16 *hdcp_port_status) > +{ > + u8 hdcp_status[HDCP_STATUS_SIZE]; > + int ret; > + > + mutex_lock(&mhdp->mbox_mutex); > + ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, > + HDCP_TRAN_STATUS_CHANGE, 0, NULL); > + if (ret) > + goto err_get_hdcp_status; > + > + ret = cdns_mhdp_secure_mailbox_recv_header(mhdp, MB_MODULE_ID_HDCP_TX, > + HDCP_TRAN_STATUS_CHANGE, > + sizeof(hdcp_status)); > + if (ret) > + goto err_get_hdcp_status; > + > + ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, hdcp_status, > + sizeof(hdcp_status)); > + if (ret) > + goto err_get_hdcp_status; > + > + *hdcp_port_status = ((u16)(hdcp_status[0] << 8) | hdcp_status[1]); > + > +err_get_hdcp_status: > + mutex_unlock(&mhdp->mbox_mutex); > + > + return ret; > +} > + > +static u8 cdns_mhdp_hdcp_handle_status(struct cdns_mhdp_device *mhdp, > + u16 status) > +{ > + u8 err = GET_HDCP_PORT_STS_LAST_ERR(status); > + > + if (err) > + dev_dbg(mhdp->dev, "HDCP Error = %d", err); > + > + return err; > +} > + > +static int cdns_mhdp_hdcp_rx_id_valid_response(struct cdns_mhdp_device *mhdp, > + u8 valid) > +{ > + int ret; > + > + mutex_lock(&mhdp->mbox_mutex); > + ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, > + HDCP_TRAN_RESPOND_RECEIVER_ID_VALID, > + 1, &valid); > + mutex_unlock(&mhdp->mbox_mutex); > + > + return ret; > +} > + > +static int cdns_mhdp_hdcp_rx_id_valid(struct cdns_mhdp_device *mhdp, > + u8 *recv_num, u8 *hdcp_rx_id) > +{ > + u8 rec_id_hdr[2]; > + u8 status; > + int ret; > + > + mutex_lock(&mhdp->mbox_mutex); > + ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, > + HDCP_TRAN_IS_REC_ID_VALID, 0, NULL); > + if (ret) > + goto err_rx_id_valid; > + > + ret = cdns_mhdp_secure_mailbox_recv_header(mhdp, MB_MODULE_ID_HDCP_TX, > + HDCP_TRAN_IS_REC_ID_VALID, > + sizeof(status)); > + if (ret) > + goto err_rx_id_valid; > + > + ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, rec_id_hdr, 2); > + if (ret) > + goto err_rx_id_valid; > + > + *recv_num = rec_id_hdr[0]; > + > + ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, hdcp_rx_id, 5 * *recv_num); > + > +err_rx_id_valid: > + mutex_unlock(&mhdp->mbox_mutex); > + > + return ret; > +} > + > +static int cdns_mhdp_hdcp_km_stored_resp(struct cdns_mhdp_device *mhdp, > + u32 size, u8 *km) > +{ > + int ret; > + > + mutex_lock(&mhdp->mbox_mutex); > + ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, > + HDCP2X_TX_RESPOND_KM, size, km); > + mutex_unlock(&mhdp->mbox_mutex); > + > + return ret; > +} > + > +static int cdns_mhdp_hdcp_tx_is_km_stored(struct cdns_mhdp_device *mhdp, > + u8 *resp, u32 size) > +{ > + int ret; > + > + mutex_lock(&mhdp->mbox_mutex); > + ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, > + HDCP2X_TX_IS_KM_STORED, 0, NULL); > + if (ret) > + goto err_is_km_stored; > + > + ret = cdns_mhdp_secure_mailbox_recv_header(mhdp, MB_MODULE_ID_HDCP_TX, > + HDCP2X_TX_IS_KM_STORED, > + size); > + if (ret) > + goto err_is_km_stored; > + > + ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, resp, size); > +err_is_km_stored: > + mutex_unlock(&mhdp->mbox_mutex); > + > + return ret; > +} > + > +static int cdns_mhdp_hdcp_tx_config(struct cdns_mhdp_device *mhdp, > + u8 hdcp_cfg) > +{ > + int ret; > + > + mutex_lock(&mhdp->mbox_mutex); > + ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, > + HDCP_TRAN_CONFIGURATION, 1, &hdcp_cfg); > + mutex_unlock(&mhdp->mbox_mutex); > + > + return ret; > +} > + > +static int cdns_mhdp_hdcp_set_config(struct cdns_mhdp_device *mhdp, > + u8 hdcp_config, bool enable) > +{ > + u16 hdcp_port_status; > + u32 ret_event; > + u8 hdcp_cfg; > + int ret; > + > + hdcp_cfg = hdcp_config | (enable ? 0x04 : 0) | > + (HDCP_CONTENT_TYPE_0 << 3); > + cdns_mhdp_hdcp_tx_config(mhdp, hdcp_cfg); > + ret_event = cdns_mhdp_wait_for_sw_event(mhdp, CDNS_HDCP_TX_STATUS); > + if (!ret_event) > + return -1; > + > + ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status); > + if (ret || cdns_mhdp_hdcp_handle_status(mhdp, hdcp_port_status)) > + return -1; > + > + return 0; > +} > + > +static int cdns_mhdp_hdcp_auth_check(struct cdns_mhdp_device *mhdp) > +{ > + u16 hdcp_port_status; > + u32 ret_event; > + int ret; > + > + ret_event = cdns_mhdp_wait_for_sw_event(mhdp, CDNS_HDCP_TX_STATUS); > + if (!ret_event) > + return -1; > + > + ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status); > + if (ret || cdns_mhdp_hdcp_handle_status(mhdp, hdcp_port_status)) > + return -1; > + > + if (hdcp_port_status & 1) { > + dev_dbg(mhdp->dev, "Authentication completed successfully!\n"); > + return 0; > + } > + > + dev_dbg(mhdp->dev, "Authentication failed\n"); > + > + return -1; > +} > + > +static int cdns_mhdp_hdcp_check_receviers(struct cdns_mhdp_device *mhdp) > +{ > + u8 hdcp_rec_id[HDCP_MAX_RECEIVERS][HDCP_RECEIVER_ID_SIZE_BYTES]; > + u8 hdcp_num_rec; > + u32 ret_event; > + > + ret_event = cdns_mhdp_wait_for_sw_event(mhdp, > + CDNS_HDCP_TX_IS_RCVR_ID_VALID); > + if (!ret_event) > + return -1; > + > + hdcp_num_rec = 0; > + memset(&hdcp_rec_id, 0, sizeof(hdcp_rec_id)); > + cdns_mhdp_hdcp_rx_id_valid(mhdp, &hdcp_num_rec, (u8 *)hdcp_rec_id); > + cdns_mhdp_hdcp_rx_id_valid_response(mhdp, 1); > + > + return 0; > +} > + > +static int cdns_mhdp_hdcp_auth_22(struct cdns_mhdp_device *mhdp) > +{ > + u8 resp[HDCP_STATUS_SIZE]; > + u16 hdcp_port_status; > + u32 ret_event; > + int ret; > + > + dev_dbg(mhdp->dev, "HDCP: Start 2.2 Authentication\n"); > + ret_event = cdns_mhdp_wait_for_sw_event(mhdp, > + CDNS_HDCP2_TX_IS_KM_STORED); > + if (!ret_event) > + return -1; > + > + if (ret_event & CDNS_HDCP_TX_STATUS) { > + mhdp->sw_events &= ~CDNS_HDCP_TX_STATUS; > + ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status); > + if (ret || cdns_mhdp_hdcp_handle_status(mhdp, hdcp_port_status)) > + return -1; > + } > + > + cdns_mhdp_hdcp_tx_is_km_stored(mhdp, resp, sizeof(resp)); > + cdns_mhdp_hdcp_km_stored_resp(mhdp, 0, NULL); > + > + if (cdns_mhdp_hdcp_check_receviers(mhdp)) > + return -1; > + > + return 0; > +} > + > +static inline int cdns_mhdp_hdcp_auth_14(struct cdns_mhdp_device *mhdp) > +{ > + dev_dbg(mhdp->dev, "HDCP: Starting 1.4 Authentication\n"); > + return cdns_mhdp_hdcp_check_receviers(mhdp); > +} > + > +static int cdns_mhdp_hdcp_auth(struct cdns_mhdp_device *mhdp, > + u8 hdcp_config) > +{ > + int ret; > + > + ret = cdns_mhdp_hdcp_set_config(mhdp, hdcp_config, true); > + if (ret) > + goto auth_failed; > + > + if (hdcp_config == HDCP_TX_1) > + ret = cdns_mhdp_hdcp_auth_14(mhdp); > + else > + ret = cdns_mhdp_hdcp_auth_22(mhdp); > + > + if (ret) > + goto auth_failed; > + > + ret = cdns_mhdp_hdcp_auth_check(mhdp); > + if (ret) > + ret = cdns_mhdp_hdcp_auth_check(mhdp); > + > +auth_failed: > + return ret; > +} > + > +static int _cdns_mhdp_hdcp_disable(struct cdns_mhdp_device *mhdp) > +{ > + int ret; > + > + dev_dbg(mhdp->dev, "[%s:%d] HDCP is being disabled...\n", > + mhdp->connector.name, mhdp->connector.base.id); > + > + ret = cdns_mhdp_hdcp_set_config(mhdp, 0, false); > + > + return ret; > +} > + > +static int _cdns_mhdp_hdcp_enable(struct cdns_mhdp_device *mhdp) > +{ > + int ret, tries = 3; > + u32 i; > + > + for (i = 0; i < tries; i++) { > + if (mhdp->hdcp.config & HDCP_CONFIG_2_2) { > + ret = cdns_mhdp_hdcp_auth(mhdp, HDCP_TX_2); > + if (!ret) > + return 0; > + } > + _cdns_mhdp_hdcp_disable(mhdp); > + > + if (mhdp->hdcp.config & HDCP_CONFIG_1_4) { > + ret = cdns_mhdp_hdcp_auth(mhdp, HDCP_TX_1); > + if (!ret) > + return 0; > + } > + _cdns_mhdp_hdcp_disable(mhdp); > + } > + > + dev_err(mhdp->dev, "HDCP authentication failed (%d tries/%d)\n", > + tries, ret); > + > + return ret; > +} > + > +static int cdns_mhdp_hdcp_check_link(struct cdns_mhdp_device *mhdp) > +{ > + u16 hdcp_port_status; > + int ret = 0; > + > + mutex_lock(&mhdp->hdcp.mutex); > + if (mhdp->hdcp.value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) > + goto out; > + > + ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status); > + if (!ret && hdcp_port_status & HDCP_PORT_STS_AUTH) > + goto out; > + > + dev_err(mhdp->dev, > + "[%s:%d] HDCP link failed, retrying authentication\n", > + mhdp->connector.name, mhdp->connector.base.id); > + > + ret = _cdns_mhdp_hdcp_disable(mhdp); > + if (ret) { > + mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_DESIRED; > + schedule_work(&mhdp->hdcp.prop_work); > + goto out; > + } > + > + ret = _cdns_mhdp_hdcp_enable(mhdp); > + if (ret) { > + mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_DESIRED; > + schedule_work(&mhdp->hdcp.prop_work); > + } > +out: > + mutex_unlock(&mhdp->hdcp.mutex); > + return ret; > +} > + > +static void cdns_mhdp_hdcp_check_work(struct work_struct *work) > +{ > + struct delayed_work *d_work = to_delayed_work(work); > + struct cdns_mhdp_hdcp *hdcp = container_of(d_work, > + struct cdns_mhdp_hdcp, > + check_work); > + struct cdns_mhdp_device *mhdp = container_of(hdcp, > + struct cdns_mhdp_device, > + hdcp); > + > + if (!cdns_mhdp_hdcp_check_link(mhdp)) > + schedule_delayed_work(&hdcp->check_work, > + DRM_HDCP_CHECK_PERIOD_MS); > +} > + > +static void cdns_mhdp_hdcp_prop_work(struct work_struct *work) > +{ > + struct cdns_mhdp_hdcp *hdcp = container_of(work, > + struct cdns_mhdp_hdcp, > + prop_work); > + struct cdns_mhdp_device *mhdp = container_of(hdcp, > + struct cdns_mhdp_device, > + hdcp); > + struct drm_device *dev = mhdp->connector.dev; > + struct drm_connector_state *state; > + > + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); > + mutex_lock(&mhdp->hdcp.mutex); > + if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { > + state = mhdp->connector.state; > + state->content_protection = mhdp->hdcp.value; > + } > + mutex_unlock(&mhdp->hdcp.mutex); > + drm_modeset_unlock(&dev->mode_config.connection_mutex); > +} > + > +int cdns_mhdp_hdcp_set_lc(struct cdns_mhdp_device *mhdp, u8 *val) > +{ > + int ret; > + > + mutex_lock(&mhdp->mbox_mutex); > + ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_GENERAL, > + HDCP_GENERAL_SET_LC_128, > + 16, val); > + mutex_unlock(&mhdp->mbox_mutex); > + > + return ret; > +} > + > +int > +cdns_mhdp_hdcp_set_public_key_params(struct cdns_mhdp_device *mhdp, > + struct cdns_hdcp_tx_public_key_param *val) > +{ > + int ret; > + > + mutex_lock(&mhdp->mbox_mutex); > + ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX, > + HDCP2X_TX_SET_PUBLIC_KEY_PARAMS, > + sizeof(*val), (u8 *)val); > + mutex_unlock(&mhdp->mbox_mutex); > + > + return ret; > +} > + > +int cdns_mhdp_hdcp_enable(struct cdns_mhdp_device *mhdp) > +{ > + int ret; > + > + mutex_lock(&mhdp->hdcp.mutex); > + ret = _cdns_mhdp_hdcp_enable(mhdp); > + if (ret) > + goto out; > + > + mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_ENABLED; > + schedule_work(&mhdp->hdcp.prop_work); > + schedule_delayed_work(&mhdp->hdcp.check_work, > + DRM_HDCP_CHECK_PERIOD_MS); > +out: > + mutex_unlock(&mhdp->hdcp.mutex); > + return ret; > +} > + > +int cdns_mhdp_hdcp_disable(struct cdns_mhdp_device *mhdp) > +{ > + int ret = 0; > + > + mutex_lock(&mhdp->hdcp.mutex); > + if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { > + mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; > + schedule_work(&mhdp->hdcp.prop_work); > + ret = _cdns_mhdp_hdcp_disable(mhdp); > + } > + mutex_unlock(&mhdp->hdcp.mutex); > + cancel_delayed_work_sync(&mhdp->hdcp.check_work); > + > + return ret; > +} > + > +int cdns_mhdp_hdcp_init(struct cdns_mhdp_device *mhdp, > + struct device_node *of_node) > +{ > + u32 config; > + int ret; > + > + ret = of_property_read_u32(of_node, "hdcp-config", &config); > + if (ret) > + mhdp->hdcp.config = HDCP_CONFIG_ALL; > + else > + mhdp->hdcp.config = config; > + > + INIT_DELAYED_WORK(&mhdp->hdcp.check_work, cdns_mhdp_hdcp_check_work); > + INIT_WORK(&mhdp->hdcp.prop_work, cdns_mhdp_hdcp_prop_work); > + mutex_init(&mhdp->hdcp.mutex); > + > + return 0; > +} > diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.h > new file mode 100644 > index 000000000000..fc694a9e4c04 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.h > @@ -0,0 +1,89 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Cadence MHDP8546 DP bridge driver. > + * > + * Copyright (C) 2020 Cadence Design Systems, Inc. > + * > + */ > + > +#ifndef CDNS_MHDP8546_HDCP_H > +#define CDNS_MHDP8546_HDCP_H > + > +#include "cdns-mhdp8546-core.h" > + > +#define HDCP_MAX_RECEIVERS 32 > +#define HDCP_RECEIVER_ID_SIZE_BYTES 5 > +#define HDCP_STATUS_SIZE 0x5 > +#define HDCP_PORT_STS_AUTH 0x1 > +#define HDCP_PORT_STS_LAST_ERR_SHIFT 0x5 > +#define HDCP_PORT_STS_LAST_ERR_MASK (0x0F << 5) > +#define GET_HDCP_PORT_STS_LAST_ERR(__sts__) \ > + (((__sts__) & HDCP_PORT_STS_LAST_ERR_MASK) >> \ > + HDCP_PORT_STS_LAST_ERR_SHIFT) > + > +#define HDCP_CONFIG_1_4 BIT(0) /* use HDCP 1.4 only */ > +#define HDCP_CONFIG_2_2 BIT(1) /* use HDCP 2.2 only */ > +/* use All HDCP versions */ > +#define HDCP_CONFIG_ALL (BIT(0) | BIT(1)) > +#define HDCP_CONFIG_NONE 0 > + > +enum { > + HDCP_GENERAL_SET_LC_128, > + HDCP_SET_SEED, > +}; > + > +enum { > + HDCP_TRAN_CONFIGURATION, > + HDCP2X_TX_SET_PUBLIC_KEY_PARAMS, > + HDCP2X_TX_SET_DEBUG_RANDOM_NUMBERS, > + HDCP2X_TX_RESPOND_KM, > + HDCP1_TX_SEND_KEYS, > + HDCP1_TX_SEND_RANDOM_AN, > + HDCP_TRAN_STATUS_CHANGE, > + HDCP2X_TX_IS_KM_STORED, > + HDCP2X_TX_STORE_KM, > + HDCP_TRAN_IS_REC_ID_VALID, > + HDCP_TRAN_RESPOND_RECEIVER_ID_VALID, > + HDCP_TRAN_TEST_KEYS, > + HDCP2X_TX_SET_KM_KEY_PARAMS, > + HDCP_NUM_OF_SUPPORTED_MESSAGES > +}; > + > +enum { > + HDCP_CONTENT_TYPE_0, > + HDCP_CONTENT_TYPE_1, > +}; > + > +#define DRM_HDCP_CHECK_PERIOD_MS (128 * 16) > + > +#define HDCP_PAIRING_R_ID 5 > +#define HDCP_PAIRING_M_LEN 16 > +#define HDCP_KM_LEN 16 > +#define HDCP_PAIRING_M_EKH 16 > + > +struct cdns_hdcp_pairing_data { > + u8 receiver_id[HDCP_PAIRING_R_ID]; > + u8 m[HDCP_PAIRING_M_LEN]; > + u8 km[HDCP_KM_LEN]; > + u8 ekh[HDCP_PAIRING_M_EKH]; > +}; > + > +enum { > + HDCP_TX_2, > + HDCP_TX_1, > + HDCP_TX_BOTH, > +}; > + > +#define DLP_MODULUS_N 384 > +#define DLP_E 3 > + > +struct cdns_hdcp_tx_public_key_param { > + u8 N[DLP_MODULUS_N]; > + u8 E[DLP_E]; > +}; > + > +int cdns_mhdp_hdcp_enable(struct cdns_mhdp_device *mhdp); > +int cdns_mhdp_hdcp_disable(struct cdns_mhdp_device *mhdp); > +int cdns_mhdp_hdcp_init(struct cdns_mhdp_device *mhdp, struct device_node *of_node); > + > +#endif > -- > 2.25.1 > _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel