Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 39547368 authored by Tatenda Chipeperekwa's avatar Tatenda Chipeperekwa
Browse files

drm/msm/dp: add support for HDCP 2.2



Add a module to control the interactions between the Receiver,
Transmitter, and HDCP enforcing hardware layer as part of the
HDCP 2.2 protocol. This module is used by DisplayPort interface
driver to initiate HDCP 2.2 with a sink and provide a secure
link for content.

This implementation is based on a snapshot of the DisplayPort
HDCP 2.2 module as per this commit Ief0a8ce77a932 ("msm: mdss:
hdcp2p2: fix sysfs node name") on kernel-4.4.

CRs-Fixed: 2057884
Change-Id: Ic1f493c78afc9275ad62f9494bb5b3f26d1dab70
Signed-off-by: default avatarTatenda Chipeperekwa <tatendac@codeaurora.org>
parent d1ae6b16
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ msm_drm-y := \
	dp/dp_ctrl.o \
	dp/dp_display.o \
	dp/dp_drm.o \
	dp/dp_hdcp2p2.o \
	sde/sde_crtc.o \
	sde/sde_encoder.o \
	sde/sde_encoder_phys_vid.o \
+17 −0
Original line number Diff line number Diff line
@@ -424,6 +424,22 @@ static void dp_catalog_aux_get_irq(struct dp_catalog_aux *aux, bool cmd_busy)
}

/* controller related catalog functions */
static u32 dp_catalog_ctrl_read_hdcp_status(struct dp_catalog_ctrl *ctrl)
{
	struct dp_catalog_private *catalog;
	void __iomem *base;

	if (!ctrl) {
		pr_err("invalid input\n");
		return -EINVAL;
	}

	dp_catalog_get_priv(ctrl);
	base = catalog->io->ctrl_io.base;

	return dp_read(base + DP_HDCP_STATUS);
}

static void dp_catalog_ctrl_update_transfer_unit(struct dp_catalog_ctrl *ctrl)
{
	struct dp_catalog_private *catalog;
@@ -917,6 +933,7 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io)
		.update_vx_px   = dp_catalog_ctrl_update_vx_px,
		.get_interrupt  = dp_catalog_ctrl_get_interrupt,
		.update_transfer_unit = dp_catalog_ctrl_update_transfer_unit,
		.read_hdcp_status     = dp_catalog_ctrl_read_hdcp_status,
	};
	struct dp_catalog_audio audio = {
		.acr_ctrl      = dp_catalog_audio_acr_ctrl,
+1 −0
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ struct dp_catalog_ctrl {
				u8 p_level);
	void (*get_interrupt)(struct dp_catalog_ctrl *ctrl);
	void (*update_transfer_unit)(struct dp_catalog_ctrl *ctrl);
	u32 (*read_hdcp_status)(struct dp_catalog_ctrl *ctrl);
};

struct dp_catalog_audio {
+254 −0
Original line number Diff line number Diff line
@@ -31,9 +31,25 @@
#include "dp_panel.h"
#include "dp_ctrl.h"
#include "dp_display.h"
#include "sde_hdcp.h"

static struct dp_display *g_dp_display;

struct dp_hdcp {
	void *data;
	struct sde_hdcp_ops *ops;

	void *hdcp1;
	void *hdcp2;

	int enc_lvl;

	bool auth_state;
	bool hdcp1_present;
	bool hdcp2_present;
	bool feature_enabled;
};

struct dp_display_private {
	char *name;
	int irq;
@@ -55,10 +71,16 @@ struct dp_display_private {
	struct dp_link    *link;
	struct dp_panel   *panel;
	struct dp_ctrl    *ctrl;
	struct dp_hdcp hdcp;

	struct dp_usbpd_cb usbpd_cb;
	struct dp_display_mode mode;
	struct dp_display dp_display;

	struct workqueue_struct *hdcp_workqueue;
	struct delayed_work hdcp_cb_work;
	struct mutex hdcp_mutex;
	int hdcp_status;
};

static const struct of_device_id dp_dt_match[] = {
@@ -66,6 +88,13 @@ static const struct of_device_id dp_dt_match[] = {
	{}
};

static inline bool dp_display_is_hdcp_enabled(struct dp_display_private *dp)
{
	return dp->hdcp.feature_enabled &&
		(dp->hdcp.hdcp1_present || dp->hdcp.hdcp2_present) &&
		dp->hdcp.ops;
}

static irqreturn_t dp_display_irq(int irq, void *dev_id)
{
	struct dp_display_private *dp = dev_id;
@@ -81,6 +110,12 @@ static irqreturn_t dp_display_irq(int irq, void *dev_id)
	/* DP aux isr */
	dp->aux->isr(dp->aux);

	/* HDCP isr */
	if (dp_display_is_hdcp_enabled(dp) && dp->hdcp.ops->isr) {
		if (dp->hdcp.ops->isr(dp->hdcp.data))
			pr_err("dp_hdcp_isr failed\n");
	}

	return IRQ_HANDLED;
}

@@ -158,6 +193,189 @@ static int dp_display_debugfs_deinit(struct dp_display_private *dp)
	return 0;
}

static void dp_display_hdcp_cb_work(struct work_struct *work)
{
	struct dp_display_private *dp;
	struct delayed_work *dw = to_delayed_work(work);
	struct sde_hdcp_ops *ops;
	int rc = 0;
	u32 hdcp_auth_state;

	dp = container_of(dw, struct dp_display_private, hdcp_cb_work);

	rc = dp->catalog->ctrl.read_hdcp_status(&dp->catalog->ctrl);
	if (rc >= 0) {
		hdcp_auth_state = (rc >> 20) & 0x3;
		pr_debug("hdcp auth state %d\n", hdcp_auth_state);
	}

	ops = dp->hdcp.ops;

	switch (dp->hdcp_status) {
	case HDCP_STATE_AUTHENTICATING:
		pr_debug("start authenticaton\n");

		if (dp->hdcp.ops && dp->hdcp.ops->authenticate)
			rc = dp->hdcp.ops->authenticate(dp->hdcp.data);

		break;
	case HDCP_STATE_AUTHENTICATED:
		pr_debug("hdcp authenticated\n");
		dp->hdcp.auth_state = true;
		break;
	case HDCP_STATE_AUTH_FAIL:
		dp->hdcp.auth_state = false;

		if (dp->power_on) {
			pr_debug("Reauthenticating\n");
			if (ops && ops->reauthenticate) {
				rc = ops->reauthenticate(dp->hdcp.data);
				if (rc)
					pr_err("reauth failed rc=%d\n", rc);
			}
		} else {
			pr_debug("not reauthenticating, cable disconnected\n");
		}

		break;
	default:
		break;
	}
}

static void dp_display_notify_hdcp_status_cb(void *ptr,
		enum sde_hdcp_states status)
{
	struct dp_display_private *dp = ptr;

	if (!dp) {
		pr_err("invalid input\n");
		return;
	}

	dp->hdcp_status = status;

