From: Zheng Yang Date: Mon, 4 Jun 2012 03:22:49 +0000 (+0800) Subject: rk30 hdmi: support hdcp. X-Git-Tag: firefly_0821_release~9138 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=4112ce1e874951c1bb78df76424f2441c68f1a3b;p=firefly-linux-kernel-4.4.55.git rk30 hdmi: support hdcp. --- diff --git a/drivers/video/rockchip/hdmi/Kconfig b/drivers/video/rockchip/hdmi/Kconfig index 5a4de87b4f8a..76ac81f3ce0f 100755 --- a/drivers/video/rockchip/hdmi/Kconfig +++ b/drivers/video/rockchip/hdmi/Kconfig @@ -1,8 +1,15 @@ config HDMI_RK30 - bool "rk30 hdmi support" + bool "RK30 HDMI support" depends on LCDC_RK30 select FB_MODE_HELPERS -# default y help Support rk30 hdmi if you say y here - + +config HDMI_RK30_DEBUG + bool "RK30 HDMI Debugging" + depends on HDMI_RK30 && LCDC_RK30 + default n + help + Enableds verbose debugging the the HDMI drivers + +source "drivers/video/rockchip/hdmi/hdcp/Kconfig" \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/Makefile b/drivers/video/rockchip/hdmi/Makefile index 3dd2ee7ea256..4692311dbd9a 100755 --- a/drivers/video/rockchip/hdmi/Makefile +++ b/drivers/video/rockchip/hdmi/Makefile @@ -1 +1,8 @@ +# +# Makefile for HDMI linux kernel module. +# + +ccflags-$(CONFIG_HDMI_RK30_DEBUG) = -DDEBUG -DHDMI_DEBUG + obj-$(CONFIG_HDMI_RK30) += rk30_hdmi_hw.o rk30_hdmi_edid.o rk30_hdmi_lcdc.o rk30_hdmi_task.o rk30_hdmi_sysfs.o rk30_hdmi.o +obj-$(CONFIG_HDCP_RK30) += hdcp/ diff --git a/drivers/video/rockchip/hdmi/hdcp/Kconfig b/drivers/video/rockchip/hdmi/hdcp/Kconfig new file mode 100755 index 000000000000..1bb3c3a24f56 --- /dev/null +++ b/drivers/video/rockchip/hdmi/hdcp/Kconfig @@ -0,0 +1,14 @@ +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 diff --git a/drivers/video/rockchip/hdmi/hdcp/Makefile b/drivers/video/rockchip/hdmi/hdcp/Makefile new file mode 100755 index 000000000000..108b67ca134e --- /dev/null +++ b/drivers/video/rockchip/hdmi/hdcp/Makefile @@ -0,0 +1,8 @@ +# +# 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 diff --git a/drivers/video/rockchip/hdmi/hdcp/rk30_hdcp.c b/drivers/video/rockchip/hdmi/hdcp/rk30_hdcp.c new file mode 100755 index 000000000000..dbc6b5227b77 --- /dev/null +++ b/drivers/video/rockchip/hdmi/hdcp/rk30_hdcp.c @@ -0,0 +1,565 @@ +#include +#include +#include +#include +#include +#include +#include +#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) +{ + DBG("%s 0x%x", __FUNCTION__, interrupt); + if(interrupt & m_INT_HDCP_ERR) + { + 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); \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/hdcp/rk30_hdmi_hdcp.c b/drivers/video/rockchip/hdmi/hdcp/rk30_hdmi_hdcp.c new file mode 100755 index 000000000000..82ccff1bcb7d --- /dev/null +++ b/drivers/video/rockchip/hdmi/hdcp/rk30_hdmi_hdcp.c @@ -0,0 +1,155 @@ +#include +#include +#include +#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; + } + + + // 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; +} diff --git a/drivers/video/rockchip/hdmi/hdcp/rk30_hdmi_hdcp.h b/drivers/video/rockchip/hdmi/hdcp/rk30_hdmi_hdcp.h new file mode 100755 index 000000000000..3c085720360b --- /dev/null +++ b/drivers/video/rockchip/hdmi/hdcp/rk30_hdmi_hdcp.h @@ -0,0 +1,98 @@ +#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 + +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__ */ \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi.c b/drivers/video/rockchip/hdmi/rk30_hdmi.c index 37ab8575b758..40421c799fd1 100755 --- a/drivers/video/rockchip/hdmi/rk30_hdmi.c +++ b/drivers/video/rockchip/hdmi/rk30_hdmi.c @@ -27,6 +27,22 @@ extern struct rk_lcdc_device_driver * rk_get_lcdc_drv(char *name); extern void hdmi_register_display_sysfs(struct hdmi *hdmi, struct device *parent); extern void hdmi_unregister_display_sysfs(struct hdmi *hdmi); +int rk30_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void), + void (*hdcp_irq_cb)(int status), + int (*hdcp_power_on_cb)(void), + void (*hdcp_power_off_cb)(void)) +{ + if(hdmi == NULL) + return HDMI_ERROR_FALSE; + + hdmi->hdcp_cb = hdcp_cb; + hdmi->hdcp_irq_cb = hdcp_irq_cb; + hdmi->hdcp_power_on_cb = hdcp_power_on_cb; + hdmi->hdcp_power_off_cb = hdcp_power_off_cb; + + return HDMI_ERROR_SUCESS; +} + #ifdef CONFIG_HAS_EARLYSUSPEND static void hdmi_early_suspend(struct early_suspend *h) { @@ -55,6 +71,7 @@ static void hdmi_early_resume(struct early_suspend *h) hdmi_dbg(hdmi->dev, "hdmi exit early resume\n"); mutex_lock(&hdmi->enable_mutex); hdmi->suspend = 0; + rk30_hdmi_initial(); if(hdmi->enable) { enable_irq(hdmi->irq); } @@ -63,10 +80,6 @@ static void hdmi_early_resume(struct early_suspend *h) } #endif - - - - static inline void hdmi_io_remap(void) { unsigned int value; @@ -79,9 +92,6 @@ static inline void hdmi_io_remap(void) // Select LCDC0 as video source and enabled. value = (HDMI_SOURCE_DEFAULT << 14) | (1 << 30); writel(value, GRF_SOC_CON0 + RK30_GRF_BASE); - - // internal hclk = hdmi_hclk/20 - HDMIWrReg(0x800, 19); } static int __devinit rk30_hdmi_probe (struct platform_device *pdev) @@ -128,7 +138,8 @@ static int __devinit rk30_hdmi_probe (struct platform_device *pdev) ret = -ENXIO; goto err0; } - + hdmi->regbase_phy = res->start; + hdmi->regsize_phy = (res->end - res->start) + 1; mem = request_mem_region(res->start, (res->end - res->start) + 1, pdev->name); if (!mem) { @@ -145,6 +156,10 @@ static int __devinit rk30_hdmi_probe (struct platform_device *pdev) goto err1; } + ret = rk30_hdmi_initial(); + if(ret != HDMI_ERROR_SUCESS) + goto err1; + hdmi_io_remap(); hdmi_sys_init(); @@ -207,6 +222,11 @@ err0: static int __devexit rk30_hdmi_remove(struct platform_device *pdev) { if(hdmi) { + mutex_lock(&hdmi->enable_mutex); + if(!hdmi->suspend && hdmi->enable) + disable_irq(hdmi->irq); + mutex_unlock(&hdmi->enable_mutex); + free_irq(hdmi->irq, NULL); flush_workqueue(hdmi->workqueue); destroy_workqueue(hdmi->workqueue); #ifdef CONFIG_SWITCH @@ -217,7 +237,7 @@ static int __devexit rk30_hdmi_remove(struct platform_device *pdev) unregister_early_suspend(&hdmi->early_suspend); #endif iounmap((void*)hdmi->regbase); - // release_mem_region(res->start,(res->end - res->start) + 1); + release_mem_region(hdmi->regbase_phy, hdmi->regsize_phy); clk_disable(hdmi->hclk); fb_destroy_modelist(&hdmi->edid.modelist); if(hdmi->edid.audio) diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi.h b/drivers/video/rockchip/hdmi/rk30_hdmi.h index 83acb6c86e58..45275f39f9a1 100755 --- a/drivers/video/rockchip/hdmi/rk30_hdmi.h +++ b/drivers/video/rockchip/hdmi/rk30_hdmi.h @@ -14,7 +14,7 @@ #ifdef CONFIG_HAS_EARLYSUSPEND #include #endif - +#include #include #include #include "rk_hdmi.h" @@ -45,6 +45,8 @@ struct hdmi { struct clk *hclk; //HDMI AHP clk int regbase; int irq; + int regbase_phy; + int regsize_phy; struct rk_lcdc_device_driver *lcdc; #ifdef CONFIG_SWITCH @@ -78,6 +80,12 @@ struct hdmi { int display; // HDMI display status int xscale; // x direction scale value int yscale; // y directoon scale value + + // call back for hdcp operatoion + void (*hdcp_cb)(void); + void (*hdcp_irq_cb)(int); + int (*hdcp_power_on_cb)(void); + void (*hdcp_power_off_cb)(void); }; extern struct hdmi *hdmi; @@ -93,4 +101,8 @@ extern int hdmi_find_best_mode(struct hdmi* hdmi, int vic); extern int hdmi_ouputmode_select(struct hdmi *hdmi, int edid_ok); extern int hdmi_switch_fb(struct hdmi *hdmi, int vic); extern void hdmi_sys_remove(void); +extern int rk30_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void), + void (*hdcp_irq_cb)(int status), + int (*hdcp_power_on_cb)(void), + void (*hdcp_power_off_cb)(void)); #endif /* __RK30_HDMI_H__ */ diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_edid.c b/drivers/video/rockchip/hdmi/rk30_hdmi_edid.c old mode 100644 new mode 100755 index a5f291e42703..3154bc255134 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_edid.c +++ b/drivers/video/rockchip/hdmi/rk30_hdmi_edid.c @@ -88,7 +88,7 @@ static int hdmi_edid_parse_dtd(unsigned char *block, struct fb_videomode *mode) static int hdmi_edid_parse_base(unsigned char *buf, int *extend_num, struct hdmi_edid *pedid) { - int rc; + int rc, i; if(buf == NULL || extend_num == NULL) return E_HDMI_EDID_PARAM; diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_hw.c b/drivers/video/rockchip/hdmi/rk30_hdmi_hw.c index c8504f62a085..572717486330 100755 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_hw.c +++ b/drivers/video/rockchip/hdmi/rk30_hdmi_hw.c @@ -11,6 +11,18 @@ static inline void delay100us(void) msleep(1); } +int rk30_hdmi_initial(void) +{ + int rc = HDMI_ERROR_SUCESS; + // internal hclk = hdmi_hclk/20 + HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV); + + if(hdmi->hdcp_power_on_cb) + rc = hdmi->hdcp_power_on_cb(); + + return rc; +} + static void rk30_hdmi_set_pwr_mode(int mode) { if(hdmi->pwr_mode == mode) @@ -88,6 +100,7 @@ int rk30_hdmi_read_edid(int block, unsigned char *buff) { spin_lock_irqsave(&hdmi->irq_lock, flags); interrupt = edid_result; + edid_result = 0; spin_unlock_irqrestore(&hdmi->irq_lock, flags); if(interrupt & (m_INT_EDID_ERR | m_INT_EDID_READY)) break; @@ -111,7 +124,7 @@ int rk30_hdmi_read_edid(int block, unsigned char *buff) break; } if(interrupt & m_INT_EDID_ERR) - hdmi_dbg(hdmi->dev, "[%s] edid read error\n", __FUNCTION__); + hdmi_err(hdmi->dev, "[%s] edid read error\n", __FUNCTION__); } // Disable edid interrupt @@ -178,7 +191,7 @@ static void rk30_hdmi_config_phy(unsigned char vic) rk30_hdmi_config_phy_reg(0x178, 0x00); break; default: - hdmi_dbg(hdmi->dev, "not support such vic %d\n", vic); + hdmi_err(hdmi->dev, "not support such vic %d\n", vic); break; } } @@ -332,13 +345,17 @@ int rk30_hdmi_config_video(struct rk30_hdmi_video_para *vpara) struct fb_videomode *mode; hdmi_dbg(hdmi->dev, "[%s]\n", __FUNCTION__); - if(vpara == NULL) + if(vpara == NULL) { + hdmi_err(hdmi->dev, "[%s] input parameter error\n", __FUNCTION__); return -1; - + } if(hdmi->pwr_mode == PWR_SAVE_MODE_E) rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_D); if(hdmi->pwr_mode == PWR_SAVE_MODE_D || hdmi->pwr_mode == PWR_SAVE_MODE_A) rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_B); + + if(hdmi->hdcp_power_off_cb) + hdmi->hdcp_power_off_cb(); // Input video mode is RGB24bit, Data enable signal from external HDMIMskReg(value, AV_CTRL1, m_INPUT_VIDEO_MODE | m_DE_SIGNAL_SELECT, \ @@ -355,7 +372,7 @@ int rk30_hdmi_config_video(struct rk30_hdmi_video_para *vpara) mode = (struct fb_videomode *)hdmi_vic_to_videomode(vpara->vic); if(mode == NULL) { - hdmi_dbg(hdmi->dev, "[%s] not found vic %d\n", __FUNCTION__, vpara->vic); + hdmi_err(hdmi->dev, "[%s] not found vic %d\n", __FUNCTION__, vpara->vic); return -ENOENT; } value = v_EXT_VIDEO_ENABLE(1) | v_INTERLACE(mode->vmode); @@ -473,7 +490,7 @@ int rk30_hdmi_config_audio(struct hdmi_audio *audio) N = N_192K; break; default: - dev_err(hdmi->dev, "[%s] not support such sample rate %d\n", __FUNCTION__, audio->rate); + hdmi_err(hdmi->dev, "[%s] not support such sample rate %d\n", __FUNCTION__, audio->rate); return -ENOENT; } switch(audio->word_length) @@ -488,7 +505,7 @@ int rk30_hdmi_config_audio(struct hdmi_audio *audio) word_length = 0x0b; break; default: - dev_err(hdmi->dev, "[%s] not support such word length %d\n", __FUNCTION__, audio->word_length); + hdmi_err(hdmi->dev, "[%s] not support such word length %d\n", __FUNCTION__, audio->word_length); return -ENOENT; } //set_audio_if I2S @@ -524,10 +541,14 @@ void rk30_hdmi_control_output(int enable) HDMIWrReg(VIDEO_SETTING2, 0x03); } else { - // Switch to power save mode_d - rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_D); - // Switch to power save mode_e - rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_E); + if(hdmi->pwr_mode == PWR_SAVE_MODE_B) { + // Switch to power save mode_d + rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_D); + } + if(hdmi->pwr_mode == PWR_SAVE_MODE_D) { + // Switch to power save mode_e + rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_E); + } HDMIWrReg(VIDEO_SETTING2, 0x00); rk30_hdmi_audio_reset(); } @@ -552,8 +573,11 @@ int rk30_hdmi_removed(void) // Disable color space convertion HDMIWrReg(AV_CTRL2, v_CSC_ENABLE(0)); HDMIWrReg(CSC_CONFIG1, v_CSC_MODE(CSC_MODE_AUTO) | v_CSC_BRSWAP_DIABLE(1)); + if(hdmi->hdcp_power_off_cb) + hdmi->hdcp_power_off_cb(); rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_A); - } + } + dev_printk(KERN_INFO , hdmi->dev , "Removed.\n"); return HDMI_ERROR_SUCESS; } @@ -605,6 +629,8 @@ irqreturn_t hdmi_irq(int irq, void *priv) HDMIWrReg(SYS_CTRL, 0x10); hdmi->pwr_mode = PWR_SAVE_MODE_A; } + if(interrupt2 && hdmi->hdcp_irq_cb) + hdmi->hdcp_irq_cb(interrupt2); } return IRQ_HANDLED; } diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_hw.h b/drivers/video/rockchip/hdmi/rk30_hdmi_hw.h index 8a766bcc5770..45f986bf6f2e 100755 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_hw.h +++ b/drivers/video/rockchip/hdmi/rk30_hdmi_hw.h @@ -121,6 +121,20 @@ enum{ #define TMDS_CLOCK_MODE_MASK 0x3 << 6 #define TMDS_CLOCK_MODE(n) (n) << 6 +/* VIDEO_CTRL2 */ +#define VIDEO_SETTING2 0x114 +#define m_UNMUTE (1 << 7) +#define m_MUTE (1 << 6) +#define m_AUDIO_RESET (1 << 2) +#define m_NOT_SEND_AUDIO (1 << 1) +#define m_NOT_SEND_VIDEO (1 << 0) +#define AV_UNMUTE (1 << 7) // Unmute video and audio, send normal video and audio data +#define AV_MUTE (1 << 6) // Mute video and audio, send black video data and silent audio data +#define AUDIO_CAPTURE_RESET (1 << 2) // Reset audio process logic, only available in pwr_e mode. +#define NOT_SEND_AUDIO (1 << 1) // Send silent audio data +#define NOT_SEND_VIDEO (1 << 0) // Send black video data + +/* Color Space Convertion Parameter*/ #define CSC_PARA_C0_H 0x60 #define CSC_PARA_C0_L 0x64 #define CSC_PARA_C1_H 0x68 @@ -172,19 +186,6 @@ enum { #define v_CSC_VID_SELECT(n) (n << 1) #define v_CSC_BRSWAP_DIABLE(n) (n) -/* VIDEO_SETTING2 */ -#define VIDEO_SETTING2 0x114 -#define m_UNMUTE (1 << 7) -#define m_MUTE (1 << 6) -#define m_AUDIO_RESET (1 << 2) -#define m_NOT_SEND_AUDIO (1 << 1) -#define m_NOT_SEND_VIDEO (1 << 0) -#define AV_UNMUTE (1 << 7) // Unmute video and audio, send normal video and audio data -#define AV_MUTE (1 << 6) // Mute video and audio, send black video data and silent audio data -#define AUDIO_CAPTURE_RESET (1 << 2) // Reset audio process logic, only available in pwr_e mode. -#define NOT_SEND_AUDIO (1 << 1) // Send silent audio data -#define NOT_SEND_VIDEO (1 << 0) // Send black video data - /* CONTROL_PACKET_BUF_INDEX */ #define CONTROL_PACKET_BUF_INDEX 0x17c enum { @@ -220,17 +221,7 @@ enum { ACTIVE_ASPECT_RATE_14_9 }; - -/* HDCP_CTRL */ -#define HDCP_CTRL 0x2bc - -enum { - OUTPUT_DVI = 0, - OUTPUT_HDMI -}; -#define m_HDMI_DVI (1 << 1) -#define v_HDMI_DVI(n) (n << 1) - +/* External Video Parameter Setting*/ #define EXT_VIDEO_PARA 0xC0 #define m_VSYNC_OFFSET (0xF << 4) #define m_VSYNC_POLARITY (1 << 3) @@ -294,21 +285,27 @@ enum { #define v_PLLB_BIT16(n) (n << 4) #define v_AML(n) (n) +/* Interrupt Setting */ #define INTR_MASK1 0x248 -#define INTR_MASK2 0x24c -#define INTR_MASK3 0x258 -#define INTR_MASK4 0x25c #define INTR_STATUS1 0x250 + #define m_INT_HOTPLUG (1 << 7) + #define m_INT_MSENS (1 << 6) + #define m_INT_VSYNC (1 << 5) + #define m_INT_AUDIO_FIFO_FULL (1 << 4) + #define m_INT_EDID_READY (1 << 2) + #define m_INT_EDID_ERR (1 << 1) +#define INTR_MASK2 0x24c #define INTR_STATUS2 0x254 + #define m_INT_HDCP_ERR (1 << 7) // HDCP error detected + #define m_INT_BKSV_RPRDY (1 << 6) // BKSV list ready from repeater + #define m_INT_BKSV_RCRDY (1 << 5) // BKSV list ready from receiver + #define m_INT_AUTH_DONE (1 << 4) // HDCP authentication done + #define m_INT_AUTH_READY (1 << 3) // HDCP authentication ready +#define INTR_MASK3 0x258 #define INTR_STATUS3 0x260 -#define INTR_STATUS4 0x264 -#define m_INT_HOTPLUG (1 << 7) -#define m_INT_MSENS (1 << 6) -#define m_INT_VSYNC (1 << 5) -#define m_INT_AUDIO_FIFO_FULL (1 << 4) -#define m_INT_EDID_READY (1 << 2) -#define m_INT_EDID_ERR (1 << 1) +#define INTR_MASK4 0x25c +#define INTR_STATUS4 0x264 #define DDC_READ_FIFO_ADDR 0x200 #define DDC_BUS_FREQ_L 0x204 @@ -337,6 +334,78 @@ enum { #define m_HOTPLUG_STATUS (1 << 7) #define m_MSEN_STATUS (1 << 6) +/* HDCP_CTRL */ +#define HDCP_CTRL 0x2bc + enum { + OUTPUT_DVI = 0, + OUTPUT_HDMI + }; + #define m_HDCP_AUTH_START (1 << 7) // start hdcp + #define m_HDCP_BKSV_PASS (1 << 6) // bksv valid + #define m_HDCP_BKSV_FAILED (1 << 5) // bksv invalid + #define m_HDCP_FRAMED_ENCRYPED (1 << 4) + #define m_HDCP_AUTH_STOP (1 << 3) // stop hdcp + #define m_HDCP_ADV_CIPHER (1 << 2) // advanced cipher mode + #define m_HDMI_DVI (1 << 1) + #define m_HDCP_RESET (1 << 0) // reset hdcp + #define v_HDCP_AUTH_START(n) (n << 7) + #define v_HDCP_BKSV_PASS(n) (n << 6) + #define v_HDCP_BKSV_FAILED(n) (n << 5) + #define v_HDCP_FRAMED_ENCRYPED(n) (n << 4) + #define v_HDCP_AUTH_STOP(n) (n << 3) + #define v_HDCP_ADV_CIPHER(n) (n << 2) + #define v_HDMI_DVI(n) (n << 1) + #define v_HDCP_RESET(n) (n << 0) +#define HDCP_CTRL2 0x340 + +/* HDCP Key Memory Access Control */ +#define HDCP_KEY_ACCESS_CTRL1 0x338 +#define HDCP_KEY_ACCESS_CTRL2 0x33c + #define m_LOAD_FACSIMILE_HDCP_KEY (1 << 1) + #define m_LOAD_HDCP_KEY (1 << 0) +/* HDCP Key Memory Control */ +#define HDCP_KEY_MEM_CTRL 0x348 + #define m_USE_KEY1 (1 << 6) + #define m_USE_KEY2 (1 << 5) + #define m_LOAD_AKSV (1 << 4) + #define m_KSV_SELECTED (1 << 3) + #define m_KSV_VALID (1 << 2) + #define m_KEY_VALID (1 << 1) + #define m_KEY_READY (1 << 0) + #define v_USE_KEY1(n) (n << 6) + #define v_USE_KEY2(n) (n << 5) + #define v_LOAD_AKSV(n) (n << 4) + +/* HDCP B device capacity */ +#define HDCP_BCAPS 0x2f8 + #define m_HDMI_RECEIVED (1 << 7) //If HDCP receiver support HDMI, this bit must be 1. + #define m_REPEATER (1 << 6) + #define m_KSV_FIFO_READY (1 << 5) + #define m_DDC_FAST (1 << 4) + #define m_1_1_FEATURE (1 << 1) + #define m_FAST_REAUTHENTICATION (1 << 0) //For HDMI, this function is supported whether this bit is enabled or not. + +/* HDCP KSV Value */ +#define HDCP_KSV_BYTE0 0x2fc +#define HDCP_KSV_BYTE1 0x300 +#define HDCP_KSV_BYTE2 0x304 +#define HDCP_KSV_BYTE3 0x308 +#define HDCP_KSV_BYTE4 0x30c + +/* HDCP 100 ms timer */ +#define HDCP_TIMER_100MS 0x324 +/* HDCP 5s timer */ +#define HDCP_TIMER_5S 0x328 + +/* HDCP Key ram address */ +#define HDCP_RAM_KEY_KSV1 0x400 +#define HDCP_RAM_KEY_KSV2 0x407 +#define HDCP_RAM_KEY_PRIVATE 0x40e +#define HDCP_KEY_LENGTH 0x13C + + +#define HDCP_ENABLE_HW_AUTH // Enable hardware authentication mode +#define HDMI_INTERANL_CLK_DIV 0x19 #define HDMIRdReg(addr) __raw_readl(hdmi->regbase + addr) #define HDMIWrReg(addr, val) __raw_writel((val), hdmi->regbase + addr); @@ -363,10 +432,12 @@ enum { CSC_ITU709_16_235_TO_RGB_0_255 //YCbCr 16-235 input to RGB 0-255 output according BT709 }; +extern int rk30_hdmi_initial(void); extern int rk30_hdmi_detect_hotplug(void); extern int rk30_hdmi_read_edid(int block, unsigned char *buff); extern int rk30_hdmi_removed(void); extern int rk30_hdmi_config_video(struct rk30_hdmi_video_para *vpara); extern int rk30_hdmi_config_audio(struct hdmi_audio *audio); extern void rk30_hdmi_control_output(int enable); + #endif \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_task.c b/drivers/video/rockchip/hdmi/rk30_hdmi_task.c index b4c9c2c37e77..cf34d4576967 100755 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_task.c +++ b/drivers/video/rockchip/hdmi/rk30_hdmi_task.c @@ -206,6 +206,7 @@ void hdmi_work(struct work_struct *work) hdmi_sys_sleep(); do { + hdmi_sys_show_state(hdmi->state); state_last = hdmi->state; switch(hdmi->state) { @@ -260,7 +261,11 @@ void hdmi_work(struct work_struct *work) if(hdmi->display != HDMI_ENABLE) { rk30_hdmi_control_output(HDMI_ENABLE); hdmi->display = HDMI_ENABLE; + if(hdmi->hdcp_cb) { + hdmi->hdcp_cb(); + } } + if(hdmi->wait == 1) { complete(&hdmi->complete); hdmi->wait = 0; @@ -276,19 +281,18 @@ void hdmi_work(struct work_struct *work) } if(hdmi->state != state_last) trytimes = 0; - - hdmi_sys_show_state(hdmi->state); + }while((hdmi->state != state_last || (rc != HDMI_ERROR_SUCESS) ) && trytimes < HDMI_MAX_TRY_TIMES); - if(trytimes == HDMI_MAX_TRY_TIMES) - { - if(hdmi->hotplug) { - hdmi_sys_remove(); - hdmi->hotplug = HDMI_HPD_REMOVED; - hdmi_sys_sleep(); - - } - } +// if(trytimes == HDMI_MAX_TRY_TIMES) +// { +// if(hdmi->hotplug) { +// hdmi_sys_remove(); +// hdmi->hotplug = HDMI_HPD_REMOVED; +// hdmi_sys_sleep(); +// +// } +// } hdmi_dbg(hdmi->dev, "[%s] done\n", __FUNCTION__); mutex_unlock(&work_mutex); } \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/rk_hdmi.h b/drivers/video/rockchip/hdmi/rk_hdmi.h index baa3a353f0ef..534ba51ab5bd 100755 --- a/drivers/video/rockchip/hdmi/rk_hdmi.h +++ b/drivers/video/rockchip/hdmi/rk_hdmi.h @@ -187,7 +187,8 @@ struct hdmi_edid { extern const struct fb_videomode hdmi_mode[]; -#define HDMI_DEBUG +#define hdmi_err(dev, format, arg...) \ + dev_printk(KERN_ERR , dev , format , ## arg) #ifdef HDMI_DEBUG #define hdmi_dbg(dev, format, arg...) \