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

Commit bd658efa authored by Rohith Palakurthi's avatar Rohith Palakurthi
Browse files

Merge remote-tracking branch 'quic/dev/msm-4.14-display' into msm-4.14



* quic/dev/msm-4.14-display:
  drm/msm/sde: Fix for match NULL handle in msm_pdev_probe
  drm/msm/dp: Add MST support for HDCP 2.2
  drm/msm/hdcp: Extend SDE HDCP to support stream registration
  drm/msm/hdcp: Update HDCP QSEECOM to start auth on user command

Change-Id: Id15e9abfe5f57bcdc963c0a7ac0dcd0efda616be
Signed-off-by: default avatarRohith Palakurthi <prohit@codeaurora.org>
parents 8f24df85 a2b81990
Loading
Loading
Loading
Loading
+94 −7
Original line number Original line Diff line number Diff line
@@ -241,16 +241,62 @@ static void dp_display_check_source_hdcp_caps(struct dp_display_private *dp)
		struct sde_hdcp_ops *ops = dev->ops;
		struct sde_hdcp_ops *ops = dev->ops;
		void *fd = dev->fd;
		void *fd = dev->fd;


		if (!fd || !ops || (dp->hdcp.source_cap & dev->ver))
		if (!fd || !ops)
			continue;
			continue;


		if (ops->feature_supported(fd))
		if (ops->set_mode && ops->set_mode(fd, dp->mst.mst_active))
			continue;

		if (!(dp->hdcp.source_cap & dev->ver) &&
				ops->feature_supported &&
				ops->feature_supported(fd))
			dp->hdcp.source_cap |= dev->ver;
			dp->hdcp.source_cap |= dev->ver;
	}
	}


	dp_display_update_hdcp_status(dp, false);
	dp_display_update_hdcp_status(dp, false);
}
}


static void dp_display_hdcp_register_streams(struct dp_display_private *dp)
{
	int rc;
	struct sde_hdcp_ops *ops = dp->hdcp.ops;
	void *data = dp->hdcp.data;

	if (dp_display_is_ready(dp) && dp->mst.mst_active && ops &&
			ops->register_streams){
		struct stream_info streams[DP_STREAM_MAX];
		int index = 0;

		pr_debug("Registering all active panel streams with HDCP\n");
		for (size_t i = DP_STREAM_0; i < DP_STREAM_MAX; i++) {
			if (!dp->active_panels[i])
				continue;
			streams[index].stream_id = i;
			streams[index].virtual_channel =
				dp->active_panels[i]->vcpi;
			index++;
		}

		if (index > 0) {
			rc = ops->register_streams(data, index, streams);
			if (rc)
				pr_err("failed to register streams. rc = %d\n",
					rc);
		}
	}
}

static void dp_display_hdcp_deregister_stream(struct dp_display_private *dp,
		enum dp_stream_id stream_id)
{
	if (dp->hdcp.ops->deregister_streams) {
		struct stream_info stream = {stream_id, 0};

		pr_debug("Deregistering stream within HDCP library");
		dp->hdcp.ops->deregister_streams(dp->hdcp.data, 1, &stream);
	}
}

