disp: hdcp: add driver to handle userspace interactions
Create a new driver 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. This implementation is based on a snapshot of the msm_hdcp driver as of this commit 10ffbfa2c7e03c09 ("drm/msm/dp: Snapshot of DP and supporting files") on kernel 4.19 project. Change-Id: I834620420b8d6a580f1905a2b3250cf4e5b8f293 Signed-off-by: Tatenda Chipeperekwa <tatendac@codeaurora.org>
这个提交包含在:
1
Makefile
1
Makefile
@@ -36,3 +36,4 @@ endif
|
|||||||
|
|
||||||
obj-$(CONFIG_DRM_MSM) += msm/
|
obj-$(CONFIG_DRM_MSM) += msm/
|
||||||
obj-$(CONFIG_MSM_SDE_ROTATOR) += rotator/
|
obj-$(CONFIG_MSM_SDE_ROTATOR) += rotator/
|
||||||
|
obj-$(CONFIG_HDCP_QSEECOM) += hdcp/
|
||||||
|
3
hdcp/Makefile
普通文件
3
hdcp/Makefile
普通文件
@@ -0,0 +1,3 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
obj-$(CONFIG_HDCP_QSEECOM) += msm_hdcp.o \
|
342
hdcp/msm_hdcp.c
普通文件
342
hdcp/msm_hdcp.c
普通文件
@@ -0,0 +1,342 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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, u8 data);
|
||||||
|
};
|
||||||
|
|
||||||
|
void msm_hdcp_register_cb(struct device *dev, void *ctx,
|
||||||
|
void (*cb)(void *ctx, u8 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 tp_show(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 tp_store(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 min_level_change_store(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_RW(tp);
|
||||||
|
|
||||||
|
static DEVICE_ATTR_WO(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);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, hdcp, 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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
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");
|
17
include/linux/msm_hdcp.h
普通文件
17
include/linux/msm_hdcp.h
普通文件
@@ -0,0 +1,17 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __MSM_HDCP_H
|
||||||
|
#define __MSM_HDCP_H
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include "hdcp/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, u8 data));
|
||||||
|
|
||||||
|
#endif /* __MSM_HDCP_H */
|
@@ -0,0 +1,59 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _UAPI__MSM_HDMI_HDCP_MGR_H
|
||||||
|
#define _UAPI__MSM_HDMI_HDCP_MGR_H
|
||||||
|
|
||||||
|
enum DS_TYPE { /* type of downstream device */
|
||||||
|
DS_UNKNOWN,
|
||||||
|
DS_RECEIVER,
|
||||||
|
DS_REPEATER,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MSG_ID_IDX,
|
||||||
|
RET_CODE_IDX,
|
||||||
|
HEADER_LEN,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RET_CODE {
|
||||||
|
HDCP_NOT_AUTHED,
|
||||||
|
HDCP_AUTHED,
|
||||||
|
HDCP_DISABLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MSG_ID { /* List of functions expected to be called after it */
|
||||||
|
DOWN_CHECK_TOPOLOGY,
|
||||||
|
UP_REQUEST_TOPOLOGY,
|
||||||
|
UP_SEND_TOPOLOGY,
|
||||||
|
DOWN_REQUEST_TOPOLOGY,
|
||||||
|
MSG_NUM,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SOURCE_ID {
|
||||||
|
HDCP_V1_TX,
|
||||||
|
HDCP_V1_RX,
|
||||||
|
HDCP_V2_RX,
|
||||||
|
HDCP_V2_TX,
|
||||||
|
SRC_NUM,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* how to parse sysfs params buffer
|
||||||
|
* from hdcp_tx driver.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct HDCP_V2V1_MSG_TOPOLOGY {
|
||||||
|
/* indicates downstream's type */
|
||||||
|
uint32_t ds_type;
|
||||||
|
uint8_t bksv[5];
|
||||||
|
uint8_t dev_count;
|
||||||
|
uint8_t depth;
|
||||||
|
uint8_t ksv_list[5 * 127];
|
||||||
|
uint32_t max_cascade_exceeded;
|
||||||
|
uint32_t max_dev_exceeded;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _UAPI__MSM_HDMI_HDCP_MGR_H */
|
@@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010-2019, The Linux Foundation. All rights reserved.
|
* Copyright (c) 2010-2020, The Linux Foundation. All rights reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define pr_fmt(fmt) "[sde-hdcp1x] %s: " fmt, __func__
|
#define pr_fmt(fmt) "[sde-hdcp1x] %s: " fmt, __func__
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
#include <linux/msm_hdcp.h>
|
#include <linux/msm_hdcp.h>
|
||||||
#include <drm/drm_dp_helper.h>
|
#include <drm/drm_dp_helper.h>
|
||||||
#include "sde_hdcp.h"
|
#include "sde_hdcp.h"
|
||||||
#include "video/msm_hdmi_hdcp_mgr.h"
|
#include "hdcp/msm_hdmi_hdcp_mgr.h"
|
||||||
#include "dp/dp_reg.h"
|
#include "dp/dp_reg.h"
|
||||||
|
|
||||||
#define SDE_HDCP_STATE_NAME (sde_hdcp_state_name(hdcp->hdcp_state))
|
#define SDE_HDCP_STATE_NAME (sde_hdcp_state_name(hdcp->hdcp_state))
|
||||||
|
在新工单中引用
屏蔽一个用户