Browse Source

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 <[email protected]>
Tatenda Chipeperekwa 5 years ago
parent
commit
9b75dd6713
6 changed files with 424 additions and 2 deletions
  1. 1 0
      Makefile
  2. 3 0
      hdcp/Makefile
  3. 342 0
      hdcp/msm_hdcp.c
  4. 17 0
      include/linux/msm_hdcp.h
  5. 59 0
      include/uapi/display/hdcp/msm_hdmi_hdcp_mgr.h
  6. 2 2
      msm/sde_hdcp_1x.c

+ 1 - 0
Makefile

@@ -36,3 +36,4 @@ endif
 
 obj-$(CONFIG_DRM_MSM) += msm/
 obj-$(CONFIG_MSM_SDE_ROTATOR) += rotator/
+obj-$(CONFIG_HDCP_QSEECOM) += hdcp/

+ 3 - 0
hdcp/Makefile

@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_HDCP_QSEECOM) += msm_hdcp.o \

+ 342 - 0
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 - 0
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 */

+ 59 - 0
include/uapi/display/hdcp/msm_hdmi_hdcp_mgr.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 */

+ 2 - 2
msm/sde_hdcp_1x.c

@@ -1,6 +1,6 @@
 // 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__
@@ -14,7 +14,7 @@
 #include <linux/msm_hdcp.h>
 #include <drm/drm_dp_helper.h>
 #include "sde_hdcp.h"
-#include "video/msm_hdmi_hdcp_mgr.h"
+#include "hdcp/msm_hdmi_hdcp_mgr.h"
 #include "dp/dp_reg.h"
 
 #define SDE_HDCP_STATE_NAME (sde_hdcp_state_name(hdcp->hdcp_state))