static void dp_display_hdcp_cb_work(struct work_struct *work)
static void dp_display_hdcp_cb_work(struct work_struct *work)
{
{
	struct dp_display_private *dp;
	struct dp_display_private *dp;
@@ -273,6 +319,11 @@ static void dp_display_hdcp_cb_work(struct work_struct *work)
		dp_display_update_hdcp_info(dp);
		dp_display_update_hdcp_info(dp);


		if (dp_display_is_hdcp_enabled(dp)) {
		if (dp_display_is_hdcp_enabled(dp)) {
			if (dp->hdcp.ops && dp->hdcp.ops->on &&
					dp->hdcp.ops->on(dp->hdcp.data)) {
				dp_display_update_hdcp_status(dp, true);
				return;
			}
			status->hdcp_state = HDCP_STATE_AUTHENTICATING;
			status->hdcp_state = HDCP_STATE_AUTHENTICATING;
		} else {
		} else {
			dp_display_update_hdcp_status(dp, true);
			dp_display_update_hdcp_status(dp, true);
@@ -299,11 +350,18 @@ static void dp_display_hdcp_cb_work(struct work_struct *work)


	switch (status->hdcp_state) {
	switch (status->hdcp_state) {
	case HDCP_STATE_AUTHENTICATING:
	case HDCP_STATE_AUTHENTICATING:
		dp_display_hdcp_register_streams(dp);
		if (dp->hdcp.ops && dp->hdcp.ops->authenticate)
		if (dp->hdcp.ops && dp->hdcp.ops->authenticate)
			rc = dp->hdcp.ops->authenticate(data);
			rc = dp->hdcp.ops->authenticate(data);
		break;
		break;
	case HDCP_STATE_AUTH_FAIL:
	case HDCP_STATE_AUTH_FAIL:
		if (dp_display_is_ready(dp) && dp->power_on) {
		if (dp_display_is_ready(dp) && dp->power_on) {
			if (ops && ops->on && ops->on(data)) {
				dp_display_update_hdcp_status(dp, true);
				return;
			}
			dp_display_hdcp_register_streams(dp);
			status->hdcp_state = HDCP_STATE_AUTHENTICATING;
			if (ops && ops->reauthenticate) {
			if (ops && ops->reauthenticate) {
				rc = ops->reauthenticate(data);
				rc = ops->reauthenticate(data);
				if (rc)
				if (rc)
@@ -314,6 +372,7 @@ static void dp_display_hdcp_cb_work(struct work_struct *work)
		}
		}
		break;
		break;
	default:
	default:
		dp_display_hdcp_register_streams(dp);
		break;
		break;
	}
	}
}
}
@@ -721,15 +780,23 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp)
{
{
	int rc = 0, idx;
	int rc = 0, idx;
	struct dp_panel *dp_panel;
	struct dp_panel *dp_panel;
	struct dp_link_hdcp_status *status;


	mutex_lock(&dp->session_lock);
	mutex_lock(&dp->session_lock);


	status = &dp->link->hdcp_status;
	dp->is_connected = false;
	dp->is_connected = false;
	dp->process_hpd_connect = false;
	dp->process_hpd_connect = false;


	if (dp_display_is_hdcp_enabled(dp) && dp->hdcp.ops->off)
	if (dp_display_is_hdcp_enabled(dp) &&
			status->hdcp_state != HDCP_STATE_INACTIVE) {
		cancel_delayed_work_sync(&dp->hdcp_cb_work);
		if (dp->hdcp.ops->off)
			dp->hdcp.ops->off(dp->hdcp.data);
			dp->hdcp.ops->off(dp->hdcp.data);


		dp_display_update_hdcp_status(dp, true);
	}

	for (idx = DP_STREAM_0; idx < DP_STREAM_MAX; idx++) {
	for (idx = DP_STREAM_0; idx < DP_STREAM_MAX; idx++) {
		if (!dp->active_panels[idx])
		if (!dp->active_panels[idx])
			continue;
			continue;
@@ -823,8 +890,10 @@ static void dp_display_clean(struct dp_display_private *dp)
{
{
	int idx;
	int idx;
	struct dp_panel *dp_panel;
	struct dp_panel *dp_panel;
	struct dp_link_hdcp_status *status = &dp->link->hdcp_status;


	if (dp_display_is_hdcp_enabled(dp)) {
	if (dp_display_is_hdcp_enabled(dp) &&
			status->hdcp_state != HDCP_STATE_INACTIVE) {
		cancel_delayed_work_sync(&dp->hdcp_cb_work);
		cancel_delayed_work_sync(&dp->hdcp_cb_work);
		if (dp->hdcp.ops->off)
		if (dp->hdcp.ops->off)
			dp->hdcp.ops->off(dp->hdcp.data);
			dp->hdcp.ops->off(dp->hdcp.data);
@@ -1583,6 +1652,7 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel)
{
{
	struct dp_display_private *dp;
	struct dp_display_private *dp;
	struct dp_panel *dp_panel = panel;
	struct dp_panel *dp_panel = panel;
	struct dp_link_hdcp_status *status;
	int rc = 0;
	int rc = 0;


	if (!dp_display || !panel) {
	if (!dp_display || !panel) {
@@ -1594,19 +1664,36 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel)


	mutex_lock(&dp->session_lock);
	mutex_lock(&dp->session_lock);


	status = &dp->link->hdcp_status;

	if (!dp->power_on) {
	if (!dp->power_on) {
		pr_debug("stream already powered off, return\n");
		pr_debug("stream already powered off, return\n");
		goto end;
		goto end;
	}
	}


	if (dp_display_is_hdcp_enabled(dp)) {
	if (dp_display_is_hdcp_enabled(dp) &&
		cancel_delayed_work_sync(&dp->hdcp_cb_work);
			status->hdcp_state != HDCP_STATE_INACTIVE) {
		flush_delayed_work(&dp->hdcp_cb_work);
		if (dp->mst.mst_active) {
			dp_display_hdcp_deregister_stream(dp,
				dp_panel->stream_id);
			for (int i = DP_STREAM_0; i < DP_STREAM_MAX; i++) {
				if (i != dp_panel->stream_id)
					continue;
				if (dp->active_panels[i]) {
					pr_debug("Streams are still active. Skip disabling HDCP\n");
					goto stream;
				}
			}
		}

		if (dp->hdcp.ops->off)
		if (dp->hdcp.ops->off)
			dp->hdcp.ops->off(dp->hdcp.data);
			dp->hdcp.ops->off(dp->hdcp.data);


		dp_display_update_hdcp_status(dp, true);
		dp_display_update_hdcp_status(dp, true);
	}
	}


stream:
	if (dp_panel->audio_supported)
	if (dp_panel->audio_supported)
		dp_panel->audio->off(dp_panel->audio);
		dp_panel->audio->off(dp_panel->audio);


+147 −54
Original line number Original line Diff line number Diff line
/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * it under the terms of the GNU General Public License version 2 and
@@ -76,6 +76,25 @@ struct dp_hdcp2p2_interrupts {
	struct dp_hdcp2p2_int_set *int_set;
	struct dp_hdcp2p2_int_set *int_set;
};
};


static inline int dp_hdcp2p2_valid_handle(struct dp_hdcp2p2_ctrl *ctrl)
{
	if (!ctrl) {
		pr_err("invalid input\n");
		return -EINVAL;
	}

	if (!ctrl->lib_ctx) {
		pr_err("HDCP library needs to be acquired\n");
		return -EINVAL;
	}

	if (!ctrl->lib) {
		pr_err("invalid lib ops data\n");
		return -EINVAL;
	}
	return 0;
}

static inline bool dp_hdcp2p2_is_valid_state(struct dp_hdcp2p2_ctrl *ctrl)
static inline bool dp_hdcp2p2_is_valid_state(struct dp_hdcp2p2_ctrl *ctrl)
{
{
	if (ctrl->wakeup_cmd == HDCP_TRANSPORT_CMD_AUTHENTICATE)
	if (ctrl->wakeup_cmd == HDCP_TRANSPORT_CMD_AUTHENTICATE)
@@ -255,20 +274,57 @@ static void dp_hdcp2p2_reset(struct dp_hdcp2p2_ctrl *ctrl)
	atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE);
	atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE);
}
}


static void dp_hdcp2p2_off(void *input)
static int dp_hdcp2p2_register(void *input, bool mst_enabled)
{
{
	int rc;
	enum sde_hdcp_2x_device_type device_type;
	struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input;
	struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input;
	struct hdcp_transport_wakeup_data cdata = {
					HDCP_TRANSPORT_CMD_AUTHENTICATE};


	if (!ctrl) {
	rc = dp_hdcp2p2_valid_handle(ctrl);
		pr_err("invalid input\n");
	if (rc)
		return;
		return rc;

	if (mst_enabled)
		device_type = HDCP_TXMTR_DP_MST;
	else
		device_type = HDCP_TXMTR_DP;

	return sde_hdcp_2x_enable(ctrl->lib_ctx, device_type);
}
}


	if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
static int dp_hdcp2p2_on(void *input)
		pr_err("hdcp is off\n");
{
	int rc = 0;
	struct dp_hdcp2p2_ctrl *ctrl = input;
	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID};

	rc = dp_hdcp2p2_valid_handle(ctrl);
	if (rc)
		return rc;

	cdata.cmd = HDCP_2X_CMD_START;
	cdata.context = ctrl->lib_ctx;
	rc = ctrl->lib->wakeup(&cdata);
	if (rc)
		pr_err("Unable to start the HDCP 2.2 library. Error - %d", rc);

	return rc;
}

static void dp_hdcp2p2_off(void *input)
{
	int rc;
	struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input;
	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID};

	rc = dp_hdcp2p2_valid_handle(ctrl);
	if (rc)
		return;
		return;

	if (atomic_read(&ctrl->auth_state) != HDCP_STATE_AUTH_FAIL) {
		cdata.cmd = HDCP_2X_CMD_STOP;
		cdata.context = ctrl->lib_ctx;
		dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
	}
	}


	dp_hdcp2p2_set_interrupts(ctrl, false);
	dp_hdcp2p2_set_interrupts(ctrl, false);
@@ -277,16 +333,18 @@ static void dp_hdcp2p2_off(void *input)


	kthread_flush_worker(&ctrl->worker);
	kthread_flush_worker(&ctrl->worker);


	cdata.context = input;
	sde_hdcp_2x_disable(ctrl->lib_ctx);
	dp_hdcp2p2_wakeup(&cdata);
}
}


static int dp_hdcp2p2_authenticate(void *input)
static int dp_hdcp2p2_authenticate(void *input)
{
{
	int rc;
	struct dp_hdcp2p2_ctrl *ctrl = input;
	struct dp_hdcp2p2_ctrl *ctrl = input;
	struct hdcp_transport_wakeup_data cdata = {
	struct hdcp_transport_wakeup_data cdata = {
					HDCP_TRANSPORT_CMD_AUTHENTICATE};
					HDCP_TRANSPORT_CMD_AUTHENTICATE};
	int rc = 0;
	rc = dp_hdcp2p2_valid_handle(ctrl);
	if (rc)
		return rc;


	kthread_flush_worker(&ctrl->worker);
	kthread_flush_worker(&ctrl->worker);


@@ -407,44 +465,34 @@ static int dp_hdcp2p2_aux_write_message(struct dp_hdcp2p2_ctrl *ctrl,


static bool dp_hdcp2p2_feature_supported(void *input)
static bool dp_hdcp2p2_feature_supported(void *input)
{
{
	int rc;
	struct dp_hdcp2p2_ctrl *ctrl = input;
	struct dp_hdcp2p2_ctrl *ctrl = input;
	struct sde_hdcp_2x_ops *lib = NULL;
	struct sde_hdcp_2x_ops *lib = NULL;
	bool supported = false;
	bool supported = false;


	if (!ctrl) {
	rc = dp_hdcp2p2_valid_handle(ctrl);
		pr_err("invalid input\n");
	if (rc)
		goto end;
		return supported;
	}


	lib = ctrl->lib;
	lib = ctrl->lib;
	if (!lib) {
		pr_err("invalid lib ops data\n");
		goto end;
	}

	if (lib->feature_supported)
	if (lib->feature_supported)
		supported = lib->feature_supported(
		supported = lib->feature_supported(
			ctrl->lib_ctx);
			ctrl->lib_ctx);
end:

	return supported;
	return supported;
}
}


static void dp_hdcp2p2_force_encryption(void *data, bool enable)
static void dp_hdcp2p2_force_encryption(void *data, bool enable)
{
{
	int rc;
	struct dp_hdcp2p2_ctrl *ctrl = data;
	struct dp_hdcp2p2_ctrl *ctrl = data;
	struct sde_hdcp_2x_ops *lib = NULL;
	struct sde_hdcp_2x_ops *lib = NULL;


	if (!ctrl) {
	rc = dp_hdcp2p2_valid_handle(ctrl);
		pr_err("invalid input\n");
	if (rc)
		return;
		return;
	}


	lib = ctrl->lib;
	lib = ctrl->lib;
	if (!lib) {
		pr_err("invalid lib ops data\n");
		return;
	}

	if (lib->force_encryption)
	if (lib->force_encryption)
		lib->force_encryption(ctrl->lib_ctx, enable);
		lib->force_encryption(ctrl->lib_ctx, enable);
}
}
@@ -594,19 +642,17 @@ static void dp_hdcp2p2_link_work(struct kthread_work *work)


static void dp_hdcp2p2_auth_work(struct kthread_work *work)
static void dp_hdcp2p2_auth_work(struct kthread_work *work)
{
{
	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID};
	struct dp_hdcp2p2_ctrl *ctrl = container_of(work,
	struct dp_hdcp2p2_ctrl *ctrl = container_of(work,
		struct dp_hdcp2p2_ctrl, auth);
		struct dp_hdcp2p2_ctrl, auth);


	cdata.context = ctrl->lib_ctx;
	if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING) {

		struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID};
	if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING)
		cdata.cmd = HDCP_2X_CMD_START;
	else
		cdata.cmd = HDCP_2X_CMD_STOP;


		cdata.context = ctrl->lib_ctx;
		cdata.cmd = HDCP_2X_CMD_START_AUTH;
		dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
		dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
	}
	}
}


static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl,
static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl,
		u8 *rx_status)
		u8 *rx_status)
@@ -651,41 +697,36 @@ static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl,


static int dp_hdcp2p2_cp_irq(void *input)
static int dp_hdcp2p2_cp_irq(void *input)
{
{
	int rc = 0;
	int rc;
	struct dp_hdcp2p2_ctrl *ctrl = input;
	struct dp_hdcp2p2_ctrl *ctrl = input;


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


	if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTH_FAIL ||
	if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTH_FAIL ||
		atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
		atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
		pr_err("invalid hdcp state\n");
		pr_err("invalid hdcp state\n");
		rc = -EINVAL;
		return -EINVAL;
		goto error;
	}
	}


	ctrl->sink_rx_status = 0;
	ctrl->sink_rx_status = 0;
	rc = dp_hdcp2p2_read_rx_status(ctrl, &ctrl->sink_rx_status);
	rc = dp_hdcp2p2_read_rx_status(ctrl, &ctrl->sink_rx_status);
	if (rc) {
	if (rc) {
		pr_err("failed to read rx status\n");
		pr_err("failed to read rx status\n");
		goto error;
		return rc;
	}
	}


	pr_debug("sink_rx_status=0x%x\n", ctrl->sink_rx_status);
	pr_debug("sink_rx_status=0x%x\n", ctrl->sink_rx_status);


	if (!ctrl->sink_rx_status) {
	if (!ctrl->sink_rx_status) {
		pr_debug("not a hdcp 2.2 irq\n");
		pr_debug("not a hdcp 2.2 irq\n");
		rc = -EINVAL;
		return -EINVAL;
		goto error;
	}
	}


	kthread_queue_work(&ctrl->worker, &ctrl->link);
	kthread_queue_work(&ctrl->worker, &ctrl->link);


	return 0;
	return 0;
error:
	return rc;
}
}


static int dp_hdcp2p2_isr(void *input)
static int dp_hdcp2p2_isr(void *input)
@@ -753,6 +794,51 @@ static bool dp_hdcp2p2_supported(void *input)
	return false;
	return false;
}
}


