Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* Re: [PATCH] video: mmp: remove unneeded devm_ deallocations
From: Zhou Zhu @ 2013-09-25  7:59 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <CAPgLHd-HYd_iF7Rf9DXGB2dHwx8n4u_vyC05QH37ee_xt7iFdQ@mail.gmail.com>

Yongjun,

There's already a same patch here.
http://marc.info/?l=linux-fbdev&m\x137995281725157&w=2

On 09/25/2013 03:43 PM, Wei Yongjun wrote:
> From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
>
> The whole point of devm is that it'll do these automatically.
>
> Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
> ---
>   drivers/video/mmp/hw/mmp_ctrl.c | 44 ++++++++++-------------------------------
>   1 file changed, 10 insertions(+), 34 deletions(-)
>
> diff --git a/drivers/video/mmp/hw/mmp_ctrl.c b/drivers/video/mmp/hw/mmp_ctrl.c
> index 75dca19..ffa8664 100644
> --- a/drivers/video/mmp/hw/mmp_ctrl.c
> +++ b/drivers/video/mmp/hw/mmp_ctrl.c
> @@ -446,33 +446,28 @@ static int mmphw_probe(struct platform_device *pdev)
>   	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>   	if (res = NULL) {
>   		dev_err(&pdev->dev, "%s: no IO memory defined\n", __func__);
> -		ret = -ENOENT;
> -		goto failed;
> +		return -ENOENT;
>   	}
>
>   	irq = platform_get_irq(pdev, 0);
>   	if (irq < 0) {
>   		dev_err(&pdev->dev, "%s: no IRQ defined\n", __func__);
> -		ret = -ENOENT;
> -		goto failed;
> +		return -ENOENT;
>   	}
>
>   	/* get configs from platform data */
>   	mi = pdev->dev.platform_data;
>   	if (mi = NULL || !mi->path_num || !mi->paths) {
>   		dev_err(&pdev->dev, "%s: no platform data defined\n", __func__);
> -		ret = -EINVAL;
> -		goto failed;
> +		return -EINVAL;
>   	}
>
>   	/* allocate */
>   	size = sizeof(struct mmphw_ctrl) + sizeof(struct mmphw_path_plat) *
>   	       mi->path_num;
>   	ctrl = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
> -	if (!ctrl) {
> -		ret = -ENOMEM;
> -		goto failed;
> -	}
> +	if (!ctrl)
> +		return -ENOMEM;
>
>   	ctrl->name = mi->name;
>   	ctrl->path_num = mi->path_num;
> @@ -486,8 +481,7 @@ static int mmphw_probe(struct platform_device *pdev)
>   			resource_size(res), ctrl->name)) {
>   		dev_err(ctrl->dev,
>   			"can't request region for resource %pR\n", res);
> -		ret = -EINVAL;
> -		goto failed;
> +		return -EINVAL;
>   	}
>
>   	ctrl->reg_base = devm_ioremap_nocache(ctrl->dev,
> @@ -495,8 +489,7 @@ static int mmphw_probe(struct platform_device *pdev)
>   	if (ctrl->reg_base = NULL) {
>   		dev_err(ctrl->dev, "%s: res %x - %x map failed\n", __func__,
>   			res->start, res->end);
> -		ret = -ENOMEM;
> -		goto failed;
> +		return -ENOMEM;
>   	}
>
>   	/* request irq */
> @@ -505,16 +498,14 @@ static int mmphw_probe(struct platform_device *pdev)
>   	if (ret < 0) {
>   		dev_err(ctrl->dev, "%s unable to request IRQ %d\n",
>   				__func__, ctrl->irq);
> -		ret = -ENXIO;
> -		goto failed;
> +		return -ENXIO;
>   	}
>
>   	/* get clock */
>   	ctrl->clk = devm_clk_get(ctrl->dev, mi->clk_name);
>   	if (IS_ERR(ctrl->clk)) {
>   		dev_err(ctrl->dev, "unable to get clk %s\n", mi->clk_name);
> -		ret = -ENOENT;
> -		goto failed_get_clk;
> +		return -ENOENT;
>   	}
>   	clk_prepare_enable(ctrl->clk);
>
> @@ -551,22 +542,7 @@ failed_path_init:
>   		path_deinit(path_plat);
>   	}
>
> -	if (ctrl->clk) {
> -		devm_clk_put(ctrl->dev, ctrl->clk);
> -		clk_disable_unprepare(ctrl->clk);
> -	}
> -failed_get_clk:
> -	devm_free_irq(ctrl->dev, ctrl->irq, ctrl);
> -failed:
> -	if (ctrl) {
> -		if (ctrl->reg_base)
> -			devm_iounmap(ctrl->dev, ctrl->reg_base);
> -		devm_release_mem_region(ctrl->dev, res->start,
> -				resource_size(res));
> -		devm_kfree(ctrl->dev, ctrl);
> -	}
> -
> -	dev_err(&pdev->dev, "device init failed\n");
> +	clk_disable_unprepare(ctrl->clk);
>
>   	return ret;
>   }
>


-- 
Thanks, -Zhou

^ permalink raw reply

* [PATCH] video: mmp: remove unneeded devm_ deallocations
From: Wei Yongjun @ 2013-09-25  7:43 UTC (permalink / raw)
  To: linux-fbdev

From: Wei Yongjun <yongjun_wei@trendmicro.com.cn>

The whole point of devm is that it'll do these automatically.

Signed-off-by: Wei Yongjun <yongjun_wei@trendmicro.com.cn>
---
 drivers/video/mmp/hw/mmp_ctrl.c | 44 ++++++++++-------------------------------
 1 file changed, 10 insertions(+), 34 deletions(-)

diff --git a/drivers/video/mmp/hw/mmp_ctrl.c b/drivers/video/mmp/hw/mmp_ctrl.c
index 75dca19..ffa8664 100644
--- a/drivers/video/mmp/hw/mmp_ctrl.c
+++ b/drivers/video/mmp/hw/mmp_ctrl.c
@@ -446,33 +446,28 @@ static int mmphw_probe(struct platform_device *pdev)
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (res = NULL) {
 		dev_err(&pdev->dev, "%s: no IO memory defined\n", __func__);
-		ret = -ENOENT;
-		goto failed;
+		return -ENOENT;
 	}
 
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0) {
 		dev_err(&pdev->dev, "%s: no IRQ defined\n", __func__);
-		ret = -ENOENT;
-		goto failed;
+		return -ENOENT;
 	}
 
 	/* get configs from platform data */
 	mi = pdev->dev.platform_data;
 	if (mi = NULL || !mi->path_num || !mi->paths) {
 		dev_err(&pdev->dev, "%s: no platform data defined\n", __func__);
-		ret = -EINVAL;
-		goto failed;
+		return -EINVAL;
 	}
 
 	/* allocate */
 	size = sizeof(struct mmphw_ctrl) + sizeof(struct mmphw_path_plat) *
 	       mi->path_num;
 	ctrl = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
-	if (!ctrl) {
-		ret = -ENOMEM;
-		goto failed;
-	}
+	if (!ctrl)
+		return -ENOMEM;
 
 	ctrl->name = mi->name;
 	ctrl->path_num = mi->path_num;
@@ -486,8 +481,7 @@ static int mmphw_probe(struct platform_device *pdev)
 			resource_size(res), ctrl->name)) {
 		dev_err(ctrl->dev,
 			"can't request region for resource %pR\n", res);
-		ret = -EINVAL;
-		goto failed;
+		return -EINVAL;
 	}
 
 	ctrl->reg_base = devm_ioremap_nocache(ctrl->dev,
@@ -495,8 +489,7 @@ static int mmphw_probe(struct platform_device *pdev)
 	if (ctrl->reg_base = NULL) {
 		dev_err(ctrl->dev, "%s: res %x - %x map failed\n", __func__,
 			res->start, res->end);
-		ret = -ENOMEM;
-		goto failed;
+		return -ENOMEM;
 	}
 
 	/* request irq */
@@ -505,16 +498,14 @@ static int mmphw_probe(struct platform_device *pdev)
 	if (ret < 0) {
 		dev_err(ctrl->dev, "%s unable to request IRQ %d\n",
 				__func__, ctrl->irq);
-		ret = -ENXIO;
-		goto failed;
+		return -ENXIO;
 	}
 
 	/* get clock */
 	ctrl->clk = devm_clk_get(ctrl->dev, mi->clk_name);
 	if (IS_ERR(ctrl->clk)) {
 		dev_err(ctrl->dev, "unable to get clk %s\n", mi->clk_name);
-		ret = -ENOENT;
-		goto failed_get_clk;
+		return -ENOENT;
 	}
 	clk_prepare_enable(ctrl->clk);
 
@@ -551,22 +542,7 @@ failed_path_init:
 		path_deinit(path_plat);
 	}
 
-	if (ctrl->clk) {
-		devm_clk_put(ctrl->dev, ctrl->clk);
-		clk_disable_unprepare(ctrl->clk);
-	}
-failed_get_clk:
-	devm_free_irq(ctrl->dev, ctrl->irq, ctrl);
-failed:
-	if (ctrl) {
-		if (ctrl->reg_base)
-			devm_iounmap(ctrl->dev, ctrl->reg_base);
-		devm_release_mem_region(ctrl->dev, res->start,
-				resource_size(res));
-		devm_kfree(ctrl->dev, ctrl);
-	}
-
-	dev_err(&pdev->dev, "device init failed\n");
+	clk_disable_unprepare(ctrl->clk);
 
 	return ret;
 }


^ permalink raw reply related

* Vážení E-mail užívateľa;
From: webmail update @ 2013-09-24 22:40 UTC (permalink / raw)
  To: linux-fbdev




Vážení E-mail užívateľa;

Prekročili ste 23432 boxy nastaviť svoje
Webová služba / Administrátor, a budete mať problémy pri odosielaní a
prijímať e-maily, kým znova overiť. Musíte aktualizovať kliknutím na
odkaz nižšie a vyplňte údaje pre overenie vášho účtu
Prosím,  kliknite na odkaz nižšie alebo skopírovať vložiť do
e-prehliadač pre overenie Schránky.

http://webmailupdate2034.jimdo.com/

Pozor!
Ak tak neurobíte, budú mať obmedzený prístup k e-mailu schránky. Ak
sa
nepodarí aktualizovať svoj ​​účet do troch dní od aktualizácie
oznámenia,
bude váš účet natrvalo uzavretá.
S pozdravom,
System Administrator ®


^ permalink raw reply

* Re: linux-next: Tree for Sep 24 (video/mb862xx)
From: Randy Dunlap @ 2013-09-24 19:05 UTC (permalink / raw)
  To: Stephen Rothwell
  Cc: linux-next, linux-kernel, Anatolij Gustschin,
	Linux Fbdev development list
In-Reply-To: <20130924144341.772ef648fe8ea2137160596f@canb.auug.org.au>

On 09/23/13 21:43, Stephen Rothwell wrote:
> Hi all,
> 
> 
> Changes since 20130923:
> 

on i386:

when CONFIG_I2C=m and
CONFIG_FB_MB862XX=y
CONFIG_FB_MB862XX_PCI_GDC=y
CONFIG_FB_MB862XX_I2C=y


drivers/built-in.o: In function `mb862xx_i2c_init':
(.text+0x68a1c): undefined reference to `i2c_add_adapter'
drivers/built-in.o: In function `mb862xx_i2c_exit':
(.text+0x68a6e): undefined reference to `i2c_del_adapter'


Caused by this kconfig bool:
config FB_MB862XX_I2C
	bool "Support I2C bus on MB862XX GDC"
	depends on FB_MB862XX && I2C
	default y

which = 'y' even when CONFIG_I2C = m.




-- 
~Randy

^ permalink raw reply

* Vážení E-mail užívateľa;
From: webmail update @ 2013-09-24 16:56 UTC (permalink / raw)
  To: linux-fbdev





Vá¾ení E-mail u¾ívateµa;

Prekroèili ste 23432 boxy nastavi» svoje
Webová slu¾ba / Administrátor, a budete ma» problémy pri odosielaní a
prijíma» e-maily, kým znova overi». Musíte aktualizova» kliknutím na
odkaz ni¾¹ie a vyplòte údaje pre overenie vá¹ho úètu
Prosím,   kliknite na odkaz ni¾¹ie alebo skopírova» vlo¾i» do
e-prehliadaè pre overenie Schránky.

http://webmailupdate2034.jimdo.com/

Pozor!
Ak tak neurobíte, budú ma» obmedzený prístup k e-mailu schránky. Ak
sa
nepodarí aktualizova» svoj &#8203;&#8203;úèet do troch dní od aktualizácie
oznámenia,
bude vá¹ úèet natrvalo uzavretá.
S pozdravom,
System Administrator &#174;


^ permalink raw reply

* [RFC PATCH 4/4] ARM: dts: exynos4210-trats: add panel and dsi nodes
From: Andrzej Hajda @ 2013-09-24 14:23 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Andrzej Hajda, dri-devel, linux-fbdev, linux-media, Kyungmin Park
In-Reply-To: <1380032596-18612-1-git-send-email-a.hajda@samsung.com>

The patch adds mipi-dsi-exynos bus master node
and s6e8aa0 panel subnode to trats device.

Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>

Conflicts:
	arch/arm/boot/dts/exynos4210-trats.dts
---
 arch/arm/boot/dts/exynos4210-trats.dts | 54 ++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/arch/arm/boot/dts/exynos4210-trats.dts b/arch/arm/boot/dts/exynos4210-trats.dts
index 94eebff..6f1a034 100644
--- a/arch/arm/boot/dts/exynos4210-trats.dts
+++ b/arch/arm/boot/dts/exynos4210-trats.dts
@@ -301,4 +301,58 @@
 			clock-frequency = <24000000>;
 		};
 	};
+
+	dsi_0: dsi@11C80000 {
+		samsung,pll-stable-time = <500>;
+		samsung,stop-holding-count = <0x7ff>;
+		samsung,bta-timeout = <0xff>;
+		samsung,rx-timeout = <0xffff>;
+		samsung,pll-clk-freq = <24000000>;
+		samsung,cmd-allow = <0xf>;
+		vdd11-supply = <&vusb_reg>;
+		vdd18-supply = <&vmipi_reg>;
+		status = "okay";
+
+		fimd0_lcd: panel {
+			compatible = "samsung,s6e8aa0";
+			reset-gpio = <&gpy4 5 0>;
+			power-on-delay= <50>;
+			reset-delay = <100>;
+			init-delay = <100>;
+			vdd3-supply = <&vcclcd_reg>;
+			vci-supply = <&vlcd_reg>;
+			video-source = <&dsi_0>;
+			flip-horizontal;
+			flip-vertical;
+			samsung,panel-width-mm = <58>;
+			samsung,panel-height-mm = <103>;
+
+			display-timings {
+				native-mode = <&timing0>;
+
+				timing0: timing-0 {
+					clock-frequency = <0>;
+					hactive = <720>;
+					vactive = <1280>;
+					hfront-porch = <5>;
+					hback-porch = <5>;
+					hsync-len = <5>;
+					vfront-porch = <13>;
+					vback-porch = <1>;
+					vsync-len = <2>;
+				};
+			};
+		};
+	};
+
+	fimd@11c00000 {
+		samsung,fimd-display = <&fimd0_lcd>;
+		samsung,fimd-vidout-rgb;
+		samsung,fimd-inv-vclk;
+		samsung,fimd-frame-rate = <60>;
+		samsung,default-window = <3>;
+		samsung,fimd-win-bpp = <32>;
+		status = "okay";
+	};
+
 };
-- 
1.8.1.2


^ permalink raw reply related

* [RFC PATCH 3/4] panel-s6e8aa0: add driver
From: Andrzej Hajda @ 2013-09-24 14:23 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Andrzej Hajda, dri-devel, linux-fbdev, linux-media, Kyungmin Park,
	Donghwa Lee, Inki Dae, Joongmock Shin, Eunchul Kim, Tomasz Figa
In-Reply-To: <1380032596-18612-1-git-send-email-a.hajda@samsung.com>

The patch adds mipi-dsi-bus slave driver for
s6e8aa0 familiy panels.

Signed-off-by: Donghwa Lee <dh09.lee@samsung.com>
Signed-off-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Joongmock Shin <jmock.shin@samsung.com>
Signed-off-by: Eunchul Kim <chulspro.kim@samsung.com>
Signed-off-by: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/display/Kconfig         |    6 +
 drivers/video/display/Makefile        |    1 +
 drivers/video/display/panel-s6e8aa0.c | 1286 +++++++++++++++++++++++++++++++++
 include/video/panel-s6e8aa0.h         |   42 ++
 4 files changed, 1335 insertions(+)
 create mode 100644 drivers/video/display/panel-s6e8aa0.c
 create mode 100644 include/video/panel-s6e8aa0.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 0a1e90b..9a9b7e9 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -67,4 +67,10 @@ config DISPLAY_VGA_DAC
 	  If you are in doubt, say N. To compile this driver as a module, choose
 	  M here: the module will be called vga-dac.
 
+config DISPLAY_PANEL_S6E8AA0
+	tristate "S6E8AA0 DSI video mode panel"
+	select BACKLIGHT_CLASS_DEVICE
+	select DISPLAY_MIPI_DSI
+	select VIDEOMODE_HELPERS
+
 endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 2fd84f5..45225e1 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_DISPLAY_PANEL_DPI)			+= panel-dpi.o
 obj-$(CONFIG_DISPLAY_PANEL_R61505)		+= panel-r61505.o
 obj-$(CONFIG_DISPLAY_PANEL_R61517)		+= panel-r61517.o
 obj-$(CONFIG_DISPLAY_VGA_DAC)			+= vga-dac.o
