Эх сурвалжийг харах

Merge branch 'audio_display-ti-linux-4.19.y' of git.ti.com:~jyrisarha/ti-linux-kernel/jyrisarhas-audio-video-linux-feature-tree into ti-linux-4.19.y

TI-Feature: audio-display
TI-Tree: git@git.ti.com:~jyrisarha/ti-linux-kernel/jyrisarhas-audio-video-linux-feature-tree.git
TI-Branch: audio_display-ti-linux-4.19.y

* 'audio_display-ti-linux-4.19.y' of git.ti.com:~jyrisarha/ti-linux-kernel/jyrisarhas-audio-video-linux-feature-tree: (87 commits)
  drm/bridge: sii902x: HACK Add more relaxed custom DDC transfer implementation
  ARM: dts: am437x-gp-evm: add HDMI audio support
  ARM: dts: keystone-k2g-evm: Add LCD support for K2G EVM
  ARM: dts: keystone-k2g-evm: add HDMI and analog audio data
  ARM: dts: keystone-k2g: Add DSS node
  drm/bridge: sii902x: pixel clock unit is 10kHz instead of 1kHz
  drm/bridge: sii902x: Implement HDMI audio support
  drm/bridge: sii902x: Set output mode to HDMI or DVI according to EDID
  drm/tidss: dispc6: Fix "max-memory-bandwidth" property implementation
  ti_config_fragments: Add WB to audio_display.cfg
  drm/omap: add WB support
  drm/omap: add omap_plane_reserve/release_wb
  ARM: dts: am437x-gp-evm: add HDMI support
  ARM: dts: am437x-gp/epos-evm: fix panel compatible
  drm/omap: add global alpha and pre multipled alpha plane props
  drm/omap: trans_key property can't be used with dual plane
  drm/omap: Fix zpos handling when Transparency Color Key mode is enabled
  drm/omap: add alpha blender property
  drm/omap: add crtc transparency key property
  drm/omap: add crtc background property
  ...

Signed-off-by: LCPD Auto Merger <lcpd_integration@list.ti.com>
LCPD Auto Merger 6 жил өмнө
parent
commit
28cda193c1
100 өөрчлөгдсөн 7841 нэмэгдсэн , 2099 устгасан
  1. 29 0
      Documentation/devicetree/bindings/display/bridge/sii902x.txt
  2. 18 6
      Documentation/devicetree/bindings/display/bridge/ti,tfp410.txt
  3. 12 0
      Documentation/devicetree/bindings/display/panel/osddisplays,osd070t1718-19ts.txt
  4. 49 0
      Documentation/devicetree/bindings/display/ti/ti,dra7evm-tpd12s015.txt
  5. 1 0
      Documentation/devicetree/bindings/vendor-prefixes.txt
  6. 15 1
      arch/arm/boot/dts/Makefile
  7. 122 0
      arch/arm/boot/dts/am437x-gp-evm-hdmi.dts
  8. 1 1
      arch/arm/boot/dts/am437x-gp-evm.dts
  9. 1 1
      arch/arm/boot/dts/am43x-epos-evm.dts
  10. 108 0
      arch/arm/boot/dts/am57xx-idk-common.dtsi
  11. 103 0
      arch/arm/boot/dts/dra7-evm.dts
  12. 99 0
      arch/arm/boot/dts/keystone-k2g-evm-lcd.dts
  13. 218 0
      arch/arm/boot/dts/keystone-k2g-evm.dts
  14. 22 0
      arch/arm/boot/dts/keystone-k2g.dtsi
  15. 6 1
      arch/arm/boot/dts/ti/Makefile
  16. 13 0
      arch/arm/boot/dts/ti/am571x-idk-touchscreen.dtso
  17. 13 0
      arch/arm/boot/dts/ti/am572x-idk-touchscreen.dtso
  18. 56 0
      arch/arm/boot/dts/ti/am57xx-idk-osd-lcd-common.dtso
  19. 122 0
      arch/arm/boot/dts/ti/am57xx-idk.its
  20. 106 0
      arch/arm/boot/dts/ti/dra71-evm-lcd-auo-g101evn01.0.dtso
  21. 48 0
      arch/arm/boot/dts/ti/dra71-evm.its
  22. 13 0
      arch/arm/boot/dts/ti/dra72-evm-touchscreen.dtso
  23. 89 0
      arch/arm/boot/dts/ti/dra72-evm.its
  24. 13 0
      arch/arm/boot/dts/ti/dra74-evm-touchscreen.dtso
  25. 76 0
      arch/arm/boot/dts/ti/dra74-evm.its
  26. 113 0
      arch/arm/boot/dts/ti/dra7x-evm-osd-lcd-common.dtso
  27. 34 0
      arch/arm/boot/dts/ti/lcd-osd101t2045.dtso
  28. 34 0
      arch/arm/boot/dts/ti/lcd-osd101t2587.dtso
  29. 10 0
      drivers/gpu/drm/bridge/Kconfig
  30. 1 0
      drivers/gpu/drm/bridge/Makefile
  31. 3 3
      drivers/gpu/drm/bridge/dumb-vga-dac.c
  32. 539 13
      drivers/gpu/drm/bridge/sii902x.c
  33. 917 0
      drivers/gpu/drm/bridge/tc358768.c
  34. 105 4
      drivers/gpu/drm/bridge/ti-tfp410.c
  35. 2 2
      drivers/gpu/drm/drm_atomic_helper.c
  36. 6 6
      drivers/gpu/drm/drm_modes.c
  37. 1 1
      drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
  38. 1 1
      drivers/gpu/drm/imx/ipuv3-crtc.c
  39. 3 3
      drivers/gpu/drm/mxsfb/mxsfb_crtc.c
  40. 11 0
      drivers/gpu/drm/omapdrm/Kconfig
  41. 2 0
      drivers/gpu/drm/omapdrm/Makefile
  42. 5 15
      drivers/gpu/drm/omapdrm/displays/Kconfig
  43. 1 3
      drivers/gpu/drm/omapdrm/displays/Makefile
  44. 1 44
      drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c
  45. 0 330
      drivers/gpu/drm/omapdrm/displays/connector-dvi.c
  46. 1 44
      drivers/gpu/drm/omapdrm/displays/connector-hdmi.c
  47. 369 0
      drivers/gpu/drm/omapdrm/displays/dra7-evm-encoder-tpd12s015.c
  48. 2 37
      drivers/gpu/drm/omapdrm/displays/encoder-opa362.c
  49. 0 170
      drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c
  50. 0 40
      drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c
  51. 0 221
      drivers/gpu/drm/omapdrm/displays/panel-dpi.c
  52. 55 85
      drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c
  53. 10 31
      drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c
  54. 10 31
      drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c
  55. 27 34
      drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c
  56. 13 42
      drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c
  57. 16 36
      drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c
  58. 15 33
      drivers/gpu/drm/omapdrm/displays/panel-tpo-td043mtea1.c
  59. 102 42
      drivers/gpu/drm/omapdrm/dss/base.c
  60. 2 2
      drivers/gpu/drm/omapdrm/dss/dispc.c
  61. 22 2
      drivers/gpu/drm/omapdrm/dss/display.c
  62. 18 46
      drivers/gpu/drm/omapdrm/dss/dpi.c
  63. 30 47
      drivers/gpu/drm/omapdrm/dss/dsi.c
  64. 7 53
      drivers/gpu/drm/omapdrm/dss/dss-of.c
  65. 1 1
      drivers/gpu/drm/omapdrm/dss/dss.c
  66. 12 42
      drivers/gpu/drm/omapdrm/dss/hdmi4.c
  67. 12 42
      drivers/gpu/drm/omapdrm/dss/hdmi5.c
  68. 18 1
      drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c
  69. 35 43
      drivers/gpu/drm/omapdrm/dss/omapdss.h
  70. 39 4
      drivers/gpu/drm/omapdrm/dss/output.c
  71. 21 47
      drivers/gpu/drm/omapdrm/dss/sdi.c
  72. 101 128
      drivers/gpu/drm/omapdrm/dss/venc.c
  73. 87 105
      drivers/gpu/drm/omapdrm/omap_connector.c
  74. 5 3
      drivers/gpu/drm/omapdrm/omap_connector.h
  75. 104 11
      drivers/gpu/drm/omapdrm/omap_crtc.c
  76. 3 0
      drivers/gpu/drm/omapdrm/omap_crtc.h
  77. 172 121
      drivers/gpu/drm/omapdrm/omap_drv.c
  78. 32 1
      drivers/gpu/drm/omapdrm/omap_drv.h
  79. 129 82
      drivers/gpu/drm/omapdrm/omap_encoder.c
  80. 1 2
      drivers/gpu/drm/omapdrm/omap_encoder.h
  81. 4 0
      drivers/gpu/drm/omapdrm/omap_irq.c
  82. 76 0
      drivers/gpu/drm/omapdrm/omap_overlay.c
  83. 8 0
      drivers/gpu/drm/omapdrm/omap_overlay.h
  84. 105 1
      drivers/gpu/drm/omapdrm/omap_plane.c
  85. 4 0
      drivers/gpu/drm/omapdrm/omap_plane.h
  86. 179 0
      drivers/gpu/drm/omapdrm/omap_wb.c
  87. 214 0
      drivers/gpu/drm/omapdrm/omap_wb.h
  88. 1044 0
      drivers/gpu/drm/omapdrm/omap_wb_cap.c
  89. 1199 0
      drivers/gpu/drm/omapdrm/omap_wb_m2m.c
  90. 6 0
      drivers/gpu/drm/panel/Kconfig
  91. 1 0
      drivers/gpu/drm/panel/Makefile
  92. 2 2
      drivers/gpu/drm/panel/panel-arm-versatile.c
  93. 2 2
      drivers/gpu/drm/panel/panel-ilitek-ili9322.c
  94. 252 0
      drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c
  95. 1 1
      drivers/gpu/drm/panel/panel-seiko-43wvf1g.c
  96. 111 12
      drivers/gpu/drm/panel/panel-simple.c
  97. 1 1
      drivers/gpu/drm/pl111/pl111_display.c
  98. 0 2
      drivers/gpu/drm/tidss/tidss_dispc.h
  99. 17 13
      drivers/gpu/drm/tidss/tidss_dispc6.c
  100. 2 1
      drivers/gpu/drm/tve200/tve200_display.c

+ 29 - 0
Documentation/devicetree/bindings/display/bridge/sii902x.txt

@@ -8,6 +8,26 @@ Optional properties:
 	- interrupts: describe the interrupt line used to inform the host 
 	  about hotplug events.
 	- reset-gpios: OF device-tree gpio specification for RST_N pin.
+	- i2s-fifo-routing: Array of exactly 4 integers indicating i2s
+	  pins for audio fifo routing. First integer defines routing to
+	  fifo 0 and second to fifo 1, etc. Integers can be filled with
+	  definitions from: include/dt-bindings/sound/sii902x-audio.h
+	  The available definitions are:
+	  - ENABLE_BIT:	enable this audio fifo
+	  - CONNECT_SD#: route audio input from SD0, SD1, SD2, or SD3 i2s
+			 data input pin
+	  - LEFT_RIGHT_SWAP_BIT: swap i2s input channels for this fifo
+	  I2S HDMI audio is configured only if this property is found.
+	- clocks: describes SII902x MCLK input. MCLK is used to produce
+	  HDMI audio CTS values. This property is required if
+	  "i2s-fifo-routing"-property is present. This property follows
+	  Documentation/devicetree/bindings/clock/clock-bindings.txt
+	  consumer binding.
+	- use-custom-ddc-probe: Use custom DDC read function for
+	  getting EDID. SiI902X DDC pass trough transfers appears to
+	  be somewhat unreliable on some systems. When this property
+	  is present there is an extra sleep before each DDC transfer
+	  operation. This helps to make the ddc probe more reliable.
 
 Optional subnodes:
 	- video input: this subnode can contain a video input port node
@@ -21,6 +41,15 @@ Example:
 		compatible = "sil,sii9022";
 		reg = <0x39>;
 		reset-gpios = <&pioA 1 0>;
+
+		i2s-fifo-routing = <
+			(ENABLE_BIT|CONNECT_SD0)
+			0
+			0
+			0
+		>;
+		clocks = <&mclk>;
+
 		ports {
 			#address-cells = <1>;
 			#size-cells = <0>;

+ 18 - 6
Documentation/devicetree/bindings/display/bridge/ti,tfp410.txt

@@ -6,15 +6,25 @@ Required properties:
 
 Optional properties:
 - powerdown-gpios: power-down gpio
-- reg: I2C address. If and only if present the device node
-       should be placed into the i2c controller node where the
-       tfp410 i2c is connected to.
+- reg: I2C address. If and only if present the device node should be placed
+  into the I2C controller node where the TFP410 I2C is connected to.
+- ti,deskew: data de-skew in 350ps increments, from -4 to +3, as configured
+  through th DK[3:1] pins. This property shall be present only if the TFP410
+  is not connected through I2C.
 
 Required nodes:
-- Video port 0 for DPI input [1].
-- Video port 1 for DVI output [1].
 
-[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
+This device has two video ports. Their connections are modeled using the OF
+graph bindings specified in [1]. Each port node shall have a single endpoint.
+
+- Port 0 is the DPI input port. Its endpoint subnode shall contain a
+  pclk-sample property and a remote-endpoint property as specified in [1].
+
+- Port 1 is the DVI output port. Its endpoint subnode shall contain a
+  remote-endpoint property is specified in [1].
+
+[1] Documentation/devicetree/bindings/media/video-interfaces.txt
+
 
 Example
 -------
@@ -22,6 +32,7 @@ Example
 tfp410: encoder@0 {
 	compatible = "ti,tfp410";
 	powerdown-gpios = <&twl_gpio 2 GPIO_ACTIVE_LOW>;
+	ti,deskew = <4>;
 
 	ports {
 		#address-cells = <1>;
@@ -31,6 +42,7 @@ tfp410: encoder@0 {
 			reg = <0>;
 
 			tfp410_in: endpoint@0 {
+				pclk-sample = <1>;
 				remote-endpoint = <&dpi_out>;
 			};
 		};

+ 12 - 0
Documentation/devicetree/bindings/display/panel/osddisplays,osd070t1718-19ts.txt

@@ -0,0 +1,12 @@
+OSD Displays OSD070T1718-19TS 7" WVGA TFT LCD panel
+
+Required properties:
+- compatible: shall be "osddisplays,osd070t1718-19ts"
+- power-supply: see simple-panel.txt
+
+Optional properties:
+- backlight: see simple-panel.txt
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory. No other simple-panel properties than
+the ones specified herein are valid.

+ 49 - 0
Documentation/devicetree/bindings/display/ti/ti,dra7evm-tpd12s015.txt

@@ -0,0 +1,49 @@
+TPD12S015 HDMI level shifter and ESD protection chip customized for DRA7 EVM
+============================================================================
+
+Required properties:
+- compatible: "ti,dra7evm-tpd12s015"
+
+Optional properties:
+- gpios: CT CP HPD, LS OE and HPD gpios
+- disable-hpd: Disables hot plug detect, required for old DRA7
+  EVMs(Rev D and older)
+
+Required nodes:
+- Video port 0 for HDMI input
+- Video port 1 for HDMI output
+
+Example
+-------
+
+tpd12s015: encoder@1 {
+	compatible = "ti,dra7evm-tpd12s015";
+
+	gpios = <&gpio2 28 GPIO_ACTIVE_HIGH>,	/* 60, CT CP HPD */
+		<&gpio2 9 GPIO_ACTIVE_HIGH>,	/* 41, LS OE */
+		<&gpio2 31 GPIO_ACTIVE_HIGH>	/* 63, HPD */
+		<&mcasp8 2 GPIO_ACTIVE_HIGH>;	/* mcasp8.axr2, i2c2/ddc switch */
+
+	disable-hpd;
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@0 {
+			reg = <0>;
+
+			tpd12s015_in: endpoint@0 {
+				remote-endpoint = <&hdmi_out>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+
+			tpd12s015_out: endpoint@0 {
+				remote-endpoint = <&hdmi_connector_in>;
+			};
+		};
+	};
+};

+ 1 - 0
Documentation/devicetree/bindings/vendor-prefixes.txt

@@ -285,6 +285,7 @@ oranth	Shenzhen Oranth Technology Co., Ltd.
 ORCL	Oracle Corporation
 orisetech	Orise Technology
 ortustech	Ortus Technology Co., Ltd.
+osddisplays	OSD Displays
 ovti	OmniVision Technologies
 oxsemi	Oxford Semiconductor, Ltd.
 panasonic	Panasonic Corporation

+ 15 - 1
arch/arm/boot/dts/Makefile

@@ -230,6 +230,7 @@ dtb-$(CONFIG_ARCH_KEYSTONE) += \
 	keystone-k2l-evm.dtb \
 	keystone-k2e-evm.dtb \
 	keystone-k2g-evm.dtb \
+	keystone-k2g-evm-lcd.dtb \
 	keystone-k2g-ice.dtb
 dtb-$(CONFIG_MACH_KIRKWOOD) += \
 	kirkwood-b3.dtb \
@@ -733,6 +734,7 @@ dtb-$(CONFIG_SOC_AM43XX) += \
 	am43x-epos-evm.dtb \
 	am437x-cm-t43.dtb \
 	am437x-gp-evm.dtb \
+	am437x-gp-evm-hdmi.dtb \
 	am437x-idk-evm.dtb \
 	am437x-sbc-t43.dtb \
 	am437x-sk-evm.dtb
@@ -761,7 +763,19 @@ dtb-merge-$(CONFIG_SOC_DRA7XX) += \
 	am57xx-evm-cam-ov10635.dtb \
 	am57xx-evm-reva3.dtb \
 	am57xx-evm-reva3-cam-mt9t111.dtb \
-	am57xx-evm-reva3-cam-ov10635.dtb
+	am57xx-evm-reva3-cam-ov10635.dtb \
+	am571x-idk-lcd-osd101t2045.dtb \
+	am571x-idk-lcd-osd101t2587.dtb \
+	am572x-idk-lcd-osd101t2045.dtb \
+	am572x-idk-lcd-osd101t2587.dtb \
+	am574x-idk-lcd-osd101t2587.dtb \
+	dra71-evm-lcd-auo-g101evn01.0.dtb \
+	dra72-evm-lcd-osd101t2045.dtb \
+	dra72-evm-lcd-osd101t2587.dtb \
+	dra72-evm-revc-lcd-osd101t2045.dtb \
+	dra72-evm-revc-lcd-osd101t2587.dtb \
+	dra7-evm-lcd-osd101t2045.dtb \
+	dra7-evm-lcd-osd101t2587.dtb
 ifeq ($(CONFIG_SOC_DRA7XX),y)
 dts-dirs += ti
 endif

+ 122 - 0
arch/arm/boot/dts/am437x-gp-evm-hdmi.dts

@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* AM437x GP EVM with HDMI output */
+
+#include "am437x-gp-evm.dts"
+
+/delete-node/ &lcd0;
+
+#include <dt-bindings/sound/sii902x-audio.h>
+
+/ {
+	aliases {
+		display0 = &hdmi;
+	};
+
+	hdmi: connector {
+		compatible = "hdmi-connector";
+		label = "hdmi";
+
+		type = "b";
+
+		port {
+			hdmi_connector_in: endpoint {
+				remote-endpoint = <&sii9022_out>;
+			};
+		};
+	};
+
+	sound@1 {
+		compatible = "simple-audio-card";
+		simple-audio-card,name = "HDMI";
+		simple-audio-card,format = "i2s";
+		simple-audio-card,bitclock-master = <&hdmi_dailink_master>;
+		simple-audio-card,frame-master = <&hdmi_dailink_master>;
+		hdmi_dailink_master: simple-audio-card,cpu {
+			sound-dai = <&mcasp1>;
+			system-clock-frequency = <24000000>;
+			system-clock-direction-out;
+		};
+
+		simple-audio-card,codec {
+			sound-dai = <&sii9022>;
+		};
+	};
+
+	sii9022_mclk: sii9022_mclk {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <12000000>;
+	};
+};
+
+&lcd_bl {
+	status = "disabled";
+};
+
+&sound0 {
+	status = "disabled";
+};
+
+&tlv320aic3106 {
+	status = "disabled";
+};
+
+&i2c1 {
+	sii9022: sii9022@3b {
+		#sound-dai-cells = <0>;
+		compatible = "sil,sii9022";
+		reg = <0x3b>;
+
+		interrupt-parent = <&gpio3>;
+		interrupts = <24 IRQ_TYPE_LEVEL_LOW>;
+
+		i2s-fifo-routing = <
+			(ENABLE_BIT|CONNECT_SD0)
+			0
+			0
+			0
+		>;
+		clocks = <&sii9022_mclk>;
+		use-custom-ddc-probe;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+
+				sii9022_in: endpoint {
+					remote-endpoint = <&dpi_out>;
+				};
+			};
+
+			port@1 {
+				reg = <1>;
+
+				sii9022_out: endpoint {
+					remote-endpoint = <&hdmi_connector_in>;
+				};
+			};
+		};
+	};
+};
+
+&dpi_out {
+	remote-endpoint = <&sii9022_in>;
+	data-lines = <24>;
+};
+
+/* Override SelLCDorHDMI from am437x-gp-evm.dts to select HDMI */
+&gpio5 {
+	p8 {
+		output-low;
+	};
+};

+ 1 - 1
arch/arm/boot/dts/am437x-gp-evm.dts

@@ -89,7 +89,7 @@
 		};
 
 	lcd0: display {
-		compatible = "osddisplays,osd057T0559-34ts", "panel-dpi";
+		compatible = "osddisplays,osd070t1718-19ts", "panel-dpi";
 		label = "lcd";
 
 		backlight = <&lcd_bl>;

+ 1 - 1
arch/arm/boot/dts/am43x-epos-evm.dts

@@ -45,7 +45,7 @@
 	};
 
 	lcd0: display {
-		compatible = "osddisplays,osd057T0559-34ts", "panel-dpi";
+		compatible = "osddisplays,osd070t1718-19ts", "panel-dpi";
 		label = "lcd";
 
 		backlight = <&lcd_bl>;

+ 108 - 0
arch/arm/boot/dts/am57xx-idk-common.dtsi

@@ -12,6 +12,7 @@
 	aliases {
 		rtc0 = &tps659038_rtc;
 		rtc1 = &rtc;
+		display0 = &hdmi0;
 	};
 
 	chosen {
@@ -105,6 +106,54 @@
 			default-state = "off";
 		};
 	};
+
+	hdmi0: connector@0 {
+		compatible = "hdmi-connector";
+		label = "hdmi";
+
+		type = "a";
+
+		port {
+			hdmi_connector_in: endpoint {
+				remote-endpoint = <&tpd12s015_out>;
+			};
+		};
+	};
+
+	tpd12s015: encoder@0 {
+		compatible = "ti,tpd12s016", "ti,tpd12s015";
+
+		gpios = <0>, /* optional CT_CP_HPD */
+			<0>, /* optional LS_OE */
+			<&gpio7 12 GPIO_ACTIVE_HIGH>;	/* HPD */
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+
+				tpd12s015_in: endpoint@0 {
+					remote-endpoint = <&hdmi_out>;
+				};
+			};
+
+			port@1 {
+				reg = <1>;
+
+				tpd12s015_out: endpoint@0 {
+					remote-endpoint = <&hdmi_connector_in>;
+				};
+			};
+		};
+	};
+
+	src_clk_x1: src_clk_x1 {
+		#clock-cells = <0>;
+		compatible = "fixed-clock";
+		clock-frequency = <20000000>;
+	};
 };
 
 &dra7_pmx_core {
@@ -363,6 +412,28 @@
 			};
 		};
 	};
+
+	dsi_bridge: tc358778@0e {
+		compatible = "toshiba,tc358778", "toshiba,tc358768";
+		reg = <0x0e>;
+		status = "disabled";
+
+		clocks = <&src_clk_x1>;
+		clock-names = "refclk";
+
+		dsi_bridge_ports: ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				rgb_in: endpoint {
+					remote-endpoint = <&dpi_out>;
+					data-lines = <24>;
+				};
+			};
+		};
+	};
 };
 
 &mcspi3 {
@@ -511,3 +582,40 @@
 &gpu {
 	status = "ok";
 };
+
+&hdmi {
+	status = "okay";
+	/*
+	 * XXX: Support AM572x-Rev 1.2a. this is wrong for AM571x-rev 1.3a,
+	 * AM572x-Rev1.3a - but thanks to always-on, they work.
+	 * TODO: SWITCH TO LDO4 once rev 1.2a is deprecated
+	 * (on rev 1.3a availability)
+	 */
+	vdda-supply = <&ldo3_reg>;
+
+	port {
+		hdmi_out: endpoint {
+			remote-endpoint = <&tpd12s015_in>;
+		};
+	};
+};
+
+&dss {
+	status = "okay";
+
+	vdda_video-supply = <&ldoln_reg>;
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@0 {
+			reg = <0>;
+
+			dpi_out: endpoint {
+				remote-endpoint = <&rgb_in>;
+				data-lines = <24>;
+			};
+		};
+	};
+};

+ 103 - 0
arch/arm/boot/dts/dra7-evm.dts

@@ -15,6 +15,12 @@
 	model = "TI DRA742";
 	compatible = "ti,dra7-evm", "ti,dra742", "ti,dra74", "ti,dra7";
 
+	aliases {
+		display0 = &hdmi0;
+		sound0 = &sound0;
+		sound1 = &hdmi;
+	};
+
 	memory@0 {
 		device_type = "memory";
 		reg = <0x0 0x80000000 0x0 0x60000000>; /* 1536 MB */
@@ -118,6 +124,54 @@
 		gpio = <&gpio7 11 GPIO_ACTIVE_HIGH>;
 	};
 
+	hdmi0: connector@1 {
+		compatible = "hdmi-connector";
+		label = "hdmi";
+
+		type = "a";
+
+		port {
+			hdmi_connector_in: endpoint {
+				remote-endpoint = <&tpd12s015_out>;
+			};
+		};
+	};
+
+	tpd12s015: encoder@1 {
+		compatible = "ti,dra7evm-tpd12s015";
+
+		pinctrl-names = "i2c", "ddc";
+		pinctrl-0 = <&hdmi_i2c_pins_i2c>;
+		pinctrl-1 = <&hdmi_i2c_pins_ddc>;
+
+		ddc-i2c-bus = <&i2c2>;
+
+		gpios = <&pcf_hdmi 4 0>,	/* P4, CT CP HPD */
+			<&pcf_hdmi 5 0>,	/* P5, LS OE */
+			<&gpio7 12 0>,	/* gpio7_12/sp1_cs2, HPD */
+			<&mcasp8 2 0>;	/* i2c2/ddc switch */
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+
+				tpd12s015_in: endpoint@0 {
+					remote-endpoint = <&hdmi_out>;
+				};
+			};
+
+			port@1 {
+				reg = <1>;
+
+				tpd12s015_out: endpoint@0 {
+					remote-endpoint = <&hdmi_connector_in>;
+				};
+			};
+		};
+	};
 };
 
 &dra7_pmx_core {
@@ -134,6 +188,27 @@
 			DRA7XX_CORE_IOPAD(0x3818, MUX_MODE15 | PULL_UP)	/* wakeup0.off */
 		>;
 	};
+
+	mcasp8_axr2_pin: pinmux_mcasp8_axr2_pin {
+		pinctrl-single,pins = <
+			/* this pin is used as a GPIO via mcasp */
+			DRA7XX_CORE_IOPAD(0x36fc, PIN_OUTPUT | MUX_MODE1) /* mcasp2_aclkr.mcasp8_axr2 */
+		>;
+	};
+
+	hdmi_i2c_pins_i2c: pinmux_hdmi_i2c_pins_default {
+		pinctrl-single,pins = <
+			DRA7XX_CORE_IOPAD(0x3808, PIN_INPUT | MUX_MODE0) /* i2c2_sda.i2c2_sda */
+			DRA7XX_CORE_IOPAD(0x380c, PIN_INPUT | MUX_MODE0) /* i2c2_scl.i2c2_scl */
+		>;
+	};
+
+	hdmi_i2c_pins_ddc: pinmux_hdmi_i2c_pins_ddc {
+		pinctrl-single,pins = <
+			DRA7XX_CORE_IOPAD(0x3808, PIN_INPUT | MUX_MODE1) /* i2c2_sda.hdmi1_ddc_scl */
+			DRA7XX_CORE_IOPAD(0x380c, PIN_INPUT | MUX_MODE1) /* i2c2_scl.hdmi1_ddc_sda */
+		>;
+	};
 };
 
 &i2c1 {
@@ -555,3 +630,31 @@
 		remote-endpoint = <&onboardLI>;
 	};
 };
+
+&dss {
+	status = "ok";
+
+	vdda_video-supply = <&ldoln_reg>;
+};
+
+&hdmi {
+	status = "ok";
+	vdda-supply = <&ldo3_reg>;
+
+	port {
+		hdmi_out: endpoint {
+			remote-endpoint = <&tpd12s015_in>;
+		};
+	};
+};
+
+&mcasp8 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&mcasp8_axr2_pin>;
+
+	gpio-controller;
+	#gpio-cells = <2>;
+
+	/* Not used for audio, only AXR2 pin is used as GPIO */
+	status = "okay";
+};

+ 99 - 0
arch/arm/boot/dts/keystone-k2g-evm-lcd.dts

@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device Tree Source for K2G EVM with LCD Display
+ *
+ * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#include "keystone-k2g-evm.dts"
+#include <dt-bindings/pwm/pwm.h>
+
+/ {
+	lcd0: display {
+		compatible = "newhaven,nhd-4.3-480272ef-atxl";
+		label = "lcd";
+
+		backlight = <&lcd_bl>;
+
+		port {
+			lcd_in: endpoint {
+				remote-endpoint = <&dpi_out>;
+			};
+		};
+	};
+
+	lcd_bl: backlight {
+		compatible = "pwm-backlight";
+		pwms = <&ecap0 0 50000 PWM_POLARITY_INVERTED>;
+		brightness-levels = <0 32 64 96 128 160 192 224 255>;
+		default-brightness-level = <8>;
+	};
+
+	sound1: sound@1 {
+		compatible = "simple-audio-card";
+		simple-audio-card,name = "K2G-EVM-LCD";
+		simple-audio-card,widgets =
+			"Headphone", "Headphone Jack",
+			"Line", "Line In";
+		simple-audio-card,routing =
+			"Headphone Jack",       "HPLOUT",
+			"Headphone Jack",       "HPROUT",
+			"LINE1L",               "Line In",
+			"LINE1R",               "Line In";
+		simple-audio-card,format = "i2s";
+		simple-audio-card,bitclock-master = <&sound1_master>;
+		simple-audio-card,frame-master = <&sound1_master>;
+
+		sound1_master: simple-audio-card,cpu {
+			sound-dai = <&mcasp2>;
+			clocks = <&k2g_clks 0x6 1>;
+			system-clock-id = <MCASP_CLK_HCLK_AUXCLK>;
+		};
+
+		simple-audio-card,codec {
+			sound-dai = <&tlv320aic3106>;
+			system-clock-frequency = <12288000>;
+		};
+	};
+};
+
+&i2c1 {
+	edt-ft5306@38 {
+		status = "okay";
+		compatible = "edt,edt-ft5306", "edt,edt-ft5x06";
+		reg = <0x38>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&edt_ft5306_ts_pins>;
+
+		interrupt-parent = <&gpio1>;
+		interrupts = <42 IRQ_TYPE_EDGE_FALLING>;
+
+		touchscreen-size-x = <480>;
+		touchscreen-size-y = <272>;
+	};
+};
+
+&k2g_pinctrl {
+	edt_ft5306_ts_pins: edt_ft5306_ts_pins {
+		pinctrl-single,pins = <
+			K2G_CORE_IOPAD(0x1364) (BUFFER_CLASS_B | PIN_PULLDOWN | MUX_MODE3)	/* pr1_pru1_gpo16.gpio1_42 */
+		>;
+	};
+};
+
+&dpi_out {
+	remote-endpoint = <&lcd_in>;
+};
+
+&hdmi {
+	status = "disabled";
+};
+
+&sii9022 {
+	status = "disabled";
+};
+
+&sound0 {
+	status = "disabled";
+};

+ 218 - 0
arch/arm/boot/dts/keystone-k2g-evm.dts

@@ -6,6 +6,8 @@
  */
 /dts-v1/;
 
+#include <dt-bindings/sound/ti-mcasp.h>
+#include <dt-bindings/sound/sii902x-audio.h>
 #include "keystone-k2g.dtsi"
 
 / {
@@ -48,10 +50,80 @@
 	vcc1v8_ldo1_reg: fixedregulator-vcc1v8-ldo1 {
 		compatible = "regulator-fixed";
 		regulator-name = "ldo1";
+	};
+
+	/* This is actually coming from TPS659118:LDO2_1V8 */
+	vcc1v8_aud_reg: fixedregulator-vcc1v8-aud {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc1v8_aud_fixed";
 		regulator-min-microvolt = <1800000>;
 		regulator-max-microvolt = <1800000>;
 		regulator-always-on;
 	};
+
+	hdmi: connector {
+		compatible = "hdmi-connector";
+		label = "hdmi";
+
+		type = "a";
+
+		port {
+			hdmi_connector_in: endpoint {
+				remote-endpoint = <&sii9022_out>;
+			};
+		};
+	};
+
+	sound0: sound@0 {
+		compatible = "simple-audio-card";
+		simple-audio-card,name = "K2G-EVM";
+		simple-audio-card,widgets =
+			"Headphone", "Headphone Jack",
+			"Line", "Line In";
+		simple-audio-card,routing =
+			"Headphone Jack",	"HPLOUT",
+			"Headphone Jack",	"HPROUT",
+			"LINE1L",		"Line In",
+			"LINE1R",		"Line In";
+
+		simple-audio-card,dai-link@0 {
+			format = "i2s";
+			bitclock-master = <&sound0_0_master>;
+			frame-master = <&sound0_0_master>;
+			sound0_0_master: cpu {
+				sound-dai = <&mcasp2>;
+				clocks = <&k2g_clks 0x6 1>;
+				system-clock-id = <MCASP_CLK_HCLK_AUXCLK>;
+			};
+
+			codec {
+				sound-dai = <&tlv320aic3106>;
+				system-clock-frequency = <12288000>;
+			};
+		};
+
+		simple-audio-card,dai-link@1 {
+			format = "i2s";
+			bitclock-master = <&sound0_1_master>;
+			frame-master = <&sound0_1_master>;
+			sound0_1_master: cpu {
+				sound-dai = <&mcasp2>;
+				clocks = <&k2g_clks 0x6 1>;
+				system-clock-id = <MCASP_CLK_HCLK_AUXCLK>;
+			};
+
+			codec {
+				sound-dai = <&sii9022>;
+				system-clock-frequency = <12288000>;
+			};
+		};
+	};
+
+	sii9022_mclk: sii9022_mclk {
+               compatible = "fixed-clock";
+               #clock-cells = <0>;
+               clock-frequency = <12288000>;
+       };
 };
 
 &k2g_pinctrl {
@@ -96,6 +168,13 @@
 		>;
 	};
 
+	i2c1_pins: pinmux_i2c1_pins {
+		pinctrl-single,pins = <
+			K2G_CORE_IOPAD(0x1384) (BUFFER_CLASS_B | PIN_PULLUP | MUX_MODE0)	/* i2c1_scl.i2c1_scl */
+			K2G_CORE_IOPAD(0x1388) (BUFFER_CLASS_B | PIN_PULLUP | MUX_MODE0)	/* i2c1_sda.i2c1_sda */
+		>;
+	};
+
 	ecap0_pins: ecap0_pins {
 		pinctrl-single,pins = <
 			K2G_CORE_IOPAD(0x1374) (BUFFER_CLASS_B | MUX_MODE4)	/* pr1_mdio_data.ecap0_in_apwm0_out */
@@ -167,6 +246,49 @@
 			K2G_CORE_IOPAD(0x1188) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0)	/* MDIO_DATA.MDIO_DATA */
 		>;
 	};
+
+	vout_pins: pinmux_vout_pins {
+		pinctrl-single,pins = <
+			K2G_CORE_IOPAD(0x1078) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata23.dssdata23 */
+			K2G_CORE_IOPAD(0x107c) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata22.dssdata22 */
+			K2G_CORE_IOPAD(0x1080) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata21.dssdata21 */
+			K2G_CORE_IOPAD(0x1084) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata20.dssdata20 */
+			K2G_CORE_IOPAD(0x1088) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata19.dssdata19 */
+			K2G_CORE_IOPAD(0x108c) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata18.dssdata18 */
+			K2G_CORE_IOPAD(0x1090) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata17.dssdata17 */
+			K2G_CORE_IOPAD(0x1094) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata16.dssdata16 */
+			K2G_CORE_IOPAD(0x1098) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata15.dssdata15 */
+			K2G_CORE_IOPAD(0x109c) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata14.dssdata14 */
+			K2G_CORE_IOPAD(0x10a0) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata13.dssdata13 */
+			K2G_CORE_IOPAD(0x10a4) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata12.dssdata12 */
+			K2G_CORE_IOPAD(0x10a8) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata11.dssdata11 */
+			K2G_CORE_IOPAD(0x10ac) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata10.dssdata10 */
+			K2G_CORE_IOPAD(0x10b0) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata9.dssdata9 */
+			K2G_CORE_IOPAD(0x10b4) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata8.dssdata8 */
+			K2G_CORE_IOPAD(0x10b8) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata7.dssdata7 */
+			K2G_CORE_IOPAD(0x10bc) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata6.dssdata6 */
+			K2G_CORE_IOPAD(0x10c0) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata5.dssdata5 */
+			K2G_CORE_IOPAD(0x10c4) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata4.dssdata4 */
+			K2G_CORE_IOPAD(0x10c8) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata3.dssdata3 */
+			K2G_CORE_IOPAD(0x10cc) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata2.dssdata2 */
+			K2G_CORE_IOPAD(0x10d0) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata1.dssdata1 */
+			K2G_CORE_IOPAD(0x10d4) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssdata0.dssdata0 */
+			K2G_CORE_IOPAD(0x10d8) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssvsync.dssvsync */
+			K2G_CORE_IOPAD(0x10dc) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dsshsync.dsshsync */
+			K2G_CORE_IOPAD(0x10e0) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dsspclk.dsspclk */
+			K2G_CORE_IOPAD(0x10e4) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssde.dssde */
+			K2G_CORE_IOPAD(0x10e8) (BUFFER_CLASS_B | PULL_DISABLE | MUX_MODE0) /* dssfid.dssfid */
+		>;
+	};
+
+	mcasp2_pins: pinmux_mcasp2_pins {
+		pinctrl-single,pins = <
+			K2G_CORE_IOPAD(0x1234) (BUFFER_CLASS_B | PIN_PULLDOWN | MUX_MODE4)	/* pr0_pru_gpo2.mcasp2_axr2 */
+			K2G_CORE_IOPAD(0x1238) (BUFFER_CLASS_B | PIN_PULLDOWN | MUX_MODE4)	/* pr0_pru_gpo3.mcasp2_axr3 */
+			K2G_CORE_IOPAD(0x1254) (BUFFER_CLASS_B | PIN_PULLDOWN | MUX_MODE4)	/* pr0_pru_gpo10.mcasp2_afsx */
+			K2G_CORE_IOPAD(0x125c) (BUFFER_CLASS_B | PIN_PULLDOWN | MUX_MODE4)	/* pr0_pru_gpo12.mcasp2_aclkx */
+		>;
+	};
 };
 
 &uart0 {
@@ -364,3 +486,99 @@
 	pinctrl-0 = <&emac_pins>;
 	status = "okay";
 };
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&i2c1_pins>;
+	status = "okay";
+	clock-frequency = <400000>;
+
+	sii9022: sii9022@3b {
+		#sound-dai-cells = <0>;
+		compatible = "sil,sii9022";
+		reg = <0x3b>;
+
+		i2s-fifo-routing = <
+			(ENABLE_BIT|CONNECT_SD0)
+			0
+			0
+			0
+		>;
+		clocks = <&sii9022_mclk>;
+		use-custom-ddc-probe;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+
+				sii9022_in: endpoint {
+					remote-endpoint = <&dpi_out>;
+				};
+			};
+
+			port@1 {
+				reg = <1>;
+
+				sii9022_out: endpoint {
+					remote-endpoint = <&hdmi_connector_in>;
+				};
+			};
+		};
+	};
+
+	tlv320aic3106: tlv320aic3106@1b {
+		#sound-dai-cells = <0>;
+		compatible = "ti,tlv320aic3106";
+		reg = <0x1b>;
+		status = "okay";
+
+		/* Regulators */
+		AVDD-supply = <&vcc3v3_dcin_reg>;
+		IOVDD-supply = <&vcc3v3_dcin_reg>;
+		DRVDD-supply = <&vcc3v3_dcin_reg>;
+		DVDD-supply = <&vcc1v8_aud_reg>;
+	};
+};
+
+&dss {
+	pinctrl-names = "default";
+	pinctrl-0 = <&vout_pins>;
+	status = "ok";
+
+	port {
+		dpi_out: endpoint {
+			remote-endpoint = <&sii9022_in>;
+			data-lines = <24>;
+		};
+	};
+};
+
+&k2g_clks {
+	/* on the board 22.5792MHz is connected to AUDOSC_IN */
+	assigned-clocks = <&k2g_clks 0x4c 2>;
+	assigned-clock-rates = <22579200>;
+};
+
+&mcasp2 {
+	#sound-dai-cells = <0>;
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&mcasp2_pins>;
+
+	assigned-clocks = <&k2g_clks 0x6 1>;
+	assigned-clock-parents = <&k2g_clks 0x6 2>;
+
+	status = "okay";
+
+	op-mode = <0>;		/* MCASP_IIS_MODE */
+	tdm-slots = <2>;
+	/* 6 serializer */
+	serial-dir = <	/* 0: INACTIVE, 1: TX, 2: RX */
+		0 0 1 2 0 0 // AXR2: TX, AXR3: rx
+	>;
+	tx-num-evt = <32>;
+	rx-num-evt = <32>;
+};

+ 22 - 0
arch/arm/boot/dts/keystone-k2g.dtsi

@@ -337,6 +337,28 @@
 			clock-names = "gpio";
 		};
 
+		dss: dss@02540000 {
+			compatible = "ti,k2g-dss";
+			reg =	<0x02540000 0x400>,
+				<0x02550000 0x1000>,
+				<0x02557000 0x1000>,
+				<0x0255a800 0x100>,
+				<0x0255ac00 0x100>;
+			reg-names = "cfg", "common", "vid1", "ovr1", "vp1";
+			clocks =	<&k2g_clks 0x2 0>,
+					<&k2g_clks 0x2 1>;
+			clock-names = "fck", "vp1";
+			interrupts = <GIC_SPI 247 IRQ_TYPE_EDGE_RISING>;
+
+			power-domains = <&k2g_pds 0x2>;
+			status = "disabled";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges;
+
+			max-memory-bandwidth = <230000000>;
+		};
+
 		edma0: edma@2700000 {
 			compatible = "ti,k2g-edma3-tpcc", "ti,edma3-tpcc";
 			reg =	<0x02700000 0x8000>;

+ 6 - 1
arch/arm/boot/dts/ti/Makefile

@@ -39,7 +39,12 @@ $(obj)/%.itb: $(src)/%.its FORCE
 	$(call cmd,mkfit)
 
 ifeq ($(BUILD_ITBS),y)
-dtb-$(CONFIG_SOC_DRA7XX) += am57xx-evm.itb
+dtb-$(CONFIG_SOC_DRA7XX) += \
+	am57xx-evm.itb \
+	am57xx-idk.itb \
+	dra71-evm.itb \
+	dra72-evm.itb \
+	dra74-evm.itb
 else
 dtb-$(CONFIG_SOC_DRA7XX) += $(shell grep incbin $(srctree)/$(src)/*.its | grep dtb | cut -d "\"" -f 2)
 endif

+ 13 - 0
arch/arm/boot/dts/ti/am571x-idk-touchscreen.dtso

@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/interrupt-controller/irq.h>
+
+&touchscreen {
+	interrupt-parent = <&gpio5>;
+	interrupts = <6 IRQ_TYPE_EDGE_FALLING>;
+};

+ 13 - 0
arch/arm/boot/dts/ti/am572x-idk-touchscreen.dtso

@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/interrupt-controller/irq.h>
+
+&touchscreen {
+	interrupt-parent = <&gpio3>;
+	interrupts = <14 IRQ_TYPE_EDGE_FALLING>;
+};

+ 56 - 0
arch/arm/boot/dts/ti/am57xx-idk-osd-lcd-common.dtso

@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+/ {
+  fragment@101 {
+	target-path = "/";
+
+	__overlay__ {
+		aliases {
+			display0 = "/display";
+			display1 = "/connector";
+		};
+
+		lcd_bl: backlight {
+			compatible = "pwm-backlight";
+			pwms = <&ecap0 0 50000 1>;
+			brightness-levels = <0 51 53 56 62 75 101 152 255>;
+			default-brightness-level = <8>;
+		};
+	};
+  };
+};
+
+&dsi_bridge {
+	status = "okay";
+};
+
+&i2c1 {
+	touchscreen: edt-ft5506@38 {
+		status = "okay";
+		compatible = "edt,edt-ft5506", "edt,edt-ft5x06";
+
+		reg = <0x38>;
+
+		/* GPIO line is inverted before going to touch panel */
+		reset-gpios = <&gpio6 15 GPIO_ACTIVE_LOW>;
+
+		touchscreen-size-x = <1920>;
+		touchscreen-size-y = <1200>;
+	};
+};
+
+&epwmss0 {
+	status = "okay";
+};
+
+&ecap0 {
+	status = "okay";
+};

+ 122 - 0
arch/arm/boot/dts/ti/am57xx-idk.its

@@ -0,0 +1,122 @@
+/dts-v1/;
+
+/ {
+	description = "DRA72-evm";
+	#address-cells = <1>;
+
+	images {
+		kernel@1 {
+			description = "LCPD kernel";
+			data = /incbin/("../../zImage");
+			type = "kernel";
+			arch = "arm";
+			os = "linux";
+			compression = "none";
+			load = <0x82000000>;
+			entry = <0x82000000>;
+		};
+		fdt@1 {
+			description = "am571x-idk";
+			data = /incbin/("am571x-idk.dtb");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83000000>;
+		};
+		fdt@2 {
+			description = "am572x-idk";
+			data = /incbin/("am572x-idk.dtb");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83000000>;
+		};
+		fdt@3 {
+			description = "am574x-idk";
+			data = /incbin/("am574x-idk.dtb");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83000000>;
+		};
+		fdt@4 {
+			description = "am57xx-idk OSD LCD common";
+			data = /incbin/("am57xx-idk-osd-lcd-common.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83080000>;
+		};
+		fdt@5 {
+			description = "am571x-idk touchscreen";
+			data = /incbin/("am571x-idk-touchscreen.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83100000>;
+		};
+		fdt@6 {
+			description = "am572x-idk touchscreen";
+			data = /incbin/("am572x-idk-touchscreen.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83100000>;
+		};
+		fdt@7 {
+			description = "osd101t2045 LCD";
+			data = /incbin/("lcd-osd101t2045.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83180000>;
+		};
+		fdt@8 {
+			description = "osd101t2587 LCD";
+			data = /incbin/("lcd-osd101t2587.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83180000>;
+		};
+	};
+
+	configurations {
+		default = "dra72-evm.dtb";
+		am571x-idk.dtb {
+			description = "am571x-idk";
+			kernel = "kernel@1";
+			fdt = "fdt@1";
+		};
+		am572x-idk.dtb {
+			description = "am572x-idk";
+			kernel = "kernel@1";
+			fdt = "fdt@2";
+		};
+		am574x-idk.dtb {
+			description = "am574x-idk";
+			kernel = "kernel@1";
+			fdt = "fdt@3";
+		};
+		am571x-idk-lcd-osd101t2045 {
+			description = "am571x-idk with osd101t2045 LCD";
+			fdt = "fdt@1", "fdt@4", "fdt@5", "fdt@7";
+		};
+		am571x-idk-lcd-osd101t2587 {
+			description = "am571x-idk with osd101t2587 LCD";
+			fdt = "fdt@1", "fdt@4", "fdt@5", "fdt@8";
+		};
+		am572x-idk-lcd-osd101t2045 {
+			description = "am572x-idk with osd101t2045 LCD";
+			fdt = "fdt@2", "fdt@4", "fdt@6", "fdt@7";
+		};
+		am572x-idk-lcd-osd101t2587 {
+			description = "am572x-idk with osd101t2587 LCD";
+			fdt = "fdt@2", "fdt@4", "fdt@6", "fdt@8";
+		};
+		am574x-idk-lcd-osd101t2587 {
+			description = "am574x-idk with osd101t2587 LCD";
+			fdt = "fdt@3", "fdt@4", "fdt@6", "fdt@8";
+		};
+	};
+};

+ 106 - 0
arch/arm/boot/dts/ti/dra71-evm-lcd-auo-g101evn01.0.dtso

@@ -0,0 +1,106 @@
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+  fragment@101 {
+	target-path = "/";
+
+	__overlay__ {
+		model = "TI DRA71 EVM-LCD-AUO-Display";
+
+		aliases {
+			display0 = "/display";
+			display1 = "/connector";
+		};
+
+		lcd_bl: backlight {
+			compatible = "led-backlight";
+			brightness-levels = <0 2 38 74 110 146 182 218 255>;
+			default-brightness-level = <8>;
+			enable-gpios = <&pcf_display_board 0 GPIO_ACTIVE_LOW>;
+			leds = <&backlight_led>;
+		};
+
+		lcd: display {
+			compatible = "auo,g101evn01.0";
+			enable-gpios = <&pcf_lcd 13 GPIO_ACTIVE_LOW>;
+			label = "lcd";
+			backlight = <&lcd_bl>;
+
+			port {
+				lcd_in: endpoint {
+					remote-endpoint = <&dpi_out>;
+				};
+			};
+		};
+	};
+  };
+};
+
+&pcf_gpio_21 {
+	p0 {
+		gpio-hog;
+		gpios = <0 GPIO_ACTIVE_HIGH>;
+		line-name = "sel_gpmc_ad_vid_s0";
+		output-low;
+	};
+
+	p7 {
+		gpio-hog;
+		gpios = <7 GPIO_ACTIVE_HIGH>;
+		line-name = "sel_gpmc_ad_vid_s2";
+		output-high;
+	};
+};
+
+&i2c1 {
+	pcf_display_board: gpio@27 {
+		compatible = "nxp,pcf8575";
+		#gpio-cells = <2>;
+		gpio-controller;
+		reg = <0x27>;
+	};
+
+	tlc59108@40 {
+		compatible = "ti,tlc59108";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x40>;
+
+		backlight_led: bl@2 {
+			label = "backlight";
+			reg = <0x2>;
+		};
+	};
+
+	touchscreen: goodix-gt9271@14 {
+		compatible = "goodix,gt9271";
+		interrupt-parent = <&gpio1>;
+		interrupts = <15 IRQ_TYPE_LEVEL_HIGH>;
+		irq-gpios = <&pcf_display_board 6 GPIO_ACTIVE_HIGH>;
+		reg = <0x14>;
+		reset-gpios = <&pcf_display_board 5 GPIO_ACTIVE_LOW>;
+		status = "okay";
+		touchscreen-inverted-y;
+		touchscreen-size-x = <1280>;
+		touchscreen-size-y = <800>;
+	};
+};
+
+&dss {
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		dss_port: port {
+			reg = <2>;
+
+			dpi_out: endpoint {
+				data-lines = <24>;
+				remote-endpoint = <&lcd_in>;
+			};
+		};
+	};
+};

+ 48 - 0
arch/arm/boot/dts/ti/dra71-evm.its

@@ -0,0 +1,48 @@
+/dts-v1/;
+
+/ {
+	description = "DRA71x-evm";
+	#address-cells = <1>;
+
+	images {
+		kernel@1 {
+			description = "LCPD kernel";
+			data = /incbin/("../../zImage");
+			type = "kernel";
+			arch = "arm";
+			os = "linux";
+			compression = "none";
+			load = <0x82000000>;
+			entry = <0x82000000>;
+		};
+		fdt@1 {
+			description = "DRA71x-evm";
+			data = /incbin/("dra71-evm.dtb");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83000000>;
+		};
+		fdt@2 {
+			description = "DRA71x-evm LCD";
+			data = /incbin/("dra71-evm-lcd-auo-g101evn01.0.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83100000>;
+		};
+	};
+
+	configurations {
+		default = "dra71-evm.dtb";
+		dra71-evm.dtb {
+			description = "DRA71x-evm";
+			kernel = "kernel@1";
+			fdt = "fdt@1";
+		};
+		lcd-auo-g101evn01.0 {
+			description = "DRA71x-evm with LCD overlay";
+			fdt = "fdt@2";
+		};
+	};
+};

+ 13 - 0
arch/arm/boot/dts/ti/dra72-evm-touchscreen.dtso

@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/interrupt-controller/irq.h>
+
+&touchscreen {
+	interrupt-parent = <&gpio1>;
+	interrupts = <15 IRQ_TYPE_EDGE_FALLING>;
+};

+ 89 - 0
arch/arm/boot/dts/ti/dra72-evm.its

@@ -0,0 +1,89 @@
+/dts-v1/;
+
+/ {
+	description = "DRA72-evm";
+	#address-cells = <1>;
+
+	images {
+		kernel@1 {
+			description = "LCPD kernel";
+			data = /incbin/("../../zImage");
+			type = "kernel";
+			arch = "arm";
+			os = "linux";
+			compression = "none";
+			load = <0x82000000>;
+			entry = <0x82000000>;
+		};
+		fdt@1 {
+			description = "DRA72-evm";
+			data = /incbin/("dra72-evm.dtb");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83000000>;
+		};
+		fdt@2 {
+			description = "DRA72-evm-revc";
+			data = /incbin/("dra72-evm-revc.dtb");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83000000>;
+		};
+		fdt@3 {
+			description = "DRA7x-evm OSD LCD common";
+			data = /incbin/("dra7x-evm-osd-lcd-common.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83080000>;
+		};
+		fdt@4 {
+			description = "DRA72-evm touchscreen";
+			data = /incbin/("dra72-evm-touchscreen.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83100000>;
+		};
+		fdt@5 {
+			description = "osd101t2045 LCD";
+			data = /incbin/("lcd-osd101t2045.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83100000>;
+		};
+		fdt@6 {
+			description = "osd101t2587 LCD";
+			data = /incbin/("lcd-osd101t2587.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83100000>;
+		};
+	};
+
+	configurations {
+		default = "dra72-evm.dtb";
+		dra72-evm.dtb {
+			description = "DRA72-evm";
+			kernel = "kernel@1";
+			fdt = "fdt@1";
+		};
+		dra72-evm-revc.dtb {
+			description = "DRA72-evm-revc";
+			kernel = "kernel@1";
+			fdt = "fdt@2";
+		};
+		lcd-osd101t2045 {
+			description = "osd101t2045 LCD overlay";
+			fdt = "fdt@3", "fdt@4", "fdt@5";
+		};
+		lcd-osd101t2587 {
+			description = "osd101t2587 LCD overlay";
+			fdt = "fdt@3", "fdt@4", "fdt@6";
+		};
+	};
+};

+ 13 - 0
arch/arm/boot/dts/ti/dra74-evm-touchscreen.dtso

@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/interrupt-controller/irq.h>
+
+&touchscreen {
+	interrupt-parent = <&gpio1>;
+	interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
+};

+ 76 - 0
arch/arm/boot/dts/ti/dra74-evm.its

@@ -0,0 +1,76 @@
+/dts-v1/;
+
+/ {
+	description = "DRA74-evm";
+	#address-cells = <1>;
+
+	images {
+		kernel@1 {
+			description = "LCPD kernel";
+			data = /incbin/("../../zImage");
+			type = "kernel";
+			arch = "arm";
+			os = "linux";
+			compression = "none";
+			load = <0x82000000>;
+			entry = <0x82000000>;
+		};
+		fdt@1 {
+			description = "DRA74-evm";
+			data = /incbin/("dra7-evm.dtb");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83000000>;
+		};
+		fdt@2 {
+			description = "DRA7x-evm OSD LCD common";
+			data = /incbin/("dra7x-evm-osd-lcd-common.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83080000>;
+		};
+		fdt@3 {
+			description = "DRA74-evm touchscreen";
+			data = /incbin/("dra74-evm-touchscreen.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83100000>;
+		};
+		fdt@4 {
+			description = "osd101t2045 LCD";
+			data = /incbin/("lcd-osd101t2045.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83100000>;
+		};
+		fdt@5 {
+			description = "osd101t2587 LCD";
+			data = /incbin/("lcd-osd101t2587.dtbo");
+			type = "flat_dt";
+			arch = "arm";
+			compression = "none";
+			load = <0x83100000>;
+		};
+	};
+
+	configurations {
+		default = "dra7-evm.dtb";
+		dra7-evm.dtb {
+			description = "DRA74-evm";
+			kernel = "kernel@1";
+			fdt = "fdt@1";
+		};
+		lcd-osd101t2045 {
+			description = "osd101t2045 LCD overlay";
+			fdt = "fdt@2", "fdt@3", "fdt@4";
+		};
+		lcd-osd101t2587 {
+			description = "osd101t2587 LCD overlay";
+			fdt = "fdt@2", "fdt@3", "fdt@5";
+		};
+	};
+};

+ 113 - 0
arch/arm/boot/dts/ti/dra7x-evm-osd-lcd-common.dtso

@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+/dts-v1/;
+/plugin/;
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+
+/ {
+  fragment@101 {
+	target-path = "/";
+
+	__overlay__ {
+		aliases {
+			display0 = "/display";
+			display1 = "/connector";
+		};
+
+		lcd_bl: backlight {
+			compatible = "led-backlight";
+			leds = <&backlight_led>;
+			brightness-levels = <0 243 245 247 248 249 251 252 255>;
+			default-brightness-level = <8>;
+
+			enable-gpios = <&pcf_lcd 13 GPIO_ACTIVE_LOW>;
+		};
+
+		tc358768_refclk: tc358768_refclk {
+			#clock-cells = <0>;
+			compatible = "fixed-clock";
+			clock-frequency = <20000000>;
+		};
+	};
+  };
+};
+
+&i2c1 {
+	dsi_bridge: tc358768@0e {
+		compatible = "toshiba,tc358768";
+		reg = <0x0e>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		clocks = <&tc358768_refclk>;
+		clock-names = "refclk";
+
+		reset-gpios = <&pcf_display_board 0 GPIO_ACTIVE_LOW>;
+
+		dsi_bridge_ports: ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				rgb_in: endpoint {
+					remote-endpoint = <&dpi_out>;
+					data-lines = <24>;
+				};
+			};
+		};
+	};
+
+	tlc59108: tlc59116@40 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		compatible = "ti,tlc59108";
+		reg = <0x40>;
+
+		backlight_led: bl@2 {
+			label = "backlight";
+			reg = <0x2>;
+		};
+	};
+
+	pcf_display_board: gpio@27 {
+		compatible = "nxp,pcf8575";
+		reg = <0x27>;
+		gpio-controller;
+		#gpio-cells = <2>;
+	};
+
+	touchscreen: edt-ft5506@38 {
+		status = "okay";
+		compatible = "edt,edt-ft5506", "edt,edt-ft5x06";
+
+		reg = <0x38>;
+
+		/* GPIO line is inverted before going to touch panel */
+		reset-gpios = <&pcf_display_board 5 GPIO_ACTIVE_HIGH>;
+
+		touchscreen-size-x = <1920>;
+		touchscreen-size-y = <1200>;
+	};
+};
+
+&dss {
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@0 {
+			reg = <0>;
+
+			dpi_out: endpoint {
+				remote-endpoint = <&rgb_in>;
+				data-lines = <24>;
+			};
+		};
+
+	};
+};

+ 34 - 0
arch/arm/boot/dts/ti/lcd-osd101t2045.dtso

@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+/dts-v1/;
+/plugin/;
+
+
+&dsi_bridge {
+	lcd: display {
+		compatible = "osd,osd101t2045-53ts";
+		reg = <0>;
+
+		label = "lcd";
+
+		backlight = <&lcd_bl>;
+
+		port {
+			lcd_in: endpoint {
+				remote-endpoint = <&dsi_out>;
+			};
+		};
+	};
+};
+
+&dsi_bridge_ports {
+	port@1 {
+		reg = <1>;
+		dsi_out: endpoint {
+			remote-endpoint = <&lcd_in>;
+		};
+	};
+};

+ 34 - 0
arch/arm/boot/dts/ti/lcd-osd101t2587.dtso

@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+/dts-v1/;
+/plugin/;
+
+
+&dsi_bridge {
+	lcd: display {
+		compatible = "osd,osd101t2587-53ts";
+		reg = <0>;
+
+		label = "lcd";
+
+		backlight = <&lcd_bl>;
+
+		port {
+			lcd_in: endpoint {
+				remote-endpoint = <&dsi_out>;
+			};
+		};
+	};
+};
+
+&dsi_bridge_ports {
+	port@1 {
+		reg = <1>;
+		dsi_out: endpoint {
+			remote-endpoint = <&lcd_in>;
+		};
+	};
+};

+ 10 - 0
drivers/gpu/drm/bridge/Kconfig

@@ -121,6 +121,16 @@ config DRM_TOSHIBA_TC358767
 	---help---
 	  Toshiba TC358767 eDP bridge chip driver.
 
+config DRM_TOSHIBA_TC358768
+	tristate "Toshiba TC358768 MIPI DSI bridge"
+	depends on OF
+	select DRM_KMS_HELPER
+	select REGMAP_I2C
+	select DRM_PANEL
+	select DRM_MIPI_DSI
+	---help---
+	  Toshiba TC358768AXBG/TC358778XBG DSI bridge chip driver.
+
 config DRM_TI_TFP410
 	tristate "TI TFP410 DVI/HDMI bridge"
 	depends on OF

+ 1 - 0
drivers/gpu/drm/bridge/Makefile

@@ -11,6 +11,7 @@ obj-$(CONFIG_DRM_SII902X) += sii902x.o
 obj-$(CONFIG_DRM_SII9234) += sii9234.o
 obj-$(CONFIG_DRM_THINE_THC63LVD1024) += thc63lvd1024.o
 obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
+obj-$(CONFIG_DRM_TOSHIBA_TC358768) += tc358768.o
 obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
 obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
 obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o

+ 3 - 3
drivers/gpu/drm/bridge/dumb-vga-dac.c

@@ -234,7 +234,7 @@ static int dumb_vga_remove(struct platform_device *pdev)
  */
 static const struct drm_bridge_timings default_dac_timings = {
 	/* Timing specifications, datasheet page 7 */
-	.sampling_edge = DRM_BUS_FLAG_PIXDATA_POSEDGE,
+	.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
 	.setup_time_ps = 500,
 	.hold_time_ps = 1500,
 };
@@ -245,7 +245,7 @@ static const struct drm_bridge_timings default_dac_timings = {
  */
 static const struct drm_bridge_timings ti_ths8134_dac_timings = {
 	/* From timing diagram, datasheet page 9 */
-	.sampling_edge = DRM_BUS_FLAG_PIXDATA_POSEDGE,
+	.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
 	/* From datasheet, page 12 */
 	.setup_time_ps = 3000,
 	/* I guess this means latched input */
@@ -258,7 +258,7 @@ static const struct drm_bridge_timings ti_ths8134_dac_timings = {
  */
 static const struct drm_bridge_timings ti_ths8135_dac_timings = {
 	/* From timing diagram, datasheet page 14 */
-	.sampling_edge = DRM_BUS_FLAG_PIXDATA_POSEDGE,
+	.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
 	/* From datasheet, page 16 */
 	.setup_time_ps = 2000,
 	.hold_time_ps = 500,

+ 539 - 13
drivers/gpu/drm/bridge/sii902x.c

@@ -24,12 +24,16 @@
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/regmap.h>
+#include <linux/clk.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
 
+#include <sound/hdmi-codec.h>
+#include <dt-bindings/sound/sii902x-audio.h>
+
 #define SII902X_TPI_VIDEO_DATA			0x0
 
 #define SII902X_TPI_PIXEL_REPETITION		0x8
@@ -71,6 +75,80 @@
 #define SII902X_AVI_POWER_STATE_MSK		GENMASK(1, 0)
 #define SII902X_AVI_POWER_STATE_D(l)		((l) & SII902X_AVI_POWER_STATE_MSK)
 
+/* Audio  */
+#define SII902X_TPI_I2S_ENABLE_MAPPING_REG	0x1f
+#define SII902X_TPI_I2S_CONFIG_FIFO0			(0 << 0)
+#define SII902X_TPI_I2S_CONFIG_FIFO1			(1 << 0)
+#define SII902X_TPI_I2S_CONFIG_FIFO2			(2 << 0)
+#define SII902X_TPI_I2S_CONFIG_FIFO3			(3 << 0)
+#define SII902X_TPI_I2S_LEFT_RIGHT_SWAP			(1 << 2)
+#define SII902X_TPI_I2S_AUTO_DOWNSAMPLE			(1 << 3)
+#define SII902X_TPI_I2S_SELECT_SD0			(0 << 4)
+#define SII902X_TPI_I2S_SELECT_SD1			(1 << 4)
+#define SII902X_TPI_I2S_SELECT_SD2			(2 << 4)
+#define SII902X_TPI_I2S_SELECT_SD3			(3 << 4)
+#define SII902X_TPI_I2S_FIFO_ENABLE			(1 << 7)
+
+#define SII902X_TPI_I2S_INPUT_CONFIG_REG	0x20
+#define SII902X_TPI_I2S_FIRST_BIT_SHIFT_YES		(0 << 0)
+#define SII902X_TPI_I2S_FIRST_BIT_SHIFT_NO		(1 << 0)
+#define SII902X_TPI_I2S_SD_DIRECTION_MSB_FIRST		(0 << 1)
+#define SII902X_TPI_I2S_SD_DIRECTION_LSB_FIRST		(1 << 1)
+#define SII902X_TPI_I2S_SD_JUSTIFY_LEFT			(0 << 2)
+#define SII902X_TPI_I2S_SD_JUSTIFY_RIGHT		(1 << 2)
+#define SII902X_TPI_I2S_WS_POLARITY_LOW			(0 << 3)
+#define SII902X_TPI_I2S_WS_POLARITY_HIGH		(1 << 3)
+#define SII902X_TPI_I2S_MCLK_MULTIPLIER_128		(0 << 4)
+#define SII902X_TPI_I2S_MCLK_MULTIPLIER_256		(1 << 4)
+#define SII902X_TPI_I2S_MCLK_MULTIPLIER_384		(2 << 4)
+#define SII902X_TPI_I2S_MCLK_MULTIPLIER_512		(3 << 4)
+#define SII902X_TPI_I2S_MCLK_MULTIPLIER_768		(4 << 4)
+#define SII902X_TPI_I2S_MCLK_MULTIPLIER_1024		(5 << 4)
+#define SII902X_TPI_I2S_MCLK_MULTIPLIER_1152		(6 << 4)
+#define SII902X_TPI_I2S_MCLK_MULTIPLIER_192		(7 << 4)
+#define SII902X_TPI_I2S_SCK_EDGE_FALLING		(0 << 7)
+#define SII902X_TPI_I2S_SCK_EDGE_RISING			(1 << 7)
+
+#define SII902X_TPI_I2S_STRM_HDR_BASE	0x21
+#define SII902X_TPI_I2S_STRM_HDR_SIZE	5
+
+#define SII902X_TPI_AUDIO_CONFIG_BYTE2_REG	0x26
+#define SII902X_TPI_AUDIO_CODING_STREAM_HEADER		(0 << 0)
+#define SII902X_TPI_AUDIO_CODING_PCM			(1 << 0)
+#define SII902X_TPI_AUDIO_CODING_AC3			(2 << 0)
+#define SII902X_TPI_AUDIO_CODING_MPEG1			(3 << 0)
+#define SII902X_TPI_AUDIO_CODING_MP3			(4 << 0)
+#define SII902X_TPI_AUDIO_CODING_MPEG2			(5 << 0)
+#define SII902X_TPI_AUDIO_CODING_AAC			(6 << 0)
+#define SII902X_TPI_AUDIO_CODING_DTS			(7 << 0)
+#define SII902X_TPI_AUDIO_CODING_ATRAC			(8 << 0)
+#define SII902X_TPI_AUDIO_MUTE_DISABLE			(0 << 4)
+#define SII902X_TPI_AUDIO_MUTE_ENABLE			(1 << 4)
+#define SII902X_TPI_AUDIO_LAYOUT_2_CHANNELS		(0 << 5)
+#define SII902X_TPI_AUDIO_LAYOUT_8_CHANNELS		(1 << 5)
+#define SII902X_TPI_AUDIO_INTERFACE_DISABLE		(0 << 6)
+#define SII902X_TPI_AUDIO_INTERFACE_SPDIF		(1 << 6)
+#define SII902X_TPI_AUDIO_INTERFACE_I2S			(2 << 6)
+
+#define SII902X_TPI_AUDIO_CONFIG_BYTE3_REG	0x27
+#define SII902X_TPI_AUDIO_CHANNEL_STREAM		(0 << 0)
+#define SII902X_TPI_AUDIO_2_CHANNEL			(1 << 0)
+#define SII902X_TPI_AUDIO_8_CHANNEL			(7 << 0)
+#define SII902X_TPI_AUDIO_FREQ_STREAM			(0 << 3)
+#define SII902X_TPI_AUDIO_FREQ_32KHZ			(1 << 3)
+#define SII902X_TPI_AUDIO_FREQ_44KHZ			(2 << 3)
+#define SII902X_TPI_AUDIO_FREQ_48KHZ			(3 << 3)
+#define SII902X_TPI_AUDIO_FREQ_88KHZ			(4 << 3)
+#define SII902X_TPI_AUDIO_FREQ_96KHZ			(5 << 3)
+#define SII902X_TPI_AUDIO_FREQ_176KHZ			(6 << 3)
+#define SII902X_TPI_AUDIO_FREQ_192KHZ			(7 << 3)
+#define SII902X_TPI_AUDIO_SAMPLE_SIZE_STREAM		(0 << 6)
+#define SII902X_TPI_AUDIO_SAMPLE_SIZE_16		(1 << 6)
+#define SII902X_TPI_AUDIO_SAMPLE_SIZE_20		(2 << 6)
+#define SII902X_TPI_AUDIO_SAMPLE_SIZE_24		(3 << 6)
+
+#define SII902X_TPI_AUDIO_CONFIG_BYTE4_REG	0x28
+
 #define SII902X_INT_ENABLE			0x3c
 #define SII902X_INT_STATUS			0x3d
 #define SII902X_HOTPLUG_EVENT			BIT(0)
@@ -78,6 +156,16 @@
 
 #define SII902X_REG_TPI_RQB			0xc7
 
+/* Indirect internal register access */
+#define SII902X_IND_SET_PAGE			0xbc
+#define SII902X_IND_OFFSET			0xbd
+#define SII902X_IND_VALUE			0xbe
+
+#define SII902X_TPI_MISC_INFOFRAME_BASE		0xbf
+#define SII902X_TPI_MISC_INFOFRAME_END		0xde
+#define SII902X_TPI_MISC_INFOFRAME_SIZE	\
+	(SII902X_TPI_MISC_INFOFRAME_END - SII902X_TPI_MISC_INFOFRAME_BASE)
+
 #define SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS	500
 
 struct sii902x {
@@ -86,6 +174,14 @@ struct sii902x {
 	struct drm_bridge bridge;
 	struct drm_connector connector;
 	struct gpio_desc *reset_gpio;
+	bool use_custom_ddc_probe;
+	struct mutex mutex;
+	struct sii902x_audio {
+		struct platform_device *pdev;
+		u8 channels;
+		struct clk *mclk;
+		u32 i2s_fifo_routing[4];
+	} audio;
 };
 
 static inline struct sii902x *bridge_to_sii902x(struct drm_bridge *bridge)
@@ -117,8 +213,12 @@ sii902x_connector_detect(struct drm_connector *connector, bool force)
 	struct sii902x *sii902x = connector_to_sii902x(connector);
 	unsigned int status;
 
+	mutex_lock(&sii902x->mutex);
+
 	regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status);
 
+	mutex_unlock(&sii902x->mutex);
+
 	return (status & SII902X_PLUGGED_STATUS) ?
 	       connector_status_connected : connector_status_disconnected;
 }
@@ -132,6 +232,84 @@ static const struct drm_connector_funcs sii902x_connector_funcs = {
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
+/*
+ * This is a copy-paste from drm_do_probe_ddc_edid() with the extra
+ * sleep to make SiI902x pass trough DDC transfer more reliable.
+ */
+#define DDC_SEGMENT_ADDR 0x30
+static int
+sii902x_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len)
+{
+	struct i2c_adapter *adapter = data;
+	unsigned char start = block * EDID_LENGTH;
+	unsigned char segment = block >> 1;
+	unsigned char xfers = segment ? 3 : 2;
+	int ret, retries = 5;
+
+	/*
+	 * The core I2C driver will automatically retry the transfer if the
+	 * adapter reports EAGAIN. However, we find that bit-banging transfers
+	 * are susceptible to errors under a heavily loaded machine and
+	 * generate spurious NAKs and timeouts. Retrying the transfer
+	 * of the individual block a few times seems to overcome this.
+	 */
+	do {
+		struct i2c_msg msgs[] = {
+			{
+				.addr	= DDC_SEGMENT_ADDR,
+				.flags	= 0,
+				.len	= 1,
+				.buf	= &segment,
+			}, {
+				.addr	= DDC_ADDR,
+				.flags	= 0,
+				.len	= 1,
+				.buf	= &start,
+			}, {
+				.addr	= DDC_ADDR,
+				.flags	= I2C_M_RD,
+				.len	= len,
+				.buf	= buf,
+			}
+		};
+
+		/* This sleep helps k2g-evm DDC read to become reliable */
+		msleep(10);
+
+		/*
+		 * Avoid sending the segment addr to not upset non-compliant
+		 * DDC monitors.
+		 */
+		ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers);
+
+		if (ret == -ENXIO) {
+			DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n",
+					adapter->name);
+			break;
+		}
+	} while (ret != xfers && --retries);
+
+	return ret == xfers ? 0 : -1;
+}
+
+/*
+ * This is a simplified version of drm_get_edid(). It does not implement
+ * all the features - just the necessary parts - and it uses the above
+ * custom DDC read function. Tile group feature is not supported.
+ */
+static struct edid *sii902x_get_edid(struct drm_connector *connector,
+				     struct i2c_adapter *adapter)
+{
+	struct edid *edid;
+
+	if (connector->force == DRM_FORCE_OFF)
+		return NULL;
+
+	edid = drm_do_get_edid(connector, sii902x_do_probe_ddc_edid, adapter);
+
+	return edid;
+}
+
 static int sii902x_get_modes(struct drm_connector *connector)
 {
 	struct sii902x *sii902x = connector_to_sii902x(connector);
@@ -142,36 +320,46 @@ static int sii902x_get_modes(struct drm_connector *connector)
 	unsigned int retries;
 	unsigned int status;
 	struct edid *edid;
+	u8 output_mode = SII902X_SYS_CTRL_OUTPUT_DVI;
 	int num = 0;
 	int ret;
 
+	mutex_lock(&sii902x->mutex);
+
 	ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA,
 				 SII902X_SYS_CTRL_DDC_BUS_REQ,
 				 SII902X_SYS_CTRL_DDC_BUS_REQ);
 	if (ret)
-		return ret;
+		goto error_out;
 
 	timeout = jiffies +
 		  msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
 	do {
 		ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status);
 		if (ret)
-			return ret;
+			goto error_out;
 	} while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
 		 time_before(jiffies, timeout));
 
 	if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
 		dev_err(dev, "failed to acquire the i2c bus\n");
-		return -ETIMEDOUT;
+		ret = -ETIMEDOUT;
+		goto error_out;
 	}
 
 	ret = regmap_write(regmap, SII902X_SYS_CTRL_DATA, status);
 	if (ret)
-		return ret;
+		goto error_out;
 
-	edid = drm_get_edid(connector, sii902x->i2c->adapter);
+	if (sii902x->use_custom_ddc_probe)
+		edid = sii902x_get_edid(connector, sii902x->i2c->adapter);
+	else
+		edid = drm_get_edid(connector, sii902x->i2c->adapter);
 	drm_connector_update_edid_property(connector, edid);
 	if (edid) {
+	        if (drm_detect_hdmi_monitor(edid))
+			output_mode = SII902X_SYS_CTRL_OUTPUT_HDMI;
+
 		num = drm_add_edid_modes(connector, edid);
 		kfree(edid);
 	}
@@ -179,7 +367,7 @@ static int sii902x_get_modes(struct drm_connector *connector)
 	ret = drm_display_info_set_bus_formats(&connector->display_info,
 					       &bus_format, 1);
 	if (ret)
-		return ret;
+		goto error_out;
 
 	/*
 	 * Sometimes the I2C bus can stall after failure to use the
@@ -197,16 +385,17 @@ static int sii902x_get_modes(struct drm_connector *connector)
 
 	ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA,
 				 SII902X_SYS_CTRL_DDC_BUS_REQ |
-				 SII902X_SYS_CTRL_DDC_BUS_GRTD, 0);
+				 SII902X_SYS_CTRL_DDC_BUS_GRTD |
+				 SII902X_SYS_CTRL_OUTPUT_MODE, output_mode);
 	if (ret)
-		return ret;
+		goto error_out;
 
 	timeout = jiffies +
 		  msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
 	do {
 		ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status);
 		if (ret)
-			return ret;
+			goto error_out;
 	} while (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
 			   SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
 		 time_before(jiffies, timeout));
@@ -214,10 +403,16 @@ static int sii902x_get_modes(struct drm_connector *connector)
 	if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
 		      SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
 		dev_err(dev, "failed to release the i2c bus\n");
-		return -ETIMEDOUT;
+		ret = -ETIMEDOUT;
+		goto error_out;
 	}
 
-	return num;
+	ret = num;
+
+error_out:
+	mutex_unlock(&sii902x->mutex);
+
+	return ret;
 }
 
 static enum drm_mode_status sii902x_mode_valid(struct drm_connector *connector,
@@ -237,20 +432,28 @@ static void sii902x_bridge_disable(struct drm_bridge *bridge)
 {
 	struct sii902x *sii902x = bridge_to_sii902x(bridge);
 
+	mutex_lock(&sii902x->mutex);
+
 	regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,
 			   SII902X_SYS_CTRL_PWR_DWN,
 			   SII902X_SYS_CTRL_PWR_DWN);
+
+	mutex_unlock(&sii902x->mutex);
 }
 
 static void sii902x_bridge_enable(struct drm_bridge *bridge)
 {
 	struct sii902x *sii902x = bridge_to_sii902x(bridge);
 
+	mutex_lock(&sii902x->mutex);
+
 	regmap_update_bits(sii902x->regmap, SII902X_PWR_STATE_CTRL,
 			   SII902X_AVI_POWER_STATE_MSK,
 			   SII902X_AVI_POWER_STATE_D(0));
 	regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,
 			   SII902X_SYS_CTRL_PWR_DWN, 0);
+
+	mutex_unlock(&sii902x->mutex);
 }
 
 static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
@@ -261,10 +464,11 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
 	struct regmap *regmap = sii902x->regmap;
 	u8 buf[HDMI_INFOFRAME_SIZE(AVI)];
 	struct hdmi_avi_infoframe frame;
+	u16 pixel_clock_10kHz = adj->clock / 10;
 	int ret;
 
-	buf[0] = adj->clock;
-	buf[1] = adj->clock >> 8;
+	buf[0] = pixel_clock_10kHz & 0xFF;
+	buf[1] = pixel_clock_10kHz >> 8;
 	buf[2] = adj->vrefresh;
 	buf[3] = 0x00;
 	buf[4] = adj->hdisplay;
@@ -276,6 +480,8 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
 	buf[9] = SII902X_TPI_AVI_INPUT_RANGE_AUTO |
 		 SII902X_TPI_AVI_INPUT_COLORSPACE_RGB;
 
+	mutex_lock(&sii902x->mutex);
+
 	ret = regmap_bulk_write(regmap, SII902X_TPI_VIDEO_DATA, buf, 10);
 	if (ret)
 		return;
@@ -296,6 +502,8 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
 	regmap_bulk_write(regmap, SII902X_TPI_AVI_INFOFRAME,
 			  buf + HDMI_INFOFRAME_HEADER_SIZE - 1,
 			  HDMI_AVI_INFOFRAME_SIZE + 1);
+
+	mutex_unlock(&sii902x->mutex);
 }
 
 static int sii902x_bridge_attach(struct drm_bridge *bridge)
@@ -336,6 +544,308 @@ static const struct drm_bridge_funcs sii902x_bridge_funcs = {
 	.enable = sii902x_bridge_enable,
 };
 
+static int sii902x_mute(struct sii902x *sii902x, int mute)
+{
+	u8 channel_layout = sii902x->audio.channels > 2 ?
+		SII902X_TPI_AUDIO_LAYOUT_8_CHANNELS :
+		SII902X_TPI_AUDIO_LAYOUT_2_CHANNELS;
+	struct device *dev = &sii902x->i2c->dev;
+	int ret;
+
+	if (mute) {
+		dev_dbg(dev, "%s: Muted (%d)\n", __func__, mute);
+		ret = regmap_write(sii902x->regmap,
+				   SII902X_TPI_AUDIO_CONFIG_BYTE2_REG,
+				   SII902X_TPI_AUDIO_INTERFACE_I2S |
+				   channel_layout |
+				   SII902X_TPI_AUDIO_MUTE_ENABLE |
+				   SII902X_TPI_AUDIO_CODING_PCM);
+	} else {
+		dev_dbg(dev, "%s: Unmuted (%d)\n", __func__, mute);
+		ret = regmap_write(sii902x->regmap,
+				   SII902X_TPI_AUDIO_CONFIG_BYTE2_REG,
+				   SII902X_TPI_AUDIO_INTERFACE_I2S |
+				   channel_layout |
+				   SII902X_TPI_AUDIO_MUTE_DISABLE |
+				   SII902X_TPI_AUDIO_CODING_PCM);
+	}
+	return ret;
+}
+
+static const unsigned int sii902x_mclk_div_table[] = {
+	128, 256, 384, 512, 768, 1024, 1152, 192 };
+
+static int sii902x_select_mclk_div(u8 *i2s_config_reg, unsigned int rate,
+				   unsigned int mclk)
+{
+	int distance = 100000;
+	u8 i, nearest = 0;
+
+	for (i = 0; i < ARRAY_SIZE(sii902x_mclk_div_table); i++) {
+		unsigned int div = mclk / rate;
+
+		if (abs(div - sii902x_mclk_div_table[i]) < distance) {
+			nearest = i;
+			distance = abs(div - sii902x_mclk_div_table[i]);
+		}
+		if (div == sii902x_mclk_div_table[i])
+			break;
+	}
+
+	*i2s_config_reg |= nearest << 4;
+
+	if (i == ARRAY_SIZE(sii902x_mclk_div_table))
+		return sii902x_mclk_div_table[nearest];
+
+	return 0;
+}
+
+static int sii902x_audio_hw_params(struct device *dev, void *data,
+				   struct hdmi_codec_daifmt *daifmt,
+				   struct hdmi_codec_params *params)
+{
+	struct sii902x *sii902x = dev_get_drvdata(dev);
+	u8 i2s_config_reg = SII902X_TPI_I2S_SD_DIRECTION_MSB_FIRST;
+	u8 config_byte3_reg = SII902X_TPI_AUDIO_CHANNEL_STREAM;
+	u8 infoframe_buf[HDMI_INFOFRAME_SIZE(AUDIO)];
+	unsigned long mclk_rate;
+	int i, ret;
+
+	if (daifmt->bit_clk_master || daifmt->frame_clk_master) {
+		dev_dbg(dev, "%s: I2S master mode not supported\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (daifmt->fmt) {
+	case HDMI_I2S:
+		i2s_config_reg |= SII902X_TPI_I2S_FIRST_BIT_SHIFT_YES |
+			SII902X_TPI_I2S_SD_JUSTIFY_LEFT;
+		break;
+	case HDMI_RIGHT_J:
+		i2s_config_reg |= SII902X_TPI_I2S_SD_JUSTIFY_RIGHT;
+		break;
+	case HDMI_LEFT_J:
+		i2s_config_reg |= SII902X_TPI_I2S_SD_JUSTIFY_LEFT;
+		break;
+	default:
+		dev_dbg(dev, "%s: Unsupported i2s format %u\n", __func__,
+			daifmt->fmt);
+		return -EINVAL;
+	}
+
+	if (daifmt->bit_clk_inv)
+		i2s_config_reg |= SII902X_TPI_I2S_SCK_EDGE_FALLING;
+	else
+		i2s_config_reg |= SII902X_TPI_I2S_SCK_EDGE_RISING;
+
+	if (daifmt->frame_clk_inv)
+		i2s_config_reg |= SII902X_TPI_I2S_WS_POLARITY_LOW;
+	else
+		i2s_config_reg |= SII902X_TPI_I2S_WS_POLARITY_HIGH;
+
+	switch (params->sample_width) {
+	case 16:
+		config_byte3_reg |= SII902X_TPI_AUDIO_SAMPLE_SIZE_16;
+		break;
+	case 20:
+		config_byte3_reg |= SII902X_TPI_AUDIO_SAMPLE_SIZE_20;
+		break;
+	case 24:
+	case 32:
+		config_byte3_reg |= SII902X_TPI_AUDIO_SAMPLE_SIZE_24;
+		break;
+	default:
+		dev_err(dev, "%s: Unsupported sample width %u\n", __func__,
+			params->sample_width);
+		return -EINVAL;
+	};
+
+	mclk_rate = clk_get_rate(sii902x->audio.mclk);
+
+	ret = sii902x_select_mclk_div(&i2s_config_reg, params->sample_rate,
+				      mclk_rate);
+	if (ret)
+		dev_dbg(dev, "Inaccurate reference clock (%ld/%d != %u)\n",
+			mclk_rate, ret, params->sample_rate);
+
+	sii902x->audio.channels = params->channels;
+
+	ret = clk_prepare_enable(sii902x->audio.mclk);
+	if (ret) {
+		dev_err(dev, "Enabling mclk failed: %d\n", ret);
+		return ret;
+	}
+
+	mutex_lock(&sii902x->mutex);
+
+	ret = sii902x_mute(sii902x, true);
+	if (ret < 0)
+		goto out;
+
+	ret = regmap_write(sii902x->regmap, SII902X_TPI_I2S_INPUT_CONFIG_REG,
+			   i2s_config_reg);
+	if (ret)
+		goto out;
+
+	for (i = 0; i < ARRAY_SIZE(sii902x->audio.i2s_fifo_routing); i++)
+		regmap_write(sii902x->regmap,
+			     SII902X_TPI_I2S_ENABLE_MAPPING_REG,
+			     sii902x->audio.i2s_fifo_routing[i]);
+
+	ret = regmap_write(sii902x->regmap, SII902X_TPI_AUDIO_CONFIG_BYTE3_REG,
+			   config_byte3_reg);
+	if (ret)
+		goto out;
+
+	ret = regmap_bulk_write(sii902x->regmap, SII902X_TPI_I2S_STRM_HDR_BASE,
+				params->iec.status,
+				min((size_t) SII902X_TPI_I2S_STRM_HDR_SIZE,
+				    sizeof(params->iec.status)));
+	if (ret)
+		goto out;
+
+	ret = hdmi_audio_infoframe_pack(&params->cea, infoframe_buf,
+					sizeof(infoframe_buf));
+	if (ret < 0) {
+		dev_err(dev, "%s: Failed to pack audio infoframe: %d\n",
+			__func__, ret);
+		goto out;
+	}
+
+	ret = regmap_bulk_write(sii902x->regmap,
+				SII902X_TPI_MISC_INFOFRAME_BASE,
+				infoframe_buf,
+				min(ret, SII902X_TPI_MISC_INFOFRAME_SIZE));
+	if (ret)
+		goto out;
+
+	/* Decode Level 0 Packets */
+	ret = regmap_write(sii902x->regmap, SII902X_IND_SET_PAGE, 0x02);
+	if (ret)
+		goto out;
+
+	ret = regmap_write(sii902x->regmap, SII902X_IND_OFFSET, 0x24);
+	if (ret)
+		goto out;
+
+	ret = regmap_write(sii902x->regmap, SII902X_IND_VALUE, 0x02);
+	if (ret)
+		goto out;
+
+	dev_dbg(dev, "%s: hdmi audio enabled\n", __func__);
+out:
+	mutex_unlock(&sii902x->mutex);
+
+	if (ret) {
+		clk_disable_unprepare(sii902x->audio.mclk);
+		dev_err(dev, "%s: hdmi audio enable failed: %d\n", __func__,
+			ret);
+	}
+
+	return ret;
+}
+
+static void sii902x_audio_shutdown(struct device *dev, void *data)
+{
+	struct sii902x *sii902x = dev_get_drvdata(dev);
+
+	mutex_lock(&sii902x->mutex);
+
+	regmap_write(sii902x->regmap, SII902X_TPI_AUDIO_CONFIG_BYTE2_REG,
+		     SII902X_TPI_AUDIO_INTERFACE_DISABLE);
+
+	mutex_unlock(&sii902x->mutex);
+
+	clk_disable_unprepare(sii902x->audio.mclk);
+}
+
+int sii902x_audio_digital_mute(struct device *dev, void *data, bool enable)
+{
+	struct sii902x *sii902x = dev_get_drvdata(dev);
+
+	mutex_lock(&sii902x->mutex);
+
+	sii902x_mute(sii902x, enable);
+
+	mutex_unlock(&sii902x->mutex);
+
+	return 0;
+}
+
+static int sii902x_audio_get_eld(struct device *dev, void *data,
+				 uint8_t *buf, size_t len)
+{
+	struct sii902x *sii902x = dev_get_drvdata(dev);
+
+	mutex_lock(&sii902x->mutex);
+
+	memcpy(buf, sii902x->connector.eld,
+	       min(sizeof(sii902x->connector.eld), len));
+
+	mutex_unlock(&sii902x->mutex);
+
+	return 0;
+}
+
+static const struct hdmi_codec_ops sii902x_audio_codec_ops = {
+	.hw_params = sii902x_audio_hw_params,
+	.audio_shutdown = sii902x_audio_shutdown,
+	.digital_mute = sii902x_audio_digital_mute,
+	.get_eld = sii902x_audio_get_eld,
+};
+
+static int sii902x_audio_codec_init(struct sii902x *sii902x,
+				    struct device *dev)
+{
+	static const u8 i2s_fifo_defaults[] = {
+		SII902X_TPI_I2S_CONFIG_FIFO0,
+		SII902X_TPI_I2S_CONFIG_FIFO1,
+		SII902X_TPI_I2S_CONFIG_FIFO2,
+		SII902X_TPI_I2S_CONFIG_FIFO3,
+	};
+	struct hdmi_codec_pdata codec_data = {
+		.ops = &sii902x_audio_codec_ops,
+		.i2s = 1, /* Only i2s support for now. */
+		.spdif = 0,
+		.max_i2s_channels = 0,
+	};
+	int ret, i;
+
+	ret = of_property_read_u32_array(dev->of_node, "i2s-fifo-routing",
+					 sii902x->audio.i2s_fifo_routing,
+					 ARRAY_SIZE(sii902x->audio.i2s_fifo_routing));
+
+	if (ret != 0) {
+		if (ret == -EINVAL)
+			dev_dbg(dev, "%s: No \"i2s-fifo-routing\", no audio\n",
+			__func__);
+		else
+			dev_err(dev,
+				"%s: Error gettin \"i2s-fifo-routing\": %d\n",
+				__func__, ret);
+		return 0;
+	}
+
+	sii902x->audio.mclk = devm_clk_get(dev, NULL);
+	if (IS_ERR(sii902x->audio.mclk)) {
+		dev_err(dev, "%s: No clock (audio mclk) found: %ld\n",
+			__func__, PTR_ERR(sii902x->audio.mclk));
+		return 0;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(sii902x->audio.i2s_fifo_routing); i++) {
+		if (sii902x->audio.i2s_fifo_routing[i] | ENABLE_BIT)
+			codec_data.max_i2s_channels += 2;
+		sii902x->audio.i2s_fifo_routing[i] |= i2s_fifo_defaults[i];
+	}
+
+	sii902x->audio.pdev = platform_device_register_data(
+		dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+		&codec_data, sizeof(codec_data));
+
+	return PTR_ERR_OR_ZERO(sii902x->audio.pdev);
+}
+
 static const struct regmap_range sii902x_volatile_ranges[] = {
 	{ .range_min = 0, .range_max = 0xff },
 };
@@ -348,6 +858,7 @@ static const struct regmap_access_table sii902x_volatile_table = {
 static const struct regmap_config sii902x_regmap_config = {
 	.reg_bits = 8,
 	.val_bits = 8,
+	.max_register = SII902X_TPI_MISC_INFOFRAME_END,
 	.volatile_table = &sii902x_volatile_table,
 	.cache_type = REGCACHE_NONE,
 };
@@ -366,6 +877,12 @@ static irqreturn_t sii902x_interrupt(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+static const struct drm_bridge_timings default_sii902x_timings = {
+	.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE
+		 | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE
+		 | DRM_BUS_FLAG_DE_HIGH,
+};
+
 static int sii902x_probe(struct i2c_client *client,
 			 const struct i2c_device_id *id)
 {
@@ -392,6 +909,9 @@ static int sii902x_probe(struct i2c_client *client,
 		return PTR_ERR(sii902x->reset_gpio);
 	}
 
+	/* protect audio and video functions from each other */
+	mutex_init(&sii902x->mutex);
+
 	sii902x_reset(sii902x);
 
 	ret = regmap_write(sii902x->regmap, SII902X_REG_TPI_RQB, 0x0);
@@ -427,10 +947,16 @@ static int sii902x_probe(struct i2c_client *client,
 			return ret;
 	}
 
+	sii902x->use_custom_ddc_probe =
+		of_property_read_bool(dev->of_node, "use-custom-ddc-probe");
+
 	sii902x->bridge.funcs = &sii902x_bridge_funcs;
 	sii902x->bridge.of_node = dev->of_node;
+	sii902x->bridge.timings = &default_sii902x_timings;
 	drm_bridge_add(&sii902x->bridge);
 
+	sii902x_audio_codec_init(sii902x, dev);
+
 	i2c_set_clientdata(client, sii902x);
 
 	return 0;

+ 917 - 0
drivers/gpu/drm/bridge/tc358768.c

@@ -0,0 +1,917 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
+ *  Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_mipi_dsi.h>
+#include <video/mipi_display.h>
+#include <video/videomode.h>
+
+/* Global (16-bit addressable) */
+#define TC358768_CHIPID			0x0000
+#define TC358768_SYSCTL			0x0002
+#define TC358768_CONFCTL		0x0004
+#define TC358768_VSDLY			0x0006
+#define TC358768_DATAFMT		0x0008
+#define TC358768_GPIOEN			0x000E
+#define TC358768_GPIODIR		0x0010
+#define TC358768_GPIOIN			0x0012
+#define TC358768_GPIOOUT		0x0014
+#define TC358768_PLLCTL0		0x0016
+#define TC358768_PLLCTL1		0x0018
+#define TC358768_CMDBYTE		0x0022
+#define TC358768_PP_MISC		0x0032
+#define TC358768_DSITX_DT		0x0050
+#define TC358768_FIFOSTATUS		0x00F8
+
+/* Debug (16-bit addressable) */
+#define TC358768_VBUFCTRL		0x00E0
+#define TC358768_DBG_WIDTH		0x00E2
+#define TC358768_DBG_VBLANK		0x00E4
+#define TC358768_DBG_DATA		0x00E8
+
+/* TX PHY (32-bit addressable) */
+#define TC358768_CLW_DPHYCONTTX		0x0100
+#define TC358768_D0W_DPHYCONTTX		0x0104
+#define TC358768_D1W_DPHYCONTTX		0x0108
+#define TC358768_D2W_DPHYCONTTX		0x010C
+#define TC358768_D3W_DPHYCONTTX		0x0110
+#define TC358768_CLW_CNTRL		0x0140
+#define TC358768_D0W_CNTRL		0x0144
+#define TC358768_D1W_CNTRL		0x0148
+#define TC358768_D2W_CNTRL		0x014C
+#define TC358768_D3W_CNTRL		0x0150
+
+/* TX PPI (32-bit addressable) */
+#define TC358768_STARTCNTRL		0x0204
+#define TC358768_DSITXSTATUS		0x0208
+#define TC358768_LINEINITCNT		0x0210
+#define TC358768_LPTXTIMECNT		0x0214
+#define TC358768_TCLK_HEADERCNT		0x0218
+#define TC358768_TCLK_TRAILCNT		0x021C
+#define TC358768_THS_HEADERCNT		0x0220
+#define TC358768_TWAKEUP		0x0224
+#define TC358768_TCLK_POSTCNT		0x0228
+#define TC358768_THS_TRAILCNT		0x022C
+#define TC358768_HSTXVREGCNT		0x0230
+#define TC358768_HSTXVREGEN		0x0234
+#define TC358768_TXOPTIONCNTRL		0x0238
+#define TC358768_BTACNTRL1		0x023C
+
+/* TX CTRL (32-bit addressable) */
+#define TC358768_DSI_CONTROL		0x040C
+#define TC358768_DSI_STATUS		0x0410
+#define TC358768_DSI_INT		0x0414
+#define TC358768_DSI_INT_ENA		0x0418
+#define TC358768_DSICMD_RDFIFO		0x0430
+#define TC358768_DSI_ACKERR		0x0434
+#define TC358768_DSI_ACKERR_INTENA	0x0438
+#define TC358768_DSI_ACKERR_HALT	0x043c
+#define TC358768_DSI_RXERR		0x0440
+#define TC358768_DSI_RXERR_INTENA	0x0444
+#define TC358768_DSI_RXERR_HALT		0x0448
+#define TC358768_DSI_ERR		0x044C
+#define TC358768_DSI_ERR_INTENA		0x0450
+#define TC358768_DSI_ERR_HALT		0x0454
+#define TC358768_DSI_CONFW		0x0500
+#define TC358768_DSI_LPCMD		0x0500
+#define TC358768_DSI_RESET		0x0504
+#define TC358768_DSI_INT_CLR		0x050C
+#define TC358768_DSI_START		0x0518
+
+/* DSITX CTRL (16-bit addressable) */
+#define TC358768_DSICMD_TX		0x0600
+#define TC358768_DSICMD_TYPE		0x0602
+#define TC358768_DSICMD_WC		0x0604
+#define TC358768_DSICMD_WD0		0x0610
+#define TC358768_DSICMD_WD1		0x0612
+#define TC358768_DSICMD_WD2		0x0614
+#define TC358768_DSICMD_WD3		0x0616
+#define TC358768_DSI_EVENT		0x0620
+#define TC358768_DSI_VSW		0x0622
+#define TC358768_DSI_VBPR		0x0624
+#define TC358768_DSI_VACT		0x0626
+#define TC358768_DSI_HSW		0x0628
+#define TC358768_DSI_HBPR		0x062A
+#define TC358768_DSI_HACT		0x062C
+
+struct tc358768_dsi_output {
+	struct mipi_dsi_device *dev;
+	struct drm_panel *panel;
+	struct drm_bridge *bridge;
+};
+
+struct tc358768_priv {
+	struct device *dev;
+	struct regmap *regmap;
+	struct gpio_desc *reset_gpio;
+	int enabled;
+	struct clk *refclk;
+
+	struct mipi_dsi_host dsi_host;
+	struct drm_bridge bridge;
+	struct tc358768_dsi_output output;
+
+	const struct drm_display_mode *mode;
+	u32 pd_lines; /* number of Parallel Port Input Data Lines */
+	u32 dsi_lanes; /* number of DSI Lanes */
+
+	u32 fbd;
+	u32 prd;
+	u32 frs;
+
+	u32 dsiclk; /* pll_clk / 2 */
+};
+
+static inline struct tc358768_priv *dsi_host_to_tc358768(struct mipi_dsi_host
+							 *host)
+{
+	return container_of(host, struct tc358768_priv, dsi_host);
+}
+
+static inline struct tc358768_priv *bridge_to_tc358768(struct drm_bridge
+						       *bridge)
+{
+	return container_of(bridge, struct tc358768_priv, bridge);
+}
+
+static int tc358768_write(struct tc358768_priv *priv, u32 reg, u32 val)
+{
+	size_t count = 2;
+
+	/* 16-bit register? */
+	if (reg < 0x100 || reg >= 0x600)
+		count = 1;
+
+	return regmap_bulk_write(priv->regmap, reg, &val, count);
+}
+
+static int tc358768_read(struct tc358768_priv *priv, u32 reg, u32 *val)
+{
+	size_t count = 2;
+
+	/* 16-bit register? */
+	if (reg < 0x100 || reg >= 0x600) {
+		*val = 0;
+		count = 1;
+	}
+
+	return regmap_bulk_read(priv->regmap, reg, val, count);
+}
+
+static int tc358768_update_bits(struct tc358768_priv *priv, u32 reg, u32 mask,
+				u32 val)
+{
+	int ret;
+	u32 tmp, orig;
+
+	ret = tc358768_read(priv, reg, &orig);
+	if (ret != 0)
+		return ret;
+
+	tmp = orig & ~mask;
+	tmp |= val & mask;
+
+	if (tmp != orig)
+		ret = tc358768_write(priv, reg, tmp);
+
+	return ret;
+}
+
+static void tc358768_sw_reset(struct tc358768_priv *priv)
+{
+	/* Assert Reset */
+	tc358768_write(priv, TC358768_SYSCTL, 1);
+	/* Release Reset, Exit Sleep */
+	tc358768_write(priv, TC358768_SYSCTL, 0);
+}
+
+static void tc358768_hw_enable(struct tc358768_priv *priv, bool enable)
+{
+	if (!priv->reset_gpio)
+		return;
+
+	if (enable && priv->enabled++ > 0)
+		return;
+
+	if (!enable && --priv->enabled != 0)
+		return;
+
+	gpiod_set_value_cansleep(priv->reset_gpio, enable);
+	regcache_cache_only(priv->regmap, !enable);
+	if (enable) {
+		/* wait for encoder clocks to stabilize */
+		usleep_range(1000, 2000);
+
+		regcache_sync(priv->regmap);
+	}
+}
+
+static u32 tc358768_pll_to_pclk(struct tc358768_priv *priv, u32 pll_clk)
+{
+	return (u32)div_u64((u64)pll_clk * priv->dsi_lanes, priv->pd_lines);
+}
+
+static u32 tc358768_pclk_to_pll(struct tc358768_priv *priv, u32 pclk)
+{
+	return (u32)div_u64((u64)pclk * priv->pd_lines, priv->dsi_lanes);
+}
+
+static int tc358768_calc_pll(struct tc358768_priv *priv)
+{
+	const u32 frs_limits[] = {
+		1000000000,
+		500000000,
+		250000000,
+		125000000,
+		62500000
+	};
+	unsigned long refclk;
+	u32 prd, target_pll, i, max_pll, min_pll;
+	u32 frs, best_diff, best_pll, best_prd, best_fbd;
+
+	target_pll = tc358768_pclk_to_pll(priv, priv->mode->clock * 1000);
+
+	/* pll_clk = RefClk * [(FBD + 1)/ (PRD + 1)] * [1 / (2^FRS)] */
+
+	frs = UINT_MAX;
+
+	for (i = 0; i < ARRAY_SIZE(frs_limits) - 1; i++) {
+		if (target_pll < frs_limits[i] &&
+		    target_pll >= frs_limits[i + 1]) {
+			frs = i;
+			max_pll = frs_limits[i];
+			min_pll = frs_limits[i + 1];
+			break;
+		}
+	}
+
+	if (frs == UINT_MAX)
+		return -EINVAL;
+
+	refclk = clk_get_rate(priv->refclk);
+
+	best_diff = UINT_MAX;
+	best_pll = 0;
+	best_prd = 0;
+	best_fbd = 0;
+
+	for (prd = 0; prd < 16; ++prd) {
+		u32 divisor = (prd + 1) * (1 << frs);
+		u32 fbd;
+
+		for (fbd = 0; fbd < 512; ++fbd) {
+			u32 pll, diff;
+
+			pll = (u32)div_u64((u64)refclk * (fbd + 1), divisor);
+
+			if (pll >= max_pll || pll < min_pll)
+				continue;
+
+			diff = max(pll, target_pll) - min(pll, target_pll);
+
+			if (diff < best_diff) {
+				best_diff = diff;
+				best_pll = pll;
+				best_prd = prd;
+				best_fbd = fbd;
+			}
+
+			if (best_diff == 0)
+				break;
+		}
+
+		if (best_diff == 0)
+			break;
+	}
+
+	if (best_diff == UINT_MAX) {
+		dev_err(priv->dev, "could not find suitable PLL setup\n");
+		return -EINVAL;
+	}
+
+	priv->fbd = best_fbd;
+	priv->prd = best_prd;
+	priv->frs = frs;
+	priv->dsiclk = best_pll / 2;
+
+	return 0;
+}
+
+static int tc358768_dsi_host_attach(struct mipi_dsi_host *host,
+				    struct mipi_dsi_device *dev)
+{
+	struct tc358768_priv *priv = dsi_host_to_tc358768(host);
+	struct drm_bridge *bridge;
+	struct drm_panel *panel;
+	struct device_node *ep;
+	int ret;
+
+	if (dev->lanes > 4) {
+		dev_err(priv->dev, "unsupported number of data lanes(%u)\n",
+			dev->lanes);
+		return -EINVAL;
+	}
+
+	/*
+	 * tc358768 supports both Video and Pulse mode, but the driver only
+	 * implements Video (event) mode currently
+	 */
+	if (!(dev->mode_flags & MIPI_DSI_MODE_VIDEO)) {
+		dev_err(priv->dev, "Only MIPI_DSI_MODE_VIDEO is supported\n");
+		return -ENOTSUPP;
+	}
+
+	/*
+	 * tc358768 supports RGB888, RGB666, RGB666_PACKED and RGB565, but only
+	 * RGB888 is verified.
+	 */
+	if (dev->format != MIPI_DSI_FMT_RGB888) {
+		dev_warn(priv->dev, "Only MIPI_DSI_FMT_RGB888 tested!\n");
+		return -ENOTSUPP;
+	}
+
+	ret = drm_of_find_panel_or_bridge(host->dev->of_node, 1, 0, &panel,
+					  &bridge);
+	if (ret)
+		return ret;
+
+	if (panel) {
+		bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DSI);
+		if (IS_ERR(bridge))
+			return PTR_ERR(bridge);
+	}
+
+	priv->output.dev = dev;
+	priv->output.bridge = bridge;
+	priv->output.panel = panel;
+
+	priv->dsi_lanes = dev->lanes;
+
+	/* get input ep (port0/endpoint0) */
+	ret = -EINVAL;
+	ep = of_graph_get_endpoint_by_regs(host->dev->of_node, 0, 0);
+	if (ep) {
+		ret = of_property_read_u32(ep, "data-lines", &priv->pd_lines);
+
+		of_node_put(ep);
+	}
+
+	if (ret)
+		priv->pd_lines = mipi_dsi_pixel_format_to_bpp(dev->format);
+
+	drm_bridge_add(&priv->bridge);
+
+	return 0;
+}
+
+static int tc358768_dsi_host_detach(struct mipi_dsi_host *host,
+				    struct mipi_dsi_device *dev)
+{
+	struct tc358768_priv *priv = dsi_host_to_tc358768(host);
+
+	drm_bridge_remove(&priv->bridge);
+	if (priv->output.panel)
+		drm_panel_bridge_remove(priv->output.bridge);
+
+	return 0;
+}
+
+static ssize_t tc358768_dsi_host_transfer(struct mipi_dsi_host *host,
+					  const struct mipi_dsi_msg *msg)
+{
+	struct tc358768_priv *priv = dsi_host_to_tc358768(host);
+	struct mipi_dsi_packet packet;
+	int ret;
+
+	if (msg->rx_len) {
+		dev_warn(priv->dev, "MIPI rx is not supported\n");
+		return -ENOTSUPP;
+	}
+
+	if (msg->tx_len > 8) {
+		dev_warn(priv->dev, "Maximum 8 byte MIPI tx is supported\n");
+		return -ENOTSUPP;
+	}
+
+	ret = mipi_dsi_create_packet(&packet, msg);
+	if (ret)
+		return ret;
+
+	tc358768_hw_enable(priv, true);
+
+	if (mipi_dsi_packet_format_is_short(msg->type)) {
+		tc358768_write(priv, TC358768_DSICMD_TYPE,
+			       (0x10 << 8) | (packet.header[0] & 0x3f));
+		tc358768_write(priv, TC358768_DSICMD_WC, 0);
+		tc358768_write(priv, TC358768_DSICMD_WD0,
+			       (packet.header[2] << 8) | packet.header[1]);
+	} else {
+		int i, j;
+
+		tc358768_write(priv, TC358768_DSICMD_TYPE,
+			       (0x40 << 8) | (packet.header[0] & 0x3f));
+		tc358768_write(priv, TC358768_DSICMD_WC, packet.payload_length);
+		for (i = 0; i < packet.payload_length; i += 2) {
+			u16 val = 0;
+
+			for (j = 0; j < 2 && i + j < packet.payload_length; j++)
+				val |= packet.payload[i + j] << (8 * j);
+
+			tc358768_write(priv, TC358768_DSICMD_WD0 + i, val);
+		}
+	}
+
+	/* start transfer */
+	tc358768_write(priv, TC358768_DSICMD_TX, 1);
+
+	tc358768_hw_enable(priv, false);
+
+	return 0;
+}
+
+static const struct mipi_dsi_host_ops tc358768_dsi_host_ops = {
+	.attach = tc358768_dsi_host_attach,
+	.detach = tc358768_dsi_host_detach,
+	.transfer = tc358768_dsi_host_transfer,
+};
+
+static int tc358768_bridge_attach(struct drm_bridge *bridge)
+{
+	struct tc358768_priv *priv = bridge_to_tc358768(bridge);
+
+	if (!drm_core_check_feature(bridge->dev, DRIVER_ATOMIC)) {
+		dev_err(priv->dev, "needs atomic updates support\n");
+		return -ENOTSUPP;
+	}
+
+	return drm_bridge_attach(bridge->encoder, priv->output.bridge, bridge);
+}
+
+static enum drm_mode_status
+tc358768_bridge_mode_valid(struct drm_bridge *bridge,
+			   const struct drm_display_mode *mode)
+{
+	struct tc358768_priv *priv = bridge_to_tc358768(bridge);
+
+	priv->mode = mode;
+
+	if (tc358768_calc_pll(priv))
+		return MODE_CLOCK_RANGE;
+
+	return MODE_OK;
+}
+
+static void tc358768_bridge_disable(struct drm_bridge *bridge)
+{
+	struct tc358768_priv *priv = bridge_to_tc358768(bridge);
+
+	/* set FrmStop */
+	tc358768_update_bits(priv, TC358768_PP_MISC, BIT(15), BIT(15));
+
+	/* wait at least for one frame */
+	msleep(50);
+
+	/* clear PP_en */
+	tc358768_update_bits(priv, TC358768_CONFCTL, BIT(6), 0);
+
+	/* set RstPtr */
+	tc358768_update_bits(priv, TC358768_PP_MISC, BIT(14), BIT(14));
+
+	tc358768_hw_enable(priv, false);
+}
+
+static void tc358768_setup_pll(struct tc358768_priv *priv)
+{
+	u32 fbd, prd, frs;
+
+	fbd = priv->fbd;
+	prd = priv->prd;
+	frs = priv->frs;
+
+	dev_dbg(priv->dev, "PLL: refclk %lu, fbd %u, prd %u, frs %u\n",
+		clk_get_rate(priv->refclk), fbd, prd, frs);
+	dev_dbg(priv->dev, "PLL: pll_clk: %u, DSIClk %u, DSIByteClk %u\n",
+		priv->dsiclk * 2, priv->dsiclk, priv->dsiclk / 4);
+	dev_dbg(priv->dev, "PLL: pclk %u (panel: %u)\n",
+		tc358768_pll_to_pclk(priv, priv->dsiclk * 2),
+		priv->mode->clock * 1000);
+
+	/* PRD[15:12] FBD[8:0] */
+	tc358768_write(priv, TC358768_PLLCTL0, (prd << 12) | fbd);
+
+	/* FRS[11:10] LBWS[9:8] CKEN[4] RESETB[1] EN[0] */
+	tc358768_write(priv, TC358768_PLLCTL1,
+		       (frs << 10) | (0x2 << 8) | BIT(1) | BIT(0));
+
+	/* wait for lock */
+	usleep_range(1000, 2000);
+
+	/* FRS[11:10] LBWS[9:8] CKEN[4] PLL_CKEN[4] RESETB[1] EN[0] */
+	tc358768_write(priv, TC358768_PLLCTL1,
+		       (frs << 10) | (0x2 << 8) | BIT(4) | BIT(1) | BIT(0));
+}
+
+#define TC358768_PRECISION	1000
+static u32 tc358768_ns_to_cnt(u32 ns, u32 period_nsk)
+{
+	return (ns * TC358768_PRECISION + period_nsk) / period_nsk;
+}
+
+static u32 tc358768_to_ns(u32 nsk)
+{
+	return (nsk / TC358768_PRECISION);
+}
+
+static void tc358768_bridge_enable(struct drm_bridge *bridge)
+{
+	struct tc358768_priv *priv = bridge_to_tc358768(bridge);
+	const struct drm_display_mode *mode = priv->mode;
+	struct mipi_dsi_device *dsi_dev = priv->output.dev;
+	u32 val, val2, lptxcnt, hact, data_type;
+	u32 dsiclk = priv->dsiclk;
+	u32 dsibclk = dsiclk / 4;
+	u32 dsibclk_nsk, dsiclk_nsk, ui_nsk, phy_delay_nsk;
+	int i;
+
+	tc358768_hw_enable(priv, true);
+
+	tc358768_sw_reset(priv);
+
+	tc358768_setup_pll(priv);
+
+	/* VSDly[9:0] */
+	tc358768_write(priv, TC358768_VSDLY, 1);
+
+	/* Data Format Control Register */
+	val = BIT(2) | BIT(1) | BIT(0); /* rdswap_en | dsitx_en | txdt_en */
+	switch (dsi_dev->format) {
+	case MIPI_DSI_FMT_RGB888:
+		val |= (0x3 << 4);
+		hact = mode->hdisplay * 3;
+		data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+		val |= (0x4 << 4);
+		hact = mode->hdisplay * 3;
+		data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
+		break;
+
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		val |= (0x4 << 4) | BIT(3);
+		hact = mode->hdisplay * 18 / 8;
+		data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
+		break;
+
+	case MIPI_DSI_FMT_RGB565:
+		val |= (0x5 << 4);
+		hact = mode->hdisplay * 2;
+		data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
+		break;
+	default:
+		dev_err(priv->dev, "Invalid data format (%u)\n",
+			dsi_dev->format);
+		return;
+	}
+
+	tc358768_write(priv, TC358768_DATAFMT, val);
+	tc358768_write(priv, TC358768_DSITX_DT, data_type);
+
+	/* Enable D-PHY (HiZ->LP11) */
+	tc358768_write(priv, TC358768_CLW_CNTRL, 0x0000);
+	/* Enable lanes */
+	for (i = 0; i < dsi_dev->lanes; i++)
+		tc358768_write(priv, TC358768_D0W_CNTRL + i * 4, 0x0000);
+
+	/* DSI Timings */
+	dsibclk_nsk = (u32)div_u64((u64)1000000000 * TC358768_PRECISION,
+				  dsibclk);
+	dsiclk_nsk = (u32)div_u64((u64)1000000000 * TC358768_PRECISION, dsiclk);
+	ui_nsk = dsiclk_nsk / 2;
+	phy_delay_nsk = dsibclk_nsk + 2 * dsiclk_nsk;
+	dev_dbg(priv->dev, "dsiclk_nsk: %u\n", dsiclk_nsk);
+	dev_dbg(priv->dev, "ui_nsk: %u\n", ui_nsk);
+	dev_dbg(priv->dev, "dsibclk_nsk: %u\n", dsibclk_nsk);
+	dev_dbg(priv->dev, "phy_delay_nsk: %u\n", phy_delay_nsk);
+
+	/* LP11 > 100us for D-PHY Rx Init */
+	val = tc358768_ns_to_cnt(100 * 1000, dsibclk_nsk) - 1;
+	dev_dbg(priv->dev, "LINEINITCNT: 0x%x\n", val);
+	tc358768_write(priv, TC358768_LINEINITCNT, val);
+
+	/* LPTimeCnt > 50ns */
+	val = tc358768_ns_to_cnt(50, dsibclk_nsk) - 1;
+	lptxcnt = val;
+	dev_dbg(priv->dev, "LPTXTIMECNT: 0x%x\n", val);
+	tc358768_write(priv, TC358768_LPTXTIMECNT, val);
+
+	/* 38ns < TCLK_PREPARE < 95ns */
+	val = tc358768_ns_to_cnt(65, dsibclk_nsk) - 1;
+	/* TCLK_PREPARE > 300ns */
+	val2 = tc358768_ns_to_cnt(300 + tc358768_to_ns(3 * ui_nsk),
+				  dsibclk_nsk);
+	val |= (val2 - tc358768_to_ns(phy_delay_nsk - dsibclk_nsk)) << 8;
+	dev_dbg(priv->dev, "TCLK_HEADERCNT: 0x%x\n", val);
+	tc358768_write(priv, TC358768_TCLK_HEADERCNT, val);
+
+	/* TCLK_TRAIL > 60ns + 3*UI */
+	val = 60 + tc358768_to_ns(3 * ui_nsk);
+	val = tc358768_ns_to_cnt(val, dsibclk_nsk) - 5;
+	dev_dbg(priv->dev, "TCLK_TRAILCNT: 0x%x\n", val);
+	tc358768_write(priv, TC358768_TCLK_TRAILCNT, val);
+
+	/* 40ns + 4*UI < THS_PREPARE < 85ns + 6*UI */
+	val = 50 + tc358768_to_ns(4 * ui_nsk);
+	val = tc358768_ns_to_cnt(val, dsibclk_nsk) - 1;
+	/* THS_ZERO > 145ns + 10*UI */
+	val2 = tc358768_ns_to_cnt(145 - tc358768_to_ns(ui_nsk), dsibclk_nsk);
+	val |= (val2 - tc358768_to_ns(phy_delay_nsk)) << 8;
+	dev_dbg(priv->dev, "THS_HEADERCNT: 0x%x\n", val);
+	tc358768_write(priv, TC358768_THS_HEADERCNT, val);
+
+	/* TWAKEUP > 1ms in lptxcnt steps */
+	val = tc358768_ns_to_cnt(1020000, dsibclk_nsk);
+	val = val / (lptxcnt + 1) - 1;
+	dev_dbg(priv->dev, "TWAKEUP: 0x%x\n", val);
+	tc358768_write(priv, TC358768_TWAKEUP, val);
+
+	/* TCLK_POSTCNT > 60ns + 52*UI */
+	val = tc358768_ns_to_cnt(60 + tc358768_to_ns(52 * ui_nsk),
+				 dsibclk_nsk) - 3;
+	dev_dbg(priv->dev, "TCLK_POSTCNT: 0x%x\n", val);
+	tc358768_write(priv, TC358768_TCLK_POSTCNT, val);
+
+	/* 60ns + 4*UI < THS_PREPARE < 105ns + 12*UI */
+	val = tc358768_ns_to_cnt(60 + tc358768_to_ns(15 * ui_nsk),
+				 dsibclk_nsk) - 5;
+	dev_dbg(priv->dev, "THS_TRAILCNT: 0x%x\n", val);
+	tc358768_write(priv, TC358768_THS_TRAILCNT, val);
+
+	val = BIT(0);
+	for (i = 0; i < dsi_dev->lanes; i++)
+		val |= BIT(i + 1);
+	tc358768_write(priv, TC358768_HSTXVREGEN, val);
+
+	if (!(dsi_dev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))
+		tc358768_write(priv, TC358768_TXOPTIONCNTRL, 0x1);
+
+	/* TXTAGOCNT[26:16] RXTASURECNT[10:0] */
+	val = tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk * 4);
+	val = tc358768_ns_to_cnt(val, dsibclk_nsk) - 1;
+	val2 = tc358768_ns_to_cnt(tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk),
+				  dsibclk_nsk) - 2;
+	val |= val2 << 16;
+	dev_dbg(priv->dev, "BTACNTRL1: 0x%x\n", val);
+	tc358768_write(priv, TC358768_BTACNTRL1, val);
+
+	/* START[0] */
+	tc358768_write(priv, TC358768_STARTCNTRL, 1);
+
+	/* Set event mode */
+	tc358768_write(priv, TC358768_DSI_EVENT, 1);
+
+	/* vsw (+ vbp) */
+	tc358768_write(priv, TC358768_DSI_VSW,
+		       mode->vtotal - mode->vsync_start);
+	/* vbp (not used in event mode) */
+	tc358768_write(priv, TC358768_DSI_VBPR, 0);
+	/* vact */
+	tc358768_write(priv, TC358768_DSI_VACT, mode->vdisplay);
+
+	/* (hsw + hbp) * byteclk * ndl / pclk */
+	val = (u32)div_u64((mode->htotal - mode->hsync_start) *
+			   ((u64)priv->dsiclk / 4) * priv->dsi_lanes,
+			   mode->clock * 1000);
+	tc358768_write(priv, TC358768_DSI_HSW, val);
+	/* hbp (not used in event mode) */
+	tc358768_write(priv, TC358768_DSI_HBPR, 0);
+	/* hact (bytes) */
+	tc358768_write(priv, TC358768_DSI_HACT, hact);
+
+	/* VSYNC polarity */
+	if (!(mode->flags & DRM_MODE_FLAG_NVSYNC))
+		tc358768_update_bits(priv, TC358768_CONFCTL, BIT(5), BIT(5));
+	/* HSYNC polarity */
+	if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+		tc358768_update_bits(priv, TC358768_PP_MISC, BIT(0), BIT(0));
+
+	/* Start DSI Tx */
+	tc358768_write(priv, TC358768_DSI_START, 0x1);
+
+	/* Configure DSI_Control register */
+	val = (6 << 29) | (0x3 << 24); /* CLEAR, DSI_Control */
+	val |= BIT(7) | BIT(5) | 0x3 << 1 | BIT(0);
+	tc358768_write(priv, TC358768_DSI_CONFW, val);
+
+	val = (5 << 29) | (0x3 << 24); /* SET, DSI_Control */
+	if (!(dsi_dev->mode_flags & MIPI_DSI_MODE_LPM))
+		val |= BIT(7);
+	if (!(dsi_dev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))
+		val |= BIT(5);
+	val |= (dsi_dev->lanes - 1) << 1;
+	if (dsi_dev->mode_flags & MIPI_DSI_MODE_EOT_PACKET)
+		val |= BIT(0);
+	tc358768_write(priv, TC358768_DSI_CONFW, val);
+
+	val = (6 << 29) | (0x3 << 24); /* CLEAR, DSI_Control */
+	val |= 0x8000; /* DSI mode */
+	tc358768_write(priv, TC358768_DSI_CONFW, val);
+
+	/* clear FrmStop and RstPtr */
+	tc358768_update_bits(priv, TC358768_PP_MISC, 0x3 << 14, 0);
+
+	/* set PP_en */
+	tc358768_update_bits(priv, TC358768_CONFCTL, BIT(6), BIT(6));
+}
+
+static const struct drm_bridge_funcs tc358768_bridge_funcs = {
+	.attach = tc358768_bridge_attach,
+	.mode_valid = tc358768_bridge_mode_valid,
+	.disable = tc358768_bridge_disable,
+	.enable = tc358768_bridge_enable,
+};
+
+static const struct drm_bridge_timings default_tc358768_timings = {
+	.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE
+		 | DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE
+		 | DRM_BUS_FLAG_DE_HIGH,
+};
+
+static bool tc358768_is_reserved_reg(unsigned int reg)
+{
+	switch (reg) {
+	case 0x114 ... 0x13f:
+	case 0x200:
+	case 0x20c:
+	case 0x400 ... 0x408:
+	case 0x41c ... 0x42f:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool tc358768_writeable_reg(struct device *dev, unsigned int reg)
+{
+	if (tc358768_is_reserved_reg(reg))
+		return false;
+
+	switch (reg) {
+	case TC358768_CHIPID:
+	case TC358768_FIFOSTATUS:
+	case TC358768_DSITXSTATUS ... (TC358768_DSITXSTATUS + 2):
+	case TC358768_DSI_CONTROL ... (TC358768_DSI_INT_ENA + 2):
+	case TC358768_DSICMD_RDFIFO ... (TC358768_DSI_ERR_HALT + 2):
+		return false;
+	default:
+		return true;
+	}
+}
+
+static bool tc358768_readable_reg(struct device *dev, unsigned int reg)
+{
+	if (tc358768_is_reserved_reg(reg))
+		return false;
+
+	switch (reg) {
+	case TC358768_STARTCNTRL:
+	case TC358768_DSI_CONFW ... (TC358768_DSI_CONFW + 2):
+	case TC358768_DSI_INT_CLR ... (TC358768_DSI_INT_CLR + 2):
+	case TC358768_DSI_START ... (TC358768_DSI_START + 2):
+	case TC358768_DBG_DATA:
+		return false;
+	default:
+		return true;
+	}
+}
+
+static bool tc358768_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case TC358768_FIFOSTATUS:
+	case TC358768_STARTCNTRL ... (TC358768_DSITXSTATUS + 2):
+	case TC358768_DSI_CONTROL ... (TC358768_DSI_INT_ENA + 2):
+	case TC358768_DSICMD_RDFIFO ... (TC358768_DSI_ERR_HALT + 2):
+	case TC358768_DSICMD_TX:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config tc358768_regmap_config = {
+	.name = "tc358768",
+	.reg_bits = 16,
+	.val_bits = 16,
+	.max_register = TC358768_DSI_HACT,
+	.cache_type = REGCACHE_RBTREE,
+	.writeable_reg = tc358768_writeable_reg,
+	.readable_reg = tc358768_readable_reg,
+	.volatile_reg = tc358768_volatile_reg,
+	.reg_format_endian = REGMAP_ENDIAN_BIG,
+	.val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static const struct i2c_device_id tc358768_i2c_ids[] = {
+	{ "tc358768", 0 },
+	{ "tc358778", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tc358768_i2c_ids);
+
+static const struct of_device_id tc358768_of_ids[] = {
+	{ .compatible = "toshiba,tc358768", },
+	{ .compatible = "toshiba,tc358778", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, tc358768_of_ids);
+
+static int tc358768_i2c_probe(struct i2c_client *client,
+			      const struct i2c_device_id *id)
+{
+	struct tc358768_priv *priv;
+	struct device *dev = &client->dev;
+	struct device_node *np = dev->of_node;
+	int ret;
+
+	if (!np)
+		return -ENODEV;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, priv);
+	priv->dev = dev;
+
+	priv->refclk = devm_clk_get(dev, "refclk");
+	if (IS_ERR(priv->refclk))
+		return PTR_ERR(priv->refclk);
+
+	priv->reset_gpio  = devm_gpiod_get_optional(dev, "reset",
+						    GPIOD_OUT_LOW);
+	if (IS_ERR(priv->reset_gpio))
+		return PTR_ERR(priv->reset_gpio);
+
+	priv->regmap = devm_regmap_init_i2c(client, &tc358768_regmap_config);
+	if (IS_ERR(priv->regmap)) {
+		dev_err(dev, "Failed to init regmap\n");
+		return PTR_ERR(priv->regmap);
+	}
+
+	if (priv->reset_gpio)
+		regcache_cache_only(priv->regmap, true);
+
+	priv->dsi_host.dev = dev;
+	priv->dsi_host.ops = &tc358768_dsi_host_ops;
+
+	priv->bridge.funcs = &tc358768_bridge_funcs;
+	priv->bridge.timings = &default_tc358768_timings;
+	priv->bridge.of_node = np;
+
+	i2c_set_clientdata(client, priv);
+
+	ret = mipi_dsi_host_register(&priv->dsi_host);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int tc358768_i2c_remove(struct i2c_client *client)
+{
+	struct tc358768_priv *priv = i2c_get_clientdata(client);
+
+	mipi_dsi_host_unregister(&priv->dsi_host);
+
+	return 0;
+}
+
+static struct i2c_driver tc358768_driver = {
+	.driver = {
+		.name = "tc358768",
+		.of_match_table = tc358768_of_ids,
+	},
+	.id_table = tc358768_i2c_ids,
+	.probe = tc358768_i2c_probe,
+	.remove	= tc358768_i2c_remove,
+};
+module_i2c_driver(tc358768_driver);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_DESCRIPTION("TC358768AXBG/TC358778XBG DSI bridge");
+MODULE_LICENSE("GPL v2");

+ 105 - 4
drivers/gpu/drm/bridge/ti-tfp410.c

@@ -27,10 +27,14 @@
 struct tfp410 {
 	struct drm_bridge	bridge;
 	struct drm_connector	connector;
+	unsigned int		connector_type;
 
 	struct i2c_adapter	*ddc;
 	struct gpio_desc	*hpd;
 	struct delayed_work	hpd_work;
+	struct gpio_desc	*powerdown;
+
+	struct drm_bridge_timings timings;
 
 	struct device *dev;
 };
@@ -126,7 +130,7 @@ static int tfp410_attach(struct drm_bridge *bridge)
 	drm_connector_helper_add(&dvi->connector,
 				 &tfp410_con_helper_funcs);
 	ret = drm_connector_init(bridge->dev, &dvi->connector,
-				 &tfp410_con_funcs, DRM_MODE_CONNECTOR_HDMIA);
+				 &tfp410_con_funcs, dvi->connector_type);
 	if (ret) {
 		dev_err(dvi->dev, "drm_connector_init() failed: %d\n", ret);
 		return ret;
@@ -138,8 +142,24 @@ static int tfp410_attach(struct drm_bridge *bridge)
 	return 0;
 }
 
+static void tfp410_enable(struct drm_bridge *bridge)
+{
+	struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
+
+	gpiod_set_value_cansleep(dvi->powerdown, 0);
+}
+
+static void tfp410_disable(struct drm_bridge *bridge)
+{
+	struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
+
+	gpiod_set_value_cansleep(dvi->powerdown, 1);
+}
+
 static const struct drm_bridge_funcs tfp410_bridge_funcs = {
 	.attach		= tfp410_attach,
+	.enable		= tfp410_enable,
+	.disable	= tfp410_disable,
 };
 
 static void tfp410_hpd_work_func(struct work_struct *work)
@@ -162,6 +182,70 @@ static irqreturn_t tfp410_hpd_irq_thread(int irq, void *arg)
 	return IRQ_HANDLED;
 }
 
+static const struct drm_bridge_timings tfp410_default_timings = {
+	.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE
+			 | DRM_BUS_FLAG_DE_HIGH,
+	.setup_time_ps = 1200,
+	.hold_time_ps = 1300,
+};
+
+static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c)
+{
+	struct drm_bridge_timings *timings = &dvi->timings;
+	struct device_node *ep;
+	u32 pclk_sample = 0;
+	s32 deskew = 0;
+
+	/* Start with defaults. */
+	*timings = tfp410_default_timings;
+
+	if (i2c)
+		/*
+		 * In I2C mode timings are configured through the I2C interface.
+		 * As the driver doesn't support I2C configuration yet, we just
+		 * go with the defaults (BSEL=1, DSEL=1, DKEN=0, EDGE=1).
+		 */
+		return 0;
+
+	/*
+	 * In non-I2C mode, timings are configured through the BSEL, DSEL, DKEN
+	 * and EDGE pins. They are specified in DT through endpoint properties
+	 * and vendor-specific properties.
+	 */
+	ep = of_graph_get_endpoint_by_regs(dvi->dev->of_node, 0, 0);
+	if (!ep)
+		return -EINVAL;
+
+	/* Get the sampling edge from the endpoint. */
+	of_property_read_u32(ep, "pclk-sample", &pclk_sample);
+	of_node_put(ep);
+
+	timings->input_bus_flags = DRM_BUS_FLAG_DE_HIGH;
+
+	switch (pclk_sample) {
+	case 0:
+		timings->input_bus_flags |= DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE
+					 |  DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE;
+		break;
+	case 1:
+		timings->input_bus_flags |= DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE
+					 |  DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Get the setup and hold time from vendor-specific properties. */
+	of_property_read_u32(dvi->dev->of_node, "ti,deskew", (u32 *)&deskew);
+	if (deskew < -4 || deskew > 3)
+		return -EINVAL;
+
+	timings->setup_time_ps = min(0, 1200 - 350 * deskew);
+	timings->hold_time_ps = min(0, 1300 + 350 * deskew);
+
+	return 0;
+}
+
 static int tfp410_get_connector_properties(struct tfp410 *dvi)
 {
 	struct device_node *connector_node, *ddc_phandle;
@@ -172,6 +256,11 @@ static int tfp410_get_connector_properties(struct tfp410 *dvi)
 	if (!connector_node)
 		return -ENODEV;
 
+	if (of_device_is_compatible(connector_node, "hdmi-connector"))
+		dvi->connector_type = DRM_MODE_CONNECTOR_HDMIA;
+	else
+		dvi->connector_type = DRM_MODE_CONNECTOR_DVID;
+
 	dvi->hpd = fwnode_get_named_gpiod(&connector_node->fwnode,
 					"hpd-gpios", 0, GPIOD_IN, "hpd");
 	if (IS_ERR(dvi->hpd)) {
@@ -200,7 +289,7 @@ fail:
 	return ret;
 }
 
-static int tfp410_init(struct device *dev)
+static int tfp410_init(struct device *dev, bool i2c)
 {
 	struct tfp410 *dvi;
 	int ret;
@@ -217,12 +306,24 @@ static int tfp410_init(struct device *dev)
 
 	dvi->bridge.funcs = &tfp410_bridge_funcs;
 	dvi->bridge.of_node = dev->of_node;
+	dvi->bridge.timings = &dvi->timings;
 	dvi->dev = dev;
 
+	ret = tfp410_parse_timings(dvi, i2c);
+	if (ret)
+		goto fail;
+
 	ret = tfp410_get_connector_properties(dvi);
 	if (ret)
 		goto fail;
 
+	dvi->powerdown = devm_gpiod_get_optional(dev, "powerdown",
+						 GPIOD_OUT_HIGH);
+	if (IS_ERR(dvi->powerdown)) {
+		dev_err(dev, "failed to parse powerdown gpio\n");
+		return PTR_ERR(dvi->powerdown);
+	}
+
 	if (dvi->hpd) {
 		INIT_DELAYED_WORK(&dvi->hpd_work, tfp410_hpd_work_func);
 
@@ -264,7 +365,7 @@ static int tfp410_fini(struct device *dev)
 
 static int tfp410_probe(struct platform_device *pdev)
 {
-	return tfp410_init(&pdev->dev);
+	return tfp410_init(&pdev->dev, false);
 }
 
 static int tfp410_remove(struct platform_device *pdev)
@@ -301,7 +402,7 @@ static int tfp410_i2c_probe(struct i2c_client *client,
 		return -ENXIO;
 	}
 
-	return tfp410_init(&client->dev);
+	return tfp410_init(&client->dev, true);
 }
 
 static int tfp410_i2c_remove(struct i2c_client *client)

+ 2 - 2
drivers/gpu/drm/drm_atomic_helper.c

@@ -457,7 +457,7 @@ mode_fixup(struct drm_atomic_state *state)
 static enum drm_mode_status mode_valid_path(struct drm_connector *connector,
 					    struct drm_encoder *encoder,
 					    struct drm_crtc *crtc,
-					    struct drm_display_mode *mode)
+					    const struct drm_display_mode *mode)
 {
 	enum drm_mode_status ret;
 
@@ -496,7 +496,7 @@ mode_valid(struct drm_atomic_state *state)
 		struct drm_crtc *crtc = conn_state->crtc;
 		struct drm_crtc_state *crtc_state;
 		enum drm_mode_status mode_status;
-		struct drm_display_mode *mode;
+		const struct drm_display_mode *mode;
 
 		if (!crtc || !encoder)
 			continue;

+ 6 - 6
drivers/gpu/drm/drm_modes.c

@@ -662,22 +662,22 @@ EXPORT_SYMBOL_GPL(drm_display_mode_to_videomode);
  * @bus_flags: information about pixelclk, sync and DE polarity will be stored
  * here
  *
- * Sets DRM_BUS_FLAG_DE_(LOW|HIGH),  DRM_BUS_FLAG_PIXDATA_(POS|NEG)EDGE and
- * DISPLAY_FLAGS_SYNC_(POS|NEG)EDGE in @bus_flags according to DISPLAY_FLAGS
+ * Sets DRM_BUS_FLAG_DE_(LOW|HIGH),  DRM_BUS_FLAG_PIXDATA_DRIVE_(POS|NEG)EDGE
+ * and DISPLAY_FLAGS_SYNC_(POS|NEG)EDGE in @bus_flags according to DISPLAY_FLAGS
  * found in @vm
  */
 void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags)
 {
 	*bus_flags = 0;
 	if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
-		*bus_flags |= DRM_BUS_FLAG_PIXDATA_POSEDGE;
+		*bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
 	if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
-		*bus_flags |= DRM_BUS_FLAG_PIXDATA_NEGEDGE;
+		*bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
 
 	if (vm->flags & DISPLAY_FLAGS_SYNC_POSEDGE)
-		*bus_flags |= DRM_BUS_FLAG_SYNC_POSEDGE;
+		*bus_flags |= DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE;
 	if (vm->flags & DISPLAY_FLAGS_SYNC_NEGEDGE)
-		*bus_flags |= DRM_BUS_FLAG_SYNC_NEGEDGE;
+		*bus_flags |= DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE;
 
 	if (vm->flags & DISPLAY_FLAGS_DE_LOW)
 		*bus_flags |= DRM_BUS_FLAG_DE_LOW;

+ 1 - 1
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c

@@ -99,7 +99,7 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
 	vsw = mode->vsync_end - mode->vsync_start;
 
 	/* INV_PXCK as default (most display sample data on rising edge) */
-	if (!(con->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE))
+	if (!(con->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE))
 		pol |= DCU_SYN_POL_INV_PXCK;
 
 	if (mode->flags & DRM_MODE_FLAG_NHSYNC)

+ 1 - 1
drivers/gpu/drm/imx/ipuv3-crtc.c

@@ -277,7 +277,7 @@ static void ipu_crtc_mode_set_nofb(struct drm_crtc *crtc)
 	sig_cfg.enable_pol = !(imx_crtc_state->bus_flags & DRM_BUS_FLAG_DE_LOW);
 	/* Default to driving pixel data on negative clock edges */
 	sig_cfg.clk_pol = !!(imx_crtc_state->bus_flags &
-			     DRM_BUS_FLAG_PIXDATA_POSEDGE);
+			     DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE);
 	sig_cfg.bus_format = imx_crtc_state->bus_format;
 	sig_cfg.v_to_h_sync = 0;
 	sig_cfg.hsync_pin = imx_crtc_state->di_hsync_pin;

+ 3 - 3
drivers/gpu/drm/mxsfb/mxsfb_crtc.c

@@ -242,12 +242,12 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
 	if (!(bus_flags & DRM_BUS_FLAG_DE_LOW))
 		vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH;
 	/*
-	 * DRM_BUS_FLAG_PIXDATA_ defines are controller centric,
+	 * DRM_BUS_FLAG_PIXDATA_DRIVE_ defines are controller centric,
 	 * controllers VDCTRL0_DOTCLK is display centric.
 	 * Drive on positive edge       -> display samples on falling edge
-	 * DRM_BUS_FLAG_PIXDATA_POSEDGE -> VDCTRL0_DOTCLK_ACT_FALLING
+	 * DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE -> VDCTRL0_DOTCLK_ACT_FALLING
 	 */
-	if (bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE)
+	if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
 		vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING;
 
 	writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0);

+ 11 - 0
drivers/gpu/drm/omapdrm/Kconfig

@@ -10,6 +10,17 @@ config DRM_OMAP
 
 if DRM_OMAP
 
+config DRM_OMAP_WB
+	bool "Enable writeback support for OMAP DRM driver"
+	depends on DRM_OMAP
+	depends on (VIDEO_V4L2 = y) || (VIDEO_V4L2 = m && DRM_OMAP = m)
+	depends on VIDEO_DEV && HAS_DMA
+	select VIDEOBUF2_DMA_CONTIG
+	select V4L2_MEM2MEM_DEV
+	default n
+	help
+	  Select this to enable memory-to-memory/capture writeback support.
+
 source "drivers/gpu/drm/omapdrm/dss/Kconfig"
 source "drivers/gpu/drm/omapdrm/displays/Kconfig"
 

+ 2 - 0
drivers/gpu/drm/omapdrm/Makefile

@@ -23,4 +23,6 @@ omapdrm-y := omap_drv.o \
 
 omapdrm-$(CONFIG_DRM_FBDEV_EMULATION) += omap_fbdev.o
 
+omapdrm-$(CONFIG_DRM_OMAP_WB) += omap_wb.o omap_wb_cap.o omap_wb_m2m.o
+
 obj-$(CONFIG_DRM_OMAP)	+= omapdrm.o

+ 5 - 15
drivers/gpu/drm/omapdrm/displays/Kconfig

@@ -6,22 +6,18 @@ config DRM_OMAP_ENCODER_OPA362
 	  Driver for OPA362 external analog TV amplifier controlled
 	  through a GPIO.
 
-config DRM_OMAP_ENCODER_TFP410
-        tristate "TFP410 DPI to DVI Encoder"
-	help
-	  Driver for TFP410 DPI to DVI encoder.
-
 config DRM_OMAP_ENCODER_TPD12S015
         tristate "TPD12S015 HDMI ESD protection and level shifter"
 	help
 	  Driver for TPD12S015, which offers HDMI ESD protection and level
 	  shifting.
 
-config DRM_OMAP_CONNECTOR_DVI
-        tristate "DVI Connector"
-	depends on I2C
+config DRM_OMAP_DRA7EVM_ENCODER_TPD12S015
+        tristate "DRA7 EVM TPD12S015 HDMI ESD protection and level shifter"
+	depends on SND_SOC_DAVINCI_MCASP
 	help
-	  Driver for a generic DVI connector.
+	  A custom TPD12S015 driver for the DRA7 EVM board, it contains some
+	  hacks required for HDMI to work properly on J6 EVM.
 
 config DRM_OMAP_CONNECTOR_HDMI
         tristate "HDMI Connector"
@@ -33,12 +29,6 @@ config DRM_OMAP_CONNECTOR_ANALOG_TV
 	help
 	  Driver for a generic analog TV connector.
 
-config DRM_OMAP_PANEL_DPI
-	tristate "Generic DPI panel"
-	depends on BACKLIGHT_CLASS_DEVICE
-	help
-	  Driver for generic DPI panels.
-
 config DRM_OMAP_PANEL_DSI_CM
 	tristate "Generic DSI Command Mode Panel"
 	depends on BACKLIGHT_CLASS_DEVICE

+ 1 - 3
drivers/gpu/drm/omapdrm/displays/Makefile

@@ -1,11 +1,9 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_DRM_OMAP_ENCODER_OPA362) += encoder-opa362.o
-obj-$(CONFIG_DRM_OMAP_ENCODER_TFP410) += encoder-tfp410.o
 obj-$(CONFIG_DRM_OMAP_ENCODER_TPD12S015) += encoder-tpd12s015.o
-obj-$(CONFIG_DRM_OMAP_CONNECTOR_DVI) += connector-dvi.o
+obj-$(CONFIG_DRM_OMAP_DRA7EVM_ENCODER_TPD12S015) += dra7-evm-encoder-tpd12s015.o
 obj-$(CONFIG_DRM_OMAP_CONNECTOR_HDMI) += connector-hdmi.o
 obj-$(CONFIG_DRM_OMAP_CONNECTOR_ANALOG_TV) += connector-analog-tv.o
-obj-$(CONFIG_DRM_OMAP_PANEL_DPI) += panel-dpi.o
 obj-$(CONFIG_DRM_OMAP_PANEL_DSI_CM) += panel-dsi-cm.o
 obj-$(CONFIG_DRM_OMAP_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
 obj-$(CONFIG_DRM_OMAP_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o

+ 1 - 44
drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c

@@ -35,50 +35,9 @@ static void tvc_disconnect(struct omap_dss_device *src,
 {
 }
 
-static int tvc_enable(struct omap_dss_device *dssdev)
-{
-	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-	int r;
-
-	dev_dbg(ddata->dev, "enable\n");
-
-	if (!omapdss_device_is_connected(dssdev))
-		return -ENODEV;
-
-	if (omapdss_device_is_enabled(dssdev))
-		return 0;
-
-	r = src->ops->enable(src);
-	if (r)
-		return r;
-
-	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-	return r;
-}
-
-static void tvc_disable(struct omap_dss_device *dssdev)
-{
-	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-
-	dev_dbg(ddata->dev, "disable\n");
-
-	if (!omapdss_device_is_enabled(dssdev))
-		return;
-
-	src->ops->disable(src);
-
-	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
-}
-
 static const struct omap_dss_device_ops tvc_ops = {
 	.connect		= tvc_connect,
 	.disconnect		= tvc_disconnect,
-
-	.enable			= tvc_enable,
-	.disable		= tvc_disable,
 };
 
 static int tvc_probe(struct platform_device *pdev)
@@ -97,6 +56,7 @@ static int tvc_probe(struct platform_device *pdev)
 	dssdev->ops = &tvc_ops;
 	dssdev->dev = &pdev->dev;
 	dssdev->type = OMAP_DISPLAY_TYPE_VENC;
+	dssdev->display = true;
 	dssdev->owner = THIS_MODULE;
 	dssdev->of_ports = BIT(0);
 
@@ -109,12 +69,9 @@ static int tvc_probe(struct platform_device *pdev)
 static int __exit tvc_remove(struct platform_device *pdev)
 {
 	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
-	struct omap_dss_device *dssdev = &ddata->dssdev;
 
 	omapdss_device_unregister(&ddata->dssdev);
 
-	tvc_disable(dssdev);
-
 	return 0;
 }
 

+ 0 - 330
drivers/gpu/drm/omapdrm/displays/connector-dvi.c

@@ -1,330 +0,0 @@
-/*
- * Generic DVI Connector driver
- *
- * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
- * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- */
-
-#include <linux/gpio/consumer.h>
-#include <linux/i2c.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include <drm/drm_edid.h>
-
-#include "../dss/omapdss.h"
-
-struct panel_drv_data {
-	struct omap_dss_device dssdev;
-
-	struct i2c_adapter *i2c_adapter;
-
-	struct gpio_desc *hpd_gpio;
-
-	void (*hpd_cb)(void *cb_data, enum drm_connector_status status);
-	void *hpd_cb_data;
-	bool hpd_enabled;
-	/* mutex for hpd fields above */
-	struct mutex hpd_lock;
-};
-
-#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
-
-static int dvic_connect(struct omap_dss_device *src,
-			struct omap_dss_device *dst)
-{
-	return 0;
-}
-
-static void dvic_disconnect(struct omap_dss_device *src,
-			    struct omap_dss_device *dst)
-{
-}
-
-static int dvic_enable(struct omap_dss_device *dssdev)
-{
-	struct omap_dss_device *src = dssdev->src;
-	int r;
-
-	if (!omapdss_device_is_connected(dssdev))
-		return -ENODEV;
-
-	if (omapdss_device_is_enabled(dssdev))
-		return 0;
-
-	r = src->ops->enable(src);
-	if (r)
-		return r;
-
-	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-	return 0;
-}
-
-static void dvic_disable(struct omap_dss_device *dssdev)
-{
-	struct omap_dss_device *src = dssdev->src;
-
-	if (!omapdss_device_is_enabled(dssdev))
-		return;
-
-	src->ops->disable(src);
-
-	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
-}
-
-static int dvic_ddc_read(struct i2c_adapter *adapter,
-		unsigned char *buf, u16 count, u8 offset)
-{
-	int r, retries;
-
-	for (retries = 3; retries > 0; retries--) {
-		struct i2c_msg msgs[] = {
-			{
-				.addr   = DDC_ADDR,
-				.flags  = 0,
-				.len    = 1,
-				.buf    = &offset,
-			}, {
-				.addr   = DDC_ADDR,
-				.flags  = I2C_M_RD,
-				.len    = count,
-				.buf    = buf,
-			}
-		};
-
-		r = i2c_transfer(adapter, msgs, 2);
-		if (r == 2)
-			return 0;
-
-		if (r != -EAGAIN)
-			break;
-	}
-
-	return r < 0 ? r : -EIO;
-}
-
-static int dvic_read_edid(struct omap_dss_device *dssdev,
-		u8 *edid, int len)
-{
-	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	int r, l, bytes_read;
-
-	l = min(EDID_LENGTH, len);
-	r = dvic_ddc_read(ddata->i2c_adapter, edid, l, 0);
-	if (r)
-		return r;
-
-	bytes_read = l;
-
-	/* if there are extensions, read second block */
-	if (len > EDID_LENGTH && edid[0x7e] > 0) {
-		l = min(EDID_LENGTH, len - EDID_LENGTH);
-
-		r = dvic_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH,
-				l, EDID_LENGTH);
-		if (r)
-			return r;
-
-		bytes_read += l;
-	}
-
-	return bytes_read;
-}
-
-static bool dvic_detect(struct omap_dss_device *dssdev)
-{
-	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	unsigned char out;
-	int r;
-
-	if (ddata->hpd_gpio)
-		return gpiod_get_value_cansleep(ddata->hpd_gpio);
-
-	if (!ddata->i2c_adapter)
-		return true;
-
-	r = dvic_ddc_read(ddata->i2c_adapter, &out, 1, 0);
-
-	return r == 0;
-}
-
-static void dvic_register_hpd_cb(struct omap_dss_device *dssdev,
-				 void (*cb)(void *cb_data,
-					    enum drm_connector_status status),
-				 void *cb_data)
-{
-	struct panel_drv_data *ddata = to_panel_data(dssdev);
-
-	mutex_lock(&ddata->hpd_lock);
-	ddata->hpd_cb = cb;
-	ddata->hpd_cb_data = cb_data;
-	mutex_unlock(&ddata->hpd_lock);
-}
-
-static void dvic_unregister_hpd_cb(struct omap_dss_device *dssdev)
-{
-	struct panel_drv_data *ddata = to_panel_data(dssdev);
-
-	mutex_lock(&ddata->hpd_lock);
-	ddata->hpd_cb = NULL;
-	ddata->hpd_cb_data = NULL;
-	mutex_unlock(&ddata->hpd_lock);
-}
-
-static const struct omap_dss_device_ops dvic_ops = {
-	.connect	= dvic_connect,
-	.disconnect	= dvic_disconnect,
-
-	.enable		= dvic_enable,
-	.disable	= dvic_disable,
-
-	.read_edid	= dvic_read_edid,
-	.detect		= dvic_detect,
-
-	.register_hpd_cb	= dvic_register_hpd_cb,
-	.unregister_hpd_cb	= dvic_unregister_hpd_cb,
-};
-
-static irqreturn_t dvic_hpd_isr(int irq, void *data)
-{
-	struct panel_drv_data *ddata = data;
-
-	mutex_lock(&ddata->hpd_lock);
-	if (ddata->hpd_enabled && ddata->hpd_cb) {
-		enum drm_connector_status status;
-
-		if (dvic_detect(&ddata->dssdev))
-			status = connector_status_connected;
-		else
-			status = connector_status_disconnected;
-
-		ddata->hpd_cb(ddata->hpd_cb_data, status);
-	}
-	mutex_unlock(&ddata->hpd_lock);
-
-	return IRQ_HANDLED;
-}
-
-static int dvic_probe_of(struct platform_device *pdev)
-{
-	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
-	struct device_node *node = pdev->dev.of_node;
-	struct device_node *adapter_node;
-	struct i2c_adapter *adapter;
-	struct gpio_desc *gpio;
-	int r;
-
-	gpio = devm_gpiod_get_optional(&pdev->dev, "hpd", GPIOD_IN);
-	if (IS_ERR(gpio)) {
-		dev_err(&pdev->dev, "failed to parse HPD gpio\n");
-		return PTR_ERR(gpio);
-	}
-
-	ddata->hpd_gpio = gpio;
-
-	mutex_init(&ddata->hpd_lock);
-
-	if (ddata->hpd_gpio) {
-		r = devm_request_threaded_irq(&pdev->dev,
-			gpiod_to_irq(ddata->hpd_gpio), NULL, dvic_hpd_isr,
-			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-			"DVI HPD", ddata);
-		if (r)
-			return r;
-	}
-
-	adapter_node = of_parse_phandle(node, "ddc-i2c-bus", 0);
-	if (adapter_node) {
-		adapter = of_get_i2c_adapter_by_node(adapter_node);
-		of_node_put(adapter_node);
-		if (adapter == NULL) {
-			dev_err(&pdev->dev, "failed to parse ddc-i2c-bus\n");
-			return -EPROBE_DEFER;
-		}
-
-		ddata->i2c_adapter = adapter;
-	}
-
-	return 0;
-}
-
-static int dvic_probe(struct platform_device *pdev)
-{
-	struct panel_drv_data *ddata;
-	struct omap_dss_device *dssdev;
-	int r;
-
-	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
-	if (!ddata)
-		return -ENOMEM;
-
-	platform_set_drvdata(pdev, ddata);
-
-	r = dvic_probe_of(pdev);
-	if (r)
-		return r;
-
-	dssdev = &ddata->dssdev;
-	dssdev->ops = &dvic_ops;
-	dssdev->dev = &pdev->dev;
-	dssdev->type = OMAP_DISPLAY_TYPE_DVI;
-	dssdev->owner = THIS_MODULE;
-	dssdev->of_ports = BIT(0);
-
-	if (ddata->hpd_gpio)
-		dssdev->ops_flags |= OMAP_DSS_DEVICE_OP_DETECT
-				  |  OMAP_DSS_DEVICE_OP_HPD;
-	if (ddata->i2c_adapter)
-		dssdev->ops_flags |= OMAP_DSS_DEVICE_OP_DETECT
-				  |  OMAP_DSS_DEVICE_OP_EDID;
-
-	omapdss_display_init(dssdev);
-	omapdss_device_register(dssdev);
-
-	return 0;
-}
-
-static int __exit dvic_remove(struct platform_device *pdev)
-{
-	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
-	struct omap_dss_device *dssdev = &ddata->dssdev;
-
-	omapdss_device_unregister(&ddata->dssdev);
-
-	dvic_disable(dssdev);
-
-	i2c_put_adapter(ddata->i2c_adapter);
-
-	mutex_destroy(&ddata->hpd_lock);
-
-	return 0;
-}
-
-static const struct of_device_id dvic_of_match[] = {
-	{ .compatible = "omapdss,dvi-connector", },
-	{},
-};
-
-MODULE_DEVICE_TABLE(of, dvic_of_match);
-
-static struct platform_driver dvi_connector_driver = {
-	.probe	= dvic_probe,
-	.remove	= __exit_p(dvic_remove),
-	.driver	= {
-		.name	= "connector-dvi",
-		.of_match_table = dvic_of_match,
-		.suppress_bind_attrs = true,
-	},
-};
-
-module_platform_driver(dvi_connector_driver);
-
-MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
-MODULE_DESCRIPTION("Generic DVI Connector driver");
-MODULE_LICENSE("GPL");

+ 1 - 44
drivers/gpu/drm/omapdrm/displays/connector-hdmi.c

@@ -41,44 +41,6 @@ static void hdmic_disconnect(struct omap_dss_device *src,
 {
 }
 
-static int hdmic_enable(struct omap_dss_device *dssdev)
-{
-	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-	int r;
-
-	dev_dbg(ddata->dev, "enable\n");
-
-	if (!omapdss_device_is_connected(dssdev))
-		return -ENODEV;
-
-	if (omapdss_device_is_enabled(dssdev))
-		return 0;
-
-	r = src->ops->enable(src);
-	if (r)
-		return r;
-
-	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-	return r;
-}
-
-static void hdmic_disable(struct omap_dss_device *dssdev)
-{
-	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-
-	dev_dbg(ddata->dev, "disable\n");
-
-	if (!omapdss_device_is_enabled(dssdev))
-		return;
-
-	src->ops->disable(src);
-
-	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
-}
-
 static bool hdmic_detect(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
@@ -113,9 +75,6 @@ static const struct omap_dss_device_ops hdmic_ops = {
 	.connect		= hdmic_connect,
 	.disconnect		= hdmic_disconnect,
 
-	.enable			= hdmic_enable,
-	.disable		= hdmic_disable,
-
 	.detect			= hdmic_detect,
 	.register_hpd_cb	= hdmic_register_hpd_cb,
 	.unregister_hpd_cb	= hdmic_unregister_hpd_cb,
@@ -181,6 +140,7 @@ static int hdmic_probe(struct platform_device *pdev)
 	dssdev->ops = &hdmic_ops;
 	dssdev->dev = &pdev->dev;
 	dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
+	dssdev->display = true;
 	dssdev->owner = THIS_MODULE;
 	dssdev->of_ports = BIT(0);
 	dssdev->ops_flags = ddata->hpd_gpio
@@ -196,12 +156,9 @@ static int hdmic_probe(struct platform_device *pdev)
 static int __exit hdmic_remove(struct platform_device *pdev)
 {
 	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
-	struct omap_dss_device *dssdev = &ddata->dssdev;
 
 	omapdss_device_unregister(&ddata->dssdev);
 
-	hdmic_disable(dssdev);
-
 	return 0;
 }
 

+ 369 - 0
drivers/gpu/drm/omapdrm/displays/dra7-evm-encoder-tpd12s015.c

@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DRA7 EVM TPD12S015 HDMI ESD protection & level shifter chip driver
+ *
+ * Copyright (C) 2013-2018 Texas Instruments Incorporated -  http://www.ti.com/
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ */
+
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/suspend.h>
+#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/spinlock.h>
+
+#include "../dss/omapdss.h"
+
+#define SEL_I2C2	0
+#define SEL_HDMI	1
+
+/* HPD gpio debounce time in microseconds */
+#define HPD_DEBOUNCE_TIME	1000
+
+struct i2c_mux_wa {
+	struct gpio_desc *i2c_ddc_gpio;
+
+	struct pinctrl *pins;
+	struct pinctrl_state *pin_state_i2c;
+	struct pinctrl_state *pin_state_ddc;
+
+	struct i2c_adapter *ddc_i2c_adapter;
+};
+
+struct panel_drv_data {
+	struct omap_dss_device dssdev;
+	struct omap_dss_device *src;
+	void (*hpd_cb)(void *cb_data, enum drm_connector_status status);
+	void *hpd_cb_data;
+	struct mutex hpd_lock;
+
+	struct gpio_desc *ct_cp_hpd_gpio;
+	struct gpio_desc *ls_oe_gpio;
+	struct gpio_desc *hpd_gpio;
+
+	struct i2c_mux_wa i2c_wa;
+};
+
+/*
+ * use SEL_I2C2 to configure pcf8575@26 to set/unset LS_OE and CT_HPD, and use
+ * SEL_HDMI to read edid via the HDMI ddc lines
+ */
+static void tpd_i2c_ddc_demux(struct i2c_mux_wa *i2c_wa, int sel)
+{
+	/*
+	 * switch to I2C2 or HDMI DDC internal pinmux gpio to low or high to
+	 * select I2C2 or HDMI path respectively
+	 */
+	if (sel == SEL_I2C2) {
+		pinctrl_select_state(i2c_wa->pins, i2c_wa->pin_state_i2c);
+		gpiod_set_value(i2c_wa->i2c_ddc_gpio, 0);
+	} else {
+		pinctrl_select_state(i2c_wa->pins, i2c_wa->pin_state_ddc);
+		gpiod_set_value(i2c_wa->i2c_ddc_gpio, 1);
+	}
+
+	/* let it propagate */
+	udelay(5);
+}
+
+#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
+
+static int tpd_connect(struct omap_dss_device *src,
+		       struct omap_dss_device *dst)
+{
+	struct panel_drv_data *ddata = to_panel_data(dst);
+	int r;
+
+	r = omapdss_device_connect(dst->dss, dst, dst->next);
+	if (r)
+		return r;
+
+	gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
+	gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1);
+
+	/* DC-DC converter needs at max 300us to get to 90% of 5V */
+	udelay(300);
+
+	/*
+	 * The HPD GPIO debounce causes a delay until we see the real HPD state.
+	 * If tpd_read_edid() or tpd_detect() are called very soon after setting
+	 * the ct_cp_hpd-gpio, we could observe wrong HPD value. So sleep here
+	 * until the GPIO values has become valid.
+	 */
+	msleep(DIV_ROUND_UP(HPD_DEBOUNCE_TIME, 1000));
+
+	ddata->src = src;
+
+	return 0;
+}
+
+static void tpd_disconnect(struct omap_dss_device *src,
+			   struct omap_dss_device *dst)
+{
+	struct panel_drv_data *ddata = to_panel_data(dst);
+
+	gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
+	gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0);
+
+	omapdss_device_disconnect(dst, dst->next);
+	ddata->src = NULL;
+}
+
+static int tpd_read_edid(struct omap_dss_device *dssdev,
+		u8 *edid, int len)
+{
+	struct panel_drv_data *ddata = to_panel_data(dssdev);
+	struct omap_dss_device *src = ddata->src;
+	struct i2c_mux_wa *i2c_wa = &ddata->i2c_wa;
+	int r = 0;
+
+	if (!gpiod_get_value_cansleep(ddata->hpd_gpio))
+		return -ENODEV;
+
+	i2c_lock_bus(i2c_wa->ddc_i2c_adapter, I2C_LOCK_ROOT_ADAPTER);
+
+	tpd_i2c_ddc_demux(i2c_wa, SEL_HDMI);
+
+	r = src->ops->read_edid(src, edid, len);
+
+	tpd_i2c_ddc_demux(i2c_wa, SEL_I2C2);
+
+	i2c_unlock_bus(i2c_wa->ddc_i2c_adapter, I2C_LOCK_ROOT_ADAPTER);
+
+	return r;
+}
+
+static bool tpd_detect(struct omap_dss_device *dssdev)
+{
+	struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+	return gpiod_get_value_cansleep(ddata->hpd_gpio);
+}
+
+static void tpd_register_hpd_cb(struct omap_dss_device *dssdev,
+				void (*cb)(void *cb_data,
+					  enum drm_connector_status status),
+				void *cb_data)
+{
+	struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+	mutex_lock(&ddata->hpd_lock);
+	ddata->hpd_cb = cb;
+	ddata->hpd_cb_data = cb_data;
+	mutex_unlock(&ddata->hpd_lock);
+}
+
+static void tpd_unregister_hpd_cb(struct omap_dss_device *dssdev)
+{
+	struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+	mutex_lock(&ddata->hpd_lock);
+	ddata->hpd_cb = NULL;
+	ddata->hpd_cb_data = NULL;
+	mutex_unlock(&ddata->hpd_lock);
+}
+
+static const struct omap_dss_device_ops tpd_ops = {
+	.connect		= tpd_connect,
+	.disconnect		= tpd_disconnect,
+	.detect			= tpd_detect,
+	.register_hpd_cb	= tpd_register_hpd_cb,
+	.unregister_hpd_cb	= tpd_unregister_hpd_cb,
+	.read_edid		= tpd_read_edid,
+};
+
+static irqreturn_t tpd_hpd_isr(int irq, void *data)
+{
+	struct panel_drv_data *ddata = data;
+
+	mutex_lock(&ddata->hpd_lock);
+	if (ddata->hpd_cb) {
+		enum drm_connector_status status;
+
+		if (tpd_detect(&ddata->dssdev))
+			status = connector_status_connected;
+		else
+			status = connector_status_disconnected;
+
+		ddata->hpd_cb(ddata->hpd_cb_data, status);
+	}
+	mutex_unlock(&ddata->hpd_lock);
+
+	return IRQ_HANDLED;
+}
+
+static int tpd_init_i2c_mux(struct platform_device *pdev)
+{
+	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+	struct i2c_mux_wa *i2c_wa = &ddata->i2c_wa;
+	struct device_node *node;
+
+	i2c_wa->i2c_ddc_gpio = devm_gpiod_get_index(&pdev->dev, NULL,
+						    3, GPIOD_OUT_LOW);
+	if (IS_ERR(i2c_wa->i2c_ddc_gpio))
+		return PTR_ERR(i2c_wa->i2c_ddc_gpio);
+
+	i2c_wa->pins = devm_pinctrl_get(&pdev->dev);
+	if (IS_ERR(i2c_wa->pins))
+		return PTR_ERR(i2c_wa->pins);
+
+	i2c_wa->pin_state_i2c = pinctrl_lookup_state(i2c_wa->pins, "i2c");
+	if (IS_ERR(i2c_wa->pin_state_i2c))
+		return PTR_ERR(i2c_wa->pin_state_i2c);
+
+	i2c_wa->pin_state_ddc = pinctrl_lookup_state(i2c_wa->pins, "ddc");
+	if (IS_ERR(i2c_wa->pin_state_ddc))
+		return PTR_ERR(i2c_wa->pin_state_ddc);
+
+	node = of_parse_phandle(pdev->dev.of_node, "ddc-i2c-bus", 0);
+	if (!node)
+		return -ENODEV;
+
+	i2c_wa->ddc_i2c_adapter = of_find_i2c_adapter_by_node(node);
+	if (!i2c_wa->ddc_i2c_adapter)
+		return -ENODEV;
+
+	tpd_i2c_ddc_demux(i2c_wa, SEL_I2C2);
+
+	return 0;
+}
+
+static void tpd_uninit_i2c_mux(struct platform_device *pdev)
+{
+	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+
+	tpd_i2c_ddc_demux(&ddata->i2c_wa, SEL_I2C2);
+
+	i2c_put_adapter(ddata->i2c_wa.ddc_i2c_adapter);
+}
+
+static int tpd_probe(struct platform_device *pdev)
+{
+	struct omap_dss_device *dssdev;
+	struct panel_drv_data *ddata;
+	int r;
+	struct gpio_desc *gpio;
+
+	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+	if (!ddata)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, ddata);
+
+	gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
+		 GPIOD_OUT_LOW);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+
+	ddata->ct_cp_hpd_gpio = gpio;
+
+	gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1,
+		 GPIOD_OUT_LOW);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+
+	ddata->ls_oe_gpio = gpio;
+
+	gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2,
+		GPIOD_IN);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+
+	ddata->hpd_gpio = gpio;
+
+	mutex_init(&ddata->hpd_lock);
+
+	r = tpd_init_i2c_mux(pdev);
+	if (r)
+		return r;
+
+	/*
+	 * we see some low voltage glitches on the HPD_B line before it
+	 * stabalizes to around 5V. We see the effects of this glitch on the
+	 * HPD_A side, and hence on the gpio on DRA7x. The glitch is quite short
+	 * in duration, but it takes a while for the voltage to go down back to
+	 * 0 volts, we set a debounce value of 1 millisecond to prevent this,
+	 * the reason for the glitch not being taken care of by the TPD chip
+	 * needs to be investigated
+	 */
+	r = gpiod_set_debounce(ddata->hpd_gpio, HPD_DEBOUNCE_TIME);
+	if (r)
+		goto err;
+
+	r = devm_request_threaded_irq(&pdev->dev, gpiod_to_irq(ddata->hpd_gpio),
+		NULL, tpd_hpd_isr,
+		IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+		"tpd12s015 hpd", ddata);
+	if (r)
+		goto err;
+
+	dssdev = &ddata->dssdev;
+	dssdev->ops = &tpd_ops;
+	dssdev->dev = &pdev->dev;
+	dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
+	dssdev->owner = THIS_MODULE;
+	dssdev->of_ports = BIT(1) | BIT(0);
+	dssdev->ops_flags = OMAP_DSS_DEVICE_OP_DETECT
+			  | OMAP_DSS_DEVICE_OP_EDID
+			  | OMAP_DSS_DEVICE_OP_HPD;
+
+	dssdev->next = omapdss_of_find_connected_device(pdev->dev.of_node, 1);
+	if (IS_ERR(dssdev->next)) {
+		if (PTR_ERR(dssdev->next) != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "failed to find video sink\n");
+		r = PTR_ERR(dssdev->next);
+		goto err;
+	}
+
+	omapdss_device_register(dssdev);
+
+	return 0;
+err:
+	tpd_uninit_i2c_mux(pdev);
+	return r;
+}
+
+static int __exit tpd_remove(struct platform_device *pdev)
+{
+	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+	struct omap_dss_device *dssdev = &ddata->dssdev;
+
+	tpd_uninit_i2c_mux(pdev);
+
+	if (dssdev->next)
+		omapdss_device_put(dssdev->next);
+	omapdss_device_unregister(&ddata->dssdev);
+
+	return 0;
+}
+
+static const struct of_device_id tpd_of_match[] = {
+	{ .compatible = "omapdss,ti,dra7evm-tpd12s015", },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, tpd_of_match);
+
+static struct platform_driver tpd_driver = {
+	.probe	= tpd_probe,
+	.remove	= __exit_p(tpd_remove),
+	.driver	= {
+		.name	= "dra7evm-tpd12s015",
+		.of_match_table = tpd_of_match,
+		.suppress_bind_attrs = true,
+	},
+};
+
+module_platform_driver(tpd_driver);
+
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
+MODULE_DESCRIPTION("TPD12S015 driver");
+MODULE_LICENSE("GPL");

+ 2 - 37
drivers/gpu/drm/omapdrm/displays/encoder-opa362.c

@@ -41,48 +41,20 @@ static void opa362_disconnect(struct omap_dss_device *src,
 	omapdss_device_disconnect(dst, dst->next);
 }
 
-static int opa362_enable(struct omap_dss_device *dssdev)
+static void opa362_enable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-	int r;
-
-	dev_dbg(dssdev->dev, "enable\n");
-
-	if (!omapdss_device_is_connected(dssdev))
-		return -ENODEV;
-
-	if (omapdss_device_is_enabled(dssdev))
-		return 0;
-
-	r = src->ops->enable(src);
-	if (r)
-		return r;
 
 	if (ddata->enable_gpio)
 		gpiod_set_value_cansleep(ddata->enable_gpio, 1);
-
-	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-	return 0;
 }
 
 static void opa362_disable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-
-	dev_dbg(dssdev->dev, "disable\n");
-
-	if (!omapdss_device_is_enabled(dssdev))
-		return;
 
 	if (ddata->enable_gpio)
 		gpiod_set_value_cansleep(ddata->enable_gpio, 0);
-
-	src->ops->disable(src);
-
-	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 }
 
 static const struct omap_dss_device_ops opa362_ops = {
@@ -116,7 +88,6 @@ static int opa362_probe(struct platform_device *pdev)
 	dssdev->ops = &opa362_ops;
 	dssdev->dev = &pdev->dev;
 	dssdev->type = OMAP_DISPLAY_TYPE_VENC;
-	dssdev->output_type = OMAP_DISPLAY_TYPE_VENC;
 	dssdev->owner = THIS_MODULE;
 	dssdev->of_ports = BIT(1) | BIT(0);
 
@@ -141,13 +112,7 @@ static int __exit opa362_remove(struct platform_device *pdev)
 		omapdss_device_put(dssdev->next);
 	omapdss_device_unregister(&ddata->dssdev);
 
-	WARN_ON(omapdss_device_is_enabled(dssdev));
-	if (omapdss_device_is_enabled(dssdev))
-		opa362_disable(dssdev);
-
-	WARN_ON(omapdss_device_is_connected(dssdev));
-	if (omapdss_device_is_connected(dssdev))
-		omapdss_device_disconnect(NULL, dssdev);
+	opa362_disable(dssdev);
 
 	return 0;
 }

+ 0 - 170
drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c

@@ -1,170 +0,0 @@
-/*
- * TFP410 DPI-to-DVI encoder driver
- *
- * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
- * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- */
-
-#include <linux/gpio/consumer.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include "../dss/omapdss.h"
-
-struct panel_drv_data {
-	struct omap_dss_device dssdev;
-
-	struct gpio_desc *pd_gpio;
-};
-
-#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
-
-static int tfp410_connect(struct omap_dss_device *src,
-			  struct omap_dss_device *dst)
-{
-	return omapdss_device_connect(dst->dss, dst, dst->next);
-}
-
-static void tfp410_disconnect(struct omap_dss_device *src,
-			      struct omap_dss_device *dst)
-{
-	omapdss_device_disconnect(dst, dst->next);
-}
-
-static int tfp410_enable(struct omap_dss_device *dssdev)
-{
-	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-	int r;
-
-	if (!omapdss_device_is_connected(dssdev))
-		return -ENODEV;
-
-	if (omapdss_device_is_enabled(dssdev))
-		return 0;
-
-	r = src->ops->enable(src);
-	if (r)
-		return r;
-
-	if (ddata->pd_gpio)
-		gpiod_set_value_cansleep(ddata->pd_gpio, 0);
-
-	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-	return 0;
-}
-
-static void tfp410_disable(struct omap_dss_device *dssdev)
-{
-	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-
-	if (!omapdss_device_is_enabled(dssdev))
-		return;
-
-	if (ddata->pd_gpio)
-		gpiod_set_value_cansleep(ddata->pd_gpio, 0);
-
-	src->ops->disable(src);
-
-	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
-}
-
-static const struct omap_dss_device_ops tfp410_ops = {
-	.connect	= tfp410_connect,
-	.disconnect	= tfp410_disconnect,
-	.enable		= tfp410_enable,
-	.disable	= tfp410_disable,
-};
-
-static int tfp410_probe(struct platform_device *pdev)
-{
-	struct panel_drv_data *ddata;
-	struct omap_dss_device *dssdev;
-	struct gpio_desc *gpio;
-
-	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
-	if (!ddata)
-		return -ENOMEM;
-
-	platform_set_drvdata(pdev, ddata);
-
-	/* Powerdown GPIO */
-	gpio = devm_gpiod_get_optional(&pdev->dev, "powerdown", GPIOD_OUT_HIGH);
-	if (IS_ERR(gpio)) {
-		dev_err(&pdev->dev, "failed to parse powerdown gpio\n");
-		return PTR_ERR(gpio);
-	}
-
-	ddata->pd_gpio = gpio;
-
-	dssdev = &ddata->dssdev;
-	dssdev->ops = &tfp410_ops;
-	dssdev->dev = &pdev->dev;
-	dssdev->type = OMAP_DISPLAY_TYPE_DPI;
-	dssdev->output_type = OMAP_DISPLAY_TYPE_DVI;
-	dssdev->owner = THIS_MODULE;
-	dssdev->of_ports = BIT(1) | BIT(0);
-	dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_SYNC_POSEDGE
-			  | DRM_BUS_FLAG_PIXDATA_POSEDGE;
-
-	dssdev->next = omapdss_of_find_connected_device(pdev->dev.of_node, 1);
-	if (IS_ERR(dssdev->next)) {
-		if (PTR_ERR(dssdev->next) != -EPROBE_DEFER)
-			dev_err(&pdev->dev, "failed to find video sink\n");
-		return PTR_ERR(dssdev->next);
-	}
-
-	omapdss_device_register(dssdev);
-
-	return 0;
-}
-
-static int __exit tfp410_remove(struct platform_device *pdev)
-{
-	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
-	struct omap_dss_device *dssdev = &ddata->dssdev;
-
-	if (dssdev->next)
-		omapdss_device_put(dssdev->next);
-	omapdss_device_unregister(&ddata->dssdev);
-
-	WARN_ON(omapdss_device_is_enabled(dssdev));
-	if (omapdss_device_is_enabled(dssdev))
-		tfp410_disable(dssdev);
-
-	WARN_ON(omapdss_device_is_connected(dssdev));
-	if (omapdss_device_is_connected(dssdev))
-		omapdss_device_disconnect(NULL, dssdev);
-
-	return 0;
-}
-
-static const struct of_device_id tfp410_of_match[] = {
-	{ .compatible = "omapdss,ti,tfp410", },
-	{},
-};
-
-MODULE_DEVICE_TABLE(of, tfp410_of_match);
-
-static struct platform_driver tfp410_driver = {
-	.probe	= tfp410_probe,
-	.remove	= __exit_p(tfp410_remove),
-	.driver	= {
-		.name	= "tfp410",
-		.of_match_table = tfp410_of_match,
-		.suppress_bind_attrs = true,
-	},
-};
-
-module_platform_driver(tfp410_driver);
-
-MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
-MODULE_DESCRIPTION("TFP410 DPI to DVI encoder driver");
-MODULE_LICENSE("GPL");

+ 0 - 40
drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c

@@ -62,35 +62,6 @@ static void tpd_disconnect(struct omap_dss_device *src,
 	omapdss_device_disconnect(dst, dst->next);
 }
 
-static int tpd_enable(struct omap_dss_device *dssdev)
-{
-	struct omap_dss_device *src = dssdev->src;
-	int r;
-
-	if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
-		return 0;
-
-	r = src->ops->enable(src);
-	if (r)
-		return r;
-
-	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-	return r;
-}
-
-static void tpd_disable(struct omap_dss_device *dssdev)
-{
-	struct omap_dss_device *src = dssdev->src;
-
-	if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
-		return;
-
-	src->ops->disable(src);
-
-	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
-}
-
 static bool tpd_detect(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
@@ -124,8 +95,6 @@ static void tpd_unregister_hpd_cb(struct omap_dss_device *dssdev)
 static const struct omap_dss_device_ops tpd_ops = {
 	.connect		= tpd_connect,
 	.disconnect		= tpd_disconnect,
-	.enable			= tpd_enable,
-	.disable		= tpd_disable,
 	.detect			= tpd_detect,
 	.register_hpd_cb	= tpd_register_hpd_cb,
 	.unregister_hpd_cb	= tpd_unregister_hpd_cb,
@@ -198,7 +167,6 @@ static int tpd_probe(struct platform_device *pdev)
 	dssdev->ops = &tpd_ops;
 	dssdev->dev = &pdev->dev;
 	dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
-	dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI;
 	dssdev->owner = THIS_MODULE;
 	dssdev->of_ports = BIT(1) | BIT(0);
 	dssdev->ops_flags = OMAP_DSS_DEVICE_OP_DETECT
@@ -225,14 +193,6 @@ static int __exit tpd_remove(struct platform_device *pdev)
 		omapdss_device_put(dssdev->next);
 	omapdss_device_unregister(&ddata->dssdev);
 
-	WARN_ON(omapdss_device_is_enabled(dssdev));
-	if (omapdss_device_is_enabled(dssdev))
-		tpd_disable(dssdev);
-
-	WARN_ON(omapdss_device_is_connected(dssdev));
-	if (omapdss_device_is_connected(dssdev))
-		omapdss_device_disconnect(NULL, dssdev);
-
 	return 0;
 }
 

+ 0 - 221
drivers/gpu/drm/omapdrm/displays/panel-dpi.c

@@ -1,221 +0,0 @@
-/*
- * Generic MIPI DPI Panel Driver
- *
- * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
- * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- */
-
-#include <linux/gpio/consumer.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/regulator/consumer.h>
-#include <linux/backlight.h>
-
-#include <video/of_display_timing.h>
-
-#include "../dss/omapdss.h"
-
-struct panel_drv_data {
-	struct omap_dss_device dssdev;
-
-	struct videomode vm;
-
-	struct backlight_device *backlight;
-
-	struct gpio_desc *enable_gpio;
-	struct regulator *vcc_supply;
-};
-
-#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
-
-static int panel_dpi_connect(struct omap_dss_device *src,
-			     struct omap_dss_device *dst)
-{
-	return 0;
-}
-
-static void panel_dpi_disconnect(struct omap_dss_device *src,
-				 struct omap_dss_device *dst)
-{
-}
-
-static int panel_dpi_enable(struct omap_dss_device *dssdev)
-{
-	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-	int r;
-
-	if (!omapdss_device_is_connected(dssdev))
-		return -ENODEV;
-
-	if (omapdss_device_is_enabled(dssdev))
-		return 0;
-
-	r = src->ops->enable(src);
-	if (r)
-		return r;
-
-	r = regulator_enable(ddata->vcc_supply);
-	if (r) {
-		src->ops->disable(src);
-		return r;
-	}
-
-	gpiod_set_value_cansleep(ddata->enable_gpio, 1);
-	backlight_enable(ddata->backlight);
-
-	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-	return 0;
-}
-
-static void panel_dpi_disable(struct omap_dss_device *dssdev)
-{
-	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-
-	if (!omapdss_device_is_enabled(dssdev))
-		return;
-
-	backlight_disable(ddata->backlight);
-
-	gpiod_set_value_cansleep(ddata->enable_gpio, 0);
-	regulator_disable(ddata->vcc_supply);
-
-	src->ops->disable(src);
-
-	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
-}
-
-static void panel_dpi_get_timings(struct omap_dss_device *dssdev,
-				  struct videomode *vm)
-{
-	struct panel_drv_data *ddata = to_panel_data(dssdev);
-
-	*vm = ddata->vm;
-}
-
-static const struct omap_dss_device_ops panel_dpi_ops = {
-	.connect	= panel_dpi_connect,
-	.disconnect	= panel_dpi_disconnect,
-
-	.enable		= panel_dpi_enable,
-	.disable	= panel_dpi_disable,
-
-	.get_timings	= panel_dpi_get_timings,
-};
-
-static int panel_dpi_probe_of(struct platform_device *pdev)
-{
-	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
-	struct device_node *node = pdev->dev.of_node;
-	int r;
-	struct display_timing timing;
-	struct gpio_desc *gpio;
-
-	gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);
-	if (IS_ERR(gpio))
-		return PTR_ERR(gpio);
-
-	ddata->enable_gpio = gpio;
-
-	/*
-	 * Many different panels are supported by this driver and there are
-	 * probably very different needs for their reset pins in regards to
-	 * timing and order relative to the enable gpio. So for now it's just
-	 * ensured that the reset line isn't active.
-	 */
-	gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
-	if (IS_ERR(gpio))
-		return PTR_ERR(gpio);
-
-	ddata->vcc_supply = devm_regulator_get(&pdev->dev, "vcc");
-	if (IS_ERR(ddata->vcc_supply))
-		return PTR_ERR(ddata->vcc_supply);
-
-	ddata->backlight = devm_of_find_backlight(&pdev->dev);
-
-	if (IS_ERR(ddata->backlight))
-		return PTR_ERR(ddata->backlight);
-
-	r = of_get_display_timing(node, "panel-timing", &timing);
-	if (r) {
-		dev_err(&pdev->dev, "failed to get video timing\n");
-		return r;
-	}
-
-	videomode_from_timing(&timing, &ddata->vm);
-
-	return 0;
-}
-
-static int panel_dpi_probe(struct platform_device *pdev)
-{
-	struct panel_drv_data *ddata;
-	struct omap_dss_device *dssdev;
-	int r;
-
-	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
-	if (ddata == NULL)
-		return -ENOMEM;
-
-	platform_set_drvdata(pdev, ddata);
-
-	r = panel_dpi_probe_of(pdev);
-	if (r)
-		return r;
-
-	dssdev = &ddata->dssdev;
-	dssdev->dev = &pdev->dev;
-	dssdev->ops = &panel_dpi_ops;
-	dssdev->type = OMAP_DISPLAY_TYPE_DPI;
-	dssdev->owner = THIS_MODULE;
-	dssdev->of_ports = BIT(0);
-	drm_bus_flags_from_videomode(&ddata->vm, &dssdev->bus_flags);
-
-	omapdss_display_init(dssdev);
-	omapdss_device_register(dssdev);
-
-	return 0;
-}
-
-static int __exit panel_dpi_remove(struct platform_device *pdev)
-{
-	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
-	struct omap_dss_device *dssdev = &ddata->dssdev;
-
-	omapdss_device_unregister(dssdev);
-
-	panel_dpi_disable(dssdev);
-
-	return 0;
-}
-
-static const struct of_device_id panel_dpi_of_match[] = {
-	{ .compatible = "omapdss,panel-dpi", },
-	{},
-};
-
-MODULE_DEVICE_TABLE(of, panel_dpi_of_match);
-
-static struct platform_driver panel_dpi_driver = {
-	.probe = panel_dpi_probe,
-	.remove = __exit_p(panel_dpi_remove),
-	.driver = {
-		.name = "panel-dpi",
-		.of_match_table = panel_dpi_of_match,
-		.suppress_bind_attrs = true,
-	},
-};
-
-module_platform_driver(panel_dpi_driver);
-
-MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
-MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");
-MODULE_LICENSE("GPL");

+ 55 - 85
drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c

@@ -24,6 +24,8 @@
 #include <linux/of_device.h>
 #include <linux/regulator/consumer.h>
 
+#include <drm/drm_connector.h>
+
 #include <video/mipi_display.h>
 #include <video/of_display_timing.h>
 
@@ -41,6 +43,7 @@
 
 struct panel_drv_data {
 	struct omap_dss_device dssdev;
+	struct omap_dss_device *src;
 
 	struct videomode vm;
 
@@ -141,7 +144,7 @@ static void hw_guard_wait(struct panel_drv_data *ddata)
 
 static int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data)
 {
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	int r;
 	u8 buf[1];
 
@@ -157,14 +160,14 @@ static int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data)
 
 static int dsicm_dcs_write_0(struct panel_drv_data *ddata, u8 dcs_cmd)
 {
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 
 	return src->ops->dsi.dcs_write(src, ddata->channel, &dcs_cmd, 1);
 }
 
 static int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param)
 {
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	u8 buf[2] = { dcs_cmd, param };
 
 	return src->ops->dsi.dcs_write(src, ddata->channel, buf, 2);
@@ -173,7 +176,7 @@ static int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param)
 static int dsicm_sleep_in(struct panel_drv_data *ddata)
 
 {
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	u8 cmd;
 	int r;
 
@@ -228,7 +231,7 @@ static int dsicm_get_id(struct panel_drv_data *ddata, u8 *id1, u8 *id2, u8 *id3)
 static int dsicm_set_update_window(struct panel_drv_data *ddata,
 		u16 x, u16 y, u16 w, u16 h)
 {
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	int r;
 	u16 x1 = x;
 	u16 x2 = x + w - 1;
@@ -275,7 +278,7 @@ static void dsicm_cancel_ulps_work(struct panel_drv_data *ddata)
 
 static int dsicm_enter_ulps(struct panel_drv_data *ddata)
 {
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	int r;
 
 	if (ddata->ulps_enabled)
@@ -309,18 +312,13 @@ err:
 
 static int dsicm_exit_ulps(struct panel_drv_data *ddata)
 {
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	int r;
 
 	if (!ddata->ulps_enabled)
 		return 0;
 
-	r = src->ops->enable(src);
-	if (r) {
-		dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
-		goto err1;
-	}
-
+	src->ops->enable(src);
 	src->ops->dsi.enable_hs(src, ddata->channel, true);
 
 	r = _dsicm_enable_te(ddata, true);
@@ -347,7 +345,7 @@ err2:
 			enable_irq(gpiod_to_irq(ddata->ext_te_gpio));
 		ddata->ulps_enabled = false;
 	}
-err1:
+
 	dsicm_queue_ulps_work(ddata);
 
 	return r;
@@ -366,7 +364,7 @@ static int dsicm_wake_up(struct panel_drv_data *ddata)
 static int dsicm_bl_update_status(struct backlight_device *dev)
 {
 	struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	int r = 0;
 	int level;
 
@@ -414,7 +412,7 @@ static ssize_t dsicm_num_errors_show(struct device *dev,
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	u8 errors = 0;
 	int r;
 
@@ -446,7 +444,7 @@ static ssize_t dsicm_hw_revision_show(struct device *dev,
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	u8 id1, id2, id3;
 	int r;
 
@@ -478,7 +476,7 @@ static ssize_t dsicm_store_ulps(struct device *dev,
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	unsigned long t;
 	int r;
 
@@ -528,7 +526,7 @@ static ssize_t dsicm_store_ulps_timeout(struct device *dev,
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	unsigned long t;
 	int r;
 
@@ -603,7 +601,7 @@ static void dsicm_hw_reset(struct panel_drv_data *ddata)
 
 static int dsicm_power_on(struct panel_drv_data *ddata)
 {
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	u8 id1, id2, id3;
 	int r;
 	struct omap_dss_dsi_config dsi_config = {
@@ -649,11 +647,7 @@ static int dsicm_power_on(struct panel_drv_data *ddata)
 		goto err_vddi;
 	}
 
-	r = src->ops->enable(src);
-	if (r) {
-		dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
-		goto err_vddi;
-	}
+	src->ops->enable(src);
 
 	dsicm_hw_reset(ddata);
 
@@ -722,7 +716,7 @@ err_vpnl:
 
 static void dsicm_power_off(struct panel_drv_data *ddata)
 {
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	int r;
 
 	src->ops->dsi.disable_video_output(src, ddata->channel);
@@ -776,6 +770,7 @@ static int dsicm_connect(struct omap_dss_device *src,
 		return r;
 	}
 
+	ddata->src = src;
 	return 0;
 }
 
@@ -785,28 +780,17 @@ static void dsicm_disconnect(struct omap_dss_device *src,
 	struct panel_drv_data *ddata = to_panel_data(dst);
 
 	src->ops->dsi.release_vc(src, ddata->channel);
+	ddata->src = NULL;
 }
 
-static int dsicm_enable(struct omap_dss_device *dssdev)
+static void dsicm_enable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
+	struct omap_dss_device *src = ddata->src;
 	int r;
 
-	dev_dbg(&ddata->pdev->dev, "enable\n");
-
 	mutex_lock(&ddata->lock);
 
-	if (!omapdss_device_is_connected(dssdev)) {
-		r = -ENODEV;
-		goto err;
-	}
-
-	if (omapdss_device_is_enabled(dssdev)) {
-		r = 0;
-		goto err;
-	}
-
 	src->ops->dsi.bus_lock(src);
 
 	r = dsicm_power_on(ddata);
@@ -816,27 +800,22 @@ static int dsicm_enable(struct omap_dss_device *dssdev)
 	if (r)
 		goto err;
 
-	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
 	mutex_unlock(&ddata->lock);
 
 	dsicm_bl_power(ddata, true);
 
-	return 0;
+	return;
 err:
-	dev_dbg(&ddata->pdev->dev, "enable failed\n");
+	dev_dbg(&ddata->pdev->dev, "enable failed (%d)\n", r);
 	mutex_unlock(&ddata->lock);
-	return r;
 }
 
 static void dsicm_disable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
+	struct omap_dss_device *src = ddata->src;
 	int r;
 
-	dev_dbg(&ddata->pdev->dev, "disable\n");
-
 	dsicm_bl_power(ddata, false);
 
 	mutex_lock(&ddata->lock);
@@ -845,23 +824,19 @@ static void dsicm_disable(struct omap_dss_device *dssdev)
 
 	src->ops->dsi.bus_lock(src);
 
-	if (omapdss_device_is_enabled(dssdev)) {
-		r = dsicm_wake_up(ddata);
-		if (!r)
-			dsicm_power_off(ddata);
-	}
+	r = dsicm_wake_up(ddata);
+	if (!r)
+		dsicm_power_off(ddata);
 
 	src->ops->dsi.bus_unlock(src);
 
-	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
-
 	mutex_unlock(&ddata->lock);
 }
 
 static void dsicm_framedone_cb(int err, void *data)
 {
 	struct panel_drv_data *ddata = data;
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 
 	dev_dbg(&ddata->pdev->dev, "framedone, err %d\n", err);
 	src->ops->dsi.bus_unlock(src);
@@ -870,7 +845,7 @@ static void dsicm_framedone_cb(int err, void *data)
 static irqreturn_t dsicm_te_isr(int irq, void *data)
 {
 	struct panel_drv_data *ddata = data;
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	int old;
 	int r;
 
@@ -896,7 +871,7 @@ static void dsicm_te_timeout_work_callback(struct work_struct *work)
 {
 	struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
 					te_timeout_work.work);
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 
 	dev_err(&ddata->pdev->dev, "TE not received for 250ms!\n");
 
@@ -908,7 +883,7 @@ static int dsicm_update(struct omap_dss_device *dssdev,
 				    u16 x, u16 y, u16 w, u16 h)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
+	struct omap_dss_device *src = ddata->src;
 	int r;
 
 	dev_dbg(&ddata->pdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
@@ -954,7 +929,7 @@ err:
 static int dsicm_sync(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
+	struct omap_dss_device *src = ddata->src;
 
 	dev_dbg(&ddata->pdev->dev, "sync\n");
 
@@ -970,7 +945,7 @@ static int dsicm_sync(struct omap_dss_device *dssdev)
 
 static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable)
 {
-	struct omap_dss_device *src = ddata->dssdev.src;
+	struct omap_dss_device *src = ddata->src;
 	int r;
 
 	if (enable)
@@ -990,7 +965,7 @@ static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable)
 static int dsicm_enable_te(struct omap_dss_device *dssdev, bool enable)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
+	struct omap_dss_device *src = ddata->src;
 	int r;
 
 	mutex_lock(&ddata->lock);
@@ -1041,7 +1016,7 @@ static int dsicm_memory_read(struct omap_dss_device *dssdev,
 		u16 x, u16 y, u16 w, u16 h)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
+	struct omap_dss_device *src = ddata->src;
 	int r;
 	int first = 1;
 	int plen;
@@ -1123,7 +1098,7 @@ static void dsicm_ulps_work(struct work_struct *work)
 	struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
 			ulps_work.work);
 	struct omap_dss_device *dssdev = &ddata->dssdev;
-	struct omap_dss_device *src = dssdev->src;
+	struct omap_dss_device *src = ddata->src;
 
 	mutex_lock(&ddata->lock);
 
@@ -1140,29 +1115,32 @@ static void dsicm_ulps_work(struct work_struct *work)
 	mutex_unlock(&ddata->lock);
 }
 
-static void dsicm_get_timings(struct omap_dss_device *dssdev,
-			      struct videomode *vm)
+static int dsicm_get_modes(struct omap_dss_device *dssdev,
+			   struct drm_connector *connector)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
 
-	*vm = ddata->vm;
+	connector->display_info.width_mm = ddata->width_mm;
+	connector->display_info.height_mm = ddata->height_mm;
+
+	return omapdss_display_get_modes(connector, &ddata->vm);
 }
 
 static int dsicm_check_timings(struct omap_dss_device *dssdev,
-			       struct videomode *vm)
+			       struct drm_display_mode *mode)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
 	int ret = 0;
 
-	if (vm->hactive != ddata->vm.hactive)
+	if (mode->hdisplay != ddata->vm.hactive)
 		ret = -EINVAL;
 
-	if (vm->vactive != ddata->vm.vactive)
+	if (mode->vdisplay != ddata->vm.vactive)
 		ret = -EINVAL;
 
 	if (ret) {
 		dev_warn(dssdev->dev, "wrong resolution: %d x %d",
-			 vm->hactive, vm->vactive);
+			 mode->hdisplay, mode->vdisplay);
 		dev_warn(dssdev->dev, "panel resolution: %d x %d",
 			 ddata->vm.hactive, ddata->vm.vactive);
 	}
@@ -1170,15 +1148,6 @@ static int dsicm_check_timings(struct omap_dss_device *dssdev,
 	return ret;
 }
 
-static void dsicm_get_size(struct omap_dss_device *dssdev,
-			  unsigned int *width, unsigned int *height)
-{
-	struct panel_drv_data *ddata = to_panel_data(dssdev);
-
-	*width = ddata->width_mm;
-	*height = ddata->height_mm;
-}
-
 static const struct omap_dss_device_ops dsicm_ops = {
 	.connect	= dsicm_connect,
 	.disconnect	= dsicm_disconnect,
@@ -1186,7 +1155,7 @@ static const struct omap_dss_device_ops dsicm_ops = {
 	.enable		= dsicm_enable,
 	.disable	= dsicm_disable,
 
-	.get_timings	= dsicm_get_timings,
+	.get_modes	= dsicm_get_modes,
 	.check_timings	= dsicm_check_timings,
 };
 
@@ -1194,8 +1163,6 @@ static const struct omap_dss_driver dsicm_dss_driver = {
 	.update		= dsicm_update,
 	.sync		= dsicm_sync,
 
-	.get_size	= dsicm_get_size,
-
 	.enable_te	= dsicm_enable_te,
 	.get_te		= dsicm_get_te,
 
@@ -1305,8 +1272,10 @@ static int dsicm_probe(struct platform_device *pdev)
 	dssdev->ops = &dsicm_ops;
 	dssdev->driver = &dsicm_dss_driver;
 	dssdev->type = OMAP_DISPLAY_TYPE_DSI;
+	dssdev->display = true;
 	dssdev->owner = THIS_MODULE;
 	dssdev->of_ports = BIT(0);
+	dssdev->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
 
 	dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
 		OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
@@ -1385,8 +1354,9 @@ static int __exit dsicm_remove(struct platform_device *pdev)
 
 	omapdss_device_unregister(dssdev);
 
-	dsicm_disable(dssdev);
-	omapdss_device_disconnect(dssdev->src, dssdev);
+	if (omapdss_device_is_enabled(dssdev))
+		dsicm_disable(dssdev);
+	omapdss_device_disconnect(ddata->src, dssdev);
 
 	sysfs_remove_group(&pdev->dev.kobj, &dsicm_attr_group);
 

+ 10 - 31
drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c

@@ -123,52 +123,28 @@ static void lb035q02_disconnect(struct omap_dss_device *src,
 {
 }
 
-static int lb035q02_enable(struct omap_dss_device *dssdev)
+static void lb035q02_enable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-	int r;
-
-	if (!omapdss_device_is_connected(dssdev))
-		return -ENODEV;
-
-	if (omapdss_device_is_enabled(dssdev))
-		return 0;
-
-	r = src->ops->enable(src);
-	if (r)
-		return r;
 
 	if (ddata->enable_gpio)
 		gpiod_set_value_cansleep(ddata->enable_gpio, 1);
-
-	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-	return 0;
 }
 
 static void lb035q02_disable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-
-	if (!omapdss_device_is_enabled(dssdev))
-		return;
 
 	if (ddata->enable_gpio)
 		gpiod_set_value_cansleep(ddata->enable_gpio, 0);
-
-	src->ops->disable(src);
-
-	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 }
 
-static void lb035q02_get_timings(struct omap_dss_device *dssdev,
-				 struct videomode *vm)
+static int lb035q02_get_modes(struct omap_dss_device *dssdev,
+			      struct drm_connector *connector)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
 
-	*vm = ddata->vm;
+	return omapdss_display_get_modes(connector, &ddata->vm);
 }
 
 static const struct omap_dss_device_ops lb035q02_ops = {
@@ -178,7 +154,7 @@ static const struct omap_dss_device_ops lb035q02_ops = {
 	.enable		= lb035q02_enable,
 	.disable	= lb035q02_disable,
 
-	.get_timings	= lb035q02_get_timings,
+	.get_modes	= lb035q02_get_modes,
 };
 
 static int lb035q02_probe_of(struct spi_device *spi)
@@ -221,16 +197,19 @@ static int lb035q02_panel_spi_probe(struct spi_device *spi)
 	dssdev->dev = &spi->dev;
 	dssdev->ops = &lb035q02_ops;
 	dssdev->type = OMAP_DISPLAY_TYPE_DPI;
+	dssdev->display = true;
 	dssdev->owner = THIS_MODULE;
 	dssdev->of_ports = BIT(0);
+	dssdev->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
 
 	/*
 	 * Note: According to the panel documentation:
 	 * DE is active LOW
 	 * DATA needs to be driven on the FALLING edge
 	 */
-	dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_SYNC_NEGEDGE
-			  | DRM_BUS_FLAG_PIXDATA_POSEDGE;
+	dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH
+			  | DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE
+			  | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
 
 	omapdss_display_init(dssdev);
 	omapdss_device_register(dssdev);

+ 10 - 31
drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c

@@ -118,50 +118,26 @@ static void nec_8048_disconnect(struct omap_dss_device *src,
 {
 }
 
-static int nec_8048_enable(struct omap_dss_device *dssdev)
+static void nec_8048_enable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-	int r;
-
-	if (!omapdss_device_is_connected(dssdev))
-		return -ENODEV;
-
-	if (omapdss_device_is_enabled(dssdev))
-		return 0;
-
-	r = src->ops->enable(src);
-	if (r)
-		return r;
 
 	gpiod_set_value_cansleep(ddata->res_gpio, 1);
-
-	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-	return 0;
 }
 
 static void nec_8048_disable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-
-	if (!omapdss_device_is_enabled(dssdev))
-		return;
 
 	gpiod_set_value_cansleep(ddata->res_gpio, 0);
-
-	src->ops->disable(src);
-
-	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 }
 
-static void nec_8048_get_timings(struct omap_dss_device *dssdev,
-				 struct videomode *vm)
+static int nec_8048_get_modes(struct omap_dss_device *dssdev,
+			      struct drm_connector *connector)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
 
-	*vm = ddata->vm;
+	return omapdss_display_get_modes(connector, &ddata->vm);
 }
 
 static const struct omap_dss_device_ops nec_8048_ops = {
@@ -171,7 +147,7 @@ static const struct omap_dss_device_ops nec_8048_ops = {
 	.enable		= nec_8048_enable,
 	.disable	= nec_8048_disable,
 
-	.get_timings	= nec_8048_get_timings,
+	.get_modes	= nec_8048_get_modes,
 };
 
 static int nec_8048_probe(struct spi_device *spi)
@@ -216,10 +192,13 @@ static int nec_8048_probe(struct spi_device *spi)
 	dssdev->dev = &spi->dev;
 	dssdev->ops = &nec_8048_ops;
 	dssdev->type = OMAP_DISPLAY_TYPE_DPI;
+	dssdev->display = true;
 	dssdev->owner = THIS_MODULE;
 	dssdev->of_ports = BIT(0);
-	dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_SYNC_POSEDGE
-			  | DRM_BUS_FLAG_PIXDATA_POSEDGE;
+	dssdev->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
+	dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH
+			  | DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE
+			  | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
 
 	omapdss_display_init(dssdev);
 	omapdss_device_register(dssdev);

+ 27 - 34
drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c

@@ -62,29 +62,22 @@ static void sharp_ls_disconnect(struct omap_dss_device *src,
 {
 }
 
-static int sharp_ls_enable(struct omap_dss_device *dssdev)
+static void sharp_ls_pre_enable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
 	int r;
 
-	if (!omapdss_device_is_connected(dssdev))
-		return -ENODEV;
-
-	if (omapdss_device_is_enabled(dssdev))
-		return 0;
-
 	if (ddata->vcc) {
 		r = regulator_enable(ddata->vcc);
-		if (r != 0)
-			return r;
+		if (r)
+			dev_err(dssdev->dev, "%s: failed to enable regulator\n",
+				__func__);
 	}
+}
 
-	r = src->ops->enable(src);
-	if (r) {
-		regulator_disable(ddata->vcc);
-		return r;
-	}
+static void sharp_ls_enable(struct omap_dss_device *dssdev)
+{
+	struct panel_drv_data *ddata = to_panel_data(dssdev);
 
 	/* wait couple of vsyncs until enabling the LCD */
 	msleep(50);
@@ -94,19 +87,11 @@ static int sharp_ls_enable(struct omap_dss_device *dssdev)
 
 	if (ddata->ini_gpio)
 		gpiod_set_value_cansleep(ddata->ini_gpio, 1);
-
-	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-	return 0;
 }
 
 static void sharp_ls_disable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-
-	if (!omapdss_device_is_enabled(dssdev))
-		return;
 
 	if (ddata->ini_gpio)
 		gpiod_set_value_cansleep(ddata->ini_gpio, 0);
@@ -115,33 +100,35 @@ static void sharp_ls_disable(struct omap_dss_device *dssdev)
 		gpiod_set_value_cansleep(ddata->resb_gpio, 0);
 
 	/* wait at least 5 vsyncs after disabling the LCD */
-
 	msleep(100);
+}
 
-	src->ops->disable(src);
+static void sharp_ls_post_disable(struct omap_dss_device *dssdev)
+{
+	struct panel_drv_data *ddata = to_panel_data(dssdev);
 
 	if (ddata->vcc)
 		regulator_disable(ddata->vcc);
-
-	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 }
 
-static void sharp_ls_get_timings(struct omap_dss_device *dssdev,
-				 struct videomode *vm)
+static int sharp_ls_get_modes(struct omap_dss_device *dssdev,
+			      struct drm_connector *connector)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
 
-	*vm = ddata->vm;
+	return omapdss_display_get_modes(connector, &ddata->vm);
 }
 
 static const struct omap_dss_device_ops sharp_ls_ops = {
 	.connect	= sharp_ls_connect,
 	.disconnect	= sharp_ls_disconnect,
 
+	.pre_enable	= sharp_ls_pre_enable,
 	.enable		= sharp_ls_enable,
 	.disable	= sharp_ls_disable,
+	.post_disable	= sharp_ls_post_disable,
 
-	.get_timings	= sharp_ls_get_timings,
+	.get_modes	= sharp_ls_get_modes,
 };
 
 static  int sharp_ls_get_gpio_of(struct device *dev, int index, int val,
@@ -220,15 +207,18 @@ static int sharp_ls_probe(struct platform_device *pdev)
 	dssdev->dev = &pdev->dev;
 	dssdev->ops = &sharp_ls_ops;
 	dssdev->type = OMAP_DISPLAY_TYPE_DPI;
+	dssdev->display = true;
 	dssdev->owner = THIS_MODULE;
 	dssdev->of_ports = BIT(0);
+	dssdev->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
 
 	/*
 	 * Note: According to the panel documentation:
 	 * DATA needs to be driven on the FALLING edge
 	 */
-	dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_SYNC_NEGEDGE
-			  | DRM_BUS_FLAG_PIXDATA_POSEDGE;
+	dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH
+			  | DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE
+			  | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
 
 	omapdss_display_init(dssdev);
 	omapdss_device_register(dssdev);
@@ -243,7 +233,10 @@ static int __exit sharp_ls_remove(struct platform_device *pdev)
 
 	omapdss_device_unregister(dssdev);
 
-	sharp_ls_disable(dssdev);
+	if (omapdss_device_is_enabled(dssdev)) {
+		sharp_ls_disable(dssdev);
+		sharp_ls_post_disable(dssdev);
+	}
 
 	return 0;
 }

+ 13 - 42
drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c

@@ -516,17 +516,9 @@ static void acx565akm_disconnect(struct omap_dss_device *src,
 static int acx565akm_panel_power_on(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-	int r;
 
 	dev_dbg(&ddata->spi->dev, "%s\n", __func__);
 
-	r = src->ops->enable(src);
-	if (r) {
-		pr_err("%s sdi enable failed\n", __func__);
-		return r;
-	}
-
 	/*FIXME tweak me */
 	msleep(50);
 
@@ -562,7 +554,6 @@ static int acx565akm_panel_power_on(struct omap_dss_device *dssdev)
 static void acx565akm_panel_power_off(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
 
 	dev_dbg(dssdev->dev, "%s\n", __func__);
 
@@ -585,56 +576,32 @@ static void acx565akm_panel_power_off(struct omap_dss_device *dssdev)
 
 	/* FIXME need to tweak this delay */
 	msleep(100);
-
-	src->ops->disable(src);
 }
 
-static int acx565akm_enable(struct omap_dss_device *dssdev)
+static void acx565akm_enable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	int r;
-
-	dev_dbg(dssdev->dev, "%s\n", __func__);
-
-	if (!omapdss_device_is_connected(dssdev))
-		return -ENODEV;
-
-	if (omapdss_device_is_enabled(dssdev))
-		return 0;
 
 	mutex_lock(&ddata->mutex);
-	r = acx565akm_panel_power_on(dssdev);
+	acx565akm_panel_power_on(dssdev);
 	mutex_unlock(&ddata->mutex);
-	if (r)
-		return r;
-
-	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-	return 0;
 }
 
 static void acx565akm_disable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
 
-	dev_dbg(dssdev->dev, "%s\n", __func__);
-
-	if (!omapdss_device_is_enabled(dssdev))
-		return;
-
 	mutex_lock(&ddata->mutex);
 	acx565akm_panel_power_off(dssdev);
 	mutex_unlock(&ddata->mutex);
-
-	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 }
 
-static void acx565akm_get_timings(struct omap_dss_device *dssdev,
-				  struct videomode *vm)
+static int acx565akm_get_modes(struct omap_dss_device *dssdev,
+			       struct drm_connector *connector)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
 
-	*vm = ddata->vm;
+	return omapdss_display_get_modes(connector, &ddata->vm);
 }
 
 static const struct omap_dss_device_ops acx565akm_ops = {
@@ -644,7 +611,7 @@ static const struct omap_dss_device_ops acx565akm_ops = {
 	.enable		= acx565akm_enable,
 	.disable	= acx565akm_disable,
 
-	.get_timings	= acx565akm_get_timings,
+	.get_modes	= acx565akm_get_modes,
 };
 
 static int acx565akm_probe(struct spi_device *spi)
@@ -739,10 +706,13 @@ static int acx565akm_probe(struct spi_device *spi)
 	dssdev->dev = &spi->dev;
 	dssdev->ops = &acx565akm_ops;
 	dssdev->type = OMAP_DISPLAY_TYPE_SDI;
+	dssdev->display = true;
 	dssdev->owner = THIS_MODULE;
 	dssdev->of_ports = BIT(0);
-	dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_SYNC_NEGEDGE
-			  | DRM_BUS_FLAG_PIXDATA_POSEDGE;
+	dssdev->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
+	dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH
+			  | DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE
+			  | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
 
 	omapdss_display_init(dssdev);
 	omapdss_device_register(dssdev);
@@ -766,7 +736,8 @@ static int acx565akm_remove(struct spi_device *spi)
 
 	omapdss_device_unregister(dssdev);
 
-	acx565akm_disable(dssdev);
+	if (omapdss_device_is_enabled(dssdev))
+		acx565akm_disable(dssdev);
 
 	return 0;
 }

+ 16 - 36
drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c

@@ -169,24 +169,12 @@ static void td028ttec1_panel_disconnect(struct omap_dss_device *src,
 {
 }
 
-static int td028ttec1_panel_enable(struct omap_dss_device *dssdev)
+static void td028ttec1_panel_enable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-	int r;
-
-	if (!omapdss_device_is_connected(dssdev))
-		return -ENODEV;
+	int r = 0;
 
-	if (omapdss_device_is_enabled(dssdev))
-		return 0;
-
-	r = src->ops->enable(src);
-	if (r)
-		return r;
-
-	dev_dbg(dssdev->dev, "td028ttec1_panel_enable() - state %d\n",
-		dssdev->state);
+	dev_dbg(dssdev->dev, "%s: state %d\n", __func__, dssdev->state);
 
 	/* three times command zero */
 	r |= jbt_ret_write_0(ddata, 0x00);
@@ -197,8 +185,8 @@ static int td028ttec1_panel_enable(struct omap_dss_device *dssdev)
 	usleep_range(1000, 2000);
 
 	if (r) {
-		dev_warn(dssdev->dev, "transfer error\n");
-		goto transfer_err;
+		dev_warn(dssdev->dev, "%s: transfer error\n", __func__);
+		return;
 	}
 
 	/* deep standby out */
@@ -268,20 +256,13 @@ static int td028ttec1_panel_enable(struct omap_dss_device *dssdev)
 
 	r |= jbt_ret_write_0(ddata, JBT_REG_DISPLAY_ON);
 
-	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-transfer_err:
-
-	return r ? -EIO : 0;
+	if (r)
+		dev_err(dssdev->dev, "%s: write error\n", __func__);
 }
 
 static void td028ttec1_panel_disable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-
-	if (!omapdss_device_is_enabled(dssdev))
-		return;
 
 	dev_dbg(dssdev->dev, "td028ttec1_panel_disable()\n");
 
@@ -289,18 +270,14 @@ static void td028ttec1_panel_disable(struct omap_dss_device *dssdev)
 	jbt_reg_write_2(ddata, JBT_REG_OUTPUT_CONTROL, 0x8002);
 	jbt_ret_write_0(ddata, JBT_REG_SLEEP_IN);
 	jbt_reg_write_1(ddata, JBT_REG_POWER_ON_OFF, 0x00);
-
-	src->ops->disable(src);
-
-	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 }
 
-static void td028ttec1_panel_get_timings(struct omap_dss_device *dssdev,
-					 struct videomode *vm)
+static int td028ttec1_panel_get_modes(struct omap_dss_device *dssdev,
+				      struct drm_connector *connector)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
 
-	*vm = ddata->vm;
+	return omapdss_display_get_modes(connector, &ddata->vm);
 }
 
 static const struct omap_dss_device_ops td028ttec1_ops = {
@@ -310,7 +287,7 @@ static const struct omap_dss_device_ops td028ttec1_ops = {
 	.enable		= td028ttec1_panel_enable,
 	.disable	= td028ttec1_panel_disable,
 
-	.get_timings	= td028ttec1_panel_get_timings,
+	.get_modes	= td028ttec1_panel_get_modes,
 };
 
 static int td028ttec1_panel_probe(struct spi_device *spi)
@@ -344,15 +321,18 @@ static int td028ttec1_panel_probe(struct spi_device *spi)
 	dssdev->dev = &spi->dev;
 	dssdev->ops = &td028ttec1_ops;
 	dssdev->type = OMAP_DISPLAY_TYPE_DPI;
+	dssdev->display = true;
 	dssdev->owner = THIS_MODULE;
 	dssdev->of_ports = BIT(0);
+	dssdev->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
 
 	/*
 	 * Note: According to the panel documentation:
 	 * SYNC needs to be driven on the FALLING edge
 	 */
-	dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_SYNC_POSEDGE
-			  | DRM_BUS_FLAG_PIXDATA_NEGEDGE;
+	dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH
+			  | DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE
+			  | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
 
 	omapdss_display_init(dssdev);
 	omapdss_device_register(dssdev);

+ 15 - 33
drivers/gpu/drm/omapdrm/displays/panel-tpo-td043mtea1.c

@@ -320,22 +320,11 @@ static void tpo_td043_disconnect(struct omap_dss_device *src,
 {
 }
 
-static int tpo_td043_enable(struct omap_dss_device *dssdev)
+static void tpo_td043_enable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
 	int r;
 
-	if (!omapdss_device_is_connected(dssdev))
-		return -ENODEV;
-
-	if (omapdss_device_is_enabled(dssdev))
-		return 0;
-
-	r = src->ops->enable(src);
-	if (r)
-		return r;
-
 	/*
 	 * If we are resuming from system suspend, SPI clocks might not be
 	 * enabled yet, so we'll program the LCD from SPI PM resume callback.
@@ -343,38 +332,27 @@ static int tpo_td043_enable(struct omap_dss_device *dssdev)
 	if (!ddata->spi_suspended) {
 		r = tpo_td043_power_on(ddata);
 		if (r) {
-			src->ops->disable(src);
-			return r;
+			dev_err(&ddata->spi->dev, "%s: power on failed (%d)\n",
+				__func__, r);
+			return;
 		}
 	}
-
-	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
-	return 0;
 }
 
 static void tpo_td043_disable(struct omap_dss_device *dssdev)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
-	struct omap_dss_device *src = dssdev->src;
-
-	if (!omapdss_device_is_enabled(dssdev))
-		return;
-
-	src->ops->disable(src);
 
 	if (!ddata->spi_suspended)
 		tpo_td043_power_off(ddata);
-
-	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 }
 
-static void tpo_td043_get_timings(struct omap_dss_device *dssdev,
-				  struct videomode *vm)
+static int tpo_td043_get_modes(struct omap_dss_device *dssdev,
+			       struct drm_connector *connector)
 {
 	struct panel_drv_data *ddata = to_panel_data(dssdev);
 
-	*vm = ddata->vm;
+	return omapdss_display_get_modes(connector, &ddata->vm);
 }
 
 static const struct omap_dss_device_ops tpo_td043_ops = {
@@ -384,7 +362,7 @@ static const struct omap_dss_device_ops tpo_td043_ops = {
 	.enable		= tpo_td043_enable,
 	.disable	= tpo_td043_disable,
 
-	.get_timings	= tpo_td043_get_timings,
+	.get_modes	= tpo_td043_get_modes,
 };
 
 static int tpo_td043_probe(struct spi_device *spi)
@@ -442,15 +420,18 @@ static int tpo_td043_probe(struct spi_device *spi)
 	dssdev->dev = &spi->dev;
 	dssdev->ops = &tpo_td043_ops;
 	dssdev->type = OMAP_DISPLAY_TYPE_DPI;
+	dssdev->display = true;
 	dssdev->owner = THIS_MODULE;
 	dssdev->of_ports = BIT(0);
+	dssdev->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
 
 	/*
 	 * Note: According to the panel documentation:
 	 * SYNC needs to be driven on the FALLING edge
 	 */
-	dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_SYNC_POSEDGE
-			  | DRM_BUS_FLAG_PIXDATA_NEGEDGE;
+	dssdev->bus_flags = DRM_BUS_FLAG_DE_HIGH
+			  | DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE
+			  | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
 
 	omapdss_display_init(dssdev);
 	omapdss_device_register(dssdev);
@@ -467,7 +448,8 @@ static int tpo_td043_remove(struct spi_device *spi)
 
 	omapdss_device_unregister(dssdev);
 
-	tpo_td043_disable(dssdev);
+	if (omapdss_device_is_enabled(dssdev))
+		tpo_td043_disable(dssdev);
 
 	sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group);
 

+ 102 - 42
drivers/gpu/drm/omapdrm/dss/base.c

@@ -19,6 +19,7 @@
 #include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/of_graph.h>
+#include <linux/platform_device.h>
 
 #include "dss.h"
 #include "omapdss.h"
@@ -112,13 +113,12 @@ void omapdss_device_put(struct omap_dss_device *dssdev)
 }
 EXPORT_SYMBOL(omapdss_device_put);
 
-struct omap_dss_device *omapdss_find_device_by_port(struct device_node *src,
-						    unsigned int port)
+struct omap_dss_device *omapdss_find_device_by_node(struct device_node *node)
 {
 	struct omap_dss_device *dssdev;
 
 	list_for_each_entry(dssdev, &omapdss_devices_list, list) {
-		if (dssdev->dev->of_node == src && dssdev->of_ports & BIT(port))
+		if (dssdev->dev->of_node == node)
 			return omapdss_device_get(dssdev);
 	}
 
@@ -126,13 +126,10 @@ struct omap_dss_device *omapdss_find_device_by_port(struct device_node *src,
 }
 
 /*
- * Search for the next device starting at @from. The type argument specfies
- * which device types to consider when searching. Searching for multiple types
- * is supported by and'ing their type flags. Release the reference to the @from
- * device, and acquire a reference to the returned device if found.
+ * Search for the next output device starting at @from. Release the reference to
+ * the @from device, and acquire a reference to the returned device if found.
  */
-struct omap_dss_device *omapdss_device_get_next(struct omap_dss_device *from,
-						enum omap_dss_device_type type)
+struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from)
 {
 	struct omap_dss_device *dssdev;
 	struct list_head *list;
@@ -160,15 +157,8 @@ struct omap_dss_device *omapdss_device_get_next(struct omap_dss_device *from,
 			goto done;
 		}
 
-		/*
-		 * Accept display entities if the display type is requested,
-		 * and output entities if the output type is requested.
-		 */
-		if ((type & OMAP_DSS_DEVICE_TYPE_DISPLAY) &&
-		    !dssdev->output_type)
-			goto done;
-		if ((type & OMAP_DSS_DEVICE_TYPE_OUTPUT) && dssdev->id &&
-		    dssdev->next)
+		if (dssdev->id &&
+		    (dssdev->next || dssdev->bridge || dssdev->panel))
 			goto done;
 	}
 
@@ -183,7 +173,12 @@ done:
 	mutex_unlock(&omapdss_devices_lock);
 	return dssdev;
 }
-EXPORT_SYMBOL(omapdss_device_get_next);
+EXPORT_SYMBOL(omapdss_device_next_output);
+
+static bool omapdss_device_is_connected(struct omap_dss_device *dssdev)
+{
+	return dssdev->dss;
+}
 
 int omapdss_device_connect(struct dss_device *dss,
 			   struct omap_dss_device *src,
@@ -191,7 +186,19 @@ int omapdss_device_connect(struct dss_device *dss,
 {
 	int ret;
 
-	dev_dbg(dst->dev, "connect\n");
+	dev_dbg(&dss->pdev->dev, "connect(%s, %s)\n",
+		src ? dev_name(src->dev) : "NULL",
+		dst ? dev_name(dst->dev) : "NULL");
+
+	if (!dst) {
+		/*
+		 * The destination is NULL when the source is connected to a
+		 * bridge or panel instead of a DSS device. Stop here, we will
+		 * attach the bridge or panel later when we will have a DRM
+		 * encoder.
+		 */
+		return src && (src->bridge || src->panel) ? 0 : -EINVAL;
+	}
 
 	if (omapdss_device_is_connected(dst))
 		return -EBUSY;
@@ -204,12 +211,6 @@ int omapdss_device_connect(struct dss_device *dss,
 		return ret;
 	}
 
-	if (src) {
-		WARN_ON(src->dst);
-		dst->src = src;
-		src->dst = dst;
-	}
-
 	return 0;
 }
 EXPORT_SYMBOL_GPL(omapdss_device_connect);
@@ -217,19 +218,20 @@ EXPORT_SYMBOL_GPL(omapdss_device_connect);
 void omapdss_device_disconnect(struct omap_dss_device *src,
 			       struct omap_dss_device *dst)
 {
-	dev_dbg(dst->dev, "disconnect\n");
+	struct dss_device *dss = src ? src->dss : dst->dss;
 
-	if (!dst->id && !omapdss_device_is_connected(dst)) {
-		WARN_ON(dst->output_type);
+	dev_dbg(&dss->pdev->dev, "disconnect(%s, %s)\n",
+		src ? dev_name(src->dev) : "NULL",
+		dst ? dev_name(dst->dev) : "NULL");
+
+	if (!dst) {
+		WARN_ON(!src->bridge && !src->panel);
 		return;
 	}
 
-	if (src) {
-		if (WARN_ON(dst != src->dst))
-			return;
-
-		dst->src = NULL;
-		src->dst = NULL;
+	if (!dst->id && !omapdss_device_is_connected(dst)) {
+		WARN_ON(!dst->display);
+		return;
 	}
 
 	WARN_ON(dst->state != OMAP_DSS_DISPLAY_DISABLED);
@@ -239,6 +241,58 @@ void omapdss_device_disconnect(struct omap_dss_device *src,
 }
 EXPORT_SYMBOL_GPL(omapdss_device_disconnect);
 
+void omapdss_device_pre_enable(struct omap_dss_device *dssdev)
+{
+	if (!dssdev)
+		return;
+
+	omapdss_device_pre_enable(dssdev->next);
+
+	if (dssdev->ops->pre_enable)
+		dssdev->ops->pre_enable(dssdev);
+}
+EXPORT_SYMBOL_GPL(omapdss_device_pre_enable);
+
+void omapdss_device_enable(struct omap_dss_device *dssdev)
+{
+	if (!dssdev)
+		return;
+
+	if (dssdev->ops->enable)
+		dssdev->ops->enable(dssdev);
+
+	omapdss_device_enable(dssdev->next);
+
+	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+}
+EXPORT_SYMBOL_GPL(omapdss_device_enable);
+
+void omapdss_device_disable(struct omap_dss_device *dssdev)
+{
+	if (!dssdev)
+		return;
+
+	omapdss_device_disable(dssdev->next);
+
+	if (dssdev->ops->disable)
+		dssdev->ops->disable(dssdev);
+}
+EXPORT_SYMBOL_GPL(omapdss_device_disable);
+
+void omapdss_device_post_disable(struct omap_dss_device *dssdev)
+{
+	if (!dssdev)
+		return;
+
+	if (dssdev->ops->post_disable)
+		dssdev->ops->post_disable(dssdev);
+
+	omapdss_device_post_disable(dssdev->next);
+
+	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+EXPORT_SYMBOL_GPL(omapdss_device_post_disable);
+
 /* -----------------------------------------------------------------------------
  * Components Handling
  */
@@ -249,6 +303,7 @@ struct omapdss_comp_node {
 	struct list_head list;
 	struct device_node *node;
 	bool dss_core_component;
+	const char *compat;
 };
 
 static bool omapdss_list_contains(const struct device_node *node)
@@ -266,13 +321,20 @@ static bool omapdss_list_contains(const struct device_node *node)
 static void omapdss_walk_device(struct device *dev, struct device_node *node,
 				bool dss_core)
 {
+	struct omapdss_comp_node *comp;
 	struct device_node *n;
-	struct omapdss_comp_node *comp = devm_kzalloc(dev, sizeof(*comp),
-						      GFP_KERNEL);
+	const char *compat;
+	int ret;
 
+	ret = of_property_read_string(node, "compatible", &compat);
+	if (ret < 0)
+		return;
+
+	comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
 	if (comp) {
 		comp->node = node;
 		comp->dss_core_component = dss_core;
+		comp->compat = compat;
 		list_add(&comp->list, &omapdss_comp_list);
 	}
 
@@ -312,12 +374,8 @@ void omapdss_gather_components(struct device *dev)
 
 	omapdss_walk_device(dev, dev->of_node, true);
 
-	for_each_available_child_of_node(dev->of_node, child) {
-		if (!of_find_property(child, "compatible", NULL))
-			continue;
-
+	for_each_available_child_of_node(dev->of_node, child)
 		omapdss_walk_device(dev, child, true);
-	}
 }
 EXPORT_SYMBOL(omapdss_gather_components);
 
@@ -325,6 +383,8 @@ static bool omapdss_component_is_loaded(struct omapdss_comp_node *comp)
 {
 	if (comp->dss_core_component)
 		return true;
+	if (!strstarts(comp->compat, "omapdss,"))
+		return true;
 	if (omapdss_device_is_registered(comp->node))
 		return true;
 

+ 2 - 2
drivers/gpu/drm/omapdrm/dss/dispc.c

@@ -3080,7 +3080,7 @@ static void dispc_mgr_setup(struct dispc_device *dispc,
 				info->trans_key);
 	dispc_mgr_enable_trans_key(dispc, channel, info->trans_enabled);
 	dispc_mgr_enable_alpha_fixed_zorder(dispc, channel,
-			info->partial_alpha_enabled);
+			info->alpha_blender_enabled);
 	if (dispc_has_feature(dispc, FEAT_CPR)) {
 		dispc_mgr_enable_cpr(dispc, channel, info->cpr_enable);
 		dispc_mgr_set_cpr_coef(dispc, channel, &info->cpr_coefs);
@@ -4711,7 +4711,7 @@ static const struct dispc_errata_i734_data {
 	.mgri = {
 		.default_color = 0,
 		.trans_enabled = false,
-		.partial_alpha_enabled = false,
+		.alpha_blender_enabled = false,
 		.cpr_enable = false,
 	},
 	.lcd_conf = {

+ 22 - 2
drivers/gpu/drm/omapdrm/dss/display.c

@@ -23,6 +23,9 @@
 #include <linux/kernel.h>
 #include <linux/of.h>
 
+#include <drm/drm_connector.h>
+#include <drm/drm_modes.h>
+
 #include "omapdss.h"
 
 static int disp_num_counter;
@@ -39,8 +42,6 @@ void omapdss_display_init(struct omap_dss_device *dssdev)
 	if (id < 0)
 		id = disp_num_counter++;
 
-	dssdev->alias_id = id;
-
 	/* Use 'label' property for name, if it exists */
 	of_property_read_string(dssdev->dev->of_node, "label", &dssdev->name);
 
@@ -58,3 +59,22 @@ struct omap_dss_device *omapdss_display_get(struct omap_dss_device *output)
 	return omapdss_device_get(output);
 }
 EXPORT_SYMBOL_GPL(omapdss_display_get);
+
+int omapdss_display_get_modes(struct drm_connector *connector,
+			      const struct videomode *vm)
+{
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_create(connector->dev);
+	if (!mode)
+		return 0;
+
+	drm_display_mode_from_videomode(vm, mode);
+
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	return 1;
+}
+EXPORT_SYMBOL_GPL(omapdss_display_get_modes);

+ 18 - 46
drivers/gpu/drm/omapdrm/dss/dpi.c

@@ -47,8 +47,8 @@ struct dpi_data {
 
 	struct mutex lock;
 
-	struct videomode vm;
 	struct dss_lcd_mgr_config mgr_config;
+	unsigned long pixelclock;
 	int data_lines;
 
 	struct omap_dss_device output;
@@ -347,16 +347,15 @@ static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req,
 
 static int dpi_set_mode(struct dpi_data *dpi)
 {
-	const struct videomode *vm = &dpi->vm;
 	int lck_div = 0, pck_div = 0;
 	unsigned long fck = 0;
 	int r = 0;
 
 	if (dpi->pll)
 		r = dpi_set_pll_clk(dpi, dpi->output.dispc_channel,
-				    vm->pixelclock, &fck, &lck_div, &pck_div);
+				    dpi->pixelclock, &fck, &lck_div, &pck_div);
 	else
-		r = dpi_set_dispc_clk(dpi, vm->pixelclock, &fck,
+		r = dpi_set_dispc_clk(dpi, dpi->pixelclock, &fck,
 				&lck_div, &pck_div);
 	if (r)
 		return r;
@@ -378,7 +377,7 @@ static void dpi_config_lcd_manager(struct dpi_data *dpi)
 	dss_mgr_set_lcd_config(&dpi->output, &dpi->mgr_config);
 }
 
-static int dpi_display_enable(struct omap_dss_device *dssdev)
+static void dpi_display_enable(struct omap_dss_device *dssdev)
 {
 	struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
 	struct omap_dss_device *out = &dpi->output;
@@ -386,12 +385,6 @@ static int dpi_display_enable(struct omap_dss_device *dssdev)
 
 	mutex_lock(&dpi->lock);
 
-	if (!out->dispc_channel_connected) {
-		DSSERR("failed to enable display: no output/manager\n");
-		r = -ENODEV;
-		goto err_no_out_mgr;
-	}
-
 	if (dpi->vdds_dsi_reg) {
 		r = regulator_enable(dpi->vdds_dsi_reg);
 		if (r)
@@ -426,7 +419,7 @@ static int dpi_display_enable(struct omap_dss_device *dssdev)
 
 	mutex_unlock(&dpi->lock);
 
-	return 0;
+	return;
 
 err_mgr_enable:
 err_set_mode:
@@ -439,9 +432,7 @@ err_get_dispc:
 	if (dpi->vdds_dsi_reg)
 		regulator_disable(dpi->vdds_dsi_reg);
 err_reg_enable:
-err_no_out_mgr:
 	mutex_unlock(&dpi->lock);
-	return r;
 }
 
 static void dpi_display_disable(struct omap_dss_device *dssdev)
@@ -467,7 +458,7 @@ static void dpi_display_disable(struct omap_dss_device *dssdev)
 }
 
 static void dpi_set_timings(struct omap_dss_device *dssdev,
-			    const struct videomode *vm)
+			    const struct drm_display_mode *mode)
 {
 	struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
 
@@ -475,13 +466,13 @@ static void dpi_set_timings(struct omap_dss_device *dssdev,
 
 	mutex_lock(&dpi->lock);
 
-	dpi->vm = *vm;
+	dpi->pixelclock = mode->clock * 1000;
 
 	mutex_unlock(&dpi->lock);
 }
 
 static int dpi_check_timings(struct omap_dss_device *dssdev,
-			     struct videomode *vm)
+			     struct drm_display_mode *mode)
 {
 	struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);
 	int lck_div, pck_div;
@@ -490,20 +481,20 @@ static int dpi_check_timings(struct omap_dss_device *dssdev,
 	struct dpi_clk_calc_ctx ctx;
 	bool ok;
 
-	if (vm->hactive % 8 != 0)
+	if (mode->hdisplay % 8 != 0)
 		return -EINVAL;
 
-	if (vm->pixelclock == 0)
+	if (mode->clock == 0)
 		return -EINVAL;
 
 	if (dpi->pll) {
-		ok = dpi_pll_clk_calc(dpi, vm->pixelclock, &ctx);
+		ok = dpi_pll_clk_calc(dpi, mode->clock * 1000, &ctx);
 		if (!ok)
 			return -EINVAL;
 
 		fck = ctx.pll_cinfo.clkout[ctx.clkout_idx];
 	} else {
-		ok = dpi_dss_clk_calc(dpi, vm->pixelclock, &ctx);
+		ok = dpi_dss_clk_calc(dpi, mode->clock * 1000, &ctx);
 		if (!ok)
 			return -EINVAL;
 
@@ -515,7 +506,7 @@ static int dpi_check_timings(struct omap_dss_device *dssdev,
 
 	pck = fck / lck_div / pck_div;
 
-	vm->pixelclock = pck;
+	mode->clock = pck / 1000;
 
 	return 0;
 }
@@ -596,23 +587,15 @@ static int dpi_connect(struct omap_dss_device *src,
 		       struct omap_dss_device *dst)
 {
 	struct dpi_data *dpi = dpi_get_data_from_dssdev(dst);
-	int r;
 
 	dpi_init_pll(dpi);
 
-	r = omapdss_device_connect(dst->dss, dst, dst->next);
-	if (r)
-		return r;
-
-	dst->dispc_channel_connected = true;
-	return 0;
+	return omapdss_device_connect(dst->dss, dst, dst->next);
 }
 
 static void dpi_disconnect(struct omap_dss_device *src,
 			   struct omap_dss_device *dst)
 {
-	dst->dispc_channel_connected = false;
-
 	omapdss_device_disconnect(dst, dst->next);
 }
 
@@ -651,25 +634,15 @@ static int dpi_init_output_port(struct dpi_data *dpi, struct device_node *port)
 
 	out->dev = &dpi->pdev->dev;
 	out->id = OMAP_DSS_OUTPUT_DPI;
-	out->output_type = OMAP_DISPLAY_TYPE_DPI;
+	out->type = OMAP_DISPLAY_TYPE_DPI;
 	out->dispc_channel = dpi_get_channel(dpi);
 	out->of_ports = BIT(port_num);
 	out->ops = &dpi_ops;
 	out->owner = THIS_MODULE;
 
-	out->next = omapdss_of_find_connected_device(out->dev->of_node, 0);
-	if (IS_ERR(out->next)) {
-		if (PTR_ERR(out->next) != -EPROBE_DEFER)
-			dev_err(out->dev, "failed to find video sink\n");
-		return PTR_ERR(out->next);
-	}
-
-	r = omapdss_output_validate(out);
-	if (r) {
-		omapdss_device_put(out->next);
-		out->next = NULL;
+	r = omapdss_device_init_output(out);
+	if (r < 0)
 		return r;
-	}
 
 	omapdss_device_register(out);
 
@@ -681,9 +654,8 @@ static void dpi_uninit_output_port(struct device_node *port)
 	struct dpi_data *dpi = port->data;
 	struct omap_dss_device *out = &dpi->output;
 
-	if (out->next)
-		omapdss_device_put(out->next);
 	omapdss_device_unregister(out);
+	omapdss_device_cleanup_output(out);
 }
 
 static const struct soc_device_attribute dpi_soc_devices[] = {

+ 30 - 47
drivers/gpu/drm/omapdrm/dss/dsi.c

@@ -1406,7 +1406,7 @@ static void dsi_pll_disable(struct dss_pll *pll)
 
 static int dsi_dump_dsi_clocks(struct seq_file *s, void *p)
 {
-	struct dsi_data *dsi = p;
+	struct dsi_data *dsi = s->private;
 	struct dss_pll_clock_info *cinfo = &dsi->pll.cinfo;
 	enum dss_clk_source dispc_clk_src, dsi_clk_src;
 	int dsi_module = dsi->module_id;
@@ -1467,7 +1467,7 @@ static int dsi_dump_dsi_clocks(struct seq_file *s, void *p)
 #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
 static int dsi_dump_dsi_irqs(struct seq_file *s, void *p)
 {
-	struct dsi_data *dsi = p;
+	struct dsi_data *dsi = s->private;
 	unsigned long flags;
 	struct dsi_irq_stats stats;
 
@@ -1558,7 +1558,7 @@ static int dsi_dump_dsi_irqs(struct seq_file *s, void *p)
 
 static int dsi_dump_dsi_regs(struct seq_file *s, void *p)
 {
-	struct dsi_data *dsi = p;
+	struct dsi_data *dsi = s->private;
 
 	if (dsi_runtime_get(dsi))
 		return 0;
@@ -3753,19 +3753,13 @@ static int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel)
 {
 	struct dsi_data *dsi = to_dsi_data(dssdev);
 	int bpp = dsi_get_pixel_size(dsi->pix_fmt);
-	struct omap_dss_device *out = &dsi->output;
 	u8 data_type;
 	u16 word_count;
 	int r;
 
-	if (!out->dispc_channel_connected) {
-		DSSERR("failed to enable display: no output/manager\n");
-		return -ENODEV;
-	}
-
 	r = dsi_display_init_dispc(dsi);
 	if (r)
-		goto err_init_dispc;
+		return r;
 
 	if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) {
 		switch (dsi->pix_fmt) {
@@ -3814,7 +3808,6 @@ err_mgr_enable:
 	}
 err_pix_fmt:
 	dsi_display_uninit_dispc(dsi);
-err_init_dispc:
 	return r;
 }
 
@@ -4161,10 +4154,10 @@ static void dsi_display_uninit_dsi(struct dsi_data *dsi, bool disconnect_lanes,
 	dsi_pll_uninit(dsi, disconnect_lanes);
 }
 
-static int dsi_display_enable(struct omap_dss_device *dssdev)
+static void dsi_display_enable(struct omap_dss_device *dssdev)
 {
 	struct dsi_data *dsi = to_dsi_data(dssdev);
-	int r = 0;
+	int r;
 
 	DSSDBG("dsi_display_enable\n");
 
@@ -4184,14 +4177,13 @@ static int dsi_display_enable(struct omap_dss_device *dssdev)
 
 	mutex_unlock(&dsi->lock);
 
-	return 0;
+	return;
 
 err_init_dsi:
 	dsi_runtime_put(dsi);
 err_get_dsi:
 	mutex_unlock(&dsi->lock);
 	DSSDBG("dsi_display_enable FAILED\n");
-	return r;
 }
 
 static void dsi_display_disable(struct omap_dss_device *dssdev,
@@ -4751,6 +4743,17 @@ static int dsi_set_config(struct omap_dss_device *dssdev,
 	dsi->vm.flags |= DISPLAY_FLAGS_HSYNC_HIGH;
 	dsi->vm.flags &= ~DISPLAY_FLAGS_VSYNC_LOW;
 	dsi->vm.flags |= DISPLAY_FLAGS_VSYNC_HIGH;
+	/*
+	 * HACK: These flags should be handled through the omap_dss_device bus
+	 * flags, but this will only be possible when the DSI encoder will be
+	 * converted to the omapdrm-managed encoder model.
+	 */
+	dsi->vm.flags &= ~DISPLAY_FLAGS_PIXDATA_NEGEDGE;
+	dsi->vm.flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
+	dsi->vm.flags &= ~DISPLAY_FLAGS_DE_LOW;
+	dsi->vm.flags |= DISPLAY_FLAGS_DE_HIGH;
+	dsi->vm.flags &= ~DISPLAY_FLAGS_SYNC_POSEDGE;
+	dsi->vm.flags |= DISPLAY_FLAGS_SYNC_NEGEDGE;
 
 	dss_mgr_set_timings(&dsi->output, &dsi->vm);
 
@@ -4877,21 +4880,12 @@ static int dsi_get_clocks(struct dsi_data *dsi)
 static int dsi_connect(struct omap_dss_device *src,
 		       struct omap_dss_device *dst)
 {
-	int r;
-
-	r = omapdss_device_connect(dst->dss, dst, dst->next);
-	if (r)
-		return r;
-
-	dst->dispc_channel_connected = true;
-	return 0;
+	return omapdss_device_connect(dst->dss, dst, dst->next);
 }
 
 static void dsi_disconnect(struct omap_dss_device *src,
 			   struct omap_dss_device *dst)
 {
-	dst->dispc_channel_connected = false;
-
 	omapdss_device_disconnect(dst, dst->next);
 }
 
@@ -5083,15 +5077,15 @@ static int dsi_bind(struct device *dev, struct device *master, void *data)
 
 	snprintf(name, sizeof(name), "dsi%u_regs", dsi->module_id + 1);
 	dsi->debugfs.regs = dss_debugfs_create_file(dss, name,
-						    dsi_dump_dsi_regs, &dsi);
+						    dsi_dump_dsi_regs, dsi);
 #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
 	snprintf(name, sizeof(name), "dsi%u_irqs", dsi->module_id + 1);
 	dsi->debugfs.irqs = dss_debugfs_create_file(dss, name,
-						    dsi_dump_dsi_irqs, &dsi);
+						    dsi_dump_dsi_irqs, dsi);
 #endif
 	snprintf(name, sizeof(name), "dsi%u_clks", dsi->module_id + 1);
 	dsi->debugfs.clks = dss_debugfs_create_file(dss, name,
-						    dsi_dump_dsi_clocks, &dsi);
+						    dsi_dump_dsi_clocks, dsi);
 
 	return 0;
 }
@@ -5104,8 +5098,6 @@ static void dsi_unbind(struct device *dev, struct device *master, void *data)
 	dss_debugfs_remove_file(dsi->debugfs.irqs);
 	dss_debugfs_remove_file(dsi->debugfs.regs);
 
-	of_platform_depopulate(dev);
-
 	WARN_ON(dsi->scp_clk_refcount > 0);
 
 	dss_pll_unregister(&dsi->pll);
@@ -5129,29 +5121,19 @@ static int dsi_init_output(struct dsi_data *dsi)
 	out->id = dsi->module_id == 0 ?
 			OMAP_DSS_OUTPUT_DSI1 : OMAP_DSS_OUTPUT_DSI2;
 
-	out->output_type = OMAP_DISPLAY_TYPE_DSI;
+	out->type = OMAP_DISPLAY_TYPE_DSI;
 	out->name = dsi->module_id == 0 ? "dsi.0" : "dsi.1";
 	out->dispc_channel = dsi_get_channel(dsi);
 	out->ops = &dsi_ops;
 	out->owner = THIS_MODULE;
 	out->of_ports = BIT(0);
-	out->bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE
+	out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE
 		       | DRM_BUS_FLAG_DE_HIGH
-		       | DRM_BUS_FLAG_SYNC_NEGEDGE;
+		       | DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE;
 
-	out->next = omapdss_of_find_connected_device(out->dev->of_node, 0);
-	if (IS_ERR(out->next)) {
-		if (PTR_ERR(out->next) != -EPROBE_DEFER)
-			dev_err(out->dev, "failed to find video sink\n");
-		return PTR_ERR(out->next);
-	}
-
-	r = omapdss_output_validate(out);
-	if (r) {
-		omapdss_device_put(out->next);
-		out->next = NULL;
+	r = omapdss_device_init_output(out);
+	if (r < 0)
 		return r;
-	}
 
 	omapdss_device_register(out);
 
@@ -5162,9 +5144,8 @@ static void dsi_uninit_output(struct dsi_data *dsi)
 {
 	struct omap_dss_device *out = &dsi->output;
 
-	if (out->next)
-		omapdss_device_put(out->next);
 	omapdss_device_unregister(out);
+	omapdss_device_cleanup_output(out);
 }
 
 static int dsi_probe_of(struct dsi_data *dsi)
@@ -5457,6 +5438,8 @@ static int dsi_remove(struct platform_device *pdev)
 
 	dsi_uninit_output(dsi);
 
+	of_platform_depopulate(&pdev->dev);
+
 	pm_runtime_disable(&pdev->dev);
 
 	if (dsi->vdds_dsi_reg != NULL && dsi->vdds_dsi_enabled) {

+ 7 - 53
drivers/gpu/drm/omapdrm/dss/dss-of.c

@@ -12,71 +12,25 @@
  * more details.
  */
 
-#include <linux/device.h>
 #include <linux/err.h>
-#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_graph.h>
-#include <linux/seq_file.h>
 
 #include "omapdss.h"
 
-static struct device_node *
-dss_of_port_get_parent_device(struct device_node *port)
-{
-	struct device_node *np;
-	int i;
-
-	if (!port)
-		return NULL;
-
-	np = of_get_parent(port);
-
-	for (i = 0; i < 2 && np; ++i) {
-		struct property *prop;
-
-		prop = of_find_property(np, "compatible", NULL);
-
-		if (prop)
-			return np;
-
-		np = of_get_next_parent(np);
-	}
-
-	return NULL;
-}
-
 struct omap_dss_device *
 omapdss_of_find_connected_device(struct device_node *node, unsigned int port)
 {
-	struct device_node *src_node;
-	struct device_node *src_port;
-	struct device_node *ep;
-	struct omap_dss_device *src;
-	u32 port_number = 0;
+	struct device_node *remote_node;
+	struct omap_dss_device *dssdev;
 
-	/* Get the endpoint... */
-	ep = of_graph_get_endpoint_by_regs(node, port, 0);
-	if (!ep)
+	remote_node = of_graph_get_remote_node(node, port, 0);
+	if (!remote_node)
 		return NULL;
 
-	/* ... and its remote port... */
-	src_port = of_graph_get_remote_port(ep);
-	of_node_put(ep);
-	if (!src_port)
-		return NULL;
-
-	/* ... and the remote port's number and parent... */
-	of_property_read_u32(src_port, "reg", &port_number);
-	src_node = dss_of_port_get_parent_device(src_port);
-	of_node_put(src_port);
-	if (!src_node)
-		return ERR_PTR(-EINVAL);
-
-	/* ... and finally the connected device. */
-	src = omapdss_find_device_by_port(src_node, port_number);
-	of_node_put(src_node);
+	dssdev = omapdss_find_device_by_node(remote_node);
+	of_node_put(remote_node);
 
-	return src ? src : ERR_PTR(-EPROBE_DEFER);
+	return dssdev ? dssdev : ERR_PTR(-EPROBE_DEFER);
 }
 EXPORT_SYMBOL_GPL(omapdss_of_find_connected_device);

+ 1 - 1
drivers/gpu/drm/omapdrm/dss/dss.c

@@ -1560,7 +1560,7 @@ static void dss_shutdown(struct platform_device *pdev)
 
 	DSSDBG("shutdown\n");
 
-	for_each_dss_display(dssdev) {
+	for_each_dss_output(dssdev) {
 		if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
 			dssdev->ops->disable(dssdev);
 	}

+ 12 - 42
drivers/gpu/drm/omapdrm/dss/hdmi4.c

@@ -249,15 +249,15 @@ static void hdmi_power_off_full(struct omap_hdmi *hdmi)
 }
 
 static void hdmi_display_set_timings(struct omap_dss_device *dssdev,
-				     const struct videomode *vm)
+				     const struct drm_display_mode *mode)
 {
 	struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
 
 	mutex_lock(&hdmi->lock);
 
-	hdmi->cfg.vm = *vm;
+	drm_display_mode_to_videomode(mode, &hdmi->cfg.vm);
 
-	dispc_set_tv_pclk(hdmi->dss->dispc, vm->pixelclock);
+	dispc_set_tv_pclk(hdmi->dss->dispc, mode->clock * 1000);
 
 	mutex_unlock(&hdmi->lock);
 }
@@ -312,26 +312,20 @@ static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
 	hdmi_wp_audio_enable(&hd->wp, false);
 }
 
-static int hdmi_display_enable(struct omap_dss_device *dssdev)
+static void hdmi_display_enable(struct omap_dss_device *dssdev)
 {
 	struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
 	unsigned long flags;
-	int r = 0;
+	int r;
 
 	DSSDBG("ENTER hdmi_display_enable\n");
 
 	mutex_lock(&hdmi->lock);
 
-	if (!dssdev->dispc_channel_connected) {
-		DSSERR("failed to enable display: no output/manager\n");
-		r = -ENODEV;
-		goto err0;
-	}
-
 	r = hdmi_power_on_full(hdmi);
 	if (r) {
 		DSSERR("failed to power on device\n");
-		goto err0;
+		goto done;
 	}
 
 	if (hdmi->audio_configured) {
@@ -351,12 +345,8 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev)
 	hdmi->display_enabled = true;
 	spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
 
+done:
 	mutex_unlock(&hdmi->lock);
-	return 0;
-
-err0:
-	mutex_unlock(&hdmi->lock);
-	return r;
 }
 
 static void hdmi_display_disable(struct omap_dss_device *dssdev)
@@ -417,21 +407,12 @@ void hdmi4_core_disable(struct hdmi_core_data *core)
 static int hdmi_connect(struct omap_dss_device *src,
 			struct omap_dss_device *dst)
 {
-	int r;
-
-	r = omapdss_device_connect(dst->dss, dst, dst->next);
-	if (r)
-		return r;
-
-	dst->dispc_channel_connected = true;
-	return 0;
+	return omapdss_device_connect(dst->dss, dst, dst->next);
 }
 
 static void hdmi_disconnect(struct omap_dss_device *src,
 			    struct omap_dss_device *dst)
 {
-	dst->dispc_channel_connected = false;
-
 	omapdss_device_disconnect(dst, dst->next);
 }
 
@@ -698,7 +679,7 @@ static int hdmi4_init_output(struct omap_hdmi *hdmi)
 
 	out->dev = &hdmi->pdev->dev;
 	out->id = OMAP_DSS_OUTPUT_HDMI;
-	out->output_type = OMAP_DISPLAY_TYPE_HDMI;
+	out->type = OMAP_DISPLAY_TYPE_HDMI;
 	out->name = "hdmi.0";
 	out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
 	out->ops = &hdmi_ops;
@@ -706,19 +687,9 @@ static int hdmi4_init_output(struct omap_hdmi *hdmi)
 	out->of_ports = BIT(0);
 	out->ops_flags = OMAP_DSS_DEVICE_OP_EDID;
 
-	out->next = omapdss_of_find_connected_device(out->dev->of_node, 0);
-	if (IS_ERR(out->next)) {
-		if (PTR_ERR(out->next) != -EPROBE_DEFER)
-			dev_err(out->dev, "failed to find video sink\n");
-		return PTR_ERR(out->next);
-	}
-
-	r = omapdss_output_validate(out);
-	if (r) {
-		omapdss_device_put(out->next);
-		out->next = NULL;
+	r = omapdss_device_init_output(out);
+	if (r < 0)
 		return r;
-	}
 
 	omapdss_device_register(out);
 
@@ -729,9 +700,8 @@ static void hdmi4_uninit_output(struct omap_hdmi *hdmi)
 {
 	struct omap_dss_device *out = &hdmi->output;
 
-	if (out->next)
-		omapdss_device_put(out->next);
 	omapdss_device_unregister(out);
+	omapdss_device_cleanup_output(out);
 }
 
 static int hdmi4_probe_of(struct omap_hdmi *hdmi)

+ 12 - 42
drivers/gpu/drm/omapdrm/dss/hdmi5.c

@@ -248,15 +248,15 @@ static void hdmi_power_off_full(struct omap_hdmi *hdmi)
 }
 
 static void hdmi_display_set_timings(struct omap_dss_device *dssdev,
-				     const struct videomode *vm)
+				     const struct drm_display_mode *mode)
 {
 	struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
 
 	mutex_lock(&hdmi->lock);
 
-	hdmi->cfg.vm = *vm;
+	drm_display_mode_to_videomode(mode, &hdmi->cfg.vm);
 
-	dispc_set_tv_pclk(hdmi->dss->dispc, vm->pixelclock);
+	dispc_set_tv_pclk(hdmi->dss->dispc, mode->clock * 1000);
 
 	mutex_unlock(&hdmi->lock);
 }
@@ -320,26 +320,20 @@ static void hdmi_stop_audio_stream(struct omap_hdmi *hd)
 	REG_FLD_MOD(hd->wp.base, HDMI_WP_SYSCONFIG, hd->wp_idlemode, 3, 2);
 }
 
-static int hdmi_display_enable(struct omap_dss_device *dssdev)
+static void hdmi_display_enable(struct omap_dss_device *dssdev)
 {
 	struct omap_hdmi *hdmi = dssdev_to_hdmi(dssdev);
 	unsigned long flags;
-	int r = 0;
+	int r;
 
 	DSSDBG("ENTER hdmi_display_enable\n");
 
 	mutex_lock(&hdmi->lock);
 
-	if (!dssdev->dispc_channel_connected) {
-		DSSERR("failed to enable display: no output/manager\n");
-		r = -ENODEV;
-		goto err0;
-	}
-
 	r = hdmi_power_on_full(hdmi);
 	if (r) {
 		DSSERR("failed to power on device\n");
-		goto err0;
+		goto done;
 	}
 
 	if (hdmi->audio_configured) {
@@ -359,12 +353,8 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev)
 	hdmi->display_enabled = true;
 	spin_unlock_irqrestore(&hdmi->audio_playing_lock, flags);
 
+done:
 	mutex_unlock(&hdmi->lock);
-	return 0;
-
-err0:
-	mutex_unlock(&hdmi->lock);
-	return r;
 }
 
 static void hdmi_display_disable(struct omap_dss_device *dssdev)
@@ -422,21 +412,12 @@ static void hdmi_core_disable(struct omap_hdmi *hdmi)
 static int hdmi_connect(struct omap_dss_device *src,
 			struct omap_dss_device *dst)
 {
-	int r;
-
-	r = omapdss_device_connect(dst->dss, dst, dst->next);
-	if (r)
-		return r;
-
-	dst->dispc_channel_connected = true;
-	return 0;
+	return omapdss_device_connect(dst->dss, dst, dst->next);
 }
 
 static void hdmi_disconnect(struct omap_dss_device *src,
 			    struct omap_dss_device *dst)
 {
-	dst->dispc_channel_connected = false;
-
 	omapdss_device_disconnect(dst, dst->next);
 }
 
@@ -682,7 +663,7 @@ static int hdmi5_init_output(struct omap_hdmi *hdmi)
 
 	out->dev = &hdmi->pdev->dev;
 	out->id = OMAP_DSS_OUTPUT_HDMI;
-	out->output_type = OMAP_DISPLAY_TYPE_HDMI;
+	out->type = OMAP_DISPLAY_TYPE_HDMI;
 	out->name = "hdmi.0";
 	out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
 	out->ops = &hdmi_ops;
@@ -690,19 +671,9 @@ static int hdmi5_init_output(struct omap_hdmi *hdmi)
 	out->of_ports = BIT(0);
 	out->ops_flags = OMAP_DSS_DEVICE_OP_EDID;
 
-	out->next = omapdss_of_find_connected_device(out->dev->of_node, 0);
-	if (IS_ERR(out->next)) {
-		if (PTR_ERR(out->next) != -EPROBE_DEFER)
-			dev_err(out->dev, "failed to find video sink\n");
-		return PTR_ERR(out->next);
-	}
-
-	r = omapdss_output_validate(out);
-	if (r) {
-		omapdss_device_put(out->next);
-		out->next = NULL;
+	r = omapdss_device_init_output(out);
+	if (r < 0)
 		return r;
-	}
 
 	omapdss_device_register(out);
 
@@ -713,9 +684,8 @@ static void hdmi5_uninit_output(struct omap_hdmi *hdmi)
 {
 	struct omap_dss_device *out = &hdmi->output;
 
-	if (out->next)
-		omapdss_device_put(out->next);
 	omapdss_device_unregister(out);
+	omapdss_device_cleanup_output(out);
 }
 
 static int hdmi5_probe_of(struct omap_hdmi *hdmi)

+ 18 - 1
drivers/gpu/drm/omapdrm/dss/omapdss-boot-init.c

@@ -184,6 +184,23 @@ static const struct of_device_id omapdss_of_match[] __initconst = {
 	{},
 };
 
+static const struct of_device_id omapdss_of_fixups_whitelist[] __initconst = {
+	{ .compatible = "composite-video-connector" },
+	{ .compatible = "hdmi-connector" },
+	{ .compatible = "lgphilips,lb035q02" },
+	{ .compatible = "nec,nl8048hl11" },
+	{ .compatible = "panel-dsi-cm" },
+	{ .compatible = "sharp,ls037v7dw01" },
+	{ .compatible = "sony,acx565akm" },
+	{ .compatible = "svideo-connector" },
+	{ .compatible = "ti,opa362" },
+	{ .compatible = "ti,tpd12s015" },
+	{ .compatible = "ti,dra7evm-tpd12s015" },
+	{ .compatible = "toppoly,td028ttec1" },
+	{ .compatible = "tpo,td028ttec1" },
+	{ .compatible = "tpo,td043mtea1" },
+};
+
 static int __init omapdss_boot_init(void)
 {
 	struct device_node *dss, *child;
@@ -210,7 +227,7 @@ static int __init omapdss_boot_init(void)
 		n = list_first_entry(&dss_conv_list, struct dss_conv_node,
 			list);
 
-		if (!n->root)
+		if (of_match_node(omapdss_of_fixups_whitelist, n->node))
 			omapdss_omapify_node(n->node);
 
 		list_del(&n->list);

+ 35 - 43
drivers/gpu/drm/omapdrm/dss/omapdss.h

@@ -19,7 +19,6 @@
 #define __OMAP_DRM_DSS_H
 
 #include <linux/list.h>
-#include <linux/kobject.h>
 #include <linux/device.h>
 #include <linux/interrupt.h>
 #include <video/videomode.h>
@@ -69,6 +68,7 @@ struct dss_lcd_mgr_config;
 struct snd_aes_iec958;
 struct snd_cea_861_aud_if;
 struct hdmi_avi_infoframe;
+struct drm_connector;
 
 enum omap_display_type {
 	OMAP_DISPLAY_TYPE_NONE		= 0,
@@ -267,7 +267,7 @@ struct omap_overlay_manager_info {
 	u32 trans_key;
 	bool trans_enabled;
 
-	bool partial_alpha_enabled;
+	bool alpha_blender_enabled;
 
 	bool cpr_enable;
 	struct omap_dss_cpr_coefs cpr_coefs;
@@ -364,15 +364,15 @@ struct omap_dss_device_ops {
 	void (*disconnect)(struct omap_dss_device *dssdev,
 			struct omap_dss_device *dst);
 
-	int (*enable)(struct omap_dss_device *dssdev);
+	void (*pre_enable)(struct omap_dss_device *dssdev);
+	void (*enable)(struct omap_dss_device *dssdev);
 	void (*disable)(struct omap_dss_device *dssdev);
+	void (*post_disable)(struct omap_dss_device *dssdev);
 
 	int (*check_timings)(struct omap_dss_device *dssdev,
-			     struct videomode *vm);
-	void (*get_timings)(struct omap_dss_device *dssdev,
-			    struct videomode *vm);
+			     struct drm_display_mode *mode);
 	void (*set_timings)(struct omap_dss_device *dssdev,
-			    const struct videomode *vm);
+			    const struct drm_display_mode *mode);
 
 	bool (*detect)(struct omap_dss_device *dssdev);
 
@@ -384,6 +384,9 @@ struct omap_dss_device_ops {
 
 	int (*read_edid)(struct omap_dss_device *dssdev, u8 *buf, int len);
 
+	int (*get_modes)(struct omap_dss_device *dssdev,
+			 struct drm_connector *connector);
+
 	union {
 		const struct omapdss_hdmi_ops hdmi;
 		const struct omapdss_dsi_ops dsi;
@@ -394,42 +397,40 @@ struct omap_dss_device_ops {
  * enum omap_dss_device_ops_flag - Indicates which device ops are supported
  * @OMAP_DSS_DEVICE_OP_DETECT: The device supports output connection detection
  * @OMAP_DSS_DEVICE_OP_HPD: The device supports all hot-plug-related operations
- * @OMAP_DSS_DEVICE_OP_EDID: The device supports readind EDID
+ * @OMAP_DSS_DEVICE_OP_EDID: The device supports reading EDID
+ * @OMAP_DSS_DEVICE_OP_MODES: The device supports reading modes
  */
 enum omap_dss_device_ops_flag {
 	OMAP_DSS_DEVICE_OP_DETECT = BIT(0),
 	OMAP_DSS_DEVICE_OP_HPD = BIT(1),
 	OMAP_DSS_DEVICE_OP_EDID = BIT(2),
-};
-
-enum omap_dss_device_type {
-	OMAP_DSS_DEVICE_TYPE_OUTPUT = (1 << 0),
-	OMAP_DSS_DEVICE_TYPE_DISPLAY = (1 << 1),
+	OMAP_DSS_DEVICE_OP_MODES = BIT(3),
 };
 
 struct omap_dss_device {
-	struct kobject kobj;
 	struct device *dev;
 
 	struct module *owner;
 
 	struct dss_device *dss;
-	struct omap_dss_device *src;
-	struct omap_dss_device *dst;
 	struct omap_dss_device *next;
+	struct drm_bridge *bridge;
+	struct drm_panel *panel;
 
 	struct list_head list;
 
-	unsigned int alias_id;
-
+	/*
+	 * DSS type that this device generates (for DSS internal devices) or
+	 * requires (for external encoders, connectors and panels). Must be a
+	 * non-zero (different than OMAP_DISPLAY_TYPE_NONE) value.
+	 */
 	enum omap_display_type type;
+
 	/*
-	 * DSS output type that this device generates (for DSS internal devices)
-	 * or requires (for external encoders). Must be OMAP_DISPLAY_TYPE_NONE
-	 * for display devices (connectors and panels) and to non-zero value for
-	 * all other devices.
+	 * True if the device is a display (panel or connector) at the end of
+	 * the pipeline, false otherwise.
 	 */
-	enum omap_display_type output_type;
+	bool display;
 
 	const char *name;
 
@@ -438,9 +439,6 @@ struct omap_dss_device {
 	unsigned long ops_flags;
 	u32 bus_flags;
 
-	/* helper variable for driver suspend/resume */
-	bool activate_after_resume;
-
 	enum omap_display_caps caps;
 
 	enum omap_dss_display_state state;
@@ -449,7 +447,6 @@ struct omap_dss_device {
 
 	/* DISPC channel for this output */
 	enum omap_channel dispc_channel;
-	bool dispc_channel_connected;
 
 	/* output instance */
 	enum omap_dss_output_id id;
@@ -469,9 +466,6 @@ struct omap_dss_driver {
 	int (*memory_read)(struct omap_dss_device *dssdev,
 			void *buf, size_t size,
 			u16 x, u16 y, u16 w, u16 h);
-
-	void (*get_size)(struct omap_dss_device *dssdev,
-			 unsigned int *width, unsigned int *height);
 };
 
 struct dss_device *omapdss_get_dss(void);
@@ -481,32 +475,35 @@ static inline bool omapdss_is_initialized(void)
 	return !!omapdss_get_dss();
 }
 
-#define for_each_dss_display(d) \
-	while ((d = omapdss_device_get_next(d, OMAP_DSS_DEVICE_TYPE_DISPLAY)) != NULL)
 void omapdss_display_init(struct omap_dss_device *dssdev);
 struct omap_dss_device *omapdss_display_get(struct omap_dss_device *output);
+int omapdss_display_get_modes(struct drm_connector *connector,
+			      const struct videomode *vm);
 
 void omapdss_device_register(struct omap_dss_device *dssdev);
 void omapdss_device_unregister(struct omap_dss_device *dssdev);
 struct omap_dss_device *omapdss_device_get(struct omap_dss_device *dssdev);
 void omapdss_device_put(struct omap_dss_device *dssdev);
-struct omap_dss_device *omapdss_find_device_by_port(struct device_node *src,
-						    unsigned int port);
-struct omap_dss_device *omapdss_device_get_next(struct omap_dss_device *from,
-						enum omap_dss_device_type type);
+struct omap_dss_device *omapdss_find_device_by_node(struct device_node *node);
 int omapdss_device_connect(struct dss_device *dss,
 			   struct omap_dss_device *src,
 			   struct omap_dss_device *dst);
 void omapdss_device_disconnect(struct omap_dss_device *src,
 			       struct omap_dss_device *dst);
+void omapdss_device_pre_enable(struct omap_dss_device *dssdev);
+void omapdss_device_enable(struct omap_dss_device *dssdev);
+void omapdss_device_disable(struct omap_dss_device *dssdev);
+void omapdss_device_post_disable(struct omap_dss_device *dssdev);
 
 int omap_dss_get_num_overlay_managers(void);
 
 int omap_dss_get_num_overlays(void);
 
 #define for_each_dss_output(d) \
-	while ((d = omapdss_device_get_next(d, OMAP_DSS_DEVICE_TYPE_OUTPUT)) != NULL)
-int omapdss_output_validate(struct omap_dss_device *out);
+	while ((d = omapdss_device_next_output(d)) != NULL)
+struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from);
+int omapdss_device_init_output(struct omap_dss_device *out);
+void omapdss_device_cleanup_output(struct omap_dss_device *out);
 
 typedef void (*omap_dispc_isr_t) (void *arg, u32 mask);
 int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask);
@@ -515,11 +512,6 @@ int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask);
 int omapdss_compat_init(void);
 void omapdss_compat_uninit(void);
 
-static inline bool omapdss_device_is_connected(struct omap_dss_device *dssdev)
-{
-	return dssdev->src;
-}
-
 static inline bool omapdss_device_is_enabled(struct omap_dss_device *dssdev)
 {
 	return dssdev->state == OMAP_DSS_DISPLAY_ACTIVE;

+ 39 - 4
drivers/gpu/drm/omapdrm/dss/output.c

@@ -20,20 +20,55 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/of.h>
+#include <linux/of_graph.h>
+
+#include <drm/drm_panel.h>
 
 #include "dss.h"
 #include "omapdss.h"
 
-int omapdss_output_validate(struct omap_dss_device *out)
+int omapdss_device_init_output(struct omap_dss_device *out)
 {
-	if (out->next && out->output_type != out->next->type) {
+	struct device_node *remote_node;
+	u32 port_num;
+
+	/*
+	 * FIXME: DSS outputs have only a single bit set, so we can use __ffs.
+	 * This is a temporary fix until the port management has been cleaned.
+	 */
+	port_num = __ffs(out->of_ports);
+
+	remote_node = of_graph_get_remote_node(out->dev->of_node, port_num, 0);
+	if (!remote_node) {
+		dev_dbg(out->dev, "failed to find video sink\n");
+		return 0;
+	}
+
+	out->next = omapdss_find_device_by_node(remote_node);
+	out->bridge = of_drm_find_bridge(remote_node);
+	out->panel = of_drm_find_panel(remote_node);
+	if (IS_ERR(out->panel))
+		out->panel = NULL;
+
+	of_node_put(remote_node);
+
+	if (out->next && out->type != out->next->type) {
 		dev_err(out->dev, "output type and display type don't match\n");
+		omapdss_device_put(out->next);
+		out->next = NULL;
 		return -EINVAL;
 	}
 
-	return 0;
+	return out->next || out->bridge || out->panel ? 0 : -EPROBE_DEFER;
+}
+EXPORT_SYMBOL(omapdss_device_init_output);
+
+void omapdss_device_cleanup_output(struct omap_dss_device *out)
+{
+	if (out->next)
+		omapdss_device_put(out->next);
 }
-EXPORT_SYMBOL(omapdss_output_validate);
+EXPORT_SYMBOL(omapdss_device_cleanup_output);
 
 int dss_install_mgr_ops(struct dss_device *dss,
 			const struct dss_mgr_ops *mgr_ops,

+ 21 - 47
drivers/gpu/drm/omapdrm/dss/sdi.c

@@ -37,7 +37,7 @@ struct sdi_device {
 	struct regulator *vdds_sdi_reg;
 
 	struct dss_lcd_mgr_config mgr_config;
-	struct videomode vm;
+	unsigned long pixelclock;
 	int datapairs;
 
 	struct omap_dss_device output;
@@ -129,27 +129,22 @@ static void sdi_config_lcd_manager(struct sdi_device *sdi)
 	dss_mgr_set_lcd_config(&sdi->output, &sdi->mgr_config);
 }
 
-static int sdi_display_enable(struct omap_dss_device *dssdev)
+static void sdi_display_enable(struct omap_dss_device *dssdev)
 {
 	struct sdi_device *sdi = dssdev_to_sdi(dssdev);
 	struct dispc_clock_info dispc_cinfo;
 	unsigned long fck;
 	int r;
 
-	if (!sdi->output.dispc_channel_connected) {
-		DSSERR("failed to enable display: no output/manager\n");
-		return -ENODEV;
-	}
-
 	r = regulator_enable(sdi->vdds_sdi_reg);
 	if (r)
-		goto err_reg_enable;
+		return;
 
 	r = dispc_runtime_get(sdi->dss->dispc);
 	if (r)
 		goto err_get_dispc;
 
-	r = sdi_calc_clock_div(sdi, sdi->vm.pixelclock, &fck, &dispc_cinfo);
+	r = sdi_calc_clock_div(sdi, sdi->pixelclock, &fck, &dispc_cinfo);
 	if (r)
 		goto err_calc_clock_div;
 
@@ -185,7 +180,7 @@ static int sdi_display_enable(struct omap_dss_device *dssdev)
 	if (r)
 		goto err_mgr_enable;
 
-	return 0;
+	return;
 
 err_mgr_enable:
 	dss_sdi_disable(sdi->dss);
@@ -195,8 +190,6 @@ err_calc_clock_div:
 	dispc_runtime_put(sdi->dss->dispc);
 err_get_dispc:
 	regulator_disable(sdi->vdds_sdi_reg);
-err_reg_enable:
-	return r;
 }
 
 static void sdi_display_disable(struct omap_dss_device *dssdev)
@@ -213,36 +206,37 @@ static void sdi_display_disable(struct omap_dss_device *dssdev)
 }
 
 static void sdi_set_timings(struct omap_dss_device *dssdev,
-			    const struct videomode *vm)
+			    const struct drm_display_mode *mode)
 {
 	struct sdi_device *sdi = dssdev_to_sdi(dssdev);
 
-	sdi->vm = *vm;
+	sdi->pixelclock = mode->clock * 1000;
 }
 
 static int sdi_check_timings(struct omap_dss_device *dssdev,
-			     struct videomode *vm)
+			     struct drm_display_mode *mode)
 {
 	struct sdi_device *sdi = dssdev_to_sdi(dssdev);
 	struct dispc_clock_info dispc_cinfo;
+	unsigned long pixelclock = mode->clock * 1000;
 	unsigned long fck;
 	unsigned long pck;
 	int r;
 
-	if (vm->pixelclock == 0)
+	if (pixelclock == 0)
 		return -EINVAL;
 
-	r = sdi_calc_clock_div(sdi, vm->pixelclock, &fck, &dispc_cinfo);
+	r = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo);
 	if (r)
 		return r;
 
 	pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div;
 
-	if (pck != vm->pixelclock) {
+	if (pck != pixelclock) {
 		DSSWARN("Pixel clock adjusted from %lu Hz to %lu Hz\n",
-			vm->pixelclock, pck);
+			pixelclock, pck);
 
-		vm->pixelclock = pck;
+		mode->clock = pck / 1000;
 	}
 
 	return 0;
@@ -251,21 +245,12 @@ static int sdi_check_timings(struct omap_dss_device *dssdev,
 static int sdi_connect(struct omap_dss_device *src,
 		       struct omap_dss_device *dst)
 {
-	int r;
-
-	r = omapdss_device_connect(dst->dss, dst, dst->next);
-	if (r)
-		return r;
-
-	dst->dispc_channel_connected = true;
-	return 0;
+	return omapdss_device_connect(dst->dss, dst, dst->next);
 }
 
 static void sdi_disconnect(struct omap_dss_device *src,
 			   struct omap_dss_device *dst)
 {
-	dst->dispc_channel_connected = false;
-
 	omapdss_device_disconnect(dst, dst->next);
 }
 
@@ -287,29 +272,19 @@ static int sdi_init_output(struct sdi_device *sdi)
 
 	out->dev = &sdi->pdev->dev;
 	out->id = OMAP_DSS_OUTPUT_SDI;
-	out->output_type = OMAP_DISPLAY_TYPE_SDI;
+	out->type = OMAP_DISPLAY_TYPE_SDI;
 	out->name = "sdi.0";
 	out->dispc_channel = OMAP_DSS_CHANNEL_LCD;
 	/* We have SDI only on OMAP3, where it's on port 1 */
 	out->of_ports = BIT(1);
 	out->ops = &sdi_ops;
 	out->owner = THIS_MODULE;
-	out->bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE	/* 15.5.9.1.2 */
-		       | DRM_BUS_FLAG_SYNC_POSEDGE;
-
-	out->next = omapdss_of_find_connected_device(out->dev->of_node, 1);
-	if (IS_ERR(out->next)) {
-		if (PTR_ERR(out->next) != -EPROBE_DEFER)
-			dev_err(out->dev, "failed to find video sink\n");
-		return PTR_ERR(out->next);
-	}
+	out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE	/* 15.5.9.1.2 */
+		       | DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE;
 
-	r = omapdss_output_validate(out);
-	if (r) {
-		omapdss_device_put(out->next);
-		out->next = NULL;
+	r = omapdss_device_init_output(out);
+	if (r < 0)
 		return r;
-	}
 
 	omapdss_device_register(out);
 
@@ -318,9 +293,8 @@ static int sdi_init_output(struct sdi_device *sdi)
 
 static void sdi_uninit_output(struct sdi_device *sdi)
 {
-	if (sdi->output.next)
-		omapdss_device_put(sdi->output.next);
 	omapdss_device_unregister(&sdi->output);
+	omapdss_device_cleanup_output(&sdi->output);
 }
 
 int sdi_init_port(struct dss_device *dss, struct platform_device *pdev,

+ 101 - 128
drivers/gpu/drm/omapdrm/dss/venc.c

@@ -267,63 +267,40 @@ enum venc_videomode {
 	VENC_MODE_NTSC,
 };
 
-static const struct videomode omap_dss_pal_vm = {
-	.hactive	= 720,
-	.vactive	= 574,
-	.pixelclock	= 13500000,
-	.hsync_len	= 64,
-	.hfront_porch	= 12,
-	.hback_porch	= 68,
-	.vsync_len	= 5,
-	.vfront_porch	= 5,
-	.vback_porch	= 41,
-
-	.flags		= DISPLAY_FLAGS_INTERLACED | DISPLAY_FLAGS_HSYNC_LOW |
-			  DISPLAY_FLAGS_VSYNC_LOW | DISPLAY_FLAGS_DE_HIGH |
-			  DISPLAY_FLAGS_PIXDATA_POSEDGE |
-			  DISPLAY_FLAGS_SYNC_NEGEDGE,
+static const struct drm_display_mode omap_dss_pal_mode = {
+	.hdisplay	= 720,
+	.hsync_start	= 732,
+	.hsync_end	= 796,
+	.htotal		= 864,
+	.vdisplay	= 574,
+	.vsync_start	= 579,
+	.vsync_end	= 584,
+	.vtotal		= 625,
+	.clock		= 13500,
+
+	.flags		= DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_NHSYNC |
+			  DRM_MODE_FLAG_NVSYNC,
 };
 
-static const struct videomode omap_dss_ntsc_vm = {
-	.hactive	= 720,
-	.vactive	= 482,
-	.pixelclock	= 13500000,
-	.hsync_len	= 64,
-	.hfront_porch	= 16,
-	.hback_porch	= 58,
-	.vsync_len	= 6,
-	.vfront_porch	= 6,
-	.vback_porch	= 31,
-
-	.flags		= DISPLAY_FLAGS_INTERLACED | DISPLAY_FLAGS_HSYNC_LOW |
-			  DISPLAY_FLAGS_VSYNC_LOW | DISPLAY_FLAGS_DE_HIGH |
-			  DISPLAY_FLAGS_PIXDATA_POSEDGE |
-			  DISPLAY_FLAGS_SYNC_NEGEDGE,
+static const struct drm_display_mode omap_dss_ntsc_mode = {
+	.hdisplay	= 720,
+	.hsync_start	= 736,
+	.hsync_end	= 800,
+	.htotal		= 858,
+	.vdisplay	= 482,
+	.vsync_start	= 488,
+	.vsync_end	= 494,
+	.vtotal		= 525,
+	.clock		= 13500,
+
+	.flags		= DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_NHSYNC |
+			  DRM_MODE_FLAG_NVSYNC,
 };
 
-static enum venc_videomode venc_get_videomode(const struct videomode *vm)
-{
-	if (!(vm->flags & DISPLAY_FLAGS_INTERLACED))
-		return VENC_MODE_UNKNOWN;
-
-	if (vm->pixelclock == omap_dss_pal_vm.pixelclock &&
-	    vm->hactive == omap_dss_pal_vm.hactive &&
-	    vm->vactive == omap_dss_pal_vm.vactive)
-		return VENC_MODE_PAL;
-
-	if (vm->pixelclock == omap_dss_ntsc_vm.pixelclock &&
-	    vm->hactive == omap_dss_ntsc_vm.hactive &&
-	    vm->vactive == omap_dss_ntsc_vm.vactive)
-		return VENC_MODE_NTSC;
-
-	return VENC_MODE_UNKNOWN;
-}
-
 struct venc_device {
 	struct platform_device *pdev;
 	void __iomem *base;
 	struct mutex venc_lock;
-	u32 wss_data;
 	struct regulator *vdda_dac_reg;
 	struct dss_device *dss;
 
@@ -331,7 +308,7 @@ struct venc_device {
 
 	struct clk	*tv_dac_clk;
 
-	struct videomode vm;
+	const struct venc_config *config;
 	enum omap_dss_venc_type type;
 	bool invert_polarity;
 	bool requires_tv_dac_clk;
@@ -367,8 +344,7 @@ static void venc_write_config(struct venc_device *venc,
 	venc_write_reg(venc, VENC_BLACK_LEVEL, config->black_level);
 	venc_write_reg(venc, VENC_BLANK_LEVEL, config->blank_level);
 	venc_write_reg(venc, VENC_M_CONTROL, config->m_control);
-	venc_write_reg(venc, VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data |
-		       venc->wss_data);
+	venc_write_reg(venc, VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data);
 	venc_write_reg(venc, VENC_S_CARR, config->s_carr);
 	venc_write_reg(venc, VENC_L21__WC_CTL, config->l21__wc_ctl);
 	venc_write_reg(venc, VENC_SAVID__EAVID, config->savid__eavid);
@@ -452,18 +428,6 @@ static void venc_runtime_put(struct venc_device *venc)
 	WARN_ON(r < 0 && r != -ENOSYS);
 }
 
-static const struct venc_config *venc_timings_to_config(const struct videomode *vm)
-{
-	switch (venc_get_videomode(vm)) {
-	default:
-		WARN_ON_ONCE(1);
-	case VENC_MODE_PAL:
-		return &venc_config_pal_trm;
-	case VENC_MODE_NTSC:
-		return &venc_config_ntsc_trm;
-	}
-}
-
 static int venc_power_on(struct venc_device *venc)
 {
 	u32 l;
@@ -474,7 +438,7 @@ static int venc_power_on(struct venc_device *venc)
 		goto err0;
 
 	venc_reset(venc);
-	venc_write_config(venc, venc_timings_to_config(&venc->vm));
+	venc_write_config(venc, venc->config);
 
 	dss_set_venc_output(venc->dss, venc->type);
 	dss_set_dac_pwrdn_bgz(venc->dss, 1);
@@ -524,33 +488,17 @@ static void venc_power_off(struct venc_device *venc)
 	venc_runtime_put(venc);
 }
 
-static int venc_display_enable(struct omap_dss_device *dssdev)
+static void venc_display_enable(struct omap_dss_device *dssdev)
 {
 	struct venc_device *venc = dssdev_to_venc(dssdev);
-	int r;
 
 	DSSDBG("venc_display_enable\n");
 
 	mutex_lock(&venc->venc_lock);
 
-	if (!dssdev->dispc_channel_connected) {
-		DSSERR("Failed to enable display: no output/manager\n");
-		r = -ENODEV;
-		goto err0;
-	}
-
-	r = venc_power_on(venc);
-	if (r)
-		goto err0;
-
-	venc->wss_data = 0;
+	venc_power_on(venc);
 
 	mutex_unlock(&venc->venc_lock);
-
-	return 0;
-err0:
-	mutex_unlock(&venc->venc_lock);
-	return r;
 }
 
 static void venc_display_disable(struct omap_dss_device *dssdev)
@@ -566,30 +514,70 @@ static void venc_display_disable(struct omap_dss_device *dssdev)
 	mutex_unlock(&venc->venc_lock);
 }
 
-static void venc_get_timings(struct omap_dss_device *dssdev,
-			     struct videomode *vm)
+static int venc_get_modes(struct omap_dss_device *dssdev,
+			  struct drm_connector *connector)
 {
-	struct venc_device *venc = dssdev_to_venc(dssdev);
+	static const struct drm_display_mode *modes[] = {
+		&omap_dss_pal_mode,
+		&omap_dss_ntsc_mode,
+	};
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(modes); ++i) {
+		struct drm_display_mode *mode;
+
+		mode = drm_mode_duplicate(connector->dev, modes[i]);
+		if (!mode)
+			return i;
+
+		mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+		drm_mode_set_name(mode);
+		drm_mode_probed_add(connector, mode);
+	}
 
-	mutex_lock(&venc->venc_lock);
-	*vm = venc->vm;
-	mutex_unlock(&venc->venc_lock);
+	return ARRAY_SIZE(modes);
+}
+
+static enum venc_videomode venc_get_videomode(const struct drm_display_mode *mode)
+{
+	if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
+		return VENC_MODE_UNKNOWN;
+
+	if (mode->clock == omap_dss_pal_mode.clock &&
+	    mode->hdisplay == omap_dss_pal_mode.hdisplay &&
+	    mode->vdisplay == omap_dss_pal_mode.vdisplay)
+		return VENC_MODE_PAL;
+
+	if (mode->clock == omap_dss_ntsc_mode.clock &&
+	    mode->hdisplay == omap_dss_ntsc_mode.hdisplay &&
+	    mode->vdisplay == omap_dss_ntsc_mode.vdisplay)
+		return VENC_MODE_NTSC;
+
+	return VENC_MODE_UNKNOWN;
 }
 
 static void venc_set_timings(struct omap_dss_device *dssdev,
-			     const struct videomode *vm)
+			     const struct drm_display_mode *mode)
 {
 	struct venc_device *venc = dssdev_to_venc(dssdev);
+	enum venc_videomode venc_mode = venc_get_videomode(mode);
 
 	DSSDBG("venc_set_timings\n");
 
 	mutex_lock(&venc->venc_lock);
 
-	/* Reset WSS data when the TV standard changes. */
-	if (memcmp(&venc->vm, vm, sizeof(*vm)))
-		venc->wss_data = 0;
+	switch (venc_mode) {
+	default:
+		WARN_ON_ONCE(1);
+		/* Fall-through */
+	case VENC_MODE_PAL:
+		venc->config = &venc_config_pal_trm;
+		break;
 
-	venc->vm = *vm;
+	case VENC_MODE_NTSC:
+		venc->config = &venc_config_ntsc_trm;
+		break;
+	}
 
 	dispc_set_tv_pclk(venc->dss->dispc, 13500000);
 
@@ -597,22 +585,26 @@ static void venc_set_timings(struct omap_dss_device *dssdev,
 }
 
 static int venc_check_timings(struct omap_dss_device *dssdev,
-			      struct videomode *vm)
+			      struct drm_display_mode *mode)
 {
 	DSSDBG("venc_check_timings\n");
 
-	switch (venc_get_videomode(vm)) {
+	switch (venc_get_videomode(mode)) {
 	case VENC_MODE_PAL:
-		*vm = omap_dss_pal_vm;
-		return 0;
+		drm_mode_copy(mode, &omap_dss_pal_mode);
+		break;
 
 	case VENC_MODE_NTSC:
-		*vm = omap_dss_ntsc_vm;
-		return 0;
+		drm_mode_copy(mode, &omap_dss_ntsc_mode);
+		break;
 
 	default:
 		return -EINVAL;
 	}
+
+	drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+	drm_mode_set_name(mode);
+	return 0;
 }
 
 static int venc_dump_regs(struct seq_file *s, void *p)
@@ -695,21 +687,12 @@ static int venc_get_clocks(struct venc_device *venc)
 static int venc_connect(struct omap_dss_device *src,
 			struct omap_dss_device *dst)
 {
-	int r;
-
-	r = omapdss_device_connect(dst->dss, dst, dst->next);
-	if (r)
-		return r;
-
-	dst->dispc_channel_connected = true;
-	return 0;
+	return omapdss_device_connect(dst->dss, dst, dst->next);
 }
 
 static void venc_disconnect(struct omap_dss_device *src,
 			    struct omap_dss_device *dst)
 {
-	dst->dispc_channel_connected = false;
-
 	omapdss_device_disconnect(dst, dst->next);
 }
 
@@ -721,8 +704,9 @@ static const struct omap_dss_device_ops venc_ops = {
 	.disable = venc_display_disable,
 
 	.check_timings = venc_check_timings,
-	.get_timings = venc_get_timings,
 	.set_timings = venc_set_timings,
+
+	.get_modes = venc_get_modes,
 };
 
 /* -----------------------------------------------------------------------------
@@ -776,26 +760,17 @@ static int venc_init_output(struct venc_device *venc)
 
 	out->dev = &venc->pdev->dev;
 	out->id = OMAP_DSS_OUTPUT_VENC;
-	out->output_type = OMAP_DISPLAY_TYPE_VENC;
+	out->type = OMAP_DISPLAY_TYPE_VENC;
 	out->name = "venc.0";
 	out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT;
 	out->ops = &venc_ops;
 	out->owner = THIS_MODULE;
 	out->of_ports = BIT(0);
+	out->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
 
-	out->next = omapdss_of_find_connected_device(out->dev->of_node, 0);
-	if (IS_ERR(out->next)) {
-		if (PTR_ERR(out->next) != -EPROBE_DEFER)
-			dev_err(out->dev, "failed to find video sink\n");
-		return PTR_ERR(out->next);
-	}
-
-	r = omapdss_output_validate(out);
-	if (r) {
-		omapdss_device_put(out->next);
-		out->next = NULL;
+	r = omapdss_device_init_output(out);
+	if (r < 0)
 		return r;
-	}
 
 	omapdss_device_register(out);
 
@@ -804,9 +779,8 @@ static int venc_init_output(struct venc_device *venc)
 
 static void venc_uninit_output(struct venc_device *venc)
 {
-	if (venc->output.next)
-		omapdss_device_put(venc->output.next);
 	omapdss_device_unregister(&venc->output);
+	omapdss_device_cleanup_output(&venc->output);
 }
 
 static int venc_probe_of(struct venc_device *venc)
@@ -878,8 +852,7 @@ static int venc_probe(struct platform_device *pdev)
 
 	mutex_init(&venc->venc_lock);
 
-	venc->wss_data = 0;
-	venc->vm = omap_dss_pal_vm;
+	venc->config = &venc_config_pal_trm;
 
 	venc_mem = platform_get_resource(venc->pdev, IORESOURCE_MEM, 0);
 	venc->base = devm_ioremap_resource(&pdev->dev, venc_mem);

+ 87 - 105
drivers/gpu/drm/omapdrm/omap_connector.c

@@ -18,6 +18,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
 
 #include "omap_drv.h"
 
@@ -30,24 +31,27 @@
 struct omap_connector {
 	struct drm_connector base;
 	struct omap_dss_device *output;
-	struct omap_dss_device *display;
 	struct omap_dss_device *hpd;
 	bool hdmi_mode;
 };
 
 static void omap_connector_hpd_notify(struct drm_connector *connector,
-				      struct omap_dss_device *src,
 				      enum drm_connector_status status)
 {
-	if (status == connector_status_disconnected) {
-		/*
-		 * If the source is an HDMI encoder, notify it of disconnection.
-		 * This is required to let the HDMI encoder reset any internal
-		 * state related to connection status, such as the CEC address.
-		 */
-		if (src && src->type == OMAP_DISPLAY_TYPE_HDMI &&
-		    src->ops->hdmi.lost_hotplug)
-			src->ops->hdmi.lost_hotplug(src);
+	struct omap_connector *omap_connector = to_omap_connector(connector);
+	struct omap_dss_device *dssdev;
+
+	if (status != connector_status_disconnected)
+		return;
+
+	/*
+	 * Notify all devics in the pipeline of disconnection. This is required
+	 * to let the HDMI encoders reset their internal state related to
+	 * connection status, such as the CEC address.
+	 */
+	for (dssdev = omap_connector->output; dssdev; dssdev = dssdev->next) {
+		if (dssdev->ops && dssdev->ops->hdmi.lost_hotplug)
+			dssdev->ops->hdmi.lost_hotplug(dssdev);
 	}
 }
 
@@ -67,7 +71,7 @@ static void omap_connector_hpd_cb(void *cb_data,
 	if (old_status == status)
 		return;
 
-	omap_connector_hpd_notify(connector, omap_connector->hpd, status);
+	omap_connector_hpd_notify(connector, status);
 
 	drm_kms_helper_hotplug_event(dev);
 }
@@ -103,20 +107,20 @@ omap_connector_find_device(struct drm_connector *connector,
 			   enum omap_dss_device_ops_flag op)
 {
 	struct omap_connector *omap_connector = to_omap_connector(connector);
-	struct omap_dss_device *dssdev;
+	struct omap_dss_device *dssdev = NULL;
+	struct omap_dss_device *d;
 
-	for (dssdev = omap_connector->display; dssdev; dssdev = dssdev->src) {
-		if (dssdev->ops_flags & op)
-			return dssdev;
+	for (d = omap_connector->output; d; d = d->next) {
+		if (d->ops_flags & op)
+			dssdev = d;
 	}
 
-	return NULL;
+	return dssdev;
 }
 
 static enum drm_connector_status omap_connector_detect(
 		struct drm_connector *connector, bool force)
 {
-	struct omap_connector *omap_connector = to_omap_connector(connector);
 	struct omap_dss_device *dssdev;
 	enum drm_connector_status status;
 
@@ -128,13 +132,12 @@ static enum drm_connector_status omap_connector_detect(
 		       ? connector_status_connected
 		       : connector_status_disconnected;
 
-		omap_connector_hpd_notify(connector, dssdev->src, status);
+		omap_connector_hpd_notify(connector, status);
 	} else {
-		switch (omap_connector->display->type) {
-		case OMAP_DISPLAY_TYPE_DPI:
-		case OMAP_DISPLAY_TYPE_DBI:
-		case OMAP_DISPLAY_TYPE_SDI:
-		case OMAP_DISPLAY_TYPE_DSI:
+		switch (connector->connector_type) {
+		case DRM_MODE_CONNECTOR_DPI:
+		case DRM_MODE_CONNECTOR_LVDS:
+		case DRM_MODE_CONNECTOR_DSI:
 			status = connector_status_connected;
 			break;
 		default:
@@ -143,7 +146,7 @@ static enum drm_connector_status omap_connector_detect(
 		}
 	}
 
-	VERB("%s: %d (force=%d)", omap_connector->display->name, status, force);
+	VERB("%s: %d (force=%d)", connector->name, status, force);
 
 	return status;
 }
@@ -152,7 +155,7 @@ static void omap_connector_destroy(struct drm_connector *connector)
 {
 	struct omap_connector *omap_connector = to_omap_connector(connector);
 
-	DBG("%s", omap_connector->display->name);
+	DBG("%s", connector->name);
 
 	if (omap_connector->hpd) {
 		struct omap_dss_device *hpd = omap_connector->hpd;
@@ -166,7 +169,6 @@ static void omap_connector_destroy(struct drm_connector *connector)
 	drm_connector_cleanup(connector);
 
 	omapdss_device_put(omap_connector->output);
-	omapdss_device_put(omap_connector->display);
 
 	kfree(omap_connector);
 }
@@ -212,10 +214,8 @@ static int omap_connector_get_modes(struct drm_connector *connector)
 {
 	struct omap_connector *omap_connector = to_omap_connector(connector);
 	struct omap_dss_device *dssdev;
-	struct drm_display_mode *mode;
-	struct videomode vm = {0};
 
-	DBG("%s", omap_connector->display->name);
+	DBG("%s", connector->name);
 
 	/*
 	 * If display exposes EDID, then we parse that in the normal way to
@@ -227,94 +227,71 @@ static int omap_connector_get_modes(struct drm_connector *connector)
 		return omap_connector_get_modes_edid(connector, dssdev);
 
 	/*
-	 * Otherwise we have either a fixed resolution panel or an output that
-	 * doesn't support modes discovery (e.g. DVI or VGA with the DDC bus
-	 * unconnected, or analog TV). Start by querying the size.
+	 * Otherwise if the display pipeline reports modes (e.g. with a fixed
+	 * resolution panel or an analog TV output), query it.
 	 */
-	dssdev = omap_connector->display;
-	if (dssdev->driver && dssdev->driver->get_size)
-		dssdev->driver->get_size(dssdev,
-					 &connector->display_info.width_mm,
-					 &connector->display_info.height_mm);
+	dssdev = omap_connector_find_device(connector,
+					    OMAP_DSS_DEVICE_OP_MODES);
+	if (dssdev)
+		return dssdev->ops->get_modes(dssdev, connector);
 
 	/*
-	 * Iterate over the pipeline to find the first device that can provide
-	 * timing information. If we can't find any, we just let the KMS core
-	 * add the default modes.
+	 * Otherwise if the display pipeline uses a drm_panel, we delegate the
+	 * operation to the panel API.
 	 */
-	for (dssdev = omap_connector->display; dssdev; dssdev = dssdev->src) {
-		if (dssdev->ops->get_timings)
-			break;
-	}
-	if (!dssdev)
-		return 0;
+	if (omap_connector->output->panel)
+		return drm_panel_get_modes(omap_connector->output->panel);
+
+	/*
+	 * We can't retrieve modes, which can happen for instance for a DVI or
+	 * VGA output with the DDC bus unconnected. The KMS core will add the
+	 * default modes.
+	 */
+	return 0;
+}
 
-	/* Add a single mode corresponding to the fixed panel timings. */
-	mode = drm_mode_create(connector->dev);
-	if (!mode)
-		return 0;
+enum drm_mode_status omap_connector_mode_fixup(struct omap_dss_device *dssdev,
+					const struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+	int ret;
 
-	dssdev->ops->get_timings(dssdev, &vm);
+	drm_mode_copy(adjusted_mode, mode);
 
-	drm_display_mode_from_videomode(&vm, mode);
+	for (; dssdev; dssdev = dssdev->next) {
+		if (!dssdev->ops->check_timings)
+			continue;
 
-	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
-	drm_mode_set_name(mode);
-	drm_mode_probed_add(connector, mode);
+		ret = dssdev->ops->check_timings(dssdev, adjusted_mode);
+		if (ret)
+			return MODE_BAD;
+	}
 
-	return 1;
+	return MODE_OK;
 }
 
-static int omap_connector_mode_valid(struct drm_connector *connector,
+static enum drm_mode_status omap_connector_mode_valid(struct drm_connector *connector,
 				 struct drm_display_mode *mode)
 {
 	struct omap_connector *omap_connector = to_omap_connector(connector);
-	enum omap_channel channel = omap_connector->output->dispc_channel;
-	struct omap_drm_private *priv = connector->dev->dev_private;
-	struct omap_dss_device *dssdev;
-	struct videomode vm = {0};
-	struct drm_device *dev = connector->dev;
-	struct drm_display_mode *new_mode;
-	int r, ret = MODE_BAD;
-
-	drm_display_mode_to_videomode(mode, &vm);
-	mode->vrefresh = drm_mode_vrefresh(mode);
+	struct drm_display_mode new_mode = { { 0 } };
+	enum drm_mode_status status;
 
-	r = priv->dispc_ops->mgr_check_timings(priv->dispc, channel, &vm);
-	if (r)
+	status = omap_connector_mode_fixup(omap_connector->output, mode,
+					   &new_mode);
+	if (status != MODE_OK)
 		goto done;
 
-	for (dssdev = omap_connector->output; dssdev; dssdev = dssdev->next) {
-		if (!dssdev->ops->check_timings)
-			continue;
-
-		r = dssdev->ops->check_timings(dssdev, &vm);
-		if (r)
-			goto done;
-	}
-
-	/* check if vrefresh is still valid */
-	new_mode = drm_mode_duplicate(dev, mode);
-	if (!new_mode)
-		return MODE_BAD;
-
-	new_mode->clock = vm.pixelclock / 1000;
-	new_mode->vrefresh = 0;
-	if (mode->vrefresh == drm_mode_vrefresh(new_mode))
-		ret = MODE_OK;
-	drm_mode_destroy(dev, new_mode);
+	/* Check if vrefresh is still valid. */
+	if (drm_mode_vrefresh(mode) != drm_mode_vrefresh(&new_mode))
+		status = MODE_NOCLOCK;
 
 done:
-	DBG("connector: mode %s: "
-			"%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
-			(ret == MODE_OK) ? "valid" : "invalid",
-			mode->base.id, mode->name, mode->vrefresh, mode->clock,
-			mode->hdisplay, mode->hsync_start,
-			mode->hsync_end, mode->htotal,
-			mode->vdisplay, mode->vsync_start,
-			mode->vsync_end, mode->vtotal, mode->type, mode->flags);
-
-	return ret;
+	DBG("connector: mode %s: " DRM_MODE_FMT,
+			(status == MODE_OK) ? "valid" : "invalid",
+			DRM_MODE_ARG(mode));
+
+	return status;
 }
 
 static const struct drm_connector_funcs omap_connector_funcs = {
@@ -331,9 +308,16 @@ static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
 	.mode_valid = omap_connector_mode_valid,
 };
 
-static int omap_connector_get_type(struct omap_dss_device *display)
+static int omap_connector_get_type(struct omap_dss_device *output)
 {
-	switch (display->type) {
+	struct omap_dss_device *display;
+	enum omap_display_type type;
+
+	display = omapdss_display_get(output);
+	type = display->type;
+	omapdss_device_put(display);
+
+	switch (type) {
 	case OMAP_DISPLAY_TYPE_HDMI:
 		return DRM_MODE_CONNECTOR_HDMIA;
 	case OMAP_DISPLAY_TYPE_DVI:
@@ -356,28 +340,26 @@ static int omap_connector_get_type(struct omap_dss_device *display)
 /* initialize connector */
 struct drm_connector *omap_connector_init(struct drm_device *dev,
 					  struct omap_dss_device *output,
-					  struct omap_dss_device *display,
 					  struct drm_encoder *encoder)
 {
 	struct drm_connector *connector = NULL;
 	struct omap_connector *omap_connector;
 	struct omap_dss_device *dssdev;
 
-	DBG("%s", display->name);
+	DBG("%s", output->name);
 
 	omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL);
 	if (!omap_connector)
 		goto fail;
 
 	omap_connector->output = omapdss_device_get(output);
-	omap_connector->display = omapdss_device_get(display);
 
 	connector = &omap_connector->base;
 	connector->interlace_allowed = 1;
 	connector->doublescan_allowed = 0;
 
 	drm_connector_init(dev, connector, &omap_connector_funcs,
-			   omap_connector_get_type(display));
+			   omap_connector_get_type(output));
 	drm_connector_helper_add(connector, &omap_connector_helper_funcs);
 
 	/*

+ 5 - 3
drivers/gpu/drm/omapdrm/omap_connector.h

@@ -22,6 +22,8 @@
 
 #include <linux/types.h>
 
+enum drm_mode_status;
+
 struct drm_connector;
 struct drm_device;
 struct drm_encoder;
@@ -29,12 +31,12 @@ struct omap_dss_device;
 
 struct drm_connector *omap_connector_init(struct drm_device *dev,
 					  struct omap_dss_device *output,
-					  struct omap_dss_device *display,
 					  struct drm_encoder *encoder);
-struct drm_encoder *omap_connector_attached_encoder(
-		struct drm_connector *connector);
 bool omap_connector_get_hdmi_mode(struct drm_connector *connector);
 void omap_connector_enable_hpd(struct drm_connector *connector);
 void omap_connector_disable_hpd(struct drm_connector *connector);
+enum drm_mode_status omap_connector_mode_fixup(struct omap_dss_device *dssdev,
+					const struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode);
 
 #endif /* __OMAPDRM_CONNECTOR_H__ */

+ 104 - 11
drivers/gpu/drm/omapdrm/omap_crtc.c

@@ -33,6 +33,11 @@ struct omap_crtc_state {
 	/* Shadow values for legacy userspace support. */
 	unsigned int rotation;
 	unsigned int zpos;
+
+	u32 default_color;
+	unsigned int trans_key_mode;
+	unsigned int trans_key;
+	bool alpha_blender_enabled;
 };
 
 #define to_omap_crtc(x) container_of(x, struct omap_crtc, base)
@@ -129,7 +134,7 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
 	if (WARN_ON(omap_crtc->enabled == enable))
 		return;
 
-	if (omap_crtc->pipe->output->output_type == OMAP_DISPLAY_TYPE_HDMI) {
+	if (omap_crtc->pipe->output->type == OMAP_DISPLAY_TYPE_HDMI) {
 		priv->dispc_ops->mgr_enable(priv->dispc, channel, enable);
 		omap_crtc->enabled = enable;
 		return;
@@ -334,12 +339,31 @@ static void omap_crtc_write_crtc_properties(struct drm_crtc *crtc)
 	struct omap_drm_private *priv = crtc->dev->dev_private;
 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 	struct omap_overlay_manager_info info;
+	const struct omap_crtc_state *omap_state =
+		to_omap_crtc_state(crtc->state);
 
 	memset(&info, 0, sizeof(info));
 
-	info.default_color = 0x000000;
-	info.trans_enabled = false;
-	info.partial_alpha_enabled = false;
+	info.default_color = omap_state->default_color;
+
+	info.trans_key = omap_state->trans_key;
+
+	switch (omap_state->trans_key_mode) {
+	case 0:
+	default:
+		info.trans_enabled = false;
+		break;
+	case 1:
+		info.trans_enabled = true;
+		info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+		break;
+	case 2:
+		info.trans_enabled = true;
+		info.trans_key_type = OMAP_DSS_COLOR_KEY_VID_SRC;
+		break;
+	}
+
+	info.alpha_blender_enabled = omap_state->alpha_blender_enabled;
 
 	if (crtc->state->ctm) {
 		struct drm_color_ctm *ctm =
@@ -426,6 +450,15 @@ static enum drm_mode_status omap_crtc_mode_valid(struct drm_crtc *crtc,
 					const struct drm_display_mode *mode)
 {
 	struct omap_drm_private *priv = crtc->dev->dev_private;
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	struct videomode vm = {0};
+	int r;
+
+	drm_display_mode_to_videomode(mode, &vm);
+	r = priv->dispc_ops->mgr_check_timings(priv->dispc, omap_crtc->channel,
+					       &vm);
+	if (r)
+		return r;
 
 	/* Check for bandwidth limit */
 	if (priv->max_bandwidth) {
@@ -462,12 +495,8 @@ static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc)
 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
 
-	DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
-	    omap_crtc->name, mode->base.id, mode->name,
-	    mode->vrefresh, mode->clock,
-	    mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal,
-	    mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal,
-	    mode->type, mode->flags);
+	DBG("%s: set mode: " DRM_MODE_FMT,
+	    omap_crtc->name, DRM_MODE_ARG(mode));
 
 	drm_display_mode_to_videomode(mode, &omap_crtc->vm);
 }
@@ -475,6 +504,7 @@ static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc)
 static int omap_crtc_atomic_check(struct drm_crtc *crtc,
 				struct drm_crtc_state *state)
 {
+	const struct omap_crtc_state *omap_state = to_omap_crtc_state(state);
 	struct drm_plane_state *pri_state;
 
 	if (state->color_mgmt_changed && state->gamma_lut) {
@@ -485,6 +515,25 @@ static int omap_crtc_atomic_check(struct drm_crtc *crtc,
 			return -EINVAL;
 	}
 
+	if (omap_state->trans_key_mode) {
+		struct drm_plane *plane;
+		struct drm_plane_state *plane_state;
+		u32 zpos_mask = 0;
+
+		drm_for_each_plane_mask(plane, crtc->dev, state->plane_mask) {
+			plane_state = drm_atomic_get_plane_state(state->state,
+								 plane);
+			if (IS_ERR(plane_state))
+				return PTR_ERR(plane_state);
+
+			if (zpos_mask & BIT(plane_state->zpos))
+				return -EINVAL;
+
+			zpos_mask |= BIT(plane_state->zpos);
+			plane_state->normalized_zpos = plane_state->zpos;
+		}
+	}
+
 	pri_state = drm_atomic_get_new_plane_state(state->state, crtc->primary);
 	if (pri_state) {
 		struct omap_crtc_state *omap_crtc_state =
@@ -548,6 +597,7 @@ static int omap_crtc_atomic_set_property(struct drm_crtc *crtc,
 {
 	struct omap_drm_private *priv = crtc->dev->dev_private;
 	struct drm_plane_state *plane_state;
+	struct omap_crtc_state *omap_state = to_omap_crtc_state(state);
 
 	/*
 	 * Delegate property set to the primary plane. Get the plane state and
@@ -563,6 +613,14 @@ static int omap_crtc_atomic_set_property(struct drm_crtc *crtc,
 		plane_state->rotation = val;
 	else if (property == priv->zorder_prop)
 		plane_state->zpos = val;
+	else if (property == priv->background_color_prop)
+		omap_state->default_color = val;
+	else if (property == priv->trans_key_mode_prop)
+		omap_state->trans_key_mode = val;
+	else if (property == priv->trans_key_prop)
+		omap_state->trans_key = val;
+	else if (property == priv->alpha_blender_prop)
+		omap_state->alpha_blender_enabled = !!val;
 	else
 		return -EINVAL;
 
@@ -581,12 +639,28 @@ static int omap_crtc_atomic_get_property(struct drm_crtc *crtc,
 		*val = omap_state->rotation;
 	else if (property == priv->zorder_prop)
 		*val = omap_state->zpos;
+	else if (property == priv->background_color_prop)
+		*val = omap_state->default_color;
+	else if (property == priv->trans_key_mode_prop)
+		*val = omap_state->trans_key_mode;
+	else if (property == priv->trans_key_prop)
+		*val = omap_state->trans_key;
+	else if (property == priv->alpha_blender_prop)
+		*val = omap_state->alpha_blender_enabled;
 	else
 		return -EINVAL;
 
 	return 0;
 }
 
+int omap_crtc_atomic_get_trans_key_mode(struct drm_crtc *crtc,
+					const struct drm_crtc_state *state)
+{
+	struct omap_crtc_state *omap_state = to_omap_crtc_state(state);
+
+	return omap_state->trans_key_mode;
+}
+
 static void omap_crtc_reset(struct drm_crtc *crtc)
 {
 	if (crtc->state)
@@ -618,6 +692,12 @@ omap_crtc_duplicate_state(struct drm_crtc *crtc)
 	state->zpos = current_state->zpos;
 	state->rotation = current_state->rotation;
 
+	state->default_color = current_state->default_color;
+
+	state->trans_key_mode = current_state->trans_key_mode;
+	state->trans_key = current_state->trans_key;
+	state->alpha_blender_enabled = current_state->alpha_blender_enabled;
+
 	return &state->base;
 }
 
@@ -666,6 +746,18 @@ void omap_crtc_pre_uninit(struct omap_drm_private *priv)
 	dss_uninstall_mgr_ops(priv->dss);
 }
 
+static void omap_crtc_install_properties(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_mode_object *obj = &crtc->base;
+	struct omap_drm_private *priv = dev->dev_private;
+
+	drm_object_attach_property(obj, priv->background_color_prop, 0);
+	drm_object_attach_property(obj, priv->trans_key_mode_prop, 0);
+	drm_object_attach_property(obj, priv->trans_key_prop, 0);
+	drm_object_attach_property(obj, priv->alpha_blender_prop, 0);
+}
+
 /* initialize crtc */
 struct drm_crtc *omap_crtc_init(struct drm_device *dev,
 				struct omap_drm_pipeline *pipe,
@@ -697,7 +789,7 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
 					&omap_crtc_funcs, NULL);
 	if (ret < 0) {
 		dev_err(dev->dev, "%s(): could not init crtc for: %s\n",
-			__func__, pipe->display->name);
+			__func__, pipe->output->name);
 		kfree(omap_crtc);
 		return ERR_PTR(ret);
 	}
@@ -718,6 +810,7 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
 		drm_mode_crtc_set_gamma_size(crtc, gamma_lut_size);
 	}
 
+	omap_crtc_install_properties(crtc);
 	omap_plane_install_properties(crtc->primary, &crtc->base);
 
 	return crtc;

+ 3 - 0
drivers/gpu/drm/omapdrm/omap_crtc.h

@@ -41,5 +41,8 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
 int omap_crtc_wait_pending(struct drm_crtc *crtc);
 void omap_crtc_error_irq(struct drm_crtc *crtc, u32 irqstatus);
 void omap_crtc_vblank_irq(struct drm_crtc *crtc);
+int omap_crtc_atomic_get_trans_key_mode(struct drm_crtc *crtc,
+					const struct drm_crtc_state *state);
+
 
 #endif /* __OMAPDRM_CRTC_H__ */

+ 172 - 121
drivers/gpu/drm/omapdrm/omap_drv.c

@@ -23,6 +23,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_panel.h>
 
 #include "omap_dmm_tiler.h"
 #include "omap_drv.h"
@@ -149,6 +150,9 @@ static int omap_atomic_update_normalize_zpos(struct drm_device *dev,
 		    !new_state->zpos_changed)
 			continue;
 
+		if (omap_crtc_atomic_get_trans_key_mode(crtc, new_state))
+			continue;
+
 		/* Reset plane increment and index value for every crtc */
 		n = 0;
 
@@ -314,12 +318,13 @@ static void omap_disconnect_pipelines(struct drm_device *ddev)
 	for (i = 0; i < priv->num_pipes; i++) {
 		struct omap_drm_pipeline *pipe = &priv->pipes[i];
 
+		if (pipe->output->panel)
+			drm_panel_detach(pipe->output->panel);
+
 		omapdss_device_disconnect(NULL, pipe->output);
 
 		omapdss_device_put(pipe->output);
-		omapdss_device_put(pipe->display);
 		pipe->output = NULL;
-		pipe->display = NULL;
 	}
 
 	memset(&priv->channels, 0, sizeof(priv->channels));
@@ -327,33 +332,17 @@ static void omap_disconnect_pipelines(struct drm_device *ddev)
 	priv->num_pipes = 0;
 }
 
-static int omap_compare_pipes(const void *a, const void *b)
-{
-	const struct omap_drm_pipeline *pipe1 = a;
-	const struct omap_drm_pipeline *pipe2 = b;
-
-	if (pipe1->display->alias_id > pipe2->display->alias_id)
-		return 1;
-	else if (pipe1->display->alias_id < pipe2->display->alias_id)
-		return -1;
-	return 0;
-}
-
 static int omap_connect_pipelines(struct drm_device *ddev)
 {
 	struct omap_drm_private *priv = ddev->dev_private;
 	struct omap_dss_device *output = NULL;
-	unsigned int i;
 	int r;
 
-	if (!omapdss_stack_is_ready())
-		return -EPROBE_DEFER;
-
 	for_each_dss_output(output) {
 		r = omapdss_device_connect(priv->dss, NULL, output);
 		if (r == -EPROBE_DEFER) {
 			omapdss_device_put(output);
-			goto cleanup;
+			return r;
 		} else if (r) {
 			dev_warn(output->dev, "could not connect output %s\n",
 				 output->name);
@@ -362,7 +351,6 @@ static int omap_connect_pipelines(struct drm_device *ddev)
 
 			pipe = &priv->pipes[priv->num_pipes++];
 			pipe->output = omapdss_device_get(output);
-			pipe->display = omapdss_display_get(output);
 
 			if (priv->num_pipes == ARRAY_SIZE(priv->pipes)) {
 				/* To balance the 'for_each_dss_output' loop */
@@ -372,36 +360,19 @@ static int omap_connect_pipelines(struct drm_device *ddev)
 		}
 	}
 
-	/* Sort the list by DT aliases */
-	sort(priv->pipes, priv->num_pipes, sizeof(priv->pipes[0]),
-	     omap_compare_pipes, NULL);
-
-	/*
-	 * Populate the pipeline lookup table by DISPC channel. Only one display
-	 * is allowed per channel.
-	 */
-	for (i = 0; i < priv->num_pipes; ++i) {
-		struct omap_drm_pipeline *pipe = &priv->pipes[i];
-		enum omap_channel channel = pipe->output->dispc_channel;
-
-		if (WARN_ON(priv->channels[channel] != NULL)) {
-			r = -EINVAL;
-			goto cleanup;
-		}
-
-		priv->channels[channel] = pipe;
-	}
-
 	return 0;
+}
 
-cleanup:
-	/*
-	 * if we are deferring probe, we disconnect the devices we previously
-	 * connected
-	 */
-	omap_disconnect_pipelines(ddev);
+static int omap_compare_pipelines(const void *a, const void *b)
+{
+	const struct omap_drm_pipeline *pipe1 = a;
+	const struct omap_drm_pipeline *pipe2 = b;
 
-	return r;
+	if (pipe1->alias_id > pipe2->alias_id)
+		return 1;
+	else if (pipe1->alias_id < pipe2->alias_id)
+		return -1;
+	return 0;
 }
 
 static int omap_modeset_init_properties(struct drm_device *dev)
@@ -409,14 +380,77 @@ static int omap_modeset_init_properties(struct drm_device *dev)
 	struct omap_drm_private *priv = dev->dev_private;
 	unsigned int num_planes = priv->dispc_ops->get_num_ovls(priv->dispc);
 
+	static const struct drm_prop_enum_list trans_key_mode_list[] = {
+		{ 0, "disable"},
+		{ 1, "gfx-dst"},
+		{ 2, "vid-src"},
+	};
+
 	priv->zorder_prop = drm_property_create_range(dev, 0, "zorder", 0,
 						      num_planes - 1);
 	if (!priv->zorder_prop)
 		return -ENOMEM;
 
+	priv->global_alpha_prop = drm_property_create_range(dev, 0,
+		"global_alpha", 0, 255);
+	if (!priv->global_alpha_prop)
+		return -ENOMEM;
+
+	priv->pre_mult_alpha_prop = drm_property_create_bool(dev, 0,
+		"pre_mult_alpha");
+	if (!priv->pre_mult_alpha_prop)
+		return -ENOMEM;
+
+	/* crtc properties */
+
+	priv->background_color_prop = drm_property_create_range(dev, 0,
+		"background", 0, 0xffffff);
+	if (!priv->background_color_prop)
+		return -ENOMEM;
+
+	priv->trans_key_mode_prop = drm_property_create_enum(dev, 0,
+		"trans-key-mode",
+		trans_key_mode_list, ARRAY_SIZE(trans_key_mode_list));
+	if (!priv->trans_key_mode_prop)
+		return -ENOMEM;
+
+	priv->trans_key_prop = drm_property_create_range(dev, 0, "trans-key",
+		0, 0xffffff);
+	if (!priv->trans_key_prop)
+		return -ENOMEM;
+
+	priv->alpha_blender_prop = drm_property_create_bool(dev, 0,
+		"alpha_blender");
+	if (!priv->alpha_blender_prop)
+		return -ENOMEM;
+
 	return 0;
 }
 
+static int omap_display_id(struct omap_dss_device *output)
+{
+	struct device_node *node = NULL;
+
+	if (output->next) {
+		struct omap_dss_device *display;
+
+		display = omapdss_display_get(output);
+		node = display->dev->of_node;
+		omapdss_device_put(display);
+	} else if (output->bridge) {
+		struct drm_bridge *bridge = output->bridge;
+
+		while (bridge->next)
+			bridge = bridge->next;
+
+		node = bridge->of_node;
+	} else if (output->panel) {
+		node = output->panel->dev->of_node;
+	}
+
+	return node ? of_alias_get_id(node, "display") : -ENODEV;
+}
+
 static int omap_modeset_init(struct drm_device *dev)
 {
 	struct omap_drm_private *priv = dev->dev_private;
@@ -426,6 +460,9 @@ static int omap_modeset_init(struct drm_device *dev)
 	int ret;
 	u32 plane_crtc_mask;
 
+	if (!omapdss_stack_is_ready())
+		return -EPROBE_DEFER;
+
 	drm_mode_config_init(dev);
 
 	ret = omap_modeset_init_properties(dev);
@@ -440,6 +477,10 @@ static int omap_modeset_init(struct drm_device *dev)
 	 * configuration does not match the expectations or exceeds
 	 * the available resources, the configuration is rejected.
 	 */
+	ret = omap_connect_pipelines(dev);
+	if (ret < 0)
+		return ret;
+
 	if (priv->num_pipes > num_mgrs || priv->num_pipes > num_ovls) {
 		dev_err(dev->dev, "%s(): Too many connected displays\n",
 			__func__);
@@ -465,33 +506,75 @@ static int omap_modeset_init(struct drm_device *dev)
 		priv->planes[priv->num_planes++] = plane;
 	}
 
-	/* Create the CRTCs, encoders and connectors. */
+	/*
+	 * Create the encoders, attach the bridges and get the pipeline alias
+	 * IDs.
+	 */
 	for (i = 0; i < priv->num_pipes; i++) {
 		struct omap_drm_pipeline *pipe = &priv->pipes[i];
-		struct omap_dss_device *display = pipe->display;
-		struct drm_connector *connector;
-		struct drm_encoder *encoder;
-		struct drm_crtc *crtc;
+		int id;
 
-		encoder = omap_encoder_init(dev, pipe->output, display);
-		if (!encoder)
+		pipe->encoder = omap_encoder_init(dev, pipe->output);
+		if (!pipe->encoder)
 			return -ENOMEM;
 
-		connector = omap_connector_init(dev, pipe->output, display,
-						encoder);
-		if (!connector)
-			return -ENOMEM;
+		if (pipe->output->bridge) {
+			ret = drm_bridge_attach(pipe->encoder,
+						pipe->output->bridge, NULL);
+			if (ret < 0)
+				return ret;
+		}
+
+		id = omap_display_id(pipe->output);
+		pipe->alias_id = id >= 0 ? id : i;
+	}
+
+	/* Sort the pipelines by DT aliases. */
+	sort(priv->pipes, priv->num_pipes, sizeof(priv->pipes[0]),
+	     omap_compare_pipelines, NULL);
+
+	/*
+	 * Populate the pipeline lookup table by DISPC channel. Only one display
+	 * is allowed per channel.
+	 */
+	for (i = 0; i < priv->num_pipes; ++i) {
+		struct omap_drm_pipeline *pipe = &priv->pipes[i];
+		enum omap_channel channel = pipe->output->dispc_channel;
+
+		if (WARN_ON(priv->channels[channel] != NULL))
+			return -EINVAL;
+
+		priv->channels[channel] = pipe;
+	}
+
+	/* Create the connectors and CRTCs. */
+	for (i = 0; i < priv->num_pipes; i++) {
+		struct omap_drm_pipeline *pipe = &priv->pipes[i];
+		struct drm_encoder *encoder = pipe->encoder;
+		struct drm_crtc *crtc;
+
+		if (!pipe->output->bridge) {
+			pipe->connector = omap_connector_init(dev, pipe->output,
+							      encoder);
+			if (!pipe->connector)
+				return -ENOMEM;
+
+			drm_connector_attach_encoder(pipe->connector, encoder);
+
+			if (pipe->output->panel) {
+				ret = drm_panel_attach(pipe->output->panel,
+						       pipe->connector);
+				if (ret < 0)
+					return ret;
+			}
+		}
 
 		crtc = omap_crtc_init(dev, pipe, priv->planes[i]);
 		if (IS_ERR(crtc))
 			return PTR_ERR(crtc);
 
-		drm_connector_attach_encoder(connector, encoder);
 		encoder->possible_crtcs = 1 << i;
-
 		pipe->crtc = crtc;
-		pipe->encoder = encoder;
-		pipe->connector = connector;
 	}
 
 	DBG("registered %u planes, %u crtcs/encoders/connectors\n",
@@ -528,10 +611,12 @@ static int omap_modeset_init(struct drm_device *dev)
 static void omap_modeset_enable_external_hpd(struct drm_device *ddev)
 {
 	struct omap_drm_private *priv = ddev->dev_private;
-	int i;
+	unsigned int i;
 
-	for (i = 0; i < priv->num_pipes; i++)
-		omap_connector_enable_hpd(priv->pipes[i].connector);
+	for (i = 0; i < priv->num_pipes; i++) {
+		if (priv->pipes[i].connector)
+			omap_connector_enable_hpd(priv->pipes[i].connector);
+	}
 }
 
 /*
@@ -540,10 +625,12 @@ static void omap_modeset_enable_external_hpd(struct drm_device *ddev)
 static void omap_modeset_disable_external_hpd(struct drm_device *ddev)
 {
 	struct omap_drm_private *priv = ddev->dev_private;
-	int i;
+	unsigned int i;
 
-	for (i = 0; i < priv->num_pipes; i++)
-		omap_connector_disable_hpd(priv->pipes[i].connector);
+	for (i = 0; i < priv->num_pipes; i++) {
+		if (priv->pipes[i].connector)
+			omap_connector_disable_hpd(priv->pipes[i].connector);
+	}
 }
 
 /*
@@ -728,10 +815,6 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev)
 
 	omap_crtc_pre_init(priv);
 
-	ret = omap_connect_pipelines(ddev);
-	if (ret)
-		goto err_crtc_uninit;
-
 	soc = soc_device_match(omapdrm_soc_devices);
 	priv->omaprev = soc ? (unsigned int)soc->data : 0;
 	priv->wq = alloc_ordered_workqueue("omapdrm", 0);
@@ -775,6 +858,14 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev)
 	drm_kms_helper_poll_init(ddev);
 	omap_modeset_enable_external_hpd(ddev);
 
+	if (priv->dispc_ops->has_writeback(priv->dispc)) {
+		ret = omap_wb_init(ddev);
+		if (ret)
+			dev_warn(priv->dev, "failed to initialize writeback\n");
+		else
+			priv->wb_initialized = true;
+	}
+
 	/*
 	 * Register the DRM device with the core and the connectors with
 	 * sysfs.
@@ -786,7 +877,11 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev)
 	return 0;
 
 err_cleanup_helpers:
+	if (priv->wb_initialized)
+		omap_wb_cleanup(ddev);
+
 	omap_modeset_disable_external_hpd(ddev);
+
 	drm_kms_helper_poll_fini(ddev);
 
 	omap_fbdev_fini(ddev);
@@ -801,7 +896,6 @@ err_gem_deinit:
 	omap_gem_deinit(ddev);
 	destroy_workqueue(priv->wq);
 	omap_disconnect_pipelines(ddev);
-err_crtc_uninit:
 	omap_crtc_pre_uninit(priv);
 	drm_dev_put(ddev);
 	return ret;
@@ -815,6 +909,9 @@ static void omapdrm_cleanup(struct omap_drm_private *priv)
 
 	drm_dev_unregister(ddev);
 
+	if (priv->wb_initialized)
+		omap_wb_cleanup(ddev);
+
 	omap_modeset_disable_external_hpd(ddev);
 	drm_kms_helper_poll_fini(ddev);
 
@@ -877,54 +974,12 @@ static int pdev_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM_SLEEP
-static int omap_drm_suspend_all_displays(struct drm_device *ddev)
-{
-	struct omap_drm_private *priv = ddev->dev_private;
-	int i;
-
-	for (i = 0; i < priv->num_pipes; i++) {
-		struct omap_dss_device *display = priv->pipes[i].display;
-
-		if (display->state == OMAP_DSS_DISPLAY_ACTIVE) {
-			display->ops->disable(display);
-			display->activate_after_resume = true;
-		} else {
-			display->activate_after_resume = false;
-		}
-	}
-
-	return 0;
-}
-
-static int omap_drm_resume_all_displays(struct drm_device *ddev)
-{
-	struct omap_drm_private *priv = ddev->dev_private;
-	int i;
-
-	for (i = 0; i < priv->num_pipes; i++) {
-		struct omap_dss_device *display = priv->pipes[i].display;
-
-		if (display->activate_after_resume) {
-			display->ops->enable(display);
-			display->activate_after_resume = false;
-		}
-	}
-
-	return 0;
-}
-
 static int omap_drm_suspend(struct device *dev)
 {
 	struct omap_drm_private *priv = dev_get_drvdata(dev);
 	struct drm_device *drm_dev = priv->ddev;
 
-	drm_kms_helper_poll_disable(drm_dev);
-
-	drm_modeset_lock_all(drm_dev);
-	omap_drm_suspend_all_displays(drm_dev);
-	drm_modeset_unlock_all(drm_dev);
-
-	return 0;
+	return drm_mode_config_helper_suspend(drm_dev);
 }
 
 static int omap_drm_resume(struct device *dev)
@@ -932,11 +987,7 @@ static int omap_drm_resume(struct device *dev)
 	struct omap_drm_private *priv = dev_get_drvdata(dev);
 	struct drm_device *drm_dev = priv->ddev;
 
-	drm_modeset_lock_all(drm_dev);
-	omap_drm_resume_all_displays(drm_dev);
-	drm_modeset_unlock_all(drm_dev);
-
-	drm_kms_helper_poll_enable(drm_dev);
+	drm_mode_config_helper_resume(drm_dev);
 
 	return omap_gem_resume(drm_dev);
 }

+ 32 - 1
drivers/gpu/drm/omapdrm/omap_drv.h

@@ -52,7 +52,7 @@ struct omap_drm_pipeline {
 	struct drm_encoder *encoder;
 	struct drm_connector *connector;
 	struct omap_dss_device *output;
-	struct omap_dss_device *display;
+	unsigned int alias_id;
 };
 
 /*
@@ -110,6 +110,14 @@ struct omap_drm_private {
 
 	/* properties: */
 	struct drm_property *zorder_prop;
+	struct drm_property *global_alpha_prop;
+	struct drm_property *pre_mult_alpha_prop;
+
+	/* crtc properties */
+	struct drm_property *background_color_prop;
+	struct drm_property *trans_key_mode_prop;
+	struct drm_property *trans_key_prop;
+	struct drm_property *alpha_blender_prop;
 
 	/* irq handling: */
 	spinlock_t wait_lock;		/* protects the wait_list */
@@ -118,6 +126,9 @@ struct omap_drm_private {
 
 	/* memory bandwidth limit if it is needed on the platform */
 	unsigned int max_bandwidth;
+
+	void *wb_private;	      /* Write-back private data */
+	bool wb_initialized;
 };
 
 
@@ -127,4 +138,24 @@ omap_get_global_state(struct drm_atomic_state *s);
 struct omap_global_state *
 omap_get_existing_global_state(struct omap_drm_private *priv);
 
+#if IS_ENABLED(CONFIG_DRM_OMAP_WB)
+
+#define OMAP_WB_IRQ_MASK (DISPC_IRQ_FRAMEDONEWB | \
+			  DISPC_IRQ_WBBUFFEROVERFLOW | \
+			  DISPC_IRQ_WBUNCOMPLETEERROR)
+
+int omap_wb_init(struct drm_device *drmdev);
+void omap_wb_cleanup(struct drm_device *drmdev);
+void omap_wb_irq(void *priv, u32 irqstatus);
+
+#else
+
+#define OMAP_WB_IRQ_MASK (0)
+
+static inline int omap_wb_init(struct drm_device *drmdev) { return 0; }
+static inline void omap_wb_cleanup(struct drm_device *drmdev) { }
+static inline void omap_wb_irq(void *priv, u32 irqstatus) { }
+
+#endif
+
 #endif /* __OMAPDRM_DRV_H__ */

+ 129 - 82
drivers/gpu/drm/omapdrm/omap_encoder.c

@@ -20,6 +20,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
+#include <drm/drm_panel.h>
 
 #include "omap_drv.h"
 
@@ -37,7 +38,6 @@
 struct omap_encoder {
 	struct drm_encoder base;
 	struct omap_dss_device *output;
-	struct omap_dss_device *display;
 };
 
 static void omap_encoder_destroy(struct drm_encoder *encoder)
@@ -52,22 +52,43 @@ static const struct drm_encoder_funcs omap_encoder_funcs = {
 	.destroy = omap_encoder_destroy,
 };
 
-static void omap_encoder_hdmi_mode_set(struct drm_encoder *encoder,
+static void omap_encoder_update_videomode_flags(struct videomode *vm,
+						u32 bus_flags)
+{
+	if (!(vm->flags & (DISPLAY_FLAGS_DE_LOW |
+			   DISPLAY_FLAGS_DE_HIGH))) {
+		if (bus_flags & DRM_BUS_FLAG_DE_LOW)
+			vm->flags |= DISPLAY_FLAGS_DE_LOW;
+		else if (bus_flags & DRM_BUS_FLAG_DE_HIGH)
+			vm->flags |= DISPLAY_FLAGS_DE_HIGH;
+	}
+
+	if (!(vm->flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE |
+			   DISPLAY_FLAGS_PIXDATA_NEGEDGE))) {
+		if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
+			vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
+		else if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
+			vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
+	}
+
+	if (!(vm->flags & (DISPLAY_FLAGS_SYNC_POSEDGE |
+			   DISPLAY_FLAGS_SYNC_NEGEDGE))) {
+		if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE)
+			vm->flags |= DISPLAY_FLAGS_SYNC_POSEDGE;
+		else if (bus_flags & DRM_BUS_FLAG_SYNC_DRIVE_NEGEDGE)
+			vm->flags |= DISPLAY_FLAGS_SYNC_NEGEDGE;
+	}
+}
+
+static void omap_encoder_hdmi_mode_set(struct drm_connector *connector,
+				       struct drm_encoder *encoder,
 				       struct drm_display_mode *adjusted_mode)
 {
-	struct drm_device *dev = encoder->dev;
 	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
 	struct omap_dss_device *dssdev = omap_encoder->output;
-	struct drm_connector *connector;
 	bool hdmi_mode;
 
-	hdmi_mode = false;
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-		if (connector->encoder == encoder) {
-			hdmi_mode = omap_connector_get_hdmi_mode(connector);
-			break;
-		}
-	}
+	hdmi_mode = omap_connector_get_hdmi_mode(connector);
 
 	if (dssdev->ops->hdmi.set_hdmi_mode)
 		dssdev->ops->hdmi.set_hdmi_mode(dssdev, hdmi_mode);
@@ -88,8 +109,18 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,
 				  struct drm_display_mode *adjusted_mode)
 {
 	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+	struct omap_dss_device *output = omap_encoder->output;
 	struct omap_dss_device *dssdev;
+	struct drm_device *dev = encoder->dev;
+	struct drm_connector *connector;
+	struct drm_bridge *bridge;
 	struct videomode vm = { 0 };
+	u32 bus_flags;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		if (connector->encoder == encoder)
+			break;
+	}
 
 	drm_display_mode_to_videomode(adjusted_mode, &vm);
 
@@ -102,66 +133,102 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,
 	 *
 	 * A better solution is to use DRM's bus-flags through the whole driver.
 	 */
-	for (dssdev = omap_encoder->output; dssdev; dssdev = dssdev->next) {
-		unsigned long bus_flags = dssdev->bus_flags;
-
-		if (!(vm.flags & (DISPLAY_FLAGS_DE_LOW |
-				  DISPLAY_FLAGS_DE_HIGH))) {
-			if (bus_flags & DRM_BUS_FLAG_DE_LOW)
-				vm.flags |= DISPLAY_FLAGS_DE_LOW;
-			else if (bus_flags & DRM_BUS_FLAG_DE_HIGH)
-				vm.flags |= DISPLAY_FLAGS_DE_HIGH;
-		}
-
-		if (!(vm.flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE |
-				  DISPLAY_FLAGS_PIXDATA_NEGEDGE))) {
-			if (bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE)
-				vm.flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
-			else if (bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE)
-				vm.flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
-		}
-
-		if (!(vm.flags & (DISPLAY_FLAGS_SYNC_POSEDGE |
-				  DISPLAY_FLAGS_SYNC_NEGEDGE))) {
-			if (bus_flags & DRM_BUS_FLAG_SYNC_POSEDGE)
-				vm.flags |= DISPLAY_FLAGS_SYNC_POSEDGE;
-			else if (bus_flags & DRM_BUS_FLAG_SYNC_NEGEDGE)
-				vm.flags |= DISPLAY_FLAGS_SYNC_NEGEDGE;
-		}
+	for (dssdev = output; dssdev; dssdev = dssdev->next)
+		omap_encoder_update_videomode_flags(&vm, dssdev->bus_flags);
+
+	for (bridge = output->bridge; bridge; bridge = bridge->next) {
+		if (!bridge->timings)
+			continue;
+
+		bus_flags = bridge->timings->input_bus_flags;
+		omap_encoder_update_videomode_flags(&vm, bus_flags);
 	}
 
+	bus_flags = connector->display_info.bus_flags;
+	omap_encoder_update_videomode_flags(&vm, bus_flags);
+
 	/* Set timings for all devices in the display pipeline. */
-	dss_mgr_set_timings(omap_encoder->output, &vm);
+	dss_mgr_set_timings(output, &vm);
 
-	for (dssdev = omap_encoder->output; dssdev; dssdev = dssdev->next) {
+	for (dssdev = output; dssdev; dssdev = dssdev->next) {
 		if (dssdev->ops->set_timings)
-			dssdev->ops->set_timings(dssdev, &vm);
+			dssdev->ops->set_timings(dssdev, adjusted_mode);
 	}
 
 	/* Set the HDMI mode and HDMI infoframe if applicable. */
-	if (omap_encoder->output->output_type == OMAP_DISPLAY_TYPE_HDMI)
-		omap_encoder_hdmi_mode_set(encoder, adjusted_mode);
+	if (output->type == OMAP_DISPLAY_TYPE_HDMI)
+		omap_encoder_hdmi_mode_set(connector, encoder, adjusted_mode);
 }
 
 static void omap_encoder_disable(struct drm_encoder *encoder)
 {
 	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
-	struct omap_dss_device *dssdev = omap_encoder->display;
+	struct omap_dss_device *dssdev = omap_encoder->output;
+	struct drm_device *dev = encoder->dev;
+
+	dev_dbg(dev->dev, "disable(%s)\n", dssdev->name);
+
+	/* Disable the panel if present. */
+	if (dssdev->panel) {
+		drm_panel_disable(dssdev->panel);
+		drm_panel_unprepare(dssdev->panel);
+	}
+
+	/*
+	 * Disable the chain of external devices, starting at the one at the
+	 * internal encoder's output.
+	 */
+	omapdss_device_disable(dssdev->next);
+
+	/*
+	 * Disable the internal encoder. This will disable the DSS output. The
+	 * DSI is treated as an exception as DSI pipelines still use the legacy
+	 * flow where the pipeline output controls the encoder.
+	 */
+	if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) {
+		dssdev->ops->disable(dssdev);
+		dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+	}
 
-	dssdev->ops->disable(dssdev);
+	/*
+	 * Perform the post-disable operations on the chain of external devices
+	 * to complete the display pipeline disable.
+	 */
+	omapdss_device_post_disable(dssdev->next);
 }
 
 static void omap_encoder_enable(struct drm_encoder *encoder)
 {
 	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
-	struct omap_dss_device *dssdev = omap_encoder->display;
-	int r;
-
-	r = dssdev->ops->enable(dssdev);
-	if (r)
-		dev_err(encoder->dev->dev,
-			"Failed to enable display '%s': %d\n",
-			dssdev->name, r);
+	struct omap_dss_device *dssdev = omap_encoder->output;
+	struct drm_device *dev = encoder->dev;
+
+	dev_dbg(dev->dev, "enable(%s)\n", dssdev->name);
+
+	/* Prepare the chain of external devices for pipeline enable. */
+	omapdss_device_pre_enable(dssdev->next);
+
+	/*
+	 * Enable the internal encoder. This will enable the DSS output. The
+	 * DSI is treated as an exception as DSI pipelines still use the legacy
+	 * flow where the pipeline output controls the encoder.
+	 */
+	if (dssdev->type != OMAP_DISPLAY_TYPE_DSI) {
+		dssdev->ops->enable(dssdev);
+		dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+	}
+
+	/*
+	 * Enable the chain of external devices, starting at the one at the
+	 * internal encoder's output.
+	 */
+	omapdss_device_enable(dssdev->next);
+
+	/* Enable the panel if present. */
+	if (dssdev->panel) {
+		drm_panel_prepare(dssdev->panel);
+		drm_panel_enable(dssdev->panel);
+	}
 }
 
 static int omap_encoder_atomic_check(struct drm_encoder *encoder,
@@ -169,35 +236,17 @@ static int omap_encoder_atomic_check(struct drm_encoder *encoder,
 				     struct drm_connector_state *conn_state)
 {
 	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
-	enum omap_channel channel = omap_encoder->output->dispc_channel;
-	struct drm_device *dev = encoder->dev;
-	struct omap_drm_private *priv = dev->dev_private;
-	struct omap_dss_device *dssdev;
-	struct videomode vm = { 0 };
-	int ret;
-
-	drm_display_mode_to_videomode(&crtc_state->mode, &vm);
-
-	ret = priv->dispc_ops->mgr_check_timings(priv->dispc, channel, &vm);
-	if (ret)
-		goto done;
-
-	for (dssdev = omap_encoder->output; dssdev; dssdev = dssdev->next) {
-		if (!dssdev->ops->check_timings)
-			continue;
-
-		ret = dssdev->ops->check_timings(dssdev, &vm);
-		if (ret)
-			goto done;
+	enum drm_mode_status status;
+
+	status = omap_connector_mode_fixup(omap_encoder->output,
+					   &crtc_state->mode,
+					   &crtc_state->adjusted_mode);
+	if (status != MODE_OK) {
+		dev_err(encoder->dev->dev, "invalid timings: %d\n", status);
+		return -EINVAL;
 	}
 
-	drm_display_mode_from_videomode(&vm, &crtc_state->adjusted_mode);
-
-done:
-	if (ret)
-		dev_err(dev->dev, "invalid timings: %d\n", ret);
-
-	return ret;
+	return 0;
 }
 
 static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
@@ -209,8 +258,7 @@ static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
 
 /* initialize encoder */
 struct drm_encoder *omap_encoder_init(struct drm_device *dev,
-				      struct omap_dss_device *output,
-				      struct omap_dss_device *display)
+				      struct omap_dss_device *output)
 {
 	struct drm_encoder *encoder = NULL;
 	struct omap_encoder *omap_encoder;
@@ -220,7 +268,6 @@ struct drm_encoder *omap_encoder_init(struct drm_device *dev,
 		goto fail;
 
 	omap_encoder->output = output;
-	omap_encoder->display = display;
 
 	encoder = &omap_encoder->base;
 

+ 1 - 2
drivers/gpu/drm/omapdrm/omap_encoder.h

@@ -25,7 +25,6 @@ struct drm_encoder;
 struct omap_dss_device;
 
 struct drm_encoder *omap_encoder_init(struct drm_device *dev,
-				      struct omap_dss_device *output,
-				      struct omap_dss_device *display);
+				      struct omap_dss_device *output);
 
 #endif /* __OMAPDRM_ENCODER_H__ */

+ 4 - 0
drivers/gpu/drm/omapdrm/omap_irq.c

@@ -221,6 +221,7 @@ static irqreturn_t omap_irq_handler(int irq, void *arg)
 
 	omap_irq_ocp_error_handler(dev, irqstatus);
 	omap_irq_fifo_underflow(priv, irqstatus);
+	omap_wb_irq(priv->wb_private, irqstatus);
 
 	spin_lock_irqsave(&priv->wait_lock, flags);
 	list_for_each_entry_safe(wait, n, &priv->wait_list, node) {
@@ -269,6 +270,9 @@ int omap_drm_irq_install(struct drm_device *dev)
 	for (i = 0; i < num_mgrs; ++i)
 		priv->irq_mask |= priv->dispc_ops->mgr_get_sync_lost_irq(priv->dispc, i);
 
+	if (priv->dispc_ops->has_writeback(priv->dispc))
+		priv->irq_mask |= OMAP_WB_IRQ_MASK;
+
 	priv->dispc_ops->runtime_get(priv->dispc);
 	priv->dispc_ops->clear_irqstatus(priv->dispc, 0xffffffff);
 	priv->dispc_ops->runtime_put(priv->dispc);

+ 76 - 0
drivers/gpu/drm/omapdrm/omap_overlay.c

@@ -191,6 +191,82 @@ void omap_overlay_disable(struct drm_atomic_state *s,
 	 */
 }
 
+int omap_overlay_assign_wb(struct omap_drm_private *priv,
+			   struct drm_plane *plane,
+			   u32 caps, u32 fourcc, u32 crtc_mask,
+			   struct omap_hw_overlay **overlay)
+{
+	struct omap_global_state *old_global_state;
+	struct drm_plane **overlay_map;
+	struct omap_hw_overlay *ovl;
+
+	/*
+	 * As there is no state here we can't really grab the global obj lock.
+	 * This might cause issue!
+	 */
+	old_global_state = omap_get_existing_global_state(priv);
+	DBG("old_global_state: %p", old_global_state);
+
+	overlay_map = old_global_state->hwoverlay_to_plane;
+
+	if (!*overlay) {
+		ovl = omap_plane_find_free_overlay(plane->dev, overlay_map,
+						   caps, fourcc, crtc_mask);
+		if (!ovl)
+			return -ENOMEM;
+
+		overlay_map[ovl->idx] = plane;
+		*overlay = ovl;
+
+		DBG("%s: assign to WB plane %s for caps %x",
+		    (*overlay)->name, plane->name, caps);
+	}
+
+	return 0;
+}
+
+void omap_overlay_release_wb(struct omap_drm_private *priv,
+			     struct drm_plane *plane,
+			     struct omap_hw_overlay *overlay)
+{
+	struct omap_global_state *old_global_state;
+	struct drm_plane **overlay_map;
+
+	if (!overlay)
+		return;
+
+	/*
+	 * As there is no state here we can't really grab the global obj lock.
+	 * This might cause issue!
+	 */
+	old_global_state = omap_get_existing_global_state(priv);
+	DBG("old_global_state: %p", old_global_state);
+
+	overlay_map = old_global_state->hwoverlay_to_plane;
+
+	if (WARN_ON(!overlay_map[overlay->idx]))
+		return;
+	/*
+	 * Check that the overlay we are releasing is actually
+	 * assigned to the plane we are trying to release it from.
+	 */
+	if (overlay_map[overlay->idx] == plane) {
+		DBG("%s: release from WB plane %s", overlay->name, plane->name);
+
+		/*
+		 * As this might get called without having done any other
+		 * actual h/w access make sure the module is enabled before
+		 * trying to access it.
+		 */
+		priv->dispc_ops->runtime_get(priv->dispc);
+		priv->dispc_ops->ovl_enable(priv->dispc, overlay->overlay_id,
+					    false);
+		priv->dispc_ops->runtime_put(priv->dispc);
+		overlay->possible_crtcs = (1 << priv->num_pipes) - 1;
+		overlay_map[overlay->idx] = NULL;
+	}
+}
+
 static void omap_overlay_destroy(struct omap_hw_overlay *overlay)
 {
 	kfree(overlay);

+ 8 - 0
drivers/gpu/drm/omapdrm/omap_overlay.h

@@ -38,4 +38,12 @@ void omap_overlay_release(struct drm_atomic_state *s,
 void omap_overlay_disable(struct drm_atomic_state *s,
 			  struct drm_plane *plane,
 			  struct omap_hw_overlay *overlay);
+int omap_overlay_assign_wb(struct omap_drm_private *priv,
+			   struct drm_plane *plane,
+			   u32 caps, u32 fourcc, u32 crtc_mask,
+			   struct omap_hw_overlay **overlay);
+void omap_overlay_release_wb(struct omap_drm_private *priv,
+			     struct drm_plane *plane,
+			     struct omap_hw_overlay *overlay);
+
 #endif /* __OMAPDRM_OVERLAY_H__ */

+ 105 - 1
drivers/gpu/drm/omapdrm/omap_plane.c

@@ -33,6 +33,9 @@ struct omap_plane_state {
 
 	struct omap_hw_overlay *overlay;
 	struct omap_hw_overlay *r_overlay;  /* right overlay */
+
+	unsigned int global_alpha;
+	unsigned int pre_mult_alpha;
 };
 
 #define to_omap_plane(x) container_of(x, struct omap_plane, base)
@@ -41,6 +44,12 @@ struct omap_plane {
 	struct drm_plane base;
 	enum omap_plane_id id;
 	const char *name;
+
+	/*
+	 * WB has no notion of atomic state we need to keep
+	 * a reference to the allocated overlay here.
+	 */
+	struct omap_hw_overlay *reserved_wb_overlay;
 };
 
 bool is_omap_plane_dual_overlay(struct drm_plane_state *state)
@@ -103,7 +112,8 @@ static void omap_plane_atomic_update(struct drm_plane *plane,
 	memset(&info, 0, sizeof(info));
 	info.rotation_type = OMAP_DSS_ROT_NONE;
 	info.rotation = DRM_MODE_ROTATE_0;
-	info.global_alpha = 0xff;
+	info.global_alpha = new_omap_state->global_alpha;
+	info.pre_mult_alpha = new_omap_state->pre_mult_alpha;
 	info.zorder = state->normalized_zpos;
 	info.color_encoding = state->color_encoding;
 	info.color_range = state->color_range;
@@ -197,6 +207,7 @@ static int omap_plane_atomic_check(struct drm_plane *plane,
 				   struct drm_plane_state *state)
 {
 	struct omap_drm_private *priv = plane->dev->dev_private;
+	struct omap_plane *omap_plane = to_omap_plane(plane);
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *crtc_state;
 	u16 width, height;
@@ -213,6 +224,9 @@ static int omap_plane_atomic_check(struct drm_plane *plane,
 	int min_scale, max_scale;
 	int ret;
 
+	if (omap_plane->reserved_wb_overlay)
+		return -EBUSY;
+
 	omap_overlay_global_state = omap_get_global_state(state->state);
 	if (IS_ERR(omap_overlay_global_state))
 		return PTR_ERR(omap_overlay_global_state);
@@ -240,6 +254,13 @@ static int omap_plane_atomic_check(struct drm_plane *plane,
 		is_fourcc_yuv = state->fb->format->is_yuv;
 
 	if (state->src_w > width_fp || state->crtc_w > width) {
+		/*
+		 * We cannot have dual plane/overlay and trans_key_mode
+		 * enabled concurrently, hence rejecting this configuration
+		 */
+		if (omap_crtc_atomic_get_trans_key_mode(crtc, crtc_state))
+			return -EINVAL;
+
 		if (is_fourcc_yuv &&
 		    (((state->src_w >> 16) / 2 & 1) ||
 		     state->crtc_w / 2 & 1)) {
@@ -433,6 +454,9 @@ static void omap_plane_reset(struct drm_plane *plane)
 			   ? 0 : omap_plane->id;
 	plane->state->color_encoding = DRM_COLOR_YCBCR_BT601;
 	plane->state->color_range = DRM_COLOR_YCBCR_FULL_RANGE;
+
+	omap_state->global_alpha = 0xff;
+	omap_state->pre_mult_alpha = 0;
 }
 
 static struct drm_plane_state *
@@ -451,6 +475,9 @@ omap_plane_atomic_duplicate_state(struct drm_plane *plane)
 
 	__drm_atomic_helper_plane_duplicate_state(plane, &copy->base);
 
+	copy->global_alpha = state->global_alpha;
+	copy->pre_mult_alpha = state->pre_mult_alpha;
+
 	return &copy->base;
 }
 
@@ -489,9 +516,14 @@ static int omap_plane_atomic_set_property(struct drm_plane *plane,
 					  u64 val)
 {
 	struct omap_drm_private *priv = plane->dev->dev_private;
+	struct omap_plane_state *omap_state = to_omap_plane_state(state);
 
 	if (property == priv->zorder_prop)
 		state->zpos = val;
+	else if (property == priv->global_alpha_prop)
+		omap_state->global_alpha = val;
+	else if (property == priv->pre_mult_alpha_prop)
+		omap_state->pre_mult_alpha = val;
 	else
 		return -EINVAL;
 
@@ -504,9 +536,14 @@ static int omap_plane_atomic_get_property(struct drm_plane *plane,
 					  u64 *val)
 {
 	struct omap_drm_private *priv = plane->dev->dev_private;
+	const struct omap_plane_state *omap_state = to_omap_plane_state(state);
 
 	if (property == priv->zorder_prop)
 		*val = state->zpos;
+	else if (property == priv->global_alpha_prop)
+		*val = omap_state->global_alpha;
+	else if (property == priv->pre_mult_alpha_prop)
+		*val = omap_state->pre_mult_alpha;
 	else
 		return -EINVAL;
 
@@ -603,6 +640,9 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
 					DRM_COLOR_YCBCR_BT601,
 					DRM_COLOR_YCBCR_FULL_RANGE);
 
+	drm_object_attach_property(&plane->base, priv->global_alpha_prop, 0);
+	drm_object_attach_property(&plane->base, priv->pre_mult_alpha_prop, 0);
+
 	return plane;
 
 error:
@@ -612,3 +652,67 @@ error:
 	kfree(omap_plane);
 	return NULL;
 }
+
+enum omap_plane_id omap_plane_id_wb(struct drm_plane *plane)
+{
+	struct omap_plane *omap_plane = to_omap_plane(plane);
+
+	return omap_plane->reserved_wb_overlay->overlay_id;
+}
+
+struct drm_plane *omap_plane_reserve_wb(struct drm_device *dev)
+{
+	struct omap_drm_private *priv = dev->dev_private;
+	int i, ret;
+
+	/*
+	 * Look from the last plane to the first to lessen chances of the
+	 * display side trying to use the same plane as writeback.
+	 */
+	for (i = priv->num_planes - 1; i >= 0; --i) {
+		struct drm_plane *plane = priv->planes[i];
+		struct omap_plane *omap_plane = to_omap_plane(plane);
+		struct omap_hw_overlay *new_ovl = NULL;
+		u32 crtc_mask = (1 << priv->num_pipes) - 1;
+		u32 fourcc = DRM_FORMAT_YUYV;
+		u32 caps = OMAP_DSS_OVL_CAP_SCALE;
+
+		if (plane->state->crtc || plane->state->fb)
+			continue;
+
+		if (omap_plane->reserved_wb_overlay)
+			continue;
+
+		ret = omap_overlay_assign_wb(priv, plane, caps, fourcc,
+					     crtc_mask, &new_ovl);
+		if (ret) {
+			DBG("%s: failed to assign hw_overlay for wb!",
+			    plane->name);
+			return NULL;
+		}
+
+		omap_plane->reserved_wb_overlay = new_ovl;
+
+		return plane;
+	}
+
+	return NULL;
+}
+
+void omap_plane_release_wb(struct drm_plane *plane)
+{
+	struct omap_drm_private *priv = plane->dev->dev_private;
+	struct omap_plane *omap_plane;
+
+	/*
+	 * This is also called on module unload at which point plane might
+	 * not be set. In that case just return as there is nothing to do.
+	 */
+	if (!plane)
+		return;
+
+	omap_plane = to_omap_plane(plane);
+
+	omap_overlay_release_wb(priv, plane, omap_plane->reserved_wb_overlay);
+	omap_plane->reserved_wb_overlay = NULL;
+}

+ 4 - 0
drivers/gpu/drm/omapdrm/omap_plane.h

@@ -35,4 +35,8 @@ void omap_plane_install_properties(struct drm_plane *plane,
 		struct drm_mode_object *obj);
 bool is_omap_plane_dual_overlay(struct drm_plane_state *state);
 
+enum omap_plane_id omap_plane_id_wb(struct drm_plane *plane);
+struct drm_plane *omap_plane_reserve_wb(struct drm_device *dev);
+void omap_plane_release_wb(struct drm_plane *plane);
+
 #endif /* __OMAPDRM_PLANE_H__ */

+ 179 - 0
drivers/gpu/drm/omapdrm/omap_wb.c

@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2016-2018 Texas Instruments Incorporated -  http://www.ti.com/
+ * Author: Benoit Parrot <bparrot@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "omap_wb.h"
+
+unsigned int wbdebug;
+module_param(wbdebug, uint, 0644);
+MODULE_PARM_DESC(wbdebug, "activates debug info");
+
+struct wb_fmt wb_formats[] = {
+	{
+		.fourcc		= V4L2_PIX_FMT_NV12,
+		.coplanar	= 0,
+		.depth		= {8, 4},
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_NV12M,
+		.coplanar	= 1,
+		.depth		= {8, 4},
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_YUYV,
+		.coplanar	= 0,
+		.depth		= {16, 0},
+	},
+	{
+		.fourcc		= V4L2_PIX_FMT_UYVY,
+		.coplanar	= 0,
+		.depth		= {16, 0},
+	},
+	{
+		/* "XR24", DRM_FORMAT_XRGB8888 */
+		.fourcc		= V4L2_PIX_FMT_XBGR32,
+		.coplanar	= 0,
+		.depth		= {32, 0},
+	},
+};
+
+unsigned int num_wb_formats = ARRAY_SIZE(wb_formats);
+
+/* find our format description corresponding to the passed v4l2_format */
+struct wb_fmt *find_format(struct v4l2_format *f)
+{
+	struct wb_fmt *fmt;
+	unsigned int k;
+
+	for (k = 0; k < num_wb_formats; k++) {
+		fmt = &wb_formats[k];
+		if (fmt->fourcc == f->fmt.pix_mp.pixelformat)
+			return fmt;
+	}
+
+	return NULL;
+}
+
+int omap_wb_fourcc_v4l2_to_drm(u32 fourcc)
+{
+	switch (fourcc) {
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV12M:
+		return DRM_FORMAT_NV12;
+	case V4L2_PIX_FMT_YUYV:
+		return DRM_FORMAT_YUYV;
+	case V4L2_PIX_FMT_UYVY:
+		return DRM_FORMAT_UYVY;
+	case V4L2_PIX_FMT_XBGR32:
+		return DRM_FORMAT_XRGB8888;
+	default:
+		WARN(1, "WB: unsupported fourcc\n");
+		return 0;
+	}
+}
+
+void omap_wb_irq(void *priv, u32 irqstatus)
+{
+	struct wb_dev *dev = (struct wb_dev *)priv;
+	const u32 mask = OMAP_WB_IRQ_MASK |
+			 DISPC_IRQ_VSYNC |
+			 DISPC_IRQ_VSYNC2 |
+			 DISPC_IRQ_VSYNC3 |
+			 DISPC_IRQ_EVSYNC_EVEN |
+			 DISPC_IRQ_EVSYNC_ODD;
+
+	if (!dev)
+		return;
+
+	irqstatus &= mask;
+	if (!irqstatus)
+		return;
+
+	if (!atomic_read(&dev->irq_enabled))
+		return;
+
+	switch (dev->mode) {
+	case OMAP_WB_NOT_CONFIGURED:
+		break;
+	case OMAP_WB_MEM2MEM_OVL:
+		wbm2m_irq(dev->m2m, irqstatus);
+		break;
+	case OMAP_WB_MEM2MEM_MGR:
+		/* To be added */
+		break;
+	case OMAP_WB_CAPTURE_MGR:
+		wbcap_irq(dev->cap, irqstatus);
+		break;
+	default:
+		WARN_ONCE(1, "WB: unknown WB mode: 0x%x\n", dev->mode);
+		break;
+	}
+}
+
+/*
+ * The initial setup of this device instance. Note that the initial state of
+ * the driver should be complete. So the initial format, standard, timings
+ * and video input should all be initialized to some reasonable value.
+ */
+int omap_wb_init(struct drm_device *drmdev)
+{
+	struct omap_drm_private *priv = drmdev->dev_private;
+	struct wb_dev *dev;
+	int ret = 0;
+
+	/* Allocate a new instance */
+	dev = devm_kzalloc(drmdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	dev->drm_dev = drmdev;
+
+	/* set pseudo v4l2 device name so we can use v4l2_printk */
+	strlcpy(dev->v4l2_dev.name, WB_MODULE_NAME,
+		sizeof(dev->v4l2_dev.name));
+
+	priv->wb_private = dev;
+
+	mutex_init(&dev->lock);
+
+	atomic_set(&dev->irq_enabled, 0);
+
+	dev->mode = OMAP_WB_NOT_CONFIGURED;
+
+	ret = wbcap_init(dev);
+	if (ret) {
+		log_err(dev, "Failed to initialize wb capture\n");
+		goto error;
+	}
+
+	ret = wbm2m_init(dev);
+	if (ret) {
+		log_err(dev, "Failed to initialize wb m2m\n");
+		goto free_cap;
+	}
+
+	log_dbg(dev, "WB loaded\n");
+	return 0;
+
+free_cap:
+	wbcap_cleanup(dev);
+error:
+	return ret;
+}
+
+void omap_wb_cleanup(struct drm_device *drmdev)
+{
+	struct omap_drm_private *priv = drmdev->dev_private;
+	struct wb_dev *dev = priv->wb_private;
+
+	log_dbg(dev, "Cleanup WB\n");
+
+	wbcap_cleanup(dev);
+	wbm2m_cleanup(dev);
+}
+

+ 214 - 0
drivers/gpu/drm/omapdrm/omap_wb.h

@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2016-2018 Texas Instruments Incorporated -  http://www.ti.com/
+ * Author: Benoit Parrot <bparrot@ti.com>
+ */
+
+#ifndef __OMAP_WB_H__
+#define __OMAP_WB_H__
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/hrtimer.h>
+#include <drm/drm_fourcc.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "dss/omapdss.h"
+#include "omap_drv.h"
+
+#define WB_MODULE_NAME "omapwb"
+#define WBM2M_MODULE_NAME "omapwb-m2m"
+#define WBCAP_MODULE_NAME "omapwb-cap"
+
+extern unsigned int wbdebug;
+
+#define log_dbg(dev, fmt, arg...)	\
+		v4l2_dbg(1, wbdebug, &dev->v4l2_dev, "%s: " fmt, \
+			 __func__, ## arg)
+#define log_err(dev, fmt, arg...)	\
+		v4l2_err(&dev->v4l2_dev, fmt, ## arg)
+#define log_info(dev, fmt, arg...)	\
+		v4l2_info(&dev->v4l2_dev, fmt, ## arg)
+
+/* minimum and maximum frame sizes */
+#define MIN_W		32
+#define MIN_H		32
+#define MAX_W		2048
+#define MAX_H		2048
+
+/* required alignments */
+#define S_ALIGN		0	/* multiple of 1 */
+#define H_ALIGN		0	/* multiple of 2 */
+
+/* used as plane indices */
+#define MAX_PLANES	2
+#define LUMA_PLANE	0
+#define CHROMA_PLANE	1
+
+enum omap_wb_mode {
+	OMAP_WB_NOT_CONFIGURED = 0,
+	/* mem2mem from single ovl to wb */
+	OMAP_WB_MEM2MEM_OVL = 1,
+	/* mem2mem from N overlays via single mgr to wb */
+	OMAP_WB_MEM2MEM_MGR = 2,
+	/* capture from single mgr to wb */
+	OMAP_WB_CAPTURE_MGR = 3
+};
+
+enum wb_state {
+	WB_STATE_NONE = 0,
+	WB_STATE_FIRST_FRAME,
+	WB_STATE_CAPTURING,
+	WB_STATE_STOPPING,
+	WB_STATE_STOPPED,
+};
+
+/* driver info for each of the supported video formats */
+struct wb_fmt {
+	u32	fourcc;			/* standard format identifier */
+	u8	coplanar;		/* set for unpacked Luma and Chroma */
+	u8	depth[MAX_PLANES];	/* Bits per pixel per plane*/
+};
+
+extern struct wb_fmt wb_formats[];
+extern unsigned int num_wb_formats;
+
+struct wb_buffer {
+	struct vb2_v4l2_buffer	vb;
+	struct list_head	list;
+};
+
+/*
+ * per-queue, driver-specific private data.
+ * MEM-2-MEM: Source: V4L2_BUF_TYPE_VIDEO_OUTPUT*
+ *            Destination: V4L2_BUF_TYPE_VIDEO_CAPTURE*
+ * CAPTURE:   Destination: V4L2_BUF_TYPE_VIDEO_CAPTURE* only
+ */
+struct wb_q_data {
+	/* format info */
+	struct v4l2_format	format;
+	/* crop/compose rectangle */
+	struct v4l2_rect	c_rect;
+	/* format info */
+	struct wb_fmt		*fmt;
+};
+
+enum {
+	Q_DATA_SRC = 0,
+	Q_DATA_DST = 1,
+};
+
+/* find our format description corresponding to the passed v4l2_format */
+struct wb_fmt *find_format(struct v4l2_format *f);
+
+struct wb_dev {
+	struct v4l2_device	v4l2_dev;
+	struct drm_device	*drm_dev;
+
+	atomic_t		irq_enabled;
+
+	/* v4l2_ioctl mutex */
+	struct mutex		lock;
+
+	enum omap_wb_mode	mode;
+	struct wbcap_dev	*cap;
+	struct wbm2m_dev	*m2m;
+};
+
+/*
+ * there is one wbcap_dev structure in the driver.
+ */
+struct wbcap_dev {
+	struct v4l2_device	v4l2_dev;
+	struct video_device	vdev;
+	struct v4l2_fh		fh;
+	struct wb_dev		*dev;
+	struct v4l2_ctrl_handler hdl;
+
+	/* dst queue data */
+	struct wb_q_data	q_data[2];
+
+	unsigned int		input;
+
+	struct vb2_queue	queue;
+	struct vb2_alloc_ctx	*alloc_ctx;
+
+	spinlock_t		qlock;
+	struct list_head	buf_list;
+
+	/* Current  v4l2_buffer */
+	struct wb_buffer	*cur_frm;
+	/* Next v4l2_buffer */
+	struct wb_buffer	*next_frm;
+
+	unsigned int		field;
+	unsigned int		sequence;
+
+	bool			stopping;
+	wait_queue_head_t	event;
+
+	enum wb_state state;
+
+	/* timer used to wait for wb go bit to be cleared */
+	struct hrtimer		wbgo_timer;
+};
+
+/*
+ * there is one wbm2m_dev structure in the driver.
+ */
+struct wbm2m_dev {
+	struct v4l2_device	v4l2_dev;
+	struct video_device	vfd;
+	struct v4l2_m2m_dev	*m2m_dev;
+	struct wb_dev		*dev;
+	struct drm_plane	*plane;
+
+	/* v4l2 buffers lock */
+	spinlock_t		lock;
+
+	struct vb2_alloc_ctx	*alloc_ctx;
+};
+
+/*
+ * There is one wbm2m_ctx structure for each m2m context.
+ */
+struct wbm2m_ctx {
+	struct v4l2_fh		fh;
+	struct wbm2m_dev	*dev;
+	struct v4l2_ctrl_handler hdl;
+
+	/* current frame seq */
+	unsigned int		sequence;
+	/* abort after next irq */
+	unsigned int		aborting;
+
+	/* src & dst queue data */
+	struct wb_q_data	q_data[2];
+};
+
+static inline struct wb_buffer *to_wb_buffer(struct vb2_buffer *vb2)
+{
+	return container_of(vb2, struct wb_buffer, vb.vb2_buf);
+}
+
+int omap_wb_fourcc_v4l2_to_drm(u32 fourcc);
+
+void wbm2m_irq(struct wbm2m_dev *dev, uint32_t irqstatus);
+int wbm2m_init(struct wb_dev *dev);
+void wbm2m_cleanup(struct wb_dev *dev);
+
+void wbcap_irq(struct wbcap_dev *dev, u32 irqstatus);
+int wbcap_init(struct wb_dev *dev);
+void wbcap_cleanup(struct wb_dev *dev);
+
+#endif /* __OMAP_WB_H__ */

+ 1044 - 0
drivers/gpu/drm/omapdrm/omap_wb_cap.c

@@ -0,0 +1,1044 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2016-2018 Texas Instruments Incorporated -  http://www.ti.com/
+ * Author: Benoit Parrot <bparrot@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+
+#include "omap_wb.h"
+
+static int omap_channel_to_wb_channel(int oc)
+{
+	switch (oc) {
+	case OMAP_DSS_CHANNEL_LCD:
+		return DSS_WB_LCD1_MGR;
+	case OMAP_DSS_CHANNEL_DIGIT:
+		return DSS_WB_TV_MGR;
+	case OMAP_DSS_CHANNEL_LCD2:
+		return DSS_WB_LCD2_MGR;
+	case OMAP_DSS_CHANNEL_LCD3:
+		return DSS_WB_LCD3_MGR;
+	default:
+		return DSS_WB_LCD1_MGR;
+	}
+}
+
+static char *omap_channel_to_name(int oc)
+{
+	switch (oc) {
+	case OMAP_DSS_CHANNEL_LCD:
+		return "LCD1";
+	case OMAP_DSS_CHANNEL_DIGIT:
+		return "DIGIT/TV";
+	case OMAP_DSS_CHANNEL_LCD2:
+		return "LCD2";
+	case OMAP_DSS_CHANNEL_LCD3:
+		return "LCD3";
+	default:
+		return "LCD1";
+	}
+}
+
+/* driver info for each of the supported input overlay/mgr */
+struct wb_input {
+	char name[64];
+	u32 wb_channel;
+	u32 omap_channel;
+	u32 crtc_index;
+};
+
+static struct wb_input wb_inputs[8];
+static int num_wb_input;
+
+static bool is_input_active(struct wbcap_dev *wbcap)
+{
+	struct omap_drm_private *priv = wbcap->dev->drm_dev->dev_private;
+	u32 oc = wb_inputs[wbcap->input].omap_channel;
+
+	return priv->dispc_ops->mgr_is_enabled(priv->dispc, oc);
+}
+
+static bool is_input_enabled(struct wbcap_dev *wbcap)
+{
+	struct omap_drm_private *priv = wbcap->dev->drm_dev->dev_private;
+	struct drm_crtc *crtc;
+	struct wb_input *input;
+
+	input = &wb_inputs[wbcap->input];
+	crtc = priv->pipes[input->crtc_index].crtc;
+
+	return crtc->enabled;
+}
+
+static void build_input_table(struct wbcap_dev *wbcap)
+{
+	struct omap_drm_private *priv = wbcap->dev->drm_dev->dev_private;
+	struct drm_crtc *crtc;
+	struct wb_input *input;
+	int i;
+
+	for (i = 0; i < priv->num_pipes; i++) {
+		crtc = priv->pipes[i].crtc;
+		input = &wb_inputs[i];
+
+		input->crtc_index = i;
+		input->omap_channel = omap_crtc_channel(crtc);
+		input->wb_channel =
+			omap_channel_to_wb_channel(input->omap_channel);
+		snprintf(input->name, sizeof(input->name), "CRTC#%d - %s",
+			 i, omap_channel_to_name(input->omap_channel));
+
+		log_dbg(wbcap, "Input# %d, name:'%s' omap_channel:%d wb_channel:%d\n",
+			i, input->name, input->omap_channel, input->wb_channel);
+	}
+	num_wb_input = i;
+}
+
+static struct wb_q_data *get_q_data(struct wbcap_dev *dev,
+				    enum v4l2_buf_type type)
+{
+	switch (type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		return &dev->q_data[Q_DATA_DST];
+	default:
+		return NULL;
+	}
+	return NULL;
+}
+
+static bool wb_cap_setup(struct wbcap_dev *dev,
+			 enum dss_writeback_channel wb_channel,
+			 const struct omap_dss_writeback_info *wb_info)
+{
+	struct omap_drm_private *priv = dev->dev->drm_dev->dev_private;
+	struct drm_crtc *crtc;
+	struct videomode *ct;
+	int r;
+
+	crtc = priv->pipes[wb_inputs[dev->input].crtc_index].crtc;
+	ct = omap_crtc_timings(crtc);
+
+	/* configure wb */
+	r = priv->dispc_ops->wb_setup(priv->dispc, wb_info, false, ct, wb_channel);
+	if (r)
+		return false;
+
+	if (is_input_active(dev)) {
+		priv->dispc_ops->ovl_enable(priv->dispc, OMAP_DSS_WB, true);
+		priv->dispc_ops->wb_go(priv->dispc);
+	} else {
+		log_err(dev, "CHANNEL %u not enabled, skip WB GO\n",
+			wb_inputs[dev->input].omap_channel);
+	}
+
+	return true;
+}
+
+static bool is_input_irq_vsync_set(struct wbcap_dev *dev, u32 irqstatus)
+{
+	struct omap_drm_private *priv = dev->dev->drm_dev->dev_private;
+	u32 oc = wb_inputs[dev->input].omap_channel;
+
+	if (irqstatus & priv->dispc_ops->mgr_get_vsync_irq(priv->dispc, oc))
+		return true;
+	return false;
+}
+
+static int wbcap_schedule_next_buffer(struct wbcap_dev *dev)
+{
+	struct wb_buffer *buf;
+	unsigned long addr_y = 0;
+	unsigned long addr_uv = 0;
+	struct wb_q_data *q_data;
+	int num_planes;
+	bool ok;
+	struct omap_dss_writeback_info wb_info = { 0 };
+	struct v4l2_pix_format_mplane *pix;
+	unsigned long flags;
+
+	if (!is_input_active(dev)) {
+		dev->next_frm = NULL;
+		return 0;
+	}
+
+	spin_lock_irqsave(&dev->qlock, flags);
+	if (list_empty(&dev->buf_list)) {
+		dev->next_frm = NULL;
+		spin_unlock_irqrestore(&dev->qlock, flags);
+		return 0;
+	}
+
+	buf = list_entry(dev->buf_list.next, struct wb_buffer, list);
+	dev->next_frm = buf;
+	list_del(&buf->list);
+	spin_unlock_irqrestore(&dev->qlock, flags);
+
+	q_data = get_q_data(dev, buf->vb.vb2_buf.type);
+	if (!q_data)
+		return -EINVAL;
+
+	pix = &q_data->format.fmt.pix_mp;
+	num_planes = pix->num_planes;
+
+	addr_y = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+	if (num_planes == 2)
+		addr_uv = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 1);
+	else if (pix->pixelformat == V4L2_PIX_FMT_NV12)
+		addr_uv = addr_y + (pix->plane_fmt[0].bytesperline *
+				    pix->height);
+
+	/* fill WB DSS info */
+	wb_info.paddr = (u32)addr_y;
+	wb_info.p_uv_addr = (u32)addr_uv;
+	wb_info.buf_width = pix->plane_fmt[0].bytesperline /
+			    (q_data->fmt->depth[LUMA_PLANE] / 8);
+
+	wb_info.width = pix->width;
+	wb_info.height = pix->height;
+	wb_info.fourcc = omap_wb_fourcc_v4l2_to_drm(pix->pixelformat);
+	wb_info.pre_mult_alpha = 1;
+
+	wb_info.rotation = DRM_MODE_ROTATE_0;
+	wb_info.rotation_type = OMAP_DSS_ROT_NONE;
+
+	ok = wb_cap_setup(dev,
+			  wb_inputs[dev->input].wb_channel,
+			  &wb_info);
+	if (!ok)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void wbcap_process_buffer_complete(struct wbcap_dev *dev)
+{
+	dev->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns();
+	dev->cur_frm->vb.field = dev->field;
+	dev->cur_frm->vb.sequence = dev->sequence++;
+
+	vb2_buffer_done(&dev->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
+	dev->cur_frm = dev->next_frm;
+}
+
+static enum hrtimer_restart wbcap_wbgo_timer(struct hrtimer *timer)
+{
+	struct wbcap_dev *dev = container_of(timer,
+					     struct wbcap_dev, wbgo_timer);
+	struct omap_drm_private *priv = dev->dev->drm_dev->dev_private;
+
+	if (priv->dispc_ops->wb_go_busy(priv->dispc))
+		log_err(dev, "WARNING, WB BUSY at hrtimer, state %u\n",
+			dev->state);
+
+	switch (dev->state) {
+	case WB_STATE_NONE:
+		break;
+
+	case WB_STATE_FIRST_FRAME:
+		dev->cur_frm = dev->next_frm;
+		wbcap_schedule_next_buffer(dev);
+		dev->state = WB_STATE_CAPTURING;
+		break;
+
+	case WB_STATE_CAPTURING:
+		if (dev->cur_frm && dev->next_frm) {
+			/*
+			 * We have cur_frm that was just captured, and next_frm
+			 * to which the HW will start capturing.
+			 * This means cur_frm is now released from DSS HW.
+			 */
+			wbcap_process_buffer_complete(dev);
+			dev->next_frm = NULL;
+		} else {
+			/*
+			 * We have cur_frm which has a captured frame,
+			 * but we don't have next_frm.
+			 * This means cur_frm is will still be used by
+			 * DSS for capture
+			 */
+		}
+
+		if (dev->stopping) {
+			/* XXX should we set WB GO? */
+			priv->dispc_ops->ovl_enable(priv->dispc, OMAP_DSS_WB,
+						    false);
+			dev->state = WB_STATE_STOPPING;
+		} else {
+			wbcap_schedule_next_buffer(dev);
+		}
+		break;
+
+	case WB_STATE_STOPPING:
+		if (dev->cur_frm)
+			wbcap_process_buffer_complete(dev);
+
+		dev->state = WB_STATE_STOPPED;
+		atomic_dec(&dev->dev->irq_enabled);
+		dev->stopping = false;
+		wake_up(&dev->event);
+		break;
+
+	case WB_STATE_STOPPED:
+		log_err(dev, "ERROR: timer triggered in the stopped state. This shouldn't happen\n");
+		break;
+	}
+
+	return HRTIMER_NORESTART;
+}
+
+static void wbcap_handle_vsync(struct wbcap_dev *dev)
+{
+	/*
+	 * In writeback capture mode, the GO bit doesn't get reset
+	 * at the manager's VSYNC interrupt. It takes an extra
+	 * 'WBDELAYCOUNTER' time after VSYNC when the writeback
+	 * FIFOs are flushed and the shadow registers are taken in.
+	 * There isn't any DSS interrupt to notify this point in time.
+	 * The correct solution is to set a timer far enough that it
+	 * should cover the period defined by WBDELAYCOUNTER.
+	 * The max value allowed in WBDELAYCOUNTER is 255 which
+	 * correspond to 255 lines. So waiting anywhere from 1/4 to
+	 * 1/2 a frame (i.e. 2ms at 60  to 120 fps) should be safe
+	 * enough.
+	 */
+
+	hrtimer_start_range_ns(&dev->wbgo_timer, ms_to_ktime(3), 1000000,
+			       HRTIMER_MODE_REL);
+}
+
+void wbcap_irq(struct wbcap_dev *dev, u32 irqstatus)
+{
+	if (irqstatus & DISPC_IRQ_FRAMEDONEWB)
+		log_dbg(dev, "WB: FRAMEDONE\n");
+
+	if (irqstatus & DISPC_IRQ_WBBUFFEROVERFLOW)
+		log_err(dev, "WB: UNDERFLOW\n");
+
+	if (irqstatus & DISPC_IRQ_WBUNCOMPLETEERROR)
+		log_err(dev, "WB: WBUNCOMPLETEERROR\n");
+
+	if (is_input_irq_vsync_set(dev, irqstatus)) {
+		if (dev->field != V4L2_FIELD_NONE) {
+			if (irqstatus & DISPC_IRQ_EVSYNC_EVEN)
+				dev->field = V4L2_FIELD_BOTTOM;
+			else if (irqstatus & DISPC_IRQ_EVSYNC_ODD)
+				dev->field = V4L2_FIELD_TOP;
+		}
+		wbcap_handle_vsync(dev);
+	}
+}
+
+/*
+ * Setup the constraints of the queue: besides setting the number of planes
+ * per buffer and the size and allocation context of each plane, it also
+ * checks if sufficient buffers have been allocated. Usually 3 is a good
+ * minimum number: many DMA engines need a minimum of 2 buffers in the
+ * queue and you need to have another available for userspace processing.
+ */
+static int queue_setup(struct vb2_queue *vq,
+		       unsigned int *nbuffers, unsigned int *nplanes,
+		       unsigned int sizes[], struct device *alloc_devs[])
+{
+	int i;
+	struct wbcap_dev *wbcap = vb2_get_drv_priv(vq);
+	struct wb_q_data *q_data;
+
+	q_data = get_q_data(wbcap, vq->type);
+
+	if (!q_data)
+		return -EINVAL;
+
+	if (vq->num_buffers + *nbuffers < 2)
+		*nbuffers = 2 - vq->num_buffers;
+
+	*nplanes = q_data->format.fmt.pix_mp.num_planes;
+
+	for (i = 0; i < *nplanes; i++)
+		sizes[i] = q_data->format.fmt.pix_mp.plane_fmt[i].sizeimage;
+
+	log_dbg(wbcap, "get %d buffer(s) of size %d\n", *nbuffers,
+		sizes[LUMA_PLANE]);
+	if (*nplanes == 2)
+		log_dbg(wbcap, " and %d\n", sizes[CHROMA_PLANE]);
+
+	return 0;
+}
+
+/*
+ * Prepare the buffer for queueing to the DMA engine: check and set the
+ * payload size.
+ */
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+	struct wbcap_dev *wbcap = vb2_get_drv_priv(vb->vb2_queue);
+	struct wb_q_data *q_data;
+	struct v4l2_pix_format_mplane *mp;
+	int i, num_planes;
+
+	q_data = get_q_data(wbcap, vb->vb2_queue->type);
+	if (!q_data)
+		return -EINVAL;
+	num_planes = q_data->format.fmt.pix_mp.num_planes;
+
+	for (i = 0; i < num_planes; i++) {
+		mp = &q_data->format.fmt.pix_mp;
+		if (vb2_plane_size(vb, i) < mp->plane_fmt[i].sizeimage) {
+			log_err(wbcap,
+				"data will not fit into plane (%lu < %lu)\n",
+				vb2_plane_size(vb, i),
+				(long)mp->plane_fmt[i].sizeimage);
+			return -EINVAL;
+		}
+		vb2_set_plane_payload(vb, i, mp->plane_fmt[i].sizeimage);
+	}
+
+	return 0;
+}
+
+/*
+ * Queue this buffer to the DMA engine.
+ */
+static void buffer_queue(struct vb2_buffer *vb)
+{
+	struct wbcap_dev *wbcap = vb2_get_drv_priv(vb->vb2_queue);
+	struct wb_buffer *buf = to_wb_buffer(vb);
+	unsigned long flags;
+
+	spin_lock_irqsave(&wbcap->qlock, flags);
+	list_add_tail(&buf->list, &wbcap->buf_list);
+
+	spin_unlock_irqrestore(&wbcap->qlock, flags);
+}
+
+static void return_all_buffers(struct wbcap_dev *wbcap,
+			       enum vb2_buffer_state state)
+{
+	struct wb_buffer *buf, *node;
+	unsigned long flags;
+
+	spin_lock_irqsave(&wbcap->qlock, flags);
+	list_for_each_entry_safe(buf, node, &wbcap->buf_list, list) {
+		vb2_buffer_done(&buf->vb.vb2_buf, state);
+		list_del(&buf->list);
+	}
+
+	if (wbcap->cur_frm) {
+		vb2_buffer_done(&wbcap->cur_frm->vb.vb2_buf, state);
+		wbcap->cur_frm = NULL;
+	}
+
+	if (wbcap->next_frm) {
+		vb2_buffer_done(&wbcap->next_frm->vb.vb2_buf, state);
+		wbcap->next_frm = NULL;
+	}
+
+	spin_unlock_irqrestore(&wbcap->qlock, flags);
+}
+
+/*
+ * Start streaming. First check if the minimum number of buffers have been
+ * queued. If not, then return -ENOBUFS and the vb2 framework will call
+ * this function again the next time a buffer has been queued until enough
+ * buffers are available to actually start the DMA engine.
+ */
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct wbcap_dev *wbcap = vb2_get_drv_priv(vq);
+	struct omap_drm_private *priv = wbcap->dev->drm_dev->dev_private;
+	struct drm_crtc *crtc;
+	int ret;
+	struct wb_q_data *q_data;
+
+	priv->dispc_ops->runtime_get(priv->dispc);
+
+	wbcap->sequence = 0;
+	q_data = get_q_data(wbcap, wbcap->queue.type);
+	if (!q_data) {
+		log_err(wbcap, "ERROR: getting q_data failed\n");
+		return_all_buffers(wbcap, VB2_BUF_STATE_QUEUED);
+		priv->dispc_ops->runtime_put(priv->dispc);
+		return -EINVAL;
+	}
+
+	if (q_data->format.fmt.pix_mp.field == V4L2_FIELD_ALTERNATE)
+		wbcap->field = V4L2_FIELD_TOP;
+	else
+		wbcap->field = V4L2_FIELD_NONE;
+
+	log_dbg(wbcap, "Input (%s) is %s : %s\n",
+		wb_inputs[wbcap->input].name,
+		is_input_enabled(wbcap) ? "enabled" : "disabled",
+		is_input_active(wbcap) ? "active" : "inactive");
+
+	if (!is_input_active(wbcap)) {
+		log_err(wbcap, "ERROR: Selected input (%s) is not active, bailing out\n",
+			wb_inputs[wbcap->input].name);
+		return_all_buffers(wbcap, VB2_BUF_STATE_QUEUED);
+		priv->dispc_ops->runtime_put(priv->dispc);
+		return -EINVAL;
+	}
+
+	/* Enable vsync irq on the input crtc */
+	crtc = priv->pipes[wb_inputs[wbcap->input].crtc_index].crtc;
+	ret = drm_crtc_vblank_get(crtc);
+	WARN_ON(ret != 0);
+
+	if (wbcap_schedule_next_buffer(wbcap)) {
+		return_all_buffers(wbcap, VB2_BUF_STATE_QUEUED);
+		priv->dispc_ops->runtime_put(priv->dispc);
+		return -EINVAL;
+	}
+
+	wbcap->state = WB_STATE_FIRST_FRAME;
+	atomic_inc(&wbcap->dev->irq_enabled);
+	return 0;
+}
+
+/*
+ * Stop the DMA engine. Any remaining buffers in the DMA queue are dequeued
+ * and passed on to the vb2 framework marked as STATE_ERROR.
+ */
+static void stop_streaming(struct vb2_queue *vq)
+{
+	struct wbcap_dev *wbcap = vb2_get_drv_priv(vq);
+	struct omap_drm_private *priv = wbcap->dev->drm_dev->dev_private;
+	struct drm_crtc *crtc;
+	int ret;
+
+	log_dbg(wbcap, "Stopping WB\n");
+	log_dbg(wbcap, "current state: %d\n", wbcap->state);
+
+	wbcap->stopping = true;
+	ret = wait_event_timeout(wbcap->event,
+				 !wbcap->stopping,
+				 msecs_to_jiffies(250));
+
+	log_dbg(wbcap, "Returning VB2 buffers\n");
+
+	if (priv->dispc_ops->wb_go_busy(priv->dispc))
+		log_err(wbcap, "WARNING, WB BUSY when stopping\n");
+
+	/* Release all active buffers */
+	return_all_buffers(wbcap, VB2_BUF_STATE_ERROR);
+
+	/* Disable vsync irq on the input crtc */
+	crtc = priv->pipes[wb_inputs[wbcap->input].crtc_index].crtc;
+	drm_crtc_vblank_put(crtc);
+
+	priv->dispc_ops->runtime_put(priv->dispc);
+}
+
+/*
+ * The vb2 queue ops. Note that since q->lock is set we can use the standard
+ * vb2_ops_wait_prepare/finish helper functions. If q->lock would be NULL,
+ * then this driver would have to provide these ops.
+ */
+static struct vb2_ops wbcap_qops = {
+	.queue_setup		= queue_setup,
+	.buf_prepare		= buffer_prepare,
+	.buf_queue		= buffer_queue,
+	.start_streaming	= start_streaming,
+	.stop_streaming		= stop_streaming,
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+/*
+ * Required ioctl querycap. Note that the version field is prefilled with
+ * the version of the kernel.
+ */
+static int wbcap_querycap(struct file *file, void *priv,
+			  struct v4l2_capability *cap)
+{
+	struct wbcap_dev *wbcap = video_drvdata(file);
+
+	strlcpy(cap->driver, WBCAP_MODULE_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, WBCAP_MODULE_NAME, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+		 wbcap->v4l2_dev.name);
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_READWRITE |
+			   V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+	return 0;
+}
+
+/*
+ * Helper function to check and correct struct v4l2_pix_format. It's used
+ * not only in VIDIOC_TRY/S_FMT, but also elsewhere if changes to the SDTV
+ * standard, HDTV timings or the video input would require updating the
+ * current format.
+ */
+static int wbcap_fill_pix_format(struct wbcap_dev *wbcap,
+				 struct v4l2_format *f)
+{
+	struct wb_fmt *fmt = find_format(f);
+	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+	struct v4l2_plane_pix_format *plane_fmt;
+	unsigned int w_align;
+	int i, depth, depth_bytes;
+
+	if (!fmt) {
+		log_dbg(wbcap, "Fourcc format (0x%08x) invalid.\n",
+			pix->pixelformat);
+		fmt = &wb_formats[1];
+	}
+
+	/* we only allow V4L2_FIELD_NONE or V4L2_FIELD_ALTERNATE */
+	if (pix->field != V4L2_FIELD_NONE &&
+	    pix->field != V4L2_FIELD_ALTERNATE)
+		pix->field = V4L2_FIELD_NONE;
+
+	depth = fmt->depth[LUMA_PLANE];
+
+	/*
+	 * The line stride needs to be even is even.
+	 * Special case is with YUV422 interleaved format an even number
+	 * of pixels is required also.
+	 */
+	depth_bytes = depth >> 3;
+
+	w_align = 0;
+	if ((depth_bytes == 3) || (depth_bytes == 1))
+		w_align = 1;
+	else if ((depth_bytes == 2) &&
+		 (fmt->fourcc == V4L2_PIX_FMT_YUYV ||
+		  fmt->fourcc == V4L2_PIX_FMT_UYVY))
+		w_align = 1;
+
+	v4l_bound_align_image(&pix->width, MIN_W, MAX_W, w_align,
+			      &pix->height, MIN_H, MAX_H, H_ALIGN,
+			      S_ALIGN);
+	pix->num_planes = fmt->coplanar ? 2 : 1;
+	pix->pixelformat = fmt->fourcc;
+
+	pix->colorspace = V4L2_COLORSPACE_SRGB;
+	pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+	pix->quantization = V4L2_QUANTIZATION_DEFAULT;
+	pix->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+
+	memset(pix->reserved, 0, sizeof(pix->reserved));
+	for (i = 0; i < pix->num_planes; i++) {
+		plane_fmt = &pix->plane_fmt[i];
+		depth = fmt->depth[i];
+
+		if (i == LUMA_PLANE)
+			plane_fmt->bytesperline = pix->width * depth / 8;
+		else
+			plane_fmt->bytesperline = pix->width;
+
+		plane_fmt->sizeimage = (pix->height * pix->width *
+					depth) / 8;
+
+		if (fmt->fourcc == V4L2_PIX_FMT_NV12) {
+			/*
+			 * Since we are using a single plane buffer
+			 * we need to adjust the reported sizeimage
+			 * to include the colocated UV part.
+			 */
+			plane_fmt->sizeimage += (pix->height / 2 *
+				plane_fmt->bytesperline);
+		}
+
+		memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved));
+	}
+
+	return 0;
+}
+
+static int wbcap_try_fmt_vid_cap(struct file *file, void *priv,
+				 struct v4l2_format *f)
+{
+	struct wbcap_dev *wbcap = video_drvdata(file);
+	struct omap_drm_private *drmpriv = wbcap->dev->drm_dev->dev_private;
+	struct drm_crtc *crtc;
+	struct videomode *ct;
+
+	log_dbg(wbcap, "requested fourcc:%4.4s size: %dx%d\n",
+		(char *)&f->fmt.pix_mp.pixelformat,
+		f->fmt.pix_mp.width, f->fmt.pix_mp.height);
+
+	/*
+	 * Scaling currently does not work properly for Capture mode.
+	 * So we are temporarily forcing the frame size to be the
+	 * same as the source crtc for now.
+	 */
+	crtc = drmpriv->pipes[wb_inputs[wbcap->input].crtc_index].crtc;
+	ct = omap_crtc_timings(crtc);
+
+	f->fmt.pix.width = ct->hactive;
+	f->fmt.pix.height = ct->vactive;
+
+	if (ct->flags & DISPLAY_FLAGS_INTERLACED) {
+		f->fmt.pix.height /= 2;
+		f->fmt.pix_mp.field = V4L2_FIELD_ALTERNATE;
+	}
+
+	log_dbg(wbcap, "replied fourcc:%4.4s size: %dx%d\n",
+		(char *)&f->fmt.pix_mp.pixelformat,
+		f->fmt.pix_mp.width, f->fmt.pix_mp.height);
+
+	return wbcap_fill_pix_format(wbcap, f);
+}
+
+static int wbcap_s_fmt_vid_cap(struct file *file, void *priv,
+			       struct v4l2_format *f)
+{
+	struct wbcap_dev *wbcap = video_drvdata(file);
+	int ret;
+	struct wb_q_data *q_data;
+
+	log_dbg(wbcap, "type:%d\n", f->type);
+
+	ret = wbcap_try_fmt_vid_cap(file, priv, f);
+	if (ret)
+		return ret;
+
+	q_data = get_q_data(wbcap, f->type);
+	if (!q_data)
+		return -EINVAL;
+
+	/*
+	 * It is not allowed to change the format while buffers for use with
+	 * streaming have already been allocated.
+	 */
+	if (vb2_is_busy(&wbcap->queue))
+		return -EBUSY;
+
+	q_data->format = *f;
+	q_data->fmt = find_format(f);
+
+	log_dbg(wbcap, "Setting format for type %d, %dx%d, fmt: %4.4s bpl_y %d",
+		f->type, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
+		(char *)&f->fmt.pix_mp.pixelformat,
+		f->fmt.pix_mp.plane_fmt[LUMA_PLANE].bytesperline);
+	if (f->fmt.pix_mp.num_planes == 2)
+		log_dbg(wbcap, " bpl_uv %d\n",
+			f->fmt.pix_mp.plane_fmt[CHROMA_PLANE].bytesperline);
+
+	return 0;
+}
+
+static int wbcap_g_fmt_vid_cap(struct file *file, void *priv,
+			       struct v4l2_format *f)
+{
+	struct wbcap_dev *wbcap = video_drvdata(file);
+	struct wb_q_data *q_data;
+
+	log_dbg(wbcap, "type:%d\n", f->type);
+
+	q_data = get_q_data(wbcap, f->type);
+	if (!q_data)
+		return -EINVAL;
+
+	*f = q_data->format;
+	return 0;
+}
+
+static int wbcap_enum_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_fmtdesc *f)
+{
+	if (f->index >= num_wb_formats)
+		return -EINVAL;
+
+	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	f->pixelformat = wb_formats[f->index].fourcc;
+	return 0;
+}
+
+static int wbcap_enum_input(struct file *file, void *priv,
+			    struct v4l2_input *i)
+{
+	if (i->index >= num_wb_input)
+		return -EINVAL;
+
+	i->type = V4L2_INPUT_TYPE_CAMERA;
+	strlcpy(i->name, wb_inputs[i->index].name, sizeof(i->name));
+	return 0;
+}
+
+static int wbcap_s_input(struct file *file, void *priv, unsigned int i)
+{
+	struct wbcap_dev *wbcap = video_drvdata(file);
+	struct wb_q_data *q_data;
+
+	log_dbg(wbcap, "%d\n", i);
+
+	q_data = get_q_data(wbcap, wbcap->queue.type);
+	if (!q_data)
+		return -EINVAL;
+
+	if (i >= num_wb_input)
+		return -EINVAL;
+
+	/*
+	 * Changing the input implies a format change, which is not allowed
+	 * while buffers for use with streaming have already been allocated.
+	 */
+	if (vb2_is_busy(&wbcap->queue))
+		return -EBUSY;
+
+	wbcap->input = i;
+
+	/* Update the internal format to match the selected input */
+	wbcap_try_fmt_vid_cap(file, priv, &q_data->format);
+	return 0;
+}
+
+static int wbcap_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	struct wbcap_dev *wbcap = video_drvdata(file);
+
+	log_dbg(wbcap, "%d\n", wbcap->input);
+
+	*i = wbcap->input;
+	return 0;
+}
+
+/*
+ * File operations
+ */
+static int wbcap_open(struct file *file)
+{
+	struct wbcap_dev *dev = video_drvdata(file);
+	int ret;
+
+	log_dbg(dev, "enter\n");
+
+	if (mutex_lock_interruptible(&dev->dev->lock)) {
+		ret = -ERESTARTSYS;
+		goto unlock;
+	}
+
+	if ((dev->dev->mode != OMAP_WB_NOT_CONFIGURED) &&
+	    (dev->dev->mode != OMAP_WB_CAPTURE_MGR)) {
+		/* WB is already open for other modes */
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	ret = v4l2_fh_open(file);
+	if (ret) {
+		log_err(dev, "v4l2_fh_open failed\n");
+		goto unlock;
+	}
+
+	if (v4l2_fh_is_singular_file(file))
+		dev->dev->mode = OMAP_WB_CAPTURE_MGR;
+
+unlock:
+	mutex_unlock(&dev->dev->lock);
+	return ret;
+}
+
+static int wbcap_release(struct file *file)
+{
+	struct wbcap_dev *dev = video_drvdata(file);
+	bool fh_singular;
+	int ret;
+
+	log_dbg(dev, "releasing\n");
+
+	mutex_lock(&dev->dev->lock);
+
+	/* Save the singular status before we call the clean-up helper */
+	fh_singular = v4l2_fh_is_singular_file(file);
+
+	/* the release helper will cleanup any on-going streaming */
+	ret = _vb2_fop_release(file, NULL);
+
+	if (fh_singular)
+		dev->dev->mode = OMAP_WB_NOT_CONFIGURED;
+
+	mutex_unlock(&dev->dev->lock);
+
+	return ret;
+}
+
+/*
+ * The set of all supported ioctls. Note that all the streaming ioctls
+ * use the vb2 helper functions that take care of all the locking and
+ * that also do ownership tracking (i.e. only the filehandle that requested
+ * the buffers can call the streaming ioctls, all other filehandles will
+ * receive -EBUSY if they attempt to call the same streaming ioctls).
+ *
+ * The last three ioctls also use standard helper functions: these implement
+ * standard behavior for drivers with controls.
+ */
+static const struct v4l2_ioctl_ops wbcap_ioctl_ops = {
+	.vidioc_querycap = wbcap_querycap,
+	.vidioc_try_fmt_vid_cap_mplane = wbcap_try_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap_mplane = wbcap_s_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap_mplane = wbcap_g_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_cap_mplane = wbcap_enum_fmt_vid_cap,
+
+	.vidioc_enum_input = wbcap_enum_input,
+	.vidioc_g_input = wbcap_g_input,
+	.vidioc_s_input = wbcap_s_input,
+
+	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs = vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+	.vidioc_querybuf = vb2_ioctl_querybuf,
+	.vidioc_qbuf = vb2_ioctl_qbuf,
+	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_expbuf = vb2_ioctl_expbuf,
+	.vidioc_streamon = vb2_ioctl_streamon,
+	.vidioc_streamoff = vb2_ioctl_streamoff,
+
+	.vidioc_log_status = v4l2_ctrl_log_status,
+	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/*
+ * The set of file operations. Note that all these ops are standard core
+ * helper functions.
+ */
+static const struct v4l2_file_operations wbcap_fops = {
+	.owner = THIS_MODULE,
+	.open = wbcap_open,
+	.release = wbcap_release,
+	.unlocked_ioctl = video_ioctl2,
+	.read = vb2_fop_read,
+	.mmap = vb2_fop_mmap,
+	.poll = vb2_fop_poll,
+};
+
+/*
+ * The initial setup of this device instance. Note that the initial state of
+ * the driver should be complete. So the initial format, standard, timings
+ * and video input should all be initialized to some reasonable value.
+ */
+int wbcap_init(struct wb_dev *dev)
+{
+	struct wbcap_dev *wbcap;
+	struct video_device *vdev;
+	struct vb2_queue *q;
+	struct wb_q_data *q_data;
+	int ret;
+
+	if (!dev)
+		return -ENOMEM;
+
+	/* Allocate a new instance */
+	wbcap = devm_kzalloc(dev->drm_dev->dev, sizeof(*wbcap), GFP_KERNEL);
+	if (!wbcap)
+		return -ENOMEM;
+
+	dev->cap = wbcap;
+	wbcap->dev = dev;
+
+	/* Fill in the initial format-related settings */
+	q_data = &wbcap->q_data[Q_DATA_DST];
+	q_data->fmt = &wb_formats[1];
+	q_data->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	q_data->format.fmt.pix_mp.pixelformat = q_data->fmt->fourcc;
+	q_data->format.fmt.pix_mp.width = 1920;
+	q_data->format.fmt.pix_mp.height = 1080;
+	wbcap_fill_pix_format(wbcap, &q_data->format);
+
+	/* Initialize the top-level structure */
+	strlcpy(wbcap->v4l2_dev.name, WBCAP_MODULE_NAME,
+		sizeof(wbcap->v4l2_dev.name));
+	ret = v4l2_device_register(dev->drm_dev->dev, &wbcap->v4l2_dev);
+	if (ret)
+		return ret;
+
+	/*
+	 * This lock is now created by the main level.
+	 * We might need one per sub structure in the future
+	 *
+	 *  mutex_init(&dev->lock);
+	 */
+
+	/* Initialize the vb2 queue */
+	q = &wbcap->queue;
+	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
+	q->drv_priv = wbcap;
+	q->buf_struct_size = sizeof(struct wb_buffer);
+	q->ops = &wbcap_qops;
+	q->mem_ops = &vb2_dma_contig_memops;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	q->dev = wbcap->v4l2_dev.dev;
+
+	/*
+	 * Assume that this DMA engine needs to have at least two buffers
+	 * available before it can be started. The start_streaming() op
+	 * won't be called until at least this many buffers are queued up.
+	 */
+	q->min_buffers_needed = 2;
+	/*
+	 * The serialization lock for the streaming ioctls. This is the same
+	 * as the main serialization lock, but if some of the non-streaming
+	 * ioctls could take a long time to execute, then you might want to
+	 * have a different lock here to prevent VIDIOC_DQBUF from being
+	 * blocked while waiting for another action to finish. This is
+	 * generally not needed for PCI devices, but USB devices usually do
+	 * want a separate lock here.
+	 */
+	q->lock = &dev->lock;
+	/*
+	 * Since this driver can only do 32-bit DMA we must make sure that
+	 * the vb2 core will allocate the buffers in 32-bit DMA memory.
+	 */
+	q->gfp_flags = GFP_DMA32;
+	ret = vb2_queue_init(q);
+	if (ret)
+		goto free_hdl;
+
+	INIT_LIST_HEAD(&wbcap->buf_list);
+	spin_lock_init(&wbcap->qlock);
+
+	/* Initialize the video_device structure */
+	vdev = &wbcap->vdev;
+	strlcpy(vdev->name, WBCAP_MODULE_NAME, sizeof(vdev->name));
+	/*
+	 * There is nothing to clean up, so release is set to an empty release
+	 * function. The release callback must be non-NULL.
+	 */
+	vdev->release = video_device_release_empty;
+	vdev->fops = &wbcap_fops,
+	vdev->ioctl_ops = &wbcap_ioctl_ops,
+	/*
+	 * The main serialization lock. All ioctls are serialized by this
+	 * lock. Exception: if q->lock is set, then the streaming ioctls
+	 * are serialized by that separate lock.
+	 */
+	vdev->lock = &dev->lock;
+	vdev->queue = q;
+	vdev->v4l2_dev = &wbcap->v4l2_dev;
+	video_set_drvdata(vdev, wbcap);
+
+	hrtimer_init(&wbcap->wbgo_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	wbcap->wbgo_timer.function = wbcap_wbgo_timer;
+
+	init_waitqueue_head(&wbcap->event);
+	wbcap->stopping = false;
+
+	build_input_table(wbcap);
+
+	ret = video_register_device(vdev, VFL_TYPE_GRABBER, 11);
+	if (ret)
+		goto free_hdl;
+
+	log_dbg(wbcap, "Device registered as %s\n",
+		video_device_node_name(vdev));
+	return 0;
+
+free_hdl:
+	v4l2_device_unregister(&wbcap->v4l2_dev);
+	return ret;
+}
+
+void wbcap_cleanup(struct wb_dev *dev)
+{
+	log_dbg(dev, "Cleanup WB Capture\n");
+
+	video_unregister_device(&dev->cap->vdev);
+	v4l2_device_unregister(&dev->cap->v4l2_dev);
+}

+ 1199 - 0
drivers/gpu/drm/omapdrm/omap_wb_m2m.c

@@ -0,0 +1,1199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2018 Texas Instruments Incorporated -  http://www.ti.com/
+ * Benoit Parrot <bparrot@ti.com>
+ *
+ * Based on the virtual v4l2-mem2mem example device
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "omap_wb.h"
+
+MODULE_DESCRIPTION("TI OMAP WB M2M driver");
+MODULE_AUTHOR("Benoit Parrot <bparrot@ti.com>");
+MODULE_LICENSE("GPL v2");
+
+/*
+ * M2M devices get 2 queues.
+ * Return the queue given the type.
+ */
+static struct wb_q_data *get_q_data(struct wbm2m_ctx *ctx,
+				    enum v4l2_buf_type type)
+{
+	switch (type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		return &ctx->q_data[Q_DATA_SRC];
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		return &ctx->q_data[Q_DATA_DST];
+	default:
+		return NULL;
+	}
+	return NULL;
+}
+
+static bool wbm2m_convert(struct wbm2m_dev *dev, enum omap_plane_id src_plane,
+			  const struct omap_overlay_info *src_info,
+			  const struct omap_dss_writeback_info *wb_info)
+{
+	struct omap_drm_private *priv = dev->dev->drm_dev->dev_private;
+	enum dss_writeback_channel wb_channel;
+	struct videomode t = { 0 };
+	int r;
+
+	t.hactive = src_info->out_width;
+	t.vactive = src_info->out_height;
+
+	/* configure input */
+
+	r = priv->dispc_ops->ovl_setup(priv->dispc, src_plane, src_info, &t,
+				       true, OMAP_DSS_CHANNEL_WB);
+	if (r)
+		return false;
+
+	priv->dispc_ops->ovl_enable(priv->dispc, src_plane, true);
+
+	/* configure output */
+
+	switch (src_plane) {
+	case OMAP_DSS_GFX:
+		wb_channel = DSS_WB_OVL0; break;
+	case OMAP_DSS_VIDEO1:
+		wb_channel = DSS_WB_OVL1; break;
+	case OMAP_DSS_VIDEO2:
+		wb_channel = DSS_WB_OVL2; break;
+	case OMAP_DSS_VIDEO3:
+		wb_channel = DSS_WB_OVL3; break;
+	default:
+		/*
+		 * if src_plane is not valid it should have been flagged
+		 * during the ovl_setup() step above. Let's set a default
+		 * at any rate.
+		 */
+		wb_channel = DSS_WB_OVL3; break;
+	}
+
+	r = priv->dispc_ops->wb_setup(priv->dispc, wb_info, true, &t,
+				      wb_channel);
+	if (r) {
+		priv->dispc_ops->ovl_enable(priv->dispc, src_plane, false);
+		return false;
+	}
+
+	priv->dispc_ops->ovl_enable(priv->dispc, OMAP_DSS_WB, true);
+
+	return true;
+}
+
+/*
+ * mem2mem callbacks
+ */
+
+/**
+ * job_ready() - check whether an instance is ready to be scheduled to run
+ */
+static int job_ready(void *priv)
+{
+	struct wbm2m_ctx *ctx = priv;
+
+	/*
+	 * This check is needed as this might be called directly from driver
+	 * When called by m2m framework, this will always satisy, but when
+	 * called from wbm2m_irq, this might fail.
+	 * (src stream with zero buffers)
+	 */
+	if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) <= 0 ||
+	    v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) <= 0)
+		return 0;
+
+	return 1;
+}
+
+static void job_abort(void *priv)
+{
+	struct wbm2m_ctx *ctx = priv;
+
+	/* Will cancel the transaction in the next interrupt handler */
+	ctx->aborting = 1;
+
+	log_dbg(ctx->dev, "Aborting transaction\n");
+	v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+/* device_run() - prepares and starts the device
+ *
+ * This function is only called when both the source and destination
+ * buffers are in place.
+ */
+static void device_run(void *priv)
+{
+	struct wbm2m_ctx *ctx = priv;
+	struct wbm2m_dev *dev = ctx->dev;
+	struct wb_q_data *d_q_data = &ctx->q_data[Q_DATA_DST];
+	struct wb_q_data *s_q_data = &ctx->q_data[Q_DATA_SRC];
+	struct vb2_buffer *s_vb, *d_vb;
+	struct vb2_v4l2_buffer *src_vb, *dst_vb;
+	dma_addr_t src_dma_addr[2] = {0, 0};
+	dma_addr_t dst_dma_addr[2] = {0, 0};
+	struct omap_overlay_info src_info = { 0 };
+	struct omap_dss_writeback_info wb_info = { 0 };
+	struct v4l2_pix_format_mplane *spix, *dpix;
+	struct v4l2_rect *srect, *drect;
+	u32 stride, depth;
+	bool ok;
+
+	src_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+	if (!src_vb) {
+		log_err(dev, "getting next source buffer failed\n");
+		return;
+	}
+
+	s_vb = &src_vb->vb2_buf;
+	if (!s_vb) {
+		log_err(dev, "getting next src vb2_buf addr failed\n");
+		return;
+	}
+
+	dst_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+	if (!dst_vb) {
+		log_err(dev, "getting next dest buffer failed\n");
+		return;
+	}
+
+	d_vb = &dst_vb->vb2_buf;
+	if (!d_vb) {
+		log_err(dev, "getting next dest vb2_buf addr failed\n");
+		return;
+	}
+
+	srect = &s_q_data->c_rect;
+	spix = &s_q_data->format.fmt.pix_mp;
+	src_dma_addr[0] = vb2_dma_contig_plane_dma_addr(s_vb, 0);
+	if (spix->num_planes == 2)
+		src_dma_addr[1] = vb2_dma_contig_plane_dma_addr(s_vb, 1);
+	else if (spix->pixelformat == V4L2_PIX_FMT_NV12)
+		src_dma_addr[1] = src_dma_addr[0] +
+			(spix->plane_fmt[0].bytesperline * spix->height);
+	if (!src_dma_addr[0]) {
+		log_err(dev,
+			"acquiring source buffer(%d) dma_addr failed\n",
+			s_vb->index);
+		return;
+	}
+
+	drect = &d_q_data->c_rect;
+	dpix = &d_q_data->format.fmt.pix_mp;
+	dst_dma_addr[0] = vb2_dma_contig_plane_dma_addr(d_vb, 0);
+	if (dpix->num_planes == 2)
+		dst_dma_addr[1] = vb2_dma_contig_plane_dma_addr(d_vb, 1);
+	else if (dpix->pixelformat == V4L2_PIX_FMT_NV12)
+		dst_dma_addr[1] = dst_dma_addr[0] +
+			(dpix->plane_fmt[0].bytesperline * dpix->height);
+	if (!dst_dma_addr[0]) {
+		log_err(dev,
+			"acquiring destination buffer(%d) dma_addr failed\n",
+			d_vb->index);
+		return;
+	}
+
+	/* fill source DSS info */
+	src_info.paddr = (u32)src_dma_addr[0];
+	src_info.p_uv_addr = (u32)src_dma_addr[1];
+
+	/* update addr based on cropping window */
+	stride = spix->plane_fmt[0].bytesperline;
+	depth = s_q_data->fmt->depth[0];
+	src_info.paddr += srect->top * stride + (srect->left * depth / 8);
+
+	if (src_info.p_uv_addr) {
+		u32 top = srect->top;
+
+		if (spix->pixelformat == V4L2_PIX_FMT_NV12 ||
+		    spix->pixelformat == V4L2_PIX_FMT_NV12M) {
+			top >>= 1;
+			depth = 8;
+		}
+		src_info.p_uv_addr += top * stride + (srect->left * depth / 8);
+	}
+
+	src_info.screen_width = spix->plane_fmt[0].bytesperline /
+				(s_q_data->fmt->depth[0] / 8);
+
+	src_info.width = srect->width;
+	src_info.height = srect->height;
+
+	src_info.pos_x = 0;
+	src_info.pos_y = 0;
+	src_info.out_width = srect->width;
+	src_info.out_height = srect->height;
+
+	src_info.fourcc = omap_wb_fourcc_v4l2_to_drm(spix->pixelformat);
+	src_info.global_alpha = 0xff;
+
+	src_info.rotation = DRM_MODE_ROTATE_0;
+	src_info.rotation_type = OMAP_DSS_ROT_NONE;
+
+	log_dbg(dev, "SRC: ctx %pa buf_index %d %dx%d, sw %d\n",
+		&ctx, s_vb->index,
+		src_info.width, src_info.height, src_info.screen_width);
+
+	/* fill WB DSS info */
+	wb_info.paddr = (u32)dst_dma_addr[0];
+	wb_info.p_uv_addr = (u32)dst_dma_addr[1];
+
+	wb_info.buf_width = dpix->plane_fmt[0].bytesperline /
+			    (d_q_data->fmt->depth[0] / 8);
+
+	/* update addr based on compose window */
+	stride = dpix->plane_fmt[0].bytesperline;
+	depth = d_q_data->fmt->depth[0];
+	wb_info.paddr += drect->top * stride + (drect->left * depth / 8);
+
+	if (wb_info.p_uv_addr) {
+		u32 top = drect->top;
+
+		if (dpix->pixelformat == V4L2_PIX_FMT_NV12 ||
+		    dpix->pixelformat == V4L2_PIX_FMT_NV12M) {
+			top >>= 1;
+			depth = 8;
+		}
+		wb_info.p_uv_addr += top * stride + (drect->left * depth / 8);
+	}
+
+	wb_info.width = drect->width;
+	wb_info.height = drect->height;
+	wb_info.fourcc = omap_wb_fourcc_v4l2_to_drm(dpix->pixelformat);
+	wb_info.pre_mult_alpha = 1;
+
+	wb_info.rotation = DRM_MODE_ROTATE_0;
+	wb_info.rotation_type = OMAP_DSS_ROT_NONE;
+
+	log_dbg(dev, "DST: ctx %pa buf_index %d %dx%d, sw %d\n",
+		&ctx, d_vb->index,
+		wb_info.width, wb_info.height, wb_info.buf_width);
+
+	ok = wbm2m_convert(dev, omap_plane_id_wb(dev->plane), &src_info,
+			   &wb_info);
+	if (!ok) {
+		log_err(dev,
+			"Conversion setup failed, check source and destination parameters\n"
+			);
+		log_err(dev, "\tSRC: %dx%d, fmt: %4.4s sw %d\n",
+			src_info.width, src_info.height,
+			(char *)&spix->pixelformat,
+			src_info.screen_width);
+		log_err(dev, "\tDST: %dx%d, fmt: %4.4s sw %d\n",
+			wb_info.width, wb_info.height,
+			(char *)&dpix->pixelformat,
+			wb_info.buf_width);
+		return;
+	}
+}
+
+void wbm2m_irq(struct wbm2m_dev *wbm2m, u32 irqstatus)
+{
+	struct wbm2m_ctx *ctx;
+	struct vb2_v4l2_buffer *s_vb, *d_vb;
+	unsigned long flags;
+
+	if (irqstatus & DISPC_IRQ_WBBUFFEROVERFLOW)
+		log_err(wbm2m, "WB: UNDERFLOW\n");
+
+	if (irqstatus & DISPC_IRQ_WBUNCOMPLETEERROR)
+		log_err(wbm2m, "WB: DISPC_IRQ_WBUNCOMPLETEERROR\n");
+
+	/* If DISPC_IRQ_FRAMEDONEWB is not set then we are done */
+	if (!(irqstatus & DISPC_IRQ_FRAMEDONEWB))
+		goto handled;
+
+	log_dbg(wbm2m, "WB: FRAMEDONE\n");
+
+	ctx = v4l2_m2m_get_curr_priv(wbm2m->m2m_dev);
+	if (!ctx) {
+		log_err(wbm2m, "instance released before end of transaction\n");
+		goto handled;
+	}
+
+	log_dbg(ctx->dev, "ctx %pa\n", &ctx);
+
+	s_vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+	d_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+	if (!s_vb || !d_vb) {
+		log_err(wbm2m, "source or dest vb pointer is NULL!!");
+		goto handled;
+	}
+
+	d_vb->flags = s_vb->flags;
+
+	d_vb->vb2_buf.timestamp = s_vb->vb2_buf.timestamp;
+	if (s_vb->flags & V4L2_BUF_FLAG_TIMECODE)
+		d_vb->timecode = s_vb->timecode;
+
+	d_vb->sequence = ctx->sequence;
+	s_vb->sequence = ctx->sequence;
+	log_dbg(wbm2m, "ctx %pa sequence %d\n",
+		&ctx, ctx->sequence);
+
+	d_vb->field = V4L2_FIELD_NONE;
+	ctx->sequence++;
+
+	spin_lock_irqsave(&wbm2m->lock, flags);
+
+	v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_DONE);
+	v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_DONE);
+
+	spin_unlock_irqrestore(&wbm2m->lock, flags);
+
+	v4l2_m2m_job_finish(wbm2m->m2m_dev, ctx->fh.m2m_ctx);
+handled:
+	return;
+}
+
+/*
+ * video ioctls
+ */
+static int wbm2m_querycap(struct file *file, void *priv,
+			  struct v4l2_capability *cap)
+{
+	struct wbm2m_ctx *ctx = file->private_data;
+
+	strncpy(cap->driver, WBM2M_MODULE_NAME, sizeof(cap->driver) - 1);
+	strncpy(cap->card, WBM2M_MODULE_NAME, sizeof(cap->card) - 1);
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+		 ctx->dev->v4l2_dev.name);
+	cap->device_caps  = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+	return 0;
+}
+
+static int wbm2m_enum_fmt(struct file *file, void *priv,
+			  struct v4l2_fmtdesc *f)
+{
+	if (f->index >= num_wb_formats)
+		return -EINVAL;
+
+	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	f->pixelformat = wb_formats[f->index].fourcc;
+	return 0;
+}
+
+static int wbm2m_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct wbm2m_ctx *ctx = file->private_data;
+	struct vb2_queue *vq;
+	struct wb_q_data *q_data;
+	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (!vq)
+		return -EINVAL;
+
+	q_data = get_q_data(ctx, f->type);
+	if (!q_data)
+		return -EINVAL;
+
+	*f = q_data->format;
+
+	if (!V4L2_TYPE_IS_OUTPUT(f->type)) {
+		struct wb_q_data *s_q_data;
+
+		/* get colorspace from the source queue */
+		s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+		f->fmt.pix_mp.colorspace =
+			s_q_data->format.fmt.pix_mp.colorspace;
+	}
+
+	log_dbg(ctx->dev, "ctx %pa type %d, %dx%d, fmt: %4.4s bpl_y %d",
+		&ctx, f->type, pix->width, pix->height,
+		(char *)&pix->pixelformat,
+		pix->plane_fmt[LUMA_PLANE].bytesperline);
+	if (pix->num_planes == 2)
+		log_dbg(ctx->dev, " bpl_uv %d\n",
+			pix->plane_fmt[CHROMA_PLANE].bytesperline);
+
+	return 0;
+}
+
+static int wbm2m_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct wbm2m_ctx *ctx = file->private_data;
+	struct wb_fmt *fmt = find_format(f);
+	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+	struct v4l2_plane_pix_format *plane_fmt;
+	unsigned int w_align;
+	int i, depth, depth_bytes;
+
+	if (!fmt) {
+		log_dbg(ctx->dev, "Fourcc format (0x%08x) invalid.\n",
+			pix->pixelformat);
+		fmt = &wb_formats[1];
+	}
+
+	/* we only allow V4L2_FIELD_NONE */
+	if (pix->field != V4L2_FIELD_NONE)
+		pix->field = V4L2_FIELD_NONE;
+
+	depth = fmt->depth[LUMA_PLANE];
+
+	/*
+	 * The line stride needs to be even is even.
+	 * Special case is with YUV422 interleaved format an even number
+	 * of pixels is required also.
+	 */
+	depth_bytes = depth >> 3;
+
+	w_align = 0;
+	if ((depth_bytes == 3) || (depth_bytes == 1))
+		w_align = 1;
+	else if ((depth_bytes == 2) &&
+		 (fmt->fourcc == V4L2_PIX_FMT_YUYV ||
+		  fmt->fourcc == V4L2_PIX_FMT_UYVY))
+		w_align = 1;
+
+	v4l_bound_align_image(&pix->width, MIN_W, MAX_W, w_align,
+			      &pix->height, MIN_H, MAX_H, H_ALIGN,
+			      S_ALIGN);
+	pix->num_planes = fmt->coplanar ? 2 : 1;
+	pix->pixelformat = fmt->fourcc;
+
+	/* Probably need something better here */
+	if (!pix->colorspace) {
+		if (fmt->fourcc == V4L2_PIX_FMT_RGB24 ||
+		    fmt->fourcc == V4L2_PIX_FMT_BGR24 ||
+		    fmt->fourcc == V4L2_PIX_FMT_RGB32 ||
+		    fmt->fourcc == V4L2_PIX_FMT_BGR32) {
+			pix->colorspace = V4L2_COLORSPACE_SRGB;
+		} else {
+			if (pix->height > 1280)	/* HD */
+				pix->colorspace = V4L2_COLORSPACE_REC709;
+			else			/* SD */
+				pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+		}
+	}
+
+	memset(pix->reserved, 0, sizeof(pix->reserved));
+	for (i = 0; i < pix->num_planes; i++) {
+		plane_fmt = &pix->plane_fmt[i];
+		depth = fmt->depth[i];
+
+		if (i == LUMA_PLANE)
+			plane_fmt->bytesperline = pix->width * depth / 8;
+		else
+			plane_fmt->bytesperline = pix->width;
+
+		plane_fmt->sizeimage = (pix->height * pix->width *
+					depth) / 8;
+
+		if (fmt->fourcc == V4L2_PIX_FMT_NV12) {
+			/*
+			 * Since we are using a single plane buffer
+			 * we need to adjust the reported sizeimage
+			 * to include the colocated UV part.
+			 */
+			plane_fmt->sizeimage += (pix->height / 2 *
+				plane_fmt->bytesperline);
+		}
+
+		memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved));
+	}
+
+	return 0;
+}
+
+static int __wbm2m_s_fmt(struct wbm2m_ctx *ctx, struct v4l2_format *f)
+{
+	struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+	struct wb_q_data *q_data;
+	struct vb2_queue *vq;
+
+	vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+	if (!vq)
+		return -EINVAL;
+
+	if (vb2_is_busy(vq)) {
+		log_err(ctx->dev, "queue busy\n");
+		return -EBUSY;
+	}
+
+	q_data = get_q_data(ctx, f->type);
+	if (!q_data)
+		return -EINVAL;
+
+	q_data->fmt = find_format(f);
+	q_data->format = *f;
+
+	q_data->c_rect.left	= 0;
+	q_data->c_rect.top	= 0;
+	q_data->c_rect.width	= pix->width;
+	q_data->c_rect.height	= pix->height;
+
+	log_dbg(ctx->dev, "ctx %pa type %d, %dx%d, fmt: %4.4s bpl_y %d",
+		&ctx, f->type, pix->width, pix->height,
+		(char *)&pix->pixelformat,
+		pix->plane_fmt[LUMA_PLANE].bytesperline);
+	if (pix->num_planes == 2)
+		log_dbg(ctx->dev, " bpl_uv %d\n",
+			pix->plane_fmt[CHROMA_PLANE].bytesperline);
+
+	return 0;
+}
+
+static int wbm2m_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	int ret;
+	struct wbm2m_ctx *ctx = file->private_data;
+
+	ret = wbm2m_try_fmt(file, priv, f);
+	if (ret)
+		return ret;
+
+	ret = __wbm2m_s_fmt(ctx, f);
+	if (ret)
+		return ret;
+
+	ctx->sequence = 0;
+
+	return 0;
+}
+
+static int __wbm2m_try_selection(struct wbm2m_ctx *ctx,
+				 struct v4l2_selection *s)
+{
+	struct wb_q_data *q_data;
+	struct v4l2_pix_format_mplane *pix;
+	unsigned int w_align;
+	int depth_bytes;
+
+	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
+		return -EINVAL;
+
+	q_data = get_q_data(ctx, s->type);
+	if (!q_data)
+		return -EINVAL;
+
+	pix = &q_data->format.fmt.pix_mp;
+
+	switch (s->target) {
+	case V4L2_SEL_TGT_COMPOSE:
+		/*
+		 * COMPOSE target is only valid for capture buffer type, return
+		 * error for output buffer type
+		 */
+		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+		break;
+	case V4L2_SEL_TGT_CROP:
+		/*
+		 * CROP target is only valid for output buffer type, return
+		 * error for capture buffer type
+		 */
+		if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		break;
+	/*
+	 * bound and default crop/compose targets are invalid targets to
+	 * try/set
+	 */
+	default:
+		return -EINVAL;
+	}
+
+	if (s->r.top < 0 || s->r.left < 0) {
+		log_err(ctx->dev, "negative values for top and left\n");
+		s->r.top = 0;
+		s->r.left = 0;
+	}
+
+	depth_bytes = q_data->fmt->depth[LUMA_PLANE] >> 3;
+
+	w_align = 0;
+	if ((depth_bytes == 3) || (depth_bytes == 1))
+		w_align = 1;
+	else if ((depth_bytes == 2) &&
+		 (pix->pixelformat == V4L2_PIX_FMT_YUYV ||
+		  pix->pixelformat == V4L2_PIX_FMT_UYVY))
+		w_align = 1;
+
+	v4l_bound_align_image(&s->r.width, MIN_W, pix->width, w_align,
+			      &s->r.height, MIN_H, pix->height,
+			      H_ALIGN, S_ALIGN);
+
+	/* adjust left/top if cropping rectangle is out of bounds */
+	if (s->r.left + s->r.width > pix->width)
+		s->r.left = pix->width - s->r.width;
+	if (s->r.top + s->r.height > pix->height)
+		s->r.top = pix->height - s->r.height;
+
+	return 0;
+}
+
+static int wbm2m_g_selection(struct file *file, void *fh,
+			     struct v4l2_selection *s)
+{
+	struct wbm2m_ctx *ctx = file->private_data;
+	struct wb_q_data *q_data;
+	struct v4l2_pix_format_mplane *pix;
+	bool use_c_rect = false;
+
+	if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+	    (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT))
+		return -EINVAL;
+
+	q_data = get_q_data(ctx, s->type);
+	if (!q_data)
+		return -EINVAL;
+
+	pix = &q_data->format.fmt.pix_mp;
+
+	switch (s->target) {
+	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+		break;
+	case V4L2_SEL_TGT_CROP_BOUNDS:
+	case V4L2_SEL_TGT_CROP_DEFAULT:
+		if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		break;
+	case V4L2_SEL_TGT_COMPOSE:
+		if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			return -EINVAL;
+		use_c_rect = true;
+		break;
+	case V4L2_SEL_TGT_CROP:
+		if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		use_c_rect = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (use_c_rect) {
+		/*
+		 * for CROP/COMPOSE target type, return c_rect params from the
+		 * respective buffer type
+		 */
+		s->r = q_data->c_rect;
+	} else {
+		/*
+		 * for DEFAULT/BOUNDS target type, return width and height from
+		 * S_FMT of the respective buffer type
+		 */
+		s->r.left = 0;
+		s->r.top = 0;
+		s->r.width = pix->width;
+		s->r.height = pix->height;
+	}
+
+	return 0;
+}
+
+static int wbm2m_s_selection(struct file *file, void *fh,
+			     struct v4l2_selection *s)
+{
+	struct wbm2m_ctx *ctx = file->private_data;
+	struct wb_q_data *q_data;
+	struct v4l2_selection sel = *s;
+	int ret;
+
+	ret = __wbm2m_try_selection(ctx, &sel);
+	if (ret)
+		return ret;
+
+	q_data = get_q_data(ctx, sel.type);
+	if (!q_data)
+		return -EINVAL;
+
+	if ((q_data->c_rect.left == sel.r.left) &&
+	    (q_data->c_rect.top == sel.r.top) &&
+	    (q_data->c_rect.width == sel.r.width) &&
+	    (q_data->c_rect.height == sel.r.height)) {
+		log_dbg(ctx->dev,
+			"type: %d, requested crop/compose values are already set\n",
+			sel.type);
+		return 0;
+	}
+
+	q_data->c_rect = sel.r;
+
+	ctx->sequence = 0;
+
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops wbm2m_ioctl_ops = {
+	.vidioc_querycap		= wbm2m_querycap,
+
+	.vidioc_enum_fmt_vid_cap_mplane	= wbm2m_enum_fmt,
+	.vidioc_g_fmt_vid_cap_mplane	= wbm2m_g_fmt,
+	.vidioc_try_fmt_vid_cap_mplane	= wbm2m_try_fmt,
+	.vidioc_s_fmt_vid_cap_mplane	= wbm2m_s_fmt,
+
+	.vidioc_enum_fmt_vid_out_mplane	= wbm2m_enum_fmt,
+	.vidioc_g_fmt_vid_out_mplane	= wbm2m_g_fmt,
+	.vidioc_try_fmt_vid_out_mplane	= wbm2m_try_fmt,
+	.vidioc_s_fmt_vid_out_mplane	= wbm2m_s_fmt,
+
+	.vidioc_g_selection		= wbm2m_g_selection,
+	.vidioc_s_selection		= wbm2m_s_selection,
+
+	.vidioc_reqbufs			= v4l2_m2m_ioctl_reqbufs,
+	.vidioc_create_bufs		= v4l2_m2m_ioctl_create_bufs,
+	.vidioc_prepare_buf		= v4l2_m2m_ioctl_prepare_buf,
+	.vidioc_querybuf		= v4l2_m2m_ioctl_querybuf,
+	.vidioc_qbuf			= v4l2_m2m_ioctl_qbuf,
+	.vidioc_dqbuf			= v4l2_m2m_ioctl_dqbuf,
+	.vidioc_expbuf			= v4l2_m2m_ioctl_expbuf,
+	.vidioc_streamon		= v4l2_m2m_ioctl_streamon,
+	.vidioc_streamoff		= v4l2_m2m_ioctl_streamoff,
+
+	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
+};
+
+/*
+ * Queue operations
+ */
+static int wbm2m_queue_setup(struct vb2_queue *vq,
+			     unsigned int *nbuffers, unsigned int *nplanes,
+			     unsigned int sizes[], struct device *alloc_devs[])
+{
+	int i;
+	struct wbm2m_ctx *ctx = vb2_get_drv_priv(vq);
+	struct wb_q_data *q_data;
+
+	q_data = get_q_data(ctx, vq->type);
+	if (!q_data)
+		return -EINVAL;
+
+	*nplanes = q_data->format.fmt.pix_mp.num_planes;
+
+	for (i = 0; i < *nplanes; i++)
+		sizes[i] = q_data->format.fmt.pix_mp.plane_fmt[i].sizeimage;
+
+	log_dbg(ctx->dev, "get %d buffer(s) of size %d\n", *nbuffers,
+		sizes[LUMA_PLANE]);
+	if (*nplanes == 2)
+		log_dbg(ctx->dev, " and %d\n", sizes[CHROMA_PLANE]);
+
+	return 0;
+}
+
+static int wbm2m_buf_prepare(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct wbm2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+	struct wb_q_data *q_data;
+	struct v4l2_pix_format_mplane *mp;
+	int i, num_planes;
+
+	log_dbg(ctx->dev, "type: %d\n", vb->vb2_queue->type);
+
+	q_data = get_q_data(ctx, vb->vb2_queue->type);
+	if (!q_data)
+		return -EINVAL;
+
+	if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		vbuf->field = V4L2_FIELD_NONE;
+
+	num_planes = q_data->format.fmt.pix_mp.num_planes;
+
+	for (i = 0; i < num_planes; i++) {
+		mp = &q_data->format.fmt.pix_mp;
+
+		if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+			if (vb2_get_plane_payload(vb, i) <
+			    mp->plane_fmt[i].sizeimage) {
+				log_dbg(ctx->dev,
+					"the payload is too small for plane plane (%lu < %lu)\n",
+					vb2_get_plane_payload(vb, i),
+					(long)mp->plane_fmt[i].sizeimage);
+				return -EINVAL;
+			}
+		} else {
+			if (vb2_plane_size(vb, i) <
+			    mp->plane_fmt[i].sizeimage) {
+				log_dbg(ctx->dev,
+					"data will not fit into plane (%lu < %lu)\n",
+					vb2_plane_size(vb, i),
+					(long)mp->plane_fmt[i].sizeimage);
+				return -EINVAL;
+			}
+			vb2_set_plane_payload(vb, i,
+					      mp->plane_fmt[i].sizeimage);
+		}
+	}
+
+	if (num_planes == 2) {
+		if (vb->planes[0].m.fd ==
+		    vb->planes[1].m.fd) {
+			/*
+			 * So it appears we are in a single memory buffer
+			 * with 2 plane case. Then we need to also set the
+			 * data_offset properly
+			 */
+			vb->planes[1].data_offset =
+				vb2_get_plane_payload(vb, 0);
+		}
+	}
+	return 0;
+}
+
+static void wbm2m_buf_queue(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct wbm2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+	log_dbg(ctx->dev, "queueing buffer: %s index %d\n",
+		V4L2_TYPE_IS_OUTPUT(vb->type) ? "OUTPUT" : "CAPTURE",
+		vb->index);
+
+	v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static int wbm2m_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct wbm2m_ctx *ctx = vb2_get_drv_priv(q);
+	struct omap_drm_private *priv = ctx->dev->dev->drm_dev->dev_private;
+
+	log_dbg(ctx->dev, "ctx %pa queue: %s\n", &ctx,
+		V4L2_TYPE_IS_OUTPUT(q->type) ? "OUTPUT" : "CAPTURE");
+
+	ctx->sequence = 0;
+
+	priv->dispc_ops->runtime_get(priv->dispc);
+	atomic_inc(&ctx->dev->dev->irq_enabled);
+
+	return 0;
+}
+
+static void wbm2m_stop_streaming(struct vb2_queue *q)
+{
+	struct wbm2m_ctx *ctx = vb2_get_drv_priv(q);
+	struct omap_drm_private *priv = ctx->dev->dev->drm_dev->dev_private;
+	struct vb2_v4l2_buffer *vb;
+	unsigned long flags;
+
+	log_dbg(ctx->dev, "ctx %pa queue: %s\n", &ctx,
+		V4L2_TYPE_IS_OUTPUT(q->type) ? "OUTPUT" : "CAPTURE");
+
+	atomic_dec(&ctx->dev->dev->irq_enabled);
+
+	for (;;) {
+		if (V4L2_TYPE_IS_OUTPUT(q->type))
+			vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+		else
+			vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+		if (!vb)
+			break;
+		log_dbg(ctx->dev, "returning from queue: buffer index %d\n",
+			vb->vb2_buf.index);
+		spin_lock_irqsave(&ctx->dev->lock, flags);
+		v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
+		spin_unlock_irqrestore(&ctx->dev->lock, flags);
+	}
+
+	/*
+	 * Cleanup the in-transit vb2 buffers that have been
+	 * removed from their respective queue already but for
+	 * which procecessing has not been completed yet.
+	 */
+	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+		/*
+		 * DRA7xx errata i829 (Reusing Pipe Connected To Writeback
+		 * Pipeline On The Fly To An Active Panel)
+		 */
+		priv->dispc_ops->ovl_enable(priv->dispc,
+					    omap_plane_id_wb(ctx->dev->plane),
+					    false);
+		priv->dispc_ops->ovl_enable(priv->dispc, OMAP_DSS_WB, true);
+		priv->dispc_ops->ovl_enable(priv->dispc, OMAP_DSS_WB, false);
+	}
+
+	priv->dispc_ops->runtime_put(priv->dispc);
+}
+
+static struct vb2_ops wbm2m_qops = {
+	.queue_setup	 = wbm2m_queue_setup,
+	.buf_prepare	 = wbm2m_buf_prepare,
+	.buf_queue	 = wbm2m_buf_queue,
+	.wait_prepare	 = vb2_ops_wait_prepare,
+	.wait_finish	 = vb2_ops_wait_finish,
+	.start_streaming = wbm2m_start_streaming,
+	.stop_streaming  = wbm2m_stop_streaming,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+		      struct vb2_queue *dst_vq)
+{
+	struct wbm2m_ctx *ctx = priv;
+	struct wbm2m_dev *dev = ctx->dev;
+	int ret;
+
+	memset(src_vq, 0, sizeof(*src_vq));
+	src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+	src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	src_vq->drv_priv = ctx;
+	src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+	src_vq->ops = &wbm2m_qops;
+	src_vq->mem_ops = &vb2_dma_contig_memops;
+	src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	src_vq->lock = &dev->dev->lock;
+	src_vq->min_buffers_needed = 1;
+	src_vq->dev = dev->v4l2_dev.dev;
+
+	ret = vb2_queue_init(src_vq);
+	if (ret)
+		return ret;
+
+	memset(dst_vq, 0, sizeof(*dst_vq));
+	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+	dst_vq->drv_priv = ctx;
+	dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+	dst_vq->ops = &wbm2m_qops;
+	dst_vq->mem_ops = &vb2_dma_contig_memops;
+	dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+	dst_vq->lock = &dev->dev->lock;
+	dst_vq->min_buffers_needed = 1;
+	dst_vq->dev = dev->v4l2_dev.dev;
+
+	return vb2_queue_init(dst_vq);
+}
+
+/*
+ * File operations
+ */
+static int wbm2m_open(struct file *file)
+{
+	struct wbm2m_dev *dev = video_drvdata(file);
+	struct wb_q_data *s_q_data;
+	struct wbm2m_ctx *ctx;
+	struct v4l2_pix_format_mplane *pix;
+	int ret;
+
+	log_dbg(dev, "enter\n");
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->dev = dev;
+
+	if (mutex_lock_interruptible(&dev->dev->lock)) {
+		ret = -ERESTARTSYS;
+		goto free_ctx;
+	}
+
+	if ((dev->dev->mode != OMAP_WB_NOT_CONFIGURED) &&
+	    (dev->dev->mode != OMAP_WB_MEM2MEM_OVL)) {
+		/* WB is already open for other modes */
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	v4l2_fh_init(&ctx->fh, video_devdata(file));
+	file->private_data = ctx;
+
+	s_q_data = &ctx->q_data[Q_DATA_SRC];
+	s_q_data->fmt = &wb_formats[1];
+	pix = &s_q_data->format.fmt.pix_mp;
+	pix->pixelformat = s_q_data->fmt->fourcc;
+	s_q_data->format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+	pix->width = 1920;
+	pix->height = 1080;
+	pix->plane_fmt[LUMA_PLANE].bytesperline = (pix->width *
+			s_q_data->fmt->depth[LUMA_PLANE]) >> 3;
+	pix->plane_fmt[LUMA_PLANE].sizeimage =
+			pix->plane_fmt[LUMA_PLANE].bytesperline *
+			pix->height;
+	pix->num_planes = s_q_data->fmt->coplanar ? 2 : 1;
+	pix->colorspace = V4L2_COLORSPACE_REC709;
+	pix->field = V4L2_FIELD_NONE;
+	s_q_data->c_rect.left = 0;
+	s_q_data->c_rect.top = 0;
+	s_q_data->c_rect.width = pix->width;
+	s_q_data->c_rect.height = pix->height;
+
+	ctx->q_data[Q_DATA_DST] = *s_q_data;
+	ctx->q_data[Q_DATA_DST].format.type =
+			V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+
+	ctx->sequence = 0;
+
+	ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+
+	if (IS_ERR(ctx->fh.m2m_ctx)) {
+		ret = PTR_ERR(ctx->fh.m2m_ctx);
+		goto exit_fh;
+	}
+
+	v4l2_fh_add(&ctx->fh);
+
+	if (v4l2_fh_is_singular_file(file)) {
+		log_dbg(dev, "first instance created\n");
+
+		drm_modeset_lock_all(dev->dev->drm_dev);
+		dev->plane = omap_plane_reserve_wb(dev->dev->drm_dev);
+		drm_modeset_unlock_all(dev->dev->drm_dev);
+
+		if (!dev->plane) {
+			log_dbg(dev, "Could not reserve plane!\n");
+			ret = -EBUSY;
+			goto free_fh;
+		}
+
+		dev->dev->mode = OMAP_WB_MEM2MEM_OVL;
+	}
+
+	log_dbg(dev, "created instance %pa, m2m_ctx: %pa\n",
+		&ctx, &ctx->fh.m2m_ctx);
+
+	mutex_unlock(&dev->dev->lock);
+
+	return 0;
+
+free_fh:
+	v4l2_fh_del(&ctx->fh);
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+exit_fh:
+	v4l2_fh_exit(&ctx->fh);
+unlock:
+	mutex_unlock(&dev->dev->lock);
+free_ctx:
+	kfree(ctx);
+	return ret;
+}
+
+static int wbm2m_release(struct file *file)
+{
+	struct wbm2m_dev *dev = video_drvdata(file);
+	struct wbm2m_ctx *ctx = file->private_data;
+	bool fh_singular;
+
+	log_dbg(dev, "releasing instance %pa\n", &ctx);
+
+	mutex_lock(&dev->dev->lock);
+
+	/* Save the singular status before we call the clean-up helper */
+	fh_singular = v4l2_fh_is_singular_file(file);
+
+	v4l2_fh_del(&ctx->fh);
+	v4l2_fh_exit(&ctx->fh);
+	v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+
+	kfree(ctx);
+
+	if (fh_singular) {
+		log_dbg(dev, "last instance released\n");
+
+		drm_modeset_lock_all(dev->dev->drm_dev);
+		omap_plane_release_wb(dev->plane);
+		drm_modeset_unlock_all(dev->dev->drm_dev);
+		dev->dev->mode = OMAP_WB_NOT_CONFIGURED;
+	}
+
+	mutex_unlock(&dev->dev->lock);
+
+	return 0;
+}
+
+static const struct v4l2_file_operations wbm2m_fops = {
+	.owner		= THIS_MODULE,
+	.open		= wbm2m_open,
+	.release	= wbm2m_release,
+	.poll		= v4l2_m2m_fop_poll,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= v4l2_m2m_fop_mmap,
+};
+
+static struct video_device wbm2m_videodev = {
+	.name		= WBM2M_MODULE_NAME,
+	.fops		= &wbm2m_fops,
+	.ioctl_ops	= &wbm2m_ioctl_ops,
+	.minor		= -1,
+	.release	= video_device_release_empty,
+	.vfl_dir	= VFL_DIR_M2M,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+	.device_run	= device_run,
+	.job_ready	= job_ready,
+	.job_abort	= job_abort,
+};
+
+int wbm2m_init(struct wb_dev *dev)
+{
+	struct wbm2m_dev *wbm2m;
+	struct video_device *vfd;
+	int ret;
+
+	if (!dev)
+		return -ENOMEM;
+
+	/* Allocate a new instance */
+	wbm2m = devm_kzalloc(dev->drm_dev->dev, sizeof(*wbm2m), GFP_KERNEL);
+	if (!wbm2m)
+		return -ENOMEM;
+
+	dev->m2m = wbm2m;
+	wbm2m->dev = dev;
+
+	spin_lock_init(&wbm2m->lock);
+
+	snprintf(wbm2m->v4l2_dev.name, sizeof(wbm2m->v4l2_dev.name),
+		 "%s", WBM2M_MODULE_NAME);
+	ret = v4l2_device_register(dev->drm_dev->dev, &wbm2m->v4l2_dev);
+	if (ret)
+		return ret;
+
+	wbm2m->m2m_dev = v4l2_m2m_init(&m2m_ops);
+	if (IS_ERR(wbm2m->m2m_dev)) {
+		log_err(wbm2m, "Failed to init mem2mem device\n");
+		ret = PTR_ERR(wbm2m->m2m_dev);
+		goto v4l2_dev_unreg;
+	}
+
+	vfd = &wbm2m->vfd;
+	*vfd = wbm2m_videodev;
+	vfd->lock = &dev->lock;
+	vfd->v4l2_dev = &wbm2m->v4l2_dev;
+
+	ret = video_register_device(vfd, VFL_TYPE_GRABBER, 10);
+	if (ret) {
+		log_err(wbm2m, "Failed to register video device\n");
+		goto rel_m2m;
+	}
+
+	video_set_drvdata(vfd, wbm2m);
+	snprintf(vfd->name, sizeof(vfd->name), "%s", wbm2m_videodev.name);
+	log_dbg(wbm2m, "Device registered as %s\n",
+		video_device_node_name(vfd));
+
+	return 0;
+
+rel_m2m:
+	v4l2_m2m_release(wbm2m->m2m_dev);
+v4l2_dev_unreg:
+	v4l2_device_unregister(&wbm2m->v4l2_dev);
+
+	return ret;
+}
+
+void wbm2m_cleanup(struct wb_dev *dev)
+{
+	log_dbg(dev->m2m, "Cleanup WB M2M\n");
+
+	v4l2_m2m_release(dev->m2m->m2m_dev);
+	video_unregister_device(&dev->m2m->vfd);
+	v4l2_device_unregister(&dev->m2m->v4l2_dev);
+}

+ 6 - 0
drivers/gpu/drm/panel/Kconfig

@@ -99,6 +99,12 @@ config DRM_PANEL_ORISETECH_OTM8009A
 	  Say Y here if you want to enable support for Orise Technology
 	  otm8009a 480x800 dsi 2dl panel.
 
+config DRM_PANEL_OSD_OSD101T2587_53TS
+	tristate "OSD OSD101T2587-53TS DSI 1920x1200 video mode panel"
+	depends on OF
+	depends on DRM_MIPI_DSI
+	depends on BACKLIGHT_CLASS_DEVICE
+
 config DRM_PANEL_PANASONIC_VVX10F034N00
 	tristate "Panasonic VVX10F034N00 1920x1200 video mode panel"
 	depends on OF

+ 1 - 0
drivers/gpu/drm/panel/Makefile

@@ -8,6 +8,7 @@ obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o
 obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
 obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
+obj-$(CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS) += panel-osd-osd101t2587-53ts.o
 obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
 obj-$(CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN) += panel-raspberrypi-touchscreen.o
 obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o

+ 2 - 2
drivers/gpu/drm/panel/panel-arm-versatile.c

@@ -191,7 +191,7 @@ static const struct versatile_panel_type versatile_panels[] = {
 			.vrefresh = 390,
 			.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
 		},
-		.bus_flags = DRM_BUS_FLAG_PIXDATA_NEGEDGE,
+		.bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
 	},
 	/*
 	 * Sanyo ALR252RGT 240x320 portrait display found on the
@@ -215,7 +215,7 @@ static const struct versatile_panel_type versatile_panels[] = {
 			.vrefresh = 116,
 			.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
 		},
-		.bus_flags = DRM_BUS_FLAG_PIXDATA_NEGEDGE,
+		.bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
 		.ib2 = true,
 	},
 };

+ 2 - 2
drivers/gpu/drm/panel/panel-ilitek-ili9322.c

@@ -412,11 +412,11 @@ static int ili9322_init(struct drm_panel *panel, struct ili9322 *ili)
 	if (ili->conf->dclk_active_high) {
 		reg = ILI9322_POL_DCLK;
 		connector->display_info.bus_flags |=
-			DRM_BUS_FLAG_PIXDATA_POSEDGE;
+			DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
 	} else {
 		reg = 0;
 		connector->display_info.bus_flags |=
-			DRM_BUS_FLAG_PIXDATA_NEGEDGE;
+			DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
 	}
 	if (ili->conf->de_active_high) {
 		reg |= ILI9322_POL_DE;

+ 252 - 0
drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c

@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com
+ *  Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ */
+
+#include <linux/backlight.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_device.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+struct osd101t2587_panel {
+	struct drm_panel base;
+	struct mipi_dsi_device *dsi;
+
+	struct backlight_device *backlight;
+	struct regulator *supply;
+
+	bool prepared;
+	bool enabled;
+
+	const struct drm_display_mode *default_mode;
+};
+
+static inline struct osd101t2587_panel *to_osd101t2587_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct osd101t2587_panel, base);
+}
+
+static int osd101t2587_panel_disable(struct drm_panel *panel)
+{
+	struct osd101t2587_panel *osd101t2587 = to_osd101t2587_panel(panel);
+	int ret;
+
+	if (!osd101t2587->enabled)
+		return 0;
+
+	backlight_disable(osd101t2587->backlight);
+
+	ret = mipi_dsi_shutdown_peripheral(osd101t2587->dsi);
+
+	osd101t2587->enabled = false;
+
+	return ret;
+}
+
+static int osd101t2587_panel_unprepare(struct drm_panel *panel)
+{
+	struct osd101t2587_panel *osd101t2587 = to_osd101t2587_panel(panel);
+
+	if (!osd101t2587->prepared)
+		return 0;
+
+	regulator_disable(osd101t2587->supply);
+	osd101t2587->prepared = false;
+
+	return 0;
+}
+
+static int osd101t2587_panel_prepare(struct drm_panel *panel)
+{
+	struct osd101t2587_panel *osd101t2587 = to_osd101t2587_panel(panel);
+	int ret;
+
+	if (osd101t2587->prepared)
+		return 0;
+
+	ret = regulator_enable(osd101t2587->supply);
+	if (!ret)
+		osd101t2587->prepared = true;
+
+	return ret;
+}
+
+static int osd101t2587_panel_enable(struct drm_panel *panel)
+{
+	struct osd101t2587_panel *osd101t2587 = to_osd101t2587_panel(panel);
+	int ret;
+
+	if (osd101t2587->enabled)
+		return 0;
+
+	ret = mipi_dsi_turn_on_peripheral(osd101t2587->dsi);
+	if (ret)
+		return ret;
+
+	backlight_enable(osd101t2587->backlight);
+
+	osd101t2587->enabled = true;
+
+	return ret;
+}
+
+static const struct drm_display_mode default_mode_osd101t2587 = {
+	.clock = 164400,
+	.hdisplay = 1920,
+	.hsync_start = 1920 + 152,
+	.hsync_end = 1920 + 152 + 52,
+	.htotal = 1920 + 152 + 52 + 20,
+	.vdisplay = 1200,
+	.vsync_start = 1200 + 24,
+	.vsync_end = 1200 + 24 + 6,
+	.vtotal = 1200 + 24 + 6 + 48,
+	.vrefresh = 60,
+	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+};
+
+static int osd101t2587_panel_get_modes(struct drm_panel *panel)
+{
+	struct osd101t2587_panel *osd101t2587 = to_osd101t2587_panel(panel);
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, osd101t2587->default_mode);
+	if (!mode) {
+		dev_err(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+			osd101t2587->default_mode->hdisplay,
+			osd101t2587->default_mode->vdisplay,
+			osd101t2587->default_mode->vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	drm_mode_probed_add(panel->connector, mode);
+
+	panel->connector->display_info.width_mm = 217;
+	panel->connector->display_info.height_mm = 136;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs osd101t2587_panel_funcs = {
+	.disable = osd101t2587_panel_disable,
+	.unprepare = osd101t2587_panel_unprepare,
+	.prepare = osd101t2587_panel_prepare,
+	.enable = osd101t2587_panel_enable,
+	.get_modes = osd101t2587_panel_get_modes,
+};
+
+static const struct of_device_id osd101t2587_of_match[] = {
+	{
+		.compatible = "osd,osd101t2587-53ts",
+		.data = &default_mode_osd101t2587,
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, osd101t2587_of_match);
+
+static int osd101t2587_panel_add(struct osd101t2587_panel *osd101t2587)
+{
+	struct device *dev = &osd101t2587->dsi->dev;
+
+	osd101t2587->supply = devm_regulator_get(dev, "power");
+	if (IS_ERR(osd101t2587->supply))
+		return PTR_ERR(osd101t2587->supply);
+
+	osd101t2587->backlight = devm_of_find_backlight(dev);
+	if (IS_ERR(osd101t2587->backlight))
+		return PTR_ERR(osd101t2587->backlight);
+
+	drm_panel_init(&osd101t2587->base);
+	osd101t2587->base.funcs = &osd101t2587_panel_funcs;
+	osd101t2587->base.dev = &osd101t2587->dsi->dev;
+
+	return drm_panel_add(&osd101t2587->base);
+}
+
+static int osd101t2587_panel_probe(struct mipi_dsi_device *dsi)
+{
+	struct osd101t2587_panel *osd101t2587;
+	const struct of_device_id *id;
+	int ret;
+
+	id = of_match_node(osd101t2587_of_match, dsi->dev.of_node);
+	if (!id)
+		return -ENODEV;
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
+			  MIPI_DSI_MODE_VIDEO_BURST |
+			  MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+			  MIPI_DSI_MODE_EOT_PACKET;
+
+	osd101t2587 = devm_kzalloc(&dsi->dev, sizeof(*osd101t2587), GFP_KERNEL);
+	if (!osd101t2587)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, osd101t2587);
+
+	osd101t2587->dsi = dsi;
+	osd101t2587->default_mode = id->data;
+
+	ret = osd101t2587_panel_add(osd101t2587);
+	if (ret < 0)
+		return ret;
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret)
+		drm_panel_remove(&osd101t2587->base);
+
+	return ret;
+}
+
+static int osd101t2587_panel_remove(struct mipi_dsi_device *dsi)
+{
+	struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi);
+	int ret;
+
+	ret = osd101t2587_panel_disable(&osd101t2587->base);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
+
+	ret = mipi_dsi_detach(dsi);
+	if (ret < 0)
+		dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret);
+
+	drm_panel_remove(&osd101t2587->base);
+
+	return 0;
+}
+
+static void osd101t2587_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+	struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi);
+
+	osd101t2587_panel_disable(&osd101t2587->base);
+	osd101t2587_panel_unprepare(&osd101t2587->base);
+}
+
+static struct mipi_dsi_driver osd101t2587_panel_driver = {
+	.driver = {
+		.name = "panel-osd-osd101t2587-53ts",
+		.of_match_table = osd101t2587_of_match,
+	},
+	.probe = osd101t2587_panel_probe,
+	.remove = osd101t2587_panel_remove,
+	.shutdown = osd101t2587_panel_shutdown,
+};
+module_mipi_dsi_driver(osd101t2587_panel_driver);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_DESCRIPTION("OSD101T2587-53TS DSI panel");
+MODULE_LICENSE("GPL v2");

+ 1 - 1
drivers/gpu/drm/panel/panel-seiko-43wvf1g.c

@@ -331,7 +331,7 @@ static const struct seiko_panel_desc seiko_43wvf1g = {
 		.height = 57,
 	},
 	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
-	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_NEGEDGE,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
 };
 
 static const struct of_device_id platform_of_match[] = {

+ 111 - 12
drivers/gpu/drm/panel/panel-simple.c

@@ -608,6 +608,32 @@ static const struct panel_desc auo_g070vvn01 = {
 	},
 };
 
+static const struct drm_display_mode auo_g101evn01_0_mode = {
+	.clock = 68930,
+	.hdisplay = 1280,
+	.hsync_start = 1280 + 48,
+	.hsync_end = 1280 + 48 + 32,
+	.htotal = 1280 + 48 + 32 + 48,
+	.vdisplay = 800,
+	.vsync_start = 800 + 4,
+	.vsync_end = 800 + 4 + 4,
+	.vtotal = 800 + 4 + 4 + 8,
+	.vrefresh = 60,
+	.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+};
+
+static const struct panel_desc auo_g101evn01_0 = {
+	.modes = &auo_g101evn01_0_mode,
+	.num_modes = 1,
+	.bpc = 8,
+	.size = {
+		.width = 217,
+		.height = 136,
+	},
+	.bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
+};
+
 static const struct drm_display_mode auo_g104sn02_mode = {
 	.clock = 40000,
 	.hdisplay = 800,
@@ -929,7 +955,7 @@ static const struct panel_desc dataimage_scf0700c48ggu18 = {
 		.height = 91,
 	},
 	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
-	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
 };
 
 static const struct display_timing dlc_dlc0700yzg_1_timing = {
@@ -984,7 +1010,7 @@ static const struct panel_desc edt_et057090dhu = {
 		.height = 86,
 	},
 	.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
-	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_NEGEDGE,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
 };
 
 static const struct drm_display_mode edt_etm0700g0dh6_mode = {
@@ -1010,7 +1036,7 @@ static const struct panel_desc edt_etm0700g0dh6 = {
 		.height = 91,
 	},
 	.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
-	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_NEGEDGE,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
 };
 
 static const struct panel_desc edt_etm0700g0bdh6 = {
@@ -1022,7 +1048,7 @@ static const struct panel_desc edt_etm0700g0bdh6 = {
 		.height = 91,
 	},
 	.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
-	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
 };
 
 static const struct drm_display_mode foxlink_fl500wvr00_a0t_mode = {
@@ -1176,7 +1202,7 @@ static const struct panel_desc innolux_at043tn24 = {
 		.height = 54,
 	},
 	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
-	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
 };
 
 static const struct drm_display_mode innolux_at070tn92_mode = {
@@ -1658,7 +1684,7 @@ static const struct panel_desc nec_nl4827hc19_05b = {
 		.height = 54,
 	},
 	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
-	.bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE,
+	.bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
 };
 
 static const struct drm_display_mode netron_dy_e231732_mode = {
@@ -1707,8 +1733,8 @@ static const struct panel_desc newhaven_nhd_43_480272ef_atxl = {
 		.height = 54,
 	},
 	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
-	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE |
-		     DRM_BUS_FLAG_SYNC_POSEDGE,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE |
+		     DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE,
 };
 
 static const struct display_timing nlt_nl192108ac18_02d_timing = {
@@ -1869,7 +1895,33 @@ static const struct panel_desc ortustech_com43h4m85ulc = {
 		.height = 93,
 	},
 	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
-	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
+};
+
+static const struct drm_display_mode osddisplays_osd070t1718_19ts_mode  = {
+	.clock = 33000,
+	.hdisplay = 800,
+	.hsync_start = 800 + 210,
+	.hsync_end = 800 + 210 + 30,
+	.htotal = 800 + 210 + 30 + 16,
+	.vdisplay = 480,
+	.vsync_start = 480 + 22,
+	.vsync_end = 480 + 22 + 13,
+	.vtotal = 480 + 22 + 13 + 10,
+	.vrefresh = 60,
+	.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+};
+
+static const struct panel_desc osddisplays_osd070t1718_19ts = {
+	.modes = &osddisplays_osd070t1718_19ts_mode,
+	.num_modes = 1,
+	.bpc = 8,
+	.size = {
+		.width = 152,
+		.height = 91,
+	},
+	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
 };
 
 static const struct drm_display_mode qd43003c0_40_mode = {
@@ -2242,7 +2294,7 @@ static const struct panel_desc toshiba_lt089ac29000 = {
 		.height = 116,
 	},
 	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
-	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
 };
 
 static const struct drm_display_mode tpk_f07a_0102_mode = {
@@ -2265,7 +2317,7 @@ static const struct panel_desc tpk_f07a_0102 = {
 		.width = 152,
 		.height = 91,
 	},
-	.bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE,
+	.bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
 };
 
 static const struct drm_display_mode tpk_f10a_0102_mode = {
@@ -2379,6 +2431,9 @@ static const struct of_device_id platform_of_match[] = {
 	}, {
 		.compatible = "auo,g070vvn01",
 		.data = &auo_g070vvn01,
+	}, {
+		.compatible = "auo,g101evn01.0",
+		.data = &auo_g101evn01_0,
 	}, {
 		.compatible = "auo,g104sn02",
 		.data = &auo_g104sn02,
@@ -2532,6 +2587,9 @@ static const struct of_device_id platform_of_match[] = {
 	}, {
 		.compatible = "ortustech,com43h4m85ulc",
 		.data = &ortustech_com43h4m85ulc,
+	}, {
+		.compatible = "osddisplays,osd070t1718-19ts",
+		.data = &osddisplays_osd070t1718_19ts,
 	}, {
 		.compatible = "qiaodian,qd43003c0-40",
 		.data = &qd43003c0_40,
@@ -2792,6 +2850,37 @@ static const struct panel_desc_dsi panasonic_vvx10f004b00 = {
 	.lanes = 4,
 };
 
+static const struct drm_display_mode osd101t2045_53ts_mode = {
+	.clock = 154500,
+	.hdisplay = 1920,
+	.hsync_start = 1920 + 112,
+	.hsync_end = 1920 + 112 + 16,
+	.htotal = 1920 + 112 + 16 + 32,
+	.vdisplay = 1200,
+	.vsync_start = 1200 + 16,
+	.vsync_end = 1200 + 16 + 2,
+	.vtotal = 1200 + 16 + 2 + 16,
+	.vrefresh = 60,
+	.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
+};
+
+static const struct panel_desc_dsi osd101t2045_53ts = {
+	.desc = {
+		.modes = &osd101t2045_53ts_mode,
+		.num_modes = 1,
+		.bpc = 8,
+		.size = {
+			.width = 217,
+			.height = 136,
+		},
+	},
+	.flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+		 MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+		 MIPI_DSI_MODE_EOT_PACKET,
+	.format = MIPI_DSI_FMT_RGB888,
+	.lanes = 4,
+};
+
 static const struct of_device_id dsi_of_match[] = {
 	{
 		.compatible = "auo,b080uan01",
@@ -2808,6 +2897,9 @@ static const struct of_device_id dsi_of_match[] = {
 	}, {
 		.compatible = "panasonic,vvx10f004b00",
 		.data = &panasonic_vvx10f004b00
+	}, {
+		.compatible = "osd,osd101t2045-53ts",
+		.data = &osd101t2045_53ts
 	}, {
 		/* sentinel */
 	}
@@ -2834,7 +2926,14 @@ static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
 	dsi->format = desc->format;
 	dsi->lanes = desc->lanes;
 
-	return mipi_dsi_attach(dsi);
+	err = mipi_dsi_attach(dsi);
+	if (err) {
+		struct panel_simple *panel = dev_get_drvdata(&dsi->dev);
+
+		drm_panel_remove(&panel->base);
+	}
+
+	return err;
 }
 
 static int panel_simple_dsi_remove(struct mipi_dsi_device *dsi)

+ 1 - 1
drivers/gpu/drm/pl111/pl111_display.c

@@ -188,7 +188,7 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
 			tim2 |= TIM2_IOE;
 
 		if (connector->display_info.bus_flags &
-		    DRM_BUS_FLAG_PIXDATA_NEGEDGE)
+		    DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
 			tim2 |= TIM2_IPC;
 	}
 

+ 0 - 2
drivers/gpu/drm/tidss/tidss_dispc.h

@@ -81,8 +81,6 @@ struct tidss_dispc_ops {
 	u64 (*read_and_clear_irqstatus)(struct dispc_device *dispc);
 	void (*write_irqenable)(struct dispc_device *dispc, u64 enable);
 
-	u32 (*get_memory_bandwidth_limit)(struct dispc_device *dispc);
-
 	int (*get_num_vps)(struct dispc_device *dispc);
 	const char *(*vp_name)(struct dispc_device *dispc,
 			       u32 hw_videoport);

+ 17 - 13
drivers/gpu/drm/tidss/tidss_dispc6.c

@@ -101,6 +101,8 @@ struct dispc_device {
 	struct clk *fclk;
 	struct clk *vp_clk;
 
+	u32 memory_bandwidth_limit;
+
 	bool is_enabled;
 
 	u32 gamma_table[DISPC6_GAMMA_TABLE_SIZE];
@@ -576,6 +578,18 @@ static enum drm_mode_status dispc6_vp_mode_valid(struct dispc_device *dispc,
 	    vbp < 0 || vbp > 4095)
 		return MODE_BAD_VVALUE;
 
+	if (dispc->memory_bandwidth_limit) {
+		const unsigned int bpp = 4;
+		u64 bandwidth;
+
+		bandwidth = 1000 * mode->clock;
+		bandwidth = bandwidth * mode->hdisplay * mode->vdisplay * bpp;
+		bandwidth = div_u64(bandwidth, mode->htotal * mode->vtotal);
+
+		if (dispc->memory_bandwidth_limit < bandwidth)
+			return MODE_BAD;
+	}
+
 	return MODE_OK;
 }
 
@@ -1230,17 +1244,6 @@ static int dispc6_init_gamma_tables(struct dispc_device *dispc)
 	return 0;
 }
 
-static u32 dispc6_get_memory_bandwidth_limit(struct dispc_device *dispc)
-{
-	u32 limit = 0;
-
-	/* Optional maximum memory bandwidth */
-	of_property_read_u32(dispc->dev->of_node, "max-memory-bandwidth",
-			     &limit);
-
-	return limit;
-}
-
 static const char *dispc6_plane_name(struct dispc_device *dispc,
 				     u32 hw_plane)
 {
@@ -1370,8 +1373,6 @@ static const struct tidss_dispc_ops dispc6_ops = {
 	.read_and_clear_irqstatus = dispc6_read_and_clear_irqstatus,
 	.write_irqenable = dispc6_write_irqenable,
 
-	.get_memory_bandwidth_limit = dispc6_get_memory_bandwidth_limit,
-
 	.get_num_vps = dispc6_get_num_vps,
 	.vp_name = dispc6_vp_name,
 	.vp_feat = dispc6_vp_feat,
@@ -1479,6 +1480,9 @@ int dispc6_init(struct tidss_device *tidss)
 		goto err_free;
 	}
 
+	of_property_read_u32(dispc->dev->of_node, "max-memory-bandwidth",
+			     &dispc->memory_bandwidth_limit);
+
 	r = dispc6_init_gamma_tables(dispc);
 	if (r)
 		goto err_free;

+ 2 - 1
drivers/gpu/drm/tve200/tve200_display.c

@@ -149,7 +149,8 @@ static void tve200_display_enable(struct drm_simple_display_pipe *pipe,
 	/* Vsync IRQ at start of Vsync at first */
 	ctrl1 |= TVE200_VSTSTYPE_VSYNC;
 
-	if (connector->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE)
+	if (connector->display_info.bus_flags &
+	    DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
 		ctrl1 |= TVE200_CTRL_TVCLKP;
 
 	if ((mode->hdisplay == 352 && mode->vdisplay == 240) || /* SIF(525) */

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно