This patch series includes the following: - FIMD I80 interface - DSI command mode interface for Exynos5420 - S6E3FA0 command mode type panel driver - Some bugs modification
The patch series is based on exynos-drm-next branch.
Thank you. Best regards YJ
YoungJun Cho (14): drm/exynos: dsi: move the Eot packets configuration point drm/exynos: dsi: delay setting clocks after reset drm/exynos: use wait_event_timeout() for safety usage ARM: dts: add exynos5 compatible to sysreg ARM: dts: samsung-fimd: add I80 specific properties drm/exynos: support MIPI DSI command mode ARM: dts: exynos_dsim: add exynos5420 Soc compatible drm/exynos: dsi: add driver data to support Exynos5420 ARM: dts: s6e3fa0: add DT bindings drm/panel: add S6E3FA0 driver ARM: dts: exynos4: add system register node ARM: dts: exynos5: add system register support ARM: dts: exynos5420: add mipi-phy node ARM: dts: exynos5420: add dsi node
.../devicetree/bindings/arm/samsung/sysreg.txt | 1 + .../devicetree/bindings/panel/samsung,s6e3fa0.txt | 52 ++ .../devicetree/bindings/video/exynos_dsim.txt | 4 +- .../devicetree/bindings/video/samsung-fimd.txt | 9 + arch/arm/boot/dts/exynos4.dtsi | 1 + arch/arm/boot/dts/exynos5.dtsi | 6 + arch/arm/boot/dts/exynos5420.dtsi | 20 + drivers/gpu/drm/exynos/Kconfig | 1 + drivers/gpu/drm/exynos/exynos_drm_crtc.c | 16 +- drivers/gpu/drm/exynos/exynos_drm_crtc.h | 2 + drivers/gpu/drm/exynos/exynos_drm_drv.h | 2 + drivers/gpu/drm/exynos/exynos_drm_dsi.c | 121 ++++- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 280 ++++++++-- drivers/gpu/drm/panel/Kconfig | 7 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-s6e3fa0.c | 544 ++++++++++++++++++++ include/drm/drm_mipi_dsi.h | 2 + include/video/samsung_fimd.h | 3 +- 18 files changed, 1001 insertions(+), 71 deletions(-) create mode 100644 Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt create mode 100644 drivers/gpu/drm/panel/panel-s6e3fa0.c
This configuration could be used in MIPI DSI command mode also.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index eb73e3b..956e5f3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -472,8 +472,6 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH)) reg |= DSIM_MFLUSH_VS; - if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET)) - reg |= DSIM_EOT_DISABLE; if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) reg |= DSIM_SYNC_INFORM; if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) @@ -490,6 +488,9 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi) reg |= DSIM_HSA_MODE; }
+ if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET)) + reg |= DSIM_EOT_DISABLE; + switch (dsi->format) { case MIPI_DSI_FMT_RGB888: reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
Some phy control registers are not kept after software reset. So this patch makes the clocks containing phy control to be set after software reset.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 956e5f3..2cf1f0b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -946,10 +946,10 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
static int exynos_dsi_init(struct exynos_dsi *dsi) { - exynos_dsi_enable_clock(dsi); exynos_dsi_reset(dsi); enable_irq(dsi->irq); exynos_dsi_wait_for_reset(dsi); + exynos_dsi_enable_clock(dsi); exynos_dsi_init_link(dsi);
return 0;
Hi YoungJun,
Thanks for the whole patchset.
On 04/15/2014 07:47 AM, YoungJun Cho wrote:
Some phy control registers are not kept after software reset. So this patch makes the clocks containing phy control to be set after software reset.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 956e5f3..2cf1f0b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -946,10 +946,10 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
static int exynos_dsi_init(struct exynos_dsi *dsi) {
- exynos_dsi_enable_clock(dsi); exynos_dsi_reset(dsi); enable_irq(dsi->irq); exynos_dsi_wait_for_reset(dsi);
exynos_dsi_enable_clock(dsi); exynos_dsi_init_link(dsi);
return 0;
Are you sure this sequence is OK? I have observed that sequence:
dsi power off dsi power on dsi_reset
on 4210 or 4412 resulted in lack of irq after reset, only enabling clocks helped. And according to documentation reset do not touch registers set by exynos_dsi_enable_clock, so the current solution should work.
Regards Andrzej
Hi Andrzej
Thank you for comments.
On 04/18/2014 09:15 PM, Andrzej Hajda wrote:
Hi YoungJun,
Thanks for the whole patchset.
On 04/15/2014 07:47 AM, YoungJun Cho wrote:
Some phy control registers are not kept after software reset. So this patch makes the clocks containing phy control to be set after software reset.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 956e5f3..2cf1f0b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -946,10 +946,10 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
static int exynos_dsi_init(struct exynos_dsi *dsi) {
- exynos_dsi_enable_clock(dsi); exynos_dsi_reset(dsi); enable_irq(dsi->irq); exynos_dsi_wait_for_reset(dsi);
exynos_dsi_enable_clock(dsi); exynos_dsi_init_link(dsi);
return 0;
Are you sure this sequence is OK? I have observed that sequence:
dsi power off dsi power on dsi_reset
on 4210 or 4412 resulted in lack of irq after reset, only enabling clocks helped. And according to documentation reset do not touch registers set by exynos_dsi_enable_clock, so the current solution should work.
As I mentioned in description, it came from phy control registers. Fortunately Exynos4 SoCs are safe, but the DSIM_PHYCTRL_REG, DSIM_PHYTIMING_REG, DSIM_PHYTIMING1_REG and DSIM_PHYTIMING2_REG are affected which are used in exynos_dsi_set_pll() for Exynos5 SoCs.
So this patch is required for Exynos5 SoCs.
Thank you best regards YJ
Regards Andrzej
There could be the case that the page flip operation isn't finished correctly with some abnormal condition such as panel reset. So this patch replaces wait_event() with wait_event_timeout() to avoid waiting for page flip completion infinitely.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_crtc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index e930d4f..1419d11 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -69,8 +69,9 @@ static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
if (mode > DRM_MODE_DPMS_ON) { /* wait for the completion of page flip. */ - wait_event(exynos_crtc->pending_flip_queue, - atomic_read(&exynos_crtc->pending_flip) == 0); + wait_event_timeout(exynos_crtc->pending_flip_queue, + !atomic_read(&exynos_crtc->pending_flip), + HZ/20); drm_vblank_off(crtc->dev, exynos_crtc->pipe); }
This patch adds sysreg support for exynos5 SoCs.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- .../devicetree/bindings/arm/samsung/sysreg.txt | 1 + 1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/arm/samsung/sysreg.txt b/Documentation/devicetree/bindings/arm/samsung/sysreg.txt index 0ab3251..fd71581 100644 --- a/Documentation/devicetree/bindings/arm/samsung/sysreg.txt +++ b/Documentation/devicetree/bindings/arm/samsung/sysreg.txt @@ -3,6 +3,7 @@ SAMSUNG S5P/Exynos SoC series System Registers (SYSREG) Properties: - compatible : should contain "samsung,<chip name>-sysreg", "syscon"; For Exynos4 SoC series it should be "samsung,exynos4-sysreg", "syscon"; + For Exynos5 SoC series it should be "samsung,exynos5-sysreg", "syscon"; - reg : offset and length of the register set.
Example:
Hi YoungJun,
On 15 April 2014 11:17, YoungJun Cho yj44.cho@samsung.com wrote:
This patch adds sysreg support for exynos5 SoCs.
The patch title and commit description seem a bit off here. This patch does not add support per se. It only updates the binding documentaion.
This patch adds relevant to exynos5 compatible for exynos5 SoCs.
Changelog v2: - Changes title and description (commented by Sachin Kamat)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- .../devicetree/bindings/arm/samsung/sysreg.txt | 1 + 1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/arm/samsung/sysreg.txt b/Documentation/devicetree/bindings/arm/samsung/sysreg.txt index 0ab3251..fd71581 100644 --- a/Documentation/devicetree/bindings/arm/samsung/sysreg.txt +++ b/Documentation/devicetree/bindings/arm/samsung/sysreg.txt @@ -3,6 +3,7 @@ SAMSUNG S5P/Exynos SoC series System Registers (SYSREG) Properties: - compatible : should contain "samsung,<chip name>-sysreg", "syscon"; For Exynos4 SoC series it should be "samsung,exynos4-sysreg", "syscon"; + For Exynos5 SoC series it should be "samsung,exynos5-sysreg", "syscon"; - reg : offset and length of the register set.
Example:
In case of using CPU interface panel, the relevant registers should be set. So this patch adds relevant dt bindings.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- .../devicetree/bindings/video/samsung-fimd.txt | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/Documentation/devicetree/bindings/video/samsung-fimd.txt b/Documentation/devicetree/bindings/video/samsung-fimd.txt index 2dad41b..924c2e1 100644 --- a/Documentation/devicetree/bindings/video/samsung-fimd.txt +++ b/Documentation/devicetree/bindings/video/samsung-fimd.txt @@ -44,6 +44,15 @@ Optional Properties: - display-timings: timing settings for FIMD, as described in document [1]. Can be used in case timings cannot be provided otherwise or to override timings provided by the panel. +- samsung,sysreg-phandle: handle to syscon used to control the system registers +- vidout-i80-ldi: boolean to support i80 interface instead of rgb one +- cs-setup: clock cycles for the active period of address signal enable until + chip select is enable in i80 interface +- wr-setup: clock cycles for the active period of CS signal enable until + write signal is enable in i80 interface +- wr-act: clock cycles for the active period of CS enable in i80 interface +- wr-hold: clock cycles for the active period of CS disable until write signal + is disable in i80 interface
The device node can contain 'port' child nodes according to the bindings defined in [2]. The following are properties specific to those nodes:
Hi YoungJun,
Thank you for the patch.
On Tuesday 15 April 2014 14:47:33 YoungJun Cho wrote:
In case of using CPU interface panel, the relevant registers should be set. So this patch adds relevant dt bindings.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
.../devicetree/bindings/video/samsung-fimd.txt | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/Documentation/devicetree/bindings/video/samsung-fimd.txt b/Documentation/devicetree/bindings/video/samsung-fimd.txt index 2dad41b..924c2e1 100644 --- a/Documentation/devicetree/bindings/video/samsung-fimd.txt +++ b/Documentation/devicetree/bindings/video/samsung-fimd.txt @@ -44,6 +44,15 @@ Optional Properties:
- display-timings: timing settings for FIMD, as described in document [1]. Can be used in case timings cannot be provided otherwise or to override timings provided by the panel.
+- samsung,sysreg-phandle: handle to syscon used to control the system registers +- vidout-i80-ldi: boolean to support i80 interface instead of rgb one +- cs-setup: clock cycles for the active period of address signal enable until + chip select is enable in i80 interface +- wr-setup: clock cycles for the active period of CS signal enable until
- write signal is enable in i80 interface
+- wr-act: clock cycles for the active period of CS enable in i80 interface +- wr-hold: clock cycles for the active period of CS disable until write signal + is disable in i80 interface
Shouldn't the interface parameters be considered as a property of the slave device instead ? The bus master side is programmable, and different slaves would have different timing requirements. I think it would make more sense to specify the timings on the slave (panel) side and query them dynamically at runtime. Depending on the slave the timings could be hardcoded in the driver (as they're usually an intrinsic property of the slave) or partially or fully specified in the slave DT node.
The device node can contain 'port' child nodes according to the bindings defined in [2]. The following are properties specific to those nodes:
Hi Laurent
Thank you for the comment.
On 04/17/2014 06:26 AM, Laurent Pinchart wrote:
Hi YoungJun,
Thank you for the patch.
On Tuesday 15 April 2014 14:47:33 YoungJun Cho wrote:
In case of using CPU interface panel, the relevant registers should be set. So this patch adds relevant dt bindings.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
.../devicetree/bindings/video/samsung-fimd.txt | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/Documentation/devicetree/bindings/video/samsung-fimd.txt b/Documentation/devicetree/bindings/video/samsung-fimd.txt index 2dad41b..924c2e1 100644 --- a/Documentation/devicetree/bindings/video/samsung-fimd.txt +++ b/Documentation/devicetree/bindings/video/samsung-fimd.txt @@ -44,6 +44,15 @@ Optional Properties:
- display-timings: timing settings for FIMD, as described in document [1]. Can be used in case timings cannot be provided otherwise or to override timings provided by the panel.
+- samsung,sysreg-phandle: handle to syscon used to control the system
Oops... I have to change "samsung,sysreg-phandle" to "samsung,sysreg".
registers +- vidout-i80-ldi: boolean to support i80 interface instead of rgb one +- cs-setup: clock cycles for the active period of address signal enable until + chip select is enable in i80 interface +- wr-setup: clock cycles for the active period of CS signal enable until
- write signal is enable in i80 interface
+- wr-act: clock cycles for the active period of CS enable in i80 interface +- wr-hold: clock cycles for the active period of CS disable until write signal + is disable in i80 interface
Shouldn't the interface parameters be considered as a property of the slave device instead ? The bus master side is programmable, and different slaves would have different timing requirements. I think it would make more sense to specify the timings on the slave (panel) side and query them dynamically at runtime. Depending on the slave the timings could be hardcoded in the driver (as they're usually an intrinsic property of the slave) or partially or fully specified in the slave DT node.
Yes, you're right. These properties are related to panel in a sense.
But my intention is to use these properties to fimd node in the board specific dts file, because it decides the interface with panel and these are required by fimd only.
If dynamic query is more logical approach, I should find some ways with considering probing order.
Thank you. Best regards YJ
The device node can contain 'port' child nodes according to the bindings defined in [2]. The following are properties specific to those nodes:
In case of using CPU interface panel, the relevant registers should be set. So this patch adds relevant dt bindings.
Changelog v2: - Changes "samsung,sysreg-phandle" to "samsung,sysreg"
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- .../devicetree/bindings/video/samsung-fimd.txt | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/Documentation/devicetree/bindings/video/samsung-fimd.txt b/Documentation/devicetree/bindings/video/samsung-fimd.txt index 2dad41b..6ea1adc 100644 --- a/Documentation/devicetree/bindings/video/samsung-fimd.txt +++ b/Documentation/devicetree/bindings/video/samsung-fimd.txt @@ -44,6 +44,15 @@ Optional Properties: - display-timings: timing settings for FIMD, as described in document [1]. Can be used in case timings cannot be provided otherwise or to override timings provided by the panel. +- samsung,sysreg: handle to syscon used to control the system registers +- vidout-i80-ldi: boolean to support i80 interface instead of rgb one +- cs-setup: clock cycles for the active period of address signal enable until + chip select is enable in i80 interface +- wr-setup: clock cycles for the active period of CS signal enable until + write signal is enable in i80 interface +- wr-act: clock cycles for the active period of CS enable in i80 interface +- wr-hold: clock cycles for the active period of CS disable until write signal + is disable in i80 interface
The device node can contain 'port' child nodes according to the bindings defined in [2]. The following are properties specific to those nodes:
Hi again,
On 04/17/2014 01:53 PM, YoungJun Cho wrote:
In case of using CPU interface panel, the relevant registers should be set. So this patch adds relevant dt bindings.
Changelog v2:
- Changes "samsung,sysreg-phandle" to "samsung,sysreg"
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
.../devicetree/bindings/video/samsung-fimd.txt | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/Documentation/devicetree/bindings/video/samsung-fimd.txt b/Documentation/devicetree/bindings/video/samsung-fimd.txt index 2dad41b..6ea1adc 100644 --- a/Documentation/devicetree/bindings/video/samsung-fimd.txt +++ b/Documentation/devicetree/bindings/video/samsung-fimd.txt @@ -44,6 +44,15 @@ Optional Properties:
- display-timings: timing settings for FIMD, as described in document [1]. Can be used in case timings cannot be provided otherwise or to override timings provided by the panel.
+- samsung,sysreg: handle to syscon used to control the system registers +- vidout-i80-ldi: boolean to support i80 interface instead of rgb one +- cs-setup: clock cycles for the active period of address signal enable until
- chip select is enable in i80 interface
+- wr-setup: clock cycles for the active period of CS signal enable until
- write signal is enable in i80 interface
+- wr-act: clock cycles for the active period of CS enable in i80 interface +- wr-hold: clock cycles for the active period of CS disable until write signal
- is disable in i80 interface
As Laurent wrote earlier it would be good to consider providing these properties by panel. Panel can pass it to DSI probably via mipi_dsi_device structure. DSI to FIMD can use exynos drm_framework probably. Anyway if you add optional properties please add info about default value, ie when property is not present.
Regards Andrzej
The device node can contain 'port' child nodes according to the bindings defined in [2]. The following are properties specific to those nodes:
Hi Andrzej
Thank you for comments.
On 04/18/2014 09:32 PM, Andrzej Hajda wrote:
Hi again,
On 04/17/2014 01:53 PM, YoungJun Cho wrote:
In case of using CPU interface panel, the relevant registers should be set. So this patch adds relevant dt bindings.
Changelog v2:
- Changes "samsung,sysreg-phandle" to "samsung,sysreg"
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
.../devicetree/bindings/video/samsung-fimd.txt | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/Documentation/devicetree/bindings/video/samsung-fimd.txt b/Documentation/devicetree/bindings/video/samsung-fimd.txt index 2dad41b..6ea1adc 100644 --- a/Documentation/devicetree/bindings/video/samsung-fimd.txt +++ b/Documentation/devicetree/bindings/video/samsung-fimd.txt @@ -44,6 +44,15 @@ Optional Properties:
- display-timings: timing settings for FIMD, as described in document [1]. Can be used in case timings cannot be provided otherwise or to override timings provided by the panel.
+- samsung,sysreg: handle to syscon used to control the system registers +- vidout-i80-ldi: boolean to support i80 interface instead of rgb one +- cs-setup: clock cycles for the active period of address signal enable until
- chip select is enable in i80 interface
+- wr-setup: clock cycles for the active period of CS signal enable until
- write signal is enable in i80 interface
+- wr-act: clock cycles for the active period of CS enable in i80 interface +- wr-hold: clock cycles for the active period of CS disable until write signal
- is disable in i80 interface
As Laurent wrote earlier it would be good to consider providing these properties by panel. Panel can pass it to DSI probably via mipi_dsi_device structure. DSI to FIMD can use exynos drm_framework probably. Anyway if you add optional properties please add info about default value, ie when property is not present.
You and Laurent thought these CPU timings should be in panel.
Ok, how about that vidout-i80-ldi is remained in fimd board specific DT entry and other CPU timings relevant properties are moved to panel for considering probe order?
That's because the IRQ resource of fimd should be "lcd_sys" for I80 interface and decided in probe time(bind time after adopting super device node). But the fimd probe routine is prior to panel probe routine and it is also ugly that fimd parses the properties of panel DT for that.
Do you have any better idea?
Thank you Best regards YJ
Regards Andrzej
The device node can contain 'port' child nodes according to the bindings defined in [2]. The following are properties specific to those nodes:
Hi YoungJun,
On Thursday 17 April 2014 14:33:37 YoungJun Cho wrote:
On 04/17/2014 06:26 AM, Laurent Pinchart wrote:
On Tuesday 15 April 2014 14:47:33 YoungJun Cho wrote:
In case of using CPU interface panel, the relevant registers should be set. So this patch adds relevant dt bindings.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
.../devicetree/bindings/video/samsung-fimd.txt | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/Documentation/devicetree/bindings/video/samsung-fimd.txt b/Documentation/devicetree/bindings/video/samsung-fimd.txt index 2dad41b..924c2e1 100644 --- a/Documentation/devicetree/bindings/video/samsung-fimd.txt +++ b/Documentation/devicetree/bindings/video/samsung-fimd.txt
@@ -44,6 +44,15 @@ Optional Properties:
- display-timings: timing settings for FIMD, as described in document
[1].
Can be used in case timings cannot be provided otherwise or to override timings provided by the panel.
+- samsung,sysreg-phandle: handle to syscon used to control the system
Oops... I have to change "samsung,sysreg-phandle" to "samsung,sysreg".
registers +- vidout-i80-ldi: boolean to support i80 interface instead of rgb one +- cs-setup: clock cycles for the active period of address signal enable until
- chip select is enable in i80 interface
+- wr-setup: clock cycles for the active period of CS signal enable until
- write signal is enable in i80 interface
+- wr-act: clock cycles for the active period of CS enable in i80 interface +- wr-hold: clock cycles for the active period of CS disable until write signal
- is disable in i80 interface
Shouldn't the interface parameters be considered as a property of the slave device instead ? The bus master side is programmable, and different slaves would have different timing requirements. I think it would make more sense to specify the timings on the slave (panel) side and query them dynamically at runtime. Depending on the slave the timings could be hardcoded in the driver (as they're usually an intrinsic property of the slave) or partially or fully specified in the slave DT node.
Yes, you're right. These properties are related to panel in a sense.
But my intention is to use these properties to fimd node in the board specific dts file, because it decides the interface with panel and these are required by fimd only.
I agree, it's a bit of a gray area. The properties describe the connection between the display controller output and the panel. As such they're shared between the two components, so deciding on which node to include them in is a bit difficult. In this specific case, as the panel dictates the interface timings and the display controller is then programmed to comply with those timings, I would be tempted to consider timing information as panel properties. This obviously complicates driver implementation, as we would need to communicate timing information from panel drivers to display controller drivers.
If dynamic query is more logical approach, I should find some ways with considering probing order.
I won't push too much for storing interface timing information in the panel DT node, but I'm a bit worried that storing it in the display controller DT node might cause issues at some point. As DT is an ABI, we need to carefully consider the potential issues.
The device node can contain 'port' child nodes according to the bindings
defined in [2]. The following are properties specific to those nodes:
This patch adds I80 interface for FIMD to support command mode panel.
For this, the below features are added: - Set display interface mode relevant registers properly according to the interface type from DT - Add TE interrupt handler . FIMD driver should know TE signal from lcd panel to avoid tearing issue. - Add trigger feature . In case of command mode panel, FIMD should set trigger bit, so that image data has to be transferred to display bus or lcd panel.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/Kconfig | 1 + drivers/gpu/drm/exynos/exynos_drm_crtc.c | 11 ++ drivers/gpu/drm/exynos/exynos_drm_crtc.h | 2 + drivers/gpu/drm/exynos/exynos_drm_drv.h | 2 + drivers/gpu/drm/exynos/exynos_drm_dsi.c | 13 ++ drivers/gpu/drm/exynos/exynos_drm_fimd.c | 280 +++++++++++++++++++++++++----- include/drm/drm_mipi_dsi.h | 2 + include/video/samsung_fimd.h | 3 +- 8 files changed, 270 insertions(+), 44 deletions(-)
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 5bf5bca..f4d34f0 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -28,6 +28,7 @@ config DRM_EXYNOS_FIMD bool "Exynos DRM FIMD" depends on DRM_EXYNOS && !FB_S3C && !ARCH_MULTIPLATFORM select FB_MODE_HELPERS + select MFD_SYSCON help Choose this option if you want to use Exynos FIMD for DRM.
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 1419d11..d902d64 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -491,3 +491,14 @@ void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb) manager->ops->wait_for_vblank(manager); } } + +int exynos_drm_crtc_te_handler(struct drm_crtc *crtc) +{ + struct exynos_drm_manager *manager = to_exynos_crtc(crtc)->manager; + int ret = 0; + + if (manager->ops->te_handler) + ret = manager->ops->te_handler(manager); + + return ret; +} diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/drivers/gpu/drm/exynos/exynos_drm_crtc.h index c27b66c..8482df2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.h +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -32,4 +32,6 @@ void exynos_drm_crtc_plane_commit(struct drm_crtc *crtc, int zpos); void exynos_drm_crtc_plane_enable(struct drm_crtc *crtc, int zpos); void exynos_drm_crtc_plane_disable(struct drm_crtc *crtc, int zpos);
+int exynos_drm_crtc_te_handler(struct drm_crtc *crtc); + #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 4c5cf68..7cb0baf 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -186,6 +186,7 @@ struct exynos_drm_display { * @win_commit: apply hardware specific overlay data to registers. * @win_enable: enable hardware specific overlay. * @win_disable: disable hardware specific overlay. + * @te_handler: call driver specific TE handler callback. */ struct exynos_drm_manager; struct exynos_drm_manager_ops { @@ -207,6 +208,7 @@ struct exynos_drm_manager_ops { void (*win_commit)(struct exynos_drm_manager *mgr, int zpos); void (*win_enable)(struct exynos_drm_manager *mgr, int zpos); void (*win_disable)(struct exynos_drm_manager *mgr, int zpos); + int (*te_handler)(struct exynos_drm_manager *mgr); };
/* diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 2cf1f0b..179f2fa 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -23,6 +23,7 @@ #include <video/mipi_display.h> #include <video/videomode.h>
+#include "exynos_drm_crtc.h" #include "exynos_drm_drv.h"
/* returns true iff both arguments logically differs */ @@ -1032,10 +1033,22 @@ static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host, return (ret < 0) ? ret : xfer.rx_done; }
+static int exynos_dsi_host_te_handler(struct mipi_dsi_host *host) +{ + struct exynos_dsi *dsi = host_to_dsi(host); + struct drm_encoder *encoder = dsi->encoder; + + if (!(dsi->state & DSIM_STATE_ENABLED)) + return -EPERM; + + return exynos_drm_crtc_te_handler(encoder->crtc); +} + static const struct mipi_dsi_host_ops exynos_dsi_ops = { .attach = exynos_dsi_host_attach, .detach = exynos_dsi_host_detach, .transfer = exynos_dsi_host_transfer, + .te_handler = exynos_dsi_host_te_handler, };
static int exynos_dsi_poweron(struct exynos_dsi *dsi) diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 40fd6cc..a7a2018 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -19,6 +19,8 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/pm_runtime.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h>
#include <video/of_display_timing.h> #include <video/of_videomode.h> @@ -59,6 +61,22 @@ /* color key value register for hardware window 1 ~ 4. */ #define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + ((x - 1) * 8))
+/* i80 / RGB trigger control register */ +#define TRIGCON 0x1A4 + +/* display mode change control register except exynos4 */ +#define VIDOUT_CON 0x000 +#define VIDOUT_CON_F_I80_LDI0 (0x2 << 8) + +/* i80 interface control for main LDI register */ +#define I80IFCONFAx(x) (0x1B0 + x * 4) +#define I80IFCONFBx(x) (0x1B8 + x * 4) +#define LCD_CS_SETUP(x) (x << 16) +#define LCD_WR_SETUP(x) (x << 12) +#define LCD_WR_ACT(x) (x << 8) +#define LCD_WR_HOLD(x) (x << 4) +#define I80IFEN_ENABLE (1 << 0) + /* FIMD has totally five hardware windows. */ #define WINDOWS_NR 5
@@ -66,10 +84,14 @@
struct fimd_driver_data { unsigned int timing_base; + unsigned int lcdblk_off; + unsigned int lcdblk_vt_shift; + unsigned int lcdblk_bypass_shift;
unsigned int has_shadowcon:1; unsigned int has_clksel:1; unsigned int has_limited_fmt:1; + unsigned int has_vidoutcon:1; };
static struct fimd_driver_data s3c64xx_fimd_driver_data = { @@ -80,12 +102,19 @@ static struct fimd_driver_data s3c64xx_fimd_driver_data = {
static struct fimd_driver_data exynos4_fimd_driver_data = { .timing_base = 0x0, + .lcdblk_off = 0x210, + .lcdblk_vt_shift = 10, + .lcdblk_bypass_shift = 1, .has_shadowcon = 1, };
static struct fimd_driver_data exynos5_fimd_driver_data = { .timing_base = 0x20000, + .lcdblk_off = 0x214, + .lcdblk_vt_shift = 24, + .lcdblk_bypass_shift = 15, .has_shadowcon = 1, + .has_vidoutcon = 1, };
struct fimd_win_data { @@ -110,15 +139,23 @@ struct fimd_context { struct clk *bus_clk; struct clk *lcd_clk; void __iomem *regs; + struct regmap *sysreg; struct drm_display_mode mode; struct fimd_win_data win_data[WINDOWS_NR]; unsigned int default_win; unsigned long irq_flags; + u32 vidcon0; u32 vidcon1; + u32 vidout_con; + u32 i80ifcon; + bool i80_if; bool suspended; int pipe; wait_queue_head_t wait_vsync_queue; atomic_t wait_vsync_event; + atomic_t win_updated; + atomic_t triggering; + spinlock_t win_updated_lock;
struct exynos_drm_panel_info panel; struct fimd_driver_data *driver_data; @@ -190,6 +227,14 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx, unsigned long ideal_clk = mode->htotal * mode->vtotal * mode->vrefresh; u32 clkdiv;
+ if (ctx->i80_if) { + /* + * The frame done interrupt should be occurred prior to the + * next TE signal. + */ + ideal_clk *= 2; + } + /* Find the clock divider value that gets us closest to ideal_clk */ clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk);
@@ -218,11 +263,10 @@ static void fimd_commit(struct exynos_drm_manager *mgr) { struct fimd_context *ctx = mgr->ctx; struct drm_display_mode *mode = &ctx->mode; - struct fimd_driver_data *driver_data; - u32 val, clkdiv, vidcon1; - int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; + struct fimd_driver_data *driver_data = ctx->driver_data; + void *timing_base = ctx->regs + driver_data->timing_base; + u32 val, clkdiv;
- driver_data = ctx->driver_data; if (ctx->suspended) return;
@@ -230,33 +274,65 @@ static void fimd_commit(struct exynos_drm_manager *mgr) if (mode->htotal == 0 || mode->vtotal == 0) return;
- /* setup polarity values */ - vidcon1 = ctx->vidcon1; - if (mode->flags & DRM_MODE_FLAG_NVSYNC) - vidcon1 |= VIDCON1_INV_VSYNC; - if (mode->flags & DRM_MODE_FLAG_NHSYNC) - vidcon1 |= VIDCON1_INV_HSYNC; - writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); - - /* setup vertical timing values. */ - vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; - vbpd = mode->crtc_vtotal - mode->crtc_vsync_end; - vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay; - - val = VIDTCON0_VBPD(vbpd - 1) | - VIDTCON0_VFPD(vfpd - 1) | - VIDTCON0_VSPW(vsync_len - 1); - writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); - - /* setup horizontal timing values. */ - hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; - hbpd = mode->crtc_htotal - mode->crtc_hsync_end; - hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay; - - val = VIDTCON1_HBPD(hbpd - 1) | - VIDTCON1_HFPD(hfpd - 1) | - VIDTCON1_HSPW(hsync_len - 1); - writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); + if (ctx->i80_if) { + val = ctx->i80ifcon | I80IFEN_ENABLE; + writel(val, timing_base + I80IFCONFAx(0)); + + /* disable auto frame rate */ + writel(0, timing_base + I80IFCONFBx(0)); + + if (ctx->vidout_con) + writel(ctx->vidout_con, timing_base + VIDOUT_CON); + + /* set video type selection to i80 interface */ + if (ctx->sysreg && regmap_update_bits(ctx->sysreg, + driver_data->lcdblk_off, + 0x3 << driver_data->lcdblk_vt_shift, + 0x1 << driver_data->lcdblk_vt_shift)) { + DRM_ERROR("Failed to update sysreg for i80 i/f.\n"); + return; + } + } else { + int vsync_len, vbpd, vfpd, hsync_len, hbpd, hfpd; + u32 vidcon1; + + /* setup polarity values */ + vidcon1 = ctx->vidcon1; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + vidcon1 |= VIDCON1_INV_VSYNC; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + vidcon1 |= VIDCON1_INV_HSYNC; + writel(vidcon1, ctx->regs + driver_data->timing_base + VIDCON1); + + /* setup vertical timing values. */ + vsync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; + vbpd = mode->crtc_vtotal - mode->crtc_vsync_end; + vfpd = mode->crtc_vsync_start - mode->crtc_vdisplay; + + val = VIDTCON0_VBPD(vbpd - 1) | + VIDTCON0_VFPD(vfpd - 1) | + VIDTCON0_VSPW(vsync_len - 1); + writel(val, ctx->regs + driver_data->timing_base + VIDTCON0); + + /* setup horizontal timing values. */ + hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; + hbpd = mode->crtc_htotal - mode->crtc_hsync_end; + hfpd = mode->crtc_hsync_start - mode->crtc_hdisplay; + + val = VIDTCON1_HBPD(hbpd - 1) | + VIDTCON1_HFPD(hfpd - 1) | + VIDTCON1_HSPW(hsync_len - 1); + writel(val, ctx->regs + driver_data->timing_base + VIDTCON1); + } + + /* set bypass selection */ + if (ctx->sysreg && regmap_update_bits(ctx->sysreg, + driver_data->lcdblk_off, + 0x1 << driver_data->lcdblk_bypass_shift, + 0x1 << driver_data->lcdblk_bypass_shift)) { + DRM_ERROR("Failed to update sysreg for bypass setting.\n"); + return; + }
/* setup horizontal and vertical display size. */ val = VIDTCON2_LINEVAL(mode->vdisplay - 1) | @@ -613,6 +689,14 @@ static void fimd_win_commit(struct exynos_drm_manager *mgr, int zpos) }
win_data->enabled = true; + + if (ctx->i80_if) { + unsigned long flags; + + spin_lock_irqsave(&ctx->win_updated_lock, flags); + atomic_set(&ctx->win_updated, 1); + spin_unlock_irqrestore(&ctx->win_updated_lock, flags); + } }
static void fimd_win_disable(struct exynos_drm_manager *mgr, int zpos) @@ -802,6 +886,68 @@ static void fimd_dpms(struct exynos_drm_manager *mgr, int mode) } }
+static void fimd_trigger(struct device *dev) +{ + struct exynos_drm_manager *mgr = get_fimd_manager(dev); + struct fimd_context *ctx = mgr->ctx; + struct fimd_driver_data *driver_data = ctx->driver_data; + void *timing_base = ctx->regs + driver_data->timing_base; + u32 reg; + + atomic_set(&ctx->triggering, 1); + + reg = readl(ctx->regs + VIDINTCON0); + reg |= (VIDINTCON0_INT_ENABLE | VIDINTCON0_INT_I80IFDONE | + VIDINTCON0_INT_SYSMAINCON); + writel(reg, ctx->regs + VIDINTCON0); + + reg = readl(timing_base + TRIGCON); + reg |= 1 << 0 | 1 << 1; + writel(reg, timing_base + TRIGCON); +} + +static int fimd_te_handler(struct exynos_drm_manager *mgr) +{ + struct fimd_context *ctx = mgr->ctx; + unsigned long flags; + + /* check the crtc is detached already from encoder */ + if (ctx->pipe < 0 || !ctx->drm_dev) + return -EINVAL; + + /* + * Skips triggering if in triggering state, because multiple + * triggering request can cause panel reset. + */ + if (atomic_read(&ctx->triggering)) + return 0; + + spin_lock_irqsave(&ctx->win_updated_lock, flags); + + /* + * If there is a page flip request, trigger and handle the page flip + * event so that current fb can be updated into panel GRAM. + */ + if (atomic_read(&ctx->win_updated)) { + atomic_set(&ctx->win_updated, 0); + + fimd_trigger(ctx->dev); + } + + spin_unlock_irqrestore(&ctx->win_updated_lock, flags); + + /* wake up vsync event queue */ + if (atomic_read(&ctx->wait_vsync_event)) { + atomic_set(&ctx->wait_vsync_event, 0); + wake_up(&ctx->wait_vsync_queue); + + if (!atomic_read(&ctx->triggering)) + drm_handle_vblank(ctx->drm_dev, ctx->pipe); + } + + return 0; +} + static struct exynos_drm_manager_ops fimd_manager_ops = { .initialize = fimd_mgr_initialize, .remove = fimd_mgr_remove, @@ -815,6 +961,7 @@ static struct exynos_drm_manager_ops fimd_manager_ops = { .win_mode_set = fimd_win_mode_set, .win_commit = fimd_win_commit, .win_disable = fimd_win_disable, + .te_handler = fimd_te_handler, };
static struct exynos_drm_manager fimd_manager = { @@ -825,26 +972,40 @@ static struct exynos_drm_manager fimd_manager = { static irqreturn_t fimd_irq_handler(int irq, void *dev_id) { struct fimd_context *ctx = (struct fimd_context *)dev_id; - u32 val; + u32 val, clear_bit;
val = readl(ctx->regs + VIDINTCON1);
- if (val & VIDINTCON1_INT_FRAME) - /* VSYNC interrupt */ - writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); + clear_bit = ctx->i80_if ? VIDINTCON1_INT_I80 : VIDINTCON1_INT_FRAME; + if (val & clear_bit) + writel(clear_bit, ctx->regs + VIDINTCON1);
/* check the crtc is detached already from encoder */ if (ctx->pipe < 0 || !ctx->drm_dev) goto out;
- drm_handle_vblank(ctx->drm_dev, ctx->pipe); - exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); + if (ctx->i80_if) { + /* unset i80 frame done interrupt */ + val = readl(ctx->regs + VIDINTCON0); + val &= ~(VIDINTCON0_INT_I80IFDONE | VIDINTCON0_INT_SYSMAINCON); + writel(val, ctx->regs + VIDINTCON0);
- /* set wait vsync event to zero and wake up queue. */ - if (atomic_read(&ctx->wait_vsync_event)) { - atomic_set(&ctx->wait_vsync_event, 0); - wake_up(&ctx->wait_vsync_queue); + /* exit triggering mode */ + atomic_set(&ctx->triggering, 0); + + drm_handle_vblank(ctx->drm_dev, ctx->pipe); + exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); + } else { + drm_handle_vblank(ctx->drm_dev, ctx->pipe); + exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); + + /* set wait vsync event to zero and wake up queue. */ + if (atomic_read(&ctx->wait_vsync_event)) { + atomic_set(&ctx->wait_vsync_event, 0); + wake_up(&ctx->wait_vsync_queue); + } } + out: return IRQ_HANDLED; } @@ -853,6 +1014,7 @@ static int fimd_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct fimd_context *ctx; + struct device_node *np; struct resource *res; int win; int ret = -EINVAL; @@ -866,12 +1028,44 @@ static int fimd_probe(struct platform_device *pdev)
ctx->dev = dev; ctx->suspended = true; + ctx->driver_data = drm_fimd_get_driver_data(pdev);
if (of_property_read_bool(dev->of_node, "samsung,invert-vden")) ctx->vidcon1 |= VIDCON1_INV_VDEN; if (of_property_read_bool(dev->of_node, "samsung,invert-vclk")) ctx->vidcon1 |= VIDCON1_INV_VCLK;
+ np = of_find_node_by_name(dev->of_node, "vidout-i80-ldi"); + if (np) { + u32 val; + + ctx->i80_if = true; + + if (ctx->driver_data->has_vidoutcon) + ctx->vidout_con |= VIDOUT_CON_F_I80_LDI0; + else + ctx->vidcon0 |= VIDCON0_VIDOUT_I80_LDI0; + ctx->vidcon0 |= VIDCON0_DSI_EN; + + if (!of_property_read_u32(np, "cs-setup", &val)) + ctx->i80ifcon = LCD_CS_SETUP(val); + if (!of_property_read_u32(np, "wr-setup", &val)) + ctx->i80ifcon |= LCD_WR_SETUP(val); + if (!of_property_read_u32(np, "wr-act", &val)) + ctx->i80ifcon |= LCD_WR_ACT(val); + if (!of_property_read_u32(np, "wr-hold", &val)) + ctx->i80ifcon |= LCD_WR_HOLD(val); + + spin_lock_init(&ctx->win_updated_lock); + } + + ctx->sysreg = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,sysreg"); + if (IS_ERR(ctx->sysreg)) { + dev_warn(dev, "failed to get system register.\n"); + ctx->sysreg = NULL; + } + ctx->bus_clk = devm_clk_get(dev, "fimd"); if (IS_ERR(ctx->bus_clk)) { dev_err(dev, "failed to get bus clock\n"); @@ -890,7 +1084,8 @@ static int fimd_probe(struct platform_device *pdev) if (IS_ERR(ctx->regs)) return PTR_ERR(ctx->regs);
- res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "vsync"); + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + ctx->i80_if ? "lcd_sys" : "vsync"); if (!res) { dev_err(dev, "irq request failed.\n"); return -ENXIO; @@ -903,7 +1098,6 @@ static int fimd_probe(struct platform_device *pdev) return ret; }
- ctx->driver_data = drm_fimd_get_driver_data(pdev); init_waitqueue_head(&ctx->wait_vsync_queue); atomic_set(&ctx->wait_vsync_event, 0);
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 7209df1..244d197 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -49,6 +49,7 @@ struct mipi_dsi_msg { * @detach: detach DSI device from DSI host * @transfer: send and/or receive DSI packet, return number of received bytes, * or error + * @te_handler: call CRTC TE handler callback from DSI host */ struct mipi_dsi_host_ops { int (*attach)(struct mipi_dsi_host *host, @@ -57,6 +58,7 @@ struct mipi_dsi_host_ops { struct mipi_dsi_device *dsi); ssize_t (*transfer)(struct mipi_dsi_host *host, struct mipi_dsi_msg *msg); + int (*te_handler)(struct mipi_dsi_host *host); };
/** diff --git a/include/video/samsung_fimd.h b/include/video/samsung_fimd.h index b039320..eaad58b 100644 --- a/include/video/samsung_fimd.h +++ b/include/video/samsung_fimd.h @@ -19,6 +19,7 @@ /* VIDCON0 */
#define VIDCON0 0x00 +#define VIDCON0_DSI_EN (1 << 30) #define VIDCON0_INTERLACE (1 << 29) #define VIDCON0_VIDOUT_MASK (0x7 << 26) #define VIDCON0_VIDOUT_SHIFT 26 @@ -355,7 +356,7 @@ #define VIDINTCON0_INT_ENABLE (1 << 0)
#define VIDINTCON1 0x134 -#define VIDINTCON1_INT_I180 (1 << 2) +#define VIDINTCON1_INT_I80 (1 << 2) #define VIDINTCON1_INT_FRAME (1 << 1) #define VIDINTCON1_INT_FIFO (1 << 0)
This patch adds exynos5420 SoC support.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- .../devicetree/bindings/video/exynos_dsim.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/video/exynos_dsim.txt b/Documentation/devicetree/bindings/video/exynos_dsim.txt index 33b5730..9bbf82b 100644 --- a/Documentation/devicetree/bindings/video/exynos_dsim.txt +++ b/Documentation/devicetree/bindings/video/exynos_dsim.txt @@ -1,7 +1,9 @@ Exynos MIPI DSI Master
Required properties: - - compatible: "samsung,exynos4210-mipi-dsi" + - compatible: value should be one of the following + "samsung,exynos4210-mipi-dsi" /* for Exynos4 SoCs */ + "samsung,exynos5420-mipi-dsi" /* for Exynos5420 Soc */ - reg: physical base address and length of the registers set for the device - interrupts: should contain DSI interrupt - clocks: list of clock specifiers, must contain an entry for each required
On 15 April 2014 11:17, YoungJun Cho yj44.cho@samsung.com wrote:
This patch adds exynos5420 SoC support.
This patch just updates binding documentation :)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
.../devicetree/bindings/video/exynos_dsim.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/video/exynos_dsim.txt b/Documentation/devicetree/bindings/video/exynos_dsim.txt index 33b5730..9bbf82b 100644 --- a/Documentation/devicetree/bindings/video/exynos_dsim.txt +++ b/Documentation/devicetree/bindings/video/exynos_dsim.txt @@ -1,7 +1,9 @@ Exynos MIPI DSI Master
Required properties:
- compatible: "samsung,exynos4210-mipi-dsi"
- compatible: value should be one of the following
"samsung,exynos4210-mipi-dsi" /* for Exynos4 SoCs */
"samsung,exynos5420-mipi-dsi" /* for Exynos5420 Soc */
s/Soc/SoC
This patch adds relevant to exynos5420 compatible for exynos5420 SoC support.
Changelog v2: - Changes title, description and fixes typo (commented by Sachin Kamat)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- .../devicetree/bindings/video/exynos_dsim.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/video/exynos_dsim.txt b/Documentation/devicetree/bindings/video/exynos_dsim.txt index 33b5730..29bf3b2 100644 --- a/Documentation/devicetree/bindings/video/exynos_dsim.txt +++ b/Documentation/devicetree/bindings/video/exynos_dsim.txt @@ -1,7 +1,9 @@ Exynos MIPI DSI Master
Required properties: - - compatible: "samsung,exynos4210-mipi-dsi" + - compatible: value should be one of the following + "samsung,exynos4210-mipi-dsi" /* for Exynos4 SoCs */ + "samsung,exynos5420-mipi-dsi" /* for Exynos5420 SoCs */ - reg: physical base address and length of the registers set for the device - interrupts: should contain DSI interrupt - clocks: list of clock specifiers, must contain an entry for each required
The offset of register DSIM_PLLTMR_REG in Exynos5420 is different from the one in Exynos4 SoC.
In case of Exynos5420 SoC, there is no frequency band bit in DSIM_PLLCTRL_REG, and it uses DSIM_PHYCTRL_REG and DSIM_PHYTIMING*_REG instead. So this patch adds driver data to distinguish it.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_dsi.c | 101 ++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 179f2fa..fcd577f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -17,6 +17,7 @@
#include <linux/clk.h> #include <linux/irq.h> +#include <linux/of_device.h> #include <linux/phy/phy.h> #include <linux/regulator/consumer.h>
@@ -54,9 +55,12 @@
/* 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 */ +#define DSIM_PHYCTRL_REG 0x5c +#define DSIM_PHYTIMING_REG 0x64 +#define DSIM_PHYTIMING1_REG 0x68 +#define DSIM_PHYTIMING2_REG 0x6c
/* DSIM_STATUS */ #define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) @@ -233,6 +237,12 @@ struct exynos_dsi_transfer { #define DSIM_STATE_INITIALIZED BIT(1) #define DSIM_STATE_CMD_LPM BIT(2)
+struct exynos_dsi_driver_data { + unsigned int plltmr_reg; + + unsigned int has_freqband:1; +}; + struct exynos_dsi { struct mipi_dsi_host dsi_host; struct drm_connector connector; @@ -262,11 +272,39 @@ struct exynos_dsi {
spinlock_t transfer_lock; /* protects transfer_list */ struct list_head transfer_list; + + struct exynos_dsi_driver_data *driver_data; };
#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host) #define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
+static struct exynos_dsi_driver_data exynos4_dsi_driver_data = { + .plltmr_reg = 0x50, + .has_freqband = 1, +}; + +static struct exynos_dsi_driver_data exynos5_dsi_driver_data = { + .plltmr_reg = 0x58, +}; + +static struct of_device_id exynos_dsi_of_match[] = { + { .compatible = "samsung,exynos4210-mipi-dsi", + .data = &exynos4_dsi_driver_data }, + { .compatible = "samsung,exynos5420-mipi-dsi", + .data = &exynos5_dsi_driver_data }, + { } +}; + +static inline struct exynos_dsi_driver_data *exynos_dsi_get_driver_data( + struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(exynos_dsi_of_match, &pdev->dev); + + return (struct exynos_dsi_driver_data *)of_id->data; +} + static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi) { if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300))) @@ -340,14 +378,9 @@ static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi, static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, unsigned long freq) { - 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, - }; + struct exynos_dsi_driver_data *driver_data = dsi->driver_data; unsigned long fin, fout; - int timeout, band; + int timeout; u8 p, s; u16 m; u32 reg; @@ -368,18 +401,30 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, "failed to find PLL PMS 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; + writel(500, dsi->reg_base + driver_data->plltmr_reg); + + reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s); + + if (driver_data->has_freqband) { + 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, + }; + int band;
- dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d), band %d\n", fout, - p, m, s, band); + for (band = 0; band < ARRAY_SIZE(freq_bands); ++band) + if (fout < freq_bands[band]) + break;
- writel(500, dsi->reg_base + DSIM_PLLTMR_REG); + dev_dbg(dsi->dev, "band %d\n", band); + + reg |= DSIM_FREQ_BAND(band); + }
- 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; @@ -391,6 +436,24 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, reg = readl(dsi->reg_base + DSIM_STATUS_REG); } while ((reg & DSIM_PLL_STABLE) == 0);
+ if (!driver_data->has_freqband) { + /* b dphy ctrl */ + reg = 0x0af & 0x1ff; + writel(reg, dsi->reg_base + DSIM_PHYCTRL_REG); + + /* phy timing */ + reg = 0x06 << 8 | 0x0b; + writel(reg, dsi->reg_base + DSIM_PHYTIMING_REG); + + /* phy timing 1 */ + reg = 0x07 << 24 | 0x27 << 16 | 0x0d << 8 | 0x08; + writel(reg, dsi->reg_base + DSIM_PHYTIMING1_REG); + + /* phy timing 2 */ + reg = 0x09 << 16 | 0x0d << 8 | 0x0b; + writel(reg, dsi->reg_base + DSIM_PHYTIMING2_REG); + } + return fout; }
@@ -1412,6 +1475,7 @@ static int exynos_dsi_probe(struct platform_device *pdev) dsi->dsi_host.dev = &pdev->dev;
dsi->dev = &pdev->dev; + dsi->driver_data = exynos_dsi_get_driver_data(pdev);
ret = exynos_dsi_parse_dt(dsi); if (ret) @@ -1516,11 +1580,6 @@ static const struct dev_pm_ops exynos_dsi_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume) };
-static struct of_device_id exynos_dsi_of_match[] = { - { .compatible = "samsung,exynos4210-mipi-dsi" }, - { } -}; - struct platform_driver dsi_driver = { .probe = exynos_dsi_probe, .remove = exynos_dsi_remove,
This patch adds DT bindings for s6e3fa0 panel. The bindings describes panel resources, display timings, delays and physical size.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- .../devicetree/bindings/panel/samsung,s6e3fa0.txt | 52 ++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt
diff --git a/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt new file mode 100644 index 0000000..5986899 --- /dev/null +++ b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt @@ -0,0 +1,52 @@ +Samsung S6E3FA0 AMOLED LCD 5.7 inch panel + +Required properties: + - compatible: "samsung,s6e3fa0" + - reg: the virtual channel number of a DSI peripheral + - vdd3-supply: core voltage supply + - vci-supply: voltage supply for analog circuits + - reset-gpio: a GPIO spec for the reset pin + - display-timings: timings for the connected panel as described by [1] + +Optional properties: + - power-on-delay: delay after turning regulators on [ms] + - reset-delay: delay after reset sequence [ms] + - init-delay: delay after initialization sequence [ms] + - panel-width-mm: physical panel width [mm] + - panel-height-mm: physical panel height [mm] + +The device node can contain one 'port' child node with one child +'endpoint' node, according to the bindings defined in [2]. This +node should describe panel's video bus. + +[1]: Documentation/devicetree/bindings/video/display-timing.txt +[2]: Documentation/devicetree/bindings/media/video-interfaces.txt + +Example: + + panel { + compatible = "samsung,s6e3fa0"; + reg = <0>; + vdd3-supply = <&vcclcd_reg>; + vci-supply = <&vlcd_reg>; + reset-gpio = <&gpy7 4 0>; + power-on-delay= <10>; + reset-delay = <5>; + init-delay = <120>; + panel-width-mm = <71>; + panel-height-mm = <126>; + + display-timings { + timing0: timing-0 { + clock-frequency = <57153600>; + hactive = <1080>; + vactive = <1920>; + hfront-porch = <2>; + hback-porch = <2>; + hsync-len = <1>; + vfront-porch = <1>; + vback-porch = <4>; + vsync-len = <1>; + }; + }; + };
On 15 April 2014 11:17, YoungJun Cho yj44.cho@samsung.com wrote:
This patch adds DT bindings for s6e3fa0 panel. The bindings describes panel resources, display timings, delays and physical size.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
.../devicetree/bindings/panel/samsung,s6e3fa0.txt | 52 ++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt
diff --git a/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt new file mode 100644 index 0000000..5986899 --- /dev/null +++ b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt @@ -0,0 +1,52 @@ +Samsung S6E3FA0 AMOLED LCD 5.7 inch panel
+Required properties:
- compatible: "samsung,s6e3fa0"
- reg: the virtual channel number of a DSI peripheral
- vdd3-supply: core voltage supply
- vci-supply: voltage supply for analog circuits
- reset-gpio: a GPIO spec for the reset pin
- display-timings: timings for the connected panel as described by [1]
+Optional properties:
- power-on-delay: delay after turning regulators on [ms]
- reset-delay: delay after reset sequence [ms]
- init-delay: delay after initialization sequence [ms]
- panel-width-mm: physical panel width [mm]
- panel-height-mm: physical panel height [mm]
+The device node can contain one 'port' child node with one child +'endpoint' node, according to the bindings defined in [2]. This +node should describe panel's video bus.
+[1]: Documentation/devicetree/bindings/video/display-timing.txt +[2]: Documentation/devicetree/bindings/media/video-interfaces.txt
+Example:
panel {
How about panel@0?
compatible = "samsung,s6e3fa0";
reg = <0>;
This patch adds DT bindings for s6e3fa0 panel. The bindings describes panel resources, display timings, delays and physical size.
Changelog v2: - Adds unit address (commented by Sachin Kamat)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- .../devicetree/bindings/panel/samsung,s6e3fa0.txt | 52 ++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt
diff --git a/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt new file mode 100644 index 0000000..889a74d --- /dev/null +++ b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt @@ -0,0 +1,52 @@ +Samsung S6E3FA0 AMOLED LCD 5.7 inch panel + +Required properties: + - compatible: "samsung,s6e3fa0" + - reg: the virtual channel number of a DSI peripheral + - vdd3-supply: core voltage supply + - vci-supply: voltage supply for analog circuits + - reset-gpio: a GPIO spec for the reset pin + - display-timings: timings for the connected panel as described by [1] + +Optional properties: + - power-on-delay: delay after turning regulators on [ms] + - reset-delay: delay after reset sequence [ms] + - init-delay: delay after initialization sequence [ms] + - panel-width-mm: physical panel width [mm] + - panel-height-mm: physical panel height [mm] + +The device node can contain one 'port' child node with one child +'endpoint' node, according to the bindings defined in [2]. This +node should describe panel's video bus. + +[1]: Documentation/devicetree/bindings/video/display-timing.txt +[2]: Documentation/devicetree/bindings/media/video-interfaces.txt + +Example: + + panel@0 { + compatible = "samsung,s6e3fa0"; + reg = <0>; + vdd3-supply = <&vcclcd_reg>; + vci-supply = <&vlcd_reg>; + reset-gpio = <&gpy7 4 0>; + power-on-delay= <10>; + reset-delay = <5>; + init-delay = <120>; + panel-width-mm = <71>; + panel-height-mm = <126>; + + display-timings { + timing0: timing-0 { + clock-frequency = <57153600>; + hactive = <1080>; + vactive = <1920>; + hfront-porch = <2>; + hback-porch = <2>; + hsync-len = <1>; + vfront-porch = <1>; + vback-porch = <4>; + vsync-len = <1>; + }; + }; + };
Hi YoungJun,
Thank you for the patch.
On Wednesday 16 April 2014 13:38:57 YoungJun Cho wrote:
This patch adds DT bindings for s6e3fa0 panel. The bindings describes panel resources, display timings, delays and physical size.
Changelog v2:
- Adds unit address (commented by Sachin Kamat)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
.../devicetree/bindings/panel/samsung,s6e3fa0.txt | 52 +++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt
diff --git a/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt new file mode 100644 index 0000000..889a74d --- /dev/null +++ b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt @@ -0,0 +1,52 @@ +Samsung S6E3FA0 AMOLED LCD 5.7 inch panel
+Required properties:
- compatible: "samsung,s6e3fa0"
- reg: the virtual channel number of a DSI peripheral
- vdd3-supply: core voltage supply
- vci-supply: voltage supply for analog circuits
- reset-gpio: a GPIO spec for the reset pin
- display-timings: timings for the connected panel as described by [1]
+Optional properties:
- power-on-delay: delay after turning regulators on [ms]
- reset-delay: delay after reset sequence [ms]
- init-delay: delay after initialization sequence [ms]
- panel-width-mm: physical panel width [mm]
- panel-height-mm: physical panel height [mm]
Based on the next patch the panel seems to require quite a lot of device- specific handling, although I might be mistaken as I'm not too familiar with DSI. If my understanding is correct, do we really need to specify all those properties in DT, or could they be hardcoded in the driver ? I would usually push for generic drivers that read device information from DT, but I'm wondering whether that is the right choice here.
If you decide to keep the information in DT I think you should document what happens when optional properties are not specified. I suppose the driver uses a default value, I would document it in the DT bindings.
+The device node can contain one 'port' child node with one child +'endpoint' node, according to the bindings defined in [2]. This +node should describe panel's video bus.
+[1]: Documentation/devicetree/bindings/video/display-timing.txt +[2]: Documentation/devicetree/bindings/media/video-interfaces.txt
+Example:
- panel@0 {
compatible = "samsung,s6e3fa0";
reg = <0>;
vdd3-supply = <&vcclcd_reg>;
vci-supply = <&vlcd_reg>;
reset-gpio = <&gpy7 4 0>;
power-on-delay= <10>;
reset-delay = <5>;
init-delay = <120>;
panel-width-mm = <71>;
panel-height-mm = <126>;
display-timings {
timing0: timing-0 {
clock-frequency = <57153600>;
hactive = <1080>;
vactive = <1920>;
hfront-porch = <2>;
hback-porch = <2>;
hsync-len = <1>;
vfront-porch = <1>;
vback-porch = <4>;
vsync-len = <1>;
};
};
- };
This patch adds DT bindings for s6e3fa0 panel. The bindings describes panel resources, display timings, delays and physical size.
Changelog v2: - Adds unit address (commented by Sachin Kamat) Changelog v3: - Removes optional delay, size properties (commented by Laurent Pinchart) - Adds OLED detection, TE gpio properties
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- .../devicetree/bindings/panel/samsung,s6e3fa0.txt | 46 ++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt
diff --git a/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt new file mode 100644 index 0000000..8aabeca --- /dev/null +++ b/Documentation/devicetree/bindings/panel/samsung,s6e3fa0.txt @@ -0,0 +1,46 @@ +Samsung S6E3FA0 AMOLED LCD 5.7 inch panel + +Required properties: + - compatible: "samsung,s6e3fa0" + - reg: the virtual channel number of a DSI peripheral + - vdd3-supply: core voltage supply + - vci-supply: voltage supply for analog circuits + - reset-gpio: a GPIO spec for the reset pin + - det-gpio: a GPIO spec for the OLED detection pin + - te-gpio: a GPIO spec for the TE pin + - display-timings: timings for the connected panel as described by [1] + +Optional properties: + +The device node can contain one 'port' child node with one child +'endpoint' node, according to the bindings defined in [2]. This +node should describe panel's video bus. + +[1]: Documentation/devicetree/bindings/video/display-timing.txt +[2]: Documentation/devicetree/bindings/media/video-interfaces.txt + +Example: + + panel@0 { + compatible = "samsung,s6e3fa0"; + reg = <0>; + vdd3-supply = <&vcclcd_reg>; + vci-supply = <&vlcd_reg>; + reset-gpio = <&gpy7 4 0>; + det-gpio = <&gpg0 6 0>; + te-gpio = <&gpd1 7 0>; + + display-timings { + timing0: timing-0 { + clock-frequency = <0>; + hactive = <1080>; + vactive = <1920>; + hfront-porch = <2>; + hback-porch = <2>; + hsync-len = <1>; + vfront-porch = <1>; + vback-porch = <4>; + vsync-len = <1>; + }; + }; + };
This patch adds MIPI-DSI command mode based S6E3FA0 AMOLED LCD Panel driver.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/panel/Kconfig | 7 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-s6e3fa0.c | 544 +++++++++++++++++++++++++++++++++ 3 files changed, 552 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-s6e3fa0.c
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 4ec874d..be1392e 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -30,4 +30,11 @@ config DRM_PANEL_S6E8AA0 select DRM_MIPI_DSI select VIDEOMODE_HELPERS
+config DRM_PANEL_S6E3FA0 + tristate "S6E3FA0 DSI command mode panel" + depends on DRM && DRM_PANEL + depends on OF + select DRM_MIPI_DSI + select VIDEOMODE_HELPERS + endmenu diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 8b92921..85c6738 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o obj-$(CONFIG_DRM_PANEL_LD9040) += panel-ld9040.o obj-$(CONFIG_DRM_PANEL_S6E8AA0) += panel-s6e8aa0.o +obj-$(CONFIG_DRM_PANEL_S6E3FA0) += panel-s6e3fa0.o diff --git a/drivers/gpu/drm/panel/panel-s6e3fa0.c b/drivers/gpu/drm/panel/panel-s6e3fa0.c new file mode 100644 index 0000000..784a614 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-s6e3fa0.c @@ -0,0 +1,544 @@ +/* + * MIPI-DSI based s6e3fa0 AMOLED LCD 5.7 inch panel driver. + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * + * YoungJun Cho yj44.cho@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 <drm/drmP.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h> + +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> +#include <video/of_videomode.h> +#include <video/videomode.h> + +#define MTP_ID_LEN 3 +#define GAMMA_LEVEL_NUM 30 + +struct s6e3fa0 { + struct device *dev; + struct drm_panel panel; + + struct regulator_bulk_data supplies[2]; + struct gpio_desc *reset_gpio; + struct gpio_desc *det_gpio; + struct gpio_desc *te_gpio; + unsigned int power_on_delay; + unsigned int reset_delay; + unsigned int init_delay; + struct videomode vm; + unsigned int width_mm; + unsigned int height_mm; + + unsigned char id; + unsigned char vddm; + unsigned int brightness; +}; + +#define panel_to_s6e3fa0(p) container_of(p, struct s6e3fa0, panel) + +static const unsigned char s6e3fa0_vddm_lut[][2] = { + {0x00, 0x0d}, {0x01, 0x0d}, {0x02, 0x0e}, {0x03, 0x0f}, {0x04, 0x10}, + {0x05, 0x11}, {0x06, 0x12}, {0x07, 0x13}, {0x08, 0x14}, {0x09, 0x15}, + {0x0a, 0x16}, {0x0b, 0x17}, {0x0c, 0x18}, {0x0d, 0x19}, {0x0e, 0x1a}, + {0x0f, 0x1b}, {0x10, 0x1c}, {0x11, 0x1d}, {0x12, 0x1e}, {0x13, 0x1f}, + {0x14, 0x20}, {0x15, 0x21}, {0x16, 0x22}, {0x17, 0x23}, {0x18, 0x24}, + {0x19, 0x25}, {0x1a, 0x26}, {0x1b, 0x27}, {0x1c, 0x28}, {0x1d, 0x29}, + {0x1e, 0x2a}, {0x1f, 0x2b}, {0x20, 0x2c}, {0x21, 0x2d}, {0x22, 0x2e}, + {0x23, 0x2f}, {0x24, 0x30}, {0x25, 0x31}, {0x26, 0x32}, {0x27, 0x33}, + {0x28, 0x34}, {0x29, 0x35}, {0x2a, 0x36}, {0x2b, 0x37}, {0x2c, 0x38}, + {0x2d, 0x39}, {0x2e, 0x3a}, {0x2f, 0x3b}, {0x30, 0x3c}, {0x31, 0x3d}, + {0x32, 0x3e}, {0x33, 0x3f}, {0x34, 0x3f}, {0x35, 0x3f}, {0x36, 0x3f}, + {0x37, 0x3f}, {0x38, 0x3f}, {0x39, 0x3f}, {0x3a, 0x3f}, {0x3b, 0x3f}, + {0x3c, 0x3f}, {0x3d, 0x3f}, {0x3e, 0x3f}, {0x3f, 0x3f}, {0x40, 0x0c}, + {0x41, 0x0b}, {0x42, 0x0a}, {0x43, 0x09}, {0x44, 0x08}, {0x45, 0x07}, + {0x46, 0x06}, {0x47, 0x05}, {0x48, 0x04}, {0x49, 0x03}, {0x4a, 0x02}, + {0x4b, 0x01}, {0x4c, 0x40}, {0x4d, 0x41}, {0x4e, 0x42}, {0x4f, 0x43}, + {0x50, 0x44}, {0x51, 0x45}, {0x52, 0x46}, {0x53, 0x47}, {0x54, 0x48}, + {0x55, 0x49}, {0x56, 0x4a}, {0x57, 0x4b}, {0x58, 0x4c}, {0x59, 0x4d}, + {0x5a, 0x4e}, {0x5b, 0x4f}, {0x5c, 0x50}, {0x5d, 0x51}, {0x5e, 0x52}, + {0x5f, 0x53}, {0x60, 0x54}, {0x61, 0x55}, {0x62, 0x56}, {0x63, 0x57}, + {0x64, 0x58}, {0x65, 0x59}, {0x66, 0x5a}, {0x67, 0x5b}, {0x68, 0x5c}, + {0x69, 0x5d}, {0x6a, 0x5e}, {0x6b, 0x5f}, {0x6c, 0x60}, {0x6d, 0x61}, + {0x6e, 0x62}, {0x6f, 0x63}, {0x70, 0x64}, {0x71, 0x65}, {0x72, 0x66}, + {0x73, 0x67}, {0x74, 0x68}, {0x75, 0x69}, {0x76, 0x6a}, {0x77, 0x6b}, + {0x78, 0x6c}, {0x79, 0x6d}, {0x7a, 0x6e}, {0x7b, 0x6f}, {0x7c, 0x70}, + {0x7d, 0x71}, {0x7e, 0x72}, {0x7f, 0x73}, +}; + +static int s6e3fa0_dcs_read(struct s6e3fa0 *ctx, unsigned char cmd, + void *data, size_t len) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + + return mipi_dsi_dcs_read(dsi, dsi->channel, cmd, data, len); +} + +static void s6e3fa0_dcs_write(struct s6e3fa0 *ctx, const void *data, size_t len) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + + mipi_dsi_dcs_write(dsi, dsi->channel, data, len); +} + +#define s6e3fa0_dcs_write_seq(ctx, seq...) \ +do { \ + const unsigned char d[] = { seq }; \ + BUILD_BUG_ON_MSG(ARRAY_SIZE(d) > 64, "too big seq for stack"); \ + s6e3fa0_dcs_write(ctx, d, ARRAY_SIZE(d)); \ +} while (0) + +#define s6e3fa0_dcs_write_seq_static(ctx, seq...) \ +do { \ + static const unsigned char d[] = { seq }; \ + s6e3fa0_dcs_write(ctx, d, ARRAY_SIZE(d)); \ +} while (0) + +static void s6e3fa0_apply_level_1_key(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xf0, 0x5a, 0x5a); +} + +static void s6e3fa0_apply_level_2_key(struct s6e3fa0 *ctx, bool on) +{ + if (on) + s6e3fa0_dcs_write_seq_static(ctx, 0xfc, 0x5a, 0x5a); + else + s6e3fa0_dcs_write_seq_static(ctx, 0xfc, 0xa5, 0xa5); +} + +static void s6e3fa0_set_maximum_return_packet_size(struct s6e3fa0 *ctx, + unsigned int size) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + const struct mipi_dsi_host_ops *ops = dsi->host->ops; + + if (ops && ops->transfer) { + unsigned char buf[] = {size, 0}; + struct mipi_dsi_msg msg = { + .channel = dsi->channel, + .type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, + .tx_len = sizeof(buf), + .tx_buf = buf + }; + + ops->transfer(dsi->host, &msg); + } +} + +static void s6e3fa0_read_mtp_id(struct s6e3fa0 *ctx) +{ + unsigned char id[MTP_ID_LEN]; + int ret; + + s6e3fa0_set_maximum_return_packet_size(ctx, MTP_ID_LEN); + ret = s6e3fa0_dcs_read(ctx, 0x04, id, MTP_ID_LEN); + if (ret < MTP_ID_LEN || id[0] == 0x00) { + dev_err(ctx->dev, "failed to read id\n"); + return; + } + + dev_info(ctx->dev, "ID: 0x%02x, 0x%02x, 0x%02x\n", id[0], id[1], id[2]); + + ctx->id = id[2]; +} + +static void s6e3fa0_read_vddm(struct s6e3fa0 *ctx) +{ + unsigned char vddm; + int ret; + + s6e3fa0_apply_level_2_key(ctx, true); + s6e3fa0_dcs_write_seq_static(ctx, 0xb0, 0x16); + s6e3fa0_set_maximum_return_packet_size(ctx, 1); + ret = s6e3fa0_dcs_read(ctx, 0xd7, &vddm, 1); + s6e3fa0_apply_level_2_key(ctx, false); + + if (ret < 1 || vddm > 0x7f) { + dev_err(ctx->dev, "failed to read vddm\n"); + return; + } + + ctx->vddm = s6e3fa0_vddm_lut[vddm][1]; +} + +static void s6e3fa0_set_pentile_control(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xc0, 0x00, 0x02, 0x03, 0x32, 0x03, + 0x44, 0x44, 0xc0, 0x00, 0x1c, 0x20, 0xe8); +} + +static void s6e3fa0_write_als(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xe3, 0xff, 0xff, 0xff, 0xff); +} + +static void s6e3fa0_set_readability_enhancement(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xfe, 0x00, 0x03, 0xb0, 0x2b, 0xfe, + 0xe4); +} + +static void s6e3fa0_set_common_control(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_pentile_control(ctx); + s6e3fa0_write_als(ctx); + s6e3fa0_set_readability_enhancement(ctx); +} + +static void s6e3fa0_set_gamma(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xca, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x00); +} + +static void s6e3fa0_set_aid(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xb2, 0x00, 0x06, 0x00, 0x06); +} + +static void s6e3fa0_set_elvss(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xb6, 0x88, 0x0a); +} + +static void s6e3fa0_update_panel(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xf7, 0x03); +} + +static void s6e3fa0_write_acl(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0x55, 0x02); +} + +static void s6e3fa0_set_brightness_control(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_gamma(ctx); + s6e3fa0_set_aid(ctx); + s6e3fa0_set_elvss(ctx); + s6e3fa0_update_panel(ctx); + s6e3fa0_write_acl(ctx); +} + +static void s6e3fa0_set_temperature(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xb0, 0x05, 0xb8, 0x19); +} + +static void s6e3fa0_set_elvss_control(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_temperature(ctx); + s6e3fa0_set_elvss(ctx); +} + +static void s6e3fa0_set_te_on(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0x35, 0x00); +} + +static void s6e3fa0_set_etc_and_write_vddm(struct s6e3fa0 *ctx) +{ + s6e3fa0_apply_level_2_key(ctx, true); + s6e3fa0_dcs_write_seq(ctx, 0xed, 0x0c, 0x04, 0xff, 0x01, 0xb0, 0x16, + 0xd7, ctx->vddm); + s6e3fa0_apply_level_2_key(ctx, false); +} + +static void s6e3fa0_set_etc_condition(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_te_on(ctx); + s6e3fa0_set_etc_and_write_vddm(ctx); +} + +static void s6e3fa0_panel_init(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_common_control(ctx); + s6e3fa0_set_brightness_control(ctx); + s6e3fa0_set_elvss_control(ctx); + s6e3fa0_set_etc_condition(ctx); + + msleep(ctx->init_delay); +} + +static int s6e3fa0_power_off(struct s6e3fa0 *ctx) +{ + gpiod_set_value(ctx->reset_gpio, 0); + + return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); +} + +static int s6e3fa0_power_on(struct s6e3fa0 *ctx) +{ + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + if (ret) + return ret; + + msleep(ctx->power_on_delay); + + gpiod_set_value(ctx->reset_gpio, 1); + gpiod_set_value(ctx->reset_gpio, 0); + usleep_range(1000, 2000); + gpiod_set_value(ctx->reset_gpio, 1); + + msleep(ctx->reset_delay); + + return 0; +} + +static void s6e3fa0_set_sequence(struct s6e3fa0 *ctx) +{ + s6e3fa0_apply_level_1_key(ctx); + s6e3fa0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE); + msleep(20); + + s6e3fa0_read_mtp_id(ctx); + s6e3fa0_read_vddm(ctx); + + s6e3fa0_panel_init(ctx); + + s6e3fa0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON); +} + +static int s6e3fa0_disable(struct drm_panel *panel) +{ + struct s6e3fa0 *ctx = panel_to_s6e3fa0(panel); + + s6e3fa0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE); + s6e3fa0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF); + msleep(35); + + return s6e3fa0_power_off(ctx); +} + +static int s6e3fa0_enable(struct drm_panel *panel) +{ + struct s6e3fa0 *ctx = panel_to_s6e3fa0(panel); + int ret; + + ret = s6e3fa0_power_on(ctx); + if (ret) + return ret; + + s6e3fa0_set_sequence(ctx); + + return ret; +} + +static int s6e3fa0_get_modes(struct drm_panel *panel) +{ + struct drm_connector *connector = panel->connector; + struct s6e3fa0 *ctx = panel_to_s6e3fa0(panel); + struct drm_display_mode *mode; + + mode = drm_mode_create(connector->dev); + if (!mode) { + DRM_ERROR("failed to create a new display mode\n"); + return 0; + } + + drm_display_mode_from_videomode(&ctx->vm, mode); + mode->width_mm = ctx->width_mm; + mode->height_mm = ctx->height_mm; + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs s6e3fa0_drm_funcs = { + .disable = s6e3fa0_disable, + .enable = s6e3fa0_enable, + .get_modes = s6e3fa0_get_modes, +}; + +static int s6e3fa0_parse_dt(struct s6e3fa0 *ctx) +{ + struct device *dev = ctx->dev; + struct device_node *np = dev->of_node; + int ret; + + ret = of_get_videomode(np, &ctx->vm, 0); + if (ret) + return ret; + + of_property_read_u32(np, "power-on-delay", &ctx->power_on_delay); + of_property_read_u32(np, "reset-delay", &ctx->reset_delay); + of_property_read_u32(np, "init-delay", &ctx->init_delay); + of_property_read_u32(np, "panel-width-mm", &ctx->width_mm); + of_property_read_u32(np, "panel-height-mm", &ctx->height_mm); + + return 0; +} + +irqreturn_t s6e3fa0_det_interrupt(int irq, void *dev_id) +{ + /* TODO */ + return IRQ_HANDLED; +} + +irqreturn_t s6e3fa0_te_interrupt(int irq, void *dev_id) +{ + struct s6e3fa0 *ctx = dev_id; + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + struct mipi_dsi_host *host = dsi->host; + const struct mipi_dsi_host_ops *ops = host->ops; + + if (ops && ops->te_handler) + ops->te_handler(host); + + return IRQ_HANDLED; +} + +static int s6e3fa0_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct s6e3fa0 *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(struct s6e3fa0), GFP_KERNEL); + if (!ctx) { + dev_err(dev, "failed to allocate s6e3fa0 structure.\n"); + return -ENOMEM; + } + + mipi_dsi_set_drvdata(dsi, ctx); + + ctx->dev = dev; + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + + ret = s6e3fa0_parse_dt(ctx); + if (ret) + return ret; + + ctx->supplies[0].supply = "vdd3"; + ctx->supplies[1].supply = "vci"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), + ctx->supplies); + if (ret) { + dev_err(dev, "failed to get regulators: %d\n", ret); + return ret; + } + + ctx->reset_gpio = devm_gpiod_get(dev, "reset"); + if (IS_ERR(ctx->reset_gpio)) { + dev_err(dev, "failed to get reset gpio: %ld\n", + PTR_ERR(ctx->reset_gpio)); + return PTR_ERR(ctx->reset_gpio); + } + ret = gpiod_direction_output(ctx->reset_gpio, 1); + if (ret < 0) { + dev_err(dev, "failed to configure reset gpio: %d\n", ret); + return ret; + } + + ctx->det_gpio = devm_gpiod_get(dev, "det"); + if (IS_ERR(ctx->det_gpio)) { + dev_err(dev, "failed to get det gpio: %ld\n", + PTR_ERR(ctx->det_gpio)); + return PTR_ERR(ctx->det_gpio); + } + ret = gpiod_direction_input(ctx->det_gpio); + if (ret < 0) { + dev_err(dev, "failed to configure det gpio: %d\n", ret); + return ret; + } + ret = devm_request_irq(dev, gpiod_to_irq(ctx->det_gpio), + s6e3fa0_det_interrupt, + IRQF_TRIGGER_FALLING, + "oled-det", ctx); + if (ret) { + dev_err(dev, "failed to request det irq: %d\n", ret); + return ret; + } + + ctx->te_gpio = devm_gpiod_get(dev, "te"); + if (IS_ERR(ctx->te_gpio)) { + dev_err(dev, "failed to get te gpio: %ld\n", + PTR_ERR(ctx->te_gpio)); + return PTR_ERR(ctx->te_gpio); + } + ret = gpiod_direction_input(ctx->te_gpio); + if (ret < 0) { + dev_err(dev, "failed to configure te gpio: %d\n", ret); + return ret; + } + ret = devm_request_irq(dev, gpiod_to_irq(ctx->te_gpio), + s6e3fa0_te_interrupt, + IRQF_TRIGGER_RISING, + "TE", ctx); + if (ret) { + dev_err(dev, "failed to request det irq: %d\n", ret); + return ret; + } + + ctx->brightness = GAMMA_LEVEL_NUM - 1; + + drm_panel_init(&ctx->panel); + ctx->panel.dev = dev; + ctx->panel.funcs = &s6e3fa0_drm_funcs; + + ret = drm_panel_add(&ctx->panel); + if (ret) + return ret; + + ret = mipi_dsi_attach(dsi); + if (ret) + drm_panel_remove(&ctx->panel); + + return ret; +} + +static int s6e3fa0_remove(struct mipi_dsi_device *dsi) +{ + struct s6e3fa0 *ctx = mipi_dsi_get_drvdata(dsi); + + s6e3fa0_power_off(ctx); + + mipi_dsi_detach(dsi); + drm_panel_remove(&ctx->panel); + + return 0; +} + +static struct of_device_id s6e3fa0_of_match[] = { + { .compatible = "samsung,s6e3fa0" }, + { } +}; +MODULE_DEVICE_TABLE(of, s6e3fa0_of_match); + +static struct mipi_dsi_driver s6e3fa0_driver = { + .probe = s6e3fa0_probe, + .remove = s6e3fa0_remove, + .driver = { + .name = "panel_s6e3fa0", + .owner = THIS_MODULE, + .of_match_table = s6e3fa0_of_match, + }, +}; +module_mipi_dsi_driver(s6e3fa0_driver); + +MODULE_AUTHOR("YoungJun Cho yj44.cho@samsung.com"); +MODULE_DESCRIPTION("MIPI-DSI based s6e3fa0 AMOLED LCD Panel Driver"); +MODULE_LICENSE("GPL v2");
This patch adds MIPI-DSI command mode based S6E3FA0 AMOLED LCD Panel driver.
Changelog v2: - Declares delay, size properties in probe routine instead of DT
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- drivers/gpu/drm/panel/Kconfig | 7 + drivers/gpu/drm/panel/Makefile | 1 + drivers/gpu/drm/panel/panel-s6e3fa0.c | 547 +++++++++++++++++++++++++++++++++ 3 files changed, 555 insertions(+) create mode 100644 drivers/gpu/drm/panel/panel-s6e3fa0.c
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 4ec874d..be1392e 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -30,4 +30,11 @@ config DRM_PANEL_S6E8AA0 select DRM_MIPI_DSI select VIDEOMODE_HELPERS
+config DRM_PANEL_S6E3FA0 + tristate "S6E3FA0 DSI command mode panel" + depends on DRM && DRM_PANEL + depends on OF + select DRM_MIPI_DSI + select VIDEOMODE_HELPERS + endmenu diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 8b92921..85c6738 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o obj-$(CONFIG_DRM_PANEL_LD9040) += panel-ld9040.o obj-$(CONFIG_DRM_PANEL_S6E8AA0) += panel-s6e8aa0.o +obj-$(CONFIG_DRM_PANEL_S6E3FA0) += panel-s6e3fa0.o diff --git a/drivers/gpu/drm/panel/panel-s6e3fa0.c b/drivers/gpu/drm/panel/panel-s6e3fa0.c new file mode 100644 index 0000000..4014951 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-s6e3fa0.c @@ -0,0 +1,547 @@ +/* + * MIPI-DSI based s6e3fa0 AMOLED LCD 5.7 inch panel driver. + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * + * YoungJun Cho yj44.cho@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 <drm/drmP.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h> + +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> +#include <video/of_videomode.h> +#include <video/videomode.h> + +#define MTP_ID_LEN 3 +#define GAMMA_LEVEL_NUM 30 + +struct s6e3fa0 { + struct device *dev; + struct drm_panel panel; + + struct regulator_bulk_data supplies[2]; + struct gpio_desc *reset_gpio; + struct gpio_desc *det_gpio; + struct gpio_desc *te_gpio; + struct videomode vm; + + unsigned int power_on_delay; + unsigned int reset_delay; + unsigned int init_delay; + unsigned int width_mm; + unsigned int height_mm; + + unsigned char id; + unsigned char vddm; + unsigned int brightness; +}; + +#define panel_to_s6e3fa0(p) container_of(p, struct s6e3fa0, panel) + +static const unsigned char s6e3fa0_vddm_lut[][2] = { + {0x00, 0x0d}, {0x01, 0x0d}, {0x02, 0x0e}, {0x03, 0x0f}, {0x04, 0x10}, + {0x05, 0x11}, {0x06, 0x12}, {0x07, 0x13}, {0x08, 0x14}, {0x09, 0x15}, + {0x0a, 0x16}, {0x0b, 0x17}, {0x0c, 0x18}, {0x0d, 0x19}, {0x0e, 0x1a}, + {0x0f, 0x1b}, {0x10, 0x1c}, {0x11, 0x1d}, {0x12, 0x1e}, {0x13, 0x1f}, + {0x14, 0x20}, {0x15, 0x21}, {0x16, 0x22}, {0x17, 0x23}, {0x18, 0x24}, + {0x19, 0x25}, {0x1a, 0x26}, {0x1b, 0x27}, {0x1c, 0x28}, {0x1d, 0x29}, + {0x1e, 0x2a}, {0x1f, 0x2b}, {0x20, 0x2c}, {0x21, 0x2d}, {0x22, 0x2e}, + {0x23, 0x2f}, {0x24, 0x30}, {0x25, 0x31}, {0x26, 0x32}, {0x27, 0x33}, + {0x28, 0x34}, {0x29, 0x35}, {0x2a, 0x36}, {0x2b, 0x37}, {0x2c, 0x38}, + {0x2d, 0x39}, {0x2e, 0x3a}, {0x2f, 0x3b}, {0x30, 0x3c}, {0x31, 0x3d}, + {0x32, 0x3e}, {0x33, 0x3f}, {0x34, 0x3f}, {0x35, 0x3f}, {0x36, 0x3f}, + {0x37, 0x3f}, {0x38, 0x3f}, {0x39, 0x3f}, {0x3a, 0x3f}, {0x3b, 0x3f}, + {0x3c, 0x3f}, {0x3d, 0x3f}, {0x3e, 0x3f}, {0x3f, 0x3f}, {0x40, 0x0c}, + {0x41, 0x0b}, {0x42, 0x0a}, {0x43, 0x09}, {0x44, 0x08}, {0x45, 0x07}, + {0x46, 0x06}, {0x47, 0x05}, {0x48, 0x04}, {0x49, 0x03}, {0x4a, 0x02}, + {0x4b, 0x01}, {0x4c, 0x40}, {0x4d, 0x41}, {0x4e, 0x42}, {0x4f, 0x43}, + {0x50, 0x44}, {0x51, 0x45}, {0x52, 0x46}, {0x53, 0x47}, {0x54, 0x48}, + {0x55, 0x49}, {0x56, 0x4a}, {0x57, 0x4b}, {0x58, 0x4c}, {0x59, 0x4d}, + {0x5a, 0x4e}, {0x5b, 0x4f}, {0x5c, 0x50}, {0x5d, 0x51}, {0x5e, 0x52}, + {0x5f, 0x53}, {0x60, 0x54}, {0x61, 0x55}, {0x62, 0x56}, {0x63, 0x57}, + {0x64, 0x58}, {0x65, 0x59}, {0x66, 0x5a}, {0x67, 0x5b}, {0x68, 0x5c}, + {0x69, 0x5d}, {0x6a, 0x5e}, {0x6b, 0x5f}, {0x6c, 0x60}, {0x6d, 0x61}, + {0x6e, 0x62}, {0x6f, 0x63}, {0x70, 0x64}, {0x71, 0x65}, {0x72, 0x66}, + {0x73, 0x67}, {0x74, 0x68}, {0x75, 0x69}, {0x76, 0x6a}, {0x77, 0x6b}, + {0x78, 0x6c}, {0x79, 0x6d}, {0x7a, 0x6e}, {0x7b, 0x6f}, {0x7c, 0x70}, + {0x7d, 0x71}, {0x7e, 0x72}, {0x7f, 0x73}, +}; + +static int s6e3fa0_dcs_read(struct s6e3fa0 *ctx, unsigned char cmd, + void *data, size_t len) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + + return mipi_dsi_dcs_read(dsi, dsi->channel, cmd, data, len); +} + +static void s6e3fa0_dcs_write(struct s6e3fa0 *ctx, const void *data, size_t len) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + + mipi_dsi_dcs_write(dsi, dsi->channel, data, len); +} + +#define s6e3fa0_dcs_write_seq(ctx, seq...) \ +do { \ + const unsigned char d[] = { seq }; \ + BUILD_BUG_ON_MSG(ARRAY_SIZE(d) > 64, "too big seq for stack"); \ + s6e3fa0_dcs_write(ctx, d, ARRAY_SIZE(d)); \ +} while (0) + +#define s6e3fa0_dcs_write_seq_static(ctx, seq...) \ +do { \ + static const unsigned char d[] = { seq }; \ + s6e3fa0_dcs_write(ctx, d, ARRAY_SIZE(d)); \ +} while (0) + +static void s6e3fa0_apply_level_1_key(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xf0, 0x5a, 0x5a); +} + +static void s6e3fa0_apply_level_2_key(struct s6e3fa0 *ctx, bool on) +{ + if (on) + s6e3fa0_dcs_write_seq_static(ctx, 0xfc, 0x5a, 0x5a); + else + s6e3fa0_dcs_write_seq_static(ctx, 0xfc, 0xa5, 0xa5); +} + +static void s6e3fa0_set_maximum_return_packet_size(struct s6e3fa0 *ctx, + unsigned int size) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + const struct mipi_dsi_host_ops *ops = dsi->host->ops; + + if (ops && ops->transfer) { + unsigned char buf[] = {size, 0}; + struct mipi_dsi_msg msg = { + .channel = dsi->channel, + .type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, + .tx_len = sizeof(buf), + .tx_buf = buf + }; + + ops->transfer(dsi->host, &msg); + } +} + +static void s6e3fa0_read_mtp_id(struct s6e3fa0 *ctx) +{ + unsigned char id[MTP_ID_LEN]; + int ret; + + s6e3fa0_set_maximum_return_packet_size(ctx, MTP_ID_LEN); + ret = s6e3fa0_dcs_read(ctx, 0x04, id, MTP_ID_LEN); + if (ret < MTP_ID_LEN || id[0] == 0x00) { + dev_err(ctx->dev, "failed to read id\n"); + return; + } + + dev_info(ctx->dev, "ID: 0x%02x, 0x%02x, 0x%02x\n", id[0], id[1], id[2]); + + ctx->id = id[2]; +} + +static void s6e3fa0_read_vddm(struct s6e3fa0 *ctx) +{ + unsigned char vddm; + int ret; + + s6e3fa0_apply_level_2_key(ctx, true); + s6e3fa0_dcs_write_seq_static(ctx, 0xb0, 0x16); + s6e3fa0_set_maximum_return_packet_size(ctx, 1); + ret = s6e3fa0_dcs_read(ctx, 0xd7, &vddm, 1); + s6e3fa0_apply_level_2_key(ctx, false); + + if (ret < 1 || vddm > 0x7f) { + dev_err(ctx->dev, "failed to read vddm\n"); + return; + } + + ctx->vddm = s6e3fa0_vddm_lut[vddm][1]; +} + +static void s6e3fa0_set_pentile_control(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xc0, 0x00, 0x02, 0x03, 0x32, 0x03, + 0x44, 0x44, 0xc0, 0x00, 0x1c, 0x20, 0xe8); +} + +static void s6e3fa0_write_als(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xe3, 0xff, 0xff, 0xff, 0xff); +} + +static void s6e3fa0_set_readability_enhancement(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xfe, 0x00, 0x03, 0xb0, 0x2b, 0xfe, + 0xe4); +} + +static void s6e3fa0_set_common_control(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_pentile_control(ctx); + s6e3fa0_write_als(ctx); + s6e3fa0_set_readability_enhancement(ctx); +} + +static void s6e3fa0_set_gamma(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xca, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x00); +} + +static void s6e3fa0_set_aid(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xb2, 0x00, 0x06, 0x00, 0x06); +} + +static void s6e3fa0_set_elvss(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xb6, 0x88, 0x0a); +} + +static void s6e3fa0_update_panel(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xf7, 0x03); +} + +static void s6e3fa0_write_acl(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0x55, 0x02); +} + +static void s6e3fa0_set_brightness_control(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_gamma(ctx); + s6e3fa0_set_aid(ctx); + s6e3fa0_set_elvss(ctx); + s6e3fa0_update_panel(ctx); + s6e3fa0_write_acl(ctx); +} + +static void s6e3fa0_set_temperature(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0xb0, 0x05, 0xb8, 0x19); +} + +static void s6e3fa0_set_elvss_control(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_temperature(ctx); + s6e3fa0_set_elvss(ctx); +} + +static void s6e3fa0_set_te_on(struct s6e3fa0 *ctx) +{ + s6e3fa0_dcs_write_seq_static(ctx, 0x35, 0x00); +} + +static void s6e3fa0_set_etc_and_write_vddm(struct s6e3fa0 *ctx) +{ + s6e3fa0_apply_level_2_key(ctx, true); + s6e3fa0_dcs_write_seq(ctx, 0xed, 0x0c, 0x04, 0xff, 0x01, 0xb0, 0x16, + 0xd7, ctx->vddm); + s6e3fa0_apply_level_2_key(ctx, false); +} + +static void s6e3fa0_set_etc_condition(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_te_on(ctx); + s6e3fa0_set_etc_and_write_vddm(ctx); +} + +static void s6e3fa0_panel_init(struct s6e3fa0 *ctx) +{ + s6e3fa0_set_common_control(ctx); + s6e3fa0_set_brightness_control(ctx); + s6e3fa0_set_elvss_control(ctx); + s6e3fa0_set_etc_condition(ctx); + + msleep(ctx->init_delay); +} + +static int s6e3fa0_power_off(struct s6e3fa0 *ctx) +{ + gpiod_set_value(ctx->reset_gpio, 0); + + return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); +} + +static int s6e3fa0_power_on(struct s6e3fa0 *ctx) +{ + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + if (ret) + return ret; + + msleep(ctx->power_on_delay); + + gpiod_set_value(ctx->reset_gpio, 1); + gpiod_set_value(ctx->reset_gpio, 0); + usleep_range(1000, 2000); + gpiod_set_value(ctx->reset_gpio, 1); + + msleep(ctx->reset_delay); + + return 0; +} + +static void s6e3fa0_set_sequence(struct s6e3fa0 *ctx) +{ + s6e3fa0_apply_level_1_key(ctx); + s6e3fa0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE); + msleep(20); + + s6e3fa0_read_mtp_id(ctx); + s6e3fa0_read_vddm(ctx); + + s6e3fa0_panel_init(ctx); + + s6e3fa0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON); +} + +static int s6e3fa0_disable(struct drm_panel *panel) +{ + struct s6e3fa0 *ctx = panel_to_s6e3fa0(panel); + + s6e3fa0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE); + s6e3fa0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF); + msleep(35); + + return s6e3fa0_power_off(ctx); +} + +static int s6e3fa0_enable(struct drm_panel *panel) +{ + struct s6e3fa0 *ctx = panel_to_s6e3fa0(panel); + int ret; + + ret = s6e3fa0_power_on(ctx); + if (ret) + return ret; + + s6e3fa0_set_sequence(ctx); + + return ret; +} + +static int s6e3fa0_get_modes(struct drm_panel *panel) +{ + struct drm_connector *connector = panel->connector; + struct s6e3fa0 *ctx = panel_to_s6e3fa0(panel); + struct drm_display_mode *mode; + + mode = drm_mode_create(connector->dev); + if (!mode) { + DRM_ERROR("failed to create a new display mode\n"); + return 0; + } + + drm_display_mode_from_videomode(&ctx->vm, mode); + mode->width_mm = ctx->width_mm; + mode->height_mm = ctx->height_mm; + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_panel_funcs s6e3fa0_drm_funcs = { + .disable = s6e3fa0_disable, + .enable = s6e3fa0_enable, + .get_modes = s6e3fa0_get_modes, +}; + +static int s6e3fa0_parse_dt(struct s6e3fa0 *ctx) +{ + struct device *dev = ctx->dev; + struct device_node *np = dev->of_node; + int ret; + + ret = of_get_videomode(np, &ctx->vm, 0); + if (ret) { + dev_err(dev, "failed to get video mode: %d\n", ret); + return ret; + } + + return 0; +} + +irqreturn_t s6e3fa0_det_interrupt(int irq, void *dev_id) +{ + /* TODO */ + return IRQ_HANDLED; +} + +irqreturn_t s6e3fa0_te_interrupt(int irq, void *dev_id) +{ + struct s6e3fa0 *ctx = dev_id; + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + struct mipi_dsi_host *host = dsi->host; + const struct mipi_dsi_host_ops *ops = host->ops; + + if (ops && ops->te_handler) + ops->te_handler(host); + + return IRQ_HANDLED; +} + +static int s6e3fa0_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct s6e3fa0 *ctx; + int ret; + + ctx = devm_kzalloc(dev, sizeof(struct s6e3fa0), GFP_KERNEL); + if (!ctx) { + dev_err(dev, "failed to allocate s6e3fa0 structure.\n"); + return -ENOMEM; + } + + mipi_dsi_set_drvdata(dsi, ctx); + + ctx->dev = dev; + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + + ret = s6e3fa0_parse_dt(ctx); + if (ret) + return ret; + + ctx->supplies[0].supply = "vdd3"; + ctx->supplies[1].supply = "vci"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ctx->supplies), + ctx->supplies); + if (ret) { + dev_err(dev, "failed to get regulators: %d\n", ret); + return ret; + } + + ctx->reset_gpio = devm_gpiod_get(dev, "reset"); + if (IS_ERR(ctx->reset_gpio)) { + dev_err(dev, "failed to get reset gpio: %ld\n", + PTR_ERR(ctx->reset_gpio)); + return PTR_ERR(ctx->reset_gpio); + } + ret = gpiod_direction_output(ctx->reset_gpio, 1); + if (ret < 0) { + dev_err(dev, "failed to configure reset gpio: %d\n", ret); + return ret; + } + + ctx->det_gpio = devm_gpiod_get(dev, "det"); + if (IS_ERR(ctx->det_gpio)) { + dev_err(dev, "failed to get det gpio: %ld\n", + PTR_ERR(ctx->det_gpio)); + return PTR_ERR(ctx->det_gpio); + } + ret = gpiod_direction_input(ctx->det_gpio); + if (ret < 0) { + dev_err(dev, "failed to configure det gpio: %d\n", ret); + return ret; + } + ret = devm_request_irq(dev, gpiod_to_irq(ctx->det_gpio), + s6e3fa0_det_interrupt, + IRQF_TRIGGER_FALLING, + "oled-det", ctx); + if (ret) { + dev_err(dev, "failed to request det irq: %d\n", ret); + return ret; + } + + ctx->te_gpio = devm_gpiod_get(dev, "te"); + if (IS_ERR(ctx->te_gpio)) { + dev_err(dev, "failed to get te gpio: %ld\n", + PTR_ERR(ctx->te_gpio)); + return PTR_ERR(ctx->te_gpio); + } + ret = gpiod_direction_input(ctx->te_gpio); + if (ret < 0) { + dev_err(dev, "failed to configure te gpio: %d\n", ret); + return ret; + } + ret = devm_request_irq(dev, gpiod_to_irq(ctx->te_gpio), + s6e3fa0_te_interrupt, + IRQF_TRIGGER_RISING, + "TE", ctx); + if (ret) { + dev_err(dev, "failed to request det irq: %d\n", ret); + return ret; + } + + ctx->power_on_delay = 10; + ctx->reset_delay = 5; + ctx->init_delay = 120; + ctx->width_mm = 71; + ctx->height_mm = 126; + + ctx->brightness = GAMMA_LEVEL_NUM - 1; + + drm_panel_init(&ctx->panel); + ctx->panel.dev = dev; + ctx->panel.funcs = &s6e3fa0_drm_funcs; + + ret = drm_panel_add(&ctx->panel); + if (ret) + return ret; + + ret = mipi_dsi_attach(dsi); + if (ret) + drm_panel_remove(&ctx->panel); + + return ret; +} + +static int s6e3fa0_remove(struct mipi_dsi_device *dsi) +{ + struct s6e3fa0 *ctx = mipi_dsi_get_drvdata(dsi); + + s6e3fa0_power_off(ctx); + + mipi_dsi_detach(dsi); + drm_panel_remove(&ctx->panel); + + return 0; +} + +static struct of_device_id s6e3fa0_of_match[] = { + { .compatible = "samsung,s6e3fa0" }, + { } +}; +MODULE_DEVICE_TABLE(of, s6e3fa0_of_match); + +static struct mipi_dsi_driver s6e3fa0_driver = { + .probe = s6e3fa0_probe, + .remove = s6e3fa0_remove, + .driver = { + .name = "panel_s6e3fa0", + .owner = THIS_MODULE, + .of_match_table = s6e3fa0_of_match, + }, +}; +module_mipi_dsi_driver(s6e3fa0_driver); + +MODULE_AUTHOR("YoungJun Cho yj44.cho@samsung.com"); +MODULE_DESCRIPTION("MIPI-DSI based s6e3fa0 AMOLED LCD Panel Driver"); +MODULE_LICENSE("GPL v2");
This patch adds sysreg property to fimd device node which is required to use I80 interface.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- arch/arm/boot/dts/exynos4.dtsi | 1 + 1 file changed, 1 insertion(+)
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi index 3d14cdb..a10aa50 100644 --- a/arch/arm/boot/dts/exynos4.dtsi +++ b/arch/arm/boot/dts/exynos4.dtsi @@ -528,6 +528,7 @@ clocks = <&clock 140>, <&clock 283>; clock-names = "sclk_fimd", "fimd"; samsung,power-domain = <&pd_lcd0>; + samsung,sysreg = <&sys_reg>; status = "disabled"; }; };
This patch adds sysreg device node, and sysreg property to fimd device node which is required to use I80 interface.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- arch/arm/boot/dts/exynos5.dtsi | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/exynos5.dtsi b/arch/arm/boot/dts/exynos5.dtsi index 258dca4..f938bbb 100644 --- a/arch/arm/boot/dts/exynos5.dtsi +++ b/arch/arm/boot/dts/exynos5.dtsi @@ -88,12 +88,18 @@ status = "disabled"; };
+ sys_reg: syscon@10050000 { + compatible = "samsung,exynos5-sysreg", "syscon"; + reg = <0x10050000 0x500>; + }; + fimd@14400000 { compatible = "samsung,exynos5250-fimd"; interrupt-parent = <&combiner>; reg = <0x14400000 0x40000>; interrupt-names = "fifo", "vsync", "lcd_sys"; interrupts = <18 4>, <18 5>, <18 6>; + samsung,sysreg = <&sys_reg>; status = "disabled"; };
On 15 April 2014 11:17, YoungJun Cho yj44.cho@samsung.com wrote:
This patch adds sysreg device node, and sysreg property to fimd device node which is required to use I80 interface.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
arch/arm/boot/dts/exynos5.dtsi | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/exynos5.dtsi b/arch/arm/boot/dts/exynos5.dtsi index 258dca4..f938bbb 100644 --- a/arch/arm/boot/dts/exynos5.dtsi +++ b/arch/arm/boot/dts/exynos5.dtsi @@ -88,12 +88,18 @@ status = "disabled"; };
sys_reg: syscon@10050000 {
compatible = "samsung,exynos5-sysreg", "syscon";
Do we really need a separate string for this? Can't we use "samsung,exynos4-sysreg" itself?
On 15/04/14 10:41, Sachin Kamat wrote:
On 15 April 2014 11:17, YoungJun Cho yj44.cho@samsung.com wrote:
This patch adds sysreg device node, and sysreg property to fimd device node which is required to use I80 interface.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
arch/arm/boot/dts/exynos5.dtsi | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/exynos5.dtsi b/arch/arm/boot/dts/exynos5.dtsi index 258dca4..f938bbb 100644 --- a/arch/arm/boot/dts/exynos5.dtsi +++ b/arch/arm/boot/dts/exynos5.dtsi @@ -88,12 +88,18 @@ status = "disabled"; };
sys_reg: syscon@10050000 {
compatible = "samsung,exynos5-sysreg", "syscon";
Do we really need a separate string for this? Can't we use "samsung,exynos4-sysreg" itself?
Currently only "syscon" is meaningful in Linux, and we add second SoC specific compatible to be able to distinguish between various SoC revisions, should any SoC specific quirks be handled in future. Thus there is no much point in adding "samsung,exynos4-sysreg" to exynos5.dtsi. We could as well only leave "syscon" entry alone. My suggestion is to keep "samsung,exynos5-sysreg", so for instance Exynos4 and Exynos5 SOC series SYSREG blocks can be identified in an OS.
-- Regards, Sylwester
On 15 April 2014 14:48, Sylwester Nawrocki s.nawrocki@samsung.com wrote:
On 15/04/14 10:41, Sachin Kamat wrote:
On 15 April 2014 11:17, YoungJun Cho yj44.cho@samsung.com wrote:
This patch adds sysreg device node, and sysreg property to fimd device node which is required to use I80 interface.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
arch/arm/boot/dts/exynos5.dtsi | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/exynos5.dtsi b/arch/arm/boot/dts/exynos5.dtsi index 258dca4..f938bbb 100644 --- a/arch/arm/boot/dts/exynos5.dtsi +++ b/arch/arm/boot/dts/exynos5.dtsi @@ -88,12 +88,18 @@ status = "disabled"; };
sys_reg: syscon@10050000 {
compatible = "samsung,exynos5-sysreg", "syscon";
Do we really need a separate string for this? Can't we use "samsung,exynos4-sysreg" itself?
Currently only "syscon" is meaningful in Linux, and we add second SoC specific compatible to be able to distinguish between various SoC revisions, should any SoC specific quirks be handled in future. Thus there is no much point in adding "samsung,exynos4-sysreg" to exynos5.dtsi. We could as well only leave "syscon" entry alone. My suggestion is to keep "samsung,exynos5-sysreg", so for instance Exynos4 and Exynos5 SOC series SYSREG blocks can be identified in an OS.
Yes, this sounds reasonable.
This patch adds mipi-phy node for MIPI-DSI device.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- arch/arm/boot/dts/exynos5420.dtsi | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi index 8db792b..f0184c7 100644 --- a/arch/arm/boot/dts/exynos5420.dtsi +++ b/arch/arm/boot/dts/exynos5420.dtsi @@ -416,6 +416,12 @@ phy-names = "dp"; };
+ mipi_phy: video-phy@10040714 { + compatible = "samsung,s5pv210-mipi-video-phy"; + reg = <0x10040714 12>; + #phy-cells = <1>; + }; + fimd@14400000 { samsung,power-domain = <&disp_pd>; clocks = <&clock 147>, <&clock 421>;
This patch adds common part of dsi node.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- arch/arm/boot/dts/exynos5420.dtsi | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi index f0184c7..aa2350a 100644 --- a/arch/arm/boot/dts/exynos5420.dtsi +++ b/arch/arm/boot/dts/exynos5420.dtsi @@ -422,6 +422,20 @@ #phy-cells = <1>; };
+ dsi@14500000 { + compatible = "samsung,exynos5420-mipi-dsi"; + reg = <0x14500000 0x10000>; + interrupts = <0 82 0>; + samsung,power-domain = <&disp_pd>; + phys = <&mipi_phy 1>; + phy-names = "dsim"; + clocks = <&clock 411>, <&clock 146>; + clock-names = "bus_clk", "pll_clk"; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + fimd@14400000 { samsung,power-domain = <&disp_pd>; clocks = <&clock 147>, <&clock 421>;
Hi YoungJun,
On 15 April 2014 11:17, YoungJun Cho yj44.cho@samsung.com wrote:
This patch adds common part of dsi node.
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com
arch/arm/boot/dts/exynos5420.dtsi | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi index f0184c7..aa2350a 100644 --- a/arch/arm/boot/dts/exynos5420.dtsi +++ b/arch/arm/boot/dts/exynos5420.dtsi @@ -422,6 +422,20 @@ #phy-cells = <1>; };
dsi@14500000 {
compatible = "samsung,exynos5420-mipi-dsi";
reg = <0x14500000 0x10000>;
interrupts = <0 82 0>;
samsung,power-domain = <&disp_pd>;
phys = <&mipi_phy 1>;
phy-names = "dsim";
clocks = <&clock 411>, <&clock 146>;
Please use macros instead of numbers.
clock-names = "bus_clk", "pll_clk";
status = "disabled";
nit: Move this to the end for readability.
This patch adds common part of dsi node.
Changelog v2: - Uses clock macros instead of numbers (commented by Sachin Kamat)
Signed-off-by: YoungJun Cho yj44.cho@samsung.com Signed-off-by: Inki Dae inki.dae@samsung.com Signed-off-by: Kyungmin Park kyungmin.park@samsung.com --- arch/arm/boot/dts/exynos5420.dtsi | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi index f0184c7..f1030f5 100644 --- a/arch/arm/boot/dts/exynos5420.dtsi +++ b/arch/arm/boot/dts/exynos5420.dtsi @@ -17,6 +17,7 @@ #include "exynos5420-pinctrl.dtsi"
#include <dt-bindings/clk/exynos-audss-clk.h> +#include <dt-bindings/clock/exynos5420.h>
/ { compatible = "samsung,exynos5420"; @@ -422,6 +423,20 @@ #phy-cells = <1>; };
+ dsi@14500000 { + compatible = "samsung,exynos5420-mipi-dsi"; + reg = <0x14500000 0x10000>; + interrupts = <0 82 0>; + samsung,power-domain = <&disp_pd>; + phys = <&mipi_phy 1>; + phy-names = "dsim"; + clocks = <&clock CLK_DSIM1>, <&clock CLK_SCLK_MIPI1>; + clock-names = "bus_clk", "pll_clk"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + fimd@14400000 { samsung,power-domain = <&disp_pd>; clocks = <&clock 147>, <&clock 421>;
dri-devel@lists.freedesktop.org