Unverified Commit 6a3608ea authored by Parshuram Thombare's avatar Parshuram Thombare Committed by Robert Foss
Browse files

drm: bridge: cdns-mhdp8546: Enable HDCP

parent 7169d082
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
# 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
+116 −12
Original line number Diff line number Diff line
@@ -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,51 @@ 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 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 = {
@@ -1662,7 +1704,10 @@ static int cdns_mhdp_connector_init(struct cdns_mhdp_device *mhdp)
		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,
@@ -1957,6 +2002,15 @@ static void cdns_mhdp_atomic_enable(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;
@@ -2000,6 +2054,9 @@ static void cdns_mhdp_atomic_disable(struct drm_bridge *bridge,

	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;
@@ -2288,7 +2345,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,6 +2363,43 @@ static irqreturn_t cdns_mhdp_irq_handler(int irq, void *data)
	spin_unlock(&mhdp->start_lock);

	if (bridge_attached && (sw_ev0 & CDNS_DPTX_HPD)) {
		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)
@@ -2318,9 +2411,6 @@ static irqreturn_t cdns_mhdp_irq_handler(int irq, void *data)
	}
}

	return IRQ_HANDLED;
}

static int cdns_mhdp_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
@@ -2356,6 +2446,15 @@ static int cdns_mhdp_probe(struct platform_device *pdev)
		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");
@@ -2430,13 +2529,18 @@ 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;

	if (mhdp->hdcp_supported)
		cdns_mhdp_hdcp_init(mhdp);

	drm_bridge_add(&mhdp->bridge);

	return 0;
+22 −0
Original line number Diff line number Diff line
@@ -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 hdcp_content_type;
};

struct cdns_mhdp_device {
	void __iomem *regs;
	void __iomem *sapb_regs;
	void __iomem *j721e_regs;

	struct device *dev;
@@ -392,9 +405,18 @@ 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;
	bool hdcp_supported;
};

#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
+570 −0
Original line number Diff line number Diff line
// 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);
}
+92 −0
Original line number Diff line number Diff line
/* 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_set_public_key_param(struct cdns_mhdp_device *mhdp,
					struct cdns_hdcp_tx_public_key_param *val);
int cdns_mhdp_hdcp_set_lc(struct cdns_mhdp_device *mhdp, u8 *val);
int cdns_mhdp_hdcp_enable(struct cdns_mhdp_device *mhdp, u8 content_type);
int cdns_mhdp_hdcp_disable(struct cdns_mhdp_device *mhdp);
void cdns_mhdp_hdcp_init(struct cdns_mhdp_device *mhdp);

#endif