All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-07-22 13:11 ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree, linux-kernel, linux-arm-kernel, Bo Shen,
	Boris BREZILLON

Hello,

This patch series adds support for Atmel HLCDC (HLCD Controller) available
on some Atmel SoCs (i.e. the sama5d3 family).

The HLCDC actually provides a Display Controller and a PWM device, hence I
decided to declare an MFD device exposing 2 subdevices: a display
controller and a PWM chip.
This also solves a circular dependency issue preventing HLCDC driver from
unloading.
The HLCDC request a drm_panel device, which request a backlight device
(a PWM backlight), which depends on a PWM which is provided by the HLCDC
driver (hlcdc -> panel -> backlight -> hlcdc (pwm part)).

The current implementation only supports sama5d3 SoCs but other SoCs should
be easily ported by defining new compatible strings and adding HLCDC
description structures for these SoCs.

The drivers supports basic CRTC functionalities, several overlays and an
hardware cursor.

At the moment, it only supports connection to LCD panels through an RGB
connector (defined as an LVDS connector in my implementation), though
connection to other kind of devices (like DRM bridges) could be added later.

It also supports several RGB formats on all planes and some YUV formats on
the HEO overlay plane.

This series depends on 2 other series currently under review: [1] and [2].

Best Regards,

Boris

[1]http://lkml.iu.edu/hypermail/linux/kernel/1407.1/04171.html
[2]http://www.spinics.net/lists/kernel/msg1791681.html

Changes since v3:
- rework the layer code to simplify several parts (locking and layer
  disabling)
- make use of the drm_flip_work infrastructure
- rely on default HW cursor implementation using on the cursor plane
- rework the display controller DT bindings (based on OF graph
  representation)
- add rotation support
- retrive RGB bus format from drm_display_info
- drop the dynamic pinctrl state selection
- rework HLCDC output handling (previously specialized to interface
  with LCD panels)
- drop ".module = THIS_MODULE" lines
- change display controller compatible string

Changes since v2:
- fix coding style issues (macro indentation)
- make use of GENMASK in several places
- declare regmap config as a static structure
- rework hlcdc plane update API
- rework cursor handling to make use of the new plane update API
- fix backporch config
- do not use devm_regmap_init_mmio_clk to avoid extra clk_enable
  clk disable calls when accessing registers
- explicitely include regmap and clk headers instead of relying on
  atmel-hlcdc.h inclusions
- make the atmel-hlcdc driver depends on CONFIG_OF
- separate DT bindings documentation from driver implementation
- support several pin muxing for HLCDC pins on sama5d3 SoCs

Changes since v1:
- replace the backlight driver by a PWM driver
- make use of drm_panel infrastructure
- split driver code in several subsystem: MFD, PWM and DRM
- add support for overlays
- add support for hardware cursor


Boris BREZILLON (11):
  mfd: add atmel-hlcdc driver
  mfd: add documentation for atmel-hlcdc DT bindings
  pwm: add support for atmel-hlcdc-pwm device
  pwm: add DT bindings documentation for atmel-hlcdc-pwm driver
  drm: add Atmel HLCDC Display Controller support
  drm: add DT bindings documentation for atmel-hlcdc-dc driver
  ARM: AT91/dt: split sama5d3 lcd pin definitions to match RGB mode
    configs
  ARM: AT91/dt: add alternative pin muxing for sama5d3 lcd pins
  ARM: at91/dt: define the HLCDC node available on sama5d3 SoCs
  ARM: at91/dt: add LCD panel description to sama5d3xdm.dtsi
  ARM: at91/dt: enable the LCD panel on sama5d3xek boards

 .../devicetree/bindings/drm/atmel-hlcdc-dc.txt     |  54 ++
 .../devicetree/bindings/mfd/atmel-hlcdc.txt        |  50 ++
 .../devicetree/bindings/pwm/atmel-hlcdc-pwm.txt    |  55 ++
 arch/arm/boot/dts/sama5d31ek.dts                   |  20 +
 arch/arm/boot/dts/sama5d33ek.dts                   |  20 +
 arch/arm/boot/dts/sama5d34ek.dts                   |  20 +
 arch/arm/boot/dts/sama5d36ek.dts                   |  20 +
 arch/arm/boot/dts/sama5d3_lcd.dtsi                 | 205 +++++-
 arch/arm/boot/dts/sama5d3xdm.dtsi                  |  58 ++
 drivers/gpu/drm/Kconfig                            |   2 +
 drivers/gpu/drm/Makefile                           |   1 +
 drivers/gpu/drm/atmel-hlcdc/Kconfig                |  11 +
 drivers/gpu/drm/atmel-hlcdc/Makefile               |   7 +
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c     | 286 ++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c       | 488 +++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h       | 224 ++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c    | 635 ++++++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h    | 396 ++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c   | 478 ++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c    | 804 +++++++++++++++++++++
 drivers/mfd/Kconfig                                |  12 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/atmel-hlcdc.c                          | 118 +++
 drivers/pwm/Kconfig                                |   9 +
 drivers/pwm/Makefile                               |   1 +
 drivers/pwm/pwm-atmel-hlcdc.c                      | 229 ++++++
 include/linux/mfd/atmel-hlcdc.h                    |  78 ++
 27 files changed, 4251 insertions(+), 31 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt
 create mode 100644 Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
 create mode 100644 Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/Kconfig
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/Makefile
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
 create mode 100644 drivers/mfd/atmel-hlcdc.c
 create mode 100644 drivers/pwm/pwm-atmel-hlcdc.c
 create mode 100644 include/linux/mfd/atmel-hlcdc.h

-- 
1.8.3.2


^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-07-22 13:11 ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, linux-kernel,
	Rob Herring, Bo Shen, Kumar Gala, linux-arm-kernel

Hello,

This patch series adds support for Atmel HLCDC (HLCD Controller) available
on some Atmel SoCs (i.e. the sama5d3 family).

The HLCDC actually provides a Display Controller and a PWM device, hence I
decided to declare an MFD device exposing 2 subdevices: a display
controller and a PWM chip.
This also solves a circular dependency issue preventing HLCDC driver from
unloading.
The HLCDC request a drm_panel device, which request a backlight device
(a PWM backlight), which depends on a PWM which is provided by the HLCDC
driver (hlcdc -> panel -> backlight -> hlcdc (pwm part)).

The current implementation only supports sama5d3 SoCs but other SoCs should
be easily ported by defining new compatible strings and adding HLCDC
description structures for these SoCs.

The drivers supports basic CRTC functionalities, several overlays and an
hardware cursor.

At the moment, it only supports connection to LCD panels through an RGB
connector (defined as an LVDS connector in my implementation), though
connection to other kind of devices (like DRM bridges) could be added later.

It also supports several RGB formats on all planes and some YUV formats on
the HEO overlay plane.

This series depends on 2 other series currently under review: [1] and [2].

Best Regards,

Boris

[1]http://lkml.iu.edu/hypermail/linux/kernel/1407.1/04171.html
[2]http://www.spinics.net/lists/kernel/msg1791681.html

Changes since v3:
- rework the layer code to simplify several parts (locking and layer
  disabling)
- make use of the drm_flip_work infrastructure
- rely on default HW cursor implementation using on the cursor plane
- rework the display controller DT bindings (based on OF graph
  representation)
- add rotation support
- retrive RGB bus format from drm_display_info
- drop the dynamic pinctrl state selection
- rework HLCDC output handling (previously specialized to interface
  with LCD panels)
- drop ".module = THIS_MODULE" lines
- change display controller compatible string

Changes since v2:
- fix coding style issues (macro indentation)
- make use of GENMASK in several places
- declare regmap config as a static structure
- rework hlcdc plane update API
- rework cursor handling to make use of the new plane update API
- fix backporch config
- do not use devm_regmap_init_mmio_clk to avoid extra clk_enable
  clk disable calls when accessing registers
- explicitely include regmap and clk headers instead of relying on
  atmel-hlcdc.h inclusions
- make the atmel-hlcdc driver depends on CONFIG_OF
- separate DT bindings documentation from driver implementation
- support several pin muxing for HLCDC pins on sama5d3 SoCs

Changes since v1:
- replace the backlight driver by a PWM driver
- make use of drm_panel infrastructure
- split driver code in several subsystem: MFD, PWM and DRM
- add support for overlays
- add support for hardware cursor


Boris BREZILLON (11):
  mfd: add atmel-hlcdc driver
  mfd: add documentation for atmel-hlcdc DT bindings
  pwm: add support for atmel-hlcdc-pwm device
  pwm: add DT bindings documentation for atmel-hlcdc-pwm driver
  drm: add Atmel HLCDC Display Controller support
  drm: add DT bindings documentation for atmel-hlcdc-dc driver
  ARM: AT91/dt: split sama5d3 lcd pin definitions to match RGB mode
    configs
  ARM: AT91/dt: add alternative pin muxing for sama5d3 lcd pins
  ARM: at91/dt: define the HLCDC node available on sama5d3 SoCs
  ARM: at91/dt: add LCD panel description to sama5d3xdm.dtsi
  ARM: at91/dt: enable the LCD panel on sama5d3xek boards

 .../devicetree/bindings/drm/atmel-hlcdc-dc.txt     |  54 ++
 .../devicetree/bindings/mfd/atmel-hlcdc.txt        |  50 ++
 .../devicetree/bindings/pwm/atmel-hlcdc-pwm.txt    |  55 ++
 arch/arm/boot/dts/sama5d31ek.dts                   |  20 +
 arch/arm/boot/dts/sama5d33ek.dts                   |  20 +
 arch/arm/boot/dts/sama5d34ek.dts                   |  20 +
 arch/arm/boot/dts/sama5d36ek.dts                   |  20 +
 arch/arm/boot/dts/sama5d3_lcd.dtsi                 | 205 +++++-
 arch/arm/boot/dts/sama5d3xdm.dtsi                  |  58 ++
 drivers/gpu/drm/Kconfig                            |   2 +
 drivers/gpu/drm/Makefile                           |   1 +
 drivers/gpu/drm/atmel-hlcdc/Kconfig                |  11 +
 drivers/gpu/drm/atmel-hlcdc/Makefile               |   7 +
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c     | 286 ++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c       | 488 +++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h       | 224 ++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c    | 635 ++++++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h    | 396 ++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c   | 478 ++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c    | 804 +++++++++++++++++++++
 drivers/mfd/Kconfig                                |  12 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/atmel-hlcdc.c                          | 118 +++
 drivers/pwm/Kconfig                                |   9 +
 drivers/pwm/Makefile                               |   1 +
 drivers/pwm/pwm-atmel-hlcdc.c                      | 229 ++++++
 include/linux/mfd/atmel-hlcdc.h                    |  78 ++
 27 files changed, 4251 insertions(+), 31 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt
 create mode 100644 Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
 create mode 100644 Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/Kconfig
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/Makefile
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
 create mode 100644 drivers/mfd/atmel-hlcdc.c
 create mode 100644 drivers/pwm/pwm-atmel-hlcdc.c
 create mode 100644 include/linux/mfd/atmel-hlcdc.h

-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-07-22 13:11 ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

This patch series adds support for Atmel HLCDC (HLCD Controller) available
on some Atmel SoCs (i.e. the sama5d3 family).

The HLCDC actually provides a Display Controller and a PWM device, hence I
decided to declare an MFD device exposing 2 subdevices: a display
controller and a PWM chip.
This also solves a circular dependency issue preventing HLCDC driver from
unloading.
The HLCDC request a drm_panel device, which request a backlight device
(a PWM backlight), which depends on a PWM which is provided by the HLCDC
driver (hlcdc -> panel -> backlight -> hlcdc (pwm part)).

The current implementation only supports sama5d3 SoCs but other SoCs should
be easily ported by defining new compatible strings and adding HLCDC
description structures for these SoCs.

The drivers supports basic CRTC functionalities, several overlays and an
hardware cursor.

At the moment, it only supports connection to LCD panels through an RGB
connector (defined as an LVDS connector in my implementation), though
connection to other kind of devices (like DRM bridges) could be added later.

It also supports several RGB formats on all planes and some YUV formats on
the HEO overlay plane.

This series depends on 2 other series currently under review: [1] and [2].

Best Regards,

Boris

[1]http://lkml.iu.edu/hypermail/linux/kernel/1407.1/04171.html
[2]http://www.spinics.net/lists/kernel/msg1791681.html

Changes since v3:
- rework the layer code to simplify several parts (locking and layer
  disabling)
- make use of the drm_flip_work infrastructure
- rely on default HW cursor implementation using on the cursor plane
- rework the display controller DT bindings (based on OF graph
  representation)
- add rotation support
- retrive RGB bus format from drm_display_info
- drop the dynamic pinctrl state selection
- rework HLCDC output handling (previously specialized to interface
  with LCD panels)
- drop ".module = THIS_MODULE" lines
- change display controller compatible string

Changes since v2:
- fix coding style issues (macro indentation)
- make use of GENMASK in several places
- declare regmap config as a static structure
- rework hlcdc plane update API
- rework cursor handling to make use of the new plane update API
- fix backporch config
- do not use devm_regmap_init_mmio_clk to avoid extra clk_enable
  clk disable calls when accessing registers
- explicitely include regmap and clk headers instead of relying on
  atmel-hlcdc.h inclusions
- make the atmel-hlcdc driver depends on CONFIG_OF
- separate DT bindings documentation from driver implementation
- support several pin muxing for HLCDC pins on sama5d3 SoCs

Changes since v1:
- replace the backlight driver by a PWM driver
- make use of drm_panel infrastructure
- split driver code in several subsystem: MFD, PWM and DRM
- add support for overlays
- add support for hardware cursor


Boris BREZILLON (11):
  mfd: add atmel-hlcdc driver
  mfd: add documentation for atmel-hlcdc DT bindings
  pwm: add support for atmel-hlcdc-pwm device
  pwm: add DT bindings documentation for atmel-hlcdc-pwm driver
  drm: add Atmel HLCDC Display Controller support
  drm: add DT bindings documentation for atmel-hlcdc-dc driver
  ARM: AT91/dt: split sama5d3 lcd pin definitions to match RGB mode
    configs
  ARM: AT91/dt: add alternative pin muxing for sama5d3 lcd pins
  ARM: at91/dt: define the HLCDC node available on sama5d3 SoCs
  ARM: at91/dt: add LCD panel description to sama5d3xdm.dtsi
  ARM: at91/dt: enable the LCD panel on sama5d3xek boards

 .../devicetree/bindings/drm/atmel-hlcdc-dc.txt     |  54 ++
 .../devicetree/bindings/mfd/atmel-hlcdc.txt        |  50 ++
 .../devicetree/bindings/pwm/atmel-hlcdc-pwm.txt    |  55 ++
 arch/arm/boot/dts/sama5d31ek.dts                   |  20 +
 arch/arm/boot/dts/sama5d33ek.dts                   |  20 +
 arch/arm/boot/dts/sama5d34ek.dts                   |  20 +
 arch/arm/boot/dts/sama5d36ek.dts                   |  20 +
 arch/arm/boot/dts/sama5d3_lcd.dtsi                 | 205 +++++-
 arch/arm/boot/dts/sama5d3xdm.dtsi                  |  58 ++
 drivers/gpu/drm/Kconfig                            |   2 +
 drivers/gpu/drm/Makefile                           |   1 +
 drivers/gpu/drm/atmel-hlcdc/Kconfig                |  11 +
 drivers/gpu/drm/atmel-hlcdc/Makefile               |   7 +
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c     | 286 ++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c       | 488 +++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h       | 224 ++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c    | 635 ++++++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h    | 396 ++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c   | 478 ++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c    | 804 +++++++++++++++++++++
 drivers/mfd/Kconfig                                |  12 +
 drivers/mfd/Makefile                               |   1 +
 drivers/mfd/atmel-hlcdc.c                          | 118 +++
 drivers/pwm/Kconfig                                |   9 +
 drivers/pwm/Makefile                               |   1 +
 drivers/pwm/pwm-atmel-hlcdc.c                      | 229 ++++++
 include/linux/mfd/atmel-hlcdc.h                    |  78 ++
 27 files changed, 4251 insertions(+), 31 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt
 create mode 100644 Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
 create mode 100644 Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/Kconfig
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/Makefile
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
 create mode 100644 drivers/mfd/atmel-hlcdc.c
 create mode 100644 drivers/pwm/pwm-atmel-hlcdc.c
 create mode 100644 include/linux/mfd/atmel-hlcdc.h

-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 01/11] mfd: add atmel-hlcdc driver
  2014-07-22 13:11 ` Boris BREZILLON
  (?)
@ 2014-07-22 13:11   ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree, linux-kernel, linux-arm-kernel, Bo Shen,
	Boris BREZILLON

The HLCDC IP available on some Atmel SoCs (i.e. at91sam9n12, at91sam9x5
family or sama5d3 family) exposes 2 subdevices:
- a display controller (controlled by a DRM driver)
- a PWM chip

The MFD device provides a regmap and several clocks (those connected
to this hardware block) to its subdevices.

This way concurrent accesses to the iomem range are handled by the regmap
framework, and each subdevice can safely access HLCDC registers.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Acked-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mfd/Kconfig             |  12 ++++
 drivers/mfd/Makefile            |   1 +
 drivers/mfd/atmel-hlcdc.c       | 118 ++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/atmel-hlcdc.h |  78 ++++++++++++++++++++++++++
 4 files changed, 209 insertions(+)
 create mode 100644 drivers/mfd/atmel-hlcdc.c
 create mode 100644 include/linux/mfd/atmel-hlcdc.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 6cc4b6a..c3c007e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -59,6 +59,18 @@ config MFD_AAT2870_CORE
 	  additional drivers must be enabled in order to use the
 	  functionality of the device.
 
+config MFD_ATMEL_HLCDC
+	tristate "Atmel HLCDC (HLCD Controller)"
+	select MFD_CORE
+	select REGMAP_MMIO
+	depends on OF
+	help
+	  Choose this option if you have an ATMEL SoC with an HLCDC (HLCD
+	  Controller) IP (i.e. at91sam9n12, at91sam9x5 family or sama5d3
+	  family).
+	  This MFD device exposes two subdevices: a PWM chip and a Display
+	  Controller.
+
 config MFD_BCM590XX
 	tristate "Broadcom BCM590xx PMUs"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 8afedba..5f25b0d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -156,6 +156,7 @@ obj-$(CONFIG_MFD_PM8921_CORE) 	+= pm8921-core.o ssbi.o
 obj-$(CONFIG_TPS65911_COMPARATOR)	+= tps65911-comparator.o
 obj-$(CONFIG_MFD_TPS65090)	+= tps65090.o
 obj-$(CONFIG_MFD_AAT2870_CORE)	+= aat2870-core.o
+obj-$(CONFIG_MFD_ATMEL_HLCDC)	+= atmel-hlcdc.o
 obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
 obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
 obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c
new file mode 100644
index 0000000..aadb371
--- /dev/null
+++ b/drivers/mfd/atmel-hlcdc.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/atmel-hlcdc.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define ATMEL_HLCDC_REG_MAX		(0x4000 - 0x4)
+
+static const struct mfd_cell atmel_hlcdc_cells[] = {
+	{
+		.name = "atmel-hlcdc-pwm",
+		.of_compatible = "atmel,hlcdc-pwm",
+	},
+	{
+		.name = "atmel-hlcdc-dc",
+		.of_compatible = "atmel,hlcdc-display-controller",
+	},
+};
+
+static const struct regmap_config atmel_hlcdc_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = ATMEL_HLCDC_REG_MAX,
+};
+
+static int atmel_hlcdc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct atmel_hlcdc *hlcdc;
+	struct resource *res;
+	void __iomem *regs;
+
+	hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL);
+	if (!hlcdc)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	hlcdc->periph_clk = devm_clk_get(dev, "periph_clk");
+	if (IS_ERR(hlcdc->periph_clk)) {
+		dev_err(dev, "failed to get peripheral clock\n");
+		return PTR_ERR(hlcdc->periph_clk);
+	}
+
+	hlcdc->sys_clk = devm_clk_get(dev, "sys_clk");
+	if (IS_ERR(hlcdc->sys_clk)) {
+		dev_err(dev, "failed to get system clock\n");
+		return PTR_ERR(hlcdc->sys_clk);
+	}
+
+	hlcdc->slow_clk = devm_clk_get(dev, "slow_clk");
+	if (IS_ERR(hlcdc->slow_clk)) {
+		dev_err(dev, "failed to get slow clock\n");
+		return PTR_ERR(hlcdc->slow_clk);
+	}
+
+	hlcdc->regmap = devm_regmap_init_mmio(dev, regs,
+					      &atmel_hlcdc_regmap_config);
+	if (IS_ERR(hlcdc->regmap))
+		return PTR_ERR(hlcdc->regmap);
+
+	dev_set_drvdata(dev, hlcdc);
+
+	return mfd_add_devices(dev, -1, atmel_hlcdc_cells,
+			       ARRAY_SIZE(atmel_hlcdc_cells),
+			       NULL, 0, NULL);
+}
+
+static int atmel_hlcdc_remove(struct platform_device *pdev)
+{
+	mfd_remove_devices(&pdev->dev);
+
+	return 0;
+}
+
+static const struct of_device_id atmel_hlcdc_match[] = {
+	{ .compatible = "atmel,sama5d3-hlcdc" },
+	{ /* sentinel */ },
+};
+
+static struct platform_driver atmel_hlcdc_driver = {
+	.probe = atmel_hlcdc_probe,
+	.remove = atmel_hlcdc_remove,
+	.driver = {
+		.name = "atmel-hlcdc",
+		.of_match_table = atmel_hlcdc_match,
+	},
+};
+module_platform_driver(atmel_hlcdc_driver);
+
+MODULE_ALIAS("platform:atmel-hlcdc");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Atmel HLCDC driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/atmel-hlcdc.h b/include/linux/mfd/atmel-hlcdc.h
new file mode 100644
index 0000000..e9a503d
--- /dev/null
+++ b/include/linux/mfd/atmel-hlcdc.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_MFD_HLCDC_H
+#define __LINUX_MFD_HLCDC_H
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#define ATMEL_HLCDC_CFG(i)		((i) * 0x4)
+#define ATMEL_HLCDC_SIG_CFG		LCDCFG(5)
+#define ATMEL_HLCDC_HSPOL		BIT(0)
+#define ATMEL_HLCDC_VSPOL		BIT(1)
+#define ATMEL_HLCDC_VSPDLYS		BIT(2)
+#define ATMEL_HLCDC_VSPDLYE		BIT(3)
+#define ATMEL_HLCDC_DISPPOL		BIT(4)
+#define ATMEL_HLCDC_DITHER		BIT(6)
+#define ATMEL_HLCDC_DISPDLY		BIT(7)
+#define ATMEL_HLCDC_MODE_MASK		GENMASK(9, 8)
+#define ATMEL_HLCDC_PP			BIT(10)
+#define ATMEL_HLCDC_VSPSU		BIT(12)
+#define ATMEL_HLCDC_VSPHO		BIT(13)
+#define ATMEL_HLCDC_GUARDTIME_MASK	GENMASK(20, 16)
+
+#define ATMEL_HLCDC_EN			0x20
+#define ATMEL_HLCDC_DIS			0x24
+#define ATMEL_HLCDC_SR			0x28
+#define ATMEL_HLCDC_IER			0x2c
+#define ATMEL_HLCDC_IDR			0x30
+#define ATMEL_HLCDC_IMR			0x34
+#define ATMEL_HLCDC_ISR			0x38
+
+#define ATMEL_HLCDC_CLKPOL		BIT(0)
+#define ATMEL_HLCDC_CLKSEL		BIT(2)
+#define ATMEL_HLCDC_CLKPWMSEL		BIT(3)
+#define ATMEL_HLCDC_CGDIS(i)		BIT(8 + (i))
+#define ATMEL_HLCDC_CLKDIV_SHFT		16
+#define ATMEL_HLCDC_CLKDIV_MASK		GENMASK(23, 16)
+#define ATMEL_HLCDC_CLKDIV(div)		((div - 2) << ATMEL_HLCDC_CLKDIV_SHFT)
+
+#define ATMEL_HLCDC_PIXEL_CLK		BIT(0)
+#define ATMEL_HLCDC_SYNC		BIT(1)
+#define ATMEL_HLCDC_DISP		BIT(2)
+#define ATMEL_HLCDC_PWM			BIT(3)
+#define ATMEL_HLCDC_SIP			BIT(4)
+
+/**
+ * Structure shared by the MFD device and its subdevices.
+ *
+ * @regmap: register map used to access HLCDC IP registers
+ * @periph_clk: the hlcdc peripheral clock
+ * @sys_clk: the hlcdc system clock
+ * @slow_clk: the system slow clk
+ */
+struct atmel_hlcdc {
+	struct regmap *regmap;
+	struct clk *periph_clk;
+	struct clk *sys_clk;
+	struct clk *slow_clk;
+};
+
+#endif /* __LINUX_MFD_HLCDC_H */
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 01/11] mfd: add atmel-hlcdc driver
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, linux-kernel,
	Rob Herring, Bo Shen, Kumar Gala, linux-arm-kernel

The HLCDC IP available on some Atmel SoCs (i.e. at91sam9n12, at91sam9x5
family or sama5d3 family) exposes 2 subdevices:
- a display controller (controlled by a DRM driver)
- a PWM chip

The MFD device provides a regmap and several clocks (those connected
to this hardware block) to its subdevices.

This way concurrent accesses to the iomem range are handled by the regmap
framework, and each subdevice can safely access HLCDC registers.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Acked-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mfd/Kconfig             |  12 ++++
 drivers/mfd/Makefile            |   1 +
 drivers/mfd/atmel-hlcdc.c       | 118 ++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/atmel-hlcdc.h |  78 ++++++++++++++++++++++++++
 4 files changed, 209 insertions(+)
 create mode 100644 drivers/mfd/atmel-hlcdc.c
 create mode 100644 include/linux/mfd/atmel-hlcdc.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 6cc4b6a..c3c007e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -59,6 +59,18 @@ config MFD_AAT2870_CORE
 	  additional drivers must be enabled in order to use the
 	  functionality of the device.
 
+config MFD_ATMEL_HLCDC
+	tristate "Atmel HLCDC (HLCD Controller)"
+	select MFD_CORE
+	select REGMAP_MMIO
+	depends on OF
+	help
+	  Choose this option if you have an ATMEL SoC with an HLCDC (HLCD
+	  Controller) IP (i.e. at91sam9n12, at91sam9x5 family or sama5d3
+	  family).
+	  This MFD device exposes two subdevices: a PWM chip and a Display
+	  Controller.
+
 config MFD_BCM590XX
 	tristate "Broadcom BCM590xx PMUs"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 8afedba..5f25b0d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -156,6 +156,7 @@ obj-$(CONFIG_MFD_PM8921_CORE) 	+= pm8921-core.o ssbi.o
 obj-$(CONFIG_TPS65911_COMPARATOR)	+= tps65911-comparator.o
 obj-$(CONFIG_MFD_TPS65090)	+= tps65090.o
 obj-$(CONFIG_MFD_AAT2870_CORE)	+= aat2870-core.o
+obj-$(CONFIG_MFD_ATMEL_HLCDC)	+= atmel-hlcdc.o
 obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
 obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
 obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c
new file mode 100644
index 0000000..aadb371
--- /dev/null
+++ b/drivers/mfd/atmel-hlcdc.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/atmel-hlcdc.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define ATMEL_HLCDC_REG_MAX		(0x4000 - 0x4)
+
+static const struct mfd_cell atmel_hlcdc_cells[] = {
+	{
+		.name = "atmel-hlcdc-pwm",
+		.of_compatible = "atmel,hlcdc-pwm",
+	},
+	{
+		.name = "atmel-hlcdc-dc",
+		.of_compatible = "atmel,hlcdc-display-controller",
+	},
+};
+
+static const struct regmap_config atmel_hlcdc_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = ATMEL_HLCDC_REG_MAX,
+};
+
+static int atmel_hlcdc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct atmel_hlcdc *hlcdc;
+	struct resource *res;
+	void __iomem *regs;
+
+	hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL);
+	if (!hlcdc)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	hlcdc->periph_clk = devm_clk_get(dev, "periph_clk");
+	if (IS_ERR(hlcdc->periph_clk)) {
+		dev_err(dev, "failed to get peripheral clock\n");
+		return PTR_ERR(hlcdc->periph_clk);
+	}
+
+	hlcdc->sys_clk = devm_clk_get(dev, "sys_clk");
+	if (IS_ERR(hlcdc->sys_clk)) {
+		dev_err(dev, "failed to get system clock\n");
+		return PTR_ERR(hlcdc->sys_clk);
+	}
+
+	hlcdc->slow_clk = devm_clk_get(dev, "slow_clk");
+	if (IS_ERR(hlcdc->slow_clk)) {
+		dev_err(dev, "failed to get slow clock\n");
+		return PTR_ERR(hlcdc->slow_clk);
+	}
+
+	hlcdc->regmap = devm_regmap_init_mmio(dev, regs,
+					      &atmel_hlcdc_regmap_config);
+	if (IS_ERR(hlcdc->regmap))
+		return PTR_ERR(hlcdc->regmap);
+
+	dev_set_drvdata(dev, hlcdc);
+
+	return mfd_add_devices(dev, -1, atmel_hlcdc_cells,
+			       ARRAY_SIZE(atmel_hlcdc_cells),
+			       NULL, 0, NULL);
+}
+
+static int atmel_hlcdc_remove(struct platform_device *pdev)
+{
+	mfd_remove_devices(&pdev->dev);
+
+	return 0;
+}
+
+static const struct of_device_id atmel_hlcdc_match[] = {
+	{ .compatible = "atmel,sama5d3-hlcdc" },
+	{ /* sentinel */ },
+};
+
+static struct platform_driver atmel_hlcdc_driver = {
+	.probe = atmel_hlcdc_probe,
+	.remove = atmel_hlcdc_remove,
+	.driver = {
+		.name = "atmel-hlcdc",
+		.of_match_table = atmel_hlcdc_match,
+	},
+};
+module_platform_driver(atmel_hlcdc_driver);
+
+MODULE_ALIAS("platform:atmel-hlcdc");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Atmel HLCDC driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/atmel-hlcdc.h b/include/linux/mfd/atmel-hlcdc.h
new file mode 100644
index 0000000..e9a503d
--- /dev/null
+++ b/include/linux/mfd/atmel-hlcdc.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_MFD_HLCDC_H
+#define __LINUX_MFD_HLCDC_H
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#define ATMEL_HLCDC_CFG(i)		((i) * 0x4)
+#define ATMEL_HLCDC_SIG_CFG		LCDCFG(5)
+#define ATMEL_HLCDC_HSPOL		BIT(0)
+#define ATMEL_HLCDC_VSPOL		BIT(1)
+#define ATMEL_HLCDC_VSPDLYS		BIT(2)
+#define ATMEL_HLCDC_VSPDLYE		BIT(3)
+#define ATMEL_HLCDC_DISPPOL		BIT(4)
+#define ATMEL_HLCDC_DITHER		BIT(6)
+#define ATMEL_HLCDC_DISPDLY		BIT(7)
+#define ATMEL_HLCDC_MODE_MASK		GENMASK(9, 8)
+#define ATMEL_HLCDC_PP			BIT(10)
+#define ATMEL_HLCDC_VSPSU		BIT(12)
+#define ATMEL_HLCDC_VSPHO		BIT(13)
+#define ATMEL_HLCDC_GUARDTIME_MASK	GENMASK(20, 16)
+
+#define ATMEL_HLCDC_EN			0x20
+#define ATMEL_HLCDC_DIS			0x24
+#define ATMEL_HLCDC_SR			0x28
+#define ATMEL_HLCDC_IER			0x2c
+#define ATMEL_HLCDC_IDR			0x30
+#define ATMEL_HLCDC_IMR			0x34
+#define ATMEL_HLCDC_ISR			0x38
+
+#define ATMEL_HLCDC_CLKPOL		BIT(0)
+#define ATMEL_HLCDC_CLKSEL		BIT(2)
+#define ATMEL_HLCDC_CLKPWMSEL		BIT(3)
+#define ATMEL_HLCDC_CGDIS(i)		BIT(8 + (i))
+#define ATMEL_HLCDC_CLKDIV_SHFT		16
+#define ATMEL_HLCDC_CLKDIV_MASK		GENMASK(23, 16)
+#define ATMEL_HLCDC_CLKDIV(div)		((div - 2) << ATMEL_HLCDC_CLKDIV_SHFT)
+
+#define ATMEL_HLCDC_PIXEL_CLK		BIT(0)
+#define ATMEL_HLCDC_SYNC		BIT(1)
+#define ATMEL_HLCDC_DISP		BIT(2)
+#define ATMEL_HLCDC_PWM			BIT(3)
+#define ATMEL_HLCDC_SIP			BIT(4)
+
+/**
+ * Structure shared by the MFD device and its subdevices.
+ *
+ * @regmap: register map used to access HLCDC IP registers
+ * @periph_clk: the hlcdc peripheral clock
+ * @sys_clk: the hlcdc system clock
+ * @slow_clk: the system slow clk
+ */
+struct atmel_hlcdc {
+	struct regmap *regmap;
+	struct clk *periph_clk;
+	struct clk *sys_clk;
+	struct clk *slow_clk;
+};
+
+#endif /* __LINUX_MFD_HLCDC_H */
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 01/11] mfd: add atmel-hlcdc driver
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

The HLCDC IP available on some Atmel SoCs (i.e. at91sam9n12, at91sam9x5
family or sama5d3 family) exposes 2 subdevices:
- a display controller (controlled by a DRM driver)
- a PWM chip

The MFD device provides a regmap and several clocks (those connected
to this hardware block) to its subdevices.

This way concurrent accesses to the iomem range are handled by the regmap
framework, and each subdevice can safely access HLCDC registers.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Acked-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mfd/Kconfig             |  12 ++++
 drivers/mfd/Makefile            |   1 +
 drivers/mfd/atmel-hlcdc.c       | 118 ++++++++++++++++++++++++++++++++++++++++
 include/linux/mfd/atmel-hlcdc.h |  78 ++++++++++++++++++++++++++
 4 files changed, 209 insertions(+)
 create mode 100644 drivers/mfd/atmel-hlcdc.c
 create mode 100644 include/linux/mfd/atmel-hlcdc.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 6cc4b6a..c3c007e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -59,6 +59,18 @@ config MFD_AAT2870_CORE
 	  additional drivers must be enabled in order to use the
 	  functionality of the device.
 
+config MFD_ATMEL_HLCDC
+	tristate "Atmel HLCDC (HLCD Controller)"
+	select MFD_CORE
+	select REGMAP_MMIO
+	depends on OF
+	help
+	  Choose this option if you have an ATMEL SoC with an HLCDC (HLCD
+	  Controller) IP (i.e. at91sam9n12, at91sam9x5 family or sama5d3
+	  family).
+	  This MFD device exposes two subdevices: a PWM chip and a Display
+	  Controller.
+
 config MFD_BCM590XX
 	tristate "Broadcom BCM590xx PMUs"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 8afedba..5f25b0d 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -156,6 +156,7 @@ obj-$(CONFIG_MFD_PM8921_CORE) 	+= pm8921-core.o ssbi.o
 obj-$(CONFIG_TPS65911_COMPARATOR)	+= tps65911-comparator.o
 obj-$(CONFIG_MFD_TPS65090)	+= tps65090.o
 obj-$(CONFIG_MFD_AAT2870_CORE)	+= aat2870-core.o
+obj-$(CONFIG_MFD_ATMEL_HLCDC)	+= atmel-hlcdc.o
 obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
 obj-$(CONFIG_MFD_PALMAS)	+= palmas.o
 obj-$(CONFIG_MFD_VIPERBOARD)    += viperboard.o
diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c
new file mode 100644
index 0000000..aadb371
--- /dev/null
+++ b/drivers/mfd/atmel-hlcdc.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/atmel-hlcdc.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define ATMEL_HLCDC_REG_MAX		(0x4000 - 0x4)
+
+static const struct mfd_cell atmel_hlcdc_cells[] = {
+	{
+		.name = "atmel-hlcdc-pwm",
+		.of_compatible = "atmel,hlcdc-pwm",
+	},
+	{
+		.name = "atmel-hlcdc-dc",
+		.of_compatible = "atmel,hlcdc-display-controller",
+	},
+};
+
+static const struct regmap_config atmel_hlcdc_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = ATMEL_HLCDC_REG_MAX,
+};
+
+static int atmel_hlcdc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct atmel_hlcdc *hlcdc;
+	struct resource *res;
+	void __iomem *regs;
+
+	hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL);
+	if (!hlcdc)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	hlcdc->periph_clk = devm_clk_get(dev, "periph_clk");
+	if (IS_ERR(hlcdc->periph_clk)) {
+		dev_err(dev, "failed to get peripheral clock\n");
+		return PTR_ERR(hlcdc->periph_clk);
+	}
+
+	hlcdc->sys_clk = devm_clk_get(dev, "sys_clk");
+	if (IS_ERR(hlcdc->sys_clk)) {
+		dev_err(dev, "failed to get system clock\n");
+		return PTR_ERR(hlcdc->sys_clk);
+	}
+
+	hlcdc->slow_clk = devm_clk_get(dev, "slow_clk");
+	if (IS_ERR(hlcdc->slow_clk)) {
+		dev_err(dev, "failed to get slow clock\n");
+		return PTR_ERR(hlcdc->slow_clk);
+	}
+
+	hlcdc->regmap = devm_regmap_init_mmio(dev, regs,
+					      &atmel_hlcdc_regmap_config);
+	if (IS_ERR(hlcdc->regmap))
+		return PTR_ERR(hlcdc->regmap);
+
+	dev_set_drvdata(dev, hlcdc);
+
+	return mfd_add_devices(dev, -1, atmel_hlcdc_cells,
+			       ARRAY_SIZE(atmel_hlcdc_cells),
+			       NULL, 0, NULL);
+}
+
+static int atmel_hlcdc_remove(struct platform_device *pdev)
+{
+	mfd_remove_devices(&pdev->dev);
+
+	return 0;
+}
+
+static const struct of_device_id atmel_hlcdc_match[] = {
+	{ .compatible = "atmel,sama5d3-hlcdc" },
+	{ /* sentinel */ },
+};
+
+static struct platform_driver atmel_hlcdc_driver = {
+	.probe = atmel_hlcdc_probe,
+	.remove = atmel_hlcdc_remove,
+	.driver = {
+		.name = "atmel-hlcdc",
+		.of_match_table = atmel_hlcdc_match,
+	},
+};
+module_platform_driver(atmel_hlcdc_driver);
+
+MODULE_ALIAS("platform:atmel-hlcdc");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Atmel HLCDC driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/atmel-hlcdc.h b/include/linux/mfd/atmel-hlcdc.h
new file mode 100644
index 0000000..e9a503d
--- /dev/null
+++ b/include/linux/mfd/atmel-hlcdc.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_MFD_HLCDC_H
+#define __LINUX_MFD_HLCDC_H
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#define ATMEL_HLCDC_CFG(i)		((i) * 0x4)
+#define ATMEL_HLCDC_SIG_CFG		LCDCFG(5)
+#define ATMEL_HLCDC_HSPOL		BIT(0)
+#define ATMEL_HLCDC_VSPOL		BIT(1)
+#define ATMEL_HLCDC_VSPDLYS		BIT(2)
+#define ATMEL_HLCDC_VSPDLYE		BIT(3)
+#define ATMEL_HLCDC_DISPPOL		BIT(4)
+#define ATMEL_HLCDC_DITHER		BIT(6)
+#define ATMEL_HLCDC_DISPDLY		BIT(7)
+#define ATMEL_HLCDC_MODE_MASK		GENMASK(9, 8)
+#define ATMEL_HLCDC_PP			BIT(10)
+#define ATMEL_HLCDC_VSPSU		BIT(12)
+#define ATMEL_HLCDC_VSPHO		BIT(13)
+#define ATMEL_HLCDC_GUARDTIME_MASK	GENMASK(20, 16)
+
+#define ATMEL_HLCDC_EN			0x20
+#define ATMEL_HLCDC_DIS			0x24
+#define ATMEL_HLCDC_SR			0x28
+#define ATMEL_HLCDC_IER			0x2c
+#define ATMEL_HLCDC_IDR			0x30
+#define ATMEL_HLCDC_IMR			0x34
+#define ATMEL_HLCDC_ISR			0x38
+
+#define ATMEL_HLCDC_CLKPOL		BIT(0)
+#define ATMEL_HLCDC_CLKSEL		BIT(2)
+#define ATMEL_HLCDC_CLKPWMSEL		BIT(3)
+#define ATMEL_HLCDC_CGDIS(i)		BIT(8 + (i))
+#define ATMEL_HLCDC_CLKDIV_SHFT		16
+#define ATMEL_HLCDC_CLKDIV_MASK		GENMASK(23, 16)
+#define ATMEL_HLCDC_CLKDIV(div)		((div - 2) << ATMEL_HLCDC_CLKDIV_SHFT)
+
+#define ATMEL_HLCDC_PIXEL_CLK		BIT(0)
+#define ATMEL_HLCDC_SYNC		BIT(1)
+#define ATMEL_HLCDC_DISP		BIT(2)
+#define ATMEL_HLCDC_PWM			BIT(3)
+#define ATMEL_HLCDC_SIP			BIT(4)
+
+/**
+ * Structure shared by the MFD device and its subdevices.
+ *
+ * @regmap: register map used to access HLCDC IP registers
+ * @periph_clk: the hlcdc peripheral clock
+ * @sys_clk: the hlcdc system clock
+ * @slow_clk: the system slow clk
+ */
+struct atmel_hlcdc {
+	struct regmap *regmap;
+	struct clk *periph_clk;
+	struct clk *sys_clk;
+	struct clk *slow_clk;
+};
+
+#endif /* __LINUX_MFD_HLCDC_H */
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 02/11] mfd: add documentation for atmel-hlcdc DT bindings
  2014-07-22 13:11 ` Boris BREZILLON
  (?)
@ 2014-07-22 13:11   ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree, linux-kernel, linux-arm-kernel, Bo Shen,
	Boris BREZILLON

The HLCDC IP available on some Atmel SoCs (i.e. at91sam9n12, at91sam9x5
family or sama5d3 family) exposes 2 subdevices:
- a display controller (controlled by a DRM driver)
- a PWM chip

This patch adds documentation for atmel-hlcdc DT bindings.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 .../devicetree/bindings/mfd/atmel-hlcdc.txt        | 50 ++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt

diff --git a/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
new file mode 100644
index 0000000..e9cc1b2
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
@@ -0,0 +1,50 @@
+Device-Tree bindings for Atmel's HLCDC (High LCD Controller) MFD driver
+
+Required properties:
+ - compatible: value should be one of the following:
+   "atmel,sama5d3-hlcdc"
+ - reg: base address and size of the HLCDC device registers.
+ - clock-names: the name of the 3 clocks requested by the HLCDC device.
+   Should contain "periph_clk", "sys_clk" and "slow_clk".
+ - clocks: should contain the 3 clocks requested by the HLCDC device.
+
+The HLCDC IP exposes two subdevices:
+ - a PWM chip: see ../pwm/atmel-hlcdc-pwm.txt
+ - a Display Controller: see ../drm/atmel-hlcdc-dc.txt
+
+Example:
+
+	hlcdc: hlcdc@f0030000 {
+		compatible = "atmel,sama5d3-hlcdc";
+		reg = <0xf0030000 0x2000>;
+		clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>;
+		clock-names = "periph_clk","sys_clk", "slow_clk";
+		status = "disabled";
+
+		hlcdc-display-controller {
+			compatible = "atmel,hlcdc-display-controller";
+			interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <0>;
+
+				hlcdc_panel_output: endpoint@0 {
+					reg = <0>;
+					remote-endpoint = <&panel_input>;
+				};
+			};
+		};
+
+		hlcdc_pwm: hlcdc-pwm {
+			compatible = "atmel,hlcdc-pwm";
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_pwm>;
+			#pwm-cells = <3>;
+		};
+	};
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 02/11] mfd: add documentation for atmel-hlcdc DT bindings
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, linux-kernel,
	Rob Herring, Bo Shen, Kumar Gala, linux-arm-kernel

The HLCDC IP available on some Atmel SoCs (i.e. at91sam9n12, at91sam9x5
family or sama5d3 family) exposes 2 subdevices:
- a display controller (controlled by a DRM driver)
- a PWM chip

This patch adds documentation for atmel-hlcdc DT bindings.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 .../devicetree/bindings/mfd/atmel-hlcdc.txt        | 50 ++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt

diff --git a/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
new file mode 100644
index 0000000..e9cc1b2
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
@@ -0,0 +1,50 @@
+Device-Tree bindings for Atmel's HLCDC (High LCD Controller) MFD driver
+
+Required properties:
+ - compatible: value should be one of the following:
+   "atmel,sama5d3-hlcdc"
+ - reg: base address and size of the HLCDC device registers.
+ - clock-names: the name of the 3 clocks requested by the HLCDC device.
+   Should contain "periph_clk", "sys_clk" and "slow_clk".
+ - clocks: should contain the 3 clocks requested by the HLCDC device.
+
+The HLCDC IP exposes two subdevices:
+ - a PWM chip: see ../pwm/atmel-hlcdc-pwm.txt
+ - a Display Controller: see ../drm/atmel-hlcdc-dc.txt
+
+Example:
+
+	hlcdc: hlcdc@f0030000 {
+		compatible = "atmel,sama5d3-hlcdc";
+		reg = <0xf0030000 0x2000>;
+		clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>;
+		clock-names = "periph_clk","sys_clk", "slow_clk";
+		status = "disabled";
+
+		hlcdc-display-controller {
+			compatible = "atmel,hlcdc-display-controller";
+			interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <0>;
+
+				hlcdc_panel_output: endpoint@0 {
+					reg = <0>;
+					remote-endpoint = <&panel_input>;
+				};
+			};
+		};
+
+		hlcdc_pwm: hlcdc-pwm {
+			compatible = "atmel,hlcdc-pwm";
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_pwm>;
+			#pwm-cells = <3>;
+		};
+	};
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 02/11] mfd: add documentation for atmel-hlcdc DT bindings
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

The HLCDC IP available on some Atmel SoCs (i.e. at91sam9n12, at91sam9x5
family or sama5d3 family) exposes 2 subdevices:
- a display controller (controlled by a DRM driver)
- a PWM chip

This patch adds documentation for atmel-hlcdc DT bindings.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 .../devicetree/bindings/mfd/atmel-hlcdc.txt        | 50 ++++++++++++++++++++++
 1 file changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt

diff --git a/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
new file mode 100644
index 0000000..e9cc1b2
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
@@ -0,0 +1,50 @@
+Device-Tree bindings for Atmel's HLCDC (High LCD Controller) MFD driver
+
+Required properties:
+ - compatible: value should be one of the following:
+   "atmel,sama5d3-hlcdc"
+ - reg: base address and size of the HLCDC device registers.
+ - clock-names: the name of the 3 clocks requested by the HLCDC device.
+   Should contain "periph_clk", "sys_clk" and "slow_clk".
+ - clocks: should contain the 3 clocks requested by the HLCDC device.
+
+The HLCDC IP exposes two subdevices:
+ - a PWM chip: see ../pwm/atmel-hlcdc-pwm.txt
+ - a Display Controller: see ../drm/atmel-hlcdc-dc.txt
+
+Example:
+
+	hlcdc: hlcdc at f0030000 {
+		compatible = "atmel,sama5d3-hlcdc";
+		reg = <0xf0030000 0x2000>;
+		clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>;
+		clock-names = "periph_clk","sys_clk", "slow_clk";
+		status = "disabled";
+
+		hlcdc-display-controller {
+			compatible = "atmel,hlcdc-display-controller";
+			interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port at 0 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <0>;
+
+				hlcdc_panel_output: endpoint at 0 {
+					reg = <0>;
+					remote-endpoint = <&panel_input>;
+				};
+			};
+		};
+
+		hlcdc_pwm: hlcdc-pwm {
+			compatible = "atmel,hlcdc-pwm";
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_pwm>;
+			#pwm-cells = <3>;
+		};
+	};
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 03/11] pwm: add support for atmel-hlcdc-pwm device
  2014-07-22 13:11 ` Boris BREZILLON
  (?)
@ 2014-07-22 13:11   ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree, linux-kernel, linux-arm-kernel, Bo Shen,
	Boris BREZILLON

The HLCDC IP available in some Atmel SoCs (i.e. sam9x5i.e. at91sam9n12,
at91sam9x5 family or sama5d3 family) provide a PWM device.

This driver add support for a PWM chip exposing a single PWM device (which
will most likely be used to drive a backlight device).

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 drivers/pwm/Kconfig           |   9 ++
 drivers/pwm/Makefile          |   1 +
 drivers/pwm/pwm-atmel-hlcdc.c | 229 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 239 insertions(+)
 create mode 100644 drivers/pwm/pwm-atmel-hlcdc.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 4ad7b89..3c8b7d0 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -50,6 +50,15 @@ config PWM_ATMEL
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-atmel.
 
+config PWM_ATMEL_HLCDC_PWM
+	tristate "Atmel HLCDC PWM support"
+	depends on MFD_ATMEL_HLCDC
+	help
+	  Generic PWM framework driver for Atmel HLCDC PWM.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-atmel.
+
 config PWM_ATMEL_TCB
 	tristate "Atmel TC Block PWM support"
 	depends on ATMEL_TCLIB && OF
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 5c86a19..26ae965 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_PWM)		+= core.o
 obj-$(CONFIG_PWM_SYSFS)		+= sysfs.o
 obj-$(CONFIG_PWM_AB8500)	+= pwm-ab8500.o
 obj-$(CONFIG_PWM_ATMEL)		+= pwm-atmel.o
+obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM)	+= pwm-atmel-hlcdc.o
 obj-$(CONFIG_PWM_ATMEL_TCB)	+= pwm-atmel-tcb.o
 obj-$(CONFIG_PWM_BCM_KONA)	+= pwm-bcm-kona.o
 obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c
new file mode 100644
index 0000000..7f25197
--- /dev/null
+++ b/drivers/pwm/pwm-atmel-hlcdc.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/atmel-hlcdc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+
+#define ATMEL_HLCDC_PWMCVAL_MASK	GENMASK(15, 8)
+#define ATMEL_HLCDC_PWMCVAL(x)		((x << 8) & ATMEL_HLCDC_PWMCVAL_MASK)
+#define ATMEL_HLCDC_PWMPOL		BIT(4)
+#define ATMEL_HLCDC_PWMPS_MASK		GENMASK(2, 0)
+#define ATMEL_HLCDC_PWMPS_MAX		0x6
+#define ATMEL_HLCDC_PWMPS(x)		((x) & ATMEL_HLCDC_PWMPS_MASK)
+
+struct atmel_hlcdc_pwm_chip {
+	struct pwm_chip chip;
+	struct atmel_hlcdc *hlcdc;
+	struct clk *cur_clk;
+};
+
+static inline struct atmel_hlcdc_pwm_chip *
+pwm_chip_to_atmel_hlcdc_pwm_chip(struct pwm_chip *chip)
+{
+	return container_of(chip, struct atmel_hlcdc_pwm_chip, chip);
+}
+
+static int atmel_hlcdc_pwm_config(struct pwm_chip *c,
+				  struct pwm_device *pwm,
+				  int duty_ns, int period_ns)
+{
+	struct atmel_hlcdc_pwm_chip *chip =
+				pwm_chip_to_atmel_hlcdc_pwm_chip(c);
+	struct atmel_hlcdc *hlcdc = chip->hlcdc;
+	struct clk *new_clk = hlcdc->slow_clk;
+	u64 pwmcval = duty_ns * 256;
+	unsigned long clk_freq;
+	u64 clk_period_ns;
+	u32 pwmcfg;
+	int pres;
+
+	clk_freq = clk_get_rate(new_clk);
+	clk_period_ns = 1000000000;
+	clk_period_ns *= 256;
+	do_div(clk_period_ns, clk_freq);
+
+	if (clk_period_ns > period_ns) {
+		new_clk = hlcdc->sys_clk;
+		clk_freq = clk_get_rate(new_clk);
+		clk_period_ns = 1000000000;
+		clk_period_ns *= 256;
+		do_div(clk_period_ns, clk_freq);
+	}
+
+	for (pres = ATMEL_HLCDC_PWMPS_MAX; pres >= 0; pres--) {
+		if ((clk_period_ns << pres) <= period_ns)
+			break;
+	}
+
+	if (pres > ATMEL_HLCDC_PWMPS_MAX)
+		return -EINVAL;
+
+	pwmcfg = ATMEL_HLCDC_PWMPS(pres);
+
+	if (new_clk != chip->cur_clk) {
+		u32 gencfg = 0;
+
+		clk_prepare_enable(new_clk);
+		clk_disable_unprepare(chip->cur_clk);
+		chip->cur_clk = new_clk;
+
+		if (new_clk != hlcdc->slow_clk)
+			gencfg = ATMEL_HLCDC_CLKPWMSEL;
+		regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(0),
+				   ATMEL_HLCDC_CLKPWMSEL, gencfg);
+	}
+
+	do_div(pwmcval, period_ns);
+	if (pwmcval > 255)
+		pwmcval = 255;
+
+	pwmcfg |= ATMEL_HLCDC_PWMCVAL(pwmcval);
+
+	regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),
+			   ATMEL_HLCDC_PWMCVAL_MASK | ATMEL_HLCDC_PWMPS_MASK,
+			   pwmcfg);
+
+	return 0;
+}
+
+static int atmel_hlcdc_pwm_set_polarity(struct pwm_chip *c,
+					struct pwm_device *pwm,
+					enum pwm_polarity polarity)
+{
+	struct atmel_hlcdc_pwm_chip *chip =
+				pwm_chip_to_atmel_hlcdc_pwm_chip(c);
+	struct atmel_hlcdc *hlcdc = chip->hlcdc;
+	u32 cfg = 0;
+
+	if (polarity == PWM_POLARITY_NORMAL)
+		cfg = ATMEL_HLCDC_PWMPOL;
+
+	regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),
+			   ATMEL_HLCDC_PWMPOL, cfg);
+
+	return 0;
+}
+
+static int atmel_hlcdc_pwm_enable(struct pwm_chip *c,
+				  struct pwm_device *pwm)
+{
+	struct atmel_hlcdc_pwm_chip *chip =
+				pwm_chip_to_atmel_hlcdc_pwm_chip(c);
+	struct atmel_hlcdc *hlcdc = chip->hlcdc;
+	u32 status;
+
+	regmap_write(hlcdc->regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PWM);
+	while (!regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status) &&
+	       !(status & ATMEL_HLCDC_PWM))
+		;
+
+	return 0;
+}
+
+static void atmel_hlcdc_pwm_disable(struct pwm_chip *c,
+				    struct pwm_device *pwm)
+{
+	struct atmel_hlcdc_pwm_chip *chip =
+				pwm_chip_to_atmel_hlcdc_pwm_chip(c);
+	struct atmel_hlcdc *hlcdc = chip->hlcdc;
+	u32 status;
+
+	regmap_write(hlcdc->regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PWM);
+	while (!regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status) &&
+	       (status & ATMEL_HLCDC_PWM))
+		;
+}
+
+static const struct pwm_ops atmel_hlcdc_pwm_ops = {
+	.config = atmel_hlcdc_pwm_config,
+	.set_polarity = atmel_hlcdc_pwm_set_polarity,
+	.enable = atmel_hlcdc_pwm_enable,
+	.disable = atmel_hlcdc_pwm_disable,
+	.owner = THIS_MODULE,
+};
+
+static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
+{
+	struct atmel_hlcdc_pwm_chip *chip;
+	struct device *dev = &pdev->dev;
+	struct atmel_hlcdc *hlcdc;
+	int ret;
+
+	hlcdc = dev_get_drvdata(dev->parent);
+	if (!hlcdc)
+		return -EINVAL;
+
+	ret = clk_prepare_enable(hlcdc->periph_clk);
+	if (ret)
+		return ret;
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->hlcdc = hlcdc;
+	chip->chip.ops = &atmel_hlcdc_pwm_ops;
+	chip->chip.dev = dev;
+	chip->chip.base = -1;
+	chip->chip.npwm = 1;
+	chip->chip.of_xlate = of_pwm_xlate_with_flags;
+	chip->chip.of_pwm_n_cells = 3;
+	chip->chip.can_sleep = 1;
+
+	ret = pwmchip_add(&chip->chip);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, chip);
+
+	return 0;
+}
+
+static int atmel_hlcdc_pwm_remove(struct platform_device *pdev)
+{
+	struct atmel_hlcdc_pwm_chip *chip = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(chip->hlcdc->periph_clk);
+
+	return pwmchip_remove(&chip->chip);
+}
+
+static const struct of_device_id atmel_hlcdc_pwm_dt_ids[] = {
+	{ .compatible = "atmel,hlcdc-pwm" },
+	{ /* sentinel */ },
+};
+
+static struct platform_driver atmel_hlcdc_pwm_driver = {
+	.driver = {
+		.name = "atmel-hlcdc-pwm",
+		.of_match_table = atmel_hlcdc_pwm_dt_ids,
+	},
+	.probe = atmel_hlcdc_pwm_probe,
+	.remove = atmel_hlcdc_pwm_remove,
+};
+module_platform_driver(atmel_hlcdc_pwm_driver);
+
+MODULE_ALIAS("platform:atmel-hlcdc-pwm");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Atmel HLCDC PWM driver");
+MODULE_LICENSE("GPL");
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 03/11] pwm: add support for atmel-hlcdc-pwm device
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, linux-kernel,
	Rob Herring, Bo Shen, Kumar Gala, linux-arm-kernel

The HLCDC IP available in some Atmel SoCs (i.e. sam9x5i.e. at91sam9n12,
at91sam9x5 family or sama5d3 family) provide a PWM device.

This driver add support for a PWM chip exposing a single PWM device (which
will most likely be used to drive a backlight device).

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 drivers/pwm/Kconfig           |   9 ++
 drivers/pwm/Makefile          |   1 +
 drivers/pwm/pwm-atmel-hlcdc.c | 229 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 239 insertions(+)
 create mode 100644 drivers/pwm/pwm-atmel-hlcdc.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 4ad7b89..3c8b7d0 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -50,6 +50,15 @@ config PWM_ATMEL
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-atmel.
 
+config PWM_ATMEL_HLCDC_PWM
+	tristate "Atmel HLCDC PWM support"
+	depends on MFD_ATMEL_HLCDC
+	help
+	  Generic PWM framework driver for Atmel HLCDC PWM.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-atmel.
+
 config PWM_ATMEL_TCB
 	tristate "Atmel TC Block PWM support"
 	depends on ATMEL_TCLIB && OF
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 5c86a19..26ae965 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_PWM)		+= core.o
 obj-$(CONFIG_PWM_SYSFS)		+= sysfs.o
 obj-$(CONFIG_PWM_AB8500)	+= pwm-ab8500.o
 obj-$(CONFIG_PWM_ATMEL)		+= pwm-atmel.o
+obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM)	+= pwm-atmel-hlcdc.o
 obj-$(CONFIG_PWM_ATMEL_TCB)	+= pwm-atmel-tcb.o
 obj-$(CONFIG_PWM_BCM_KONA)	+= pwm-bcm-kona.o
 obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c
new file mode 100644
index 0000000..7f25197
--- /dev/null
+++ b/drivers/pwm/pwm-atmel-hlcdc.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/atmel-hlcdc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+
+#define ATMEL_HLCDC_PWMCVAL_MASK	GENMASK(15, 8)
+#define ATMEL_HLCDC_PWMCVAL(x)		((x << 8) & ATMEL_HLCDC_PWMCVAL_MASK)
+#define ATMEL_HLCDC_PWMPOL		BIT(4)
+#define ATMEL_HLCDC_PWMPS_MASK		GENMASK(2, 0)
+#define ATMEL_HLCDC_PWMPS_MAX		0x6
+#define ATMEL_HLCDC_PWMPS(x)		((x) & ATMEL_HLCDC_PWMPS_MASK)
+
+struct atmel_hlcdc_pwm_chip {
+	struct pwm_chip chip;
+	struct atmel_hlcdc *hlcdc;
+	struct clk *cur_clk;
+};
+
+static inline struct atmel_hlcdc_pwm_chip *
+pwm_chip_to_atmel_hlcdc_pwm_chip(struct pwm_chip *chip)
+{
+	return container_of(chip, struct atmel_hlcdc_pwm_chip, chip);
+}
+
+static int atmel_hlcdc_pwm_config(struct pwm_chip *c,
+				  struct pwm_device *pwm,
+				  int duty_ns, int period_ns)
+{
+	struct atmel_hlcdc_pwm_chip *chip =
+				pwm_chip_to_atmel_hlcdc_pwm_chip(c);
+	struct atmel_hlcdc *hlcdc = chip->hlcdc;
+	struct clk *new_clk = hlcdc->slow_clk;
+	u64 pwmcval = duty_ns * 256;
+	unsigned long clk_freq;
+	u64 clk_period_ns;
+	u32 pwmcfg;
+	int pres;
+
+	clk_freq = clk_get_rate(new_clk);
+	clk_period_ns = 1000000000;
+	clk_period_ns *= 256;
+	do_div(clk_period_ns, clk_freq);
+
+	if (clk_period_ns > period_ns) {
+		new_clk = hlcdc->sys_clk;
+		clk_freq = clk_get_rate(new_clk);
+		clk_period_ns = 1000000000;
+		clk_period_ns *= 256;
+		do_div(clk_period_ns, clk_freq);
+	}
+
+	for (pres = ATMEL_HLCDC_PWMPS_MAX; pres >= 0; pres--) {
+		if ((clk_period_ns << pres) <= period_ns)
+			break;
+	}
+
+	if (pres > ATMEL_HLCDC_PWMPS_MAX)
+		return -EINVAL;
+
+	pwmcfg = ATMEL_HLCDC_PWMPS(pres);
+
+	if (new_clk != chip->cur_clk) {
+		u32 gencfg = 0;
+
+		clk_prepare_enable(new_clk);
+		clk_disable_unprepare(chip->cur_clk);
+		chip->cur_clk = new_clk;
+
+		if (new_clk != hlcdc->slow_clk)
+			gencfg = ATMEL_HLCDC_CLKPWMSEL;
+		regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(0),
+				   ATMEL_HLCDC_CLKPWMSEL, gencfg);
+	}
+
+	do_div(pwmcval, period_ns);
+	if (pwmcval > 255)
+		pwmcval = 255;
+
+	pwmcfg |= ATMEL_HLCDC_PWMCVAL(pwmcval);
+
+	regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),
+			   ATMEL_HLCDC_PWMCVAL_MASK | ATMEL_HLCDC_PWMPS_MASK,
+			   pwmcfg);
+
+	return 0;
+}
+
+static int atmel_hlcdc_pwm_set_polarity(struct pwm_chip *c,
+					struct pwm_device *pwm,
+					enum pwm_polarity polarity)
+{
+	struct atmel_hlcdc_pwm_chip *chip =
+				pwm_chip_to_atmel_hlcdc_pwm_chip(c);
+	struct atmel_hlcdc *hlcdc = chip->hlcdc;
+	u32 cfg = 0;
+
+	if (polarity == PWM_POLARITY_NORMAL)
+		cfg = ATMEL_HLCDC_PWMPOL;
+
+	regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),
+			   ATMEL_HLCDC_PWMPOL, cfg);
+
+	return 0;
+}
+
+static int atmel_hlcdc_pwm_enable(struct pwm_chip *c,
+				  struct pwm_device *pwm)
+{
+	struct atmel_hlcdc_pwm_chip *chip =
+				pwm_chip_to_atmel_hlcdc_pwm_chip(c);
+	struct atmel_hlcdc *hlcdc = chip->hlcdc;
+	u32 status;
+
+	regmap_write(hlcdc->regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PWM);
+	while (!regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status) &&
+	       !(status & ATMEL_HLCDC_PWM))
+		;
+
+	return 0;
+}
+
+static void atmel_hlcdc_pwm_disable(struct pwm_chip *c,
+				    struct pwm_device *pwm)
+{
+	struct atmel_hlcdc_pwm_chip *chip =
+				pwm_chip_to_atmel_hlcdc_pwm_chip(c);
+	struct atmel_hlcdc *hlcdc = chip->hlcdc;
+	u32 status;
+
+	regmap_write(hlcdc->regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PWM);
+	while (!regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status) &&
+	       (status & ATMEL_HLCDC_PWM))
+		;
+}
+
+static const struct pwm_ops atmel_hlcdc_pwm_ops = {
+	.config = atmel_hlcdc_pwm_config,
+	.set_polarity = atmel_hlcdc_pwm_set_polarity,
+	.enable = atmel_hlcdc_pwm_enable,
+	.disable = atmel_hlcdc_pwm_disable,
+	.owner = THIS_MODULE,
+};
+
+static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
+{
+	struct atmel_hlcdc_pwm_chip *chip;
+	struct device *dev = &pdev->dev;
+	struct atmel_hlcdc *hlcdc;
+	int ret;
+
+	hlcdc = dev_get_drvdata(dev->parent);
+	if (!hlcdc)
+		return -EINVAL;
+
+	ret = clk_prepare_enable(hlcdc->periph_clk);
+	if (ret)
+		return ret;
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->hlcdc = hlcdc;
+	chip->chip.ops = &atmel_hlcdc_pwm_ops;
+	chip->chip.dev = dev;
+	chip->chip.base = -1;
+	chip->chip.npwm = 1;
+	chip->chip.of_xlate = of_pwm_xlate_with_flags;
+	chip->chip.of_pwm_n_cells = 3;
+	chip->chip.can_sleep = 1;
+
+	ret = pwmchip_add(&chip->chip);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, chip);
+
+	return 0;
+}
+
+static int atmel_hlcdc_pwm_remove(struct platform_device *pdev)
+{
+	struct atmel_hlcdc_pwm_chip *chip = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(chip->hlcdc->periph_clk);
+
+	return pwmchip_remove(&chip->chip);
+}
+
+static const struct of_device_id atmel_hlcdc_pwm_dt_ids[] = {
+	{ .compatible = "atmel,hlcdc-pwm" },
+	{ /* sentinel */ },
+};
+
+static struct platform_driver atmel_hlcdc_pwm_driver = {
+	.driver = {
+		.name = "atmel-hlcdc-pwm",
+		.of_match_table = atmel_hlcdc_pwm_dt_ids,
+	},
+	.probe = atmel_hlcdc_pwm_probe,
+	.remove = atmel_hlcdc_pwm_remove,
+};
+module_platform_driver(atmel_hlcdc_pwm_driver);
+
+MODULE_ALIAS("platform:atmel-hlcdc-pwm");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Atmel HLCDC PWM driver");
+MODULE_LICENSE("GPL");
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 03/11] pwm: add support for atmel-hlcdc-pwm device
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

The HLCDC IP available in some Atmel SoCs (i.e. sam9x5i.e. at91sam9n12,
at91sam9x5 family or sama5d3 family) provide a PWM device.

This driver add support for a PWM chip exposing a single PWM device (which
will most likely be used to drive a backlight device).

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 drivers/pwm/Kconfig           |   9 ++
 drivers/pwm/Makefile          |   1 +
 drivers/pwm/pwm-atmel-hlcdc.c | 229 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 239 insertions(+)
 create mode 100644 drivers/pwm/pwm-atmel-hlcdc.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 4ad7b89..3c8b7d0 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -50,6 +50,15 @@ config PWM_ATMEL
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-atmel.
 
+config PWM_ATMEL_HLCDC_PWM
+	tristate "Atmel HLCDC PWM support"
+	depends on MFD_ATMEL_HLCDC
+	help
+	  Generic PWM framework driver for Atmel HLCDC PWM.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called pwm-atmel.
+
 config PWM_ATMEL_TCB
 	tristate "Atmel TC Block PWM support"
 	depends on ATMEL_TCLIB && OF
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 5c86a19..26ae965 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_PWM)		+= core.o
 obj-$(CONFIG_PWM_SYSFS)		+= sysfs.o
 obj-$(CONFIG_PWM_AB8500)	+= pwm-ab8500.o
 obj-$(CONFIG_PWM_ATMEL)		+= pwm-atmel.o
+obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM)	+= pwm-atmel-hlcdc.o
 obj-$(CONFIG_PWM_ATMEL_TCB)	+= pwm-atmel-tcb.o
 obj-$(CONFIG_PWM_BCM_KONA)	+= pwm-bcm-kona.o
 obj-$(CONFIG_PWM_BFIN)		+= pwm-bfin.o
diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c
new file mode 100644
index 0000000..7f25197
--- /dev/null
+++ b/drivers/pwm/pwm-atmel-hlcdc.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/atmel-hlcdc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+
+#define ATMEL_HLCDC_PWMCVAL_MASK	GENMASK(15, 8)
+#define ATMEL_HLCDC_PWMCVAL(x)		((x << 8) & ATMEL_HLCDC_PWMCVAL_MASK)
+#define ATMEL_HLCDC_PWMPOL		BIT(4)
+#define ATMEL_HLCDC_PWMPS_MASK		GENMASK(2, 0)
+#define ATMEL_HLCDC_PWMPS_MAX		0x6
+#define ATMEL_HLCDC_PWMPS(x)		((x) & ATMEL_HLCDC_PWMPS_MASK)
+
+struct atmel_hlcdc_pwm_chip {
+	struct pwm_chip chip;
+	struct atmel_hlcdc *hlcdc;
+	struct clk *cur_clk;
+};
+
+static inline struct atmel_hlcdc_pwm_chip *
+pwm_chip_to_atmel_hlcdc_pwm_chip(struct pwm_chip *chip)
+{
+	return container_of(chip, struct atmel_hlcdc_pwm_chip, chip);
+}
+
+static int atmel_hlcdc_pwm_config(struct pwm_chip *c,
+				  struct pwm_device *pwm,
+				  int duty_ns, int period_ns)
+{
+	struct atmel_hlcdc_pwm_chip *chip =
+				pwm_chip_to_atmel_hlcdc_pwm_chip(c);
+	struct atmel_hlcdc *hlcdc = chip->hlcdc;
+	struct clk *new_clk = hlcdc->slow_clk;
+	u64 pwmcval = duty_ns * 256;
+	unsigned long clk_freq;
+	u64 clk_period_ns;
+	u32 pwmcfg;
+	int pres;
+
+	clk_freq = clk_get_rate(new_clk);
+	clk_period_ns = 1000000000;
+	clk_period_ns *= 256;
+	do_div(clk_period_ns, clk_freq);
+
+	if (clk_period_ns > period_ns) {
+		new_clk = hlcdc->sys_clk;
+		clk_freq = clk_get_rate(new_clk);
+		clk_period_ns = 1000000000;
+		clk_period_ns *= 256;
+		do_div(clk_period_ns, clk_freq);
+	}
+
+	for (pres = ATMEL_HLCDC_PWMPS_MAX; pres >= 0; pres--) {
+		if ((clk_period_ns << pres) <= period_ns)
+			break;
+	}
+
+	if (pres > ATMEL_HLCDC_PWMPS_MAX)
+		return -EINVAL;
+
+	pwmcfg = ATMEL_HLCDC_PWMPS(pres);
+
+	if (new_clk != chip->cur_clk) {
+		u32 gencfg = 0;
+
+		clk_prepare_enable(new_clk);
+		clk_disable_unprepare(chip->cur_clk);
+		chip->cur_clk = new_clk;
+
+		if (new_clk != hlcdc->slow_clk)
+			gencfg = ATMEL_HLCDC_CLKPWMSEL;
+		regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(0),
+				   ATMEL_HLCDC_CLKPWMSEL, gencfg);
+	}
+
+	do_div(pwmcval, period_ns);
+	if (pwmcval > 255)
+		pwmcval = 255;
+
+	pwmcfg |= ATMEL_HLCDC_PWMCVAL(pwmcval);
+
+	regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),
+			   ATMEL_HLCDC_PWMCVAL_MASK | ATMEL_HLCDC_PWMPS_MASK,
+			   pwmcfg);
+
+	return 0;
+}
+
+static int atmel_hlcdc_pwm_set_polarity(struct pwm_chip *c,
+					struct pwm_device *pwm,
+					enum pwm_polarity polarity)
+{
+	struct atmel_hlcdc_pwm_chip *chip =
+				pwm_chip_to_atmel_hlcdc_pwm_chip(c);
+	struct atmel_hlcdc *hlcdc = chip->hlcdc;
+	u32 cfg = 0;
+
+	if (polarity == PWM_POLARITY_NORMAL)
+		cfg = ATMEL_HLCDC_PWMPOL;
+
+	regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),
+			   ATMEL_HLCDC_PWMPOL, cfg);
+
+	return 0;
+}
+
+static int atmel_hlcdc_pwm_enable(struct pwm_chip *c,
+				  struct pwm_device *pwm)
+{
+	struct atmel_hlcdc_pwm_chip *chip =
+				pwm_chip_to_atmel_hlcdc_pwm_chip(c);
+	struct atmel_hlcdc *hlcdc = chip->hlcdc;
+	u32 status;
+
+	regmap_write(hlcdc->regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PWM);
+	while (!regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status) &&
+	       !(status & ATMEL_HLCDC_PWM))
+		;
+
+	return 0;
+}
+
+static void atmel_hlcdc_pwm_disable(struct pwm_chip *c,
+				    struct pwm_device *pwm)
+{
+	struct atmel_hlcdc_pwm_chip *chip =
+				pwm_chip_to_atmel_hlcdc_pwm_chip(c);
+	struct atmel_hlcdc *hlcdc = chip->hlcdc;
+	u32 status;
+
+	regmap_write(hlcdc->regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PWM);
+	while (!regmap_read(hlcdc->regmap, ATMEL_HLCDC_SR, &status) &&
+	       (status & ATMEL_HLCDC_PWM))
+		;
+}
+
+static const struct pwm_ops atmel_hlcdc_pwm_ops = {
+	.config = atmel_hlcdc_pwm_config,
+	.set_polarity = atmel_hlcdc_pwm_set_polarity,
+	.enable = atmel_hlcdc_pwm_enable,
+	.disable = atmel_hlcdc_pwm_disable,
+	.owner = THIS_MODULE,
+};
+
+static int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
+{
+	struct atmel_hlcdc_pwm_chip *chip;
+	struct device *dev = &pdev->dev;
+	struct atmel_hlcdc *hlcdc;
+	int ret;
+
+	hlcdc = dev_get_drvdata(dev->parent);
+	if (!hlcdc)
+		return -EINVAL;
+
+	ret = clk_prepare_enable(hlcdc->periph_clk);
+	if (ret)
+		return ret;
+
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->hlcdc = hlcdc;
+	chip->chip.ops = &atmel_hlcdc_pwm_ops;
+	chip->chip.dev = dev;
+	chip->chip.base = -1;
+	chip->chip.npwm = 1;
+	chip->chip.of_xlate = of_pwm_xlate_with_flags;
+	chip->chip.of_pwm_n_cells = 3;
+	chip->chip.can_sleep = 1;
+
+	ret = pwmchip_add(&chip->chip);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, chip);
+
+	return 0;
+}
+
+static int atmel_hlcdc_pwm_remove(struct platform_device *pdev)
+{
+	struct atmel_hlcdc_pwm_chip *chip = platform_get_drvdata(pdev);
+
+	clk_disable_unprepare(chip->hlcdc->periph_clk);
+
+	return pwmchip_remove(&chip->chip);
+}
+
+static const struct of_device_id atmel_hlcdc_pwm_dt_ids[] = {
+	{ .compatible = "atmel,hlcdc-pwm" },
+	{ /* sentinel */ },
+};
+
+static struct platform_driver atmel_hlcdc_pwm_driver = {
+	.driver = {
+		.name = "atmel-hlcdc-pwm",
+		.of_match_table = atmel_hlcdc_pwm_dt_ids,
+	},
+	.probe = atmel_hlcdc_pwm_probe,
+	.remove = atmel_hlcdc_pwm_remove,
+};
+module_platform_driver(atmel_hlcdc_pwm_driver);
+
+MODULE_ALIAS("platform:atmel-hlcdc-pwm");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Atmel HLCDC PWM driver");
+MODULE_LICENSE("GPL");
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 04/11] pwm: add DT bindings documentation for atmel-hlcdc-pwm driver
  2014-07-22 13:11 ` Boris BREZILLON
  (?)
@ 2014-07-22 13:11   ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree, linux-kernel, linux-arm-kernel, Bo Shen,
	Boris BREZILLON

The HLCDC IP available in some Atmel SoCs (i.e. sam9x5i.e. at91sam9n12,
at91sam9x5 family or sama5d3 family) provide a PWM device.

The DT bindings used for this PWM device is following the default 3 cells
bindings described in Documentation/devicetree/bindings/pwm/pwm.txt.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 .../devicetree/bindings/pwm/atmel-hlcdc-pwm.txt    | 55 ++++++++++++++++++++++
 1 file changed, 55 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt

diff --git a/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt b/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
new file mode 100644
index 0000000..86ad3e2
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
@@ -0,0 +1,55 @@
+Device-Tree bindings for Atmel's HLCDC (High LCD Controller) PWM driver
+
+The Atmel HLCDC PWM is subdevice of the HLCDC MFD device.
+See ../mfd/atmel-hlcdc.txt for more details.
+
+Required properties:
+ - compatible: value should be one of the following:
+   "atmel,hlcdc-pwm"
+ - pinctr-names: the pin control state names. Should contain "default".
+ - pinctrl-0: should contain the pinctrl states described by pinctrl
+   default.
+ - #pwm-cells: should be set to 3. This PWM chip use the default 3 cells
+   bindings defined in Documentation/devicetree/bindings/pwm/pwm.txt.
+   The first cell encodes the PWM id (0 is the only acceptable value here,
+   because the chip only provide one PWM).
+   The second cell encodes the PWM period in nanoseconds.
+   The third cell encodes the PWM flags (the only supported flag is
+   PWM_POLARITY_INVERTED)
+
+Example:
+
+	hlcdc: hlcdc@f0030000 {
+		compatible = "atmel,sama5d3-hlcdc";
+		reg = <0xf0030000 0x2000>;
+		clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>;
+		clock-names = "periph_clk","sys_clk", "slow_clk";
+		status = "disabled";
+
+		hlcdc-display-controller {
+			compatible = "atmel,hlcdc-display-controller";
+			interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <0>;
+
+				hlcdc_panel_output: endpoint@0 {
+					reg = <0>;
+					remote-endpoint = <&panel_input>;
+				};
+			};
+		};
+
+		hlcdc_pwm: hlcdc-pwm {
+			compatible = "atmel,hlcdc-pwm";
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_pwm>;
+			#pwm-cells = <3>;
+		};
+	};
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 04/11] pwm: add DT bindings documentation for atmel-hlcdc-pwm driver
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, linux-kernel,
	Rob Herring, Bo Shen, Kumar Gala, linux-arm-kernel

The HLCDC IP available in some Atmel SoCs (i.e. sam9x5i.e. at91sam9n12,
at91sam9x5 family or sama5d3 family) provide a PWM device.

The DT bindings used for this PWM device is following the default 3 cells
bindings described in Documentation/devicetree/bindings/pwm/pwm.txt.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 .../devicetree/bindings/pwm/atmel-hlcdc-pwm.txt    | 55 ++++++++++++++++++++++
 1 file changed, 55 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt

diff --git a/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt b/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
new file mode 100644
index 0000000..86ad3e2
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
@@ -0,0 +1,55 @@
+Device-Tree bindings for Atmel's HLCDC (High LCD Controller) PWM driver
+
+The Atmel HLCDC PWM is subdevice of the HLCDC MFD device.
+See ../mfd/atmel-hlcdc.txt for more details.
+
+Required properties:
+ - compatible: value should be one of the following:
+   "atmel,hlcdc-pwm"
+ - pinctr-names: the pin control state names. Should contain "default".
+ - pinctrl-0: should contain the pinctrl states described by pinctrl
+   default.
+ - #pwm-cells: should be set to 3. This PWM chip use the default 3 cells
+   bindings defined in Documentation/devicetree/bindings/pwm/pwm.txt.
+   The first cell encodes the PWM id (0 is the only acceptable value here,
+   because the chip only provide one PWM).
+   The second cell encodes the PWM period in nanoseconds.
+   The third cell encodes the PWM flags (the only supported flag is
+   PWM_POLARITY_INVERTED)
+
+Example:
+
+	hlcdc: hlcdc@f0030000 {
+		compatible = "atmel,sama5d3-hlcdc";
+		reg = <0xf0030000 0x2000>;
+		clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>;
+		clock-names = "periph_clk","sys_clk", "slow_clk";
+		status = "disabled";
+
+		hlcdc-display-controller {
+			compatible = "atmel,hlcdc-display-controller";
+			interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <0>;
+
+				hlcdc_panel_output: endpoint@0 {
+					reg = <0>;
+					remote-endpoint = <&panel_input>;
+				};
+			};
+		};
+
+		hlcdc_pwm: hlcdc-pwm {
+			compatible = "atmel,hlcdc-pwm";
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_pwm>;
+			#pwm-cells = <3>;
+		};
+	};
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 04/11] pwm: add DT bindings documentation for atmel-hlcdc-pwm driver
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

The HLCDC IP available in some Atmel SoCs (i.e. sam9x5i.e. at91sam9n12,
at91sam9x5 family or sama5d3 family) provide a PWM device.

The DT bindings used for this PWM device is following the default 3 cells
bindings described in Documentation/devicetree/bindings/pwm/pwm.txt.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 .../devicetree/bindings/pwm/atmel-hlcdc-pwm.txt    | 55 ++++++++++++++++++++++
 1 file changed, 55 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt

diff --git a/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt b/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
new file mode 100644
index 0000000..86ad3e2
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
@@ -0,0 +1,55 @@
+Device-Tree bindings for Atmel's HLCDC (High LCD Controller) PWM driver
+
+The Atmel HLCDC PWM is subdevice of the HLCDC MFD device.
+See ../mfd/atmel-hlcdc.txt for more details.
+
+Required properties:
+ - compatible: value should be one of the following:
+   "atmel,hlcdc-pwm"
+ - pinctr-names: the pin control state names. Should contain "default".
+ - pinctrl-0: should contain the pinctrl states described by pinctrl
+   default.
+ - #pwm-cells: should be set to 3. This PWM chip use the default 3 cells
+   bindings defined in Documentation/devicetree/bindings/pwm/pwm.txt.
+   The first cell encodes the PWM id (0 is the only acceptable value here,
+   because the chip only provide one PWM).
+   The second cell encodes the PWM period in nanoseconds.
+   The third cell encodes the PWM flags (the only supported flag is
+   PWM_POLARITY_INVERTED)
+
+Example:
+
+	hlcdc: hlcdc at f0030000 {
+		compatible = "atmel,sama5d3-hlcdc";
+		reg = <0xf0030000 0x2000>;
+		clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>;
+		clock-names = "periph_clk","sys_clk", "slow_clk";
+		status = "disabled";
+
+		hlcdc-display-controller {
+			compatible = "atmel,hlcdc-display-controller";
+			interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port at 0 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <0>;
+
+				hlcdc_panel_output: endpoint at 0 {
+					reg = <0>;
+					remote-endpoint = <&panel_input>;
+				};
+			};
+		};
+
+		hlcdc_pwm: hlcdc-pwm {
+			compatible = "atmel,hlcdc-pwm";
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_pwm>;
+			#pwm-cells = <3>;
+		};
+	};
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 05/11] drm: add Atmel HLCDC Display Controller support
  2014-07-22 13:11 ` Boris BREZILLON
  (?)
@ 2014-07-22 13:11   ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree, linux-kernel, linux-arm-kernel, Bo Shen,
	Boris BREZILLON

The Atmel HLCDC (HLCD Controller) IP available on some Atmel SoCs (i.e.
at91sam9n12, at91sam9x5 family or sama5d3 family) provides a display
controller device.

This display controller supports at least one primary plane and might
provide several overlays and an hardware cursor depending on the IP
version.

At the moment, this driver only implements an RGB connector to interface
with LCD panels, but support for other kind of external devices might be
added later.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 drivers/gpu/drm/Kconfig                          |   2 +
 drivers/gpu/drm/Makefile                         |   1 +
 drivers/gpu/drm/atmel-hlcdc/Kconfig              |  11 +
 drivers/gpu/drm/atmel-hlcdc/Makefile             |   7 +
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c   | 286 ++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c     | 488 ++++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h     | 224 +++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c  | 635 ++++++++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h  | 396 +++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 478 ++++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c  | 804 +++++++++++++++++++++++
 11 files changed, 3332 insertions(+)
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/Kconfig
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/Makefile
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f512004..9183a78 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -184,6 +184,8 @@ source "drivers/gpu/drm/cirrus/Kconfig"
 
 source "drivers/gpu/drm/armada/Kconfig"
 
+source "drivers/gpu/drm/atmel-hlcdc/Kconfig"
+
 source "drivers/gpu/drm/rcar-du/Kconfig"
 
 source "drivers/gpu/drm/shmobile/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index af9a609..07d388c 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
 obj-$(CONFIG_DRM_ARMADA) += armada/
+obj-$(CONFIG_DRM_ATMEL_HLCDC)	+= atmel-hlcdc/
 obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-$(CONFIG_DRM_OMAP)	+= omapdrm/
diff --git a/drivers/gpu/drm/atmel-hlcdc/Kconfig b/drivers/gpu/drm/atmel-hlcdc/Kconfig
new file mode 100644
index 0000000..bc07315
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/Kconfig
@@ -0,0 +1,11 @@
+config DRM_ATMEL_HLCDC
+	tristate "DRM Support for ATMEL HLCDC Display Controller"
+	depends on DRM && OF && MFD_ATMEL_HLCDC && COMMON_CLK
+	select DRM_GEM_CMA_HELPER
+	select DRM_KMS_HELPER
+	select DRM_KMS_FB_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_PANEL
+	help
+	  Choose this option if you have an ATMEL SoC with an HLCDC display
+	  controller (i.e. at91sam9n12, at91sam9x5 family or sama5d3 family).
diff --git a/drivers/gpu/drm/atmel-hlcdc/Makefile b/drivers/gpu/drm/atmel-hlcdc/Makefile
new file mode 100644
index 0000000..10ae426
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/Makefile
@@ -0,0 +1,7 @@
+atmel-hlcdc-dc-y := atmel_hlcdc_crtc.o \
+		atmel_hlcdc_dc.o \
+		atmel_hlcdc_layer.o \
+		atmel_hlcdc_output.o \
+		atmel_hlcdc_plane.o
+
+obj-$(CONFIG_DRM_ATMEL_HLCDC)	+= atmel-hlcdc-dc.o
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
new file mode 100644
index 0000000..2186830
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2014 Free Electrons
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drmP.h>
+
+#include <video/videomode.h>
+
+#include "atmel_hlcdc_dc.h"
+
+/**
+ * Atmel HLCDC CRTC structure
+ *
+ * @base: base DRM CRTC structure
+ * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
+ * @event: pointer to the current page flip event
+ * @id: CRTC id (returned by drm_crtc_index)
+ * @dpms: DPMS mode
+ */
+struct atmel_hlcdc_crtc {
+	struct drm_crtc base;
+	struct atmel_hlcdc *hlcdc;
+	struct drm_pending_vblank_event *event;
+	int id;
+	int dpms;
+};
+
+static inline struct atmel_hlcdc_crtc *
+drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct atmel_hlcdc_crtc, base);
+}
+
+
+static void atmel_hlcdc_crtc_dpms(struct drm_crtc *c, int mode)
+{
+	struct drm_device *dev = c->dev;
+
+	if (mode != DRM_MODE_DPMS_ON)
+		mode = DRM_MODE_DPMS_OFF;
+
+	pm_runtime_get_sync(dev->dev);
+
+	if (mode == DRM_MODE_DPMS_ON)
+		pm_runtime_forbid(dev->dev);
+	else
+		pm_runtime_allow(dev->dev);
+
+	pm_runtime_put_sync(dev->dev);
+}
+
+static int atmel_hlcdc_crtc_mode_set(struct drm_crtc *c,
+				     struct drm_display_mode *mode,
+				     struct drm_display_mode *adjusted,
+				     int x, int y,
+				     struct drm_framebuffer *old_fb)
+{
+	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
+	struct regmap *regmap = crtc->hlcdc->regmap;
+	struct drm_plane *plane = c->primary;
+	struct drm_framebuffer *fb;
+	struct videomode vm;
+
+	vm.vfront_porch = mode->vsync_start - mode->vdisplay;
+	vm.vback_porch = mode->vtotal - mode->vsync_end;
+	vm.vsync_len = mode->vsync_end - mode->vsync_start;
+	vm.hfront_porch = mode->hsync_start - mode->hdisplay;
+	vm.hback_porch = mode->htotal - mode->hsync_end;
+	vm.hsync_len = mode->hsync_end - mode->hsync_start;
+
+	if (vm.hsync_len > 0x40 || vm.hsync_len < 1 ||
+	    vm.vsync_len > 0x40 || vm.vsync_len < 1 ||
+	    vm.vfront_porch > 0x40 || vm.vfront_porch < 1 ||
+	    vm.vback_porch > 0x40 || vm.vback_porch < 0 ||
+	    vm.hfront_porch > 0x200 || vm.hfront_porch < 1 ||
+	    vm.hback_porch > 0x200 || vm.hback_porch < 1 ||
+	    mode->hdisplay > 2048 || mode->hdisplay < 1 ||
+	    mode->vdisplay > 2048 || mode->vdisplay < 1)
+		return -EINVAL;
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(1),
+		     (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16));
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(2),
+		     (vm.vfront_porch - 1) | (vm.vback_porch << 16));
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(3),
+		     (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16));
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(4),
+		     (mode->hdisplay - 1) | ((mode->vdisplay - 1) << 16));
+
+	fb = plane->fb;
+	plane->fb = old_fb;
+
+	return plane->funcs->update_plane(plane, c, fb,
+					  0, 0,
+					  mode->hdisplay, mode->vdisplay,
+					  c->x << 16, c->y << 16,
+					  mode->hdisplay << 16,
+					  mode->vdisplay << 16);
+}
+
+static void atmel_hlcdc_crtc_prepare(struct drm_crtc *crtc)
+{
+	atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static void atmel_hlcdc_crtc_commit(struct drm_crtc *crtc)
+{
+	atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *crtc,
+					const struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+
+static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = {
+
+	.mode_fixup = atmel_hlcdc_crtc_mode_fixup,
+	.dpms = atmel_hlcdc_crtc_dpms,
+	.mode_set = atmel_hlcdc_crtc_mode_set,
+	.prepare = atmel_hlcdc_crtc_prepare,
+	.commit = atmel_hlcdc_crtc_commit,
+};
+
+static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c)
+{
+	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
+
+	drm_crtc_cleanup(c);
+	kfree(crtc);
+}
+
+void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *c,
+				       struct drm_file *file)
+{
+	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
+	struct drm_pending_vblank_event *event;
+	struct drm_device *dev = c->dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	event = crtc->event;
+	if (event && event->base.file_priv == file) {
+		event->base.destroy(&event->base);
+		drm_vblank_put(dev, crtc->id);
+		crtc->event = NULL;
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static void atmel_hlcdc_crtc_finish_page_flip(void *data)
+{
+	struct atmel_hlcdc_crtc *crtc = data;
+	struct drm_device *dev = crtc->base.dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	if (crtc->event) {
+		drm_send_vblank_event(dev, crtc->id, crtc->event);
+		drm_vblank_put(dev, crtc->id);
+		crtc->event = NULL;
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int atmel_hlcdc_crtc_page_flip(struct drm_crtc *c,
+				      struct drm_framebuffer *fb,
+				      struct drm_pending_vblank_event *event,
+				      uint32_t page_flip_flags)
+{
+	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
+	struct atmel_hlcdc_plane_update_req req;
+	struct drm_plane *plane = c->primary;
+	int ret;
+
+	if (crtc->event)
+		return -EBUSY;
+
+	memset(&req, 0, sizeof(req));
+	req.crtc_x = 0;
+	req.crtc_y = 0;
+	req.crtc_h = c->mode.crtc_vdisplay;
+	req.crtc_w = c->mode.crtc_hdisplay;
+	req.src_x = c->x << 16;
+	req.src_y = c->y << 16;
+	req.src_w = req.crtc_w << 16;
+	req.src_h = req.crtc_h << 16;
+	req.fb = fb;
+	req.crtc = c;
+	req.finished = atmel_hlcdc_crtc_finish_page_flip;
+	req.finished_data = crtc;
+
+	ret = atmel_hlcdc_plane_prepare_update_req(plane, &req);
+	if (ret)
+		return ret;
+
+	if (event) {
+		crtc->event = event;
+		drm_vblank_get(c->dev, crtc->id);
+	}
+
+	ret = atmel_hlcdc_plane_apply_update_req(plane, &req);
+	if (ret) {
+		crtc->event = NULL;
+		drm_vblank_put(c->dev, crtc->id);
+	} else {
+		plane->fb = fb;
+	}
+
+	return ret;
+}
+
+static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
+	.page_flip = atmel_hlcdc_crtc_page_flip,
+	.set_config = drm_crtc_helper_set_config,
+	.destroy = atmel_hlcdc_crtc_destroy,
+};
+
+int atmel_hlcdc_crtc_create(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct atmel_hlcdc_planes *planes = dc->planes;
+	struct atmel_hlcdc_crtc *crtc;
+	int ret;
+	int i;
+
+	crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
+	if (!crtc) {
+		dev_err(dev->dev, "allocation failed\n");
+		return -ENOMEM;
+	}
+
+	crtc->hlcdc = dc->hlcdc;
+
+	ret = drm_crtc_init_with_planes(dev, &crtc->base,
+				&planes->primary->base,
+				planes->cursor ? &planes->cursor->base : NULL,
+				&atmel_hlcdc_crtc_funcs);
+	if (ret < 0)
+		goto fail;
+
+	crtc->id = drm_crtc_index(&crtc->base);
+
+	if (planes->cursor)
+		planes->cursor->base.possible_crtcs = 1 << crtc->id;
+
+	for (i = 0; i < planes->noverlays; i++)
+		planes->overlays[i]->base.possible_crtcs = 1 << crtc->id;
+
+	drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
+
+	return 0;
+
+fail:
+	atmel_hlcdc_crtc_destroy(&crtc->base);
+	return ret;
+}
+
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
new file mode 100644
index 0000000..9581977
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#include "atmel_hlcdc_dc.h"
+
+#define ATMEL_HLCDC_LAYER_IRQS_OFFSET		8
+
+static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
+	{
+		.name = "base",
+		.formats = &atmel_hlcdc_plane_rgb_formats,
+		.regs_offset = 0x40,
+		.id = 0,
+		.type = ATMEL_HLCDC_BASE_LAYER,
+		.nconfigs = 7,
+		.layout = {
+			.xstride = { 2 },
+			.default_color = 3,
+			.general_config = 4,
+			.disc_pos = 5,
+			.disc_size = 6,
+		},
+	},
+	{
+		.name = "overlay1",
+		.formats = &atmel_hlcdc_plane_rgb_formats,
+		.regs_offset = 0x140,
+		.id = 1,
+		.type = ATMEL_HLCDC_OVERLAY_LAYER,
+		.nconfigs = 10,
+		.layout = {
+			.pos = 2,
+			.size = 3,
+			.xstride = { 4 },
+			.pstride = { 5 },
+			.default_color = 6,
+			.chroma_key = 7,
+			.chroma_key_mask = 8,
+			.general_config = 9,
+		},
+	},
+	{
+		.name = "overlay2",
+		.formats = &atmel_hlcdc_plane_rgb_formats,
+		.regs_offset = 0x240,
+		.id = 2,
+		.type = ATMEL_HLCDC_OVERLAY_LAYER,
+		.nconfigs = 10,
+		.layout = {
+			.pos = 2,
+			.size = 3,
+			.xstride = { 4 },
+			.pstride = { 5 },
+			.default_color = 6,
+			.chroma_key = 7,
+			.chroma_key_mask = 8,
+			.general_config = 9,
+		},
+	},
+	{
+		.name = "high-end-overlay",
+		.formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
+		.regs_offset = 0x340,
+		.id = 3,
+		.type = ATMEL_HLCDC_OVERLAY_LAYER,
+		.nconfigs = 42,
+		.layout = {
+			.pos = 2,
+			.size = 3,
+			.memsize = 4,
+			.xstride = { 5, 7 },
+			.pstride = { 6, 8 },
+			.default_color = 9,
+			.chroma_key = 10,
+			.chroma_key_mask = 11,
+			.general_config = 12,
+			.csc = 14,
+		},
+	},
+	{
+		.name = "cursor",
+		.formats = &atmel_hlcdc_plane_rgb_formats,
+		.regs_offset = 0x440,
+		.id = 4,
+		.type = ATMEL_HLCDC_CURSOR_LAYER,
+		.nconfigs = 10,
+		.max_width = 128,
+		.max_height = 128,
+		.layout = {
+			.pos = 2,
+			.size = 3,
+			.xstride = { 4 },
+			.pstride = { 5 },
+			.default_color = 6,
+			.chroma_key = 7,
+			.chroma_key_mask = 8,
+			.general_config = 9,
+		},
+	},
+};
+
+static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = {
+	.min_width = 0,
+	.min_height = 0,
+	.max_width = 2048,
+	.max_height = 2048,
+	.nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers),
+	.layers = atmel_hlcdc_sama5d3_layers,
+};
+
+static const struct of_device_id atmel_hlcdc_of_match[] = {
+	{
+		.compatible = "atmel,sama5d3-hlcdc",
+		.data = &atmel_hlcdc_dc_sama5d3,
+	},
+	{ /* sentinel */ },
+};
+
+static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
+{
+	struct drm_device *dev = data;
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	unsigned long status;
+	unsigned int imr, isr;
+	int bit;
+
+	regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr);
+	regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
+	status = imr & isr;
+	if (!status)
+		return IRQ_NONE;
+
+	bit = ATMEL_HLCDC_LAYER_IRQS_OFFSET;
+	for_each_set_bit_from(bit, &status, ATMEL_HLCDC_LAYER_IRQS_OFFSET +
+					    ATMEL_HLCDC_MAX_LAYERS) {
+		int layerid = bit - ATMEL_HLCDC_LAYER_IRQS_OFFSET;
+		struct atmel_hlcdc_layer *layer = dc->layers[layerid];
+
+		if (layer)
+			atmel_hlcdc_layer_irq(layer);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev,
+		struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	return drm_fb_cma_create(dev, file_priv, mode_cmd);
+}
+
+static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+
+	if (dc->fbdev) {
+		drm_fbdev_cma_hotplug_event(dc->fbdev);
+	} else {
+		dc->fbdev = drm_fbdev_cma_init(dev, 24,
+				dev->mode_config.num_crtc,
+				dev->mode_config.num_connector);
+		if (IS_ERR(dc->fbdev))
+			dc->fbdev = NULL;
+	}
+}
+
+static const struct drm_mode_config_funcs mode_config_funcs = {
+	.fb_create = atmel_hlcdc_fb_create,
+	.output_poll_changed = atmel_hlcdc_fb_output_poll_changed,
+};
+
+static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct atmel_hlcdc_planes *planes;
+	int ret;
+	int i;
+
+	drm_mode_config_init(dev);
+
+	ret = atmel_hlcdc_create_outputs(dev);
+	if (ret) {
+		dev_err(dev->dev, "failed to create panel: %d\n", ret);
+		return ret;
+	}
+
+	planes = atmel_hlcdc_create_planes(dev);
+	if (IS_ERR(planes)) {
+		dev_err(dev->dev, "failed to create planes\n");
+		return PTR_ERR(planes);
+	}
+
+	dc->planes = planes;
+
+	dc->layers[planes->primary->layer.desc->id] =
+						&planes->primary->layer;
+
+	if (planes->cursor)
+		dc->layers[planes->cursor->layer.desc->id] =
+							&planes->cursor->layer;
+
+	for (i = 0; i < planes->noverlays; i++)
+		dc->layers[planes->overlays[i]->layer.desc->id] =
+						&planes->overlays[i]->layer;
+
+	ret = atmel_hlcdc_crtc_create(dev);
+	if (ret) {
+		dev_err(dev->dev, "failed to create crtc\n");
+		return ret;
+	}
+
+	dev->mode_config.min_width = dc->desc->min_width;
+	dev->mode_config.min_height = dc->desc->min_height;
+	dev->mode_config.max_width = dc->desc->max_width;
+	dev->mode_config.max_height = dc->desc->max_height;
+	dev->mode_config.funcs = &mode_config_funcs;
+
+	return 0;
+}
+
+static int atmel_hlcdc_dc_load(struct drm_device *dev, unsigned long flags)
+{
+	struct platform_device *pdev = dev->platformdev;
+	const struct of_device_id *match;
+	struct atmel_hlcdc_dc *dc;
+	int irq;
+	int ret;
+
+	match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node);
+	if (!match) {
+		dev_err(&pdev->dev, "invalid compatible string\n");
+		return -ENODEV;
+	}
+
+	if (!match->data) {
+		dev_err(&pdev->dev, "invalid hlcdc description\n");
+		return -EINVAL;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL);
+	if (!dc) {
+		dev_err(dev->dev, "failed to allocate private data\n");
+		return -ENOMEM;
+	}
+
+	dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0);
+	if (!dc->wq)
+		return -ENOMEM;
+
+	dc->desc = match->data;
+	dc->hlcdc = dev_get_drvdata(dev->dev->parent);
+	dev->dev_private = dc;
+
+	ret = clk_prepare_enable(dc->hlcdc->periph_clk);
+	if (ret) {
+		dev_err(dev->dev, "failed to enable periph_clk\n");
+		goto err_destroy_wq;
+	}
+
+	pm_runtime_enable(dev->dev);
+
+	pm_runtime_put_sync(dev->dev);
+
+	ret = atmel_hlcdc_dc_modeset_init(dev);
+	if (ret < 0) {
+		dev_err(dev->dev, "failed to initialize mode setting\n");
+		goto err_periph_clk_disable;
+	}
+
+	ret = drm_vblank_init(dev, 1);
+	if (ret < 0) {
+		dev_err(dev->dev, "failed to initialize vblank\n");
+		goto err_periph_clk_disable;
+	}
+
+	pm_runtime_get_sync(dev->dev);
+	ret = drm_irq_install(dev, irq);
+	pm_runtime_put_sync(dev->dev);
+	if (ret < 0) {
+		dev_err(dev->dev, "failed to install IRQ handler\n");
+		goto err_periph_clk_disable;
+	}
+
+	platform_set_drvdata(pdev, dev);
+
+	drm_kms_helper_poll_init(dev);
+
+	/* force connectors detection */
+	drm_helper_hpd_irq_event(dev);
+
+	return 0;
+
+err_periph_clk_disable:
+	clk_disable_unprepare(dc->hlcdc->periph_clk);
+
+err_destroy_wq:
+	destroy_workqueue(dc->wq);
+
+	return ret;
+}
+
+static int atmel_hlcdc_dc_unload(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+
+	drm_kms_helper_poll_fini(dev);
+	drm_mode_config_cleanup(dev);
+	drm_vblank_cleanup(dev);
+
+	pm_runtime_get_sync(dev->dev);
+	drm_irq_uninstall(dev);
+	pm_runtime_put_sync(dev->dev);
+
+	dev->dev_private = NULL;
+
+	pm_runtime_disable(dev->dev);
+	clk_disable_unprepare(dc->hlcdc->periph_clk);
+
+	flush_workqueue(dc->wq);
+	destroy_workqueue(dc->wq);
+
+	return 0;
+}
+
+static void atmel_hlcdc_dc_preclose(struct drm_device *dev,
+				    struct drm_file *file)
+{
+	struct drm_crtc *crtc;
+
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+		atmel_hlcdc_crtc_cancel_page_flip(crtc, file);
+}
+
+static void atmel_hlcdc_dc_lastclose(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+
+	drm_fbdev_cma_restore_mode(dc->fbdev);
+}
+
+static void atmel_hlcdc_dc_irq_preinstall(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	unsigned int isr;
+
+	regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff);
+	regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
+}
+
+static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	int i;
+
+	/* Enable interrupts on activated layers */
+	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
+		if (dc->layers[i])
+			regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER,
+				     BIT(i + 8));
+	}
+
+	return 0;
+}
+
+static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev)
+{
+
+}
+
+static int atmel_hlcdc_dc_enable_vblank(struct drm_device *dev, int crtc)
+{
+	return 0;
+}
+
+static void atmel_hlcdc_dc_disable_vblank(struct drm_device *dev, int crtc)
+{
+}
+
+static const struct file_operations fops = {
+	.owner              = THIS_MODULE,
+	.open               = drm_open,
+	.release            = drm_release,
+	.unlocked_ioctl     = drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl       = drm_compat_ioctl,
+#endif
+	.poll               = drm_poll,
+	.read               = drm_read,
+	.llseek             = no_llseek,
+	.mmap               = drm_gem_cma_mmap,
+};
+
+static struct drm_driver atmel_hlcdc_dc_driver = {
+	.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET,
+	.load = atmel_hlcdc_dc_load,
+	.unload = atmel_hlcdc_dc_unload,
+	.preclose = atmel_hlcdc_dc_preclose,
+	.lastclose = atmel_hlcdc_dc_lastclose,
+	.irq_handler = atmel_hlcdc_dc_irq_handler,
+	.irq_preinstall = atmel_hlcdc_dc_irq_preinstall,
+	.irq_postinstall = atmel_hlcdc_dc_irq_postinstall,
+	.irq_uninstall = atmel_hlcdc_dc_irq_uninstall,
+	.get_vblank_counter = drm_vblank_count,
+	.enable_vblank = atmel_hlcdc_dc_enable_vblank,
+	.disable_vblank = atmel_hlcdc_dc_disable_vblank,
+	.gem_free_object = drm_gem_cma_free_object,
+	.gem_vm_ops = &drm_gem_cma_vm_ops,
+	.dumb_create = drm_gem_cma_dumb_create,
+	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
+	.dumb_destroy = drm_gem_dumb_destroy,
+	.fops = &fops,
+	.name = "atmel-hlcdc",
+	.desc = "Atmel HLCD Controller DRM",
+	.date = "20141504",
+	.major = 1,
+	.minor = 0,
+};
+
+static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+	if (ret)
+		return ret;
+
+	ret = drm_platform_init(&atmel_hlcdc_dc_driver, pdev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
+{
+	drm_put_dev(platform_get_drvdata(pdev));
+
+	return 0;
+}
+
+static const struct of_device_id atmel_hlcdc_dc_of_match[] = {
+	{ .compatible = "atmel,hlcdc-display-controller" },
+	{ },
+};
+
+static struct platform_driver atmel_hlcdc_dc_platform_driver = {
+	.probe	= atmel_hlcdc_dc_drm_probe,
+	.remove	= atmel_hlcdc_dc_drm_remove,
+	.driver	= {
+		.name	= "atmel-hlcdc-display-controller",
+		.of_match_table = atmel_hlcdc_dc_of_match,
+	},
+};
+module_platform_driver(atmel_hlcdc_dc_platform_driver);
+
+MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>");
+MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atmel-hlcdc-dc");
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
new file mode 100644
index 0000000..a67df13
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DRM_ATMEL_HLCDC_H
+#define DRM_ATMEL_HLCDC_H
+
+#include <linux/clk.h>
+#include <linux/irqdomain.h>
+#include <linux/pwm.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drmP.h>
+
+#include "atmel_hlcdc_layer.h"
+
+#define ATMEL_HLCDC_MAX_LAYERS		5
+
+/**
+ * Atmel HLCDC Display Controller description structure.
+ *
+ * This structure describe the HLCDC IP capabilities and depends on the
+ * HLCDC IP version (or Atmel SoC family).
+ *
+ * @min_width: minimum width supported by the Display Controller
+ * @min_height: minimum height supported by the Display Controller
+ * @max_width: maximum width supported by the Display Controller
+ * @max_height: maximum height supported by the Display Controller
+ * @layer: a layer description table describing available layers
+ * @nlayers: layer description table size
+ */
+struct atmel_hlcdc_dc_desc {
+	int min_width;
+	int min_height;
+	int max_width;
+	int max_height;
+	const struct atmel_hlcdc_layer_desc *layers;
+	int nlayers;
+};
+
+/**
+ * Atmel HLCDC Plane properties.
+ *
+ * This structure stores plane property definitions.
+ *
+ * @alpha: alpha blending (or transparency) property
+ * @csc: YUV to RGB conversion factors property
+ */
+struct atmel_hlcdc_plane_properties {
+	struct drm_property *alpha;
+	struct drm_property *rotation;
+};
+
+/**
+ * Atmel HLCDC plane rotation enum
+ *
+ * TODO: export DRM_ROTATE_XX macros defined by omap driver and use them
+ * instead of defining this enum.
+ */
+enum atmel_hlcdc_plane_rotation {
+	ATMEL_HLCDC_PLANE_NO_ROTATION,
+	ATMEL_HLCDC_PLANE_90DEG_ROTATION,
+	ATMEL_HLCDC_PLANE_180DEG_ROTATION,
+	ATMEL_HLCDC_PLANE_270DEG_ROTATION,
+};
+
+/**
+ * Atmel HLCDC Plane.
+ *
+ * @base: base DRM plane structure
+ * @layer: HLCDC layer structure
+ * @properties: pointer to the property definitions structure
+ * @alpha: current alpha blending (or transparency) status
+ */
+struct atmel_hlcdc_plane {
+	struct drm_plane base;
+	struct atmel_hlcdc_layer layer;
+	struct atmel_hlcdc_plane_properties *properties;
+	enum atmel_hlcdc_plane_rotation rotation;
+};
+
+static inline struct atmel_hlcdc_plane *
+drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p)
+{
+	return container_of(p, struct atmel_hlcdc_plane, base);
+}
+
+static inline struct atmel_hlcdc_plane *
+atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *l)
+{
+	return container_of(l, struct atmel_hlcdc_plane, layer);
+}
+
+/**
+ * Atmel HLCDC Plane update request structure.
+ *
+ * @crtc_x: x position of the plane relative to the CRTC
+ * @crtc_y: y position of the plane relative to the CRTC
+ * @crtc_w: visible width of the plane
+ * @crtc_h: visible height of the plane
+ * @src_x: x buffer position
+ * @src_y: y buffer position
+ * @src_w: buffer width
+ * @src_h: buffer height
+ * @pixel_format: pixel format
+ * @gems: GEM object object containing image buffers
+ * @offsets: offsets to apply to the GEM buffers
+ * @pitches: line size in bytes
+ * @crtc: crtc to display on
+ * @finished: finished callback
+ * @finished_data: data passed to the finished callback
+ * @bpp: bytes per pixel deduced from pixel_format
+ * @xstride: value to add to the pixel pointer between each line
+ * @pstride: value to add to the pixel pointer between each pixel
+ * @nplanes: number of planes (deduced from pixel_format)
+ */
+struct atmel_hlcdc_plane_update_req {
+	int crtc_x;
+	int crtc_y;
+	unsigned int crtc_w;
+	unsigned int crtc_h;
+	uint32_t src_x;
+	uint32_t src_y;
+	uint32_t src_w;
+	uint32_t src_h;
+	struct drm_framebuffer *fb;
+	struct drm_crtc *crtc;
+	void (*finished)(void *data);
+	void *finished_data;
+
+	/* These fields are private and should not be touched */
+	int bpp[ATMEL_HLCDC_MAX_PLANES];
+	unsigned int offsets[ATMEL_HLCDC_MAX_PLANES];
+	int xstride[ATMEL_HLCDC_MAX_PLANES];
+	int pstride[ATMEL_HLCDC_MAX_PLANES];
+	int nplanes;
+};
+
+/**
+ * Atmel HLCDC Planes.
+ *
+ * This structure stores the instantiated HLCDC Planes and can be accessed by
+ * the HLCDC Display Controller or the HLCDC CRTC.
+ *
+ * @primary: primary plane
+ * @cursor: hardware cursor plane
+ * @overlays: overlay plane table
+ * @noverlays: number of overlay planes
+ */
+struct atmel_hlcdc_planes {
+	struct atmel_hlcdc_plane *primary;
+	struct atmel_hlcdc_plane *cursor;
+	struct atmel_hlcdc_plane **overlays;
+	int noverlays;
+};
+
+/**
+ * Atmel HLCDC Display Controller.
+ *
+ * @desc: HLCDC Display Controller description
+ * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
+ * @fbdev: framebuffer device attached to the Display Controller
+ * @planes: instantiated planes
+ * @layers: active HLCDC layer
+ * @wq: display controller workqueue
+ */
+struct atmel_hlcdc_dc {
+	const struct atmel_hlcdc_dc_desc *desc;
+	struct atmel_hlcdc *hlcdc;
+	struct drm_fbdev_cma *fbdev;
+	struct atmel_hlcdc_planes *planes;
+	struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS];
+	struct workqueue_struct *wq;
+};
+
+extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats;
+extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats;
+
+struct atmel_hlcdc_planes *
+atmel_hlcdc_create_planes(struct drm_device *dev);
+
+int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req);
+
+int atmel_hlcdc_plane_apply_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req);
+
+void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *crtc,
+				       struct drm_file *file);
+
+int atmel_hlcdc_crtc_create(struct drm_device *dev);
+
+int atmel_hlcdc_create_outputs(struct drm_device *dev);
+
+struct atmel_hlcdc_pwm_chip *atmel_hlcdc_pwm_create(struct drm_device *dev,
+						    struct clk *slow_clk,
+						    struct clk *sys_clk,
+						    void __iomem *regs);
+
+int atmel_hlcdc_pwm_destroy(struct drm_device *dev,
+			    struct atmel_hlcdc_pwm_chip *chip);
+
+#endif /* DRM_ATMEL_HLCDC_H */
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
new file mode 100644
index 0000000..d31c2e4
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
@@ -0,0 +1,635 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+
+#include "atmel_hlcdc_dc.h"
+
+static void
+atmel_hlcdc_layer_fb_flip_release(struct drm_flip_work *work, void *val)
+{
+	struct atmel_hlcdc_layer_fb_flip *flip = val;
+
+	if (flip->fb)
+		drm_framebuffer_unreference(flip->fb);
+	kfree(flip);
+}
+
+static void
+atmel_hlcdc_layer_fb_flip_destroy(struct atmel_hlcdc_layer_fb_flip *flip)
+{
+	if (flip->fb)
+		drm_framebuffer_unreference(flip->fb);
+	kfree(flip->task);
+	kfree(flip);
+}
+
+static void
+atmel_hlcdc_layer_fb_flip_release_queue(struct atmel_hlcdc_layer *layer,
+					struct atmel_hlcdc_layer_fb_flip *flip)
+{
+	int i;
+
+	if (!flip)
+		return;
+
+	for (i = 0; i < layer->max_planes; i++) {
+		if (!flip->dscrs[i])
+			break;
+
+		flip->dscrs[i]->status = 0;
+		flip->dscrs[i] = NULL;
+	}
+
+	drm_flip_work_queue_task(&layer->gc, flip->task);
+	drm_flip_work_commit(&layer->gc, layer->wq);
+}
+
+static void atmel_hlcdc_layer_update_reset(struct atmel_hlcdc_layer *layer,
+					   int id)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_update_slot *slot;
+
+	if (id < 0 || id > 1)
+		return;
+
+	slot = &upd->slots[id];
+	bitmap_clear(slot->updated_configs, 0, layer->desc->nconfigs);
+	memset(slot->configs, 0,
+	       sizeof(*slot->configs) * layer->desc->nconfigs);
+
+	if (slot->fb_flip) {
+		atmel_hlcdc_layer_fb_flip_release_queue(layer, slot->fb_flip);
+		slot->fb_flip = NULL;
+	}
+}
+
+static void atmel_hlcdc_layer_update_apply(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct regmap *regmap = layer->hlcdc->regmap;
+	struct atmel_hlcdc_layer_update_slot *slot;
+	struct atmel_hlcdc_layer_fb_flip *fb_flip;
+	struct atmel_hlcdc_dma_channel_dscr *dscr;
+	unsigned int cfg;
+	u32 action = 0;
+	int i = 0;
+
+	if (upd->pending < 0 || upd->pending > 1 ||
+	    dma->status == ATMEL_HLCDC_LAYER_DISABLING)
+		return;
+
+	slot = &upd->slots[upd->pending];
+
+	for_each_set_bit(cfg, slot->updated_configs, layer->desc->nconfigs) {
+		regmap_write(regmap,
+			     desc->regs_offset +
+			     ATMEL_HLCDC_LAYER_CFG(layer, cfg),
+			     slot->configs[cfg]);
+		action |= ATMEL_HLCDC_LAYER_UPDATE;
+	}
+
+	fb_flip = slot->fb_flip;
+
+	if (!fb_flip->fb)
+		goto apply;
+
+	if (dma->status == ATMEL_HLCDC_LAYER_DISABLED) {
+		for (i = 0; i < fb_flip->ngems; i++) {
+			dscr =  fb_flip->dscrs[i];
+			dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
+				     ATMEL_HLCDC_LAYER_DMA_IRQ |
+				     ATMEL_HLCDC_LAYER_ADD_IRQ |
+				     ATMEL_HLCDC_LAYER_DONE_IRQ;
+
+			regmap_write(regmap,
+				     desc->regs_offset +
+				     ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
+				     dscr->addr);
+			regmap_write(regmap,
+				     desc->regs_offset +
+				     ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
+				     dscr->ctrl);
+			regmap_write(regmap,
+				     desc->regs_offset +
+				     ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
+				     dscr->next);
+		}
+
+		action |= ATMEL_HLCDC_LAYER_DMA_CHAN;
+		dma->status = ATMEL_HLCDC_LAYER_ENABLED;
+	} else {
+		for (i = 0; i < fb_flip->ngems; i++) {
+			dscr =  fb_flip->dscrs[i];
+			dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
+				     ATMEL_HLCDC_LAYER_DMA_IRQ |
+				     ATMEL_HLCDC_LAYER_DSCR_IRQ |
+				     ATMEL_HLCDC_LAYER_DONE_IRQ;
+
+			regmap_write(regmap,
+				     desc->regs_offset +
+				     ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
+				     dscr->next);
+		}
+
+		action |= ATMEL_HLCDC_LAYER_A2Q;
+	}
+
+	/* Release unneeded descriptors */
+	for (i = fb_flip->ngems; i < layer->max_planes; i++) {
+		fb_flip->dscrs[i]->status = 0;
+		fb_flip->dscrs[i] = NULL;
+	}
+
+	dma->queue = fb_flip;
+	slot->fb_flip = NULL;
+
+apply:
+	if (action)
+		regmap_write(regmap,
+			     desc->regs_offset + ATMEL_HLCDC_LAYER_CHER,
+			     action);
+
+	atmel_hlcdc_layer_update_reset(layer, upd->pending);
+
+	upd->pending = -1;
+}
+
+void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
+	struct regmap *regmap = layer->hlcdc->regmap;
+	struct atmel_hlcdc_layer_fb_flip *flip;
+	unsigned long flags;
+	unsigned int isr, imr;
+	unsigned int status;
+	unsigned int plane_status;
+	u32 flip_status;
+
+	int i;
+
+	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IMR, &imr);
+	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
+	status = imr & isr;
+	if (!status)
+		return;
+
+	spin_lock_irqsave(&layer->lock, flags);
+
+	flip = dma->queue ? dma->queue : dma->cur;
+
+	if (!flip) {
+		spin_unlock_irqrestore(&layer->lock, flags);
+		return;
+	}
+
+	flip_status = 0;
+	for (i = 0; i < flip->ngems; i++) {
+		plane_status = (status >> (8 * i));
+
+		if (plane_status &
+		    (ATMEL_HLCDC_LAYER_ADD_IRQ |
+		     ATMEL_HLCDC_LAYER_DSCR_IRQ) &
+		    ~flip->dscrs[i]->ctrl) {
+			flip->dscrs[i]->status |=
+					ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED;
+			flip->dscrs[i]->ctrl |=
+					ATMEL_HLCDC_LAYER_ADD_IRQ |
+					ATMEL_HLCDC_LAYER_DSCR_IRQ;
+		}
+
+		if (plane_status &
+		    ATMEL_HLCDC_LAYER_DONE_IRQ &
+		    ~flip->dscrs[i]->ctrl) {
+			flip->dscrs[i]->status |=
+					ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
+			flip->dscrs[i]->ctrl |=
+					ATMEL_HLCDC_LAYER_DONE_IRQ;
+		}
+
+		flip_status |= flip->dscrs[i]->status;
+	}
+
+	/* Get changed bits */
+	flip_status ^= flip->status;
+
+	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED) {
+		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
+		dma->cur = dma->queue;
+		dma->queue = NULL;
+	}
+
+	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE) {
+		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
+		dma->cur = NULL;
+	}
+
+	flip->status |= flip_status;
+
+	if (!dma->queue) {
+		atmel_hlcdc_layer_update_apply(layer);
+
+		if (!dma->cur)
+			dma->status = ATMEL_HLCDC_LAYER_DISABLED;
+	}
+
+	spin_unlock_irqrestore(&layer->lock, flags);
+}
+
+int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_fb_flip *flip;
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&layer->lock, flags);
+
+	/*
+	 * First disable DMA transfers. If a DMA transfer has been queued
+	 * we're stopping this one instead of the current one because we
+	 * can't know for sure if queued transfer has been started or not.
+	 */
+	flip = dma->queue ? dma->queue : dma->cur;
+	if (flip) {
+		for (i = 0; i < flip->ngems; i++)
+			flip->dscrs[i]->ctrl &= ~(ATMEL_HLCDC_LAYER_DFETCH |
+						  ATMEL_HLCDC_LAYER_DONE_IRQ);
+
+		dma->status = ATMEL_HLCDC_LAYER_DISABLING;
+	}
+
+	/*
+	 * Then discard the pending update request (if any) to prevent
+	 * DMA irq handler from restarting the DMA channel after it has
+	 * been disabled.
+	 */
+	if (upd->pending >= 0) {
+		atmel_hlcdc_layer_update_reset(layer, upd->pending);
+		upd->pending = -1;
+	}
+
+	spin_unlock_irqrestore(&layer->lock, flags);
+
+	return 0;
+}
+
+int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct regmap *regmap = layer->hlcdc->regmap;
+	struct atmel_hlcdc_layer_fb_flip *fb_flip;
+	struct atmel_hlcdc_layer_update_slot *slot;
+	unsigned long flags;
+	int i, j = 0;
+
+	fb_flip = kzalloc(sizeof(*fb_flip), GFP_KERNEL);
+	if (!fb_flip)
+		return -ENOMEM;
+
+	fb_flip->task = drm_flip_work_allocate_task(fb_flip, GFP_KERNEL);
+	if (!fb_flip->task) {
+		kfree(fb_flip);
+		return -ENOMEM;
+	}
+
+	spin_lock_irqsave(&layer->lock, flags);
+
+	upd->next = upd->pending ? 0 : 1;
+
+	slot = &upd->slots[upd->next];
+
+	for (i = 0; i < layer->max_planes * 4; i++) {
+		if (!dma->dscrs[i].status) {
+			fb_flip->dscrs[j++] = &dma->dscrs[i];
+			dma->dscrs[i].status =
+				ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED;
+			if (j == layer->max_planes)
+				break;
+		}
+	}
+
+	if (j < layer->max_planes) {
+		for (i = 0; i < j; i++)
+			fb_flip->dscrs[i]->status = 0;
+	}
+
+	if (j < layer->max_planes) {
+		spin_unlock_irqrestore(&layer->lock, flags);
+		atmel_hlcdc_layer_fb_flip_destroy(fb_flip);
+		return -EBUSY;
+	}
+
+	slot->fb_flip = fb_flip;
+
+	if (upd->pending >= 0) {
+		memcpy(slot->configs,
+		       upd->slots[upd->pending].configs,
+		       layer->desc->nconfigs * sizeof(u32));
+		memcpy(slot->updated_configs,
+		       upd->slots[upd->pending].updated_configs,
+		       DIV_ROUND_UP(layer->desc->nconfigs,
+				    BITS_PER_BYTE * sizeof(unsigned long)) *
+		       sizeof(unsigned long));
+		slot->fb_flip->fb = upd->slots[upd->pending].fb_flip->fb;
+		if (upd->slots[upd->pending].fb_flip->fb) {
+			slot->fb_flip->fb =
+				upd->slots[upd->pending].fb_flip->fb;
+			slot->fb_flip->ngems =
+				upd->slots[upd->pending].fb_flip->ngems;
+			drm_framebuffer_reference(slot->fb_flip->fb);
+		}
+	} else {
+		regmap_bulk_read(regmap,
+				 layer->desc->regs_offset +
+				 ATMEL_HLCDC_LAYER_CFG(layer, 0),
+				 upd->slots[upd->next].configs,
+				 layer->desc->nconfigs);
+	}
+
+	spin_unlock_irqrestore(&layer->lock, flags);
+
+	return 0;
+}
+
+void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+
+	atmel_hlcdc_layer_update_reset(layer, upd->next);
+	upd->next = -1;
+}
+
+void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
+				     struct drm_framebuffer *fb,
+				     unsigned int *offsets)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_fb_flip *fb_flip;
+	struct atmel_hlcdc_layer_update_slot *slot;
+	struct atmel_hlcdc_dma_channel_dscr *dscr;
+	struct drm_framebuffer *old_fb;
+	int nplanes = 0;
+	int i;
+
+	if (upd->next < 0 || upd->next > 1)
+		return;
+
+	if (fb)
+		nplanes = drm_format_num_planes(fb->pixel_format);
+
+	if (nplanes > layer->max_planes)
+		return;
+
+	slot = &upd->slots[upd->next];
+
+	fb_flip = slot->fb_flip;
+	old_fb = slot->fb_flip->fb;
+
+	for (i = 0; i < nplanes; i++) {
+		struct drm_gem_cma_object *gem;
+
+		dscr = slot->fb_flip->dscrs[i];
+		gem = drm_fb_cma_get_gem_obj(fb, i);
+		dscr->addr = gem->paddr + offsets[i];
+	}
+
+	fb_flip->ngems = nplanes;
+	fb_flip->fb = fb;
+
+	if (fb)
+		drm_framebuffer_reference(fb);
+
+	if (old_fb)
+		drm_framebuffer_unreference(old_fb);
+}
+
+void atmel_hlcdc_layer_update_set_finished(struct atmel_hlcdc_layer *layer,
+					   void (*finished)(void *data),
+					   void *finished_data)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_update_slot *slot;
+
+	if (upd->next < 0 || upd->next > 1)
+		return;
+
+	slot = &upd->slots[upd->next];
+
+	slot->fb_flip->finished = finished;
+	slot->fb_flip->finished_data = finished_data;
+}
+
+void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
+				  u32 mask, u32 val)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_update_slot *slot;
+
+	if (upd->next < 0 || upd->next > 1)
+		return;
+
+	if (cfg >= layer->desc->nconfigs)
+		return;
+
+	slot = &upd->slots[upd->next];
+	slot->configs[cfg] &= ~mask;
+	slot->configs[cfg] |= (val & mask);
+	set_bit(cfg, slot->updated_configs);
+}
+
+void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_update_slot *slot;
+	unsigned long flags;
+
+	if (upd->next < 0  || upd->next > 1)
+		return;
+
+	slot = &upd->slots[upd->next];
+
+	spin_lock_irqsave(&layer->lock, flags);
+
+	/*
+	 * Release pending update request and replace it by the new one.
+	 */
+	if (upd->pending >= 0)
+		atmel_hlcdc_layer_update_reset(layer, upd->pending);
+
+	upd->pending = upd->next;
+	upd->next = -1;
+
+	if (!dma->queue)
+		atmel_hlcdc_layer_update_apply(layer);
+
+	spin_unlock_irqrestore(&layer->lock, flags);
+
+
+	upd->next = -1;
+}
+
+static int atmel_hlcdc_layer_dma_init(struct drm_device *dev,
+				      struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	dma_addr_t dma_addr;
+	int i;
+
+	dma->dscrs = dma_alloc_coherent(dev->dev,
+					layer->max_planes * 4 *
+					sizeof(*dma->dscrs),
+					&dma_addr, GFP_KERNEL);
+	if (!dma->dscrs)
+		return -ENOMEM;
+
+	for (i = 0; i < layer->max_planes * 4; i++) {
+		struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i];
+
+		dscr->next = dma_addr + (i * sizeof(*dscr));
+	}
+
+	return 0;
+}
+
+static void atmel_hlcdc_layer_dma_cleanup(struct drm_device *dev,
+					  struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	int i;
+
+	for (i = 0; i < layer->max_planes * 4; i++) {
+		struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i];
+
+		dscr->status = 0;
+	}
+
+	dma_free_coherent(dev->dev, layer->max_planes * 4 *
+			  sizeof(*dma->dscrs), dma->dscrs,
+			  dma->dscrs[0].next);
+}
+
+static int atmel_hlcdc_layer_update_init(struct drm_device *dev,
+				struct atmel_hlcdc_layer *layer,
+				const struct atmel_hlcdc_layer_desc *desc)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	int updated_size;
+	void *buffer;
+	int i;
+
+	updated_size = DIV_ROUND_UP(desc->nconfigs,
+				    BITS_PER_BYTE *
+				    sizeof(unsigned long));
+
+	buffer = devm_kzalloc(dev->dev,
+			      ((desc->nconfigs * sizeof(u32)) +
+				(updated_size * sizeof(unsigned long))) * 2,
+			      GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	for (i = 0; i < 2; i++) {
+		upd->slots[i].updated_configs = buffer;
+		buffer += updated_size * sizeof(unsigned long);
+		upd->slots[i].configs = buffer;
+		buffer += desc->nconfigs * sizeof(u32);
+	}
+
+	upd->pending = -1;
+	upd->next = -1;
+
+	return 0;
+}
+
+int atmel_hlcdc_layer_init(struct drm_device *dev,
+			   struct atmel_hlcdc_layer *layer,
+			   const struct atmel_hlcdc_layer_desc *desc)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct regmap *regmap = dc->hlcdc->regmap;
+	unsigned int tmp;
+	int ret;
+	int i;
+
+	layer->hlcdc = dc->hlcdc;
+	layer->wq = dc->wq;
+	layer->desc = desc;
+
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
+		     ATMEL_HLCDC_LAYER_RST);
+	for (i = 0; i < desc->formats->nformats; i++) {
+		int nplanes = drm_format_num_planes(desc->formats->formats[i]);
+
+		if (nplanes > layer->max_planes)
+			layer->max_planes = nplanes;
+	}
+
+	spin_lock_init(&layer->lock);
+	drm_flip_work_init(&layer->gc, desc->name,
+			   atmel_hlcdc_layer_fb_flip_release);
+	ret = atmel_hlcdc_layer_dma_init(dev, layer);
+	if (ret)
+		return ret;
+
+	ret = atmel_hlcdc_layer_update_init(dev, layer, desc);
+	if (ret)
+		return ret;
+
+	/* Flush Status Register */
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR,
+		     0xffffffff);
+	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR,
+		    &tmp);
+
+	tmp = 0;
+	for (i = 0; i < layer->max_planes; i++)
+		tmp |= (ATMEL_HLCDC_LAYER_DMA_IRQ |
+			ATMEL_HLCDC_LAYER_DSCR_IRQ |
+			ATMEL_HLCDC_LAYER_ADD_IRQ |
+			ATMEL_HLCDC_LAYER_DONE_IRQ |
+			ATMEL_HLCDC_LAYER_OVR_IRQ) << (8 * i);
+
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IER, tmp);
+
+	return 0;
+}
+
+void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
+			       struct atmel_hlcdc_layer *layer)
+{
+	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
+	struct regmap *regmap = layer->hlcdc->regmap;
+
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR,
+		     0xffffffff);
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
+		     ATMEL_HLCDC_LAYER_RST);
+
+	atmel_hlcdc_layer_dma_cleanup(dev, layer);
+	drm_flip_work_cleanup(&layer->gc);
+}
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
new file mode 100644
index 0000000..885b57a
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DRM_ATMEL_HLCDC_LAYER_H
+#define DRM_ATMEL_HLCDC_LAYER_H
+
+#include <linux/mfd/atmel-hlcdc.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_flip_work.h>
+#include <drm/drmP.h>
+
+#define ATMEL_HLCDC_LAYER_CHER			0x0
+#define ATMEL_HLCDC_LAYER_CHDR			0x4
+#define ATMEL_HLCDC_LAYER_CHSR			0x8
+#define ATMEL_HLCDC_LAYER_DMA_CHAN		BIT(0)
+#define ATMEL_HLCDC_LAYER_UPDATE		BIT(1)
+#define ATMEL_HLCDC_LAYER_A2Q			BIT(2)
+#define ATMEL_HLCDC_LAYER_RST			BIT(8)
+
+#define ATMEL_HLCDC_LAYER_IER			0xc
+#define ATMEL_HLCDC_LAYER_IDR			0x10
+#define ATMEL_HLCDC_LAYER_IMR			0x14
+#define ATMEL_HLCDC_LAYER_ISR			0x18
+#define ATMEL_HLCDC_LAYER_DFETCH		BIT(0)
+#define ATMEL_HLCDC_LAYER_LFETCH		BIT(1)
+#define ATMEL_HLCDC_LAYER_DMA_IRQ		BIT(2)
+#define ATMEL_HLCDC_LAYER_DSCR_IRQ		BIT(3)
+#define ATMEL_HLCDC_LAYER_ADD_IRQ		BIT(4)
+#define ATMEL_HLCDC_LAYER_DONE_IRQ		BIT(5)
+#define ATMEL_HLCDC_LAYER_OVR_IRQ		BIT(6)
+
+#define ATMEL_HLCDC_LAYER_PLANE_HEAD(n)		(((n) * 0x10) + 0x1c)
+#define ATMEL_HLCDC_LAYER_PLANE_ADDR(n)		(((n) * 0x10) + 0x20)
+#define ATMEL_HLCDC_LAYER_PLANE_CTRL(n)		(((n) * 0x10) + 0x24)
+#define ATMEL_HLCDC_LAYER_PLANE_NEXT(n)		(((n) * 0x10) + 0x28)
+#define ATMEL_HLCDC_LAYER_CFG(p, c)		(((c) * 4) + ((p)->max_planes * 0x10) + 0x1c)
+
+#define ATMEL_HLCDC_LAYER_DMA_CFG_ID		0
+#define ATMEL_HLCDC_LAYER_DMA_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_DMA_CFG_ID)
+
+#define ATMEL_HLCDC_LAYER_FORMAT_CFG_ID		1
+#define ATMEL_HLCDC_LAYER_FORMAT_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_FORMAT_CFG_ID)
+#define ATMEL_HLCDC_LAYER_RGB			(0 << 0)
+#define ATMEL_HLCDC_LAYER_CLUT			(1 << 0)
+#define ATMEL_HLCDC_LAYER_YUV			(2 << 0)
+#define ATMEL_HLCDC_RGB_MODE(m)			(((m) & 0xf) << 4)
+#define ATMEL_HLCDC_CLUT_MODE(m)		(((m) & 0x3) << 8)
+#define ATMEL_HLCDC_YUV_MODE(m)			(((m) & 0xf) << 12)
+#define ATMEL_HLCDC_YUV422ROT			(1 << 16)
+#define ATMEL_HLCDC_YUV422SWP			(1 << 17)
+#define ATMEL_HLCDC_DSCALEOPT			(1 << 20)
+
+#define ATMEL_HLCDC_XRGB4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(0))
+#define ATMEL_HLCDC_ARGB4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(1))
+#define ATMEL_HLCDC_RGBA4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(2))
+#define ATMEL_HLCDC_RGB565_MODE			(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(3))
+#define ATMEL_HLCDC_ARGB1555_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(4))
+#define ATMEL_HLCDC_XRGB8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(9))
+#define ATMEL_HLCDC_RGB888_MODE			(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(10))
+#define ATMEL_HLCDC_ARGB8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(12))
+#define ATMEL_HLCDC_RGBA8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(13))
+
+#define ATMEL_HLCDC_AYUV_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(0))
+#define ATMEL_HLCDC_YUYV_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(1))
+#define ATMEL_HLCDC_UYVY_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(2))
+#define ATMEL_HLCDC_YVYU_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(3))
+#define ATMEL_HLCDC_VYUY_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(4))
+#define ATMEL_HLCDC_NV61_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(5))
+#define ATMEL_HLCDC_YUV422_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(6))
+#define ATMEL_HLCDC_NV21_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(7))
+#define ATMEL_HLCDC_YUV420_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(8))
+
+#define ATMEL_HLCDC_LAYER_POS_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pos)
+#define ATMEL_HLCDC_LAYER_SIZE_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.size)
+#define ATMEL_HLCDC_LAYER_MEMSIZE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.memsize)
+#define ATMEL_HLCDC_LAYER_XSTRIDE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.xstride)
+#define ATMEL_HLCDC_LAYER_PSTRIDE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pstride)
+#define ATMEL_HLCDC_LAYER_DFLTCOLOR_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.default_color)
+#define ATMEL_HLCDC_LAYER_CRKEY_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key)
+#define ATMEL_HLCDC_LAYER_CRKEY_MASK_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key_mask)
+
+#define ATMEL_HLCDC_LAYER_GENERAL_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.general_config)
+#define ATMEL_HLCDC_LAYER_CRKEY			BIT(0)
+#define ATMEL_HLCDC_LAYER_INV			BIT(1)
+#define ATMEL_HLCDC_LAYER_ITER2BL		BIT(2)
+#define ATMEL_HLCDC_LAYER_ITER			BIT(3)
+#define ATMEL_HLCDC_LAYER_REVALPHA		BIT(4)
+#define ATMEL_HLCDC_LAYER_GAEN			BIT(5)
+#define ATMEL_HLCDC_LAYER_LAEN			BIT(6)
+#define ATMEL_HLCDC_LAYER_OVR			BIT(7)
+#define ATMEL_HLCDC_LAYER_DMA			BIT(8)
+#define ATMEL_HLCDC_LAYER_REP			BIT(9)
+#define ATMEL_HLCDC_LAYER_DSTKEY		BIT(10)
+#define ATMEL_HLCDC_LAYER_GA_MASK		GENMASK(23, 16)
+#define ATMEL_HLCDC_LAYER_GA_SHIFT		16
+
+#define ATMEL_HLCDC_LAYER_CSC_CFG(p, o)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.csc + o)
+
+#define ATMEL_HLCDC_LAYER_DISC_POS_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_pos)
+
+#define ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_size)
+
+#define ATMEL_HLCDC_MAX_PLANES			3
+
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED	BIT(0)
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED	BIT(1)
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE	BIT(2)
+
+/**
+ * Atmel HLCDC Layer registers layout structure
+ *
+ * Each HLCDC layer has its own register organization and a given register
+ * by be placed differently on 2 different layers depending on its
+ * capabilities.
+ * This structure stores common registers layout for a given layer and is
+ * used by HLCDC layer code to chose the appropriate register to write to
+ * or to read from.
+ *
+ * For all fields, a value of zero means "unsupported".
+ *
+ * See Atmel's datasheet for a detailled description of these registers.
+ *
+ * @xstride: xstride registers
+ * @pstride: pstride registers
+ * @pos: position register
+ * @size: displayed size register
+ * @memsize: memory size register
+ * @default_color: default color register
+ * @chroma_key: chroma key register
+ * @chroma_key_mask: chroma key mask register
+ * @general_config: general layer config register
+ * @disc_pos: discard area position register
+ * @disc_size: discard area size register
+ * @csc: color space conversion register
+ */
+struct atmel_hlcdc_layer_cfg_layout {
+	int xstride[ATMEL_HLCDC_MAX_PLANES];
+	int pstride[ATMEL_HLCDC_MAX_PLANES];
+	int pos;
+	int size;
+	int memsize;
+	int default_color;
+	int chroma_key;
+	int chroma_key_mask;
+	int general_config;
+	int disc_pos;
+	int disc_size;
+	int csc;
+};
+
+/**
+ * Atmel HLCDC framebuffer flip structure
+ *
+ * This structure is allocated when someone asked for a layer update (most
+ * likely a DRM plane update, either primary, overlay or cursor plane) and
+ * released when the layer do not need to reference the framebuffer object
+ * anymore (i.e. the layer was disabled or updated).
+ *
+ * @fb: the referenced framebuffer object.
+ * @refcnt: the number of GEM object still referenced by the layer.
+ *	    When no more objects are referenced the fb flip structure is
+ *	    added to the garbage collector.
+ * @ngems: number of GEM objects referenced by the fb element.
+ * @finished: finished callback, called when the layer framebuffer flip is
+ *	      finished.
+ * @finished_data: data passed to the finished callback.
+ */
+struct atmel_hlcdc_layer_fb_flip {
+	struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_MAX_PLANES];
+	struct drm_flip_task *task;
+	struct drm_framebuffer *fb;
+	int ngems;
+	u32 status;
+	void (*finished)(void *data);
+	void *finished_data;
+};
+
+/**
+ * Atmel HLCDC DMA descriptor structure
+ *
+ * This structure is used by the HLCDC DMA engine to schedule a DMA transfer.
+ *
+ * The structure fields must remain in this specific order, because they're
+ * used by the HLCDC DMA engine, which expect them in this order.
+ *
+ * @addr: buffer DMA address
+ * @ctrl: DMA transfer options
+ * @next: next DMA descriptor to fetch
+ * @gem_flip: the attached gem_flip operation
+ */
+struct atmel_hlcdc_dma_channel_dscr {
+	dma_addr_t addr;
+	u32 ctrl;
+	dma_addr_t next;
+	u32 status;
+} __aligned(sizeof(u64));
+
+/**
+ * Atmel HLCDC layer types
+ */
+enum atmel_hlcdc_layer_type {
+	ATMEL_HLCDC_BASE_LAYER,
+	ATMEL_HLCDC_OVERLAY_LAYER,
+	ATMEL_HLCDC_CURSOR_LAYER,
+	ATMEL_HLCDC_PP_LAYER,
+};
+
+/**
+ * Atmel HLCDC Supported formats structure
+ *
+ * This structure list all the formats supported by a given layer.
+ *
+ * @nformats: number of supported formats
+ * @formats: supported formats
+ */
+struct atmel_hlcdc_formats {
+	int nformats;
+	uint32_t *formats;
+};
+
+/**
+ * Atmel HLCDC Layer description structure
+ *
+ * This structure describe the capabilities provided by a given layer.
+ *
+ * @name: layer name
+ * @type: layer type
+ * @id: layer id
+ * @regs_offset: offset of the layer registers from the HLCDC registers base
+ * @nconfigs: number of config registers provided by this layer
+ * @layout: config registers layout
+ * @max_width: maximum width supported by this layer (0 means unlimited)
+ * @max_height: maximum height supported by this layer (0 means unlimited)
+ */
+struct atmel_hlcdc_layer_desc {
+	const char *name;
+	enum atmel_hlcdc_layer_type type;
+	int id;
+	int regs_offset;
+	int nconfigs;
+	struct atmel_hlcdc_formats *formats;
+	struct atmel_hlcdc_layer_cfg_layout layout;
+	int max_width;
+	int max_height;
+};
+
+/**
+ * Atmel HLCDC Layer Update Slot structure
+ *
+ * This structure stores layer update requests to be applied on next frame.
+ * This is the base structure behind the atomic layer update infrastructure.
+ *
+ * Atomic layer update provides a way to update all layer's parameters
+ * simultaneously. This is needed to avoid incompatible sequential updates
+ * like this one:
+ * 1) update layer format from RGB888 (1 plane/buffer) to YUV422
+ *    (2 planes/buffers)
+ * 2) the format update is applied but the DMA channel for the second
+ *    plane/buffer is not enabled
+ * 3) enable the DMA channel for the second plane
+ *
+ * @dscrs: DMA channel descriptors
+ * @fb_flip: fb_flip object
+ * @updated_configs: bitmask used to record modified configs
+ * @configs: new config values
+ */
+struct atmel_hlcdc_layer_update_slot {
+	struct atmel_hlcdc_layer_fb_flip *fb_flip;
+	unsigned long *updated_configs;
+	u32 *configs;
+};
+
+/**
+ * Atmel HLCDC Layer Update structure
+ *
+ * This structure provides a way to queue layer update requests.
+ *
+ * At a given time there is at most:
+ *  - one pending update request, which means the update request has been
+ *    commited (or validated) and is waiting for the DMA channel(s) to be
+ *    available
+ *  - one request being prepared, which means someone started a layer update
+ *    but has not commited it yet. There cannot be more than one started
+ *    request, because the update lock is taken when starting a layer update
+ *    and release when commiting or rolling back the request.
+ *
+ * @slots: update slots. One is used for pending request and the other one
+ *	   for started update request
+ * @pending: the pending slot index or -1 if no request is pending
+ * @next: the started update slot index or -1 no update has been started
+ */
+struct atmel_hlcdc_layer_update {
+	struct atmel_hlcdc_layer_update_slot slots[2];
+	int pending;
+	int next;
+};
+
+enum atmel_hlcdc_layer_dma_channel_status {
+	ATMEL_HLCDC_LAYER_DISABLED,
+	ATMEL_HLCDC_LAYER_ENABLED,
+	ATMEL_HLCDC_LAYER_DISABLING,
+};
+
+/**
+ * Atmel HLCDC Layer DMA channel structure
+ *
+ * This structure stores informations on the DMA channel associated to a
+ * given layer.
+ *
+ * @status: DMA channel status
+ * @cur: current framebuffer
+ * @queue: next framebuffer
+ * @dscrs: allocated DMA descriptors
+ */
+struct atmel_hlcdc_layer_dma_channel {
+	enum atmel_hlcdc_layer_dma_channel_status status;
+	struct atmel_hlcdc_layer_fb_flip *cur;
+	struct atmel_hlcdc_layer_fb_flip *queue;
+	struct atmel_hlcdc_dma_channel_dscr *dscrs;
+};
+
+/**
+ * Atmel HLCDC Layer structure
+ *
+ * This structure stores information on the layer instance.
+ *
+ * @desc: layer description
+ * @max_planes: maximum planes/buffers that can be associated with this layer.
+ *	       This depends on the supported formats.
+ * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
+ * @dma: dma channel
+ * @gc: fb flip garbage collector
+ * @update: update handler
+ * @lock: layer lock
+ */
+struct atmel_hlcdc_layer {
+	const struct atmel_hlcdc_layer_desc *desc;
+	int max_planes;
+	struct atmel_hlcdc *hlcdc;
+	struct workqueue_struct *wq;
+	struct drm_flip_work gc;
+	struct atmel_hlcdc_layer_dma_channel dma;
+	struct atmel_hlcdc_layer_update update;
+	spinlock_t lock;
+};
+
+void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer);
+
+int atmel_hlcdc_layer_init(struct drm_device *dev,
+			   struct atmel_hlcdc_layer *layer,
+			   const struct atmel_hlcdc_layer_desc *desc);
+
+void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
+			       struct atmel_hlcdc_layer *layer);
+
+int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer);
+
+void atmel_hlcdc_layer_set_finished(struct atmel_hlcdc_layer *layer,
+				    void (*finished)(void *data),
+				    void *data);
+
+int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer);
+
+void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
+				  u32 mask, u32 val);
+
+void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
+				     struct drm_framebuffer *fb,
+				     unsigned int *offsets);
+
+void atmel_hlcdc_layer_update_set_finished(struct atmel_hlcdc_layer *layer,
+					   void (*finished)(void *data),
+					   void *finished_data);
+
+void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer);
+
+void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer);
+
+#endif /* DRM_ATMEL_HLCDC_LAYER_H */
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
new file mode 100644
index 0000000..de95eb7
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_panel.h>
+
+#include "atmel_hlcdc_dc.h"
+
+/**
+ * Atmel HLCDC RGB output mode
+ */
+enum atmel_hlcdc_connector_rgb_mode {
+	ATMEL_HLCDC_CONNECTOR_RGB444,
+	ATMEL_HLCDC_CONNECTOR_RGB565,
+	ATMEL_HLCDC_CONNECTOR_RGB666,
+	ATMEL_HLCDC_CONNECTOR_RGB888,
+};
+
+struct atmel_hlcdc_slave;
+
+/**
+ * Atmel HLCDC Slave device operations structure
+ *
+ * This structure defines an abstraction to be implemented by each slave
+ * device type (panel, convertors, ...).
+ *
+ * @enable: Enable the slave device
+ * @disable: Disable the slave device
+ * @get_modes: retrieve modes supported by the slave device
+ * @destroy: detroy the slave device and all associated data
+ */
+struct atmel_hlcdc_slave_ops {
+	int (*enable)(struct atmel_hlcdc_slave *slave);
+	int (*disable)(struct atmel_hlcdc_slave *slave);
+	int (*get_modes)(struct atmel_hlcdc_slave *slave);
+	void (*destroy)(struct atmel_hlcdc_slave *slave);
+};
+
+/**
+ * Atmel HLCDC Slave device structure
+ *
+ * This structure is the base slave device structure to be overloaded by
+ * each slave device implementation.
+ *
+ * @ops: slave device operations
+ */
+struct atmel_hlcdc_slave {
+	const struct atmel_hlcdc_slave_ops *ops;
+};
+
+/**
+ * Atmel HLCDC Panel device structure
+ *
+ * This structure is specialization of the slave device structure to
+ * interface with drm panels.
+ *
+ * @slave: base slave device fields
+ * @panel: drm panel attached to this slave device
+ */
+struct atmel_hlcdc_panel {
+	struct atmel_hlcdc_slave slave;
+	struct drm_panel *panel;
+};
+
+static inline struct atmel_hlcdc_panel *
+atmel_hlcdc_slave_to_panel(struct atmel_hlcdc_slave *slave)
+{
+	return container_of(slave, struct atmel_hlcdc_panel, slave);
+}
+
+/**
+ * Atmel HLCDC RGB connector structure
+ *
+ * This structure stores informations about an DRM panel connected through
+ * the RGB connector.
+ *
+ * @connector: DRM connector
+ * @encoder: DRM encoder
+ * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
+ * @slave: slave device connected to this output
+ * @endpoint: DT endpoint representing this output
+ * @dpms: current DPMS mode
+ */
+struct atmel_hlcdc_rgb_output {
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct atmel_hlcdc *hlcdc;
+	struct atmel_hlcdc_slave *slave;
+	struct of_endpoint endpoint;
+	int dpms;
+};
+
+static inline struct atmel_hlcdc_rgb_output *
+drm_connector_to_atmel_hlcdc_rgb_output(struct drm_connector *connector)
+{
+	return container_of(connector, struct atmel_hlcdc_rgb_output,
+			    connector);
+}
+
+static inline struct atmel_hlcdc_rgb_output *
+drm_encoder_to_atmel_hlcdc_rgb_output(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder);
+}
+
+static int atmel_hlcdc_panel_enable(struct atmel_hlcdc_slave *slave)
+{
+	struct atmel_hlcdc_panel *panel = atmel_hlcdc_slave_to_panel(slave);
+
+	return drm_panel_enable(panel->panel);
+}
+
+static int atmel_hlcdc_panel_disable(struct atmel_hlcdc_slave *slave)
+{
+	struct atmel_hlcdc_panel *panel = atmel_hlcdc_slave_to_panel(slave);
+
+	return drm_panel_disable(panel->panel);
+}
+
+static int atmel_hlcdc_panel_get_modes(struct atmel_hlcdc_slave *slave)
+{
+	struct atmel_hlcdc_panel *panel = atmel_hlcdc_slave_to_panel(slave);
+
+	return panel->panel->funcs->get_modes(panel->panel);
+}
+
+static void atmel_hlcdc_panel_destroy(struct atmel_hlcdc_slave *slave)
+{
+	struct atmel_hlcdc_panel *panel = atmel_hlcdc_slave_to_panel(slave);
+
+	drm_panel_detach(panel->panel);
+	kfree(panel);
+}
+
+static const struct atmel_hlcdc_slave_ops atmel_hlcdc_panel_ops = {
+	.enable = atmel_hlcdc_panel_enable,
+	.disable = atmel_hlcdc_panel_disable,
+	.get_modes = atmel_hlcdc_panel_get_modes,
+	.destroy = atmel_hlcdc_panel_destroy,
+};
+
+static struct atmel_hlcdc_slave *
+atmel_hlcdc_panel_detect(struct atmel_hlcdc_rgb_output *rgb)
+{
+	struct device_node *np;
+	struct drm_panel *p = NULL;
+	struct atmel_hlcdc_panel *panel;
+
+	np = of_graph_get_remote_port_parent(rgb->endpoint.local_node);
+	if (!np)
+		return NULL;
+
+	p = of_drm_find_panel(np);
+	of_node_put(np);
+
+	if (p) {
+		panel = kzalloc(sizeof(*panel), GFP_KERNEL);
+		if (!panel)
+			return NULL;
+
+		drm_panel_attach(p, &rgb->connector);
+		panel->panel = p;
+		panel->slave.ops = &atmel_hlcdc_panel_ops;
+		return &panel->slave;
+	}
+
+	return NULL;
+}
+
+static void atmel_hlcdc_rgb_encoder_dpms(struct drm_encoder *encoder,
+					 int mode)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
+	struct regmap *regmap = rgb->hlcdc->regmap;
+	unsigned int status;
+
+	if (mode != DRM_MODE_DPMS_ON)
+		mode = DRM_MODE_DPMS_OFF;
+
+	if (mode == rgb->dpms)
+		return;
+
+	if (mode != DRM_MODE_DPMS_ON) {
+		regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       (status & ATMEL_HLCDC_DISP))
+			cpu_relax();
+
+		regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       (status & ATMEL_HLCDC_SYNC))
+			cpu_relax();
+
+		regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       (status & ATMEL_HLCDC_PIXEL_CLK))
+			cpu_relax();
+
+		clk_disable_unprepare(rgb->hlcdc->sys_clk);
+
+		rgb->slave->ops->disable(rgb->slave);
+	} else {
+		rgb->slave->ops->enable(rgb->slave);
+
+		clk_prepare_enable(rgb->hlcdc->sys_clk);
+
+		regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       !(status & ATMEL_HLCDC_PIXEL_CLK))
+			cpu_relax();
+
+
+		regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       !(status & ATMEL_HLCDC_SYNC))
+			cpu_relax();
+
+		regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       !(status & ATMEL_HLCDC_DISP))
+			cpu_relax();
+	}
+
+	rgb->dpms = mode;
+}
+
+static bool
+atmel_hlcdc_rgb_encoder_mode_fixup(struct drm_encoder *encoder,
+				   const struct drm_display_mode *mode,
+				   struct drm_display_mode *adjusted)
+{
+	return true;
+}
+
+static void atmel_hlcdc_rgb_encoder_prepare(struct drm_encoder *encoder)
+{
+	atmel_hlcdc_rgb_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static void atmel_hlcdc_rgb_encoder_commit(struct drm_encoder *encoder)
+{
+	atmel_hlcdc_rgb_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void
+atmel_hlcdc_rgb_encoder_mode_set(struct drm_encoder *encoder,
+				 struct drm_display_mode *mode,
+				 struct drm_display_mode *adjusted)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
+	struct drm_display_info *info = &rgb->connector.display_info;
+	unsigned long prate = clk_get_rate(rgb->hlcdc->sys_clk);
+	unsigned long mode_rate = mode->clock * 1000;
+	u32 cfg = 0;
+	int div;
+
+	if ((prate / 2) < mode_rate) {
+		prate *= 2;
+		cfg |= ATMEL_HLCDC_CLKSEL;
+	}
+
+	div = DIV_ROUND_UP(prate, mode_rate);
+	if (div < 2)
+		div = 2;
+
+	cfg |= ATMEL_HLCDC_CLKDIV(div);
+
+	if (mode->flags & DRM_MODE_FLAG_NCSYNC)
+		cfg |= ATMEL_HLCDC_CLKPOL;
+
+	regmap_update_bits(rgb->hlcdc->regmap, ATMEL_HLCDC_CFG(0),
+			   ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK, cfg);
+
+	cfg = 0;
+
+	if (info->nbus_formats) {
+		switch (info->bus_formats[0]) {
+		case VIDEO_BUS_FMT_RGB565_1X16:
+			cfg |= ATMEL_HLCDC_CONNECTOR_RGB565 << 8;
+			break;
+		case VIDEO_BUS_FMT_RGB666_1X18:
+			cfg |= ATMEL_HLCDC_CONNECTOR_RGB666 << 8;
+			break;
+		case VIDEO_BUS_FMT_RGB888_1X24:
+			cfg |= ATMEL_HLCDC_CONNECTOR_RGB888 << 8;
+			break;
+		case VIDEO_BUS_FMT_RGB444_1X12:
+		default:
+			break;
+		}
+	}
+
+	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+		cfg |= ATMEL_HLCDC_VSPOL;
+
+	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+		cfg |= ATMEL_HLCDC_HSPOL;
+
+	regmap_update_bits(rgb->hlcdc->regmap, ATMEL_HLCDC_CFG(5),
+			   ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL |
+			   ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE |
+			   ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY |
+			   ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO |
+			   ATMEL_HLCDC_MODE_MASK | ATMEL_HLCDC_GUARDTIME_MASK,
+			   cfg);
+}
+
+static struct drm_encoder_helper_funcs atmel_hlcdc_rgb_encoder_helper_funcs = {
+	.dpms = atmel_hlcdc_rgb_encoder_dpms,
+	.mode_fixup = atmel_hlcdc_rgb_encoder_mode_fixup,
+	.prepare = atmel_hlcdc_rgb_encoder_prepare,
+	.commit = atmel_hlcdc_rgb_encoder_commit,
+	.mode_set = atmel_hlcdc_rgb_encoder_mode_set,
+};
+
+static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+	memset(encoder, 0, sizeof(*encoder));
+}
+
+static const struct drm_encoder_funcs atmel_hlcdc_rgb_encoder_funcs = {
+	.destroy = atmel_hlcdc_rgb_encoder_destroy,
+};
+
+static int atmel_hlcdc_rgb_get_modes(struct drm_connector *connector)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_connector_to_atmel_hlcdc_rgb_output(connector);
+
+	return rgb->slave->ops->get_modes(rgb->slave);
+}
+
+static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector,
+				      struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+atmel_hlcdc_rgb_best_encoder(struct drm_connector *connector)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_connector_to_atmel_hlcdc_rgb_output(connector);
+
+	return &rgb->encoder;
+}
+
+static struct drm_connector_helper_funcs atmel_hlcdc_rgb_connector_helper_funcs = {
+	.get_modes = atmel_hlcdc_rgb_get_modes,
+	.mode_valid = atmel_hlcdc_rgb_mode_valid,
+	.best_encoder = atmel_hlcdc_rgb_best_encoder,
+};
+
+static enum drm_connector_status
+atmel_hlcdc_rgb_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_connector_to_atmel_hlcdc_rgb_output(connector);
+
+	if (!rgb->slave) {
+		/* At the moment we only support panel devices */
+		rgb->slave = atmel_hlcdc_panel_detect(rgb);
+	}
+
+	if (rgb->slave)
+		return connector_status_connected;
+
+	return connector_status_disconnected;
+}
+
+static void
+atmel_hlcdc_rgb_connector_destroy(struct drm_connector *connector)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_connector_to_atmel_hlcdc_rgb_output(connector);
+
+	if (rgb->slave && rgb->slave->ops->destroy)
+		rgb->slave->ops->destroy(rgb->slave);
+
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs atmel_hlcdc_rgb_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = atmel_hlcdc_rgb_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = atmel_hlcdc_rgb_connector_destroy,
+};
+
+static int atmel_hlcdc_create_output(struct drm_device *dev,
+				     struct of_endpoint *ep)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct atmel_hlcdc_rgb_output *rgb;
+
+	rgb = devm_kzalloc(dev->dev, sizeof(*rgb), GFP_KERNEL);
+	if (!rgb)
+		return -ENOMEM;
+
+	rgb->endpoint = *ep;
+
+	rgb->dpms = DRM_MODE_DPMS_OFF;
+
+	rgb->hlcdc = dc->hlcdc;
+
+	drm_connector_init(dev, &rgb->connector,
+			   &atmel_hlcdc_rgb_connector_funcs,
+			   DRM_MODE_CONNECTOR_LVDS);
+	drm_connector_helper_add(&rgb->connector,
+				 &atmel_hlcdc_rgb_connector_helper_funcs);
+	rgb->connector.dpms = DRM_MODE_DPMS_OFF;
+	rgb->connector.polled = DRM_CONNECTOR_POLL_CONNECT;
+
+	drm_encoder_init(dev, &rgb->encoder, &atmel_hlcdc_rgb_encoder_funcs,
+			 DRM_MODE_ENCODER_LVDS);
+	drm_encoder_helper_add(&rgb->encoder,
+			       &atmel_hlcdc_rgb_encoder_helper_funcs);
+
+	drm_mode_connector_attach_encoder(&rgb->connector, &rgb->encoder);
+	drm_sysfs_connector_add(&rgb->connector);
+
+	rgb->encoder.possible_crtcs = 0x1;
+
+	return 0;
+}
+
+int atmel_hlcdc_create_outputs(struct drm_device *dev)
+{
+	struct device_node *port_np, *np;
+	struct of_endpoint ep;
+	int ret;
+
+	port_np = of_get_child_by_name(dev->dev->of_node, "port");
+	if (!port_np)
+		return -EINVAL;
+
+	np = of_get_child_by_name(port_np, "endpoint");
+	of_node_put(port_np);
+
+	if (!np)
+		return -EINVAL;
+
+	ret = of_graph_parse_endpoint(np, &ep);
+	of_node_put(port_np);
+
+	if (ret)
+		return ret;
+
+	ret = atmel_hlcdc_create_output(dev, &ep);
+	if (ret)
+		return ret;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
new file mode 100644
index 0000000..d42cd73
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "atmel_hlcdc_dc.h"
+
+#define SUBPIXEL_MASK			0xffff
+
+static uint32_t rgb_formats[] = {
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_RGBA4444,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGBA8888,
+};
+
+struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {
+	.formats = rgb_formats,
+	.nformats = ARRAY_SIZE(rgb_formats),
+};
+
+static uint32_t rgb_and_yuv_formats[] = {
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_RGBA4444,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGBA8888,
+	DRM_FORMAT_AYUV,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_VYUY,
+	DRM_FORMAT_NV21,
+	DRM_FORMAT_NV61,
+	DRM_FORMAT_YUV422,
+	DRM_FORMAT_YUV420,
+};
+
+struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {
+	.formats = rgb_and_yuv_formats,
+	.nformats = ARRAY_SIZE(rgb_and_yuv_formats),
+};
+
+static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
+{
+	switch (format) {
+	case DRM_FORMAT_XRGB4444:
+		*mode = ATMEL_HLCDC_XRGB4444_MODE;
+		break;
+	case DRM_FORMAT_ARGB4444:
+		*mode = ATMEL_HLCDC_ARGB4444_MODE;
+		break;
+	case DRM_FORMAT_RGBA4444:
+		*mode = ATMEL_HLCDC_RGBA4444_MODE;
+		break;
+	case DRM_FORMAT_RGB565:
+		*mode = ATMEL_HLCDC_RGB565_MODE;
+		break;
+	case DRM_FORMAT_RGB888:
+		*mode = ATMEL_HLCDC_RGB888_MODE;
+		break;
+	case DRM_FORMAT_ARGB1555:
+		*mode = ATMEL_HLCDC_ARGB1555_MODE;
+		break;
+	case DRM_FORMAT_XRGB8888:
+		*mode = ATMEL_HLCDC_XRGB8888_MODE;
+		break;
+	case DRM_FORMAT_ARGB8888:
+		*mode = ATMEL_HLCDC_ARGB8888_MODE;
+		break;
+	case DRM_FORMAT_RGBA8888:
+		*mode = ATMEL_HLCDC_RGBA8888_MODE;
+		break;
+	case DRM_FORMAT_AYUV:
+		*mode = ATMEL_HLCDC_AYUV_MODE;
+		break;
+	case DRM_FORMAT_YUYV:
+		*mode = ATMEL_HLCDC_YUYV_MODE;
+		break;
+	case DRM_FORMAT_UYVY:
+		*mode = ATMEL_HLCDC_UYVY_MODE;
+		break;
+	case DRM_FORMAT_YVYU:
+		*mode = ATMEL_HLCDC_YVYU_MODE;
+		break;
+	case DRM_FORMAT_VYUY:
+		*mode = ATMEL_HLCDC_VYUY_MODE;
+		break;
+	case DRM_FORMAT_NV21:
+		*mode = ATMEL_HLCDC_NV21_MODE;
+		break;
+	case DRM_FORMAT_NV61:
+		*mode = ATMEL_HLCDC_NV61_MODE;
+		break;
+	case DRM_FORMAT_YUV420:
+		*mode = ATMEL_HLCDC_YUV420_MODE;
+		break;
+	case DRM_FORMAT_YUV422:
+		*mode = ATMEL_HLCDC_YUV422_MODE;
+		break;
+	default:
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+static bool atmel_hlcdc_format_embedds_alpha(u32 format)
+{
+	int i;
+
+	for (i = 0; i < sizeof(format); i++) {
+		char tmp = (format >> (8 * i)) & 0xff;
+
+		if (tmp == 'A')
+			return true;
+	}
+
+	return false;
+}
+
+static u32 heo_downscaling_xcoef[] = {
+	0x11343311,
+	0x000000f7,
+	0x1635300c,
+	0x000000f9,
+	0x1b362c08,
+	0x000000fb,
+	0x1f372804,
+	0x000000fe,
+	0x24382400,
+	0x00000000,
+	0x28371ffe,
+	0x00000004,
+	0x2c361bfb,
+	0x00000008,
+	0x303516f9,
+	0x0000000c,
+};
+
+static u32 heo_downscaling_ycoef[] = {
+	0x00123737,
+	0x00173732,
+	0x001b382d,
+	0x001f3928,
+	0x00243824,
+	0x0028391f,
+	0x002d381b,
+	0x00323717,
+};
+
+static u32 heo_upscaling_xcoef[] = {
+	0xf74949f7,
+	0x00000000,
+	0xf55f33fb,
+	0x000000fe,
+	0xf5701efe,
+	0x000000ff,
+	0xf87c0dff,
+	0x00000000,
+	0x00800000,
+	0x00000000,
+	0x0d7cf800,
+	0x000000ff,
+	0x1e70f5ff,
+	0x000000fe,
+	0x335ff5fe,
+	0x000000fb,
+};
+
+static u32 heo_upscaling_ycoef[] = {
+	0x00004040,
+	0x00075920,
+	0x00056f0c,
+	0x00027b03,
+	0x00008000,
+	0x00037b02,
+	0x000c6f05,
+	0x00205907,
+};
+
+static void
+atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	const struct atmel_hlcdc_layer_cfg_layout *layout =
+						&plane->layer.desc->layout;
+
+	if (layout->size)
+		atmel_hlcdc_layer_update_cfg(&plane->layer,
+					     layout->size,
+					     0xffffffff,
+					     (req->crtc_w - 1) |
+					     ((req->crtc_h - 1) << 16));
+
+	if (layout->memsize)
+		atmel_hlcdc_layer_update_cfg(&plane->layer,
+					     layout->memsize,
+					     0xffffffff,
+					     (req->src_w - 1) |
+					     ((req->src_h - 1) << 16));
+
+	if (layout->pos)
+		atmel_hlcdc_layer_update_cfg(&plane->layer,
+					     layout->pos,
+					     0xffffffff,
+					     req->crtc_x |
+					     (req->crtc_y  << 16));
+
+	/* TODO: rework the rescaling part */
+	if (req->crtc_w != req->src_w || req->crtc_h != req->src_h) {
+		u32 factor_reg = 0;
+
+		if (req->crtc_w != req->src_w) {
+			int i;
+			u32 factor;
+			u32 *coeff_tab = heo_upscaling_xcoef;
+			u32 max_memsize;
+
+			if (req->crtc_w < req->src_w)
+				coeff_tab = heo_downscaling_xcoef;
+			for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++)
+				atmel_hlcdc_layer_update_cfg(&plane->layer,
+							     17 + i,
+							     0xffffffff,
+							     coeff_tab[i]);
+			factor = ((8 * 256 * req->src_w) - (256 * 4)) /
+				 req->crtc_w;
+			factor++;
+			max_memsize = ((factor * req->crtc_w) + (256 * 4)) /
+				      2048;
+			if (max_memsize > req->src_w)
+				factor--;
+			factor_reg |= factor | 0x80000000;
+		}
+
+		if (req->crtc_h != req->src_h) {
+			int i;
+			u32 factor;
+			u32 *coeff_tab = heo_upscaling_ycoef;
+			u32 max_memsize;
+
+			if (req->crtc_w < req->src_w)
+				coeff_tab = heo_downscaling_ycoef;
+			for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
+				atmel_hlcdc_layer_update_cfg(&plane->layer,
+							     33 + i,
+							     0xffffffff,
+							     coeff_tab[i]);
+			factor = ((8 * 256 * req->src_w) - (256 * 4)) /
+				 req->crtc_w;
+			factor++;
+			max_memsize = ((factor * req->crtc_w) + (256 * 4)) /
+				      2048;
+			if (max_memsize > req->src_w)
+				factor--;
+			factor_reg |= (factor << 16) | 0x80000000;
+		}
+
+		atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff,
+					     factor_reg);
+	}
+}
+
+static void
+atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	const struct atmel_hlcdc_layer_cfg_layout *layout =
+						&plane->layer.desc->layout;
+	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA;
+
+	if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
+		cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
+		       ATMEL_HLCDC_LAYER_ITER;
+
+		if (atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format))
+			cfg |= ATMEL_HLCDC_LAYER_LAEN;
+		else
+			cfg |= ATMEL_HLCDC_LAYER_GAEN;
+	}
+
+	atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
+				     ATMEL_HLCDC_LAYER_ITER2BL |
+				     ATMEL_HLCDC_LAYER_ITER |
+				     ATMEL_HLCDC_LAYER_GAEN |
+				     ATMEL_HLCDC_LAYER_LAEN |
+				     ATMEL_HLCDC_LAYER_OVR |
+				     ATMEL_HLCDC_LAYER_DMA, cfg);
+}
+
+static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	u32 mode;
+	int ret;
+
+	ret = atmel_hlcdc_format_to_plane_mode(req->fb->pixel_format, &mode);
+	if (ret)
+		return;
+
+	atmel_hlcdc_layer_update_cfg(&plane->layer,
+				     ATMEL_HLCDC_LAYER_FORMAT_CFG_ID,
+				     0xffffffff,
+				     mode);
+}
+
+static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	struct atmel_hlcdc_layer *layer = &plane->layer;
+	const struct atmel_hlcdc_layer_cfg_layout *layout =
+							&layer->desc->layout;
+	int i;
+
+	atmel_hlcdc_layer_update_set_fb(&plane->layer, req->fb, req->offsets);
+
+	for (i = 0; i < req->nplanes; i++) {
+		if (layout->xstride[i]) {
+			atmel_hlcdc_layer_update_cfg(&plane->layer,
+						layout->xstride[i],
+						0xffffffff,
+						req->xstride[i]);
+		}
+
+		if (layout->pstride[i]) {
+			atmel_hlcdc_layer_update_cfg(&plane->layer,
+						layout->pstride[i],
+						0xffffffff,
+						req->pstride[i]);
+		}
+	}
+}
+
+static int atmel_hlcdc_plane_check_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	const struct atmel_hlcdc_layer_cfg_layout *layout =
+						&plane->layer.desc->layout;
+
+	if (!layout->size &&
+	    (req->crtc->mode.crtc_hdisplay != req->crtc_w ||
+	     req->crtc->mode.crtc_vdisplay != req->crtc_h))
+		return -EINVAL;
+
+	if (plane->layer.desc->max_height &&
+	    req->crtc_h > plane->layer.desc->max_height)
+		return -EINVAL;
+
+	if (plane->layer.desc->max_width &&
+	    req->crtc_w > plane->layer.desc->max_width)
+		return -EINVAL;
+
+	if ((req->crtc_h != req->src_h || req->crtc_w != req->src_w) &&
+	    (!layout->memsize ||
+	     atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format)))
+		return -EINVAL;
+
+	return 0;
+}
+
+int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	unsigned int patched_crtc_w;
+	unsigned int patched_crtc_h;
+	unsigned int patched_src_w;
+	unsigned int patched_src_h;
+	unsigned int tmp;
+	int x_offset = 0;
+	int y_offset = 0;
+	int i;
+
+	if ((req->src_x | req->src_y | req->src_w | req->src_h) &
+	    SUBPIXEL_MASK)
+		return -EINVAL;
+
+	req->src_x >>= 16;
+	req->src_y >>= 16;
+	req->src_w >>= 16;
+	req->src_h >>= 16;
+
+	req->nplanes = drm_format_num_planes(req->fb->pixel_format);
+	if (req->nplanes > ATMEL_HLCDC_MAX_PLANES)
+		return -EINVAL;
+
+	/*
+	 * Swap width and size if in case of 90 or 270 degrees rotation
+	 */
+	if (plane->rotation == ATMEL_HLCDC_PLANE_90DEG_ROTATION ||
+	    plane->rotation == ATMEL_HLCDC_PLANE_270DEG_ROTATION) {
+		tmp = req->crtc_w;
+		req->crtc_w = req->crtc_h;
+		req->crtc_h = tmp;
+		tmp = req->src_w;
+		req->src_w = req->src_h;
+		req->src_h = tmp;
+	}
+
+	if (req->crtc_x + req->crtc_w > req->crtc->mode.hdisplay)
+		patched_crtc_w = req->crtc->mode.hdisplay - req->crtc_x;
+	else
+		patched_crtc_w = req->crtc_w;
+
+	if (req->crtc_x < 0) {
+		patched_crtc_w += req->crtc_x;
+		x_offset = -req->crtc_x;
+		req->crtc_x = 0;
+	}
+
+	if (req->crtc_y + req->crtc_h > req->crtc->mode.vdisplay)
+		patched_crtc_h = req->crtc->mode.vdisplay - req->crtc_y;
+	else
+		patched_crtc_h = req->crtc_h;
+
+	if (req->crtc_y < 0) {
+		patched_crtc_h += req->crtc_y;
+		y_offset = -req->crtc_y;
+		req->crtc_y = 0;
+	}
+
+	patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * req->src_w,
+					  req->crtc_w);
+	patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * req->src_h,
+					  req->crtc_h);
+
+	for (i = 0; i < req->nplanes; i++) {
+		unsigned int offset = 0;
+
+		req->bpp[i] = drm_format_plane_cpp(req->fb->pixel_format, i);
+		if (!req->bpp[i])
+			return -EINVAL;
+
+		switch (plane->rotation) {
+		case ATMEL_HLCDC_PLANE_90DEG_ROTATION:
+			offset = (y_offset + req->src_y + patched_src_w - 1) *
+				 req->fb->pitches[i];
+			offset += (x_offset + req->src_x) * req->bpp[i];
+			req->xstride[i] = req->fb->pitches[i] *
+					  (patched_src_w - 1);
+			req->pstride[i] = -req->fb->pitches[i] - req->bpp[i];
+			break;
+		case ATMEL_HLCDC_PLANE_180DEG_ROTATION:
+			offset = (y_offset + req->src_y + patched_src_h - 1) *
+				 req->fb->pitches[i];
+			offset += (x_offset + req->src_x + patched_src_w) *
+				  req->bpp[i];
+			req->xstride[i] = ((patched_src_w - 2) * req->bpp[i]) -
+					   req->fb->pitches[i];
+			req->pstride[i] = -2 * req->bpp[i];
+			break;
+		case ATMEL_HLCDC_PLANE_270DEG_ROTATION:
+			offset = (y_offset + req->src_y) * req->fb->pitches[i];
+			offset += (x_offset + req->src_x + patched_src_h) *
+				  req->bpp[i];
+			req->xstride[i] = -(req->fb->pitches[i] *
+					    (patched_src_w - 1)) -
+					  (2 * req->bpp[i]);
+			req->pstride[i] = req->fb->pitches[i] - req->bpp[i];
+			break;
+		case ATMEL_HLCDC_PLANE_NO_ROTATION:
+		default:
+			offset = (y_offset + req->src_y) * req->fb->pitches[i];
+			offset += (x_offset + req->src_x) * req->bpp[i];
+			req->xstride[i] = req->fb->pitches[i] -
+					  (patched_src_w * req->bpp[i]);
+			req->pstride[i] = 0;
+			break;
+		}
+
+		req->offsets[i] = offset + req->fb->offsets[i];
+	}
+
+	req->src_w = patched_src_w;
+	req->src_h = patched_src_h;
+	req->crtc_w = patched_crtc_w;
+	req->crtc_h = patched_crtc_h;
+
+	return atmel_hlcdc_plane_check_update_req(p, req);
+}
+
+int atmel_hlcdc_plane_apply_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	int ret;
+
+	ret = atmel_hlcdc_layer_update_start(&plane->layer);
+	if (ret)
+		return ret;
+
+	atmel_hlcdc_plane_update_pos_and_size(plane, req);
+	atmel_hlcdc_plane_update_general_settings(plane, req);
+	atmel_hlcdc_plane_update_format(plane, req);
+	atmel_hlcdc_plane_update_buffers(plane, req);
+	atmel_hlcdc_layer_update_set_finished(&plane->layer, req->finished,
+					      req->finished_data);
+
+	atmel_hlcdc_layer_update_commit(&plane->layer);
+
+	return 0;
+}
+
+static int atmel_hlcdc_plane_update(struct drm_plane *p,
+				    struct drm_crtc *crtc,
+				    struct drm_framebuffer *fb,
+				    int crtc_x, int crtc_y,
+				    unsigned int crtc_w, unsigned int crtc_h,
+				    uint32_t src_x, uint32_t src_y,
+				    uint32_t src_w, uint32_t src_h)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	struct atmel_hlcdc_plane_update_req req;
+	int ret = 0;
+
+	memset(&req, 0, sizeof(req));
+	req.crtc_x = crtc_x;
+	req.crtc_y = crtc_y;
+	req.crtc_w = crtc_w;
+	req.crtc_h = crtc_h;
+	req.src_x = src_x;
+	req.src_y = src_y;
+	req.src_w = src_w;
+	req.src_h = src_h;
+	req.fb = fb;
+	req.crtc = crtc;
+
+	ret = atmel_hlcdc_plane_prepare_update_req(&plane->base, &req);
+	if (ret)
+		return ret;
+
+	if (!req.crtc_h || !req.crtc_w)
+		return atmel_hlcdc_layer_disable(&plane->layer);
+
+	return atmel_hlcdc_plane_apply_update_req(&plane->base, &req);
+}
+
+static int atmel_hlcdc_plane_disable(struct drm_plane *p)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+
+	return atmel_hlcdc_layer_disable(&plane->layer);
+}
+
+static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+
+	if (plane->base.fb)
+		drm_framebuffer_unreference(plane->base.fb);
+
+	atmel_hlcdc_layer_cleanup(p->dev, &plane->layer);
+
+	drm_plane_cleanup(p);
+	devm_kfree(p->dev->dev, plane);
+}
+
+static int atmel_hlcdc_plane_set_alpha(struct atmel_hlcdc_plane *plane,
+				       u8 alpha)
+{
+	atmel_hlcdc_layer_update_start(&plane->layer);
+	atmel_hlcdc_layer_update_cfg(&plane->layer,
+				     plane->layer.desc->layout.general_config,
+				     ATMEL_HLCDC_LAYER_GA_MASK,
+				     alpha << ATMEL_HLCDC_LAYER_GA_SHIFT);
+	atmel_hlcdc_layer_update_commit(&plane->layer);
+
+	return 0;
+}
+
+static int atmel_hlcdc_plane_set_rotation(struct atmel_hlcdc_plane *plane,
+					  enum atmel_hlcdc_plane_rotation rot)
+{
+	plane->rotation = rot;
+
+	return 0;
+}
+
+static int atmel_hlcdc_plane_set_property(struct drm_plane *p,
+					  struct drm_property *property,
+					  uint64_t value)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	struct atmel_hlcdc_plane_properties *props = plane->properties;
+
+	if (property == props->alpha)
+		atmel_hlcdc_plane_set_alpha(plane, value);
+	else if (property == props->rotation)
+		atmel_hlcdc_plane_set_rotation(plane, value);
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
+				const struct atmel_hlcdc_layer_desc *desc,
+				struct atmel_hlcdc_plane_properties *props)
+{
+	struct regmap *regmap = plane->layer.hlcdc->regmap;
+
+	if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
+	    desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
+		drm_object_attach_property(&plane->base.base,
+					   props->alpha, 255);
+
+		/* Set default alpha value */
+		regmap_update_bits(regmap,
+				desc->regs_offset +
+				ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer),
+				ATMEL_HLCDC_LAYER_GA_MASK,
+				ATMEL_HLCDC_LAYER_GA_MASK);
+	}
+
+	if (desc->layout.xstride && desc->layout.pstride)
+		drm_object_attach_property(&plane->base.base,
+					   props->rotation,
+					   ATMEL_HLCDC_PLANE_NO_ROTATION);
+
+	if (desc->layout.csc) {
+		/*
+		 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
+		 * userspace modify these factors (using a BLOB property ?).
+		 */
+		regmap_write(regmap,
+			     desc->regs_offset +
+			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0),
+			     0x4c900091);
+		regmap_write(regmap,
+			     desc->regs_offset +
+			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1),
+			     0x7a5f5090);
+		regmap_write(regmap,
+			     desc->regs_offset +
+			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2),
+			     0x40040890);
+	}
+}
+
+static struct drm_plane_funcs layer_plane_funcs = {
+	.update_plane = atmel_hlcdc_plane_update,
+	.disable_plane = atmel_hlcdc_plane_disable,
+	.set_property = atmel_hlcdc_plane_set_property,
+	.destroy = atmel_hlcdc_plane_destroy,
+};
+
+static struct atmel_hlcdc_plane *
+atmel_hlcdc_plane_create(struct drm_device *dev,
+			 const struct atmel_hlcdc_layer_desc *desc,
+			 struct atmel_hlcdc_plane_properties *props)
+{
+	struct atmel_hlcdc_plane *plane;
+	enum drm_plane_type type;
+	int ret;
+
+	plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
+	if (!plane)
+		return ERR_PTR(-ENOMEM);
+
+	ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc);
+	if (ret)
+		return ERR_PTR(ret);
+
+	if (desc->type == ATMEL_HLCDC_BASE_LAYER)
+		type = DRM_PLANE_TYPE_PRIMARY;
+	else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)
+		type = DRM_PLANE_TYPE_CURSOR;
+	else
+		type = DRM_PLANE_TYPE_OVERLAY;
+
+	ret = drm_universal_plane_init(dev, &plane->base, 0,
+				       &layer_plane_funcs,
+				       desc->formats->formats,
+				       desc->formats->nformats, type);
+	if (ret)
+		return ERR_PTR(ret);
+
+	/* Set default property values*/
+	atmel_hlcdc_plane_init_properties(plane, desc, props);
+
+	return plane;
+}
+
+static struct atmel_hlcdc_plane_properties *
+atmel_hlcdc_plane_create_properties(struct drm_device *dev)
+{
+	struct atmel_hlcdc_plane_properties *props;
+	const struct drm_prop_enum_list rotations[] = {
+		{ ATMEL_HLCDC_PLANE_NO_ROTATION,   "rotate-0" },
+		{ ATMEL_HLCDC_PLANE_90DEG_ROTATION,  "rotate-90" },
+		{ ATMEL_HLCDC_PLANE_180DEG_ROTATION, "rotate-180" },
+		{ ATMEL_HLCDC_PLANE_270DEG_ROTATION, "rotate-270" },
+	};
+
+	props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL);
+	if (!props)
+		return ERR_PTR(-ENOMEM);
+
+	props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255);
+	if (!props->alpha)
+		return ERR_PTR(-ENOMEM);
+
+	props->rotation = drm_property_create_enum(dev, 0, "rotation",
+						rotations,
+						ARRAY_SIZE(rotations));
+	return props;
+}
+
+struct atmel_hlcdc_planes *
+atmel_hlcdc_create_planes(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct atmel_hlcdc_plane_properties *props;
+	struct atmel_hlcdc_planes *planes;
+	const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
+	int nlayers = dc->desc->nlayers;
+	int i;
+
+	planes = devm_kzalloc(dev->dev, sizeof(*planes), GFP_KERNEL);
+	if (!planes)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < nlayers; i++) {
+		if (descs[i].type == ATMEL_HLCDC_OVERLAY_LAYER)
+			planes->noverlays++;
+	}
+
+	if (planes->noverlays) {
+		planes->overlays = devm_kzalloc(dev->dev,
+						planes->noverlays *
+						sizeof(*planes->overlays),
+						GFP_KERNEL);
+		if (!planes->overlays)
+			return ERR_PTR(-ENOMEM);
+	}
+
+	props = atmel_hlcdc_plane_create_properties(dev);
+	if (IS_ERR(props))
+		return ERR_CAST(props);
+
+	planes->noverlays = 0;
+	for (i = 0; i < nlayers; i++) {
+		struct atmel_hlcdc_plane *plane;
+
+		if (descs[i].type == ATMEL_HLCDC_PP_LAYER)
+			continue;
+
+		plane = atmel_hlcdc_plane_create(dev, &descs[i], props);
+		if (IS_ERR(plane))
+			return ERR_CAST(plane);
+
+		plane->properties = props;
+
+		switch (descs[i].type) {
+		case ATMEL_HLCDC_BASE_LAYER:
+			if (planes->primary)
+				return ERR_PTR(-EINVAL);
+			planes->primary = plane;
+			break;
+
+		case ATMEL_HLCDC_OVERLAY_LAYER:
+			planes->overlays[planes->noverlays++] = plane;
+			break;
+
+		case ATMEL_HLCDC_CURSOR_LAYER:
+			if (planes->cursor)
+				return ERR_PTR(-EINVAL);
+			planes->cursor = plane;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	return planes;
+}
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 05/11] drm: add Atmel HLCDC Display Controller support
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, linux-kernel,
	Rob Herring, Bo Shen, Kumar Gala, linux-arm-kernel

The Atmel HLCDC (HLCD Controller) IP available on some Atmel SoCs (i.e.
at91sam9n12, at91sam9x5 family or sama5d3 family) provides a display
controller device.

This display controller supports at least one primary plane and might
provide several overlays and an hardware cursor depending on the IP
version.

At the moment, this driver only implements an RGB connector to interface
with LCD panels, but support for other kind of external devices might be
added later.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 drivers/gpu/drm/Kconfig                          |   2 +
 drivers/gpu/drm/Makefile                         |   1 +
 drivers/gpu/drm/atmel-hlcdc/Kconfig              |  11 +
 drivers/gpu/drm/atmel-hlcdc/Makefile             |   7 +
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c   | 286 ++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c     | 488 ++++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h     | 224 +++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c  | 635 ++++++++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h  | 396 +++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 478 ++++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c  | 804 +++++++++++++++++++++++
 11 files changed, 3332 insertions(+)
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/Kconfig
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/Makefile
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f512004..9183a78 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -184,6 +184,8 @@ source "drivers/gpu/drm/cirrus/Kconfig"
 
 source "drivers/gpu/drm/armada/Kconfig"
 
+source "drivers/gpu/drm/atmel-hlcdc/Kconfig"
+
 source "drivers/gpu/drm/rcar-du/Kconfig"
 
 source "drivers/gpu/drm/shmobile/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index af9a609..07d388c 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
 obj-$(CONFIG_DRM_ARMADA) += armada/
+obj-$(CONFIG_DRM_ATMEL_HLCDC)	+= atmel-hlcdc/
 obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-$(CONFIG_DRM_OMAP)	+= omapdrm/
diff --git a/drivers/gpu/drm/atmel-hlcdc/Kconfig b/drivers/gpu/drm/atmel-hlcdc/Kconfig
new file mode 100644
index 0000000..bc07315
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/Kconfig
@@ -0,0 +1,11 @@
+config DRM_ATMEL_HLCDC
+	tristate "DRM Support for ATMEL HLCDC Display Controller"
+	depends on DRM && OF && MFD_ATMEL_HLCDC && COMMON_CLK
+	select DRM_GEM_CMA_HELPER
+	select DRM_KMS_HELPER
+	select DRM_KMS_FB_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_PANEL
+	help
+	  Choose this option if you have an ATMEL SoC with an HLCDC display
+	  controller (i.e. at91sam9n12, at91sam9x5 family or sama5d3 family).
diff --git a/drivers/gpu/drm/atmel-hlcdc/Makefile b/drivers/gpu/drm/atmel-hlcdc/Makefile
new file mode 100644
index 0000000..10ae426
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/Makefile
@@ -0,0 +1,7 @@
+atmel-hlcdc-dc-y := atmel_hlcdc_crtc.o \
+		atmel_hlcdc_dc.o \
+		atmel_hlcdc_layer.o \
+		atmel_hlcdc_output.o \
+		atmel_hlcdc_plane.o
+
+obj-$(CONFIG_DRM_ATMEL_HLCDC)	+= atmel-hlcdc-dc.o
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
new file mode 100644
index 0000000..2186830
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2014 Free Electrons
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drmP.h>
+
+#include <video/videomode.h>
+
+#include "atmel_hlcdc_dc.h"
+
+/**
+ * Atmel HLCDC CRTC structure
+ *
+ * @base: base DRM CRTC structure
+ * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
+ * @event: pointer to the current page flip event
+ * @id: CRTC id (returned by drm_crtc_index)
+ * @dpms: DPMS mode
+ */
+struct atmel_hlcdc_crtc {
+	struct drm_crtc base;
+	struct atmel_hlcdc *hlcdc;
+	struct drm_pending_vblank_event *event;
+	int id;
+	int dpms;
+};
+
+static inline struct atmel_hlcdc_crtc *
+drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct atmel_hlcdc_crtc, base);
+}
+
+
+static void atmel_hlcdc_crtc_dpms(struct drm_crtc *c, int mode)
+{
+	struct drm_device *dev = c->dev;
+
+	if (mode != DRM_MODE_DPMS_ON)
+		mode = DRM_MODE_DPMS_OFF;
+
+	pm_runtime_get_sync(dev->dev);
+
+	if (mode == DRM_MODE_DPMS_ON)
+		pm_runtime_forbid(dev->dev);
+	else
+		pm_runtime_allow(dev->dev);
+
+	pm_runtime_put_sync(dev->dev);
+}
+
+static int atmel_hlcdc_crtc_mode_set(struct drm_crtc *c,
+				     struct drm_display_mode *mode,
+				     struct drm_display_mode *adjusted,
+				     int x, int y,
+				     struct drm_framebuffer *old_fb)
+{
+	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
+	struct regmap *regmap = crtc->hlcdc->regmap;
+	struct drm_plane *plane = c->primary;
+	struct drm_framebuffer *fb;
+	struct videomode vm;
+
+	vm.vfront_porch = mode->vsync_start - mode->vdisplay;
+	vm.vback_porch = mode->vtotal - mode->vsync_end;
+	vm.vsync_len = mode->vsync_end - mode->vsync_start;
+	vm.hfront_porch = mode->hsync_start - mode->hdisplay;
+	vm.hback_porch = mode->htotal - mode->hsync_end;
+	vm.hsync_len = mode->hsync_end - mode->hsync_start;
+
+	if (vm.hsync_len > 0x40 || vm.hsync_len < 1 ||
+	    vm.vsync_len > 0x40 || vm.vsync_len < 1 ||
+	    vm.vfront_porch > 0x40 || vm.vfront_porch < 1 ||
+	    vm.vback_porch > 0x40 || vm.vback_porch < 0 ||
+	    vm.hfront_porch > 0x200 || vm.hfront_porch < 1 ||
+	    vm.hback_porch > 0x200 || vm.hback_porch < 1 ||
+	    mode->hdisplay > 2048 || mode->hdisplay < 1 ||
+	    mode->vdisplay > 2048 || mode->vdisplay < 1)
+		return -EINVAL;
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(1),
+		     (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16));
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(2),
+		     (vm.vfront_porch - 1) | (vm.vback_porch << 16));
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(3),
+		     (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16));
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(4),
+		     (mode->hdisplay - 1) | ((mode->vdisplay - 1) << 16));
+
+	fb = plane->fb;
+	plane->fb = old_fb;
+
+	return plane->funcs->update_plane(plane, c, fb,
+					  0, 0,
+					  mode->hdisplay, mode->vdisplay,
+					  c->x << 16, c->y << 16,
+					  mode->hdisplay << 16,
+					  mode->vdisplay << 16);
+}
+
+static void atmel_hlcdc_crtc_prepare(struct drm_crtc *crtc)
+{
+	atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static void atmel_hlcdc_crtc_commit(struct drm_crtc *crtc)
+{
+	atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *crtc,
+					const struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+
+static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = {
+
+	.mode_fixup = atmel_hlcdc_crtc_mode_fixup,
+	.dpms = atmel_hlcdc_crtc_dpms,
+	.mode_set = atmel_hlcdc_crtc_mode_set,
+	.prepare = atmel_hlcdc_crtc_prepare,
+	.commit = atmel_hlcdc_crtc_commit,
+};
+
+static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c)
+{
+	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
+
+	drm_crtc_cleanup(c);
+	kfree(crtc);
+}
+
+void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *c,
+				       struct drm_file *file)
+{
+	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
+	struct drm_pending_vblank_event *event;
+	struct drm_device *dev = c->dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	event = crtc->event;
+	if (event && event->base.file_priv == file) {
+		event->base.destroy(&event->base);
+		drm_vblank_put(dev, crtc->id);
+		crtc->event = NULL;
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static void atmel_hlcdc_crtc_finish_page_flip(void *data)
+{
+	struct atmel_hlcdc_crtc *crtc = data;
+	struct drm_device *dev = crtc->base.dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	if (crtc->event) {
+		drm_send_vblank_event(dev, crtc->id, crtc->event);
+		drm_vblank_put(dev, crtc->id);
+		crtc->event = NULL;
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int atmel_hlcdc_crtc_page_flip(struct drm_crtc *c,
+				      struct drm_framebuffer *fb,
+				      struct drm_pending_vblank_event *event,
+				      uint32_t page_flip_flags)
+{
+	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
+	struct atmel_hlcdc_plane_update_req req;
+	struct drm_plane *plane = c->primary;
+	int ret;
+
+	if (crtc->event)
+		return -EBUSY;
+
+	memset(&req, 0, sizeof(req));
+	req.crtc_x = 0;
+	req.crtc_y = 0;
+	req.crtc_h = c->mode.crtc_vdisplay;
+	req.crtc_w = c->mode.crtc_hdisplay;
+	req.src_x = c->x << 16;
+	req.src_y = c->y << 16;
+	req.src_w = req.crtc_w << 16;
+	req.src_h = req.crtc_h << 16;
+	req.fb = fb;
+	req.crtc = c;
+	req.finished = atmel_hlcdc_crtc_finish_page_flip;
+	req.finished_data = crtc;
+
+	ret = atmel_hlcdc_plane_prepare_update_req(plane, &req);
+	if (ret)
+		return ret;
+
+	if (event) {
+		crtc->event = event;
+		drm_vblank_get(c->dev, crtc->id);
+	}
+
+	ret = atmel_hlcdc_plane_apply_update_req(plane, &req);
+	if (ret) {
+		crtc->event = NULL;
+		drm_vblank_put(c->dev, crtc->id);
+	} else {
+		plane->fb = fb;
+	}
+
+	return ret;
+}
+
+static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
+	.page_flip = atmel_hlcdc_crtc_page_flip,
+	.set_config = drm_crtc_helper_set_config,
+	.destroy = atmel_hlcdc_crtc_destroy,
+};
+
+int atmel_hlcdc_crtc_create(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct atmel_hlcdc_planes *planes = dc->planes;
+	struct atmel_hlcdc_crtc *crtc;
+	int ret;
+	int i;
+
+	crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
+	if (!crtc) {
+		dev_err(dev->dev, "allocation failed\n");
+		return -ENOMEM;
+	}
+
+	crtc->hlcdc = dc->hlcdc;
+
+	ret = drm_crtc_init_with_planes(dev, &crtc->base,
+				&planes->primary->base,
+				planes->cursor ? &planes->cursor->base : NULL,
+				&atmel_hlcdc_crtc_funcs);
+	if (ret < 0)
+		goto fail;
+
+	crtc->id = drm_crtc_index(&crtc->base);
+
+	if (planes->cursor)
+		planes->cursor->base.possible_crtcs = 1 << crtc->id;
+
+	for (i = 0; i < planes->noverlays; i++)
+		planes->overlays[i]->base.possible_crtcs = 1 << crtc->id;
+
+	drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
+
+	return 0;
+
+fail:
+	atmel_hlcdc_crtc_destroy(&crtc->base);
+	return ret;
+}
+
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
new file mode 100644
index 0000000..9581977
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#include "atmel_hlcdc_dc.h"
+
+#define ATMEL_HLCDC_LAYER_IRQS_OFFSET		8
+
+static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
+	{
+		.name = "base",
+		.formats = &atmel_hlcdc_plane_rgb_formats,
+		.regs_offset = 0x40,
+		.id = 0,
+		.type = ATMEL_HLCDC_BASE_LAYER,
+		.nconfigs = 7,
+		.layout = {
+			.xstride = { 2 },
+			.default_color = 3,
+			.general_config = 4,
+			.disc_pos = 5,
+			.disc_size = 6,
+		},
+	},
+	{
+		.name = "overlay1",
+		.formats = &atmel_hlcdc_plane_rgb_formats,
+		.regs_offset = 0x140,
+		.id = 1,
+		.type = ATMEL_HLCDC_OVERLAY_LAYER,
+		.nconfigs = 10,
+		.layout = {
+			.pos = 2,
+			.size = 3,
+			.xstride = { 4 },
+			.pstride = { 5 },
+			.default_color = 6,
+			.chroma_key = 7,
+			.chroma_key_mask = 8,
+			.general_config = 9,
+		},
+	},
+	{
+		.name = "overlay2",
+		.formats = &atmel_hlcdc_plane_rgb_formats,
+		.regs_offset = 0x240,
+		.id = 2,
+		.type = ATMEL_HLCDC_OVERLAY_LAYER,
+		.nconfigs = 10,
+		.layout = {
+			.pos = 2,
+			.size = 3,
+			.xstride = { 4 },
+			.pstride = { 5 },
+			.default_color = 6,
+			.chroma_key = 7,
+			.chroma_key_mask = 8,
+			.general_config = 9,
+		},
+	},
+	{
+		.name = "high-end-overlay",
+		.formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
+		.regs_offset = 0x340,
+		.id = 3,
+		.type = ATMEL_HLCDC_OVERLAY_LAYER,
+		.nconfigs = 42,
+		.layout = {
+			.pos = 2,
+			.size = 3,
+			.memsize = 4,
+			.xstride = { 5, 7 },
+			.pstride = { 6, 8 },
+			.default_color = 9,
+			.chroma_key = 10,
+			.chroma_key_mask = 11,
+			.general_config = 12,
+			.csc = 14,
+		},
+	},
+	{
+		.name = "cursor",
+		.formats = &atmel_hlcdc_plane_rgb_formats,
+		.regs_offset = 0x440,
+		.id = 4,
+		.type = ATMEL_HLCDC_CURSOR_LAYER,
+		.nconfigs = 10,
+		.max_width = 128,
+		.max_height = 128,
+		.layout = {
+			.pos = 2,
+			.size = 3,
+			.xstride = { 4 },
+			.pstride = { 5 },
+			.default_color = 6,
+			.chroma_key = 7,
+			.chroma_key_mask = 8,
+			.general_config = 9,
+		},
+	},
+};
+
+static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = {
+	.min_width = 0,
+	.min_height = 0,
+	.max_width = 2048,
+	.max_height = 2048,
+	.nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers),
+	.layers = atmel_hlcdc_sama5d3_layers,
+};
+
+static const struct of_device_id atmel_hlcdc_of_match[] = {
+	{
+		.compatible = "atmel,sama5d3-hlcdc",
+		.data = &atmel_hlcdc_dc_sama5d3,
+	},
+	{ /* sentinel */ },
+};
+
+static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
+{
+	struct drm_device *dev = data;
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	unsigned long status;
+	unsigned int imr, isr;
+	int bit;
+
+	regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr);
+	regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
+	status = imr & isr;
+	if (!status)
+		return IRQ_NONE;
+
+	bit = ATMEL_HLCDC_LAYER_IRQS_OFFSET;
+	for_each_set_bit_from(bit, &status, ATMEL_HLCDC_LAYER_IRQS_OFFSET +
+					    ATMEL_HLCDC_MAX_LAYERS) {
+		int layerid = bit - ATMEL_HLCDC_LAYER_IRQS_OFFSET;
+		struct atmel_hlcdc_layer *layer = dc->layers[layerid];
+
+		if (layer)
+			atmel_hlcdc_layer_irq(layer);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev,
+		struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	return drm_fb_cma_create(dev, file_priv, mode_cmd);
+}
+
+static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+
+	if (dc->fbdev) {
+		drm_fbdev_cma_hotplug_event(dc->fbdev);
+	} else {
+		dc->fbdev = drm_fbdev_cma_init(dev, 24,
+				dev->mode_config.num_crtc,
+				dev->mode_config.num_connector);
+		if (IS_ERR(dc->fbdev))
+			dc->fbdev = NULL;
+	}
+}
+
+static const struct drm_mode_config_funcs mode_config_funcs = {
+	.fb_create = atmel_hlcdc_fb_create,
+	.output_poll_changed = atmel_hlcdc_fb_output_poll_changed,
+};
+
+static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct atmel_hlcdc_planes *planes;
+	int ret;
+	int i;
+
+	drm_mode_config_init(dev);
+
+	ret = atmel_hlcdc_create_outputs(dev);
+	if (ret) {
+		dev_err(dev->dev, "failed to create panel: %d\n", ret);
+		return ret;
+	}
+
+	planes = atmel_hlcdc_create_planes(dev);
+	if (IS_ERR(planes)) {
+		dev_err(dev->dev, "failed to create planes\n");
+		return PTR_ERR(planes);
+	}
+
+	dc->planes = planes;
+
+	dc->layers[planes->primary->layer.desc->id] =
+						&planes->primary->layer;
+
+	if (planes->cursor)
+		dc->layers[planes->cursor->layer.desc->id] =
+							&planes->cursor->layer;
+
+	for (i = 0; i < planes->noverlays; i++)
+		dc->layers[planes->overlays[i]->layer.desc->id] =
+						&planes->overlays[i]->layer;
+
+	ret = atmel_hlcdc_crtc_create(dev);
+	if (ret) {
+		dev_err(dev->dev, "failed to create crtc\n");
+		return ret;
+	}
+
+	dev->mode_config.min_width = dc->desc->min_width;
+	dev->mode_config.min_height = dc->desc->min_height;
+	dev->mode_config.max_width = dc->desc->max_width;
+	dev->mode_config.max_height = dc->desc->max_height;
+	dev->mode_config.funcs = &mode_config_funcs;
+
+	return 0;
+}
+
+static int atmel_hlcdc_dc_load(struct drm_device *dev, unsigned long flags)
+{
+	struct platform_device *pdev = dev->platformdev;
+	const struct of_device_id *match;
+	struct atmel_hlcdc_dc *dc;
+	int irq;
+	int ret;
+
+	match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node);
+	if (!match) {
+		dev_err(&pdev->dev, "invalid compatible string\n");
+		return -ENODEV;
+	}
+
+	if (!match->data) {
+		dev_err(&pdev->dev, "invalid hlcdc description\n");
+		return -EINVAL;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL);
+	if (!dc) {
+		dev_err(dev->dev, "failed to allocate private data\n");
+		return -ENOMEM;
+	}
+
+	dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0);
+	if (!dc->wq)
+		return -ENOMEM;
+
+	dc->desc = match->data;
+	dc->hlcdc = dev_get_drvdata(dev->dev->parent);
+	dev->dev_private = dc;
+
+	ret = clk_prepare_enable(dc->hlcdc->periph_clk);
+	if (ret) {
+		dev_err(dev->dev, "failed to enable periph_clk\n");
+		goto err_destroy_wq;
+	}
+
+	pm_runtime_enable(dev->dev);
+
+	pm_runtime_put_sync(dev->dev);
+
+	ret = atmel_hlcdc_dc_modeset_init(dev);
+	if (ret < 0) {
+		dev_err(dev->dev, "failed to initialize mode setting\n");
+		goto err_periph_clk_disable;
+	}
+
+	ret = drm_vblank_init(dev, 1);
+	if (ret < 0) {
+		dev_err(dev->dev, "failed to initialize vblank\n");
+		goto err_periph_clk_disable;
+	}
+
+	pm_runtime_get_sync(dev->dev);
+	ret = drm_irq_install(dev, irq);
+	pm_runtime_put_sync(dev->dev);
+	if (ret < 0) {
+		dev_err(dev->dev, "failed to install IRQ handler\n");
+		goto err_periph_clk_disable;
+	}
+
+	platform_set_drvdata(pdev, dev);
+
+	drm_kms_helper_poll_init(dev);
+
+	/* force connectors detection */
+	drm_helper_hpd_irq_event(dev);
+
+	return 0;
+
+err_periph_clk_disable:
+	clk_disable_unprepare(dc->hlcdc->periph_clk);
+
+err_destroy_wq:
+	destroy_workqueue(dc->wq);
+
+	return ret;
+}
+
+static int atmel_hlcdc_dc_unload(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+
+	drm_kms_helper_poll_fini(dev);
+	drm_mode_config_cleanup(dev);
+	drm_vblank_cleanup(dev);
+
+	pm_runtime_get_sync(dev->dev);
+	drm_irq_uninstall(dev);
+	pm_runtime_put_sync(dev->dev);
+
+	dev->dev_private = NULL;
+
+	pm_runtime_disable(dev->dev);
+	clk_disable_unprepare(dc->hlcdc->periph_clk);
+
+	flush_workqueue(dc->wq);
+	destroy_workqueue(dc->wq);
+
+	return 0;
+}
+
+static void atmel_hlcdc_dc_preclose(struct drm_device *dev,
+				    struct drm_file *file)
+{
+	struct drm_crtc *crtc;
+
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+		atmel_hlcdc_crtc_cancel_page_flip(crtc, file);
+}
+
+static void atmel_hlcdc_dc_lastclose(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+
+	drm_fbdev_cma_restore_mode(dc->fbdev);
+}
+
+static void atmel_hlcdc_dc_irq_preinstall(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	unsigned int isr;
+
+	regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff);
+	regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
+}
+
+static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	int i;
+
+	/* Enable interrupts on activated layers */
+	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
+		if (dc->layers[i])
+			regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER,
+				     BIT(i + 8));
+	}
+
+	return 0;
+}
+
+static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev)
+{
+
+}
+
+static int atmel_hlcdc_dc_enable_vblank(struct drm_device *dev, int crtc)
+{
+	return 0;
+}
+
+static void atmel_hlcdc_dc_disable_vblank(struct drm_device *dev, int crtc)
+{
+}
+
+static const struct file_operations fops = {
+	.owner              = THIS_MODULE,
+	.open               = drm_open,
+	.release            = drm_release,
+	.unlocked_ioctl     = drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl       = drm_compat_ioctl,
+#endif
+	.poll               = drm_poll,
+	.read               = drm_read,
+	.llseek             = no_llseek,
+	.mmap               = drm_gem_cma_mmap,
+};
+
+static struct drm_driver atmel_hlcdc_dc_driver = {
+	.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET,
+	.load = atmel_hlcdc_dc_load,
+	.unload = atmel_hlcdc_dc_unload,
+	.preclose = atmel_hlcdc_dc_preclose,
+	.lastclose = atmel_hlcdc_dc_lastclose,
+	.irq_handler = atmel_hlcdc_dc_irq_handler,
+	.irq_preinstall = atmel_hlcdc_dc_irq_preinstall,
+	.irq_postinstall = atmel_hlcdc_dc_irq_postinstall,
+	.irq_uninstall = atmel_hlcdc_dc_irq_uninstall,
+	.get_vblank_counter = drm_vblank_count,
+	.enable_vblank = atmel_hlcdc_dc_enable_vblank,
+	.disable_vblank = atmel_hlcdc_dc_disable_vblank,
+	.gem_free_object = drm_gem_cma_free_object,
+	.gem_vm_ops = &drm_gem_cma_vm_ops,
+	.dumb_create = drm_gem_cma_dumb_create,
+	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
+	.dumb_destroy = drm_gem_dumb_destroy,
+	.fops = &fops,
+	.name = "atmel-hlcdc",
+	.desc = "Atmel HLCD Controller DRM",
+	.date = "20141504",
+	.major = 1,
+	.minor = 0,
+};
+
+static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+	if (ret)
+		return ret;
+
+	ret = drm_platform_init(&atmel_hlcdc_dc_driver, pdev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
+{
+	drm_put_dev(platform_get_drvdata(pdev));
+
+	return 0;
+}
+
+static const struct of_device_id atmel_hlcdc_dc_of_match[] = {
+	{ .compatible = "atmel,hlcdc-display-controller" },
+	{ },
+};
+
+static struct platform_driver atmel_hlcdc_dc_platform_driver = {
+	.probe	= atmel_hlcdc_dc_drm_probe,
+	.remove	= atmel_hlcdc_dc_drm_remove,
+	.driver	= {
+		.name	= "atmel-hlcdc-display-controller",
+		.of_match_table = atmel_hlcdc_dc_of_match,
+	},
+};
+module_platform_driver(atmel_hlcdc_dc_platform_driver);
+
+MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>");
+MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atmel-hlcdc-dc");
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
new file mode 100644
index 0000000..a67df13
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DRM_ATMEL_HLCDC_H
+#define DRM_ATMEL_HLCDC_H
+
+#include <linux/clk.h>
+#include <linux/irqdomain.h>
+#include <linux/pwm.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drmP.h>
+
+#include "atmel_hlcdc_layer.h"
+
+#define ATMEL_HLCDC_MAX_LAYERS		5
+
+/**
+ * Atmel HLCDC Display Controller description structure.
+ *
+ * This structure describe the HLCDC IP capabilities and depends on the
+ * HLCDC IP version (or Atmel SoC family).
+ *
+ * @min_width: minimum width supported by the Display Controller
+ * @min_height: minimum height supported by the Display Controller
+ * @max_width: maximum width supported by the Display Controller
+ * @max_height: maximum height supported by the Display Controller
+ * @layer: a layer description table describing available layers
+ * @nlayers: layer description table size
+ */
+struct atmel_hlcdc_dc_desc {
+	int min_width;
+	int min_height;
+	int max_width;
+	int max_height;
+	const struct atmel_hlcdc_layer_desc *layers;
+	int nlayers;
+};
+
+/**
+ * Atmel HLCDC Plane properties.
+ *
+ * This structure stores plane property definitions.
+ *
+ * @alpha: alpha blending (or transparency) property
+ * @csc: YUV to RGB conversion factors property
+ */
+struct atmel_hlcdc_plane_properties {
+	struct drm_property *alpha;
+	struct drm_property *rotation;
+};
+
+/**
+ * Atmel HLCDC plane rotation enum
+ *
+ * TODO: export DRM_ROTATE_XX macros defined by omap driver and use them
+ * instead of defining this enum.
+ */
+enum atmel_hlcdc_plane_rotation {
+	ATMEL_HLCDC_PLANE_NO_ROTATION,
+	ATMEL_HLCDC_PLANE_90DEG_ROTATION,
+	ATMEL_HLCDC_PLANE_180DEG_ROTATION,
+	ATMEL_HLCDC_PLANE_270DEG_ROTATION,
+};
+
+/**
+ * Atmel HLCDC Plane.
+ *
+ * @base: base DRM plane structure
+ * @layer: HLCDC layer structure
+ * @properties: pointer to the property definitions structure
+ * @alpha: current alpha blending (or transparency) status
+ */
+struct atmel_hlcdc_plane {
+	struct drm_plane base;
+	struct atmel_hlcdc_layer layer;
+	struct atmel_hlcdc_plane_properties *properties;
+	enum atmel_hlcdc_plane_rotation rotation;
+};
+
+static inline struct atmel_hlcdc_plane *
+drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p)
+{
+	return container_of(p, struct atmel_hlcdc_plane, base);
+}
+
+static inline struct atmel_hlcdc_plane *
+atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *l)
+{
+	return container_of(l, struct atmel_hlcdc_plane, layer);
+}
+
+/**
+ * Atmel HLCDC Plane update request structure.
+ *
+ * @crtc_x: x position of the plane relative to the CRTC
+ * @crtc_y: y position of the plane relative to the CRTC
+ * @crtc_w: visible width of the plane
+ * @crtc_h: visible height of the plane
+ * @src_x: x buffer position
+ * @src_y: y buffer position
+ * @src_w: buffer width
+ * @src_h: buffer height
+ * @pixel_format: pixel format
+ * @gems: GEM object object containing image buffers
+ * @offsets: offsets to apply to the GEM buffers
+ * @pitches: line size in bytes
+ * @crtc: crtc to display on
+ * @finished: finished callback
+ * @finished_data: data passed to the finished callback
+ * @bpp: bytes per pixel deduced from pixel_format
+ * @xstride: value to add to the pixel pointer between each line
+ * @pstride: value to add to the pixel pointer between each pixel
+ * @nplanes: number of planes (deduced from pixel_format)
+ */
+struct atmel_hlcdc_plane_update_req {
+	int crtc_x;
+	int crtc_y;
+	unsigned int crtc_w;
+	unsigned int crtc_h;
+	uint32_t src_x;
+	uint32_t src_y;
+	uint32_t src_w;
+	uint32_t src_h;
+	struct drm_framebuffer *fb;
+	struct drm_crtc *crtc;
+	void (*finished)(void *data);
+	void *finished_data;
+
+	/* These fields are private and should not be touched */
+	int bpp[ATMEL_HLCDC_MAX_PLANES];
+	unsigned int offsets[ATMEL_HLCDC_MAX_PLANES];
+	int xstride[ATMEL_HLCDC_MAX_PLANES];
+	int pstride[ATMEL_HLCDC_MAX_PLANES];
+	int nplanes;
+};
+
+/**
+ * Atmel HLCDC Planes.
+ *
+ * This structure stores the instantiated HLCDC Planes and can be accessed by
+ * the HLCDC Display Controller or the HLCDC CRTC.
+ *
+ * @primary: primary plane
+ * @cursor: hardware cursor plane
+ * @overlays: overlay plane table
+ * @noverlays: number of overlay planes
+ */
+struct atmel_hlcdc_planes {
+	struct atmel_hlcdc_plane *primary;
+	struct atmel_hlcdc_plane *cursor;
+	struct atmel_hlcdc_plane **overlays;
+	int noverlays;
+};
+
+/**
+ * Atmel HLCDC Display Controller.
+ *
+ * @desc: HLCDC Display Controller description
+ * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
+ * @fbdev: framebuffer device attached to the Display Controller
+ * @planes: instantiated planes
+ * @layers: active HLCDC layer
+ * @wq: display controller workqueue
+ */
+struct atmel_hlcdc_dc {
+	const struct atmel_hlcdc_dc_desc *desc;
+	struct atmel_hlcdc *hlcdc;
+	struct drm_fbdev_cma *fbdev;
+	struct atmel_hlcdc_planes *planes;
+	struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS];
+	struct workqueue_struct *wq;
+};
+
+extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats;
+extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats;
+
+struct atmel_hlcdc_planes *
+atmel_hlcdc_create_planes(struct drm_device *dev);
+
+int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req);
+
+int atmel_hlcdc_plane_apply_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req);
+
+void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *crtc,
+				       struct drm_file *file);
+
+int atmel_hlcdc_crtc_create(struct drm_device *dev);
+
+int atmel_hlcdc_create_outputs(struct drm_device *dev);
+
+struct atmel_hlcdc_pwm_chip *atmel_hlcdc_pwm_create(struct drm_device *dev,
+						    struct clk *slow_clk,
+						    struct clk *sys_clk,
+						    void __iomem *regs);
+
+int atmel_hlcdc_pwm_destroy(struct drm_device *dev,
+			    struct atmel_hlcdc_pwm_chip *chip);
+
+#endif /* DRM_ATMEL_HLCDC_H */
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
new file mode 100644
index 0000000..d31c2e4
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
@@ -0,0 +1,635 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+
+#include "atmel_hlcdc_dc.h"
+
+static void
+atmel_hlcdc_layer_fb_flip_release(struct drm_flip_work *work, void *val)
+{
+	struct atmel_hlcdc_layer_fb_flip *flip = val;
+
+	if (flip->fb)
+		drm_framebuffer_unreference(flip->fb);
+	kfree(flip);
+}
+
+static void
+atmel_hlcdc_layer_fb_flip_destroy(struct atmel_hlcdc_layer_fb_flip *flip)
+{
+	if (flip->fb)
+		drm_framebuffer_unreference(flip->fb);
+	kfree(flip->task);
+	kfree(flip);
+}
+
+static void
+atmel_hlcdc_layer_fb_flip_release_queue(struct atmel_hlcdc_layer *layer,
+					struct atmel_hlcdc_layer_fb_flip *flip)
+{
+	int i;
+
+	if (!flip)
+		return;
+
+	for (i = 0; i < layer->max_planes; i++) {
+		if (!flip->dscrs[i])
+			break;
+
+		flip->dscrs[i]->status = 0;
+		flip->dscrs[i] = NULL;
+	}
+
+	drm_flip_work_queue_task(&layer->gc, flip->task);
+	drm_flip_work_commit(&layer->gc, layer->wq);
+}
+
+static void atmel_hlcdc_layer_update_reset(struct atmel_hlcdc_layer *layer,
+					   int id)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_update_slot *slot;
+
+	if (id < 0 || id > 1)
+		return;
+
+	slot = &upd->slots[id];
+	bitmap_clear(slot->updated_configs, 0, layer->desc->nconfigs);
+	memset(slot->configs, 0,
+	       sizeof(*slot->configs) * layer->desc->nconfigs);
+
+	if (slot->fb_flip) {
+		atmel_hlcdc_layer_fb_flip_release_queue(layer, slot->fb_flip);
+		slot->fb_flip = NULL;
+	}
+}
+
+static void atmel_hlcdc_layer_update_apply(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct regmap *regmap = layer->hlcdc->regmap;
+	struct atmel_hlcdc_layer_update_slot *slot;
+	struct atmel_hlcdc_layer_fb_flip *fb_flip;
+	struct atmel_hlcdc_dma_channel_dscr *dscr;
+	unsigned int cfg;
+	u32 action = 0;
+	int i = 0;
+
+	if (upd->pending < 0 || upd->pending > 1 ||
+	    dma->status == ATMEL_HLCDC_LAYER_DISABLING)
+		return;
+
+	slot = &upd->slots[upd->pending];
+
+	for_each_set_bit(cfg, slot->updated_configs, layer->desc->nconfigs) {
+		regmap_write(regmap,
+			     desc->regs_offset +
+			     ATMEL_HLCDC_LAYER_CFG(layer, cfg),
+			     slot->configs[cfg]);
+		action |= ATMEL_HLCDC_LAYER_UPDATE;
+	}
+
+	fb_flip = slot->fb_flip;
+
+	if (!fb_flip->fb)
+		goto apply;
+
+	if (dma->status == ATMEL_HLCDC_LAYER_DISABLED) {
+		for (i = 0; i < fb_flip->ngems; i++) {
+			dscr =  fb_flip->dscrs[i];
+			dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
+				     ATMEL_HLCDC_LAYER_DMA_IRQ |
+				     ATMEL_HLCDC_LAYER_ADD_IRQ |
+				     ATMEL_HLCDC_LAYER_DONE_IRQ;
+
+			regmap_write(regmap,
+				     desc->regs_offset +
+				     ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
+				     dscr->addr);
+			regmap_write(regmap,
+				     desc->regs_offset +
+				     ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
+				     dscr->ctrl);
+			regmap_write(regmap,
+				     desc->regs_offset +
+				     ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
+				     dscr->next);
+		}
+
+		action |= ATMEL_HLCDC_LAYER_DMA_CHAN;
+		dma->status = ATMEL_HLCDC_LAYER_ENABLED;
+	} else {
+		for (i = 0; i < fb_flip->ngems; i++) {
+			dscr =  fb_flip->dscrs[i];
+			dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
+				     ATMEL_HLCDC_LAYER_DMA_IRQ |
+				     ATMEL_HLCDC_LAYER_DSCR_IRQ |
+				     ATMEL_HLCDC_LAYER_DONE_IRQ;
+
+			regmap_write(regmap,
+				     desc->regs_offset +
+				     ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
+				     dscr->next);
+		}
+
+		action |= ATMEL_HLCDC_LAYER_A2Q;
+	}
+
+	/* Release unneeded descriptors */
+	for (i = fb_flip->ngems; i < layer->max_planes; i++) {
+		fb_flip->dscrs[i]->status = 0;
+		fb_flip->dscrs[i] = NULL;
+	}
+
+	dma->queue = fb_flip;
+	slot->fb_flip = NULL;
+
+apply:
+	if (action)
+		regmap_write(regmap,
+			     desc->regs_offset + ATMEL_HLCDC_LAYER_CHER,
+			     action);
+
+	atmel_hlcdc_layer_update_reset(layer, upd->pending);
+
+	upd->pending = -1;
+}
+
+void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
+	struct regmap *regmap = layer->hlcdc->regmap;
+	struct atmel_hlcdc_layer_fb_flip *flip;
+	unsigned long flags;
+	unsigned int isr, imr;
+	unsigned int status;
+	unsigned int plane_status;
+	u32 flip_status;
+
+	int i;
+
+	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IMR, &imr);
+	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
+	status = imr & isr;
+	if (!status)
+		return;
+
+	spin_lock_irqsave(&layer->lock, flags);
+
+	flip = dma->queue ? dma->queue : dma->cur;
+
+	if (!flip) {
+		spin_unlock_irqrestore(&layer->lock, flags);
+		return;
+	}
+
+	flip_status = 0;
+	for (i = 0; i < flip->ngems; i++) {
+		plane_status = (status >> (8 * i));
+
+		if (plane_status &
+		    (ATMEL_HLCDC_LAYER_ADD_IRQ |
+		     ATMEL_HLCDC_LAYER_DSCR_IRQ) &
+		    ~flip->dscrs[i]->ctrl) {
+			flip->dscrs[i]->status |=
+					ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED;
+			flip->dscrs[i]->ctrl |=
+					ATMEL_HLCDC_LAYER_ADD_IRQ |
+					ATMEL_HLCDC_LAYER_DSCR_IRQ;
+		}
+
+		if (plane_status &
+		    ATMEL_HLCDC_LAYER_DONE_IRQ &
+		    ~flip->dscrs[i]->ctrl) {
+			flip->dscrs[i]->status |=
+					ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
+			flip->dscrs[i]->ctrl |=
+					ATMEL_HLCDC_LAYER_DONE_IRQ;
+		}
+
+		flip_status |= flip->dscrs[i]->status;
+	}
+
+	/* Get changed bits */
+	flip_status ^= flip->status;
+
+	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED) {
+		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
+		dma->cur = dma->queue;
+		dma->queue = NULL;
+	}
+
+	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE) {
+		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
+		dma->cur = NULL;
+	}
+
+	flip->status |= flip_status;
+
+	if (!dma->queue) {
+		atmel_hlcdc_layer_update_apply(layer);
+
+		if (!dma->cur)
+			dma->status = ATMEL_HLCDC_LAYER_DISABLED;
+	}
+
+	spin_unlock_irqrestore(&layer->lock, flags);
+}
+
+int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_fb_flip *flip;
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&layer->lock, flags);
+
+	/*
+	 * First disable DMA transfers. If a DMA transfer has been queued
+	 * we're stopping this one instead of the current one because we
+	 * can't know for sure if queued transfer has been started or not.
+	 */
+	flip = dma->queue ? dma->queue : dma->cur;
+	if (flip) {
+		for (i = 0; i < flip->ngems; i++)
+			flip->dscrs[i]->ctrl &= ~(ATMEL_HLCDC_LAYER_DFETCH |
+						  ATMEL_HLCDC_LAYER_DONE_IRQ);
+
+		dma->status = ATMEL_HLCDC_LAYER_DISABLING;
+	}
+
+	/*
+	 * Then discard the pending update request (if any) to prevent
+	 * DMA irq handler from restarting the DMA channel after it has
+	 * been disabled.
+	 */
+	if (upd->pending >= 0) {
+		atmel_hlcdc_layer_update_reset(layer, upd->pending);
+		upd->pending = -1;
+	}
+
+	spin_unlock_irqrestore(&layer->lock, flags);
+
+	return 0;
+}
+
+int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct regmap *regmap = layer->hlcdc->regmap;
+	struct atmel_hlcdc_layer_fb_flip *fb_flip;
+	struct atmel_hlcdc_layer_update_slot *slot;
+	unsigned long flags;
+	int i, j = 0;
+
+	fb_flip = kzalloc(sizeof(*fb_flip), GFP_KERNEL);
+	if (!fb_flip)
+		return -ENOMEM;
+
+	fb_flip->task = drm_flip_work_allocate_task(fb_flip, GFP_KERNEL);
+	if (!fb_flip->task) {
+		kfree(fb_flip);
+		return -ENOMEM;
+	}
+
+	spin_lock_irqsave(&layer->lock, flags);
+
+	upd->next = upd->pending ? 0 : 1;
+
+	slot = &upd->slots[upd->next];
+
+	for (i = 0; i < layer->max_planes * 4; i++) {
+		if (!dma->dscrs[i].status) {
+			fb_flip->dscrs[j++] = &dma->dscrs[i];
+			dma->dscrs[i].status =
+				ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED;
+			if (j == layer->max_planes)
+				break;
+		}
+	}
+
+	if (j < layer->max_planes) {
+		for (i = 0; i < j; i++)
+			fb_flip->dscrs[i]->status = 0;
+	}
+
+	if (j < layer->max_planes) {
+		spin_unlock_irqrestore(&layer->lock, flags);
+		atmel_hlcdc_layer_fb_flip_destroy(fb_flip);
+		return -EBUSY;
+	}
+
+	slot->fb_flip = fb_flip;
+
+	if (upd->pending >= 0) {
+		memcpy(slot->configs,
+		       upd->slots[upd->pending].configs,
+		       layer->desc->nconfigs * sizeof(u32));
+		memcpy(slot->updated_configs,
+		       upd->slots[upd->pending].updated_configs,
+		       DIV_ROUND_UP(layer->desc->nconfigs,
+				    BITS_PER_BYTE * sizeof(unsigned long)) *
+		       sizeof(unsigned long));
+		slot->fb_flip->fb = upd->slots[upd->pending].fb_flip->fb;
+		if (upd->slots[upd->pending].fb_flip->fb) {
+			slot->fb_flip->fb =
+				upd->slots[upd->pending].fb_flip->fb;
+			slot->fb_flip->ngems =
+				upd->slots[upd->pending].fb_flip->ngems;
+			drm_framebuffer_reference(slot->fb_flip->fb);
+		}
+	} else {
+		regmap_bulk_read(regmap,
+				 layer->desc->regs_offset +
+				 ATMEL_HLCDC_LAYER_CFG(layer, 0),
+				 upd->slots[upd->next].configs,
+				 layer->desc->nconfigs);
+	}
+
+	spin_unlock_irqrestore(&layer->lock, flags);
+
+	return 0;
+}
+
+void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+
+	atmel_hlcdc_layer_update_reset(layer, upd->next);
+	upd->next = -1;
+}
+
+void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
+				     struct drm_framebuffer *fb,
+				     unsigned int *offsets)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_fb_flip *fb_flip;
+	struct atmel_hlcdc_layer_update_slot *slot;
+	struct atmel_hlcdc_dma_channel_dscr *dscr;
+	struct drm_framebuffer *old_fb;
+	int nplanes = 0;
+	int i;
+
+	if (upd->next < 0 || upd->next > 1)
+		return;
+
+	if (fb)
+		nplanes = drm_format_num_planes(fb->pixel_format);
+
+	if (nplanes > layer->max_planes)
+		return;
+
+	slot = &upd->slots[upd->next];
+
+	fb_flip = slot->fb_flip;
+	old_fb = slot->fb_flip->fb;
+
+	for (i = 0; i < nplanes; i++) {
+		struct drm_gem_cma_object *gem;
+
+		dscr = slot->fb_flip->dscrs[i];
+		gem = drm_fb_cma_get_gem_obj(fb, i);
+		dscr->addr = gem->paddr + offsets[i];
+	}
+
+	fb_flip->ngems = nplanes;
+	fb_flip->fb = fb;
+
+	if (fb)
+		drm_framebuffer_reference(fb);
+
+	if (old_fb)
+		drm_framebuffer_unreference(old_fb);
+}
+
+void atmel_hlcdc_layer_update_set_finished(struct atmel_hlcdc_layer *layer,
+					   void (*finished)(void *data),
+					   void *finished_data)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_update_slot *slot;
+
+	if (upd->next < 0 || upd->next > 1)
+		return;
+
+	slot = &upd->slots[upd->next];
+
+	slot->fb_flip->finished = finished;
+	slot->fb_flip->finished_data = finished_data;
+}
+
+void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
+				  u32 mask, u32 val)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_update_slot *slot;
+
+	if (upd->next < 0 || upd->next > 1)
+		return;
+
+	if (cfg >= layer->desc->nconfigs)
+		return;
+
+	slot = &upd->slots[upd->next];
+	slot->configs[cfg] &= ~mask;
+	slot->configs[cfg] |= (val & mask);
+	set_bit(cfg, slot->updated_configs);
+}
+
+void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_update_slot *slot;
+	unsigned long flags;
+
+	if (upd->next < 0  || upd->next > 1)
+		return;
+
+	slot = &upd->slots[upd->next];
+
+	spin_lock_irqsave(&layer->lock, flags);
+
+	/*
+	 * Release pending update request and replace it by the new one.
+	 */
+	if (upd->pending >= 0)
+		atmel_hlcdc_layer_update_reset(layer, upd->pending);
+
+	upd->pending = upd->next;
+	upd->next = -1;
+
+	if (!dma->queue)
+		atmel_hlcdc_layer_update_apply(layer);
+
+	spin_unlock_irqrestore(&layer->lock, flags);
+
+
+	upd->next = -1;
+}
+
+static int atmel_hlcdc_layer_dma_init(struct drm_device *dev,
+				      struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	dma_addr_t dma_addr;
+	int i;
+
+	dma->dscrs = dma_alloc_coherent(dev->dev,
+					layer->max_planes * 4 *
+					sizeof(*dma->dscrs),
+					&dma_addr, GFP_KERNEL);
+	if (!dma->dscrs)
+		return -ENOMEM;
+
+	for (i = 0; i < layer->max_planes * 4; i++) {
+		struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i];
+
+		dscr->next = dma_addr + (i * sizeof(*dscr));
+	}
+
+	return 0;
+}
+
+static void atmel_hlcdc_layer_dma_cleanup(struct drm_device *dev,
+					  struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	int i;
+
+	for (i = 0; i < layer->max_planes * 4; i++) {
+		struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i];
+
+		dscr->status = 0;
+	}
+
+	dma_free_coherent(dev->dev, layer->max_planes * 4 *
+			  sizeof(*dma->dscrs), dma->dscrs,
+			  dma->dscrs[0].next);
+}
+
+static int atmel_hlcdc_layer_update_init(struct drm_device *dev,
+				struct atmel_hlcdc_layer *layer,
+				const struct atmel_hlcdc_layer_desc *desc)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	int updated_size;
+	void *buffer;
+	int i;
+
+	updated_size = DIV_ROUND_UP(desc->nconfigs,
+				    BITS_PER_BYTE *
+				    sizeof(unsigned long));
+
+	buffer = devm_kzalloc(dev->dev,
+			      ((desc->nconfigs * sizeof(u32)) +
+				(updated_size * sizeof(unsigned long))) * 2,
+			      GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	for (i = 0; i < 2; i++) {
+		upd->slots[i].updated_configs = buffer;
+		buffer += updated_size * sizeof(unsigned long);
+		upd->slots[i].configs = buffer;
+		buffer += desc->nconfigs * sizeof(u32);
+	}
+
+	upd->pending = -1;
+	upd->next = -1;
+
+	return 0;
+}
+
+int atmel_hlcdc_layer_init(struct drm_device *dev,
+			   struct atmel_hlcdc_layer *layer,
+			   const struct atmel_hlcdc_layer_desc *desc)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct regmap *regmap = dc->hlcdc->regmap;
+	unsigned int tmp;
+	int ret;
+	int i;
+
+	layer->hlcdc = dc->hlcdc;
+	layer->wq = dc->wq;
+	layer->desc = desc;
+
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
+		     ATMEL_HLCDC_LAYER_RST);
+	for (i = 0; i < desc->formats->nformats; i++) {
+		int nplanes = drm_format_num_planes(desc->formats->formats[i]);
+
+		if (nplanes > layer->max_planes)
+			layer->max_planes = nplanes;
+	}
+
+	spin_lock_init(&layer->lock);
+	drm_flip_work_init(&layer->gc, desc->name,
+			   atmel_hlcdc_layer_fb_flip_release);
+	ret = atmel_hlcdc_layer_dma_init(dev, layer);
+	if (ret)
+		return ret;
+
+	ret = atmel_hlcdc_layer_update_init(dev, layer, desc);
+	if (ret)
+		return ret;
+
+	/* Flush Status Register */
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR,
+		     0xffffffff);
+	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR,
+		    &tmp);
+
+	tmp = 0;
+	for (i = 0; i < layer->max_planes; i++)
+		tmp |= (ATMEL_HLCDC_LAYER_DMA_IRQ |
+			ATMEL_HLCDC_LAYER_DSCR_IRQ |
+			ATMEL_HLCDC_LAYER_ADD_IRQ |
+			ATMEL_HLCDC_LAYER_DONE_IRQ |
+			ATMEL_HLCDC_LAYER_OVR_IRQ) << (8 * i);
+
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IER, tmp);
+
+	return 0;
+}
+
+void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
+			       struct atmel_hlcdc_layer *layer)
+{
+	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
+	struct regmap *regmap = layer->hlcdc->regmap;
+
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR,
+		     0xffffffff);
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
+		     ATMEL_HLCDC_LAYER_RST);
+
+	atmel_hlcdc_layer_dma_cleanup(dev, layer);
+	drm_flip_work_cleanup(&layer->gc);
+}
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
new file mode 100644
index 0000000..885b57a
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DRM_ATMEL_HLCDC_LAYER_H
+#define DRM_ATMEL_HLCDC_LAYER_H
+
+#include <linux/mfd/atmel-hlcdc.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_flip_work.h>
+#include <drm/drmP.h>
+
+#define ATMEL_HLCDC_LAYER_CHER			0x0
+#define ATMEL_HLCDC_LAYER_CHDR			0x4
+#define ATMEL_HLCDC_LAYER_CHSR			0x8
+#define ATMEL_HLCDC_LAYER_DMA_CHAN		BIT(0)
+#define ATMEL_HLCDC_LAYER_UPDATE		BIT(1)
+#define ATMEL_HLCDC_LAYER_A2Q			BIT(2)
+#define ATMEL_HLCDC_LAYER_RST			BIT(8)
+
+#define ATMEL_HLCDC_LAYER_IER			0xc
+#define ATMEL_HLCDC_LAYER_IDR			0x10
+#define ATMEL_HLCDC_LAYER_IMR			0x14
+#define ATMEL_HLCDC_LAYER_ISR			0x18
+#define ATMEL_HLCDC_LAYER_DFETCH		BIT(0)
+#define ATMEL_HLCDC_LAYER_LFETCH		BIT(1)
+#define ATMEL_HLCDC_LAYER_DMA_IRQ		BIT(2)
+#define ATMEL_HLCDC_LAYER_DSCR_IRQ		BIT(3)
+#define ATMEL_HLCDC_LAYER_ADD_IRQ		BIT(4)
+#define ATMEL_HLCDC_LAYER_DONE_IRQ		BIT(5)
+#define ATMEL_HLCDC_LAYER_OVR_IRQ		BIT(6)
+
+#define ATMEL_HLCDC_LAYER_PLANE_HEAD(n)		(((n) * 0x10) + 0x1c)
+#define ATMEL_HLCDC_LAYER_PLANE_ADDR(n)		(((n) * 0x10) + 0x20)
+#define ATMEL_HLCDC_LAYER_PLANE_CTRL(n)		(((n) * 0x10) + 0x24)
+#define ATMEL_HLCDC_LAYER_PLANE_NEXT(n)		(((n) * 0x10) + 0x28)
+#define ATMEL_HLCDC_LAYER_CFG(p, c)		(((c) * 4) + ((p)->max_planes * 0x10) + 0x1c)
+
+#define ATMEL_HLCDC_LAYER_DMA_CFG_ID		0
+#define ATMEL_HLCDC_LAYER_DMA_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_DMA_CFG_ID)
+
+#define ATMEL_HLCDC_LAYER_FORMAT_CFG_ID		1
+#define ATMEL_HLCDC_LAYER_FORMAT_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_FORMAT_CFG_ID)
+#define ATMEL_HLCDC_LAYER_RGB			(0 << 0)
+#define ATMEL_HLCDC_LAYER_CLUT			(1 << 0)
+#define ATMEL_HLCDC_LAYER_YUV			(2 << 0)
+#define ATMEL_HLCDC_RGB_MODE(m)			(((m) & 0xf) << 4)
+#define ATMEL_HLCDC_CLUT_MODE(m)		(((m) & 0x3) << 8)
+#define ATMEL_HLCDC_YUV_MODE(m)			(((m) & 0xf) << 12)
+#define ATMEL_HLCDC_YUV422ROT			(1 << 16)
+#define ATMEL_HLCDC_YUV422SWP			(1 << 17)
+#define ATMEL_HLCDC_DSCALEOPT			(1 << 20)
+
+#define ATMEL_HLCDC_XRGB4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(0))
+#define ATMEL_HLCDC_ARGB4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(1))
+#define ATMEL_HLCDC_RGBA4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(2))
+#define ATMEL_HLCDC_RGB565_MODE			(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(3))
+#define ATMEL_HLCDC_ARGB1555_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(4))
+#define ATMEL_HLCDC_XRGB8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(9))
+#define ATMEL_HLCDC_RGB888_MODE			(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(10))
+#define ATMEL_HLCDC_ARGB8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(12))
+#define ATMEL_HLCDC_RGBA8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(13))
+
+#define ATMEL_HLCDC_AYUV_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(0))
+#define ATMEL_HLCDC_YUYV_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(1))
+#define ATMEL_HLCDC_UYVY_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(2))
+#define ATMEL_HLCDC_YVYU_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(3))
+#define ATMEL_HLCDC_VYUY_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(4))
+#define ATMEL_HLCDC_NV61_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(5))
+#define ATMEL_HLCDC_YUV422_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(6))
+#define ATMEL_HLCDC_NV21_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(7))
+#define ATMEL_HLCDC_YUV420_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(8))
+
+#define ATMEL_HLCDC_LAYER_POS_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pos)
+#define ATMEL_HLCDC_LAYER_SIZE_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.size)
+#define ATMEL_HLCDC_LAYER_MEMSIZE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.memsize)
+#define ATMEL_HLCDC_LAYER_XSTRIDE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.xstride)
+#define ATMEL_HLCDC_LAYER_PSTRIDE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pstride)
+#define ATMEL_HLCDC_LAYER_DFLTCOLOR_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.default_color)
+#define ATMEL_HLCDC_LAYER_CRKEY_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key)
+#define ATMEL_HLCDC_LAYER_CRKEY_MASK_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key_mask)
+
+#define ATMEL_HLCDC_LAYER_GENERAL_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.general_config)
+#define ATMEL_HLCDC_LAYER_CRKEY			BIT(0)
+#define ATMEL_HLCDC_LAYER_INV			BIT(1)
+#define ATMEL_HLCDC_LAYER_ITER2BL		BIT(2)
+#define ATMEL_HLCDC_LAYER_ITER			BIT(3)
+#define ATMEL_HLCDC_LAYER_REVALPHA		BIT(4)
+#define ATMEL_HLCDC_LAYER_GAEN			BIT(5)
+#define ATMEL_HLCDC_LAYER_LAEN			BIT(6)
+#define ATMEL_HLCDC_LAYER_OVR			BIT(7)
+#define ATMEL_HLCDC_LAYER_DMA			BIT(8)
+#define ATMEL_HLCDC_LAYER_REP			BIT(9)
+#define ATMEL_HLCDC_LAYER_DSTKEY		BIT(10)
+#define ATMEL_HLCDC_LAYER_GA_MASK		GENMASK(23, 16)
+#define ATMEL_HLCDC_LAYER_GA_SHIFT		16
+
+#define ATMEL_HLCDC_LAYER_CSC_CFG(p, o)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.csc + o)
+
+#define ATMEL_HLCDC_LAYER_DISC_POS_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_pos)
+
+#define ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_size)
+
+#define ATMEL_HLCDC_MAX_PLANES			3
+
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED	BIT(0)
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED	BIT(1)
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE	BIT(2)
+
+/**
+ * Atmel HLCDC Layer registers layout structure
+ *
+ * Each HLCDC layer has its own register organization and a given register
+ * by be placed differently on 2 different layers depending on its
+ * capabilities.
+ * This structure stores common registers layout for a given layer and is
+ * used by HLCDC layer code to chose the appropriate register to write to
+ * or to read from.
+ *
+ * For all fields, a value of zero means "unsupported".
+ *
+ * See Atmel's datasheet for a detailled description of these registers.
+ *
+ * @xstride: xstride registers
+ * @pstride: pstride registers
+ * @pos: position register
+ * @size: displayed size register
+ * @memsize: memory size register
+ * @default_color: default color register
+ * @chroma_key: chroma key register
+ * @chroma_key_mask: chroma key mask register
+ * @general_config: general layer config register
+ * @disc_pos: discard area position register
+ * @disc_size: discard area size register
+ * @csc: color space conversion register
+ */
+struct atmel_hlcdc_layer_cfg_layout {
+	int xstride[ATMEL_HLCDC_MAX_PLANES];
+	int pstride[ATMEL_HLCDC_MAX_PLANES];
+	int pos;
+	int size;
+	int memsize;
+	int default_color;
+	int chroma_key;
+	int chroma_key_mask;
+	int general_config;
+	int disc_pos;
+	int disc_size;
+	int csc;
+};
+
+/**
+ * Atmel HLCDC framebuffer flip structure
+ *
+ * This structure is allocated when someone asked for a layer update (most
+ * likely a DRM plane update, either primary, overlay or cursor plane) and
+ * released when the layer do not need to reference the framebuffer object
+ * anymore (i.e. the layer was disabled or updated).
+ *
+ * @fb: the referenced framebuffer object.
+ * @refcnt: the number of GEM object still referenced by the layer.
+ *	    When no more objects are referenced the fb flip structure is
+ *	    added to the garbage collector.
+ * @ngems: number of GEM objects referenced by the fb element.
+ * @finished: finished callback, called when the layer framebuffer flip is
+ *	      finished.
+ * @finished_data: data passed to the finished callback.
+ */
+struct atmel_hlcdc_layer_fb_flip {
+	struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_MAX_PLANES];
+	struct drm_flip_task *task;
+	struct drm_framebuffer *fb;
+	int ngems;
+	u32 status;
+	void (*finished)(void *data);
+	void *finished_data;
+};
+
+/**
+ * Atmel HLCDC DMA descriptor structure
+ *
+ * This structure is used by the HLCDC DMA engine to schedule a DMA transfer.
+ *
+ * The structure fields must remain in this specific order, because they're
+ * used by the HLCDC DMA engine, which expect them in this order.
+ *
+ * @addr: buffer DMA address
+ * @ctrl: DMA transfer options
+ * @next: next DMA descriptor to fetch
+ * @gem_flip: the attached gem_flip operation
+ */
+struct atmel_hlcdc_dma_channel_dscr {
+	dma_addr_t addr;
+	u32 ctrl;
+	dma_addr_t next;
+	u32 status;
+} __aligned(sizeof(u64));
+
+/**
+ * Atmel HLCDC layer types
+ */
+enum atmel_hlcdc_layer_type {
+	ATMEL_HLCDC_BASE_LAYER,
+	ATMEL_HLCDC_OVERLAY_LAYER,
+	ATMEL_HLCDC_CURSOR_LAYER,
+	ATMEL_HLCDC_PP_LAYER,
+};
+
+/**
+ * Atmel HLCDC Supported formats structure
+ *
+ * This structure list all the formats supported by a given layer.
+ *
+ * @nformats: number of supported formats
+ * @formats: supported formats
+ */
+struct atmel_hlcdc_formats {
+	int nformats;
+	uint32_t *formats;
+};
+
+/**
+ * Atmel HLCDC Layer description structure
+ *
+ * This structure describe the capabilities provided by a given layer.
+ *
+ * @name: layer name
+ * @type: layer type
+ * @id: layer id
+ * @regs_offset: offset of the layer registers from the HLCDC registers base
+ * @nconfigs: number of config registers provided by this layer
+ * @layout: config registers layout
+ * @max_width: maximum width supported by this layer (0 means unlimited)
+ * @max_height: maximum height supported by this layer (0 means unlimited)
+ */
+struct atmel_hlcdc_layer_desc {
+	const char *name;
+	enum atmel_hlcdc_layer_type type;
+	int id;
+	int regs_offset;
+	int nconfigs;
+	struct atmel_hlcdc_formats *formats;
+	struct atmel_hlcdc_layer_cfg_layout layout;
+	int max_width;
+	int max_height;
+};
+
+/**
+ * Atmel HLCDC Layer Update Slot structure
+ *
+ * This structure stores layer update requests to be applied on next frame.
+ * This is the base structure behind the atomic layer update infrastructure.
+ *
+ * Atomic layer update provides a way to update all layer's parameters
+ * simultaneously. This is needed to avoid incompatible sequential updates
+ * like this one:
+ * 1) update layer format from RGB888 (1 plane/buffer) to YUV422
+ *    (2 planes/buffers)
+ * 2) the format update is applied but the DMA channel for the second
+ *    plane/buffer is not enabled
+ * 3) enable the DMA channel for the second plane
+ *
+ * @dscrs: DMA channel descriptors
+ * @fb_flip: fb_flip object
+ * @updated_configs: bitmask used to record modified configs
+ * @configs: new config values
+ */
+struct atmel_hlcdc_layer_update_slot {
+	struct atmel_hlcdc_layer_fb_flip *fb_flip;
+	unsigned long *updated_configs;
+	u32 *configs;
+};
+
+/**
+ * Atmel HLCDC Layer Update structure
+ *
+ * This structure provides a way to queue layer update requests.
+ *
+ * At a given time there is at most:
+ *  - one pending update request, which means the update request has been
+ *    commited (or validated) and is waiting for the DMA channel(s) to be
+ *    available
+ *  - one request being prepared, which means someone started a layer update
+ *    but has not commited it yet. There cannot be more than one started
+ *    request, because the update lock is taken when starting a layer update
+ *    and release when commiting or rolling back the request.
+ *
+ * @slots: update slots. One is used for pending request and the other one
+ *	   for started update request
+ * @pending: the pending slot index or -1 if no request is pending
+ * @next: the started update slot index or -1 no update has been started
+ */
+struct atmel_hlcdc_layer_update {
+	struct atmel_hlcdc_layer_update_slot slots[2];
+	int pending;
+	int next;
+};
+
+enum atmel_hlcdc_layer_dma_channel_status {
+	ATMEL_HLCDC_LAYER_DISABLED,
+	ATMEL_HLCDC_LAYER_ENABLED,
+	ATMEL_HLCDC_LAYER_DISABLING,
+};
+
+/**
+ * Atmel HLCDC Layer DMA channel structure
+ *
+ * This structure stores informations on the DMA channel associated to a
+ * given layer.
+ *
+ * @status: DMA channel status
+ * @cur: current framebuffer
+ * @queue: next framebuffer
+ * @dscrs: allocated DMA descriptors
+ */
+struct atmel_hlcdc_layer_dma_channel {
+	enum atmel_hlcdc_layer_dma_channel_status status;
+	struct atmel_hlcdc_layer_fb_flip *cur;
+	struct atmel_hlcdc_layer_fb_flip *queue;
+	struct atmel_hlcdc_dma_channel_dscr *dscrs;
+};
+
+/**
+ * Atmel HLCDC Layer structure
+ *
+ * This structure stores information on the layer instance.
+ *
+ * @desc: layer description
+ * @max_planes: maximum planes/buffers that can be associated with this layer.
+ *	       This depends on the supported formats.
+ * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
+ * @dma: dma channel
+ * @gc: fb flip garbage collector
+ * @update: update handler
+ * @lock: layer lock
+ */
+struct atmel_hlcdc_layer {
+	const struct atmel_hlcdc_layer_desc *desc;
+	int max_planes;
+	struct atmel_hlcdc *hlcdc;
+	struct workqueue_struct *wq;
+	struct drm_flip_work gc;
+	struct atmel_hlcdc_layer_dma_channel dma;
+	struct atmel_hlcdc_layer_update update;
+	spinlock_t lock;
+};
+
+void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer);
+
+int atmel_hlcdc_layer_init(struct drm_device *dev,
+			   struct atmel_hlcdc_layer *layer,
+			   const struct atmel_hlcdc_layer_desc *desc);
+
+void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
+			       struct atmel_hlcdc_layer *layer);
+
+int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer);
+
+void atmel_hlcdc_layer_set_finished(struct atmel_hlcdc_layer *layer,
+				    void (*finished)(void *data),
+				    void *data);
+
+int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer);
+
+void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
+				  u32 mask, u32 val);
+
+void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
+				     struct drm_framebuffer *fb,
+				     unsigned int *offsets);
+
+void atmel_hlcdc_layer_update_set_finished(struct atmel_hlcdc_layer *layer,
+					   void (*finished)(void *data),
+					   void *finished_data);
+
+void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer);
+
+void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer);
+
+#endif /* DRM_ATMEL_HLCDC_LAYER_H */
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
new file mode 100644
index 0000000..de95eb7
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_panel.h>
+
+#include "atmel_hlcdc_dc.h"
+
+/**
+ * Atmel HLCDC RGB output mode
+ */
+enum atmel_hlcdc_connector_rgb_mode {
+	ATMEL_HLCDC_CONNECTOR_RGB444,
+	ATMEL_HLCDC_CONNECTOR_RGB565,
+	ATMEL_HLCDC_CONNECTOR_RGB666,
+	ATMEL_HLCDC_CONNECTOR_RGB888,
+};
+
+struct atmel_hlcdc_slave;
+
+/**
+ * Atmel HLCDC Slave device operations structure
+ *
+ * This structure defines an abstraction to be implemented by each slave
+ * device type (panel, convertors, ...).
+ *
+ * @enable: Enable the slave device
+ * @disable: Disable the slave device
+ * @get_modes: retrieve modes supported by the slave device
+ * @destroy: detroy the slave device and all associated data
+ */
+struct atmel_hlcdc_slave_ops {
+	int (*enable)(struct atmel_hlcdc_slave *slave);
+	int (*disable)(struct atmel_hlcdc_slave *slave);
+	int (*get_modes)(struct atmel_hlcdc_slave *slave);
+	void (*destroy)(struct atmel_hlcdc_slave *slave);
+};
+
+/**
+ * Atmel HLCDC Slave device structure
+ *
+ * This structure is the base slave device structure to be overloaded by
+ * each slave device implementation.
+ *
+ * @ops: slave device operations
+ */
+struct atmel_hlcdc_slave {
+	const struct atmel_hlcdc_slave_ops *ops;
+};
+
+/**
+ * Atmel HLCDC Panel device structure
+ *
+ * This structure is specialization of the slave device structure to
+ * interface with drm panels.
+ *
+ * @slave: base slave device fields
+ * @panel: drm panel attached to this slave device
+ */
+struct atmel_hlcdc_panel {
+	struct atmel_hlcdc_slave slave;
+	struct drm_panel *panel;
+};
+
+static inline struct atmel_hlcdc_panel *
+atmel_hlcdc_slave_to_panel(struct atmel_hlcdc_slave *slave)
+{
+	return container_of(slave, struct atmel_hlcdc_panel, slave);
+}
+
+/**
+ * Atmel HLCDC RGB connector structure
+ *
+ * This structure stores informations about an DRM panel connected through
+ * the RGB connector.
+ *
+ * @connector: DRM connector
+ * @encoder: DRM encoder
+ * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
+ * @slave: slave device connected to this output
+ * @endpoint: DT endpoint representing this output
+ * @dpms: current DPMS mode
+ */
+struct atmel_hlcdc_rgb_output {
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct atmel_hlcdc *hlcdc;
+	struct atmel_hlcdc_slave *slave;
+	struct of_endpoint endpoint;
+	int dpms;
+};
+
+static inline struct atmel_hlcdc_rgb_output *
+drm_connector_to_atmel_hlcdc_rgb_output(struct drm_connector *connector)
+{
+	return container_of(connector, struct atmel_hlcdc_rgb_output,
+			    connector);
+}
+
+static inline struct atmel_hlcdc_rgb_output *
+drm_encoder_to_atmel_hlcdc_rgb_output(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder);
+}
+
+static int atmel_hlcdc_panel_enable(struct atmel_hlcdc_slave *slave)
+{
+	struct atmel_hlcdc_panel *panel = atmel_hlcdc_slave_to_panel(slave);
+
+	return drm_panel_enable(panel->panel);
+}
+
+static int atmel_hlcdc_panel_disable(struct atmel_hlcdc_slave *slave)
+{
+	struct atmel_hlcdc_panel *panel = atmel_hlcdc_slave_to_panel(slave);
+
+	return drm_panel_disable(panel->panel);
+}
+
+static int atmel_hlcdc_panel_get_modes(struct atmel_hlcdc_slave *slave)
+{
+	struct atmel_hlcdc_panel *panel = atmel_hlcdc_slave_to_panel(slave);
+
+	return panel->panel->funcs->get_modes(panel->panel);
+}
+
+static void atmel_hlcdc_panel_destroy(struct atmel_hlcdc_slave *slave)
+{
+	struct atmel_hlcdc_panel *panel = atmel_hlcdc_slave_to_panel(slave);
+
+	drm_panel_detach(panel->panel);
+	kfree(panel);
+}
+
+static const struct atmel_hlcdc_slave_ops atmel_hlcdc_panel_ops = {
+	.enable = atmel_hlcdc_panel_enable,
+	.disable = atmel_hlcdc_panel_disable,
+	.get_modes = atmel_hlcdc_panel_get_modes,
+	.destroy = atmel_hlcdc_panel_destroy,
+};
+
+static struct atmel_hlcdc_slave *
+atmel_hlcdc_panel_detect(struct atmel_hlcdc_rgb_output *rgb)
+{
+	struct device_node *np;
+	struct drm_panel *p = NULL;
+	struct atmel_hlcdc_panel *panel;
+
+	np = of_graph_get_remote_port_parent(rgb->endpoint.local_node);
+	if (!np)
+		return NULL;
+
+	p = of_drm_find_panel(np);
+	of_node_put(np);
+
+	if (p) {
+		panel = kzalloc(sizeof(*panel), GFP_KERNEL);
+		if (!panel)
+			return NULL;
+
+		drm_panel_attach(p, &rgb->connector);
+		panel->panel = p;
+		panel->slave.ops = &atmel_hlcdc_panel_ops;
+		return &panel->slave;
+	}
+
+	return NULL;
+}
+
+static void atmel_hlcdc_rgb_encoder_dpms(struct drm_encoder *encoder,
+					 int mode)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
+	struct regmap *regmap = rgb->hlcdc->regmap;
+	unsigned int status;
+
+	if (mode != DRM_MODE_DPMS_ON)
+		mode = DRM_MODE_DPMS_OFF;
+
+	if (mode == rgb->dpms)
+		return;
+
+	if (mode != DRM_MODE_DPMS_ON) {
+		regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       (status & ATMEL_HLCDC_DISP))
+			cpu_relax();
+
+		regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       (status & ATMEL_HLCDC_SYNC))
+			cpu_relax();
+
+		regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       (status & ATMEL_HLCDC_PIXEL_CLK))
+			cpu_relax();
+
+		clk_disable_unprepare(rgb->hlcdc->sys_clk);
+
+		rgb->slave->ops->disable(rgb->slave);
+	} else {
+		rgb->slave->ops->enable(rgb->slave);
+
+		clk_prepare_enable(rgb->hlcdc->sys_clk);
+
+		regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       !(status & ATMEL_HLCDC_PIXEL_CLK))
+			cpu_relax();
+
+
+		regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       !(status & ATMEL_HLCDC_SYNC))
+			cpu_relax();
+
+		regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       !(status & ATMEL_HLCDC_DISP))
+			cpu_relax();
+	}
+
+	rgb->dpms = mode;
+}
+
+static bool
+atmel_hlcdc_rgb_encoder_mode_fixup(struct drm_encoder *encoder,
+				   const struct drm_display_mode *mode,
+				   struct drm_display_mode *adjusted)
+{
+	return true;
+}
+
+static void atmel_hlcdc_rgb_encoder_prepare(struct drm_encoder *encoder)
+{
+	atmel_hlcdc_rgb_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static void atmel_hlcdc_rgb_encoder_commit(struct drm_encoder *encoder)
+{
+	atmel_hlcdc_rgb_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void
+atmel_hlcdc_rgb_encoder_mode_set(struct drm_encoder *encoder,
+				 struct drm_display_mode *mode,
+				 struct drm_display_mode *adjusted)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
+	struct drm_display_info *info = &rgb->connector.display_info;
+	unsigned long prate = clk_get_rate(rgb->hlcdc->sys_clk);
+	unsigned long mode_rate = mode->clock * 1000;
+	u32 cfg = 0;
+	int div;
+
+	if ((prate / 2) < mode_rate) {
+		prate *= 2;
+		cfg |= ATMEL_HLCDC_CLKSEL;
+	}
+
+	div = DIV_ROUND_UP(prate, mode_rate);
+	if (div < 2)
+		div = 2;
+
+	cfg |= ATMEL_HLCDC_CLKDIV(div);
+
+	if (mode->flags & DRM_MODE_FLAG_NCSYNC)
+		cfg |= ATMEL_HLCDC_CLKPOL;
+
+	regmap_update_bits(rgb->hlcdc->regmap, ATMEL_HLCDC_CFG(0),
+			   ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK, cfg);
+
+	cfg = 0;
+
+	if (info->nbus_formats) {
+		switch (info->bus_formats[0]) {
+		case VIDEO_BUS_FMT_RGB565_1X16:
+			cfg |= ATMEL_HLCDC_CONNECTOR_RGB565 << 8;
+			break;
+		case VIDEO_BUS_FMT_RGB666_1X18:
+			cfg |= ATMEL_HLCDC_CONNECTOR_RGB666 << 8;
+			break;
+		case VIDEO_BUS_FMT_RGB888_1X24:
+			cfg |= ATMEL_HLCDC_CONNECTOR_RGB888 << 8;
+			break;
+		case VIDEO_BUS_FMT_RGB444_1X12:
+		default:
+			break;
+		}
+	}
+
+	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+		cfg |= ATMEL_HLCDC_VSPOL;
+
+	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+		cfg |= ATMEL_HLCDC_HSPOL;
+
+	regmap_update_bits(rgb->hlcdc->regmap, ATMEL_HLCDC_CFG(5),
+			   ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL |
+			   ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE |
+			   ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY |
+			   ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO |
+			   ATMEL_HLCDC_MODE_MASK | ATMEL_HLCDC_GUARDTIME_MASK,
+			   cfg);
+}
+
+static struct drm_encoder_helper_funcs atmel_hlcdc_rgb_encoder_helper_funcs = {
+	.dpms = atmel_hlcdc_rgb_encoder_dpms,
+	.mode_fixup = atmel_hlcdc_rgb_encoder_mode_fixup,
+	.prepare = atmel_hlcdc_rgb_encoder_prepare,
+	.commit = atmel_hlcdc_rgb_encoder_commit,
+	.mode_set = atmel_hlcdc_rgb_encoder_mode_set,
+};
+
+static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+	memset(encoder, 0, sizeof(*encoder));
+}
+
+static const struct drm_encoder_funcs atmel_hlcdc_rgb_encoder_funcs = {
+	.destroy = atmel_hlcdc_rgb_encoder_destroy,
+};
+
+static int atmel_hlcdc_rgb_get_modes(struct drm_connector *connector)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_connector_to_atmel_hlcdc_rgb_output(connector);
+
+	return rgb->slave->ops->get_modes(rgb->slave);
+}
+
+static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector,
+				      struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+atmel_hlcdc_rgb_best_encoder(struct drm_connector *connector)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_connector_to_atmel_hlcdc_rgb_output(connector);
+
+	return &rgb->encoder;
+}
+
+static struct drm_connector_helper_funcs atmel_hlcdc_rgb_connector_helper_funcs = {
+	.get_modes = atmel_hlcdc_rgb_get_modes,
+	.mode_valid = atmel_hlcdc_rgb_mode_valid,
+	.best_encoder = atmel_hlcdc_rgb_best_encoder,
+};
+
+static enum drm_connector_status
+atmel_hlcdc_rgb_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_connector_to_atmel_hlcdc_rgb_output(connector);
+
+	if (!rgb->slave) {
+		/* At the moment we only support panel devices */
+		rgb->slave = atmel_hlcdc_panel_detect(rgb);
+	}
+
+	if (rgb->slave)
+		return connector_status_connected;
+
+	return connector_status_disconnected;
+}
+
+static void
+atmel_hlcdc_rgb_connector_destroy(struct drm_connector *connector)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_connector_to_atmel_hlcdc_rgb_output(connector);
+
+	if (rgb->slave && rgb->slave->ops->destroy)
+		rgb->slave->ops->destroy(rgb->slave);
+
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs atmel_hlcdc_rgb_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = atmel_hlcdc_rgb_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = atmel_hlcdc_rgb_connector_destroy,
+};
+
+static int atmel_hlcdc_create_output(struct drm_device *dev,
+				     struct of_endpoint *ep)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct atmel_hlcdc_rgb_output *rgb;
+
+	rgb = devm_kzalloc(dev->dev, sizeof(*rgb), GFP_KERNEL);
+	if (!rgb)
+		return -ENOMEM;
+
+	rgb->endpoint = *ep;
+
+	rgb->dpms = DRM_MODE_DPMS_OFF;
+
+	rgb->hlcdc = dc->hlcdc;
+
+	drm_connector_init(dev, &rgb->connector,
+			   &atmel_hlcdc_rgb_connector_funcs,
+			   DRM_MODE_CONNECTOR_LVDS);
+	drm_connector_helper_add(&rgb->connector,
+				 &atmel_hlcdc_rgb_connector_helper_funcs);
+	rgb->connector.dpms = DRM_MODE_DPMS_OFF;
+	rgb->connector.polled = DRM_CONNECTOR_POLL_CONNECT;
+
+	drm_encoder_init(dev, &rgb->encoder, &atmel_hlcdc_rgb_encoder_funcs,
+			 DRM_MODE_ENCODER_LVDS);
+	drm_encoder_helper_add(&rgb->encoder,
+			       &atmel_hlcdc_rgb_encoder_helper_funcs);
+
+	drm_mode_connector_attach_encoder(&rgb->connector, &rgb->encoder);
+	drm_sysfs_connector_add(&rgb->connector);
+
+	rgb->encoder.possible_crtcs = 0x1;
+
+	return 0;
+}
+
+int atmel_hlcdc_create_outputs(struct drm_device *dev)
+{
+	struct device_node *port_np, *np;
+	struct of_endpoint ep;
+	int ret;
+
+	port_np = of_get_child_by_name(dev->dev->of_node, "port");
+	if (!port_np)
+		return -EINVAL;
+
+	np = of_get_child_by_name(port_np, "endpoint");
+	of_node_put(port_np);
+
+	if (!np)
+		return -EINVAL;
+
+	ret = of_graph_parse_endpoint(np, &ep);
+	of_node_put(port_np);
+
+	if (ret)
+		return ret;
+
+	ret = atmel_hlcdc_create_output(dev, &ep);
+	if (ret)
+		return ret;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
new file mode 100644
index 0000000..d42cd73
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "atmel_hlcdc_dc.h"
+
+#define SUBPIXEL_MASK			0xffff
+
+static uint32_t rgb_formats[] = {
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_RGBA4444,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGBA8888,
+};
+
+struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {
+	.formats = rgb_formats,
+	.nformats = ARRAY_SIZE(rgb_formats),
+};
+
+static uint32_t rgb_and_yuv_formats[] = {
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_RGBA4444,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGBA8888,
+	DRM_FORMAT_AYUV,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_VYUY,
+	DRM_FORMAT_NV21,
+	DRM_FORMAT_NV61,
+	DRM_FORMAT_YUV422,
+	DRM_FORMAT_YUV420,
+};
+
+struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {
+	.formats = rgb_and_yuv_formats,
+	.nformats = ARRAY_SIZE(rgb_and_yuv_formats),
+};
+
+static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
+{
+	switch (format) {
+	case DRM_FORMAT_XRGB4444:
+		*mode = ATMEL_HLCDC_XRGB4444_MODE;
+		break;
+	case DRM_FORMAT_ARGB4444:
+		*mode = ATMEL_HLCDC_ARGB4444_MODE;
+		break;
+	case DRM_FORMAT_RGBA4444:
+		*mode = ATMEL_HLCDC_RGBA4444_MODE;
+		break;
+	case DRM_FORMAT_RGB565:
+		*mode = ATMEL_HLCDC_RGB565_MODE;
+		break;
+	case DRM_FORMAT_RGB888:
+		*mode = ATMEL_HLCDC_RGB888_MODE;
+		break;
+	case DRM_FORMAT_ARGB1555:
+		*mode = ATMEL_HLCDC_ARGB1555_MODE;
+		break;
+	case DRM_FORMAT_XRGB8888:
+		*mode = ATMEL_HLCDC_XRGB8888_MODE;
+		break;
+	case DRM_FORMAT_ARGB8888:
+		*mode = ATMEL_HLCDC_ARGB8888_MODE;
+		break;
+	case DRM_FORMAT_RGBA8888:
+		*mode = ATMEL_HLCDC_RGBA8888_MODE;
+		break;
+	case DRM_FORMAT_AYUV:
+		*mode = ATMEL_HLCDC_AYUV_MODE;
+		break;
+	case DRM_FORMAT_YUYV:
+		*mode = ATMEL_HLCDC_YUYV_MODE;
+		break;
+	case DRM_FORMAT_UYVY:
+		*mode = ATMEL_HLCDC_UYVY_MODE;
+		break;
+	case DRM_FORMAT_YVYU:
+		*mode = ATMEL_HLCDC_YVYU_MODE;
+		break;
+	case DRM_FORMAT_VYUY:
+		*mode = ATMEL_HLCDC_VYUY_MODE;
+		break;
+	case DRM_FORMAT_NV21:
+		*mode = ATMEL_HLCDC_NV21_MODE;
+		break;
+	case DRM_FORMAT_NV61:
+		*mode = ATMEL_HLCDC_NV61_MODE;
+		break;
+	case DRM_FORMAT_YUV420:
+		*mode = ATMEL_HLCDC_YUV420_MODE;
+		break;
+	case DRM_FORMAT_YUV422:
+		*mode = ATMEL_HLCDC_YUV422_MODE;
+		break;
+	default:
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+static bool atmel_hlcdc_format_embedds_alpha(u32 format)
+{
+	int i;
+
+	for (i = 0; i < sizeof(format); i++) {
+		char tmp = (format >> (8 * i)) & 0xff;
+
+		if (tmp == 'A')
+			return true;
+	}
+
+	return false;
+}
+
+static u32 heo_downscaling_xcoef[] = {
+	0x11343311,
+	0x000000f7,
+	0x1635300c,
+	0x000000f9,
+	0x1b362c08,
+	0x000000fb,
+	0x1f372804,
+	0x000000fe,
+	0x24382400,
+	0x00000000,
+	0x28371ffe,
+	0x00000004,
+	0x2c361bfb,
+	0x00000008,
+	0x303516f9,
+	0x0000000c,
+};
+
+static u32 heo_downscaling_ycoef[] = {
+	0x00123737,
+	0x00173732,
+	0x001b382d,
+	0x001f3928,
+	0x00243824,
+	0x0028391f,
+	0x002d381b,
+	0x00323717,
+};
+
+static u32 heo_upscaling_xcoef[] = {
+	0xf74949f7,
+	0x00000000,
+	0xf55f33fb,
+	0x000000fe,
+	0xf5701efe,
+	0x000000ff,
+	0xf87c0dff,
+	0x00000000,
+	0x00800000,
+	0x00000000,
+	0x0d7cf800,
+	0x000000ff,
+	0x1e70f5ff,
+	0x000000fe,
+	0x335ff5fe,
+	0x000000fb,
+};
+
+static u32 heo_upscaling_ycoef[] = {
+	0x00004040,
+	0x00075920,
+	0x00056f0c,
+	0x00027b03,
+	0x00008000,
+	0x00037b02,
+	0x000c6f05,
+	0x00205907,
+};
+
+static void
+atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	const struct atmel_hlcdc_layer_cfg_layout *layout =
+						&plane->layer.desc->layout;
+
+	if (layout->size)
+		atmel_hlcdc_layer_update_cfg(&plane->layer,
+					     layout->size,
+					     0xffffffff,
+					     (req->crtc_w - 1) |
+					     ((req->crtc_h - 1) << 16));
+
+	if (layout->memsize)
+		atmel_hlcdc_layer_update_cfg(&plane->layer,
+					     layout->memsize,
+					     0xffffffff,
+					     (req->src_w - 1) |
+					     ((req->src_h - 1) << 16));
+
+	if (layout->pos)
+		atmel_hlcdc_layer_update_cfg(&plane->layer,
+					     layout->pos,
+					     0xffffffff,
+					     req->crtc_x |
+					     (req->crtc_y  << 16));
+
+	/* TODO: rework the rescaling part */
+	if (req->crtc_w != req->src_w || req->crtc_h != req->src_h) {
+		u32 factor_reg = 0;
+
+		if (req->crtc_w != req->src_w) {
+			int i;
+			u32 factor;
+			u32 *coeff_tab = heo_upscaling_xcoef;
+			u32 max_memsize;
+
+			if (req->crtc_w < req->src_w)
+				coeff_tab = heo_downscaling_xcoef;
+			for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++)
+				atmel_hlcdc_layer_update_cfg(&plane->layer,
+							     17 + i,
+							     0xffffffff,
+							     coeff_tab[i]);
+			factor = ((8 * 256 * req->src_w) - (256 * 4)) /
+				 req->crtc_w;
+			factor++;
+			max_memsize = ((factor * req->crtc_w) + (256 * 4)) /
+				      2048;
+			if (max_memsize > req->src_w)
+				factor--;
+			factor_reg |= factor | 0x80000000;
+		}
+
+		if (req->crtc_h != req->src_h) {
+			int i;
+			u32 factor;
+			u32 *coeff_tab = heo_upscaling_ycoef;
+			u32 max_memsize;
+
+			if (req->crtc_w < req->src_w)
+				coeff_tab = heo_downscaling_ycoef;
+			for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
+				atmel_hlcdc_layer_update_cfg(&plane->layer,
+							     33 + i,
+							     0xffffffff,
+							     coeff_tab[i]);
+			factor = ((8 * 256 * req->src_w) - (256 * 4)) /
+				 req->crtc_w;
+			factor++;
+			max_memsize = ((factor * req->crtc_w) + (256 * 4)) /
+				      2048;
+			if (max_memsize > req->src_w)
+				factor--;
+			factor_reg |= (factor << 16) | 0x80000000;
+		}
+
+		atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff,
+					     factor_reg);
+	}
+}
+
+static void
+atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	const struct atmel_hlcdc_layer_cfg_layout *layout =
+						&plane->layer.desc->layout;
+	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA;
+
+	if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
+		cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
+		       ATMEL_HLCDC_LAYER_ITER;
+
+		if (atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format))
+			cfg |= ATMEL_HLCDC_LAYER_LAEN;
+		else
+			cfg |= ATMEL_HLCDC_LAYER_GAEN;
+	}
+
+	atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
+				     ATMEL_HLCDC_LAYER_ITER2BL |
+				     ATMEL_HLCDC_LAYER_ITER |
+				     ATMEL_HLCDC_LAYER_GAEN |
+				     ATMEL_HLCDC_LAYER_LAEN |
+				     ATMEL_HLCDC_LAYER_OVR |
+				     ATMEL_HLCDC_LAYER_DMA, cfg);
+}
+
+static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	u32 mode;
+	int ret;
+
+	ret = atmel_hlcdc_format_to_plane_mode(req->fb->pixel_format, &mode);
+	if (ret)
+		return;
+
+	atmel_hlcdc_layer_update_cfg(&plane->layer,
+				     ATMEL_HLCDC_LAYER_FORMAT_CFG_ID,
+				     0xffffffff,
+				     mode);
+}
+
+static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	struct atmel_hlcdc_layer *layer = &plane->layer;
+	const struct atmel_hlcdc_layer_cfg_layout *layout =
+							&layer->desc->layout;
+	int i;
+
+	atmel_hlcdc_layer_update_set_fb(&plane->layer, req->fb, req->offsets);
+
+	for (i = 0; i < req->nplanes; i++) {
+		if (layout->xstride[i]) {
+			atmel_hlcdc_layer_update_cfg(&plane->layer,
+						layout->xstride[i],
+						0xffffffff,
+						req->xstride[i]);
+		}
+
+		if (layout->pstride[i]) {
+			atmel_hlcdc_layer_update_cfg(&plane->layer,
+						layout->pstride[i],
+						0xffffffff,
+						req->pstride[i]);
+		}
+	}
+}
+
+static int atmel_hlcdc_plane_check_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	const struct atmel_hlcdc_layer_cfg_layout *layout =
+						&plane->layer.desc->layout;
+
+	if (!layout->size &&
+	    (req->crtc->mode.crtc_hdisplay != req->crtc_w ||
+	     req->crtc->mode.crtc_vdisplay != req->crtc_h))
+		return -EINVAL;
+
+	if (plane->layer.desc->max_height &&
+	    req->crtc_h > plane->layer.desc->max_height)
+		return -EINVAL;
+
+	if (plane->layer.desc->max_width &&
+	    req->crtc_w > plane->layer.desc->max_width)
+		return -EINVAL;
+
+	if ((req->crtc_h != req->src_h || req->crtc_w != req->src_w) &&
+	    (!layout->memsize ||
+	     atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format)))
+		return -EINVAL;
+
+	return 0;
+}
+
+int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	unsigned int patched_crtc_w;
+	unsigned int patched_crtc_h;
+	unsigned int patched_src_w;
+	unsigned int patched_src_h;
+	unsigned int tmp;
+	int x_offset = 0;
+	int y_offset = 0;
+	int i;
+
+	if ((req->src_x | req->src_y | req->src_w | req->src_h) &
+	    SUBPIXEL_MASK)
+		return -EINVAL;
+
+	req->src_x >>= 16;
+	req->src_y >>= 16;
+	req->src_w >>= 16;
+	req->src_h >>= 16;
+
+	req->nplanes = drm_format_num_planes(req->fb->pixel_format);
+	if (req->nplanes > ATMEL_HLCDC_MAX_PLANES)
+		return -EINVAL;
+
+	/*
+	 * Swap width and size if in case of 90 or 270 degrees rotation
+	 */
+	if (plane->rotation == ATMEL_HLCDC_PLANE_90DEG_ROTATION ||
+	    plane->rotation == ATMEL_HLCDC_PLANE_270DEG_ROTATION) {
+		tmp = req->crtc_w;
+		req->crtc_w = req->crtc_h;
+		req->crtc_h = tmp;
+		tmp = req->src_w;
+		req->src_w = req->src_h;
+		req->src_h = tmp;
+	}
+
+	if (req->crtc_x + req->crtc_w > req->crtc->mode.hdisplay)
+		patched_crtc_w = req->crtc->mode.hdisplay - req->crtc_x;
+	else
+		patched_crtc_w = req->crtc_w;
+
+	if (req->crtc_x < 0) {
+		patched_crtc_w += req->crtc_x;
+		x_offset = -req->crtc_x;
+		req->crtc_x = 0;
+	}
+
+	if (req->crtc_y + req->crtc_h > req->crtc->mode.vdisplay)
+		patched_crtc_h = req->crtc->mode.vdisplay - req->crtc_y;
+	else
+		patched_crtc_h = req->crtc_h;
+
+	if (req->crtc_y < 0) {
+		patched_crtc_h += req->crtc_y;
+		y_offset = -req->crtc_y;
+		req->crtc_y = 0;
+	}
+
+	patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * req->src_w,
+					  req->crtc_w);
+	patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * req->src_h,
+					  req->crtc_h);
+
+	for (i = 0; i < req->nplanes; i++) {
+		unsigned int offset = 0;
+
+		req->bpp[i] = drm_format_plane_cpp(req->fb->pixel_format, i);
+		if (!req->bpp[i])
+			return -EINVAL;
+
+		switch (plane->rotation) {
+		case ATMEL_HLCDC_PLANE_90DEG_ROTATION:
+			offset = (y_offset + req->src_y + patched_src_w - 1) *
+				 req->fb->pitches[i];
+			offset += (x_offset + req->src_x) * req->bpp[i];
+			req->xstride[i] = req->fb->pitches[i] *
+					  (patched_src_w - 1);
+			req->pstride[i] = -req->fb->pitches[i] - req->bpp[i];
+			break;
+		case ATMEL_HLCDC_PLANE_180DEG_ROTATION:
+			offset = (y_offset + req->src_y + patched_src_h - 1) *
+				 req->fb->pitches[i];
+			offset += (x_offset + req->src_x + patched_src_w) *
+				  req->bpp[i];
+			req->xstride[i] = ((patched_src_w - 2) * req->bpp[i]) -
+					   req->fb->pitches[i];
+			req->pstride[i] = -2 * req->bpp[i];
+			break;
+		case ATMEL_HLCDC_PLANE_270DEG_ROTATION:
+			offset = (y_offset + req->src_y) * req->fb->pitches[i];
+			offset += (x_offset + req->src_x + patched_src_h) *
+				  req->bpp[i];
+			req->xstride[i] = -(req->fb->pitches[i] *
+					    (patched_src_w - 1)) -
+					  (2 * req->bpp[i]);
+			req->pstride[i] = req->fb->pitches[i] - req->bpp[i];
+			break;
+		case ATMEL_HLCDC_PLANE_NO_ROTATION:
+		default:
+			offset = (y_offset + req->src_y) * req->fb->pitches[i];
+			offset += (x_offset + req->src_x) * req->bpp[i];
+			req->xstride[i] = req->fb->pitches[i] -
+					  (patched_src_w * req->bpp[i]);
+			req->pstride[i] = 0;
+			break;
+		}
+
+		req->offsets[i] = offset + req->fb->offsets[i];
+	}
+
+	req->src_w = patched_src_w;
+	req->src_h = patched_src_h;
+	req->crtc_w = patched_crtc_w;
+	req->crtc_h = patched_crtc_h;
+
+	return atmel_hlcdc_plane_check_update_req(p, req);
+}
+
+int atmel_hlcdc_plane_apply_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	int ret;
+
+	ret = atmel_hlcdc_layer_update_start(&plane->layer);
+	if (ret)
+		return ret;
+
+	atmel_hlcdc_plane_update_pos_and_size(plane, req);
+	atmel_hlcdc_plane_update_general_settings(plane, req);
+	atmel_hlcdc_plane_update_format(plane, req);
+	atmel_hlcdc_plane_update_buffers(plane, req);
+	atmel_hlcdc_layer_update_set_finished(&plane->layer, req->finished,
+					      req->finished_data);
+
+	atmel_hlcdc_layer_update_commit(&plane->layer);
+
+	return 0;
+}
+
+static int atmel_hlcdc_plane_update(struct drm_plane *p,
+				    struct drm_crtc *crtc,
+				    struct drm_framebuffer *fb,
+				    int crtc_x, int crtc_y,
+				    unsigned int crtc_w, unsigned int crtc_h,
+				    uint32_t src_x, uint32_t src_y,
+				    uint32_t src_w, uint32_t src_h)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	struct atmel_hlcdc_plane_update_req req;
+	int ret = 0;
+
+	memset(&req, 0, sizeof(req));
+	req.crtc_x = crtc_x;
+	req.crtc_y = crtc_y;
+	req.crtc_w = crtc_w;
+	req.crtc_h = crtc_h;
+	req.src_x = src_x;
+	req.src_y = src_y;
+	req.src_w = src_w;
+	req.src_h = src_h;
+	req.fb = fb;
+	req.crtc = crtc;
+
+	ret = atmel_hlcdc_plane_prepare_update_req(&plane->base, &req);
+	if (ret)
+		return ret;
+
+	if (!req.crtc_h || !req.crtc_w)
+		return atmel_hlcdc_layer_disable(&plane->layer);
+
+	return atmel_hlcdc_plane_apply_update_req(&plane->base, &req);
+}
+
+static int atmel_hlcdc_plane_disable(struct drm_plane *p)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+
+	return atmel_hlcdc_layer_disable(&plane->layer);
+}
+
+static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+
+	if (plane->base.fb)
+		drm_framebuffer_unreference(plane->base.fb);
+
+	atmel_hlcdc_layer_cleanup(p->dev, &plane->layer);
+
+	drm_plane_cleanup(p);
+	devm_kfree(p->dev->dev, plane);
+}
+
+static int atmel_hlcdc_plane_set_alpha(struct atmel_hlcdc_plane *plane,
+				       u8 alpha)
+{
+	atmel_hlcdc_layer_update_start(&plane->layer);
+	atmel_hlcdc_layer_update_cfg(&plane->layer,
+				     plane->layer.desc->layout.general_config,
+				     ATMEL_HLCDC_LAYER_GA_MASK,
+				     alpha << ATMEL_HLCDC_LAYER_GA_SHIFT);
+	atmel_hlcdc_layer_update_commit(&plane->layer);
+
+	return 0;
+}
+
+static int atmel_hlcdc_plane_set_rotation(struct atmel_hlcdc_plane *plane,
+					  enum atmel_hlcdc_plane_rotation rot)
+{
+	plane->rotation = rot;
+
+	return 0;
+}
+
+static int atmel_hlcdc_plane_set_property(struct drm_plane *p,
+					  struct drm_property *property,
+					  uint64_t value)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	struct atmel_hlcdc_plane_properties *props = plane->properties;
+
+	if (property == props->alpha)
+		atmel_hlcdc_plane_set_alpha(plane, value);
+	else if (property == props->rotation)
+		atmel_hlcdc_plane_set_rotation(plane, value);
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
+				const struct atmel_hlcdc_layer_desc *desc,
+				struct atmel_hlcdc_plane_properties *props)
+{
+	struct regmap *regmap = plane->layer.hlcdc->regmap;
+
+	if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
+	    desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
+		drm_object_attach_property(&plane->base.base,
+					   props->alpha, 255);
+
+		/* Set default alpha value */
+		regmap_update_bits(regmap,
+				desc->regs_offset +
+				ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer),
+				ATMEL_HLCDC_LAYER_GA_MASK,
+				ATMEL_HLCDC_LAYER_GA_MASK);
+	}
+
+	if (desc->layout.xstride && desc->layout.pstride)
+		drm_object_attach_property(&plane->base.base,
+					   props->rotation,
+					   ATMEL_HLCDC_PLANE_NO_ROTATION);
+
+	if (desc->layout.csc) {
+		/*
+		 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
+		 * userspace modify these factors (using a BLOB property ?).
+		 */
+		regmap_write(regmap,
+			     desc->regs_offset +
+			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0),
+			     0x4c900091);
+		regmap_write(regmap,
+			     desc->regs_offset +
+			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1),
+			     0x7a5f5090);
+		regmap_write(regmap,
+			     desc->regs_offset +
+			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2),
+			     0x40040890);
+	}
+}
+
+static struct drm_plane_funcs layer_plane_funcs = {
+	.update_plane = atmel_hlcdc_plane_update,
+	.disable_plane = atmel_hlcdc_plane_disable,
+	.set_property = atmel_hlcdc_plane_set_property,
+	.destroy = atmel_hlcdc_plane_destroy,
+};
+
+static struct atmel_hlcdc_plane *
+atmel_hlcdc_plane_create(struct drm_device *dev,
+			 const struct atmel_hlcdc_layer_desc *desc,
+			 struct atmel_hlcdc_plane_properties *props)
+{
+	struct atmel_hlcdc_plane *plane;
+	enum drm_plane_type type;
+	int ret;
+
+	plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
+	if (!plane)
+		return ERR_PTR(-ENOMEM);
+
+	ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc);
+	if (ret)
+		return ERR_PTR(ret);
+
+	if (desc->type == ATMEL_HLCDC_BASE_LAYER)
+		type = DRM_PLANE_TYPE_PRIMARY;
+	else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)
+		type = DRM_PLANE_TYPE_CURSOR;
+	else
+		type = DRM_PLANE_TYPE_OVERLAY;
+
+	ret = drm_universal_plane_init(dev, &plane->base, 0,
+				       &layer_plane_funcs,
+				       desc->formats->formats,
+				       desc->formats->nformats, type);
+	if (ret)
+		return ERR_PTR(ret);
+
+	/* Set default property values*/
+	atmel_hlcdc_plane_init_properties(plane, desc, props);
+
+	return plane;
+}
+
+static struct atmel_hlcdc_plane_properties *
+atmel_hlcdc_plane_create_properties(struct drm_device *dev)
+{
+	struct atmel_hlcdc_plane_properties *props;
+	const struct drm_prop_enum_list rotations[] = {
+		{ ATMEL_HLCDC_PLANE_NO_ROTATION,   "rotate-0" },
+		{ ATMEL_HLCDC_PLANE_90DEG_ROTATION,  "rotate-90" },
+		{ ATMEL_HLCDC_PLANE_180DEG_ROTATION, "rotate-180" },
+		{ ATMEL_HLCDC_PLANE_270DEG_ROTATION, "rotate-270" },
+	};
+
+	props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL);
+	if (!props)
+		return ERR_PTR(-ENOMEM);
+
+	props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255);
+	if (!props->alpha)
+		return ERR_PTR(-ENOMEM);
+
+	props->rotation = drm_property_create_enum(dev, 0, "rotation",
+						rotations,
+						ARRAY_SIZE(rotations));
+	return props;
+}
+
+struct atmel_hlcdc_planes *
+atmel_hlcdc_create_planes(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct atmel_hlcdc_plane_properties *props;
+	struct atmel_hlcdc_planes *planes;
+	const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
+	int nlayers = dc->desc->nlayers;
+	int i;
+
+	planes = devm_kzalloc(dev->dev, sizeof(*planes), GFP_KERNEL);
+	if (!planes)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < nlayers; i++) {
+		if (descs[i].type == ATMEL_HLCDC_OVERLAY_LAYER)
+			planes->noverlays++;
+	}
+
+	if (planes->noverlays) {
+		planes->overlays = devm_kzalloc(dev->dev,
+						planes->noverlays *
+						sizeof(*planes->overlays),
+						GFP_KERNEL);
+		if (!planes->overlays)
+			return ERR_PTR(-ENOMEM);
+	}
+
+	props = atmel_hlcdc_plane_create_properties(dev);
+	if (IS_ERR(props))
+		return ERR_CAST(props);
+
+	planes->noverlays = 0;
+	for (i = 0; i < nlayers; i++) {
+		struct atmel_hlcdc_plane *plane;
+
+		if (descs[i].type == ATMEL_HLCDC_PP_LAYER)
+			continue;
+
+		plane = atmel_hlcdc_plane_create(dev, &descs[i], props);
+		if (IS_ERR(plane))
+			return ERR_CAST(plane);
+
+		plane->properties = props;
+
+		switch (descs[i].type) {
+		case ATMEL_HLCDC_BASE_LAYER:
+			if (planes->primary)
+				return ERR_PTR(-EINVAL);
+			planes->primary = plane;
+			break;
+
+		case ATMEL_HLCDC_OVERLAY_LAYER:
+			planes->overlays[planes->noverlays++] = plane;
+			break;
+
+		case ATMEL_HLCDC_CURSOR_LAYER:
+			if (planes->cursor)
+				return ERR_PTR(-EINVAL);
+			planes->cursor = plane;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	return planes;
+}
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 05/11] drm: add Atmel HLCDC Display Controller support
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

The Atmel HLCDC (HLCD Controller) IP available on some Atmel SoCs (i.e.
at91sam9n12, at91sam9x5 family or sama5d3 family) provides a display
controller device.

This display controller supports at least one primary plane and might
provide several overlays and an hardware cursor depending on the IP
version.

At the moment, this driver only implements an RGB connector to interface
with LCD panels, but support for other kind of external devices might be
added later.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 drivers/gpu/drm/Kconfig                          |   2 +
 drivers/gpu/drm/Makefile                         |   1 +
 drivers/gpu/drm/atmel-hlcdc/Kconfig              |  11 +
 drivers/gpu/drm/atmel-hlcdc/Makefile             |   7 +
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c   | 286 ++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c     | 488 ++++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h     | 224 +++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c  | 635 ++++++++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h  | 396 +++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 478 ++++++++++++++
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c  | 804 +++++++++++++++++++++++
 11 files changed, 3332 insertions(+)
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/Kconfig
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/Makefile
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
 create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index f512004..9183a78 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -184,6 +184,8 @@ source "drivers/gpu/drm/cirrus/Kconfig"
 
 source "drivers/gpu/drm/armada/Kconfig"
 
+source "drivers/gpu/drm/atmel-hlcdc/Kconfig"
+
 source "drivers/gpu/drm/rcar-du/Kconfig"
 
 source "drivers/gpu/drm/shmobile/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index af9a609..07d388c 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_DRM_GMA500) += gma500/
 obj-$(CONFIG_DRM_UDL) += udl/
 obj-$(CONFIG_DRM_AST) += ast/
 obj-$(CONFIG_DRM_ARMADA) += armada/
+obj-$(CONFIG_DRM_ATMEL_HLCDC)	+= atmel-hlcdc/
 obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
 obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
 obj-$(CONFIG_DRM_OMAP)	+= omapdrm/
diff --git a/drivers/gpu/drm/atmel-hlcdc/Kconfig b/drivers/gpu/drm/atmel-hlcdc/Kconfig
new file mode 100644
index 0000000..bc07315
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/Kconfig
@@ -0,0 +1,11 @@
+config DRM_ATMEL_HLCDC
+	tristate "DRM Support for ATMEL HLCDC Display Controller"
+	depends on DRM && OF && MFD_ATMEL_HLCDC && COMMON_CLK
+	select DRM_GEM_CMA_HELPER
+	select DRM_KMS_HELPER
+	select DRM_KMS_FB_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_PANEL
+	help
+	  Choose this option if you have an ATMEL SoC with an HLCDC display
+	  controller (i.e. at91sam9n12, at91sam9x5 family or sama5d3 family).
diff --git a/drivers/gpu/drm/atmel-hlcdc/Makefile b/drivers/gpu/drm/atmel-hlcdc/Makefile
new file mode 100644
index 0000000..10ae426
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/Makefile
@@ -0,0 +1,7 @@
+atmel-hlcdc-dc-y := atmel_hlcdc_crtc.o \
+		atmel_hlcdc_dc.o \
+		atmel_hlcdc_layer.o \
+		atmel_hlcdc_output.o \
+		atmel_hlcdc_plane.o
+
+obj-$(CONFIG_DRM_ATMEL_HLCDC)	+= atmel-hlcdc-dc.o
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
new file mode 100644
index 0000000..2186830
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2014 Free Electrons
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drmP.h>
+
+#include <video/videomode.h>
+
+#include "atmel_hlcdc_dc.h"
+
+/**
+ * Atmel HLCDC CRTC structure
+ *
+ * @base: base DRM CRTC structure
+ * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
+ * @event: pointer to the current page flip event
+ * @id: CRTC id (returned by drm_crtc_index)
+ * @dpms: DPMS mode
+ */
+struct atmel_hlcdc_crtc {
+	struct drm_crtc base;
+	struct atmel_hlcdc *hlcdc;
+	struct drm_pending_vblank_event *event;
+	int id;
+	int dpms;
+};
+
+static inline struct atmel_hlcdc_crtc *
+drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct atmel_hlcdc_crtc, base);
+}
+
+
+static void atmel_hlcdc_crtc_dpms(struct drm_crtc *c, int mode)
+{
+	struct drm_device *dev = c->dev;
+
+	if (mode != DRM_MODE_DPMS_ON)
+		mode = DRM_MODE_DPMS_OFF;
+
+	pm_runtime_get_sync(dev->dev);
+
+	if (mode == DRM_MODE_DPMS_ON)
+		pm_runtime_forbid(dev->dev);
+	else
+		pm_runtime_allow(dev->dev);
+
+	pm_runtime_put_sync(dev->dev);
+}
+
+static int atmel_hlcdc_crtc_mode_set(struct drm_crtc *c,
+				     struct drm_display_mode *mode,
+				     struct drm_display_mode *adjusted,
+				     int x, int y,
+				     struct drm_framebuffer *old_fb)
+{
+	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
+	struct regmap *regmap = crtc->hlcdc->regmap;
+	struct drm_plane *plane = c->primary;
+	struct drm_framebuffer *fb;
+	struct videomode vm;
+
+	vm.vfront_porch = mode->vsync_start - mode->vdisplay;
+	vm.vback_porch = mode->vtotal - mode->vsync_end;
+	vm.vsync_len = mode->vsync_end - mode->vsync_start;
+	vm.hfront_porch = mode->hsync_start - mode->hdisplay;
+	vm.hback_porch = mode->htotal - mode->hsync_end;
+	vm.hsync_len = mode->hsync_end - mode->hsync_start;
+
+	if (vm.hsync_len > 0x40 || vm.hsync_len < 1 ||
+	    vm.vsync_len > 0x40 || vm.vsync_len < 1 ||
+	    vm.vfront_porch > 0x40 || vm.vfront_porch < 1 ||
+	    vm.vback_porch > 0x40 || vm.vback_porch < 0 ||
+	    vm.hfront_porch > 0x200 || vm.hfront_porch < 1 ||
+	    vm.hback_porch > 0x200 || vm.hback_porch < 1 ||
+	    mode->hdisplay > 2048 || mode->hdisplay < 1 ||
+	    mode->vdisplay > 2048 || mode->vdisplay < 1)
+		return -EINVAL;
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(1),
+		     (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16));
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(2),
+		     (vm.vfront_porch - 1) | (vm.vback_porch << 16));
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(3),
+		     (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16));
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(4),
+		     (mode->hdisplay - 1) | ((mode->vdisplay - 1) << 16));
+
+	fb = plane->fb;
+	plane->fb = old_fb;
+
+	return plane->funcs->update_plane(plane, c, fb,
+					  0, 0,
+					  mode->hdisplay, mode->vdisplay,
+					  c->x << 16, c->y << 16,
+					  mode->hdisplay << 16,
+					  mode->vdisplay << 16);
+}
+
+static void atmel_hlcdc_crtc_prepare(struct drm_crtc *crtc)
+{
+	atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static void atmel_hlcdc_crtc_commit(struct drm_crtc *crtc)
+{
+	atmel_hlcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *crtc,
+					const struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+
+static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = {
+
+	.mode_fixup = atmel_hlcdc_crtc_mode_fixup,
+	.dpms = atmel_hlcdc_crtc_dpms,
+	.mode_set = atmel_hlcdc_crtc_mode_set,
+	.prepare = atmel_hlcdc_crtc_prepare,
+	.commit = atmel_hlcdc_crtc_commit,
+};
+
+static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c)
+{
+	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
+
+	drm_crtc_cleanup(c);
+	kfree(crtc);
+}
+
+void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *c,
+				       struct drm_file *file)
+{
+	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
+	struct drm_pending_vblank_event *event;
+	struct drm_device *dev = c->dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	event = crtc->event;
+	if (event && event->base.file_priv == file) {
+		event->base.destroy(&event->base);
+		drm_vblank_put(dev, crtc->id);
+		crtc->event = NULL;
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static void atmel_hlcdc_crtc_finish_page_flip(void *data)
+{
+	struct atmel_hlcdc_crtc *crtc = data;
+	struct drm_device *dev = crtc->base.dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	if (crtc->event) {
+		drm_send_vblank_event(dev, crtc->id, crtc->event);
+		drm_vblank_put(dev, crtc->id);
+		crtc->event = NULL;
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int atmel_hlcdc_crtc_page_flip(struct drm_crtc *c,
+				      struct drm_framebuffer *fb,
+				      struct drm_pending_vblank_event *event,
+				      uint32_t page_flip_flags)
+{
+	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
+	struct atmel_hlcdc_plane_update_req req;
+	struct drm_plane *plane = c->primary;
+	int ret;
+
+	if (crtc->event)
+		return -EBUSY;
+
+	memset(&req, 0, sizeof(req));
+	req.crtc_x = 0;
+	req.crtc_y = 0;
+	req.crtc_h = c->mode.crtc_vdisplay;
+	req.crtc_w = c->mode.crtc_hdisplay;
+	req.src_x = c->x << 16;
+	req.src_y = c->y << 16;
+	req.src_w = req.crtc_w << 16;
+	req.src_h = req.crtc_h << 16;
+	req.fb = fb;
+	req.crtc = c;
+	req.finished = atmel_hlcdc_crtc_finish_page_flip;
+	req.finished_data = crtc;
+
+	ret = atmel_hlcdc_plane_prepare_update_req(plane, &req);
+	if (ret)
+		return ret;
+
+	if (event) {
+		crtc->event = event;
+		drm_vblank_get(c->dev, crtc->id);
+	}
+
+	ret = atmel_hlcdc_plane_apply_update_req(plane, &req);
+	if (ret) {
+		crtc->event = NULL;
+		drm_vblank_put(c->dev, crtc->id);
+	} else {
+		plane->fb = fb;
+	}
+
+	return ret;
+}
+
+static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
+	.page_flip = atmel_hlcdc_crtc_page_flip,
+	.set_config = drm_crtc_helper_set_config,
+	.destroy = atmel_hlcdc_crtc_destroy,
+};
+
+int atmel_hlcdc_crtc_create(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct atmel_hlcdc_planes *planes = dc->planes;
+	struct atmel_hlcdc_crtc *crtc;
+	int ret;
+	int i;
+
+	crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
+	if (!crtc) {
+		dev_err(dev->dev, "allocation failed\n");
+		return -ENOMEM;
+	}
+
+	crtc->hlcdc = dc->hlcdc;
+
+	ret = drm_crtc_init_with_planes(dev, &crtc->base,
+				&planes->primary->base,
+				planes->cursor ? &planes->cursor->base : NULL,
+				&atmel_hlcdc_crtc_funcs);
+	if (ret < 0)
+		goto fail;
+
+	crtc->id = drm_crtc_index(&crtc->base);
+
+	if (planes->cursor)
+		planes->cursor->base.possible_crtcs = 1 << crtc->id;
+
+	for (i = 0; i < planes->noverlays; i++)
+		planes->overlays[i]->base.possible_crtcs = 1 << crtc->id;
+
+	drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
+
+	return 0;
+
+fail:
+	atmel_hlcdc_crtc_destroy(&crtc->base);
+	return ret;
+}
+
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
new file mode 100644
index 0000000..9581977
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#include "atmel_hlcdc_dc.h"
+
+#define ATMEL_HLCDC_LAYER_IRQS_OFFSET		8
+
+static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = {
+	{
+		.name = "base",
+		.formats = &atmel_hlcdc_plane_rgb_formats,
+		.regs_offset = 0x40,
+		.id = 0,
+		.type = ATMEL_HLCDC_BASE_LAYER,
+		.nconfigs = 7,
+		.layout = {
+			.xstride = { 2 },
+			.default_color = 3,
+			.general_config = 4,
+			.disc_pos = 5,
+			.disc_size = 6,
+		},
+	},
+	{
+		.name = "overlay1",
+		.formats = &atmel_hlcdc_plane_rgb_formats,
+		.regs_offset = 0x140,
+		.id = 1,
+		.type = ATMEL_HLCDC_OVERLAY_LAYER,
+		.nconfigs = 10,
+		.layout = {
+			.pos = 2,
+			.size = 3,
+			.xstride = { 4 },
+			.pstride = { 5 },
+			.default_color = 6,
+			.chroma_key = 7,
+			.chroma_key_mask = 8,
+			.general_config = 9,
+		},
+	},
+	{
+		.name = "overlay2",
+		.formats = &atmel_hlcdc_plane_rgb_formats,
+		.regs_offset = 0x240,
+		.id = 2,
+		.type = ATMEL_HLCDC_OVERLAY_LAYER,
+		.nconfigs = 10,
+		.layout = {
+			.pos = 2,
+			.size = 3,
+			.xstride = { 4 },
+			.pstride = { 5 },
+			.default_color = 6,
+			.chroma_key = 7,
+			.chroma_key_mask = 8,
+			.general_config = 9,
+		},
+	},
+	{
+		.name = "high-end-overlay",
+		.formats = &atmel_hlcdc_plane_rgb_and_yuv_formats,
+		.regs_offset = 0x340,
+		.id = 3,
+		.type = ATMEL_HLCDC_OVERLAY_LAYER,
+		.nconfigs = 42,
+		.layout = {
+			.pos = 2,
+			.size = 3,
+			.memsize = 4,
+			.xstride = { 5, 7 },
+			.pstride = { 6, 8 },
+			.default_color = 9,
+			.chroma_key = 10,
+			.chroma_key_mask = 11,
+			.general_config = 12,
+			.csc = 14,
+		},
+	},
+	{
+		.name = "cursor",
+		.formats = &atmel_hlcdc_plane_rgb_formats,
+		.regs_offset = 0x440,
+		.id = 4,
+		.type = ATMEL_HLCDC_CURSOR_LAYER,
+		.nconfigs = 10,
+		.max_width = 128,
+		.max_height = 128,
+		.layout = {
+			.pos = 2,
+			.size = 3,
+			.xstride = { 4 },
+			.pstride = { 5 },
+			.default_color = 6,
+			.chroma_key = 7,
+			.chroma_key_mask = 8,
+			.general_config = 9,
+		},
+	},
+};
+
+static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = {
+	.min_width = 0,
+	.min_height = 0,
+	.max_width = 2048,
+	.max_height = 2048,
+	.nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers),
+	.layers = atmel_hlcdc_sama5d3_layers,
+};
+
+static const struct of_device_id atmel_hlcdc_of_match[] = {
+	{
+		.compatible = "atmel,sama5d3-hlcdc",
+		.data = &atmel_hlcdc_dc_sama5d3,
+	},
+	{ /* sentinel */ },
+};
+
+static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data)
+{
+	struct drm_device *dev = data;
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	unsigned long status;
+	unsigned int imr, isr;
+	int bit;
+
+	regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr);
+	regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
+	status = imr & isr;
+	if (!status)
+		return IRQ_NONE;
+
+	bit = ATMEL_HLCDC_LAYER_IRQS_OFFSET;
+	for_each_set_bit_from(bit, &status, ATMEL_HLCDC_LAYER_IRQS_OFFSET +
+					    ATMEL_HLCDC_MAX_LAYERS) {
+		int layerid = bit - ATMEL_HLCDC_LAYER_IRQS_OFFSET;
+		struct atmel_hlcdc_layer *layer = dc->layers[layerid];
+
+		if (layer)
+			atmel_hlcdc_layer_irq(layer);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev,
+		struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	return drm_fb_cma_create(dev, file_priv, mode_cmd);
+}
+
+static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+
+	if (dc->fbdev) {
+		drm_fbdev_cma_hotplug_event(dc->fbdev);
+	} else {
+		dc->fbdev = drm_fbdev_cma_init(dev, 24,
+				dev->mode_config.num_crtc,
+				dev->mode_config.num_connector);
+		if (IS_ERR(dc->fbdev))
+			dc->fbdev = NULL;
+	}
+}
+
+static const struct drm_mode_config_funcs mode_config_funcs = {
+	.fb_create = atmel_hlcdc_fb_create,
+	.output_poll_changed = atmel_hlcdc_fb_output_poll_changed,
+};
+
+static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct atmel_hlcdc_planes *planes;
+	int ret;
+	int i;
+
+	drm_mode_config_init(dev);
+
+	ret = atmel_hlcdc_create_outputs(dev);
+	if (ret) {
+		dev_err(dev->dev, "failed to create panel: %d\n", ret);
+		return ret;
+	}
+
+	planes = atmel_hlcdc_create_planes(dev);
+	if (IS_ERR(planes)) {
+		dev_err(dev->dev, "failed to create planes\n");
+		return PTR_ERR(planes);
+	}
+
+	dc->planes = planes;
+
+	dc->layers[planes->primary->layer.desc->id] =
+						&planes->primary->layer;
+
+	if (planes->cursor)
+		dc->layers[planes->cursor->layer.desc->id] =
+							&planes->cursor->layer;
+
+	for (i = 0; i < planes->noverlays; i++)
+		dc->layers[planes->overlays[i]->layer.desc->id] =
+						&planes->overlays[i]->layer;
+
+	ret = atmel_hlcdc_crtc_create(dev);
+	if (ret) {
+		dev_err(dev->dev, "failed to create crtc\n");
+		return ret;
+	}
+
+	dev->mode_config.min_width = dc->desc->min_width;
+	dev->mode_config.min_height = dc->desc->min_height;
+	dev->mode_config.max_width = dc->desc->max_width;
+	dev->mode_config.max_height = dc->desc->max_height;
+	dev->mode_config.funcs = &mode_config_funcs;
+
+	return 0;
+}
+
+static int atmel_hlcdc_dc_load(struct drm_device *dev, unsigned long flags)
+{
+	struct platform_device *pdev = dev->platformdev;
+	const struct of_device_id *match;
+	struct atmel_hlcdc_dc *dc;
+	int irq;
+	int ret;
+
+	match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node);
+	if (!match) {
+		dev_err(&pdev->dev, "invalid compatible string\n");
+		return -ENODEV;
+	}
+
+	if (!match->data) {
+		dev_err(&pdev->dev, "invalid hlcdc description\n");
+		return -EINVAL;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL);
+	if (!dc) {
+		dev_err(dev->dev, "failed to allocate private data\n");
+		return -ENOMEM;
+	}
+
+	dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0);
+	if (!dc->wq)
+		return -ENOMEM;
+
+	dc->desc = match->data;
+	dc->hlcdc = dev_get_drvdata(dev->dev->parent);
+	dev->dev_private = dc;
+
+	ret = clk_prepare_enable(dc->hlcdc->periph_clk);
+	if (ret) {
+		dev_err(dev->dev, "failed to enable periph_clk\n");
+		goto err_destroy_wq;
+	}
+
+	pm_runtime_enable(dev->dev);
+
+	pm_runtime_put_sync(dev->dev);
+
+	ret = atmel_hlcdc_dc_modeset_init(dev);
+	if (ret < 0) {
+		dev_err(dev->dev, "failed to initialize mode setting\n");
+		goto err_periph_clk_disable;
+	}
+
+	ret = drm_vblank_init(dev, 1);
+	if (ret < 0) {
+		dev_err(dev->dev, "failed to initialize vblank\n");
+		goto err_periph_clk_disable;
+	}
+
+	pm_runtime_get_sync(dev->dev);
+	ret = drm_irq_install(dev, irq);
+	pm_runtime_put_sync(dev->dev);
+	if (ret < 0) {
+		dev_err(dev->dev, "failed to install IRQ handler\n");
+		goto err_periph_clk_disable;
+	}
+
+	platform_set_drvdata(pdev, dev);
+
+	drm_kms_helper_poll_init(dev);
+
+	/* force connectors detection */
+	drm_helper_hpd_irq_event(dev);
+
+	return 0;
+
+err_periph_clk_disable:
+	clk_disable_unprepare(dc->hlcdc->periph_clk);
+
+err_destroy_wq:
+	destroy_workqueue(dc->wq);
+
+	return ret;
+}
+
+static int atmel_hlcdc_dc_unload(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+
+	drm_kms_helper_poll_fini(dev);
+	drm_mode_config_cleanup(dev);
+	drm_vblank_cleanup(dev);
+
+	pm_runtime_get_sync(dev->dev);
+	drm_irq_uninstall(dev);
+	pm_runtime_put_sync(dev->dev);
+
+	dev->dev_private = NULL;
+
+	pm_runtime_disable(dev->dev);
+	clk_disable_unprepare(dc->hlcdc->periph_clk);
+
+	flush_workqueue(dc->wq);
+	destroy_workqueue(dc->wq);
+
+	return 0;
+}
+
+static void atmel_hlcdc_dc_preclose(struct drm_device *dev,
+				    struct drm_file *file)
+{
+	struct drm_crtc *crtc;
+
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+		atmel_hlcdc_crtc_cancel_page_flip(crtc, file);
+}
+
+static void atmel_hlcdc_dc_lastclose(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+
+	drm_fbdev_cma_restore_mode(dc->fbdev);
+}
+
+static void atmel_hlcdc_dc_irq_preinstall(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	unsigned int isr;
+
+	regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff);
+	regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr);
+}
+
+static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	int i;
+
+	/* Enable interrupts on activated layers */
+	for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
+		if (dc->layers[i])
+			regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER,
+				     BIT(i + 8));
+	}
+
+	return 0;
+}
+
+static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev)
+{
+
+}
+
+static int atmel_hlcdc_dc_enable_vblank(struct drm_device *dev, int crtc)
+{
+	return 0;
+}
+
+static void atmel_hlcdc_dc_disable_vblank(struct drm_device *dev, int crtc)
+{
+}
+
+static const struct file_operations fops = {
+	.owner              = THIS_MODULE,
+	.open               = drm_open,
+	.release            = drm_release,
+	.unlocked_ioctl     = drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl       = drm_compat_ioctl,
+#endif
+	.poll               = drm_poll,
+	.read               = drm_read,
+	.llseek             = no_llseek,
+	.mmap               = drm_gem_cma_mmap,
+};
+
+static struct drm_driver atmel_hlcdc_dc_driver = {
+	.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET,
+	.load = atmel_hlcdc_dc_load,
+	.unload = atmel_hlcdc_dc_unload,
+	.preclose = atmel_hlcdc_dc_preclose,
+	.lastclose = atmel_hlcdc_dc_lastclose,
+	.irq_handler = atmel_hlcdc_dc_irq_handler,
+	.irq_preinstall = atmel_hlcdc_dc_irq_preinstall,
+	.irq_postinstall = atmel_hlcdc_dc_irq_postinstall,
+	.irq_uninstall = atmel_hlcdc_dc_irq_uninstall,
+	.get_vblank_counter = drm_vblank_count,
+	.enable_vblank = atmel_hlcdc_dc_enable_vblank,
+	.disable_vblank = atmel_hlcdc_dc_disable_vblank,
+	.gem_free_object = drm_gem_cma_free_object,
+	.gem_vm_ops = &drm_gem_cma_vm_ops,
+	.dumb_create = drm_gem_cma_dumb_create,
+	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
+	.dumb_destroy = drm_gem_dumb_destroy,
+	.fops = &fops,
+	.name = "atmel-hlcdc",
+	.desc = "Atmel HLCD Controller DRM",
+	.date = "20141504",
+	.major = 1,
+	.minor = 0,
+};
+
+static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+	if (ret)
+		return ret;
+
+	ret = drm_platform_init(&atmel_hlcdc_dc_driver, pdev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev)
+{
+	drm_put_dev(platform_get_drvdata(pdev));
+
+	return 0;
+}
+
+static const struct of_device_id atmel_hlcdc_dc_of_match[] = {
+	{ .compatible = "atmel,hlcdc-display-controller" },
+	{ },
+};
+
+static struct platform_driver atmel_hlcdc_dc_platform_driver = {
+	.probe	= atmel_hlcdc_dc_drm_probe,
+	.remove	= atmel_hlcdc_dc_drm_remove,
+	.driver	= {
+		.name	= "atmel-hlcdc-display-controller",
+		.of_match_table = atmel_hlcdc_dc_of_match,
+	},
+};
+module_platform_driver(atmel_hlcdc_dc_platform_driver);
+
+MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>");
+MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atmel-hlcdc-dc");
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
new file mode 100644
index 0000000..a67df13
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DRM_ATMEL_HLCDC_H
+#define DRM_ATMEL_HLCDC_H
+
+#include <linux/clk.h>
+#include <linux/irqdomain.h>
+#include <linux/pwm.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drmP.h>
+
+#include "atmel_hlcdc_layer.h"
+
+#define ATMEL_HLCDC_MAX_LAYERS		5
+
+/**
+ * Atmel HLCDC Display Controller description structure.
+ *
+ * This structure describe the HLCDC IP capabilities and depends on the
+ * HLCDC IP version (or Atmel SoC family).
+ *
+ * @min_width: minimum width supported by the Display Controller
+ * @min_height: minimum height supported by the Display Controller
+ * @max_width: maximum width supported by the Display Controller
+ * @max_height: maximum height supported by the Display Controller
+ * @layer: a layer description table describing available layers
+ * @nlayers: layer description table size
+ */
+struct atmel_hlcdc_dc_desc {
+	int min_width;
+	int min_height;
+	int max_width;
+	int max_height;
+	const struct atmel_hlcdc_layer_desc *layers;
+	int nlayers;
+};
+
+/**
+ * Atmel HLCDC Plane properties.
+ *
+ * This structure stores plane property definitions.
+ *
+ * @alpha: alpha blending (or transparency) property
+ * @csc: YUV to RGB conversion factors property
+ */
+struct atmel_hlcdc_plane_properties {
+	struct drm_property *alpha;
+	struct drm_property *rotation;
+};
+
+/**
+ * Atmel HLCDC plane rotation enum
+ *
+ * TODO: export DRM_ROTATE_XX macros defined by omap driver and use them
+ * instead of defining this enum.
+ */
+enum atmel_hlcdc_plane_rotation {
+	ATMEL_HLCDC_PLANE_NO_ROTATION,
+	ATMEL_HLCDC_PLANE_90DEG_ROTATION,
+	ATMEL_HLCDC_PLANE_180DEG_ROTATION,
+	ATMEL_HLCDC_PLANE_270DEG_ROTATION,
+};
+
+/**
+ * Atmel HLCDC Plane.
+ *
+ * @base: base DRM plane structure
+ * @layer: HLCDC layer structure
+ * @properties: pointer to the property definitions structure
+ * @alpha: current alpha blending (or transparency) status
+ */
+struct atmel_hlcdc_plane {
+	struct drm_plane base;
+	struct atmel_hlcdc_layer layer;
+	struct atmel_hlcdc_plane_properties *properties;
+	enum atmel_hlcdc_plane_rotation rotation;
+};
+
+static inline struct atmel_hlcdc_plane *
+drm_plane_to_atmel_hlcdc_plane(struct drm_plane *p)
+{
+	return container_of(p, struct atmel_hlcdc_plane, base);
+}
+
+static inline struct atmel_hlcdc_plane *
+atmel_hlcdc_layer_to_plane(struct atmel_hlcdc_layer *l)
+{
+	return container_of(l, struct atmel_hlcdc_plane, layer);
+}
+
+/**
+ * Atmel HLCDC Plane update request structure.
+ *
+ * @crtc_x: x position of the plane relative to the CRTC
+ * @crtc_y: y position of the plane relative to the CRTC
+ * @crtc_w: visible width of the plane
+ * @crtc_h: visible height of the plane
+ * @src_x: x buffer position
+ * @src_y: y buffer position
+ * @src_w: buffer width
+ * @src_h: buffer height
+ * @pixel_format: pixel format
+ * @gems: GEM object object containing image buffers
+ * @offsets: offsets to apply to the GEM buffers
+ * @pitches: line size in bytes
+ * @crtc: crtc to display on
+ * @finished: finished callback
+ * @finished_data: data passed to the finished callback
+ * @bpp: bytes per pixel deduced from pixel_format
+ * @xstride: value to add to the pixel pointer between each line
+ * @pstride: value to add to the pixel pointer between each pixel
+ * @nplanes: number of planes (deduced from pixel_format)
+ */
+struct atmel_hlcdc_plane_update_req {
+	int crtc_x;
+	int crtc_y;
+	unsigned int crtc_w;
+	unsigned int crtc_h;
+	uint32_t src_x;
+	uint32_t src_y;
+	uint32_t src_w;
+	uint32_t src_h;
+	struct drm_framebuffer *fb;
+	struct drm_crtc *crtc;
+	void (*finished)(void *data);
+	void *finished_data;
+
+	/* These fields are private and should not be touched */
+	int bpp[ATMEL_HLCDC_MAX_PLANES];
+	unsigned int offsets[ATMEL_HLCDC_MAX_PLANES];
+	int xstride[ATMEL_HLCDC_MAX_PLANES];
+	int pstride[ATMEL_HLCDC_MAX_PLANES];
+	int nplanes;
+};
+
+/**
+ * Atmel HLCDC Planes.
+ *
+ * This structure stores the instantiated HLCDC Planes and can be accessed by
+ * the HLCDC Display Controller or the HLCDC CRTC.
+ *
+ * @primary: primary plane
+ * @cursor: hardware cursor plane
+ * @overlays: overlay plane table
+ * @noverlays: number of overlay planes
+ */
+struct atmel_hlcdc_planes {
+	struct atmel_hlcdc_plane *primary;
+	struct atmel_hlcdc_plane *cursor;
+	struct atmel_hlcdc_plane **overlays;
+	int noverlays;
+};
+
+/**
+ * Atmel HLCDC Display Controller.
+ *
+ * @desc: HLCDC Display Controller description
+ * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
+ * @fbdev: framebuffer device attached to the Display Controller
+ * @planes: instantiated planes
+ * @layers: active HLCDC layer
+ * @wq: display controller workqueue
+ */
+struct atmel_hlcdc_dc {
+	const struct atmel_hlcdc_dc_desc *desc;
+	struct atmel_hlcdc *hlcdc;
+	struct drm_fbdev_cma *fbdev;
+	struct atmel_hlcdc_planes *planes;
+	struct atmel_hlcdc_layer *layers[ATMEL_HLCDC_MAX_LAYERS];
+	struct workqueue_struct *wq;
+};
+
+extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats;
+extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats;
+
+struct atmel_hlcdc_planes *
+atmel_hlcdc_create_planes(struct drm_device *dev);
+
+int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req);
+
+int atmel_hlcdc_plane_apply_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req);
+
+void atmel_hlcdc_crtc_cancel_page_flip(struct drm_crtc *crtc,
+				       struct drm_file *file);
+
+int atmel_hlcdc_crtc_create(struct drm_device *dev);
+
+int atmel_hlcdc_create_outputs(struct drm_device *dev);
+
+struct atmel_hlcdc_pwm_chip *atmel_hlcdc_pwm_create(struct drm_device *dev,
+						    struct clk *slow_clk,
+						    struct clk *sys_clk,
+						    void __iomem *regs);
+
+int atmel_hlcdc_pwm_destroy(struct drm_device *dev,
+			    struct atmel_hlcdc_pwm_chip *chip);
+
+#endif /* DRM_ATMEL_HLCDC_H */
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
new file mode 100644
index 0000000..d31c2e4
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
@@ -0,0 +1,635 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+
+#include "atmel_hlcdc_dc.h"
+
+static void
+atmel_hlcdc_layer_fb_flip_release(struct drm_flip_work *work, void *val)
+{
+	struct atmel_hlcdc_layer_fb_flip *flip = val;
+
+	if (flip->fb)
+		drm_framebuffer_unreference(flip->fb);
+	kfree(flip);
+}
+
+static void
+atmel_hlcdc_layer_fb_flip_destroy(struct atmel_hlcdc_layer_fb_flip *flip)
+{
+	if (flip->fb)
+		drm_framebuffer_unreference(flip->fb);
+	kfree(flip->task);
+	kfree(flip);
+}
+
+static void
+atmel_hlcdc_layer_fb_flip_release_queue(struct atmel_hlcdc_layer *layer,
+					struct atmel_hlcdc_layer_fb_flip *flip)
+{
+	int i;
+
+	if (!flip)
+		return;
+
+	for (i = 0; i < layer->max_planes; i++) {
+		if (!flip->dscrs[i])
+			break;
+
+		flip->dscrs[i]->status = 0;
+		flip->dscrs[i] = NULL;
+	}
+
+	drm_flip_work_queue_task(&layer->gc, flip->task);
+	drm_flip_work_commit(&layer->gc, layer->wq);
+}
+
+static void atmel_hlcdc_layer_update_reset(struct atmel_hlcdc_layer *layer,
+					   int id)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_update_slot *slot;
+
+	if (id < 0 || id > 1)
+		return;
+
+	slot = &upd->slots[id];
+	bitmap_clear(slot->updated_configs, 0, layer->desc->nconfigs);
+	memset(slot->configs, 0,
+	       sizeof(*slot->configs) * layer->desc->nconfigs);
+
+	if (slot->fb_flip) {
+		atmel_hlcdc_layer_fb_flip_release_queue(layer, slot->fb_flip);
+		slot->fb_flip = NULL;
+	}
+}
+
+static void atmel_hlcdc_layer_update_apply(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct regmap *regmap = layer->hlcdc->regmap;
+	struct atmel_hlcdc_layer_update_slot *slot;
+	struct atmel_hlcdc_layer_fb_flip *fb_flip;
+	struct atmel_hlcdc_dma_channel_dscr *dscr;
+	unsigned int cfg;
+	u32 action = 0;
+	int i = 0;
+
+	if (upd->pending < 0 || upd->pending > 1 ||
+	    dma->status == ATMEL_HLCDC_LAYER_DISABLING)
+		return;
+
+	slot = &upd->slots[upd->pending];
+
+	for_each_set_bit(cfg, slot->updated_configs, layer->desc->nconfigs) {
+		regmap_write(regmap,
+			     desc->regs_offset +
+			     ATMEL_HLCDC_LAYER_CFG(layer, cfg),
+			     slot->configs[cfg]);
+		action |= ATMEL_HLCDC_LAYER_UPDATE;
+	}
+
+	fb_flip = slot->fb_flip;
+
+	if (!fb_flip->fb)
+		goto apply;
+
+	if (dma->status == ATMEL_HLCDC_LAYER_DISABLED) {
+		for (i = 0; i < fb_flip->ngems; i++) {
+			dscr =  fb_flip->dscrs[i];
+			dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
+				     ATMEL_HLCDC_LAYER_DMA_IRQ |
+				     ATMEL_HLCDC_LAYER_ADD_IRQ |
+				     ATMEL_HLCDC_LAYER_DONE_IRQ;
+
+			regmap_write(regmap,
+				     desc->regs_offset +
+				     ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
+				     dscr->addr);
+			regmap_write(regmap,
+				     desc->regs_offset +
+				     ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
+				     dscr->ctrl);
+			regmap_write(regmap,
+				     desc->regs_offset +
+				     ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
+				     dscr->next);
+		}
+
+		action |= ATMEL_HLCDC_LAYER_DMA_CHAN;
+		dma->status = ATMEL_HLCDC_LAYER_ENABLED;
+	} else {
+		for (i = 0; i < fb_flip->ngems; i++) {
+			dscr =  fb_flip->dscrs[i];
+			dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH |
+				     ATMEL_HLCDC_LAYER_DMA_IRQ |
+				     ATMEL_HLCDC_LAYER_DSCR_IRQ |
+				     ATMEL_HLCDC_LAYER_DONE_IRQ;
+
+			regmap_write(regmap,
+				     desc->regs_offset +
+				     ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
+				     dscr->next);
+		}
+
+		action |= ATMEL_HLCDC_LAYER_A2Q;
+	}
+
+	/* Release unneeded descriptors */
+	for (i = fb_flip->ngems; i < layer->max_planes; i++) {
+		fb_flip->dscrs[i]->status = 0;
+		fb_flip->dscrs[i] = NULL;
+	}
+
+	dma->queue = fb_flip;
+	slot->fb_flip = NULL;
+
+apply:
+	if (action)
+		regmap_write(regmap,
+			     desc->regs_offset + ATMEL_HLCDC_LAYER_CHER,
+			     action);
+
+	atmel_hlcdc_layer_update_reset(layer, upd->pending);
+
+	upd->pending = -1;
+}
+
+void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
+	struct regmap *regmap = layer->hlcdc->regmap;
+	struct atmel_hlcdc_layer_fb_flip *flip;
+	unsigned long flags;
+	unsigned int isr, imr;
+	unsigned int status;
+	unsigned int plane_status;
+	u32 flip_status;
+
+	int i;
+
+	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IMR, &imr);
+	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
+	status = imr & isr;
+	if (!status)
+		return;
+
+	spin_lock_irqsave(&layer->lock, flags);
+
+	flip = dma->queue ? dma->queue : dma->cur;
+
+	if (!flip) {
+		spin_unlock_irqrestore(&layer->lock, flags);
+		return;
+	}
+
+	flip_status = 0;
+	for (i = 0; i < flip->ngems; i++) {
+		plane_status = (status >> (8 * i));
+
+		if (plane_status &
+		    (ATMEL_HLCDC_LAYER_ADD_IRQ |
+		     ATMEL_HLCDC_LAYER_DSCR_IRQ) &
+		    ~flip->dscrs[i]->ctrl) {
+			flip->dscrs[i]->status |=
+					ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED;
+			flip->dscrs[i]->ctrl |=
+					ATMEL_HLCDC_LAYER_ADD_IRQ |
+					ATMEL_HLCDC_LAYER_DSCR_IRQ;
+		}
+
+		if (plane_status &
+		    ATMEL_HLCDC_LAYER_DONE_IRQ &
+		    ~flip->dscrs[i]->ctrl) {
+			flip->dscrs[i]->status |=
+					ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE;
+			flip->dscrs[i]->ctrl |=
+					ATMEL_HLCDC_LAYER_DONE_IRQ;
+		}
+
+		flip_status |= flip->dscrs[i]->status;
+	}
+
+	/* Get changed bits */
+	flip_status ^= flip->status;
+
+	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED) {
+		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
+		dma->cur = dma->queue;
+		dma->queue = NULL;
+	}
+
+	if (flip_status & ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE) {
+		atmel_hlcdc_layer_fb_flip_release_queue(layer, dma->cur);
+		dma->cur = NULL;
+	}
+
+	flip->status |= flip_status;
+
+	if (!dma->queue) {
+		atmel_hlcdc_layer_update_apply(layer);
+
+		if (!dma->cur)
+			dma->status = ATMEL_HLCDC_LAYER_DISABLED;
+	}
+
+	spin_unlock_irqrestore(&layer->lock, flags);
+}
+
+int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_fb_flip *flip;
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&layer->lock, flags);
+
+	/*
+	 * First disable DMA transfers. If a DMA transfer has been queued
+	 * we're stopping this one instead of the current one because we
+	 * can't know for sure if queued transfer has been started or not.
+	 */
+	flip = dma->queue ? dma->queue : dma->cur;
+	if (flip) {
+		for (i = 0; i < flip->ngems; i++)
+			flip->dscrs[i]->ctrl &= ~(ATMEL_HLCDC_LAYER_DFETCH |
+						  ATMEL_HLCDC_LAYER_DONE_IRQ);
+
+		dma->status = ATMEL_HLCDC_LAYER_DISABLING;
+	}
+
+	/*
+	 * Then discard the pending update request (if any) to prevent
+	 * DMA irq handler from restarting the DMA channel after it has
+	 * been disabled.
+	 */
+	if (upd->pending >= 0) {
+		atmel_hlcdc_layer_update_reset(layer, upd->pending);
+		upd->pending = -1;
+	}
+
+	spin_unlock_irqrestore(&layer->lock, flags);
+
+	return 0;
+}
+
+int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct regmap *regmap = layer->hlcdc->regmap;
+	struct atmel_hlcdc_layer_fb_flip *fb_flip;
+	struct atmel_hlcdc_layer_update_slot *slot;
+	unsigned long flags;
+	int i, j = 0;
+
+	fb_flip = kzalloc(sizeof(*fb_flip), GFP_KERNEL);
+	if (!fb_flip)
+		return -ENOMEM;
+
+	fb_flip->task = drm_flip_work_allocate_task(fb_flip, GFP_KERNEL);
+	if (!fb_flip->task) {
+		kfree(fb_flip);
+		return -ENOMEM;
+	}
+
+	spin_lock_irqsave(&layer->lock, flags);
+
+	upd->next = upd->pending ? 0 : 1;
+
+	slot = &upd->slots[upd->next];
+
+	for (i = 0; i < layer->max_planes * 4; i++) {
+		if (!dma->dscrs[i].status) {
+			fb_flip->dscrs[j++] = &dma->dscrs[i];
+			dma->dscrs[i].status =
+				ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED;
+			if (j == layer->max_planes)
+				break;
+		}
+	}
+
+	if (j < layer->max_planes) {
+		for (i = 0; i < j; i++)
+			fb_flip->dscrs[i]->status = 0;
+	}
+
+	if (j < layer->max_planes) {
+		spin_unlock_irqrestore(&layer->lock, flags);
+		atmel_hlcdc_layer_fb_flip_destroy(fb_flip);
+		return -EBUSY;
+	}
+
+	slot->fb_flip = fb_flip;
+
+	if (upd->pending >= 0) {
+		memcpy(slot->configs,
+		       upd->slots[upd->pending].configs,
+		       layer->desc->nconfigs * sizeof(u32));
+		memcpy(slot->updated_configs,
+		       upd->slots[upd->pending].updated_configs,
+		       DIV_ROUND_UP(layer->desc->nconfigs,
+				    BITS_PER_BYTE * sizeof(unsigned long)) *
+		       sizeof(unsigned long));
+		slot->fb_flip->fb = upd->slots[upd->pending].fb_flip->fb;
+		if (upd->slots[upd->pending].fb_flip->fb) {
+			slot->fb_flip->fb =
+				upd->slots[upd->pending].fb_flip->fb;
+			slot->fb_flip->ngems =
+				upd->slots[upd->pending].fb_flip->ngems;
+			drm_framebuffer_reference(slot->fb_flip->fb);
+		}
+	} else {
+		regmap_bulk_read(regmap,
+				 layer->desc->regs_offset +
+				 ATMEL_HLCDC_LAYER_CFG(layer, 0),
+				 upd->slots[upd->next].configs,
+				 layer->desc->nconfigs);
+	}
+
+	spin_unlock_irqrestore(&layer->lock, flags);
+
+	return 0;
+}
+
+void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+
+	atmel_hlcdc_layer_update_reset(layer, upd->next);
+	upd->next = -1;
+}
+
+void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
+				     struct drm_framebuffer *fb,
+				     unsigned int *offsets)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_fb_flip *fb_flip;
+	struct atmel_hlcdc_layer_update_slot *slot;
+	struct atmel_hlcdc_dma_channel_dscr *dscr;
+	struct drm_framebuffer *old_fb;
+	int nplanes = 0;
+	int i;
+
+	if (upd->next < 0 || upd->next > 1)
+		return;
+
+	if (fb)
+		nplanes = drm_format_num_planes(fb->pixel_format);
+
+	if (nplanes > layer->max_planes)
+		return;
+
+	slot = &upd->slots[upd->next];
+
+	fb_flip = slot->fb_flip;
+	old_fb = slot->fb_flip->fb;
+
+	for (i = 0; i < nplanes; i++) {
+		struct drm_gem_cma_object *gem;
+
+		dscr = slot->fb_flip->dscrs[i];
+		gem = drm_fb_cma_get_gem_obj(fb, i);
+		dscr->addr = gem->paddr + offsets[i];
+	}
+
+	fb_flip->ngems = nplanes;
+	fb_flip->fb = fb;
+
+	if (fb)
+		drm_framebuffer_reference(fb);
+
+	if (old_fb)
+		drm_framebuffer_unreference(old_fb);
+}
+
+void atmel_hlcdc_layer_update_set_finished(struct atmel_hlcdc_layer *layer,
+					   void (*finished)(void *data),
+					   void *finished_data)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_update_slot *slot;
+
+	if (upd->next < 0 || upd->next > 1)
+		return;
+
+	slot = &upd->slots[upd->next];
+
+	slot->fb_flip->finished = finished;
+	slot->fb_flip->finished_data = finished_data;
+}
+
+void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
+				  u32 mask, u32 val)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_update_slot *slot;
+
+	if (upd->next < 0 || upd->next > 1)
+		return;
+
+	if (cfg >= layer->desc->nconfigs)
+		return;
+
+	slot = &upd->slots[upd->next];
+	slot->configs[cfg] &= ~mask;
+	slot->configs[cfg] |= (val & mask);
+	set_bit(cfg, slot->updated_configs);
+}
+
+void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	struct atmel_hlcdc_layer_update_slot *slot;
+	unsigned long flags;
+
+	if (upd->next < 0  || upd->next > 1)
+		return;
+
+	slot = &upd->slots[upd->next];
+
+	spin_lock_irqsave(&layer->lock, flags);
+
+	/*
+	 * Release pending update request and replace it by the new one.
+	 */
+	if (upd->pending >= 0)
+		atmel_hlcdc_layer_update_reset(layer, upd->pending);
+
+	upd->pending = upd->next;
+	upd->next = -1;
+
+	if (!dma->queue)
+		atmel_hlcdc_layer_update_apply(layer);
+
+	spin_unlock_irqrestore(&layer->lock, flags);
+
+
+	upd->next = -1;
+}
+
+static int atmel_hlcdc_layer_dma_init(struct drm_device *dev,
+				      struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	dma_addr_t dma_addr;
+	int i;
+
+	dma->dscrs = dma_alloc_coherent(dev->dev,
+					layer->max_planes * 4 *
+					sizeof(*dma->dscrs),
+					&dma_addr, GFP_KERNEL);
+	if (!dma->dscrs)
+		return -ENOMEM;
+
+	for (i = 0; i < layer->max_planes * 4; i++) {
+		struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i];
+
+		dscr->next = dma_addr + (i * sizeof(*dscr));
+	}
+
+	return 0;
+}
+
+static void atmel_hlcdc_layer_dma_cleanup(struct drm_device *dev,
+					  struct atmel_hlcdc_layer *layer)
+{
+	struct atmel_hlcdc_layer_dma_channel *dma = &layer->dma;
+	int i;
+
+	for (i = 0; i < layer->max_planes * 4; i++) {
+		struct atmel_hlcdc_dma_channel_dscr *dscr = &dma->dscrs[i];
+
+		dscr->status = 0;
+	}
+
+	dma_free_coherent(dev->dev, layer->max_planes * 4 *
+			  sizeof(*dma->dscrs), dma->dscrs,
+			  dma->dscrs[0].next);
+}
+
+static int atmel_hlcdc_layer_update_init(struct drm_device *dev,
+				struct atmel_hlcdc_layer *layer,
+				const struct atmel_hlcdc_layer_desc *desc)
+{
+	struct atmel_hlcdc_layer_update *upd = &layer->update;
+	int updated_size;
+	void *buffer;
+	int i;
+
+	updated_size = DIV_ROUND_UP(desc->nconfigs,
+				    BITS_PER_BYTE *
+				    sizeof(unsigned long));
+
+	buffer = devm_kzalloc(dev->dev,
+			      ((desc->nconfigs * sizeof(u32)) +
+				(updated_size * sizeof(unsigned long))) * 2,
+			      GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	for (i = 0; i < 2; i++) {
+		upd->slots[i].updated_configs = buffer;
+		buffer += updated_size * sizeof(unsigned long);
+		upd->slots[i].configs = buffer;
+		buffer += desc->nconfigs * sizeof(u32);
+	}
+
+	upd->pending = -1;
+	upd->next = -1;
+
+	return 0;
+}
+
+int atmel_hlcdc_layer_init(struct drm_device *dev,
+			   struct atmel_hlcdc_layer *layer,
+			   const struct atmel_hlcdc_layer_desc *desc)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct regmap *regmap = dc->hlcdc->regmap;
+	unsigned int tmp;
+	int ret;
+	int i;
+
+	layer->hlcdc = dc->hlcdc;
+	layer->wq = dc->wq;
+	layer->desc = desc;
+
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
+		     ATMEL_HLCDC_LAYER_RST);
+	for (i = 0; i < desc->formats->nformats; i++) {
+		int nplanes = drm_format_num_planes(desc->formats->formats[i]);
+
+		if (nplanes > layer->max_planes)
+			layer->max_planes = nplanes;
+	}
+
+	spin_lock_init(&layer->lock);
+	drm_flip_work_init(&layer->gc, desc->name,
+			   atmel_hlcdc_layer_fb_flip_release);
+	ret = atmel_hlcdc_layer_dma_init(dev, layer);
+	if (ret)
+		return ret;
+
+	ret = atmel_hlcdc_layer_update_init(dev, layer, desc);
+	if (ret)
+		return ret;
+
+	/* Flush Status Register */
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR,
+		     0xffffffff);
+	regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR,
+		    &tmp);
+
+	tmp = 0;
+	for (i = 0; i < layer->max_planes; i++)
+		tmp |= (ATMEL_HLCDC_LAYER_DMA_IRQ |
+			ATMEL_HLCDC_LAYER_DSCR_IRQ |
+			ATMEL_HLCDC_LAYER_ADD_IRQ |
+			ATMEL_HLCDC_LAYER_DONE_IRQ |
+			ATMEL_HLCDC_LAYER_OVR_IRQ) << (8 * i);
+
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IER, tmp);
+
+	return 0;
+}
+
+void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
+			       struct atmel_hlcdc_layer *layer)
+{
+	const struct atmel_hlcdc_layer_desc *desc = layer->desc;
+	struct regmap *regmap = layer->hlcdc->regmap;
+
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_IDR,
+		     0xffffffff);
+	regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
+		     ATMEL_HLCDC_LAYER_RST);
+
+	atmel_hlcdc_layer_dma_cleanup(dev, layer);
+	drm_flip_work_cleanup(&layer->gc);
+}
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
new file mode 100644
index 0000000..885b57a
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DRM_ATMEL_HLCDC_LAYER_H
+#define DRM_ATMEL_HLCDC_LAYER_H
+
+#include <linux/mfd/atmel-hlcdc.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_flip_work.h>
+#include <drm/drmP.h>
+
+#define ATMEL_HLCDC_LAYER_CHER			0x0
+#define ATMEL_HLCDC_LAYER_CHDR			0x4
+#define ATMEL_HLCDC_LAYER_CHSR			0x8
+#define ATMEL_HLCDC_LAYER_DMA_CHAN		BIT(0)
+#define ATMEL_HLCDC_LAYER_UPDATE		BIT(1)
+#define ATMEL_HLCDC_LAYER_A2Q			BIT(2)
+#define ATMEL_HLCDC_LAYER_RST			BIT(8)
+
+#define ATMEL_HLCDC_LAYER_IER			0xc
+#define ATMEL_HLCDC_LAYER_IDR			0x10
+#define ATMEL_HLCDC_LAYER_IMR			0x14
+#define ATMEL_HLCDC_LAYER_ISR			0x18
+#define ATMEL_HLCDC_LAYER_DFETCH		BIT(0)
+#define ATMEL_HLCDC_LAYER_LFETCH		BIT(1)
+#define ATMEL_HLCDC_LAYER_DMA_IRQ		BIT(2)
+#define ATMEL_HLCDC_LAYER_DSCR_IRQ		BIT(3)
+#define ATMEL_HLCDC_LAYER_ADD_IRQ		BIT(4)
+#define ATMEL_HLCDC_LAYER_DONE_IRQ		BIT(5)
+#define ATMEL_HLCDC_LAYER_OVR_IRQ		BIT(6)
+
+#define ATMEL_HLCDC_LAYER_PLANE_HEAD(n)		(((n) * 0x10) + 0x1c)
+#define ATMEL_HLCDC_LAYER_PLANE_ADDR(n)		(((n) * 0x10) + 0x20)
+#define ATMEL_HLCDC_LAYER_PLANE_CTRL(n)		(((n) * 0x10) + 0x24)
+#define ATMEL_HLCDC_LAYER_PLANE_NEXT(n)		(((n) * 0x10) + 0x28)
+#define ATMEL_HLCDC_LAYER_CFG(p, c)		(((c) * 4) + ((p)->max_planes * 0x10) + 0x1c)
+
+#define ATMEL_HLCDC_LAYER_DMA_CFG_ID		0
+#define ATMEL_HLCDC_LAYER_DMA_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_DMA_CFG_ID)
+
+#define ATMEL_HLCDC_LAYER_FORMAT_CFG_ID		1
+#define ATMEL_HLCDC_LAYER_FORMAT_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, ATMEL_HLCDC_LAYER_FORMAT_CFG_ID)
+#define ATMEL_HLCDC_LAYER_RGB			(0 << 0)
+#define ATMEL_HLCDC_LAYER_CLUT			(1 << 0)
+#define ATMEL_HLCDC_LAYER_YUV			(2 << 0)
+#define ATMEL_HLCDC_RGB_MODE(m)			(((m) & 0xf) << 4)
+#define ATMEL_HLCDC_CLUT_MODE(m)		(((m) & 0x3) << 8)
+#define ATMEL_HLCDC_YUV_MODE(m)			(((m) & 0xf) << 12)
+#define ATMEL_HLCDC_YUV422ROT			(1 << 16)
+#define ATMEL_HLCDC_YUV422SWP			(1 << 17)
+#define ATMEL_HLCDC_DSCALEOPT			(1 << 20)
+
+#define ATMEL_HLCDC_XRGB4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(0))
+#define ATMEL_HLCDC_ARGB4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(1))
+#define ATMEL_HLCDC_RGBA4444_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(2))
+#define ATMEL_HLCDC_RGB565_MODE			(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(3))
+#define ATMEL_HLCDC_ARGB1555_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(4))
+#define ATMEL_HLCDC_XRGB8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(9))
+#define ATMEL_HLCDC_RGB888_MODE			(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(10))
+#define ATMEL_HLCDC_ARGB8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(12))
+#define ATMEL_HLCDC_RGBA8888_MODE		(ATMEL_HLCDC_LAYER_RGB | ATMEL_HLCDC_RGB_MODE(13))
+
+#define ATMEL_HLCDC_AYUV_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(0))
+#define ATMEL_HLCDC_YUYV_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(1))
+#define ATMEL_HLCDC_UYVY_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(2))
+#define ATMEL_HLCDC_YVYU_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(3))
+#define ATMEL_HLCDC_VYUY_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(4))
+#define ATMEL_HLCDC_NV61_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(5))
+#define ATMEL_HLCDC_YUV422_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(6))
+#define ATMEL_HLCDC_NV21_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(7))
+#define ATMEL_HLCDC_YUV420_MODE			(ATMEL_HLCDC_LAYER_YUV | ATMEL_HLCDC_YUV_MODE(8))
+
+#define ATMEL_HLCDC_LAYER_POS_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pos)
+#define ATMEL_HLCDC_LAYER_SIZE_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.size)
+#define ATMEL_HLCDC_LAYER_MEMSIZE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.memsize)
+#define ATMEL_HLCDC_LAYER_XSTRIDE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.xstride)
+#define ATMEL_HLCDC_LAYER_PSTRIDE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.pstride)
+#define ATMEL_HLCDC_LAYER_DFLTCOLOR_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.default_color)
+#define ATMEL_HLCDC_LAYER_CRKEY_CFG(p)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key)
+#define ATMEL_HLCDC_LAYER_CRKEY_MASK_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.chroma_key_mask)
+
+#define ATMEL_HLCDC_LAYER_GENERAL_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.general_config)
+#define ATMEL_HLCDC_LAYER_CRKEY			BIT(0)
+#define ATMEL_HLCDC_LAYER_INV			BIT(1)
+#define ATMEL_HLCDC_LAYER_ITER2BL		BIT(2)
+#define ATMEL_HLCDC_LAYER_ITER			BIT(3)
+#define ATMEL_HLCDC_LAYER_REVALPHA		BIT(4)
+#define ATMEL_HLCDC_LAYER_GAEN			BIT(5)
+#define ATMEL_HLCDC_LAYER_LAEN			BIT(6)
+#define ATMEL_HLCDC_LAYER_OVR			BIT(7)
+#define ATMEL_HLCDC_LAYER_DMA			BIT(8)
+#define ATMEL_HLCDC_LAYER_REP			BIT(9)
+#define ATMEL_HLCDC_LAYER_DSTKEY		BIT(10)
+#define ATMEL_HLCDC_LAYER_GA_MASK		GENMASK(23, 16)
+#define ATMEL_HLCDC_LAYER_GA_SHIFT		16
+
+#define ATMEL_HLCDC_LAYER_CSC_CFG(p, o)		ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.csc + o)
+
+#define ATMEL_HLCDC_LAYER_DISC_POS_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_pos)
+
+#define ATMEL_HLCDC_LAYER_DISC_SIZE_CFG(p)	ATMEL_HLCDC_LAYER_CFG(p, (p)->desc->layout.disc_size)
+
+#define ATMEL_HLCDC_MAX_PLANES			3
+
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_RESERVED	BIT(0)
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_LOADED	BIT(1)
+#define ATMEL_HLCDC_DMA_CHANNEL_DSCR_DONE	BIT(2)
+
+/**
+ * Atmel HLCDC Layer registers layout structure
+ *
+ * Each HLCDC layer has its own register organization and a given register
+ * by be placed differently on 2 different layers depending on its
+ * capabilities.
+ * This structure stores common registers layout for a given layer and is
+ * used by HLCDC layer code to chose the appropriate register to write to
+ * or to read from.
+ *
+ * For all fields, a value of zero means "unsupported".
+ *
+ * See Atmel's datasheet for a detailled description of these registers.
+ *
+ * @xstride: xstride registers
+ * @pstride: pstride registers
+ * @pos: position register
+ * @size: displayed size register
+ * @memsize: memory size register
+ * @default_color: default color register
+ * @chroma_key: chroma key register
+ * @chroma_key_mask: chroma key mask register
+ * @general_config: general layer config register
+ * @disc_pos: discard area position register
+ * @disc_size: discard area size register
+ * @csc: color space conversion register
+ */
+struct atmel_hlcdc_layer_cfg_layout {
+	int xstride[ATMEL_HLCDC_MAX_PLANES];
+	int pstride[ATMEL_HLCDC_MAX_PLANES];
+	int pos;
+	int size;
+	int memsize;
+	int default_color;
+	int chroma_key;
+	int chroma_key_mask;
+	int general_config;
+	int disc_pos;
+	int disc_size;
+	int csc;
+};
+
+/**
+ * Atmel HLCDC framebuffer flip structure
+ *
+ * This structure is allocated when someone asked for a layer update (most
+ * likely a DRM plane update, either primary, overlay or cursor plane) and
+ * released when the layer do not need to reference the framebuffer object
+ * anymore (i.e. the layer was disabled or updated).
+ *
+ * @fb: the referenced framebuffer object.
+ * @refcnt: the number of GEM object still referenced by the layer.
+ *	    When no more objects are referenced the fb flip structure is
+ *	    added to the garbage collector.
+ * @ngems: number of GEM objects referenced by the fb element.
+ * @finished: finished callback, called when the layer framebuffer flip is
+ *	      finished.
+ * @finished_data: data passed to the finished callback.
+ */
+struct atmel_hlcdc_layer_fb_flip {
+	struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_MAX_PLANES];
+	struct drm_flip_task *task;
+	struct drm_framebuffer *fb;
+	int ngems;
+	u32 status;
+	void (*finished)(void *data);
+	void *finished_data;
+};
+
+/**
+ * Atmel HLCDC DMA descriptor structure
+ *
+ * This structure is used by the HLCDC DMA engine to schedule a DMA transfer.
+ *
+ * The structure fields must remain in this specific order, because they're
+ * used by the HLCDC DMA engine, which expect them in this order.
+ *
+ * @addr: buffer DMA address
+ * @ctrl: DMA transfer options
+ * @next: next DMA descriptor to fetch
+ * @gem_flip: the attached gem_flip operation
+ */
+struct atmel_hlcdc_dma_channel_dscr {
+	dma_addr_t addr;
+	u32 ctrl;
+	dma_addr_t next;
+	u32 status;
+} __aligned(sizeof(u64));
+
+/**
+ * Atmel HLCDC layer types
+ */
+enum atmel_hlcdc_layer_type {
+	ATMEL_HLCDC_BASE_LAYER,
+	ATMEL_HLCDC_OVERLAY_LAYER,
+	ATMEL_HLCDC_CURSOR_LAYER,
+	ATMEL_HLCDC_PP_LAYER,
+};
+
+/**
+ * Atmel HLCDC Supported formats structure
+ *
+ * This structure list all the formats supported by a given layer.
+ *
+ * @nformats: number of supported formats
+ * @formats: supported formats
+ */
+struct atmel_hlcdc_formats {
+	int nformats;
+	uint32_t *formats;
+};
+
+/**
+ * Atmel HLCDC Layer description structure
+ *
+ * This structure describe the capabilities provided by a given layer.
+ *
+ * @name: layer name
+ * @type: layer type
+ * @id: layer id
+ * @regs_offset: offset of the layer registers from the HLCDC registers base
+ * @nconfigs: number of config registers provided by this layer
+ * @layout: config registers layout
+ * @max_width: maximum width supported by this layer (0 means unlimited)
+ * @max_height: maximum height supported by this layer (0 means unlimited)
+ */
+struct atmel_hlcdc_layer_desc {
+	const char *name;
+	enum atmel_hlcdc_layer_type type;
+	int id;
+	int regs_offset;
+	int nconfigs;
+	struct atmel_hlcdc_formats *formats;
+	struct atmel_hlcdc_layer_cfg_layout layout;
+	int max_width;
+	int max_height;
+};
+
+/**
+ * Atmel HLCDC Layer Update Slot structure
+ *
+ * This structure stores layer update requests to be applied on next frame.
+ * This is the base structure behind the atomic layer update infrastructure.
+ *
+ * Atomic layer update provides a way to update all layer's parameters
+ * simultaneously. This is needed to avoid incompatible sequential updates
+ * like this one:
+ * 1) update layer format from RGB888 (1 plane/buffer) to YUV422
+ *    (2 planes/buffers)
+ * 2) the format update is applied but the DMA channel for the second
+ *    plane/buffer is not enabled
+ * 3) enable the DMA channel for the second plane
+ *
+ * @dscrs: DMA channel descriptors
+ * @fb_flip: fb_flip object
+ * @updated_configs: bitmask used to record modified configs
+ * @configs: new config values
+ */
+struct atmel_hlcdc_layer_update_slot {
+	struct atmel_hlcdc_layer_fb_flip *fb_flip;
+	unsigned long *updated_configs;
+	u32 *configs;
+};
+
+/**
+ * Atmel HLCDC Layer Update structure
+ *
+ * This structure provides a way to queue layer update requests.
+ *
+ * At a given time there is at most:
+ *  - one pending update request, which means the update request has been
+ *    commited (or validated) and is waiting for the DMA channel(s) to be
+ *    available
+ *  - one request being prepared, which means someone started a layer update
+ *    but has not commited it yet. There cannot be more than one started
+ *    request, because the update lock is taken when starting a layer update
+ *    and release when commiting or rolling back the request.
+ *
+ * @slots: update slots. One is used for pending request and the other one
+ *	   for started update request
+ * @pending: the pending slot index or -1 if no request is pending
+ * @next: the started update slot index or -1 no update has been started
+ */
+struct atmel_hlcdc_layer_update {
+	struct atmel_hlcdc_layer_update_slot slots[2];
+	int pending;
+	int next;
+};
+
+enum atmel_hlcdc_layer_dma_channel_status {
+	ATMEL_HLCDC_LAYER_DISABLED,
+	ATMEL_HLCDC_LAYER_ENABLED,
+	ATMEL_HLCDC_LAYER_DISABLING,
+};
+
+/**
+ * Atmel HLCDC Layer DMA channel structure
+ *
+ * This structure stores informations on the DMA channel associated to a
+ * given layer.
+ *
+ * @status: DMA channel status
+ * @cur: current framebuffer
+ * @queue: next framebuffer
+ * @dscrs: allocated DMA descriptors
+ */
+struct atmel_hlcdc_layer_dma_channel {
+	enum atmel_hlcdc_layer_dma_channel_status status;
+	struct atmel_hlcdc_layer_fb_flip *cur;
+	struct atmel_hlcdc_layer_fb_flip *queue;
+	struct atmel_hlcdc_dma_channel_dscr *dscrs;
+};
+
+/**
+ * Atmel HLCDC Layer structure
+ *
+ * This structure stores information on the layer instance.
+ *
+ * @desc: layer description
+ * @max_planes: maximum planes/buffers that can be associated with this layer.
+ *	       This depends on the supported formats.
+ * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
+ * @dma: dma channel
+ * @gc: fb flip garbage collector
+ * @update: update handler
+ * @lock: layer lock
+ */
+struct atmel_hlcdc_layer {
+	const struct atmel_hlcdc_layer_desc *desc;
+	int max_planes;
+	struct atmel_hlcdc *hlcdc;
+	struct workqueue_struct *wq;
+	struct drm_flip_work gc;
+	struct atmel_hlcdc_layer_dma_channel dma;
+	struct atmel_hlcdc_layer_update update;
+	spinlock_t lock;
+};
+
+void atmel_hlcdc_layer_irq(struct atmel_hlcdc_layer *layer);
+
+int atmel_hlcdc_layer_init(struct drm_device *dev,
+			   struct atmel_hlcdc_layer *layer,
+			   const struct atmel_hlcdc_layer_desc *desc);
+
+void atmel_hlcdc_layer_cleanup(struct drm_device *dev,
+			       struct atmel_hlcdc_layer *layer);
+
+int atmel_hlcdc_layer_disable(struct atmel_hlcdc_layer *layer);
+
+void atmel_hlcdc_layer_set_finished(struct atmel_hlcdc_layer *layer,
+				    void (*finished)(void *data),
+				    void *data);
+
+int atmel_hlcdc_layer_update_start(struct atmel_hlcdc_layer *layer);
+
+void atmel_hlcdc_layer_update_cfg(struct atmel_hlcdc_layer *layer, int cfg,
+				  u32 mask, u32 val);
+
+void atmel_hlcdc_layer_update_set_fb(struct atmel_hlcdc_layer *layer,
+				     struct drm_framebuffer *fb,
+				     unsigned int *offsets);
+
+void atmel_hlcdc_layer_update_set_finished(struct atmel_hlcdc_layer *layer,
+					   void (*finished)(void *data),
+					   void *finished_data);
+
+void atmel_hlcdc_layer_update_rollback(struct atmel_hlcdc_layer *layer);
+
+void atmel_hlcdc_layer_update_commit(struct atmel_hlcdc_layer *layer);
+
+#endif /* DRM_ATMEL_HLCDC_LAYER_H */
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
new file mode 100644
index 0000000..de95eb7
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2014 Traphandler
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_panel.h>
+
+#include "atmel_hlcdc_dc.h"
+
+/**
+ * Atmel HLCDC RGB output mode
+ */
+enum atmel_hlcdc_connector_rgb_mode {
+	ATMEL_HLCDC_CONNECTOR_RGB444,
+	ATMEL_HLCDC_CONNECTOR_RGB565,
+	ATMEL_HLCDC_CONNECTOR_RGB666,
+	ATMEL_HLCDC_CONNECTOR_RGB888,
+};
+
+struct atmel_hlcdc_slave;
+
+/**
+ * Atmel HLCDC Slave device operations structure
+ *
+ * This structure defines an abstraction to be implemented by each slave
+ * device type (panel, convertors, ...).
+ *
+ * @enable: Enable the slave device
+ * @disable: Disable the slave device
+ * @get_modes: retrieve modes supported by the slave device
+ * @destroy: detroy the slave device and all associated data
+ */
+struct atmel_hlcdc_slave_ops {
+	int (*enable)(struct atmel_hlcdc_slave *slave);
+	int (*disable)(struct atmel_hlcdc_slave *slave);
+	int (*get_modes)(struct atmel_hlcdc_slave *slave);
+	void (*destroy)(struct atmel_hlcdc_slave *slave);
+};
+
+/**
+ * Atmel HLCDC Slave device structure
+ *
+ * This structure is the base slave device structure to be overloaded by
+ * each slave device implementation.
+ *
+ * @ops: slave device operations
+ */
+struct atmel_hlcdc_slave {
+	const struct atmel_hlcdc_slave_ops *ops;
+};
+
+/**
+ * Atmel HLCDC Panel device structure
+ *
+ * This structure is specialization of the slave device structure to
+ * interface with drm panels.
+ *
+ * @slave: base slave device fields
+ * @panel: drm panel attached to this slave device
+ */
+struct atmel_hlcdc_panel {
+	struct atmel_hlcdc_slave slave;
+	struct drm_panel *panel;
+};
+
+static inline struct atmel_hlcdc_panel *
+atmel_hlcdc_slave_to_panel(struct atmel_hlcdc_slave *slave)
+{
+	return container_of(slave, struct atmel_hlcdc_panel, slave);
+}
+
+/**
+ * Atmel HLCDC RGB connector structure
+ *
+ * This structure stores informations about an DRM panel connected through
+ * the RGB connector.
+ *
+ * @connector: DRM connector
+ * @encoder: DRM encoder
+ * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
+ * @slave: slave device connected to this output
+ * @endpoint: DT endpoint representing this output
+ * @dpms: current DPMS mode
+ */
+struct atmel_hlcdc_rgb_output {
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct atmel_hlcdc *hlcdc;
+	struct atmel_hlcdc_slave *slave;
+	struct of_endpoint endpoint;
+	int dpms;
+};
+
+static inline struct atmel_hlcdc_rgb_output *
+drm_connector_to_atmel_hlcdc_rgb_output(struct drm_connector *connector)
+{
+	return container_of(connector, struct atmel_hlcdc_rgb_output,
+			    connector);
+}
+
+static inline struct atmel_hlcdc_rgb_output *
+drm_encoder_to_atmel_hlcdc_rgb_output(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder);
+}
+
+static int atmel_hlcdc_panel_enable(struct atmel_hlcdc_slave *slave)
+{
+	struct atmel_hlcdc_panel *panel = atmel_hlcdc_slave_to_panel(slave);
+
+	return drm_panel_enable(panel->panel);
+}
+
+static int atmel_hlcdc_panel_disable(struct atmel_hlcdc_slave *slave)
+{
+	struct atmel_hlcdc_panel *panel = atmel_hlcdc_slave_to_panel(slave);
+
+	return drm_panel_disable(panel->panel);
+}
+
+static int atmel_hlcdc_panel_get_modes(struct atmel_hlcdc_slave *slave)
+{
+	struct atmel_hlcdc_panel *panel = atmel_hlcdc_slave_to_panel(slave);
+
+	return panel->panel->funcs->get_modes(panel->panel);
+}
+
+static void atmel_hlcdc_panel_destroy(struct atmel_hlcdc_slave *slave)
+{
+	struct atmel_hlcdc_panel *panel = atmel_hlcdc_slave_to_panel(slave);
+
+	drm_panel_detach(panel->panel);
+	kfree(panel);
+}
+
+static const struct atmel_hlcdc_slave_ops atmel_hlcdc_panel_ops = {
+	.enable = atmel_hlcdc_panel_enable,
+	.disable = atmel_hlcdc_panel_disable,
+	.get_modes = atmel_hlcdc_panel_get_modes,
+	.destroy = atmel_hlcdc_panel_destroy,
+};
+
+static struct atmel_hlcdc_slave *
+atmel_hlcdc_panel_detect(struct atmel_hlcdc_rgb_output *rgb)
+{
+	struct device_node *np;
+	struct drm_panel *p = NULL;
+	struct atmel_hlcdc_panel *panel;
+
+	np = of_graph_get_remote_port_parent(rgb->endpoint.local_node);
+	if (!np)
+		return NULL;
+
+	p = of_drm_find_panel(np);
+	of_node_put(np);
+
+	if (p) {
+		panel = kzalloc(sizeof(*panel), GFP_KERNEL);
+		if (!panel)
+			return NULL;
+
+		drm_panel_attach(p, &rgb->connector);
+		panel->panel = p;
+		panel->slave.ops = &atmel_hlcdc_panel_ops;
+		return &panel->slave;
+	}
+
+	return NULL;
+}
+
+static void atmel_hlcdc_rgb_encoder_dpms(struct drm_encoder *encoder,
+					 int mode)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
+	struct regmap *regmap = rgb->hlcdc->regmap;
+	unsigned int status;
+
+	if (mode != DRM_MODE_DPMS_ON)
+		mode = DRM_MODE_DPMS_OFF;
+
+	if (mode == rgb->dpms)
+		return;
+
+	if (mode != DRM_MODE_DPMS_ON) {
+		regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       (status & ATMEL_HLCDC_DISP))
+			cpu_relax();
+
+		regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       (status & ATMEL_HLCDC_SYNC))
+			cpu_relax();
+
+		regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       (status & ATMEL_HLCDC_PIXEL_CLK))
+			cpu_relax();
+
+		clk_disable_unprepare(rgb->hlcdc->sys_clk);
+
+		rgb->slave->ops->disable(rgb->slave);
+	} else {
+		rgb->slave->ops->enable(rgb->slave);
+
+		clk_prepare_enable(rgb->hlcdc->sys_clk);
+
+		regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       !(status & ATMEL_HLCDC_PIXEL_CLK))
+			cpu_relax();
+
+
+		regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       !(status & ATMEL_HLCDC_SYNC))
+			cpu_relax();
+
+		regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP);
+		while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
+		       !(status & ATMEL_HLCDC_DISP))
+			cpu_relax();
+	}
+
+	rgb->dpms = mode;
+}
+
+static bool
+atmel_hlcdc_rgb_encoder_mode_fixup(struct drm_encoder *encoder,
+				   const struct drm_display_mode *mode,
+				   struct drm_display_mode *adjusted)
+{
+	return true;
+}
+
+static void atmel_hlcdc_rgb_encoder_prepare(struct drm_encoder *encoder)
+{
+	atmel_hlcdc_rgb_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+}
+
+static void atmel_hlcdc_rgb_encoder_commit(struct drm_encoder *encoder)
+{
+	atmel_hlcdc_rgb_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+}
+
+static void
+atmel_hlcdc_rgb_encoder_mode_set(struct drm_encoder *encoder,
+				 struct drm_display_mode *mode,
+				 struct drm_display_mode *adjusted)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
+	struct drm_display_info *info = &rgb->connector.display_info;
+	unsigned long prate = clk_get_rate(rgb->hlcdc->sys_clk);
+	unsigned long mode_rate = mode->clock * 1000;
+	u32 cfg = 0;
+	int div;
+
+	if ((prate / 2) < mode_rate) {
+		prate *= 2;
+		cfg |= ATMEL_HLCDC_CLKSEL;
+	}
+
+	div = DIV_ROUND_UP(prate, mode_rate);
+	if (div < 2)
+		div = 2;
+
+	cfg |= ATMEL_HLCDC_CLKDIV(div);
+
+	if (mode->flags & DRM_MODE_FLAG_NCSYNC)
+		cfg |= ATMEL_HLCDC_CLKPOL;
+
+	regmap_update_bits(rgb->hlcdc->regmap, ATMEL_HLCDC_CFG(0),
+			   ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK, cfg);
+
+	cfg = 0;
+
+	if (info->nbus_formats) {
+		switch (info->bus_formats[0]) {
+		case VIDEO_BUS_FMT_RGB565_1X16:
+			cfg |= ATMEL_HLCDC_CONNECTOR_RGB565 << 8;
+			break;
+		case VIDEO_BUS_FMT_RGB666_1X18:
+			cfg |= ATMEL_HLCDC_CONNECTOR_RGB666 << 8;
+			break;
+		case VIDEO_BUS_FMT_RGB888_1X24:
+			cfg |= ATMEL_HLCDC_CONNECTOR_RGB888 << 8;
+			break;
+		case VIDEO_BUS_FMT_RGB444_1X12:
+		default:
+			break;
+		}
+	}
+
+	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+		cfg |= ATMEL_HLCDC_VSPOL;
+
+	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+		cfg |= ATMEL_HLCDC_HSPOL;
+
+	regmap_update_bits(rgb->hlcdc->regmap, ATMEL_HLCDC_CFG(5),
+			   ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL |
+			   ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE |
+			   ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY |
+			   ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO |
+			   ATMEL_HLCDC_MODE_MASK | ATMEL_HLCDC_GUARDTIME_MASK,
+			   cfg);
+}
+
+static struct drm_encoder_helper_funcs atmel_hlcdc_rgb_encoder_helper_funcs = {
+	.dpms = atmel_hlcdc_rgb_encoder_dpms,
+	.mode_fixup = atmel_hlcdc_rgb_encoder_mode_fixup,
+	.prepare = atmel_hlcdc_rgb_encoder_prepare,
+	.commit = atmel_hlcdc_rgb_encoder_commit,
+	.mode_set = atmel_hlcdc_rgb_encoder_mode_set,
+};
+
+static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+	memset(encoder, 0, sizeof(*encoder));
+}
+
+static const struct drm_encoder_funcs atmel_hlcdc_rgb_encoder_funcs = {
+	.destroy = atmel_hlcdc_rgb_encoder_destroy,
+};
+
+static int atmel_hlcdc_rgb_get_modes(struct drm_connector *connector)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_connector_to_atmel_hlcdc_rgb_output(connector);
+
+	return rgb->slave->ops->get_modes(rgb->slave);
+}
+
+static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector,
+				      struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+atmel_hlcdc_rgb_best_encoder(struct drm_connector *connector)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_connector_to_atmel_hlcdc_rgb_output(connector);
+
+	return &rgb->encoder;
+}
+
+static struct drm_connector_helper_funcs atmel_hlcdc_rgb_connector_helper_funcs = {
+	.get_modes = atmel_hlcdc_rgb_get_modes,
+	.mode_valid = atmel_hlcdc_rgb_mode_valid,
+	.best_encoder = atmel_hlcdc_rgb_best_encoder,
+};
+
+static enum drm_connector_status
+atmel_hlcdc_rgb_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_connector_to_atmel_hlcdc_rgb_output(connector);
+
+	if (!rgb->slave) {
+		/* At the moment we only support panel devices */
+		rgb->slave = atmel_hlcdc_panel_detect(rgb);
+	}
+
+	if (rgb->slave)
+		return connector_status_connected;
+
+	return connector_status_disconnected;
+}
+
+static void
+atmel_hlcdc_rgb_connector_destroy(struct drm_connector *connector)
+{
+	struct atmel_hlcdc_rgb_output *rgb =
+			drm_connector_to_atmel_hlcdc_rgb_output(connector);
+
+	if (rgb->slave && rgb->slave->ops->destroy)
+		rgb->slave->ops->destroy(rgb->slave);
+
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs atmel_hlcdc_rgb_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = atmel_hlcdc_rgb_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = atmel_hlcdc_rgb_connector_destroy,
+};
+
+static int atmel_hlcdc_create_output(struct drm_device *dev,
+				     struct of_endpoint *ep)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct atmel_hlcdc_rgb_output *rgb;
+
+	rgb = devm_kzalloc(dev->dev, sizeof(*rgb), GFP_KERNEL);
+	if (!rgb)
+		return -ENOMEM;
+
+	rgb->endpoint = *ep;
+
+	rgb->dpms = DRM_MODE_DPMS_OFF;
+
+	rgb->hlcdc = dc->hlcdc;
+
+	drm_connector_init(dev, &rgb->connector,
+			   &atmel_hlcdc_rgb_connector_funcs,
+			   DRM_MODE_CONNECTOR_LVDS);
+	drm_connector_helper_add(&rgb->connector,
+				 &atmel_hlcdc_rgb_connector_helper_funcs);
+	rgb->connector.dpms = DRM_MODE_DPMS_OFF;
+	rgb->connector.polled = DRM_CONNECTOR_POLL_CONNECT;
+
+	drm_encoder_init(dev, &rgb->encoder, &atmel_hlcdc_rgb_encoder_funcs,
+			 DRM_MODE_ENCODER_LVDS);
+	drm_encoder_helper_add(&rgb->encoder,
+			       &atmel_hlcdc_rgb_encoder_helper_funcs);
+
+	drm_mode_connector_attach_encoder(&rgb->connector, &rgb->encoder);
+	drm_sysfs_connector_add(&rgb->connector);
+
+	rgb->encoder.possible_crtcs = 0x1;
+
+	return 0;
+}
+
+int atmel_hlcdc_create_outputs(struct drm_device *dev)
+{
+	struct device_node *port_np, *np;
+	struct of_endpoint ep;
+	int ret;
+
+	port_np = of_get_child_by_name(dev->dev->of_node, "port");
+	if (!port_np)
+		return -EINVAL;
+
+	np = of_get_child_by_name(port_np, "endpoint");
+	of_node_put(port_np);
+
+	if (!np)
+		return -EINVAL;
+
+	ret = of_graph_parse_endpoint(np, &ep);
+	of_node_put(port_np);
+
+	if (ret)
+		return ret;
+
+	ret = atmel_hlcdc_create_output(dev, &ep);
+	if (ret)
+		return ret;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
new file mode 100644
index 0000000..d42cd73
--- /dev/null
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "atmel_hlcdc_dc.h"
+
+#define SUBPIXEL_MASK			0xffff
+
+static uint32_t rgb_formats[] = {
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_RGBA4444,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGBA8888,
+};
+
+struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {
+	.formats = rgb_formats,
+	.nformats = ARRAY_SIZE(rgb_formats),
+};
+
+static uint32_t rgb_and_yuv_formats[] = {
+	DRM_FORMAT_XRGB4444,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_RGBA4444,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_RGBA8888,
+	DRM_FORMAT_AYUV,
+	DRM_FORMAT_YUYV,
+	DRM_FORMAT_UYVY,
+	DRM_FORMAT_YVYU,
+	DRM_FORMAT_VYUY,
+	DRM_FORMAT_NV21,
+	DRM_FORMAT_NV61,
+	DRM_FORMAT_YUV422,
+	DRM_FORMAT_YUV420,
+};
+
+struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {
+	.formats = rgb_and_yuv_formats,
+	.nformats = ARRAY_SIZE(rgb_and_yuv_formats),
+};
+
+static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
+{
+	switch (format) {
+	case DRM_FORMAT_XRGB4444:
+		*mode = ATMEL_HLCDC_XRGB4444_MODE;
+		break;
+	case DRM_FORMAT_ARGB4444:
+		*mode = ATMEL_HLCDC_ARGB4444_MODE;
+		break;
+	case DRM_FORMAT_RGBA4444:
+		*mode = ATMEL_HLCDC_RGBA4444_MODE;
+		break;
+	case DRM_FORMAT_RGB565:
+		*mode = ATMEL_HLCDC_RGB565_MODE;
+		break;
+	case DRM_FORMAT_RGB888:
+		*mode = ATMEL_HLCDC_RGB888_MODE;
+		break;
+	case DRM_FORMAT_ARGB1555:
+		*mode = ATMEL_HLCDC_ARGB1555_MODE;
+		break;
+	case DRM_FORMAT_XRGB8888:
+		*mode = ATMEL_HLCDC_XRGB8888_MODE;
+		break;
+	case DRM_FORMAT_ARGB8888:
+		*mode = ATMEL_HLCDC_ARGB8888_MODE;
+		break;
+	case DRM_FORMAT_RGBA8888:
+		*mode = ATMEL_HLCDC_RGBA8888_MODE;
+		break;
+	case DRM_FORMAT_AYUV:
+		*mode = ATMEL_HLCDC_AYUV_MODE;
+		break;
+	case DRM_FORMAT_YUYV:
+		*mode = ATMEL_HLCDC_YUYV_MODE;
+		break;
+	case DRM_FORMAT_UYVY:
+		*mode = ATMEL_HLCDC_UYVY_MODE;
+		break;
+	case DRM_FORMAT_YVYU:
+		*mode = ATMEL_HLCDC_YVYU_MODE;
+		break;
+	case DRM_FORMAT_VYUY:
+		*mode = ATMEL_HLCDC_VYUY_MODE;
+		break;
+	case DRM_FORMAT_NV21:
+		*mode = ATMEL_HLCDC_NV21_MODE;
+		break;
+	case DRM_FORMAT_NV61:
+		*mode = ATMEL_HLCDC_NV61_MODE;
+		break;
+	case DRM_FORMAT_YUV420:
+		*mode = ATMEL_HLCDC_YUV420_MODE;
+		break;
+	case DRM_FORMAT_YUV422:
+		*mode = ATMEL_HLCDC_YUV422_MODE;
+		break;
+	default:
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+static bool atmel_hlcdc_format_embedds_alpha(u32 format)
+{
+	int i;
+
+	for (i = 0; i < sizeof(format); i++) {
+		char tmp = (format >> (8 * i)) & 0xff;
+
+		if (tmp == 'A')
+			return true;
+	}
+
+	return false;
+}
+
+static u32 heo_downscaling_xcoef[] = {
+	0x11343311,
+	0x000000f7,
+	0x1635300c,
+	0x000000f9,
+	0x1b362c08,
+	0x000000fb,
+	0x1f372804,
+	0x000000fe,
+	0x24382400,
+	0x00000000,
+	0x28371ffe,
+	0x00000004,
+	0x2c361bfb,
+	0x00000008,
+	0x303516f9,
+	0x0000000c,
+};
+
+static u32 heo_downscaling_ycoef[] = {
+	0x00123737,
+	0x00173732,
+	0x001b382d,
+	0x001f3928,
+	0x00243824,
+	0x0028391f,
+	0x002d381b,
+	0x00323717,
+};
+
+static u32 heo_upscaling_xcoef[] = {
+	0xf74949f7,
+	0x00000000,
+	0xf55f33fb,
+	0x000000fe,
+	0xf5701efe,
+	0x000000ff,
+	0xf87c0dff,
+	0x00000000,
+	0x00800000,
+	0x00000000,
+	0x0d7cf800,
+	0x000000ff,
+	0x1e70f5ff,
+	0x000000fe,
+	0x335ff5fe,
+	0x000000fb,
+};
+
+static u32 heo_upscaling_ycoef[] = {
+	0x00004040,
+	0x00075920,
+	0x00056f0c,
+	0x00027b03,
+	0x00008000,
+	0x00037b02,
+	0x000c6f05,
+	0x00205907,
+};
+
+static void
+atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	const struct atmel_hlcdc_layer_cfg_layout *layout =
+						&plane->layer.desc->layout;
+
+	if (layout->size)
+		atmel_hlcdc_layer_update_cfg(&plane->layer,
+					     layout->size,
+					     0xffffffff,
+					     (req->crtc_w - 1) |
+					     ((req->crtc_h - 1) << 16));
+
+	if (layout->memsize)
+		atmel_hlcdc_layer_update_cfg(&plane->layer,
+					     layout->memsize,
+					     0xffffffff,
+					     (req->src_w - 1) |
+					     ((req->src_h - 1) << 16));
+
+	if (layout->pos)
+		atmel_hlcdc_layer_update_cfg(&plane->layer,
+					     layout->pos,
+					     0xffffffff,
+					     req->crtc_x |
+					     (req->crtc_y  << 16));
+
+	/* TODO: rework the rescaling part */
+	if (req->crtc_w != req->src_w || req->crtc_h != req->src_h) {
+		u32 factor_reg = 0;
+
+		if (req->crtc_w != req->src_w) {
+			int i;
+			u32 factor;
+			u32 *coeff_tab = heo_upscaling_xcoef;
+			u32 max_memsize;
+
+			if (req->crtc_w < req->src_w)
+				coeff_tab = heo_downscaling_xcoef;
+			for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++)
+				atmel_hlcdc_layer_update_cfg(&plane->layer,
+							     17 + i,
+							     0xffffffff,
+							     coeff_tab[i]);
+			factor = ((8 * 256 * req->src_w) - (256 * 4)) /
+				 req->crtc_w;
+			factor++;
+			max_memsize = ((factor * req->crtc_w) + (256 * 4)) /
+				      2048;
+			if (max_memsize > req->src_w)
+				factor--;
+			factor_reg |= factor | 0x80000000;
+		}
+
+		if (req->crtc_h != req->src_h) {
+			int i;
+			u32 factor;
+			u32 *coeff_tab = heo_upscaling_ycoef;
+			u32 max_memsize;
+
+			if (req->crtc_w < req->src_w)
+				coeff_tab = heo_downscaling_ycoef;
+			for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
+				atmel_hlcdc_layer_update_cfg(&plane->layer,
+							     33 + i,
+							     0xffffffff,
+							     coeff_tab[i]);
+			factor = ((8 * 256 * req->src_w) - (256 * 4)) /
+				 req->crtc_w;
+			factor++;
+			max_memsize = ((factor * req->crtc_w) + (256 * 4)) /
+				      2048;
+			if (max_memsize > req->src_w)
+				factor--;
+			factor_reg |= (factor << 16) | 0x80000000;
+		}
+
+		atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff,
+					     factor_reg);
+	}
+}
+
+static void
+atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	const struct atmel_hlcdc_layer_cfg_layout *layout =
+						&plane->layer.desc->layout;
+	unsigned int cfg = ATMEL_HLCDC_LAYER_DMA;
+
+	if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
+		cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
+		       ATMEL_HLCDC_LAYER_ITER;
+
+		if (atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format))
+			cfg |= ATMEL_HLCDC_LAYER_LAEN;
+		else
+			cfg |= ATMEL_HLCDC_LAYER_GAEN;
+	}
+
+	atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
+				     ATMEL_HLCDC_LAYER_ITER2BL |
+				     ATMEL_HLCDC_LAYER_ITER |
+				     ATMEL_HLCDC_LAYER_GAEN |
+				     ATMEL_HLCDC_LAYER_LAEN |
+				     ATMEL_HLCDC_LAYER_OVR |
+				     ATMEL_HLCDC_LAYER_DMA, cfg);
+}
+
+static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	u32 mode;
+	int ret;
+
+	ret = atmel_hlcdc_format_to_plane_mode(req->fb->pixel_format, &mode);
+	if (ret)
+		return;
+
+	atmel_hlcdc_layer_update_cfg(&plane->layer,
+				     ATMEL_HLCDC_LAYER_FORMAT_CFG_ID,
+				     0xffffffff,
+				     mode);
+}
+
+static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	struct atmel_hlcdc_layer *layer = &plane->layer;
+	const struct atmel_hlcdc_layer_cfg_layout *layout =
+							&layer->desc->layout;
+	int i;
+
+	atmel_hlcdc_layer_update_set_fb(&plane->layer, req->fb, req->offsets);
+
+	for (i = 0; i < req->nplanes; i++) {
+		if (layout->xstride[i]) {
+			atmel_hlcdc_layer_update_cfg(&plane->layer,
+						layout->xstride[i],
+						0xffffffff,
+						req->xstride[i]);
+		}
+
+		if (layout->pstride[i]) {
+			atmel_hlcdc_layer_update_cfg(&plane->layer,
+						layout->pstride[i],
+						0xffffffff,
+						req->pstride[i]);
+		}
+	}
+}
+
+static int atmel_hlcdc_plane_check_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	const struct atmel_hlcdc_layer_cfg_layout *layout =
+						&plane->layer.desc->layout;
+
+	if (!layout->size &&
+	    (req->crtc->mode.crtc_hdisplay != req->crtc_w ||
+	     req->crtc->mode.crtc_vdisplay != req->crtc_h))
+		return -EINVAL;
+
+	if (plane->layer.desc->max_height &&
+	    req->crtc_h > plane->layer.desc->max_height)
+		return -EINVAL;
+
+	if (plane->layer.desc->max_width &&
+	    req->crtc_w > plane->layer.desc->max_width)
+		return -EINVAL;
+
+	if ((req->crtc_h != req->src_h || req->crtc_w != req->src_w) &&
+	    (!layout->memsize ||
+	     atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format)))
+		return -EINVAL;
+
+	return 0;
+}
+
+int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	unsigned int patched_crtc_w;
+	unsigned int patched_crtc_h;
+	unsigned int patched_src_w;
+	unsigned int patched_src_h;
+	unsigned int tmp;
+	int x_offset = 0;
+	int y_offset = 0;
+	int i;
+
+	if ((req->src_x | req->src_y | req->src_w | req->src_h) &
+	    SUBPIXEL_MASK)
+		return -EINVAL;
+
+	req->src_x >>= 16;
+	req->src_y >>= 16;
+	req->src_w >>= 16;
+	req->src_h >>= 16;
+
+	req->nplanes = drm_format_num_planes(req->fb->pixel_format);
+	if (req->nplanes > ATMEL_HLCDC_MAX_PLANES)
+		return -EINVAL;
+
+	/*
+	 * Swap width and size if in case of 90 or 270 degrees rotation
+	 */
+	if (plane->rotation == ATMEL_HLCDC_PLANE_90DEG_ROTATION ||
+	    plane->rotation == ATMEL_HLCDC_PLANE_270DEG_ROTATION) {
+		tmp = req->crtc_w;
+		req->crtc_w = req->crtc_h;
+		req->crtc_h = tmp;
+		tmp = req->src_w;
+		req->src_w = req->src_h;
+		req->src_h = tmp;
+	}
+
+	if (req->crtc_x + req->crtc_w > req->crtc->mode.hdisplay)
+		patched_crtc_w = req->crtc->mode.hdisplay - req->crtc_x;
+	else
+		patched_crtc_w = req->crtc_w;
+
+	if (req->crtc_x < 0) {
+		patched_crtc_w += req->crtc_x;
+		x_offset = -req->crtc_x;
+		req->crtc_x = 0;
+	}
+
+	if (req->crtc_y + req->crtc_h > req->crtc->mode.vdisplay)
+		patched_crtc_h = req->crtc->mode.vdisplay - req->crtc_y;
+	else
+		patched_crtc_h = req->crtc_h;
+
+	if (req->crtc_y < 0) {
+		patched_crtc_h += req->crtc_y;
+		y_offset = -req->crtc_y;
+		req->crtc_y = 0;
+	}
+
+	patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * req->src_w,
+					  req->crtc_w);
+	patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * req->src_h,
+					  req->crtc_h);
+
+	for (i = 0; i < req->nplanes; i++) {
+		unsigned int offset = 0;
+
+		req->bpp[i] = drm_format_plane_cpp(req->fb->pixel_format, i);
+		if (!req->bpp[i])
+			return -EINVAL;
+
+		switch (plane->rotation) {
+		case ATMEL_HLCDC_PLANE_90DEG_ROTATION:
+			offset = (y_offset + req->src_y + patched_src_w - 1) *
+				 req->fb->pitches[i];
+			offset += (x_offset + req->src_x) * req->bpp[i];
+			req->xstride[i] = req->fb->pitches[i] *
+					  (patched_src_w - 1);
+			req->pstride[i] = -req->fb->pitches[i] - req->bpp[i];
+			break;
+		case ATMEL_HLCDC_PLANE_180DEG_ROTATION:
+			offset = (y_offset + req->src_y + patched_src_h - 1) *
+				 req->fb->pitches[i];
+			offset += (x_offset + req->src_x + patched_src_w) *
+				  req->bpp[i];
+			req->xstride[i] = ((patched_src_w - 2) * req->bpp[i]) -
+					   req->fb->pitches[i];
+			req->pstride[i] = -2 * req->bpp[i];
+			break;
+		case ATMEL_HLCDC_PLANE_270DEG_ROTATION:
+			offset = (y_offset + req->src_y) * req->fb->pitches[i];
+			offset += (x_offset + req->src_x + patched_src_h) *
+				  req->bpp[i];
+			req->xstride[i] = -(req->fb->pitches[i] *
+					    (patched_src_w - 1)) -
+					  (2 * req->bpp[i]);
+			req->pstride[i] = req->fb->pitches[i] - req->bpp[i];
+			break;
+		case ATMEL_HLCDC_PLANE_NO_ROTATION:
+		default:
+			offset = (y_offset + req->src_y) * req->fb->pitches[i];
+			offset += (x_offset + req->src_x) * req->bpp[i];
+			req->xstride[i] = req->fb->pitches[i] -
+					  (patched_src_w * req->bpp[i]);
+			req->pstride[i] = 0;
+			break;
+		}
+
+		req->offsets[i] = offset + req->fb->offsets[i];
+	}
+
+	req->src_w = patched_src_w;
+	req->src_h = patched_src_h;
+	req->crtc_w = patched_crtc_w;
+	req->crtc_h = patched_crtc_h;
+
+	return atmel_hlcdc_plane_check_update_req(p, req);
+}
+
+int atmel_hlcdc_plane_apply_update_req(struct drm_plane *p,
+				struct atmel_hlcdc_plane_update_req *req)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	int ret;
+
+	ret = atmel_hlcdc_layer_update_start(&plane->layer);
+	if (ret)
+		return ret;
+
+	atmel_hlcdc_plane_update_pos_and_size(plane, req);
+	atmel_hlcdc_plane_update_general_settings(plane, req);
+	atmel_hlcdc_plane_update_format(plane, req);
+	atmel_hlcdc_plane_update_buffers(plane, req);
+	atmel_hlcdc_layer_update_set_finished(&plane->layer, req->finished,
+					      req->finished_data);
+
+	atmel_hlcdc_layer_update_commit(&plane->layer);
+
+	return 0;
+}
+
+static int atmel_hlcdc_plane_update(struct drm_plane *p,
+				    struct drm_crtc *crtc,
+				    struct drm_framebuffer *fb,
+				    int crtc_x, int crtc_y,
+				    unsigned int crtc_w, unsigned int crtc_h,
+				    uint32_t src_x, uint32_t src_y,
+				    uint32_t src_w, uint32_t src_h)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	struct atmel_hlcdc_plane_update_req req;
+	int ret = 0;
+
+	memset(&req, 0, sizeof(req));
+	req.crtc_x = crtc_x;
+	req.crtc_y = crtc_y;
+	req.crtc_w = crtc_w;
+	req.crtc_h = crtc_h;
+	req.src_x = src_x;
+	req.src_y = src_y;
+	req.src_w = src_w;
+	req.src_h = src_h;
+	req.fb = fb;
+	req.crtc = crtc;
+
+	ret = atmel_hlcdc_plane_prepare_update_req(&plane->base, &req);
+	if (ret)
+		return ret;
+
+	if (!req.crtc_h || !req.crtc_w)
+		return atmel_hlcdc_layer_disable(&plane->layer);
+
+	return atmel_hlcdc_plane_apply_update_req(&plane->base, &req);
+}
+
+static int atmel_hlcdc_plane_disable(struct drm_plane *p)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+
+	return atmel_hlcdc_layer_disable(&plane->layer);
+}
+
+static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+
+	if (plane->base.fb)
+		drm_framebuffer_unreference(plane->base.fb);
+
+	atmel_hlcdc_layer_cleanup(p->dev, &plane->layer);
+
+	drm_plane_cleanup(p);
+	devm_kfree(p->dev->dev, plane);
+}
+
+static int atmel_hlcdc_plane_set_alpha(struct atmel_hlcdc_plane *plane,
+				       u8 alpha)
+{
+	atmel_hlcdc_layer_update_start(&plane->layer);
+	atmel_hlcdc_layer_update_cfg(&plane->layer,
+				     plane->layer.desc->layout.general_config,
+				     ATMEL_HLCDC_LAYER_GA_MASK,
+				     alpha << ATMEL_HLCDC_LAYER_GA_SHIFT);
+	atmel_hlcdc_layer_update_commit(&plane->layer);
+
+	return 0;
+}
+
+static int atmel_hlcdc_plane_set_rotation(struct atmel_hlcdc_plane *plane,
+					  enum atmel_hlcdc_plane_rotation rot)
+{
+	plane->rotation = rot;
+
+	return 0;
+}
+
+static int atmel_hlcdc_plane_set_property(struct drm_plane *p,
+					  struct drm_property *property,
+					  uint64_t value)
+{
+	struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
+	struct atmel_hlcdc_plane_properties *props = plane->properties;
+
+	if (property == props->alpha)
+		atmel_hlcdc_plane_set_alpha(plane, value);
+	else if (property == props->rotation)
+		atmel_hlcdc_plane_set_rotation(plane, value);
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
+				const struct atmel_hlcdc_layer_desc *desc,
+				struct atmel_hlcdc_plane_properties *props)
+{
+	struct regmap *regmap = plane->layer.hlcdc->regmap;
+
+	if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
+	    desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
+		drm_object_attach_property(&plane->base.base,
+					   props->alpha, 255);
+
+		/* Set default alpha value */
+		regmap_update_bits(regmap,
+				desc->regs_offset +
+				ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer),
+				ATMEL_HLCDC_LAYER_GA_MASK,
+				ATMEL_HLCDC_LAYER_GA_MASK);
+	}
+
+	if (desc->layout.xstride && desc->layout.pstride)
+		drm_object_attach_property(&plane->base.base,
+					   props->rotation,
+					   ATMEL_HLCDC_PLANE_NO_ROTATION);
+
+	if (desc->layout.csc) {
+		/*
+		 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
+		 * userspace modify these factors (using a BLOB property ?).
+		 */
+		regmap_write(regmap,
+			     desc->regs_offset +
+			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0),
+			     0x4c900091);
+		regmap_write(regmap,
+			     desc->regs_offset +
+			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1),
+			     0x7a5f5090);
+		regmap_write(regmap,
+			     desc->regs_offset +
+			     ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2),
+			     0x40040890);
+	}
+}
+
+static struct drm_plane_funcs layer_plane_funcs = {
+	.update_plane = atmel_hlcdc_plane_update,
+	.disable_plane = atmel_hlcdc_plane_disable,
+	.set_property = atmel_hlcdc_plane_set_property,
+	.destroy = atmel_hlcdc_plane_destroy,
+};
+
+static struct atmel_hlcdc_plane *
+atmel_hlcdc_plane_create(struct drm_device *dev,
+			 const struct atmel_hlcdc_layer_desc *desc,
+			 struct atmel_hlcdc_plane_properties *props)
+{
+	struct atmel_hlcdc_plane *plane;
+	enum drm_plane_type type;
+	int ret;
+
+	plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
+	if (!plane)
+		return ERR_PTR(-ENOMEM);
+
+	ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc);
+	if (ret)
+		return ERR_PTR(ret);
+
+	if (desc->type == ATMEL_HLCDC_BASE_LAYER)
+		type = DRM_PLANE_TYPE_PRIMARY;
+	else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)
+		type = DRM_PLANE_TYPE_CURSOR;
+	else
+		type = DRM_PLANE_TYPE_OVERLAY;
+
+	ret = drm_universal_plane_init(dev, &plane->base, 0,
+				       &layer_plane_funcs,
+				       desc->formats->formats,
+				       desc->formats->nformats, type);
+	if (ret)
+		return ERR_PTR(ret);
+
+	/* Set default property values*/
+	atmel_hlcdc_plane_init_properties(plane, desc, props);
+
+	return plane;
+}
+
+static struct atmel_hlcdc_plane_properties *
+atmel_hlcdc_plane_create_properties(struct drm_device *dev)
+{
+	struct atmel_hlcdc_plane_properties *props;
+	const struct drm_prop_enum_list rotations[] = {
+		{ ATMEL_HLCDC_PLANE_NO_ROTATION,   "rotate-0" },
+		{ ATMEL_HLCDC_PLANE_90DEG_ROTATION,  "rotate-90" },
+		{ ATMEL_HLCDC_PLANE_180DEG_ROTATION, "rotate-180" },
+		{ ATMEL_HLCDC_PLANE_270DEG_ROTATION, "rotate-270" },
+	};
+
+	props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL);
+	if (!props)
+		return ERR_PTR(-ENOMEM);
+
+	props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255);
+	if (!props->alpha)
+		return ERR_PTR(-ENOMEM);
+
+	props->rotation = drm_property_create_enum(dev, 0, "rotation",
+						rotations,
+						ARRAY_SIZE(rotations));
+	return props;
+}
+
+struct atmel_hlcdc_planes *
+atmel_hlcdc_create_planes(struct drm_device *dev)
+{
+	struct atmel_hlcdc_dc *dc = dev->dev_private;
+	struct atmel_hlcdc_plane_properties *props;
+	struct atmel_hlcdc_planes *planes;
+	const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
+	int nlayers = dc->desc->nlayers;
+	int i;
+
+	planes = devm_kzalloc(dev->dev, sizeof(*planes), GFP_KERNEL);
+	if (!planes)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < nlayers; i++) {
+		if (descs[i].type == ATMEL_HLCDC_OVERLAY_LAYER)
+			planes->noverlays++;
+	}
+
+	if (planes->noverlays) {
+		planes->overlays = devm_kzalloc(dev->dev,
+						planes->noverlays *
+						sizeof(*planes->overlays),
+						GFP_KERNEL);
+		if (!planes->overlays)
+			return ERR_PTR(-ENOMEM);
+	}
+
+	props = atmel_hlcdc_plane_create_properties(dev);
+	if (IS_ERR(props))
+		return ERR_CAST(props);
+
+	planes->noverlays = 0;
+	for (i = 0; i < nlayers; i++) {
+		struct atmel_hlcdc_plane *plane;
+
+		if (descs[i].type == ATMEL_HLCDC_PP_LAYER)
+			continue;
+
+		plane = atmel_hlcdc_plane_create(dev, &descs[i], props);
+		if (IS_ERR(plane))
+			return ERR_CAST(plane);
+
+		plane->properties = props;
+
+		switch (descs[i].type) {
+		case ATMEL_HLCDC_BASE_LAYER:
+			if (planes->primary)
+				return ERR_PTR(-EINVAL);
+			planes->primary = plane;
+			break;
+
+		case ATMEL_HLCDC_OVERLAY_LAYER:
+			planes->overlays[planes->noverlays++] = plane;
+			break;
+
+		case ATMEL_HLCDC_CURSOR_LAYER:
+			if (planes->cursor)
+				return ERR_PTR(-EINVAL);
+			planes->cursor = plane;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	return planes;
+}
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 06/11] drm: add DT bindings documentation for atmel-hlcdc-dc driver
  2014-07-22 13:11 ` Boris BREZILLON
  (?)
@ 2014-07-22 13:11   ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree, linux-kernel, linux-arm-kernel, Bo Shen,
	Boris BREZILLON

The Atmel HLCDC (HLCD Controller) IP available on some Atmel SoCs (i.e.
at91sam9n12, at91sam9x5 family or sama5d3 family) provides a display
controller device.

The HLCDC block provides a single RGB output port, and only supports LCD
panels connection to LCD panels for now.

The atmel,panel property link the HLCDC RGB output with the LCD panel
connected on this port (note that the HLCDC RGB connector implementation
makes use of the DRM panel framework).

Connection to other external devices (DRM bridges) might be added later by
mean of a new atmel,xxx (atmel,bridge) property.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 .../devicetree/bindings/drm/atmel-hlcdc-dc.txt     | 54 ++++++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt

diff --git a/Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt b/Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt
new file mode 100644
index 0000000..73b9860
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt
@@ -0,0 +1,54 @@
+Device-Tree bindings for Atmel's HLCDC (High LCD Controller) DRM driver
+
+The Atmel HLCDC Display Controller is subdevice of the HLCDC MFD device.
+See ../mfd/atmel-hlcdc.txt for more details.
+
+Required properties:
+ - compatible: value should be "atmel,hlcdc-display-controller"
+ - interrupts: the HLCDC interrupt definition
+ - pinctrl-names: the pin control state names. Should contain "default".
+ - pinctrl-0: should contain the default pinctrl states.
+ - #address-cells: should be set to 1.
+ - #size-cells: should be set to 0.
+
+Required children nodes:
+ Children nodes are encoding available output ports and their connections
+ to external devices using the OF graph reprensentation (see ../graph.txt).
+ At least one port node is required.
+
+Example:
+
+	hlcdc: hlcdc@f0030000 {
+		compatible = "atmel,sama5d3-hlcdc";
+		reg = <0xf0030000 0x2000>;
+		clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>;
+		clock-names = "periph_clk","sys_clk", "slow_clk";
+		status = "disabled";
+
+		hlcdc-display-controller {
+			compatible = "atmel,hlcdc-display-controller";
+			interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <0>;
+
+				hlcdc_panel_output: endpoint@0 {
+					reg = <0>;
+					remote-endpoint = <&panel_input>;
+				};
+			};
+		};
+
+		hlcdc_pwm: hlcdc-pwm {
+			compatible = "atmel,hlcdc-pwm";
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_pwm>;
+			#pwm-cells = <3>;
+		};
+	};
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 06/11] drm: add DT bindings documentation for atmel-hlcdc-dc driver
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, linux-kernel,
	Rob Herring, Bo Shen, Kumar Gala, linux-arm-kernel

The Atmel HLCDC (HLCD Controller) IP available on some Atmel SoCs (i.e.
at91sam9n12, at91sam9x5 family or sama5d3 family) provides a display
controller device.

The HLCDC block provides a single RGB output port, and only supports LCD
panels connection to LCD panels for now.

The atmel,panel property link the HLCDC RGB output with the LCD panel
connected on this port (note that the HLCDC RGB connector implementation
makes use of the DRM panel framework).

Connection to other external devices (DRM bridges) might be added later by
mean of a new atmel,xxx (atmel,bridge) property.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 .../devicetree/bindings/drm/atmel-hlcdc-dc.txt     | 54 ++++++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt

diff --git a/Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt b/Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt
new file mode 100644
index 0000000..73b9860
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt
@@ -0,0 +1,54 @@
+Device-Tree bindings for Atmel's HLCDC (High LCD Controller) DRM driver
+
+The Atmel HLCDC Display Controller is subdevice of the HLCDC MFD device.
+See ../mfd/atmel-hlcdc.txt for more details.
+
+Required properties:
+ - compatible: value should be "atmel,hlcdc-display-controller"
+ - interrupts: the HLCDC interrupt definition
+ - pinctrl-names: the pin control state names. Should contain "default".
+ - pinctrl-0: should contain the default pinctrl states.
+ - #address-cells: should be set to 1.
+ - #size-cells: should be set to 0.
+
+Required children nodes:
+ Children nodes are encoding available output ports and their connections
+ to external devices using the OF graph reprensentation (see ../graph.txt).
+ At least one port node is required.
+
+Example:
+
+	hlcdc: hlcdc@f0030000 {
+		compatible = "atmel,sama5d3-hlcdc";
+		reg = <0xf0030000 0x2000>;
+		clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>;
+		clock-names = "periph_clk","sys_clk", "slow_clk";
+		status = "disabled";
+
+		hlcdc-display-controller {
+			compatible = "atmel,hlcdc-display-controller";
+			interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <0>;
+
+				hlcdc_panel_output: endpoint@0 {
+					reg = <0>;
+					remote-endpoint = <&panel_input>;
+				};
+			};
+		};
+
+		hlcdc_pwm: hlcdc-pwm {
+			compatible = "atmel,hlcdc-pwm";
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_pwm>;
+			#pwm-cells = <3>;
+		};
+	};
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 06/11] drm: add DT bindings documentation for atmel-hlcdc-dc driver
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

The Atmel HLCDC (HLCD Controller) IP available on some Atmel SoCs (i.e.
at91sam9n12, at91sam9x5 family or sama5d3 family) provides a display
controller device.

The HLCDC block provides a single RGB output port, and only supports LCD
panels connection to LCD panels for now.

The atmel,panel property link the HLCDC RGB output with the LCD panel
connected on this port (note that the HLCDC RGB connector implementation
makes use of the DRM panel framework).

Connection to other external devices (DRM bridges) might be added later by
mean of a new atmel,xxx (atmel,bridge) property.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 .../devicetree/bindings/drm/atmel-hlcdc-dc.txt     | 54 ++++++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt

diff --git a/Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt b/Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt
new file mode 100644
index 0000000..73b9860
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt
@@ -0,0 +1,54 @@
+Device-Tree bindings for Atmel's HLCDC (High LCD Controller) DRM driver
+
+The Atmel HLCDC Display Controller is subdevice of the HLCDC MFD device.
+See ../mfd/atmel-hlcdc.txt for more details.
+
+Required properties:
+ - compatible: value should be "atmel,hlcdc-display-controller"
+ - interrupts: the HLCDC interrupt definition
+ - pinctrl-names: the pin control state names. Should contain "default".
+ - pinctrl-0: should contain the default pinctrl states.
+ - #address-cells: should be set to 1.
+ - #size-cells: should be set to 0.
+
+Required children nodes:
+ Children nodes are encoding available output ports and their connections
+ to external devices using the OF graph reprensentation (see ../graph.txt).
+ At least one port node is required.
+
+Example:
+
+	hlcdc: hlcdc at f0030000 {
+		compatible = "atmel,sama5d3-hlcdc";
+		reg = <0xf0030000 0x2000>;
+		clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>;
+		clock-names = "periph_clk","sys_clk", "slow_clk";
+		status = "disabled";
+
+		hlcdc-display-controller {
+			compatible = "atmel,hlcdc-display-controller";
+			interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port at 0 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				reg = <0>;
+
+				hlcdc_panel_output: endpoint at 0 {
+					reg = <0>;
+					remote-endpoint = <&panel_input>;
+				};
+			};
+		};
+
+		hlcdc_pwm: hlcdc-pwm {
+			compatible = "atmel,hlcdc-pwm";
+			pinctrl-names = "default";
+			pinctrl-0 = <&pinctrl_lcd_pwm>;
+			#pwm-cells = <3>;
+		};
+	};
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 07/11] ARM: AT91/dt: split sama5d3 lcd pin definitions to match RGB mode configs
  2014-07-22 13:11 ` Boris BREZILLON
  (?)
@ 2014-07-22 13:11   ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree, linux-kernel, linux-arm-kernel, Bo Shen,
	Boris BREZILLON

The HLCDC (HLCD Controller) IP supports 4 different output mode (RGB444,
RGB565, RGB666 and RGB888) and the pin muxing will depend on the chosen
RGB mode.

Split pin definitions to be able to set pin config according to the
selected mode.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d3_lcd.dtsi | 127 ++++++++++++++++++++++++++++---------
 1 file changed, 96 insertions(+), 31 deletions(-)

diff --git a/arch/arm/boot/dts/sama5d3_lcd.dtsi b/arch/arm/boot/dts/sama5d3_lcd.dtsi
index 85d3027..2186b89 100644
--- a/arch/arm/boot/dts/sama5d3_lcd.dtsi
+++ b/arch/arm/boot/dts/sama5d3_lcd.dtsi
@@ -15,38 +15,103 @@
 		apb {
 			pinctrl@fffff200 {
 				lcd {
-					pinctrl_lcd: lcd-0 {
+					pinctrl_lcd_pwm: lcd-pwm-0 {
+						atmel,pins = <AT91_PIOA 24 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDPWM */
+					};
+
+					pinctrl_lcd_base: lcd-base-0 {
+						atmel,pins =
+							<AT91_PIOA 26 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDVSYNC */
+							 AT91_PIOA 27 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDHSYNC */
+							 AT91_PIOA 25 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDDISP */
+							 AT91_PIOA 29 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDDEN */
+							 AT91_PIOA 28 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDPCK */
+					};
+
+					pinctrl_lcd_rgb444: lcd-rgb-0 {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDD11 pin */
+					};
+
+					pinctrl_lcd_rgb565: lcd-rgb-1 {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDD15 pin */
+					};
+
+					pinctrl_lcd_rgb666: lcd-rgb-2 {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
+							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD16 pin */
+							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE>;	/* LCDD17 pin */
+					};
+
+					pinctrl_lcd_rgb888: lcd-rgb-3 {
 						atmel,pins =
-							<AT91_PIOA 24 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA24 periph A LCDPWM */
-							 AT91_PIOA 26 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA26 periph A LCDVSYNC */
-							 AT91_PIOA 27 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA27 periph A LCDHSYNC */
-							 AT91_PIOA 25 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA25 periph A LCDDISP */
-							 AT91_PIOA 29 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA29 periph A LCDDEN */
-							 AT91_PIOA 28 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA28 periph A LCDPCK */
-							 AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA0 periph A LCDD0 pin */
-							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA1 periph A LCDD1 pin */
-							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA2 periph A LCDD2 pin */
-							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA3 periph A LCDD3 pin */
-							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA4 periph A LCDD4 pin */
-							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA5 periph A LCDD5 pin */
-							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA6 periph A LCDD6 pin */
-							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA7 periph A LCDD7 pin */
-							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA8 periph A LCDD8 pin */
-							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA9 periph A LCDD9 pin */
-							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA10 periph A LCDD10 pin */
-							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA11 periph A LCDD11 pin */
-							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA12 periph A LCDD12 pin */
-							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA13 periph A LCDD13 pin */
-							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA14 periph A LCDD14 pin */
-							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA15 periph A LCDD15 pin */
-							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC14 periph C LCDD16 pin */
-							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC13 periph C LCDD17 pin */
-							 AT91_PIOC 12 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC12 periph C LCDD18 pin */
-							 AT91_PIOC 11 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC11 periph C LCDD19 pin */
-							 AT91_PIOC 10 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC10 periph C LCDD20 pin */
-							 AT91_PIOC 15 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC15 periph C LCDD21 pin */
-							 AT91_PIOE 27 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PE27 periph C LCDD22 pin */
-							 AT91_PIOE 28 AT91_PERIPH_C AT91_PINCTRL_NONE>;	/* PE28 periph C LCDD23 pin */
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
+							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD16 pin */
+							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD17 pin */
+							 AT91_PIOC 12 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD18 pin */
+							 AT91_PIOC 11 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD19 pin */
+							 AT91_PIOC 10 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD20 pin */
+							 AT91_PIOC 15 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD21 pin */
+							 AT91_PIOE 27 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD22 pin */
+							 AT91_PIOE 28 AT91_PERIPH_C AT91_PINCTRL_NONE>;	/* LCDD23 pin */
 					};
 				};
 			};
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 07/11] ARM: AT91/dt: split sama5d3 lcd pin definitions to match RGB mode configs
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, linux-kernel,
	Rob Herring, Bo Shen, Kumar Gala, linux-arm-kernel

The HLCDC (HLCD Controller) IP supports 4 different output mode (RGB444,
RGB565, RGB666 and RGB888) and the pin muxing will depend on the chosen
RGB mode.

Split pin definitions to be able to set pin config according to the
selected mode.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d3_lcd.dtsi | 127 ++++++++++++++++++++++++++++---------
 1 file changed, 96 insertions(+), 31 deletions(-)

diff --git a/arch/arm/boot/dts/sama5d3_lcd.dtsi b/arch/arm/boot/dts/sama5d3_lcd.dtsi
index 85d3027..2186b89 100644
--- a/arch/arm/boot/dts/sama5d3_lcd.dtsi
+++ b/arch/arm/boot/dts/sama5d3_lcd.dtsi
@@ -15,38 +15,103 @@
 		apb {
 			pinctrl@fffff200 {
 				lcd {
-					pinctrl_lcd: lcd-0 {
+					pinctrl_lcd_pwm: lcd-pwm-0 {
+						atmel,pins = <AT91_PIOA 24 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDPWM */
+					};
+
+					pinctrl_lcd_base: lcd-base-0 {
+						atmel,pins =
+							<AT91_PIOA 26 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDVSYNC */
+							 AT91_PIOA 27 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDHSYNC */
+							 AT91_PIOA 25 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDDISP */
+							 AT91_PIOA 29 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDDEN */
+							 AT91_PIOA 28 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDPCK */
+					};
+
+					pinctrl_lcd_rgb444: lcd-rgb-0 {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDD11 pin */
+					};
+
+					pinctrl_lcd_rgb565: lcd-rgb-1 {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDD15 pin */
+					};
+
+					pinctrl_lcd_rgb666: lcd-rgb-2 {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
+							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD16 pin */
+							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE>;	/* LCDD17 pin */
+					};
+
+					pinctrl_lcd_rgb888: lcd-rgb-3 {
 						atmel,pins =
-							<AT91_PIOA 24 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA24 periph A LCDPWM */
-							 AT91_PIOA 26 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA26 periph A LCDVSYNC */
-							 AT91_PIOA 27 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA27 periph A LCDHSYNC */
-							 AT91_PIOA 25 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA25 periph A LCDDISP */
-							 AT91_PIOA 29 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA29 periph A LCDDEN */
-							 AT91_PIOA 28 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA28 periph A LCDPCK */
-							 AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA0 periph A LCDD0 pin */
-							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA1 periph A LCDD1 pin */
-							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA2 periph A LCDD2 pin */
-							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA3 periph A LCDD3 pin */
-							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA4 periph A LCDD4 pin */
-							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA5 periph A LCDD5 pin */
-							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA6 periph A LCDD6 pin */
-							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA7 periph A LCDD7 pin */
-							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA8 periph A LCDD8 pin */
-							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA9 periph A LCDD9 pin */
-							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA10 periph A LCDD10 pin */
-							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA11 periph A LCDD11 pin */
-							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA12 periph A LCDD12 pin */
-							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA13 periph A LCDD13 pin */
-							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA14 periph A LCDD14 pin */
-							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA15 periph A LCDD15 pin */
-							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC14 periph C LCDD16 pin */
-							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC13 periph C LCDD17 pin */
-							 AT91_PIOC 12 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC12 periph C LCDD18 pin */
-							 AT91_PIOC 11 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC11 periph C LCDD19 pin */
-							 AT91_PIOC 10 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC10 periph C LCDD20 pin */
-							 AT91_PIOC 15 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC15 periph C LCDD21 pin */
-							 AT91_PIOE 27 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PE27 periph C LCDD22 pin */
-							 AT91_PIOE 28 AT91_PERIPH_C AT91_PINCTRL_NONE>;	/* PE28 periph C LCDD23 pin */
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
+							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD16 pin */
+							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD17 pin */
+							 AT91_PIOC 12 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD18 pin */
+							 AT91_PIOC 11 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD19 pin */
+							 AT91_PIOC 10 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD20 pin */
+							 AT91_PIOC 15 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD21 pin */
+							 AT91_PIOE 27 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD22 pin */
+							 AT91_PIOE 28 AT91_PERIPH_C AT91_PINCTRL_NONE>;	/* LCDD23 pin */
 					};
 				};
 			};
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 07/11] ARM: AT91/dt: split sama5d3 lcd pin definitions to match RGB mode configs
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

The HLCDC (HLCD Controller) IP supports 4 different output mode (RGB444,
RGB565, RGB666 and RGB888) and the pin muxing will depend on the chosen
RGB mode.

Split pin definitions to be able to set pin config according to the
selected mode.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d3_lcd.dtsi | 127 ++++++++++++++++++++++++++++---------
 1 file changed, 96 insertions(+), 31 deletions(-)

diff --git a/arch/arm/boot/dts/sama5d3_lcd.dtsi b/arch/arm/boot/dts/sama5d3_lcd.dtsi
index 85d3027..2186b89 100644
--- a/arch/arm/boot/dts/sama5d3_lcd.dtsi
+++ b/arch/arm/boot/dts/sama5d3_lcd.dtsi
@@ -15,38 +15,103 @@
 		apb {
 			pinctrl at fffff200 {
 				lcd {
-					pinctrl_lcd: lcd-0 {
+					pinctrl_lcd_pwm: lcd-pwm-0 {
+						atmel,pins = <AT91_PIOA 24 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDPWM */
+					};
+
+					pinctrl_lcd_base: lcd-base-0 {
+						atmel,pins =
+							<AT91_PIOA 26 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDVSYNC */
+							 AT91_PIOA 27 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDHSYNC */
+							 AT91_PIOA 25 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDDISP */
+							 AT91_PIOA 29 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDDEN */
+							 AT91_PIOA 28 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDPCK */
+					};
+
+					pinctrl_lcd_rgb444: lcd-rgb-0 {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDD11 pin */
+					};
+
+					pinctrl_lcd_rgb565: lcd-rgb-1 {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDD15 pin */
+					};
+
+					pinctrl_lcd_rgb666: lcd-rgb-2 {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
+							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD16 pin */
+							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE>;	/* LCDD17 pin */
+					};
+
+					pinctrl_lcd_rgb888: lcd-rgb-3 {
 						atmel,pins =
-							<AT91_PIOA 24 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA24 periph A LCDPWM */
-							 AT91_PIOA 26 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA26 periph A LCDVSYNC */
-							 AT91_PIOA 27 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA27 periph A LCDHSYNC */
-							 AT91_PIOA 25 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA25 periph A LCDDISP */
-							 AT91_PIOA 29 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA29 periph A LCDDEN */
-							 AT91_PIOA 28 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA28 periph A LCDPCK */
-							 AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA0 periph A LCDD0 pin */
-							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA1 periph A LCDD1 pin */
-							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA2 periph A LCDD2 pin */
-							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA3 periph A LCDD3 pin */
-							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA4 periph A LCDD4 pin */
-							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA5 periph A LCDD5 pin */
-							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA6 periph A LCDD6 pin */
-							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA7 periph A LCDD7 pin */
-							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA8 periph A LCDD8 pin */
-							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA9 periph A LCDD9 pin */
-							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA10 periph A LCDD10 pin */
-							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA11 periph A LCDD11 pin */
-							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA12 periph A LCDD12 pin */
-							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA13 periph A LCDD13 pin */
-							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA14 periph A LCDD14 pin */
-							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* PA15 periph A LCDD15 pin */
-							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC14 periph C LCDD16 pin */
-							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC13 periph C LCDD17 pin */
-							 AT91_PIOC 12 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC12 periph C LCDD18 pin */
-							 AT91_PIOC 11 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC11 periph C LCDD19 pin */
-							 AT91_PIOC 10 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC10 periph C LCDD20 pin */
-							 AT91_PIOC 15 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PC15 periph C LCDD21 pin */
-							 AT91_PIOE 27 AT91_PERIPH_C AT91_PINCTRL_NONE	/* PE27 periph C LCDD22 pin */
-							 AT91_PIOE 28 AT91_PERIPH_C AT91_PINCTRL_NONE>;	/* PE28 periph C LCDD23 pin */
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
+							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD16 pin */
+							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD17 pin */
+							 AT91_PIOC 12 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD18 pin */
+							 AT91_PIOC 11 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD19 pin */
+							 AT91_PIOC 10 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD20 pin */
+							 AT91_PIOC 15 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD21 pin */
+							 AT91_PIOE 27 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD22 pin */
+							 AT91_PIOE 28 AT91_PERIPH_C AT91_PINCTRL_NONE>;	/* LCDD23 pin */
 					};
 				};
 			};
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 08/11] ARM: AT91/dt: add alternative pin muxing for sama5d3 lcd pins
  2014-07-22 13:11 ` Boris BREZILLON
  (?)
@ 2014-07-22 13:11   ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree, linux-kernel, linux-arm-kernel, Bo Shen,
	Boris BREZILLON

Define alternative pin muxing for the LCDC pins.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d3_lcd.dtsi | 50 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d3_lcd.dtsi b/arch/arm/boot/dts/sama5d3_lcd.dtsi
index 2186b89..e7581f6 100644
--- a/arch/arm/boot/dts/sama5d3_lcd.dtsi
+++ b/arch/arm/boot/dts/sama5d3_lcd.dtsi
@@ -82,6 +82,28 @@
 							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
 							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
 							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
+							 AT91_PIOA 16 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD16 pin */
+							 AT91_PIOA 17 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDD17 pin */
+					};
+
+					pinctrl_lcd_rgb666_alt: lcd-rgb-2-alt {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
 							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD16 pin */
 							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE>;	/* LCDD17 pin */
 					};
@@ -104,6 +126,34 @@
 							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
 							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
 							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
+							 AT91_PIOA 16 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD16 pin */
+							 AT91_PIOA 17 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD17 pin */
+							 AT91_PIOA 18 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD18 pin */
+							 AT91_PIOA 19 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD19 pin */
+							 AT91_PIOA 20 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD20 pin */
+							 AT91_PIOA 21 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD21 pin */
+							 AT91_PIOA 22 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD22 pin */
+							 AT91_PIOA 23 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDD23 pin */
+					};
+
+					pinctrl_lcd_rgb888_alt: lcd-rgb-3-alt {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
 							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD16 pin */
 							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD17 pin */
 							 AT91_PIOC 12 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD18 pin */
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 08/11] ARM: AT91/dt: add alternative pin muxing for sama5d3 lcd pins
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, linux-kernel,
	Rob Herring, Bo Shen, Kumar Gala, linux-arm-kernel

Define alternative pin muxing for the LCDC pins.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d3_lcd.dtsi | 50 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d3_lcd.dtsi b/arch/arm/boot/dts/sama5d3_lcd.dtsi
index 2186b89..e7581f6 100644
--- a/arch/arm/boot/dts/sama5d3_lcd.dtsi
+++ b/arch/arm/boot/dts/sama5d3_lcd.dtsi
@@ -82,6 +82,28 @@
 							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
 							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
 							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
+							 AT91_PIOA 16 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD16 pin */
+							 AT91_PIOA 17 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDD17 pin */
+					};
+
+					pinctrl_lcd_rgb666_alt: lcd-rgb-2-alt {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
 							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD16 pin */
 							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE>;	/* LCDD17 pin */
 					};
@@ -104,6 +126,34 @@
 							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
 							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
 							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
+							 AT91_PIOA 16 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD16 pin */
+							 AT91_PIOA 17 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD17 pin */
+							 AT91_PIOA 18 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD18 pin */
+							 AT91_PIOA 19 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD19 pin */
+							 AT91_PIOA 20 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD20 pin */
+							 AT91_PIOA 21 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD21 pin */
+							 AT91_PIOA 22 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD22 pin */
+							 AT91_PIOA 23 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDD23 pin */
+					};
+
+					pinctrl_lcd_rgb888_alt: lcd-rgb-3-alt {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
 							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD16 pin */
 							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD17 pin */
 							 AT91_PIOC 12 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD18 pin */
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 08/11] ARM: AT91/dt: add alternative pin muxing for sama5d3 lcd pins
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

Define alternative pin muxing for the LCDC pins.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d3_lcd.dtsi | 50 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d3_lcd.dtsi b/arch/arm/boot/dts/sama5d3_lcd.dtsi
index 2186b89..e7581f6 100644
--- a/arch/arm/boot/dts/sama5d3_lcd.dtsi
+++ b/arch/arm/boot/dts/sama5d3_lcd.dtsi
@@ -82,6 +82,28 @@
 							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
 							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
 							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
+							 AT91_PIOA 16 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD16 pin */
+							 AT91_PIOA 17 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDD17 pin */
+					};
+
+					pinctrl_lcd_rgb666_alt: lcd-rgb-2-alt {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
 							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD16 pin */
 							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE>;	/* LCDD17 pin */
 					};
@@ -104,6 +126,34 @@
 							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
 							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
 							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
+							 AT91_PIOA 16 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD16 pin */
+							 AT91_PIOA 17 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD17 pin */
+							 AT91_PIOA 18 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD18 pin */
+							 AT91_PIOA 19 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD19 pin */
+							 AT91_PIOA 20 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD20 pin */
+							 AT91_PIOA 21 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD21 pin */
+							 AT91_PIOA 22 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD22 pin */
+							 AT91_PIOA 23 AT91_PERIPH_A AT91_PINCTRL_NONE>;	/* LCDD23 pin */
+					};
+
+					pinctrl_lcd_rgb888_alt: lcd-rgb-3-alt {
+						atmel,pins =
+							<AT91_PIOA 0 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD0 pin */
+							 AT91_PIOA 1 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD1 pin */
+							 AT91_PIOA 2 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD2 pin */
+							 AT91_PIOA 3 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD3 pin */
+							 AT91_PIOA 4 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD4 pin */
+							 AT91_PIOA 5 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD5 pin */
+							 AT91_PIOA 6 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD6 pin */
+							 AT91_PIOA 7 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD7 pin */
+							 AT91_PIOA 8 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD8 pin */
+							 AT91_PIOA 9 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD9 pin */
+							 AT91_PIOA 10 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD10 pin */
+							 AT91_PIOA 11 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD11 pin */
+							 AT91_PIOA 12 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD12 pin */
+							 AT91_PIOA 13 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD13 pin */
+							 AT91_PIOA 14 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD14 pin */
+							 AT91_PIOA 15 AT91_PERIPH_A AT91_PINCTRL_NONE	/* LCDD15 pin */
 							 AT91_PIOC 14 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD16 pin */
 							 AT91_PIOC 13 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD17 pin */
 							 AT91_PIOC 12 AT91_PERIPH_C AT91_PINCTRL_NONE	/* LCDD18 pin */
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 09/11] ARM: at91/dt: define the HLCDC node available on sama5d3 SoCs
  2014-07-22 13:11 ` Boris BREZILLON
  (?)
@ 2014-07-22 13:11   ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree, linux-kernel, linux-arm-kernel, Bo Shen,
	Boris BREZILLON

Define the HLCDC (HLCD Controller) IP available on some sama5d3 SoCs
(i.e. sama5d31, sama5d33, sama5d34 and sama5d36) in sama5d3 dtsi file.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d3_lcd.dtsi | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d3_lcd.dtsi b/arch/arm/boot/dts/sama5d3_lcd.dtsi
index e7581f6..1874cf7 100644
--- a/arch/arm/boot/dts/sama5d3_lcd.dtsi
+++ b/arch/arm/boot/dts/sama5d3_lcd.dtsi
@@ -166,6 +166,34 @@
 				};
 			};
 
+			hlcdc: hlcdc@f0030000 {
+				compatible = "atmel,sama5d3-hlcdc";
+				reg = <0xf0030000 0x2000>;
+				clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>;
+				clock-names = "periph_clk","sys_clk", "slow_clk";
+				status = "disabled";
+
+				hlcdc-display-controller {
+					compatible = "atmel,hlcdc-display-controller";
+					interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
+					#address-cells = <1>;
+					#size-cells = <0>;
+
+					port@0 {
+						#address-cells = <1>;
+						#size-cells = <0>;
+						reg = <0>;
+					};
+				};
+
+				hlcdc_pwm: hlcdc-pwm {
+					compatible = "atmel,hlcdc-pwm";
+					pinctrl-names = "default";
+					pinctrl-0 = <&pinctrl_lcd_pwm>;
+					#pwm-cells = <3>;
+				};
+			};
+
 			pmc: pmc@fffffc00 {
 				periphck {
 					lcdc_clk: lcdc_clk {
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 09/11] ARM: at91/dt: define the HLCDC node available on sama5d3 SoCs
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, linux-kernel,
	Rob Herring, Bo Shen, Kumar Gala, linux-arm-kernel

Define the HLCDC (HLCD Controller) IP available on some sama5d3 SoCs
(i.e. sama5d31, sama5d33, sama5d34 and sama5d36) in sama5d3 dtsi file.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d3_lcd.dtsi | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d3_lcd.dtsi b/arch/arm/boot/dts/sama5d3_lcd.dtsi
index e7581f6..1874cf7 100644
--- a/arch/arm/boot/dts/sama5d3_lcd.dtsi
+++ b/arch/arm/boot/dts/sama5d3_lcd.dtsi
@@ -166,6 +166,34 @@
 				};
 			};
 
+			hlcdc: hlcdc@f0030000 {
+				compatible = "atmel,sama5d3-hlcdc";
+				reg = <0xf0030000 0x2000>;
+				clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>;
+				clock-names = "periph_clk","sys_clk", "slow_clk";
+				status = "disabled";
+
+				hlcdc-display-controller {
+					compatible = "atmel,hlcdc-display-controller";
+					interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
+					#address-cells = <1>;
+					#size-cells = <0>;
+
+					port@0 {
+						#address-cells = <1>;
+						#size-cells = <0>;
+						reg = <0>;
+					};
+				};
+
+				hlcdc_pwm: hlcdc-pwm {
+					compatible = "atmel,hlcdc-pwm";
+					pinctrl-names = "default";
+					pinctrl-0 = <&pinctrl_lcd_pwm>;
+					#pwm-cells = <3>;
+				};
+			};
+
 			pmc: pmc@fffffc00 {
 				periphck {
 					lcdc_clk: lcdc_clk {
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 09/11] ARM: at91/dt: define the HLCDC node available on sama5d3 SoCs
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

Define the HLCDC (HLCD Controller) IP available on some sama5d3 SoCs
(i.e. sama5d31, sama5d33, sama5d34 and sama5d36) in sama5d3 dtsi file.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d3_lcd.dtsi | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d3_lcd.dtsi b/arch/arm/boot/dts/sama5d3_lcd.dtsi
index e7581f6..1874cf7 100644
--- a/arch/arm/boot/dts/sama5d3_lcd.dtsi
+++ b/arch/arm/boot/dts/sama5d3_lcd.dtsi
@@ -166,6 +166,34 @@
 				};
 			};
 
+			hlcdc: hlcdc at f0030000 {
+				compatible = "atmel,sama5d3-hlcdc";
+				reg = <0xf0030000 0x2000>;
+				clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>;
+				clock-names = "periph_clk","sys_clk", "slow_clk";
+				status = "disabled";
+
+				hlcdc-display-controller {
+					compatible = "atmel,hlcdc-display-controller";
+					interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
+					#address-cells = <1>;
+					#size-cells = <0>;
+
+					port at 0 {
+						#address-cells = <1>;
+						#size-cells = <0>;
+						reg = <0>;
+					};
+				};
+
+				hlcdc_pwm: hlcdc-pwm {
+					compatible = "atmel,hlcdc-pwm";
+					pinctrl-names = "default";
+					pinctrl-0 = <&pinctrl_lcd_pwm>;
+					#pwm-cells = <3>;
+				};
+			};
+
 			pmc: pmc at fffffc00 {
 				periphck {
 					lcdc_clk: lcdc_clk {
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 10/11] ARM: at91/dt: add LCD panel description to sama5d3xdm.dtsi
  2014-07-22 13:11 ` Boris BREZILLON
  (?)
@ 2014-07-22 13:11   ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree, linux-kernel, linux-arm-kernel, Bo Shen,
	Boris BREZILLON

Add LCD panel related nodes (backlight, regulators and panel) to sama5d3
Display Module dtsi.

Reference LCD pin muxing used by sama5d3xek boards.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d3xdm.dtsi | 58 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d3xdm.dtsi b/arch/arm/boot/dts/sama5d3xdm.dtsi
index 035ab72..91975eb 100644
--- a/arch/arm/boot/dts/sama5d3xdm.dtsi
+++ b/arch/arm/boot/dts/sama5d3xdm.dtsi
@@ -36,6 +36,64 @@
 					};
 				};
 			};
+
+			hlcdc: hlcdc@f0030000 {
+				hlcdc-display-controller {
+					pinctrl-names = "default";
+					pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888_alt>;
+
+					port@0 {
+						hlcdc_panel_output: endpoint@0 {
+							reg = <0>;
+							remote-endpoint = <&panel_input>;
+						};
+					};
+				};
+			};
+		};
+	};
+
+	bl_reg: backlight_regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "backlight-power-supply";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		status = "disabled";
+	};
+
+	panel_reg: panel_regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "panel-power-supply";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		status = "disabled";
+	};
+
+	backlight: backlight {
+		compatible = "pwm-backlight";
+		pwms = <&hlcdc_pwm 0 50000 0>;
+		brightness-levels = <0 4 8 16 32 64 128 255>;
+		default-brightness-level = <6>;
+		power-supply = <&bl_reg>;
+		status = "disabled";
+	};
+
+	panel: panel {
+		compatible = "foxlink,fl500wvr00-a0t", "simple-panel";
+		backlight = <&backlight>;
+		power-supply = <&panel_reg>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+
+		port@0 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			panel_input: endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&hlcdc_panel_output>;
+			};
 		};
 	};
 };
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 10/11] ARM: at91/dt: add LCD panel description to sama5d3xdm.dtsi
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, linux-kernel,
	Rob Herring, Bo Shen, Kumar Gala, linux-arm-kernel

Add LCD panel related nodes (backlight, regulators and panel) to sama5d3
Display Module dtsi.

Reference LCD pin muxing used by sama5d3xek boards.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d3xdm.dtsi | 58 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d3xdm.dtsi b/arch/arm/boot/dts/sama5d3xdm.dtsi
index 035ab72..91975eb 100644
--- a/arch/arm/boot/dts/sama5d3xdm.dtsi
+++ b/arch/arm/boot/dts/sama5d3xdm.dtsi
@@ -36,6 +36,64 @@
 					};
 				};
 			};
+
+			hlcdc: hlcdc@f0030000 {
+				hlcdc-display-controller {
+					pinctrl-names = "default";
+					pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888_alt>;
+
+					port@0 {
+						hlcdc_panel_output: endpoint@0 {
+							reg = <0>;
+							remote-endpoint = <&panel_input>;
+						};
+					};
+				};
+			};
+		};
+	};
+
+	bl_reg: backlight_regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "backlight-power-supply";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		status = "disabled";
+	};
+
+	panel_reg: panel_regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "panel-power-supply";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		status = "disabled";
+	};
+
+	backlight: backlight {
+		compatible = "pwm-backlight";
+		pwms = <&hlcdc_pwm 0 50000 0>;
+		brightness-levels = <0 4 8 16 32 64 128 255>;
+		default-brightness-level = <6>;
+		power-supply = <&bl_reg>;
+		status = "disabled";
+	};
+
+	panel: panel {
+		compatible = "foxlink,fl500wvr00-a0t", "simple-panel";
+		backlight = <&backlight>;
+		power-supply = <&panel_reg>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+
+		port@0 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			panel_input: endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&hlcdc_panel_output>;
+			};
 		};
 	};
 };
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 10/11] ARM: at91/dt: add LCD panel description to sama5d3xdm.dtsi
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

Add LCD panel related nodes (backlight, regulators and panel) to sama5d3
Display Module dtsi.

Reference LCD pin muxing used by sama5d3xek boards.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d3xdm.dtsi | 58 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d3xdm.dtsi b/arch/arm/boot/dts/sama5d3xdm.dtsi
index 035ab72..91975eb 100644
--- a/arch/arm/boot/dts/sama5d3xdm.dtsi
+++ b/arch/arm/boot/dts/sama5d3xdm.dtsi
@@ -36,6 +36,64 @@
 					};
 				};
 			};
+
+			hlcdc: hlcdc at f0030000 {
+				hlcdc-display-controller {
+					pinctrl-names = "default";
+					pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888_alt>;
+
+					port at 0 {
+						hlcdc_panel_output: endpoint at 0 {
+							reg = <0>;
+							remote-endpoint = <&panel_input>;
+						};
+					};
+				};
+			};
+		};
+	};
+
+	bl_reg: backlight_regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "backlight-power-supply";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		status = "disabled";
+	};
+
+	panel_reg: panel_regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "panel-power-supply";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		status = "disabled";
+	};
+
+	backlight: backlight {
+		compatible = "pwm-backlight";
+		pwms = <&hlcdc_pwm 0 50000 0>;
+		brightness-levels = <0 4 8 16 32 64 128 255>;
+		default-brightness-level = <6>;
+		power-supply = <&bl_reg>;
+		status = "disabled";
+	};
+
+	panel: panel {
+		compatible = "foxlink,fl500wvr00-a0t", "simple-panel";
+		backlight = <&backlight>;
+		power-supply = <&panel_reg>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+
+		port at 0 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			panel_input: endpoint at 0 {
+				reg = <0>;
+				remote-endpoint = <&hlcdc_panel_output>;
+			};
 		};
 	};
 };
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 11/11] ARM: at91/dt: enable the LCD panel on sama5d3xek boards
  2014-07-22 13:11 ` Boris BREZILLON
  (?)
@ 2014-07-22 13:11   ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree, linux-kernel, linux-arm-kernel, Bo Shen,
	Boris BREZILLON

Enable LCD related nodes.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d31ek.dts | 20 ++++++++++++++++++++
 arch/arm/boot/dts/sama5d33ek.dts | 20 ++++++++++++++++++++
 arch/arm/boot/dts/sama5d34ek.dts | 20 ++++++++++++++++++++
 arch/arm/boot/dts/sama5d36ek.dts | 20 ++++++++++++++++++++
 4 files changed, 80 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d31ek.dts b/arch/arm/boot/dts/sama5d31ek.dts
index 04eec0d..6e605fe 100644
--- a/arch/arm/boot/dts/sama5d31ek.dts
+++ b/arch/arm/boot/dts/sama5d31ek.dts
@@ -33,6 +33,10 @@
 				status = "okay";
 			};
 
+			hlcdc: hlcdc@f0030000 {
+				status = "okay";
+			};
+
 			macb1: ethernet@f802c000 {
 				status = "okay";
 			};
@@ -46,6 +50,22 @@
 		};
 	};
 
+	bl_reg: backlight_regulator {
+		status = "okay";
+	};
+
+	panel_reg: panel_regulator {
+		status = "okay";
+	};
+
+	backlight: backlight {
+		status = "okay";
+	};
+
+	panel: panel {
+		status = "okay";
+	};
+
 	sound {
 		status = "okay";
 	};
diff --git a/arch/arm/boot/dts/sama5d33ek.dts b/arch/arm/boot/dts/sama5d33ek.dts
index cbd6a3f..0400641 100644
--- a/arch/arm/boot/dts/sama5d33ek.dts
+++ b/arch/arm/boot/dts/sama5d33ek.dts
@@ -36,9 +36,29 @@
 			macb0: ethernet@f0028000 {
 				status = "okay";
 			};
+
+			hlcdc: hlcdc@f0030000 {
+				status = "okay";
+			};
 		};
 	};
 
+	bl_reg: backlight_regulator {
+		status = "okay";
+	};
+
+	panel_reg: panel_regulator {
+		status = "okay";
+	};
+
+	backlight: backlight {
+		status = "okay";
+	};
+
+	panel: panel {
+		status = "okay";
+	};
+
 	sound {
 		status = "okay";
 	};
diff --git a/arch/arm/boot/dts/sama5d34ek.dts b/arch/arm/boot/dts/sama5d34ek.dts
index 878aa16..9cf473e 100644
--- a/arch/arm/boot/dts/sama5d34ek.dts
+++ b/arch/arm/boot/dts/sama5d34ek.dts
@@ -46,6 +46,10 @@
 			macb0: ethernet@f0028000 {
 				status = "okay";
 			};
+
+			hlcdc: hlcdc@f0030000 {
+				status = "okay";
+			};
 		};
 	};
 
@@ -56,6 +60,22 @@
 		};
 	};
 
+	bl_reg: backlight_regulator {
+		status = "okay";
+	};
+
+	panel_reg: panel_regulator {
+		status = "okay";
+	};
+
+	backlight: backlight {
+		status = "okay";
+	};
+
+	panel: panel {
+		status = "okay";
+	};
+
 	sound {
 		status = "okay";
 	};
diff --git a/arch/arm/boot/dts/sama5d36ek.dts b/arch/arm/boot/dts/sama5d36ek.dts
index 59576c6..1c65741 100644
--- a/arch/arm/boot/dts/sama5d36ek.dts
+++ b/arch/arm/boot/dts/sama5d36ek.dts
@@ -41,12 +41,32 @@
 				status = "okay";
 			};
 
+			hlcdc: hlcdc@f0030000 {
+				status = "okay";
+			};
+
 			macb1: ethernet@f802c000 {
 				status = "okay";
 			};
 		};
 	};
 
+	bl_reg: backlight_regulator {
+		status = "okay";
+	};
+
+	panel_reg: panel_regulator {
+		status = "okay";
+	};
+
+	backlight: backlight {
+		status = "okay";
+	};
+
+	panel: panel {
+		status = "okay";
+	};
+
 	sound {
 		status = "okay";
 	};
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 11/11] ARM: at91/dt: enable the LCD panel on sama5d3xek boards
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Mark Rutland, devicetree, Pawel Moll, Ian Campbell, linux-kernel,
	Rob Herring, Bo Shen, Kumar Gala, linux-arm-kernel

Enable LCD related nodes.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d31ek.dts | 20 ++++++++++++++++++++
 arch/arm/boot/dts/sama5d33ek.dts | 20 ++++++++++++++++++++
 arch/arm/boot/dts/sama5d34ek.dts | 20 ++++++++++++++++++++
 arch/arm/boot/dts/sama5d36ek.dts | 20 ++++++++++++++++++++
 4 files changed, 80 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d31ek.dts b/arch/arm/boot/dts/sama5d31ek.dts
index 04eec0d..6e605fe 100644
--- a/arch/arm/boot/dts/sama5d31ek.dts
+++ b/arch/arm/boot/dts/sama5d31ek.dts
@@ -33,6 +33,10 @@
 				status = "okay";
 			};
 
+			hlcdc: hlcdc@f0030000 {
+				status = "okay";
+			};
+
 			macb1: ethernet@f802c000 {
 				status = "okay";
 			};
@@ -46,6 +50,22 @@
 		};
 	};
 
+	bl_reg: backlight_regulator {
+		status = "okay";
+	};
+
+	panel_reg: panel_regulator {
+		status = "okay";
+	};
+
+	backlight: backlight {
+		status = "okay";
+	};
+
+	panel: panel {
+		status = "okay";
+	};
+
 	sound {
 		status = "okay";
 	};
diff --git a/arch/arm/boot/dts/sama5d33ek.dts b/arch/arm/boot/dts/sama5d33ek.dts
index cbd6a3f..0400641 100644
--- a/arch/arm/boot/dts/sama5d33ek.dts
+++ b/arch/arm/boot/dts/sama5d33ek.dts
@@ -36,9 +36,29 @@
 			macb0: ethernet@f0028000 {
 				status = "okay";
 			};
+
+			hlcdc: hlcdc@f0030000 {
+				status = "okay";
+			};
 		};
 	};
 
+	bl_reg: backlight_regulator {
+		status = "okay";
+	};
+
+	panel_reg: panel_regulator {
+		status = "okay";
+	};
+
+	backlight: backlight {
+		status = "okay";
+	};
+
+	panel: panel {
+		status = "okay";
+	};
+
 	sound {
 		status = "okay";
 	};
diff --git a/arch/arm/boot/dts/sama5d34ek.dts b/arch/arm/boot/dts/sama5d34ek.dts
index 878aa16..9cf473e 100644
--- a/arch/arm/boot/dts/sama5d34ek.dts
+++ b/arch/arm/boot/dts/sama5d34ek.dts
@@ -46,6 +46,10 @@
 			macb0: ethernet@f0028000 {
 				status = "okay";
 			};
+
+			hlcdc: hlcdc@f0030000 {
+				status = "okay";
+			};
 		};
 	};
 
@@ -56,6 +60,22 @@
 		};
 	};
 
+	bl_reg: backlight_regulator {
+		status = "okay";
+	};
+
+	panel_reg: panel_regulator {
+		status = "okay";
+	};
+
+	backlight: backlight {
+		status = "okay";
+	};
+
+	panel: panel {
+		status = "okay";
+	};
+
 	sound {
 		status = "okay";
 	};
diff --git a/arch/arm/boot/dts/sama5d36ek.dts b/arch/arm/boot/dts/sama5d36ek.dts
index 59576c6..1c65741 100644
--- a/arch/arm/boot/dts/sama5d36ek.dts
+++ b/arch/arm/boot/dts/sama5d36ek.dts
@@ -41,12 +41,32 @@
 				status = "okay";
 			};
 
+			hlcdc: hlcdc@f0030000 {
+				status = "okay";
+			};
+
 			macb1: ethernet@f802c000 {
 				status = "okay";
 			};
 		};
 	};
 
+	bl_reg: backlight_regulator {
+		status = "okay";
+	};
+
+	panel_reg: panel_regulator {
+		status = "okay";
+	};
+
+	backlight: backlight {
+		status = "okay";
+	};
+
+	panel: panel {
+		status = "okay";
+	};
+
 	sound {
 		status = "okay";
 	};
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* [PATCH v4 11/11] ARM: at91/dt: enable the LCD panel on sama5d3xek boards
@ 2014-07-22 13:11   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-07-22 13:11 UTC (permalink / raw)
  To: linux-arm-kernel

Enable LCD related nodes.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 arch/arm/boot/dts/sama5d31ek.dts | 20 ++++++++++++++++++++
 arch/arm/boot/dts/sama5d33ek.dts | 20 ++++++++++++++++++++
 arch/arm/boot/dts/sama5d34ek.dts | 20 ++++++++++++++++++++
 arch/arm/boot/dts/sama5d36ek.dts | 20 ++++++++++++++++++++
 4 files changed, 80 insertions(+)

diff --git a/arch/arm/boot/dts/sama5d31ek.dts b/arch/arm/boot/dts/sama5d31ek.dts
index 04eec0d..6e605fe 100644
--- a/arch/arm/boot/dts/sama5d31ek.dts
+++ b/arch/arm/boot/dts/sama5d31ek.dts
@@ -33,6 +33,10 @@
 				status = "okay";
 			};
 
+			hlcdc: hlcdc at f0030000 {
+				status = "okay";
+			};
+
 			macb1: ethernet at f802c000 {
 				status = "okay";
 			};
@@ -46,6 +50,22 @@
 		};
 	};
 
+	bl_reg: backlight_regulator {
+		status = "okay";
+	};
+
+	panel_reg: panel_regulator {
+		status = "okay";
+	};
+
+	backlight: backlight {
+		status = "okay";
+	};
+
+	panel: panel {
+		status = "okay";
+	};
+
 	sound {
 		status = "okay";
 	};
diff --git a/arch/arm/boot/dts/sama5d33ek.dts b/arch/arm/boot/dts/sama5d33ek.dts
index cbd6a3f..0400641 100644
--- a/arch/arm/boot/dts/sama5d33ek.dts
+++ b/arch/arm/boot/dts/sama5d33ek.dts
@@ -36,9 +36,29 @@
 			macb0: ethernet at f0028000 {
 				status = "okay";
 			};
+
+			hlcdc: hlcdc at f0030000 {
+				status = "okay";
+			};
 		};
 	};
 
+	bl_reg: backlight_regulator {
+		status = "okay";
+	};
+
+	panel_reg: panel_regulator {
+		status = "okay";
+	};
+
+	backlight: backlight {
+		status = "okay";
+	};
+
+	panel: panel {
+		status = "okay";
+	};
+
 	sound {
 		status = "okay";
 	};
diff --git a/arch/arm/boot/dts/sama5d34ek.dts b/arch/arm/boot/dts/sama5d34ek.dts
index 878aa16..9cf473e 100644
--- a/arch/arm/boot/dts/sama5d34ek.dts
+++ b/arch/arm/boot/dts/sama5d34ek.dts
@@ -46,6 +46,10 @@
 			macb0: ethernet at f0028000 {
 				status = "okay";
 			};
+
+			hlcdc: hlcdc at f0030000 {
+				status = "okay";
+			};
 		};
 	};
 
@@ -56,6 +60,22 @@
 		};
 	};
 
+	bl_reg: backlight_regulator {
+		status = "okay";
+	};
+
+	panel_reg: panel_regulator {
+		status = "okay";
+	};
+
+	backlight: backlight {
+		status = "okay";
+	};
+
+	panel: panel {
+		status = "okay";
+	};
+
 	sound {
 		status = "okay";
 	};
diff --git a/arch/arm/boot/dts/sama5d36ek.dts b/arch/arm/boot/dts/sama5d36ek.dts
index 59576c6..1c65741 100644
--- a/arch/arm/boot/dts/sama5d36ek.dts
+++ b/arch/arm/boot/dts/sama5d36ek.dts
@@ -41,12 +41,32 @@
 				status = "okay";
 			};
 
+			hlcdc: hlcdc at f0030000 {
+				status = "okay";
+			};
+
 			macb1: ethernet at f802c000 {
 				status = "okay";
 			};
 		};
 	};
 
+	bl_reg: backlight_regulator {
+		status = "okay";
+	};
+
+	panel_reg: panel_regulator {
+		status = "okay";
+	};
+
+	backlight: backlight {
+		status = "okay";
+	};
+
+	panel: panel {
+		status = "okay";
+	};
+
 	sound {
 		status = "okay";
 	};
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 02/11] mfd: add documentation for atmel-hlcdc DT bindings
  2014-07-22 13:11   ` Boris BREZILLON
@ 2014-07-22 14:32     ` Varka Bhadram
  -1 siblings, 0 replies; 103+ messages in thread
From: Varka Bhadram @ 2014-07-22 14:32 UTC (permalink / raw)
  To: Boris BREZILLON, Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree, linux-kernel, linux-arm-kernel, Bo Shen


On Tuesday 22 July 2014 06:41 PM, Boris BREZILLON wrote:
> The HLCDC IP available on some Atmel SoCs (i.e. at91sam9n12, at91sam9x5
> family or sama5d3 family) exposes 2 subdevices:
> - a display controller (controlled by a DRM driver)
> - a PWM chip
>
> This patch adds documentation for atmel-hlcdc DT bindings.
>
> Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
> ---
>   .../devicetree/bindings/mfd/atmel-hlcdc.txt        | 50 ++++++++++++++++++++++
>   1 file changed, 50 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
>
> diff --git a/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
> new file mode 100644
> index 0000000..e9cc1b2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
> @@ -0,0 +1,50 @@
> +Device-Tree bindings for Atmel's HLCDC (High LCD Controller) MFD driver
> +
> +Required properties:
> + - compatible: value should be one of the following:
> +   "atmel,sama5d3-hlcdc"
> + - reg: base address and size of the HLCDC device registers.
> + - clock-names: the name of the 3 clocks requested by the HLCDC device.
> +   Should contain "periph_clk", "sys_clk" and "slow_clk".
> + - clocks: should contain the 3 clocks requested by the HLCDC device.
> +

These bindings not clearly readable. It would be readable if

Required properties:
  - compatible	: value should be one of the following:"atmel,sama5d3-hlcdc"
  - reg		: base address and size of the HLCDC device registers.
  - clock-names	: the name of the 3 clocks requested by the HLCDC device.
		  Should contain "periph_clk", "sys_clk" and "slow_clk".
  - clocks	: should contain the 3 clocks requested by the HLCDC device.

......


-- 
Regards,
Varka Bhadram


^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 02/11] mfd: add documentation for atmel-hlcdc DT bindings
@ 2014-07-22 14:32     ` Varka Bhadram
  0 siblings, 0 replies; 103+ messages in thread
From: Varka Bhadram @ 2014-07-22 14:32 UTC (permalink / raw)
  To: linux-arm-kernel


On Tuesday 22 July 2014 06:41 PM, Boris BREZILLON wrote:
> The HLCDC IP available on some Atmel SoCs (i.e. at91sam9n12, at91sam9x5
> family or sama5d3 family) exposes 2 subdevices:
> - a display controller (controlled by a DRM driver)
> - a PWM chip
>
> This patch adds documentation for atmel-hlcdc DT bindings.
>
> Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
> ---
>   .../devicetree/bindings/mfd/atmel-hlcdc.txt        | 50 ++++++++++++++++++++++
>   1 file changed, 50 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
>
> diff --git a/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
> new file mode 100644
> index 0000000..e9cc1b2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
> @@ -0,0 +1,50 @@
> +Device-Tree bindings for Atmel's HLCDC (High LCD Controller) MFD driver
> +
> +Required properties:
> + - compatible: value should be one of the following:
> +   "atmel,sama5d3-hlcdc"
> + - reg: base address and size of the HLCDC device registers.
> + - clock-names: the name of the 3 clocks requested by the HLCDC device.
> +   Should contain "periph_clk", "sys_clk" and "slow_clk".
> + - clocks: should contain the 3 clocks requested by the HLCDC device.
> +

These bindings not clearly readable. It would be readable if

Required properties:
  - compatible	: value should be one of the following:"atmel,sama5d3-hlcdc"
  - reg		: base address and size of the HLCDC device registers.
  - clock-names	: the name of the 3 clocks requested by the HLCDC device.
		  Should contain "periph_clk", "sys_clk" and "slow_clk".
  - clocks	: should contain the 3 clocks requested by the HLCDC device.

......


-- 
Regards,
Varka Bhadram

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 04/11] pwm: add DT bindings documentation for atmel-hlcdc-pwm driver
  2014-07-22 13:11   ` Boris BREZILLON
@ 2014-07-22 14:36     ` Varka Bhadram
  -1 siblings, 0 replies; 103+ messages in thread
From: Varka Bhadram @ 2014-07-22 14:36 UTC (permalink / raw)
  To: Boris BREZILLON, Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	devicetree, linux-kernel, linux-arm-kernel, Bo Shen


On Tuesday 22 July 2014 06:41 PM, Boris BREZILLON wrote:
> The HLCDC IP available in some Atmel SoCs (i.e. sam9x5i.e. at91sam9n12,
> at91sam9x5 family or sama5d3 family) provide a PWM device.
>
> The DT bindings used for this PWM device is following the default 3 cells
> bindings described in Documentation/devicetree/bindings/pwm/pwm.txt.
>
> Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
> ---
>   .../devicetree/bindings/pwm/atmel-hlcdc-pwm.txt    | 55 ++++++++++++++++++++++
>   1 file changed, 55 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
>
> diff --git a/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt b/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
> new file mode 100644
> index 0000000..86ad3e2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
> @@ -0,0 +1,55 @@
> +Device-Tree bindings for Atmel's HLCDC (High LCD Controller) PWM driver
> +
> +The Atmel HLCDC PWM is subdevice of the HLCDC MFD device.
> +See ../mfd/atmel-hlcdc.txt for more details.
> +
> +Required properties:
> + - compatible: value should be one of the following:
> +   "atmel,hlcdc-pwm"
> + - pinctr-names: the pin control state names. Should contain "default".
> + - pinctrl-0: should contain the pinctrl states described by pinctrl
> +   default.
> + - #pwm-cells: should be set to 3. This PWM chip use the default 3 cells
> +   bindings defined in Documentation/devicetree/bindings/pwm/pwm.txt.
> +   The first cell encodes the PWM id (0 is the only acceptable value here,
> +   because the chip only provide one PWM).
> +   The second cell encodes the PWM period in nanoseconds.
> +   The third cell encodes the PWM flags (the only supported flag is
> +   PWM_POLARITY_INVERTED)

It will be readable if:
Required properties:
  - compatible	: value should be one of the following: "atmel,hlcdc-pwm"
  - pinctr-names	: the pin control state names. Should contain "default".
  - pinctrl-0	: should contain the pinctrl states described by pinctrl default.
  - #pwm-cells	: should be set to 3. This PWM chip use the default 3 cells
		  bindings defined in Documentation/devicetree/bindings/pwm/pwm.txt.
		  The first cell encodes the PWM id (0 is the only acceptable value here,
		  because the chip only provide one PWM).
		  The second cell encodes the PWM period in nanoseconds.
		  The third cell encodes the PWM flags (the only supported flag is
		  PWM_POLARITY_INVERTED)
....


-- 
Regards,
Varka Bhadram


^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 04/11] pwm: add DT bindings documentation for atmel-hlcdc-pwm driver
@ 2014-07-22 14:36     ` Varka Bhadram
  0 siblings, 0 replies; 103+ messages in thread
From: Varka Bhadram @ 2014-07-22 14:36 UTC (permalink / raw)
  To: linux-arm-kernel


On Tuesday 22 July 2014 06:41 PM, Boris BREZILLON wrote:
> The HLCDC IP available in some Atmel SoCs (i.e. sam9x5i.e. at91sam9n12,
> at91sam9x5 family or sama5d3 family) provide a PWM device.
>
> The DT bindings used for this PWM device is following the default 3 cells
> bindings described in Documentation/devicetree/bindings/pwm/pwm.txt.
>
> Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
> ---
>   .../devicetree/bindings/pwm/atmel-hlcdc-pwm.txt    | 55 ++++++++++++++++++++++
>   1 file changed, 55 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
>
> diff --git a/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt b/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
> new file mode 100644
> index 0000000..86ad3e2
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
> @@ -0,0 +1,55 @@
> +Device-Tree bindings for Atmel's HLCDC (High LCD Controller) PWM driver
> +
> +The Atmel HLCDC PWM is subdevice of the HLCDC MFD device.
> +See ../mfd/atmel-hlcdc.txt for more details.
> +
> +Required properties:
> + - compatible: value should be one of the following:
> +   "atmel,hlcdc-pwm"
> + - pinctr-names: the pin control state names. Should contain "default".
> + - pinctrl-0: should contain the pinctrl states described by pinctrl
> +   default.
> + - #pwm-cells: should be set to 3. This PWM chip use the default 3 cells
> +   bindings defined in Documentation/devicetree/bindings/pwm/pwm.txt.
> +   The first cell encodes the PWM id (0 is the only acceptable value here,
> +   because the chip only provide one PWM).
> +   The second cell encodes the PWM period in nanoseconds.
> +   The third cell encodes the PWM flags (the only supported flag is
> +   PWM_POLARITY_INVERTED)

It will be readable if:
Required properties:
  - compatible	: value should be one of the following: "atmel,hlcdc-pwm"
  - pinctr-names	: the pin control state names. Should contain "default".
  - pinctrl-0	: should contain the pinctrl states described by pinctrl default.
  - #pwm-cells	: should be set to 3. This PWM chip use the default 3 cells
		  bindings defined in Documentation/devicetree/bindings/pwm/pwm.txt.
		  The first cell encodes the PWM id (0 is the only acceptable value here,
		  because the chip only provide one PWM).
		  The second cell encodes the PWM period in nanoseconds.
		  The third cell encodes the PWM flags (the only supported flag is
		  PWM_POLARITY_INVERTED)
....


-- 
Regards,
Varka Bhadram

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-07-22 13:11 ` Boris BREZILLON
  (?)
@ 2014-08-21  8:16   ` Ludovic Desroches
  -1 siblings, 0 replies; 103+ messages in thread
From: Ludovic Desroches @ 2014-08-21  8:16 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart, Mark Rutland, devicetree, Pawel Moll,
	Ian Campbell, linux-kernel, Rob Herring, Bo Shen, Kumar Gala,
	linux-arm-kernel

Hi Boris,

You can add

Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>

Only one issue but not related to your patches, you can't display
quickly the bootup logo since the panel detection takes too much
time.

Regards

Ludovic

On Tue, Jul 22, 2014 at 03:11:24PM +0200, Boris BREZILLON wrote:
> Hello,
> 
> This patch series adds support for Atmel HLCDC (HLCD Controller) available
> on some Atmel SoCs (i.e. the sama5d3 family).
> 
> The HLCDC actually provides a Display Controller and a PWM device, hence I
> decided to declare an MFD device exposing 2 subdevices: a display
> controller and a PWM chip.
> This also solves a circular dependency issue preventing HLCDC driver from
> unloading.
> The HLCDC request a drm_panel device, which request a backlight device
> (a PWM backlight), which depends on a PWM which is provided by the HLCDC
> driver (hlcdc -> panel -> backlight -> hlcdc (pwm part)).
> 
> The current implementation only supports sama5d3 SoCs but other SoCs should
> be easily ported by defining new compatible strings and adding HLCDC
> description structures for these SoCs.
> 
> The drivers supports basic CRTC functionalities, several overlays and an
> hardware cursor.
> 
> At the moment, it only supports connection to LCD panels through an RGB
> connector (defined as an LVDS connector in my implementation), though
> connection to other kind of devices (like DRM bridges) could be added later.
> 
> It also supports several RGB formats on all planes and some YUV formats on
> the HEO overlay plane.
> 
> This series depends on 2 other series currently under review: [1] and [2].
> 
> Best Regards,
> 
> Boris
> 
> [1]http://lkml.iu.edu/hypermail/linux/kernel/1407.1/04171.html
> [2]http://www.spinics.net/lists/kernel/msg1791681.html
> 
> Changes since v3:
> - rework the layer code to simplify several parts (locking and layer
>   disabling)
> - make use of the drm_flip_work infrastructure
> - rely on default HW cursor implementation using on the cursor plane
> - rework the display controller DT bindings (based on OF graph
>   representation)
> - add rotation support
> - retrive RGB bus format from drm_display_info
> - drop the dynamic pinctrl state selection
> - rework HLCDC output handling (previously specialized to interface
>   with LCD panels)
> - drop ".module = THIS_MODULE" lines
> - change display controller compatible string
> 
> Changes since v2:
> - fix coding style issues (macro indentation)
> - make use of GENMASK in several places
> - declare regmap config as a static structure
> - rework hlcdc plane update API
> - rework cursor handling to make use of the new plane update API
> - fix backporch config
> - do not use devm_regmap_init_mmio_clk to avoid extra clk_enable
>   clk disable calls when accessing registers
> - explicitely include regmap and clk headers instead of relying on
>   atmel-hlcdc.h inclusions
> - make the atmel-hlcdc driver depends on CONFIG_OF
> - separate DT bindings documentation from driver implementation
> - support several pin muxing for HLCDC pins on sama5d3 SoCs
> 
> Changes since v1:
> - replace the backlight driver by a PWM driver
> - make use of drm_panel infrastructure
> - split driver code in several subsystem: MFD, PWM and DRM
> - add support for overlays
> - add support for hardware cursor
> 
> 
> Boris BREZILLON (11):
>   mfd: add atmel-hlcdc driver
>   mfd: add documentation for atmel-hlcdc DT bindings
>   pwm: add support for atmel-hlcdc-pwm device
>   pwm: add DT bindings documentation for atmel-hlcdc-pwm driver
>   drm: add Atmel HLCDC Display Controller support
>   drm: add DT bindings documentation for atmel-hlcdc-dc driver
>   ARM: AT91/dt: split sama5d3 lcd pin definitions to match RGB mode
>     configs
>   ARM: AT91/dt: add alternative pin muxing for sama5d3 lcd pins
>   ARM: at91/dt: define the HLCDC node available on sama5d3 SoCs
>   ARM: at91/dt: add LCD panel description to sama5d3xdm.dtsi
>   ARM: at91/dt: enable the LCD panel on sama5d3xek boards
> 
>  .../devicetree/bindings/drm/atmel-hlcdc-dc.txt     |  54 ++
>  .../devicetree/bindings/mfd/atmel-hlcdc.txt        |  50 ++
>  .../devicetree/bindings/pwm/atmel-hlcdc-pwm.txt    |  55 ++
>  arch/arm/boot/dts/sama5d31ek.dts                   |  20 +
>  arch/arm/boot/dts/sama5d33ek.dts                   |  20 +
>  arch/arm/boot/dts/sama5d34ek.dts                   |  20 +
>  arch/arm/boot/dts/sama5d36ek.dts                   |  20 +
>  arch/arm/boot/dts/sama5d3_lcd.dtsi                 | 205 +++++-
>  arch/arm/boot/dts/sama5d3xdm.dtsi                  |  58 ++
>  drivers/gpu/drm/Kconfig                            |   2 +
>  drivers/gpu/drm/Makefile                           |   1 +
>  drivers/gpu/drm/atmel-hlcdc/Kconfig                |  11 +
>  drivers/gpu/drm/atmel-hlcdc/Makefile               |   7 +
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c     | 286 ++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c       | 488 +++++++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h       | 224 ++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c    | 635 ++++++++++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h    | 396 ++++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c   | 478 ++++++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c    | 804 +++++++++++++++++++++
>  drivers/mfd/Kconfig                                |  12 +
>  drivers/mfd/Makefile                               |   1 +
>  drivers/mfd/atmel-hlcdc.c                          | 118 +++
>  drivers/pwm/Kconfig                                |   9 +
>  drivers/pwm/Makefile                               |   1 +
>  drivers/pwm/pwm-atmel-hlcdc.c                      | 229 ++++++
>  include/linux/mfd/atmel-hlcdc.h                    |  78 ++
>  27 files changed, 4251 insertions(+), 31 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt
>  create mode 100644 Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
>  create mode 100644 Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/Kconfig
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/Makefile
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
>  create mode 100644 drivers/mfd/atmel-hlcdc.c
>  create mode 100644 drivers/pwm/pwm-atmel-hlcdc.c
>  create mode 100644 include/linux/mfd/atmel-hlcdc.h
> 
> -- 
> 1.8.3.2
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21  8:16   ` Ludovic Desroches
  0 siblings, 0 replies; 103+ messages in thread
From: Ludovic Desroches @ 2014-08-21  8:16 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	Thierry Reding, linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark,
	Laurent Pinchart, Mark Rutland, devicetree, Pawel Moll,
	Ian Campbell, linux-kernel, Rob Herring, Bo Shen, Kumar Gala,
	linux-arm-kernel

Hi Boris,

You can add

Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>

Only one issue but not related to your patches, you can't display
quickly the bootup logo since the panel detection takes too much
time.

Regards

Ludovic

On Tue, Jul 22, 2014 at 03:11:24PM +0200, Boris BREZILLON wrote:
> Hello,
> 
> This patch series adds support for Atmel HLCDC (HLCD Controller) available
> on some Atmel SoCs (i.e. the sama5d3 family).
> 
> The HLCDC actually provides a Display Controller and a PWM device, hence I
> decided to declare an MFD device exposing 2 subdevices: a display
> controller and a PWM chip.
> This also solves a circular dependency issue preventing HLCDC driver from
> unloading.
> The HLCDC request a drm_panel device, which request a backlight device
> (a PWM backlight), which depends on a PWM which is provided by the HLCDC
> driver (hlcdc -> panel -> backlight -> hlcdc (pwm part)).
> 
> The current implementation only supports sama5d3 SoCs but other SoCs should
> be easily ported by defining new compatible strings and adding HLCDC
> description structures for these SoCs.
> 
> The drivers supports basic CRTC functionalities, several overlays and an
> hardware cursor.
> 
> At the moment, it only supports connection to LCD panels through an RGB
> connector (defined as an LVDS connector in my implementation), though
> connection to other kind of devices (like DRM bridges) could be added later.
> 
> It also supports several RGB formats on all planes and some YUV formats on
> the HEO overlay plane.
> 
> This series depends on 2 other series currently under review: [1] and [2].
> 
> Best Regards,
> 
> Boris
> 
> [1]http://lkml.iu.edu/hypermail/linux/kernel/1407.1/04171.html
> [2]http://www.spinics.net/lists/kernel/msg1791681.html
> 
> Changes since v3:
> - rework the layer code to simplify several parts (locking and layer
>   disabling)
> - make use of the drm_flip_work infrastructure
> - rely on default HW cursor implementation using on the cursor plane
> - rework the display controller DT bindings (based on OF graph
>   representation)
> - add rotation support
> - retrive RGB bus format from drm_display_info
> - drop the dynamic pinctrl state selection
> - rework HLCDC output handling (previously specialized to interface
>   with LCD panels)
> - drop ".module = THIS_MODULE" lines
> - change display controller compatible string
> 
> Changes since v2:
> - fix coding style issues (macro indentation)
> - make use of GENMASK in several places
> - declare regmap config as a static structure
> - rework hlcdc plane update API
> - rework cursor handling to make use of the new plane update API
> - fix backporch config
> - do not use devm_regmap_init_mmio_clk to avoid extra clk_enable
>   clk disable calls when accessing registers
> - explicitely include regmap and clk headers instead of relying on
>   atmel-hlcdc.h inclusions
> - make the atmel-hlcdc driver depends on CONFIG_OF
> - separate DT bindings documentation from driver implementation
> - support several pin muxing for HLCDC pins on sama5d3 SoCs
> 
> Changes since v1:
> - replace the backlight driver by a PWM driver
> - make use of drm_panel infrastructure
> - split driver code in several subsystem: MFD, PWM and DRM
> - add support for overlays
> - add support for hardware cursor
> 
> 
> Boris BREZILLON (11):
>   mfd: add atmel-hlcdc driver
>   mfd: add documentation for atmel-hlcdc DT bindings
>   pwm: add support for atmel-hlcdc-pwm device
>   pwm: add DT bindings documentation for atmel-hlcdc-pwm driver
>   drm: add Atmel HLCDC Display Controller support
>   drm: add DT bindings documentation for atmel-hlcdc-dc driver
>   ARM: AT91/dt: split sama5d3 lcd pin definitions to match RGB mode
>     configs
>   ARM: AT91/dt: add alternative pin muxing for sama5d3 lcd pins
>   ARM: at91/dt: define the HLCDC node available on sama5d3 SoCs
>   ARM: at91/dt: add LCD panel description to sama5d3xdm.dtsi
>   ARM: at91/dt: enable the LCD panel on sama5d3xek boards
> 
>  .../devicetree/bindings/drm/atmel-hlcdc-dc.txt     |  54 ++
>  .../devicetree/bindings/mfd/atmel-hlcdc.txt        |  50 ++
>  .../devicetree/bindings/pwm/atmel-hlcdc-pwm.txt    |  55 ++
>  arch/arm/boot/dts/sama5d31ek.dts                   |  20 +
>  arch/arm/boot/dts/sama5d33ek.dts                   |  20 +
>  arch/arm/boot/dts/sama5d34ek.dts                   |  20 +
>  arch/arm/boot/dts/sama5d36ek.dts                   |  20 +
>  arch/arm/boot/dts/sama5d3_lcd.dtsi                 | 205 +++++-
>  arch/arm/boot/dts/sama5d3xdm.dtsi                  |  58 ++
>  drivers/gpu/drm/Kconfig                            |   2 +
>  drivers/gpu/drm/Makefile                           |   1 +
>  drivers/gpu/drm/atmel-hlcdc/Kconfig                |  11 +
>  drivers/gpu/drm/atmel-hlcdc/Makefile               |   7 +
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c     | 286 ++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c       | 488 +++++++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h       | 224 ++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c    | 635 ++++++++++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h    | 396 ++++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c   | 478 ++++++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c    | 804 +++++++++++++++++++++
>  drivers/mfd/Kconfig                                |  12 +
>  drivers/mfd/Makefile                               |   1 +
>  drivers/mfd/atmel-hlcdc.c                          | 118 +++
>  drivers/pwm/Kconfig                                |   9 +
>  drivers/pwm/Makefile                               |   1 +
>  drivers/pwm/pwm-atmel-hlcdc.c                      | 229 ++++++
>  include/linux/mfd/atmel-hlcdc.h                    |  78 ++
>  27 files changed, 4251 insertions(+), 31 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt
>  create mode 100644 Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
>  create mode 100644 Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/Kconfig
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/Makefile
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
>  create mode 100644 drivers/mfd/atmel-hlcdc.c
>  create mode 100644 drivers/pwm/pwm-atmel-hlcdc.c
>  create mode 100644 include/linux/mfd/atmel-hlcdc.h
> 
> -- 
> 1.8.3.2
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21  8:16   ` Ludovic Desroches
  0 siblings, 0 replies; 103+ messages in thread
From: Ludovic Desroches @ 2014-08-21  8:16 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Boris,

You can add

Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>

Only one issue but not related to your patches, you can't display
quickly the bootup logo since the panel detection takes too much
time.

Regards

Ludovic

On Tue, Jul 22, 2014 at 03:11:24PM +0200, Boris BREZILLON wrote:
> Hello,
> 
> This patch series adds support for Atmel HLCDC (HLCD Controller) available
> on some Atmel SoCs (i.e. the sama5d3 family).
> 
> The HLCDC actually provides a Display Controller and a PWM device, hence I
> decided to declare an MFD device exposing 2 subdevices: a display
> controller and a PWM chip.
> This also solves a circular dependency issue preventing HLCDC driver from
> unloading.
> The HLCDC request a drm_panel device, which request a backlight device
> (a PWM backlight), which depends on a PWM which is provided by the HLCDC
> driver (hlcdc -> panel -> backlight -> hlcdc (pwm part)).
> 
> The current implementation only supports sama5d3 SoCs but other SoCs should
> be easily ported by defining new compatible strings and adding HLCDC
> description structures for these SoCs.
> 
> The drivers supports basic CRTC functionalities, several overlays and an
> hardware cursor.
> 
> At the moment, it only supports connection to LCD panels through an RGB
> connector (defined as an LVDS connector in my implementation), though
> connection to other kind of devices (like DRM bridges) could be added later.
> 
> It also supports several RGB formats on all planes and some YUV formats on
> the HEO overlay plane.
> 
> This series depends on 2 other series currently under review: [1] and [2].
> 
> Best Regards,
> 
> Boris
> 
> [1]http://lkml.iu.edu/hypermail/linux/kernel/1407.1/04171.html
> [2]http://www.spinics.net/lists/kernel/msg1791681.html
> 
> Changes since v3:
> - rework the layer code to simplify several parts (locking and layer
>   disabling)
> - make use of the drm_flip_work infrastructure
> - rely on default HW cursor implementation using on the cursor plane
> - rework the display controller DT bindings (based on OF graph
>   representation)
> - add rotation support
> - retrive RGB bus format from drm_display_info
> - drop the dynamic pinctrl state selection
> - rework HLCDC output handling (previously specialized to interface
>   with LCD panels)
> - drop ".module = THIS_MODULE" lines
> - change display controller compatible string
> 
> Changes since v2:
> - fix coding style issues (macro indentation)
> - make use of GENMASK in several places
> - declare regmap config as a static structure
> - rework hlcdc plane update API
> - rework cursor handling to make use of the new plane update API
> - fix backporch config
> - do not use devm_regmap_init_mmio_clk to avoid extra clk_enable
>   clk disable calls when accessing registers
> - explicitely include regmap and clk headers instead of relying on
>   atmel-hlcdc.h inclusions
> - make the atmel-hlcdc driver depends on CONFIG_OF
> - separate DT bindings documentation from driver implementation
> - support several pin muxing for HLCDC pins on sama5d3 SoCs
> 
> Changes since v1:
> - replace the backlight driver by a PWM driver
> - make use of drm_panel infrastructure
> - split driver code in several subsystem: MFD, PWM and DRM
> - add support for overlays
> - add support for hardware cursor
> 
> 
> Boris BREZILLON (11):
>   mfd: add atmel-hlcdc driver
>   mfd: add documentation for atmel-hlcdc DT bindings
>   pwm: add support for atmel-hlcdc-pwm device
>   pwm: add DT bindings documentation for atmel-hlcdc-pwm driver
>   drm: add Atmel HLCDC Display Controller support
>   drm: add DT bindings documentation for atmel-hlcdc-dc driver
>   ARM: AT91/dt: split sama5d3 lcd pin definitions to match RGB mode
>     configs
>   ARM: AT91/dt: add alternative pin muxing for sama5d3 lcd pins
>   ARM: at91/dt: define the HLCDC node available on sama5d3 SoCs
>   ARM: at91/dt: add LCD panel description to sama5d3xdm.dtsi
>   ARM: at91/dt: enable the LCD panel on sama5d3xek boards
> 
>  .../devicetree/bindings/drm/atmel-hlcdc-dc.txt     |  54 ++
>  .../devicetree/bindings/mfd/atmel-hlcdc.txt        |  50 ++
>  .../devicetree/bindings/pwm/atmel-hlcdc-pwm.txt    |  55 ++
>  arch/arm/boot/dts/sama5d31ek.dts                   |  20 +
>  arch/arm/boot/dts/sama5d33ek.dts                   |  20 +
>  arch/arm/boot/dts/sama5d34ek.dts                   |  20 +
>  arch/arm/boot/dts/sama5d36ek.dts                   |  20 +
>  arch/arm/boot/dts/sama5d3_lcd.dtsi                 | 205 +++++-
>  arch/arm/boot/dts/sama5d3xdm.dtsi                  |  58 ++
>  drivers/gpu/drm/Kconfig                            |   2 +
>  drivers/gpu/drm/Makefile                           |   1 +
>  drivers/gpu/drm/atmel-hlcdc/Kconfig                |  11 +
>  drivers/gpu/drm/atmel-hlcdc/Makefile               |   7 +
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c     | 286 ++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c       | 488 +++++++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h       | 224 ++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c    | 635 ++++++++++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h    | 396 ++++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c   | 478 ++++++++++++
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c    | 804 +++++++++++++++++++++
>  drivers/mfd/Kconfig                                |  12 +
>  drivers/mfd/Makefile                               |   1 +
>  drivers/mfd/atmel-hlcdc.c                          | 118 +++
>  drivers/pwm/Kconfig                                |   9 +
>  drivers/pwm/Makefile                               |   1 +
>  drivers/pwm/pwm-atmel-hlcdc.c                      | 229 ++++++
>  include/linux/mfd/atmel-hlcdc.h                    |  78 ++
>  27 files changed, 4251 insertions(+), 31 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/drm/atmel-hlcdc-dc.txt
>  create mode 100644 Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt
>  create mode 100644 Documentation/devicetree/bindings/pwm/atmel-hlcdc-pwm.txt
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/Kconfig
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/Makefile
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.c
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_layer.h
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
>  create mode 100644 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
>  create mode 100644 drivers/mfd/atmel-hlcdc.c
>  create mode 100644 drivers/pwm/pwm-atmel-hlcdc.c
>  create mode 100644 include/linux/mfd/atmel-hlcdc.h
> 
> -- 
> 1.8.3.2
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21  8:16   ` Ludovic Desroches
  (?)
@ 2014-08-21  8:37     ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21  8:37 UTC (permalink / raw)
  To: Ludovic Desroches, Thierry Reding
  Cc: Nicolas Ferre, Jean-Christophe Plagniol-Villard,
	Alexandre Belloni, Andrew Victor, David Airlie, dri-devel,
	linux-pwm, Samuel Ortiz, Lee Jones, Rob Clark, Laurent Pinchart,
	Mark Rutland, devicetree, Pawel Moll, Ian Campbell, linux-kernel,
	Rob Herring, Bo Shen, Kumar Gala, linux-arm-kernel

Hi Ludovic,

On Thu, 21 Aug 2014 10:16:19 +0200
Ludovic Desroches <ludovic.desroches@atmel.com> wrote:

> Hi Boris,
> 
> You can add
> 
> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>

Thanks for testing this driver.

> 
> Only one issue but not related to your patches, you can't display
> quickly the bootup logo since the panel detection takes too much
> time.

Yes, actually this is related to the device probe order: the
hlcdc-display-controller device is probed before the simple-panel, thus
nothing is detected on the RGB connector (I use of_drm_find_panel to
check for panel availability) when the display controller is
instantiated. I rely on the default polling infrastructure provided by
the DRM/KMS framework which polls for a new connector every 10s, and
this is far more than you kernel boot time.

Do anyone see a solution to reduce this delay (without changing the
polling interval). I thought we could add a notifier infrastructure to
the DRM panel framework, but I'm not sure this is how you want things
done...

Best Regards,

Boris

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21  8:37     ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21  8:37 UTC (permalink / raw)
  To: Ludovic Desroches, Thierry Reding
  Cc: Mark Rutland, linux-pwm, Samuel Ortiz, Pawel Moll, devicetree,
	Lee Jones, Ian Campbell, Nicolas Ferre, linux-kernel, dri-devel,
	Rob Herring, Alexandre Belloni, Laurent Pinchart, Bo Shen,
	Kumar Gala, Jean-Christophe Plagniol-Villard, Andrew Victor,
	linux-arm-kernel

Hi Ludovic,

On Thu, 21 Aug 2014 10:16:19 +0200
Ludovic Desroches <ludovic.desroches@atmel.com> wrote:

> Hi Boris,
> 
> You can add
> 
> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>

Thanks for testing this driver.

> 
> Only one issue but not related to your patches, you can't display
> quickly the bootup logo since the panel detection takes too much
> time.

Yes, actually this is related to the device probe order: the
hlcdc-display-controller device is probed before the simple-panel, thus
nothing is detected on the RGB connector (I use of_drm_find_panel to
check for panel availability) when the display controller is
instantiated. I rely on the default polling infrastructure provided by
the DRM/KMS framework which polls for a new connector every 10s, and
this is far more than you kernel boot time.

Do anyone see a solution to reduce this delay (without changing the
polling interval). I thought we could add a notifier infrastructure to
the DRM panel framework, but I'm not sure this is how you want things
done...

Best Regards,

Boris

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21  8:37     ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21  8:37 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Ludovic,

On Thu, 21 Aug 2014 10:16:19 +0200
Ludovic Desroches <ludovic.desroches@atmel.com> wrote:

> Hi Boris,
> 
> You can add
> 
> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>

Thanks for testing this driver.

> 
> Only one issue but not related to your patches, you can't display
> quickly the bootup logo since the panel detection takes too much
> time.

Yes, actually this is related to the device probe order: the
hlcdc-display-controller device is probed before the simple-panel, thus
nothing is detected on the RGB connector (I use of_drm_find_panel to
check for panel availability) when the display controller is
instantiated. I rely on the default polling infrastructure provided by
the DRM/KMS framework which polls for a new connector every 10s, and
this is far more than you kernel boot time.

Do anyone see a solution to reduce this delay (without changing the
polling interval). I thought we could add a notifier infrastructure to
the DRM panel framework, but I'm not sure this is how you want things
done...

Best Regards,

Boris

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21  8:37     ` Boris BREZILLON
  (?)
@ 2014-08-21  9:04       ` Thierry Reding
  -1 siblings, 0 replies; 103+ messages in thread
From: Thierry Reding @ 2014-08-21  9:04 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Laurent Pinchart, Mark Rutland, devicetree,
	Pawel Moll, Ian Campbell, linux-kernel, Rob Herring, Bo Shen,
	Kumar Gala, linux-arm-kernel

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

On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> Hi Ludovic,
> 
> On Thu, 21 Aug 2014 10:16:19 +0200
> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> 
> > Hi Boris,
> > 
> > You can add
> > 
> > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> 
> Thanks for testing this driver.
> 
> > 
> > Only one issue but not related to your patches, you can't display
> > quickly the bootup logo since the panel detection takes too much
> > time.
> 
> Yes, actually this is related to the device probe order: the
> hlcdc-display-controller device is probed before the simple-panel, thus
> nothing is detected on the RGB connector (I use of_drm_find_panel to
> check for panel availability) when the display controller is
> instantiated. I rely on the default polling infrastructure provided by
> the DRM/KMS framework which polls for a new connector every 10s, and
> this is far more than you kernel boot time.
> 
> Do anyone see a solution to reduce this delay (without changing the
> polling interval). I thought we could add a notifier infrastructure to
> the DRM panel framework, but I'm not sure this is how you want things
> done...

Other drivers return -EPROBE_DEFER when a panel hasn't been registered
yet. This will automatically take care of ordering things in a way that
DRM/KMS will only be initialized after the panel has been probed. That
will still cause some delay before everything gets set up, but hopefully
less than what you're seeing now. There's also another thread where this
is being discussed because deferred probing is causing "unacceptable"
delays as well.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21  9:04       ` Thierry Reding
  0 siblings, 0 replies; 103+ messages in thread
From: Thierry Reding @ 2014-08-21  9:04 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Mark Rutland, linux-pwm, Samuel Ortiz, Pawel Moll, devicetree,
	Lee Jones, Ian Campbell, Nicolas Ferre, linux-kernel, dri-devel,
	Rob Herring, Ludovic Desroches, Alexandre Belloni,
	Laurent Pinchart, Bo Shen, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Andrew Victor,
	linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 1665 bytes --]

On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> Hi Ludovic,
> 
> On Thu, 21 Aug 2014 10:16:19 +0200
> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> 
> > Hi Boris,
> > 
> > You can add
> > 
> > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> 
> Thanks for testing this driver.
> 
> > 
> > Only one issue but not related to your patches, you can't display
> > quickly the bootup logo since the panel detection takes too much
> > time.
> 
> Yes, actually this is related to the device probe order: the
> hlcdc-display-controller device is probed before the simple-panel, thus
> nothing is detected on the RGB connector (I use of_drm_find_panel to
> check for panel availability) when the display controller is
> instantiated. I rely on the default polling infrastructure provided by
> the DRM/KMS framework which polls for a new connector every 10s, and
> this is far more than you kernel boot time.
> 
> Do anyone see a solution to reduce this delay (without changing the
> polling interval). I thought we could add a notifier infrastructure to
> the DRM panel framework, but I'm not sure this is how you want things
> done...

Other drivers return -EPROBE_DEFER when a panel hasn't been registered
yet. This will automatically take care of ordering things in a way that
DRM/KMS will only be initialized after the panel has been probed. That
will still cause some delay before everything gets set up, but hopefully
less than what you're seeing now. There's also another thread where this
is being discussed because deferred probing is causing "unacceptable"
delays as well.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21  9:04       ` Thierry Reding
  0 siblings, 0 replies; 103+ messages in thread
From: Thierry Reding @ 2014-08-21  9:04 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> Hi Ludovic,
> 
> On Thu, 21 Aug 2014 10:16:19 +0200
> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> 
> > Hi Boris,
> > 
> > You can add
> > 
> > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> 
> Thanks for testing this driver.
> 
> > 
> > Only one issue but not related to your patches, you can't display
> > quickly the bootup logo since the panel detection takes too much
> > time.
> 
> Yes, actually this is related to the device probe order: the
> hlcdc-display-controller device is probed before the simple-panel, thus
> nothing is detected on the RGB connector (I use of_drm_find_panel to
> check for panel availability) when the display controller is
> instantiated. I rely on the default polling infrastructure provided by
> the DRM/KMS framework which polls for a new connector every 10s, and
> this is far more than you kernel boot time.
> 
> Do anyone see a solution to reduce this delay (without changing the
> polling interval). I thought we could add a notifier infrastructure to
> the DRM panel framework, but I'm not sure this is how you want things
> done...

Other drivers return -EPROBE_DEFER when a panel hasn't been registered
yet. This will automatically take care of ordering things in a way that
DRM/KMS will only be initialized after the panel has been probed. That
will still cause some delay before everything gets set up, but hopefully
less than what you're seeing now. There's also another thread where this
is being discussed because deferred probing is causing "unacceptable"
delays as well.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140821/5a32aeba/attachment.sig>

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21  9:04       ` Thierry Reding
  (?)
@ 2014-08-21  9:41         ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21  9:41 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Laurent Pinchart, Mark Rutland, devicetree,
	Pawel Moll, Ian Campbell, linux-kernel, Rob Herring, Bo Shen,
	Kumar Gala, linux-arm-kernel

On Thu, 21 Aug 2014 11:04:07 +0200
Thierry Reding <thierry.reding@gmail.com> wrote:

> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > Hi Ludovic,
> > 
> > On Thu, 21 Aug 2014 10:16:19 +0200
> > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > 
> > > Hi Boris,
> > > 
> > > You can add
> > > 
> > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > 
> > Thanks for testing this driver.
> > 
> > > 
> > > Only one issue but not related to your patches, you can't display
> > > quickly the bootup logo since the panel detection takes too much
> > > time.
> > 
> > Yes, actually this is related to the device probe order: the
> > hlcdc-display-controller device is probed before the simple-panel, thus
> > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > check for panel availability) when the display controller is
> > instantiated. I rely on the default polling infrastructure provided by
> > the DRM/KMS framework which polls for a new connector every 10s, and
> > this is far more than you kernel boot time.
> > 
> > Do anyone see a solution to reduce this delay (without changing the
> > polling interval). I thought we could add a notifier infrastructure to
> > the DRM panel framework, but I'm not sure this is how you want things
> > done...
> 
> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> yet. This will automatically take care of ordering things in a way that
> DRM/KMS will only be initialized after the panel has been probed.

Actually I'd like to avoid doing this with a deferred probe, because,
AFAIU, the remote endpoint is not tightly linked with the display
controller driver (I mean the display controller can still be
initialized without having a display connected on it).
Moreover the atmel dev kit I'm using has an HDMI bridge connected on
the same RGB connector and I'd like to use it in a near future.
Returning -EPROBE_DEFER in case of several devices connected on the
same connector implies that I'll have to wait for all the remote
end-points to be available before my display controller could be
instantiated.

While this could be acceptable when all drivers are statically linked
in the kernel, it might be problematic when you're using modules,
meaning that you won't be able to display anything on your LCD panel
until your HDMI bridge module has been loaded.

> That
> will still cause some delay before everything gets set up, but hopefully
> less than what you're seeing now. There's also another thread where this
> is being discussed because deferred probing is causing "unacceptable"
> delays as well.

Could you point this thread out to me please ?

Best Regards,

Boris

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21  9:41         ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21  9:41 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Mark Rutland, linux-pwm, Samuel Ortiz, Pawel Moll, devicetree,
	Lee Jones, Ian Campbell, Nicolas Ferre, linux-kernel, dri-devel,
	Rob Herring, Ludovic Desroches, Alexandre Belloni,
	Laurent Pinchart, Bo Shen, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Andrew Victor,
	linux-arm-kernel

On Thu, 21 Aug 2014 11:04:07 +0200
Thierry Reding <thierry.reding@gmail.com> wrote:

> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > Hi Ludovic,
> > 
> > On Thu, 21 Aug 2014 10:16:19 +0200
> > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > 
> > > Hi Boris,
> > > 
> > > You can add
> > > 
> > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > 
> > Thanks for testing this driver.
> > 
> > > 
> > > Only one issue but not related to your patches, you can't display
> > > quickly the bootup logo since the panel detection takes too much
> > > time.
> > 
> > Yes, actually this is related to the device probe order: the
> > hlcdc-display-controller device is probed before the simple-panel, thus
> > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > check for panel availability) when the display controller is
> > instantiated. I rely on the default polling infrastructure provided by
> > the DRM/KMS framework which polls for a new connector every 10s, and
> > this is far more than you kernel boot time.
> > 
> > Do anyone see a solution to reduce this delay (without changing the
> > polling interval). I thought we could add a notifier infrastructure to
> > the DRM panel framework, but I'm not sure this is how you want things
> > done...
> 
> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> yet. This will automatically take care of ordering things in a way that
> DRM/KMS will only be initialized after the panel has been probed.

Actually I'd like to avoid doing this with a deferred probe, because,
AFAIU, the remote endpoint is not tightly linked with the display
controller driver (I mean the display controller can still be
initialized without having a display connected on it).
Moreover the atmel dev kit I'm using has an HDMI bridge connected on
the same RGB connector and I'd like to use it in a near future.
Returning -EPROBE_DEFER in case of several devices connected on the
same connector implies that I'll have to wait for all the remote
end-points to be available before my display controller could be
instantiated.

While this could be acceptable when all drivers are statically linked
in the kernel, it might be problematic when you're using modules,
meaning that you won't be able to display anything on your LCD panel
until your HDMI bridge module has been loaded.

> That
> will still cause some delay before everything gets set up, but hopefully
> less than what you're seeing now. There's also another thread where this
> is being discussed because deferred probing is causing "unacceptable"
> delays as well.

Could you point this thread out to me please ?

Best Regards,

Boris

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21  9:41         ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21  9:41 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 21 Aug 2014 11:04:07 +0200
Thierry Reding <thierry.reding@gmail.com> wrote:

> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > Hi Ludovic,
> > 
> > On Thu, 21 Aug 2014 10:16:19 +0200
> > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > 
> > > Hi Boris,
> > > 
> > > You can add
> > > 
> > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > 
> > Thanks for testing this driver.
> > 
> > > 
> > > Only one issue but not related to your patches, you can't display
> > > quickly the bootup logo since the panel detection takes too much
> > > time.
> > 
> > Yes, actually this is related to the device probe order: the
> > hlcdc-display-controller device is probed before the simple-panel, thus
> > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > check for panel availability) when the display controller is
> > instantiated. I rely on the default polling infrastructure provided by
> > the DRM/KMS framework which polls for a new connector every 10s, and
> > this is far more than you kernel boot time.
> > 
> > Do anyone see a solution to reduce this delay (without changing the
> > polling interval). I thought we could add a notifier infrastructure to
> > the DRM panel framework, but I'm not sure this is how you want things
> > done...
> 
> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> yet. This will automatically take care of ordering things in a way that
> DRM/KMS will only be initialized after the panel has been probed.

Actually I'd like to avoid doing this with a deferred probe, because,
AFAIU, the remote endpoint is not tightly linked with the display
controller driver (I mean the display controller can still be
initialized without having a display connected on it).
Moreover the atmel dev kit I'm using has an HDMI bridge connected on
the same RGB connector and I'd like to use it in a near future.
Returning -EPROBE_DEFER in case of several devices connected on the
same connector implies that I'll have to wait for all the remote
end-points to be available before my display controller could be
instantiated.

While this could be acceptable when all drivers are statically linked
in the kernel, it might be problematic when you're using modules,
meaning that you won't be able to display anything on your LCD panel
until your HDMI bridge module has been loaded.

> That
> will still cause some delay before everything gets set up, but hopefully
> less than what you're seeing now. There's also another thread where this
> is being discussed because deferred probing is causing "unacceptable"
> delays as well.

Could you point this thread out to me please ?

Best Regards,

Boris

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21  9:41         ` Boris BREZILLON
  (?)
@ 2014-08-21  9:49           ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21  9:49 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Thierry Reding, Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Laurent Pinchart, Mark Rutland, devicetree,
	Pawel Moll, Ian Campbell, linux-kernel, Rob Herring, Bo Shen,
	Kumar Gala, linux-arm-kernel

On Thu, 21 Aug 2014 11:41:59 +0200
Boris BREZILLON <boris.brezillon@free-electrons.com> wrote:

> On Thu, 21 Aug 2014 11:04:07 +0200
> Thierry Reding <thierry.reding@gmail.com> wrote:
> 
> > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > Hi Ludovic,
> > > 
> > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > 
> > > > Hi Boris,
> > > > 
> > > > You can add
> > > > 
> > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > 
> > > Thanks for testing this driver.
> > > 
> > > > 
> > > > Only one issue but not related to your patches, you can't display
> > > > quickly the bootup logo since the panel detection takes too much
> > > > time.
> > > 
> > > Yes, actually this is related to the device probe order: the
> > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > check for panel availability) when the display controller is
> > > instantiated. I rely on the default polling infrastructure provided by
> > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > this is far more than you kernel boot time.
> > > 
> > > Do anyone see a solution to reduce this delay (without changing the
> > > polling interval). I thought we could add a notifier infrastructure to
> > > the DRM panel framework, but I'm not sure this is how you want things
> > > done...
> > 
> > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > yet. This will automatically take care of ordering things in a way that
> > DRM/KMS will only be initialized after the panel has been probed.
> 
> Actually I'd like to avoid doing this with a deferred probe, because,
> AFAIU, the remote endpoint is not tightly linked with the display
> controller driver (I mean the display controller can still be
> initialized without having a display connected on it).
> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> the same RGB connector and I'd like to use it in a near future.
> Returning -EPROBE_DEFER in case of several devices connected on the
> same connector implies that I'll have to wait for all the remote
> end-points to be available before my display controller could be
> instantiated.
> 
> While this could be acceptable when all drivers are statically linked
> in the kernel, it might be problematic when you're using modules,
> meaning that you won't be able to display anything on your LCD panel
> until your HDMI bridge module has been loaded.
> 
> > That
> > will still cause some delay before everything gets set up, but hopefully
> > less than what you're seeing now. There's also another thread where this
> > is being discussed because deferred probing is causing "unacceptable"
> > delays as well.
> 
> Could you point this thread out to me please ?

Sorry, I didn't check my emails before asking this :-).

> 
> Best Regards,
> 
> Boris
> 



-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21  9:49           ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21  9:49 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Mark Rutland, devicetree, Nicolas Ferre, dri-devel,
	Alexandre Belloni, Laurent Pinchart, Bo Shen, Lee Jones,
	Samuel Ortiz, Jean-Christophe Plagniol-Villard, linux-pwm,
	Pawel Moll, Ian Campbell, Rob Herring, Andrew Victor,
	linux-arm-kernel, linux-kernel, Ludovic Desroches, Kumar Gala

On Thu, 21 Aug 2014 11:41:59 +0200
Boris BREZILLON <boris.brezillon@free-electrons.com> wrote:

> On Thu, 21 Aug 2014 11:04:07 +0200
> Thierry Reding <thierry.reding@gmail.com> wrote:
> 
> > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > Hi Ludovic,
> > > 
> > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > 
> > > > Hi Boris,
> > > > 
> > > > You can add
> > > > 
> > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > 
> > > Thanks for testing this driver.
> > > 
> > > > 
> > > > Only one issue but not related to your patches, you can't display
> > > > quickly the bootup logo since the panel detection takes too much
> > > > time.
> > > 
> > > Yes, actually this is related to the device probe order: the
> > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > check for panel availability) when the display controller is
> > > instantiated. I rely on the default polling infrastructure provided by
> > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > this is far more than you kernel boot time.
> > > 
> > > Do anyone see a solution to reduce this delay (without changing the
> > > polling interval). I thought we could add a notifier infrastructure to
> > > the DRM panel framework, but I'm not sure this is how you want things
> > > done...
> > 
> > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > yet. This will automatically take care of ordering things in a way that
> > DRM/KMS will only be initialized after the panel has been probed.
> 
> Actually I'd like to avoid doing this with a deferred probe, because,
> AFAIU, the remote endpoint is not tightly linked with the display
> controller driver (I mean the display controller can still be
> initialized without having a display connected on it).
> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> the same RGB connector and I'd like to use it in a near future.
> Returning -EPROBE_DEFER in case of several devices connected on the
> same connector implies that I'll have to wait for all the remote
> end-points to be available before my display controller could be
> instantiated.
> 
> While this could be acceptable when all drivers are statically linked
> in the kernel, it might be problematic when you're using modules,
> meaning that you won't be able to display anything on your LCD panel
> until your HDMI bridge module has been loaded.
> 
> > That
> > will still cause some delay before everything gets set up, but hopefully
> > less than what you're seeing now. There's also another thread where this
> > is being discussed because deferred probing is causing "unacceptable"
> > delays as well.
> 
> Could you point this thread out to me please ?

Sorry, I didn't check my emails before asking this :-).

> 
> Best Regards,
> 
> Boris
> 



-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21  9:49           ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21  9:49 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 21 Aug 2014 11:41:59 +0200
Boris BREZILLON <boris.brezillon@free-electrons.com> wrote:

> On Thu, 21 Aug 2014 11:04:07 +0200
> Thierry Reding <thierry.reding@gmail.com> wrote:
> 
> > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > Hi Ludovic,
> > > 
> > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > 
> > > > Hi Boris,
> > > > 
> > > > You can add
> > > > 
> > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > 
> > > Thanks for testing this driver.
> > > 
> > > > 
> > > > Only one issue but not related to your patches, you can't display
> > > > quickly the bootup logo since the panel detection takes too much
> > > > time.
> > > 
> > > Yes, actually this is related to the device probe order: the
> > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > check for panel availability) when the display controller is
> > > instantiated. I rely on the default polling infrastructure provided by
> > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > this is far more than you kernel boot time.
> > > 
> > > Do anyone see a solution to reduce this delay (without changing the
> > > polling interval). I thought we could add a notifier infrastructure to
> > > the DRM panel framework, but I'm not sure this is how you want things
> > > done...
> > 
> > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > yet. This will automatically take care of ordering things in a way that
> > DRM/KMS will only be initialized after the panel has been probed.
> 
> Actually I'd like to avoid doing this with a deferred probe, because,
> AFAIU, the remote endpoint is not tightly linked with the display
> controller driver (I mean the display controller can still be
> initialized without having a display connected on it).
> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> the same RGB connector and I'd like to use it in a near future.
> Returning -EPROBE_DEFER in case of several devices connected on the
> same connector implies that I'll have to wait for all the remote
> end-points to be available before my display controller could be
> instantiated.
> 
> While this could be acceptable when all drivers are statically linked
> in the kernel, it might be problematic when you're using modules,
> meaning that you won't be able to display anything on your LCD panel
> until your HDMI bridge module has been loaded.
> 
> > That
> > will still cause some delay before everything gets set up, but hopefully
> > less than what you're seeing now. There's also another thread where this
> > is being discussed because deferred probing is causing "unacceptable"
> > delays as well.
> 
> Could you point this thread out to me please ?

Sorry, I didn't check my emails before asking this :-).

> 
> Best Regards,
> 
> Boris
> 



-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21  9:41         ` Boris BREZILLON
  (?)
@ 2014-08-21  9:52           ` Thierry Reding
  -1 siblings, 0 replies; 103+ messages in thread
From: Thierry Reding @ 2014-08-21  9:52 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Laurent Pinchart, Mark Rutland, devicetree,
	Pawel Moll, Ian Campbell, linux-kernel, Rob Herring, Bo Shen,
	Kumar Gala, linux-arm-kernel

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

On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 11:04:07 +0200
> Thierry Reding <thierry.reding@gmail.com> wrote:
> 
> > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > Hi Ludovic,
> > > 
> > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > 
> > > > Hi Boris,
> > > > 
> > > > You can add
> > > > 
> > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > 
> > > Thanks for testing this driver.
> > > 
> > > > 
> > > > Only one issue but not related to your patches, you can't display
> > > > quickly the bootup logo since the panel detection takes too much
> > > > time.
> > > 
> > > Yes, actually this is related to the device probe order: the
> > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > check for panel availability) when the display controller is
> > > instantiated. I rely on the default polling infrastructure provided by
> > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > this is far more than you kernel boot time.
> > > 
> > > Do anyone see a solution to reduce this delay (without changing the
> > > polling interval). I thought we could add a notifier infrastructure to
> > > the DRM panel framework, but I'm not sure this is how you want things
> > > done...
> > 
> > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > yet. This will automatically take care of ordering things in a way that
> > DRM/KMS will only be initialized after the panel has been probed.
> 
> Actually I'd like to avoid doing this with a deferred probe, because,
> AFAIU, the remote endpoint is not tightly linked with the display
> controller driver (I mean the display controller can still be
> initialized without having a display connected on it).
> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> the same RGB connector and I'd like to use it in a near future.
> Returning -EPROBE_DEFER in case of several devices connected on the
> same connector implies that I'll have to wait for all the remote
> end-points to be available before my display controller could be
> instantiated.
> 
> While this could be acceptable when all drivers are statically linked
> in the kernel, it might be problematic when you're using modules,
> meaning that you won't be able to display anything on your LCD panel
> until your HDMI bridge module has been loaded.

No. HDMI should be using proper hotplugging anyway, hence it should be
always be loaded anyway. You're in for a world of pain if you think you
can run DRM with a driver that's composed of separate kernel modules.

Also if you don't want to use deferred probe, then you're in for the
full hotplugging panel dance and that implies that you need to fix a
bunch of things in DRM (one being the framebuffer console instantiation
that I referred to in the other thread). You also can't be using the
current device tree bindings because they all assume a dependency from
the display controller/output to the panel. For hotplugging you'd need
the dependency the other way around (the panel needs to refer to the
output by phandle).

> > That
> > will still cause some delay before everything gets set up, but hopefully
> > less than what you're seeing now. There's also another thread where this
> > is being discussed because deferred probing is causing "unacceptable"
> > delays as well.
> 
> Could you point this thread out to me please ?

I Cc'ed you on it.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21  9:52           ` Thierry Reding
  0 siblings, 0 replies; 103+ messages in thread
From: Thierry Reding @ 2014-08-21  9:52 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Mark Rutland, linux-pwm, Samuel Ortiz, Pawel Moll, devicetree,
	Lee Jones, Ian Campbell, Nicolas Ferre, linux-kernel, dri-devel,
	Rob Herring, Ludovic Desroches, Alexandre Belloni,
	Laurent Pinchart, Bo Shen, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Andrew Victor,
	linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 3702 bytes --]

On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 11:04:07 +0200
> Thierry Reding <thierry.reding@gmail.com> wrote:
> 
> > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > Hi Ludovic,
> > > 
> > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > 
> > > > Hi Boris,
> > > > 
> > > > You can add
> > > > 
> > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > 
> > > Thanks for testing this driver.
> > > 
> > > > 
> > > > Only one issue but not related to your patches, you can't display
> > > > quickly the bootup logo since the panel detection takes too much
> > > > time.
> > > 
> > > Yes, actually this is related to the device probe order: the
> > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > check for panel availability) when the display controller is
> > > instantiated. I rely on the default polling infrastructure provided by
> > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > this is far more than you kernel boot time.
> > > 
> > > Do anyone see a solution to reduce this delay (without changing the
> > > polling interval). I thought we could add a notifier infrastructure to
> > > the DRM panel framework, but I'm not sure this is how you want things
> > > done...
> > 
> > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > yet. This will automatically take care of ordering things in a way that
> > DRM/KMS will only be initialized after the panel has been probed.
> 
> Actually I'd like to avoid doing this with a deferred probe, because,
> AFAIU, the remote endpoint is not tightly linked with the display
> controller driver (I mean the display controller can still be
> initialized without having a display connected on it).
> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> the same RGB connector and I'd like to use it in a near future.
> Returning -EPROBE_DEFER in case of several devices connected on the
> same connector implies that I'll have to wait for all the remote
> end-points to be available before my display controller could be
> instantiated.
> 
> While this could be acceptable when all drivers are statically linked
> in the kernel, it might be problematic when you're using modules,
> meaning that you won't be able to display anything on your LCD panel
> until your HDMI bridge module has been loaded.

No. HDMI should be using proper hotplugging anyway, hence it should be
always be loaded anyway. You're in for a world of pain if you think you
can run DRM with a driver that's composed of separate kernel modules.

Also if you don't want to use deferred probe, then you're in for the
full hotplugging panel dance and that implies that you need to fix a
bunch of things in DRM (one being the framebuffer console instantiation
that I referred to in the other thread). You also can't be using the
current device tree bindings because they all assume a dependency from
the display controller/output to the panel. For hotplugging you'd need
the dependency the other way around (the panel needs to refer to the
output by phandle).

> > That
> > will still cause some delay before everything gets set up, but hopefully
> > less than what you're seeing now. There's also another thread where this
> > is being discussed because deferred probing is causing "unacceptable"
> > delays as well.
> 
> Could you point this thread out to me please ?

I Cc'ed you on it.

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21  9:52           ` Thierry Reding
  0 siblings, 0 replies; 103+ messages in thread
From: Thierry Reding @ 2014-08-21  9:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 11:04:07 +0200
> Thierry Reding <thierry.reding@gmail.com> wrote:
> 
> > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > Hi Ludovic,
> > > 
> > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > 
> > > > Hi Boris,
> > > > 
> > > > You can add
> > > > 
> > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > 
> > > Thanks for testing this driver.
> > > 
> > > > 
> > > > Only one issue but not related to your patches, you can't display
> > > > quickly the bootup logo since the panel detection takes too much
> > > > time.
> > > 
> > > Yes, actually this is related to the device probe order: the
> > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > check for panel availability) when the display controller is
> > > instantiated. I rely on the default polling infrastructure provided by
> > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > this is far more than you kernel boot time.
> > > 
> > > Do anyone see a solution to reduce this delay (without changing the
> > > polling interval). I thought we could add a notifier infrastructure to
> > > the DRM panel framework, but I'm not sure this is how you want things
> > > done...
> > 
> > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > yet. This will automatically take care of ordering things in a way that
> > DRM/KMS will only be initialized after the panel has been probed.
> 
> Actually I'd like to avoid doing this with a deferred probe, because,
> AFAIU, the remote endpoint is not tightly linked with the display
> controller driver (I mean the display controller can still be
> initialized without having a display connected on it).
> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> the same RGB connector and I'd like to use it in a near future.
> Returning -EPROBE_DEFER in case of several devices connected on the
> same connector implies that I'll have to wait for all the remote
> end-points to be available before my display controller could be
> instantiated.
> 
> While this could be acceptable when all drivers are statically linked
> in the kernel, it might be problematic when you're using modules,
> meaning that you won't be able to display anything on your LCD panel
> until your HDMI bridge module has been loaded.

No. HDMI should be using proper hotplugging anyway, hence it should be
always be loaded anyway. You're in for a world of pain if you think you
can run DRM with a driver that's composed of separate kernel modules.

Also if you don't want to use deferred probe, then you're in for the
full hotplugging panel dance and that implies that you need to fix a
bunch of things in DRM (one being the framebuffer console instantiation
that I referred to in the other thread). You also can't be using the
current device tree bindings because they all assume a dependency from
the display controller/output to the panel. For hotplugging you'd need
the dependency the other way around (the panel needs to refer to the
output by phandle).

> > That
> > will still cause some delay before everything gets set up, but hopefully
> > less than what you're seeing now. There's also another thread where this
> > is being discussed because deferred probing is causing "unacceptable"
> > delays as well.
> 
> Could you point this thread out to me please ?

I Cc'ed you on it.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140821/b2a2b1e8/attachment.sig>

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21  9:41         ` Boris BREZILLON
  (?)
@ 2014-08-21 10:16           ` Andrzej Hajda
  -1 siblings, 0 replies; 103+ messages in thread
From: Andrzej Hajda @ 2014-08-21 10:16 UTC (permalink / raw)
  To: Boris BREZILLON, Thierry Reding
  Cc: Mark Rutland, linux-pwm, Samuel Ortiz, Pawel Moll, devicetree,
	Lee Jones, Ian Campbell, Nicolas Ferre, linux-kernel, dri-devel,
	Rob Herring, Ludovic Desroches, Alexandre Belloni,
	Laurent Pinchart, Bo Shen, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Andrew Victor,
	linux-arm-kernel

On 08/21/2014 11:41 AM, Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 11:04:07 +0200
> Thierry Reding <thierry.reding@gmail.com> wrote:
> 
>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
>>> Hi Ludovic,
>>>
>>> On Thu, 21 Aug 2014 10:16:19 +0200
>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
>>>
>>>> Hi Boris,
>>>>
>>>> You can add
>>>>
>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>>>
>>> Thanks for testing this driver.
>>>
>>>>
>>>> Only one issue but not related to your patches, you can't display
>>>> quickly the bootup logo since the panel detection takes too much
>>>> time.
>>>
>>> Yes, actually this is related to the device probe order: the
>>> hlcdc-display-controller device is probed before the simple-panel, thus
>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
>>> check for panel availability) when the display controller is
>>> instantiated. I rely on the default polling infrastructure provided by
>>> the DRM/KMS framework which polls for a new connector every 10s, and
>>> this is far more than you kernel boot time.
>>>
>>> Do anyone see a solution to reduce this delay (without changing the
>>> polling interval). I thought we could add a notifier infrastructure to
>>> the DRM panel framework, but I'm not sure this is how you want things
>>> done...
>>
>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
>> yet. This will automatically take care of ordering things in a way that
>> DRM/KMS will only be initialized after the panel has been probed.
> 
> Actually I'd like to avoid doing this with a deferred probe, because,
> AFAIU, the remote endpoint is not tightly linked with the display
> controller driver (I mean the display controller can still be
> initialized without having a display connected on it).
> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> the same RGB connector and I'd like to use it in a near future.
> Returning -EPROBE_DEFER in case of several devices connected on the
> same connector implies that I'll have to wait for all the remote
> end-points to be available before my display controller could be
> instantiated.
> 
> While this could be acceptable when all drivers are statically linked
> in the kernel, it might be problematic when you're using modules,
> meaning that you won't be able to display anything on your LCD panel
> until your HDMI bridge module has been loaded.

I agree that deferring whole drm initialization due to panel not being
ready is not an optimal solution. Nice solution of this problem is with
DSI panels, DSI bus infrastructure provides callbacks for DRM encoder
which are called when DSI panel is ready. Simple panels are platform
devices so they do not have such callbacks. To solve this in a generic
way I have proposed framework for tracking interfaces[1], the series
contains also solution for the same problem with exynos display
controller[2].

Regards
Andrzej

[1]: https://lkml.org/lkml/2014/4/30/345
[2]: https://lkml.org/lkml/2014/4/30/653

> 
>> That
>> will still cause some delay before everything gets set up, but hopefully
>> less than what you're seeing now. There's also another thread where this
>> is being discussed because deferred probing is causing "unacceptable"
>> delays as well.
> 
> Could you point this thread out to me please ?
> 
> Best Regards,
> 
> Boris
> 


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 10:16           ` Andrzej Hajda
  0 siblings, 0 replies; 103+ messages in thread
From: Andrzej Hajda @ 2014-08-21 10:16 UTC (permalink / raw)
  To: Boris BREZILLON, Thierry Reding
  Cc: Mark Rutland, linux-pwm, Samuel Ortiz, Pawel Moll, devicetree,
	Ludovic Desroches, Ian Campbell, Nicolas Ferre, linux-kernel,
	dri-devel, Rob Herring, Alexandre Belloni, Laurent Pinchart,
	Bo Shen, Kumar Gala, Jean-Christophe Plagniol-Villard, Lee Jones,
	Andrew Victor, linux-arm-kernel

On 08/21/2014 11:41 AM, Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 11:04:07 +0200
> Thierry Reding <thierry.reding@gmail.com> wrote:
> 
>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
>>> Hi Ludovic,
>>>
>>> On Thu, 21 Aug 2014 10:16:19 +0200
>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
>>>
>>>> Hi Boris,
>>>>
>>>> You can add
>>>>
>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>>>
>>> Thanks for testing this driver.
>>>
>>>>
>>>> Only one issue but not related to your patches, you can't display
>>>> quickly the bootup logo since the panel detection takes too much
>>>> time.
>>>
>>> Yes, actually this is related to the device probe order: the
>>> hlcdc-display-controller device is probed before the simple-panel, thus
>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
>>> check for panel availability) when the display controller is
>>> instantiated. I rely on the default polling infrastructure provided by
>>> the DRM/KMS framework which polls for a new connector every 10s, and
>>> this is far more than you kernel boot time.
>>>
>>> Do anyone see a solution to reduce this delay (without changing the
>>> polling interval). I thought we could add a notifier infrastructure to
>>> the DRM panel framework, but I'm not sure this is how you want things
>>> done...
>>
>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
>> yet. This will automatically take care of ordering things in a way that
>> DRM/KMS will only be initialized after the panel has been probed.
> 
> Actually I'd like to avoid doing this with a deferred probe, because,
> AFAIU, the remote endpoint is not tightly linked with the display
> controller driver (I mean the display controller can still be
> initialized without having a display connected on it).
> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> the same RGB connector and I'd like to use it in a near future.
> Returning -EPROBE_DEFER in case of several devices connected on the
> same connector implies that I'll have to wait for all the remote
> end-points to be available before my display controller could be
> instantiated.
> 
> While this could be acceptable when all drivers are statically linked
> in the kernel, it might be problematic when you're using modules,
> meaning that you won't be able to display anything on your LCD panel
> until your HDMI bridge module has been loaded.

I agree that deferring whole drm initialization due to panel not being
ready is not an optimal solution. Nice solution of this problem is with
DSI panels, DSI bus infrastructure provides callbacks for DRM encoder
which are called when DSI panel is ready. Simple panels are platform
devices so they do not have such callbacks. To solve this in a generic
way I have proposed framework for tracking interfaces[1], the series
contains also solution for the same problem with exynos display
controller[2].

Regards
Andrzej

[1]: https://lkml.org/lkml/2014/4/30/345
[2]: https://lkml.org/lkml/2014/4/30/653

> 
>> That
>> will still cause some delay before everything gets set up, but hopefully
>> less than what you're seeing now. There's also another thread where this
>> is being discussed because deferred probing is causing "unacceptable"
>> delays as well.
> 
> Could you point this thread out to me please ?
> 
> Best Regards,
> 
> Boris
> 

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 10:16           ` Andrzej Hajda
  0 siblings, 0 replies; 103+ messages in thread
From: Andrzej Hajda @ 2014-08-21 10:16 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/21/2014 11:41 AM, Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 11:04:07 +0200
> Thierry Reding <thierry.reding@gmail.com> wrote:
> 
>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
>>> Hi Ludovic,
>>>
>>> On Thu, 21 Aug 2014 10:16:19 +0200
>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
>>>
>>>> Hi Boris,
>>>>
>>>> You can add
>>>>
>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>>>
>>> Thanks for testing this driver.
>>>
>>>>
>>>> Only one issue but not related to your patches, you can't display
>>>> quickly the bootup logo since the panel detection takes too much
>>>> time.
>>>
>>> Yes, actually this is related to the device probe order: the
>>> hlcdc-display-controller device is probed before the simple-panel, thus
>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
>>> check for panel availability) when the display controller is
>>> instantiated. I rely on the default polling infrastructure provided by
>>> the DRM/KMS framework which polls for a new connector every 10s, and
>>> this is far more than you kernel boot time.
>>>
>>> Do anyone see a solution to reduce this delay (without changing the
>>> polling interval). I thought we could add a notifier infrastructure to
>>> the DRM panel framework, but I'm not sure this is how you want things
>>> done...
>>
>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
>> yet. This will automatically take care of ordering things in a way that
>> DRM/KMS will only be initialized after the panel has been probed.
> 
> Actually I'd like to avoid doing this with a deferred probe, because,
> AFAIU, the remote endpoint is not tightly linked with the display
> controller driver (I mean the display controller can still be
> initialized without having a display connected on it).
> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> the same RGB connector and I'd like to use it in a near future.
> Returning -EPROBE_DEFER in case of several devices connected on the
> same connector implies that I'll have to wait for all the remote
> end-points to be available before my display controller could be
> instantiated.
> 
> While this could be acceptable when all drivers are statically linked
> in the kernel, it might be problematic when you're using modules,
> meaning that you won't be able to display anything on your LCD panel
> until your HDMI bridge module has been loaded.

I agree that deferring whole drm initialization due to panel not being
ready is not an optimal solution. Nice solution of this problem is with
DSI panels, DSI bus infrastructure provides callbacks for DRM encoder
which are called when DSI panel is ready. Simple panels are platform
devices so they do not have such callbacks. To solve this in a generic
way I have proposed framework for tracking interfaces[1], the series
contains also solution for the same problem with exynos display
controller[2].

Regards
Andrzej

[1]: https://lkml.org/lkml/2014/4/30/345
[2]: https://lkml.org/lkml/2014/4/30/653

> 
>> That
>> will still cause some delay before everything gets set up, but hopefully
>> less than what you're seeing now. There's also another thread where this
>> is being discussed because deferred probing is causing "unacceptable"
>> delays as well.
> 
> Could you point this thread out to me please ?
> 
> Best Regards,
> 
> Boris
> 

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21  9:52           ` Thierry Reding
@ 2014-08-21 10:32             ` Andrzej Hajda
  -1 siblings, 0 replies; 103+ messages in thread
From: Andrzej Hajda @ 2014-08-21 10:32 UTC (permalink / raw)
  To: Thierry Reding, Boris BREZILLON
  Cc: Mark Rutland, linux-pwm, Samuel Ortiz, Pawel Moll, devicetree,
	Lee Jones, Ian Campbell, Nicolas Ferre, linux-kernel, dri-devel,
	Rob Herring, Ludovic Desroches, Alexandre Belloni,
	Laurent Pinchart, Bo Shen, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Andrew Victor,
	linux-arm-kernel

On 08/21/2014 11:52 AM, Thierry Reding wrote:
> On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
>> On Thu, 21 Aug 2014 11:04:07 +0200
>> Thierry Reding <thierry.reding@gmail.com> wrote:
>>
>>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
>>>> Hi Ludovic,
>>>>
>>>> On Thu, 21 Aug 2014 10:16:19 +0200
>>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
>>>>
>>>>> Hi Boris,
>>>>>
>>>>> You can add
>>>>>
>>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>>>>
>>>> Thanks for testing this driver.
>>>>
>>>>>
>>>>> Only one issue but not related to your patches, you can't display
>>>>> quickly the bootup logo since the panel detection takes too much
>>>>> time.
>>>>
>>>> Yes, actually this is related to the device probe order: the
>>>> hlcdc-display-controller device is probed before the simple-panel, thus
>>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
>>>> check for panel availability) when the display controller is
>>>> instantiated. I rely on the default polling infrastructure provided by
>>>> the DRM/KMS framework which polls for a new connector every 10s, and
>>>> this is far more than you kernel boot time.
>>>>
>>>> Do anyone see a solution to reduce this delay (without changing the
>>>> polling interval). I thought we could add a notifier infrastructure to
>>>> the DRM panel framework, but I'm not sure this is how you want things
>>>> done...
>>>
>>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
>>> yet. This will automatically take care of ordering things in a way that
>>> DRM/KMS will only be initialized after the panel has been probed.
>>
>> Actually I'd like to avoid doing this with a deferred probe, because,
>> AFAIU, the remote endpoint is not tightly linked with the display
>> controller driver (I mean the display controller can still be
>> initialized without having a display connected on it).
>> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
>> the same RGB connector and I'd like to use it in a near future.
>> Returning -EPROBE_DEFER in case of several devices connected on the
>> same connector implies that I'll have to wait for all the remote
>> end-points to be available before my display controller could be
>> instantiated.
>>
>> While this could be acceptable when all drivers are statically linked
>> in the kernel, it might be problematic when you're using modules,
>> meaning that you won't be able to display anything on your LCD panel
>> until your HDMI bridge module has been loaded.
> 
> No. HDMI should be using proper hotplugging anyway, hence it should be
> always be loaded anyway. You're in for a world of pain if you think you
> can run DRM with a driver that's composed of separate kernel modules.
> 
> Also if you don't want to use deferred probe, then you're in for the
> full hotplugging panel dance and that implies that you need to fix a
> bunch of things in DRM (one being the framebuffer console instantiation
> that I referred to in the other thread). You also can't be using the
> current device tree bindings because they all assume a dependency from
> the display controller/output to the panel. For hotplugging you'd need
> the dependency the other way around (the panel needs to refer to the
> output by phandle).

I have tested panel as a module in exynos-dsi + panel-s6e8aa0
configuration, everything works. There is a workaround for fb console
not being reconfigurable, but it does not make thing worse than before.
And I do not see a problem with phandles, ie in DT they point both ways,
according to binding advices at the time, but in the code it is display
controller/encoder which is looking for the panel.

Regards
Andrzej

> 
>>> That
>>> will still cause some delay before everything gets set up, but hopefully
>>> less than what you're seeing now. There's also another thread where this
>>> is being discussed because deferred probing is causing "unacceptable"
>>> delays as well.
>>
>> Could you point this thread out to me please ?
> 
> I Cc'ed you on it.
> 
> Thierry
> 
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
> 


^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 10:32             ` Andrzej Hajda
  0 siblings, 0 replies; 103+ messages in thread
From: Andrzej Hajda @ 2014-08-21 10:32 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/21/2014 11:52 AM, Thierry Reding wrote:
> On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
>> On Thu, 21 Aug 2014 11:04:07 +0200
>> Thierry Reding <thierry.reding@gmail.com> wrote:
>>
>>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
>>>> Hi Ludovic,
>>>>
>>>> On Thu, 21 Aug 2014 10:16:19 +0200
>>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
>>>>
>>>>> Hi Boris,
>>>>>
>>>>> You can add
>>>>>
>>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>>>>
>>>> Thanks for testing this driver.
>>>>
>>>>>
>>>>> Only one issue but not related to your patches, you can't display
>>>>> quickly the bootup logo since the panel detection takes too much
>>>>> time.
>>>>
>>>> Yes, actually this is related to the device probe order: the
>>>> hlcdc-display-controller device is probed before the simple-panel, thus
>>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
>>>> check for panel availability) when the display controller is
>>>> instantiated. I rely on the default polling infrastructure provided by
>>>> the DRM/KMS framework which polls for a new connector every 10s, and
>>>> this is far more than you kernel boot time.
>>>>
>>>> Do anyone see a solution to reduce this delay (without changing the
>>>> polling interval). I thought we could add a notifier infrastructure to
>>>> the DRM panel framework, but I'm not sure this is how you want things
>>>> done...
>>>
>>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
>>> yet. This will automatically take care of ordering things in a way that
>>> DRM/KMS will only be initialized after the panel has been probed.
>>
>> Actually I'd like to avoid doing this with a deferred probe, because,
>> AFAIU, the remote endpoint is not tightly linked with the display
>> controller driver (I mean the display controller can still be
>> initialized without having a display connected on it).
>> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
>> the same RGB connector and I'd like to use it in a near future.
>> Returning -EPROBE_DEFER in case of several devices connected on the
>> same connector implies that I'll have to wait for all the remote
>> end-points to be available before my display controller could be
>> instantiated.
>>
>> While this could be acceptable when all drivers are statically linked
>> in the kernel, it might be problematic when you're using modules,
>> meaning that you won't be able to display anything on your LCD panel
>> until your HDMI bridge module has been loaded.
> 
> No. HDMI should be using proper hotplugging anyway, hence it should be
> always be loaded anyway. You're in for a world of pain if you think you
> can run DRM with a driver that's composed of separate kernel modules.
> 
> Also if you don't want to use deferred probe, then you're in for the
> full hotplugging panel dance and that implies that you need to fix a
> bunch of things in DRM (one being the framebuffer console instantiation
> that I referred to in the other thread). You also can't be using the
> current device tree bindings because they all assume a dependency from
> the display controller/output to the panel. For hotplugging you'd need
> the dependency the other way around (the panel needs to refer to the
> output by phandle).

I have tested panel as a module in exynos-dsi + panel-s6e8aa0
configuration, everything works. There is a workaround for fb console
not being reconfigurable, but it does not make thing worse than before.
And I do not see a problem with phandles, ie in DT they point both ways,
according to binding advices at the time, but in the code it is display
controller/encoder which is looking for the panel.

Regards
Andrzej

> 
>>> That
>>> will still cause some delay before everything gets set up, but hopefully
>>> less than what you're seeing now. There's also another thread where this
>>> is being discussed because deferred probing is causing "unacceptable"
>>> delays as well.
>>
>> Could you point this thread out to me please ?
> 
> I Cc'ed you on it.
> 
> Thierry
> 
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
> 

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21  9:52           ` Thierry Reding
@ 2014-08-21 13:06             ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21 13:06 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Laurent Pinchart, Mark Rutland, devicetree,
	Pawel Moll, Ian Campbell, linux-kernel, Rob Herring, Bo Shen,
	Kumar Gala, linux-arm-kernel

On Thu, 21 Aug 2014 11:52:03 +0200
Thierry Reding <thierry.reding@gmail.com> wrote:

> On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> > On Thu, 21 Aug 2014 11:04:07 +0200
> > Thierry Reding <thierry.reding@gmail.com> wrote:
> > 
> > > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > > Hi Ludovic,
> > > > 
> > > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > > 
> > > > > Hi Boris,
> > > > > 
> > > > > You can add
> > > > > 
> > > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > > 
> > > > Thanks for testing this driver.
> > > > 
> > > > > 
> > > > > Only one issue but not related to your patches, you can't display
> > > > > quickly the bootup logo since the panel detection takes too much
> > > > > time.
> > > > 
> > > > Yes, actually this is related to the device probe order: the
> > > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > > check for panel availability) when the display controller is
> > > > instantiated. I rely on the default polling infrastructure provided by
> > > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > > this is far more than you kernel boot time.
> > > > 
> > > > Do anyone see a solution to reduce this delay (without changing the
> > > > polling interval). I thought we could add a notifier infrastructure to
> > > > the DRM panel framework, but I'm not sure this is how you want things
> > > > done...
> > > 
> > > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > > yet. This will automatically take care of ordering things in a way that
> > > DRM/KMS will only be initialized after the panel has been probed.
> > 
> > Actually I'd like to avoid doing this with a deferred probe, because,
> > AFAIU, the remote endpoint is not tightly linked with the display
> > controller driver (I mean the display controller can still be
> > initialized without having a display connected on it).
> > Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> > the same RGB connector and I'd like to use it in a near future.
> > Returning -EPROBE_DEFER in case of several devices connected on the
> > same connector implies that I'll have to wait for all the remote
> > end-points to be available before my display controller could be
> > instantiated.
> > 
> > While this could be acceptable when all drivers are statically linked
> > in the kernel, it might be problematic when you're using modules,
> > meaning that you won't be able to display anything on your LCD panel
> > until your HDMI bridge module has been loaded.
> 
> No. HDMI should be using proper hotplugging anyway, hence it should be
> always be loaded anyway. You're in for a world of pain if you think you
> can run DRM with a driver that's composed of separate kernel modules.

I was talking about the external RGB to HDMI encoder, should the driver
for this encoder (which is not on On Chip block) be compiled
statically too ?

> 
> Also if you don't want to use deferred probe, then you're in for the
> full hotplugging panel dance and that implies that you need to fix a
> bunch of things in DRM (one being the framebuffer console instantiation
> that I referred to in the other thread).

For now, I wait until there is a device connected on the RGB connector
(connector status set to connector_status_connected) before creating an
fbdev. It might not be the cleanest way to solve this issue, but it
works :-).

> You also can't be using the
> current device tree bindings because they all assume a dependency from
> the display controller/output to the panel. For hotplugging you'd need
> the dependency the other way around (the panel needs to refer to the
> output by phandle).

Here [1] is a proposal for notification support in the drm_panel
infrastructure (which is not that complicated), and here [2] is how
I use it in my atmel-hlcdc driver to generate hotplug events.

Let me know if you want me to submit a proper patch series...

Best Regards,

Boris

[1]http://code.bulix.org/scq4g3-86804
[2]http://code.bulix.org/7dg501-86805

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 13:06             ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21 13:06 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 21 Aug 2014 11:52:03 +0200
Thierry Reding <thierry.reding@gmail.com> wrote:

> On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> > On Thu, 21 Aug 2014 11:04:07 +0200
> > Thierry Reding <thierry.reding@gmail.com> wrote:
> > 
> > > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > > Hi Ludovic,
> > > > 
> > > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > > 
> > > > > Hi Boris,
> > > > > 
> > > > > You can add
> > > > > 
> > > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > > 
> > > > Thanks for testing this driver.
> > > > 
> > > > > 
> > > > > Only one issue but not related to your patches, you can't display
> > > > > quickly the bootup logo since the panel detection takes too much
> > > > > time.
> > > > 
> > > > Yes, actually this is related to the device probe order: the
> > > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > > check for panel availability) when the display controller is
> > > > instantiated. I rely on the default polling infrastructure provided by
> > > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > > this is far more than you kernel boot time.
> > > > 
> > > > Do anyone see a solution to reduce this delay (without changing the
> > > > polling interval). I thought we could add a notifier infrastructure to
> > > > the DRM panel framework, but I'm not sure this is how you want things
> > > > done...
> > > 
> > > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > > yet. This will automatically take care of ordering things in a way that
> > > DRM/KMS will only be initialized after the panel has been probed.
> > 
> > Actually I'd like to avoid doing this with a deferred probe, because,
> > AFAIU, the remote endpoint is not tightly linked with the display
> > controller driver (I mean the display controller can still be
> > initialized without having a display connected on it).
> > Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> > the same RGB connector and I'd like to use it in a near future.
> > Returning -EPROBE_DEFER in case of several devices connected on the
> > same connector implies that I'll have to wait for all the remote
> > end-points to be available before my display controller could be
> > instantiated.
> > 
> > While this could be acceptable when all drivers are statically linked
> > in the kernel, it might be problematic when you're using modules,
> > meaning that you won't be able to display anything on your LCD panel
> > until your HDMI bridge module has been loaded.
> 
> No. HDMI should be using proper hotplugging anyway, hence it should be
> always be loaded anyway. You're in for a world of pain if you think you
> can run DRM with a driver that's composed of separate kernel modules.

I was talking about the external RGB to HDMI encoder, should the driver
for this encoder (which is not on On Chip block) be compiled
statically too ?

> 
> Also if you don't want to use deferred probe, then you're in for the
> full hotplugging panel dance and that implies that you need to fix a
> bunch of things in DRM (one being the framebuffer console instantiation
> that I referred to in the other thread).

For now, I wait until there is a device connected on the RGB connector
(connector status set to connector_status_connected) before creating an
fbdev. It might not be the cleanest way to solve this issue, but it
works :-).

> You also can't be using the
> current device tree bindings because they all assume a dependency from
> the display controller/output to the panel. For hotplugging you'd need
> the dependency the other way around (the panel needs to refer to the
> output by phandle).

Here [1] is a proposal for notification support in the drm_panel
infrastructure (which is not that complicated), and here [2] is how
I use it in my atmel-hlcdc driver to generate hotplug events.

Let me know if you want me to submit a proper patch series...

Best Regards,

Boris

[1]http://code.bulix.org/scq4g3-86804
[2]http://code.bulix.org/7dg501-86805

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21 13:06             ` Boris BREZILLON
  (?)
@ 2014-08-21 13:16               ` Thierry Reding
  -1 siblings, 0 replies; 103+ messages in thread
From: Thierry Reding @ 2014-08-21 13:16 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Laurent Pinchart, Mark Rutland, devicetree,
	Pawel Moll, Ian Campbell, linux-kernel, Rob Herring, Bo Shen,
	Kumar Gala, linux-arm-kernel

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

On Thu, Aug 21, 2014 at 03:06:00PM +0200, Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 11:52:03 +0200
> Thierry Reding <thierry.reding@gmail.com> wrote:
> 
> > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> > > On Thu, 21 Aug 2014 11:04:07 +0200
> > > Thierry Reding <thierry.reding@gmail.com> wrote:
> > > 
> > > > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > > > Hi Ludovic,
> > > > > 
> > > > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > > > 
> > > > > > Hi Boris,
> > > > > > 
> > > > > > You can add
> > > > > > 
> > > > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > > > 
> > > > > Thanks for testing this driver.
> > > > > 
> > > > > > 
> > > > > > Only one issue but not related to your patches, you can't display
> > > > > > quickly the bootup logo since the panel detection takes too much
> > > > > > time.
> > > > > 
> > > > > Yes, actually this is related to the device probe order: the
> > > > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > > > check for panel availability) when the display controller is
> > > > > instantiated. I rely on the default polling infrastructure provided by
> > > > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > > > this is far more than you kernel boot time.
> > > > > 
> > > > > Do anyone see a solution to reduce this delay (without changing the
> > > > > polling interval). I thought we could add a notifier infrastructure to
> > > > > the DRM panel framework, but I'm not sure this is how you want things
> > > > > done...
> > > > 
> > > > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > > > yet. This will automatically take care of ordering things in a way that
> > > > DRM/KMS will only be initialized after the panel has been probed.
> > > 
> > > Actually I'd like to avoid doing this with a deferred probe, because,
> > > AFAIU, the remote endpoint is not tightly linked with the display
> > > controller driver (I mean the display controller can still be
> > > initialized without having a display connected on it).
> > > Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> > > the same RGB connector and I'd like to use it in a near future.
> > > Returning -EPROBE_DEFER in case of several devices connected on the
> > > same connector implies that I'll have to wait for all the remote
> > > end-points to be available before my display controller could be
> > > instantiated.
> > > 
> > > While this could be acceptable when all drivers are statically linked
> > > in the kernel, it might be problematic when you're using modules,
> > > meaning that you won't be able to display anything on your LCD panel
> > > until your HDMI bridge module has been loaded.
> > 
> > No. HDMI should be using proper hotplugging anyway, hence it should be
> > always be loaded anyway. You're in for a world of pain if you think you
> > can run DRM with a driver that's composed of separate kernel modules.
> 
> I was talking about the external RGB to HDMI encoder, should the driver
> for this encoder (which is not on On Chip block) be compiled
> statically too ?
> 
> > 
> > Also if you don't want to use deferred probe, then you're in for the
> > full hotplugging panel dance and that implies that you need to fix a
> > bunch of things in DRM (one being the framebuffer console instantiation
> > that I referred to in the other thread).
> 
> For now, I wait until there is a device connected on the RGB connector
> (connector status set to connector_status_connected) before creating an
> fbdev. It might not be the cleanest way to solve this issue, but it
> works :-).

Yeah, I guess that's one way to do it. But it's tricky to get right when
you have several outputs. Which one should be considered the primary and
trigger fbdev creation?

> > You also can't be using the
> > current device tree bindings because they all assume a dependency from
> > the display controller/output to the panel. For hotplugging you'd need
> > the dependency the other way around (the panel needs to refer to the
> > output by phandle).
> 
> Here [1] is a proposal for notification support in the drm_panel
> infrastructure (which is not that complicated), and here [2] is how
> I use it in my atmel-hlcdc driver to generate hotplug events.
> 
> Let me know if you want me to submit a proper patch series...
> 
> Best Regards,
> 
> Boris
> 
> [1]http://code.bulix.org/scq4g3-86804
> [2]http://code.bulix.org/7dg501-86805

Those look interesting. Any chance you could look into how to do the
same without resorting to notifiers?

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 13:16               ` Thierry Reding
  0 siblings, 0 replies; 103+ messages in thread
From: Thierry Reding @ 2014-08-21 13:16 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Mark Rutland, linux-pwm, Samuel Ortiz, Pawel Moll, devicetree,
	Lee Jones, Ian Campbell, Nicolas Ferre, linux-kernel, dri-devel,
	Rob Herring, Ludovic Desroches, Alexandre Belloni,
	Laurent Pinchart, Bo Shen, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Andrew Victor,
	linux-arm-kernel


[-- Attachment #1.1: Type: text/plain, Size: 4888 bytes --]

On Thu, Aug 21, 2014 at 03:06:00PM +0200, Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 11:52:03 +0200
> Thierry Reding <thierry.reding@gmail.com> wrote:
> 
> > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> > > On Thu, 21 Aug 2014 11:04:07 +0200
> > > Thierry Reding <thierry.reding@gmail.com> wrote:
> > > 
> > > > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > > > Hi Ludovic,
> > > > > 
> > > > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > > > 
> > > > > > Hi Boris,
> > > > > > 
> > > > > > You can add
> > > > > > 
> > > > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > > > 
> > > > > Thanks for testing this driver.
> > > > > 
> > > > > > 
> > > > > > Only one issue but not related to your patches, you can't display
> > > > > > quickly the bootup logo since the panel detection takes too much
> > > > > > time.
> > > > > 
> > > > > Yes, actually this is related to the device probe order: the
> > > > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > > > check for panel availability) when the display controller is
> > > > > instantiated. I rely on the default polling infrastructure provided by
> > > > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > > > this is far more than you kernel boot time.
> > > > > 
> > > > > Do anyone see a solution to reduce this delay (without changing the
> > > > > polling interval). I thought we could add a notifier infrastructure to
> > > > > the DRM panel framework, but I'm not sure this is how you want things
> > > > > done...
> > > > 
> > > > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > > > yet. This will automatically take care of ordering things in a way that
> > > > DRM/KMS will only be initialized after the panel has been probed.
> > > 
> > > Actually I'd like to avoid doing this with a deferred probe, because,
> > > AFAIU, the remote endpoint is not tightly linked with the display
> > > controller driver (I mean the display controller can still be
> > > initialized without having a display connected on it).
> > > Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> > > the same RGB connector and I'd like to use it in a near future.
> > > Returning -EPROBE_DEFER in case of several devices connected on the
> > > same connector implies that I'll have to wait for all the remote
> > > end-points to be available before my display controller could be
> > > instantiated.
> > > 
> > > While this could be acceptable when all drivers are statically linked
> > > in the kernel, it might be problematic when you're using modules,
> > > meaning that you won't be able to display anything on your LCD panel
> > > until your HDMI bridge module has been loaded.
> > 
> > No. HDMI should be using proper hotplugging anyway, hence it should be
> > always be loaded anyway. You're in for a world of pain if you think you
> > can run DRM with a driver that's composed of separate kernel modules.
> 
> I was talking about the external RGB to HDMI encoder, should the driver
> for this encoder (which is not on On Chip block) be compiled
> statically too ?
> 
> > 
> > Also if you don't want to use deferred probe, then you're in for the
> > full hotplugging panel dance and that implies that you need to fix a
> > bunch of things in DRM (one being the framebuffer console instantiation
> > that I referred to in the other thread).
> 
> For now, I wait until there is a device connected on the RGB connector
> (connector status set to connector_status_connected) before creating an
> fbdev. It might not be the cleanest way to solve this issue, but it
> works :-).

Yeah, I guess that's one way to do it. But it's tricky to get right when
you have several outputs. Which one should be considered the primary and
trigger fbdev creation?

> > You also can't be using the
> > current device tree bindings because they all assume a dependency from
> > the display controller/output to the panel. For hotplugging you'd need
> > the dependency the other way around (the panel needs to refer to the
> > output by phandle).
> 
> Here [1] is a proposal for notification support in the drm_panel
> infrastructure (which is not that complicated), and here [2] is how
> I use it in my atmel-hlcdc driver to generate hotplug events.
> 
> Let me know if you want me to submit a proper patch series...
> 
> Best Regards,
> 
> Boris
> 
> [1]http://code.bulix.org/scq4g3-86804
> [2]http://code.bulix.org/7dg501-86805

Those look interesting. Any chance you could look into how to do the
same without resorting to notifiers?

Thierry

[-- Attachment #1.2: Type: application/pgp-signature, Size: 819 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 13:16               ` Thierry Reding
  0 siblings, 0 replies; 103+ messages in thread
From: Thierry Reding @ 2014-08-21 13:16 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Aug 21, 2014 at 03:06:00PM +0200, Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 11:52:03 +0200
> Thierry Reding <thierry.reding@gmail.com> wrote:
> 
> > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> > > On Thu, 21 Aug 2014 11:04:07 +0200
> > > Thierry Reding <thierry.reding@gmail.com> wrote:
> > > 
> > > > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > > > Hi Ludovic,
> > > > > 
> > > > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > > > 
> > > > > > Hi Boris,
> > > > > > 
> > > > > > You can add
> > > > > > 
> > > > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > > > 
> > > > > Thanks for testing this driver.
> > > > > 
> > > > > > 
> > > > > > Only one issue but not related to your patches, you can't display
> > > > > > quickly the bootup logo since the panel detection takes too much
> > > > > > time.
> > > > > 
> > > > > Yes, actually this is related to the device probe order: the
> > > > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > > > check for panel availability) when the display controller is
> > > > > instantiated. I rely on the default polling infrastructure provided by
> > > > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > > > this is far more than you kernel boot time.
> > > > > 
> > > > > Do anyone see a solution to reduce this delay (without changing the
> > > > > polling interval). I thought we could add a notifier infrastructure to
> > > > > the DRM panel framework, but I'm not sure this is how you want things
> > > > > done...
> > > > 
> > > > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > > > yet. This will automatically take care of ordering things in a way that
> > > > DRM/KMS will only be initialized after the panel has been probed.
> > > 
> > > Actually I'd like to avoid doing this with a deferred probe, because,
> > > AFAIU, the remote endpoint is not tightly linked with the display
> > > controller driver (I mean the display controller can still be
> > > initialized without having a display connected on it).
> > > Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> > > the same RGB connector and I'd like to use it in a near future.
> > > Returning -EPROBE_DEFER in case of several devices connected on the
> > > same connector implies that I'll have to wait for all the remote
> > > end-points to be available before my display controller could be
> > > instantiated.
> > > 
> > > While this could be acceptable when all drivers are statically linked
> > > in the kernel, it might be problematic when you're using modules,
> > > meaning that you won't be able to display anything on your LCD panel
> > > until your HDMI bridge module has been loaded.
> > 
> > No. HDMI should be using proper hotplugging anyway, hence it should be
> > always be loaded anyway. You're in for a world of pain if you think you
> > can run DRM with a driver that's composed of separate kernel modules.
> 
> I was talking about the external RGB to HDMI encoder, should the driver
> for this encoder (which is not on On Chip block) be compiled
> statically too ?
> 
> > 
> > Also if you don't want to use deferred probe, then you're in for the
> > full hotplugging panel dance and that implies that you need to fix a
> > bunch of things in DRM (one being the framebuffer console instantiation
> > that I referred to in the other thread).
> 
> For now, I wait until there is a device connected on the RGB connector
> (connector status set to connector_status_connected) before creating an
> fbdev. It might not be the cleanest way to solve this issue, but it
> works :-).

Yeah, I guess that's one way to do it. But it's tricky to get right when
you have several outputs. Which one should be considered the primary and
trigger fbdev creation?

> > You also can't be using the
> > current device tree bindings because they all assume a dependency from
> > the display controller/output to the panel. For hotplugging you'd need
> > the dependency the other way around (the panel needs to refer to the
> > output by phandle).
> 
> Here [1] is a proposal for notification support in the drm_panel
> infrastructure (which is not that complicated), and here [2] is how
> I use it in my atmel-hlcdc driver to generate hotplug events.
> 
> Let me know if you want me to submit a proper patch series...
> 
> Best Regards,
> 
> Boris
> 
> [1]http://code.bulix.org/scq4g3-86804
> [2]http://code.bulix.org/7dg501-86805

Those look interesting. Any chance you could look into how to do the
same without resorting to notifiers?

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140821/076454eb/attachment.sig>

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21 10:32             ` Andrzej Hajda
@ 2014-08-21 13:21               ` Thierry Reding
  -1 siblings, 0 replies; 103+ messages in thread
From: Thierry Reding @ 2014-08-21 13:21 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: Boris BREZILLON, Mark Rutland, linux-pwm, Samuel Ortiz,
	Pawel Moll, devicetree, Lee Jones, Ian Campbell, Nicolas Ferre,
	linux-kernel, dri-devel, Rob Herring, Ludovic Desroches,
	Alexandre Belloni, Laurent Pinchart, Bo Shen, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Andrew Victor,
	linux-arm-kernel

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

On Thu, Aug 21, 2014 at 12:32:43PM +0200, Andrzej Hajda wrote:
> On 08/21/2014 11:52 AM, Thierry Reding wrote:
> > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> >> On Thu, 21 Aug 2014 11:04:07 +0200
> >> Thierry Reding <thierry.reding@gmail.com> wrote:
> >>
> >>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> >>>> Hi Ludovic,
> >>>>
> >>>> On Thu, 21 Aug 2014 10:16:19 +0200
> >>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> >>>>
> >>>>> Hi Boris,
> >>>>>
> >>>>> You can add
> >>>>>
> >>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> >>>>
> >>>> Thanks for testing this driver.
> >>>>
> >>>>>
> >>>>> Only one issue but not related to your patches, you can't display
> >>>>> quickly the bootup logo since the panel detection takes too much
> >>>>> time.
> >>>>
> >>>> Yes, actually this is related to the device probe order: the
> >>>> hlcdc-display-controller device is probed before the simple-panel, thus
> >>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
> >>>> check for panel availability) when the display controller is
> >>>> instantiated. I rely on the default polling infrastructure provided by
> >>>> the DRM/KMS framework which polls for a new connector every 10s, and
> >>>> this is far more than you kernel boot time.
> >>>>
> >>>> Do anyone see a solution to reduce this delay (without changing the
> >>>> polling interval). I thought we could add a notifier infrastructure to
> >>>> the DRM panel framework, but I'm not sure this is how you want things
> >>>> done...
> >>>
> >>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> >>> yet. This will automatically take care of ordering things in a way that
> >>> DRM/KMS will only be initialized after the panel has been probed.
> >>
> >> Actually I'd like to avoid doing this with a deferred probe, because,
> >> AFAIU, the remote endpoint is not tightly linked with the display
> >> controller driver (I mean the display controller can still be
> >> initialized without having a display connected on it).
> >> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> >> the same RGB connector and I'd like to use it in a near future.
> >> Returning -EPROBE_DEFER in case of several devices connected on the
> >> same connector implies that I'll have to wait for all the remote
> >> end-points to be available before my display controller could be
> >> instantiated.
> >>
> >> While this could be acceptable when all drivers are statically linked
> >> in the kernel, it might be problematic when you're using modules,
> >> meaning that you won't be able to display anything on your LCD panel
> >> until your HDMI bridge module has been loaded.
> > 
> > No. HDMI should be using proper hotplugging anyway, hence it should be
> > always be loaded anyway. You're in for a world of pain if you think you
> > can run DRM with a driver that's composed of separate kernel modules.
> > 
> > Also if you don't want to use deferred probe, then you're in for the
> > full hotplugging panel dance and that implies that you need to fix a
> > bunch of things in DRM (one being the framebuffer console instantiation
> > that I referred to in the other thread). You also can't be using the
> > current device tree bindings because they all assume a dependency from
> > the display controller/output to the panel. For hotplugging you'd need
> > the dependency the other way around (the panel needs to refer to the
> > output by phandle).
> 
> I have tested panel as a module in exynos-dsi + panel-s6e8aa0
> configuration, everything works. There is a workaround for fb console
> not being reconfigurable, but it does not make thing worse than before.
> And I do not see a problem with phandles, ie in DT they point both ways,
> according to binding advices at the time, but in the code it is display
> controller/encoder which is looking for the panel.

That works because it's DSI. And we have attach/detach callbacks for
DSI. We don't have those for regular panels, so we'd need to find a way
to add that.

The way that this currently works is that an encoder/connector driver
looks up the panel and attaches it to itself. If you allow panels to be
hotpluggable, then they have no knowledge about what they are connected
to, so there needs to be a way to inject that knowledge so that they can
attach to a connector.

Thierry

[-- Attachment #2: Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 13:21               ` Thierry Reding
  0 siblings, 0 replies; 103+ messages in thread
From: Thierry Reding @ 2014-08-21 13:21 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Aug 21, 2014 at 12:32:43PM +0200, Andrzej Hajda wrote:
> On 08/21/2014 11:52 AM, Thierry Reding wrote:
> > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> >> On Thu, 21 Aug 2014 11:04:07 +0200
> >> Thierry Reding <thierry.reding@gmail.com> wrote:
> >>
> >>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> >>>> Hi Ludovic,
> >>>>
> >>>> On Thu, 21 Aug 2014 10:16:19 +0200
> >>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> >>>>
> >>>>> Hi Boris,
> >>>>>
> >>>>> You can add
> >>>>>
> >>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> >>>>
> >>>> Thanks for testing this driver.
> >>>>
> >>>>>
> >>>>> Only one issue but not related to your patches, you can't display
> >>>>> quickly the bootup logo since the panel detection takes too much
> >>>>> time.
> >>>>
> >>>> Yes, actually this is related to the device probe order: the
> >>>> hlcdc-display-controller device is probed before the simple-panel, thus
> >>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
> >>>> check for panel availability) when the display controller is
> >>>> instantiated. I rely on the default polling infrastructure provided by
> >>>> the DRM/KMS framework which polls for a new connector every 10s, and
> >>>> this is far more than you kernel boot time.
> >>>>
> >>>> Do anyone see a solution to reduce this delay (without changing the
> >>>> polling interval). I thought we could add a notifier infrastructure to
> >>>> the DRM panel framework, but I'm not sure this is how you want things
> >>>> done...
> >>>
> >>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> >>> yet. This will automatically take care of ordering things in a way that
> >>> DRM/KMS will only be initialized after the panel has been probed.
> >>
> >> Actually I'd like to avoid doing this with a deferred probe, because,
> >> AFAIU, the remote endpoint is not tightly linked with the display
> >> controller driver (I mean the display controller can still be
> >> initialized without having a display connected on it).
> >> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> >> the same RGB connector and I'd like to use it in a near future.
> >> Returning -EPROBE_DEFER in case of several devices connected on the
> >> same connector implies that I'll have to wait for all the remote
> >> end-points to be available before my display controller could be
> >> instantiated.
> >>
> >> While this could be acceptable when all drivers are statically linked
> >> in the kernel, it might be problematic when you're using modules,
> >> meaning that you won't be able to display anything on your LCD panel
> >> until your HDMI bridge module has been loaded.
> > 
> > No. HDMI should be using proper hotplugging anyway, hence it should be
> > always be loaded anyway. You're in for a world of pain if you think you
> > can run DRM with a driver that's composed of separate kernel modules.
> > 
> > Also if you don't want to use deferred probe, then you're in for the
> > full hotplugging panel dance and that implies that you need to fix a
> > bunch of things in DRM (one being the framebuffer console instantiation
> > that I referred to in the other thread). You also can't be using the
> > current device tree bindings because they all assume a dependency from
> > the display controller/output to the panel. For hotplugging you'd need
> > the dependency the other way around (the panel needs to refer to the
> > output by phandle).
> 
> I have tested panel as a module in exynos-dsi + panel-s6e8aa0
> configuration, everything works. There is a workaround for fb console
> not being reconfigurable, but it does not make thing worse than before.
> And I do not see a problem with phandles, ie in DT they point both ways,
> according to binding advices at the time, but in the code it is display
> controller/encoder which is looking for the panel.

That works because it's DSI. And we have attach/detach callbacks for
DSI. We don't have those for regular panels, so we'd need to find a way
to add that.

The way that this currently works is that an encoder/connector driver
looks up the panel and attaches it to itself. If you allow panels to be
hotpluggable, then they have no knowledge about what they are connected
to, so there needs to be a way to inject that knowledge so that they can
attach to a connector.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140821/2c3db61d/attachment.sig>

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21 13:16               ` Thierry Reding
@ 2014-08-21 13:30                 ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21 13:30 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Laurent Pinchart, Mark Rutland, devicetree,
	Pawel Moll, Ian Campbell, linux-kernel, Rob Herring, Bo Shen,
	Kumar Gala, linux-arm-kernel

On Thu, 21 Aug 2014 15:16:08 +0200
Thierry Reding <thierry.reding@gmail.com> wrote:

> On Thu, Aug 21, 2014 at 03:06:00PM +0200, Boris BREZILLON wrote:
> > On Thu, 21 Aug 2014 11:52:03 +0200
> > Thierry Reding <thierry.reding@gmail.com> wrote:
> > 
> > > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> > > > On Thu, 21 Aug 2014 11:04:07 +0200
> > > > Thierry Reding <thierry.reding@gmail.com> wrote:
> > > > 
> > > > > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > > > > Hi Ludovic,
> > > > > > 
> > > > > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > > > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > > > > 
> > > > > > > Hi Boris,
> > > > > > > 
> > > > > > > You can add
> > > > > > > 
> > > > > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > > > > 
> > > > > > Thanks for testing this driver.
> > > > > > 
> > > > > > > 
> > > > > > > Only one issue but not related to your patches, you can't display
> > > > > > > quickly the bootup logo since the panel detection takes too much
> > > > > > > time.
> > > > > > 
> > > > > > Yes, actually this is related to the device probe order: the
> > > > > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > > > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > > > > check for panel availability) when the display controller is
> > > > > > instantiated. I rely on the default polling infrastructure provided by
> > > > > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > > > > this is far more than you kernel boot time.
> > > > > > 
> > > > > > Do anyone see a solution to reduce this delay (without changing the
> > > > > > polling interval). I thought we could add a notifier infrastructure to
> > > > > > the DRM panel framework, but I'm not sure this is how you want things
> > > > > > done...
> > > > > 
> > > > > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > > > > yet. This will automatically take care of ordering things in a way that
> > > > > DRM/KMS will only be initialized after the panel has been probed.
> > > > 
> > > > Actually I'd like to avoid doing this with a deferred probe, because,
> > > > AFAIU, the remote endpoint is not tightly linked with the display
> > > > controller driver (I mean the display controller can still be
> > > > initialized without having a display connected on it).
> > > > Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> > > > the same RGB connector and I'd like to use it in a near future.
> > > > Returning -EPROBE_DEFER in case of several devices connected on the
> > > > same connector implies that I'll have to wait for all the remote
> > > > end-points to be available before my display controller could be
> > > > instantiated.
> > > > 
> > > > While this could be acceptable when all drivers are statically linked
> > > > in the kernel, it might be problematic when you're using modules,
> > > > meaning that you won't be able to display anything on your LCD panel
> > > > until your HDMI bridge module has been loaded.
> > > 
> > > No. HDMI should be using proper hotplugging anyway, hence it should be
> > > always be loaded anyway. You're in for a world of pain if you think you
> > > can run DRM with a driver that's composed of separate kernel modules.
> > 
> > I was talking about the external RGB to HDMI encoder, should the driver
> > for this encoder (which is not on On Chip block) be compiled
> > statically too ?
> > 
> > > 
> > > Also if you don't want to use deferred probe, then you're in for the
> > > full hotplugging panel dance and that implies that you need to fix a
> > > bunch of things in DRM (one being the framebuffer console instantiation
> > > that I referred to in the other thread).
> > 
> > For now, I wait until there is a device connected on the RGB connector
> > (connector status set to connector_status_connected) before creating an
> > fbdev. It might not be the cleanest way to solve this issue, but it
> > works :-).
> 
> Yeah, I guess that's one way to do it. But it's tricky to get right when
> you have several outputs. Which one should be considered the primary and
> trigger fbdev creation?

I take the first valid one :D (which indeed is not really reliable when
you have several output devices). I guess marking one output as the
primary output in the DT is not an acceptable solution either because
it describes a configuration rather than an HW capability.

Still opened to any suggestions regarding this issue ;-).

> 
> > > You also can't be using the
> > > current device tree bindings because they all assume a dependency from
> > > the display controller/output to the panel. For hotplugging you'd need
> > > the dependency the other way around (the panel needs to refer to the
> > > output by phandle).
> > 
> > Here [1] is a proposal for notification support in the drm_panel
> > infrastructure (which is not that complicated), and here [2] is how
> > I use it in my atmel-hlcdc driver to generate hotplug events.
> > 
> > Let me know if you want me to submit a proper patch series...
> > 
> > Best Regards,
> > 
> > Boris
> > 
> > [1]http://code.bulix.org/scq4g3-86804
> > [2]http://code.bulix.org/7dg501-86805
> 
> Those look interesting. Any chance you could look into how to do the
> same without resorting to notifiers?

Sure, it should be pretty easy too.

I also had a look at the component framework, but I'm not sure it
applies in this context. AFAIU it is designed to delay a master device
registration until all it's slaves are ready to act upon.


-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 13:30                 ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21 13:30 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 21 Aug 2014 15:16:08 +0200
Thierry Reding <thierry.reding@gmail.com> wrote:

> On Thu, Aug 21, 2014 at 03:06:00PM +0200, Boris BREZILLON wrote:
> > On Thu, 21 Aug 2014 11:52:03 +0200
> > Thierry Reding <thierry.reding@gmail.com> wrote:
> > 
> > > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> > > > On Thu, 21 Aug 2014 11:04:07 +0200
> > > > Thierry Reding <thierry.reding@gmail.com> wrote:
> > > > 
> > > > > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > > > > Hi Ludovic,
> > > > > > 
> > > > > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > > > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > > > > 
> > > > > > > Hi Boris,
> > > > > > > 
> > > > > > > You can add
> > > > > > > 
> > > > > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > > > > 
> > > > > > Thanks for testing this driver.
> > > > > > 
> > > > > > > 
> > > > > > > Only one issue but not related to your patches, you can't display
> > > > > > > quickly the bootup logo since the panel detection takes too much
> > > > > > > time.
> > > > > > 
> > > > > > Yes, actually this is related to the device probe order: the
> > > > > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > > > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > > > > check for panel availability) when the display controller is
> > > > > > instantiated. I rely on the default polling infrastructure provided by
> > > > > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > > > > this is far more than you kernel boot time.
> > > > > > 
> > > > > > Do anyone see a solution to reduce this delay (without changing the
> > > > > > polling interval). I thought we could add a notifier infrastructure to
> > > > > > the DRM panel framework, but I'm not sure this is how you want things
> > > > > > done...
> > > > > 
> > > > > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > > > > yet. This will automatically take care of ordering things in a way that
> > > > > DRM/KMS will only be initialized after the panel has been probed.
> > > > 
> > > > Actually I'd like to avoid doing this with a deferred probe, because,
> > > > AFAIU, the remote endpoint is not tightly linked with the display
> > > > controller driver (I mean the display controller can still be
> > > > initialized without having a display connected on it).
> > > > Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> > > > the same RGB connector and I'd like to use it in a near future.
> > > > Returning -EPROBE_DEFER in case of several devices connected on the
> > > > same connector implies that I'll have to wait for all the remote
> > > > end-points to be available before my display controller could be
> > > > instantiated.
> > > > 
> > > > While this could be acceptable when all drivers are statically linked
> > > > in the kernel, it might be problematic when you're using modules,
> > > > meaning that you won't be able to display anything on your LCD panel
> > > > until your HDMI bridge module has been loaded.
> > > 
> > > No. HDMI should be using proper hotplugging anyway, hence it should be
> > > always be loaded anyway. You're in for a world of pain if you think you
> > > can run DRM with a driver that's composed of separate kernel modules.
> > 
> > I was talking about the external RGB to HDMI encoder, should the driver
> > for this encoder (which is not on On Chip block) be compiled
> > statically too ?
> > 
> > > 
> > > Also if you don't want to use deferred probe, then you're in for the
> > > full hotplugging panel dance and that implies that you need to fix a
> > > bunch of things in DRM (one being the framebuffer console instantiation
> > > that I referred to in the other thread).
> > 
> > For now, I wait until there is a device connected on the RGB connector
> > (connector status set to connector_status_connected) before creating an
> > fbdev. It might not be the cleanest way to solve this issue, but it
> > works :-).
> 
> Yeah, I guess that's one way to do it. But it's tricky to get right when
> you have several outputs. Which one should be considered the primary and
> trigger fbdev creation?

I take the first valid one :D (which indeed is not really reliable when
you have several output devices). I guess marking one output as the
primary output in the DT is not an acceptable solution either because
it describes a configuration rather than an HW capability.

Still opened to any suggestions regarding this issue ;-).

> 
> > > You also can't be using the
> > > current device tree bindings because they all assume a dependency from
> > > the display controller/output to the panel. For hotplugging you'd need
> > > the dependency the other way around (the panel needs to refer to the
> > > output by phandle).
> > 
> > Here [1] is a proposal for notification support in the drm_panel
> > infrastructure (which is not that complicated), and here [2] is how
> > I use it in my atmel-hlcdc driver to generate hotplug events.
> > 
> > Let me know if you want me to submit a proper patch series...
> > 
> > Best Regards,
> > 
> > Boris
> > 
> > [1]http://code.bulix.org/scq4g3-86804
> > [2]http://code.bulix.org/7dg501-86805
> 
> Those look interesting. Any chance you could look into how to do the
> same without resorting to notifiers?

Sure, it should be pretty easy too.

I also had a look at the component framework, but I'm not sure it
applies in this context. AFAIU it is designed to delay a master device
registration until all it's slaves are ready to act upon.


-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21 13:16               ` Thierry Reding
  (?)
@ 2014-08-21 14:32                 ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21 14:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Laurent Pinchart, Mark Rutland, devicetree,
	Pawel Moll, Ian Campbell, linux-kernel, Rob Herring, Bo Shen,
	Kumar Gala, linux-arm-kernel

On Thu, 21 Aug 2014 15:16:08 +0200
Thierry Reding <thierry.reding@gmail.com> wrote:

> On Thu, Aug 21, 2014 at 03:06:00PM +0200, Boris BREZILLON wrote:
> > On Thu, 21 Aug 2014 11:52:03 +0200
> > Thierry Reding <thierry.reding@gmail.com> wrote:
> > 
> > > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> > > > On Thu, 21 Aug 2014 11:04:07 +0200
> > > > Thierry Reding <thierry.reding@gmail.com> wrote:
> > > > 
> > > > > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > > > > Hi Ludovic,
> > > > > > 
> > > > > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > > > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > > > > 
> > > > > > > Hi Boris,
> > > > > > > 
> > > > > > > You can add
> > > > > > > 
> > > > > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > > > > 
> > > > > > Thanks for testing this driver.
> > > > > > 
> > > > > > > 
> > > > > > > Only one issue but not related to your patches, you can't display
> > > > > > > quickly the bootup logo since the panel detection takes too much
> > > > > > > time.
> > > > > > 
> > > > > > Yes, actually this is related to the device probe order: the
> > > > > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > > > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > > > > check for panel availability) when the display controller is
> > > > > > instantiated. I rely on the default polling infrastructure provided by
> > > > > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > > > > this is far more than you kernel boot time.
> > > > > > 
> > > > > > Do anyone see a solution to reduce this delay (without changing the
> > > > > > polling interval). I thought we could add a notifier infrastructure to
> > > > > > the DRM panel framework, but I'm not sure this is how you want things
> > > > > > done...
> > > > > 
> > > > > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > > > > yet. This will automatically take care of ordering things in a way that
> > > > > DRM/KMS will only be initialized after the panel has been probed.
> > > > 
> > > > Actually I'd like to avoid doing this with a deferred probe, because,
> > > > AFAIU, the remote endpoint is not tightly linked with the display
> > > > controller driver (I mean the display controller can still be
> > > > initialized without having a display connected on it).
> > > > Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> > > > the same RGB connector and I'd like to use it in a near future.
> > > > Returning -EPROBE_DEFER in case of several devices connected on the
> > > > same connector implies that I'll have to wait for all the remote
> > > > end-points to be available before my display controller could be
> > > > instantiated.
> > > > 
> > > > While this could be acceptable when all drivers are statically linked
> > > > in the kernel, it might be problematic when you're using modules,
> > > > meaning that you won't be able to display anything on your LCD panel
> > > > until your HDMI bridge module has been loaded.
> > > 
> > > No. HDMI should be using proper hotplugging anyway, hence it should be
> > > always be loaded anyway. You're in for a world of pain if you think you
> > > can run DRM with a driver that's composed of separate kernel modules.
> > 
> > I was talking about the external RGB to HDMI encoder, should the driver
> > for this encoder (which is not on On Chip block) be compiled
> > statically too ?
> > 
> > > 
> > > Also if you don't want to use deferred probe, then you're in for the
> > > full hotplugging panel dance and that implies that you need to fix a
> > > bunch of things in DRM (one being the framebuffer console instantiation
> > > that I referred to in the other thread).
> > 
> > For now, I wait until there is a device connected on the RGB connector
> > (connector status set to connector_status_connected) before creating an
> > fbdev. It might not be the cleanest way to solve this issue, but it
> > works :-).
> 
> Yeah, I guess that's one way to do it. But it's tricky to get right when
> you have several outputs. Which one should be considered the primary and
> trigger fbdev creation?
> 
> > > You also can't be using the
> > > current device tree bindings because they all assume a dependency from
> > > the display controller/output to the panel. For hotplugging you'd need
> > > the dependency the other way around (the panel needs to refer to the
> > > output by phandle).
> > 
> > Here [1] is a proposal for notification support in the drm_panel
> > infrastructure (which is not that complicated), and here [2] is how
> > I use it in my atmel-hlcdc driver to generate hotplug events.
> > 
> > Let me know if you want me to submit a proper patch series...
> > 
> > Best Regards,
> > 
> > Boris
> > 
> > [1]http://code.bulix.org/scq4g3-86804
> > [2]http://code.bulix.org/7dg501-86805
> 
> Those look interesting. Any chance you could look into how to do the
> same without resorting to notifiers?

A new version without notifiers usage:
http://code.bulix.org/7urh8v-86806


-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 14:32                 ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21 14:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Mark Rutland, linux-pwm, Samuel Ortiz, Pawel Moll, devicetree,
	Lee Jones, Ian Campbell, Nicolas Ferre, linux-kernel, dri-devel,
	Rob Herring, Ludovic Desroches, Alexandre Belloni,
	Laurent Pinchart, Bo Shen, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Andrew Victor,
	linux-arm-kernel

On Thu, 21 Aug 2014 15:16:08 +0200
Thierry Reding <thierry.reding@gmail.com> wrote:

> On Thu, Aug 21, 2014 at 03:06:00PM +0200, Boris BREZILLON wrote:
> > On Thu, 21 Aug 2014 11:52:03 +0200
> > Thierry Reding <thierry.reding@gmail.com> wrote:
> > 
> > > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> > > > On Thu, 21 Aug 2014 11:04:07 +0200
> > > > Thierry Reding <thierry.reding@gmail.com> wrote:
> > > > 
> > > > > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > > > > Hi Ludovic,
> > > > > > 
> > > > > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > > > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > > > > 
> > > > > > > Hi Boris,
> > > > > > > 
> > > > > > > You can add
> > > > > > > 
> > > > > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > > > > 
> > > > > > Thanks for testing this driver.
> > > > > > 
> > > > > > > 
> > > > > > > Only one issue but not related to your patches, you can't display
> > > > > > > quickly the bootup logo since the panel detection takes too much
> > > > > > > time.
> > > > > > 
> > > > > > Yes, actually this is related to the device probe order: the
> > > > > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > > > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > > > > check for panel availability) when the display controller is
> > > > > > instantiated. I rely on the default polling infrastructure provided by
> > > > > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > > > > this is far more than you kernel boot time.
> > > > > > 
> > > > > > Do anyone see a solution to reduce this delay (without changing the
> > > > > > polling interval). I thought we could add a notifier infrastructure to
> > > > > > the DRM panel framework, but I'm not sure this is how you want things
> > > > > > done...
> > > > > 
> > > > > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > > > > yet. This will automatically take care of ordering things in a way that
> > > > > DRM/KMS will only be initialized after the panel has been probed.
> > > > 
> > > > Actually I'd like to avoid doing this with a deferred probe, because,
> > > > AFAIU, the remote endpoint is not tightly linked with the display
> > > > controller driver (I mean the display controller can still be
> > > > initialized without having a display connected on it).
> > > > Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> > > > the same RGB connector and I'd like to use it in a near future.
> > > > Returning -EPROBE_DEFER in case of several devices connected on the
> > > > same connector implies that I'll have to wait for all the remote
> > > > end-points to be available before my display controller could be
> > > > instantiated.
> > > > 
> > > > While this could be acceptable when all drivers are statically linked
> > > > in the kernel, it might be problematic when you're using modules,
> > > > meaning that you won't be able to display anything on your LCD panel
> > > > until your HDMI bridge module has been loaded.
> > > 
> > > No. HDMI should be using proper hotplugging anyway, hence it should be
> > > always be loaded anyway. You're in for a world of pain if you think you
> > > can run DRM with a driver that's composed of separate kernel modules.
> > 
> > I was talking about the external RGB to HDMI encoder, should the driver
> > for this encoder (which is not on On Chip block) be compiled
> > statically too ?
> > 
> > > 
> > > Also if you don't want to use deferred probe, then you're in for the
> > > full hotplugging panel dance and that implies that you need to fix a
> > > bunch of things in DRM (one being the framebuffer console instantiation
> > > that I referred to in the other thread).
> > 
> > For now, I wait until there is a device connected on the RGB connector
> > (connector status set to connector_status_connected) before creating an
> > fbdev. It might not be the cleanest way to solve this issue, but it
> > works :-).
> 
> Yeah, I guess that's one way to do it. But it's tricky to get right when
> you have several outputs. Which one should be considered the primary and
> trigger fbdev creation?
> 
> > > You also can't be using the
> > > current device tree bindings because they all assume a dependency from
> > > the display controller/output to the panel. For hotplugging you'd need
> > > the dependency the other way around (the panel needs to refer to the
> > > output by phandle).
> > 
> > Here [1] is a proposal for notification support in the drm_panel
> > infrastructure (which is not that complicated), and here [2] is how
> > I use it in my atmel-hlcdc driver to generate hotplug events.
> > 
> > Let me know if you want me to submit a proper patch series...
> > 
> > Best Regards,
> > 
> > Boris
> > 
> > [1]http://code.bulix.org/scq4g3-86804
> > [2]http://code.bulix.org/7dg501-86805
> 
> Those look interesting. Any chance you could look into how to do the
> same without resorting to notifiers?

A new version without notifiers usage:
http://code.bulix.org/7urh8v-86806


-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 14:32                 ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21 14:32 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 21 Aug 2014 15:16:08 +0200
Thierry Reding <thierry.reding@gmail.com> wrote:

> On Thu, Aug 21, 2014 at 03:06:00PM +0200, Boris BREZILLON wrote:
> > On Thu, 21 Aug 2014 11:52:03 +0200
> > Thierry Reding <thierry.reding@gmail.com> wrote:
> > 
> > > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> > > > On Thu, 21 Aug 2014 11:04:07 +0200
> > > > Thierry Reding <thierry.reding@gmail.com> wrote:
> > > > 
> > > > > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > > > > Hi Ludovic,
> > > > > > 
> > > > > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > > > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > > > > 
> > > > > > > Hi Boris,
> > > > > > > 
> > > > > > > You can add
> > > > > > > 
> > > > > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > > > > 
> > > > > > Thanks for testing this driver.
> > > > > > 
> > > > > > > 
> > > > > > > Only one issue but not related to your patches, you can't display
> > > > > > > quickly the bootup logo since the panel detection takes too much
> > > > > > > time.
> > > > > > 
> > > > > > Yes, actually this is related to the device probe order: the
> > > > > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > > > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > > > > check for panel availability) when the display controller is
> > > > > > instantiated. I rely on the default polling infrastructure provided by
> > > > > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > > > > this is far more than you kernel boot time.
> > > > > > 
> > > > > > Do anyone see a solution to reduce this delay (without changing the
> > > > > > polling interval). I thought we could add a notifier infrastructure to
> > > > > > the DRM panel framework, but I'm not sure this is how you want things
> > > > > > done...
> > > > > 
> > > > > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > > > > yet. This will automatically take care of ordering things in a way that
> > > > > DRM/KMS will only be initialized after the panel has been probed.
> > > > 
> > > > Actually I'd like to avoid doing this with a deferred probe, because,
> > > > AFAIU, the remote endpoint is not tightly linked with the display
> > > > controller driver (I mean the display controller can still be
> > > > initialized without having a display connected on it).
> > > > Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> > > > the same RGB connector and I'd like to use it in a near future.
> > > > Returning -EPROBE_DEFER in case of several devices connected on the
> > > > same connector implies that I'll have to wait for all the remote
> > > > end-points to be available before my display controller could be
> > > > instantiated.
> > > > 
> > > > While this could be acceptable when all drivers are statically linked
> > > > in the kernel, it might be problematic when you're using modules,
> > > > meaning that you won't be able to display anything on your LCD panel
> > > > until your HDMI bridge module has been loaded.
> > > 
> > > No. HDMI should be using proper hotplugging anyway, hence it should be
> > > always be loaded anyway. You're in for a world of pain if you think you
> > > can run DRM with a driver that's composed of separate kernel modules.
> > 
> > I was talking about the external RGB to HDMI encoder, should the driver
> > for this encoder (which is not on On Chip block) be compiled
> > statically too ?
> > 
> > > 
> > > Also if you don't want to use deferred probe, then you're in for the
> > > full hotplugging panel dance and that implies that you need to fix a
> > > bunch of things in DRM (one being the framebuffer console instantiation
> > > that I referred to in the other thread).
> > 
> > For now, I wait until there is a device connected on the RGB connector
> > (connector status set to connector_status_connected) before creating an
> > fbdev. It might not be the cleanest way to solve this issue, but it
> > works :-).
> 
> Yeah, I guess that's one way to do it. But it's tricky to get right when
> you have several outputs. Which one should be considered the primary and
> trigger fbdev creation?
> 
> > > You also can't be using the
> > > current device tree bindings because they all assume a dependency from
> > > the display controller/output to the panel. For hotplugging you'd need
> > > the dependency the other way around (the panel needs to refer to the
> > > output by phandle).
> > 
> > Here [1] is a proposal for notification support in the drm_panel
> > infrastructure (which is not that complicated), and here [2] is how
> > I use it in my atmel-hlcdc driver to generate hotplug events.
> > 
> > Let me know if you want me to submit a proper patch series...
> > 
> > Best Regards,
> > 
> > Boris
> > 
> > [1]http://code.bulix.org/scq4g3-86804
> > [2]http://code.bulix.org/7dg501-86805
> 
> Those look interesting. Any chance you could look into how to do the
> same without resorting to notifiers?

A new version without notifiers usage:
http://code.bulix.org/7urh8v-86806


-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21 13:21               ` Thierry Reding
@ 2014-08-21 15:04                 ` Andrzej Hajda
  -1 siblings, 0 replies; 103+ messages in thread
From: Andrzej Hajda @ 2014-08-21 15:04 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Boris BREZILLON, Mark Rutland, linux-pwm, Samuel Ortiz,
	Pawel Moll, devicetree, Lee Jones, Ian Campbell, Nicolas Ferre,
	linux-kernel, dri-devel, Rob Herring, Ludovic Desroches,
	Alexandre Belloni, Laurent Pinchart, Bo Shen, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Andrew Victor,
	linux-arm-kernel

On 08/21/2014 03:21 PM, Thierry Reding wrote:
> On Thu, Aug 21, 2014 at 12:32:43PM +0200, Andrzej Hajda wrote:
>> On 08/21/2014 11:52 AM, Thierry Reding wrote:
>>> On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
>>>> On Thu, 21 Aug 2014 11:04:07 +0200
>>>> Thierry Reding <thierry.reding@gmail.com> wrote:
>>>>
>>>>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
>>>>>> Hi Ludovic,
>>>>>>
>>>>>> On Thu, 21 Aug 2014 10:16:19 +0200
>>>>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
>>>>>>
>>>>>>> Hi Boris,
>>>>>>>
>>>>>>> You can add
>>>>>>>
>>>>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>>>>>> Thanks for testing this driver.
>>>>>>
>>>>>>> Only one issue but not related to your patches, you can't display
>>>>>>> quickly the bootup logo since the panel detection takes too much
>>>>>>> time.
>>>>>> Yes, actually this is related to the device probe order: the
>>>>>> hlcdc-display-controller device is probed before the simple-panel, thus
>>>>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
>>>>>> check for panel availability) when the display controller is
>>>>>> instantiated. I rely on the default polling infrastructure provided by
>>>>>> the DRM/KMS framework which polls for a new connector every 10s, and
>>>>>> this is far more than you kernel boot time.
>>>>>>
>>>>>> Do anyone see a solution to reduce this delay (without changing the
>>>>>> polling interval). I thought we could add a notifier infrastructure to
>>>>>> the DRM panel framework, but I'm not sure this is how you want things
>>>>>> done...
>>>>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
>>>>> yet. This will automatically take care of ordering things in a way that
>>>>> DRM/KMS will only be initialized after the panel has been probed.
>>>> Actually I'd like to avoid doing this with a deferred probe, because,
>>>> AFAIU, the remote endpoint is not tightly linked with the display
>>>> controller driver (I mean the display controller can still be
>>>> initialized without having a display connected on it).
>>>> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
>>>> the same RGB connector and I'd like to use it in a near future.
>>>> Returning -EPROBE_DEFER in case of several devices connected on the
>>>> same connector implies that I'll have to wait for all the remote
>>>> end-points to be available before my display controller could be
>>>> instantiated.
>>>>
>>>> While this could be acceptable when all drivers are statically linked
>>>> in the kernel, it might be problematic when you're using modules,
>>>> meaning that you won't be able to display anything on your LCD panel
>>>> until your HDMI bridge module has been loaded.
>>> No. HDMI should be using proper hotplugging anyway, hence it should be
>>> always be loaded anyway. You're in for a world of pain if you think you
>>> can run DRM with a driver that's composed of separate kernel modules.
>>>
>>> Also if you don't want to use deferred probe, then you're in for the
>>> full hotplugging panel dance and that implies that you need to fix a
>>> bunch of things in DRM (one being the framebuffer console instantiation
>>> that I referred to in the other thread). You also can't be using the
>>> current device tree bindings because they all assume a dependency from
>>> the display controller/output to the panel. For hotplugging you'd need
>>> the dependency the other way around (the panel needs to refer to the
>>> output by phandle).
>> I have tested panel as a module in exynos-dsi + panel-s6e8aa0
>> configuration, everything works. There is a workaround for fb console
>> not being reconfigurable, but it does not make thing worse than before.
>> And I do not see a problem with phandles, ie in DT they point both ways,
>> according to binding advices at the time, but in the code it is display
>> controller/encoder which is looking for the panel.
> That works because it's DSI. And we have attach/detach callbacks for
> DSI. We don't have those for regular panels, so we'd need to find a way
> to add that.

Maybe I have misread your answer, but you showed it as very
difficult/painful
process: "hotplugging panel dance", "fix a bunch of things in DRM". In fact
we are missing here only good notifications about panel appearance.

>
> The way that this currently works is that an encoder/connector driver
> looks up the panel and attaches it to itself. If you allow panels to be
> hotpluggable, then they have no knowledge about what they are connected
> to, so there needs to be a way to inject that knowledge so that they can
> attach to a connector.

I do not understand that. Currently it is the connector who looks for
the panel
and attaches it.
So the scenario, after adding panel tracking, could be:
 - encoder parses its phandle to panel, and start tracking appearance of
the panel
identified by this phandle,
 - when panel appears encoder callback is called, and encoder attaches
the panel,
 - when panel wants to disappear encoder callback is called, encoder
detaches the panel.

All this I have already presented together with generic interface
tracker [1].

Regards
Andrzej

[1]: https://lkml.org/lkml/2014/4/30/345


>
> Thierry


^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 15:04                 ` Andrzej Hajda
  0 siblings, 0 replies; 103+ messages in thread
From: Andrzej Hajda @ 2014-08-21 15:04 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/21/2014 03:21 PM, Thierry Reding wrote:
> On Thu, Aug 21, 2014 at 12:32:43PM +0200, Andrzej Hajda wrote:
>> On 08/21/2014 11:52 AM, Thierry Reding wrote:
>>> On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
>>>> On Thu, 21 Aug 2014 11:04:07 +0200
>>>> Thierry Reding <thierry.reding@gmail.com> wrote:
>>>>
>>>>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
>>>>>> Hi Ludovic,
>>>>>>
>>>>>> On Thu, 21 Aug 2014 10:16:19 +0200
>>>>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
>>>>>>
>>>>>>> Hi Boris,
>>>>>>>
>>>>>>> You can add
>>>>>>>
>>>>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>>>>>> Thanks for testing this driver.
>>>>>>
>>>>>>> Only one issue but not related to your patches, you can't display
>>>>>>> quickly the bootup logo since the panel detection takes too much
>>>>>>> time.
>>>>>> Yes, actually this is related to the device probe order: the
>>>>>> hlcdc-display-controller device is probed before the simple-panel, thus
>>>>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
>>>>>> check for panel availability) when the display controller is
>>>>>> instantiated. I rely on the default polling infrastructure provided by
>>>>>> the DRM/KMS framework which polls for a new connector every 10s, and
>>>>>> this is far more than you kernel boot time.
>>>>>>
>>>>>> Do anyone see a solution to reduce this delay (without changing the
>>>>>> polling interval). I thought we could add a notifier infrastructure to
>>>>>> the DRM panel framework, but I'm not sure this is how you want things
>>>>>> done...
>>>>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
>>>>> yet. This will automatically take care of ordering things in a way that
>>>>> DRM/KMS will only be initialized after the panel has been probed.
>>>> Actually I'd like to avoid doing this with a deferred probe, because,
>>>> AFAIU, the remote endpoint is not tightly linked with the display
>>>> controller driver (I mean the display controller can still be
>>>> initialized without having a display connected on it).
>>>> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
>>>> the same RGB connector and I'd like to use it in a near future.
>>>> Returning -EPROBE_DEFER in case of several devices connected on the
>>>> same connector implies that I'll have to wait for all the remote
>>>> end-points to be available before my display controller could be
>>>> instantiated.
>>>>
>>>> While this could be acceptable when all drivers are statically linked
>>>> in the kernel, it might be problematic when you're using modules,
>>>> meaning that you won't be able to display anything on your LCD panel
>>>> until your HDMI bridge module has been loaded.
>>> No. HDMI should be using proper hotplugging anyway, hence it should be
>>> always be loaded anyway. You're in for a world of pain if you think you
>>> can run DRM with a driver that's composed of separate kernel modules.
>>>
>>> Also if you don't want to use deferred probe, then you're in for the
>>> full hotplugging panel dance and that implies that you need to fix a
>>> bunch of things in DRM (one being the framebuffer console instantiation
>>> that I referred to in the other thread). You also can't be using the
>>> current device tree bindings because they all assume a dependency from
>>> the display controller/output to the panel. For hotplugging you'd need
>>> the dependency the other way around (the panel needs to refer to the
>>> output by phandle).
>> I have tested panel as a module in exynos-dsi + panel-s6e8aa0
>> configuration, everything works. There is a workaround for fb console
>> not being reconfigurable, but it does not make thing worse than before.
>> And I do not see a problem with phandles, ie in DT they point both ways,
>> according to binding advices at the time, but in the code it is display
>> controller/encoder which is looking for the panel.
> That works because it's DSI. And we have attach/detach callbacks for
> DSI. We don't have those for regular panels, so we'd need to find a way
> to add that.

Maybe I have misread your answer, but you showed it as very
difficult/painful
process: "hotplugging panel dance", "fix a bunch of things in DRM". In fact
we are missing here only good notifications about panel appearance.

>
> The way that this currently works is that an encoder/connector driver
> looks up the panel and attaches it to itself. If you allow panels to be
> hotpluggable, then they have no knowledge about what they are connected
> to, so there needs to be a way to inject that knowledge so that they can
> attach to a connector.

I do not understand that. Currently it is the connector who looks for
the panel
and attaches it.
So the scenario, after adding panel tracking, could be:
 - encoder parses its phandle to panel, and start tracking appearance of
the panel
identified by this phandle,
 - when panel appears encoder callback is called, and encoder attaches
the panel,
 - when panel wants to disappear encoder callback is called, encoder
detaches the panel.

All this I have already presented together with generic interface
tracker [1].

Regards
Andrzej

[1]: https://lkml.org/lkml/2014/4/30/345


>
> Thierry

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21 15:04                 ` Andrzej Hajda
  (?)
@ 2014-08-21 15:30                   ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21 15:30 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: Thierry Reding, Mark Rutland, linux-pwm, Samuel Ortiz,
	Pawel Moll, devicetree, Lee Jones, Ian Campbell, Nicolas Ferre,
	linux-kernel, dri-devel, Rob Herring, Ludovic Desroches,
	Alexandre Belloni, Laurent Pinchart, Bo Shen, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Andrew Victor,
	linux-arm-kernel

On Thu, 21 Aug 2014 17:04:34 +0200
Andrzej Hajda <a.hajda@samsung.com> wrote:

> On 08/21/2014 03:21 PM, Thierry Reding wrote:
> > On Thu, Aug 21, 2014 at 12:32:43PM +0200, Andrzej Hajda wrote:
> >> On 08/21/2014 11:52 AM, Thierry Reding wrote:
> >>> On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> >>>> On Thu, 21 Aug 2014 11:04:07 +0200
> >>>> Thierry Reding <thierry.reding@gmail.com> wrote:
> >>>>
> >>>>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> >>>>>> Hi Ludovic,
> >>>>>>
> >>>>>> On Thu, 21 Aug 2014 10:16:19 +0200
> >>>>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> >>>>>>
> >>>>>>> Hi Boris,
> >>>>>>>
> >>>>>>> You can add
> >>>>>>>
> >>>>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> >>>>>> Thanks for testing this driver.
> >>>>>>
> >>>>>>> Only one issue but not related to your patches, you can't display
> >>>>>>> quickly the bootup logo since the panel detection takes too much
> >>>>>>> time.
> >>>>>> Yes, actually this is related to the device probe order: the
> >>>>>> hlcdc-display-controller device is probed before the simple-panel, thus
> >>>>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
> >>>>>> check for panel availability) when the display controller is
> >>>>>> instantiated. I rely on the default polling infrastructure provided by
> >>>>>> the DRM/KMS framework which polls for a new connector every 10s, and
> >>>>>> this is far more than you kernel boot time.
> >>>>>>
> >>>>>> Do anyone see a solution to reduce this delay (without changing the
> >>>>>> polling interval). I thought we could add a notifier infrastructure to
> >>>>>> the DRM panel framework, but I'm not sure this is how you want things
> >>>>>> done...
> >>>>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> >>>>> yet. This will automatically take care of ordering things in a way that
> >>>>> DRM/KMS will only be initialized after the panel has been probed.
> >>>> Actually I'd like to avoid doing this with a deferred probe, because,
> >>>> AFAIU, the remote endpoint is not tightly linked with the display
> >>>> controller driver (I mean the display controller can still be
> >>>> initialized without having a display connected on it).
> >>>> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> >>>> the same RGB connector and I'd like to use it in a near future.
> >>>> Returning -EPROBE_DEFER in case of several devices connected on the
> >>>> same connector implies that I'll have to wait for all the remote
> >>>> end-points to be available before my display controller could be
> >>>> instantiated.
> >>>>
> >>>> While this could be acceptable when all drivers are statically linked
> >>>> in the kernel, it might be problematic when you're using modules,
> >>>> meaning that you won't be able to display anything on your LCD panel
> >>>> until your HDMI bridge module has been loaded.
> >>> No. HDMI should be using proper hotplugging anyway, hence it should be
> >>> always be loaded anyway. You're in for a world of pain if you think you
> >>> can run DRM with a driver that's composed of separate kernel modules.
> >>>
> >>> Also if you don't want to use deferred probe, then you're in for the
> >>> full hotplugging panel dance and that implies that you need to fix a
> >>> bunch of things in DRM (one being the framebuffer console instantiation
> >>> that I referred to in the other thread). You also can't be using the
> >>> current device tree bindings because they all assume a dependency from
> >>> the display controller/output to the panel. For hotplugging you'd need
> >>> the dependency the other way around (the panel needs to refer to the
> >>> output by phandle).
> >> I have tested panel as a module in exynos-dsi + panel-s6e8aa0
> >> configuration, everything works. There is a workaround for fb console
> >> not being reconfigurable, but it does not make thing worse than before.
> >> And I do not see a problem with phandles, ie in DT they point both ways,
> >> according to binding advices at the time, but in the code it is display
> >> controller/encoder which is looking for the panel.
> > That works because it's DSI. And we have attach/detach callbacks for
> > DSI. We don't have those for regular panels, so we'd need to find a way
> > to add that.
> 
> Maybe I have misread your answer, but you showed it as very
> difficult/painful
> process: "hotplugging panel dance", "fix a bunch of things in DRM". In fact
> we are missing here only good notifications about panel appearance.
> 
> >
> > The way that this currently works is that an encoder/connector driver
> > looks up the panel and attaches it to itself. If you allow panels to be
> > hotpluggable, then they have no knowledge about what they are connected
> > to, so there needs to be a way to inject that knowledge so that they can
> > attach to a connector.
> 
> I do not understand that. Currently it is the connector who looks for
> the panel
> and attaches it.
> So the scenario, after adding panel tracking, could be:
>  - encoder parses its phandle to panel, and start tracking appearance of
> the panel
> identified by this phandle,
>  - when panel appears encoder callback is called, and encoder attaches
> the panel,
>  - when panel wants to disappear encoder callback is called, encoder
> detaches the panel.
> 
> All this I have already presented together with generic interface
> tracker [1].

Well, your attempt at doing a generic tracker framework sounds
interesting, but given the answer you've got from greg-kh and Russel,
I'd say this patch series is in a dead-end (unless there are other
versions I haven't seen yet).

How about implementing a specific notifier interface for the drm_panel
framework first, and move to your generic implementation if it gets
accepted.

These are the two proposal I sent to Thierry:

http://code.bulix.org/scq4g3-86804 (v1)

and

http://code.bulix.org/7urh8v-86806 (v2)

Feel free to propose any alternative to those implementations.

Best Regards,

Boris


-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 15:30                   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21 15:30 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: Mark Rutland, linux-pwm, Samuel Ortiz, Pawel Moll, devicetree,
	Ludovic Desroches, Ian Campbell, Nicolas Ferre, linux-kernel,
	dri-devel, Rob Herring, Alexandre Belloni, Laurent Pinchart,
	Bo Shen, Kumar Gala, Jean-Christophe Plagniol-Villard, Lee Jones,
	Andrew Victor, linux-arm-kernel

On Thu, 21 Aug 2014 17:04:34 +0200
Andrzej Hajda <a.hajda@samsung.com> wrote:

> On 08/21/2014 03:21 PM, Thierry Reding wrote:
> > On Thu, Aug 21, 2014 at 12:32:43PM +0200, Andrzej Hajda wrote:
> >> On 08/21/2014 11:52 AM, Thierry Reding wrote:
> >>> On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> >>>> On Thu, 21 Aug 2014 11:04:07 +0200
> >>>> Thierry Reding <thierry.reding@gmail.com> wrote:
> >>>>
> >>>>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> >>>>>> Hi Ludovic,
> >>>>>>
> >>>>>> On Thu, 21 Aug 2014 10:16:19 +0200
> >>>>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> >>>>>>
> >>>>>>> Hi Boris,
> >>>>>>>
> >>>>>>> You can add
> >>>>>>>
> >>>>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> >>>>>> Thanks for testing this driver.
> >>>>>>
> >>>>>>> Only one issue but not related to your patches, you can't display
> >>>>>>> quickly the bootup logo since the panel detection takes too much
> >>>>>>> time.
> >>>>>> Yes, actually this is related to the device probe order: the
> >>>>>> hlcdc-display-controller device is probed before the simple-panel, thus
> >>>>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
> >>>>>> check for panel availability) when the display controller is
> >>>>>> instantiated. I rely on the default polling infrastructure provided by
> >>>>>> the DRM/KMS framework which polls for a new connector every 10s, and
> >>>>>> this is far more than you kernel boot time.
> >>>>>>
> >>>>>> Do anyone see a solution to reduce this delay (without changing the
> >>>>>> polling interval). I thought we could add a notifier infrastructure to
> >>>>>> the DRM panel framework, but I'm not sure this is how you want things
> >>>>>> done...
> >>>>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> >>>>> yet. This will automatically take care of ordering things in a way that
> >>>>> DRM/KMS will only be initialized after the panel has been probed.
> >>>> Actually I'd like to avoid doing this with a deferred probe, because,
> >>>> AFAIU, the remote endpoint is not tightly linked with the display
> >>>> controller driver (I mean the display controller can still be
> >>>> initialized without having a display connected on it).
> >>>> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> >>>> the same RGB connector and I'd like to use it in a near future.
> >>>> Returning -EPROBE_DEFER in case of several devices connected on the
> >>>> same connector implies that I'll have to wait for all the remote
> >>>> end-points to be available before my display controller could be
> >>>> instantiated.
> >>>>
> >>>> While this could be acceptable when all drivers are statically linked
> >>>> in the kernel, it might be problematic when you're using modules,
> >>>> meaning that you won't be able to display anything on your LCD panel
> >>>> until your HDMI bridge module has been loaded.
> >>> No. HDMI should be using proper hotplugging anyway, hence it should be
> >>> always be loaded anyway. You're in for a world of pain if you think you
> >>> can run DRM with a driver that's composed of separate kernel modules.
> >>>
> >>> Also if you don't want to use deferred probe, then you're in for the
> >>> full hotplugging panel dance and that implies that you need to fix a
> >>> bunch of things in DRM (one being the framebuffer console instantiation
> >>> that I referred to in the other thread). You also can't be using the
> >>> current device tree bindings because they all assume a dependency from
> >>> the display controller/output to the panel. For hotplugging you'd need
> >>> the dependency the other way around (the panel needs to refer to the
> >>> output by phandle).
> >> I have tested panel as a module in exynos-dsi + panel-s6e8aa0
> >> configuration, everything works. There is a workaround for fb console
> >> not being reconfigurable, but it does not make thing worse than before.
> >> And I do not see a problem with phandles, ie in DT they point both ways,
> >> according to binding advices at the time, but in the code it is display
> >> controller/encoder which is looking for the panel.
> > That works because it's DSI. And we have attach/detach callbacks for
> > DSI. We don't have those for regular panels, so we'd need to find a way
> > to add that.
> 
> Maybe I have misread your answer, but you showed it as very
> difficult/painful
> process: "hotplugging panel dance", "fix a bunch of things in DRM". In fact
> we are missing here only good notifications about panel appearance.
> 
> >
> > The way that this currently works is that an encoder/connector driver
> > looks up the panel and attaches it to itself. If you allow panels to be
> > hotpluggable, then they have no knowledge about what they are connected
> > to, so there needs to be a way to inject that knowledge so that they can
> > attach to a connector.
> 
> I do not understand that. Currently it is the connector who looks for
> the panel
> and attaches it.
> So the scenario, after adding panel tracking, could be:
>  - encoder parses its phandle to panel, and start tracking appearance of
> the panel
> identified by this phandle,
>  - when panel appears encoder callback is called, and encoder attaches
> the panel,
>  - when panel wants to disappear encoder callback is called, encoder
> detaches the panel.
> 
> All this I have already presented together with generic interface
> tracker [1].

Well, your attempt at doing a generic tracker framework sounds
interesting, but given the answer you've got from greg-kh and Russel,
I'd say this patch series is in a dead-end (unless there are other
versions I haven't seen yet).

How about implementing a specific notifier interface for the drm_panel
framework first, and move to your generic implementation if it gets
accepted.

These are the two proposal I sent to Thierry:

http://code.bulix.org/scq4g3-86804 (v1)

and

http://code.bulix.org/7urh8v-86806 (v2)

Feel free to propose any alternative to those implementations.

Best Regards,

Boris


-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 15:30                   ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21 15:30 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 21 Aug 2014 17:04:34 +0200
Andrzej Hajda <a.hajda@samsung.com> wrote:

> On 08/21/2014 03:21 PM, Thierry Reding wrote:
> > On Thu, Aug 21, 2014 at 12:32:43PM +0200, Andrzej Hajda wrote:
> >> On 08/21/2014 11:52 AM, Thierry Reding wrote:
> >>> On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> >>>> On Thu, 21 Aug 2014 11:04:07 +0200
> >>>> Thierry Reding <thierry.reding@gmail.com> wrote:
> >>>>
> >>>>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> >>>>>> Hi Ludovic,
> >>>>>>
> >>>>>> On Thu, 21 Aug 2014 10:16:19 +0200
> >>>>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> >>>>>>
> >>>>>>> Hi Boris,
> >>>>>>>
> >>>>>>> You can add
> >>>>>>>
> >>>>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> >>>>>> Thanks for testing this driver.
> >>>>>>
> >>>>>>> Only one issue but not related to your patches, you can't display
> >>>>>>> quickly the bootup logo since the panel detection takes too much
> >>>>>>> time.
> >>>>>> Yes, actually this is related to the device probe order: the
> >>>>>> hlcdc-display-controller device is probed before the simple-panel, thus
> >>>>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
> >>>>>> check for panel availability) when the display controller is
> >>>>>> instantiated. I rely on the default polling infrastructure provided by
> >>>>>> the DRM/KMS framework which polls for a new connector every 10s, and
> >>>>>> this is far more than you kernel boot time.
> >>>>>>
> >>>>>> Do anyone see a solution to reduce this delay (without changing the
> >>>>>> polling interval). I thought we could add a notifier infrastructure to
> >>>>>> the DRM panel framework, but I'm not sure this is how you want things
> >>>>>> done...
> >>>>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> >>>>> yet. This will automatically take care of ordering things in a way that
> >>>>> DRM/KMS will only be initialized after the panel has been probed.
> >>>> Actually I'd like to avoid doing this with a deferred probe, because,
> >>>> AFAIU, the remote endpoint is not tightly linked with the display
> >>>> controller driver (I mean the display controller can still be
> >>>> initialized without having a display connected on it).
> >>>> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> >>>> the same RGB connector and I'd like to use it in a near future.
> >>>> Returning -EPROBE_DEFER in case of several devices connected on the
> >>>> same connector implies that I'll have to wait for all the remote
> >>>> end-points to be available before my display controller could be
> >>>> instantiated.
> >>>>
> >>>> While this could be acceptable when all drivers are statically linked
> >>>> in the kernel, it might be problematic when you're using modules,
> >>>> meaning that you won't be able to display anything on your LCD panel
> >>>> until your HDMI bridge module has been loaded.
> >>> No. HDMI should be using proper hotplugging anyway, hence it should be
> >>> always be loaded anyway. You're in for a world of pain if you think you
> >>> can run DRM with a driver that's composed of separate kernel modules.
> >>>
> >>> Also if you don't want to use deferred probe, then you're in for the
> >>> full hotplugging panel dance and that implies that you need to fix a
> >>> bunch of things in DRM (one being the framebuffer console instantiation
> >>> that I referred to in the other thread). You also can't be using the
> >>> current device tree bindings because they all assume a dependency from
> >>> the display controller/output to the panel. For hotplugging you'd need
> >>> the dependency the other way around (the panel needs to refer to the
> >>> output by phandle).
> >> I have tested panel as a module in exynos-dsi + panel-s6e8aa0
> >> configuration, everything works. There is a workaround for fb console
> >> not being reconfigurable, but it does not make thing worse than before.
> >> And I do not see a problem with phandles, ie in DT they point both ways,
> >> according to binding advices at the time, but in the code it is display
> >> controller/encoder which is looking for the panel.
> > That works because it's DSI. And we have attach/detach callbacks for
> > DSI. We don't have those for regular panels, so we'd need to find a way
> > to add that.
> 
> Maybe I have misread your answer, but you showed it as very
> difficult/painful
> process: "hotplugging panel dance", "fix a bunch of things in DRM". In fact
> we are missing here only good notifications about panel appearance.
> 
> >
> > The way that this currently works is that an encoder/connector driver
> > looks up the panel and attaches it to itself. If you allow panels to be
> > hotpluggable, then they have no knowledge about what they are connected
> > to, so there needs to be a way to inject that knowledge so that they can
> > attach to a connector.
> 
> I do not understand that. Currently it is the connector who looks for
> the panel
> and attaches it.
> So the scenario, after adding panel tracking, could be:
>  - encoder parses its phandle to panel, and start tracking appearance of
> the panel
> identified by this phandle,
>  - when panel appears encoder callback is called, and encoder attaches
> the panel,
>  - when panel wants to disappear encoder callback is called, encoder
> detaches the panel.
> 
> All this I have already presented together with generic interface
> tracker [1].

Well, your attempt at doing a generic tracker framework sounds
interesting, but given the answer you've got from greg-kh and Russel,
I'd say this patch series is in a dead-end (unless there are other
versions I haven't seen yet).

How about implementing a specific notifier interface for the drm_panel
framework first, and move to your generic implementation if it gets
accepted.

These are the two proposal I sent to Thierry:

http://code.bulix.org/scq4g3-86804 (v1)

and

http://code.bulix.org/7urh8v-86806 (v2)

Feel free to propose any alternative to those implementations.

Best Regards,

Boris


-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21 15:30                   ` Boris BREZILLON
  (?)
@ 2014-08-21 16:10                     ` Andrzej Hajda
  -1 siblings, 0 replies; 103+ messages in thread
From: Andrzej Hajda @ 2014-08-21 16:10 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Thierry Reding, Mark Rutland, linux-pwm, Samuel Ortiz,
	Pawel Moll, devicetree, Lee Jones, Ian Campbell, Nicolas Ferre,
	linux-kernel, dri-devel, Rob Herring, Ludovic Desroches,
	Alexandre Belloni, Laurent Pinchart, Bo Shen, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Andrew Victor,
	linux-arm-kernel

On 08/21/2014 05:30 PM, Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 17:04:34 +0200
> Andrzej Hajda <a.hajda@samsung.com> wrote:
>
>> On 08/21/2014 03:21 PM, Thierry Reding wrote:
>>> On Thu, Aug 21, 2014 at 12:32:43PM +0200, Andrzej Hajda wrote:
>>>> On 08/21/2014 11:52 AM, Thierry Reding wrote:
>>>>> On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
>>>>>> On Thu, 21 Aug 2014 11:04:07 +0200
>>>>>> Thierry Reding <thierry.reding@gmail.com> wrote:
>>>>>>
>>>>>>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
>>>>>>>> Hi Ludovic,
>>>>>>>>
>>>>>>>> On Thu, 21 Aug 2014 10:16:19 +0200
>>>>>>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
>>>>>>>>
>>>>>>>>> Hi Boris,
>>>>>>>>>
>>>>>>>>> You can add
>>>>>>>>>
>>>>>>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>>>>>>>> Thanks for testing this driver.
>>>>>>>>
>>>>>>>>> Only one issue but not related to your patches, you can't display
>>>>>>>>> quickly the bootup logo since the panel detection takes too much
>>>>>>>>> time.
>>>>>>>> Yes, actually this is related to the device probe order: the
>>>>>>>> hlcdc-display-controller device is probed before the simple-panel, thus
>>>>>>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
>>>>>>>> check for panel availability) when the display controller is
>>>>>>>> instantiated. I rely on the default polling infrastructure provided by
>>>>>>>> the DRM/KMS framework which polls for a new connector every 10s, and
>>>>>>>> this is far more than you kernel boot time.
>>>>>>>>
>>>>>>>> Do anyone see a solution to reduce this delay (without changing the
>>>>>>>> polling interval). I thought we could add a notifier infrastructure to
>>>>>>>> the DRM panel framework, but I'm not sure this is how you want things
>>>>>>>> done...
>>>>>>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
>>>>>>> yet. This will automatically take care of ordering things in a way that
>>>>>>> DRM/KMS will only be initialized after the panel has been probed.
>>>>>> Actually I'd like to avoid doing this with a deferred probe, because,
>>>>>> AFAIU, the remote endpoint is not tightly linked with the display
>>>>>> controller driver (I mean the display controller can still be
>>>>>> initialized without having a display connected on it).
>>>>>> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
>>>>>> the same RGB connector and I'd like to use it in a near future.
>>>>>> Returning -EPROBE_DEFER in case of several devices connected on the
>>>>>> same connector implies that I'll have to wait for all the remote
>>>>>> end-points to be available before my display controller could be
>>>>>> instantiated.
>>>>>>
>>>>>> While this could be acceptable when all drivers are statically linked
>>>>>> in the kernel, it might be problematic when you're using modules,
>>>>>> meaning that you won't be able to display anything on your LCD panel
>>>>>> until your HDMI bridge module has been loaded.
>>>>> No. HDMI should be using proper hotplugging anyway, hence it should be
>>>>> always be loaded anyway. You're in for a world of pain if you think you
>>>>> can run DRM with a driver that's composed of separate kernel modules.
>>>>>
>>>>> Also if you don't want to use deferred probe, then you're in for the
>>>>> full hotplugging panel dance and that implies that you need to fix a
>>>>> bunch of things in DRM (one being the framebuffer console instantiation
>>>>> that I referred to in the other thread). You also can't be using the
>>>>> current device tree bindings because they all assume a dependency from
>>>>> the display controller/output to the panel. For hotplugging you'd need
>>>>> the dependency the other way around (the panel needs to refer to the
>>>>> output by phandle).
>>>> I have tested panel as a module in exynos-dsi + panel-s6e8aa0
>>>> configuration, everything works. There is a workaround for fb console
>>>> not being reconfigurable, but it does not make thing worse than before.
>>>> And I do not see a problem with phandles, ie in DT they point both ways,
>>>> according to binding advices at the time, but in the code it is display
>>>> controller/encoder which is looking for the panel.
>>> That works because it's DSI. And we have attach/detach callbacks for
>>> DSI. We don't have those for regular panels, so we'd need to find a way
>>> to add that.
>> Maybe I have misread your answer, but you showed it as very
>> difficult/painful
>> process: "hotplugging panel dance", "fix a bunch of things in DRM". In fact
>> we are missing here only good notifications about panel appearance.
>>
>>> The way that this currently works is that an encoder/connector driver
>>> looks up the panel and attaches it to itself. If you allow panels to be
>>> hotpluggable, then they have no knowledge about what they are connected
>>> to, so there needs to be a way to inject that knowledge so that they can
>>> attach to a connector.
>> I do not understand that. Currently it is the connector who looks for
>> the panel
>> and attaches it.
>> So the scenario, after adding panel tracking, could be:
>>  - encoder parses its phandle to panel, and start tracking appearance of
>> the panel
>> identified by this phandle,
>>  - when panel appears encoder callback is called, and encoder attaches
>> the panel,
>>  - when panel wants to disappear encoder callback is called, encoder
>> detaches the panel.
>>
>> All this I have already presented together with generic interface
>> tracker [1].
> Well, your attempt at doing a generic tracker framework sounds
> interesting, but given the answer you've got from greg-kh and Russel,
> I'd say this patch series is in a dead-end (unless there are other
> versions I haven't seen yet).

As I remember I have positively answered Russel's concerns.
Greg had only concern in style 'why another framework'.

>
> How about implementing a specific notifier interface for the drm_panel
> framework first, and move to your generic implementation if it gets
> accepted.

The interface tracker can be also used for that as well, ie it can be
added to
drm_panel framework, until it wont be accepted for wider set of clients.

>
> These are the two proposal I sent to Thierry:
>
> http://code.bulix.org/scq4g3-86804 (v1)
>
> and
>
> http://code.bulix.org/7urh8v-86806 (v2)
>
> Feel free to propose any alternative to those implementations.
>
> Best Regards,
>
> Boris
>
>
After quick look at the patches I guess they will not work correctly
if panel will be added before the observer registration.
Btw in case of other proposals could you include them in the mail,
my company firewall does not like http://code.bulix.org/.

Regards
Andrzej


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 16:10                     ` Andrzej Hajda
  0 siblings, 0 replies; 103+ messages in thread
From: Andrzej Hajda @ 2014-08-21 16:10 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Mark Rutland, linux-pwm, Samuel Ortiz, Pawel Moll, devicetree,
	Ludovic Desroches, Ian Campbell, Nicolas Ferre, linux-kernel,
	dri-devel, Rob Herring, Alexandre Belloni, Laurent Pinchart,
	Bo Shen, Kumar Gala, Jean-Christophe Plagniol-Villard, Lee Jones,
	Andrew Victor, linux-arm-kernel

On 08/21/2014 05:30 PM, Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 17:04:34 +0200
> Andrzej Hajda <a.hajda@samsung.com> wrote:
>
>> On 08/21/2014 03:21 PM, Thierry Reding wrote:
>>> On Thu, Aug 21, 2014 at 12:32:43PM +0200, Andrzej Hajda wrote:
>>>> On 08/21/2014 11:52 AM, Thierry Reding wrote:
>>>>> On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
>>>>>> On Thu, 21 Aug 2014 11:04:07 +0200
>>>>>> Thierry Reding <thierry.reding@gmail.com> wrote:
>>>>>>
>>>>>>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
>>>>>>>> Hi Ludovic,
>>>>>>>>
>>>>>>>> On Thu, 21 Aug 2014 10:16:19 +0200
>>>>>>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
>>>>>>>>
>>>>>>>>> Hi Boris,
>>>>>>>>>
>>>>>>>>> You can add
>>>>>>>>>
>>>>>>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>>>>>>>> Thanks for testing this driver.
>>>>>>>>
>>>>>>>>> Only one issue but not related to your patches, you can't display
>>>>>>>>> quickly the bootup logo since the panel detection takes too much
>>>>>>>>> time.
>>>>>>>> Yes, actually this is related to the device probe order: the
>>>>>>>> hlcdc-display-controller device is probed before the simple-panel, thus
>>>>>>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
>>>>>>>> check for panel availability) when the display controller is
>>>>>>>> instantiated. I rely on the default polling infrastructure provided by
>>>>>>>> the DRM/KMS framework which polls for a new connector every 10s, and
>>>>>>>> this is far more than you kernel boot time.
>>>>>>>>
>>>>>>>> Do anyone see a solution to reduce this delay (without changing the
>>>>>>>> polling interval). I thought we could add a notifier infrastructure to
>>>>>>>> the DRM panel framework, but I'm not sure this is how you want things
>>>>>>>> done...
>>>>>>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
>>>>>>> yet. This will automatically take care of ordering things in a way that
>>>>>>> DRM/KMS will only be initialized after the panel has been probed.
>>>>>> Actually I'd like to avoid doing this with a deferred probe, because,
>>>>>> AFAIU, the remote endpoint is not tightly linked with the display
>>>>>> controller driver (I mean the display controller can still be
>>>>>> initialized without having a display connected on it).
>>>>>> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
>>>>>> the same RGB connector and I'd like to use it in a near future.
>>>>>> Returning -EPROBE_DEFER in case of several devices connected on the
>>>>>> same connector implies that I'll have to wait for all the remote
>>>>>> end-points to be available before my display controller could be
>>>>>> instantiated.
>>>>>>
>>>>>> While this could be acceptable when all drivers are statically linked
>>>>>> in the kernel, it might be problematic when you're using modules,
>>>>>> meaning that you won't be able to display anything on your LCD panel
>>>>>> until your HDMI bridge module has been loaded.
>>>>> No. HDMI should be using proper hotplugging anyway, hence it should be
>>>>> always be loaded anyway. You're in for a world of pain if you think you
>>>>> can run DRM with a driver that's composed of separate kernel modules.
>>>>>
>>>>> Also if you don't want to use deferred probe, then you're in for the
>>>>> full hotplugging panel dance and that implies that you need to fix a
>>>>> bunch of things in DRM (one being the framebuffer console instantiation
>>>>> that I referred to in the other thread). You also can't be using the
>>>>> current device tree bindings because they all assume a dependency from
>>>>> the display controller/output to the panel. For hotplugging you'd need
>>>>> the dependency the other way around (the panel needs to refer to the
>>>>> output by phandle).
>>>> I have tested panel as a module in exynos-dsi + panel-s6e8aa0
>>>> configuration, everything works. There is a workaround for fb console
>>>> not being reconfigurable, but it does not make thing worse than before.
>>>> And I do not see a problem with phandles, ie in DT they point both ways,
>>>> according to binding advices at the time, but in the code it is display
>>>> controller/encoder which is looking for the panel.
>>> That works because it's DSI. And we have attach/detach callbacks for
>>> DSI. We don't have those for regular panels, so we'd need to find a way
>>> to add that.
>> Maybe I have misread your answer, but you showed it as very
>> difficult/painful
>> process: "hotplugging panel dance", "fix a bunch of things in DRM". In fact
>> we are missing here only good notifications about panel appearance.
>>
>>> The way that this currently works is that an encoder/connector driver
>>> looks up the panel and attaches it to itself. If you allow panels to be
>>> hotpluggable, then they have no knowledge about what they are connected
>>> to, so there needs to be a way to inject that knowledge so that they can
>>> attach to a connector.
>> I do not understand that. Currently it is the connector who looks for
>> the panel
>> and attaches it.
>> So the scenario, after adding panel tracking, could be:
>>  - encoder parses its phandle to panel, and start tracking appearance of
>> the panel
>> identified by this phandle,
>>  - when panel appears encoder callback is called, and encoder attaches
>> the panel,
>>  - when panel wants to disappear encoder callback is called, encoder
>> detaches the panel.
>>
>> All this I have already presented together with generic interface
>> tracker [1].
> Well, your attempt at doing a generic tracker framework sounds
> interesting, but given the answer you've got from greg-kh and Russel,
> I'd say this patch series is in a dead-end (unless there are other
> versions I haven't seen yet).

As I remember I have positively answered Russel's concerns.
Greg had only concern in style 'why another framework'.

>
> How about implementing a specific notifier interface for the drm_panel
> framework first, and move to your generic implementation if it gets
> accepted.

The interface tracker can be also used for that as well, ie it can be
added to
drm_panel framework, until it wont be accepted for wider set of clients.

>
> These are the two proposal I sent to Thierry:
>
> http://code.bulix.org/scq4g3-86804 (v1)
>
> and
>
> http://code.bulix.org/7urh8v-86806 (v2)
>
> Feel free to propose any alternative to those implementations.
>
> Best Regards,
>
> Boris
>
>
After quick look at the patches I guess they will not work correctly
if panel will be added before the observer registration.
Btw in case of other proposals could you include them in the mail,
my company firewall does not like http://code.bulix.org/.

Regards
Andrzej

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 16:10                     ` Andrzej Hajda
  0 siblings, 0 replies; 103+ messages in thread
From: Andrzej Hajda @ 2014-08-21 16:10 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/21/2014 05:30 PM, Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 17:04:34 +0200
> Andrzej Hajda <a.hajda@samsung.com> wrote:
>
>> On 08/21/2014 03:21 PM, Thierry Reding wrote:
>>> On Thu, Aug 21, 2014 at 12:32:43PM +0200, Andrzej Hajda wrote:
>>>> On 08/21/2014 11:52 AM, Thierry Reding wrote:
>>>>> On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
>>>>>> On Thu, 21 Aug 2014 11:04:07 +0200
>>>>>> Thierry Reding <thierry.reding@gmail.com> wrote:
>>>>>>
>>>>>>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
>>>>>>>> Hi Ludovic,
>>>>>>>>
>>>>>>>> On Thu, 21 Aug 2014 10:16:19 +0200
>>>>>>>> Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
>>>>>>>>
>>>>>>>>> Hi Boris,
>>>>>>>>>
>>>>>>>>> You can add
>>>>>>>>>
>>>>>>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
>>>>>>>> Thanks for testing this driver.
>>>>>>>>
>>>>>>>>> Only one issue but not related to your patches, you can't display
>>>>>>>>> quickly the bootup logo since the panel detection takes too much
>>>>>>>>> time.
>>>>>>>> Yes, actually this is related to the device probe order: the
>>>>>>>> hlcdc-display-controller device is probed before the simple-panel, thus
>>>>>>>> nothing is detected on the RGB connector (I use of_drm_find_panel to
>>>>>>>> check for panel availability) when the display controller is
>>>>>>>> instantiated. I rely on the default polling infrastructure provided by
>>>>>>>> the DRM/KMS framework which polls for a new connector every 10s, and
>>>>>>>> this is far more than you kernel boot time.
>>>>>>>>
>>>>>>>> Do anyone see a solution to reduce this delay (without changing the
>>>>>>>> polling interval). I thought we could add a notifier infrastructure to
>>>>>>>> the DRM panel framework, but I'm not sure this is how you want things
>>>>>>>> done...
>>>>>>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
>>>>>>> yet. This will automatically take care of ordering things in a way that
>>>>>>> DRM/KMS will only be initialized after the panel has been probed.
>>>>>> Actually I'd like to avoid doing this with a deferred probe, because,
>>>>>> AFAIU, the remote endpoint is not tightly linked with the display
>>>>>> controller driver (I mean the display controller can still be
>>>>>> initialized without having a display connected on it).
>>>>>> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
>>>>>> the same RGB connector and I'd like to use it in a near future.
>>>>>> Returning -EPROBE_DEFER in case of several devices connected on the
>>>>>> same connector implies that I'll have to wait for all the remote
>>>>>> end-points to be available before my display controller could be
>>>>>> instantiated.
>>>>>>
>>>>>> While this could be acceptable when all drivers are statically linked
>>>>>> in the kernel, it might be problematic when you're using modules,
>>>>>> meaning that you won't be able to display anything on your LCD panel
>>>>>> until your HDMI bridge module has been loaded.
>>>>> No. HDMI should be using proper hotplugging anyway, hence it should be
>>>>> always be loaded anyway. You're in for a world of pain if you think you
>>>>> can run DRM with a driver that's composed of separate kernel modules.
>>>>>
>>>>> Also if you don't want to use deferred probe, then you're in for the
>>>>> full hotplugging panel dance and that implies that you need to fix a
>>>>> bunch of things in DRM (one being the framebuffer console instantiation
>>>>> that I referred to in the other thread). You also can't be using the
>>>>> current device tree bindings because they all assume a dependency from
>>>>> the display controller/output to the panel. For hotplugging you'd need
>>>>> the dependency the other way around (the panel needs to refer to the
>>>>> output by phandle).
>>>> I have tested panel as a module in exynos-dsi + panel-s6e8aa0
>>>> configuration, everything works. There is a workaround for fb console
>>>> not being reconfigurable, but it does not make thing worse than before.
>>>> And I do not see a problem with phandles, ie in DT they point both ways,
>>>> according to binding advices at the time, but in the code it is display
>>>> controller/encoder which is looking for the panel.
>>> That works because it's DSI. And we have attach/detach callbacks for
>>> DSI. We don't have those for regular panels, so we'd need to find a way
>>> to add that.
>> Maybe I have misread your answer, but you showed it as very
>> difficult/painful
>> process: "hotplugging panel dance", "fix a bunch of things in DRM". In fact
>> we are missing here only good notifications about panel appearance.
>>
>>> The way that this currently works is that an encoder/connector driver
>>> looks up the panel and attaches it to itself. If you allow panels to be
>>> hotpluggable, then they have no knowledge about what they are connected
>>> to, so there needs to be a way to inject that knowledge so that they can
>>> attach to a connector.
>> I do not understand that. Currently it is the connector who looks for
>> the panel
>> and attaches it.
>> So the scenario, after adding panel tracking, could be:
>>  - encoder parses its phandle to panel, and start tracking appearance of
>> the panel
>> identified by this phandle,
>>  - when panel appears encoder callback is called, and encoder attaches
>> the panel,
>>  - when panel wants to disappear encoder callback is called, encoder
>> detaches the panel.
>>
>> All this I have already presented together with generic interface
>> tracker [1].
> Well, your attempt at doing a generic tracker framework sounds
> interesting, but given the answer you've got from greg-kh and Russel,
> I'd say this patch series is in a dead-end (unless there are other
> versions I haven't seen yet).

As I remember I have positively answered Russel's concerns.
Greg had only concern in style 'why another framework'.

>
> How about implementing a specific notifier interface for the drm_panel
> framework first, and move to your generic implementation if it gets
> accepted.

The interface tracker can be also used for that as well, ie it can be
added to
drm_panel framework, until it wont be accepted for wider set of clients.

>
> These are the two proposal I sent to Thierry:
>
> http://code.bulix.org/scq4g3-86804 (v1)
>
> and
>
> http://code.bulix.org/7urh8v-86806 (v2)
>
> Feel free to propose any alternative to those implementations.
>
> Best Regards,
>
> Boris
>
>
After quick look at the patches I guess they will not work correctly
if panel will be added before the observer registration.
Btw in case of other proposals could you include them in the mail,
my company firewall does not like http://code.bulix.org/.

Regards
Andrzej

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21 13:06             ` Boris BREZILLON
  (?)
@ 2014-08-21 17:08               ` Laurent Pinchart
  -1 siblings, 0 replies; 103+ messages in thread
From: Laurent Pinchart @ 2014-08-21 17:08 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Thierry Reding, Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Mark Rutland, devicetree, Pawel Moll,
	Ian Campbell, linux-kernel, Rob Herring, Bo Shen, Kumar Gala,
	linux-arm-kernel

Hi Boris,

On Thursday 21 August 2014 15:06:00 Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 11:52:03 +0200 Thierry Reding wrote:
> > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> >> On Thu, 21 Aug 2014 11:04:07 +0200 Thierry Reding wrote:
> >>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> >>>> On Thu, 21 Aug 2014 10:16:19 +0200 Ludovic Desroches wrote:
> >>>>> Hi Boris,
> >>>>> 
> >>>>> You can add
> >>>>> 
> >>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> >>>> 
> >>>> Thanks for testing this driver.
> >>>> 
> >>>>> Only one issue but not related to your patches, you can't display
> >>>>> quickly the bootup logo since the panel detection takes too much
> >>>>> time.
> >>>> 
> >>>> Yes, actually this is related to the device probe order: the
> >>>> hlcdc-display-controller device is probed before the simple-panel,
> >>>> thus nothing is detected on the RGB connector (I use
> >>>> of_drm_find_panel to check for panel availability) when the display
> >>>> controller is instantiated. I rely on the default polling
> >>>> infrastructure provided by the DRM/KMS framework which polls for a
> >>>> new connector every 10s, and this is far more than you kernel boot
> >>>> time.
> >>>> 
> >>>> Do anyone see a solution to reduce this delay (without changing the
> >>>> polling interval). I thought we could add a notifier infrastructure
> >>>> to the DRM panel framework, but I'm not sure this is how you want
> >>>> things done...
> >>> 
> >>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> >>> yet. This will automatically take care of ordering things in a way
> >>> that DRM/KMS will only be initialized after the panel has been probed.
> >> 
> >> Actually I'd like to avoid doing this with a deferred probe, because,
> >> AFAIU, the remote endpoint is not tightly linked with the display
> >> controller driver (I mean the display controller can still be
> >> initialized without having a display connected on it).
> >> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> >> the same RGB connector and I'd like to use it in a near future.
> >> Returning -EPROBE_DEFER in case of several devices connected on the
> >> same connector implies that I'll have to wait for all the remote
> >> end-points to be available before my display controller could be
> >> instantiated.
> >> 
> >> While this could be acceptable when all drivers are statically linked
> >> in the kernel, it might be problematic when you're using modules,
> >> meaning that you won't be able to display anything on your LCD panel
> >> until your HDMI bridge module has been loaded.
> > 
> > No. HDMI should be using proper hotplugging anyway, hence it should be
> > always be loaded anyway. You're in for a world of pain if you think you
> > can run DRM with a driver that's composed of separate kernel modules.
> 
> I was talking about the external RGB to HDMI encoder, should the driver
> for this encoder (which is not on On Chip block) be compiled
> statically too ?

Given the move to multiplatform kernels we need to aim for as few modules 
compiled in as possible. I'd say this includes HDMI encoders, panels and 
display controllers.

> > Also if you don't want to use deferred probe, then you're in for the
> > full hotplugging panel dance and that implies that you need to fix a
> > bunch of things in DRM (one being the framebuffer console instantiation
> > that I referred to in the other thread).
> 
> For now, I wait until there is a device connected on the RGB connector
> (connector status set to connector_status_connected) before creating an
> fbdev. It might not be the cleanest way to solve this issue, but it
> works :-).

Do you create a new drm_encoder at runtime for the HDMI encoder when it 
appears ? I thought the DRM core and API were not able to correctly cope with 
that.

> > You also can't be using the current device tree bindings because they all
> > assume a dependency from the display controller/output to the panel. For
> > hotplugging you'd need the dependency the other way around (the panel
> > needs to refer to the output by phandle).
> 
> Here [1] is a proposal for notification support in the drm_panel
> infrastructure (which is not that complicated), and here [2] is how
> I use it in my atmel-hlcdc driver to generate hotplug events.

Is there a way we could use the component framework for that ? I know that 
partial notification isn't supported at the moment, but Russell agreed it was 
a real use case that should be implemented at some point.

> Let me know if you want me to submit a proper patch series...
> 
> Best Regards,
> 
> Boris
> 
> [1]http://code.bulix.org/scq4g3-86804
> [2]http://code.bulix.org/7dg501-86805

-- 
Regards,

Laurent Pinchart


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 17:08               ` Laurent Pinchart
  0 siblings, 0 replies; 103+ messages in thread
From: Laurent Pinchart @ 2014-08-21 17:08 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Mark Rutland, linux-pwm, Samuel Ortiz, Pawel Moll,
	Ludovic Desroches, Lee Jones, Ian Campbell, Nicolas Ferre,
	linux-kernel, dri-devel, Rob Herring, Alexandre Belloni,
	linux-arm-kernel, Bo Shen, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Andrew Victor, devicetree

Hi Boris,

On Thursday 21 August 2014 15:06:00 Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 11:52:03 +0200 Thierry Reding wrote:
> > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> >> On Thu, 21 Aug 2014 11:04:07 +0200 Thierry Reding wrote:
> >>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> >>>> On Thu, 21 Aug 2014 10:16:19 +0200 Ludovic Desroches wrote:
> >>>>> Hi Boris,
> >>>>> 
> >>>>> You can add
> >>>>> 
> >>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> >>>> 
> >>>> Thanks for testing this driver.
> >>>> 
> >>>>> Only one issue but not related to your patches, you can't display
> >>>>> quickly the bootup logo since the panel detection takes too much
> >>>>> time.
> >>>> 
> >>>> Yes, actually this is related to the device probe order: the
> >>>> hlcdc-display-controller device is probed before the simple-panel,
> >>>> thus nothing is detected on the RGB connector (I use
> >>>> of_drm_find_panel to check for panel availability) when the display
> >>>> controller is instantiated. I rely on the default polling
> >>>> infrastructure provided by the DRM/KMS framework which polls for a
> >>>> new connector every 10s, and this is far more than you kernel boot
> >>>> time.
> >>>> 
> >>>> Do anyone see a solution to reduce this delay (without changing the
> >>>> polling interval). I thought we could add a notifier infrastructure
> >>>> to the DRM panel framework, but I'm not sure this is how you want
> >>>> things done...
> >>> 
> >>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> >>> yet. This will automatically take care of ordering things in a way
> >>> that DRM/KMS will only be initialized after the panel has been probed.
> >> 
> >> Actually I'd like to avoid doing this with a deferred probe, because,
> >> AFAIU, the remote endpoint is not tightly linked with the display
> >> controller driver (I mean the display controller can still be
> >> initialized without having a display connected on it).
> >> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> >> the same RGB connector and I'd like to use it in a near future.
> >> Returning -EPROBE_DEFER in case of several devices connected on the
> >> same connector implies that I'll have to wait for all the remote
> >> end-points to be available before my display controller could be
> >> instantiated.
> >> 
> >> While this could be acceptable when all drivers are statically linked
> >> in the kernel, it might be problematic when you're using modules,
> >> meaning that you won't be able to display anything on your LCD panel
> >> until your HDMI bridge module has been loaded.
> > 
> > No. HDMI should be using proper hotplugging anyway, hence it should be
> > always be loaded anyway. You're in for a world of pain if you think you
> > can run DRM with a driver that's composed of separate kernel modules.
> 
> I was talking about the external RGB to HDMI encoder, should the driver
> for this encoder (which is not on On Chip block) be compiled
> statically too ?

Given the move to multiplatform kernels we need to aim for as few modules 
compiled in as possible. I'd say this includes HDMI encoders, panels and 
display controllers.

> > Also if you don't want to use deferred probe, then you're in for the
> > full hotplugging panel dance and that implies that you need to fix a
> > bunch of things in DRM (one being the framebuffer console instantiation
> > that I referred to in the other thread).
> 
> For now, I wait until there is a device connected on the RGB connector
> (connector status set to connector_status_connected) before creating an
> fbdev. It might not be the cleanest way to solve this issue, but it
> works :-).

Do you create a new drm_encoder at runtime for the HDMI encoder when it 
appears ? I thought the DRM core and API were not able to correctly cope with 
that.

> > You also can't be using the current device tree bindings because they all
> > assume a dependency from the display controller/output to the panel. For
> > hotplugging you'd need the dependency the other way around (the panel
> > needs to refer to the output by phandle).
> 
> Here [1] is a proposal for notification support in the drm_panel
> infrastructure (which is not that complicated), and here [2] is how
> I use it in my atmel-hlcdc driver to generate hotplug events.

Is there a way we could use the component framework for that ? I know that 
partial notification isn't supported at the moment, but Russell agreed it was 
a real use case that should be implemented at some point.

> Let me know if you want me to submit a proper patch series...
> 
> Best Regards,
> 
> Boris
> 
> [1]http://code.bulix.org/scq4g3-86804
> [2]http://code.bulix.org/7dg501-86805

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 17:08               ` Laurent Pinchart
  0 siblings, 0 replies; 103+ messages in thread
From: Laurent Pinchart @ 2014-08-21 17:08 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Boris,

On Thursday 21 August 2014 15:06:00 Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 11:52:03 +0200 Thierry Reding wrote:
> > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> >> On Thu, 21 Aug 2014 11:04:07 +0200 Thierry Reding wrote:
> >>> On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> >>>> On Thu, 21 Aug 2014 10:16:19 +0200 Ludovic Desroches wrote:
> >>>>> Hi Boris,
> >>>>> 
> >>>>> You can add
> >>>>> 
> >>>>> Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> >>>> 
> >>>> Thanks for testing this driver.
> >>>> 
> >>>>> Only one issue but not related to your patches, you can't display
> >>>>> quickly the bootup logo since the panel detection takes too much
> >>>>> time.
> >>>> 
> >>>> Yes, actually this is related to the device probe order: the
> >>>> hlcdc-display-controller device is probed before the simple-panel,
> >>>> thus nothing is detected on the RGB connector (I use
> >>>> of_drm_find_panel to check for panel availability) when the display
> >>>> controller is instantiated. I rely on the default polling
> >>>> infrastructure provided by the DRM/KMS framework which polls for a
> >>>> new connector every 10s, and this is far more than you kernel boot
> >>>> time.
> >>>> 
> >>>> Do anyone see a solution to reduce this delay (without changing the
> >>>> polling interval). I thought we could add a notifier infrastructure
> >>>> to the DRM panel framework, but I'm not sure this is how you want
> >>>> things done...
> >>> 
> >>> Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> >>> yet. This will automatically take care of ordering things in a way
> >>> that DRM/KMS will only be initialized after the panel has been probed.
> >> 
> >> Actually I'd like to avoid doing this with a deferred probe, because,
> >> AFAIU, the remote endpoint is not tightly linked with the display
> >> controller driver (I mean the display controller can still be
> >> initialized without having a display connected on it).
> >> Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> >> the same RGB connector and I'd like to use it in a near future.
> >> Returning -EPROBE_DEFER in case of several devices connected on the
> >> same connector implies that I'll have to wait for all the remote
> >> end-points to be available before my display controller could be
> >> instantiated.
> >> 
> >> While this could be acceptable when all drivers are statically linked
> >> in the kernel, it might be problematic when you're using modules,
> >> meaning that you won't be able to display anything on your LCD panel
> >> until your HDMI bridge module has been loaded.
> > 
> > No. HDMI should be using proper hotplugging anyway, hence it should be
> > always be loaded anyway. You're in for a world of pain if you think you
> > can run DRM with a driver that's composed of separate kernel modules.
> 
> I was talking about the external RGB to HDMI encoder, should the driver
> for this encoder (which is not on On Chip block) be compiled
> statically too ?

Given the move to multiplatform kernels we need to aim for as few modules 
compiled in as possible. I'd say this includes HDMI encoders, panels and 
display controllers.

> > Also if you don't want to use deferred probe, then you're in for the
> > full hotplugging panel dance and that implies that you need to fix a
> > bunch of things in DRM (one being the framebuffer console instantiation
> > that I referred to in the other thread).
> 
> For now, I wait until there is a device connected on the RGB connector
> (connector status set to connector_status_connected) before creating an
> fbdev. It might not be the cleanest way to solve this issue, but it
> works :-).

Do you create a new drm_encoder at runtime for the HDMI encoder when it 
appears ? I thought the DRM core and API were not able to correctly cope with 
that.

> > You also can't be using the current device tree bindings because they all
> > assume a dependency from the display controller/output to the panel. For
> > hotplugging you'd need the dependency the other way around (the panel
> > needs to refer to the output by phandle).
> 
> Here [1] is a proposal for notification support in the drm_panel
> infrastructure (which is not that complicated), and here [2] is how
> I use it in my atmel-hlcdc driver to generate hotplug events.

Is there a way we could use the component framework for that ? I know that 
partial notification isn't supported at the moment, but Russell agreed it was 
a real use case that should be implemented at some point.

> Let me know if you want me to submit a proper patch series...
> 
> Best Regards,
> 
> Boris
> 
> [1]http://code.bulix.org/scq4g3-86804
> [2]http://code.bulix.org/7dg501-86805

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21 17:08               ` Laurent Pinchart
@ 2014-08-21 17:26                 ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21 17:26 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Thierry Reding, Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Mark Rutland, devicetree, Pawel Moll,
	Ian Campbell, linux-kernel, Rob Herring, Bo Shen, Kumar Gala,
	linux-arm-kernel

Hi Laurent,

On Thu, 21 Aug 2014 19:08:53 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

[...]

> > >> 
> > >> While this could be acceptable when all drivers are statically linked
> > >> in the kernel, it might be problematic when you're using modules,
> > >> meaning that you won't be able to display anything on your LCD panel
> > >> until your HDMI bridge module has been loaded.
> > > 
> > > No. HDMI should be using proper hotplugging anyway, hence it should be
> > > always be loaded anyway. You're in for a world of pain if you think you
> > > can run DRM with a driver that's composed of separate kernel modules.
> > 
> > I was talking about the external RGB to HDMI encoder, should the driver
> > for this encoder (which is not on On Chip block) be compiled
> > statically too ?
> 
> Given the move to multiplatform kernels we need to aim for as few modules 
> compiled in as possible. I'd say this includes HDMI encoders, panels and 
> display controllers.
> 
> > > Also if you don't want to use deferred probe, then you're in for the
> > > full hotplugging panel dance and that implies that you need to fix a
> > > bunch of things in DRM (one being the framebuffer console instantiation
> > > that I referred to in the other thread).
> > 
> > For now, I wait until there is a device connected on the RGB connector
> > (connector status set to connector_status_connected) before creating an
> > fbdev. It might not be the cleanest way to solve this issue, but it
> > works :-).
> 
> Do you create a new drm_encoder at runtime for the HDMI encoder when it 
> appears ? I thought the DRM core and API were not able to correctly cope with 
> that.

I haven't started to work on the HDMI encoder yet, and ATM I only have
a single connector (which is true from an HW POV), which is then bound
to an LCD panel (the only type of remote endpoint I currently support).

BTW, I wonder how my use case should be represented in the DRM
subsystem. As I said, from an HW POV I only have one RGB (or whatever
name you choose for it) connector. But on such kind of connectors you
can connect several output devices (panels, encoders, ...).
And in my case I have 2 devices on the same RGB connector: a panel and
an RGB to HDMI converter.

> 
> > > You also can't be using the current device tree bindings because they all
> > > assume a dependency from the display controller/output to the panel. For
> > > hotplugging you'd need the dependency the other way around (the panel
> > > needs to refer to the output by phandle).
> > 
> > Here [1] is a proposal for notification support in the drm_panel
> > infrastructure (which is not that complicated), and here [2] is how
> > I use it in my atmel-hlcdc driver to generate hotplug events.
> 
> Is there a way we could use the component framework for that ? I know that 
> partial notification isn't supported at the moment, but Russell agreed it was 
> a real use case that should be implemented at some point.

I'll give it a try.

Best Regards,

Boris



-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-21 17:26                 ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-21 17:26 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Laurent,

On Thu, 21 Aug 2014 19:08:53 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

[...]

> > >> 
> > >> While this could be acceptable when all drivers are statically linked
> > >> in the kernel, it might be problematic when you're using modules,
> > >> meaning that you won't be able to display anything on your LCD panel
> > >> until your HDMI bridge module has been loaded.
> > > 
> > > No. HDMI should be using proper hotplugging anyway, hence it should be
> > > always be loaded anyway. You're in for a world of pain if you think you
> > > can run DRM with a driver that's composed of separate kernel modules.
> > 
> > I was talking about the external RGB to HDMI encoder, should the driver
> > for this encoder (which is not on On Chip block) be compiled
> > statically too ?
> 
> Given the move to multiplatform kernels we need to aim for as few modules 
> compiled in as possible. I'd say this includes HDMI encoders, panels and 
> display controllers.
> 
> > > Also if you don't want to use deferred probe, then you're in for the
> > > full hotplugging panel dance and that implies that you need to fix a
> > > bunch of things in DRM (one being the framebuffer console instantiation
> > > that I referred to in the other thread).
> > 
> > For now, I wait until there is a device connected on the RGB connector
> > (connector status set to connector_status_connected) before creating an
> > fbdev. It might not be the cleanest way to solve this issue, but it
> > works :-).
> 
> Do you create a new drm_encoder at runtime for the HDMI encoder when it 
> appears ? I thought the DRM core and API were not able to correctly cope with 
> that.

I haven't started to work on the HDMI encoder yet, and ATM I only have
a single connector (which is true from an HW POV), which is then bound
to an LCD panel (the only type of remote endpoint I currently support).

BTW, I wonder how my use case should be represented in the DRM
subsystem. As I said, from an HW POV I only have one RGB (or whatever
name you choose for it) connector. But on such kind of connectors you
can connect several output devices (panels, encoders, ...).
And in my case I have 2 devices on the same RGB connector: a panel and
an RGB to HDMI converter.

> 
> > > You also can't be using the current device tree bindings because they all
> > > assume a dependency from the display controller/output to the panel. For
> > > hotplugging you'd need the dependency the other way around (the panel
> > > needs to refer to the output by phandle).
> > 
> > Here [1] is a proposal for notification support in the drm_panel
> > infrastructure (which is not that complicated), and here [2] is how
> > I use it in my atmel-hlcdc driver to generate hotplug events.
> 
> Is there a way we could use the component framework for that ? I know that 
> partial notification isn't supported at the moment, but Russell agreed it was 
> a real use case that should be implemented at some point.

I'll give it a try.

Best Regards,

Boris



-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21 13:16               ` Thierry Reding
  (?)
@ 2014-08-25 12:45                 ` Daniel Vetter
  -1 siblings, 0 replies; 103+ messages in thread
From: Daniel Vetter @ 2014-08-25 12:45 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Boris BREZILLON, Mark Rutland, linux-pwm, Samuel Ortiz,
	Pawel Moll, devicetree, Lee Jones, Ian Campbell, Nicolas Ferre,
	linux-kernel, dri-devel, Rob Herring, Ludovic Desroches,
	Alexandre Belloni, Laurent Pinchart, Bo Shen, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Andrew Victor,
	linux-arm-kernel

On Thu, Aug 21, 2014 at 03:16:08PM +0200, Thierry Reding wrote:
> On Thu, Aug 21, 2014 at 03:06:00PM +0200, Boris BREZILLON wrote:
> > On Thu, 21 Aug 2014 11:52:03 +0200
> > Thierry Reding <thierry.reding@gmail.com> wrote:
> > 
> > > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> > > > On Thu, 21 Aug 2014 11:04:07 +0200
> > > > Thierry Reding <thierry.reding@gmail.com> wrote:
> > > > 
> > > > > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > > > > Hi Ludovic,
> > > > > > 
> > > > > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > > > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > > > > 
> > > > > > > Hi Boris,
> > > > > > > 
> > > > > > > You can add
> > > > > > > 
> > > > > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > > > > 
> > > > > > Thanks for testing this driver.
> > > > > > 
> > > > > > > 
> > > > > > > Only one issue but not related to your patches, you can't display
> > > > > > > quickly the bootup logo since the panel detection takes too much
> > > > > > > time.
> > > > > > 
> > > > > > Yes, actually this is related to the device probe order: the
> > > > > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > > > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > > > > check for panel availability) when the display controller is
> > > > > > instantiated. I rely on the default polling infrastructure provided by
> > > > > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > > > > this is far more than you kernel boot time.
> > > > > > 
> > > > > > Do anyone see a solution to reduce this delay (without changing the
> > > > > > polling interval). I thought we could add a notifier infrastructure to
> > > > > > the DRM panel framework, but I'm not sure this is how you want things
> > > > > > done...
> > > > > 
> > > > > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > > > > yet. This will automatically take care of ordering things in a way that
> > > > > DRM/KMS will only be initialized after the panel has been probed.
> > > > 
> > > > Actually I'd like to avoid doing this with a deferred probe, because,
> > > > AFAIU, the remote endpoint is not tightly linked with the display
> > > > controller driver (I mean the display controller can still be
> > > > initialized without having a display connected on it).
> > > > Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> > > > the same RGB connector and I'd like to use it in a near future.
> > > > Returning -EPROBE_DEFER in case of several devices connected on the
> > > > same connector implies that I'll have to wait for all the remote
> > > > end-points to be available before my display controller could be
> > > > instantiated.
> > > > 
> > > > While this could be acceptable when all drivers are statically linked
> > > > in the kernel, it might be problematic when you're using modules,
> > > > meaning that you won't be able to display anything on your LCD panel
> > > > until your HDMI bridge module has been loaded.
> > > 
> > > No. HDMI should be using proper hotplugging anyway, hence it should be
> > > always be loaded anyway. You're in for a world of pain if you think you
> > > can run DRM with a driver that's composed of separate kernel modules.
> > 
> > I was talking about the external RGB to HDMI encoder, should the driver
> > for this encoder (which is not on On Chip block) be compiled
> > statically too ?
> > 
> > > 
> > > Also if you don't want to use deferred probe, then you're in for the
> > > full hotplugging panel dance and that implies that you need to fix a
> > > bunch of things in DRM (one being the framebuffer console instantiation
> > > that I referred to in the other thread).
> > 
> > For now, I wait until there is a device connected on the RGB connector
> > (connector status set to connector_status_connected) before creating an
> > fbdev. It might not be the cleanest way to solve this issue, but it
> > works :-).
> 
> Yeah, I guess that's one way to do it. But it's tricky to get right when
> you have several outputs. Which one should be considered the primary and
> trigger fbdev creation?

We could just reallocate the fbdev backing storage (probably only increase
it for safety since fbdev is bonghits) when new outputs show up. There has
been (and maybe still is) some provisions in the fbdev helper library to
do just that.

Mostly it would mean to split out drm_fb_helper_single_fb_probe so that
drivers could call it from their hotplug work. And then adjust the
->fb_probe callback of drivers which do this to reallocate the fbdev
buffer if it's only a resize. Overall this shouldn't be too much fuzz to
get going. Of course only as an opt-in, but imo that's the only sane way
to do this anyway.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-25 12:45                 ` Daniel Vetter
  0 siblings, 0 replies; 103+ messages in thread
From: Daniel Vetter @ 2014-08-25 12:45 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Mark Rutland, Samuel Ortiz, Pawel Moll, devicetree,
	Ludovic Desroches, Ian Campbell, Nicolas Ferre, linux-kernel,
	linux-pwm, Rob Herring, Alexandre Belloni, dri-devel, Bo Shen,
	Kumar Gala, Jean-Christophe Plagniol-Villard, Lee Jones,
	Andrew Victor, linux-arm-kernel, Laurent Pinchart

On Thu, Aug 21, 2014 at 03:16:08PM +0200, Thierry Reding wrote:
> On Thu, Aug 21, 2014 at 03:06:00PM +0200, Boris BREZILLON wrote:
> > On Thu, 21 Aug 2014 11:52:03 +0200
> > Thierry Reding <thierry.reding@gmail.com> wrote:
> > 
> > > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> > > > On Thu, 21 Aug 2014 11:04:07 +0200
> > > > Thierry Reding <thierry.reding@gmail.com> wrote:
> > > > 
> > > > > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > > > > Hi Ludovic,
> > > > > > 
> > > > > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > > > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > > > > 
> > > > > > > Hi Boris,
> > > > > > > 
> > > > > > > You can add
> > > > > > > 
> > > > > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > > > > 
> > > > > > Thanks for testing this driver.
> > > > > > 
> > > > > > > 
> > > > > > > Only one issue but not related to your patches, you can't display
> > > > > > > quickly the bootup logo since the panel detection takes too much
> > > > > > > time.
> > > > > > 
> > > > > > Yes, actually this is related to the device probe order: the
> > > > > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > > > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > > > > check for panel availability) when the display controller is
> > > > > > instantiated. I rely on the default polling infrastructure provided by
> > > > > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > > > > this is far more than you kernel boot time.
> > > > > > 
> > > > > > Do anyone see a solution to reduce this delay (without changing the
> > > > > > polling interval). I thought we could add a notifier infrastructure to
> > > > > > the DRM panel framework, but I'm not sure this is how you want things
> > > > > > done...
> > > > > 
> > > > > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > > > > yet. This will automatically take care of ordering things in a way that
> > > > > DRM/KMS will only be initialized after the panel has been probed.
> > > > 
> > > > Actually I'd like to avoid doing this with a deferred probe, because,
> > > > AFAIU, the remote endpoint is not tightly linked with the display
> > > > controller driver (I mean the display controller can still be
> > > > initialized without having a display connected on it).
> > > > Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> > > > the same RGB connector and I'd like to use it in a near future.
> > > > Returning -EPROBE_DEFER in case of several devices connected on the
> > > > same connector implies that I'll have to wait for all the remote
> > > > end-points to be available before my display controller could be
> > > > instantiated.
> > > > 
> > > > While this could be acceptable when all drivers are statically linked
> > > > in the kernel, it might be problematic when you're using modules,
> > > > meaning that you won't be able to display anything on your LCD panel
> > > > until your HDMI bridge module has been loaded.
> > > 
> > > No. HDMI should be using proper hotplugging anyway, hence it should be
> > > always be loaded anyway. You're in for a world of pain if you think you
> > > can run DRM with a driver that's composed of separate kernel modules.
> > 
> > I was talking about the external RGB to HDMI encoder, should the driver
> > for this encoder (which is not on On Chip block) be compiled
> > statically too ?
> > 
> > > 
> > > Also if you don't want to use deferred probe, then you're in for the
> > > full hotplugging panel dance and that implies that you need to fix a
> > > bunch of things in DRM (one being the framebuffer console instantiation
> > > that I referred to in the other thread).
> > 
> > For now, I wait until there is a device connected on the RGB connector
> > (connector status set to connector_status_connected) before creating an
> > fbdev. It might not be the cleanest way to solve this issue, but it
> > works :-).
> 
> Yeah, I guess that's one way to do it. But it's tricky to get right when
> you have several outputs. Which one should be considered the primary and
> trigger fbdev creation?

We could just reallocate the fbdev backing storage (probably only increase
it for safety since fbdev is bonghits) when new outputs show up. There has
been (and maybe still is) some provisions in the fbdev helper library to
do just that.

Mostly it would mean to split out drm_fb_helper_single_fb_probe so that
drivers could call it from their hotplug work. And then adjust the
->fb_probe callback of drivers which do this to reallocate the fbdev
buffer if it's only a resize. Overall this shouldn't be too much fuzz to
get going. Of course only as an opt-in, but imo that's the only sane way
to do this anyway.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-25 12:45                 ` Daniel Vetter
  0 siblings, 0 replies; 103+ messages in thread
From: Daniel Vetter @ 2014-08-25 12:45 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Aug 21, 2014 at 03:16:08PM +0200, Thierry Reding wrote:
> On Thu, Aug 21, 2014 at 03:06:00PM +0200, Boris BREZILLON wrote:
> > On Thu, 21 Aug 2014 11:52:03 +0200
> > Thierry Reding <thierry.reding@gmail.com> wrote:
> > 
> > > On Thu, Aug 21, 2014 at 11:41:59AM +0200, Boris BREZILLON wrote:
> > > > On Thu, 21 Aug 2014 11:04:07 +0200
> > > > Thierry Reding <thierry.reding@gmail.com> wrote:
> > > > 
> > > > > On Thu, Aug 21, 2014 at 10:37:06AM +0200, Boris BREZILLON wrote:
> > > > > > Hi Ludovic,
> > > > > > 
> > > > > > On Thu, 21 Aug 2014 10:16:19 +0200
> > > > > > Ludovic Desroches <ludovic.desroches@atmel.com> wrote:
> > > > > > 
> > > > > > > Hi Boris,
> > > > > > > 
> > > > > > > You can add
> > > > > > > 
> > > > > > > Tested-by: Ludovic Desroches <ludovic.desroches@atmel.com>
> > > > > > 
> > > > > > Thanks for testing this driver.
> > > > > > 
> > > > > > > 
> > > > > > > Only one issue but not related to your patches, you can't display
> > > > > > > quickly the bootup logo since the panel detection takes too much
> > > > > > > time.
> > > > > > 
> > > > > > Yes, actually this is related to the device probe order: the
> > > > > > hlcdc-display-controller device is probed before the simple-panel, thus
> > > > > > nothing is detected on the RGB connector (I use of_drm_find_panel to
> > > > > > check for panel availability) when the display controller is
> > > > > > instantiated. I rely on the default polling infrastructure provided by
> > > > > > the DRM/KMS framework which polls for a new connector every 10s, and
> > > > > > this is far more than you kernel boot time.
> > > > > > 
> > > > > > Do anyone see a solution to reduce this delay (without changing the
> > > > > > polling interval). I thought we could add a notifier infrastructure to
> > > > > > the DRM panel framework, but I'm not sure this is how you want things
> > > > > > done...
> > > > > 
> > > > > Other drivers return -EPROBE_DEFER when a panel hasn't been registered
> > > > > yet. This will automatically take care of ordering things in a way that
> > > > > DRM/KMS will only be initialized after the panel has been probed.
> > > > 
> > > > Actually I'd like to avoid doing this with a deferred probe, because,
> > > > AFAIU, the remote endpoint is not tightly linked with the display
> > > > controller driver (I mean the display controller can still be
> > > > initialized without having a display connected on it).
> > > > Moreover the atmel dev kit I'm using has an HDMI bridge connected on
> > > > the same RGB connector and I'd like to use it in a near future.
> > > > Returning -EPROBE_DEFER in case of several devices connected on the
> > > > same connector implies that I'll have to wait for all the remote
> > > > end-points to be available before my display controller could be
> > > > instantiated.
> > > > 
> > > > While this could be acceptable when all drivers are statically linked
> > > > in the kernel, it might be problematic when you're using modules,
> > > > meaning that you won't be able to display anything on your LCD panel
> > > > until your HDMI bridge module has been loaded.
> > > 
> > > No. HDMI should be using proper hotplugging anyway, hence it should be
> > > always be loaded anyway. You're in for a world of pain if you think you
> > > can run DRM with a driver that's composed of separate kernel modules.
> > 
> > I was talking about the external RGB to HDMI encoder, should the driver
> > for this encoder (which is not on On Chip block) be compiled
> > statically too ?
> > 
> > > 
> > > Also if you don't want to use deferred probe, then you're in for the
> > > full hotplugging panel dance and that implies that you need to fix a
> > > bunch of things in DRM (one being the framebuffer console instantiation
> > > that I referred to in the other thread).
> > 
> > For now, I wait until there is a device connected on the RGB connector
> > (connector status set to connector_status_connected) before creating an
> > fbdev. It might not be the cleanest way to solve this issue, but it
> > works :-).
> 
> Yeah, I guess that's one way to do it. But it's tricky to get right when
> you have several outputs. Which one should be considered the primary and
> trigger fbdev creation?

We could just reallocate the fbdev backing storage (probably only increase
it for safety since fbdev is bonghits) when new outputs show up. There has
been (and maybe still is) some provisions in the fbdev helper library to
do just that.

Mostly it would mean to split out drm_fb_helper_single_fb_probe so that
drivers could call it from their hotplug work. And then adjust the
->fb_probe callback of drivers which do this to reallocate the fbdev
buffer if it's only a resize. Overall this shouldn't be too much fuzz to
get going. Of course only as an opt-in, but imo that's the only sane way
to do this anyway.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-21 17:26                 ` Boris BREZILLON
@ 2014-08-25 23:39                   ` Laurent Pinchart
  -1 siblings, 0 replies; 103+ messages in thread
From: Laurent Pinchart @ 2014-08-25 23:39 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Thierry Reding, Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Mark Rutland, devicetree, Pawel Moll,
	Ian Campbell, linux-kernel, Rob Herring, Bo Shen, Kumar Gala,
	linux-arm-kernel

Hi Boris,

On Thursday 21 August 2014 19:26:33 Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 19:08:53 +0200
> Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> 
> [...]
> 
> >>>> While this could be acceptable when all drivers are statically linked
> >>>> in the kernel, it might be problematic when you're using modules,
> >>>> meaning that you won't be able to display anything on your LCD panel
> >>>> until your HDMI bridge module has been loaded.
> >>> 
> >>> No. HDMI should be using proper hotplugging anyway, hence it should be
> >>> always be loaded anyway. You're in for a world of pain if you think
> >>> you can run DRM with a driver that's composed of separate kernel
> >>> modules.
> >> 
> >> I was talking about the external RGB to HDMI encoder, should the driver
> >> for this encoder (which is not on On Chip block) be compiled
> >> statically too ?
> > 
> > Given the move to multiplatform kernels we need to aim for as few modules
> > compiled in as possible. I'd say this includes HDMI encoders, panels and
> > display controllers.
> > 
> >>> Also if you don't want to use deferred probe, then you're in for the
> >>> full hotplugging panel dance and that implies that you need to fix a
> >>> bunch of things in DRM (one being the framebuffer console
> >>> instantiation
> >>> that I referred to in the other thread).
> >> 
> >> For now, I wait until there is a device connected on the RGB connector
> >> (connector status set to connector_status_connected) before creating an
> >> fbdev. It might not be the cleanest way to solve this issue, but it
> >> works :-).
> > 
> > Do you create a new drm_encoder at runtime for the HDMI encoder when it
> > appears ? I thought the DRM core and API were not able to correctly cope
> > with that.
> 
> I haven't started to work on the HDMI encoder yet, and ATM I only have
> a single connector (which is true from an HW POV), which is then bound
> to an LCD panel (the only type of remote endpoint I currently support).
> 
> BTW, I wonder how my use case should be represented in the DRM
> subsystem. As I said, from an HW POV I only have one RGB (or whatever
> name you choose for it) connector. But on such kind of connectors you
> can connect several output devices (panels, encoders, ...).
> And in my case I have 2 devices on the same RGB connector: a panel and
> an RGB to HDMI converter.

The DRM connector object was initially meant to model a physical user-
accessible connector on a board (VGA, DVI, HDMI, ...) and the properties of 
the monitor plugged into it. It has then been (ab)used to represent panels, as 
they're similar to monitors.

In your case the VGA and HDMI connectors should be modeled as DRM connectors, 
the RGB to HDMI encoder as a DRM encoder, and the LCDC as a DRM CRTC.

As DRM hardcodes the pipeline model to CRTC -> encoder -> connector, you will 
also need a DRM encoder in the VGA path. I suppose your board has a VGA DAC, 
that's the component you should expose as a DRM encoder (even if it can't be 
controlled and doesn't limit the valid modes).

> >>> You also can't be using the current device tree bindings because they
> >>> all assume a dependency from the display controller/output to the
> >>> panel. For hotplugging you'd need the dependency the other way around
> >>> (the panel needs to refer to the output by phandle).
> >> 
> >> Here [1] is a proposal for notification support in the drm_panel
> >> infrastructure (which is not that complicated), and here [2] is how
> >> I use it in my atmel-hlcdc driver to generate hotplug events.
> > 
> > Is there a way we could use the component framework for that ? I know that
> > partial notification isn't supported at the moment, but Russell agreed it
> > was a real use case that should be implemented at some point.
> 
> I'll give it a try.

-- 
Regards,

Laurent Pinchart


^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-25 23:39                   ` Laurent Pinchart
  0 siblings, 0 replies; 103+ messages in thread
From: Laurent Pinchart @ 2014-08-25 23:39 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Boris,

On Thursday 21 August 2014 19:26:33 Boris BREZILLON wrote:
> On Thu, 21 Aug 2014 19:08:53 +0200
> Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> 
> [...]
> 
> >>>> While this could be acceptable when all drivers are statically linked
> >>>> in the kernel, it might be problematic when you're using modules,
> >>>> meaning that you won't be able to display anything on your LCD panel
> >>>> until your HDMI bridge module has been loaded.
> >>> 
> >>> No. HDMI should be using proper hotplugging anyway, hence it should be
> >>> always be loaded anyway. You're in for a world of pain if you think
> >>> you can run DRM with a driver that's composed of separate kernel
> >>> modules.
> >> 
> >> I was talking about the external RGB to HDMI encoder, should the driver
> >> for this encoder (which is not on On Chip block) be compiled
> >> statically too ?
> > 
> > Given the move to multiplatform kernels we need to aim for as few modules
> > compiled in as possible. I'd say this includes HDMI encoders, panels and
> > display controllers.
> > 
> >>> Also if you don't want to use deferred probe, then you're in for the
> >>> full hotplugging panel dance and that implies that you need to fix a
> >>> bunch of things in DRM (one being the framebuffer console
> >>> instantiation
> >>> that I referred to in the other thread).
> >> 
> >> For now, I wait until there is a device connected on the RGB connector
> >> (connector status set to connector_status_connected) before creating an
> >> fbdev. It might not be the cleanest way to solve this issue, but it
> >> works :-).
> > 
> > Do you create a new drm_encoder at runtime for the HDMI encoder when it
> > appears ? I thought the DRM core and API were not able to correctly cope
> > with that.
> 
> I haven't started to work on the HDMI encoder yet, and ATM I only have
> a single connector (which is true from an HW POV), which is then bound
> to an LCD panel (the only type of remote endpoint I currently support).
> 
> BTW, I wonder how my use case should be represented in the DRM
> subsystem. As I said, from an HW POV I only have one RGB (or whatever
> name you choose for it) connector. But on such kind of connectors you
> can connect several output devices (panels, encoders, ...).
> And in my case I have 2 devices on the same RGB connector: a panel and
> an RGB to HDMI converter.

The DRM connector object was initially meant to model a physical user-
accessible connector on a board (VGA, DVI, HDMI, ...) and the properties of 
the monitor plugged into it. It has then been (ab)used to represent panels, as 
they're similar to monitors.

In your case the VGA and HDMI connectors should be modeled as DRM connectors, 
the RGB to HDMI encoder as a DRM encoder, and the LCDC as a DRM CRTC.

As DRM hardcodes the pipeline model to CRTC -> encoder -> connector, you will 
also need a DRM encoder in the VGA path. I suppose your board has a VGA DAC, 
that's the component you should expose as a DRM encoder (even if it can't be 
controlled and doesn't limit the valid modes).

> >>> You also can't be using the current device tree bindings because they
> >>> all assume a dependency from the display controller/output to the
> >>> panel. For hotplugging you'd need the dependency the other way around
> >>> (the panel needs to refer to the output by phandle).
> >> 
> >> Here [1] is a proposal for notification support in the drm_panel
> >> infrastructure (which is not that complicated), and here [2] is how
> >> I use it in my atmel-hlcdc driver to generate hotplug events.
> > 
> > Is there a way we could use the component framework for that ? I know that
> > partial notification isn't supported at the moment, but Russell agreed it
> > was a real use case that should be implemented at some point.
> 
> I'll give it a try.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-25 23:39                   ` Laurent Pinchart
@ 2014-08-27  7:52                     ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-27  7:52 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Thierry Reding, Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Mark Rutland, devicetree, Pawel Moll,
	Ian Campbell, linux-kernel, Rob Herring, Bo Shen, Kumar Gala,
	linux-arm-kernel

Hi Laurent,

On Tue, 26 Aug 2014 01:39:21 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> On Thursday 21 August 2014 19:26:33 Boris BREZILLON wrote:
> > On Thu, 21 Aug 2014 19:08:53 +0200
> > Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> > 
> > [...]
> > 
> > >>>> While this could be acceptable when all drivers are statically linked
> > >>>> in the kernel, it might be problematic when you're using modules,
> > >>>> meaning that you won't be able to display anything on your LCD panel
> > >>>> until your HDMI bridge module has been loaded.
> > >>> 
> > >>> No. HDMI should be using proper hotplugging anyway, hence it should be
> > >>> always be loaded anyway. You're in for a world of pain if you think
> > >>> you can run DRM with a driver that's composed of separate kernel
> > >>> modules.
> > >> 
> > >> I was talking about the external RGB to HDMI encoder, should the driver
> > >> for this encoder (which is not on On Chip block) be compiled
> > >> statically too ?
> > > 
> > > Given the move to multiplatform kernels we need to aim for as few modules
> > > compiled in as possible. I'd say this includes HDMI encoders, panels and
> > > display controllers.
> > > 
> > >>> Also if you don't want to use deferred probe, then you're in for the
> > >>> full hotplugging panel dance and that implies that you need to fix a
> > >>> bunch of things in DRM (one being the framebuffer console
> > >>> instantiation
> > >>> that I referred to in the other thread).
> > >> 
> > >> For now, I wait until there is a device connected on the RGB connector
> > >> (connector status set to connector_status_connected) before creating an
> > >> fbdev. It might not be the cleanest way to solve this issue, but it
> > >> works :-).
> > > 
> > > Do you create a new drm_encoder at runtime for the HDMI encoder when it
> > > appears ? I thought the DRM core and API were not able to correctly cope
> > > with that.
> > 
> > I haven't started to work on the HDMI encoder yet, and ATM I only have
> > a single connector (which is true from an HW POV), which is then bound
> > to an LCD panel (the only type of remote endpoint I currently support).
> > 
> > BTW, I wonder how my use case should be represented in the DRM
> > subsystem. As I said, from an HW POV I only have one RGB (or whatever
> > name you choose for it) connector. But on such kind of connectors you
> > can connect several output devices (panels, encoders, ...).
> > And in my case I have 2 devices on the same RGB connector: a panel and
> > an RGB to HDMI converter.
> 
> The DRM connector object was initially meant to model a physical user-
> accessible connector on a board (VGA, DVI, HDMI, ...) and the properties of 
> the monitor plugged into it. It has then been (ab)used to represent panels, as 
> they're similar to monitors.
> 
> In your case the VGA and HDMI connectors should be modeled as DRM connectors, 
> the RGB to HDMI encoder as a DRM encoder, and the LCDC as a DRM CRTC.

I don't have any VGA connector (or I'm missing something :-)), but I
have an LCD panel and an RGB to HDMI encoder connected on the same RGB
connector.

> 
> As DRM hardcodes the pipeline model to CRTC -> encoder -> connector, you will 
> also need a DRM encoder in the VGA path. I suppose your board has a VGA DAC, 
> that's the component you should expose as a DRM encoder (even if it can't be 
> controlled and doesn't limit the valid modes).

Actually, my problem is that both devices are connected on the same RGB
connector, and thus share the same display mode (resolution, HSYNC,
VSYNC, RGB output mode, ...).
This means that all remote devices have to agree on a specific mode if
we want to mirror the display on several output devices, otherwise we
must disable one of the output devices.

> 
> > >>> You also can't be using the current device tree bindings because they
> > >>> all assume a dependency from the display controller/output to the
> > >>> panel. For hotplugging you'd need the dependency the other way around
> > >>> (the panel needs to refer to the output by phandle).
> > >> 
> > >> Here [1] is a proposal for notification support in the drm_panel
> > >> infrastructure (which is not that complicated), and here [2] is how
> > >> I use it in my atmel-hlcdc driver to generate hotplug events.
> > > 
> > > Is there a way we could use the component framework for that ? I know that
> > > partial notification isn't supported at the moment, but Russell agreed it
> > > was a real use case that should be implemented at some point.
> > 
> > I'll give it a try.
> 



-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-27  7:52                     ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-27  7:52 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Laurent,

On Tue, 26 Aug 2014 01:39:21 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> On Thursday 21 August 2014 19:26:33 Boris BREZILLON wrote:
> > On Thu, 21 Aug 2014 19:08:53 +0200
> > Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> > 
> > [...]
> > 
> > >>>> While this could be acceptable when all drivers are statically linked
> > >>>> in the kernel, it might be problematic when you're using modules,
> > >>>> meaning that you won't be able to display anything on your LCD panel
> > >>>> until your HDMI bridge module has been loaded.
> > >>> 
> > >>> No. HDMI should be using proper hotplugging anyway, hence it should be
> > >>> always be loaded anyway. You're in for a world of pain if you think
> > >>> you can run DRM with a driver that's composed of separate kernel
> > >>> modules.
> > >> 
> > >> I was talking about the external RGB to HDMI encoder, should the driver
> > >> for this encoder (which is not on On Chip block) be compiled
> > >> statically too ?
> > > 
> > > Given the move to multiplatform kernels we need to aim for as few modules
> > > compiled in as possible. I'd say this includes HDMI encoders, panels and
> > > display controllers.
> > > 
> > >>> Also if you don't want to use deferred probe, then you're in for the
> > >>> full hotplugging panel dance and that implies that you need to fix a
> > >>> bunch of things in DRM (one being the framebuffer console
> > >>> instantiation
> > >>> that I referred to in the other thread).
> > >> 
> > >> For now, I wait until there is a device connected on the RGB connector
> > >> (connector status set to connector_status_connected) before creating an
> > >> fbdev. It might not be the cleanest way to solve this issue, but it
> > >> works :-).
> > > 
> > > Do you create a new drm_encoder at runtime for the HDMI encoder when it
> > > appears ? I thought the DRM core and API were not able to correctly cope
> > > with that.
> > 
> > I haven't started to work on the HDMI encoder yet, and ATM I only have
> > a single connector (which is true from an HW POV), which is then bound
> > to an LCD panel (the only type of remote endpoint I currently support).
> > 
> > BTW, I wonder how my use case should be represented in the DRM
> > subsystem. As I said, from an HW POV I only have one RGB (or whatever
> > name you choose for it) connector. But on such kind of connectors you
> > can connect several output devices (panels, encoders, ...).
> > And in my case I have 2 devices on the same RGB connector: a panel and
> > an RGB to HDMI converter.
> 
> The DRM connector object was initially meant to model a physical user-
> accessible connector on a board (VGA, DVI, HDMI, ...) and the properties of 
> the monitor plugged into it. It has then been (ab)used to represent panels, as 
> they're similar to monitors.
> 
> In your case the VGA and HDMI connectors should be modeled as DRM connectors, 
> the RGB to HDMI encoder as a DRM encoder, and the LCDC as a DRM CRTC.

I don't have any VGA connector (or I'm missing something :-)), but I
have an LCD panel and an RGB to HDMI encoder connected on the same RGB
connector.

> 
> As DRM hardcodes the pipeline model to CRTC -> encoder -> connector, you will 
> also need a DRM encoder in the VGA path. I suppose your board has a VGA DAC, 
> that's the component you should expose as a DRM encoder (even if it can't be 
> controlled and doesn't limit the valid modes).

Actually, my problem is that both devices are connected on the same RGB
connector, and thus share the same display mode (resolution, HSYNC,
VSYNC, RGB output mode, ...).
This means that all remote devices have to agree on a specific mode if
we want to mirror the display on several output devices, otherwise we
must disable one of the output devices.

> 
> > >>> You also can't be using the current device tree bindings because they
> > >>> all assume a dependency from the display controller/output to the
> > >>> panel. For hotplugging you'd need the dependency the other way around
> > >>> (the panel needs to refer to the output by phandle).
> > >> 
> > >> Here [1] is a proposal for notification support in the drm_panel
> > >> infrastructure (which is not that complicated), and here [2] is how
> > >> I use it in my atmel-hlcdc driver to generate hotplug events.
> > > 
> > > Is there a way we could use the component framework for that ? I know that
> > > partial notification isn't supported at the moment, but Russell agreed it
> > > was a real use case that should be implemented at some point.
> > 
> > I'll give it a try.
> 



-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-27  7:52                     ` Boris BREZILLON
  (?)
@ 2014-08-28 12:19                       ` Laurent Pinchart
  -1 siblings, 0 replies; 103+ messages in thread
From: Laurent Pinchart @ 2014-08-28 12:19 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Thierry Reding, Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Mark Rutland, devicetree, Pawel Moll,
	Ian Campbell, linux-kernel, Rob Herring, Bo Shen, Kumar Gala,
	linux-arm-kernel

Hi Boris,

On Wednesday 27 August 2014 09:52:35 Boris BREZILLON wrote:
> On Tue, 26 Aug 2014 01:39:21 +0200 Laurent Pinchart wrote:
> > On Thursday 21 August 2014 19:26:33 Boris BREZILLON wrote:
> >> On Thu, 21 Aug 2014 19:08:53 +0200
> >> Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> >> 
> >> [...]
> >> 
> >>>>>> While this could be acceptable when all drivers are statically
> >>>>>> linked in the kernel, it might be problematic when you're using
> >>>>>> modules, meaning that you won't be able to display anything on your
> >>>>>> LCD panel until your HDMI bridge module has been loaded.
> >>>>> 
> >>>>> No. HDMI should be using proper hotplugging anyway, hence it should
> >>>>> be always be loaded anyway. You're in for a world of pain if you
> >>>>> think you can run DRM with a driver that's composed of separate
> >>>>> kernel modules.
> >>>> 
> >>>> I was talking about the external RGB to HDMI encoder, should the
> >>>> driver for this encoder (which is not on On Chip block) be compiled
> >>>> statically too ?
> >>> 
> >>> Given the move to multiplatform kernels we need to aim for as few
> >>> modules compiled in as possible. I'd say this includes HDMI encoders,
> >>> panels and display controllers.
> >>> 
> >>>>> Also if you don't want to use deferred probe, then you're in for the
> >>>>> full hotplugging panel dance and that implies that you need to fix a
> >>>>> bunch of things in DRM (one being the framebuffer console
> >>>>> instantiation that I referred to in the other thread).
> >>>> 
> >>>> For now, I wait until there is a device connected on the RGB
> >>>> connector (connector status set to connector_status_connected) before
> >>>> creating an fbdev. It might not be the cleanest way to solve this
> >>>> issue, but it works :-).
> >>> 
> >>> Do you create a new drm_encoder at runtime for the HDMI encoder when
> >>> it appears ? I thought the DRM core and API were not able to correctly
> >>> cope with that.
> >> 
> >> I haven't started to work on the HDMI encoder yet, and ATM I only have
> >> a single connector (which is true from an HW POV), which is then bound
> >> to an LCD panel (the only type of remote endpoint I currently support).
> >> 
> >> BTW, I wonder how my use case should be represented in the DRM
> >> subsystem. As I said, from an HW POV I only have one RGB (or whatever
> >> name you choose for it) connector. But on such kind of connectors you
> >> can connect several output devices (panels, encoders, ...).
> >> And in my case I have 2 devices on the same RGB connector: a panel and
> >> an RGB to HDMI converter.
> > 
> > The DRM connector object was initially meant to model a physical user-
> > accessible connector on a board (VGA, DVI, HDMI, ...) and the properties
> > of the monitor plugged into it. It has then been (ab)used to represent
> > panels, as they're similar to monitors.
> > 
> > In your case the VGA and HDMI connectors should be modeled as DRM
> > connectors, the RGB to HDMI encoder as a DRM encoder, and the LCDC as a
> > DRM CRTC.
>
> I don't have any VGA connector (or I'm missing something :-)),

My bad.

> but I have an LCD panel and an RGB to HDMI encoder connected on the same RGB
> connector.

There's no such thing as an RGB connector in DRM. Your SoC has a parallel RGB 
video output (I assume it's a DPI bus). From a DRM point of view, that bus 
corresponds to the output of the CRTC.

> > As DRM hardcodes the pipeline model to CRTC -> encoder -> connector, you
> > will also need a DRM encoder in the VGA path. I suppose your board has a
> > VGA DAC, that's the component you should expose as a DRM encoder (even if
> > it can't be controlled and doesn't limit the valid modes).
> 
> Actually, my problem is that both devices are connected on the same RGB
> connector, and thus share the same display mode (resolution, HSYNC,
> VSYNC, RGB output mode, ...).
> This means that all remote devices have to agree on a specific mode if
> we want to mirror the display on several output devices, otherwise we
> must disable one of the output devices.

That's not really a problem. From a DRM perspective you need to model your 
device as

,------.       ,---------------.       ,-----------------.
| CRTC | -+--> | Dummy Encoder | ----> | Panel Connector |
`------´  |    `---------------´       `-----------------´
          |    ,---------------.       ,-----------------.
          \--> | HDMI Encoder  | ----> | HDMI Connector  |
               `---------------´       `-----------------´

The HDMI pipeline is pretty straightforward.

You have told me that the panel has a parallel RGB input without any encoder 
in the panel pipeline (by the way, which panel model are you using ?). 
However, DRM requires an encoder in every pipeline. You will thus need to 
instantiate a dummy encoder. One option would be to set the encoder and 
connector types to DRM_MODE_ENCODER_LVDS and DRM_MODE_CONNECTOR_LVDS 
respectively, as that's what userspace usually expects for panels. That 
doesn't reflect the reality in your case though, so creating a new 
DRM_MODE_CONNECTOR_DPI type might be needed, possibly to be used with 
DRM_MODE_ENCODER_NONE.

As neither encoder can modify the mode, the same mode will be output on the 
two connectors.

> >>>>> You also can't be using the current device tree bindings because
> >>>>> they all assume a dependency from the display controller/output to
> >>>>> the panel. For hotplugging you'd need the dependency the other way
> >>>>> around (the panel needs to refer to the output by phandle).
> >>>> 
> >>>> Here [1] is a proposal for notification support in the drm_panel
> >>>> infrastructure (which is not that complicated), and here [2] is how
> >>>> I use it in my atmel-hlcdc driver to generate hotplug events.
> >>> 
> >>> Is there a way we could use the component framework for that ? I know
> >>> that partial notification isn't supported at the moment, but Russell
> >>> agreed it was a real use case that should be implemented at some
> >>> point.
> >> 
> >> I'll give it a try.

-- 
Regards,

Laurent Pinchart


^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-28 12:19                       ` Laurent Pinchart
  0 siblings, 0 replies; 103+ messages in thread
From: Laurent Pinchart @ 2014-08-28 12:19 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Mark Rutland, linux-pwm, Samuel Ortiz, Pawel Moll,
	Ludovic Desroches, Lee Jones, Ian Campbell, Nicolas Ferre,
	linux-kernel, dri-devel, Rob Herring, Alexandre Belloni,
	linux-arm-kernel, Bo Shen, Kumar Gala,
	Jean-Christophe Plagniol-Villard, Andrew Victor, devicetree

Hi Boris,

On Wednesday 27 August 2014 09:52:35 Boris BREZILLON wrote:
> On Tue, 26 Aug 2014 01:39:21 +0200 Laurent Pinchart wrote:
> > On Thursday 21 August 2014 19:26:33 Boris BREZILLON wrote:
> >> On Thu, 21 Aug 2014 19:08:53 +0200
> >> Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> >> 
> >> [...]
> >> 
> >>>>>> While this could be acceptable when all drivers are statically
> >>>>>> linked in the kernel, it might be problematic when you're using
> >>>>>> modules, meaning that you won't be able to display anything on your
> >>>>>> LCD panel until your HDMI bridge module has been loaded.
> >>>>> 
> >>>>> No. HDMI should be using proper hotplugging anyway, hence it should
> >>>>> be always be loaded anyway. You're in for a world of pain if you
> >>>>> think you can run DRM with a driver that's composed of separate
> >>>>> kernel modules.
> >>>> 
> >>>> I was talking about the external RGB to HDMI encoder, should the
> >>>> driver for this encoder (which is not on On Chip block) be compiled
> >>>> statically too ?
> >>> 
> >>> Given the move to multiplatform kernels we need to aim for as few
> >>> modules compiled in as possible. I'd say this includes HDMI encoders,
> >>> panels and display controllers.
> >>> 
> >>>>> Also if you don't want to use deferred probe, then you're in for the
> >>>>> full hotplugging panel dance and that implies that you need to fix a
> >>>>> bunch of things in DRM (one being the framebuffer console
> >>>>> instantiation that I referred to in the other thread).
> >>>> 
> >>>> For now, I wait until there is a device connected on the RGB
> >>>> connector (connector status set to connector_status_connected) before
> >>>> creating an fbdev. It might not be the cleanest way to solve this
> >>>> issue, but it works :-).
> >>> 
> >>> Do you create a new drm_encoder at runtime for the HDMI encoder when
> >>> it appears ? I thought the DRM core and API were not able to correctly
> >>> cope with that.
> >> 
> >> I haven't started to work on the HDMI encoder yet, and ATM I only have
> >> a single connector (which is true from an HW POV), which is then bound
> >> to an LCD panel (the only type of remote endpoint I currently support).
> >> 
> >> BTW, I wonder how my use case should be represented in the DRM
> >> subsystem. As I said, from an HW POV I only have one RGB (or whatever
> >> name you choose for it) connector. But on such kind of connectors you
> >> can connect several output devices (panels, encoders, ...).
> >> And in my case I have 2 devices on the same RGB connector: a panel and
> >> an RGB to HDMI converter.
> > 
> > The DRM connector object was initially meant to model a physical user-
> > accessible connector on a board (VGA, DVI, HDMI, ...) and the properties
> > of the monitor plugged into it. It has then been (ab)used to represent
> > panels, as they're similar to monitors.
> > 
> > In your case the VGA and HDMI connectors should be modeled as DRM
> > connectors, the RGB to HDMI encoder as a DRM encoder, and the LCDC as a
> > DRM CRTC.
>
> I don't have any VGA connector (or I'm missing something :-)),

My bad.

> but I have an LCD panel and an RGB to HDMI encoder connected on the same RGB
> connector.

There's no such thing as an RGB connector in DRM. Your SoC has a parallel RGB 
video output (I assume it's a DPI bus). From a DRM point of view, that bus 
corresponds to the output of the CRTC.

> > As DRM hardcodes the pipeline model to CRTC -> encoder -> connector, you
> > will also need a DRM encoder in the VGA path. I suppose your board has a
> > VGA DAC, that's the component you should expose as a DRM encoder (even if
> > it can't be controlled and doesn't limit the valid modes).
> 
> Actually, my problem is that both devices are connected on the same RGB
> connector, and thus share the same display mode (resolution, HSYNC,
> VSYNC, RGB output mode, ...).
> This means that all remote devices have to agree on a specific mode if
> we want to mirror the display on several output devices, otherwise we
> must disable one of the output devices.

That's not really a problem. From a DRM perspective you need to model your 
device as

,------.       ,---------------.       ,-----------------.
| CRTC | -+--> | Dummy Encoder | ----> | Panel Connector |
`------´  |    `---------------´       `-----------------´
          |    ,---------------.       ,-----------------.
          \--> | HDMI Encoder  | ----> | HDMI Connector  |
               `---------------´       `-----------------´

The HDMI pipeline is pretty straightforward.

You have told me that the panel has a parallel RGB input without any encoder 
in the panel pipeline (by the way, which panel model are you using ?). 
However, DRM requires an encoder in every pipeline. You will thus need to 
instantiate a dummy encoder. One option would be to set the encoder and 
connector types to DRM_MODE_ENCODER_LVDS and DRM_MODE_CONNECTOR_LVDS 
respectively, as that's what userspace usually expects for panels. That 
doesn't reflect the reality in your case though, so creating a new 
DRM_MODE_CONNECTOR_DPI type might be needed, possibly to be used with 
DRM_MODE_ENCODER_NONE.

As neither encoder can modify the mode, the same mode will be output on the 
two connectors.

> >>>>> You also can't be using the current device tree bindings because
> >>>>> they all assume a dependency from the display controller/output to
> >>>>> the panel. For hotplugging you'd need the dependency the other way
> >>>>> around (the panel needs to refer to the output by phandle).
> >>>> 
> >>>> Here [1] is a proposal for notification support in the drm_panel
> >>>> infrastructure (which is not that complicated), and here [2] is how
> >>>> I use it in my atmel-hlcdc driver to generate hotplug events.
> >>> 
> >>> Is there a way we could use the component framework for that ? I know
> >>> that partial notification isn't supported at the moment, but Russell
> >>> agreed it was a real use case that should be implemented at some
> >>> point.
> >> 
> >> I'll give it a try.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-28 12:19                       ` Laurent Pinchart
  0 siblings, 0 replies; 103+ messages in thread
From: Laurent Pinchart @ 2014-08-28 12:19 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Boris,

On Wednesday 27 August 2014 09:52:35 Boris BREZILLON wrote:
> On Tue, 26 Aug 2014 01:39:21 +0200 Laurent Pinchart wrote:
> > On Thursday 21 August 2014 19:26:33 Boris BREZILLON wrote:
> >> On Thu, 21 Aug 2014 19:08:53 +0200
> >> Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> >> 
> >> [...]
> >> 
> >>>>>> While this could be acceptable when all drivers are statically
> >>>>>> linked in the kernel, it might be problematic when you're using
> >>>>>> modules, meaning that you won't be able to display anything on your
> >>>>>> LCD panel until your HDMI bridge module has been loaded.
> >>>>> 
> >>>>> No. HDMI should be using proper hotplugging anyway, hence it should
> >>>>> be always be loaded anyway. You're in for a world of pain if you
> >>>>> think you can run DRM with a driver that's composed of separate
> >>>>> kernel modules.
> >>>> 
> >>>> I was talking about the external RGB to HDMI encoder, should the
> >>>> driver for this encoder (which is not on On Chip block) be compiled
> >>>> statically too ?
> >>> 
> >>> Given the move to multiplatform kernels we need to aim for as few
> >>> modules compiled in as possible. I'd say this includes HDMI encoders,
> >>> panels and display controllers.
> >>> 
> >>>>> Also if you don't want to use deferred probe, then you're in for the
> >>>>> full hotplugging panel dance and that implies that you need to fix a
> >>>>> bunch of things in DRM (one being the framebuffer console
> >>>>> instantiation that I referred to in the other thread).
> >>>> 
> >>>> For now, I wait until there is a device connected on the RGB
> >>>> connector (connector status set to connector_status_connected) before
> >>>> creating an fbdev. It might not be the cleanest way to solve this
> >>>> issue, but it works :-).
> >>> 
> >>> Do you create a new drm_encoder at runtime for the HDMI encoder when
> >>> it appears ? I thought the DRM core and API were not able to correctly
> >>> cope with that.
> >> 
> >> I haven't started to work on the HDMI encoder yet, and ATM I only have
> >> a single connector (which is true from an HW POV), which is then bound
> >> to an LCD panel (the only type of remote endpoint I currently support).
> >> 
> >> BTW, I wonder how my use case should be represented in the DRM
> >> subsystem. As I said, from an HW POV I only have one RGB (or whatever
> >> name you choose for it) connector. But on such kind of connectors you
> >> can connect several output devices (panels, encoders, ...).
> >> And in my case I have 2 devices on the same RGB connector: a panel and
> >> an RGB to HDMI converter.
> > 
> > The DRM connector object was initially meant to model a physical user-
> > accessible connector on a board (VGA, DVI, HDMI, ...) and the properties
> > of the monitor plugged into it. It has then been (ab)used to represent
> > panels, as they're similar to monitors.
> > 
> > In your case the VGA and HDMI connectors should be modeled as DRM
> > connectors, the RGB to HDMI encoder as a DRM encoder, and the LCDC as a
> > DRM CRTC.
>
> I don't have any VGA connector (or I'm missing something :-)),

My bad.

> but I have an LCD panel and an RGB to HDMI encoder connected on the same RGB
> connector.

There's no such thing as an RGB connector in DRM. Your SoC has a parallel RGB 
video output (I assume it's a DPI bus). From a DRM point of view, that bus 
corresponds to the output of the CRTC.

> > As DRM hardcodes the pipeline model to CRTC -> encoder -> connector, you
> > will also need a DRM encoder in the VGA path. I suppose your board has a
> > VGA DAC, that's the component you should expose as a DRM encoder (even if
> > it can't be controlled and doesn't limit the valid modes).
> 
> Actually, my problem is that both devices are connected on the same RGB
> connector, and thus share the same display mode (resolution, HSYNC,
> VSYNC, RGB output mode, ...).
> This means that all remote devices have to agree on a specific mode if
> we want to mirror the display on several output devices, otherwise we
> must disable one of the output devices.

That's not really a problem. From a DRM perspective you need to model your 
device as

,------.       ,---------------.       ,-----------------.
| CRTC | -+--> | Dummy Encoder | ----> | Panel Connector |
`------?  |    `---------------?       `-----------------?
          |    ,---------------.       ,-----------------.
          \--> | HDMI Encoder  | ----> | HDMI Connector  |
               `---------------?       `-----------------?

The HDMI pipeline is pretty straightforward.

You have told me that the panel has a parallel RGB input without any encoder 
in the panel pipeline (by the way, which panel model are you using ?). 
However, DRM requires an encoder in every pipeline. You will thus need to 
instantiate a dummy encoder. One option would be to set the encoder and 
connector types to DRM_MODE_ENCODER_LVDS and DRM_MODE_CONNECTOR_LVDS 
respectively, as that's what userspace usually expects for panels. That 
doesn't reflect the reality in your case though, so creating a new 
DRM_MODE_CONNECTOR_DPI type might be needed, possibly to be used with 
DRM_MODE_ENCODER_NONE.

As neither encoder can modify the mode, the same mode will be output on the 
two connectors.

> >>>>> You also can't be using the current device tree bindings because
> >>>>> they all assume a dependency from the display controller/output to
> >>>>> the panel. For hotplugging you'd need the dependency the other way
> >>>>> around (the panel needs to refer to the output by phandle).
> >>>> 
> >>>> Here [1] is a proposal for notification support in the drm_panel
> >>>> infrastructure (which is not that complicated), and here [2] is how
> >>>> I use it in my atmel-hlcdc driver to generate hotplug events.
> >>> 
> >>> Is there a way we could use the component framework for that ? I know
> >>> that partial notification isn't supported at the moment, but Russell
> >>> agreed it was a real use case that should be implemented at some
> >>> point.
> >> 
> >> I'll give it a try.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-28 12:19                       ` Laurent Pinchart
  (?)
@ 2014-08-28 14:21                         ` Boris BREZILLON
  -1 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-28 14:21 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Thierry Reding, Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Mark Rutland, devicetree, Pawel Moll,
	Ian Campbell, linux-kernel, Rob Herring, Bo Shen, Kumar Gala,
	linux-arm-kernel

Hi Laurent,

On Thu, 28 Aug 2014 14:19:22 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,

[...]

> >
> > I don't have any VGA connector (or I'm missing something :-)),
> 
> My bad.

No problem.

> 
> > but I have an LCD panel and an RGB to HDMI encoder connected on the same RGB
> > connector.
> 
> There's no such thing as an RGB connector in DRM. Your SoC has a parallel RGB 
> video output (I assume it's a DPI bus). From a DRM point of view, that bus 
> corresponds to the output of the CRTC.

Okay, this mean I'll have to dispatch some of the code I've put in
atmel_hlcdc_output.c into atmel_hlcdc_crtc.c (BTW, any chance you could
take a look at this files ?).

> 
> > > As DRM hardcodes the pipeline model to CRTC -> encoder -> connector, you
> > > will also need a DRM encoder in the VGA path. I suppose your board has a
> > > VGA DAC, that's the component you should expose as a DRM encoder (even if
> > > it can't be controlled and doesn't limit the valid modes).
> > 
> > Actually, my problem is that both devices are connected on the same RGB
> > connector, and thus share the same display mode (resolution, HSYNC,
> > VSYNC, RGB output mode, ...).
> > This means that all remote devices have to agree on a specific mode if
> > we want to mirror the display on several output devices, otherwise we
> > must disable one of the output devices.
> 
> That's not really a problem. From a DRM perspective you need to model your 
> device as
> 
> ,------.       ,---------------.       ,-----------------.
> | CRTC | -+--> | Dummy Encoder | ----> | Panel Connector |
> `------´  |    `---------------´       `-----------------´
>           |    ,---------------.       ,-----------------.
>           \--> | HDMI Encoder  | ----> | HDMI Connector  |
>                `---------------´       `-----------------´
> 
> The HDMI pipeline is pretty straightforward.
> 
> You have told me that the panel has a parallel RGB input without any encoder 
> in the panel pipeline (by the way, which panel model are you using ?). 
> However, DRM requires an encoder in every pipeline. You will thus need to 
> instantiate a dummy encoder. One option would be to set the encoder and 
> connector types to DRM_MODE_ENCODER_LVDS and DRM_MODE_CONNECTOR_LVDS 
> respectively, as that's what userspace usually expects for panels. That 
> doesn't reflect the reality in your case though, so creating a new 
> DRM_MODE_CONNECTOR_DPI type might be needed, possibly to be used with 
> DRM_MODE_ENCODER_NONE.
> 
> As neither encoder can modify the mode, the same mode will be output on the 
> two connectors.

There are still several things to I'd like to understand:
 1) who's gonna configure the RGB bus output format (RGB444, RGB666,
    RGB888) which directly depends on the device connected on this bus:
    the CRTC or the dummy and HDMI encoders.
 2) Where should the HDMI encoder/connector support be implemented:
    in drivers/gpu/drm/atmel-hlcdc, drivers/gpu/drm/bridge or somewhere
    else. My point is that I don't want to add specific support for the
    Sil902x transmitter chip in the hlcdc driver.

Sorry if these are silly questions, but I'm still trying to understand
how my case should be modeled :-).

Best Regards,

Boris

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-28 14:21                         ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-28 14:21 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Thierry Reding, Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	linux-pwm-u79uwXL29TY76Z2rM5mHXA, Samuel Ortiz, Lee Jones,
	Rob Clark, Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Pawel Moll, Ian Campbell, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	Rob Herring, Bo Shen, Kumar Gala,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Laurent,

On Thu, 28 Aug 2014 14:19:22 +0200
Laurent Pinchart <laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org> wrote:

> Hi Boris,

[...]

> >
> > I don't have any VGA connector (or I'm missing something :-)),
> 
> My bad.

No problem.

> 
> > but I have an LCD panel and an RGB to HDMI encoder connected on the same RGB
> > connector.
> 
> There's no such thing as an RGB connector in DRM. Your SoC has a parallel RGB 
> video output (I assume it's a DPI bus). From a DRM point of view, that bus 
> corresponds to the output of the CRTC.

Okay, this mean I'll have to dispatch some of the code I've put in
atmel_hlcdc_output.c into atmel_hlcdc_crtc.c (BTW, any chance you could
take a look at this files ?).

> 
> > > As DRM hardcodes the pipeline model to CRTC -> encoder -> connector, you
> > > will also need a DRM encoder in the VGA path. I suppose your board has a
> > > VGA DAC, that's the component you should expose as a DRM encoder (even if
> > > it can't be controlled and doesn't limit the valid modes).
> > 
> > Actually, my problem is that both devices are connected on the same RGB
> > connector, and thus share the same display mode (resolution, HSYNC,
> > VSYNC, RGB output mode, ...).
> > This means that all remote devices have to agree on a specific mode if
> > we want to mirror the display on several output devices, otherwise we
> > must disable one of the output devices.
> 
> That's not really a problem. From a DRM perspective you need to model your 
> device as
> 
> ,------.       ,---------------.       ,-----------------.
> | CRTC | -+--> | Dummy Encoder | ----> | Panel Connector |
> `------´  |    `---------------´       `-----------------´
>           |    ,---------------.       ,-----------------.
>           \--> | HDMI Encoder  | ----> | HDMI Connector  |
>                `---------------´       `-----------------´
> 
> The HDMI pipeline is pretty straightforward.
> 
> You have told me that the panel has a parallel RGB input without any encoder 
> in the panel pipeline (by the way, which panel model are you using ?). 
> However, DRM requires an encoder in every pipeline. You will thus need to 
> instantiate a dummy encoder. One option would be to set the encoder and 
> connector types to DRM_MODE_ENCODER_LVDS and DRM_MODE_CONNECTOR_LVDS 
> respectively, as that's what userspace usually expects for panels. That 
> doesn't reflect the reality in your case though, so creating a new 
> DRM_MODE_CONNECTOR_DPI type might be needed, possibly to be used with 
> DRM_MODE_ENCODER_NONE.
> 
> As neither encoder can modify the mode, the same mode will be output on the 
> two connectors.

There are still several things to I'd like to understand:
 1) who's gonna configure the RGB bus output format (RGB444, RGB666,
    RGB888) which directly depends on the device connected on this bus:
    the CRTC or the dummy and HDMI encoders.
 2) Where should the HDMI encoder/connector support be implemented:
    in drivers/gpu/drm/atmel-hlcdc, drivers/gpu/drm/bridge or somewhere
    else. My point is that I don't want to add specific support for the
    Sil902x transmitter chip in the hlcdc driver.

Sorry if these are silly questions, but I'm still trying to understand
how my case should be modeled :-).

Best Regards,

Boris

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-28 14:21                         ` Boris BREZILLON
  0 siblings, 0 replies; 103+ messages in thread
From: Boris BREZILLON @ 2014-08-28 14:21 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Laurent,

On Thu, 28 Aug 2014 14:19:22 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,

[...]

> >
> > I don't have any VGA connector (or I'm missing something :-)),
> 
> My bad.

No problem.

> 
> > but I have an LCD panel and an RGB to HDMI encoder connected on the same RGB
> > connector.
> 
> There's no such thing as an RGB connector in DRM. Your SoC has a parallel RGB 
> video output (I assume it's a DPI bus). From a DRM point of view, that bus 
> corresponds to the output of the CRTC.

Okay, this mean I'll have to dispatch some of the code I've put in
atmel_hlcdc_output.c into atmel_hlcdc_crtc.c (BTW, any chance you could
take a look at this files ?).

> 
> > > As DRM hardcodes the pipeline model to CRTC -> encoder -> connector, you
> > > will also need a DRM encoder in the VGA path. I suppose your board has a
> > > VGA DAC, that's the component you should expose as a DRM encoder (even if
> > > it can't be controlled and doesn't limit the valid modes).
> > 
> > Actually, my problem is that both devices are connected on the same RGB
> > connector, and thus share the same display mode (resolution, HSYNC,
> > VSYNC, RGB output mode, ...).
> > This means that all remote devices have to agree on a specific mode if
> > we want to mirror the display on several output devices, otherwise we
> > must disable one of the output devices.
> 
> That's not really a problem. From a DRM perspective you need to model your 
> device as
> 
> ,------.       ,---------------.       ,-----------------.
> | CRTC | -+--> | Dummy Encoder | ----> | Panel Connector |
> `------?  |    `---------------?       `-----------------?
>           |    ,---------------.       ,-----------------.
>           \--> | HDMI Encoder  | ----> | HDMI Connector  |
>                `---------------?       `-----------------?
> 
> The HDMI pipeline is pretty straightforward.
> 
> You have told me that the panel has a parallel RGB input without any encoder 
> in the panel pipeline (by the way, which panel model are you using ?). 
> However, DRM requires an encoder in every pipeline. You will thus need to 
> instantiate a dummy encoder. One option would be to set the encoder and 
> connector types to DRM_MODE_ENCODER_LVDS and DRM_MODE_CONNECTOR_LVDS 
> respectively, as that's what userspace usually expects for panels. That 
> doesn't reflect the reality in your case though, so creating a new 
> DRM_MODE_CONNECTOR_DPI type might be needed, possibly to be used with 
> DRM_MODE_ENCODER_NONE.
> 
> As neither encoder can modify the mode, the same mode will be output on the 
> two connectors.

There are still several things to I'd like to understand:
 1) who's gonna configure the RGB bus output format (RGB444, RGB666,
    RGB888) which directly depends on the device connected on this bus:
    the CRTC or the dummy and HDMI encoders.
 2) Where should the HDMI encoder/connector support be implemented:
    in drivers/gpu/drm/atmel-hlcdc, drivers/gpu/drm/bridge or somewhere
    else. My point is that I don't want to add specific support for the
    Sil902x transmitter chip in the hlcdc driver.

Sorry if these are silly questions, but I'm still trying to understand
how my case should be modeled :-).

Best Regards,

Boris

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 103+ messages in thread

* Re: [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
  2014-08-28 14:21                         ` Boris BREZILLON
@ 2014-08-28 22:52                           ` Laurent Pinchart
  -1 siblings, 0 replies; 103+ messages in thread
From: Laurent Pinchart @ 2014-08-28 22:52 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Thierry Reding, Ludovic Desroches, Nicolas Ferre,
	Jean-Christophe Plagniol-Villard, Alexandre Belloni,
	Andrew Victor, David Airlie, dri-devel, linux-pwm, Samuel Ortiz,
	Lee Jones, Rob Clark, Mark Rutland, devicetree, Pawel Moll,
	Ian Campbell, linux-kernel, Rob Herring, Bo Shen, Kumar Gala,
	linux-arm-kernel

Hi Boris,

On Thursday 28 August 2014 16:21:00 Boris BREZILLON wrote:
> On Thu, 28 Aug 2014 14:19:22 +0200 Laurent Pinchart wrote:
> > Hi Boris,
> 
> [...]
> 
> >> I don't have any VGA connector (or I'm missing something :-)),
> > 
> > My bad.
> 
> No problem.
> 
> >> but I have an LCD panel and an RGB to HDMI encoder connected on the same
> >> RGB connector.
> > 
> > There's no such thing as an RGB connector in DRM. Your SoC has a parallel
> > RGB video output (I assume it's a DPI bus). From a DRM point of view,
> > that bus corresponds to the output of the CRTC.
> 
> Okay, this mean I'll have to dispatch some of the code I've put in
> atmel_hlcdc_output.c into atmel_hlcdc_crtc.c (BTW, any chance you could
> take a look at this files ?).

Not in the very near future I'm afraid, I'm moving to a new flat in a couple 
of days, that will keep me pretty busy. If nobody has reviewed your patches in 
a week from now feel free to ping me.

> >>> As DRM hardcodes the pipeline model to CRTC -> encoder -> connector,
> >>> you will also need a DRM encoder in the VGA path. I suppose your board
> >>> has a VGA DAC, that's the component you should expose as a DRM encoder
> >>> (even if it can't be controlled and doesn't limit the valid modes).
> >> 
> >> Actually, my problem is that both devices are connected on the same RGB
> >> connector, and thus share the same display mode (resolution, HSYNC,
> >> VSYNC, RGB output mode, ...).
> >> This means that all remote devices have to agree on a specific mode if
> >> we want to mirror the display on several output devices, otherwise we
> >> must disable one of the output devices.
> > 
> > That's not really a problem. From a DRM perspective you need to model your
> > device as
> > 
> > ,------.       ,---------------.       ,-----------------.
> > | CRTC | -+--> | Dummy Encoder | ----> | Panel Connector |
> > `------´  |    `---------------´       `-----------------´
> >           |    ,---------------.       ,-----------------.
> >           \--> | HDMI Encoder  | ----> | HDMI Connector  |
> >                `---------------´       `-----------------´
> > 
> > The HDMI pipeline is pretty straightforward.
> > 
> > You have told me that the panel has a parallel RGB input without any
> > encoder in the panel pipeline (by the way, which panel model are you
> > using ?). However, DRM requires an encoder in every pipeline. You will
> > thus need to instantiate a dummy encoder. One option would be to set the
> > encoder and connector types to DRM_MODE_ENCODER_LVDS and
> > DRM_MODE_CONNECTOR_LVDS respectively, as that's what userspace usually
> > expects for panels. That doesn't reflect the reality in your case though,
> > so creating a new DRM_MODE_CONNECTOR_DPI type might be needed, possibly
> > to be used with DRM_MODE_ENCODER_NONE.
> > 
> > As neither encoder can modify the mode, the same mode will be output on
> > the two connectors.
> 
> There are still several things to I'd like to understand:
>  1) who's gonna configure the RGB bus output format (RGB444, RGB666,
>     RGB888) which directly depends on the device connected on this bus:
>     the CRTC or the dummy and HDMI encoders.

Your mileage my vary, but in general I believe this should be the 
responsibility of the CRTC driver (the HLCDC driver in your case), from 
information it gets from DT and/or queries dynamically from the encoders at 
runtime.

>  2) Where should the HDMI encoder/connector support be implemented:
>     in drivers/gpu/drm/atmel-hlcdc, drivers/gpu/drm/bridge or somewhere
>     else. My point is that I don't want to add specific support for the
>     Sil902x transmitter chip in the hlcdc driver.

The HDMI encoder should definitely be handled by a standalone driver. We have 
two infrastructures for this at the moment, drm_bridge and drm_encoder_slave. 
I'd like to see them being merged. I need to implement support for an HDMI 
encoder as well, I'll see if I can give this a try.

> Sorry if these are silly questions, but I'm still trying to understand
> how my case should be modeled :-).

As I don't have straightforward answers I won't consider the questions as 
silly :-)

-- 
Regards,

Laurent Pinchart


^ permalink raw reply	[flat|nested] 103+ messages in thread

* [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller
@ 2014-08-28 22:52                           ` Laurent Pinchart
  0 siblings, 0 replies; 103+ messages in thread
From: Laurent Pinchart @ 2014-08-28 22:52 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Boris,

On Thursday 28 August 2014 16:21:00 Boris BREZILLON wrote:
> On Thu, 28 Aug 2014 14:19:22 +0200 Laurent Pinchart wrote:
> > Hi Boris,
> 
> [...]
> 
> >> I don't have any VGA connector (or I'm missing something :-)),
> > 
> > My bad.
> 
> No problem.
> 
> >> but I have an LCD panel and an RGB to HDMI encoder connected on the same
> >> RGB connector.
> > 
> > There's no such thing as an RGB connector in DRM. Your SoC has a parallel
> > RGB video output (I assume it's a DPI bus). From a DRM point of view,
> > that bus corresponds to the output of the CRTC.
> 
> Okay, this mean I'll have to dispatch some of the code I've put in
> atmel_hlcdc_output.c into atmel_hlcdc_crtc.c (BTW, any chance you could
> take a look at this files ?).

Not in the very near future I'm afraid, I'm moving to a new flat in a couple 
of days, that will keep me pretty busy. If nobody has reviewed your patches in 
a week from now feel free to ping me.

> >>> As DRM hardcodes the pipeline model to CRTC -> encoder -> connector,
> >>> you will also need a DRM encoder in the VGA path. I suppose your board
> >>> has a VGA DAC, that's the component you should expose as a DRM encoder
> >>> (even if it can't be controlled and doesn't limit the valid modes).
> >> 
> >> Actually, my problem is that both devices are connected on the same RGB
> >> connector, and thus share the same display mode (resolution, HSYNC,
> >> VSYNC, RGB output mode, ...).
> >> This means that all remote devices have to agree on a specific mode if
> >> we want to mirror the display on several output devices, otherwise we
> >> must disable one of the output devices.
> > 
> > That's not really a problem. From a DRM perspective you need to model your
> > device as
> > 
> > ,------.       ,---------------.       ,-----------------.
> > | CRTC | -+--> | Dummy Encoder | ----> | Panel Connector |
> > `------?  |    `---------------?       `-----------------?
> >           |    ,---------------.       ,-----------------.
> >           \--> | HDMI Encoder  | ----> | HDMI Connector  |
> >                `---------------?       `-----------------?
> > 
> > The HDMI pipeline is pretty straightforward.
> > 
> > You have told me that the panel has a parallel RGB input without any
> > encoder in the panel pipeline (by the way, which panel model are you
> > using ?). However, DRM requires an encoder in every pipeline. You will
> > thus need to instantiate a dummy encoder. One option would be to set the
> > encoder and connector types to DRM_MODE_ENCODER_LVDS and
> > DRM_MODE_CONNECTOR_LVDS respectively, as that's what userspace usually
> > expects for panels. That doesn't reflect the reality in your case though,
> > so creating a new DRM_MODE_CONNECTOR_DPI type might be needed, possibly
> > to be used with DRM_MODE_ENCODER_NONE.
> > 
> > As neither encoder can modify the mode, the same mode will be output on
> > the two connectors.
> 
> There are still several things to I'd like to understand:
>  1) who's gonna configure the RGB bus output format (RGB444, RGB666,
>     RGB888) which directly depends on the device connected on this bus:
>     the CRTC or the dummy and HDMI encoders.

Your mileage my vary, but in general I believe this should be the 
responsibility of the CRTC driver (the HLCDC driver in your case), from 
information it gets from DT and/or queries dynamically from the encoders at 
runtime.

>  2) Where should the HDMI encoder/connector support be implemented:
>     in drivers/gpu/drm/atmel-hlcdc, drivers/gpu/drm/bridge or somewhere
>     else. My point is that I don't want to add specific support for the
>     Sil902x transmitter chip in the hlcdc driver.

The HDMI encoder should definitely be handled by a standalone driver. We have 
two infrastructures for this at the moment, drm_bridge and drm_encoder_slave. 
I'd like to see them being merged. I need to implement support for an HDMI 
encoder as well, I'll see if I can give this a try.

> Sorry if these are silly questions, but I'm still trying to understand
> how my case should be modeled :-).

As I don't have straightforward answers I won't consider the questions as 
silly :-)

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 103+ messages in thread

end of thread, other threads:[~2014-08-28 22:52 UTC | newest]

Thread overview: 103+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-07-22 13:11 [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller Boris BREZILLON
2014-07-22 13:11 ` Boris BREZILLON
2014-07-22 13:11 ` Boris BREZILLON
2014-07-22 13:11 ` [PATCH v4 01/11] mfd: add atmel-hlcdc driver Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11 ` [PATCH v4 02/11] mfd: add documentation for atmel-hlcdc DT bindings Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 14:32   ` Varka Bhadram
2014-07-22 14:32     ` Varka Bhadram
2014-07-22 13:11 ` [PATCH v4 03/11] pwm: add support for atmel-hlcdc-pwm device Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11 ` [PATCH v4 04/11] pwm: add DT bindings documentation for atmel-hlcdc-pwm driver Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 14:36   ` Varka Bhadram
2014-07-22 14:36     ` Varka Bhadram
2014-07-22 13:11 ` [PATCH v4 05/11] drm: add Atmel HLCDC Display Controller support Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11 ` [PATCH v4 06/11] drm: add DT bindings documentation for atmel-hlcdc-dc driver Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11 ` [PATCH v4 07/11] ARM: AT91/dt: split sama5d3 lcd pin definitions to match RGB mode configs Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11 ` [PATCH v4 08/11] ARM: AT91/dt: add alternative pin muxing for sama5d3 lcd pins Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11 ` [PATCH v4 09/11] ARM: at91/dt: define the HLCDC node available on sama5d3 SoCs Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11 ` [PATCH v4 10/11] ARM: at91/dt: add LCD panel description to sama5d3xdm.dtsi Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11 ` [PATCH v4 11/11] ARM: at91/dt: enable the LCD panel on sama5d3xek boards Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-07-22 13:11   ` Boris BREZILLON
2014-08-21  8:16 ` [PATCH v4 00/11] drm: add support for Atmel HLCDC Display Controller Ludovic Desroches
2014-08-21  8:16   ` Ludovic Desroches
2014-08-21  8:16   ` Ludovic Desroches
2014-08-21  8:37   ` Boris BREZILLON
2014-08-21  8:37     ` Boris BREZILLON
2014-08-21  8:37     ` Boris BREZILLON
2014-08-21  9:04     ` Thierry Reding
2014-08-21  9:04       ` Thierry Reding
2014-08-21  9:04       ` Thierry Reding
2014-08-21  9:41       ` Boris BREZILLON
2014-08-21  9:41         ` Boris BREZILLON
2014-08-21  9:41         ` Boris BREZILLON
2014-08-21  9:49         ` Boris BREZILLON
2014-08-21  9:49           ` Boris BREZILLON
2014-08-21  9:49           ` Boris BREZILLON
2014-08-21  9:52         ` Thierry Reding
2014-08-21  9:52           ` Thierry Reding
2014-08-21  9:52           ` Thierry Reding
2014-08-21 10:32           ` Andrzej Hajda
2014-08-21 10:32             ` Andrzej Hajda
2014-08-21 13:21             ` Thierry Reding
2014-08-21 13:21               ` Thierry Reding
2014-08-21 15:04               ` Andrzej Hajda
2014-08-21 15:04                 ` Andrzej Hajda
2014-08-21 15:30                 ` Boris BREZILLON
2014-08-21 15:30                   ` Boris BREZILLON
2014-08-21 15:30                   ` Boris BREZILLON
2014-08-21 16:10                   ` Andrzej Hajda
2014-08-21 16:10                     ` Andrzej Hajda
2014-08-21 16:10                     ` Andrzej Hajda
2014-08-21 13:06           ` Boris BREZILLON
2014-08-21 13:06             ` Boris BREZILLON
2014-08-21 13:16             ` Thierry Reding
2014-08-21 13:16               ` Thierry Reding
2014-08-21 13:16               ` Thierry Reding
2014-08-21 13:30               ` Boris BREZILLON
2014-08-21 13:30                 ` Boris BREZILLON
2014-08-21 14:32               ` Boris BREZILLON
2014-08-21 14:32                 ` Boris BREZILLON
2014-08-21 14:32                 ` Boris BREZILLON
2014-08-25 12:45               ` Daniel Vetter
2014-08-25 12:45                 ` Daniel Vetter
2014-08-25 12:45                 ` Daniel Vetter
2014-08-21 17:08             ` Laurent Pinchart
2014-08-21 17:08               ` Laurent Pinchart
2014-08-21 17:08               ` Laurent Pinchart
2014-08-21 17:26               ` Boris BREZILLON
2014-08-21 17:26                 ` Boris BREZILLON
2014-08-25 23:39                 ` Laurent Pinchart
2014-08-25 23:39                   ` Laurent Pinchart
2014-08-27  7:52                   ` Boris BREZILLON
2014-08-27  7:52                     ` Boris BREZILLON
2014-08-28 12:19                     ` Laurent Pinchart
2014-08-28 12:19                       ` Laurent Pinchart
2014-08-28 12:19                       ` Laurent Pinchart
2014-08-28 14:21                       ` Boris BREZILLON
2014-08-28 14:21                         ` Boris BREZILLON
2014-08-28 14:21                         ` Boris BREZILLON
2014-08-28 22:52                         ` Laurent Pinchart
2014-08-28 22:52                           ` Laurent Pinchart
2014-08-21 10:16         ` Andrzej Hajda
2014-08-21 10:16           ` Andrzej Hajda
2014-08-21 10:16           ` Andrzej Hajda

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.