Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* Re: [PATCH 0/15] fbdev: sh_mipi_dsi: care un-explained register settings
From: Florian Tobias Schandinat @ 2011-11-11 15:37 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <874nydc3vz.wl%kuninori.morimoto.gx@renesas.com>

Hi Guennadi, Magnus,

On 11/09/2011 04:32 AM, kuninori.morimoto.gx@renesas.com wrote:
> Dear Florian, Paul
> 
> These are sh_mipi_dsi update patches.
> 
> Kuninori Morimoto (15):
>       fbdev: sh_mobile_lcdcfb: fixup LDHAJR :: HSYNPAJ needs mask
>       fbdev: sh_mipi_dsi: tidyup dsip_clk
>       fbdev: sh_mipi_dsi: typo fix of SH_MIPI_DSI_HBPBM
>       fbdev: sh_mipi_dsi: tidyup VMCTR2 parameter expression
>       fbdev: sh_mipi_dsi: add SH_MIPI_DSI_HFPBM flag
>       fbdev: sh_mipi_dsi: add SH_MIPI_DSI_BL2E flag
>       fbdev: sh_mipi_dsi: add lane control support
>       fbdev: sh_mipi_dsi: add sync_pulses/sync_events/burst mode
>       fbdev: sh_mipi_dsi: add VMLEN1/VMLEN2 calculation
>       fbdev: sh_mipi_dsi: add set_dot_clock() for each platform
>       fbdev: sh_mipi_dsi: add HSxxCLK support
>       fbdev: sh_mipi_dsi: sh_mipi has pdata instead of dev
>       fbdev: sh_mipi_dsi: fixup setup timing of sh_mipi_setup()
>       fbdev: sh_mipi_dsi: fixup setup timing of SYSCONF
>       fbdev: sh_mipi_dsi: fixup setup timing DSICTRL

can you have a look at these patches?
After a quick glance they look okay to me, but as this is all about hardware
(which I don't know) I'd be happier if someone more qualified could have a look
at it.


Thanks,

Florian Tobias Schandinat

> SH MIPI DSI has many registers, but some registers are not explained for detail in SH manual.
> This time, I asked it to Renesas MIPI guys.
> These patches include this result.
> 
> for example, if your board is using AP5R chip,
> HS4divCLK is needed for 1920x1080p 60Hz output.
> But you can NOT find its explain and calculation method on AP5R manual.
> 
> These are based on latest linus/master tree
> 
>>> Paul
> 
> Can you please check #2, #7, #8, #10, #11 pache which modify arch/arm/mach-shmobile/xxx
> 


^ permalink raw reply

* Re: [PATCH 09/18] MIPS: Alchemy: move au1200fb global functions to
From: Ralf Baechle @ 2011-11-09 14:52 UTC (permalink / raw)
  To: Manuel Lauss; +Cc: Linux-MIPS, linux-fbdev
In-Reply-To: <1320174224-27305-10-git-send-email-manuel.lauss@googlemail.com>

No (n)ack or comments received, so I've queued this for 3.3.  Thanks,

  Ralf

^ permalink raw reply

* [PATCH 15/15] fbdev: sh_mipi_dsi: fixup setup timing DSICTRL
From: Kuninori Morimoto @ 2011-11-09  4:36 UTC (permalink / raw)
  To: linux-fbdev

DSICTRL should be called after all mipi settings

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 drivers/video/sh_mipi_dsi.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 187e127..05151b8 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -265,8 +265,6 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	iowrite32(0x0fffffff, base + TATOVSET);
 	/* Peripheral reset timeout, default 0xffffffff */
 	iowrite32(0x0fffffff, base + PRTOVSET);
-	/* Enable timeout counters */
-	iowrite32(0x00000f00, base + DSICTRL);
 	/* Interrupts not used, disable all */
 	iowrite32(0, base + DSIINTE);
 	/* DSI-Tx bias on */
@@ -388,6 +386,9 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 			  pixfmt << 4);
 	sh_mipi_dcs(ch->chan, MIPI_DCS_SET_DISPLAY_ON);
 
+	/* Enable timeout counters */
+	iowrite32(0x00000f00, base + DSICTRL);
+
 	return 0;
 }
 
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 14/15] fbdev: sh_mipi_dsi: fixup setup timing of SYSCONF
From: Kuninori Morimoto @ 2011-11-09  4:35 UTC (permalink / raw)
  To: linux-fbdev

SYSCONF should be set after PHYCTRL

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 drivers/video/sh_mipi_dsi.c |   23 +++++++++++------------
 1 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index f2c3f94..187e127 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -246,18 +246,6 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	/* setup DSI link */
 
 	/*
-	 * Default = ULPS enable |
-	 *	Contention detection enabled |
-	 *	EoT packet transmission enable |
-	 *	CRC check enable |
-	 *	ECC check enable
-	 * additionally enable first two lanes
-	 */
-	bitmap_fill((unsigned long *)&tmp, pdata->lane);
-	tmp |= 0x00003700;
-	iowrite32(tmp, base + SYSCONF);
-
-	/*
 	 * T_wakeup = 0x7000
 	 * T_hs-trail = 3
 	 * T_hs-prepare = 3
@@ -287,6 +275,17 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	/* Deassert resets, power on */
 	iowrite32(0x03070001, base + PHYCTRL);
 
+	/*
+	 * Default = ULPS enable |
+	 *	Contention detection enabled |
+	 *	EoT packet transmission enable |
+	 *	CRC check enable |
+	 *	ECC check enable
+	 */
+	bitmap_fill((unsigned long *)&tmp, pdata->lane);
+	tmp |= 0x00003700;
+	iowrite32(tmp, base + SYSCONF);
+
 	/* setup l-bridge */
 
 	/*
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 13/15] fbdev: sh_mipi_dsi: fixup setup timing of sh_mipi_setup()
From: Kuninori Morimoto @ 2011-11-09  4:35 UTC (permalink / raw)
  To: linux-fbdev

sh_mipi_setup() should be called after setting of CPG

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 drivers/video/sh_mipi_dsi.c |   79 ++++++++++++++++++++++++-------------------
 1 files changed, 44 insertions(+), 35 deletions(-)

diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 7e46505..f2c3f94 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -125,28 +125,6 @@ static void sh_mipi_shutdown(struct platform_device *pdev)
 	sh_mipi_dsi_enable(mipi, false);
 }
 
-static void mipi_display_on(void *arg, struct fb_info *info)
-{
-	struct sh_mipi *mipi = arg;
-
-	pm_runtime_get_sync(&mipi->pdev->dev);
-	sh_mipi_dsi_enable(mipi, true);
-
-	if (mipi->next_display_on)
-		mipi->next_display_on(mipi->next_board_data, info);
-}
-
-static void mipi_display_off(void *arg)
-{
-	struct sh_mipi *mipi = arg;
-
-	if (mipi->next_display_off)
-		mipi->next_display_off(mipi->next_board_data);
-
-	sh_mipi_dsi_enable(mipi, false);
-	pm_runtime_put(&mipi->pdev->dev);
-}
-
 static int __init sh_mipi_setup(struct sh_mipi *mipi,
 				struct sh_mipi_dsi_info *pdata)
 {
@@ -414,6 +392,50 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	return 0;
 }
 
+static void mipi_display_on(void *arg, struct fb_info *info)
+{
+	struct sh_mipi *mipi = arg;
+	struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data;
+	int ret;
+
+	pm_runtime_get_sync(&mipi->pdev->dev);
+
+	ret = pdata->set_dot_clock(mipi->pdev, mipi->base, 1);
+	if (ret < 0)
+		goto mipi_display_on_fail1;
+
+	ret = sh_mipi_setup(mipi, pdata);
+	if (ret < 0)
+		goto mipi_display_on_fail2;
+
+	sh_mipi_dsi_enable(mipi, true);
+
+	if (mipi->next_display_on)
+		mipi->next_display_on(mipi->next_board_data, info);
+
+	return;
+
+mipi_display_on_fail1:
+	pm_runtime_put_sync(&mipi->pdev->dev);
+mipi_display_on_fail2:
+	pdata->set_dot_clock(mipi->pdev, mipi->base, 0);
+}
+
+static void mipi_display_off(void *arg)
+{
+	struct sh_mipi *mipi = arg;
+	struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data;
+
+	if (mipi->next_display_off)
+		mipi->next_display_off(mipi->next_board_data);
+
+	sh_mipi_dsi_enable(mipi, false);
+
+	pdata->set_dot_clock(mipi->pdev, mipi->base, 0);
+
+	pm_runtime_put_sync(&mipi->pdev->dev);
+}
+
 static int __init sh_mipi_probe(struct platform_device *pdev)
 {
 	struct sh_mipi *mipi;
@@ -498,14 +520,6 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
 	pm_runtime_enable(&pdev->dev);
 	pm_runtime_resume(&pdev->dev);
 
-	ret = sh_mipi_setup(mipi, pdata);
-	if (ret < 0)
-		goto emipisetup;
-
-	ret = pdata->set_dot_clock(pdev, mipi->base, 1);
-	if (ret < 0)
-		goto emipisetup;
-
 	mutex_unlock(&array_lock);
 	platform_set_drvdata(pdev, mipi);
 
@@ -522,10 +536,6 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
 
 	return 0;
 
-emipisetup:
-	mipi_dsi[idx] = NULL;
-	pm_runtime_disable(&pdev->dev);
-	clk_disable(mipi->dsit_clk);
 eclkton:
 esettrate:
 	clk_put(mipi->dsit_clk);
@@ -579,7 +589,6 @@ static int __exit sh_mipi_remove(struct platform_device *pdev)
 	pm_runtime_disable(&pdev->dev);
 	clk_disable(mipi->dsit_clk);
 	clk_put(mipi->dsit_clk);
-	pdata->set_dot_clock(pdev, mipi->base, 0);
 
 	iounmap(mipi->linkbase);
 	if (res2)
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 12/15] fbdev: sh_mipi_dsi: sh_mipi has pdata instead of dev
From: Kuninori Morimoto @ 2011-11-09  4:35 UTC (permalink / raw)
  To: linux-fbdev

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 drivers/video/sh_mipi_dsi.c |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index b8aea8c..7e46505 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -53,7 +53,7 @@ struct sh_mipi {
 	void __iomem	*base;
 	void __iomem	*linkbase;
 	struct clk	*dsit_clk;
-	struct device	*dev;
+	struct platform_device *pdev;
 
 	void	*next_board_data;
 	void	(*next_display_on)(void *board_data, struct fb_info *info);
@@ -129,7 +129,7 @@ static void mipi_display_on(void *arg, struct fb_info *info)
 {
 	struct sh_mipi *mipi = arg;
 
-	pm_runtime_get_sync(mipi->dev);
+	pm_runtime_get_sync(&mipi->pdev->dev);
 	sh_mipi_dsi_enable(mipi, true);
 
 	if (mipi->next_display_on)
@@ -144,7 +144,7 @@ static void mipi_display_off(void *arg)
 		mipi->next_display_off(mipi->next_board_data);
 
 	sh_mipi_dsi_enable(mipi, false);
-	pm_runtime_put(mipi->dev);
+	pm_runtime_put(&mipi->pdev->dev);
 }
 
 static int __init sh_mipi_setup(struct sh_mipi *mipi,
@@ -469,7 +469,7 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
 		goto emap2;
 	}
 
-	mipi->dev = &pdev->dev;
+	mipi->pdev = pdev;
 
 	mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk");
 	if (IS_ERR(mipi->dsit_clk)) {
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 11/15] fbdev: sh_mipi_dsi: add HSxxCLK support
From: Kuninori Morimoto @ 2011-11-09  4:35 UTC (permalink / raw)
  To: linux-fbdev

SH MIPI manual explains the calculation method of HBP/HFP.
it is based on HSbyteCLK settings.
SH73a0 chip can use HS6divCLK/HS4divCLK for it.
This patch has compatibility to SH7372 mipi

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 arch/arm/mach-shmobile/board-ag5evm.c |    5 +++--
 arch/arm/mach-shmobile/board-ap4evb.c |    3 ++-
 drivers/video/sh_mipi_dsi.c           |   14 ++++++++++----
 include/video/sh_mipi_dsi.h           |    4 ++++
 4 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/arch/arm/mach-shmobile/board-ag5evm.c b/arch/arm/mach-shmobile/board-ag5evm.c
index a0b7f8d..d109bed 100644
--- a/arch/arm/mach-shmobile/board-ag5evm.c
+++ b/arch/arm/mach-shmobile/board-ag5evm.c
@@ -357,8 +357,9 @@ static struct sh_mipi_dsi_info mipidsi0_info = {
 	.lane		= 2,
 	.vsynw_offset	= 20,
 	.clksrc		= 1,
-	.flags		= SH_MIPI_DSI_HSABM |
-			  SH_MIPI_DSI_SYNC_PULSES_MODE,
+	.flags		= SH_MIPI_DSI_HSABM		|
+			  SH_MIPI_DSI_SYNC_PULSES_MODE	|
+			  SH_MIPI_DSI_HSbyteCLK,
 	.set_dot_clock	= sh_mipi_set_dot_clock,
 };
 
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index 47f899b..e677c72 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -606,7 +606,8 @@ static struct sh_mipi_dsi_info mipidsi0_info = {
 	.lcd_chan	= &lcdc_info.ch[0],
 	.lane		= 2,
 	.vsynw_offset	= 17,
-	.flags		= SH_MIPI_DSI_SYNC_PULSES_MODE,
+	.flags		= SH_MIPI_DSI_SYNC_PULSES_MODE |
+			  SH_MIPI_DSI_HSbyteCLK,
 	.set_dot_clock	= sh_mipi_set_dot_clock,
 };
 
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 77743f4..b8aea8c 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -153,7 +153,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	void __iomem *base = mipi->base;
 	struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan;
 	u32 pctype, datatype, pixfmt, linelength, vmctr2;
-	u32 tmp, top, bottom, delay;
+	u32 tmp, top, bottom, delay, div;
 	bool yuv;
 	int bpp;
 
@@ -364,17 +364,23 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	bottom	= 0x00000001;
 	delay	= 0;
 
+	div = 1;	/* HSbyteCLK is calculation base
+			 * HS4divCLK = HSbyteCLK/2
+			 * HS6divCLK is not supported for now */
+	if (pdata->flags & SH_MIPI_DSI_HS4divCLK)
+		div = 2;
+
 	if (pdata->flags & SH_MIPI_DSI_HFPBM) {	/* HBPLEN */
 		top = ch->lcd_cfg[0].hsync_len + ch->lcd_cfg[0].left_margin;
-		top = ((pdata->lane * top) - 10) << 16;
+		top = ((pdata->lane * top / div) - 10) << 16;
 	}
 	if (pdata->flags & SH_MIPI_DSI_HBPBM) { /* HFPLEN */
 		bottom = ch->lcd_cfg[0].right_margin;
-		bottom = (pdata->lane * bottom) - 12;
+		bottom = (pdata->lane * bottom / div) - 12;
 	}
 
 	bpp = linelength / ch->lcd_cfg[0].xres; /* byte / pixel */
-	if (pdata->lane > bpp) {
+	if ((pdata->lane / div) > bpp) {
 		tmp = ch->lcd_cfg[0].xres / bpp; /* output cycle */
 		tmp = ch->lcd_cfg[0].xres - tmp; /* (input - output) cycle */
 		delay = (pdata->lane * tmp);
diff --git a/include/video/sh_mipi_dsi.h b/include/video/sh_mipi_dsi.h
index 310b883..434d56b 100644
--- a/include/video/sh_mipi_dsi.h
+++ b/include/video/sh_mipi_dsi.h
@@ -35,6 +35,10 @@ struct sh_mobile_lcdc_chan_cfg;
 #define SH_MIPI_DSI_HSEE	(1 << 5)
 #define SH_MIPI_DSI_HSAE	(1 << 6)
 
+#define SH_MIPI_DSI_HSbyteCLK	(1 << 24)
+#define SH_MIPI_DSI_HS6divCLK	(1 << 25)
+#define SH_MIPI_DSI_HS4divCLK	(1 << 26)
+
 #define SH_MIPI_DSI_SYNC_PULSES_MODE	(SH_MIPI_DSI_VSEE | \
 					 SH_MIPI_DSI_HSEE | \
 					 SH_MIPI_DSI_HSAE)
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 10/15] fbdev: sh_mipi_dsi: add set_dot_clock() for each platform
From: Kuninori Morimoto @ 2011-11-09  4:35 UTC (permalink / raw)
  To: linux-fbdev

Dot clock of SH MIPI are depends on each platform board.
This patch adds set_dot_clock() function for it.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 arch/arm/mach-shmobile/board-ag5evm.c |   36 ++++++++++++++++++++++---
 arch/arm/mach-shmobile/board-ap4evb.c |   25 ++++++++++++++++++
 drivers/video/sh_mipi_dsi.c           |   45 ++++++++-------------------------
 include/video/sh_mipi_dsi.h           |    3 ++
 4 files changed, 70 insertions(+), 39 deletions(-)

diff --git a/arch/arm/mach-shmobile/board-ag5evm.c b/arch/arm/mach-shmobile/board-ag5evm.c
index 0895671..a0b7f8d 100644
--- a/arch/arm/mach-shmobile/board-ag5evm.c
+++ b/arch/arm/mach-shmobile/board-ag5evm.c
@@ -321,6 +321,36 @@ static struct resource mipidsi0_resources[] = {
 	},
 };
 
+#define DSI0PHYCR	0xe615006c
+static int sh_mipi_set_dot_clock(struct platform_device *pdev,
+				 void __iomem *base,
+				 int enable)
+{
+	struct clk *pck;
+	int ret;
+
+	pck = clk_get(&pdev->dev, "dsip_clk");
+	if (IS_ERR(pck)) {
+		ret = PTR_ERR(pck);
+		goto sh_mipi_set_dot_clock_pck_err;
+	}
+
+	if (enable) {
+		clk_set_rate(pck, clk_round_rate(pck,  24000000));
+		__raw_writel(0x2a809010, DSI0PHYCR);
+		clk_enable(pck);
+	} else {
+		clk_disable(pck);
+	}
+
+	ret = 0;
+
+	clk_put(pck);
+
+sh_mipi_set_dot_clock_pck_err:
+	return ret;
+}
+
 static struct sh_mipi_dsi_info mipidsi0_info = {
 	.data_format	= MIPI_RGB888,
 	.lcd_chan	= &lcdc0_info.ch[0],
@@ -329,6 +359,7 @@ static struct sh_mipi_dsi_info mipidsi0_info = {
 	.clksrc		= 1,
 	.flags		= SH_MIPI_DSI_HSABM |
 			  SH_MIPI_DSI_SYNC_PULSES_MODE,
+	.set_dot_clock	= sh_mipi_set_dot_clock,
 };
 
 static struct platform_device mipidsi0_device = {
@@ -476,8 +507,6 @@ static void __init ag5evm_map_io(void)
 	shmobile_setup_console();
 }
 
-#define DSI0PHYCR	0xe615006c
-
 static void __init ag5evm_init(void)
 {
 	sh73a0_pinmux_init();
@@ -558,9 +587,6 @@ static void __init ag5evm_init(void)
 	gpio_direction_output(GPIO_PORT235, 0);
 	lcd_backlight_reset();
 
-	/* MIPI-DSI clock setup */
-	__raw_writel(0x2a809010, DSI0PHYCR);
-
 	/* enable SDHI0 on CN15 [SD I/F] */
 	gpio_request(GPIO_FN_SDHICD0, NULL);
 	gpio_request(GPIO_FN_SDHIWP0, NULL);
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index 9b39e86..47f899b 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -564,6 +564,30 @@ static struct platform_device keysc_device = {
 };
 
 /* MIPI-DSI */
+#define PHYCTRL		0x0070
+static int sh_mipi_set_dot_clock(struct platform_device *pdev,
+				 void __iomem *base,
+				 int enable)
+{
+	struct clk *pck = clk_get(&pdev->dev, "dsip_clk");
+	void __iomem *phy =  base + PHYCTRL;
+
+	if (IS_ERR(pck))
+		return PTR_ERR(pck);
+
+	if (enable) {
+		clk_set_rate(pck, clk_round_rate(pck, 24000000));
+		iowrite32(ioread32(phy) | (0xb << 8), phy);
+		clk_enable(pck);
+	} else {
+		clk_disable(pck);
+	}
+
+	clk_put(pck);
+
+	return 0;
+}
+
 static struct resource mipidsi0_resources[] = {
 	[0] = {
 		.start  = 0xffc60000,
@@ -583,6 +607,7 @@ static struct sh_mipi_dsi_info mipidsi0_info = {
 	.lane		= 2,
 	.vsynw_offset	= 17,
 	.flags		= SH_MIPI_DSI_SYNC_PULSES_MODE,
+	.set_dot_clock	= sh_mipi_set_dot_clock,
 };
 
 static struct platform_device mipidsi0_device = {
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 190e941..77743f4 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -53,7 +53,6 @@ struct sh_mipi {
 	void __iomem	*base;
 	void __iomem	*linkbase;
 	struct clk	*dsit_clk;
-	struct clk	*dsip_clk;
 	struct device	*dev;
 
 	void	*next_board_data;
@@ -307,8 +306,8 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	/* DSI-Tx bias on */
 	iowrite32(0x00000001, base + PHYCTRL);
 	udelay(200);
-	/* Deassert resets, power on, set multiplier */
-	iowrite32(0x03070b01, base + PHYCTRL);
+	/* Deassert resets, power on */
+	iowrite32(0x03070001, base + PHYCTRL);
 
 	/* setup l-bridge */
 
@@ -421,6 +420,9 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
 	if (!res || !res2 || idx >= ARRAY_SIZE(mipi_dsi) || !pdata)
 		return -ENODEV;
 
+	if (!pdata->set_dot_clock)
+		return -EINVAL;
+
 	mutex_lock(&array_lock);
 	if (idx < 0)
 		for (idx = 0; idx < ARRAY_SIZE(mipi_dsi) && mipi_dsi[idx]; idx++)
@@ -481,34 +483,10 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
 
 	dev_dbg(&pdev->dev, "DSI-T clk %lu -> %lu\n", f_current, rate);
 
-	mipi->dsip_clk = clk_get(&pdev->dev, "dsip_clk");
-	if (IS_ERR(mipi->dsip_clk)) {
-		ret = PTR_ERR(mipi->dsip_clk);
-		goto eclkpget;
-	}
-
-	f_current = clk_get_rate(mipi->dsip_clk);
-	/* Between 10 and 50MHz */
-	rate = clk_round_rate(mipi->dsip_clk, 24000000);
-	if (rate > 0 && rate != f_current)
-		ret = clk_set_rate(mipi->dsip_clk, rate);
-	else
-		ret = rate;
-	if (ret < 0)
-		goto esetprate;
-
-	dev_dbg(&pdev->dev, "DSI-P clk %lu -> %lu\n", f_current, rate);
-
-	msleep(10);
-
 	ret = clk_enable(mipi->dsit_clk);
 	if (ret < 0)
 		goto eclkton;
 
-	ret = clk_enable(mipi->dsip_clk);
-	if (ret < 0)
-		goto eclkpon;
-
 	mipi_dsi[idx] = mipi;
 
 	pm_runtime_enable(&pdev->dev);
@@ -518,6 +496,10 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
 	if (ret < 0)
 		goto emipisetup;
 
+	ret = pdata->set_dot_clock(pdev, mipi->base, 1);
+	if (ret < 0)
+		goto emipisetup;
+
 	mutex_unlock(&array_lock);
 	platform_set_drvdata(pdev, mipi);
 
@@ -537,13 +519,8 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
 emipisetup:
 	mipi_dsi[idx] = NULL;
 	pm_runtime_disable(&pdev->dev);
-	clk_disable(mipi->dsip_clk);
-eclkpon:
 	clk_disable(mipi->dsit_clk);
 eclkton:
-esetprate:
-	clk_put(mipi->dsip_clk);
-eclkpget:
 esettrate:
 	clk_put(mipi->dsit_clk);
 eclktget:
@@ -594,10 +571,10 @@ static int __exit sh_mipi_remove(struct platform_device *pdev)
 	pdata->lcd_chan->board_cfg.board_data = NULL;
 
 	pm_runtime_disable(&pdev->dev);
-	clk_disable(mipi->dsip_clk);
 	clk_disable(mipi->dsit_clk);
 	clk_put(mipi->dsit_clk);
-	clk_put(mipi->dsip_clk);
+	pdata->set_dot_clock(pdev, mipi->base, 0);
+
 	iounmap(mipi->linkbase);
 	if (res2)
 		release_mem_region(res2->start, resource_size(res2));
diff --git a/include/video/sh_mipi_dsi.h b/include/video/sh_mipi_dsi.h
index c8225b4..310b883 100644
--- a/include/video/sh_mipi_dsi.h
+++ b/include/video/sh_mipi_dsi.h
@@ -48,6 +48,9 @@ struct sh_mipi_dsi_info {
 	unsigned long			flags;
 	u32				clksrc;
 	unsigned int			vsynw_offset;
+	int	(*set_dot_clock)(struct platform_device *pdev,
+				 void __iomem *base,
+				 int enable);
 };
 
 #endif
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 09/15] fbdev: sh_mipi_dsi: add VMLEN1/VMLEN2 calculation
From: Kuninori Morimoto @ 2011-11-09  4:35 UTC (permalink / raw)
  To: linux-fbdev

VMLEN1/VMLEN2 needs blanking length which is calculated from
video image size.

The calculation methods are explained on
[SH MIPI] - [Video Mode] - [Blanking Packet setting]
But HFPLEN (= VMLEN2) is un-understandable.

For example, if SH-MIPI input was RGB888 (3byte),
output was RGB888 (3byte)
and 3lane connection, the date goes straight.

But if SH-MIPI input was RGB888 (3byte),
output was RGB565 (2byte)
and 4lane connection, it needs delay in HFPLEN.
Then (input cycle - output cycle) * lane is necessary the delay

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 drivers/video/sh_mipi_dsi.c |   45 ++++++++++++++++++++++++++++++++++++++----
 1 files changed, 40 insertions(+), 5 deletions(-)

diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index b8c4873..190e941 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -42,6 +42,7 @@
 #define VMCTR1		0x0020
 #define VMCTR2		0x0024
 #define VMLEN1		0x0028
+#define VMLEN2		0x002c
 #define CMTSRTREQ	0x0070
 #define CMTSRTCTR	0x00d0
 
@@ -153,8 +154,9 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	void __iomem *base = mipi->base;
 	struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan;
 	u32 pctype, datatype, pixfmt, linelength, vmctr2;
+	u32 tmp, top, bottom, delay;
 	bool yuv;
-	u32 tmp;
+	int bpp;
 
 	/*
 	 * Select data format. MIPI DSI is not hot-pluggable, so, we just use
@@ -342,11 +344,44 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	iowrite32(vmctr2, mipi->linkbase + VMCTR2);
 
 	/*
-	 * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see
-	 * sh_mobile_lcdc_info.ch[0].lcd_cfg[0].xres), HSALEN = 1 - default
-	 * (unused if VMCTR2[HSABM] = 0)
+	 * VMLEN1 = RGBLEN | HSALEN
+	 *
+	 * see
+	 *  Video mode - Blanking Packet setting
 	 */
-	iowrite32(1 | (linelength << 16), mipi->linkbase + VMLEN1);
+	top = linelength << 16; /* RGBLEN */
+	bottom = 0x00000001;
+	if (pdata->flags & SH_MIPI_DSI_HSABM) /* HSALEN */
+		bottom = (pdata->lane * ch->lcd_cfg[0].hsync_len) - 10;
+	iowrite32(top | bottom , mipi->linkbase + VMLEN1);
+
+	/*
+	 * VMLEN2 = HBPLEN | HFPLEN
+	 *
+	 * see
+	 *  Video mode - Blanking Packet setting
+	 */
+	top	= 0x00010000;
+	bottom	= 0x00000001;
+	delay	= 0;
+
+	if (pdata->flags & SH_MIPI_DSI_HFPBM) {	/* HBPLEN */
+		top = ch->lcd_cfg[0].hsync_len + ch->lcd_cfg[0].left_margin;
+		top = ((pdata->lane * top) - 10) << 16;
+	}
+	if (pdata->flags & SH_MIPI_DSI_HBPBM) { /* HFPLEN */
+		bottom = ch->lcd_cfg[0].right_margin;
+		bottom = (pdata->lane * bottom) - 12;
+	}
+
+	bpp = linelength / ch->lcd_cfg[0].xres; /* byte / pixel */
+	if (pdata->lane > bpp) {
+		tmp = ch->lcd_cfg[0].xres / bpp; /* output cycle */
+		tmp = ch->lcd_cfg[0].xres - tmp; /* (input - output) cycle */
+		delay = (pdata->lane * tmp);
+	}
+
+	iowrite32(top | (bottom + delay) , mipi->linkbase + VMLEN2);
 
 	msleep(5);
 
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 08/15] fbdev: sh_mipi_dsi: add sync_pulses/sync_events/burst mode
From: Kuninori Morimoto @ 2011-11-09  4:34 UTC (permalink / raw)
  To: linux-fbdev

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 arch/arm/mach-shmobile/board-ag5evm.c |    3 ++-
 arch/arm/mach-shmobile/board-ap4evb.c |    1 +
 drivers/video/sh_mipi_dsi.c           |    9 ++++++++-
 include/video/sh_mipi_dsi.h           |    9 +++++++++
 4 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-shmobile/board-ag5evm.c b/arch/arm/mach-shmobile/board-ag5evm.c
index dd3881a..0895671 100644
--- a/arch/arm/mach-shmobile/board-ag5evm.c
+++ b/arch/arm/mach-shmobile/board-ag5evm.c
@@ -327,7 +327,8 @@ static struct sh_mipi_dsi_info mipidsi0_info = {
 	.lane		= 2,
 	.vsynw_offset	= 20,
 	.clksrc		= 1,
-	.flags		= SH_MIPI_DSI_HSABM,
+	.flags		= SH_MIPI_DSI_HSABM |
+			  SH_MIPI_DSI_SYNC_PULSES_MODE,
 };
 
 static struct platform_device mipidsi0_device = {
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index 187d6fc..9b39e86 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -582,6 +582,7 @@ static struct sh_mipi_dsi_info mipidsi0_info = {
 	.lcd_chan	= &lcdc_info.ch[0],
 	.lane		= 2,
 	.vsynw_offset	= 17,
+	.flags		= SH_MIPI_DSI_SYNC_PULSES_MODE,
 };
 
 static struct platform_device mipidsi0_device = {
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index af6bec2..b8c4873 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -152,7 +152,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 {
 	void __iomem *base = mipi->base;
 	struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan;
-	u32 pctype, datatype, pixfmt, linelength, vmctr2 = 0x00e00000;
+	u32 pctype, datatype, pixfmt, linelength, vmctr2;
 	bool yuv;
 	u32 tmp;
 
@@ -324,6 +324,13 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	 * Non-burst mode with sync pulses: VSE and HSE are output,
 	 * HSA period allowed, no commands in LP
 	 */
+	vmctr2 = 0;
+	if (pdata->flags & SH_MIPI_DSI_VSEE)
+		vmctr2 |= 1 << 23;
+	if (pdata->flags & SH_MIPI_DSI_HSEE)
+		vmctr2 |= 1 << 22;
+	if (pdata->flags & SH_MIPI_DSI_HSAE)
+		vmctr2 |= 1 << 21;
 	if (pdata->flags & SH_MIPI_DSI_BL2E)
 		vmctr2 |= 1 << 17;
 	if (pdata->flags & SH_MIPI_DSI_HSABM)
diff --git a/include/video/sh_mipi_dsi.h b/include/video/sh_mipi_dsi.h
index 3d0ec50..c8225b4 100644
--- a/include/video/sh_mipi_dsi.h
+++ b/include/video/sh_mipi_dsi.h
@@ -31,6 +31,15 @@ struct sh_mobile_lcdc_chan_cfg;
 #define SH_MIPI_DSI_HBPBM	(1 << 1)
 #define SH_MIPI_DSI_HFPBM	(1 << 2)
 #define SH_MIPI_DSI_BL2E	(1 << 3)
+#define SH_MIPI_DSI_VSEE	(1 << 4)
+#define SH_MIPI_DSI_HSEE	(1 << 5)
+#define SH_MIPI_DSI_HSAE	(1 << 6)
+
+#define SH_MIPI_DSI_SYNC_PULSES_MODE	(SH_MIPI_DSI_VSEE | \
+					 SH_MIPI_DSI_HSEE | \
+					 SH_MIPI_DSI_HSAE)
+#define SH_MIPI_DSI_SYNC_EVENTS_MODE	(0)
+#define SH_MIPI_DSI_SYNC_BURST_MODE	(SH_MIPI_DSI_BL2E)
 
 struct sh_mipi_dsi_info {
 	enum sh_mipi_dsi_data_fmt	data_format;
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 07/15] fbdev: sh_mipi_dsi: add lane control support
From: Kuninori Morimoto @ 2011-11-09  4:34 UTC (permalink / raw)
  To: linux-fbdev

SH MIPI DSI can use 0-4 lane

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 arch/arm/mach-shmobile/board-ag5evm.c |    1 +
 arch/arm/mach-shmobile/board-ap4evb.c |    1 +
 drivers/video/sh_mipi_dsi.c           |   10 +++++++++-
 include/video/sh_mipi_dsi.h           |    1 +
 4 files changed, 12 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-shmobile/board-ag5evm.c b/arch/arm/mach-shmobile/board-ag5evm.c
index 83624e2..dd3881a 100644
--- a/arch/arm/mach-shmobile/board-ag5evm.c
+++ b/arch/arm/mach-shmobile/board-ag5evm.c
@@ -324,6 +324,7 @@ static struct resource mipidsi0_resources[] = {
 static struct sh_mipi_dsi_info mipidsi0_info = {
 	.data_format	= MIPI_RGB888,
 	.lcd_chan	= &lcdc0_info.ch[0],
+	.lane		= 2,
 	.vsynw_offset	= 20,
 	.clksrc		= 1,
 	.flags		= SH_MIPI_DSI_HSABM,
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index a3aa0f6..187d6fc 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -580,6 +580,7 @@ static struct resource mipidsi0_resources[] = {
 static struct sh_mipi_dsi_info mipidsi0_info = {
 	.data_format	= MIPI_RGB888,
 	.lcd_chan	= &lcdc_info.ch[0],
+	.lane		= 2,
 	.vsynw_offset	= 17,
 };
 
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index dfd5154..af6bec2 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -8,6 +8,7 @@
  * published by the Free Software Foundation.
  */
 
+#include <linux/bitmap.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/init.h>
@@ -153,6 +154,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan;
 	u32 pctype, datatype, pixfmt, linelength, vmctr2 = 0x00e00000;
 	bool yuv;
+	u32 tmp;
 
 	/*
 	 * Select data format. MIPI DSI is not hot-pluggable, so, we just use
@@ -253,6 +255,9 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	    (!yuv && ch->interface_type != RGB24))
 		return -EINVAL;
 
+	if (!pdata->lane)
+		return -EINVAL;
+
 	/* reset DSI link */
 	iowrite32(0x00000001, base + SYSCTRL);
 	/* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */
@@ -269,7 +274,10 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	 *	ECC check enable
 	 * additionally enable first two lanes
 	 */
-	iowrite32(0x00003703, base + SYSCONF);
+	bitmap_fill((unsigned long *)&tmp, pdata->lane);
+	tmp |= 0x00003700;
+	iowrite32(tmp, base + SYSCONF);
+
 	/*
 	 * T_wakeup = 0x7000
 	 * T_hs-trail = 3
diff --git a/include/video/sh_mipi_dsi.h b/include/video/sh_mipi_dsi.h
index 58b78f8..3d0ec50 100644
--- a/include/video/sh_mipi_dsi.h
+++ b/include/video/sh_mipi_dsi.h
@@ -35,6 +35,7 @@ struct sh_mobile_lcdc_chan_cfg;
 struct sh_mipi_dsi_info {
 	enum sh_mipi_dsi_data_fmt	data_format;
 	struct sh_mobile_lcdc_chan_cfg	*lcd_chan;
+	int				lane;
 	unsigned long			flags;
 	u32				clksrc;
 	unsigned int			vsynw_offset;
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 06/15] fbdev: sh_mipi_dsi: add SH_MIPI_DSI_BL2E flag
From: Kuninori Morimoto @ 2011-11-09  4:34 UTC (permalink / raw)
  To: linux-fbdev

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 drivers/video/sh_mipi_dsi.c |    2 ++
 include/video/sh_mipi_dsi.h |    1 +
 2 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index c9fac13..dfd5154 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -316,6 +316,8 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	 * Non-burst mode with sync pulses: VSE and HSE are output,
 	 * HSA period allowed, no commands in LP
 	 */
+	if (pdata->flags & SH_MIPI_DSI_BL2E)
+		vmctr2 |= 1 << 17;
 	if (pdata->flags & SH_MIPI_DSI_HSABM)
 		vmctr2 |= 1 << 5;
 	if (pdata->flags & SH_MIPI_DSI_HBPBM)
diff --git a/include/video/sh_mipi_dsi.h b/include/video/sh_mipi_dsi.h
index 86a72c0..58b78f8 100644
--- a/include/video/sh_mipi_dsi.h
+++ b/include/video/sh_mipi_dsi.h
@@ -30,6 +30,7 @@ struct sh_mobile_lcdc_chan_cfg;
 #define SH_MIPI_DSI_HSABM	(1 << 0)
 #define SH_MIPI_DSI_HBPBM	(1 << 1)
 #define SH_MIPI_DSI_HFPBM	(1 << 2)
+#define SH_MIPI_DSI_BL2E	(1 << 3)
 
 struct sh_mipi_dsi_info {
 	enum sh_mipi_dsi_data_fmt	data_format;
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 05/15] fbdev: sh_mipi_dsi: add SH_MIPI_DSI_HFPBM flag
From: Kuninori Morimoto @ 2011-11-09  4:34 UTC (permalink / raw)
  To: linux-fbdev

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 drivers/video/sh_mipi_dsi.c |    2 ++
 include/video/sh_mipi_dsi.h |    1 +
 2 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 20ccc23..c9fac13 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -320,6 +320,8 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 		vmctr2 |= 1 << 5;
 	if (pdata->flags & SH_MIPI_DSI_HBPBM)
 		vmctr2 |= 1 << 4;
+	if (pdata->flags & SH_MIPI_DSI_HFPBM)
+		vmctr2 |= 1 << 3;
 	iowrite32(vmctr2, mipi->linkbase + VMCTR2);
 
 	/*
diff --git a/include/video/sh_mipi_dsi.h b/include/video/sh_mipi_dsi.h
index 4e2bcb5..86a72c0 100644
--- a/include/video/sh_mipi_dsi.h
+++ b/include/video/sh_mipi_dsi.h
@@ -29,6 +29,7 @@ struct sh_mobile_lcdc_chan_cfg;
 
 #define SH_MIPI_DSI_HSABM	(1 << 0)
 #define SH_MIPI_DSI_HBPBM	(1 << 1)
+#define SH_MIPI_DSI_HFPBM	(1 << 2)
 
 struct sh_mipi_dsi_info {
 	enum sh_mipi_dsi_data_fmt	data_format;
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 04/15] fbdev: sh_mipi_dsi: tidyup VMCTR2 parameter expression
From: Kuninori Morimoto @ 2011-11-09  4:34 UTC (permalink / raw)
  To: linux-fbdev

VMCTR2 parameter will be supported more in the future.
1 << xx style is easy to understand.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 drivers/video/sh_mipi_dsi.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 94bb1bb..20ccc23 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -317,9 +317,9 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	 * HSA period allowed, no commands in LP
 	 */
 	if (pdata->flags & SH_MIPI_DSI_HSABM)
-		vmctr2 |= 0x20;
+		vmctr2 |= 1 << 5;
 	if (pdata->flags & SH_MIPI_DSI_HBPBM)
-		vmctr2 |= 0x10;
+		vmctr2 |= 1 << 4;
 	iowrite32(vmctr2, mipi->linkbase + VMCTR2);
 
 	/*
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 03/15] fbdev: sh_mipi_dsi: typo fix of SH_MIPI_DSI_HBPBM
From: Kuninori Morimoto @ 2011-11-09  4:34 UTC (permalink / raw)
  To: linux-fbdev

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 drivers/video/sh_mipi_dsi.c |    2 +-
 include/video/sh_mipi_dsi.h |    2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 4aa5053..94bb1bb 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -318,7 +318,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
 	 */
 	if (pdata->flags & SH_MIPI_DSI_HSABM)
 		vmctr2 |= 0x20;
-	if (pdata->flags & SH_MIPI_DSI_HSPBM)
+	if (pdata->flags & SH_MIPI_DSI_HBPBM)
 		vmctr2 |= 0x10;
 	iowrite32(vmctr2, mipi->linkbase + VMCTR2);
 
diff --git a/include/video/sh_mipi_dsi.h b/include/video/sh_mipi_dsi.h
index 6cb95c9..4e2bcb5 100644
--- a/include/video/sh_mipi_dsi.h
+++ b/include/video/sh_mipi_dsi.h
@@ -28,7 +28,7 @@ enum sh_mipi_dsi_data_fmt {
 struct sh_mobile_lcdc_chan_cfg;
 
 #define SH_MIPI_DSI_HSABM	(1 << 0)
-#define SH_MIPI_DSI_HSPBM	(1 << 1)
+#define SH_MIPI_DSI_HBPBM	(1 << 1)
 
 struct sh_mipi_dsi_info {
 	enum sh_mipi_dsi_data_fmt	data_format;
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 02/15] fbdev: sh_mipi_dsi: tidyup dsip_clk
From: Kuninori Morimoto @ 2011-11-09  4:33 UTC (permalink / raw)
  To: linux-fbdev

dsipck clock is controled by CLKDEV_ICK_ID() in clock-shxxx.
dsi0p_clk/dsi1p_clk naming is not needed.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 arch/arm/mach-shmobile/clock-sh7372.c |    4 ++--
 arch/arm/mach-shmobile/clock-sh73a0.c |    4 ++--
 drivers/video/sh_mipi_dsi.c           |    4 +---
 3 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/arch/arm/mach-shmobile/clock-sh7372.c b/arch/arm/mach-shmobile/clock-sh7372.c
index 6697592..a736d94 100644
--- a/arch/arm/mach-shmobile/clock-sh7372.c
+++ b/arch/arm/mach-shmobile/clock-sh7372.c
@@ -612,8 +612,8 @@ static struct clk_lookup lookups[] = {
 	CLKDEV_CON_ID("hdmi_clk", &div6_reparent_clks[DIV6_HDMI]),
 	CLKDEV_ICK_ID("dsit_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSIT]),
 	CLKDEV_ICK_ID("dsit_clk", "sh-mipi-dsi.1", &div6_clks[DIV6_DSIT]),
-	CLKDEV_ICK_ID("dsi0p_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSI0P]),
-	CLKDEV_ICK_ID("dsi1p_clk", "sh-mipi-dsi.1", &div6_clks[DIV6_DSI1P]),
+	CLKDEV_ICK_ID("dsip_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSI0P]),
+	CLKDEV_ICK_ID("dsip_clk", "sh-mipi-dsi.1", &div6_clks[DIV6_DSI1P]),
 
 	/* MSTP32 clocks */
 	CLKDEV_DEV_ID("i2c-sh_mobile.2", &mstp_clks[MSTP001]), /* IIC2 */
diff --git a/arch/arm/mach-shmobile/clock-sh73a0.c b/arch/arm/mach-shmobile/clock-sh73a0.c
index 61a846b..3da30ba 100644
--- a/arch/arm/mach-shmobile/clock-sh73a0.c
+++ b/arch/arm/mach-shmobile/clock-sh73a0.c
@@ -319,8 +319,8 @@ static struct clk_lookup lookups[] = {
 	CLKDEV_CON_ID("sdhi2_clk", &div6_clks[DIV6_SDHI2]),
 	CLKDEV_ICK_ID("dsit_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSIT]),
 	CLKDEV_ICK_ID("dsit_clk", "sh-mipi-dsi.1", &div6_clks[DIV6_DSIT]),
-	CLKDEV_ICK_ID("dsi0p_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSI0P]),
-	CLKDEV_ICK_ID("dsi1p_clk", "sh-mipi-dsi.1", &div6_clks[DIV6_DSI1P]),
+	CLKDEV_ICK_ID("dsip_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSI0P]),
+	CLKDEV_ICK_ID("dsip_clk", "sh-mipi-dsi.1", &div6_clks[DIV6_DSI1P]),
 
 	/* MSTP32 clocks */
 	CLKDEV_DEV_ID("i2c-sh_mobile.2", &mstp_clks[MSTP001]), /* I2C2 */
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 72ee96b..4aa5053 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -363,7 +363,6 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
 	struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 	unsigned long rate, f_current;
 	int idx = pdev->id, ret;
-	char dsip_clk[] = "dsi.p_clk";
 
 	if (!res || !res2 || idx >= ARRAY_SIZE(mipi_dsi) || !pdata)
 		return -ENODEV;
@@ -428,8 +427,7 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
 
 	dev_dbg(&pdev->dev, "DSI-T clk %lu -> %lu\n", f_current, rate);
 
-	sprintf(dsip_clk, "dsi%1.1dp_clk", idx);
-	mipi->dsip_clk = clk_get(&pdev->dev, dsip_clk);
+	mipi->dsip_clk = clk_get(&pdev->dev, "dsip_clk");
 	if (IS_ERR(mipi->dsip_clk)) {
 		ret = PTR_ERR(mipi->dsip_clk);
 		goto eclkpget;
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 01/15] fbdev: sh_mobile_lcdcfb: fixup LDHAJR :: HSYNPAJ needs mask
From: Kuninori Morimoto @ 2011-11-09  4:33 UTC (permalink / raw)
  To: linux-fbdev

LDHAJR register will be broken by hsync_pos without this patch

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
 drivers/video/sh_mobile_lcdcfb.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index facffc2..1f49ab4 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -420,7 +420,7 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
 	tmp = ((display_var->xres & 7) << 24) |
 		((display_h_total & 7) << 16) |
 		((display_var->hsync_len & 7) << 8) |
-		hsync_pos;
+		(hsync_pos & 7);
 	lcdc_write_chan(ch, LDHAJR, tmp);
 }
 
-- 
1.7.5.4


^ permalink raw reply related

* [PATCH 0/15] fbdev: sh_mipi_dsi: care un-explained register settings
From: kuninori.morimoto.gx @ 2011-11-09  4:32 UTC (permalink / raw)
  To: linux-fbdev


Dear Florian, Paul

These are sh_mipi_dsi update patches.

Kuninori Morimoto (15):
      fbdev: sh_mobile_lcdcfb: fixup LDHAJR :: HSYNPAJ needs mask
      fbdev: sh_mipi_dsi: tidyup dsip_clk
      fbdev: sh_mipi_dsi: typo fix of SH_MIPI_DSI_HBPBM
      fbdev: sh_mipi_dsi: tidyup VMCTR2 parameter expression
      fbdev: sh_mipi_dsi: add SH_MIPI_DSI_HFPBM flag
      fbdev: sh_mipi_dsi: add SH_MIPI_DSI_BL2E flag
      fbdev: sh_mipi_dsi: add lane control support
      fbdev: sh_mipi_dsi: add sync_pulses/sync_events/burst mode
      fbdev: sh_mipi_dsi: add VMLEN1/VMLEN2 calculation
      fbdev: sh_mipi_dsi: add set_dot_clock() for each platform
      fbdev: sh_mipi_dsi: add HSxxCLK support
      fbdev: sh_mipi_dsi: sh_mipi has pdata instead of dev
      fbdev: sh_mipi_dsi: fixup setup timing of sh_mipi_setup()
      fbdev: sh_mipi_dsi: fixup setup timing of SYSCONF
      fbdev: sh_mipi_dsi: fixup setup timing DSICTRL

SH MIPI DSI has many registers, but some registers are not explained for detail in SH manual.
This time, I asked it to Renesas MIPI guys.
These patches include this result.

for example, if your board is using AP5R chip,
HS4divCLK is needed for 1920x1080p 60Hz output.
But you can NOT find its explain and calculation method on AP5R manual.

These are based on latest linus/master tree

>> Paul

Can you please check #2, #7, #8, #10, #11 pache which modify arch/arm/mach-shmobile/xxx

^ permalink raw reply

* [RFC] drivers/video: fsl-diu-fb: combine several allocated buffers into one
From: Timur Tabi @ 2011-11-08 23:04 UTC (permalink / raw)
  To: linux-fbdev

(Florian: this patch is the first of my changes for 3.3, but because it's
so extensive, I wanted to get your feedback before I continue with the rest
of my changes).

The DIU driver allocates several different objects separately, many of
which are accessed directly by the hardware and must be aligned on various
boundaries.  This results in multiple allocations and awkward data
structures used to keep track of all the pointers and physical addresses.

Instead, merge all of these objects into the fsl_diu_data structure, and
enforce the alignment within the structure.  Only one physical address
needs to be remember, and a macro is used to calculate the physical
address of any field.  Frame buffers are still allocated separately,
since their sizes vary with the resolutions.

Documentation for some affected data structures and variables is added.

The code assume that dma_alloc_coherent() will always return an aligned
memory block.  This function uses a page allocator on every architecture
that has a DIU, so this is safe.  The alternative is to allocate an extra
31 bytes that will never be used, and keep track of offsets that will
always be zero.  However, to be future-proof, we still validate the
alignement.

Signed-off-by: Timur Tabi <timur@freescale.com>
---
 drivers/video/fsl-diu-fb.c |  264 ++++++++++++++++++++------------------------
 1 files changed, 118 insertions(+), 146 deletions(-)

diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
index a16beeb..68f24a5 100644
--- a/drivers/video/fsl-diu-fb.c
+++ b/drivers/video/fsl-diu-fb.c
@@ -36,11 +36,9 @@
 #include <linux/fsl-diu-fb.h>
 #include "edid.h"

-#define FSL_AOI_NUM	6	/* 5 AOIs and one dummy AOI */
-				/* 1 for plane 0, 2 for plane 1&2 each */
+#define NUM_AOIS	5	/* 1 for plane 0, 2 for planes 1 & 2 each */

-/* HW cursor parameters */
-#define MAX_CURS		32
+#define MAX_CURS	32	/* HW cursor is 32 * 32 pixels, 16 bpp */

 /* INT_STATUS/INT_MASK field descriptions */
 #define INT_VSYNC	0x01	/* Vsync interrupt  */
@@ -49,12 +47,6 @@
 #define INT_PARERR	0x08	/* Display parameters error interrupt */
 #define INT_LS_BF_VS	0x10	/* Lines before vsync. interrupt */

-struct diu_addr {
-	void *vaddr;		/* Virtual address */
-	dma_addr_t paddr;	/* Physical address */
-	__u32 offset;
-};
-
 /*
  * List of supported video modes
  *
@@ -330,22 +322,43 @@ static unsigned int d_cache_line_size;

 static DEFINE_SPINLOCK(diu_lock);

+/**
+ * struct fsl_diu_data - per-DIU data structure
+ * @phys: physical address of this structure
+ * @fsl_diu_info: fb_info objects, one per AOI
+ * @dev_attr: sysfs structure
+ * @irq: IRQ
+ * @fb_enabled: TRUE if the DIU is enabled, FALSE if not
+ * @monitor_port: the monitor port this DIU is connected to
+ * @diu_reg: pointer to the DIU hardware registers
+ * @reg_lock: spinlock for register access
+ * @dummy_aoi: video buffer for the 4x4 32-bit dummy AOI
+ * dummy_ad: DIU Area Descriptor for the dummy AOI
+ * @ad[]: Area Descriptors for each real AOI
+ * @gamma: gamma color table
+ * @cursor: hardware cursor data
+ *
+ * This data structure must be allocated with 32-byte alignment, so that the
+ * internal fields can be aligned properly.
+ */
 struct fsl_diu_data {
-	struct fb_info *fsl_diu_info[FSL_AOI_NUM - 1];
-				/*FSL_AOI_NUM has one dummy AOI */
+	dma_addr_t phys;
+	struct fb_info *fsl_diu_info[NUM_AOIS];
 	struct device_attribute dev_attr;
-	struct diu_ad *dummy_ad;
-	void *dummy_aoi_virt;
 	unsigned int irq;
 	int fb_enabled;
 	enum fsl_diu_monitor_port monitor_port;
 	struct diu __iomem *diu_reg;
 	spinlock_t reg_lock;
-	struct diu_addr ad;
-	struct diu_addr gamma;
-	struct diu_addr pallete;
-	struct diu_addr cursor;
-};
+	u8 dummy_aoi[4 * 4 * 4];
+	struct diu_ad dummy_ad __aligned(8);
+	struct diu_ad ad[NUM_AOIS] __aligned(8);
+	u8 gamma[256 * 3] __aligned(32);
+	u8 cursor[MAX_CURS * MAX_CURS * 2] __aligned(32);
+} __aligned(32);
+
+/* Determine the physical address of a member of the fsl_diu_data structure */
+#define PHYS_ADDR(p, f) ((p)->phys + offsetof(struct fsl_diu_data, f))

 enum mfb_index {
 	PLANE0 = 0,	/* Plane 0, only one AOI that fills the screen */
@@ -355,6 +368,21 @@ enum mfb_index {
 	PLANE2_AOI1,	/* Plane 2, second AOI */
 };

+/**
+ * struct mfb_info - per-AOI data structure
+ * @index: the AOI index
+ * @id: the name of this AOI
+ * @registered: TRUE = this framebuffer has been registed with fbdev
+ * @pseudo_palette: the pseudo-palette
+ * @ad: pointer to the Area Descriptor for this AOI
+ * @cursor_reset: unusued
+ * @g_alpha: the global alpha value, can be set by ioctl
+ * @count: the number of times this framebuffer has been opened
+ * @x_aoi_d: X-offset of the AOI.  0 is the left edge of the physical screen
+ * @y_aoi_d: Y-offset of the AOI.  0 is the top edge of the physical screen
+ * @parent: pointer to fsl_diu_data structure
+ * @edid_data: EDID data
+ */
 struct mfb_info {
 	enum mfb_index index;
 	char *id;
@@ -364,8 +392,8 @@ struct mfb_info {
 	int cursor_reset;
 	unsigned char g_alpha;
 	unsigned int count;
-	int x_aoi_d;		/* aoi display x offset to physical screen */
-	int y_aoi_d;		/* aoi display y offset to physical screen */
+	int x_aoi_d;
+	int y_aoi_d;
 	struct fsl_diu_data *parent;
 	u8 *edid_data;
 };
@@ -528,7 +556,7 @@ static void fsl_diu_enable_panel(struct fb_info *info)
 	case PLANE1_AOI1:
 		pmfbi = machine_data->fsl_diu_info[1]->par;
 		ad->next_ad = 0;
-		if (hw->desc[1] = machine_data->dummy_ad->paddr)
+		if (hw->desc[1] = machine_data->dummy_ad.paddr)
 			wr_reg_wa(&hw->desc[1], ad->paddr);
 		else					/* AOI0 open */
 			pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
@@ -536,7 +564,7 @@ static void fsl_diu_enable_panel(struct fb_info *info)
 	case PLANE2_AOI1:
 		pmfbi = machine_data->fsl_diu_info[3]->par;
 		ad->next_ad = 0;
-		if (hw->desc[2] = machine_data->dummy_ad->paddr)
+		if (hw->desc[2] = machine_data->dummy_ad.paddr)
 			wr_reg_wa(&hw->desc[2], ad->paddr);
 		else				/* AOI0 was open */
 			pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
@@ -553,8 +581,8 @@ static void fsl_diu_disable_panel(struct fb_info *info)

 	switch (mfbi->index) {
 	case PLANE0:
-		if (hw->desc[0] != machine_data->dummy_ad->paddr)
-			wr_reg_wa(&hw->desc[0], machine_data->dummy_ad->paddr);
+		if (hw->desc[0] != machine_data->dummy_ad.paddr)
+			wr_reg_wa(&hw->desc[0], machine_data->dummy_ad.paddr);
 		break;
 	case PLANE1_AOI0:
 		cmfbi = machine_data->fsl_diu_info[2]->par;
@@ -562,7 +590,7 @@ static void fsl_diu_disable_panel(struct fb_info *info)
 			wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr);
 					/* move AOI1 to the first */
 		else			/* AOI1 was closed */
-			wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr);
+			wr_reg_wa(&hw->desc[1], machine_data->dummy_ad.paddr);
 					/* close AOI 0 */
 		break;
 	case PLANE2_AOI0:
@@ -571,7 +599,7 @@ static void fsl_diu_disable_panel(struct fb_info *info)
 			wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr);
 					/* move AOI1 to the first */
 		else			/* AOI1 was closed */
-			wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr);
+			wr_reg_wa(&hw->desc[2], machine_data->dummy_ad.paddr);
 					/* close AOI 0 */
 		break;
 	case PLANE1_AOI1:
@@ -582,7 +610,7 @@ static void fsl_diu_disable_panel(struct fb_info *info)
 					/* AOI0 is open, must be the first */
 				pmfbi->ad->next_ad = 0;
 		} else			/* AOI1 is the first in the chain */
-			wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr);
+			wr_reg_wa(&hw->desc[1], machine_data->dummy_ad.paddr);
 					/* close AOI 1 */
 		break;
 	case PLANE2_AOI1:
@@ -593,7 +621,7 @@ static void fsl_diu_disable_panel(struct fb_info *info)
 				/* AOI0 is open, must be the first */
 				pmfbi->ad->next_ad = 0;
 		} else		/* AOI1 is the first in the chain */
-			wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr);
+			wr_reg_wa(&hw->desc[2], machine_data->dummy_ad.paddr);
 				/* close AOI 1 */
 		break;
 	}
@@ -812,15 +840,15 @@ static void update_lcdc(struct fb_info *info)
 	struct fsl_diu_data *machine_data = mfbi->parent;
 	struct diu __iomem *hw;
 	int i, j;
-	char __iomem *cursor_base, *gamma_table_base;
+	u8 *gamma_table_base;

 	u32 temp;

 	hw = machine_data->diu_reg;

 	diu_ops.set_monitor_port(machine_data->monitor_port);
-	gamma_table_base = machine_data->gamma.vaddr;
-	cursor_base = machine_data->cursor.vaddr;
+	gamma_table_base = machine_data->gamma;
+
 	/* Prep for DIU init  - gamma table, cursor table */

 	for (i = 0; i <= 2; i++)
@@ -828,14 +856,14 @@ static void update_lcdc(struct fb_info *info)
 			*gamma_table_base++ = j;

 	diu_ops.set_gamma_table(machine_data->monitor_port,
-				machine_data->gamma.vaddr);
+		machine_data->gamma);

 	disable_lcdc(info);

 	/* Program DIU registers */

-	out_be32(&hw->gamma, machine_data->gamma.paddr);
-	out_be32(&hw->cursor, machine_data->cursor.paddr);
+	out_be32(&hw->gamma, PHYS_ADDR(machine_data, gamma));
+	out_be32(&hw->cursor, PHYS_ADDR(machine_data, cursor));

 	out_be32(&hw->bgnd, 0x007F7F7F); 	/* BGND */
 	out_be32(&hw->bgnd_wb, 0); 		/* BGND_WB */
@@ -1423,37 +1451,6 @@ static int fsl_diu_resume(struct platform_device *ofdev)
 #define fsl_diu_resume NULL
 #endif				/* CONFIG_PM */

-/* Align to 64-bit(8-byte), 32-byte, etc. */
-static int allocate_buf(struct device *dev, struct diu_addr *buf, u32 size,
-			u32 bytes_align)
-{
-	u32 offset;
-	dma_addr_t mask;
-
-	buf->vaddr -		dma_alloc_coherent(dev, size + bytes_align, &buf->paddr,
-				   GFP_DMA | __GFP_ZERO);
-	if (!buf->vaddr)
-		return -ENOMEM;
-
-	mask = bytes_align - 1;
-	offset = buf->paddr & mask;
-	if (offset) {
-		buf->offset = bytes_align - offset;
-		buf->paddr = buf->paddr + offset;
-	} else
-		buf->offset = 0;
-
-	return 0;
-}
-
-static void free_buf(struct device *dev, struct diu_addr *buf, u32 size,
-		     u32 bytes_align)
-{
-	dma_free_coherent(dev, size + bytes_align, buf->vaddr,
-			  buf->paddr - buf->offset);
-}
-
 static ssize_t store_monitor(struct device *device,
 	struct device_attribute *attr, const char *buf, size_t count)
 {
@@ -1499,28 +1496,54 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
 	struct mfb_info *mfbi;
-	phys_addr_t dummy_ad_addr = 0;
-	int ret, i, error = 0;
 	struct fsl_diu_data *machine_data;
 	int diu_mode;
+	dma_addr_t phys_addr;	/* physical addr of machine_data struct */
+	unsigned int i;
+	int ret;

-	machine_data = kzalloc(sizeof(struct fsl_diu_data), GFP_KERNEL);
+	machine_data = dma_alloc_coherent(&pdev->dev,
+		sizeof(struct fsl_diu_data), &phys_addr, GFP_DMA | __GFP_ZERO);
 	if (!machine_data)
 		return -ENOMEM;
+	machine_data->phys = phys_addr;
+
+	/*
+	 * dma_alloc_coherent() uses a page allocator, so the address is
+	 * always page-aligned.  We need the memory to be 32-byte aligned,
+	 * so that's good.  However, if one day the allocator changes, we
+	 * need to catch that.  It's not worth the effort to handle unaligned
+	 * alloctions now because it's highly unlikely to ever be a problem.
+	 */
+	if ((unsigned long)machine_data & 31) {
+		dev_err(&pdev->dev, "misaligned allocation");
+		ret = -ENOMEM;
+		goto error;
+	}

 	spin_lock_init(&machine_data->reg_lock);

-	for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) {
+	for (i = 0; i < NUM_AOIS; i++) {
 		machine_data->fsl_diu_info[i]  			framebuffer_alloc(sizeof(struct mfb_info), &pdev->dev);
 		if (!machine_data->fsl_diu_info[i]) {
 			dev_err(&pdev->dev, "cannot allocate memory\n");
 			ret = -ENOMEM;
-			goto error2;
+			goto error;
 		}
+		/*
+		 * We store the physical address of the AD in the reserved
+		 * 'paddr' field of the AD itself.
+		 */
+		machine_data->ad[i].paddr = PHYS_ADDR(machine_data, ad[i]);
+
+		machine_data->fsl_diu_info[i]->fix.smem_start = 0;
+
+		/* Initialize the AOI data structure */
 		mfbi = machine_data->fsl_diu_info[i]->par;
 		memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
 		mfbi->parent = machine_data;
+		mfbi->ad = &machine_data->ad[i];

 		if (mfbi->index = PLANE0) {
 			const u8 *prop;
@@ -1538,7 +1561,7 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
 	if (!machine_data->diu_reg) {
 		dev_err(&pdev->dev, "cannot map DIU registers\n");
 		ret = -EFAULT;
-		goto error2;
+		goto error;
 	}

 	diu_mode = in_be32(&machine_data->diu_reg->diu_mode);
@@ -1555,41 +1578,16 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
 	}
 	machine_data->monitor_port = monitor_port;

-	/* Area descriptor memory pool aligns to 64-bit boundary */
-	if (allocate_buf(&pdev->dev, &machine_data->ad,
-			 sizeof(struct diu_ad) * FSL_AOI_NUM, 8))
-		return -ENOMEM;
-
-	/* Get memory for Gamma Table  - 32-byte aligned memory */
-	if (allocate_buf(&pdev->dev, &machine_data->gamma, 768, 32)) {
-		ret = -ENOMEM;
-		goto error;
-	}
-
-	/* For performance, cursor bitmap buffer aligns to 32-byte boundary */
-	if (allocate_buf(&pdev->dev, &machine_data->cursor,
-			 MAX_CURS * MAX_CURS * 2, 32)) {
-		ret = -ENOMEM;
-		goto error;
-	}
-
-	i = ARRAY_SIZE(machine_data->fsl_diu_info);
-	machine_data->dummy_ad = (struct diu_ad *)((u32)machine_data->ad.vaddr +
-			machine_data->ad.offset) + i;
-	machine_data->dummy_ad->paddr = machine_data->ad.paddr +
-			i * sizeof(struct diu_ad);
-	machine_data->dummy_aoi_virt = fsl_diu_alloc(64, &dummy_ad_addr);
-	if (!machine_data->dummy_aoi_virt) {
-		ret = -ENOMEM;
-		goto error;
-	}
-	machine_data->dummy_ad->addr = cpu_to_le32(dummy_ad_addr);
-	machine_data->dummy_ad->pix_fmt = 0x88882317;
-	machine_data->dummy_ad->src_size_g_alpha = cpu_to_le32((4 << 12) | 4);
-	machine_data->dummy_ad->aoi_size = cpu_to_le32((4 << 16) |  2);
-	machine_data->dummy_ad->offset_xyi = 0;
-	machine_data->dummy_ad->offset_xyd = 0;
-	machine_data->dummy_ad->next_ad = 0;
+	/* Initialize the dummy Area Descriptor */
+	machine_data->dummy_ad.addr +		cpu_to_le32(PHYS_ADDR(machine_data, dummy_aoi));
+	machine_data->dummy_ad.pix_fmt = 0x88882317;
+	machine_data->dummy_ad.src_size_g_alpha = cpu_to_le32((4 << 12) | 4);
+	machine_data->dummy_ad.aoi_size = cpu_to_le32((4 << 16) |  2);
+	machine_data->dummy_ad.offset_xyi = 0;
+	machine_data->dummy_ad.offset_xyd = 0;
+	machine_data->dummy_ad.next_ad = 0;
+	machine_data->dummy_ad.paddr = PHYS_ADDR(machine_data, dummy_ad);

 	/*
 	 * Let DIU display splash screen if it was pre-initialized
@@ -1597,18 +1595,12 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
 	 */
 	if (diu_mode = MFB_MODE0)
 		out_be32(&machine_data->diu_reg->desc[0],
-			 machine_data->dummy_ad->paddr);
+			 machine_data->dummy_ad.paddr);

-	out_be32(&machine_data->diu_reg->desc[1], machine_data->dummy_ad->paddr);
-	out_be32(&machine_data->diu_reg->desc[2], machine_data->dummy_ad->paddr);
+	out_be32(&machine_data->diu_reg->desc[1], machine_data->dummy_ad.paddr);
+	out_be32(&machine_data->diu_reg->desc[2], machine_data->dummy_ad.paddr);

-	for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) {
-		machine_data->fsl_diu_info[i]->fix.smem_start = 0;
-		mfbi = machine_data->fsl_diu_info[i]->par;
-		mfbi->ad = (struct diu_ad *)((u32)machine_data->ad.vaddr
-					+ machine_data->ad.offset) + i;
-		mfbi->ad->paddr -			machine_data->ad.paddr + i * sizeof(struct diu_ad);
+	for (i = 0; i < NUM_AOIS; i++) {
 		ret = install_fb(machine_data->fsl_diu_info[i]);
 		if (ret) {
 			dev_err(&pdev->dev, "could not register fb %d\n", i);
@@ -1626,9 +1618,8 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
 	machine_data->dev_attr.attr.mode = S_IRUGO|S_IWUSR;
 	machine_data->dev_attr.show = show_monitor;
 	machine_data->dev_attr.store = store_monitor;
-	error = device_create_file(machine_data->fsl_diu_info[0]->dev,
-				  &machine_data->dev_attr);
-	if (error) {
+	ret = device_create_file(&pdev->dev, &machine_data->dev_attr);
+	if (ret) {
 		dev_err(&pdev->dev, "could not create sysfs file %s\n",
 			machine_data->dev_attr.attr.name);
 	}
@@ -1640,52 +1631,33 @@ error:
 	for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
 		uninstall_fb(machine_data->fsl_diu_info[i]);

-	if (machine_data->ad.vaddr)
-		free_buf(&pdev->dev, &machine_data->ad,
-			 sizeof(struct diu_ad) * FSL_AOI_NUM, 8);
-	if (machine_data->gamma.vaddr)
-		free_buf(&pdev->dev, &machine_data->gamma, 768, 32);
-	if (machine_data->cursor.vaddr)
-		free_buf(&pdev->dev, &machine_data->cursor,
-			 MAX_CURS * MAX_CURS * 2, 32);
-	if (machine_data->dummy_aoi_virt)
-		fsl_diu_free(machine_data->dummy_aoi_virt, 64);
 	iounmap(machine_data->diu_reg);

-error2:
 	for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
 		if (machine_data->fsl_diu_info[i])
 			framebuffer_release(machine_data->fsl_diu_info[i]);
-	kfree(machine_data);
+
+	dma_free_coherent(&pdev->dev, sizeof(struct fsl_diu_data),
+		machine_data, machine_data->phys);

 	return ret;
 }

 static int fsl_diu_remove(struct platform_device *pdev)
 {
-	struct fsl_diu_data *machine_data;
-	int i;
+	struct fsl_diu_data *machine_data = dev_get_drvdata(&pdev->dev);
+	unsigned int i;

-	machine_data = dev_get_drvdata(&pdev->dev);
 	disable_lcdc(machine_data->fsl_diu_info[0]);
 	free_irq_local(machine_data);
 	for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
 		uninstall_fb(machine_data->fsl_diu_info[i]);
-	if (machine_data->ad.vaddr)
-		free_buf(&pdev->dev, &machine_data->ad,
-			 sizeof(struct diu_ad) * FSL_AOI_NUM, 8);
-	if (machine_data->gamma.vaddr)
-		free_buf(&pdev->dev, &machine_data->gamma, 768, 32);
-	if (machine_data->cursor.vaddr)
-		free_buf(&pdev->dev, &machine_data->cursor,
-			 MAX_CURS * MAX_CURS * 2, 32);
-	if (machine_data->dummy_aoi_virt)
-		fsl_diu_free(machine_data->dummy_aoi_virt, 64);
 	iounmap(machine_data->diu_reg);
 	for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
 		if (machine_data->fsl_diu_info[i])
 			framebuffer_release(machine_data->fsl_diu_info[i]);
-	kfree(machine_data);
+	dma_free_coherent(&pdev->dev, sizeof(struct fsl_diu_data),
+		machine_data, machine_data->phys);

 	return 0;
 }
--
1.7.3.4



^ permalink raw reply related

* Re: [RFC] Virtual CRTCs (proposal + experimental code)
From: Ilija Hadzic @ 2011-11-07 13:52 UTC (permalink / raw)
  To: Dave Airlie; +Cc: linux-fbdev, dri-devel
In-Reply-To: <CAPM=9tyHDGHrRt1uOYR6ksxpxbss7Cbq9brFjpLy1S+kOk9AVw@mail.gmail.com>



On Mon, 7 Nov 2011, Dave Airlie wrote:

>
> So I expect the way I'd like this to work, is that we have full blown drm
> drivers for all the devices and then some sort of linkage layer between them,
> so one driver can export crtcs from another, instead of having special case
> ctd drivers.

I agree and that is actually a long-term plan from our side. CTD 
functionality should be integral part of existing drivers not the new 
driver, unless there is a new functionality that makes sense as CTD-only 
(like v4l2ctd).

In the world that I imagine, likage layer is VCRTCM. Unaccelerated 
frabebuffer devices (UDL for example, but in general anything that "lives" 
in fbdev world) can choose (based on some policy from userland) whether to 
act as CTD driver and register themselves with VCRTCM (when they want 
acceleration "assistance" from a GPU in the system) or to load as
normal fbdev devices (when they want to run on their own).


> Now I can see the reason for the v4l one, but I have for example
> a udl kms driver, and I'd like to have it work alongside this stuff,
> and userspace
> could bind the crtcs from it to another driver. I'm not sure how much work
> this would be or if its just a matter of adding a CTD interface to the udl kms
> device.
>

The only reason we wrote a new udlctd driver was  because it was quicker 
that way (we just ripped some code from udlfb driver and added our CTD 
functionality).

The plan was always to merge udlctd and udlfb at some point, but first 
I'd like to see how perceptive is the community to the concept. If the 
concept makes sense, then we'll throw in enough programming to consolidate 
the drivers. Nobody wants three competing drivers for the same device 
(udlfb, your udl kms driver and our udlctd).

Speaking of udl driver, is your udl-v2 branch competing with udlfb? 
Externally, they seem to do similar or the same thing, but one is based on 
DRM (and I guess the required DDX is xf86-video-modesetting), while the 
other is based on fbdev and the required DDX is xf8b-video-fbdev. While 
from my perspective both could be consilidated with a CTD functionality, 
but it makes me wonder in which direction is the community development 
moving: is everything from fbdev-world moving under DRM as a set of 
unaccelerated KMS drivers or is fbdev staying separate for good ?
Depending on what the trend is, one or the other udl driver (udl-v2 or 
udlfb) will make more sense to be merged with udlctd.

-- Ilija


^ permalink raw reply

* Re: [RFC] Virtual CRTCs (proposal + experimental code)
From: Dave Airlie @ 2011-11-07 12:58 UTC (permalink / raw)
  To: Ilija Hadzic; +Cc: linux-fbdev, dri-devel
In-Reply-To: <CA+4h6Hk3gE+8ou6NSMhoGJWZJ-5aM1OgC0HFoKs55fjfQ-qN8g@mail.gmail.com>

> In general any device that can do something good with rendered pixels
> can act as a CTD device, allowing a GPU to be an acceleration device
> for a less capable display device or (the opposite) a frame-buffer-based
> display device to be an expansion card for a GPU. Of course, for
> each display device, a driver would have to be made compatible with our
> new infrastructure (which is what we have done with DisplayLink driver
> and also wrote one "synthetic" driver to fake out a V4L2 device as a
> CTD device).

So I expect the way I'd like this to work, is that we have full blown drm
drivers for all the devices and then some sort of linkage layer between them,
so one driver can export crtcs from another, instead of having special case
ctd drivers. Now I can see the reason for the v4l one, but I have for example
a udl kms driver, and I'd like to have it work alongside this stuff,
and userspace
could bind the crtcs from it to another driver. I'm not sure how much work
this would be or if its just a matter of adding a CTD interface to the udl kms
device.

Dave

^ permalink raw reply

* RFC: abstract common HDMI code
From: Jun Nie @ 2011-11-07 10:20 UTC (permalink / raw)
  To: linux-fbdev

[-- Attachment #1: Type: text/plain, Size: 41733 bytes --]

Hi everyone,

    I am writing another HDMI driver for our new SOC and find that
sh_mobile_hdmi.c implemented some logic that can be reused by my
driver. I do not want to duplicated similar code and enlarge kernel
code size with it. So my plan is to abstract some common code to an
independent file to ease other developers. If you guys think it is
good to have a such common layer, I will put more effort on this and
contribute to the community. Also any help and co-work is warmly
welcomed!

    The attachment is my every preliminary code that only support
video resolution change and hot-plug on my platform with my private
code base. Sorry for the lot of style issues and small bugs for my
hurry debug. In my code, video resolution interface is still based on
original /sys/class/graphics/fb1/mode and more HDMI specific item is
added in /sys/class/hdmi/, such as video ratio. EDID part is not
ported yet and use predefined mode list in the first step. ASoC can be
added later just as sh_mobile_hdmi.c does, CEC and DHCP are the same.

    Thanks for any comments!

Jun


diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
new file mode 100644
index 0000000..c845dc7
--- /dev/null
+++ b/drivers/video/hdmi.c
@@ -0,0 +1,675 @@
+/*
+ * HDMI Control Abstraction
+ *
+ * This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/freezer.h>
+#include <linux/hdmi.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+#include <linux/lcd.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define HDMI_DEBUG
+static int hdmi_core_debug = 5;
+#define hdmi_dprintk(level, fmt, arg...)	if (hdmi_core_debug >= level) \
+	printk(KERN_DEBUG "%s: " fmt , __func__, ## arg)
+
+/* Define the max number of events to buffer */
+#define MAX_HDMI_EVENT_SIZE      16
+
+static int hdmidevno;
+
+/* ==================== info frame utility ===================== */
+
+int hdmi_vender_info_frame (struct hdmi_dev *hd, char buf[])
+{
+	buf[0] = 0x84;
+	buf[1] = 0x1;
+	buf[2] = 0xa;
+	buf[3] = 0x70;
+	buf[4] = 0x1;
+}
+EXPORT_SYMBOL_GPL(hdmi_vender_info_frame);
+
+int hdmi_avi_info_frame (struct hdmi_dev *hd, char buf[])
+{
+	int count;
+	char check_sum;
+
+	hdmi_dprintk(3, "pack avi infoframe\n");
+	/* packet header for AVI Packet in pkt1 */
+	/* Fix me. we should set bits according to format */
+	buf[0] = 0x82; /* InfoFrame Type, AVI frame */
+	buf[1] = 0x2;  /* Version = 02 */
+	buf[2] = 0xd;  /* Length = 13 */
+	buf[3] = 0x0;  /* checksum */
+	buf[4] = 0x0; /* Fix to RGB format currently */
+	/*
+	buf[4] = 0x10; // RGB, Active Format(r0,r1,r2,r3) valid, no bar, no scan
+	buf[4] = 0x30; // HDMI_YUV422, Active Format(r0,r1,r2,r3) valid, no
bar, no scan
+	buf[4] = 0x50; // HDMI_YUV444, Active Format(r0,r1,r2,r3) valid, no
bar, no scan
+	*/
+
+	buf[5] = 0xa8;  /* ITU-R 709, 16:9 */
+	buf[6] = 0x20;
+	/* buf[6] = 0x0;   // No IT content, xvYCC601, no scaling */
+	buf[7] = hd->mode_id;
+	/* limited YCC range, graphics, no repeat  Pixel repitition factor */
+	buf[8] = hd->pixel_rept;
+
+	buf[9] = 0x0;
+	buf[10] = 0x0;
+	buf[11] = 0x0;
+	buf[12] = 0x0;
+	buf[13] = 0x0;
+	check_sum = 0;
+    for (count = 0; count < 14; count++)
+	    check_sum += buf[count];
+    buf[3] = 0x100 - check_sum;
+    return 0;
+}
+EXPORT_SYMBOL_GPL(hdmi_avi_info_frame);
+
+int hdmi_get_freq (struct hdmi_dev *hd, u32 *need_freq)
+{
+	u64 div_result;
+	int freq;
+
+	/* Calc divider according to pclk in pico second */
+	div_result = 1000000000000ll;
+	do_div(div_result, cea_modes[hd->mode_id].pixclock);
+	freq = (u32)div_result;
+
+	hdmi_dprintk(3, "get base pclk %d\n", freq);
+
+	if (FB_VMODE_INTERLACED & cea_modes[hd->mode_id].vmode)
+		freq >>= 1;
+
+	if (36 == hd->fb_info->var.bits_per_pixel)
+		freq += freq >> 1;
+	else if (30 == hd->fb_info->var.bits_per_pixel)
+		freq += freq >> 2;
+
+	/* We only support repeat=2/4 currently. Add more later */
+	if (!(hd->pixel_rept == 0 || hd->pixel_rept == 1
+				|| hd->pixel_rept == 3)) {
+		hdmi_dprintk(3, "Not support pix repeat %d\n", hd->pixel_rept);
+		return -EINVAL;
+	}
+
+	if (1 == hd->pixel_rept)
+	{
+		if (!(FB_VMODE_INTERLACED & cea_modes[hd->mode_id].vmode))
+			freq <<= 1;
+	} else if (3 == hd->pixel_rept) {
+		if (!(FB_VMODE_INTERLACED & cea_modes[hd->mode_id].vmode))
+			freq <<= 2;
+		else
+			freq <<= 1;
+	}
+
+	hdmi_dprintk(3, "get hdmi pclk %d\n", freq);
+	*need_freq = freq;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hdmi_get_freq);
+
+/* ==================== video part ===================== */
+int hdmi_lcd_check_fb(struct lcd_device * ld, struct fb_info *fi)
+{
+	struct hdmi_dev *hd = lcd_get_data(ld);
+
+	hdmi_dprintk(3, "hdmi_lcd_check_fb..\n");
+	return 1;
+}
+
+/* We'd better update mode list in hot plug operation.
+   LCD framework does not implement get_mode yet */
+static int hdmi_lcd_get_mode(struct lcd_device *ld, struct fb_videomode *mode)
+{
+	struct hdmi_dev *hd = lcd_get_data(ld);
+
+	/* TODO: return parsed mode list */
+	return 0;
+}
+
+static int hdmi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *mode)
+{
+	//struct hdmi_lcd_data *data = lcd_get_data(lcd);
+	struct hdmi_dev *hd = lcd_get_data(ld);
+	cea_mode_id mode_id;
+
+	hdmi_dprintk(3, "hdmi_lcd_set_mode dev\n");
+	mode_id = fb_find_cea_mode_index(mode);
+	if (!mode_id) {
+		hdmi_dprintk(1, "unknown video mode %d\n", mode_id);
+		return -EINVAL;
+	}
+	/* If high level on detection signal, no connection */
+	if(! hd->ops->get_hpd(hd))
+		return 0;
+
+	hd->ops->set_mode(hd, mode_id);
+
+	if (hd->ops && hd->ops->set_enable)
+		hd->ops->set_enable(hd, 1);
+}
+
+static struct lcd_ops hdmi_lcd_ops = {
+	.get_mode = hdmi_lcd_get_mode,
+	.set_mode = hdmi_lcd_set_mode,
+	.check_fb = hdmi_lcd_check_fb,
+};
+
+static int hdmi_register_lcd(struct hdmi_dev *dev)
+{
+	int ret = 0;
+
+	hdmi_dprintk(3, "registering lcd device...\n");
+	/* FIX ME! How to hook expected fb if more than one fb exist? */
+	dev->lcd = lcd_device_register("hdmi-lcd", dev->dev, dev,
+			&hdmi_lcd_ops);
+
+	if (IS_ERR(dev->lcd)) {
+		ret = PTR_ERR(dev->lcd);
+		dev->lcd = NULL;
+		return ret;
+	}
+	hdmi_dprintk(3, "registering lcd device ok\n");
+	return 0;
+}
+
+/* ==================== audio part ===================== */
+/* ==================== cec part ===================== */
+/* ==================== edid part ===================== */
+
+/* ==================== hot plug part ===================== */
+static int hdmi_hotplug(struct hdmi_dev *hd)
+{
+	int ret;
+
+	msleep(100);
+	ret = hd->ops->get_hpd(hd);
+	if(!ret) {
+		hdmi_dprintk(3, "uplugged. dev %p\n", hd);
+		ret = hd->ops->set_enable(hd, 0);
+		lock_fb_info(hd->fb_info);
+		/* We should clear mode list here */
+		/* ... */
+		unlock_fb_info(hd->fb_info);
+		return 0;
+	}
+
+	ret = hd->ops->init(hd);
+	if (ret) {
+		hdmi_dprintk(1, "init fail\n");
+	}
+
+	if(hd->fb_info && hd->fb_info->mode) {
+		lock_fb_info(hd->fb_info);
+		/* Fix me! We should get EDID and parse mode list here */
+		/* But now we use fix mode list first before EDID util is ready */
+		/* ... */
+		unlock_fb_info(hd->fb_info);
+
+		ret = hdmi_lcd_set_mode(hd->lcd, hd->fb_info->mode);
+		if (ret) {
+			hdmi_dprintk(1, "fail to set video mode\n");
+		}
+	} else {
+		hdmi_dprintk(3, "no avaliable video mode\n");
+	}
+
+	if (hd->enable)
+		ret = hd->ops->set_enable(hd, 1);
+	return ret;
+}
+
+/* Handling hot plug HDCP and CEC */
+static int hdmi_event_thread(void *data)
+{
+	struct hdmi_dev *dev = (struct hdmi_dev *)data;
+	struct hdmi_event *evt;
+
+	while (!kthread_should_stop()) {
+
+		spin_lock_irq(&dev->lock);
+		if (list_empty(&dev->hdmi_evt_list)) {
+			set_current_state(TASK_INTERRUPTIBLE);
+
+			if (kthread_should_stop())
+				set_current_state(TASK_RUNNING);
+
+			spin_unlock_irq(&dev->lock);
+			schedule();
+			continue;
+		}
+
+		evt = list_entry(dev->hdmi_evt_list.next, typeof(*evt), list);
+		list_del(&evt->list);
+		spin_unlock_irq(&dev->lock);
+		hdmi_dprintk(1, "Receive evt type %d\n", evt->type);
+
+		mutex_lock(&dev->hdmi_lock);
+		switch (evt->type) {
+			/* trigger gpio irq */
+			case HDMI_EV_PLUG:
+				hdmi_hotplug(dev);
+				break;
+			/* trigger from TV/CEC */
+			case HDMI_EV_CEC:
+				break;
+			default:
+				hdmi_dprintk(1, "Invalid event type %d\n", evt->type);
+				break;
+		}
+		mutex_unlock(&dev->hdmi_lock);
+	}
+
+	return 0;
+}
+
+/**
+ * hdmi_event_signal() - schedules the hdmi event thread
+ * @dev:	the struct hdmi_dev device
+ *
+ * This routine will tell hdmi-core to start decoding stored ir data.
+ */
+void hdmi_event_signal(struct hdmi_dev *dev, enum hdmi_event_type type)
+{
+	unsigned long flags;
+	struct hdmi_event *evt;
+
+	hdmi_dprintk(1, "hdmi event %d stored, dev %p\n", type, dev);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if(HDMI_EV_PLUG == type) {
+		list_for_each_entry(evt, &dev->hdmi_evt_list, list) {
+			if (HDMI_EV_PLUG == evt->type) {
+				hdmi_dprintk(1, "pending hotplug event, ignore new ones\n");
+				spin_unlock_irqrestore(&dev->lock, flags);
+				return;
+			}
+		}
+	}
+	evt = list_entry(dev->free_evt_list.next, struct hdmi_event, list);
+	evt->type = type;
+	list_move_tail(&evt->list, &dev->hdmi_evt_list);
+	wake_up_process(dev->thread);
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+EXPORT_SYMBOL_GPL(hdmi_event_signal);
+
+/* ==================== sys fs part ===================== */
+#ifdef HDMI_DEBUG
+static ssize_t hdmi_show_reg(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	int rc;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->get_reg)
+		rc = sprintf(buf, "%d\n", hd->ops->get_reg(hd));
+	else
+		rc = -ENXIO;
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_store_reg(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc = -ENXIO;
+	char *endp;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+	int power = simple_strtoul(buf, &endp, 0);
+	size_t size = endp - buf;
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->set_reg) {
+		hd->ops->set_reg(hd, power, power);
+		rc = count;
+	}
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_show_reg_addr(struct device *dev, struct
device_attribute *attr,
+		char *buf)
+{
+	int rc;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->get_reg)
+		rc = sprintf(buf, "%d\n", hd->ops->get_reg(hd));
+	else
+		rc = -ENXIO;
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_store_reg_addr(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc = -ENXIO;
+	char *endp;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+	int power = simple_strtoul(buf, &endp, 0);
+	size_t size = endp - buf;
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->set_reg) {
+		hd->ops->set_reg(hd, power, power);
+		rc = count;
+	}
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+#endif
+
+static ssize_t hdmi_show_ratio(struct device *dev, struct
device_attribute *attr,
+		char *buf)
+{
+	int rc;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ratio == RATIO_16V9)
+		rc = sprintf(buf, "%s\n", RATIO_16V9_STR);
+	else if (hd->ratio == RATIO_4V3)
+		rc = sprintf(buf, "%s\n", RATIO_4V3_STR);
+	else
+		rc = -ENXIO;
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_store_ratio(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc = -ENXIO;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+	mutex_lock(&hd->update_lock);
+	if (sysfs_streq(buf, RATIO_16V9_STR)) {
+		hd->ratio = RATIO_16V9;
+	} else if (sysfs_streq(buf, RATIO_4V3_STR)) {
+		hd->ratio = RATIO_4V3;
+	}
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_show_power(struct device *dev, struct
device_attribute *attr,
+		char *buf)
+{
+	int rc;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->get_power) {
+		hd->ops->get_power(hd);
+		rc = sprintf(buf, "%d\n", hd->ops->get_power(hd));
+	}
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_store_power(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc = -ENXIO;
+	char *endp;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+	int power = simple_strtoul(buf, &endp, 0);
+	size_t size = endp - buf;
+
+	if (isspace(*endp))
+		size++;
+	if (size != count)
+		return -EINVAL;
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->set_power) {
+		hdmi_dprintk(3, "hdmi: set power to %d\n", power);
+		hd->ops->set_power(hd, power);
+		rc = count;
+	}
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_show_enable(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int rc = -ENXIO;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->get_enable)
+		rc = sprintf(buf, "%d\n", hd->ops->get_enable(hd));
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_store_enable(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc = -ENXIO;
+	char *endp;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+	int enable = simple_strtoul(buf, &endp, 0);
+	size_t size = endp - buf;
+
+	if (isspace(*endp))
+		size++;
+	if (size != count)
+		return -EINVAL;
+
+	mutex_lock(&hd->update_lock);
+	hd->enable = enable;
+	if (hd->ops && hd->ops->set_enable) {
+		hdmi_dprintk(3, "hdmi: set enable to %d\n", enable);
+		hd->ops->set_enable(hd, enable);
+		rc = count;
+	}
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_show_hotplug(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc = -ENXIO;
+	char *endp;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->get_hpd) {
+		rc = sprintf(buf, "%d\n", ! hd->ops->get_hpd(hd));
+	}
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static struct class *hdmi_class;
+
+static void hdmi_dev_release(struct device *dev)
+{
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+	kfree(hd);
+}
+
+static struct device_attribute hdmi_dev_attributes[] = {
+	__ATTR(power, 0644, hdmi_show_power, hdmi_store_power),
+	__ATTR(ratio, 0644, hdmi_show_ratio, hdmi_store_ratio),
+#ifdef HDMI_DEBUG
+	__ATTR(reg_val, 0644, hdmi_show_reg, hdmi_store_reg),
+	__ATTR(reg_addr, 0644, hdmi_show_reg_addr, hdmi_store_reg_addr),
+#endif
+	__ATTR(enable, 0644, hdmi_show_enable, hdmi_store_enable),
+	__ATTR(hotplug, 0644, hdmi_show_hotplug, NULL),
+	__ATTR_NULL,
+};
+
+/**
+ * hdmi_dev_register - register a new object of hdmi_dev class.
+ * @name: the name of the new object(must be the same as the name of the
+ *   respective framebuffer device).
+ * @devdata: an optional pointer to be stored in the device. The
+ *   methods may retrieve it by using hdmi_get_data(hd).
+ * @ops: the hdmi operations structure.
+ *
+ * Creates and registers a new hdmi device. Returns either an ERR_PTR()
+ * or a pointer to the newly allocated device.
+ */
+int hdmi_dev_register(struct hdmi_dev *hd, struct device *parent,
+		void *devdata, struct hdmi_ops *ops, int fb_index)
+{
+	struct hdmi_event *evt_list;
+	int i;
+	int rc;
+
+	hd->dev = device_create(hdmi_class, parent,
+			MKDEV(HDMI_MAJOR, hdmidevno), hd, "hdmi%d", hdmidevno);
+	if (IS_ERR(hd->dev)) {
+		/* Not fatal */
+		hdmi_dprintk(1, "Unable to create device for hdmi %d; errno = %ld\n",
+			hdmidevno, PTR_ERR(hd->dev));
+		hd->dev = NULL;
+	}
+
+	spin_lock_init(&hd->lock);
+	mutex_init(&hd->hdmi_lock);
+	mutex_init(&hd->update_lock);
+
+	INIT_LIST_HEAD(&hd->hdmi_evt_list);
+	INIT_LIST_HEAD(&hd->free_evt_list);
+	hd->evt_ptr = evt_list =
+		kzalloc(MAX_HDMI_EVENT_SIZE * sizeof(struct hdmi_event), GFP_KERNEL);
+	for (i = 0; i < MAX_HDMI_EVENT_SIZE; i++) {
+		list_add(&evt_list->list, &hd->free_evt_list);
+		evt_list ++;
+	}
+
+	rc = hdmi_register_lcd(hd);
+	if (rc) {
+		device_unregister(hd->dev);
+		hdmi_dprintk(1, "Unable to create lcd device for hdmi\n");
+		goto dev_out;
+	}
+
+	hd->ops = ops;
+	hd->fb_info = registered_fb[fb_index];
+	if (! hd->fb_info) {
+		hdmi_dprintk(1, "Invalid associated fb\n");
+		goto dev_out;
+	}
+
+	/* Register CEC input device here */
+	/* Register audio Asoc device here */
+	/* Init hot plug uevent here */
+	/* Init HDCP here */
+
+	hd->thread = kthread_run(hdmi_event_thread, hd,
+				       "hdmi%d", hdmidevno++);
+
+	if (IS_ERR(hd->thread)) {
+		rc = PTR_ERR(hd->thread);
+		hdmi_dprintk(1, "Unable to create internal thread for hdmi\n");
+		goto lcd_out;
+	}
+
+	return 0;
+
+	kthread_stop(hd->thread);
+lcd_out:
+	lcd_device_unregister(hd->lcd);
+dev_out:
+	device_unregister(hd->dev);
+	hdmi_dprintk(1, "Fail to register hdmi dev\n");
+	return rc;
+}
+EXPORT_SYMBOL(hdmi_dev_register);
+
+/**
+ * hdmi_dev_unregister - unregisters a object of hdmi_dev class.
+ * @hd: the hdmi device object to be unregistered and freed.
+ *
+ * Unregisters a previously registered via hdmi_dev_register object.
+ */
+void hdmi_dev_unregister(struct hdmi_dev *hd)
+{
+	if (!hd)
+		return;
+
+	mutex_lock(&hd->hdmi_lock);
+	hd->ops = NULL;
+	mutex_unlock(&hd->hdmi_lock);
+	lcd_device_unregister(hd->lcd);
+
+	kthread_stop(hd->thread);
+	device_unregister(hd->dev);
+}
+EXPORT_SYMBOL(hdmi_dev_unregister);
+
+static void __exit hdmi_class_exit(void)
+{
+	class_destroy(hdmi_class);
+}
+
+static int __init hdmi_class_init(void)
+{
+	hdmi_dprintk(1, "create hdmi class...\n");
+	hdmi_class = class_create(THIS_MODULE, "hdmi");
+	if (IS_ERR(hdmi_class)) {
+		hdmi_dprintk(1, "Unable to create hdmi class; errno = %ld\n",
+				PTR_ERR(hdmi_class));
+		return PTR_ERR(hdmi_class);
+	}
+
+	hdmi_class->dev_attrs = hdmi_dev_attributes;
+	hdmi_dprintk(1, "create hdmi class ok\n");
+	return 0;
+}
+
+subsys_initcall(hdmi_class_init);
+module_exit(hdmi_class_exit);
+
+module_param_named(debug, hdmi_core_debug, int, 0644);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/hdmi-mmp.c b/drivers/video/hdmi-mmp.c
new file mode 100644
index 0000000..f71f9fa
--- /dev/null
+++ b/drivers/video/hdmi-mmp.c
@@ -0,0 +1,445 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/freezer.h>
+#include <linux/gpio.h>
+#include <linux/hdmi.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+#include <linux/lcd.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <mach/mmp_hdmi.h>
+#include "hdmi-mmp.h"
+
+struct hdmi_mmp_dev {
+	struct hdmi_dev hdmi_dev;
+	struct clk *clk;
+	void *base;
+	void *sspa1_reg_base;
+	unsigned int gpio;
+	unsigned int irq;
+};
+
+static void hdmi_direct_write(void *base, unsigned addr, unsigned data)
+{
+	__raw_writel(data, base + BASE_OFFSET + addr);
+}
+
+static unsigned hdmi_direct_read(void *base, unsigned addr)
+{
+	return __raw_readl(base + BASE_OFFSET + addr);
+}
+
+static unsigned hdmi_read(void *base, unsigned addr)
+{
+	__raw_writel(addr | 1 << 30, base + BASE_OFFSET + HDMI_ADDR);
+	return __raw_readl((base + BASE_OFFSET + HDMI_DATA)) & 0xff;
+}
+
+static void hdmi_write(void *base, unsigned addr, unsigned data)
+{
+	__raw_writel(data & 0xff, base + BASE_OFFSET + HDMI_DATA);
+	__raw_writel(addr | 1 << 31, base + BASE_OFFSET + HDMI_ADDR);
+}
+
+static void hdmi_write_multi(struct hdmi_mmp_dev *md, u32 addr, u8 *buf,
+		u32 length)
+{
+	u32 i;
+	for (i = 0; i < length; i++)
+		hdmi_write(md->base, addr + i, *(buf + i));
+}
+
+static int hdmi_mmp_init_controller(struct hdmi_dev *dev)
+{
+	struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+	u32 tmp;
+
+	printk(KERN_DEBUG "Init mmp hdmi ctroller...\n");
+	hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 1<<27);
+
+	hdmi_direct_write(md->base, HDMI_PHY_CFG_0, 0X00249b6d);
+	hdmi_direct_write(md->base, HDMI_PHY_CFG_1, 0X0492eeee);
+	/* hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 0X00003c30); */
+	hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 0X00003c10);
+	tmp = hdmi_direct_read(md->base, HDMI_PHY_CFG_3);
+	hdmi_direct_write(md->base, HDMI_PHY_CFG_3, (tmp | 0X0000001));
+	msleep(50);
+	hdmi_direct_write(md->base, HDMI_PHY_CFG_3, (tmp & (~0X0000001)));
+	/* temporatyly set the aduio cfg */
+	/* hdmi_direct_write(md->base, HDMI_AUDIO_CFG, 0x869); */
+
+	hdmi_write(md->base, HDTX_HST_PKT_CTRL0, 0);
+	hdmi_write(md->base, HDTX_HST_PKT_CTRL1, 0);
+
+	return 0;
+}
+
+static int hdmi_mmp_set_freq(struct hdmi_mmp_dev *md)
+{
+	int ret = 0;
+
+	/* Fix the pll2refdiv to 1(+2), to get 8.66MHz ref clk
+	 * Stable val recomended between 8-12MHz. To get the reqd
+	 * freq val, just program the fbdiv
+	 * freq takes effect during a fc req
+	 */
+	u32 residual;
+	volatile unsigned int temp = 0;
+	u32 freq;
+	unsigned int postdiv;
+	unsigned int freq_offset_inner;
+	unsigned int hdmi_pll_fbdiv;
+	unsigned int hdmi_pll_refdiv;
+	unsigned int kvco;
+	unsigned int freq_offset_inner_16;
+	unsigned int VPLL_CALCLK_DIV = 1;
+	unsigned int VDDM = 1;
+	unsigned int VDDL = 0x9;
+	unsigned int ICP = 0x9;
+	unsigned int VREG_IVREF = 2;
+	unsigned int INTPI = 5;
+	unsigned int VTH_VPLL_CA = 2;
+	int count = 0x10000;
+
+	ret = hdmi_get_freq ((struct hdmi_dev *)md, &freq);
+	if (ret)
+		return ret;
+
+	freq /= 1000;
+	/* round to freq = N*100k Hz */
+	residual = freq % 100;
+	freq = residual? (freq + 100 - residual) : freq;
+	freq /= 1000;
+
+	switch (freq) {
+	case 25:
+		postdiv = 0x3;
+		freq_offset_inner = 0x1C47;
+		freq_offset_inner_16 = 1;
+		hdmi_pll_fbdiv = 39;
+		hdmi_pll_refdiv = 0;
+		kvco = 0x4;
+		break;
+	case 27:
+		postdiv = 0x3;
+		freq_offset_inner = 0x2d03;
+		freq_offset_inner_16 = 1;
+		hdmi_pll_fbdiv = 42;
+		hdmi_pll_refdiv = 0;
+		kvco = 0x4;
+		break;
+	case 54:
+		postdiv = 0x2;
+		freq_offset_inner = 0x2d03;
+		freq_offset_inner_16 = 1;
+		hdmi_pll_fbdiv = 42;
+		hdmi_pll_refdiv = 0;
+		kvco = 0x4;
+		break;
+	case 74:
+		postdiv = 0x2;
+		freq_offset_inner = 0x084B;
+		freq_offset_inner_16 = 0;
+		hdmi_pll_fbdiv = 57;
+		hdmi_pll_refdiv = 0;
+		kvco = 0x4;
+		break;
+	case 108:
+		postdiv = 0x1;
+		freq_offset_inner = 0x2d03;
+		freq_offset_inner_16 = 1;
+		hdmi_pll_fbdiv = 42;
+		hdmi_pll_refdiv = 0;
+		kvco = 0x4;
+		break;
+	case 148:
+		postdiv = 0x1;
+		freq_offset_inner = 0x084B;
+		freq_offset_inner_16 = 0;
+		hdmi_pll_fbdiv = 57;
+		hdmi_pll_refdiv = 0;
+		kvco = 0x4;
+		break;
+	default:
+		printk(KERN_ERR "not supported freq %d\n", freq);
+		return -EINVAL;
+	}
+
+	hdmi_direct_write(md->base, HDMI_CLOCK_CFG, 1|1<<2|1<<4|4<<5|5<<9|5<<13);
+	//hdmi_direct_write(md->base, HDMI_CLOCK_CFG, 1|1<<2|4<<5|5<<9|5<<13);
+
+	/* power up the pll */
+	temp |= HDMI_PLL_CFG_3_HDMI_PLL_ON;
+	hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+	temp = ((INTPI << HDMI_PLL_CFG_0_INTPI_BASE)	|
+			(HDMI_PLL_CFG_0_CLK_DET_EN)	|
+			(VDDM << HDMI_PLL_CFG_0_VDDM_BASE)	|
+			(VDDL << HDMI_PLL_CFG_0_VDDL_BASE)	|
+			(ICP << HDMI_PLL_CFG_0_ICP_BASE)	|
+			(kvco << HDMI_PLL_CFG_0_KVCO_BASE)	|
+			(VREG_IVREF << HDMI_PLL_CFG_0_VREG_IVREF_BASE)	|
+			(VPLL_CALCLK_DIV << HDMI_PLL_CFG_0_VPLL_CALCLK_DIV_BASE) |
+			(postdiv << HDMI_PLL_CFG_0_POSTDIV_BASE)	|
+			(ICP  << HDMI_PLL_CFG_0_ICP_BASE));
+	hdmi_direct_write(md->base, HDMI_PLL_CFG_0, temp);
+
+	temp = ((HDMI_PLL_CFG_1_MODE) |
+			(HDMI_PLL_CFG_1_EN_PANNEL)|
+			(HDMI_PLL_CFG_1_EN_HDMI)|
+			(VTH_VPLL_CA << HDMI_PLL_CFG_1_VTH_VPLL_CA_BASE)|
+			(freq_offset_inner << HDMI_PLL_CFG_1_FREQ_OFFSET_INNER_BASE)|
+			(freq_offset_inner_16 << 20));
+	hdmi_direct_write(md->base, HDMI_PLL_CFG_1, temp);
+
+	temp = 0;
+	hdmi_direct_write(md->base, HDMI_PLL_CFG_2, temp);
+
+	/* Power Down the pll and set the divider ratios*/
+	temp = (hdmi_pll_fbdiv << HDMI_PLL_CFG_3_HDMI_PLL_FBDIV_BASE)|
+		(hdmi_pll_refdiv << HDMI_PLL_CFG_3_HDMI_PLL_REFDIV_BASE);
+	hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+	/* power up the pll */
+	temp |= HDMI_PLL_CFG_3_HDMI_PLL_ON;
+	hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+	/*wait 10us : just an estimate*/
+	udelay(100);
+
+	/* release the pll out of reset*/
+	temp |= HDMI_PLL_CFG_3_HDMI_PLL_RSTB;
+	hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+	/* PLL_LOCK should be 1, */
+	temp = hdmi_direct_read(md->base, HDMI_PLL_CFG_3);
+	temp &= 0x400000;
+	while ( !temp )	{
+		temp = hdmi_direct_read(md->base, HDMI_PLL_CFG_3);
+		temp &= 0x400000;
+		udelay(10);
+		count--;
+		if (count <= 0) {
+			printk(KERN_ERR "%s PLL lock error, PLL_CFG_3 %x\n",
+				__func__, temp);
+			return -EIO;
+		}
+	}
+	/* pll stablize in 10ms */
+	msleep(10);
+	return ret;
+}
+
+static irqreturn_t hdmi_mmp_hpd(int irq, void *data)
+{
+	printk(KERN_INFO "gpio hpd detected\n");
+
+	hdmi_event_signal((struct hdmi_dev *)data, HDMI_EV_PLUG);
+	return IRQ_HANDLED;
+}
+
+static int hdmi_mmp_get_hpd_status (struct hdmi_dev *dev)
+{
+	struct hdmi_mmp_dev *mh = (struct hdmi_mmp_dev *)dev;
+
+	return ! gpio_get_value(mh->gpio);
+}
+
+static int hdmi_send_packet (struct hdmi_mmp_dev *md, char *buf, int idx)
+{
+	u32 v;
+
+	hdmi_write_multi (md, HDTX_PKT0_BYTE0 + 0x20 * idx, buf, 32);
+
+	v = hdmi_read(md->base, HDTX_HST_PKT_CTRL0);
+	hdmi_write(md->base, HDTX_HST_PKT_CTRL0, v | (1 << idx));
+	/* HW bug */
+	v &= ~(1 << idx);
+	hdmi_write(md->base, HDTX_HST_PKT_CTRL0, v);
+	v = hdmi_read(md->base, HDTX_HST_PKT_CTRL1);
+	hdmi_write(md->base, HDTX_HST_PKT_CTRL1, v | (1 << idx));
+}
+
+static int hdmi_mmp_get_reg (struct hdmi_dev *dev)
+{
+	struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+	int i;
+
+	printk("************direct register*******************\n");
+	for (i = 0x8; i <= 0x30; i += 4)
+		printk("direct offset 0x%x is 0x%x\n", i, hdmi_direct_read(md->base, i));
+	printk("************indirect register*******************\n");
+	for (i = 0; i < 0x13e; i++)
+		printk("offset 0x%x is 0x%x\n", i, hdmi_read(md->base, i));
+}
+
+static int hdmi_mmp_set_reg (struct hdmi_dev *dev)
+{
+	struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+
+	return 0;
+}
+
+static int hdmi_mmp_set_mode (struct hdmi_dev *dev, cea_mode_id id)
+{
+	char *buf;
+	u32 v;
+	struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+
+	dev->mode_id = id;
+	hdmi_mmp_set_freq (md);
+	buf = kzalloc (32, GFP_KERNEL);
+
+	/* get proper avi packet */
+	hdmi_avi_info_frame (dev, buf);
+	/* write avi packet */
+	hdmi_send_packet (dev, buf, 0);
+	memset (buf, 0, 32);
+	hdmi_vender_info_frame (dev, buf);
+	hdmi_send_packet (dev, buf, 1);
+
+	v = hdmi_read(md->base, HDTX_HDMI_CTRL);
+	v &= ~0x78;
+	hdmi_write(md->base, HDTX_HDMI_CTRL, v | 0x1 | (dev->pixel_rept));
+	/* auto detect lcd timing */
+	hdmi_write(md->base, HDTX_VIDEO_CTRL, 0x58);
+	return 0;
+}
+
+static int hdmi_mmp_enable(struct hdmi_dev *dev, int enable)
+{
+	struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+	u32 v;
+
+	v = hdmi_direct_read(md->base, HDMI_CLOCK_CFG);
+	v = enable ? (v | (1<<4)) : (v & ~(1<<4));
+	hdmi_direct_write(md->base, HDMI_CLOCK_CFG, v);
+
+	/* magic register */
+	hdmi_write(md->base, HDTX_TDATA3_1, 0x83);
+	hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 0x3c10);
+	//hdmi_direct_write(md->base,0x1c, 0xaa95);
+	return 0;
+}
+
+static struct hdmi_ops mmp_hdmi_ops = {
+	.init = hdmi_mmp_init_controller,
+	.get_hpd = hdmi_mmp_get_hpd_status,
+	.set_mode = hdmi_mmp_set_mode,
+	.set_enable = hdmi_mmp_enable,
+	.get_reg = hdmi_mmp_get_reg,
+	.set_reg = hdmi_mmp_set_reg,
+};
+
+static int __devinit hdmi_mmp_probe(struct platform_device *pdev)
+{
+	struct hdmi_mmp_mach_info *pdata;
+	struct hdmi_mmp_dev *mdev;
+	struct resource *res;
+	struct clk *clk;
+	int ret = -ENOMEM;
+
+	clk = clk_get(NULL, "HDMICLK");
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "unable to get HDMICLK\n");
+		return PTR_ERR(clk);
+	}
+	clk_enable(clk);
+
+	mdev = kzalloc(sizeof(struct hdmi_mmp_dev), GFP_KERNEL);
+	if (!mdev)
+		goto out_mdev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	pdata = pdev->dev.platform_data;
+	if (res == NULL) {
+		printk(KERN_ERR "hdmi_probe: no memory resources given\n");
+		goto out_mdev;
+	}
+
+	mdev->base = ioremap(res->start, res->end - res->start + 1);
+	if (mdev->base == NULL) {
+		printk(KERN_ERR "%s: can't remap resgister area\n", __func__);
+		goto out_mdev;
+	}
+	mdev->gpio = pdata->gpio;
+	mdev->clk = clk;
+
+	ret = gpio_request(pdata->gpio, pdev->name);
+	if (ret < 0)
+		goto out_mdev;
+
+	ret = gpio_direction_input(pdata->gpio);
+	if (ret < 0)
+		goto out_free;
+
+	ret = request_irq(gpio_to_irq(pdata->gpio),
+			hdmi_mmp_hpd, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+			"HDMI HPD irq", mdev);
+	if(ret < 0)
+		goto free_gpio;
+
+	platform_set_drvdata(pdev, pdata);
+
+	ret = hdmi_dev_register((struct hdmi_dev*)mdev, &pdev->dev,
+		mdev, &mmp_hdmi_ops, pdata->fb_index);
+	if (ret) {
+		printk(KERN_ERR "mmp hdmi_dev_register fail\n");
+		goto irq_free;
+	}
+	return 0;
+
+irq_free:
+	free_irq(gpio_to_irq(pdata->gpio), NULL);
+free_gpio:
+	gpio_free(pdata->gpio);
+out_free:
+out_mdev:
+	kfree(mdev);
+	clk_disable(clk);
+	clk_put(clk);
+	return ret;
+}
+
+static struct platform_driver hdmi_mmp_driver = {
+	.driver         = {
+		.name   = "mmp-hdmi",
+		.owner  = THIS_MODULE,
+	},
+	.probe          = hdmi_mmp_probe,
+	//.remove         = __devexit_p(hdmi_mmp_remove),
+};
+
+static int __devinit hdmi_mmp_init(void)
+{
+	return platform_driver_register(&hdmi_mmp_driver);
+}
+
+module_init(hdmi_mmp_init);
+
+MODULE_AUTHOR("Jun Nie <njun@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("HDMI driver for Marvell MMP");
+MODULE_ALIAS("hdmi-mmp");
diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
index cb175fe..93f5b6e 100644
--- a/drivers/video/modedb.c
+++ b/drivers/video/modedb.c
@@ -292,62 +292,63 @@ static const struct fb_videomode modedb[] = {
 };

 #ifdef CONFIG_FB_MODE_HELPERS
-const struct fb_videomode cea_modes[64] = {
+const struct fb_videomode cea_modes[65] = {
 	/* #1: 640x480p@59.94/60Hz */
 	[1] = {
 		NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
-		FB_VMODE_NONINTERLACED, 0,
+		FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #3: 720x480p@59.94/60Hz */
 	[3] = {
 		NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
-		FB_VMODE_NONINTERLACED, 0,
+		FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #5: 1920x1080i@59.94/60Hz */
 	[5] = {
 		NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5,
 		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-		FB_VMODE_INTERLACED, 0,
+		FB_VMODE_INTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #7: 720(1440)x480iH@59.94/60Hz */
 	[7] = {
 		NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
-		FB_VMODE_INTERLACED, 0,
+		FB_VMODE_INTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #9: 720(1440)x240pH@59.94/60Hz */
 	[9] = {
 		NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0,
-		FB_VMODE_NONINTERLACED, 0,
+		FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #18: 720x576pH@50Hz */
 	[18] = {
 		NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
-		FB_VMODE_NONINTERLACED, 0,
+		FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #19: 1280x720p@50Hz */
 	[19] = {
 		NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5,
 		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-		FB_VMODE_NONINTERLACED, 0,
+		FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #20: 1920x1080i@50Hz */
 	[20] = {
 		NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5,
 		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-		FB_VMODE_INTERLACED, 0,
+		FB_VMODE_INTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #32: 1920x1080p@23.98/24Hz */
 	[32] = {
 		NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5,
 		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-		FB_VMODE_NONINTERLACED, 0,
+		FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #35: (2880)x480p4x@59.94/60Hz */
 	[35] = {
 		NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0,
-		FB_VMODE_NONINTERLACED, 0,
+		FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
 	},
 };
+EXPORT_SYMBOL(cea_modes);

 const struct fb_videomode vesa_modes[] = {
 	/* 0 640x350-85 VESA */
@@ -1119,6 +1120,24 @@ finished:
 }
 EXPORT_SYMBOL(fb_find_best_display);

+/**
+ * fb_find_cea_mode_index - find a cea video mode index
+ *
+ * RETURNS:
+ * cea mode index, 0 if none found
+ */
+const int fb_find_cea_mode_index(struct fb_videomode *m)
+{
+	int idx;
+
+	for (idx = 1; idx <= 64; idx ++) {
+		if (fb_mode_is_equal(&cea_modes[idx], m))
+			return idx;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(fb_find_cea_mode_index);
+
 EXPORT_SYMBOL(fb_videomode_to_var);
 EXPORT_SYMBOL(fb_var_to_videomode);
 EXPORT_SYMBOL(fb_mode_is_equal);
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 6a82748..14672f8 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -1082,10 +1082,24 @@ extern void fb_bl_default_curve(struct fb_info
*fb_info, u8 off, u8 min, u8 max)
 #define FB_MODE_IS_DETAILED	1
 #define FB_MODE_IS_STANDARD	2
 #define FB_MODE_IS_VESA		4
+#define FB_MODE_IS_CEA		5
 #define FB_MODE_IS_CALCULATED	8
 #define FB_MODE_IS_FIRST	16
 #define FB_MODE_IS_FROM_VAR     32

+typedef enum {
+	CEA_640x480p60 = 1,
+	CEA_720x480p60 = 3,
+	CEA_1920x1080i60 = 5,
+	CEA_720x480i60 = 7,
+	CEA_720x240p60 = 9,
+	CEA_720x576p50 = 18,
+	CEA_1280x720p50 = 19,
+	CEA_1920x1080i50 = 20,
+	CEA_1920x1080p24 = 32,
+	CEA_2880x480p60 =35,
+} cea_mode_id;
+
 extern int fbmon_dpms(const struct fb_info *fb_info);
 extern int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var,
 		       struct fb_info *info);
@@ -1124,6 +1138,7 @@ extern void fb_videomode_to_modelist(const
struct fb_videomode *modedb, int num,
 				     struct list_head *head);
 extern const struct fb_videomode *fb_find_best_display(const struct
fb_monspecs *specs,
 						       struct list_head *head);
+extern const int fb_find_cea_mode_index(struct fb_videomode *m);

 /* drivers/video/fbcmap.c */
 extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp);
@@ -1155,7 +1170,7 @@ struct fb_videomode {

 extern const char *fb_mode_option;
 extern const struct fb_videomode vesa_modes[];
-extern const struct fb_videomode cea_modes[64];
+extern const struct fb_videomode cea_modes[65];

 struct fb_modelist {
 	struct list_head list;
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
new file mode 100644
index 0000000..d8a3da9
--- /dev/null
+++ b/include/linux/hdmi.h
@@ -0,0 +1,164 @@
+/*
+ * HDMI Control Abstraction
+ *
+ */
+
+#ifndef _LINUX_HDMI_H
+#define _LINUX_HDMI_H
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+//#include <linux/notifier.h>
+#include <linux/fb.h>
+#include <linux/lcd.h>
+
+/* Notes on locking:
+ *
+ * hdmi_dev->ops_lock is an internal backlight lock protecting the ops
+ * field and no code outside the core should need to touch it.
+ *
+ * Access to set_power() is serialised by the update_lock mutex since
+ * most drivers seem to need this and historically get it wrong.
+ *
+ * Most drivers don't need locking on their get_power() method.
+ * If yours does, you need to implement it in the driver. You can use the
+ * update_lock mutex if appropriate.
+ *
+ * Any other use of the locks below is probably wrong.
+ */
+
+struct hdmi_dev;
+struct fb_info;
+
+struct hdmi_properties {
+	/* source or sink */
+	u16 type;
+};
+
+struct hdmi_ops {
+	int (*init)(struct hdmi_dev *);
+	int (*get_hpd)(struct hdmi_dev *);
+	int (*enable)(struct hdmi_dev *, int);
+	/* Get the LCD panel power status (0: full on, 1..3: controller
+	   power on, flat panel power off, 4: full off), see FB_BLANK_XXX */
+	int (*get_power)(struct hdmi_dev *);
+	/* Enable or disable power to the LCD (0: on; 4: off, see FB_BLANK_XXX) */
+	int (*set_power)(struct hdmi_dev *, int power);
+	/* For debug */
+	int (*get_reg)(struct hdmi_dev *);
+	int (*set_reg)(struct hdmi_dev *, int offset, int v);
+	/* Get the current audio_cfg setting (0-max_audio_cfg) */
+	int (*get_audio_cfg)(struct hdmi_dev *);
+	/* Set LCD panel audio_cfg */
+        int (*set_audio_cfg)(struct hdmi_dev *, int audio_cfg);
+	int (*get_mode)(struct hdmi_dev *, struct fb_videomode *);
+	/* Set LCD panel mode (resolutions ...) */
+	int (*set_mode)(struct hdmi_dev *, cea_mode_id);
+	int (*get_enable)(struct hdmi_dev *);
+	int (*set_enable)(struct hdmi_dev *, int enable);
+	/* Check if given framebuffer device is the one LCD is bound to;
+	   return 0 if not, !=0 if it is. If NULL, hdmi always matches the fb. */
+	int (*check_mode)(struct hdmi_dev *, struct fb_videomode *);
+};
+
+enum hdmi_event_type {
+	/* trigger gpio irq */
+	HDMI_EV_PLUG,
+	HDMI_EV_UNPLUG,
+	/* trigger in gpio irq or from fb change */
+	HDMI_EV_VIDEO,
+	/* trigger in gpio irq or from Asoc change */
+	HDMI_EV_AUDIO,
+	/* trigger from CEC/TV */
+	HDMI_EV_CEC,
+};
+
+struct hdmi_event {
+	enum hdmi_event_type type;
+	struct list_head list;
+};
+
+#define RATIO_16V9_STR "16:9"
+#define RATIO_4V3_STR "4:3"
+#define RATIO_16V9 (0)
+#define RATIO_4V3 (1)
+
+struct hdmi_dev {
+	struct device		*dev;
+	struct hdmi_properties	props;
+	/* This protects the 'ops' field. If 'ops' is NULL, the driver that
+	   registered this device has been unloaded, and if class_get_devdata()
+	   points to something in the body of that driver, it is also invalid. */
+	struct mutex		hdmi_lock;
+	/* Serialise access to set_power method */
+	struct mutex		update_lock;
+	spinlock_t              lock;           /* lock for claim and bus ops */
+	struct list_head	hdmi_evt_list;
+	struct list_head	free_evt_list;
+	struct task_struct	*thread;
+	struct hdmi_event	*evt_ptr;
+	/* If this is NULL, the backing module is unloaded */
+	struct hdmi_ops *ops;
+	/* The framebuffer notifier block */
+	//struct notifier_block fb_notif;
+	struct lcd_device	*lcd;
+	struct fb_info		*fb_info;
+	cea_mode_id		mode_id;
+	//u32			deep_color;
+	u32			enable;
+	u32			pixel_rept;
+	u32			ratio;
+};
+
+struct hdmi_platform_data {
+	/* reset hdmi panel device. */
+	int (*reset)(struct hdmi_dev *ld);
+	/* on or off to hdmi panel. if 'enable' is 0 then
+	   hdmi power off and 1, hdmi power on. */
+	int (*power_on)(struct hdmi_dev *ld, int enable);
+
+	/* it indicates whether hdmi panel was enabled
+	   from bootloader or not. */
+	int hdmi_enabled;
+	/* video, audio, cec, hdcp */
+	unsigned int features;
+	/* it means delay for stable time when it becomes low to high
+	   or high to low that is dependent on whether reset gpio is
+	   low active or high active. */
+	unsigned int reset_delay;
+	/* stable time needing to become hdmi power on. */
+	unsigned int power_on_delay;
+	/* stable time needing to become hdmi power off. */
+	unsigned int power_off_delay;
+
+	/* it could be used for any purpose. */
+	void *pdata;
+};
+
+static inline void hdmi_set_power(struct hdmi_dev *ld, int power)
+{
+	mutex_lock(&ld->update_lock);
+	if (ld->ops && ld->ops->set_power)
+		ld->ops->set_power(ld, power);
+	mutex_unlock(&ld->update_lock);
+}
+
+extern int hdmi_dev_register(struct hdmi_dev *hdmi_dev,
+	struct device *parent, void *devdata, struct hdmi_ops *ops,
+	int fb_index);
+extern void hdmi_dev_unregister(struct hdmi_dev *ld);
+
+#define to_hdmi_dev(obj) container_of(obj, struct hdmi_dev, dev)
+
+static inline void * hdmi_get_data(struct hdmi_dev *ld_dev)
+{
+	return dev_get_drvdata(ld_dev->dev);
+}
+
+
+#endif
+
+int hdmi_get_freq (struct hdmi_dev *hd, u32 *need_freq);
+int hdmi_vender_info_frame (struct hdmi_dev *hd, char buf[]);
+int hdmi_avi_info_frame (struct hdmi_dev *hd, char buf[]);
+void hdmi_event_signal(struct hdmi_dev *dev, enum hdmi_event_type type);
diff --git a/include/linux/lcd.h b/include/linux/lcd.h
index 8877123..b09e0f9 100644
--- a/include/linux/lcd.h
+++ b/include/linux/lcd.h
@@ -48,6 +48,8 @@ struct lcd_ops {
         int (*set_contrast)(struct lcd_device *, int contrast);
 	/* Set LCD panel mode (resolutions ...) */
 	int (*set_mode)(struct lcd_device *, struct fb_videomode *);
+	/* Get LCD panel mode (resolutions ...) */
+	int (*get_mode)(struct lcd_device *, struct fb_videomode *);
 	/* Check if given framebuffer device is the one LCD is bound to;
 	   return 0 if not, !=0 if it is. If NULL, lcd always matches the fb. */
 	int (*check_fb)(struct lcd_device *, struct fb_info *);
diff --git a/include/linux/major.h b/include/linux/major.h
index 6a8ca98..e6e46e2 100644
--- a/include/linux/major.h
+++ b/include/linux/major.h
@@ -170,6 +170,7 @@
 #define IBM_FS3270_MAJOR	228

 #define VIOTAPE_MAJOR		230
+#define HDMI_MAJOR		231

 #define BLOCK_EXT_MAJOR		259
 #define SCSI_OSD_MAJOR		260	/* open-osd's OSD scsi device */

[-- Attachment #2: hdmi_draft.diff --]
[-- Type: text/x-patch, Size: 40666 bytes --]

diff --git a/drivers/video/hdmi-mmp.c b/drivers/video/hdmi-mmp.c
new file mode 100644
index 0000000..f71f9fa
--- /dev/null
+++ b/drivers/video/hdmi-mmp.c
@@ -0,0 +1,445 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/freezer.h>
+#include <linux/gpio.h>
+#include <linux/hdmi.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+#include <linux/lcd.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <mach/mmp_hdmi.h>
+#include "hdmi-mmp.h"
+
+struct hdmi_mmp_dev {
+	struct hdmi_dev hdmi_dev;
+	struct clk *clk;
+	void *base;
+	void *sspa1_reg_base;
+	unsigned int gpio;
+	unsigned int irq;
+};
+
+static void hdmi_direct_write(void *base, unsigned addr, unsigned data)
+{
+	__raw_writel(data, base + BASE_OFFSET + addr);
+}
+
+static unsigned hdmi_direct_read(void *base, unsigned addr)
+{
+	return __raw_readl(base + BASE_OFFSET + addr);
+}
+
+static unsigned hdmi_read(void *base, unsigned addr)
+{
+	__raw_writel(addr | 1 << 30, base + BASE_OFFSET + HDMI_ADDR);
+	return __raw_readl((base + BASE_OFFSET + HDMI_DATA)) & 0xff;
+}
+
+static void hdmi_write(void *base, unsigned addr, unsigned data)
+{
+	__raw_writel(data & 0xff, base + BASE_OFFSET + HDMI_DATA);
+	__raw_writel(addr | 1 << 31, base + BASE_OFFSET + HDMI_ADDR);
+}
+
+static void hdmi_write_multi(struct hdmi_mmp_dev *md, u32 addr, u8 *buf,
+		u32 length)
+{
+	u32 i;
+	for (i = 0; i < length; i++)
+		hdmi_write(md->base, addr + i, *(buf + i));
+}
+
+static int hdmi_mmp_init_controller(struct hdmi_dev *dev)
+{
+	struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+	u32 tmp;
+
+	printk(KERN_DEBUG "Init mmp hdmi ctroller...\n");
+	hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 1<<27);
+
+	hdmi_direct_write(md->base, HDMI_PHY_CFG_0, 0X00249b6d);
+	hdmi_direct_write(md->base, HDMI_PHY_CFG_1, 0X0492eeee);
+	/* hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 0X00003c30); */
+	hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 0X00003c10);
+	tmp = hdmi_direct_read(md->base, HDMI_PHY_CFG_3);
+	hdmi_direct_write(md->base, HDMI_PHY_CFG_3, (tmp | 0X0000001));
+	msleep(50);
+	hdmi_direct_write(md->base, HDMI_PHY_CFG_3, (tmp & (~0X0000001)));
+	/* temporatyly set the aduio cfg */
+	/* hdmi_direct_write(md->base, HDMI_AUDIO_CFG, 0x869); */
+
+	hdmi_write(md->base, HDTX_HST_PKT_CTRL0, 0);
+	hdmi_write(md->base, HDTX_HST_PKT_CTRL1, 0);
+
+	return 0;
+}
+
+static int hdmi_mmp_set_freq(struct hdmi_mmp_dev *md)
+{
+	int ret = 0;
+
+	/* Fix the pll2refdiv to 1(+2), to get 8.66MHz ref clk
+	 * Stable val recomended between 8-12MHz. To get the reqd
+	 * freq val, just program the fbdiv
+	 * freq takes effect during a fc req
+	 */
+	u32 residual;
+	volatile unsigned int temp = 0;
+	u32 freq;
+	unsigned int postdiv;
+	unsigned int freq_offset_inner;
+	unsigned int hdmi_pll_fbdiv;
+	unsigned int hdmi_pll_refdiv;
+	unsigned int kvco;
+	unsigned int freq_offset_inner_16;
+	unsigned int VPLL_CALCLK_DIV = 1;
+	unsigned int VDDM = 1;
+	unsigned int VDDL = 0x9;
+	unsigned int ICP = 0x9;
+	unsigned int VREG_IVREF = 2;
+	unsigned int INTPI = 5;
+	unsigned int VTH_VPLL_CA = 2;
+	int count = 0x10000;
+
+	ret = hdmi_get_freq ((struct hdmi_dev *)md, &freq);
+	if (ret)
+		return ret;
+
+	freq /= 1000;
+	/* round to freq = N*100k Hz */
+	residual = freq % 100;
+	freq = residual? (freq + 100 - residual) : freq;
+	freq /= 1000;
+
+	switch (freq) {
+	case 25:
+		postdiv = 0x3;
+		freq_offset_inner = 0x1C47;
+		freq_offset_inner_16 = 1;
+		hdmi_pll_fbdiv = 39;
+		hdmi_pll_refdiv = 0;
+		kvco = 0x4;
+		break;
+	case 27:
+		postdiv = 0x3;
+		freq_offset_inner = 0x2d03;
+		freq_offset_inner_16 = 1;
+		hdmi_pll_fbdiv = 42;
+		hdmi_pll_refdiv = 0;
+		kvco = 0x4;
+		break;
+	case 54:
+		postdiv = 0x2;
+		freq_offset_inner = 0x2d03;
+		freq_offset_inner_16 = 1;
+		hdmi_pll_fbdiv = 42;
+		hdmi_pll_refdiv = 0;
+		kvco = 0x4;
+		break;
+	case 74:
+		postdiv = 0x2;
+		freq_offset_inner = 0x084B;
+		freq_offset_inner_16 = 0;
+		hdmi_pll_fbdiv = 57;
+		hdmi_pll_refdiv = 0;
+		kvco = 0x4;
+		break;
+	case 108:
+		postdiv = 0x1;
+		freq_offset_inner = 0x2d03;
+		freq_offset_inner_16 = 1;
+		hdmi_pll_fbdiv = 42;
+		hdmi_pll_refdiv = 0;
+		kvco = 0x4;
+		break;
+	case 148:
+		postdiv = 0x1;
+		freq_offset_inner = 0x084B;
+		freq_offset_inner_16 = 0;
+		hdmi_pll_fbdiv = 57;
+		hdmi_pll_refdiv = 0;
+		kvco = 0x4;
+		break;
+	default:
+		printk(KERN_ERR "not supported freq %d\n", freq);
+		return -EINVAL;
+	}
+
+	hdmi_direct_write(md->base, HDMI_CLOCK_CFG, 1|1<<2|1<<4|4<<5|5<<9|5<<13);
+	//hdmi_direct_write(md->base, HDMI_CLOCK_CFG, 1|1<<2|4<<5|5<<9|5<<13);
+
+	/* power up the pll */
+	temp |= HDMI_PLL_CFG_3_HDMI_PLL_ON;
+	hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+	temp = ((INTPI << HDMI_PLL_CFG_0_INTPI_BASE)	|
+			(HDMI_PLL_CFG_0_CLK_DET_EN)	|
+			(VDDM << HDMI_PLL_CFG_0_VDDM_BASE)	|
+			(VDDL << HDMI_PLL_CFG_0_VDDL_BASE)	|
+			(ICP << HDMI_PLL_CFG_0_ICP_BASE)	|
+			(kvco << HDMI_PLL_CFG_0_KVCO_BASE)	|
+			(VREG_IVREF << HDMI_PLL_CFG_0_VREG_IVREF_BASE)	|
+			(VPLL_CALCLK_DIV << HDMI_PLL_CFG_0_VPLL_CALCLK_DIV_BASE) |
+			(postdiv << HDMI_PLL_CFG_0_POSTDIV_BASE)	|
+			(ICP  << HDMI_PLL_CFG_0_ICP_BASE));
+	hdmi_direct_write(md->base, HDMI_PLL_CFG_0, temp);
+
+	temp = ((HDMI_PLL_CFG_1_MODE) |
+			(HDMI_PLL_CFG_1_EN_PANNEL)|
+			(HDMI_PLL_CFG_1_EN_HDMI)|
+			(VTH_VPLL_CA << HDMI_PLL_CFG_1_VTH_VPLL_CA_BASE)|
+			(freq_offset_inner << HDMI_PLL_CFG_1_FREQ_OFFSET_INNER_BASE)|
+			(freq_offset_inner_16 << 20));
+	hdmi_direct_write(md->base, HDMI_PLL_CFG_1, temp);
+
+	temp = 0;
+	hdmi_direct_write(md->base, HDMI_PLL_CFG_2, temp);
+
+	/* Power Down the pll and set the divider ratios*/
+	temp = (hdmi_pll_fbdiv << HDMI_PLL_CFG_3_HDMI_PLL_FBDIV_BASE)|
+		(hdmi_pll_refdiv << HDMI_PLL_CFG_3_HDMI_PLL_REFDIV_BASE);
+	hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+	/* power up the pll */
+	temp |= HDMI_PLL_CFG_3_HDMI_PLL_ON;
+	hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+	/*wait 10us : just an estimate*/
+	udelay(100);
+
+	/* release the pll out of reset*/
+	temp |= HDMI_PLL_CFG_3_HDMI_PLL_RSTB;
+	hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+	/* PLL_LOCK should be 1, */
+	temp = hdmi_direct_read(md->base, HDMI_PLL_CFG_3);
+	temp &= 0x400000;
+	while ( !temp )	{
+		temp = hdmi_direct_read(md->base, HDMI_PLL_CFG_3);
+		temp &= 0x400000;
+		udelay(10);
+		count--;
+		if (count <= 0) {
+			printk(KERN_ERR "%s PLL lock error, PLL_CFG_3 %x\n",
+				__func__, temp);
+			return -EIO;
+		}
+	}
+	/* pll stablize in 10ms */
+	msleep(10);
+	return ret;
+}
+
+static irqreturn_t hdmi_mmp_hpd(int irq, void *data)
+{
+	printk(KERN_INFO "gpio hpd detected\n");
+
+	hdmi_event_signal((struct hdmi_dev *)data, HDMI_EV_PLUG);
+	return IRQ_HANDLED;
+}
+
+static int hdmi_mmp_get_hpd_status (struct hdmi_dev *dev)
+{
+	struct hdmi_mmp_dev *mh = (struct hdmi_mmp_dev *)dev;
+
+	return ! gpio_get_value(mh->gpio);
+}
+
+static int hdmi_send_packet (struct hdmi_mmp_dev *md, char *buf, int idx)
+{
+	u32 v;
+
+	hdmi_write_multi (md, HDTX_PKT0_BYTE0 + 0x20 * idx, buf, 32);
+
+	v = hdmi_read(md->base, HDTX_HST_PKT_CTRL0);
+	hdmi_write(md->base, HDTX_HST_PKT_CTRL0, v | (1 << idx));
+	/* HW bug */
+	v &= ~(1 << idx);
+	hdmi_write(md->base, HDTX_HST_PKT_CTRL0, v);
+	v = hdmi_read(md->base, HDTX_HST_PKT_CTRL1);
+	hdmi_write(md->base, HDTX_HST_PKT_CTRL1, v | (1 << idx));
+}
+
+static int hdmi_mmp_get_reg (struct hdmi_dev *dev)
+{
+	struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+	int i;
+
+	printk("************direct register*******************\n");
+	for (i = 0x8; i <= 0x30; i += 4)
+		printk("direct offset 0x%x is 0x%x\n", i, hdmi_direct_read(md->base, i));
+	printk("************indirect register*******************\n");
+	for (i = 0; i < 0x13e; i++)
+		printk("offset 0x%x is 0x%x\n", i, hdmi_read(md->base, i));
+}
+
+static int hdmi_mmp_set_reg (struct hdmi_dev *dev)
+{
+	struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+
+	return 0;
+}
+
+static int hdmi_mmp_set_mode (struct hdmi_dev *dev, cea_mode_id id)
+{
+	char *buf;
+	u32 v;
+	struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+
+	dev->mode_id = id;
+	hdmi_mmp_set_freq (md);
+	buf = kzalloc (32, GFP_KERNEL);
+
+	/* get proper avi packet */
+	hdmi_avi_info_frame (dev, buf);
+	/* write avi packet */
+	hdmi_send_packet (dev, buf, 0);
+	memset (buf, 0, 32);
+	hdmi_vender_info_frame (dev, buf);
+	hdmi_send_packet (dev, buf, 1);
+
+	v = hdmi_read(md->base, HDTX_HDMI_CTRL);
+	v &= ~0x78;
+	hdmi_write(md->base, HDTX_HDMI_CTRL, v | 0x1 | (dev->pixel_rept));
+	/* auto detect lcd timing */
+	hdmi_write(md->base, HDTX_VIDEO_CTRL, 0x58);
+	return 0;
+}
+
+static int hdmi_mmp_enable(struct hdmi_dev *dev, int enable)
+{
+	struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+	u32 v;
+
+	v = hdmi_direct_read(md->base, HDMI_CLOCK_CFG);
+	v = enable ? (v | (1<<4)) : (v & ~(1<<4));
+	hdmi_direct_write(md->base, HDMI_CLOCK_CFG, v);
+
+	/* magic register */
+	hdmi_write(md->base, HDTX_TDATA3_1, 0x83);
+	hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 0x3c10);
+	//hdmi_direct_write(md->base,0x1c, 0xaa95);
+	return 0;
+}
+
+static struct hdmi_ops mmp_hdmi_ops = {
+	.init = hdmi_mmp_init_controller,
+	.get_hpd = hdmi_mmp_get_hpd_status,
+	.set_mode = hdmi_mmp_set_mode,
+	.set_enable = hdmi_mmp_enable,
+	.get_reg = hdmi_mmp_get_reg,
+	.set_reg = hdmi_mmp_set_reg,
+};
+
+static int __devinit hdmi_mmp_probe(struct platform_device *pdev)
+{
+	struct hdmi_mmp_mach_info *pdata;
+	struct hdmi_mmp_dev *mdev;
+	struct resource *res;
+	struct clk *clk;
+	int ret = -ENOMEM;
+
+	clk = clk_get(NULL, "HDMICLK");
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "unable to get HDMICLK\n");
+		return PTR_ERR(clk);
+	}
+	clk_enable(clk);
+
+	mdev = kzalloc(sizeof(struct hdmi_mmp_dev), GFP_KERNEL);
+	if (!mdev)
+		goto out_mdev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	pdata = pdev->dev.platform_data;
+	if (res == NULL) {
+		printk(KERN_ERR "hdmi_probe: no memory resources given\n");
+		goto out_mdev;
+	}
+
+	mdev->base = ioremap(res->start, res->end - res->start + 1);
+	if (mdev->base == NULL) {
+		printk(KERN_ERR "%s: can't remap resgister area\n", __func__);
+		goto out_mdev;
+	}
+	mdev->gpio = pdata->gpio;
+	mdev->clk = clk;
+
+	ret = gpio_request(pdata->gpio, pdev->name);
+	if (ret < 0)
+		goto out_mdev;
+
+	ret = gpio_direction_input(pdata->gpio);
+	if (ret < 0)
+		goto out_free;
+
+	ret = request_irq(gpio_to_irq(pdata->gpio),
+			hdmi_mmp_hpd, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+			"HDMI HPD irq", mdev);
+	if(ret < 0)
+		goto free_gpio;
+
+	platform_set_drvdata(pdev, pdata);
+
+	ret = hdmi_dev_register((struct hdmi_dev*)mdev, &pdev->dev,
+		mdev, &mmp_hdmi_ops, pdata->fb_index);
+	if (ret) {
+		printk(KERN_ERR "mmp hdmi_dev_register fail\n");
+		goto irq_free;
+	}
+	return 0;
+
+irq_free:
+	free_irq(gpio_to_irq(pdata->gpio), NULL);
+free_gpio:
+	gpio_free(pdata->gpio);
+out_free:
+out_mdev:
+	kfree(mdev);
+	clk_disable(clk);
+	clk_put(clk);
+	return ret;
+}
+
+static struct platform_driver hdmi_mmp_driver = {
+	.driver         = {
+		.name   = "mmp-hdmi",
+		.owner  = THIS_MODULE,
+	},
+	.probe          = hdmi_mmp_probe,
+	//.remove         = __devexit_p(hdmi_mmp_remove),
+};
+
+static int __devinit hdmi_mmp_init(void)
+{
+	return platform_driver_register(&hdmi_mmp_driver);
+}
+
+module_init(hdmi_mmp_init);
+
+MODULE_AUTHOR("Jun Nie <njun@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("HDMI driver for Marvell MMP");
+MODULE_ALIAS("hdmi-mmp");
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
new file mode 100644
index 0000000..c845dc7
--- /dev/null
+++ b/drivers/video/hdmi.c
@@ -0,0 +1,675 @@
+/*
+ * HDMI Control Abstraction
+ *
+ * This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/freezer.h>
+#include <linux/hdmi.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+#include <linux/lcd.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define HDMI_DEBUG
+static int hdmi_core_debug = 5;
+#define hdmi_dprintk(level, fmt, arg...)	if (hdmi_core_debug >= level) \
+	printk(KERN_DEBUG "%s: " fmt , __func__, ## arg)
+
+/* Define the max number of events to buffer */
+#define MAX_HDMI_EVENT_SIZE      16
+
+static int hdmidevno;
+
+/* ==================== info frame utility ===================== */
+
+int hdmi_vender_info_frame (struct hdmi_dev *hd, char buf[])
+{
+	buf[0] = 0x84;
+	buf[1] = 0x1;
+	buf[2] = 0xa;
+	buf[3] = 0x70;
+	buf[4] = 0x1;
+}
+EXPORT_SYMBOL_GPL(hdmi_vender_info_frame);
+
+int hdmi_avi_info_frame (struct hdmi_dev *hd, char buf[])
+{
+	int count;
+	char check_sum;
+
+	hdmi_dprintk(3, "pack avi infoframe\n");
+	/* packet header for AVI Packet in pkt1 */
+	/* Fix me. we should set bits according to format */
+	buf[0] = 0x82; /* InfoFrame Type, AVI frame */
+	buf[1] = 0x2;  /* Version = 02 */
+	buf[2] = 0xd;  /* Length = 13 */
+	buf[3] = 0x0;  /* checksum */
+	buf[4] = 0x0; /* Fix to RGB format currently */
+	/*
+	buf[4] = 0x10; // RGB, Active Format(r0,r1,r2,r3) valid, no bar, no scan
+	buf[4] = 0x30; // HDMI_YUV422, Active Format(r0,r1,r2,r3) valid, no bar, no scan
+	buf[4] = 0x50; // HDMI_YUV444, Active Format(r0,r1,r2,r3) valid, no bar, no scan
+	*/
+
+	buf[5] = 0xa8;  /* ITU-R 709, 16:9 */
+	buf[6] = 0x20;
+	/* buf[6] = 0x0;   // No IT content, xvYCC601, no scaling */
+	buf[7] = hd->mode_id;
+	/* limited YCC range, graphics, no repeat  Pixel repitition factor */
+	buf[8] = hd->pixel_rept;
+
+	buf[9] = 0x0;
+	buf[10] = 0x0;
+	buf[11] = 0x0;
+	buf[12] = 0x0;
+	buf[13] = 0x0;
+	check_sum = 0;
+    for (count = 0; count < 14; count++)
+	    check_sum += buf[count];
+    buf[3] = 0x100 - check_sum;
+    return 0;
+}
+EXPORT_SYMBOL_GPL(hdmi_avi_info_frame);
+
+int hdmi_get_freq (struct hdmi_dev *hd, u32 *need_freq)
+{
+	u64 div_result;
+	int freq;
+
+	/* Calc divider according to pclk in pico second */
+	div_result = 1000000000000ll;
+	do_div(div_result, cea_modes[hd->mode_id].pixclock);
+	freq = (u32)div_result;
+
+	hdmi_dprintk(3, "get base pclk %d\n", freq);
+
+	if (FB_VMODE_INTERLACED & cea_modes[hd->mode_id].vmode)
+		freq >>= 1;
+
+	if (36 == hd->fb_info->var.bits_per_pixel)
+		freq += freq >> 1;
+	else if (30 == hd->fb_info->var.bits_per_pixel)
+		freq += freq >> 2;
+
+	/* We only support repeat=2/4 currently. Add more later */
+	if (!(hd->pixel_rept == 0 || hd->pixel_rept == 1
+				|| hd->pixel_rept == 3)) {
+		hdmi_dprintk(3, "Not support pix repeat %d\n", hd->pixel_rept);
+		return -EINVAL;
+	}
+
+	if (1 == hd->pixel_rept)
+	{
+		if (!(FB_VMODE_INTERLACED & cea_modes[hd->mode_id].vmode))
+			freq <<= 1;
+	} else if (3 == hd->pixel_rept) {
+		if (!(FB_VMODE_INTERLACED & cea_modes[hd->mode_id].vmode))
+			freq <<= 2;
+		else
+			freq <<= 1;
+	}
+
+	hdmi_dprintk(3, "get hdmi pclk %d\n", freq);
+	*need_freq = freq;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hdmi_get_freq);
+
+/* ==================== video part ===================== */
+int hdmi_lcd_check_fb(struct lcd_device * ld, struct fb_info *fi)
+{
+	struct hdmi_dev *hd = lcd_get_data(ld);
+
+	hdmi_dprintk(3, "hdmi_lcd_check_fb..\n");
+	return 1;
+}
+
+/* We'd better update mode list in hot plug operation.
+   LCD framework does not implement get_mode yet */
+static int hdmi_lcd_get_mode(struct lcd_device *ld, struct fb_videomode *mode)
+{
+	struct hdmi_dev *hd = lcd_get_data(ld);
+
+	/* TODO: return parsed mode list */
+	return 0;
+}
+
+static int hdmi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *mode)
+{
+	//struct hdmi_lcd_data *data = lcd_get_data(lcd);
+	struct hdmi_dev *hd = lcd_get_data(ld);
+	cea_mode_id mode_id;
+
+	hdmi_dprintk(3, "hdmi_lcd_set_mode dev\n");
+	mode_id = fb_find_cea_mode_index(mode);
+	if (!mode_id) {
+		hdmi_dprintk(1, "unknown video mode %d\n", mode_id);
+		return -EINVAL;
+	}
+	/* If high level on detection signal, no connection */
+	if(! hd->ops->get_hpd(hd))
+		return 0;
+
+	hd->ops->set_mode(hd, mode_id);
+
+	if (hd->ops && hd->ops->set_enable)
+		hd->ops->set_enable(hd, 1);
+}
+
+static struct lcd_ops hdmi_lcd_ops = {
+	.get_mode = hdmi_lcd_get_mode,
+	.set_mode = hdmi_lcd_set_mode,
+	.check_fb = hdmi_lcd_check_fb,
+};
+
+static int hdmi_register_lcd(struct hdmi_dev *dev)
+{
+	int ret = 0;
+
+	hdmi_dprintk(3, "registering lcd device...\n");
+	/* FIX ME! How to hook expected fb if more than one fb exist? */
+	dev->lcd = lcd_device_register("hdmi-lcd", dev->dev, dev,
+			&hdmi_lcd_ops);
+
+	if (IS_ERR(dev->lcd)) {
+		ret = PTR_ERR(dev->lcd);
+		dev->lcd = NULL;
+		return ret;
+	}
+	hdmi_dprintk(3, "registering lcd device ok\n");
+	return 0;
+}
+
+/* ==================== audio part ===================== */
+/* ==================== cec part ===================== */
+/* ==================== edid part ===================== */
+
+/* ==================== hot plug part ===================== */
+static int hdmi_hotplug(struct hdmi_dev *hd)
+{
+	int ret;
+
+	msleep(100);
+	ret = hd->ops->get_hpd(hd);
+	if(!ret) {
+		hdmi_dprintk(3, "uplugged. dev %p\n", hd);
+		ret = hd->ops->set_enable(hd, 0);
+		lock_fb_info(hd->fb_info);
+		/* We should clear mode list here */
+		/* ... */
+		unlock_fb_info(hd->fb_info);
+		return 0;
+	}
+
+	ret = hd->ops->init(hd);
+	if (ret) {
+		hdmi_dprintk(1, "init fail\n");
+	}
+
+	if(hd->fb_info && hd->fb_info->mode) {
+		lock_fb_info(hd->fb_info);
+		/* Fix me! We should get EDID and parse mode list here */
+		/* But now we use fix mode list first before EDID util is ready */
+		/* ... */
+		unlock_fb_info(hd->fb_info);
+
+		ret = hdmi_lcd_set_mode(hd->lcd, hd->fb_info->mode);
+		if (ret) {
+			hdmi_dprintk(1, "fail to set video mode\n");
+		}
+	} else {
+		hdmi_dprintk(3, "no avaliable video mode\n");
+	}
+
+	if (hd->enable)
+		ret = hd->ops->set_enable(hd, 1);
+	return ret;
+}
+
+/* Handling hot plug HDCP and CEC */
+static int hdmi_event_thread(void *data)
+{
+	struct hdmi_dev *dev = (struct hdmi_dev *)data;
+	struct hdmi_event *evt;
+
+	while (!kthread_should_stop()) {
+
+		spin_lock_irq(&dev->lock);
+		if (list_empty(&dev->hdmi_evt_list)) {
+			set_current_state(TASK_INTERRUPTIBLE);
+
+			if (kthread_should_stop())
+				set_current_state(TASK_RUNNING);
+
+			spin_unlock_irq(&dev->lock);
+			schedule();
+			continue;
+		}
+
+		evt = list_entry(dev->hdmi_evt_list.next, typeof(*evt), list);
+		list_del(&evt->list);
+		spin_unlock_irq(&dev->lock);
+		hdmi_dprintk(1, "Receive evt type %d\n", evt->type);
+
+		mutex_lock(&dev->hdmi_lock);
+		switch (evt->type) {
+			/* trigger gpio irq */
+			case HDMI_EV_PLUG:
+				hdmi_hotplug(dev);
+				break;
+			/* trigger from TV/CEC */
+			case HDMI_EV_CEC:
+				break;
+			default:
+				hdmi_dprintk(1, "Invalid event type %d\n", evt->type);
+				break;
+		}
+		mutex_unlock(&dev->hdmi_lock);
+	}
+
+	return 0;
+}
+
+/**
+ * hdmi_event_signal() - schedules the hdmi event thread
+ * @dev:	the struct hdmi_dev device
+ *
+ * This routine will tell hdmi-core to start decoding stored ir data.
+ */
+void hdmi_event_signal(struct hdmi_dev *dev, enum hdmi_event_type type)
+{
+	unsigned long flags;
+	struct hdmi_event *evt;
+
+	hdmi_dprintk(1, "hdmi event %d stored, dev %p\n", type, dev);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if(HDMI_EV_PLUG == type) {
+		list_for_each_entry(evt, &dev->hdmi_evt_list, list) {
+			if (HDMI_EV_PLUG == evt->type) {
+				hdmi_dprintk(1, "pending hotplug event, ignore new ones\n");
+				spin_unlock_irqrestore(&dev->lock, flags);
+				return;
+			}
+		}
+	}
+	evt = list_entry(dev->free_evt_list.next, struct hdmi_event, list);
+	evt->type = type;
+	list_move_tail(&evt->list, &dev->hdmi_evt_list);
+	wake_up_process(dev->thread);
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+EXPORT_SYMBOL_GPL(hdmi_event_signal);
+
+/* ==================== sys fs part ===================== */
+#ifdef HDMI_DEBUG
+static ssize_t hdmi_show_reg(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	int rc;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->get_reg)
+		rc = sprintf(buf, "%d\n", hd->ops->get_reg(hd));
+	else
+		rc = -ENXIO;
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_store_reg(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc = -ENXIO;
+	char *endp;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+	int power = simple_strtoul(buf, &endp, 0);
+	size_t size = endp - buf;
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->set_reg) {
+		hd->ops->set_reg(hd, power, power);
+		rc = count;
+	}
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_show_reg_addr(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	int rc;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->get_reg)
+		rc = sprintf(buf, "%d\n", hd->ops->get_reg(hd));
+	else
+		rc = -ENXIO;
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_store_reg_addr(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc = -ENXIO;
+	char *endp;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+	int power = simple_strtoul(buf, &endp, 0);
+	size_t size = endp - buf;
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->set_reg) {
+		hd->ops->set_reg(hd, power, power);
+		rc = count;
+	}
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+#endif
+
+static ssize_t hdmi_show_ratio(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	int rc;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ratio == RATIO_16V9)
+		rc = sprintf(buf, "%s\n", RATIO_16V9_STR);
+	else if (hd->ratio == RATIO_4V3)
+		rc = sprintf(buf, "%s\n", RATIO_4V3_STR);
+	else
+		rc = -ENXIO;
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_store_ratio(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc = -ENXIO;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+	mutex_lock(&hd->update_lock);
+	if (sysfs_streq(buf, RATIO_16V9_STR)) {
+		hd->ratio = RATIO_16V9;
+	} else if (sysfs_streq(buf, RATIO_4V3_STR)) {
+		hd->ratio = RATIO_4V3;
+	}
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_show_power(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	int rc;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->get_power) {
+		hd->ops->get_power(hd);
+		rc = sprintf(buf, "%d\n", hd->ops->get_power(hd));
+	}
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_store_power(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc = -ENXIO;
+	char *endp;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+	int power = simple_strtoul(buf, &endp, 0);
+	size_t size = endp - buf;
+
+	if (isspace(*endp))
+		size++;
+	if (size != count)
+		return -EINVAL;
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->set_power) {
+		hdmi_dprintk(3, "hdmi: set power to %d\n", power);
+		hd->ops->set_power(hd, power);
+		rc = count;
+	}
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_show_enable(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int rc = -ENXIO;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->get_enable)
+		rc = sprintf(buf, "%d\n", hd->ops->get_enable(hd));
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_store_enable(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc = -ENXIO;
+	char *endp;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+	int enable = simple_strtoul(buf, &endp, 0);
+	size_t size = endp - buf;
+
+	if (isspace(*endp))
+		size++;
+	if (size != count)
+		return -EINVAL;
+
+	mutex_lock(&hd->update_lock);
+	hd->enable = enable;
+	if (hd->ops && hd->ops->set_enable) {
+		hdmi_dprintk(3, "hdmi: set enable to %d\n", enable);
+		hd->ops->set_enable(hd, enable);
+		rc = count;
+	}
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static ssize_t hdmi_show_hotplug(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc = -ENXIO;
+	char *endp;
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+	mutex_lock(&hd->update_lock);
+	if (hd->ops && hd->ops->get_hpd) {
+		rc = sprintf(buf, "%d\n", ! hd->ops->get_hpd(hd));
+	}
+	mutex_unlock(&hd->update_lock);
+
+	return rc;
+}
+
+static struct class *hdmi_class;
+
+static void hdmi_dev_release(struct device *dev)
+{
+	struct hdmi_dev *hd = dev_get_drvdata(dev);
+	kfree(hd);
+}
+
+static struct device_attribute hdmi_dev_attributes[] = {
+	__ATTR(power, 0644, hdmi_show_power, hdmi_store_power),
+	__ATTR(ratio, 0644, hdmi_show_ratio, hdmi_store_ratio),
+#ifdef HDMI_DEBUG
+	__ATTR(reg_val, 0644, hdmi_show_reg, hdmi_store_reg),
+	__ATTR(reg_addr, 0644, hdmi_show_reg_addr, hdmi_store_reg_addr),
+#endif
+	__ATTR(enable, 0644, hdmi_show_enable, hdmi_store_enable),
+	__ATTR(hotplug, 0644, hdmi_show_hotplug, NULL),
+	__ATTR_NULL,
+};
+
+/**
+ * hdmi_dev_register - register a new object of hdmi_dev class.
+ * @name: the name of the new object(must be the same as the name of the
+ *   respective framebuffer device).
+ * @devdata: an optional pointer to be stored in the device. The
+ *   methods may retrieve it by using hdmi_get_data(hd).
+ * @ops: the hdmi operations structure.
+ *
+ * Creates and registers a new hdmi device. Returns either an ERR_PTR()
+ * or a pointer to the newly allocated device.
+ */
+int hdmi_dev_register(struct hdmi_dev *hd, struct device *parent,
+		void *devdata, struct hdmi_ops *ops, int fb_index)
+{
+	struct hdmi_event *evt_list;
+	int i;
+	int rc;
+
+	hd->dev = device_create(hdmi_class, parent,
+			MKDEV(HDMI_MAJOR, hdmidevno), hd, "hdmi%d", hdmidevno);
+	if (IS_ERR(hd->dev)) {
+		/* Not fatal */
+		hdmi_dprintk(1, "Unable to create device for hdmi %d; errno = %ld\n",
+			hdmidevno, PTR_ERR(hd->dev));
+		hd->dev = NULL;
+	}
+
+	spin_lock_init(&hd->lock);
+	mutex_init(&hd->hdmi_lock);
+	mutex_init(&hd->update_lock);
+
+	INIT_LIST_HEAD(&hd->hdmi_evt_list);
+	INIT_LIST_HEAD(&hd->free_evt_list);
+	hd->evt_ptr = evt_list =
+		kzalloc(MAX_HDMI_EVENT_SIZE * sizeof(struct hdmi_event), GFP_KERNEL);
+	for (i = 0; i < MAX_HDMI_EVENT_SIZE; i++) {
+		list_add(&evt_list->list, &hd->free_evt_list);
+		evt_list ++;
+	}
+
+	rc = hdmi_register_lcd(hd);
+	if (rc) {
+		device_unregister(hd->dev);
+		hdmi_dprintk(1, "Unable to create lcd device for hdmi\n");
+		goto dev_out;
+	}
+
+	hd->ops = ops;
+	hd->fb_info = registered_fb[fb_index];
+	if (! hd->fb_info) {
+		hdmi_dprintk(1, "Invalid associated fb\n");
+		goto dev_out;
+	}
+
+	/* Register CEC input device here */
+	/* Register audio Asoc device here */
+	/* Init hot plug uevent here */
+	/* Init HDCP here */
+
+	hd->thread = kthread_run(hdmi_event_thread, hd,
+				       "hdmi%d", hdmidevno++);
+
+	if (IS_ERR(hd->thread)) {
+		rc = PTR_ERR(hd->thread);
+		hdmi_dprintk(1, "Unable to create internal thread for hdmi\n");
+		goto lcd_out;
+	}
+
+	return 0;
+
+	kthread_stop(hd->thread);
+lcd_out:
+	lcd_device_unregister(hd->lcd);
+dev_out:
+	device_unregister(hd->dev);
+	hdmi_dprintk(1, "Fail to register hdmi dev\n");
+	return rc;
+}
+EXPORT_SYMBOL(hdmi_dev_register);
+
+/**
+ * hdmi_dev_unregister - unregisters a object of hdmi_dev class.
+ * @hd: the hdmi device object to be unregistered and freed.
+ *
+ * Unregisters a previously registered via hdmi_dev_register object.
+ */
+void hdmi_dev_unregister(struct hdmi_dev *hd)
+{
+	if (!hd)
+		return;
+
+	mutex_lock(&hd->hdmi_lock);
+	hd->ops = NULL;
+	mutex_unlock(&hd->hdmi_lock);
+	lcd_device_unregister(hd->lcd);
+
+	kthread_stop(hd->thread);
+	device_unregister(hd->dev);
+}
+EXPORT_SYMBOL(hdmi_dev_unregister);
+
+static void __exit hdmi_class_exit(void)
+{
+	class_destroy(hdmi_class);
+}
+
+static int __init hdmi_class_init(void)
+{
+	hdmi_dprintk(1, "create hdmi class...\n");
+	hdmi_class = class_create(THIS_MODULE, "hdmi");
+	if (IS_ERR(hdmi_class)) {
+		hdmi_dprintk(1, "Unable to create hdmi class; errno = %ld\n",
+				PTR_ERR(hdmi_class));
+		return PTR_ERR(hdmi_class);
+	}
+
+	hdmi_class->dev_attrs = hdmi_dev_attributes;
+	hdmi_dprintk(1, "create hdmi class ok\n");
+	return 0;
+}
+
+subsys_initcall(hdmi_class_init);
+module_exit(hdmi_class_exit);
+
+module_param_named(debug, hdmi_core_debug, int, 0644);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
index cb175fe..93f5b6e 100644
--- a/drivers/video/modedb.c
+++ b/drivers/video/modedb.c
@@ -292,62 +292,63 @@ static const struct fb_videomode modedb[] = {
 };
 
 #ifdef CONFIG_FB_MODE_HELPERS
-const struct fb_videomode cea_modes[64] = {
+const struct fb_videomode cea_modes[65] = {
 	/* #1: 640x480p@59.94/60Hz */
 	[1] = {
 		NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
-		FB_VMODE_NONINTERLACED, 0,
+		FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #3: 720x480p@59.94/60Hz */
 	[3] = {
 		NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
-		FB_VMODE_NONINTERLACED, 0,
+		FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #5: 1920x1080i@59.94/60Hz */
 	[5] = {
 		NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5,
 		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-		FB_VMODE_INTERLACED, 0,
+		FB_VMODE_INTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #7: 720(1440)x480iH@59.94/60Hz */
 	[7] = {
 		NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
-		FB_VMODE_INTERLACED, 0,
+		FB_VMODE_INTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #9: 720(1440)x240pH@59.94/60Hz */
 	[9] = {
 		NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0,
-		FB_VMODE_NONINTERLACED, 0,
+		FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #18: 720x576pH@50Hz */
 	[18] = {
 		NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
-		FB_VMODE_NONINTERLACED, 0,
+		FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #19: 1280x720p@50Hz */
 	[19] = {
 		NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5,
 		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-		FB_VMODE_NONINTERLACED, 0,
+		FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #20: 1920x1080i@50Hz */
 	[20] = {
 		NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5,
 		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-		FB_VMODE_INTERLACED, 0,
+		FB_VMODE_INTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #32: 1920x1080p@23.98/24Hz */
 	[32] = {
 		NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5,
 		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
-		FB_VMODE_NONINTERLACED, 0,
+		FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
 	},
 	/* #35: (2880)x480p4x@59.94/60Hz */
 	[35] = {
 		NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0,
-		FB_VMODE_NONINTERLACED, 0,
+		FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
 	},
 };
+EXPORT_SYMBOL(cea_modes);
 
 const struct fb_videomode vesa_modes[] = {
 	/* 0 640x350-85 VESA */
@@ -1119,6 +1120,24 @@ finished:
 }
 EXPORT_SYMBOL(fb_find_best_display);
 
+/**
+ * fb_find_cea_mode_index - find a cea video mode index
+ *
+ * RETURNS:
+ * cea mode index, 0 if none found
+ */
+const int fb_find_cea_mode_index(struct fb_videomode *m)
+{
+	int idx;
+
+	for (idx = 1; idx <= 64; idx ++) {
+		if (fb_mode_is_equal(&cea_modes[idx], m))
+			return idx;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(fb_find_cea_mode_index);
+
 EXPORT_SYMBOL(fb_videomode_to_var);
 EXPORT_SYMBOL(fb_var_to_videomode);
 EXPORT_SYMBOL(fb_mode_is_equal);
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 6a82748..14672f8 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -1082,10 +1082,24 @@ extern void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max)
 #define FB_MODE_IS_DETAILED	1
 #define FB_MODE_IS_STANDARD	2
 #define FB_MODE_IS_VESA		4
+#define FB_MODE_IS_CEA		5
 #define FB_MODE_IS_CALCULATED	8
 #define FB_MODE_IS_FIRST	16
 #define FB_MODE_IS_FROM_VAR     32
 
+typedef enum {
+	CEA_640x480p60 = 1,
+	CEA_720x480p60 = 3,
+	CEA_1920x1080i60 = 5,
+	CEA_720x480i60 = 7,
+	CEA_720x240p60 = 9,
+	CEA_720x576p50 = 18,
+	CEA_1280x720p50 = 19,
+	CEA_1920x1080i50 = 20,
+	CEA_1920x1080p24 = 32,
+	CEA_2880x480p60 =35,
+} cea_mode_id;
+
 extern int fbmon_dpms(const struct fb_info *fb_info);
 extern int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var,
 		       struct fb_info *info);
@@ -1124,6 +1138,7 @@ extern void fb_videomode_to_modelist(const struct fb_videomode *modedb, int num,
 				     struct list_head *head);
 extern const struct fb_videomode *fb_find_best_display(const struct fb_monspecs *specs,
 						       struct list_head *head);
+extern const int fb_find_cea_mode_index(struct fb_videomode *m);
 
 /* drivers/video/fbcmap.c */
 extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp);
@@ -1155,7 +1170,7 @@ struct fb_videomode {
 
 extern const char *fb_mode_option;
 extern const struct fb_videomode vesa_modes[];
-extern const struct fb_videomode cea_modes[64];
+extern const struct fb_videomode cea_modes[65];
 
 struct fb_modelist {
 	struct list_head list;
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
new file mode 100644
index 0000000..d8a3da9
--- /dev/null
+++ b/include/linux/hdmi.h
@@ -0,0 +1,164 @@
+/*
+ * HDMI Control Abstraction
+ *
+ */
+
+#ifndef _LINUX_HDMI_H
+#define _LINUX_HDMI_H
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+//#include <linux/notifier.h>
+#include <linux/fb.h>
+#include <linux/lcd.h>
+
+/* Notes on locking:
+ *
+ * hdmi_dev->ops_lock is an internal backlight lock protecting the ops
+ * field and no code outside the core should need to touch it.
+ *
+ * Access to set_power() is serialised by the update_lock mutex since
+ * most drivers seem to need this and historically get it wrong.
+ *
+ * Most drivers don't need locking on their get_power() method.
+ * If yours does, you need to implement it in the driver. You can use the
+ * update_lock mutex if appropriate.
+ *
+ * Any other use of the locks below is probably wrong.
+ */
+
+struct hdmi_dev;
+struct fb_info;
+
+struct hdmi_properties {
+	/* source or sink */
+	u16 type;
+};
+
+struct hdmi_ops {
+	int (*init)(struct hdmi_dev *);
+	int (*get_hpd)(struct hdmi_dev *);
+	int (*enable)(struct hdmi_dev *, int);
+	/* Get the LCD panel power status (0: full on, 1..3: controller
+	   power on, flat panel power off, 4: full off), see FB_BLANK_XXX */
+	int (*get_power)(struct hdmi_dev *);
+	/* Enable or disable power to the LCD (0: on; 4: off, see FB_BLANK_XXX) */
+	int (*set_power)(struct hdmi_dev *, int power);
+	/* For debug */
+	int (*get_reg)(struct hdmi_dev *);
+	int (*set_reg)(struct hdmi_dev *, int offset, int v);
+	/* Get the current audio_cfg setting (0-max_audio_cfg) */
+	int (*get_audio_cfg)(struct hdmi_dev *);
+	/* Set LCD panel audio_cfg */
+        int (*set_audio_cfg)(struct hdmi_dev *, int audio_cfg);
+	int (*get_mode)(struct hdmi_dev *, struct fb_videomode *);
+	/* Set LCD panel mode (resolutions ...) */
+	int (*set_mode)(struct hdmi_dev *, cea_mode_id);
+	int (*get_enable)(struct hdmi_dev *);
+	int (*set_enable)(struct hdmi_dev *, int enable);
+	/* Check if given framebuffer device is the one LCD is bound to;
+	   return 0 if not, !=0 if it is. If NULL, hdmi always matches the fb. */
+	int (*check_mode)(struct hdmi_dev *, struct fb_videomode *);
+};
+
+enum hdmi_event_type {
+	/* trigger gpio irq */
+	HDMI_EV_PLUG,
+	HDMI_EV_UNPLUG,
+	/* trigger in gpio irq or from fb change */
+	HDMI_EV_VIDEO,
+	/* trigger in gpio irq or from Asoc change */
+	HDMI_EV_AUDIO,
+	/* trigger from CEC/TV */
+	HDMI_EV_CEC,
+};
+
+struct hdmi_event {
+	enum hdmi_event_type type;
+	struct list_head list;
+};
+
+#define RATIO_16V9_STR "16:9"
+#define RATIO_4V3_STR "4:3"
+#define RATIO_16V9 (0)
+#define RATIO_4V3 (1)
+
+struct hdmi_dev {
+	struct device		*dev;
+	struct hdmi_properties	props;
+	/* This protects the 'ops' field. If 'ops' is NULL, the driver that
+	   registered this device has been unloaded, and if class_get_devdata()
+	   points to something in the body of that driver, it is also invalid. */
+	struct mutex		hdmi_lock;
+	/* Serialise access to set_power method */
+	struct mutex		update_lock;
+	spinlock_t              lock;           /* lock for claim and bus ops */
+	struct list_head	hdmi_evt_list;
+	struct list_head	free_evt_list;
+	struct task_struct	*thread;
+	struct hdmi_event	*evt_ptr;
+	/* If this is NULL, the backing module is unloaded */
+	struct hdmi_ops *ops;
+	/* The framebuffer notifier block */
+	//struct notifier_block fb_notif;
+	struct lcd_device	*lcd;
+	struct fb_info		*fb_info;
+	cea_mode_id		mode_id;
+	//u32			deep_color;
+	u32			enable;
+	u32			pixel_rept;
+	u32			ratio;
+};
+
+struct hdmi_platform_data {
+	/* reset hdmi panel device. */
+	int (*reset)(struct hdmi_dev *ld);
+	/* on or off to hdmi panel. if 'enable' is 0 then
+	   hdmi power off and 1, hdmi power on. */
+	int (*power_on)(struct hdmi_dev *ld, int enable);
+
+	/* it indicates whether hdmi panel was enabled
+	   from bootloader or not. */
+	int hdmi_enabled;
+	/* video, audio, cec, hdcp */
+	unsigned int features;
+	/* it means delay for stable time when it becomes low to high
+	   or high to low that is dependent on whether reset gpio is
+	   low active or high active. */
+	unsigned int reset_delay;
+	/* stable time needing to become hdmi power on. */
+	unsigned int power_on_delay;
+	/* stable time needing to become hdmi power off. */
+	unsigned int power_off_delay;
+
+	/* it could be used for any purpose. */
+	void *pdata;
+};
+
+static inline void hdmi_set_power(struct hdmi_dev *ld, int power)
+{
+	mutex_lock(&ld->update_lock);
+	if (ld->ops && ld->ops->set_power)
+		ld->ops->set_power(ld, power);
+	mutex_unlock(&ld->update_lock);
+}
+
+extern int hdmi_dev_register(struct hdmi_dev *hdmi_dev,
+	struct device *parent, void *devdata, struct hdmi_ops *ops,
+	int fb_index);
+extern void hdmi_dev_unregister(struct hdmi_dev *ld);
+
+#define to_hdmi_dev(obj) container_of(obj, struct hdmi_dev, dev)
+
+static inline void * hdmi_get_data(struct hdmi_dev *ld_dev)
+{
+	return dev_get_drvdata(ld_dev->dev);
+}
+
+
+#endif
+
+int hdmi_get_freq (struct hdmi_dev *hd, u32 *need_freq);
+int hdmi_vender_info_frame (struct hdmi_dev *hd, char buf[]);
+int hdmi_avi_info_frame (struct hdmi_dev *hd, char buf[]);
+void hdmi_event_signal(struct hdmi_dev *dev, enum hdmi_event_type type);
diff --git a/include/linux/lcd.h b/include/linux/lcd.h
index 8877123..b09e0f9 100644
--- a/include/linux/lcd.h
+++ b/include/linux/lcd.h
@@ -48,6 +48,8 @@ struct lcd_ops {
         int (*set_contrast)(struct lcd_device *, int contrast);
 	/* Set LCD panel mode (resolutions ...) */
 	int (*set_mode)(struct lcd_device *, struct fb_videomode *);
+	/* Get LCD panel mode (resolutions ...) */
+	int (*get_mode)(struct lcd_device *, struct fb_videomode *);
 	/* Check if given framebuffer device is the one LCD is bound to;
 	   return 0 if not, !=0 if it is. If NULL, lcd always matches the fb. */
 	int (*check_fb)(struct lcd_device *, struct fb_info *);
diff --git a/include/linux/major.h b/include/linux/major.h
index 6a8ca98..e6e46e2 100644
--- a/include/linux/major.h
+++ b/include/linux/major.h
@@ -170,6 +170,7 @@
 #define IBM_FS3270_MAJOR	228
 
 #define VIOTAPE_MAJOR		230
+#define HDMI_MAJOR		231 
 
 #define BLOCK_EXT_MAJOR		259
 #define SCSI_OSD_MAJOR		260	/* open-osd's OSD scsi device */

^ permalink raw reply related

* [PATCH] video: s3c-fb: fix transparency length for pixel blending
From: Jingoo Han @ 2011-11-07  6:03 UTC (permalink / raw)
  To: linux-fbdev

Transparency length is fixed for pixel blending in order to support
ARGB 8888 format.

Signed-off-by: Jingoo Han <jg1.han@samsung.com>
---
 drivers/video/s3c-fb.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c
index 0753b1c..12eaee0 100644
--- a/drivers/video/s3c-fb.c
+++ b/drivers/video/s3c-fb.c
@@ -621,7 +621,8 @@ static int s3c_fb_set_par(struct fb_info *info)
 		} else if (var->transp.length = 1)
 			data |= WINCON1_BPPMODE_25BPP_A1888
 				| WINCON1_BLD_PIX;
-		else if (var->transp.length = 4)
+		else if ((var->transp.length = 4) ||
+			(var->transp.length = 8))
 			data |= WINCON1_BPPMODE_28BPP_A4888
 				| WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
 		else
-- 
1.7.1


^ permalink raw reply related

* Re: [PATCH v3] GIO bus support for SGI IP22/28
From: Ralf Baechle @ 2011-11-04 11:22 UTC (permalink / raw)
  To: Thomas Bogendoerfer; +Cc: linux-mips, linux-fbdev, FlorianSchandinat
In-Reply-To: <20111104110905.531871BB71D@solo.franken.de>

Thanks,

queued for 3.3.

  Ralf

^ permalink raw reply

* [PATCH v3] GIO bus support for SGI IP22/28
From: Thomas Bogendoerfer @ 2011-11-04 11:09 UTC (permalink / raw)
  To: linux-mips, linux-fbdev; +Cc: ralf, FlorianSchandinat

SGI IP22/IP28 machines have GIO busses for adding graphics and other
extension cards. This patch adds support for GIO driver/device
handling and converts the newport console driver to a GIO driver.

Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Acked-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
---

Changes in v3:
- reworked device tree for gio bus
- make module autoloading working

Changes in v2:
- use EXPORT_SYMBOL_GPL
- ChallengeS has only 2 slots
- use subsys_initcall for gio detection

 arch/mips/include/asm/gio_device.h  |   56 +++++
 arch/mips/sgi-ip22/Makefile         |    2 +-
 arch/mips/sgi-ip22/ip22-gio.c       |  428 +++++++++++++++++++++++++++++++++++
 arch/mips/sgi-ip22/ip22-mc.c        |   10 +-
 arch/mips/sgi-ip22/ip22-setup.c     |   21 --
 drivers/video/console/newport_con.c |   63 ++++--
 6 files changed, 534 insertions(+), 46 deletions(-)

diff --git a/arch/mips/include/asm/gio_device.h b/arch/mips/include/asm/gio_device.h
new file mode 100644
index 0000000..5437c84
--- /dev/null
+++ b/arch/mips/include/asm/gio_device.h
@@ -0,0 +1,56 @@
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+
+struct gio_device_id {
+	__u8 id;
+};
+
+struct gio_device {
+	struct device   dev;
+	struct resource resource;
+	unsigned int    irq;
+	unsigned int    slotno;
+
+	const char      *name;
+	struct gio_device_id id;
+	unsigned        id32:1;
+	unsigned        gio64:1;
+};
+#define to_gio_device(d) container_of(d, struct gio_device, dev)
+
+struct gio_driver {
+	const char    *name;
+	struct module *owner;
+	const struct gio_device_id *id_table;
+
+	int  (*probe)(struct gio_device *, const struct gio_device_id *);
+	void (*remove)(struct gio_device *);
+	int  (*suspend)(struct gio_device *, pm_message_t);
+	int  (*resume)(struct gio_device *);
+	void (*shutdown)(struct gio_device *);
+
+	struct device_driver driver;
+};
+#define to_gio_driver(drv) container_of(drv, struct gio_driver, driver)
+
+extern const struct gio_device_id *gio_match_device(const struct gio_device_id *,
+						    const struct gio_device *);
+extern struct gio_device *gio_dev_get(struct gio_device *);
+extern void gio_dev_put(struct gio_device *);
+
+extern int gio_device_register(struct gio_device *);
+extern void gio_device_unregister(struct gio_device *);
+extern void gio_release_dev(struct device *);
+
+static inline void gio_device_free(struct gio_device *dev)
+{
+	gio_release_dev(&dev->dev);
+}
+
+extern int gio_register_driver(struct gio_driver *);
+extern void gio_unregister_driver(struct gio_driver *);
+
+#define gio_get_drvdata(_dev)        drv_get_drvdata(&(_dev)->dev)
+#define gio_set_drvdata(_dev, data)  drv_set_drvdata(&(_dev)->dev, (data))
+
+extern void gio_set_master(struct gio_device *);
diff --git a/arch/mips/sgi-ip22/Makefile b/arch/mips/sgi-ip22/Makefile
index cc53849..411cda9 100644
--- a/arch/mips/sgi-ip22/Makefile
+++ b/arch/mips/sgi-ip22/Makefile
@@ -4,7 +4,7 @@
 #
 
 obj-y	+= ip22-mc.o ip22-hpc.o ip22-int.o ip22-time.o ip22-nvram.o \
-	   ip22-platform.o ip22-reset.o ip22-setup.o
+	   ip22-platform.o ip22-reset.o ip22-setup.o ip22-gio.o
 
 obj-$(CONFIG_SGI_IP22) += ip22-berr.o
 obj-$(CONFIG_SGI_IP28) += ip28-berr.o
diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c
new file mode 100644
index 0000000..b6537d9
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-gio.c
@@ -0,0 +1,428 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#include <asm/addrspace.h>
+#include <asm/paccess.h>
+#include <asm/gio_device.h>
+#include <asm/sgi/gio.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/ip22.h>
+
+static struct bus_type gio_bus_type;
+
+static struct {
+	const char *name;
+	__u8       id;
+} gio_name_table[] = {
+	{ .name = "SGI Impact", .id = 0x10 },
+	{ .name = "Phobos G160", .id = 0x35 },
+	/* fake IDs */
+	{ .name = "SGI Newport", .id = 0x7e },
+	{ .name = "SGI GR2/GR3", .id = 0x7f },
+};
+
+static struct device gio_bus = {
+	.init_name = "gio",
+};
+
+/**
+ * gio_match_device - Tell if an of_device structure has a matching
+ * gio_match structure
+ * @ids: array of of device match structures to search in
+ * @dev: the of device structure to match against
+ *
+ * Used by a driver to check whether an of_device present in the
+ * system is in its list of supported devices.
+ */
+const struct gio_device_id *gio_match_device(const struct gio_device_id *match,
+		     const struct gio_device *dev)
+{
+	const struct gio_device_id *ids;
+
+	for (ids = match; ids->id != 0xff; ids++)
+		if (ids->id = dev->id.id)
+			return ids;
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(gio_match_device);
+
+struct gio_device *gio_dev_get(struct gio_device *dev)
+{
+	struct device *tmp;
+
+	if (!dev)
+		return NULL;
+	tmp = get_device(&dev->dev);
+	if (tmp)
+		return to_gio_device(tmp);
+	else
+		return NULL;
+}
+EXPORT_SYMBOL_GPL(gio_dev_get);
+
+void gio_dev_put(struct gio_device *dev)
+{
+	if (dev)
+		put_device(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(gio_dev_put);
+
+/**
+ * gio_release_dev - free an gio device structure when all users of it are finished.
+ * @dev: device that's been disconnected
+ *
+ * Will be called only by the device core when all users of this gio device are
+ * done.
+ */
+void gio_release_dev(struct device *dev)
+{
+	struct gio_device *giodev;
+
+	giodev = to_gio_device(dev);
+	kfree(giodev);
+}
+EXPORT_SYMBOL_GPL(gio_release_dev);
+
+int gio_device_register(struct gio_device *giodev)
+{
+	giodev->dev.bus = &gio_bus_type;
+	giodev->dev.parent = &gio_bus;
+	return device_register(&giodev->dev);
+}
+EXPORT_SYMBOL_GPL(gio_device_register);
+
+void gio_device_unregister(struct gio_device *giodev)
+{
+	device_unregister(&giodev->dev);
+}
+EXPORT_SYMBOL_GPL(gio_device_unregister);
+
+static int gio_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct gio_device *gio_dev = to_gio_device(dev);
+	struct gio_driver *gio_drv = to_gio_driver(drv);
+
+	return gio_match_device(gio_drv->id_table, gio_dev) != NULL;
+}
+
+static int gio_device_probe(struct device *dev)
+{
+	int error = -ENODEV;
+	struct gio_driver *drv;
+	struct gio_device *gio_dev;
+	const struct gio_device_id *match;
+
+	drv = to_gio_driver(dev->driver);
+	gio_dev = to_gio_device(dev);
+
+	if (!drv->probe)
+		return error;
+
+	gio_dev_get(gio_dev);
+
+	match = gio_match_device(drv->id_table, gio_dev);
+	if (match)
+		error = drv->probe(gio_dev, match);
+	if (error)
+		gio_dev_put(gio_dev);
+
+	return error;
+}
+
+static int gio_device_remove(struct device *dev)
+{
+	struct gio_device *gio_dev = to_gio_device(dev);
+	struct gio_driver *drv = to_gio_driver(dev->driver);
+
+	if (dev->driver && drv->remove)
+		drv->remove(gio_dev);
+	return 0;
+}
+
+static int gio_device_suspend(struct device *dev, pm_message_t state)
+{
+	struct gio_device *gio_dev = to_gio_device(dev);
+	struct gio_driver *drv = to_gio_driver(dev->driver);
+	int error = 0;
+
+	if (dev->driver && drv->suspend)
+		error = drv->suspend(gio_dev, state);
+	return error;
+}
+
+static int gio_device_resume(struct device *dev)
+{
+	struct gio_device *gio_dev = to_gio_device(dev);
+	struct gio_driver *drv = to_gio_driver(dev->driver);
+	int error = 0;
+
+	if (dev->driver && drv->resume)
+		error = drv->resume(gio_dev);
+	return error;
+}
+
+static void gio_device_shutdown(struct device *dev)
+{
+	struct gio_device *gio_dev = to_gio_device(dev);
+	struct gio_driver *drv = to_gio_driver(dev->driver);
+
+	if (dev->driver && drv->shutdown)
+		drv->shutdown(gio_dev);
+}
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
+			     char *buf)
+{
+	struct gio_device *gio_dev = to_gio_device(dev);
+	int len = snprintf(buf, PAGE_SIZE, "gio:%x\n", gio_dev->id.id);
+
+	return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static ssize_t name_show(struct device *dev,
+			 struct device_attribute *attr, char *buf)
+{
+	struct gio_device *giodev;
+
+	giodev = to_gio_device(dev);
+	return sprintf(buf, "%s", giodev->name);
+}
+
+static ssize_t id_show(struct device *dev,
+		       struct device_attribute *attr, char *buf)
+{
+	struct gio_device *giodev;
+
+	giodev = to_gio_device(dev);
+	return sprintf(buf, "%x", giodev->id.id);
+}
+
+static struct device_attribute gio_dev_attrs[] = {
+	__ATTR_RO(modalias),
+	__ATTR_RO(name),
+	__ATTR_RO(id),
+	__ATTR_NULL,
+};
+
+static int gio_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct gio_device *gio_dev = to_gio_device(dev);
+
+	add_uevent_var(env, "MODALIAS=gio:%x", gio_dev->id.id);
+	return 0;
+}
+
+int gio_register_driver(struct gio_driver *drv)
+{
+	/* initialize common driver fields */
+	if (!drv->driver.name)
+		drv->driver.name = drv->name;
+	if (!drv->driver.owner)
+		drv->driver.owner = drv->owner;
+	drv->driver.bus = &gio_bus_type;
+
+	/* register with core */
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(gio_register_driver);
+
+void gio_unregister_driver(struct gio_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(gio_unregister_driver);
+
+void gio_set_master(struct gio_device *dev)
+{
+	u32 tmp = sgimc->giopar;
+
+	switch (dev->slotno) {
+	case 0:
+		tmp |= SGIMC_GIOPAR_MASTERGFX;
+		break;
+	case 1:
+		tmp |= SGIMC_GIOPAR_MASTEREXP0;
+		break;
+	case 2:
+		tmp |= SGIMC_GIOPAR_MASTEREXP1;
+		break;
+	}
+	sgimc->giopar = tmp;
+}
+EXPORT_SYMBOL_GPL(gio_set_master);
+
+void ip22_gio_set_64bit(int slotno)
+{
+	u32 tmp = sgimc->giopar;
+
+	switch (slotno) {
+	case 0:
+		tmp |= SGIMC_GIOPAR_GFX64;
+		break;
+	case 1:
+		tmp |= SGIMC_GIOPAR_EXP064;
+		break;
+	case 2:
+		tmp |= SGIMC_GIOPAR_EXP164;
+		break;
+	}
+	sgimc->giopar = tmp;
+}
+
+static int ip22_gio_id(unsigned long addr, u32 *res)
+{
+	u8 tmp8;
+	u8 tmp16;
+	u32 tmp32;
+	u8 *ptr8;
+	u16 *ptr16;
+	u32 *ptr32;
+
+	ptr32 = (void *)CKSEG1ADDR(addr);
+	if (!get_dbe(tmp32, ptr32)) {
+		/*
+		 * We got no DBE, but this doesn't mean anything.
+		 * If GIO is pipelined (which can't be disabled
+		 * for GFX slot) we don't get a DBE, but we see
+		 * the transfer size as data. So we do an 8bit
+		 * and a 16bit access and check whether the common
+		 * data matches
+		 */
+		ptr8 = (void *)CKSEG1ADDR(addr + 3);
+		get_dbe(tmp8, ptr8);
+		ptr16 = (void *)CKSEG1ADDR(addr + 2);
+		get_dbe(tmp16, ptr16);
+		if (tmp8 = (tmp16 & 0xff) &&
+		    tmp8 = (tmp32 & 0xff) &&
+		    tmp16 = (tmp32 & 0xffff)) {
+			*res = tmp32;
+			return 1;
+		}
+	}
+	return 0; /* nothing here */
+}
+
+#define HQ2_MYSTERY_OFFS       0x6A07C
+#define NEWPORT_USTATUS_OFFS   0xF133C
+
+static int ip22_is_gr2(unsigned long addr)
+{
+	u32 tmp;
+	u32 *ptr;
+
+	/* HQ2 only allows 32bit accesses */
+	ptr = (void *)CKSEG1ADDR(addr + HQ2_MYSTERY_OFFS);
+	if (!get_dbe(tmp, ptr)) {
+		if (tmp = 0xdeadbeef)
+			return 1;
+	}
+	return 0;
+}
+
+
+static void ip22_check_gio(int slotno, unsigned long addr)
+{
+	const char *name = "Unknown";
+	struct gio_device *gio_dev;
+	u32 tmp;
+	__u8 id;
+	int i;
+
+	/* first look for GR2/GR3 by checking mystery register */
+	if (ip22_is_gr2(addr))
+		tmp = 0x7f;
+	else {
+		if (!ip22_gio_id(addr, &tmp)) {
+			/*
+			 * no GIO signature at start address of slot, but
+			 * Newport doesn't have one, so let's check usea
+			 * status register
+			 */
+			if (ip22_gio_id(addr + NEWPORT_USTATUS_OFFS, &tmp))
+				tmp = 0x7e;
+			else
+				tmp = 0;
+		}
+	}
+	if (tmp) {
+		id = GIO_ID(tmp);
+		if (tmp & GIO_32BIT_ID) {
+			if (tmp & GIO_64BIT_IFACE)
+				ip22_gio_set_64bit(slotno);
+		}
+		for (i = 0; i < ARRAY_SIZE(gio_name_table); i++) {
+			if (id = gio_name_table[i].id) {
+				name = gio_name_table[i].name;
+				break;
+			}
+		}
+		printk(KERN_INFO "GIO: slot %d : %s (id %x)\n",
+		       slotno, name, id);
+		gio_dev = kzalloc(sizeof *gio_dev, GFP_KERNEL);
+		gio_dev->name = name;
+		gio_dev->slotno = slotno;
+		gio_dev->id.id = id;
+		gio_dev->resource.start = addr;
+		gio_dev->resource.end = addr + 0x3fffff;
+		gio_dev->resource.flags = IORESOURCE_MEM;
+		dev_set_name(&gio_dev->dev, "%d", slotno);
+		gio_device_register(gio_dev);
+	} else
+		printk(KERN_INFO "GIO: slot %d : Empty\n", slotno);
+}
+
+static struct bus_type gio_bus_type = {
+	.name      = "gio",
+	.dev_attrs = gio_dev_attrs,
+	.match     = gio_bus_match,
+	.probe     = gio_device_probe,
+	.remove    = gio_device_remove,
+	.suspend   = gio_device_suspend,
+	.resume    = gio_device_resume,
+	.shutdown  = gio_device_shutdown,
+	.uevent    = gio_device_uevent,
+};
+
+static struct resource gio_bus_resource = {
+	.start = GIO_SLOT_GFX_BASE,
+	.end   = GIO_SLOT_GFX_BASE + 0x9fffff,
+	.name  = "GIO Bus",
+	.flags = IORESOURCE_MEM,
+};
+
+int __init ip22_gio_init(void)
+{
+	unsigned int pbdma __maybe_unused;
+	int ret;
+
+	ret = device_register(&gio_bus);
+	if (ret)
+		return ret;
+
+	ret = bus_register(&gio_bus_type);
+	if (!ret) {
+		request_resource(&iomem_resource, &gio_bus_resource);
+		printk(KERN_INFO "GIO: Probing bus...\n");
+
+		if (ip22_is_fullhouse() ||
+		    !get_dbe(pbdma, (unsigned int *)&hpc3c1->pbdma[1])) {
+			/* Indigo2 and ChallengeS */
+			ip22_check_gio(0, GIO_SLOT_GFX_BASE);
+			ip22_check_gio(1, GIO_SLOT_EXP0_BASE);
+		} else {
+			/* Indy */
+			ip22_check_gio(0, GIO_SLOT_GFX_BASE);
+			ip22_check_gio(1, GIO_SLOT_EXP0_BASE);
+			ip22_check_gio(2, GIO_SLOT_EXP1_BASE);
+		}
+	} else
+		device_unregister(&gio_bus);
+
+	return ret;
+}
+
+subsys_initcall(ip22_gio_init);
+
diff --git a/arch/mips/sgi-ip22/ip22-mc.c b/arch/mips/sgi-ip22/ip22-mc.c
index d22262e..75ada8a 100644
--- a/arch/mips/sgi-ip22/ip22-mc.c
+++ b/arch/mips/sgi-ip22/ip22-mc.c
@@ -139,11 +139,11 @@ void __init sgimc_init(void)
 	 *         zero.
 	 */
 	/* don't touch parity settings for IP28 */
-#ifndef CONFIG_SGI_IP28
 	tmp = sgimc->cpuctrl0;
-	tmp |= (SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM |
-		SGIMC_CCTRL0_R4KNOCHKPARR);
+#ifndef CONFIG_SGI_IP28
+	tmp |= SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM;
 #endif
+	tmp |= SGIMC_CCTRL0_R4KNOCHKPARR;
 	sgimc->cpuctrl0 = tmp;
 
 	/* Step 3: Setup the MC write buffer depth, this is controlled
@@ -178,7 +178,8 @@ void __init sgimc_init(void)
 	 */
 
 	/* First the basic invariants across all GIO64 implementations. */
-	tmp = SGIMC_GIOPAR_HPC64;	/* All 1st HPC's interface at 64bits */
+	tmp = sgimc->giopar & SGIMC_GIOPAR_GFX64; /* keep gfx 64bit settings */
+	tmp |= SGIMC_GIOPAR_HPC64;	/* All 1st HPC's interface at 64bits */
 	tmp |= SGIMC_GIOPAR_ONEBUS;	/* Only one physical GIO bus exists */
 
 	if (ip22_is_fullhouse()) {
@@ -193,7 +194,6 @@ void __init sgimc_init(void)
 			tmp |= SGIMC_GIOPAR_PLINEEXP0;	/* exp[01] pipelined */
 			tmp |= SGIMC_GIOPAR_PLINEEXP1;
 			tmp |= SGIMC_GIOPAR_MASTEREISA;	/* EISA masters */
-			tmp |= SGIMC_GIOPAR_GFX64;	/* GFX at 64 bits */
 		}
 	} else {
 		/* Guiness specific settings. */
diff --git a/arch/mips/sgi-ip22/ip22-setup.c b/arch/mips/sgi-ip22/ip22-setup.c
index 5e66213..c7bdfe4 100644
--- a/arch/mips/sgi-ip22/ip22-setup.c
+++ b/arch/mips/sgi-ip22/ip22-setup.c
@@ -26,9 +26,6 @@
 #include <asm/sgi/hpc3.h>
 #include <asm/sgi/ip22.h>
 
-unsigned long sgi_gfxaddr;
-EXPORT_SYMBOL_GPL(sgi_gfxaddr);
-
 extern void ip22_be_init(void) __init;
 
 void __init plat_mem_setup(void)
@@ -78,22 +75,4 @@ void __init plat_mem_setup(void)
 		prom_flags |= PROM_FLAG_USE_AS_CONSOLE;
 		add_preferred_console("arc", 0, NULL);
 	}
-
-#if defined(CONFIG_VT) && defined(CONFIG_SGI_NEWPORT_CONSOLE)
-	{
-		ULONG *gfxinfo;
-		ULONG * (*__vec)(void) = (void *) (long)
-			*((_PULONG *)(long)((PROMBLOCK)->pvector + 0x20));
-
-		gfxinfo = __vec();
-		sgi_gfxaddr = ((gfxinfo[1] >= 0xa0000000
-			       && gfxinfo[1] <= 0xc0000000)
-			       ? gfxinfo[1] - 0xa0000000 : 0);
-
-		/* newport addresses? */
-		if (sgi_gfxaddr = 0x1f0f0000 || sgi_gfxaddr = 0x1f4f0000) {
-			conswitchp = &newport_con;
-		}
-	}
-#endif
 }
diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c
index 93317b5..a122d92 100644
--- a/drivers/video/console/newport_con.c
+++ b/drivers/video/console/newport_con.c
@@ -25,14 +25,13 @@
 #include <asm/system.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
+#include <asm/gio_device.h>
+
 #include <video/newport.h>
 
 #include <linux/linux_logo.h>
 #include <linux/font.h>
 
-
-extern unsigned long sgi_gfxaddr;
-
 #define FONT_DATA ((unsigned char *)font_vga_8x16.data)
 
 /* borrowed from fbcon.c */
@@ -304,12 +303,6 @@ static const char *newport_startup(void)
 {
 	int i;
 
-	if (!sgi_gfxaddr)
-		return NULL;
-
-	if (!npregs)
-		npregs = (struct newport_regs *)/* ioremap cannot fail */
-			ioremap(sgi_gfxaddr, sizeof(struct newport_regs));
 	npregs->cset.config = NPORT_CFG_GD0;
 
 	if (newport_wait(npregs))
@@ -743,26 +736,58 @@ const struct consw newport_con = {
 	.con_save_screen  = DUMMY
 };
 
-#ifdef MODULE
-static int __init newport_console_init(void)
+static int newport_probe(struct gio_device *dev,
+			 const struct gio_device_id *id)
 {
-	if (!sgi_gfxaddr)
-		return 0;
+	unsigned long newport_addr;
 
-	if (!npregs)
-		npregs = (struct newport_regs *)/* ioremap cannot fail */
-			ioremap(sgi_gfxaddr, sizeof(struct newport_regs));
+	if (!dev->resource.start)
+		return -EINVAL;
+
+	if (npregs)
+		return -EBUSY; /* we only support one Newport as console */
+
+	newport_addr = dev->resource.start + 0xF0000;
+	if (!request_mem_region(newport_addr, 0x10000, "Newport"))
+		return -ENODEV;
+
+	npregs = (struct newport_regs *)/* ioremap cannot fail */
+		ioremap(newport_addr, sizeof(struct newport_regs));
 
 	return take_over_console(&newport_con, 0, MAX_NR_CONSOLES - 1, 1);
 }
-module_init(newport_console_init);
 
-static void __exit newport_console_exit(void)
+static void newport_remove(struct gio_device *dev)
 {
 	give_up_console(&newport_con);
 	iounmap((void *)npregs);
 }
+
+static struct gio_device_id newport_ids[] = {
+	{ .id = 0x7e },
+	{ .id = 0xff }
+};
+
+MODULE_ALIAS("gio:7e");
+
+static struct gio_driver newport_driver = {
+	.name = "newport",
+	.id_table = newport_ids,
+	.probe = newport_probe,
+	.remove = newport_remove,
+};
+
+int __init newport_console_init(void)
+{
+	return gio_register_driver(&newport_driver);
+}
+
+void __exit newport_console_exit(void)
+{
+	gio_unregister_driver(&newport_driver);
+}
+
+module_init(newport_console_init);
 module_exit(newport_console_exit);
-#endif
 
 MODULE_LICENSE("GPL");

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox