linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 00/11] Add DRM for stih4xx platforms
@ 2014-05-29  6:36 Benjamin Gaignard
  2014-05-29  6:36 ` [PATCH v4 01/11] drm: sti: add bindings for DRM driver Benjamin Gaignard
                   ` (11 more replies)
  0 siblings, 12 replies; 19+ messages in thread
From: Benjamin Gaignard @ 2014-05-29  6:36 UTC (permalink / raw)
  To: dri-devel, linaro-mm-sig, linux-kernel, airlied
  Cc: lee.jones, Benjamin Gaignard

This series of patches add the support of DRM/KMS drivers for STMicroelectronics
chipsets stih416 and stih407.

version 4:
	- Remove depency between TVout it subdevices HDMI and HDA
	- Rework and simplify VTG and VTAC code
	- Fix numbers of typo and indentation
	- Remove debug (will be push in separate patches)
	- Fix remarks done in previous patcheset
	
	patches could be found here: git://git.linaro.org/people/benjamin.gaignard/kernel.git
	on branch: drm_kms_for_next-v4

version 3:
	- Correctly split code between probe and bind funtions
	- Squash some commits
	- remove HQ-VDP device code to have a smaller patcheset,
	  we will introduce it later.

	patches could be found here: git://git.linaro.org/people/benjamin.gaignard/kernel.git
	on branch: drm_kms_for_next-v3

version 2:
	- Use componentized device instead of register sub-devices in master
	driver probe function
	- Fix Makefile and Kconfig to only allow built-in compilation

	patches could be found here: git://git.linaro.org/people/benjamin.gaignard/kernel.git
	on branch: drm_kms_for_next-v2
	
version 1:
	- First path submission

Hardware is split in two main blocks: Compositor and TVout. Each of them
includes specific hardware IPs and the display timing are controlled by a specific
Video Timing Generator hardware IP (VTG).

Compositor is made of the follow hardware IPs:
 - GDP (Generic Display Pipeline) which is an entry point for graphic (RGB)
   buffers
 - VDP (Video Diplay Pipeline) which is an entry point for video (YUV) buffers
 - HQVDP (High Quality Video Display Processor) that supports scaling,
   deinterlacing and some miscellaneous image quality improvements.
   It fetches the Video decoded buffers from memory, processes them and pushes
   them to the Compositor through a HW dedicated bus.
 - Mixer is responsible of mixing all the entries depending of their
   respective z-order and layout

TVout is divided in 3 parts:
 - HDMI to generate HDMI signals, depending of chipset version HDMI phy can
   change.
 - HDA to generate signals for HD analog TV
 - VIP to control/switch data path coming from Compositor

On stih416 compositor and Tvout are on different dies so a Video Trafic Advance
inter-die Communication mechanism (VTAC) is needed.

+---------------------------------------------+   +----------------------------------------+
| +-------------------------------+   +----+  |   |  +----+   +--------------------------+ |
| |                               |   |    |  |   |  |    |   |  +---------+     +----+  | |
| | +----+              +------+  |   |    |  |   |  |    |   |  | VIP     |---->|HDMI|  | |
| | |GPD +------------->|      |  |   |    |  |   |  |    |   |  |         |     +----+  | |
| | +----+              |Mixer |--|-->|    |  |   |  |    |---|->| switcher|             | |
| |                     |      |  |   |    |  |   |  |    |   |  |         |     +----+  | |
| |                     |      |  |   |    |  |   |  |    |   |  |         |---->|HDA |  | |
| |                     +------+  |   |VTAC|========>|VTAC|   |  +---------+     +----+  | |
| |                               |   |    |  |   |  |    |   |                          | |
| |         Compositor            |   |    |  |   |  |    |   |           TVout          | |
| +-------------------------------+   |    |  |   |  |    |   +--------------------------+ |
|                      ^              |    |  |   |  |    |             ^                  |
|                      |              |    |  |   |  |    |             |                  |
|               +--------------+      |    |  |   |  |    |      +-------------+           |
|               | VTG (master) |----->|    |  |   |  |    |----->| VTG (slave) |           |
|               +--------------+      +----+  |   |  +----+      +-------------+           |
|Digital die                                  |   |                              Analog Die|
+---------------------------------------------+   +----------------------------------------+

On stih407 Compositor and Tvout are on the same die

+-----------------------------------------------------------------+
| +-------------------------------+  +--------------------------+ |
| |                               |  |  +---------+     +----+  | |
| | +----+              +------+  |  |  | VIP     |---->|HDMI|  | |
| | |GPD +------------->|      |  |  |  |         |     +----+  | |
| | +----+              |Mixer |--|--|->| switcher|             | |
| | +----+   +-----+    |      |  |  |  |         |     +----+  | |
| | |VDP +-->+HQVDP+--->|      |  |  |  |         |---->|HDA |  | |
| | +----+   +-----+    +------+  |  |  +---------+     +----+  | |
| |                               |  |                          | |
| |         Compositor            |  |           TVout          | |
| +-------------------------------+  +--------------------------+ |
|                              ^        ^                         |
|                              |        |                         |
|                           +--------------+                      |
|                           |     VTG      |                      |
|                           +--------------+                      |
|Digital die                                                      |
+-----------------------------------------------------------------+

In addition of the drivers for the IPs listed before a thin I2C driver (hdmiddc) is used
by HDMI driver to retrieve EDID for monitor.

To unify interfaces of GDP and VDP we create a "layer" interface called by
compositor to control both GPD and VDP.

Hardware have memory contraints (alignment, contiguous) so we use CMA drm helpers functions
to allocate frame buffer.

File naming convention is:
 - sti_* for IPs drivers
 - sti_drm_* for drm functions implementation.

Benjamin Gaignard (11):
  drm: sti: add bindings for DRM driver
  drm: sti: add VTG driver
  drm: sti: add VTAC drivers
  drm: sti: add HDMI driver
  drm: sti: add HDA driver
  drm: sti: add TVOut driver
  drm: sti: add GDP layer
  drm: sti: add VID layer
  drm: sti: add Mixer
  drm: sti: add Compositor
  drm: sti: Add DRM driver itself

 .../devicetree/bindings/gpu/st,stih4xx.txt         | 189 ++++++
 drivers/gpu/drm/Kconfig                            |   2 +
 drivers/gpu/drm/Makefile                           |   1 +
 drivers/gpu/drm/sti/Kconfig                        |  14 +
 drivers/gpu/drm/sti/Makefile                       |  19 +
 drivers/gpu/drm/sti/sti_compositor.c               | 265 ++++++++
 drivers/gpu/drm/sti/sti_compositor.h               |  90 +++
 drivers/gpu/drm/sti/sti_drm_connector.c            | 114 ++++
 drivers/gpu/drm/sti/sti_drm_connector.h            |  55 ++
 drivers/gpu/drm/sti/sti_drm_crtc.c                 | 436 ++++++++++++
 drivers/gpu/drm/sti/sti_drm_crtc.h                 |  21 +
 drivers/gpu/drm/sti/sti_drm_drv.c                  | 229 +++++++
 drivers/gpu/drm/sti/sti_drm_drv.h                  |  35 +
 drivers/gpu/drm/sti/sti_drm_encoder.c              | 108 +++
 drivers/gpu/drm/sti/sti_drm_encoder.h              |  17 +
 drivers/gpu/drm/sti/sti_drm_plane.c                | 192 ++++++
 drivers/gpu/drm/sti/sti_drm_plane.h                |  17 +
 drivers/gpu/drm/sti/sti_gdp.c                      | 512 ++++++++++++++
 drivers/gpu/drm/sti/sti_gdp.h                      |  73 ++
 drivers/gpu/drm/sti/sti_hda.c                      | 727 ++++++++++++++++++++
 drivers/gpu/drm/sti/sti_hdmi.c                     | 748 +++++++++++++++++++++
 drivers/gpu/drm/sti/sti_hdmi.h                     |  88 +++
 drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c         | 336 +++++++++
 drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h         |  14 +
 drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c         | 211 ++++++
 drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h         |  14 +
 drivers/gpu/drm/sti/sti_layer.c                    | 312 +++++++++
 drivers/gpu/drm/sti/sti_layer.h                    | 114 ++++
 drivers/gpu/drm/sti/sti_mixer.c                    | 249 +++++++
 drivers/gpu/drm/sti/sti_mixer.h                    |  52 ++
 drivers/gpu/drm/sti/sti_tvout.c                    | 687 +++++++++++++++++++
 drivers/gpu/drm/sti/sti_tvout.h                    |  18 +
 drivers/gpu/drm/sti/sti_vid.c                      | 145 ++++
 drivers/gpu/drm/sti/sti_vid.h                      |  33 +
 drivers/gpu/drm/sti/sti_vtac.c                     | 211 ++++++
 drivers/gpu/drm/sti/sti_vtg.c                      | 356 ++++++++++
 drivers/gpu/drm/sti/sti_vtg.h                      |  28 +
 37 files changed, 6732 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpu/st,stih4xx.txt
 create mode 100644 drivers/gpu/drm/sti/Kconfig
 create mode 100644 drivers/gpu/drm/sti/Makefile
 create mode 100644 drivers/gpu/drm/sti/sti_compositor.c
 create mode 100644 drivers/gpu/drm/sti/sti_compositor.h
 create mode 100644 drivers/gpu/drm/sti/sti_drm_connector.c
 create mode 100644 drivers/gpu/drm/sti/sti_drm_connector.h
 create mode 100644 drivers/gpu/drm/sti/sti_drm_crtc.c
 create mode 100644 drivers/gpu/drm/sti/sti_drm_crtc.h
 create mode 100644 drivers/gpu/drm/sti/sti_drm_drv.c
 create mode 100644 drivers/gpu/drm/sti/sti_drm_drv.h
 create mode 100644 drivers/gpu/drm/sti/sti_drm_encoder.c
 create mode 100644 drivers/gpu/drm/sti/sti_drm_encoder.h
 create mode 100644 drivers/gpu/drm/sti/sti_drm_plane.c
 create mode 100644 drivers/gpu/drm/sti/sti_drm_plane.h
 create mode 100644 drivers/gpu/drm/sti/sti_gdp.c
 create mode 100644 drivers/gpu/drm/sti/sti_gdp.h
 create mode 100644 drivers/gpu/drm/sti/sti_hda.c
 create mode 100644 drivers/gpu/drm/sti/sti_hdmi.c
 create mode 100644 drivers/gpu/drm/sti/sti_hdmi.h
 create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c
 create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h
 create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c
 create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h
 create mode 100644 drivers/gpu/drm/sti/sti_layer.c
 create mode 100644 drivers/gpu/drm/sti/sti_layer.h
 create mode 100644 drivers/gpu/drm/sti/sti_mixer.c
 create mode 100644 drivers/gpu/drm/sti/sti_mixer.h
 create mode 100644 drivers/gpu/drm/sti/sti_tvout.c
 create mode 100644 drivers/gpu/drm/sti/sti_tvout.h
 create mode 100644 drivers/gpu/drm/sti/sti_vid.c
 create mode 100644 drivers/gpu/drm/sti/sti_vid.h
 create mode 100644 drivers/gpu/drm/sti/sti_vtac.c
 create mode 100644 drivers/gpu/drm/sti/sti_vtg.c
 create mode 100644 drivers/gpu/drm/sti/sti_vtg.h

-- 
1.9.1


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

* [PATCH v4 01/11] drm: sti: add bindings for DRM driver
  2014-05-29  6:36 [PATCH v4 00/11] Add DRM for stih4xx platforms Benjamin Gaignard
@ 2014-05-29  6:36 ` Benjamin Gaignard
  2014-05-29  6:36 ` [PATCH v4 02/11] drm: sti: add VTG driver Benjamin Gaignard
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Benjamin Gaignard @ 2014-05-29  6:36 UTC (permalink / raw)
  To: dri-devel, linaro-mm-sig, linux-kernel, airlied
  Cc: lee.jones, Benjamin Gaignard

Add DRM/KMS driver bindings documentation.
Describe the required properties for each of the hardware IPs drivers.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
---
 .../devicetree/bindings/gpu/st,stih4xx.txt         | 189 +++++++++++++++++++++
 1 file changed, 189 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpu/st,stih4xx.txt

diff --git a/Documentation/devicetree/bindings/gpu/st,stih4xx.txt b/Documentation/devicetree/bindings/gpu/st,stih4xx.txt
new file mode 100644
index 0000000..59a59a7
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpu/st,stih4xx.txt
@@ -0,0 +1,189 @@
+STMicroelectronics stih4xx platforms
+
+- sti-vtg: video timing generator
+  Required properties:
+  - compatible: "st,vtg"
+  - reg: Physical base address of the IP registers and length of memory mapped region.
+  Optional properties:
+  - interrupts : VTG interrupt number to the CPU.
+  - st,slave: phandle on a slave vtg
+
+- sti-vtac: video timing advanced inter dye communication Rx and TX
+  Required properties:
+  - compatible: "st,vtac-main" or "st,vtac-aux"
+  - reg: Physical base address of the IP registers and length of memory mapped region.
+  - clocks: from common clock binding: handle hardware IP needed clocks, the
+    number of clocks may depend of the SoC type.
+    See ../clocks/clock-bindings.txt for details.
+  - clock-names: names of the clocks listed in clocks property in the same
+    order.
+
+- sti-display-subsystem: Master device for DRM sub-components
+  This device must be the parent of all the sub-components and is responsible
+  of bind them.
+  Required properties:
+  - compatible: "st,sti-display-subsystem"
+  - ranges: to allow probing of subdevices
+
+- sti-compositor: frame compositor engine
+  must be a child of sti-display-subsystem
+  Required properties:
+  - compatible: "st,stih<chip>-compositor"
+  - reg: Physical base address of the IP registers and length of memory mapped region.
+  - clocks: from common clock binding: handle hardware IP needed clocks, the
+    number of clocks may depend of the SoC type.
+    See ../clocks/clock-bindings.txt for details.
+  - clock-names: names of the clocks listed in clocks property in the same
+    order.
+  - resets: resets to be used by the device
+    See ../reset/reset.txt for details.
+  - reset-names: names of the resets listed in resets property in the same
+    order.
+  - st,vtg: phandle(s) on vtg device (main and aux) nodes.
+
+- sti-tvout: video out hardware block
+  must be a child of sti-display-subsystem
+  Required properties:
+  - compatible: "st,stih<chip>-tvout"
+  - reg: Physical base address of the IP registers and length of memory mapped region.
+  - reg-names: names of the mapped memory regions listed in regs property in
+    the same order.
+  - resets: resets to be used by the device
+    See ../reset/reset.txt for details.
+  - reset-names: names of the resets listed in resets property in the same
+    order.
+  - ranges: to allow probing of subdevices
+
+- sti-hdmi: hdmi output block
+  must be a child of sti-tvout
+  Required properties:
+  - compatible: "st,stih<chip>-hdmi";
+  - reg: Physical base address of the IP registers and length of memory mapped region.
+  - reg-names: names of the mapped memory regions listed in regs property in
+    the same order.
+  - interrupts : HDMI interrupt number to the CPU.
+  - interrupt-names: name of the interrupts listed in interrupts property in
+    the same order
+  - clocks: from common clock binding: handle hardware IP needed clocks, the
+    number of clocks may depend of the SoC type.
+  - clock-names: names of the clocks listed in clocks property in the same
+    order.
+  - hdmi,hpd-gpio: gpio id to detect if an hdmi cable is plugged or not.
+
+sti-hda:
+  Required properties:
+  must be a child of sti-tvout
+  - compatible: "st,stih<chip>-hda"
+  - reg: Physical base address of the IP registers and length of memory mapped region.
+  - reg-names: names of the mapped memory regions listed in regs property in
+    the same order.
+  - clocks: from common clock binding: handle hardware IP needed clocks, the
+    number of clocks may depend of the SoC type.
+    See ../clocks/clock-bindings.txt for details.
+  - clock-names: names of the clocks listed in clocks property in the same
+    order.
+
+Example:
+
+/ {
+	...
+
+	vtg_main_slave: sti-vtg-main-slave@fe85A800 {
+		compatible	= "st,vtg";
+		reg		= <0xfe85A800 0x300>;
+		interrupts	= <GIC_SPI 175 IRQ_TYPE_NONE>;
+	};
+
+	vtg_main: sti-vtg-main-master@fd348000 {
+		compatible	= "st,vtg";
+		reg		= <0xfd348000 0x400>;
+		st,slave	= <&vtg_main_slave>;
+	};
+
+	vtg_aux_slave: sti-vtg-aux-slave@fd348400 {
+		compatible	= "st,vtg";
+		reg		= <0xfe858200 0x300>;
+		interrupts	= <GIC_SPI 176 IRQ_TYPE_NONE>;
+	};
+
+	vtg_aux: sti-vtg-aux-master@fd348400 {
+		compatible	= "st,vtg";
+		reg		= <0xfd348400 0x400>;
+		st,slave	= <&vtg_aux_slave>;
+	};
+
+
+	sti-vtac-rx-main@fee82800 {
+		compatible	= "st,vtac-main";
+		reg		= <0xfee82800 0x200>;
+		clock-names     = "vtac";
+		clocks          = <&CLK_M_A2_DIV0 CLK_M_VTAC_MAIN_PHY>;
+	};
+
+	sti-vtac-rx-aux@fee82a00 {
+		compatible	= "st,vtac-aux";
+		reg		= <0xfee82a00 0x200>;
+		clock-names     = "vtac";
+		clocks          = <&CLK_M_A2_DIV0 CLK_M_VTAC_AUX_PHY>;
+	};
+
+	sti-vtac-tx-main@fd349000 {
+		compatible	= "st,vtac-main";
+		reg		= <0xfd349000 0x200>, <0xfd320000 0x10000>;
+		clock-names     = "vtac";
+		clocks           = <&CLK_S_A1_HS CLK_S_VTAC_TX_PHY>;
+	};
+
+	sti-vtac-tx-aux@fd349200 {
+		compatible	= "st,vtac-aux";
+		reg		= <0xfd349200 0x200>, <0xfd320000 0x10000>;
+		clock-names     = "vtac";
+		clocks          = <&CLK_S_A1_HS CLK_S_VTAC_TX_PHY>;
+	};
+
+	sti-display-subsystem {
+		compatible = "st,sti-display-subsystem";
+		ranges;
+
+		sti-compositor@fd340000 {
+			compatible	= "st,stih416-compositor";
+			reg		= <0xfd340000 0x1000>;
+			clock-names	= "compo_main", "compo_aux",
+			                  "pix_main", "pix_aux";
+			clocks          = <&CLK_M_A2_DIV1 CLK_M_COMPO_MAIN>, <&CLK_M_A2_DIV1 CLK_M_COMPO_AUX>,
+					  <&CLOCKGEN_C_VCC CLK_S_PIX_MAIN>, <&CLOCKGEN_C_VCC CLK_S_PIX_AUX>;
+			reset-names     = "compo-main", "compo-aux";
+			resets          = <&softreset STIH416_COMPO_M_SOFTRESET>, <&softreset STIH416_COMPO_A_SOFTRESET>;
+			st,vtg		= <&vtg_main>, <&vtg_aux>;
+		};
+
+		sti-tvout@fe000000 {
+			compatible	= "st,stih416-tvout";
+			reg		= <0xfe000000 0x1000>, <0xfe85a000 0x400>, <0xfe830000 0x10000>;
+			reg-names	= "tvout-reg", "hda-reg", "syscfg";
+			reset-names     = "tvout";
+			resets          = <&softreset STIH416_HDTVOUT_SOFTRESET>;
+			ranges;
+
+			sti-hdmi@fe85c000 {
+				compatible	= "st,stih416-hdmi";
+				reg		= <0xfe85c000 0x1000>, <0xfe830000 0x10000>;
+				reg-names	= "hdmi-reg", "syscfg";
+				interrupts	= <GIC_SPI 173 IRQ_TYPE_NONE>;
+				interrupt-names	= "irq";
+				clock-names	= "pix", "tmds", "phy", "audio";
+				clocks          = <&CLOCKGEN_C_VCC CLK_S_PIX_HDMI>, <&CLOCKGEN_C_VCC CLK_S_TMDS_HDMI>, <&CLOCKGEN_C_VCC CLK_S_HDMI_REJECT_PLL>, <&CLOCKGEN_B1 CLK_S_PCM_0>;
+				hdmi,hpd-gpio	= <&PIO2 5>;
+			};
+
+			sti-hda@fe85a000 {
+				compatible	= "st,stih416-hda";
+				reg		= <0xfe85a000 0x400>, <0xfe83085c 0x4>;
+				reg-names	= "hda-reg", "video-dacs-ctrl";
+				clock-names	= "pix", "hddac";
+				clocks          = <&CLOCKGEN_C_VCC CLK_S_PIX_HD>, <&CLOCKGEN_C_VCC CLK_S_HDDAC>;
+			};
+		};
+	};
+	...
+};
-- 
1.9.1


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

* [PATCH v4 02/11] drm: sti: add VTG driver
  2014-05-29  6:36 [PATCH v4 00/11] Add DRM for stih4xx platforms Benjamin Gaignard
  2014-05-29  6:36 ` [PATCH v4 01/11] drm: sti: add bindings for DRM driver Benjamin Gaignard
@ 2014-05-29  6:36 ` Benjamin Gaignard
  2014-05-29  6:36 ` [PATCH v4 03/11] drm: sti: add VTAC drivers Benjamin Gaignard
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Benjamin Gaignard @ 2014-05-29  6:36 UTC (permalink / raw)
  To: dri-devel, linaro-mm-sig, linux-kernel, airlied
  Cc: lee.jones, Benjamin Gaignard

Video Time Generator drivers are used to synchronize the compositor
and tvout hardware IPs by providing line count, sample count,
synchronization signals (HSYNC, VSYNC) and top and bottom fields
indication.
VTG are used by pair for each data path (main or auxiliary)
one for master and one for slave.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
---
 drivers/gpu/drm/Kconfig       |   2 +
 drivers/gpu/drm/Makefile      |   1 +
 drivers/gpu/drm/sti/Kconfig   |   5 +
 drivers/gpu/drm/sti/Makefile  |   2 +
 drivers/gpu/drm/sti/sti_vtg.c | 356 ++++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sti/sti_vtg.h |  28 ++++
 6 files changed, 394 insertions(+)
 create mode 100644 drivers/gpu/drm/sti/Kconfig
 create mode 100644 drivers/gpu/drm/sti/Makefile
 create mode 100644 drivers/gpu/drm/sti/sti_vtg.c
 create mode 100644 drivers/gpu/drm/sti/sti_vtg.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index d1cc2f6..0e30029 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -201,3 +201,5 @@ source "drivers/gpu/drm/tegra/Kconfig"
 source "drivers/gpu/drm/panel/Kconfig"
 
 source "drivers/gpu/drm/bridge/Kconfig"
+
+source "drivers/gpu/drm/sti/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 5e792b0..44f7b17 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_DRM_QXL) += qxl/
 obj-$(CONFIG_DRM_BOCHS) += bochs/
 obj-$(CONFIG_DRM_MSM) += msm/
 obj-$(CONFIG_DRM_TEGRA) += tegra/
+obj-$(CONFIG_DRM_STI) += sti/
 obj-y			+= i2c/
 obj-y			+= panel/
 obj-y			+= bridge/
diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig
new file mode 100644
index 0000000..cbd664b
--- /dev/null
+++ b/drivers/gpu/drm/sti/Kconfig
@@ -0,0 +1,5 @@
+config DRM_STI
+	bool "DRM Support for STMicroelectronics SoC stiH41x Series"
+	depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM)
+	help
+	  Choose this option to enable DRM on STM stiH41x chipset
diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
new file mode 100644
index 0000000..3658a13e
--- /dev/null
+++ b/drivers/gpu/drm/sti/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_DRM_STI) += \
+	sti_vtg.o
diff --git a/drivers/gpu/drm/sti/sti_vtg.c b/drivers/gpu/drm/sti/sti_vtg.c
new file mode 100644
index 0000000..49f95d4
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vtg.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          Vincent Abriou <vincent.abriou@st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+
+#include <drm/drmP.h>
+
+#include "sti_vtg.h"
+
+#define VTG_TYPE_MASTER         0
+#define VTG_TYPE_SLAVE_BY_EXT0  1
+
+/* registers offset */
+#define VTG_MODE            0x0000
+#define VTG_CLKLN           0x0008
+#define VTG_HLFLN           0x000C
+#define VTG_DRST_AUTOC      0x0010
+#define VTG_VID_TFO         0x0040
+#define VTG_VID_TFS         0x0044
+#define VTG_VID_BFO         0x0048
+#define VTG_VID_BFS         0x004C
+
+#define VTG_HOST_ITS        0x0078
+#define VTG_HOST_ITS_BCLR   0x007C
+#define VTG_HOST_ITM_BCLR   0x0088
+#define VTG_HOST_ITM_BSET   0x008C
+
+#define VTG_H_HD_1          0x00C0
+#define VTG_TOP_V_VD_1      0x00C4
+#define VTG_BOT_V_VD_1      0x00C8
+#define VTG_TOP_V_HD_1      0x00CC
+#define VTG_BOT_V_HD_1      0x00D0
+
+#define VTG_H_HD_2          0x00E0
+#define VTG_TOP_V_VD_2      0x00E4
+#define VTG_BOT_V_VD_2      0x00E8
+#define VTG_TOP_V_HD_2      0x00EC
+#define VTG_BOT_V_HD_2      0x00F0
+
+#define VTG_H_HD_3          0x0100
+#define VTG_TOP_V_VD_3      0x0104
+#define VTG_BOT_V_VD_3      0x0108
+#define VTG_TOP_V_HD_3      0x010C
+#define VTG_BOT_V_HD_3      0x0110
+
+#define VTG_IRQ_BOTTOM      BIT(0)
+#define VTG_IRQ_TOP         BIT(1)
+#define VTG_IRQ_MASK        (VTG_IRQ_TOP | VTG_IRQ_BOTTOM)
+
+/* delay introduced by the Arbitrary Waveform Generator in nb of pixels */
+#define AWG_DELAY_HD        (-9)
+#define AWG_DELAY_ED        (-8)
+#define AWG_DELAY_SD        (-7)
+
+LIST_HEAD(vtg_lookup);
+
+/**
+ * STI VTG structure
+ *
+ * @dev: pointer to device driver
+ * @data: data associated to the device
+ * @irq: VTG irq
+ * @type: VTG type (main or aux)
+ * @notifier_list: notifier callback
+ * @crtc_id: the crtc id for vblank event
+ * @slave: slave vtg
+ * @link: List node to link the structure in lookup list
+ */
+struct sti_vtg {
+	struct device *dev;
+	struct device_node *np;
+	void __iomem *regs;
+	int irq;
+	u32 irq_status;
+	struct raw_notifier_head notifier_list;
+	int crtc_id;
+	struct sti_vtg *slave;
+	struct list_head link;
+};
+
+static void vtg_register(struct sti_vtg *vtg)
+{
+	list_add_tail(&vtg->link, &vtg_lookup);
+}
+
+struct sti_vtg *of_vtg_find(struct device_node *np)
+{
+	struct sti_vtg *vtg;
+
+	list_for_each_entry(vtg, &vtg_lookup, link) {
+		if (vtg->np == np)
+			return vtg;
+	}
+	return NULL;
+}
+
+static void vtg_reset(struct sti_vtg *vtg)
+{
+	/* reset slave and then master */
+	if (vtg->slave)
+		vtg_reset(vtg->slave);
+
+	writel(1, vtg->regs + VTG_DRST_AUTOC);
+}
+
+static void vtg_set_mode(struct sti_vtg *vtg,
+			 int type, const struct drm_display_mode *mode)
+{
+	u32 tmp;
+
+	if (vtg->slave)
+		vtg_set_mode(vtg->slave, VTG_TYPE_SLAVE_BY_EXT0, mode);
+
+	writel(mode->htotal, vtg->regs + VTG_CLKLN);
+	writel(mode->vtotal * 2, vtg->regs + VTG_HLFLN);
+
+	tmp = (mode->vtotal - mode->vsync_start + 1) << 16;
+	tmp |= mode->htotal - mode->hsync_start;
+	writel(tmp, vtg->regs + VTG_VID_TFO);
+	writel(tmp, vtg->regs + VTG_VID_BFO);
+
+	tmp = (mode->vdisplay + mode->vtotal - mode->vsync_start + 1) << 16;
+	tmp |= mode->hdisplay + mode->htotal - mode->hsync_start;
+	writel(tmp, vtg->regs + VTG_VID_TFS);
+	writel(tmp, vtg->regs + VTG_VID_BFS);
+
+	/* prepare VTG set 1 and 2 for HDMI and VTG set 3 for HD DAC */
+	tmp = (mode->hsync_end - mode->hsync_start) << 16;
+	writel(tmp, vtg->regs + VTG_H_HD_1);
+	writel(tmp, vtg->regs + VTG_H_HD_2);
+
+	tmp = (mode->vsync_end - mode->vsync_start + 1) << 16;
+	tmp |= 1;
+	writel(tmp, vtg->regs + VTG_TOP_V_VD_1);
+	writel(tmp, vtg->regs + VTG_BOT_V_VD_1);
+	writel(0, vtg->regs + VTG_TOP_V_HD_1);
+	writel(0, vtg->regs + VTG_BOT_V_HD_1);
+
+	/* prepare VTG set 2 for for HD DCS */
+	writel(tmp, vtg->regs + VTG_TOP_V_VD_2);
+	writel(tmp, vtg->regs + VTG_BOT_V_VD_2);
+	writel(0, vtg->regs + VTG_TOP_V_HD_2);
+	writel(0, vtg->regs + VTG_BOT_V_HD_2);
+
+	/* prepare VTG set 3 for HD Analog in HD mode */
+	tmp = (mode->hsync_end - mode->hsync_start + AWG_DELAY_HD) << 16;
+	tmp |= mode->htotal + AWG_DELAY_HD;
+	writel(tmp, vtg->regs + VTG_H_HD_3);
+
+	tmp = (mode->vsync_end - mode->vsync_start) << 16;
+	tmp |= mode->vtotal;
+	writel(tmp, vtg->regs + VTG_TOP_V_VD_3);
+	writel(tmp, vtg->regs + VTG_BOT_V_VD_3);
+
+	tmp = (mode->htotal + AWG_DELAY_HD) << 16;
+	tmp |= mode->htotal + AWG_DELAY_HD;
+	writel(tmp, vtg->regs + VTG_TOP_V_HD_3);
+	writel(tmp, vtg->regs + VTG_BOT_V_HD_3);
+
+	/* mode */
+	writel(type, vtg->regs + VTG_MODE);
+}
+
+static void vtg_enable_irq(struct sti_vtg *vtg)
+{
+	/* clear interrupt status and mask */
+	writel(0xFFFF, vtg->regs + VTG_HOST_ITS_BCLR);
+	writel(0xFFFF, vtg->regs + VTG_HOST_ITM_BCLR);
+	writel(VTG_IRQ_MASK, vtg->regs + VTG_HOST_ITM_BSET);
+}
+
+void sti_vtg_set_config(struct sti_vtg *vtg,
+		const struct drm_display_mode *mode)
+{
+	/* write configuration */
+	vtg_set_mode(vtg, VTG_TYPE_MASTER, mode);
+
+	vtg_reset(vtg);
+
+	/* enable irq for the vtg vblank synchro */
+	if (vtg->slave)
+		vtg_enable_irq(vtg->slave);
+	else
+		vtg_enable_irq(vtg);
+}
+
+/**
+ * sti_vtg_get_line_number
+ *
+ * @mode: display mode to be used
+ * @y:    line
+ *
+ * Return the line number according to the display mode taking
+ * into account the Sync and Back Porch information.
+ * Video frame line numbers start at 1, y starts at 0.
+ * In interlaced modes the start line is the field line number of the odd
+ * field, but y is still defined as a progressive frame.
+ */
+u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y)
+{
+	u32 start_line = mode.vtotal - mode.vsync_start + 1;
+
+	if (mode.flags & DRM_MODE_FLAG_INTERLACE)
+		start_line *= 2;
+
+	return start_line + y;
+}
+
+/**
+ * sti_vtg_get_pixel_number
+ *
+ * @mode: display mode to be used
+ * @x:    row
+ *
+ * Return the pixel number according to the display mode taking
+ * into account the Sync and Back Porch information.
+ * Pixels are counted from 0.
+ */
+u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x)
+{
+	return mode.htotal - mode.hsync_start + x;
+}
+
+int sti_vtg_register_client(struct sti_vtg *vtg,
+		struct notifier_block *nb, int crtc_id)
+{
+	if (vtg->slave)
+		return sti_vtg_register_client(vtg->slave, nb, crtc_id);
+
+	vtg->crtc_id = crtc_id;
+	return raw_notifier_chain_register(&vtg->notifier_list, nb);
+}
+
+int sti_vtg_unregister_client(struct sti_vtg *vtg, struct notifier_block *nb)
+{
+	if (vtg->slave)
+		return sti_vtg_unregister_client(vtg->slave, nb);
+
+	return raw_notifier_chain_unregister(&vtg->notifier_list, nb);
+}
+
+static irqreturn_t vtg_irq_thread(int irq, void *arg)
+{
+	struct sti_vtg *vtg = arg;
+	u32 event;
+
+	event = (vtg->irq_status & VTG_IRQ_TOP) ?
+		VTG_TOP_FIELD_EVENT : VTG_BOTTOM_FIELD_EVENT;
+
+	raw_notifier_call_chain(&vtg->notifier_list, event, &vtg->crtc_id);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vtg_irq(int irq, void *arg)
+{
+	struct sti_vtg *vtg = arg;
+
+	vtg->irq_status = readl(vtg->regs + VTG_HOST_ITS);
+
+	writel(vtg->irq_status, vtg->regs + VTG_HOST_ITS_BCLR);
+
+	/* force sync bus write */
+	readl(vtg->regs + VTG_HOST_ITS);
+
+	return IRQ_WAKE_THREAD;
+}
+
+static int vtg_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np;
+	struct sti_vtg *vtg;
+	struct resource *res;
+	char irq_name[32];
+	int ret;
+
+	vtg = devm_kzalloc(dev, sizeof(*vtg), GFP_KERNEL);
+	if (!vtg)
+		return -ENOMEM;
+
+	vtg->dev = dev;
+	vtg->np = pdev->dev.of_node;
+
+	/* Get Memory ressources */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		DRM_ERROR("Get memory resource failed\n");
+		return -ENOMEM;
+	}
+	vtg->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
+
+	np = of_parse_phandle(pdev->dev.of_node, "st,slave", 0);
+	if (np) {
+		vtg->slave = of_vtg_find(np);
+
+		if (!vtg->slave)
+			return -EPROBE_DEFER;
+	} else {
+		vtg->irq = platform_get_irq(pdev, 0);
+		if (IS_ERR_VALUE(vtg->irq)) {
+			DRM_ERROR("Failed to get VTG interrupt\n");
+			return vtg->irq;
+		}
+
+		snprintf(irq_name, sizeof(irq_name), "vsync-%s",
+				dev_name(vtg->dev));
+
+		RAW_INIT_NOTIFIER_HEAD(&vtg->notifier_list);
+
+		ret = devm_request_threaded_irq(dev, vtg->irq, vtg_irq,
+				vtg_irq_thread, IRQF_ONESHOT, irq_name, vtg);
+		if (IS_ERR_VALUE(ret)) {
+			DRM_ERROR("Failed to register VTG interrupt\n");
+			return ret;
+		}
+	}
+
+	vtg_register(vtg);
+	platform_set_drvdata(pdev, vtg);
+
+	DRM_INFO("%s %s\n", __func__, dev_name(vtg->dev));
+
+	return 0;
+}
+
+static int vtg_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id vtg_of_match[] = {
+	{ .compatible = "st,vtg", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, vtg_of_match);
+
+struct platform_driver sti_vtg_driver = {
+	.driver = {
+		.name = "sti-vtg",
+		.owner = THIS_MODULE,
+		.of_match_table = vtg_of_match,
+	},
+	.probe	= vtg_probe,
+	.remove = vtg_remove,
+};
+
+module_platform_driver(sti_vtg_driver);
diff --git a/drivers/gpu/drm/sti/sti_vtg.h b/drivers/gpu/drm/sti/sti_vtg.h
new file mode 100644
index 0000000..e84d23f
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vtg.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_VTG_H_
+#define _STI_VTG_H_
+
+#define VTG_TOP_FIELD_EVENT     1
+#define VTG_BOTTOM_FIELD_EVENT  2
+
+struct sti_vtg;
+struct drm_display_mode;
+struct notifier_block;
+
+struct sti_vtg *of_vtg_find(struct device_node *np);
+void sti_vtg_set_config(struct sti_vtg *vtg,
+		const struct drm_display_mode *mode);
+int sti_vtg_register_client(struct sti_vtg *vtg,
+		struct notifier_block *nb, int crtc_id);
+int sti_vtg_unregister_client(struct sti_vtg *vtg,
+		struct notifier_block *nb);
+
+u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y);
+u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x);
+
+#endif
-- 
1.9.1


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

* [PATCH v4 03/11] drm: sti: add VTAC drivers
  2014-05-29  6:36 [PATCH v4 00/11] Add DRM for stih4xx platforms Benjamin Gaignard
  2014-05-29  6:36 ` [PATCH v4 01/11] drm: sti: add bindings for DRM driver Benjamin Gaignard
  2014-05-29  6:36 ` [PATCH v4 02/11] drm: sti: add VTG driver Benjamin Gaignard
@ 2014-05-29  6:36 ` Benjamin Gaignard
  2014-05-29  6:37 ` [PATCH v4 04/11] drm: sti: add HDMI driver Benjamin Gaignard
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Benjamin Gaignard @ 2014-05-29  6:36 UTC (permalink / raw)
  To: dri-devel, linaro-mm-sig, linux-kernel, airlied
  Cc: lee.jones, Benjamin Gaignard

Video Traffic Advance Communication Rx and Tx drivers are designed
for inter-die communication.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
---
 drivers/gpu/drm/sti/Makefile   |   3 +-
 drivers/gpu/drm/sti/sti_vtac.c | 211 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 213 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/sti/sti_vtac.c

diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
index 3658a13e..52e2e80 100644
--- a/drivers/gpu/drm/sti/Makefile
+++ b/drivers/gpu/drm/sti/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_DRM_STI) += \
-	sti_vtg.o
+	sti_vtg.o \
+	sti_vtac.o
diff --git a/drivers/gpu/drm/sti/sti_vtac.c b/drivers/gpu/drm/sti/sti_vtac.c
new file mode 100644
index 0000000..5226a04
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vtac.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <drm/drmP.h>
+
+/* registers offset */
+#define VTAC_CONFIG                     0x00
+#define VTAC_RX_FIFO_CONFIG             0x04
+#define VTAC_FIFO_CONFIG_VAL            0x04
+
+#define VTAC_SYS_CFG8521                0x824
+#define VTAC_SYS_CFG8522                0x828
+
+/* Number of phyts per pixel */
+#define VTAC_2_5_PPP                    0x0005
+#define VTAC_3_PPP                      0x0006
+#define VTAC_4_PPP                      0x0008
+#define VTAC_5_PPP                      0x000A
+#define VTAC_6_PPP                      0x000C
+#define VTAC_13_PPP                     0x001A
+#define VTAC_14_PPP                     0x001C
+#define VTAC_15_PPP                     0x001E
+#define VTAC_16_PPP                     0x0020
+#define VTAC_17_PPP                     0x0022
+#define VTAC_18_PPP                     0x0024
+
+/* enable bits */
+#define VTAC_ENABLE                     0x3003
+
+#define VTAC_TX_PHY_ENABLE_CLK_PHY      BIT(0)
+#define VTAC_TX_PHY_ENABLE_CLK_DLL      BIT(1)
+#define VTAC_TX_PHY_PLL_NOT_OSC_MODE    BIT(3)
+#define VTAC_TX_PHY_RST_N_DLL_SWITCH    BIT(4)
+#define VTAC_TX_PHY_PROG_N3             BIT(9)
+
+
+/**
+ * VTAC mode structure
+ *
+ * @vid_in_width: Video Data Resolution
+ * @phyts_width: Width of phyt buses(phyt low and phyt high).
+ * @phyts_per_pixel: Number of phyts sent per pixel
+ */
+struct sti_vtac_mode {
+	u32 vid_in_width;
+	u32 phyts_width;
+	u32 phyts_per_pixel;
+};
+
+static const struct sti_vtac_mode vtac_mode_main = {0x2, 0x2, VTAC_5_PPP};
+static const struct sti_vtac_mode vtac_mode_aux = {0x1, 0x0, VTAC_17_PPP};
+
+/**
+ * VTAC structure
+ *
+ * @dev: pointer to device structure
+ * @regs: ioremapped registers for RX and TX devices
+ * @phy_regs: phy registers for TX device
+ * @clk: clock
+ * @mode: main or auxillary configuration mode
+ */
+struct sti_vtac {
+	struct device *dev;
+	void __iomem *regs;
+	void __iomem *phy_regs;
+	struct clk *clk;
+	const struct sti_vtac_mode *mode;
+};
+
+static void sti_vtac_rx_set_config(struct sti_vtac *vtac)
+{
+	u32 config;
+
+	/* Enable VTAC clock */
+	if (clk_prepare_enable(vtac->clk))
+		DRM_ERROR("Failed to prepare/enable vtac_rx clock.\n");
+
+	writel(VTAC_FIFO_CONFIG_VAL, vtac->regs + VTAC_RX_FIFO_CONFIG);
+
+	config = VTAC_ENABLE;
+	config |= vtac->mode->vid_in_width << 4;
+	config |= vtac->mode->phyts_width << 16;
+	config |= vtac->mode->phyts_per_pixel << 23;
+	writel(config, vtac->regs + VTAC_CONFIG);
+}
+
+static void sti_vtac_tx_set_config(struct sti_vtac *vtac)
+{
+	u32 phy_config;
+	u32 config;
+
+	/* Enable VTAC clock */
+	if (clk_prepare_enable(vtac->clk))
+		DRM_ERROR("Failed to prepare/enable vtac_tx clock.\n");
+
+	/* Configure vtac phy */
+	phy_config = 0x00000000;
+	writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8522);
+	phy_config = VTAC_TX_PHY_ENABLE_CLK_PHY;
+	writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
+	phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521);
+	phy_config |= VTAC_TX_PHY_PROG_N3;
+	writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
+	phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521);
+	phy_config |= VTAC_TX_PHY_ENABLE_CLK_DLL;
+	writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
+	phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521);
+	phy_config |= VTAC_TX_PHY_RST_N_DLL_SWITCH;
+	writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
+	phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521);
+	phy_config |= VTAC_TX_PHY_PLL_NOT_OSC_MODE;
+	writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
+
+	/* Configure vtac tx */
+	config = VTAC_ENABLE;
+	config |= vtac->mode->vid_in_width << 4;
+	config |= vtac->mode->phyts_width << 16;
+	config |= vtac->mode->phyts_per_pixel << 23;
+	writel(config, vtac->regs + VTAC_CONFIG);
+}
+
+static const struct of_device_id vtac_of_match[] = {
+	{
+		.compatible = "st,vtac-main",
+		.data = &vtac_mode_main,
+	}, {
+		.compatible = "st,vtac-aux",
+		.data = &vtac_mode_aux,
+	}, {
+		/* end node */
+	}
+};
+MODULE_DEVICE_TABLE(of, vtac_of_match);
+
+static int sti_vtac_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	const struct of_device_id *id;
+	struct sti_vtac *vtac;
+	struct resource *res;
+
+	vtac = devm_kzalloc(dev, sizeof(*vtac), GFP_KERNEL);
+	if (!vtac)
+		return -ENOMEM;
+
+	vtac->dev = dev;
+
+	id = of_match_node(vtac_of_match, np);
+	if (!id)
+		return -ENOMEM;
+
+	vtac->mode = id->data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		DRM_ERROR("Invalid resource\n");
+		return -ENOMEM;
+	}
+	vtac->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(vtac->regs))
+		return PTR_ERR(vtac->regs);
+
+
+	vtac->clk = devm_clk_get(dev, "vtac");
+	if (IS_ERR(vtac->clk)) {
+		DRM_ERROR("Cannot get vtac clock\n");
+		return PTR_ERR(vtac->clk);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (res) {
+		vtac->phy_regs = devm_ioremap_nocache(dev, res->start,
+						 resource_size(res));
+		sti_vtac_tx_set_config(vtac);
+	} else {
+
+		sti_vtac_rx_set_config(vtac);
+	}
+
+	platform_set_drvdata(pdev, vtac);
+	DRM_INFO("%s %s\n", __func__, dev_name(vtac->dev));
+
+	return 0;
+}
+
+static int sti_vtac_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+struct platform_driver sti_vtac_driver = {
+	.driver = {
+		.name = "sti-vtac",
+		.owner = THIS_MODULE,
+		.of_match_table = vtac_of_match,
+	},
+	.probe = sti_vtac_probe,
+	.remove = sti_vtac_remove,
+};
+
+module_platform_driver(sti_vtac_driver);
-- 
1.9.1


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

* [PATCH v4 04/11] drm: sti: add HDMI driver
  2014-05-29  6:36 [PATCH v4 00/11] Add DRM for stih4xx platforms Benjamin Gaignard
                   ` (2 preceding siblings ...)
  2014-05-29  6:36 ` [PATCH v4 03/11] drm: sti: add VTAC drivers Benjamin Gaignard
@ 2014-05-29  6:37 ` Benjamin Gaignard
  2014-05-29  6:37 ` [PATCH v4 05/11] drm: sti: add HDA driver Benjamin Gaignard
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Benjamin Gaignard @ 2014-05-29  6:37 UTC (permalink / raw)
  To: dri-devel, linaro-mm-sig, linux-kernel, airlied
  Cc: lee.jones, Benjamin Gaignard

Add driver for HDMI output.
HDMI PHY registers are mixed into HDMI device registers
and their is only one IRQ for all this hardware block.
That is why PHYs aren't using phy framework but only a
thin hdmi_phy_ops structure with start and stop functions.

This patch introduce sti_connector structure which will be
use later in the drm driver.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
---
 drivers/gpu/drm/sti/Makefile               |   5 +-
 drivers/gpu/drm/sti/sti_drm_connector.h    |  51 ++
 drivers/gpu/drm/sti/sti_drm_encoder.h      |  14 +
 drivers/gpu/drm/sti/sti_hdmi.c             | 744 +++++++++++++++++++++++++++++
 drivers/gpu/drm/sti/sti_hdmi.h             |  88 ++++
 drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c | 336 +++++++++++++
 drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h |  14 +
 drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c | 211 ++++++++
 drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h |  14 +
 9 files changed, 1476 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/sti/sti_drm_connector.h
 create mode 100644 drivers/gpu/drm/sti/sti_drm_encoder.h
 create mode 100644 drivers/gpu/drm/sti/sti_hdmi.c
 create mode 100644 drivers/gpu/drm/sti/sti_hdmi.h
 create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c
 create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h
 create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c
 create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h

diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
index 52e2e80..5521a85 100644
--- a/drivers/gpu/drm/sti/Makefile
+++ b/drivers/gpu/drm/sti/Makefile
@@ -1,3 +1,6 @@
 obj-$(CONFIG_DRM_STI) += \
 	sti_vtg.o \
-	sti_vtac.o
+	sti_vtac.o \
+	sti_hdmi.o \
+	sti_hdmi_tx3g0c55phy.o \
+	sti_hdmi_tx3g4c28phy.o
\ No newline at end of file
diff --git a/drivers/gpu/drm/sti/sti_drm_connector.h b/drivers/gpu/drm/sti/sti_drm_connector.h
new file mode 100644
index 0000000..798bd5c
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_connector.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_DRM_CONNECTOR_H_
+#define _STI_DRM_CONNECTOR_H_
+
+/*
+ * enum listing the supported connector
+ */
+enum sti_connector_type {
+	STI_CONNECTOR_HDMI,
+	STI_CONNECTOR_HDA,
+	STI_CONNECTOR_DVO,
+	STI_CONNECTOR_DENC,
+};
+
+/**
+ * struct sti_connector
+ *
+ * internal definition of connector
+ *
+ * @drm_connector: generic drm_connector struct
+ * @encoder: the linked encoder
+ * @dev: the connector device
+ * @master: the master device (i.e. tvout device)
+ * @type: the connector type
+ */
+struct sti_connector {
+	struct drm_connector drm_connector;
+	struct drm_encoder *encoder;
+	struct device *dev;
+	struct device *master;
+	enum  sti_connector_type type;
+	int  (*start)(struct drm_connector *connector);
+	void (*stop)(struct drm_connector *connector);
+	int  (*get_modes)(struct drm_connector *drm_connector);
+	int  (*check_mode)(struct drm_connector *connector,
+			struct drm_display_mode *mode);
+	int  (*set_mode)(struct drm_connector *connector,
+			struct drm_display_mode *mode, bool main_path);
+	int  (*detect)(struct drm_connector *connector);
+	void (*prepare)(struct drm_connector *connector);
+	bool (*is_enabled)(struct drm_connector *connector);
+};
+
+#define to_sti_connector(x) container_of(x, struct sti_connector, drm_connector)
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_drm_encoder.h b/drivers/gpu/drm/sti/sti_drm_encoder.h
new file mode 100644
index 0000000..bc7a91c
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_encoder.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_DRM_ENCODER_H_
+#define _STI_DRM_ENCODER_H_
+
+#include <drm/drmP.h>
+
+#define ENCODER_MAIN_CRTC_MASK BIT(0)
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
new file mode 100644
index 0000000..c963468
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_hdmi.c
@@ -0,0 +1,744 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/hdmi.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+
+#include "sti_drm_connector.h"
+#include "sti_drm_encoder.h"
+#include "sti_hdmi.h"
+#include "sti_hdmi_tx3g4c28phy.h"
+#include "sti_hdmi_tx3g0c55phy.h"
+#include "sti_vtg.h"
+
+#define HDMI_CFG                        0x0000
+#define HDMI_INT_EN                     0x0004
+#define HDMI_INT_STA                    0x0008
+#define HDMI_INT_CLR                    0x000C
+#define HDMI_STA                        0x0010
+#define HDMI_ACTIVE_VID_XMIN            0x0100
+#define HDMI_ACTIVE_VID_XMAX            0x0104
+#define HDMI_ACTIVE_VID_YMIN            0x0108
+#define HDMI_ACTIVE_VID_YMAX            0x010C
+#define HDMI_DFLT_CHL0_DAT              0x0110
+#define HDMI_DFLT_CHL1_DAT              0x0114
+#define HDMI_DFLT_CHL2_DAT              0x0118
+#define HDMI_SW_DI_1_HEAD_WORD          0x0210
+#define HDMI_SW_DI_1_PKT_WORD0          0x0214
+#define HDMI_SW_DI_1_PKT_WORD1          0x0218
+#define HDMI_SW_DI_1_PKT_WORD2          0x021C
+#define HDMI_SW_DI_1_PKT_WORD3          0x0220
+#define HDMI_SW_DI_1_PKT_WORD4          0x0224
+#define HDMI_SW_DI_1_PKT_WORD5          0x0228
+#define HDMI_SW_DI_1_PKT_WORD6          0x022C
+#define HDMI_SW_DI_CFG                  0x0230
+
+#define HDMI_IFRAME_SLOT_AVI            1
+
+#define  XCAT(prefix, x, suffix)        prefix ## x ## suffix
+#define  HDMI_SW_DI_N_HEAD_WORD(x)      XCAT(HDMI_SW_DI_, x, _HEAD_WORD)
+#define  HDMI_SW_DI_N_PKT_WORD0(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD0)
+#define  HDMI_SW_DI_N_PKT_WORD1(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD1)
+#define  HDMI_SW_DI_N_PKT_WORD2(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD2)
+#define  HDMI_SW_DI_N_PKT_WORD3(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD3)
+#define  HDMI_SW_DI_N_PKT_WORD4(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD4)
+#define  HDMI_SW_DI_N_PKT_WORD5(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD5)
+#define  HDMI_SW_DI_N_PKT_WORD6(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD6)
+
+#define HDMI_IFRAME_DISABLED            0x0
+#define HDMI_IFRAME_SINGLE_SHOT         0x1
+#define HDMI_IFRAME_FIELD               0x2
+#define HDMI_IFRAME_FRAME               0x3
+#define HDMI_IFRAME_MASK                0x3
+#define HDMI_IFRAME_CFG_DI_N(x, n)       ((x) << ((n-1)*4)) /* n from 1 to 6 */
+
+#define HDMI_CFG_DEVICE_EN              BIT(0)
+#define HDMI_CFG_HDMI_NOT_DVI           BIT(1)
+#define HDMI_CFG_HDCP_EN                BIT(2)
+#define HDMI_CFG_ESS_NOT_OESS           BIT(3)
+#define HDMI_CFG_H_SYNC_POL_NEG         BIT(4)
+#define HDMI_CFG_SINK_TERM_DET_EN       BIT(5)
+#define HDMI_CFG_V_SYNC_POL_NEG         BIT(6)
+#define HDMI_CFG_422_EN                 BIT(8)
+#define HDMI_CFG_FIFO_OVERRUN_CLR       BIT(12)
+#define HDMI_CFG_FIFO_UNDERRUN_CLR      BIT(13)
+#define HDMI_CFG_SW_RST_EN              BIT(31)
+
+#define HDMI_INT_GLOBAL                 BIT(0)
+#define HDMI_INT_SW_RST                 BIT(1)
+#define HDMI_INT_PIX_CAP                BIT(3)
+#define HDMI_INT_HOT_PLUG               BIT(4)
+#define HDMI_INT_DLL_LCK                BIT(5)
+#define HDMI_INT_NEW_FRAME              BIT(6)
+#define HDMI_INT_GENCTRL_PKT            BIT(7)
+#define HDMI_INT_SINK_TERM_PRESENT      BIT(11)
+
+#define HDMI_DEFAULT_INT (HDMI_INT_SINK_TERM_PRESENT \
+			| HDMI_INT_DLL_LCK \
+			| HDMI_INT_HOT_PLUG \
+			| HDMI_INT_GLOBAL)
+
+#define HDMI_WORKING_INT (HDMI_INT_SINK_TERM_PRESENT \
+			| HDMI_INT_GENCTRL_PKT \
+			| HDMI_INT_NEW_FRAME \
+			| HDMI_INT_DLL_LCK \
+			| HDMI_INT_HOT_PLUG \
+			| HDMI_INT_PIX_CAP \
+			| HDMI_INT_SW_RST \
+			| HDMI_INT_GLOBAL)
+
+#define HDMI_STA_SW_RST                 BIT(1)
+
+u32 hdmi_read(struct sti_hdmi *hdmi, int offset)
+{
+	return readl(hdmi->regs + offset);
+}
+
+void hdmi_write(struct sti_hdmi *hdmi, u32 val, int offset)
+{
+	writel(val, hdmi->regs + offset);
+}
+
+/**
+ * HDMI interrupt handler threaded
+ *
+ * @irq: irq number
+ * @arg: connector structure
+ */
+static irqreturn_t hdmi_irq_thread(int irq, void *arg)
+{
+	struct sti_hdmi *hdmi = arg;
+
+	/* Hot plug/unplug IRQ */
+	if (hdmi->irq_status & HDMI_INT_HOT_PLUG) {
+		/* read gpio to get the status */
+		hdmi->hpd = gpio_get_value(hdmi->hpd_gpio);
+		if (hdmi->drm_dev)
+			drm_helper_hpd_irq_event(hdmi->drm_dev);
+	}
+
+	/* Sw reset and PLL lock are exclusive so we can use the same
+	 * event to signal them
+	 */
+	if (hdmi->irq_status & (HDMI_INT_SW_RST | HDMI_INT_DLL_LCK)) {
+		hdmi->event_received = true;
+		wake_up_interruptible(&hdmi->wait_event);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * HDMI interrupt handler
+ *
+ * @irq: irq number
+ * @arg: connector structure
+ */
+static irqreturn_t hdmi_irq(int irq, void *arg)
+{
+	struct sti_hdmi *hdmi = arg;
+
+	/* read interrupt status */
+	hdmi->irq_status = hdmi_read(hdmi, HDMI_INT_STA);
+
+	/* clear interrupt status */
+	hdmi_write(hdmi, hdmi->irq_status, HDMI_INT_CLR);
+
+	/* force sync bus write */
+	hdmi_read(hdmi, HDMI_INT_STA);
+
+	return IRQ_WAKE_THREAD;
+}
+
+/**
+ * Set hdmi active area depending on the drm display mode selected
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ */
+static void hdmi_active_area(struct sti_hdmi *hdmi)
+{
+	u32 xmin, xmax;
+	u32 ymin, ymax;
+
+	xmin = sti_vtg_get_pixel_number(hdmi->mode, 0);
+	xmax = sti_vtg_get_pixel_number(hdmi->mode, hdmi->mode.hdisplay - 1);
+	ymin = sti_vtg_get_line_number(hdmi->mode, 0);
+	ymax = sti_vtg_get_line_number(hdmi->mode, hdmi->mode.vdisplay - 1);
+
+	hdmi_write(hdmi, xmin, HDMI_ACTIVE_VID_XMIN);
+	hdmi_write(hdmi, xmax, HDMI_ACTIVE_VID_XMAX);
+	hdmi_write(hdmi, ymin, HDMI_ACTIVE_VID_YMIN);
+	hdmi_write(hdmi, ymax, HDMI_ACTIVE_VID_YMAX);
+}
+
+/**
+ * Overall hdmi configuration
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ */
+static void hdmi_config(struct sti_hdmi *hdmi)
+{
+	u32 conf;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	/* Clear overrun and underrun fifo */
+	conf = HDMI_CFG_FIFO_OVERRUN_CLR | HDMI_CFG_FIFO_UNDERRUN_CLR;
+
+	/* Enable HDMI mode not DVI */
+	conf |= HDMI_CFG_HDMI_NOT_DVI | HDMI_CFG_ESS_NOT_OESS;
+
+	/* Enable sink term detection */
+	conf |= HDMI_CFG_SINK_TERM_DET_EN;
+
+	/* Set Hsync polarity */
+	if (hdmi->mode.flags & DRM_MODE_FLAG_NHSYNC) {
+		DRM_DEBUG_DRIVER("H Sync Negative\n");
+		conf |= HDMI_CFG_H_SYNC_POL_NEG;
+	}
+
+	/* Set Vsync polarity */
+	if (hdmi->mode.flags & DRM_MODE_FLAG_NVSYNC) {
+		DRM_DEBUG_DRIVER("V Sync Negative\n");
+		conf |= HDMI_CFG_V_SYNC_POL_NEG;
+	}
+
+	/* Enable HDMI */
+	conf |= HDMI_CFG_DEVICE_EN;
+
+	hdmi_write(hdmi, conf, HDMI_CFG);
+}
+
+/**
+ * Prepare and configure the AVI infoframe
+ *
+ * AVI infoframe are transmitted at least once per two video field and
+ * contains information about HDMI transmission mode such as color space,
+ * colorimetry, ...
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ *
+ * Return negative value if error occurs
+ */
+static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi)
+{
+	struct drm_display_mode *mode = &hdmi->mode;
+	struct hdmi_avi_infoframe infoframe;
+	u8 buffer[HDMI_INFOFRAME_SIZE(AVI)];
+	u8 *frame = buffer + HDMI_INFOFRAME_HEADER_SIZE;
+	u32 val;
+	int ret;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	ret = drm_hdmi_avi_infoframe_from_display_mode(&infoframe, mode);
+	if (ret < 0) {
+		DRM_ERROR("failed to setup AVI infoframe: %d\n", ret);
+		return ret;
+	}
+
+	/* fixed infoframe configuration not linked to the mode */
+	infoframe.colorspace = HDMI_COLORSPACE_RGB;
+	infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
+	infoframe.colorimetry = HDMI_COLORIMETRY_NONE;
+
+	ret = hdmi_avi_infoframe_pack(&infoframe, buffer, sizeof(buffer));
+	if (ret < 0) {
+		DRM_ERROR("failed to pack AVI infoframe: %d\n", ret);
+		return ret;
+	}
+
+	/* Disable transmission slot for AVI infoframe */
+	val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
+	val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, HDMI_IFRAME_SLOT_AVI);
+	hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
+
+	/* Infoframe header */
+	val = buffer[0x0];
+	val |= buffer[0x1] << 8;
+	val |= buffer[0x2] << 16;
+	hdmi_write(hdmi, val, HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI));
+
+	/* Infoframe packet bytes */
+	val = frame[0x0];
+	val |= frame[0x1] << 8;
+	val |= frame[0x2] << 16;
+	val |= frame[0x3] << 24;
+	hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI));
+
+	val = frame[0x4];
+	val |= frame[0x5] << 8;
+	val |= frame[0x6] << 16;
+	val |= frame[0x7] << 24;
+	hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD1(HDMI_IFRAME_SLOT_AVI));
+
+	val = frame[0x8];
+	val |= frame[0x9] << 8;
+	val |= frame[0xA] << 16;
+	val |= frame[0xB] << 24;
+	hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD2(HDMI_IFRAME_SLOT_AVI));
+
+	val = frame[0xC];
+	val |= frame[0xD] << 8;
+	hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD3(HDMI_IFRAME_SLOT_AVI));
+
+	/* Enable transmission slot for AVI infoframe
+	 * According to the hdmi specification, AVI infoframe should be
+	 * transmitted at least once per two video fields
+	 */
+	val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
+	val |= HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_FIELD, HDMI_IFRAME_SLOT_AVI);
+	hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
+
+	return 0;
+}
+
+/**
+ * Software reset of the hdmi subsystem
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ *
+ */
+#define HDMI_TIMEOUT_SWRESET  100   /*milliseconds */
+static void hdmi_swreset(struct sti_hdmi *hdmi)
+{
+	u32 val;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	/* Enable hdmi_audio clock only during hdmi reset */
+	if (clk_prepare_enable(hdmi->clk_audio))
+		DRM_INFO("Failed to prepare/enable hdmi_audio clk\n");
+
+	/* Sw reset */
+	hdmi->event_received = false;
+
+	val = hdmi_read(hdmi, HDMI_CFG);
+	val |= HDMI_CFG_SW_RST_EN;
+	hdmi_write(hdmi, val, HDMI_CFG);
+
+	/* Wait reset completed */
+	wait_event_interruptible_timeout(hdmi->wait_event,
+					 hdmi->event_received == true,
+					 msecs_to_jiffies
+					 (HDMI_TIMEOUT_SWRESET));
+
+	/*
+	 * HDMI_STA_SW_RST bit is set to '1' when SW_RST bit in HDMI_CFG is
+	 * set to '1' and clk_audio is running.
+	 */
+	if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_SW_RST) == 0)
+		DRM_INFO("Warning: HDMI sw reset timeout occurs\n");
+
+	val = hdmi_read(hdmi, HDMI_CFG);
+	val &= ~HDMI_CFG_SW_RST_EN;
+	hdmi_write(hdmi, val, HDMI_CFG);
+
+	/* Disable hdmi_audio clock. Not used anymore for drm purpose */
+	clk_disable_unprepare(hdmi->clk_audio);
+}
+
+/**
+ * Get modes from edid
+ *
+ * @drm_connector: pointer on the drm connector
+ */
+static int sti_hdmi_get_modes(struct drm_connector *drm_connector)
+{
+	struct i2c_adapter *i2c_adap;
+	struct edid *edid;
+	int count;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	i2c_adap = i2c_get_adapter(1);
+	if (!i2c_adap)
+		goto fail;
+
+	edid = drm_get_edid(drm_connector, i2c_adap);
+	if (!edid)
+		goto fail;
+
+	count = drm_add_edid_modes(drm_connector, edid);
+	drm_mode_connector_update_edid_property(drm_connector, edid);
+
+	kfree(edid);
+	return count;
+
+fail:
+	DRM_ERROR("Can not read HDMI EDID\n");
+	return 0;
+}
+
+static int sti_hdmi_start(struct drm_connector *connector)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+	struct sti_hdmi *hdmi = dev_get_drvdata(sti_connector->dev);
+	int ret;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	/* Prepare/enable clocks */
+	if (clk_prepare_enable(hdmi->clk_pix))
+		DRM_ERROR("Failed to prepare/enable hdmi_pix clk\n");
+	if (clk_prepare_enable(hdmi->clk_tmds))
+		DRM_ERROR("Failed to prepare/enable hdmi_tmds clk\n");
+	if (clk_prepare_enable(hdmi->clk_phy))
+		DRM_ERROR("Failed to prepare/enable hdmi_rejec_pll clk\n");
+
+	hdmi->enabled = true;
+
+	/* Program hdmi serializer and start phy */
+	if (!hdmi->phy_ops->start(hdmi)) {
+		DRM_ERROR("Unable to start hdmi phy\n");
+		return -EINVAL;
+	}
+
+	/* Program hdmi active area */
+	hdmi_active_area(hdmi);
+
+	/* Enable working interrupts */
+	hdmi_write(hdmi, HDMI_WORKING_INT, HDMI_INT_EN);
+
+	/* Program hdmi config */
+	hdmi_config(hdmi);
+
+	/* Program AVI infoframe */
+	ret = hdmi_avi_infoframe_config(hdmi);
+	if (ret)
+		DRM_ERROR("Unable to configure AVI infoframe\n");
+
+	/* Sw reset */
+	hdmi_swreset(hdmi);
+
+	return ret;
+}
+
+static void sti_hdmi_stop(struct drm_connector *connector)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+	struct sti_hdmi *hdmi = dev_get_drvdata(sti_connector->dev);
+
+	u32 val = hdmi_read(hdmi, HDMI_CFG);
+
+	if (!hdmi->enabled)
+		return;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	/* Disable HDMI */
+	val &= ~HDMI_CFG_DEVICE_EN;
+	hdmi_write(hdmi, val, HDMI_CFG);
+
+	/* Stop the phy */
+	hdmi->phy_ops->stop(hdmi);
+
+	/* Disable/unprepare hdmi clock */
+	clk_disable_unprepare(hdmi->clk_phy);
+	clk_disable_unprepare(hdmi->clk_tmds);
+	clk_disable_unprepare(hdmi->clk_pix);
+
+	hdmi->enabled = false;
+}
+
+#define CLK_TOLERANCE_HZ 50
+
+static int sti_hdmi_check_mode(struct drm_connector *connector,
+			       struct drm_display_mode *mode)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+	struct sti_hdmi *hdmi = dev_get_drvdata(sti_connector->dev);
+
+	int target = mode->clock * 1000;
+	int target_min = target - CLK_TOLERANCE_HZ;
+	int target_max = target + CLK_TOLERANCE_HZ;
+	int result;
+
+	result = clk_round_rate(hdmi->clk_pix, target);
+
+	DRM_DEBUG_DRIVER("target rate = %d => available rate = %d\n",
+			 target, result);
+
+	if ((result < target_min) || (result > target_max)) {
+		DRM_DEBUG_DRIVER("hdmi pixclk=%d not supported\n", target);
+		return MODE_BAD;
+	}
+
+	return MODE_OK;
+}
+
+static int sti_hdmi_set_mode(struct drm_connector *connector,
+			     struct drm_display_mode *mode, bool main_path)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+	struct sti_hdmi *hdmi = dev_get_drvdata(sti_connector->dev);
+
+	int ret;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	/* Copy the drm display mode in the connector local structure */
+	memcpy(&hdmi->mode, mode, sizeof(struct drm_display_mode));
+
+	/* Update clock framerate according to the selected mode */
+	ret = clk_set_rate(hdmi->clk_pix, mode->clock * 1000);
+	if (ret < 0) {
+		DRM_ERROR("Cannot set rate (%dHz) for hdmi_pix clk\n",
+			  mode->clock * 1000);
+		return ret;
+	}
+	ret = clk_set_rate(hdmi->clk_phy, mode->clock * 1000);
+	if (ret < 0) {
+		DRM_ERROR("Cannot set rate (%dHz) for hdmi_rejection_pll clk\n",
+			  mode->clock * 1000);
+		return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * Detect if hdmi is connected
+ *
+ * Return the connected/disconnected status
+ */
+static int sti_hdmi_detect(struct drm_connector *connector)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+	struct sti_hdmi *hdmi = dev_get_drvdata(sti_connector->dev);
+
+	DRM_DEBUG_DRIVER("\n");
+
+	if (hdmi->hpd) {
+		DRM_DEBUG_DRIVER("hdmi cable connected\n");
+		return connector_status_connected;
+	}
+
+	DRM_DEBUG_DRIVER("hdmi cable disconnected\n");
+	return connector_status_disconnected;
+}
+
+/**
+ * Check if hdmi is enabled
+ *
+ * Return true if hdmi is enabled
+ */
+static bool sti_hdmi_is_enabled(struct drm_connector *connector)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+	struct sti_hdmi *hdmi = dev_get_drvdata(sti_connector->dev);
+
+	return hdmi->enabled;
+}
+
+/**
+ * Prepare/configure hdmi
+ */
+static void sti_hdmi_prepare(struct drm_connector *connector)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+	struct sti_hdmi *hdmi = dev_get_drvdata(sti_connector->dev);
+
+	DRM_DEBUG_DRIVER("\n");
+
+	/* HDMI initialisation */
+	hdmi_write(hdmi, 0x00000000, HDMI_CFG);
+	hdmi_write(hdmi, 0xffffffff, HDMI_INT_CLR);
+
+	/* Ensure the PHY is completely powered down */
+	hdmi->phy_ops->stop(hdmi);
+
+	/* Set the default channel data to be a dark red */
+	hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL0_DAT);
+	hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL1_DAT);
+	hdmi_write(hdmi, 0x0060, HDMI_DFLT_CHL2_DAT);
+}
+
+static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+	struct sti_hdmi *hdmi = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	struct drm_encoder *encoder;
+	struct sti_connector *connector;
+
+	/* Set the drm device handle */
+	hdmi->drm_dev = drm_dev;
+
+	encoder = devm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL);
+	if (!encoder)
+		return -ENOMEM;
+
+	encoder->possible_crtcs = ENCODER_MAIN_CRTC_MASK;
+	encoder->possible_clones = 1 << STI_CONNECTOR_HDA;
+
+	connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
+	if (!connector)
+		return -ENOMEM;
+
+	connector->dev = dev;
+	connector->master = master;
+	connector->encoder = encoder;
+	connector->start = sti_hdmi_start;
+	connector->stop = sti_hdmi_stop;
+	connector->get_modes = sti_hdmi_get_modes;
+	connector->check_mode = sti_hdmi_check_mode;
+	connector->set_mode = sti_hdmi_set_mode;
+	connector->detect = sti_hdmi_detect;
+	connector->is_enabled = sti_hdmi_is_enabled;
+	connector->prepare = sti_hdmi_prepare;
+	connector->type = STI_CONNECTOR_HDMI;
+
+	/* Enable default interrupts */
+	hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN);
+
+	return 0;
+}
+
+static void sti_hdmi_unbind(struct device *dev,
+		struct device *master, void *data)
+{
+	/* do nothing */
+}
+
+static const struct component_ops sti_hdmi_ops = {
+	.bind = sti_hdmi_bind,
+	.unbind = sti_hdmi_unbind,
+};
+
+static struct of_device_id hdmi_of_match[] = {
+	{
+		.compatible = "st,stih416-hdmi",
+		.data = &tx3g0c55phy_ops,
+	}, {
+		.compatible = "st,stih407-hdmi",
+		.data = &tx3g4c28phy_ops,
+	}, {
+		/* end node */
+	}
+};
+MODULE_DEVICE_TABLE(of, hdmi_of_match);
+
+static int sti_hdmi_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct sti_hdmi *hdmi;
+	struct device_node *np = dev->of_node;
+	struct resource *res;
+	int ret;
+
+	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi->dev = pdev->dev;
+
+	/* Get resources */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi-reg");
+	if (!res) {
+		DRM_ERROR("Invalid hdmi resource\n");
+		return -ENOMEM;
+	}
+	hdmi->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
+	if (IS_ERR(hdmi->regs))
+		return PTR_ERR(hdmi->regs);
+
+	if (of_device_is_compatible(np, "st,stih416-hdmi")) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						   "syscfg");
+		if (!res) {
+			DRM_ERROR("Invalid syscfg resource\n");
+			return -ENOMEM;
+		}
+		hdmi->syscfg = devm_ioremap_nocache(dev, res->start,
+						    resource_size(res));
+		if (IS_ERR(hdmi->syscfg))
+			return PTR_ERR(hdmi->syscfg);
+
+	}
+
+	hdmi->phy_ops = (struct hdmi_phy_ops *)
+		of_match_node(hdmi_of_match, np)->data;
+
+	/* Get clock resources */
+	hdmi->clk_pix = devm_clk_get(dev, "pix");
+	if (IS_ERR(hdmi->clk_pix)) {
+		DRM_ERROR("Cannot get hdmi_pix clock\n");
+		return PTR_ERR(hdmi->clk_pix);
+	}
+
+	hdmi->clk_tmds = devm_clk_get(dev, "tmds");
+	if (IS_ERR(hdmi->clk_tmds)) {
+		DRM_ERROR("Cannot get hdmi_tmds clock\n");
+		return PTR_ERR(hdmi->clk_tmds);
+	}
+
+	hdmi->clk_phy = devm_clk_get(dev, "phy");
+	if (IS_ERR(hdmi->clk_phy)) {
+		DRM_ERROR("Cannot get hdmi_phy clock\n");
+		return PTR_ERR(hdmi->clk_phy);
+	}
+
+	hdmi->clk_audio = devm_clk_get(dev, "audio");
+	if (IS_ERR(hdmi->clk_audio)) {
+		DRM_ERROR("Cannot get hdmi_audio clock\n");
+		return PTR_ERR(hdmi->clk_audio);
+	}
+
+	hdmi->hpd_gpio = of_get_named_gpio(np, "hdmi,hpd-gpio", 0);
+	if (hdmi->hpd_gpio < 0) {
+		DRM_ERROR("Failed to get hdmi hpd-gpio\n");
+		return -EIO;
+	}
+
+	hdmi->hpd = gpio_get_value(hdmi->hpd_gpio);
+
+	init_waitqueue_head(&hdmi->wait_event);
+
+	hdmi->irq = platform_get_irq_byname(pdev, "irq");
+
+	ret = devm_request_threaded_irq(dev, hdmi->irq, hdmi_irq,
+			hdmi_irq_thread, IRQF_ONESHOT, dev_name(dev), hdmi);
+	if (ret) {
+		DRM_ERROR("Failed to register HDMI interrupt\n");
+		return ret;
+	}
+
+	hdmi->reset = devm_reset_control_get(dev, "hdmi");
+	/* Take hdmi out of reset */
+	if (!IS_ERR(hdmi->reset))
+		reset_control_deassert(hdmi->reset);
+
+	platform_set_drvdata(pdev, hdmi);
+
+	return component_add(&pdev->dev, &sti_hdmi_ops);
+}
+
+static int sti_hdmi_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &sti_hdmi_ops);
+	return 0;
+}
+
+struct platform_driver sti_hdmi_driver = {
+	.driver = {
+		.name = "sti-hdmi",
+		.owner = THIS_MODULE,
+		.of_match_table = hdmi_of_match,
+	},
+	.probe = sti_hdmi_probe,
+	.remove = sti_hdmi_remove,
+};
+
+module_platform_driver(sti_hdmi_driver);
diff --git a/drivers/gpu/drm/sti/sti_hdmi.h b/drivers/gpu/drm/sti/sti_hdmi.h
new file mode 100644
index 0000000..61bec65
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_hdmi.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_HDMI_H_
+#define _STI_HDMI_H_
+
+#include <linux/platform_device.h>
+
+#include <drm/drmP.h>
+
+#define HDMI_STA           0x0010
+#define HDMI_STA_DLL_LCK   BIT(5)
+
+struct sti_hdmi;
+
+struct hdmi_phy_ops {
+	bool (*start)(struct sti_hdmi *hdmi);
+	void (*stop)(struct sti_hdmi *hdmi);
+};
+
+/**
+ * STI hdmi structure
+ *
+ * @dev: driver device
+ * @drm_dev: pointer to drm device
+ * @mode: current display mode selected
+ * @regs: hdmi register
+ * @syscfg: syscfg register for pll rejection configuration
+ * @clk_pix: hdmi pixel clock
+ * @clk_tmds: hdmi tmds clock
+ * @clk_phy: hdmi phy clock
+ * @clk_audio: hdmi audio clock
+ * @irq: hdmi interrupt number
+ * @irq_status: interrupt status register
+ * @phy_ops: phy start/stop operations
+ * @enabled: true if hdmi is enabled else false
+ * @hpd_gpio: hdmi hot plug detect gpio number
+ * @hpd: hot plug detect status
+ * @wait_event: wait event
+ * @event_received: wait event status
+ * @reset: reset control of the hdmi phy
+ */
+struct sti_hdmi {
+	struct device dev;
+	struct drm_device *drm_dev;
+	struct drm_display_mode mode;
+	void __iomem *regs;
+	void __iomem *syscfg;
+	struct clk *clk_pix;
+	struct clk *clk_tmds;
+	struct clk *clk_phy;
+	struct clk *clk_audio;
+	int irq;
+	u32 irq_status;
+	struct hdmi_phy_ops *phy_ops;
+	bool enabled;
+	int hpd_gpio;
+	bool hpd;
+	wait_queue_head_t wait_event;
+	bool event_received;
+	struct reset_control *reset;
+};
+
+u32 hdmi_read(struct sti_hdmi *hdmi, int offset);
+void hdmi_write(struct sti_hdmi *hdmi, u32 val, int offset);
+
+/**
+ * hdmi phy config structure
+ *
+ * A pointer to an array of these structures is passed to a TMDS (HDMI) output
+ * via the control interface to provide board and SoC specific
+ * configurations of the HDMI PHY. Each entry in the array specifies a hardware
+ * specific configuration for a given TMDS clock frequency range.
+ *
+ * @min_tmds_freq: Lower bound of TMDS clock frequency this entry applies to
+ * @max_tmds_freq: Upper bound of TMDS clock frequency this entry applies to
+ * @config: SoC specific register configuration
+ */
+struct hdmi_phy_config {
+	u32 min_tmds_freq;
+	u32 max_tmds_freq;
+	u32 config[4];
+};
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c
new file mode 100644
index 0000000..49ae8e4
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include "sti_hdmi_tx3g0c55phy.h"
+
+#define HDMI_SRZ_PLL_CFG                0x0504
+#define HDMI_SRZ_TAP_1                  0x0508
+#define HDMI_SRZ_TAP_2                  0x050C
+#define HDMI_SRZ_TAP_3                  0x0510
+#define HDMI_SRZ_CTRL                   0x0514
+
+#define HDMI_SRZ_PLL_CFG_POWER_DOWN     BIT(0)
+#define HDMI_SRZ_PLL_CFG_VCOR_SHIFT     1
+#define HDMI_SRZ_PLL_CFG_VCOR_425MHZ    0
+#define HDMI_SRZ_PLL_CFG_VCOR_850MHZ    1
+#define HDMI_SRZ_PLL_CFG_VCOR_1700MHZ   2
+#define HDMI_SRZ_PLL_CFG_VCOR_3000MHZ   3
+#define HDMI_SRZ_PLL_CFG_VCOR_MASK      3
+#define HDMI_SRZ_PLL_CFG_VCOR(x)        (x << HDMI_SRZ_PLL_CFG_VCOR_SHIFT)
+#define HDMI_SRZ_PLL_CFG_NDIV_SHIFT     8
+#define HDMI_SRZ_PLL_CFG_NDIV_MASK      (0x1F << HDMI_SRZ_PLL_CFG_NDIV_SHIFT)
+#define HDMI_SRZ_PLL_CFG_MODE_SHIFT     16
+#define HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ  0x1
+#define HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ  0x4
+#define HDMI_SRZ_PLL_CFG_MODE_27_MHZ    0x5
+#define HDMI_SRZ_PLL_CFG_MODE_33_75_MHZ 0x6
+#define HDMI_SRZ_PLL_CFG_MODE_40_5_MHZ  0x7
+#define HDMI_SRZ_PLL_CFG_MODE_54_MHZ    0x8
+#define HDMI_SRZ_PLL_CFG_MODE_67_5_MHZ  0x9
+#define HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ 0xA
+#define HDMI_SRZ_PLL_CFG_MODE_81_MHZ    0xB
+#define HDMI_SRZ_PLL_CFG_MODE_82_5_MHZ  0xC
+#define HDMI_SRZ_PLL_CFG_MODE_108_MHZ   0xD
+#define HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ 0xE
+#define HDMI_SRZ_PLL_CFG_MODE_165_MHZ   0xF
+#define HDMI_SRZ_PLL_CFG_MODE_MASK      0xF
+#define HDMI_SRZ_PLL_CFG_MODE(x)        (x << HDMI_SRZ_PLL_CFG_MODE_SHIFT)
+
+#define HDMI_SRZ_CTRL_POWER_DOWN        (1 << 0)
+#define HDMI_SRZ_CTRL_EXTERNAL_DATA_EN  (1 << 1)
+
+/* sysconf registers */
+#define HDMI_REJECTION_PLL_CONFIGURATION 0x0858	/* SYSTEM_CONFIG2534 */
+#define HDMI_REJECTION_PLL_STATUS        0x0948	/* SYSTEM_CONFIG2594 */
+
+#define REJECTION_PLL_HDMI_ENABLE_SHIFT 0
+#define REJECTION_PLL_HDMI_ENABLE_MASK  (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT)
+#define REJECTION_PLL_HDMI_PDIV_SHIFT   24
+#define REJECTION_PLL_HDMI_PDIV_MASK    (0x7 << REJECTION_PLL_HDMI_PDIV_SHIFT)
+#define REJECTION_PLL_HDMI_NDIV_SHIFT   16
+#define REJECTION_PLL_HDMI_NDIV_MASK    (0xFF << REJECTION_PLL_HDMI_NDIV_SHIFT)
+#define REJECTION_PLL_HDMI_MDIV_SHIFT   8
+#define REJECTION_PLL_HDMI_MDIV_MASK    (0xFF << REJECTION_PLL_HDMI_MDIV_SHIFT)
+
+#define REJECTION_PLL_HDMI_REJ_PLL_LOCK BIT(0)
+
+#define HDMI_TIMEOUT_PLL_LOCK  50   /*milliseconds */
+
+/**
+ * pll mode structure
+ *
+ * A pointer to an array of these structures is passed to a TMDS (HDMI) output
+ * via the control interface to provide board and SoC specific
+ * configurations of the HDMI PHY. Each entry in the array specifies a hardware
+ * specific configuration for a given TMDS clock frequency range. The array
+ * should be terminated with an entry that has all fields set to zero.
+ *
+ * @min: Lower bound of TMDS clock frequency this entry applies to
+ * @max: Upper bound of TMDS clock frequency this entry applies to
+ * @mode: SoC specific register configuration
+ */
+struct pllmode {
+	u32 min;
+	u32 max;
+	u32 mode;
+};
+
+#define NB_PLL_MODE 7
+static struct pllmode pllmodes[NB_PLL_MODE] = {
+	{13500000, 13513500, HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ},
+	{25174800, 25200000, HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ},
+	{27000000, 27027000, HDMI_SRZ_PLL_CFG_MODE_27_MHZ},
+	{54000000, 54054000, HDMI_SRZ_PLL_CFG_MODE_54_MHZ},
+	{72000000, 74250000, HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ},
+	{108000000, 108108000, HDMI_SRZ_PLL_CFG_MODE_108_MHZ},
+	{148351648, 297000000, HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ}
+};
+
+#define NB_HDMI_PHY_CONFIG 5
+static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = {
+	{0, 40000000, {0x00101010, 0x00101010, 0x00101010, 0x02} },
+	{40000000, 140000000, {0x00111111, 0x00111111, 0x00111111, 0x02} },
+	{140000000, 160000000, {0x00131313, 0x00101010, 0x00101010, 0x02} },
+	{160000000, 250000000, {0x00131313, 0x00111111, 0x00111111, 0x03FE} },
+	{250000000, 300000000, {0x00151515, 0x00101010, 0x00101010, 0x03FE} },
+};
+
+#define PLL_CHANGE_DELAY	1 /* ms */
+
+/**
+ * Disable the pll rejection
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ *
+ * return true if the pll has been disabled
+ */
+static bool disable_pll_rejection(struct sti_hdmi *hdmi)
+{
+	u32 val;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
+	val &= ~REJECTION_PLL_HDMI_ENABLE_MASK;
+	writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
+
+	msleep(PLL_CHANGE_DELAY);
+	val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS);
+
+	return !(val & REJECTION_PLL_HDMI_REJ_PLL_LOCK);
+}
+
+/**
+ * Enable the old BCH/rejection PLL is now reused to provide the CLKPXPLL
+ * clock input to the new PHY PLL that generates the serializer clock
+ * (TMDS*10) and the TMDS clock which is now fed back into the HDMI
+ * formatter instead of the TMDS clock line from ClockGenB.
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ *
+ * return true if pll has been correctly set
+ */
+static bool enable_pll_rejection(struct sti_hdmi *hdmi)
+{
+	unsigned int inputclock;
+	u32 mdiv, ndiv, pdiv, val;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	if (!disable_pll_rejection(hdmi))
+		return false;
+
+	inputclock = hdmi->mode.clock * 1000;
+
+	DRM_DEBUG_DRIVER("hdmi rejection pll input clock = %dHz\n", inputclock);
+
+
+	/* Power up the HDMI rejection PLL
+	 * Note: On this SoC (stiH416) we are forced to have the input clock
+	 * be equal to the HDMI pixel clock.
+	 *
+	 * The values here have been suggested by validation however they are
+	 * still provisional and subject to change.
+	 *
+	 * PLLout = (Fin*Mdiv) / ((2 * Ndiv) / 2^Pdiv)
+	 */
+	if (inputclock < 50000000) {
+		/*
+		 * For slower clocks we need to multiply more to keep the
+		 * internal VCO frequency within the physical specification
+		 * of the PLL.
+		 */
+		pdiv = 4;
+		ndiv = 240;
+		mdiv = 30;
+	} else {
+		pdiv = 2;
+		ndiv = 60;
+		mdiv = 30;
+	}
+
+	val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
+
+	val &= ~(REJECTION_PLL_HDMI_PDIV_MASK |
+		REJECTION_PLL_HDMI_NDIV_MASK |
+		REJECTION_PLL_HDMI_MDIV_MASK |
+		REJECTION_PLL_HDMI_ENABLE_MASK);
+
+	val |=	(pdiv << REJECTION_PLL_HDMI_PDIV_SHIFT) |
+		(ndiv << REJECTION_PLL_HDMI_NDIV_SHIFT) |
+		(mdiv << REJECTION_PLL_HDMI_MDIV_SHIFT) |
+		(0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT);
+
+	writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
+
+	msleep(PLL_CHANGE_DELAY);
+	val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS);
+
+	return (val & REJECTION_PLL_HDMI_REJ_PLL_LOCK);
+}
+
+/**
+ * Start hdmi phy macro cell tx3g0c55
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ *
+ * Return false if an error occur
+ */
+static bool sti_hdmi_tx3g0c55phy_start(struct sti_hdmi *hdmi)
+{
+	u32 ckpxpll = hdmi->mode.clock * 1000;
+	u32 val, tmdsck, freqvco, pllctrl = 0;
+	unsigned int i;
+
+	if (!enable_pll_rejection(hdmi))
+		return false;
+
+	DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll);
+
+	/* Assuming no pixel repetition and 24bits color */
+	tmdsck = ckpxpll;
+	pllctrl = 2 << HDMI_SRZ_PLL_CFG_NDIV_SHIFT;
+
+	/*
+	 * Setup the PLL mode parameter based on the ckpxpll. If we haven't got
+	 * a clock frequency supported by one of the specific PLL modes then we
+	 * will end up using the generic mode (0) which only supports a 10x
+	 * multiplier, hence only 24bit color.
+	 */
+	for (i = 0; i < NB_PLL_MODE; i++) {
+		if (ckpxpll >= pllmodes[i].min && ckpxpll <= pllmodes[i].max)
+			pllctrl |= HDMI_SRZ_PLL_CFG_MODE(pllmodes[i].mode);
+	}
+
+	freqvco = tmdsck * 10;
+	if (freqvco <= 425000000UL)
+		pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_425MHZ);
+	else if (freqvco <= 850000000UL)
+		pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_850MHZ);
+	else if (freqvco <= 1700000000UL)
+		pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_1700MHZ);
+	else if (freqvco <= 2970000000UL)
+		pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_3000MHZ);
+	else {
+		DRM_ERROR("PHY serializer clock out of range\n");
+		goto err;
+	}
+
+	/*
+	 * Configure and power up the PHY PLL
+	 */
+	hdmi->event_received = false;
+	DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl);
+	hdmi_write(hdmi, pllctrl, HDMI_SRZ_PLL_CFG);
+
+	/* wait PLL interrupt */
+	wait_event_interruptible_timeout(hdmi->wait_event,
+					 hdmi->event_received == true,
+					 msecs_to_jiffies
+					 (HDMI_TIMEOUT_PLL_LOCK));
+
+	if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) {
+		DRM_ERROR("hdmi phy pll not locked\n");
+		goto err;
+	}
+
+	DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
+
+	/*
+	 * To configure the source termination and pre-emphasis appropriately
+	 * for different high speed TMDS clock frequencies a phy configuration
+	 * table must be provided, tailored to the SoC and board combination.
+	 */
+	for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) {
+		if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) &&
+		    (hdmiphy_config[i].max_tmds_freq >= tmdsck)) {
+			val = hdmiphy_config[i].config[0];
+			hdmi_write(hdmi, val, HDMI_SRZ_TAP_1);
+			val = hdmiphy_config[i].config[1];
+			hdmi_write(hdmi, val, HDMI_SRZ_TAP_2);
+			val = hdmiphy_config[i].config[2];
+			hdmi_write(hdmi, val, HDMI_SRZ_TAP_3);
+			val = hdmiphy_config[i].config[3];
+			val |= HDMI_SRZ_CTRL_EXTERNAL_DATA_EN;
+			val &= ~HDMI_SRZ_CTRL_POWER_DOWN;
+			hdmi_write(hdmi, val, HDMI_SRZ_CTRL);
+
+			DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x 0x%x\n",
+					 hdmiphy_config[i].config[0],
+					 hdmiphy_config[i].config[1],
+					 hdmiphy_config[i].config[2],
+					 hdmiphy_config[i].config[3]);
+			return true;
+		}
+	}
+
+	/*
+	 * Default, power up the serializer with no pre-emphasis or source
+	 * termination.
+	 */
+	hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_1);
+	hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_2);
+	hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_3);
+	hdmi_write(hdmi, HDMI_SRZ_CTRL_EXTERNAL_DATA_EN, HDMI_SRZ_CTRL);
+
+	return true;
+
+err:
+	disable_pll_rejection(hdmi);
+
+	return false;
+}
+
+/**
+ * Stop hdmi phy macro cell tx3g0c55
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ */
+static void sti_hdmi_tx3g0c55phy_stop(struct sti_hdmi *hdmi)
+{
+	DRM_DEBUG_DRIVER("\n");
+
+	hdmi->event_received = false;
+
+	hdmi_write(hdmi, HDMI_SRZ_CTRL_POWER_DOWN, HDMI_SRZ_CTRL);
+	hdmi_write(hdmi, HDMI_SRZ_PLL_CFG_POWER_DOWN, HDMI_SRZ_PLL_CFG);
+
+	/* wait PLL interrupt */
+	wait_event_interruptible_timeout(hdmi->wait_event,
+					 hdmi->event_received == true,
+					 msecs_to_jiffies
+					 (HDMI_TIMEOUT_PLL_LOCK));
+
+	if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK)
+		DRM_ERROR("hdmi phy pll not well disabled\n");
+
+	disable_pll_rejection(hdmi);
+}
+
+struct hdmi_phy_ops tx3g0c55phy_ops = {
+	.start = sti_hdmi_tx3g0c55phy_start,
+	.stop = sti_hdmi_tx3g0c55phy_stop,
+};
diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h
new file mode 100644
index 0000000..068237b
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_HDMI_TX3G0C55PHY_H_
+#define _STI_HDMI_TX3G0C55PHY_H_
+
+#include "sti_hdmi.h"
+
+extern struct hdmi_phy_ops tx3g0c55phy_ops;
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c
new file mode 100644
index 0000000..8e0ceb0
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include "sti_hdmi_tx3g4c28phy.h"
+
+#define HDMI_SRZ_CFG                             0x504
+#define HDMI_SRZ_PLL_CFG                         0x510
+#define HDMI_SRZ_ICNTL                           0x518
+#define HDMI_SRZ_CALCODE_EXT                     0x520
+
+#define HDMI_SRZ_CFG_EN                          BIT(0)
+#define HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT BIT(1)
+#define HDMI_SRZ_CFG_EXTERNAL_DATA               BIT(16)
+#define HDMI_SRZ_CFG_RBIAS_EXT                   BIT(17)
+#define HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION      BIT(18)
+#define HDMI_SRZ_CFG_EN_BIASRES_DETECTION        BIT(19)
+#define HDMI_SRZ_CFG_EN_SRC_TERMINATION          BIT(24)
+
+#define HDMI_SRZ_CFG_INTERNAL_MASK  (HDMI_SRZ_CFG_EN     | \
+		HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT | \
+		HDMI_SRZ_CFG_EXTERNAL_DATA               | \
+		HDMI_SRZ_CFG_RBIAS_EXT                   | \
+		HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION      | \
+		HDMI_SRZ_CFG_EN_BIASRES_DETECTION        | \
+		HDMI_SRZ_CFG_EN_SRC_TERMINATION)
+
+#define PLL_CFG_EN                               BIT(0)
+#define PLL_CFG_NDIV_SHIFT                       (8)
+#define PLL_CFG_IDF_SHIFT                        (16)
+#define PLL_CFG_ODF_SHIFT                        (24)
+
+#define ODF_DIV_1                                (0)
+#define ODF_DIV_2                                (1)
+#define ODF_DIV_4                                (2)
+#define ODF_DIV_8                                (3)
+
+#define HDMI_TIMEOUT_PLL_LOCK  50  /*milliseconds */
+
+struct plldividers_s {
+	uint32_t min;
+	uint32_t max;
+	uint32_t idf;
+	uint32_t odf;
+};
+
+/*
+ * Functional specification recommended values
+ */
+#define NB_PLL_MODE 5
+static struct plldividers_s plldividers[NB_PLL_MODE] = {
+	{0, 20000000, 1, ODF_DIV_8},
+	{20000000, 42500000, 2, ODF_DIV_8},
+	{42500000, 85000000, 4, ODF_DIV_4},
+	{85000000, 170000000, 8, ODF_DIV_2},
+	{170000000, 340000000, 16, ODF_DIV_1}
+};
+
+#define NB_HDMI_PHY_CONFIG 2
+static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = {
+	{0, 250000000, {0x0, 0x0, 0x0, 0x0} },
+	{250000000, 300000000, {0x1110, 0x0, 0x0, 0x0} },
+};
+
+/**
+ * Start hdmi phy macro cell tx3g4c28
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ *
+ * Return false if an error occur
+ */
+static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi *hdmi)
+{
+	u32 ckpxpll = hdmi->mode.clock * 1000;
+	u32 val, tmdsck, idf, odf, pllctrl = 0;
+	bool foundplldivides = false;
+	int i;
+
+	DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll);
+
+	for (i = 0; i < NB_PLL_MODE; i++) {
+		if (ckpxpll >= plldividers[i].min &&
+		    ckpxpll < plldividers[i].max) {
+			idf = plldividers[i].idf;
+			odf = plldividers[i].odf;
+			foundplldivides = true;
+			break;
+		}
+	}
+
+	if (!foundplldivides) {
+		DRM_ERROR("input TMDS clock speed (%d) not supported\n",
+			  ckpxpll);
+		goto err;
+	}
+
+	/* Assuming no pixel repetition and 24bits color */
+	tmdsck = ckpxpll;
+	pllctrl |= 40 << PLL_CFG_NDIV_SHIFT;
+
+	if (tmdsck > 340000000) {
+		DRM_ERROR("output TMDS clock (%d) out of range\n", tmdsck);
+		goto err;
+	}
+
+	pllctrl |= idf << PLL_CFG_IDF_SHIFT;
+	pllctrl |= odf << PLL_CFG_ODF_SHIFT;
+
+	/*
+	 * Configure and power up the PHY PLL
+	 */
+	hdmi->event_received = false;
+	DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl);
+	hdmi_write(hdmi, (pllctrl | PLL_CFG_EN), HDMI_SRZ_PLL_CFG);
+
+	/* wait PLL interrupt */
+	wait_event_interruptible_timeout(hdmi->wait_event,
+					 hdmi->event_received == true,
+					 msecs_to_jiffies
+					 (HDMI_TIMEOUT_PLL_LOCK));
+
+	if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) {
+		DRM_ERROR("hdmi phy pll not locked\n");
+		goto err;
+	}
+
+	DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
+
+	val = (HDMI_SRZ_CFG_EN |
+	       HDMI_SRZ_CFG_EXTERNAL_DATA |
+	       HDMI_SRZ_CFG_EN_BIASRES_DETECTION |
+	       HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION);
+
+	if (tmdsck > 165000000)
+		val |= HDMI_SRZ_CFG_EN_SRC_TERMINATION;
+
+	/*
+	 * To configure the source termination and pre-emphasis appropriately
+	 * for different high speed TMDS clock frequencies a phy configuration
+	 * table must be provided, tailored to the SoC and board combination.
+	 */
+	for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) {
+		if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) &&
+		    (hdmiphy_config[i].max_tmds_freq >= tmdsck)) {
+			val |= (hdmiphy_config[i].config[0]
+				& ~HDMI_SRZ_CFG_INTERNAL_MASK);
+			hdmi_write(hdmi, val, HDMI_SRZ_CFG);
+
+			val = hdmiphy_config[i].config[1];
+			hdmi_write(hdmi, val, HDMI_SRZ_ICNTL);
+
+			val = hdmiphy_config[i].config[2];
+			hdmi_write(hdmi, val, HDMI_SRZ_CALCODE_EXT);
+
+			DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x\n",
+					 hdmiphy_config[i].config[0],
+					 hdmiphy_config[i].config[1],
+					 hdmiphy_config[i].config[2]);
+			return true;
+		}
+	}
+
+	/*
+	 * Default, power up the serializer with no pre-emphasis or
+	 * output swing correction
+	 */
+	hdmi_write(hdmi, val,  HDMI_SRZ_CFG);
+	hdmi_write(hdmi, 0x0, HDMI_SRZ_ICNTL);
+	hdmi_write(hdmi, 0x0, HDMI_SRZ_CALCODE_EXT);
+
+	return true;
+
+err:
+	return false;
+}
+
+/**
+ * Stop hdmi phy macro cell tx3g4c28
+ *
+ * @hdmi: pointer on the hdmi internal structure
+ */
+static void sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi *hdmi)
+{
+	int val = 0;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	hdmi->event_received = false;
+
+	val = HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION;
+	val |= HDMI_SRZ_CFG_EN_BIASRES_DETECTION;
+
+	hdmi_write(hdmi, val, HDMI_SRZ_CFG);
+	hdmi_write(hdmi, 0, HDMI_SRZ_PLL_CFG);
+
+	/* wait PLL interrupt */
+	wait_event_interruptible_timeout(hdmi->wait_event,
+					 hdmi->event_received == true,
+					 msecs_to_jiffies
+					 (HDMI_TIMEOUT_PLL_LOCK));
+
+	if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK)
+		DRM_ERROR("hdmi phy pll not well disabled\n");
+}
+
+struct hdmi_phy_ops tx3g4c28phy_ops = {
+	.start = sti_hdmi_tx3g4c28phy_start,
+	.stop = sti_hdmi_tx3g4c28phy_stop,
+};
diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h
new file mode 100644
index 0000000..f99a7ff
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_HDMI_TX3G4C28PHY_H_
+#define _STI_HDMI_TX3G4C28PHY_H_
+
+#include "sti_hdmi.h"
+
+extern struct hdmi_phy_ops tx3g4c28phy_ops;
+
+#endif
-- 
1.9.1


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

* [PATCH v4 05/11] drm: sti: add HDA driver
  2014-05-29  6:36 [PATCH v4 00/11] Add DRM for stih4xx platforms Benjamin Gaignard
                   ` (3 preceding siblings ...)
  2014-05-29  6:37 ` [PATCH v4 04/11] drm: sti: add HDMI driver Benjamin Gaignard
@ 2014-05-29  6:37 ` Benjamin Gaignard
  2014-05-29  6:37 ` [PATCH v4 06/11] drm: sti: add TVOut driver Benjamin Gaignard
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Benjamin Gaignard @ 2014-05-29  6:37 UTC (permalink / raw)
  To: dri-devel, linaro-mm-sig, linux-kernel, airlied
  Cc: lee.jones, Benjamin Gaignard

Add driver to support analog TV ouput.

As HDMI driver HDA use sti_connector structure to provide
helper functions.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
---
 drivers/gpu/drm/sti/Makefile  |   3 +-
 drivers/gpu/drm/sti/sti_hda.c | 723 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 725 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/sti/sti_hda.c

diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
index 5521a85..df47171 100644
--- a/drivers/gpu/drm/sti/Makefile
+++ b/drivers/gpu/drm/sti/Makefile
@@ -3,4 +3,5 @@ obj-$(CONFIG_DRM_STI) += \
 	sti_vtac.o \
 	sti_hdmi.o \
 	sti_hdmi_tx3g0c55phy.o \
-	sti_hdmi_tx3g4c28phy.o
\ No newline at end of file
+	sti_hdmi_tx3g4c28phy.o \
+	sti_hda.o
diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c
new file mode 100644
index 0000000..9a6278d
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_hda.c
@@ -0,0 +1,723 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <drm/drmP.h>
+
+#include "sti_drm_connector.h"
+#include "sti_drm_encoder.h"
+
+/* HDformatter registers */
+#define HDA_ANA_CFG                     0x0000
+#define HDA_ANA_SCALE_CTRL_Y            0x0004
+#define HDA_ANA_SCALE_CTRL_CB           0x0008
+#define HDA_ANA_SCALE_CTRL_CR           0x000C
+#define HDA_ANA_ANC_CTRL                0x0010
+#define HDA_ANA_SRC_Y_CFG               0x0014
+#define HDA_COEFF_Y_PH1_TAP123          0x0018
+#define HDA_COEFF_Y_PH1_TAP456          0x001C
+#define HDA_COEFF_Y_PH2_TAP123          0x0020
+#define HDA_COEFF_Y_PH2_TAP456          0x0024
+#define HDA_COEFF_Y_PH3_TAP123          0x0028
+#define HDA_COEFF_Y_PH3_TAP456          0x002C
+#define HDA_COEFF_Y_PH4_TAP123          0x0030
+#define HDA_COEFF_Y_PH4_TAP456          0x0034
+#define HDA_ANA_SRC_C_CFG               0x0040
+#define HDA_COEFF_C_PH1_TAP123          0x0044
+#define HDA_COEFF_C_PH1_TAP456          0x0048
+#define HDA_COEFF_C_PH2_TAP123          0x004C
+#define HDA_COEFF_C_PH2_TAP456          0x0050
+#define HDA_COEFF_C_PH3_TAP123          0x0054
+#define HDA_COEFF_C_PH3_TAP456          0x0058
+#define HDA_COEFF_C_PH4_TAP123          0x005C
+#define HDA_COEFF_C_PH4_TAP456          0x0060
+#define HDA_SYNC_AWGI                   0x0300
+
+/* HDA_ANA_CFG */
+#define CFG_AWG_ASYNC_EN                BIT(0)
+#define CFG_AWG_ASYNC_HSYNC_MTD         BIT(1)
+#define CFG_AWG_ASYNC_VSYNC_MTD         BIT(2)
+#define CFG_AWG_SYNC_DEL                BIT(3)
+#define CFG_AWG_FLTR_MODE_SHIFT         4
+#define CFG_AWG_FLTR_MODE_MASK          (0xF << CFG_AWG_FLTR_MODE_SHIFT)
+#define CFG_AWG_FLTR_MODE_SD            (0 << CFG_AWG_FLTR_MODE_SHIFT)
+#define CFG_AWG_FLTR_MODE_ED            (1 << CFG_AWG_FLTR_MODE_SHIFT)
+#define CFG_AWG_FLTR_MODE_HD            (2 << CFG_AWG_FLTR_MODE_SHIFT)
+#define CFG_SYNC_ON_PBPR_MASK           BIT(8)
+#define CFG_PREFILTER_EN_MASK           BIT(9)
+#define CFG_PBPR_SYNC_OFF_SHIFT         16
+#define CFG_PBPR_SYNC_OFF_MASK          (0x7FF << CFG_PBPR_SYNC_OFF_SHIFT)
+#define CFG_PBPR_SYNC_OFF_VAL           0x117 /* Voltage dependent. stiH416 */
+
+/* Default scaling values */
+#define SCALE_CTRL_Y_DFLT               0x00C50256
+#define SCALE_CTRL_CB_DFLT              0x00DB0249
+#define SCALE_CTRL_CR_DFLT              0x00DB0249
+
+/* Video DACs control */
+#define VIDEO_DACS_CONTROL_MASK         0x0FFF
+#define VIDEO_DACS_CONTROL_SYSCFG2535   0x085C /* for stih416 */
+#define DAC_CFG_HD_OFF_SHIFT            5
+#define DAC_CFG_HD_OFF_MASK             (0x7 << DAC_CFG_HD_OFF_SHIFT)
+#define VIDEO_DACS_CONTROL_SYSCFG5072   0x0120 /* for stih407 */
+#define DAC_CFG_HD_HZUVW_OFF_MASK       BIT(1)
+
+
+/* Upsampler values for the alternative 2X Filter */
+#define SAMPLER_COEF_NB                 8
+#define HDA_ANA_SRC_Y_CFG_ALT_2X        0x01130000
+static u32 coef_y_alt_2x[] = {
+	0x00FE83FB, 0x1F900401, 0x00000000, 0x00000000,
+	0x00F408F9, 0x055F7C25, 0x00000000, 0x00000000
+};
+
+#define HDA_ANA_SRC_C_CFG_ALT_2X        0x01750004
+static u32 coef_c_alt_2x[] = {
+	0x001305F7, 0x05274BD0, 0x00000000, 0x00000000,
+	0x0004907C, 0x09C80B9D, 0x00000000, 0x00000000
+};
+
+/* Upsampler values for the 4X Filter */
+#define HDA_ANA_SRC_Y_CFG_4X            0x01ED0005
+#define HDA_ANA_SRC_C_CFG_4X            0x01ED0004
+static u32 coef_yc_4x[] = {
+	0x00FC827F, 0x008FE20B, 0x00F684FC, 0x050F7C24,
+	0x00F4857C, 0x0A1F402E, 0x00FA027F, 0x0E076E1D
+};
+
+/* AWG instructions for some video modes */
+#define AWG_MAX_INST                    64
+
+/* 720p@50 */
+static u32 AWGi_720p_50[] = {
+	0x00000971, 0x00000C26, 0x0000013B, 0x00000CDA,
+	0x00000104, 0x00000E7E, 0x00000E7F, 0x0000013B,
+	0x00000D8E, 0x00000104, 0x00001804, 0x00000971,
+	0x00000C26, 0x0000003B, 0x00000FB4, 0x00000FB5,
+	0x00000104, 0x00001AE8
+};
+
+#define NN_720p_50 ARRAY_SIZE(AWGi_720p_50)
+
+/* 720p@60 */
+static u32 AWGi_720p_60[] = {
+	0x00000971, 0x00000C26, 0x0000013B, 0x00000CDA,
+	0x00000104, 0x00000E7E, 0x00000E7F, 0x0000013B,
+	0x00000C44, 0x00000104, 0x00001804, 0x00000971,
+	0x00000C26, 0x0000003B, 0x00000F0F, 0x00000F10,
+	0x00000104, 0x00001AE8
+};
+
+#define NN_720p_60 ARRAY_SIZE(AWGi_720p_60)
+
+/* 1080p@30 */
+static u32 AWGi_1080p_30[] = {
+	0x00000971, 0x00000C2A, 0x0000013B, 0x00000C56,
+	0x00000104, 0x00000FDC, 0x00000FDD, 0x0000013B,
+	0x00000C2A, 0x00000104, 0x00001804, 0x00000971,
+	0x00000C2A, 0x0000003B, 0x00000EBE, 0x00000EBF,
+	0x00000EBF, 0x00000104, 0x00001A2F, 0x00001C4B,
+	0x00001C52
+};
+
+#define NN_1080p_30 ARRAY_SIZE(AWGi_1080p_30)
+
+/* 1080p@25 */
+static u32 AWGi_1080p_25[] = {
+	0x00000971, 0x00000C2A, 0x0000013B, 0x00000C56,
+	0x00000104, 0x00000FDC, 0x00000FDD, 0x0000013B,
+	0x00000DE2, 0x00000104, 0x00001804, 0x00000971,
+	0x00000C2A, 0x0000003B, 0x00000F51, 0x00000F51,
+	0x00000F52, 0x00000104, 0x00001A2F, 0x00001C4B,
+	0x00001C52
+};
+
+#define NN_1080p_25 ARRAY_SIZE(AWGi_1080p_25)
+
+/* 1080p@24 */
+static u32 AWGi_1080p_24[] = {
+	0x00000971, 0x00000C2A, 0x0000013B, 0x00000C56,
+	0x00000104, 0x00000FDC, 0x00000FDD, 0x0000013B,
+	0x00000E50, 0x00000104, 0x00001804, 0x00000971,
+	0x00000C2A, 0x0000003B, 0x00000F76, 0x00000F76,
+	0x00000F76, 0x00000104, 0x00001A2F, 0x00001C4B,
+	0x00001C52
+};
+
+#define NN_1080p_24 ARRAY_SIZE(AWGi_1080p_24)
+
+/* 720x480p@60 */
+static u32 AWGi_720x480p_60[] = {
+	0x00000904, 0x00000F18, 0x0000013B, 0x00001805,
+	0x00000904, 0x00000C3D, 0x0000003B, 0x00001A06
+};
+
+#define NN_720x480p_60 ARRAY_SIZE(AWGi_720x480p_60)
+
+/* Video mode category */
+enum sti_hda_vid_cat {
+	VID_SD,
+	VID_ED,
+	VID_HD_74M,
+	VID_HD_148M
+};
+
+struct sti_hda_video_config {
+	struct drm_display_mode mode;
+	u32 *awg_instr;
+	int nb_instr;
+	enum sti_hda_vid_cat vid_cat;
+};
+
+/* HD analog supported modes
+ * Interlaced modes may be added when supported by the whole display chain
+ */
+static const struct sti_hda_video_config hda_supported_modes[] = {
+	/* 1080p30 74.250Mhz */
+	{{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
+		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+	 AWGi_1080p_30, NN_1080p_30, VID_HD_74M},
+	/* 1080p30 74.176Mhz */
+	{{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74176, 1920, 2008,
+		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+	 AWGi_1080p_30, NN_1080p_30, VID_HD_74M},
+	/* 1080p24 74.250Mhz */
+	{{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
+		   2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+	 AWGi_1080p_24, NN_1080p_24, VID_HD_74M},
+	/* 1080p24 74.176Mhz */
+	{{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74176, 1920, 2558,
+		   2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+	 AWGi_1080p_24, NN_1080p_24, VID_HD_74M},
+	/* 1080p25 74.250Mhz */
+	{{DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
+		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+	 AWGi_1080p_25, NN_1080p_25, VID_HD_74M},
+	/* 720p60 74.250Mhz */
+	{{DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+		   1430, 1650, 0, 720, 725, 730, 750, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+	 AWGi_720p_60, NN_720p_60, VID_HD_74M},
+	/* 720p60 74.176Mhz */
+	{{DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74176, 1280, 1390,
+		   1430, 1650, 0, 720, 725, 730, 750, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+	 AWGi_720p_60, NN_720p_60, VID_HD_74M},
+	/* 720p50 74.250Mhz */
+	{{DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
+		   1760, 1980, 0, 720, 725, 730, 750, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC)},
+	 AWGi_720p_50, NN_720p_50, VID_HD_74M},
+	/* 720x480p60 27.027Mhz */
+	{{DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27027, 720, 736,
+		   798, 858, 0, 480, 489, 495, 525, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)},
+	 AWGi_720x480p_60, NN_720x480p_60, VID_ED},
+	/* 720x480p60 27.000Mhz */
+	{{DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
+		   798, 858, 0, 480, 489, 495, 525, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)},
+	 AWGi_720x480p_60, NN_720x480p_60, VID_ED}
+};
+
+/**
+ * STI hd analog structure
+ *
+ * @dev: driver device
+ * @drm_dev: pointer to drm device
+ * @mode: current display mode selected
+ * @regs: HD analog register
+ * @video_dacs_ctrl: video DACS control register
+ * @enabled: true if HD analog is enabled else false
+ */
+struct sti_hda {
+	struct device dev;
+	struct drm_device *drm_dev;
+	struct drm_display_mode mode;
+	void __iomem *regs;
+	void __iomem *video_dacs_ctrl;
+	struct clk *clk_pix;
+	struct clk *clk_hddac;
+	bool enabled;
+};
+
+static u32 hda_read(struct sti_hda *hda, int offset)
+{
+	return readl(hda->regs + offset);
+}
+
+static void hda_write(struct sti_hda *hda, u32 val, int offset)
+{
+	writel(val, hda->regs + offset);
+}
+
+/**
+ * Search for a video mode in the supported modes table
+ *
+ * @mode: mode being searched
+ * @idx: index of the found mode
+ *
+ * Return true if mode is found
+ */
+static bool hda_get_mode_idx(struct drm_display_mode mode, int *idx)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(hda_supported_modes); i++)
+		if (drm_mode_equal(&hda_supported_modes[i].mode, &mode)) {
+			*idx = i;
+			return true;
+		}
+	return false;
+}
+
+/**
+ * Enable the HD DACS
+ *
+ * @hda: pointer to HD analog structure
+ * @enable: true if HD DACS need to be enabled, else false
+ */
+static void hda_enable_hd_dacs(struct sti_hda *hda, bool enable)
+{
+	u32 mask;
+
+	if (hda->video_dacs_ctrl) {
+		u32 val;
+
+		switch ((u32)hda->video_dacs_ctrl & VIDEO_DACS_CONTROL_MASK) {
+		case VIDEO_DACS_CONTROL_SYSCFG2535:
+			mask = DAC_CFG_HD_OFF_MASK;
+			break;
+		case VIDEO_DACS_CONTROL_SYSCFG5072:
+			mask = DAC_CFG_HD_HZUVW_OFF_MASK;
+			break;
+		default:
+			DRM_INFO("Video DACS control register not supported!");
+			return;
+		}
+
+		val = readl(hda->video_dacs_ctrl);
+		if (enable)
+			val &= ~mask;
+		else
+			val |= mask;
+
+		writel(val, hda->video_dacs_ctrl);
+	}
+
+}
+
+/**
+ * Configure AWG, writing instructions
+ *
+ * @hda: pointer to HD analog structure
+ * @awg_instr: pointer to AWG instructions table
+ * @nb: nb of AWG instructions
+ */
+static void sti_hda_configure_awg(struct sti_hda *hda, u32 *awg_instr, int nb)
+{
+	unsigned int i;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	for (i = 0; i < nb; i++)
+		hda_write(hda, awg_instr[i], HDA_SYNC_AWGI + i * 4);
+	for (i = nb; i < AWG_MAX_INST; i++)
+		hda_write(hda, 0, HDA_SYNC_AWGI + i * 4);
+}
+
+/**
+ * Get modes
+ *
+ * @drm_connector: pointer on the drm connector
+ *
+ * Return number of modes
+ */
+static int sti_hda_get_modes(struct drm_connector *drm_connector)
+{
+	struct drm_device *dev = drm_connector->dev;
+	struct drm_display_mode *mode;
+	unsigned int i;
+	int count = 0;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	for (i = 0; i < ARRAY_SIZE(hda_supported_modes); i++) {
+		mode = drm_mode_duplicate(dev, &hda_supported_modes[i].mode);
+		if (!mode)
+			continue;
+		mode->vrefresh = drm_mode_vrefresh(mode);
+
+		/* the first mode is the preferred mode */
+		if (i == 0)
+			mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+		drm_mode_probed_add(drm_connector, mode);
+		count++;
+	}
+
+	drm_mode_sort(&drm_connector->modes);
+
+	return count;
+}
+
+static int sti_hda_start(struct drm_connector *connector)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+	struct sti_hda *hda = dev_get_drvdata(sti_connector->dev);
+	u32 val, i, mode_idx;
+	u32 src_filter_y, src_filter_c;
+	u32 *coef_y, *coef_c;
+	u32 filter_mode;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	/* Prepare/enable clocks */
+	if (clk_prepare_enable(hda->clk_pix))
+		DRM_ERROR("Failed to prepare/enable hda_pix clk\n");
+	if (clk_prepare_enable(hda->clk_hddac))
+		DRM_ERROR("Failed to prepare/enable hda_hddac clk\n");
+
+	hda->enabled = true;
+
+	if (!hda_get_mode_idx(hda->mode, &mode_idx)) {
+		DRM_ERROR("Undefined mode\n");
+		return 1;
+	}
+
+	switch (hda_supported_modes[mode_idx].vid_cat) {
+	case VID_HD_148M:
+		DRM_ERROR("Beyond HD analog capabilities\n");
+		return 1;
+	case VID_HD_74M:
+		/* HD use alternate 2x filter */
+		filter_mode = CFG_AWG_FLTR_MODE_HD;
+		src_filter_y = HDA_ANA_SRC_Y_CFG_ALT_2X;
+		src_filter_c = HDA_ANA_SRC_C_CFG_ALT_2X;
+		coef_y = coef_y_alt_2x;
+		coef_c = coef_c_alt_2x;
+		break;
+	case VID_ED:
+		/* ED uses 4x filter */
+		filter_mode = CFG_AWG_FLTR_MODE_ED;
+		src_filter_y = HDA_ANA_SRC_Y_CFG_4X;
+		src_filter_c = HDA_ANA_SRC_C_CFG_4X;
+		coef_y = coef_yc_4x;
+		coef_c = coef_yc_4x;
+		break;
+	case VID_SD:
+		DRM_ERROR("Not supported\n");
+		return 1;
+	default:
+		DRM_ERROR("Undefined resolution\n");
+		return 1;
+	}
+	DRM_DEBUG_DRIVER("Using HDA mode #%d\n", mode_idx);
+
+	/* Enable HD Video DACs */
+	hda_enable_hd_dacs(hda, true);
+
+	/* Configure scaler */
+	hda_write(hda, SCALE_CTRL_Y_DFLT, HDA_ANA_SCALE_CTRL_Y);
+	hda_write(hda, SCALE_CTRL_CB_DFLT, HDA_ANA_SCALE_CTRL_CB);
+	hda_write(hda, SCALE_CTRL_CR_DFLT, HDA_ANA_SCALE_CTRL_CR);
+
+	/* Configure sampler */
+	hda_write(hda , src_filter_y, HDA_ANA_SRC_Y_CFG);
+	hda_write(hda, src_filter_c,  HDA_ANA_SRC_C_CFG);
+	for (i = 0; i < SAMPLER_COEF_NB; i++) {
+		hda_write(hda, coef_y[i], HDA_COEFF_Y_PH1_TAP123 + i * 4);
+		hda_write(hda, coef_c[i], HDA_COEFF_C_PH1_TAP123 + i * 4);
+	}
+
+	/* Configure main HDFormatter */
+	val = 0;
+	val |= (hda->mode.flags & DRM_MODE_FLAG_INTERLACE) ?
+	    0 : CFG_AWG_ASYNC_VSYNC_MTD;
+	val |= (CFG_PBPR_SYNC_OFF_VAL << CFG_PBPR_SYNC_OFF_SHIFT);
+	val |= filter_mode;
+	hda_write(hda, val, HDA_ANA_CFG);
+
+	/* Configure AWG */
+	sti_hda_configure_awg(hda, hda_supported_modes[mode_idx].awg_instr,
+			      hda_supported_modes[mode_idx].nb_instr);
+
+	/* Enable AWG */
+	val = hda_read(hda, HDA_ANA_CFG);
+	val |= CFG_AWG_ASYNC_EN;
+	hda_write(hda, val, HDA_ANA_CFG);
+
+	return 0;
+}
+
+static void sti_hda_stop(struct drm_connector *connector)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+	struct sti_hda *hda = dev_get_drvdata(sti_connector->dev);
+	u32 val;
+
+	if (!hda->enabled)
+		return;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	/* Disable HD DAC and AWG */
+	val = hda_read(hda, HDA_ANA_CFG);
+	val &= ~CFG_AWG_ASYNC_EN;
+	hda_write(hda, val, HDA_ANA_CFG);
+
+	hda_enable_hd_dacs(hda, false);
+
+	/* Disable/unprepare hda clock */
+	clk_disable_unprepare(hda->clk_hddac);
+	clk_disable_unprepare(hda->clk_pix);
+
+	hda->enabled = false;
+}
+
+#define CLK_TOLERANCE_HZ 50
+
+static int sti_hda_check_mode(struct drm_connector *connector,
+			      struct drm_display_mode *mode)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+	struct sti_hda *hda = dev_get_drvdata(sti_connector->dev);
+	int target = mode->clock * 1000;
+	int target_min = target - CLK_TOLERANCE_HZ;
+	int target_max = target + CLK_TOLERANCE_HZ;
+	int result;
+	int idx;
+
+	if (!hda_get_mode_idx(*mode, &idx)) {
+		return MODE_BAD;
+	} else {
+		result = clk_round_rate(hda->clk_pix, target);
+
+		DRM_DEBUG_DRIVER("target rate = %d => available rate = %d\n",
+				 target, result);
+
+		if ((result < target_min) || (result > target_max)) {
+			DRM_DEBUG_DRIVER("hda pixclk=%d not supported\n",
+					 target);
+			return MODE_BAD;
+		}
+	}
+
+	return MODE_OK;
+}
+
+static int sti_hda_set_mode(struct drm_connector *connector,
+			    struct drm_display_mode *mode, bool main_path)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+	struct sti_hda *hda = dev_get_drvdata(sti_connector->dev);
+	u32 mode_idx;
+	int hddac_rate;
+	int ret;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	memcpy(&hda->mode, mode, sizeof(struct drm_display_mode));
+
+	if (!hda_get_mode_idx(hda->mode, &mode_idx)) {
+		DRM_ERROR("Undefined mode\n");
+		return 1;
+	}
+
+	switch (hda_supported_modes[mode_idx].vid_cat) {
+	case VID_HD_74M:
+		/* HD use alternate 2x filter */
+		hddac_rate = mode->clock * 1000 * 2;
+		break;
+	case VID_ED:
+		/* ED uses 4x filter */
+		hddac_rate = mode->clock * 1000 * 4;
+		break;
+	default:
+		DRM_ERROR("Undefined mode\n");
+		return 1;
+	}
+
+	/* HD DAC = 148.5Mhz or 108 Mhz */
+	ret = clk_set_rate(hda->clk_hddac, hddac_rate);
+	if (ret < 0) {
+		DRM_ERROR("Cannot set rate (%dHz) for hda_hddac clk\n",
+			  hddac_rate);
+		return ret;
+	}
+
+	/* HDformatter clock = compositor clock */
+	ret = clk_set_rate(hda->clk_pix, mode->clock * 1000);
+	if (ret < 0) {
+		DRM_ERROR("Cannot set rate (%dHz) for hda_pix clk\n",
+			  mode->clock * 1000);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int sti_hda_detect(struct drm_connector *connector)
+{
+	return connector_status_connected;
+}
+
+static bool sti_hda_is_enabled(struct drm_connector *connector)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+	struct sti_hda *hda = dev_get_drvdata(sti_connector->dev);
+
+	return hda->enabled;
+}
+
+static void sti_hda_prepare(struct drm_connector *connector)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+	struct sti_hda *hda = dev_get_drvdata(sti_connector->dev);
+
+	DRM_DEBUG_DRIVER("\n");
+
+	/* reset HDF */
+	hda_write(hda, 0, HDA_ANA_CFG);
+	hda_write(hda, 0, HDA_ANA_ANC_CTRL);
+}
+
+static int sti_hda_bind(struct device *dev, struct device *master, void *data)
+{
+	struct sti_hda *hda = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	struct drm_encoder *encoder;
+	struct sti_connector *connector;
+
+	/* Set the drm device handle */
+	hda->drm_dev = drm_dev;
+
+	encoder = devm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL);
+	if (!encoder)
+		return -ENOMEM;
+
+	encoder->possible_crtcs = ENCODER_MAIN_CRTC_MASK;
+	encoder->possible_clones = 1 << STI_CONNECTOR_HDMI;
+
+	connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
+	if (!connector)
+		return -ENOMEM;
+
+	connector->dev = dev;
+	connector->master = master;
+	connector->encoder = encoder;
+	connector->start = sti_hda_start;
+	connector->stop = sti_hda_stop;
+	connector->get_modes = sti_hda_get_modes;
+	connector->check_mode = sti_hda_check_mode;
+	connector->set_mode = sti_hda_set_mode;
+	connector->detect = sti_hda_detect;
+	connector->is_enabled = sti_hda_is_enabled;
+	connector->prepare = sti_hda_prepare;
+	connector->type = STI_CONNECTOR_HDA;
+
+	return 0;
+}
+
+static void sti_hda_unbind(struct device *dev,
+		struct device *master, void *data)
+{
+	/* do nothing */
+}
+
+static const struct component_ops sti_hda_ops = {
+	.bind = sti_hda_bind,
+	.unbind = sti_hda_unbind,
+};
+
+static int sti_hda_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct sti_hda *hda;
+	struct resource *res;
+
+	DRM_INFO("%s\n", __func__);
+
+	hda = devm_kzalloc(dev, sizeof(*hda), GFP_KERNEL);
+	if (!hda)
+		return -ENOMEM;
+
+	hda->dev = pdev->dev;
+
+	/* Get resources */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hda-reg");
+	if (!res) {
+		DRM_ERROR("Invalid hda resource\n");
+		return -ENOMEM;
+	}
+	hda->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
+	if (IS_ERR(hda->regs))
+		return PTR_ERR(hda->regs);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+			"video-dacs-ctrl");
+	if (res) {
+		hda->video_dacs_ctrl = devm_ioremap_nocache(dev, res->start,
+				resource_size(res));
+		if (IS_ERR(hda->video_dacs_ctrl))
+			return PTR_ERR(hda->video_dacs_ctrl);
+	} else {
+		/* If no existing video-dacs-ctrl resource continue the probe */
+		DRM_DEBUG_DRIVER("No video-dacs-ctrl resource\n");
+		hda->video_dacs_ctrl = NULL;
+	}
+
+	/* Get clock resources */
+	hda->clk_pix = devm_clk_get(dev, "pix");
+	if (IS_ERR(hda->clk_pix)) {
+		DRM_ERROR("Cannot get hda_pix clock\n");
+		return PTR_ERR(hda->clk_pix);
+	}
+
+	hda->clk_hddac = devm_clk_get(dev, "hddac");
+	if (IS_ERR(hda->clk_hddac)) {
+		DRM_ERROR("Cannot get hda_hddac clock\n");
+		return PTR_ERR(hda->clk_hddac);
+	}
+
+	platform_set_drvdata(pdev, hda);
+
+	return component_add(&pdev->dev, &sti_hda_ops);
+}
+
+static int sti_hda_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &sti_hda_ops);
+	return 0;
+}
+
+static struct of_device_id hda_of_match[] = {
+	{ .compatible = "st,stih416-hda", },
+	{ .compatible = "st,stih407-hda", },
+	{ /* end node */ }
+};
+MODULE_DEVICE_TABLE(of, hda_of_match);
+
+struct platform_driver sti_hda_driver = {
+	.driver = {
+		.name = "sti-hda",
+		.owner = THIS_MODULE,
+		.of_match_table = hda_of_match,
+	},
+	.probe = sti_hda_probe,
+	.remove = sti_hda_remove,
+};
+
+module_platform_driver(sti_hda_driver);
-- 
1.9.1


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

* [PATCH v4 06/11] drm: sti: add TVOut driver
  2014-05-29  6:36 [PATCH v4 00/11] Add DRM for stih4xx platforms Benjamin Gaignard
                   ` (4 preceding siblings ...)
  2014-05-29  6:37 ` [PATCH v4 05/11] drm: sti: add HDA driver Benjamin Gaignard
@ 2014-05-29  6:37 ` Benjamin Gaignard
  2014-06-11 18:20   ` Rob Clark
  2014-05-29  6:37 ` [PATCH v4 07/11] drm: sti: add GDP layer Benjamin Gaignard
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 19+ messages in thread
From: Benjamin Gaignard @ 2014-05-29  6:37 UTC (permalink / raw)
  To: dri-devel, linaro-mm-sig, linux-kernel, airlied
  Cc: lee.jones, Benjamin Gaignard

TVout hardware block is responsible to dispatch the data flow coming
from compositor block to any of the output (HDMI or Analog TV).
It control when output are start/stop and configure according the
require flow path.

TVout is the parent of HDMI and HDA drivers and bind them at runtime.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
---
 drivers/gpu/drm/sti/Makefile    |   3 +-
 drivers/gpu/drm/sti/sti_tvout.c | 678 ++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sti/sti_tvout.h |  18 ++
 3 files changed, 698 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/sti/sti_tvout.c
 create mode 100644 drivers/gpu/drm/sti/sti_tvout.h

diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
index df47171..dcc9568 100644
--- a/drivers/gpu/drm/sti/Makefile
+++ b/drivers/gpu/drm/sti/Makefile
@@ -4,4 +4,5 @@ obj-$(CONFIG_DRM_STI) += \
 	sti_hdmi.o \
 	sti_hdmi_tx3g0c55phy.o \
 	sti_hdmi_tx3g4c28phy.o \
-	sti_hda.o
+	sti_hda.o \
+	sti_tvout.o
\ No newline at end of file
diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c
new file mode 100644
index 0000000..d778e35
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_tvout.c
@@ -0,0 +1,678 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ *          Vincent Abriou <vincent.abriou@st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "sti_drm_connector.h"
+#include "sti_drm_encoder.h"
+#include "sti_tvout.h"
+
+/* glue registers */
+#define TVO_CSC_MAIN_M0                  0x000
+#define TVO_CSC_MAIN_M1                  0x004
+#define TVO_CSC_MAIN_M2                  0x008
+#define TVO_CSC_MAIN_M3                  0x00c
+#define TVO_CSC_MAIN_M4                  0x010
+#define TVO_CSC_MAIN_M5                  0x014
+#define TVO_CSC_MAIN_M6                  0x018
+#define TVO_CSC_MAIN_M7                  0x01c
+#define TVO_MAIN_IN_VID_FORMAT           0x030
+#define TVO_CSC_AUX_M0                   0x100
+#define TVO_CSC_AUX_M1                   0x104
+#define TVO_CSC_AUX_M2                   0x108
+#define TVO_CSC_AUX_M3                   0x10c
+#define TVO_CSC_AUX_M4                   0x110
+#define TVO_CSC_AUX_M5                   0x114
+#define TVO_CSC_AUX_M6                   0x118
+#define TVO_CSC_AUX_M7                   0x11c
+#define TVO_AUX_IN_VID_FORMAT            0x130
+#define TVO_VIP_HDF                      0x400
+#define TVO_HD_SYNC_SEL                  0x418
+#define TVO_HD_DAC_CFG_OFF               0x420
+#define TVO_VIP_HDMI                     0x500
+#define TVO_HDMI_FORCE_COLOR_0           0x504
+#define TVO_HDMI_FORCE_COLOR_1           0x508
+#define TVO_HDMI_CLIP_VALUE_B_CB         0x50c
+#define TVO_HDMI_CLIP_VALUE_Y_G          0x510
+#define TVO_HDMI_CLIP_VALUE_R_CR         0x514
+#define TVO_HDMI_SYNC_SEL                0x518
+#define TVO_HDMI_DFV_OBS                 0x540
+
+#define TVO_IN_FMT_SIGNED                BIT(0)
+#define TVO_SYNC_EXT                     BIT(4)
+
+#define TVO_VIP_REORDER_R_SHIFT          24
+#define TVO_VIP_REORDER_G_SHIFT          20
+#define TVO_VIP_REORDER_B_SHIFT          16
+#define TVO_VIP_REORDER_MASK             0x3
+#define TVO_VIP_REORDER_Y_G_SEL          0
+#define TVO_VIP_REORDER_CB_B_SEL         1
+#define TVO_VIP_REORDER_CR_R_SEL         2
+
+#define TVO_VIP_CLIP_SHIFT               8
+#define TVO_VIP_CLIP_MASK                0x7
+#define TVO_VIP_CLIP_DISABLED            0
+#define TVO_VIP_CLIP_EAV_SAV             1
+#define TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y 2
+#define TVO_VIP_CLIP_LIMITED_RANGE_CB_CR 3
+#define TVO_VIP_CLIP_PROG_RANGE          4
+
+#define TVO_VIP_RND_SHIFT                4
+#define TVO_VIP_RND_MASK                 0x3
+#define TVO_VIP_RND_8BIT_ROUNDED         0
+#define TVO_VIP_RND_10BIT_ROUNDED        1
+#define TVO_VIP_RND_12BIT_ROUNDED        2
+
+#define TVO_VIP_SEL_INPUT_MASK           0xf
+#define TVO_VIP_SEL_INPUT_MAIN           0x0
+#define TVO_VIP_SEL_INPUT_AUX            0x8
+#define TVO_VIP_SEL_INPUT_FORCE_COLOR    0xf
+#define TVO_VIP_SEL_INPUT_BYPASS_MASK    0x1
+#define TVO_VIP_SEL_INPUT_BYPASSED       1
+
+#define TVO_SYNC_MAIN_VTG_SET_REF        0x00
+#define TVO_SYNC_MAIN_VTG_SET_1          0x01
+#define TVO_SYNC_MAIN_VTG_SET_2          0x02
+#define TVO_SYNC_MAIN_VTG_SET_3          0x03
+#define TVO_SYNC_MAIN_VTG_SET_4          0x04
+#define TVO_SYNC_MAIN_VTG_SET_5          0x05
+#define TVO_SYNC_MAIN_VTG_SET_6          0x06
+#define TVO_SYNC_AUX_VTG_SET_REF         0x10
+#define TVO_SYNC_AUX_VTG_SET_1           0x11
+#define TVO_SYNC_AUX_VTG_SET_2           0x12
+#define TVO_SYNC_AUX_VTG_SET_3           0x13
+#define TVO_SYNC_AUX_VTG_SET_4           0x14
+#define TVO_SYNC_AUX_VTG_SET_5           0x15
+#define TVO_SYNC_AUX_VTG_SET_6           0x16
+
+#define TVO_SYNC_HD_DCS_SHIFT            8
+
+/* enum listing the supported output data format */
+enum sti_tvout_video_out_type {
+	STI_TVOUT_VIDEO_OUT_RGB,
+	STI_TVOUT_VIDEO_OUT_YUV,
+};
+
+/**
+ * STI TVout structure
+ *
+ * @dev: pointer to driver device
+ * @regs: registers
+ * @reset: reset control of the tvout
+ */
+struct sti_tvout {
+	struct device *dev;
+	void __iomem *regs;
+	struct reset_control *reset;
+};
+
+/* preformatter conversion matrix */
+static const u32 rgb_to_ycbcr_601[8] = {
+	0xF927082E, 0x04C9FEAB, 0x01D30964, 0xFA95FD3D,
+	0x0000082E, 0x00002000, 0x00002000, 0x00000000
+};
+
+/* 709 RGB to YCbCr */
+static const u32 rgb_to_ycbcr_709[8] = {
+	0xF891082F, 0x0367FF40, 0x01280B71, 0xF9B1FE20,
+	0x0000082F, 0x00002000, 0x00002000, 0x00000000
+};
+
+static u32 tvout_read(struct sti_tvout *tvout, int offset)
+{
+	return readl(tvout->regs + offset);
+}
+
+static void tvout_write(struct sti_tvout *tvout, u32 val, int offset)
+{
+	writel(val, tvout->regs + offset);
+}
+
+/**
+ * Set the clipping mode of a VIP
+ *
+ * @tvout: tvout structure
+ * @cr_r:
+ * @y_g:
+ * @cb_b:
+ */
+static void tvout_vip_set_color_order(struct sti_tvout *tvout,
+				      u32 cr_r, u32 y_g, u32 cb_b)
+{
+	u32 val = tvout_read(tvout, TVO_VIP_HDMI);
+
+	val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT);
+	val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT);
+	val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT);
+	val |= cr_r << TVO_VIP_REORDER_R_SHIFT;
+	val |= y_g << TVO_VIP_REORDER_G_SHIFT;
+	val |= cb_b << TVO_VIP_REORDER_B_SHIFT;
+
+	tvout_write(tvout, val, TVO_VIP_HDMI);
+}
+
+/**
+ * Set the clipping mode of a VIP
+ *
+ * @tvout: tvout structure
+ * @range: clipping range
+ */
+static void tvout_vip_set_clip_mode(struct sti_tvout *tvout, u32 range)
+{
+	u32 val = tvout_read(tvout, TVO_VIP_HDMI);
+
+	val &= ~(TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT);
+	val |= range << TVO_VIP_CLIP_SHIFT;
+	tvout_write(tvout, val, TVO_VIP_HDMI);
+}
+
+/**
+ * Set the rounded value of a VIP
+ *
+ * @tvout: tvout structure
+ * @rnd: rounded val per component
+ */
+static void tvout_vip_set_rnd(struct sti_tvout *tvout, u32 rnd)
+{
+	u32 val = tvout_read(tvout, TVO_VIP_HDMI);
+
+	val &= ~(TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT);
+	val |= rnd << TVO_VIP_RND_SHIFT;
+	tvout_write(tvout, val, TVO_VIP_HDMI);
+}
+
+/**
+ * Select the VIP input
+ *
+ * @tvout: tvout structure
+ * @sel_input: selected_input (main/aux + conv)
+ */
+static void tvout_vip_set_sel_input(struct sti_tvout *tvout,
+				    bool main_path,
+				    bool sel_input_logic_inverted,
+				    enum sti_tvout_video_out_type video_out)
+{
+	u32 sel_input;
+	u32 val = tvout_read(tvout, TVO_VIP_HDMI);
+
+	if (main_path)
+		sel_input = TVO_VIP_SEL_INPUT_MAIN;
+	else
+		sel_input = TVO_VIP_SEL_INPUT_AUX;
+
+	switch (video_out) {
+	case STI_TVOUT_VIDEO_OUT_RGB:
+		sel_input |= TVO_VIP_SEL_INPUT_BYPASSED;
+		break;
+	case STI_TVOUT_VIDEO_OUT_YUV:
+		sel_input &= ~TVO_VIP_SEL_INPUT_BYPASSED;
+		break;
+	}
+
+	/* on stih407 chip the sel_input bypass mode logic is inverted */
+	if (sel_input_logic_inverted)
+		sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK;
+
+	val &= ~TVO_VIP_SEL_INPUT_MASK;
+	val |= sel_input;
+	tvout_write(tvout, val, TVO_VIP_HDMI);
+}
+
+/**
+ * Select the input video signed or unsigned
+ *
+ * @tvout: tvout structure
+ * @in_vid_signed: used video input format
+ */
+static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout, u32 in_vid_fmt)
+{
+	u32 val = tvout_read(tvout, TVO_VIP_HDMI);
+
+	val &= ~TVO_IN_FMT_SIGNED;
+	val |= in_vid_fmt;
+	tvout_write(tvout, val, TVO_MAIN_IN_VID_FORMAT);
+}
+
+/**
+ * Start VIP block for HDMI output
+ *
+ * @tvout: pointer on tvout structure
+ * @main_path: true if main path has to be used in the vip configuration
+ *	  else aux path is used.
+ */
+static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path)
+{
+	struct device_node *node = tvout->dev->of_node;
+	bool sel_input_logic_inverted = false;
+
+	dev_dbg(tvout->dev, "%s\n", __func__);
+
+	if (main_path) {
+		DRM_DEBUG_DRIVER("main vip for hdmi\n");
+		/* select the input sync for hdmi = VTG set 1 */
+		writel(TVO_SYNC_MAIN_VTG_SET_1,
+		       tvout->regs + TVO_HDMI_SYNC_SEL);
+	} else {
+		DRM_DEBUG_DRIVER("aux vip for hdmi\n");
+		/* select the input sync for hdmi = VTG set 1 */
+		writel(TVO_SYNC_AUX_VTG_SET_1, tvout->regs + TVO_HDMI_SYNC_SEL);
+	}
+
+	/* set color channel order */
+	tvout_vip_set_color_order(tvout,
+				  TVO_VIP_REORDER_CR_R_SEL,
+				  TVO_VIP_REORDER_Y_G_SEL,
+				  TVO_VIP_REORDER_CB_B_SEL);
+
+	/* set clipping mode (Limited range RGB/Y) */
+	tvout_vip_set_clip_mode(tvout, TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y);
+
+	/* set round mode (rounded to 8-bit per component) */
+	tvout_vip_set_rnd(tvout, TVO_VIP_RND_8BIT_ROUNDED);
+
+	if (of_device_is_compatible(node, "st,stih407-tvout")) {
+		/* set input video format */
+		tvout_vip_set_in_vid_fmt(tvout->regs + TVO_MAIN_IN_VID_FORMAT,
+					 TVO_IN_FMT_SIGNED);
+		sel_input_logic_inverted = true;
+	}
+
+	/* input selection */
+	tvout_vip_set_sel_input(tvout, main_path,
+			sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_RGB);
+}
+
+/**
+ * Prepare/configure VIP block for HDMI output
+ *
+ * @tvout: pointer on tvout structure
+ */
+static void tvout_hdmi_prepare(struct sti_tvout *tvout)
+{
+	dev_dbg(tvout->dev, "%s\n", __func__);
+
+	/* reset VIP register */
+	writel(0x00000000, tvout->regs + TVO_VIP_HDMI);
+}
+
+/**
+ * Disable HDMI VIP
+ *
+ * @tvout: pointer on tvout structure
+ */
+static void tvout_hdmi_stop(struct sti_tvout *tvout)
+{
+	dev_dbg(tvout->dev, "%s\n", __func__);
+
+	/* reset VIP register */
+	writel(0x00000000, tvout->regs + TVO_VIP_HDMI);
+}
+
+/**
+ * Start HDF VIP and HD DAC
+ *
+ * @tvout: pointer on tvout structure
+ * @main_path: true if main path has to be used in the vip configuration
+ *	  else aux path is used.
+ */
+static void tvout_hda_start(struct sti_tvout *tvout, bool main_path)
+{
+	struct device_node *node = tvout->dev->of_node;
+	bool sel_input_logic_inverted = false;
+
+	dev_dbg(tvout->dev, "%s\n", __func__);
+
+	if (!main_path) {
+		DRM_ERROR("HD Analog on aux not implemented\n");
+		return;
+	}
+
+	DRM_DEBUG_DRIVER("main vip for HDF\n");
+
+	/* set color channel order */
+	tvout_vip_set_color_order(tvout->regs + TVO_VIP_HDF,
+				  TVO_VIP_REORDER_CR_R_SEL,
+				  TVO_VIP_REORDER_Y_G_SEL,
+				  TVO_VIP_REORDER_CB_B_SEL);
+
+	/* set clipping mode (Limited range RGB/Y) */
+	tvout_vip_set_clip_mode(tvout->regs + TVO_VIP_HDF,
+				TVO_VIP_CLIP_LIMITED_RANGE_CB_CR);
+
+	/* set round mode (rounded to 10-bit per component) */
+	tvout_vip_set_rnd(tvout->regs + TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED);
+
+	if (of_device_is_compatible(node, "st,stih407-tvout")) {
+		/* set input video format */
+		tvout_vip_set_in_vid_fmt(tvout, TVO_IN_FMT_SIGNED);
+		sel_input_logic_inverted = true;
+	}
+
+	/* Input selection */
+	tvout_vip_set_sel_input(tvout->regs + TVO_VIP_HDF,
+				main_path,
+				sel_input_logic_inverted,
+				STI_TVOUT_VIDEO_OUT_YUV);
+
+	/* select the input sync for HD analog = VTG set 3
+	 * and HD DCS = VTG set 2 */
+	writel((TVO_SYNC_MAIN_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT) |
+	       TVO_SYNC_MAIN_VTG_SET_3, tvout->regs + TVO_HD_SYNC_SEL);
+
+	/* power up HD DAC */
+	writel(0, tvout->regs + TVO_HD_DAC_CFG_OFF);
+}
+
+/**
+ * Prepare/configure HDF VIP and HD DAC
+ *
+ * @tvout: pointer on tvout structure
+ */
+static void tvout_hda_prepare(struct sti_tvout *tvout)
+{
+	dev_dbg(tvout->dev, "%s\n", __func__);
+
+	/* reset VIP register */
+	writel(0x00000000, tvout->regs + TVO_VIP_HDF);
+
+	/* power down HD DAC */
+	writel(1, tvout->regs + TVO_HD_DAC_CFG_OFF);
+}
+
+/**
+ * Stop HDF VIP and HD DAC
+ *
+ * @tvout: pointer on tvout structure
+ */
+static void tvout_hda_stop(struct sti_tvout *tvout)
+{
+	dev_dbg(tvout->dev, "%s\n", __func__);
+
+	/* reset VIP register */
+	writel(0x00000000, tvout->regs + TVO_VIP_HDF);
+
+	/* power down HD DAC */
+	writel(1, tvout->regs + TVO_HD_DAC_CFG_OFF);
+}
+
+/**
+ * Forward drm display mode information to the connector
+ *
+ * @c: drm_connector
+ * @mode: mode to be set
+ * @main_path: indicated if the connector use the main or auxillary data path
+ *
+ * Return negative value if error occurs
+ */
+int sti_tvout_set_mode(struct drm_connector *c,
+		struct drm_display_mode *mode, bool main_path)
+{
+	struct sti_connector *connector = to_sti_connector(c);
+
+	if (connector)
+		if (connector->set_mode)
+			return connector->set_mode(c, mode, main_path);
+
+	return -EINVAL;
+}
+
+/**
+ * Prepare / initialize depending on the connector type
+ *
+ * @connector: drm_connector
+ *
+ * Return negative value if error occurs
+ */
+int sti_tvout_prepare(struct drm_connector *c)
+{
+	struct sti_connector *connector = to_sti_connector(c);
+	struct sti_tvout *tvout = dev_get_drvdata(connector->master);
+	int ret;
+
+	dev_dbg(tvout->dev, "%s\n", __func__);
+
+	if (connector)
+		if (connector->prepare)
+			connector->prepare(c);
+
+	switch (connector->type) {
+	case STI_CONNECTOR_HDMI:
+		tvout_hdmi_prepare(tvout);
+		ret = 0;
+		break;
+	case STI_CONNECTOR_HDA:
+		tvout_hda_prepare(tvout);
+		ret = 0;
+		break;
+	case STI_CONNECTOR_DVO:
+	case STI_CONNECTOR_DENC:
+	default:
+		/* not yet supported */
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+/**
+ * Commit / start depending on the connector type
+ *
+ * @tvout: pointer on tvout structure
+ * @type: type of connector
+ * @main_path: true if main path need to be use (false for aux path)
+ *
+ * Return negative value if error occurs
+ */
+int sti_tvout_commit(struct drm_connector *c, bool main_path)
+{
+	struct sti_connector *connector = to_sti_connector(c);
+	struct sti_tvout *tvout = dev_get_drvdata(connector->master);
+	int type = connector->type;
+	u32 matrix_offset;
+	unsigned int i;
+
+	dev_dbg(tvout->dev, "%s\n", __func__);
+
+	if (!connector)
+		return -EINVAL;
+
+	if (connector->start) {
+		if (connector->start(c)) {
+			DRM_ERROR("Unable to properly start connector\n");
+			return -EINVAL;
+		}
+	}
+
+	/* set preformatter matrix */
+	matrix_offset = main_path ? TVO_CSC_MAIN_M0 : TVO_CSC_AUX_M0;
+	for (i = 0; i < 8; i++)
+		writel(rgb_to_ycbcr_601[i],
+		       tvout->regs + matrix_offset + (i * 4));
+
+	switch (type) {
+	case STI_CONNECTOR_HDMI:
+		tvout_hdmi_start(tvout, main_path);
+		return 0;
+	case STI_CONNECTOR_HDA:
+		tvout_hda_start(tvout, main_path);
+		return 0;
+	case STI_CONNECTOR_DVO:
+	case STI_CONNECTOR_DENC:
+	default:
+		/* not yet supported */
+		return -EINVAL;
+	}
+}
+
+/**
+ * Disable / stop the tvout depending on the connector type
+ *
+ * @tvout: pointer on tvout structure
+ * @type: type of connector
+ */
+void sti_tvout_disable(struct drm_connector *c)
+{
+	struct sti_connector *connector = to_sti_connector(c);
+	struct sti_tvout *tvout = dev_get_drvdata(connector->master);
+	int type = connector->type;
+
+	switch (type) {
+	case STI_CONNECTOR_HDMI:
+		tvout_hdmi_stop(tvout);
+		break;
+	case STI_CONNECTOR_HDA:
+		tvout_hda_stop(tvout);
+		break;
+	case STI_CONNECTOR_DVO:
+	case STI_CONNECTOR_DENC:
+	default:
+		/* not yet supported */
+		return;
+	}
+
+	if (connector)
+		if (connector->stop)
+			connector->stop(c);
+}
+
+static int sti_tvout_bind(struct device *dev, struct device *master, void *data)
+{
+	return 0;
+}
+
+static void sti_tvout_unbind(struct device *dev, struct device *master,
+	void *data)
+{
+	/* do nothing */
+}
+
+static const struct component_ops sti_tvout_ops = {
+	.bind	= sti_tvout_bind,
+	.unbind	= sti_tvout_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+	struct device_node *np = data;
+
+	return dev->of_node == np;
+}
+
+static int sti_tvout_add_components(struct device *master, struct master *m)
+{
+	struct device_node *np	= master->of_node;
+	struct device_node *child_np;
+
+	child_np = of_get_next_available_child(np, NULL);
+
+	while (child_np) {
+		DRM_INFO("TVout add child %s\n", child_np->name);
+		component_master_add_child(m, compare_of, child_np);
+		of_node_put(child_np);
+		child_np = of_get_next_available_child(np, child_np);
+	}
+
+	return 0;
+}
+
+static int sti_tvout_master_bind(struct device *dev)
+{
+	return 0;
+}
+
+static void sti_tvout_master_unbind(struct device *dev)
+{
+	/* do nothing */
+}
+
+static const struct component_master_ops sti_tvout_master_ops = {
+	.add_components = sti_tvout_add_components,
+	.bind = sti_tvout_master_bind,
+	.unbind = sti_tvout_master_unbind,
+};
+
+static int sti_tvout_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct sti_tvout *tvout;
+	struct resource *res;
+
+	DRM_INFO("%s\n", __func__);
+
+	if (!node)
+		return -ENODEV;
+
+	tvout = devm_kzalloc(dev, sizeof(*tvout), GFP_KERNEL);
+	if (!tvout)
+		return -ENOMEM;
+
+	DRM_INFO("%s tvout %p\n", __func__, tvout);
+
+	tvout->dev = dev;
+	DRM_INFO("%s tvout->dev %p\n", __func__, tvout->dev);
+
+	/* get Memory ressources */
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg");
+	if (!res) {
+		DRM_ERROR("Invalid glue resource\n");
+		return -ENOMEM;
+	}
+	tvout->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
+	if (IS_ERR(tvout->regs))
+		return PTR_ERR(tvout->regs);
+
+	/* get reset resources */
+	tvout->reset = devm_reset_control_get(dev, "tvout");
+	/* take tvout out of reset */
+	if (!IS_ERR(tvout->reset))
+		reset_control_deassert(tvout->reset);
+
+	platform_set_drvdata(pdev, tvout);
+
+	of_platform_populate(node, NULL, NULL, dev);
+
+	component_master_add(&pdev->dev, &sti_tvout_master_ops);
+
+	return component_add(&pdev->dev, &sti_tvout_ops);
+}
+
+static int sti_tvout_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &sti_tvout_master_ops);
+	component_del(&pdev->dev, &sti_tvout_ops);
+	return 0;
+}
+
+static struct of_device_id tvout_of_match[] = {
+	{ .compatible = "st,stih416-tvout", },
+	{ .compatible = "st,stih407-tvout", },
+	{ /* end node */ }
+};
+MODULE_DEVICE_TABLE(of, tvout_of_match);
+
+struct platform_driver sti_tvout_driver = {
+	.driver = {
+		.name = "sti-tvout",
+		.owner = THIS_MODULE,
+		.of_match_table = tvout_of_match,
+	},
+	.probe = sti_tvout_probe,
+	.remove = sti_tvout_remove,
+};
+
+module_platform_driver(sti_tvout_driver);
diff --git a/drivers/gpu/drm/sti/sti_tvout.h b/drivers/gpu/drm/sti/sti_tvout.h
new file mode 100644
index 0000000..81dceff
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_tvout.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_TVOUT_H_
+#define _STI_TVOUT_H_
+
+#include <drm/drmP.h>
+
+int  sti_tvout_set_mode(struct drm_connector *c,
+		struct drm_display_mode *mode, bool main_path);
+int  sti_tvout_prepare(struct drm_connector *c);
+int  sti_tvout_commit(struct drm_connector *c, bool main_path);
+void sti_tvout_disable(struct drm_connector *c);
+
+#endif
-- 
1.9.1


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

* [PATCH v4 07/11] drm: sti: add GDP layer
  2014-05-29  6:36 [PATCH v4 00/11] Add DRM for stih4xx platforms Benjamin Gaignard
                   ` (5 preceding siblings ...)
  2014-05-29  6:37 ` [PATCH v4 06/11] drm: sti: add TVOut driver Benjamin Gaignard
@ 2014-05-29  6:37 ` Benjamin Gaignard
  2014-06-11 19:15   ` Rob Clark
  2014-05-29  6:37 ` [PATCH v4 08/11] drm: sti: add VID layer Benjamin Gaignard
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 19+ messages in thread
From: Benjamin Gaignard @ 2014-05-29  6:37 UTC (permalink / raw)
  To: dri-devel, linaro-mm-sig, linux-kernel, airlied
  Cc: lee.jones, Benjamin Gaignard

Generic Display Pipeline are one of the compositor input sub-devices.
GDP are dedicated to graphic input like RGB plans.
GDP is part of Compositor hardware block which will be introduce later.

A sti_layer structure is used to abstract GDP calls from Compositor.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
---
 drivers/gpu/drm/sti/Makefile    |   3 +-
 drivers/gpu/drm/sti/sti_gdp.c   | 482 ++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sti/sti_gdp.h   |  73 ++++++
 drivers/gpu/drm/sti/sti_layer.h | 111 +++++++++
 4 files changed, 668 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/sti/sti_gdp.c
 create mode 100644 drivers/gpu/drm/sti/sti_gdp.h
 create mode 100644 drivers/gpu/drm/sti/sti_layer.h

diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
index dcc9568..1745697 100644
--- a/drivers/gpu/drm/sti/Makefile
+++ b/drivers/gpu/drm/sti/Makefile
@@ -5,4 +5,5 @@ obj-$(CONFIG_DRM_STI) += \
 	sti_hdmi_tx3g0c55phy.o \
 	sti_hdmi_tx3g4c28phy.o \
 	sti_hda.o \
-	sti_tvout.o
\ No newline at end of file
+	sti_tvout.o \
+	sti_gdp.o
\ No newline at end of file
diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c
new file mode 100644
index 0000000..a014214
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_gdp.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+
+#include "sti_gdp.h"
+#include "sti_layer.h"
+#include "sti_vtg.h"
+
+#define ENA_COLOR_FILL  BIT(8)
+#define WAIT_NEXT_VSYNC BIT(31)
+
+/* GDP color formats */
+#define GDP_RGB565      0x00
+#define GDP_RGB888      0x01
+#define GDP_RGB888_32   0x02
+#define GDP_ARGB8565    0x04
+#define GDP_ARGB8888    0x05
+#define GDP_ARGB1555    0x06
+#define GDP_ARGB4444    0x07
+#define GDP_CLUT8       0x0B
+#define GDP_YCBR888     0x10
+#define GDP_YCBR422R    0x12
+#define GDP_AYCBR8888   0x15
+
+#define GAM_GDP_CTL_OFFSET      0x00
+#define GAM_GDP_AGC_OFFSET      0x04
+#define GAM_GDP_VPO_OFFSET      0x0C
+#define GAM_GDP_VPS_OFFSET      0x10
+#define GAM_GDP_PML_OFFSET      0x14
+#define GAM_GDP_PMP_OFFSET      0x18
+#define GAM_GDP_SIZE_OFFSET     0x1C
+#define GAM_GDP_NVN_OFFSET      0x24
+#define GAM_GDP_KEY1_OFFSET     0x28
+#define GAM_GDP_KEY2_OFFSET     0x2C
+#define GAM_GDP_PPT_OFFSET      0x34
+#define GAM_GDP_CML_OFFSET      0x3C
+#define GAM_GDP_MST_OFFSET      0x68
+
+#define GAM_GDP_ALPHARANGE_255  BIT(5)
+#define GAM_GDP_AGC_FULL_RANGE  0x00808080
+#define GAM_GDP_PPT_IGNORE      (BIT(1) | BIT(0))
+#define GAM_GDP_SIZE_MAX        0x7FF
+
+static const uint32_t gdp_supported_formats[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_ARGB4444,
+	DRM_FORMAT_ARGB1555,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_AYUV,
+	DRM_FORMAT_YUV444,
+	DRM_FORMAT_VYUY,
+	DRM_FORMAT_C8,
+};
+
+static const uint32_t *sti_gdp_get_formats(void)
+{
+	return gdp_supported_formats;
+}
+
+static unsigned int sti_gdp_get_nb_formats(void)
+{
+	return ARRAY_SIZE(gdp_supported_formats);
+}
+
+static int sti_gdp_fourcc2format(int fourcc)
+{
+	switch (fourcc) {
+	case DRM_FORMAT_XRGB8888:
+		return GDP_RGB888_32;
+	case DRM_FORMAT_ARGB8888:
+		return GDP_ARGB8888;
+	case DRM_FORMAT_ARGB4444:
+		return GDP_ARGB4444;
+	case DRM_FORMAT_ARGB1555:
+		return GDP_ARGB1555;
+	case DRM_FORMAT_RGB565:
+		return GDP_RGB565;
+	case DRM_FORMAT_RGB888:
+		return GDP_RGB888;
+	case DRM_FORMAT_AYUV:
+		return GDP_AYCBR8888;
+	case DRM_FORMAT_YUV444:
+		return GDP_YCBR888;
+	case DRM_FORMAT_VYUY:
+		return GDP_YCBR422R;
+	case DRM_FORMAT_C8:
+		return GDP_CLUT8;
+	}
+	return -1;
+}
+
+static int sti_gdp_get_alpharange(int format)
+{
+	switch (format) {
+	case GDP_ARGB8565:
+	case GDP_ARGB8888:
+	case GDP_AYCBR8888:
+		return GAM_GDP_ALPHARANGE_255;
+	}
+	return 0;
+}
+
+/**
+ * sti_gdp_get_free_nodes
+ * @layer: gdp layer
+ *
+ * Look for a GDP node list that is not currently read by the HW.
+ *
+ * RETURNS:
+ * Pointer to the free GDP node list
+ */
+static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_layer *layer)
+{
+	int hw_nvn;
+	void *virt_nvn;
+	struct sti_gdp *gdp = layer->gdp;
+	unsigned int i;
+
+	hw_nvn = readl(gdp->regs + GAM_GDP_NVN_OFFSET);
+	if (!hw_nvn)
+		goto end;
+
+	virt_nvn = dma_to_virt(gdp->dev, (dma_addr_t) hw_nvn);
+
+	for (i = 0; i < GDP_NODE_NB_BANK; i++)
+		if ((virt_nvn != gdp->node_list[i].btm_field) &&
+		    (virt_nvn != gdp->node_list[i].top_field))
+			return &gdp->node_list[i];
+
+end:
+	return &gdp->node_list[0];
+}
+
+/**
+ * sti_gdp_get_current_nodes
+ * @layer: GDP layer
+ *
+ * Look for GDP nodes that are currently read by the HW.
+ *
+ * RETURNS:
+ * Pointer to the current GDP node list
+ */
+static
+struct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_layer *layer)
+{
+	int hw_nvn;
+	void *virt_nvn;
+	struct sti_gdp *gdp = layer->gdp;
+	unsigned int i;
+
+	hw_nvn = readl(gdp->regs + GAM_GDP_NVN_OFFSET);
+	if (!hw_nvn)
+		goto end;
+
+	virt_nvn = dma_to_virt(gdp->dev, (dma_addr_t) hw_nvn);
+
+	for (i = 0; i < GDP_NODE_NB_BANK; i++)
+		if ((virt_nvn == gdp->node_list[i].btm_field) ||
+				(virt_nvn == gdp->node_list[i].top_field))
+			return &gdp->node_list[i];
+
+end:
+	return NULL;
+}
+
+/**
+ * sti_gdp_prepare_layer
+ * @lay: gdp layer
+ * @first_prepare: true if it is the first time this function is called
+ *
+ * Update the free GDP node list according to the layer properties.
+ *
+ * RETURNS:
+ * 0 on success.
+ */
+static int sti_gdp_prepare_layer(void *lay, bool first_prepare)
+{
+	struct sti_layer *layer = (struct sti_layer *)lay;
+	struct sti_gdp_node_list *list;
+	struct sti_gdp_node *top_field, *btm_field;
+	struct drm_display_mode *mode = layer->mode;
+	struct device *dev = layer->gdp->dev;
+	int format;
+	unsigned int depth, bpp;
+	int rate = mode->clock * 1000;
+	int res;
+	u32 ydo, xdo, yds, xds;
+
+	list = sti_gdp_get_free_nodes(layer);
+	top_field = list->top_field;
+	btm_field = list->btm_field;
+
+	/* Build the top field from layer params */
+	top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE;
+	top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC;
+	format = sti_gdp_fourcc2format(layer->format);
+	if (format == -1) {
+		DRM_ERROR("Format not supported by GDP %.4s\n",
+			  (char *)&layer->format);
+		return 1;
+	}
+	top_field->gam_gdp_ctl |= format;
+	top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format);
+	top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE;
+
+	/* pixel memory location */
+	drm_fb_get_bpp_depth(layer->format, &depth, &bpp);
+	top_field->gam_gdp_pml = (u32) layer->paddr + layer->offsets[0];
+	top_field->gam_gdp_pml += layer->src_x * (bpp >> 3);
+	top_field->gam_gdp_pml += layer->src_y * layer->pitches[0];
+
+	/* input parameters */
+	top_field->gam_gdp_pmp = layer->pitches[0];
+	top_field->gam_gdp_size =
+	    clamp_val(layer->src_h, 0, GAM_GDP_SIZE_MAX) << 16 |
+	    clamp_val(layer->src_w, 0, GAM_GDP_SIZE_MAX);
+
+	/* output parameters */
+	ydo = sti_vtg_get_line_number(*mode, layer->dst_y);
+	yds = sti_vtg_get_line_number(*mode, layer->dst_y + layer->dst_h - 1);
+	xdo = sti_vtg_get_pixel_number(*mode, layer->dst_x);
+	xds = sti_vtg_get_pixel_number(*mode, layer->dst_x + layer->dst_w - 1);
+	top_field->gam_gdp_vpo = (ydo << 16) | xdo;
+	top_field->gam_gdp_vps = (yds << 16) | xds;
+
+	/* Same content and chained together */
+	memcpy(btm_field, top_field, sizeof(*btm_field));
+	top_field->gam_gdp_nvn = virt_to_dma(dev, btm_field);
+	btm_field->gam_gdp_nvn = virt_to_dma(dev, top_field);
+
+	/* Interlaced mode */
+	if (layer->mode->flags & DRM_MODE_FLAG_INTERLACE)
+		btm_field->gam_gdp_pml = top_field->gam_gdp_pml +
+		    layer->pitches[0];
+
+	if (first_prepare) {
+		/* Set and enable gdp clock */
+		if (layer->gdp->clk_pix) {
+			res = clk_set_rate(layer->gdp->clk_pix, rate);
+			if (res < 0) {
+				DRM_ERROR("Cannot set rate (%dHz) for gdp\n",
+						rate);
+				return 1;
+			}
+
+			if (clk_prepare_enable(layer->gdp->clk_pix)) {
+				DRM_ERROR("Failed to prepare/enable gdp\n");
+				return 1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * sti_gdp_commit_layer
+ * @lay: gdp layer
+ *
+ * Update the NVN field of the 'right' field of the current GDP node (being
+ * used by the HW) with the address of the updated ('free') top field GDP node.
+ * - In interlaced mode the 'right' field is the bottom field as we update
+ *   frames starting from their top field
+ * - In progressive mode, we update both bottom and top fields which are
+ *   equal nodes.
+ * At the next VSYNC, the updated node list will be used by the HW.
+ *
+ * RETURNS:
+ * 0 on success.
+ */
+static int sti_gdp_commit_layer(void *lay)
+{
+	struct sti_layer *layer = (struct sti_layer *)lay;
+	struct sti_gdp_node_list *updated_list = sti_gdp_get_free_nodes(layer);
+	struct sti_gdp_node *updated_top_node = updated_list->top_field;
+	struct sti_gdp_node *updated_btm_node = updated_list->btm_field;
+	struct sti_gdp *gdp = layer->gdp;
+	u32 dma_updated_top = virt_to_dma(gdp->dev, updated_top_node);
+	u32 dma_updated_btm = virt_to_dma(gdp->dev, updated_btm_node);
+	struct sti_gdp_node_list *curr_list = sti_gdp_get_current_nodes(layer);
+
+	dev_dbg(gdp->dev, "Current NVN:0x%X\n",
+		readl(gdp->regs + GAM_GDP_NVN_OFFSET));
+	dev_dbg(gdp->dev, "Posted buff: %lx current buff: %x\n",
+		(unsigned long)layer->paddr,
+		readl(gdp->regs + GAM_GDP_PML_OFFSET));
+
+	if (curr_list == NULL) {
+		/* First update or invalid node should directly write in the
+		 * hw register */
+		writel(gdp->is_curr_top == true ?
+				dma_updated_btm : dma_updated_top,
+				gdp->regs + GAM_GDP_NVN_OFFSET);
+		return 0;
+	}
+
+	if (layer->mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		if (gdp->is_curr_top == true) {
+			/* Do not update in the middle of the frame, but
+			 * postpone the update after the bottom field has
+			 * been displayed */
+			curr_list->btm_field->gam_gdp_nvn = dma_updated_top;
+		} else {
+			/* Direct update to avoid one frame delay */
+			writel(dma_updated_top, gdp->regs + GAM_GDP_NVN_OFFSET);
+		}
+	} else {
+		/* Direct update for progressive to avoid one frame delay */
+		writel(dma_updated_top, gdp->regs + GAM_GDP_NVN_OFFSET);
+	}
+
+	return 0;
+}
+
+/**
+ * sti_gdp_disable_layer
+ * @lay: gdp layer
+ *
+ * Disable a GDP.
+ *
+ * RETURNS:
+ * 0 on success.
+ */
+static int sti_gdp_disable_layer(void *lay)
+{
+	unsigned int i;
+	struct sti_layer *layer = (struct sti_layer *)lay;
+	struct sti_gdp *gdp = layer->gdp;
+
+	/* Set the nodes as 'to be ignored on mixer' */
+	for (i = 0; i < GDP_NODE_NB_BANK; i++) {
+		gdp->node_list[i].top_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE;
+		gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE;
+	}
+
+	if (gdp->clk_pix)
+		clk_disable_unprepare(gdp->clk_pix);
+
+	return 0;
+}
+
+/**
+ * sti_gdp_field_cb
+ * @nb: notifier block
+ * @event: event message
+ * @data: private data
+ *
+ * Handle VTG top field and bottom field event.
+ *
+ * RETURNS:
+ * 0 on success.
+ */
+int sti_gdp_field_cb(struct notifier_block *nb,
+		unsigned long event, void *data)
+{
+	struct sti_gdp *gdp = container_of(nb, struct sti_gdp, vtg_field_nb);
+
+	switch (event) {
+	case VTG_TOP_FIELD_EVENT:
+		gdp->is_curr_top = true;
+		break;
+	case VTG_BOTTOM_FIELD_EVENT:
+		gdp->is_curr_top = false;
+		break;
+	default:
+		DRM_ERROR("unsupported event: %lu\n", event);
+		break;
+	}
+
+	return 0;
+}
+
+/**
+ * sti_gdp_create
+ * @dev:      device
+ * @id:       gdp id
+ * @baseaddr: IO addr
+ *
+ * Create a gdp object. Allocate memory and initialize parameters.
+ *
+ *
+ * RETURNS:
+ * Pointer to the created gdp object.
+ */
+struct sti_gdp *sti_gdp_create(struct device *dev, int id,
+			       void __iomem *baseaddr)
+{
+	struct sti_gdp *gdp;
+	struct device_node *np = dev->of_node;
+	dma_addr_t dma;
+	void *base;
+	unsigned int i, size;
+
+	gdp = devm_kzalloc(dev, sizeof(*gdp), GFP_KERNEL);
+	if (!gdp) {
+		DRM_ERROR("Failed to allocate memory for GDP\n");
+		return NULL;
+	}
+
+	/* Allocate all the nodes within a single memory page */
+	size = sizeof(struct sti_gdp_node) *
+	    GDP_NODE_PER_FIELD * GDP_NODE_NB_BANK;
+
+	base = dma_alloc_writecombine(dev, size, &dma, GFP_KERNEL | GFP_DMA);
+	if (!base) {
+		DRM_ERROR("Failed to allocate memory for GDP node\n");
+		goto mem_err;
+	}
+	memset(base, 0, size);
+
+	for (i = 0; i < GDP_NODE_NB_BANK; i++) {
+		if (virt_to_dma(dev, base) & 0xF) {
+			DRM_ERROR("Mem alignment failed\n");
+			goto mem_err;
+		}
+		gdp->node_list[i].top_field = base;
+		DRM_DEBUG_DRIVER("node[%d].top_field=%p\n", i, base);
+		base += sizeof(struct sti_gdp_node);
+
+		if (virt_to_dma(dev, base) & 0xF) {
+			DRM_ERROR("Mem alignment failed\n");
+			goto mem_err;
+		}
+		gdp->node_list[i].btm_field = base;
+		DRM_DEBUG_DRIVER("node[%d].btm_field=%p\n", i, base);
+		base += sizeof(struct sti_gdp_node);
+	}
+
+	gdp->dev = dev;
+	gdp->regs = baseaddr;
+	gdp->get_formats = sti_gdp_get_formats;
+	gdp->get_nb_formats = sti_gdp_get_nb_formats;
+	gdp->prepare = sti_gdp_prepare_layer;
+	gdp->commit = sti_gdp_commit_layer;
+	gdp->disable = sti_gdp_disable_layer;
+	gdp->vtg_field_nb.notifier_call = sti_gdp_field_cb;
+
+	if (of_device_is_compatible(np, "st,stih407-compositor")) {
+		/* GDP of STiH407 chip have its own pixel clock */
+		char *clk_name;
+
+		switch (id) {
+		case STI_GDP_0:
+			clk_name = "pix_gdp1";
+			break;
+		case STI_GDP_1:
+			clk_name = "pix_gdp2";
+			break;
+		case STI_GDP_2:
+			clk_name = "pix_gdp3";
+			break;
+		case STI_GDP_3:
+			clk_name = "pix_gdp4";
+			break;
+		default:
+			DRM_ERROR("GDP id not recognized\n");
+			goto err_clk_pix_gdp;
+		}
+
+		gdp->clk_pix = devm_clk_get(dev, clk_name);
+		if (IS_ERR(gdp->clk_pix)) {
+			DRM_ERROR("Cannot get %s clock\n", clk_name);
+			goto err_clk_pix_gdp;
+		}
+	}
+
+	return gdp;
+
+err_clk_pix_gdp:
+mem_err:
+	devm_kfree(dev, gdp);
+	return NULL;
+}
diff --git a/drivers/gpu/drm/sti/sti_gdp.h b/drivers/gpu/drm/sti/sti_gdp.h
new file mode 100644
index 0000000..3618039
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_gdp.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_GDP_H_
+#define _STI_GDP_H_
+
+#include <linux/types.h>
+
+#define GDP_NODE_NB_BANK	2
+#define GDP_NODE_PER_FIELD	2
+
+struct sti_gdp_node {
+	u32 gam_gdp_ctl;
+	u32 gam_gdp_agc;
+	u32 reserved1;
+	u32 gam_gdp_vpo;
+	u32 gam_gdp_vps;
+	u32 gam_gdp_pml;
+	u32 gam_gdp_pmp;
+	u32 gam_gdp_size;
+	u32 reserved2;
+	u32 gam_gdp_nvn;
+	u32 gam_gdp_key1;
+	u32 gam_gdp_key2;
+	u32 reserved3;
+	u32 gam_gdp_ppt;
+	u32 reserved4;
+	u32 gam_gdp_cml;
+};
+
+struct sti_gdp_node_list {
+	struct sti_gdp_node *top_field;
+	struct sti_gdp_node *btm_field;
+};
+
+/*
+ * STI GDP structure
+ *
+ * @device:		driver device
+ * @regs:               subdevice register
+ * @clk_pix:            pixel clock for the current gdp
+ * @vtg_field_nb:       callback for VTG FIELD (top or bottom) notification
+ * @is_curr_top:        true if the current node processed is the top field
+ * @get_formats:	get GDP supported formats
+ * @get_nb_formats:	get number of format supported
+ * @prepare:		prepare GDP before rendering
+ * @commit:		set GDP for rendering
+ * @disable:		disable GDP
+ * @node_list:		array of node list
+ */
+struct sti_gdp {
+	struct device *dev;
+	void __iomem *regs;
+	struct clk *clk_pix;
+	struct notifier_block vtg_field_nb;
+	bool is_curr_top;
+	const uint32_t* (*get_formats)(void);
+	unsigned int (*get_nb_formats)(void);
+	int (*prepare)(void *layer, bool first_prepare);
+	int (*commit)(void *layer);
+	int (*disable)(void *layer);
+	struct sti_gdp_node_list node_list[GDP_NODE_NB_BANK];
+};
+
+struct sti_gdp *sti_gdp_create(struct device *dev, int id,
+		void __iomem *baseaddr);
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_layer.h b/drivers/gpu/drm/sti/sti_layer.h
new file mode 100644
index 0000000..7ba5a40
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_layer.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_LAYER_H_
+#define _STI_LAYER_H_
+
+#include <drm/drmP.h>
+#include "sti_gdp.h"
+
+#define to_sti_layer(x) container_of(x, struct sti_layer, plane)
+
+#define STI_LAYER_TYPE_SHIFT 8
+#define STI_LAYER_TYPE_MASK (~((1<<STI_LAYER_TYPE_SHIFT)-1))
+
+
+enum sti_layer_type {
+	STI_GDP = 1 << STI_LAYER_TYPE_SHIFT,
+	STI_VID = 2 << STI_LAYER_TYPE_SHIFT,
+	STI_CUR = 3 << STI_LAYER_TYPE_SHIFT,
+	STI_BCK = 4 << STI_LAYER_TYPE_SHIFT
+};
+
+enum sti_layer_id_of_type {
+	STI_ID_0 = 0,
+	STI_ID_1 = 1,
+	STI_ID_2 = 2,
+	STI_ID_3 = 3
+};
+
+enum sti_layer_desc {
+	STI_GDP_0       = STI_GDP | STI_ID_0,
+	STI_GDP_1       = STI_GDP | STI_ID_1,
+	STI_GDP_2       = STI_GDP | STI_ID_2,
+	STI_GDP_3       = STI_GDP | STI_ID_3,
+	STI_VID_0       = STI_VID | STI_ID_0,
+	STI_VID_1       = STI_VID | STI_ID_1,
+	STI_CURSOR      = STI_CUR,
+	STI_BACK        = STI_BCK
+};
+
+struct sti_fps_info {
+	bool output;
+	unsigned int curr_frame_counter;
+	unsigned int last_frame_counter;
+	struct timespec last_timestamp;
+};
+
+/*
+ * STI layer structure
+ *
+ * @plane:              drm plane it is bound to (if any)
+ * @fb:                 drm fb it is bound to
+ * @mode:               display mode
+ * @desc:               layer type & id
+ * @zorder:             layer z-order
+ * @mixer_id:           id of the mixer used to display the layer
+ * @enabled:            to know if the layer is active or not
+ * @src_x src_y:        coordinates of the input (fb) area
+ * @src_w src_h:        size of the input (fb) area
+ * @dst_x dst_y:        coordinates of the output (crtc) area
+ * @dst_w dst_h:        size of the output (crtc) area
+ * @format:             format
+ * @pitches:            pitch of 'planes' (eg: Y, U, V)
+ * @offsets:            offset of 'planes'
+ * @paddr:              physical address of the input buffer
+ * @fps_info:           frame per second info
+ * @gdp:                related GDP (if the layer is a GDP)
+ */
+struct sti_layer {
+	struct drm_plane plane;
+	struct drm_framebuffer *fb;
+	struct drm_display_mode *mode;
+	enum sti_layer_desc desc;
+	int zorder;
+	int mixer_id;
+	bool enabled;
+	int src_x, src_y;
+	int src_w, src_h;
+	int dst_x, dst_y;
+	int dst_w, dst_h;
+	uint32_t format;
+	unsigned int pitches[4];
+	unsigned int offsets[4];
+	dma_addr_t paddr;
+	struct sti_fps_info fps_info;
+	struct sti_gdp *gdp;
+};
+
+struct sti_layer *sti_layer_create(struct device *dev, int desc,
+			void __iomem *baseaddr);
+int sti_layer_prepare(struct sti_layer *layer, struct drm_framebuffer *fb,
+			struct drm_display_mode *mode,
+			int mixer_id,
+			int dest_x, int dest_y,
+			int dest_w, int dest_h,
+			int src_x, int src_y,
+			int src_w, int src_h);
+int sti_layer_commit(struct sti_layer *layer);
+int sti_layer_disable(struct sti_layer *layer);
+const uint32_t *sti_layer_get_formats(struct sti_layer *layer);
+unsigned int sti_layer_get_nb_formats(struct sti_layer *layer);
+struct sti_layer *sti_layer_find_layer(struct sti_layer *layer[],
+			enum sti_layer_desc desc);
+const char *sti_layer_to_str(struct sti_layer *layer);
+
+#endif
-- 
1.9.1


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

* [PATCH v4 08/11] drm: sti: add VID layer
  2014-05-29  6:36 [PATCH v4 00/11] Add DRM for stih4xx platforms Benjamin Gaignard
                   ` (6 preceding siblings ...)
  2014-05-29  6:37 ` [PATCH v4 07/11] drm: sti: add GDP layer Benjamin Gaignard
@ 2014-05-29  6:37 ` Benjamin Gaignard
  2014-05-29  6:37 ` [PATCH v4 09/11] drm: sti: add Mixer Benjamin Gaignard
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Benjamin Gaignard @ 2014-05-29  6:37 UTC (permalink / raw)
  To: dri-devel, linaro-mm-sig, linux-kernel, airlied
  Cc: lee.jones, Benjamin Gaignard

VIDeo plug are one of the compositor input sub-devices.
VID are dedicated to video inputs like YUV plans.

Like GDP, VID are part of Compositor hardware block
and use sti_layer structure to provide an abstraction for
Compositor calls.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
---
 drivers/gpu/drm/sti/Makefile    |   3 +-
 drivers/gpu/drm/sti/sti_layer.h |   3 +
 drivers/gpu/drm/sti/sti_vid.c   | 138 ++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sti/sti_vid.h   |  33 ++++++++++
 4 files changed, 176 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/sti/sti_vid.c
 create mode 100644 drivers/gpu/drm/sti/sti_vid.h

diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
index 1745697..bb0f6b3 100644
--- a/drivers/gpu/drm/sti/Makefile
+++ b/drivers/gpu/drm/sti/Makefile
@@ -6,4 +6,5 @@ obj-$(CONFIG_DRM_STI) += \
 	sti_hdmi_tx3g4c28phy.o \
 	sti_hda.o \
 	sti_tvout.o \
-	sti_gdp.o
\ No newline at end of file
+	sti_gdp.o \
+	sti_vid.o
\ No newline at end of file
diff --git a/drivers/gpu/drm/sti/sti_layer.h b/drivers/gpu/drm/sti/sti_layer.h
index 7ba5a40..c8a159d 100644
--- a/drivers/gpu/drm/sti/sti_layer.h
+++ b/drivers/gpu/drm/sti/sti_layer.h
@@ -11,6 +11,7 @@
 
 #include <drm/drmP.h>
 #include "sti_gdp.h"
+#include "sti_vid.h"
 
 #define to_sti_layer(x) container_of(x, struct sti_layer, plane)
 
@@ -70,6 +71,7 @@ struct sti_fps_info {
  * @paddr:              physical address of the input buffer
  * @fps_info:           frame per second info
  * @gdp:                related GDP (if the layer is a GDP)
+ * @vid:                related VID (if the layer is a VID/VDP)
  */
 struct sti_layer {
 	struct drm_plane plane;
@@ -89,6 +91,7 @@ struct sti_layer {
 	dma_addr_t paddr;
 	struct sti_fps_info fps_info;
 	struct sti_gdp *gdp;
+	struct sti_vid *vid;
 };
 
 struct sti_layer *sti_layer_create(struct device *dev, int desc,
diff --git a/drivers/gpu/drm/sti/sti_vid.c b/drivers/gpu/drm/sti/sti_vid.c
new file mode 100644
index 0000000..0197291
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vid.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <drm/drmP.h>
+
+#include "sti_layer.h"
+#include "sti_vid.h"
+#include "sti_vtg.h"
+
+/* Registers */
+#define VID_CTL                 0x00
+#define VID_ALP                 0x04
+#define VID_CLF                 0x08
+#define VID_VPO                 0x0C
+#define VID_VPS                 0x10
+#define VID_KEY1                0x28
+#define VID_KEY2                0x2C
+#define VID_MPR0                0x30
+#define VID_MPR1                0x34
+#define VID_MPR2                0x38
+#define VID_MPR3                0x3C
+#define VID_MST                 0x68
+#define VID_BC                  0x70
+#define VID_TINT                0x74
+#define VID_CSAT                0x78
+
+/* Registers values */
+#define VID_CTL_IGNORE          (BIT(31) | BIT(30))
+#define VID_CTL_PSI_ENABLE      (BIT(2) | BIT(1) | BIT(0))
+#define VID_ALP_OPAQUE          0x00000080
+#define VID_BC_DFLT             0x00008000
+#define VID_TINT_DFLT           0x00000000
+#define VID_CSAT_DFLT           0x00000080
+/* YCbCr to RGB BT709:
+ * R = Y+1.5391Cr
+ * G = Y-0.4590Cr-0.1826Cb
+ * B = Y+1.8125Cb */
+#define VID_MPR0_BT709          0x0A800000
+#define VID_MPR1_BT709          0x0AC50000
+#define VID_MPR2_BT709          0x07150545
+#define VID_MPR3_BT709          0x00000AE8
+
+static int sti_vid_prepare_layer(void *lay, bool first_prepare)
+{
+	u32 val;
+	struct sti_layer *layer = (struct sti_layer *)lay;
+	struct sti_vid *vid = layer->vid;
+
+	/* Unmask */
+	val = readl(vid->regs + VID_CTL);
+	val &= ~VID_CTL_IGNORE;
+	writel(val, vid->regs + VID_CTL);
+
+	return 0;
+}
+
+static int sti_vid_commit_layer(void *lay)
+{
+	struct sti_layer *layer = (struct sti_layer *)lay;
+	struct sti_vid *vid = layer->vid;
+	struct drm_display_mode *mode = layer->mode;
+	u32 ydo, xdo, yds, xds;
+
+	ydo = sti_vtg_get_line_number(*mode, layer->dst_y);
+	yds = sti_vtg_get_line_number(*mode, layer->dst_y + layer->dst_h - 1);
+	xdo = sti_vtg_get_pixel_number(*mode, layer->dst_x);
+	xds = sti_vtg_get_pixel_number(*mode, layer->dst_x + layer->dst_w - 1);
+
+	writel((ydo << 16) | xdo, vid->regs + VID_VPO);
+	writel((yds << 16) | xds, vid->regs + VID_VPS);
+
+	return 0;
+}
+
+static int sti_vid_disable_layer(void *lay)
+{
+	u32 val;
+	struct sti_layer *layer = (struct sti_layer *)lay;
+	struct sti_vid *vid = layer->vid;
+
+	/* Mask */
+	val = readl(vid->regs + VID_CTL);
+	val |= VID_CTL_IGNORE;
+	writel(val, vid->regs + VID_CTL);
+
+	return 0;
+}
+
+static void sti_vid_set_default(struct sti_vid *vid)
+{
+	/* Enable PSI, Mask layer */
+	writel(VID_CTL_PSI_ENABLE | VID_CTL_IGNORE, vid->regs + VID_CTL);
+
+	/* Opaque */
+	writel(VID_ALP_OPAQUE, vid->regs + VID_ALP);
+
+	/* Color conversion parameters */
+	writel(VID_MPR0_BT709, vid->regs + VID_MPR0);
+	writel(VID_MPR1_BT709, vid->regs + VID_MPR1);
+	writel(VID_MPR2_BT709, vid->regs + VID_MPR2);
+	writel(VID_MPR3_BT709, vid->regs + VID_MPR3);
+
+	/* Brightness, contrast, tint, saturation */
+	writel(VID_BC_DFLT, vid->regs + VID_BC);
+	writel(VID_TINT_DFLT, vid->regs + VID_TINT);
+	writel(VID_CSAT_DFLT, vid->regs + VID_CSAT);
+}
+
+struct sti_vid *sti_vid_create(struct device *dev, void __iomem *baseaddr)
+{
+	struct sti_vid *vid;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	vid = devm_kzalloc(dev, sizeof(*vid), GFP_KERNEL);
+	if (!vid) {
+		DRM_ERROR("Failed to allocate memory for VID\n");
+		return NULL;
+	}
+
+	vid->dev = dev;
+	vid->regs = baseaddr;
+	vid->prepare = sti_vid_prepare_layer;
+	vid->commit = sti_vid_commit_layer;
+	vid->disable = sti_vid_disable_layer;
+	/* As the VID input is HW-mapped to the VDP output, the supported
+	 * formats are under the VDP control */
+	vid->get_formats = NULL;
+	vid->get_nb_formats = NULL;
+
+	/* Set default configuration (static) */
+	sti_vid_set_default(vid);
+
+	return vid;
+}
diff --git a/drivers/gpu/drm/sti/sti_vid.h b/drivers/gpu/drm/sti/sti_vid.h
new file mode 100644
index 0000000..4a502b0
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_vid.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_VID_H_
+#define _STI_VID_H_
+
+/*
+ * STI VID structure
+ *
+ * @device:		driver device
+ * @regs:		subdevice register
+ * @get_formats:	get VID supported formats
+ * @get_nb_formats:	get number of format supported
+ * @prepare:		prepare VID before rendering
+ * @commit:		set VID for rendering
+ * @disable:		disable VID
+ */
+struct sti_vid {
+	struct device *dev;
+	void __iomem *regs;
+	const uint32_t* (*get_formats)(void);
+	unsigned int (*get_nb_formats)(void);
+	int (*prepare)(void *layer, bool first_prepare);
+	int (*commit)(void *layer);
+	int (*disable)(void *layer);
+};
+
+struct sti_vid *sti_vid_create(struct device *dev, void __iomem *baseaddr);
+
+#endif
-- 
1.9.1


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

* [PATCH v4 09/11] drm: sti: add Mixer
  2014-05-29  6:36 [PATCH v4 00/11] Add DRM for stih4xx platforms Benjamin Gaignard
                   ` (7 preceding siblings ...)
  2014-05-29  6:37 ` [PATCH v4 08/11] drm: sti: add VID layer Benjamin Gaignard
@ 2014-05-29  6:37 ` Benjamin Gaignard
  2014-06-11 19:04   ` Rob Clark
  2014-05-29  6:37 ` [PATCH v4 10/11] drm: sti: add Compositor Benjamin Gaignard
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 19+ messages in thread
From: Benjamin Gaignard @ 2014-05-29  6:37 UTC (permalink / raw)
  To: dri-devel, linaro-mm-sig, linux-kernel, airlied
  Cc: lee.jones, Benjamin Gaignard

Mixer hardware IP is responsible of mixing the different inputs layers.
Z-order is managed by the mixer.
We could 2 mixers: one for main path and one for auxillary path

Mixers are part of Compositor hardware block

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
---
 drivers/gpu/drm/sti/Makefile    |   1 +
 drivers/gpu/drm/sti/sti_mixer.c | 243 ++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sti/sti_mixer.h |  52 +++++++++
 3 files changed, 296 insertions(+)
 create mode 100644 drivers/gpu/drm/sti/sti_mixer.c
 create mode 100644 drivers/gpu/drm/sti/sti_mixer.h

diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
index bb0f6b3..ce03e15 100644
--- a/drivers/gpu/drm/sti/Makefile
+++ b/drivers/gpu/drm/sti/Makefile
@@ -6,5 +6,6 @@ obj-$(CONFIG_DRM_STI) += \
 	sti_hdmi_tx3g4c28phy.o \
 	sti_hda.o \
 	sti_tvout.o \
+	sti_mixer.o \
 	sti_gdp.o \
 	sti_vid.o
\ No newline at end of file
diff --git a/drivers/gpu/drm/sti/sti_mixer.c b/drivers/gpu/drm/sti/sti_mixer.c
new file mode 100644
index 0000000..c812c68
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_mixer.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include "sti_mixer.h"
+#include "sti_vtg.h"
+
+/* Identity: G=Y , B=Cb , R=Cr */
+static const u32 MixerColorSpaceMatIdentity[] = {
+	0x10000000, 0x00000000, 0x10000000, 0x00001000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+/* regs offset */
+#define GAM_MIXER_CTL      0x00
+#define GAM_MIXER_BKC      0x04
+#define GAM_MIXER_BCO      0x0C
+#define GAM_MIXER_BCS      0x10
+#define GAM_MIXER_AVO      0x28
+#define GAM_MIXER_AVS      0x2C
+#define GAM_MIXER_CRB      0x34
+#define GAM_MIXER_ACT      0x38
+#define GAM_MIXER_MBP      0x3C
+#define GAM_MIXER_MX0      0x80
+
+/* id for depth of CRB reg */
+#define GAM_DEPTH_VID0_ID  1
+#define GAM_DEPTH_VID1_ID  2
+#define GAM_DEPTH_GDP0_ID  3
+#define GAM_DEPTH_GDP1_ID  4
+#define GAM_DEPTH_GDP2_ID  5
+#define GAM_DEPTH_GDP3_ID  6
+#define GAM_DEPTH_MASK_ID  7
+
+/* mask in CTL reg */
+#define GAM_CTL_BACK_MASK  BIT(0)
+#define GAM_CTL_VID0_MASK  BIT(1)
+#define GAM_CTL_VID1_MASK  BIT(2)
+#define GAM_CTL_GDP0_MASK  BIT(3)
+#define GAM_CTL_GDP1_MASK  BIT(4)
+#define GAM_CTL_GDP2_MASK  BIT(5)
+#define GAM_CTL_GDP3_MASK  BIT(6)
+
+const char *sti_mixer_to_str(struct sti_mixer *mixer)
+{
+	switch (mixer->id) {
+	case STI_MIXER_MAIN:
+		return "MAIN_MIXER";
+	case STI_MIXER_AUX:
+		return "AUX_MIXER";
+	default:
+		return "<UNKNOWN MIXER>";
+	}
+}
+
+static inline u32 sti_mixer_reg_read(struct sti_mixer *mixer, u32 reg_id)
+{
+	return readl(mixer->regs + reg_id);
+}
+
+static inline void sti_mixer_reg_write(struct sti_mixer *mixer,
+				       u32 reg_id, u32 val)
+{
+	writel(val, mixer->regs + reg_id);
+}
+
+void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable)
+{
+	u32 val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL);
+
+	val &= ~GAM_CTL_BACK_MASK;
+	val |= enable;
+	sti_mixer_reg_write(mixer, GAM_MIXER_CTL, val);
+}
+
+static void sti_mixer_set_background_color(struct sti_mixer *mixer,
+					   u8 red, u8 green, u8 blue)
+{
+	u32 val = (red << 16) | (green << 8) | blue;
+
+	sti_mixer_reg_write(mixer, GAM_MIXER_BKC, val);
+}
+
+static void sti_mixer_set_background_area(struct sti_mixer *mixer,
+					  struct drm_display_mode *mode)
+{
+	u32 ydo, xdo, yds, xds;
+
+	ydo = sti_vtg_get_line_number(*mode, 0);
+	yds = sti_vtg_get_line_number(*mode, mode->vdisplay - 1);
+	xdo = sti_vtg_get_pixel_number(*mode, 0);
+	xds = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1);
+
+	sti_mixer_reg_write(mixer, GAM_MIXER_BCO, ydo << 16 | xdo);
+	sti_mixer_reg_write(mixer, GAM_MIXER_BCS, yds << 16 | xds);
+}
+
+int sti_mixer_set_layer_depth(struct sti_mixer *mixer, struct sti_layer *layer)
+{
+	int layer_id = 0, depth = layer->zorder;
+	u32 mask, val;
+
+	if (depth >= GAM_MIXER_NB_DEPTH_LEVEL)
+		return 1;
+
+	switch (layer->desc) {
+	case STI_GDP_0:
+		layer_id = GAM_DEPTH_GDP0_ID;
+		break;
+	case STI_GDP_1:
+		layer_id = GAM_DEPTH_GDP1_ID;
+		break;
+	case STI_GDP_2:
+		layer_id = GAM_DEPTH_GDP2_ID;
+		break;
+	case STI_GDP_3:
+		layer_id = GAM_DEPTH_GDP3_ID;
+		break;
+	case STI_VID_0:
+		layer_id = GAM_DEPTH_VID0_ID;
+		break;
+	case STI_VID_1:
+		layer_id = GAM_DEPTH_VID1_ID;
+		break;
+	default:
+		DRM_ERROR("Unknown layer %d\n", layer->desc);
+		return 1;
+	}
+	mask = GAM_DEPTH_MASK_ID << (3 * depth);
+	layer_id = layer_id << (3 * depth);
+
+	dev_dbg(mixer->dev, "GAM_MIXER_CRB val 0x%x mask 0x%x\n",
+		layer_id, mask);
+
+	val = sti_mixer_reg_read(mixer, GAM_MIXER_CRB);
+	val &= ~mask;
+	val |= layer_id;
+	sti_mixer_reg_write(mixer, GAM_MIXER_CRB, val);
+
+	dev_dbg(mixer->dev, "Read GAM_MIXER_CRB 0x%x\n",
+		sti_mixer_reg_read(mixer, GAM_MIXER_CRB));
+	return 0;
+}
+
+int sti_mixer_active_video_area(struct sti_mixer *mixer,
+				struct drm_display_mode *mode)
+{
+	u32 ydo, xdo, yds, xds;
+
+	ydo = sti_vtg_get_line_number(*mode, 0);
+	yds = sti_vtg_get_line_number(*mode, mode->vdisplay - 1);
+	xdo = sti_vtg_get_pixel_number(*mode, 0);
+	xds = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1);
+
+	DRM_DEBUG_DRIVER("%s active video area xdo:%d ydo:%d xds:%d yds:%d\n",
+			 sti_mixer_to_str(mixer), xdo, ydo, xds, yds);
+	sti_mixer_reg_write(mixer, GAM_MIXER_AVO, ydo << 16 | xdo);
+	sti_mixer_reg_write(mixer, GAM_MIXER_AVS, yds << 16 | xds);
+
+	sti_mixer_set_background_color(mixer, 0xFF, 0, 0);
+
+	sti_mixer_set_background_area(mixer, mode);
+	sti_mixer_set_background_status(mixer, true);
+	return 0;
+}
+
+static u32 sti_mixer_get_layer_mask(struct sti_layer *layer)
+{
+	switch (layer->desc) {
+	case STI_BACK:
+		return GAM_CTL_BACK_MASK;
+	case STI_GDP_0:
+		return GAM_CTL_GDP0_MASK;
+	case STI_GDP_1:
+		return GAM_CTL_GDP1_MASK;
+	case STI_GDP_2:
+		return GAM_CTL_GDP2_MASK;
+	case STI_GDP_3:
+		return GAM_CTL_GDP3_MASK;
+	case STI_VID_0:
+		return GAM_CTL_VID0_MASK;
+	case STI_VID_1:
+		return GAM_CTL_VID1_MASK;
+	default:
+		return 0;
+	}
+}
+
+int sti_mixer_set_layer_status(struct sti_mixer *mixer,
+			       struct sti_layer *layer, bool status)
+{
+	u32 mask, val;
+
+	mask = sti_mixer_get_layer_mask(layer);
+	if (!mask) {
+		DRM_ERROR("Can not find layer mask\n");
+		return -EINVAL;
+	}
+
+	val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL);
+	val &= ~mask;
+	val |= status ? mask : 0;
+	sti_mixer_reg_write(mixer, GAM_MIXER_CTL, val);
+
+	return 0;
+}
+
+void sti_mixer_set_matrix(struct sti_mixer *mixer)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(MixerColorSpaceMatIdentity); i++)
+		sti_mixer_reg_write(mixer, GAM_MIXER_MX0 + (i * 4),
+				    MixerColorSpaceMatIdentity[i]);
+}
+
+struct sti_mixer *sti_mixer_create(struct device *dev, int id,
+				   void __iomem *baseaddr)
+{
+	struct sti_mixer *mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
+	struct device_node *np = dev->of_node;
+
+	dev_dbg(dev, "%s\n", __func__);
+	if (!mixer) {
+		DRM_ERROR("Failed to allocated memory for mixer\n");
+		return NULL;
+	}
+	mixer->regs = baseaddr;
+	mixer->dev = dev;
+	mixer->id = id;
+
+	if (of_device_is_compatible(np, "st,stih416-compositor"))
+		sti_mixer_set_matrix(mixer);
+
+	DRM_DEBUG_DRIVER("%s created. Regs=%p\n",
+			 sti_mixer_to_str(mixer), mixer->regs);
+
+	return mixer;
+}
diff --git a/drivers/gpu/drm/sti/sti_mixer.h b/drivers/gpu/drm/sti/sti_mixer.h
new file mode 100644
index 0000000..2d86378
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_mixer.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_MIXER_H_
+#define _STI_MIXER_H_
+
+#include <drm/drmP.h>
+
+#include "sti_layer.h"
+
+#define to_sti_mixer(x) container_of(x, struct sti_mixer, drm_crtc)
+
+/**
+ * STI Mixer subdevice structure
+ *
+ * @dev: driver device
+ * @regs: mixer registers
+ * @id: id of the mixer
+ * @drm_crtc: crtc object link to the mixer
+ */
+struct sti_mixer {
+	struct device *dev;
+	void __iomem *regs;
+	int id;
+	struct drm_crtc	drm_crtc;
+};
+
+const char *sti_mixer_to_str(struct sti_mixer *mixer);
+
+struct sti_mixer *sti_mixer_create(struct device *dev, int id,
+		void __iomem *baseaddr);
+
+int sti_mixer_set_layer_status(struct sti_mixer *mixer,
+		struct sti_layer *layer, bool status);
+int sti_mixer_set_layer_depth(struct sti_mixer *mixer, struct sti_layer *layer);
+int sti_mixer_active_video_area(struct sti_mixer *mixer,
+		struct drm_display_mode *mode);
+
+void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable);
+
+/* depth in Cross-bar control = z order */
+#define GAM_MIXER_NB_DEPTH_LEVEL 7
+
+#define STI_MIXER_MAIN 0
+#define STI_MIXER_AUX  1
+
+#endif
-- 
1.9.1


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

* [PATCH v4 10/11] drm: sti: add Compositor
  2014-05-29  6:36 [PATCH v4 00/11] Add DRM for stih4xx platforms Benjamin Gaignard
                   ` (8 preceding siblings ...)
  2014-05-29  6:37 ` [PATCH v4 09/11] drm: sti: add Mixer Benjamin Gaignard
@ 2014-05-29  6:37 ` Benjamin Gaignard
  2014-06-11 18:59   ` Rob Clark
  2014-05-29  6:37 ` [PATCH v4 11/11] drm: sti: Add DRM driver itself Benjamin Gaignard
  2014-06-11 18:37 ` [PATCH v4 00/11] Add DRM for stih4xx platforms Rob Clark
  11 siblings, 1 reply; 19+ messages in thread
From: Benjamin Gaignard @ 2014-05-29  6:37 UTC (permalink / raw)
  To: dri-devel, linaro-mm-sig, linux-kernel, airlied
  Cc: lee.jones, Benjamin Gaignard

Compositor control all the input sub-device (VID, GDP)
and the mixer(s).
It is the main entry point for composition.
Layer interface is used to control the abstracted layers.

Add debug in mixer, GDP and VID

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
---
 drivers/gpu/drm/sti/Kconfig          |   1 +
 drivers/gpu/drm/sti/Makefile         |   2 +
 drivers/gpu/drm/sti/sti_compositor.c | 220 ++++++++++++++++++++++++
 drivers/gpu/drm/sti/sti_compositor.h |  90 ++++++++++
 drivers/gpu/drm/sti/sti_gdp.c        |  30 ++++
 drivers/gpu/drm/sti/sti_layer.c      | 312 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sti/sti_mixer.c      |   6 +
 drivers/gpu/drm/sti/sti_vid.c        |   7 +
 8 files changed, 668 insertions(+)
 create mode 100644 drivers/gpu/drm/sti/sti_compositor.c
 create mode 100644 drivers/gpu/drm/sti/sti_compositor.h
 create mode 100644 drivers/gpu/drm/sti/sti_layer.c

diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig
index cbd664b..1013570 100644
--- a/drivers/gpu/drm/sti/Kconfig
+++ b/drivers/gpu/drm/sti/Kconfig
@@ -1,5 +1,6 @@
 config DRM_STI
 	bool "DRM Support for STMicroelectronics SoC stiH41x Series"
 	depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM)
+	select DRM_KMS_CMA_HELPER
 	help
 	  Choose this option to enable DRM on STM stiH41x chipset
diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
index ce03e15..a0612cc 100644
--- a/drivers/gpu/drm/sti/Makefile
+++ b/drivers/gpu/drm/sti/Makefile
@@ -1,11 +1,13 @@
 obj-$(CONFIG_DRM_STI) += \
 	sti_vtg.o \
 	sti_vtac.o \
+	sti_compositor.o \
 	sti_hdmi.o \
 	sti_hdmi_tx3g0c55phy.o \
 	sti_hdmi_tx3g4c28phy.o \
 	sti_hda.o \
 	sti_tvout.o \
+	sti_layer.o \
 	sti_mixer.o \
 	sti_gdp.o \
 	sti_vid.o
\ No newline at end of file
diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c
new file mode 100644
index 0000000..3100917
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_compositor.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include <drm/drmP.h>
+
+#include "sti_compositor.h"
+#include "sti_gdp.h"
+#include "sti_vtg.h"
+
+/*
+ * stiH407 compositor properties
+ */
+struct sti_compositor_data stih407_compositor_data = {
+	.nb_subdev = 6,
+	.subdev_desc = {
+			{STI_GPD_SUBDEV, (int)STI_GDP_0, 0x100},
+			{STI_GPD_SUBDEV, (int)STI_GDP_1, 0x200},
+			{STI_GPD_SUBDEV, (int)STI_GDP_2, 0x300},
+			{STI_GPD_SUBDEV, (int)STI_GDP_3, 0x400},
+			{STI_VID_SUBDEV, (int)STI_VID_0, 0x700},
+			{STI_MIXER_MAIN_SUBDEV, STI_MIXER_MAIN, 0xC00}
+	},
+};
+
+/*
+ * stiH416 compositor properties
+ * Note:
+ * on stih416 MIXER_AUX has a different base address from MIXER_MAIN
+ * Moreover, GDPx is different for Main and Aux Mixer. So this subdev map does
+ * not fit for stiH416 if we want to enable the MIXER_AUX.
+ */
+struct sti_compositor_data stih416_compositor_data = {
+	.nb_subdev = 3,
+	.subdev_desc = {
+			{STI_GPD_SUBDEV, (int)STI_GDP_0, 0x100},
+			{STI_GPD_SUBDEV, (int)STI_GDP_1, 0x200},
+			{STI_MIXER_MAIN_SUBDEV, STI_MIXER_MAIN, 0xC00}
+	},
+};
+
+static int sti_compositor_init_subdev(struct sti_compositor *compo,
+		struct sti_compositor_subdev_descriptor *desc,
+		unsigned int array_size)
+{
+	unsigned int i, mixer_id = 0, layer_id = 0;
+
+	for (i = 0; i < array_size; i++) {
+		switch (desc[i].type) {
+		case STI_MIXER_MAIN_SUBDEV:
+		case STI_MIXER_AUX_SUBDEV:
+			compo->mixer[mixer_id++] =
+			    sti_mixer_create(compo->dev, desc[i].id,
+					     compo->regs + desc[i].offset);
+			break;
+		case STI_GPD_SUBDEV:
+		case STI_VID_SUBDEV:
+			compo->layer[layer_id++] =
+			    sti_layer_create(compo->dev, desc[i].id,
+					     compo->regs + desc[i].offset);
+			break;
+			/* case STI_CURSOR_SUBDEV : TODO */
+		default:
+			DRM_ERROR("Unknow subdev compoment type\n");
+			return 1;
+		}
+
+	}
+	compo->nb_mixers = mixer_id;
+	compo->nb_layers = layer_id;
+
+	return 0;
+}
+
+static int sti_compositor_bind(struct device *dev, struct device *master,
+	void *data)
+{
+	/* to be filled with drm driver functions */
+	return 0;
+}
+
+static void sti_compositor_unbind(struct device *dev, struct device *master,
+	void *data)
+{
+	/* do nothing */
+}
+
+static const struct component_ops sti_compositor_ops = {
+	.bind	= sti_compositor_bind,
+	.unbind	= sti_compositor_unbind,
+};
+
+static const struct of_device_id compositor_of_match[] = {
+	{
+		.compatible = "st,stih416-compositor",
+		.data = &stih416_compositor_data,
+	}, {
+		.compatible = "st,stih407-compositor",
+		.data = &stih407_compositor_data,
+	}, {
+		/* end node */
+	}
+};
+MODULE_DEVICE_TABLE(of, compositor_of_match);
+
+static int sti_compositor_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *vtg_np;
+	struct sti_compositor *compo;
+	struct resource *res;
+	int err;
+
+	compo = devm_kzalloc(dev, sizeof(*compo), GFP_KERNEL);
+	if (!compo) {
+		DRM_ERROR("Failed to allocate compositor context\n");
+		return -ENOMEM;
+	}
+	compo->dev = dev;
+
+	/* populate data structure depending on compatibility */
+	BUG_ON(!of_match_node(compositor_of_match, np)->data);
+
+	memcpy(&compo->data, of_match_node(compositor_of_match, np)->data,
+	       sizeof(struct sti_compositor_data));
+
+	/* Get Memory ressources */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		DRM_ERROR("Get memory resource failed\n");
+		return -ENXIO;
+	}
+	compo->regs = devm_ioremap(dev, res->start, resource_size(res));
+	if (compo->regs == NULL) {
+		DRM_ERROR("Register mapping failed\n");
+		return -ENXIO;
+	}
+
+	/* Get clock resources */
+	compo->clk_compo_main = devm_clk_get(dev, "compo_main");
+	if (IS_ERR(compo->clk_compo_main)) {
+		DRM_ERROR("Cannot get compo_main clock\n");
+		return PTR_ERR(compo->clk_compo_main);
+	}
+
+	compo->clk_compo_aux = devm_clk_get(dev, "compo_aux");
+	if (IS_ERR(compo->clk_compo_aux)) {
+		DRM_ERROR("Cannot get compo_aux clock\n");
+		return PTR_ERR(compo->clk_compo_aux);
+	}
+
+	compo->clk_pix_main = devm_clk_get(dev, "pix_main");
+	if (IS_ERR(compo->clk_pix_main)) {
+		DRM_ERROR("Cannot get pix_main clock\n");
+		return PTR_ERR(compo->clk_pix_main);
+	}
+
+	compo->clk_pix_aux = devm_clk_get(dev, "pix_aux");
+	if (IS_ERR(compo->clk_pix_aux)) {
+		DRM_ERROR("Cannot get pix_aux clock\n");
+		return PTR_ERR(compo->clk_pix_aux);
+	}
+
+	/* Get reset resources */
+	compo->rst_main = devm_reset_control_get(dev, "compo-main");
+	/* Take compo main out of reset */
+	if (!IS_ERR(compo->rst_main))
+		reset_control_deassert(compo->rst_main);
+
+	compo->rst_aux = devm_reset_control_get(dev, "compo-aux");
+	/* Take compo aux out of reset */
+	if (!IS_ERR(compo->rst_aux))
+		reset_control_deassert(compo->rst_aux);
+
+	vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 0);
+	if (vtg_np)
+		compo->vtg_main = of_vtg_find(vtg_np);
+
+	vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 1);
+	if (vtg_np)
+		compo->vtg_aux = of_vtg_find(vtg_np);
+
+	/* Initialize compositor subdevices */
+	err = sti_compositor_init_subdev(compo, compo->data.subdev_desc,
+					 compo->data.nb_subdev);
+	if (err)
+		return err;
+
+	platform_set_drvdata(pdev, compo);
+
+	return component_add(&pdev->dev, &sti_compositor_ops);
+}
+
+static int sti_compositor_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &sti_compositor_ops);
+	return 0;
+}
+
+static struct platform_driver sti_compositor_driver = {
+	.driver = {
+		.name = "sti-compositor",
+		.owner = THIS_MODULE,
+		.of_match_table = compositor_of_match,
+	},
+	.probe = sti_compositor_probe,
+	.remove = sti_compositor_remove,
+};
+
+module_platform_driver(sti_compositor_driver);
diff --git a/drivers/gpu/drm/sti/sti_compositor.h b/drivers/gpu/drm/sti/sti_compositor.h
new file mode 100644
index 0000000..3ea19db
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_compositor.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_COMPOSITOR_H_
+#define _STI_COMPOSITOR_H_
+
+#include <linux/clk.h>
+#include <linux/kernel.h>
+
+#include "sti_layer.h"
+#include "sti_mixer.h"
+
+#define WAIT_NEXT_VSYNC_MS      50 /*ms*/
+
+#define STI_MAX_LAYER 8
+#define STI_MAX_MIXER 2
+
+enum sti_compositor_subdev_type {
+	STI_MIXER_MAIN_SUBDEV,
+	STI_MIXER_AUX_SUBDEV,
+	STI_GPD_SUBDEV,
+	STI_VID_SUBDEV,
+	STI_CURSOR_SUBDEV,
+};
+
+struct sti_compositor_subdev_descriptor {
+	enum sti_compositor_subdev_type type;
+	int id;
+	unsigned int offset;
+};
+
+/**
+ * STI Compositor data structure
+ *
+ * @nb_subdev: number of subdevices supported by the compositor
+ * @subdev_desc: subdev list description
+ */
+#define MAX_SUBDEV 9
+struct sti_compositor_data {
+	unsigned int nb_subdev;
+	struct sti_compositor_subdev_descriptor subdev_desc[MAX_SUBDEV];
+};
+
+/**
+ * STI Compositor structure
+ *
+ * @dev: driver device
+ * @regs: registers (main)
+ * @data: device data
+ * @clk_compo_main: clock for main compo
+ * @clk_compo_aux: clock for aux compo
+ * @clk_pix_main: pixel clock for main path
+ * @clk_pix_aux: pixel clock for aux path
+ * @rst_main: reset control of the main path
+ * @rst_aux: reset control of the aux path
+ * @mixer: array of mixers
+ * @vtg_main: vtg for main data path
+ * @vtg_aux: vtg for auxillary data path
+ * @layer: array of layers
+ * @nb_mixers: number of mixers for this compositor
+ * @nb_layers: number of layers (GDP,VID,...) for this compositor
+ * @enable: true if compositor is enable else false
+ * @vtg_vblank_nb: callback for VTG VSYNC notification
+ */
+struct sti_compositor {
+	struct device *dev;
+	void __iomem *regs;
+	struct sti_compositor_data data;
+	struct clk *clk_compo_main;
+	struct clk *clk_compo_aux;
+	struct clk *clk_pix_main;
+	struct clk *clk_pix_aux;
+	struct reset_control *rst_main;
+	struct reset_control *rst_aux;
+	struct sti_mixer *mixer[STI_MAX_MIXER];
+	struct sti_vtg *vtg_main;
+	struct sti_vtg *vtg_aux;
+	struct sti_layer *layer[STI_MAX_LAYER];
+	int nb_mixers;
+	int nb_layers;
+	bool enable;
+	struct notifier_block vtg_vblank_nb;
+};
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c
index a014214..af5edc5 100644
--- a/drivers/gpu/drm/sti/sti_gdp.c
+++ b/drivers/gpu/drm/sti/sti_gdp.c
@@ -9,6 +9,7 @@
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
 
+#include "sti_compositor.h"
 #include "sti_gdp.h"
 #include "sti_layer.h"
 #include "sti_vtg.h"
@@ -137,6 +138,9 @@ static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_layer *layer)
 			return &gdp->node_list[i];
 
 end:
+	DRM_DEBUG_DRIVER("Warning, inconsistent NVN for %s: 0x%08X\n",
+			sti_layer_to_str(layer), hw_nvn);
+
 	return &gdp->node_list[0];
 }
 
@@ -169,6 +173,8 @@ struct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_layer *layer)
 			return &gdp->node_list[i];
 
 end:
+	DRM_DEBUG_DRIVER("Warning, NVN 0x%08X for %s does not match any node\n",
+				hw_nvn, sti_layer_to_str(layer));
 	return NULL;
 }
 
@@ -189,6 +195,7 @@ static int sti_gdp_prepare_layer(void *lay, bool first_prepare)
 	struct sti_gdp_node *top_field, *btm_field;
 	struct drm_display_mode *mode = layer->mode;
 	struct device *dev = layer->gdp->dev;
+	struct sti_compositor *compo = dev_get_drvdata(dev);
 	int format;
 	unsigned int depth, bpp;
 	int rate = mode->clock * 1000;
@@ -199,6 +206,9 @@ static int sti_gdp_prepare_layer(void *lay, bool first_prepare)
 	top_field = list->top_field;
 	btm_field = list->btm_field;
 
+	dev_dbg(dev, "%s %s top_node:0x%p btm_node:0x%p\n", __func__,
+			sti_layer_to_str(layer), top_field, btm_field);
+
 	/* Build the top field from layer params */
 	top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE;
 	top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC;
@@ -243,6 +253,14 @@ static int sti_gdp_prepare_layer(void *lay, bool first_prepare)
 		    layer->pitches[0];
 
 	if (first_prepare) {
+		/* Register gdp callback */
+		if (sti_vtg_register_client(layer->mixer_id == STI_MIXER_MAIN ?
+				compo->vtg_main : compo->vtg_aux,
+				&layer->gdp->vtg_field_nb, layer->mixer_id)) {
+			DRM_ERROR("Cannot register VTG notifier\n");
+			return 1;
+		}
+
 		/* Set and enable gdp clock */
 		if (layer->gdp->clk_pix) {
 			res = clk_set_rate(layer->gdp->clk_pix, rate);
@@ -288,6 +306,9 @@ static int sti_gdp_commit_layer(void *lay)
 	u32 dma_updated_btm = virt_to_dma(gdp->dev, updated_btm_node);
 	struct sti_gdp_node_list *curr_list = sti_gdp_get_current_nodes(layer);
 
+	dev_dbg(gdp->dev, "%s %s top/btm_node:0x%p/0x%p\n", __func__,
+			sti_layer_to_str(layer),
+			updated_top_node, updated_btm_node);
 	dev_dbg(gdp->dev, "Current NVN:0x%X\n",
 		readl(gdp->regs + GAM_GDP_NVN_OFFSET));
 	dev_dbg(gdp->dev, "Posted buff: %lx current buff: %x\n",
@@ -297,6 +318,8 @@ static int sti_gdp_commit_layer(void *lay)
 	if (curr_list == NULL) {
 		/* First update or invalid node should directly write in the
 		 * hw register */
+		DRM_DEBUG_DRIVER("%s first update (or invalid node)",
+				sti_layer_to_str(layer));
 		writel(gdp->is_curr_top == true ?
 				dma_updated_btm : dma_updated_top,
 				gdp->regs + GAM_GDP_NVN_OFFSET);
@@ -335,6 +358,9 @@ static int sti_gdp_disable_layer(void *lay)
 	unsigned int i;
 	struct sti_layer *layer = (struct sti_layer *)lay;
 	struct sti_gdp *gdp = layer->gdp;
+	struct sti_compositor *compo = dev_get_drvdata(layer->gdp->dev);
+
+	DRM_DEBUG_DRIVER("%s\n", sti_layer_to_str(layer));
 
 	/* Set the nodes as 'to be ignored on mixer' */
 	for (i = 0; i < GDP_NODE_NB_BANK; i++) {
@@ -342,6 +368,10 @@ static int sti_gdp_disable_layer(void *lay)
 		gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE;
 	}
 
+	if (sti_vtg_unregister_client(layer->mixer_id == STI_MIXER_MAIN ?
+			compo->vtg_main : compo->vtg_aux, &gdp->vtg_field_nb))
+		DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n");
+
 	if (gdp->clk_pix)
 		clk_disable_unprepare(gdp->clk_pix);
 
diff --git a/drivers/gpu/drm/sti/sti_layer.c b/drivers/gpu/drm/sti/sti_layer.c
new file mode 100644
index 0000000..cc7186d
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_layer.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "sti_compositor.h"
+#include "sti_layer.h"
+
+#define STI_FPS_INTERVAL_MS     3000
+
+struct sti_layer *sti_layer_find_layer(struct sti_layer *layer[],
+				       enum sti_layer_desc desc)
+{
+	unsigned int i;
+
+	for (i = 0; i < STI_MAX_LAYER; i++)
+		if (layer[i] && (layer[i]->desc == desc))
+			return layer[i];
+	return NULL;
+}
+
+const char *sti_layer_to_str(struct sti_layer *layer)
+{
+	switch (layer->desc) {
+	case STI_GDP_0:
+		return "GDP0";
+	case STI_GDP_1:
+		return "GDP1";
+	case STI_GDP_2:
+		return "GDP2";
+	case STI_GDP_3:
+		return "GDP3";
+	case STI_VID_0:
+		return "VID0";
+	case STI_VID_1:
+		return "VID1";
+	case STI_CURSOR:
+		return "CURSOR";
+	default:
+		return "<UNKNOWN LAYER>";
+	}
+}
+
+static int timespec_ms_diff(struct timespec lhs, struct timespec rhs)
+{
+	struct timespec tmp_ts = timespec_sub(lhs, rhs);
+	s64 tmp_ns = timespec_to_ns(&tmp_ts);
+
+	do_div(tmp_ns, NSEC_PER_MSEC);
+
+	return (int) tmp_ns;
+}
+
+static void sti_layer_update_fps(struct sti_layer *layer)
+{
+	struct timespec now;
+	struct sti_fps_info *fps;
+	int fpks, ms_since_last, num_frames;
+
+	getrawmonotonic(&now);
+
+	fps = &layer->fps_info;
+	fps->curr_frame_counter++;
+	ms_since_last = timespec_ms_diff(now, fps->last_timestamp);
+	num_frames = fps->curr_frame_counter - fps->last_frame_counter;
+
+	if (num_frames > 1 && ms_since_last >= STI_FPS_INTERVAL_MS) {
+		fps->last_timestamp = now;
+		fps->last_frame_counter = fps->curr_frame_counter;
+		fpks = (num_frames * 1000000) / ms_since_last;
+		if (fps->output)
+			DRM_INFO("%s @ %d.%.3d fps\n", sti_layer_to_str(layer),
+				 fpks / 1000, fpks % 1000);
+	}
+}
+
+struct sti_layer *sti_layer_create(struct device *dev, int desc,
+				   void __iomem *baseaddr)
+{
+	struct sti_layer *layer;
+
+	layer = devm_kzalloc(dev, sizeof(*layer), GFP_KERNEL);
+	if (!layer) {
+		DRM_ERROR("Failed to allocate memory for layer\n");
+		return NULL;
+	}
+
+	switch (desc & STI_LAYER_TYPE_MASK) {
+	case STI_GDP:
+		layer->gdp = sti_gdp_create(dev, desc, baseaddr);
+		if (!layer->gdp)
+			goto err;
+		break;
+	case STI_VID:
+		layer->vid = sti_vid_create(dev, baseaddr);
+		if (!layer->vid)
+			goto err;
+		break;
+	default:
+		goto err;
+	}
+
+	layer->desc = desc;
+	DRM_DEBUG_DRIVER("%s created\n", sti_layer_to_str(layer));
+
+	return layer;
+err:
+	devm_kfree(dev, layer);
+	DRM_ERROR("Failed to create layer\n");
+	return NULL;
+}
+
+int sti_layer_prepare(struct sti_layer *layer, struct drm_framebuffer *fb,
+		      struct drm_display_mode *mode, int mixer_id,
+		      int dest_x, int dest_y, int dest_w, int dest_h,
+		      int src_x, int src_y, int src_w, int src_h)
+{
+	int ret;
+	unsigned int i;
+	struct drm_gem_cma_object *cma_obj;
+
+	if (!layer || !fb || !mode) {
+		DRM_ERROR("Null fb, layer or mode\n");
+		return 1;
+	}
+
+	cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
+	if (!cma_obj) {
+		DRM_ERROR("Can't get CMA GEM object for fb\n");
+		return 1;
+	}
+
+	layer->fb = fb;
+	layer->mode = mode;
+	layer->mixer_id = mixer_id;
+	layer->dst_x = dest_x;
+	layer->dst_y = dest_y;
+	layer->dst_w = clamp_val(dest_w, 0, mode->crtc_hdisplay - dest_x);
+	layer->dst_h = clamp_val(dest_h, 0, mode->crtc_vdisplay - dest_y);
+	layer->src_x = src_x;
+	layer->src_y = src_y;
+	layer->src_w = src_w;
+	layer->src_h = src_h;
+	layer->format = fb->pixel_format;
+	layer->paddr = cma_obj->paddr;
+	for (i = 0; i < 4; i++) {
+		layer->pitches[i] = fb->pitches[i];
+		layer->offsets[i] = fb->offsets[i];
+	}
+
+	DRM_DEBUG_DRIVER("%s is associated with mixer_id %d\n",
+			 sti_layer_to_str(layer),
+			 layer->mixer_id);
+	DRM_DEBUG_DRIVER("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n",
+			 sti_layer_to_str(layer),
+			 layer->dst_w, layer->dst_h, layer->dst_x, layer->dst_y,
+			 layer->src_w, layer->src_h, layer->src_x,
+			 layer->src_y);
+
+	DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id,
+			 (char *)&layer->format, (unsigned long)layer->paddr);
+
+	/* Prepare layer specificities */
+	switch (layer->desc & STI_LAYER_TYPE_MASK) {
+	case STI_GDP:
+		if (!layer->gdp)
+			goto err_no_prepare;
+		ret = layer->gdp->prepare(layer, !layer->enabled);
+		break;
+	case STI_VID:
+		if (!layer->vid)
+			goto err_no_prepare;
+		ret = layer->vid->prepare(layer, !layer->enabled);
+		break;
+	default:
+		goto err_no_prepare;
+	}
+
+	if (!ret)
+		layer->enabled = true;
+
+	return ret;
+
+err_no_prepare:
+	DRM_ERROR("Cannot prepare\n");
+	return 1;
+}
+
+int sti_layer_commit(struct sti_layer *layer)
+{
+	int ret;
+
+	if (!layer)
+		return 1;
+
+	switch (layer->desc & STI_LAYER_TYPE_MASK) {
+	case STI_GDP:
+		if (!layer->gdp)
+			goto err_no_commit;
+		ret = layer->gdp->commit(layer);
+		break;
+	case STI_VID:
+		if (!layer->vid)
+			goto err_no_commit;
+		ret = layer->vid->commit(layer);
+		break;
+	default:
+		goto err_no_commit;
+	}
+
+	if (!ret)
+		sti_layer_update_fps(layer);
+
+	return ret;
+
+err_no_commit:
+	DRM_ERROR("Cannot commit\n");
+	return 1;
+}
+
+int sti_layer_disable(struct sti_layer *layer)
+{
+	int ret;
+
+	DRM_DEBUG_DRIVER("%s\n", sti_layer_to_str(layer));
+	if (!layer)
+		return 1;
+
+	if (!layer->enabled)
+		return 0;
+
+	switch (layer->desc & STI_LAYER_TYPE_MASK) {
+	case STI_GDP:
+		if (!layer->gdp)
+			goto err_no_disable;
+		ret = layer->gdp->disable(layer);
+		break;
+	case STI_VID:
+		if (!layer->vid)
+			goto err_no_disable;
+		ret = layer->vid->disable(layer);
+		break;
+	default:
+		goto err_no_disable;
+	}
+
+	if (!ret)
+		layer->enabled = false;
+	else
+		DRM_ERROR("Disable failed\n");
+
+	return ret;
+
+err_no_disable:
+	DRM_ERROR("Cannot disable\n");
+	return 1;
+}
+
+const uint32_t *sti_layer_get_formats(struct sti_layer *layer)
+{
+	const uint32_t *(*get_formats)(void) = NULL;
+
+	if (!layer)
+		return NULL;
+
+	switch (layer->desc & STI_LAYER_TYPE_MASK) {
+	case STI_GDP:
+		if (layer->gdp)
+			get_formats = layer->gdp->get_formats;
+		break;
+	default:
+		break;
+	}
+
+	if (!get_formats) {
+		DRM_ERROR("Cannot get formats\n");
+		return NULL;
+	}
+
+	return get_formats();
+}
+
+unsigned int sti_layer_get_nb_formats(struct sti_layer *layer)
+{
+	unsigned int (*get_nb_formats)(void) = NULL;
+
+	if (!layer)
+		return 0;
+
+	switch (layer->desc & STI_LAYER_TYPE_MASK) {
+	case STI_GDP:
+		if (layer->gdp)
+			get_nb_formats = layer->gdp->get_nb_formats;
+		break;
+	default:
+		break;
+	}
+
+	if (!get_nb_formats) {
+		DRM_ERROR("Cannot get nb formats\n");
+		return 0;
+	}
+
+	return get_nb_formats();
+}
diff --git a/drivers/gpu/drm/sti/sti_mixer.c b/drivers/gpu/drm/sti/sti_mixer.c
index c812c68..c928d85 100644
--- a/drivers/gpu/drm/sti/sti_mixer.c
+++ b/drivers/gpu/drm/sti/sti_mixer.c
@@ -6,6 +6,7 @@
  * License terms:  GNU General Public License (GPL), version 2
  */
 
+#include "sti_compositor.h"
 #include "sti_mixer.h"
 #include "sti_vtg.h"
 
@@ -133,6 +134,8 @@ int sti_mixer_set_layer_depth(struct sti_mixer *mixer, struct sti_layer *layer)
 	mask = GAM_DEPTH_MASK_ID << (3 * depth);
 	layer_id = layer_id << (3 * depth);
 
+	DRM_DEBUG_DRIVER("%s %s depth=%d\n", sti_mixer_to_str(mixer),
+			 sti_layer_to_str(layer), depth);
 	dev_dbg(mixer->dev, "GAM_MIXER_CRB val 0x%x mask 0x%x\n",
 		layer_id, mask);
 
@@ -195,6 +198,9 @@ int sti_mixer_set_layer_status(struct sti_mixer *mixer,
 {
 	u32 mask, val;
 
+	DRM_DEBUG_DRIVER("%s %s %s\n", status ? "enable" : "disable",
+			 sti_mixer_to_str(mixer), sti_layer_to_str(layer));
+
 	mask = sti_mixer_get_layer_mask(layer);
 	if (!mask) {
 		DRM_ERROR("Can not find layer mask\n");
diff --git a/drivers/gpu/drm/sti/sti_vid.c b/drivers/gpu/drm/sti/sti_vid.c
index 0197291..f75bda2 100644
--- a/drivers/gpu/drm/sti/sti_vid.c
+++ b/drivers/gpu/drm/sti/sti_vid.c
@@ -6,6 +6,7 @@
 
 #include <drm/drmP.h>
 
+#include "sti_compositor.h"
 #include "sti_layer.h"
 #include "sti_vid.h"
 #include "sti_vtg.h"
@@ -49,6 +50,8 @@ static int sti_vid_prepare_layer(void *lay, bool first_prepare)
 	struct sti_layer *layer = (struct sti_layer *)lay;
 	struct sti_vid *vid = layer->vid;
 
+	dev_dbg(vid->dev, "%s %s\n", __func__, sti_layer_to_str(layer));
+
 	/* Unmask */
 	val = readl(vid->regs + VID_CTL);
 	val &= ~VID_CTL_IGNORE;
@@ -64,6 +67,8 @@ static int sti_vid_commit_layer(void *lay)
 	struct drm_display_mode *mode = layer->mode;
 	u32 ydo, xdo, yds, xds;
 
+	dev_dbg(vid->dev, "%s %s\n", __func__, sti_layer_to_str(layer));
+
 	ydo = sti_vtg_get_line_number(*mode, layer->dst_y);
 	yds = sti_vtg_get_line_number(*mode, layer->dst_y + layer->dst_h - 1);
 	xdo = sti_vtg_get_pixel_number(*mode, layer->dst_x);
@@ -81,6 +86,8 @@ static int sti_vid_disable_layer(void *lay)
 	struct sti_layer *layer = (struct sti_layer *)lay;
 	struct sti_vid *vid = layer->vid;
 
+	DRM_DEBUG_DRIVER("%s\n", sti_layer_to_str(layer));
+
 	/* Mask */
 	val = readl(vid->regs + VID_CTL);
 	val |= VID_CTL_IGNORE;
-- 
1.9.1


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

* [PATCH v4 11/11] drm: sti: Add DRM driver itself
  2014-05-29  6:36 [PATCH v4 00/11] Add DRM for stih4xx platforms Benjamin Gaignard
                   ` (9 preceding siblings ...)
  2014-05-29  6:37 ` [PATCH v4 10/11] drm: sti: add Compositor Benjamin Gaignard
@ 2014-05-29  6:37 ` Benjamin Gaignard
  2014-06-11 20:06   ` Rob Clark
  2014-06-11 18:37 ` [PATCH v4 00/11] Add DRM for stih4xx platforms Rob Clark
  11 siblings, 1 reply; 19+ messages in thread
From: Benjamin Gaignard @ 2014-05-29  6:37 UTC (permalink / raw)
  To: dri-devel, linaro-mm-sig, linux-kernel, airlied
  Cc: lee.jones, Benjamin Gaignard

Make the link between all the hardware drivers and DRM/KMS interface.
Create the driver itself and make it register all the sub-components.
Use GEM CMA helpers for buffer allocation.

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
---
 drivers/gpu/drm/sti/Kconfig             |   8 +
 drivers/gpu/drm/sti/Makefile            |   8 +-
 drivers/gpu/drm/sti/sti_compositor.c    |  47 +++-
 drivers/gpu/drm/sti/sti_drm_connector.c | 114 +++++++++
 drivers/gpu/drm/sti/sti_drm_connector.h |   4 +
 drivers/gpu/drm/sti/sti_drm_crtc.c      | 436 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/sti/sti_drm_crtc.h      |  21 ++
 drivers/gpu/drm/sti/sti_drm_drv.c       | 229 +++++++++++++++++
 drivers/gpu/drm/sti/sti_drm_drv.h       |  35 +++
 drivers/gpu/drm/sti/sti_drm_encoder.c   | 108 ++++++++
 drivers/gpu/drm/sti/sti_drm_encoder.h   |   3 +
 drivers/gpu/drm/sti/sti_drm_plane.c     | 192 ++++++++++++++
 drivers/gpu/drm/sti/sti_drm_plane.h     |  17 ++
 drivers/gpu/drm/sti/sti_hda.c           |   4 +
 drivers/gpu/drm/sti/sti_hdmi.c          |   4 +
 drivers/gpu/drm/sti/sti_tvout.c         |   9 +
 16 files changed, 1237 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/sti/sti_drm_connector.c
 create mode 100644 drivers/gpu/drm/sti/sti_drm_crtc.c
 create mode 100644 drivers/gpu/drm/sti/sti_drm_crtc.h
 create mode 100644 drivers/gpu/drm/sti/sti_drm_drv.c
 create mode 100644 drivers/gpu/drm/sti/sti_drm_drv.h
 create mode 100644 drivers/gpu/drm/sti/sti_drm_encoder.c
 create mode 100644 drivers/gpu/drm/sti/sti_drm_plane.c
 create mode 100644 drivers/gpu/drm/sti/sti_drm_plane.h

diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig
index 1013570..bb48696 100644
--- a/drivers/gpu/drm/sti/Kconfig
+++ b/drivers/gpu/drm/sti/Kconfig
@@ -1,6 +1,14 @@
 config DRM_STI
 	bool "DRM Support for STMicroelectronics SoC stiH41x Series"
 	depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM)
+	select DRM_KMS_HELPER
+	select DRM_GEM_CMA_HELPER
 	select DRM_KMS_CMA_HELPER
 	help
 	  Choose this option to enable DRM on STM stiH41x chipset
+
+config DRM_STI_FBDEV
+	bool "DRM frame buffer device for STMicroelectronics SoC stiH41x Serie"
+	depends on DRM_STI
+	help
+	  Choose this option to enable FBDEV on top of DRM for STM stiH41x chipset
diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
index a0612cc..e938028 100644
--- a/drivers/gpu/drm/sti/Makefile
+++ b/drivers/gpu/drm/sti/Makefile
@@ -7,7 +7,13 @@ obj-$(CONFIG_DRM_STI) += \
 	sti_hdmi_tx3g4c28phy.o \
 	sti_hda.o \
 	sti_tvout.o \
+	sti_drm_crtc.o \
+	sti_drm_plane.o \
+	sti_drm_connector.o \
+	sti_drm_encoder.o \
+	sti_hda.o \
 	sti_layer.o \
 	sti_mixer.o \
 	sti_gdp.o \
-	sti_vid.o
\ No newline at end of file
+	sti_vid.o \
+	sti_drm_drv.o
diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c
index 3100917..d95da89 100644
--- a/drivers/gpu/drm/sti/sti_compositor.c
+++ b/drivers/gpu/drm/sti/sti_compositor.c
@@ -14,6 +14,9 @@
 #include <drm/drmP.h>
 
 #include "sti_compositor.h"
+#include "sti_drm_crtc.h"
+#include "sti_drm_drv.h"
+#include "sti_drm_plane.h"
 #include "sti_gdp.h"
 #include "sti_vtg.h"
 
@@ -84,7 +87,48 @@ static int sti_compositor_init_subdev(struct sti_compositor *compo,
 static int sti_compositor_bind(struct device *dev, struct device *master,
 	void *data)
 {
-	/* to be filled with drm driver functions */
+	struct sti_compositor *compo = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	unsigned int i, crtc = 0, plane = 0;
+	struct sti_drm_private *dev_priv = drm_dev->dev_private;
+
+	dev_priv->compo = compo;
+	INIT_LIST_HEAD(&dev_priv->pageflip_evt_list);
+
+	for (i = 0; i < compo->nb_mixers; i++) {
+		if (compo->mixer[i]) {
+			sti_drm_crtc_init(drm_dev, compo->mixer[i]);
+			crtc++;
+		}
+	}
+	if (crtc == 0) {
+		DRM_ERROR("No CRTC available\n");
+		return 1;
+	}
+
+	drm_vblank_init(drm_dev, crtc);
+	/* Allow usage of vblank without having to call drm_irq_install */
+	drm_dev->irq_enabled = 1;
+
+	for (i = 0; i < compo->nb_layers; i++) {
+		if (compo->layer[i]) {
+			/* Create planes for GDP.
+			 * except GDP0 as it is reserved for CRTC FB */
+			enum sti_layer_desc desc = compo->layer[i]->desc;
+			enum sti_layer_type type = desc & STI_LAYER_TYPE_MASK;
+
+			if ((type == STI_GDP) && (desc != STI_GDP_0)) {
+				sti_drm_plane_init(drm_dev, compo->layer[i],
+						   (1 << crtc) - 1);
+				plane++;
+			}
+		}
+	}
+
+	DRM_DEBUG_DRIVER("Initialized %d DRM CRTC(s) and %d DRM plane(s)\n",
+			 crtc, plane);
+	DRM_DEBUG_DRIVER("DRM plane(s) for VID/VDP not created yet\n");
+
 	return 0;
 }
 
@@ -127,6 +171,7 @@ static int sti_compositor_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	}
 	compo->dev = dev;
+	compo->vtg_vblank_nb.notifier_call = sti_drm_crtc_vblank_cb;
 
 	/* populate data structure depending on compatibility */
 	BUG_ON(!of_match_node(compositor_of_match, np)->data);
diff --git a/drivers/gpu/drm/sti/sti_drm_connector.c b/drivers/gpu/drm/sti/sti_drm_connector.c
new file mode 100644
index 0000000..a3ab81d
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_connector.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "sti_drm_connector.h"
+
+static int sti_drm_connector_get_modes(struct drm_connector *connector)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+
+	if (sti_connector)
+		if (sti_connector->detect)
+			return sti_connector->get_modes(connector);
+
+	return 0;
+}
+
+static int sti_drm_connector_mode_valid(struct drm_connector *connector,
+					struct drm_display_mode *mode)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+
+	if (sti_connector)
+		if (sti_connector->detect)
+			return sti_connector->check_mode(connector, mode);
+
+	return MODE_BAD;
+}
+
+struct drm_encoder *sti_drm_best_encoder(struct drm_connector *connector)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+
+	/* Best encoder is the one associated during connector creation */
+	return sti_connector->encoder;
+}
+
+static struct drm_connector_helper_funcs sti_drm_connector_helper_funcs = {
+	.get_modes = sti_drm_connector_get_modes,
+	.mode_valid = sti_drm_connector_mode_valid,
+	.best_encoder = sti_drm_best_encoder,
+};
+
+static void sti_drm_connector_dpms(struct drm_connector *connector, int mode)
+{
+	drm_helper_connector_dpms(connector, mode);
+}
+
+/* get detection status of display device */
+static enum drm_connector_status
+sti_drm_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+
+	if (sti_connector)
+		if (sti_connector->detect)
+			return sti_connector->detect(connector);
+
+	return connector_status_disconnected;
+}
+
+static void sti_drm_connector_destroy(struct drm_connector *connector)
+{
+	struct sti_connector *sti_connector = to_sti_connector(connector);
+
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+	kfree(sti_connector);
+}
+
+static struct drm_connector_funcs sti_drm_connector_funcs = {
+	.dpms = sti_drm_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = sti_drm_connector_detect,
+	.destroy = sti_drm_connector_destroy,
+};
+
+struct drm_connector *sti_drm_connector_init(struct drm_device *dev,
+					struct drm_encoder *encoder,
+					struct sti_connector *sti_connector,
+					int type)
+{
+	struct drm_connector *connector = (struct drm_connector *)sti_connector;
+	int err;
+
+	if (type == DRM_MODE_CONNECTOR_HDMIA)
+		connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+	drm_connector_init(dev, connector, &sti_drm_connector_funcs, type);
+	drm_connector_helper_add(connector, &sti_drm_connector_helper_funcs);
+
+	err = drm_sysfs_connector_add(connector);
+	if (err)
+		goto err_connector;
+
+	err = drm_mode_connector_attach_encoder(connector, encoder);
+	if (err) {
+		DRM_ERROR("Failed to attach a connector to a encoder\n");
+		goto err_sysfs;
+	}
+
+	return connector;
+
+err_sysfs:
+	drm_sysfs_connector_remove(connector);
+err_connector:
+	drm_connector_cleanup(connector);
+	return NULL;
+}
diff --git a/drivers/gpu/drm/sti/sti_drm_connector.h b/drivers/gpu/drm/sti/sti_drm_connector.h
index 798bd5c..b9b9d09 100644
--- a/drivers/gpu/drm/sti/sti_drm_connector.h
+++ b/drivers/gpu/drm/sti/sti_drm_connector.h
@@ -48,4 +48,8 @@ struct sti_connector {
 
 #define to_sti_connector(x) container_of(x, struct sti_connector, drm_connector)
 
+struct drm_connector *sti_drm_connector_init(struct drm_device *dev,
+					struct drm_encoder *encoder,
+					struct sti_connector *connector,
+					int type);
 #endif
diff --git a/drivers/gpu/drm/sti/sti_drm_crtc.c b/drivers/gpu/drm/sti/sti_drm_crtc.c
new file mode 100644
index 0000000..7350a67
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_crtc.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "sti_compositor.h"
+#include "sti_drm_drv.h"
+#include "sti_drm_crtc.h"
+#include "sti_vtg.h"
+
+static void sti_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	DRM_DEBUG_KMS("\n");
+}
+
+static void sti_drm_crtc_prepare(struct drm_crtc *crtc)
+{
+	struct sti_mixer *mixer = to_sti_mixer(crtc);
+	struct device *dev = mixer->dev;
+	struct sti_compositor *compo = dev_get_drvdata(dev);
+
+	compo->enable = true;
+
+	/* Prepare and enable the compo IP clock */
+	if (mixer->id == STI_MIXER_MAIN) {
+		if (clk_prepare_enable(compo->clk_compo_main))
+			DRM_INFO("Failed to prepare/enable compo_main clk\n");
+	} else {
+		if (clk_prepare_enable(compo->clk_compo_aux))
+			DRM_INFO("Failed to prepare/enable compo_aux clk\n");
+	}
+}
+
+static void sti_drm_crtc_commit(struct drm_crtc *crtc)
+{
+	struct sti_mixer *mixer = to_sti_mixer(crtc);
+	struct device *dev = mixer->dev;
+	struct sti_compositor *compo = dev_get_drvdata(dev);
+	struct sti_layer *layer;
+
+	if ((!mixer || !compo)) {
+		DRM_ERROR("Can not find mixer or compositor)\n");
+		return;
+	}
+
+	/* Find GDP0 which is reserved to the CRTC FB */
+	layer = sti_layer_find_layer(compo->layer, STI_GDP_0);
+	if (layer)
+		sti_layer_commit(layer);
+	else
+		DRM_ERROR("Can not find CRTC dedicated plane (GDP0)\n");
+
+	/* Enable layer on mixer */
+	if (sti_mixer_set_layer_status(mixer, layer, true))
+		DRM_ERROR("Can not enable layer at mixer\n");
+}
+
+static bool sti_drm_crtc_mode_fixup(struct drm_crtc *crtc,
+				    const struct drm_display_mode *mode,
+				    struct drm_display_mode *adjusted_mode)
+{
+	/* accept the provided drm_display_mode, do not fix it up */
+	return true;
+}
+
+static int
+sti_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
+		      struct drm_display_mode *adjusted_mode, int x, int y,
+		      struct drm_framebuffer *old_fb)
+{
+	struct sti_mixer *mixer = to_sti_mixer(crtc);
+	struct device *dev = mixer->dev;
+	struct sti_compositor *compo = dev_get_drvdata(dev);
+	struct sti_layer *layer;
+	struct clk *clk;
+	int rate = mode->clock * 1000;
+	int res;
+	unsigned int w, h;
+
+	DRM_DEBUG_KMS("CRTC:%d (%s) fb:%d mode:%d (%s)\n",
+		      crtc->base.id, sti_mixer_to_str(mixer),
+		      crtc->fb->base.id, mode->base.id, mode->name);
+
+	DRM_DEBUG_KMS("%d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
+		      mode->vrefresh, mode->clock,
+		      mode->hdisplay,
+		      mode->hsync_start, mode->hsync_end,
+		      mode->htotal,
+		      mode->vdisplay,
+		      mode->vsync_start, mode->vsync_end,
+		      mode->vtotal, mode->type, mode->flags);
+
+	/* Set rate and prepare/enable pixel clock */
+	if (mixer->id == STI_MIXER_MAIN)
+		clk = compo->clk_pix_main;
+	else
+		clk = compo->clk_pix_aux;
+
+	res = clk_set_rate(clk, rate);
+	if (res < 0) {
+		DRM_ERROR("Cannot set rate (%dHz) for pix clk\n", rate);
+		return -EINVAL;
+	}
+	if (clk_prepare_enable(clk)) {
+		DRM_ERROR("Failed to prepare/enable pix clk\n");
+		return -EINVAL;
+	}
+
+	sti_vtg_set_config(mixer->id == STI_MIXER_MAIN ?
+			compo->vtg_main : compo->vtg_aux, &crtc->mode);
+
+	/* GDP0 is reserved to the CRTC FB */
+	layer = sti_layer_find_layer(compo->layer, STI_GDP_0);
+	if (!layer) {
+		DRM_ERROR("Can not find GDP0)\n");
+		return -EINVAL;
+	}
+
+	/* copy the mode data adjusted by mode_fixup() into crtc->mode
+	 * so that hardware can be set to proper mode
+	 */
+	memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
+
+	res = sti_mixer_set_layer_depth(mixer, layer);
+	if (res) {
+		DRM_ERROR("Can not set layer depth\n");
+		return -EINVAL;
+	}
+	res = sti_mixer_active_video_area(mixer, &crtc->mode);
+	if (res) {
+		DRM_ERROR("Can not set active video area\n");
+		return -EINVAL;
+	}
+
+	if ((mode->hdisplay != crtc->fb->width) ||
+	    (mode->vdisplay != crtc->fb->height))
+		DRM_DEBUG_KMS("WARNING: fb and display mode sizes differ\n");
+
+	w = crtc->fb->width - x;
+	h = crtc->fb->height - y;
+
+	if ((w <= 0) || (h <= 0)) {
+		DRM_ERROR("Coordinates outside FB\n");
+		return -EINVAL;
+	}
+
+	return sti_layer_prepare(layer, crtc->fb, &crtc->mode, mixer->id,
+			0, 0, w, h, x, y, w, h);
+}
+
+static int sti_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+				      struct drm_framebuffer *old_fb)
+{
+	struct sti_mixer *mixer = to_sti_mixer(crtc);
+	struct device *dev = mixer->dev;
+	struct sti_compositor *compo = dev_get_drvdata(dev);
+	struct sti_layer *layer;
+	unsigned int w, h;
+	int ret;
+
+	DRM_DEBUG_KMS("CRTC:%d (%s) fb:%d (%d,%d)\n",
+		      crtc->base.id, sti_mixer_to_str(mixer),
+		      crtc->fb->base.id, x, y);
+
+	/* GDP0 is reserved to the CRTC FB */
+	layer = sti_layer_find_layer(compo->layer, STI_GDP_0);
+	if (!layer) {
+		DRM_ERROR("Can not find GDP0)\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	w = crtc->fb->width - crtc->x;
+	h = crtc->fb->height - crtc->y;
+
+	if ((w <= 0) || (h <= 0)) {
+		DRM_ERROR("Coordinates outside FB\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = sti_layer_prepare(layer, crtc->fb, &crtc->mode, mixer->id,
+				0, 0, w, h,
+				crtc->x, crtc->y, w, h);
+	if (ret) {
+		DRM_ERROR("Can not prepare layer\n");
+		goto out;
+	}
+
+	sti_drm_crtc_commit(crtc);
+out:
+	return ret;
+}
+
+static void sti_drm_crtc_load_lut(struct drm_crtc *crtc)
+{
+	/* do nothing */
+}
+
+static void sti_drm_crtc_disable(struct drm_crtc *crtc)
+{
+	struct sti_mixer *mixer = to_sti_mixer(crtc);
+	struct device *dev = mixer->dev;
+	struct sti_compositor *compo = dev_get_drvdata(dev);
+	struct sti_layer *layer;
+
+	if (!compo->enable)
+		return;
+
+	DRM_DEBUG_KMS("CRTC:%d (%s)\n", crtc->base.id, sti_mixer_to_str(mixer));
+
+	/* Disable Background */
+	sti_mixer_set_background_status(mixer, false);
+
+	/* Disable GDP0 */
+	layer = sti_layer_find_layer(compo->layer, STI_GDP_0);
+	if (!layer) {
+		DRM_ERROR("Cannot find GDP0\n");
+		return;
+	}
+
+	/* Disable layer at mixer level */
+	if (sti_mixer_set_layer_status(mixer, layer, false))
+		DRM_ERROR("Can not disable %s layer at mixer\n",
+				sti_layer_to_str(layer));
+
+	/* Wait a while to be sure that a Vsync event is received */
+	msleep(WAIT_NEXT_VSYNC_MS);
+
+	/* Then disable layer itself */
+	sti_layer_disable(layer);
+
+	drm_vblank_off(crtc->dev, mixer->id);
+
+	/* Disable pixel clock and compo IP clocks */
+	if (mixer->id == STI_MIXER_MAIN) {
+		clk_disable_unprepare(compo->clk_pix_main);
+		clk_disable_unprepare(compo->clk_compo_main);
+	} else {
+		clk_disable_unprepare(compo->clk_pix_aux);
+		clk_disable_unprepare(compo->clk_compo_aux);
+	}
+
+	compo->enable = false;
+}
+
+static struct drm_crtc_helper_funcs sti_crtc_helper_funcs = {
+	.dpms = sti_drm_crtc_dpms,
+	.prepare = sti_drm_crtc_prepare,
+	.commit = sti_drm_crtc_commit,
+	.mode_fixup = sti_drm_crtc_mode_fixup,
+	.mode_set = sti_drm_crtc_mode_set,
+	.mode_set_base = sti_drm_crtc_mode_set_base,
+	.load_lut = sti_drm_crtc_load_lut,
+	.disable = sti_drm_crtc_disable,
+};
+
+static int sti_drm_crtc_page_flip(struct drm_crtc *crtc,
+				  struct drm_framebuffer *fb,
+				  struct drm_pending_vblank_event *event,
+				  uint32_t page_flip_flags)
+{
+	struct drm_device *drm_dev = crtc->dev;
+	struct sti_drm_private *dev_priv = drm_dev->dev_private;
+	struct drm_framebuffer *old_fb;
+	struct sti_mixer *mixer = to_sti_mixer(crtc);
+	unsigned long flags;
+	int ret;
+
+	DRM_DEBUG_KMS("fb %d --> fb %d\n", crtc->fb->base.id, fb->base.id);
+
+	mutex_lock(&drm_dev->struct_mutex);
+
+	old_fb = crtc->fb;
+	crtc->fb = fb;
+	ret = sti_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb);
+	if (ret) {
+		DRM_ERROR("failed\n");
+		crtc->fb = old_fb;
+		goto out;
+	}
+
+	if (event) {
+		event->pipe = mixer->id;
+
+		ret = drm_vblank_get(drm_dev, event->pipe);
+		if (ret) {
+			DRM_ERROR("Cannot get vblank\n");
+			goto out;
+		}
+
+		spin_lock_irqsave(&drm_dev->event_lock, flags);
+		list_add_tail(&event->base.link, &dev_priv->pageflip_evt_list);
+		spin_unlock_irqrestore(&drm_dev->event_lock, flags);
+	}
+out:
+	mutex_unlock(&drm_dev->struct_mutex);
+	return ret;
+}
+
+static void sti_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+	DRM_DEBUG_KMS("\n");
+	drm_crtc_cleanup(crtc);
+}
+
+static int sti_drm_crtc_set_property(struct drm_crtc *crtc,
+				     struct drm_property *property,
+				     uint64_t val)
+{
+	DRM_DEBUG_KMS("\n");
+	return 0;
+}
+
+int sti_drm_crtc_vblank_cb(struct notifier_block *nb,
+			   unsigned long event, void *data)
+{
+	struct drm_device *drm_dev;
+	struct sti_compositor *compo =
+		container_of(nb, struct sti_compositor, vtg_vblank_nb);
+	int *crtc = data;
+	unsigned long flags;
+	struct drm_pending_vblank_event *e, *t;
+	struct sti_drm_private *priv;
+
+	drm_dev = compo->mixer[*crtc]->drm_crtc.dev;
+	priv = drm_dev->dev_private;
+
+	if ((event != VTG_TOP_FIELD_EVENT) &&
+	    (event != VTG_BOTTOM_FIELD_EVENT)) {
+		DRM_ERROR("unknown event: %lu\n", event);
+		return -EINVAL;
+	}
+
+	drm_handle_vblank(drm_dev, *crtc);
+
+	spin_lock_irqsave(&drm_dev->event_lock, flags);
+	list_for_each_entry_safe(e, t, &priv->pageflip_evt_list, base.link) {
+		if (*crtc != e->pipe)
+			continue;
+
+		list_del(&e->base.link);
+		drm_send_vblank_event(drm_dev, -1, e);
+		drm_vblank_put(drm_dev, *crtc);
+	}
+	spin_unlock_irqrestore(&drm_dev->event_lock, flags);
+
+	return 0;
+}
+
+int sti_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
+{
+	struct sti_drm_private *dev_priv = dev->dev_private;
+	struct sti_compositor *compo = dev_priv->compo;
+	struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb;
+	DRM_DEBUG_DRIVER("\n");
+
+	if (sti_vtg_register_client(crtc == STI_MIXER_MAIN ?
+			compo->vtg_main : compo->vtg_aux,
+			vtg_vblank_nb, crtc)) {
+		DRM_ERROR("Cannot register VTG notifier\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void sti_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
+{
+	struct sti_drm_private *priv = dev->dev_private;
+	struct sti_compositor *compo = priv->compo;
+	struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb;
+	unsigned long flags;
+	struct drm_pending_vblank_event *e, *t;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	if (sti_vtg_unregister_client(crtc == STI_MIXER_MAIN ?
+			compo->vtg_main : compo->vtg_aux, vtg_vblank_nb))
+		DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n");
+
+	/* free the resources of the pending requests */
+	spin_lock_irqsave(&dev->event_lock, flags);
+	list_for_each_entry_safe(e, t, &priv->pageflip_evt_list, base.link) {
+		if (crtc != e->pipe)
+			continue;
+		list_del(&e->base.link);
+		drm_vblank_put(dev, crtc);
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+}
+
+static struct drm_crtc_funcs sti_crtc_funcs = {
+	.set_config = drm_crtc_helper_set_config,
+	.page_flip = sti_drm_crtc_page_flip,
+	.destroy = sti_drm_crtc_destroy,
+	.set_property = sti_drm_crtc_set_property,
+};
+
+bool sti_drm_crtc_is_main(struct drm_crtc *crtc)
+{
+	struct sti_mixer *mixer = to_sti_mixer(crtc);
+
+	if (mixer->id == STI_MIXER_MAIN)
+		return true;
+
+	return false;
+}
+
+int sti_drm_crtc_init(struct drm_device *drm_dev, struct sti_mixer *mixer)
+{
+	struct drm_crtc *crtc = &mixer->drm_crtc;
+	int res;
+
+	res = drm_crtc_init(drm_dev, crtc, &sti_crtc_funcs);
+	if (res) {
+		DRM_ERROR("Can not initialze CRTC\n");
+		return -EINVAL;
+	}
+
+	drm_crtc_helper_add(crtc, &sti_crtc_helper_funcs);
+
+	DRM_DEBUG_DRIVER("drm CRTC:%d mapped to %s\n",
+			 crtc->base.id, sti_mixer_to_str(mixer));
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/sti/sti_drm_crtc.h b/drivers/gpu/drm/sti/sti_drm_crtc.h
new file mode 100644
index 0000000..e46babe
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_crtc.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_DRM_CRTC_H_
+#define _STI_DRM_CRTC_H_
+
+#include <drm/drmP.h>
+
+struct sti_mixer;
+
+int sti_drm_crtc_init(struct drm_device *drm_dev, struct sti_mixer *mixer);
+int sti_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
+void sti_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);
+int sti_drm_crtc_vblank_cb(struct notifier_block *nb,
+		unsigned long event, void *data);
+bool sti_drm_crtc_is_main(struct drm_crtc *drm_crtc);
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_drm_drv.c b/drivers/gpu/drm/sti/sti_drm_drv.c
new file mode 100644
index 0000000..54dc74e
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_drv.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <drm/drmP.h>
+
+#include <linux/component.h>
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "sti_drm_drv.h"
+#include "sti_drm_crtc.h"
+
+#define DRIVER_NAME	"sti"
+#define DRIVER_DESC	"STMicroelectronics SoC DRM"
+#define DRIVER_DATE	"20140601"
+#define DRIVER_MAJOR	1
+#define DRIVER_MINOR	0
+
+#ifdef CONFIG_DRM_STI_FBDEV
+#define FBDEV_CREATE_DELAY      2000
+
+static void stid_fbdev_create(struct work_struct *work)
+{
+	struct delayed_work *d_work = to_delayed_work(work);
+	struct sti_drm_private *private =
+		container_of(d_work, struct sti_drm_private, fb_work);
+	struct drm_device *drm_dev = private->drm_dev;
+
+	drm_fbdev_cma_init(drm_dev, 32,
+			   drm_dev->mode_config.num_crtc,
+			   drm_dev->mode_config.num_connector);
+}
+#endif
+
+static struct drm_mode_config_funcs sti_drm_mode_config_funcs = {
+	.fb_create = drm_fb_cma_create,
+};
+
+#define STI_MAX_FB_HEIGHT	4096
+#define STI_MAX_FB_WIDTH	4096
+
+static void sti_drm_mode_config_init(struct drm_device *dev)
+{
+	dev->mode_config.min_width = 0;
+	dev->mode_config.min_height = 0;
+
+	/*
+	 * set max width and height as default value.
+	 * this value would be used to check framebuffer size limitation
+	 * at drm_mode_addfb().
+	 */
+	dev->mode_config.max_width = STI_MAX_FB_HEIGHT;
+	dev->mode_config.max_height = STI_MAX_FB_WIDTH;
+
+	dev->mode_config.funcs = &sti_drm_mode_config_funcs;
+}
+
+static int sti_drm_load(struct drm_device *dev, unsigned long flags)
+{
+	struct sti_drm_private *private;
+
+	private = kzalloc(sizeof(struct sti_drm_private), GFP_KERNEL);
+	if (!private) {
+		DRM_ERROR("Failed to allocate private\n");
+		return -ENOMEM;
+	}
+	dev->dev_private = (void *)private;
+	private->drm_dev = dev;
+
+	drm_mode_config_init(dev);
+	drm_kms_helper_poll_init(dev);
+
+	sti_drm_mode_config_init(dev);
+
+	component_bind_all(dev->dev, dev);
+
+	drm_helper_disable_unused_functions(dev);
+#ifdef CONFIG_DRM_STI_FBDEV
+
+	INIT_DELAYED_WORK(&private->fb_work, stid_fbdev_create);
+
+	schedule_delayed_work(&private->fb_work,
+			      msecs_to_jiffies(FBDEV_CREATE_DELAY));
+#endif
+	return 0;
+}
+
+static const struct file_operations sti_drm_driver_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.mmap = drm_gem_cma_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+	.unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = drm_compat_ioctl,
+#endif
+	.release = drm_release,
+};
+
+static struct dma_buf *sti_drm_gem_prime_export(struct drm_device *dev,
+						struct drm_gem_object *obj,
+						int flags)
+{
+	/* we want to be able to write in mmapped buffer */
+	flags |= O_RDWR;
+	return drm_gem_prime_export(dev, obj, flags);
+}
+
+static struct drm_driver sti_drm_driver = {
+	.driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET |
+	    DRIVER_GEM | DRIVER_PRIME,
+	.load = sti_drm_load,
+	.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 = &sti_drm_driver_fops,
+
+	.get_vblank_counter = drm_vblank_count,
+	.enable_vblank = sti_drm_crtc_enable_vblank,
+	.disable_vblank = sti_drm_crtc_disable_vblank,
+
+	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.gem_prime_export = sti_drm_gem_prime_export,
+	.gem_prime_import = drm_gem_prime_import,
+	.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+	.gem_prime_vmap = drm_gem_cma_prime_vmap,
+	.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+	.gem_prime_mmap = drm_gem_cma_prime_mmap,
+
+	.name = DRIVER_NAME,
+	.desc = DRIVER_DESC,
+	.date = DRIVER_DATE,
+	.major = DRIVER_MAJOR,
+	.minor = DRIVER_MINOR,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+	struct device_node *np = data;
+
+	return dev->of_node == np;
+}
+
+static int sti_drm_add_components(struct device *master, struct master *m)
+{
+	struct device_node *np	= master->of_node;
+	struct device_node *child_np;
+
+	child_np = of_get_next_available_child(np, NULL);
+
+	while (child_np) {
+		component_master_add_child(m, compare_of, child_np);
+		of_node_put(child_np);
+		child_np = of_get_next_available_child(np, child_np);
+	}
+
+	return 0;
+}
+
+static int sti_drm_bind(struct device *dev)
+{
+	return drm_platform_init(&sti_drm_driver, to_platform_device(dev));
+}
+
+static void sti_drm_unbind(struct device *dev)
+{
+	drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops sti_drm_ops = {
+	.add_components = sti_drm_add_components,
+	.bind = sti_drm_bind,
+	.unbind = sti_drm_unbind,
+};
+
+static int sti_drm_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+
+	of_platform_populate(node, NULL, NULL, dev);
+
+	dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+
+	return component_master_add(&pdev->dev, &sti_drm_ops);
+}
+
+static int sti_drm_platform_remove(struct platform_device *pdev)
+{
+
+	component_master_del(&pdev->dev, &sti_drm_ops);
+	return 0;
+}
+
+static const struct of_device_id sti_drm_dt_ids[] = {
+	{ .compatible = "st,sti-display-subsystem", },
+	{ /* end node */ },
+};
+MODULE_DEVICE_TABLE(of, sti_drm_dt_ids);
+
+static struct platform_driver sti_drm_platform_driver = {
+	.probe = sti_drm_platform_probe,
+	.remove = sti_drm_platform_remove,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = DRIVER_NAME,
+		.of_match_table = sti_drm_dt_ids,
+	},
+};
+
+module_platform_driver(sti_drm_platform_driver);
+
+MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
+MODULE_LICENSE("GPL V2");
diff --git a/drivers/gpu/drm/sti/sti_drm_drv.h b/drivers/gpu/drm/sti/sti_drm_drv.h
new file mode 100644
index 0000000..6e999cb
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_drv.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_DRM_DRV_H_
+#define _STI_DRM_DRV_H_
+
+#include <drm/drmP.h>
+
+struct sti_compositor;
+struct sti_tvout;
+
+/**
+ * STI drm private structure
+ * This structure is stored as private in the drm_device
+ *
+ * @compo:                 compositor
+ * @tvout:                 TV OUT
+ * @pageflip_evt_list:     list of pending page flip requests
+ * @plane_zorder_property: z-order property for CRTC planes
+ * @drm_dev:               drm device
+ * @fb_work:               delayed work for framebuffer creation
+ */
+struct sti_drm_private {
+	struct sti_compositor *compo;
+	struct sti_tvout *tvout;
+	struct list_head pageflip_evt_list;
+	struct drm_property *plane_zorder_property;
+	struct drm_device *drm_dev;
+	struct delayed_work fb_work;
+};
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_drm_encoder.c b/drivers/gpu/drm/sti/sti_drm_encoder.c
new file mode 100644
index 0000000..29bdf4b
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_encoder.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "sti_drm_crtc.h"
+#include "sti_drm_encoder.h"
+#include "sti_tvout.h"
+
+static void sti_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool sti_drm_encoder_mode_fixup(struct drm_encoder *encoder,
+				       const struct drm_display_mode *mode,
+				       struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void sti_drm_encoder_mode_set(struct drm_encoder *encoder,
+				     struct drm_display_mode *mode,
+				     struct drm_display_mode *adjusted_mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_connector *connector;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		if (connector->encoder == encoder) {
+			sti_tvout_set_mode(connector, mode,
+					sti_drm_crtc_is_main(encoder->crtc));
+			break;
+		}
+	}
+}
+
+static void sti_drm_encoder_prepare(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_connector *connector;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		if (connector->encoder == encoder) {
+			sti_tvout_prepare(connector);
+			break;
+		}
+	}
+}
+
+static void sti_drm_encoder_commit(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_connector *connector;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		if (connector->encoder == encoder) {
+			sti_tvout_commit(connector,
+					 sti_drm_crtc_is_main(encoder->crtc));
+			break;
+		}
+	}
+}
+
+static void sti_drm_encoder_disable(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_connector *connector;
+	struct drm_connector_helper_funcs *connector_funcs;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		connector_funcs = connector->helper_private;
+		if (connector_funcs->best_encoder(connector) == encoder) {
+			sti_tvout_disable(connector);
+			break;
+		}
+	}
+}
+
+static const struct drm_encoder_helper_funcs sti_drm_encoder_helper_funcs = {
+	.dpms = sti_drm_encoder_dpms,
+	.mode_fixup = sti_drm_encoder_mode_fixup,
+	.mode_set = sti_drm_encoder_mode_set,
+	.prepare = sti_drm_encoder_prepare,
+	.commit = sti_drm_encoder_commit,
+	.disable = sti_drm_encoder_disable,
+};
+
+static void sti_drm_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+	kfree(encoder);
+}
+
+static const struct drm_encoder_funcs sti_drm_encoder_funcs = {
+	.destroy = sti_drm_encoder_destroy,
+};
+
+void sti_drm_encoder_init(struct drm_device *dev,
+		struct drm_encoder *encoder, int encoder_type)
+{
+	drm_encoder_init(dev, encoder, &sti_drm_encoder_funcs, encoder_type);
+
+	drm_encoder_helper_add(encoder, &sti_drm_encoder_helper_funcs);
+}
diff --git a/drivers/gpu/drm/sti/sti_drm_encoder.h b/drivers/gpu/drm/sti/sti_drm_encoder.h
index bc7a91c..b5b884f 100644
--- a/drivers/gpu/drm/sti/sti_drm_encoder.h
+++ b/drivers/gpu/drm/sti/sti_drm_encoder.h
@@ -11,4 +11,7 @@
 
 #define ENCODER_MAIN_CRTC_MASK BIT(0)
 
+void sti_drm_encoder_init(struct drm_device *dev,
+		struct drm_encoder *encoder, int encoder_type);
+
 #endif
diff --git a/drivers/gpu/drm/sti/sti_drm_plane.c b/drivers/gpu/drm/sti/sti_drm_plane.c
new file mode 100644
index 0000000..d950279
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_plane.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include "sti_compositor.h"
+#include "sti_drm_drv.h"
+#include "sti_drm_plane.h"
+#include "sti_vtg.h"
+
+enum sti_layer_desc sti_layer_default_zorder[] = {
+	STI_GDP_0,
+	STI_VID_0,
+	STI_GDP_1,
+	STI_VID_1,
+	STI_GDP_2,
+	STI_GDP_3,
+};
+
+/* (Background) < GDP0 < VID0 < GDP1 < VID1 < GDP2 < GDP3 < (ForeGround) */
+
+static int
+sti_drm_update_plane(struct drm_plane *plane, 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 sti_layer *layer = to_sti_layer(plane);
+	struct sti_mixer *mixer = to_sti_mixer(crtc);
+	int res;
+
+	DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s) drm fb:%d\n",
+		      crtc->base.id, sti_mixer_to_str(mixer),
+		      plane->base.id, sti_layer_to_str(layer), fb->base.id);
+	DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", crtc_w, crtc_h, crtc_x, crtc_y);
+
+	res = sti_mixer_set_layer_depth(mixer, layer);
+	if (res) {
+		DRM_ERROR("Can not set layer depth\n");
+		return res;
+	}
+
+	/* src_x are in 16.16 format. */
+	res = sti_layer_prepare(layer, fb, &crtc->mode, mixer->id,
+			crtc_x, crtc_y, crtc_w, crtc_h,
+			src_x >> 16, src_y >> 16,
+			src_w >> 16, src_h >> 16);
+	if (res) {
+		DRM_ERROR("Layer prepare failed\n");
+		return res;
+	}
+
+	res = sti_layer_commit(layer);
+	if (res) {
+		DRM_ERROR("Layer commit failed\n");
+		return res;
+	}
+
+	res = sti_mixer_set_layer_status(mixer, layer, true);
+	if (res) {
+		DRM_ERROR("Can not enable layer at mixer\n");
+		return res;
+	}
+
+	return 0;
+}
+
+static int sti_drm_disable_plane(struct drm_plane *plane)
+{
+	struct sti_layer *layer;
+	struct sti_mixer *mixer;
+	int lay_res, mix_res;
+
+	if (!plane->crtc) {
+		DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", plane->base.id);
+		return 0;
+	}
+	layer = to_sti_layer(plane);
+	mixer = to_sti_mixer(plane->crtc);
+
+	DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n",
+			plane->crtc->base.id, sti_mixer_to_str(mixer),
+			plane->base.id, sti_layer_to_str(layer));
+
+	/* Disable layer at mixer level */
+	mix_res = sti_mixer_set_layer_status(mixer, layer, false);
+	if (mix_res)
+		DRM_ERROR("Can not disable layer at mixer\n");
+
+	/* Wait a while to be sure that a Vsync event is received */
+	msleep(WAIT_NEXT_VSYNC_MS);
+
+	/* Then disable layer itself */
+	lay_res = sti_layer_disable(layer);
+	if (lay_res)
+		DRM_ERROR("Layer disable failed\n");
+
+	if (lay_res || mix_res)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void sti_drm_plane_destroy(struct drm_plane *plane)
+{
+	DRM_DEBUG_DRIVER("\n");
+
+	sti_drm_disable_plane(plane);
+	drm_plane_cleanup(plane);
+}
+
+static int sti_drm_plane_set_property(struct drm_plane *plane,
+				      struct drm_property *property,
+				      uint64_t val)
+{
+	struct drm_device *dev = plane->dev;
+	struct sti_drm_private *private = dev->dev_private;
+	struct sti_layer *layer = to_sti_layer(plane);
+
+	DRM_DEBUG_DRIVER("\n");
+
+	if (property == private->plane_zorder_property) {
+		layer->zorder = val;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static struct drm_plane_funcs sti_drm_plane_funcs = {
+	.update_plane = sti_drm_update_plane,
+	.disable_plane = sti_drm_disable_plane,
+	.destroy = sti_drm_plane_destroy,
+	.set_property = sti_drm_plane_set_property,
+};
+
+static void sti_drm_plane_attach_zorder_property(struct drm_plane *plane,
+						 uint64_t default_val)
+{
+	struct drm_device *dev = plane->dev;
+	struct sti_drm_private *private = dev->dev_private;
+	struct drm_property *prop;
+	struct sti_layer *layer = to_sti_layer(plane);
+
+	prop = private->plane_zorder_property;
+	if (!prop) {
+		prop = drm_property_create_range(dev, 0, "zpos", 0,
+						 GAM_MIXER_NB_DEPTH_LEVEL - 1);
+		if (!prop)
+			return;
+
+		private->plane_zorder_property = prop;
+	}
+
+	drm_object_attach_property(&plane->base, prop, default_val);
+	layer->zorder = default_val;
+}
+
+struct drm_plane *sti_drm_plane_init(struct drm_device *dev,
+				     struct sti_layer *layer,
+				     unsigned int possible_crtcs)
+{
+	int err, i;
+	uint64_t default_zorder = 0;
+
+	err = drm_plane_init(dev, &layer->plane, possible_crtcs,
+			     &sti_drm_plane_funcs,
+			     sti_layer_get_formats(layer),
+			     sti_layer_get_nb_formats(layer), false);
+	if (err) {
+		DRM_ERROR("Failed to initialize plane\n");
+		return NULL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(sti_layer_default_zorder); i++)
+		if (sti_layer_default_zorder[i] == layer->desc)
+			break;
+
+	default_zorder = i;
+
+	sti_drm_plane_attach_zorder_property(&layer->plane, default_zorder);
+
+	DRM_DEBUG_DRIVER("drm plane:%d mapped to %s with zorder:%llu\n",
+			 layer->plane.base.id,
+			 sti_layer_to_str(layer), default_zorder);
+
+	return &layer->plane;
+}
diff --git a/drivers/gpu/drm/sti/sti_drm_plane.h b/drivers/gpu/drm/sti/sti_drm_plane.h
new file mode 100644
index 0000000..1d8bc61
--- /dev/null
+++ b/drivers/gpu/drm/sti/sti_drm_plane.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2014
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _STI_DRM_PLANE_H_
+#define _STI_DRM_PLANE_H_
+
+#include <drm/drmP.h>
+
+struct sti_layer;
+
+struct drm_plane *sti_drm_plane_init(struct drm_device *dev,
+		struct sti_layer *layer, unsigned int possible_crtcs);
+
+#endif
diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c
index 9a6278d..4eb2d98 100644
--- a/drivers/gpu/drm/sti/sti_hda.c
+++ b/drivers/gpu/drm/sti/sti_hda.c
@@ -610,6 +610,7 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data)
 
 	encoder->possible_crtcs = ENCODER_MAIN_CRTC_MASK;
 	encoder->possible_clones = 1 << STI_CONNECTOR_HDMI;
+	sti_drm_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DAC);
 
 	connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
 	if (!connector)
@@ -628,6 +629,9 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data)
 	connector->prepare = sti_hda_prepare;
 	connector->type = STI_CONNECTOR_HDA;
 
+	sti_drm_connector_init(drm_dev,
+			encoder, connector, DRM_MODE_CONNECTOR_Component);
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
index c963468..8e36d33 100644
--- a/drivers/gpu/drm/sti/sti_hdmi.c
+++ b/drivers/gpu/drm/sti/sti_hdmi.c
@@ -582,6 +582,7 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
 
 	encoder->possible_crtcs = ENCODER_MAIN_CRTC_MASK;
 	encoder->possible_clones = 1 << STI_CONNECTOR_HDA;
+	sti_drm_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
 
 	connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
 	if (!connector)
@@ -600,6 +601,9 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
 	connector->prepare = sti_hdmi_prepare;
 	connector->type = STI_CONNECTOR_HDMI;
 
+	sti_drm_connector_init(drm_dev,
+			encoder, connector, DRM_MODE_CONNECTOR_HDMIA);
+
 	/* Enable default interrupts */
 	hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN);
 
diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c
index d778e35..0ac698b 100644
--- a/drivers/gpu/drm/sti/sti_tvout.c
+++ b/drivers/gpu/drm/sti/sti_tvout.c
@@ -17,6 +17,7 @@
 #include <drm/drm_crtc_helper.h>
 
 #include "sti_drm_connector.h"
+#include "sti_drm_drv.h"
 #include "sti_drm_encoder.h"
 #include "sti_tvout.h"
 
@@ -551,6 +552,14 @@ void sti_tvout_disable(struct drm_connector *c)
 
 static int sti_tvout_bind(struct device *dev, struct device *master, void *data)
 {
+	struct sti_tvout *tvout = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	struct sti_drm_private *dev_priv = drm_dev->dev_private;
+
+	dev_priv->tvout = tvout;
+
+	component_bind_all(dev, drm_dev);
+
 	return 0;
 }
 
-- 
1.9.1


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

* Re: [PATCH v4 06/11] drm: sti: add TVOut driver
  2014-05-29  6:37 ` [PATCH v4 06/11] drm: sti: add TVOut driver Benjamin Gaignard
@ 2014-06-11 18:20   ` Rob Clark
  0 siblings, 0 replies; 19+ messages in thread
From: Rob Clark @ 2014-06-11 18:20 UTC (permalink / raw)
  To: Benjamin Gaignard
  Cc: dri-devel, linaro-mm-sig, Linux Kernel Mailing List,
	David Airlie, lee.jones

On Thu, May 29, 2014 at 2:37 AM, Benjamin Gaignard
<benjamin.gaignard@linaro.org> wrote:
> TVout hardware block is responsible to dispatch the data flow coming
> from compositor block to any of the output (HDMI or Analog TV).
> It control when output are start/stop and configure according the
> require flow path.
>
> TVout is the parent of HDMI and HDA drivers and bind them at runtime.
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
> ---
>  drivers/gpu/drm/sti/Makefile    |   3 +-
>  drivers/gpu/drm/sti/sti_tvout.c | 678 ++++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sti/sti_tvout.h |  18 ++
>  3 files changed, 698 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/sti/sti_tvout.c
>  create mode 100644 drivers/gpu/drm/sti/sti_tvout.h
>
> diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
> index df47171..dcc9568 100644
> --- a/drivers/gpu/drm/sti/Makefile
> +++ b/drivers/gpu/drm/sti/Makefile
> @@ -4,4 +4,5 @@ obj-$(CONFIG_DRM_STI) += \
>         sti_hdmi.o \
>         sti_hdmi_tx3g0c55phy.o \
>         sti_hdmi_tx3g4c28phy.o \
> -       sti_hda.o
> +       sti_hda.o \
> +       sti_tvout.o
> \ No newline at end of file
> diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c
> new file mode 100644
> index 0000000..d778e35
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_tvout.c
> @@ -0,0 +1,678 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *          Vincent Abriou <vincent.abriou@st.com>
> + *          for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "sti_drm_connector.h"
> +#include "sti_drm_encoder.h"
> +#include "sti_tvout.h"
> +
> +/* glue registers */
> +#define TVO_CSC_MAIN_M0                  0x000
> +#define TVO_CSC_MAIN_M1                  0x004
> +#define TVO_CSC_MAIN_M2                  0x008
> +#define TVO_CSC_MAIN_M3                  0x00c
> +#define TVO_CSC_MAIN_M4                  0x010
> +#define TVO_CSC_MAIN_M5                  0x014
> +#define TVO_CSC_MAIN_M6                  0x018
> +#define TVO_CSC_MAIN_M7                  0x01c
> +#define TVO_MAIN_IN_VID_FORMAT           0x030
> +#define TVO_CSC_AUX_M0                   0x100
> +#define TVO_CSC_AUX_M1                   0x104
> +#define TVO_CSC_AUX_M2                   0x108
> +#define TVO_CSC_AUX_M3                   0x10c
> +#define TVO_CSC_AUX_M4                   0x110
> +#define TVO_CSC_AUX_M5                   0x114
> +#define TVO_CSC_AUX_M6                   0x118
> +#define TVO_CSC_AUX_M7                   0x11c
> +#define TVO_AUX_IN_VID_FORMAT            0x130
> +#define TVO_VIP_HDF                      0x400
> +#define TVO_HD_SYNC_SEL                  0x418
> +#define TVO_HD_DAC_CFG_OFF               0x420
> +#define TVO_VIP_HDMI                     0x500
> +#define TVO_HDMI_FORCE_COLOR_0           0x504
> +#define TVO_HDMI_FORCE_COLOR_1           0x508
> +#define TVO_HDMI_CLIP_VALUE_B_CB         0x50c
> +#define TVO_HDMI_CLIP_VALUE_Y_G          0x510
> +#define TVO_HDMI_CLIP_VALUE_R_CR         0x514
> +#define TVO_HDMI_SYNC_SEL                0x518
> +#define TVO_HDMI_DFV_OBS                 0x540
> +
> +#define TVO_IN_FMT_SIGNED                BIT(0)
> +#define TVO_SYNC_EXT                     BIT(4)
> +
> +#define TVO_VIP_REORDER_R_SHIFT          24
> +#define TVO_VIP_REORDER_G_SHIFT          20
> +#define TVO_VIP_REORDER_B_SHIFT          16
> +#define TVO_VIP_REORDER_MASK             0x3
> +#define TVO_VIP_REORDER_Y_G_SEL          0
> +#define TVO_VIP_REORDER_CB_B_SEL         1
> +#define TVO_VIP_REORDER_CR_R_SEL         2
> +
> +#define TVO_VIP_CLIP_SHIFT               8
> +#define TVO_VIP_CLIP_MASK                0x7
> +#define TVO_VIP_CLIP_DISABLED            0
> +#define TVO_VIP_CLIP_EAV_SAV             1
> +#define TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y 2
> +#define TVO_VIP_CLIP_LIMITED_RANGE_CB_CR 3
> +#define TVO_VIP_CLIP_PROG_RANGE          4
> +
> +#define TVO_VIP_RND_SHIFT                4
> +#define TVO_VIP_RND_MASK                 0x3
> +#define TVO_VIP_RND_8BIT_ROUNDED         0
> +#define TVO_VIP_RND_10BIT_ROUNDED        1
> +#define TVO_VIP_RND_12BIT_ROUNDED        2
> +
> +#define TVO_VIP_SEL_INPUT_MASK           0xf
> +#define TVO_VIP_SEL_INPUT_MAIN           0x0
> +#define TVO_VIP_SEL_INPUT_AUX            0x8
> +#define TVO_VIP_SEL_INPUT_FORCE_COLOR    0xf
> +#define TVO_VIP_SEL_INPUT_BYPASS_MASK    0x1
> +#define TVO_VIP_SEL_INPUT_BYPASSED       1
> +
> +#define TVO_SYNC_MAIN_VTG_SET_REF        0x00
> +#define TVO_SYNC_MAIN_VTG_SET_1          0x01
> +#define TVO_SYNC_MAIN_VTG_SET_2          0x02
> +#define TVO_SYNC_MAIN_VTG_SET_3          0x03
> +#define TVO_SYNC_MAIN_VTG_SET_4          0x04
> +#define TVO_SYNC_MAIN_VTG_SET_5          0x05
> +#define TVO_SYNC_MAIN_VTG_SET_6          0x06
> +#define TVO_SYNC_AUX_VTG_SET_REF         0x10
> +#define TVO_SYNC_AUX_VTG_SET_1           0x11
> +#define TVO_SYNC_AUX_VTG_SET_2           0x12
> +#define TVO_SYNC_AUX_VTG_SET_3           0x13
> +#define TVO_SYNC_AUX_VTG_SET_4           0x14
> +#define TVO_SYNC_AUX_VTG_SET_5           0x15
> +#define TVO_SYNC_AUX_VTG_SET_6           0x16
> +
> +#define TVO_SYNC_HD_DCS_SHIFT            8
> +
> +/* enum listing the supported output data format */
> +enum sti_tvout_video_out_type {
> +       STI_TVOUT_VIDEO_OUT_RGB,
> +       STI_TVOUT_VIDEO_OUT_YUV,
> +};
> +
> +/**
> + * STI TVout structure
> + *
> + * @dev: pointer to driver device
> + * @regs: registers
> + * @reset: reset control of the tvout
> + */
> +struct sti_tvout {
> +       struct device *dev;
> +       void __iomem *regs;
> +       struct reset_control *reset;
> +};
> +
> +/* preformatter conversion matrix */
> +static const u32 rgb_to_ycbcr_601[8] = {
> +       0xF927082E, 0x04C9FEAB, 0x01D30964, 0xFA95FD3D,
> +       0x0000082E, 0x00002000, 0x00002000, 0x00000000
> +};
> +
> +/* 709 RGB to YCbCr */
> +static const u32 rgb_to_ycbcr_709[8] = {
> +       0xF891082F, 0x0367FF40, 0x01280B71, 0xF9B1FE20,
> +       0x0000082F, 0x00002000, 0x00002000, 0x00000000
> +};
> +
> +static u32 tvout_read(struct sti_tvout *tvout, int offset)
> +{
> +       return readl(tvout->regs + offset);
> +}
> +
> +static void tvout_write(struct sti_tvout *tvout, u32 val, int offset)
> +{
> +       writel(val, tvout->regs + offset);
> +}
> +
> +/**
> + * Set the clipping mode of a VIP
> + *
> + * @tvout: tvout structure
> + * @cr_r:
> + * @y_g:
> + * @cb_b:
> + */
> +static void tvout_vip_set_color_order(struct sti_tvout *tvout,
> +                                     u32 cr_r, u32 y_g, u32 cb_b)
> +{
> +       u32 val = tvout_read(tvout, TVO_VIP_HDMI);
> +
> +       val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT);
> +       val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT);
> +       val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT);
> +       val |= cr_r << TVO_VIP_REORDER_R_SHIFT;
> +       val |= y_g << TVO_VIP_REORDER_G_SHIFT;
> +       val |= cb_b << TVO_VIP_REORDER_B_SHIFT;
> +
> +       tvout_write(tvout, val, TVO_VIP_HDMI);
> +}
> +
> +/**
> + * Set the clipping mode of a VIP
> + *
> + * @tvout: tvout structure
> + * @range: clipping range
> + */
> +static void tvout_vip_set_clip_mode(struct sti_tvout *tvout, u32 range)
> +{
> +       u32 val = tvout_read(tvout, TVO_VIP_HDMI);
> +
> +       val &= ~(TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT);
> +       val |= range << TVO_VIP_CLIP_SHIFT;
> +       tvout_write(tvout, val, TVO_VIP_HDMI);
> +}
> +
> +/**
> + * Set the rounded value of a VIP
> + *
> + * @tvout: tvout structure
> + * @rnd: rounded val per component
> + */
> +static void tvout_vip_set_rnd(struct sti_tvout *tvout, u32 rnd)
> +{
> +       u32 val = tvout_read(tvout, TVO_VIP_HDMI);
> +
> +       val &= ~(TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT);
> +       val |= rnd << TVO_VIP_RND_SHIFT;
> +       tvout_write(tvout, val, TVO_VIP_HDMI);
> +}
> +
> +/**
> + * Select the VIP input
> + *
> + * @tvout: tvout structure
> + * @sel_input: selected_input (main/aux + conv)
> + */
> +static void tvout_vip_set_sel_input(struct sti_tvout *tvout,
> +                                   bool main_path,
> +                                   bool sel_input_logic_inverted,
> +                                   enum sti_tvout_video_out_type video_out)
> +{
> +       u32 sel_input;
> +       u32 val = tvout_read(tvout, TVO_VIP_HDMI);
> +
> +       if (main_path)
> +               sel_input = TVO_VIP_SEL_INPUT_MAIN;
> +       else
> +               sel_input = TVO_VIP_SEL_INPUT_AUX;
> +
> +       switch (video_out) {
> +       case STI_TVOUT_VIDEO_OUT_RGB:
> +               sel_input |= TVO_VIP_SEL_INPUT_BYPASSED;
> +               break;
> +       case STI_TVOUT_VIDEO_OUT_YUV:
> +               sel_input &= ~TVO_VIP_SEL_INPUT_BYPASSED;
> +               break;
> +       }
> +
> +       /* on stih407 chip the sel_input bypass mode logic is inverted */
> +       if (sel_input_logic_inverted)
> +               sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK;
> +
> +       val &= ~TVO_VIP_SEL_INPUT_MASK;
> +       val |= sel_input;
> +       tvout_write(tvout, val, TVO_VIP_HDMI);
> +}
> +
> +/**
> + * Select the input video signed or unsigned
> + *
> + * @tvout: tvout structure
> + * @in_vid_signed: used video input format
> + */
> +static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout, u32 in_vid_fmt)
> +{
> +       u32 val = tvout_read(tvout, TVO_VIP_HDMI);
> +
> +       val &= ~TVO_IN_FMT_SIGNED;
> +       val |= in_vid_fmt;
> +       tvout_write(tvout, val, TVO_MAIN_IN_VID_FORMAT);
> +}
> +
> +/**
> + * Start VIP block for HDMI output
> + *
> + * @tvout: pointer on tvout structure
> + * @main_path: true if main path has to be used in the vip configuration
> + *       else aux path is used.
> + */
> +static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path)
> +{
> +       struct device_node *node = tvout->dev->of_node;
> +       bool sel_input_logic_inverted = false;
> +
> +       dev_dbg(tvout->dev, "%s\n", __func__);
> +
> +       if (main_path) {
> +               DRM_DEBUG_DRIVER("main vip for hdmi\n");
> +               /* select the input sync for hdmi = VTG set 1 */
> +               writel(TVO_SYNC_MAIN_VTG_SET_1,
> +                      tvout->regs + TVO_HDMI_SYNC_SEL);

btw, I notice a few places that bypass tvout_write()/tvout_readl()..
but if there is a reason, it at least doesn't seem obvious..

> +       } else {
> +               DRM_DEBUG_DRIVER("aux vip for hdmi\n");
> +               /* select the input sync for hdmi = VTG set 1 */
> +               writel(TVO_SYNC_AUX_VTG_SET_1, tvout->regs + TVO_HDMI_SYNC_SEL);
> +       }
> +
> +       /* set color channel order */
> +       tvout_vip_set_color_order(tvout,
> +                                 TVO_VIP_REORDER_CR_R_SEL,
> +                                 TVO_VIP_REORDER_Y_G_SEL,
> +                                 TVO_VIP_REORDER_CB_B_SEL);
> +
> +       /* set clipping mode (Limited range RGB/Y) */
> +       tvout_vip_set_clip_mode(tvout, TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y);
> +
> +       /* set round mode (rounded to 8-bit per component) */
> +       tvout_vip_set_rnd(tvout, TVO_VIP_RND_8BIT_ROUNDED);
> +
> +       if (of_device_is_compatible(node, "st,stih407-tvout")) {
> +               /* set input video format */
> +               tvout_vip_set_in_vid_fmt(tvout->regs + TVO_MAIN_IN_VID_FORMAT,
> +                                        TVO_IN_FMT_SIGNED);
> +               sel_input_logic_inverted = true;
> +       }
> +
> +       /* input selection */
> +       tvout_vip_set_sel_input(tvout, main_path,
> +                       sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_RGB);
> +}
> +
> +/**
> + * Prepare/configure VIP block for HDMI output
> + *
> + * @tvout: pointer on tvout structure
> + */
> +static void tvout_hdmi_prepare(struct sti_tvout *tvout)
> +{
> +       dev_dbg(tvout->dev, "%s\n", __func__);
> +
> +       /* reset VIP register */
> +       writel(0x00000000, tvout->regs + TVO_VIP_HDMI);
> +}
> +
> +/**
> + * Disable HDMI VIP
> + *
> + * @tvout: pointer on tvout structure
> + */
> +static void tvout_hdmi_stop(struct sti_tvout *tvout)
> +{
> +       dev_dbg(tvout->dev, "%s\n", __func__);
> +
> +       /* reset VIP register */
> +       writel(0x00000000, tvout->regs + TVO_VIP_HDMI);
> +}
> +
> +/**
> + * Start HDF VIP and HD DAC
> + *
> + * @tvout: pointer on tvout structure
> + * @main_path: true if main path has to be used in the vip configuration
> + *       else aux path is used.
> + */
> +static void tvout_hda_start(struct sti_tvout *tvout, bool main_path)
> +{
> +       struct device_node *node = tvout->dev->of_node;
> +       bool sel_input_logic_inverted = false;
> +
> +       dev_dbg(tvout->dev, "%s\n", __func__);
> +
> +       if (!main_path) {
> +               DRM_ERROR("HD Analog on aux not implemented\n");
> +               return;
> +       }
> +
> +       DRM_DEBUG_DRIVER("main vip for HDF\n");
> +
> +       /* set color channel order */
> +       tvout_vip_set_color_order(tvout->regs + TVO_VIP_HDF,
> +                                 TVO_VIP_REORDER_CR_R_SEL,
> +                                 TVO_VIP_REORDER_Y_G_SEL,
> +                                 TVO_VIP_REORDER_CB_B_SEL);
> +
> +       /* set clipping mode (Limited range RGB/Y) */
> +       tvout_vip_set_clip_mode(tvout->regs + TVO_VIP_HDF,
> +                               TVO_VIP_CLIP_LIMITED_RANGE_CB_CR);
> +
> +       /* set round mode (rounded to 10-bit per component) */
> +       tvout_vip_set_rnd(tvout->regs + TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED);
> +
> +       if (of_device_is_compatible(node, "st,stih407-tvout")) {
> +               /* set input video format */
> +               tvout_vip_set_in_vid_fmt(tvout, TVO_IN_FMT_SIGNED);
> +               sel_input_logic_inverted = true;
> +       }
> +
> +       /* Input selection */
> +       tvout_vip_set_sel_input(tvout->regs + TVO_VIP_HDF,
> +                               main_path,
> +                               sel_input_logic_inverted,
> +                               STI_TVOUT_VIDEO_OUT_YUV);
> +
> +       /* select the input sync for HD analog = VTG set 3
> +        * and HD DCS = VTG set 2 */
> +       writel((TVO_SYNC_MAIN_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT) |
> +              TVO_SYNC_MAIN_VTG_SET_3, tvout->regs + TVO_HD_SYNC_SEL);
> +
> +       /* power up HD DAC */
> +       writel(0, tvout->regs + TVO_HD_DAC_CFG_OFF);
> +}
> +
> +/**
> + * Prepare/configure HDF VIP and HD DAC
> + *
> + * @tvout: pointer on tvout structure
> + */
> +static void tvout_hda_prepare(struct sti_tvout *tvout)
> +{
> +       dev_dbg(tvout->dev, "%s\n", __func__);
> +
> +       /* reset VIP register */
> +       writel(0x00000000, tvout->regs + TVO_VIP_HDF);
> +
> +       /* power down HD DAC */
> +       writel(1, tvout->regs + TVO_HD_DAC_CFG_OFF);
> +}
> +
> +/**
> + * Stop HDF VIP and HD DAC
> + *
> + * @tvout: pointer on tvout structure
> + */
> +static void tvout_hda_stop(struct sti_tvout *tvout)
> +{
> +       dev_dbg(tvout->dev, "%s\n", __func__);
> +
> +       /* reset VIP register */
> +       writel(0x00000000, tvout->regs + TVO_VIP_HDF);
> +
> +       /* power down HD DAC */
> +       writel(1, tvout->regs + TVO_HD_DAC_CFG_OFF);
> +}
> +
> +/**
> + * Forward drm display mode information to the connector
> + *
> + * @c: drm_connector
> + * @mode: mode to be set
> + * @main_path: indicated if the connector use the main or auxillary data path
> + *
> + * Return negative value if error occurs
> + */
> +int sti_tvout_set_mode(struct drm_connector *c,
> +               struct drm_display_mode *mode, bool main_path)
> +{
> +       struct sti_connector *connector = to_sti_connector(c);
> +
> +       if (connector)
> +               if (connector->set_mode)
> +                       return connector->set_mode(c, mode, main_path);

hmm, I do wonder a bit whether it would make more sense to split
sti_connector into a bridge + connector, and move stuff like
set_mode() into the bridge half of it?  Normally that sort of thing
would make more sense in the encoder, but for msm I actually have
separate hw blocks (ie. encoder is part of display controller block,
but hdmi_bridge or hypothetical dp_bridge/dsi_bridge, are a different
block with (in some cases) their own irq, (in all cases) their own
register space, etc.   So the bridge/connector split worked pretty
nicely there.  I didn't look closely at why you have things split up
this way, but if the reasons are similar then I'd suggest
sti_bridge/sti_connector..


> +
> +       return -EINVAL;
> +}
> +
> +/**
> + * Prepare / initialize depending on the connector type
> + *
> + * @connector: drm_connector
> + *
> + * Return negative value if error occurs
> + */
> +int sti_tvout_prepare(struct drm_connector *c)
> +{
> +       struct sti_connector *connector = to_sti_connector(c);
> +       struct sti_tvout *tvout = dev_get_drvdata(connector->master);
> +       int ret;
> +
> +       dev_dbg(tvout->dev, "%s\n", __func__);
> +
> +       if (connector)
> +               if (connector->prepare)
> +                       connector->prepare(c);
> +
> +       switch (connector->type) {

hmm, ok, so couple issues here..

first, either that 'if (connector)' above is pointless (because you
never expect connector to be NULL) or you're going to crash deref'ing
connector->type.

Also, what is the purpose of this switch, vs just having the connector
specific implementation of connector->prepare() (which maybe should
actually be in sti_bridge) do call right thing?

Same comment for _commit() and _disable() below.

BR,
-R

> +       case STI_CONNECTOR_HDMI:
> +               tvout_hdmi_prepare(tvout);
> +               ret = 0;
> +               break;
> +       case STI_CONNECTOR_HDA:
> +               tvout_hda_prepare(tvout);
> +               ret = 0;
> +               break;
> +       case STI_CONNECTOR_DVO:
> +       case STI_CONNECTOR_DENC:
> +       default:
> +               /* not yet supported */
> +               ret = -EINVAL;
> +               break;
> +       }
> +
> +       return ret;
> +}
> +
> +/**
> + * Commit / start depending on the connector type
> + *
> + * @tvout: pointer on tvout structure
> + * @type: type of connector
> + * @main_path: true if main path need to be use (false for aux path)
> + *
> + * Return negative value if error occurs
> + */
> +int sti_tvout_commit(struct drm_connector *c, bool main_path)
> +{
> +       struct sti_connector *connector = to_sti_connector(c);
> +       struct sti_tvout *tvout = dev_get_drvdata(connector->master);
> +       int type = connector->type;
> +       u32 matrix_offset;
> +       unsigned int i;
> +
> +       dev_dbg(tvout->dev, "%s\n", __func__);
> +
> +       if (!connector)
> +               return -EINVAL;
> +
> +       if (connector->start) {
> +               if (connector->start(c)) {
> +                       DRM_ERROR("Unable to properly start connector\n");
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       /* set preformatter matrix */
> +       matrix_offset = main_path ? TVO_CSC_MAIN_M0 : TVO_CSC_AUX_M0;
> +       for (i = 0; i < 8; i++)
> +               writel(rgb_to_ycbcr_601[i],
> +                      tvout->regs + matrix_offset + (i * 4));
> +
> +       switch (type) {
> +       case STI_CONNECTOR_HDMI:
> +               tvout_hdmi_start(tvout, main_path);
> +               return 0;
> +       case STI_CONNECTOR_HDA:
> +               tvout_hda_start(tvout, main_path);
> +               return 0;
> +       case STI_CONNECTOR_DVO:
> +       case STI_CONNECTOR_DENC:
> +       default:
> +               /* not yet supported */
> +               return -EINVAL;
> +       }
> +}
> +
> +/**
> + * Disable / stop the tvout depending on the connector type
> + *
> + * @tvout: pointer on tvout structure
> + * @type: type of connector
> + */
> +void sti_tvout_disable(struct drm_connector *c)
> +{
> +       struct sti_connector *connector = to_sti_connector(c);
> +       struct sti_tvout *tvout = dev_get_drvdata(connector->master);
> +       int type = connector->type;
> +
> +       switch (type) {
> +       case STI_CONNECTOR_HDMI:
> +               tvout_hdmi_stop(tvout);
> +               break;
> +       case STI_CONNECTOR_HDA:
> +               tvout_hda_stop(tvout);
> +               break;
> +       case STI_CONNECTOR_DVO:
> +       case STI_CONNECTOR_DENC:
> +       default:
> +               /* not yet supported */
> +               return;
> +       }
> +
> +       if (connector)
> +               if (connector->stop)
> +                       connector->stop(c);
> +}
> +
> +static int sti_tvout_bind(struct device *dev, struct device *master, void *data)
> +{
> +       return 0;
> +}
> +
> +static void sti_tvout_unbind(struct device *dev, struct device *master,
> +       void *data)
> +{
> +       /* do nothing */
> +}
> +
> +static const struct component_ops sti_tvout_ops = {
> +       .bind   = sti_tvout_bind,
> +       .unbind = sti_tvout_unbind,
> +};
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> +       struct device_node *np = data;
> +
> +       return dev->of_node == np;
> +}
> +
> +static int sti_tvout_add_components(struct device *master, struct master *m)
> +{
> +       struct device_node *np  = master->of_node;
> +       struct device_node *child_np;
> +
> +       child_np = of_get_next_available_child(np, NULL);
> +
> +       while (child_np) {
> +               DRM_INFO("TVout add child %s\n", child_np->name);
> +               component_master_add_child(m, compare_of, child_np);
> +               of_node_put(child_np);
> +               child_np = of_get_next_available_child(np, child_np);
> +       }
> +
> +       return 0;
> +}
> +
> +static int sti_tvout_master_bind(struct device *dev)
> +{
> +       return 0;
> +}
> +
> +static void sti_tvout_master_unbind(struct device *dev)
> +{
> +       /* do nothing */
> +}
> +
> +static const struct component_master_ops sti_tvout_master_ops = {
> +       .add_components = sti_tvout_add_components,
> +       .bind = sti_tvout_master_bind,
> +       .unbind = sti_tvout_master_unbind,
> +};
> +
> +static int sti_tvout_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct device_node *node = dev->of_node;
> +       struct sti_tvout *tvout;
> +       struct resource *res;
> +
> +       DRM_INFO("%s\n", __func__);
> +
> +       if (!node)
> +               return -ENODEV;
> +
> +       tvout = devm_kzalloc(dev, sizeof(*tvout), GFP_KERNEL);
> +       if (!tvout)
> +               return -ENOMEM;
> +
> +       DRM_INFO("%s tvout %p\n", __func__, tvout);
> +
> +       tvout->dev = dev;
> +       DRM_INFO("%s tvout->dev %p\n", __func__, tvout->dev);
> +
> +       /* get Memory ressources */
> +       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg");
> +       if (!res) {
> +               DRM_ERROR("Invalid glue resource\n");
> +               return -ENOMEM;
> +       }
> +       tvout->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
> +       if (IS_ERR(tvout->regs))
> +               return PTR_ERR(tvout->regs);
> +
> +       /* get reset resources */
> +       tvout->reset = devm_reset_control_get(dev, "tvout");
> +       /* take tvout out of reset */
> +       if (!IS_ERR(tvout->reset))
> +               reset_control_deassert(tvout->reset);
> +
> +       platform_set_drvdata(pdev, tvout);
> +
> +       of_platform_populate(node, NULL, NULL, dev);
> +
> +       component_master_add(&pdev->dev, &sti_tvout_master_ops);
> +
> +       return component_add(&pdev->dev, &sti_tvout_ops);
> +}
> +
> +static int sti_tvout_remove(struct platform_device *pdev)
> +{
> +       component_master_del(&pdev->dev, &sti_tvout_master_ops);
> +       component_del(&pdev->dev, &sti_tvout_ops);
> +       return 0;
> +}
> +
> +static struct of_device_id tvout_of_match[] = {
> +       { .compatible = "st,stih416-tvout", },
> +       { .compatible = "st,stih407-tvout", },
> +       { /* end node */ }
> +};
> +MODULE_DEVICE_TABLE(of, tvout_of_match);
> +
> +struct platform_driver sti_tvout_driver = {
> +       .driver = {
> +               .name = "sti-tvout",
> +               .owner = THIS_MODULE,
> +               .of_match_table = tvout_of_match,
> +       },
> +       .probe = sti_tvout_probe,
> +       .remove = sti_tvout_remove,
> +};
> +
> +module_platform_driver(sti_tvout_driver);
> diff --git a/drivers/gpu/drm/sti/sti_tvout.h b/drivers/gpu/drm/sti/sti_tvout.h
> new file mode 100644
> index 0000000..81dceff
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_tvout.h
> @@ -0,0 +1,18 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef _STI_TVOUT_H_
> +#define _STI_TVOUT_H_
> +
> +#include <drm/drmP.h>
> +
> +int  sti_tvout_set_mode(struct drm_connector *c,
> +               struct drm_display_mode *mode, bool main_path);
> +int  sti_tvout_prepare(struct drm_connector *c);
> +int  sti_tvout_commit(struct drm_connector *c, bool main_path);
> +void sti_tvout_disable(struct drm_connector *c);
> +
> +#endif
> --
> 1.9.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v4 00/11] Add DRM for stih4xx platforms
  2014-05-29  6:36 [PATCH v4 00/11] Add DRM for stih4xx platforms Benjamin Gaignard
                   ` (10 preceding siblings ...)
  2014-05-29  6:37 ` [PATCH v4 11/11] drm: sti: Add DRM driver itself Benjamin Gaignard
@ 2014-06-11 18:37 ` Rob Clark
  2014-06-11 20:22   ` Rob Clark
  11 siblings, 1 reply; 19+ messages in thread
From: Rob Clark @ 2014-06-11 18:37 UTC (permalink / raw)
  To: Benjamin Gaignard
  Cc: dri-devel, linaro-mm-sig, Linux Kernel Mailing List,
	David Airlie, lee.jones

On Thu, May 29, 2014 at 2:36 AM, Benjamin Gaignard
<benjamin.gaignard@linaro.org> wrote:
> This series of patches add the support of DRM/KMS drivers for STMicroelectronics
> chipsets stih416 and stih407.
>
> version 4:
>         - Remove depency between TVout it subdevices HDMI and HDA
>         - Rework and simplify VTG and VTAC code
>         - Fix numbers of typo and indentation
>         - Remove debug (will be push in separate patches)
>         - Fix remarks done in previous patcheset
>
>         patches could be found here: git://git.linaro.org/people/benjamin.gaignard/kernel.git
>         on branch: drm_kms_for_next-v4
>
> version 3:
>         - Correctly split code between probe and bind funtions
>         - Squash some commits
>         - remove HQ-VDP device code to have a smaller patcheset,
>           we will introduce it later.
>
>         patches could be found here: git://git.linaro.org/people/benjamin.gaignard/kernel.git
>         on branch: drm_kms_for_next-v3
>
> version 2:
>         - Use componentized device instead of register sub-devices in master
>         driver probe function
>         - Fix Makefile and Kconfig to only allow built-in compilation
>
>         patches could be found here: git://git.linaro.org/people/benjamin.gaignard/kernel.git
>         on branch: drm_kms_for_next-v2
>
> version 1:
>         - First path submission
>
> Hardware is split in two main blocks: Compositor and TVout. Each of them
> includes specific hardware IPs and the display timing are controlled by a specific
> Video Timing Generator hardware IP (VTG).
>
> Compositor is made of the follow hardware IPs:
>  - GDP (Generic Display Pipeline) which is an entry point for graphic (RGB)
>    buffers
>  - VDP (Video Diplay Pipeline) which is an entry point for video (YUV) buffers
>  - HQVDP (High Quality Video Display Processor) that supports scaling,
>    deinterlacing and some miscellaneous image quality improvements.
>    It fetches the Video decoded buffers from memory, processes them and pushes
>    them to the Compositor through a HW dedicated bus.
>  - Mixer is responsible of mixing all the entries depending of their
>    respective z-order and layout
>
> TVout is divided in 3 parts:
>  - HDMI to generate HDMI signals, depending of chipset version HDMI phy can
>    change.
>  - HDA to generate signals for HD analog TV
>  - VIP to control/switch data path coming from Compositor
>
> On stih416 compositor and Tvout are on different dies so a Video Trafic Advance
> inter-die Communication mechanism (VTAC) is needed.
>
> +---------------------------------------------+   +----------------------------------------+
> | +-------------------------------+   +----+  |   |  +----+   +--------------------------+ |
> | |                               |   |    |  |   |  |    |   |  +---------+     +----+  | |
> | | +----+              +------+  |   |    |  |   |  |    |   |  | VIP     |---->|HDMI|  | |
> | | |GPD +------------->|      |  |   |    |  |   |  |    |   |  |         |     +----+  | |
> | | +----+              |Mixer |--|-->|    |  |   |  |    |---|->| switcher|             | |
> | |                     |      |  |   |    |  |   |  |    |   |  |         |     +----+  | |
> | |                     |      |  |   |    |  |   |  |    |   |  |         |---->|HDA |  | |
> | |                     +------+  |   |VTAC|========>|VTAC|   |  +---------+     +----+  | |
> | |                               |   |    |  |   |  |    |   |                          | |
> | |         Compositor            |   |    |  |   |  |    |   |           TVout          | |
> | +-------------------------------+   |    |  |   |  |    |   +--------------------------+ |
> |                      ^              |    |  |   |  |    |             ^                  |
> |                      |              |    |  |   |  |    |             |                  |
> |               +--------------+      |    |  |   |  |    |      +-------------+           |
> |               | VTG (master) |----->|    |  |   |  |    |----->| VTG (slave) |           |
> |               +--------------+      +----+  |   |  +----+      +-------------+           |
> |Digital die                                  |   |                              Analog Die|
> +---------------------------------------------+   +----------------------------------------+
>
> On stih407 Compositor and Tvout are on the same die
>
> +-----------------------------------------------------------------+
> | +-------------------------------+  +--------------------------+ |
> | |                               |  |  +---------+     +----+  | |
> | | +----+              +------+  |  |  | VIP     |---->|HDMI|  | |
> | | |GPD +------------->|      |  |  |  |         |     +----+  | |
> | | +----+              |Mixer |--|--|->| switcher|             | |
> | | +----+   +-----+    |      |  |  |  |         |     +----+  | |
> | | |VDP +-->+HQVDP+--->|      |  |  |  |         |---->|HDA |  | |
> | | +----+   +-----+    +------+  |  |  +---------+     +----+  | |
> | |                               |  |                          | |
> | |         Compositor            |  |           TVout          | |
> | +-------------------------------+  +--------------------------+ |
> |                              ^        ^                         |
> |                              |        |                         |
> |                           +--------------+                      |
> |                           |     VTG      |                      |
> |                           +--------------+                      |
> |Digital die                                                      |
> +-----------------------------------------------------------------+
>
> In addition of the drivers for the IPs listed before a thin I2C driver (hdmiddc) is used
> by HDMI driver to retrieve EDID for monitor.
>
> To unify interfaces of GDP and VDP we create a "layer" interface called by
> compositor to control both GPD and VDP.

I think it would be helpful to describe how you map the hardware to
kms constructs (planes/crtcs/etc).

Also, I kinda like sticking this sort of info (along w/ block diagrams
above, etc), in a notes/readme file.  Although I seem to be in the
minority about that.  But plenty of times when diving into other
drivers I've wished they had done the same :-)

For msm I think I more or less copy/paste the cover-letter and drm/msm/NOTES..

BR,
-R


> Hardware have memory contraints (alignment, contiguous) so we use CMA drm helpers functions
> to allocate frame buffer.
>
> File naming convention is:
>  - sti_* for IPs drivers
>  - sti_drm_* for drm functions implementation.
>
> Benjamin Gaignard (11):
>   drm: sti: add bindings for DRM driver
>   drm: sti: add VTG driver
>   drm: sti: add VTAC drivers
>   drm: sti: add HDMI driver
>   drm: sti: add HDA driver
>   drm: sti: add TVOut driver
>   drm: sti: add GDP layer
>   drm: sti: add VID layer
>   drm: sti: add Mixer
>   drm: sti: add Compositor
>   drm: sti: Add DRM driver itself
>
>  .../devicetree/bindings/gpu/st,stih4xx.txt         | 189 ++++++
>  drivers/gpu/drm/Kconfig                            |   2 +
>  drivers/gpu/drm/Makefile                           |   1 +
>  drivers/gpu/drm/sti/Kconfig                        |  14 +
>  drivers/gpu/drm/sti/Makefile                       |  19 +
>  drivers/gpu/drm/sti/sti_compositor.c               | 265 ++++++++
>  drivers/gpu/drm/sti/sti_compositor.h               |  90 +++
>  drivers/gpu/drm/sti/sti_drm_connector.c            | 114 ++++
>  drivers/gpu/drm/sti/sti_drm_connector.h            |  55 ++
>  drivers/gpu/drm/sti/sti_drm_crtc.c                 | 436 ++++++++++++
>  drivers/gpu/drm/sti/sti_drm_crtc.h                 |  21 +
>  drivers/gpu/drm/sti/sti_drm_drv.c                  | 229 +++++++
>  drivers/gpu/drm/sti/sti_drm_drv.h                  |  35 +
>  drivers/gpu/drm/sti/sti_drm_encoder.c              | 108 +++
>  drivers/gpu/drm/sti/sti_drm_encoder.h              |  17 +
>  drivers/gpu/drm/sti/sti_drm_plane.c                | 192 ++++++
>  drivers/gpu/drm/sti/sti_drm_plane.h                |  17 +
>  drivers/gpu/drm/sti/sti_gdp.c                      | 512 ++++++++++++++
>  drivers/gpu/drm/sti/sti_gdp.h                      |  73 ++
>  drivers/gpu/drm/sti/sti_hda.c                      | 727 ++++++++++++++++++++
>  drivers/gpu/drm/sti/sti_hdmi.c                     | 748 +++++++++++++++++++++
>  drivers/gpu/drm/sti/sti_hdmi.h                     |  88 +++
>  drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c         | 336 +++++++++
>  drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h         |  14 +
>  drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c         | 211 ++++++
>  drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h         |  14 +
>  drivers/gpu/drm/sti/sti_layer.c                    | 312 +++++++++
>  drivers/gpu/drm/sti/sti_layer.h                    | 114 ++++
>  drivers/gpu/drm/sti/sti_mixer.c                    | 249 +++++++
>  drivers/gpu/drm/sti/sti_mixer.h                    |  52 ++
>  drivers/gpu/drm/sti/sti_tvout.c                    | 687 +++++++++++++++++++
>  drivers/gpu/drm/sti/sti_tvout.h                    |  18 +
>  drivers/gpu/drm/sti/sti_vid.c                      | 145 ++++
>  drivers/gpu/drm/sti/sti_vid.h                      |  33 +
>  drivers/gpu/drm/sti/sti_vtac.c                     | 211 ++++++
>  drivers/gpu/drm/sti/sti_vtg.c                      | 356 ++++++++++
>  drivers/gpu/drm/sti/sti_vtg.h                      |  28 +
>  37 files changed, 6732 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/gpu/st,stih4xx.txt
>  create mode 100644 drivers/gpu/drm/sti/Kconfig
>  create mode 100644 drivers/gpu/drm/sti/Makefile
>  create mode 100644 drivers/gpu/drm/sti/sti_compositor.c
>  create mode 100644 drivers/gpu/drm/sti/sti_compositor.h
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_connector.c
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_connector.h
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_crtc.c
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_crtc.h
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_drv.c
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_drv.h
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_encoder.c
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_encoder.h
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_plane.c
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_plane.h
>  create mode 100644 drivers/gpu/drm/sti/sti_gdp.c
>  create mode 100644 drivers/gpu/drm/sti/sti_gdp.h
>  create mode 100644 drivers/gpu/drm/sti/sti_hda.c
>  create mode 100644 drivers/gpu/drm/sti/sti_hdmi.c
>  create mode 100644 drivers/gpu/drm/sti/sti_hdmi.h
>  create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c
>  create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h
>  create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c
>  create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h
>  create mode 100644 drivers/gpu/drm/sti/sti_layer.c
>  create mode 100644 drivers/gpu/drm/sti/sti_layer.h
>  create mode 100644 drivers/gpu/drm/sti/sti_mixer.c
>  create mode 100644 drivers/gpu/drm/sti/sti_mixer.h
>  create mode 100644 drivers/gpu/drm/sti/sti_tvout.c
>  create mode 100644 drivers/gpu/drm/sti/sti_tvout.h
>  create mode 100644 drivers/gpu/drm/sti/sti_vid.c
>  create mode 100644 drivers/gpu/drm/sti/sti_vid.h
>  create mode 100644 drivers/gpu/drm/sti/sti_vtac.c
>  create mode 100644 drivers/gpu/drm/sti/sti_vtg.c
>  create mode 100644 drivers/gpu/drm/sti/sti_vtg.h
>
> --
> 1.9.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v4 10/11] drm: sti: add Compositor
  2014-05-29  6:37 ` [PATCH v4 10/11] drm: sti: add Compositor Benjamin Gaignard
@ 2014-06-11 18:59   ` Rob Clark
  0 siblings, 0 replies; 19+ messages in thread
From: Rob Clark @ 2014-06-11 18:59 UTC (permalink / raw)
  To: Benjamin Gaignard
  Cc: dri-devel, linaro-mm-sig, Linux Kernel Mailing List,
	David Airlie, Lee Jones

On Thu, May 29, 2014 at 2:37 AM, Benjamin Gaignard
<benjamin.gaignard@linaro.org> wrote:
> Compositor control all the input sub-device (VID, GDP)
> and the mixer(s).
> It is the main entry point for composition.
> Layer interface is used to control the abstracted layers.
>
> Add debug in mixer, GDP and VID
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
> ---
>  drivers/gpu/drm/sti/Kconfig          |   1 +
>  drivers/gpu/drm/sti/Makefile         |   2 +
>  drivers/gpu/drm/sti/sti_compositor.c | 220 ++++++++++++++++++++++++
>  drivers/gpu/drm/sti/sti_compositor.h |  90 ++++++++++
>  drivers/gpu/drm/sti/sti_gdp.c        |  30 ++++
>  drivers/gpu/drm/sti/sti_layer.c      | 312 +++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sti/sti_mixer.c      |   6 +
>  drivers/gpu/drm/sti/sti_vid.c        |   7 +
>  8 files changed, 668 insertions(+)
>  create mode 100644 drivers/gpu/drm/sti/sti_compositor.c
>  create mode 100644 drivers/gpu/drm/sti/sti_compositor.h
>  create mode 100644 drivers/gpu/drm/sti/sti_layer.c
>
> diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig
> index cbd664b..1013570 100644
> --- a/drivers/gpu/drm/sti/Kconfig
> +++ b/drivers/gpu/drm/sti/Kconfig
> @@ -1,5 +1,6 @@
>  config DRM_STI
>         bool "DRM Support for STMicroelectronics SoC stiH41x Series"
>         depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM)
> +       select DRM_KMS_CMA_HELPER
>         help
>           Choose this option to enable DRM on STM stiH41x chipset
> diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
> index ce03e15..a0612cc 100644
> --- a/drivers/gpu/drm/sti/Makefile
> +++ b/drivers/gpu/drm/sti/Makefile
> @@ -1,11 +1,13 @@
>  obj-$(CONFIG_DRM_STI) += \
>         sti_vtg.o \
>         sti_vtac.o \
> +       sti_compositor.o \
>         sti_hdmi.o \
>         sti_hdmi_tx3g0c55phy.o \
>         sti_hdmi_tx3g4c28phy.o \
>         sti_hda.o \
>         sti_tvout.o \
> +       sti_layer.o \
>         sti_mixer.o \
>         sti_gdp.o \
>         sti_vid.o
> \ No newline at end of file
> diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c
> new file mode 100644
> index 0000000..3100917
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_compositor.c
> @@ -0,0 +1,220 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *          Fabien Dessenne <fabien.dessenne@st.com>
> + *          for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/component.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +
> +#include <drm/drmP.h>
> +
> +#include "sti_compositor.h"
> +#include "sti_gdp.h"
> +#include "sti_vtg.h"
> +
> +/*
> + * stiH407 compositor properties
> + */
> +struct sti_compositor_data stih407_compositor_data = {
> +       .nb_subdev = 6,
> +       .subdev_desc = {
> +                       {STI_GPD_SUBDEV, (int)STI_GDP_0, 0x100},
> +                       {STI_GPD_SUBDEV, (int)STI_GDP_1, 0x200},
> +                       {STI_GPD_SUBDEV, (int)STI_GDP_2, 0x300},
> +                       {STI_GPD_SUBDEV, (int)STI_GDP_3, 0x400},
> +                       {STI_VID_SUBDEV, (int)STI_VID_0, 0x700},
> +                       {STI_MIXER_MAIN_SUBDEV, STI_MIXER_MAIN, 0xC00}
> +       },
> +};
> +
> +/*
> + * stiH416 compositor properties
> + * Note:
> + * on stih416 MIXER_AUX has a different base address from MIXER_MAIN
> + * Moreover, GDPx is different for Main and Aux Mixer. So this subdev map does
> + * not fit for stiH416 if we want to enable the MIXER_AUX.
> + */
> +struct sti_compositor_data stih416_compositor_data = {
> +       .nb_subdev = 3,
> +       .subdev_desc = {
> +                       {STI_GPD_SUBDEV, (int)STI_GDP_0, 0x100},
> +                       {STI_GPD_SUBDEV, (int)STI_GDP_1, 0x200},
> +                       {STI_MIXER_MAIN_SUBDEV, STI_MIXER_MAIN, 0xC00}
> +       },
> +};
> +
> +static int sti_compositor_init_subdev(struct sti_compositor *compo,
> +               struct sti_compositor_subdev_descriptor *desc,
> +               unsigned int array_size)
> +{
> +       unsigned int i, mixer_id = 0, layer_id = 0;
> +
> +       for (i = 0; i < array_size; i++) {
> +               switch (desc[i].type) {
> +               case STI_MIXER_MAIN_SUBDEV:
> +               case STI_MIXER_AUX_SUBDEV:
> +                       compo->mixer[mixer_id++] =
> +                           sti_mixer_create(compo->dev, desc[i].id,
> +                                            compo->regs + desc[i].offset);
> +                       break;
> +               case STI_GPD_SUBDEV:
> +               case STI_VID_SUBDEV:
> +                       compo->layer[layer_id++] =
> +                           sti_layer_create(compo->dev, desc[i].id,
> +                                            compo->regs + desc[i].offset);
> +                       break;
> +                       /* case STI_CURSOR_SUBDEV : TODO */
> +               default:
> +                       DRM_ERROR("Unknow subdev compoment type\n");
> +                       return 1;
> +               }
> +
> +       }
> +       compo->nb_mixers = mixer_id;
> +       compo->nb_layers = layer_id;
> +
> +       return 0;
> +}
> +
> +static int sti_compositor_bind(struct device *dev, struct device *master,
> +       void *data)
> +{
> +       /* to be filled with drm driver functions */
> +       return 0;
> +}
> +
> +static void sti_compositor_unbind(struct device *dev, struct device *master,
> +       void *data)
> +{
> +       /* do nothing */
> +}
> +
> +static const struct component_ops sti_compositor_ops = {
> +       .bind   = sti_compositor_bind,
> +       .unbind = sti_compositor_unbind,
> +};
> +
> +static const struct of_device_id compositor_of_match[] = {
> +       {
> +               .compatible = "st,stih416-compositor",
> +               .data = &stih416_compositor_data,
> +       }, {
> +               .compatible = "st,stih407-compositor",
> +               .data = &stih407_compositor_data,
> +       }, {
> +               /* end node */
> +       }
> +};
> +MODULE_DEVICE_TABLE(of, compositor_of_match);
> +
> +static int sti_compositor_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct device_node *np = dev->of_node;
> +       struct device_node *vtg_np;
> +       struct sti_compositor *compo;
> +       struct resource *res;
> +       int err;
> +
> +       compo = devm_kzalloc(dev, sizeof(*compo), GFP_KERNEL);
> +       if (!compo) {
> +               DRM_ERROR("Failed to allocate compositor context\n");
> +               return -ENOMEM;
> +       }
> +       compo->dev = dev;
> +
> +       /* populate data structure depending on compatibility */
> +       BUG_ON(!of_match_node(compositor_of_match, np)->data);
> +
> +       memcpy(&compo->data, of_match_node(compositor_of_match, np)->data,
> +              sizeof(struct sti_compositor_data));
> +
> +       /* Get Memory ressources */
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (res == NULL) {
> +               DRM_ERROR("Get memory resource failed\n");
> +               return -ENXIO;
> +       }
> +       compo->regs = devm_ioremap(dev, res->start, resource_size(res));
> +       if (compo->regs == NULL) {
> +               DRM_ERROR("Register mapping failed\n");
> +               return -ENXIO;
> +       }
> +
> +       /* Get clock resources */
> +       compo->clk_compo_main = devm_clk_get(dev, "compo_main");
> +       if (IS_ERR(compo->clk_compo_main)) {
> +               DRM_ERROR("Cannot get compo_main clock\n");
> +               return PTR_ERR(compo->clk_compo_main);
> +       }
> +
> +       compo->clk_compo_aux = devm_clk_get(dev, "compo_aux");
> +       if (IS_ERR(compo->clk_compo_aux)) {
> +               DRM_ERROR("Cannot get compo_aux clock\n");
> +               return PTR_ERR(compo->clk_compo_aux);
> +       }
> +
> +       compo->clk_pix_main = devm_clk_get(dev, "pix_main");
> +       if (IS_ERR(compo->clk_pix_main)) {
> +               DRM_ERROR("Cannot get pix_main clock\n");
> +               return PTR_ERR(compo->clk_pix_main);
> +       }
> +
> +       compo->clk_pix_aux = devm_clk_get(dev, "pix_aux");
> +       if (IS_ERR(compo->clk_pix_aux)) {
> +               DRM_ERROR("Cannot get pix_aux clock\n");
> +               return PTR_ERR(compo->clk_pix_aux);
> +       }
> +
> +       /* Get reset resources */
> +       compo->rst_main = devm_reset_control_get(dev, "compo-main");
> +       /* Take compo main out of reset */
> +       if (!IS_ERR(compo->rst_main))
> +               reset_control_deassert(compo->rst_main);
> +
> +       compo->rst_aux = devm_reset_control_get(dev, "compo-aux");
> +       /* Take compo aux out of reset */
> +       if (!IS_ERR(compo->rst_aux))
> +               reset_control_deassert(compo->rst_aux);
> +
> +       vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 0);
> +       if (vtg_np)
> +               compo->vtg_main = of_vtg_find(vtg_np);
> +
> +       vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 1);
> +       if (vtg_np)
> +               compo->vtg_aux = of_vtg_find(vtg_np);
> +
> +       /* Initialize compositor subdevices */
> +       err = sti_compositor_init_subdev(compo, compo->data.subdev_desc,
> +                                        compo->data.nb_subdev);
> +       if (err)
> +               return err;
> +
> +       platform_set_drvdata(pdev, compo);
> +
> +       return component_add(&pdev->dev, &sti_compositor_ops);
> +}
> +
> +static int sti_compositor_remove(struct platform_device *pdev)
> +{
> +       component_del(&pdev->dev, &sti_compositor_ops);
> +       return 0;
> +}
> +
> +static struct platform_driver sti_compositor_driver = {
> +       .driver = {
> +               .name = "sti-compositor",
> +               .owner = THIS_MODULE,
> +               .of_match_table = compositor_of_match,
> +       },
> +       .probe = sti_compositor_probe,
> +       .remove = sti_compositor_remove,
> +};
> +
> +module_platform_driver(sti_compositor_driver);
> diff --git a/drivers/gpu/drm/sti/sti_compositor.h b/drivers/gpu/drm/sti/sti_compositor.h
> new file mode 100644
> index 0000000..3ea19db
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_compositor.h
> @@ -0,0 +1,90 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *          Fabien Dessenne <fabien.dessenne@st.com>
> + *          for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef _STI_COMPOSITOR_H_
> +#define _STI_COMPOSITOR_H_
> +
> +#include <linux/clk.h>
> +#include <linux/kernel.h>
> +
> +#include "sti_layer.h"
> +#include "sti_mixer.h"
> +
> +#define WAIT_NEXT_VSYNC_MS      50 /*ms*/
> +
> +#define STI_MAX_LAYER 8
> +#define STI_MAX_MIXER 2
> +
> +enum sti_compositor_subdev_type {
> +       STI_MIXER_MAIN_SUBDEV,
> +       STI_MIXER_AUX_SUBDEV,
> +       STI_GPD_SUBDEV,
> +       STI_VID_SUBDEV,
> +       STI_CURSOR_SUBDEV,
> +};
> +
> +struct sti_compositor_subdev_descriptor {
> +       enum sti_compositor_subdev_type type;
> +       int id;
> +       unsigned int offset;
> +};
> +
> +/**
> + * STI Compositor data structure
> + *
> + * @nb_subdev: number of subdevices supported by the compositor
> + * @subdev_desc: subdev list description
> + */
> +#define MAX_SUBDEV 9
> +struct sti_compositor_data {
> +       unsigned int nb_subdev;
> +       struct sti_compositor_subdev_descriptor subdev_desc[MAX_SUBDEV];
> +};
> +
> +/**
> + * STI Compositor structure
> + *
> + * @dev: driver device
> + * @regs: registers (main)
> + * @data: device data
> + * @clk_compo_main: clock for main compo
> + * @clk_compo_aux: clock for aux compo
> + * @clk_pix_main: pixel clock for main path
> + * @clk_pix_aux: pixel clock for aux path
> + * @rst_main: reset control of the main path
> + * @rst_aux: reset control of the aux path
> + * @mixer: array of mixers
> + * @vtg_main: vtg for main data path
> + * @vtg_aux: vtg for auxillary data path
> + * @layer: array of layers
> + * @nb_mixers: number of mixers for this compositor
> + * @nb_layers: number of layers (GDP,VID,...) for this compositor
> + * @enable: true if compositor is enable else false
> + * @vtg_vblank_nb: callback for VTG VSYNC notification
> + */
> +struct sti_compositor {
> +       struct device *dev;
> +       void __iomem *regs;
> +       struct sti_compositor_data data;
> +       struct clk *clk_compo_main;
> +       struct clk *clk_compo_aux;
> +       struct clk *clk_pix_main;
> +       struct clk *clk_pix_aux;
> +       struct reset_control *rst_main;
> +       struct reset_control *rst_aux;
> +       struct sti_mixer *mixer[STI_MAX_MIXER];
> +       struct sti_vtg *vtg_main;
> +       struct sti_vtg *vtg_aux;
> +       struct sti_layer *layer[STI_MAX_LAYER];
> +       int nb_mixers;
> +       int nb_layers;
> +       bool enable;
> +       struct notifier_block vtg_vblank_nb;
> +};
> +
> +#endif
> diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c
> index a014214..af5edc5 100644
> --- a/drivers/gpu/drm/sti/sti_gdp.c
> +++ b/drivers/gpu/drm/sti/sti_gdp.c
> @@ -9,6 +9,7 @@
>  #include <linux/clk.h>
>  #include <linux/dma-mapping.h>
>
> +#include "sti_compositor.h"
>  #include "sti_gdp.h"
>  #include "sti_layer.h"
>  #include "sti_vtg.h"
> @@ -137,6 +138,9 @@ static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_layer *layer)
>                         return &gdp->node_list[i];
>
>  end:
> +       DRM_DEBUG_DRIVER("Warning, inconsistent NVN for %s: 0x%08X\n",
> +                       sti_layer_to_str(layer), hw_nvn);
> +
>         return &gdp->node_list[0];
>  }
>
> @@ -169,6 +173,8 @@ struct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_layer *layer)
>                         return &gdp->node_list[i];
>
>  end:
> +       DRM_DEBUG_DRIVER("Warning, NVN 0x%08X for %s does not match any node\n",
> +                               hw_nvn, sti_layer_to_str(layer));
>         return NULL;
>  }
>
> @@ -189,6 +195,7 @@ static int sti_gdp_prepare_layer(void *lay, bool first_prepare)
>         struct sti_gdp_node *top_field, *btm_field;
>         struct drm_display_mode *mode = layer->mode;
>         struct device *dev = layer->gdp->dev;
> +       struct sti_compositor *compo = dev_get_drvdata(dev);
>         int format;
>         unsigned int depth, bpp;
>         int rate = mode->clock * 1000;
> @@ -199,6 +206,9 @@ static int sti_gdp_prepare_layer(void *lay, bool first_prepare)
>         top_field = list->top_field;
>         btm_field = list->btm_field;
>
> +       dev_dbg(dev, "%s %s top_node:0x%p btm_node:0x%p\n", __func__,
> +                       sti_layer_to_str(layer), top_field, btm_field);
> +
>         /* Build the top field from layer params */
>         top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE;
>         top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC;
> @@ -243,6 +253,14 @@ static int sti_gdp_prepare_layer(void *lay, bool first_prepare)
>                     layer->pitches[0];
>
>         if (first_prepare) {
> +               /* Register gdp callback */
> +               if (sti_vtg_register_client(layer->mixer_id == STI_MIXER_MAIN ?
> +                               compo->vtg_main : compo->vtg_aux,
> +                               &layer->gdp->vtg_field_nb, layer->mixer_id)) {
> +                       DRM_ERROR("Cannot register VTG notifier\n");
> +                       return 1;
> +               }
> +
>                 /* Set and enable gdp clock */
>                 if (layer->gdp->clk_pix) {
>                         res = clk_set_rate(layer->gdp->clk_pix, rate);
> @@ -288,6 +306,9 @@ static int sti_gdp_commit_layer(void *lay)
>         u32 dma_updated_btm = virt_to_dma(gdp->dev, updated_btm_node);
>         struct sti_gdp_node_list *curr_list = sti_gdp_get_current_nodes(layer);
>
> +       dev_dbg(gdp->dev, "%s %s top/btm_node:0x%p/0x%p\n", __func__,
> +                       sti_layer_to_str(layer),
> +                       updated_top_node, updated_btm_node);
>         dev_dbg(gdp->dev, "Current NVN:0x%X\n",
>                 readl(gdp->regs + GAM_GDP_NVN_OFFSET));
>         dev_dbg(gdp->dev, "Posted buff: %lx current buff: %x\n",
> @@ -297,6 +318,8 @@ static int sti_gdp_commit_layer(void *lay)
>         if (curr_list == NULL) {
>                 /* First update or invalid node should directly write in the
>                  * hw register */
> +               DRM_DEBUG_DRIVER("%s first update (or invalid node)",
> +                               sti_layer_to_str(layer));
>                 writel(gdp->is_curr_top == true ?
>                                 dma_updated_btm : dma_updated_top,
>                                 gdp->regs + GAM_GDP_NVN_OFFSET);
> @@ -335,6 +358,9 @@ static int sti_gdp_disable_layer(void *lay)
>         unsigned int i;
>         struct sti_layer *layer = (struct sti_layer *)lay;
>         struct sti_gdp *gdp = layer->gdp;
> +       struct sti_compositor *compo = dev_get_drvdata(layer->gdp->dev);
> +
> +       DRM_DEBUG_DRIVER("%s\n", sti_layer_to_str(layer));
>
>         /* Set the nodes as 'to be ignored on mixer' */
>         for (i = 0; i < GDP_NODE_NB_BANK; i++) {
> @@ -342,6 +368,10 @@ static int sti_gdp_disable_layer(void *lay)
>                 gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE;
>         }
>
> +       if (sti_vtg_unregister_client(layer->mixer_id == STI_MIXER_MAIN ?
> +                       compo->vtg_main : compo->vtg_aux, &gdp->vtg_field_nb))
> +               DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n");
> +
>         if (gdp->clk_pix)
>                 clk_disable_unprepare(gdp->clk_pix);
>
> diff --git a/drivers/gpu/drm/sti/sti_layer.c b/drivers/gpu/drm/sti/sti_layer.c
> new file mode 100644
> index 0000000..cc7186d
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_layer.c
> @@ -0,0 +1,312 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *          Fabien Dessenne <fabien.dessenne@st.com>
> + *          for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +
> +#include "sti_compositor.h"
> +#include "sti_layer.h"
> +
> +#define STI_FPS_INTERVAL_MS     3000
> +
> +struct sti_layer *sti_layer_find_layer(struct sti_layer *layer[],
> +                                      enum sti_layer_desc desc)
> +{
> +       unsigned int i;
> +
> +       for (i = 0; i < STI_MAX_LAYER; i++)
> +               if (layer[i] && (layer[i]->desc == desc))
> +                       return layer[i];
> +       return NULL;
> +}
> +
> +const char *sti_layer_to_str(struct sti_layer *layer)
> +{
> +       switch (layer->desc) {
> +       case STI_GDP_0:
> +               return "GDP0";
> +       case STI_GDP_1:
> +               return "GDP1";
> +       case STI_GDP_2:
> +               return "GDP2";
> +       case STI_GDP_3:
> +               return "GDP3";
> +       case STI_VID_0:
> +               return "VID0";
> +       case STI_VID_1:
> +               return "VID1";
> +       case STI_CURSOR:
> +               return "CURSOR";
> +       default:
> +               return "<UNKNOWN LAYER>";
> +       }
> +}
> +
> +static int timespec_ms_diff(struct timespec lhs, struct timespec rhs)
> +{
> +       struct timespec tmp_ts = timespec_sub(lhs, rhs);
> +       s64 tmp_ns = timespec_to_ns(&tmp_ts);
> +
> +       do_div(tmp_ns, NSEC_PER_MSEC);
> +
> +       return (int) tmp_ns;
> +}
> +
> +static void sti_layer_update_fps(struct sti_layer *layer)
> +{
> +       struct timespec now;
> +       struct sti_fps_info *fps;
> +       int fpks, ms_since_last, num_frames;
> +
> +       getrawmonotonic(&now);
> +
> +       fps = &layer->fps_info;
> +       fps->curr_frame_counter++;
> +       ms_since_last = timespec_ms_diff(now, fps->last_timestamp);
> +       num_frames = fps->curr_frame_counter - fps->last_frame_counter;
> +
> +       if (num_frames > 1 && ms_since_last >= STI_FPS_INTERVAL_MS) {
> +               fps->last_timestamp = now;
> +               fps->last_frame_counter = fps->curr_frame_counter;
> +               fpks = (num_frames * 1000000) / ms_since_last;
> +               if (fps->output)
> +                       DRM_INFO("%s @ %d.%.3d fps\n", sti_layer_to_str(layer),
> +                                fpks / 1000, fpks % 1000);
> +       }
> +}

btw, this fps stuff, as far as I can tell is only for perf/debug
traces?  In which case, I'd drop this (and 'struct sti_fps_info', etc)
and look into adding trace events instead.  This would let you log in
more detail, correlated to cpu load (and eventual fence traces, and
whatever other gpu related traces you feel like adding). That gives a
much more detailed view of system performance.  I'd done something
similar in msm, although not pushed upstream yet:

http://bloggingthemonkey.blogspot.com/2013/09/freedreno-update-moar-fps.html

but when Maarten's fence traces are removed, I should be able to
replace most of the msm specific trace events with generic ones (which
was only reason I didn't push msm trace events upstream yet).

I would suggest adding the trace events as a follow-up patch, rather
than as part of the initial patchset.  Or at least I'm not sure
offhand if trace events are considered userspace ABI or not, so having
it as a follow-up patch avoids delaying the main part of the driver
review.


> +
> +struct sti_layer *sti_layer_create(struct device *dev, int desc,
> +                                  void __iomem *baseaddr)
> +{
> +       struct sti_layer *layer;
> +
> +       layer = devm_kzalloc(dev, sizeof(*layer), GFP_KERNEL);
> +       if (!layer) {
> +               DRM_ERROR("Failed to allocate memory for layer\n");
> +               return NULL;
> +       }
> +
> +       switch (desc & STI_LAYER_TYPE_MASK) {
> +       case STI_GDP:
> +               layer->gdp = sti_gdp_create(dev, desc, baseaddr);
> +               if (!layer->gdp)
> +                       goto err;
> +               break;
> +       case STI_VID:
> +               layer->vid = sti_vid_create(dev, baseaddr);
> +               if (!layer->vid)
> +                       goto err;
> +               break;
> +       default:
> +               goto err;
> +       }
> +
> +       layer->desc = desc;
> +       DRM_DEBUG_DRIVER("%s created\n", sti_layer_to_str(layer));
> +
> +       return layer;
> +err:
> +       devm_kfree(dev, layer);
> +       DRM_ERROR("Failed to create layer\n");
> +       return NULL;
> +}
> +
> +int sti_layer_prepare(struct sti_layer *layer, struct drm_framebuffer *fb,
> +                     struct drm_display_mode *mode, int mixer_id,
> +                     int dest_x, int dest_y, int dest_w, int dest_h,
> +                     int src_x, int src_y, int src_w, int src_h)
> +{
> +       int ret;
> +       unsigned int i;
> +       struct drm_gem_cma_object *cma_obj;
> +
> +       if (!layer || !fb || !mode) {
> +               DRM_ERROR("Null fb, layer or mode\n");
> +               return 1;
> +       }
> +
> +       cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
> +       if (!cma_obj) {
> +               DRM_ERROR("Can't get CMA GEM object for fb\n");
> +               return 1;
> +       }
> +
> +       layer->fb = fb;
> +       layer->mode = mode;
> +       layer->mixer_id = mixer_id;
> +       layer->dst_x = dest_x;
> +       layer->dst_y = dest_y;
> +       layer->dst_w = clamp_val(dest_w, 0, mode->crtc_hdisplay - dest_x);
> +       layer->dst_h = clamp_val(dest_h, 0, mode->crtc_vdisplay - dest_y);
> +       layer->src_x = src_x;
> +       layer->src_y = src_y;
> +       layer->src_w = src_w;
> +       layer->src_h = src_h;
> +       layer->format = fb->pixel_format;
> +       layer->paddr = cma_obj->paddr;
> +       for (i = 0; i < 4; i++) {
> +               layer->pitches[i] = fb->pitches[i];
> +               layer->offsets[i] = fb->offsets[i];
> +       }
> +
> +       DRM_DEBUG_DRIVER("%s is associated with mixer_id %d\n",
> +                        sti_layer_to_str(layer),
> +                        layer->mixer_id);
> +       DRM_DEBUG_DRIVER("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n",
> +                        sti_layer_to_str(layer),
> +                        layer->dst_w, layer->dst_h, layer->dst_x, layer->dst_y,
> +                        layer->src_w, layer->src_h, layer->src_x,
> +                        layer->src_y);
> +
> +       DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id,
> +                        (char *)&layer->format, (unsigned long)layer->paddr);
> +
> +       /* Prepare layer specificities */
> +       switch (layer->desc & STI_LAYER_TYPE_MASK) {

you do have this 'switch (layer_type)' thing going in a few places..
maybe time for 'struct sti_layer_funcs', and then at init time plug in
appropriate gdp_layer_funcs/sti_layer_funcs/etc depending on layer
type?

BR,
-R

> +       case STI_GDP:
> +               if (!layer->gdp)
> +                       goto err_no_prepare;
> +               ret = layer->gdp->prepare(layer, !layer->enabled);
> +               break;
> +       case STI_VID:
> +               if (!layer->vid)
> +                       goto err_no_prepare;
> +               ret = layer->vid->prepare(layer, !layer->enabled);
> +               break;
> +       default:
> +               goto err_no_prepare;
> +       }
> +
> +       if (!ret)
> +               layer->enabled = true;
> +
> +       return ret;
> +
> +err_no_prepare:
> +       DRM_ERROR("Cannot prepare\n");
> +       return 1;
> +}
> +
> +int sti_layer_commit(struct sti_layer *layer)
> +{
> +       int ret;
> +
> +       if (!layer)
> +               return 1;
> +
> +       switch (layer->desc & STI_LAYER_TYPE_MASK) {
> +       case STI_GDP:
> +               if (!layer->gdp)
> +                       goto err_no_commit;
> +               ret = layer->gdp->commit(layer);
> +               break;
> +       case STI_VID:
> +               if (!layer->vid)
> +                       goto err_no_commit;
> +               ret = layer->vid->commit(layer);
> +               break;
> +       default:
> +               goto err_no_commit;
> +       }
> +
> +       if (!ret)
> +               sti_layer_update_fps(layer);
> +
> +       return ret;
> +
> +err_no_commit:
> +       DRM_ERROR("Cannot commit\n");
> +       return 1;
> +}
> +
> +int sti_layer_disable(struct sti_layer *layer)
> +{
> +       int ret;
> +
> +       DRM_DEBUG_DRIVER("%s\n", sti_layer_to_str(layer));
> +       if (!layer)
> +               return 1;
> +
> +       if (!layer->enabled)
> +               return 0;
> +
> +       switch (layer->desc & STI_LAYER_TYPE_MASK) {
> +       case STI_GDP:
> +               if (!layer->gdp)
> +                       goto err_no_disable;
> +               ret = layer->gdp->disable(layer);
> +               break;
> +       case STI_VID:
> +               if (!layer->vid)
> +                       goto err_no_disable;
> +               ret = layer->vid->disable(layer);
> +               break;
> +       default:
> +               goto err_no_disable;
> +       }
> +
> +       if (!ret)
> +               layer->enabled = false;
> +       else
> +               DRM_ERROR("Disable failed\n");
> +
> +       return ret;
> +
> +err_no_disable:
> +       DRM_ERROR("Cannot disable\n");
> +       return 1;
> +}
> +
> +const uint32_t *sti_layer_get_formats(struct sti_layer *layer)
> +{
> +       const uint32_t *(*get_formats)(void) = NULL;
> +
> +       if (!layer)
> +               return NULL;
> +
> +       switch (layer->desc & STI_LAYER_TYPE_MASK) {
> +       case STI_GDP:
> +               if (layer->gdp)
> +                       get_formats = layer->gdp->get_formats;
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       if (!get_formats) {
> +               DRM_ERROR("Cannot get formats\n");
> +               return NULL;
> +       }
> +
> +       return get_formats();
> +}
> +
> +unsigned int sti_layer_get_nb_formats(struct sti_layer *layer)
> +{
> +       unsigned int (*get_nb_formats)(void) = NULL;
> +
> +       if (!layer)
> +               return 0;
> +
> +       switch (layer->desc & STI_LAYER_TYPE_MASK) {
> +       case STI_GDP:
> +               if (layer->gdp)
> +                       get_nb_formats = layer->gdp->get_nb_formats;
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       if (!get_nb_formats) {
> +               DRM_ERROR("Cannot get nb formats\n");
> +               return 0;
> +       }
> +
> +       return get_nb_formats();
> +}
> diff --git a/drivers/gpu/drm/sti/sti_mixer.c b/drivers/gpu/drm/sti/sti_mixer.c
> index c812c68..c928d85 100644
> --- a/drivers/gpu/drm/sti/sti_mixer.c
> +++ b/drivers/gpu/drm/sti/sti_mixer.c
> @@ -6,6 +6,7 @@
>   * License terms:  GNU General Public License (GPL), version 2
>   */
>
> +#include "sti_compositor.h"
>  #include "sti_mixer.h"
>  #include "sti_vtg.h"
>
> @@ -133,6 +134,8 @@ int sti_mixer_set_layer_depth(struct sti_mixer *mixer, struct sti_layer *layer)
>         mask = GAM_DEPTH_MASK_ID << (3 * depth);
>         layer_id = layer_id << (3 * depth);
>
> +       DRM_DEBUG_DRIVER("%s %s depth=%d\n", sti_mixer_to_str(mixer),
> +                        sti_layer_to_str(layer), depth);
>         dev_dbg(mixer->dev, "GAM_MIXER_CRB val 0x%x mask 0x%x\n",
>                 layer_id, mask);
>
> @@ -195,6 +198,9 @@ int sti_mixer_set_layer_status(struct sti_mixer *mixer,
>  {
>         u32 mask, val;
>
> +       DRM_DEBUG_DRIVER("%s %s %s\n", status ? "enable" : "disable",
> +                        sti_mixer_to_str(mixer), sti_layer_to_str(layer));
> +
>         mask = sti_mixer_get_layer_mask(layer);
>         if (!mask) {
>                 DRM_ERROR("Can not find layer mask\n");
> diff --git a/drivers/gpu/drm/sti/sti_vid.c b/drivers/gpu/drm/sti/sti_vid.c
> index 0197291..f75bda2 100644
> --- a/drivers/gpu/drm/sti/sti_vid.c
> +++ b/drivers/gpu/drm/sti/sti_vid.c
> @@ -6,6 +6,7 @@
>
>  #include <drm/drmP.h>
>
> +#include "sti_compositor.h"
>  #include "sti_layer.h"
>  #include "sti_vid.h"
>  #include "sti_vtg.h"
> @@ -49,6 +50,8 @@ static int sti_vid_prepare_layer(void *lay, bool first_prepare)
>         struct sti_layer *layer = (struct sti_layer *)lay;
>         struct sti_vid *vid = layer->vid;
>
> +       dev_dbg(vid->dev, "%s %s\n", __func__, sti_layer_to_str(layer));
> +
>         /* Unmask */
>         val = readl(vid->regs + VID_CTL);
>         val &= ~VID_CTL_IGNORE;
> @@ -64,6 +67,8 @@ static int sti_vid_commit_layer(void *lay)
>         struct drm_display_mode *mode = layer->mode;
>         u32 ydo, xdo, yds, xds;
>
> +       dev_dbg(vid->dev, "%s %s\n", __func__, sti_layer_to_str(layer));
> +
>         ydo = sti_vtg_get_line_number(*mode, layer->dst_y);
>         yds = sti_vtg_get_line_number(*mode, layer->dst_y + layer->dst_h - 1);
>         xdo = sti_vtg_get_pixel_number(*mode, layer->dst_x);
> @@ -81,6 +86,8 @@ static int sti_vid_disable_layer(void *lay)
>         struct sti_layer *layer = (struct sti_layer *)lay;
>         struct sti_vid *vid = layer->vid;
>
> +       DRM_DEBUG_DRIVER("%s\n", sti_layer_to_str(layer));
> +
>         /* Mask */
>         val = readl(vid->regs + VID_CTL);
>         val |= VID_CTL_IGNORE;
> --
> 1.9.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v4 09/11] drm: sti: add Mixer
  2014-05-29  6:37 ` [PATCH v4 09/11] drm: sti: add Mixer Benjamin Gaignard
@ 2014-06-11 19:04   ` Rob Clark
  0 siblings, 0 replies; 19+ messages in thread
From: Rob Clark @ 2014-06-11 19:04 UTC (permalink / raw)
  To: Benjamin Gaignard
  Cc: dri-devel, linaro-mm-sig, Linux Kernel Mailing List,
	David Airlie, Lee Jones

On Thu, May 29, 2014 at 2:37 AM, Benjamin Gaignard
<benjamin.gaignard@linaro.org> wrote:
> Mixer hardware IP is responsible of mixing the different inputs layers.
> Z-order is managed by the mixer.
> We could 2 mixers: one for main path and one for auxillary path
>
> Mixers are part of Compositor hardware block
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
> ---
>  drivers/gpu/drm/sti/Makefile    |   1 +
>  drivers/gpu/drm/sti/sti_mixer.c | 243 ++++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sti/sti_mixer.h |  52 +++++++++
>  3 files changed, 296 insertions(+)
>  create mode 100644 drivers/gpu/drm/sti/sti_mixer.c
>  create mode 100644 drivers/gpu/drm/sti/sti_mixer.h
>
> diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
> index bb0f6b3..ce03e15 100644
> --- a/drivers/gpu/drm/sti/Makefile
> +++ b/drivers/gpu/drm/sti/Makefile
> @@ -6,5 +6,6 @@ obj-$(CONFIG_DRM_STI) += \
>         sti_hdmi_tx3g4c28phy.o \
>         sti_hda.o \
>         sti_tvout.o \
> +       sti_mixer.o \
>         sti_gdp.o \
>         sti_vid.o
> \ No newline at end of file
> diff --git a/drivers/gpu/drm/sti/sti_mixer.c b/drivers/gpu/drm/sti/sti_mixer.c
> new file mode 100644
> index 0000000..c812c68
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_mixer.c
> @@ -0,0 +1,243 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *          Fabien Dessenne <fabien.dessenne@st.com>
> + *          for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include "sti_mixer.h"
> +#include "sti_vtg.h"
> +
> +/* Identity: G=Y , B=Cb , R=Cr */
> +static const u32 MixerColorSpaceMatIdentity[] = {

minor nit: CamelCase..

BR,
-R

> +       0x10000000, 0x00000000, 0x10000000, 0x00001000,
> +       0x00000000, 0x00000000, 0x00000000, 0x00000000
> +};
> +
> +/* regs offset */
> +#define GAM_MIXER_CTL      0x00
> +#define GAM_MIXER_BKC      0x04
> +#define GAM_MIXER_BCO      0x0C
> +#define GAM_MIXER_BCS      0x10
> +#define GAM_MIXER_AVO      0x28
> +#define GAM_MIXER_AVS      0x2C
> +#define GAM_MIXER_CRB      0x34
> +#define GAM_MIXER_ACT      0x38
> +#define GAM_MIXER_MBP      0x3C
> +#define GAM_MIXER_MX0      0x80
> +
> +/* id for depth of CRB reg */
> +#define GAM_DEPTH_VID0_ID  1
> +#define GAM_DEPTH_VID1_ID  2
> +#define GAM_DEPTH_GDP0_ID  3
> +#define GAM_DEPTH_GDP1_ID  4
> +#define GAM_DEPTH_GDP2_ID  5
> +#define GAM_DEPTH_GDP3_ID  6
> +#define GAM_DEPTH_MASK_ID  7
> +
> +/* mask in CTL reg */
> +#define GAM_CTL_BACK_MASK  BIT(0)
> +#define GAM_CTL_VID0_MASK  BIT(1)
> +#define GAM_CTL_VID1_MASK  BIT(2)
> +#define GAM_CTL_GDP0_MASK  BIT(3)
> +#define GAM_CTL_GDP1_MASK  BIT(4)
> +#define GAM_CTL_GDP2_MASK  BIT(5)
> +#define GAM_CTL_GDP3_MASK  BIT(6)
> +
> +const char *sti_mixer_to_str(struct sti_mixer *mixer)
> +{
> +       switch (mixer->id) {
> +       case STI_MIXER_MAIN:
> +               return "MAIN_MIXER";
> +       case STI_MIXER_AUX:
> +               return "AUX_MIXER";
> +       default:
> +               return "<UNKNOWN MIXER>";
> +       }
> +}
> +
> +static inline u32 sti_mixer_reg_read(struct sti_mixer *mixer, u32 reg_id)
> +{
> +       return readl(mixer->regs + reg_id);
> +}
> +
> +static inline void sti_mixer_reg_write(struct sti_mixer *mixer,
> +                                      u32 reg_id, u32 val)
> +{
> +       writel(val, mixer->regs + reg_id);
> +}
> +
> +void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable)
> +{
> +       u32 val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL);
> +
> +       val &= ~GAM_CTL_BACK_MASK;
> +       val |= enable;
> +       sti_mixer_reg_write(mixer, GAM_MIXER_CTL, val);
> +}
> +
> +static void sti_mixer_set_background_color(struct sti_mixer *mixer,
> +                                          u8 red, u8 green, u8 blue)
> +{
> +       u32 val = (red << 16) | (green << 8) | blue;
> +
> +       sti_mixer_reg_write(mixer, GAM_MIXER_BKC, val);
> +}
> +
> +static void sti_mixer_set_background_area(struct sti_mixer *mixer,
> +                                         struct drm_display_mode *mode)
> +{
> +       u32 ydo, xdo, yds, xds;
> +
> +       ydo = sti_vtg_get_line_number(*mode, 0);
> +       yds = sti_vtg_get_line_number(*mode, mode->vdisplay - 1);
> +       xdo = sti_vtg_get_pixel_number(*mode, 0);
> +       xds = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1);
> +
> +       sti_mixer_reg_write(mixer, GAM_MIXER_BCO, ydo << 16 | xdo);
> +       sti_mixer_reg_write(mixer, GAM_MIXER_BCS, yds << 16 | xds);
> +}
> +
> +int sti_mixer_set_layer_depth(struct sti_mixer *mixer, struct sti_layer *layer)
> +{
> +       int layer_id = 0, depth = layer->zorder;
> +       u32 mask, val;
> +
> +       if (depth >= GAM_MIXER_NB_DEPTH_LEVEL)
> +               return 1;
> +
> +       switch (layer->desc) {
> +       case STI_GDP_0:
> +               layer_id = GAM_DEPTH_GDP0_ID;
> +               break;
> +       case STI_GDP_1:
> +               layer_id = GAM_DEPTH_GDP1_ID;
> +               break;
> +       case STI_GDP_2:
> +               layer_id = GAM_DEPTH_GDP2_ID;
> +               break;
> +       case STI_GDP_3:
> +               layer_id = GAM_DEPTH_GDP3_ID;
> +               break;
> +       case STI_VID_0:
> +               layer_id = GAM_DEPTH_VID0_ID;
> +               break;
> +       case STI_VID_1:
> +               layer_id = GAM_DEPTH_VID1_ID;
> +               break;
> +       default:
> +               DRM_ERROR("Unknown layer %d\n", layer->desc);
> +               return 1;
> +       }
> +       mask = GAM_DEPTH_MASK_ID << (3 * depth);
> +       layer_id = layer_id << (3 * depth);
> +
> +       dev_dbg(mixer->dev, "GAM_MIXER_CRB val 0x%x mask 0x%x\n",
> +               layer_id, mask);
> +
> +       val = sti_mixer_reg_read(mixer, GAM_MIXER_CRB);
> +       val &= ~mask;
> +       val |= layer_id;
> +       sti_mixer_reg_write(mixer, GAM_MIXER_CRB, val);
> +
> +       dev_dbg(mixer->dev, "Read GAM_MIXER_CRB 0x%x\n",
> +               sti_mixer_reg_read(mixer, GAM_MIXER_CRB));
> +       return 0;
> +}
> +
> +int sti_mixer_active_video_area(struct sti_mixer *mixer,
> +                               struct drm_display_mode *mode)
> +{
> +       u32 ydo, xdo, yds, xds;
> +
> +       ydo = sti_vtg_get_line_number(*mode, 0);
> +       yds = sti_vtg_get_line_number(*mode, mode->vdisplay - 1);
> +       xdo = sti_vtg_get_pixel_number(*mode, 0);
> +       xds = sti_vtg_get_pixel_number(*mode, mode->hdisplay - 1);
> +
> +       DRM_DEBUG_DRIVER("%s active video area xdo:%d ydo:%d xds:%d yds:%d\n",
> +                        sti_mixer_to_str(mixer), xdo, ydo, xds, yds);
> +       sti_mixer_reg_write(mixer, GAM_MIXER_AVO, ydo << 16 | xdo);
> +       sti_mixer_reg_write(mixer, GAM_MIXER_AVS, yds << 16 | xds);
> +
> +       sti_mixer_set_background_color(mixer, 0xFF, 0, 0);
> +
> +       sti_mixer_set_background_area(mixer, mode);
> +       sti_mixer_set_background_status(mixer, true);
> +       return 0;
> +}
> +
> +static u32 sti_mixer_get_layer_mask(struct sti_layer *layer)
> +{
> +       switch (layer->desc) {
> +       case STI_BACK:
> +               return GAM_CTL_BACK_MASK;
> +       case STI_GDP_0:
> +               return GAM_CTL_GDP0_MASK;
> +       case STI_GDP_1:
> +               return GAM_CTL_GDP1_MASK;
> +       case STI_GDP_2:
> +               return GAM_CTL_GDP2_MASK;
> +       case STI_GDP_3:
> +               return GAM_CTL_GDP3_MASK;
> +       case STI_VID_0:
> +               return GAM_CTL_VID0_MASK;
> +       case STI_VID_1:
> +               return GAM_CTL_VID1_MASK;
> +       default:
> +               return 0;
> +       }
> +}
> +
> +int sti_mixer_set_layer_status(struct sti_mixer *mixer,
> +                              struct sti_layer *layer, bool status)
> +{
> +       u32 mask, val;
> +
> +       mask = sti_mixer_get_layer_mask(layer);
> +       if (!mask) {
> +               DRM_ERROR("Can not find layer mask\n");
> +               return -EINVAL;
> +       }
> +
> +       val = sti_mixer_reg_read(mixer, GAM_MIXER_CTL);
> +       val &= ~mask;
> +       val |= status ? mask : 0;
> +       sti_mixer_reg_write(mixer, GAM_MIXER_CTL, val);
> +
> +       return 0;
> +}
> +
> +void sti_mixer_set_matrix(struct sti_mixer *mixer)
> +{
> +       unsigned int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(MixerColorSpaceMatIdentity); i++)
> +               sti_mixer_reg_write(mixer, GAM_MIXER_MX0 + (i * 4),
> +                                   MixerColorSpaceMatIdentity[i]);
> +}
> +
> +struct sti_mixer *sti_mixer_create(struct device *dev, int id,
> +                                  void __iomem *baseaddr)
> +{
> +       struct sti_mixer *mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
> +       struct device_node *np = dev->of_node;
> +
> +       dev_dbg(dev, "%s\n", __func__);
> +       if (!mixer) {
> +               DRM_ERROR("Failed to allocated memory for mixer\n");
> +               return NULL;
> +       }
> +       mixer->regs = baseaddr;
> +       mixer->dev = dev;
> +       mixer->id = id;
> +
> +       if (of_device_is_compatible(np, "st,stih416-compositor"))
> +               sti_mixer_set_matrix(mixer);
> +
> +       DRM_DEBUG_DRIVER("%s created. Regs=%p\n",
> +                        sti_mixer_to_str(mixer), mixer->regs);
> +
> +       return mixer;
> +}
> diff --git a/drivers/gpu/drm/sti/sti_mixer.h b/drivers/gpu/drm/sti/sti_mixer.h
> new file mode 100644
> index 0000000..2d86378
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_mixer.h
> @@ -0,0 +1,52 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *          Fabien Dessenne <fabien.dessenne@st.com>
> + *          for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef _STI_MIXER_H_
> +#define _STI_MIXER_H_
> +
> +#include <drm/drmP.h>
> +
> +#include "sti_layer.h"
> +
> +#define to_sti_mixer(x) container_of(x, struct sti_mixer, drm_crtc)
> +
> +/**
> + * STI Mixer subdevice structure
> + *
> + * @dev: driver device
> + * @regs: mixer registers
> + * @id: id of the mixer
> + * @drm_crtc: crtc object link to the mixer
> + */
> +struct sti_mixer {
> +       struct device *dev;
> +       void __iomem *regs;
> +       int id;
> +       struct drm_crtc drm_crtc;
> +};
> +
> +const char *sti_mixer_to_str(struct sti_mixer *mixer);
> +
> +struct sti_mixer *sti_mixer_create(struct device *dev, int id,
> +               void __iomem *baseaddr);
> +
> +int sti_mixer_set_layer_status(struct sti_mixer *mixer,
> +               struct sti_layer *layer, bool status);
> +int sti_mixer_set_layer_depth(struct sti_mixer *mixer, struct sti_layer *layer);
> +int sti_mixer_active_video_area(struct sti_mixer *mixer,
> +               struct drm_display_mode *mode);
> +
> +void sti_mixer_set_background_status(struct sti_mixer *mixer, bool enable);
> +
> +/* depth in Cross-bar control = z order */
> +#define GAM_MIXER_NB_DEPTH_LEVEL 7
> +
> +#define STI_MIXER_MAIN 0
> +#define STI_MIXER_AUX  1
> +
> +#endif
> --
> 1.9.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v4 07/11] drm: sti: add GDP layer
  2014-05-29  6:37 ` [PATCH v4 07/11] drm: sti: add GDP layer Benjamin Gaignard
@ 2014-06-11 19:15   ` Rob Clark
  0 siblings, 0 replies; 19+ messages in thread
From: Rob Clark @ 2014-06-11 19:15 UTC (permalink / raw)
  To: Benjamin Gaignard
  Cc: dri-devel, linaro-mm-sig, Linux Kernel Mailing List,
	David Airlie, Lee Jones

On Thu, May 29, 2014 at 2:37 AM, Benjamin Gaignard
<benjamin.gaignard@linaro.org> wrote:
> Generic Display Pipeline are one of the compositor input sub-devices.
> GDP are dedicated to graphic input like RGB plans.
> GDP is part of Compositor hardware block which will be introduce later.
>
> A sti_layer structure is used to abstract GDP calls from Compositor.
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
> ---
>  drivers/gpu/drm/sti/Makefile    |   3 +-
>  drivers/gpu/drm/sti/sti_gdp.c   | 482 ++++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sti/sti_gdp.h   |  73 ++++++
>  drivers/gpu/drm/sti/sti_layer.h | 111 +++++++++
>  4 files changed, 668 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/sti/sti_gdp.c
>  create mode 100644 drivers/gpu/drm/sti/sti_gdp.h
>  create mode 100644 drivers/gpu/drm/sti/sti_layer.h
>
> diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
> index dcc9568..1745697 100644
> --- a/drivers/gpu/drm/sti/Makefile
> +++ b/drivers/gpu/drm/sti/Makefile
> @@ -5,4 +5,5 @@ obj-$(CONFIG_DRM_STI) += \
>         sti_hdmi_tx3g0c55phy.o \
>         sti_hdmi_tx3g4c28phy.o \
>         sti_hda.o \
> -       sti_tvout.o
> \ No newline at end of file
> +       sti_tvout.o \
> +       sti_gdp.o
> \ No newline at end of file
> diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c
> new file mode 100644
> index 0000000..a014214
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_gdp.c
> @@ -0,0 +1,482 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *          Fabien Dessenne <fabien.dessenne@st.com>
> + *          for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/dma-mapping.h>
> +
> +#include "sti_gdp.h"
> +#include "sti_layer.h"
> +#include "sti_vtg.h"
> +
> +#define ENA_COLOR_FILL  BIT(8)
> +#define WAIT_NEXT_VSYNC BIT(31)
> +
> +/* GDP color formats */
> +#define GDP_RGB565      0x00
> +#define GDP_RGB888      0x01
> +#define GDP_RGB888_32   0x02
> +#define GDP_ARGB8565    0x04
> +#define GDP_ARGB8888    0x05
> +#define GDP_ARGB1555    0x06
> +#define GDP_ARGB4444    0x07
> +#define GDP_CLUT8       0x0B
> +#define GDP_YCBR888     0x10
> +#define GDP_YCBR422R    0x12
> +#define GDP_AYCBR8888   0x15
> +
> +#define GAM_GDP_CTL_OFFSET      0x00
> +#define GAM_GDP_AGC_OFFSET      0x04
> +#define GAM_GDP_VPO_OFFSET      0x0C
> +#define GAM_GDP_VPS_OFFSET      0x10
> +#define GAM_GDP_PML_OFFSET      0x14
> +#define GAM_GDP_PMP_OFFSET      0x18
> +#define GAM_GDP_SIZE_OFFSET     0x1C
> +#define GAM_GDP_NVN_OFFSET      0x24
> +#define GAM_GDP_KEY1_OFFSET     0x28
> +#define GAM_GDP_KEY2_OFFSET     0x2C
> +#define GAM_GDP_PPT_OFFSET      0x34
> +#define GAM_GDP_CML_OFFSET      0x3C
> +#define GAM_GDP_MST_OFFSET      0x68
> +
> +#define GAM_GDP_ALPHARANGE_255  BIT(5)
> +#define GAM_GDP_AGC_FULL_RANGE  0x00808080
> +#define GAM_GDP_PPT_IGNORE      (BIT(1) | BIT(0))
> +#define GAM_GDP_SIZE_MAX        0x7FF
> +
> +static const uint32_t gdp_supported_formats[] = {
> +       DRM_FORMAT_XRGB8888,
> +       DRM_FORMAT_ARGB8888,
> +       DRM_FORMAT_ARGB4444,
> +       DRM_FORMAT_ARGB1555,
> +       DRM_FORMAT_RGB565,
> +       DRM_FORMAT_RGB888,
> +       DRM_FORMAT_AYUV,
> +       DRM_FORMAT_YUV444,
> +       DRM_FORMAT_VYUY,
> +       DRM_FORMAT_C8,
> +};
> +
> +static const uint32_t *sti_gdp_get_formats(void)
> +{
> +       return gdp_supported_formats;
> +}
> +
> +static unsigned int sti_gdp_get_nb_formats(void)
> +{
> +       return ARRAY_SIZE(gdp_supported_formats);
> +}
> +
> +static int sti_gdp_fourcc2format(int fourcc)
> +{
> +       switch (fourcc) {
> +       case DRM_FORMAT_XRGB8888:
> +               return GDP_RGB888_32;
> +       case DRM_FORMAT_ARGB8888:
> +               return GDP_ARGB8888;
> +       case DRM_FORMAT_ARGB4444:
> +               return GDP_ARGB4444;
> +       case DRM_FORMAT_ARGB1555:
> +               return GDP_ARGB1555;
> +       case DRM_FORMAT_RGB565:
> +               return GDP_RGB565;
> +       case DRM_FORMAT_RGB888:
> +               return GDP_RGB888;
> +       case DRM_FORMAT_AYUV:
> +               return GDP_AYCBR8888;
> +       case DRM_FORMAT_YUV444:
> +               return GDP_YCBR888;
> +       case DRM_FORMAT_VYUY:
> +               return GDP_YCBR422R;
> +       case DRM_FORMAT_C8:
> +               return GDP_CLUT8;
> +       }
> +       return -1;
> +}
> +
> +static int sti_gdp_get_alpharange(int format)
> +{
> +       switch (format) {
> +       case GDP_ARGB8565:
> +       case GDP_ARGB8888:
> +       case GDP_AYCBR8888:
> +               return GAM_GDP_ALPHARANGE_255;
> +       }
> +       return 0;
> +}
> +
> +/**
> + * sti_gdp_get_free_nodes
> + * @layer: gdp layer
> + *
> + * Look for a GDP node list that is not currently read by the HW.
> + *
> + * RETURNS:
> + * Pointer to the free GDP node list
> + */
> +static struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_layer *layer)
> +{
> +       int hw_nvn;
> +       void *virt_nvn;
> +       struct sti_gdp *gdp = layer->gdp;
> +       unsigned int i;
> +
> +       hw_nvn = readl(gdp->regs + GAM_GDP_NVN_OFFSET);
> +       if (!hw_nvn)
> +               goto end;
> +
> +       virt_nvn = dma_to_virt(gdp->dev, (dma_addr_t) hw_nvn);
> +
> +       for (i = 0; i < GDP_NODE_NB_BANK; i++)
> +               if ((virt_nvn != gdp->node_list[i].btm_field) &&
> +                   (virt_nvn != gdp->node_list[i].top_field))
> +                       return &gdp->node_list[i];
> +

hmm, from looks of it, you don't expect to fall past the end of the
node list.. is that an error case, in which case a WARN_ON() might be
appropriate?  The 'goto end' part I'm less sure about (for different
generation of hw?) so not sure if the WARN should be after or before
the 'end' label.

> +end:
> +       return &gdp->node_list[0];
> +}
> +
> +/**
> + * sti_gdp_get_current_nodes
> + * @layer: GDP layer
> + *
> + * Look for GDP nodes that are currently read by the HW.
> + *
> + * RETURNS:
> + * Pointer to the current GDP node list
> + */
> +static
> +struct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_layer *layer)
> +{
> +       int hw_nvn;
> +       void *virt_nvn;
> +       struct sti_gdp *gdp = layer->gdp;
> +       unsigned int i;
> +
> +       hw_nvn = readl(gdp->regs + GAM_GDP_NVN_OFFSET);
> +       if (!hw_nvn)
> +               goto end;
> +
> +       virt_nvn = dma_to_virt(gdp->dev, (dma_addr_t) hw_nvn);
> +
> +       for (i = 0; i < GDP_NODE_NB_BANK; i++)
> +               if ((virt_nvn == gdp->node_list[i].btm_field) ||
> +                               (virt_nvn == gdp->node_list[i].top_field))
> +                       return &gdp->node_list[i];
> +
> +end:
> +       return NULL;
> +}
> +
> +/**
> + * sti_gdp_prepare_layer
> + * @lay: gdp layer
> + * @first_prepare: true if it is the first time this function is called
> + *
> + * Update the free GDP node list according to the layer properties.
> + *
> + * RETURNS:
> + * 0 on success.
> + */
> +static int sti_gdp_prepare_layer(void *lay, bool first_prepare)
> +{
> +       struct sti_layer *layer = (struct sti_layer *)lay;
> +       struct sti_gdp_node_list *list;
> +       struct sti_gdp_node *top_field, *btm_field;
> +       struct drm_display_mode *mode = layer->mode;
> +       struct device *dev = layer->gdp->dev;
> +       int format;
> +       unsigned int depth, bpp;
> +       int rate = mode->clock * 1000;
> +       int res;
> +       u32 ydo, xdo, yds, xds;
> +
> +       list = sti_gdp_get_free_nodes(layer);
> +       top_field = list->top_field;
> +       btm_field = list->btm_field;
> +
> +       /* Build the top field from layer params */
> +       top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE;
> +       top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC;
> +       format = sti_gdp_fourcc2format(layer->format);
> +       if (format == -1) {
> +               DRM_ERROR("Format not supported by GDP %.4s\n",
> +                         (char *)&layer->format);
> +               return 1;
> +       }
> +       top_field->gam_gdp_ctl |= format;
> +       top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format);
> +       top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE;
> +
> +       /* pixel memory location */
> +       drm_fb_get_bpp_depth(layer->format, &depth, &bpp);
> +       top_field->gam_gdp_pml = (u32) layer->paddr + layer->offsets[0];
> +       top_field->gam_gdp_pml += layer->src_x * (bpp >> 3);
> +       top_field->gam_gdp_pml += layer->src_y * layer->pitches[0];
> +
> +       /* input parameters */
> +       top_field->gam_gdp_pmp = layer->pitches[0];
> +       top_field->gam_gdp_size =
> +           clamp_val(layer->src_h, 0, GAM_GDP_SIZE_MAX) << 16 |
> +           clamp_val(layer->src_w, 0, GAM_GDP_SIZE_MAX);
> +
> +       /* output parameters */
> +       ydo = sti_vtg_get_line_number(*mode, layer->dst_y);
> +       yds = sti_vtg_get_line_number(*mode, layer->dst_y + layer->dst_h - 1);
> +       xdo = sti_vtg_get_pixel_number(*mode, layer->dst_x);
> +       xds = sti_vtg_get_pixel_number(*mode, layer->dst_x + layer->dst_w - 1);
> +       top_field->gam_gdp_vpo = (ydo << 16) | xdo;
> +       top_field->gam_gdp_vps = (yds << 16) | xds;
> +
> +       /* Same content and chained together */
> +       memcpy(btm_field, top_field, sizeof(*btm_field));
> +       top_field->gam_gdp_nvn = virt_to_dma(dev, btm_field);
> +       btm_field->gam_gdp_nvn = virt_to_dma(dev, top_field);
> +
> +       /* Interlaced mode */
> +       if (layer->mode->flags & DRM_MODE_FLAG_INTERLACE)
> +               btm_field->gam_gdp_pml = top_field->gam_gdp_pml +
> +                   layer->pitches[0];
> +
> +       if (first_prepare) {
> +               /* Set and enable gdp clock */
> +               if (layer->gdp->clk_pix) {
> +                       res = clk_set_rate(layer->gdp->clk_pix, rate);
> +                       if (res < 0) {
> +                               DRM_ERROR("Cannot set rate (%dHz) for gdp\n",
> +                                               rate);
> +                               return 1;
> +                       }
> +
> +                       if (clk_prepare_enable(layer->gdp->clk_pix)) {
> +                               DRM_ERROR("Failed to prepare/enable gdp\n");
> +                               return 1;
> +                       }
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * sti_gdp_commit_layer
> + * @lay: gdp layer
> + *
> + * Update the NVN field of the 'right' field of the current GDP node (being
> + * used by the HW) with the address of the updated ('free') top field GDP node.
> + * - In interlaced mode the 'right' field is the bottom field as we update
> + *   frames starting from their top field
> + * - In progressive mode, we update both bottom and top fields which are
> + *   equal nodes.
> + * At the next VSYNC, the updated node list will be used by the HW.
> + *
> + * RETURNS:
> + * 0 on success.
> + */
> +static int sti_gdp_commit_layer(void *lay)
> +{
> +       struct sti_layer *layer = (struct sti_layer *)lay;
> +       struct sti_gdp_node_list *updated_list = sti_gdp_get_free_nodes(layer);
> +       struct sti_gdp_node *updated_top_node = updated_list->top_field;
> +       struct sti_gdp_node *updated_btm_node = updated_list->btm_field;
> +       struct sti_gdp *gdp = layer->gdp;
> +       u32 dma_updated_top = virt_to_dma(gdp->dev, updated_top_node);
> +       u32 dma_updated_btm = virt_to_dma(gdp->dev, updated_btm_node);
> +       struct sti_gdp_node_list *curr_list = sti_gdp_get_current_nodes(layer);
> +
> +       dev_dbg(gdp->dev, "Current NVN:0x%X\n",
> +               readl(gdp->regs + GAM_GDP_NVN_OFFSET));
> +       dev_dbg(gdp->dev, "Posted buff: %lx current buff: %x\n",
> +               (unsigned long)layer->paddr,
> +               readl(gdp->regs + GAM_GDP_PML_OFFSET));
> +
> +       if (curr_list == NULL) {
> +               /* First update or invalid node should directly write in the
> +                * hw register */
> +               writel(gdp->is_curr_top == true ?
> +                               dma_updated_btm : dma_updated_top,
> +                               gdp->regs + GAM_GDP_NVN_OFFSET);
> +               return 0;
> +       }
> +
> +       if (layer->mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +               if (gdp->is_curr_top == true) {
> +                       /* Do not update in the middle of the frame, but
> +                        * postpone the update after the bottom field has
> +                        * been displayed */
> +                       curr_list->btm_field->gam_gdp_nvn = dma_updated_top;
> +               } else {
> +                       /* Direct update to avoid one frame delay */
> +                       writel(dma_updated_top, gdp->regs + GAM_GDP_NVN_OFFSET);
> +               }
> +       } else {
> +               /* Direct update for progressive to avoid one frame delay */
> +               writel(dma_updated_top, gdp->regs + GAM_GDP_NVN_OFFSET);
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * sti_gdp_disable_layer
> + * @lay: gdp layer
> + *
> + * Disable a GDP.
> + *
> + * RETURNS:
> + * 0 on success.
> + */
> +static int sti_gdp_disable_layer(void *lay)
> +{
> +       unsigned int i;
> +       struct sti_layer *layer = (struct sti_layer *)lay;
> +       struct sti_gdp *gdp = layer->gdp;
> +
> +       /* Set the nodes as 'to be ignored on mixer' */
> +       for (i = 0; i < GDP_NODE_NB_BANK; i++) {
> +               gdp->node_list[i].top_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE;
> +               gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE;
> +       }
> +
> +       if (gdp->clk_pix)
> +               clk_disable_unprepare(gdp->clk_pix);
> +
> +       return 0;
> +}
> +
> +/**
> + * sti_gdp_field_cb
> + * @nb: notifier block
> + * @event: event message
> + * @data: private data
> + *
> + * Handle VTG top field and bottom field event.
> + *
> + * RETURNS:
> + * 0 on success.
> + */
> +int sti_gdp_field_cb(struct notifier_block *nb,
> +               unsigned long event, void *data)
> +{
> +       struct sti_gdp *gdp = container_of(nb, struct sti_gdp, vtg_field_nb);
> +
> +       switch (event) {
> +       case VTG_TOP_FIELD_EVENT:
> +               gdp->is_curr_top = true;
> +               break;
> +       case VTG_BOTTOM_FIELD_EVENT:
> +               gdp->is_curr_top = false;
> +               break;
> +       default:
> +               DRM_ERROR("unsupported event: %lu\n", event);
> +               break;
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * sti_gdp_create
> + * @dev:      device
> + * @id:       gdp id
> + * @baseaddr: IO addr
> + *
> + * Create a gdp object. Allocate memory and initialize parameters.
> + *
> + *
> + * RETURNS:
> + * Pointer to the created gdp object.
> + */
> +struct sti_gdp *sti_gdp_create(struct device *dev, int id,
> +                              void __iomem *baseaddr)
> +{
> +       struct sti_gdp *gdp;
> +       struct device_node *np = dev->of_node;
> +       dma_addr_t dma;
> +       void *base;
> +       unsigned int i, size;
> +
> +       gdp = devm_kzalloc(dev, sizeof(*gdp), GFP_KERNEL);
> +       if (!gdp) {
> +               DRM_ERROR("Failed to allocate memory for GDP\n");
> +               return NULL;
> +       }
> +
> +       /* Allocate all the nodes within a single memory page */
> +       size = sizeof(struct sti_gdp_node) *
> +           GDP_NODE_PER_FIELD * GDP_NODE_NB_BANK;
> +
> +       base = dma_alloc_writecombine(dev, size, &dma, GFP_KERNEL | GFP_DMA);
> +       if (!base) {
> +               DRM_ERROR("Failed to allocate memory for GDP node\n");
> +               goto mem_err;
> +       }
> +       memset(base, 0, size);
> +
> +       for (i = 0; i < GDP_NODE_NB_BANK; i++) {
> +               if (virt_to_dma(dev, base) & 0xF) {
> +                       DRM_ERROR("Mem alignment failed\n");
> +                       goto mem_err;
> +               }
> +               gdp->node_list[i].top_field = base;
> +               DRM_DEBUG_DRIVER("node[%d].top_field=%p\n", i, base);
> +               base += sizeof(struct sti_gdp_node);
> +
> +               if (virt_to_dma(dev, base) & 0xF) {
> +                       DRM_ERROR("Mem alignment failed\n");
> +                       goto mem_err;
> +               }
> +               gdp->node_list[i].btm_field = base;
> +               DRM_DEBUG_DRIVER("node[%d].btm_field=%p\n", i, base);
> +               base += sizeof(struct sti_gdp_node);
> +       }
> +
> +       gdp->dev = dev;
> +       gdp->regs = baseaddr;
> +       gdp->get_formats = sti_gdp_get_formats;
> +       gdp->get_nb_formats = sti_gdp_get_nb_formats;
> +       gdp->prepare = sti_gdp_prepare_layer;
> +       gdp->commit = sti_gdp_commit_layer;
> +       gdp->disable = sti_gdp_disable_layer;

generally people like to split out the fxn ptrs to be in their own
_funcs struct, so it can be const.

BR,
-R

> +       gdp->vtg_field_nb.notifier_call = sti_gdp_field_cb;
> +
> +       if (of_device_is_compatible(np, "st,stih407-compositor")) {
> +               /* GDP of STiH407 chip have its own pixel clock */
> +               char *clk_name;
> +
> +               switch (id) {
> +               case STI_GDP_0:
> +                       clk_name = "pix_gdp1";
> +                       break;
> +               case STI_GDP_1:
> +                       clk_name = "pix_gdp2";
> +                       break;
> +               case STI_GDP_2:
> +                       clk_name = "pix_gdp3";
> +                       break;
> +               case STI_GDP_3:
> +                       clk_name = "pix_gdp4";
> +                       break;
> +               default:
> +                       DRM_ERROR("GDP id not recognized\n");
> +                       goto err_clk_pix_gdp;
> +               }
> +
> +               gdp->clk_pix = devm_clk_get(dev, clk_name);
> +               if (IS_ERR(gdp->clk_pix)) {
> +                       DRM_ERROR("Cannot get %s clock\n", clk_name);
> +                       goto err_clk_pix_gdp;
> +               }
> +       }
> +
> +       return gdp;
> +
> +err_clk_pix_gdp:
> +mem_err:
> +       devm_kfree(dev, gdp);
> +       return NULL;
> +}
> diff --git a/drivers/gpu/drm/sti/sti_gdp.h b/drivers/gpu/drm/sti/sti_gdp.h
> new file mode 100644
> index 0000000..3618039
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_gdp.h
> @@ -0,0 +1,73 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *          Fabien Dessenne <fabien.dessenne@st.com>
> + *          for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef _STI_GDP_H_
> +#define _STI_GDP_H_
> +
> +#include <linux/types.h>
> +
> +#define GDP_NODE_NB_BANK       2
> +#define GDP_NODE_PER_FIELD     2
> +
> +struct sti_gdp_node {
> +       u32 gam_gdp_ctl;
> +       u32 gam_gdp_agc;
> +       u32 reserved1;
> +       u32 gam_gdp_vpo;
> +       u32 gam_gdp_vps;
> +       u32 gam_gdp_pml;
> +       u32 gam_gdp_pmp;
> +       u32 gam_gdp_size;
> +       u32 reserved2;
> +       u32 gam_gdp_nvn;
> +       u32 gam_gdp_key1;
> +       u32 gam_gdp_key2;
> +       u32 reserved3;
> +       u32 gam_gdp_ppt;
> +       u32 reserved4;
> +       u32 gam_gdp_cml;
> +};
> +
> +struct sti_gdp_node_list {
> +       struct sti_gdp_node *top_field;
> +       struct sti_gdp_node *btm_field;
> +};
> +
> +/*
> + * STI GDP structure
> + *
> + * @device:            driver device
> + * @regs:               subdevice register
> + * @clk_pix:            pixel clock for the current gdp
> + * @vtg_field_nb:       callback for VTG FIELD (top or bottom) notification
> + * @is_curr_top:        true if the current node processed is the top field
> + * @get_formats:       get GDP supported formats
> + * @get_nb_formats:    get number of format supported
> + * @prepare:           prepare GDP before rendering
> + * @commit:            set GDP for rendering
> + * @disable:           disable GDP
> + * @node_list:         array of node list
> + */
> +struct sti_gdp {
> +       struct device *dev;
> +       void __iomem *regs;
> +       struct clk *clk_pix;
> +       struct notifier_block vtg_field_nb;
> +       bool is_curr_top;
> +       const uint32_t* (*get_formats)(void);
> +       unsigned int (*get_nb_formats)(void);
> +       int (*prepare)(void *layer, bool first_prepare);
> +       int (*commit)(void *layer);
> +       int (*disable)(void *layer);
> +       struct sti_gdp_node_list node_list[GDP_NODE_NB_BANK];
> +};
> +
> +struct sti_gdp *sti_gdp_create(struct device *dev, int id,
> +               void __iomem *baseaddr);
> +
> +#endif
> diff --git a/drivers/gpu/drm/sti/sti_layer.h b/drivers/gpu/drm/sti/sti_layer.h
> new file mode 100644
> index 0000000..7ba5a40
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_layer.h
> @@ -0,0 +1,111 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *          Fabien Dessenne <fabien.dessenne@st.com>
> + *          for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef _STI_LAYER_H_
> +#define _STI_LAYER_H_
> +
> +#include <drm/drmP.h>
> +#include "sti_gdp.h"
> +
> +#define to_sti_layer(x) container_of(x, struct sti_layer, plane)
> +
> +#define STI_LAYER_TYPE_SHIFT 8
> +#define STI_LAYER_TYPE_MASK (~((1<<STI_LAYER_TYPE_SHIFT)-1))
> +
> +
> +enum sti_layer_type {
> +       STI_GDP = 1 << STI_LAYER_TYPE_SHIFT,
> +       STI_VID = 2 << STI_LAYER_TYPE_SHIFT,
> +       STI_CUR = 3 << STI_LAYER_TYPE_SHIFT,
> +       STI_BCK = 4 << STI_LAYER_TYPE_SHIFT
> +};
> +
> +enum sti_layer_id_of_type {
> +       STI_ID_0 = 0,
> +       STI_ID_1 = 1,
> +       STI_ID_2 = 2,
> +       STI_ID_3 = 3
> +};
> +
> +enum sti_layer_desc {
> +       STI_GDP_0       = STI_GDP | STI_ID_0,
> +       STI_GDP_1       = STI_GDP | STI_ID_1,
> +       STI_GDP_2       = STI_GDP | STI_ID_2,
> +       STI_GDP_3       = STI_GDP | STI_ID_3,
> +       STI_VID_0       = STI_VID | STI_ID_0,
> +       STI_VID_1       = STI_VID | STI_ID_1,
> +       STI_CURSOR      = STI_CUR,
> +       STI_BACK        = STI_BCK
> +};
> +
> +struct sti_fps_info {
> +       bool output;
> +       unsigned int curr_frame_counter;
> +       unsigned int last_frame_counter;
> +       struct timespec last_timestamp;
> +};
> +
> +/*
> + * STI layer structure
> + *
> + * @plane:              drm plane it is bound to (if any)
> + * @fb:                 drm fb it is bound to
> + * @mode:               display mode
> + * @desc:               layer type & id
> + * @zorder:             layer z-order
> + * @mixer_id:           id of the mixer used to display the layer
> + * @enabled:            to know if the layer is active or not
> + * @src_x src_y:        coordinates of the input (fb) area
> + * @src_w src_h:        size of the input (fb) area
> + * @dst_x dst_y:        coordinates of the output (crtc) area
> + * @dst_w dst_h:        size of the output (crtc) area
> + * @format:             format
> + * @pitches:            pitch of 'planes' (eg: Y, U, V)
> + * @offsets:            offset of 'planes'
> + * @paddr:              physical address of the input buffer
> + * @fps_info:           frame per second info
> + * @gdp:                related GDP (if the layer is a GDP)
> + */
> +struct sti_layer {
> +       struct drm_plane plane;
> +       struct drm_framebuffer *fb;
> +       struct drm_display_mode *mode;
> +       enum sti_layer_desc desc;
> +       int zorder;
> +       int mixer_id;
> +       bool enabled;
> +       int src_x, src_y;
> +       int src_w, src_h;
> +       int dst_x, dst_y;
> +       int dst_w, dst_h;
> +       uint32_t format;
> +       unsigned int pitches[4];
> +       unsigned int offsets[4];
> +       dma_addr_t paddr;
> +       struct sti_fps_info fps_info;
> +       struct sti_gdp *gdp;
> +};
> +
> +struct sti_layer *sti_layer_create(struct device *dev, int desc,
> +                       void __iomem *baseaddr);
> +int sti_layer_prepare(struct sti_layer *layer, struct drm_framebuffer *fb,
> +                       struct drm_display_mode *mode,
> +                       int mixer_id,
> +                       int dest_x, int dest_y,
> +                       int dest_w, int dest_h,
> +                       int src_x, int src_y,
> +                       int src_w, int src_h);
> +int sti_layer_commit(struct sti_layer *layer);
> +int sti_layer_disable(struct sti_layer *layer);
> +const uint32_t *sti_layer_get_formats(struct sti_layer *layer);
> +unsigned int sti_layer_get_nb_formats(struct sti_layer *layer);
> +struct sti_layer *sti_layer_find_layer(struct sti_layer *layer[],
> +                       enum sti_layer_desc desc);
> +const char *sti_layer_to_str(struct sti_layer *layer);
> +
> +#endif
> --
> 1.9.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v4 11/11] drm: sti: Add DRM driver itself
  2014-05-29  6:37 ` [PATCH v4 11/11] drm: sti: Add DRM driver itself Benjamin Gaignard
@ 2014-06-11 20:06   ` Rob Clark
  0 siblings, 0 replies; 19+ messages in thread
From: Rob Clark @ 2014-06-11 20:06 UTC (permalink / raw)
  To: Benjamin Gaignard
  Cc: dri-devel, linaro-mm-sig, Linux Kernel Mailing List,
	David Airlie, Lee Jones

On Thu, May 29, 2014 at 2:37 AM, Benjamin Gaignard
<benjamin.gaignard@linaro.org> wrote:
> Make the link between all the hardware drivers and DRM/KMS interface.
> Create the driver itself and make it register all the sub-components.
> Use GEM CMA helpers for buffer allocation.
>
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
> ---
>  drivers/gpu/drm/sti/Kconfig             |   8 +
>  drivers/gpu/drm/sti/Makefile            |   8 +-
>  drivers/gpu/drm/sti/sti_compositor.c    |  47 +++-
>  drivers/gpu/drm/sti/sti_drm_connector.c | 114 +++++++++
>  drivers/gpu/drm/sti/sti_drm_connector.h |   4 +
>  drivers/gpu/drm/sti/sti_drm_crtc.c      | 436 ++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/sti/sti_drm_crtc.h      |  21 ++
>  drivers/gpu/drm/sti/sti_drm_drv.c       | 229 +++++++++++++++++
>  drivers/gpu/drm/sti/sti_drm_drv.h       |  35 +++
>  drivers/gpu/drm/sti/sti_drm_encoder.c   | 108 ++++++++
>  drivers/gpu/drm/sti/sti_drm_encoder.h   |   3 +
>  drivers/gpu/drm/sti/sti_drm_plane.c     | 192 ++++++++++++++
>  drivers/gpu/drm/sti/sti_drm_plane.h     |  17 ++
>  drivers/gpu/drm/sti/sti_hda.c           |   4 +
>  drivers/gpu/drm/sti/sti_hdmi.c          |   4 +
>  drivers/gpu/drm/sti/sti_tvout.c         |   9 +
>  16 files changed, 1237 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_connector.c
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_crtc.c
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_crtc.h
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_drv.c
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_drv.h
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_encoder.c
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_plane.c
>  create mode 100644 drivers/gpu/drm/sti/sti_drm_plane.h
>
> diff --git a/drivers/gpu/drm/sti/Kconfig b/drivers/gpu/drm/sti/Kconfig
> index 1013570..bb48696 100644
> --- a/drivers/gpu/drm/sti/Kconfig
> +++ b/drivers/gpu/drm/sti/Kconfig
> @@ -1,6 +1,14 @@
>  config DRM_STI
>         bool "DRM Support for STMicroelectronics SoC stiH41x Series"
>         depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM)
> +       select DRM_KMS_HELPER
> +       select DRM_GEM_CMA_HELPER
>         select DRM_KMS_CMA_HELPER
>         help
>           Choose this option to enable DRM on STM stiH41x chipset
> +
> +config DRM_STI_FBDEV
> +       bool "DRM frame buffer device for STMicroelectronics SoC stiH41x Serie"
> +       depends on DRM_STI
> +       help
> +         Choose this option to enable FBDEV on top of DRM for STM stiH41x chipset
> diff --git a/drivers/gpu/drm/sti/Makefile b/drivers/gpu/drm/sti/Makefile
> index a0612cc..e938028 100644
> --- a/drivers/gpu/drm/sti/Makefile
> +++ b/drivers/gpu/drm/sti/Makefile
> @@ -7,7 +7,13 @@ obj-$(CONFIG_DRM_STI) += \
>         sti_hdmi_tx3g4c28phy.o \
>         sti_hda.o \
>         sti_tvout.o \
> +       sti_drm_crtc.o \
> +       sti_drm_plane.o \
> +       sti_drm_connector.o \
> +       sti_drm_encoder.o \
> +       sti_hda.o \
>         sti_layer.o \
>         sti_mixer.o \
>         sti_gdp.o \
> -       sti_vid.o
> \ No newline at end of file
> +       sti_vid.o \
> +       sti_drm_drv.o
> diff --git a/drivers/gpu/drm/sti/sti_compositor.c b/drivers/gpu/drm/sti/sti_compositor.c
> index 3100917..d95da89 100644
> --- a/drivers/gpu/drm/sti/sti_compositor.c
> +++ b/drivers/gpu/drm/sti/sti_compositor.c
> @@ -14,6 +14,9 @@
>  #include <drm/drmP.h>
>
>  #include "sti_compositor.h"
> +#include "sti_drm_crtc.h"
> +#include "sti_drm_drv.h"
> +#include "sti_drm_plane.h"
>  #include "sti_gdp.h"
>  #include "sti_vtg.h"
>
> @@ -84,7 +87,48 @@ static int sti_compositor_init_subdev(struct sti_compositor *compo,
>  static int sti_compositor_bind(struct device *dev, struct device *master,
>         void *data)
>  {
> -       /* to be filled with drm driver functions */
> +       struct sti_compositor *compo = dev_get_drvdata(dev);
> +       struct drm_device *drm_dev = data;
> +       unsigned int i, crtc = 0, plane = 0;
> +       struct sti_drm_private *dev_priv = drm_dev->dev_private;
> +
> +       dev_priv->compo = compo;
> +       INIT_LIST_HEAD(&dev_priv->pageflip_evt_list);
> +
> +       for (i = 0; i < compo->nb_mixers; i++) {
> +               if (compo->mixer[i]) {
> +                       sti_drm_crtc_init(drm_dev, compo->mixer[i]);
> +                       crtc++;
> +               }
> +       }
> +       if (crtc == 0) {
> +               DRM_ERROR("No CRTC available\n");
> +               return 1;
> +       }
> +
> +       drm_vblank_init(drm_dev, crtc);
> +       /* Allow usage of vblank without having to call drm_irq_install */
> +       drm_dev->irq_enabled = 1;
> +
> +       for (i = 0; i < compo->nb_layers; i++) {
> +               if (compo->layer[i]) {
> +                       /* Create planes for GDP.
> +                        * except GDP0 as it is reserved for CRTC FB */
> +                       enum sti_layer_desc desc = compo->layer[i]->desc;
> +                       enum sti_layer_type type = desc & STI_LAYER_TYPE_MASK;
> +
> +                       if ((type == STI_GDP) && (desc != STI_GDP_0)) {
> +                               sti_drm_plane_init(drm_dev, compo->layer[i],
> +                                                  (1 << crtc) - 1);
> +                               plane++;
> +                       }
> +               }
> +       }
> +
> +       DRM_DEBUG_DRIVER("Initialized %d DRM CRTC(s) and %d DRM plane(s)\n",
> +                        crtc, plane);
> +       DRM_DEBUG_DRIVER("DRM plane(s) for VID/VDP not created yet\n");
> +
>         return 0;
>  }
>
> @@ -127,6 +171,7 @@ static int sti_compositor_probe(struct platform_device *pdev)
>                 return -ENOMEM;
>         }
>         compo->dev = dev;
> +       compo->vtg_vblank_nb.notifier_call = sti_drm_crtc_vblank_cb;
>
>         /* populate data structure depending on compatibility */
>         BUG_ON(!of_match_node(compositor_of_match, np)->data);
> diff --git a/drivers/gpu/drm/sti/sti_drm_connector.c b/drivers/gpu/drm/sti/sti_drm_connector.c
> new file mode 100644
> index 0000000..a3ab81d
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_drm_connector.c
> @@ -0,0 +1,114 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "sti_drm_connector.h"
> +
> +static int sti_drm_connector_get_modes(struct drm_connector *connector)
> +{
> +       struct sti_connector *sti_connector = to_sti_connector(connector);
> +
> +       if (sti_connector)

note that at least for the kms fxns, you don't have to worry about
being called with a null connector.  But if that were not the case you
should check for null on the connector, not sti_connector (in case
'struct drm_connector' were not the first element in the struct)

> +               if (sti_connector->detect)
> +                       return sti_connector->get_modes(connector);

that at least looks like a bug (check 'detect' then call 'get_modes').

also, this extra level of indirection looks funny in the first place..
why is connector->funcs->get_modes not the same as
sti_connector->get_modes directly, ie just have different sets of
drm_connector_funcs for each connector type you have.

Same comments again below.

> +
> +       return 0;
> +}
> +
> +static int sti_drm_connector_mode_valid(struct drm_connector *connector,
> +                                       struct drm_display_mode *mode)
> +{
> +       struct sti_connector *sti_connector = to_sti_connector(connector);
> +
> +       if (sti_connector)
> +               if (sti_connector->detect)
> +                       return sti_connector->check_mode(connector, mode);
> +
> +       return MODE_BAD;
> +}
> +
> +struct drm_encoder *sti_drm_best_encoder(struct drm_connector *connector)
> +{
> +       struct sti_connector *sti_connector = to_sti_connector(connector);
> +
> +       /* Best encoder is the one associated during connector creation */
> +       return sti_connector->encoder;
> +}
> +
> +static struct drm_connector_helper_funcs sti_drm_connector_helper_funcs = {
> +       .get_modes = sti_drm_connector_get_modes,
> +       .mode_valid = sti_drm_connector_mode_valid,
> +       .best_encoder = sti_drm_best_encoder,
> +};
> +
> +static void sti_drm_connector_dpms(struct drm_connector *connector, int mode)
> +{
> +       drm_helper_connector_dpms(connector, mode);
> +}
> +
> +/* get detection status of display device */
> +static enum drm_connector_status
> +sti_drm_connector_detect(struct drm_connector *connector, bool force)
> +{
> +       struct sti_connector *sti_connector = to_sti_connector(connector);
> +
> +       if (sti_connector)
> +               if (sti_connector->detect)
> +                       return sti_connector->detect(connector);
> +
> +       return connector_status_disconnected;
> +}
> +
> +static void sti_drm_connector_destroy(struct drm_connector *connector)
> +{
> +       struct sti_connector *sti_connector = to_sti_connector(connector);
> +
> +       drm_sysfs_connector_remove(connector);
> +       drm_connector_cleanup(connector);
> +       kfree(sti_connector);
> +}
> +
> +static struct drm_connector_funcs sti_drm_connector_funcs = {
> +       .dpms = sti_drm_connector_dpms,
> +       .fill_modes = drm_helper_probe_single_connector_modes,
> +       .detect = sti_drm_connector_detect,
> +       .destroy = sti_drm_connector_destroy,
> +};
> +
> +struct drm_connector *sti_drm_connector_init(struct drm_device *dev,
> +                                       struct drm_encoder *encoder,
> +                                       struct sti_connector *sti_connector,
> +                                       int type)
> +{
> +       struct drm_connector *connector = (struct drm_connector *)sti_connector;
> +       int err;
> +
> +       if (type == DRM_MODE_CONNECTOR_HDMIA)
> +               connector->polled = DRM_CONNECTOR_POLL_HPD;
> +
> +       drm_connector_init(dev, connector, &sti_drm_connector_funcs, type);
> +       drm_connector_helper_add(connector, &sti_drm_connector_helper_funcs);
> +
> +       err = drm_sysfs_connector_add(connector);
> +       if (err)
> +               goto err_connector;
> +
> +       err = drm_mode_connector_attach_encoder(connector, encoder);
> +       if (err) {
> +               DRM_ERROR("Failed to attach a connector to a encoder\n");
> +               goto err_sysfs;
> +       }
> +
> +       return connector;
> +
> +err_sysfs:
> +       drm_sysfs_connector_remove(connector);
> +err_connector:
> +       drm_connector_cleanup(connector);
> +       return NULL;
> +}
> diff --git a/drivers/gpu/drm/sti/sti_drm_connector.h b/drivers/gpu/drm/sti/sti_drm_connector.h
> index 798bd5c..b9b9d09 100644
> --- a/drivers/gpu/drm/sti/sti_drm_connector.h
> +++ b/drivers/gpu/drm/sti/sti_drm_connector.h
> @@ -48,4 +48,8 @@ struct sti_connector {
>
>  #define to_sti_connector(x) container_of(x, struct sti_connector, drm_connector)
>
> +struct drm_connector *sti_drm_connector_init(struct drm_device *dev,
> +                                       struct drm_encoder *encoder,
> +                                       struct sti_connector *connector,
> +                                       int type);
>  #endif
> diff --git a/drivers/gpu/drm/sti/sti_drm_crtc.c b/drivers/gpu/drm/sti/sti_drm_crtc.c
> new file mode 100644
> index 0000000..7350a67
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_drm_crtc.c
> @@ -0,0 +1,436 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *          Fabien Dessenne <fabien.dessenne@st.com>
> + *          for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <linux/clk.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "sti_compositor.h"
> +#include "sti_drm_drv.h"
> +#include "sti_drm_crtc.h"
> +#include "sti_vtg.h"
> +
> +static void sti_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
> +{
> +       DRM_DEBUG_KMS("\n");
> +}
> +
> +static void sti_drm_crtc_prepare(struct drm_crtc *crtc)
> +{
> +       struct sti_mixer *mixer = to_sti_mixer(crtc);
> +       struct device *dev = mixer->dev;
> +       struct sti_compositor *compo = dev_get_drvdata(dev);
> +
> +       compo->enable = true;
> +
> +       /* Prepare and enable the compo IP clock */
> +       if (mixer->id == STI_MIXER_MAIN) {
> +               if (clk_prepare_enable(compo->clk_compo_main))
> +                       DRM_INFO("Failed to prepare/enable compo_main clk\n");
> +       } else {
> +               if (clk_prepare_enable(compo->clk_compo_aux))
> +                       DRM_INFO("Failed to prepare/enable compo_aux clk\n");
> +       }
> +}
> +
> +static void sti_drm_crtc_commit(struct drm_crtc *crtc)
> +{
> +       struct sti_mixer *mixer = to_sti_mixer(crtc);
> +       struct device *dev = mixer->dev;
> +       struct sti_compositor *compo = dev_get_drvdata(dev);
> +       struct sti_layer *layer;
> +
> +       if ((!mixer || !compo)) {
> +               DRM_ERROR("Can not find mixer or compositor)\n");
> +               return;
> +       }
> +
> +       /* Find GDP0 which is reserved to the CRTC FB */
> +       layer = sti_layer_find_layer(compo->layer, STI_GDP_0);
> +       if (layer)
> +               sti_layer_commit(layer);
> +       else
> +               DRM_ERROR("Can not find CRTC dedicated plane (GDP0)\n");
> +
> +       /* Enable layer on mixer */
> +       if (sti_mixer_set_layer_status(mixer, layer, true))
> +               DRM_ERROR("Can not enable layer at mixer\n");
> +}
> +
> +static bool sti_drm_crtc_mode_fixup(struct drm_crtc *crtc,
> +                                   const struct drm_display_mode *mode,
> +                                   struct drm_display_mode *adjusted_mode)
> +{
> +       /* accept the provided drm_display_mode, do not fix it up */
> +       return true;
> +}
> +
> +static int
> +sti_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
> +                     struct drm_display_mode *adjusted_mode, int x, int y,
> +                     struct drm_framebuffer *old_fb)
> +{
> +       struct sti_mixer *mixer = to_sti_mixer(crtc);
> +       struct device *dev = mixer->dev;
> +       struct sti_compositor *compo = dev_get_drvdata(dev);
> +       struct sti_layer *layer;
> +       struct clk *clk;
> +       int rate = mode->clock * 1000;
> +       int res;
> +       unsigned int w, h;
> +
> +       DRM_DEBUG_KMS("CRTC:%d (%s) fb:%d mode:%d (%s)\n",
> +                     crtc->base.id, sti_mixer_to_str(mixer),
> +                     crtc->fb->base.id, mode->base.id, mode->name);
> +
> +       DRM_DEBUG_KMS("%d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
> +                     mode->vrefresh, mode->clock,
> +                     mode->hdisplay,
> +                     mode->hsync_start, mode->hsync_end,
> +                     mode->htotal,
> +                     mode->vdisplay,
> +                     mode->vsync_start, mode->vsync_end,
> +                     mode->vtotal, mode->type, mode->flags);
> +
> +       /* Set rate and prepare/enable pixel clock */
> +       if (mixer->id == STI_MIXER_MAIN)
> +               clk = compo->clk_pix_main;
> +       else
> +               clk = compo->clk_pix_aux;
> +
> +       res = clk_set_rate(clk, rate);
> +       if (res < 0) {
> +               DRM_ERROR("Cannot set rate (%dHz) for pix clk\n", rate);
> +               return -EINVAL;
> +       }
> +       if (clk_prepare_enable(clk)) {
> +               DRM_ERROR("Failed to prepare/enable pix clk\n");
> +               return -EINVAL;
> +       }
> +
> +       sti_vtg_set_config(mixer->id == STI_MIXER_MAIN ?
> +                       compo->vtg_main : compo->vtg_aux, &crtc->mode);
> +
> +       /* GDP0 is reserved to the CRTC FB */
> +       layer = sti_layer_find_layer(compo->layer, STI_GDP_0);
> +       if (!layer) {
> +               DRM_ERROR("Can not find GDP0)\n");
> +               return -EINVAL;
> +       }
> +
> +       /* copy the mode data adjusted by mode_fixup() into crtc->mode
> +        * so that hardware can be set to proper mode
> +        */
> +       memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
> +
> +       res = sti_mixer_set_layer_depth(mixer, layer);
> +       if (res) {
> +               DRM_ERROR("Can not set layer depth\n");
> +               return -EINVAL;
> +       }
> +       res = sti_mixer_active_video_area(mixer, &crtc->mode);
> +       if (res) {
> +               DRM_ERROR("Can not set active video area\n");
> +               return -EINVAL;
> +       }
> +
> +       if ((mode->hdisplay != crtc->fb->width) ||
> +           (mode->vdisplay != crtc->fb->height))
> +               DRM_DEBUG_KMS("WARNING: fb and display mode sizes differ\n");

this should not be a warning.  Since exactly this will happen if you
have x11 with multiple monitors (one big fb, with two crtc's scanning
out at different offsets)

> +
> +       w = crtc->fb->width - x;
> +       h = crtc->fb->height - y;
> +
> +       if ((w <= 0) || (h <= 0)) {
> +               DRM_ERROR("Coordinates outside FB\n");
> +               return -EINVAL;
> +       }

drm core should reject anything for you if fb is not big enough

> +
> +       return sti_layer_prepare(layer, crtc->fb, &crtc->mode, mixer->id,
> +                       0, 0, w, h, x, y, w, h);
> +}
> +
> +static int sti_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
> +                                     struct drm_framebuffer *old_fb)
> +{
> +       struct sti_mixer *mixer = to_sti_mixer(crtc);
> +       struct device *dev = mixer->dev;
> +       struct sti_compositor *compo = dev_get_drvdata(dev);
> +       struct sti_layer *layer;
> +       unsigned int w, h;
> +       int ret;
> +
> +       DRM_DEBUG_KMS("CRTC:%d (%s) fb:%d (%d,%d)\n",
> +                     crtc->base.id, sti_mixer_to_str(mixer),
> +                     crtc->fb->base.id, x, y);
> +
> +       /* GDP0 is reserved to the CRTC FB */
> +       layer = sti_layer_find_layer(compo->layer, STI_GDP_0);
> +       if (!layer) {
> +               DRM_ERROR("Can not find GDP0)\n");
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +
> +       w = crtc->fb->width - crtc->x;
> +       h = crtc->fb->height - crtc->y;
> +
> +       if ((w <= 0) || (h <= 0)) {
> +               DRM_ERROR("Coordinates outside FB\n");

here too, this should be unnecessary


> +               ret = -EINVAL;
> +               goto out;
> +       }
> +
> +       ret = sti_layer_prepare(layer, crtc->fb, &crtc->mode, mixer->id,
> +                               0, 0, w, h,
> +                               crtc->x, crtc->y, w, h);
> +       if (ret) {
> +               DRM_ERROR("Can not prepare layer\n");
> +               goto out;
> +       }
> +
> +       sti_drm_crtc_commit(crtc);
> +out:
> +       return ret;
> +}
> +
> +static void sti_drm_crtc_load_lut(struct drm_crtc *crtc)
> +{
> +       /* do nothing */
> +}
> +
> +static void sti_drm_crtc_disable(struct drm_crtc *crtc)
> +{
> +       struct sti_mixer *mixer = to_sti_mixer(crtc);
> +       struct device *dev = mixer->dev;
> +       struct sti_compositor *compo = dev_get_drvdata(dev);
> +       struct sti_layer *layer;
> +
> +       if (!compo->enable)
> +               return;
> +
> +       DRM_DEBUG_KMS("CRTC:%d (%s)\n", crtc->base.id, sti_mixer_to_str(mixer));
> +
> +       /* Disable Background */
> +       sti_mixer_set_background_status(mixer, false);
> +
> +       /* Disable GDP0 */
> +       layer = sti_layer_find_layer(compo->layer, STI_GDP_0);
> +       if (!layer) {
> +               DRM_ERROR("Cannot find GDP0\n");
> +               return;
> +       }
> +
> +       /* Disable layer at mixer level */
> +       if (sti_mixer_set_layer_status(mixer, layer, false))
> +               DRM_ERROR("Can not disable %s layer at mixer\n",
> +                               sti_layer_to_str(layer));
> +
> +       /* Wait a while to be sure that a Vsync event is received */
> +       msleep(WAIT_NEXT_VSYNC_MS);

well, I guess you are going to want to do better than msleep eventually ;-)

> +
> +       /* Then disable layer itself */
> +       sti_layer_disable(layer);
> +
> +       drm_vblank_off(crtc->dev, mixer->id);
> +
> +       /* Disable pixel clock and compo IP clocks */
> +       if (mixer->id == STI_MIXER_MAIN) {
> +               clk_disable_unprepare(compo->clk_pix_main);
> +               clk_disable_unprepare(compo->clk_compo_main);
> +       } else {
> +               clk_disable_unprepare(compo->clk_pix_aux);
> +               clk_disable_unprepare(compo->clk_compo_aux);
> +       }
> +
> +       compo->enable = false;
> +}
> +
> +static struct drm_crtc_helper_funcs sti_crtc_helper_funcs = {
> +       .dpms = sti_drm_crtc_dpms,
> +       .prepare = sti_drm_crtc_prepare,
> +       .commit = sti_drm_crtc_commit,
> +       .mode_fixup = sti_drm_crtc_mode_fixup,
> +       .mode_set = sti_drm_crtc_mode_set,
> +       .mode_set_base = sti_drm_crtc_mode_set_base,
> +       .load_lut = sti_drm_crtc_load_lut,
> +       .disable = sti_drm_crtc_disable,
> +};
> +
> +static int sti_drm_crtc_page_flip(struct drm_crtc *crtc,
> +                                 struct drm_framebuffer *fb,
> +                                 struct drm_pending_vblank_event *event,
> +                                 uint32_t page_flip_flags)
> +{
> +       struct drm_device *drm_dev = crtc->dev;
> +       struct sti_drm_private *dev_priv = drm_dev->dev_private;
> +       struct drm_framebuffer *old_fb;
> +       struct sti_mixer *mixer = to_sti_mixer(crtc);
> +       unsigned long flags;
> +       int ret;
> +
> +       DRM_DEBUG_KMS("fb %d --> fb %d\n", crtc->fb->base.id, fb->base.id);
> +
> +       mutex_lock(&drm_dev->struct_mutex);
> +
> +       old_fb = crtc->fb;
> +       crtc->fb = fb;
> +       ret = sti_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb);
> +       if (ret) {
> +               DRM_ERROR("failed\n");
> +               crtc->fb = old_fb;
> +               goto out;
> +       }
> +
> +       if (event) {
> +               event->pipe = mixer->id;
> +
> +               ret = drm_vblank_get(drm_dev, event->pipe);
> +               if (ret) {
> +                       DRM_ERROR("Cannot get vblank\n");
> +                       goto out;
> +               }
> +
> +               spin_lock_irqsave(&drm_dev->event_lock, flags);
> +               list_add_tail(&event->base.link, &dev_priv->pageflip_evt_list);
> +               spin_unlock_irqrestore(&drm_dev->event_lock, flags);

Well, other drivers behavior (and current userspace expectation) is to
not queue up multiple pending flips.. if you try pageflip on other
drivers while they still have a flip pending, they would return
-EBUSY.

Maybe it is useful to allow queuing as an extension.  I'd really
rather get atomic in place first, and then see if we could come up
with a why to validate hypothetical future states.  Otherwise, once
you are queuing up multiple updates which change overlay layers, it
gets tricky to properly error-check the N+2'th state.. ie. a change
that hasn't taken effect yet for update N+1 could cause N+2 to fail.
So the error checking you did before returning 0 to userspace for N+2
is not necessarily valid.

pre-atomic, you could hypothetically have the same problem if you
don't block a new setcrtc until pending flips have happened, since the
N+2 fb dimensions would be checked against the pre-setcrtc state.

I won't go so far as NAK'ing queuing, but my gut feeling is you are
setting yourself up for problems with atomic.  And, well, others may
NAK it ;-)


> +       }
> +out:
> +       mutex_unlock(&drm_dev->struct_mutex);
> +       return ret;
> +}
> +
> +static void sti_drm_crtc_destroy(struct drm_crtc *crtc)
> +{
> +       DRM_DEBUG_KMS("\n");
> +       drm_crtc_cleanup(crtc);
> +}
> +
> +static int sti_drm_crtc_set_property(struct drm_crtc *crtc,
> +                                    struct drm_property *property,
> +                                    uint64_t val)
> +{
> +       DRM_DEBUG_KMS("\n");
> +       return 0;
> +}
> +
> +int sti_drm_crtc_vblank_cb(struct notifier_block *nb,
> +                          unsigned long event, void *data)
> +{
> +       struct drm_device *drm_dev;
> +       struct sti_compositor *compo =
> +               container_of(nb, struct sti_compositor, vtg_vblank_nb);
> +       int *crtc = data;
> +       unsigned long flags;
> +       struct drm_pending_vblank_event *e, *t;
> +       struct sti_drm_private *priv;
> +
> +       drm_dev = compo->mixer[*crtc]->drm_crtc.dev;
> +       priv = drm_dev->dev_private;
> +
> +       if ((event != VTG_TOP_FIELD_EVENT) &&
> +           (event != VTG_BOTTOM_FIELD_EVENT)) {
> +               DRM_ERROR("unknown event: %lu\n", event);
> +               return -EINVAL;
> +       }
> +
> +       drm_handle_vblank(drm_dev, *crtc);
> +
> +       spin_lock_irqsave(&drm_dev->event_lock, flags);
> +       list_for_each_entry_safe(e, t, &priv->pageflip_evt_list, base.link) {
> +               if (*crtc != e->pipe)
> +                       continue;
> +
> +               list_del(&e->base.link);
> +               drm_send_vblank_event(drm_dev, -1, e);
> +               drm_vblank_put(drm_dev, *crtc);
> +       }
> +       spin_unlock_irqrestore(&drm_dev->event_lock, flags);
> +
> +       return 0;
> +}
> +
> +int sti_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
> +{
> +       struct sti_drm_private *dev_priv = dev->dev_private;
> +       struct sti_compositor *compo = dev_priv->compo;
> +       struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb;
> +       DRM_DEBUG_DRIVER("\n");
> +
> +       if (sti_vtg_register_client(crtc == STI_MIXER_MAIN ?
> +                       compo->vtg_main : compo->vtg_aux,
> +                       vtg_vblank_nb, crtc)) {
> +               DRM_ERROR("Cannot register VTG notifier\n");
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +void sti_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
> +{
> +       struct sti_drm_private *priv = dev->dev_private;
> +       struct sti_compositor *compo = priv->compo;
> +       struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb;
> +       unsigned long flags;
> +       struct drm_pending_vblank_event *e, *t;
> +
> +       DRM_DEBUG_DRIVER("\n");
> +
> +       if (sti_vtg_unregister_client(crtc == STI_MIXER_MAIN ?
> +                       compo->vtg_main : compo->vtg_aux, vtg_vblank_nb))
> +               DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n");
> +
> +       /* free the resources of the pending requests */
> +       spin_lock_irqsave(&dev->event_lock, flags);
> +       list_for_each_entry_safe(e, t, &priv->pageflip_evt_list, base.link) {
> +               if (crtc != e->pipe)
> +                       continue;
> +               list_del(&e->base.link);
> +               drm_vblank_put(dev, crtc);
> +       }
> +       spin_unlock_irqrestore(&dev->event_lock, flags);
> +
> +}
> +
> +static struct drm_crtc_funcs sti_crtc_funcs = {
> +       .set_config = drm_crtc_helper_set_config,
> +       .page_flip = sti_drm_crtc_page_flip,
> +       .destroy = sti_drm_crtc_destroy,
> +       .set_property = sti_drm_crtc_set_property,
> +};
> +
> +bool sti_drm_crtc_is_main(struct drm_crtc *crtc)
> +{
> +       struct sti_mixer *mixer = to_sti_mixer(crtc);
> +
> +       if (mixer->id == STI_MIXER_MAIN)
> +               return true;
> +
> +       return false;
> +}
> +
> +int sti_drm_crtc_init(struct drm_device *drm_dev, struct sti_mixer *mixer)
> +{
> +       struct drm_crtc *crtc = &mixer->drm_crtc;
> +       int res;
> +
> +       res = drm_crtc_init(drm_dev, crtc, &sti_crtc_funcs);
> +       if (res) {
> +               DRM_ERROR("Can not initialze CRTC\n");
> +               return -EINVAL;
> +       }
> +
> +       drm_crtc_helper_add(crtc, &sti_crtc_helper_funcs);
> +
> +       DRM_DEBUG_DRIVER("drm CRTC:%d mapped to %s\n",
> +                        crtc->base.id, sti_mixer_to_str(mixer));
> +
> +       return 0;
> +}
> diff --git a/drivers/gpu/drm/sti/sti_drm_crtc.h b/drivers/gpu/drm/sti/sti_drm_crtc.h
> new file mode 100644
> index 0000000..e46babe
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_drm_crtc.h
> @@ -0,0 +1,21 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef _STI_DRM_CRTC_H_
> +#define _STI_DRM_CRTC_H_
> +
> +#include <drm/drmP.h>
> +
> +struct sti_mixer;
> +
> +int sti_drm_crtc_init(struct drm_device *drm_dev, struct sti_mixer *mixer);
> +int sti_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
> +void sti_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);
> +int sti_drm_crtc_vblank_cb(struct notifier_block *nb,
> +               unsigned long event, void *data);
> +bool sti_drm_crtc_is_main(struct drm_crtc *drm_crtc);
> +
> +#endif
> diff --git a/drivers/gpu/drm/sti/sti_drm_drv.c b/drivers/gpu/drm/sti/sti_drm_drv.c
> new file mode 100644
> index 0000000..54dc74e
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_drm_drv.c
> @@ -0,0 +1,229 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <drm/drmP.h>
> +
> +#include <linux/component.h>
> +#include <linux/debugfs.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +
> +#include "sti_drm_drv.h"
> +#include "sti_drm_crtc.h"
> +
> +#define DRIVER_NAME    "sti"
> +#define DRIVER_DESC    "STMicroelectronics SoC DRM"
> +#define DRIVER_DATE    "20140601"
> +#define DRIVER_MAJOR   1
> +#define DRIVER_MINOR   0
> +
> +#ifdef CONFIG_DRM_STI_FBDEV
> +#define FBDEV_CREATE_DELAY      2000

Oh dear.. maybe I should run away now..

I'm a bit scared to ask why you need to defer fbdev creation, which is
presumably a way to defer initial modeset.  I hope/expect there is a
better way.  I don't even know what would happen if plymouth already
did (or was in the middle of doing) modeset when the 2s delay expired
and the kernel tried to do initial modeset.

Maybe you can explain the issue and we can come up with something
better.  But I don't think this 2s delay is the way..

BR,
-R

> +
> +static void stid_fbdev_create(struct work_struct *work)
> +{
> +       struct delayed_work *d_work = to_delayed_work(work);
> +       struct sti_drm_private *private =
> +               container_of(d_work, struct sti_drm_private, fb_work);
> +       struct drm_device *drm_dev = private->drm_dev;
> +
> +       drm_fbdev_cma_init(drm_dev, 32,
> +                          drm_dev->mode_config.num_crtc,
> +                          drm_dev->mode_config.num_connector);
> +}
> +#endif
> +
> +static struct drm_mode_config_funcs sti_drm_mode_config_funcs = {
> +       .fb_create = drm_fb_cma_create,
> +};
> +
> +#define STI_MAX_FB_HEIGHT      4096
> +#define STI_MAX_FB_WIDTH       4096
> +
> +static void sti_drm_mode_config_init(struct drm_device *dev)
> +{
> +       dev->mode_config.min_width = 0;
> +       dev->mode_config.min_height = 0;
> +
> +       /*
> +        * set max width and height as default value.
> +        * this value would be used to check framebuffer size limitation
> +        * at drm_mode_addfb().
> +        */
> +       dev->mode_config.max_width = STI_MAX_FB_HEIGHT;
> +       dev->mode_config.max_height = STI_MAX_FB_WIDTH;
> +
> +       dev->mode_config.funcs = &sti_drm_mode_config_funcs;
> +}
> +
> +static int sti_drm_load(struct drm_device *dev, unsigned long flags)
> +{
> +       struct sti_drm_private *private;
> +
> +       private = kzalloc(sizeof(struct sti_drm_private), GFP_KERNEL);
> +       if (!private) {
> +               DRM_ERROR("Failed to allocate private\n");
> +               return -ENOMEM;
> +       }
> +       dev->dev_private = (void *)private;
> +       private->drm_dev = dev;
> +
> +       drm_mode_config_init(dev);
> +       drm_kms_helper_poll_init(dev);
> +
> +       sti_drm_mode_config_init(dev);
> +
> +       component_bind_all(dev->dev, dev);
> +
> +       drm_helper_disable_unused_functions(dev);
> +#ifdef CONFIG_DRM_STI_FBDEV
> +
> +       INIT_DELAYED_WORK(&private->fb_work, stid_fbdev_create);
> +
> +       schedule_delayed_work(&private->fb_work,
> +                             msecs_to_jiffies(FBDEV_CREATE_DELAY));
> +#endif
> +       return 0;
> +}
> +
> +static const struct file_operations sti_drm_driver_fops = {
> +       .owner = THIS_MODULE,
> +       .open = drm_open,
> +       .mmap = drm_gem_cma_mmap,
> +       .poll = drm_poll,
> +       .read = drm_read,
> +       .unlocked_ioctl = drm_ioctl,
> +#ifdef CONFIG_COMPAT
> +       .compat_ioctl = drm_compat_ioctl,
> +#endif
> +       .release = drm_release,
> +};
> +
> +static struct dma_buf *sti_drm_gem_prime_export(struct drm_device *dev,
> +                                               struct drm_gem_object *obj,
> +                                               int flags)
> +{
> +       /* we want to be able to write in mmapped buffer */
> +       flags |= O_RDWR;
> +       return drm_gem_prime_export(dev, obj, flags);
> +}
> +
> +static struct drm_driver sti_drm_driver = {
> +       .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET |
> +           DRIVER_GEM | DRIVER_PRIME,
> +       .load = sti_drm_load,
> +       .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 = &sti_drm_driver_fops,
> +
> +       .get_vblank_counter = drm_vblank_count,
> +       .enable_vblank = sti_drm_crtc_enable_vblank,
> +       .disable_vblank = sti_drm_crtc_disable_vblank,
> +
> +       .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> +       .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> +       .gem_prime_export = sti_drm_gem_prime_export,
> +       .gem_prime_import = drm_gem_prime_import,
> +       .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
> +       .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
> +       .gem_prime_vmap = drm_gem_cma_prime_vmap,
> +       .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
> +       .gem_prime_mmap = drm_gem_cma_prime_mmap,
> +
> +       .name = DRIVER_NAME,
> +       .desc = DRIVER_DESC,
> +       .date = DRIVER_DATE,
> +       .major = DRIVER_MAJOR,
> +       .minor = DRIVER_MINOR,
> +};
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> +       struct device_node *np = data;
> +
> +       return dev->of_node == np;
> +}
> +
> +static int sti_drm_add_components(struct device *master, struct master *m)
> +{
> +       struct device_node *np  = master->of_node;
> +       struct device_node *child_np;
> +
> +       child_np = of_get_next_available_child(np, NULL);
> +
> +       while (child_np) {
> +               component_master_add_child(m, compare_of, child_np);
> +               of_node_put(child_np);
> +               child_np = of_get_next_available_child(np, child_np);
> +       }
> +
> +       return 0;
> +}
> +
> +static int sti_drm_bind(struct device *dev)
> +{
> +       return drm_platform_init(&sti_drm_driver, to_platform_device(dev));
> +}
> +
> +static void sti_drm_unbind(struct device *dev)
> +{
> +       drm_put_dev(dev_get_drvdata(dev));
> +}
> +
> +static const struct component_master_ops sti_drm_ops = {
> +       .add_components = sti_drm_add_components,
> +       .bind = sti_drm_bind,
> +       .unbind = sti_drm_unbind,
> +};
> +
> +static int sti_drm_platform_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct device_node *node = dev->of_node;
> +
> +       of_platform_populate(node, NULL, NULL, dev);
> +
> +       dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
> +
> +       return component_master_add(&pdev->dev, &sti_drm_ops);
> +}
> +
> +static int sti_drm_platform_remove(struct platform_device *pdev)
> +{
> +
> +       component_master_del(&pdev->dev, &sti_drm_ops);
> +       return 0;
> +}
> +
> +static const struct of_device_id sti_drm_dt_ids[] = {
> +       { .compatible = "st,sti-display-subsystem", },
> +       { /* end node */ },
> +};
> +MODULE_DEVICE_TABLE(of, sti_drm_dt_ids);
> +
> +static struct platform_driver sti_drm_platform_driver = {
> +       .probe = sti_drm_platform_probe,
> +       .remove = sti_drm_platform_remove,
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = DRIVER_NAME,
> +               .of_match_table = sti_drm_dt_ids,
> +       },
> +};
> +
> +module_platform_driver(sti_drm_platform_driver);
> +
> +MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
> +MODULE_LICENSE("GPL V2");
> diff --git a/drivers/gpu/drm/sti/sti_drm_drv.h b/drivers/gpu/drm/sti/sti_drm_drv.h
> new file mode 100644
> index 0000000..6e999cb
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_drm_drv.h
> @@ -0,0 +1,35 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef _STI_DRM_DRV_H_
> +#define _STI_DRM_DRV_H_
> +
> +#include <drm/drmP.h>
> +
> +struct sti_compositor;
> +struct sti_tvout;
> +
> +/**
> + * STI drm private structure
> + * This structure is stored as private in the drm_device
> + *
> + * @compo:                 compositor
> + * @tvout:                 TV OUT
> + * @pageflip_evt_list:     list of pending page flip requests
> + * @plane_zorder_property: z-order property for CRTC planes
> + * @drm_dev:               drm device
> + * @fb_work:               delayed work for framebuffer creation
> + */
> +struct sti_drm_private {
> +       struct sti_compositor *compo;
> +       struct sti_tvout *tvout;
> +       struct list_head pageflip_evt_list;
> +       struct drm_property *plane_zorder_property;
> +       struct drm_device *drm_dev;
> +       struct delayed_work fb_work;
> +};
> +
> +#endif
> diff --git a/drivers/gpu/drm/sti/sti_drm_encoder.c b/drivers/gpu/drm/sti/sti_drm_encoder.c
> new file mode 100644
> index 0000000..29bdf4b
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_drm_encoder.c
> @@ -0,0 +1,108 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "sti_drm_crtc.h"
> +#include "sti_drm_encoder.h"
> +#include "sti_tvout.h"
> +
> +static void sti_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
> +{
> +}
> +
> +static bool sti_drm_encoder_mode_fixup(struct drm_encoder *encoder,
> +                                      const struct drm_display_mode *mode,
> +                                      struct drm_display_mode *adjusted_mode)
> +{
> +       return true;
> +}
> +
> +static void sti_drm_encoder_mode_set(struct drm_encoder *encoder,
> +                                    struct drm_display_mode *mode,
> +                                    struct drm_display_mode *adjusted_mode)
> +{
> +       struct drm_device *dev = encoder->dev;
> +       struct drm_connector *connector;
> +
> +       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +               if (connector->encoder == encoder) {
> +                       sti_tvout_set_mode(connector, mode,
> +                                       sti_drm_crtc_is_main(encoder->crtc));
> +                       break;
> +               }
> +       }
> +}
> +
> +static void sti_drm_encoder_prepare(struct drm_encoder *encoder)
> +{
> +       struct drm_device *dev = encoder->dev;
> +       struct drm_connector *connector;
> +
> +       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +               if (connector->encoder == encoder) {
> +                       sti_tvout_prepare(connector);
> +                       break;
> +               }
> +       }
> +}
> +
> +static void sti_drm_encoder_commit(struct drm_encoder *encoder)
> +{
> +       struct drm_device *dev = encoder->dev;
> +       struct drm_connector *connector;
> +
> +       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +               if (connector->encoder == encoder) {
> +                       sti_tvout_commit(connector,
> +                                        sti_drm_crtc_is_main(encoder->crtc));
> +                       break;
> +               }
> +       }
> +}
> +
> +static void sti_drm_encoder_disable(struct drm_encoder *encoder)
> +{
> +       struct drm_device *dev = encoder->dev;
> +       struct drm_connector *connector;
> +       struct drm_connector_helper_funcs *connector_funcs;
> +
> +       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +               connector_funcs = connector->helper_private;
> +               if (connector_funcs->best_encoder(connector) == encoder) {
> +                       sti_tvout_disable(connector);
> +                       break;
> +               }
> +       }
> +}
> +
> +static const struct drm_encoder_helper_funcs sti_drm_encoder_helper_funcs = {
> +       .dpms = sti_drm_encoder_dpms,
> +       .mode_fixup = sti_drm_encoder_mode_fixup,
> +       .mode_set = sti_drm_encoder_mode_set,
> +       .prepare = sti_drm_encoder_prepare,
> +       .commit = sti_drm_encoder_commit,
> +       .disable = sti_drm_encoder_disable,
> +};
> +
> +static void sti_drm_encoder_destroy(struct drm_encoder *encoder)
> +{
> +       drm_encoder_cleanup(encoder);
> +       kfree(encoder);
> +}
> +
> +static const struct drm_encoder_funcs sti_drm_encoder_funcs = {
> +       .destroy = sti_drm_encoder_destroy,
> +};
> +
> +void sti_drm_encoder_init(struct drm_device *dev,
> +               struct drm_encoder *encoder, int encoder_type)
> +{
> +       drm_encoder_init(dev, encoder, &sti_drm_encoder_funcs, encoder_type);
> +
> +       drm_encoder_helper_add(encoder, &sti_drm_encoder_helper_funcs);
> +}
> diff --git a/drivers/gpu/drm/sti/sti_drm_encoder.h b/drivers/gpu/drm/sti/sti_drm_encoder.h
> index bc7a91c..b5b884f 100644
> --- a/drivers/gpu/drm/sti/sti_drm_encoder.h
> +++ b/drivers/gpu/drm/sti/sti_drm_encoder.h
> @@ -11,4 +11,7 @@
>
>  #define ENCODER_MAIN_CRTC_MASK BIT(0)
>
> +void sti_drm_encoder_init(struct drm_device *dev,
> +               struct drm_encoder *encoder, int encoder_type);
> +
>  #endif
> diff --git a/drivers/gpu/drm/sti/sti_drm_plane.c b/drivers/gpu/drm/sti/sti_drm_plane.c
> new file mode 100644
> index 0000000..d950279
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_drm_plane.c
> @@ -0,0 +1,192 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
> + *          Fabien Dessenne <fabien.dessenne@st.com>
> + *          for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#include "sti_compositor.h"
> +#include "sti_drm_drv.h"
> +#include "sti_drm_plane.h"
> +#include "sti_vtg.h"
> +
> +enum sti_layer_desc sti_layer_default_zorder[] = {
> +       STI_GDP_0,
> +       STI_VID_0,
> +       STI_GDP_1,
> +       STI_VID_1,
> +       STI_GDP_2,
> +       STI_GDP_3,
> +};
> +
> +/* (Background) < GDP0 < VID0 < GDP1 < VID1 < GDP2 < GDP3 < (ForeGround) */
> +
> +static int
> +sti_drm_update_plane(struct drm_plane *plane, 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 sti_layer *layer = to_sti_layer(plane);
> +       struct sti_mixer *mixer = to_sti_mixer(crtc);
> +       int res;
> +
> +       DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s) drm fb:%d\n",
> +                     crtc->base.id, sti_mixer_to_str(mixer),
> +                     plane->base.id, sti_layer_to_str(layer), fb->base.id);
> +       DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", crtc_w, crtc_h, crtc_x, crtc_y);
> +
> +       res = sti_mixer_set_layer_depth(mixer, layer);
> +       if (res) {
> +               DRM_ERROR("Can not set layer depth\n");
> +               return res;
> +       }
> +
> +       /* src_x are in 16.16 format. */
> +       res = sti_layer_prepare(layer, fb, &crtc->mode, mixer->id,
> +                       crtc_x, crtc_y, crtc_w, crtc_h,
> +                       src_x >> 16, src_y >> 16,
> +                       src_w >> 16, src_h >> 16);
> +       if (res) {
> +               DRM_ERROR("Layer prepare failed\n");
> +               return res;
> +       }
> +
> +       res = sti_layer_commit(layer);
> +       if (res) {
> +               DRM_ERROR("Layer commit failed\n");
> +               return res;
> +       }
> +
> +       res = sti_mixer_set_layer_status(mixer, layer, true);
> +       if (res) {
> +               DRM_ERROR("Can not enable layer at mixer\n");
> +               return res;
> +       }
> +
> +       return 0;
> +}
> +
> +static int sti_drm_disable_plane(struct drm_plane *plane)
> +{
> +       struct sti_layer *layer;
> +       struct sti_mixer *mixer;
> +       int lay_res, mix_res;
> +
> +       if (!plane->crtc) {
> +               DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", plane->base.id);
> +               return 0;
> +       }
> +       layer = to_sti_layer(plane);
> +       mixer = to_sti_mixer(plane->crtc);
> +
> +       DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n",
> +                       plane->crtc->base.id, sti_mixer_to_str(mixer),
> +                       plane->base.id, sti_layer_to_str(layer));
> +
> +       /* Disable layer at mixer level */
> +       mix_res = sti_mixer_set_layer_status(mixer, layer, false);
> +       if (mix_res)
> +               DRM_ERROR("Can not disable layer at mixer\n");
> +
> +       /* Wait a while to be sure that a Vsync event is received */
> +       msleep(WAIT_NEXT_VSYNC_MS);
> +
> +       /* Then disable layer itself */
> +       lay_res = sti_layer_disable(layer);
> +       if (lay_res)
> +               DRM_ERROR("Layer disable failed\n");
> +
> +       if (lay_res || mix_res)
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +static void sti_drm_plane_destroy(struct drm_plane *plane)
> +{
> +       DRM_DEBUG_DRIVER("\n");
> +
> +       sti_drm_disable_plane(plane);
> +       drm_plane_cleanup(plane);
> +}
> +
> +static int sti_drm_plane_set_property(struct drm_plane *plane,
> +                                     struct drm_property *property,
> +                                     uint64_t val)
> +{
> +       struct drm_device *dev = plane->dev;
> +       struct sti_drm_private *private = dev->dev_private;
> +       struct sti_layer *layer = to_sti_layer(plane);
> +
> +       DRM_DEBUG_DRIVER("\n");
> +
> +       if (property == private->plane_zorder_property) {
> +               layer->zorder = val;
> +               return 0;
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +static struct drm_plane_funcs sti_drm_plane_funcs = {
> +       .update_plane = sti_drm_update_plane,
> +       .disable_plane = sti_drm_disable_plane,
> +       .destroy = sti_drm_plane_destroy,
> +       .set_property = sti_drm_plane_set_property,
> +};
> +
> +static void sti_drm_plane_attach_zorder_property(struct drm_plane *plane,
> +                                                uint64_t default_val)
> +{
> +       struct drm_device *dev = plane->dev;
> +       struct sti_drm_private *private = dev->dev_private;
> +       struct drm_property *prop;
> +       struct sti_layer *layer = to_sti_layer(plane);
> +
> +       prop = private->plane_zorder_property;
> +       if (!prop) {
> +               prop = drm_property_create_range(dev, 0, "zpos", 0,
> +                                                GAM_MIXER_NB_DEPTH_LEVEL - 1);
> +               if (!prop)
> +                       return;
> +
> +               private->plane_zorder_property = prop;
> +       }
> +
> +       drm_object_attach_property(&plane->base, prop, default_val);
> +       layer->zorder = default_val;
> +}
> +
> +struct drm_plane *sti_drm_plane_init(struct drm_device *dev,
> +                                    struct sti_layer *layer,
> +                                    unsigned int possible_crtcs)
> +{
> +       int err, i;
> +       uint64_t default_zorder = 0;
> +
> +       err = drm_plane_init(dev, &layer->plane, possible_crtcs,
> +                            &sti_drm_plane_funcs,
> +                            sti_layer_get_formats(layer),
> +                            sti_layer_get_nb_formats(layer), false);
> +       if (err) {
> +               DRM_ERROR("Failed to initialize plane\n");
> +               return NULL;
> +       }
> +
> +       for (i = 0; i < ARRAY_SIZE(sti_layer_default_zorder); i++)
> +               if (sti_layer_default_zorder[i] == layer->desc)
> +                       break;
> +
> +       default_zorder = i;
> +
> +       sti_drm_plane_attach_zorder_property(&layer->plane, default_zorder);
> +
> +       DRM_DEBUG_DRIVER("drm plane:%d mapped to %s with zorder:%llu\n",
> +                        layer->plane.base.id,
> +                        sti_layer_to_str(layer), default_zorder);
> +
> +       return &layer->plane;
> +}
> diff --git a/drivers/gpu/drm/sti/sti_drm_plane.h b/drivers/gpu/drm/sti/sti_drm_plane.h
> new file mode 100644
> index 0000000..1d8bc61
> --- /dev/null
> +++ b/drivers/gpu/drm/sti/sti_drm_plane.h
> @@ -0,0 +1,17 @@
> +/*
> + * Copyright (C) STMicroelectronics SA 2014
> + * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
> + * License terms:  GNU General Public License (GPL), version 2
> + */
> +
> +#ifndef _STI_DRM_PLANE_H_
> +#define _STI_DRM_PLANE_H_
> +
> +#include <drm/drmP.h>
> +
> +struct sti_layer;
> +
> +struct drm_plane *sti_drm_plane_init(struct drm_device *dev,
> +               struct sti_layer *layer, unsigned int possible_crtcs);
> +
> +#endif
> diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c
> index 9a6278d..4eb2d98 100644
> --- a/drivers/gpu/drm/sti/sti_hda.c
> +++ b/drivers/gpu/drm/sti/sti_hda.c
> @@ -610,6 +610,7 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data)
>
>         encoder->possible_crtcs = ENCODER_MAIN_CRTC_MASK;
>         encoder->possible_clones = 1 << STI_CONNECTOR_HDMI;
> +       sti_drm_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DAC);
>
>         connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
>         if (!connector)
> @@ -628,6 +629,9 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data)
>         connector->prepare = sti_hda_prepare;
>         connector->type = STI_CONNECTOR_HDA;
>
> +       sti_drm_connector_init(drm_dev,
> +                       encoder, connector, DRM_MODE_CONNECTOR_Component);
> +
>         return 0;
>  }
>
> diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
> index c963468..8e36d33 100644
> --- a/drivers/gpu/drm/sti/sti_hdmi.c
> +++ b/drivers/gpu/drm/sti/sti_hdmi.c
> @@ -582,6 +582,7 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
>
>         encoder->possible_crtcs = ENCODER_MAIN_CRTC_MASK;
>         encoder->possible_clones = 1 << STI_CONNECTOR_HDA;
> +       sti_drm_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
>
>         connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
>         if (!connector)
> @@ -600,6 +601,9 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
>         connector->prepare = sti_hdmi_prepare;
>         connector->type = STI_CONNECTOR_HDMI;
>
> +       sti_drm_connector_init(drm_dev,
> +                       encoder, connector, DRM_MODE_CONNECTOR_HDMIA);
> +
>         /* Enable default interrupts */
>         hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN);
>
> diff --git a/drivers/gpu/drm/sti/sti_tvout.c b/drivers/gpu/drm/sti/sti_tvout.c
> index d778e35..0ac698b 100644
> --- a/drivers/gpu/drm/sti/sti_tvout.c
> +++ b/drivers/gpu/drm/sti/sti_tvout.c
> @@ -17,6 +17,7 @@
>  #include <drm/drm_crtc_helper.h>
>
>  #include "sti_drm_connector.h"
> +#include "sti_drm_drv.h"
>  #include "sti_drm_encoder.h"
>  #include "sti_tvout.h"
>
> @@ -551,6 +552,14 @@ void sti_tvout_disable(struct drm_connector *c)
>
>  static int sti_tvout_bind(struct device *dev, struct device *master, void *data)
>  {
> +       struct sti_tvout *tvout = dev_get_drvdata(dev);
> +       struct drm_device *drm_dev = data;
> +       struct sti_drm_private *dev_priv = drm_dev->dev_private;
> +
> +       dev_priv->tvout = tvout;
> +
> +       component_bind_all(dev, drm_dev);
> +
>         return 0;
>  }
>
> --
> 1.9.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v4 00/11] Add DRM for stih4xx platforms
  2014-06-11 18:37 ` [PATCH v4 00/11] Add DRM for stih4xx platforms Rob Clark
@ 2014-06-11 20:22   ` Rob Clark
  0 siblings, 0 replies; 19+ messages in thread
From: Rob Clark @ 2014-06-11 20:22 UTC (permalink / raw)
  To: Benjamin Gaignard
  Cc: dri-devel, linaro-mm-sig, Linux Kernel Mailing List,
	David Airlie, Lee Jones, Thierry Reding

On Wed, Jun 11, 2014 at 2:37 PM, Rob Clark <robdclark@gmail.com> wrote:
> On Thu, May 29, 2014 at 2:36 AM, Benjamin Gaignard
> <benjamin.gaignard@linaro.org> wrote:
>> This series of patches add the support of DRM/KMS drivers for STMicroelectronics
>> chipsets stih416 and stih407.
>>
>> version 4:
>>         - Remove depency between TVout it subdevices HDMI and HDA
>>         - Rework and simplify VTG and VTAC code
>>         - Fix numbers of typo and indentation
>>         - Remove debug (will be push in separate patches)
>>         - Fix remarks done in previous patcheset
>>
>>         patches could be found here: git://git.linaro.org/people/benjamin.gaignard/kernel.git
>>         on branch: drm_kms_for_next-v4
>>
>> version 3:
>>         - Correctly split code between probe and bind funtions
>>         - Squash some commits
>>         - remove HQ-VDP device code to have a smaller patcheset,
>>           we will introduce it later.
>>
>>         patches could be found here: git://git.linaro.org/people/benjamin.gaignard/kernel.git
>>         on branch: drm_kms_for_next-v3
>>
>> version 2:
>>         - Use componentized device instead of register sub-devices in master
>>         driver probe function
>>         - Fix Makefile and Kconfig to only allow built-in compilation
>>
>>         patches could be found here: git://git.linaro.org/people/benjamin.gaignard/kernel.git
>>         on branch: drm_kms_for_next-v2
>>
>> version 1:
>>         - First path submission
>>
>> Hardware is split in two main blocks: Compositor and TVout. Each of them
>> includes specific hardware IPs and the display timing are controlled by a specific
>> Video Timing Generator hardware IP (VTG).
>>
>> Compositor is made of the follow hardware IPs:
>>  - GDP (Generic Display Pipeline) which is an entry point for graphic (RGB)
>>    buffers
>>  - VDP (Video Diplay Pipeline) which is an entry point for video (YUV) buffers
>>  - HQVDP (High Quality Video Display Processor) that supports scaling,
>>    deinterlacing and some miscellaneous image quality improvements.
>>    It fetches the Video decoded buffers from memory, processes them and pushes
>>    them to the Compositor through a HW dedicated bus.
>>  - Mixer is responsible of mixing all the entries depending of their
>>    respective z-order and layout
>>
>> TVout is divided in 3 parts:
>>  - HDMI to generate HDMI signals, depending of chipset version HDMI phy can
>>    change.
>>  - HDA to generate signals for HD analog TV
>>  - VIP to control/switch data path coming from Compositor
>>
>> On stih416 compositor and Tvout are on different dies so a Video Trafic Advance
>> inter-die Communication mechanism (VTAC) is needed.
>>
>> +---------------------------------------------+   +----------------------------------------+
>> | +-------------------------------+   +----+  |   |  +----+   +--------------------------+ |
>> | |                               |   |    |  |   |  |    |   |  +---------+     +----+  | |
>> | | +----+              +------+  |   |    |  |   |  |    |   |  | VIP     |---->|HDMI|  | |
>> | | |GPD +------------->|      |  |   |    |  |   |  |    |   |  |         |     +----+  | |
>> | | +----+              |Mixer |--|-->|    |  |   |  |    |---|->| switcher|             | |
>> | |                     |      |  |   |    |  |   |  |    |   |  |         |     +----+  | |
>> | |                     |      |  |   |    |  |   |  |    |   |  |         |---->|HDA |  | |
>> | |                     +------+  |   |VTAC|========>|VTAC|   |  +---------+     +----+  | |
>> | |                               |   |    |  |   |  |    |   |                          | |
>> | |         Compositor            |   |    |  |   |  |    |   |           TVout          | |
>> | +-------------------------------+   |    |  |   |  |    |   +--------------------------+ |
>> |                      ^              |    |  |   |  |    |             ^                  |
>> |                      |              |    |  |   |  |    |             |                  |
>> |               +--------------+      |    |  |   |  |    |      +-------------+           |
>> |               | VTG (master) |----->|    |  |   |  |    |----->| VTG (slave) |           |
>> |               +--------------+      +----+  |   |  +----+      +-------------+           |
>> |Digital die                                  |   |                              Analog Die|
>> +---------------------------------------------+   +----------------------------------------+
>>
>> On stih407 Compositor and Tvout are on the same die
>>
>> +-----------------------------------------------------------------+
>> | +-------------------------------+  +--------------------------+ |
>> | |                               |  |  +---------+     +----+  | |
>> | | +----+              +------+  |  |  | VIP     |---->|HDMI|  | |
>> | | |GPD +------------->|      |  |  |  |         |     +----+  | |
>> | | +----+              |Mixer |--|--|->| switcher|             | |
>> | | +----+   +-----+    |      |  |  |  |         |     +----+  | |
>> | | |VDP +-->+HQVDP+--->|      |  |  |  |         |---->|HDA |  | |
>> | | +----+   +-----+    +------+  |  |  +---------+     +----+  | |
>> | |                               |  |                          | |
>> | |         Compositor            |  |           TVout          | |
>> | +-------------------------------+  +--------------------------+ |
>> |                              ^        ^                         |
>> |                              |        |                         |
>> |                           +--------------+                      |
>> |                           |     VTG      |                      |
>> |                           +--------------+                      |
>> |Digital die                                                      |
>> +-----------------------------------------------------------------+
>>
>> In addition of the drivers for the IPs listed before a thin I2C driver (hdmiddc) is used
>> by HDMI driver to retrieve EDID for monitor.
>>
>> To unify interfaces of GDP and VDP we create a "layer" interface called by
>> compositor to control both GPD and VDP.
>
> I think it would be helpful to describe how you map the hardware to
> kms constructs (planes/crtcs/etc).
>
> Also, I kinda like sticking this sort of info (along w/ block diagrams
> above, etc), in a notes/readme file.  Although I seem to be in the
> minority about that.  But plenty of times when diving into other
> drivers I've wished they had done the same :-)
>
> For msm I think I more or less copy/paste the cover-letter and drm/msm/NOTES..

btw, Daniel started converting i915 over to docbook.. I probably
should look at converting some/all of my NOTES over to docbook one of
these days..

Anyway, I think I mostly managed to cover the parts that Thierry did
not get to yet.  Although you did seem to squash/drop/rearrange some
parts, so hopefully I didn't miss something as a result.

BR,
-R

>
> BR,
> -R
>
>
>> Hardware have memory contraints (alignment, contiguous) so we use CMA drm helpers functions
>> to allocate frame buffer.
>>
>> File naming convention is:
>>  - sti_* for IPs drivers
>>  - sti_drm_* for drm functions implementation.
>>
>> Benjamin Gaignard (11):
>>   drm: sti: add bindings for DRM driver
>>   drm: sti: add VTG driver
>>   drm: sti: add VTAC drivers
>>   drm: sti: add HDMI driver
>>   drm: sti: add HDA driver
>>   drm: sti: add TVOut driver
>>   drm: sti: add GDP layer
>>   drm: sti: add VID layer
>>   drm: sti: add Mixer
>>   drm: sti: add Compositor
>>   drm: sti: Add DRM driver itself
>>
>>  .../devicetree/bindings/gpu/st,stih4xx.txt         | 189 ++++++
>>  drivers/gpu/drm/Kconfig                            |   2 +
>>  drivers/gpu/drm/Makefile                           |   1 +
>>  drivers/gpu/drm/sti/Kconfig                        |  14 +
>>  drivers/gpu/drm/sti/Makefile                       |  19 +
>>  drivers/gpu/drm/sti/sti_compositor.c               | 265 ++++++++
>>  drivers/gpu/drm/sti/sti_compositor.h               |  90 +++
>>  drivers/gpu/drm/sti/sti_drm_connector.c            | 114 ++++
>>  drivers/gpu/drm/sti/sti_drm_connector.h            |  55 ++
>>  drivers/gpu/drm/sti/sti_drm_crtc.c                 | 436 ++++++++++++
>>  drivers/gpu/drm/sti/sti_drm_crtc.h                 |  21 +
>>  drivers/gpu/drm/sti/sti_drm_drv.c                  | 229 +++++++
>>  drivers/gpu/drm/sti/sti_drm_drv.h                  |  35 +
>>  drivers/gpu/drm/sti/sti_drm_encoder.c              | 108 +++
>>  drivers/gpu/drm/sti/sti_drm_encoder.h              |  17 +
>>  drivers/gpu/drm/sti/sti_drm_plane.c                | 192 ++++++
>>  drivers/gpu/drm/sti/sti_drm_plane.h                |  17 +
>>  drivers/gpu/drm/sti/sti_gdp.c                      | 512 ++++++++++++++
>>  drivers/gpu/drm/sti/sti_gdp.h                      |  73 ++
>>  drivers/gpu/drm/sti/sti_hda.c                      | 727 ++++++++++++++++++++
>>  drivers/gpu/drm/sti/sti_hdmi.c                     | 748 +++++++++++++++++++++
>>  drivers/gpu/drm/sti/sti_hdmi.h                     |  88 +++
>>  drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c         | 336 +++++++++
>>  drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h         |  14 +
>>  drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c         | 211 ++++++
>>  drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h         |  14 +
>>  drivers/gpu/drm/sti/sti_layer.c                    | 312 +++++++++
>>  drivers/gpu/drm/sti/sti_layer.h                    | 114 ++++
>>  drivers/gpu/drm/sti/sti_mixer.c                    | 249 +++++++
>>  drivers/gpu/drm/sti/sti_mixer.h                    |  52 ++
>>  drivers/gpu/drm/sti/sti_tvout.c                    | 687 +++++++++++++++++++
>>  drivers/gpu/drm/sti/sti_tvout.h                    |  18 +
>>  drivers/gpu/drm/sti/sti_vid.c                      | 145 ++++
>>  drivers/gpu/drm/sti/sti_vid.h                      |  33 +
>>  drivers/gpu/drm/sti/sti_vtac.c                     | 211 ++++++
>>  drivers/gpu/drm/sti/sti_vtg.c                      | 356 ++++++++++
>>  drivers/gpu/drm/sti/sti_vtg.h                      |  28 +
>>  37 files changed, 6732 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/gpu/st,stih4xx.txt
>>  create mode 100644 drivers/gpu/drm/sti/Kconfig
>>  create mode 100644 drivers/gpu/drm/sti/Makefile
>>  create mode 100644 drivers/gpu/drm/sti/sti_compositor.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_compositor.h
>>  create mode 100644 drivers/gpu/drm/sti/sti_drm_connector.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_drm_connector.h
>>  create mode 100644 drivers/gpu/drm/sti/sti_drm_crtc.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_drm_crtc.h
>>  create mode 100644 drivers/gpu/drm/sti/sti_drm_drv.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_drm_drv.h
>>  create mode 100644 drivers/gpu/drm/sti/sti_drm_encoder.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_drm_encoder.h
>>  create mode 100644 drivers/gpu/drm/sti/sti_drm_plane.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_drm_plane.h
>>  create mode 100644 drivers/gpu/drm/sti/sti_gdp.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_gdp.h
>>  create mode 100644 drivers/gpu/drm/sti/sti_hda.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_hdmi.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_hdmi.h
>>  create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h
>>  create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.h
>>  create mode 100644 drivers/gpu/drm/sti/sti_layer.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_layer.h
>>  create mode 100644 drivers/gpu/drm/sti/sti_mixer.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_mixer.h
>>  create mode 100644 drivers/gpu/drm/sti/sti_tvout.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_tvout.h
>>  create mode 100644 drivers/gpu/drm/sti/sti_vid.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_vid.h
>>  create mode 100644 drivers/gpu/drm/sti/sti_vtac.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_vtg.c
>>  create mode 100644 drivers/gpu/drm/sti/sti_vtg.h
>>
>> --
>> 1.9.1
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel

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

end of thread, other threads:[~2014-06-11 20:22 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-05-29  6:36 [PATCH v4 00/11] Add DRM for stih4xx platforms Benjamin Gaignard
2014-05-29  6:36 ` [PATCH v4 01/11] drm: sti: add bindings for DRM driver Benjamin Gaignard
2014-05-29  6:36 ` [PATCH v4 02/11] drm: sti: add VTG driver Benjamin Gaignard
2014-05-29  6:36 ` [PATCH v4 03/11] drm: sti: add VTAC drivers Benjamin Gaignard
2014-05-29  6:37 ` [PATCH v4 04/11] drm: sti: add HDMI driver Benjamin Gaignard
2014-05-29  6:37 ` [PATCH v4 05/11] drm: sti: add HDA driver Benjamin Gaignard
2014-05-29  6:37 ` [PATCH v4 06/11] drm: sti: add TVOut driver Benjamin Gaignard
2014-06-11 18:20   ` Rob Clark
2014-05-29  6:37 ` [PATCH v4 07/11] drm: sti: add GDP layer Benjamin Gaignard
2014-06-11 19:15   ` Rob Clark
2014-05-29  6:37 ` [PATCH v4 08/11] drm: sti: add VID layer Benjamin Gaignard
2014-05-29  6:37 ` [PATCH v4 09/11] drm: sti: add Mixer Benjamin Gaignard
2014-06-11 19:04   ` Rob Clark
2014-05-29  6:37 ` [PATCH v4 10/11] drm: sti: add Compositor Benjamin Gaignard
2014-06-11 18:59   ` Rob Clark
2014-05-29  6:37 ` [PATCH v4 11/11] drm: sti: Add DRM driver itself Benjamin Gaignard
2014-06-11 20:06   ` Rob Clark
2014-06-11 18:37 ` [PATCH v4 00/11] Add DRM for stih4xx platforms Rob Clark
2014-06-11 20:22   ` Rob Clark

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).