Loading drivers/gpu/drm/msm/dp/dp_display.c +94 −7 Original line number Diff line number Diff line Loading @@ -241,16 +241,62 @@ static void dp_display_check_source_hdcp_caps(struct dp_display_private *dp) struct sde_hdcp_ops *ops = dev->ops; void *fd = dev->fd; if (!fd || !ops || (dp->hdcp.source_cap & dev->ver)) if (!fd || !ops) 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_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) { struct dp_display_private *dp; Loading @@ -273,6 +319,11 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) dp_display_update_hdcp_info(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; } else { dp_display_update_hdcp_status(dp, true); Loading @@ -299,11 +350,18 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) switch (status->hdcp_state) { case HDCP_STATE_AUTHENTICATING: dp_display_hdcp_register_streams(dp); if (dp->hdcp.ops && dp->hdcp.ops->authenticate) rc = dp->hdcp.ops->authenticate(data); break; case HDCP_STATE_AUTH_FAIL: 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) { rc = ops->reauthenticate(data); if (rc) Loading @@ -314,6 +372,7 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) } break; default: dp_display_hdcp_register_streams(dp); break; } } Loading Loading @@ -721,15 +780,23 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp) { int rc = 0, idx; struct dp_panel *dp_panel; struct dp_link_hdcp_status *status; mutex_lock(&dp->session_lock); status = &dp->link->hdcp_status; dp->is_connected = 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_display_update_hdcp_status(dp, true); } for (idx = DP_STREAM_0; idx < DP_STREAM_MAX; idx++) { if (!dp->active_panels[idx]) continue; Loading Loading @@ -823,8 +890,10 @@ static void dp_display_clean(struct dp_display_private *dp) { int idx; 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); if (dp->hdcp.ops->off) dp->hdcp.ops->off(dp->hdcp.data); Loading Loading @@ -1583,6 +1652,7 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) { struct dp_display_private *dp; struct dp_panel *dp_panel = panel; struct dp_link_hdcp_status *status; int rc = 0; if (!dp_display || !panel) { Loading @@ -1594,19 +1664,36 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) mutex_lock(&dp->session_lock); status = &dp->link->hdcp_status; if (!dp->power_on) { pr_debug("stream already powered off, return\n"); goto end; } if (dp_display_is_hdcp_enabled(dp)) { cancel_delayed_work_sync(&dp->hdcp_cb_work); if (dp_display_is_hdcp_enabled(dp) && 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) dp->hdcp.ops->off(dp->hdcp.data); dp_display_update_hdcp_status(dp, true); } stream: if (dp_panel->audio_supported) dp_panel->audio->off(dp_panel->audio); Loading drivers/gpu/drm/msm/dp/dp_hdcp2p2.c +147 −54 Original line number 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 * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -76,6 +76,25 @@ struct dp_hdcp2p2_interrupts { 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) { if (ctrl->wakeup_cmd == HDCP_TRANSPORT_CMD_AUTHENTICATE) Loading Loading @@ -255,20 +274,57 @@ static void dp_hdcp2p2_reset(struct dp_hdcp2p2_ctrl *ctrl) 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 hdcp_transport_wakeup_data cdata = { HDCP_TRANSPORT_CMD_AUTHENTICATE}; if (!ctrl) { pr_err("invalid input\n"); return; rc = dp_hdcp2p2_valid_handle(ctrl); if (rc) 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) { pr_err("hdcp is off\n"); static int dp_hdcp2p2_on(void *input) { 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; 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); Loading @@ -277,16 +333,18 @@ static void dp_hdcp2p2_off(void *input) kthread_flush_worker(&ctrl->worker); cdata.context = input; dp_hdcp2p2_wakeup(&cdata); sde_hdcp_2x_disable(ctrl->lib_ctx); } static int dp_hdcp2p2_authenticate(void *input) { int rc; struct dp_hdcp2p2_ctrl *ctrl = input; struct hdcp_transport_wakeup_data cdata = { HDCP_TRANSPORT_CMD_AUTHENTICATE}; int rc = 0; rc = dp_hdcp2p2_valid_handle(ctrl); if (rc) return rc; kthread_flush_worker(&ctrl->worker); Loading Loading @@ -407,44 +465,34 @@ static int dp_hdcp2p2_aux_write_message(struct dp_hdcp2p2_ctrl *ctrl, static bool dp_hdcp2p2_feature_supported(void *input) { int rc; struct dp_hdcp2p2_ctrl *ctrl = input; struct sde_hdcp_2x_ops *lib = NULL; bool supported = false; if (!ctrl) { pr_err("invalid input\n"); goto end; } rc = dp_hdcp2p2_valid_handle(ctrl); if (rc) return supported; lib = ctrl->lib; if (!lib) { pr_err("invalid lib ops data\n"); goto end; } if (lib->feature_supported) supported = lib->feature_supported( ctrl->lib_ctx); end: return supported; } static void dp_hdcp2p2_force_encryption(void *data, bool enable) { int rc; struct dp_hdcp2p2_ctrl *ctrl = data; struct sde_hdcp_2x_ops *lib = NULL; if (!ctrl) { pr_err("invalid input\n"); rc = dp_hdcp2p2_valid_handle(ctrl); if (rc) return; } lib = ctrl->lib; if (!lib) { pr_err("invalid lib ops data\n"); return; } if (lib->force_encryption) lib->force_encryption(ctrl->lib_ctx, enable); } Loading Loading @@ -594,19 +642,17 @@ static void dp_hdcp2p2_link_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, auth); cdata.context = ctrl->lib_ctx; if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING) cdata.cmd = HDCP_2X_CMD_START; else cdata.cmd = HDCP_2X_CMD_STOP; if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING) { struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID}; cdata.context = ctrl->lib_ctx; cdata.cmd = HDCP_2X_CMD_START_AUTH; dp_hdcp2p2_wakeup_lib(ctrl, &cdata); } } static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl, u8 *rx_status) Loading Loading @@ -651,41 +697,36 @@ static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl, static int dp_hdcp2p2_cp_irq(void *input) { int rc = 0; int rc; struct dp_hdcp2p2_ctrl *ctrl = input; if (!ctrl) { pr_err("invalid input\n"); return -EINVAL; } rc = dp_hdcp2p2_valid_handle(ctrl); if (rc) return rc; if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTH_FAIL || atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { pr_err("invalid hdcp state\n"); rc = -EINVAL; goto error; return -EINVAL; } ctrl->sink_rx_status = 0; rc = dp_hdcp2p2_read_rx_status(ctrl, &ctrl->sink_rx_status); if (rc) { pr_err("failed to read rx status\n"); goto error; return rc; } pr_debug("sink_rx_status=0x%x\n", ctrl->sink_rx_status); if (!ctrl->sink_rx_status) { pr_debug("not a hdcp 2.2 irq\n"); rc = -EINVAL; goto error; return -EINVAL; } kthread_queue_work(&ctrl->worker, &ctrl->link); return 0; error: return rc; } static int dp_hdcp2p2_isr(void *input) Loading Loading @@ -753,6 +794,51 @@ static bool dp_hdcp2p2_supported(void *input) 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) { struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input; Loading @@ -763,9 +849,13 @@ void sde_dp_hdcp2p2_deinit(void *input) 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); } sde_hdcp_2x_deregister(ctrl->lib_ctx); kthread_stop(ctrl->thread); Loading @@ -786,8 +876,12 @@ void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data) .feature_supported = dp_hdcp2p2_feature_supported, .force_encryption = dp_hdcp2p2_force_encryption, .sink_support = dp_hdcp2p2_supported, .set_mode = dp_hdcp2p2_register, .on = dp_hdcp2p2_on, .off = dp_hdcp2p2_off, .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 = { Loading Loading @@ -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.client_ops = &client_ops; register_data.ops = &hdcp2x_ops; register_data.device_type = HDCP_TXMTR_DP; register_data.client_data = ctrl; rc = sde_hdcp_2x_register(®ister_data); Loading drivers/gpu/drm/msm/sde_hdcp.h +22 −1 Original line number 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 * it under the terms of the GNU General Public License version 2 and Loading @@ -20,12 +20,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, Loading @@ -45,6 +48,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; Loading Loading @@ -74,7 +89,13 @@ struct sde_hdcp_ops { bool (*feature_supported)(void *input); void (*force_encryption)(void *input, bool enable); bool (*sink_support)(void *input); 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) Loading drivers/gpu/drm/msm/sde_hdcp_2x.c +265 −23 Original line number 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 * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -74,12 +74,17 @@ 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; struct kthread_worker worker; struct kthread_work wk_init; struct kthread_work wk_start_auth; struct kthread_work wk_msg_sent; struct kthread_work wk_msg_recvd; struct kthread_work wk_timeout; Loading @@ -87,6 +92,8 @@ struct sde_hdcp_2x_ctrl { struct kthread_work wk_stream; struct kthread_work wk_wait; 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) Loading Loading @@ -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) { struct list_head *element; struct sde_hdcp_stream *stream_entry; struct hdcp_transport_wakeup_data cdata = { HDCP_TRANSPORT_CMD_INVALID }; Loading @@ -384,10 +393,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); } Loading Loading @@ -569,11 +588,6 @@ static void sde_hdcp_2x_msg_sent_work(struct kthread_work *work) struct sde_hdcp_2x_ctrl *hdcp = 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); } Loading @@ -581,12 +595,29 @@ static void sde_hdcp_2x_init(struct sde_hdcp_2x_ctrl *hdcp) { int rc = 0; if (hdcp->wakeup_cmd != HDCP_2X_CMD_START) { pr_err("invalid wakeup command %d\n", hdcp->wakeup_cmd); rc = hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_START, &hdcp->app_data); if (rc) goto exit; 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) goto exit; Loading @@ -600,14 +631,16 @@ static void sde_hdcp_2x_init(struct sde_hdcp_2x_ctrl *hdcp) 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 = 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) { int rc = 0; Loading Loading @@ -664,7 +697,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; } Loading Loading @@ -796,6 +830,162 @@ static void sde_hdcp_2x_wait_for_response_work(struct kthread_work *work) 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) { struct sde_hdcp_2x_ctrl *hdcp; Loading Loading @@ -837,6 +1027,9 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data) HDCP_2X_EXECUTE(init); break; case HDCP_2X_CMD_START_AUTH: HDCP_2X_EXECUTE(start_auth); break; case HDCP_2X_CMD_STOP: atomic_set(&hdcp->hdcp_off, 1); HDCP_2X_EXECUTE(clean); Loading Loading @@ -867,6 +1060,18 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data) HDCP_2X_EXECUTE(stream); 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: pr_err("invalid wakeup command %d\n", hdcp->wakeup_cmd); } Loading Loading @@ -912,19 +1117,18 @@ 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; 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); kthread_init_worker(&hdcp->worker); 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_recvd, sde_hdcp_2x_msg_recvd_work); kthread_init_work(&hdcp->wk_timeout, sde_hdcp_2x_timeout_work); Loading @@ -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_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_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); Loading @@ -957,6 +1164,41 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data) 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) { struct sde_hdcp_2x_ctrl *hdcp = data; Loading @@ -964,8 +1206,8 @@ void sde_hdcp_2x_deregister(void *data) if (!hdcp) return; sde_hdcp_2x_disable(data); kthread_stop(hdcp->thread); mutex_destroy(&hdcp->wakeup_mutex); hdcp2_deinit(hdcp->hdcp2_ctx); kzfree(hdcp); } drivers/gpu/drm/msm/sde_hdcp_2x.h +26 −10 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
drivers/gpu/drm/msm/dp/dp_display.c +94 −7 Original line number Diff line number Diff line Loading @@ -241,16 +241,62 @@ static void dp_display_check_source_hdcp_caps(struct dp_display_private *dp) struct sde_hdcp_ops *ops = dev->ops; void *fd = dev->fd; if (!fd || !ops || (dp->hdcp.source_cap & dev->ver)) if (!fd || !ops) 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_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) { struct dp_display_private *dp; Loading @@ -273,6 +319,11 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) dp_display_update_hdcp_info(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; } else { dp_display_update_hdcp_status(dp, true); Loading @@ -299,11 +350,18 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) switch (status->hdcp_state) { case HDCP_STATE_AUTHENTICATING: dp_display_hdcp_register_streams(dp); if (dp->hdcp.ops && dp->hdcp.ops->authenticate) rc = dp->hdcp.ops->authenticate(data); break; case HDCP_STATE_AUTH_FAIL: 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) { rc = ops->reauthenticate(data); if (rc) Loading @@ -314,6 +372,7 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) } break; default: dp_display_hdcp_register_streams(dp); break; } } Loading Loading @@ -721,15 +780,23 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp) { int rc = 0, idx; struct dp_panel *dp_panel; struct dp_link_hdcp_status *status; mutex_lock(&dp->session_lock); status = &dp->link->hdcp_status; dp->is_connected = 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_display_update_hdcp_status(dp, true); } for (idx = DP_STREAM_0; idx < DP_STREAM_MAX; idx++) { if (!dp->active_panels[idx]) continue; Loading Loading @@ -823,8 +890,10 @@ static void dp_display_clean(struct dp_display_private *dp) { int idx; 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); if (dp->hdcp.ops->off) dp->hdcp.ops->off(dp->hdcp.data); Loading Loading @@ -1583,6 +1652,7 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) { struct dp_display_private *dp; struct dp_panel *dp_panel = panel; struct dp_link_hdcp_status *status; int rc = 0; if (!dp_display || !panel) { Loading @@ -1594,19 +1664,36 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) mutex_lock(&dp->session_lock); status = &dp->link->hdcp_status; if (!dp->power_on) { pr_debug("stream already powered off, return\n"); goto end; } if (dp_display_is_hdcp_enabled(dp)) { cancel_delayed_work_sync(&dp->hdcp_cb_work); if (dp_display_is_hdcp_enabled(dp) && 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) dp->hdcp.ops->off(dp->hdcp.data); dp_display_update_hdcp_status(dp, true); } stream: if (dp_panel->audio_supported) dp_panel->audio->off(dp_panel->audio); Loading
drivers/gpu/drm/msm/dp/dp_hdcp2p2.c +147 −54 Original line number 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 * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -76,6 +76,25 @@ struct dp_hdcp2p2_interrupts { 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) { if (ctrl->wakeup_cmd == HDCP_TRANSPORT_CMD_AUTHENTICATE) Loading Loading @@ -255,20 +274,57 @@ static void dp_hdcp2p2_reset(struct dp_hdcp2p2_ctrl *ctrl) 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 hdcp_transport_wakeup_data cdata = { HDCP_TRANSPORT_CMD_AUTHENTICATE}; if (!ctrl) { pr_err("invalid input\n"); return; rc = dp_hdcp2p2_valid_handle(ctrl); if (rc) 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) { pr_err("hdcp is off\n"); static int dp_hdcp2p2_on(void *input) { 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; 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); Loading @@ -277,16 +333,18 @@ static void dp_hdcp2p2_off(void *input) kthread_flush_worker(&ctrl->worker); cdata.context = input; dp_hdcp2p2_wakeup(&cdata); sde_hdcp_2x_disable(ctrl->lib_ctx); } static int dp_hdcp2p2_authenticate(void *input) { int rc; struct dp_hdcp2p2_ctrl *ctrl = input; struct hdcp_transport_wakeup_data cdata = { HDCP_TRANSPORT_CMD_AUTHENTICATE}; int rc = 0; rc = dp_hdcp2p2_valid_handle(ctrl); if (rc) return rc; kthread_flush_worker(&ctrl->worker); Loading Loading @@ -407,44 +465,34 @@ static int dp_hdcp2p2_aux_write_message(struct dp_hdcp2p2_ctrl *ctrl, static bool dp_hdcp2p2_feature_supported(void *input) { int rc; struct dp_hdcp2p2_ctrl *ctrl = input; struct sde_hdcp_2x_ops *lib = NULL; bool supported = false; if (!ctrl) { pr_err("invalid input\n"); goto end; } rc = dp_hdcp2p2_valid_handle(ctrl); if (rc) return supported; lib = ctrl->lib; if (!lib) { pr_err("invalid lib ops data\n"); goto end; } if (lib->feature_supported) supported = lib->feature_supported( ctrl->lib_ctx); end: return supported; } static void dp_hdcp2p2_force_encryption(void *data, bool enable) { int rc; struct dp_hdcp2p2_ctrl *ctrl = data; struct sde_hdcp_2x_ops *lib = NULL; if (!ctrl) { pr_err("invalid input\n"); rc = dp_hdcp2p2_valid_handle(ctrl); if (rc) return; } lib = ctrl->lib; if (!lib) { pr_err("invalid lib ops data\n"); return; } if (lib->force_encryption) lib->force_encryption(ctrl->lib_ctx, enable); } Loading Loading @@ -594,19 +642,17 @@ static void dp_hdcp2p2_link_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, auth); cdata.context = ctrl->lib_ctx; if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING) cdata.cmd = HDCP_2X_CMD_START; else cdata.cmd = HDCP_2X_CMD_STOP; if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING) { struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID}; cdata.context = ctrl->lib_ctx; cdata.cmd = HDCP_2X_CMD_START_AUTH; dp_hdcp2p2_wakeup_lib(ctrl, &cdata); } } static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl, u8 *rx_status) Loading Loading @@ -651,41 +697,36 @@ static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl, static int dp_hdcp2p2_cp_irq(void *input) { int rc = 0; int rc; struct dp_hdcp2p2_ctrl *ctrl = input; if (!ctrl) { pr_err("invalid input\n"); return -EINVAL; } rc = dp_hdcp2p2_valid_handle(ctrl); if (rc) return rc; if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTH_FAIL || atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { pr_err("invalid hdcp state\n"); rc = -EINVAL; goto error; return -EINVAL; } ctrl->sink_rx_status = 0; rc = dp_hdcp2p2_read_rx_status(ctrl, &ctrl->sink_rx_status); if (rc) { pr_err("failed to read rx status\n"); goto error; return rc; } pr_debug("sink_rx_status=0x%x\n", ctrl->sink_rx_status); if (!ctrl->sink_rx_status) { pr_debug("not a hdcp 2.2 irq\n"); rc = -EINVAL; goto error; return -EINVAL; } kthread_queue_work(&ctrl->worker, &ctrl->link); return 0; error: return rc; } static int dp_hdcp2p2_isr(void *input) Loading Loading @@ -753,6 +794,51 @@ static bool dp_hdcp2p2_supported(void *input) 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) { struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input; Loading @@ -763,9 +849,13 @@ void sde_dp_hdcp2p2_deinit(void *input) 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); } sde_hdcp_2x_deregister(ctrl->lib_ctx); kthread_stop(ctrl->thread); Loading @@ -786,8 +876,12 @@ void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data) .feature_supported = dp_hdcp2p2_feature_supported, .force_encryption = dp_hdcp2p2_force_encryption, .sink_support = dp_hdcp2p2_supported, .set_mode = dp_hdcp2p2_register, .on = dp_hdcp2p2_on, .off = dp_hdcp2p2_off, .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 = { Loading Loading @@ -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.client_ops = &client_ops; register_data.ops = &hdcp2x_ops; register_data.device_type = HDCP_TXMTR_DP; register_data.client_data = ctrl; rc = sde_hdcp_2x_register(®ister_data); Loading
drivers/gpu/drm/msm/sde_hdcp.h +22 −1 Original line number 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 * it under the terms of the GNU General Public License version 2 and Loading @@ -20,12 +20,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, Loading @@ -45,6 +48,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; Loading Loading @@ -74,7 +89,13 @@ struct sde_hdcp_ops { bool (*feature_supported)(void *input); void (*force_encryption)(void *input, bool enable); bool (*sink_support)(void *input); 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) Loading
drivers/gpu/drm/msm/sde_hdcp_2x.c +265 −23 Original line number 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 * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -74,12 +74,17 @@ 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; struct kthread_worker worker; struct kthread_work wk_init; struct kthread_work wk_start_auth; struct kthread_work wk_msg_sent; struct kthread_work wk_msg_recvd; struct kthread_work wk_timeout; Loading @@ -87,6 +92,8 @@ struct sde_hdcp_2x_ctrl { struct kthread_work wk_stream; struct kthread_work wk_wait; 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) Loading Loading @@ -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) { struct list_head *element; struct sde_hdcp_stream *stream_entry; struct hdcp_transport_wakeup_data cdata = { HDCP_TRANSPORT_CMD_INVALID }; Loading @@ -384,10 +393,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); } Loading Loading @@ -569,11 +588,6 @@ static void sde_hdcp_2x_msg_sent_work(struct kthread_work *work) struct sde_hdcp_2x_ctrl *hdcp = 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); } Loading @@ -581,12 +595,29 @@ static void sde_hdcp_2x_init(struct sde_hdcp_2x_ctrl *hdcp) { int rc = 0; if (hdcp->wakeup_cmd != HDCP_2X_CMD_START) { pr_err("invalid wakeup command %d\n", hdcp->wakeup_cmd); rc = hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_START, &hdcp->app_data); if (rc) goto exit; 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) goto exit; Loading @@ -600,14 +631,16 @@ static void sde_hdcp_2x_init(struct sde_hdcp_2x_ctrl *hdcp) 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 = 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) { int rc = 0; Loading Loading @@ -664,7 +697,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; } Loading Loading @@ -796,6 +830,162 @@ static void sde_hdcp_2x_wait_for_response_work(struct kthread_work *work) 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) { struct sde_hdcp_2x_ctrl *hdcp; Loading Loading @@ -837,6 +1027,9 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data) HDCP_2X_EXECUTE(init); break; case HDCP_2X_CMD_START_AUTH: HDCP_2X_EXECUTE(start_auth); break; case HDCP_2X_CMD_STOP: atomic_set(&hdcp->hdcp_off, 1); HDCP_2X_EXECUTE(clean); Loading Loading @@ -867,6 +1060,18 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data) HDCP_2X_EXECUTE(stream); 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: pr_err("invalid wakeup command %d\n", hdcp->wakeup_cmd); } Loading Loading @@ -912,19 +1117,18 @@ 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; 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); kthread_init_worker(&hdcp->worker); 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_recvd, sde_hdcp_2x_msg_recvd_work); kthread_init_work(&hdcp->wk_timeout, sde_hdcp_2x_timeout_work); Loading @@ -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_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_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); Loading @@ -957,6 +1164,41 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data) 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) { struct sde_hdcp_2x_ctrl *hdcp = data; Loading @@ -964,8 +1206,8 @@ void sde_hdcp_2x_deregister(void *data) if (!hdcp) return; sde_hdcp_2x_disable(data); kthread_stop(hdcp->thread); mutex_destroy(&hdcp->wakeup_mutex); hdcp2_deinit(hdcp->hdcp2_ctx); kzfree(hdcp); }
drivers/gpu/drm/msm/sde_hdcp_2x.h +26 −10 File changed.Preview size limit exceeded, changes collapsed. Show changes