drm/msm/hdcp: Extend SDE HDCP to support stream registration

MST HDCP functionality requires lower level HDCP libraries to
be aware of all active DP MST streams. Update the HDCP interface
to support DP MST mode, and allow drivers to register and deregister
streams for use with HDCP.

Change-Id: I03b89a8f34056c7f92c7483d0503f47b007b6f6b
Signed-off-by: Christopher Braga <cbraga@codeaurora.org>
Signed-off-by: Tatenda Chipeperekwa <tatendac@codeaurora.org>
This commit is contained in:
Christopher Braga 2019-01-24 12:59:42 -05:00 committed by Tatenda Chipeperekwa
parent 86411ada84
commit 0d3fb743c8
3 changed files with 207 additions and 9 deletions

View file

@ -13,12 +13,15 @@
#include <linux/debugfs.h>
#include <linux/of_device.h>
#include <linux/i2c.h>
#include <linux/list.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include <linux/hdcp_qseecom.h>
#include "sde_kms.h"
#define MAX_STREAM_COUNT 2
enum sde_hdcp_client_id {
HDCP_CLIENT_HDMI,
HDCP_CLIENT_DP,
@ -38,6 +41,18 @@ enum sde_hdcp_version {
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 device *msm_hdcp_dev;
struct dss_io_data *core_io;
@ -70,6 +85,10 @@ struct sde_hdcp_ops {
int (*set_mode)(void *input, bool mst_enabled);
int (*on)(void *input);
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)

View file

@ -64,6 +64,10 @@ struct sde_hdcp_2x_ctrl {
atomic_t hdcp_off;
enum sde_hdcp_2x_device_type device_type;
u8 min_enc_level;
struct list_head stream_handles;
u8 stream_count;
struct stream_info *streams;
u8 num_streams;
struct task_struct *thread;
struct completion response_completion;
@ -315,6 +319,8 @@ static void sde_hdcp_2x_force_encryption(void *data, bool enable)
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 = {HDCP_TRANSPORT_CMD_INVALID};
hdcp->authenticated = false;
@ -322,10 +328,20 @@ static void sde_hdcp_2x_clean(struct sde_hdcp_2x_ctrl *hdcp)
cdata.context = hdcp->client_data;
cdata.cmd = HDCP_TRANSPORT_CMD_STATUS_FAILED;
if (!atomic_read(&hdcp->hdcp_off))
sde_hdcp_2x_wakeup_client(hdcp, &cdata);
while (!list_empty(&hdcp->stream_handles)) {
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);
}
@ -546,7 +562,8 @@ static void sde_hdcp_2x_msg_recvd(struct sde_hdcp_2x_ctrl *hdcp)
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;
message_id_bytes = 1;
}
@ -632,6 +649,147 @@ static void sde_hdcp_2x_msg_recvd(struct sde_hdcp_2x_ctrl *hdcp)
sde_hdcp_2x_clean(hdcp);
}
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, i;
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 (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)
sde_hdcp_2x_query_stream(hdcp);
}
static void sde_hdcp_2x_close_stream(struct sde_hdcp_2x_ctrl *hdcp)
{
int rc;
size_t iterations, i;
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 (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)
sde_hdcp_2x_query_stream(hdcp);
}
/** sde_hdcp_2x_wakeup() - wakeup the module to execute a requested command
* @data: data required for executing corresponding command.
*
@ -655,6 +813,8 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)
hdcp->timeout_left = data->timeout;
hdcp->total_message_length = data->total_message_length;
hdcp->min_enc_level = data->min_enc_level;
hdcp->streams = data->streams;
hdcp->num_streams = data->num_streams;
if (!completion_done(&hdcp->response_completion))
complete_all(&hdcp->response_completion);
@ -743,6 +903,12 @@ static int sde_hdcp_2x_main(void *data)
}
sde_hdcp_2x_query_stream(hdcp);
break;
case HDCP_2X_CMD_OPEN_STREAMS:
sde_hdcp_2x_open_stream(hdcp);
break;
case HDCP_2X_CMD_CLOSE_STREAMS:
sde_hdcp_2x_close_stream(hdcp);
break;
default:
break;
}
@ -787,6 +953,7 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data)
goto unlock;
}
INIT_LIST_HEAD(&hdcp->stream_handles);
hdcp->client_data = data->client_data;
hdcp->client_ops = data->client_ops;

View file

@ -27,6 +27,8 @@
* @HDCP_2X_CMD_QUERY_STREAM_TYPE: start content stream processing
* @HDCP_2X_CMD_LINK_FAILED: link failure notification
* @HDCP_2X_CMD_MIN_ENC_LEVEL: trigger minimum encryption level change
* @HDCP_2X_CMD_OPEN_STREAMS: open a virtual channel
* @HDCP_2X_CMD_CLOSE_STREAMS: close a virtual channel
*/
enum sde_hdcp_2x_wakeup_cmd {
HDCP_2X_CMD_INVALID,
@ -42,6 +44,8 @@ enum sde_hdcp_2x_wakeup_cmd {
HDCP_2X_CMD_QUERY_STREAM_TYPE,
HDCP_2X_CMD_LINK_FAILED,
HDCP_2X_CMD_MIN_ENC_LEVEL,
HDCP_2X_CMD_OPEN_STREAMS,
HDCP_2X_CMD_CLOSE_STREAMS,
};
/**
@ -74,11 +78,13 @@ enum sde_hdcp_2x_device_type {
/**
* struct sde_hdcp_2x_lib_wakeup_data - command and data send to HDCP driver
* @cmd: command type
* @context: void pointer to the HDCP driver instance
* @buf: message received from the sink
* @buf_len: length of message received from the sink
* @timeout: time out value for timed transactions
* @cmd: command type
* @context: void pointer to the HDCP driver instance
* @buf: message received from the sink
* @buf_len: length of message received from the sink
* @timeout: time out value for timed transactions
* @streams: list indicating which streams need adjustment
* @num_streams: number of entries in streams
*/
struct sde_hdcp_2x_wakeup_data {
enum sde_hdcp_2x_wakeup_cmd cmd;
@ -86,6 +92,8 @@ struct sde_hdcp_2x_wakeup_data {
uint32_t total_message_length;
uint32_t timeout;
u8 min_enc_level;
struct stream_info *streams;
u8 num_streams;
};
/**
@ -154,6 +162,10 @@ static inline const char *sde_hdcp_2x_cmd_to_str(
return TO_STR(HDCP_2X_CMD_MSG_RECV_TIMEOUT);
case HDCP_2X_CMD_QUERY_STREAM_TYPE:
return TO_STR(HDCP_2X_CMD_QUERY_STREAM_TYPE);
case HDCP_2X_CMD_OPEN_STREAMS:
return TO_STR(HDCP_2X_CMD_OPEN_STREAMS);
case HDCP_2X_CMD_CLOSE_STREAMS:
return TO_STR(HDCP_2X_CMD_CLOSE_STREAMS);
default:
return "UNKNOWN";
}