rk30 hdmi: support hdcp.
authorZheng Yang <zhengyang@rock-chips.com>
Mon, 4 Jun 2012 03:22:49 +0000 (11:22 +0800)
committerZheng Yang <zhengyang@rock-chips.com>
Mon, 4 Jun 2012 03:22:49 +0000 (11:22 +0800)
14 files changed:
drivers/video/rockchip/hdmi/Kconfig
drivers/video/rockchip/hdmi/Makefile
drivers/video/rockchip/hdmi/hdcp/Kconfig [new file with mode: 0755]
drivers/video/rockchip/hdmi/hdcp/Makefile [new file with mode: 0755]
drivers/video/rockchip/hdmi/hdcp/rk30_hdcp.c [new file with mode: 0755]
drivers/video/rockchip/hdmi/hdcp/rk30_hdmi_hdcp.c [new file with mode: 0755]
drivers/video/rockchip/hdmi/hdcp/rk30_hdmi_hdcp.h [new file with mode: 0755]
drivers/video/rockchip/hdmi/rk30_hdmi.c
drivers/video/rockchip/hdmi/rk30_hdmi.h
drivers/video/rockchip/hdmi/rk30_hdmi_edid.c [changed mode: 0644->0755]
drivers/video/rockchip/hdmi/rk30_hdmi_hw.c
drivers/video/rockchip/hdmi/rk30_hdmi_hw.h
drivers/video/rockchip/hdmi/rk30_hdmi_task.c
drivers/video/rockchip/hdmi/rk_hdmi.h

index 5a4de87b4f8a2371ba7d1e7c3e83d34261ef0c6d..76ac81f3ce0f39b869f1e9f16b230bc443424439 100755 (executable)
@@ -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
index 3dd2ee7ea256a4349c30cd2b8f045fdd11dfa7c6..4692311dbd9a8725e8f81658415570d206ac2853 100755 (executable)
@@ -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 (executable)
index 0000000..1bb3c3a
--- /dev/null
@@ -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 (executable)
index 0000000..108b67c
--- /dev/null
@@ -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 (executable)
index 0000000..dbc6b52
--- /dev/null
@@ -0,0 +1,565 @@
+#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)
+{
+       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 (executable)
index 0000000..82ccff1
--- /dev/null
@@ -0,0 +1,155 @@
+#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;
+       }
+       
+       
+       // 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 (executable)
index 0000000..3c08572
--- /dev/null
@@ -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
index 37ab8575b75846b283f8f33f13be44a583d04df6..40421c799fd1c15a5c4d1d3df8183a4a2fe75549 100755 (executable)
@@ -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);\r
 extern void hdmi_unregister_display_sysfs(struct hdmi *hdmi);\r
 \r
+int rk30_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void),\r
+                                        void (*hdcp_irq_cb)(int status),\r
+                                        int (*hdcp_power_on_cb)(void),\r
+                                        void (*hdcp_power_off_cb)(void))\r
+{\r
+       if(hdmi == NULL)\r
+               return HDMI_ERROR_FALSE;\r
+\r
+       hdmi->hdcp_cb = hdcp_cb;\r
+       hdmi->hdcp_irq_cb = hdcp_irq_cb;\r
+       hdmi->hdcp_power_on_cb = hdcp_power_on_cb;\r
+       hdmi->hdcp_power_off_cb = hdcp_power_off_cb;\r
+       \r
+       return HDMI_ERROR_SUCESS;\r
+}\r
+\r
 #ifdef CONFIG_HAS_EARLYSUSPEND\r
 static void hdmi_early_suspend(struct early_suspend *h)\r
 {\r
@@ -55,6 +71,7 @@ static void hdmi_early_resume(struct early_suspend *h)
        hdmi_dbg(hdmi->dev, "hdmi exit early resume\n");\r
        mutex_lock(&hdmi->enable_mutex);\r
        hdmi->suspend = 0;\r
+       rk30_hdmi_initial();\r
        if(hdmi->enable) {\r
                enable_irq(hdmi->irq);\r
        }\r
@@ -63,10 +80,6 @@ static void hdmi_early_resume(struct early_suspend *h)
 }\r
 #endif\r
 \r
-\r
-\r
-\r
-\r
 static inline void hdmi_io_remap(void)\r
 {\r
        unsigned int value;\r
@@ -79,9 +92,6 @@ static inline void hdmi_io_remap(void)
        // Select LCDC0 as video source and enabled.\r
        value = (HDMI_SOURCE_DEFAULT << 14) | (1 << 30);\r
        writel(value, GRF_SOC_CON0 + RK30_GRF_BASE);\r
-       \r
-       // internal hclk = hdmi_hclk/20\r
-       HDMIWrReg(0x800, 19);\r
 }\r
 \r
 static int __devinit rk30_hdmi_probe (struct platform_device *pdev)\r
@@ -128,7 +138,8 @@ static int __devinit rk30_hdmi_probe (struct platform_device *pdev)
                ret = -ENXIO;\r
                goto err0;\r
        }\r
-       \r
+       hdmi->regbase_phy = res->start;\r
+       hdmi->regsize_phy = (res->end - res->start) + 1;\r
        mem = request_mem_region(res->start, (res->end - res->start) + 1, pdev->name);\r
        if (!mem)\r
        {\r
@@ -145,6 +156,10 @@ static int __devinit rk30_hdmi_probe (struct platform_device *pdev)
                goto err1;\r
        }\r
        \r
+       ret = rk30_hdmi_initial();\r
+       if(ret != HDMI_ERROR_SUCESS)\r
+               goto err1;\r
+               \r
        hdmi_io_remap();\r
        hdmi_sys_init();\r
        \r
@@ -207,6 +222,11 @@ err0:
 static int __devexit rk30_hdmi_remove(struct platform_device *pdev)\r
 {\r
        if(hdmi) {\r
+               mutex_lock(&hdmi->enable_mutex);\r
+               if(!hdmi->suspend && hdmi->enable)\r
+                       disable_irq(hdmi->irq);\r
+               mutex_unlock(&hdmi->enable_mutex);\r
+               free_irq(hdmi->irq, NULL);\r
                flush_workqueue(hdmi->workqueue);\r
                destroy_workqueue(hdmi->workqueue);\r
                #ifdef CONFIG_SWITCH\r
@@ -217,7 +237,7 @@ static int __devexit rk30_hdmi_remove(struct platform_device *pdev)
                unregister_early_suspend(&hdmi->early_suspend);\r
                #endif\r
                iounmap((void*)hdmi->regbase);\r
-       //      release_mem_region(res->start,(res->end - res->start) + 1);\r
+               release_mem_region(hdmi->regbase_phy, hdmi->regsize_phy);\r
                clk_disable(hdmi->hclk);\r
                fb_destroy_modelist(&hdmi->edid.modelist);\r
                if(hdmi->edid.audio)\r
index 83acb6c86e583417f8d62ef78613679c8d8668d4..45275f39f9a117bd09381412b778cc219d961549 100755 (executable)
@@ -14,7 +14,7 @@
 #ifdef CONFIG_HAS_EARLYSUSPEND
 #include <linux/earlysuspend.h>
 #endif
-
+#include <asm/atomic.h>
 #include<linux/rk_screen.h>
 #include <linux/rk_fb.h>
 #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__ */
old mode 100644 (file)
new mode 100755 (executable)
index a5f291e..3154bc2
@@ -88,7 +88,7 @@ static int hdmi_edid_parse_dtd(unsigned char *block, struct fb_videomode *mode)
 \r
 static int hdmi_edid_parse_base(unsigned char *buf, int *extend_num, struct hdmi_edid *pedid)\r
 {\r
-       int rc;\r
+       int rc, i;\r
        \r
        if(buf == NULL || extend_num == NULL)\r
                return E_HDMI_EDID_PARAM;\r
index c8504f62a085dda2a5e581978b3ddfc8bceff47f..5727174863304d0e78adf3c996d0961261e00ac1 100755 (executable)
@@ -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;
 }
index 8a766bcc57705ed497d95c048faeec7ebdc1ccb2..45f986bf6f2ee3609518d28a9b8fd061950ed378 100755 (executable)
@@ -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
index b4c9c2c37e7771aff9402208103d49a6e7c3a9d3..cf34d457696707d9a5d7378c1e0b672a480dc1bd 100755 (executable)
@@ -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
index baa3a353f0ef5b348153580d51c483efd210d9ce..534ba51ab5bd5e2e1111ffe24b7b578ea3aea106 100755 (executable)
@@ -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...)          \