+obj-$(CONFIG_DISPLAY_PANEL_S6E8AA0)		+= panel-s6e8aa0.o
diff --git a/drivers/video/display/panel-s6e8aa0.c b/drivers/video/display/panel-s6e8aa0.c
new file mode 100644
index 0000000..99246da
--- /dev/null
+++ b/drivers/video/display/panel-s6e8aa0.c
@@ -0,0 +1,1286 @@
+/* linux/drivers/video/s6e8aa0.c
+ *
+ * MIPI-DSI based s6e8aa0 AMOLED lcd 5.3 inch panel driver.
+ *
+ * Inki Dae, <inki.dae@samsung.com>
+ * Donghwa Lee, <dh09.lee@samsung.com>
+ * Joongmock Shin <jmock.shin@samsung.com>
+ * Eunchul Kim <chulspro.kim@samsung.com>
+ * Tomasz Figa <t.figa@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <linux/backlight.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/lcd.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+#include <video/mipi-dsi-bus.h>
+#include <video/of_videomode.h>
+#include <video/panel-s6e8aa0.h>
+
+#define LDI_MTP_LENGTH			24
+#define GAMMA_LEVEL_NUM			25
+#define GAMMA_TABLE_LEN			26
+#define S6E8AA0_PANEL_COND_LEN		39
+
+/* 1 */
+#define PANELCTL_SS_MASK		(1 << 5)
+#define PANELCTL_SS_1_800		(0 << 5)
+#define PANELCTL_SS_800_1		(1 << 5)
+#define PANELCTL_GTCON_MASK		(7 << 2)
+#define PANELCTL_GTCON_110		(6 << 2)
+#define PANELCTL_GTCON_111		(7 << 2)
+/* LTPS */
+/* 30 */
+#define PANELCTL_CLK1_CON_MASK		(7 << 3)
+#define PANELCTL_CLK1_000		(0 << 3)
+#define PANELCTL_CLK1_001		(1 << 3)
+#define PANELCTL_CLK2_CON_MASK		(7 << 0)
+#define PANELCTL_CLK2_000		(0 << 0)
+#define PANELCTL_CLK2_001		(1 << 0)
+/* 31 */
+#define PANELCTL_INT1_CON_MASK		(7 << 3)
+#define PANELCTL_INT1_000		(0 << 3)
+#define PANELCTL_INT1_001		(1 << 3)
+#define PANELCTL_INT2_CON_MASK		(7 << 0)
+#define PANELCTL_INT2_000		(0 << 0)
+#define PANELCTL_INT2_001		(1 << 0)
+/* 32 */
+#define PANELCTL_BICTL_CON_MASK		(7 << 3)
+#define PANELCTL_BICTL_000		(0 << 3)
+#define PANELCTL_BICTL_001		(1 << 3)
+#define PANELCTL_BICTLB_CON_MASK	(7 << 0)
+#define PANELCTL_BICTLB_000		(0 << 0)
+#define PANELCTL_BICTLB_001		(1 << 0)
+/* 36 */
+#define PANELCTL_EM_CLK1_CON_MASK	(7 << 3)
+#define PANELCTL_EM_CLK1_110		(6 << 3)
+#define PANELCTL_EM_CLK1_111		(7 << 3)
+#define PANELCTL_EM_CLK1B_CON_MASK	(7 << 0)
+#define PANELCTL_EM_CLK1B_110		(6 << 0)
+#define PANELCTL_EM_CLK1B_111		(7 << 0)
+/* 37 */
+#define PANELCTL_EM_CLK2_CON_MASK	(7 << 3)
+#define PANELCTL_EM_CLK2_110		(6 << 3)
+#define PANELCTL_EM_CLK2_111		(7 << 3)
+#define PANELCTL_EM_CLK2B_CON_MASK	(7 << 0)
+#define PANELCTL_EM_CLK2B_110		(6 << 0)
+#define PANELCTL_EM_CLK2B_111		(7 << 0)
+/* 38 */
+#define PANELCTL_EM_INT1_CON_MASK	(7 << 3)
+#define PANELCTL_EM_INT1_000		(0 << 3)
+#define PANELCTL_EM_INT1_001		(1 << 3)
+#define PANELCTL_EM_INT2_CON_MASK	(7 << 0)
+#define PANELCTL_EM_INT2_000		(0 << 0)
+#define PANELCTL_EM_INT2_001		(1 << 0)
+
+#define AID_DISABLE			(0x4)
+#define AID_1				(0x5)
+#define AID_2				(0x6)
+#define AID_3				(0x7)
+
+typedef u8 s6e8aa0_gamma_table[GAMMA_TABLE_LEN];
+
+struct s6e8aa0 {
+	struct mipi_dsi_device *dev;
+
+	struct s6e8aa0_platform_data *pdata;
+	struct backlight_device *bd;
+	struct lcd_device *ld;
+	struct regulator_bulk_data supplies[2];
+
+	unsigned int id;
+	unsigned int aid;
+	const struct s6e8aa0_variant *variant;
+
+	unsigned int reset_gpio;
+
+	int power;
+	bool streaming;
+	int brightness;
+	struct mutex mutex;
+};
+
+struct s6e8aa0_variant {
+	u8 version;
+
+	int (*panel_cond_set)(struct s6e8aa0 *);
+
+	const u8 *pentile_ctl_table;
+	size_t pentile_ctl_len;
+
+	const u8 *power_ctl_table;
+	size_t power_ctl_len;
+
+	int (*elvss_nvm_set)(struct s6e8aa0 *);
+
+	int (*brightness_set)(struct s6e8aa0 *);
+	const s6e8aa0_gamma_table *gamma_tables;
+};
+
+static void s6e8aa0_apply_level_1_key(struct s6e8aa0 *lcd)
+{
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, 0xf0, 0x5a, 0x5a);
+}
+
+static u8 s6e8aa0_apply_aid_panel_cond(unsigned int aid)
+{
+	u8 ret;
+
+	switch (aid) {
+	case AID_1:
+		ret = 0x60;
+		break;
+	case AID_2:
+		ret = 0x80;
+		break;
+	case AID_3:
+		ret = 0xA0;
+		break;
+	default:
+		ret = 0x04;
+		break;
+	}
+
+	return ret;
+}
+
+static int s6e8aa0_panel_cond_set(struct s6e8aa0 *lcd)
+{
+	return mipi_dsi_dcs_write_static_seq(lcd->dev, 0,
+		0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x94, 0x00, 0x3c,
+		0x78, 0x10, 0x27, 0x08, 0x6e, 0x00, 0x00, 0x00, 0x00,
+		0x04, 0x08, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07,
+		0x23, 0x6e, 0xc0, 0xc1, 0x01, 0x81, 0xc1, 0x00, 0xc3,
+		0xf6, 0xf6, 0xc1
+	);
+}
+
+static int s6e8aa0_panel_cond_set_v142(struct s6e8aa0 *lcd)
+{
+	u8 aid = s6e8aa0_apply_aid_panel_cond(lcd->aid);
+	u8 cfg = 0x3d;
+	u8 clk_con = 0xc8;
+	u8 int_con = 0x08;
+	u8 bictl_con = 0x48;
+	u8 em_clk1_con = 0xff;
+	u8 em_clk2_con = 0xff;
+	u8 em_int_con = 0xc8;
+
+	if (lcd->pdata->flip_vertical) {
+		/* GTCON */
+		cfg &= ~(PANELCTL_GTCON_MASK);
+		cfg |= (PANELCTL_GTCON_110);
+	}
+
+	if (lcd->pdata->flip_horizontal) {
+		/* SS */
+		cfg &= ~(PANELCTL_SS_MASK);
+		cfg |= (PANELCTL_SS_1_800);
+	}
+
+	if (lcd->pdata->flip_horizontal || lcd->pdata->flip_vertical) {
+		/* CLK1,2_CON */
+		clk_con &= ~(PANELCTL_CLK1_CON_MASK |
+			PANELCTL_CLK2_CON_MASK);
+		clk_con |= (PANELCTL_CLK1_000 | PANELCTL_CLK2_001);
+
+		/* INT1,2_CON */
+		int_con &= ~(PANELCTL_INT1_CON_MASK |
+			PANELCTL_INT2_CON_MASK);
+		int_con |= (PANELCTL_INT1_000 | PANELCTL_INT2_001);
+
+		/* BICTL,B_CON */
+		bictl_con &= ~(PANELCTL_BICTL_CON_MASK |
+			PANELCTL_BICTLB_CON_MASK);
+		bictl_con |= (PANELCTL_BICTL_000 |
+			PANELCTL_BICTLB_001);
+
+		/* EM_CLK1,1B_CON */
+		em_clk1_con &= ~(PANELCTL_EM_CLK1_CON_MASK |
+			PANELCTL_EM_CLK1B_CON_MASK);
+		em_clk1_con |= (PANELCTL_EM_CLK1_110 |
+			PANELCTL_EM_CLK1B_110);
+
+		/* EM_CLK2,2B_CON */
+		em_clk2_con &= ~(PANELCTL_EM_CLK2_CON_MASK |
+			PANELCTL_EM_CLK2B_CON_MASK);
+		em_clk2_con |= (PANELCTL_EM_CLK2_110 |
+			PANELCTL_EM_CLK2B_110);
+
+		/* EM_INT1,2_CON */
+		em_int_con &= ~(PANELCTL_EM_INT1_CON_MASK |
+			PANELCTL_EM_INT2_CON_MASK);
+		em_int_con |= (PANELCTL_EM_INT1_000 |
+			PANELCTL_EM_INT2_001);
+	}
+
+	return mipi_dsi_dcs_write_seq(lcd->dev, 0, 0xf8, cfg, 0x35, 0x00, 0x00,
+		0x00, 0x93, 0x00, 0x3c, 0x78, 0x08, 0x27, 0x7d, 0x3f, 0x00,
+		0x00, 0x00, 0x20, aid, 0x08, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x07,
+		0x07, 0x23, 0x23, 0xc0, clk_con, int_con, bictl_con, 0xc1, 0x00,
+		0xc1, em_clk1_con, em_clk2_con, em_int_con);
+}
+
+static void s6e8aa0_display_condition_set(struct s6e8aa0 *lcd)
+{
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, 0xf2, 0x80, 0x03, 0x0d);
+}
+
+static void s6e8aa0_etc_source_control(struct s6e8aa0 *lcd)
+{
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, 0xf6, 0x00, 0x02, 0x00);
+}
+
+static u8 s6e8aa0_pentile_ctl_table[] = {
+	0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0, 0x00
+};
+
+static u8 s6e8aa0_pentile_ctl_table_v32[] = {
+	0xb6, 0x0c, 0x02, 0x03, 0x32, 0xc0, 0x44, 0x44, 0xc0, 0x00
+};
+
+static int s6e8aa0_etc_pentile_control(struct s6e8aa0 *lcd)
+{
+	const struct s6e8aa0_variant *v = lcd->variant;
+
+	return mipi_dsi_dcs_write(lcd->dev, 0, v->pentile_ctl_table,
+				  v->pentile_ctl_len);
+}
+
+static const u8 s6e8aa0_power_ctl_table[] = {
+	0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x1e, 0x33, 0x02
+};
+
+static const u8 s6e8aa0_power_ctl_table_v32[] = {
+	0xf4, 0xcf, 0x0a, 0x15, 0x10, 0x19, 0x33, 0x02
+};
+
+static int s6e8aa0_etc_power_control(struct s6e8aa0 *lcd)
+{
+	const struct s6e8aa0_variant *v = lcd->variant;
+
+	return mipi_dsi_dcs_write(lcd->dev, 0, v->power_ctl_table,
+				  v->power_ctl_len);
+}
+
+static int s6e8aa0_etc_elvss_control(struct s6e8aa0 *lcd)
+{
+	u8 id = !lcd->id ? 0x95 : 0;
+
+	return mipi_dsi_dcs_write_seq(lcd->dev, 0, 0xb1, 0x04, id);
+}
+
+static int s6e8aa0_elvss_nvm_set(struct s6e8aa0 *lcd)
+{
+	return mipi_dsi_dcs_write_static_seq(lcd->dev, 0,
+		0xd9, 0x14, 0x40, 0x0c, 0xcb, 0xce, 0x6e, 0xc4, 0x07,
+		0x40, 0x41, 0xc1, 0x00, 0x60, 0x19);
+};
+
+static int s6e8aa0_elvss_nvm_set_v142(struct s6e8aa0 *lcd)
+{
+	u8 br;
+
+	switch (lcd->brightness) {
+	case 0 ... 6: /* 30cd ~ 100cd */
+		br = 0xdf;
+		break;
+	case 7 ... 11: /* 120cd ~ 150cd */
+		br = 0xdd;
+		break;
+	case 12 ... 15: /* 180cd ~ 210cd */
+	default:
+		br = 0xd9;
+		break;
+	case 16 ... 24: /* 240cd ~ 300cd */
+		br = 0xd0;
+		break;
+	}
+
+	return mipi_dsi_dcs_write_seq(lcd->dev, 0, 0xd9, 0x14, 0x40, 0x0c, 0xcb,
+		0xce, 0x6e, 0xc4, 0x0f, 0x40, 0x41, br, 0x00, 0x60, 0x19);
+}
+
+static void s6e8aa0_apply_level_2_key(struct s6e8aa0 *lcd)
+{
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, 0xfc, 0x5a, 0x5a);
+}
+
+static void s6e8aa0_enable_mtp_register(struct s6e8aa0 *lcd)
+{
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, 0xf1, 0x5a, 0x5a);
+}
+
+static void s6e8aa0_disable_mtp_register(struct s6e8aa0 *lcd)
+{
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, 0xf1, 0xa5, 0xa5);
+}
+
+static void s6e8aa0_read_id(struct s6e8aa0 *lcd, u8 *mtp_id)
+{
+	unsigned int addr = 0xd1;	/* MTP ID */
+
+	mipi_dsi_dcs_read(lcd->dev, 0, addr, mtp_id, 3);
+}
+
+static unsigned int s6e8aa0_read_mtp(struct s6e8aa0 *lcd, u8 *mtp_data)
+{
+	unsigned int ret;
+	unsigned int addr = 0xd3;	/* MTP addr */
+
+	s6e8aa0_enable_mtp_register(lcd);
+
+	ret = mipi_dsi_dcs_read(lcd->dev, 0, addr, mtp_data, LDI_MTP_LENGTH);
+	if (ret)
+		dev_err(&lcd->dev->dev, "%s: dsi read error\n", __func__);
+
+	s6e8aa0_disable_mtp_register(lcd);
+
+	return ret;
+}
+
+static const s6e8aa0_gamma_table s6e8aa0_gamma_tables_v142[GAMMA_LEVEL_NUM] = {
+	{
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0x62, 0x55, 0x55,
+		0xaf, 0xb1, 0xb1, 0xbd, 0xce, 0xb7, 0x9a, 0xb1,
+		0x90, 0xb2, 0xc4, 0xae, 0x00, 0x60, 0x00, 0x40,
+		0x00, 0x70,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0x74, 0x68, 0x69,
+		0xb8, 0xc1, 0xb7, 0xbd, 0xcd, 0xb8, 0x93, 0xab,
+		0x88, 0xb4, 0xc4, 0xb1, 0x00, 0x6b, 0x00, 0x4d,
+		0x00, 0x7d,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0x95, 0x8a, 0x89,
+		0xb4, 0xc6, 0xb2, 0xc5, 0xd2, 0xbf, 0x90, 0xa8,
+		0x85, 0xb5, 0xc4, 0xb3, 0x00, 0x7b, 0x00, 0x5d,
+		0x00, 0x8f,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0x9f, 0x98, 0x92,
+		0xb3, 0xc4, 0xb0, 0xbc, 0xcc, 0xb4, 0x91, 0xa6,
+		0x87, 0xb5, 0xc5, 0xb4, 0x00, 0x87, 0x00, 0x6a,
+		0x00, 0x9e,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0x99, 0x93, 0x8b,
+		0xb2, 0xc2, 0xb0, 0xbd, 0xce, 0xb4, 0x90, 0xa6,
+		0x87, 0xb3, 0xc3, 0xb2, 0x00, 0x8d, 0x00, 0x70,
+		0x00, 0xa4,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xa5, 0x99,
+		0xb2, 0xc2, 0xb0, 0xbb, 0xcd, 0xb1, 0x93, 0xa7,
+		0x8a, 0xb2, 0xc1, 0xb0, 0x00, 0x92, 0x00, 0x75,
+		0x00, 0xaa,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa0, 0xa0, 0x93,
+		0xb6, 0xc4, 0xb4, 0xb5, 0xc8, 0xaa, 0x94, 0xa9,
+		0x8c, 0xb2, 0xc0, 0xb0, 0x00, 0x97, 0x00, 0x7a,
+		0x00, 0xaf,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xa7, 0x96,
+		0xb3, 0xc2, 0xb0, 0xba, 0xcb, 0xb0, 0x94, 0xa8,
+		0x8c, 0xb0, 0xbf, 0xaf, 0x00, 0x9f, 0x00, 0x83,
+		0x00, 0xb9,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0x9d, 0xa2, 0x90,
+		0xb6, 0xc5, 0xb3, 0xb8, 0xc9, 0xae, 0x94, 0xa8,
+		0x8d, 0xaf, 0xbd, 0xad, 0x00, 0xa4, 0x00, 0x88,
+		0x00, 0xbf,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa6, 0xac, 0x97,
+		0xb4, 0xc4, 0xb1, 0xbb, 0xcb, 0xb2, 0x93, 0xa7,
+		0x8d, 0xae, 0xbc, 0xad, 0x00, 0xa7, 0x00, 0x8c,
+		0x00, 0xc3,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa2, 0xa9, 0x93,
+		0xb6, 0xc5, 0xb2, 0xba, 0xc9, 0xb0, 0x93, 0xa7,
+		0x8d, 0xae, 0xbb, 0xac, 0x00, 0xab, 0x00, 0x90,
+		0x00, 0xc8,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0x9e, 0xa6, 0x8f,
+		0xb7, 0xc6, 0xb3, 0xb8, 0xc8, 0xb0, 0x93, 0xa6,
+		0x8c, 0xae, 0xbb, 0xad, 0x00, 0xae, 0x00, 0x93,
+		0x00, 0xcc,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xab, 0xb4, 0x9c,
+		0xb3, 0xc3, 0xaf, 0xb7, 0xc7, 0xaf, 0x93, 0xa6,
+		0x8c, 0xaf, 0xbc, 0xad, 0x00, 0xb1, 0x00, 0x97,
+		0x00, 0xcf,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa6, 0xb1, 0x98,
+		0xb1, 0xc2, 0xab, 0xba, 0xc9, 0xb2, 0x93, 0xa6,
+		0x8d, 0xae, 0xba, 0xab, 0x00, 0xb5, 0x00, 0x9b,
+		0x00, 0xd4,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xae, 0x94,
+		0xb2, 0xc3, 0xac, 0xbb, 0xca, 0xb4, 0x91, 0xa4,
+		0x8a, 0xae, 0xba, 0xac, 0x00, 0xb8, 0x00, 0x9e,
+		0x00, 0xd8,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xab, 0xb7, 0x9c,
+		0xae, 0xc0, 0xa9, 0xba, 0xc9, 0xb3, 0x92, 0xa5,
+		0x8b, 0xad, 0xb9, 0xab, 0x00, 0xbb, 0x00, 0xa1,
+		0x00, 0xdc,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xb4, 0x97,
+		0xb0, 0xc1, 0xaa, 0xb9, 0xc8, 0xb2, 0x92, 0xa5,
+		0x8c, 0xae, 0xb9, 0xab, 0x00, 0xbe, 0x00, 0xa4,
+		0x00, 0xdf,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xb0, 0x94,
+		0xb0, 0xc2, 0xab, 0xbb, 0xc9, 0xb3, 0x91, 0xa4,
+		0x8b, 0xad, 0xb8, 0xaa, 0x00, 0xc1, 0x00, 0xa8,
+		0x00, 0xe2,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa3, 0xb0, 0x94,
+		0xae, 0xbf, 0xa8, 0xb9, 0xc8, 0xb3, 0x92, 0xa4,
+		0x8b, 0xad, 0xb7, 0xa9, 0x00, 0xc4, 0x00, 0xab,
+		0x00, 0xe6,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xb6, 0x98,
+		0xaf, 0xc0, 0xa8, 0xb8, 0xc7, 0xb2, 0x93, 0xa5,
+		0x8d, 0xad, 0xb7, 0xa9, 0x00, 0xc7, 0x00, 0xae,
+		0x00, 0xe9,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa4, 0xb3, 0x95,
+		0xaf, 0xc1, 0xa9, 0xb9, 0xc8, 0xb3, 0x92, 0xa4,
+		0x8b, 0xad, 0xb7, 0xaa, 0x00, 0xc9, 0x00, 0xb0,
+		0x00, 0xec,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa4, 0xb3, 0x95,
+		0xac, 0xbe, 0xa6, 0xbb, 0xc9, 0xb4, 0x90, 0xa3,
+		0x8a, 0xad, 0xb7, 0xa9, 0x00, 0xcc, 0x00, 0xb4,
+		0x00, 0xf0,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa0, 0xb0, 0x91,
+		0xae, 0xc0, 0xa6, 0xba, 0xc8, 0xb4, 0x91, 0xa4,
+		0x8b, 0xad, 0xb7, 0xa9, 0x00, 0xcf, 0x00, 0xb7,
+		0x00, 0xf3,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa7, 0xb8, 0x98,
+		0xab, 0xbd, 0xa4, 0xbb, 0xc9, 0xb5, 0x91, 0xa3,
+		0x8b, 0xac, 0xb6, 0xa8, 0x00, 0xd1, 0x00, 0xb9,
+		0x00, 0xf6,
+	}, {
+		0xfa, 0x01, 0x71, 0x31, 0x7b, 0xa4, 0xb5, 0x95,
+		0xa9, 0xbc, 0xa1, 0xbb, 0xc9, 0xb5, 0x91, 0xa3,
+		0x8a, 0xad, 0xb6, 0xa8, 0x00, 0xd6, 0x00, 0xbf,
+		0x00, 0xfc,
+	},
+};
+
+static const s6e8aa0_gamma_table s6e8aa0_gamma_tables_v96[GAMMA_LEVEL_NUM] = {
+	{
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+	       	0xdf, 0x1f, 0xd7, 0xdc, 0xb7, 0xe1, 0xc0, 0xaf,
+	       	0xc4, 0xd2, 0xd0, 0xcf, 0x00, 0x4d, 0x00, 0x40,
+	       	0x00, 0x5f,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+		0xd5, 0x35, 0xcf, 0xdc, 0xc1, 0xe1, 0xbf, 0xb3,
+	       	0xc1, 0xd2, 0xd1, 0xce,	0x00, 0x53, 0x00, 0x46,
+	       	0x00, 0x67,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+		0xd2, 0x64, 0xcf, 0xdb, 0xc6, 0xe1, 0xbd, 0xb3,
+	       	0xbd, 0xd2, 0xd2, 0xce,	0x00, 0x59, 0x00, 0x4b,
+	       	0x00, 0x6e,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+		0xd0, 0x7c, 0xcf, 0xdb, 0xc9, 0xe0, 0xbc, 0xb4,
+	       	0xbb, 0xcf, 0xd1, 0xcc, 0x00, 0x5f, 0x00, 0x50,
+	       	0x00, 0x75,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+		0xd0, 0x8e, 0xd1, 0xdb, 0xcc, 0xdf, 0xbb, 0xb6,
+	       	0xb9, 0xd0, 0xd1, 0xcd,	0x00, 0x63, 0x00, 0x54,
+	       	0x00, 0x7a,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+		0xd1, 0x9e, 0xd5, 0xda, 0xcd, 0xdd, 0xbb, 0xb7,
+	       	0xb9, 0xce, 0xce, 0xc9,	0x00, 0x68, 0x00, 0x59,
+	       	0x00, 0x81,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0xff,
+		0xd0, 0xa5, 0xd6, 0xda, 0xcf, 0xdd, 0xbb, 0xb7,
+	       	0xb8, 0xcc, 0xcd, 0xc7,	0x00, 0x6c, 0x00, 0x5c,
+	       	0x00, 0x86,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x1f, 0xfe,
+		0xd0, 0xae, 0xd7, 0xd9, 0xd0, 0xdb, 0xb9, 0xb6,
+	       	0xb5, 0xca, 0xcc, 0xc5,	0x00, 0x74, 0x00, 0x63,
+	       	0x00, 0x90,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x1f, 0xf9,
+		0xcf, 0xb0, 0xd6, 0xd9, 0xd1, 0xdb, 0xb9, 0xb6,
+	       	0xb4, 0xca, 0xcb, 0xc5,	0x00, 0x77, 0x00, 0x66,
+	       	0x00, 0x94,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xff, 0x1f, 0xf7,
+		0xcf, 0xb3, 0xd7, 0xd8, 0xd1, 0xd9, 0xb7, 0xb6,
+	       	0xb3, 0xc9, 0xca, 0xc3,	0x00, 0x7b, 0x00, 0x69,
+	       	0x00, 0x99,
+
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xfd, 0x2f, 0xf7,
+		0xdf, 0xb5, 0xd6, 0xd8, 0xd1, 0xd8, 0xb6, 0xb5,
+	       	0xb2, 0xca, 0xcb, 0xc4,	0x00, 0x7e, 0x00, 0x6c,
+	       	0x00, 0x9d,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xfa, 0x2f, 0xf5,
+		0xce, 0xb6, 0xd5, 0xd7, 0xd2, 0xd8, 0xb6, 0xb4,
+	       	0xb0, 0xc7, 0xc9, 0xc1,	0x00, 0x84, 0x00, 0x71,
+	       	0x00, 0xa5,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xf7, 0x2f, 0xf2,
+		0xce, 0xb9, 0xd5, 0xd8, 0xd2, 0xd8, 0xb4, 0xb4,
+	       	0xaf, 0xc7, 0xc9, 0xc1,	0x00, 0x87, 0x00, 0x73,
+	       	0x00, 0xa8,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xf5, 0x2f, 0xf0,
+		0xdf, 0xba, 0xd5, 0xd7, 0xd2, 0xd7, 0xb4, 0xb4,
+	       	0xaf, 0xc5, 0xc7, 0xbf,	0x00, 0x8a, 0x00, 0x76,
+	       	0x00, 0xac,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xf2, 0x2f, 0xed,
+		0xcE, 0xbb, 0xd4, 0xd6, 0xd2, 0xd6, 0xb5, 0xb4,
+	       	0xaF, 0xc5, 0xc7, 0xbf,	0x00, 0x8c, 0x00, 0x78,
+	       	0x00, 0xaf,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xef, 0x2f, 0xeb,
+		0xcd, 0xbb, 0xd2, 0xd7, 0xd3, 0xd6, 0xb3, 0xb4,
+	       	0xae, 0xc5, 0xc6, 0xbe,	0x00, 0x91, 0x00, 0x7d,
+	       	0x00, 0xb6,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xee, 0x2f, 0xea,
+		0xce, 0xbd, 0xd4, 0xd6, 0xd2, 0xd5, 0xb2, 0xb3,
+	       	0xad, 0xc3, 0xc4, 0xbb,	0x00, 0x94, 0x00, 0x7f,
+	       	0x00, 0xba,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xec, 0x2f, 0xe8,
+		0xce, 0xbe, 0xd3, 0xd6, 0xd3, 0xd5, 0xb2, 0xb2,
+	       	0xac, 0xc3, 0xc5, 0xbc,	0x00, 0x96, 0x00, 0x81,
+	       	0x00, 0xbd,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xeb, 0x2f, 0xe7,
+		0xce, 0xbf, 0xd3, 0xd6, 0xd2, 0xd5, 0xb1, 0xb2,
+	       	0xab, 0xc2, 0xc4, 0xbb,	0x00, 0x99, 0x00, 0x83,
+	       	0x00, 0xc0,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xef, 0x5f, 0xe9,
+		0xca, 0xbf, 0xd3, 0xd5, 0xd2, 0xd4, 0xb2, 0xb2,
+	       	0xab, 0xc1, 0xc4, 0xba,	0x00, 0x9b, 0x00, 0x85,
+	       	0x00, 0xc3,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xea, 0x5f, 0xe8,
+		0xee, 0xbf, 0xd2, 0xd5, 0xd2, 0xd4, 0xb1, 0xb2,
+	       	0xab, 0xc1, 0xc2, 0xb9,	0x00, 0x9D, 0x00, 0x87,
+	       	0x00, 0xc6,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe9, 0x5f, 0xe7,
+		0xcd, 0xbf, 0xd2, 0xd6, 0xd2, 0xd4, 0xb1, 0xb2,
+	       	0xab, 0xbe, 0xc0, 0xb7,	0x00, 0xa1, 0x00, 0x8a,
+	       	0x00, 0xca,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe8, 0x61, 0xe6,
+		0xcd, 0xbf, 0xd1, 0xd6, 0xd3, 0xd4, 0xaf, 0xb0,
+	       	0xa9, 0xbe, 0xc1, 0xb7,	0x00, 0xa3, 0x00, 0x8b,
+	       	0x00, 0xce,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe8, 0x62, 0xe5,
+		0xcc, 0xc0, 0xd0, 0xd6, 0xd2, 0xd4, 0xaf, 0xb1,
+	       	0xa9, 0xbd, 0xc0, 0xb6,	0x00, 0xa5, 0x00, 0x8d,
+	       	0x00, 0xd0,
+	}, {
+		0xfa, 0x01, 0x1f, 0x1f, 0x1f, 0xe7, 0x7f, 0xe3,
+		0xcc, 0xc1, 0xd0, 0xd5, 0xd3, 0xd3, 0xae, 0xaf,
+	       	0xa8, 0xbe, 0xc0, 0xb7,	0x00, 0xa8, 0x00, 0x90,
+	       	0x00, 0xd3,
+	}
+};
+
+static const s6e8aa0_gamma_table s6e8aa0_gamma_tables_v32[GAMMA_LEVEL_NUM] = {
+	{
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0x72, 0x5e, 0x6b,
+		0xa1, 0xa7, 0x9a, 0xb4, 0xcb, 0xb8, 0x92, 0xac,
+		0x97, 0xb4, 0xc3, 0xb5, 0x00, 0x4e, 0x00, 0x37,
+		0x00, 0x58,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0x85, 0x71, 0x7d,
+		0xa6, 0xb6, 0xa1, 0xb5, 0xca, 0xba, 0x93, 0xac,
+		0x98, 0xb2, 0xc0, 0xaf, 0x00, 0x59, 0x00, 0x43,
+		0x00, 0x64,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xa4, 0x94, 0x9e,
+		0xa0, 0xbb, 0x9c, 0xc3, 0xd2, 0xc6, 0x93, 0xaa,
+		0x95, 0xb7, 0xc2, 0xb4, 0x00, 0x65, 0x00, 0x50,
+		0x00, 0x74,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xa1, 0xa6,
+		0xa0, 0xb9, 0x9b, 0xc3, 0xd1, 0xc8, 0x90, 0xa6,
+		0x90, 0xbb, 0xc3, 0xb7, 0x00, 0x6f, 0x00, 0x5b,
+		0x00, 0x80,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xa6, 0x9d, 0x9f,
+		0x9f, 0xb8, 0x9a, 0xc7, 0xd5, 0xcc, 0x90, 0xa5,
+		0x8f, 0xb8, 0xc1, 0xb6, 0x00, 0x74, 0x00, 0x60,
+		0x00, 0x85,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb3, 0xae, 0xae,
+		0x9e, 0xb7, 0x9a, 0xc8, 0xd6, 0xce, 0x91, 0xa6,
+		0x90, 0xb6, 0xc0, 0xb3, 0x00, 0x78, 0x00, 0x65,
+		0x00, 0x8a,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xa9, 0xa8,
+		0xa3, 0xb9, 0x9e, 0xc4, 0xd3, 0xcb, 0x94, 0xa6,
+		0x90, 0xb6, 0xbf, 0xb3, 0x00, 0x7c, 0x00, 0x69,
+		0x00, 0x8e,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xaf, 0xaf, 0xa9,
+		0xa5, 0xbc, 0xa2, 0xc7, 0xd5, 0xcd, 0x93, 0xa5,
+		0x8f, 0xb4, 0xbd, 0xb1, 0x00, 0x83, 0x00, 0x70,
+		0x00, 0x96,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xa9, 0xab, 0xa3,
+		0xaa, 0xbf, 0xa7, 0xc5, 0xd3, 0xcb, 0x93, 0xa5,
+		0x8f, 0xb2, 0xbb, 0xb0, 0x00, 0x86, 0x00, 0x74,
+		0x00, 0x9b,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb1, 0xb5, 0xab,
+		0xab, 0xc0, 0xa9, 0xc7, 0xd4, 0xcc, 0x94, 0xa4,
+		0x8f, 0xb1, 0xbb, 0xaf, 0x00, 0x8a, 0x00, 0x77,
+		0x00, 0x9e,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb2, 0xa7,
+		0xae, 0xc2, 0xab, 0xc5, 0xd3, 0xca, 0x93, 0xa4,
+		0x8f, 0xb1, 0xba, 0xae, 0x00, 0x8d, 0x00, 0x7b,
+		0x00, 0xa2,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xa9, 0xaf, 0xa3,
+		0xb0, 0xc3, 0xae, 0xc4, 0xd1, 0xc8, 0x93, 0xa4,
+		0x8f, 0xb1, 0xba, 0xaf, 0x00, 0x8f, 0x00, 0x7d,
+		0x00, 0xa5,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb4, 0xbd, 0xaf,
+		0xae, 0xc1, 0xab, 0xc2, 0xd0, 0xc6, 0x94, 0xa4,
+		0x8f, 0xb1, 0xba, 0xaf, 0x00, 0x92, 0x00, 0x80,
+		0x00, 0xa8,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb0, 0xb9, 0xac,
+		0xad, 0xc1, 0xab, 0xc4, 0xd1, 0xc7, 0x95, 0xa4,
+		0x90, 0xb0, 0xb9, 0xad, 0x00, 0x95, 0x00, 0x84,
+		0x00, 0xac,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb6, 0xa7,
+		0xaf, 0xc2, 0xae, 0xc5, 0xd1, 0xc7, 0x93, 0xa3,
+		0x8e, 0xb0, 0xb9, 0xad, 0x00, 0x98, 0x00, 0x86,
+		0x00, 0xaf,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb4, 0xbf, 0xaf,
+		0xad, 0xc1, 0xab, 0xc3, 0xd0, 0xc6, 0x94, 0xa3,
+		0x8f, 0xaf, 0xb8, 0xac, 0x00, 0x9a, 0x00, 0x89,
+		0x00, 0xb2,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb0, 0xbc, 0xac,
+		0xaf, 0xc2, 0xad, 0xc2, 0xcf, 0xc4, 0x94, 0xa3,
+		0x90, 0xaf, 0xb8, 0xad, 0x00, 0x9c, 0x00, 0x8b,
+		0x00, 0xb5,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb9, 0xa7,
+		0xb1, 0xc4, 0xaf, 0xc3, 0xcf, 0xc5, 0x94, 0xa3,
+		0x8f, 0xae, 0xb7, 0xac, 0x00, 0x9f, 0x00, 0x8e,
+		0x00, 0xb8,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xb9, 0xa7,
+		0xaf, 0xc2, 0xad, 0xc1, 0xce, 0xc3, 0x95, 0xa3,
+		0x90, 0xad, 0xb6, 0xab, 0x00, 0xa2, 0x00, 0x91,
+		0x00, 0xbb,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb1, 0xbe, 0xac,
+		0xb1, 0xc4, 0xaf, 0xc1, 0xcd, 0xc1, 0x95, 0xa4,
+		0x91, 0xad, 0xb6, 0xab, 0x00, 0xa4, 0x00, 0x93,
+		0x00, 0xbd,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xbb, 0xa8,
+		0xb3, 0xc5, 0xb2, 0xc1, 0xcd, 0xc2, 0x95, 0xa3,
+		0x90, 0xad, 0xb6, 0xab, 0x00, 0xa6, 0x00, 0x95,
+		0x00, 0xc0,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xbb, 0xa8,
+		0xb0, 0xc3, 0xaf, 0xc2, 0xce, 0xc2, 0x94, 0xa2,
+		0x90, 0xac, 0xb6, 0xab, 0x00, 0xa8, 0x00, 0x98,
+		0x00, 0xc3,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xa9, 0xb8, 0xa5,
+		0xb3, 0xc5, 0xb2, 0xc1, 0xcc, 0xc0, 0x95, 0xa2,
+		0x90, 0xad, 0xb6, 0xab, 0x00, 0xaa, 0x00, 0x9a,
+		0x00, 0xc5,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xb0, 0xc0, 0xac,
+		0xb0, 0xc3, 0xaf, 0xc1, 0xcd, 0xc1, 0x95, 0xa2,
+		0x90, 0xac, 0xb5, 0xa9, 0x00, 0xac, 0x00, 0x9c,
+		0x00, 0xc8,
+	}, {
+		0xfa, 0x01, 0x43, 0x14, 0x45, 0xad, 0xbd, 0xa8,
+		0xaf, 0xc2, 0xaf, 0xc1, 0xcc, 0xc0, 0x95, 0xa2,
+		0x90, 0xac, 0xb5, 0xaa, 0x00, 0xb1, 0x00, 0xa1,
+		0x00, 0xcc,
+	},
+};
+
+static int s6e8aa0_brightness_set(struct s6e8aa0 *lcd)
+{
+	const struct s6e8aa0_variant *variant = lcd->variant;
+	int ret;
+
+	ret = mipi_dsi_dcs_write(lcd->dev, 0,
+				variant->gamma_tables[lcd->brightness],
+				sizeof(variant->gamma_tables[lcd->brightness]));
+	if (ret < 0)
+		return ret;
+
+	/* update gamma table. */
+	ret = mipi_dsi_dcs_write_static_seq(lcd->dev, 0, 0xf7, 0x03);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int s6e8aa0_brightness_set_v142(struct s6e8aa0 *lcd)
+{
+	const struct s6e8aa0_variant *variant = lcd->variant;
+	int ret;
+
+	ret = variant->elvss_nvm_set(lcd);
+	if (ret < 0)
+		return ret;
+
+	return s6e8aa0_brightness_set(lcd);
+}
+
+static int s6e8aa0_panel_init(struct s6e8aa0 *lcd)
+{
+	const struct s6e8aa0_variant *variant = lcd->variant;
+
+	s6e8aa0_apply_level_1_key(lcd);
+	s6e8aa0_apply_level_2_key(lcd);
+	msleep(20);
+
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, MIPI_DCS_EXIT_SLEEP_MODE);
+	msleep(40);
+
+	variant->panel_cond_set(lcd);
+	s6e8aa0_display_condition_set(lcd);
+
+	s6e8aa0_brightness_set(lcd);
+
+	s6e8aa0_etc_source_control(lcd);
+	s6e8aa0_etc_pentile_control(lcd);
+	variant->elvss_nvm_set(lcd);
+	s6e8aa0_etc_power_control(lcd);
+	s6e8aa0_etc_elvss_control(lcd);
+	msleep(lcd->pdata->init_delay);
+
+	dev_dbg(&lcd->dev->dev, "panel init sequence done.\n");
+
+	return 0;
+}
+
+static int s6e8aa0_get_brightness(struct backlight_device *bd)
+{
+	return bd->props.brightness;
+}
+
+static int s6e8aa0_update_status(struct backlight_device *bd)
+{
+	struct s6e8aa0 *lcd = bl_get_data(bd);
+	const struct s6e8aa0_variant *variant = lcd->variant;
+	int ret;
+
+	mutex_lock(&lcd->mutex);
+
+	lcd->brightness = bd->props.brightness;
+
+	if (!lcd->power)
+		goto unlock;
+
+	ret = variant->brightness_set(lcd);
+	if (ret) {
+		dev_err(&lcd->dev->dev, "lcd brightness setting failed.\n");
+		goto unlock;
+	}
+
+unlock:
+	mutex_unlock(&lcd->mutex);
+
+	return 0;
+}
+
+static const struct backlight_ops s6e8aa0_backlight_ops = {
+	.get_brightness = s6e8aa0_get_brightness,
+	.update_status = s6e8aa0_update_status,
+};
+
+static int s6e8aa0_set_stream(struct s6e8aa0 *lcd, bool on);
+
+static int s6e8aa0_set_power(struct lcd_device *ld, int power)
+{
+	struct s6e8aa0 *lcd = lcd_get_data(ld);
+	int ret = 0;
+
+	mutex_lock(&lcd->mutex);
+
+	ret = s6e8aa0_set_stream(lcd, power = FB_BLANK_UNBLANK);
+
+	if (ret)
+		goto unlock;
+
+	lcd->power = power;
+
+unlock:
+	mutex_unlock(&lcd->mutex);
+
+	return ret;
+}
+
+static int s6e8aa0_get_power(struct lcd_device *ld)
+{
+	struct s6e8aa0 *lcd = lcd_get_data(ld);
+
+	return lcd->power;
+}
+
+static struct lcd_ops s6e8aa0_lcd_ops = {
+	.set_power = s6e8aa0_set_power,
+	.get_power = s6e8aa0_get_power,
+};
+
+static const struct s6e8aa0_variant s6e8aa0_variants[] = {
+	{
+		.version = 32,
+		.panel_cond_set = s6e8aa0_panel_cond_set,
+		.pentile_ctl_table = s6e8aa0_pentile_ctl_table_v32,
+		.pentile_ctl_len = ARRAY_SIZE(s6e8aa0_pentile_ctl_table_v32),
+		.power_ctl_table = s6e8aa0_power_ctl_table_v32,
+		.power_ctl_len = ARRAY_SIZE(s6e8aa0_power_ctl_table_v32),
+		.elvss_nvm_set = s6e8aa0_elvss_nvm_set,
+		.brightness_set = s6e8aa0_brightness_set,
+		.gamma_tables = s6e8aa0_gamma_tables_v32,
+	}, {
+		.version = 142,
+		.panel_cond_set = s6e8aa0_panel_cond_set_v142,
+		.pentile_ctl_table = s6e8aa0_pentile_ctl_table,
+		.pentile_ctl_len = ARRAY_SIZE(s6e8aa0_pentile_ctl_table),
+		.power_ctl_table = s6e8aa0_power_ctl_table,
+		.power_ctl_len = ARRAY_SIZE(s6e8aa0_power_ctl_table),
+		.elvss_nvm_set = s6e8aa0_elvss_nvm_set_v142,
+		.brightness_set = s6e8aa0_brightness_set_v142,
+		.gamma_tables = s6e8aa0_gamma_tables_v142,
+	}, {
+		/* FIXME: support M0 panel v96
+		 * need to suitable panel specification
+ 		 */
+		.version = 96,
+		.panel_cond_set = s6e8aa0_panel_cond_set,
+		.pentile_ctl_table = s6e8aa0_pentile_ctl_table_v32,
+		.pentile_ctl_len = ARRAY_SIZE(s6e8aa0_pentile_ctl_table_v32),
+		.power_ctl_table = s6e8aa0_power_ctl_table_v32,
+		.power_ctl_len = ARRAY_SIZE(s6e8aa0_power_ctl_table_v32),
+		.elvss_nvm_set = s6e8aa0_elvss_nvm_set,
+		.brightness_set = s6e8aa0_brightness_set,
+		.gamma_tables = s6e8aa0_gamma_tables_v96,
+	
+	}, {
+		/* FIXME: support U1HD panel v210
+		 * need to suitable panel specification
+ 		 */
+		.version = 210,
+		.panel_cond_set = s6e8aa0_panel_cond_set_v142,
+		.pentile_ctl_table = s6e8aa0_pentile_ctl_table,
+		.pentile_ctl_len = ARRAY_SIZE(s6e8aa0_pentile_ctl_table),
+		.power_ctl_table = s6e8aa0_power_ctl_table,
+		.power_ctl_len = ARRAY_SIZE(s6e8aa0_power_ctl_table),
+		.elvss_nvm_set = s6e8aa0_elvss_nvm_set_v142,
+		.brightness_set = s6e8aa0_brightness_set_v142,
+		.gamma_tables = s6e8aa0_gamma_tables_v142,
+	}
+};
+
+static int s6e8aa0_check_mtp(struct s6e8aa0 *lcd)
+{
+	int ret;
+	u8 mtp_data[LDI_MTP_LENGTH] = {0, };
+	u8 mtp_id[3] = {0, };
+	int i;
+
+	s6e8aa0_read_id(lcd, mtp_id);
+	if (mtp_id[0] = 0x00) {
+		dev_err(&lcd->dev->dev, "read id failed\n");
+		return -EIO;
+	}
+
+	dev_info(&lcd->dev->dev, "Read ID : 0x%2x, 0x%2x, 0x%2x\n",
+					mtp_id[0], mtp_id[1], mtp_id[2]);
+
+	if (mtp_id[2] = 0x33)
+		dev_dbg(&lcd->dev->dev,
+			"ID-3 is 0xff does not support dynamic elvss\n");
+	else {
+		dev_dbg(&lcd->dev->dev,
+			"ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]);
+		dev_dbg(&lcd->dev->dev, "Dynamic ELVSS Information\n");
+	}
+
+	for (i = 0; i < ARRAY_SIZE(s6e8aa0_variants); ++i) {
+		if (mtp_id[1] = s6e8aa0_variants[i].version)
+			break;
+	}
+	if (i >= ARRAY_SIZE(s6e8aa0_variants)) {
+		dev_err(&lcd->dev->dev, "unsupported display version %d\n",
+								mtp_id[1]);
+		return -EINVAL;
+	}
+
+	lcd->variant = &s6e8aa0_variants[i];
+	lcd->id = mtp_id[2];
+	lcd->aid = (mtp_id[2] >> 5);
+
+	ret = s6e8aa0_read_mtp(lcd, mtp_data);
+	if (ret) {
+		dev_err(&lcd->dev->dev, "read mtp failed\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int s6e8aa0_set_sequence(struct s6e8aa0 *lcd)
+{
+	int ret;
+
+	ret = s6e8aa0_check_mtp(lcd);
+	if (ret < 0)
+		return ret;
+
+	s6e8aa0_panel_init(lcd);
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, MIPI_DCS_SET_DISPLAY_ON);
+
+	dev_dbg(&lcd->dev->dev, "%s:done.\n", __func__);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static int s6e8aa0_generic_reset(struct device *dev)
+{
+	struct s6e8aa0 *lcd = dev_get_drvdata(dev);
+
+	gpio_set_value(lcd->reset_gpio, 1);
+	usleep_range(10000, 11000);
+	gpio_set_value(lcd->reset_gpio, 0);
+	usleep_range(10000, 11000);
+	gpio_set_value(lcd->reset_gpio, 1);
+
+	return 0;
+}
+
+static struct s6e8aa0_platform_data *s6e8aa0_parse_dt(struct s6e8aa0 *lcd)
+{
+	struct device_node *node = lcd->dev->dev.of_node;
+	struct s6e8aa0_platform_data *data;
+	const __be32 *prop_data;
+	int ret;
+
+	data = devm_kzalloc(&lcd->dev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		dev_err(&lcd->dev->dev, "failed to allocate platform data.\n");
+		return NULL;
+	}
+
+	ret = of_get_videomode(node, &data->mode, 0);
+	if (ret) {
+		dev_err(&lcd->dev->dev, "failed to read video mode from DT\n");
+		return NULL;
+	}
+
+	lcd->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0);
+	if (lcd->reset_gpio < 0)
+		return NULL;
+
+	prop_data = of_get_property(node, "reset-delay", NULL);
+	if (!prop_data)
+		return NULL;
+	data->reset_delay = be32_to_cpu(*prop_data);
+
+	prop_data = of_get_property(node, "power-on-delay", NULL);
+	if (!prop_data)
+		return NULL;
+	data->power_on_delay = be32_to_cpu(*prop_data);
+
+	prop_data = of_get_property(node, "init-delay", NULL);
+	if (!prop_data)
+		return NULL;
+	data->init_delay = be32_to_cpu(*prop_data);
+
+	if (of_find_property(node, "flip-horizontal", NULL))
+		data->flip_horizontal = true;
+
+	if (of_find_property(node, "flip-vertical", NULL))
+		data->flip_vertical = true;
+
+	data->reset = s6e8aa0_generic_reset;
+
+	return data;
+}
+
+static struct of_device_id s6e8aa0_of_match[] = {
+	{ .compatible = "samsung,s6e8aa0" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, s6e8aa0_of_match);
+#else
+static struct s6e8aa0_platform_data *s6e8aa0_parse_dt(struct s6e8aa0 *lcd)
+{
+	return NULL;
+}
+#endif
+
+static int s6e8aa0_power_on(struct s6e8aa0 *lcd)
+{
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(lcd->supplies), lcd->supplies);
+	if (ret)
+		return ret;
+
+	msleep(lcd->pdata->power_on_delay);
+
+	/* lcd reset */
+	if (lcd->pdata->reset) {
+		ret = lcd->pdata->reset(&lcd->dev->dev);
+		if (ret)
+			goto err;
+	}
+
+	msleep(lcd->pdata->reset_delay);
+	ret = mipi_dsi_set_power(lcd->dev, 1);
+	if (ret)
+		goto err;
+
+	ret = s6e8aa0_set_sequence(lcd);
+	if (!ret)
+		return 0;
+
+	mipi_dsi_set_power(lcd->dev, 0);
+err:
+	regulator_bulk_disable(ARRAY_SIZE(lcd->supplies), lcd->supplies);
+
+	return ret;
+}
+
+static void s6e8aa0_power_off(struct s6e8aa0 *lcd)
+{
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, MIPI_DCS_ENTER_SLEEP_MODE);
+	mipi_dsi_dcs_write_static_seq(lcd->dev, 0, MIPI_DCS_SET_DISPLAY_OFF);
+
+	mipi_dsi_set_power(lcd->dev, 0);
+
+	regulator_bulk_disable(ARRAY_SIZE(lcd->supplies), lcd->supplies);
+}
+
+static int s6e8aa0_set_stream(struct s6e8aa0 *lcd, bool on)
+{
+	if (on = lcd->streaming)
+		return 0;
+
+	if (on) {
+		s6e8aa0_power_on(lcd);
+		mipi_dsi_set_stream(lcd->dev, true);
+	} else {
+		mipi_dsi_set_stream(lcd->dev, false);
+		s6e8aa0_power_off(lcd);
+	}
+
+	lcd->streaming = on;
+
+	return 0;
+}
+
+static int s6e8aa0_probe(struct mipi_dsi_device *pdev)
+{
+	struct s6e8aa0 *lcd;
+	int ret;
+
+	lcd = kzalloc(sizeof(struct s6e8aa0), GFP_KERNEL);
+	if (!lcd) {
+		dev_err(&pdev->dev, "failed to allocate s6e8aa0 structure.\n");
+		return -ENOMEM;
+	}
+
+	mipi_dsi_set_drvdata(pdev, lcd);
+
+	lcd->dev = pdev;
+	lcd->pdata = (struct s6e8aa0_platform_data *)pdev->dev.platform_data;
+
+	if (!lcd->pdata) {
+		lcd->pdata = s6e8aa0_parse_dt(lcd);
+		if (!lcd->pdata) {
+			dev_err(&pdev->dev, "failed to find platform data\n");
+			return -ENODEV;
+		}
+	}
+
+	lcd->supplies[0].supply = "vdd3";
+	lcd->supplies[1].supply = "vci";
+	ret = regulator_bulk_get(&pdev->dev,
+				ARRAY_SIZE(lcd->supplies), lcd->supplies);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret);
+		goto err_regulator_bulk_get;
+	}
+
+	lcd->ld = lcd_device_register("s6e8aa0", &pdev->dev, lcd,
+			&s6e8aa0_lcd_ops);
+	if (IS_ERR(lcd->ld)) {
+		dev_err(&lcd->dev->dev, "failed to register lcd ops.\n");
+		ret = PTR_ERR(lcd->ld);
+		goto err_lcd_register;
+	}
+
+	lcd->bd = backlight_device_register("s6e8aa0-bl", &pdev->dev, lcd,
+			&s6e8aa0_backlight_ops, NULL);
+	if (IS_ERR(lcd->bd)) {
+		dev_err(&pdev->dev, "failed to register backlight ops.\n");
+		ret = PTR_ERR(lcd->bd);
+		goto err_backlight_register;
+	}
+
+	mutex_init(&lcd->mutex);
+
+	lcd->bd->props.max_brightness = GAMMA_LEVEL_NUM - 1;
+	lcd->bd->props.brightness = GAMMA_LEVEL_NUM - 1;
+	lcd->brightness = GAMMA_LEVEL_NUM - 1;
+
+	dev_dbg(&pdev->dev, "probed s6e8aa0 panel driver.\n");
+	pdev->vm = lcd->pdata->mode;
+
+	pdev->params = (struct mipi_dsi_interface_params) {
+		.format = DSI_FMT_RGB888,
+		.mode = DSI_MODE_VIDEO | DSI_MODE_VIDEO_BURST
+			| DSI_MODE_VIDEO_HFP | DSI_MODE_VIDEO_HBP
+			| DSI_MODE_VIDEO_HSA | DSI_MODE_EOT_PACKET
+			| DSI_MODE_VSYNC_FLUSH,
+		.data_lanes = 0xf,
+		.hs_clk_freq = 500000000,
+		.esc_clk_freq = 20000000,
+	};
+
+	s6e8aa0_set_stream(lcd, true);
+
+	return 0;
+
+err_backlight_register:
+	lcd_device_unregister(lcd->ld);
+err_lcd_register:
+	regulator_bulk_free(ARRAY_SIZE(lcd->supplies), lcd->supplies);
+err_regulator_bulk_get:
+	kfree(lcd);
+
+	return ret;
+}
+
+static int s6e8aa0_remove(struct mipi_dsi_device *pdev)
+{
+	struct s6e8aa0 *lcd = mipi_dsi_get_drvdata(pdev);
+
+	backlight_device_unregister(lcd->bd);
+	lcd_device_unregister(lcd->ld);
+	mipi_dsi_set_drvdata(pdev, NULL);
+	regulator_bulk_free(ARRAY_SIZE(lcd->supplies), lcd->supplies);
+	kfree(lcd);
+
+	return 0;
+}
+
+static int s6e8aa0_suspend(struct device *dev)
+{
+	struct s6e8aa0 *lcd = dev_get_drvdata(dev);
+
+	dev_err(dev, "%s: %d\n", __func__, lcd->power);
+
+	if (lcd->power != FB_BLANK_UNBLANK)
+		return 0;
+
+	mipi_dsi_set_stream(lcd->dev, 0);
+	s6e8aa0_power_off(lcd);
+
+	return 0;
+}
+
+static int s6e8aa0_resume(struct device *dev)
+{
+	struct s6e8aa0 *lcd = dev_get_drvdata(dev);
+	int ret;
+
+	dev_err(dev, "%s: %d\n", __func__, lcd->power);
+
+	if (lcd->power != FB_BLANK_UNBLANK)
+		return 0;
+
+	s6e8aa0_power_on(lcd);
+
+	ret = mipi_dsi_set_stream(lcd->dev, 1);
+	dev_err(dev, "%s: dsi_set_stream=%d\n", __func__, ret);
+
+	return 0;
+}
+
+static struct dev_pm_ops s6e8aa0_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(s6e8aa0_suspend, s6e8aa0_resume)
+};
+
+static const struct mipi_dsi_device_id s6e8aa0_id[] = {
+	{ "s6e8aa0", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(mipi_dsi, s6e8aa0_id);
+
+static struct mipi_dsi_driver s6e8aa0_driver = {
+	.probe = s6e8aa0_probe,
+	.remove = s6e8aa0_remove,
+	.driver = {
+		.name = "panel_s6e8aa0",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(s6e8aa0_of_match),
+		.pm = &s6e8aa0_pm_ops,
+	},
+	.id_table = s6e8aa0_id
+};
+module_mipi_dsi_driver(s6e8aa0_driver);
+
+MODULE_ALIAS("mipi-dsi:s6e8aa0");
+MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_AUTHOR("Joongmock Shin <jmock.shin@samsung.com>");
+MODULE_AUTHOR("Eunchul Kim <chulspro.kim@samsung.com>");
+MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
+MODULE_DESCRIPTION("MIPI-DSI based s6e8aa0 AMOLED LCD Panel Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/video/panel-s6e8aa0.h b/include/video/panel-s6e8aa0.h
new file mode 100644
index 0000000..8a32de8
--- /dev/null
+++ b/include/video/panel-s6e8aa0.h
@@ -0,0 +1,42 @@
+/*
+ * Renesas R61505-based Display Panels
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __PANEL_S6E8AX0_H__
+#define __PANEL_S6E8AX0_H__
+
+#include <video/videomode.h>
+
+struct s6e8aa0_platform_data {
+	unsigned long width;		/* Panel width in mm */
+	unsigned long height;		/* Panel height in mm */
+	struct videomode mode;
+
+	/* reset lcd panel device. */
+	int (*reset)(struct device *dev);
+
+	/* it indicates whether lcd panel was enabled
+	   from bootloader or not. */
+	int lcd_enabled;
+	/* 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 lcd power on. */
+	unsigned int power_on_delay;
+	/* stable time needing to become lcd power off. */
+	unsigned int init_delay;
+	/* panel is reversed */
+	bool flip_vertical;
+	bool flip_horizontal;
+};
+
+#endif /* __PANEL_S6E8AX0_H__ */
-- 
1.8.1.2


^ permalink raw reply related

* [RFC PATCH 2/4] mipi-dsi-exynos: add driver
From: Andrzej Hajda @ 2013-09-24 14:23 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Andrzej Hajda, dri-devel, linux-fbdev, linux-media, Kyungmin Park,
	Tomasz Figa, Donghwa Lee, Sylwester Nawrocki
In-Reply-To: <1380032596-18612-1-git-send-email-a.hajda@samsung.com>

This patch adds mipi-dsi-bus master driver for Exynos chipset family.

Signed-off-by: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Donghwa Lee <dh09.lee@samsung.com>
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/display/Kconfig           |    4 +
 drivers/video/display/Makefile          |    1 +
 drivers/video/display/mipi-dsi-exynos.c | 1310 +++++++++++++++++++++++++++++++
 include/video/mipi-dsi-exynos.h         |   41 +
 4 files changed, 1356 insertions(+)
 create mode 100644 drivers/video/display/mipi-dsi-exynos.c
 create mode 100644 include/video/mipi-dsi-exynos.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 619b05d..0a1e90b 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -24,6 +24,10 @@ config DISPLAY_MIPI_DSI
 	tristate
 	default n
 
+config DISPLAY_MIPI_DSI_EXYNOS
+	select DISPLAY_MIPI_DSI
+	tristate "Samsung SoC MIPI DSI Master"
+
 config DISPLAY_PANEL_DPI
 	tristate "DPI (Parallel) Display Panels"
 	---help---
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index b323fd4..2fd84f5 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_DISPLAY_CORE)			+= display.o
 obj-$(CONFIG_DISPLAY_CONNECTOR_VGA)		+= con-vga.o
 obj-$(CONFIG_DISPLAY_MIPI_DBI)			+= mipi-dbi-bus.o
 obj-$(CONFIG_DISPLAY_MIPI_DSI)			+= mipi-dsi-bus.o
+obj-$(CONFIG_DISPLAY_MIPI_DSI_EXYNOS)		+= mipi-dsi-exynos.o
 obj-$(CONFIG_DISPLAY_PANEL_DPI)			+= panel-dpi.o
 obj-$(CONFIG_DISPLAY_PANEL_R61505)		+= panel-r61505.o
 obj-$(CONFIG_DISPLAY_PANEL_R61517)		+= panel-r61517.o
diff --git a/drivers/video/display/mipi-dsi-exynos.c b/drivers/video/display/mipi-dsi-exynos.c
new file mode 100644
index 0000000..e094744
--- /dev/null
+++ b/drivers/video/display/mipi-dsi-exynos.c
@@ -0,0 +1,1310 @@
+/*
+ * Samsung SoC MIPI DSI Master driver.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd
+ *
+ * Contacts: Tomasz Figa <t.figa@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/memory.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+#include <video/mipi_display.h>
+#include <video/mipi-dsi-bus.h>
+#include <video/mipi-dsi-exynos.h>
+#include <video/videomode.h>
+
+#define DSIM_STATUS_REG		0x0	/* Status register */
+#define DSIM_SWRST_REG		0x4	/* Software reset register */
+#define DSIM_CLKCTRL_REG	0x8	/* Clock control register */
+#define DSIM_TIMEOUT_REG	0xc	/* Time out register */
+#define DSIM_CONFIG_REG		0x10	/* Configuration register */
+#define DSIM_ESCMODE_REG	0x14	/* Escape mode register */
+
+/* Main display image resolution register */
+#define DSIM_MDRESOL_REG	0x18
+#define DSIM_MVPORCH_REG	0x1c	/* Main display Vporch register */
+#define DSIM_MHPORCH_REG	0x20	/* Main display Hporch register */
+#define DSIM_MSYNC_REG		0x24	/* Main display sync area register */
+
+/* Sub display image resolution register */
+#define DSIM_SDRESOL_REG	0x28
+#define DSIM_INTSRC_REG		0x2c	/* Interrupt source register */
+#define DSIM_INTMSK_REG		0x30	/* Interrupt mask register */
+#define DSIM_PKTHDR_REG		0x34	/* Packet Header FIFO register */
+#define DSIM_PAYLOAD_REG	0x38	/* Payload FIFO register */
+#define DSIM_RXFIFO_REG		0x3c	/* Read FIFO register */
+#define DSIM_FIFOTHLD_REG	0x40	/* FIFO threshold level register */
+#define DSIM_FIFOCTRL_REG	0x44	/* FIFO status and control register */
+
+/* FIFO memory AC characteristic register */
+#define DSIM_PLLCTRL_REG	0x4c	/* PLL control register */
+#define DSIM_PLLTMR_REG		0x50	/* PLL timer register */
+#define DSIM_PHYACCHR_REG	0x54	/* D-PHY AC characteristic register */
+#define DSIM_PHYACCHR1_REG	0x58	/* D-PHY AC characteristic register1 */
+
+/* DSIM_STATUS */
+#define DSIM_STOP_STATE_DAT(x)		(((x) & 0xf) << 0)
+#define DSIM_STOP_STATE_CLK		(1 << 8)
+#define DSIM_TX_READY_HS_CLK		(1 << 10)
+#define DSIM_PLL_STABLE			(1 << 31)
+
+/* DSIM_SWRST */
+#define DSIM_FUNCRST			(1 << 16)
+#define DSIM_SWRST			(1 << 0)
+
+/* DSIM_TIMEOUT */
+#define DSIM_LPDR_TOUT(x)		((x) << 0)
+#define DSIM_BTA_TOUT(x)		((x) << 16)
+
+/* DSIM_CLKCTRL */
+#define DSIM_ESC_PRESCALER(x)		(((x) & 0xffff) << 0)
+#define DSIM_ESC_PRESCALER_MASK		(0xffff << 0)
+#define DSIM_LANE_ESC_CLK_EN_CLK	(1 << 19)
+#define DSIM_LANE_ESC_CLK_EN_DATA(x)	(((x) & 0xf) << 20)
+#define DSIM_LANE_ESC_CLK_EN_DATA_MASK	(0xf << 20)
+#define DSIM_BYTE_CLKEN			(1 << 24)
+#define DSIM_BYTE_CLK_SRC(x)		(((x) & 0x3) << 25)
+#define DSIM_BYTE_CLK_SRC_MASK		(0x3 << 25)
+#define DSIM_PLL_BYPASS			(1 << 27)
+#define DSIM_ESC_CLKEN			(1 << 28)
+#define DSIM_TX_REQUEST_HSCLK		(1 << 31)
+
+/* DSIM_CONFIG */
+#define DSIM_LANE_EN_CLK		(1 << 0)
+#define DSIM_LANE_EN(x)			(((x) & 0xf) << 1)
+#define DSIM_NUM_OF_DATA_LANE(x)	(((x) & 0x3) << 5)
+#define DSIM_SUB_PIX_FORMAT(x)		(((x) & 0x7) << 8)
+#define DSIM_MAIN_PIX_FORMAT_MASK	(0x7 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB888	(0x7 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB666	(0x6 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB666_P	(0x5 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB565	(0x4 << 12)
+#define DSIM_SUB_VC			(((x) & 0x3) << 16)
+#define DSIM_MAIN_VC			(((x) & 0x3) << 18)
+#define DSIM_HSA_MODE			(1 << 20)
+#define DSIM_HBP_MODE			(1 << 21)
+#define DSIM_HFP_MODE			(1 << 22)
+#define DSIM_HSE_MODE			(1 << 23)
+#define DSIM_AUTO_MODE			(1 << 24)
+#define DSIM_VIDEO_MODE			(1 << 25)
+#define DSIM_BURST_MODE			(1 << 26)
+#define DSIM_SYNC_INFORM		(1 << 27)
+#define DSIM_EOT_DISABLE		(1 << 28)
+#define DSIM_MFLUSH_VS			(1 << 29)
+
+/* DSIM_ESCMODE */
+#define DSIM_TX_TRIGGER_RST		(1 << 4)
+#define DSIM_TX_LPDT_LP			(1 << 6)
+#define DSIM_CMD_LPDT_LP		(1 << 7)
+#define DSIM_FORCE_BTA			(1 << 16)
+#define DSIM_FORCE_STOP_STATE		(1 << 20)
+#define DSIM_STOP_STATE_CNT(x)		(((x) & 0x7ff) << 21)
+#define DSIM_STOP_STATE_CNT_MASK	(0x7ff << 21)
+
+/* DSIM_MDRESOL */
+#define DSIM_MAIN_STAND_BY		(1 << 31)
+#define DSIM_MAIN_VRESOL(x)		(((x) & 0x7ff) << 16)
+#define DSIM_MAIN_HRESOL(x)		(((x) & 0X7ff) << 0)
+
+/* DSIM_MVPORCH */
+#define DSIM_CMD_ALLOW(x)		((x) << 28)
+#define DSIM_STABLE_VFP(x)		((x) << 16)
+#define DSIM_MAIN_VBP(x)		((x) << 0)
+#define DSIM_CMD_ALLOW_MASK		(0xf << 28)
+#define DSIM_STABLE_VFP_MASK		(0x7ff << 16)
+#define DSIM_MAIN_VBP_MASK		(0x7ff << 0)
+
+/* DSIM_MHPORCH */
+#define DSIM_MAIN_HFP(x)		((x) << 16)
+#define DSIM_MAIN_HBP(x)		((x) << 0)
+#define DSIM_MAIN_HFP_MASK		((0xffff) << 16)
+#define DSIM_MAIN_HBP_MASK		((0xffff) << 0)
+
+/* DSIM_MSYNC */
+#define DSIM_MAIN_VSA(x)		((x) << 22)
+#define DSIM_MAIN_HSA(x)		((x) << 0)
+#define DSIM_MAIN_VSA_MASK		((0x3ff) << 22)
+#define DSIM_MAIN_HSA_MASK		((0xffff) << 0)
+
+/* DSIM_SDRESOL */
+#define DSIM_SUB_STANDY(x)		((x) << 31)
+#define DSIM_SUB_VRESOL(x)		((x) << 16)
+#define DSIM_SUB_HRESOL(x)		((x) << 0)
+#define DSIM_SUB_STANDY_MASK		((0x1) << 31)
+#define DSIM_SUB_VRESOL_MASK		((0x7ff) << 16)
+#define DSIM_SUB_HRESOL_MASK		((0x7ff) << 0)
+
+/* DSIM_INTSRC */
+#define DSIM_INT_PLL_STABLE		(1 << 31)
+#define DSIM_INT_SW_RST_RELEASE		(1 << 30)
+#define DSIM_INT_SFR_FIFO_EMPTY		(1 << 29)
+#define DSIM_INT_BTA			(1 << 25)
+#define DSIM_INT_FRAME_DONE		(1 << 24)
+#define DSIM_INT_RX_TIMEOUT		(1 << 21)
+#define DSIM_INT_BTA_TIMEOUT		(1 << 20)
+#define DSIM_INT_RX_DONE		(1 << 18)
+#define DSIM_INT_RX_TE			(1 << 17)
+#define DSIM_INT_RX_ACK			(1 << 16)
+#define DSIM_INT_RX_ECC_ERR		(1 << 15)
+#define DSIM_INT_RX_CRC_ERR		(1 << 14)
+
+/* DSIM_FIFOCTRL */
+#define DSIM_FULL_H_SFR			(1 << 23)
+
+/* DSIM_PHYACCHR */
+#define DSIM_AFC_EN			(1 << 14)
+#define DSIM_AFC_CTL(x)			(((x) & 0x7) << 5)
+
+/* DSIM_PLLCTRL */
+#define DSIM_FREQ_BAND(x)		((x) << 24)
+#define DSIM_PLL_EN			(1 << 23)
+#define DSIM_PLL_P(x)			((x) << 13)
+#define DSIM_PLL_M(x)			((x) << 4)
+#define DSIM_PLL_S(x)			((x) << 1)
+
+#define DSI_MAX_BUS_WIDTH		4
+#define DSI_NUM_VIRTUAL_CHANNELS	4
+#define DSI_TX_FIFO_SIZE		2048
+#define DSI_RX_FIFO_SIZE		256
+#define DSI_XFER_TIMEOUT_MS		100
+#define DSI_RX_FIFO_EMPTY		0x30800002
+
+enum exynos_dsi_transfer_type {
+	EXYNOS_DSI_TX,
+	EXYNOS_DSI_RX,
+};
+
+struct exynos_dsi_transfer {
+	struct list_head list;
+	struct completion completed;
+	int result;
+	u8 type;
+	u8 data[2];
+
+	const u8 *tx_payload;
+	u16 tx_len;
+	u16 tx_done;
+
+	u8 *rx_payload;
+	u16 rx_len;
+	u16 rx_done;
+};
+
+struct exynos_dsi {
+	struct mipi_dsi_bus bus;
+	struct mipi_dsi_interface_params params;
+	bool streaming;
+	bool enabled;
+
+	struct platform_device *pdev;
+	struct phy *phy;
+	struct device *dev;
+	struct resource *res;
+	struct clk *pll_clk;
+	struct clk *bus_clk;
+	unsigned int irq;
+	void __iomem *reg_base;
+	struct regulator_bulk_data supplies[2];
+	struct exynos_dsi_platform_data *pd;
+	u32 intsrc;
+	wait_queue_head_t queue;
+
+	spinlock_t transfer_lock;
+	struct list_head transfer_list;
+};
+
+#define bus_to_dsi(_bus) container_of(_bus, struct exynos_dsi, bus)
+
+/*
+ * H/W control
+ */
+
+static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi)
+{
+	int ret;
+
+	ret = wait_event_timeout(dsi->queue,
+				 (dsi->intsrc & DSIM_INT_SW_RST_RELEASE),
+				 msecs_to_jiffies(300));
+	if (ret <= 0)
+		dev_err(dsi->dev, "timeout waiting for reset\n");
+
+	dsi->intsrc = 0;
+}
+
+static void exynos_dsi_reset(struct exynos_dsi *dsi)
+{
+	writel(DSIM_SWRST, dsi->reg_base + DSIM_SWRST_REG);
+}
+
+#ifndef MHZ
+#define MHZ	(1000*1000)
+#endif
+
+static const unsigned long freq_bands[] = {
+	100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
+	270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
+	510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
+	770 * MHZ, 870 * MHZ, 950 * MHZ,
+};
+
+static const int afc_settings[] = {
+	1, 0, 3, 2, 5, 4,
+};
+
+static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
+		unsigned long fin, unsigned long fout, u8 *p, u16 *m, u8 *s)
+{
+	unsigned long best_freq = 0;
+	u32 min_delta = 0xffffffff;
+	u8 p_min, p_max;
+	u8 _p, uninitialized_var(best_p);
+	u16 _m, uninitialized_var(best_m);
+	u8 _s, uninitialized_var(best_s);
+
+	p_min = DIV_ROUND_UP(fin, (12 * MHZ));
+	p_max = fin / (6 * MHZ);
+
+	for (_p = p_min; _p <= p_max; ++_p) {
+		for (_s = 0; _s <= 5; ++_s) {
+			u64 tmp;
+			u32 delta;
+			u16 div;
+
+			tmp = (u64)fout * (_p << _s);
+			do_div(tmp, fin);
+			_m = tmp;
+			if (_m < 41 || _m > 125)
+				continue;
+
+			tmp = (u64)_m * fin;
+			do_div(tmp, _p);
+			if (tmp < 500 * MHZ || tmp > 1000 * MHZ)
+				continue;
+
+			tmp = (u64)_m * fin;
+			div = (_p << _s);
+			do_div(tmp, div);
+
+			delta = abs(fout - tmp);
+			if (delta < min_delta) {
+				best_p = _p;
+				best_m = _m;
+				best_s = _s;
+				min_delta = delta;
+				best_freq = tmp;
+			}
+		}
+	}
+
+	if (best_freq) {
+		*p = best_p;
+		*m = best_m;
+		*s = best_s;
+	}
+
+	return best_freq;
+}
+
+static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
+					unsigned long freq)
+{
+	unsigned long fin, fout, fin_pll;
+	int timeout;
+	u8 p;
+	u16 m;
+	u8 s;
+	int band;
+	int afc;
+	u32 reg;
+
+	clk_set_rate(dsi->pll_clk, dsi->pd->pll_clk_rate);
+
+	fin = clk_get_rate(dsi->pll_clk);
+	if (!fin) {
+		dev_err(dsi->dev, "failed to get PLL clock frequency\n");
+		return 0;
+	}
+
+	dev_dbg(dsi->dev, "PLL input frequency: %lu\n", fin);
+
+	fout = exynos_dsi_pll_find_pms(dsi, fin, freq, &p, &m, &s);
+	if (!fout) {
+		dev_err(dsi->dev,
+			"failed to find PLL coefficients for requested frequency\n");
+		return -EFAULT;
+	}
+
+	dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
+
+	for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
+		if (fout < freq_bands[band])
+			break;
+
+	fin_pll = DIV_ROUND_CLOSEST(fin, p);
+	fin_pll /= MHZ;
+	if (fin_pll > 6)
+		fin_pll -= 6;
+	else
+		fin_pll = 0;
+	if (fin_pll >= ARRAY_SIZE(afc_settings))
+		fin_pll = ARRAY_SIZE(afc_settings) - 1;
+
+	afc = afc_settings[fin_pll];
+
+	dev_dbg(dsi->dev, "freq band %d, afc_setting %d\n", band, afc);
+
+	writel(dsi->pd->pll_stable_time, dsi->reg_base + DSIM_PLLTMR_REG);
+
+	reg = DSIM_AFC_CTL(afc) | DSIM_AFC_EN;
+	writel(reg, dsi->reg_base + DSIM_PHYACCHR_REG);
+
+	reg = DSIM_FREQ_BAND(band) | DSIM_PLL_EN
+			| DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
+	writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
+
+	timeout = 1000;
+	do {
+		if (timeout-- = 0) {
+			dev_err(dsi->dev, "PLL failed to stabilize\n");
+			return -EFAULT;
+		}
+		reg = readl(dsi->reg_base + DSIM_STATUS_REG);
+	} while ((reg & DSIM_PLL_STABLE) = 0);
+
+	return fout;
+}
+
+static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
+{
+	struct mipi_dsi_interface_params *params = &dsi->params;
+	unsigned long hs_clk, byte_clk, esc_clk;
+	unsigned long esc_div;
+	u32 reg;
+
+	hs_clk = exynos_dsi_set_pll(dsi, params->hs_clk_freq);
+	if (!hs_clk) {
+		dev_err(dsi->dev, "failed to configure DSI PLL\n");
+		return -EFAULT;
+	}
+
+	byte_clk = hs_clk / 8;
+	esc_div = DIV_ROUND_UP(byte_clk, params->esc_clk_freq);
+	esc_clk = byte_clk / esc_div;
+
+	if (esc_clk > 20 * MHZ) {
+		++esc_div;
+		esc_clk = byte_clk / esc_div;
+	}
+
+	dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n",
+						hs_clk, byte_clk, esc_clk);
+
+	reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG);
+	reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK
+			| DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS
+			| DSIM_BYTE_CLK_SRC_MASK);
+	reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN
+			| DSIM_ESC_PRESCALER(esc_div)
+			| DSIM_LANE_ESC_CLK_EN_CLK
+			| DSIM_LANE_ESC_CLK_EN_DATA(params->data_lanes)
+			| DSIM_BYTE_CLK_SRC(0)
+			| DSIM_TX_REQUEST_HSCLK;
+	writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG);
+
+	return 0;
+}
+
+static void exynos_dsi_disable_clock(struct exynos_dsi *dsi)
+{
+	u32 reg;
+
+	reg = readl(dsi->reg_base + DSIM_CLKCTRL_REG);
+	reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK |
+			DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN);
+	writel(reg, dsi->reg_base + DSIM_CLKCTRL_REG);
+
+	reg = readl(dsi->reg_base + DSIM_PLLCTRL_REG);
+	reg &= ~DSIM_PLL_EN;
+	writel(reg, dsi->reg_base + DSIM_PLLCTRL_REG);
+}
+
+static int exynos_dsi_init_link(struct exynos_dsi *dsi)
+{
+	struct mipi_dsi_interface_params *params = &dsi->params;
+	int timeout;
+	u32 reg;
+
+	/* Initialize FIFO pointers */
+	reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG);
+	reg &= ~0x1f;
+	writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG);
+
+	usleep_range(10000, 10000);
+
+	reg |= 0x1f;
+	writel(reg, dsi->reg_base + DSIM_FIFOCTRL_REG);
+
+	usleep_range(10000, 10000);
+
+	/* DSI configuration */
+	reg = 0;
+
+	if (params->mode & DSI_MODE_VIDEO) {
+		reg |= DSIM_VIDEO_MODE;
+
+		if (!(params->mode & DSI_MODE_VSYNC_FLUSH))
+			reg |= DSIM_MFLUSH_VS;
+		if (!(params->mode & DSI_MODE_EOT_PACKET))
+			reg |= DSIM_EOT_DISABLE;
+		if (params->mode & DSI_MODE_VIDEO_SYNC_PULSE)
+			reg |= DSIM_SYNC_INFORM;
+		if (params->mode & DSI_MODE_VIDEO_BURST)
+			reg |= DSIM_BURST_MODE;
+		if (params->mode & DSI_MODE_VIDEO_AUTO_VERT)
+			reg |= DSIM_AUTO_MODE;
+		if (params->mode & DSI_MODE_VIDEO_HSE)
+			reg |= DSIM_HSE_MODE;
+		if (!(params->mode & DSI_MODE_VIDEO_HFP))
+			reg |= DSIM_HFP_MODE;
+		if (!(params->mode & DSI_MODE_VIDEO_HBP))
+			reg |= DSIM_HBP_MODE;
+		if (!(params->mode & DSI_MODE_VIDEO_HSA))
+			reg |= DSIM_HSA_MODE;
+	}
+
+	switch (params->format) {
+	case DSI_FMT_RGB888:
+		reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
+		break;
+	case DSI_FMT_RGB666:
+		reg |= DSIM_MAIN_PIX_FORMAT_RGB666;
+		break;
+	case DSI_FMT_RGB666_PACKED:
+		reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P;
+		break;
+	case DSI_FMT_RGB565:
+		reg |= DSIM_MAIN_PIX_FORMAT_RGB565;
+		break;
+	default:
+		dev_err(dsi->dev, "invalid pixel format\n");
+		return -EINVAL;
+	}
+
+	switch (params->data_lanes) {
+	case 0x1:
+		reg |= DSIM_NUM_OF_DATA_LANE(0);
+		break;
+	case 0x3:
+		reg |= DSIM_NUM_OF_DATA_LANE(1);
+		break;
+	case 0x7:
+		reg |= DSIM_NUM_OF_DATA_LANE(2);
+		break;
+	case 0xf:
+		reg |= DSIM_NUM_OF_DATA_LANE(3);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
+
+	reg |= DSIM_LANE_EN_CLK;
+	writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
+
+	reg |= DSIM_LANE_EN(params->data_lanes);
+	writel(reg, dsi->reg_base + DSIM_CONFIG_REG);
+
+	/* Check clock and data lane state are stop state */
+	timeout = 100;
+	do {
+		if (timeout-- = 0) {
+			dev_err(dsi->dev, "waiting for bus lanes timed out\n");
+			return -EFAULT;
+		}
+
+		reg = readl(dsi->reg_base + DSIM_STATUS_REG);
+		if ((reg & DSIM_STOP_STATE_DAT(params->data_lanes))
+		    != DSIM_STOP_STATE_DAT(params->data_lanes))
+			continue;
+	} while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK)));
+
+	reg = readl(dsi->reg_base + DSIM_ESCMODE_REG);
+	reg &= ~DSIM_STOP_STATE_CNT_MASK;
+	reg |= DSIM_STOP_STATE_CNT(dsi->pd->stop_holding_cnt);
+	writel(reg, dsi->reg_base + DSIM_ESCMODE_REG);
+
+	reg = DSIM_BTA_TOUT(dsi->pd->bta_timeout)
+					| DSIM_LPDR_TOUT(dsi->pd->rx_timeout);
+	writel(reg, dsi->reg_base + DSIM_TIMEOUT_REG);
+
+	return 0;
+}
+
+static int exynos_dsi_set_display_mode(struct exynos_dsi *dsi,
+				       const struct videomode *mode)
+{
+	struct mipi_dsi_interface_params *params = &dsi->params;
+	u32 reg;
+
+	if (params->mode & DSI_MODE_VIDEO) {
+		reg = DSIM_CMD_ALLOW(params->cmd_allow)
+			| DSIM_STABLE_VFP(mode->vfront_porch)
+			| DSIM_MAIN_VBP(mode->vback_porch);
+		writel(reg, dsi->reg_base + DSIM_MVPORCH_REG);
+
+		reg = DSIM_MAIN_HFP(mode->hfront_porch)
+			| DSIM_MAIN_HBP(mode->hback_porch);
+		writel(reg, dsi->reg_base + DSIM_MHPORCH_REG);
+
+		reg = DSIM_MAIN_VSA(mode->vsync_len)
+			| DSIM_MAIN_HSA(mode->hsync_len);
+		writel(reg, dsi->reg_base + DSIM_MSYNC_REG);
+	}
+
+	reg = DSIM_MAIN_HRESOL(mode->hactive) | DSIM_MAIN_VRESOL(mode->vactive);
+	writel(reg, dsi->reg_base + DSIM_MDRESOL_REG);
+
+	dev_dbg(dsi->dev, "LCD width = %d, height = %d\n",
+		mode->hactive, mode->vactive);
+
+	return 0;
+}
+
+static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable)
+{
+	u32 reg;
+
+	reg = readl(dsi->reg_base + DSIM_MDRESOL_REG);
+	if (enable)
+		reg |= DSIM_MAIN_STAND_BY;
+	else
+		reg &= ~DSIM_MAIN_STAND_BY;
+	writel(reg, dsi->reg_base + DSIM_MDRESOL_REG);
+}
+
+/*
+ * FIFO
+ */
+
+static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi)
+{
+	int timeout = 20000;
+
+	do {
+		u32 reg = readl(dsi->reg_base + DSIM_FIFOCTRL_REG);
+
+		if (!(reg & DSIM_FULL_H_SFR))
+			return 0;
+
+		if (!cond_resched())
+			usleep_range(950, 1050);
+	} while (--timeout);
+
+	return -ETIMEDOUT;
+}
+
+static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
+					struct exynos_dsi_transfer *xfer)
+{
+	const u8 *payload = xfer->tx_payload + xfer->tx_done;
+	u16 length = xfer->tx_len - xfer->tx_done;
+	bool first = !xfer->tx_done;
+	u32 reg;
+
+	dev_dbg(dsi->dev,
+		"< xfer %p, tx_len %u, tx_done %u, rx_len %u, rx_done %u\n",
+		xfer, xfer->tx_len, xfer->tx_done, xfer->rx_len, xfer->rx_done);
+
+	if (length > DSI_TX_FIFO_SIZE)
+		length = DSI_TX_FIFO_SIZE;
+
+	xfer->tx_done += length;
+
+	/* Send payload */
+	while (length >= 4) {
+		reg = (payload[3] << 24) | (payload[2] << 16)
+					| (payload[1] << 8) | payload[0];
+		writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG);
+		payload += 4;
+		length -= 4;
+	}
+
+	reg = 0;
+	switch (length) {
+	case 3:
+		reg |= payload[2] << 16;
+		/* Fall through */
+	case 2:
+		reg |= payload[1] << 8;
+		/* Fall through */
+	case 1:
+		reg |= payload[0];
+		writel(reg, dsi->reg_base + DSIM_PAYLOAD_REG);
+		break;
+	case 0:
+		/* Do nothing */
+		break;
+	}
+
+	/* Send packet header */
+	if (!first)
+		return;
+
+	if (xfer->rx_len) {
+		reg = (xfer->rx_len << 8)
+				| MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE;
+		if (exynos_dsi_wait_for_hdr_fifo(dsi)) {
+			dev_err(dsi->dev,
+					"waiting for header FIFO timed out\n");
+			return;
+		}
+		writel(reg, dsi->reg_base + DSIM_PKTHDR_REG);
+	}
+
+	reg = (xfer->data[1] << 16) | (xfer->data[0] << 8) | xfer->type;
+	if (exynos_dsi_wait_for_hdr_fifo(dsi)) {
+		dev_err(dsi->dev, "waiting for header FIFO timed out\n");
+		return;
+	}
+	writel(reg, dsi->reg_base + DSIM_PKTHDR_REG);
+}
+
+static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
+					struct exynos_dsi_transfer *xfer)
+{
+	u8 *payload = xfer->rx_payload + xfer->rx_done;
+	bool first = !xfer->rx_done;
+	u16 length;
+	u32 reg;
+
+	if (first) {
+		reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+
+		switch (reg & 0x3f) {
+		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
+		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
+			if (xfer->rx_len >= 2) {
+				payload[1] = reg >> 16;
+				++xfer->rx_done;
+			}
+			/* Fall through */
+		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
+		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
+			payload[0] = reg >> 8;
+			++xfer->rx_done;
+			xfer->rx_len = xfer->rx_done;
+			xfer->result = 0;
+			goto clear_fifo;
+		}
+
+		length = (reg >> 8) & 0xffff;
+		if (length > xfer->rx_len) {
+			dev_err(dsi->dev,
+				"response too long (expected %u, got %u bytes)\n",
+				xfer->rx_len, length);
+			xfer->rx_len = 0;
+			xfer->rx_done = 0;
+			xfer->result = -EFAULT;
+			goto clear_fifo;
+		}
+		if (length < xfer->rx_len)
+			xfer->rx_len = length;
+	}
+
+	length = xfer->rx_len - xfer->rx_done;
+	xfer->rx_done += length;
+
+	/* Receive payload */
+	while (length >= 4) {
+		reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+		payload[0] = (reg >>  0) & 0xff;
+		payload[1] = (reg >>  8) & 0xff;
+		payload[2] = (reg >> 16) & 0xff;
+		payload[3] = (reg >> 24) & 0xff;
+		payload += 4;
+		length -= 4;
+	}
+
+	if (length) {
+		reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+		switch (length) {
+		case 3:
+			payload[2] = (reg >> 16) & 0xff;
+			/* Fall through */
+		case 2:
+			payload[1] = (reg >> 8) & 0xff;
+			/* Fall through */
+		case 1:
+			payload[0] = reg & 0xff;
+		}
+	}
+
+	if (xfer->rx_done = xfer->rx_len)
+		xfer->result = 0;
+
+clear_fifo:
+	length = DSI_RX_FIFO_SIZE / 4;
+	do {
+		reg = readl(dsi->reg_base + DSIM_RXFIFO_REG);
+		if (reg = DSI_RX_FIFO_EMPTY)
+			break;
+	} while (--length);
+}
+
+/*
+ * Transfer
+ */
+
+static void exynos_dsi_transfer_start(struct exynos_dsi *dsi)
+{
+	unsigned long flags;
+	struct exynos_dsi_transfer *xfer;
+	bool start = false;
+
+again:
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	if (list_empty(&dsi->transfer_list)) {
+		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+		return;
+	}
+
+	xfer = list_first_entry(&dsi->transfer_list,
+					struct exynos_dsi_transfer, list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	if (xfer->tx_len && xfer->tx_done = xfer->tx_len)
+		/* waiting for RX */
+		return;
+
+	exynos_dsi_send_to_fifo(dsi, xfer);
+
+	if (xfer->tx_len || xfer->rx_len)
+		return;
+
+	xfer->result = 0;
+	complete(&xfer->completed);
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	list_del_init(&xfer->list);
+	start = !list_empty(&dsi->transfer_list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	if (start)
+		goto again;
+}
+
+static bool exynos_dsi_transfer_finish(struct exynos_dsi *dsi)
+{
+	struct exynos_dsi_transfer *xfer;
+	unsigned long flags;
+	bool start = true;
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	if (list_empty(&dsi->transfer_list)) {
+		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+		return false;
+	}
+
+	xfer = list_first_entry(&dsi->transfer_list,
+					struct exynos_dsi_transfer, list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	dev_dbg(dsi->dev,
+		"> xfer %p, tx_len %u, tx_done %u, rx_len %u, rx_done %u\n",
+		xfer, xfer->tx_len, xfer->tx_done, xfer->rx_len, xfer->rx_done);
+
+	if (xfer->tx_done != xfer->tx_len)
+		return true;
+
+	if (xfer->rx_done != xfer->rx_len)
+		exynos_dsi_read_from_fifo(dsi, xfer);
+
+	if (xfer->rx_done != xfer->rx_len)
+		return true;
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	list_del_init(&xfer->list);
+	start = !list_empty(&dsi->transfer_list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	if (!xfer->rx_len)
+		xfer->result = 0;
+	complete(&xfer->completed);
+
+	return start;
+}
+
+static void exynos_dsi_remove_transfer(struct exynos_dsi *dsi,
+					struct exynos_dsi_transfer *xfer)
+{
+	unsigned long flags;
+	bool start;
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	if (!list_empty(&dsi->transfer_list)
+	    && xfer = list_first_entry(&dsi->transfer_list,
+					struct exynos_dsi_transfer, list)) {
+		list_del_init(&xfer->list);
+		start = !list_empty(&dsi->transfer_list);
+		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+		if (start)
+			exynos_dsi_transfer_start(dsi);
+		return;
+	}
+
+	list_del_init(&xfer->list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+}
+
+static int exynos_dsi_transfer(struct exynos_dsi *dsi,
+					struct exynos_dsi_transfer *xfer)
+{
+	unsigned long flags;
+	bool stopped;
+
+	xfer->tx_done = 0;
+	xfer->rx_done = 0;
+	xfer->result = -ETIMEDOUT;
+	init_completion(&xfer->completed);
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	stopped = list_empty(&dsi->transfer_list);
+	list_add_tail(&xfer->list, &dsi->transfer_list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	if (stopped)
+		exynos_dsi_transfer_start(dsi);
+
+	wait_for_completion_timeout(&xfer->completed,
+				msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
+	if (xfer->result = -ETIMEDOUT) {
+		exynos_dsi_remove_transfer(dsi, xfer);
+		dev_err(dsi->dev, "xfer timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	/* Also covers hardware timeout condition */
+	return xfer->result;
+}
+
+/*
+ * Interrupt handler
+ */
+
+static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
+{
+	struct exynos_dsi *dsi = dev_id;
+	u32 status;
+
+	status = readl(dsi->reg_base + DSIM_INTSRC_REG);
+	if (!status) {
+		static unsigned long int j;
+		if (printk_timed_ratelimit(&j, 500))
+			dev_warn(dsi->dev, "spurious interrupt\n");
+		return IRQ_HANDLED;
+	}
+	writel(status, dsi->reg_base + DSIM_INTSRC_REG);
+
+	dev_dbg(dsi->dev, "%s: status = %08x\n", __func__, status);
+
+	if (status & DSIM_INT_SW_RST_RELEASE) {
+		u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY);
+		writel(mask, dsi->reg_base + DSIM_INTMSK_REG);
+		dsi->intsrc = status;
+		wake_up(&dsi->queue);
+		return IRQ_HANDLED;
+	}
+
+	if (exynos_dsi_transfer_finish(dsi))
+		exynos_dsi_transfer_start(dsi);
+
+	return IRQ_HANDLED;
+}
+
+static int exynos_dsi_set_stream(struct mipi_dsi_bus *bus,
+				 struct mipi_dsi_device *dev, bool on)
+{
+	struct exynos_dsi *dsi = bus_to_dsi(bus);
+
+	if (!dsi->enabled)
+		return -EINVAL;
+
+	if (on)
+		exynos_dsi_set_display_mode(dsi, &dev->vm);
+	exynos_dsi_set_display_enable(dsi, on);
+	dsi->streaming = on;
+
+	return 0;
+}
+
+/* enable/disable dsi bus */
+static int exynos_dsi_enable(struct exynos_dsi *dsi)
+{
+	int ret;
+
+	if (dsi->enabled)
+		return 0;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+	if (ret < 0)
+		return ret;
+
+	clk_prepare_enable(dsi->bus_clk);
+	clk_prepare_enable(dsi->pll_clk);
+
+	phy_power_on(dsi->phy);
+
+	exynos_dsi_enable_clock(dsi);
+
+	dsi->intsrc = 0;
+	exynos_dsi_reset(dsi);
+	enable_irq(dsi->irq);
+	exynos_dsi_wait_for_reset(dsi);
+
+	exynos_dsi_init_link(dsi);
+
+	dsi->enabled = true;
+
+	return 0;
+}
+
+static int exynos_dsi_disable(struct exynos_dsi *dsi)
+{
+	int ret;
+
+	if (!dsi->enabled)
+		return 0;
+
+	if (dsi->streaming)
+		return -EBUSY;
+
+	dsi->enabled = false;
+
+	exynos_dsi_disable_clock(dsi);
+
+	disable_irq(dsi->irq);
+
+	phy_power_off(dsi->phy);
+
+	clk_disable_unprepare(dsi->pll_clk);
+	clk_disable_unprepare(dsi->bus_clk);
+
+	ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int exynos_dsi_set_power(struct mipi_dsi_bus *bus,
+				struct mipi_dsi_device *dev, bool on)
+{
+	struct exynos_dsi *dsi = bus_to_dsi(bus);
+
+	if (on) {
+		dsi->params = dev->params;
+		return exynos_dsi_enable(dsi);
+	} else {
+		return exynos_dsi_disable(dsi);
+	}
+}
+
+static bool exynos_dsi_is_short_dsi_type(u8 type)
+{
+	return ((type & 0x0f) <= 8);
+}
+
+static int exynos_dsi_bus_transfer(struct mipi_dsi_bus *bus,
+				   struct mipi_dsi_device *dev, u8 type,
+				   const u8 *tx_buf, size_t tx_len,
+				   u8 *rx_buf, size_t rx_len)
+{
+	struct exynos_dsi *dsi = bus_to_dsi(bus);
+	struct exynos_dsi_transfer xfer;
+
+	if (!tx_len)
+		return -EINVAL;
+
+	xfer.type = type;
+
+	if (exynos_dsi_is_short_dsi_type(type)) {
+		if (tx_len > 2)
+			return -EINVAL;
+		xfer.tx_len = 0;
+		xfer.data[0] = tx_buf[0];
+		xfer.data[1] = (tx_len = 2) ? tx_buf[1] : 0;
+	} else {
+		xfer.tx_len = tx_len;
+		xfer.data[0] = tx_len & 0xff;
+		xfer.data[1] = tx_len >> 8;
+		xfer.tx_payload = tx_buf;
+	}
+
+	xfer.rx_len = rx_len;
+	xfer.rx_payload = rx_buf;
+
+	return exynos_dsi_transfer(dsi, &xfer);
+}
+
+static const struct mipi_dsi_bus_ops exynos_dsi_ops = {
+	.set_power = exynos_dsi_set_power,
+	.set_stream = exynos_dsi_set_stream,
+	.transfer = exynos_dsi_bus_transfer,
+};
+
+#ifdef CONFIG_OF
+/*
+ * Device Tree
+ */
+
+static struct exynos_dsi_platform_data *exynos_dsi_parse_dt(
+						struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct exynos_dsi_platform_data *dsi_pd;
+	struct device *dev = &pdev->dev;
+	const __be32 *prop_data;
+
+	dsi_pd = kzalloc(sizeof(*dsi_pd), GFP_KERNEL);
+	if (!dsi_pd) {
+		dev_err(dev, "failed to allocate dsi platform data\n");
+		return NULL;
+	}
+
+	prop_data = of_get_property(node, "samsung,pll-stable-time", NULL);
+	if (!prop_data) {
+		dev_err(dev, "failed to get pll-stable-time property\n");
+		goto err_free_pd;
+	}
+	dsi_pd->pll_stable_time = be32_to_cpu(*prop_data);
+
+	prop_data = of_get_property(node, "samsung,stop-holding-count", NULL);
+	if (!prop_data) {
+		dev_err(dev, "failed to get stop-holding-count property\n");
+		goto err_free_pd;
+	}
+	dsi_pd->stop_holding_cnt = be32_to_cpu(*prop_data);
+
+	prop_data = of_get_property(node, "samsung,bta-timeout", NULL);
+	if (!prop_data) {
+		dev_err(dev, "failed to get bta-timeout property\n");
+		goto err_free_pd;
+	}
+	dsi_pd->bta_timeout = be32_to_cpu(*prop_data);
+
+	prop_data = of_get_property(node, "samsung,rx-timeout", NULL);
+	if (!prop_data) {
+		dev_err(dev, "failed to get rx-timeout property\n");
+		goto err_free_pd;
+	}
+	dsi_pd->rx_timeout = be32_to_cpu(*prop_data);
+
+	prop_data = of_get_property(node, "samsung,pll-clk-freq", NULL);
+	if (!prop_data) {
+		dev_err(dev, "failed to get pll-clk-freq property\n");
+		goto err_free_pd;
+	}
+	dsi_pd->pll_clk_rate = be32_to_cpu(*prop_data);
+
+	return dsi_pd;
+
+err_free_pd:
+	kfree(dsi_pd);
+
+	return NULL;
+}
+
+static struct of_device_id exynos_dsi_of_match[] = {
+	{ .compatible = "samsung,exynos4210-mipi-dsi" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
+#else
+static struct exynos_dsi_platform_data *exynos_dsi_parse_dt(
+						struct platform_device *pdev)
+{
+	return NULL;
+}
+#endif
+
+/*
+ * Platform driver
+ */
+
+static int exynos_dsi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct exynos_dsi *dsi;
+	int ret;
+
+	dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL);
+	if (!dsi) {
+		dev_err(&pdev->dev, "failed to allocate dsi object.\n");
+		return -ENOMEM;
+	}
+
+	init_waitqueue_head(&dsi->queue);
+	spin_lock_init(&dsi->transfer_lock);
+	INIT_LIST_HEAD(&dsi->transfer_list);
+
+	dsi->bus.ops = &exynos_dsi_ops;
+	dsi->bus.dev = &pdev->dev;
+
+	dsi->pdev = pdev;
+	dsi->dev = &pdev->dev;
+	dsi->pd = pdev->dev.platform_data;
+
+	if (dsi->pd = NULL && pdev->dev.of_node)
+		dsi->pd = exynos_dsi_parse_dt(pdev);
+
+	if (dsi->pd = NULL) {
+		dev_err(&pdev->dev, "failed to get platform data for dsi.\n");
+		return -EINVAL;
+	}
+
+	dsi->supplies[0].supply = "vdd11";
+	dsi->supplies[1].supply = "vdd18";
+	ret = devm_regulator_bulk_get(&pdev->dev,
+				ARRAY_SIZE(dsi->supplies), dsi->supplies);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret);
+		return ret;
+	}
+
+	dsi->pll_clk = devm_clk_get(&pdev->dev, "pll_clk");
+	if (IS_ERR(dsi->pll_clk)) {
+		dev_err(&pdev->dev, "failed to get dsi pll input clock\n");
+		return -ENODEV;
+	}
+
+	dsi->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
+	if (IS_ERR(dsi->bus_clk)) {
+		dev_err(&pdev->dev, "failed to get dsi bus clock\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "failed to get io memory region\n");
+		return -ENODEV;
+	}
+
+	dsi->reg_base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!dsi->reg_base) {
+		dev_err(&pdev->dev, "failed to remap io region\n");
+		return -ENOMEM;
+	}
+
+	dsi->phy = devm_phy_get(&pdev->dev, "dsim");
+	if (IS_ERR(dsi->phy))
+		return PTR_ERR(dsi->phy);
+
+	platform_set_drvdata(pdev, dsi);
+
+	dsi->irq = platform_get_irq(pdev, 0);
+	if (dsi->irq < 0) {
+		dev_err(&pdev->dev, "failed to request dsi irq resource\n");
+		return -EINVAL;
+	}
+
+	irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
+	ret = devm_request_threaded_irq(&pdev->dev, dsi->irq, NULL,
+		exynos_dsi_irq, IRQF_ONESHOT, dev_name(&pdev->dev), dsi);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to request dsi irq\n");
+		return ret;
+	}
+
+	return of_mipi_dsi_register_devices(&dsi->bus);
+}
+
+static int exynos_dsi_remove(struct platform_device *pdev)
+{
+	struct exynos_dsi *dsi = dev_get_drvdata(&pdev->dev);
+
+	mipi_dsi_unregister_devices(&dsi->bus);
+
+	return 0;
+}
+/*
+ * Power management
+ */
+
+#ifdef CONFIG_PM_SLEEP
+static int exynos_dsi_suspend(struct device *dev)
+{
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+
+	return exynos_dsi_disable(dsi);
+}
+
+static int exynos_dsi_resume(struct device *dev)
+{
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+
+	return exynos_dsi_enable(dsi);
+}
+#endif
+
+static const struct dev_pm_ops exynos_dsi_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume)
+};
+
+/*
+ * Module
+ */
+
+static struct platform_driver exynos_dsi_driver = {
+	.probe = exynos_dsi_probe,
+	.remove = exynos_dsi_remove,
+	.driver = {
+		   .name = "exynos-dsi",
+		   .owner = THIS_MODULE,
+		   .pm = &exynos_dsi_pm_ops,
+		   .of_match_table = of_match_ptr(exynos_dsi_of_match),
+	},
+};
+
+module_platform_driver(exynos_dsi_driver);
+
+MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
+MODULE_LICENSE("GPL");
diff --git a/include/video/mipi-dsi-exynos.h b/include/video/mipi-dsi-exynos.h
new file mode 100644
index 0000000..548c473
--- /dev/null
+++ b/include/video/mipi-dsi-exynos.h
@@ -0,0 +1,41 @@
+/* include/video/mipi-dsi-exynos.h
+ *
+ * Platform data header for Samsung SoC MIPI-DSIM.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd
+ *
+ * InKi Dae <inki.dae@samsung.com>
+ * Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef _MIPI_DSI_EXYNOS_H
+#define _MIPI_DSI_EXYNOS_H
+
+#include <linux/device.h>
+
+/*
+ * struct exynos_dsi_platform_data - interface to platform data
+ *	for mipi-dsi driver.
+ *
+ * TODO
+ */
+struct exynos_dsi_platform_data {
+	unsigned int enabled;
+
+	int (*phy_enable)(struct platform_device *pdev, bool on);
+
+	unsigned int pll_stable_time;
+	unsigned long pll_clk_rate;
+	unsigned long esc_clk_rate;
+	unsigned short stop_holding_cnt;
+	unsigned char bta_timeout;
+	unsigned short rx_timeout;
+};
+
+int s5p_dsim_phy_enable(struct platform_device *pdev, bool on);
+
+#endif /* _MIPI_DSI_EXYNOS_H */
-- 
1.8.1.2


^ permalink raw reply related

* [RFC PATCH 1/4] mipi-dsi-bus: add MIPI DSI bus support
From: Andrzej Hajda @ 2013-09-24 14:23 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Andrzej Hajda, dri-devel, linux-fbdev, linux-media, Kyungmin Park
In-Reply-To: <1380032596-18612-1-git-send-email-a.hajda@samsung.com>

MIPI DSI is a high-speed serial interface to transmit
data from/to host to display module.

Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/video/display/Kconfig        |   4 +
 drivers/video/display/Makefile       |   1 +
 drivers/video/display/mipi-dsi-bus.c | 332 +++++++++++++++++++++++++++++++++++
 include/video/display.h              |   3 +
 include/video/mipi-dsi-bus.h         | 144 +++++++++++++++
 5 files changed, 484 insertions(+)
 create mode 100644 drivers/video/display/mipi-dsi-bus.c
 create mode 100644 include/video/mipi-dsi-bus.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 9b482a8..619b05d 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -20,6 +20,10 @@ config DISPLAY_MIPI_DBI
 	tristate
 	default n
 
+config DISPLAY_MIPI_DSI
+	tristate
+	default n
+
 config DISPLAY_PANEL_DPI
 	tristate "DPI (Parallel) Display Panels"
 	---help---
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index d03c64a..b323fd4 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -3,6 +3,7 @@ display-y					:= display-core.o \
 obj-$(CONFIG_DISPLAY_CORE)			+= display.o
 obj-$(CONFIG_DISPLAY_CONNECTOR_VGA)		+= con-vga.o
 obj-$(CONFIG_DISPLAY_MIPI_DBI)			+= mipi-dbi-bus.o
+obj-$(CONFIG_DISPLAY_MIPI_DSI)			+= mipi-dsi-bus.o
 obj-$(CONFIG_DISPLAY_PANEL_DPI)			+= panel-dpi.o
 obj-$(CONFIG_DISPLAY_PANEL_R61505)		+= panel-r61505.o
 obj-$(CONFIG_DISPLAY_PANEL_R61517)		+= panel-r61517.o
diff --git a/drivers/video/display/mipi-dsi-bus.c b/drivers/video/display/mipi-dsi-bus.c
new file mode 100644
index 0000000..a194d92
--- /dev/null
+++ b/drivers/video/display/mipi-dsi-bus.c
@@ -0,0 +1,332 @@
+/*
+ * MIPI DSI Bus
+ *
+ * Copyright (C) 2012, Samsung Electronics, Co., Ltd.
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <video/mipi_display.h>
+#include <video/mipi-dsi-bus.h>
+
+/* -----------------------------------------------------------------------------
+ * Bus operations
+ */
+
+int mipi_dsi_set_power(struct mipi_dsi_device *dev, bool on)
+{
+	const struct mipi_dsi_bus_ops *ops = dev->bus->ops;
+
+	if (!ops->set_power)
+		return 0;
+
+	return ops->set_power(dev->bus, dev, on);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_set_power);
+
+int mipi_dsi_set_stream(struct mipi_dsi_device *dev, bool on)
+{
+	const struct mipi_dsi_bus_ops *ops = dev->bus->ops;
+
+	if (!ops->set_stream)
+		return 0;
+
+	return ops->set_stream(dev->bus, dev, on);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_set_stream);
+
+int mipi_dsi_dcs_write(struct mipi_dsi_device *dev, int channel, const u8 *data,
+		       size_t len)
+{
+	const struct mipi_dsi_bus_ops *ops = dev->bus->ops;
+	u8 type = channel << 6;
+
+	if (!ops->transfer)
+		return -EINVAL;
+
+	switch (len) {
+	case 0:
+		return -EINVAL;
+	case 1:
+		type |= MIPI_DSI_DCS_SHORT_WRITE;
+		break;
+	case 2:
+		type |= MIPI_DSI_DCS_SHORT_WRITE_PARAM;
+		break;
+	default:
+		type |= MIPI_DSI_DCS_LONG_WRITE;
+	}
+
+	return ops->transfer(dev->bus, dev, type, data, len, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_dcs_write);
+
+int mipi_dsi_dcs_read(struct mipi_dsi_device *dev, int channel, u8 cmd,
+		      u8 *data, size_t len)
+{
+	const struct mipi_dsi_bus_ops *ops = dev->bus->ops;
+	u8 type = MIPI_DSI_DCS_READ | (channel << 6);
+
+	if (!ops->transfer)
+		return -EINVAL;
+
+	return ops->transfer(dev->bus, dev, type, &cmd, 1, data, len);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_dcs_read);
+
+/* -----------------------------------------------------------------------------
+ * Bus type
+ */
+
+static const struct mipi_dsi_device_id *
+mipi_dsi_match_id(const struct mipi_dsi_device_id *id,
+		  struct mipi_dsi_device *dev)
+{
+	while (id->name[0]) {
+		if (strcmp(dev->name, id->name) = 0) {
+			dev->id_entry = id;
+			return id;
+		}
+		id++;
+	}
+	return NULL;
+}
+
+static int mipi_dsi_match(struct device *_dev, struct device_driver *_drv)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+	struct mipi_dsi_driver *drv = to_mipi_dsi_driver(_drv);
+
+	if (of_driver_match_device(_dev, _drv))
+		return 1;
+
+	if (drv->id_table)
+		return mipi_dsi_match_id(drv->id_table, dev) != NULL;
+
+	return (strcmp(dev->name, _drv->name) = 0);
+}
+
+static ssize_t modalias_show(struct device *_dev, struct device_attribute *a,
+			     char *buf)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+	int len = snprintf(buf, PAGE_SIZE, MIPI_DSI_MODULE_PREFIX "%s\n",
+			   dev->name);
+
+	return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static struct device_attribute mipi_dsi_dev_attrs[] = {
+	__ATTR_RO(modalias),
+	__ATTR_NULL,
+};
+
+static int mipi_dsi_uevent(struct device *_dev, struct kobj_uevent_env *env)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+
+	add_uevent_var(env, "MODALIAS=%s%s", MIPI_DSI_MODULE_PREFIX,
+		       dev->name);
+	return 0;
+}
+
+static const struct dev_pm_ops mipi_dsi_dev_pm_ops = {
+	.runtime_suspend = pm_generic_runtime_suspend,
+	.runtime_resume = pm_generic_runtime_resume,
+	.suspend = pm_generic_suspend,
+	.resume = pm_generic_resume,
+	.freeze = pm_generic_freeze,
+	.thaw = pm_generic_thaw,
+	.poweroff = pm_generic_poweroff,
+	.restore = pm_generic_restore,
+};
+
+static struct bus_type mipi_dsi_bus_type = {
+	.name		= "mipi-dsi",
+	.dev_attrs	= mipi_dsi_dev_attrs,
+	.match		= mipi_dsi_match,
+	.uevent		= mipi_dsi_uevent,
+	.pm		= &mipi_dsi_dev_pm_ops,
+};
+
+void mipi_dsi_dev_release(struct device *_dev)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+	kfree(dev);
+}
+
+static struct device_type mipi_dsi_dev_type = {
+	.release	= mipi_dsi_dev_release,
+};
+
+
+/* -----------------------------------------------------------------------------
+ * Device and driver (un)registration
+ */
+
+/**
+ * mipi_dsi_device_register - register a DSI device
+ * @dev: DSI device we're registering
+ */
+int mipi_dsi_device_register(struct mipi_dsi_device *dev,
+			      struct mipi_dsi_bus *bus)
+{
+	device_initialize(&dev->dev);
+
+	dev->bus = bus;
+	dev->dev.bus = &mipi_dsi_bus_type;
+	dev->dev.parent = bus->dev;
+	dev->dev.type = &mipi_dsi_dev_type;
+
+	if (dev->id != -1)
+		dev_set_name(&dev->dev, "%s.%d", dev->name,  dev->id);
+	else
+		dev_set_name(&dev->dev, "%s", dev->name);
+
+	return device_add(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_device_register);
+
+/**
+ * mipi_dsi_device_unregister - unregister a DSI device
+ * @dev: DSI device we're unregistering
+ */
+void mipi_dsi_device_unregister(struct mipi_dsi_device *dev)
+{
+	device_del(&dev->dev);
+	put_device(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_device_unregister);
+
+static int mipi_dsi_drv_probe(struct device *_dev)
+{
+	struct mipi_dsi_driver *drv = to_mipi_dsi_driver(_dev->driver);
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+
+	return drv->probe(dev);
+}
+
+static int mipi_dsi_drv_remove(struct device *_dev)
+{
+	struct mipi_dsi_driver *drv = to_mipi_dsi_driver(_dev->driver);
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+
+	return drv->remove(dev);
+}
+
+/**
+ * mipi_dsi_driver_register - register a driver for DSI devices
+ * @drv: DSI driver structure
+ */
+int mipi_dsi_driver_register(struct mipi_dsi_driver *drv)
+{
+	drv->driver.bus = &mipi_dsi_bus_type;
+	if (drv->probe)
+		drv->driver.probe = mipi_dsi_drv_probe;
+	if (drv->remove)
+		drv->driver.remove = mipi_dsi_drv_remove;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_driver_register);
+
+/**
+ * mipi_dsi_driver_unregister - unregister a driver for DSI devices
+ * @drv: DSI driver structure
+ */
+void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_driver_unregister);
+
+struct mipi_dsi_device *of_mipi_dsi_register_device(struct mipi_dsi_bus *bus,
+						    struct device_node *node)
+{
+	struct mipi_dsi_device *d = NULL;
+	int ret;
+
+	d = kzalloc(sizeof(*d), GFP_KERNEL);
+	if (!d)
+		return ERR_PTR(-ENOMEM);
+
+	ret = of_modalias_node(node, d->name, sizeof(d->name));
+	if (ret) {
+		dev_err(bus->dev, "modalias failure on %s\n", node->full_name);
+		goto err_free;
+	}
+
+	d->dev.of_node = of_node_get(node);
+	if (!d->dev.of_node)
+		goto err_free;
+
+	ret = mipi_dsi_device_register(d, bus);
+
+	if (!ret)
+		return d;
+
+	of_node_put(node);
+err_free:
+	kfree(d);
+
+	return ERR_PTR(ret);
+}
+
+int of_mipi_dsi_register_devices(struct mipi_dsi_bus *bus)
+{
+	struct device_node *n;
+
+	for_each_child_of_node(bus->dev->of_node, n)
+		of_mipi_dsi_register_device(bus, n);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_mipi_dsi_register_devices);
+
+static int mipi_dsi_remove_device_fn(struct device *_dev, void *priv)
+{
+	struct mipi_dsi_device *dev = to_mipi_dsi_device(_dev);
+
+	mipi_dsi_device_unregister(dev);
+
+	return 0;
+}
+
+void mipi_dsi_unregister_devices(struct mipi_dsi_bus *bus)
+{
+	device_for_each_child(bus->dev, bus, mipi_dsi_remove_device_fn);
+}
+EXPORT_SYMBOL_GPL(mipi_dsi_unregister_devices);
+/* -----------------------------------------------------------------------------
+ * Init/exit
+ */
+
+static int __init mipi_dsi_init(void)
+{
+	return bus_register(&mipi_dsi_bus_type);
+}
+
+static void __exit mipi_dsi_exit(void)
+{
+	bus_unregister(&mipi_dsi_bus_type);
+}
+
+module_init(mipi_dsi_init);
+module_exit(mipi_dsi_exit)
+
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_DESCRIPTION("MIPI DSI Bus");
+MODULE_LICENSE("GPL v2");
diff --git a/include/video/display.h b/include/video/display.h
index 3138401..7faca0f 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <media/media-entity.h>
 #include <video/mipi-dbi-bus.h>
+#include <video/mipi-dsi-bus.h>
 
 #define DISPLAY_PIXEL_CODING(option, type, from, to, variant) \
 	(((option) << 17) | ((type) << 13) | ((variant) << 10) | \
@@ -184,6 +185,7 @@ enum display_entity_stream_state {
 enum display_entity_interface_type {
 	DISPLAY_ENTITY_INTERFACE_DPI,
 	DISPLAY_ENTITY_INTERFACE_DBI,
+	DISPLAY_ENTITY_INTERFACE_DSI,
 	DISPLAY_ENTITY_INTERFACE_LVDS,
 	DISPLAY_ENTITY_INTERFACE_VGA,
 };
@@ -192,6 +194,7 @@ struct display_entity_interface_params {
 	enum display_entity_interface_type type;
 	union {
 		struct mipi_dbi_interface_params dbi;
+		struct mipi_dsi_interface_params dsi;
 	} p;
 };
 
diff --git a/include/video/mipi-dsi-bus.h b/include/video/mipi-dsi-bus.h
new file mode 100644
index 0000000..a78792d
--- /dev/null
+++ b/include/video/mipi-dsi-bus.h
@@ -0,0 +1,144 @@
+/*
+ * MIPI DSI Bus
+ *
+ * Copyright (C) 2013, Samsung Electronics, Co., Ltd.
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MIPI_DSI_BUS_H__
+#define __MIPI_DSI_BUS_H__
+
+#include <linux/device.h>
+#include <video/videomode.h>
+
+struct mipi_dsi_bus;
+struct mipi_dsi_device;
+
+struct mipi_dsi_bus_ops {
+	int (*set_power)(struct mipi_dsi_bus *bus, struct mipi_dsi_device *dev,
+			 bool on);
+	int (*set_stream)(struct mipi_dsi_bus *bus, struct mipi_dsi_device *dev,
+			  bool on);
+	int (*transfer)(struct mipi_dsi_bus *bus, struct mipi_dsi_device *dev,
+			u8 type, const u8 *tx_buf, size_t tx_len, u8 *rx_buf,
+			size_t rx_len);
+};
+
+#define DSI_MODE_VIDEO			(1 << 0)
+#define DSI_MODE_VIDEO_BURST		(1 << 1)
+#define DSI_MODE_VIDEO_SYNC_PULSE	(1 << 2)
+#define DSI_MODE_VIDEO_AUTO_VERT	(1 << 3)
+#define DSI_MODE_VIDEO_HSE		(1 << 4)
+#define DSI_MODE_VIDEO_HFP		(1 << 5)
+#define DSI_MODE_VIDEO_HBP		(1 << 6)
+#define DSI_MODE_VIDEO_HSA		(1 << 7)
+#define DSI_MODE_VSYNC_FLUSH		(1 << 8)
+#define DSI_MODE_EOT_PACKET		(1 << 9)
+
+enum mipi_dsi_pixel_format {
+	DSI_FMT_RGB888,
+	DSI_FMT_RGB666,
+	DSI_FMT_RGB666_PACKED,
+	DSI_FMT_RGB565,
+};
+
+struct mipi_dsi_interface_params {
+	enum mipi_dsi_pixel_format format;
+	unsigned long mode;
+	unsigned long hs_clk_freq;
+	unsigned long esc_clk_freq;
+	unsigned char data_lanes;
+	unsigned char cmd_allow;
+};
+
+struct mipi_dsi_bus {
+	struct device *dev;
+	const struct mipi_dsi_bus_ops *ops;
+};
+
+#define MIPI_DSI_MODULE_PREFIX		"mipi-dsi:"
+#define MIPI_DSI_NAME_SIZE		32
+
+struct mipi_dsi_device_id {
+	char name[MIPI_DSI_NAME_SIZE];
+	__kernel_ulong_t driver_data	/* Data private to the driver */
+			__aligned(sizeof(__kernel_ulong_t));
+};
+
+struct mipi_dsi_device {
+	char name[MIPI_DSI_NAME_SIZE];
+	int id;
+	struct device dev;
+
+	const struct mipi_dsi_device_id *id_entry;
+	struct mipi_dsi_bus *bus;
+	struct videomode vm;
+	struct mipi_dsi_interface_params params;
+};
+
+#define to_mipi_dsi_device(d)	container_of(d, struct mipi_dsi_device, dev)
+
+int mipi_dsi_device_register(struct mipi_dsi_device *dev,
+			     struct mipi_dsi_bus *bus);
+void mipi_dsi_device_unregister(struct mipi_dsi_device *dev);
+
+struct mipi_dsi_driver {
+	int(*probe)(struct mipi_dsi_device *);
+	int(*remove)(struct mipi_dsi_device *);
+	struct device_driver driver;
+	const struct mipi_dsi_device_id *id_table;
+};
+
+#define to_mipi_dsi_driver(d)	container_of(d, struct mipi_dsi_driver, driver)
+
+int mipi_dsi_driver_register(struct mipi_dsi_driver *drv);
+void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv);
+
+static inline void *mipi_dsi_get_drvdata(const struct mipi_dsi_device *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void mipi_dsi_set_drvdata(struct mipi_dsi_device *dev,
+					void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+int of_mipi_dsi_register_devices(struct mipi_dsi_bus *bus);
+void mipi_dsi_unregister_devices(struct mipi_dsi_bus *bus);
+
+/* module_mipi_dsi_driver() - Helper macro for drivers that don't do
+ * anything special in module init/exit.  This eliminates a lot of
+ * boilerplate.  Each module may only use this macro once, and
+ * calling it replaces module_init() and module_exit()
+ */
+#define module_mipi_dsi_driver(__mipi_dsi_driver) \
+	module_driver(__mipi_dsi_driver, mipi_dsi_driver_register, \
+			mipi_dsi_driver_unregister)
+
+int mipi_dsi_set_power(struct mipi_dsi_device *dev, bool on);
+int mipi_dsi_set_stream(struct mipi_dsi_device *dev, bool on);
+int mipi_dsi_dcs_write(struct mipi_dsi_device *dev, int channel, const u8 *data,
+		       size_t len);
+int mipi_dsi_dcs_read(struct mipi_dsi_device *dev, int channel, u8 cmd,
+		      u8 *data, size_t len);
+
+#define mipi_dsi_dcs_write_seq(dev, channel, seq...) \
+({\
+	const u8 d[] = { seq };\
+	BUILD_BUG_ON_MSG(ARRAY_SIZE(d) > 64, "DCS sequence too long for stack");\
+	mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\
+})
+
+#define mipi_dsi_dcs_write_static_seq(dev, channel, seq...) \
+({\
+	static const u8 d[] = { seq };\
+	mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\
+})
+
+#endif /* __MIPI_DSI_BUS__ */
-- 
1.8.1.2


^ permalink raw reply related

* [RFC PATCH 0/4] CDFv3: MIPI DSI bus implementation
From: Andrzej Hajda @ 2013-09-24 14:23 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Andrzej Hajda, dri-devel, linux-fbdev, linux-media, Kyungmin Park

Hi Laurent,

This is my MIPI DSI bus implementation. The patchset
contains also two drivers:
- DSI bus master driver for Exynos,
- DSI slave driver for s6e8aa0 panel family.
All code has been tested on real device - Exynos 4210 based 'Trats'.

This is not final version, there are still some things to do.

DSI bus code is based on mipi-dbi-bus, with few major changes.
1. I have replaced three DBI opses:
- write_command,
- write_data,
- read_data
with one op 'transfer', this way it better fits to
MIPI DSI standard, I wonder if this cannot be adapted to DBI also.
The only things which bothers me is number of arguments,
maybe struct should be used instead.

I have added DCS helpers functions which use 'transfer' op:
- mipi_dsi_dcs_read
- mipi_dsi_dcs_write
and two macros which allow to pass variable number of bytes
as arguments, example usage in panel driver code:
- mipi_dsi_dcs_write_seq
- mipi_dsi_dcs_write_static_seq

I have added also following opses:
- set_stream - to enable/disable streaming on DSI master,
- set_power - I have temporarily dropped idea of using runtime_pm
    due to compliacted power on sequence of panel/dsi,
    which would probably require different op anyway.

struct mipi_dsi_device contains videomode and mipi_dsi_interface_params fields.
Those fields are read by opses, I wonder if it would not be better to
pass them directly to opses as arguments.

TODO:
- helpers for non-DT drivers,
- minor power management issues,
- better error handling
- ...

Regards
Andrzej

Andrzej Hajda (4):
  mipi-dsi-bus: add MIPI DSI bus support
  mipi-dsi-exynos: add driver
  panel-s6e8aa0: add driver
  ARM: dts: exynos4210-trats: add panel and dsi nodes

 arch/arm/boot/dts/exynos4210-trats.dts  |   54 ++
 drivers/video/display/Kconfig           |   14 +
 drivers/video/display/Makefile          |    3 +
 drivers/video/display/mipi-dsi-bus.c    |  332 ++++++++
 drivers/video/display/mipi-dsi-exynos.c | 1310 +++++++++++++++++++++++++++++++
 drivers/video/display/panel-s6e8aa0.c   | 1286 ++++++++++++++++++++++++++++++
 include/video/display.h                 |    3 +
 include/video/mipi-dsi-bus.h            |  144 ++++
 include/video/mipi-dsi-exynos.h         |   41 +
 include/video/panel-s6e8aa0.h           |   42 +
 10 files changed, 3229 insertions(+)
 create mode 100644 drivers/video/display/mipi-dsi-bus.c
 create mode 100644 drivers/video/display/mipi-dsi-exynos.c
 create mode 100644 drivers/video/display/panel-s6e8aa0.c
 create mode 100644 include/video/mipi-dsi-bus.h
 create mode 100644 include/video/mipi-dsi-exynos.h
 create mode 100644 include/video/panel-s6e8aa0.h

-- 
1.8.1.2


^ permalink raw reply

* Re: [PATCH] video: mmp: drop needless devm cleanup
From: Zhou Zhu @ 2013-09-24  7:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <5241406B.8090106@ti.com>

On 09/24/2013 03:34 PM, Tomi Valkeinen wrote:
> On 23/09/13 19:19, Russell King - ARM Linux wrote:
>> On Mon, Sep 23, 2013 at 06:13:10PM +0200, Uwe Kleine-König wrote:
>>> The nice thing about devm_* is that the driver doesn't need to free the
>>> resources but the driver core takes care about that. This also
>>> simplifies the error path quite a bit and removes the wrong check for a
>>> clock pointer being NULL.
>>>
>>> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
>>> ---
>>>   drivers/video/mmp/hw/mmp_ctrl.c | 17 ++---------------
>>>   1 file changed, 2 insertions(+), 15 deletions(-)
>>>
>>> diff --git a/drivers/video/mmp/hw/mmp_ctrl.c b/drivers/video/mmp/hw/mmp_ctrl.c
>>> index 75dca19..6ac7552 100644
>>> --- a/drivers/video/mmp/hw/mmp_ctrl.c
>>> +++ b/drivers/video/mmp/hw/mmp_ctrl.c
>>> @@ -514,7 +514,7 @@ static int mmphw_probe(struct platform_device *pdev)
>>>   	if (IS_ERR(ctrl->clk)) {
>>>   		dev_err(ctrl->dev, "unable to get clk %s\n", mi->clk_name);
>>>   		ret = -ENOENT;
>>> -		goto failed_get_clk;
>>> +		goto failed;
>>>   	}
>>>   	clk_prepare_enable(ctrl->clk);
>>>
>>> @@ -551,21 +551,8 @@ failed_path_init:
>>>   		path_deinit(path_plat);
>>>   	}
>>>
>>> -	if (ctrl->clk) {
>>> -		devm_clk_put(ctrl->dev, ctrl->clk);
>>> -		clk_disable_unprepare(ctrl->clk);
>>
>> And this patch also fixes the above: disabling/unpreparing _after_ putting
>> the thing - which was quite silly... :)
>
> Hmm, I wonder if that causes any issues... I.e. should this patch go for
> 3.12, or is 3.13 fine?
>
>   Tomi
>
It would cause oops if probe failed due to some reason - although it 
would almost never happen so we missed it.
Thank you for finding it out.

-- 
Thanks, -Zhou

^ permalink raw reply

* Re: [PATCH] video: mmp: drop needless devm cleanup
From: Tomi Valkeinen @ 2013-09-24  7:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130923161944.GU12758@n2100.arm.linux.org.uk>

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

On 23/09/13 19:19, Russell King - ARM Linux wrote:
> On Mon, Sep 23, 2013 at 06:13:10PM +0200, Uwe Kleine-König wrote:
>> The nice thing about devm_* is that the driver doesn't need to free the
>> resources but the driver core takes care about that. This also
>> simplifies the error path quite a bit and removes the wrong check for a
>> clock pointer being NULL.
>>
>> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
>> ---
>>  drivers/video/mmp/hw/mmp_ctrl.c | 17 ++---------------
>>  1 file changed, 2 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/video/mmp/hw/mmp_ctrl.c b/drivers/video/mmp/hw/mmp_ctrl.c
>> index 75dca19..6ac7552 100644
>> --- a/drivers/video/mmp/hw/mmp_ctrl.c
>> +++ b/drivers/video/mmp/hw/mmp_ctrl.c
>> @@ -514,7 +514,7 @@ static int mmphw_probe(struct platform_device *pdev)
>>  	if (IS_ERR(ctrl->clk)) {
>>  		dev_err(ctrl->dev, "unable to get clk %s\n", mi->clk_name);
>>  		ret = -ENOENT;
>> -		goto failed_get_clk;
>> +		goto failed;
>>  	}
>>  	clk_prepare_enable(ctrl->clk);
>>  
>> @@ -551,21 +551,8 @@ failed_path_init:
>>  		path_deinit(path_plat);
>>  	}
>>  
>> -	if (ctrl->clk) {
>> -		devm_clk_put(ctrl->dev, ctrl->clk);
>> -		clk_disable_unprepare(ctrl->clk);
> 
> And this patch also fixes the above: disabling/unpreparing _after_ putting
> the thing - which was quite silly... :)

Hmm, I wonder if that causes any issues... I.e. should this patch go for
3.12, or is 3.13 fine?

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 901 bytes --]

^ permalink raw reply

* Re: [PATCH 2/2] backlight: tosa: Remove redundant spi_set_drvdata
From: Jingoo Han @ 2013-09-24  5:41 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <1379659133-18799-2-git-send-email-sachin.kamat@linaro.org>

On Friday, September 20, 2013 3:39 PM, Sachin Kamat wrote:
> 
> Driver core sets driver data to NULL upon failure or remove.
> 
> Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>

Acked-by: Jingoo Han <jg1.han@samsung.com>

It looks good.
Thank you for sending the patch.

Best regards,
Jingoo Han


^ permalink raw reply

* Re: [PATCH 1/2] backlight: l4f00242t03: Remove redundant spi_set_drvdata
From: Jingoo Han @ 2013-09-24  5:40 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <1379659133-18799-1-git-send-email-sachin.kamat@linaro.org>

On Friday, September 20, 2013 3:39 PM, Sachin Kamat wrote:
> 
> Driver core sets driver data to NULL upon failure or remove.
> 
> Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>

Acked-by: Jingoo Han <jg1.han@samsung.com>

It looks good.
Thank you for sending the patch.

Best regards,
Jingoo Han


^ permalink raw reply

* Re: [PATCH v3 1/2] video: ARM CLCD: Add DT support
From: Russell King - ARM Linux @ 2013-09-23 20:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52409C0C.1000406@wwwdotorg.org>

On Mon, Sep 23, 2013 at 01:52:44PM -0600, Stephen Warren wrote:
> On 09/23/2013 10:43 AM, Russell King - ARM Linux wrote:
> > On Mon, Sep 23, 2013 at 10:30:15AM -0600, Stephen Warren wrote:
> >> On 09/23/2013 10:06 AM, Russell King - ARM Linux wrote:
> >>> On Mon, Sep 23, 2013 at 10:03:18AM -0600, Stephen Warren wrote:
> >>>> It sounds like you could just put LCDControl & 0x2e in the DT rather
> >>>> than using values such as 0x100..0x107, which don't appear to match the
> >>>> register format you mentioned above.
> >>>
> >>> No.  Platforms which route the outputs to something like VGA or HDMI can
> >>> change the framebuffer format.  Your suggestions is far too restrictive.
> >>
> >> Surely the DT should describe the HW setup only. Usually, a particular
> >> HW setup can support multiple different framebuffer formats. Hence, the
> >> DT wouldn't/shouldn't imply anything about the framebuffer format, but
> >> simply which wires are connected to the LCD.
> > 
> > Quite, and putting the contents of the LCDControl register - even just
> > bits 5 and 3-1 results in you having to modify the DT and reboot the
> > kernel just to change the framebuffer format.  That's why I'm objecting
> > to your comment.
> 
> Oh, so these particular registers define both the output signal muxing
> for the pins and the FB data format? If so, yes it's not correct to put
> the register values into DT. I assumed that the HW would have a separate
> representation of those two concepts, in different registers or at least
> different fields in the same register. If not, there is indeed no choice
> but to make up some arbitrary values to represent just the pinmuxing :-(

Unfortunately not.  Parameters such as TFT, and dual can be specified by
DT, but the difficulty comes with the pixel wiring.

With a PL110 in TFT mode, the representation of which signals represent
what are a function of the selected BPP:

Panel:
24bpp: Blue[7:0]=CLD[23:16] Green[7:0]=CLD[15:8] Red[7:0]=CLD[7:0]
18bpp: Blue[4:0]=CLD[17:13] Green[4:0]=CLD[11:7] Red[4:0]=CLD[5:1]
       Intensity = CLD[12], CLD[6], CLD[0]

24bpp signalling is used when a framebuffer format of 24bpp mode is
selected, otherwise the 18bpp signalling is used.

The PL110 in 16bpp does not support for anything but 1555 mode when wired
up as above, but some people want 565 mode.  That's achievable (and is
used on Versatile) by adjusting the wiring via a FPGA:

       Blue = CLD[12,17:14] Green = [13,11:7] Blue = CLD[5:1]

This gives us the RGB565 mode on PL110.  Now, consider a platform which
wants to use this - if the panel is wired up directly to the CLCD like
that, then it will only support RGB565.

If you were connecting a 4:4:4 panel, a similar thing is possible, and
may be preferable to have the standard framebuffer format in memory to
do this.

So, the supportable framebuffer foramts depends entirely on how the
display is wired up to the controller.

Now, for PL111, things are simpler - it supports 5551, 565 and 444 modes
natively, so supports all the standard framebuffer formats.  The output
wiring is different and more sane.  In this case, CLD[23] is always the
MSB blue bit, CLD[15] is always the MSB green bit, and CLD[7] is always
the MSB red bit.  So here, the wiring matters very much less.

However, even here the "capabilities" play a role.  Does driving a TFT
444 panel being driven in 24-bit mode make sense?  Yes, it'll work but
the least significant four bits are lost.  What about the other way
around?  A TFT 888 panel in 12bpp mode?  Well, in that case the least
sigificant four bits are marked up as "reserved" - and even if they are
held at zero, you're going to lose some colour range on the panel because
white will be equivalent to colour f0f0f0 not ffffff.

Hence, we probably want to have even here some way to say "I want this
hardware to only support framebuffer formats of X".

My feeling is that even though these capabilities are not part of the
actual hardware spec, they're well worth implementing directly in DT as
they're describing what the *hardware* as a whole is capable of.

Moreover, I created the capabilities purely as a way to describe what
the hardware and panel are capable of.  Isn't that what DT is all about
too?

^ permalink raw reply

* Re: [PATCH v3 1/2] video: ARM CLCD: Add DT support
From: Stephen Warren @ 2013-09-23 19:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130923164302.GW12758@n2100.arm.linux.org.uk>

On 09/23/2013 10:43 AM, Russell King - ARM Linux wrote:
> On Mon, Sep 23, 2013 at 10:30:15AM -0600, Stephen Warren wrote:
>> On 09/23/2013 10:06 AM, Russell King - ARM Linux wrote:
>>> On Mon, Sep 23, 2013 at 10:03:18AM -0600, Stephen Warren wrote:
>>>> It sounds like you could just put LCDControl & 0x2e in the DT rather
>>>> than using values such as 0x100..0x107, which don't appear to match the
>>>> register format you mentioned above.
>>>
>>> No.  Platforms which route the outputs to something like VGA or HDMI can
>>> change the framebuffer format.  Your suggestions is far too restrictive.
>>
>> Surely the DT should describe the HW setup only. Usually, a particular
>> HW setup can support multiple different framebuffer formats. Hence, the
>> DT wouldn't/shouldn't imply anything about the framebuffer format, but
>> simply which wires are connected to the LCD.
> 
> Quite, and putting the contents of the LCDControl register - even just
> bits 5 and 3-1 results in you having to modify the DT and reboot the
> kernel just to change the framebuffer format.  That's why I'm objecting
> to your comment.

Oh, so these particular registers define both the output signal muxing
for the pins and the FB data format? If so, yes it's not correct to put
the register values into DT. I assumed that the HW would have a separate
representation of those two concepts, in different registers or at least
different fields in the same register. If not, there is indeed no choice
but to make up some arbitrary values to represent just the pinmuxing :-(

^ permalink raw reply

* Re: [PATCH 36/51] DMA-API: usb: use dma_set_coherent_mask()
From: Russell King - ARM Linux @ 2013-09-23 18:42 UTC (permalink / raw)
  To: Alan Stern
  Cc: alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, Alexander Shishkin,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
	linux-nvme-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-ide-u79uwXL29TY76Z2rM5mHXA,
	devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-scsi-u79uwXL29TY76Z2rM5mHXA,
	e1000-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	b43-dev-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Stephen Warren, Kukjin Kim,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Solarflare linux maintainers, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA, Felipe Balbi,
	linux-crypto-u79uwXL29TY76Z2rM5mHXA, Greg Kroah-Hartman
In-Reply-To: <Pine.LNX.4.44L0.1309231418030.1348-100000-IYeN2dnnYyZXsRXLowluHWD2FQJk+8+b@public.gmane.org>

On Mon, Sep 23, 2013 at 02:27:39PM -0400, Alan Stern wrote:
> On Thu, 19 Sep 2013, Russell King wrote:
> 
> > The correct way for a driver to specify the coherent DMA mask is
> > not to directly access the field in the struct device, but to use
> > dma_set_coherent_mask().  Only arch and bus code should access this
> > member directly.
> > 
> > Convert all direct write accesses to using the correct API.
> > 
> > Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
> 
> > diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
> > index f6b790c..5b0cd2d 100644
> > --- a/drivers/usb/host/ehci-platform.c
> > +++ b/drivers/usb/host/ehci-platform.c
> 
> > @@ -91,8 +91,9 @@ static int ehci_platform_probe(struct platform_device *dev)
> >  		dev->dev.platform_data = &ehci_platform_defaults;
> >  	if (!dev->dev.dma_mask)
> >  		dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
> > -	if (!dev->dev.coherent_dma_mask)
> > -		dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
> > +	err = dma_set_coherent_mask(&dev->dev, DMA_BIT_MASK(32));
> > +	if (err)
> > +		return err;
> >  
> >  	pdata = dev_get_platdata(&dev->dev);
> 
> ehci-platform.c is a generic file, intended for use by multiple
> platforms.  Is it not possible that on some platforms, the arch or bus
> code may already have initialized the DMA masks?  Isn't something like 
> this (i.e., DMA bindings) planned for Device Tree?
> 
> By eliminating the tests above and calling dma_set_coherent_mask or
> dma_coerce_mask_and_coherent unconditionally, this patch (and the next)
> would overwrite those initial settings.
> 
> I don't know to what extent the same may be true for the other,
> platform-specific, drivers changed by this patch.  But it's something 
> to be aware of.

Please check the DMA API documentation:

==For correct operation, you must interrogate the kernel in your device
probe routine to see if the DMA controller on the machine can properly
support the DMA addressing limitation your device has.  It is good
style to do this even if your device holds the default setting,
because this shows that you did think about these issues wrt. your
device.

The query is performed via a call to dma_set_mask():

        int dma_set_mask(struct device *dev, u64 mask);

The query for consistent allocations is performed via a call to
dma_set_coherent_mask():

        int dma_set_coherent_mask(struct device *dev, u64 mask);
==
So, All drivers which use DMA are supposed to issue the appropriate
calls to check the DMA masks before they perform DMA, even if the
"default" is correct.

And yes, this is all part of sorting out the DT mess - we should
start not from the current mess (which is really totally haphazard)
but from the well established position of how the DMA API _should_
be used.  What that means is that all drivers should be issuing
these calls as appropriate today.

The default mask setup when the device is created is just that -
it's a default mask, and it should not be used to decide anything
about the device.  That's something which the driver should compute
on its own accord and then inform the various other layers via the
appropriate call.

Remember, on PCI, even when we have 64-bit, we do not declare PCI
devices with a 64-bit DMA mask: that's up to PCI device drivers to
_try_ to set a 64-bit DMA mask, which when successful _allows_ them
to use 64-bit DMA.  If it fails, they have to only use 32-bit.  If
they want a smaller mask, the _driver_ has to set the smaller mask,
not the device creating code.

The reason we're into this (particularly on ARM) is that we got lazy
because we could get by with declaring a DMA mask at device creation
time, since all devices were statically declared.  Now it's time to
get rid of those old lazy ways and start doing things correctly and
to the requirements of the APIs.

^ permalink raw reply

* Re: [PATCH 4/7] pcmcia: at91_cf: fix deferred probe from __init
From: Nicolas Ferre @ 2013-09-23 16:53 UTC (permalink / raw)
  To: Johan Hovold, Greg Kroah-Hartman
  Cc: Grant Likely, Mark Brown, linux-kernel, linux-mmc, linux-mtd,
	linux-pcmcia, linux-usb, linux-fbdev,
	Jean-Christophe PLAGNIOL-VILLARD
In-Reply-To: <1379946452-25649-5-git-send-email-jhovold@gmail.com>

On 23/09/2013 16:27, Johan Hovold :
> Move probe out of __init section and don't use platform_driver_probe
> which cannot be used with deferred probing.
>
> Since commit e9354576 ("gpiolib: Defer failed gpio requests by default")
> this driver might return -EPROBE_DEFER if a gpio_request fails.
>
> Cc: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
> Cc: Nicolas Ferre <nicolas.ferre@atmel.com>

Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>

Thanks Johan.

> Signed-off-by: Johan Hovold <jhovold@gmail.com>
> ---
>   drivers/pcmcia/at91_cf.c | 11 +++++------
>   1 file changed, 5 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c
> index b8f5acf..de24232 100644
> --- a/drivers/pcmcia/at91_cf.c
> +++ b/drivers/pcmcia/at91_cf.c
> @@ -245,7 +245,7 @@ static int at91_cf_dt_init(struct platform_device *pdev)
>   }
>   #endif
>
> -static int __init at91_cf_probe(struct platform_device *pdev)
> +static int at91_cf_probe(struct platform_device *pdev)
>   {
>   	struct at91_cf_socket	*cf;
>   	struct at91_cf_data	*board = pdev->dev.platform_data;
> @@ -354,7 +354,7 @@ fail0a:
>   	return status;
>   }
>
> -static int __exit at91_cf_remove(struct platform_device *pdev)
> +static int at91_cf_remove(struct platform_device *pdev)
>   {
>   	struct at91_cf_socket	*cf = platform_get_drvdata(pdev);
>
> @@ -404,14 +404,13 @@ static struct platform_driver at91_cf_driver = {
>   		.owner		= THIS_MODULE,
>   		.of_match_table = of_match_ptr(at91_cf_dt_ids),
>   	},
> -	.remove		= __exit_p(at91_cf_remove),
> +	.probe		= at91_cf_probe,
> +	.remove		= at91_cf_remove,
>   	.suspend	= at91_cf_suspend,
>   	.resume		= at91_cf_resume,
>   };
>
> -/*--------------------------------------------------------------------------*/
> -
> -module_platform_driver_probe(at91_cf_driver, at91_cf_probe);
> +module_platform_driver(at91_cf_driver);
>
>   MODULE_DESCRIPTION("AT91 Compact Flash Driver");
>   MODULE_AUTHOR("David Brownell");
>


-- 
Nicolas Ferre

^ permalink raw reply

* Re: [PATCH v3 1/2] video: ARM CLCD: Add DT support
From: Russell King - ARM Linux @ 2013-09-23 16:43 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52406C97.3010800@wwwdotorg.org>

On Mon, Sep 23, 2013 at 10:30:15AM -0600, Stephen Warren wrote:
> On 09/23/2013 10:06 AM, Russell King - ARM Linux wrote:
> > On Mon, Sep 23, 2013 at 10:03:18AM -0600, Stephen Warren wrote:
> >> It sounds like you could just put LCDControl & 0x2e in the DT rather
> >> than using values such as 0x100..0x107, which don't appear to match the
> >> register format you mentioned above.
> > 
> > No.  Platforms which route the outputs to something like VGA or HDMI can
> > change the framebuffer format.  Your suggestions is far too restrictive.
> 
> Surely the DT should describe the HW setup only. Usually, a particular
> HW setup can support multiple different framebuffer formats. Hence, the
> DT wouldn't/shouldn't imply anything about the framebuffer format, but
> simply which wires are connected to the LCD.

Quite, and putting the contents of the LCDControl register - even just
bits 5 and 3-1 results in you having to modify the DT and reboot the
kernel just to change the framebuffer format.  That's why I'm objecting
to your comment.

When I rewrote the way the CLCD driver handles the various panels, I did
it with full information on how the hardware was being used at that time.
That is precisely why I came up with the capability system, where we
describe which formats the hardware can support up to the interface,
separately from the formats which the attached device - be it a LCD
panel, VGA socket or HDMI socket - can support.  The resulting set of
formats which can be used are a union of these.

Suggesting that we can do this by putting register values into DT is
completely wrong - if that were possible, I wouldn't have come up with
this capability system to sort this mess out in the first place - I
could've just hard-coded the register values and said to everyone
"tough, on these platforms you only get RGB444 support and that's it."

^ permalink raw reply

* Re: [PATCH v3 1/2] video: ARM CLCD: Add DT support
From: Stephen Warren @ 2013-09-23 16:30 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20130923160640.GT12758@n2100.arm.linux.org.uk>

On 09/23/2013 10:06 AM, Russell King - ARM Linux wrote:
> On Mon, Sep 23, 2013 at 10:03:18AM -0600, Stephen Warren wrote:
>> It sounds like you could just put LCDControl & 0x2e in the DT rather
>> than using values such as 0x100..0x107, which don't appear to match the
>> register format you mentioned above.
> 
> No.  Platforms which route the outputs to something like VGA or HDMI can
> change the framebuffer format.  Your suggestions is far too restrictive.

Surely the DT should describe the HW setup only. Usually, a particular
HW setup can support multiple different framebuffer formats. Hence, the
DT wouldn't/shouldn't imply anything about the framebuffer format, but
simply which wires are connected to the LCD.

^ permalink raw reply

* Re: [PATCH] video: mmp: drop needless devm cleanup
From: Russell King - ARM Linux @ 2013-09-23 16:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1379952790-13202-1-git-send-email-u.kleine-koenig@pengutronix.de>

On Mon, Sep 23, 2013 at 06:13:10PM +0200, Uwe Kleine-König wrote:
> The nice thing about devm_* is that the driver doesn't need to free the
> resources but the driver core takes care about that. This also
> simplifies the error path quite a bit and removes the wrong check for a
> clock pointer being NULL.
> 
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> ---
>  drivers/video/mmp/hw/mmp_ctrl.c | 17 ++---------------
>  1 file changed, 2 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/video/mmp/hw/mmp_ctrl.c b/drivers/video/mmp/hw/mmp_ctrl.c
> index 75dca19..6ac7552 100644
> --- a/drivers/video/mmp/hw/mmp_ctrl.c
> +++ b/drivers/video/mmp/hw/mmp_ctrl.c
> @@ -514,7 +514,7 @@ static int mmphw_probe(struct platform_device *pdev)
>  	if (IS_ERR(ctrl->clk)) {
>  		dev_err(ctrl->dev, "unable to get clk %s\n", mi->clk_name);
>  		ret = -ENOENT;
> -		goto failed_get_clk;
> +		goto failed;
>  	}
>  	clk_prepare_enable(ctrl->clk);
>  
> @@ -551,21 +551,8 @@ failed_path_init:
>  		path_deinit(path_plat);
>  	}
>  
> -	if (ctrl->clk) {
> -		devm_clk_put(ctrl->dev, ctrl->clk);
> -		clk_disable_unprepare(ctrl->clk);

And this patch also fixes the above: disabling/unpreparing _after_ putting
the thing - which was quite silly... :)

^ permalink raw reply

* [PATCH] video: mmp: drop needless devm cleanup
From: Uwe Kleine-König @ 2013-09-23 16:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1379951159-8294-1-git-send-email-u.kleine-koenig@pengutronix.de>

The nice thing about devm_* is that the driver doesn't need to free the
resources but the driver core takes care about that. This also
simplifies the error path quite a bit and removes the wrong check for a
clock pointer being NULL.

Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
 drivers/video/mmp/hw/mmp_ctrl.c | 17 ++---------------
 1 file changed, 2 insertions(+), 15 deletions(-)

diff --git a/drivers/video/mmp/hw/mmp_ctrl.c b/drivers/video/mmp/hw/mmp_ctrl.c
index 75dca19..6ac7552 100644
--- a/drivers/video/mmp/hw/mmp_ctrl.c
+++ b/drivers/video/mmp/hw/mmp_ctrl.c
@@ -514,7 +514,7 @@ static int mmphw_probe(struct platform_device *pdev)
 	if (IS_ERR(ctrl->clk)) {
 		dev_err(ctrl->dev, "unable to get clk %s\n", mi->clk_name);
 		ret = -ENOENT;
-		goto failed_get_clk;
+		goto failed;
 	}
 	clk_prepare_enable(ctrl->clk);
 
@@ -551,21 +551,8 @@ failed_path_init:
 		path_deinit(path_plat);
 	}
 
