blob: ea8e84dff74e7bf03a3d30d3c332d9263b854e33 [file] [log] [blame]
Tatenda Chipeperekwa8a77c8a2018-01-30 14:50:11 -08001/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define pr_fmt(fmt) "[msm-hdcp] %s: " fmt, __func__
14
15#include <linux/platform_device.h>
16#include <linux/kernel.h>
17#include <linux/slab.h>
18#include <linux/module.h>
19#include <linux/fs.h>
20#include <linux/file.h>
21#include <linux/uaccess.h>
22#include <linux/cdev.h>
23#include <linux/list.h>
24#include <linux/device.h>
25#include <linux/errno.h>
26#include <linux/msm_hdcp.h>
27#include <linux/of.h>
28
29#define CLASS_NAME "hdcp"
30#define DRIVER_NAME "msm_hdcp"
31
32struct msm_hdcp {
33 struct platform_device *pdev;
34 dev_t dev_num;
35 struct cdev cdev;
36 struct class *class;
37 struct device *device;
38 struct HDCP_V2V1_MSG_TOPOLOGY cached_tp;
39 u32 tp_msgid;
40 void *client_ctx;
41 void (*cb)(void *ctx, int data);
42};
43
44void msm_hdcp_register_cb(struct device *dev, void *ctx,
45 void (*cb)(void *ctx, int data))
46{
47 struct msm_hdcp *hdcp = NULL;
48
49 if (!dev) {
50 pr_err("invalid device pointer\n");
51 return;
52 }
53
54 hdcp = dev_get_drvdata(dev);
55 if (!hdcp) {
56 pr_err("invalid driver pointer\n");
57 return;
58 }
59
60 hdcp->cb = cb;
61 hdcp->client_ctx = ctx;
62}
63
64void msm_hdcp_notify_topology(struct device *dev)
65{
66 char *envp[4];
67 char tp[SZ_16];
68 char ver[SZ_16];
69 struct msm_hdcp *hdcp = NULL;
70
71 if (!dev) {
72 pr_err("invalid device pointer\n");
73 return;
74 }
75
76 hdcp = dev_get_drvdata(dev);
77 if (!hdcp) {
78 pr_err("invalid driver pointer\n");
79 return;
80 }
81
82 snprintf(tp, SZ_16, "%d", DOWN_CHECK_TOPOLOGY);
83 snprintf(ver, SZ_16, "%d", HDCP_V1_TX);
84
85 envp[0] = "HDCP_MGR_EVENT=MSG_READY";
86 envp[1] = tp;
87 envp[2] = ver;
88 envp[3] = NULL;
89
90 kobject_uevent_env(&hdcp->device->kobj, KOBJ_CHANGE, envp);
91}
92
93void msm_hdcp_cache_repeater_topology(struct device *dev,
94 struct HDCP_V2V1_MSG_TOPOLOGY *tp)
95{
96 struct msm_hdcp *hdcp = NULL;
97
98 if (!dev || !tp) {
99 pr_err("invalid input\n");
100 return;
101 }
102
103 hdcp = dev_get_drvdata(dev);
104 if (!hdcp) {
105 pr_err("invalid driver pointer\n");
106 return;
107 }
108
109 memcpy(&hdcp->cached_tp, tp,
110 sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
111}
112
113static ssize_t msm_hdcp_1x_sysfs_rda_tp(struct device *dev,
114 struct device_attribute *attr, char *buf)
115{
116 ssize_t ret = 0;
117 struct msm_hdcp *hdcp = NULL;
118
119 if (!dev) {
120 pr_err("invalid device pointer\n");
121 return -ENODEV;
122 }
123
124 hdcp = dev_get_drvdata(dev);
125 if (!hdcp) {
126 pr_err("invalid driver pointer\n");
127 return -ENODEV;
128 }
129
130 switch (hdcp->tp_msgid) {
131 case DOWN_CHECK_TOPOLOGY:
132 case DOWN_REQUEST_TOPOLOGY:
133 buf[MSG_ID_IDX] = hdcp->tp_msgid;
134 buf[RET_CODE_IDX] = HDCP_AUTHED;
135 ret = HEADER_LEN;
136
137 memcpy(buf + HEADER_LEN, &hdcp->cached_tp,
138 sizeof(struct HDCP_V2V1_MSG_TOPOLOGY));
139
140 ret += sizeof(struct HDCP_V2V1_MSG_TOPOLOGY);
141
142 /* clear the flag once data is read back to user space*/
143 hdcp->tp_msgid = -1;
144 break;
145 default:
146 ret = -EINVAL;
147 }
148
149 return ret;
150}
151
152static ssize_t msm_hdcp_1x_sysfs_wta_tp(struct device *dev,
153 struct device_attribute *attr, const char *buf, size_t count)
154{
155 int msgid = 0;
156 ssize_t ret = count;
157 struct msm_hdcp *hdcp = NULL;
158
159 if (!dev) {
160 pr_err("invalid device pointer\n");
161 return -ENODEV;
162 }
163
164 hdcp = dev_get_drvdata(dev);
165 if (!hdcp) {
166 pr_err("invalid driver pointer\n");
167 return -ENODEV;
168 }
169
170 msgid = buf[0];
171
172 switch (msgid) {
173 case DOWN_CHECK_TOPOLOGY:
174 case DOWN_REQUEST_TOPOLOGY:
175 hdcp->tp_msgid = msgid;
176 break;
177 default:
178 ret = -EINVAL;
179 }
180
181 return ret;
182}
183
184static ssize_t msm_hdcp_2x_sysfs_wta_min_level_change(struct device *dev,
185 struct device_attribute *attr, const char *buf, size_t count)
186{
187 int rc;
188 int min_enc_lvl;
189 ssize_t ret = count;
190 struct msm_hdcp *hdcp = NULL;
191
192 if (!dev) {
193 pr_err("invalid device pointer\n");
194 return -ENODEV;
195 }
196
197 hdcp = dev_get_drvdata(dev);
198 if (!hdcp) {
199 pr_err("invalid driver pointer\n");
200 return -ENODEV;
201 }
202
203 rc = kstrtoint(buf, 10, &min_enc_lvl);
204 if (rc) {
205 pr_err("kstrtoint failed. rc=%d\n", rc);
206 return -EINVAL;
207 }
208
209 if (hdcp->cb && hdcp->client_ctx)
210 hdcp->cb(hdcp->client_ctx, min_enc_lvl);
211
212 return ret;
213}
214
215static DEVICE_ATTR(tp, 0644, msm_hdcp_1x_sysfs_rda_tp,
216 msm_hdcp_1x_sysfs_wta_tp);
217
218static DEVICE_ATTR(min_level_change, 0200, NULL,
219 msm_hdcp_2x_sysfs_wta_min_level_change);
220
221static struct attribute *msm_hdcp_fs_attrs[] = {
222 &dev_attr_tp.attr,
223 &dev_attr_min_level_change.attr,
224 NULL
225};
226
227static struct attribute_group msm_hdcp_fs_attr_group = {
228 .attrs = msm_hdcp_fs_attrs
229};
230
231static int msm_hdcp_open(struct inode *inode, struct file *file)
232{
233 return 0;
234}
235
236static int msm_hdcp_close(struct inode *inode, struct file *file)
237{
238 return 0;
239}
240
241static const struct file_operations msm_hdcp_fops = {
242 .owner = THIS_MODULE,
243 .open = msm_hdcp_open,
244 .release = msm_hdcp_close,
245};
246
247static const struct of_device_id msm_hdcp_dt_match[] = {
248 { .compatible = "qcom,msm-hdcp",},
249 {}
250};
251
252MODULE_DEVICE_TABLE(of, msm_hdcp_dt_match);
253
254static int msm_hdcp_probe(struct platform_device *pdev)
255{
256 int ret;
257 struct msm_hdcp *hdcp;
258
259 hdcp = devm_kzalloc(&pdev->dev, sizeof(struct msm_hdcp), GFP_KERNEL);
260 if (!hdcp)
261 return -ENOMEM;
262
263 hdcp->pdev = pdev;
264
265 platform_set_drvdata(pdev, hdcp);
266
267 ret = alloc_chrdev_region(&hdcp->dev_num, 0, 1, DRIVER_NAME);
268 if (ret < 0) {
269 pr_err("alloc_chrdev_region failed ret = %d\n", ret);
270 goto error_get_dev_num;
271 }
272
273 hdcp->class = class_create(THIS_MODULE, CLASS_NAME);
274 if (IS_ERR(hdcp->class)) {
275 ret = PTR_ERR(hdcp->class);
276 pr_err("couldn't create class rc = %d\n", ret);
277 goto error_class_create;
278 }
279
280 hdcp->device = device_create(hdcp->class, NULL,
281 hdcp->dev_num, NULL, DRIVER_NAME);
282 if (IS_ERR(hdcp->device)) {
283 ret = PTR_ERR(hdcp->device);
284 pr_err("device_create failed %d\n", ret);
285 goto error_class_device_create;
286 }
287
288 cdev_init(&hdcp->cdev, &msm_hdcp_fops);
289 ret = cdev_add(&hdcp->cdev, MKDEV(MAJOR(hdcp->dev_num), 0), 1);
290 if (ret < 0) {
291 pr_err("cdev_add failed %d\n", ret);
292 goto error_cdev_add;
293 }
294
295 ret = sysfs_create_group(&hdcp->device->kobj, &msm_hdcp_fs_attr_group);
296 if (ret)
297 pr_err("unable to register msm_hdcp sysfs nodes\n");
298
299 return 0;
300error_cdev_add:
301 device_destroy(hdcp->class, hdcp->dev_num);
302error_class_device_create:
303 class_destroy(hdcp->class);
304error_class_create:
305 unregister_chrdev_region(hdcp->dev_num, 1);
306error_get_dev_num:
307 devm_kfree(&pdev->dev, hdcp);
308 hdcp = NULL;
309 return ret;
310}
311
312static int msm_hdcp_remove(struct platform_device *pdev)
313{
314 struct msm_hdcp *hdcp;
315
316 hdcp = platform_get_drvdata(pdev);
317 if (!hdcp)
318 return -ENODEV;
319
320 sysfs_remove_group(&hdcp->device->kobj,
321 &msm_hdcp_fs_attr_group);
322 cdev_del(&hdcp->cdev);
323 device_destroy(hdcp->class, hdcp->dev_num);
324 class_destroy(hdcp->class);
325 unregister_chrdev_region(hdcp->dev_num, 1);
326
327 devm_kfree(&pdev->dev, hdcp);
328 hdcp = NULL;
329 return 0;
330}
331
332static struct platform_driver msm_hdcp_driver = {
333 .probe = msm_hdcp_probe,
334 .remove = msm_hdcp_remove,
335 .driver = {
336 .name = "msm_hdcp",
337 .of_match_table = msm_hdcp_dt_match,
338 .pm = NULL,
339 }
340};
341
342static int __init msm_hdcp_init(void)
343{
344 return platform_driver_register(&msm_hdcp_driver);
345}
346
347static void __exit msm_hdcp_exit(void)
348{
349 return platform_driver_unregister(&msm_hdcp_driver);
350}
351
352module_init(msm_hdcp_init);
353module_exit(msm_hdcp_exit);
354
355MODULE_DESCRIPTION("MSM HDCP driver");
356MODULE_LICENSE("GPL v2");