From 44084d4e12ef823977d18dd67fb7ffd978d86421 Mon Sep 17 00:00:00 2001 From: yzq Date: Sun, 7 Apr 2013 16:13:27 +0800 Subject: [PATCH] support rk616 hdmi --- drivers/video/rockchip/hdmi/chips/Kconfig | 10 + drivers/video/rockchip/hdmi/chips/Makefile | 1 + .../video/rockchip/hdmi/chips/rk616/Kconfig | 14 + .../video/rockchip/hdmi/chips/rk616/Makefile | 9 + .../rockchip/hdmi/chips/rk616/rk616_hdcp.c | 563 ++++++++++++++++++ .../rockchip/hdmi/chips/rk616/rk616_hdcp.h | 190 ++++++ .../rockchip/hdmi/chips/rk616/rk616_hdmi.c | 243 ++++++++ .../rockchip/hdmi/chips/rk616/rk616_hdmi.h | 18 + .../hdmi/chips/rk616/rk616_hdmi_hdcp.c | 143 +++++ .../rockchip/hdmi/chips/rk616/rk616_hdmi_hw.c | 470 +++++++++++++++ .../rockchip/hdmi/chips/rk616/rk616_hdmi_hw.h | 288 +++++++++ 11 files changed, 1949 insertions(+) create mode 100755 drivers/video/rockchip/hdmi/chips/rk616/Kconfig create mode 100755 drivers/video/rockchip/hdmi/chips/rk616/Makefile create mode 100755 drivers/video/rockchip/hdmi/chips/rk616/rk616_hdcp.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk616/rk616_hdcp.h create mode 100755 drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi.h create mode 100755 drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi_hdcp.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi_hw.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi_hw.h diff --git a/drivers/video/rockchip/hdmi/chips/Kconfig b/drivers/video/rockchip/hdmi/chips/Kconfig index 2ab2c527b341..69064120c159 100755 --- a/drivers/video/rockchip/hdmi/chips/Kconfig +++ b/drivers/video/rockchip/hdmi/chips/Kconfig @@ -37,6 +37,16 @@ if HDMI_RK610 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 diff --git a/drivers/video/rockchip/hdmi/chips/Makefile b/drivers/video/rockchip/hdmi/chips/Makefile index 65872d19a944..6a6c23409760 100755 --- a/drivers/video/rockchip/hdmi/chips/Makefile +++ b/drivers/video/rockchip/hdmi/chips/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_HDMI_RK30) += rk30/ obj-$(CONFIG_HDMI_RK2928) += rk2928/ obj-$(CONFIG_HDMI_RK610) += rk610/ obj-$(CONFIG_HDMI_CAT66121) += cat66121/ +obj-$(CONFIG_HDMI_RK616) += rk616/ diff --git a/drivers/video/rockchip/hdmi/chips/rk616/Kconfig b/drivers/video/rockchip/hdmi/chips/rk616/Kconfig new file mode 100755 index 000000000000..dff44615ab97 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk616/Kconfig @@ -0,0 +1,14 @@ +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 diff --git a/drivers/video/rockchip/hdmi/chips/rk616/Makefile b/drivers/video/rockchip/hdmi/chips/rk616/Makefile new file mode 100755 index 000000000000..685f5cad6cc5 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk616/Makefile @@ -0,0 +1,9 @@ +# +# 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 diff --git a/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdcp.c b/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdcp.c new file mode 100755 index 000000000000..8b03fedf0f49 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdcp.c @@ -0,0 +1,563 @@ +#include +#include +#include +#include +#include +#include +#include +#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); diff --git a/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdcp.h b/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdcp.h new file mode 100755 index 000000000000..d97b0b6c04f7 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdcp.h @@ -0,0 +1,190 @@ +#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__ */ diff --git a/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi.c b/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi.c new file mode 100755 index 000000000000..c98e61c4aa11 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi.c @@ -0,0 +1,243 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#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); diff --git a/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi.h b/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi.h new file mode 100755 index 000000000000..7cc85ccce3a0 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi.h @@ -0,0 +1,18 @@ +#ifndef __RK616_HDMI_H__ +#define __RK616_HDMI_H__ + +#include "../../rk_hdmi.h" +#include + +#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__ */ diff --git a/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi_hdcp.c b/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi_hdcp.c new file mode 100755 index 000000000000..67952427133f --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi_hdcp.c @@ -0,0 +1,143 @@ +#include +#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; +} diff --git a/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi_hw.c b/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi_hw.c new file mode 100755 index 000000000000..8af156e4823d --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi_hw.c @@ -0,0 +1,470 @@ +#include +#include +#include +#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; +} diff --git a/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi_hw.h b/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi_hw.h new file mode 100755 index 000000000000..40b1863f8241 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk616/rk616_hdmi_hw.h @@ -0,0 +1,288 @@ +#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 -- 2.34.1