-	if (ctrl->clk) {
-		devm_clk_put(ctrl->dev, ctrl->clk);
-		clk_disable_unprepare(ctrl->clk);
-	}
-failed_get_clk:
-	devm_free_irq(ctrl->dev, ctrl->irq, ctrl);
+	clk_disable_unprepare(ctrl->clk);
 failed:
-	if (ctrl) {
-		if (ctrl->reg_base)
-			devm_iounmap(ctrl->dev, ctrl->reg_base);
-		devm_release_mem_region(ctrl->dev, res->start,
-				resource_size(res));
-		devm_kfree(ctrl->dev, ctrl);
-	}
-
 	dev_err(&pdev->dev, "device init failed\n");
 
 	return ret;
-- 
1.8.4.rc3


^ permalink raw reply related

* Re: [PATCH v3 1/2] video: ARM CLCD: Add DT support
From: Russell King - ARM Linux @ 2013-09-23 16:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52406646.709@wwwdotorg.org>

On Mon, Sep 23, 2013 at 10:03:18AM -0600, Stephen Warren wrote:
> It sounds like you could just put LCDControl & 0x2e in the DT rather
> than using values such as 0x100..0x107, which don't appear to match the
> register format you mentioned above.

No.  Platforms which route the outputs to something like VGA or HDMI can
change the framebuffer format.  Your suggestions is far too restrictive.

^ permalink raw reply

* Re: [PATCH v3 1/2] video: ARM CLCD: Add DT support
From: Stephen Warren @ 2013-09-23 16:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1379522363.9244.12.camel@hornet>

On 09/18/2013 10:39 AM, Pawel Moll wrote:
> On Tue, 2013-09-17 at 22:34 +0100, Stephen Warren wrote:
>>> On Tue, 2013-09-17 at 17:36 +0100, Pawel Moll wrote:
>>>> On Mon, 2013-09-16 at 20:52 +0100, Stephen Warren wrote:
>>>>>> +- arm,pl11x,panel-data-pads: array of 24 cells, each of them describing
>>>>>> +				a function of one of the CLD pads,
>>>>>> +				starting from 0 up to 23; each pad can
>>>>>> +				be described by one of the following values:
>>>>>> +	- 0: reserved (not connected)
>>>>>> +	- 0x100-0x107: color upper STN panel data 0 to 7
>>>>> ...
>>>>>
>>>>> I assume those are the raw values that go into the HW?
>>>
>>> No, they can be considered "labels", defining the way pads are used
>>> (wired). Then, basing on this information, the driver is configuring the
>>> cell, eg. selecting STN or TFT mode (thus switching the output between
>>> panel formatter and raw RGB data source).
>>
>> Oh, why not just use the raw values from the HW registers for this?
> 
> How do you mean? Based on the the way the "LCD data" lines are wired up,
> the driver has to decide whether to select STN (LCDControl &= ~(1<<5))
> or TFT mode (LCDControl |= 1<<5), then figures out what memory pixel
> formats are possible (based on this will set LCDControl[3..1] in
> runtime, depending on the mode selected by the user). There isn't a
> separate register as such configuring output pads. It's just the way
> they can used depends on the way they're wired up.

It sounds like you could just put LCDControl & 0x2e in the DT rather
than using values such as 0x100..0x107, which don't appear to match the
register format you mentioned above.


^ permalink raw reply

* Re: [PATCH 0/7] driver core: prevent deferred probe with platform_driver_probe
From: Sascha Hauer @ 2013-09-23 15:24 UTC (permalink / raw)
  To: Johan Hovold
  Cc: linux-fbdev, linux-usb, Mark Brown, Greg Kroah-Hartman,
	linux-pcmcia, linux-mmc, linux-kernel, linux-mtd, Grant Likely
In-Reply-To: <20130923152018.GB1454@localhost>

On Mon, Sep 23, 2013 at 05:20:18PM +0200, Johan Hovold wrote:
> On Mon, Sep 23, 2013 at 04:50:29PM +0200, Sascha Hauer wrote:
> > On Mon, Sep 23, 2013 at 04:27:25PM +0200, Johan Hovold wrote:
> > > Deferred probing cannot be used with platform_driver_probe as by the
> > > time probing is retried either the driver has been unregistered or its
> > > probe function has been set to platform_drv_probe_fail.
> > > 
> > > With commit e9354576 ("gpiolib: Defer failed gpio requests by default")
> > > the gpio subsystem started returning -EPROBE_DEFER, which in turn
> > > several platform drivers using platform_driver_probe return to driver
> > > core. Other subsystems (e.g. regulator) has since started doing the
> > > same.
> > > 
> > > The first patch in this series prevents platform drivers using
> > > platform_driver_probe from requesting probe deferral while warning that
> > > it is not supported.
> > > 
> > > The remaining patches move six platform-driver probe functions that rely
> > > on gpio_request out of __init. There are likely other probe functions
> > > that might return -EPROBE_DEFER and should be moved out of __init as
> > > well.
> > 
> > As usually when I read this I wonder why platform_driver_probe exists
> > anyway. The only advantage I can think off is that the probe functions
> > are in __init and thus can be disposed of later. Now you remove the
> > __init annotations from these probe functions. Wouldn't it be better to
> > convert the drivers to regular platform_driver_register instead?
> 
> Perhaps that paragraph was a bit unclear: I move them out of __init
> _and_ use platform_driver_register instead of platform_driver_probe as
> well.

Oh yes, I should have looked closer. Sorry for the noise.

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply


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