From 53a3f46e4b7fa1e42a29a18787f744798963576b Mon Sep 17 00:00:00 2001 From: Zheng Yang Date: Mon, 10 Sep 2012 14:12:30 +0800 Subject: [PATCH] rk3066B: support rk610 hdmi. --- arch/arm/mach-rk30/board-rk3066b-m701.c | 4 +- arch/arm/mach-rk30/board-rk3066b-sdk.c | 4 +- drivers/video/rockchip/hdmi/chips/Kconfig | 23 +- drivers/video/rockchip/hdmi/chips/Makefile | 1 + .../rockchip/hdmi/chips/rk2928/rk2928_hdmi.c | 9 +- .../rockchip/hdmi/chips/rk30/rk30_hdmi.c | 8 +- .../video/rockchip/hdmi/chips/rk610/Kconfig | 14 + .../video/rockchip/hdmi/chips/rk610/Makefile | 6 + .../rockchip/hdmi/chips/rk610/rk610_hdcp.c | 563 ++++++++++++++++++ .../rockchip/hdmi/chips/rk610/rk610_hdcp.h | 192 ++++++ .../rockchip/hdmi/chips/rk610/rk610_hdmi.c | 281 +++++++++ .../rockchip/hdmi/chips/rk610/rk610_hdmi.h | 31 + .../hdmi/chips/rk610/rk610_hdmi_hdcp.c | 156 +++++ .../rockchip/hdmi/chips/rk610/rk610_hdmi_hw.c | 409 +++++++++++++ .../rockchip/hdmi/chips/rk610/rk610_hdmi_hw.h | 237 ++++++++ drivers/video/rockchip/hdmi/rk_hdmi.h | 5 +- drivers/video/rockchip/hdmi/rk_hdmi_sysfs.c | 8 +- drivers/video/rockchip/hdmi/rk_hdmi_task.c | 4 +- 18 files changed, 1931 insertions(+), 24 deletions(-) mode change 100644 => 100755 arch/arm/mach-rk30/board-rk3066b-m701.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk610/Kconfig create mode 100755 drivers/video/rockchip/hdmi/chips/rk610/Makefile create mode 100755 drivers/video/rockchip/hdmi/chips/rk610/rk610_hdcp.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk610/rk610_hdcp.h create mode 100755 drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi.h create mode 100755 drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi_hdcp.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi_hw.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi_hw.h diff --git a/arch/arm/mach-rk30/board-rk3066b-m701.c b/arch/arm/mach-rk30/board-rk3066b-m701.c old mode 100644 new mode 100755 index 9d7c8c1a15ad..19e31e7a578e --- a/arch/arm/mach-rk30/board-rk3066b-m701.c +++ b/arch/arm/mach-rk30/board-rk3066b-m701.c @@ -1330,12 +1330,12 @@ static struct i2c_board_info __initdata i2c0_info[] = { .flags = 0, }, #endif -#ifdef CONFIG_RK610_HDMI +#ifdef CONFIG_HDMI_RK610 { .type = "rk610_hdmi", .addr = 0x46, .flags = 0, - .irq = RK29_PIN5_PA2, + .irq = INVALID_GPIO, }, #endif #ifdef CONFIG_SND_SOC_RK610 diff --git a/arch/arm/mach-rk30/board-rk3066b-sdk.c b/arch/arm/mach-rk30/board-rk3066b-sdk.c index 3cc34d0074bb..ebfec737d24f 100755 --- a/arch/arm/mach-rk30/board-rk3066b-sdk.c +++ b/arch/arm/mach-rk30/board-rk3066b-sdk.c @@ -1163,12 +1163,12 @@ static struct i2c_board_info __initdata i2c0_info[] = { .flags = 0, }, #endif -#ifdef CONFIG_RK610_HDMI +#ifdef CONFIG_HDMI_RK610 { .type = "rk610_hdmi", .addr = 0x46, .flags = 0, - .irq = RK29_PIN5_PA2, + .irq = INVALID_GPIO, }, #endif #ifdef CONFIG_SND_SOC_RK610 diff --git a/drivers/video/rockchip/hdmi/chips/Kconfig b/drivers/video/rockchip/hdmi/chips/Kconfig index c01155675dcc..75786166fdd2 100755 --- a/drivers/video/rockchip/hdmi/chips/Kconfig +++ b/drivers/video/rockchip/hdmi/chips/Kconfig @@ -1,21 +1,30 @@ -choice - prompt "HDMI chips select" config HDMI_RK30 bool "RK30 HDMI support" depends on LCDC_RK30 help Support rk30 hdmi if you say y here + +if HDMI_RK30 +source "drivers/video/rockchip/hdmi/chips/rk30/Kconfig" +endif + config HDMI_RK2928 bool "RK2928 HDMI support" depends on LCDC_RK2928 help Support rk2928 hdmi if you say y here -endchoice - -if HDMI_RK30 -source "drivers/video/rockchip/hdmi/chips/rk30/Kconfig" -endif if HDMI_RK2928 source "drivers/video/rockchip/hdmi/chips/rk2928/Kconfig" endif + +config HDMI_RK610 + bool "RK610 HDMI support" + depends on MFD_RK610 + help + Support rk610 hdmi if you say y here + +if HDMI_RK610 +source "drivers/video/rockchip/hdmi/chips/rk610/Kconfig" +endif + diff --git a/drivers/video/rockchip/hdmi/chips/Makefile b/drivers/video/rockchip/hdmi/chips/Makefile index a43ac0633d6a..38cb73e24fda 100755 --- a/drivers/video/rockchip/hdmi/chips/Makefile +++ b/drivers/video/rockchip/hdmi/chips/Makefile @@ -6,3 +6,4 @@ ccflags-$(CONFIG_HDMI_RK30_DEBUG) = -DDEBUG -DHDMI_DEBUG obj-$(CONFIG_HDMI_RK30) += rk30/ obj-$(CONFIG_HDMI_RK2928) += rk2928/ +obj-$(CONFIG_HDMI_RK610) += rk610/ diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.c b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.c index 18f61087ce8a..8a9926c4df03 100755 --- a/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.c +++ b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.c @@ -47,6 +47,10 @@ int rk2928_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void), 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); + + rk30_mux_api_set(GPIO0A7_I2C3_SDA_HDMI_DDCSDA_NAME, GPIO0A_GPIO0A7); + rk30_mux_api_set(GPIO0A6_I2C3_SCL_HDMI_DDCSCL_NAME, GPIO0A_GPIO0A6); + flush_delayed_work(&hdmi->delay_work); mutex_lock(&hdmi->enable_mutex); hdmi->suspend = 1; @@ -63,10 +67,7 @@ static void hdmi_early_suspend(struct early_suspend *h) wait_for_completion_interruptible_timeout(&hdmi->complete, msecs_to_jiffies(5000)); flush_delayed_work(&hdmi->delay_work); - // When HDMI 1.1V and 2.5V power off, DDC channel will be pull down, current is produced - // from VCC_IO which is pull up outside soc. We need to switch DDC IO to GPIO. - rk30_mux_api_set(GPIO0A7_I2C3_SDA_HDMI_DDCSDA_NAME, GPIO0A_GPIO0A7); - rk30_mux_api_set(GPIO0A6_I2C3_SCL_HDMI_DDCSCL_NAME, GPIO0A_GPIO0A6); + return; } diff --git a/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.c b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.c index 0aae519acc9e..aab3ecb4ee54 100755 --- a/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.c +++ b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.c @@ -47,6 +47,10 @@ int rk30_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void), 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); + // When HDMI 1.1V and 2.5V power off, DDC channel will be pull down, current is produced + // from VCC_IO which is pull up outside soc. We need to switch DDC IO to GPIO. + rk30_mux_api_set(GPIO0A2_HDMII2CSDA_NAME, GPIO0A_GPIO0A2); + rk30_mux_api_set(GPIO0A1_HDMII2CSCL_NAME, GPIO0A_GPIO0A1); flush_delayed_work(&hdmi->delay_work); mutex_lock(&hdmi->enable_mutex); hdmi->suspend = 1; @@ -63,10 +67,6 @@ static void hdmi_early_suspend(struct early_suspend *h) wait_for_completion_interruptible_timeout(&hdmi->complete, msecs_to_jiffies(5000)); flush_delayed_work(&hdmi->delay_work); - // When HDMI 1.1V and 2.5V power off, DDC channel will be pull down, current is produced - // from VCC_IO which is pull up outside soc. We need to switch DDC IO to GPIO. - rk30_mux_api_set(GPIO0A2_HDMII2CSDA_NAME, GPIO0A_GPIO0A2); - rk30_mux_api_set(GPIO0A1_HDMII2CSCL_NAME, GPIO0A_GPIO0A1); return; } diff --git a/drivers/video/rockchip/hdmi/chips/rk610/Kconfig b/drivers/video/rockchip/hdmi/chips/rk610/Kconfig new file mode 100755 index 000000000000..389f127ae56e --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk610/Kconfig @@ -0,0 +1,14 @@ +config HDCP_RK610 + bool "RK610 HDCP support" + depends on HDMI_RK610 + default n + help + HDCP Interface. This adds the High Definition Content Protection Interface. + See http://www.digital-cp.com/ for HDCP specification. + +config HDCP_RK610_DEBUG + bool "RK610 HDCP Debugging" + depends on HDCP_RK610 + default n + help + Enableds verbose debugging the the HDCP drivers diff --git a/drivers/video/rockchip/hdmi/chips/rk610/Makefile b/drivers/video/rockchip/hdmi/chips/rk610/Makefile new file mode 100755 index 000000000000..68750e29d820 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk610/Makefile @@ -0,0 +1,6 @@ +ccflags-$(CONFIG_RK_HDMI_DEBUG) = -DDEBUG -DHDMI_DEBUG +ccflags-$(CONFIG_HDCP_RK2928_DEBUG) = -DHDCP_DEBUG + +obj-$(CONFIG_HDMI_RK610) += rk610_hdmi_hw.o rk610_hdmi.o +obj-$(CONFIG_HDCP_RK610) += rk610_hdmi_hdcp.o rk610_hdcp.o + diff --git a/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdcp.c b/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdcp.c new file mode 100755 index 000000000000..61207db705e9 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdcp.c @@ -0,0 +1,563 @@ +#include +#include +#include +#include +#include +#include +#include +#include "rk610_hdmi.h" +#include "rk610_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; + } + + rk610_hdcp_disable(); + rk610_hdmi_sys_enalbe_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 = rk610_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 = rk610_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) +{ + rk610_hdmi_sys_enalbe_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); + rk610_hdcp_disable(); + if(event == HDCP_DISABLE_CTL) { + hdcp->hdcp_state = HDCP_DISABLED; + if(hdcp->hdmi_state == HDMI_STARTED) + rk610_hdmi_sys_enalbe_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; + + rk610_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 rk610_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 rk610_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; + } + + rk610_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 rk610_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(rk610_hdcp_init); +module_exit(rk610_hdcp_exit); diff --git a/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdcp.h b/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdcp.h new file mode 100755 index 000000000000..a33d1bcaa4e1 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdcp.h @@ -0,0 +1,192 @@ +#ifndef __RK610_HDCP_H__ +#define __RK610_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; + +#define HDCP_DEBUG + +#ifdef HDCP_DEBUG +#define DBG(format, ...) \ + printk(KERN_INFO "HDCP: " format "\n", ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +extern void rk610_hdcp_disable(void); +extern int rk610_hdcp_start_authentication(void); +extern int rk610_hdcp_check_bksv(void); +extern int rk610_hdcp_load_key2mem(struct hdcp_keys *key); +extern void rk610_hdcp_interrupt(char *status1, char *status2); +#endif /* __RK610_HDCP_H__ */ \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi.c b/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi.c new file mode 100755 index 000000000000..97fbb0734172 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi.c @@ -0,0 +1,281 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "rk610_hdmi.h" + +struct rk610_hdmi_pdata *rk610_hdmi = NULL; +struct hdmi *hdmi=NULL; + +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 rk610_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)) +{ + 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; + } + + #ifdef HDMI_USE_IRQ + if(hdmi->irq) + disable_irq(hdmi->irq); + #endif + + 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; + #ifdef HDMI_USE_IRQ + if(hdmi->enable && hdmi->irq) { + enable_irq(hdmi->irq); + } + #else + queue_delayed_work(rk610_hdmi->workqueue, &rk610_hdmi->delay_work, 100); + #endif + queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, msecs_to_jiffies(10)); + mutex_unlock(&hdmi->enable_mutex); + return; +} +#endif + +static void rk610_irq_work_func(struct work_struct *work) +{ + if(hdmi->suspend == 0) { + if(hdmi->enable == 1) { + rk610_hdmi_interrupt(); + if(hdmi->hdcp_irq_cb) + hdmi->hdcp_irq_cb(0); + } + #ifndef HDMI_USE_IRQ + queue_delayed_work(rk610_hdmi->workqueue, &rk610_hdmi->delay_work, 50); + #endif + } +} + +#ifdef HDMI_USE_IRQ +static irqreturn_t rk610_irq(int irq, void *dev_id) +{ + printk(KERN_INFO "rk610 irq triggered.\n"); + schedule_work(&rk610_hdmi->irq_work); + return IRQ_HANDLED; +} +#endif + +static int rk610_hdmi_i2c_probe(struct i2c_client *client,const struct i2c_device_id *id) +{ + int rc = 0; + + rk610_hdmi = kzalloc(sizeof(struct rk610_hdmi_pdata), GFP_KERNEL); + if(!rk610_hdmi) + { + dev_err(&client->dev, "no memory for state\n"); + return -ENOMEM; + } + rk610_hdmi->client = client; + i2c_set_clientdata(client, rk610_hdmi); + + hdmi = kmalloc(sizeof(struct hdmi), GFP_KERNEL); + if(!hdmi) + { + dev_err(&client->dev, "rk610 hdmi kmalloc fail!"); + goto err_kzalloc_hdmi; + } + memset(hdmi, 0, sizeof(struct hdmi)); + hdmi->dev = &client->dev; + + 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"); + rc = -ENXIO; + goto err_request_lcdc; + } + hdmi->xscale = 95; + hdmi->yscale = 95; + hdmi->insert = rk610_hdmi_sys_insert; + hdmi->remove = rk610_hdmi_sys_remove; + hdmi->control_output = rk610_hdmi_sys_enalbe_output; + hdmi->config_video = rk610_hdmi_sys_config_video; + hdmi->config_audio = rk610_hdmi_sys_config_audio; + hdmi->detect_hotplug = rk610_hdmi_sys_detect_hpd; + hdmi->read_edid = rk610_hdmi_sys_read_edid; + 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); + + rk610_hdmi_sys_init(); + +#ifdef HDMI_USE_IRQ + if(client->irq != INVALID_GPIO) { + INIT_WORK(&rk610_hdmi->irq_work, rk610_irq_work_func); + if((rc = gpio_request(client->irq, "hdmi gpio")) < 0) + { + dev_err(&client->dev, "fail to request gpio %d\n", client->irq); + goto err_request_lcdc; + } + hdmi->irq = gpio_to_irq(client->irq); + rk610_hdmi->gpio = client->irq; + gpio_pull_updown(client->irq, GPIOPullUp); + gpio_direction_input(client->irq); + if((rc = request_irq(rk610_hdmi->irq, rk610_irq, IRQF_TRIGGER_RISING, NULL, hdmi)) < 0) + { + dev_err(&client->dev, "fail to request hdmi irq\n"); + goto err_request_irq; + } + } + else +#else + { + rk610_hdmi->workqueue = create_singlethread_workqueue("rk610 irq"); + INIT_DELAYED_WORK(&(rk610_hdmi->delay_work), rk610_irq_work_func); + rk610_irq_work_func(NULL); + } +#endif + + dev_info(&client->dev, "rk610 hdmi i2c probe ok\n"); + + return 0; + +err_request_irq: + gpio_free(client->irq); +err_request_lcdc: + kfree(hdmi); + hdmi = NULL; +err_kzalloc_hdmi: + kfree(rk610_hdmi); + rk610_hdmi = NULL; + dev_err(&client->dev, "rk610 hdmi probe error\n"); + return rc; + +} + +static int __devexit rk610_hdmi_i2c_remove(struct i2c_client *client) +{ + hdmi_dbg(hdmi->dev, "%s\n", __func__); + if(hdmi) { + mutex_lock(&hdmi->enable_mutex); + if(!hdmi->suspend && hdmi->enable && hdmi->irq) + disable_irq(hdmi->irq); + mutex_unlock(&hdmi->enable_mutex); + if(hdmi->irq) + 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; + } + return 0; +} + +static void rk610_hdmi_i2c_shutdown(struct i2c_client *client) +{ + if(hdmi) { + #ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&hdmi->early_suspend); + #endif + } + printk(KERN_INFO "rk610 hdmi shut down.\n"); +} + +static const struct i2c_device_id rk610_hdmi_id[] = { + { "rk610_hdmi", 0 }, + { } +}; + +static struct i2c_driver rk610_hdmi_i2c_driver = { + .driver = { + .name = "rk610_hdmi", + .owner = THIS_MODULE, + }, + .probe = rk610_hdmi_i2c_probe, + .remove = rk610_hdmi_i2c_remove, + .shutdown = rk610_hdmi_i2c_shutdown, + .id_table = rk610_hdmi_id, +}; + +static int __init rk610_hdmi_init(void) +{ + return i2c_add_driver(&rk610_hdmi_i2c_driver); +} + +static void __exit rk610_hdmi_exit(void) +{ + i2c_del_driver(&rk610_hdmi_i2c_driver); +} + +module_init(rk610_hdmi_init); +//fs_initcall(rk610_init); +module_exit(rk610_hdmi_exit); diff --git a/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi.h b/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi.h new file mode 100755 index 000000000000..5ddd8aeb2da0 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi.h @@ -0,0 +1,31 @@ +#ifndef __RK610_HDMI_H__ +#define __RK610_HDMI_H__ +#include "../../rk_hdmi.h" + +#define HDMI_SOURCE_DEFAULT HDMI_SOURCE_LCDC0 + +struct rk610_hdmi_pdata { + int gpio; + struct i2c_client *client; + struct delayed_work delay_work; + #ifndef HDMI_USE_IRQ + struct workqueue_struct *workqueue; + #endif +}; + +extern struct rk610_hdmi_pdata *rk610_hdmi; + +extern int rk610_hdmi_sys_init(void); +extern void rk610_hdmi_interrupt(void); +extern int rk610_hdmi_sys_detect_hpd(void); +extern int rk610_hdmi_sys_insert(void); +extern int rk610_hdmi_sys_remove(void); +extern int rk610_hdmi_sys_read_edid(int block, unsigned char *buff); +extern int rk610_hdmi_sys_config_video(struct hdmi_video_para *vpara); +extern int rk610_hdmi_sys_config_audio(struct hdmi_audio *audio); +extern void rk610_hdmi_sys_enalbe_output(int enable); +extern int rk610_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 diff --git a/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi_hdcp.c b/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi_hdcp.c new file mode 100755 index 000000000000..e690f715b596 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi_hdcp.c @@ -0,0 +1,156 @@ +#include +#include "rk610_hdmi.h" +#include "rk610_hdmi_hw.h" +#include "rk610_hdcp.h" + +static char rk610_hdmi_i2c_read_reg(char reg) +{ + char val = 0; + + if(i2c_master_reg8_recv(rk610_hdmi->client, reg, &val, 1, 100*1000) > 0) + return val; + else { + printk(KERN_ERR "[%s] read reg %02x error\n", __FUNCTION__, reg); + return 0; + } +} +static char rk610_hdmi_i2c_write_reg(char reg, char val) +{ + if(i2c_master_reg8_send(rk610_hdmi->client, reg, &val, 1, 100*1000) > 0) + return 0; + else { + printk(KERN_ERR "[%s] write reg %02x error\n", __FUNCTION__, reg); + return -EINVAL; + } +} + +#define HDCPWrReg rk610_hdmi_i2c_write_reg +#define HDCPRdReg rk610_hdmi_i2c_read_reg +#define HDCPMskReg(temp, addr, msk, val) \ + temp = HDCPRdReg(addr) & (0xFF - (msk)) ; \ + HDCPWrReg(addr, temp | ( (val) & (msk) )); + +void rk610_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 rk610_hdcp_load_key2mem(struct hdcp_keys *key) +{ + int i; + DBG("HDCP: rk610_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: rk610_hdcp_load_key2mem end"); + return HDCP_OK; +} + +int rk610_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); + + temp = HDCPRdReg(HDCP_KEY_STATUS); + while( ( temp & m_KEY_READY) == 0 ) { + if(retry > 10) { + printk(KERN_ERR "HDCP: loaded key error\n"); + return HDCP_KEY_ERR; + } + rk610_hdcp_load_key2mem(hdcp->keys); + msleep(1); + temp = HDCPRdReg(HDCP_KEY_STATUS); + } + + // 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 rk610_hdcp_check_bksv(void) +{ + int i, j; + char temp = 0, bksv[5]; + char *invalidkey; + + for(i = 0; i < 5; i++) { + bksv[i] = HDCPRdReg(HDCP_KSV_BYTE0 + (4 - i)) & 0xFF; + } + DBG("bksv is 0x%02x%02x%02x%02x%02x", bksv[0], bksv[1], bksv[2], bksv[3], bksv[4]); + + 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 rk610_hdcp_interrupt(char *status1, char *status2) +{ + char interrupt1 = HDCPRdReg(HDCP_INT_STATUS1); + char interrupt2 = HDCPRdReg(HDCP_INT_STATUS2); + if(interrupt1) { + HDCPWrReg(HDCP_INT_STATUS1, interrupt1); + if(interrupt1 & m_INT_HDCP_ERR) + printk(KERN_INFO "HDCP: Error 0x%02x\n", HDCPRdReg(HDCP_ERROR)); + } + if(interrupt2) + HDCPWrReg(HDCP_INT_STATUS2, interrupt2); + + *status1 = interrupt1; + *status2 = interrupt2; +} \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi_hw.c b/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi_hw.c new file mode 100755 index 000000000000..9c3396f5f898 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi_hw.c @@ -0,0 +1,409 @@ +#include +#include "rk610_hdmi.h" +#include "rk610_hdmi_hw.h" +#include + +static atomic_t edid_ready; + +static int rk610_hdmi_i2c_read_reg(char reg, char *val) +{ + if(i2c_master_reg8_recv(rk610_hdmi->client, reg, val, 1, 100*1000) > 0) + return 0; + else { + printk("[%s] reg %02x error\n", __FUNCTION__, reg); + return -EINVAL; + } +} +static int rk610_hdmi_i2c_write_reg(char reg, char val) +{ + return i2c_master_reg8_send(rk610_hdmi->client, reg, &val, 1, 100*1000) > 0? 0: -EINVAL; +} + +#define HDMIWrReg rk610_hdmi_i2c_write_reg + +int rk610_hdmi_sys_init(void) +{ + // System power power off + HDMIWrReg(SYS_CTRL, v_REG_CLK_SOURCE_IIS | v_PWR_OFF | v_INT_POL_HIGH); + + //Synchronize analog module. +// HDMIWrReg(PHY_SYNC, 0x00); +// HDMIWrReg(PHY_SYNC, 0x01); + + // set hdmi phy parameters + // driver mode + HDMIWrReg(PHY_DRIVER, v_MAIN_DRIVER(8)| v_PRE_DRIVER(0) | v_TX_ENABLE(0)); +// HDMIWrReg(PHY_PRE_EMPHASIS, 0x04); + HDMIWrReg(PHY_PRE_EMPHASIS, v_PRE_EMPHASIS(0) | v_TMDS_PWRDOWN(1)); //Driver power down + // pll mode + HDMIWrReg(0xe8, 0x10); + HDMIWrReg(0xe6, 0x2c); + + HDMIWrReg(PHY_PLL_CTRL, v_PLL_DISABLE(1) | v_PLL_RESET(1) | v_TMDS_RESET(1)); + HDMIWrReg(PHY_PLL_LDO_PWR, v_LDO_PWR_DOWN(1)); + HDMIWrReg(PHY_BANDGAP_PWR, v_BANDGAP_PWR_DOWN); + + // Enable Hotplug interrupt + HDMIWrReg(INTERRUPT_MASK1, m_INT_HOTPLUG); + return HDMI_ERROR_SUCESS; +} + +void rk610_hdmi_interrupt() +{ + char interrupt = 0; + + if(rk610_hdmi_i2c_read_reg(INTERRUPT_STATUS1, &interrupt)) + return; + + HDMIWrReg(INTERRUPT_STATUS1, interrupt); + + if(interrupt) + HDMIWrReg(INTERRUPT_STATUS1, interrupt); + + if(interrupt & m_INT_HOTPLUG) { + hdmi_dbg(hdmi->dev, "%s interrupt %02x\n", __FUNCTION__, interrupt); + if(hdmi->state == HDMI_SLEEP) + hdmi->state = WAIT_HOTPLUG; + queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, msecs_to_jiffies(10)); + } + else if(interrupt & m_INT_EDID_READY) { + atomic_set(&edid_ready, 1); + } +} + +int rk610_hdmi_sys_detect_hpd(void) +{ + char hdmi_status = 0; + + #ifdef HDMI_USE_IRQ + rk610_hdmi_i2c_read_reg(INTERRUPT_STATUS1, &hdmi_status); + HDMIWrReg(INTERRUPT_STATUS1, hdmi_status); + #endif + hdmi_status = 0; + rk610_hdmi_i2c_read_reg(HDMI_STATUS, &hdmi_status); +// printk("%s value is %02x\n", __FUNCTION__, hdmi_status); + if(hdmi_status) + return HDMI_HPD_ACTIVED; + else + return HDMI_HPD_REMOVED; +} + +#define SYSCLK 11289600 +#define DDC_CLK 100000 +int rk610_hdmi_sys_read_edid(int block, unsigned char *buff) +{ + char value; + int count, rc = HDMI_ERROR_EDID; + int trytime = 2; + + // Config DDC bus clock: ddc_clk = reg_clk/4*(reg 0x4c 0x4b) + // when reg00 select reg_clk equal to sys_clk which is equal + // to i2s clk, it gernerally is 11.2896MHz. + + count = SYSCLK/(DDC_CLK*4); + HDMIWrReg(DDC_CLK_L, count & 0xFF); + HDMIWrReg(DDC_CLK_H, (count >> 8) & 0xFF); + + // Enable EDID Interrupt +// edid_ready = 0; + atomic_set(&edid_ready, 0); + value = 0; + rk610_hdmi_i2c_read_reg(INTERRUPT_MASK1, &value); + value |= m_INT_EDID_READY; + HDMIWrReg(INTERRUPT_MASK1, value); + + + while(trytime--) { + // Reset FIFO offset + HDMIWrReg(EDID_FIFO_OFFSET, 0); + // Set EDID read addr. + HDMIWrReg(EDID_WORD_ADDR, (block%2) * 0x80); + HDMIWrReg(EDID_SEGMENT_POINTER, block/2); + + count = 0; + while(count++ < 10) + { + value = atomic_read(&edid_ready); + if(value) + { + for(count = 0; count < 128; count++) + rk610_hdmi_i2c_read_reg(EDID_FIFO_ADDR, buff + count); + rc = HDMI_ERROR_SUCESS; + break; + } + msleep(100); + } + } + // Disable EDID interrupt. + value = 0; + rk610_hdmi_i2c_read_reg(INTERRUPT_MASK1, &value); + value &= ~m_INT_EDID_READY; + HDMIWrReg(INTERRUPT_MASK1, value); + return rc; +} + +static void rk610_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]); +} + +int rk610_hdmi_sys_config_video(struct hdmi_video_para *vpara) +{ + char 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); + + #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) { + rk610_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__); + } + + // Power on TMDS + HDMIWrReg(PHY_PRE_EMPHASIS, v_PRE_EMPHASIS(0) | v_TMDS_PWRDOWN(0)); // TMDS power on + + // Enable TMDS + value = 0; + rk610_hdmi_i2c_read_reg(PHY_DRIVER, &value); + value |= v_TX_ENABLE(1); + HDMIWrReg(PHY_DRIVER, value); + + return 0; +} + +static void rk610_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]); +} + +int rk610_hdmi_sys_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); + rk610_hdmi_config_aai(); + + return 0; +} + +void rk610_hdmi_sys_enalbe_output(int enable) +{ + char mutestatus = 0; + + if(enable) { + rk610_hdmi_i2c_read_reg(AV_MUTE, &mutestatus); + if(mutestatus && (m_AUDIO_MUTE | m_VIDEO_BLACK)) { + HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0)); + HDMIWrReg(SYS_CTRL, v_REG_CLK_SOURCE_IIS | v_PWR_ON | v_INT_POL_HIGH); + HDMIWrReg(SYS_CTRL, v_REG_CLK_SOURCE_IIS | v_PWR_OFF | v_INT_POL_HIGH); + HDMIWrReg(SYS_CTRL, v_REG_CLK_SOURCE_IIS | v_PWR_ON | v_INT_POL_HIGH); + if(hdmi->hdcp_cb) + hdmi->hdcp_cb(); + } + } + else { + HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1)); + } +} + +int rk610_hdmi_sys_insert(void) +{ + hdmi_dbg(hdmi->dev, "%s \n", __FUNCTION__); + //Bring up analog module. + HDMIWrReg(PHY_BANDGAP_PWR, v_BANDGAP_PWR_UP); //BG power on + HDMIWrReg(PHY_PLL_LDO_PWR, 0x00); //PLL power on + msleep(1); + HDMIWrReg(PHY_PLL_CTRL, v_PLL_DISABLE(0)); //Analog reset + return 0; +} + +int rk610_hdmi_sys_remove(void) +{ + hdmi_dbg(hdmi->dev, "%s \n", __FUNCTION__); + if(hdmi->hdcp_power_off_cb) + hdmi->hdcp_power_off_cb(); + HDMIWrReg(PHY_DRIVER, v_MAIN_DRIVER(8)| v_PRE_DRIVER(0) | v_TX_ENABLE(0)); + HDMIWrReg(PHY_PRE_EMPHASIS, v_PRE_EMPHASIS(0) | v_TMDS_PWRDOWN(1)); //Driver power down + HDMIWrReg(PHY_PLL_CTRL, v_PLL_DISABLE(1) | v_PLL_RESET(1) | v_TMDS_RESET(1)); + HDMIWrReg(PHY_PLL_LDO_PWR, v_LDO_PWR_DOWN(1)); + HDMIWrReg(PHY_BANDGAP_PWR, v_BANDGAP_PWR_DOWN); + return 0; +} \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi_hw.h b/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi_hw.h new file mode 100755 index 000000000000..0e4cce913f74 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk610/rk610_hdmi_hw.h @@ -0,0 +1,237 @@ +#ifndef _RK610_HDMI_HW_H +#define _RK610_HDMI_HW_H + +enum { + OUTPUT_DVI = 0, + OUTPUT_HDMI + }; + +#define SYS_CTRL 0x00 + #define m_INT_POL (1 << 0) + #define m_POWER (1 << 1) + #define m_REG_CLK_SOURCE (1 << 2) + #define v_INT_POL_HIGH 1 + #define v_INT_POL_LOW 0 + #define v_PWR_ON (0 << 1) + #define v_PWR_OFF (1 << 1) + #define v_REG_CLK_SOURCE_TMDS (0 << 2) + #define v_REG_CLK_SOURCE_IIS (1 << 2) +#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_DRIVER 0xe1 + #define v_MAIN_DRIVER(n) (n << 4) + #define v_PRE_DRIVER(n) (n << 2) + #define v_TX_ENABLE(n) (n << 1) + +#define PHY_PRE_EMPHASIS 0xe2 + #define v_PRE_EMPHASIS(n) (n << 4) + #define v_TMDS_PWRDOWN(n) (n) + +#define PHY_PLL_TEST 0xe3 +#define PHY_BANDGAP_PWR 0xe4 + #define v_BANDGAP_PWR_DOWN 0x03 + #define v_BANDGAP_PWR_UP 0 + +#define PHY_PLL_CTRL 0xe5 + #define v_PLL_DISABLE(n) (n << 4) + #define v_PLL_RESET(n) (n << 3) + #define v_TMDS_RESET(n) (n << 2) + +#define PHY_PLL_LDO_PWR 0xe7 + #define v_LDO_PWR_DOWN(n) (n << 2) + +#endif \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/rk_hdmi.h b/drivers/video/rockchip/hdmi/rk_hdmi.h index 7222a8cf33e2..5c4cf8ad4803 100755 --- a/drivers/video/rockchip/hdmi/rk_hdmi.h +++ b/drivers/video/rockchip/hdmi/rk_hdmi.h @@ -30,12 +30,13 @@ enum { /* If HDMI_ENABLE, system will auto configure output mode according to EDID * If HDMI_DISABLE, system will output mode according to macro HDMI_VIDEO_DEFAULT_MODE */ -#define HDMI_AUTO_CONFIGURE HDMI_ENABLE +#define HDMI_AUTO_CONFIGURE HDMI_DISABLE /* default HDMI output audio mode */ #define HDMI_AUDIO_DEFAULT_CHANNEL 2 #define HDMI_AUDIO_DEFAULT_RATE HDMI_AUDIO_FS_44100 #define HDMI_AUDIO_DEFAULT_WORD_LENGTH HDMI_AUDIO_WORD_LENGTH_16bit + enum { VIDEO_INPUT_RGB_YCBCR_444 = 0, VIDEO_INPUT_YCBCR422, @@ -45,11 +46,13 @@ enum { VIDEO_INPUT_RGB444_DDR, VIDEO_INPUT_YCBCR422_DDR }; + enum { VIDEO_OUTPUT_RGB444 = 0, VIDEO_OUTPUT_YCBCR444, VIDEO_OUTPUT_YCBCR422 }; + enum { VIDEO_INPUT_COLOR_RGB = 0, VIDEO_INPUT_COLOR_YCBCR diff --git a/drivers/video/rockchip/hdmi/rk_hdmi_sysfs.c b/drivers/video/rockchip/hdmi/rk_hdmi_sysfs.c index dc0c5d82fb4f..14f7924d041c 100755 --- a/drivers/video/rockchip/hdmi/rk_hdmi_sysfs.c +++ b/drivers/video/rockchip/hdmi/rk_hdmi_sysfs.c @@ -33,13 +33,17 @@ static int hdmi_set_enable(struct rk_display_device *device, int enable) } if(enable == 0) { - disable_irq(hdmi->irq); + if(hdmi->irq) + disable_irq(hdmi->irq); mutex_unlock(&hdmi->enable_mutex); hdmi->command = HDMI_CONFIG_ENABLE; queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0); } else { - enable_irq(hdmi->irq); + if(hdmi->irq) + enable_irq(hdmi->irq); + else + queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0); mutex_unlock(&hdmi->enable_mutex); } return 0; diff --git a/drivers/video/rockchip/hdmi/rk_hdmi_task.c b/drivers/video/rockchip/hdmi/rk_hdmi_task.c index 5f34b23236dc..3fe5274551f0 100755 --- a/drivers/video/rockchip/hdmi/rk_hdmi_task.c +++ b/drivers/video/rockchip/hdmi/rk_hdmi_task.c @@ -93,11 +93,11 @@ void hdmi_sys_remove(void) static void hdmi_sys_sleep(void) { mutex_lock(&hdmi->enable_mutex); - if(hdmi->enable) + if(hdmi->enable && hdmi->irq) disable_irq(hdmi->irq); hdmi->state = HDMI_SLEEP; hdmi->remove(); - if(hdmi->enable) + if(hdmi->enable && hdmi->irq) enable_irq(hdmi->irq); mutex_unlock(&hdmi->enable_mutex); } -- 2.34.1