	if (dp->dp_display.is_connected)
		queue_delayed_work(dp->hdcp_workqueue, &dp->hdcp_cb_work, HZ/4);
}

static int dp_display_create_hdcp_workqueue(struct dp_display_private *dp)
{
	dp->hdcp_workqueue = create_workqueue("sdm_dp_hdcp");
	if (IS_ERR_OR_NULL(dp->hdcp_workqueue)) {
		pr_err("Error creating hdcp_workqueue\n");
		return -EPERM;
	}

	INIT_DELAYED_WORK(&dp->hdcp_cb_work, dp_display_hdcp_cb_work);

	return 0;
}

static void dp_display_destroy_hdcp_workqueue(struct dp_display_private *dp)
{
	if (dp->hdcp_workqueue)
		destroy_workqueue(dp->hdcp_workqueue);
}

static void dp_display_update_hdcp_info(struct dp_display_private *dp)
{
	void *fd = NULL;
	struct sde_hdcp_ops *ops = NULL;

	if (!dp) {
		pr_err("invalid input\n");
		return;
	}

	if (!dp->hdcp.feature_enabled) {
		pr_debug("feature not enabled\n");
		return;
	}

	fd = dp->hdcp.hdcp2;
	if (fd)
		ops = sde_dp_hdcp2p2_start(fd);

	if (ops && ops->feature_supported)
		dp->hdcp.hdcp2_present = ops->feature_supported(fd);
	else
		dp->hdcp.hdcp2_present = false;

	if (dp->hdcp.hdcp2_present || dp->hdcp.hdcp1_present) {
		dp->hdcp.data = fd;
		dp->hdcp.ops = ops;
	} else {
		dp->hdcp.data = NULL;
		dp->hdcp.ops = NULL;
	}
}

static void dp_display_deinitialize_hdcp(struct dp_display_private *dp)
{
	if (!dp) {
		pr_err("invalid input\n");
		return;
	}

	sde_dp_hdcp2p2_deinit(dp->hdcp.data);
	dp_display_destroy_hdcp_workqueue(dp);
	if (&dp->hdcp_mutex)
		mutex_destroy(&dp->hdcp_mutex);
}

static int dp_display_initialize_hdcp(struct dp_display_private *dp)
{
	struct sde_hdcp_init_data hdcp_init_data;
	struct resource *res;
	int rc = 0;

	if (!dp) {
		pr_err("invalid input\n");
		return -EINVAL;
	}

	mutex_init(&dp->hdcp_mutex);

	rc = dp_display_create_hdcp_workqueue(dp);
	if (rc) {
		pr_err("Failed to create HDCP workqueue\n");
		goto error;
	}

	res = platform_get_resource_byname(dp->pdev,
		IORESOURCE_MEM, "dp_ctrl");
	if (!res) {
		pr_err("Error getting dp ctrl resource\n");
		rc = -EINVAL;
		goto error;
	}

	hdcp_init_data.phy_addr      = res->start;
	hdcp_init_data.client_id     = HDCP_CLIENT_DP;
	hdcp_init_data.drm_aux       = dp->aux->drm_aux;
	hdcp_init_data.cb_data       = (void *)dp;
	hdcp_init_data.workq         = dp->hdcp_workqueue;
	hdcp_init_data.mutex         = &dp->hdcp_mutex;
	hdcp_init_data.sec_access    = true;
	hdcp_init_data.notify_status = dp_display_notify_hdcp_status_cb;
	hdcp_init_data.core_io       = &dp->parser->io.ctrl_io;
	hdcp_init_data.qfprom_io     = &dp->parser->io.qfprom_io;
	hdcp_init_data.hdcp_io       = &dp->parser->io.hdcp_io;
	hdcp_init_data.revision      = &dp->panel->link_info.revision;

	dp->hdcp.hdcp2 = sde_dp_hdcp2p2_init(&hdcp_init_data);
	if (!IS_ERR_OR_NULL(dp->hdcp.hdcp2))
		pr_err("HDCP 2.2 initialized\n");

	dp->hdcp.feature_enabled = true;

	return 0;
error:
	dp_display_deinitialize_hdcp(dp);
	return rc;
}

static int dp_display_bind(struct device *dev, struct device *master,
		void *data)
{
@@ -215,6 +433,12 @@ static int dp_display_bind(struct device *dev, struct device *master,
		pr_err("Power client create failed\n");
		goto end;
	}

	rc = dp_display_initialize_hdcp(dp);
	if (rc) {
		pr_err("HDCP initialization failed\n");
		goto end;
	}
end:
	return rc;
}
@@ -240,6 +464,7 @@ static void dp_display_unbind(struct device *dev, struct device *master,
	(void)dp->panel->sde_edid_deregister(dp->panel);
	(void)dp->aux->drm_aux_deregister(dp->aux);
	(void)dp_display_debugfs_deinit(dp);
	dp_display_deinitialize_hdcp(dp);
}

static const struct component_ops dp_display_comp_ops = {
@@ -313,6 +538,11 @@ static void dp_display_process_hpd_low(struct dp_display_private *dp)
	/* cancel any pending request */
	dp->ctrl->abort(dp->ctrl);

	if (dp_display_is_hdcp_enabled(dp) && dp->hdcp.ops->off) {
		cancel_delayed_work_sync(&dp->hdcp_cb_work);
		dp->hdcp.ops->off(dp->hdcp.data);
	}

	dp->dp_display.is_connected = false;
	drm_helper_hpd_irq_event(dp->dp_display.connector->dev);

@@ -408,6 +638,12 @@ static int dp_display_usbpd_attention_cb(struct device *dev)

	if (dp->usbpd->hpd_irq) {
		dp->hpd_irq_on = true;

		if (dp_display_is_hdcp_enabled(dp) && dp->hdcp.ops->cp_irq) {
			if (!dp->hdcp.ops->cp_irq(dp->hdcp.data))
				goto end;
		}

		rc = dp->link->process_request(dp->link);
		dp->hpd_irq_on = false;
		if (!rc)
@@ -564,6 +800,16 @@ static int dp_display_post_enable(struct dp_display *dp_display)
	dp = container_of(dp_display, struct dp_display_private, dp_display);

	complete_all(&dp->notification_comp);

	dp_display_update_hdcp_info(dp);

	if (dp_display_is_hdcp_enabled(dp)) {
		cancel_delayed_work_sync(&dp->hdcp_cb_work);

		dp->hdcp_status = HDCP_STATE_AUTHENTICATING;
		queue_delayed_work(dp->hdcp_workqueue,
				&dp->hdcp_cb_work, HZ / 2);
	}
end:
	return rc;
}
@@ -581,6 +827,14 @@ static int dp_display_pre_disable(struct dp_display *dp_display)

	dp = container_of(dp_display, struct dp_display_private, dp_display);

	if (dp_display_is_hdcp_enabled(dp)) {
		dp->hdcp_status = HDCP_STATE_INACTIVE;

		cancel_delayed_work_sync(&dp->hdcp_cb_work);
		if (dp->hdcp.ops->off)
			dp->hdcp.ops->off(dp->hdcp.data);
	}

	dp->ctrl->push_idle(dp->ctrl);
error:
	return rc;
+925 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading