From: yzq Date: Fri, 20 Jul 2012 09:04:55 +0000 (+0800) Subject: rk2928 hdmi support X-Git-Tag: firefly_0821_release~8912^2~77 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=3a35b5af72dcd168af3a63a992846ad2461dbae7;p=firefly-linux-kernel-4.4.55.git rk2928 hdmi support --- diff --git a/arch/arm/mach-rk2928/devices.c b/arch/arm/mach-rk2928/devices.c index d90a8055eec8..21e24335a532 100755 --- a/arch/arm/mach-rk2928/devices.c +++ b/arch/arm/mach-rk2928/devices.c @@ -522,6 +522,28 @@ static void __init rk2928_init_spim(void) platform_device_register(&rk29xx_device_spi0m); #endif } + +#ifdef CONFIG_HDMI_RK2928 +static struct resource resource_hdmi[] = { + [0] = { + .start = RK2928_HDMI_PHYS, + .end = RK2928_HDMI_PHYS + RK2928_HDMI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_HDMI, + .end = IRQ_HDMI, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device device_hdmi = { + .name = "rk2928-hdmi", + .id = -1, + .num_resources = ARRAY_SIZE(resource_hdmi), + .resource = resource_hdmi, +}; +#endif #ifdef CONFIG_RGA_RK30 static struct resource resource_rga[] = { [0] = { diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index b4117f636264..47d5735793ba 100755 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2438,7 +2438,11 @@ source "drivers/video/omap2/Kconfig" source "drivers/video/backlight/Kconfig" source "drivers/video/display/Kconfig" + +if !LCDC_RK30 && !LCDC_RK2928 source "drivers/video/hdmi/Kconfig" +endif + source "drivers/video/rockchip/Kconfig" if VT diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile index ed778bb77b99..68967a49fc80 100755 --- a/drivers/video/rockchip/Makefile +++ b/drivers/video/rockchip/Makefile @@ -4,4 +4,4 @@ obj-$(CONFIG_LCDC_RK30) += chips/rk30_lcdc.o obj-$(CONFIG_LCDC_RK2928) += chips/rk2928_lcdc.o obj-$(CONFIG_LCDC_RK31) += chips/rk31_lcdc.o obj-$(CONFIG_RGA_RK30) += rga/ -obj-$(CONFIG_HDMI_RK30) += hdmi/ +obj-$(CONFIG_RK_HDMI) += hdmi/ diff --git a/drivers/video/rockchip/hdmi/Kconfig b/drivers/video/rockchip/hdmi/Kconfig index 654bfa0e67db..e3700bbd817d 100755 --- a/drivers/video/rockchip/hdmi/Kconfig +++ b/drivers/video/rockchip/hdmi/Kconfig @@ -1,23 +1,9 @@ -menuconfig HDMI_RK30 - bool "RK30 HDMI support" - depends on LCDC_RK30 - select FB_MODE_HELPERS - help - Support rk30 hdmi if you say y here +menu "RK_HDMI" +config RK_HDMI + bool "RK_HDMI support" + +if RK_HDMI +source "drivers/video/rockchip/hdmi/chips/Kconfig" +endif -config HDMI_RK30_CTL_CODEC - bool "Mute Codec When HDMI Actived" - depends on HDMI_RK30 - default n - help - If you say y heres, Codec will be mute when hdmi inserted, - and unmute when removed. - -config HDMI_RK30_DEBUG - bool "RK30 HDMI Debugging" - depends on HDMI_RK30 && LCDC_RK30 - default n - help - Enableds verbose debugging the the HDMI drivers - -source "drivers/video/rockchip/hdmi/hdcp/Kconfig" \ No newline at end of file +endmenu diff --git a/drivers/video/rockchip/hdmi/Makefile b/drivers/video/rockchip/hdmi/Makefile index 4692311dbd9a..ed00a162460d 100755 --- a/drivers/video/rockchip/hdmi/Makefile +++ b/drivers/video/rockchip/hdmi/Makefile @@ -2,7 +2,7 @@ # Makefile for HDMI linux kernel module. # -ccflags-$(CONFIG_HDMI_RK30_DEBUG) = -DDEBUG -DHDMI_DEBUG +ccflags-$(CONFIG_RK_HDMI_DEBUG) = -DDEBUG -DHDMI_DEBUG -obj-$(CONFIG_HDMI_RK30) += rk30_hdmi_hw.o rk30_hdmi_edid.o rk30_hdmi_lcdc.o rk30_hdmi_task.o rk30_hdmi_sysfs.o rk30_hdmi.o -obj-$(CONFIG_HDCP_RK30) += hdcp/ +obj-$(CONFIG_RK_HDMI) += rk_hdmi_edid.o rk_hdmi_lcdc.o rk_hdmi_task.o rk_hdmi_sysfs.o +obj-$(CONFIG_RK_HDMI) += chips/ diff --git a/drivers/video/rockchip/hdmi/chips/Kconfig b/drivers/video/rockchip/hdmi/chips/Kconfig new file mode 100755 index 000000000000..e0c1c271bdc1 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/Kconfig @@ -0,0 +1,23 @@ +choice + prompt "HDMI chips select" +config HDMI_RK30 + bool "RK30 HDMI support" + depends on LCDC_RK30 + select FB_MODE_HELPERS + help + Support rk30 hdmi if you say y here +config HDMI_RK2928 + bool "RK2928 HDMI support" + depends on LCDC_RK2928 + select FB_MODE_HELPERS + 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 diff --git a/drivers/video/rockchip/hdmi/chips/Makefile b/drivers/video/rockchip/hdmi/chips/Makefile new file mode 100755 index 000000000000..a43ac0633d6a --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for HDMI linux kernel module. +# + +ccflags-$(CONFIG_HDMI_RK30_DEBUG) = -DDEBUG -DHDMI_DEBUG + +obj-$(CONFIG_HDMI_RK30) += rk30/ +obj-$(CONFIG_HDMI_RK2928) += rk2928/ diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/Kconfig b/drivers/video/rockchip/hdmi/chips/rk2928/Kconfig new file mode 100755 index 000000000000..8b137891791f --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/Kconfig @@ -0,0 +1 @@ + diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/Makefile b/drivers/video/rockchip/hdmi/chips/rk2928/Makefile new file mode 100755 index 000000000000..d3227c7f27d9 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for HDMI linux kernel module. +# + +ccflags-$(CONFIG_HDMI_RK30_DEBUG) = -DDEBUG -DHDMI_DEBUG + +obj-y += rk2928_hdmi_hw.o rk2928_hdmi.o +obj-$(CONFIG_HDCP_RK2928) += hdcp/ diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/Kconfig b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/Kconfig new file mode 100755 index 000000000000..1bb3c3a24f56 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/Kconfig @@ -0,0 +1,14 @@ +config HDCP_RK30 + bool "RK30 HDCP support" + depends on LCDC_RK30 && HDMI_RK30 + default n + help + HDCP Interface. This adds the High Definition Content Protection Interface. + See http://www.digital-cp.com/ for HDCP specification. + +config HDCP_RK30_DEBUG + bool "RK30 HDCP Debugging" + depends on HDCP_RK30 + default n + help + Enableds verbose debugging the the HDCP drivers diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/Makefile b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/Makefile new file mode 100755 index 000000000000..108b67ca134e --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for HDCP linux kernel module. +# + +ccflags-$(CONFIG_HDCP_RK30_DEBUG) = -DDEBUG -DHDCP_DEBUG + +obj-$(CONFIG_HDCP_RK30) += hdcp.o +hdcp-y := rk30_hdcp.o rk30_hdmi_hdcp.o diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdcp.c b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdcp.c new file mode 100755 index 000000000000..68d0ac841a67 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdcp.c @@ -0,0 +1,570 @@ +#include +#include +#include +#include +#include +#include +#include +#include "../rk30_hdmi.h" +#include "../rk30_hdmi_hw.h" +#include "rk30_hdmi_hdcp.h" + +struct hdcp *hdcp = NULL; + +static void hdcp_work_queue(struct work_struct *work); + +/*----------------------------------------------------------------------------- + * Function: hdcp_submit_work + *----------------------------------------------------------------------------- + */ +static struct delayed_work *hdcp_submit_work(int event, int delay) +{ + struct hdcp_delayed_work *work; + + DBG("%s event %04x delay %d", __FUNCTION__, event, delay); + + work = kmalloc(sizeof(struct hdcp_delayed_work), GFP_ATOMIC); + + if (work) { + INIT_DELAYED_WORK(&work->work, hdcp_work_queue); + work->event = event; + queue_delayed_work(hdcp->workqueue, + &work->work, + msecs_to_jiffies(delay)); + } else { + printk(KERN_WARNING "HDCP: Cannot allocate memory to " + "create work\n"); + return 0; + } + + return &work->work; +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_cancel_work + *----------------------------------------------------------------------------- + */ +static void hdcp_cancel_work(struct delayed_work **work) +{ + int ret = 0; + + if (*work) { + ret = cancel_delayed_work(*work); + if (ret != 1) { + ret = cancel_work_sync(&((*work)->work)); + printk(KERN_INFO "Canceling work failed - " + "cancel_work_sync done %d\n", ret); + } + kfree(*work); + *work = 0; + } +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_authentication_failure + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_authentication_failure(void) +{ + if (hdcp->hdmi_state == HDMI_STOPPED) { + return; + } + + rk30_hdcp_disable(); + rk30_hdmi_control_output(false); + + hdcp_cancel_work(&hdcp->pending_wq_event); + + if (hdcp->retry_cnt && (hdcp->hdmi_state != HDMI_STOPPED)) { + if (hdcp->retry_cnt < HDCP_INFINITE_REAUTH) { + hdcp->retry_cnt--; + printk(KERN_INFO "HDCP: authentication failed - " + "retrying, attempts=%d\n", + hdcp->retry_cnt); + } else + printk(KERN_INFO "HDCP: authentication failed - " + "retrying\n"); + + hdcp->hdcp_state = HDCP_AUTHENTICATION_START; + + hdcp->pending_wq_event = hdcp_submit_work(HDCP_AUTH_REATT_EVENT, + HDCP_REAUTH_DELAY); + } else { + printk(KERN_INFO "HDCP: authentication failed - " + "HDCP disabled\n"); + hdcp->hdcp_state = HDCP_ENABLE_PENDING; + } + +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_start_authentication + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_start_authentication(void) +{ + int status = HDCP_OK; + + hdcp->hdcp_state = HDCP_AUTHENTICATION_START; + + DBG("HDCP: authentication start"); + + status = rk30_hdcp_start_authentication(); + + if (status != HDCP_OK) { + DBG("HDCP: authentication failed"); + hdcp_wq_authentication_failure(); + } else { + hdcp->hdcp_state = HDCP_WAIT_KSV_LIST; + } +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_check_bksv + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_check_bksv(void) +{ + int status = HDCP_OK; + + DBG("Check BKSV start"); + + status = rk30_hdcp_check_bksv(); + + if (status != HDCP_OK) { + printk(KERN_INFO "HDCP: Check BKSV failed"); + hdcp->retry_cnt = 0; + hdcp_wq_authentication_failure(); + } + else { + DBG("HDCP: Check BKSV successful"); + + hdcp->hdcp_state = HDCP_LINK_INTEGRITY_CHECK; + + /* Restore retry counter */ + if(hdcp->retry_times == 0) + hdcp->retry_cnt = HDCP_INFINITE_REAUTH; + else + hdcp->retry_cnt = hdcp->retry_times; + } +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_authentication_sucess + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_authentication_sucess(void) +{ + printk(KERN_INFO "HDCP: authentication pass"); + rk30_hdmi_control_output(true); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_disable + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_disable(int event) +{ + printk(KERN_INFO "HDCP: disabled"); + + hdcp_cancel_work(&hdcp->pending_wq_event); + rk30_hdcp_disable(); + if(event == HDCP_DISABLE_CTL) { + hdcp->hdcp_state = HDCP_DISABLED; + if(hdcp->hdmi_state == HDMI_STARTED) + rk30_hdmi_control_output(true); + } + else if(event == HDCP_STOP_FRAME_EVENT) + hdcp->hdcp_state = HDCP_ENABLE_PENDING; +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_work_queue + *----------------------------------------------------------------------------- + */ +static void hdcp_work_queue(struct work_struct *work) +{ + struct hdcp_delayed_work *hdcp_w = + container_of(work, struct hdcp_delayed_work, work.work); + int event = hdcp_w->event; + + mutex_lock(&hdcp->lock); + + DBG("hdcp_work_queue() - START - %u hdmi=%d hdcp=%d evt= %x %d", + jiffies_to_msecs(jiffies), + hdcp->hdmi_state, + hdcp->hdcp_state, + (event & 0xFF00) >> 8, + event & 0xFF); + + if(event == HDCP_STOP_FRAME_EVENT) { + hdcp->hdmi_state = HDMI_STOPPED; + } + + if (event == HDCP_DISABLE_CTL || event == HDCP_STOP_FRAME_EVENT) { + hdcp_wq_disable(event); + } + + if (event & HDCP_WORKQUEUE_SRC) + hdcp->pending_wq_event = 0; + + /* First handle HDMI state */ + if (event == HDCP_START_FRAME_EVENT) { + hdcp->pending_start = 0; + hdcp->hdmi_state = HDMI_STARTED; + } + + /**********************/ + /* HDCP state machine */ + /**********************/ + switch (hdcp->hdcp_state) { + case HDCP_DISABLED: + /* HDCP enable control or re-authentication event */ + if (event == HDCP_ENABLE_CTL) { + if(hdcp->retry_times == 0) + hdcp->retry_cnt = HDCP_INFINITE_REAUTH; + else + hdcp->retry_cnt = hdcp->retry_times; + if (hdcp->hdmi_state == HDMI_STARTED) + hdcp_wq_start_authentication(); + else + hdcp->hdcp_state = HDCP_ENABLE_PENDING; + } + break; + + case HDCP_ENABLE_PENDING: + /* HDMI start frame event */ + if (event == HDCP_START_FRAME_EVENT) + hdcp_wq_start_authentication(); + + break; + + case HDCP_AUTHENTICATION_START: + /* Re-authentication */ + if (event == HDCP_AUTH_REATT_EVENT) + hdcp_wq_start_authentication(); + + break; + + case HDCP_WAIT_KSV_LIST: + /* KSV failure */ + if (event == HDCP_FAIL_EVENT) { + printk(KERN_INFO "HDCP: KSV switch failure\n"); + + hdcp_wq_authentication_failure(); + } + /* KSV list ready event */ + else if (event == HDCP_KSV_LIST_RDY_EVENT) + hdcp_wq_check_bksv(); + break; + + case HDCP_LINK_INTEGRITY_CHECK: + /* Ri failure */ + if (event == HDCP_FAIL_EVENT) { + printk(KERN_INFO "HDCP: Ri check failure\n"); + hdcp_wq_authentication_failure(); + } + else if(event == HDCP_AUTH_PASS_EVENT) + hdcp_wq_authentication_sucess(); + break; + + default: + printk(KERN_WARNING "HDCP: error - unknow HDCP state\n"); + break; + } + + kfree(hdcp_w); + if(event == HDCP_STOP_FRAME_EVENT) + complete(&hdcp->complete); + + mutex_unlock(&hdcp->lock); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_start_frame_cb + *----------------------------------------------------------------------------- + */ +static void hdcp_start_frame_cb(void) +{ + DBG("hdcp_start_frame_cb()"); + + /* Cancel any pending work */ + if (hdcp->pending_start) + hdcp_cancel_work(&hdcp->pending_start); + if (hdcp->pending_wq_event) + hdcp_cancel_work(&hdcp->pending_wq_event); + + hdcp->pending_start = hdcp_submit_work(HDCP_START_FRAME_EVENT, + HDCP_ENABLE_DELAY); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_irq_cb + *----------------------------------------------------------------------------- + */ +static void hdcp_irq_cb(int interrupt) +{ + int value; + DBG("%s 0x%x", __FUNCTION__, interrupt); + if(interrupt & m_INT_HDCP_ERR) + { + value = HDMIRdReg(HDCP_ERROR); + HDMIWrReg(HDCP_ERROR, value); + printk(KERN_INFO "HDCP: Error 0x%02x\n", value); + + if( (hdcp->hdcp_state != HDCP_DISABLED) && + (hdcp->hdcp_state != HDCP_ENABLE_PENDING) ) + { + hdcp_submit_work(HDCP_FAIL_EVENT, 0); + } + } + else if(interrupt & (m_INT_BKSV_RPRDY | m_INT_BKSV_RCRDY)) + hdcp_submit_work(HDCP_KSV_LIST_RDY_EVENT, 0); + else if(interrupt & m_INT_AUTH_DONE) + hdcp_submit_work(HDCP_AUTH_PASS_EVENT, 0); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_power_on_cb + *----------------------------------------------------------------------------- + */ +static int hdcp_power_on_cb(void) +{ + DBG("%s", __FUNCTION__); + return rk30_hdcp_load_key2mem(hdcp->keys); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_power_off_cb + *----------------------------------------------------------------------------- + */ +static void hdcp_power_off_cb(void) +{ + DBG("%s", __FUNCTION__); + if(!hdcp->enable) + return; + + hdcp_cancel_work(&hdcp->pending_start); + hdcp_cancel_work(&hdcp->pending_wq_event); + init_completion(&hdcp->complete); + /* Post event to workqueue */ + if (hdcp_submit_work(HDCP_STOP_FRAME_EVENT, 0)) + wait_for_completion_interruptible_timeout(&hdcp->complete, + msecs_to_jiffies(2000)); +} + +// Load HDCP key to external HDCP memory +static void hdcp_load_keys_cb(const struct firmware *fw, void *context) +{ + if (!fw) { + pr_err("HDCP: failed to load keys\n"); + return; + } + + if(fw->size < HDCP_KEY_SIZE) { + pr_err("HDCP: firmware wrong size %d\n", fw->size); + return; + } + + hdcp->keys = kmalloc(HDCP_KEY_SIZE, GFP_KERNEL); + if(hdcp->keys == NULL) { + pr_err("HDCP: can't allocated space for keys\n"); + return; + } + + memcpy(hdcp->keys, fw->data, HDCP_KEY_SIZE); + + rk30_hdcp_load_key2mem(hdcp->keys); + printk(KERN_INFO "HDCP: loaded hdcp key success\n"); + + if(fw->size > HDCP_KEY_SIZE) { + DBG("%s invalid key size %d", __FUNCTION__, fw->size - HDCP_KEY_SIZE); + if((fw->size - HDCP_KEY_SIZE) % 5) { + pr_err("HDCP: failed to load invalid keys\n"); + return; + } + hdcp->invalidkeys = kmalloc(fw->size - HDCP_KEY_SIZE, GFP_KERNEL); + if(hdcp->invalidkeys == NULL) { + pr_err("HDCP: can't allocated space for invalid keys\n"); + return; + } + memcpy(hdcp->invalidkeys, fw->data + HDCP_KEY_SIZE, fw->size - HDCP_KEY_SIZE); + hdcp->invalidkey = (fw->size - HDCP_KEY_SIZE)/5; + printk(KERN_INFO "HDCP: loaded hdcp invalid key success\n"); + } +} + +static ssize_t hdcp_enable_read(struct device *device, + struct device_attribute *attr, char *buf) +{ + int enable = 0; + + if(hdcp) + enable = hdcp->enable; + + return snprintf(buf, PAGE_SIZE, "%d\n", enable); +} + +static ssize_t hdcp_enable_write(struct device *device, + struct device_attribute *attr, const char *buf, size_t count) +{ + int enable; + + if(hdcp == NULL) + return -EINVAL; + + sscanf(buf, "%d", &enable); + if(hdcp->enable != enable) + { + /* Post event to workqueue */ + if(enable) { + if (hdcp_submit_work(HDCP_ENABLE_CTL, 0) == 0) + return -EFAULT; + } + else { + hdcp_cancel_work(&hdcp->pending_start); + hdcp_cancel_work(&hdcp->pending_wq_event); + + /* Post event to workqueue */ + if (hdcp_submit_work(HDCP_DISABLE_CTL, 0) == 0) + return -EFAULT; + } + hdcp->enable = enable; + } + return count; +} + +static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR, hdcp_enable_read, hdcp_enable_write); + +static ssize_t hdcp_trytimes_read(struct device *device, + struct device_attribute *attr, char *buf) +{ + int trytimes = 0; + + if(hdcp) + trytimes = hdcp->retry_times; + + return snprintf(buf, PAGE_SIZE, "%d\n", trytimes); +} + +static ssize_t hdcp_trytimes_wrtie(struct device *device, + struct device_attribute *attr, const char *buf, size_t count) +{ + int trytimes; + + if(hdcp == NULL) + return -EINVAL; + + sscanf(buf, "%d", &trytimes); + if(hdcp->retry_times != trytimes) + hdcp->retry_times = trytimes; + + return count; +} + + +static DEVICE_ATTR(trytimes, S_IRUGO|S_IWUSR, hdcp_trytimes_read, hdcp_trytimes_wrtie); + + +static struct miscdevice mdev; + +static int __init rk30_hdcp_init(void) +{ + int ret; + + DBG("[%s] %u", __FUNCTION__, jiffies_to_msecs(jiffies)); + + hdcp = kmalloc(sizeof(struct hdcp), GFP_KERNEL); + if(!hdcp) + { + printk(KERN_ERR ">>HDCP: kmalloc fail!"); + ret = -ENOMEM; + goto error0; + } + memset(hdcp, 0, sizeof(struct hdcp)); + mutex_init(&hdcp->lock); + + mdev.minor = MISC_DYNAMIC_MINOR; + mdev.name = "hdcp"; + mdev.mode = 0666; + if (misc_register(&mdev)) { + printk(KERN_ERR "HDCP: Could not add character driver\n"); + ret = HDMI_ERROR_FALSE; + goto error1; + } + ret = device_create_file(mdev.this_device, &dev_attr_enable); + if(ret) + { + printk(KERN_ERR "HDCP: Could not add sys file enable\n"); + ret = -EINVAL; + goto error2; + } + + ret = device_create_file(mdev.this_device, &dev_attr_trytimes); + if(ret) + { + printk(KERN_ERR "HDCP: Could not add sys file trytimes\n"); + ret = -EINVAL; + goto error3; + } + + hdcp->workqueue = create_singlethread_workqueue("hdcp"); + if (hdcp->workqueue == NULL) { + printk(KERN_ERR "HDCP,: create workqueue failed.\n"); + goto error4; + } + + + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, + "hdcp.keys", mdev.this_device, GFP_KERNEL, + hdcp, hdcp_load_keys_cb); + if (ret < 0) { + printk(KERN_ERR "HDCP: request_firmware_nowait failed: %d\n", ret); + goto error5; + } + + rk30_hdmi_register_hdcp_callbacks( hdcp_start_frame_cb, + hdcp_irq_cb, + hdcp_power_on_cb, + hdcp_power_off_cb); + + DBG("%s success %u", __FUNCTION__, jiffies_to_msecs(jiffies)); + return 0; + +error5: + destroy_workqueue(hdcp->workqueue); +error4: + device_remove_file(mdev.this_device, &dev_attr_trytimes); +error3: + device_remove_file(mdev.this_device, &dev_attr_enable); +error2: + misc_deregister(&mdev); +error1: + if(hdcp->keys) + kfree(hdcp->keys); + if(hdcp->invalidkeys) + kfree(hdcp->invalidkeys); + kfree(hdcp); +error0: + return ret; +} + +static void __exit rk30_hdcp_exit(void) +{ + if(hdcp) { + mutex_lock(&hdcp->lock); + rk30_hdmi_register_hdcp_callbacks(0, 0, 0, 0); + device_remove_file(mdev.this_device, &dev_attr_enable); + misc_deregister(&mdev); + destroy_workqueue(hdcp->workqueue); + if(hdcp->keys) + kfree(hdcp->keys); + if(hdcp->invalidkeys) + kfree(hdcp->invalidkeys); + mutex_unlock(&hdcp->lock); + kfree(hdcp); + } +} + +module_init(rk30_hdcp_init); +module_exit(rk30_hdcp_exit); \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.c b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.c new file mode 100755 index 000000000000..1184989a57b2 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.c @@ -0,0 +1,157 @@ +#include +#include +#include +#include "../rk30_hdmi.h" +#include "../rk30_hdmi_hw.h" +#include "rk30_hdmi_hdcp.h" + +static void rk30_hdcp_write_mem(int addr_8, char value) +{ + int temp; + int addr_32 = addr_8 - addr_8%4; + int shift = (addr_8%4) * 8; + + temp = HDMIRdReg(addr_32); + temp &= ~(0xff << shift); + temp |= value << shift; +// printk("temp is %08x\n", temp); + HDMIWrReg(addr_32, temp); +} + +int rk30_hdcp_load_key2mem(struct hdcp_keys *key) +{ + int i; + + if(key == NULL) return HDMI_ERROR_FALSE; + + HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV); + + for(i = 0; i < 7; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_KSV1 + i, key->KSV[i]); + for(i = 0; i < 7; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_KSV2 + i, key->KSV[i]); + for(i = 0; i < HDCP_PRIVATE_KEY_SIZE; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_PRIVATE + i, key->DeviceKey[i]); + for(i = 0; i < HDCP_KEY_SHA_SIZE; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_PRIVATE + HDCP_PRIVATE_KEY_SIZE + i, key->sha1[i]); + + HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV | 0x20); + return HDCP_OK; +} + +void rk30_hdcp_disable(void) +{ + int temp; + // Diable HDCP Interrupt + HDMIWrReg(INTR_MASK2, 0x00); + // Stop and Reset HDCP + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_FRAMED_ENCRYPED | m_HDCP_AUTH_STOP | m_HDCP_RESET, + v_HDCP_FRAMED_ENCRYPED(0) | v_HDCP_AUTH_STOP(1) | v_HDCP_RESET(1) ); +} + +static int rk30_hdcp_load_key(void) +{ + int value, temp = 0; + + if(hdcp->keys == NULL) { + pr_err("[%s] HDCP key not loaded.\n", __FUNCTION__); + return HDCP_KEY_ERR; + } + + value = HDMIRdReg(HDCP_KEY_MEM_CTRL); + //Check HDCP key loaded from external HDCP memory + while((value & (m_KSV_VALID | m_KEY_VALID | m_KEY_READY)) != (m_KSV_VALID | m_KEY_VALID | m_KEY_READY)) { + if(temp > 10) { + pr_err("[%s] loaded hdcp key is incorrectable %02x\n", __FUNCTION__, value & 0xFF); + return HDCP_KEY_ERR; + } + //Load HDCP Key from external HDCP memory + HDMIWrReg(HDCP_KEY_ACCESS_CTRL2, m_LOAD_HDCP_KEY); + msleep(1); + value = HDMIRdReg(HDCP_KEY_MEM_CTRL); + temp++; + } + + return HDCP_OK; +} + + +int rk30_hdcp_start_authentication(void) +{ + int rc, temp; + + rc = rk30_hdcp_load_key(); + if(rc != HDCP_OK) + return rc; + + // Set 100ms & 5 sec timer + switch(hdmi->vic) + { + case HDMI_720x576p_50Hz_4_3: + case HDMI_720x576p_50Hz_16_9: + case HDMI_1280x720p_50Hz: + case HDMI_1920x1080i_50Hz: + case HDMI_720x576i_50Hz_4_3: + case HDMI_720x576i_50Hz_16_9: + case HDMI_1920x1080p_50Hz: + HDMIWrReg(HDCP_TIMER_100MS, 5); + HDMIWrReg(HDCP_TIMER_5S, 250); + break; + + default: + HDMIWrReg(HDCP_TIMER_100MS, 0x26); + HDMIWrReg(HDCP_TIMER_5S, 0x2c); + break; + } + // Config DDC Clock + temp = (hdmi->tmdsclk/HDCP_DDC_CLK)/4; + HDMIWrReg(DDC_BUS_FREQ_L, temp & 0xFF); + HDMIWrReg(DDC_BUS_FREQ_H, (temp >> 8) & 0xFF); + // Enable HDCP Interrupt + HDMIWrReg(INTR_MASK2, m_INT_HDCP_ERR | m_INT_BKSV_RPRDY | m_INT_BKSV_RCRDY | m_INT_AUTH_DONE | m_INT_AUTH_READY); + // Start HDCP + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_AUTH_START | m_HDCP_FRAMED_ENCRYPED, v_HDCP_AUTH_START(1) | v_HDCP_FRAMED_ENCRYPED(0)); + + return HDCP_OK; +} + +int rk30_hdcp_check_bksv(void) +{ + int i, temp; + char bksv[5]; + char *invalidkey; + + temp = HDMIRdReg(HDCP_BCAPS); + DBG("Receiver capacity is 0x%02x", temp); + +#ifdef DEBUG + if(temp & m_HDMI_RECEIVED) + DBG("Receiver support HDMI"); + if(temp & m_REPEATER) + DBG("Receiver is a repeater"); + if(temp & m_DDC_FAST) + DBG("Receiver support 400K DDC"); + if(temp & m_1_1_FEATURE) + DBG("Receiver support 1.1 features, such as advanced cipher, EESS."); + if(temp & m_FAST_REAUTHENTICATION) + DBG("Receiver support fast reauthentication."); +#endif + + for(i = 0; i < 5; i++) { + bksv[i] = HDMIRdReg(HDCP_KSV_BYTE0 + (4 - i)*4) & 0xFF; + } + + DBG("bksv is 0x%02x%02x%02x%02x%02x", bksv[0], bksv[1], bksv[2], bksv[3], bksv[4]); + + for(i = 0; i < hdcp->invalidkey; i++) + { + invalidkey = hdcp->invalidkeys + i *5; + if(memcmp(bksv, invalidkey, 5) == 0) { + printk(KERN_ERR "HDCP: BKSV was revocated!!!\n"); + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_BKSV_FAILED | m_HDCP_FRAMED_ENCRYPED, v_HDCP_BKSV_FAILED(1) | v_HDCP_FRAMED_ENCRYPED(0)); + return HDCP_KSV_ERR; + } + } + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_BKSV_PASS | m_HDCP_FRAMED_ENCRYPED, v_HDCP_BKSV_PASS(1) | v_HDCP_FRAMED_ENCRYPED(1)); + return HDCP_OK; +} diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.h b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.h new file mode 100755 index 000000000000..0224d88ab134 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/hdcp/rk30_hdmi_hdcp.h @@ -0,0 +1,99 @@ +#ifndef __RK30_HDMI_HDCP_H__ +#define __RK30_HDMI_HDCP_H__ + +/***************************/ +/* Definitions */ +/***************************/ + +/* Status / error codes */ +#define HDCP_OK 0 +#define HDCP_KEY_ERR 1 +#define HDCP_KSV_ERR 2 + +/* Delays */ +#define HDCP_ENABLE_DELAY 300 +#define HDCP_REAUTH_DELAY 100 + +/* Event source */ +#define HDCP_SRC_SHIFT 8 +#define HDCP_IOCTL_SRC (0x1 << HDCP_SRC_SHIFT) +#define HDCP_HDMI_SRC (0x2 << HDCP_SRC_SHIFT) +#define HDCP_IRQ_SRC (0x4 << HDCP_SRC_SHIFT) +#define HDCP_WORKQUEUE_SRC (0x8 << HDCP_SRC_SHIFT) + +/* Event */ +#define HDCP_ENABLE_CTL (HDCP_IOCTL_SRC | 0) +#define HDCP_DISABLE_CTL (HDCP_IOCTL_SRC | 1) +#define HDCP_START_FRAME_EVENT (HDCP_HDMI_SRC | 2) +#define HDCP_STOP_FRAME_EVENT (HDCP_HDMI_SRC | 3) +#define HDCP_KSV_LIST_RDY_EVENT (HDCP_IRQ_SRC | 4) +#define HDCP_FAIL_EVENT (HDCP_IRQ_SRC | 5) +#define HDCP_AUTH_PASS_EVENT (HDCP_IRQ_SRC | 6) +#define HDCP_AUTH_REATT_EVENT (HDCP_WORKQUEUE_SRC | 7) + +/* Key size */ +#define HDCP_KEY_SIZE 308 + +/* Authentication retry times */ +#define HDCP_INFINITE_REAUTH 0x100 + +enum hdcp_states { + HDCP_DISABLED, + HDCP_ENABLE_PENDING, + HDCP_AUTHENTICATION_START, + HDCP_WAIT_KSV_LIST, + HDCP_LINK_INTEGRITY_CHECK, +}; + +enum hdmi_states { + HDMI_STOPPED, + HDMI_STARTED +}; + +#define HDCP_PRIVATE_KEY_SIZE 280 +#define HDCP_KEY_SHA_SIZE 20 +#define HDCP_DDC_CLK 100000 + +struct hdcp_keys{ + u8 KSV[8]; + u8 DeviceKey[HDCP_PRIVATE_KEY_SIZE]; + u8 sha1[HDCP_KEY_SHA_SIZE]; +}; + +struct hdcp_delayed_work { + struct delayed_work work; + int event; +}; + +struct hdcp { + int enable; + int retry_times; + struct hdcp_keys *keys; + int invalidkey; + char *invalidkeys; + struct mutex lock; + struct completion complete; + struct workqueue_struct *workqueue; + + enum hdmi_states hdmi_state; + enum hdcp_states hdcp_state; + + struct delayed_work *pending_start; + struct delayed_work *pending_wq_event; + int retry_cnt; +}; + +extern struct hdcp *hdcp; + +#ifdef HDCP_DEBUG +#define DBG(format, ...) \ + printk(KERN_INFO "HDCP: " format "\n", ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +extern void rk30_hdcp_disable(void); +extern int rk30_hdcp_start_authentication(void); +extern int rk30_hdcp_check_bksv(void); +extern int rk30_hdcp_load_key2mem(struct hdcp_keys *key); +#endif /* __RK30_HDMI_HDCP_H__ */ \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.c b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.c new file mode 100755 index 000000000000..1ef14180bd6f --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.c @@ -0,0 +1,299 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "rk2928_hdmi.h" +#include "rk2928_hdmi_hw.h" + +struct hdmi *hdmi = NULL; + +extern irqreturn_t hdmi_irq(int irq, void *priv); +extern void hdmi_work(struct work_struct *work); +extern struct rk_lcdc_device_driver * rk_get_lcdc_drv(char *name); +extern void hdmi_register_display_sysfs(struct hdmi *hdmi, struct device *parent); +extern void hdmi_unregister_display_sysfs(struct hdmi *hdmi); + +int rk2928_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void), + void (*hdcp_irq_cb)(int status), + int (*hdcp_power_on_cb)(void), + void (*hdcp_power_off_cb)(void)) +{ + if(hdmi == NULL) + return HDMI_ERROR_FALSE; + + hdmi->hdcp_cb = hdcp_cb; + hdmi->hdcp_irq_cb = hdcp_irq_cb; + hdmi->hdcp_power_on_cb = hdcp_power_on_cb; + hdmi->hdcp_power_off_cb = hdcp_power_off_cb; + + return HDMI_ERROR_SUCESS; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void hdmi_early_suspend(struct early_suspend *h) +{ + hdmi_dbg(hdmi->dev, "hdmi enter early suspend pwr %d state %d\n", hdmi->pwr_mode, hdmi->state); + flush_delayed_work(&hdmi->delay_work); + mutex_lock(&hdmi->enable_mutex); + hdmi->suspend = 1; + if(!hdmi->enable) { + mutex_unlock(&hdmi->enable_mutex); + return; + } + disable_irq(hdmi->irq); + mutex_unlock(&hdmi->enable_mutex); + hdmi->command = HDMI_CONFIG_ENABLE; + init_completion(&hdmi->complete); + hdmi->wait = 1; + queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0); + wait_for_completion_interruptible_timeout(&hdmi->complete, + msecs_to_jiffies(5000)); + flush_delayed_work(&hdmi->delay_work); + // 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. +// rk2928_mux_api_set(GPIO0A7_I2C3_SDA_HDMI_DDCSDA_NAME, GPIO0A_GPIO0A7); +// rk2928_mux_api_set(GPIO0A6_I2C3_SCL_HDMI_DDCSCL_NAME, GPIO0A_GPIO0A6); + return; +} + +static void hdmi_early_resume(struct early_suspend *h) +{ + hdmi_dbg(hdmi->dev, "hdmi exit early resume\n"); + mutex_lock(&hdmi->enable_mutex); + +// rk2928_mux_api_set(GPIO0A7_I2C3_SDA_HDMI_DDCSDA_NAME, GPIO0A_HDMI_DDCSDA); +// rk2928_mux_api_set(GPIO0A6_I2C3_SCL_HDMI_DDCSCL_NAME, GPIO0A_HDMI_DDCSCL); + + hdmi->suspend = 0; + rk2928_hdmi_initial(); + if(hdmi->enable) { + enable_irq(hdmi->irq); + } + mutex_unlock(&hdmi->enable_mutex); + return; +} +#endif + +static inline void hdmi_io_remap(void) +{ + unsigned int value; + + // Remap HDMI IO Pin +// rk2928_mux_api_set(GPIO0A7_I2C3_SDA_HDMI_DDCSDA_NAME, GPIO0A_HDMI_DDCSDA); +// rk2928_mux_api_set(GPIO0A6_I2C3_SCL_HDMI_DDCSCL_NAME, GPIO0A_HDMI_DDCSCL); +// rk2928_mux_api_set(GPIO0B7_HDMI_HOTPLUGIN_NAME, GPIO0B_HDMI_HOTPLUGIN); + + // Select LCDC0 as video source and enabled. +// value = (HDMI_SOURCE_DEFAULT << 14) | (1 << 30); +// writel(value, GRF_SOC_CON0 + rk2928_GRF_BASE); +} + +static int __devinit rk2928_hdmi_probe (struct platform_device *pdev) +{ + int ret; + struct resource *res; + struct resource *mem; + + hdmi = kmalloc(sizeof(struct hdmi), GFP_KERNEL); + if(!hdmi) + { + dev_err(&pdev->dev, ">>rk30 hdmi kmalloc fail!"); + return -ENOMEM; + } + memset(hdmi, 0, sizeof(struct hdmi)); + hdmi->dev = &pdev->dev; + platform_set_drvdata(pdev, hdmi); + + if(HDMI_SOURCE_DEFAULT == HDMI_SOURCE_LCDC0) + hdmi->lcdc = rk_get_lcdc_drv("lcdc0"); + else + hdmi->lcdc = rk_get_lcdc_drv("lcdc1"); + if(hdmi->lcdc == NULL) + { + dev_err(hdmi->dev, "can not connect to video source lcdc\n"); + ret = -ENXIO; + goto err0; + } + hdmi->xscale = 95; + hdmi->yscale = 95; + + hdmi->hclk = clk_get(NULL,"hclk_hdmi"); + if(IS_ERR(hdmi->hclk)) + { + dev_err(hdmi->dev, "Unable to get hdmi hclk\n"); + ret = -ENXIO; + goto err0; + } + clk_enable(hdmi->hclk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(hdmi->dev, "Unable to get register resource\n"); + ret = -ENXIO; + goto err0; + } + hdmi->regbase_phy = res->start; + hdmi->regsize_phy = (res->end - res->start) + 1; + mem = request_mem_region(res->start, (res->end - res->start) + 1, pdev->name); + if (!mem) + { + dev_err(hdmi->dev, "failed to request mem region for hdmi\n"); + ret = -ENOENT; + goto err0; + } + + + hdmi->regbase = (int)ioremap(res->start, (res->end - res->start) + 1); + if (!hdmi->regbase) { + dev_err(hdmi->dev, "cannot ioremap registers\n"); + ret = -ENXIO; + goto err1; + } + + ret = rk2928_hdmi_initial(); + if(ret != HDMI_ERROR_SUCESS) + goto err1; + + hdmi_io_remap(); + hdmi_sys_init(); + + hdmi->workqueue = create_singlethread_workqueue("hdmi"); + INIT_DELAYED_WORK(&(hdmi->delay_work), hdmi_work); + + #ifdef CONFIG_HAS_EARLYSUSPEND + hdmi->early_suspend.suspend = hdmi_early_suspend; + hdmi->early_suspend.resume = hdmi_early_resume; + hdmi->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 10; + register_early_suspend(&hdmi->early_suspend); + #endif + + hdmi_register_display_sysfs(hdmi, NULL); + #ifdef CONFIG_SWITCH + hdmi->switch_hdmi.name="hdmi"; + switch_dev_register(&(hdmi->switch_hdmi)); + #endif + + spin_lock_init(&hdmi->irq_lock); + mutex_init(&hdmi->enable_mutex); + + /* get the IRQ */ + hdmi->irq = platform_get_irq(pdev, 0); + if(hdmi->irq <= 0) { + dev_err(hdmi->dev, "failed to get hdmi irq resource (%d).\n", hdmi->irq); + ret = -ENXIO; + goto err2; + } + + /* request the IRQ */ + ret = request_irq(hdmi->irq, hdmi_irq, 0, dev_name(&pdev->dev), hdmi); + if (ret) + { + dev_err(hdmi->dev, "hdmi request_irq failed (%d).\n", ret); + goto err2; + } + + hdmi_dbg(hdmi->dev, "rk30 hdmi probe sucess.\n"); + return 0; +err2: + #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 + iounmap((void*)hdmi->regbase); +err1: + release_mem_region(res->start,(res->end - res->start) + 1); + clk_disable(hdmi->hclk); +err0: + hdmi_dbg(hdmi->dev, "rk30 hdmi probe error.\n"); + kfree(hdmi); + hdmi = NULL; + return ret; +} + +static int __devexit rk2928_hdmi_remove(struct platform_device *pdev) +{ + if(hdmi) { + mutex_lock(&hdmi->enable_mutex); + if(!hdmi->suspend && hdmi->enable) + disable_irq(hdmi->irq); + mutex_unlock(&hdmi->enable_mutex); + free_irq(hdmi->irq, NULL); + flush_workqueue(hdmi->workqueue); + destroy_workqueue(hdmi->workqueue); + #ifdef CONFIG_SWITCH + switch_dev_unregister(&(hdmi->switch_hdmi)); + #endif + hdmi_unregister_display_sysfs(hdmi); + #ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&hdmi->early_suspend); + #endif + iounmap((void*)hdmi->regbase); + release_mem_region(hdmi->regbase_phy, hdmi->regsize_phy); + clk_disable(hdmi->hclk); + fb_destroy_modelist(&hdmi->edid.modelist); + if(hdmi->edid.audio) + kfree(hdmi->edid.audio); + if(hdmi->edid.specs) + { + if(hdmi->edid.specs->modedb) + kfree(hdmi->edid.specs->modedb); + kfree(hdmi->edid.specs); + } + kfree(hdmi); + hdmi = NULL; + } + printk(KERN_INFO "rk30 hdmi removed.\n"); + return 0; +} + +static void rk2928_hdmi_shutdown(struct platform_device *pdev) +{ + if(hdmi) { + #ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&hdmi->early_suspend); + #endif + } + printk(KERN_INFO "rk30 hdmi shut down.\n"); +} + +static struct platform_driver rk2928_hdmi_driver = { + .probe = rk2928_hdmi_probe, + .remove = __devexit_p(rk2928_hdmi_remove), + .driver = { + .name = "rk30-hdmi", + .owner = THIS_MODULE, + }, + .shutdown = rk2928_hdmi_shutdown, +}; + +static int __init rk2928_hdmi_init(void) +{ + return platform_driver_register(&rk2928_hdmi_driver); +} + +static void __exit rk2928_hdmi_exit(void) +{ + platform_driver_unregister(&rk2928_hdmi_driver); +} + + +//fs_initcall(rk2928_hdmi_init); +module_init(rk2928_hdmi_init); +module_exit(rk2928_hdmi_exit); diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.h b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.h new file mode 100755 index 000000000000..4695086bf329 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi.h @@ -0,0 +1,11 @@ +#ifndef __RK30_HDMI_H__ +#define __RK30_HDMI_H__ + +#include "../../rk_hdmi.h" + + +extern int rk30_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void), + void (*hdcp_irq_cb)(int status), + int (*hdcp_power_on_cb)(void), + void (*hdcp_power_off_cb)(void)); +#endif /* __RK30_HDMI_H__ */ diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.c b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.c new file mode 100755 index 000000000000..4f6e636cc956 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.c @@ -0,0 +1,456 @@ +#include +#include +#include +#include "rk2928_hdmi.h" +#include "rk2928_hdmi_hw.h" + +static char edid_result = 0; +static bool analog_sync = 0; + +static inline void delay100us(void) +{ + msleep(1); +} + + +static void rk2928_hdmi_av_mute(bool enable) +{ + HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(enable) | v_VIDEO_MUTE(enable)); +} + +static void rk2928_hdmi_sys_power_up(void) +{ + hdmi_dbg(hdmi->dev,"%s \n",__FUNCTION__); + HDMIWrReg(SYS_CTRL, v_REG_CLK_SOURCE_IIS | v_PWR_ON | v_INT_POL_HIGH); +} +static void rk2928_hdmi_sys_power_down(void) +{ + hdmi_dbg(hdmi->dev,"%s \n",__FUNCTION__); + HDMIWrReg(SYS_CTRL, v_REG_CLK_SOURCE_IIS | v_PWR_OFF | v_INT_POL_HIGH); +} + + + +static void rk2928_hdmi_set_pwr_mode(int mode) +{ + hdmi_dbg(hdmi->dev,"%s \n",__FUNCTION__); + if(hdmi->pwr_mode == mode) + return; + switch(mode){ + case NORMAL: + rk2928_hdmi_sys_power_down(); + HDMIWrReg(0xe3, 0x82); + HDMIWrReg(0xe5, 0x00); + HDMIWrReg(0xe4, 0x00); + HDMIWrReg(0xe7, 0x00); + HDMIWrReg(0xe1, 0x8e); + HDMIWrReg(0xce, 0x00); + HDMIWrReg(0xce, 0x01); + rk2928_hdmi_av_mute(1); + rk2928_hdmi_sys_power_up(); + analog_sync = 1; + break; + case LOWER_PWR: + rk2928_hdmi_av_mute(0); + rk2928_hdmi_sys_power_down(); + HDMIWrReg(0xe3, 0x02); + HDMIWrReg(0xe5, 0x1c); + HDMIWrReg(0xe1, 0x8c); + HDMIWrReg(0xe7, 0x04); + HDMIWrReg(0xe4, 0x03); + break; + default: + hdmi_dbg(hdmi->dev,"unkown rk2928 hdmi pwr mode %d\n",mode); + } + hdmi->pwr_mode = mode; +} + + +int rk2928_hdmi_detect_hotplug(void) +{ + int value = HDMIRdReg(HDMI_STATUS); + + hdmi_dbg(hdmi->dev, "[%s] value %02x\n", __FUNCTION__, value); + value &= m_HOTPLUG; + if(value == m_HOTPLUG) + return HDMI_HPD_ACTIVED; + else if(value) + return HDMI_HPD_INSERT; + else + return HDMI_HPD_REMOVED; +} + +#define HDMI_SYS_FREG_CLK 11289600 +#define HDMI_SCL_RATE (100*1000) +#define HDMI_DDC_CONFIG (HDMI_SYS_FREG_CLK>>2)/HDMI_SCL_RATE +#define DDC_BUS_FREQ_L 0x4b +#define DDC_BUS_FREQ_H 0x4c + +int rk2928_hdmi_read_edid(int block, unsigned char *buff) +{ + int value, ret = -1, ddc_bus_freq = 0; + char interrupt = 0, trytime = 2; + unsigned long flags; + + hdmi_dbg(hdmi->dev, "[%s] block %d\n", __FUNCTION__, block); + spin_lock_irqsave(&hdmi->irq_lock, flags); + edid_result = 0; + spin_unlock_irqrestore(&hdmi->irq_lock, flags); + //Before Phy parameter was set, DDC_CLK is equal to PLLA freq which is 30MHz. + //Set DDC I2C CLK which devided from DDC_CLK to 100KHz. + + ddc_bus_freq = HDMI_DDC_CONFIG; + HDMIWrReg(DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF); + HDMIWrReg(DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF); + + // Enable edid interrupt + HDMIWrReg(INTERRUPT_MASK1, m_INT_HOTPLUG | m_INT_EDID_READY); + + while(trytime--) { + // Config EDID block and segment addr + HDMIWrReg(EDID_WORD_ADDR, (block%2) * 0x80); + HDMIWrReg(EDID_SEGMENT_POINTER, block/2); + + value = 10; + while(value--) + { + spin_lock_irqsave(&hdmi->irq_lock, flags); + interrupt = edid_result; + edid_result = 0; + spin_unlock_irqrestore(&hdmi->irq_lock, flags); + if(interrupt & (m_INT_EDID_READY)) + break; + msleep(10); + } + hdmi_dbg(hdmi->dev, "[%s] edid read value %d\n", __FUNCTION__, value); + if(interrupt & m_INT_EDID_READY) + { + for(value = 0; value < HDMI_EDID_BLOCK_SIZE; value++) + buff[value] = HDMIRdReg(EDID_FIFO_ADDR); + ret = 0; + + hdmi_dbg(hdmi->dev, "[%s] edid read sucess\n", __FUNCTION__); +#ifdef HDMI_DEBUG + for(value = 0; value < 128; value++) { + printk("%02x ,", buff[value]); + if( (value + 1) % 16 == 0) + printk("\n"); + } +#endif + break; + }else + hdmi_err(hdmi->dev, "[%s] edid read error\n", __FUNCTION__); + + } + // Disable edid interrupt + HDMIWrReg(INTERRUPT_MASK1, m_INT_HOTPLUG); +// msleep(100); + return ret; +} + +static void rk2928_hdmi_config_avi(unsigned char vic, unsigned char output_color) +{ + int i; + char info[SIZE_AVI_INFOFRAME]; + + memset(info, 0, SIZE_AVI_INFOFRAME); + HDMIWrReg(CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI); + info[0] = 0x82; + info[1] = 0x02; + info[2] = 0x0D; + info[3] = info[0] + info[1] + info[2]; + info[4] = (AVI_COLOR_MODE_RGB << 5); + info[5] = (AVI_COLORIMETRY_NO_DATA << 6) | (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) | ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME; + info[6] = 0; + info[7] = vic; + info[8] = 0; + + // Calculate AVI InfoFrame ChecKsum + for (i = 4; i < SIZE_AVI_INFOFRAME; i++) + { + info[3] += info[i]; + } + info[3] = 0x100 - info[3]; + + for(i = 0; i < SIZE_AVI_INFOFRAME; i++) + HDMIWrReg(CONTROL_PACKET_ADDR + i, info[i]); +} + +static int rk2928_hdmi_config_video(struct hdmi_video_para *vpara) +{ + int value; + struct fb_videomode *mode; + + hdmi_dbg(hdmi->dev, "[%s]\n", __FUNCTION__); + if(vpara == NULL) { + hdmi_err(hdmi->dev, "[%s] input parameter error\n", __FUNCTION__); + return -1; + } + + if(hdmi->hdcp_power_off_cb) + hdmi->hdcp_power_off_cb(); + // Diable video and audio output + HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1)); + + // Input video mode is SDR RGB24bit, Data enable signal from external + HDMIWrReg(VIDEO_CONTRL1, v_VIDEO_INPUT_FORMAT(VIDEO_INPUT_SDR_RGB444) | v_DE_EXTERNAL); + HDMIWrReg(VIDEO_CONTRL2, v_VIDEO_INPUT_BITS(VIDEO_INPUT_8BITS) | (vpara->output_color & 0xFF)); + + // Set HDMI Mode + HDMIWrReg(HDCP_CTRL, v_HDMI_DVI(vpara->output_mode)); + + // Enable or disalbe color space convert + if(vpara->input_color != vpara->output_color) { + value = v_SOF_DISABLE | v_CSC_ENABLE; + } + else + value = v_SOF_DISABLE; + HDMIWrReg(VIDEO_CONTRL3, value); + + // Set ext video +#if 1 + HDMIWrReg(VIDEO_TIMING_CTL, 0); + mode = (struct fb_videomode *)hdmi_vic_to_videomode(vpara->vic); + if(mode == NULL) + { + hdmi_err(hdmi->dev, "[%s] not found vic %d\n", __FUNCTION__, vpara->vic); + return -ENOENT; + } + hdmi->tmdsclk = mode->pixclock; +#else + value = v_EXTERANL_VIDEO(1) | v_INETLACE(mode->vmode); + if(mode->sync & FB_SYNC_HOR_HIGH_ACT) + value |= v_HSYNC_POLARITY(1); + if(mode->sync & FB_SYNC_VERT_HIGH_ACT) + value |= v_VSYNC_POLARITY(1); + HDMIWrReg(VIDEO_TIMING_CTL, value); + + value = mode->left_margin + mode->xres + mode->right_margin + mode->hsync_len; + HDMIWrReg(VIDEO_EXT_HTOTAL_L, value & 0xFF); + HDMIWrReg(VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF); + + value = mode->left_margin + mode->right_margin + mode->hsync_len; + HDMIWrReg(VIDEO_EXT_HBLANK_L, value & 0xFF); + HDMIWrReg(VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF); + + value = mode->left_margin + mode->hsync_len; + HDMIWrReg(VIDEO_EXT_HDELAY_L, value & 0xFF); + HDMIWrReg(VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF); + + value = mode->hsync_len; + HDMIWrReg(VIDEO_EXT_HDURATION_L, value & 0xFF); + HDMIWrReg(VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF); + + value = mode->upper_margin + mode->yres + mode->lower_margin + mode->vsync_len; + HDMIWrReg(VIDEO_EXT_VTOTAL_L, value & 0xFF); + HDMIWrReg(VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF); + + value = mode->upper_margin + mode->vsync_len + mode->lower_margin; + HDMIWrReg(VIDEO_EXT_VBLANK, value & 0xFF); + + if(vpara->vic == HDMI_720x480p_60Hz_4_3 || vpara->vic == HDMI_720x480p_60Hz_16_9) + value = 42; + else + value = mode->upper_margin + mode->vsync_len; + + HDMIWrReg(VIDEO_EXT_VDELAY, value & 0xFF); + + value = mode->vsync_len; + HDMIWrReg(VIDEO_EXT_VDURATION, value & 0xFF); +#endif + + if(vpara->output_mode == OUTPUT_HDMI) { + rk2928_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 = HDMIRdReg(PHY_DRIVER); + value |= v_TX_ENABLE(1); + HDMIWrReg(PHY_DRIVER, value); + + return 0; +} + +static void rk2928_hdmi_config_aai(void) +{ + int i; + char info[SIZE_AUDIO_INFOFRAME]; + + memset(info, 0, SIZE_AUDIO_INFOFRAME); + + info[0] = 0x84; + info[1] = 0x01; + info[2] = 0x0A; + + info[3] = info[0] + info[1] + info[2]; + for (i = 4; i < SIZE_AUDIO_INFOFRAME; i++) + info[3] += info[i]; + + info[3] = 0x100 - info[3]; + + HDMIWrReg(CONTROL_PACKET_BUF_INDEX, INFOFRAME_AAI); + for(i = 0; i < SIZE_AUDIO_INFOFRAME; i++) + HDMIWrReg(CONTROL_PACKET_ADDR + i, info[i]); +} + +static int rk2928_hdmi_config_audio(struct hdmi_audio *audio) +{ + int rate, N, channel, mclk_fs; + + if(audio->channel < 3) + channel = I2S_CHANNEL_1_2; + else if(audio->channel < 5) + channel = I2S_CHANNEL_3_4; + else if(audio->channel < 7) + channel = I2S_CHANNEL_5_6; + else + channel = I2S_CHANNEL_7_8; + + switch(audio->rate) + { + case HDMI_AUDIO_FS_32000: + rate = AUDIO_32K; + N = N_32K; + mclk_fs = MCLK_384FS; + break; + case HDMI_AUDIO_FS_44100: + rate = AUDIO_441K; + N = N_441K; + mclk_fs = MCLK_256FS; + break; + case HDMI_AUDIO_FS_48000: + rate = AUDIO_48K; + N = N_48K; + mclk_fs = MCLK_256FS; + break; + case HDMI_AUDIO_FS_88200: + rate = AUDIO_882K; + N = N_882K; + mclk_fs = MCLK_128FS; + break; + case HDMI_AUDIO_FS_96000: + rate = AUDIO_96K; + N = N_96K; + mclk_fs = MCLK_128FS; + break; + case HDMI_AUDIO_FS_176400: + rate = AUDIO_1764K; + N = N_1764K; + mclk_fs = MCLK_128FS; + break; + case HDMI_AUDIO_FS_192000: + rate = AUDIO_192K; + N = N_192K; + mclk_fs = MCLK_128FS; + break; + default: + dev_err(hdmi->dev, "[%s] not support such sample rate %d\n", __FUNCTION__, audio->rate); + return -ENOENT; + } + + //set_audio source I2S + HDMIWrReg(AUDIO_CTRL1, 0x00); //internal CTS, disable down sample, i2s input, disable MCLK + HDMIWrReg(AUDIO_SAMPLE_RATE, rate); + HDMIWrReg(AUDIO_I2S_MODE, v_I2S_MODE(I2S_STANDARD) | v_I2S_CHANNEL(channel) ); + HDMIWrReg(AUDIO_I2S_MAP, 0x00); + HDMIWrReg(AUDIO_I2S_SWAPS_SPDIF, 0); // no swap + + //Set N value + HDMIWrReg(AUDIO_N_H, (N >> 16) & 0x0F); + HDMIWrReg(AUDIO_N_M, (N >> 8) & 0xFF); + HDMIWrReg(AUDIO_N_L, N & 0xFF); + rk2928_hdmi_config_aai(); + + return 0; +} + +static void rk2928_hdmi_control_output(int enable) +{ + char mutestatus = 0; + + if(enable) { + mutestatus = HDMIRdReg(AV_MUTE); + if(mutestatus && (m_AUDIO_MUTE | m_VIDEO_BLACK)) { + HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0)); + rk2928_hdmi_sys_power_up(); + rk2928_hdmi_sys_power_down(); + rk2928_hdmi_sys_power_up(); + if(analog_sync){ + HDMIWrReg(0xce, 0x00); + delay100us(); + HDMIWrReg(0xce, 0x01); + analog_sync = 0; + } + } + } + else { + HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1)); + } +} + +int rk2928_hdmi_removed(void) +{ + + dev_printk(KERN_INFO , hdmi->dev , "Removed.\n"); + rk2928_hdmi_set_pwr_mode(LOWER_PWR); + + return HDMI_ERROR_SUCESS; +} + + +irqreturn_t hdmi_irq(int irq, void *priv) +{ + char interrupt1 = 0; + unsigned long flags; + spin_lock_irqsave(&hdmi->irq_lock,flags); + interrupt1 = HDMIRdReg(INTERRUPT_STATUS1); + HDMIWrReg(INTERRUPT_STATUS1, interrupt1); +#if 1 + hdmi_dbg(hdmi->dev, "[%s] interrupt1 %02x interrupt2 %02x \n",\ + __FUNCTION__, interrupt1, interrupt2); +#endif + if(interrupt1 & m_INT_HOTPLUG ){ + if(hdmi->state == HDMI_SLEEP) + hdmi->state = WAIT_HOTPLUG; + if(hdmi->pwr_mode == LOWER_PWR) + rk2928_hdmi_set_pwr_mode(NORMAL); + queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, msecs_to_jiffies(10)); + }else if(interrupt1 & m_INT_EDID_READY) { + edid_result = interrupt1; + }else if(hdmi->state == HDMI_SLEEP) { + hdmi_dbg(hdmi->dev, "hdmi return to sleep mode\n"); + rk2928_hdmi_set_pwr_mode(LOWER_PWR); + } +#if 0 + if(hdmi->hdcp_irq_cb) + hdmi->hdcp_irq_cb(interrupt2); +#endif + spin_unlock_irqrestore(&hdmi->irq_lock,flags); + return IRQ_HANDLED; +} + +int rk2928_hdmi_initial(void) +{ + int rc = HDMI_ERROR_SUCESS; + + hdmi->pwr_mode = NORMAL; + hdmi->hdmi_removed = rk2928_hdmi_removed ; + hdmi->control_output = rk2928_hdmi_control_output; + hdmi->config_video = rk2928_hdmi_config_video; + hdmi->config_audio = rk2928_hdmi_config_audio; + hdmi->detect_hotplug = rk2928_hdmi_detect_hotplug; + hdmi->read_edid = rk2928_hdmi_read_edid; + // internal hclk = hdmi_hclk/20 + //HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV); + + if(hdmi->hdcp_power_on_cb) + rc = hdmi->hdcp_power_on_cb(); + + return rc; +} diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.h b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.h new file mode 100644 index 000000000000..6fd0f957a1ad --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.h @@ -0,0 +1,248 @@ +#ifndef _RK2928_HDMI_HW_H +#define _RK2928_HDMI_HW_H + +enum PWR_MODE{ + NORMAL, + LOWER_PWR, +}; +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) + +#define HDMIRdReg(addr) __raw_readl(hdmi->regbase + addr) +#define HDMIWrReg(addr, val) __raw_writel((val), hdmi->regbase + addr); +#define HDMIMskReg(temp, addr, msk, val) \ + temp = __raw_readl(hdmi->regbase + addr) & (0xFF - (msk)) ; \ + __raw_writel(temp | ( (val) & (msk) ), hdmi->regbase + addr); + +extern int rk2928_hdmi_initial(void); + +#endif diff --git a/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.hbak b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.hbak new file mode 100755 index 000000000000..fe06fe040278 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk2928/rk2928_hdmi_hw.hbak @@ -0,0 +1,497 @@ +#ifndef __RK2928_HDMI_HW_H__ +#define __RK2928_HDMI_HW_H__ + + +//0x00 +#define INT_POL 1 +#define SYS_PWR_ON 0 +#define SYS_PWR_OFF 1 +#define PHY_CLK 0 +#define SYS_CLK 1 + +#define MCLK_FS 0x01 //256fs +//0x01 +// INPUT_VIDEO_FORMAT +#define RGB_YUV444 0x00 +#define DDR_RGB444_YUV444 0x05 +#define DDR_YUV422 0x06 + +//0x02 +//video output format +#define RGB444 0x00 +#define YUV444 0x01 +#define YUV422 0x02 + +//DATA WIDTH +#define DATA_12BIT 0X00 +#define DATA_10BIT 0X01 +#define DATA_8BIT 0X03 + +//0X04 +//1:after 0:not After 1st sof for external DE sample +#define DE_AFTER_SOF 0 +#define DE_NOAFTER_SOF 1 + +#define CSC_ENABLE 0 +#define CSC_DISABLE 1 + +//0X05 +#define CLEAR_AVMUTE(x) (x)<<7 +#define SET_AVMUTE(x) (x)<<6 +#define AUDIO_MUTE(x) (x)<<1 +#define VIDEO_BLACK(x) (x)<<0 //1:black 0:normal + +//0x08 +#define VSYNC_POL(x) (x)<<3 //0:Negative 1:Positive +#define HSYNC_POL(x) (x)<<2 //0:Negative 1:Positive +#define INTER_PROGRESSIVE(x) (x)<<1 //0: progressive 1:interlace +#define VIDEO_SET_ENABLE(x) (x)<<0 //0:disable 1: enable + +/* 0xe1 */ +//Main-driver strength :0000~1111: the strength from low to high +#define M_DRIVER_STR(x) (((x)&0xf)<<4) +//Pre-driver strength :00~11: the strength from low to high +#define P_DRIVER_STR(x) (((x)&3)<<2) +//TX driver enable 1: enable 0: disable +#define TX_DRIVER_EN(x) (((x)&1)<<1) +/* 0xe2 */ +//Pre-emphasis strength 00~11: the strength from 0 to high +#define P_EMPHASIS_STR(x) (((x)&3)<<4) +//Power down TMDS driver 1: power down. 0: not +#define PWR_DOWN_TMDS(x) (((x)&1)<<0) +/* 0xe3 */ +//PLL out enable. Just for test. need set to 1’b0 +#define PLL_OUT_EN(x) (((x)&1)<<7) +/* 0xe4 */ +// Band-Gap power down 11: power down 00: not +#define BAND_PWR(x) (((x)&3)<<0) +/* 0xe5 */ +//PLL disable 1: disable 0: enable +#define PLL_PWR(x) (((x)&1)<<4) +// PLL reset 1: reset 0: not +#define PLL_RST(x) (((x)&1)<<3) +//PHY TMDS channels reset 1: reset 0: not +#define TMDS_RST(x) (((x)&1)<<2) +/* 0xe7 */ +// PLL LDO power down 1: power down 0: not +#define PLL_LDO_PWR(x) (((x)&1)<<2) + +enum PWR_MODE{ + NORMAL, + LOWER_PWR, +}; + +#if 0 +/* HDMI_SYS_CONTROL */ +#define SYS_CTRL 0x0 + +enum { + PWR_SAVE_MODE_A = 1, + PWR_SAVE_MODE_B = 2, + PWR_SAVE_MODE_D = 4, + PWR_SAVE_MODE_E = 8 +}; +#define m_PWR_SAVE_MODE 0xF0 +#define v_PWR_SAVE_MODE(n) (n << 4) +#define PLL_B_RESET (1 << 3) + +#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 LR_SWAP_N3 0x04 +#define N_2 0x08 +#define N_1 0x0c + +#define AUDIO_CTRL1 0x28 +#define AUDIO_CTRL2 0x2c +#define I2S_AUDIO_CTRL 0x30 +enum { + I2S_MODE_STANDARD = 0, + I2S_MODE_RIGHT_JUSTIFIED, + I2S_MODE_LEFT_JUSTIFIED +}; +#define v_I2S_MODE(n) n +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 ) + +#define I2S_INPUT_SWAP 0x40 + +#define SRC_NUM_AUDIO_LEN 0x50 + +/* HDMI_AV_CTRL1*/ +#define AV_CTRL1 0x54 +enum { + AUDIO_32K = 0x3, + AUDIO_441K = 0x0, + AUDIO_48K = 0x2, + AUDIO_882K = 0x8, + AUDIO_96K = 0xa, + AUDIO_1764K = 0xc, + AUDIO_192K = 0xe, +}; +#define m_AUDIO_SAMPLE_RATE 0xF0 +#define v_AUDIO_SAMPLE_RATE(n) (n << 4) +#define m_INPUT_VIDEO_MODE (7 << 1) +#define v_INPUT_VIDEO_MODE(n) (n << 1) +enum { + INTERNAL_DE = 0, + EXTERNAL_DE +}; +#define m_DE_SIGNAL_SELECT (1 << 0) + +/* HDMI_AV_CTRL2 */ +#define AV_CTRL2 0xec +#define m_CSC_ENABLE (1 << 0) +#define v_CSC_ENABLE(n) (n) + +/* HDMI_VIDEO_CTRL1 */ +#define VIDEO_CTRL1 0x58 + +#define m_VIDEO_OUTPUT_MODE (0x3 << 6) +#define v_VIDEO_OUTPUT_MODE(n) (n << 6) +enum { + VIDEO_INPUT_DEPTH_12BIT = 0, + VIDEO_INPUT_DEPTH_10BIT = 0x1, + VIDEO_INPUT_DEPTH_8BIT = 0x3 +}; +#define m_VIDEO_INPUT_DEPTH (3 << 4) +#define v_VIDEO_INPUT_DEPTH(n) (n << 4) +enum { + VIDEO_EMBEDDED_SYNC_LOCATION_0 = 0, + VIDEO_EMBEDDED_SYNC_LOCATION_1, + VIDEO_EMBEDDED_SYNC_LOCATION_2 +}; +#define m_VIDEO_EMBEDDED_SYNC_LOCATION (3 << 2) +#define VIDEO_EMBEDDED_SYNC_LOCATION(n) (n << 2) +#define m_VIDEO_INPUT_COLOR_MODE (1 << 0) + +/* DEEP_COLOR_MODE */ +#define DEEP_COLOR_MODE 0x5c +enum{ + TMDS_CLOCK_MODE_8BIT = 0, + TMDS_CLOKK_MODE_10BIT, + TMDS_CLOKK_MODE_12BIT +}; +#define TMDS_CLOCK_MODE_MASK 0x3 << 6 +#define TMDS_CLOCK_MODE(n) (n) << 6 + +/* VIDEO_CTRL2 */ +#define VIDEO_SETTING2 0x114 +#define m_UNMUTE (1 << 7) +#define m_MUTE (1 << 6) +#define m_AUDIO_RESET (1 << 2) +#define m_NOT_SEND_AUDIO (1 << 1) +#define m_NOT_SEND_VIDEO (1 << 0) +#define AV_UNMUTE (1 << 7) // Unmute video and audio, send normal video and audio data +#define AV_MUTE (1 << 6) // Mute video and audio, send black video data and silent audio data +#define AUDIO_CAPTURE_RESET (1 << 2) // Reset audio process logic, only available in pwr_e mode. +#define NOT_SEND_AUDIO (1 << 1) // Send silent audio data +#define NOT_SEND_VIDEO (1 << 0) // Send black video data + +/* Color Space Convertion Parameter*/ +#define CSC_PARA_C0_H 0x60 +#define CSC_PARA_C0_L 0x64 +#define CSC_PARA_C1_H 0x68 +#define CSC_PARA_C1_L 0x6c +#define CSC_PARA_C2_H 0x70 +#define CSC_PARA_C2_L 0x74 +#define CSC_PARA_C3_H 0x78 +#define CSC_PARA_C3_L 0x7c +#define CSC_PARA_C4_H 0x80 +#define CSC_PARA_C4_L 0x84 +#define CSC_PARA_C5_H 0x88 +#define CSC_PARA_C5_L 0x8c +#define CSC_PARA_C6_H 0x90 +#define CSC_PARA_C6_L 0x94 +#define CSC_PARA_C7_H 0x98 +#define CSC_PARA_C7_L 0x9c +#define CSC_PARA_C8_H 0xa0 +#define CSC_PARA_C8_L 0xa4 +#define CSC_PARA_C9_H 0xa8 +#define CSC_PARA_C9_L 0xac +#define CSC_PARA_C10_H 0xac +#define CSC_PARA_C10_L 0xb4 +#define CSC_PARA_C11_H 0xb8 +#define CSC_PARA_C11_L 0xbc + +#define CSC_CONFIG1 0x34c +#define m_CSC_MODE (1 << 7) +#define m_CSC_COEF_MODE (0xF << 3) //Only used in auto csc mode +#define m_CSC_STATUS (1 << 2) +#define m_CSC_VID_SELECT (1 << 1) +#define m_CSC_BRSWAP_DIABLE (1) + +enum { + CSC_MODE_MANUAL = 0, + CSC_MODE_AUTO +}; +#define v_CSC_MODE(n) (n << 7) +enum { + COE_SDTV_LIMITED_RANGE = 0x08, + COE_SDTV_FULL_RANGE = 0x04, + COE_HDTV_60Hz = 0x2, + COE_HDTV_50Hz = 0x1 +}; +#define v_CSC_COE_MODE(n) (n << 3) +enum { + CSC_INPUT_VID_5_19 = 0, + CSC_INPUT_VID_28_29 +}; +#define v_CSC_VID_SELECT(n) (n << 1) +#define v_CSC_BRSWAP_DIABLE(n) (n) +#endif +/* CONTROL_PACKET_BUF_INDEX */ +#define CONTROL_PACKET_BUF_INDEX 0x17c +enum { + INFOFRAME_AVI = 0x06, + INFOFRAME_AAI = 0x08 +}; +#define CONTROL_PACKET_HB0 0x180 +#define CONTROL_PACKET_HB1 0x184 +#define CONTROL_PACKET_HB2 0x188 +#define CONTROL_PACKET_PB_ADDR 0x18c +#define SIZE_AVI_INFOFRAME 0x11 // 17 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 +}; +#if 0 +/* External Video Parameter Setting*/ +#define EXT_VIDEO_PARA 0xC0 +#define m_VSYNC_OFFSET (0xF << 4) +#define m_VSYNC_POLARITY (1 << 3) +#define m_HSYNC_POLARITY (1 << 2) +#define m_INTERLACE (1 << 1) +#define m_EXT_VIDEO_ENABLE (1 << 0) + +#define v_VSYNC_OFFSET(n) (n << 4) +#define v_VSYNC_POLARITY(n) (n << 3) +#define v_HSYNC_POLARITY(n) (n << 2) +#define v_INTERLACE(n) (n << 1) +#define v_EXT_VIDEO_ENABLE(n) (n << 0) + +#define EXT_VIDEO_PARA_HTOTAL_L 0xC4 +#define EXT_VIDEO_PARA_HTOTAL_H 0xC8 +#define EXT_VIDEO_PARA_HBLANK_L 0xCC +#define EXT_VIDEO_PARA_HBLANK_H 0xD0 +#define EXT_VIDEO_PARA_HDELAY_L 0xD4 +#define EXT_VIDEO_PARA_HDELAY_H 0xD8 +#define EXT_VIDEO_PARA_HSYNCWIDTH_L 0xDC +#define EXT_VIDEO_PARA_HSYNCWIDTH_H 0xE0 + +#define EXT_VIDEO_PARA_VTOTAL_L 0xE4 +#define EXT_VIDEO_PARA_VTOTAL_H 0xE8 +#define EXT_VIDEO_PARA_VBLANK_L 0xF4 +#define EXT_VIDEO_PARA_VDELAY 0xF8 +#define EXT_VIDEO_PARA_VSYNCWIDTH 0xFC + +#define PHY_PLL_SPEED 0x158 + #define v_TEST_EN(n) (n << 6) + #define v_PLLA_BYPASS(n) (n << 4) + #define v_PLLB_SPEED(n) (n << 2) + #define v_PLLA_SPEED(n) (n) + enum { + PLL_SPEED_LOWEST = 0, + PLL_SPEED_MIDLOW, + PLL_SPEED_MIDHIGH, + PLL_SPEED_HIGHEST + }; + +#define PHY_PLL_17 0x15c // PLL A & B config bit 17 + #define v_PLLA_BIT17(n) (n << 2) + #define v_PLLB_BIT17(n) (n << 1) + +#define PHY_BGR 0x160 + #define v_BGR_DISCONNECT(n) (n << 7) + #define v_BGR_V_OFFSET(n) (n << 4) + #define v_BGR_I_OFFSET(n) (n) + +#define PHY_PLLA_1 0x164 +#define PHY_PLLA_2 0x168 +#define PHY_PLLB_1 0x16c +#define PHY_PLLB_2 0x170 + +#define PHY_DRIVER_PREEMPHASIS 0x174 + #define v_TMDS_SWING(n) (n << 4) + #define v_PRE_EMPHASIS(n) (n) + +#define PHY_PLL_16_AML 0x178 // PLL A & B config bit 16 and AML control + #define v_PLLA_BIT16(n) (n << 5) + #define v_PLLB_BIT16(n) (n << 4) + #define v_AML(n) (n) +#endif +/* Interrupt Setting */ +#define INTR_MASK1 0xc0 +#define INTR_STATUS1 0xc1 + #define m_INT_HOTPLUG (1 << 7) + #define m_INT_VSYNC (1 << 5) + #define m_INT_EDID_READY (1 << 2) + +#define INTR_MASK2 0xc2 +#define INTR_STATUS2 0xc3 + #define m_INT_HDCP_ERR (1 << 7) // HDCP error detected + #define m_INT_BKSV_RPRDY (1 << 6) // BKSV list ready from repeater + #define m_INT_AUTH_DONE (1 << 4) // HDCP authentication done + +#if 0 +#define DDC_READ_FIFO_ADDR 0x200 +#define DDC_BUS_FREQ_L 0x204 +#define DDC_BUS_FREQ_H 0x208 +#define DDC_BUS_CTRL 0x2dc +#define DDC_I2C_LEN 0x278 +#define DDC_I2C_OFFSET 0x280 +#define DDC_I2C_CTRL 0x284 +#define DDC_I2C_READ_BUF0 0x288 +#define DDC_I2C_READ_BUF1 0x28c +#define DDC_I2C_READ_BUF2 0x290 +#define DDC_I2C_READ_BUF3 0x294 +#define DDC_I2C_WRITE_BUF0 0x298 +#define DDC_I2C_WRITE_BUF1 0x29c +#define DDC_I2C_WRITE_BUF2 0x2a0 +#define DDC_I2C_WRITE_BUF3 0x2a4 +#define DDC_I2C_WRITE_BUF4 0x2ac +#define DDC_I2C_WRITE_BUF5 0x2b0 +#define DDC_I2C_WRITE_BUF6 0x2b4 + +#endif +#define EDID_SEGMENT_POINTER 0x4d +#define EDID_WORD_ADDR 0x4e +#define EDID_FIFO_ADDR 0x4f + +#define PIN_STATUS 0xc8 +#define m_HOTPLUG_STATUS (1 << 7) +#define m_DDCSDA_STATUS (1 << 5) +#define m_DDCSCL_STATUS (1 << 4) +#if 0 +/* HDCP_CTRL */ +#define HDCP_CTRL 0x2bc + enum { + OUTPUT_DVI = 0, + OUTPUT_HDMI + }; + #define m_HDCP_AUTH_START (1 << 7) // start hdcp + #define m_HDCP_BKSV_PASS (1 << 6) // bksv valid + #define m_HDCP_BKSV_FAILED (1 << 5) // bksv invalid + #define m_HDCP_FRAMED_ENCRYPED (1 << 4) + #define m_HDCP_AUTH_STOP (1 << 3) // stop hdcp + #define m_HDCP_ADV_CIPHER (1 << 2) // advanced cipher mode + #define m_HDMI_DVI (1 << 1) + #define m_HDCP_RESET (1 << 0) // reset hdcp + #define v_HDCP_AUTH_START(n) (n << 7) + #define v_HDCP_BKSV_PASS(n) (n << 6) + #define v_HDCP_BKSV_FAILED(n) (n << 5) + #define v_HDCP_FRAMED_ENCRYPED(n) (n << 4) + #define v_HDCP_AUTH_STOP(n) (n << 3) + #define v_HDCP_ADV_CIPHER(n) (n << 2) + #define v_HDMI_DVI(n) (n << 1) + #define v_HDCP_RESET(n) (n << 0) +#define HDCP_CTRL2 0x340 + +/* HDCP Key Memory Access Control */ +#define HDCP_KEY_ACCESS_CTRL1 0x338 +#define HDCP_KEY_ACCESS_CTRL2 0x33c + #define m_LOAD_FACSIMILE_HDCP_KEY (1 << 1) + #define m_LOAD_HDCP_KEY (1 << 0) +/* HDCP Key Memory Control */ +#define HDCP_KEY_MEM_CTRL 0x348 + #define m_USE_KEY1 (1 << 6) + #define m_USE_KEY2 (1 << 5) + #define m_LOAD_AKSV (1 << 4) + #define m_KSV_SELECTED (1 << 3) + #define m_KSV_VALID (1 << 2) + #define m_KEY_VALID (1 << 1) + #define m_KEY_READY (1 << 0) + #define v_USE_KEY1(n) (n << 6) + #define v_USE_KEY2(n) (n << 5) + #define v_LOAD_AKSV(n) (n << 4) + +/* HDCP B device capacity */ +#define HDCP_BCAPS 0x2f8 + #define m_HDMI_RECEIVED (1 << 7) //If HDCP receiver support HDMI, this bit must be 1. + #define m_REPEATER (1 << 6) + #define m_KSV_FIFO_READY (1 << 5) + #define m_DDC_FAST (1 << 4) + #define m_1_1_FEATURE (1 << 1) + #define m_FAST_REAUTHENTICATION (1 << 0) //For HDMI, this function is supported whether this bit is enabled or not. + +/* HDCP KSV Value */ +#define HDCP_KSV_BYTE0 0x2fc +#define HDCP_KSV_BYTE1 0x300 +#define HDCP_KSV_BYTE2 0x304 +#define HDCP_KSV_BYTE3 0x308 +#define HDCP_KSV_BYTE4 0x30c + +/* HDCP error status */ +#define HDCP_ERROR 0x320 + +/* HDCP 100 ms timer */ +#define HDCP_TIMER_100MS 0x324 +/* HDCP 5s timer */ +#define HDCP_TIMER_5S 0x328 + +/* HDCP Key ram address */ +#define HDCP_RAM_KEY_KSV1 0x400 +#define HDCP_RAM_KEY_KSV2 0x407 +#define HDCP_RAM_KEY_PRIVATE 0x40e +#define HDCP_KEY_LENGTH 0x13C + + +#define HDCP_ENABLE_HW_AUTH // Enable hardware authentication mode +#define HDMI_INTERANL_CLK_DIV 0x19 + +#define HDMIRdReg(addr) __raw_readl(hdmi->regbase + addr) +#define HDMIWrReg(addr, val) __raw_writel((val), hdmi->regbase + addr); +#define HDMIMskReg(temp, addr, msk, val) \ + temp = __raw_readl(hdmi->regbase + addr) & (0xFF - (msk)) ; \ + __raw_writel(temp | ( (val) & (msk) ), hdmi->regbase + addr); + + + +/* Color Space Convertion Mode */ +enum { + CSC_RGB_0_255_TO_ITU601_16_235 = 0, //RGB 0-255 input to YCbCr 16-235 output according BT601 + CSC_RGB_0_255_TO_ITU709_16_235, //RGB 0-255 input to YCbCr 16-235 output accroding BT709 + CSC_ITU601_16_235_TO_RGB_16_235, //YCbCr 16-235 input to RGB 16-235 output according BT601 + CSC_ITU709_16_235_TO_RGB_16_235, //YCbCr 16-235 input to RGB 16-235 output according BT709 + CSC_ITU601_16_235_TO_RGB_0_255, //YCbCr 16-235 input to RGB 0-255 output according BT601 + CSC_ITU709_16_235_TO_RGB_0_255 //YCbCr 16-235 input to RGB 0-255 output according BT709 +}; +#endif +extern int rk2928_hdmi_initial(void); +extern int rk2928_hdmi_detect_hotplug(void); +extern int rk2928_hdmi_read_edid(int block, unsigned char *buff); +extern int rk2928_hdmi_removed(void); +extern int rk2928_hdmi_config_video(struct hdmi_video_para *vpara); +extern int rk2928_hdmi_config_audio(struct hdmi_audio *audio); +extern void rk2928_hdmi_control_output(int enable); + +#endif diff --git a/drivers/video/rockchip/hdmi/chips/rk30/Kconfig b/drivers/video/rockchip/hdmi/chips/rk30/Kconfig new file mode 100755 index 000000000000..c33318d43622 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/Kconfig @@ -0,0 +1,15 @@ +config HDMI_RK30_CTL_CODEC + bool "Mute Codec When HDMI Actived" + depends on HDMI_RK30 + default n + help + If you say y heres, Codec will be mute when hdmi inserted, + and unmute when removed. + +config HDMI_RK30_DEBUG + bool "RK30 HDMI Debugging" + depends on HDMI_RK30 && LCDC_RK30 + default n + help + Enableds verbose debugging the the HDMI drivers + diff --git a/drivers/video/rockchip/hdmi/chips/rk30/Makefile b/drivers/video/rockchip/hdmi/chips/rk30/Makefile new file mode 100755 index 000000000000..98dfae276674 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for HDMI linux kernel module. +# + +ccflags-$(CONFIG_HDMI_RK30_DEBUG) = -DDEBUG -DHDMI_DEBUG + +obj-$(CONFIG_HDMI_RK30) += rk30_hdmi_hw.o rk30_hdmi.o +obj-$(CONFIG_HDCP_RK30) += hdcp/ diff --git a/drivers/video/rockchip/hdmi/chips/rk30/hdcp/Kconfig b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/Kconfig new file mode 100755 index 000000000000..1bb3c3a24f56 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/Kconfig @@ -0,0 +1,14 @@ +config HDCP_RK30 + bool "RK30 HDCP support" + depends on LCDC_RK30 && HDMI_RK30 + default n + help + HDCP Interface. This adds the High Definition Content Protection Interface. + See http://www.digital-cp.com/ for HDCP specification. + +config HDCP_RK30_DEBUG + bool "RK30 HDCP Debugging" + depends on HDCP_RK30 + default n + help + Enableds verbose debugging the the HDCP drivers diff --git a/drivers/video/rockchip/hdmi/chips/rk30/hdcp/Makefile b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/Makefile new file mode 100755 index 000000000000..108b67ca134e --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for HDCP linux kernel module. +# + +ccflags-$(CONFIG_HDCP_RK30_DEBUG) = -DDEBUG -DHDCP_DEBUG + +obj-$(CONFIG_HDCP_RK30) += hdcp.o +hdcp-y := rk30_hdcp.o rk30_hdmi_hdcp.o diff --git a/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdcp.c b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdcp.c new file mode 100755 index 000000000000..68d0ac841a67 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdcp.c @@ -0,0 +1,570 @@ +#include +#include +#include +#include +#include +#include +#include +#include "../rk30_hdmi.h" +#include "../rk30_hdmi_hw.h" +#include "rk30_hdmi_hdcp.h" + +struct hdcp *hdcp = NULL; + +static void hdcp_work_queue(struct work_struct *work); + +/*----------------------------------------------------------------------------- + * Function: hdcp_submit_work + *----------------------------------------------------------------------------- + */ +static struct delayed_work *hdcp_submit_work(int event, int delay) +{ + struct hdcp_delayed_work *work; + + DBG("%s event %04x delay %d", __FUNCTION__, event, delay); + + work = kmalloc(sizeof(struct hdcp_delayed_work), GFP_ATOMIC); + + if (work) { + INIT_DELAYED_WORK(&work->work, hdcp_work_queue); + work->event = event; + queue_delayed_work(hdcp->workqueue, + &work->work, + msecs_to_jiffies(delay)); + } else { + printk(KERN_WARNING "HDCP: Cannot allocate memory to " + "create work\n"); + return 0; + } + + return &work->work; +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_cancel_work + *----------------------------------------------------------------------------- + */ +static void hdcp_cancel_work(struct delayed_work **work) +{ + int ret = 0; + + if (*work) { + ret = cancel_delayed_work(*work); + if (ret != 1) { + ret = cancel_work_sync(&((*work)->work)); + printk(KERN_INFO "Canceling work failed - " + "cancel_work_sync done %d\n", ret); + } + kfree(*work); + *work = 0; + } +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_authentication_failure + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_authentication_failure(void) +{ + if (hdcp->hdmi_state == HDMI_STOPPED) { + return; + } + + rk30_hdcp_disable(); + rk30_hdmi_control_output(false); + + hdcp_cancel_work(&hdcp->pending_wq_event); + + if (hdcp->retry_cnt && (hdcp->hdmi_state != HDMI_STOPPED)) { + if (hdcp->retry_cnt < HDCP_INFINITE_REAUTH) { + hdcp->retry_cnt--; + printk(KERN_INFO "HDCP: authentication failed - " + "retrying, attempts=%d\n", + hdcp->retry_cnt); + } else + printk(KERN_INFO "HDCP: authentication failed - " + "retrying\n"); + + hdcp->hdcp_state = HDCP_AUTHENTICATION_START; + + hdcp->pending_wq_event = hdcp_submit_work(HDCP_AUTH_REATT_EVENT, + HDCP_REAUTH_DELAY); + } else { + printk(KERN_INFO "HDCP: authentication failed - " + "HDCP disabled\n"); + hdcp->hdcp_state = HDCP_ENABLE_PENDING; + } + +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_start_authentication + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_start_authentication(void) +{ + int status = HDCP_OK; + + hdcp->hdcp_state = HDCP_AUTHENTICATION_START; + + DBG("HDCP: authentication start"); + + status = rk30_hdcp_start_authentication(); + + if (status != HDCP_OK) { + DBG("HDCP: authentication failed"); + hdcp_wq_authentication_failure(); + } else { + hdcp->hdcp_state = HDCP_WAIT_KSV_LIST; + } +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_check_bksv + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_check_bksv(void) +{ + int status = HDCP_OK; + + DBG("Check BKSV start"); + + status = rk30_hdcp_check_bksv(); + + if (status != HDCP_OK) { + printk(KERN_INFO "HDCP: Check BKSV failed"); + hdcp->retry_cnt = 0; + hdcp_wq_authentication_failure(); + } + else { + DBG("HDCP: Check BKSV successful"); + + hdcp->hdcp_state = HDCP_LINK_INTEGRITY_CHECK; + + /* Restore retry counter */ + if(hdcp->retry_times == 0) + hdcp->retry_cnt = HDCP_INFINITE_REAUTH; + else + hdcp->retry_cnt = hdcp->retry_times; + } +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_authentication_sucess + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_authentication_sucess(void) +{ + printk(KERN_INFO "HDCP: authentication pass"); + rk30_hdmi_control_output(true); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_wq_disable + *----------------------------------------------------------------------------- + */ +static void hdcp_wq_disable(int event) +{ + printk(KERN_INFO "HDCP: disabled"); + + hdcp_cancel_work(&hdcp->pending_wq_event); + rk30_hdcp_disable(); + if(event == HDCP_DISABLE_CTL) { + hdcp->hdcp_state = HDCP_DISABLED; + if(hdcp->hdmi_state == HDMI_STARTED) + rk30_hdmi_control_output(true); + } + else if(event == HDCP_STOP_FRAME_EVENT) + hdcp->hdcp_state = HDCP_ENABLE_PENDING; +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_work_queue + *----------------------------------------------------------------------------- + */ +static void hdcp_work_queue(struct work_struct *work) +{ + struct hdcp_delayed_work *hdcp_w = + container_of(work, struct hdcp_delayed_work, work.work); + int event = hdcp_w->event; + + mutex_lock(&hdcp->lock); + + DBG("hdcp_work_queue() - START - %u hdmi=%d hdcp=%d evt= %x %d", + jiffies_to_msecs(jiffies), + hdcp->hdmi_state, + hdcp->hdcp_state, + (event & 0xFF00) >> 8, + event & 0xFF); + + if(event == HDCP_STOP_FRAME_EVENT) { + hdcp->hdmi_state = HDMI_STOPPED; + } + + if (event == HDCP_DISABLE_CTL || event == HDCP_STOP_FRAME_EVENT) { + hdcp_wq_disable(event); + } + + if (event & HDCP_WORKQUEUE_SRC) + hdcp->pending_wq_event = 0; + + /* First handle HDMI state */ + if (event == HDCP_START_FRAME_EVENT) { + hdcp->pending_start = 0; + hdcp->hdmi_state = HDMI_STARTED; + } + + /**********************/ + /* HDCP state machine */ + /**********************/ + switch (hdcp->hdcp_state) { + case HDCP_DISABLED: + /* HDCP enable control or re-authentication event */ + if (event == HDCP_ENABLE_CTL) { + if(hdcp->retry_times == 0) + hdcp->retry_cnt = HDCP_INFINITE_REAUTH; + else + hdcp->retry_cnt = hdcp->retry_times; + if (hdcp->hdmi_state == HDMI_STARTED) + hdcp_wq_start_authentication(); + else + hdcp->hdcp_state = HDCP_ENABLE_PENDING; + } + break; + + case HDCP_ENABLE_PENDING: + /* HDMI start frame event */ + if (event == HDCP_START_FRAME_EVENT) + hdcp_wq_start_authentication(); + + break; + + case HDCP_AUTHENTICATION_START: + /* Re-authentication */ + if (event == HDCP_AUTH_REATT_EVENT) + hdcp_wq_start_authentication(); + + break; + + case HDCP_WAIT_KSV_LIST: + /* KSV failure */ + if (event == HDCP_FAIL_EVENT) { + printk(KERN_INFO "HDCP: KSV switch failure\n"); + + hdcp_wq_authentication_failure(); + } + /* KSV list ready event */ + else if (event == HDCP_KSV_LIST_RDY_EVENT) + hdcp_wq_check_bksv(); + break; + + case HDCP_LINK_INTEGRITY_CHECK: + /* Ri failure */ + if (event == HDCP_FAIL_EVENT) { + printk(KERN_INFO "HDCP: Ri check failure\n"); + hdcp_wq_authentication_failure(); + } + else if(event == HDCP_AUTH_PASS_EVENT) + hdcp_wq_authentication_sucess(); + break; + + default: + printk(KERN_WARNING "HDCP: error - unknow HDCP state\n"); + break; + } + + kfree(hdcp_w); + if(event == HDCP_STOP_FRAME_EVENT) + complete(&hdcp->complete); + + mutex_unlock(&hdcp->lock); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_start_frame_cb + *----------------------------------------------------------------------------- + */ +static void hdcp_start_frame_cb(void) +{ + DBG("hdcp_start_frame_cb()"); + + /* Cancel any pending work */ + if (hdcp->pending_start) + hdcp_cancel_work(&hdcp->pending_start); + if (hdcp->pending_wq_event) + hdcp_cancel_work(&hdcp->pending_wq_event); + + hdcp->pending_start = hdcp_submit_work(HDCP_START_FRAME_EVENT, + HDCP_ENABLE_DELAY); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_irq_cb + *----------------------------------------------------------------------------- + */ +static void hdcp_irq_cb(int interrupt) +{ + int value; + DBG("%s 0x%x", __FUNCTION__, interrupt); + if(interrupt & m_INT_HDCP_ERR) + { + value = HDMIRdReg(HDCP_ERROR); + HDMIWrReg(HDCP_ERROR, value); + printk(KERN_INFO "HDCP: Error 0x%02x\n", value); + + if( (hdcp->hdcp_state != HDCP_DISABLED) && + (hdcp->hdcp_state != HDCP_ENABLE_PENDING) ) + { + hdcp_submit_work(HDCP_FAIL_EVENT, 0); + } + } + else if(interrupt & (m_INT_BKSV_RPRDY | m_INT_BKSV_RCRDY)) + hdcp_submit_work(HDCP_KSV_LIST_RDY_EVENT, 0); + else if(interrupt & m_INT_AUTH_DONE) + hdcp_submit_work(HDCP_AUTH_PASS_EVENT, 0); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_power_on_cb + *----------------------------------------------------------------------------- + */ +static int hdcp_power_on_cb(void) +{ + DBG("%s", __FUNCTION__); + return rk30_hdcp_load_key2mem(hdcp->keys); +} + +/*----------------------------------------------------------------------------- + * Function: hdcp_power_off_cb + *----------------------------------------------------------------------------- + */ +static void hdcp_power_off_cb(void) +{ + DBG("%s", __FUNCTION__); + if(!hdcp->enable) + return; + + hdcp_cancel_work(&hdcp->pending_start); + hdcp_cancel_work(&hdcp->pending_wq_event); + init_completion(&hdcp->complete); + /* Post event to workqueue */ + if (hdcp_submit_work(HDCP_STOP_FRAME_EVENT, 0)) + wait_for_completion_interruptible_timeout(&hdcp->complete, + msecs_to_jiffies(2000)); +} + +// Load HDCP key to external HDCP memory +static void hdcp_load_keys_cb(const struct firmware *fw, void *context) +{ + if (!fw) { + pr_err("HDCP: failed to load keys\n"); + return; + } + + if(fw->size < HDCP_KEY_SIZE) { + pr_err("HDCP: firmware wrong size %d\n", fw->size); + return; + } + + hdcp->keys = kmalloc(HDCP_KEY_SIZE, GFP_KERNEL); + if(hdcp->keys == NULL) { + pr_err("HDCP: can't allocated space for keys\n"); + return; + } + + memcpy(hdcp->keys, fw->data, HDCP_KEY_SIZE); + + rk30_hdcp_load_key2mem(hdcp->keys); + printk(KERN_INFO "HDCP: loaded hdcp key success\n"); + + if(fw->size > HDCP_KEY_SIZE) { + DBG("%s invalid key size %d", __FUNCTION__, fw->size - HDCP_KEY_SIZE); + if((fw->size - HDCP_KEY_SIZE) % 5) { + pr_err("HDCP: failed to load invalid keys\n"); + return; + } + hdcp->invalidkeys = kmalloc(fw->size - HDCP_KEY_SIZE, GFP_KERNEL); + if(hdcp->invalidkeys == NULL) { + pr_err("HDCP: can't allocated space for invalid keys\n"); + return; + } + memcpy(hdcp->invalidkeys, fw->data + HDCP_KEY_SIZE, fw->size - HDCP_KEY_SIZE); + hdcp->invalidkey = (fw->size - HDCP_KEY_SIZE)/5; + printk(KERN_INFO "HDCP: loaded hdcp invalid key success\n"); + } +} + +static ssize_t hdcp_enable_read(struct device *device, + struct device_attribute *attr, char *buf) +{ + int enable = 0; + + if(hdcp) + enable = hdcp->enable; + + return snprintf(buf, PAGE_SIZE, "%d\n", enable); +} + +static ssize_t hdcp_enable_write(struct device *device, + struct device_attribute *attr, const char *buf, size_t count) +{ + int enable; + + if(hdcp == NULL) + return -EINVAL; + + sscanf(buf, "%d", &enable); + if(hdcp->enable != enable) + { + /* Post event to workqueue */ + if(enable) { + if (hdcp_submit_work(HDCP_ENABLE_CTL, 0) == 0) + return -EFAULT; + } + else { + hdcp_cancel_work(&hdcp->pending_start); + hdcp_cancel_work(&hdcp->pending_wq_event); + + /* Post event to workqueue */ + if (hdcp_submit_work(HDCP_DISABLE_CTL, 0) == 0) + return -EFAULT; + } + hdcp->enable = enable; + } + return count; +} + +static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR, hdcp_enable_read, hdcp_enable_write); + +static ssize_t hdcp_trytimes_read(struct device *device, + struct device_attribute *attr, char *buf) +{ + int trytimes = 0; + + if(hdcp) + trytimes = hdcp->retry_times; + + return snprintf(buf, PAGE_SIZE, "%d\n", trytimes); +} + +static ssize_t hdcp_trytimes_wrtie(struct device *device, + struct device_attribute *attr, const char *buf, size_t count) +{ + int trytimes; + + if(hdcp == NULL) + return -EINVAL; + + sscanf(buf, "%d", &trytimes); + if(hdcp->retry_times != trytimes) + hdcp->retry_times = trytimes; + + return count; +} + + +static DEVICE_ATTR(trytimes, S_IRUGO|S_IWUSR, hdcp_trytimes_read, hdcp_trytimes_wrtie); + + +static struct miscdevice mdev; + +static int __init rk30_hdcp_init(void) +{ + int ret; + + DBG("[%s] %u", __FUNCTION__, jiffies_to_msecs(jiffies)); + + hdcp = kmalloc(sizeof(struct hdcp), GFP_KERNEL); + if(!hdcp) + { + printk(KERN_ERR ">>HDCP: kmalloc fail!"); + ret = -ENOMEM; + goto error0; + } + memset(hdcp, 0, sizeof(struct hdcp)); + mutex_init(&hdcp->lock); + + mdev.minor = MISC_DYNAMIC_MINOR; + mdev.name = "hdcp"; + mdev.mode = 0666; + if (misc_register(&mdev)) { + printk(KERN_ERR "HDCP: Could not add character driver\n"); + ret = HDMI_ERROR_FALSE; + goto error1; + } + ret = device_create_file(mdev.this_device, &dev_attr_enable); + if(ret) + { + printk(KERN_ERR "HDCP: Could not add sys file enable\n"); + ret = -EINVAL; + goto error2; + } + + ret = device_create_file(mdev.this_device, &dev_attr_trytimes); + if(ret) + { + printk(KERN_ERR "HDCP: Could not add sys file trytimes\n"); + ret = -EINVAL; + goto error3; + } + + hdcp->workqueue = create_singlethread_workqueue("hdcp"); + if (hdcp->workqueue == NULL) { + printk(KERN_ERR "HDCP,: create workqueue failed.\n"); + goto error4; + } + + + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, + "hdcp.keys", mdev.this_device, GFP_KERNEL, + hdcp, hdcp_load_keys_cb); + if (ret < 0) { + printk(KERN_ERR "HDCP: request_firmware_nowait failed: %d\n", ret); + goto error5; + } + + rk30_hdmi_register_hdcp_callbacks( hdcp_start_frame_cb, + hdcp_irq_cb, + hdcp_power_on_cb, + hdcp_power_off_cb); + + DBG("%s success %u", __FUNCTION__, jiffies_to_msecs(jiffies)); + return 0; + +error5: + destroy_workqueue(hdcp->workqueue); +error4: + device_remove_file(mdev.this_device, &dev_attr_trytimes); +error3: + device_remove_file(mdev.this_device, &dev_attr_enable); +error2: + misc_deregister(&mdev); +error1: + if(hdcp->keys) + kfree(hdcp->keys); + if(hdcp->invalidkeys) + kfree(hdcp->invalidkeys); + kfree(hdcp); +error0: + return ret; +} + +static void __exit rk30_hdcp_exit(void) +{ + if(hdcp) { + mutex_lock(&hdcp->lock); + rk30_hdmi_register_hdcp_callbacks(0, 0, 0, 0); + device_remove_file(mdev.this_device, &dev_attr_enable); + misc_deregister(&mdev); + destroy_workqueue(hdcp->workqueue); + if(hdcp->keys) + kfree(hdcp->keys); + if(hdcp->invalidkeys) + kfree(hdcp->invalidkeys); + mutex_unlock(&hdcp->lock); + kfree(hdcp); + } +} + +module_init(rk30_hdcp_init); +module_exit(rk30_hdcp_exit); \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.c b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.c new file mode 100755 index 000000000000..1184989a57b2 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.c @@ -0,0 +1,157 @@ +#include +#include +#include +#include "../rk30_hdmi.h" +#include "../rk30_hdmi_hw.h" +#include "rk30_hdmi_hdcp.h" + +static void rk30_hdcp_write_mem(int addr_8, char value) +{ + int temp; + int addr_32 = addr_8 - addr_8%4; + int shift = (addr_8%4) * 8; + + temp = HDMIRdReg(addr_32); + temp &= ~(0xff << shift); + temp |= value << shift; +// printk("temp is %08x\n", temp); + HDMIWrReg(addr_32, temp); +} + +int rk30_hdcp_load_key2mem(struct hdcp_keys *key) +{ + int i; + + if(key == NULL) return HDMI_ERROR_FALSE; + + HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV); + + for(i = 0; i < 7; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_KSV1 + i, key->KSV[i]); + for(i = 0; i < 7; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_KSV2 + i, key->KSV[i]); + for(i = 0; i < HDCP_PRIVATE_KEY_SIZE; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_PRIVATE + i, key->DeviceKey[i]); + for(i = 0; i < HDCP_KEY_SHA_SIZE; i++) + rk30_hdcp_write_mem(HDCP_RAM_KEY_PRIVATE + HDCP_PRIVATE_KEY_SIZE + i, key->sha1[i]); + + HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV | 0x20); + return HDCP_OK; +} + +void rk30_hdcp_disable(void) +{ + int temp; + // Diable HDCP Interrupt + HDMIWrReg(INTR_MASK2, 0x00); + // Stop and Reset HDCP + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_FRAMED_ENCRYPED | m_HDCP_AUTH_STOP | m_HDCP_RESET, + v_HDCP_FRAMED_ENCRYPED(0) | v_HDCP_AUTH_STOP(1) | v_HDCP_RESET(1) ); +} + +static int rk30_hdcp_load_key(void) +{ + int value, temp = 0; + + if(hdcp->keys == NULL) { + pr_err("[%s] HDCP key not loaded.\n", __FUNCTION__); + return HDCP_KEY_ERR; + } + + value = HDMIRdReg(HDCP_KEY_MEM_CTRL); + //Check HDCP key loaded from external HDCP memory + while((value & (m_KSV_VALID | m_KEY_VALID | m_KEY_READY)) != (m_KSV_VALID | m_KEY_VALID | m_KEY_READY)) { + if(temp > 10) { + pr_err("[%s] loaded hdcp key is incorrectable %02x\n", __FUNCTION__, value & 0xFF); + return HDCP_KEY_ERR; + } + //Load HDCP Key from external HDCP memory + HDMIWrReg(HDCP_KEY_ACCESS_CTRL2, m_LOAD_HDCP_KEY); + msleep(1); + value = HDMIRdReg(HDCP_KEY_MEM_CTRL); + temp++; + } + + return HDCP_OK; +} + + +int rk30_hdcp_start_authentication(void) +{ + int rc, temp; + + rc = rk30_hdcp_load_key(); + if(rc != HDCP_OK) + return rc; + + // Set 100ms & 5 sec timer + switch(hdmi->vic) + { + case HDMI_720x576p_50Hz_4_3: + case HDMI_720x576p_50Hz_16_9: + case HDMI_1280x720p_50Hz: + case HDMI_1920x1080i_50Hz: + case HDMI_720x576i_50Hz_4_3: + case HDMI_720x576i_50Hz_16_9: + case HDMI_1920x1080p_50Hz: + HDMIWrReg(HDCP_TIMER_100MS, 5); + HDMIWrReg(HDCP_TIMER_5S, 250); + break; + + default: + HDMIWrReg(HDCP_TIMER_100MS, 0x26); + HDMIWrReg(HDCP_TIMER_5S, 0x2c); + break; + } + // Config DDC Clock + temp = (hdmi->tmdsclk/HDCP_DDC_CLK)/4; + HDMIWrReg(DDC_BUS_FREQ_L, temp & 0xFF); + HDMIWrReg(DDC_BUS_FREQ_H, (temp >> 8) & 0xFF); + // Enable HDCP Interrupt + HDMIWrReg(INTR_MASK2, m_INT_HDCP_ERR | m_INT_BKSV_RPRDY | m_INT_BKSV_RCRDY | m_INT_AUTH_DONE | m_INT_AUTH_READY); + // Start HDCP + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_AUTH_START | m_HDCP_FRAMED_ENCRYPED, v_HDCP_AUTH_START(1) | v_HDCP_FRAMED_ENCRYPED(0)); + + return HDCP_OK; +} + +int rk30_hdcp_check_bksv(void) +{ + int i, temp; + char bksv[5]; + char *invalidkey; + + temp = HDMIRdReg(HDCP_BCAPS); + DBG("Receiver capacity is 0x%02x", temp); + +#ifdef DEBUG + if(temp & m_HDMI_RECEIVED) + DBG("Receiver support HDMI"); + if(temp & m_REPEATER) + DBG("Receiver is a repeater"); + if(temp & m_DDC_FAST) + DBG("Receiver support 400K DDC"); + if(temp & m_1_1_FEATURE) + DBG("Receiver support 1.1 features, such as advanced cipher, EESS."); + if(temp & m_FAST_REAUTHENTICATION) + DBG("Receiver support fast reauthentication."); +#endif + + for(i = 0; i < 5; i++) { + bksv[i] = HDMIRdReg(HDCP_KSV_BYTE0 + (4 - i)*4) & 0xFF; + } + + DBG("bksv is 0x%02x%02x%02x%02x%02x", bksv[0], bksv[1], bksv[2], bksv[3], bksv[4]); + + for(i = 0; i < hdcp->invalidkey; i++) + { + invalidkey = hdcp->invalidkeys + i *5; + if(memcmp(bksv, invalidkey, 5) == 0) { + printk(KERN_ERR "HDCP: BKSV was revocated!!!\n"); + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_BKSV_FAILED | m_HDCP_FRAMED_ENCRYPED, v_HDCP_BKSV_FAILED(1) | v_HDCP_FRAMED_ENCRYPED(0)); + return HDCP_KSV_ERR; + } + } + HDMIMskReg(temp, HDCP_CTRL, m_HDCP_BKSV_PASS | m_HDCP_FRAMED_ENCRYPED, v_HDCP_BKSV_PASS(1) | v_HDCP_FRAMED_ENCRYPED(1)); + return HDCP_OK; +} diff --git a/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.h b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.h new file mode 100755 index 000000000000..0224d88ab134 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/hdcp/rk30_hdmi_hdcp.h @@ -0,0 +1,99 @@ +#ifndef __RK30_HDMI_HDCP_H__ +#define __RK30_HDMI_HDCP_H__ + +/***************************/ +/* Definitions */ +/***************************/ + +/* Status / error codes */ +#define HDCP_OK 0 +#define HDCP_KEY_ERR 1 +#define HDCP_KSV_ERR 2 + +/* Delays */ +#define HDCP_ENABLE_DELAY 300 +#define HDCP_REAUTH_DELAY 100 + +/* Event source */ +#define HDCP_SRC_SHIFT 8 +#define HDCP_IOCTL_SRC (0x1 << HDCP_SRC_SHIFT) +#define HDCP_HDMI_SRC (0x2 << HDCP_SRC_SHIFT) +#define HDCP_IRQ_SRC (0x4 << HDCP_SRC_SHIFT) +#define HDCP_WORKQUEUE_SRC (0x8 << HDCP_SRC_SHIFT) + +/* Event */ +#define HDCP_ENABLE_CTL (HDCP_IOCTL_SRC | 0) +#define HDCP_DISABLE_CTL (HDCP_IOCTL_SRC | 1) +#define HDCP_START_FRAME_EVENT (HDCP_HDMI_SRC | 2) +#define HDCP_STOP_FRAME_EVENT (HDCP_HDMI_SRC | 3) +#define HDCP_KSV_LIST_RDY_EVENT (HDCP_IRQ_SRC | 4) +#define HDCP_FAIL_EVENT (HDCP_IRQ_SRC | 5) +#define HDCP_AUTH_PASS_EVENT (HDCP_IRQ_SRC | 6) +#define HDCP_AUTH_REATT_EVENT (HDCP_WORKQUEUE_SRC | 7) + +/* Key size */ +#define HDCP_KEY_SIZE 308 + +/* Authentication retry times */ +#define HDCP_INFINITE_REAUTH 0x100 + +enum hdcp_states { + HDCP_DISABLED, + HDCP_ENABLE_PENDING, + HDCP_AUTHENTICATION_START, + HDCP_WAIT_KSV_LIST, + HDCP_LINK_INTEGRITY_CHECK, +}; + +enum hdmi_states { + HDMI_STOPPED, + HDMI_STARTED +}; + +#define HDCP_PRIVATE_KEY_SIZE 280 +#define HDCP_KEY_SHA_SIZE 20 +#define HDCP_DDC_CLK 100000 + +struct hdcp_keys{ + u8 KSV[8]; + u8 DeviceKey[HDCP_PRIVATE_KEY_SIZE]; + u8 sha1[HDCP_KEY_SHA_SIZE]; +}; + +struct hdcp_delayed_work { + struct delayed_work work; + int event; +}; + +struct hdcp { + int enable; + int retry_times; + struct hdcp_keys *keys; + int invalidkey; + char *invalidkeys; + struct mutex lock; + struct completion complete; + struct workqueue_struct *workqueue; + + enum hdmi_states hdmi_state; + enum hdcp_states hdcp_state; + + struct delayed_work *pending_start; + struct delayed_work *pending_wq_event; + int retry_cnt; +}; + +extern struct hdcp *hdcp; + +#ifdef HDCP_DEBUG +#define DBG(format, ...) \ + printk(KERN_INFO "HDCP: " format "\n", ## __VA_ARGS__) +#else +#define DBG(format, ...) +#endif + +extern void rk30_hdcp_disable(void); +extern int rk30_hdcp_start_authentication(void); +extern int rk30_hdcp_check_bksv(void); +extern int rk30_hdcp_load_key2mem(struct hdcp_keys *key); +#endif /* __RK30_HDMI_HDCP_H__ */ \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.c b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.c new file mode 100755 index 000000000000..0aae519acc9e --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.c @@ -0,0 +1,299 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "rk30_hdmi.h" +#include "rk30_hdmi_hw.h" + +struct hdmi *hdmi = NULL; + +extern irqreturn_t hdmi_irq(int irq, void *priv); +extern void hdmi_work(struct work_struct *work); +extern struct rk_lcdc_device_driver * rk_get_lcdc_drv(char *name); +extern void hdmi_register_display_sysfs(struct hdmi *hdmi, struct device *parent); +extern void hdmi_unregister_display_sysfs(struct hdmi *hdmi); + +int rk30_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void), + void (*hdcp_irq_cb)(int status), + int (*hdcp_power_on_cb)(void), + void (*hdcp_power_off_cb)(void)) +{ + if(hdmi == NULL) + return HDMI_ERROR_FALSE; + + hdmi->hdcp_cb = hdcp_cb; + hdmi->hdcp_irq_cb = hdcp_irq_cb; + hdmi->hdcp_power_on_cb = hdcp_power_on_cb; + hdmi->hdcp_power_off_cb = hdcp_power_off_cb; + + return HDMI_ERROR_SUCESS; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void hdmi_early_suspend(struct early_suspend *h) +{ + hdmi_dbg(hdmi->dev, "hdmi enter early suspend pwr %d state %d\n", hdmi->pwr_mode, hdmi->state); + flush_delayed_work(&hdmi->delay_work); + mutex_lock(&hdmi->enable_mutex); + hdmi->suspend = 1; + if(!hdmi->enable) { + mutex_unlock(&hdmi->enable_mutex); + return; + } + disable_irq(hdmi->irq); + mutex_unlock(&hdmi->enable_mutex); + hdmi->command = HDMI_CONFIG_ENABLE; + init_completion(&hdmi->complete); + hdmi->wait = 1; + queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0); + wait_for_completion_interruptible_timeout(&hdmi->complete, + msecs_to_jiffies(5000)); + flush_delayed_work(&hdmi->delay_work); + // 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; +} + +static void hdmi_early_resume(struct early_suspend *h) +{ + hdmi_dbg(hdmi->dev, "hdmi exit early resume\n"); + mutex_lock(&hdmi->enable_mutex); + + rk30_mux_api_set(GPIO0A2_HDMII2CSDA_NAME, GPIO0A_HDMI_I2C_SDA); + rk30_mux_api_set(GPIO0A1_HDMII2CSCL_NAME, GPIO0A_HDMI_I2C_SCL); + + hdmi->suspend = 0; + rk30_hdmi_initial(); + if(hdmi->enable) { + enable_irq(hdmi->irq); + } + mutex_unlock(&hdmi->enable_mutex); + return; +} +#endif + +static inline void hdmi_io_remap(void) +{ + unsigned int value; + + // Remap HDMI IO Pin + rk30_mux_api_set(GPIO0A2_HDMII2CSDA_NAME, GPIO0A_HDMI_I2C_SDA); + rk30_mux_api_set(GPIO0A1_HDMII2CSCL_NAME, GPIO0A_HDMI_I2C_SCL); + rk30_mux_api_set(GPIO0A0_HDMIHOTPLUGIN_NAME, GPIO0A_HDMI_HOT_PLUG_IN); + + // Select LCDC0 as video source and enabled. + value = (HDMI_SOURCE_DEFAULT << 14) | (1 << 30); + writel(value, GRF_SOC_CON0 + RK30_GRF_BASE); +} + +static int __devinit rk30_hdmi_probe (struct platform_device *pdev) +{ + int ret; + struct resource *res; + struct resource *mem; + + hdmi = kmalloc(sizeof(struct hdmi), GFP_KERNEL); + if(!hdmi) + { + dev_err(&pdev->dev, ">>rk30 hdmi kmalloc fail!"); + return -ENOMEM; + } + memset(hdmi, 0, sizeof(struct hdmi)); + hdmi->dev = &pdev->dev; + platform_set_drvdata(pdev, hdmi); + + if(HDMI_SOURCE_DEFAULT == HDMI_SOURCE_LCDC0) + hdmi->lcdc = rk_get_lcdc_drv("lcdc0"); + else + hdmi->lcdc = rk_get_lcdc_drv("lcdc1"); + if(hdmi->lcdc == NULL) + { + dev_err(hdmi->dev, "can not connect to video source lcdc\n"); + ret = -ENXIO; + goto err0; + } + hdmi->xscale = 95; + hdmi->yscale = 95; + + hdmi->hclk = clk_get(NULL,"hclk_hdmi"); + if(IS_ERR(hdmi->hclk)) + { + dev_err(hdmi->dev, "Unable to get hdmi hclk\n"); + ret = -ENXIO; + goto err0; + } + clk_enable(hdmi->hclk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(hdmi->dev, "Unable to get register resource\n"); + ret = -ENXIO; + goto err0; + } + hdmi->regbase_phy = res->start; + hdmi->regsize_phy = (res->end - res->start) + 1; + mem = request_mem_region(res->start, (res->end - res->start) + 1, pdev->name); + if (!mem) + { + dev_err(hdmi->dev, "failed to request mem region for hdmi\n"); + ret = -ENOENT; + goto err0; + } + + + hdmi->regbase = (int)ioremap(res->start, (res->end - res->start) + 1); + if (!hdmi->regbase) { + dev_err(hdmi->dev, "cannot ioremap registers\n"); + ret = -ENXIO; + goto err1; + } + + ret = rk30_hdmi_initial(); + if(ret != HDMI_ERROR_SUCESS) + goto err1; + + hdmi_io_remap(); + hdmi_sys_init(); + + hdmi->workqueue = create_singlethread_workqueue("hdmi"); + INIT_DELAYED_WORK(&(hdmi->delay_work), hdmi_work); + + #ifdef CONFIG_HAS_EARLYSUSPEND + hdmi->early_suspend.suspend = hdmi_early_suspend; + hdmi->early_suspend.resume = hdmi_early_resume; + hdmi->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 10; + register_early_suspend(&hdmi->early_suspend); + #endif + + hdmi_register_display_sysfs(hdmi, NULL); + #ifdef CONFIG_SWITCH + hdmi->switch_hdmi.name="hdmi"; + switch_dev_register(&(hdmi->switch_hdmi)); + #endif + + spin_lock_init(&hdmi->irq_lock); + mutex_init(&hdmi->enable_mutex); + + /* get the IRQ */ + hdmi->irq = platform_get_irq(pdev, 0); + if(hdmi->irq <= 0) { + dev_err(hdmi->dev, "failed to get hdmi irq resource (%d).\n", hdmi->irq); + ret = -ENXIO; + goto err2; + } + + /* request the IRQ */ + ret = request_irq(hdmi->irq, hdmi_irq, 0, dev_name(&pdev->dev), hdmi); + if (ret) + { + dev_err(hdmi->dev, "hdmi request_irq failed (%d).\n", ret); + goto err2; + } + + hdmi_dbg(hdmi->dev, "rk30 hdmi probe sucess.\n"); + return 0; +err2: + #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 + iounmap((void*)hdmi->regbase); +err1: + release_mem_region(res->start,(res->end - res->start) + 1); + clk_disable(hdmi->hclk); +err0: + hdmi_dbg(hdmi->dev, "rk30 hdmi probe error.\n"); + kfree(hdmi); + hdmi = NULL; + return ret; +} + +static int __devexit rk30_hdmi_remove(struct platform_device *pdev) +{ + if(hdmi) { + mutex_lock(&hdmi->enable_mutex); + if(!hdmi->suspend && hdmi->enable) + disable_irq(hdmi->irq); + mutex_unlock(&hdmi->enable_mutex); + free_irq(hdmi->irq, NULL); + flush_workqueue(hdmi->workqueue); + destroy_workqueue(hdmi->workqueue); + #ifdef CONFIG_SWITCH + switch_dev_unregister(&(hdmi->switch_hdmi)); + #endif + hdmi_unregister_display_sysfs(hdmi); + #ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&hdmi->early_suspend); + #endif + iounmap((void*)hdmi->regbase); + release_mem_region(hdmi->regbase_phy, hdmi->regsize_phy); + clk_disable(hdmi->hclk); + fb_destroy_modelist(&hdmi->edid.modelist); + if(hdmi->edid.audio) + kfree(hdmi->edid.audio); + if(hdmi->edid.specs) + { + if(hdmi->edid.specs->modedb) + kfree(hdmi->edid.specs->modedb); + kfree(hdmi->edid.specs); + } + kfree(hdmi); + hdmi = NULL; + } + printk(KERN_INFO "rk30 hdmi removed.\n"); + return 0; +} + +static void rk30_hdmi_shutdown(struct platform_device *pdev) +{ + if(hdmi) { + #ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&hdmi->early_suspend); + #endif + } + printk(KERN_INFO "rk30 hdmi shut down.\n"); +} + +static struct platform_driver rk30_hdmi_driver = { + .probe = rk30_hdmi_probe, + .remove = __devexit_p(rk30_hdmi_remove), + .driver = { + .name = "rk30-hdmi", + .owner = THIS_MODULE, + }, + .shutdown = rk30_hdmi_shutdown, +}; + +static int __init rk30_hdmi_init(void) +{ + return platform_driver_register(&rk30_hdmi_driver); +} + +static void __exit rk30_hdmi_exit(void) +{ + platform_driver_unregister(&rk30_hdmi_driver); +} + + +//fs_initcall(rk30_hdmi_init); +module_init(rk30_hdmi_init); +module_exit(rk30_hdmi_exit); diff --git a/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.h b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.h new file mode 100755 index 000000000000..4695086bf329 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi.h @@ -0,0 +1,11 @@ +#ifndef __RK30_HDMI_H__ +#define __RK30_HDMI_H__ + +#include "../../rk_hdmi.h" + + +extern int rk30_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void), + void (*hdcp_irq_cb)(int status), + int (*hdcp_power_on_cb)(void), + void (*hdcp_power_off_cb)(void)); +#endif /* __RK30_HDMI_H__ */ diff --git a/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi_hw.c b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi_hw.c new file mode 100755 index 000000000000..e8b5f1e74b74 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi_hw.c @@ -0,0 +1,656 @@ +#include +#include +#include +#include "rk30_hdmi.h" +#include "rk30_hdmi_hw.h" + +static char edid_result = 0; + +static inline void delay100us(void) +{ + msleep(1); +} + +int rk30_hdmi_initial(void) +{ + int rc = HDMI_ERROR_SUCESS; + + hdmi->pwr_mode = PWR_SAVE_MODE_A; + hdmi->hdmi_removed = rk30_hdmi_removed ; + hdmi->control_output = rk30_hdmi_control_output; + hdmi->config_video = rk30_hdmi_config_video; + hdmi->config_audio = rk30_hdmi_config_audio; + hdmi->detect_hotplug = rk30_hdmi_detect_hotplug; + hdmi->read_edid = rk30_hdmi_read_edid; + // internal hclk = hdmi_hclk/20 + HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV); + + if(hdmi->hdcp_power_on_cb) + rc = hdmi->hdcp_power_on_cb(); + + return rc; +} + +static void rk30_hdmi_set_pwr_mode(int mode) +{ + if(hdmi->pwr_mode == mode) + return; + hdmi_dbg(hdmi->dev, "[%s] mode %d\n", __FUNCTION__, mode); + switch(mode) + { + case PWR_SAVE_MODE_A: + HDMIWrReg(SYS_CTRL, 0x10); + break; + case PWR_SAVE_MODE_B: + HDMIWrReg(SYS_CTRL, 0x20); + break; + case PWR_SAVE_MODE_D: + // reset PLL A&B + HDMIWrReg(SYS_CTRL, 0x4C); + delay100us(); + // release PLL A reset + HDMIWrReg(SYS_CTRL, 0x48); + delay100us(); + // release PLL B reset + HDMIWrReg(SYS_CTRL, 0x40); + break; + case PWR_SAVE_MODE_E: + HDMIWrReg(SYS_CTRL, 0x80); + break; + } + hdmi->pwr_mode = mode; + if(mode != PWR_SAVE_MODE_A) + msleep(10); + hdmi_dbg(hdmi->dev, "[%s] curmode %02x\n", __FUNCTION__, HDMIRdReg(SYS_CTRL)); +} + +int rk30_hdmi_detect_hotplug(void) +{ + int value = HDMIRdReg(HPD_MENS_STA); + + hdmi_dbg(hdmi->dev, "[%s] value %02x\n", __FUNCTION__, value); + value &= m_HOTPLUG_STATUS | m_MSEN_STATUS; + if(value == (m_HOTPLUG_STATUS | m_MSEN_STATUS) ) + return HDMI_HPD_ACTIVED; + else if(value) + return HDMI_HPD_INSERT; + else + return HDMI_HPD_REMOVED; +} + +#define HDMI_EDID_DDC_CLK 90000 +int rk30_hdmi_read_edid(int block, unsigned char *buff) +{ + int value, ret = -1, ddc_bus_freq = 0; + char interrupt = 0, trytime = 2; + unsigned long flags; + + hdmi_dbg(hdmi->dev, "[%s] block %d\n", __FUNCTION__, block); + spin_lock_irqsave(&hdmi->irq_lock, flags); + edid_result = 0; + spin_unlock_irqrestore(&hdmi->irq_lock, flags); + //Before Phy parameter was set, DDC_CLK is equal to PLLA freq which is 30MHz. + //Set DDC I2C CLK which devided from DDC_CLK to 100KHz. + ddc_bus_freq = (30000000/HDMI_EDID_DDC_CLK)/4; + HDMIWrReg(DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF); + HDMIWrReg(DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF); + + // Enable edid interrupt + HDMIWrReg(INTR_MASK1, m_INT_HOTPLUG | m_INT_MSENS | m_INT_EDID_ERR | m_INT_EDID_READY); + + while(trytime--) { + // Config EDID block and segment addr + HDMIWrReg(EDID_WORD_ADDR, (block%2) * 0x80); + HDMIWrReg(EDID_SEGMENT_POINTER, block/2); + + value = 100; + while(value--) + { + spin_lock_irqsave(&hdmi->irq_lock, flags); + interrupt = edid_result; + edid_result = 0; + spin_unlock_irqrestore(&hdmi->irq_lock, flags); + if(interrupt & (m_INT_EDID_ERR | m_INT_EDID_READY)) + break; + msleep(10); + } + hdmi_dbg(hdmi->dev, "[%s] edid read value %d\n", __FUNCTION__, value); + if(interrupt & m_INT_EDID_READY) + { + for(value = 0; value < HDMI_EDID_BLOCK_SIZE; value++) + buff[value] = HDMIRdReg(DDC_READ_FIFO_ADDR); + ret = 0; + + hdmi_dbg(hdmi->dev, "[%s] edid read sucess\n", __FUNCTION__); +#ifdef HDMI_DEBUG + for(value = 0; value < 128; value++) { + printk("%02x ,", buff[value]); + if( (value + 1) % 16 == 0) + printk("\n"); + } +#endif + break; + } + if(interrupt & m_INT_EDID_ERR) + hdmi_err(hdmi->dev, "[%s] edid read error\n", __FUNCTION__); + + } + // Disable edid interrupt + HDMIWrReg(INTR_MASK1, m_INT_HOTPLUG | m_INT_MSENS); +// msleep(100); + return ret; +} + +static inline void rk30_hdmi_config_phy_reg(int reg, int value) +{ + HDMIWrReg(reg, value); + HDMIWrReg(SYS_CTRL, 0x2C); + delay100us(); + HDMIWrReg(SYS_CTRL, 0x20); + msleep(1); +} + +static void rk30_hdmi_config_phy(unsigned char vic) +{ + HDMIWrReg(DEEP_COLOR_MODE, 0x22); // tmds frequency same as input dlck + rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_B); + switch(vic) + { + case HDMI_1920x1080p_60Hz: + case HDMI_1920x1080p_50Hz: + rk30_hdmi_config_phy_reg(0x158, 0x0E); + rk30_hdmi_config_phy_reg(0x15c, 0x00); + rk30_hdmi_config_phy_reg(0x160, 0x60); + rk30_hdmi_config_phy_reg(0x164, 0x00); + rk30_hdmi_config_phy_reg(0x168, 0xDA); + rk30_hdmi_config_phy_reg(0x16c, 0xA1); + rk30_hdmi_config_phy_reg(0x170, 0x0e); + rk30_hdmi_config_phy_reg(0x174, 0x22); + rk30_hdmi_config_phy_reg(0x178, 0x00); + break; + + case HDMI_1920x1080i_60Hz: + case HDMI_1920x1080i_50Hz: + case HDMI_1280x720p_60Hz: + case HDMI_1280x720p_50Hz: + rk30_hdmi_config_phy_reg(0x158, 0x06); + rk30_hdmi_config_phy_reg(0x15c, 0x00); + rk30_hdmi_config_phy_reg(0x160, 0x60); + rk30_hdmi_config_phy_reg(0x164, 0x00); + rk30_hdmi_config_phy_reg(0x168, 0xCA); + rk30_hdmi_config_phy_reg(0x16c, 0xA3); + rk30_hdmi_config_phy_reg(0x170, 0x0e); + rk30_hdmi_config_phy_reg(0x174, 0x20); + rk30_hdmi_config_phy_reg(0x178, 0x00); + break; + + case HDMI_720x576p_50Hz_4_3: + case HDMI_720x576p_50Hz_16_9: + case HDMI_720x480p_60Hz_4_3: + case HDMI_720x480p_60Hz_16_9: + rk30_hdmi_config_phy_reg(0x158, 0x02); + rk30_hdmi_config_phy_reg(0x15c, 0x00); + rk30_hdmi_config_phy_reg(0x160, 0x60); + rk30_hdmi_config_phy_reg(0x164, 0x00); + rk30_hdmi_config_phy_reg(0x168, 0xC2); + rk30_hdmi_config_phy_reg(0x16c, 0xA2); + rk30_hdmi_config_phy_reg(0x170, 0x0e); + rk30_hdmi_config_phy_reg(0x174, 0x20); + rk30_hdmi_config_phy_reg(0x178, 0x00); + break; + default: + hdmi_err(hdmi->dev, "not support such vic %d\n", vic); + break; + } +} + +static void rk30_hdmi_config_avi(unsigned char vic, unsigned char output_color) +{ + int i, clolorimetry, aspect_ratio; + 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]; + + if(output_color == VIDEO_OUTPUT_YCBCR444) + info[4] = (AVI_COLOR_MODE_YCBCR444 << 5); + else if(output_color == VIDEO_OUTPUT_YCBCR422) + info[4] = (AVI_COLOR_MODE_YCBCR422 << 5); + else + info[4] = (AVI_COLOR_MODE_RGB << 5); + info[4] |= (1 << 4); //Enable active format data bits is present in info[2] + + switch(vic) + { + case HDMI_720x480i_60Hz_4_3: + case HDMI_720x576i_50Hz_4_3: + case HDMI_720x480p_60Hz_4_3: + case HDMI_720x576p_50Hz_4_3: + aspect_ratio = AVI_CODED_FRAME_ASPECT_4_3; + clolorimetry = AVI_COLORIMETRY_SMPTE_170M; + break; + case HDMI_720x480i_60Hz_16_9: + case HDMI_720x576i_50Hz_16_9: + case HDMI_720x480p_60Hz_16_9: + case HDMI_720x576p_50Hz_16_9: + aspect_ratio = AVI_CODED_FRAME_ASPECT_16_9; + clolorimetry = AVI_COLORIMETRY_SMPTE_170M; + break; + default: + aspect_ratio = AVI_CODED_FRAME_ASPECT_16_9; + clolorimetry = AVI_COLORIMETRY_ITU709; + } + + if(output_color == VIDEO_OUTPUT_RGB444) + clolorimetry = AVI_COLORIMETRY_NO_DATA; + + info[5] = (clolorimetry << 6) | (aspect_ratio << 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_HB0 + i*4, info[i]); +} + +static char coeff_csc[][24] = { + //G B R Bias + { //CSC_RGB_0_255_TO_ITU601_16_235 + 0x11, 0xb6, 0x02, 0x0b, 0x10, 0x55, 0x00, 0x80, //Cr + 0x02, 0x59, 0x01, 0x32, 0x00, 0x75, 0x00, 0x10, //Y + 0x11, 0x5b, 0x10, 0xb0, 0x02, 0x0b, 0x00, 0x80, //Cb + }, + { //CSC_RGB_0_255_TO_ITU709_16_235 + 0x11, 0xdb, 0x02, 0x0b, 0x10, 0x30, 0x00, 0x80, //Cr + 0x02, 0xdc, 0x00, 0xda, 0x00, 0x4a, 0x00, 0x10, //Y + 0x11, 0x93, 0x10, 0x78, 0x02, 0x0b, 0x00, 0x80, //Cb + }, + //Y Cr Cb Bias + { //CSC_ITU601_16_235_TO_RGB_16_235 + 0x04, 0x00, 0x05, 0x7c, 0x00, 0x00, 0x02, 0xaf, //R + 0x04, 0x00, 0x12, 0xcb, 0x11, 0x58, 0x00, 0x84, //G + 0x04, 0x00, 0x00, 0x00, 0x06, 0xee, 0x02, 0xde, //B + }, + { //CSC_ITU709_16_235_TO_RGB_16_235 + 0x04, 0x00, 0x06, 0x29, 0x00, 0x00, 0x02, 0xc5, //R + 0x04, 0x00, 0x11, 0xd6, 0x10, 0xbb, 0x00, 0x52, //G + 0x04, 0x00, 0x00, 0x00, 0x07, 0x44, 0x02, 0xe8, //B + }, + { //CSC_ITU601_16_235_TO_RGB_0_255 + 0x04, 0xa8, 0x05, 0x7c, 0x00, 0x00, 0x02, 0xc2, //R + 0x04, 0xa8, 0x12, 0xcb, 0x11, 0x58, 0x00, 0x72, //G + 0x04, 0xa8, 0x00, 0x00, 0x06, 0xee, 0x02, 0xf0, //B + }, + { //CSC_ITU709_16_235_TO_RGB_0_255 + 0x04, 0xa8, 0x06, 0x29, 0x00, 0x00, 0x02, 0xd8, //R + 0x04, 0xa8, 0x11, 0xd6, 0x10, 0xbb, 0x00, 0x40, //G + 0x04, 0xa8, 0x00, 0x00, 0x07, 0x44, 0x02, 0xfb, //B + }, + +}; + +static void rk30_hdmi_config_csc(struct hdmi_video_para *vpara) +{ + int i, mode; + char *coeff = NULL; + + if( ((vpara->input_color == VIDEO_INPUT_COLOR_RGB) && (vpara->output_color == VIDEO_OUTPUT_RGB444)) || + ((vpara->input_color == VIDEO_INPUT_COLOR_YCBCR) && (vpara->output_color != VIDEO_OUTPUT_RGB444) )) + { + return; + } + switch(vpara->vic) + { + case HDMI_720x480i_60Hz_4_3: + case HDMI_720x576i_50Hz_4_3: + case HDMI_720x480p_60Hz_4_3: + case HDMI_720x576p_50Hz_4_3: + case HDMI_720x480i_60Hz_16_9: + case HDMI_720x576i_50Hz_16_9: + case HDMI_720x480p_60Hz_16_9: + case HDMI_720x576p_50Hz_16_9: + if(vpara->input_color == VIDEO_INPUT_COLOR_RGB) + mode = CSC_RGB_0_255_TO_ITU601_16_235; + else if(vpara->output_mode == OUTPUT_HDMI) + mode = CSC_ITU601_16_235_TO_RGB_16_235; + else + mode = CSC_ITU601_16_235_TO_RGB_0_255; + break; + default: + if(vpara->input_color == VIDEO_INPUT_COLOR_RGB) + mode = CSC_RGB_0_255_TO_ITU709_16_235; + else if(vpara->output_mode == OUTPUT_HDMI) + mode = CSC_ITU709_16_235_TO_RGB_16_235; + else + mode = CSC_ITU709_16_235_TO_RGB_0_255; + break; + } + + coeff = coeff_csc[mode]; + + HDMIWrReg(CSC_CONFIG1, v_CSC_MODE(CSC_MODE_MANUAL) | v_CSC_BRSWAP_DIABLE(1)); + + for(i = 0; i < 24; i++) + HDMIWrReg(CSC_PARA_C0_H + i*4, coeff[i]); + + HDMIWrReg(AV_CTRL2, v_CSC_ENABLE(1)); +} + +int rk30_hdmi_config_video(struct hdmi_video_para *vpara) +{ + int value; + struct fb_videomode *mode; + + hdmi_dbg(hdmi->dev, "[%s]\n", __FUNCTION__); + if(vpara == NULL) { + hdmi_err(hdmi->dev, "[%s] input parameter error\n", __FUNCTION__); + return -1; + } + if(hdmi->pwr_mode == PWR_SAVE_MODE_E) + rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_D); + if(hdmi->pwr_mode == PWR_SAVE_MODE_D || hdmi->pwr_mode == PWR_SAVE_MODE_A) + rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_B); + + if(hdmi->hdcp_power_off_cb) + hdmi->hdcp_power_off_cb(); + + // Input video mode is RGB24bit, Data enable signal from external + HDMIMskReg(value, AV_CTRL1, m_INPUT_VIDEO_MODE | m_DE_SIGNAL_SELECT, \ + v_INPUT_VIDEO_MODE(vpara->input_mode) | EXTERNAL_DE) + HDMIMskReg(value, VIDEO_CTRL1, m_VIDEO_OUTPUT_MODE | m_VIDEO_INPUT_DEPTH | m_VIDEO_INPUT_COLOR_MODE, \ + v_VIDEO_OUTPUT_MODE(vpara->output_color) | v_VIDEO_INPUT_DEPTH(VIDEO_INPUT_DEPTH_8BIT) | vpara->input_color) + HDMIWrReg(DEEP_COLOR_MODE, 0x20); + // color space convert + rk30_hdmi_config_csc(vpara); + // Set HDMI Mode + HDMIWrReg(HDCP_CTRL, v_HDMI_DVI(vpara->output_mode)); + + // Set ext video + 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; + + if( (vpara->vic == HDMI_720x480p_60Hz_4_3) || (vpara->vic == HDMI_720x480p_60Hz_16_9) ) + value = v_VSYNC_OFFSET(6); + else + value = v_VSYNC_OFFSET(0); + value |= v_EXT_VIDEO_ENABLE(1) | v_INTERLACE(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(EXT_VIDEO_PARA, value); + value = mode->left_margin + mode->xres + mode->right_margin + mode->hsync_len; + HDMIWrReg(EXT_VIDEO_PARA_HTOTAL_L, value & 0xFF); + HDMIWrReg(EXT_VIDEO_PARA_HTOTAL_H, (value >> 8) & 0xFF); + + value = mode->left_margin + mode->right_margin + mode->hsync_len; + HDMIWrReg(EXT_VIDEO_PARA_HBLANK_L, value & 0xFF); + HDMIWrReg(EXT_VIDEO_PARA_HBLANK_H, (value >> 8) & 0xFF); + + value = mode->left_margin + mode->hsync_len; + HDMIWrReg(EXT_VIDEO_PARA_HDELAY_L, value & 0xFF); + HDMIWrReg(EXT_VIDEO_PARA_HDELAY_H, (value >> 8) & 0xFF); + + value = mode->hsync_len; + HDMIWrReg(EXT_VIDEO_PARA_HSYNCWIDTH_L, value & 0xFF); + HDMIWrReg(EXT_VIDEO_PARA_HSYNCWIDTH_H, (value >> 8) & 0xFF); + + value = mode->upper_margin + mode->yres + mode->lower_margin + mode->vsync_len; + HDMIWrReg(EXT_VIDEO_PARA_VTOTAL_L, value & 0xFF); + HDMIWrReg(EXT_VIDEO_PARA_VTOTAL_H, (value >> 8) & 0xFF); + + value = mode->upper_margin + mode->vsync_len + mode->lower_margin; + HDMIWrReg(EXT_VIDEO_PARA_VBLANK_L, 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(EXT_VIDEO_PARA_VDELAY, value & 0xFF); + + value = mode->vsync_len; + HDMIWrReg(EXT_VIDEO_PARA_VSYNCWIDTH, value & 0xFF); + + if(vpara->output_mode == OUTPUT_HDMI) { + rk30_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__); + } + + rk30_hdmi_config_phy(vpara->vic); + rk30_hdmi_control_output(0); + return 0; +} + +static void rk30_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_HB0 + i*4, info[i]); +} + +int rk30_hdmi_config_audio(struct hdmi_audio *audio) +{ + int value, rate, N; + char word_length, channel; + + 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; + break; + case HDMI_AUDIO_FS_44100: + rate = AUDIO_441K; + N = N_441K; + break; + case HDMI_AUDIO_FS_48000: + rate = AUDIO_48K; + N = N_48K; + break; + case HDMI_AUDIO_FS_88200: + rate = AUDIO_882K; + N = N_882K; + break; + case HDMI_AUDIO_FS_96000: + rate = AUDIO_96K; + N = N_96K; + break; + case HDMI_AUDIO_FS_176400: + rate = AUDIO_1764K; + N = N_1764K; + break; + case HDMI_AUDIO_FS_192000: + rate = AUDIO_192K; + N = N_192K; + break; + default: + hdmi_err(hdmi->dev, "[%s] not support such sample rate %d\n", __FUNCTION__, audio->rate); + return -ENOENT; + } +// switch(audio->word_length) +// { +// case HDMI_AUDIO_WORD_LENGTH_16bit: +// word_length = 0x02; +// break; +// case HDMI_AUDIO_WORD_LENGTH_20bit: +// word_length = 0x0a; +// break; +// case HDMI_AUDIO_WORD_LENGTH_24bit: +// word_length = 0x0b; +// break; +// default: +// hdmi_err(hdmi->dev, "[%s] not support such word length %d\n", __FUNCTION__, audio->word_length); +// return -ENOENT; +// } + //set_audio_if I2S + HDMIWrReg(AUDIO_CTRL1, 0x00); //internal CTS, disable down sample, i2s input, disable MCLK + HDMIWrReg(AUDIO_CTRL2, 0x40); + HDMIWrReg(I2S_AUDIO_CTRL, v_I2S_MODE(I2S_MODE_STANDARD) | v_I2S_CHANNEL(channel) ); + HDMIWrReg(I2S_INPUT_SWAP, 0x00); //no swap + HDMIMskReg(value, AV_CTRL1, m_AUDIO_SAMPLE_RATE, v_AUDIO_SAMPLE_RATE(rate)) +// HDMIWrReg(SRC_NUM_AUDIO_LEN, word_length); + + //Set N value 6144, fs=48kHz + HDMIWrReg(N_1, N & 0xFF); + HDMIWrReg(N_2, (N >> 8) & 0xFF); + HDMIWrReg(LR_SWAP_N3, (N >> 16) & 0x0F); + + rk30_hdmi_config_aai(); + return 0; +} + +static void rk30_hdmi_audio_reset(void) +{ + int value; + + HDMIMskReg(value, VIDEO_SETTING2, m_AUDIO_RESET, AUDIO_CAPTURE_RESET) + msleep(1); + HDMIMskReg(value, VIDEO_SETTING2, m_AUDIO_RESET, 0) +} + +void rk30_hdmi_control_output(int enable) +{ + hdmi_dbg(hdmi->dev, "[%s] %d\n", __FUNCTION__, enable); + if(enable == 0) { + HDMIWrReg(AV_MUTE, v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1)); + } + else { + HDMIWrReg(VIDEO_SETTING2, 0x03); + if(hdmi->pwr_mode == PWR_SAVE_MODE_B) { + // Switch to power save mode_d + rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_D); + } + if(hdmi->pwr_mode == PWR_SAVE_MODE_D) { + // Switch to power save mode_e + rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_E); + } + HDMIWrReg(VIDEO_SETTING2, 0x00); + rk30_hdmi_audio_reset(); + } +} + +int rk30_hdmi_removed(void) +{ + if(hdmi->pwr_mode == PWR_SAVE_MODE_E) + { + HDMIWrReg(VIDEO_SETTING2, 0x00); + rk30_hdmi_audio_reset(); + rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_D); + } + if(hdmi->pwr_mode == PWR_SAVE_MODE_D) + rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_B); + if(hdmi->pwr_mode == PWR_SAVE_MODE_B && hdmi->state == HDMI_SLEEP) + { + HDMIWrReg(INTR_MASK1, m_INT_HOTPLUG | m_INT_MSENS); + HDMIWrReg(INTR_MASK2, 0); + HDMIWrReg(INTR_MASK3, 0); + HDMIWrReg(INTR_MASK4, 0); + // Disable color space convertion + HDMIWrReg(AV_CTRL2, v_CSC_ENABLE(0)); + HDMIWrReg(CSC_CONFIG1, v_CSC_MODE(CSC_MODE_AUTO) | v_CSC_BRSWAP_DIABLE(1)); + if(hdmi->hdcp_power_off_cb) + hdmi->hdcp_power_off_cb(); + rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_A); + } + dev_printk(KERN_INFO , hdmi->dev , "Removed.\n"); + return HDMI_ERROR_SUCESS; +} + + +irqreturn_t hdmi_irq(int irq, void *priv) +{ + char interrupt1 = 0, interrupt2 = 0, interrupt3 = 0, interrupt4 = 0; + + if(hdmi->pwr_mode == PWR_SAVE_MODE_A) + { + HDMIWrReg(SYS_CTRL, 0x20); + hdmi->pwr_mode = PWR_SAVE_MODE_B; + + hdmi_dbg(hdmi->dev, "hdmi irq wake up\n"); + // HDMI was inserted when system is sleeping, irq was triggered only once + // when wake up. So we need to check hotplug status. + if(HDMIRdReg(HPD_MENS_STA) & (m_HOTPLUG_STATUS | m_MSEN_STATUS)) { + queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, msecs_to_jiffies(10)); + } + } + else + { + interrupt1 = HDMIRdReg(INTR_STATUS1); + interrupt2 = HDMIRdReg(INTR_STATUS2); + interrupt3 = HDMIRdReg(INTR_STATUS3); + interrupt4 = HDMIRdReg(INTR_STATUS4); + HDMIWrReg(INTR_STATUS1, interrupt1); + HDMIWrReg(INTR_STATUS2, interrupt2); + HDMIWrReg(INTR_STATUS3, interrupt3); + HDMIWrReg(INTR_STATUS4, interrupt4); +#if 0 + hdmi_dbg(hdmi->dev, "[%s] interrupt1 %02x interrupt2 %02x interrupt3 %02x interrupt4 %02x\n",\ + __FUNCTION__, interrupt1, interrupt2, interrupt3, interrupt4); +#endif + if(interrupt1 & (m_INT_HOTPLUG | m_INT_MSENS)) + { + if(hdmi->state == HDMI_SLEEP) + hdmi->state = WAIT_HOTPLUG; + interrupt1 &= ~(m_INT_HOTPLUG | m_INT_MSENS); + queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, msecs_to_jiffies(10)); + } + else if(interrupt1 & (m_INT_EDID_READY | m_INT_EDID_ERR)) { + spin_lock(&hdmi->irq_lock); + edid_result = interrupt1; + spin_unlock(&hdmi->irq_lock); + } + else if(hdmi->state == HDMI_SLEEP) { + hdmi_dbg(hdmi->dev, "hdmi return to sleep mode\n"); + HDMIWrReg(SYS_CTRL, 0x10); + hdmi->pwr_mode = PWR_SAVE_MODE_A; + } + if(interrupt2 && hdmi->hdcp_irq_cb) + hdmi->hdcp_irq_cb(interrupt2); + } + return IRQ_HANDLED; +} + diff --git a/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi_hw.h b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi_hw.h new file mode 100755 index 000000000000..c42f0614b942 --- /dev/null +++ b/drivers/video/rockchip/hdmi/chips/rk30/rk30_hdmi_hw.h @@ -0,0 +1,422 @@ +#ifndef __RK30_HDMI_HW_H__ +#define __RK30_HDMI_HW_H__ + +/* HDMI_SYS_CONTROL */ +#define SYS_CTRL 0x0 + +enum { + PWR_SAVE_MODE_A = 1, + PWR_SAVE_MODE_B = 2, + PWR_SAVE_MODE_D = 4, + PWR_SAVE_MODE_E = 8 +}; +#define m_PWR_SAVE_MODE 0xF0 +#define v_PWR_SAVE_MODE(n) (n << 4) +#define PLL_B_RESET (1 << 3) + +#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 LR_SWAP_N3 0x04 +#define N_2 0x08 +#define N_1 0x0c + +#define AUDIO_CTRL1 0x28 +#define AUDIO_CTRL2 0x2c +#define I2S_AUDIO_CTRL 0x30 +enum { + I2S_MODE_STANDARD = 0, + I2S_MODE_RIGHT_JUSTIFIED, + I2S_MODE_LEFT_JUSTIFIED +}; +#define v_I2S_MODE(n) n +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 ) + +#define I2S_INPUT_SWAP 0x40 + +#define SRC_NUM_AUDIO_LEN 0x50 + +/* HDMI_AV_CTRL1*/ +#define AV_CTRL1 0x54 +enum { + AUDIO_32K = 0x3, + AUDIO_441K = 0x0, + AUDIO_48K = 0x2, + AUDIO_882K = 0x8, + AUDIO_96K = 0xa, + AUDIO_1764K = 0xc, + AUDIO_192K = 0xe, +}; +#define m_AUDIO_SAMPLE_RATE 0xF0 +#define v_AUDIO_SAMPLE_RATE(n) (n << 4) +#define m_INPUT_VIDEO_MODE (7 << 1) +#define v_INPUT_VIDEO_MODE(n) (n << 1) +enum { + INTERNAL_DE = 0, + EXTERNAL_DE +}; +#define m_DE_SIGNAL_SELECT (1 << 0) + +/* HDMI_AV_CTRL2 */ +#define AV_CTRL2 0xec +#define m_CSC_ENABLE (1 << 0) +#define v_CSC_ENABLE(n) (n) + +/* HDMI_VIDEO_CTRL1 */ +#define VIDEO_CTRL1 0x58 + +#define m_VIDEO_OUTPUT_MODE (0x3 << 6) +#define v_VIDEO_OUTPUT_MODE(n) (n << 6) +enum { + VIDEO_INPUT_DEPTH_12BIT = 0, + VIDEO_INPUT_DEPTH_10BIT = 0x1, + VIDEO_INPUT_DEPTH_8BIT = 0x3 +}; +#define m_VIDEO_INPUT_DEPTH (3 << 4) +#define v_VIDEO_INPUT_DEPTH(n) (n << 4) +enum { + VIDEO_EMBEDDED_SYNC_LOCATION_0 = 0, + VIDEO_EMBEDDED_SYNC_LOCATION_1, + VIDEO_EMBEDDED_SYNC_LOCATION_2 +}; +#define m_VIDEO_EMBEDDED_SYNC_LOCATION (3 << 2) +#define VIDEO_EMBEDDED_SYNC_LOCATION(n) (n << 2) +#define m_VIDEO_INPUT_COLOR_MODE (1 << 0) + +/* DEEP_COLOR_MODE */ +#define DEEP_COLOR_MODE 0x5c +enum{ + TMDS_CLOCK_MODE_8BIT = 0, + TMDS_CLOKK_MODE_10BIT, + TMDS_CLOKK_MODE_12BIT +}; +#define TMDS_CLOCK_MODE_MASK 0x3 << 6 +#define TMDS_CLOCK_MODE(n) (n) << 6 + +/* VIDEO_CTRL2 */ +#define VIDEO_SETTING2 0x114 +#define m_UNMUTE (1 << 7) +#define m_MUTE (1 << 6) +#define m_AUDIO_RESET (1 << 2) +#define m_NOT_SEND_AUDIO (1 << 1) +#define m_NOT_SEND_VIDEO (1 << 0) +#define AV_UNMUTE (1 << 7) // Unmute video and audio, send normal video and audio data +#define AV_MUTE (1 << 6) // Mute video and audio, send black video data and silent audio data +#define AUDIO_CAPTURE_RESET (1 << 2) // Reset audio process logic, only available in pwr_e mode. +#define NOT_SEND_AUDIO (1 << 1) // Send silent audio data +#define NOT_SEND_VIDEO (1 << 0) // Send black video data + +/* Color Space Convertion Parameter*/ +#define CSC_PARA_C0_H 0x60 +#define CSC_PARA_C0_L 0x64 +#define CSC_PARA_C1_H 0x68 +#define CSC_PARA_C1_L 0x6c +#define CSC_PARA_C2_H 0x70 +#define CSC_PARA_C2_L 0x74 +#define CSC_PARA_C3_H 0x78 +#define CSC_PARA_C3_L 0x7c +#define CSC_PARA_C4_H 0x80 +#define CSC_PARA_C4_L 0x84 +#define CSC_PARA_C5_H 0x88 +#define CSC_PARA_C5_L 0x8c +#define CSC_PARA_C6_H 0x90 +#define CSC_PARA_C6_L 0x94 +#define CSC_PARA_C7_H 0x98 +#define CSC_PARA_C7_L 0x9c +#define CSC_PARA_C8_H 0xa0 +#define CSC_PARA_C8_L 0xa4 +#define CSC_PARA_C9_H 0xa8 +#define CSC_PARA_C9_L 0xac +#define CSC_PARA_C10_H 0xac +#define CSC_PARA_C10_L 0xb4 +#define CSC_PARA_C11_H 0xb8 +#define CSC_PARA_C11_L 0xbc + +#define CSC_CONFIG1 0x34c +#define m_CSC_MODE (1 << 7) +#define m_CSC_COEF_MODE (0xF << 3) //Only used in auto csc mode +#define m_CSC_STATUS (1 << 2) +#define m_CSC_VID_SELECT (1 << 1) +#define m_CSC_BRSWAP_DIABLE (1) + +enum { + CSC_MODE_MANUAL = 0, + CSC_MODE_AUTO +}; +#define v_CSC_MODE(n) (n << 7) +enum { + COE_SDTV_LIMITED_RANGE = 0x08, + COE_SDTV_FULL_RANGE = 0x04, + COE_HDTV_60Hz = 0x2, + COE_HDTV_50Hz = 0x1 +}; +#define v_CSC_COE_MODE(n) (n << 3) +enum { + CSC_INPUT_VID_5_19 = 0, + CSC_INPUT_VID_28_29 +}; +#define v_CSC_VID_SELECT(n) (n << 1) +#define v_CSC_BRSWAP_DIABLE(n) (n) + +/* CONTROL_PACKET_BUF_INDEX */ +#define CONTROL_PACKET_BUF_INDEX 0x17c +enum { + INFOFRAME_AVI = 0x06, + INFOFRAME_AAI = 0x08 +}; +#define CONTROL_PACKET_HB0 0x180 +#define CONTROL_PACKET_HB1 0x184 +#define CONTROL_PACKET_HB2 0x188 +#define CONTROL_PACKET_PB_ADDR 0x18c +#define SIZE_AVI_INFOFRAME 0x11 // 17 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 +}; + +/* External Video Parameter Setting*/ +#define EXT_VIDEO_PARA 0xC0 +#define m_VSYNC_OFFSET (0xF << 4) +#define m_VSYNC_POLARITY (1 << 3) +#define m_HSYNC_POLARITY (1 << 2) +#define m_INTERLACE (1 << 1) +#define m_EXT_VIDEO_ENABLE (1 << 0) + +#define v_VSYNC_OFFSET(n) (n << 4) +#define v_VSYNC_POLARITY(n) (n << 3) +#define v_HSYNC_POLARITY(n) (n << 2) +#define v_INTERLACE(n) (n << 1) +#define v_EXT_VIDEO_ENABLE(n) (n << 0) + +#define EXT_VIDEO_PARA_HTOTAL_L 0xC4 +#define EXT_VIDEO_PARA_HTOTAL_H 0xC8 +#define EXT_VIDEO_PARA_HBLANK_L 0xCC +#define EXT_VIDEO_PARA_HBLANK_H 0xD0 +#define EXT_VIDEO_PARA_HDELAY_L 0xD4 +#define EXT_VIDEO_PARA_HDELAY_H 0xD8 +#define EXT_VIDEO_PARA_HSYNCWIDTH_L 0xDC +#define EXT_VIDEO_PARA_HSYNCWIDTH_H 0xE0 + +#define EXT_VIDEO_PARA_VTOTAL_L 0xE4 +#define EXT_VIDEO_PARA_VTOTAL_H 0xE8 +#define EXT_VIDEO_PARA_VBLANK_L 0xF4 +#define EXT_VIDEO_PARA_VDELAY 0xF8 +#define EXT_VIDEO_PARA_VSYNCWIDTH 0xFC + +#define PHY_PLL_SPEED 0x158 + #define v_TEST_EN(n) (n << 6) + #define v_PLLA_BYPASS(n) (n << 4) + #define v_PLLB_SPEED(n) (n << 2) + #define v_PLLA_SPEED(n) (n) + enum { + PLL_SPEED_LOWEST = 0, + PLL_SPEED_MIDLOW, + PLL_SPEED_MIDHIGH, + PLL_SPEED_HIGHEST + }; + +#define PHY_PLL_17 0x15c // PLL A & B config bit 17 + #define v_PLLA_BIT17(n) (n << 2) + #define v_PLLB_BIT17(n) (n << 1) + +#define PHY_BGR 0x160 + #define v_BGR_DISCONNECT(n) (n << 7) + #define v_BGR_V_OFFSET(n) (n << 4) + #define v_BGR_I_OFFSET(n) (n) + +#define PHY_PLLA_1 0x164 +#define PHY_PLLA_2 0x168 +#define PHY_PLLB_1 0x16c +#define PHY_PLLB_2 0x170 + +#define PHY_DRIVER_PREEMPHASIS 0x174 + #define v_TMDS_SWING(n) (n << 4) + #define v_PRE_EMPHASIS(n) (n) + +#define PHY_PLL_16_AML 0x178 // PLL A & B config bit 16 and AML control + #define v_PLLA_BIT16(n) (n << 5) + #define v_PLLB_BIT16(n) (n << 4) + #define v_AML(n) (n) + +/* Interrupt Setting */ +#define INTR_MASK1 0x248 +#define INTR_STATUS1 0x250 + #define m_INT_HOTPLUG (1 << 7) + #define m_INT_MSENS (1 << 6) + #define m_INT_VSYNC (1 << 5) + #define m_INT_AUDIO_FIFO_FULL (1 << 4) + #define m_INT_EDID_READY (1 << 2) + #define m_INT_EDID_ERR (1 << 1) +#define INTR_MASK2 0x24c +#define INTR_STATUS2 0x254 + #define m_INT_HDCP_ERR (1 << 7) // HDCP error detected + #define m_INT_BKSV_RPRDY (1 << 6) // BKSV list ready from repeater + #define m_INT_BKSV_RCRDY (1 << 5) // BKSV list ready from receiver + #define m_INT_AUTH_DONE (1 << 4) // HDCP authentication done + #define m_INT_AUTH_READY (1 << 3) // HDCP authentication ready +#define INTR_MASK3 0x258 +#define INTR_STATUS3 0x260 + +#define INTR_MASK4 0x25c +#define INTR_STATUS4 0x264 + +#define DDC_READ_FIFO_ADDR 0x200 +#define DDC_BUS_FREQ_L 0x204 +#define DDC_BUS_FREQ_H 0x208 +#define DDC_BUS_CTRL 0x2dc +#define DDC_I2C_LEN 0x278 +#define DDC_I2C_OFFSET 0x280 +#define DDC_I2C_CTRL 0x284 +#define DDC_I2C_READ_BUF0 0x288 +#define DDC_I2C_READ_BUF1 0x28c +#define DDC_I2C_READ_BUF2 0x290 +#define DDC_I2C_READ_BUF3 0x294 +#define DDC_I2C_WRITE_BUF0 0x298 +#define DDC_I2C_WRITE_BUF1 0x29c +#define DDC_I2C_WRITE_BUF2 0x2a0 +#define DDC_I2C_WRITE_BUF3 0x2a4 +#define DDC_I2C_WRITE_BUF4 0x2ac +#define DDC_I2C_WRITE_BUF5 0x2b0 +#define DDC_I2C_WRITE_BUF6 0x2b4 + +#define EDID_SEGMENT_POINTER 0x310 +#define EDID_WORD_ADDR 0x314 +#define EDID_FIFO_ADDR 0x318 + +#define HPD_MENS_STA 0x37c +#define m_HOTPLUG_STATUS (1 << 7) +#define m_MSEN_STATUS (1 << 6) + +/* HDCP_CTRL */ +#define HDCP_CTRL 0x2bc + enum { + OUTPUT_DVI = 0, + OUTPUT_HDMI + }; + #define m_HDCP_AUTH_START (1 << 7) // start hdcp + #define m_HDCP_BKSV_PASS (1 << 6) // bksv valid + #define m_HDCP_BKSV_FAILED (1 << 5) // bksv invalid + #define m_HDCP_FRAMED_ENCRYPED (1 << 4) + #define m_HDCP_AUTH_STOP (1 << 3) // stop hdcp + #define m_HDCP_ADV_CIPHER (1 << 2) // advanced cipher mode + #define m_HDMI_DVI (1 << 1) + #define m_HDCP_RESET (1 << 0) // reset hdcp + #define v_HDCP_AUTH_START(n) (n << 7) + #define v_HDCP_BKSV_PASS(n) (n << 6) + #define v_HDCP_BKSV_FAILED(n) (n << 5) + #define v_HDCP_FRAMED_ENCRYPED(n) (n << 4) + #define v_HDCP_AUTH_STOP(n) (n << 3) + #define v_HDCP_ADV_CIPHER(n) (n << 2) + #define v_HDMI_DVI(n) (n << 1) + #define v_HDCP_RESET(n) (n << 0) +#define HDCP_CTRL2 0x340 + +/* HDCP Key Memory Access Control */ +#define HDCP_KEY_ACCESS_CTRL1 0x338 +#define HDCP_KEY_ACCESS_CTRL2 0x33c + #define m_LOAD_FACSIMILE_HDCP_KEY (1 << 1) + #define m_LOAD_HDCP_KEY (1 << 0) +/* HDCP Key Memory Control */ +#define HDCP_KEY_MEM_CTRL 0x348 + #define m_USE_KEY1 (1 << 6) + #define m_USE_KEY2 (1 << 5) + #define m_LOAD_AKSV (1 << 4) + #define m_KSV_SELECTED (1 << 3) + #define m_KSV_VALID (1 << 2) + #define m_KEY_VALID (1 << 1) + #define m_KEY_READY (1 << 0) + #define v_USE_KEY1(n) (n << 6) + #define v_USE_KEY2(n) (n << 5) + #define v_LOAD_AKSV(n) (n << 4) + +/* HDCP B device capacity */ +#define HDCP_BCAPS 0x2f8 + #define m_HDMI_RECEIVED (1 << 7) //If HDCP receiver support HDMI, this bit must be 1. + #define m_REPEATER (1 << 6) + #define m_KSV_FIFO_READY (1 << 5) + #define m_DDC_FAST (1 << 4) + #define m_1_1_FEATURE (1 << 1) + #define m_FAST_REAUTHENTICATION (1 << 0) //For HDMI, this function is supported whether this bit is enabled or not. + +/* HDCP KSV Value */ +#define HDCP_KSV_BYTE0 0x2fc +#define HDCP_KSV_BYTE1 0x300 +#define HDCP_KSV_BYTE2 0x304 +#define HDCP_KSV_BYTE3 0x308 +#define HDCP_KSV_BYTE4 0x30c + +/* HDCP error status */ +#define HDCP_ERROR 0x320 + +/* HDCP 100 ms timer */ +#define HDCP_TIMER_100MS 0x324 +/* HDCP 5s timer */ +#define HDCP_TIMER_5S 0x328 + +/* HDCP Key ram address */ +#define HDCP_RAM_KEY_KSV1 0x400 +#define HDCP_RAM_KEY_KSV2 0x407 +#define HDCP_RAM_KEY_PRIVATE 0x40e +#define HDCP_KEY_LENGTH 0x13C + + +#define HDCP_ENABLE_HW_AUTH // Enable hardware authentication mode +#define HDMI_INTERANL_CLK_DIV 0x19 + +#define HDMIRdReg(addr) __raw_readl(hdmi->regbase + addr) +#define HDMIWrReg(addr, val) __raw_writel((val), hdmi->regbase + addr); +#define HDMIMskReg(temp, addr, msk, val) \ + temp = __raw_readl(hdmi->regbase + addr) & (0xFF - (msk)) ; \ + __raw_writel(temp | ( (val) & (msk) ), hdmi->regbase + addr); + + + +/* Color Space Convertion Mode */ +enum { + CSC_RGB_0_255_TO_ITU601_16_235 = 0, //RGB 0-255 input to YCbCr 16-235 output according BT601 + CSC_RGB_0_255_TO_ITU709_16_235, //RGB 0-255 input to YCbCr 16-235 output accroding BT709 + CSC_ITU601_16_235_TO_RGB_16_235, //YCbCr 16-235 input to RGB 16-235 output according BT601 + CSC_ITU709_16_235_TO_RGB_16_235, //YCbCr 16-235 input to RGB 16-235 output according BT709 + CSC_ITU601_16_235_TO_RGB_0_255, //YCbCr 16-235 input to RGB 0-255 output according BT601 + CSC_ITU709_16_235_TO_RGB_0_255 //YCbCr 16-235 input to RGB 0-255 output according BT709 +}; + +extern int rk30_hdmi_initial(void); +extern int rk30_hdmi_detect_hotplug(void); +extern int rk30_hdmi_read_edid(int block, unsigned char *buff); +extern int rk30_hdmi_removed(void); +extern int rk30_hdmi_config_video(struct hdmi_video_para *vpara); +extern int rk30_hdmi_config_audio(struct hdmi_audio *audio); +extern void rk30_hdmi_control_output(int enable); + +#endif diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi.c b/drivers/video/rockchip/hdmi/rk30_hdmi.c deleted file mode 100755 index 0aae519acc9e..000000000000 --- a/drivers/video/rockchip/hdmi/rk30_hdmi.c +++ /dev/null @@ -1,299 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include "rk30_hdmi.h" -#include "rk30_hdmi_hw.h" - -struct hdmi *hdmi = NULL; - -extern irqreturn_t hdmi_irq(int irq, void *priv); -extern void hdmi_work(struct work_struct *work); -extern struct rk_lcdc_device_driver * rk_get_lcdc_drv(char *name); -extern void hdmi_register_display_sysfs(struct hdmi *hdmi, struct device *parent); -extern void hdmi_unregister_display_sysfs(struct hdmi *hdmi); - -int rk30_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void), - void (*hdcp_irq_cb)(int status), - int (*hdcp_power_on_cb)(void), - void (*hdcp_power_off_cb)(void)) -{ - if(hdmi == NULL) - return HDMI_ERROR_FALSE; - - hdmi->hdcp_cb = hdcp_cb; - hdmi->hdcp_irq_cb = hdcp_irq_cb; - hdmi->hdcp_power_on_cb = hdcp_power_on_cb; - hdmi->hdcp_power_off_cb = hdcp_power_off_cb; - - return HDMI_ERROR_SUCESS; -} - -#ifdef CONFIG_HAS_EARLYSUSPEND -static void hdmi_early_suspend(struct early_suspend *h) -{ - hdmi_dbg(hdmi->dev, "hdmi enter early suspend pwr %d state %d\n", hdmi->pwr_mode, hdmi->state); - flush_delayed_work(&hdmi->delay_work); - mutex_lock(&hdmi->enable_mutex); - hdmi->suspend = 1; - if(!hdmi->enable) { - mutex_unlock(&hdmi->enable_mutex); - return; - } - disable_irq(hdmi->irq); - mutex_unlock(&hdmi->enable_mutex); - hdmi->command = HDMI_CONFIG_ENABLE; - init_completion(&hdmi->complete); - hdmi->wait = 1; - queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0); - wait_for_completion_interruptible_timeout(&hdmi->complete, - msecs_to_jiffies(5000)); - flush_delayed_work(&hdmi->delay_work); - // 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; -} - -static void hdmi_early_resume(struct early_suspend *h) -{ - hdmi_dbg(hdmi->dev, "hdmi exit early resume\n"); - mutex_lock(&hdmi->enable_mutex); - - rk30_mux_api_set(GPIO0A2_HDMII2CSDA_NAME, GPIO0A_HDMI_I2C_SDA); - rk30_mux_api_set(GPIO0A1_HDMII2CSCL_NAME, GPIO0A_HDMI_I2C_SCL); - - hdmi->suspend = 0; - rk30_hdmi_initial(); - if(hdmi->enable) { - enable_irq(hdmi->irq); - } - mutex_unlock(&hdmi->enable_mutex); - return; -} -#endif - -static inline void hdmi_io_remap(void) -{ - unsigned int value; - - // Remap HDMI IO Pin - rk30_mux_api_set(GPIO0A2_HDMII2CSDA_NAME, GPIO0A_HDMI_I2C_SDA); - rk30_mux_api_set(GPIO0A1_HDMII2CSCL_NAME, GPIO0A_HDMI_I2C_SCL); - rk30_mux_api_set(GPIO0A0_HDMIHOTPLUGIN_NAME, GPIO0A_HDMI_HOT_PLUG_IN); - - // Select LCDC0 as video source and enabled. - value = (HDMI_SOURCE_DEFAULT << 14) | (1 << 30); - writel(value, GRF_SOC_CON0 + RK30_GRF_BASE); -} - -static int __devinit rk30_hdmi_probe (struct platform_device *pdev) -{ - int ret; - struct resource *res; - struct resource *mem; - - hdmi = kmalloc(sizeof(struct hdmi), GFP_KERNEL); - if(!hdmi) - { - dev_err(&pdev->dev, ">>rk30 hdmi kmalloc fail!"); - return -ENOMEM; - } - memset(hdmi, 0, sizeof(struct hdmi)); - hdmi->dev = &pdev->dev; - platform_set_drvdata(pdev, hdmi); - - if(HDMI_SOURCE_DEFAULT == HDMI_SOURCE_LCDC0) - hdmi->lcdc = rk_get_lcdc_drv("lcdc0"); - else - hdmi->lcdc = rk_get_lcdc_drv("lcdc1"); - if(hdmi->lcdc == NULL) - { - dev_err(hdmi->dev, "can not connect to video source lcdc\n"); - ret = -ENXIO; - goto err0; - } - hdmi->xscale = 95; - hdmi->yscale = 95; - - hdmi->hclk = clk_get(NULL,"hclk_hdmi"); - if(IS_ERR(hdmi->hclk)) - { - dev_err(hdmi->dev, "Unable to get hdmi hclk\n"); - ret = -ENXIO; - goto err0; - } - clk_enable(hdmi->hclk); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(hdmi->dev, "Unable to get register resource\n"); - ret = -ENXIO; - goto err0; - } - hdmi->regbase_phy = res->start; - hdmi->regsize_phy = (res->end - res->start) + 1; - mem = request_mem_region(res->start, (res->end - res->start) + 1, pdev->name); - if (!mem) - { - dev_err(hdmi->dev, "failed to request mem region for hdmi\n"); - ret = -ENOENT; - goto err0; - } - - - hdmi->regbase = (int)ioremap(res->start, (res->end - res->start) + 1); - if (!hdmi->regbase) { - dev_err(hdmi->dev, "cannot ioremap registers\n"); - ret = -ENXIO; - goto err1; - } - - ret = rk30_hdmi_initial(); - if(ret != HDMI_ERROR_SUCESS) - goto err1; - - hdmi_io_remap(); - hdmi_sys_init(); - - hdmi->workqueue = create_singlethread_workqueue("hdmi"); - INIT_DELAYED_WORK(&(hdmi->delay_work), hdmi_work); - - #ifdef CONFIG_HAS_EARLYSUSPEND - hdmi->early_suspend.suspend = hdmi_early_suspend; - hdmi->early_suspend.resume = hdmi_early_resume; - hdmi->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 10; - register_early_suspend(&hdmi->early_suspend); - #endif - - hdmi_register_display_sysfs(hdmi, NULL); - #ifdef CONFIG_SWITCH - hdmi->switch_hdmi.name="hdmi"; - switch_dev_register(&(hdmi->switch_hdmi)); - #endif - - spin_lock_init(&hdmi->irq_lock); - mutex_init(&hdmi->enable_mutex); - - /* get the IRQ */ - hdmi->irq = platform_get_irq(pdev, 0); - if(hdmi->irq <= 0) { - dev_err(hdmi->dev, "failed to get hdmi irq resource (%d).\n", hdmi->irq); - ret = -ENXIO; - goto err2; - } - - /* request the IRQ */ - ret = request_irq(hdmi->irq, hdmi_irq, 0, dev_name(&pdev->dev), hdmi); - if (ret) - { - dev_err(hdmi->dev, "hdmi request_irq failed (%d).\n", ret); - goto err2; - } - - hdmi_dbg(hdmi->dev, "rk30 hdmi probe sucess.\n"); - return 0; -err2: - #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 - iounmap((void*)hdmi->regbase); -err1: - release_mem_region(res->start,(res->end - res->start) + 1); - clk_disable(hdmi->hclk); -err0: - hdmi_dbg(hdmi->dev, "rk30 hdmi probe error.\n"); - kfree(hdmi); - hdmi = NULL; - return ret; -} - -static int __devexit rk30_hdmi_remove(struct platform_device *pdev) -{ - if(hdmi) { - mutex_lock(&hdmi->enable_mutex); - if(!hdmi->suspend && hdmi->enable) - disable_irq(hdmi->irq); - mutex_unlock(&hdmi->enable_mutex); - free_irq(hdmi->irq, NULL); - flush_workqueue(hdmi->workqueue); - destroy_workqueue(hdmi->workqueue); - #ifdef CONFIG_SWITCH - switch_dev_unregister(&(hdmi->switch_hdmi)); - #endif - hdmi_unregister_display_sysfs(hdmi); - #ifdef CONFIG_HAS_EARLYSUSPEND - unregister_early_suspend(&hdmi->early_suspend); - #endif - iounmap((void*)hdmi->regbase); - release_mem_region(hdmi->regbase_phy, hdmi->regsize_phy); - clk_disable(hdmi->hclk); - fb_destroy_modelist(&hdmi->edid.modelist); - if(hdmi->edid.audio) - kfree(hdmi->edid.audio); - if(hdmi->edid.specs) - { - if(hdmi->edid.specs->modedb) - kfree(hdmi->edid.specs->modedb); - kfree(hdmi->edid.specs); - } - kfree(hdmi); - hdmi = NULL; - } - printk(KERN_INFO "rk30 hdmi removed.\n"); - return 0; -} - -static void rk30_hdmi_shutdown(struct platform_device *pdev) -{ - if(hdmi) { - #ifdef CONFIG_HAS_EARLYSUSPEND - unregister_early_suspend(&hdmi->early_suspend); - #endif - } - printk(KERN_INFO "rk30 hdmi shut down.\n"); -} - -static struct platform_driver rk30_hdmi_driver = { - .probe = rk30_hdmi_probe, - .remove = __devexit_p(rk30_hdmi_remove), - .driver = { - .name = "rk30-hdmi", - .owner = THIS_MODULE, - }, - .shutdown = rk30_hdmi_shutdown, -}; - -static int __init rk30_hdmi_init(void) -{ - return platform_driver_register(&rk30_hdmi_driver); -} - -static void __exit rk30_hdmi_exit(void) -{ - platform_driver_unregister(&rk30_hdmi_driver); -} - - -//fs_initcall(rk30_hdmi_init); -module_init(rk30_hdmi_init); -module_exit(rk30_hdmi_exit); diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi.h b/drivers/video/rockchip/hdmi/rk30_hdmi.h deleted file mode 100755 index 54f5a5c0a676..000000000000 --- a/drivers/video/rockchip/hdmi/rk30_hdmi.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef __RK30_HDMI_H__ -#define __RK30_HDMI_H__ - -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_SWITCH -#include -#endif -#ifdef CONFIG_HAS_EARLYSUSPEND -#include -#endif -#include -#include -#include -#include "rk_hdmi.h" - -// HDMI video source -enum { - HDMI_SOURCE_LCDC0 = 0, - HDMI_SOURCE_LCDC1 = 1 -}; - -/* default HDMI video source */ -#define HDMI_SOURCE_DEFAULT HDMI_SOURCE_LCDC1 - -/* 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 - -/* default HDMI output video mode */ -#define HDMI_VIDEO_DEFAULT_MODE HDMI_1280x720p_60Hz//HDMI_1920x1080p_60Hz -/* 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 - -struct hdmi { - struct device *dev; - struct clk *hclk; //HDMI AHP clk - int regbase; - int irq; - int regbase_phy; - int regsize_phy; - struct rk_lcdc_device_driver *lcdc; - - #ifdef CONFIG_SWITCH - struct switch_dev switch_hdmi; - #endif - - struct workqueue_struct *workqueue; - struct delayed_work delay_work; - - spinlock_t irq_lock; - struct mutex enable_mutex; - - int wait; - struct completion complete; - - int suspend; -#ifdef CONFIG_HAS_EARLYSUSPEND - struct early_suspend early_suspend; -#endif - - struct hdmi_edid edid; - int enable; // Enable HDMI output or not - int vic; // HDMI output video mode code - struct hdmi_audio audio; // HDMI output audio type. - - int pwr_mode; // power mode - int hotplug; // hot plug status - int state; // hdmi state machine status - int autoconfig; // if true, auto config hdmi output mode according to EDID. - int command; // HDMI configuration command - int display; // HDMI display status - int xscale; // x direction scale value - int yscale; // y directoon scale value - int tmdsclk; // TDMS Clock frequency - // call back for hdcp operatoion - void (*hdcp_cb)(void); - void (*hdcp_irq_cb)(int); - int (*hdcp_power_on_cb)(void); - void (*hdcp_power_off_cb)(void); -}; - -extern struct hdmi *hdmi; - -extern int hdmi_sys_init(void); -extern int hdmi_sys_parse_edid(struct hdmi* hdmi); -extern const char *hdmi_get_video_mode_name(unsigned char vic); -extern int hdmi_videomode_to_vic(struct fb_videomode *vmode); -extern const struct fb_videomode* hdmi_vic_to_videomode(int vic); -extern int hdmi_add_videomode(const struct fb_videomode *mode, struct list_head *head); -extern struct hdmi_video_timing * hdmi_find_mode(int vic); -extern int hdmi_find_best_mode(struct hdmi* hdmi, int vic); -extern int hdmi_ouputmode_select(struct hdmi *hdmi, int edid_ok); -extern int hdmi_switch_fb(struct hdmi *hdmi, int vic); -extern void hdmi_sys_remove(void); -extern int rk30_hdmi_register_hdcp_callbacks(void (*hdcp_cb)(void), - void (*hdcp_irq_cb)(int status), - int (*hdcp_power_on_cb)(void), - void (*hdcp_power_off_cb)(void)); -#endif /* __RK30_HDMI_H__ */ diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_edid.c b/drivers/video/rockchip/hdmi/rk30_hdmi_edid.c deleted file mode 100755 index 9fec195fea3c..000000000000 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_edid.c +++ /dev/null @@ -1,410 +0,0 @@ -#include "rk30_hdmi.h" -#include "rk30_hdmi_hw.h" -#include "../../edid.h" - -#define hdmi_edid_error(fmt, ...) \ - printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__) - -#if 0 -#define hdmi_edid_debug(fmt, ...) \ - printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__) -#else -#define hdmi_edid_debug(fmt, ...) -#endif - -typedef enum HDMI_EDID_ERRORCODE -{ - E_HDMI_EDID_SUCCESS = 0, - E_HDMI_EDID_PARAM, - E_HDMI_EDID_HEAD, - E_HDMI_EDID_CHECKSUM, - E_HDMI_EDID_VERSION, - E_HDMI_EDID_UNKOWNDATA, - E_HDMI_EDID_NOMEMORY -}HDMI_EDID_ErrorCode; - -static const unsigned int double_aspect_vic[] = {3, 7, 9, 11, 13, 15, 18, 22, 24, 26, 28, 30, 36, 38, 43, 45, 49, 51, 53, 55, 57, 59}; -static int hdmi_edid_checksum(unsigned char *buf) -{ - int i; - int checksum = 0; - - for(i = 0; i < HDMI_EDID_BLOCK_SIZE; i++) - checksum += buf[i]; - - checksum &= 0xff; - - if(checksum == 0) - return E_HDMI_EDID_SUCCESS; - else - return E_HDMI_EDID_CHECKSUM; -} - -/* - @Des Parse Detail Timing Descriptor. - @Param buf : pointer to DTD data. - @Param pvic: VIC of DTD descripted. - */ -static int hdmi_edid_parse_dtd(unsigned char *block, struct fb_videomode *mode) -{ - mode->xres = H_ACTIVE; - mode->yres = V_ACTIVE; - mode->pixclock = PIXEL_CLOCK; -// mode->pixclock /= 1000; -// mode->pixclock = KHZ2PICOS(mode->pixclock); - mode->right_margin = H_SYNC_OFFSET; - mode->left_margin = (H_ACTIVE + H_BLANKING) - - (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH); - mode->upper_margin = V_BLANKING - V_SYNC_OFFSET - - V_SYNC_WIDTH; - mode->lower_margin = V_SYNC_OFFSET; - mode->hsync_len = H_SYNC_WIDTH; - mode->vsync_len = V_SYNC_WIDTH; - if (HSYNC_POSITIVE) - mode->sync |= FB_SYNC_HOR_HIGH_ACT; - if (VSYNC_POSITIVE) - mode->sync |= FB_SYNC_VERT_HIGH_ACT; - mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) * - (V_ACTIVE + V_BLANKING)); - if (INTERLACED) { - mode->yres *= 2; - mode->upper_margin *= 2; - mode->lower_margin *= 2; - mode->vsync_len *= 2; - mode->vmode |= FB_VMODE_INTERLACED; - } - mode->flag = FB_MODE_IS_DETAILED; - - hdmi_edid_debug("<<<<<<<>>>>>>>>\n"); - hdmi_edid_debug("%d KHz Refresh %d Hz", PIXEL_CLOCK/1000, mode->refresh); - hdmi_edid_debug("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET, - H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING); - hdmi_edid_debug("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET, - V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING); - hdmi_edid_debug("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-", - (VSYNC_POSITIVE) ? "+" : "-"); - return E_HDMI_EDID_SUCCESS; -} - -static int hdmi_edid_parse_base(unsigned char *buf, int *extend_num, struct hdmi_edid *pedid) -{ - int rc, i; - - if(buf == NULL || extend_num == NULL) - return E_HDMI_EDID_PARAM; - - #ifdef DEBUG - for(i = 0; i < HDMI_EDID_BLOCK_SIZE; i++) - { - hdmi_edid_debug("%02x ", buf[i]&0xff); - if((i+1) % 16 == 0) - hdmi_edid_debug("\n"); - } - #endif - - // Check first 8 byte to ensure it is an edid base block. - if( buf[0] != 0x00 || - buf[1] != 0xFF || - buf[2] != 0xFF || - buf[3] != 0xFF || - buf[4] != 0xFF || - buf[5] != 0xFF || - buf[6] != 0xFF || - buf[7] != 0x00) - { - hdmi_edid_error("[EDID] check header error\n"); - return E_HDMI_EDID_HEAD; - } - - *extend_num = buf[0x7e]; - #ifdef DEBUG - hdmi_edid_debug("[EDID] extend block num is %d\n", buf[0x7e]); - #endif - - // Checksum - rc = hdmi_edid_checksum(buf); - if( rc != E_HDMI_EDID_SUCCESS) - { - hdmi_edid_error("[EDID] base block checksum error\n"); - return E_HDMI_EDID_CHECKSUM; - } - - pedid->specs = kzalloc(sizeof(struct fb_monspecs), GFP_KERNEL); - if(pedid->specs == NULL) - return E_HDMI_EDID_NOMEMORY; - - fb_edid_to_monspecs(buf, pedid->specs); - - return E_HDMI_EDID_SUCCESS; -} - -// Parse CEA Short Video Descriptor -static int hdmi_edid_get_cea_svd(unsigned char *buf, struct hdmi_edid *pedid) -{ - const struct fb_videomode *mode; - int count, i, j, vic; - - count = buf[0] & 0x1F; - for(i = 0; i < count; i++) - { - hdmi_edid_debug("[EDID-CEA] %02x VID %d native %d\n", buf[1 + i], buf[1 + i] & 0x7f, buf[1 + i] >> 7); - vic = buf[1 + i] & 0x7f; - for(j = 0; j < ARRAY_SIZE(double_aspect_vic); j++) - { - if(vic == double_aspect_vic[j]) - { - vic--; - break; - } - } - if(vic) - { - mode = hdmi_vic_to_videomode(vic); - if(mode) - { - hdmi_add_videomode(mode, &pedid->modelist); - } - } - } - return 0; -} - -// Parse CEA Short Audio Descriptor -static int hdmi_edid_parse_cea_sad(unsigned char *buf, struct hdmi_edid *pedid) -{ - int i, count; - - count = buf[0] & 0x1F; - pedid->audio = kmalloc((count/3)*sizeof(struct hdmi_audio), GFP_KERNEL); - if(pedid->audio == NULL) - return E_HDMI_EDID_NOMEMORY; - pedid->audio_num = count/3; - for(i = 0; i < pedid->audio_num; i++) - { - pedid->audio[i].type = (buf[1 + i*3] >> 3) & 0x0F; - pedid->audio[i].channel = (buf[1 + i*3] & 0x07) + 1; - pedid->audio[i].rate = buf[1 + i*3 + 1]; - if(pedid->audio[i].type == HDMI_AUDIO_LPCM)//LPCM - { - pedid->audio[i].word_length = buf[1 + i*3 + 2]; - } -// printk("[EDID-CEA] type %d channel %d rate %d word length %d\n", -// pedid->audio[i].type, pedid->audio[i].channel, pedid->audio[i].rate, pedid->audio[i].word_length); - } - return E_HDMI_EDID_SUCCESS; -} -// Parse CEA 861 Serial Extension. -static int hdmi_edid_parse_extensions_cea(unsigned char *buf, struct hdmi_edid *pedid) -{ - unsigned int ddc_offset, native_dtd_num, cur_offset = 4; - unsigned int underscan_support, baseaudio_support; - unsigned int tag, IEEEOUI = 0; -// unsigned int supports_ai, dc_48bit, dc_36bit, dc_30bit, dc_y444; -// unsigned char vic; - - if(buf == NULL) - return E_HDMI_EDID_PARAM; - - // Check ces extension version - if(buf[1] != 3) - { - hdmi_edid_error("[EDID-CEA] error version.\n"); - return E_HDMI_EDID_VERSION; - } - - ddc_offset = buf[2]; - underscan_support = (buf[3] >> 7) & 0x01; - baseaudio_support = (buf[3] >> 6) & 0x01; - pedid->ycbcr444 = (buf[3] >> 5) & 0x01; - pedid->ycbcr422 = (buf[3] >> 4) & 0x01; - native_dtd_num = buf[3] & 0x0F; -// hdmi_edid_debug("[EDID-CEA] ddc_offset %d underscan_support %d baseaudio_support %d yuv_support %d native_dtd_num %d\n", ddc_offset, underscan_support, baseaudio_support, yuv_support, native_dtd_num); - // Parse data block - while(cur_offset < ddc_offset) - { - tag = buf[cur_offset] >> 5; - switch(tag) - { - case 0x02: // Video Data Block - hdmi_edid_debug("[EDID-CEA] It is a Video Data Block.\n"); - hdmi_edid_get_cea_svd(buf + cur_offset, pedid); - break; - case 0x01: // Audio Data Block - hdmi_edid_debug("[EDID-CEA] It is a Audio Data Block.\n"); - hdmi_edid_parse_cea_sad(buf + cur_offset, pedid); - break; - case 0x04: // Speaker Allocation Data Block - hdmi_edid_debug("[EDID-CEA] It is a Speaker Allocatio Data Block.\n"); - break; - case 0x03: // Vendor Specific Data Block - hdmi_edid_debug("[EDID-CEA] It is a Vendor Specific Data Block.\n"); - - IEEEOUI = buf[cur_offset + 2 + 1]; - IEEEOUI <<= 8; - IEEEOUI += buf[cur_offset + 1 + 1]; - IEEEOUI <<= 8; - IEEEOUI += buf[cur_offset + 1]; - hdmi_edid_debug("[EDID-CEA] IEEEOUI is 0x%08x.\n", IEEEOUI); - if(IEEEOUI == 0x0c03) - pedid->sink_hdmi = 1; -// if(count > 5) -// { -// pedid->deepcolor = (buf[cur_offset + 5] >> 3) & 0x0F; -// supports_ai = buf[cur_offset + 5] >> 7; -// dc_48bit = (buf[cur_offset + 5] >> 6) & 0x1; -// dc_36bit = (buf[cur_offset + 5] >> 5) & 0x1; -// dc_30bit = (buf[cur_offset + 5] >> 4) & 0x1; -// dc_y444 = (buf[cur_offset + 5] >> 3) & 0x1; -// hdmi_edid_debug("[EDID-CEA] supports_ai %d dc_48bit %d dc_36bit %d dc_30bit %d dc_y444 %d \n", supports_ai, dc_48bit, dc_36bit, dc_30bit, dc_y444); -// } -// if(count > 6) -// pedid->maxtmdsclock = buf[cur_offset + 6] * 5000000; -// if(count > 7) -// { -// pedid->latency_fields_present = (buf[cur_offset + 7] & 0x80) ? 1:0; -// pedid->i_latency_fields_present = (buf[cur_offset + 7] & 0x40) ? 1:0; -// } -// if(count > 9 && pedid->latency_fields_present) -// { -// pedid->video_latency = buf[cur_offset + 8]; -// pedid->audio_latency = buf[cur_offset + 9]; -// } -// if(count > 11 && pedid->i_latency_fields_present) -// { -// pedid->interlaced_video_latency = buf[cur_offset + 10]; -// pedid->interlaced_audio_latency = buf[cur_offset + 11]; -// } - break; - case 0x05: // VESA DTC Data Block - hdmi_edid_debug("[EDID-CEA] It is a VESA DTC Data Block.\n"); - break; - case 0x07: // Use Extended Tag - hdmi_edid_debug("[EDID-CEA] It is a Use Extended Tag Data Block.\n"); - break; - default: - hdmi_edid_error("[EDID-CEA] unkowned data block tag.\n"); - break; - } - cur_offset += (buf[cur_offset] & 0x1F) + 1; - } -#if 1 -{ - // Parse DTD - struct fb_videomode *vmode = kmalloc(sizeof(struct fb_videomode), GFP_KERNEL); - if(vmode == NULL) - return E_HDMI_EDID_SUCCESS; - while(ddc_offset < HDMI_EDID_BLOCK_SIZE - 2) //buf[126] = 0 and buf[127] = checksum - { - if(!buf[ddc_offset] && !buf[ddc_offset + 1]) - break; - memset(vmode, 0, sizeof(struct fb_videomode)); - hdmi_edid_parse_dtd(buf + ddc_offset, vmode); - hdmi_add_videomode(vmode, &pedid->modelist); - ddc_offset += 18; - } - kfree(vmode); -} -#endif - return E_HDMI_EDID_SUCCESS; -} - -static int hdmi_edid_parse_extensions(unsigned char *buf, struct hdmi_edid *pedid) -{ - int rc; - - if(buf == NULL || pedid == NULL) - return E_HDMI_EDID_PARAM; - - // Checksum - rc = hdmi_edid_checksum(buf); - if( rc != E_HDMI_EDID_SUCCESS) - { - hdmi_edid_error("[EDID] extensions block checksum error\n"); - return E_HDMI_EDID_CHECKSUM; - } - - switch(buf[0]) - { - case 0xF0: - hdmi_edid_debug("[EDID-EXTEND] It is a extensions block map.\n"); - break; - case 0x02: - hdmi_edid_debug("[EDID-EXTEND] It is a CEA 861 Series Extension.\n"); - hdmi_edid_parse_extensions_cea(buf, pedid); - break; - case 0x10: - hdmi_edid_debug("[EDID-EXTEND] It is a Video Timing Block Extension.\n"); - break; - case 0x40: - hdmi_edid_debug("[EDID-EXTEND] It is a Display Information Extension.\n"); - break; - case 0x50: - hdmi_edid_debug("[EDID-EXTEND] It is a Localized String Extension.\n"); - break; - case 0x60: - hdmi_edid_debug("[EDID-EXTEND] It is a Digital Packet Video Link Extension.\n"); - break; - default: - hdmi_edid_debug("[EDID-EXTEND] Unkowned extension.\n"); - break; - } - - return E_HDMI_EDID_SUCCESS; -} - - -int hdmi_sys_parse_edid(struct hdmi* hdmi) -{ - struct hdmi_edid *pedid; - unsigned char *buff = NULL; - int rc = HDMI_ERROR_SUCESS, extendblock = 0, i; - - if(hdmi == NULL) - return HDMI_ERROR_FALSE; - - pedid = &(hdmi->edid); - memset(pedid, 0, sizeof(struct hdmi_edid)); - INIT_LIST_HEAD(&pedid->modelist); - - buff = kmalloc(HDMI_EDID_BLOCK_SIZE, GFP_KERNEL); - if(buff == NULL) - { - hdmi_dbg(hdmi->dev, "[%s] can not allocate memory for edid buff.\n", __FUNCTION__); - return -1; - } - // Read base block edid. - memset(buff, 0 , HDMI_EDID_BLOCK_SIZE); - rc = rk30_hdmi_read_edid(0, buff); - if(rc) - { - dev_err(hdmi->dev, "[HDMI] read edid base block error\n"); - goto out; - } - rc = hdmi_edid_parse_base(buff, &extendblock, pedid); - if(rc) - { - dev_err(hdmi->dev, "[HDMI] parse edid base block error\n"); - goto out; - } - for(i = 1; i < extendblock + 1; i++) - { - memset(buff, 0 , HDMI_EDID_BLOCK_SIZE); - rc = rk30_hdmi_read_edid(i, buff); - if(rc) - { - printk("[HDMI] read edid block %d error\n", i); - goto out; - } - rc = hdmi_edid_parse_extensions(buff, pedid); - if(rc) - { - dev_err(hdmi->dev, "[HDMI] parse edid block %d error\n", i); - continue; - } - } -out: - if(buff) - kfree(buff); - rc = hdmi_ouputmode_select(hdmi, rc); - return rc; -} \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_hw.c b/drivers/video/rockchip/hdmi/rk30_hdmi_hw.c deleted file mode 100755 index 8cb76ebfb14d..000000000000 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_hw.c +++ /dev/null @@ -1,647 +0,0 @@ -#include -#include -#include -#include "rk30_hdmi.h" -#include "rk30_hdmi_hw.h" - -static char edid_result = 0; - -static inline void delay100us(void) -{ - msleep(1); -} - -int rk30_hdmi_initial(void) -{ - int rc = HDMI_ERROR_SUCESS; - // internal hclk = hdmi_hclk/20 - HDMIWrReg(0x800, HDMI_INTERANL_CLK_DIV); - - if(hdmi->hdcp_power_on_cb) - rc = hdmi->hdcp_power_on_cb(); - - return rc; -} - -static void rk30_hdmi_set_pwr_mode(int mode) -{ - if(hdmi->pwr_mode == mode) - return; - hdmi_dbg(hdmi->dev, "[%s] mode %d\n", __FUNCTION__, mode); - switch(mode) - { - case PWR_SAVE_MODE_A: - HDMIWrReg(SYS_CTRL, 0x10); - break; - case PWR_SAVE_MODE_B: - HDMIWrReg(SYS_CTRL, 0x20); - break; - case PWR_SAVE_MODE_D: - // reset PLL A&B - HDMIWrReg(SYS_CTRL, 0x4C); - delay100us(); - // release PLL A reset - HDMIWrReg(SYS_CTRL, 0x48); - delay100us(); - // release PLL B reset - HDMIWrReg(SYS_CTRL, 0x40); - break; - case PWR_SAVE_MODE_E: - HDMIWrReg(SYS_CTRL, 0x80); - break; - } - hdmi->pwr_mode = mode; - if(mode != PWR_SAVE_MODE_A) - msleep(10); - hdmi_dbg(hdmi->dev, "[%s] curmode %02x\n", __FUNCTION__, HDMIRdReg(SYS_CTRL)); -} - -int rk30_hdmi_detect_hotplug(void) -{ - int value = HDMIRdReg(HPD_MENS_STA); - - hdmi_dbg(hdmi->dev, "[%s] value %02x\n", __FUNCTION__, value); - value &= m_HOTPLUG_STATUS | m_MSEN_STATUS; - if(value == (m_HOTPLUG_STATUS | m_MSEN_STATUS) ) - return HDMI_HPD_ACTIVED; - else if(value) - return HDMI_HPD_INSERT; - else - return HDMI_HPD_REMOVED; -} - -#define HDMI_EDID_DDC_CLK 90000 -int rk30_hdmi_read_edid(int block, unsigned char *buff) -{ - int value, ret = -1, ddc_bus_freq = 0; - char interrupt = 0, trytime = 2; - unsigned long flags; - - hdmi_dbg(hdmi->dev, "[%s] block %d\n", __FUNCTION__, block); - spin_lock_irqsave(&hdmi->irq_lock, flags); - edid_result = 0; - spin_unlock_irqrestore(&hdmi->irq_lock, flags); - //Before Phy parameter was set, DDC_CLK is equal to PLLA freq which is 30MHz. - //Set DDC I2C CLK which devided from DDC_CLK to 100KHz. - ddc_bus_freq = (30000000/HDMI_EDID_DDC_CLK)/4; - HDMIWrReg(DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF); - HDMIWrReg(DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF); - - // Enable edid interrupt - HDMIWrReg(INTR_MASK1, m_INT_HOTPLUG | m_INT_MSENS | m_INT_EDID_ERR | m_INT_EDID_READY); - - while(trytime--) { - // Config EDID block and segment addr - HDMIWrReg(EDID_WORD_ADDR, (block%2) * 0x80); - HDMIWrReg(EDID_SEGMENT_POINTER, block/2); - - value = 100; - while(value--) - { - spin_lock_irqsave(&hdmi->irq_lock, flags); - interrupt = edid_result; - edid_result = 0; - spin_unlock_irqrestore(&hdmi->irq_lock, flags); - if(interrupt & (m_INT_EDID_ERR | m_INT_EDID_READY)) - break; - msleep(10); - } - hdmi_dbg(hdmi->dev, "[%s] edid read value %d\n", __FUNCTION__, value); - if(interrupt & m_INT_EDID_READY) - { - for(value = 0; value < HDMI_EDID_BLOCK_SIZE; value++) - buff[value] = HDMIRdReg(DDC_READ_FIFO_ADDR); - ret = 0; - - hdmi_dbg(hdmi->dev, "[%s] edid read sucess\n", __FUNCTION__); -#ifdef HDMI_DEBUG - for(value = 0; value < 128; value++) { - printk("%02x ,", buff[value]); - if( (value + 1) % 16 == 0) - printk("\n"); - } -#endif - break; - } - if(interrupt & m_INT_EDID_ERR) - hdmi_err(hdmi->dev, "[%s] edid read error\n", __FUNCTION__); - - } - // Disable edid interrupt - HDMIWrReg(INTR_MASK1, m_INT_HOTPLUG | m_INT_MSENS); -// msleep(100); - return ret; -} - -static inline void rk30_hdmi_config_phy_reg(int reg, int value) -{ - HDMIWrReg(reg, value); - HDMIWrReg(SYS_CTRL, 0x2C); - delay100us(); - HDMIWrReg(SYS_CTRL, 0x20); - msleep(1); -} - -static void rk30_hdmi_config_phy(unsigned char vic) -{ - HDMIWrReg(DEEP_COLOR_MODE, 0x22); // tmds frequency same as input dlck - rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_B); - switch(vic) - { - case HDMI_1920x1080p_60Hz: - case HDMI_1920x1080p_50Hz: - rk30_hdmi_config_phy_reg(0x158, 0x0E); - rk30_hdmi_config_phy_reg(0x15c, 0x00); - rk30_hdmi_config_phy_reg(0x160, 0x60); - rk30_hdmi_config_phy_reg(0x164, 0x00); - rk30_hdmi_config_phy_reg(0x168, 0xDA); - rk30_hdmi_config_phy_reg(0x16c, 0xA1); - rk30_hdmi_config_phy_reg(0x170, 0x0e); - rk30_hdmi_config_phy_reg(0x174, 0x22); - rk30_hdmi_config_phy_reg(0x178, 0x00); - break; - - case HDMI_1920x1080i_60Hz: - case HDMI_1920x1080i_50Hz: - case HDMI_1280x720p_60Hz: - case HDMI_1280x720p_50Hz: - rk30_hdmi_config_phy_reg(0x158, 0x06); - rk30_hdmi_config_phy_reg(0x15c, 0x00); - rk30_hdmi_config_phy_reg(0x160, 0x60); - rk30_hdmi_config_phy_reg(0x164, 0x00); - rk30_hdmi_config_phy_reg(0x168, 0xCA); - rk30_hdmi_config_phy_reg(0x16c, 0xA3); - rk30_hdmi_config_phy_reg(0x170, 0x0e); - rk30_hdmi_config_phy_reg(0x174, 0x20); - rk30_hdmi_config_phy_reg(0x178, 0x00); - break; - - case HDMI_720x576p_50Hz_4_3: - case HDMI_720x576p_50Hz_16_9: - case HDMI_720x480p_60Hz_4_3: - case HDMI_720x480p_60Hz_16_9: - rk30_hdmi_config_phy_reg(0x158, 0x02); - rk30_hdmi_config_phy_reg(0x15c, 0x00); - rk30_hdmi_config_phy_reg(0x160, 0x60); - rk30_hdmi_config_phy_reg(0x164, 0x00); - rk30_hdmi_config_phy_reg(0x168, 0xC2); - rk30_hdmi_config_phy_reg(0x16c, 0xA2); - rk30_hdmi_config_phy_reg(0x170, 0x0e); - rk30_hdmi_config_phy_reg(0x174, 0x20); - rk30_hdmi_config_phy_reg(0x178, 0x00); - break; - default: - hdmi_err(hdmi->dev, "not support such vic %d\n", vic); - break; - } -} - -static void rk30_hdmi_config_avi(unsigned char vic, unsigned char output_color) -{ - int i, clolorimetry, aspect_ratio; - 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]; - - if(output_color == VIDEO_OUTPUT_YCBCR444) - info[4] = (AVI_COLOR_MODE_YCBCR444 << 5); - else if(output_color == VIDEO_OUTPUT_YCBCR422) - info[4] = (AVI_COLOR_MODE_YCBCR422 << 5); - else - info[4] = (AVI_COLOR_MODE_RGB << 5); - info[4] |= (1 << 4); //Enable active format data bits is present in info[2] - - switch(vic) - { - case HDMI_720x480i_60Hz_4_3: - case HDMI_720x576i_50Hz_4_3: - case HDMI_720x480p_60Hz_4_3: - case HDMI_720x576p_50Hz_4_3: - aspect_ratio = AVI_CODED_FRAME_ASPECT_4_3; - clolorimetry = AVI_COLORIMETRY_SMPTE_170M; - break; - case HDMI_720x480i_60Hz_16_9: - case HDMI_720x576i_50Hz_16_9: - case HDMI_720x480p_60Hz_16_9: - case HDMI_720x576p_50Hz_16_9: - aspect_ratio = AVI_CODED_FRAME_ASPECT_16_9; - clolorimetry = AVI_COLORIMETRY_SMPTE_170M; - break; - default: - aspect_ratio = AVI_CODED_FRAME_ASPECT_16_9; - clolorimetry = AVI_COLORIMETRY_ITU709; - } - - if(output_color == VIDEO_OUTPUT_RGB444) - clolorimetry = AVI_COLORIMETRY_NO_DATA; - - info[5] = (clolorimetry << 6) | (aspect_ratio << 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_HB0 + i*4, info[i]); -} - -static char coeff_csc[][24] = { - //G B R Bias - { //CSC_RGB_0_255_TO_ITU601_16_235 - 0x11, 0xb6, 0x02, 0x0b, 0x10, 0x55, 0x00, 0x80, //Cr - 0x02, 0x59, 0x01, 0x32, 0x00, 0x75, 0x00, 0x10, //Y - 0x11, 0x5b, 0x10, 0xb0, 0x02, 0x0b, 0x00, 0x80, //Cb - }, - { //CSC_RGB_0_255_TO_ITU709_16_235 - 0x11, 0xdb, 0x02, 0x0b, 0x10, 0x30, 0x00, 0x80, //Cr - 0x02, 0xdc, 0x00, 0xda, 0x00, 0x4a, 0x00, 0x10, //Y - 0x11, 0x93, 0x10, 0x78, 0x02, 0x0b, 0x00, 0x80, //Cb - }, - //Y Cr Cb Bias - { //CSC_ITU601_16_235_TO_RGB_16_235 - 0x04, 0x00, 0x05, 0x7c, 0x00, 0x00, 0x02, 0xaf, //R - 0x04, 0x00, 0x12, 0xcb, 0x11, 0x58, 0x00, 0x84, //G - 0x04, 0x00, 0x00, 0x00, 0x06, 0xee, 0x02, 0xde, //B - }, - { //CSC_ITU709_16_235_TO_RGB_16_235 - 0x04, 0x00, 0x06, 0x29, 0x00, 0x00, 0x02, 0xc5, //R - 0x04, 0x00, 0x11, 0xd6, 0x10, 0xbb, 0x00, 0x52, //G - 0x04, 0x00, 0x00, 0x00, 0x07, 0x44, 0x02, 0xe8, //B - }, - { //CSC_ITU601_16_235_TO_RGB_0_255 - 0x04, 0xa8, 0x05, 0x7c, 0x00, 0x00, 0x02, 0xc2, //R - 0x04, 0xa8, 0x12, 0xcb, 0x11, 0x58, 0x00, 0x72, //G - 0x04, 0xa8, 0x00, 0x00, 0x06, 0xee, 0x02, 0xf0, //B - }, - { //CSC_ITU709_16_235_TO_RGB_0_255 - 0x04, 0xa8, 0x06, 0x29, 0x00, 0x00, 0x02, 0xd8, //R - 0x04, 0xa8, 0x11, 0xd6, 0x10, 0xbb, 0x00, 0x40, //G - 0x04, 0xa8, 0x00, 0x00, 0x07, 0x44, 0x02, 0xfb, //B - }, - -}; - -static void rk30_hdmi_config_csc(struct rk30_hdmi_video_para *vpara) -{ - int i, mode; - char *coeff = NULL; - - if( ((vpara->input_color == VIDEO_INPUT_COLOR_RGB) && (vpara->output_color == VIDEO_OUTPUT_RGB444)) || - ((vpara->input_color == VIDEO_INPUT_COLOR_YCBCR) && (vpara->output_color != VIDEO_OUTPUT_RGB444) )) - { - return; - } - switch(vpara->vic) - { - case HDMI_720x480i_60Hz_4_3: - case HDMI_720x576i_50Hz_4_3: - case HDMI_720x480p_60Hz_4_3: - case HDMI_720x576p_50Hz_4_3: - case HDMI_720x480i_60Hz_16_9: - case HDMI_720x576i_50Hz_16_9: - case HDMI_720x480p_60Hz_16_9: - case HDMI_720x576p_50Hz_16_9: - if(vpara->input_color == VIDEO_INPUT_COLOR_RGB) - mode = CSC_RGB_0_255_TO_ITU601_16_235; - else if(vpara->output_mode == OUTPUT_HDMI) - mode = CSC_ITU601_16_235_TO_RGB_16_235; - else - mode = CSC_ITU601_16_235_TO_RGB_0_255; - break; - default: - if(vpara->input_color == VIDEO_INPUT_COLOR_RGB) - mode = CSC_RGB_0_255_TO_ITU709_16_235; - else if(vpara->output_mode == OUTPUT_HDMI) - mode = CSC_ITU709_16_235_TO_RGB_16_235; - else - mode = CSC_ITU709_16_235_TO_RGB_0_255; - break; - } - - coeff = coeff_csc[mode]; - - HDMIWrReg(CSC_CONFIG1, v_CSC_MODE(CSC_MODE_MANUAL) | v_CSC_BRSWAP_DIABLE(1)); - - for(i = 0; i < 24; i++) - HDMIWrReg(CSC_PARA_C0_H + i*4, coeff[i]); - - HDMIWrReg(AV_CTRL2, v_CSC_ENABLE(1)); -} - -int rk30_hdmi_config_video(struct rk30_hdmi_video_para *vpara) -{ - int value; - struct fb_videomode *mode; - - hdmi_dbg(hdmi->dev, "[%s]\n", __FUNCTION__); - if(vpara == NULL) { - hdmi_err(hdmi->dev, "[%s] input parameter error\n", __FUNCTION__); - return -1; - } - if(hdmi->pwr_mode == PWR_SAVE_MODE_E) - rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_D); - if(hdmi->pwr_mode == PWR_SAVE_MODE_D || hdmi->pwr_mode == PWR_SAVE_MODE_A) - rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_B); - - if(hdmi->hdcp_power_off_cb) - hdmi->hdcp_power_off_cb(); - - // Input video mode is RGB24bit, Data enable signal from external - HDMIMskReg(value, AV_CTRL1, m_INPUT_VIDEO_MODE | m_DE_SIGNAL_SELECT, \ - v_INPUT_VIDEO_MODE(vpara->input_mode) | EXTERNAL_DE) - HDMIMskReg(value, VIDEO_CTRL1, m_VIDEO_OUTPUT_MODE | m_VIDEO_INPUT_DEPTH | m_VIDEO_INPUT_COLOR_MODE, \ - v_VIDEO_OUTPUT_MODE(vpara->output_color) | v_VIDEO_INPUT_DEPTH(VIDEO_INPUT_DEPTH_8BIT) | vpara->input_color) - HDMIWrReg(DEEP_COLOR_MODE, 0x20); - // color space convert - rk30_hdmi_config_csc(vpara); - // Set HDMI Mode - HDMIWrReg(HDCP_CTRL, v_HDMI_DVI(vpara->output_mode)); - - // Set ext video - 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; - - if( (vpara->vic == HDMI_720x480p_60Hz_4_3) || (vpara->vic == HDMI_720x480p_60Hz_16_9) ) - value = v_VSYNC_OFFSET(6); - else - value = v_VSYNC_OFFSET(0); - value |= v_EXT_VIDEO_ENABLE(1) | v_INTERLACE(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(EXT_VIDEO_PARA, value); - value = mode->left_margin + mode->xres + mode->right_margin + mode->hsync_len; - HDMIWrReg(EXT_VIDEO_PARA_HTOTAL_L, value & 0xFF); - HDMIWrReg(EXT_VIDEO_PARA_HTOTAL_H, (value >> 8) & 0xFF); - - value = mode->left_margin + mode->right_margin + mode->hsync_len; - HDMIWrReg(EXT_VIDEO_PARA_HBLANK_L, value & 0xFF); - HDMIWrReg(EXT_VIDEO_PARA_HBLANK_H, (value >> 8) & 0xFF); - - value = mode->left_margin + mode->hsync_len; - HDMIWrReg(EXT_VIDEO_PARA_HDELAY_L, value & 0xFF); - HDMIWrReg(EXT_VIDEO_PARA_HDELAY_H, (value >> 8) & 0xFF); - - value = mode->hsync_len; - HDMIWrReg(EXT_VIDEO_PARA_HSYNCWIDTH_L, value & 0xFF); - HDMIWrReg(EXT_VIDEO_PARA_HSYNCWIDTH_H, (value >> 8) & 0xFF); - - value = mode->upper_margin + mode->yres + mode->lower_margin + mode->vsync_len; - HDMIWrReg(EXT_VIDEO_PARA_VTOTAL_L, value & 0xFF); - HDMIWrReg(EXT_VIDEO_PARA_VTOTAL_H, (value >> 8) & 0xFF); - - value = mode->upper_margin + mode->vsync_len + mode->lower_margin; - HDMIWrReg(EXT_VIDEO_PARA_VBLANK_L, 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(EXT_VIDEO_PARA_VDELAY, value & 0xFF); - - value = mode->vsync_len; - HDMIWrReg(EXT_VIDEO_PARA_VSYNCWIDTH, value & 0xFF); - - if(vpara->output_mode == OUTPUT_HDMI) { - rk30_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__); - } - - rk30_hdmi_config_phy(vpara->vic); - rk30_hdmi_control_output(0); - return 0; -} - -static void rk30_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_HB0 + i*4, info[i]); -} - -int rk30_hdmi_config_audio(struct hdmi_audio *audio) -{ - int value, rate, N; - char word_length, channel; - - 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; - break; - case HDMI_AUDIO_FS_44100: - rate = AUDIO_441K; - N = N_441K; - break; - case HDMI_AUDIO_FS_48000: - rate = AUDIO_48K; - N = N_48K; - break; - case HDMI_AUDIO_FS_88200: - rate = AUDIO_882K; - N = N_882K; - break; - case HDMI_AUDIO_FS_96000: - rate = AUDIO_96K; - N = N_96K; - break; - case HDMI_AUDIO_FS_176400: - rate = AUDIO_1764K; - N = N_1764K; - break; - case HDMI_AUDIO_FS_192000: - rate = AUDIO_192K; - N = N_192K; - break; - default: - hdmi_err(hdmi->dev, "[%s] not support such sample rate %d\n", __FUNCTION__, audio->rate); - return -ENOENT; - } -// switch(audio->word_length) -// { -// case HDMI_AUDIO_WORD_LENGTH_16bit: -// word_length = 0x02; -// break; -// case HDMI_AUDIO_WORD_LENGTH_20bit: -// word_length = 0x0a; -// break; -// case HDMI_AUDIO_WORD_LENGTH_24bit: -// word_length = 0x0b; -// break; -// default: -// hdmi_err(hdmi->dev, "[%s] not support such word length %d\n", __FUNCTION__, audio->word_length); -// return -ENOENT; -// } - //set_audio_if I2S - HDMIWrReg(AUDIO_CTRL1, 0x00); //internal CTS, disable down sample, i2s input, disable MCLK - HDMIWrReg(AUDIO_CTRL2, 0x40); - HDMIWrReg(I2S_AUDIO_CTRL, v_I2S_MODE(I2S_MODE_STANDARD) | v_I2S_CHANNEL(channel) ); - HDMIWrReg(I2S_INPUT_SWAP, 0x00); //no swap - HDMIMskReg(value, AV_CTRL1, m_AUDIO_SAMPLE_RATE, v_AUDIO_SAMPLE_RATE(rate)) -// HDMIWrReg(SRC_NUM_AUDIO_LEN, word_length); - - //Set N value 6144, fs=48kHz - HDMIWrReg(N_1, N & 0xFF); - HDMIWrReg(N_2, (N >> 8) & 0xFF); - HDMIWrReg(LR_SWAP_N3, (N >> 16) & 0x0F); - - rk30_hdmi_config_aai(); - return 0; -} - -static void rk30_hdmi_audio_reset(void) -{ - int value; - - HDMIMskReg(value, VIDEO_SETTING2, m_AUDIO_RESET, AUDIO_CAPTURE_RESET) - msleep(1); - HDMIMskReg(value, VIDEO_SETTING2, m_AUDIO_RESET, 0) -} - -void rk30_hdmi_control_output(int enable) -{ - hdmi_dbg(hdmi->dev, "[%s] %d\n", __FUNCTION__, enable); - if(enable == 0) { - HDMIWrReg(VIDEO_SETTING2, 0x03); - } - else { - if(hdmi->pwr_mode == PWR_SAVE_MODE_B) { - // Switch to power save mode_d - rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_D); - } - if(hdmi->pwr_mode == PWR_SAVE_MODE_D) { - // Switch to power save mode_e - rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_E); - } - HDMIWrReg(VIDEO_SETTING2, 0x00); - rk30_hdmi_audio_reset(); - } -} - -int rk30_hdmi_removed(void) -{ - if(hdmi->pwr_mode == PWR_SAVE_MODE_E) - { - HDMIWrReg(VIDEO_SETTING2, 0x00); - rk30_hdmi_audio_reset(); - rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_D); - } - if(hdmi->pwr_mode == PWR_SAVE_MODE_D) - rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_B); - if(hdmi->pwr_mode == PWR_SAVE_MODE_B && hdmi->state == HDMI_SLEEP) - { - HDMIWrReg(INTR_MASK1, m_INT_HOTPLUG | m_INT_MSENS); - HDMIWrReg(INTR_MASK2, 0); - HDMIWrReg(INTR_MASK3, 0); - HDMIWrReg(INTR_MASK4, 0); - // Disable color space convertion - HDMIWrReg(AV_CTRL2, v_CSC_ENABLE(0)); - HDMIWrReg(CSC_CONFIG1, v_CSC_MODE(CSC_MODE_AUTO) | v_CSC_BRSWAP_DIABLE(1)); - if(hdmi->hdcp_power_off_cb) - hdmi->hdcp_power_off_cb(); - rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_A); - } - dev_printk(KERN_INFO , hdmi->dev , "Removed.\n"); - return HDMI_ERROR_SUCESS; -} - - -irqreturn_t hdmi_irq(int irq, void *priv) -{ - char interrupt1 = 0, interrupt2 = 0, interrupt3 = 0, interrupt4 = 0; - - if(hdmi->pwr_mode == PWR_SAVE_MODE_A) - { - HDMIWrReg(SYS_CTRL, 0x20); - hdmi->pwr_mode = PWR_SAVE_MODE_B; - - hdmi_dbg(hdmi->dev, "hdmi irq wake up\n"); - // HDMI was inserted when system is sleeping, irq was triggered only once - // when wake up. So we need to check hotplug status. - if(HDMIRdReg(HPD_MENS_STA) & (m_HOTPLUG_STATUS | m_MSEN_STATUS)) { - queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, msecs_to_jiffies(10)); - } - } - else - { - interrupt1 = HDMIRdReg(INTR_STATUS1); - interrupt2 = HDMIRdReg(INTR_STATUS2); - interrupt3 = HDMIRdReg(INTR_STATUS3); - interrupt4 = HDMIRdReg(INTR_STATUS4); - HDMIWrReg(INTR_STATUS1, interrupt1); - HDMIWrReg(INTR_STATUS2, interrupt2); - HDMIWrReg(INTR_STATUS3, interrupt3); - HDMIWrReg(INTR_STATUS4, interrupt4); -#if 0 - hdmi_dbg(hdmi->dev, "[%s] interrupt1 %02x interrupt2 %02x interrupt3 %02x interrupt4 %02x\n",\ - __FUNCTION__, interrupt1, interrupt2, interrupt3, interrupt4); -#endif - if(interrupt1 & (m_INT_HOTPLUG | m_INT_MSENS)) - { - if(hdmi->state == HDMI_SLEEP) - hdmi->state = WAIT_HOTPLUG; - interrupt1 &= ~(m_INT_HOTPLUG | m_INT_MSENS); - queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, msecs_to_jiffies(10)); - } - else if(interrupt1 & (m_INT_EDID_READY | m_INT_EDID_ERR)) { - spin_lock(&hdmi->irq_lock); - edid_result = interrupt1; - spin_unlock(&hdmi->irq_lock); - } - else if(hdmi->state == HDMI_SLEEP) { - hdmi_dbg(hdmi->dev, "hdmi return to sleep mode\n"); - HDMIWrReg(SYS_CTRL, 0x10); - hdmi->pwr_mode = PWR_SAVE_MODE_A; - } - if(interrupt2 && hdmi->hdcp_irq_cb) - hdmi->hdcp_irq_cb(interrupt2); - } - return IRQ_HANDLED; -} - diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_hw.h b/drivers/video/rockchip/hdmi/rk30_hdmi_hw.h deleted file mode 100755 index 6a5fc3368a62..000000000000 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_hw.h +++ /dev/null @@ -1,446 +0,0 @@ -#ifndef __RK30_HDMI_HW_H__ -#define __RK30_HDMI_HW_H__ - -/* HDMI_SYS_CONTROL */ -#define SYS_CTRL 0x0 - -enum { - PWR_SAVE_MODE_A = 1, - PWR_SAVE_MODE_B = 2, - PWR_SAVE_MODE_D = 4, - PWR_SAVE_MODE_E = 8 -}; -#define m_PWR_SAVE_MODE 0xF0 -#define v_PWR_SAVE_MODE(n) (n << 4) -#define PLL_B_RESET (1 << 3) - -#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 LR_SWAP_N3 0x04 -#define N_2 0x08 -#define N_1 0x0c - -#define AUDIO_CTRL1 0x28 -#define AUDIO_CTRL2 0x2c -#define I2S_AUDIO_CTRL 0x30 -enum { - I2S_MODE_STANDARD = 0, - I2S_MODE_RIGHT_JUSTIFIED, - I2S_MODE_LEFT_JUSTIFIED -}; -#define v_I2S_MODE(n) n -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 ) - -#define I2S_INPUT_SWAP 0x40 - -#define SRC_NUM_AUDIO_LEN 0x50 - -/* HDMI_AV_CTRL1*/ -#define AV_CTRL1 0x54 -enum { - AUDIO_32K = 0x3, - AUDIO_441K = 0x0, - AUDIO_48K = 0x2, - AUDIO_882K = 0x8, - AUDIO_96K = 0xa, - AUDIO_1764K = 0xc, - AUDIO_192K = 0xe, -}; -#define m_AUDIO_SAMPLE_RATE 0xF0 -#define v_AUDIO_SAMPLE_RATE(n) (n << 4) -enum { - VIDEO_INPUT_RGB_YCBCR_444 = 0, - VIDEO_INPUT_YCBCR422, - VIDEO_INPUT_YCBCR422_EMBEDDED_SYNC, - VIDEO_INPUT_2X_CLOCK, - VIDEO_INPUT_2X_CLOCK_EMBEDDED_SYNC, - VIDEO_INPUT_RGB444_DDR, - VIDEO_INPUT_YCBCR422_DDR -}; -#define m_INPUT_VIDEO_MODE (7 << 1) -#define v_INPUT_VIDEO_MODE(n) (n << 1) -enum { - INTERNAL_DE = 0, - EXTERNAL_DE -}; -#define m_DE_SIGNAL_SELECT (1 << 0) - -/* HDMI_AV_CTRL2 */ -#define AV_CTRL2 0xec -#define m_CSC_ENABLE (1 << 0) -#define v_CSC_ENABLE(n) (n) - -/* HDMI_VIDEO_CTRL1 */ -#define VIDEO_CTRL1 0x58 -enum { - VIDEO_OUTPUT_RGB444 = 0, - VIDEO_OUTPUT_YCBCR444, - VIDEO_OUTPUT_YCBCR422 -}; -#define m_VIDEO_OUTPUT_MODE (0x3 << 6) -#define v_VIDEO_OUTPUT_MODE(n) (n << 6) -enum { - VIDEO_INPUT_DEPTH_12BIT = 0, - VIDEO_INPUT_DEPTH_10BIT = 0x1, - VIDEO_INPUT_DEPTH_8BIT = 0x3 -}; -#define m_VIDEO_INPUT_DEPTH (3 << 4) -#define v_VIDEO_INPUT_DEPTH(n) (n << 4) -enum { - VIDEO_EMBEDDED_SYNC_LOCATION_0 = 0, - VIDEO_EMBEDDED_SYNC_LOCATION_1, - VIDEO_EMBEDDED_SYNC_LOCATION_2 -}; -#define m_VIDEO_EMBEDDED_SYNC_LOCATION (3 << 2) -#define VIDEO_EMBEDDED_SYNC_LOCATION(n) (n << 2) -enum { - VIDEO_INPUT_COLOR_RGB = 0, - VIDEO_INPUT_COLOR_YCBCR -}; -#define m_VIDEO_INPUT_COLOR_MODE (1 << 0) - -/* DEEP_COLOR_MODE */ -#define DEEP_COLOR_MODE 0x5c -enum{ - TMDS_CLOCK_MODE_8BIT = 0, - TMDS_CLOKK_MODE_10BIT, - TMDS_CLOKK_MODE_12BIT -}; -#define TMDS_CLOCK_MODE_MASK 0x3 << 6 -#define TMDS_CLOCK_MODE(n) (n) << 6 - -/* VIDEO_CTRL2 */ -#define VIDEO_SETTING2 0x114 -#define m_UNMUTE (1 << 7) -#define m_MUTE (1 << 6) -#define m_AUDIO_RESET (1 << 2) -#define m_NOT_SEND_AUDIO (1 << 1) -#define m_NOT_SEND_VIDEO (1 << 0) -#define AV_UNMUTE (1 << 7) // Unmute video and audio, send normal video and audio data -#define AV_MUTE (1 << 6) // Mute video and audio, send black video data and silent audio data -#define AUDIO_CAPTURE_RESET (1 << 2) // Reset audio process logic, only available in pwr_e mode. -#define NOT_SEND_AUDIO (1 << 1) // Send silent audio data -#define NOT_SEND_VIDEO (1 << 0) // Send black video data - -/* Color Space Convertion Parameter*/ -#define CSC_PARA_C0_H 0x60 -#define CSC_PARA_C0_L 0x64 -#define CSC_PARA_C1_H 0x68 -#define CSC_PARA_C1_L 0x6c -#define CSC_PARA_C2_H 0x70 -#define CSC_PARA_C2_L 0x74 -#define CSC_PARA_C3_H 0x78 -#define CSC_PARA_C3_L 0x7c -#define CSC_PARA_C4_H 0x80 -#define CSC_PARA_C4_L 0x84 -#define CSC_PARA_C5_H 0x88 -#define CSC_PARA_C5_L 0x8c -#define CSC_PARA_C6_H 0x90 -#define CSC_PARA_C6_L 0x94 -#define CSC_PARA_C7_H 0x98 -#define CSC_PARA_C7_L 0x9c -#define CSC_PARA_C8_H 0xa0 -#define CSC_PARA_C8_L 0xa4 -#define CSC_PARA_C9_H 0xa8 -#define CSC_PARA_C9_L 0xac -#define CSC_PARA_C10_H 0xac -#define CSC_PARA_C10_L 0xb4 -#define CSC_PARA_C11_H 0xb8 -#define CSC_PARA_C11_L 0xbc - -#define CSC_CONFIG1 0x34c -#define m_CSC_MODE (1 << 7) -#define m_CSC_COEF_MODE (0xF << 3) //Only used in auto csc mode -#define m_CSC_STATUS (1 << 2) -#define m_CSC_VID_SELECT (1 << 1) -#define m_CSC_BRSWAP_DIABLE (1) - -enum { - CSC_MODE_MANUAL = 0, - CSC_MODE_AUTO -}; -#define v_CSC_MODE(n) (n << 7) -enum { - COE_SDTV_LIMITED_RANGE = 0x08, - COE_SDTV_FULL_RANGE = 0x04, - COE_HDTV_60Hz = 0x2, - COE_HDTV_50Hz = 0x1 -}; -#define v_CSC_COE_MODE(n) (n << 3) -enum { - CSC_INPUT_VID_5_19 = 0, - CSC_INPUT_VID_28_29 -}; -#define v_CSC_VID_SELECT(n) (n << 1) -#define v_CSC_BRSWAP_DIABLE(n) (n) - -/* CONTROL_PACKET_BUF_INDEX */ -#define CONTROL_PACKET_BUF_INDEX 0x17c -enum { - INFOFRAME_AVI = 0x06, - INFOFRAME_AAI = 0x08 -}; -#define CONTROL_PACKET_HB0 0x180 -#define CONTROL_PACKET_HB1 0x184 -#define CONTROL_PACKET_HB2 0x188 -#define CONTROL_PACKET_PB_ADDR 0x18c -#define SIZE_AVI_INFOFRAME 0x11 // 17 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 -}; - -/* External Video Parameter Setting*/ -#define EXT_VIDEO_PARA 0xC0 -#define m_VSYNC_OFFSET (0xF << 4) -#define m_VSYNC_POLARITY (1 << 3) -#define m_HSYNC_POLARITY (1 << 2) -#define m_INTERLACE (1 << 1) -#define m_EXT_VIDEO_ENABLE (1 << 0) - -#define v_VSYNC_OFFSET(n) (n << 4) -#define v_VSYNC_POLARITY(n) (n << 3) -#define v_HSYNC_POLARITY(n) (n << 2) -#define v_INTERLACE(n) (n << 1) -#define v_EXT_VIDEO_ENABLE(n) (n << 0) - -#define EXT_VIDEO_PARA_HTOTAL_L 0xC4 -#define EXT_VIDEO_PARA_HTOTAL_H 0xC8 -#define EXT_VIDEO_PARA_HBLANK_L 0xCC -#define EXT_VIDEO_PARA_HBLANK_H 0xD0 -#define EXT_VIDEO_PARA_HDELAY_L 0xD4 -#define EXT_VIDEO_PARA_HDELAY_H 0xD8 -#define EXT_VIDEO_PARA_HSYNCWIDTH_L 0xDC -#define EXT_VIDEO_PARA_HSYNCWIDTH_H 0xE0 - -#define EXT_VIDEO_PARA_VTOTAL_L 0xE4 -#define EXT_VIDEO_PARA_VTOTAL_H 0xE8 -#define EXT_VIDEO_PARA_VBLANK_L 0xF4 -#define EXT_VIDEO_PARA_VDELAY 0xF8 -#define EXT_VIDEO_PARA_VSYNCWIDTH 0xFC - -#define PHY_PLL_SPEED 0x158 - #define v_TEST_EN(n) (n << 6) - #define v_PLLA_BYPASS(n) (n << 4) - #define v_PLLB_SPEED(n) (n << 2) - #define v_PLLA_SPEED(n) (n) - enum { - PLL_SPEED_LOWEST = 0, - PLL_SPEED_MIDLOW, - PLL_SPEED_MIDHIGH, - PLL_SPEED_HIGHEST - }; - -#define PHY_PLL_17 0x15c // PLL A & B config bit 17 - #define v_PLLA_BIT17(n) (n << 2) - #define v_PLLB_BIT17(n) (n << 1) - -#define PHY_BGR 0x160 - #define v_BGR_DISCONNECT(n) (n << 7) - #define v_BGR_V_OFFSET(n) (n << 4) - #define v_BGR_I_OFFSET(n) (n) - -#define PHY_PLLA_1 0x164 -#define PHY_PLLA_2 0x168 -#define PHY_PLLB_1 0x16c -#define PHY_PLLB_2 0x170 - -#define PHY_DRIVER_PREEMPHASIS 0x174 - #define v_TMDS_SWING(n) (n << 4) - #define v_PRE_EMPHASIS(n) (n) - -#define PHY_PLL_16_AML 0x178 // PLL A & B config bit 16 and AML control - #define v_PLLA_BIT16(n) (n << 5) - #define v_PLLB_BIT16(n) (n << 4) - #define v_AML(n) (n) - -/* Interrupt Setting */ -#define INTR_MASK1 0x248 -#define INTR_STATUS1 0x250 - #define m_INT_HOTPLUG (1 << 7) - #define m_INT_MSENS (1 << 6) - #define m_INT_VSYNC (1 << 5) - #define m_INT_AUDIO_FIFO_FULL (1 << 4) - #define m_INT_EDID_READY (1 << 2) - #define m_INT_EDID_ERR (1 << 1) -#define INTR_MASK2 0x24c -#define INTR_STATUS2 0x254 - #define m_INT_HDCP_ERR (1 << 7) // HDCP error detected - #define m_INT_BKSV_RPRDY (1 << 6) // BKSV list ready from repeater - #define m_INT_BKSV_RCRDY (1 << 5) // BKSV list ready from receiver - #define m_INT_AUTH_DONE (1 << 4) // HDCP authentication done - #define m_INT_AUTH_READY (1 << 3) // HDCP authentication ready -#define INTR_MASK3 0x258 -#define INTR_STATUS3 0x260 - -#define INTR_MASK4 0x25c -#define INTR_STATUS4 0x264 - -#define DDC_READ_FIFO_ADDR 0x200 -#define DDC_BUS_FREQ_L 0x204 -#define DDC_BUS_FREQ_H 0x208 -#define DDC_BUS_CTRL 0x2dc -#define DDC_I2C_LEN 0x278 -#define DDC_I2C_OFFSET 0x280 -#define DDC_I2C_CTRL 0x284 -#define DDC_I2C_READ_BUF0 0x288 -#define DDC_I2C_READ_BUF1 0x28c -#define DDC_I2C_READ_BUF2 0x290 -#define DDC_I2C_READ_BUF3 0x294 -#define DDC_I2C_WRITE_BUF0 0x298 -#define DDC_I2C_WRITE_BUF1 0x29c -#define DDC_I2C_WRITE_BUF2 0x2a0 -#define DDC_I2C_WRITE_BUF3 0x2a4 -#define DDC_I2C_WRITE_BUF4 0x2ac -#define DDC_I2C_WRITE_BUF5 0x2b0 -#define DDC_I2C_WRITE_BUF6 0x2b4 - -#define EDID_SEGMENT_POINTER 0x310 -#define EDID_WORD_ADDR 0x314 -#define EDID_FIFO_ADDR 0x318 - -#define HPD_MENS_STA 0x37c -#define m_HOTPLUG_STATUS (1 << 7) -#define m_MSEN_STATUS (1 << 6) - -/* HDCP_CTRL */ -#define HDCP_CTRL 0x2bc - enum { - OUTPUT_DVI = 0, - OUTPUT_HDMI - }; - #define m_HDCP_AUTH_START (1 << 7) // start hdcp - #define m_HDCP_BKSV_PASS (1 << 6) // bksv valid - #define m_HDCP_BKSV_FAILED (1 << 5) // bksv invalid - #define m_HDCP_FRAMED_ENCRYPED (1 << 4) - #define m_HDCP_AUTH_STOP (1 << 3) // stop hdcp - #define m_HDCP_ADV_CIPHER (1 << 2) // advanced cipher mode - #define m_HDMI_DVI (1 << 1) - #define m_HDCP_RESET (1 << 0) // reset hdcp - #define v_HDCP_AUTH_START(n) (n << 7) - #define v_HDCP_BKSV_PASS(n) (n << 6) - #define v_HDCP_BKSV_FAILED(n) (n << 5) - #define v_HDCP_FRAMED_ENCRYPED(n) (n << 4) - #define v_HDCP_AUTH_STOP(n) (n << 3) - #define v_HDCP_ADV_CIPHER(n) (n << 2) - #define v_HDMI_DVI(n) (n << 1) - #define v_HDCP_RESET(n) (n << 0) -#define HDCP_CTRL2 0x340 - -/* HDCP Key Memory Access Control */ -#define HDCP_KEY_ACCESS_CTRL1 0x338 -#define HDCP_KEY_ACCESS_CTRL2 0x33c - #define m_LOAD_FACSIMILE_HDCP_KEY (1 << 1) - #define m_LOAD_HDCP_KEY (1 << 0) -/* HDCP Key Memory Control */ -#define HDCP_KEY_MEM_CTRL 0x348 - #define m_USE_KEY1 (1 << 6) - #define m_USE_KEY2 (1 << 5) - #define m_LOAD_AKSV (1 << 4) - #define m_KSV_SELECTED (1 << 3) - #define m_KSV_VALID (1 << 2) - #define m_KEY_VALID (1 << 1) - #define m_KEY_READY (1 << 0) - #define v_USE_KEY1(n) (n << 6) - #define v_USE_KEY2(n) (n << 5) - #define v_LOAD_AKSV(n) (n << 4) - -/* HDCP B device capacity */ -#define HDCP_BCAPS 0x2f8 - #define m_HDMI_RECEIVED (1 << 7) //If HDCP receiver support HDMI, this bit must be 1. - #define m_REPEATER (1 << 6) - #define m_KSV_FIFO_READY (1 << 5) - #define m_DDC_FAST (1 << 4) - #define m_1_1_FEATURE (1 << 1) - #define m_FAST_REAUTHENTICATION (1 << 0) //For HDMI, this function is supported whether this bit is enabled or not. - -/* HDCP KSV Value */ -#define HDCP_KSV_BYTE0 0x2fc -#define HDCP_KSV_BYTE1 0x300 -#define HDCP_KSV_BYTE2 0x304 -#define HDCP_KSV_BYTE3 0x308 -#define HDCP_KSV_BYTE4 0x30c - -/* HDCP error status */ -#define HDCP_ERROR 0x320 - -/* HDCP 100 ms timer */ -#define HDCP_TIMER_100MS 0x324 -/* HDCP 5s timer */ -#define HDCP_TIMER_5S 0x328 - -/* HDCP Key ram address */ -#define HDCP_RAM_KEY_KSV1 0x400 -#define HDCP_RAM_KEY_KSV2 0x407 -#define HDCP_RAM_KEY_PRIVATE 0x40e -#define HDCP_KEY_LENGTH 0x13C - - -#define HDCP_ENABLE_HW_AUTH // Enable hardware authentication mode -#define HDMI_INTERANL_CLK_DIV 0x19 - -#define HDMIRdReg(addr) __raw_readl(hdmi->regbase + addr) -#define HDMIWrReg(addr, val) __raw_writel((val), hdmi->regbase + addr); -#define HDMIMskReg(temp, addr, msk, val) \ - temp = __raw_readl(hdmi->regbase + addr) & (0xFF - (msk)) ; \ - __raw_writel(temp | ( (val) & (msk) ), hdmi->regbase + addr); - -/* RK30 HDMI Video Configure Parameters */ -struct rk30_hdmi_video_para { - int vic; - int input_mode; //input video data interface - int input_color; //input video color mode - int output_mode; //output hdmi or dvi - int output_color; //output video color mode -}; - -/* Color Space Convertion Mode */ -enum { - CSC_RGB_0_255_TO_ITU601_16_235 = 0, //RGB 0-255 input to YCbCr 16-235 output according BT601 - CSC_RGB_0_255_TO_ITU709_16_235, //RGB 0-255 input to YCbCr 16-235 output accroding BT709 - CSC_ITU601_16_235_TO_RGB_16_235, //YCbCr 16-235 input to RGB 16-235 output according BT601 - CSC_ITU709_16_235_TO_RGB_16_235, //YCbCr 16-235 input to RGB 16-235 output according BT709 - CSC_ITU601_16_235_TO_RGB_0_255, //YCbCr 16-235 input to RGB 0-255 output according BT601 - CSC_ITU709_16_235_TO_RGB_0_255 //YCbCr 16-235 input to RGB 0-255 output according BT709 -}; - -extern int rk30_hdmi_initial(void); -extern int rk30_hdmi_detect_hotplug(void); -extern int rk30_hdmi_read_edid(int block, unsigned char *buff); -extern int rk30_hdmi_removed(void); -extern int rk30_hdmi_config_video(struct rk30_hdmi_video_para *vpara); -extern int rk30_hdmi_config_audio(struct hdmi_audio *audio); -extern void rk30_hdmi_control_output(int enable); - -#endif \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_lcdc.c b/drivers/video/rockchip/hdmi/rk30_hdmi_lcdc.c deleted file mode 100755 index 406837e2e200..000000000000 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_lcdc.c +++ /dev/null @@ -1,527 +0,0 @@ -#include -#include "rk30_hdmi.h" -#include "rk30_hdmi_hw.h" -#include - -#define OUT_TYPE SCREEN_HDMI -#define OUT_FACE OUT_P888 -#define DCLK_POL 1 -#define SWAP_RB 0 -#define LCD_ACLK 800000000 - -const struct fb_videomode hdmi_mode [] = { - //name refresh xres yres pixclock h_bp h_fp v_bp v_fp h_pw v_pw polariry PorI flag(used for vic) -//{ "640x480p@60Hz", 60, 640, 480, 25175000, 48, 16, 33, 10, 96, 2, 0, 0, 1 }, -//{ "720x480i@60Hz", 60, 720, 480, 27000000, 114, 38, 15, 4, 124, 3, 0, 1, 6 }, -//{ "720x576i@50Hz", 50, 720, 576, 27000000, 138, 24, 19, 2, 126, 3, 0, 1, 21 }, -{ "720x480p@60Hz", 60, 720, 480, 27000000, 60, 16, 30, 9, 62, 6, 0, 0, 2 }, -{ "720x576p@50Hz", 50, 720, 576, 27000000, 68, 12, 39, 5, 64, 5, 0, 0, 17 }, -//{ "1280x720p@24Hz", 24, 1280, 720, 59400000, 220, 1760, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 60 }, -//{ "1280x720p@25Hz", 25, 1280, 720, 74250000, 220, 2420, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 61 }, -//{ "1280x720p@30Hz", 30, 1280, 720, 74250000, 220, 1760, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 62 }, -{ "1280x720p@50Hz", 50, 1280, 720, 74250000, 220, 440, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 19 }, -{ "1280x720p@60Hz", 60, 1280, 720, 74250000, 220, 110, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 4 }, -//{ "1920x1080p@24Hz", 24, 1920, 1080, 74250000, 148, 638, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 32 }, -//{ "1920x1080p@25Hz", 25, 1920, 1080, 74250000, 148, 528, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 33 }, -//{ "1920x1080p@30Hz", 30, 1920, 1080, 74250000, 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 34 }, -//{ "1920x1080i@50Hz_2",50, 1920, 1080, 72000000, 184, 32, 57, 23, 168, 5, FB_SYNC_HOR_HIGH_ACT, 1, 39 }, -//{ "1920x1080i@50Hz", 50, 1920, 1080, 74250000, 148, 528, 15, 2, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 1, 20 }, -//{ "1920x1080i@60Hz", 60, 1920, 1080, 74250000, 148, 88, 15, 2, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 1, 5 }, -{ "1920x1080p@50Hz", 50, 1920, 1080, 148500000, 148, 528, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 31 }, -{ "1920x1080p@60Hz", 60, 1920, 1080, 148500000, 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 16 }, -/* -{ "1440x288p@50Hz", 50, 720, 480, 27000000, 138, 24, 19, 2, 126, 3, 0, 0, 23 }, -{ "2880x576i@50Hz", 50, 1440, 240, 54000000, 276, 48, 19, 2, 252, 3, 0, 1, 25 }, -{ "2880x288p@50Hz", 50, 2880, 480, 54000000, 276, 48, 19, 3, 252, 3, 0, 0, 27 }, -{ "1440x576p@50Hz", 50, 2880, 480, 54000000, 136, 24, 39, 5, 128, 5, 0, 0, 29 }, -{ "2880x576p@50Hz", 50, 1920, 1080, 108000000, 272, 48, 39, 5, 256, 5, 0, 0, 37 }, -{ "1440x240p@60Hz", 60, 1440, 240, 27000000, 114, 38, 15, 4, 124, 3, 0, 0, 8 }, -{ "2880x480i@60Hz", 60, 2880, 480, 54000000, 228, 76, 15, 4, 248, 3, 0, 1, 10 }, -{ "2880x480p@60Hz", 60, 2880, 480, 54000000, 228, 76, 15, 4, 248, 3, 0, 0, 12 }, -{ "1440x480p@60Hz", 60, 1440, 480, 54000000, 120, 32, 30, 9, 124, 6, 0, 0, 14 }, -{ "2880x480p@60Hz", 60, 2880, 480, 54000000, 240, 64, 30, 9, 248, 6, 0, 0, 35 }, - -{ "1920x1080i@100Hz", 100, 1920, 1080, 148500000, 148, 528, 15, 2, 44, 5, 1, 1, 40 }, -{ "1280x720p@100Hz", 100, 1280, 720, 148500000, 220, 440, 20, 5, 40, 5, 1, 0, 41 }, -{ "720x576p@100Hz", 100, 720, 576, 54000000, 68, 12, 39, 5, 64, 5, 0, 0, 42 }, -{ "1440x576i@100Hz", 100, 1440, 576, 54000000, 138, 24, 19, 2, 12, 3, 0, 1, 44 }, -{ "1920x1080p@100Hz", 100, 1920, 1080, 297000000, 148, 528, 36, 4, 44, 5, 1, 0, 64 }, - -{ "1920x1080i@120Hz", 120, 1920, 1080, 148500000, 148, 88, 15, 2, 44, 5, 1, 1, 46 }, -{ "1280x720p@120Hz", 120, 1280, 720, 148500000, 220, 110, 20, 5, 40, 5, 1, 0, 47 }, -{ "720x480p@120Hz", 120, 720, 480, 54000000, 60, 16, 30, 9, 62, 6, 0, 0, 48 }, -{ "1440x480i@120Hz", 120, 1440, 480, 54000000, 114, 38, 15, 4, 12, 3, 0, 1, 50 }, -{ "1920x1080p@120Hz", 120, 1920, 1080, 297000000, 148, 88, 36, 4, 44, 5, 1, 0, 63 }, - -{ "720x576p@200Hz", 200, 720, 576, 108000000, 68, 12, 39, 5, 64, 5, 0, 0, 52 }, -{ "1440x576i@200Hz", 200, 1920, 1080, 108000000, 138, 24, 19, 2, 12, 3, 0, 1, 54 }, - -{ "720x480p@240Hz", 240, 720, 480, 108000000, 60, 16, 30, 9, 62, 6, 0, 0, 56 }, -{ "1440x480i@240Hz", 240, 1440, 480, 108000000, 114, 38, 15, 4, 12, 3, 0, 1, 58 }, -*/ - -}; - -void hdmi_init_lcdc(struct rk29fb_screen *screen, struct rk29lcd_info *lcd_info) -{ - hdmi_set_info(screen, HDMI_VIDEO_DEFAULT_MODE); -} - -int hdmi_set_info(struct rk29fb_screen *screen, unsigned int vic) -{ - int i; - - if(screen == NULL) - return -1; - - if(vic == 0) - vic = HDMI_VIDEO_DEFAULT_MODE; - - for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++) - { - if(hdmi_mode[i].flag == vic) - break; - } - if(i == ARRAY_SIZE(hdmi_mode)) - return -1; - - memset(screen, 0, sizeof(struct rk29fb_screen)); - - /* screen type & face */ - screen->type = OUT_TYPE; - screen->face = OUT_FACE; - - /* Screen size */ - screen->x_res = hdmi_mode[i].xres; - screen->y_res = hdmi_mode[i].yres; - - /* Timing */ - screen->pixclock = hdmi_mode[i].pixclock; - screen->lcdc_aclk = LCD_ACLK; - screen->left_margin = hdmi_mode[i].left_margin; - screen->right_margin = hdmi_mode[i].right_margin; - screen->hsync_len = hdmi_mode[i].hsync_len; - screen->upper_margin = hdmi_mode[i].upper_margin; - screen->lower_margin = hdmi_mode[i].lower_margin; - screen->vsync_len = hdmi_mode[i].vsync_len; - - /* Pin polarity */ - if(FB_SYNC_HOR_HIGH_ACT & hdmi_mode[i].sync) - screen->pin_hsync = 1; - else - screen->pin_hsync = 0; - if(FB_SYNC_VERT_HIGH_ACT & hdmi_mode[i].sync) - screen->pin_vsync = 1; - else - screen->pin_vsync = 0; - screen->pin_den = 0; - screen->pin_dclk = DCLK_POL; - - /* Swap rule */ - screen->swap_rb = SWAP_RB; - screen->swap_rg = 0; - screen->swap_gb = 0; - screen->swap_delta = 0; - screen->swap_dumy = 0; - - /* Operation function*/ - screen->init = NULL; - screen->standby = NULL; - - return 0; -} - -static void hdmi_show_sink_info(struct hdmi *hdmi) -{ - struct list_head *pos, *head = &hdmi->edid.modelist; - struct fb_modelist *modelist; - struct fb_videomode *m; - int i; - struct hdmi_audio *audio; - - hdmi_dbg(hdmi->dev, "******** Show Sink Info ********\n"); - hdmi_dbg(hdmi->dev, "Support video mode: \n"); - list_for_each(pos, head) { - modelist = list_entry(pos, struct fb_modelist, list); - m = &modelist->mode; - hdmi_dbg(hdmi->dev, " %s.\n", m->name); - } - - for(i = 0; i < hdmi->edid.audio_num; i++) - { - audio = &(hdmi->edid.audio[i]); - switch(audio->type) - { - case HDMI_AUDIO_LPCM: - hdmi_dbg(hdmi->dev, "Support audio type: LPCM\n"); - break; - case HDMI_AUDIO_AC3: - hdmi_dbg(hdmi->dev, "Support audio type: AC3\n"); - break; - case HDMI_AUDIO_MPEG1: - hdmi_dbg(hdmi->dev, "Support audio type: MPEG1\n"); - break; - case HDMI_AUDIO_MP3: - hdmi_dbg(hdmi->dev, "Support audio type: MP3\n"); - break; - case HDMI_AUDIO_MPEG2: - hdmi_dbg(hdmi->dev, "Support audio type: MPEG2\n"); - break; - case HDMI_AUDIO_AAC_LC: - hdmi_dbg(hdmi->dev, "Support audio type: AAC\n"); - break; - case HDMI_AUDIO_DTS: - hdmi_dbg(hdmi->dev, "Support audio type: DTS\n"); - break; - case HDMI_AUDIO_ATARC: - hdmi_dbg(hdmi->dev, "Support audio type: ATARC\n"); - break; - case HDMI_AUDIO_DSD: - hdmi_dbg(hdmi->dev, "Support audio type: DSD\n"); - break; - case HDMI_AUDIO_E_AC3: - hdmi_dbg(hdmi->dev, "Support audio type: E-AC3\n"); - break; - case HDMI_AUDIO_DTS_HD: - hdmi_dbg(hdmi->dev, "Support audio type: DTS-HD\n"); - break; - case HDMI_AUDIO_MLP: - hdmi_dbg(hdmi->dev, "Support audio type: MLP\n"); - break; - case HDMI_AUDIO_DST: - hdmi_dbg(hdmi->dev, "Support audio type: DST\n"); - break; - case HDMI_AUDIO_WMA_PRO: - hdmi_dbg(hdmi->dev, "Support audio type: WMP-PRO\n"); - break; - default: - hdmi_dbg(hdmi->dev, "Support audio type: Unkown\n"); - break; - } - - hdmi_dbg(hdmi->dev, "Support audio sample rate: \n"); - if(audio->rate & HDMI_AUDIO_FS_32000) - hdmi_dbg(hdmi->dev, " 32000\n"); - if(audio->rate & HDMI_AUDIO_FS_44100) - hdmi_dbg(hdmi->dev, " 44100\n"); - if(audio->rate & HDMI_AUDIO_FS_48000) - hdmi_dbg(hdmi->dev, " 48000\n"); - if(audio->rate & HDMI_AUDIO_FS_88200) - hdmi_dbg(hdmi->dev, " 88200\n"); - if(audio->rate & HDMI_AUDIO_FS_96000) - hdmi_dbg(hdmi->dev, " 96000\n"); - if(audio->rate & HDMI_AUDIO_FS_176400) - hdmi_dbg(hdmi->dev, " 176400\n"); - if(audio->rate & HDMI_AUDIO_FS_192000) - hdmi_dbg(hdmi->dev, " 192000\n"); - - hdmi_dbg(hdmi->dev, "Support audio word lenght: \n"); - if(audio->rate & HDMI_AUDIO_WORD_LENGTH_16bit) - hdmi_dbg(hdmi->dev, " 16bit\n"); - if(audio->rate & HDMI_AUDIO_WORD_LENGTH_20bit) - hdmi_dbg(hdmi->dev, " 20bit\n"); - if(audio->rate & HDMI_AUDIO_WORD_LENGTH_24bit) - hdmi_dbg(hdmi->dev, " 24bit\n"); - } - hdmi_dbg(hdmi->dev, "******** Show Sink Info ********\n"); -} - -/** - * hdmi_ouputmode_select - select hdmi transmitter output mode: hdmi or dvi? - * @hdmi: handle of hdmi - * @edid_ok: get EDID data success or not, HDMI_ERROR_SUCESS means success. - */ -int hdmi_ouputmode_select(struct hdmi *hdmi, int edid_ok) -{ - struct list_head *head = &hdmi->edid.modelist; - struct fb_monspecs *specs = hdmi->edid.specs; - struct fb_videomode *modedb = NULL; - int i, pixclock; - - if(edid_ok != HDMI_ERROR_SUCESS) { - dev_err(hdmi->dev, "warning: EDID error, assume sink as HDMI !!!!"); - hdmi->edid.sink_hdmi = 1; - } - - if(edid_ok != HDMI_ERROR_SUCESS) { - hdmi->edid.ycbcr444 = 0; - hdmi->edid.ycbcr422 = 0; - hdmi->autoconfig = HDMI_DISABLE; - } - if(head->next == head) { - dev_info(hdmi->dev, "warning: no CEA video mode parsed from EDID !!!!"); - // If EDID get error, list all system supported mode. - // If output mode is set to DVI and EDID is ok, check - // the output timing. - - if(hdmi->edid.sink_hdmi == 0 && specs && specs->modedb_len) { - /* Get max resolution timing */ - modedb = &specs->modedb[0]; - for (i = 0; i < specs->modedb_len; i++) { - if(specs->modedb[i].xres > modedb->xres) - modedb = &specs->modedb[i]; - else if(specs->modedb[i].yres > modedb->yres) - modedb = &specs->modedb[i]; - } - // For some monitor, the max pixclock read from EDID is smaller - // than the clock of max resolution mode supported. We fix it. - pixclock = PICOS2KHZ(modedb->pixclock); - pixclock /= 250; - pixclock *= 250; - pixclock *= 1000; - if(pixclock == 148250000) - pixclock = 148500000; - if(pixclock > specs->dclkmax) - specs->dclkmax = pixclock; - } - - for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++) { - if(modedb) { - if( (hdmi_mode[i].pixclock < specs->dclkmin) || - (hdmi_mode[i].pixclock > specs->dclkmax) || - (hdmi_mode[i].refresh < specs->vfmin) || - (hdmi_mode[i].refresh > specs->vfmax) || - (hdmi_mode[i].xres > modedb->xres) || - (hdmi_mode[i].yres > modedb->yres) ) - continue; - } - hdmi_add_videomode(&hdmi_mode[i], head); - } - } - - #ifdef HDMI_DEBUG - hdmi_show_sink_info(hdmi); - #endif - return HDMI_ERROR_SUCESS; -} -/** - * hdmi_videomode_compare - compare 2 videomodes - * @mode1: first videomode - * @mode2: second videomode - * - * RETURNS: - * 1 if mode1 > mode2, 0 if mode1 = mode2, -1 mode1 < mode2 - */ -static int hdmi_videomode_compare(const struct fb_videomode *mode1, - const struct fb_videomode *mode2) -{ - if(mode1->xres > mode2->xres) - return 1; - else if(mode1->xres == mode2->xres) - { - if(mode1->yres > mode2->yres) - return 1; - else if(mode1->yres == mode2->yres) - { - if(mode1->pixclock > mode2->pixclock) - return 1; - else if(mode1->pixclock == mode2->pixclock) - { - if(mode1->refresh > mode2->refresh) - return 1; - else if(mode1->refresh == mode2->refresh) - return 0; - } - } - } - return -1; -} -/** - * hdmi_add_videomode: adds videomode entry to modelist - * @mode: videomode to add - * @head: struct list_head of modelist - * - * NOTES: - * Will only add unmatched mode entries - */ -int hdmi_add_videomode(const struct fb_videomode *mode, struct list_head *head) -{ - struct list_head *pos; - struct fb_modelist *modelist, *modelist_new; - struct fb_videomode *m; - int i, found = 0; - - for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++) - { - m =(struct fb_videomode*) &hdmi_mode[i]; - if (fb_mode_is_equal(m, mode)) { - found = 1; - break; - } - } - - if (found) { - list_for_each(pos, head) { - modelist = list_entry(pos, struct fb_modelist, list); - m = &modelist->mode; - if (fb_mode_is_equal(m, mode)) { - // m == mode - return 0; - } - else - { - if(hdmi_videomode_compare(m, mode) == -1) { - break; - } - } - } - - modelist_new = kmalloc(sizeof(struct fb_modelist), - GFP_KERNEL); - if (!modelist_new) - return -ENOMEM; - modelist_new->mode = hdmi_mode[i]; - list_add_tail(&modelist_new->list, pos); - } - - return 0; -} - -/** - * hdmi_videomode_to_vic: transverse video mode to vic - * @vmode: videomode to transverse - * - */ -int hdmi_videomode_to_vic(struct fb_videomode *vmode) -{ - unsigned char vic = 0; - int i = 0; - - for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++) - { - if( vmode->vmode == hdmi_mode[i].vmode && - vmode->refresh == hdmi_mode[i].refresh && - vmode->xres == hdmi_mode[i].xres && - vmode->left_margin == hdmi_mode[i].left_margin && - vmode->right_margin == hdmi_mode[i].right_margin && - vmode->upper_margin == hdmi_mode[i].upper_margin && - vmode->lower_margin == hdmi_mode[i].lower_margin && - vmode->hsync_len == hdmi_mode[i].hsync_len && - vmode->vsync_len == hdmi_mode[i].vsync_len) - { - if( (vmode->vmode == FB_VMODE_NONINTERLACED && vmode->yres == hdmi_mode[i].yres) || - (vmode->vmode == FB_VMODE_INTERLACED && vmode->yres == hdmi_mode[i].yres/2)) - { - vic = hdmi_mode[i].flag; - break; - } - } - } - return vic; -} - -/** - * hdmi_vic_to_videomode: transverse vic mode to video mode - * @vmode: vic to transverse - * - */ -const struct fb_videomode* hdmi_vic_to_videomode(int vic) -{ - int i; - - if(vic == 0) - return NULL; - - for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++) - { - if(hdmi_mode[i].flag == vic) - return &hdmi_mode[i]; - } - return NULL; -} - -/** - * hdmi_find_best_mode: find the video mode nearest to input vic - * @hdmi: - * @vic: input vic - * - * NOTES: - * If vic is zero, return the high resolution video mode vic. - */ -int hdmi_find_best_mode(struct hdmi* hdmi, int vic) -{ - struct list_head *pos, *head = &hdmi->edid.modelist; - struct fb_modelist *modelist; - struct fb_videomode *m = NULL; - int found = 0; - - if(vic) - { - list_for_each(pos, head) { - modelist = list_entry(pos, struct fb_modelist, list); - m = &modelist->mode; - if(m->flag == vic) - { - found = 1; - break; - } - } - } - if( (vic == 0 || found == 0) && head->next != head) - { - modelist = list_entry(head->next, struct fb_modelist, list); - m = &modelist->mode; - } - if(m != NULL) - return m->flag; - else - return 0; -} - -const char *hdmi_get_video_mode_name(unsigned char vic) -{ - int i; - - for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++) - { - if(vic == hdmi_mode[i].flag) - break; - } - if(i == ARRAY_SIZE(hdmi_mode)) - return NULL; - else - return hdmi_mode[i].name; -} - -/** - * hdmi_switch_fb: switch lcdc mode to required video mode - * @hdmi: - * @type: - * - * NOTES: - * - */ -int hdmi_switch_fb(struct hdmi *hdmi, int vic) -{ - int rc = 0; - - if(hdmi->vic == 0) - hdmi->vic = HDMI_VIDEO_DEFAULT_MODE; - - if(hdmi->lcdc == NULL || hdmi->lcdc->screen == NULL) { - dev_err(hdmi->dev, "lcdc %d not exist\n", HDMI_SOURCE_DEFAULT); - return -1; - } - - rc = hdmi_set_info(hdmi->lcdc->screen, hdmi->vic); - - if(rc == 0) { - rk_fb_switch_screen(hdmi->lcdc->screen, 1, HDMI_SOURCE_DEFAULT); - rk_fb_disp_scale(hdmi->xscale, hdmi->yscale, HDMI_SOURCE_DEFAULT); - } - return rc; -} - -/** - * hdmi_get_status: get hdmi hotplug status - * - * NOTES: - * - */ -int hdmi_get_hotplug(void) -{ - if(hdmi) - return hdmi->hotplug; - else - return HDMI_HPD_REMOVED; -} \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_sysfs.c b/drivers/video/rockchip/hdmi/rk30_hdmi_sysfs.c deleted file mode 100755 index 828b77120f2f..000000000000 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_sysfs.c +++ /dev/null @@ -1,177 +0,0 @@ -#include -#include -#include -#include -#include "rk30_hdmi.h" - -static int hdmi_get_enable(struct rk_display_device *device) -{ - struct hdmi *hdmi = device->priv_data; - int enable; - - mutex_lock(&hdmi->enable_mutex); - enable = hdmi->enable; - mutex_unlock(&hdmi->enable_mutex); - - return enable; -} - -static int hdmi_set_enable(struct rk_display_device *device, int enable) -{ - struct hdmi *hdmi = device->priv_data; - - mutex_lock(&hdmi->enable_mutex); - if(hdmi->enable == enable) { - mutex_unlock(&hdmi->enable_mutex); - return 0; - } - hdmi->enable = enable; - - if(hdmi->suspend ) { - mutex_unlock(&hdmi->enable_mutex); - return 0; - } - - if(enable == 0) { - 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); - mutex_unlock(&hdmi->enable_mutex); - } - return 0; -} - -static int hdmi_get_status(struct rk_display_device *device) -{ - struct hdmi *hdmi = device->priv_data; - if(hdmi->hotplug == HDMI_HPD_ACTIVED) - return 1; - else - return 0; -} - -static int hdmi_get_modelist(struct rk_display_device *device, struct list_head **modelist) -{ - struct hdmi *hdmi = device->priv_data; - if(!hdmi->hotplug) - return -1; - *modelist = &hdmi->edid.modelist; - return 0; -} - -static int hdmi_set_mode(struct rk_display_device *device, struct fb_videomode *mode) -{ - struct hdmi *hdmi = device->priv_data; - int vic = hdmi_videomode_to_vic(mode); - - if(!hdmi->hotplug) - return -1; - hdmi->autoconfig = HDMI_DISABLE; - if(vic && hdmi->vic != vic) - { - hdmi->vic = vic; - hdmi->command = HDMI_CONFIG_VIDEO; - 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(10000)); - } - return 0; -} - -static int hdmi_get_mode(struct rk_display_device *device, struct fb_videomode *mode) -{ - struct hdmi *hdmi = device->priv_data; - struct fb_videomode *vmode; - - if(!hdmi->hotplug) - return -1; - - vmode = (struct fb_videomode*) hdmi_vic_to_videomode(hdmi->vic); - if(unlikely(vmode == NULL)) - return -1; - *mode = *vmode; - return 0; -} - -static int hdmi_set_scale(struct rk_display_device *device, int direction, int value) -{ - struct hdmi *hdmi = device->priv_data; - - if(!hdmi || value < 0 || value > 100) - return -1; - - if(direction == DISPLAY_SCALE_X) - hdmi->xscale = value; - else if(direction == DISPLAY_SCALE_Y) - hdmi->yscale = value; - else - return -1; - rk_fb_disp_scale(hdmi->xscale, hdmi->yscale, HDMI_SOURCE_DEFAULT); - return 0; -} - -static int hdmi_get_scale(struct rk_display_device *device, int direction) -{ - struct hdmi *hdmi = device->priv_data; - - if(!hdmi) - return -1; - - if(direction == DISPLAY_SCALE_X) - return hdmi->xscale; - else if(direction == DISPLAY_SCALE_Y) - return hdmi->yscale; - else - return -1; -} - -struct rk_display_ops hdmi_display_ops = { - .setenable = hdmi_set_enable, - .getenable = hdmi_get_enable, - .getstatus = hdmi_get_status, - .getmodelist = hdmi_get_modelist, - .setmode = hdmi_set_mode, - .getmode = hdmi_get_mode, - .setscale = hdmi_set_scale, - .getscale = hdmi_get_scale, -}; - -#if 1 -static int hdmi_display_probe(struct rk_display_device *device, void *devdata) -{ - device->owner = THIS_MODULE; - strcpy(device->type, "HDMI"); - device->priority = DISPLAY_PRIORITY_HDMI; -// device->name = kmalloc(strlen(name), GFP_KERNEL); -// if(device->name) -// { -// strcpy(device->name, name); -// } - device->priv_data = devdata; - device->ops = &hdmi_display_ops; - return 1; -} - -static struct rk_display_driver display_hdmi = { - .probe = hdmi_display_probe, -}; - -static struct rk_display_device *display_device_hdmi = NULL; - -void hdmi_register_display_sysfs(struct hdmi *hdmi, struct device *parent) -{ - display_device_hdmi = rk_display_device_register(&display_hdmi, parent, hdmi); -} - -void hdmi_unregister_display_sysfs(struct hdmi *hdmi) -{ - if(display_device_hdmi) - rk_display_device_unregister(display_device_hdmi); -} -#endif \ No newline at end of file diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_task.c b/drivers/video/rockchip/hdmi/rk30_hdmi_task.c deleted file mode 100755 index cbee83a5d509..000000000000 --- a/drivers/video/rockchip/hdmi/rk30_hdmi_task.c +++ /dev/null @@ -1,312 +0,0 @@ -#include -#include -#include "rk30_hdmi.h" -#include "rk30_hdmi_hw.h" - -#ifdef CONFIG_HDMI_RK30_CTL_CODEC -extern void codec_set_spk(bool on); -#endif - -#define HDMI_MAX_TRY_TIMES 1 - -static char *envp[] = {"INTERFACE=HDMI", NULL}; - -static void hdmi_sys_show_state(int state) -{ - switch(state) - { - case HDMI_SLEEP: - dev_printk(KERN_INFO, hdmi->dev, "HDMI_SLEEP\n"); - break; - case HDMI_INITIAL: - dev_printk(KERN_INFO, hdmi->dev, "HDMI_INITIAL\n"); - break; - case WAIT_HOTPLUG: - dev_printk(KERN_INFO, hdmi->dev, "WAIT_HOTPLUG\n"); - break; - case READ_PARSE_EDID: - dev_printk(KERN_INFO, hdmi->dev, "READ_PARSE_EDID\n"); - break; - case WAIT_HDMI_ENABLE: - dev_printk(KERN_INFO, hdmi->dev, "WAIT_HDMI_ENABLE\n"); - break; - case SYSTEM_CONFIG: - dev_printk(KERN_INFO, hdmi->dev, "SYSTEM_CONFIG\n"); - break; - case CONFIG_VIDEO: - dev_printk(KERN_INFO, hdmi->dev, "CONFIG_VIDEO\n"); - break; - case CONFIG_AUDIO: - dev_printk(KERN_INFO, hdmi->dev, "CONFIG_AUDIO\n"); - break; - case PLAY_BACK: - dev_printk(KERN_INFO, hdmi->dev, "PLAY_BACK\n"); - break; - default: - dev_printk(KERN_INFO, hdmi->dev, "Unkown State %d\n", state); - break; - } -} - -int hdmi_sys_init(void) -{ - hdmi->pwr_mode = PWR_SAVE_MODE_A; - hdmi->hotplug = HDMI_HPD_REMOVED; - hdmi->state = HDMI_SLEEP; - hdmi->enable = HDMI_ENABLE; - hdmi->autoconfig = HDMI_AUTO_CONFIGURE; - hdmi->display = HDMI_DISABLE; - - hdmi->vic = HDMI_VIDEO_DEFAULT_MODE; - hdmi->audio.channel = HDMI_AUDIO_DEFAULT_CHANNEL; - hdmi->audio.rate = HDMI_AUDIO_DEFAULT_RATE; - hdmi->audio.word_length = HDMI_AUDIO_DEFAULT_WORD_LENGTH; - - memset(&hdmi->edid, 0, sizeof(struct hdmi_edid)); - INIT_LIST_HEAD(&hdmi->edid.modelist); - return 0; -} - -void hdmi_sys_remove(void) -{ - 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); - } - memset(&hdmi->edid, 0, sizeof(struct hdmi_edid)); - INIT_LIST_HEAD(&hdmi->edid.modelist); - hdmi->display = HDMI_DISABLE; - rk_fb_switch_screen(hdmi->lcdc->screen, 0, HDMI_SOURCE_DEFAULT); - kobject_uevent_env(&hdmi->dev->kobj, KOBJ_REMOVE, envp); - #ifdef CONFIG_SWITCH - switch_set_state(&(hdmi->switch_hdmi), 0); - #endif - #ifdef CONFIG_HDMI_RK30_CTL_CODEC - codec_set_spk(1); - #endif -} - -static void hdmi_sys_sleep(void) -{ - mutex_lock(&hdmi->enable_mutex); - if(hdmi->enable) - disable_irq(hdmi->irq); - hdmi->state = HDMI_SLEEP; - rk30_hdmi_removed(); - if(hdmi->enable) - enable_irq(hdmi->irq); - mutex_unlock(&hdmi->enable_mutex); -} - -static int hdmi_process_command(void) -{ - int change, state = hdmi->state; - - change = hdmi->command; - if(change != HDMI_CONFIG_NONE) - { - hdmi->command = HDMI_CONFIG_NONE; - switch(change) - { - case HDMI_CONFIG_ENABLE: - /* disable HDMI */ - mutex_lock(&hdmi->enable_mutex); - if(!hdmi->enable || hdmi->suspend) - { - if(hdmi->hotplug == HDMI_HPD_ACTIVED) - hdmi_sys_remove(); - hdmi->state = HDMI_SLEEP; - hdmi->hotplug = HDMI_HPD_REMOVED; - rk30_hdmi_removed(); - state = HDMI_SLEEP; - } - mutex_unlock(&hdmi->enable_mutex); - if(hdmi->wait == 1) { - complete(&hdmi->complete); - hdmi->wait = 0; - } - break; - case HDMI_CONFIG_COLOR: - if(state > CONFIG_VIDEO) - state = CONFIG_VIDEO; - break; - case HDMI_CONFIG_HDCP: - break; - case HDMI_CONFIG_DISPLAY: - break; - case HDMI_CONFIG_AUDIO: - if(state > CONFIG_AUDIO) - state = CONFIG_AUDIO; - break; - case HDMI_CONFIG_VIDEO: - default: - if(state > SYSTEM_CONFIG) - state = SYSTEM_CONFIG; - else - { - if(hdmi->wait == 1) { - complete(&hdmi->complete); - hdmi->wait = 0; - } - } - break; - } - } - else if(state == HDMI_SLEEP) - state = WAIT_HOTPLUG; - return state; -} - -static DEFINE_MUTEX(work_mutex); - -void hdmi_work(struct work_struct *work) -{ - int hotplug, state_last; - int rc = HDMI_ERROR_SUCESS, trytimes = 0; - struct rk30_hdmi_video_para video; - - mutex_lock(&work_mutex); - /* Process hdmi command */ - hdmi->state = hdmi_process_command(); - - if(!hdmi->enable || hdmi->suspend) { - mutex_unlock(&work_mutex); - return; - } - hotplug = rk30_hdmi_detect_hotplug(); - hdmi_dbg(hdmi->dev, "[%s] hotplug %02x curvalue %d\n", __FUNCTION__, hotplug, hdmi->hotplug); - - if(hotplug != hdmi->hotplug) - { - if(hotplug == HDMI_HPD_ACTIVED){ - hdmi->state = READ_PARSE_EDID; - } - else if(hdmi->hotplug == HDMI_HPD_ACTIVED) { - hdmi_sys_remove(); - hdmi->hotplug = hotplug; - if(hotplug == HDMI_HPD_REMOVED) - hdmi_sys_sleep(); - else { - hdmi->state = WAIT_HOTPLUG; - rk30_hdmi_removed(); - } - if(hdmi->wait == 1) { - complete(&hdmi->complete); - hdmi->wait = 0; - } - mutex_unlock(&work_mutex); - return; - } - else if(hotplug == HDMI_HPD_REMOVED) { - hdmi->state = HDMI_SLEEP; - rk30_hdmi_removed(); - } - hdmi->hotplug = hotplug; - } - else if(hotplug == HDMI_HPD_REMOVED) - hdmi_sys_sleep(); - - do { - hdmi_sys_show_state(hdmi->state); - state_last = hdmi->state; - switch(hdmi->state) - { - case READ_PARSE_EDID: - rc = hdmi_sys_parse_edid(hdmi); - if(rc == HDMI_ERROR_SUCESS) - { - hdmi->state = SYSTEM_CONFIG; - kobject_uevent_env(&hdmi->dev->kobj, KOBJ_ADD, envp); - #ifdef CONFIG_SWITCH - switch_set_state(&(hdmi->switch_hdmi), 1); - #endif - #ifdef CONFIG_HDMI_RK30_CTL_CODEC - codec_set_spk(0); - #endif - } - break; - case SYSTEM_CONFIG: - if(hdmi->autoconfig) - hdmi->vic = hdmi_find_best_mode(hdmi, 0); - else - hdmi->vic = hdmi_find_best_mode(hdmi, hdmi->vic); - rc = hdmi_switch_fb(hdmi, hdmi->vic); - if(rc == HDMI_ERROR_SUCESS) - hdmi->state = CONFIG_VIDEO; - break; - case CONFIG_VIDEO: - hdmi->display = HDMI_DISABLE; - video.vic = hdmi->vic; - video.input_mode = VIDEO_INPUT_RGB_YCBCR_444; - video.input_color = VIDEO_INPUT_COLOR_RGB;//VIDEO_INPUT_COLOR_YCBCR - video.output_mode = hdmi->edid.sink_hdmi; - - if(hdmi->edid.ycbcr444) - video.output_color = VIDEO_OUTPUT_YCBCR444; - else if(hdmi->edid.ycbcr422) - video.output_color = VIDEO_OUTPUT_YCBCR422; - else - video.output_color = VIDEO_OUTPUT_RGB444; - // For DVI, output RGB - if(hdmi->edid.sink_hdmi == 0) - video.output_color = VIDEO_OUTPUT_RGB444; - - rc = rk30_hdmi_config_video(&video); - if(rc == HDMI_ERROR_SUCESS) - { - if(hdmi->edid.sink_hdmi) - hdmi->state = CONFIG_AUDIO; - else - hdmi->state = PLAY_BACK; - } - break; - case CONFIG_AUDIO: - rc = rk30_hdmi_config_audio(&(hdmi->audio)); - - if(rc == HDMI_ERROR_SUCESS) - hdmi->state = PLAY_BACK; - break; - case PLAY_BACK: - if(hdmi->display != HDMI_ENABLE) { - rk30_hdmi_control_output(HDMI_ENABLE); - hdmi->display = HDMI_ENABLE; - if(hdmi->hdcp_cb) { - hdmi->hdcp_cb(); - } - } - - if(hdmi->wait == 1) { - complete(&hdmi->complete); - hdmi->wait = 0; - } - break; - default: - break; - } - if(rc != HDMI_ERROR_SUCESS) - { - trytimes++; - msleep(10); - } - if(hdmi->state != state_last) - trytimes = 0; - - }while((hdmi->state != state_last || (rc != HDMI_ERROR_SUCESS) ) && trytimes < HDMI_MAX_TRY_TIMES); - -// if(trytimes == HDMI_MAX_TRY_TIMES) -// { -// if(hdmi->hotplug) { -// hdmi_sys_remove(); -// hdmi->hotplug = HDMI_HPD_REMOVED; -// hdmi_sys_sleep(); -// -// } -// } - hdmi_dbg(hdmi->dev, "[%s] done\n", __FUNCTION__); - mutex_unlock(&work_mutex); -} \ 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 534ba51ab5bd..cf6d34c6ce80 100755 --- a/drivers/video/rockchip/hdmi/rk_hdmi.h +++ b/drivers/video/rockchip/hdmi/rk_hdmi.h @@ -1,6 +1,61 @@ #ifndef __RK_HDMI_H__ #define __RK_HDMI_H__ +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SWITCH +#include +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#include +#include +#include + +/* default HDMI output video mode */ +#define HDMI_VIDEO_DEFAULT_MODE HDMI_1280x720p_60Hz//HDMI_1920x1080p_60Hz + +// HDMI video source +enum { + HDMI_SOURCE_LCDC0 = 0, + HDMI_SOURCE_LCDC1 = 1 +}; + +/* default HDMI video source */ +#define HDMI_SOURCE_DEFAULT HDMI_SOURCE_LCDC1 +/* 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 + +/* 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, + VIDEO_INPUT_YCBCR422_EMBEDDED_SYNC, + VIDEO_INPUT_2X_CLOCK, + VIDEO_INPUT_2X_CLOCK_EMBEDDED_SYNC, + 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 +}; /******************************************************************** ** ½á¹¹¶¨Òå * ********************************************************************/ @@ -186,6 +241,72 @@ struct hdmi_edid { }; extern const struct fb_videomode hdmi_mode[]; +/* RK HDMI Video Configure Parameters */ +struct hdmi_video_para { + int vic; + int input_mode; //input video data interface + int input_color; //input video color mode + int output_mode; //output hdmi or dvi + int output_color; //output video color mode +}; +struct hdmi { + struct device *dev; + struct clk *hclk; //HDMI AHP clk + int id; + int regbase; + int irq; + int regbase_phy; + int regsize_phy; + struct rk_lcdc_device_driver *lcdc; + + #ifdef CONFIG_SWITCH + struct switch_dev switch_hdmi; + #endif + + struct workqueue_struct *workqueue; + struct delayed_work delay_work; + + spinlock_t irq_lock; + struct mutex enable_mutex; + + int wait; + struct completion complete; + + int suspend; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + + struct hdmi_edid edid; + int enable; // Enable HDMI output or not + int vic; // HDMI output video mode code + struct hdmi_audio audio; // HDMI output audio type. + + int pwr_mode; // power mode + int hotplug; // hot plug status + int state; // hdmi state machine status + int autoconfig; // if true, auto config hdmi output mode according to EDID. + int command; // HDMI configuration command + int display; // HDMI display status + int xscale; // x direction scale value + int yscale; // y directoon scale value + int tmdsclk; // TDMS Clock frequency + + + int (*hdmi_removed)(void); + void (*control_output)(int enable); + int (*config_video)(struct hdmi_video_para *vpara); + int (*config_audio)(struct hdmi_audio *audio); + int (*detect_hotplug)(void); + // call back for edid + int (*read_edid)(int block, unsigned char *buff); + + // call back for hdcp operatoion + void (*hdcp_cb)(void); + void (*hdcp_irq_cb)(int); + int (*hdcp_power_on_cb)(void); + void (*hdcp_power_off_cb)(void); +}; #define hdmi_err(dev, format, arg...) \ dev_printk(KERN_ERR , dev , format , ## arg) @@ -197,7 +318,22 @@ extern const struct fb_videomode hdmi_mode[]; #define hdmi_dbg(dev, format, arg...) #endif +extern struct hdmi *hdmi; +extern struct hdmi *hdmi_register(int extra); +extern void hdmi_unregister(struct hdmi *hdmi); extern int hdmi_get_hotplug(void); extern int hdmi_set_info(struct rk29fb_screen *screen, unsigned int vic); extern void hdmi_init_lcdc(struct rk29fb_screen *screen, struct rk29lcd_info *lcd_info); +extern int hdmi_sys_init(void); +extern int hdmi_sys_parse_edid(struct hdmi* hdmi); +extern const char *hdmi_get_video_mode_name(unsigned char vic); +extern int hdmi_videomode_to_vic(struct fb_videomode *vmode); +extern const struct fb_videomode* hdmi_vic_to_videomode(int vic); +extern int hdmi_add_videomode(const struct fb_videomode *mode, struct list_head *head); +extern struct hdmi_video_timing * hdmi_find_mode(int vic); +extern int hdmi_find_best_mode(struct hdmi* hdmi, int vic); +extern int hdmi_ouputmode_select(struct hdmi *hdmi, int edid_ok); +extern int hdmi_switch_fb(struct hdmi *hdmi, int vic); +extern void hdmi_sys_remove(void); + #endif diff --git a/drivers/video/rockchip/hdmi/rk_hdmi_core.c b/drivers/video/rockchip/hdmi/rk_hdmi_core.c new file mode 100755 index 000000000000..1b17185b6da9 --- /dev/null +++ b/drivers/video/rockchip/hdmi/rk_hdmi_core.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include +#include +#include + + +struct class *hdmi_class; +struct hdmi_id_ref_info { + struct hdmi *hdmi; + int id; + int ref; +}ref_info[HDMI_MAX_ID]; +#ifdef CONFIG_SYSFS + +extern int hdmi_create_attrs(struct hdmi *hdmi); +extern void hdmi_remove_attrs(struct hdmi *hdmi); + +#else + +static inline int hdmi_create_attrs(struct hdmi *hdmi) +{ return 0; } +static inline void hdmi_remove_attrs(struct hdmi *hdmi) {} + +#endif /* CONFIG_SYSFS */ +static void __hdmi_changed(struct hdmi *hdmi) +{ + int precent; + + mutex_lock(&hdmi->lock); + precent = hdmi->ops->hdmi_precent(hdmi); + if(precent && (hdmi->mode == DISP_ON_LCD) && hdmi->display_on){ + if(hdmi->ops->insert(hdmi) == 0){ + hdmi->mode = hdmi->display_on; + kobject_uevent(&hdmi->dev->kobj, KOBJ_CHANGE); + } + else + hdmi_dbg(hdmi->dev, "insert error\n"); + hdmi_set_backlight(hdmi->display_on==DISP_ON_HDMI?HDMI_DISABLE: HDMI_ENABLE); + + } + else if(precent &&(hdmi->mode != hdmi->display_on)&& hdmi->display_on){ + hdmi->mode = hdmi->display_on; + hdmi_set_backlight(hdmi->display_on==DISP_ON_HDMI?HDMI_DISABLE: HDMI_ENABLE); + } + else if((!precent || !hdmi->display_on) && hdmi->mode != DISP_ON_LCD){ + if(hdmi->ops->remove(hdmi) == 0){ + hdmi->mode = DISP_ON_LCD; + hdmi_set_backlight(HDMI_ENABLE); + kobject_uevent(&hdmi->dev->kobj, KOBJ_CHANGE); + } + else + hdmi_dbg(hdmi->dev, "remove error\n"); + } + mutex_unlock(&hdmi->lock); + return; +} + +void hdmi_changed(struct hdmi *hdmi, int msec) +{ + schedule_delayed_work(&hdmi->work, msecs_to_jiffies(msec)); + return; +} +void hdmi_suspend(struct hdmi *hdmi) +{ + del_timer(&hdmi->timer); + flush_delayed_work(&hdmi->work); + if(hdmi->mode != DISP_ON_LCD){ + hdmi->ops->remove(hdmi); + hdmi->mode = DISP_ON_LCD; + } + return; +} +void hdmi_resume(struct hdmi *hdmi) +{ + mod_timer(&hdmi->timer, jiffies + msecs_to_jiffies(10)); + return; +} + +static void hdmi_changed_work(struct work_struct *work) +{ + struct hdmi *hdmi = container_of(work, struct hdmi, + work.work); + + __hdmi_changed(hdmi); + return; +} + +void *hdmi_priv(struct hdmi *hdmi) +{ + return (void *)hdmi->priv; +} +static void hdmi_detect_timer(unsigned long data) +{ + struct hdmi *hdmi = (struct hdmi*)data; + + int precent = hdmi->ops->hdmi_precent(hdmi); + + if((precent && hdmi->mode == DISP_ON_LCD) || + (!precent && hdmi->mode != DISP_ON_LCD)) + hdmi_changed(hdmi, 100); + mod_timer(&hdmi->timer, jiffies + msecs_to_jiffies(200)); +} +struct hdmi *hdmi_register(int extra, struct device *parent) +{ + int rc = 0, i; + char name[8]; + struct hdmi *hdmi = kzalloc(sizeof(struct hdmi)+ extra, GFP_KERNEL); + + if(!hdmi) + return NULL; + for(i = 0; i < HDMI_MAX_ID; i++) + { + if(ref_info[i].ref == 0) + { + ref_info[i].ref = 1; + hdmi->id = i; + break; + } + } + if(i == HDMI_MAX_ID) + { + kfree(hdmi); + return NULL; + } + sprintf(name, "hdmi-%d", hdmi->id); + + hdmi->dev = device_create(hdmi_class, parent, 0, + "%s", name); + if (IS_ERR(hdmi->dev)) { + rc = PTR_ERR(hdmi->dev); + goto dev_create_failed; + } + + dev_set_drvdata(hdmi->dev, hdmi); + ref_info[i].hdmi = hdmi; + + INIT_DELAYED_WORK(&hdmi->work, hdmi_changed_work); + + rc = hdmi_create_attrs(hdmi); + if (rc) + goto create_attrs_failed; + + goto success; + +create_attrs_failed: + device_unregister(hdmi->dev); +dev_create_failed: + hdmi_remove_attrs(hdmi); + kfree(hdmi); + return NULL; +success: + mutex_init(&hdmi->lock); + setup_timer(&hdmi->timer, hdmi_detect_timer,(unsigned long)hdmi); + mod_timer(&hdmi->timer, jiffies + msecs_to_jiffies(200)); + return hdmi; +} +void hdmi_unregister(struct hdmi *hdmi) +{ + int id; + + if(!hdmi) + return; + id = hdmi->id; + del_timer(&hdmi->timer); + flush_scheduled_work(); + hdmi_remove_attrs(hdmi); + device_unregister(hdmi->dev); + + kfree(hdmi); + hdmi = NULL; + ref_info[id].ref = 0; + ref_info[id].hdmi = NULL; +} +struct hdmi *get_hdmi_struct(int nr) +{ + if(ref_info[nr].ref == 0) + return NULL; + else + return ref_info[nr].hdmi; +} +int hdmi_is_insert(void) +{ + struct hdmi *hdmi = get_hdmi_struct(0); + + if(hdmi && hdmi->ops && hdmi->ops->hdmi_precent) + return hdmi->ops->hdmi_precent(hdmi); + else + return 0; +} +int hdmi_get_scale(void) +{ + struct hdmi* hdmi = get_hdmi_struct(0); + if(!hdmi) + return 100; + else if(hdmi->mode != DISP_ON_LCD) + return hdmi->scale; + else + return 100; +} + +int hdmi_set_scale(int event, char *data, int len) +{ + int result; + struct hdmi* hdmi = get_hdmi_struct(0); + + if(!hdmi) + return -1; + if(len != 4) + return -1; + if(fb_get_video_mode() || hdmi->mode == DISP_ON_LCD) + return -1; + + result = data[0] | data[1]<<1 | data[2]<<2; + if(event != MOUSE_NONE && (result & event) != event) + return -1; + + hdmi->scale += data[3]; + + hdmi->scale = (hdmi->scale>100)?100:hdmi->scale; + hdmi->scale = (hdmi->scalescale; + return 0; +} + +static int __init hdmi_class_init(void) +{ + int i; + + hdmi_class = class_create(THIS_MODULE, "hdmi"); + + if (IS_ERR(hdmi_class)) + return PTR_ERR(hdmi_class); + for(i = 0; i < HDMI_MAX_ID; i++) { + ref_info[i].id = i; + ref_info[i].ref = 0; + ref_info[i].hdmi = NULL; + } + return 0; +} + +static void __exit hdmi_class_exit(void) +{ + class_destroy(hdmi_class); +} +EXPORT_SYMBOL(hdmi_changed); +EXPORT_SYMBOL(hdmi_register); +EXPORT_SYMBOL(hdmi_unregister); +EXPORT_SYMBOL(get_hdmi_struct); + +subsys_initcall(hdmi_class_init); +module_exit(hdmi_class_exit); + diff --git a/drivers/video/rockchip/hdmi/rk_hdmi_edid.c b/drivers/video/rockchip/hdmi/rk_hdmi_edid.c new file mode 100755 index 000000000000..ff44f4898f34 --- /dev/null +++ b/drivers/video/rockchip/hdmi/rk_hdmi_edid.c @@ -0,0 +1,409 @@ +#include "rk_hdmi.h" +#include "../../edid.h" + +#define hdmi_edid_error(fmt, ...) \ + printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__) + +#if 0 +#define hdmi_edid_debug(fmt, ...) \ + printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__) +#else +#define hdmi_edid_debug(fmt, ...) +#endif + +typedef enum HDMI_EDID_ERRORCODE +{ + E_HDMI_EDID_SUCCESS = 0, + E_HDMI_EDID_PARAM, + E_HDMI_EDID_HEAD, + E_HDMI_EDID_CHECKSUM, + E_HDMI_EDID_VERSION, + E_HDMI_EDID_UNKOWNDATA, + E_HDMI_EDID_NOMEMORY +}HDMI_EDID_ErrorCode; + +static const unsigned int double_aspect_vic[] = {3, 7, 9, 11, 13, 15, 18, 22, 24, 26, 28, 30, 36, 38, 43, 45, 49, 51, 53, 55, 57, 59}; +static int hdmi_edid_checksum(unsigned char *buf) +{ + int i; + int checksum = 0; + + for(i = 0; i < HDMI_EDID_BLOCK_SIZE; i++) + checksum += buf[i]; + + checksum &= 0xff; + + if(checksum == 0) + return E_HDMI_EDID_SUCCESS; + else + return E_HDMI_EDID_CHECKSUM; +} + +/* + @Des Parse Detail Timing Descriptor. + @Param buf : pointer to DTD data. + @Param pvic: VIC of DTD descripted. + */ +static int hdmi_edid_parse_dtd(unsigned char *block, struct fb_videomode *mode) +{ + mode->xres = H_ACTIVE; + mode->yres = V_ACTIVE; + mode->pixclock = PIXEL_CLOCK; +// mode->pixclock /= 1000; +// mode->pixclock = KHZ2PICOS(mode->pixclock); + mode->right_margin = H_SYNC_OFFSET; + mode->left_margin = (H_ACTIVE + H_BLANKING) - + (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH); + mode->upper_margin = V_BLANKING - V_SYNC_OFFSET - + V_SYNC_WIDTH; + mode->lower_margin = V_SYNC_OFFSET; + mode->hsync_len = H_SYNC_WIDTH; + mode->vsync_len = V_SYNC_WIDTH; + if (HSYNC_POSITIVE) + mode->sync |= FB_SYNC_HOR_HIGH_ACT; + if (VSYNC_POSITIVE) + mode->sync |= FB_SYNC_VERT_HIGH_ACT; + mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) * + (V_ACTIVE + V_BLANKING)); + if (INTERLACED) { + mode->yres *= 2; + mode->upper_margin *= 2; + mode->lower_margin *= 2; + mode->vsync_len *= 2; + mode->vmode |= FB_VMODE_INTERLACED; + } + mode->flag = FB_MODE_IS_DETAILED; + + hdmi_edid_debug("<<<<<<<>>>>>>>>\n"); + hdmi_edid_debug("%d KHz Refresh %d Hz", PIXEL_CLOCK/1000, mode->refresh); + hdmi_edid_debug("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET, + H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING); + hdmi_edid_debug("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET, + V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING); + hdmi_edid_debug("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-", + (VSYNC_POSITIVE) ? "+" : "-"); + return E_HDMI_EDID_SUCCESS; +} + +static int hdmi_edid_parse_base(unsigned char *buf, int *extend_num, struct hdmi_edid *pedid) +{ + int rc, i; + + if(buf == NULL || extend_num == NULL) + return E_HDMI_EDID_PARAM; + + #ifdef DEBUG + for(i = 0; i < HDMI_EDID_BLOCK_SIZE; i++) + { + hdmi_edid_debug("%02x ", buf[i]&0xff); + if((i+1) % 16 == 0) + hdmi_edid_debug("\n"); + } + #endif + + // Check first 8 byte to ensure it is an edid base block. + if( buf[0] != 0x00 || + buf[1] != 0xFF || + buf[2] != 0xFF || + buf[3] != 0xFF || + buf[4] != 0xFF || + buf[5] != 0xFF || + buf[6] != 0xFF || + buf[7] != 0x00) + { + hdmi_edid_error("[EDID] check header error\n"); + return E_HDMI_EDID_HEAD; + } + + *extend_num = buf[0x7e]; + #ifdef DEBUG + hdmi_edid_debug("[EDID] extend block num is %d\n", buf[0x7e]); + #endif + + // Checksum + rc = hdmi_edid_checksum(buf); + if( rc != E_HDMI_EDID_SUCCESS) + { + hdmi_edid_error("[EDID] base block checksum error\n"); + return E_HDMI_EDID_CHECKSUM; + } + + pedid->specs = kzalloc(sizeof(struct fb_monspecs), GFP_KERNEL); + if(pedid->specs == NULL) + return E_HDMI_EDID_NOMEMORY; + + fb_edid_to_monspecs(buf, pedid->specs); + + return E_HDMI_EDID_SUCCESS; +} + +// Parse CEA Short Video Descriptor +static int hdmi_edid_get_cea_svd(unsigned char *buf, struct hdmi_edid *pedid) +{ + const struct fb_videomode *mode; + int count, i, j, vic; + + count = buf[0] & 0x1F; + for(i = 0; i < count; i++) + { + hdmi_edid_debug("[EDID-CEA] %02x VID %d native %d\n", buf[1 + i], buf[1 + i] & 0x7f, buf[1 + i] >> 7); + vic = buf[1 + i] & 0x7f; + for(j = 0; j < ARRAY_SIZE(double_aspect_vic); j++) + { + if(vic == double_aspect_vic[j]) + { + vic--; + break; + } + } + if(vic) + { + mode = hdmi_vic_to_videomode(vic); + if(mode) + { + hdmi_add_videomode(mode, &pedid->modelist); + } + } + } + return 0; +} + +// Parse CEA Short Audio Descriptor +static int hdmi_edid_parse_cea_sad(unsigned char *buf, struct hdmi_edid *pedid) +{ + int i, count; + + count = buf[0] & 0x1F; + pedid->audio = kmalloc((count/3)*sizeof(struct hdmi_audio), GFP_KERNEL); + if(pedid->audio == NULL) + return E_HDMI_EDID_NOMEMORY; + pedid->audio_num = count/3; + for(i = 0; i < pedid->audio_num; i++) + { + pedid->audio[i].type = (buf[1 + i*3] >> 3) & 0x0F; + pedid->audio[i].channel = (buf[1 + i*3] & 0x07) + 1; + pedid->audio[i].rate = buf[1 + i*3 + 1]; + if(pedid->audio[i].type == HDMI_AUDIO_LPCM)//LPCM + { + pedid->audio[i].word_length = buf[1 + i*3 + 2]; + } +// printk("[EDID-CEA] type %d channel %d rate %d word length %d\n", +// pedid->audio[i].type, pedid->audio[i].channel, pedid->audio[i].rate, pedid->audio[i].word_length); + } + return E_HDMI_EDID_SUCCESS; +} +// Parse CEA 861 Serial Extension. +static int hdmi_edid_parse_extensions_cea(unsigned char *buf, struct hdmi_edid *pedid) +{ + unsigned int ddc_offset, native_dtd_num, cur_offset = 4; + unsigned int underscan_support, baseaudio_support; + unsigned int tag, IEEEOUI = 0; +// unsigned int supports_ai, dc_48bit, dc_36bit, dc_30bit, dc_y444; +// unsigned char vic; + + if(buf == NULL) + return E_HDMI_EDID_PARAM; + + // Check ces extension version + if(buf[1] != 3) + { + hdmi_edid_error("[EDID-CEA] error version.\n"); + return E_HDMI_EDID_VERSION; + } + + ddc_offset = buf[2]; + underscan_support = (buf[3] >> 7) & 0x01; + baseaudio_support = (buf[3] >> 6) & 0x01; + pedid->ycbcr444 = (buf[3] >> 5) & 0x01; + pedid->ycbcr422 = (buf[3] >> 4) & 0x01; + native_dtd_num = buf[3] & 0x0F; +// hdmi_edid_debug("[EDID-CEA] ddc_offset %d underscan_support %d baseaudio_support %d yuv_support %d native_dtd_num %d\n", ddc_offset, underscan_support, baseaudio_support, yuv_support, native_dtd_num); + // Parse data block + while(cur_offset < ddc_offset) + { + tag = buf[cur_offset] >> 5; + switch(tag) + { + case 0x02: // Video Data Block + hdmi_edid_debug("[EDID-CEA] It is a Video Data Block.\n"); + hdmi_edid_get_cea_svd(buf + cur_offset, pedid); + break; + case 0x01: // Audio Data Block + hdmi_edid_debug("[EDID-CEA] It is a Audio Data Block.\n"); + hdmi_edid_parse_cea_sad(buf + cur_offset, pedid); + break; + case 0x04: // Speaker Allocation Data Block + hdmi_edid_debug("[EDID-CEA] It is a Speaker Allocatio Data Block.\n"); + break; + case 0x03: // Vendor Specific Data Block + hdmi_edid_debug("[EDID-CEA] It is a Vendor Specific Data Block.\n"); + + IEEEOUI = buf[cur_offset + 2 + 1]; + IEEEOUI <<= 8; + IEEEOUI += buf[cur_offset + 1 + 1]; + IEEEOUI <<= 8; + IEEEOUI += buf[cur_offset + 1]; + hdmi_edid_debug("[EDID-CEA] IEEEOUI is 0x%08x.\n", IEEEOUI); + if(IEEEOUI == 0x0c03) + pedid->sink_hdmi = 1; +// if(count > 5) +// { +// pedid->deepcolor = (buf[cur_offset + 5] >> 3) & 0x0F; +// supports_ai = buf[cur_offset + 5] >> 7; +// dc_48bit = (buf[cur_offset + 5] >> 6) & 0x1; +// dc_36bit = (buf[cur_offset + 5] >> 5) & 0x1; +// dc_30bit = (buf[cur_offset + 5] >> 4) & 0x1; +// dc_y444 = (buf[cur_offset + 5] >> 3) & 0x1; +// hdmi_edid_debug("[EDID-CEA] supports_ai %d dc_48bit %d dc_36bit %d dc_30bit %d dc_y444 %d \n", supports_ai, dc_48bit, dc_36bit, dc_30bit, dc_y444); +// } +// if(count > 6) +// pedid->maxtmdsclock = buf[cur_offset + 6] * 5000000; +// if(count > 7) +// { +// pedid->latency_fields_present = (buf[cur_offset + 7] & 0x80) ? 1:0; +// pedid->i_latency_fields_present = (buf[cur_offset + 7] & 0x40) ? 1:0; +// } +// if(count > 9 && pedid->latency_fields_present) +// { +// pedid->video_latency = buf[cur_offset + 8]; +// pedid->audio_latency = buf[cur_offset + 9]; +// } +// if(count > 11 && pedid->i_latency_fields_present) +// { +// pedid->interlaced_video_latency = buf[cur_offset + 10]; +// pedid->interlaced_audio_latency = buf[cur_offset + 11]; +// } + break; + case 0x05: // VESA DTC Data Block + hdmi_edid_debug("[EDID-CEA] It is a VESA DTC Data Block.\n"); + break; + case 0x07: // Use Extended Tag + hdmi_edid_debug("[EDID-CEA] It is a Use Extended Tag Data Block.\n"); + break; + default: + hdmi_edid_error("[EDID-CEA] unkowned data block tag.\n"); + break; + } + cur_offset += (buf[cur_offset] & 0x1F) + 1; + } +#if 1 +{ + // Parse DTD + struct fb_videomode *vmode = kmalloc(sizeof(struct fb_videomode), GFP_KERNEL); + if(vmode == NULL) + return E_HDMI_EDID_SUCCESS; + while(ddc_offset < HDMI_EDID_BLOCK_SIZE - 2) //buf[126] = 0 and buf[127] = checksum + { + if(!buf[ddc_offset] && !buf[ddc_offset + 1]) + break; + memset(vmode, 0, sizeof(struct fb_videomode)); + hdmi_edid_parse_dtd(buf + ddc_offset, vmode); + hdmi_add_videomode(vmode, &pedid->modelist); + ddc_offset += 18; + } + kfree(vmode); +} +#endif + return E_HDMI_EDID_SUCCESS; +} + +static int hdmi_edid_parse_extensions(unsigned char *buf, struct hdmi_edid *pedid) +{ + int rc; + + if(buf == NULL || pedid == NULL) + return E_HDMI_EDID_PARAM; + + // Checksum + rc = hdmi_edid_checksum(buf); + if( rc != E_HDMI_EDID_SUCCESS) + { + hdmi_edid_error("[EDID] extensions block checksum error\n"); + return E_HDMI_EDID_CHECKSUM; + } + + switch(buf[0]) + { + case 0xF0: + hdmi_edid_debug("[EDID-EXTEND] It is a extensions block map.\n"); + break; + case 0x02: + hdmi_edid_debug("[EDID-EXTEND] It is a CEA 861 Series Extension.\n"); + hdmi_edid_parse_extensions_cea(buf, pedid); + break; + case 0x10: + hdmi_edid_debug("[EDID-EXTEND] It is a Video Timing Block Extension.\n"); + break; + case 0x40: + hdmi_edid_debug("[EDID-EXTEND] It is a Display Information Extension.\n"); + break; + case 0x50: + hdmi_edid_debug("[EDID-EXTEND] It is a Localized String Extension.\n"); + break; + case 0x60: + hdmi_edid_debug("[EDID-EXTEND] It is a Digital Packet Video Link Extension.\n"); + break; + default: + hdmi_edid_debug("[EDID-EXTEND] Unkowned extension.\n"); + break; + } + + return E_HDMI_EDID_SUCCESS; +} + + +int hdmi_sys_parse_edid(struct hdmi* hdmi) +{ + struct hdmi_edid *pedid; + unsigned char *buff = NULL; + int rc = HDMI_ERROR_SUCESS, extendblock = 0, i; + + if(hdmi == NULL) + return HDMI_ERROR_FALSE; + + pedid = &(hdmi->edid); + memset(pedid, 0, sizeof(struct hdmi_edid)); + INIT_LIST_HEAD(&pedid->modelist); + + buff = kmalloc(HDMI_EDID_BLOCK_SIZE, GFP_KERNEL); + if(buff == NULL) + { + hdmi_dbg(hdmi->dev, "[%s] can not allocate memory for edid buff.\n", __FUNCTION__); + return -1; + } + // Read base block edid. + memset(buff, 0 , HDMI_EDID_BLOCK_SIZE); + rc = hdmi->read_edid(0, buff); + if(rc) + { + dev_err(hdmi->dev, "[HDMI] read edid base block error\n"); + goto out; + } + rc = hdmi_edid_parse_base(buff, &extendblock, pedid); + if(rc) + { + dev_err(hdmi->dev, "[HDMI] parse edid base block error\n"); + goto out; + } + for(i = 1; i < extendblock + 1; i++) + { + memset(buff, 0 , HDMI_EDID_BLOCK_SIZE); + rc = hdmi->read_edid(i, buff); + if(rc) + { + printk("[HDMI] read edid block %d error\n", i); + goto out; + } + rc = hdmi_edid_parse_extensions(buff, pedid); + if(rc) + { + dev_err(hdmi->dev, "[HDMI] parse edid block %d error\n", i); + continue; + } + } +out: + if(buff) + kfree(buff); + rc = hdmi_ouputmode_select(hdmi, rc); + return rc; +} diff --git a/drivers/video/rockchip/hdmi/rk_hdmi_lcdc.c b/drivers/video/rockchip/hdmi/rk_hdmi_lcdc.c new file mode 100755 index 000000000000..da445b953211 --- /dev/null +++ b/drivers/video/rockchip/hdmi/rk_hdmi_lcdc.c @@ -0,0 +1,526 @@ +#include +#include +#include "rk_hdmi.h" + +#define OUT_TYPE SCREEN_HDMI +#define OUT_FACE OUT_P888 +#define DCLK_POL 1 +#define SWAP_RB 0 +#define LCD_ACLK 800000000 + +const struct fb_videomode hdmi_mode [] = { + //name refresh xres yres pixclock h_bp h_fp v_bp v_fp h_pw v_pw polariry PorI flag(used for vic) +//{ "640x480p@60Hz", 60, 640, 480, 25175000, 48, 16, 33, 10, 96, 2, 0, 0, 1 }, +//{ "720x480i@60Hz", 60, 720, 480, 27000000, 114, 38, 15, 4, 124, 3, 0, 1, 6 }, +//{ "720x576i@50Hz", 50, 720, 576, 27000000, 138, 24, 19, 2, 126, 3, 0, 1, 21 }, +{ "720x480p@60Hz", 60, 720, 480, 27000000, 60, 16, 30, 9, 62, 6, 0, 0, 2 }, +{ "720x576p@50Hz", 50, 720, 576, 27000000, 68, 12, 39, 5, 64, 5, 0, 0, 17 }, +//{ "1280x720p@24Hz", 24, 1280, 720, 59400000, 220, 1760, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 60 }, +//{ "1280x720p@25Hz", 25, 1280, 720, 74250000, 220, 2420, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 61 }, +//{ "1280x720p@30Hz", 30, 1280, 720, 74250000, 220, 1760, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 62 }, +{ "1280x720p@50Hz", 50, 1280, 720, 74250000, 220, 440, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 19 }, +{ "1280x720p@60Hz", 60, 1280, 720, 74250000, 220, 110, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 4 }, +//{ "1920x1080p@24Hz", 24, 1920, 1080, 74250000, 148, 638, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 32 }, +//{ "1920x1080p@25Hz", 25, 1920, 1080, 74250000, 148, 528, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 33 }, +//{ "1920x1080p@30Hz", 30, 1920, 1080, 74250000, 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 34 }, +//{ "1920x1080i@50Hz_2",50, 1920, 1080, 72000000, 184, 32, 57, 23, 168, 5, FB_SYNC_HOR_HIGH_ACT, 1, 39 }, +//{ "1920x1080i@50Hz", 50, 1920, 1080, 74250000, 148, 528, 15, 2, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 1, 20 }, +//{ "1920x1080i@60Hz", 60, 1920, 1080, 74250000, 148, 88, 15, 2, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 1, 5 }, +{ "1920x1080p@50Hz", 50, 1920, 1080, 148500000, 148, 528, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 31 }, +{ "1920x1080p@60Hz", 60, 1920, 1080, 148500000, 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 16 }, +/* +{ "1440x288p@50Hz", 50, 720, 480, 27000000, 138, 24, 19, 2, 126, 3, 0, 0, 23 }, +{ "2880x576i@50Hz", 50, 1440, 240, 54000000, 276, 48, 19, 2, 252, 3, 0, 1, 25 }, +{ "2880x288p@50Hz", 50, 2880, 480, 54000000, 276, 48, 19, 3, 252, 3, 0, 0, 27 }, +{ "1440x576p@50Hz", 50, 2880, 480, 54000000, 136, 24, 39, 5, 128, 5, 0, 0, 29 }, +{ "2880x576p@50Hz", 50, 1920, 1080, 108000000, 272, 48, 39, 5, 256, 5, 0, 0, 37 }, +{ "1440x240p@60Hz", 60, 1440, 240, 27000000, 114, 38, 15, 4, 124, 3, 0, 0, 8 }, +{ "2880x480i@60Hz", 60, 2880, 480, 54000000, 228, 76, 15, 4, 248, 3, 0, 1, 10 }, +{ "2880x480p@60Hz", 60, 2880, 480, 54000000, 228, 76, 15, 4, 248, 3, 0, 0, 12 }, +{ "1440x480p@60Hz", 60, 1440, 480, 54000000, 120, 32, 30, 9, 124, 6, 0, 0, 14 }, +{ "2880x480p@60Hz", 60, 2880, 480, 54000000, 240, 64, 30, 9, 248, 6, 0, 0, 35 }, + +{ "1920x1080i@100Hz", 100, 1920, 1080, 148500000, 148, 528, 15, 2, 44, 5, 1, 1, 40 }, +{ "1280x720p@100Hz", 100, 1280, 720, 148500000, 220, 440, 20, 5, 40, 5, 1, 0, 41 }, +{ "720x576p@100Hz", 100, 720, 576, 54000000, 68, 12, 39, 5, 64, 5, 0, 0, 42 }, +{ "1440x576i@100Hz", 100, 1440, 576, 54000000, 138, 24, 19, 2, 12, 3, 0, 1, 44 }, +{ "1920x1080p@100Hz", 100, 1920, 1080, 297000000, 148, 528, 36, 4, 44, 5, 1, 0, 64 }, + +{ "1920x1080i@120Hz", 120, 1920, 1080, 148500000, 148, 88, 15, 2, 44, 5, 1, 1, 46 }, +{ "1280x720p@120Hz", 120, 1280, 720, 148500000, 220, 110, 20, 5, 40, 5, 1, 0, 47 }, +{ "720x480p@120Hz", 120, 720, 480, 54000000, 60, 16, 30, 9, 62, 6, 0, 0, 48 }, +{ "1440x480i@120Hz", 120, 1440, 480, 54000000, 114, 38, 15, 4, 12, 3, 0, 1, 50 }, +{ "1920x1080p@120Hz", 120, 1920, 1080, 297000000, 148, 88, 36, 4, 44, 5, 1, 0, 63 }, + +{ "720x576p@200Hz", 200, 720, 576, 108000000, 68, 12, 39, 5, 64, 5, 0, 0, 52 }, +{ "1440x576i@200Hz", 200, 1920, 1080, 108000000, 138, 24, 19, 2, 12, 3, 0, 1, 54 }, + +{ "720x480p@240Hz", 240, 720, 480, 108000000, 60, 16, 30, 9, 62, 6, 0, 0, 56 }, +{ "1440x480i@240Hz", 240, 1440, 480, 108000000, 114, 38, 15, 4, 12, 3, 0, 1, 58 }, +*/ + +}; + +void hdmi_init_lcdc(struct rk29fb_screen *screen, struct rk29lcd_info *lcd_info) +{ + hdmi_set_info(screen, HDMI_VIDEO_DEFAULT_MODE); +} + +int hdmi_set_info(struct rk29fb_screen *screen, unsigned int vic) +{ + int i; + + if(screen == NULL) + return -1; + + if(vic == 0) + vic = HDMI_VIDEO_DEFAULT_MODE; + + for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++) + { + if(hdmi_mode[i].flag == vic) + break; + } + if(i == ARRAY_SIZE(hdmi_mode)) + return -1; + + memset(screen, 0, sizeof(struct rk29fb_screen)); + + /* screen type & face */ + screen->type = OUT_TYPE; + screen->face = OUT_FACE; + + /* Screen size */ + screen->x_res = hdmi_mode[i].xres; + screen->y_res = hdmi_mode[i].yres; + + /* Timing */ + screen->pixclock = hdmi_mode[i].pixclock; + screen->lcdc_aclk = LCD_ACLK; + screen->left_margin = hdmi_mode[i].left_margin; + screen->right_margin = hdmi_mode[i].right_margin; + screen->hsync_len = hdmi_mode[i].hsync_len; + screen->upper_margin = hdmi_mode[i].upper_margin; + screen->lower_margin = hdmi_mode[i].lower_margin; + screen->vsync_len = hdmi_mode[i].vsync_len; + + /* Pin polarity */ + if(FB_SYNC_HOR_HIGH_ACT & hdmi_mode[i].sync) + screen->pin_hsync = 1; + else + screen->pin_hsync = 0; + if(FB_SYNC_VERT_HIGH_ACT & hdmi_mode[i].sync) + screen->pin_vsync = 1; + else + screen->pin_vsync = 0; + screen->pin_den = 0; + screen->pin_dclk = DCLK_POL; + + /* Swap rule */ + screen->swap_rb = SWAP_RB; + screen->swap_rg = 0; + screen->swap_gb = 0; + screen->swap_delta = 0; + screen->swap_dumy = 0; + + /* Operation function*/ + screen->init = NULL; + screen->standby = NULL; + + return 0; +} + +static void hdmi_show_sink_info(struct hdmi *hdmi) +{ + struct list_head *pos, *head = &hdmi->edid.modelist; + struct fb_modelist *modelist; + struct fb_videomode *m; + int i; + struct hdmi_audio *audio; + + hdmi_dbg(hdmi->dev, "******** Show Sink Info ********\n"); + hdmi_dbg(hdmi->dev, "Support video mode: \n"); + list_for_each(pos, head) { + modelist = list_entry(pos, struct fb_modelist, list); + m = &modelist->mode; + hdmi_dbg(hdmi->dev, " %s.\n", m->name); + } + + for(i = 0; i < hdmi->edid.audio_num; i++) + { + audio = &(hdmi->edid.audio[i]); + switch(audio->type) + { + case HDMI_AUDIO_LPCM: + hdmi_dbg(hdmi->dev, "Support audio type: LPCM\n"); + break; + case HDMI_AUDIO_AC3: + hdmi_dbg(hdmi->dev, "Support audio type: AC3\n"); + break; + case HDMI_AUDIO_MPEG1: + hdmi_dbg(hdmi->dev, "Support audio type: MPEG1\n"); + break; + case HDMI_AUDIO_MP3: + hdmi_dbg(hdmi->dev, "Support audio type: MP3\n"); + break; + case HDMI_AUDIO_MPEG2: + hdmi_dbg(hdmi->dev, "Support audio type: MPEG2\n"); + break; + case HDMI_AUDIO_AAC_LC: + hdmi_dbg(hdmi->dev, "Support audio type: AAC\n"); + break; + case HDMI_AUDIO_DTS: + hdmi_dbg(hdmi->dev, "Support audio type: DTS\n"); + break; + case HDMI_AUDIO_ATARC: + hdmi_dbg(hdmi->dev, "Support audio type: ATARC\n"); + break; + case HDMI_AUDIO_DSD: + hdmi_dbg(hdmi->dev, "Support audio type: DSD\n"); + break; + case HDMI_AUDIO_E_AC3: + hdmi_dbg(hdmi->dev, "Support audio type: E-AC3\n"); + break; + case HDMI_AUDIO_DTS_HD: + hdmi_dbg(hdmi->dev, "Support audio type: DTS-HD\n"); + break; + case HDMI_AUDIO_MLP: + hdmi_dbg(hdmi->dev, "Support audio type: MLP\n"); + break; + case HDMI_AUDIO_DST: + hdmi_dbg(hdmi->dev, "Support audio type: DST\n"); + break; + case HDMI_AUDIO_WMA_PRO: + hdmi_dbg(hdmi->dev, "Support audio type: WMP-PRO\n"); + break; + default: + hdmi_dbg(hdmi->dev, "Support audio type: Unkown\n"); + break; + } + + hdmi_dbg(hdmi->dev, "Support audio sample rate: \n"); + if(audio->rate & HDMI_AUDIO_FS_32000) + hdmi_dbg(hdmi->dev, " 32000\n"); + if(audio->rate & HDMI_AUDIO_FS_44100) + hdmi_dbg(hdmi->dev, " 44100\n"); + if(audio->rate & HDMI_AUDIO_FS_48000) + hdmi_dbg(hdmi->dev, " 48000\n"); + if(audio->rate & HDMI_AUDIO_FS_88200) + hdmi_dbg(hdmi->dev, " 88200\n"); + if(audio->rate & HDMI_AUDIO_FS_96000) + hdmi_dbg(hdmi->dev, " 96000\n"); + if(audio->rate & HDMI_AUDIO_FS_176400) + hdmi_dbg(hdmi->dev, " 176400\n"); + if(audio->rate & HDMI_AUDIO_FS_192000) + hdmi_dbg(hdmi->dev, " 192000\n"); + + hdmi_dbg(hdmi->dev, "Support audio word lenght: \n"); + if(audio->rate & HDMI_AUDIO_WORD_LENGTH_16bit) + hdmi_dbg(hdmi->dev, " 16bit\n"); + if(audio->rate & HDMI_AUDIO_WORD_LENGTH_20bit) + hdmi_dbg(hdmi->dev, " 20bit\n"); + if(audio->rate & HDMI_AUDIO_WORD_LENGTH_24bit) + hdmi_dbg(hdmi->dev, " 24bit\n"); + } + hdmi_dbg(hdmi->dev, "******** Show Sink Info ********\n"); +} + +/** + * hdmi_ouputmode_select - select hdmi transmitter output mode: hdmi or dvi? + * @hdmi: handle of hdmi + * @edid_ok: get EDID data success or not, HDMI_ERROR_SUCESS means success. + */ +int hdmi_ouputmode_select(struct hdmi *hdmi, int edid_ok) +{ + struct list_head *head = &hdmi->edid.modelist; + struct fb_monspecs *specs = hdmi->edid.specs; + struct fb_videomode *modedb = NULL; + int i, pixclock; + + if(edid_ok != HDMI_ERROR_SUCESS) { + dev_err(hdmi->dev, "warning: EDID error, assume sink as HDMI !!!!"); + hdmi->edid.sink_hdmi = 1; + } + + if(edid_ok != HDMI_ERROR_SUCESS) { + hdmi->edid.ycbcr444 = 0; + hdmi->edid.ycbcr422 = 0; + hdmi->autoconfig = HDMI_DISABLE; + } + if(head->next == head) { + dev_info(hdmi->dev, "warning: no CEA video mode parsed from EDID !!!!"); + // If EDID get error, list all system supported mode. + // If output mode is set to DVI and EDID is ok, check + // the output timing. + + if(hdmi->edid.sink_hdmi == 0 && specs && specs->modedb_len) { + /* Get max resolution timing */ + modedb = &specs->modedb[0]; + for (i = 0; i < specs->modedb_len; i++) { + if(specs->modedb[i].xres > modedb->xres) + modedb = &specs->modedb[i]; + else if(specs->modedb[i].yres > modedb->yres) + modedb = &specs->modedb[i]; + } + // For some monitor, the max pixclock read from EDID is smaller + // than the clock of max resolution mode supported. We fix it. + pixclock = PICOS2KHZ(modedb->pixclock); + pixclock /= 250; + pixclock *= 250; + pixclock *= 1000; + if(pixclock == 148250000) + pixclock = 148500000; + if(pixclock > specs->dclkmax) + specs->dclkmax = pixclock; + } + + for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++) { + if(modedb) { + if( (hdmi_mode[i].pixclock < specs->dclkmin) || + (hdmi_mode[i].pixclock > specs->dclkmax) || + (hdmi_mode[i].refresh < specs->vfmin) || + (hdmi_mode[i].refresh > specs->vfmax) || + (hdmi_mode[i].xres > modedb->xres) || + (hdmi_mode[i].yres > modedb->yres) ) + continue; + } + hdmi_add_videomode(&hdmi_mode[i], head); + } + } + + #ifdef HDMI_DEBUG + hdmi_show_sink_info(hdmi); + #endif + return HDMI_ERROR_SUCESS; +} +/** + * hdmi_videomode_compare - compare 2 videomodes + * @mode1: first videomode + * @mode2: second videomode + * + * RETURNS: + * 1 if mode1 > mode2, 0 if mode1 = mode2, -1 mode1 < mode2 + */ +static int hdmi_videomode_compare(const struct fb_videomode *mode1, + const struct fb_videomode *mode2) +{ + if(mode1->xres > mode2->xres) + return 1; + else if(mode1->xres == mode2->xres) + { + if(mode1->yres > mode2->yres) + return 1; + else if(mode1->yres == mode2->yres) + { + if(mode1->pixclock > mode2->pixclock) + return 1; + else if(mode1->pixclock == mode2->pixclock) + { + if(mode1->refresh > mode2->refresh) + return 1; + else if(mode1->refresh == mode2->refresh) + return 0; + } + } + } + return -1; +} +/** + * hdmi_add_videomode: adds videomode entry to modelist + * @mode: videomode to add + * @head: struct list_head of modelist + * + * NOTES: + * Will only add unmatched mode entries + */ +int hdmi_add_videomode(const struct fb_videomode *mode, struct list_head *head) +{ + struct list_head *pos; + struct fb_modelist *modelist, *modelist_new; + struct fb_videomode *m; + int i, found = 0; + + for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++) + { + m =(struct fb_videomode*) &hdmi_mode[i]; + if (fb_mode_is_equal(m, mode)) { + found = 1; + break; + } + } + + if (found) { + list_for_each(pos, head) { + modelist = list_entry(pos, struct fb_modelist, list); + m = &modelist->mode; + if (fb_mode_is_equal(m, mode)) { + // m == mode + return 0; + } + else + { + if(hdmi_videomode_compare(m, mode) == -1) { + break; + } + } + } + + modelist_new = kmalloc(sizeof(struct fb_modelist), + GFP_KERNEL); + if (!modelist_new) + return -ENOMEM; + modelist_new->mode = hdmi_mode[i]; + list_add_tail(&modelist_new->list, pos); + } + + return 0; +} + +/** + * hdmi_videomode_to_vic: transverse video mode to vic + * @vmode: videomode to transverse + * + */ +int hdmi_videomode_to_vic(struct fb_videomode *vmode) +{ + unsigned char vic = 0; + int i = 0; + + for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++) + { + if( vmode->vmode == hdmi_mode[i].vmode && + vmode->refresh == hdmi_mode[i].refresh && + vmode->xres == hdmi_mode[i].xres && + vmode->left_margin == hdmi_mode[i].left_margin && + vmode->right_margin == hdmi_mode[i].right_margin && + vmode->upper_margin == hdmi_mode[i].upper_margin && + vmode->lower_margin == hdmi_mode[i].lower_margin && + vmode->hsync_len == hdmi_mode[i].hsync_len && + vmode->vsync_len == hdmi_mode[i].vsync_len) + { + if( (vmode->vmode == FB_VMODE_NONINTERLACED && vmode->yres == hdmi_mode[i].yres) || + (vmode->vmode == FB_VMODE_INTERLACED && vmode->yres == hdmi_mode[i].yres/2)) + { + vic = hdmi_mode[i].flag; + break; + } + } + } + return vic; +} + +/** + * hdmi_vic_to_videomode: transverse vic mode to video mode + * @vmode: vic to transverse + * + */ +const struct fb_videomode* hdmi_vic_to_videomode(int vic) +{ + int i; + + if(vic == 0) + return NULL; + + for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++) + { + if(hdmi_mode[i].flag == vic) + return &hdmi_mode[i]; + } + return NULL; +} + +/** + * hdmi_find_best_mode: find the video mode nearest to input vic + * @hdmi: + * @vic: input vic + * + * NOTES: + * If vic is zero, return the high resolution video mode vic. + */ +int hdmi_find_best_mode(struct hdmi* hdmi, int vic) +{ + struct list_head *pos, *head = &hdmi->edid.modelist; + struct fb_modelist *modelist; + struct fb_videomode *m = NULL; + int found = 0; + + if(vic) + { + list_for_each(pos, head) { + modelist = list_entry(pos, struct fb_modelist, list); + m = &modelist->mode; + if(m->flag == vic) + { + found = 1; + break; + } + } + } + if( (vic == 0 || found == 0) && head->next != head) + { + modelist = list_entry(head->next, struct fb_modelist, list); + m = &modelist->mode; + } + if(m != NULL) + return m->flag; + else + return 0; +} + +const char *hdmi_get_video_mode_name(unsigned char vic) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(hdmi_mode); i++) + { + if(vic == hdmi_mode[i].flag) + break; + } + if(i == ARRAY_SIZE(hdmi_mode)) + return NULL; + else + return hdmi_mode[i].name; +} + +/** + * hdmi_switch_fb: switch lcdc mode to required video mode + * @hdmi: + * @type: + * + * NOTES: + * + */ +int hdmi_switch_fb(struct hdmi *hdmi, int vic) +{ + int rc = 0; + + if(hdmi->vic == 0) + hdmi->vic = HDMI_VIDEO_DEFAULT_MODE; + + if(hdmi->lcdc == NULL || hdmi->lcdc->screen == NULL) { + dev_err(hdmi->dev, "lcdc %d not exist\n", HDMI_SOURCE_DEFAULT); + return -1; + } + + rc = hdmi_set_info(hdmi->lcdc->screen, hdmi->vic); + + if(rc == 0) { + rk_fb_switch_screen(hdmi->lcdc->screen, 1, HDMI_SOURCE_DEFAULT); + rk_fb_disp_scale(hdmi->xscale, hdmi->yscale, HDMI_SOURCE_DEFAULT); + } + return rc; +} + +/** + * hdmi_get_status: get hdmi hotplug status + * + * NOTES: + * + */ +int hdmi_get_hotplug(void) +{ + if(hdmi) + return hdmi->hotplug; + else + return HDMI_HPD_REMOVED; +} diff --git a/drivers/video/rockchip/hdmi/rk_hdmi_sysfs.c b/drivers/video/rockchip/hdmi/rk_hdmi_sysfs.c new file mode 100755 index 000000000000..45d28eea57b5 --- /dev/null +++ b/drivers/video/rockchip/hdmi/rk_hdmi_sysfs.c @@ -0,0 +1,177 @@ +#include +#include +#include +#include +#include "rk_hdmi.h" + +static int hdmi_get_enable(struct rk_display_device *device) +{ + struct hdmi *hdmi = device->priv_data; + int enable; + + mutex_lock(&hdmi->enable_mutex); + enable = hdmi->enable; + mutex_unlock(&hdmi->enable_mutex); + + return enable; +} + +static int hdmi_set_enable(struct rk_display_device *device, int enable) +{ + struct hdmi *hdmi = device->priv_data; + + mutex_lock(&hdmi->enable_mutex); + if(hdmi->enable == enable) { + mutex_unlock(&hdmi->enable_mutex); + return 0; + } + hdmi->enable = enable; + + if(hdmi->suspend ) { + mutex_unlock(&hdmi->enable_mutex); + return 0; + } + + if(enable == 0) { + 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); + mutex_unlock(&hdmi->enable_mutex); + } + return 0; +} + +static int hdmi_get_status(struct rk_display_device *device) +{ + struct hdmi *hdmi = device->priv_data; + if(hdmi->hotplug == HDMI_HPD_ACTIVED) + return 1; + else + return 0; +} + +static int hdmi_get_modelist(struct rk_display_device *device, struct list_head **modelist) +{ + struct hdmi *hdmi = device->priv_data; + if(!hdmi->hotplug) + return -1; + *modelist = &hdmi->edid.modelist; + return 0; +} + +static int hdmi_set_mode(struct rk_display_device *device, struct fb_videomode *mode) +{ + struct hdmi *hdmi = device->priv_data; + int vic = hdmi_videomode_to_vic(mode); + + if(!hdmi->hotplug) + return -1; + hdmi->autoconfig = HDMI_DISABLE; + if(vic && hdmi->vic != vic) + { + hdmi->vic = vic; + hdmi->command = HDMI_CONFIG_VIDEO; + 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(10000)); + } + return 0; +} + +static int hdmi_get_mode(struct rk_display_device *device, struct fb_videomode *mode) +{ + struct hdmi *hdmi = device->priv_data; + struct fb_videomode *vmode; + + if(!hdmi->hotplug) + return -1; + + vmode = (struct fb_videomode*) hdmi_vic_to_videomode(hdmi->vic); + if(unlikely(vmode == NULL)) + return -1; + *mode = *vmode; + return 0; +} + +static int hdmi_set_scale(struct rk_display_device *device, int direction, int value) +{ + struct hdmi *hdmi = device->priv_data; + + if(!hdmi || value < 0 || value > 100) + return -1; + + if(direction == DISPLAY_SCALE_X) + hdmi->xscale = value; + else if(direction == DISPLAY_SCALE_Y) + hdmi->yscale = value; + else + return -1; + rk_fb_disp_scale(hdmi->xscale, hdmi->yscale, HDMI_SOURCE_DEFAULT); + return 0; +} + +static int hdmi_get_scale(struct rk_display_device *device, int direction) +{ + struct hdmi *hdmi = device->priv_data; + + if(!hdmi) + return -1; + + if(direction == DISPLAY_SCALE_X) + return hdmi->xscale; + else if(direction == DISPLAY_SCALE_Y) + return hdmi->yscale; + else + return -1; +} + +struct rk_display_ops hdmi_display_ops = { + .setenable = hdmi_set_enable, + .getenable = hdmi_get_enable, + .getstatus = hdmi_get_status, + .getmodelist = hdmi_get_modelist, + .setmode = hdmi_set_mode, + .getmode = hdmi_get_mode, + .setscale = hdmi_set_scale, + .getscale = hdmi_get_scale, +}; + +#if 1 +static int hdmi_display_probe(struct rk_display_device *device, void *devdata) +{ + device->owner = THIS_MODULE; + strcpy(device->type, "HDMI"); + device->priority = DISPLAY_PRIORITY_HDMI; +// device->name = kmalloc(strlen(name), GFP_KERNEL); +// if(device->name) +// { +// strcpy(device->name, name); +// } + device->priv_data = devdata; + device->ops = &hdmi_display_ops; + return 1; +} + +static struct rk_display_driver display_hdmi = { + .probe = hdmi_display_probe, +}; + +static struct rk_display_device *display_device_hdmi = NULL; + +void hdmi_register_display_sysfs(struct hdmi *hdmi, struct device *parent) +{ + display_device_hdmi = rk_display_device_register(&display_hdmi, parent, hdmi); +} + +void hdmi_unregister_display_sysfs(struct hdmi *hdmi) +{ + if(display_device_hdmi) + rk_display_device_unregister(display_device_hdmi); +} +#endif diff --git a/drivers/video/rockchip/hdmi/rk_hdmi_task.c b/drivers/video/rockchip/hdmi/rk_hdmi_task.c new file mode 100755 index 000000000000..233d1757ff04 --- /dev/null +++ b/drivers/video/rockchip/hdmi/rk_hdmi_task.c @@ -0,0 +1,312 @@ +#include +#include +#include "rk_hdmi.h" + +#ifdef CONFIG_HDMI_RK30_CTL_CODEC +extern void codec_set_spk(bool on); +#endif + +#define HDMI_MAX_TRY_TIMES 1 +#define HDMI_MAX_ID 1 + +static char *envp[] = {"INTERFACE=HDMI", NULL}; + +static void hdmi_sys_show_state(int state) +{ + switch(state) + { + case HDMI_SLEEP: + dev_printk(KERN_INFO, hdmi->dev, "HDMI_SLEEP\n"); + break; + case HDMI_INITIAL: + dev_printk(KERN_INFO, hdmi->dev, "HDMI_INITIAL\n"); + break; + case WAIT_HOTPLUG: + dev_printk(KERN_INFO, hdmi->dev, "WAIT_HOTPLUG\n"); + break; + case READ_PARSE_EDID: + dev_printk(KERN_INFO, hdmi->dev, "READ_PARSE_EDID\n"); + break; + case WAIT_HDMI_ENABLE: + dev_printk(KERN_INFO, hdmi->dev, "WAIT_HDMI_ENABLE\n"); + break; + case SYSTEM_CONFIG: + dev_printk(KERN_INFO, hdmi->dev, "SYSTEM_CONFIG\n"); + break; + case CONFIG_VIDEO: + dev_printk(KERN_INFO, hdmi->dev, "CONFIG_VIDEO\n"); + break; + case CONFIG_AUDIO: + dev_printk(KERN_INFO, hdmi->dev, "CONFIG_AUDIO\n"); + break; + case PLAY_BACK: + dev_printk(KERN_INFO, hdmi->dev, "PLAY_BACK\n"); + break; + default: + dev_printk(KERN_INFO, hdmi->dev, "Unkown State %d\n", state); + break; + } +} + +int hdmi_sys_init(void) +{ + hdmi->hotplug = HDMI_HPD_REMOVED; + hdmi->state = HDMI_SLEEP; + hdmi->enable = HDMI_ENABLE; + hdmi->autoconfig = HDMI_AUTO_CONFIGURE; + hdmi->display = HDMI_DISABLE; + + hdmi->vic = HDMI_VIDEO_DEFAULT_MODE; + hdmi->audio.channel = HDMI_AUDIO_DEFAULT_CHANNEL; + hdmi->audio.rate = HDMI_AUDIO_DEFAULT_RATE; + hdmi->audio.word_length = HDMI_AUDIO_DEFAULT_WORD_LENGTH; + + memset(&hdmi->edid, 0, sizeof(struct hdmi_edid)); + INIT_LIST_HEAD(&hdmi->edid.modelist); + return 0; +} + +void hdmi_sys_remove(void) +{ + 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); + } + memset(&hdmi->edid, 0, sizeof(struct hdmi_edid)); + INIT_LIST_HEAD(&hdmi->edid.modelist); + hdmi->display = HDMI_DISABLE; + rk_fb_switch_screen(hdmi->lcdc->screen, 0, HDMI_SOURCE_DEFAULT); + kobject_uevent_env(&hdmi->dev->kobj, KOBJ_REMOVE, envp); + #ifdef CONFIG_SWITCH + switch_set_state(&(hdmi->switch_hdmi), 0); + #endif + #ifdef CONFIG_HDMI_RK30_CTL_CODEC + codec_set_spk(1); + #endif +} + +static void hdmi_sys_sleep(void) +{ + mutex_lock(&hdmi->enable_mutex); + if(hdmi->enable) + disable_irq(hdmi->irq); + hdmi->state = HDMI_SLEEP; + hdmi->hdmi_removed(); + if(hdmi->enable) + enable_irq(hdmi->irq); + mutex_unlock(&hdmi->enable_mutex); +} + +static int hdmi_process_command(void) +{ + int change, state = hdmi->state; + + change = hdmi->command; + if(change != HDMI_CONFIG_NONE) + { + hdmi->command = HDMI_CONFIG_NONE; + switch(change) + { + case HDMI_CONFIG_ENABLE: + /* disable HDMI */ + mutex_lock(&hdmi->enable_mutex); + if(!hdmi->enable || hdmi->suspend) + { + if(hdmi->hotplug == HDMI_HPD_ACTIVED) + hdmi_sys_remove(); + hdmi->state = HDMI_SLEEP; + hdmi->hotplug = HDMI_HPD_REMOVED; + hdmi->hdmi_removed(); + state = HDMI_SLEEP; + } + mutex_unlock(&hdmi->enable_mutex); + if(hdmi->wait == 1) { + complete(&hdmi->complete); + hdmi->wait = 0; + } + break; + case HDMI_CONFIG_COLOR: + if(state > CONFIG_VIDEO) + state = CONFIG_VIDEO; + break; + case HDMI_CONFIG_HDCP: + break; + case HDMI_CONFIG_DISPLAY: + break; + case HDMI_CONFIG_AUDIO: + if(state > CONFIG_AUDIO) + state = CONFIG_AUDIO; + break; + case HDMI_CONFIG_VIDEO: + default: + if(state > SYSTEM_CONFIG) + state = SYSTEM_CONFIG; + else + { + if(hdmi->wait == 1) { + complete(&hdmi->complete); + hdmi->wait = 0; + } + } + break; + } + } + else if(state == HDMI_SLEEP) + state = WAIT_HOTPLUG; + return state; +} + +static DEFINE_MUTEX(work_mutex); + +void hdmi_work(struct work_struct *work) +{ + int hotplug, state_last; + int rc = HDMI_ERROR_SUCESS, trytimes = 0; + struct hdmi_video_para video; + + mutex_lock(&work_mutex); + /* Process hdmi command */ + hdmi->state = hdmi_process_command(); + + if(!hdmi->enable || hdmi->suspend) { + mutex_unlock(&work_mutex); + return; + } + hotplug = hdmi->detect_hotplug(); + hdmi_dbg(hdmi->dev, "[%s] hotplug %02x curvalue %d\n", __FUNCTION__, hotplug, hdmi->hotplug); + + if(hotplug != hdmi->hotplug) + { + if(hotplug == HDMI_HPD_ACTIVED){ + hdmi->state = READ_PARSE_EDID; + } + else if(hdmi->hotplug == HDMI_HPD_ACTIVED) { + hdmi_sys_remove(); + hdmi->hotplug = hotplug; + if(hotplug == HDMI_HPD_REMOVED) + hdmi_sys_sleep(); + else { + hdmi->state = WAIT_HOTPLUG; + hdmi->hdmi_removed(); + } + if(hdmi->wait == 1) { + complete(&hdmi->complete); + hdmi->wait = 0; + } + mutex_unlock(&work_mutex); + return; + } + else if(hotplug == HDMI_HPD_REMOVED) { + hdmi->state = HDMI_SLEEP; + hdmi->hdmi_removed(); + } + hdmi->hotplug = hotplug; + } + else if(hotplug == HDMI_HPD_REMOVED) + hdmi_sys_sleep(); + + do { + hdmi_sys_show_state(hdmi->state); + state_last = hdmi->state; + switch(hdmi->state) + { + case READ_PARSE_EDID: + rc = hdmi_sys_parse_edid(hdmi); + if(rc == HDMI_ERROR_SUCESS) + { + hdmi->state = SYSTEM_CONFIG; + kobject_uevent_env(&hdmi->dev->kobj, KOBJ_ADD, envp); + #ifdef CONFIG_SWITCH + switch_set_state(&(hdmi->switch_hdmi), 1); + #endif + #ifdef CONFIG_HDMI_RK30_CTL_CODEC + codec_set_spk(0); + #endif + } + break; + case SYSTEM_CONFIG: + if(hdmi->autoconfig) + hdmi->vic = hdmi_find_best_mode(hdmi, 0); + else + hdmi->vic = hdmi_find_best_mode(hdmi, hdmi->vic); + rc = hdmi_switch_fb(hdmi, hdmi->vic); + if(rc == HDMI_ERROR_SUCESS) + hdmi->state = CONFIG_VIDEO; + break; + case CONFIG_VIDEO: + hdmi->display = HDMI_DISABLE; + video.vic = hdmi->vic; + video.input_mode = VIDEO_INPUT_RGB_YCBCR_444; + video.input_color = VIDEO_INPUT_COLOR_RGB;//VIDEO_INPUT_COLOR_YCBCR + video.output_mode = hdmi->edid.sink_hdmi; + + if(hdmi->edid.ycbcr444) + video.output_color = VIDEO_OUTPUT_YCBCR444; + else if(hdmi->edid.ycbcr422) + video.output_color = VIDEO_OUTPUT_YCBCR422; + else + video.output_color = VIDEO_OUTPUT_RGB444; + // For DVI, output RGB + if(hdmi->edid.sink_hdmi == 0) + video.output_color = VIDEO_OUTPUT_RGB444; + + rc = hdmi->config_video(&video); + if(rc == HDMI_ERROR_SUCESS) + { + if(hdmi->edid.sink_hdmi) + hdmi->state = CONFIG_AUDIO; + else + hdmi->state = PLAY_BACK; + } + break; + case CONFIG_AUDIO: + rc = hdmi->config_audio(&(hdmi->audio)); + + if(rc == HDMI_ERROR_SUCESS) + hdmi->state = PLAY_BACK; + break; + case PLAY_BACK: + if(hdmi->display != HDMI_ENABLE) { + hdmi->control_output(HDMI_ENABLE); + hdmi->display = HDMI_ENABLE; + if(hdmi->hdcp_cb) { + hdmi->hdcp_cb(); + } + } + + if(hdmi->wait == 1) { + complete(&hdmi->complete); + hdmi->wait = 0; + } + break; + default: + break; + } + if(rc != HDMI_ERROR_SUCESS) + { + trytimes++; + msleep(10); + } + if(hdmi->state != state_last) + trytimes = 0; + + }while((hdmi->state != state_last || (rc != HDMI_ERROR_SUCESS) ) && trytimes < HDMI_MAX_TRY_TIMES); + +// if(trytimes == HDMI_MAX_TRY_TIMES) +// { +// if(hdmi->hotplug) { +// hdmi_sys_remove(); +// hdmi->hotplug = HDMI_HPD_REMOVED; +// hdmi_sys_sleep(); +// +// } +// } + hdmi_dbg(hdmi->dev, "[%s] done\n", __FUNCTION__); + mutex_unlock(&work_mutex); +} +