From 302f5ef634edc4c19b84f5ebec93c9828fe6ec08 Mon Sep 17 00:00:00 2001 From: hjc Date: Tue, 15 Jul 2014 19:03:02 +0800 Subject: [PATCH] rk3036 hdmi: rk3036 hdmi driver independent from rk616 --- drivers/video/rockchip/hdmi/chips/Kconfig | 12 +- drivers/video/rockchip/hdmi/chips/Makefile | 1 + .../video/rockchip/hdmi/chips/rk3036/Kconfig | 13 + .../video/rockchip/hdmi/chips/rk3036/Makefile | 9 + .../rockchip/hdmi/chips/rk3036/rk3036_hdcp.c | 563 +++++++++++++++++ .../rockchip/hdmi/chips/rk3036/rk3036_hdcp.h | 190 ++++++ .../rockchip/hdmi/chips/rk3036/rk3036_hdmi.c | 483 +++++++++++++++ .../rockchip/hdmi/chips/rk3036/rk3036_hdmi.h | 38 ++ .../hdmi/chips/rk3036/rk3036_hdmi_hdcp.c | 143 +++++ .../hdmi/chips/rk3036/rk3036_hdmi_hw.c | 573 ++++++++++++++++++ .../hdmi/chips/rk3036/rk3036_hdmi_hw.h | 323 ++++++++++ drivers/video/rockchip/hdmi/rk_hdmi_lcdc.c | 2 +- 12 files changed, 2348 insertions(+), 2 deletions(-) create mode 100755 drivers/video/rockchip/hdmi/chips/rk3036/Kconfig create mode 100755 drivers/video/rockchip/hdmi/chips/rk3036/Makefile create mode 100755 drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdcp.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdcp.h create mode 100755 drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi.h create mode 100755 drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi_hdcp.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi_hw.c create mode 100755 drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi_hw.h diff --git a/drivers/video/rockchip/hdmi/chips/Kconfig b/drivers/video/rockchip/hdmi/chips/Kconfig index 8f72dfed7b94..93462410064a 100755 --- a/drivers/video/rockchip/hdmi/chips/Kconfig +++ b/drivers/video/rockchip/hdmi/chips/Kconfig @@ -39,7 +39,7 @@ endif config HDMI_RK616 bool "RK616 HDMI support" -#depends on MFD_RK616 || ARCH_RK3026 +depends on MFD_RK616 || ARCH_RK3026 default y help Support rk616 hdmi if you say y here @@ -48,6 +48,16 @@ if HDMI_RK616 source "drivers/video/rockchip/hdmi/chips/rk616/Kconfig" endif +config HDMI_RK3036 + bool "RK3036 HDMI support" + default y + help + Support rk3036 hdmi if you say y here + +if HDMI_RK3036 +source "drivers/video/rockchip/hdmi/chips/rk3036/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 0f2e4a1f146d..e61356c98eef 100755 --- a/drivers/video/rockchip/hdmi/chips/Makefile +++ b/drivers/video/rockchip/hdmi/chips/Makefile @@ -9,4 +9,5 @@ obj-$(CONFIG_HDMI_RK2928) += rk2928/ obj-$(CONFIG_HDMI_RK610) += rk610/ obj-$(CONFIG_HDMI_CAT66121) += cat66121/ obj-$(CONFIG_HDMI_RK616) += rk616/ +obj-$(CONFIG_HDMI_RK3036) += rk3036/ obj-y += rk3288/ diff --git a/drivers/video/rockchip/hdmi/chips/rk3036/Kconfig b/drivers/video/rockchip/hdmi/chips/rk3036/Kconfig new file mode 100755 index 000000000000..c60c1ffc8971 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk3036/Kconfig @@ -0,0 +1,13 @@ +config HDCP_RK3036 + bool "RK3036 HDCP support" + default n + help + HDCP Interface. This adds the High Definition Content Protection Interface. + See http://www.digital-cp.com/ for HDCP specification. + +config HDCP_RK3036_DEBUG + bool "RK3036 HDCP Debugging" + depends on HDCP_RK3036 + default n + help + Enableds verbose debugging the the HDCP drivers diff --git a/drivers/video/rockchip/hdmi/chips/rk3036/Makefile b/drivers/video/rockchip/hdmi/chips/rk3036/Makefile new file mode 100755 index 000000000000..f6f27f932d72 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk3036/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_RK3036) += rk3036_hdmi_hw.o rk3036_hdmi.o +obj-$(CONFIG_HDCP_RK3036) += rk3036_hdmi_hdcp.o rk3036_hdcp.o diff --git a/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdcp.c b/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdcp.c new file mode 100755 index 000000000000..5d7d419deee5 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdcp.c @@ -0,0 +1,563 @@ +#include +#include +#include +#include +#include +#include +#include +#include "rk3036_hdmi.h" +#include "rk3036_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; + } + + rk3036_hdcp_disable(); + rk3036_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 = rk3036_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 = rk3036_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) +{ + rk3036_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); + rk3036_hdcp_disable(); + if(event == HDCP_DISABLE_CTL) { + hdcp->hdcp_state = HDCP_DISABLED; + if(hdcp->hdmi_state == HDMI_STARTED) + rk3036_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; + + rk3036_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 rk3036_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 rk3036_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; + } + + rk3036_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 rk3036_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(rk3036_hdcp_init); +module_exit(rk3036_hdcp_exit); diff --git a/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdcp.h b/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdcp.h new file mode 100755 index 000000000000..c465f54d079d --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdcp.h @@ -0,0 +1,190 @@ +#ifndef __RK3036_HDCP_H__ +#define __RK3036_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 rk3036_hdcp_disable(void); +extern int rk3036_hdcp_start_authentication(void); +extern int rk3036_hdcp_check_bksv(void); +extern int rk3036_hdcp_load_key2mem(struct hdcp_keys *key); +extern void rk3036_hdcp_interrupt(char *status1, char *status2); +#endif /* __rk3036_HDCP_H__ */ diff --git a/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi.c b/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi.c new file mode 100755 index 000000000000..f96bb0a69596 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi.c @@ -0,0 +1,483 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if defined(CONFIG_DEBUG_FS) +#include +#include +#include +#endif + +#include "rk3036_hdmi.h" +#include "rk3036_hdmi_hw.h" + +static struct rk_hdmi_device *hdmi_dev; + +#if defined(CONFIG_DEBUG_FS) +static int rk3036_hdmi_reg_show(struct seq_file *s, void *v) +{ + int i = 0; + u32 val = 0; + + seq_puts(s, "\n\n>>>rk3036_ctl reg"); + for (i = 0; i < 16; i++) + seq_printf(s, " %2x", i); + + seq_puts(s, + "\n-----------------------------------------------------------------"); + + for (i = 0; i <= PHY_PRE_DIV_RATIO; i++) { + hdmi_readl(hdmi_dev, i, &val); + if (i % 16 == 0) + seq_printf(s, "\n>>>rk3036_ctl %2x:", i); + seq_printf(s, " %02x", val); + + } + seq_puts(s, + "\n-----------------------------------------------------------------\n"); + + return 0; +} + +static ssize_t rk3036_hdmi_reg_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + u32 reg; + u32 val; + char kbuf[25]; + struct hdmi *hdmi_drv = &hdmi_dev->driver; + + if (copy_from_user(kbuf, buf, count)) + return -EFAULT; + sscanf(kbuf, "%x%x", ®, &val); + if ((reg < 0) || (reg > 0xed)) { + dev_info(hdmi_drv->dev, "it is no hdmi reg\n"); + return count; + } + dev_info(hdmi_drv->dev, "/**********rk3036 reg config******/"); + dev_info(hdmi_drv->dev, "\n reg=%x val=%x\n", reg, val); + hdmi_writel(hdmi_dev, reg, val); + + return count; +} + +static int rk3036_hdmi_reg_open(struct inode *inode, struct file *file) +{ + return single_open(file, rk3036_hdmi_reg_show, NULL); +} + +static const struct file_operations rk3036_hdmi_reg_fops = { + .owner = THIS_MODULE, + .open = rk3036_hdmi_reg_open, + .read = seq_read, + .write = rk3036_hdmi_reg_write, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +static int rk3036_hdmi_clk_enable(struct rk_hdmi_device *hdmi_dev) +{ + if (!hdmi_dev->clk_on) { + clk_prepare_enable(hdmi_dev->hclk); + spin_lock(&hdmi_dev->reg_lock); + hdmi_dev->clk_on = 1; + spin_unlock(&hdmi_dev->reg_lock); + } + + return 0; +} + +static int rk3036_hdmi_clk_disable(struct rk_hdmi_device *hdmi_dev) +{ + if (!hdmi_dev->clk_on) { + spin_lock(&hdmi_dev->reg_lock); + hdmi_dev->clk_on = 0; + spin_unlock(&hdmi_dev->reg_lock); + clk_disable_unprepare(hdmi_dev->hclk); + } + + return 0; +} + +int rk3036_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)) +{ + struct hdmi *hdmi_drv = &hdmi_dev->driver; + + if (hdmi_drv == NULL) + return HDMI_ERROR_FALSE; + + hdmi_drv->hdcp_cb = hdcp_cb; + hdmi_drv->hdcp_irq_cb = hdcp_irq_cb; + hdmi_drv->hdcp_power_on_cb = hdcp_power_on_cb; + hdmi_drv->hdcp_power_off_cb = hdcp_power_off_cb; + + return HDMI_ERROR_SUCESS; +} + +static void rk3036_hdmi_early_suspend(void) +{ + struct hdmi *hdmi_drv = &hdmi_dev->driver; + + hdmi_dbg(hdmi_drv->dev, "hdmi enter early suspend pwr %d state %d\n", + hdmi_drv->pwr_mode, hdmi_drv->state); + + flush_delayed_work(&hdmi_drv->delay_work); + mutex_lock(&hdmi_drv->enable_mutex); + hdmi_drv->suspend = 1; + if (!hdmi_drv->enable) { + mutex_unlock(&hdmi_drv->enable_mutex); + return; + } + + if (hdmi_drv->irq) + disable_irq(hdmi_drv->irq); + + mutex_unlock(&hdmi_drv->enable_mutex); + hdmi_drv->command = HDMI_CONFIG_ENABLE; + init_completion(&hdmi_drv->complete); + hdmi_drv->wait = 1; + queue_delayed_work(hdmi_drv->workqueue, &hdmi_drv->delay_work, 0); + wait_for_completion_interruptible_timeout(&hdmi_drv->complete, + msecs_to_jiffies(5000)); + flush_delayed_work(&hdmi_drv->delay_work); + +} + +static void rk3036_hdmi_early_resume(void) +{ + struct hdmi *hdmi_drv = &hdmi_dev->driver; + + hdmi_dbg(hdmi_drv->dev, "hdmi exit early resume\n"); + + mutex_lock(&hdmi_drv->enable_mutex); + + hdmi_drv->suspend = 0; + rk3036_hdmi_initial(hdmi_drv); + if (hdmi_drv->enable && hdmi_drv->irq) { + enable_irq(hdmi_drv->irq); + rk3036_hdmi_irq(hdmi_drv); + } + + queue_delayed_work(hdmi_drv->workqueue, &hdmi_drv->delay_work, + msecs_to_jiffies(10)); + mutex_unlock(&hdmi_drv->enable_mutex); +} + +static int rk3036_hdmi_fb_event_notify(struct notifier_block *self, + unsigned long action, void *data) +{ + struct fb_event *event = data; + int blank_mode = *((int *)event->data); + + if (action == FB_EARLY_EVENT_BLANK) { + switch (blank_mode) { + case FB_BLANK_UNBLANK: + break; + default: + rk3036_hdmi_early_suspend(); + break; + } + } else if (action == FB_EVENT_BLANK) { + switch (blank_mode) { + case FB_BLANK_UNBLANK: + rk3036_hdmi_early_resume(); + break; + default: + break; + } + } + + return NOTIFY_OK; +} + +static struct notifier_block rk3036_hdmi_fb_notifier = { + .notifier_call = rk3036_hdmi_fb_event_notify, +}; + + +static void rk3036_delay_work_func(struct work_struct *work) +{ + struct hdmi *hdmi_drv = &hdmi_dev->driver; + + if (hdmi_drv->suspend == 0) { + if (hdmi_drv->enable == 1) + rk3036_hdmi_irq(hdmi_drv); + if (hdmi_drv->irq == 0) + queue_delayed_work(hdmi_drv->workqueue, &hdmi_dev->rk3036_delay_work, + msecs_to_jiffies(100)); + } +} + +static irqreturn_t rk3036_hdmi_irq_func(int irq, void *dev_id) +{ + struct hdmi *hdmi_drv = &hdmi_dev->driver; + if ((hdmi_drv->suspend == 0) && (hdmi_drv->enable == 1)) { + hdmi_dbg(hdmi_drv->dev, + "line = %d, rk3036_hdmi_irq_func irq triggered.\n", + __LINE__); + rk3036_hdmi_irq(hdmi_drv); + } + + return IRQ_HANDLED; +} + +static int rk3036_hdmi_drv_init(struct hdmi *hdmi_drv) +{ + int ret = 0; + int lcdc_id = 0; + struct rk_screen screen; + + rk_fb_get_prmry_screen(&screen); + + /* hdmi is extend as default,TODO modify if hdmi is primary */ + /*lcdc_id = (screen.lcdc_id == 0) ? 1 : 0;*/ + lcdc_id = screen.lcdc_id;//for box,hdmi is primary + + if (lcdc_id == 0) + hdmi_drv->lcdc = rk_get_lcdc_drv("lcdc0"); + else + hdmi_drv->lcdc = rk_get_lcdc_drv("lcdc1"); + + if (IS_ERR(hdmi_drv->lcdc)) { + dev_err(hdmi_drv->dev, + "can not connect to video source lcdc\n"); + ret = -ENXIO; + return ret; + } + + hdmi_drv->xscale = 100; + hdmi_drv->yscale = 100; + + /*spin_lock_init(&hdmi_drv->irq_lock);*/ + mutex_init(&hdmi_drv->enable_mutex); + hdmi_sys_init(hdmi_drv); + ret = rk3036_hdmi_initial(hdmi_drv); + + return ret; +} + +#if defined(CONFIG_OF) +static const struct of_device_id rk3036_hdmi_of_match[] = { + {.compatible = "rockchip,rk3036-hdmi",}, + {} +}; + +MODULE_DEVICE_TABLE(of, rk3036_hdmi_of_match); +#endif + +static int rk3036_hdmi_probe(struct platform_device *pdev) +{ + int ret; + struct hdmi *hdmi_drv; + struct resource *res; + + hdmi_dev = devm_kzalloc(&pdev->dev, sizeof(struct rk_hdmi_device), + GFP_KERNEL); + if (!hdmi_dev) { + dev_err(&pdev->dev, ">>rk3036_hdmi kmalloc fail!"); + return -ENOMEM; + } + + hdmi_drv = &hdmi_dev->driver; + hdmi_drv->dev = &pdev->dev; + platform_set_drvdata(pdev, hdmi_dev); + spin_lock_init(&hdmi_dev->reg_lock); + +#ifdef CONFIG_SWITCH + hdmi_drv->switch_hdmi.name = "hdmi"; + switch_dev_register(&(hdmi_drv->switch_hdmi)); +#endif + hdmi_register_display_sysfs(hdmi_drv, NULL); + fb_register_client(&rk3036_hdmi_fb_notifier); + + hdmi_drv->workqueue = create_singlethread_workqueue("hdmi"); + INIT_DELAYED_WORK(&(hdmi_drv->delay_work), hdmi_work); + INIT_DELAYED_WORK(&hdmi_dev->rk3036_delay_work, rk3036_delay_work_func); + + /* enable clk */ + hdmi_dev->hclk = devm_clk_get(hdmi_drv->dev, "pclk_hdmi"); + if (IS_ERR(hdmi_dev->hclk)) { + dev_err(hdmi_drv->dev, "Unable to get hdmi hclk\n"); + ret = -ENXIO; + goto err1; + } + rk3036_hdmi_clk_enable(hdmi_dev); /* enable clk may move to irq func */ + hdmi_dev->hclk_rate = clk_get_rate(hdmi_dev->hclk); + /* request and remap iomem */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(hdmi_drv->dev, "Unable to get register resource\n"); + ret = -ENXIO; + goto err2; + } + hdmi_dev->regbase_phy = res->start; + hdmi_dev->regsize_phy = resource_size(res); + hdmi_dev->regbase = devm_ioremap_resource(hdmi_drv->dev, res); + if (IS_ERR(hdmi_dev->regbase)) { + ret = PTR_ERR(hdmi_dev->regbase); + dev_err(hdmi_drv->dev, "cannot ioremap registers,err=%d\n", + ret); + goto err2; + } + if (rk3036_hdmi_drv_init(hdmi_drv)) + goto err0; + + /* get the IRQ */ + hdmi_drv->irq = platform_get_irq(pdev, 0); + if (hdmi_drv->irq <= 0) { + dev_err(hdmi_drv->dev, "failed to get hdmi irq resource (%d).\n", + hdmi_drv->irq); + hdmi_drv->irq = 0; + } else { + /* request the IRQ */ + ret = devm_request_irq(hdmi_drv->dev, hdmi_drv->irq, + rk3036_hdmi_irq_func, 0, + dev_name(hdmi_drv->dev), hdmi_drv); + if (ret) { + dev_err(hdmi_drv->dev, "hdmi request_irq failed (%d)\n", + ret); + goto err2; + } + } + +#if defined(CONFIG_DEBUG_FS) + hdmi_dev->debugfs_dir = debugfs_create_dir("rk3036", NULL); + if (IS_ERR(hdmi_dev->debugfs_dir)) { + dev_err(hdmi_drv->dev, + "failed to create debugfs dir for rk3036!\n"); + } else { + debugfs_create_file("hdmi", S_IRUSR, + hdmi_dev->debugfs_dir, hdmi_drv, + &rk3036_hdmi_reg_fops); + } + +#endif + + queue_delayed_work(hdmi_drv->workqueue, &hdmi_dev->rk3036_delay_work, + msecs_to_jiffies(0)); + dev_info(hdmi_drv->dev, "rk3036 hdmi probe success.\n"); + + return 0; + +err2: + rk3036_hdmi_clk_disable(hdmi_dev); +err1: + fb_unregister_client(&rk3036_hdmi_fb_notifier); + hdmi_unregister_display_sysfs(hdmi_drv); +#ifdef CONFIG_SWITCH + switch_dev_unregister(&(hdmi_drv->switch_hdmi)); +#endif + +err0: + hdmi_dbg(hdmi_drv->dev, "rk3036 hdmi probe error.\n"); + kfree(hdmi_dev); + hdmi_dev = NULL; + return ret; +} + +static int rk3036_hdmi_remove(struct platform_device *pdev) +{ + struct rk_hdmi_device *hdmi_dev = platform_get_drvdata(pdev); + struct hdmi *hdmi_drv = NULL; + + if (hdmi_dev) { + hdmi_drv = &hdmi_dev->driver; + mutex_lock(&hdmi_drv->enable_mutex); + if (!hdmi_drv->suspend && hdmi_drv->enable && hdmi_drv->irq) + disable_irq(hdmi_drv->irq); + mutex_unlock(&hdmi_drv->enable_mutex); + if (hdmi_drv->irq) + free_irq(hdmi_drv->irq, NULL); + + flush_workqueue(hdmi_drv->workqueue); + destroy_workqueue(hdmi_drv->workqueue); +#ifdef CONFIG_SWITCH + switch_dev_unregister(&(hdmi_drv->switch_hdmi)); +#endif + hdmi_unregister_display_sysfs(hdmi_drv); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&hdmi_drv->early_suspend); +#endif + fb_destroy_modelist(&hdmi_drv->edid.modelist); + if (hdmi_drv->edid.audio) + kfree(hdmi_drv->edid.audio); + if (hdmi_drv->edid.specs) { + if (hdmi_drv->edid.specs->modedb) + kfree(hdmi_drv->edid.specs->modedb); + kfree(hdmi_drv->edid.specs); + } + + hdmi_dbg(hdmi_drv->dev, "rk3036 hdmi removed.\n"); + kfree(hdmi_dev); + hdmi_dev = NULL; + } + + return 0; +} + +static void rk3036_hdmi_shutdown(struct platform_device *pdev) +{ + struct rk_hdmi_device *hdmi_dev = platform_get_drvdata(pdev); + struct hdmi *hdmi_drv = NULL; + + if (hdmi_dev) { + hdmi_drv = &hdmi_dev->driver; +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&hdmi_drv->early_suspend); +#endif + flush_delayed_work(&hdmi_drv->delay_work); + mutex_lock(&hdmi_drv->enable_mutex); + hdmi_drv->suspend = 1; + if (!hdmi_drv->enable) { + mutex_unlock(&hdmi_drv->enable_mutex); + return; + } + if (hdmi_drv->irq) + disable_irq(hdmi_drv->irq); + mutex_unlock(&hdmi_drv->enable_mutex); + } + hdmi_dbg(hdmi_drv->dev, "rk3036 hdmi shut down.\n"); +} + + +static struct platform_driver rk3036_hdmi_driver = { + .probe = rk3036_hdmi_probe, + .remove = rk3036_hdmi_remove, + .driver = { + .name = "rk3036-hdmi", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rk3036_hdmi_of_match), + }, + .shutdown = rk3036_hdmi_shutdown, +}; + +static int __init rk3036_hdmi_init(void) +{ + return platform_driver_register(&rk3036_hdmi_driver); +} + +static void __exit rk3036_hdmi_exit(void) +{ + platform_driver_unregister(&rk3036_hdmi_driver); +} + +late_initcall(rk3036_hdmi_init); +module_exit(rk3036_hdmi_exit); diff --git a/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi.h b/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi.h new file mode 100755 index 000000000000..afdec401392e --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi.h @@ -0,0 +1,38 @@ +#ifndef __RK3036_HDMI_H__ +#define __RK3036_HDMI_H__ + +#include "../../rk_hdmi.h" + +enum { + INPUT_IIS, + INPUT_SPDIF +}; + +#if defined(CONFIG_SND_RK_SOC_HDMI_SPDIF) +#define HDMI_CODEC_SOURCE_SELECT INPUT_SPDIF +#else +#define HDMI_CODEC_SOURCE_SELECT INPUT_IIS +#endif + +extern void rk3036_hdmi_control_output(struct hdmi *hdmi, int enable); +extern int rk3036_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)); + +struct rk_hdmi_device { + int clk_on; + spinlock_t reg_lock; + struct hdmi driver; + void __iomem *regbase; + int regbase_phy; + int regsize_phy; + struct clk *pd; + struct clk *hclk; /* HDMI AHP clk */ + struct delayed_work rk3036_delay_work; + struct work_struct rk3036_irq_work_struct; + struct dentry *debugfs_dir; + unsigned int hclk_rate; +}; + +#endif /* __RK3036_HDMI_H__ */ diff --git a/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi_hdcp.c b/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi_hdcp.c new file mode 100755 index 000000000000..1177f9439d32 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi_hdcp.c @@ -0,0 +1,143 @@ +#include +#include "rk3036_hdmi.h" +#include "rk3036_hdmi_hw.h" +#include "rk3036_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 rk3036_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 rk3036_hdcp_load_key2mem(struct hdcp_keys *key) +{ + int i; + DBG("HDCP: rk3036_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: rk3036_hdcp_load_key2mem end"); + return HDCP_OK; +} + +int rk3036_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; + } + rk3036_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 rk3036_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 rk3036_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/rk3036/rk3036_hdmi_hw.c b/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi_hw.c new file mode 100755 index 000000000000..5ea34365bd17 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi_hw.c @@ -0,0 +1,573 @@ +#include +#include +#include +#include +#include "rk3036_hdmi.h" +#include "rk3036_hdmi_hw.h" + +static int __maybe_unused rk3036_hdmi_show_reg(struct hdmi *hdmi_drv) +{ + int i = 0; + u32 val = 0; + struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv, + struct rk_hdmi_device, + driver); + + printk("\n>>>rk3036_ctl reg"); + for (i = 0; i < 16; i++) + printk(" %2x", i); + + printk("\n-----------------------------------------------------------------"); + + for (i = 0; i <= PHY_PRE_DIV_RATIO; i++) { + hdmi_readl(hdmi_dev, i, &val); + if (i % 16 == 0) + printk("\n>>>rk3036_ctl %2x:", i); + printk(" %02x", val); + } + printk("\n-----------------------------------------------------------------\n"); + + return 0; +} + +static inline void delay100us(void) +{ + msleep(1); +} + +static void rk3036_hdmi_av_mute(struct hdmi *hdmi_drv, bool enable) +{ + struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv, + struct rk_hdmi_device, + driver); + + hdmi_writel(hdmi_dev, AV_MUTE, + v_AUDIO_MUTE(enable) | v_VIDEO_MUTE(enable)); +} + +static void rk3036_hdmi_sys_power(struct hdmi *hdmi_drv, bool enable) +{ + struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv, + struct rk_hdmi_device, + driver); + + if (enable) + hdmi_msk_reg(hdmi_dev, SYS_CTRL, m_POWER, v_PWR_ON); + else + hdmi_msk_reg(hdmi_dev, SYS_CTRL, m_POWER, v_PWR_OFF); +} + +static void rk3036_hdmi_set_pwr_mode(struct hdmi *hdmi_drv, int mode) +{ + struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv, + struct rk_hdmi_device, + driver); + + if (hdmi_drv->pwr_mode == mode) + return; + + hdmi_dbg(hdmi_drv->dev, "%s change pwr_mode %d --> %d\n", __func__, + hdmi_drv->pwr_mode, mode); + + switch (mode) { + case NORMAL: + hdmi_dbg(hdmi_drv->dev, + "%s change pwr_mode NORMAL pwr_mode = %d, mode = %d\n", + __func__, hdmi_drv->pwr_mode, mode); + rk3036_hdmi_sys_power(hdmi_drv, false); + hdmi_writel(hdmi_dev, PHY_DRIVER, 0x99); + hdmi_writel(hdmi_dev, PHY_PRE_EMPHASIS, 0x0f); + hdmi_writel(hdmi_dev, PHY_SYS_CTL, 0x15); + hdmi_writel(hdmi_dev, PHY_SYS_CTL, 0x14); + hdmi_writel(hdmi_dev, PHY_SYS_CTL, 0x10); + hdmi_writel(hdmi_dev, PHY_CHG_PWR, 0x0f); + hdmi_writel(hdmi_dev, 0xce, 0x00); + hdmi_writel(hdmi_dev, 0xce, 0x01); + rk3036_hdmi_av_mute(hdmi_drv, 1); + rk3036_hdmi_sys_power(hdmi_drv, true); + break; + case LOWER_PWR: + hdmi_dbg(hdmi_drv->dev, + "%s change pwr_mode LOWER_PWR pwr_mode = %d, mode = %d\n", + __func__, hdmi_drv->pwr_mode, mode); + rk3036_hdmi_av_mute(hdmi_drv, 0); + rk3036_hdmi_sys_power(hdmi_drv, false); + hdmi_writel(hdmi_dev, PHY_DRIVER, 0x00); + hdmi_writel(hdmi_dev, PHY_PRE_EMPHASIS, 0x00); + hdmi_writel(hdmi_dev, PHY_CHG_PWR, 0x00); + hdmi_writel(hdmi_dev, PHY_SYS_CTL, 0x17); + break; + default: + hdmi_dbg(hdmi_drv->dev, "unkown rk3036 hdmi pwr mode %d\n", + mode); + } + + hdmi_drv->pwr_mode = mode; +} + +int rk3036_hdmi_detect_hotplug(struct hdmi *hdmi_drv) +{ + int value = 0; + struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv, + struct rk_hdmi_device, + driver); + + hdmi_dbg(hdmi_drv->dev, "[%s] value %02x\n", __func__, value); + hdmi_readl(hdmi_dev, HDMI_STATUS, &value); + value &= m_HOTPLUG; + if (value == m_HOTPLUG) + return HDMI_HPD_ACTIVED; + else if (value) + return HDMI_HPD_INSERT; + else + return HDMI_HPD_REMOVED; +} +int rk3036_hdmi_insert(struct hdmi *hdmi_drv) +{ + rk3036_hdmi_set_pwr_mode(hdmi_drv, NORMAL); + return 0; +} + + +int rk3036_hdmi_read_edid(struct hdmi *hdmi_drv, int block, u8 *buf) +{ + u32 c = 0; + u8 segment = 0; + u8 offset = 0; + int ret = -1; + int i, j; + int ddc_bus_freq; + int trytime; + int checksum = 0; + struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv, + struct rk_hdmi_device, + driver); + if (block % 2) + offset = HDMI_EDID_BLOCK_SIZE; + + if (block / 2) + segment = 1; + ddc_bus_freq = (hdmi_dev->hclk_rate >> 2) / HDMI_SCL_RATE; + hdmi_writel(hdmi_dev, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF); + hdmi_writel(hdmi_dev, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF); + + hdmi_dbg(hdmi_drv->dev, + "EDID DATA (Segment = %d Block = %d Offset = %d):\n", + (int)segment, (int)block, (int)offset); + disable_irq(hdmi_drv->irq); + + /* Enable edid interrupt */ + hdmi_writel(hdmi_dev, INTERRUPT_MASK1, m_INT_EDID_READY); + + for (trytime = 0; trytime < 10; trytime++) { + checksum = 0; + hdmi_writel(hdmi_dev, INTERRUPT_STATUS1, 0x04); + + /* Set edid fifo first addr */ + hdmi_writel(hdmi_dev, EDID_FIFO_OFFSET, 0x00); + + /* Set edid word address 0x00/0x80 */ + hdmi_writel(hdmi_dev, EDID_WORD_ADDR, offset); + + /* Set edid segment pointer */ + hdmi_writel(hdmi_dev, EDID_SEGMENT_POINTER, segment); + + for (i = 0; i < 10; i++) { + /* Wait edid interrupt */ + msleep(10); + c = 0x00; + hdmi_readl(hdmi_dev, INTERRUPT_STATUS1, &c); + + if (c & m_INT_EDID_READY) + break; + } + + if (c & m_INT_EDID_READY) { + for (j = 0; j < HDMI_EDID_BLOCK_SIZE; j++) { + c = 0; + hdmi_readl(hdmi_dev, 0x50, &c); + buf[j] = c; + checksum += c; +#ifdef HDMI_DEBUG + if (j % 16 == 0) + printk("\n>>>0x%02x: ",j); + + printk("0x%02x ", c); +#endif + } + + /* clear EDID interrupt reg */ + hdmi_writel(hdmi_dev, INTERRUPT_STATUS1, + m_INT_EDID_READY); + + if ((checksum & 0xff) == 0) { + ret = 0; + hdmi_dbg(hdmi_drv->dev, + "[%s] edid read sucess\n", __func__); + break; + } + } + } + /*close edid irq*/ + hdmi_writel(hdmi_dev, INTERRUPT_MASK1, 0); + enable_irq(hdmi_drv->irq); + + return ret; +} + +static void rk3036_hdmi_config_avi(struct hdmi *hdmi_drv, + unsigned char vic, unsigned char output_color) +{ + int i; + char info[SIZE_AVI_INFOFRAME]; + struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv, + struct rk_hdmi_device, + driver); + + memset(info, 0, SIZE_AVI_INFOFRAME); + hdmi_writel(hdmi_dev, 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++) + hdmi_writel(hdmi_dev, CONTROL_PACKET_ADDR + i, info[i]); +} + +static int rk3036_hdmi_config_video(struct hdmi *hdmi_drv, + struct hdmi_video_para *vpara) +{ + int value; + struct fb_videomode *mode; + struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv, + struct rk_hdmi_device, + driver); + + hdmi_dbg(hdmi_drv->dev, "[%s]\n", __func__); + + if (vpara == NULL) { + hdmi_err(hdmi_drv->dev, "[%s] input parameter error\n", + __func__); + return -1; + } + + /* Output RGB as default */ + vpara->output_color = VIDEO_OUTPUT_RGB444; + if (hdmi_drv->pwr_mode == LOWER_PWR) + rk3036_hdmi_set_pwr_mode(hdmi_drv, NORMAL); + + /* Disable video and audio output */ + hdmi_writel(hdmi_dev, AV_MUTE, v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1)); + + /* Input video mode is SDR RGB24bit, Data enable signal from external */ + hdmi_writel(hdmi_dev, VIDEO_CONTRL1, + v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444) | + v_DE_EXTERNAL); + hdmi_writel(hdmi_dev, VIDEO_CONTRL2, + v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) | + v_VIDEO_OUTPUT_FORMAT(vpara->output_color & 0xFF)); + + /* Set HDMI Mode */ + hdmi_writel(hdmi_dev, 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; + hdmi_writel(hdmi_dev, VIDEO_CONTRL3, value); + + /* Set ext video timing */ +#if 1 + hdmi_writel(hdmi_dev, VIDEO_TIMING_CTL, 0); + mode = (struct fb_videomode *)hdmi_vic_to_videomode(vpara->vic); + if (mode == NULL) { + hdmi_err(hdmi_drv->dev, "[%s] not found vic %d\n", __func__, + vpara->vic); + return -ENOENT; + } + hdmi_drv->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); + hdmi_writel(hdmi_dev, VIDEO_TIMING_CTL, value); + + value = mode->left_margin + mode->xres + mode->right_margin + + mode->hsync_len; + hdmi_writel(hdmi_dev, VIDEO_EXT_HTOTAL_L, value & 0xFF); + hdmi_writel(hdmi_dev, VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF); + + value = mode->left_margin + mode->right_margin + mode->hsync_len; + hdmi_writel(hdmi_dev, VIDEO_EXT_HBLANK_L, value & 0xFF); + hdmi_writel(hdmi_dev, VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF); + + value = mode->left_margin + mode->hsync_len; + hdmi_writel(hdmi_dev, VIDEO_EXT_HDELAY_L, value & 0xFF); + hdmi_writel(hdmi_dev, VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF); + + value = mode->hsync_len; + hdmi_writel(hdmi_dev, VIDEO_EXT_HDURATION_L, value & 0xFF); + hdmi_writel(hdmi_dev, VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF); + + value = mode->upper_margin + mode->yres + mode->lower_margin + + mode->vsync_len; + hdmi_writel(hdmi_dev, VIDEO_EXT_VTOTAL_L, value & 0xFF); + hdmi_writel(hdmi_dev, VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF); + + value = mode->upper_margin + mode->vsync_len + mode->lower_margin; + hdmi_writel(hdmi_dev, 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; + + hdmi_writel(hdmi_dev, VIDEO_EXT_VDELAY, value & 0xFF); + + value = mode->vsync_len; + hdmi_writel(hdmi_dev, VIDEO_EXT_VDURATION, value & 0xFF); +#endif + + if (vpara->output_mode == OUTPUT_HDMI) { + rk3036_hdmi_config_avi(hdmi_drv, vpara->vic, + vpara->output_color); + hdmi_dbg(hdmi_drv->dev, "[%s] sucess output HDMI.\n", __func__); + } else { + hdmi_dbg(hdmi_drv->dev, "[%s] sucess output DVI.\n", __func__); + } + + /* rk3028a */ + hdmi_writel(hdmi_dev, PHY_PRE_DIV_RATIO, 0x1e); + hdmi_writel(hdmi_dev, PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c); + hdmi_writel(hdmi_dev, PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01); + + return 0; +} + +static void rk3036_hdmi_config_aai(struct hdmi *hdmi_drv) +{ + int i; + char info[SIZE_AUDIO_INFOFRAME]; + struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv, + struct rk_hdmi_device, + driver); + + 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]; + + hdmi_writel(hdmi_dev, CONTROL_PACKET_BUF_INDEX, INFOFRAME_AAI); + for (i = 0; i < SIZE_AUDIO_INFOFRAME; i++) + hdmi_writel(hdmi_dev, CONTROL_PACKET_ADDR + i, info[i]); +} + +static int rk3036_hdmi_config_audio(struct hdmi *hdmi_drv, + struct hdmi_audio *audio) +{ + int rate, N, channel, mclk_fs; + struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv, + struct rk_hdmi_device, + driver); + + 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_drv->dev, "[%s] not support such sample rate %d\n", + __func__, audio->rate); + return -ENOENT; + } + + /* set_audio source I2S */ + if (HDMI_CODEC_SOURCE_SELECT == INPUT_IIS) { + hdmi_writel(hdmi_dev, AUDIO_CTRL1, 0x00); + hdmi_writel(hdmi_dev, AUDIO_SAMPLE_RATE, rate); + hdmi_writel(hdmi_dev, AUDIO_I2S_MODE, + v_I2S_MODE(I2S_STANDARD) | v_I2S_CHANNEL(channel)); + hdmi_writel(hdmi_dev, AUDIO_I2S_MAP, 0x00); + /* no swap */ + hdmi_writel(hdmi_dev, AUDIO_I2S_SWAPS_SPDIF, 0); + } else { + hdmi_writel(hdmi_dev, AUDIO_CTRL1, 0x08); + /* no swap */ + hdmi_writel(hdmi_dev, AUDIO_I2S_SWAPS_SPDIF, 0); + } + + /* Set N value */ + hdmi_writel(hdmi_dev, AUDIO_N_H, (N >> 16) & 0x0F); + hdmi_writel(hdmi_dev, AUDIO_N_M, (N >> 8) & 0xFF); + hdmi_writel(hdmi_dev, AUDIO_N_L, N & 0xFF); + rk3036_hdmi_config_aai(hdmi_drv); + + return 0; +} + +void rk3036_hdmi_control_output(struct hdmi *hdmi_drv, int enable) +{ + int mutestatus = 0; + struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv, + struct rk_hdmi_device, + driver); + + if (enable) { + if (hdmi_drv->pwr_mode == LOWER_PWR) + rk3036_hdmi_set_pwr_mode(hdmi_drv, NORMAL); + hdmi_readl(hdmi_dev, AV_MUTE, &mutestatus); + if (mutestatus && (m_AUDIO_MUTE | m_VIDEO_BLACK)) { + hdmi_writel(hdmi_dev, AV_MUTE, + v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0)); + } + rk3036_hdmi_sys_power(hdmi_drv, true); + rk3036_hdmi_sys_power(hdmi_drv, false); + rk3036_hdmi_sys_power(hdmi_drv, true); + hdmi_writel(hdmi_dev, 0xce, 0x00); + delay100us(); + hdmi_writel(hdmi_dev, 0xce, 0x01); + } else { + hdmi_writel(hdmi_dev, AV_MUTE, + v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1)); + } +} + +int rk3036_hdmi_removed(struct hdmi *hdmi_drv) +{ + dev_info(hdmi_drv->dev, "Removed.\n"); + if (hdmi_drv->hdcp_power_off_cb) + hdmi_drv->hdcp_power_off_cb(); + rk3036_hdmi_set_pwr_mode(hdmi_drv, LOWER_PWR); + + return HDMI_ERROR_SUCESS; +} + +void rk3036_hdmi_irq(struct hdmi *hdmi_drv) +{ + u32 interrupt = 0; + struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv, + struct rk_hdmi_device, + driver); + hdmi_readl(hdmi_dev, HDMI_STATUS, &interrupt); + if(interrupt) { + hdmi_writel(hdmi_dev, HDMI_STATUS, interrupt); + } + if (interrupt & m_INT_HOTPLUG) { + if (hdmi_drv->state == HDMI_SLEEP) + hdmi_drv->state = WAIT_HOTPLUG; + + queue_delayed_work(hdmi_drv->workqueue, &hdmi_drv->delay_work, + msecs_to_jiffies(40)); + + }/*plug out*/ + + if (hdmi_drv->hdcp_irq_cb) + hdmi_drv->hdcp_irq_cb(0); +} + +static void rk3036_hdmi_reset(struct hdmi *hdmi_drv) +{ + u32 val = 0; + u32 msk = 0; + struct rk_hdmi_device *hdmi_dev = container_of(hdmi_drv, + struct rk_hdmi_device, + driver); + + hdmi_msk_reg(hdmi_dev, SYS_CTRL, m_RST_DIGITAL, v_NOT_RST_DIGITAL); + delay100us(); + hdmi_msk_reg(hdmi_dev, SYS_CTRL, m_RST_ANALOG, v_NOT_RST_ANALOG); + delay100us(); + msk = m_REG_CLK_INV | m_REG_CLK_SOURCE | m_POWER | m_INT_POL; + val = v_REG_CLK_INV | v_REG_CLK_SOURCE_SYS | v_PWR_ON | v_INT_POL_HIGH; + hdmi_msk_reg(hdmi_dev, SYS_CTRL, msk, val); + + rk3036_hdmi_set_pwr_mode(hdmi_drv, LOWER_PWR); +} + +int rk3036_hdmi_initial(struct hdmi *hdmi_drv) +{ + int rc = HDMI_ERROR_SUCESS; + + hdmi_drv->pwr_mode = NORMAL; + hdmi_drv->remove = rk3036_hdmi_removed; + hdmi_drv->control_output = rk3036_hdmi_control_output; + hdmi_drv->config_video = rk3036_hdmi_config_video; + hdmi_drv->config_audio = rk3036_hdmi_config_audio; + hdmi_drv->detect_hotplug = rk3036_hdmi_detect_hotplug; + hdmi_drv->read_edid = rk3036_hdmi_read_edid; + hdmi_drv->insert = rk3036_hdmi_insert; + + rk3036_hdmi_reset_pclk(); + rk3036_hdmi_reset(hdmi_drv); + + if (hdmi_drv->hdcp_power_on_cb) + rc = hdmi_drv->hdcp_power_on_cb(); + + return rc; +} diff --git a/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi_hw.h b/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi_hw.h new file mode 100755 index 000000000000..70f384118f41 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk3036/rk3036_hdmi_hw.h @@ -0,0 +1,323 @@ +#ifndef _RK3036_HDMI_HW_H +#define _RK3036_HDMI_HW_H + +#include +enum PWR_MODE { + NORMAL, + LOWER_PWR, +}; +enum { + OUTPUT_DVI = 0, + OUTPUT_HDMI +}; + +#ifdef RK616_USE_MCLK_12M +#define HDMI_SYS_FREG_CLK 12000000 +#else +#define HDMI_SYS_FREG_CLK 11289600 +#endif + +#define HDMI_SCL_RATE (100*1000) +#define DDC_BUS_FREQ_L 0x4b +#define DDC_BUS_FREQ_H 0x4c + +#define SYS_CTRL 0x00 +#define m_RST_ANALOG (1 << 6) +#define v_RST_ANALOG (0 << 6) +#define v_NOT_RST_ANALOG (1 << 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 (1 << 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_ACTIVE_VSYNC (1 << 5) +#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_MASK_INT_HOTPLUG (1 << 5) + #define m_INT_HOTPLUG (1 << 1) + + +#define HDMI_COLORBAR 0xc9 + +#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 (1 << 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 & 0x1f) + +static inline int hdmi_readl(struct rk_hdmi_device *hdmi_dev, u16 offset, + u32 *val) +{ + int ret = 0; + + *val = readl_relaxed(hdmi_dev->regbase + (offset) * 0x04); + return ret; +} + +static inline int hdmi_writel(struct rk_hdmi_device *hdmi_dev, u16 offset, + u32 val) +{ + int ret = 0; + + writel_relaxed(val, hdmi_dev->regbase + (offset) * 0x04); + return ret; +} + +static inline int hdmi_msk_reg(struct rk_hdmi_device *hdmi_dev, u16 offset, + u32 msk, u32 val) +{ + int ret = 0; + u32 temp; + + temp = readl_relaxed(hdmi_dev->regbase + (offset) * 0x04) & (0xFF - (msk)); + writel_relaxed(temp | ((val) & (msk)), hdmi_dev->regbase + (offset) * 0x04); + return ret; +} +static inline void rk3036_hdmi_reset_pclk(void) +{ + writel_relaxed(0x00010001, RK_CRU_VIRT+ 0x128); + msleep(100); + writel_relaxed(0x00010000, RK_CRU_VIRT + 0x128); +} + +extern int rk3036_hdmi_initial(struct hdmi *hdmi); +extern void rk3036_hdmi_irq(struct hdmi *hdmi); + +#endif diff --git a/drivers/video/rockchip/hdmi/rk_hdmi_lcdc.c b/drivers/video/rockchip/hdmi/rk_hdmi_lcdc.c index cb61763ac68c..fe65b3760f76 100755 --- a/drivers/video/rockchip/hdmi/rk_hdmi_lcdc.c +++ b/drivers/video/rockchip/hdmi/rk_hdmi_lcdc.c @@ -124,7 +124,7 @@ int hdmi_set_info(struct rk_screen *screen, unsigned int vic) screen->hdmi_resolution = hdmi_mode[i].flag; /* Pin polarity */ -#if defined(CONFIG_HDMI_RK616) && !defined(CONFIG_ARCH_RK3026) && !defined(SOC_CONFIG_RK3036) +#if defined(CONFIG_HDMI_RK616) && !defined(CONFIG_ARCH_RK3026) screen->pin_hsync = 0; screen->pin_vsync = 0; #else -- 2.34.1