#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>
 #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)
        return MODE_OK;
 }
 
+static int cdns_mhdp_connector_atomic_check(struct drm_connector *conn,
+                                           struct drm_atomic_state *state)
+{
+       struct cdns_mhdp_device *mhdp = connector_to_mhdp(conn);
+       struct drm_connector_state *old_state, *new_state;
+       struct drm_crtc_state *crtc_state;
+       u64 old_cp, new_cp;
+
+       if (!mhdp->hdcp_supported)
+               return 0;
+
+       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 (old_state->hdcp_content_type != new_state->hdcp_content_type &&
+           new_cp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+               new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+               goto mode_changed;
+       }
+
+       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;
+
+mode_changed:
+       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 = {
                return ret;
        }
 
-       return 0;
+       if (mhdp->hdcp_supported)
+               ret = drm_connector_attach_content_protection_property(conn, true);
+
+       return ret;
 }
 
 static int cdns_mhdp_attach(struct drm_bridge *bridge,
        if (WARN_ON(!conn_state))
                goto out;
 
+       if (mhdp->hdcp_supported &&
+           mhdp->hw_state == MHDP_HW_READY &&
+           conn_state->content_protection ==
+           DRM_MODE_CONTENT_PROTECTION_DESIRED) {
+               mutex_unlock(&mhdp->link_mutex);
+               cdns_mhdp_hdcp_enable(mhdp, conn_state->hdcp_content_type);
+               mutex_lock(&mhdp->link_mutex);
+       }
+
        crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
        if (WARN_ON(!crtc_state))
                goto out;
 
        mutex_lock(&mhdp->link_mutex);
 
+       if (mhdp->hdcp_supported)
+               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;
        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))
        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;
                return PTR_ERR(mhdp->regs);
        }
 
+       mhdp->sapb_regs = devm_platform_ioremap_resource_byname(pdev, "mhdptx-sapb");
+       if (IS_ERR(mhdp->sapb_regs)) {
+               mhdp->hdcp_supported = false;
+               dev_warn(dev,
+                        "Failed to get SAPB memory resource, HDCP not supported\n");
+       } else {
+               mhdp->hdcp_supported = true;
+       }
+
        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");
 
        /* 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;
 
+       if (mhdp->hdcp_supported)
+               cdns_mhdp_hdcp_init(mhdp);
+
        drm_bridge_add(&mhdp->bridge);
 
        return 0;
 
--- /dev/null
+// 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 <drm/drm_hdcp.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, u8 content_type)
+{
+       int ret, tries = 3;
+       u32 i;
+
+       for (i = 0; i < tries; i++) {
+               if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0 ||
+                   content_type == DRM_MODE_HDCP_CONTENT_TYPE1) {
+                       ret = cdns_mhdp_hdcp_auth(mhdp, HDCP_TX_2);
+                       if (!ret)
+                               return 0;
+                       _cdns_mhdp_hdcp_disable(mhdp);
+               }
+
+               if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) {
+                       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, mhdp->hdcp.hdcp_content_type);
+       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_param(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, u8 content_type)
+{
+       int ret;
+
+       mutex_lock(&mhdp->hdcp.mutex);
+       ret = _cdns_mhdp_hdcp_enable(mhdp, content_type);
+       if (ret)
+               goto out;
+
+       mhdp->hdcp.hdcp_content_type = content_type;
+       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;
+}
+
+void cdns_mhdp_hdcp_init(struct cdns_mhdp_device *mhdp)
+{
+       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);
+}