static int dp_hdcp2p2_change_streams(struct dp_hdcp2p2_ctrl *ctrl,
		struct sde_hdcp_2x_wakeup_data *cdata)
{
	if (!ctrl || cdata->num_streams == 0 || !cdata->streams) {
		pr_err("invalid input\n");
		return -EINVAL;
	}

	if (!ctrl->lib_ctx) {
		pr_err("HDCP library needs to be acquired\n");
		return -EINVAL;
	}

	if (!ctrl->lib) {
		pr_err("invalid lib ops data\n");
		return -EINVAL;
	}

	cdata->context = ctrl->lib_ctx;
	return ctrl->lib->wakeup(cdata);
}


static int dp_hdcp2p2_register_streams(void *input, u8 num_streams,
			struct stream_info *streams)
{
	struct dp_hdcp2p2_ctrl *ctrl = input;
	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_OPEN_STREAMS};

	cdata.streams = streams;
	cdata.num_streams = num_streams;
	return dp_hdcp2p2_change_streams(ctrl, &cdata);
}

static int dp_hdcp2p2_deregister_streams(void *input, u8 num_streams,
			struct stream_info *streams)
{
	struct dp_hdcp2p2_ctrl *ctrl = input;
	struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_CLOSE_STREAMS};

	cdata.streams = streams;
	cdata.num_streams = num_streams;
	return dp_hdcp2p2_change_streams(ctrl, &cdata);
}

void sde_dp_hdcp2p2_deinit(void *input)
void sde_dp_hdcp2p2_deinit(void *input)
{
{
	struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input;
	struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input;
@@ -763,9 +849,13 @@ void sde_dp_hdcp2p2_deinit(void *input)
		return;
		return;
	}
	}


	if (atomic_read(&ctrl->auth_state) != HDCP_STATE_AUTH_FAIL) {
		cdata.cmd = HDCP_2X_CMD_STOP;
		cdata.cmd = HDCP_2X_CMD_STOP;
		cdata.context = ctrl->lib_ctx;
		cdata.context = ctrl->lib_ctx;
		dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
		dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
	}

	sde_hdcp_2x_deregister(ctrl->lib_ctx);


	kthread_stop(ctrl->thread);
	kthread_stop(ctrl->thread);


@@ -786,8 +876,12 @@ void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data)
		.feature_supported = dp_hdcp2p2_feature_supported,
		.feature_supported = dp_hdcp2p2_feature_supported,
		.force_encryption = dp_hdcp2p2_force_encryption,
		.force_encryption = dp_hdcp2p2_force_encryption,
		.sink_support = dp_hdcp2p2_supported,
		.sink_support = dp_hdcp2p2_supported,
		.set_mode = dp_hdcp2p2_register,
		.on = dp_hdcp2p2_on,
		.off = dp_hdcp2p2_off,
		.off = dp_hdcp2p2_off,
		.cp_irq = dp_hdcp2p2_cp_irq,
		.cp_irq = dp_hdcp2p2_cp_irq,
		.register_streams = dp_hdcp2p2_register_streams,
		.deregister_streams = dp_hdcp2p2_deregister_streams,
	};
	};


	static struct hdcp_transport_ops client_ops = {
	static struct hdcp_transport_ops client_ops = {
@@ -840,7 +934,6 @@ void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data)
	register_data.hdcp_data = &ctrl->lib_ctx;
	register_data.hdcp_data = &ctrl->lib_ctx;
	register_data.client_ops = &client_ops;
	register_data.client_ops = &client_ops;
	register_data.ops = &hdcp2x_ops;
	register_data.ops = &hdcp2x_ops;
	register_data.device_type = HDCP_TXMTR_DP;
	register_data.client_data = ctrl;
	register_data.client_data = ctrl;


	rc = sde_hdcp_2x_register(&register_data);
	rc = sde_hdcp_2x_register(&register_data);
+4 −1
Original line number Original line Diff line number Diff line
/*
/*
 * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
 * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
 * Copyright (C) 2013 Red Hat
 * Copyright (C) 2013 Red Hat
 * Author: Rob Clark <robdclark@gmail.com>
 * Author: Rob Clark <robdclark@gmail.com>
 *
 *
@@ -1973,6 +1973,9 @@ static int msm_pdev_probe(struct platform_device *pdev)
	if (ret)
	if (ret)
		return ret;
		return ret;


	if (!match)
		return -ENODEV;

	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
	return component_master_add_with_match(&pdev->dev, &msm_drm_ops, match);
	return component_master_add_with_match(&pdev->dev, &msm_drm_ops, match);
}
}
+22 −1
Original line number Original line Diff line number Diff line
/* Copyright (c) 2012, 2014-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012, 2014-2019, The Linux Foundation. All rights reserved.
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * it under the terms of the GNU General Public License version 2 and
@@ -20,12 +20,15 @@
#include <linux/debugfs.h>
#include <linux/debugfs.h>
#include <linux/of_device.h>
#include <linux/of_device.h>
#include <linux/i2c.h>
#include <linux/i2c.h>
#include <linux/list.h>
#include <drm/drmP.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include <drm/drm_edid.h>
#include <linux/hdcp_qseecom.h>
#include <linux/hdcp_qseecom.h>
#include "sde_kms.h"
#include "sde_kms.h"


#define MAX_STREAM_COUNT 2

enum sde_hdcp_client_id {
enum sde_hdcp_client_id {
	HDCP_CLIENT_HDMI,
	HDCP_CLIENT_HDMI,
	HDCP_CLIENT_DP,
	HDCP_CLIENT_DP,
@@ -45,6 +48,18 @@ enum sde_hdcp_version {
	HDCP_VERSION_MAX = BIT(2),
	HDCP_VERSION_MAX = BIT(2),
};
};


struct stream_info {
	u8 stream_id;
	u8 virtual_channel;
};

struct sde_hdcp_stream {
	struct list_head list;
	u8 stream_id;
	u8 virtual_channel;
	u32 stream_handle;
};

struct sde_hdcp_init_data {
struct sde_hdcp_init_data {
	struct device *msm_hdcp_dev;
	struct device *msm_hdcp_dev;
	struct dss_io_data *core_io;
	struct dss_io_data *core_io;
@@ -74,7 +89,13 @@ struct sde_hdcp_ops {
	bool (*feature_supported)(void *input);
	bool (*feature_supported)(void *input);
	void (*force_encryption)(void *input, bool enable);
	void (*force_encryption)(void *input, bool enable);
	bool (*sink_support)(void *input);
	bool (*sink_support)(void *input);
	int (*set_mode)(void *input, bool mst_enabled);
	int (*on)(void *input);
	void (*off)(void *hdcp_ctrl);
	void (*off)(void *hdcp_ctrl);
	int (*register_streams)(void *input, u8 num_streams,
			struct stream_info *streams);
	int (*deregister_streams)(void *input, u8 num_streams,
			struct stream_info *streams);
};
};


static inline const char *sde_hdcp_state_name(enum sde_hdcp_state hdcp_state)
static inline const char *sde_hdcp_state_name(enum sde_hdcp_state hdcp_state)
+265 −23
Original line number Original line Diff line number Diff line
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * it under the terms of the GNU General Public License version 2 and
@@ -74,12 +74,17 @@ struct sde_hdcp_2x_ctrl {
	atomic_t hdcp_off;
	atomic_t hdcp_off;
	enum sde_hdcp_2x_device_type device_type;
	enum sde_hdcp_2x_device_type device_type;
	u8 min_enc_level;
	u8 min_enc_level;
	struct list_head stream_handles;
	u8 stream_count;
	struct stream_info *streams;
	u8 num_streams;


	struct task_struct *thread;
	struct task_struct *thread;
	struct completion response_completion;
	struct completion response_completion;


	struct kthread_worker worker;
	struct kthread_worker worker;
	struct kthread_work wk_init;
	struct kthread_work wk_init;
	struct kthread_work wk_start_auth;
	struct kthread_work wk_msg_sent;
	struct kthread_work wk_msg_sent;
	struct kthread_work wk_msg_recvd;
	struct kthread_work wk_msg_recvd;
	struct kthread_work wk_timeout;
	struct kthread_work wk_timeout;
@@ -87,6 +92,8 @@ struct sde_hdcp_2x_ctrl {
	struct kthread_work wk_stream;
	struct kthread_work wk_stream;
	struct kthread_work wk_wait;
	struct kthread_work wk_wait;
	struct kthread_work wk_send_type;
	struct kthread_work wk_send_type;
	struct kthread_work wk_open_stream;
	struct kthread_work wk_close_stream;
};
};


static const char *sde_hdcp_2x_message_name(int msg_id)
static const char *sde_hdcp_2x_message_name(int msg_id)
@@ -376,6 +383,8 @@ static int sde_hdcp_2x_check_valid_state(struct sde_hdcp_2x_ctrl *hdcp)


static void sde_hdcp_2x_clean(struct sde_hdcp_2x_ctrl *hdcp)
static void sde_hdcp_2x_clean(struct sde_hdcp_2x_ctrl *hdcp)
{
{
	struct list_head *element;
	struct sde_hdcp_stream *stream_entry;
	struct hdcp_transport_wakeup_data cdata = {
	struct hdcp_transport_wakeup_data cdata = {
						HDCP_TRANSPORT_CMD_INVALID };
						HDCP_TRANSPORT_CMD_INVALID };


@@ -384,10 +393,20 @@ static void sde_hdcp_2x_clean(struct sde_hdcp_2x_ctrl *hdcp)
	cdata.context = hdcp->client_data;
	cdata.context = hdcp->client_data;
	cdata.cmd = HDCP_TRANSPORT_CMD_STATUS_FAILED;
	cdata.cmd = HDCP_TRANSPORT_CMD_STATUS_FAILED;


	if (!atomic_read(&hdcp->hdcp_off))
	while (!list_empty(&hdcp->stream_handles)) {
		sde_hdcp_2x_wakeup_client(hdcp, &cdata);
		element = hdcp->stream_handles.next;
		list_del(element);


	atomic_set(&hdcp->hdcp_off, 1);
		stream_entry = list_entry(element, struct sde_hdcp_stream,
			list);
		hdcp2_close_stream(hdcp->hdcp2_ctx,
			stream_entry->stream_handle);
		kzfree(stream_entry);
		hdcp->stream_count--;
	}

	if (!atomic_xchg(&hdcp->hdcp_off, 1))
		sde_hdcp_2x_wakeup_client(hdcp, &cdata);


	hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_STOP, &hdcp->app_data);
	hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_STOP, &hdcp->app_data);
}
}
@@ -569,11 +588,6 @@ static void sde_hdcp_2x_msg_sent_work(struct kthread_work *work)
	struct sde_hdcp_2x_ctrl *hdcp =
	struct sde_hdcp_2x_ctrl *hdcp =
		container_of(work, struct sde_hdcp_2x_ctrl, wk_msg_sent);
		container_of(work, struct sde_hdcp_2x_ctrl, wk_msg_sent);


	if (hdcp->wakeup_cmd != HDCP_2X_CMD_MSG_SEND_SUCCESS) {
		pr_err("invalid wakeup command %d\n", hdcp->wakeup_cmd);
		return;
	}

	sde_hdcp_2x_msg_sent(hdcp);
	sde_hdcp_2x_msg_sent(hdcp);
}
}


@@ -581,12 +595,29 @@ static void sde_hdcp_2x_init(struct sde_hdcp_2x_ctrl *hdcp)
{
{
	int rc = 0;
	int rc = 0;


	if (hdcp->wakeup_cmd != HDCP_2X_CMD_START) {
	rc = hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_START, &hdcp->app_data);
		pr_err("invalid wakeup command %d\n", hdcp->wakeup_cmd);
	if (rc)
		goto exit;

	return;
	return;
exit:
	HDCP_2X_EXECUTE(clean);
}
}


	rc = hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_START, &hdcp->app_data);
static void sde_hdcp_2x_init_work(struct kthread_work *work)
{
	struct sde_hdcp_2x_ctrl *hdcp =
		container_of(work, struct sde_hdcp_2x_ctrl, wk_init);

	sde_hdcp_2x_init(hdcp);
}

static void sde_hdcp_2x_start_auth(struct sde_hdcp_2x_ctrl *hdcp)
{
	int rc = 0;

	rc = hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_START_AUTH,
		&hdcp->app_data);
	if (rc)
	if (rc)
		goto exit;
		goto exit;


@@ -600,14 +631,16 @@ static void sde_hdcp_2x_init(struct sde_hdcp_2x_ctrl *hdcp)
	HDCP_2X_EXECUTE(clean);
	HDCP_2X_EXECUTE(clean);
}
}


static void sde_hdcp_2x_init_work(struct kthread_work *work)

static void sde_hdcp_2x_start_auth_work(struct kthread_work *work)
{
{
	struct sde_hdcp_2x_ctrl *hdcp =
	struct sde_hdcp_2x_ctrl *hdcp =
		container_of(work, struct sde_hdcp_2x_ctrl, wk_init);
		container_of(work, struct sde_hdcp_2x_ctrl, wk_start_auth);


	sde_hdcp_2x_init(hdcp);
	sde_hdcp_2x_start_auth(hdcp);
}
}



static void sde_hdcp_2x_timeout(struct sde_hdcp_2x_ctrl *hdcp)
static void sde_hdcp_2x_timeout(struct sde_hdcp_2x_ctrl *hdcp)
{
{
	int rc = 0;
	int rc = 0;
@@ -664,7 +697,8 @@ static void sde_hdcp_2x_msg_recvd(struct sde_hdcp_2x_ctrl *hdcp)
		goto exit;
		goto exit;
	}
	}


	if (hdcp->device_type == HDCP_TXMTR_DP) {
	if (hdcp->device_type == HDCP_TXMTR_DP ||
			hdcp->device_type == HDCP_TXMTR_DP_MST) {
		msg[0] = hdcp->last_msg;
		msg[0] = hdcp->last_msg;
		message_id_bytes = 1;
		message_id_bytes = 1;
	}
	}
@@ -796,6 +830,162 @@ static void sde_hdcp_2x_wait_for_response_work(struct kthread_work *work)
	hdcp->wait_timeout_ms = 0;
	hdcp->wait_timeout_ms = 0;
}
}


static struct list_head *sde_hdcp_2x_stream_present(
		struct sde_hdcp_2x_ctrl *hdcp, u8 stream_id, u8 virtual_channel)
{
	struct sde_hdcp_stream *stream_entry;
	struct list_head *entry;
	bool present = false;

	list_for_each(entry, &hdcp->stream_handles) {
		stream_entry = list_entry(entry,
			struct sde_hdcp_stream, list);
		if (stream_entry->virtual_channel == virtual_channel &&
				stream_entry->stream_id == stream_id) {
			present = true;
			break;
		}
	}

	if (!present)
		entry = NULL;
	return entry;
}

static void sde_hdcp_2x_open_stream(struct sde_hdcp_2x_ctrl *hdcp)
{
	int rc;
	size_t iterations;
	u8 stream_id;
	u8 virtual_channel;
	u32 stream_handle = 0;
	bool query_streams = false;

	if (!hdcp->streams) {
		pr_err("Array of streams to register is NULL\n");
		return;
	}

	iterations = min(hdcp->num_streams, (u8)(MAX_STREAM_COUNT));

	for (size_t i  = 0; i < iterations; i++) {
		if (hdcp->stream_count == MAX_STREAM_COUNT) {
			pr_debug("Registered the maximum amount of streams\n");
			break;
		}

		stream_id = hdcp->streams[i].stream_id;
		virtual_channel = hdcp->streams[i].virtual_channel;

		pr_debug("Opening stream %d, virtual channel %d\n",
			stream_id, virtual_channel);

		if (sde_hdcp_2x_stream_present(hdcp, stream_id,
				virtual_channel)) {
			pr_debug("Stream %d, virtual channel %d already open\n",
				stream_id, virtual_channel);
			continue;
		}

		rc = hdcp2_open_stream(hdcp->hdcp2_ctx, virtual_channel,
				stream_id, &stream_handle);
		if (rc) {
			pr_err("Unable to open stream %d, virtual channel %d\n",
				stream_id, virtual_channel);
		} else {
			struct sde_hdcp_stream *stream =
				kzalloc(sizeof(struct sde_hdcp_stream),
					GFP_KERNEL);
			if (!stream)
				break;

			INIT_LIST_HEAD(&stream->list);
			stream->stream_handle = stream_handle;
			stream->stream_id = stream_id;
			stream->virtual_channel = virtual_channel;

			list_add(&stream->list, &hdcp->stream_handles);
			hdcp->stream_count++;

			query_streams = true;
		}
	}

	if (query_streams && hdcp->authenticated)
		HDCP_2X_EXECUTE(stream);
}

static void sde_hdcp_2x_open_stream_work(struct kthread_work *work)
{
	struct sde_hdcp_2x_ctrl *hdcp =
		container_of(work, struct sde_hdcp_2x_ctrl, wk_open_stream);

	sde_hdcp_2x_open_stream(hdcp);
}

static void sde_hdcp_2x_close_stream(struct sde_hdcp_2x_ctrl *hdcp)
{
	int rc;
	size_t iterations;
	u8 stream_id;
	u8 virtual_channel;
	struct list_head *entry;
	struct sde_hdcp_stream *stream_entry;
	bool query_streams = false;

	if (!hdcp->streams) {
		pr_err("Array of streams to register is NULL\n");
		return;
	}

	iterations = min(hdcp->num_streams, (u8)(MAX_STREAM_COUNT));

	for (size_t i = 0; i < iterations; i++) {
		if (hdcp->stream_count == 0) {
			pr_debug("No streams are currently registered\n");
			return;
		}

		stream_id = hdcp->streams[i].stream_id;
		virtual_channel = hdcp->streams[i].virtual_channel;

		pr_debug("Closing stream %d, virtual channel %d\n",
			stream_id, virtual_channel);

		entry = sde_hdcp_2x_stream_present(hdcp, stream_id,
			virtual_channel);

		if (!entry) {
			pr_err("Unable to find stream %d, virtual channel %d\n"
				, stream_id, virtual_channel);
			continue;
		}

		stream_entry = list_entry(entry, struct sde_hdcp_stream,
			list);

		rc = hdcp2_close_stream(hdcp->hdcp2_ctx,
			stream_entry->stream_handle);
		if (rc)
			pr_err("Unable to close stream %d, virtual channel %d\n"
				, stream_id, virtual_channel);
		hdcp->stream_count--;
		list_del(entry);
		kzfree(stream_entry);
		query_streams = true;
	}

	if (query_streams && hdcp->authenticated)
		HDCP_2X_EXECUTE(stream);
}

static void sde_hdcp_2x_close_stream_work(struct kthread_work *work)
{
	struct sde_hdcp_2x_ctrl *hdcp =
		container_of(work, struct sde_hdcp_2x_ctrl, wk_close_stream);
	sde_hdcp_2x_close_stream(hdcp);
}

static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)
static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)
{
{
	struct sde_hdcp_2x_ctrl *hdcp;
	struct sde_hdcp_2x_ctrl *hdcp;
@@ -837,6 +1027,9 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)


		HDCP_2X_EXECUTE(init);
		HDCP_2X_EXECUTE(init);
		break;
		break;
	case HDCP_2X_CMD_START_AUTH:
		HDCP_2X_EXECUTE(start_auth);
		break;
	case HDCP_2X_CMD_STOP:
	case HDCP_2X_CMD_STOP:
		atomic_set(&hdcp->hdcp_off, 1);
		atomic_set(&hdcp->hdcp_off, 1);
		HDCP_2X_EXECUTE(clean);
		HDCP_2X_EXECUTE(clean);
@@ -867,6 +1060,18 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)


		HDCP_2X_EXECUTE(stream);
		HDCP_2X_EXECUTE(stream);
		break;
		break;
	case HDCP_2X_CMD_OPEN_STREAMS:
		hdcp->streams = data->streams;
		hdcp->num_streams = data->num_streams;
		HDCP_2X_EXECUTE(open_stream);
		kthread_flush_work(&hdcp->wk_open_stream);
		break;
	case HDCP_2X_CMD_CLOSE_STREAMS:
		hdcp->streams = data->streams;
		hdcp->num_streams = data->num_streams;
		HDCP_2X_EXECUTE(close_stream);
		kthread_flush_work(&hdcp->wk_close_stream);
		break;
	default:
	default:
		pr_err("invalid wakeup command %d\n", hdcp->wakeup_cmd);
		pr_err("invalid wakeup command %d\n", hdcp->wakeup_cmd);
	}
	}
@@ -912,19 +1117,18 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data)
		goto unlock;
		goto unlock;
	}
	}


	INIT_LIST_HEAD(&hdcp->stream_handles);
	hdcp->client_data = data->client_data;
	hdcp->client_data = data->client_data;
	hdcp->client_ops = data->client_ops;
	hdcp->client_ops = data->client_ops;
	hdcp->device_type = data->device_type;

	hdcp->hdcp2_ctx = hdcp2_init(hdcp->device_type);


	atomic_set(&hdcp->hdcp_off, 0);
	atomic_set(&hdcp->hdcp_off, 1);


	mutex_init(&hdcp->wakeup_mutex);
	mutex_init(&hdcp->wakeup_mutex);


	kthread_init_worker(&hdcp->worker);
	kthread_init_worker(&hdcp->worker);


	kthread_init_work(&hdcp->wk_init,      sde_hdcp_2x_init_work);
	kthread_init_work(&hdcp->wk_init,      sde_hdcp_2x_init_work);
	kthread_init_work(&hdcp->wk_start_auth, sde_hdcp_2x_start_auth_work);
	kthread_init_work(&hdcp->wk_msg_sent,  sde_hdcp_2x_msg_sent_work);
	kthread_init_work(&hdcp->wk_msg_sent,  sde_hdcp_2x_msg_sent_work);
	kthread_init_work(&hdcp->wk_msg_recvd, sde_hdcp_2x_msg_recvd_work);
	kthread_init_work(&hdcp->wk_msg_recvd, sde_hdcp_2x_msg_recvd_work);
	kthread_init_work(&hdcp->wk_timeout,   sde_hdcp_2x_timeout_work);
	kthread_init_work(&hdcp->wk_timeout,   sde_hdcp_2x_timeout_work);
@@ -932,6 +1136,9 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data)
	kthread_init_work(&hdcp->wk_stream,    sde_hdcp_2x_query_stream_work);
	kthread_init_work(&hdcp->wk_stream,    sde_hdcp_2x_query_stream_work);
	kthread_init_work(&hdcp->wk_wait, sde_hdcp_2x_wait_for_response_work);
	kthread_init_work(&hdcp->wk_wait, sde_hdcp_2x_wait_for_response_work);
	kthread_init_work(&hdcp->wk_send_type,    sde_hdcp_2x_send_type_work);
	kthread_init_work(&hdcp->wk_send_type,    sde_hdcp_2x_send_type_work);
	kthread_init_work(&hdcp->wk_open_stream, sde_hdcp_2x_open_stream_work);
	kthread_init_work(&hdcp->wk_close_stream,
				sde_hdcp_2x_close_stream_work);


	init_completion(&hdcp->response_completion);
	init_completion(&hdcp->response_completion);


@@ -957,6 +1164,41 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data)
	return rc;
	return rc;
}
}


int sde_hdcp_2x_enable(void *data, enum sde_hdcp_2x_device_type device_type)
{
	int rc =  0;
	struct sde_hdcp_2x_ctrl *hdcp = data;

	if (!hdcp)
		return  -EINVAL;

	if (hdcp->hdcp2_ctx) {
		pr_debug("HDCP library context already acquired\n");
		return 0;
	}

	hdcp->device_type = device_type;
	hdcp->hdcp2_ctx = hdcp2_init(hdcp->device_type);
	if (!hdcp->hdcp2_ctx) {
		pr_err("Unable to acquire HDCP library handle\n");
		return -ENOMEM;
	}

	return rc;
}

void sde_hdcp_2x_disable(void *data)
{
	struct sde_hdcp_2x_ctrl *hdcp = data;

	if (!hdcp->hdcp2_ctx)
		return;

	kthread_flush_worker(&hdcp->worker);
	hdcp2_deinit(hdcp->hdcp2_ctx);
	hdcp->hdcp2_ctx = NULL;
}

void sde_hdcp_2x_deregister(void *data)
void sde_hdcp_2x_deregister(void *data)
{
{
	struct sde_hdcp_2x_ctrl *hdcp = data;
	struct sde_hdcp_2x_ctrl *hdcp = data;
@@ -964,8 +1206,8 @@ void sde_hdcp_2x_deregister(void *data)
	if (!hdcp)
	if (!hdcp)
		return;
		return;


	sde_hdcp_2x_disable(data);
	kthread_stop(hdcp->thread);
	kthread_stop(hdcp->thread);
	mutex_destroy(&hdcp->wakeup_mutex);
	mutex_destroy(&hdcp->wakeup_mutex);
	hdcp2_deinit(hdcp->hdcp2_ctx);
	kzfree(hdcp);
	kzfree(hdcp);
}
}
Loading