source "drivers/video/rockchip/hdmi/chips/rk610/Kconfig"
endif
+config HDMI_RK616
+ bool "RK616 HDMI support"
+ depends on MFD_RK616
+ help
+ Support rk616 hdmi if you say y here
+
+if HDMI_RK616
+source "drivers/video/rockchip/hdmi/chips/rk616/Kconfig"
+endif
+
choice
prompt "HDMI Source LCDC select"
config HDMI_SOURCE_LCDC0
obj-$(CONFIG_HDMI_RK2928) += rk2928/
obj-$(CONFIG_HDMI_RK610) += rk610/
obj-$(CONFIG_HDMI_CAT66121) += cat66121/
+obj-$(CONFIG_HDMI_RK616) += rk616/
--- /dev/null
+config HDCP_RK616
+ bool "RK616 HDCP support"
+ depends on HDMI_RK616
+ default n
+ help
+ HDCP Interface. This adds the High Definition Content Protection Interface.
+ See http://www.digital-cp.com/ for HDCP specification.
+
+config HDCP_RK616_DEBUG
+ bool "RK616 HDCP Debugging"
+ depends on HDCP_RK616
+ default n
+ help
+ Enableds verbose debugging the the HDCP drivers
--- /dev/null
+#
+# Makefile for HDMI linux kernel module.
+#
+
+ccflags-$(CONFIG_RK_HDMI_DEBUG) = -DDEBUG -DHDMI_DEBUG
+ccflags-$(CONFIG_HDCP_RK616_DEBUG) = -DHDCP_DEBUG
+
+obj-$(CONFIG_HDMI_RK616) += rk616_hdmi_hw.o rk616_hdmi.o
+obj-$(CONFIG_HDCP_RK616) += rk616_hdmi_hdcp.o rk616_hdcp.o
--- /dev/null
+#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 "rk616_hdmi.h"
+#include "rk616_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;
+ }
+
+ rk616_hdcp_disable();
+ rk616_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 = rk616_hdcp_start_authentication();
+
+ if (status != HDCP_OK) {
+ DBG("HDCP: authentication failed");
+ hdcp_wq_authentication_failure();
+ } else {
+ hdcp->hdcp_state = HDCP_WAIT_KSV_LIST;
+// hdcp->hdcp_state = HDCP_LINK_INTEGRITY_CHECK;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_wq_check_bksv
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_wq_check_bksv(void)
+{
+ int status = HDCP_OK;
+
+ DBG("Check BKSV start");
+
+ status = rk616_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)
+{
+ rk616_hdmi_control_output(true);
+ printk(KERN_INFO "HDCP: authentication pass");
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_wq_disable
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_wq_disable(int event)
+{
+ printk(KERN_INFO "HDCP: disabled");
+
+ hdcp_cancel_work(&hdcp->pending_wq_event);
+ rk616_hdcp_disable();
+ if(event == HDCP_DISABLE_CTL) {
+ hdcp->hdcp_state = HDCP_DISABLED;
+ if(hdcp->hdmi_state == HDMI_STARTED)
+ rk616_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 status)
+{
+ char interrupt1;
+ char interrupt2;
+
+ rk616_hdcp_interrupt(&interrupt1, &interrupt2);
+ DBG("%s 0x%02x 0x%02x", __FUNCTION__, interrupt1, interrupt2);
+ if(interrupt1 & 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(interrupt1 & (m_INT_BKSV_READY | m_INT_BKSV_UPDATE))
+ hdcp_submit_work(HDCP_KSV_LIST_RDY_EVENT, 0);
+ else if(interrupt1 & m_INT_AUTH_SUCCESS)
+ hdcp_submit_work(HDCP_AUTH_PASS_EVENT, 0);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_power_on_cb
+ *-----------------------------------------------------------------------------
+ */
+static int hdcp_power_on_cb(void)
+{
+ DBG("%s", __FUNCTION__);
+// return rk616_hdcp_load_key2mem(hdcp->keys);
+ return HDCP_OK;
+}
+
+/*-----------------------------------------------------------------------------
+ * 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(5000));
+}
+
+// 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);
+
+ printk(KERN_INFO "HDCP: load 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 rk616_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;
+ }
+
+ rk616_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 rk616_hdcp_exit(void)
+{
+ device_remove_file(mdev.this_device, &dev_attr_enable);
+ misc_deregister(&mdev);
+ if(hdcp->keys)
+ kfree(hdcp->keys);
+ if(hdcp->invalidkeys)
+ kfree(hdcp->invalidkeys);
+ kfree(hdcp);
+}
+
+module_init(rk616_hdcp_init);
+module_exit(rk616_hdcp_exit);
--- /dev/null
+#ifndef __rk616_HDCP_H__
+#define __rk616_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
+
+/* HDCP DDC Clock */
+#define HDCP_DDC_CLK 100000
+
+/* Authentication retry times */
+#define HDCP_INFINITE_REAUTH 0x100
+
+/* HDCP Regs */
+#define HDCP_CTRL1 0x52
+ #define m_AUTH_START (1 << 7)
+ #define m_BKSV_VALID (1 << 6)
+ #define m_BKSV_INVALID (1 << 5)
+ #define m_ENCRYPT_ENABLE (1 << 4)
+ #define m_AUTH_STOP (1 << 3)
+ #define m_ADVANED_ENABLE (1 << 2)
+ #define m_HDMI_DVI (1 << 1)
+ #define m_HDCP_RESET (1 << 0)
+
+ #define v_AUTH_START(n) (n << 7)
+ #define v_BKSV_VALID(n) (n << 6)
+ #define v_BKSV_INVALID(n) (n << 5)
+ #define v_ENCRYPT_ENABLE(n) (n << 4)
+ #define v_AUTH_STOP(n) (n << 3)
+ #define v_ADVANED_ENABLE(n) (n << 2)
+ #define v_HDMI_DVI(n) (n << 1)
+ #define v_HDCP_RESET(n) (n << 0)
+
+#define HDCP_CTRL2 0x53
+ #define m_DISABLE_127_CHECK (1 << 7)
+ #define m_SKIP_BKSV_CHECK (1 << 6)
+ #define m_ENABLE_PJ_CHECK (1 << 5)
+ #define m_DISABLE_DEVICE_NUMBER_CHECK (1 << 4)
+ #define m_DELAY_RI_1_CLK (1 << 3)
+ #define m_USE_PRESET_AN (1 << 2)
+ #define m_KEY_COMBINATION (3 << 0)
+
+ #define v_DISABLE_127_CHECK(n) (n << 7)
+ #define v_SKIP_BKSV_CHECK(n) (n << 6)
+ #define v_ENABLE_PJ_CHECK(n) (n << 5)
+ #define v_DISABLE_DEVICE_NUMBER_CHECK(n)(n << 4)
+ #define v_DELAY_RI_1_CLK(n) (n << 3)
+ #define v_USE_PRESET_AN(n) (n << 2)
+ #define v_KEY_COMBINATION(n) (n << 0)
+
+#define HDCP_KEY_STATUS 0x54
+ #define m_KEY_READY (1 << 0)
+
+#define HDCP_CTRL_SOFT 0x57
+ #define m_DISABLE_127_CHECK (1 << 7)
+ #define m_SKIP_BKSV_CHECK (1 << 6)
+ #define m_NOT_AUTHENTICATED (1 << 5)
+ #define m_ENCRYPTED (1 << 4)
+ #define m_ADVANCED_CIPHER (1 << 3)
+
+#define HDCP_BCAPS_RX 0x58
+#define HDCP_TIMER_100MS 0x63
+#define HDCP_TIMER_5S 0x64
+#define HDCP_ERROR 0x65
+ #define m_DDC_NO_ACK (1 << 3)
+ #define m_PJ_MISMACH (1 << 2)
+ #define m_RI_MISMACH (1 << 1)
+ #define m_BKSV_WRONG (1 << 0)
+
+#define HDCP_KSV_BYTE0 0x66
+#define HDCP_KSV_BYTE1 0x67
+#define HDCP_KSV_BYTE2 0x68
+#define HDCP_KSV_BYTE3 0x69
+#define HDCP_KSV_BYTE4 0x6a
+
+#define HDCP_AN_SEED 0x6c
+
+#define HDCP_BCAPS_TX 0x80
+#define HDCP_BSTATE_0 0x81
+#define HDCP_BSTATE_1 0x82
+
+#define HDCP_KEY_FIFO 0x98
+
+#define HDCP_INT_MASK1 0xc2
+#define HDCP_INT_STATUS1 0xc3
+ #define m_INT_HDCP_ERR (1 << 7)
+ #define m_INT_BKSV_READY (1 << 6)
+ #define m_INT_BKSV_UPDATE (1 << 5)
+ #define m_INT_AUTH_SUCCESS (1 << 4)
+ #define m_INT_AUTH_READY (1 << 3)
+
+#define HDCP_INT_MASK2 0xc4
+#define HDCP_INT_STATUS2 0xc5
+ #define m_INT_SOFT_MODE_READY (1 << 7)
+ #define m_INT_AUTH_M0_REDAY (1 << 6)
+ #define m_INT_1st_FRAME_ARRIVE (1 << 5)
+ #define m_INT_AN_READY (1 << 4)
+ #define m_INT_ENCRYPTED (1 << 2)
+ #define m_INT_NOT_ENCRYPTED_AVMUTE (1 << 1)
+ #define m_INT_NOT_ENCRYPTED_AVUNMUTE (1 << 0)
+
+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 rk616_hdcp_disable(void);
+extern int rk616_hdcp_start_authentication(void);
+extern int rk616_hdcp_check_bksv(void);
+extern int rk616_hdcp_load_key2mem(struct hdcp_keys *key);
+extern void rk616_hdcp_interrupt(char *status1, char *status2);
+#endif /* __rk616_HDCP_H__ */
--- /dev/null
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <mach/board.h>
+#include <mach/io.h>
+#include <mach/gpio.h>
+#include <mach/iomux.h>
+#include "rk616_hdmi.h"
+#include "rk616_hdmi_hw.h"
+
+struct hdmi *hdmi = NULL;
+struct mfd_rk616 *g_rk616_hdmi = NULL;
+
+extern irqreturn_t hdmi_irq(int irq, void *priv);
+extern void hdmi_work(struct work_struct *work);
+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 rk616_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)
+{
+ hdmi_dbg(hdmi->dev, "hdmi enter early suspend pwr %d state %d\n", hdmi->pwr_mode, hdmi->state);
+
+ flush_delayed_work(&hdmi->delay_work);
+ mutex_lock(&hdmi->enable_mutex);
+ hdmi->suspend = 1;
+ if(!hdmi->enable) {
+ mutex_unlock(&hdmi->enable_mutex);
+ return;
+ }
+ disable_irq(hdmi->irq);
+ mutex_unlock(&hdmi->enable_mutex);
+ hdmi->command = HDMI_CONFIG_ENABLE;
+ init_completion(&hdmi->complete);
+ hdmi->wait = 1;
+ queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0);
+ wait_for_completion_interruptible_timeout(&hdmi->complete,
+ msecs_to_jiffies(5000));
+ flush_delayed_work(&hdmi->delay_work);
+
+ return;
+}
+
+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;
+ rk616_hdmi_initial();
+ if(hdmi->enable) {
+ enable_irq(hdmi->irq);
+ }
+ mutex_unlock(&hdmi->enable_mutex);
+ return;
+}
+#endif
+
+static int __devinit rk616_hdmi_probe (struct platform_device *pdev)
+{
+ int ret;
+
+ struct mfd_rk616 *g_rk616_hdmi = dev_get_drvdata(pdev->dev.parent);
+ if(!g_rk616_hdmi)
+ {
+ dev_err(&pdev->dev,"null mfd device rk616!\n");
+ return -ENODEV;
+ }
+
+ hdmi = kmalloc(sizeof(struct hdmi), GFP_KERNEL);
+ if(!hdmi)
+ {
+ dev_err(&pdev->dev, ">>rk616 hdmi kmalloc fail!");
+ return -ENOMEM;
+ }
+ memset(hdmi, 0, sizeof(struct hdmi));
+ hdmi->dev = &pdev->dev;
+ platform_set_drvdata(pdev, hdmi);
+
+ if(HDMI_SOURCE_DEFAULT == HDMI_SOURCE_LCDC0)
+ hdmi->lcdc = rk_get_lcdc_drv("lcdc0");
+ else
+ hdmi->lcdc = rk_get_lcdc_drv("lcdc1");
+ if(hdmi->lcdc == NULL)
+ {
+ dev_err(hdmi->dev, "can not connect to video source lcdc\n");
+ ret = -ENXIO;
+ goto err0;
+ }
+ hdmi->xscale = 100;
+ hdmi->yscale = 100;
+
+ ret = rk616_hdmi_initial();
+
+ hdmi_sys_init();
+
+ hdmi->workqueue = create_singlethread_workqueue("hdmi");
+ INIT_DELAYED_WORK(&(hdmi->delay_work), hdmi_work);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ hdmi->early_suspend.suspend = hdmi_early_suspend;
+ hdmi->early_suspend.resume = hdmi_early_resume;
+ hdmi->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 10;
+ register_early_suspend(&hdmi->early_suspend);
+#endif
+
+ hdmi_register_display_sysfs(hdmi, NULL);
+#ifdef CONFIG_SWITCH
+ hdmi->switch_hdmi.name="hdmi";
+ switch_dev_register(&(hdmi->switch_hdmi));
+#endif
+
+ spin_lock_init(&hdmi->irq_lock);
+ mutex_init(&hdmi->enable_mutex);
+
+ /* get the IRQ */
+ hdmi->irq = platform_get_irq(pdev, 0);
+ if(hdmi->irq <= 0) {
+ dev_err(hdmi->dev, "failed to get hdmi irq resource (%d).\n", hdmi->irq);
+ ret = -ENXIO;
+ goto err1;
+ }
+
+ /* request the IRQ */
+ ret = request_irq(hdmi->irq, hdmi_irq, 0, dev_name(&pdev->dev), hdmi);
+ if (ret)
+ {
+ dev_err(hdmi->dev, "hdmi request_irq failed (%d).\n", ret);
+ goto err1;
+ }
+
+ dev_info(hdmi->dev, "rk616 hdmi probe success.\n");
+ return 0;
+err1:
+#ifdef CONFIG_SWITCH
+ switch_dev_unregister(&(hdmi->switch_hdmi));
+#endif
+ hdmi_unregister_display_sysfs(hdmi);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&hdmi->early_suspend);
+#endif
+err0:
+ hdmi_dbg(hdmi->dev, "rk616 hdmi probe error.\n");
+ kfree(hdmi);
+ hdmi = NULL;
+ return ret;
+}
+
+static int __devexit rk616_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
+ switch_dev_unregister(&(hdmi->switch_hdmi));
+#endif
+ hdmi_unregister_display_sysfs(hdmi);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&hdmi->early_suspend);
+#endif
+ fb_destroy_modelist(&hdmi->edid.modelist);
+ if(hdmi->edid.audio)
+ kfree(hdmi->edid.audio);
+ if(hdmi->edid.specs)
+ {
+ if(hdmi->edid.specs->modedb)
+ kfree(hdmi->edid.specs->modedb);
+ kfree(hdmi->edid.specs);
+ }
+ kfree(hdmi);
+ hdmi = NULL;
+ }
+ printk(KERN_INFO "rk616 hdmi removed.\n");
+ return 0;
+}
+
+static void rk616_hdmi_shutdown(struct platform_device *pdev)
+{
+ if(hdmi) {
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&hdmi->early_suspend);
+#endif
+ }
+ printk(KERN_INFO "rk616 hdmi shut down.\n");
+}
+
+static struct platform_driver rk616_hdmi_driver = {
+ .probe = rk616_hdmi_probe,
+ .remove = __devexit_p(rk616_hdmi_remove),
+ .driver = {
+ .name = "rk616-hdmi",
+ .owner = THIS_MODULE,
+ },
+ .shutdown = rk616_hdmi_shutdown,
+};
+
+static int __init rk616_hdmi_init(void)
+{
+ return platform_driver_register(&rk616_hdmi_driver);
+}
+
+static void __exit rk616_hdmi_exit(void)
+{
+ platform_driver_unregister(&rk616_hdmi_driver);
+}
+
+
+//fs_initcall(rk616_hdmi_init);
+late_initcall(rk616_hdmi_init);
+module_exit(rk616_hdmi_exit);
--- /dev/null
+#ifndef __RK616_HDMI_H__
+#define __RK616_HDMI_H__
+
+#include "../../rk_hdmi.h"
+#include <linux/mfd/rk616.h>
+
+#if defined(CONFIG_HDMI_SOURCE_LCDC1)
+#define HDMI_SOURCE_DEFAULT HDMI_SOURCE_LCDC1
+#else
+#define HDMI_SOURCE_DEFAULT HDMI_SOURCE_LCDC0
+#endif
+
+extern void rk616_hdmi_control_output(int enable);
+extern int rk616_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__ */
--- /dev/null
+#include <linux/delay.h>
+#include "rk616_hdmi.h"
+#include "rk616_hdmi_hw.h"
+#include "rk616_hdcp.h"
+
+#define HDCPWrReg HDMIWrReg
+#define HDCPRdReg HDMIRdReg
+#define HDCPMskReg(temp,addr,Msk,val) do{ \
+ HDMIRdReg(addr,&temp); \
+ HDMIWrReg(addr, ((val)&(Msk))|(temp&(~Msk))); \
+ }while(0)
+
+void rk616_hdcp_disable(void)
+{
+ char temp;
+
+ // Diable HDCP Interrupt
+ HDCPWrReg(HDCP_INT_MASK1, 0x00);
+ // Stop and Reset HDCP
+ HDCPMskReg(temp, HDCP_CTRL1, m_ENCRYPT_ENABLE | m_AUTH_STOP | m_HDCP_RESET,
+ v_ENCRYPT_ENABLE(0) | v_AUTH_STOP(1) | v_HDCP_RESET(1) );
+}
+
+int rk616_hdcp_load_key2mem(struct hdcp_keys *key)
+{
+ int i;
+ DBG("HDCP: rk616_hdcp_load_key2mem start");
+ // Write 40 private key
+ for(i = 0; i < HDCP_PRIVATE_KEY_SIZE; i++)
+ HDCPWrReg(HDCP_KEY_FIFO, key->DeviceKey[i]);
+
+ // Write 1st aksv
+ for(i = 0; i < 5; i++)
+ HDCPWrReg(HDCP_KEY_FIFO, key->KSV[i]);
+
+ // Write 2nd aksv
+ for(i = 0; i < 5; i++)
+ HDCPWrReg(HDCP_KEY_FIFO, key->KSV[i]);
+ DBG("HDCP: rk616_hdcp_load_key2mem end");
+ return HDCP_OK;
+}
+
+int rk616_hdcp_start_authentication(void)
+{
+ char temp;
+ int retry = 0;
+
+ if(hdcp->keys == NULL) {
+ printk(KERN_ERR "HDCP: key is not loaded\n");
+ return HDCP_KEY_ERR;
+ }
+
+ // Select TMDS CLK to configure regs
+ HDCPMskReg(temp, SYS_CTRL, m_REG_CLK_SOURCE, v_REG_CLK_SOURCE_TMDS);
+
+ HDCPRdReg(HDCP_KEY_STATUS,&temp);
+ while( ( temp & m_KEY_READY) == 0 ) {
+ if(retry > 10) {
+ printk(KERN_ERR "HDCP: loaded key error\n");
+ return HDCP_KEY_ERR;
+ }
+ rk616_hdcp_load_key2mem(hdcp->keys);
+ msleep(1);
+ HDCPRdReg(HDCP_KEY_STATUS,&temp);
+ }
+
+ // Config DDC bus clock: ddc_clk = reg_clk/4*(reg 0x4c 0x4b)
+ DBG("TMDS frequency %d", hdmi->tmdsclk);
+ retry = hdmi->tmdsclk/(HDCP_DDC_CLK*4);
+ HDCPWrReg(DDC_CLK_L, retry & 0xFF);
+ HDCPWrReg(DDC_CLK_H, (retry >> 8) & 0xFF);
+
+ HDCPWrReg(HDCP_CTRL2, 0x00);
+
+ //Enable interrupt
+ HDCPWrReg(HDCP_INT_MASK1, m_INT_HDCP_ERR | m_INT_BKSV_READY | m_INT_BKSV_UPDATE | m_INT_AUTH_SUCCESS | m_INT_AUTH_READY);
+// HDCPWrReg(HDCP_INT_MASK2, 0xFF);
+ //Start authentication
+ HDCPMskReg(temp, HDCP_CTRL1, m_AUTH_START | m_ENCRYPT_ENABLE | m_ADVANED_ENABLE, v_AUTH_START(1) | v_ENCRYPT_ENABLE(1) | v_ADVANED_ENABLE(0));
+
+ return HDCP_OK;
+}
+
+int rk616_hdcp_check_bksv(void)
+{
+ int i, j;
+ char temp = 0, bksv[5];
+ char *invalidkey;
+
+ for(i = 0; i < 5; i++) {
+ HDCPRdReg(HDCP_KSV_BYTE0 + (4 - i),&temp);
+ bksv[i] = temp & 0xFF;
+ }
+ DBG("bksv is 0x%02x%02x%02x%02x%02x", bksv[0], bksv[1], bksv[2], bksv[3], bksv[4]);
+
+ temp = 0;
+ for (i = 0; i < 5; i++)
+ {
+ for (j = 0; j < 8; j++)
+ {
+ if (bksv[i] & 0x01)
+ {
+ temp++;
+ }
+ bksv[i] >>= 1;
+ }
+ }
+ if (temp != 20)
+ return HDCP_KSV_ERR;
+
+ 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");
+ HDCPMskReg(temp, HDCP_CTRL1, m_BKSV_INVALID | m_ENCRYPT_ENABLE, v_BKSV_INVALID(1) | v_ENCRYPT_ENABLE(1));
+ return HDCP_KSV_ERR;
+ }
+ }
+ HDCPMskReg(temp, HDCP_CTRL1, m_BKSV_VALID | m_ENCRYPT_ENABLE, v_BKSV_VALID(1) | v_ENCRYPT_ENABLE(1));
+ return HDCP_OK;
+}
+
+void rk616_hdcp_interrupt(char *status1, char *status2)
+{
+ char interrupt1 = 0;
+ char interrupt2 = 0;
+ char temp =0;
+ HDCPRdReg(HDCP_INT_STATUS1,&interrupt1);
+ HDCPRdReg(HDCP_INT_STATUS2,&interrupt2);
+ if(interrupt1) {
+ HDCPWrReg(HDCP_INT_STATUS1, interrupt1);
+ if(interrupt1 & m_INT_HDCP_ERR){
+ HDCPRdReg(HDCP_ERROR,&temp);
+ printk(KERN_INFO "HDCP: Error 0x%02x\n", temp);
+ }
+ }
+ if(interrupt2)
+ HDCPWrReg(HDCP_INT_STATUS2, interrupt2);
+
+ *status1 = interrupt1;
+ *status2 = interrupt2;
+}
--- /dev/null
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <mach/io.h>
+#include "rk616_hdmi.h"
+#include "rk616_hdmi_hw.h"
+
+static char edid_result = 0;
+static bool analog_sync = 0;
+
+static inline void delay100us(void)
+{
+ msleep(1);
+}
+
+
+static void rk616_hdmi_av_mute(bool enable)
+{
+ HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(enable) | v_VIDEO_MUTE(enable));
+}
+
+static void rk616_hdmi_sys_power_up(void)
+{
+ HDMIMskReg(SYS_CTRL,m_POWER, v_PWR_ON);
+}
+static void rk616_hdmi_sys_power_down(void)
+{
+ HDMIMskReg(SYS_CTRL,m_POWER, v_PWR_OFF);
+}
+
+
+static void rk616_hdmi_set_pwr_mode(int mode)
+{
+ hdmi_dbg(hdmi->dev,"%s \n",__FUNCTION__);
+ if(hdmi->pwr_mode == mode)
+ return;
+ switch(mode){
+ case NORMAL:
+ rk616_hdmi_sys_power_down();
+ HDMIWrReg(PHY_DRIVER,0xaa);
+ HDMIWrReg(PHY_PRE_EMPHASIS,0x0f);
+ HDMIWrReg(PHY_SYS_CTL,0x2d);
+ HDMIWrReg(PHY_SYS_CTL,0x2c);
+ HDMIWrReg(PHY_SYS_CTL,0x28);
+ HDMIWrReg(PHY_SYS_CTL,0x20);
+ HDMIWrReg(PHY_CHG_PWR,0x0f);
+ HDMIWrReg(0xce, 0x00);
+ HDMIWrReg(0xce, 0x01);
+ rk616_hdmi_av_mute(1);
+ rk616_hdmi_sys_power_up();
+ analog_sync = 1;
+ break;
+ case LOWER_PWR:
+ rk616_hdmi_av_mute(0);
+ rk616_hdmi_sys_power_down();
+ HDMIWrReg(PHY_DRIVER,0x00);
+ HDMIWrReg(PHY_PRE_EMPHASIS,0x00);
+ HDMIWrReg(PHY_CHG_PWR,0x00);
+ HDMIWrReg(PHY_SYS_CTL,0x2f);
+ break;
+ default:
+ hdmi_dbg(hdmi->dev,"unkown rk616 hdmi pwr mode %d\n",mode);
+ }
+ hdmi->pwr_mode = mode;
+}
+
+
+int rk616_hdmi_detect_hotplug(void)
+{
+ int value = 0;
+ HDMIRdReg(HDMI_STATUS,&value);
+
+ hdmi_dbg(hdmi->dev, "[%s] value %02x\n", __FUNCTION__, value);
+ value &= m_HOTPLUG;
+ if(value == m_HOTPLUG)
+ return HDMI_HPD_ACTIVED;
+ else if(value)
+ return HDMI_HPD_INSERT;
+ else
+ return HDMI_HPD_REMOVED;
+}
+
+#define HDMI_SYS_FREG_CLK 11289600
+#define HDMI_SCL_RATE (100*1000)
+#define HDMI_DDC_CONFIG (HDMI_SYS_FREG_CLK>>2)/HDMI_SCL_RATE
+#define DDC_BUS_FREQ_L 0x4b
+#define DDC_BUS_FREQ_H 0x4c
+
+int rk616_hdmi_read_edid(int block, unsigned char *buff)
+{
+ int value, ret = -1, ddc_bus_freq = 0;
+ char interrupt = 0, trytime = 2;
+ unsigned long flags;
+
+ hdmi_dbg(hdmi->dev, "[%s] block %d\n", __FUNCTION__, block);
+ spin_lock_irqsave(&hdmi->irq_lock, flags);
+ edid_result = 0;
+ spin_unlock_irqrestore(&hdmi->irq_lock, flags);
+ //Before Phy parameter was set, DDC_CLK is equal to PLLA freq which is 30MHz.
+ //Set DDC I2C CLK which devided from DDC_CLK to 100KHz.
+
+ ddc_bus_freq = HDMI_DDC_CONFIG;
+ HDMIWrReg(DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
+ HDMIWrReg(DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
+
+ // Enable edid interrupt
+ HDMIWrReg(INTERRUPT_MASK1, m_INT_HOTPLUG | m_INT_EDID_READY);
+
+ while(trytime--) {
+ // Config EDID block and segment addr
+ HDMIWrReg(EDID_WORD_ADDR, (block%2) * 0x80);
+ HDMIWrReg(EDID_SEGMENT_POINTER, block/2);
+
+ value = 10;
+ while(value--)
+ {
+ 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_READY))
+ break;
+ msleep(10);
+ }
+ hdmi_dbg(hdmi->dev, "[%s] edid read value %d\n", __FUNCTION__, value);
+ if(interrupt & m_INT_EDID_READY)
+ {
+ for(value = 0; value < HDMI_EDID_BLOCK_SIZE; value++)
+ HDMIRdReg(EDID_FIFO_ADDR,(buff+value));
+ ret = 0;
+
+ hdmi_dbg(hdmi->dev, "[%s] edid read sucess\n", __FUNCTION__);
+#ifdef HDMI_DEBUG
+ for(value = 0; value < 128; value++) {
+ printk("%02x ,", buff[value]);
+ if( (value + 1) % 16 == 0)
+ printk("\n");
+ }
+#endif
+ break;
+ }else
+ hdmi_err(hdmi->dev, "[%s] edid read error\n", __FUNCTION__);
+
+ hdmi_dbg(hdmi->dev, "[%s] edid try times %d\n", __FUNCTION__, trytime);
+ msleep(100);
+ }
+ // Disable edid interrupt
+ HDMIWrReg(INTERRUPT_MASK1, m_INT_HOTPLUG);
+// msleep(100);
+ return ret;
+}
+
+static void rk616_hdmi_config_avi(unsigned char vic, unsigned char output_color)
+{
+ int i;
+ char info[SIZE_AVI_INFOFRAME];
+
+ memset(info, 0, SIZE_AVI_INFOFRAME);
+ HDMIWrReg(CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
+ info[0] = 0x82;
+ info[1] = 0x02;
+ info[2] = 0x0D;
+ info[3] = info[0] + info[1] + info[2];
+ info[4] = (AVI_COLOR_MODE_RGB << 5);
+ info[5] = (AVI_COLORIMETRY_NO_DATA << 6) | (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) | ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME;
+ info[6] = 0;
+ info[7] = vic;
+ info[8] = 0;
+
+ // Calculate AVI InfoFrame ChecKsum
+ for (i = 4; i < SIZE_AVI_INFOFRAME; i++)
+ {
+ info[3] += info[i];
+ }
+ info[3] = 0x100 - info[3];
+
+ for(i = 0; i < SIZE_AVI_INFOFRAME; i++)
+ HDMIWrReg(CONTROL_PACKET_ADDR + i, info[i]);
+}
+
+static int rk616_hdmi_config_video(struct hdmi_video_para *vpara)
+{
+ int value;
+ struct fb_videomode *mode;
+
+ hdmi_dbg(hdmi->dev, "[%s]\n", __FUNCTION__);
+ if(vpara == NULL) {
+ hdmi_err(hdmi->dev, "[%s] input parameter error\n", __FUNCTION__);
+ return -1;
+ }
+
+ if(hdmi->hdcp_power_off_cb)
+ hdmi->hdcp_power_off_cb();
+ // Diable video and audio output
+ HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
+
+ // Input video mode is SDR RGB24bit, Data enable signal from external
+ HDMIWrReg(VIDEO_CONTRL1, v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444) | v_DE_EXTERNAL);
+ HDMIWrReg(VIDEO_CONTRL2, v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) | (vpara->output_color & 0xFF));
+
+ // Set HDMI Mode
+ HDMIWrReg(HDCP_CTRL, v_HDMI_DVI(vpara->output_mode));
+
+ // Enable or disalbe color space convert
+ if(vpara->input_color != vpara->output_color) {
+ value = v_SOF_DISABLE | v_CSC_ENABLE;
+ }
+ else
+ value = v_SOF_DISABLE;
+ HDMIWrReg(VIDEO_CONTRL3, value);
+
+ // Set ext video
+#if 1
+ HDMIWrReg(VIDEO_TIMING_CTL, 0);
+ mode = (struct fb_videomode *)hdmi_vic_to_videomode(vpara->vic);
+ if(mode == NULL)
+ {
+ hdmi_err(hdmi->dev, "[%s] not found vic %d\n", __FUNCTION__, vpara->vic);
+ return -ENOENT;
+ }
+ hdmi->tmdsclk = mode->pixclock;
+#else
+ value = v_EXTERANL_VIDEO(1) | v_INETLACE(mode->vmode);
+ if(mode->sync & FB_SYNC_HOR_HIGH_ACT)
+ value |= v_HSYNC_POLARITY(1);
+ if(mode->sync & FB_SYNC_VERT_HIGH_ACT)
+ value |= v_VSYNC_POLARITY(1);
+ HDMIWrReg(VIDEO_TIMING_CTL, value);
+
+ value = mode->left_margin + mode->xres + mode->right_margin + mode->hsync_len;
+ HDMIWrReg(VIDEO_EXT_HTOTAL_L, value & 0xFF);
+ HDMIWrReg(VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
+
+ value = mode->left_margin + mode->right_margin + mode->hsync_len;
+ HDMIWrReg(VIDEO_EXT_HBLANK_L, value & 0xFF);
+ HDMIWrReg(VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
+
+ value = mode->left_margin + mode->hsync_len;
+ HDMIWrReg(VIDEO_EXT_HDELAY_L, value & 0xFF);
+ HDMIWrReg(VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
+
+ value = mode->hsync_len;
+ HDMIWrReg(VIDEO_EXT_HDURATION_L, value & 0xFF);
+ HDMIWrReg(VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
+
+ value = mode->upper_margin + mode->yres + mode->lower_margin + mode->vsync_len;
+ HDMIWrReg(VIDEO_EXT_VTOTAL_L, value & 0xFF);
+ HDMIWrReg(VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
+
+ value = mode->upper_margin + mode->vsync_len + mode->lower_margin;
+ HDMIWrReg(VIDEO_EXT_VBLANK, value & 0xFF);
+
+ if(vpara->vic == HDMI_720x480p_60Hz_4_3 || vpara->vic == HDMI_720x480p_60Hz_16_9)
+ value = 42;
+ else
+ value = mode->upper_margin + mode->vsync_len;
+
+ HDMIWrReg(VIDEO_EXT_VDELAY, value & 0xFF);
+
+ value = mode->vsync_len;
+ HDMIWrReg(VIDEO_EXT_VDURATION, value & 0xFF);
+#endif
+
+ if(vpara->output_mode == OUTPUT_HDMI) {
+ rk616_hdmi_config_avi(vpara->vic, vpara->output_color);
+ hdmi_dbg(hdmi->dev, "[%s] sucess output HDMI.\n", __FUNCTION__);
+ }
+ else {
+ hdmi_dbg(hdmi->dev, "[%s] sucess output DVI.\n", __FUNCTION__);
+ }
+
+ if(hdmi->tmdsclk >= 148500000) {
+ HDMIWrReg(0xe3, 0x4f);
+ }
+ else {
+ HDMIWrReg(0xe3, 0x0f);
+ }
+ return 0;
+}
+
+static void rk616_hdmi_config_aai(void)
+{
+ int i;
+ char info[SIZE_AUDIO_INFOFRAME];
+
+ memset(info, 0, SIZE_AUDIO_INFOFRAME);
+
+ info[0] = 0x84;
+ info[1] = 0x01;
+ info[2] = 0x0A;
+
+ info[3] = info[0] + info[1] + info[2];
+ for (i = 4; i < SIZE_AUDIO_INFOFRAME; i++)
+ info[3] += info[i];
+
+ info[3] = 0x100 - info[3];
+
+ HDMIWrReg(CONTROL_PACKET_BUF_INDEX, INFOFRAME_AAI);
+ for(i = 0; i < SIZE_AUDIO_INFOFRAME; i++)
+ HDMIWrReg(CONTROL_PACKET_ADDR + i, info[i]);
+}
+
+static int rk616_hdmi_config_audio(struct hdmi_audio *audio)
+{
+ int rate, N, channel, mclk_fs;
+
+ if(audio->channel < 3)
+ channel = I2S_CHANNEL_1_2;
+ else if(audio->channel < 5)
+ channel = I2S_CHANNEL_3_4;
+ else if(audio->channel < 7)
+ channel = I2S_CHANNEL_5_6;
+ else
+ channel = I2S_CHANNEL_7_8;
+
+ switch(audio->rate)
+ {
+ case HDMI_AUDIO_FS_32000:
+ rate = AUDIO_32K;
+ N = N_32K;
+ mclk_fs = MCLK_384FS;
+ break;
+ case HDMI_AUDIO_FS_44100:
+ rate = AUDIO_441K;
+ N = N_441K;
+ mclk_fs = MCLK_256FS;
+ break;
+ case HDMI_AUDIO_FS_48000:
+ rate = AUDIO_48K;
+ N = N_48K;
+ mclk_fs = MCLK_256FS;
+ break;
+ case HDMI_AUDIO_FS_88200:
+ rate = AUDIO_882K;
+ N = N_882K;
+ mclk_fs = MCLK_128FS;
+ break;
+ case HDMI_AUDIO_FS_96000:
+ rate = AUDIO_96K;
+ N = N_96K;
+ mclk_fs = MCLK_128FS;
+ break;
+ case HDMI_AUDIO_FS_176400:
+ rate = AUDIO_1764K;
+ N = N_1764K;
+ mclk_fs = MCLK_128FS;
+ break;
+ case HDMI_AUDIO_FS_192000:
+ rate = AUDIO_192K;
+ N = N_192K;
+ mclk_fs = MCLK_128FS;
+ break;
+ default:
+ dev_err(hdmi->dev, "[%s] not support such sample rate %d\n", __FUNCTION__, audio->rate);
+ return -ENOENT;
+ }
+
+ //set_audio source I2S
+ HDMIWrReg(AUDIO_CTRL1, 0x00); //internal CTS, disable down sample, i2s input, disable MCLK
+ HDMIWrReg(AUDIO_SAMPLE_RATE, rate);
+ HDMIWrReg(AUDIO_I2S_MODE, v_I2S_MODE(I2S_STANDARD) | v_I2S_CHANNEL(channel) );
+ HDMIWrReg(AUDIO_I2S_MAP, 0x00);
+ HDMIWrReg(AUDIO_I2S_SWAPS_SPDIF, 0); // no swap
+
+ //Set N value
+ HDMIWrReg(AUDIO_N_H, (N >> 16) & 0x0F);
+ HDMIWrReg(AUDIO_N_M, (N >> 8) & 0xFF);
+ HDMIWrReg(AUDIO_N_L, N & 0xFF);
+ rk616_hdmi_config_aai();
+
+ return 0;
+}
+
+void rk616_hdmi_control_output(int enable)
+{
+ char mutestatus = 0;
+
+ if(enable) {
+ if(hdmi->pwr_mode == LOWER_PWR)
+ rk616_hdmi_set_pwr_mode(NORMAL);
+ HDMIRdReg(AV_MUTE,&mutestatus);
+ if(mutestatus && (m_AUDIO_MUTE | m_VIDEO_BLACK)) {
+ HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
+ rk616_hdmi_sys_power_up();
+ rk616_hdmi_sys_power_down();
+ rk616_hdmi_sys_power_up();
+ if(analog_sync){
+ HDMIWrReg(0xce, 0x00);
+ delay100us();
+ HDMIWrReg(0xce, 0x01);
+ analog_sync = 0;
+ }
+ }
+ }
+ else {
+ HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
+ }
+}
+
+int rk616_hdmi_removed(void)
+{
+
+ dev_printk(KERN_INFO , hdmi->dev , "Removed.\n");
+ rk616_hdmi_set_pwr_mode(LOWER_PWR);
+
+ return HDMI_ERROR_SUCESS;
+}
+
+
+irqreturn_t hdmi_irq(int irq, void *priv)
+{
+ char interrupt1 = 0;
+ unsigned long flags;
+ spin_lock_irqsave(&hdmi->irq_lock,flags);
+ HDMIRdReg(INTERRUPT_STATUS1,&interrupt1);
+ HDMIWrReg(INTERRUPT_STATUS1, interrupt1);
+#if 1
+ hdmi_dbg(hdmi->dev, "[%s] interrupt1 %02x \n",\
+ __FUNCTION__, interrupt1);
+#endif
+ if(interrupt1 & m_INT_HOTPLUG ){
+ if(hdmi->state == HDMI_SLEEP)
+ hdmi->state = WAIT_HOTPLUG;
+ if(hdmi->pwr_mode == LOWER_PWR)
+ rk616_hdmi_set_pwr_mode(NORMAL);
+ queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, msecs_to_jiffies(10));
+ }else if(interrupt1 & m_INT_EDID_READY) {
+ edid_result = interrupt1;
+ }else if(hdmi->state == HDMI_SLEEP) {
+ hdmi_dbg(hdmi->dev, "hdmi return to sleep mode\n");
+ rk616_hdmi_set_pwr_mode(LOWER_PWR);
+ }
+#if 0
+ if(hdmi->hdcp_irq_cb)
+ hdmi->hdcp_irq_cb(interrupt2);
+#endif
+ spin_unlock_irqrestore(&hdmi->irq_lock,flags);
+ return IRQ_HANDLED;
+}
+
+static void rk616_hdmi_reset(void)
+{
+ char val = 0;
+ val = v_REG_CLK_INV| v_VCLK_INV |v_REG_CLK_SOURCE_SYS|v_PWR_ON |v_INT_POL_LOW;
+ HDMIWrReg(SYS_CTRL,val);
+ delay100us();
+ HDMIMskReg(SYS_CTRL,m_RST_ANALOG,v_NOT_RST_ANALOG);
+ delay100us();
+ HDMIMskReg(SYS_CTRL,m_RST_DIGITAL,v_NOT_RST_DIGITAL);
+ rk616_hdmi_set_pwr_mode(LOWER_PWR);
+}
+
+int rk616_hdmi_initial(void)
+{
+ int rc = HDMI_ERROR_SUCESS;
+
+ hdmi->pwr_mode = NORMAL;
+ hdmi->remove = rk616_hdmi_removed ;
+ hdmi->control_output = rk616_hdmi_control_output;
+ hdmi->config_video = rk616_hdmi_config_video;
+ hdmi->config_audio = rk616_hdmi_config_audio;
+ hdmi->detect_hotplug = rk616_hdmi_detect_hotplug;
+ hdmi->read_edid = rk616_hdmi_read_edid;
+
+ rk616_hdmi_reset();
+
+ if(hdmi->hdcp_power_on_cb)
+ rc = hdmi->hdcp_power_on_cb();
+
+ return rc;
+}
--- /dev/null
+#ifndef _RK616_HDMI_HW_H
+#define _RK616_HDMI_HW_H
+
+enum PWR_MODE{
+ NORMAL,
+ LOWER_PWR,
+};
+enum {
+ OUTPUT_DVI = 0,
+ OUTPUT_HDMI
+ };
+#define SYS_CTRL 0x00
+ #define m_RST_ANALOG (1<<6)
+ #define v_RST_ANALOG (0<<6)
+ #define v_NOT_RST_ANALOG (0<<6)
+ #define m_RST_DIGITAL (1<<5)
+ #define v_RST_DIGITAL (0<<5)
+ #define v_NOT_RST_DIGITAL (1<<5)
+ #define m_REG_CLK_INV (1<<4)
+ #define v_REG_CLK_NOT_INV (0<<4)
+ #define v_REG_CLK_INV (1<<4)
+ #define m_VCLK_INV (1<<3)
+ #define v_VCLK_NOT_INV (0<<3)
+ #define v_VCLK_INV (0<<3)
+ #define m_REG_CLK_SOURCE (1<<2)
+ #define v_REG_CLK_SOURCE_TMDS (0<<2)
+ #define v_REG_CLK_SOURCE_SYS (1<<2)
+ #define m_POWER (1<<1)
+ #define v_PWR_ON (0<<1)
+ #define v_PWR_OFF (1<<1)
+ #define m_INT_POL (1<<0)
+ #define v_INT_POL_HIGH 1
+ #define v_INT_POL_LOW 0
+
+#define VIDEO_CONTRL1 0x01
+ #define m_VIDEO_INPUT_FORMAT (7 << 1)
+ #define m_DE_SOURCE (1 << 0)
+ enum {
+ VIDEO_INPUT_SDR_RGB444 = 0,
+ VIDEO_INPUT_DDR_RGB444 = 5,
+ VIDEO_INPUT_DDR_YCBCR422 = 6
+ };
+ #define v_VIDEO_INPUT_FORMAT(n) (n << 1)
+ #define v_DE_EXTERNAL 1
+ #define v_DE_INTERANL 0
+
+#define VIDEO_CONTRL2 0x02
+ #define m_VIDEO_OUTPUT_FORMAT (3 << 6)
+ #define m_VIDEO_INPUT_BITS (3 << 4)
+ #define v_VIDEO_OUTPUT_FORMAT(n)(n << 6)
+ #define v_VIDEO_INPUT_BITS(n) (n << 4)
+ enum{
+ VIDEO_INPUT_12BITS = 0,
+ VIDEO_INPUT_10BITS,
+ VIDEO_INPUT_8BITS
+ };
+#define VIDEO_CONTRL3 0x04
+ #define m_SOF (1 << 3)
+ #define m_CSC (1 << 0)
+ #define v_SOF_ENABLE (0 << 3)
+ #define v_SOF_DISABLE (1 << 3)
+ #define v_CSC_ENABLE 1
+ #define v_CSC_DISABLE 0
+
+#define AV_MUTE 0x05
+ #define m_AVMUTE_CLEAR (1 << 7)
+ #define m_AVMUTE_ENABLE (1 << 6)
+ #define m_AUDIO_MUTE (1 << 1)
+ #define m_VIDEO_BLACK (1 << 0)
+ #define v_AUDIO_MUTE(n) (n << 1)
+ #define v_VIDEO_MUTE(n) (n << 0)
+
+#define VIDEO_TIMING_CTL 0x08
+ #define v_HSYNC_POLARITY(n) (n << 3)
+ #define v_VSYNC_POLARITY(n) (n << 2)
+ #define v_INETLACE(n) (n << 1)
+ #define v_EXTERANL_VIDEO(n) (n << 0)
+
+#define VIDEO_EXT_HTOTAL_L 0x09
+#define VIDEO_EXT_HTOTAL_H 0x0a
+#define VIDEO_EXT_HBLANK_L 0x0b
+#define VIDEO_EXT_HBLANK_H 0x0c
+#define VIDEO_EXT_HDELAY_L 0x0d
+#define VIDEO_EXT_HDELAY_H 0x0e
+#define VIDEO_EXT_HDURATION_L 0x0f
+#define VIDEO_EXT_HDURATION_H 0x10
+#define VIDEO_EXT_VTOTAL_L 0x11
+#define VIDEO_EXT_VTOTAL_H 0x12
+#define VIDEO_EXT_VBLANK 0x13
+#define VIDEO_EXT_VDELAY 0x14
+#define VIDEO_EXT_VDURATION 0x15
+
+#define AUDIO_CTRL1 0x35
+ enum {
+ CTS_SOURCE_INTERNAL = 0,
+ CTS_SOURCE_EXTERNAL
+ };
+ #define v_CTS_SOURCE(n) (n << 7)
+ enum {
+ DOWNSAMPLE_DISABLE = 0,
+ DOWNSAMPLE_1_2,
+ DOWNSAMPLE_1_4
+ };
+ #define v_DOWN_SAMPLE(n) (n << 5)
+ enum {
+ AUDIO_SOURCE_IIS = 0,
+ AUDIO_SOURCE_SPDIF
+ };
+ #define v_AUDIO_SOURCE(n) (n << 3)
+ #define v_MCLK_ENABLE(n) (n << 2)
+ enum {
+ MCLK_128FS = 0,
+ MCLK_256FS,
+ MCLK_384FS,
+ MCLK_512FS
+ };
+ #define v_MCLK_RATIO(n) (n)
+
+#define AUDIO_SAMPLE_RATE 0x37
+ enum {
+ AUDIO_32K = 0x3,
+ AUDIO_441K = 0x0,
+ AUDIO_48K = 0x2,
+ AUDIO_882K = 0x8,
+ AUDIO_96K = 0xa,
+ AUDIO_1764K = 0xc,
+ AUDIO_192K = 0xe,
+ };
+
+#define AUDIO_I2S_MODE 0x38
+ enum {
+ I2S_CHANNEL_1_2 = 1,
+ I2S_CHANNEL_3_4 = 3,
+ I2S_CHANNEL_5_6 = 7,
+ I2S_CHANNEL_7_8 = 0xf
+ };
+ #define v_I2S_CHANNEL(n) ((n) << 2)
+ enum {
+ I2S_STANDARD = 0,
+ I2S_LEFT_JUSTIFIED,
+ I2S_RIGHT_JUSTIFIED
+ };
+ #define v_I2S_MODE(n) (n)
+
+#define AUDIO_I2S_MAP 0x39
+#define AUDIO_I2S_SWAPS_SPDIF 0x3a
+ #define v_SPIDF_FREQ(n) (n)
+
+#define N_32K 0x1000
+#define N_441K 0x1880
+#define N_882K 0x3100
+#define N_1764K 0x6200
+#define N_48K 0x1800
+#define N_96K 0x3000
+#define N_192K 0x6000
+
+#define AUDIO_N_H 0x3f
+#define AUDIO_N_M 0x40
+#define AUDIO_N_L 0x41
+
+#define AUDIO_CTS_H 0x45
+#define AUDIO_CTS_M 0x46
+#define AUDIO_CTS_L 0x47
+
+
+#define DDC_CLK_L 0x4b
+#define DDC_CLK_H 0x4c
+
+#define EDID_SEGMENT_POINTER 0x4d
+#define EDID_WORD_ADDR 0x4e
+#define EDID_FIFO_OFFSET 0x4f
+#define EDID_FIFO_ADDR 0x50
+
+/* CONTROL_PACKET_BUF_INDEX */
+#define CONTROL_PACKET_BUF_INDEX 0x9f
+enum {
+ INFOFRAME_AVI = 0x06,
+ INFOFRAME_AAI = 0x08
+};
+#define CONTROL_PACKET_ADDR 0xa0
+
+
+#define SIZE_AVI_INFOFRAME 0x11 // 14 bytes
+#define SIZE_AUDIO_INFOFRAME 0x0F // 15 bytes
+enum {
+ AVI_COLOR_MODE_RGB = 0,
+ AVI_COLOR_MODE_YCBCR422,
+ AVI_COLOR_MODE_YCBCR444
+};
+enum {
+ AVI_COLORIMETRY_NO_DATA = 0,
+ AVI_COLORIMETRY_SMPTE_170M,
+ AVI_COLORIMETRY_ITU709,
+ AVI_COLORIMETRY_EXTENDED
+};
+enum {
+ AVI_CODED_FRAME_ASPECT_NO_DATA,
+ AVI_CODED_FRAME_ASPECT_4_3,
+ AVI_CODED_FRAME_ASPECT_16_9
+};
+enum {
+ ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
+ ACTIVE_ASPECT_RATE_4_3,
+ ACTIVE_ASPECT_RATE_16_9,
+ ACTIVE_ASPECT_RATE_14_9
+};
+
+#define HDCP_CTRL 0x52
+ #define m_HDMI_DVI (1 << 1)
+ #define v_HDMI_DVI(n) (n << 1)
+
+#define INTERRUPT_MASK1 0xc0
+#define INTERRUPT_STATUS1 0xc1
+ #define m_INT_HOTPLUG (1 << 7)
+ #define m_INT_ACTIVE_VSYNC (1 << 6)
+ #define m_INT_EDID_READY (1 << 2)
+
+#define INTERRUPT_MASK2 0xc2
+#define INTERRUPT_STATUS2 0xc3
+ #define m_INT_HDCP_ERR (1 << 7)
+ #define m_INT_BKSV_FLAG (1 << 6)
+ #define m_INT_HDCP_OK (1 << 4)
+
+#define HDMI_STATUS 0xc8
+ #define m_HOTPLUG (1 << 7)
+ #define m_DDC_SDA (1 << 5)
+ #define m_DDC_SDC (1 << 4)
+
+#define PHY_SYNC 0xce //sync phy parameter
+#define PHY_SYS_CTL 0xe0
+ #define m_TMDS_CLK_SOURCE (1<<5)
+ #define v_TMDS_FROM_PLL (0<<5)
+ #define v_TMDS_FROM_GEN (1<<5)
+ #define m_PHASE_CLK (1<<4)
+ #define v_DEFAULT_PHASE (0<<4)
+ #define v_SYNC_PHASE (0<<4)
+ #define m_TMDS_CURRENT_PWR (1<<3)
+ #define v_TURN_ON_CURRENT (0<<3)
+ #define v_CAT_OFF_CURRENT (1<<3)
+ #define m_BANDGAP_PWR (1<<2)
+ #define v_BANDGAP_PWR_UP (0<<2)
+ #define v_BANDGAP_PWR_DOWN (1<<2)
+ #define m_PLL_PWR (1<<1)
+ #define v_PLL_PWR_UP (0<<1)
+ #define v_PLL_PWR_DOWN (1<<1)
+ #define m_TMDS_CHG_PWR (1<<0)
+ #define v_TMDS_CHG_PWR_UP (0<<0)
+ #define v_TMDS_CHG_PWR_DOWN (1<<0)
+
+#define PHY_CHG_PWR 0xe1
+ #define v_CLK_CHG_PWR(n) ((n&1)<<3)
+ #define v_DATA_CHG_PWR(n) ((n&7)<<0)
+
+#define PHY_DRIVER 0xe2
+ #define v_CLK_MAIN_DRIVER(n) (n << 4)
+ #define v_DATA_MAIN_DRIVER(n) (n << 0)
+
+#define PHY_PRE_EMPHASIS 0xe3
+ #define v_PRE_EMPHASIS(n) ((n&7)<<4)
+ #define v_CLK_PRE_DRIVER(n) ((n&3)<<2)
+ #define v_DATA_PRE_DRIVER(n) ((n&3)<<0)
+
+#define PHY_FEEDBACK_DIV_RATIO_LOW 0xe7
+ #define v_FEEDBACK_DIV_LOW(n) (n&0xff)
+#define PHY_FEEDBACK_DIV_RATIO_HIGH 0xe8
+ #define v_FEEDBACK_DIV_HIGH(n) (n&1)
+
+#define PHY_PRE_DIV_RATIO 0xed
+ #define v_PRE_DIV_RATIO(n) (n&1f)
+
+#define HDMIRdReg(addr,val) g_rk616_hdmi->read_dev(g_rk616_hdmi,addr,(u32 *)val)
+
+#define HDMIWrReg(addr, val) do{ \
+ u32 temp = val; \
+ g_rk616_hdmi->write_dev(g_rk616_hdmi,addr,&temp); \
+ }while(0)
+
+#define HDMIMskReg(addr,Msk,val) do{ \
+ u32 temp=0; \
+ HDMIRdReg(addr,&temp); \
+ HDMIWrReg(addr, ((val)&(Msk))|(temp&(~Msk))); \
+ }while(0)
+
+extern struct mfd_rk616 *g_rk616_hdmi;
+
+extern int rk616_hdmi_initial(void);
+
+#endif