mirror of
https://github.com/torvalds/linux.git
synced 2026-06-08 22:52:35 +02:00
rk30 hdmi: Modify Kconfig to support hdcp.
This commit is contained in:
parent
486c766794
commit
28e07bcda6
|
|
@ -13,3 +13,4 @@ config HDMI_RK30_DEBUG
|
|||
help
|
||||
Enableds verbose debugging the the HDMI drivers
|
||||
|
||||
source "drivers/video/rockchip/hdmi/chips/rk30/hdcp/Kconfig"
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
config HDCP_RK30
|
||||
bool "RK30 HDCP support"
|
||||
depends on LCDC_RK30 && HDMI_RK30
|
||||
default n
|
||||
help
|
||||
HDCP Interface. This adds the High Definition Content Protection Interface.
|
||||
See http://www.digital-cp.com/ for HDCP specification.
|
||||
|
||||
config HDCP_RK30_DEBUG
|
||||
bool "RK30 HDCP Debugging"
|
||||
depends on HDCP_RK30
|
||||
default n
|
||||
help
|
||||
Enableds verbose debugging the the HDCP drivers
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
#
|
||||
# Makefile for HDCP linux kernel module.
|
||||
#
|
||||
|
||||
ccflags-$(CONFIG_HDCP_RK30_DEBUG) = -DDEBUG -DHDCP_DEBUG
|
||||
|
||||
obj-$(CONFIG_HDCP_RK30) += hdcp.o
|
||||
hdcp-y := rk30_hdcp.o rk30_hdmi_hdcp.o
|
||||
|
|
@ -1,570 +0,0 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/firmware.h>
|
||||
#include "../rk30_hdmi.h"
|
||||
#include "../rk30_hdmi_hw.h"
|
||||
#include "rk30_hdmi_hdcp.h"
|
||||
|
||||
struct hdcp *hdcp = NULL;
|
||||
|
||||
static void hdcp_work_queue(struct work_struct *work);
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Function: hdcp_submit_work
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
static struct delayed_work *hdcp_submit_work(int event, int delay)
|
||||
{
|
||||
struct hdcp_delayed_work *work;
|
||||
|
||||
DBG("%s event %04x delay %d", __FUNCTION__, event, delay);
|
||||
|
||||
work = kmalloc(sizeof(struct hdcp_delayed_work), GFP_ATOMIC);
|
||||
|
||||
if (work) {
|
||||
INIT_DELAYED_WORK(&work->work, hdcp_work_queue);
|
||||
work->event = event;
|
||||
queue_delayed_work(hdcp->workqueue,
|
||||
&work->work,
|
||||
msecs_to_jiffies(delay));
|
||||
} else {
|
||||
printk(KERN_WARNING "HDCP: Cannot allocate memory to "
|
||||
"create work\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return &work->work;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Function: hdcp_cancel_work
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
static void hdcp_cancel_work(struct delayed_work **work)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (*work) {
|
||||
ret = cancel_delayed_work(*work);
|
||||
if (ret != 1) {
|
||||
ret = cancel_work_sync(&((*work)->work));
|
||||
printk(KERN_INFO "Canceling work failed - "
|
||||
"cancel_work_sync done %d\n", ret);
|
||||
}
|
||||
kfree(*work);
|
||||
*work = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Function: hdcp_wq_authentication_failure
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
static void hdcp_wq_authentication_failure(void)
|
||||
{
|
||||
if (hdcp->hdmi_state == HDMI_STOPPED) {
|
||||
return;
|
||||
}
|
||||
|
||||
rk30_hdcp_disable();
|
||||
rk30_hdmi_control_output(false);
|
||||
|
||||
hdcp_cancel_work(&hdcp->pending_wq_event);
|
||||
|
||||
if (hdcp->retry_cnt && (hdcp->hdmi_state != HDMI_STOPPED)) {
|
||||
if (hdcp->retry_cnt < HDCP_INFINITE_REAUTH) {
|
||||
hdcp->retry_cnt--;
|
||||
printk(KERN_INFO "HDCP: authentication failed - "
|
||||
"retrying, attempts=%d\n",
|
||||
hdcp->retry_cnt);
|
||||
} else
|
||||
printk(KERN_INFO "HDCP: authentication failed - "
|
||||
"retrying\n");
|
||||
|
||||
hdcp->hdcp_state = HDCP_AUTHENTICATION_START;
|
||||
|
||||
hdcp->pending_wq_event = hdcp_submit_work(HDCP_AUTH_REATT_EVENT,
|
||||
HDCP_REAUTH_DELAY);
|
||||
} else {
|
||||
printk(KERN_INFO "HDCP: authentication failed - "
|
||||
"HDCP disabled\n");
|
||||
hdcp->hdcp_state = HDCP_ENABLE_PENDING;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Function: hdcp_wq_start_authentication
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
static void hdcp_wq_start_authentication(void)
|
||||
{
|
||||
int status = HDCP_OK;
|
||||
|
||||
hdcp->hdcp_state = HDCP_AUTHENTICATION_START;
|
||||
|
||||
DBG("HDCP: authentication start");
|
||||
|
||||
status = rk30_hdcp_start_authentication();
|
||||
|
||||
if (status != HDCP_OK) {
|
||||
DBG("HDCP: authentication failed");
|
||||
hdcp_wq_authentication_failure();
|
||||
} else {
|
||||
hdcp->hdcp_state = HDCP_WAIT_KSV_LIST;
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Function: hdcp_wq_check_bksv
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
static void hdcp_wq_check_bksv(void)
|
||||
{
|
||||
int status = HDCP_OK;
|
||||
|
||||
DBG("Check BKSV start");
|
||||
|
||||
status = rk30_hdcp_check_bksv();
|
||||
|
||||
if (status != HDCP_OK) {
|
||||
printk(KERN_INFO "HDCP: Check BKSV failed");
|
||||
hdcp->retry_cnt = 0;
|
||||
hdcp_wq_authentication_failure();
|
||||
}
|
||||
else {
|
||||
DBG("HDCP: Check BKSV successful");
|
||||
|
||||
hdcp->hdcp_state = HDCP_LINK_INTEGRITY_CHECK;
|
||||
|
||||
/* Restore retry counter */
|
||||
if(hdcp->retry_times == 0)
|
||||
hdcp->retry_cnt = HDCP_INFINITE_REAUTH;
|
||||
else
|
||||
hdcp->retry_cnt = hdcp->retry_times;
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Function: hdcp_wq_authentication_sucess
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
static void hdcp_wq_authentication_sucess(void)
|
||||
{
|
||||
printk(KERN_INFO "HDCP: authentication pass");
|
||||
rk30_hdmi_control_output(true);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Function: hdcp_wq_disable
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
static void hdcp_wq_disable(int event)
|
||||
{
|
||||
printk(KERN_INFO "HDCP: disabled");
|
||||
|
||||
hdcp_cancel_work(&hdcp->pending_wq_event);
|
||||
rk30_hdcp_disable();
|
||||
if(event == HDCP_DISABLE_CTL) {
|
||||
hdcp->hdcp_state = HDCP_DISABLED;
|
||||
if(hdcp->hdmi_state == HDMI_STARTED)
|
||||
rk30_hdmi_control_output(true);
|
||||
}
|
||||
else if(event == HDCP_STOP_FRAME_EVENT)
|
||||
hdcp->hdcp_state = HDCP_ENABLE_PENDING;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Function: hdcp_work_queue
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
static void hdcp_work_queue(struct work_struct *work)
|
||||
{
|
||||
struct hdcp_delayed_work *hdcp_w =
|
||||
container_of(work, struct hdcp_delayed_work, work.work);
|
||||
int event = hdcp_w->event;
|
||||
|
||||
mutex_lock(&hdcp->lock);
|
||||
|
||||
DBG("hdcp_work_queue() - START - %u hdmi=%d hdcp=%d evt= %x %d",
|
||||
jiffies_to_msecs(jiffies),
|
||||
hdcp->hdmi_state,
|
||||
hdcp->hdcp_state,
|
||||
(event & 0xFF00) >> 8,
|
||||
event & 0xFF);
|
||||
|
||||
if(event == HDCP_STOP_FRAME_EVENT) {
|
||||
hdcp->hdmi_state = HDMI_STOPPED;
|
||||
}
|
||||
|
||||
if (event == HDCP_DISABLE_CTL || event == HDCP_STOP_FRAME_EVENT) {
|
||||
hdcp_wq_disable(event);
|
||||
}
|
||||
|
||||
if (event & HDCP_WORKQUEUE_SRC)
|
||||
hdcp->pending_wq_event = 0;
|
||||
|
||||
/* First handle HDMI state */
|
||||
if (event == HDCP_START_FRAME_EVENT) {
|
||||
hdcp->pending_start = 0;
|
||||
hdcp->hdmi_state = HDMI_STARTED;
|
||||
}
|
||||
|
||||
/**********************/
|
||||
/* HDCP state machine */
|
||||
/**********************/
|
||||
switch (hdcp->hdcp_state) {
|
||||
case HDCP_DISABLED:
|
||||
/* HDCP enable control or re-authentication event */
|
||||
if (event == HDCP_ENABLE_CTL) {
|
||||
if(hdcp->retry_times == 0)
|
||||
hdcp->retry_cnt = HDCP_INFINITE_REAUTH;
|
||||
else
|
||||
hdcp->retry_cnt = hdcp->retry_times;
|
||||
if (hdcp->hdmi_state == HDMI_STARTED)
|
||||
hdcp_wq_start_authentication();
|
||||
else
|
||||
hdcp->hdcp_state = HDCP_ENABLE_PENDING;
|
||||
}
|
||||
break;
|
||||
|
||||
case HDCP_ENABLE_PENDING:
|
||||
/* HDMI start frame event */
|
||||
if (event == HDCP_START_FRAME_EVENT)
|
||||
hdcp_wq_start_authentication();
|
||||
|
||||
break;
|
||||
|
||||
case HDCP_AUTHENTICATION_START:
|
||||
/* Re-authentication */
|
||||
if (event == HDCP_AUTH_REATT_EVENT)
|
||||
hdcp_wq_start_authentication();
|
||||
|
||||
break;
|
||||
|
||||
case HDCP_WAIT_KSV_LIST:
|
||||
/* KSV failure */
|
||||
if (event == HDCP_FAIL_EVENT) {
|
||||
printk(KERN_INFO "HDCP: KSV switch failure\n");
|
||||
|
||||
hdcp_wq_authentication_failure();
|
||||
}
|
||||
/* KSV list ready event */
|
||||
else if (event == HDCP_KSV_LIST_RDY_EVENT)
|
||||
hdcp_wq_check_bksv();
|
||||
break;
|
||||
|
||||
case HDCP_LINK_INTEGRITY_CHECK:
|
||||
/* Ri failure */
|
||||
if (event == HDCP_FAIL_EVENT) {
|
||||
printk(KERN_INFO "HDCP: Ri check failure\n");
|
||||
hdcp_wq_authentication_failure();
|
||||
}
|
||||
else if(event == HDCP_AUTH_PASS_EVENT)
|
||||
hdcp_wq_authentication_sucess();
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_WARNING "HDCP: error - unknow HDCP state\n");
|
||||
break;
|
||||
}
|
||||
|
||||
kfree(hdcp_w);
|
||||
if(event == HDCP_STOP_FRAME_EVENT)
|
||||
complete(&hdcp->complete);
|
||||
|
||||
mutex_unlock(&hdcp->lock);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Function: hdcp_start_frame_cb
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
static void hdcp_start_frame_cb(void)
|
||||
{
|
||||
DBG("hdcp_start_frame_cb()");
|
||||
|
||||
/* Cancel any pending work */
|
||||
if (hdcp->pending_start)
|
||||
hdcp_cancel_work(&hdcp->pending_start);
|
||||
if (hdcp->pending_wq_event)
|
||||
hdcp_cancel_work(&hdcp->pending_wq_event);
|
||||
|
||||
hdcp->pending_start = hdcp_submit_work(HDCP_START_FRAME_EVENT,
|
||||
HDCP_ENABLE_DELAY);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Function: hdcp_irq_cb
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
static void hdcp_irq_cb(int interrupt)
|
||||
{
|
||||
int value;
|
||||
DBG("%s 0x%x", __FUNCTION__, interrupt);
|
||||
if(interrupt & m_INT_HDCP_ERR)
|
||||
{
|
||||
value = HDMIRdReg(HDCP_ERROR);
|
||||
HDMIWrReg(HDCP_ERROR, value);
|
||||
printk(KERN_INFO "HDCP: Error 0x%02x\n", value);
|
||||
|
||||
if( (hdcp->hdcp_state != HDCP_DISABLED) &&
|
||||
(hdcp->hdcp_state != HDCP_ENABLE_PENDING) )
|
||||
{
|
||||
hdcp_submit_work(HDCP_FAIL_EVENT, 0);
|
||||
}
|
||||
}
|
||||
else if(interrupt & (m_INT_BKSV_RPRDY | m_INT_BKSV_RCRDY))
|
||||
hdcp_submit_work(HDCP_KSV_LIST_RDY_EVENT, 0);
|
||||
else if(interrupt & m_INT_AUTH_DONE)
|
||||
hdcp_submit_work(HDCP_AUTH_PASS_EVENT, 0);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Function: hdcp_power_on_cb
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
static int hdcp_power_on_cb(void)
|
||||
{
|
||||
DBG("%s", __FUNCTION__);
|
||||
return rk30_hdcp_load_key2mem(hdcp->keys);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Function: hdcp_power_off_cb
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
static void hdcp_power_off_cb(void)
|
||||
{
|
||||
DBG("%s", __FUNCTION__);
|
||||
if(!hdcp->enable)
|
||||
return;
|
||||
|
||||
hdcp_cancel_work(&hdcp->pending_start);
|
||||
hdcp_cancel_work(&hdcp->pending_wq_event);
|
||||
init_completion(&hdcp->complete);
|
||||
/* Post event to workqueue */
|
||||
if (hdcp_submit_work(HDCP_STOP_FRAME_EVENT, 0))
|
||||
wait_for_completion_interruptible_timeout(&hdcp->complete,
|
||||
msecs_to_jiffies(2000));
|
||||
}
|
||||
|
||||
// Load HDCP key to external HDCP memory
|
||||
static void hdcp_load_keys_cb(const struct firmware *fw, void *context)
|
||||
{
|
||||
if (!fw) {
|
||||
pr_err("HDCP: failed to load keys\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(fw->size < HDCP_KEY_SIZE) {
|
||||
pr_err("HDCP: firmware wrong size %d\n", fw->size);
|
||||
return;
|
||||
}
|
||||
|
||||
hdcp->keys = kmalloc(HDCP_KEY_SIZE, GFP_KERNEL);
|
||||
if(hdcp->keys == NULL) {
|
||||
pr_err("HDCP: can't allocated space for keys\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(hdcp->keys, fw->data, HDCP_KEY_SIZE);
|
||||
|
||||
rk30_hdcp_load_key2mem(hdcp->keys);
|
||||
printk(KERN_INFO "HDCP: loaded hdcp key success\n");
|
||||
|
||||
if(fw->size > HDCP_KEY_SIZE) {
|
||||
DBG("%s invalid key size %d", __FUNCTION__, fw->size - HDCP_KEY_SIZE);
|
||||
if((fw->size - HDCP_KEY_SIZE) % 5) {
|
||||
pr_err("HDCP: failed to load invalid keys\n");
|
||||
return;
|
||||
}
|
||||
hdcp->invalidkeys = kmalloc(fw->size - HDCP_KEY_SIZE, GFP_KERNEL);
|
||||
if(hdcp->invalidkeys == NULL) {
|
||||
pr_err("HDCP: can't allocated space for invalid keys\n");
|
||||
return;
|
||||
}
|
||||
memcpy(hdcp->invalidkeys, fw->data + HDCP_KEY_SIZE, fw->size - HDCP_KEY_SIZE);
|
||||
hdcp->invalidkey = (fw->size - HDCP_KEY_SIZE)/5;
|
||||
printk(KERN_INFO "HDCP: loaded hdcp invalid key success\n");
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t hdcp_enable_read(struct device *device,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int enable = 0;
|
||||
|
||||
if(hdcp)
|
||||
enable = hdcp->enable;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", enable);
|
||||
}
|
||||
|
||||
static ssize_t hdcp_enable_write(struct device *device,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
int enable;
|
||||
|
||||
if(hdcp == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
sscanf(buf, "%d", &enable);
|
||||
if(hdcp->enable != enable)
|
||||
{
|
||||
/* Post event to workqueue */
|
||||
if(enable) {
|
||||
if (hdcp_submit_work(HDCP_ENABLE_CTL, 0) == 0)
|
||||
return -EFAULT;
|
||||
}
|
||||
else {
|
||||
hdcp_cancel_work(&hdcp->pending_start);
|
||||
hdcp_cancel_work(&hdcp->pending_wq_event);
|
||||
|
||||
/* Post event to workqueue */
|
||||
if (hdcp_submit_work(HDCP_DISABLE_CTL, 0) == 0)
|
||||
return -EFAULT;
|
||||
}
|
||||
hdcp->enable = enable;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR, hdcp_enable_read, hdcp_enable_write);
|
||||
|
||||
static ssize_t hdcp_trytimes_read(struct device *device,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int trytimes = 0;
|
||||
|
||||
if(hdcp)
|
||||
trytimes = hdcp->retry_times;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", trytimes);
|
||||
}
|
||||
|
||||
static ssize_t hdcp_trytimes_wrtie(struct device *device,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
int trytimes;
|
||||
|
||||
if(hdcp == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
sscanf(buf, "%d", &trytimes);
|
||||
if(hdcp->retry_times != trytimes)
|
||||
hdcp->retry_times = trytimes;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static DEVICE_ATTR(trytimes, S_IRUGO|S_IWUSR, hdcp_trytimes_read, hdcp_trytimes_wrtie);
|
||||
|
||||
|
||||
static struct miscdevice mdev;
|
||||
|
||||
static int __init rk30_hdcp_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DBG("[%s] %u", __FUNCTION__, jiffies_to_msecs(jiffies));
|
||||
|
||||
hdcp = kmalloc(sizeof(struct hdcp), GFP_KERNEL);
|
||||
if(!hdcp)
|
||||
{
|
||||
printk(KERN_ERR ">>HDCP: kmalloc fail!");
|
||||
ret = -ENOMEM;
|
||||
goto error0;
|
||||
}
|
||||
memset(hdcp, 0, sizeof(struct hdcp));
|
||||
mutex_init(&hdcp->lock);
|
||||
|
||||
mdev.minor = MISC_DYNAMIC_MINOR;
|
||||
mdev.name = "hdcp";
|
||||
mdev.mode = 0666;
|
||||
if (misc_register(&mdev)) {
|
||||
printk(KERN_ERR "HDCP: Could not add character driver\n");
|
||||
ret = HDMI_ERROR_FALSE;
|
||||
goto error1;
|
||||
}
|
||||
ret = device_create_file(mdev.this_device, &dev_attr_enable);
|
||||
if(ret)
|
||||
{
|
||||
printk(KERN_ERR "HDCP: Could not add sys file enable\n");
|
||||
ret = -EINVAL;
|
||||
goto error2;
|
||||
}
|
||||
|
||||
ret = device_create_file(mdev.this_device, &dev_attr_trytimes);
|
||||
if(ret)
|
||||
{
|
||||
printk(KERN_ERR "HDCP: Could not add sys file trytimes\n");
|
||||
ret = -EINVAL;
|
||||
goto error3;
|
||||
}
|
||||
|
||||
hdcp->workqueue = create_singlethread_workqueue("hdcp");
|
||||
if (hdcp->workqueue == NULL) {
|
||||
printk(KERN_ERR "HDCP,: create workqueue failed.\n");
|
||||
goto error4;
|
||||
}
|
||||
|
||||
|
||||
ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
|
||||
"hdcp.keys", mdev.this_device, GFP_KERNEL,
|
||||
hdcp, hdcp_load_keys_cb);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "HDCP: request_firmware_nowait failed: %d\n", ret);
|
||||
goto error5;
|
||||
}
|
||||
|
||||
rk30_hdmi_register_hdcp_callbacks( hdcp_start_frame_cb,
|
||||
hdcp_irq_cb,
|
||||
hdcp_power_on_cb,
|
||||
hdcp_power_off_cb);
|
||||
|
||||
DBG("%s success %u", __FUNCTION__, jiffies_to_msecs(jiffies));
|
||||
return 0;
|
||||
|
||||
error5:
|
||||
destroy_workqueue(hdcp->workqueue);
|
||||
error4:
|
||||
device_remove_file(mdev.this_device, &dev_attr_trytimes);
|
||||
error3:
|
||||
device_remove_file(mdev.this_device, &dev_attr_enable);
|
||||
error2:
|
||||
misc_deregister(&mdev);
|
||||
error1:
|
||||
if(hdcp->keys)
|
||||
kfree(hdcp->keys);
|
||||
if(hdcp->invalidkeys)
|
||||
kfree(hdcp->invalidkeys);
|
||||
kfree(hdcp);
|
||||
error0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit rk30_hdcp_exit(void)
|
||||
{
|
||||
if(hdcp) {
|
||||
mutex_lock(&hdcp->lock);
|
||||
rk30_hdmi_register_hdcp_callbacks(0, 0, 0, 0);
|
||||
device_remove_file(mdev.this_device, &dev_attr_enable);
|
||||
misc_deregister(&mdev);
|
||||
destroy_workqueue(hdcp->workqueue);
|
||||
if(hdcp->keys)
|
||||
kfree(hdcp->keys);
|
||||
if(hdcp->invalidkeys)
|
||||
kfree(hdcp->invalidkeys);
|
||||
mutex_unlock(&hdcp->lock);
|
||||
kfree(hdcp);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(rk30_hdcp_init);
|
||||
module_exit(rk30_hdcp_exit);
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
#include <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
#include <mach/io.h>
|
||||
#include "../rk30_hdmi.h"
|
||||
#include "../rk30_hdmi_hw.h"
|
||||
#include "rk30_hdmi_hdcp.h"
|
||||
|
||||
static void rk30_hdcp_write_mem(int addr_8, char value)
|
||||
{
|
||||
int temp;
|
||||
int addr_32 = addr_8 - addr_8%4;
|
||||
int shift = (addr_8%4) * 8;
|
||||
|
||||
temp = HDMIRdReg(addr_32);
|
||||
temp &= ~(0xff << shift);
|
||||
temp |= value << shift;
|
||||
// printk("temp is %08x\n", temp);
|
||||
HDMIWrReg(addr_32, temp);
|
||||
}
|
||||
|
||||
int rk30_hdcp_load_key2mem(struct hdcp_keys *key)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(key == NULL) return HDMI_ERROR_FALSE;
|
||||
|
||||
HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV);
|
||||
|
||||
for(i = 0; i < 7; i++)
|
||||
rk30_hdcp_write_mem(HDCP_RAM_KEY_KSV1 + i, key->KSV[i]);
|
||||
for(i = 0; i < 7; i++)
|
||||
rk30_hdcp_write_mem(HDCP_RAM_KEY_KSV2 + i, key->KSV[i]);
|
||||
for(i = 0; i < HDCP_PRIVATE_KEY_SIZE; i++)
|
||||
rk30_hdcp_write_mem(HDCP_RAM_KEY_PRIVATE + i, key->DeviceKey[i]);
|
||||
for(i = 0; i < HDCP_KEY_SHA_SIZE; i++)
|
||||
rk30_hdcp_write_mem(HDCP_RAM_KEY_PRIVATE + HDCP_PRIVATE_KEY_SIZE + i, key->sha1[i]);
|
||||
|
||||
HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV | 0x20);
|
||||
return HDCP_OK;
|
||||
}
|
||||
|
||||
void rk30_hdcp_disable(void)
|
||||
{
|
||||
int temp;
|
||||
// Diable HDCP Interrupt
|
||||
HDMIWrReg(INTR_MASK2, 0x00);
|
||||
// Stop and Reset HDCP
|
||||
HDMIMskReg(temp, HDCP_CTRL, m_HDCP_FRAMED_ENCRYPED | m_HDCP_AUTH_STOP | m_HDCP_RESET,
|
||||
v_HDCP_FRAMED_ENCRYPED(0) | v_HDCP_AUTH_STOP(1) | v_HDCP_RESET(1) );
|
||||
}
|
||||
|
||||
static int rk30_hdcp_load_key(void)
|
||||
{
|
||||
int value, temp = 0;
|
||||
|
||||
if(hdcp->keys == NULL) {
|
||||
pr_err("[%s] HDCP key not loaded.\n", __FUNCTION__);
|
||||
return HDCP_KEY_ERR;
|
||||
}
|
||||
|
||||
value = HDMIRdReg(HDCP_KEY_MEM_CTRL);
|
||||
//Check HDCP key loaded from external HDCP memory
|
||||
while((value & (m_KSV_VALID | m_KEY_VALID | m_KEY_READY)) != (m_KSV_VALID | m_KEY_VALID | m_KEY_READY)) {
|
||||
if(temp > 10) {
|
||||
pr_err("[%s] loaded hdcp key is incorrectable %02x\n", __FUNCTION__, value & 0xFF);
|
||||
return HDCP_KEY_ERR;
|
||||
}
|
||||
//Load HDCP Key from external HDCP memory
|
||||
HDMIWrReg(HDCP_KEY_ACCESS_CTRL2, m_LOAD_HDCP_KEY);
|
||||
msleep(1);
|
||||
value = HDMIRdReg(HDCP_KEY_MEM_CTRL);
|
||||
temp++;
|
||||
}
|
||||
|
||||
return HDCP_OK;
|
||||
}
|
||||
|
||||
|
||||
int rk30_hdcp_start_authentication(void)
|
||||
{
|
||||
int rc, temp;
|
||||
|
||||
rc = rk30_hdcp_load_key();
|
||||
if(rc != HDCP_OK)
|
||||
return rc;
|
||||
|
||||
// Set 100ms & 5 sec timer
|
||||
switch(hdmi->vic)
|
||||
{
|
||||
case HDMI_720x576p_50Hz_4_3:
|
||||
case HDMI_720x576p_50Hz_16_9:
|
||||
case HDMI_1280x720p_50Hz:
|
||||
case HDMI_1920x1080i_50Hz:
|
||||
case HDMI_720x576i_50Hz_4_3:
|
||||
case HDMI_720x576i_50Hz_16_9:
|
||||
case HDMI_1920x1080p_50Hz:
|
||||
HDMIWrReg(HDCP_TIMER_100MS, 5);
|
||||
HDMIWrReg(HDCP_TIMER_5S, 250);
|
||||
break;
|
||||
|
||||
default:
|
||||
HDMIWrReg(HDCP_TIMER_100MS, 0x26);
|
||||
HDMIWrReg(HDCP_TIMER_5S, 0x2c);
|
||||
break;
|
||||
}
|
||||
// Config DDC Clock
|
||||
temp = (hdmi->tmdsclk/HDCP_DDC_CLK)/4;
|
||||
HDMIWrReg(DDC_BUS_FREQ_L, temp & 0xFF);
|
||||
HDMIWrReg(DDC_BUS_FREQ_H, (temp >> 8) & 0xFF);
|
||||
// Enable HDCP Interrupt
|
||||
HDMIWrReg(INTR_MASK2, m_INT_HDCP_ERR | m_INT_BKSV_RPRDY | m_INT_BKSV_RCRDY | m_INT_AUTH_DONE | m_INT_AUTH_READY);
|
||||
// Start HDCP
|
||||
HDMIMskReg(temp, HDCP_CTRL, m_HDCP_AUTH_START | m_HDCP_FRAMED_ENCRYPED, v_HDCP_AUTH_START(1) | v_HDCP_FRAMED_ENCRYPED(0));
|
||||
|
||||
return HDCP_OK;
|
||||
}
|
||||
|
||||
int rk30_hdcp_check_bksv(void)
|
||||
{
|
||||
int i, temp;
|
||||
char bksv[5];
|
||||
char *invalidkey;
|
||||
|
||||
temp = HDMIRdReg(HDCP_BCAPS);
|
||||
DBG("Receiver capacity is 0x%02x", temp);
|
||||
|
||||
#ifdef DEBUG
|
||||
if(temp & m_HDMI_RECEIVED)
|
||||
DBG("Receiver support HDMI");
|
||||
if(temp & m_REPEATER)
|
||||
DBG("Receiver is a repeater");
|
||||
if(temp & m_DDC_FAST)
|
||||
DBG("Receiver support 400K DDC");
|
||||
if(temp & m_1_1_FEATURE)
|
||||
DBG("Receiver support 1.1 features, such as advanced cipher, EESS.");
|
||||
if(temp & m_FAST_REAUTHENTICATION)
|
||||
DBG("Receiver support fast reauthentication.");
|
||||
#endif
|
||||
|
||||
for(i = 0; i < 5; i++) {
|
||||
bksv[i] = HDMIRdReg(HDCP_KSV_BYTE0 + (4 - i)*4) & 0xFF;
|
||||
}
|
||||
|
||||
DBG("bksv is 0x%02x%02x%02x%02x%02x", bksv[0], bksv[1], bksv[2], bksv[3], bksv[4]);
|
||||
|
||||
for(i = 0; i < hdcp->invalidkey; i++)
|
||||
{
|
||||
invalidkey = hdcp->invalidkeys + i *5;
|
||||
if(memcmp(bksv, invalidkey, 5) == 0) {
|
||||
printk(KERN_ERR "HDCP: BKSV was revocated!!!\n");
|
||||
HDMIMskReg(temp, HDCP_CTRL, m_HDCP_BKSV_FAILED | m_HDCP_FRAMED_ENCRYPED, v_HDCP_BKSV_FAILED(1) | v_HDCP_FRAMED_ENCRYPED(0));
|
||||
return HDCP_KSV_ERR;
|
||||
}
|
||||
}
|
||||
HDMIMskReg(temp, HDCP_CTRL, m_HDCP_BKSV_PASS | m_HDCP_FRAMED_ENCRYPED, v_HDCP_BKSV_PASS(1) | v_HDCP_FRAMED_ENCRYPED(1));
|
||||
return HDCP_OK;
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
#ifndef __RK30_HDMI_HDCP_H__
|
||||
#define __RK30_HDMI_HDCP_H__
|
||||
|
||||
/***************************/
|
||||
/* Definitions */
|
||||
/***************************/
|
||||
|
||||
/* Status / error codes */
|
||||
#define HDCP_OK 0
|
||||
#define HDCP_KEY_ERR 1
|
||||
#define HDCP_KSV_ERR 2
|
||||
|
||||
/* Delays */
|
||||
#define HDCP_ENABLE_DELAY 300
|
||||
#define HDCP_REAUTH_DELAY 100
|
||||
|
||||
/* Event source */
|
||||
#define HDCP_SRC_SHIFT 8
|
||||
#define HDCP_IOCTL_SRC (0x1 << HDCP_SRC_SHIFT)
|
||||
#define HDCP_HDMI_SRC (0x2 << HDCP_SRC_SHIFT)
|
||||
#define HDCP_IRQ_SRC (0x4 << HDCP_SRC_SHIFT)
|
||||
#define HDCP_WORKQUEUE_SRC (0x8 << HDCP_SRC_SHIFT)
|
||||
|
||||
/* Event */
|
||||
#define HDCP_ENABLE_CTL (HDCP_IOCTL_SRC | 0)
|
||||
#define HDCP_DISABLE_CTL (HDCP_IOCTL_SRC | 1)
|
||||
#define HDCP_START_FRAME_EVENT (HDCP_HDMI_SRC | 2)
|
||||
#define HDCP_STOP_FRAME_EVENT (HDCP_HDMI_SRC | 3)
|
||||
#define HDCP_KSV_LIST_RDY_EVENT (HDCP_IRQ_SRC | 4)
|
||||
#define HDCP_FAIL_EVENT (HDCP_IRQ_SRC | 5)
|
||||
#define HDCP_AUTH_PASS_EVENT (HDCP_IRQ_SRC | 6)
|
||||
#define HDCP_AUTH_REATT_EVENT (HDCP_WORKQUEUE_SRC | 7)
|
||||
|
||||
/* Key size */
|
||||
#define HDCP_KEY_SIZE 308
|
||||
|
||||
/* Authentication retry times */
|
||||
#define HDCP_INFINITE_REAUTH 0x100
|
||||
|
||||
enum hdcp_states {
|
||||
HDCP_DISABLED,
|
||||
HDCP_ENABLE_PENDING,
|
||||
HDCP_AUTHENTICATION_START,
|
||||
HDCP_WAIT_KSV_LIST,
|
||||
HDCP_LINK_INTEGRITY_CHECK,
|
||||
};
|
||||
|
||||
enum hdmi_states {
|
||||
HDMI_STOPPED,
|
||||
HDMI_STARTED
|
||||
};
|
||||
|
||||
#define HDCP_PRIVATE_KEY_SIZE 280
|
||||
#define HDCP_KEY_SHA_SIZE 20
|
||||
#define HDCP_DDC_CLK 100000
|
||||
|
||||
struct hdcp_keys{
|
||||
u8 KSV[8];
|
||||
u8 DeviceKey[HDCP_PRIVATE_KEY_SIZE];
|
||||
u8 sha1[HDCP_KEY_SHA_SIZE];
|
||||
};
|
||||
|
||||
struct hdcp_delayed_work {
|
||||
struct delayed_work work;
|
||||
int event;
|
||||
};
|
||||
|
||||
struct hdcp {
|
||||
int enable;
|
||||
int retry_times;
|
||||
struct hdcp_keys *keys;
|
||||
int invalidkey;
|
||||
char *invalidkeys;
|
||||
struct mutex lock;
|
||||
struct completion complete;
|
||||
struct workqueue_struct *workqueue;
|
||||
|
||||
enum hdmi_states hdmi_state;
|
||||
enum hdcp_states hdcp_state;
|
||||
|
||||
struct delayed_work *pending_start;
|
||||
struct delayed_work *pending_wq_event;
|
||||
int retry_cnt;
|
||||
};
|
||||
|
||||
extern struct hdcp *hdcp;
|
||||
|
||||
#ifdef HDCP_DEBUG
|
||||
#define DBG(format, ...) \
|
||||
printk(KERN_INFO "HDCP: " format "\n", ## __VA_ARGS__)
|
||||
#else
|
||||
#define DBG(format, ...)
|
||||
#endif
|
||||
|
||||
extern void rk30_hdcp_disable(void);
|
||||
extern int rk30_hdcp_start_authentication(void);
|
||||
extern int rk30_hdcp_check_bksv(void);
|
||||
extern int rk30_hdcp_load_key2mem(struct hdcp_keys *key);
|
||||
#endif /* __RK30_HDMI_HDCP_H__ */
|
||||
Loading…
Reference in New Issue
Block a user