drivers/misc: create new module to handle sysfs and topology events

Create a new module to handle sysfs and topology events that are
related to application/userspace layer interactions for HDCP
functionality. In turn, this will create a clear separation from
the HDCP QSEECOM layer that defines the communication mechanism between
the kernel and the TrustZone layers.

CRs-Fixed: 2183436
Change-Id: I048aa95b24044646126e80d8af88893af16e72d3
Signed-off-by: Ajay Singh Parmar <aparmar@codeaurora.org>
Signed-off-by: Tatenda Chipeperekwa <tatendac@codeaurora.org>
This commit is contained in:
Tatenda Chipeperekwa 2018-01-30 14:50:11 -08:00
commit 8a77c8a11d
14 changed files with 518 additions and 447 deletions

View file

@ -809,6 +809,10 @@
<&apps_smmu 0x716 0x1>;
};
qcom_msmhdcp: qcom,msm_hdcp {
compatible = "qcom,msm-hdcp";
};
qcom_crypto: qcrypto@1de0000 {
compatible = "qcom,qcrypto";
reg = <0x1de0000 0x20000>,

View file

@ -48,11 +48,6 @@ struct dp_hdcp {
void *hdcp1;
void *hdcp2;
int enc_lvl;
bool auth_state;
bool hdcp1_present;
bool hdcp2_present;
bool feature_enabled;
};
@ -93,7 +88,6 @@ struct dp_display_private {
struct work_struct attention_work;
struct mutex hdcp_mutex;
struct mutex session_lock;
int hdcp_status;
unsigned long audio_status;
};
@ -109,9 +103,8 @@ static bool dp_display_framework_ready(struct dp_display_private *dp)
static inline bool dp_display_is_hdcp_enabled(struct dp_display_private *dp)
{
return dp->hdcp.feature_enabled &&
(dp->hdcp.hdcp1_present || dp->hdcp.hdcp2_present) &&
dp->hdcp.ops;
return dp->hdcp.feature_enabled && dp->link->hdcp_status.hdcp_version
&& dp->hdcp.ops;
}
static irqreturn_t dp_display_irq(int irq, void *dev_id)
@ -156,32 +149,25 @@ static void dp_display_hdcp_cb_work(struct work_struct *work)
ops = dp->hdcp.ops;
switch (dp->hdcp_status) {
case HDCP_STATE_AUTHENTICATING:
pr_debug("start authenticaton\n");
pr_debug("%s: %s\n",
sde_hdcp_version(dp->link->hdcp_status.hdcp_version),
sde_hdcp_state_name(dp->link->hdcp_status.hdcp_state));
switch (dp->link->hdcp_status.hdcp_state) {
case HDCP_STATE_AUTHENTICATING:
if (dp->hdcp.ops && dp->hdcp.ops->authenticate)
rc = dp->hdcp.ops->authenticate(dp->hdcp.data);
break;
case HDCP_STATE_AUTHENTICATED:
pr_debug("hdcp authenticated\n");
dp->hdcp.auth_state = true;
break;
case HDCP_STATE_AUTH_FAIL:
dp->hdcp.auth_state = false;
if (dp->power_on) {
pr_debug("Reauthenticating\n");
if (ops && ops->reauthenticate) {
rc = ops->reauthenticate(dp->hdcp.data);
if (rc)
pr_err("reauth failed rc=%d\n", rc);
pr_err("failed rc=%d\n", rc);
}
} else {
pr_debug("not reauthenticating, cable disconnected\n");
}
break;
default:
break;
@ -189,7 +175,7 @@ static void dp_display_hdcp_cb_work(struct work_struct *work)
}
static void dp_display_notify_hdcp_status_cb(void *ptr,
enum sde_hdcp_states status)
enum sde_hdcp_state state)
{
struct dp_display_private *dp = ptr;
@ -198,7 +184,7 @@ static void dp_display_notify_hdcp_status_cb(void *ptr,
return;
}
dp->hdcp_status = status;
dp->link->hdcp_status.hdcp_state = state;
if (dp->dp_display.is_connected)
queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ/4);
@ -214,12 +200,16 @@ static void dp_display_update_hdcp_info(struct dp_display_private *dp)
{
void *fd = NULL;
struct sde_hdcp_ops *ops = NULL;
bool hdcp2_present = false, hdcp1_present = false;
if (!dp) {
pr_err("invalid input\n");
return;
}
dp->link->hdcp_status.hdcp_state = HDCP_STATE_INACTIVE;
dp->link->hdcp_status.hdcp_version = HDCP_VERSION_NONE;
if (!dp->hdcp.feature_enabled) {
pr_debug("feature not enabled\n");
return;
@ -230,26 +220,29 @@ static void dp_display_update_hdcp_info(struct dp_display_private *dp)
ops = sde_dp_hdcp2p2_start(fd);
if (ops && ops->feature_supported)
dp->hdcp.hdcp2_present = ops->feature_supported(fd);
hdcp2_present = ops->feature_supported(fd);
else
dp->hdcp.hdcp2_present = false;
hdcp2_present = false;
pr_debug("hdcp2p2: %s\n",
dp->hdcp.hdcp2_present ? "supported" : "not supported");
hdcp2_present ? "supported" : "not supported");
if (!dp->hdcp.hdcp2_present) {
dp->hdcp.hdcp1_present = hdcp1_check_if_supported_load_app();
if (!hdcp2_present) {
hdcp1_present = hdcp1_check_if_supported_load_app();
if (dp->hdcp.hdcp1_present) {
if (hdcp1_present) {
fd = dp->hdcp.hdcp1;
ops = sde_hdcp_1x_start(fd);
dp->link->hdcp_status.hdcp_version = HDCP_VERSION_1X;
}
} else {
dp->link->hdcp_status.hdcp_version = HDCP_VERSION_2P2;
}
pr_debug("hdcp1x: %s\n",
dp->hdcp.hdcp1_present ? "supported" : "not supported");
hdcp1_present ? "supported" : "not supported");
if (dp->hdcp.hdcp2_present || dp->hdcp.hdcp1_present) {
if (hdcp2_present || hdcp1_present) {
dp->hdcp.data = fd;
dp->hdcp.ops = ops;
} else {
@ -287,7 +280,6 @@ static int dp_display_initialize_hdcp(struct dp_display_private *dp)
hdcp_init_data.drm_aux = dp->aux->drm_aux;
hdcp_init_data.cb_data = (void *)dp;
hdcp_init_data.workq = dp->wq;
hdcp_init_data.mutex = &dp->hdcp_mutex;
hdcp_init_data.sec_access = true;
hdcp_init_data.notify_status = dp_display_notify_hdcp_status_cb;
hdcp_init_data.core_io = &dp->parser->io.ctrl_io;
@ -298,6 +290,7 @@ static int dp_display_initialize_hdcp(struct dp_display_private *dp)
hdcp_init_data.qfprom_io = &dp->parser->io.qfprom_io;
hdcp_init_data.hdcp_io = &dp->parser->io.hdcp_io;
hdcp_init_data.revision = &dp->panel->link_info.revision;
hdcp_init_data.msm_hdcp_dev = dp->parser->msm_hdcp_dev;
dp->hdcp.hdcp1 = sde_hdcp_1x_init(&hdcp_init_data);
if (IS_ERR_OR_NULL(dp->hdcp.hdcp1)) {
@ -623,7 +616,7 @@ end:
static void dp_display_clean(struct dp_display_private *dp)
{
if (dp_display_is_hdcp_enabled(dp)) {
dp->hdcp_status = HDCP_STATE_INACTIVE;
dp->link->hdcp_status.hdcp_state = HDCP_STATE_INACTIVE;
cancel_delayed_work_sync(&dp->hdcp_cb_work);
if (dp->hdcp.ops->off)
@ -1103,7 +1096,7 @@ static int dp_display_post_enable(struct dp_display *dp_display)
if (dp_display_is_hdcp_enabled(dp)) {
cancel_delayed_work_sync(&dp->hdcp_cb_work);
dp->hdcp_status = HDCP_STATE_AUTHENTICATING;
dp->link->hdcp_status.hdcp_state = HDCP_STATE_AUTHENTICATING;
queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ / 2);
}
@ -1137,7 +1130,7 @@ static int dp_display_pre_disable(struct dp_display *dp_display)
}
if (dp_display_is_hdcp_enabled(dp)) {
dp->hdcp_status = HDCP_STATE_INACTIVE;
dp->link->hdcp_status.hdcp_state = HDCP_STATE_INACTIVE;
cancel_delayed_work_sync(&dp->hdcp_cb_work);
if (dp->hdcp.ops->off)

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2018, 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
@ -19,6 +19,7 @@
#include <linux/types.h>
#include <linux/kthread.h>
#include <linux/hdcp_qseecom.h>
#include <linux/msm_hdcp.h>
#include <drm/drm_dp_helper.h>
#include "sde_hdcp.h"
@ -320,35 +321,16 @@ static void dp_hdcp2p2_min_level_change(void *client_ctx,
struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)client_ctx;
struct hdcp_lib_wakeup_data cdata = {
HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE};
bool enc_notify = true;
int enc_lvl;
if (!ctrl) {
pr_err("invalid input\n");
return;
}
switch (min_enc_level) {
case 0:
enc_lvl = HDCP_STATE_AUTH_ENC_NONE;
break;
case 1:
enc_lvl = HDCP_STATE_AUTH_ENC_1X;
break;
case 2:
enc_lvl = HDCP_STATE_AUTH_ENC_2P2;
break;
default:
enc_notify = false;
}
pr_debug("enc level changed %d\n", min_enc_level);
cdata.context = ctrl->lib_ctx;
dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
if (enc_notify && ctrl->init_data.notify_status)
ctrl->init_data.notify_status(ctrl->init_data.cb_data, enc_lvl);
}
static void dp_hdcp2p2_auth_failed(struct dp_hdcp2p2_ctrl *ctrl)
@ -812,7 +794,6 @@ void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data)
static struct hdcp_client_ops client_ops = {
.wakeup = dp_hdcp2p2_wakeup,
.notify_lvl_change = dp_hdcp2p2_min_level_change,
};
static struct dp_hdcp2p2_int_set int_set1[] = {
{BIT(17), "authentication successful", NULL},
@ -867,6 +848,9 @@ void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data)
goto error;
}
msm_hdcp_register_cb(init_data->msm_hdcp_dev, ctrl,
dp_hdcp2p2_min_level_change);
kthread_init_worker(&ctrl->worker);
kthread_init_work(&ctrl->auth, dp_hdcp2p2_auth_work);

View file

@ -72,6 +72,11 @@ struct dp_link_test_audio {
u32 test_audio_period_ch_8;
};
struct dp_link_hdcp_status {
int hdcp_state;
int hdcp_version;
};
struct dp_link_phy_params {
u32 phy_test_pattern_sel;
u8 v_level;
@ -110,6 +115,7 @@ struct dp_link {
struct dp_link_test_audio test_audio;
struct dp_link_phy_params phy_params;
struct dp_link_params link_params;
struct dp_link_hdcp_status hdcp_status;
u32 (*get_test_bits_depth)(struct dp_link *dp_link, u32 bpp);
int (*process_request)(struct dp_link *dp_link);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
* Copyright (c) 2012-2018, 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
@ -15,6 +15,7 @@
#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include "dp_parser.h"
@ -215,6 +216,30 @@ static int dp_parser_misc(struct dp_parser *parser)
return 0;
}
static int dp_parser_msm_hdcp_dev(struct dp_parser *parser)
{
struct device_node *node;
struct platform_device *pdev;
node = of_find_compatible_node(NULL, NULL, "qcom,msm-hdcp");
if (!node) {
// This is a non-fatal error, module initialization can proceed
pr_warn("couldn't find msm-hdcp node\n");
return 0;
}
pdev = of_find_device_by_node(node);
if (!pdev) {
// This is a non-fatal error, module initialization can proceed
pr_warn("couldn't find msm-hdcp pdev\n");
return 0;
}
parser->msm_hdcp_dev = &pdev->dev;
return 0;
}
static int dp_parser_pinctrl(struct dp_parser *parser)
{
int rc = 0;
@ -643,6 +668,10 @@ static int dp_parser_parse(struct dp_parser *parser)
goto err;
rc = dp_parser_pinctrl(parser);
if (rc)
goto err;
rc = dp_parser_msm_hdcp_dev(parser);
err:
return rc;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
* Copyright (c) 2012-2018, 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
@ -166,6 +166,7 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
* struct dp_parser - DP parser's data exposed to clients
*
* @pdev: platform data of the client
* @msm_hdcp_dev: device pointer for the HDCP driver
* @mp: gpio, regulator and clock related data
* @pinctrl: pin-control related data
* @ctrl_resouce: controller's register address realated data
@ -174,6 +175,7 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
*/
struct dp_parser {
struct platform_device *pdev;
struct device *msm_hdcp_dev;
struct dss_module_power mp[DP_MAX_PM];
struct dp_pinctrl pinctrl;
struct dp_io io;

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2012, 2014-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012, 2014-2018, 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
@ -30,17 +30,21 @@ enum sde_hdcp_client_id {
HDCP_CLIENT_DP,
};
enum sde_hdcp_states {
enum sde_hdcp_state {
HDCP_STATE_INACTIVE,
HDCP_STATE_AUTHENTICATING,
HDCP_STATE_AUTHENTICATED,
HDCP_STATE_AUTH_FAIL,
HDCP_STATE_AUTH_ENC_NONE,
HDCP_STATE_AUTH_ENC_1X,
HDCP_STATE_AUTH_ENC_2P2
};
enum sde_hdcp_version {
HDCP_VERSION_NONE,
HDCP_VERSION_1X,
HDCP_VERSION_2P2
};
struct sde_hdcp_init_data {
struct device *msm_hdcp_dev;
struct dss_io_data *core_io;
struct dss_io_data *dp_ahb;
struct dss_io_data *dp_aux;
@ -52,7 +56,7 @@ struct sde_hdcp_init_data {
struct mutex *mutex;
struct workqueue_struct *workq;
void *cb_data;
void (*notify_status)(void *cb_data, enum sde_hdcp_states status);
void (*notify_status)(void *cb_data, enum sde_hdcp_state state);
u8 sink_rx_status;
unsigned char *revision;
u32 phy_addr;
@ -69,11 +73,32 @@ struct sde_hdcp_ops {
void (*off)(void *hdcp_ctrl);
};
static inline const char *sde_hdcp_state_name(enum sde_hdcp_state hdcp_state)
{
switch (hdcp_state) {
case HDCP_STATE_INACTIVE: return "HDCP_STATE_INACTIVE";
case HDCP_STATE_AUTHENTICATING: return "HDCP_STATE_AUTHENTICATING";
case HDCP_STATE_AUTHENTICATED: return "HDCP_STATE_AUTHENTICATED";
case HDCP_STATE_AUTH_FAIL: return "HDCP_STATE_AUTH_FAIL";
default: return "???";
}
}
static inline const char *sde_hdcp_version(enum sde_hdcp_version hdcp_version)
{
switch (hdcp_version) {
case HDCP_VERSION_NONE: return "HDCP_VERSION_NONE";
case HDCP_VERSION_1X: return "HDCP_VERSION_1X";
case HDCP_VERSION_2P2: return "HDCP_VERSION_2P2";
default: return "???";
}
}
void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data);
void sde_hdcp_1x_deinit(void *input);
struct sde_hdcp_ops *sde_hdcp_1x_start(void *input);
void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data);
void sde_dp_hdcp2p2_deinit(void *input);
const char *sde_hdcp_state_name(enum sde_hdcp_states hdcp_state);
const char *sde_hdcp_state_name(enum sde_hdcp_state hdcp_state);
struct sde_hdcp_ops *sde_dp_hdcp2p2_start(void *input);
#endif /* __SDE_HDCP_H__ */

View file

@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/iopoll.h>
#include <linux/msm_hdcp.h>
#include <linux/hdcp_qseecom.h>
#include <drm/drm_dp_helper.h>
#include "sde_hdcp.h"
@ -213,8 +214,7 @@ struct sde_hdcp_1x {
bool sink_r0_ready;
bool reauth;
bool ksv_ready;
enum sde_hdcp_states hdcp_state;
struct HDCP_V2V1_MSG_TOPOLOGY cached_tp;
enum sde_hdcp_state hdcp_state;
struct HDCP_V2V1_MSG_TOPOLOGY current_tp;
struct delayed_work hdcp_auth_work;
struct completion r0_checked;
@ -227,17 +227,6 @@ struct sde_hdcp_1x {
struct workqueue_struct *workq;
};
const char *sde_hdcp_state_name(enum sde_hdcp_states hdcp_state)
{
switch (hdcp_state) {
case HDCP_STATE_INACTIVE: return "HDCP_STATE_INACTIVE";
case HDCP_STATE_AUTHENTICATING: return "HDCP_STATE_AUTHENTICATING";
case HDCP_STATE_AUTHENTICATED: return "HDCP_STATE_AUTHENTICATED";
case HDCP_STATE_AUTH_FAIL: return "HDCP_STATE_AUTH_FAIL";
default: return "???";
}
}
static int sde_hdcp_1x_count_one(u8 *array, u8 len)
{
int i, j, count = 0;
@ -1062,30 +1051,12 @@ error:
return rc;
}
static void sde_hdcp_1x_cache_topology(struct sde_hdcp_1x *hdcp)
{
if (!hdcp || !hdcp->init_data.dp_ahb || !hdcp->init_data.dp_aux ||
!hdcp->init_data.dp_link || !hdcp->init_data.dp_p0) {
pr_err("invalid input\n");
return;
}
memcpy((void *)&hdcp->cached_tp,
(void *) &hdcp->current_tp,
sizeof(hdcp->cached_tp));
hdcp1_cache_repeater_topology((void *)&hdcp->cached_tp);
}
static void sde_hdcp_1x_notify_topology(void)
{
hdcp1_notify_topology();
}
static void sde_hdcp_1x_update_auth_status(struct sde_hdcp_1x *hdcp)
{
if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATED)) {
sde_hdcp_1x_cache_topology(hdcp);
sde_hdcp_1x_notify_topology();
msm_hdcp_cache_repeater_topology(hdcp->init_data.msm_hdcp_dev,
&hdcp->current_tp);
msm_hdcp_notify_topology(hdcp->init_data.msm_hdcp_dev);
}
if (hdcp->init_data.notify_status &&
@ -1262,11 +1233,9 @@ static void sde_hdcp_1x_off(void *input)
* Also, need to set the state to inactive here so that any ongoing
* reauth works will know that the HDCP session has been turned off.
*/
mutex_lock(hdcp->init_data.mutex);
DSS_REG_W(io, isr->int_reg,
DSS_REG_R(io, isr->int_reg) & ~HDCP_INT_EN);
hdcp->hdcp_state = HDCP_STATE_INACTIVE;
mutex_unlock(hdcp->init_data.mutex);
/* complete any wait pending */
complete_all(&hdcp->sink_r0_available);
@ -1510,7 +1479,7 @@ void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data)
.off = sde_hdcp_1x_off
};
if (!init_data || !init_data->mutex || !init_data->notify_status ||
if (!init_data || !init_data->notify_status ||
!init_data->workq || !init_data->cb_data) {
pr_err("invalid input\n");
goto error;

View file

@ -49,7 +49,8 @@ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o
obj-$(CONFIG_SRAM) += sram.o
obj-y += mic/
obj-$(CONFIG_GENWQE) += genwqe/
obj-$(CONFIG_HDCP_QSEECOM) += hdcp.o
obj-$(CONFIG_HDCP_QSEECOM) += hdcp_qseecom.o
obj-$(CONFIG_HDCP_QSEECOM) += msm_hdcp.o
obj-$(CONFIG_QSEECOM) += qseecom.o
obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o

2240
drivers/misc/hdcp_qseecom.c Normal file

File diff suppressed because it is too large Load diff

356
drivers/misc/msm_hdcp.c Normal file
View file

@ -0,0 +1,356 @@
/* Copyright (c) 2018, 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "[msm-hdcp] %s: " fmt, __func__
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/msm_hdcp.h>
#include <linux/of.h>
#define CLASS_NAME "hdcp"
#define DRIVER_NAME "msm_hdcp"
struct msm_hdcp {
struct platform_device *pdev;
dev_t dev_num;
struct cdev cdev;
struct class *class;
struct device *device;
struct HDCP_V2V1_MSG_TOPOLOGY cached_tp;
u32 tp_msgid;
void *client_ctx;
void (*cb)(void *ctx, int data);
};
void msm_hdcp_register_cb(struct device *dev, void *ctx,
void (*cb)(void *ctx, int data))
{
struct msm_hdcp *hdcp = NULL;
if (!dev) {
pr_err("invalid device pointer\n");
return;
}
hdcp = dev_get_drvdata(dev);
if (!hdcp) {
pr_err("invalid driver pointer\n");
return;
}
hdcp->cb = cb;
hdcp->client_ctx = ctx;
}
void msm_hdcp_notify_topology(struct device *dev)
{
char *envp[4];
char tp[SZ_16];
char ver[SZ_16];
struct msm_hdcp *hdcp = NULL;
if (!dev) {
pr_err("invalid device pointer\n");
return;
}
hdcp = dev_get_drvdata(dev);
if (!hdcp) {
pr_err("invalid driver pointer\n");
return;
}
snprintf(tp, SZ_16, "%d", DOWN_CHECK_TOPOLOGY);
snprintf(ver, SZ_16, "%d", HDCP_V1_TX);
envp[0] = "HDCP_MGR_EVENT=MSG_READY";
envp[1] = tp;
envp[2] = ver;
envp[3] = NULL;
kobject_uevent_env(&hdcp->device->kobj, KOBJ_CHANGE, envp);
}
void msm_hdcp_cache_repeater_topology(struct device *dev,
struct HDCP_V2V1_MSG_TOPOLOGY *tp)
{
struct msm_hdcp *hdcp = NULL;
if (!dev || !tp) {
pr_err("invalid input\n");
return;
}
hdcp = dev_get_drvdata(dev);
if (!hdcp) {
pr_err("invalid driver pointer\n");
return;
}
memcpy(&hdcp->cached_tp, tp,
sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
}
static ssize_t msm_hdcp_1x_sysfs_rda_tp(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t ret = 0;
struct msm_hdcp *hdcp = NULL;
if (!dev) {
pr_err("invalid device pointer\n");
return -ENODEV;
}
hdcp = dev_get_drvdata(dev);
if (!hdcp) {
pr_err("invalid driver pointer\n");
return -ENODEV;
}
switch (hdcp->tp_msgid) {
case DOWN_CHECK_TOPOLOGY:
case DOWN_REQUEST_TOPOLOGY:
buf[MSG_ID_IDX] = hdcp->tp_msgid;
buf[RET_CODE_IDX] = HDCP_AUTHED;
ret = HEADER_LEN;
memcpy(buf + HEADER_LEN, &hdcp->cached_tp,
sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
ret += sizeof(struct HDCP_V2V1_MSG_TOPOLOGY);
/* clear the flag once data is read back to user space*/
hdcp->tp_msgid = -1;
break;
default:
ret = -EINVAL;
}
return ret;
}
static ssize_t msm_hdcp_1x_sysfs_wta_tp(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int msgid = 0;
ssize_t ret = count;
struct msm_hdcp *hdcp = NULL;
if (!dev) {
pr_err("invalid device pointer\n");
return -ENODEV;
}
hdcp = dev_get_drvdata(dev);
if (!hdcp) {
pr_err("invalid driver pointer\n");
return -ENODEV;
}
msgid = buf[0];
switch (msgid) {
case DOWN_CHECK_TOPOLOGY:
case DOWN_REQUEST_TOPOLOGY:
hdcp->tp_msgid = msgid;
break;
default:
ret = -EINVAL;
}
return ret;
}
static ssize_t msm_hdcp_2x_sysfs_wta_min_level_change(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int rc;
int min_enc_lvl;
ssize_t ret = count;
struct msm_hdcp *hdcp = NULL;
if (!dev) {
pr_err("invalid device pointer\n");
return -ENODEV;
}
hdcp = dev_get_drvdata(dev);
if (!hdcp) {
pr_err("invalid driver pointer\n");
return -ENODEV;
}
rc = kstrtoint(buf, 10, &min_enc_lvl);
if (rc) {
pr_err("kstrtoint failed. rc=%d\n", rc);
return -EINVAL;
}
if (hdcp->cb && hdcp->client_ctx)
hdcp->cb(hdcp->client_ctx, min_enc_lvl);
return ret;
}
static DEVICE_ATTR(tp, 0644, msm_hdcp_1x_sysfs_rda_tp,
msm_hdcp_1x_sysfs_wta_tp);
static DEVICE_ATTR(min_level_change, 0200, NULL,
msm_hdcp_2x_sysfs_wta_min_level_change);
static struct attribute *msm_hdcp_fs_attrs[] = {
&dev_attr_tp.attr,
&dev_attr_min_level_change.attr,
NULL
};
static struct attribute_group msm_hdcp_fs_attr_group = {
.attrs = msm_hdcp_fs_attrs
};
static int msm_hdcp_open(struct inode *inode, struct file *file)
{
return 0;
}
static int msm_hdcp_close(struct inode *inode, struct file *file)
{
return 0;
}
static const struct file_operations msm_hdcp_fops = {
.owner = THIS_MODULE,
.open = msm_hdcp_open,
.release = msm_hdcp_close,
};
static const struct of_device_id msm_hdcp_dt_match[] = {
{ .compatible = "qcom,msm-hdcp",},
{}
};
MODULE_DEVICE_TABLE(of, msm_hdcp_dt_match);
static int msm_hdcp_probe(struct platform_device *pdev)
{
int ret;
struct msm_hdcp *hdcp;
hdcp = devm_kzalloc(&pdev->dev, sizeof(struct msm_hdcp), GFP_KERNEL);
if (!hdcp)
return -ENOMEM;
hdcp->pdev = pdev;
platform_set_drvdata(pdev, hdcp);
ret = alloc_chrdev_region(&hdcp->dev_num, 0, 1, DRIVER_NAME);
if (ret < 0) {
pr_err("alloc_chrdev_region failed ret = %d\n", ret);
goto error_get_dev_num;
}
hdcp->class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(hdcp->class)) {
ret = PTR_ERR(hdcp->class);
pr_err("couldn't create class rc = %d\n", ret);
goto error_class_create;
}
hdcp->device = device_create(hdcp->class, NULL,
hdcp->dev_num, NULL, DRIVER_NAME);
if (IS_ERR(hdcp->device)) {
ret = PTR_ERR(hdcp->device);
pr_err("device_create failed %d\n", ret);
goto error_class_device_create;
}
cdev_init(&hdcp->cdev, &msm_hdcp_fops);
ret = cdev_add(&hdcp->cdev, MKDEV(MAJOR(hdcp->dev_num), 0), 1);
if (ret < 0) {
pr_err("cdev_add failed %d\n", ret);
goto error_cdev_add;
}
ret = sysfs_create_group(&hdcp->device->kobj, &msm_hdcp_fs_attr_group);
if (ret)
pr_err("unable to register msm_hdcp sysfs nodes\n");
return 0;
error_cdev_add:
device_destroy(hdcp->class, hdcp->dev_num);
error_class_device_create:
class_destroy(hdcp->class);
error_class_create:
unregister_chrdev_region(hdcp->dev_num, 1);
error_get_dev_num:
devm_kfree(&pdev->dev, hdcp);
hdcp = NULL;
return ret;
}
static int msm_hdcp_remove(struct platform_device *pdev)
{
struct msm_hdcp *hdcp;
hdcp = platform_get_drvdata(pdev);
if (!hdcp)
return -ENODEV;
sysfs_remove_group(&hdcp->device->kobj,
&msm_hdcp_fs_attr_group);
cdev_del(&hdcp->cdev);
device_destroy(hdcp->class, hdcp->dev_num);
class_destroy(hdcp->class);
unregister_chrdev_region(hdcp->dev_num, 1);
devm_kfree(&pdev->dev, hdcp);
hdcp = NULL;
return 0;
}
static struct platform_driver msm_hdcp_driver = {
.probe = msm_hdcp_probe,
.remove = msm_hdcp_remove,
.driver = {
.name = "msm_hdcp",
.of_match_table = msm_hdcp_dt_match,
.pm = NULL,
}
};
static int __init msm_hdcp_init(void)
{
return platform_driver_register(&msm_hdcp_driver);
}
static void __exit msm_hdcp_exit(void)
{
return platform_driver_unregister(&msm_hdcp_driver);
}
module_init(msm_hdcp_init);
module_exit(msm_hdcp_exit);
MODULE_DESCRIPTION("MSM HDCP driver");
MODULE_LICENSE("GPL v2");

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2018, 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
@ -181,11 +181,9 @@ struct hdcp_txmtr_ops {
/**
* struct hdcp_client_ops - call back functions to display transport layer
* @wakeup: wake up display transport layer with a new command
* @notify_lvl_change notify of encryption level changes
*/
struct hdcp_client_ops {
int (*wakeup)(struct hdcp_wakeup_data *data);
void (*notify_lvl_change)(void *client_ctx, int min_lvl);
};
/**
@ -219,6 +217,4 @@ void hdcp_library_deregister(void *phdcpcontext);
bool hdcp1_check_if_supported_load_app(void);
int hdcp1_set_keys(uint32_t *aksv_msb, uint32_t *aksv_lsb);
int hdcp1_set_enc(bool enable);
void hdcp1_cache_repeater_topology(void *hdcp1_cached_tp);
void hdcp1_notify_topology(void);
#endif /* __HDCP_QSEECOM_H */

26
include/linux/msm_hdcp.h Normal file
View file

@ -0,0 +1,26 @@
/* Copyright (c) 2018, 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __MSM_HDCP_H
#define __MSM_HDCP_H
#include <linux/types.h>
#include "video/msm_hdmi_hdcp_mgr.h"
void msm_hdcp_notify_topology(struct device *dev);
void msm_hdcp_cache_repeater_topology(struct device *dev,
struct HDCP_V2V1_MSG_TOPOLOGY *tp);
void msm_hdcp_register_cb(struct device *dev, void *ctx,
void (*cb)(void *ctx, int data));
#endif /* __MSM_HDCP_H */

View file

@ -1,4 +1,4 @@
#ifndef _UAPI__HDMI_HDCP_MGR_H
#ifndef _UAPI__MSM_HDMI_HDCP_MGR_H
#define _UAPI__MSM_HDMI_HDCP_MGR_H
enum DS_TYPE { /* type of downstream device */