rk30: support hdmi.
authorZheng Yang <zhengyang@rock-chips.com>
Sat, 31 Mar 2012 07:13:35 +0000 (15:13 +0800)
committerZheng Yang <zhengyang@rock-chips.com>
Sat, 31 Mar 2012 07:13:35 +0000 (15:13 +0800)
13 files changed:
arch/arm/mach-rk30/devices.c
drivers/video/rockchip/Kconfig
drivers/video/rockchip/Makefile
drivers/video/rockchip/hdmi/Kconfig [new file with mode: 0644]
drivers/video/rockchip/hdmi/Makefile [new file with mode: 0755]
drivers/video/rockchip/hdmi/rk30_hdmi.c [new file with mode: 0755]
drivers/video/rockchip/hdmi/rk30_hdmi.h [new file with mode: 0755]
drivers/video/rockchip/hdmi/rk30_hdmi_edid.c [new file with mode: 0644]
drivers/video/rockchip/hdmi/rk30_hdmi_hw.c [new file with mode: 0755]
drivers/video/rockchip/hdmi/rk30_hdmi_hw.h [new file with mode: 0755]
drivers/video/rockchip/hdmi/rk30_hdmi_lcdc.c [new file with mode: 0755]
drivers/video/rockchip/hdmi/rk30_hdmi_task.c [new file with mode: 0755]
drivers/video/rockchip/hdmi/rk_hdmi.h [new file with mode: 0755]

index 2dd1c4ef3406ce57790d76168b3568a49fb3c8ce..dc0677036f61a564ceeebf651953805d1629424d 100755 (executable)
@@ -782,6 +782,28 @@ static struct platform_device device_lcdc1 = {
 };
 #endif
 
+#ifdef CONFIG_HDMI_RK30
+static struct resource resource_hdmi[] = {
+       [0] = {
+               .start = RK30_HDMI_PHYS,
+               .end   = RK30_HDMI_PHYS + RK30_HDMI_SIZE - 1,
+               .flags = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start = IRQ_HDMI,
+               .end   = IRQ_HDMI,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device device_hdmi = {
+       .name                           = "rk30-hdmi",
+       .id                                     = -1,
+       .num_resources          = ARRAY_SIZE(resource_hdmi),
+       .resource                       = resource_hdmi,
+};
+#endif
+
 #ifdef CONFIG_RGA_RK30
 static struct resource resource_rga[] = {
        [0] = {
@@ -1089,6 +1111,9 @@ static int __init rk30_init_devices(void)
 #ifdef CONFIG_LCDC1_RK30
        platform_device_register(&device_lcdc1);
 #endif
+#ifdef CONFIG_HDMI_RK30
+       platform_device_register(&device_hdmi);
+#endif
 #ifdef CONFIG_ADC_RK30
        platform_device_register(&device_adc);
 #endif
index d68e5f450b6c70f3f2e31da41f8a90f48d3bfca0..bd4490fd3c79ad3d9975570ad79d71dca6d6c0b7 100644 (file)
@@ -27,4 +27,5 @@ config LCDC1_RK30
         help
            Support rk30 lcdc1 if you say y here
 
+source "drivers/video/rockchip/hdmi/Kconfig"
 source "drivers/video/rockchip/rga/Kconfig"
index 90f3b91aa59151e406b4d12a3cb96abb0636d588..31ef7412ce190196daec5be142d9d5bf4caac69d 100644 (file)
@@ -1,3 +1,4 @@
 obj-$(CONFIG_FB_ROCKCHIP) += rk_fb.o rkfb_sysfs.o
 obj-$(CONFIG_LCDC_RK30) += chips/rk30_lcdc.o  
 obj-$(CONFIG_RGA_RK30) += rga/
+obj-$(CONFIG_HDMI_RK30) += hdmi/
diff --git a/drivers/video/rockchip/hdmi/Kconfig b/drivers/video/rockchip/hdmi/Kconfig
new file mode 100644 (file)
index 0000000..f92621d
--- /dev/null
@@ -0,0 +1,7 @@
+config HDMI_RK30
+        bool "hdmi support"
+        depends on LCDC_RK30
+#        default y
+        help
+           Support rk30 hdmi if you say y here
+           
diff --git a/drivers/video/rockchip/hdmi/Makefile b/drivers/video/rockchip/hdmi/Makefile
new file mode 100755 (executable)
index 0000000..470549a
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_HDMI_RK30) += rk30_hdmi_hw.o rk30_hdmi_edid.o rk30_hdmi_lcdc.o rk30_hdmi_task.o rk30_hdmi.o
diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi.c b/drivers/video/rockchip/hdmi/rk30_hdmi.c
new file mode 100755 (executable)
index 0000000..cbc08af
--- /dev/null
@@ -0,0 +1,196 @@
+#include <linux/module.h>\r
+#include <linux/kernel.h>\r
+#include <linux/errno.h>\r
+#include <linux/string.h>\r
+#include <linux/mm.h>\r
+#include <linux/slab.h>\r
+#include <linux/delay.h>\r
+#include <linux/device.h>\r
+#include <linux/init.h>\r
+#include <linux/dma-mapping.h>\r
+#include <linux/interrupt.h>\r
+#include <linux/platform_device.h>\r
+#include <linux/clk.h>\r
+\r
+#include <mach/board.h>\r
+#include <mach/io.h>\r
+#include <mach/gpio.h>\r
+#include <mach/iomux.h>\r
+#include "rk30_hdmi.h"\r
+#include "rk30_hdmi_hw.h"\r
+\r
+struct hdmi *hdmi = NULL;\r
+\r
+extern irqreturn_t hdmi_irq(int irq, void *priv);\r
+extern void hdmi_work(struct work_struct *work);\r
+extern struct rk_lcdc_device_driver * rk_get_lcdc_drv(int  id);\r
+\r
+#ifdef CONFIG_HAS_EARLYSUSPEND\r
+static void hdmi_early_suspend(struct early_suspend *h)\r
+{\r
+       hdmi_dbg(hdmi->dev, "hdmi enter early suspend\n");\r
+       disable_irq(hdmi->irq);\r
+       if(hdmi->hotplug)\r
+               hdmi_sys_remove();\r
+       return;\r
+}\r
+\r
+static void hdmi_early_resume(struct early_suspend *h)\r
+{\r
+       hdmi_dbg(hdmi->dev, "hdmi exit early resume\n");\r
+       enable_irq(hdmi->irq);\r
+       return;\r
+}\r
+#endif\r
+\r
+\r
+\r
+\r
+\r
+static inline void hdmi_io_remap(void)\r
+{\r
+       unsigned int value;\r
+       \r
+       // Remap HDMI IO Pin\r
+       rk30_mux_api_set(GPIO0A2_HDMII2CSDA_NAME, GPIO0A_HDMI_I2C_SDA);\r
+       rk30_mux_api_set(GPIO0A1_HDMII2CSCL_NAME, GPIO0A_HDMI_I2C_SCL);\r
+       rk30_mux_api_set(GPIO0A0_HDMIHOTPLUGIN_NAME, GPIO0A_HDMI_HOT_PLUG_IN);\r
+               \r
+       // Select LCDC0 as video source and enabled.\r
+       value = (HDMI_SOURCE_DEFAULT << 14) | (1 << 30);\r
+       writel(value, GRF_SOC_CON0 + RK30_GRF_BASE);\r
+       \r
+       // internal hclk = hdmi_hclk/32\r
+       HDMIWrReg(0x800, 19);\r
+       \r
+       hdmi->lcdc = rk_get_lcdc_drv(HDMI_SOURCE_DEFAULT);\r
+}\r
+\r
+static int __devinit rk30_hdmi_probe (struct platform_device *pdev)\r
+{\r
+       int ret;\r
+       struct resource *res;\r
+       struct resource *mem;\r
+       \r
+       hdmi = kmalloc(sizeof(struct hdmi), GFP_KERNEL);\r
+       if(!hdmi)\r
+       {\r
+       dev_err(&pdev->dev, ">>rk30 lcdc inf kmalloc fail!");\r
+       return -ENOMEM;\r
+       }\r
+       memset(hdmi, 0, sizeof(struct hdmi));\r
+       hdmi->dev = &pdev->dev;\r
+       platform_set_drvdata(pdev, hdmi);\r
+\r
+       hdmi->hclk = clk_get(NULL,"hclk_hdmi");\r
+       if(IS_ERR(hdmi->hclk))\r
+       {\r
+               dev_err(hdmi->dev, "Unable to get hdmi hclk\n");\r
+               ret = -ENXIO;\r
+               goto err0;\r
+       }\r
+       clk_enable(hdmi->hclk);\r
+       \r
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);\r
+       if (!res) {\r
+               dev_err(hdmi->dev, "Unable to get register resource\n");\r
+               ret = -ENXIO;\r
+               goto err0;\r
+       }\r
+       \r
+       mem = request_mem_region(res->start, (res->end - res->start) + 1, pdev->name);\r
+       if (!mem)\r
+       {\r
+       dev_err(hdmi->dev, "failed to request mem region for hdmi\n");\r
+       ret = -ENOENT;\r
+       goto err0;\r
+       }\r
+\r
+       \r
+       hdmi->regbase = (int)ioremap(res->start, (res->end - res->start) + 1);\r
+       if (!hdmi->regbase) {\r
+               dev_err(hdmi->dev, "cannot ioremap registers\n");\r
+               ret = -ENXIO;\r
+               goto err1;\r
+       }\r
+       \r
+       hdmi_io_remap();\r
+       \r
+       hdmi_sys_init();\r
+       \r
+       hdmi->workqueue = create_singlethread_workqueue("hdmi");\r
+       INIT_DELAYED_WORK(&(hdmi->delay_work), hdmi_work);\r
+\r
+       #ifdef CONFIG_HAS_EARLYSUSPEND\r
+       hdmi->early_suspend.suspend = hdmi_early_suspend;\r
+       hdmi->early_suspend.resume = hdmi_early_resume;\r
+       hdmi->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN - 1;\r
+       register_early_suspend(&hdmi->early_suspend);\r
+       #endif\r
+\r
+       /* get the IRQ */\r
+       hdmi->irq = platform_get_irq(pdev, 0);\r
+       if(hdmi->irq <= 0) {\r
+               dev_err(hdmi->dev, "failed to get hdmi irq resource (%d).\n", hdmi->irq);\r
+               ret = -ENXIO;\r
+               goto err2;\r
+       }\r
+       hdmi_dbg(hdmi->dev, "[%s] hdmi irq is 0x%x\n", __FUNCTION__, hdmi->irq);\r
+       /* request the IRQ */\r
+       ret = request_irq(hdmi->irq, hdmi_irq, 0, dev_name(&pdev->dev), hdmi);\r
+       if (ret)\r
+       {\r
+               dev_err(hdmi->dev, "hdmi request_irq failed (%d).\n", ret);\r
+               goto err2;\r
+       }\r
+\r
+       hdmi_dbg(hdmi->dev, "rk30 hdmi probe sucess.\n");\r
+       return 0;\r
+err2:\r
+#ifdef CONFIG_HAS_EARLYSUSPEND\r
+       unregister_early_suspend(&hdmi->early_suspend);\r
+#endif\r
+       iounmap((void*)hdmi->regbase);\r
+err1:\r
+       release_mem_region(res->start,(res->end - res->start) + 1);\r
+       clk_disable(hdmi->hclk);\r
+err0:\r
+       kfree(hdmi);\r
+       hdmi_dbg(hdmi->dev, "rk30 hdmi probe error.\n");\r
+       return ret;\r
+}\r
+\r
+static int __devexit rk30_hdmi_remove(struct platform_device *pdev)\r
+{\r
+       return 0;\r
+}\r
+\r
+static void rk30_hdmi_shutdown(struct platform_device *pdev)\r
+{\r
+\r
+}\r
+\r
+static struct platform_driver rk30_hdmi_driver = {\r
+       .probe          = rk30_hdmi_probe,\r
+       .remove         = __devexit_p(rk30_hdmi_remove),\r
+       .driver         = {\r
+               .name   = "rk30-hdmi",\r
+               .owner  = THIS_MODULE,\r
+       },\r
+       .shutdown   = rk30_hdmi_shutdown,\r
+};\r
+\r
+static int __init rk30_hdmi_init(void)\r
+{\r
+    return platform_driver_register(&rk30_hdmi_driver);\r
+}\r
+\r
+static void __exit rk30_hdmi_exit(void)\r
+{\r
+    platform_driver_unregister(&rk30_hdmi_driver);\r
+}\r
+\r
+\r
+fs_initcall(rk30_hdmi_init);\r
+//module_init(rk30_hdmi_init);\r
+module_exit(rk30_hdmi_exit);
\ No newline at end of file
diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi.h b/drivers/video/rockchip/hdmi/rk30_hdmi.h
new file mode 100755 (executable)
index 0000000..61fa202
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef __RK30_HDMI_H__
+#define __RK30_HDMI_H__
+
+#include <linux/fb.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+#include <linux/display-sys.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include "../../display/screen/screen.h"
+#include <linux/rk_fb.h>
+#include "rk_hdmi.h"
+
+// HDMI video source
+enum {
+       HDMI_SOURCE_LCDC0 = 0,
+       HDMI_SOURCE_LCDC1
+};
+
+#define HDMI_SOURCE_DEFAULT            HDMI_SOURCE_LCDC1
+
+/* default HDMI output video mode */
+#define HDMI_VIDEO_DEFAULT_MODE                        HDMI_1280x720p_60Hz//HDMI_1920x1080p_60Hz
+#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;
+       struct rk_lcdc_device_driver *lcdc;
+       
+       struct workqueue_struct *workqueue;
+       struct delayed_work delay_work;
+       
+       int wait;
+       struct completion       complete;
+#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
+       
+};
+
+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);
+#endif /* __RK30_HDMI_H__ */
\ No newline at end of file
diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_edid.c b/drivers/video/rockchip/hdmi/rk30_hdmi_edid.c
new file mode 100644 (file)
index 0000000..a5f291e
--- /dev/null
@@ -0,0 +1,410 @@
+#include "rk30_hdmi.h"\r
+#include "rk30_hdmi_hw.h"\r
+#include "../../edid.h"\r
+\r
+#define hdmi_edid_error(fmt, ...) \\r
+        printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)\r
+\r
+#if 0\r
+#define hdmi_edid_debug(fmt, ...) \\r
+        printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)\r
+#else\r
+#define hdmi_edid_debug(fmt, ...)      \r
+#endif\r
+\r
+typedef enum HDMI_EDID_ERRORCODE\r
+{\r
+       E_HDMI_EDID_SUCCESS = 0,\r
+       E_HDMI_EDID_PARAM,\r
+       E_HDMI_EDID_HEAD,\r
+       E_HDMI_EDID_CHECKSUM,\r
+       E_HDMI_EDID_VERSION,\r
+       E_HDMI_EDID_UNKOWNDATA,\r
+       E_HDMI_EDID_NOMEMORY\r
+}HDMI_EDID_ErrorCode;\r
+\r
+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};\r
+static int hdmi_edid_checksum(unsigned char *buf)\r
+{\r
+       int i;\r
+       int checksum = 0;\r
+       \r
+       for(i = 0; i < HDMI_EDID_BLOCK_SIZE; i++)\r
+               checksum += buf[i];     \r
+       \r
+       checksum &= 0xff;\r
+       \r
+       if(checksum == 0)\r
+               return E_HDMI_EDID_SUCCESS;\r
+       else\r
+               return E_HDMI_EDID_CHECKSUM;\r
+}\r
+\r
+/*\r
+       @Des    Parse Detail Timing Descriptor.\r
+       @Param  buf     :       pointer to DTD data.\r
+       @Param  pvic:   VIC of DTD descripted.\r
+ */\r
+static int hdmi_edid_parse_dtd(unsigned char *block, struct fb_videomode *mode)\r
+{\r
+       mode->xres = H_ACTIVE;\r
+       mode->yres = V_ACTIVE;\r
+       mode->pixclock = PIXEL_CLOCK;\r
+//     mode->pixclock /= 1000;\r
+//     mode->pixclock = KHZ2PICOS(mode->pixclock);\r
+       mode->right_margin = H_SYNC_OFFSET;\r
+       mode->left_margin = (H_ACTIVE + H_BLANKING) -\r
+               (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);\r
+       mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -\r
+               V_SYNC_WIDTH;\r
+       mode->lower_margin = V_SYNC_OFFSET;\r
+       mode->hsync_len = H_SYNC_WIDTH;\r
+       mode->vsync_len = V_SYNC_WIDTH;\r
+       if (HSYNC_POSITIVE)\r
+               mode->sync |= FB_SYNC_HOR_HIGH_ACT;\r
+       if (VSYNC_POSITIVE)\r
+               mode->sync |= FB_SYNC_VERT_HIGH_ACT;\r
+       mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *\r
+                                    (V_ACTIVE + V_BLANKING));\r
+       if (INTERLACED) {\r
+               mode->yres *= 2;\r
+               mode->upper_margin *= 2;\r
+               mode->lower_margin *= 2;\r
+               mode->vsync_len *= 2;\r
+               mode->vmode |= FB_VMODE_INTERLACED;\r
+       }\r
+       mode->flag = FB_MODE_IS_DETAILED;\r
+\r
+       hdmi_edid_debug("<<<<<<<<Detailed Time>>>>>>>>>\n");\r
+       hdmi_edid_debug("%d KHz Refresh %d Hz",  PIXEL_CLOCK/1000, mode->refresh);\r
+       hdmi_edid_debug("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,\r
+              H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);\r
+       hdmi_edid_debug("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,\r
+              V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);\r
+       hdmi_edid_debug("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",\r
+              (VSYNC_POSITIVE) ? "+" : "-");\r
+       return E_HDMI_EDID_SUCCESS;\r
+}\r
+\r
+static int hdmi_edid_parse_base(unsigned char *buf, int *extend_num, struct hdmi_edid *pedid)\r
+{\r
+       int rc;\r
+       \r
+       if(buf == NULL || extend_num == NULL)\r
+               return E_HDMI_EDID_PARAM;\r
+               \r
+       #ifdef DEBUG    \r
+       for(i = 0; i < HDMI_EDID_BLOCK_SIZE; i++)\r
+       {\r
+               hdmi_edid_debug("%02x ", buf[i]&0xff);\r
+               if((i+1) % 16 == 0)\r
+                       hdmi_edid_debug("\n");\r
+       }\r
+       #endif\r
+       \r
+       // Check first 8 byte to ensure it is an edid base block.\r
+       if( buf[0] != 0x00 ||\r
+           buf[1] != 0xFF ||\r
+           buf[2] != 0xFF ||\r
+           buf[3] != 0xFF ||\r
+           buf[4] != 0xFF ||\r
+           buf[5] != 0xFF ||\r
+           buf[6] != 0xFF ||\r
+           buf[7] != 0x00)\r
+    {\r
+        hdmi_edid_error("[EDID] check header error\n");\r
+        return E_HDMI_EDID_HEAD;\r
+    }\r
+    \r
+    *extend_num = buf[0x7e];\r
+    #ifdef DEBUG\r
+    hdmi_edid_debug("[EDID] extend block num is %d\n", buf[0x7e]);\r
+    #endif\r
+    \r
+    // Checksum\r
+    rc = hdmi_edid_checksum(buf);\r
+    if( rc != E_HDMI_EDID_SUCCESS)\r
+    {\r
+       hdmi_edid_error("[EDID] base block checksum error\n");\r
+       return E_HDMI_EDID_CHECKSUM;\r
+    }\r
+\r
+       pedid->specs = kzalloc(sizeof(struct fb_monspecs), GFP_KERNEL);\r
+       if(pedid->specs == NULL)\r
+               return E_HDMI_EDID_NOMEMORY;\r
+               \r
+       fb_edid_to_monspecs(buf, pedid->specs);\r
+       \r
+    return E_HDMI_EDID_SUCCESS;\r
+}\r
+\r
+// Parse CEA Short Video Descriptor\r
+static int hdmi_edid_get_cea_svd(unsigned char *buf, struct hdmi_edid *pedid)\r
+{\r
+       const struct fb_videomode *mode;\r
+       int count, i, j, vic;\r
+\r
+       count = buf[0] & 0x1F;\r
+       for(i = 0; i < count; i++)\r
+       {\r
+               hdmi_edid_debug("[EDID-CEA] %02x VID %d native %d\n", buf[1 + i], buf[1 + i] & 0x7f, buf[1 + i] >> 7);\r
+               vic = buf[1 + i] & 0x7f;\r
+               for(j = 0; j < ARRAY_SIZE(double_aspect_vic); j++)\r
+               {\r
+                       if(vic == double_aspect_vic[j])\r
+                       {       \r
+                               vic--;\r
+                               break;\r
+                       }\r
+               }\r
+               if(vic)\r
+               {\r
+                       mode = hdmi_vic_to_videomode(vic);\r
+                       if(mode)\r
+                       {       \r
+                               hdmi_add_videomode(mode, &pedid->modelist);\r
+                       }\r
+               }\r
+       }\r
+       return 0;\r
+}\r
+\r
+// Parse CEA Short Audio Descriptor\r
+static int hdmi_edid_parse_cea_sad(unsigned char *buf, struct hdmi_edid *pedid)\r
+{\r
+       int i, count;\r
+       \r
+       count = buf[0] & 0x1F;\r
+       pedid->audio = kmalloc((count/3)*sizeof(struct hdmi_audio), GFP_KERNEL);\r
+       if(pedid->audio == NULL)\r
+               return E_HDMI_EDID_NOMEMORY;\r
+       pedid->audio_num = count/3;\r
+       for(i = 0; i < pedid->audio_num; i++)\r
+       {\r
+               pedid->audio[i].type = (buf[1 + i*3] >> 3) & 0x0F;\r
+               pedid->audio[i].channel = (buf[1 + i*3] & 0x07) + 1;\r
+               pedid->audio[i].rate = buf[1 + i*3 + 1];\r
+               if(pedid->audio[i].type == HDMI_AUDIO_LPCM)//LPCM \r
+               {\r
+                       pedid->audio[i].word_length = buf[1 + i*3 + 2];\r
+               }\r
+//             printk("[EDID-CEA] type %d channel %d rate %d word length %d\n", \r
+//                     pedid->audio[i].type, pedid->audio[i].channel, pedid->audio[i].rate, pedid->audio[i].word_length);\r
+       }\r
+       return E_HDMI_EDID_SUCCESS;\r
+}\r
+// Parse CEA 861 Serial Extension.\r
+static int hdmi_edid_parse_extensions_cea(unsigned char *buf, struct hdmi_edid *pedid)\r
+{\r
+       unsigned int ddc_offset, native_dtd_num, cur_offset = 4;\r
+       unsigned int underscan_support, baseaudio_support;\r
+       unsigned int tag, IEEEOUI = 0;\r
+//     unsigned int supports_ai,  dc_48bit, dc_36bit, dc_30bit, dc_y444;\r
+//     unsigned char vic;\r
+       \r
+       if(buf == NULL)\r
+               return E_HDMI_EDID_PARAM;\r
+               \r
+       // Check ces extension version\r
+       if(buf[1] != 3)\r
+       {\r
+               hdmi_edid_error("[EDID-CEA] error version.\n");\r
+               return E_HDMI_EDID_VERSION;\r
+       }\r
+       \r
+       ddc_offset = buf[2];\r
+       underscan_support = (buf[3] >> 7) & 0x01;\r
+       baseaudio_support = (buf[3] >> 6) & 0x01;\r
+       pedid->ycbcr444 = (buf[3] >> 5) & 0x01;\r
+       pedid->ycbcr422 = (buf[3] >> 4) & 0x01;\r
+       native_dtd_num = buf[3] & 0x0F;\r
+       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);\r
+       // Parse data block\r
+       while(cur_offset < ddc_offset)\r
+       {\r
+               tag = buf[cur_offset] >> 5;\r
+               switch(tag)\r
+               {\r
+                       case 0x02:      // Video Data Block\r
+                               hdmi_edid_debug("[EDID-CEA] It is a Video Data Block.\n");\r
+                               hdmi_edid_get_cea_svd(buf + cur_offset, pedid);\r
+                               break;\r
+                       case 0x01:      // Audio Data Block\r
+                               hdmi_edid_debug("[EDID-CEA] It is a Audio Data Block.\n");\r
+                               hdmi_edid_parse_cea_sad(buf + cur_offset, pedid);\r
+                               break;\r
+                       case 0x04:      // Speaker Allocation Data Block\r
+                               hdmi_edid_debug("[EDID-CEA] It is a Speaker Allocatio Data Block.\n");\r
+                               break;\r
+                       case 0x03:      // Vendor Specific Data Block\r
+                               hdmi_edid_debug("[EDID-CEA] It is a Vendor Specific Data Block.\n");\r
+\r
+                               IEEEOUI = buf[cur_offset + 2 + 1];\r
+                               IEEEOUI <<= 8;\r
+                               IEEEOUI += buf[cur_offset + 1 + 1];\r
+                               IEEEOUI <<= 8;\r
+                               IEEEOUI += buf[cur_offset + 1];\r
+                               hdmi_edid_debug("[EDID-CEA] IEEEOUI is 0x%08x.\n", IEEEOUI);\r
+                               if(IEEEOUI == 0x0c03)\r
+                                       pedid->sink_hdmi = 1;\r
+//                             if(count > 5)\r
+//                             {\r
+//                                     pedid->deepcolor = (buf[cur_offset + 5] >> 3) & 0x0F;\r
+//                                     supports_ai = buf[cur_offset + 5] >> 7;\r
+//                                     dc_48bit = (buf[cur_offset + 5] >> 6) & 0x1;\r
+//                                     dc_36bit = (buf[cur_offset + 5] >> 5) & 0x1;\r
+//                                     dc_30bit = (buf[cur_offset + 5] >> 4) & 0x1;\r
+//                                     dc_y444 = (buf[cur_offset + 5] >> 3) & 0x1;\r
+//                                     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);\r
+//                             }\r
+//                             if(count > 6)\r
+//                                     pedid->maxtmdsclock = buf[cur_offset + 6] * 5000000;\r
+//                             if(count > 7)\r
+//                             {\r
+//                                     pedid->latency_fields_present = (buf[cur_offset + 7] & 0x80) ? 1:0;\r
+//                                     pedid->i_latency_fields_present = (buf[cur_offset + 7] & 0x40) ? 1:0;\r
+//                             }\r
+//                             if(count > 9 && pedid->latency_fields_present)\r
+//                             {\r
+//                                     pedid->video_latency = buf[cur_offset + 8];\r
+//                                     pedid->audio_latency = buf[cur_offset + 9];\r
+//                             }\r
+//                             if(count > 11 && pedid->i_latency_fields_present)\r
+//                             {\r
+//                                     pedid->interlaced_video_latency = buf[cur_offset + 10];\r
+//                                     pedid->interlaced_audio_latency = buf[cur_offset + 11];\r
+//                             }\r
+                               break;          \r
+                       case 0x05:      // VESA DTC Data Block\r
+                               hdmi_edid_debug("[EDID-CEA] It is a VESA DTC Data Block.\n");\r
+                               break;\r
+                       case 0x07:      // Use Extended Tag\r
+                               hdmi_edid_debug("[EDID-CEA] It is a Use Extended Tag Data Block.\n");\r
+                               break;\r
+                       default:\r
+                               hdmi_edid_error("[EDID-CEA] unkowned data block tag.\n");\r
+                               break;\r
+               }\r
+               cur_offset += (buf[cur_offset] & 0x1F) + 1;\r
+       }\r
+#if 1  \r
+{\r
+       // Parse DTD\r
+       struct fb_videomode *vmode = kmalloc(sizeof(struct fb_videomode), GFP_KERNEL);\r
+       if(vmode == NULL)\r
+               return E_HDMI_EDID_SUCCESS; \r
+       while(ddc_offset < HDMI_EDID_BLOCK_SIZE - 2)    //buf[126] = 0 and buf[127] = checksum\r
+       {\r
+               if(!buf[ddc_offset] && !buf[ddc_offset + 1])\r
+                       break;\r
+               memset(vmode, 0, sizeof(struct fb_videomode));\r
+               hdmi_edid_parse_dtd(buf + ddc_offset, vmode);\r
+               hdmi_add_videomode(vmode, &pedid->modelist);\r
+               ddc_offset += 18;\r
+       }\r
+       kfree(vmode);\r
+}\r
+#endif\r
+       return E_HDMI_EDID_SUCCESS;\r
+}\r
+\r
+static int hdmi_edid_parse_extensions(unsigned char *buf, struct hdmi_edid *pedid)\r
+{\r
+       int rc;\r
+       \r
+       if(buf == NULL || pedid == NULL)\r
+               return E_HDMI_EDID_PARAM;\r
+               \r
+       // Checksum\r
+    rc = hdmi_edid_checksum(buf);\r
+    if( rc != E_HDMI_EDID_SUCCESS)\r
+    {\r
+       hdmi_edid_error("[EDID] extensions block checksum error\n");\r
+       return E_HDMI_EDID_CHECKSUM;\r
+    }\r
+    \r
+    switch(buf[0])\r
+    {\r
+       case 0xF0:\r
+               hdmi_edid_debug("[EDID-EXTEND] It is a extensions block map.\n");\r
+               break;\r
+       case 0x02:\r
+               hdmi_edid_debug("[EDID-EXTEND] It is a  CEA 861 Series Extension.\n");\r
+               hdmi_edid_parse_extensions_cea(buf, pedid);\r
+               break;\r
+       case 0x10:\r
+               hdmi_edid_debug("[EDID-EXTEND] It is a Video Timing Block Extension.\n");\r
+               break;\r
+       case 0x40:\r
+               hdmi_edid_debug("[EDID-EXTEND] It is a Display Information Extension.\n");\r
+               break;\r
+       case 0x50:\r
+               hdmi_edid_debug("[EDID-EXTEND] It is a Localized String Extension.\n");\r
+               break;\r
+       case 0x60:\r
+               hdmi_edid_debug("[EDID-EXTEND] It is a Digital Packet Video Link Extension.\n");\r
+               break;\r
+       default:\r
+               hdmi_edid_debug("[EDID-EXTEND] Unkowned extension.\n");\r
+               break;\r
+    }\r
+    \r
+    return E_HDMI_EDID_SUCCESS;\r
+}\r
+\r
+\r
+int hdmi_sys_parse_edid(struct hdmi* hdmi)\r
+{\r
+       struct hdmi_edid *pedid;\r
+       unsigned char *buff = NULL;\r
+       int rc = HDMI_ERROR_SUCESS, extendblock = 0, i;\r
+       \r
+       if(hdmi == NULL)\r
+               return HDMI_ERROR_FALSE;\r
+\r
+       pedid = &(hdmi->edid);\r
+       memset(pedid, 0, sizeof(struct hdmi_edid));\r
+       INIT_LIST_HEAD(&pedid->modelist);\r
+       \r
+       buff = kmalloc(HDMI_EDID_BLOCK_SIZE, GFP_KERNEL);\r
+       if(buff == NULL)\r
+       {               \r
+               hdmi_dbg(hdmi->dev, "[%s] can not allocate memory for edid buff.\n", __FUNCTION__);\r
+               return -1;\r
+       }\r
+       // Read base block edid.\r
+       memset(buff, 0 , HDMI_EDID_BLOCK_SIZE);\r
+       rc = rk30_hdmi_read_edid(0, buff);\r
+       if(rc)\r
+       {\r
+               dev_err(hdmi->dev, "[HDMI] read edid base block error\n");\r
+               goto out;\r
+       }\r
+       rc = hdmi_edid_parse_base(buff, &extendblock, pedid);\r
+       if(rc)\r
+       {\r
+               dev_err(hdmi->dev, "[HDMI] parse edid base block error\n");\r
+               goto out;\r
+       }\r
+       for(i = 1; i < extendblock + 1; i++)\r
+       {\r
+               memset(buff, 0 , HDMI_EDID_BLOCK_SIZE);\r
+               rc = rk30_hdmi_read_edid(i, buff);\r
+               if(rc)\r
+               {\r
+                       printk("[HDMI] read edid block %d error\n", i); \r
+                       goto out;\r
+               }\r
+               rc = hdmi_edid_parse_extensions(buff, pedid);\r
+               if(rc)\r
+               {\r
+                       dev_err(hdmi->dev, "[HDMI] parse edid block %d error\n", i);\r
+                       continue;\r
+               }\r
+       }\r
+out:\r
+       if(buff)\r
+               kfree(buff);\r
+       rc = hdmi_ouputmode_select(hdmi, rc);\r
+       return rc;\r
+}
\ 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
new file mode 100755 (executable)
index 0000000..9686744
--- /dev/null
@@ -0,0 +1,440 @@
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <mach/io.h>
+#include "rk30_hdmi.h"
+#include "rk30_hdmi_hw.h"
+
+static char interrupt1 = 0, interrupt2 = 0;
+
+static inline void delay100us(void)
+{
+       msleep(1);
+}
+
+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;
+       msleep(10);
+}
+
+int rk30_hdmi_detect_hotplug(void)
+{
+       int value =     HDMIRdReg(HPD_MENS_STA);
+       
+       hdmi_dbg(hdmi->dev, "[%s] value %02x\n", __FUNCTION__, value);
+       if( (value & (m_HOTPLUG_STATUS | m_MSEN_STATUS)) == (m_HOTPLUG_STATUS | m_MSEN_STATUS) )
+               return HDMI_HPD_INSERT;
+       else
+               return HDMI_HPD_REMOVED;
+}
+
+#define HDMI_EDID_DDC_CLK      100000
+int rk30_hdmi_read_edid(int block, unsigned char *buff)
+{
+       int value, ret = -ENXIO, ddc_bus_freq = 0;
+       char interrupt = 0;
+       
+       hdmi_dbg(hdmi->dev, "[%s] block %d\n", __FUNCTION__, block);
+       
+       //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_L, (ddc_bus_freq >> 8) & 0xFF);
+       
+       // Enable edid interrupt
+//     HDMIMskReg(value, INTR_MASK1, (m_INT_EDID_ERR | m_INT_EDID_READY), (m_INT_EDID_ERR | m_INT_EDID_READY));
+       HDMIWrReg(INTR_MASK1, m_INT_EDID_ERR | m_INT_EDID_READY | m_INT_HOTPLUG | m_INT_MSENS);
+       // Config EDID block and segment addr
+       HDMIWrReg(EDID_WORD_ADDR, (block%2) * 0x80);
+       HDMIWrReg(EDID_SEGMENT_POINTER, block/2);       
+
+       value = 200;
+       while(value--)
+       {
+               interrupt = interrupt1;
+//             hdmi_dbg(hdmi->dev, "[%s] interrupt %02x\n", __FUNCTION__, interrupt);
+               if(interrupt & (m_INT_EDID_ERR | m_INT_EDID_READY))
+               {
+                       interrupt1 &= ~(m_INT_EDID_ERR | m_INT_EDID_READY);
+                       break;
+               }
+               msleep(10);
+       }
+       hdmi_dbg(hdmi->dev, "[%s] edid read value %d\n", __FUNCTION__, value);
+       // Disable edid interrupt
+       HDMIMskReg(value, INTR_MASK1, (m_INT_EDID_ERR|m_INT_EDID_READY), 0);
+       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__);
+//             for(value = 0; value < 128; value++) {
+//                     printk("%02x ,", buff[value]);
+//                     if( (value + 1) % 8 == 0)
+//                             printk("\n");
+//             }
+       }
+       if(interrupt & m_INT_EDID_ERR)
+       {
+                hdmi_dbg(hdmi->dev, "[%s] edid read error\n", __FUNCTION__);
+               
+       }
+       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)
+{
+       hdmi_dbg(hdmi->dev, "[%s] line %d\n", __FUNCTION__, __LINE__);
+       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, 0xA2);
+                       rk30_hdmi_config_phy_reg(0x170, 0x0e);
+                       rk30_hdmi_config_phy_reg(0x174, 0x22);
+                       rk30_hdmi_config_phy_reg(0x178, 0x00);
+                       break;
+               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_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_dbg(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;
+       char info[SIZE_AVI_INFOFRAME];
+       
+       HDMIWrReg(CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
+       HDMIWrReg(CONTROL_PACKET_HB0, 0x82);
+       HDMIWrReg(CONTROL_PACKET_HB1, 0x2);
+       HDMIWrReg(CONTROL_PACKET_HB2, 0x0D);
+       memset(info, 0, SIZE_AVI_INFOFRAME);
+       
+       info[1] = (AVI_COLOR_MODE_RGB << 5);
+       info[2] = (AVI_COLORIMETRY_NO_DATA << 6) | (AVI_CODED_FRAME_ASPECT_NO_DATA << 4) | ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME;
+       info[3] = 0;
+       info[4] = vic;
+       info[5] = 0;
+
+       // Calculate AVI InfoFrame ChecKsum
+       info[0] = 0x82 + 0x02 +0x0D;
+       for (i = 1; i < SIZE_AVI_INFOFRAME; i++)
+       {
+       info[0] += info[i];
+       }
+       info[0] = 0x100 - info[0];
+       
+       for(i = 0; i < SIZE_AVI_INFOFRAME; i++)
+               HDMIWrReg(CONTROL_PACKET_PB_ADDR + i*4, info[i]);
+}
+
+int rk30_hdmi_config_video(int vic, int output_color, int output_mode)
+{
+       int value;
+       struct fb_videomode *mode;
+       
+       hdmi_dbg(hdmi->dev, "[%s]\n", __FUNCTION__);
+       
+       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);
+               
+       // 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(VIDEO_INPUT_RGB_YCBCR_444) | EXTERNAL_DE)    
+       HDMIMskReg(value, VIDEO_CTRL1, m_VIDEO_OUTPUT_MODE | m_VIDEO_INPUT_DEPTH | m_VIDEO_INPUT_COLOR_MODE, \
+               v_VIDEO_OUTPUT_MODE(output_color) | v_VIDEO_INPUT_DEPTH(VIDEO_INPUT_DEPTH_8BIT) | VIDEO_INPUT_COLOR_RGB)
+       
+       // Set HDMI Mode
+       HDMIWrReg(HDCP_CTRL, v_HDMI_DVI(output_mode));
+
+       // Set ext video
+       mode = (struct fb_videomode *)hdmi_vic_to_videomode(vic);
+       if(mode == NULL)
+       {
+               hdmi_dbg(hdmi->dev, "[%s] not found vic %d\n", __FUNCTION__, vic);
+               return -ENOENT;
+       }
+       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);
+       
+       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(output_mode == OUTPUT_HDMI) {
+               rk30_hdmi_config_avi(vic, output_mode);
+               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(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;
+       
+       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:
+                       dev_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:
+                       dev_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( (audio->channel + audio->channel%2)/2));       
+       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 {
+               //  Switch to power save mode_d
+               rk30_hdmi_set_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)
+               rk30_hdmi_set_pwr_mode(PWR_SAVE_MODE_A);
+               
+       return HDMI_ERROR_SUCESS;
+}
+
+
+
+
+irqreturn_t hdmi_irq(int irq, void *priv)
+{              
+       unsigned int count = 0x10000;
+       if(hdmi->pwr_mode == PWR_SAVE_MODE_A)
+       {
+               HDMIWrReg(SYS_CTRL, 0x20);
+               hdmi->pwr_mode = PWR_SAVE_MODE_B;
+               while(count--);
+       }
+       else
+       {
+               interrupt1 = HDMIRdReg(INTR_STATUS1);
+               interrupt2 = HDMIRdReg(INTR_STATUS2);
+               HDMIWrReg(INTR_STATUS1, interrupt1);
+               HDMIWrReg(INTR_STATUS2, interrupt2);
+//             hdmi_dbg(hdmi->dev, "[%s] interrupt1 %02x\n", __FUNCTION__, interrupt1);
+               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, 100);    
+               }
+//             else if(hdmi->state == HDMI_SLEEP)
+//                     HDMIWrReg(SYS_CTRL, 0x10);
+
+       }
+       return IRQ_HANDLED;
+}
+
diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_hw.h b/drivers/video/rockchip/hdmi/rk30_hdmi_hw.h
new file mode 100755 (executable)
index 0000000..c326a68
--- /dev/null
@@ -0,0 +1,262 @@
+#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 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_SETTING2 */
+#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
+
+/* 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                     0xe             // 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
+};
+
+
+/* HDCP_CTRL */
+#define HDCP_CTRL              0x2bc
+
+enum {
+       OUTPUT_DVI = 0,
+       OUTPUT_HDMI
+};
+#define m_HDMI_DVI             (1 << 1)
+#define v_HDMI_DVI(n)  (n << 1)
+
+#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 INTR_MASK1                                     0x248
+#define INTR_MASK2                                     0x24c
+#define INTR_MASK3                                     0x258
+#define INTR_MASK4                                     0x25c
+#define INTR_STATUS1                           0x250
+#define INTR_STATUS2                           0x254
+#define INTR_STATUS3                           0x260
+#define INTR_STATUS4                           0x264
+
+#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 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)
+
+
+#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 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(int vic, int output_color, int output_mode);
+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
new file mode 100755 (executable)
index 0000000..b5f8899
--- /dev/null
@@ -0,0 +1,505 @@
+#include <linux/console.h>
+#include "rk30_hdmi.h"
+#include "rk30_hdmi_hw.h"
+#include<linux/rk_fb.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      },
+*/
+
+};
+
+int hdmi_set_info(struct rk29fb_screen *screen, unsigned int vic)
+{
+    int i;
+    
+    if(screen == NULL || vic == 0)
+       return -1;
+    
+    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->config_set.resolution == 0)
+//             hdmi->config_set.resolution = HDMI_DEFAULT_RESOLUTION;
+               
+       if(hdmi->lcdc == NULL || hdmi->lcdc->screen == NULL) {
+               printk("***************\n");
+               return -1;
+       }
+
+       rc = hdmi_set_info(hdmi->lcdc->screen, vic);
+
+       if(rc == 0 &&  hdmi->lcdc->load_screen) {
+               
+               rc = hdmi->lcdc->load_screen(hdmi->lcdc, 0);
+       }
+       return rc;
+}
diff --git a/drivers/video/rockchip/hdmi/rk30_hdmi_task.c b/drivers/video/rockchip/hdmi/rk30_hdmi_task.c
new file mode 100755 (executable)
index 0000000..00a59eb
--- /dev/null
@@ -0,0 +1,181 @@
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include "rk30_hdmi.h"
+#include "rk30_hdmi_hw.h"
+
+
+#define HDMI_MAX_TRY_TIMES     1
+
+static char *envp[] = {"INTERFACE=HDMI", NULL};
+
+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->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;
+       
+       return 0;
+}
+
+void hdmi_sys_remove(void)
+{
+       rk30_hdmi_removed();
+       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->state = HDMI_SLEEP;
+       hdmi->hotplug = HDMI_HPD_REMOVED;
+}
+
+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 */
+                               if(!hdmi->enable)
+                               {
+                                       if(hdmi->hotplug)
+                                               hdmi_sys_remove();
+                                       state = HDMI_SLEEP;
+                               }
+                               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;
+               }
+       }
+       
+       return state;
+}
+
+void hdmi_work(struct work_struct *work)
+{
+       int hotplug, state_last, state;
+       int rc = HDMI_ERROR_SUCESS, trytimes = 0;
+       /* Process hdmi command */
+       state = hdmi_process_command();
+       
+       if(!hdmi->enable)
+               return;
+       
+       hotplug = rk30_hdmi_detect_hotplug();
+       hdmi_dbg(hdmi->dev, "[%s] hotplug %02x curvalue %d\n", __FUNCTION__, hotplug, hdmi->hotplug);
+       if(hotplug != hdmi->hotplug)
+       {
+               hdmi->hotplug  = hotplug;
+               if(hdmi->hotplug  == HDMI_HPD_INSERT)
+                       state = READ_PARSE_EDID;
+               else {
+                       hdmi_sys_remove();
+                       kobject_uevent_env(&hdmi->dev->kobj, KOBJ_REMOVE, envp);
+                       return;
+               }                       
+       }
+       do {
+               state_last = state;
+               switch(state)
+               {
+                       case READ_PARSE_EDID:
+                               rc = hdmi_sys_parse_edid(hdmi);
+                               if(rc == HDMI_ERROR_SUCESS)
+                               {
+                                       state = SYSTEM_CONFIG;  
+                                       kobject_uevent_env(&hdmi->dev->kobj, KOBJ_ADD, envp);
+                               }
+                               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)
+                                       state = CONFIG_VIDEO;
+                               break;
+                       case CONFIG_VIDEO:                                      
+                               rc = rk30_hdmi_config_video(hdmi->vic, VIDEO_OUTPUT_RGB444, hdmi->edid.sink_hdmi);                      
+                               if(rc == HDMI_ERROR_SUCESS)
+                               {
+                                       if(hdmi->edid.sink_hdmi)
+                                               state = CONFIG_AUDIO;
+                                       else
+                                               state = PLAY_BACK;
+                               }
+                               break;
+                       case CONFIG_AUDIO:
+                               rc = rk30_hdmi_config_audio(&(hdmi->audio));
+                                                       
+                               if(rc == HDMI_ERROR_SUCESS)
+                                       state = PLAY_BACK;
+                               break;
+                       case PLAY_BACK:
+                               rk30_hdmi_control_output(1);
+                               if(hdmi->wait == 1) {   
+                                       complete(&hdmi->complete);
+                                       hdmi->wait = 0;                                         
+                               }
+                               break;
+                       default:
+                               break;
+               }
+               if(rc != HDMI_ERROR_SUCESS)
+               {
+                       trytimes++;
+                       msleep(10);
+               }
+               if(state != state_last)
+                       trytimes = 0;
+       }while((state != state_last || (rc != HDMI_ERROR_SUCESS) ) && trytimes < HDMI_MAX_TRY_TIMES);
+       
+       if(trytimes == HDMI_MAX_TRY_TIMES)
+       {
+               if(hdmi->hotplug)
+                       hdmi_sys_remove();
+       }
+}
\ No newline at end of file
diff --git a/drivers/video/rockchip/hdmi/rk_hdmi.h b/drivers/video/rockchip/hdmi/rk_hdmi.h
new file mode 100755 (executable)
index 0000000..c40a180
--- /dev/null
@@ -0,0 +1,198 @@
+#ifndef __RK_HDMI_H__
+#define __RK_HDMI_H__
+
+/********************************************************************
+**                          ½á¹¹¶¨Òå                                *
+********************************************************************/
+/* HDMI video mode code according CEA-861-E*/
+enum hdmi_video_mode
+{
+       HDMI_640x480p_60Hz = 1,
+       HDMI_720x480p_60Hz_4_3,
+       HDMI_720x480p_60Hz_16_9,
+       HDMI_1280x720p_60Hz,
+       HDMI_1920x1080i_60Hz,           //5
+       HDMI_720x480i_60Hz_4_3,
+       HDMI_720x480i_60Hz_16_9,
+       HDMI_720x240p_60Hz_4_3,
+       HDMI_720x240p_60Hz_16_9,
+       HDMI_2880x480i_60Hz_4_3,        //10
+       HDMI_2880x480i_60Hz_16_9,
+       HDMI_2880x240p_60Hz_4_3,
+       HDMI_2880x240p_60Hz_16_9,
+       HDMI_1440x480p_60Hz_4_3,
+       HDMI_1440x480p_60Hz_16_9,       //15
+       HDMI_1920x1080p_60Hz,
+       HDMI_720x576p_50Hz_4_3,
+       HDMI_720x576p_50Hz_16_9,
+       HDMI_1280x720p_50Hz,
+       HDMI_1920x1080i_50Hz,           //20
+       HDMI_720x576i_50Hz_4_3,
+       HDMI_720x576i_50Hz_16_9,
+       HDMI_720x288p_50Hz_4_3,
+       HDMI_720x288p_50Hz_16_9,
+       HDMI_2880x576i_50Hz_4_3,        //25
+       HDMI_2880x576i_50Hz_16_9,
+       HDMI_2880x288p_50Hz_4_3,
+       HDMI_2880x288p_50Hz_16_9,
+       HDMI_1440x576p_50Hz_4_3,
+       HDMI_1440x576p_50Hz_16_9,       //30
+       HDMI_1920x1080p_50Hz,
+       HDMI_1920x1080p_24Hz,
+       HDMI_1920x1080p_25Hz,
+       HDMI_1920x1080p_30Hz,
+       HDMI_2880x480p_60Hz_4_3,        //35
+       HDMI_2880x480p_60Hz_16_9,
+       HDMI_2880x576p_50Hz_4_3,
+       HDMI_2880x576p_50Hz_16_9,
+       HDMI_1920x1080i_50Hz_2,         // V Line 1250 total
+       HDMI_1920x1080i_100Hz,          //40
+       HDMI_1280x720p_100Hz,
+       HDMI_720x576p_100Hz_4_3,
+       HDMI_720x576p_100Hz_16_9,
+       HDMI_720x576i_100Hz_4_3,
+       HDMI_720x576i_100Hz_16_9,       //45
+       HDMI_1920x1080i_120Hz,
+       HDMI_1280x720p_120Hz,
+       HDMI_720x480p_120Hz_4_3,
+       HDMI_720x480p_120Hz_16_9,       
+       HDMI_720x480i_120Hz_4_3,        //50
+       HDMI_720x480i_120Hz_16_9,
+       HDMI_720x576p_200Hz_4_3,
+       HDMI_720x576p_200Hz_16_9,
+       HDMI_720x576i_200Hz_4_3,
+       HDMI_720x576i_200Hz_16_9,       //55
+       HDMI_720x480p_240Hz_4_3,
+       HDMI_720x480p_240Hz_16_9,       
+       HDMI_720x480i_240Hz_4_3,
+       HDMI_720x480i_240Hz_16_9,
+       HDMI_1280x720p_24Hz,            //60
+       HDMI_1280x720p_25Hz,
+       HDMI_1280x720p_30Hz,
+       HDMI_1920x1080p_120Hz,
+       HDMI_1920x1080p_100Hz,
+};
+
+/* HDMI Video Data Color Mode */
+enum {
+       HDMI_COLOR_RGB = 0,
+       HDMI_COLOR_YCbCr422,
+       HDMI_COLOR_YCbCr444
+};
+
+/* HDMI Audio type */
+enum hdmi_audio_type
+{
+       HDMI_AUDIO_LPCM = 1,
+       HDMI_AUDIO_AC3,
+       HDMI_AUDIO_MPEG1,
+       HDMI_AUDIO_MP3,
+       HDMI_AUDIO_MPEG2,
+       HDMI_AUDIO_AAC_LC,              //AAC
+       HDMI_AUDIO_DTS,
+       HDMI_AUDIO_ATARC,
+       HDMI_AUDIO_DSD,                 //One bit Audio
+       HDMI_AUDIO_E_AC3,
+       HDMI_AUDIO_DTS_HD,
+       HDMI_AUDIO_MLP,
+       HDMI_AUDIO_DST,
+       HDMI_AUDIO_WMA_PRO
+};
+
+/* I2S Fs */
+enum hdmi_audio_fs {
+       HDMI_AUDIO_FS_32000  = 0x1,
+       HDMI_AUDIO_FS_44100  = 0x2,
+       HDMI_AUDIO_FS_48000  = 0x4,
+       HDMI_AUDIO_FS_88200  = 0x8,
+       HDMI_AUDIO_FS_96000  = 0x10,
+       HDMI_AUDIO_FS_176400 = 0x20,
+       HDMI_AUDIO_FS_192000 = 0x40
+};
+
+/* Audio Word Length */
+enum hdmi_audio_word_length {
+       HDMI_AUDIO_WORD_LENGTH_16bit = 0x1,
+       HDMI_AUDIO_WORD_LENGTH_20bit = 0x2,
+       HDMI_AUDIO_WORD_LENGTH_24bit = 0x4
+};
+
+/* EDID block size */
+#define HDMI_EDID_BLOCK_SIZE   128
+
+// HDMI state machine
+enum hdmi_state{
+       HDMI_SLEEP = 0,
+       HDMI_INITIAL,
+       WAIT_HOTPLUG,
+       READ_PARSE_EDID,
+       WAIT_HDMI_ENABLE,
+       SYSTEM_CONFIG,
+       CONFIG_VIDEO,
+       CONFIG_AUDIO,
+       PLAY_BACK,
+};
+
+// HDMI configuration command
+enum hdmi_change {
+       HDMI_CONFIG_NONE = 0,
+       HDMI_CONFIG_VIDEO,
+       HDMI_CONFIG_AUDIO,
+       HDMI_CONFIG_COLOR,
+       HDMI_CONFIG_HDCP,
+       HDMI_CONFIG_ENABLE,
+       HDMI_CONFIG_DISABLE,
+       HDMI_CONFIG_DISPLAY
+};
+
+// HDMI Hotplug status
+enum {
+       HDMI_HPD_REMOVED = 0,
+       HDMI_HPD_INSERT
+};
+
+/* HDMI STATUS */
+#define HDMI_DISABLE   0
+#define HDMI_ENABLE            1
+#define HDMI_UNKOWN            0xFF
+
+/* HDMI Error Code */
+enum hdmi_errorcode
+{
+       HDMI_ERROR_SUCESS = 0,
+       HDMI_ERROR_FALSE,
+       HDMI_ERROR_I2C,
+       HDMI_ERROR_EDID,
+};
+
+/* HDMI audio parameters */
+struct hdmi_audio {
+       u32 type;                                                       //Audio type
+       u32     channel;                                                //Audio channel number
+       u32     rate;                                                   //Audio sampling rate
+       u32     word_length;                                    //Audio data word length
+};
+
+struct hdmi_edid {
+       unsigned char sink_hdmi;                        //HDMI display device flag
+       unsigned char ycbcr444;                         //Display device support YCbCr444
+       unsigned char ycbcr422;                         //Display device support YCbCr422
+       unsigned char deepcolor;                        //bit3:DC_48bit; bit2:DC_36bit; bit1:DC_30bit; bit0:DC_Y444;
+       struct fb_monspecs      *specs;                 //Device spec
+       struct list_head modelist;                      //Device supported display mode list
+       struct hdmi_audio *audio;                       //Device supported audio info
+       int     audio_num;                                              //Device supported audio type number
+};
+
+extern const struct fb_videomode hdmi_mode[];
+
+#define HDMI_DEBUG
+
+#ifdef HDMI_DEBUG
+#define hdmi_dbg(dev, format, arg...)          \
+       dev_printk(KERN_INFO , dev , format , ## arg)
+#else
+#define hdmi_dbg(dev, format, arg...)  
+#endif
+
+#endif