config HDMI_RK30
bool "hdmi support"
depends on LCDC_RK30
+ select FB_MODE_HELPERS
# default y
help
Support rk30 hdmi if you say y here
-obj-$(CONFIG_HDMI_RK30) += rk30_hdmi_hw.o rk30_hdmi_edid.o rk30_hdmi_lcdc.o rk30_hdmi_task.o rk30_hdmi.o
+obj-$(CONFIG_HDMI_RK30) += rk30_hdmi_hw.o rk30_hdmi_edid.o rk30_hdmi_lcdc.o rk30_hdmi_task.o rk30_hdmi_sysfs.o rk30_hdmi.o
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
+extern void hdmi_register_display_sysfs(struct hdmi *hdmi, struct device *parent);\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
+ hdmi->enable = 0;\r
+ hdmi->command = HDMI_CONFIG_ENABLE;\r
+ /* wait for hdmi configuration finish */\r
+ while(hdmi->wait)\r
+ msleep(10);\r
+ init_completion(&hdmi->complete);\r
+ hdmi->wait = 1;\r
+ queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0);\r
+ wait_for_completion_interruptible_timeout(&hdmi->complete,\r
+ msecs_to_jiffies(5000));\r
+ flush_delayed_work(&hdmi->delay_work);\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
+ hdmi->enable = 1;\r
enable_irq(hdmi->irq);\r
return;\r
}\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
hdmi->dev = &pdev->dev;\r
platform_set_drvdata(pdev, hdmi);\r
\r
+ hdmi->lcdc = rk_get_lcdc_drv(HDMI_SOURCE_DEFAULT);\r
+ if(hdmi->lcdc == NULL)\r
+ {\r
+ dev_err(hdmi->dev, "can not connect to video source lcdc\n");\r
+ ret = -ENXIO;\r
+ goto err0;\r
+ }\r
+\r
hdmi->hclk = clk_get(NULL,"hclk_hdmi");\r
if(IS_ERR(hdmi->hclk))\r
{\r
}\r
\r
hdmi_io_remap();\r
- \r
hdmi_sys_init();\r
\r
hdmi->workqueue = create_singlethread_workqueue("hdmi");\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
+ hdmi->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 10;\r
register_early_suspend(&hdmi->early_suspend);\r
#endif\r
+ \r
+ \r
+ hdmi_register_display_sysfs(hdmi, hdmi->dev);\r
+ \r
+ spin_lock_init(&hdmi->irq_lock);\r
\r
/* get the IRQ */\r
hdmi->irq = platform_get_irq(pdev, 0);\r
ret = -ENXIO;\r
goto err2;\r
}\r
- hdmi_dbg(hdmi->dev, "[%s] hdmi irq is 0x%x\n", __FUNCTION__, hdmi->irq);\r
+\r
/* request the IRQ */\r
ret = request_irq(hdmi->irq, hdmi_irq, 0, dev_name(&pdev->dev), hdmi);\r
if (ret)\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
+ kfree(hdmi);\r
return ret;\r
}\r
\r
static int __devexit rk30_hdmi_remove(struct platform_device *pdev)\r
{\r
+ flush_scheduled_work();\r
+ destroy_workqueue(hdmi->workqueue);\r
+ #ifdef CONFIG_HAS_EARLYSUSPEND\r
+ unregister_early_suspend(&hdmi->early_suspend);\r
+ #endif\r
+ iounmap((void*)hdmi->regbase);\r
+// release_mem_region(res->start,(res->end - res->start) + 1);\r
+ clk_disable(hdmi->hclk);\r
+ kfree(hdmi);\r
+ hdmi_dbg(hdmi->dev, "rk30 hdmi unregistered.\n");\r
return 0;\r
}\r
\r
}\r
\r
\r
-fs_initcall(rk30_hdmi_init);\r
-//module_init(rk30_hdmi_init);\r
+//fs_initcall(rk30_hdmi_init);\r
+module_init(rk30_hdmi_init);\r
module_exit(rk30_hdmi_exit);
\ No newline at end of file
#define __RK30_HDMI_H__
#include <linux/fb.h>
+#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/display-sys.h>
// HDMI video source
enum {
HDMI_SOURCE_LCDC0 = 0,
- HDMI_SOURCE_LCDC1
+ HDMI_SOURCE_LCDC1 = 1
};
#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_VIDEO_DEFAULT_MODE HDMI_1920x1080p_60Hz//HDMI_1280x720p_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 workqueue_struct *workqueue;
struct delayed_work delay_work;
+ spinlock_t irq_lock;
+
int wait;
struct completion complete;
#ifdef CONFIG_HAS_EARLYSUSPEND
#include "rk30_hdmi.h"
#include "rk30_hdmi_hw.h"
-static char interrupt1 = 0, interrupt2 = 0;
+static char interrupt1 = 0, interrupt2 = 0, interrupt3 = 0, interrupt4 = 0;
static inline void delay100us(void)
{
}
hdmi->pwr_mode = mode;
msleep(10);
+ hdmi_dbg(hdmi->dev, "[%s] curmode %02x\n", __FUNCTION__, HDMIRdReg(SYS_CTRL));
}
int rk30_hdmi_detect_hotplug(void)
#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;
+ int value, ret = -1, ddc_bus_freq = 0;
+ char interrupt = 0, trytime = 2;
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);
+ msleep(10);
// 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);
+ HDMIMskReg(value, INTR_MASK1, (m_INT_EDID_ERR | m_INT_EDID_READY), (m_INT_EDID_ERR | m_INT_EDID_READY));
- 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))
+ while(trytime--) {
+ // Config EDID block and segment addr
+ HDMIWrReg(EDID_WORD_ADDR, (block%2) * 0x80);
+ HDMIWrReg(EDID_SEGMENT_POINTER, block/2);
+
+ value = 100;
+ while(value--)
{
- interrupt1 &= ~(m_INT_EDID_ERR | m_INT_EDID_READY);
- break;
+ spin_lock(&hdmi->irq_lock);
+ interrupt = interrupt1;
+ spin_unlock(&hdmi->irq_lock);
+// hdmi_dbg(hdmi->dev, "[%s] interrupt %02x value %d\n", __FUNCTION__, interrupt, value);
+ if(interrupt & (m_INT_EDID_ERR | m_INT_EDID_READY))
+ {
+ interrupt1 &= ~(m_INT_EDID_ERR | m_INT_EDID_READY);
+ break;
+ }
+ msleep(10);
}
- msleep(10);
+ hdmi_dbg(hdmi->dev, "[%s] edid read value %d\n", __FUNCTION__, value);
+ if(interrupt & m_INT_EDID_READY)
+ {
+ for(value = 0; value < HDMI_EDID_BLOCK_SIZE; value++)
+ buff[value] = HDMIRdReg(DDC_READ_FIFO_ADDR);
+ ret = 0;
+
+ hdmi_dbg(hdmi->dev, "[%s] edid read sucess\n", __FUNCTION__);
+#if 0
+ for(value = 0; value < 128; value++) {
+ printk("%02x ,", buff[value]);
+ if( (value + 1) % 8 == 0)
+ printk("\n");
+ }
+#endif
+ break;
+ }
+ if(interrupt & m_INT_EDID_ERR)
+ hdmi_dbg(hdmi->dev, "[%s] edid read error\n", __FUNCTION__);
+
}
- 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;
}
rk30_hdmi_config_phy_reg(0x174, 0x22);
rk30_hdmi_config_phy_reg(0x178, 0x00);
break;
+
+ case HDMI_1920x1080i_60Hz:
+ case HDMI_1920x1080i_50Hz:
case HDMI_1280x720p_60Hz:
case HDMI_1280x720p_50Hz:
rk30_hdmi_config_phy_reg(0x158, 0x06);
rk30_hdmi_config_phy_reg(0x174, 0x20);
rk30_hdmi_config_phy_reg(0x178, 0x00);
break;
+
+ case HDMI_720x576p_50Hz_4_3:
+ case HDMI_720x576p_50Hz_16_9:
case HDMI_720x480p_60Hz_4_3:
case HDMI_720x480p_60Hz_16_9:
rk30_hdmi_config_phy_reg(0x158, 0x02);
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)
+ {
+ HDMIWrReg(INTR_MASK1, m_INT_HOTPLUG | m_INT_MSENS);
+ HDMIWrReg(INTR_MASK2, 0);
+ HDMIWrReg(INTR_MASK3, 0);
+ HDMIWrReg(INTR_MASK4, 0);
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)
{
+ hdmi_dbg(hdmi->dev, "hdmi irq wake up\n");
HDMIWrReg(SYS_CTRL, 0x20);
hdmi->pwr_mode = PWR_SAVE_MODE_B;
- while(count--);
+
+ // HDMI was inserted when system is sleeping, irq was triggered only once
+ // when wake up. So we need to check hotplug status.
+ if((rk30_hdmi_detect_hotplug() == HDMI_HPD_INSERT)) {
+ if(hdmi->state == HDMI_SLEEP)
+ hdmi->state = WAIT_HOTPLUG;
+ queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, msecs_to_jiffies(10));
+ }
}
else
{
+ spin_lock(&hdmi->irq_lock);
interrupt1 = HDMIRdReg(INTR_STATUS1);
interrupt2 = HDMIRdReg(INTR_STATUS2);
+ interrupt3 = HDMIRdReg(INTR_STATUS3);
+ interrupt4 = HDMIRdReg(INTR_STATUS4);
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) )
+ HDMIWrReg(INTR_STATUS3, interrupt3);
+ HDMIWrReg(INTR_STATUS4, interrupt4);
+#if 1
+ hdmi_dbg(hdmi->dev, "[%s] interrupt1 %02x interrupt2 %02x interrupt3 %02x interrupt4 %02x\n",\
+ __FUNCTION__, interrupt1, interrupt2, interrupt3, interrupt4);
+#endif
+ if(interrupt1 & (m_INT_HOTPLUG | m_INT_MSENS))
{
if(hdmi->state == HDMI_SLEEP)
hdmi->state = WAIT_HOTPLUG;
interrupt1 &= ~(m_INT_HOTPLUG | m_INT_MSENS);
- queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 100);
+ queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, msecs_to_jiffies(10));
}
-// else if(hdmi->state == HDMI_SLEEP)
-// HDMIWrReg(SYS_CTRL, 0x10);
-
+ else if(hdmi->state == HDMI_SLEEP) {
+ hdmi_dbg(hdmi->dev, "hdmi return to sleep mode\n");
+ HDMIWrReg(SYS_CTRL, 0x10);
+ hdmi->pwr_mode = PWR_SAVE_MODE_A;
+ }
+ spin_unlock(&hdmi->irq_lock);
}
return IRQ_HANDLED;
}
#define HDMIRdReg(addr) __raw_readl(hdmi->regbase + addr)
-#define HDMIWrReg(addr, val) __raw_writel(val, 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);
// hdmi->config_set.resolution = HDMI_DEFAULT_RESOLUTION;
if(hdmi->lcdc == NULL || hdmi->lcdc->screen == NULL) {
- printk("***************\n");
+ dev_err(hdmi->dev, "lcdc %d not exist\n", HDMI_SOURCE_DEFAULT);
return -1;
}
--- /dev/null
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/display-sys.h>
+#include "rk30_hdmi.h"
+
+static int hdmi_get_enable(struct rk_display_device *device)
+{
+ struct hdmi *hdmi = device->priv_data;
+
+ return hdmi->enable;
+}
+
+static int hdmi_set_enable(struct rk_display_device *device, int enable)
+{
+ struct hdmi *hdmi = device->priv_data;
+
+// if(hdmi->enable == enable)
+// return 0;
+// hdmi->enable = enable;
+// hdmi->command = HDMI_CONFIG_DISPLAY;
+// queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0);
+ return 0;
+}
+
+static int hdmi_get_status(struct rk_display_device *device)
+{
+ struct hdmi *hdmi = device->priv_data;
+ if(hdmi->hotplug > HDMI_HPD_REMOVED)
+ return 1;
+ else
+ return 0;
+}
+
+static int hdmi_get_modelist(struct rk_display_device *device, struct list_head **modelist)
+{
+ struct hdmi *hdmi = device->priv_data;
+ if(!hdmi->hotplug)
+ return -1;
+ *modelist = &hdmi->edid.modelist;
+ return 0;
+}
+
+static int hdmi_set_mode(struct rk_display_device *device, struct fb_videomode *mode)
+{
+ struct hdmi *hdmi = device->priv_data;
+ int vic = hdmi_videomode_to_vic(mode);
+
+ if(!hdmi->hotplug)
+ return -1;
+ hdmi->autoconfig = HDMI_DISABLE;
+ if(vic && hdmi->vic != vic)
+ {
+ hdmi->vic = vic;
+ hdmi->command = HDMI_CONFIG_VIDEO;
+ init_completion(&hdmi->complete);
+ hdmi->wait = 1;
+ queue_delayed_work(hdmi->workqueue, &hdmi->delay_work, 0);
+ wait_for_completion_interruptible_timeout(&hdmi->complete,
+ msecs_to_jiffies(10000));
+ }
+ return 0;
+}
+
+static int hdmi_get_mode(struct rk_display_device *device, struct fb_videomode *mode)
+{
+ struct hdmi *hdmi = device->priv_data;
+ struct fb_videomode *vmode;
+
+ if(!hdmi->hotplug)
+ return -1;
+
+ vmode = (struct fb_videomode*) hdmi_vic_to_videomode(hdmi->vic);
+ if(unlikely(vmode == NULL))
+ return -1;
+ *mode = *vmode;
+ return 0;
+}
+
+struct rk_display_ops hdmi_display_ops = {
+ .setenable = hdmi_set_enable,
+ .getenable = hdmi_get_enable,
+ .getstatus = hdmi_get_status,
+ .getmodelist = hdmi_get_modelist,
+ .setmode = hdmi_set_mode,
+ .getmode = hdmi_get_mode,
+};
+
+#if 1
+static int hdmi_display_probe(struct rk_display_device *device, void *devdata)
+{
+ device->owner = THIS_MODULE;
+ strcpy(device->type, "HDMI");
+ device->priority = DISPLAY_PRIORITY_HDMI;
+// device->name = kmalloc(strlen(name), GFP_KERNEL);
+// if(device->name)
+// {
+// strcpy(device->name, name);
+// }
+ device->priv_data = devdata;
+ device->ops = &hdmi_display_ops;
+ return 1;
+}
+
+static struct rk_display_driver display_hdmi = {
+ .probe = hdmi_display_probe,
+};
+
+static struct rk_display_device *display_device_hdmi = NULL;
+
+void hdmi_register_display_sysfs(struct hdmi *hdmi, struct device *parent)
+{
+ display_device_hdmi = rk_display_device_register(&display_hdmi, parent, hdmi);
+}
+
+void hdmi_unregister_display_sysfs(struct hdmi *hdmi)
+{
+ if(display_device_hdmi)
+ rk_display_device_unregister(display_device_hdmi);
+}
+#endif
\ No newline at end of file
static char *envp[] = {"INTERFACE=HDMI", NULL};
+static void hdmi_sys_show_state(int state)
+{
+ switch(state)
+ {
+ case HDMI_INITIAL:
+ dev_printk(KERN_INFO, hdmi->dev, "HDMI_INITIAL\n");
+ break;
+ case WAIT_HOTPLUG:
+ dev_printk(KERN_INFO, hdmi->dev, "WAIT_HOTPLUG\n");
+ break;
+ case READ_PARSE_EDID:
+ dev_printk(KERN_INFO, hdmi->dev, "READ_PARSE_EDID\n");
+ break;
+ case WAIT_HDMI_ENABLE:
+ dev_printk(KERN_INFO, hdmi->dev, "WAIT_HDMI_ENABLE\n");
+ break;
+ case SYSTEM_CONFIG:
+ dev_printk(KERN_INFO, hdmi->dev, "SYSTEM_CONFIG\n");
+ break;
+ case CONFIG_VIDEO:
+ dev_printk(KERN_INFO, hdmi->dev, "CONFIG_VIDEO\n");
+ break;
+ case CONFIG_AUDIO:
+ dev_printk(KERN_INFO, hdmi->dev, "CONFIG_AUDIO\n");
+ break;
+ case PLAY_BACK:
+ dev_printk(KERN_INFO, hdmi->dev, "PLAY_BACK\n");
+ break;
+ default:
+ dev_printk(KERN_INFO, hdmi->dev, "Unkown State\n");
+ break;
+ }
+}
+
int hdmi_sys_init(void)
{
hdmi->pwr_mode = PWR_SAVE_MODE_A;
}
if(state != state_last)
trytimes = 0;
+
+ hdmi_sys_show_state(state);
}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();
}
+ hdmi->state = state;
}
\ No newline at end of file