All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] ARM CLCD DRM driver
@ 2017-03-17 22:47 ` Eric Anholt
  0 siblings, 0 replies; 18+ messages in thread
From: Eric Anholt @ 2017-03-17 22:47 UTC (permalink / raw)
  To: dri-devel, tom.cooksey, Russell King; +Cc: linux-kernel, Eric Anholt

This is a resurrection of Tom Cooksey's old CLCD DRM driver, which I
needed in order to do dmabuf buffer sharing from VC4 to the CLCD
display on the bcm911360_entphn platform.  With a couple of hacks to
the X Server, I've got it (sometimes) running glxgears on vc4 and
displaying on CLCD.

The panel power sequencing part is not thoroughly tested.  My DRM
panel driver (not included) for this board is still a stub and doesn't
do the power on SPI reset sequence.

There are a few TODO entries left, which are noted in the
documentation.  My primary concern with this driver is that, since it
binds to the same device as fbdev but isn't a complete replacement,
people might turn it on and break their display setup.  I figure until
someone wants to turn DRM CLCD on in a multi-platform defconfig, we
ought to be fine with that, though.

My plan would be for this to go through the drm-misc tree once it's
ready.

For anyone interested in looking through the history of what I changed
from Tom's v1 patch,
https://github.com/anholt/linux/tree/bcm11360-clcd has it (and all the
rest of the platform bits I've needed).

Eric Anholt (1):
  video: ARM CLCD: Move registers to a separate header.

Tom Cooksey (1):
  drm/pl111: Initial drm/kms driver for pl111

 Documentation/gpu/index.rst             |   1 +
 Documentation/gpu/pl111.rst             |   6 +
 drivers/gpu/drm/Kconfig                 |   2 +
 drivers/gpu/drm/Makefile                |   1 +
 drivers/gpu/drm/pl111/Kconfig           |  12 ++
 drivers/gpu/drm/pl111/Makefile          |   8 +
 drivers/gpu/drm/pl111/pl111_connector.c | 127 ++++++++++++++
 drivers/gpu/drm/pl111/pl111_crtc.c      | 239 ++++++++++++++++++++++++++
 drivers/gpu/drm/pl111/pl111_drm.h       |  64 +++++++
 drivers/gpu/drm/pl111/pl111_drv.c       | 292 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/pl111/pl111_encoder.c   |  50 ++++++
 drivers/gpu/drm/pl111/pl111_gem.c       |  35 ++++
 drivers/gpu/drm/pl111/pl111_plane.c     | 167 ++++++++++++++++++
 include/linux/amba/clcd-regs.h          |  76 +++++++++
 include/linux/amba/clcd.h               |  68 +-------
 15 files changed, 1081 insertions(+), 67 deletions(-)
 create mode 100644 Documentation/gpu/pl111.rst
 create mode 100644 drivers/gpu/drm/pl111/Kconfig
 create mode 100644 drivers/gpu/drm/pl111/Makefile
 create mode 100644 drivers/gpu/drm/pl111/pl111_connector.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_crtc.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_drm.h
 create mode 100644 drivers/gpu/drm/pl111/pl111_drv.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_encoder.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_gem.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_plane.c
 create mode 100644 include/linux/amba/clcd-regs.h

-- 
2.11.0

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

* [PATCH 0/2] ARM CLCD DRM driver
@ 2017-03-17 22:47 ` Eric Anholt
  0 siblings, 0 replies; 18+ messages in thread
From: Eric Anholt @ 2017-03-17 22:47 UTC (permalink / raw)
  To: dri-devel, tom.cooksey, Russell King; +Cc: linux-kernel

This is a resurrection of Tom Cooksey's old CLCD DRM driver, which I
needed in order to do dmabuf buffer sharing from VC4 to the CLCD
display on the bcm911360_entphn platform.  With a couple of hacks to
the X Server, I've got it (sometimes) running glxgears on vc4 and
displaying on CLCD.

The panel power sequencing part is not thoroughly tested.  My DRM
panel driver (not included) for this board is still a stub and doesn't
do the power on SPI reset sequence.

There are a few TODO entries left, which are noted in the
documentation.  My primary concern with this driver is that, since it
binds to the same device as fbdev but isn't a complete replacement,
people might turn it on and break their display setup.  I figure until
someone wants to turn DRM CLCD on in a multi-platform defconfig, we
ought to be fine with that, though.

My plan would be for this to go through the drm-misc tree once it's
ready.

For anyone interested in looking through the history of what I changed
from Tom's v1 patch,
https://github.com/anholt/linux/tree/bcm11360-clcd has it (and all the
rest of the platform bits I've needed).

Eric Anholt (1):
  video: ARM CLCD: Move registers to a separate header.

Tom Cooksey (1):
  drm/pl111: Initial drm/kms driver for pl111

 Documentation/gpu/index.rst             |   1 +
 Documentation/gpu/pl111.rst             |   6 +
 drivers/gpu/drm/Kconfig                 |   2 +
 drivers/gpu/drm/Makefile                |   1 +
 drivers/gpu/drm/pl111/Kconfig           |  12 ++
 drivers/gpu/drm/pl111/Makefile          |   8 +
 drivers/gpu/drm/pl111/pl111_connector.c | 127 ++++++++++++++
 drivers/gpu/drm/pl111/pl111_crtc.c      | 239 ++++++++++++++++++++++++++
 drivers/gpu/drm/pl111/pl111_drm.h       |  64 +++++++
 drivers/gpu/drm/pl111/pl111_drv.c       | 292 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/pl111/pl111_encoder.c   |  50 ++++++
 drivers/gpu/drm/pl111/pl111_gem.c       |  35 ++++
 drivers/gpu/drm/pl111/pl111_plane.c     | 167 ++++++++++++++++++
 include/linux/amba/clcd-regs.h          |  76 +++++++++
 include/linux/amba/clcd.h               |  68 +-------
 15 files changed, 1081 insertions(+), 67 deletions(-)
 create mode 100644 Documentation/gpu/pl111.rst
 create mode 100644 drivers/gpu/drm/pl111/Kconfig
 create mode 100644 drivers/gpu/drm/pl111/Makefile
 create mode 100644 drivers/gpu/drm/pl111/pl111_connector.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_crtc.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_drm.h
 create mode 100644 drivers/gpu/drm/pl111/pl111_drv.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_encoder.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_gem.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_plane.c
 create mode 100644 include/linux/amba/clcd-regs.h

-- 
2.11.0

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

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

* [PATCH 1/2] video: ARM CLCD: Move registers to a separate header.
  2017-03-17 22:47 ` Eric Anholt
@ 2017-03-17 22:47   ` Eric Anholt
  -1 siblings, 0 replies; 18+ messages in thread
From: Eric Anholt @ 2017-03-17 22:47 UTC (permalink / raw)
  To: dri-devel, tom.cooksey, Russell King; +Cc: linux-kernel, Eric Anholt

We'd like to reuse these register definitions for the DRM CLCD driver,
but there's a bunch of fbdev-specific code in the current header.

Signed-off-by: Eric Anholt <eric@anholt.net>
---
 include/linux/amba/clcd-regs.h | 76 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/amba/clcd.h      | 68 +------------------------------------
 2 files changed, 77 insertions(+), 67 deletions(-)
 create mode 100644 include/linux/amba/clcd-regs.h

diff --git a/include/linux/amba/clcd-regs.h b/include/linux/amba/clcd-regs.h
new file mode 100644
index 000000000000..1b924c4a63ab
--- /dev/null
+++ b/include/linux/amba/clcd-regs.h
@@ -0,0 +1,76 @@
+/*
+ * David A Rusling
+ *
+ * Copyright (C) 2001 ARM Limited
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+/*
+ * CLCD Controller Internal Register addresses
+ */
+#define CLCD_TIM0		0x00000000
+#define CLCD_TIM1 		0x00000004
+#define CLCD_TIM2 		0x00000008
+#define CLCD_TIM3 		0x0000000c
+#define CLCD_UBAS 		0x00000010
+#define CLCD_LBAS 		0x00000014
+
+#define CLCD_PL110_IENB		0x00000018
+#define CLCD_PL110_CNTL		0x0000001c
+#define CLCD_PL110_STAT		0x00000020
+#define CLCD_PL110_INTR 	0x00000024
+#define CLCD_PL110_UCUR		0x00000028
+#define CLCD_PL110_LCUR		0x0000002C
+
+#define CLCD_PL111_CNTL		0x00000018
+#define CLCD_PL111_IENB		0x0000001c
+#define CLCD_PL111_RIS		0x00000020
+#define CLCD_PL111_MIS		0x00000024
+#define CLCD_PL111_ICR		0x00000028
+#define CLCD_PL111_UCUR		0x0000002c
+#define CLCD_PL111_LCUR		0x00000030
+
+#define CLCD_PALL 		0x00000200
+#define CLCD_PALETTE		0x00000200
+
+#define TIM2_CLKSEL		(1 << 5)
+#define TIM2_IVS		(1 << 11)
+#define TIM2_IHS		(1 << 12)
+#define TIM2_IPC		(1 << 13)
+#define TIM2_IOE		(1 << 14)
+#define TIM2_BCD		(1 << 26)
+
+#define CNTL_LCDEN		(1 << 0)
+#define CNTL_LCDBPP1		(0 << 1)
+#define CNTL_LCDBPP2		(1 << 1)
+#define CNTL_LCDBPP4		(2 << 1)
+#define CNTL_LCDBPP8		(3 << 1)
+#define CNTL_LCDBPP16		(4 << 1)
+#define CNTL_LCDBPP16_565	(6 << 1)
+#define CNTL_LCDBPP16_444	(7 << 1)
+#define CNTL_LCDBPP24		(5 << 1)
+#define CNTL_LCDBW		(1 << 4)
+#define CNTL_LCDTFT		(1 << 5)
+#define CNTL_LCDMONO8		(1 << 6)
+#define CNTL_LCDDUAL		(1 << 7)
+#define CNTL_BGR		(1 << 8)
+#define CNTL_BEBO		(1 << 9)
+#define CNTL_BEPO		(1 << 10)
+#define CNTL_LCDPWR		(1 << 11)
+#define CNTL_LCDVCOMP(x)	((x) << 12)
+#define CNTL_LDMAFIFOTIME	(1 << 15)
+#define CNTL_WATERMARK		(1 << 16)
+
+/* ST Microelectronics variant bits */
+#define CNTL_ST_1XBPP_444	0x0
+#define CNTL_ST_1XBPP_5551	(1 << 17)
+#define CNTL_ST_1XBPP_565	(1 << 18)
+#define CNTL_ST_CDWID_12	0x0
+#define CNTL_ST_CDWID_16	(1 << 19)
+#define CNTL_ST_CDWID_18	(1 << 20)
+#define CNTL_ST_CDWID_24	((1 << 19)|(1 << 20))
+#define CNTL_ST_CEAEN		(1 << 21)
+#define CNTL_ST_LCDBPP24_PACKED	(6 << 1)
diff --git a/include/linux/amba/clcd.h b/include/linux/amba/clcd.h
index 1035879b322c..d0c3be77c18e 100644
--- a/include/linux/amba/clcd.h
+++ b/include/linux/amba/clcd.h
@@ -10,73 +10,7 @@
  * for more details.
  */
 #include <linux/fb.h>
