| /* |
| * drivers/amlogic/drm/meson_hdcp_tee.c |
| * |
| * Copyright (C) 2017 Amlogic, Inc. 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 as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * 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. |
| * |
| */ |
| |
| #include <linux/miscdevice.h> |
| #include <drm/drmP.h> |
| #include <linux/amlogic/media/vout/hdmi_tx/hdmi_common.h> |
| #include <linux/amlogic/media/vout/hdmi_tx/hdmi_tx_module.h> |
| #include <linux/uaccess.h> |
| #include <linux/workqueue.h> |
| #include "meson_hdmi.h" |
| #include "meson_hdcp.h" |
| |
| /* ioctl numbers */ |
| enum { |
| TEE_HDCP_START, |
| TEE_HDCP_END, |
| HDCP_DAEMON_LOAD_END, |
| HDCP_DAEMON_REPORT, |
| HDCP_EXE_VER_SET, |
| HDCP_TX_VER_REPORT, |
| HDCP_DOWNSTR_VER_REPORT, |
| HDCP_EXE_VER_REPORT |
| }; |
| |
| #define TEE_HDCP_IOC_START _IOW('P', TEE_HDCP_START, int) |
| #define TEE_HDCP_IOC_END _IOW('P', TEE_HDCP_END, int) |
| |
| #define HDCP_DAEMON_IOC_LOAD_END _IOW('P', HDCP_DAEMON_LOAD_END, int) |
| #define HDCP_DAEMON_IOC_REPORT _IOR('P', HDCP_DAEMON_REPORT, int) |
| |
| #define HDCP_EXE_VER_IOC_SET _IOW('P', HDCP_EXE_VER_SET, int) |
| #define HDCP_TX_VER_IOC_REPORT _IOR('P', HDCP_TX_VER_REPORT, int) |
| #define HDCP_DOWNSTR_VER_IOC_REPORT _IOR('P', HDCP_DOWNSTR_VER_REPORT, int) |
| #define HDCP_EXE_VER_IOC_REPORT _IOR('P', HDCP_EXE_VER_REPORT, int) |
| |
| static long hdcp_comm_ioctl(struct file *file, |
| unsigned int cmd, |
| unsigned long arg) |
| { |
| int rtn_val; |
| unsigned int out_size; |
| struct miscdevice *phdcp_comm_device; |
| struct am_hdmi_tx *am_hdmi; |
| |
| phdcp_comm_device = file->private_data; |
| am_hdmi = container_of(phdcp_comm_device, |
| struct am_hdmi_tx, |
| hdcp_comm_device); |
| |
| switch (cmd) { |
| case TEE_HDCP_IOC_START: |
| rtn_val = 0; |
| get_hdcp_hdmitx_version(am_hdmi); |
| if (am_hdmi->hdcp_tx_type & 0x2) |
| am_hdcp22_init(am_hdmi); |
| break; |
| case TEE_HDCP_IOC_END: |
| rtn_val = 0; |
| break; |
| case HDCP_DAEMON_IOC_LOAD_END: |
| am_hdmi->hdcp_poll_report = am_hdmi->hdcp_report; |
| rtn_val = 0; |
| break; |
| case HDCP_DAEMON_IOC_REPORT: |
| rtn_val = copy_to_user((void __user *)arg, |
| (void *)&am_hdmi->hdcp_report, |
| sizeof(am_hdmi->hdcp_report)); |
| if (rtn_val != 0) { |
| out_size = sizeof(am_hdmi->hdcp_report); |
| DRM_INFO("out_size: %u, leftsize: %u\n", |
| out_size, rtn_val); |
| rtn_val = -1; |
| } |
| break; |
| case HDCP_EXE_VER_IOC_SET: |
| if (arg > 2) { |
| rtn_val = -1; |
| } else { |
| am_hdmi->hdcp_user_type = arg; |
| rtn_val = 0; |
| if (hdmitx_hpd_hw_op(HPD_READ_HPD_GPIO)) { |
| cancel_delayed_work(&am_hdmi->hdcp_prd_proc); |
| flush_workqueue(am_hdmi->hdcp_wq); |
| am_hdcp_disable(am_hdmi); |
| queue_delayed_work(am_hdmi->hdcp_wq, |
| &am_hdmi->hdcp_prd_proc, 0); |
| } |
| } |
| break; |
| case HDCP_TX_VER_IOC_REPORT: |
| rtn_val = copy_to_user((void __user *)arg, |
| (void *)&am_hdmi->hdcp_tx_type, |
| sizeof(am_hdmi->hdcp_tx_type)); |
| if (rtn_val != 0) { |
| out_size = sizeof(am_hdmi->hdcp_tx_type); |
| DRM_INFO("out_size: %u, leftsize: %u\n", |
| out_size, rtn_val); |
| rtn_val = -1; |
| } |
| break; |
| case HDCP_DOWNSTR_VER_IOC_REPORT: |
| rtn_val = copy_to_user((void __user *)arg, |
| (void *)&am_hdmi->hdcp_downstream_type, |
| sizeof(am_hdmi->hdcp_downstream_type)); |
| if (rtn_val != 0) { |
| out_size = sizeof(am_hdmi->hdcp_downstream_type); |
| DRM_INFO("out_size: %u, leftsize: %u\n", |
| out_size, rtn_val); |
| rtn_val = -1; |
| } |
| break; |
| case HDCP_EXE_VER_IOC_REPORT: |
| rtn_val = copy_to_user((void __user *)arg, |
| (void *)&am_hdmi->hdcp_execute_type, |
| sizeof(am_hdmi->hdcp_execute_type)); |
| if (rtn_val != 0) { |
| out_size = sizeof(am_hdmi->hdcp_execute_type); |
| DRM_INFO("out_size: %u, leftsize: %u\n", |
| out_size, rtn_val); |
| rtn_val = -1; |
| } |
| break; |
| default: |
| rtn_val = -EPERM; |
| } |
| return rtn_val; |
| } |
| |
| static unsigned int hdcp_comm_poll(struct file *file, poll_table *wait) |
| { |
| struct miscdevice *phdcp_comm_device; |
| struct am_hdmi_tx *am_hdmi; |
| unsigned int mask = 0; |
| |
| phdcp_comm_device = file->private_data; |
| am_hdmi = container_of(phdcp_comm_device, |
| struct am_hdmi_tx, |
| hdcp_comm_device); |
| poll_wait(file, &am_hdmi->hdcp_comm_queue, wait); |
| if (am_hdmi->hdcp_report != am_hdmi->hdcp_poll_report) { |
| mask = POLLIN | POLLRDNORM; |
| am_hdmi->hdcp_poll_report = am_hdmi->hdcp_report; |
| } |
| return mask; |
| } |
| |
| static const struct file_operations hdcp_comm_file_operations = { |
| .unlocked_ioctl = hdcp_comm_ioctl, |
| #ifdef CONFIG_COMPAT |
| .compat_ioctl = hdcp_comm_ioctl, |
| #endif |
| .poll = hdcp_comm_poll, |
| .owner = THIS_MODULE, |
| }; |
| |
| int hdcp_comm_init(struct am_hdmi_tx *am_hdmi) |
| { |
| am_hdmi->hdcp_user_type = 3; |
| init_waitqueue_head(&am_hdmi->hdcp_comm_queue); |
| am_hdmi->hdcp_wq = alloc_workqueue(DEVICE_NAME, |
| WQ_HIGHPRI | WQ_CPU_INTENSIVE, 0); |
| INIT_DELAYED_WORK(&am_hdmi->hdcp_prd_proc, |
| am_hdcp_enable); |
| am_hdmi->hdcp_comm_device.minor = MISC_DYNAMIC_MINOR; |
| am_hdmi->hdcp_comm_device.name = "tee_comm_hdcp"; |
| am_hdmi->hdcp_comm_device.fops = &hdcp_comm_file_operations; |
| return misc_register(&am_hdmi->hdcp_comm_device); |
| } |
| |
| void hdcp_comm_exit(struct am_hdmi_tx *am_hdmi) |
| { |
| misc_deregister(&am_hdmi->hdcp_comm_device); |
| } |
| |