-
-/*
- * CLCD Controller Internal Register addresses
- */
-#define CLCD_TIM0		0x00000000
-#define CLCD_TIM1 		0x00000004
-#define CLCD_TIM2 		0x00000008
-#define CLCD_TIM3 		0x0000000c
-#define CLCD_UBAS 		0x00000010
-#define CLCD_LBAS 		0x00000014
-
-#define CLCD_PL110_IENB		0x00000018
-#define CLCD_PL110_CNTL		0x0000001c
-#define CLCD_PL110_STAT		0x00000020
-#define CLCD_PL110_INTR 	0x00000024
-#define CLCD_PL110_UCUR		0x00000028
-#define CLCD_PL110_LCUR		0x0000002C
-
-#define CLCD_PL111_CNTL		0x00000018
-#define CLCD_PL111_IENB		0x0000001c
-#define CLCD_PL111_RIS		0x00000020
-#define CLCD_PL111_MIS		0x00000024
-#define CLCD_PL111_ICR		0x00000028
-#define CLCD_PL111_UCUR		0x0000002c
-#define CLCD_PL111_LCUR		0x00000030
-
-#define CLCD_PALL 		0x00000200
-#define CLCD_PALETTE		0x00000200
-
-#define TIM2_CLKSEL		(1 << 5)
-#define TIM2_IVS		(1 << 11)
-#define TIM2_IHS		(1 << 12)
-#define TIM2_IPC		(1 << 13)
-#define TIM2_IOE		(1 << 14)
-#define TIM2_BCD		(1 << 26)
-
-#define CNTL_LCDEN		(1 << 0)
-#define CNTL_LCDBPP1		(0 << 1)
-#define CNTL_LCDBPP2		(1 << 1)
-#define CNTL_LCDBPP4		(2 << 1)
-#define CNTL_LCDBPP8		(3 << 1)
-#define CNTL_LCDBPP16		(4 << 1)
-#define CNTL_LCDBPP16_565	(6 << 1)
-#define CNTL_LCDBPP16_444	(7 << 1)
-#define CNTL_LCDBPP24		(5 << 1)
-#define CNTL_LCDBW		(1 << 4)
-#define CNTL_LCDTFT		(1 << 5)
-#define CNTL_LCDMONO8		(1 << 6)
-#define CNTL_LCDDUAL		(1 << 7)
-#define CNTL_BGR		(1 << 8)
-#define CNTL_BEBO		(1 << 9)
-#define CNTL_BEPO		(1 << 10)
-#define CNTL_LCDPWR		(1 << 11)
-#define CNTL_LCDVCOMP(x)	((x) << 12)
-#define CNTL_LDMAFIFOTIME	(1 << 15)
-#define CNTL_WATERMARK		(1 << 16)
-
-/* ST Microelectronics variant bits */
-#define CNTL_ST_1XBPP_444	0x0
-#define CNTL_ST_1XBPP_5551	(1 << 17)
-#define CNTL_ST_1XBPP_565	(1 << 18)
-#define CNTL_ST_CDWID_12	0x0
-#define CNTL_ST_CDWID_16	(1 << 19)
-#define CNTL_ST_CDWID_18	(1 << 20)
-#define CNTL_ST_CDWID_24	((1 << 19)|(1 << 20))
-#define CNTL_ST_CEAEN		(1 << 21)
-#define CNTL_ST_LCDBPP24_PACKED	(6 << 1)
+#include <linux/amba/clcd-regs.h>
 
 enum {
 	/* individual formats */
-- 
2.11.0

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

* [PATCH 1/2] video: ARM CLCD: Move registers to a separate header.
@ 2017-03-17 22:47   ` Eric Anholt
  0 siblings, 0 replies; 18+ messages in thread
From: Eric Anholt @ 2017-03-17 22:47 UTC (permalink / raw)
  To: dri-devel, tom.cooksey, Russell King; +Cc: linux-kernel

We'd like to reuse these register definitions for the DRM CLCD driver,
but there's a bunch of fbdev-specific code in the current header.

Signed-off-by: Eric Anholt <eric@anholt.net>
---
 include/linux/amba/clcd-regs.h | 76 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/amba/clcd.h      | 68 +------------------------------------
 2 files changed, 77 insertions(+), 67 deletions(-)
 create mode 100644 include/linux/amba/clcd-regs.h

diff --git a/include/linux/amba/clcd-regs.h b/include/linux/amba/clcd-regs.h
new file mode 100644
index 000000000000..1b924c4a63ab
--- /dev/null
+++ b/include/linux/amba/clcd-regs.h
@@ -0,0 +1,76 @@
+/*
+ * David A Rusling
+ *
+ * Copyright (C) 2001 ARM Limited
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+/*
+ * CLCD Controller Internal Register addresses
+ */
+#define CLCD_TIM0		0x00000000
+#define CLCD_TIM1 		0x00000004
+#define CLCD_TIM2 		0x00000008
+#define CLCD_TIM3 		0x0000000c
+#define CLCD_UBAS 		0x00000010
+#define CLCD_LBAS 		0x00000014
+
+#define CLCD_PL110_IENB		0x00000018
+#define CLCD_PL110_CNTL		0x0000001c
+#define CLCD_PL110_STAT		0x00000020
+#define CLCD_PL110_INTR 	0x00000024
+#define CLCD_PL110_UCUR		0x00000028
+#define CLCD_PL110_LCUR		0x0000002C
+
+#define CLCD_PL111_CNTL		0x00000018
+#define CLCD_PL111_IENB		0x0000001c
+#define CLCD_PL111_RIS		0x00000020
+#define CLCD_PL111_MIS		0x00000024
+#define CLCD_PL111_ICR		0x00000028
+#define CLCD_PL111_UCUR		0x0000002c
+#define CLCD_PL111_LCUR		0x00000030
+
+#define CLCD_PALL 		0x00000200
+#define CLCD_PALETTE		0x00000200
+
+#define TIM2_CLKSEL		(1 << 5)
+#define TIM2_IVS		(1 << 11)
+#define TIM2_IHS		(1 << 12)
+#define TIM2_IPC		(1 << 13)
+#define TIM2_IOE		(1 << 14)
+#define TIM2_BCD		(1 << 26)
+
+#define CNTL_LCDEN		(1 << 0)
+#define CNTL_LCDBPP1		(0 << 1)
+#define CNTL_LCDBPP2		(1 << 1)
+#define CNTL_LCDBPP4		(2 << 1)
+#define CNTL_LCDBPP8		(3 << 1)
+#define CNTL_LCDBPP16		(4 << 1)
+#define CNTL_LCDBPP16_565	(6 << 1)
+#define CNTL_LCDBPP16_444	(7 << 1)
+#define CNTL_LCDBPP24		(5 << 1)
+#define CNTL_LCDBW		(1 << 4)
+#define CNTL_LCDTFT		(1 << 5)
+#define CNTL_LCDMONO8		(1 << 6)
+#define CNTL_LCDDUAL		(1 << 7)
+#define CNTL_BGR		(1 << 8)
+#define CNTL_BEBO		(1 << 9)
+#define CNTL_BEPO		(1 << 10)
+#define CNTL_LCDPWR		(1 << 11)
+#define CNTL_LCDVCOMP(x)	((x) << 12)
+#define CNTL_LDMAFIFOTIME	(1 << 15)
+#define CNTL_WATERMARK		(1 << 16)
+
+/* ST Microelectronics variant bits */
+#define CNTL_ST_1XBPP_444	0x0
+#define CNTL_ST_1XBPP_5551	(1 << 17)
+#define CNTL_ST_1XBPP_565	(1 << 18)
+#define CNTL_ST_CDWID_12	0x0
+#define CNTL_ST_CDWID_16	(1 << 19)
+#define CNTL_ST_CDWID_18	(1 << 20)
+#define CNTL_ST_CDWID_24	((1 << 19)|(1 << 20))
+#define CNTL_ST_CEAEN		(1 << 21)
+#define CNTL_ST_LCDBPP24_PACKED	(6 << 1)
diff --git a/include/linux/amba/clcd.h b/include/linux/amba/clcd.h
index 1035879b322c..d0c3be77c18e 100644
--- a/include/linux/amba/clcd.h
+++ b/include/linux/amba/clcd.h
@@ -10,73 +10,7 @@
  * for more details.
  */
 #include <linux/fb.h>
-
-/*
- * CLCD Controller Internal Register addresses
- */
-#define CLCD_TIM0		0x00000000
-#define CLCD_TIM1 		0x00000004
-#define CLCD_TIM2 		0x00000008
-#define CLCD_TIM3 		0x0000000c
-#define CLCD_UBAS 		0x00000010
-#define CLCD_LBAS 		0x00000014
-
-#define CLCD_PL110_IENB		0x00000018
-#define CLCD_PL110_CNTL		0x0000001c
-#define CLCD_PL110_STAT		0x00000020
-#define CLCD_PL110_INTR 	0x00000024
-#define CLCD_PL110_UCUR		0x00000028
-#define CLCD_PL110_LCUR		0x0000002C
-
-#define CLCD_PL111_CNTL		0x00000018
-#define CLCD_PL111_IENB		0x0000001c
-#define CLCD_PL111_RIS		0x00000020
-#define CLCD_PL111_MIS		0x00000024
-#define CLCD_PL111_ICR		0x00000028
-#define CLCD_PL111_UCUR		0x0000002c
-#define CLCD_PL111_LCUR		0x00000030
-
-#define CLCD_PALL 		0x00000200
-#define CLCD_PALETTE		0x00000200
-
-#define TIM2_CLKSEL		(1 << 5)
-#define TIM2_IVS		(1 << 11)
-#define TIM2_IHS		(1 << 12)
-#define TIM2_IPC		(1 << 13)
-#define TIM2_IOE		(1 << 14)
-#define TIM2_BCD		(1 << 26)
-
-#define CNTL_LCDEN		(1 << 0)
-#define CNTL_LCDBPP1		(0 << 1)
-#define CNTL_LCDBPP2		(1 << 1)
-#define CNTL_LCDBPP4		(2 << 1)
-#define CNTL_LCDBPP8		(3 << 1)
-#define CNTL_LCDBPP16		(4 << 1)
-#define CNTL_LCDBPP16_565	(6 << 1)
-#define CNTL_LCDBPP16_444	(7 << 1)
-#define CNTL_LCDBPP24		(5 << 1)
-#define CNTL_LCDBW		(1 << 4)
-#define CNTL_LCDTFT		(1 << 5)
-#define CNTL_LCDMONO8		(1 << 6)
-#define CNTL_LCDDUAL		(1 << 7)
-#define CNTL_BGR		(1 << 8)
-#define CNTL_BEBO		(1 << 9)
-#define CNTL_BEPO		(1 << 10)
-#define CNTL_LCDPWR		(1 << 11)
-#define CNTL_LCDVCOMP(x)	((x) << 12)
-#define CNTL_LDMAFIFOTIME	(1 << 15)
-#define CNTL_WATERMARK		(1 << 16)
-
-/* ST Microelectronics variant bits */
-#define CNTL_ST_1XBPP_444	0x0
-#define CNTL_ST_1XBPP_5551	(1 << 17)
-#define CNTL_ST_1XBPP_565	(1 << 18)
-#define CNTL_ST_CDWID_12	0x0
-#define CNTL_ST_CDWID_16	(1 << 19)
-#define CNTL_ST_CDWID_18	(1 << 20)
-#define CNTL_ST_CDWID_24	((1 << 19)|(1 << 20))
-#define CNTL_ST_CEAEN		(1 << 21)
-#define CNTL_ST_LCDBPP24_PACKED	(6 << 1)
+#include <linux/amba/clcd-regs.h>
 
 enum {
 	/* individual formats */
-- 
2.11.0

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

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

* [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111
  2017-03-17 22:47 ` Eric Anholt
@ 2017-03-17 22:47   ` Eric Anholt
  -1 siblings, 0 replies; 18+ messages in thread
From: Eric Anholt @ 2017-03-17 22:47 UTC (permalink / raw)
  To: dri-devel, tom.cooksey, Russell King; +Cc: linux-kernel, Eric Anholt

From: Tom Cooksey <tom.cooksey@arm.com>

This is a modesetting driver for the pl111 CLCD display controller
found on various ARM platforms such as the Versatile Express. The
driver has only been tested on the bcm911360_entphn platform so far,
with PRIME-based buffer sharing between vc4 and clcd.

It reuses the existing devicetree binding, while not using quite as
many of its properties as the fbdev driver does (those are left for
future work).

v2: Nearly complete rewrite by anholt, cutting 2/3 of the code thanks
    to DRM core's excellent new helpers.

Signed-off-by: Tom Cooksey <tom.cooksey@arm.com>
Signed-off-by: Eric Anholt <eric@anholt.net>
---
 Documentation/gpu/index.rst             |   1 +
 Documentation/gpu/pl111.rst             |   6 +
 drivers/gpu/drm/Kconfig                 |   2 +
 drivers/gpu/drm/Makefile                |   1 +
 drivers/gpu/drm/pl111/Kconfig           |  12 ++
 drivers/gpu/drm/pl111/Makefile          |   8 +
 drivers/gpu/drm/pl111/pl111_connector.c | 127 ++++++++++++++
 drivers/gpu/drm/pl111/pl111_crtc.c      | 239 ++++++++++++++++++++++++++
 drivers/gpu/drm/pl111/pl111_drm.h       |  64 +++++++
 drivers/gpu/drm/pl111/pl111_drv.c       | 292 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/pl111/pl111_encoder.c   |  50 ++++++
 drivers/gpu/drm/pl111/pl111_gem.c       |  35 ++++
 drivers/gpu/drm/pl111/pl111_plane.c     | 167 ++++++++++++++++++
 13 files changed, 1004 insertions(+)
 create mode 100644 Documentation/gpu/pl111.rst
 create mode 100644 drivers/gpu/drm/pl111/Kconfig
 create mode 100644 drivers/gpu/drm/pl111/Makefile
 create mode 100644 drivers/gpu/drm/pl111/pl111_connector.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_crtc.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_drm.h
 create mode 100644 drivers/gpu/drm/pl111/pl111_drv.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_encoder.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_gem.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_plane.c

diff --git a/Documentation/gpu/index.rst b/Documentation/gpu/index.rst
index e998ee0d0dd5..71bf510d47e8 100644
--- a/Documentation/gpu/index.rst
+++ b/Documentation/gpu/index.rst
@@ -11,6 +11,7 @@ Linux GPU Driver Developer's Guide
    drm-kms-helpers
    drm-uapi
    i915
+   pl111
    tinydrm
    vc4
    vga-switcheroo
diff --git a/Documentation/gpu/pl111.rst b/Documentation/gpu/pl111.rst
new file mode 100644
index 000000000000..9b03736d33dd
--- /dev/null
+++ b/Documentation/gpu/pl111.rst
@@ -0,0 +1,6 @@
+==========================================
+ drm/pl111 ARM PrimeCell PL111 CLCD Driver
+==========================================
+
+.. kernel-doc:: drivers/gpu/drm/pl111/pl111_drv.c
+   :doc: ARM PrimeCell PL111 CLCD Driver
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 78d7fc0ebb57..d1c6c12199b7 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -274,6 +274,8 @@ source "drivers/gpu/drm/meson/Kconfig"
 
 source "drivers/gpu/drm/tinydrm/Kconfig"
 
+source "drivers/gpu/drm/pl111/Kconfig"
+
 # Keep legacy drivers last
 
 menuconfig DRM_LEGACY
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 59aae43005ee..99810a529bb0 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -95,3 +95,4 @@ obj-y			+= hisilicon/
 obj-$(CONFIG_DRM_ZTE)	+= zte/
 obj-$(CONFIG_DRM_MXSFB)	+= mxsfb/
 obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
+obj-$(CONFIG_DRM_PL111) += pl111/
diff --git a/drivers/gpu/drm/pl111/Kconfig b/drivers/gpu/drm/pl111/Kconfig
new file mode 100644
index 000000000000..ede49efd531f
--- /dev/null
+++ b/drivers/gpu/drm/pl111/Kconfig
@@ -0,0 +1,12 @@
+config DRM_PL111
+	tristate "DRM Support for PL111 CLCD Controller"
+	depends on DRM
+	depends on ARM || ARM64 || COMPILE_TEST
+	select DRM_KMS_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_GEM_CMA_HELPER
+	select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+	help
+	  Choose this option for DRM support for the PL111 CLCD controller.
+	  If M is selected the module will be called pl111_drm.
+
diff --git a/drivers/gpu/drm/pl111/Makefile b/drivers/gpu/drm/pl111/Makefile
new file mode 100644
index 000000000000..20a7fd76d513
--- /dev/null
+++ b/drivers/gpu/drm/pl111/Makefile
@@ -0,0 +1,8 @@
+pl111_drm-y +=	pl111_connector.o \
+		pl111_crtc.o \
+		pl111_drv.o \
+		pl111_encoder.o \
+		pl111_gem.o \
+		pl111_plane.o
+
+obj-$(CONFIG_DRM_PL111) += pl111_drm.o
diff --git a/drivers/gpu/drm/pl111/pl111_connector.c b/drivers/gpu/drm/pl111/pl111_connector.c
new file mode 100644
index 000000000000..9811d1eadb63
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_connector.c
@@ -0,0 +1,127 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * pl111_drm_connector.c
+ * Implementation of the connector functions for PL111 DRM
+ */
+#include <linux/amba/clcd-regs.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+
+#include "pl111_drm.h"
+
+static void pl111_connector_destroy(struct drm_connector *connector)
+{
+	struct pl111_drm_connector *pl111_connector =
+		to_pl111_connector(connector);
+
+	if (pl111_connector->panel)
+		drm_panel_detach(pl111_connector->panel);
+
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status pl111_connector_detect(struct drm_connector
+							*connector, bool force)
+{
+	struct pl111_drm_connector *pl111_connector =
+		to_pl111_connector(connector);
+
+	return (pl111_connector->panel ?
+		connector_status_connected :
+		connector_status_disconnected);
+}
+
+static int pl111_connector_helper_get_modes(struct drm_connector *connector)
+{
+	struct pl111_drm_connector *pl111_connector =
+		to_pl111_connector(connector);
+
+	if (!pl111_connector->panel)
+		return 0;
+
+	return drm_panel_get_modes(pl111_connector->panel);
+}
+
+const struct drm_connector_funcs connector_funcs = {
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = pl111_connector_destroy,
+	.detect = pl111_connector_detect,
+	.dpms = drm_atomic_helper_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+const struct drm_connector_helper_funcs connector_helper_funcs = {
+	.get_modes = pl111_connector_helper_get_modes,
+};
+
+/* Walks the OF graph to find the panel node and then asks DRM to look
+ * up the panel.
+ */
+static struct drm_panel *pl111_get_panel(struct device *dev)
+{
+	struct device_node *endpoint, *panel_node;
+	struct device_node *np = dev->of_node;
+	struct drm_panel *panel;
+
+	endpoint = of_graph_get_next_endpoint(np, NULL);
+	if (!endpoint) {
+		dev_err(dev, "no endpoint to fetch panel\n");
+		return NULL;
+	}
+
+	/* don't proceed if we have an endpoint but no panel_node tied to it */
+	panel_node = of_graph_get_remote_port_parent(endpoint);
+	of_node_put(endpoint);
+	if (!panel_node) {
+		dev_err(dev, "no valid panel node\n");
+		return NULL;
+	}
+
+	panel = of_drm_find_panel(panel_node);
+	of_node_put(panel_node);
+
+	return panel;
+}
+
+int pl111_connector_create(struct drm_device *dev)
+{
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+	struct pl111_drm_connector *pl111_connector = &priv->connector;
+	struct drm_connector *connector = &pl111_connector->connector;
+
+	drm_connector_init(dev, connector, &connector_funcs,
+			   DRM_MODE_CONNECTOR_DPI);
+	drm_connector_helper_add(connector, &connector_helper_funcs);
+
+	pl111_connector->panel = pl111_get_panel(dev->dev);
+	if (pl111_connector->panel)
+		drm_panel_attach(pl111_connector->panel, connector);
+
+	return 0;
+}
+
diff --git a/drivers/gpu/drm/pl111/pl111_crtc.c b/drivers/gpu/drm/pl111/pl111_crtc.c
new file mode 100644
index 000000000000..4c73bdd3b7e4
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_crtc.c
@@ -0,0 +1,239 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * pl111_drm_crtc.c
+ * Implementation of the CRTC functions for PL111 DRM
+ */
+#include <linux/amba/clcd-regs.h>
+#include <linux/clk.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+
+#include "pl111_drm.h"
+
+irqreturn_t pl111_irq(int irq, void *data)
+{
+	struct pl111_drm_dev_private *priv = data;
+	u32 irq_stat;
+	irqreturn_t status = IRQ_NONE;
+
+	irq_stat = readl(priv->regs + CLCD_PL111_MIS);
+
+	if (!irq_stat)
+		return IRQ_NONE;
+
+	if (irq_stat & CLCD_IRQ_NEXTBASE_UPDATE) {
+		drm_crtc_handle_vblank(&priv->crtc);
+
+		status = IRQ_HANDLED;
+	}
+
+	/* Clear the interrupt once done */
+	writel(irq_stat, priv->regs + CLCD_PL111_ICR);
+
+	return status;
+}
+
+static int pl111_crtc_atomic_check(struct drm_crtc *crtc,
+				   struct drm_crtc_state *state)
+{
+	const struct drm_display_mode *mode = &state->mode;
+
+	if (!state->active)
+		return 0;
+
+	if (mode->hdisplay % 16)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void pl111_crtc_helper_mode_set_nofb(struct drm_crtc *crtc)
+{
+	struct drm_device *drm = crtc->dev;
+	struct pl111_drm_dev_private *priv = crtc->dev->dev_private;
+	const struct drm_display_mode *mode = &crtc->state->mode;
+	struct drm_connector *connector = &priv->connector.connector;
+	unsigned int ppl, hsw, hfp, hbp;
+	unsigned int lpp, vsw, vfp, vbp;
+	unsigned int cpl;
+	int ret;
+
+	ret = clk_set_rate(priv->clk, mode->clock * 1000);
+	if (ret) {
+		dev_err(drm->dev,
+			"Failed to set pixel clock rate to %d: %d\n",
+			mode->clock * 1000, ret);
+	}
+
+	ppl = (mode->hdisplay / 16) - 1;
+	hsw = mode->hsync_end - mode->hsync_start - 1;
+	hfp = mode->hsync_start - mode->hdisplay - 1;
+	hbp = mode->htotal - mode->hsync_end - 1;
+
+	lpp = mode->vdisplay - 1;
+	vsw = mode->vsync_end - mode->vsync_start - 1;
+	vfp = mode->vsync_start - mode->vdisplay;
+	vbp = mode->vtotal - mode->vsync_end;
+
+	cpl = mode->hdisplay - 1;
+
+	writel((ppl << 2) |
+	       (hsw << 8) |
+	       (hfp << 16) |
+	       (hbp << 24),
+	       priv->regs + CLCD_TIM0);
+	writel(lpp |
+	       (vsw << 10) |
+	       (vfp << 16) |
+	       (vbp << 24),
+	       priv->regs + CLCD_TIM1);
+	/* XXX: We currently always use CLCDCLK with no divisor.  We
+	 * could probably reduce power consumption by using HCLK
+	 * (apb_pclk) with a divisor when it gets us near our target
+	 * pixel clock.
+	 */
+	writel(((mode->flags & DRM_MODE_FLAG_NHSYNC) ? TIM2_IHS : 0) |
+	       ((mode->flags & DRM_MODE_FLAG_NVSYNC) ? TIM2_IVS : 0) |
+	       ((connector->display_info.bus_flags &
+		 DRM_BUS_FLAG_DE_LOW) ? TIM2_IOE : 0) |
+	       ((connector->display_info.bus_flags &
+		 DRM_BUS_FLAG_PIXDATA_NEGEDGE) ? TIM2_IPC : 0) |
+	       TIM2_BCD |
+	       (cpl << 16) |
+	       TIM2_CLKSEL,
+	       priv->regs + CLCD_TIM2);
+	writel(0, priv->regs + CLCD_TIM3);
+}
+
+static void pl111_crtc_helper_enable(struct drm_crtc *crtc)
+{
+	struct pl111_drm_dev_private *priv = crtc->dev->dev_private;
+	u32 cntl;
+
+	clk_prepare_enable(priv->clk);
+
+	drm_panel_prepare(priv->connector.panel);
+
+	/* Enable and Power Up */
+	cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDPWR | CNTL_LCDVCOMP(1);
+
+	/* Keep the format that the primary plane had set up. */
+	cntl |= readl(priv->regs + CLCD_PL111_CNTL) & (7 << 1);
+
+	writel(cntl, priv->regs + CLCD_PL111_CNTL);
+
+	drm_panel_enable(priv->connector.panel);
+}
+
+void pl111_crtc_helper_disable(struct drm_crtc *crtc)
+{
+	struct pl111_drm_dev_private *priv = crtc->dev->dev_private;
+
+	drm_panel_disable(priv->connector.panel);
+
+	/* Disable and Power Down */
+	writel(readl(priv->regs + CLCD_PL111_CNTL) & (7 << 1),
+	       priv->regs + CLCD_PL111_CNTL);
+
+	drm_panel_unprepare(priv->connector.panel);
+
+	/* Disable clock */
+	clk_disable_unprepare(priv->clk);
+}
+
+static void pl111_crtc_helper_atomic_flush(struct drm_crtc *crtc,
+					   struct drm_crtc_state *old_state)
+{
+	struct drm_pending_vblank_event *event = crtc->state->event;
+
+	if (event) {
+		crtc->state->event = NULL;
+
+		spin_lock_irq(&crtc->dev->event_lock);
+		if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0)
+			drm_crtc_arm_vblank_event(crtc, event);
+		else
+			drm_crtc_send_vblank_event(crtc, event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+	}
+}
+
+static int pl111_enable_vblank(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+
+	clk_prepare_enable(priv->clk);
+
+	writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + CLCD_PL111_IENB);
+
+	return 0;
+}
+
+static void pl111_disable_vblank(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+
+	writel(0, priv->regs + CLCD_PL111_IENB);
+
+	clk_disable_unprepare(priv->clk);
+}
+
+const struct drm_crtc_funcs crtc_funcs = {
+	.set_config = drm_atomic_helper_set_config,
+	.page_flip = drm_atomic_helper_page_flip,
+	.reset = drm_atomic_helper_crtc_reset,
+	.destroy = drm_crtc_cleanup,
+	.enable_vblank = pl111_enable_vblank,
+	.disable_vblank = pl111_disable_vblank,
+	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+const struct drm_crtc_helper_funcs crtc_helper_funcs = {
+	.mode_set_nofb = pl111_crtc_helper_mode_set_nofb,
+	.atomic_check = pl111_crtc_atomic_check,
+	.atomic_flush = pl111_crtc_helper_atomic_flush,
+	.disable = pl111_crtc_helper_disable,
+	.enable = pl111_crtc_helper_enable,
+};
+
+int pl111_crtc_create(struct drm_device *dev)
+{
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+	struct drm_crtc *crtc = &priv->crtc;
+
+	drm_crtc_init_with_planes(dev, crtc,
+				  &priv->primary, NULL,
+				  &crtc_funcs, "primary");
+	drm_crtc_helper_add(crtc, &crtc_helper_funcs);
+
+	/* XXX: The runtime clock disabling still results in
+	 * occasional system hangs, and needs debugging.
+	 */
+	clk_prepare_enable(priv->clk);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h
new file mode 100644
index 000000000000..36e4ce770a6c
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_drm.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+#ifndef _PL111_DRM_H_
+#define _PL111_DRM_H_
+
+#include <drm/drm_gem.h>
+
+#define CLCD_IRQ_NEXTBASE_UPDATE BIT(2)
+
+struct pl111_drm_connector {
+	struct drm_connector connector;
+	struct drm_panel *panel;
+};
+
+struct pl111_drm_dev_private {
+	struct drm_device *drm;
+
+	struct pl111_drm_connector connector;
+	struct drm_crtc crtc;
+	struct drm_encoder encoder;
+	struct drm_plane primary;
+	struct drm_fbdev_cma *fbdev;
+
+	void *regs;
+	struct clk *clk;
+};
+
+#define to_pl111_connector(x) \
+	container_of(x, struct pl111_drm_connector, connector)
+
+/* CRTC Functions */
+int pl111_crtc_create(struct drm_device *dev);
+irqreturn_t pl111_irq(int irq, void *data);
+
+int pl111_primary_plane_init(struct drm_device *dev);
+
+/* Connector Functions */
+int pl111_connector_create(struct drm_device *dev);
+
+/* Encoder Functions */
+int pl111_encoder_init(struct drm_device *dev);
+
+/* GEM Functions */
+int pl111_dumb_create(struct drm_file *file_priv,
+		      struct drm_device *dev,
+		      struct drm_mode_create_dumb *args);
+
+#endif /* _PL111_DRM_H_ */
diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c
new file mode 100644
index 000000000000..571c296dddf7
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_drv.c
@@ -0,0 +1,292 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * DOC: ARM PrimeCell PL111 CLCD Driver
+ *
+ * The PL111 is a simple LCD controller that can support TFT and STN
+ * displays.  This driver exposes a standard KMS interface for them.
+ *
+ * This driver uses the same Device Tree binding as the fbdev CLCD
+ * driver.  While the fbdev driver supports panels that may be
+ * connected to the CLCD internally to the CLCD driver, in DRM the
+ * panels get split out to drivers/gpu/drm/panels/.  This means that,
+ * in converting from using fbdev to using DRM, you also need to write
+ * a panel driver (which may be as simple as an entry in
+ * panel-simple.c).
+ *
+ * The driver currently doesn't expose the cursor.  The DRM API for
+ * cursors requires support for 64x64 ARGB8888 cursor images, while
+ * the hardware can only support 64x64 monochrome with masking
+ * cursors.  While one could imagine trying to hack something together
+ * to look at the ARGB8888 and program reasonable in monochrome, we
+ * just don't expose the cursor at all instead, and leave cursor
+ * support to the X11 software cursor layer.
+ *
+ * TODO:
+ *
+ * - Fix race between setting plane base address and getting IRQ for
+ *   vsync firing the pageflip completion.
+ *
+ * - Expose the correct set of formats we can support based on the
+ *   "arm,pl11x,tft-r0g0b0-pads" DT property.
+ *
+ * - Use the "max-memory-bandwidth" DT property to filter the
+ *   supported formats.
+ *
+ * - Read back hardware state at boot to skip reprogramming the
+ *   hardware when doing a no-op modeset.
+ *
+ * - Use the internal clock divisor to reduce power consumption by
+ *   using HCLK (apb_pclk) when appropriate.
+ */
+
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd-regs.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "pl111_drm.h"
+
+#define DRIVER_DESC      "DRM module for PL111"
+
+struct drm_mode_config_funcs mode_config_funcs = {
+	.fb_create = drm_fb_cma_create,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static int pl111_modeset_init(struct drm_device *dev)
+{
+	struct drm_mode_config *mode_config;
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+	int ret = 0;
+
+	if (!priv)
+		return -EINVAL;
+
+	drm_mode_config_init(dev);
+	mode_config = &dev->mode_config;
+	mode_config->funcs = &mode_config_funcs;
+	mode_config->min_width = 1;
+	mode_config->max_width = 1024;
+	mode_config->min_height = 1;
+	mode_config->max_height = 768;
+
+	ret = pl111_primary_plane_init(dev);
+	if (ret != 0) {
+		dev_err(dev->dev, "Failed to init primary plane\n");
+		goto out_config;
+	}
+
+	ret = pl111_crtc_create(dev);
+	if (ret) {
+		dev_err(dev->dev, "Failed to create crtc\n");
+		goto out_config;
+	}
+
+	ret = pl111_connector_create(dev);
+	if (ret) {
+		dev_err(dev->dev, "Failed to create pl111_drm_connector\n");
+		goto out_config;
+	}
+
+	ret = pl111_encoder_init(dev);
+	if (ret) {
+		dev_err(dev->dev, "Failed to create pl111_drm_encoder\n");
+		goto out_config;
+	}
+
+	ret = drm_mode_connector_attach_encoder(&priv->connector.connector,
+						&priv->encoder);
+	if (ret != 0) {
+		dev_err(dev->dev, "Failed to attach encoder\n");
+		goto out_config;
+	}
+
+	priv->connector.connector.encoder = &priv->encoder;
+
+	ret = drm_vblank_init(dev, 1);
+	if (ret != 0) {
+		dev_err(dev->dev, "Failed to init vblank\n");
+		goto out_config;
+	}
+
+	drm_mode_config_reset(dev);
+
+	priv->fbdev = drm_fbdev_cma_init(dev, 32,
+					 dev->mode_config.num_connector);
+
+	drm_kms_helper_poll_init(dev);
+
+	goto finish;
+
+out_config:
+	drm_mode_config_cleanup(dev);
+finish:
+	return ret;
+}
+
+static const struct file_operations drm_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.release = drm_release,
+	.unlocked_ioctl = drm_ioctl,
+	.mmap = drm_gem_cma_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+};
+
+static void pl111_lastclose(struct drm_device *dev)
+{
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+
+	drm_fbdev_cma_restore_mode(priv->fbdev);
+}
+
+static struct drm_driver pl111_drm_driver = {
+	.driver_features =
+		DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC,
+	.lastclose = pl111_lastclose,
+	.ioctls = NULL,
+	.fops = &drm_fops,
+	.name = "pl111_drm",
+	.desc = DRIVER_DESC,
+	.date = "20170317",
+	.major = 1,
+	.minor = 0,
+	.patchlevel = 0,
+	.dumb_create = pl111_dumb_create,
+	.dumb_destroy = drm_gem_dumb_destroy,
+	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
+	.gem_free_object = drm_gem_cma_free_object,
+	.gem_vm_ops = &drm_gem_cma_vm_ops,
+
+	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.gem_prime_import = drm_gem_prime_import,
+	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+	.gem_prime_export = drm_gem_prime_export,
+	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
+};
+
+#ifdef CONFIG_ARM_AMBA
+static int pl111_amba_probe(struct amba_device *amba_dev,
+			    const struct amba_id *id)
+{
+	struct device *dev = &amba_dev->dev;
+	struct pl111_drm_dev_private *priv;
+	struct drm_device *drm;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	drm = drm_dev_alloc(&pl111_drm_driver, dev);
+	if (IS_ERR(drm))
+		return PTR_ERR(drm);
+	amba_set_drvdata(amba_dev, drm);
+	priv->drm = drm;
+	drm->dev_private = priv;
+
+	priv->clk = devm_clk_get(dev, "clcdclk");
+	if (IS_ERR(priv->clk)) {
+		dev_err(dev, "CLCD: unable to get clk.\n");
+		ret = PTR_ERR(priv->clk);
+		goto dev_unref;
+	}
+
+	priv->regs = devm_ioremap_resource(dev, &amba_dev->res);
+	if (!priv->regs) {
+		dev_err(dev, "%s failed mmio\n", __func__);
+		return -EINVAL;
+	}
+
+	/* turn off interrupts before requesting the irq */
+	writel(0, priv->regs + CLCD_PL111_IENB);
+
+	ret = devm_request_irq(dev, amba_dev->irq[0], pl111_irq, 0,
+			       "pl111", priv);
+	if (ret != 0) {
+		dev_err(dev, "%s failed irq %d\n", __func__, ret);
+		return ret;
+	}
+
+	ret = pl111_modeset_init(drm);
+	if (ret != 0) {
+		dev_err(dev, "Failed to init modeset\n");
+		goto dev_unref;
+	}
+
+	ret = drm_dev_register(drm, 0);
+	if (ret < 0)
+		goto dev_unref;
+
+	return 0;
+
+dev_unref:
+	drm_dev_unref(drm);
+	return ret;
+}
+
+static int pl111_amba_remove(struct amba_device *amba_dev)
+{
+	struct drm_device *drm = amba_get_drvdata(amba_dev);
+	struct pl111_drm_dev_private *priv = drm->dev_private;
+
+	drm_dev_unregister(drm);
+	if (priv->fbdev)
+		drm_fbdev_cma_fini(priv->fbdev);
+	drm_mode_config_cleanup(drm);
+	drm_dev_unref(drm);
+
+	return 0;
+}
+
+static struct amba_id pl111_id_table[] = {
+	{
+		.id = 0x00041110,
+		.mask = 0x000ffffe,
+	},
+	{0, 0},
+};
+
+static struct amba_driver pl111_amba_driver = {
+	.drv = {
+		.name = "clcd-pl11x",
+	},
+	.probe = pl111_amba_probe,
+	.remove = pl111_amba_remove,
+	.id_table = pl111_id_table,
+};
+#endif /* CONFIG_ARM_AMBA */
+
+module_amba_driver(pl111_amba_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("ARM Ltd.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pl111_drm");
diff --git a/drivers/gpu/drm/pl111/pl111_encoder.c b/drivers/gpu/drm/pl111/pl111_encoder.c
new file mode 100644
index 000000000000..78d67ceb46f3
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_encoder.c
@@ -0,0 +1,50 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * pl111_drm_encoder.c
+ * Implementation of the encoder functions for PL111 DRM
+ */
+#include <linux/amba/clcd-regs.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "pl111_drm.h"
+
+static const struct drm_encoder_funcs pl111_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+int pl111_encoder_init(struct drm_device *dev)
+{
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+	struct drm_encoder *encoder = &priv->encoder;
+	int ret;
+
+	ret = drm_encoder_init(dev, encoder, &pl111_encoder_funcs,
+			       DRM_MODE_ENCODER_DPI, NULL);
+	if (ret)
+		return ret;
+
+	encoder->crtc = &priv->crtc;
+	encoder->possible_crtcs = drm_crtc_mask(encoder->crtc);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/pl111/pl111_gem.c b/drivers/gpu/drm/pl111/pl111_gem.c
new file mode 100644
index 000000000000..b7b0e453dea9
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_gem.c
@@ -0,0 +1,35 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * pl111_drm_gem.c
+ * Implementation of the GEM functions for PL111 DRM
+ */
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include "pl111_drm.h"
+
+int pl111_dumb_create(struct drm_file *file_priv,
+		      struct drm_device *dev, struct drm_mode_create_dumb *args)
+{
+	args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+	return drm_gem_cma_dumb_create_internal(file_priv, dev, args);
+}
diff --git a/drivers/gpu/drm/pl111/pl111_plane.c b/drivers/gpu/drm/pl111/pl111_plane.c
new file mode 100644
index 000000000000..bcdc2f43de46
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_plane.c
@@ -0,0 +1,167 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+#include <linux/amba/clcd-regs.h>
+#include <linux/of_graph.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "pl111_drm.h"
+
+static int pl111_primary_plane_atomic_check(struct drm_plane *plane,
+					    struct drm_plane_state *state)
+{
+	return 0;
+}
+
+static void pl111_primary_plane_atomic_update(struct drm_plane *plane,
+					      struct drm_plane_state *old_state)
+{
+	struct drm_device *dev = plane->dev;
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+	struct drm_framebuffer *fb = plane->state->fb;
+	struct drm_gem_cma_object *obj;
+	u32 addr, cntl;
+
+	if (!fb)
+		return;
+
+	obj = drm_fb_cma_get_gem_obj(fb, 0);
+	addr = obj->paddr + fb->offsets[0];
+	addr += fb->format->cpp[0] * plane->state->src_x;
+	addr += fb->pitches[0] * plane->state->src_y;
+
+	writel(addr, priv->regs + CLCD_UBAS);
+	writel(addr + (fb->height - 1 * fb->pitches[0]),
+	       priv->regs + CLCD_LBAS);
+
+	cntl = readl(priv->regs + CLCD_PL111_CNTL);
+	cntl &= ~(7 << 1);
+
+	/* Note that the the hardware's format reader takes 'r' from
+	 * the low bit, while DRM formats list channels from high bit
+	 * to low bit as you read left to right.
+	 */
+	switch (fb->format->format) {
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_XBGR8888:
+		cntl |= CNTL_LCDBPP24;
+		break;
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XRGB8888:
+		cntl |= CNTL_LCDBPP24 | CNTL_BGR;
+		break;
+	case DRM_FORMAT_BGR565:
+		cntl |= CNTL_LCDBPP16_565;
+		break;
+	case DRM_FORMAT_RGB565:
+		cntl |= CNTL_LCDBPP16_565 | CNTL_BGR;
+		break;
+	case DRM_FORMAT_ABGR1555:
+	case DRM_FORMAT_XBGR1555:
+		cntl |= CNTL_LCDBPP16;
+		break;
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_XRGB1555:
+		cntl |= CNTL_LCDBPP16 | CNTL_BGR;
+		break;
+	case DRM_FORMAT_ABGR4444:
+	case DRM_FORMAT_XBGR4444:
+		cntl |= CNTL_LCDBPP16_444;
+		break;
+	case DRM_FORMAT_ARGB4444:
+	case DRM_FORMAT_XRGB4444:
+		cntl |= CNTL_LCDBPP16_444 | CNTL_BGR;
+		break;
+	}
+
+	writel(cntl, priv->regs + CLCD_PL111_CNTL);
+}
+
+static const struct drm_plane_helper_funcs pl111_primary_plane_helper_funcs = {
+	.atomic_check = pl111_primary_plane_atomic_check,
+	.atomic_update = pl111_primary_plane_atomic_update,
+};
+
+static const struct drm_plane_funcs pl111_primary_plane_funcs = {
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
+	.reset = drm_atomic_helper_plane_reset,
+	.destroy = drm_plane_cleanup,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+int pl111_primary_plane_init(struct drm_device *drm)
+{
+	struct pl111_drm_dev_private *priv = drm->dev_private;
+	struct drm_plane *plane = &priv->primary;
+	struct device *dev = drm->dev;
+	static const u32 formats[] = {
+		DRM_FORMAT_ABGR8888,
+		DRM_FORMAT_XBGR8888,
+		DRM_FORMAT_ARGB8888,
+		DRM_FORMAT_XRGB8888,
+		DRM_FORMAT_BGR565,
+		DRM_FORMAT_RGB565,
+		DRM_FORMAT_ABGR1555,
+		DRM_FORMAT_XBGR1555,
+		DRM_FORMAT_ARGB1555,
+		DRM_FORMAT_XRGB1555,
+		DRM_FORMAT_ABGR4444,
+		DRM_FORMAT_XBGR4444,
+		DRM_FORMAT_ARGB4444,
+		DRM_FORMAT_XRGB4444,
+	};
+	struct device_node *endpoint;
+	u32 tft_r0b0g0[3];
+	int ret;
+
+	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (!endpoint)
+		return -ENODEV;
+
+	if (of_property_read_u32_array(endpoint,
+				       "arm,pl11x,tft-r0g0b0-pads",
+				       tft_r0b0g0,
+				       ARRAY_SIZE(tft_r0b0g0)) != 0) {
+		dev_err(dev, "arm,pl11x,tft-r0g0b0-pads should be 3 ints\n");
+		of_node_put(endpoint);
+		return -ENOENT;
+	}
+	of_node_put(endpoint);
+
+	if (tft_r0b0g0[0] != 0 ||
+	    tft_r0b0g0[1] != 8 ||
+	    tft_r0b0g0[2] != 16) {
+		dev_err(dev, "arm,pl11x,tft-r0g0b0-pads != [0,8,16] not yet supported\n");
+		return -EINVAL;
+	}
+
+	ret = drm_universal_plane_init(drm, plane, 0,
+				       &pl111_primary_plane_funcs,
+				       formats, ARRAY_SIZE(formats),
+				       DRM_PLANE_TYPE_PRIMARY, NULL);
+	if (ret)
+		return ret;
+
+	drm_plane_helper_add(plane, &pl111_primary_plane_helper_funcs);
+
+	return 0;
+}
-- 
2.11.0

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

* [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111
@ 2017-03-17 22:47   ` Eric Anholt
  0 siblings, 0 replies; 18+ messages in thread
From: Eric Anholt @ 2017-03-17 22:47 UTC (permalink / raw)
  To: dri-devel, tom.cooksey, Russell King; +Cc: linux-kernel

From: Tom Cooksey <tom.cooksey@arm.com>

This is a modesetting driver for the pl111 CLCD display controller
found on various ARM platforms such as the Versatile Express. The
driver has only been tested on the bcm911360_entphn platform so far,
with PRIME-based buffer sharing between vc4 and clcd.

It reuses the existing devicetree binding, while not using quite as
many of its properties as the fbdev driver does (those are left for
future work).

v2: Nearly complete rewrite by anholt, cutting 2/3 of the code thanks
    to DRM core's excellent new helpers.

Signed-off-by: Tom Cooksey <tom.cooksey@arm.com>
Signed-off-by: Eric Anholt <eric@anholt.net>
---
 Documentation/gpu/index.rst             |   1 +
 Documentation/gpu/pl111.rst             |   6 +
 drivers/gpu/drm/Kconfig                 |   2 +
 drivers/gpu/drm/Makefile                |   1 +
 drivers/gpu/drm/pl111/Kconfig           |  12 ++
 drivers/gpu/drm/pl111/Makefile          |   8 +
 drivers/gpu/drm/pl111/pl111_connector.c | 127 ++++++++++++++
 drivers/gpu/drm/pl111/pl111_crtc.c      | 239 ++++++++++++++++++++++++++
 drivers/gpu/drm/pl111/pl111_drm.h       |  64 +++++++
 drivers/gpu/drm/pl111/pl111_drv.c       | 292 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/pl111/pl111_encoder.c   |  50 ++++++
 drivers/gpu/drm/pl111/pl111_gem.c       |  35 ++++
 drivers/gpu/drm/pl111/pl111_plane.c     | 167 ++++++++++++++++++
 13 files changed, 1004 insertions(+)
 create mode 100644 Documentation/gpu/pl111.rst
 create mode 100644 drivers/gpu/drm/pl111/Kconfig
 create mode 100644 drivers/gpu/drm/pl111/Makefile
 create mode 100644 drivers/gpu/drm/pl111/pl111_connector.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_crtc.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_drm.h
 create mode 100644 drivers/gpu/drm/pl111/pl111_drv.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_encoder.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_gem.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_plane.c

diff --git a/Documentation/gpu/index.rst b/Documentation/gpu/index.rst
index e998ee0d0dd5..71bf510d47e8 100644
--- a/Documentation/gpu/index.rst
+++ b/Documentation/gpu/index.rst
@@ -11,6 +11,7 @@ Linux GPU Driver Developer's Guide
    drm-kms-helpers
    drm-uapi
    i915
+   pl111
    tinydrm
    vc4
    vga-switcheroo
diff --git a/Documentation/gpu/pl111.rst b/Documentation/gpu/pl111.rst
new file mode 100644
index 000000000000..9b03736d33dd
--- /dev/null
+++ b/Documentation/gpu/pl111.rst
@@ -0,0 +1,6 @@
+==========================================
+ drm/pl111 ARM PrimeCell PL111 CLCD Driver
+==========================================
+
+.. kernel-doc:: drivers/gpu/drm/pl111/pl111_drv.c
+   :doc: ARM PrimeCell PL111 CLCD Driver
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 78d7fc0ebb57..d1c6c12199b7 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -274,6 +274,8 @@ source "drivers/gpu/drm/meson/Kconfig"
 
 source "drivers/gpu/drm/tinydrm/Kconfig"
 
+source "drivers/gpu/drm/pl111/Kconfig"
+
 # Keep legacy drivers last
 
 menuconfig DRM_LEGACY
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 59aae43005ee..99810a529bb0 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -95,3 +95,4 @@ obj-y			+= hisilicon/
 obj-$(CONFIG_DRM_ZTE)	+= zte/
 obj-$(CONFIG_DRM_MXSFB)	+= mxsfb/
 obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
+obj-$(CONFIG_DRM_PL111) += pl111/
diff --git a/drivers/gpu/drm/pl111/Kconfig b/drivers/gpu/drm/pl111/Kconfig
new file mode 100644
index 000000000000..ede49efd531f
--- /dev/null
+++ b/drivers/gpu/drm/pl111/Kconfig
@@ -0,0 +1,12 @@
+config DRM_PL111
+	tristate "DRM Support for PL111 CLCD Controller"
+	depends on DRM
+	depends on ARM || ARM64 || COMPILE_TEST
+	select DRM_KMS_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_GEM_CMA_HELPER
+	select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+	help
+	  Choose this option for DRM support for the PL111 CLCD controller.
+	  If M is selected the module will be called pl111_drm.
+
diff --git a/drivers/gpu/drm/pl111/Makefile b/drivers/gpu/drm/pl111/Makefile
new file mode 100644
index 000000000000..20a7fd76d513
--- /dev/null
+++ b/drivers/gpu/drm/pl111/Makefile
@@ -0,0 +1,8 @@
+pl111_drm-y +=	pl111_connector.o \
+		pl111_crtc.o \
+		pl111_drv.o \
+		pl111_encoder.o \
+		pl111_gem.o \
+		pl111_plane.o
+
+obj-$(CONFIG_DRM_PL111) += pl111_drm.o
diff --git a/drivers/gpu/drm/pl111/pl111_connector.c b/drivers/gpu/drm/pl111/pl111_connector.c
new file mode 100644
index 000000000000..9811d1eadb63
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_connector.c
@@ -0,0 +1,127 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * pl111_drm_connector.c
+ * Implementation of the connector functions for PL111 DRM
+ */
+#include <linux/amba/clcd-regs.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+
+#include "pl111_drm.h"
+
+static void pl111_connector_destroy(struct drm_connector *connector)
+{
+	struct pl111_drm_connector *pl111_connector =
+		to_pl111_connector(connector);
+
+	if (pl111_connector->panel)
+		drm_panel_detach(pl111_connector->panel);
+
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status pl111_connector_detect(struct drm_connector
+							*connector, bool force)
+{
+	struct pl111_drm_connector *pl111_connector =
+		to_pl111_connector(connector);
+
+	return (pl111_connector->panel ?
+		connector_status_connected :
+		connector_status_disconnected);
+}
+
+static int pl111_connector_helper_get_modes(struct drm_connector *connector)
+{
+	struct pl111_drm_connector *pl111_connector =
+		to_pl111_connector(connector);
+
+	if (!pl111_connector->panel)
+		return 0;
+
+	return drm_panel_get_modes(pl111_connector->panel);
+}
+
+const struct drm_connector_funcs connector_funcs = {
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = pl111_connector_destroy,
+	.detect = pl111_connector_detect,
+	.dpms = drm_atomic_helper_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+const struct drm_connector_helper_funcs connector_helper_funcs = {
+	.get_modes = pl111_connector_helper_get_modes,
+};
+
+/* Walks the OF graph to find the panel node and then asks DRM to look
+ * up the panel.
+ */
+static struct drm_panel *pl111_get_panel(struct device *dev)
+{
+	struct device_node *endpoint, *panel_node;
+	struct device_node *np = dev->of_node;
+	struct drm_panel *panel;
+
+	endpoint = of_graph_get_next_endpoint(np, NULL);
+	if (!endpoint) {
+		dev_err(dev, "no endpoint to fetch panel\n");
+		return NULL;
+	}
+
+	/* don't proceed if we have an endpoint but no panel_node tied to it */
+	panel_node = of_graph_get_remote_port_parent(endpoint);
+	of_node_put(endpoint);
+	if (!panel_node) {
+		dev_err(dev, "no valid panel node\n");
+		return NULL;
+	}
+
+	panel = of_drm_find_panel(panel_node);
+	of_node_put(panel_node);
+
+	return panel;
+}
+
+int pl111_connector_create(struct drm_device *dev)
+{
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+	struct pl111_drm_connector *pl111_connector = &priv->connector;
+	struct drm_connector *connector = &pl111_connector->connector;
+
+	drm_connector_init(dev, connector, &connector_funcs,
+			   DRM_MODE_CONNECTOR_DPI);
+	drm_connector_helper_add(connector, &connector_helper_funcs);
+
+	pl111_connector->panel = pl111_get_panel(dev->dev);
+	if (pl111_connector->panel)
+		drm_panel_attach(pl111_connector->panel, connector);
+
+	return 0;
+}
+
diff --git a/drivers/gpu/drm/pl111/pl111_crtc.c b/drivers/gpu/drm/pl111/pl111_crtc.c
new file mode 100644
index 000000000000..4c73bdd3b7e4
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_crtc.c
@@ -0,0 +1,239 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * pl111_drm_crtc.c
+ * Implementation of the CRTC functions for PL111 DRM
+ */
+#include <linux/amba/clcd-regs.h>
+#include <linux/clk.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+
+#include "pl111_drm.h"
+
+irqreturn_t pl111_irq(int irq, void *data)
+{
+	struct pl111_drm_dev_private *priv = data;
+	u32 irq_stat;
+	irqreturn_t status = IRQ_NONE;
+
+	irq_stat = readl(priv->regs + CLCD_PL111_MIS);
+
+	if (!irq_stat)
+		return IRQ_NONE;
+
+	if (irq_stat & CLCD_IRQ_NEXTBASE_UPDATE) {
+		drm_crtc_handle_vblank(&priv->crtc);
+
+		status = IRQ_HANDLED;
+	}
+
+	/* Clear the interrupt once done */
+	writel(irq_stat, priv->regs + CLCD_PL111_ICR);
+
+	return status;
+}
+
+static int pl111_crtc_atomic_check(struct drm_crtc *crtc,
+				   struct drm_crtc_state *state)
+{
+	const struct drm_display_mode *mode = &state->mode;
+
+	if (!state->active)
+		return 0;
+
+	if (mode->hdisplay % 16)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void pl111_crtc_helper_mode_set_nofb(struct drm_crtc *crtc)
+{
+	struct drm_device *drm = crtc->dev;
+	struct pl111_drm_dev_private *priv = crtc->dev->dev_private;
+	const struct drm_display_mode *mode = &crtc->state->mode;
+	struct drm_connector *connector = &priv->connector.connector;
+	unsigned int ppl, hsw, hfp, hbp;
+	unsigned int lpp, vsw, vfp, vbp;
+	unsigned int cpl;
+	int ret;
+
+	ret = clk_set_rate(priv->clk, mode->clock * 1000);
+	if (ret) {
+		dev_err(drm->dev,
+			"Failed to set pixel clock rate to %d: %d\n",
+			mode->clock * 1000, ret);
+	}
+
+	ppl = (mode->hdisplay / 16) - 1;
+	hsw = mode->hsync_end - mode->hsync_start - 1;
+	hfp = mode->hsync_start - mode->hdisplay - 1;
+	hbp = mode->htotal - mode->hsync_end - 1;
+
+	lpp = mode->vdisplay - 1;
+	vsw = mode->vsync_end - mode->vsync_start - 1;
+	vfp = mode->vsync_start - mode->vdisplay;
+	vbp = mode->vtotal - mode->vsync_end;
+
+	cpl = mode->hdisplay - 1;
+
+	writel((ppl << 2) |
+	       (hsw << 8) |
+	       (hfp << 16) |
+	       (hbp << 24),
+	       priv->regs + CLCD_TIM0);
+	writel(lpp |
+	       (vsw << 10) |
+	       (vfp << 16) |
+	       (vbp << 24),
+	       priv->regs + CLCD_TIM1);
+	/* XXX: We currently always use CLCDCLK with no divisor.  We
+	 * could probably reduce power consumption by using HCLK
+	 * (apb_pclk) with a divisor when it gets us near our target
+	 * pixel clock.
+	 */
+	writel(((mode->flags & DRM_MODE_FLAG_NHSYNC) ? TIM2_IHS : 0) |
+	       ((mode->flags & DRM_MODE_FLAG_NVSYNC) ? TIM2_IVS : 0) |
+	       ((connector->display_info.bus_flags &
+		 DRM_BUS_FLAG_DE_LOW) ? TIM2_IOE : 0) |
+	       ((connector->display_info.bus_flags &
+		 DRM_BUS_FLAG_PIXDATA_NEGEDGE) ? TIM2_IPC : 0) |
+	       TIM2_BCD |
+	       (cpl << 16) |
+	       TIM2_CLKSEL,
+	       priv->regs + CLCD_TIM2);
+	writel(0, priv->regs + CLCD_TIM3);
+}
+
+static void pl111_crtc_helper_enable(struct drm_crtc *crtc)
+{
+	struct pl111_drm_dev_private *priv = crtc->dev->dev_private;
+	u32 cntl;
+
+	clk_prepare_enable(priv->clk);
+
+	drm_panel_prepare(priv->connector.panel);
+
+	/* Enable and Power Up */
+	cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDPWR | CNTL_LCDVCOMP(1);
+
+	/* Keep the format that the primary plane had set up. */
+	cntl |= readl(priv->regs + CLCD_PL111_CNTL) & (7 << 1);
+
+	writel(cntl, priv->regs + CLCD_PL111_CNTL);
+
+	drm_panel_enable(priv->connector.panel);
+}
+
+void pl111_crtc_helper_disable(struct drm_crtc *crtc)
+{
+	struct pl111_drm_dev_private *priv = crtc->dev->dev_private;
+
+	drm_panel_disable(priv->connector.panel);
+
+	/* Disable and Power Down */
+	writel(readl(priv->regs + CLCD_PL111_CNTL) & (7 << 1),
+	       priv->regs + CLCD_PL111_CNTL);
+
+	drm_panel_unprepare(priv->connector.panel);
+
+	/* Disable clock */
+	clk_disable_unprepare(priv->clk);
+}
+
+static void pl111_crtc_helper_atomic_flush(struct drm_crtc *crtc,
+					   struct drm_crtc_state *old_state)
+{
+	struct drm_pending_vblank_event *event = crtc->state->event;
+
+	if (event) {
+		crtc->state->event = NULL;
+
+		spin_lock_irq(&crtc->dev->event_lock);
+		if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0)
+			drm_crtc_arm_vblank_event(crtc, event);
+		else
+			drm_crtc_send_vblank_event(crtc, event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+	}
+}
+
+static int pl111_enable_vblank(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+
+	clk_prepare_enable(priv->clk);
+
+	writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + CLCD_PL111_IENB);
+
+	return 0;
+}
+
+static void pl111_disable_vblank(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+
+	writel(0, priv->regs + CLCD_PL111_IENB);
+
+	clk_disable_unprepare(priv->clk);
+}
+
+const struct drm_crtc_funcs crtc_funcs = {
+	.set_config = drm_atomic_helper_set_config,
+	.page_flip = drm_atomic_helper_page_flip,
+	.reset = drm_atomic_helper_crtc_reset,
+	.destroy = drm_crtc_cleanup,
+	.enable_vblank = pl111_enable_vblank,
+	.disable_vblank = pl111_disable_vblank,
+	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+const struct drm_crtc_helper_funcs crtc_helper_funcs = {
+	.mode_set_nofb = pl111_crtc_helper_mode_set_nofb,
+	.atomic_check = pl111_crtc_atomic_check,
+	.atomic_flush = pl111_crtc_helper_atomic_flush,
+	.disable = pl111_crtc_helper_disable,
+	.enable = pl111_crtc_helper_enable,
+};
+
+int pl111_crtc_create(struct drm_device *dev)
+{
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+	struct drm_crtc *crtc = &priv->crtc;
+
+	drm_crtc_init_with_planes(dev, crtc,
+				  &priv->primary, NULL,
+				  &crtc_funcs, "primary");
+	drm_crtc_helper_add(crtc, &crtc_helper_funcs);
+
+	/* XXX: The runtime clock disabling still results in
+	 * occasional system hangs, and needs debugging.
+	 */
+	clk_prepare_enable(priv->clk);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h
new file mode 100644
index 000000000000..36e4ce770a6c
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_drm.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+#ifndef _PL111_DRM_H_
+#define _PL111_DRM_H_
+
+#include <drm/drm_gem.h>
+
+#define CLCD_IRQ_NEXTBASE_UPDATE BIT(2)
+
+struct pl111_drm_connector {
+	struct drm_connector connector;
+	struct drm_panel *panel;
+};
+
+struct pl111_drm_dev_private {
+	struct drm_device *drm;
+
+	struct pl111_drm_connector connector;
+	struct drm_crtc crtc;
+	struct drm_encoder encoder;
+	struct drm_plane primary;
+	struct drm_fbdev_cma *fbdev;
+
+	void *regs;
+	struct clk *clk;
+};
+
+#define to_pl111_connector(x) \
+	container_of(x, struct pl111_drm_connector, connector)
+
+/* CRTC Functions */
+int pl111_crtc_create(struct drm_device *dev);
+irqreturn_t pl111_irq(int irq, void *data);
+
+int pl111_primary_plane_init(struct drm_device *dev);
+
+/* Connector Functions */
+int pl111_connector_create(struct drm_device *dev);
+
+/* Encoder Functions */
+int pl111_encoder_init(struct drm_device *dev);
+
+/* GEM Functions */
+int pl111_dumb_create(struct drm_file *file_priv,
+		      struct drm_device *dev,
+		      struct drm_mode_create_dumb *args);
+
+#endif /* _PL111_DRM_H_ */
diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c
new file mode 100644
index 000000000000..571c296dddf7
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_drv.c
@@ -0,0 +1,292 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * DOC: ARM PrimeCell PL111 CLCD Driver
+ *
+ * The PL111 is a simple LCD controller that can support TFT and STN
+ * displays.  This driver exposes a standard KMS interface for them.
+ *
+ * This driver uses the same Device Tree binding as the fbdev CLCD
+ * driver.  While the fbdev driver supports panels that may be
+ * connected to the CLCD internally to the CLCD driver, in DRM the
+ * panels get split out to drivers/gpu/drm/panels/.  This means that,
+ * in converting from using fbdev to using DRM, you also need to write
+ * a panel driver (which may be as simple as an entry in
+ * panel-simple.c).
+ *
+ * The driver currently doesn't expose the cursor.  The DRM API for
+ * cursors requires support for 64x64 ARGB8888 cursor images, while
+ * the hardware can only support 64x64 monochrome with masking
+ * cursors.  While one could imagine trying to hack something together
+ * to look at the ARGB8888 and program reasonable in monochrome, we
+ * just don't expose the cursor at all instead, and leave cursor
+ * support to the X11 software cursor layer.
+ *
+ * TODO:
+ *
+ * - Fix race between setting plane base address and getting IRQ for
+ *   vsync firing the pageflip completion.
+ *
+ * - Expose the correct set of formats we can support based on the
+ *   "arm,pl11x,tft-r0g0b0-pads" DT property.
+ *
+ * - Use the "max-memory-bandwidth" DT property to filter the
+ *   supported formats.
+ *
+ * - Read back hardware state at boot to skip reprogramming the
+ *   hardware when doing a no-op modeset.
+ *
+ * - Use the internal clock divisor to reduce power consumption by
+ *   using HCLK (apb_pclk) when appropriate.
+ */
+
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd-regs.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "pl111_drm.h"
+
+#define DRIVER_DESC      "DRM module for PL111"
+
+struct drm_mode_config_funcs mode_config_funcs = {
+	.fb_create = drm_fb_cma_create,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static int pl111_modeset_init(struct drm_device *dev)
+{
+	struct drm_mode_config *mode_config;
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+	int ret = 0;
+
+	if (!priv)
+		return -EINVAL;
+
+	drm_mode_config_init(dev);
+	mode_config = &dev->mode_config;
+	mode_config->funcs = &mode_config_funcs;
+	mode_config->min_width = 1;
+	mode_config->max_width = 1024;
+	mode_config->min_height = 1;
+	mode_config->max_height = 768;
+
+	ret = pl111_primary_plane_init(dev);
+	if (ret != 0) {
+		dev_err(dev->dev, "Failed to init primary plane\n");
+		goto out_config;
+	}
+
+	ret = pl111_crtc_create(dev);
+	if (ret) {
+		dev_err(dev->dev, "Failed to create crtc\n");
+		goto out_config;
+	}
+
+	ret = pl111_connector_create(dev);
+	if (ret) {
+		dev_err(dev->dev, "Failed to create pl111_drm_connector\n");
+		goto out_config;
+	}
+
+	ret = pl111_encoder_init(dev);
+	if (ret) {
+		dev_err(dev->dev, "Failed to create pl111_drm_encoder\n");
+		goto out_config;
+	}
+
+	ret = drm_mode_connector_attach_encoder(&priv->connector.connector,
+						&priv->encoder);
+	if (ret != 0) {
+		dev_err(dev->dev, "Failed to attach encoder\n");
+		goto out_config;
+	}
+
+	priv->connector.connector.encoder = &priv->encoder;
+
+	ret = drm_vblank_init(dev, 1);
+	if (ret != 0) {
+		dev_err(dev->dev, "Failed to init vblank\n");
+		goto out_config;
+	}
+
+	drm_mode_config_reset(dev);
+
+	priv->fbdev = drm_fbdev_cma_init(dev, 32,
+					 dev->mode_config.num_connector);
+
+	drm_kms_helper_poll_init(dev);
+
+	goto finish;
+
+out_config:
+	drm_mode_config_cleanup(dev);
+finish:
+	return ret;
+}
+
+static const struct file_operations drm_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.release = drm_release,
+	.unlocked_ioctl = drm_ioctl,
+	.mmap = drm_gem_cma_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+};
+
+static void pl111_lastclose(struct drm_device *dev)
+{
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+
+	drm_fbdev_cma_restore_mode(priv->fbdev);
+}
+
+static struct drm_driver pl111_drm_driver = {
+	.driver_features =
+		DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC,
+	.lastclose = pl111_lastclose,
+	.ioctls = NULL,
+	.fops = &drm_fops,
+	.name = "pl111_drm",
+	.desc = DRIVER_DESC,
+	.date = "20170317",
+	.major = 1,
+	.minor = 0,
+	.patchlevel = 0,
+	.dumb_create = pl111_dumb_create,
+	.dumb_destroy = drm_gem_dumb_destroy,
+	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
+	.gem_free_object = drm_gem_cma_free_object,
+	.gem_vm_ops = &drm_gem_cma_vm_ops,
+
+	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.gem_prime_import = drm_gem_prime_import,
+	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+	.gem_prime_export = drm_gem_prime_export,
+	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
+};
+
+#ifdef CONFIG_ARM_AMBA
+static int pl111_amba_probe(struct amba_device *amba_dev,
+			    const struct amba_id *id)
+{
+	struct device *dev = &amba_dev->dev;
+	struct pl111_drm_dev_private *priv;
+	struct drm_device *drm;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	drm = drm_dev_alloc(&pl111_drm_driver, dev);
+	if (IS_ERR(drm))
+		return PTR_ERR(drm);
+	amba_set_drvdata(amba_dev, drm);
+	priv->drm = drm;
+	drm->dev_private = priv;
+
+	priv->clk = devm_clk_get(dev, "clcdclk");
+	if (IS_ERR(priv->clk)) {
+		dev_err(dev, "CLCD: unable to get clk.\n");
+		ret = PTR_ERR(priv->clk);
+		goto dev_unref;
+	}
+
+	priv->regs = devm_ioremap_resource(dev, &amba_dev->res);
+	if (!priv->regs) {
+		dev_err(dev, "%s failed mmio\n", __func__);
+		return -EINVAL;
+	}
+
+	/* turn off interrupts before requesting the irq */
+	writel(0, priv->regs + CLCD_PL111_IENB);
+
+	ret = devm_request_irq(dev, amba_dev->irq[0], pl111_irq, 0,
+			       "pl111", priv);
+	if (ret != 0) {
+		dev_err(dev, "%s failed irq %d\n", __func__, ret);
+		return ret;
+	}
+
+	ret = pl111_modeset_init(drm);
+	if (ret != 0) {
+		dev_err(dev, "Failed to init modeset\n");
+		goto dev_unref;
+	}
+
+	ret = drm_dev_register(drm, 0);
+	if (ret < 0)
+		goto dev_unref;
+
+	return 0;
+
+dev_unref:
+	drm_dev_unref(drm);
+	return ret;
+}
+
+static int pl111_amba_remove(struct amba_device *amba_dev)
+{
+	struct drm_device *drm = amba_get_drvdata(amba_dev);
+	struct pl111_drm_dev_private *priv = drm->dev_private;
+
+	drm_dev_unregister(drm);
+	if (priv->fbdev)
+		drm_fbdev_cma_fini(priv->fbdev);
+	drm_mode_config_cleanup(drm);
+	drm_dev_unref(drm);
+
+	return 0;
+}
+
+static struct amba_id pl111_id_table[] = {
+	{
+		.id = 0x00041110,
+		.mask = 0x000ffffe,
+	},
+	{0, 0},
+};
+
+static struct amba_driver pl111_amba_driver = {
+	.drv = {
+		.name = "clcd-pl11x",
+	},
+	.probe = pl111_amba_probe,
+	.remove = pl111_amba_remove,
+	.id_table = pl111_id_table,
+};
+#endif /* CONFIG_ARM_AMBA */
+
+module_amba_driver(pl111_amba_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("ARM Ltd.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pl111_drm");
diff --git a/drivers/gpu/drm/pl111/pl111_encoder.c b/drivers/gpu/drm/pl111/pl111_encoder.c
new file mode 100644
index 000000000000..78d67ceb46f3
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_encoder.c
@@ -0,0 +1,50 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * pl111_drm_encoder.c
+ * Implementation of the encoder functions for PL111 DRM
+ */
+#include <linux/amba/clcd-regs.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "pl111_drm.h"
+
+static const struct drm_encoder_funcs pl111_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+int pl111_encoder_init(struct drm_device *dev)
+{
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+	struct drm_encoder *encoder = &priv->encoder;
+	int ret;
+
+	ret = drm_encoder_init(dev, encoder, &pl111_encoder_funcs,
+			       DRM_MODE_ENCODER_DPI, NULL);
+	if (ret)
+		return ret;
+
+	encoder->crtc = &priv->crtc;
+	encoder->possible_crtcs = drm_crtc_mask(encoder->crtc);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/pl111/pl111_gem.c b/drivers/gpu/drm/pl111/pl111_gem.c
new file mode 100644
index 000000000000..b7b0e453dea9
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_gem.c
@@ -0,0 +1,35 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * pl111_drm_gem.c
+ * Implementation of the GEM functions for PL111 DRM
+ */
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include "pl111_drm.h"
+
+int pl111_dumb_create(struct drm_file *file_priv,
+		      struct drm_device *dev, struct drm_mode_create_dumb *args)
+{
+	args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+	return drm_gem_cma_dumb_create_internal(file_priv, dev, args);
+}
diff --git a/drivers/gpu/drm/pl111/pl111_plane.c b/drivers/gpu/drm/pl111/pl111_plane.c
new file mode 100644
index 000000000000..bcdc2f43de46
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_plane.c
@@ -0,0 +1,167 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+#include <linux/amba/clcd-regs.h>
+#include <linux/of_graph.h>
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "pl111_drm.h"
+
+static int pl111_primary_plane_atomic_check(struct drm_plane *plane,
+					    struct drm_plane_state *state)
+{
+	return 0;
+}
+
+static void pl111_primary_plane_atomic_update(struct drm_plane *plane,
+					      struct drm_plane_state *old_state)
+{
+	struct drm_device *dev = plane->dev;
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+	struct drm_framebuffer *fb = plane->state->fb;
+	struct drm_gem_cma_object *obj;
+	u32 addr, cntl;
+
+	if (!fb)
+		return;
+
+	obj = drm_fb_cma_get_gem_obj(fb, 0);
+	addr = obj->paddr + fb->offsets[0];
+	addr += fb->format->cpp[0] * plane->state->src_x;
+	addr += fb->pitches[0] * plane->state->src_y;
+
+	writel(addr, priv->regs + CLCD_UBAS);
+	writel(addr + (fb->height - 1 * fb->pitches[0]),
+	       priv->regs + CLCD_LBAS);
+
+	cntl = readl(priv->regs + CLCD_PL111_CNTL);
+	cntl &= ~(7 << 1);
+
+	/* Note that the the hardware's format reader takes 'r' from
+	 * the low bit, while DRM formats list channels from high bit
+	 * to low bit as you read left to right.
+	 */
+	switch (fb->format->format) {
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_XBGR8888:
+		cntl |= CNTL_LCDBPP24;
+		break;
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XRGB8888:
+		cntl |= CNTL_LCDBPP24 | CNTL_BGR;
+		break;
+	case DRM_FORMAT_BGR565:
+		cntl |= CNTL_LCDBPP16_565;
+		break;
+	case DRM_FORMAT_RGB565:
+		cntl |= CNTL_LCDBPP16_565 | CNTL_BGR;
+		break;
+	case DRM_FORMAT_ABGR1555:
+	case DRM_FORMAT_XBGR1555:
+		cntl |= CNTL_LCDBPP16;
+		break;
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_XRGB1555:
+		cntl |= CNTL_LCDBPP16 | CNTL_BGR;
+		break;
+	case DRM_FORMAT_ABGR4444:
+	case DRM_FORMAT_XBGR4444:
+		cntl |= CNTL_LCDBPP16_444;
+		break;
+	case DRM_FORMAT_ARGB4444:
+	case DRM_FORMAT_XRGB4444:
+		cntl |= CNTL_LCDBPP16_444 | CNTL_BGR;
+		break;
+	}
+
+	writel(cntl, priv->regs + CLCD_PL111_CNTL);
+}
+
+static const struct drm_plane_helper_funcs pl111_primary_plane_helper_funcs = {
+	.atomic_check = pl111_primary_plane_atomic_check,
+	.atomic_update = pl111_primary_plane_atomic_update,
+};
+
+static const struct drm_plane_funcs pl111_primary_plane_funcs = {
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
+	.reset = drm_atomic_helper_plane_reset,
+	.destroy = drm_plane_cleanup,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+int pl111_primary_plane_init(struct drm_device *drm)
+{
+	struct pl111_drm_dev_private *priv = drm->dev_private;
+	struct drm_plane *plane = &priv->primary;
+	struct device *dev = drm->dev;
+	static const u32 formats[] = {
+		DRM_FORMAT_ABGR8888,
+		DRM_FORMAT_XBGR8888,
+		DRM_FORMAT_ARGB8888,
+		DRM_FORMAT_XRGB8888,
+		DRM_FORMAT_BGR565,
+		DRM_FORMAT_RGB565,
+		DRM_FORMAT_ABGR1555,
+		DRM_FORMAT_XBGR1555,
+		DRM_FORMAT_ARGB1555,
+		DRM_FORMAT_XRGB1555,
+		DRM_FORMAT_ABGR4444,
+		DRM_FORMAT_XBGR4444,
+		DRM_FORMAT_ARGB4444,
+		DRM_FORMAT_XRGB4444,
+	};
+	struct device_node *endpoint;
+	u32 tft_r0b0g0[3];
+	int ret;
+
+	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (!endpoint)
+		return -ENODEV;
+
+	if (of_property_read_u32_array(endpoint,
+				       "arm,pl11x,tft-r0g0b0-pads",
+				       tft_r0b0g0,
+				       ARRAY_SIZE(tft_r0b0g0)) != 0) {
+		dev_err(dev, "arm,pl11x,tft-r0g0b0-pads should be 3 ints\n");
+		of_node_put(endpoint);
+		return -ENOENT;
+	}
+	of_node_put(endpoint);
+
+	if (tft_r0b0g0[0] != 0 ||
+	    tft_r0b0g0[1] != 8 ||
+	    tft_r0b0g0[2] != 16) {
+		dev_err(dev, "arm,pl11x,tft-r0g0b0-pads != [0,8,16] not yet supported\n");
+		return -EINVAL;
+	}
+
+	ret = drm_universal_plane_init(drm, plane, 0,
+				       &pl111_primary_plane_funcs,
+				       formats, ARRAY_SIZE(formats),
+				       DRM_PLANE_TYPE_PRIMARY, NULL);
+	if (ret)
+		return ret;
+
+	drm_plane_helper_add(plane, &pl111_primary_plane_helper_funcs);
+
+	return 0;
+}
-- 
2.11.0

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

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

* Re: [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111
  2017-03-17 22:47   ` Eric Anholt
  (?)
@ 2017-03-17 23:09   ` Russell King - ARM Linux
  2017-03-18  0:45       ` Eric Anholt
  2017-03-23 21:54       ` Linus Walleij
  -1 siblings, 2 replies; 18+ messages in thread
From: Russell King - ARM Linux @ 2017-03-17 23:09 UTC (permalink / raw)
  To: Eric Anholt; +Cc: dri-devel, tom.cooksey, linux-kernel

On Fri, Mar 17, 2017 at 03:47:42PM -0700, Eric Anholt wrote:
> This is a modesetting driver for the pl111 CLCD display controller
> found on various ARM platforms such as the Versatile Express. The
> driver has only been tested on the bcm911360_entphn platform so far,
> with PRIME-based buffer sharing between vc4 and clcd.
> 
> It reuses the existing devicetree binding, while not using quite as
> many of its properties as the fbdev driver does (those are left for
> future work).

As we're multiplatform on ARM, and this is using the PL11x AMBA IDs,
we must ensure that it's compatible with everything that the fbdev
driver is compatible with... however, I suspect that's not worth the
effort (unless Linus W wants it?)

If we make it PL111 specific, then we don't need to handle Integrator
CP, or the Versatile PB/AB weirdness.  The only thing left is the
power etc enable bits on Realview which uses the PL111.  See the
code for Realview in drivers/video/fbdev/amba-clcd-versatile.c.

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

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

* Re: [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111
  2017-03-17 23:09   ` Russell King - ARM Linux
@ 2017-03-18  0:45       ` Eric Anholt
  2017-03-23 21:54       ` Linus Walleij
  1 sibling, 0 replies; 18+ messages in thread
From: Eric Anholt @ 2017-03-18  0:45 UTC (permalink / raw)
  To: Russell King - ARM Linux; +Cc: dri-devel, tom.cooksey, linux-kernel

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

Russell King - ARM Linux <linux@armlinux.org.uk> writes:

> On Fri, Mar 17, 2017 at 03:47:42PM -0700, Eric Anholt wrote:
>> This is a modesetting driver for the pl111 CLCD display controller
>> found on various ARM platforms such as the Versatile Express. The
>> driver has only been tested on the bcm911360_entphn platform so far,
>> with PRIME-based buffer sharing between vc4 and clcd.
>> 
>> It reuses the existing devicetree binding, while not using quite as
>> many of its properties as the fbdev driver does (those are left for
>> future work).
>
> As we're multiplatform on ARM, and this is using the PL11x AMBA IDs,
> we must ensure that it's compatible with everything that the fbdev
> driver is compatible with... however, I suspect that's not worth the
> effort (unless Linus W wants it?)
>
> If we make it PL111 specific, then we don't need to handle Integrator
> CP, or the Versatile PB/AB weirdness.  The only thing left is the
> power etc enable bits on Realview which uses the PL111.  See the
> code for Realview in drivers/video/fbdev/amba-clcd-versatile.c.

Restricting to PL111 for now sounds good to me.

Those Realview bits look like they're turning on a power domain --
shouldn't we represent those as a regulator or a power domain?  If we
did, then plugging that into a panel driver sounds straightforward.
(that's assuming that they're powering panel. not the controller -- I
can't quite tell from the code I've browsed so far)


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111
@ 2017-03-18  0:45       ` Eric Anholt
  0 siblings, 0 replies; 18+ messages in thread
From: Eric Anholt @ 2017-03-18  0:45 UTC (permalink / raw)
  To: Russell King - ARM Linux; +Cc: linux-kernel, dri-devel


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

Russell King - ARM Linux <linux@armlinux.org.uk> writes:

> On Fri, Mar 17, 2017 at 03:47:42PM -0700, Eric Anholt wrote:
>> This is a modesetting driver for the pl111 CLCD display controller
>> found on various ARM platforms such as the Versatile Express. The
>> driver has only been tested on the bcm911360_entphn platform so far,
>> with PRIME-based buffer sharing between vc4 and clcd.
>> 
>> It reuses the existing devicetree binding, while not using quite as
>> many of its properties as the fbdev driver does (those are left for
>> future work).
>
> As we're multiplatform on ARM, and this is using the PL11x AMBA IDs,
> we must ensure that it's compatible with everything that the fbdev
> driver is compatible with... however, I suspect that's not worth the
> effort (unless Linus W wants it?)
>
> If we make it PL111 specific, then we don't need to handle Integrator
> CP, or the Versatile PB/AB weirdness.  The only thing left is the
> power etc enable bits on Realview which uses the PL111.  See the
> code for Realview in drivers/video/fbdev/amba-clcd-versatile.c.

Restricting to PL111 for now sounds good to me.

Those Realview bits look like they're turning on a power domain --
shouldn't we represent those as a regulator or a power domain?  If we
did, then plugging that into a panel driver sounds straightforward.
(that's assuming that they're powering panel. not the controller -- I
can't quite tell from the code I've browsed so far)


[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

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

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

* Re: [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111
  2017-03-17 22:47   ` Eric Anholt
@ 2017-03-20  8:34     ` Daniel Vetter
  -1 siblings, 0 replies; 18+ messages in thread
From: Daniel Vetter @ 2017-03-20  8:34 UTC (permalink / raw)
  To: Eric Anholt; +Cc: dri-devel, tom.cooksey, Russell King, linux-kernel

On Fri, Mar 17, 2017 at 03:47:42PM -0700, Eric Anholt wrote:
> From: Tom Cooksey <tom.cooksey@arm.com>
> 
> This is a modesetting driver for the pl111 CLCD display controller
> found on various ARM platforms such as the Versatile Express. The
> driver has only been tested on the bcm911360_entphn platform so far,
> with PRIME-based buffer sharing between vc4 and clcd.
> 
> It reuses the existing devicetree binding, while not using quite as
> many of its properties as the fbdev driver does (those are left for
> future work).
> 
> v2: Nearly complete rewrite by anholt, cutting 2/3 of the code thanks
>     to DRM core's excellent new helpers.
> 
> Signed-off-by: Tom Cooksey <tom.cooksey@arm.com>
> Signed-off-by: Eric Anholt <eric@anholt.net>

Looks pretty. A few things below, but nothing big. I'd say if the "how
generic do we want this to be for now" question is resolved it's ready to
go in.

If you want this in drm-misc (imo fine, you're already there so doesn't
really extend the scope of the experiment), then please also add a
MAINTAINERS entry with the drm-misc git repo and yourself as reviewer.

Cheers, Daniel

> ---
>  Documentation/gpu/index.rst             |   1 +
>  Documentation/gpu/pl111.rst             |   6 +
>  drivers/gpu/drm/Kconfig                 |   2 +
>  drivers/gpu/drm/Makefile                |   1 +
>  drivers/gpu/drm/pl111/Kconfig           |  12 ++
>  drivers/gpu/drm/pl111/Makefile          |   8 +
>  drivers/gpu/drm/pl111/pl111_connector.c | 127 ++++++++++++++
>  drivers/gpu/drm/pl111/pl111_crtc.c      | 239 ++++++++++++++++++++++++++
>  drivers/gpu/drm/pl111/pl111_drm.h       |  64 +++++++
>  drivers/gpu/drm/pl111/pl111_drv.c       | 292 ++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/pl111/pl111_encoder.c   |  50 ++++++
>  drivers/gpu/drm/pl111/pl111_gem.c       |  35 ++++
>  drivers/gpu/drm/pl111/pl111_plane.c     | 167 ++++++++++++++++++
>  13 files changed, 1004 insertions(+)
>  create mode 100644 Documentation/gpu/pl111.rst
>  create mode 100644 drivers/gpu/drm/pl111/Kconfig
>  create mode 100644 drivers/gpu/drm/pl111/Makefile
>  create mode 100644 drivers/gpu/drm/pl111/pl111_connector.c
>  create mode 100644 drivers/gpu/drm/pl111/pl111_crtc.c
>  create mode 100644 drivers/gpu/drm/pl111/pl111_drm.h
>  create mode 100644 drivers/gpu/drm/pl111/pl111_drv.c
>  create mode 100644 drivers/gpu/drm/pl111/pl111_encoder.c
>  create mode 100644 drivers/gpu/drm/pl111/pl111_gem.c
>  create mode 100644 drivers/gpu/drm/pl111/pl111_plane.c
> 
> diff --git a/Documentation/gpu/index.rst b/Documentation/gpu/index.rst
> index e998ee0d0dd5..71bf510d47e8 100644
> --- a/Documentation/gpu/index.rst
> +++ b/Documentation/gpu/index.rst
> @@ -11,6 +11,7 @@ Linux GPU Driver Developer's Guide
>     drm-kms-helpers
>     drm-uapi
>     i915
> +   pl111
>     tinydrm
>     vc4
>     vga-switcheroo
> diff --git a/Documentation/gpu/pl111.rst b/Documentation/gpu/pl111.rst
> new file mode 100644
> index 000000000000..9b03736d33dd
> --- /dev/null
> +++ b/Documentation/gpu/pl111.rst
> @@ -0,0 +1,6 @@
> +==========================================
> + drm/pl111 ARM PrimeCell PL111 CLCD Driver
> +==========================================
> +
> +.. kernel-doc:: drivers/gpu/drm/pl111/pl111_drv.c
> +   :doc: ARM PrimeCell PL111 CLCD Driver
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 78d7fc0ebb57..d1c6c12199b7 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -274,6 +274,8 @@ source "drivers/gpu/drm/meson/Kconfig"
>  
>  source "drivers/gpu/drm/tinydrm/Kconfig"
>  
> +source "drivers/gpu/drm/pl111/Kconfig"
> +
>  # Keep legacy drivers last
>  
>  menuconfig DRM_LEGACY
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 59aae43005ee..99810a529bb0 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -95,3 +95,4 @@ obj-y			+= hisilicon/
>  obj-$(CONFIG_DRM_ZTE)	+= zte/
>  obj-$(CONFIG_DRM_MXSFB)	+= mxsfb/
>  obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
> +obj-$(CONFIG_DRM_PL111) += pl111/
> diff --git a/drivers/gpu/drm/pl111/Kconfig b/drivers/gpu/drm/pl111/Kconfig
> new file mode 100644
> index 000000000000..ede49efd531f
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/Kconfig
> @@ -0,0 +1,12 @@
> +config DRM_PL111
> +	tristate "DRM Support for PL111 CLCD Controller"
> +	depends on DRM
> +	depends on ARM || ARM64 || COMPILE_TEST
> +	select DRM_KMS_HELPER
> +	select DRM_KMS_CMA_HELPER
> +	select DRM_GEM_CMA_HELPER
> +	select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
> +	help
> +	  Choose this option for DRM support for the PL111 CLCD controller.
> +	  If M is selected the module will be called pl111_drm.
> +
> diff --git a/drivers/gpu/drm/pl111/Makefile b/drivers/gpu/drm/pl111/Makefile
> new file mode 100644
> index 000000000000..20a7fd76d513
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/Makefile
> @@ -0,0 +1,8 @@
> +pl111_drm-y +=	pl111_connector.o \
> +		pl111_crtc.o \
> +		pl111_drv.o \
> +		pl111_encoder.o \
> +		pl111_gem.o \
> +		pl111_plane.o
> +
> +obj-$(CONFIG_DRM_PL111) += pl111_drm.o
> diff --git a/drivers/gpu/drm/pl111/pl111_connector.c b/drivers/gpu/drm/pl111/pl111_connector.c
> new file mode 100644
> index 000000000000..9811d1eadb63
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_connector.c
> @@ -0,0 +1,127 @@
> +/*
> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
> + *
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (c) 2006-2008 Intel Corporation
> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + *
> + * This program is free software and is provided to you under the terms of the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + *
> + */
> +
> +/**
> + * pl111_drm_connector.c
> + * Implementation of the connector functions for PL111 DRM
> + */
> +#include <linux/amba/clcd-regs.h>
> +#include <linux/version.h>
> +#include <linux/shmem_fs.h>
> +#include <linux/dma-buf.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +
> +#include "pl111_drm.h"
> +
> +static void pl111_connector_destroy(struct drm_connector *connector)
> +{
> +	struct pl111_drm_connector *pl111_connector =
> +		to_pl111_connector(connector);
> +
> +	if (pl111_connector->panel)
> +		drm_panel_detach(pl111_connector->panel);
> +
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +}
> +
> +static enum drm_connector_status pl111_connector_detect(struct drm_connector
> +							*connector, bool force)
> +{
> +	struct pl111_drm_connector *pl111_connector =
> +		to_pl111_connector(connector);
> +
> +	return (pl111_connector->panel ?
> +		connector_status_connected :
> +		connector_status_disconnected);
> +}
> +
> +static int pl111_connector_helper_get_modes(struct drm_connector *connector)
> +{
> +	struct pl111_drm_connector *pl111_connector =
> +		to_pl111_connector(connector);
> +
> +	if (!pl111_connector->panel)
> +		return 0;
> +
> +	return drm_panel_get_modes(pl111_connector->panel);
> +}

Probably the umptenth time I've seen this :(

One option I think would work well is if we have a generic "wrap a
drm_panel into a drm_bridge" driver and just glue that in with an of
helper as the last element in the enc/transcoder. Laurent has that
practically written already, but he insist in calling it the lvds bridge,
because it's for a 100% dummy lvds transcoder.

But since it's 100% dummy it's indistinguishable from pure sw
abstraction/impendence mismatch helper.

Anyway, just an idea, not going to ask you to do this, but if drm_panel
takes of like crazy we'll probably want this.

> +
> +const struct drm_connector_funcs connector_funcs = {
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.destroy = pl111_connector_destroy,
> +	.detect = pl111_connector_detect,
> +	.dpms = drm_atomic_helper_connector_dpms,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +const struct drm_connector_helper_funcs connector_helper_funcs = {
> +	.get_modes = pl111_connector_helper_get_modes,
> +};
> +
> +/* Walks the OF graph to find the panel node and then asks DRM to look
> + * up the panel.
> + */
> +static struct drm_panel *pl111_get_panel(struct device *dev)
> +{
> +	struct device_node *endpoint, *panel_node;
> +	struct device_node *np = dev->of_node;
> +	struct drm_panel *panel;
> +
> +	endpoint = of_graph_get_next_endpoint(np, NULL);
> +	if (!endpoint) {
> +		dev_err(dev, "no endpoint to fetch panel\n");
> +		return NULL;
> +	}
> +
> +	/* don't proceed if we have an endpoint but no panel_node tied to it */
> +	panel_node = of_graph_get_remote_port_parent(endpoint);
> +	of_node_put(endpoint);
> +	if (!panel_node) {
> +		dev_err(dev, "no valid panel node\n");
> +		return NULL;
> +	}
> +
> +	panel = of_drm_find_panel(panel_node);
> +	of_node_put(panel_node);
> +
> +	return panel;
> +}
> +
> +int pl111_connector_create(struct drm_device *dev)
> +{
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +	struct pl111_drm_connector *pl111_connector = &priv->connector;
> +	struct drm_connector *connector = &pl111_connector->connector;
> +
> +	drm_connector_init(dev, connector, &connector_funcs,
> +			   DRM_MODE_CONNECTOR_DPI);
> +	drm_connector_helper_add(connector, &connector_helper_funcs);
> +
> +	pl111_connector->panel = pl111_get_panel(dev->dev);
> +	if (pl111_connector->panel)
> +		drm_panel_attach(pl111_connector->panel, connector);
> +
> +	return 0;
> +}
> +
> diff --git a/drivers/gpu/drm/pl111/pl111_crtc.c b/drivers/gpu/drm/pl111/pl111_crtc.c
> new file mode 100644
> index 000000000000..4c73bdd3b7e4
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_crtc.c
> @@ -0,0 +1,239 @@
> +/*
> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
> + *
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (c) 2006-2008 Intel Corporation
> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + *
> + * This program is free software and is provided to you under the terms of the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + *
> + */
> +
> +/**
> + * pl111_drm_crtc.c
> + * Implementation of the CRTC functions for PL111 DRM
> + */
> +#include <linux/amba/clcd-regs.h>
> +#include <linux/clk.h>
> +#include <linux/version.h>
> +#include <linux/shmem_fs.h>
> +#include <linux/dma-buf.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_panel.h>
> +
> +#include "pl111_drm.h"
> +
> +irqreturn_t pl111_irq(int irq, void *data)
> +{
> +	struct pl111_drm_dev_private *priv = data;
> +	u32 irq_stat;
> +	irqreturn_t status = IRQ_NONE;
> +
> +	irq_stat = readl(priv->regs + CLCD_PL111_MIS);
> +
> +	if (!irq_stat)
> +		return IRQ_NONE;
> +
> +	if (irq_stat & CLCD_IRQ_NEXTBASE_UPDATE) {
> +		drm_crtc_handle_vblank(&priv->crtc);
> +
> +		status = IRQ_HANDLED;
> +	}
> +
> +	/* Clear the interrupt once done */
> +	writel(irq_stat, priv->regs + CLCD_PL111_ICR);
> +
> +	return status;
> +}
> +
> +static int pl111_crtc_atomic_check(struct drm_crtc *crtc,
> +				   struct drm_crtc_state *state)
> +{
> +	const struct drm_display_mode *mode = &state->mode;
> +
> +	if (!state->active)
> +		return 0;
> +
> +	if (mode->hdisplay % 16)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static void pl111_crtc_helper_mode_set_nofb(struct drm_crtc *crtc)
> +{
> +	struct drm_device *drm = crtc->dev;
> +	struct pl111_drm_dev_private *priv = crtc->dev->dev_private;
> +	const struct drm_display_mode *mode = &crtc->state->mode;
> +	struct drm_connector *connector = &priv->connector.connector;
> +	unsigned int ppl, hsw, hfp, hbp;
> +	unsigned int lpp, vsw, vfp, vbp;
> +	unsigned int cpl;
> +	int ret;
> +
> +	ret = clk_set_rate(priv->clk, mode->clock * 1000);
> +	if (ret) {
> +		dev_err(drm->dev,
> +			"Failed to set pixel clock rate to %d: %d\n",
> +			mode->clock * 1000, ret);
> +	}
> +
> +	ppl = (mode->hdisplay / 16) - 1;
> +	hsw = mode->hsync_end - mode->hsync_start - 1;
> +	hfp = mode->hsync_start - mode->hdisplay - 1;
> +	hbp = mode->htotal - mode->hsync_end - 1;
> +
> +	lpp = mode->vdisplay - 1;
> +	vsw = mode->vsync_end - mode->vsync_start - 1;
> +	vfp = mode->vsync_start - mode->vdisplay;
> +	vbp = mode->vtotal - mode->vsync_end;
> +
> +	cpl = mode->hdisplay - 1;
> +
> +	writel((ppl << 2) |
> +	       (hsw << 8) |
> +	       (hfp << 16) |
> +	       (hbp << 24),
> +	       priv->regs + CLCD_TIM0);
> +	writel(lpp |
> +	       (vsw << 10) |
> +	       (vfp << 16) |
> +	       (vbp << 24),
> +	       priv->regs + CLCD_TIM1);
> +	/* XXX: We currently always use CLCDCLK with no divisor.  We
> +	 * could probably reduce power consumption by using HCLK
> +	 * (apb_pclk) with a divisor when it gets us near our target
> +	 * pixel clock.
> +	 */
> +	writel(((mode->flags & DRM_MODE_FLAG_NHSYNC) ? TIM2_IHS : 0) |
> +	       ((mode->flags & DRM_MODE_FLAG_NVSYNC) ? TIM2_IVS : 0) |
> +	       ((connector->display_info.bus_flags &
> +		 DRM_BUS_FLAG_DE_LOW) ? TIM2_IOE : 0) |
> +	       ((connector->display_info.bus_flags &
> +		 DRM_BUS_FLAG_PIXDATA_NEGEDGE) ? TIM2_IPC : 0) |
> +	       TIM2_BCD |
> +	       (cpl << 16) |
> +	       TIM2_CLKSEL,
> +	       priv->regs + CLCD_TIM2);
> +	writel(0, priv->regs + CLCD_TIM3);
> +}
> +
> +static void pl111_crtc_helper_enable(struct drm_crtc *crtc)
> +{
> +	struct pl111_drm_dev_private *priv = crtc->dev->dev_private;
> +	u32 cntl;
> +
> +	clk_prepare_enable(priv->clk);
> +
> +	drm_panel_prepare(priv->connector.panel);
> +
> +	/* Enable and Power Up */
> +	cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDPWR | CNTL_LCDVCOMP(1);
> +
> +	/* Keep the format that the primary plane had set up. */
> +	cntl |= readl(priv->regs + CLCD_PL111_CNTL) & (7 << 1);
> +
> +	writel(cntl, priv->regs + CLCD_PL111_CNTL);
> +
> +	drm_panel_enable(priv->connector.panel);
> +}
> +
> +void pl111_crtc_helper_disable(struct drm_crtc *crtc)
> +{
> +	struct pl111_drm_dev_private *priv = crtc->dev->dev_private;
> +
> +	drm_panel_disable(priv->connector.panel);
> +
> +	/* Disable and Power Down */
> +	writel(readl(priv->regs + CLCD_PL111_CNTL) & (7 << 1),
> +	       priv->regs + CLCD_PL111_CNTL);
> +
> +	drm_panel_unprepare(priv->connector.panel);
> +
> +	/* Disable clock */
> +	clk_disable_unprepare(priv->clk);
> +}
> +
> +static void pl111_crtc_helper_atomic_flush(struct drm_crtc *crtc,
> +					   struct drm_crtc_state *old_state)
> +{
> +	struct drm_pending_vblank_event *event = crtc->state->event;
> +
> +	if (event) {
> +		crtc->state->event = NULL;
> +
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0)
> +			drm_crtc_arm_vblank_event(crtc, event);
> +		else
> +			drm_crtc_send_vblank_event(crtc, event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +	}
> +}
> +
> +static int pl111_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +
> +	clk_prepare_enable(priv->clk);
> +
> +	writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + CLCD_PL111_IENB);
> +
> +	return 0;
> +}
> +
> +static void pl111_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +
> +	writel(0, priv->regs + CLCD_PL111_IENB);
> +
> +	clk_disable_unprepare(priv->clk);
> +}
> +
> +const struct drm_crtc_funcs crtc_funcs = {
> +	.set_config = drm_atomic_helper_set_config,
> +	.page_flip = drm_atomic_helper_page_flip,
> +	.reset = drm_atomic_helper_crtc_reset,
> +	.destroy = drm_crtc_cleanup,
> +	.enable_vblank = pl111_enable_vblank,
> +	.disable_vblank = pl111_disable_vblank,
> +	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
> +};
> +
> +const struct drm_crtc_helper_funcs crtc_helper_funcs = {
> +	.mode_set_nofb = pl111_crtc_helper_mode_set_nofb,
> +	.atomic_check = pl111_crtc_atomic_check,
> +	.atomic_flush = pl111_crtc_helper_atomic_flush,
> +	.disable = pl111_crtc_helper_disable,
> +	.enable = pl111_crtc_helper_enable,
> +};
> +
> +int pl111_crtc_create(struct drm_device *dev)
> +{
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +	struct drm_crtc *crtc = &priv->crtc;
> +
> +	drm_crtc_init_with_planes(dev, crtc,
> +				  &priv->primary, NULL,
> +				  &crtc_funcs, "primary");
> +	drm_crtc_helper_add(crtc, &crtc_helper_funcs);
> +
> +	/* XXX: The runtime clock disabling still results in
> +	 * occasional system hangs, and needs debugging.
> +	 */
> +	clk_prepare_enable(priv->clk);
> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h
> new file mode 100644
> index 000000000000..36e4ce770a6c
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_drm.h
> @@ -0,0 +1,64 @@
> +/*
> + *
> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
> + *
> + *
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (c) 2006-2008 Intel Corporation
> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + *
> + * This program is free software and is provided to you under the terms of the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + *
> + */
> +
> +#ifndef _PL111_DRM_H_
> +#define _PL111_DRM_H_
> +
> +#include <drm/drm_gem.h>
> +
> +#define CLCD_IRQ_NEXTBASE_UPDATE BIT(2)
> +
> +struct pl111_drm_connector {
> +	struct drm_connector connector;
> +	struct drm_panel *panel;
> +};
> +
> +struct pl111_drm_dev_private {
> +	struct drm_device *drm;
> +
> +	struct pl111_drm_connector connector;
> +	struct drm_crtc crtc;
> +	struct drm_encoder encoder;
> +	struct drm_plane primary;
> +	struct drm_fbdev_cma *fbdev;
> +
> +	void *regs;
> +	struct clk *clk;
> +};
> +
> +#define to_pl111_connector(x) \
> +	container_of(x, struct pl111_drm_connector, connector)
> +
> +/* CRTC Functions */
> +int pl111_crtc_create(struct drm_device *dev);
> +irqreturn_t pl111_irq(int irq, void *data);
> +
> +int pl111_primary_plane_init(struct drm_device *dev);
> +
> +/* Connector Functions */
> +int pl111_connector_create(struct drm_device *dev);
> +
> +/* Encoder Functions */
> +int pl111_encoder_init(struct drm_device *dev);
> +
> +/* GEM Functions */
> +int pl111_dumb_create(struct drm_file *file_priv,
> +		      struct drm_device *dev,
> +		      struct drm_mode_create_dumb *args);
> +
> +#endif /* _PL111_DRM_H_ */
> diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c
> new file mode 100644
> index 000000000000..571c296dddf7
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_drv.c
> @@ -0,0 +1,292 @@
> +/*
> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
> + *
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (c) 2006-2008 Intel Corporation
> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + *
> + * This program is free software and is provided to you under the terms of the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + *
> + */
> +
> +/**
> + * DOC: ARM PrimeCell PL111 CLCD Driver
> + *
> + * The PL111 is a simple LCD controller that can support TFT and STN
> + * displays.  This driver exposes a standard KMS interface for them.
> + *
> + * This driver uses the same Device Tree binding as the fbdev CLCD
> + * driver.  While the fbdev driver supports panels that may be
> + * connected to the CLCD internally to the CLCD driver, in DRM the
> + * panels get split out to drivers/gpu/drm/panels/.  This means that,
> + * in converting from using fbdev to using DRM, you also need to write
> + * a panel driver (which may be as simple as an entry in
> + * panel-simple.c).
> + *
> + * The driver currently doesn't expose the cursor.  The DRM API for
> + * cursors requires support for 64x64 ARGB8888 cursor images, while
> + * the hardware can only support 64x64 monochrome with masking
> + * cursors.  While one could imagine trying to hack something together
> + * to look at the ARGB8888 and program reasonable in monochrome, we
> + * just don't expose the cursor at all instead, and leave cursor
> + * support to the X11 software cursor layer.
> + *
> + * TODO:
> + *
> + * - Fix race between setting plane base address and getting IRQ for
> + *   vsync firing the pageflip completion.
> + *
> + * - Expose the correct set of formats we can support based on the
> + *   "arm,pl11x,tft-r0g0b0-pads" DT property.
> + *
> + * - Use the "max-memory-bandwidth" DT property to filter the
> + *   supported formats.
> + *
> + * - Read back hardware state at boot to skip reprogramming the
> + *   hardware when doing a no-op modeset.
> + *
> + * - Use the internal clock divisor to reduce power consumption by
> + *   using HCLK (apb_pclk) when appropriate.
> + */
> +
> +#include <linux/amba/bus.h>
> +#include <linux/amba/clcd-regs.h>
> +#include <linux/version.h>
> +#include <linux/shmem_fs.h>
> +#include <linux/dma-buf.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +
> +#include "pl111_drm.h"
> +
> +#define DRIVER_DESC      "DRM module for PL111"
> +
> +struct drm_mode_config_funcs mode_config_funcs = {
> +	.fb_create = drm_fb_cma_create,
> +	.atomic_check = drm_atomic_helper_check,
> +	.atomic_commit = drm_atomic_helper_commit,
> +};
> +
> +static int pl111_modeset_init(struct drm_device *dev)
> +{
> +	struct drm_mode_config *mode_config;
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +	int ret = 0;
> +
> +	if (!priv)
> +		return -EINVAL;
> +
> +	drm_mode_config_init(dev);
> +	mode_config = &dev->mode_config;
> +	mode_config->funcs = &mode_config_funcs;
> +	mode_config->min_width = 1;
> +	mode_config->max_width = 1024;
> +	mode_config->min_height = 1;
> +	mode_config->max_height = 768;
> +
> +	ret = pl111_primary_plane_init(dev);
> +	if (ret != 0) {
> +		dev_err(dev->dev, "Failed to init primary plane\n");
> +		goto out_config;
> +	}

I assume this display IP has a pile of planes? Otherwise the simple pipe
helpers look like a perfect fit.

> +
> +	ret = pl111_crtc_create(dev);
> +	if (ret) {
> +		dev_err(dev->dev, "Failed to create crtc\n");
> +		goto out_config;
> +	}
> +
> +	ret = pl111_connector_create(dev);
> +	if (ret) {
> +		dev_err(dev->dev, "Failed to create pl111_drm_connector\n");
> +		goto out_config;
> +	}
> +
> +	ret = pl111_encoder_init(dev);
> +	if (ret) {
> +		dev_err(dev->dev, "Failed to create pl111_drm_encoder\n");
> +		goto out_config;
> +	}
> +
> +	ret = drm_mode_connector_attach_encoder(&priv->connector.connector,
> +						&priv->encoder);
> +	if (ret != 0) {
> +		dev_err(dev->dev, "Failed to attach encoder\n");
> +		goto out_config;
> +	}
> +
> +	priv->connector.connector.encoder = &priv->encoder;
> +
> +	ret = drm_vblank_init(dev, 1);
> +	if (ret != 0) {
> +		dev_err(dev->dev, "Failed to init vblank\n");
> +		goto out_config;
> +	}
> +
> +	drm_mode_config_reset(dev);
> +
> +	priv->fbdev = drm_fbdev_cma_init(dev, 32,
> +					 dev->mode_config.num_connector);
> +
> +	drm_kms_helper_poll_init(dev);
> +
> +	goto finish;
> +
> +out_config:
> +	drm_mode_config_cleanup(dev);
> +finish:
> +	return ret;
> +}
> +
> +static const struct file_operations drm_fops = {
> +	.owner = THIS_MODULE,
> +	.open = drm_open,
> +	.release = drm_release,
> +	.unlocked_ioctl = drm_ioctl,
> +	.mmap = drm_gem_cma_mmap,
> +	.poll = drm_poll,
> +	.read = drm_read,
> +};

Very recent, but DEFINE_DRM_GEM_CMA_FOPS.

> +
> +static void pl111_lastclose(struct drm_device *dev)
> +{
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +
> +	drm_fbdev_cma_restore_mode(priv->fbdev);
> +}
> +
> +static struct drm_driver pl111_drm_driver = {
> +	.driver_features =
> +		DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC,
> +	.lastclose = pl111_lastclose,
> +	.ioctls = NULL,
> +	.fops = &drm_fops,
> +	.name = "pl111_drm",
> +	.desc = DRIVER_DESC,
> +	.date = "20170317",
> +	.major = 1,
> +	.minor = 0,
> +	.patchlevel = 0,
> +	.dumb_create = pl111_dumb_create,
> +	.dumb_destroy = drm_gem_dumb_destroy,
> +	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
> +	.gem_free_object = drm_gem_cma_free_object,
> +	.gem_vm_ops = &drm_gem_cma_vm_ops,
> +
> +	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> +	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> +	.gem_prime_import = drm_gem_prime_import,
> +	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
> +	.gem_prime_export = drm_gem_prime_export,
> +	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
> +};
> +
> +#ifdef CONFIG_ARM_AMBA
> +static int pl111_amba_probe(struct amba_device *amba_dev,
> +			    const struct amba_id *id)
> +{
> +	struct device *dev = &amba_dev->dev;
> +	struct pl111_drm_dev_private *priv;
> +	struct drm_device *drm;
> +	int ret;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	drm = drm_dev_alloc(&pl111_drm_driver, dev);
> +	if (IS_ERR(drm))
> +		return PTR_ERR(drm);
> +	amba_set_drvdata(amba_dev, drm);
> +	priv->drm = drm;
> +	drm->dev_private = priv;
> +
> +	priv->clk = devm_clk_get(dev, "clcdclk");
> +	if (IS_ERR(priv->clk)) {
> +		dev_err(dev, "CLCD: unable to get clk.\n");
> +		ret = PTR_ERR(priv->clk);
> +		goto dev_unref;
> +	}
> +
> +	priv->regs = devm_ioremap_resource(dev, &amba_dev->res);
> +	if (!priv->regs) {
> +		dev_err(dev, "%s failed mmio\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* turn off interrupts before requesting the irq */
> +	writel(0, priv->regs + CLCD_PL111_IENB);
> +
> +	ret = devm_request_irq(dev, amba_dev->irq[0], pl111_irq, 0,
> +			       "pl111", priv);
> +	if (ret != 0) {
> +		dev_err(dev, "%s failed irq %d\n", __func__, ret);
> +		return ret;
> +	}
> +
> +	ret = pl111_modeset_init(drm);
> +	if (ret != 0) {
> +		dev_err(dev, "Failed to init modeset\n");
> +		goto dev_unref;
> +	}
> +
> +	ret = drm_dev_register(drm, 0);
> +	if (ret < 0)
> +		goto dev_unref;
> +
> +	return 0;
> +
> +dev_unref:
> +	drm_dev_unref(drm);
> +	return ret;
> +}
> +
> +static int pl111_amba_remove(struct amba_device *amba_dev)
> +{
> +	struct drm_device *drm = amba_get_drvdata(amba_dev);
> +	struct pl111_drm_dev_private *priv = drm->dev_private;
> +
> +	drm_dev_unregister(drm);
> +	if (priv->fbdev)
> +		drm_fbdev_cma_fini(priv->fbdev);
> +	drm_mode_config_cleanup(drm);
> +	drm_dev_unref(drm);
> +
> +	return 0;
> +}
> +
> +static struct amba_id pl111_id_table[] = {
> +	{
> +		.id = 0x00041110,
> +		.mask = 0x000ffffe,
> +	},
> +	{0, 0},
> +};
> +
> +static struct amba_driver pl111_amba_driver = {
> +	.drv = {
> +		.name = "clcd-pl11x",
> +	},
> +	.probe = pl111_amba_probe,
> +	.remove = pl111_amba_remove,
> +	.id_table = pl111_id_table,
> +};
> +#endif /* CONFIG_ARM_AMBA */
> +
> +module_amba_driver(pl111_amba_driver);
> +
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_AUTHOR("ARM Ltd.");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:pl111_drm");
> diff --git a/drivers/gpu/drm/pl111/pl111_encoder.c b/drivers/gpu/drm/pl111/pl111_encoder.c
> new file mode 100644
> index 000000000000..78d67ceb46f3
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_encoder.c
> @@ -0,0 +1,50 @@
> +/*
> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
> + *
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (c) 2006-2008 Intel Corporation
> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + *
> + * This program is free software and is provided to you under the terms of the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + *
> + */
> +
> +/**
> + * pl111_drm_encoder.c
> + * Implementation of the encoder functions for PL111 DRM
> + */
> +#include <linux/amba/clcd-regs.h>
> +#include <linux/version.h>
> +#include <linux/shmem_fs.h>
> +#include <linux/dma-buf.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "pl111_drm.h"
> +
> +static const struct drm_encoder_funcs pl111_encoder_funcs = {
> +	.destroy = drm_encoder_cleanup,
> +};
> +
> +int pl111_encoder_init(struct drm_device *dev)
> +{
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +	struct drm_encoder *encoder = &priv->encoder;
> +	int ret;
> +
> +	ret = drm_encoder_init(dev, encoder, &pl111_encoder_funcs,
> +			       DRM_MODE_ENCODER_DPI, NULL);
> +	if (ret)
> +		return ret;
> +
> +	encoder->crtc = &priv->crtc;
> +	encoder->possible_crtcs = drm_crtc_mask(encoder->crtc);
> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/pl111/pl111_gem.c b/drivers/gpu/drm/pl111/pl111_gem.c
> new file mode 100644
> index 000000000000..b7b0e453dea9
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_gem.c
> @@ -0,0 +1,35 @@
> +/*
> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
> + *
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (c) 2006-2008 Intel Corporation
> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + *
> + * This program is free software and is provided to you under the terms of the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + *
> + */
> +
> +/**
> + * pl111_drm_gem.c
> + * Implementation of the GEM functions for PL111 DRM
> + */
> +#include <linux/version.h>
> +#include <linux/shmem_fs.h>
> +#include <linux/dma-buf.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include "pl111_drm.h"
> +
> +int pl111_dumb_create(struct drm_file *file_priv,
> +		      struct drm_device *dev, struct drm_mode_create_dumb *args)
> +{
> +	args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
> +
> +	return drm_gem_cma_dumb_create_internal(file_priv, dev, args);
> +}
> diff --git a/drivers/gpu/drm/pl111/pl111_plane.c b/drivers/gpu/drm/pl111/pl111_plane.c
> new file mode 100644
> index 000000000000..bcdc2f43de46
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_plane.c
> @@ -0,0 +1,167 @@
> +/*
> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
> + *
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (c) 2006-2008 Intel Corporation
> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + *
> + * This program is free software and is provided to you under the terms of the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + *
> + */
> +
> +#include <linux/amba/clcd-regs.h>
> +#include <linux/of_graph.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "pl111_drm.h"
> +
> +static int pl111_primary_plane_atomic_check(struct drm_plane *plane,
> +					    struct drm_plane_state *state)
> +{
> +	return 0;
> +}
> +
> +static void pl111_primary_plane_atomic_update(struct drm_plane *plane,
> +					      struct drm_plane_state *old_state)
> +{
> +	struct drm_device *dev = plane->dev;
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +	struct drm_framebuffer *fb = plane->state->fb;
> +	struct drm_gem_cma_object *obj;
> +	u32 addr, cntl;
> +
> +	if (!fb)
> +		return;
> +
> +	obj = drm_fb_cma_get_gem_obj(fb, 0);
> +	addr = obj->paddr + fb->offsets[0];
> +	addr += fb->format->cpp[0] * plane->state->src_x;
> +	addr += fb->pitches[0] * plane->state->src_y;
> +
> +	writel(addr, priv->regs + CLCD_UBAS);
> +	writel(addr + (fb->height - 1 * fb->pitches[0]),
> +	       priv->regs + CLCD_LBAS);
> +
> +	cntl = readl(priv->regs + CLCD_PL111_CNTL);
> +	cntl &= ~(7 << 1);
> +
> +	/* Note that the the hardware's format reader takes 'r' from
> +	 * the low bit, while DRM formats list channels from high bit
> +	 * to low bit as you read left to right.
> +	 */
> +	switch (fb->format->format) {
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_XBGR8888:
> +		cntl |= CNTL_LCDBPP24;
> +		break;
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XRGB8888:
> +		cntl |= CNTL_LCDBPP24 | CNTL_BGR;
> +		break;
> +	case DRM_FORMAT_BGR565:
> +		cntl |= CNTL_LCDBPP16_565;
> +		break;
> +	case DRM_FORMAT_RGB565:
> +		cntl |= CNTL_LCDBPP16_565 | CNTL_BGR;
> +		break;
> +	case DRM_FORMAT_ABGR1555:
> +	case DRM_FORMAT_XBGR1555:
> +		cntl |= CNTL_LCDBPP16;
> +		break;
> +	case DRM_FORMAT_ARGB1555:
> +	case DRM_FORMAT_XRGB1555:
> +		cntl |= CNTL_LCDBPP16 | CNTL_BGR;
> +		break;
> +	case DRM_FORMAT_ABGR4444:
> +	case DRM_FORMAT_XBGR4444:
> +		cntl |= CNTL_LCDBPP16_444;
> +		break;
> +	case DRM_FORMAT_ARGB4444:
> +	case DRM_FORMAT_XRGB4444:
> +		cntl |= CNTL_LCDBPP16_444 | CNTL_BGR;
> +		break;
> +	}
> +
> +	writel(cntl, priv->regs + CLCD_PL111_CNTL);
> +}
> +
> +static const struct drm_plane_helper_funcs pl111_primary_plane_helper_funcs = {
> +	.atomic_check = pl111_primary_plane_atomic_check,
> +	.atomic_update = pl111_primary_plane_atomic_update,
> +};
> +
> +static const struct drm_plane_funcs pl111_primary_plane_funcs = {
> +	.update_plane = drm_atomic_helper_update_plane,
> +	.disable_plane = drm_atomic_helper_disable_plane,
> +	.reset = drm_atomic_helper_plane_reset,
> +	.destroy = drm_plane_cleanup,
> +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +};
> +
> +int pl111_primary_plane_init(struct drm_device *drm)
> +{
> +	struct pl111_drm_dev_private *priv = drm->dev_private;
> +	struct drm_plane *plane = &priv->primary;
> +	struct device *dev = drm->dev;
> +	static const u32 formats[] = {
> +		DRM_FORMAT_ABGR8888,
> +		DRM_FORMAT_XBGR8888,
> +		DRM_FORMAT_ARGB8888,
> +		DRM_FORMAT_XRGB8888,
> +		DRM_FORMAT_BGR565,
> +		DRM_FORMAT_RGB565,
> +		DRM_FORMAT_ABGR1555,
> +		DRM_FORMAT_XBGR1555,
> +		DRM_FORMAT_ARGB1555,
> +		DRM_FORMAT_XRGB1555,
> +		DRM_FORMAT_ABGR4444,
> +		DRM_FORMAT_XBGR4444,
> +		DRM_FORMAT_ARGB4444,
> +		DRM_FORMAT_XRGB4444,
> +	};
> +	struct device_node *endpoint;
> +	u32 tft_r0b0g0[3];
> +	int ret;
> +
> +	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
> +	if (!endpoint)
> +		return -ENODEV;
> +
> +	if (of_property_read_u32_array(endpoint,
> +				       "arm,pl11x,tft-r0g0b0-pads",
> +				       tft_r0b0g0,
> +				       ARRAY_SIZE(tft_r0b0g0)) != 0) {
> +		dev_err(dev, "arm,pl11x,tft-r0g0b0-pads should be 3 ints\n");
> +		of_node_put(endpoint);
> +		return -ENOENT;
> +	}
> +	of_node_put(endpoint);
> +
> +	if (tft_r0b0g0[0] != 0 ||
> +	    tft_r0b0g0[1] != 8 ||
> +	    tft_r0b0g0[2] != 16) {
> +		dev_err(dev, "arm,pl11x,tft-r0g0b0-pads != [0,8,16] not yet supported\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = drm_universal_plane_init(drm, plane, 0,
> +				       &pl111_primary_plane_funcs,
> +				       formats, ARRAY_SIZE(formats),
> +				       DRM_PLANE_TYPE_PRIMARY, NULL);
> +	if (ret)
> +		return ret;
> +
> +	drm_plane_helper_add(plane, &pl111_primary_plane_helper_funcs);
> +
> +	return 0;
> +}
> -- 
> 2.11.0
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

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

* Re: [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111
@ 2017-03-20  8:34     ` Daniel Vetter
  0 siblings, 0 replies; 18+ messages in thread
From: Daniel Vetter @ 2017-03-20  8:34 UTC (permalink / raw)
  To: Eric Anholt; +Cc: linux-kernel, Russell King, dri-devel

On Fri, Mar 17, 2017 at 03:47:42PM -0700, Eric Anholt wrote:
> From: Tom Cooksey <tom.cooksey@arm.com>
> 
> This is a modesetting driver for the pl111 CLCD display controller
> found on various ARM platforms such as the Versatile Express. The
> driver has only been tested on the bcm911360_entphn platform so far,
> with PRIME-based buffer sharing between vc4 and clcd.
> 
> It reuses the existing devicetree binding, while not using quite as
> many of its properties as the fbdev driver does (those are left for
> future work).
> 
> v2: Nearly complete rewrite by anholt, cutting 2/3 of the code thanks
>     to DRM core's excellent new helpers.
> 
> Signed-off-by: Tom Cooksey <tom.cooksey@arm.com>
> Signed-off-by: Eric Anholt <eric@anholt.net>

Looks pretty. A few things below, but nothing big. I'd say if the "how
generic do we want this to be for now" question is resolved it's ready to
go in.

If you want this in drm-misc (imo fine, you're already there so doesn't
really extend the scope of the experiment), then please also add a
MAINTAINERS entry with the drm-misc git repo and yourself as reviewer.

Cheers, Daniel

> ---
>  Documentation/gpu/index.rst             |   1 +
>  Documentation/gpu/pl111.rst             |   6 +
>  drivers/gpu/drm/Kconfig                 |   2 +
>  drivers/gpu/drm/Makefile                |   1 +
>  drivers/gpu/drm/pl111/Kconfig           |  12 ++
>  drivers/gpu/drm/pl111/Makefile          |   8 +
>  drivers/gpu/drm/pl111/pl111_connector.c | 127 ++++++++++++++
>  drivers/gpu/drm/pl111/pl111_crtc.c      | 239 ++++++++++++++++++++++++++
>  drivers/gpu/drm/pl111/pl111_drm.h       |  64 +++++++
>  drivers/gpu/drm/pl111/pl111_drv.c       | 292 ++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/pl111/pl111_encoder.c   |  50 ++++++
>  drivers/gpu/drm/pl111/pl111_gem.c       |  35 ++++
>  drivers/gpu/drm/pl111/pl111_plane.c     | 167 ++++++++++++++++++
>  13 files changed, 1004 insertions(+)
>  create mode 100644 Documentation/gpu/pl111.rst
>  create mode 100644 drivers/gpu/drm/pl111/Kconfig
>  create mode 100644 drivers/gpu/drm/pl111/Makefile
>  create mode 100644 drivers/gpu/drm/pl111/pl111_connector.c
>  create mode 100644 drivers/gpu/drm/pl111/pl111_crtc.c
>  create mode 100644 drivers/gpu/drm/pl111/pl111_drm.h
>  create mode 100644 drivers/gpu/drm/pl111/pl111_drv.c
>  create mode 100644 drivers/gpu/drm/pl111/pl111_encoder.c
>  create mode 100644 drivers/gpu/drm/pl111/pl111_gem.c
>  create mode 100644 drivers/gpu/drm/pl111/pl111_plane.c
> 
> diff --git a/Documentation/gpu/index.rst b/Documentation/gpu/index.rst
> index e998ee0d0dd5..71bf510d47e8 100644
> --- a/Documentation/gpu/index.rst
> +++ b/Documentation/gpu/index.rst
> @@ -11,6 +11,7 @@ Linux GPU Driver Developer's Guide
>     drm-kms-helpers
>     drm-uapi
>     i915
> +   pl111
>     tinydrm
>     vc4
>     vga-switcheroo
> diff --git a/Documentation/gpu/pl111.rst b/Documentation/gpu/pl111.rst
> new file mode 100644
> index 000000000000..9b03736d33dd
> --- /dev/null
> +++ b/Documentation/gpu/pl111.rst
> @@ -0,0 +1,6 @@
> +==========================================
> + drm/pl111 ARM PrimeCell PL111 CLCD Driver
> +==========================================
> +
> +.. kernel-doc:: drivers/gpu/drm/pl111/pl111_drv.c
> +   :doc: ARM PrimeCell PL111 CLCD Driver
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 78d7fc0ebb57..d1c6c12199b7 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -274,6 +274,8 @@ source "drivers/gpu/drm/meson/Kconfig"
>  
>  source "drivers/gpu/drm/tinydrm/Kconfig"
>  
> +source "drivers/gpu/drm/pl111/Kconfig"
> +
>  # Keep legacy drivers last
>  
>  menuconfig DRM_LEGACY
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 59aae43005ee..99810a529bb0 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -95,3 +95,4 @@ obj-y			+= hisilicon/
>  obj-$(CONFIG_DRM_ZTE)	+= zte/
>  obj-$(CONFIG_DRM_MXSFB)	+= mxsfb/
>  obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
> +obj-$(CONFIG_DRM_PL111) += pl111/
> diff --git a/drivers/gpu/drm/pl111/Kconfig b/drivers/gpu/drm/pl111/Kconfig
> new file mode 100644
> index 000000000000..ede49efd531f
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/Kconfig
> @@ -0,0 +1,12 @@
> +config DRM_PL111
> +	tristate "DRM Support for PL111 CLCD Controller"
> +	depends on DRM
> +	depends on ARM || ARM64 || COMPILE_TEST
> +	select DRM_KMS_HELPER
> +	select DRM_KMS_CMA_HELPER
> +	select DRM_GEM_CMA_HELPER
> +	select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
> +	help
> +	  Choose this option for DRM support for the PL111 CLCD controller.
> +	  If M is selected the module will be called pl111_drm.
> +
> diff --git a/drivers/gpu/drm/pl111/Makefile b/drivers/gpu/drm/pl111/Makefile
> new file mode 100644
> index 000000000000..20a7fd76d513
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/Makefile
> @@ -0,0 +1,8 @@
> +pl111_drm-y +=	pl111_connector.o \
> +		pl111_crtc.o \
> +		pl111_drv.o \
> +		pl111_encoder.o \
> +		pl111_gem.o \
> +		pl111_plane.o
> +
> +obj-$(CONFIG_DRM_PL111) += pl111_drm.o
> diff --git a/drivers/gpu/drm/pl111/pl111_connector.c b/drivers/gpu/drm/pl111/pl111_connector.c
> new file mode 100644
> index 000000000000..9811d1eadb63
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_connector.c
> @@ -0,0 +1,127 @@
> +/*
> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
> + *
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (c) 2006-2008 Intel Corporation
> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + *
> + * This program is free software and is provided to you under the terms of the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + *
> + */
> +
> +/**
> + * pl111_drm_connector.c
> + * Implementation of the connector functions for PL111 DRM
> + */
> +#include <linux/amba/clcd-regs.h>
> +#include <linux/version.h>
> +#include <linux/shmem_fs.h>
> +#include <linux/dma-buf.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +
> +#include "pl111_drm.h"
> +
> +static void pl111_connector_destroy(struct drm_connector *connector)
> +{
> +	struct pl111_drm_connector *pl111_connector =
> +		to_pl111_connector(connector);
> +
> +	if (pl111_connector->panel)
> +		drm_panel_detach(pl111_connector->panel);
> +
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +}
> +
> +static enum drm_connector_status pl111_connector_detect(struct drm_connector
> +							*connector, bool force)
> +{
> +	struct pl111_drm_connector *pl111_connector =
> +		to_pl111_connector(connector);
> +
> +	return (pl111_connector->panel ?
> +		connector_status_connected :
> +		connector_status_disconnected);
> +}
> +
> +static int pl111_connector_helper_get_modes(struct drm_connector *connector)
> +{
> +	struct pl111_drm_connector *pl111_connector =
> +		to_pl111_connector(connector);
> +
> +	if (!pl111_connector->panel)
> +		return 0;
> +
> +	return drm_panel_get_modes(pl111_connector->panel);
> +}

Probably the umptenth time I've seen this :(

One option I think would work well is if we have a generic "wrap a
drm_panel into a drm_bridge" driver and just glue that in with an of
helper as the last element in the enc/transcoder. Laurent has that
practically written already, but he insist in calling it the lvds bridge,
because it's for a 100% dummy lvds transcoder.

But since it's 100% dummy it's indistinguishable from pure sw
abstraction/impendence mismatch helper.

Anyway, just an idea, not going to ask you to do this, but if drm_panel
takes of like crazy we'll probably want this.

> +
> +const struct drm_connector_funcs connector_funcs = {
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.destroy = pl111_connector_destroy,
> +	.detect = pl111_connector_detect,
> +	.dpms = drm_atomic_helper_connector_dpms,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +const struct drm_connector_helper_funcs connector_helper_funcs = {
> +	.get_modes = pl111_connector_helper_get_modes,
> +};
> +
> +/* Walks the OF graph to find the panel node and then asks DRM to look
> + * up the panel.
> + */
> +static struct drm_panel *pl111_get_panel(struct device *dev)
> +{
> +	struct device_node *endpoint, *panel_node;
> +	struct device_node *np = dev->of_node;
> +	struct drm_panel *panel;
> +
> +	endpoint = of_graph_get_next_endpoint(np, NULL);
> +	if (!endpoint) {
> +		dev_err(dev, "no endpoint to fetch panel\n");
> +		return NULL;
> +	}
> +
> +	/* don't proceed if we have an endpoint but no panel_node tied to it */
> +	panel_node = of_graph_get_remote_port_parent(endpoint);
> +	of_node_put(endpoint);
> +	if (!panel_node) {
> +		dev_err(dev, "no valid panel node\n");
> +		return NULL;
> +	}
> +
> +	panel = of_drm_find_panel(panel_node);
> +	of_node_put(panel_node);
> +
> +	return panel;
> +}
> +
> +int pl111_connector_create(struct drm_device *dev)
> +{
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +	struct pl111_drm_connector *pl111_connector = &priv->connector;
> +	struct drm_connector *connector = &pl111_connector->connector;
> +
> +	drm_connector_init(dev, connector, &connector_funcs,
> +			   DRM_MODE_CONNECTOR_DPI);
> +	drm_connector_helper_add(connector, &connector_helper_funcs);
> +
> +	pl111_connector->panel = pl111_get_panel(dev->dev);
> +	if (pl111_connector->panel)
> +		drm_panel_attach(pl111_connector->panel, connector);
> +
> +	return 0;
> +}
> +
> diff --git a/drivers/gpu/drm/pl111/pl111_crtc.c b/drivers/gpu/drm/pl111/pl111_crtc.c
> new file mode 100644
> index 000000000000..4c73bdd3b7e4
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_crtc.c
> @@ -0,0 +1,239 @@
> +/*
> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
> + *
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (c) 2006-2008 Intel Corporation
> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + *
> + * This program is free software and is provided to you under the terms of the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + *
> + */
> +
> +/**
> + * pl111_drm_crtc.c
> + * Implementation of the CRTC functions for PL111 DRM
> + */
> +#include <linux/amba/clcd-regs.h>
> +#include <linux/clk.h>
> +#include <linux/version.h>
> +#include <linux/shmem_fs.h>
> +#include <linux/dma-buf.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_panel.h>
> +
> +#include "pl111_drm.h"
> +
> +irqreturn_t pl111_irq(int irq, void *data)
> +{
> +	struct pl111_drm_dev_private *priv = data;
> +	u32 irq_stat;
> +	irqreturn_t status = IRQ_NONE;
> +
> +	irq_stat = readl(priv->regs + CLCD_PL111_MIS);
> +
> +	if (!irq_stat)
> +		return IRQ_NONE;
> +
> +	if (irq_stat & CLCD_IRQ_NEXTBASE_UPDATE) {
> +		drm_crtc_handle_vblank(&priv->crtc);
> +
> +		status = IRQ_HANDLED;
> +	}
> +
> +	/* Clear the interrupt once done */
> +	writel(irq_stat, priv->regs + CLCD_PL111_ICR);
> +
> +	return status;
> +}
> +
> +static int pl111_crtc_atomic_check(struct drm_crtc *crtc,
> +				   struct drm_crtc_state *state)
> +{
> +	const struct drm_display_mode *mode = &state->mode;
> +
> +	if (!state->active)
> +		return 0;
> +
> +	if (mode->hdisplay % 16)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static void pl111_crtc_helper_mode_set_nofb(struct drm_crtc *crtc)
> +{
> +	struct drm_device *drm = crtc->dev;
> +	struct pl111_drm_dev_private *priv = crtc->dev->dev_private;
> +	const struct drm_display_mode *mode = &crtc->state->mode;
> +	struct drm_connector *connector = &priv->connector.connector;
> +	unsigned int ppl, hsw, hfp, hbp;
> +	unsigned int lpp, vsw, vfp, vbp;
> +	unsigned int cpl;
> +	int ret;
> +
> +	ret = clk_set_rate(priv->clk, mode->clock * 1000);
> +	if (ret) {
> +		dev_err(drm->dev,
> +			"Failed to set pixel clock rate to %d: %d\n",
> +			mode->clock * 1000, ret);
> +	}
> +
> +	ppl = (mode->hdisplay / 16) - 1;
> +	hsw = mode->hsync_end - mode->hsync_start - 1;
> +	hfp = mode->hsync_start - mode->hdisplay - 1;
> +	hbp = mode->htotal - mode->hsync_end - 1;
> +
> +	lpp = mode->vdisplay - 1;
> +	vsw = mode->vsync_end - mode->vsync_start - 1;
> +	vfp = mode->vsync_start - mode->vdisplay;
> +	vbp = mode->vtotal - mode->vsync_end;
> +
> +	cpl = mode->hdisplay - 1;
> +
> +	writel((ppl << 2) |
> +	       (hsw << 8) |
> +	       (hfp << 16) |
> +	       (hbp << 24),
> +	       priv->regs + CLCD_TIM0);
> +	writel(lpp |
> +	       (vsw << 10) |
> +	       (vfp << 16) |
> +	       (vbp << 24),
> +	       priv->regs + CLCD_TIM1);
> +	/* XXX: We currently always use CLCDCLK with no divisor.  We
> +	 * could probably reduce power consumption by using HCLK
> +	 * (apb_pclk) with a divisor when it gets us near our target
> +	 * pixel clock.
> +	 */
> +	writel(((mode->flags & DRM_MODE_FLAG_NHSYNC) ? TIM2_IHS : 0) |
> +	       ((mode->flags & DRM_MODE_FLAG_NVSYNC) ? TIM2_IVS : 0) |
> +	       ((connector->display_info.bus_flags &
> +		 DRM_BUS_FLAG_DE_LOW) ? TIM2_IOE : 0) |
> +	       ((connector->display_info.bus_flags &
> +		 DRM_BUS_FLAG_PIXDATA_NEGEDGE) ? TIM2_IPC : 0) |
> +	       TIM2_BCD |
> +	       (cpl << 16) |
> +	       TIM2_CLKSEL,
> +	       priv->regs + CLCD_TIM2);
> +	writel(0, priv->regs + CLCD_TIM3);
> +}
> +
> +static void pl111_crtc_helper_enable(struct drm_crtc *crtc)
> +{
> +	struct pl111_drm_dev_private *priv = crtc->dev->dev_private;
> +	u32 cntl;
> +
> +	clk_prepare_enable(priv->clk);
> +
> +	drm_panel_prepare(priv->connector.panel);
> +
> +	/* Enable and Power Up */
> +	cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDPWR | CNTL_LCDVCOMP(1);
> +
> +	/* Keep the format that the primary plane had set up. */
> +	cntl |= readl(priv->regs + CLCD_PL111_CNTL) & (7 << 1);
> +
> +	writel(cntl, priv->regs + CLCD_PL111_CNTL);
> +
> +	drm_panel_enable(priv->connector.panel);
> +}
> +
> +void pl111_crtc_helper_disable(struct drm_crtc *crtc)
> +{
> +	struct pl111_drm_dev_private *priv = crtc->dev->dev_private;
> +
> +	drm_panel_disable(priv->connector.panel);
> +
> +	/* Disable and Power Down */
> +	writel(readl(priv->regs + CLCD_PL111_CNTL) & (7 << 1),
> +	       priv->regs + CLCD_PL111_CNTL);
> +
> +	drm_panel_unprepare(priv->connector.panel);
> +
> +	/* Disable clock */
> +	clk_disable_unprepare(priv->clk);
> +}
> +
> +static void pl111_crtc_helper_atomic_flush(struct drm_crtc *crtc,
> +					   struct drm_crtc_state *old_state)
> +{
> +	struct drm_pending_vblank_event *event = crtc->state->event;
> +
> +	if (event) {
> +		crtc->state->event = NULL;
> +
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0)
> +			drm_crtc_arm_vblank_event(crtc, event);
> +		else
> +			drm_crtc_send_vblank_event(crtc, event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +	}
> +}
> +
> +static int pl111_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +
> +	clk_prepare_enable(priv->clk);
> +
> +	writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + CLCD_PL111_IENB);
> +
> +	return 0;
> +}
> +
> +static void pl111_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +
> +	writel(0, priv->regs + CLCD_PL111_IENB);
> +
> +	clk_disable_unprepare(priv->clk);
> +}
> +
> +const struct drm_crtc_funcs crtc_funcs = {
> +	.set_config = drm_atomic_helper_set_config,
> +	.page_flip = drm_atomic_helper_page_flip,
> +	.reset = drm_atomic_helper_crtc_reset,
> +	.destroy = drm_crtc_cleanup,
> +	.enable_vblank = pl111_enable_vblank,
> +	.disable_vblank = pl111_disable_vblank,
> +	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
> +};
> +
> +const struct drm_crtc_helper_funcs crtc_helper_funcs = {
> +	.mode_set_nofb = pl111_crtc_helper_mode_set_nofb,
> +	.atomic_check = pl111_crtc_atomic_check,
> +	.atomic_flush = pl111_crtc_helper_atomic_flush,
> +	.disable = pl111_crtc_helper_disable,
> +	.enable = pl111_crtc_helper_enable,
> +};
> +
> +int pl111_crtc_create(struct drm_device *dev)
> +{
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +	struct drm_crtc *crtc = &priv->crtc;
> +
> +	drm_crtc_init_with_planes(dev, crtc,
> +				  &priv->primary, NULL,
> +				  &crtc_funcs, "primary");
> +	drm_crtc_helper_add(crtc, &crtc_helper_funcs);
> +
> +	/* XXX: The runtime clock disabling still results in
> +	 * occasional system hangs, and needs debugging.
> +	 */
> +	clk_prepare_enable(priv->clk);
> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/pl111/pl111_drm.h b/drivers/gpu/drm/pl111/pl111_drm.h
> new file mode 100644
> index 000000000000..36e4ce770a6c
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_drm.h
> @@ -0,0 +1,64 @@
> +/*
> + *
> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
> + *
> + *
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (c) 2006-2008 Intel Corporation
> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + *
> + * This program is free software and is provided to you under the terms of the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + *
> + */
> +
> +#ifndef _PL111_DRM_H_
> +#define _PL111_DRM_H_
> +
> +#include <drm/drm_gem.h>
> +
> +#define CLCD_IRQ_NEXTBASE_UPDATE BIT(2)
> +
> +struct pl111_drm_connector {
> +	struct drm_connector connector;
> +	struct drm_panel *panel;
> +};
> +
> +struct pl111_drm_dev_private {
> +	struct drm_device *drm;
> +
> +	struct pl111_drm_connector connector;
> +	struct drm_crtc crtc;
> +	struct drm_encoder encoder;
> +	struct drm_plane primary;
> +	struct drm_fbdev_cma *fbdev;
> +
> +	void *regs;
> +	struct clk *clk;
> +};
> +
> +#define to_pl111_connector(x) \
> +	container_of(x, struct pl111_drm_connector, connector)
> +
> +/* CRTC Functions */
> +int pl111_crtc_create(struct drm_device *dev);
> +irqreturn_t pl111_irq(int irq, void *data);
> +
> +int pl111_primary_plane_init(struct drm_device *dev);
> +
> +/* Connector Functions */
> +int pl111_connector_create(struct drm_device *dev);
> +
> +/* Encoder Functions */
> +int pl111_encoder_init(struct drm_device *dev);
> +
> +/* GEM Functions */
> +int pl111_dumb_create(struct drm_file *file_priv,
> +		      struct drm_device *dev,
> +		      struct drm_mode_create_dumb *args);
> +
> +#endif /* _PL111_DRM_H_ */
> diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c
> new file mode 100644
> index 000000000000..571c296dddf7
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_drv.c
> @@ -0,0 +1,292 @@
> +/*
> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
> + *
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (c) 2006-2008 Intel Corporation
> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + *
> + * This program is free software and is provided to you under the terms of the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + *
> + */
> +
> +/**
> + * DOC: ARM PrimeCell PL111 CLCD Driver
> + *
> + * The PL111 is a simple LCD controller that can support TFT and STN
> + * displays.  This driver exposes a standard KMS interface for them.
> + *
> + * This driver uses the same Device Tree binding as the fbdev CLCD
> + * driver.  While the fbdev driver supports panels that may be
> + * connected to the CLCD internally to the CLCD driver, in DRM the
> + * panels get split out to drivers/gpu/drm/panels/.  This means that,
> + * in converting from using fbdev to using DRM, you also need to write
> + * a panel driver (which may be as simple as an entry in
> + * panel-simple.c).
> + *
> + * The driver currently doesn't expose the cursor.  The DRM API for
> + * cursors requires support for 64x64 ARGB8888 cursor images, while
> + * the hardware can only support 64x64 monochrome with masking
> + * cursors.  While one could imagine trying to hack something together
> + * to look at the ARGB8888 and program reasonable in monochrome, we
> + * just don't expose the cursor at all instead, and leave cursor
> + * support to the X11 software cursor layer.
> + *
> + * TODO:
> + *
> + * - Fix race between setting plane base address and getting IRQ for
> + *   vsync firing the pageflip completion.
> + *
> + * - Expose the correct set of formats we can support based on the
> + *   "arm,pl11x,tft-r0g0b0-pads" DT property.
> + *
> + * - Use the "max-memory-bandwidth" DT property to filter the
> + *   supported formats.
> + *
> + * - Read back hardware state at boot to skip reprogramming the
> + *   hardware when doing a no-op modeset.
> + *
> + * - Use the internal clock divisor to reduce power consumption by
> + *   using HCLK (apb_pclk) when appropriate.
> + */
> +
> +#include <linux/amba/bus.h>
> +#include <linux/amba/clcd-regs.h>
> +#include <linux/version.h>
> +#include <linux/shmem_fs.h>
> +#include <linux/dma-buf.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +
> +#include "pl111_drm.h"
> +
> +#define DRIVER_DESC      "DRM module for PL111"
> +
> +struct drm_mode_config_funcs mode_config_funcs = {
> +	.fb_create = drm_fb_cma_create,
> +	.atomic_check = drm_atomic_helper_check,
> +	.atomic_commit = drm_atomic_helper_commit,
> +};
> +
> +static int pl111_modeset_init(struct drm_device *dev)
> +{
> +	struct drm_mode_config *mode_config;
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +	int ret = 0;
> +
> +	if (!priv)
> +		return -EINVAL;
> +
> +	drm_mode_config_init(dev);
> +	mode_config = &dev->mode_config;
> +	mode_config->funcs = &mode_config_funcs;
> +	mode_config->min_width = 1;
> +	mode_config->max_width = 1024;
> +	mode_config->min_height = 1;
> +	mode_config->max_height = 768;
> +
> +	ret = pl111_primary_plane_init(dev);
> +	if (ret != 0) {
> +		dev_err(dev->dev, "Failed to init primary plane\n");
> +		goto out_config;
> +	}

I assume this display IP has a pile of planes? Otherwise the simple pipe
helpers look like a perfect fit.

> +
> +	ret = pl111_crtc_create(dev);
> +	if (ret) {
> +		dev_err(dev->dev, "Failed to create crtc\n");
> +		goto out_config;
> +	}
> +
> +	ret = pl111_connector_create(dev);
> +	if (ret) {
> +		dev_err(dev->dev, "Failed to create pl111_drm_connector\n");
> +		goto out_config;
> +	}
> +
> +	ret = pl111_encoder_init(dev);
> +	if (ret) {
> +		dev_err(dev->dev, "Failed to create pl111_drm_encoder\n");
> +		goto out_config;
> +	}
> +
> +	ret = drm_mode_connector_attach_encoder(&priv->connector.connector,
> +						&priv->encoder);
> +	if (ret != 0) {
> +		dev_err(dev->dev, "Failed to attach encoder\n");
> +		goto out_config;
> +	}
> +
> +	priv->connector.connector.encoder = &priv->encoder;
> +
> +	ret = drm_vblank_init(dev, 1);
> +	if (ret != 0) {
> +		dev_err(dev->dev, "Failed to init vblank\n");
> +		goto out_config;
> +	}
> +
> +	drm_mode_config_reset(dev);
> +
> +	priv->fbdev = drm_fbdev_cma_init(dev, 32,
> +					 dev->mode_config.num_connector);
> +
> +	drm_kms_helper_poll_init(dev);
> +
> +	goto finish;
> +
> +out_config:
> +	drm_mode_config_cleanup(dev);
> +finish:
> +	return ret;
> +}
> +
> +static const struct file_operations drm_fops = {
> +	.owner = THIS_MODULE,
> +	.open = drm_open,
> +	.release = drm_release,
> +	.unlocked_ioctl = drm_ioctl,
> +	.mmap = drm_gem_cma_mmap,
> +	.poll = drm_poll,
> +	.read = drm_read,
> +};

Very recent, but DEFINE_DRM_GEM_CMA_FOPS.

> +
> +static void pl111_lastclose(struct drm_device *dev)
> +{
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +
> +	drm_fbdev_cma_restore_mode(priv->fbdev);
> +}
> +
> +static struct drm_driver pl111_drm_driver = {
> +	.driver_features =
> +		DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC,
> +	.lastclose = pl111_lastclose,
> +	.ioctls = NULL,
> +	.fops = &drm_fops,
> +	.name = "pl111_drm",
> +	.desc = DRIVER_DESC,
> +	.date = "20170317",
> +	.major = 1,
> +	.minor = 0,
> +	.patchlevel = 0,
> +	.dumb_create = pl111_dumb_create,
> +	.dumb_destroy = drm_gem_dumb_destroy,
> +	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
> +	.gem_free_object = drm_gem_cma_free_object,
> +	.gem_vm_ops = &drm_gem_cma_vm_ops,
> +
> +	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> +	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> +	.gem_prime_import = drm_gem_prime_import,
> +	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
> +	.gem_prime_export = drm_gem_prime_export,
> +	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
> +};
> +
> +#ifdef CONFIG_ARM_AMBA
> +static int pl111_amba_probe(struct amba_device *amba_dev,
> +			    const struct amba_id *id)
> +{
> +	struct device *dev = &amba_dev->dev;
> +	struct pl111_drm_dev_private *priv;
> +	struct drm_device *drm;
> +	int ret;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	drm = drm_dev_alloc(&pl111_drm_driver, dev);
> +	if (IS_ERR(drm))
> +		return PTR_ERR(drm);
> +	amba_set_drvdata(amba_dev, drm);
> +	priv->drm = drm;
> +	drm->dev_private = priv;
> +
> +	priv->clk = devm_clk_get(dev, "clcdclk");
> +	if (IS_ERR(priv->clk)) {
> +		dev_err(dev, "CLCD: unable to get clk.\n");
> +		ret = PTR_ERR(priv->clk);
> +		goto dev_unref;
> +	}
> +
> +	priv->regs = devm_ioremap_resource(dev, &amba_dev->res);
> +	if (!priv->regs) {
> +		dev_err(dev, "%s failed mmio\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	/* turn off interrupts before requesting the irq */
> +	writel(0, priv->regs + CLCD_PL111_IENB);
> +
> +	ret = devm_request_irq(dev, amba_dev->irq[0], pl111_irq, 0,
> +			       "pl111", priv);
> +	if (ret != 0) {
> +		dev_err(dev, "%s failed irq %d\n", __func__, ret);
> +		return ret;
> +	}
> +
> +	ret = pl111_modeset_init(drm);
> +	if (ret != 0) {
> +		dev_err(dev, "Failed to init modeset\n");
> +		goto dev_unref;
> +	}
> +
> +	ret = drm_dev_register(drm, 0);
> +	if (ret < 0)
> +		goto dev_unref;
> +
> +	return 0;
> +
> +dev_unref:
> +	drm_dev_unref(drm);
> +	return ret;
> +}
> +
> +static int pl111_amba_remove(struct amba_device *amba_dev)
> +{
> +	struct drm_device *drm = amba_get_drvdata(amba_dev);
> +	struct pl111_drm_dev_private *priv = drm->dev_private;
> +
> +	drm_dev_unregister(drm);
> +	if (priv->fbdev)
> +		drm_fbdev_cma_fini(priv->fbdev);
> +	drm_mode_config_cleanup(drm);
> +	drm_dev_unref(drm);
> +
> +	return 0;
> +}
> +
> +static struct amba_id pl111_id_table[] = {
> +	{
> +		.id = 0x00041110,
> +		.mask = 0x000ffffe,
> +	},
> +	{0, 0},
> +};
> +
> +static struct amba_driver pl111_amba_driver = {
> +	.drv = {
> +		.name = "clcd-pl11x",
> +	},
> +	.probe = pl111_amba_probe,
> +	.remove = pl111_amba_remove,
> +	.id_table = pl111_id_table,
> +};
> +#endif /* CONFIG_ARM_AMBA */
> +
> +module_amba_driver(pl111_amba_driver);
> +
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_AUTHOR("ARM Ltd.");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:pl111_drm");
> diff --git a/drivers/gpu/drm/pl111/pl111_encoder.c b/drivers/gpu/drm/pl111/pl111_encoder.c
> new file mode 100644
> index 000000000000..78d67ceb46f3
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_encoder.c
> @@ -0,0 +1,50 @@
> +/*
> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
> + *
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (c) 2006-2008 Intel Corporation
> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + *
> + * This program is free software and is provided to you under the terms of the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + *
> + */
> +
> +/**
> + * pl111_drm_encoder.c
> + * Implementation of the encoder functions for PL111 DRM
> + */
> +#include <linux/amba/clcd-regs.h>
> +#include <linux/version.h>
> +#include <linux/shmem_fs.h>
> +#include <linux/dma-buf.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "pl111_drm.h"
> +
> +static const struct drm_encoder_funcs pl111_encoder_funcs = {
> +	.destroy = drm_encoder_cleanup,
> +};
> +
> +int pl111_encoder_init(struct drm_device *dev)
> +{
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +	struct drm_encoder *encoder = &priv->encoder;
> +	int ret;
> +
> +	ret = drm_encoder_init(dev, encoder, &pl111_encoder_funcs,
> +			       DRM_MODE_ENCODER_DPI, NULL);
> +	if (ret)
> +		return ret;
> +
> +	encoder->crtc = &priv->crtc;
> +	encoder->possible_crtcs = drm_crtc_mask(encoder->crtc);
> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/pl111/pl111_gem.c b/drivers/gpu/drm/pl111/pl111_gem.c
> new file mode 100644
> index 000000000000..b7b0e453dea9
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_gem.c
> @@ -0,0 +1,35 @@
> +/*
> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
> + *
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (c) 2006-2008 Intel Corporation
> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + *
> + * This program is free software and is provided to you under the terms of the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + *
> + */
> +
> +/**
> + * pl111_drm_gem.c
> + * Implementation of the GEM functions for PL111 DRM
> + */
> +#include <linux/version.h>
> +#include <linux/shmem_fs.h>
> +#include <linux/dma-buf.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include "pl111_drm.h"
> +
> +int pl111_dumb_create(struct drm_file *file_priv,
> +		      struct drm_device *dev, struct drm_mode_create_dumb *args)
> +{
> +	args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
> +
> +	return drm_gem_cma_dumb_create_internal(file_priv, dev, args);
> +}
> diff --git a/drivers/gpu/drm/pl111/pl111_plane.c b/drivers/gpu/drm/pl111/pl111_plane.c
> new file mode 100644
> index 000000000000..bcdc2f43de46
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_plane.c
> @@ -0,0 +1,167 @@
> +/*
> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
> + *
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (c) 2006-2008 Intel Corporation
> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + *
> + * This program is free software and is provided to you under the terms of the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + *
> + */
> +
> +#include <linux/amba/clcd-regs.h>
> +#include <linux/of_graph.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "pl111_drm.h"
> +
> +static int pl111_primary_plane_atomic_check(struct drm_plane *plane,
> +					    struct drm_plane_state *state)
> +{
> +	return 0;
> +}
> +
> +static void pl111_primary_plane_atomic_update(struct drm_plane *plane,
> +					      struct drm_plane_state *old_state)
> +{
> +	struct drm_device *dev = plane->dev;
> +	struct pl111_drm_dev_private *priv = dev->dev_private;
> +	struct drm_framebuffer *fb = plane->state->fb;
> +	struct drm_gem_cma_object *obj;
> +	u32 addr, cntl;
> +
> +	if (!fb)
> +		return;
> +
> +	obj = drm_fb_cma_get_gem_obj(fb, 0);
> +	addr = obj->paddr + fb->offsets[0];
> +	addr += fb->format->cpp[0] * plane->state->src_x;
> +	addr += fb->pitches[0] * plane->state->src_y;
> +
> +	writel(addr, priv->regs + CLCD_UBAS);
> +	writel(addr + (fb->height - 1 * fb->pitches[0]),
> +	       priv->regs + CLCD_LBAS);
> +
> +	cntl = readl(priv->regs + CLCD_PL111_CNTL);
> +	cntl &= ~(7 << 1);
> +
> +	/* Note that the the hardware's format reader takes 'r' from
> +	 * the low bit, while DRM formats list channels from high bit
> +	 * to low bit as you read left to right.
> +	 */
> +	switch (fb->format->format) {
> +	case DRM_FORMAT_ABGR8888:
> +	case DRM_FORMAT_XBGR8888:
> +		cntl |= CNTL_LCDBPP24;
> +		break;
> +	case DRM_FORMAT_ARGB8888:
> +	case DRM_FORMAT_XRGB8888:
> +		cntl |= CNTL_LCDBPP24 | CNTL_BGR;
> +		break;
> +	case DRM_FORMAT_BGR565:
> +		cntl |= CNTL_LCDBPP16_565;
> +		break;
> +	case DRM_FORMAT_RGB565:
> +		cntl |= CNTL_LCDBPP16_565 | CNTL_BGR;
> +		break;
> +	case DRM_FORMAT_ABGR1555:
> +	case DRM_FORMAT_XBGR1555:
> +		cntl |= CNTL_LCDBPP16;
> +		break;
> +	case DRM_FORMAT_ARGB1555:
> +	case DRM_FORMAT_XRGB1555:
> +		cntl |= CNTL_LCDBPP16 | CNTL_BGR;
> +		break;
> +	case DRM_FORMAT_ABGR4444:
> +	case DRM_FORMAT_XBGR4444:
> +		cntl |= CNTL_LCDBPP16_444;
> +		break;
> +	case DRM_FORMAT_ARGB4444:
> +	case DRM_FORMAT_XRGB4444:
> +		cntl |= CNTL_LCDBPP16_444 | CNTL_BGR;
> +		break;
> +	}
> +
> +	writel(cntl, priv->regs + CLCD_PL111_CNTL);
> +}
> +
> +static const struct drm_plane_helper_funcs pl111_primary_plane_helper_funcs = {
> +	.atomic_check = pl111_primary_plane_atomic_check,
> +	.atomic_update = pl111_primary_plane_atomic_update,
> +};
> +
> +static const struct drm_plane_funcs pl111_primary_plane_funcs = {
> +	.update_plane = drm_atomic_helper_update_plane,
> +	.disable_plane = drm_atomic_helper_disable_plane,
> +	.reset = drm_atomic_helper_plane_reset,
> +	.destroy = drm_plane_cleanup,
> +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +};
> +
> +int pl111_primary_plane_init(struct drm_device *drm)
> +{
> +	struct pl111_drm_dev_private *priv = drm->dev_private;
> +	struct drm_plane *plane = &priv->primary;
> +	struct device *dev = drm->dev;
> +	static const u32 formats[] = {
> +		DRM_FORMAT_ABGR8888,
> +		DRM_FORMAT_XBGR8888,
> +		DRM_FORMAT_ARGB8888,
> +		DRM_FORMAT_XRGB8888,
> +		DRM_FORMAT_BGR565,
> +		DRM_FORMAT_RGB565,
> +		DRM_FORMAT_ABGR1555,
> +		DRM_FORMAT_XBGR1555,
> +		DRM_FORMAT_ARGB1555,
> +		DRM_FORMAT_XRGB1555,
> +		DRM_FORMAT_ABGR4444,
> +		DRM_FORMAT_XBGR4444,
> +		DRM_FORMAT_ARGB4444,
> +		DRM_FORMAT_XRGB4444,
> +	};
> +	struct device_node *endpoint;
> +	u32 tft_r0b0g0[3];
> +	int ret;
> +
> +	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
> +	if (!endpoint)
> +		return -ENODEV;
> +
> +	if (of_property_read_u32_array(endpoint,
> +				       "arm,pl11x,tft-r0g0b0-pads",
> +				       tft_r0b0g0,
> +				       ARRAY_SIZE(tft_r0b0g0)) != 0) {
> +		dev_err(dev, "arm,pl11x,tft-r0g0b0-pads should be 3 ints\n");
> +		of_node_put(endpoint);
> +		return -ENOENT;
> +	}
> +	of_node_put(endpoint);
> +
> +	if (tft_r0b0g0[0] != 0 ||
> +	    tft_r0b0g0[1] != 8 ||
> +	    tft_r0b0g0[2] != 16) {
> +		dev_err(dev, "arm,pl11x,tft-r0g0b0-pads != [0,8,16] not yet supported\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = drm_universal_plane_init(drm, plane, 0,
> +				       &pl111_primary_plane_funcs,
> +				       formats, ARRAY_SIZE(formats),
> +				       DRM_PLANE_TYPE_PRIMARY, NULL);
> +	if (ret)
> +		return ret;
> +
> +	drm_plane_helper_add(plane, &pl111_primary_plane_helper_funcs);
> +
> +	return 0;
> +}
> -- 
> 2.11.0
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111
  2017-03-20  8:34     ` Daniel Vetter
@ 2017-03-20 18:23       ` Eric Anholt
  -1 siblings, 0 replies; 18+ messages in thread
From: Eric Anholt @ 2017-03-20 18:23 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: dri-devel, tom.cooksey, Russell King, linux-kernel

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

Daniel Vetter <daniel@ffwll.ch> writes:

> On Fri, Mar 17, 2017 at 03:47:42PM -0700, Eric Anholt wrote:
>> From: Tom Cooksey <tom.cooksey@arm.com>
>> 
>> This is a modesetting driver for the pl111 CLCD display controller
>> found on various ARM platforms such as the Versatile Express. The
>> driver has only been tested on the bcm911360_entphn platform so far,
>> with PRIME-based buffer sharing between vc4 and clcd.
>> 
>> It reuses the existing devicetree binding, while not using quite as
>> many of its properties as the fbdev driver does (those are left for
>> future work).
>> 
>> v2: Nearly complete rewrite by anholt, cutting 2/3 of the code thanks
>>     to DRM core's excellent new helpers.
>> 
>> Signed-off-by: Tom Cooksey <tom.cooksey@arm.com>
>> Signed-off-by: Eric Anholt <eric@anholt.net>
>
> Looks pretty. A few things below, but nothing big. I'd say if the "how
> generic do we want this to be for now" question is resolved it's ready to
> go in.
>
> If you want this in drm-misc (imo fine, you're already there so doesn't
> really extend the scope of the experiment), then please also add a
> MAINTAINERS entry with the drm-misc git repo and yourself as reviewer.

Will do.

>> diff --git a/drivers/gpu/drm/pl111/pl111_connector.c b/drivers/gpu/drm/pl111/pl111_connector.c
>> new file mode 100644
>> index 000000000000..9811d1eadb63
>> --- /dev/null
>> +++ b/drivers/gpu/drm/pl111/pl111_connector.c
>> @@ -0,0 +1,127 @@
>> +/*
>> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
>> + *
>> + * Parts of this file were based on sources as follows:
>> + *
>> + * Copyright (c) 2006-2008 Intel Corporation
>> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
>> + * Copyright (C) 2011 Texas Instruments
>> + *
>> + * This program is free software and is provided to you under the terms of the
>> + * GNU General Public License version 2 as published by the Free Software
>> + * Foundation, and any use by you of this program is subject to the terms of
>> + * such GNU licence.
>> + *
>> + */
>> +
>> +/**
>> + * pl111_drm_connector.c
>> + * Implementation of the connector functions for PL111 DRM
>> + */
>> +#include <linux/amba/clcd-regs.h>
>> +#include <linux/version.h>
>> +#include <linux/shmem_fs.h>
>> +#include <linux/dma-buf.h>
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_of.h>
>> +#include <drm/drm_panel.h>
>> +
>> +#include "pl111_drm.h"
>> +
>> +static void pl111_connector_destroy(struct drm_connector *connector)
>> +{
>> +	struct pl111_drm_connector *pl111_connector =
>> +		to_pl111_connector(connector);
>> +
>> +	if (pl111_connector->panel)
>> +		drm_panel_detach(pl111_connector->panel);
>> +
>> +	drm_connector_unregister(connector);
>> +	drm_connector_cleanup(connector);
>> +}
>> +
>> +static enum drm_connector_status pl111_connector_detect(struct drm_connector
>> +							*connector, bool force)
>> +{
>> +	struct pl111_drm_connector *pl111_connector =
>> +		to_pl111_connector(connector);
>> +
>> +	return (pl111_connector->panel ?
>> +		connector_status_connected :
>> +		connector_status_disconnected);
>> +}
>> +
>> +static int pl111_connector_helper_get_modes(struct drm_connector *connector)
>> +{
>> +	struct pl111_drm_connector *pl111_connector =
>> +		to_pl111_connector(connector);
>> +
>> +	if (!pl111_connector->panel)
>> +		return 0;
>> +
>> +	return drm_panel_get_modes(pl111_connector->panel);
>> +}
>
> Probably the umptenth time I've seen this :(
>
> One option I think would work well is if we have a generic "wrap a
> drm_panel into a drm_bridge" driver and just glue that in with an of
> helper as the last element in the enc/transcoder. Laurent has that
> practically written already, but he insist in calling it the lvds bridge,
> because it's for a 100% dummy lvds transcoder.
>
> But since it's 100% dummy it's indistinguishable from pure sw
> abstraction/impendence mismatch helper.
>
> Anyway, just an idea, not going to ask you to do this, but if drm_panel
> takes of like crazy we'll probably want this.

It seems like a generalization of lvds_encoder to wrap a panel as a
connector would be useful.  I think I'd like to look at that as a
follow-on change.

>> +static int pl111_modeset_init(struct drm_device *dev)
>> +{
>> +	struct drm_mode_config *mode_config;
>> +	struct pl111_drm_dev_private *priv = dev->dev_private;
>> +	int ret = 0;
>> +
>> +	if (!priv)
>> +		return -EINVAL;
>> +
>> +	drm_mode_config_init(dev);
>> +	mode_config = &dev->mode_config;
>> +	mode_config->funcs = &mode_config_funcs;
>> +	mode_config->min_width = 1;
>> +	mode_config->max_width = 1024;
>> +	mode_config->min_height = 1;
>> +	mode_config->max_height = 768;
>> +
>> +	ret = pl111_primary_plane_init(dev);
>> +	if (ret != 0) {
>> +		dev_err(dev->dev, "Failed to init primary plane\n");
>> +		goto out_config;
>> +	}
>
> I assume this display IP has a pile of planes? Otherwise the simple pipe
> helpers look like a perfect fit.

Only two, actually.  And the other (cursor) is a 64x64 monochrome with
mask, so I'm not sure it's going to make sense to ever expose it.  I
think I'll give the simple helper a try before resubmitting.

>> +static const struct file_operations drm_fops = {
>> +	.owner = THIS_MODULE,
>> +	.open = drm_open,
>> +	.release = drm_release,
>> +	.unlocked_ioctl = drm_ioctl,
>> +	.mmap = drm_gem_cma_mmap,
>> +	.poll = drm_poll,
>> +	.read = drm_read,
>> +};
>
> Very recent, but DEFINE_DRM_GEM_CMA_FOPS.

Will do.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

* Re: [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111
@ 2017-03-20 18:23       ` Eric Anholt
  0 siblings, 0 replies; 18+ messages in thread
From: Eric Anholt @ 2017-03-20 18:23 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: linux-kernel, Russell King, dri-devel


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

Daniel Vetter <daniel@ffwll.ch> writes:

> On Fri, Mar 17, 2017 at 03:47:42PM -0700, Eric Anholt wrote:
>> From: Tom Cooksey <tom.cooksey@arm.com>
>> 
>> This is a modesetting driver for the pl111 CLCD display controller
>> found on various ARM platforms such as the Versatile Express. The
>> driver has only been tested on the bcm911360_entphn platform so far,
>> with PRIME-based buffer sharing between vc4 and clcd.
>> 
>> It reuses the existing devicetree binding, while not using quite as
>> many of its properties as the fbdev driver does (those are left for
>> future work).
>> 
>> v2: Nearly complete rewrite by anholt, cutting 2/3 of the code thanks
>>     to DRM core's excellent new helpers.
>> 
>> Signed-off-by: Tom Cooksey <tom.cooksey@arm.com>
>> Signed-off-by: Eric Anholt <eric@anholt.net>
>
> Looks pretty. A few things below, but nothing big. I'd say if the "how
> generic do we want this to be for now" question is resolved it's ready to
> go in.
>
> If you want this in drm-misc (imo fine, you're already there so doesn't
> really extend the scope of the experiment), then please also add a
> MAINTAINERS entry with the drm-misc git repo and yourself as reviewer.

Will do.

>> diff --git a/drivers/gpu/drm/pl111/pl111_connector.c b/drivers/gpu/drm/pl111/pl111_connector.c
>> new file mode 100644
>> index 000000000000..9811d1eadb63
>> --- /dev/null
>> +++ b/drivers/gpu/drm/pl111/pl111_connector.c
>> @@ -0,0 +1,127 @@
>> +/*
>> + * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
>> + *
>> + * Parts of this file were based on sources as follows:
>> + *
>> + * Copyright (c) 2006-2008 Intel Corporation
>> + * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
>> + * Copyright (C) 2011 Texas Instruments
>> + *
>> + * This program is free software and is provided to you under the terms of the
>> + * GNU General Public License version 2 as published by the Free Software
>> + * Foundation, and any use by you of this program is subject to the terms of
>> + * such GNU licence.
>> + *
>> + */
>> +
>> +/**
>> + * pl111_drm_connector.c
>> + * Implementation of the connector functions for PL111 DRM
>> + */
>> +#include <linux/amba/clcd-regs.h>
>> +#include <linux/version.h>
>> +#include <linux/shmem_fs.h>
>> +#include <linux/dma-buf.h>
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_of.h>
>> +#include <drm/drm_panel.h>
>> +
>> +#include "pl111_drm.h"
>> +
>> +static void pl111_connector_destroy(struct drm_connector *connector)
>> +{
>> +	struct pl111_drm_connector *pl111_connector =
>> +		to_pl111_connector(connector);
>> +
>> +	if (pl111_connector->panel)
>> +		drm_panel_detach(pl111_connector->panel);
>> +
>> +	drm_connector_unregister(connector);
>> +	drm_connector_cleanup(connector);
>> +}
>> +
>> +static enum drm_connector_status pl111_connector_detect(struct drm_connector
>> +							*connector, bool force)
>> +{
>> +	struct pl111_drm_connector *pl111_connector =
>> +		to_pl111_connector(connector);
>> +
>> +	return (pl111_connector->panel ?
>> +		connector_status_connected :
>> +		connector_status_disconnected);
>> +}
>> +
>> +static int pl111_connector_helper_get_modes(struct drm_connector *connector)
>> +{
>> +	struct pl111_drm_connector *pl111_connector =
>> +		to_pl111_connector(connector);
>> +
>> +	if (!pl111_connector->panel)
>> +		return 0;
>> +
>> +	return drm_panel_get_modes(pl111_connector->panel);
>> +}
>
> Probably the umptenth time I've seen this :(
>
> One option I think would work well is if we have a generic "wrap a
> drm_panel into a drm_bridge" driver and just glue that in with an of
> helper as the last element in the enc/transcoder. Laurent has that
> practically written already, but he insist in calling it the lvds bridge,
> because it's for a 100% dummy lvds transcoder.
>
> But since it's 100% dummy it's indistinguishable from pure sw
> abstraction/impendence mismatch helper.
>
> Anyway, just an idea, not going to ask you to do this, but if drm_panel
> takes of like crazy we'll probably want this.

It seems like a generalization of lvds_encoder to wrap a panel as a
connector would be useful.  I think I'd like to look at that as a
follow-on change.

>> +static int pl111_modeset_init(struct drm_device *dev)
>> +{
>> +	struct drm_mode_config *mode_config;
>> +	struct pl111_drm_dev_private *priv = dev->dev_private;
>> +	int ret = 0;
>> +
>> +	if (!priv)
>> +		return -EINVAL;
>> +
>> +	drm_mode_config_init(dev);
>> +	mode_config = &dev->mode_config;
>> +	mode_config->funcs = &mode_config_funcs;
>> +	mode_config->min_width = 1;
>> +	mode_config->max_width = 1024;
>> +	mode_config->min_height = 1;
>> +	mode_config->max_height = 768;
>> +
>> +	ret = pl111_primary_plane_init(dev);
>> +	if (ret != 0) {
>> +		dev_err(dev->dev, "Failed to init primary plane\n");
>> +		goto out_config;
>> +	}
>
> I assume this display IP has a pile of planes? Otherwise the simple pipe
> helpers look like a perfect fit.

Only two, actually.  And the other (cursor) is a 64x64 monochrome with
mask, so I'm not sure it's going to make sense to ever expose it.  I
think I'll give the simple helper a try before resubmitting.

>> +static const struct file_operations drm_fops = {
>> +	.owner = THIS_MODULE,
>> +	.open = drm_open,
>> +	.release = drm_release,
>> +	.unlocked_ioctl = drm_ioctl,
>> +	.mmap = drm_gem_cma_mmap,
>> +	.poll = drm_poll,
>> +	.read = drm_read,
>> +};
>
> Very recent, but DEFINE_DRM_GEM_CMA_FOPS.

Will do.

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

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

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

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

* Re: [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111
  2017-03-17 23:09   ` Russell King - ARM Linux
@ 2017-03-23 21:54       ` Linus Walleij
  2017-03-23 21:54       ` Linus Walleij
  1 sibling, 0 replies; 18+ messages in thread
From: Linus Walleij @ 2017-03-23 21:54 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Eric Anholt, open list:DRM PANEL DRIVERS, tom.cooksey, linux-kernel

On Sat, Mar 18, 2017 at 12:09 AM, Russell King - ARM Linux
<linux@armlinux.org.uk> wrote:
> On Fri, Mar 17, 2017 at 03:47:42PM -0700, Eric Anholt wrote:
>> This is a modesetting driver for the pl111 CLCD display controller
>> found on various ARM platforms such as the Versatile Express. The
>> driver has only been tested on the bcm911360_entphn platform so far,
>> with PRIME-based buffer sharing between vc4 and clcd.
>>
>> It reuses the existing devicetree binding, while not using quite as
>> many of its properties as the fbdev driver does (those are left for
>> future work).
>
> As we're multiplatform on ARM, and this is using the PL11x AMBA IDs,
> we must ensure that it's compatible with everything that the fbdev
> driver is compatible with... however, I suspect that's not worth the
> effort (unless Linus W wants it?)

Hm, I certainly want it... but it would be unreasonable of me to expect
Eric to cold-code a big upfront design for systems he can't even test
this on.

What I would request would rather be: please do not put in any
immediate roadblocks and keep it in the back of your head that I will
maybe come in and add support for the PL110 systems once this
works. (Of course that would be with the aim to deprecate and
delete the old fbdev driver in favor of this one.)

I can help with some RealView testing along the way to begin with.

Yours,
Linus Walleij

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

* Re: [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111
@ 2017-03-23 21:54       ` Linus Walleij
  0 siblings, 0 replies; 18+ messages in thread
From: Linus Walleij @ 2017-03-23 21:54 UTC (permalink / raw)
  To: Russell King - ARM Linux; +Cc: linux-kernel, open list:DRM PANEL DRIVERS

On Sat, Mar 18, 2017 at 12:09 AM, Russell King - ARM Linux
<linux@armlinux.org.uk> wrote:
> On Fri, Mar 17, 2017 at 03:47:42PM -0700, Eric Anholt wrote:
>> This is a modesetting driver for the pl111 CLCD display controller
>> found on various ARM platforms such as the Versatile Express. The
>> driver has only been tested on the bcm911360_entphn platform so far,
>> with PRIME-based buffer sharing between vc4 and clcd.
>>
>> It reuses the existing devicetree binding, while not using quite as
>> many of its properties as the fbdev driver does (those are left for
>> future work).
>
> As we're multiplatform on ARM, and this is using the PL11x AMBA IDs,
> we must ensure that it's compatible with everything that the fbdev
> driver is compatible with... however, I suspect that's not worth the
> effort (unless Linus W wants it?)

Hm, I certainly want it... but it would be unreasonable of me to expect
Eric to cold-code a big upfront design for systems he can't even test
this on.

What I would request would rather be: please do not put in any
immediate roadblocks and keep it in the back of your head that I will
maybe come in and add support for the PL110 systems once this
works. (Of course that would be with the aim to deprecate and
delete the old fbdev driver in favor of this one.)

I can help with some RealView testing along the way to begin with.

Yours,
Linus Walleij
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111
  2017-03-23 21:54       ` Linus Walleij
  (?)
@ 2017-03-23 23:31       ` Russell King - ARM Linux
  2017-03-24  8:08           ` Linus Walleij
  -1 siblings, 1 reply; 18+ messages in thread
From: Russell King - ARM Linux @ 2017-03-23 23:31 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Eric Anholt, open list:DRM PANEL DRIVERS, tom.cooksey, linux-kernel

On Thu, Mar 23, 2017 at 10:54:53PM +0100, Linus Walleij wrote:
> Hm, I certainly want it... but it would be unreasonable of me to expect
> Eric to cold-code a big upfront design for systems he can't even test
> this on.
> 
> What I would request would rather be: please do not put in any
> immediate roadblocks and keep it in the back of your head that I will
> maybe come in and add support for the PL110 systems once this
> works. (Of course that would be with the aim to deprecate and
> delete the old fbdev driver in favor of this one.)

I'm only suggesting that it doesn't match the PL110 hardware as long as
it doesn't support the PL110...

-- 
RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
according to speedtest.net.

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

* Re: [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111
  2017-03-23 23:31       ` Russell King - ARM Linux
@ 2017-03-24  8:08           ` Linus Walleij
  0 siblings, 0 replies; 18+ messages in thread
From: Linus Walleij @ 2017-03-24  8:08 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: Eric Anholt, open list:DRM PANEL DRIVERS, Tom Cooksey, linux-kernel

On Fri, Mar 24, 2017 at 12:31 AM, Russell King - ARM Linux
<linux@armlinux.org.uk> wrote:
> On Thu, Mar 23, 2017 at 10:54:53PM +0100, Linus Walleij wrote:
>> Hm, I certainly want it... but it would be unreasonable of me to expect
>> Eric to cold-code a big upfront design for systems he can't even test
>> this on.
>>
>> What I would request would rather be: please do not put in any
>> immediate roadblocks and keep it in the back of your head that I will
>> maybe come in and add support for the PL110 systems once this
>> works. (Of course that would be with the aim to deprecate and
>> delete the old fbdev driver in favor of this one.)
>
> I'm only suggesting that it doesn't match the PL110 hardware as long as
> it doesn't support the PL110...

Of course. Let's proceed like this.

Yours,
Linus Walleij

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

* Re: [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111
@ 2017-03-24  8:08           ` Linus Walleij
  0 siblings, 0 replies; 18+ messages in thread
From: Linus Walleij @ 2017-03-24  8:08 UTC (permalink / raw)
  To: Russell King - ARM Linux; +Cc: linux-kernel, open list:DRM PANEL DRIVERS

On Fri, Mar 24, 2017 at 12:31 AM, Russell King - ARM Linux
<linux@armlinux.org.uk> wrote:
> On Thu, Mar 23, 2017 at 10:54:53PM +0100, Linus Walleij wrote:
>> Hm, I certainly want it... but it would be unreasonable of me to expect
>> Eric to cold-code a big upfront design for systems he can't even test
>> this on.
>>
>> What I would request would rather be: please do not put in any
>> immediate roadblocks and keep it in the back of your head that I will
>> maybe come in and add support for the PL110 systems once this
>> works. (Of course that would be with the aim to deprecate and
>> delete the old fbdev driver in favor of this one.)
>
> I'm only suggesting that it doesn't match the PL110 hardware as long as
> it doesn't support the PL110...

Of course. Let's proceed like this.

Yours,
Linus Walleij
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

end of thread, other threads:[~2017-03-24  8:19 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-17 22:47 [PATCH 0/2] ARM CLCD DRM driver Eric Anholt
2017-03-17 22:47 ` Eric Anholt
2017-03-17 22:47 ` [PATCH 1/2] video: ARM CLCD: Move registers to a separate header Eric Anholt
2017-03-17 22:47   ` Eric Anholt
2017-03-17 22:47 ` [PATCH 2/2] drm/pl111: Initial drm/kms driver for pl111 Eric Anholt
2017-03-17 22:47   ` Eric Anholt
2017-03-17 23:09   ` Russell King - ARM Linux
2017-03-18  0:45     ` Eric Anholt
2017-03-18  0:45       ` Eric Anholt
2017-03-23 21:54     ` Linus Walleij
2017-03-23 21:54       ` Linus Walleij
2017-03-23 23:31       ` Russell King - ARM Linux
2017-03-24  8:08         ` Linus Walleij
2017-03-24  8:08           ` Linus Walleij
2017-03-20  8:34   ` Daniel Vetter
2017-03-20  8:34     ` Daniel Vetter
2017-03-20 18:23     ` Eric Anholt
2017-03-20 18:23       ` Eric Anholt

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