瀏覽代碼

Merge tag 'drm-misc-next-2017-06-15' of git://anongit.freedesktop.org/git/drm-misc into drm-next

Cross-subsystem Changes:
- dt-bindings: add vendor prefix for NLT Technologies, Ltd. (Lucas)
- dt-bindings: Add support for samsung s6e3hf2 panel (Hoegeun)

Core Changes:
- Add drm_panel_bridge to avoid connector boilerplate in drivers (Eric)
- Trival fixes for dupe forward decl and reduce scope of variable (Dawid)

Driver Changes:
- dw-hdmi: Use mode_valid hook on bridge instead of connector (Jose)
- vc4,atmel-hlcdc: Use drm_panel_bridge where appropriate (Eric)
- panel: Add Innolux P079ZCA panel driver (Chris)
- panel-simple: Add NL12880B20-05, NL192108AC18-02D, P320HVN03 panels (Lucas)
- panel-samsung-s6e3ha2: Add s6e3hf2 panel support (Hoegeun)
- zte,vc4,pl111,panel,mxsfb: Miscellaneous fixes

Cc: Jose Abreu <Jose.Abreu@synopsys.com>
Cc: Eric Anholt <eric@anholt.net>
Cc: Chris Zhong <zyw@rock-chips.com>
Cc: Lucas Stach <l.stach@pengutronix.de>
Cc: Hoegeun Kwon <hoegeun.kwon@samsung.com>
Cc: Dawid Kurek <dawikur@gmail.com>

* tag 'drm-misc-next-2017-06-15' of git://anongit.freedesktop.org/git/drm-misc: (26 commits)
  drm: Reduce scope of 'state' variable
  drm: mxsfb_crtc: Reset the eLCDIF controller
  drm: Remove duplicate forward declaration
  drm/panel: s6e3ha2: Add support for s6e3hf2 panel on TM2e board
  dt-bindings: Add support for samsung s6e3hf2 panel
  drm/panel: add backlight dependency for sitronix-st7789v
  drm/panel: S6E3HA2 needs backlight code
  drm/panel: simple: add support for AUO P320HVN03
  drm/panel: simple: add support for NLT NL192108AC18-02D
  dt-bindings: add vendor prefix for NLT Technologies, Ltd.
  drm/panel: simple: add support for NEC NL12880B20-05
  drm/panel: add Innolux P079ZCA panel driver
  dt-bindings: Add INNOLUX P079ZCA panel bindings
  drm/vc4: Fix resource leak in 'vc4_get_hang_state_ioctl()' in error handling path
  drm/vc4/vc4_bo.c: always set bo->resv
  drm: Add const to name field declaration in struct drm_prop_enum_list
  drm/pl111: Fix offset calculation for the primary plane.
  drm/atmel-hlcdc: Fix panel registration
  drm/bridge: Build the panel wrapper in drm_kms_helper
  drm/atmel-hlcdc: Replace the panel usage with drm_panel_bridge.
  ...
Dave Airlie 8 年之前
父節點
當前提交
bfda9aa153
共有 34 個文件被更改,包括 944 次插入643 次删除
  1. 8 0
      Documentation/devicetree/bindings/display/panel/auo,p320hvn03.txt
  2. 23 0
      Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt
  3. 8 0
      Documentation/devicetree/bindings/display/panel/nec,nl12880b20-05.txt
  4. 8 0
      Documentation/devicetree/bindings/display/panel/nlt,nl192108ac18-02d.txt
  5. 4 1
      Documentation/devicetree/bindings/display/panel/samsung,s6e3ha2.txt
  6. 1 0
      Documentation/devicetree/bindings/vendor-prefixes.txt
  7. 6 0
      Documentation/gpu/drm-kms-helpers.rst
  8. 1 0
      drivers/gpu/drm/Makefile
  9. 15 158
      drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
  10. 9 2
      drivers/gpu/drm/bridge/Kconfig
  11. 18 139
      drivers/gpu/drm/bridge/lvds-encoder.c
  12. 200 0
      drivers/gpu/drm/bridge/panel.c
  13. 13 28
      drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
  14. 2 1
      drivers/gpu/drm/drm_atomic.c
  15. 6 4
      drivers/gpu/drm/imx/dw_hdmi-imx.c
  16. 3 2
      drivers/gpu/drm/meson/meson_dw_hdmi.c
  17. 42 0
      drivers/gpu/drm/mxsfb/mxsfb_crtc.c
  18. 13 0
      drivers/gpu/drm/panel/Kconfig
  19. 1 0
      drivers/gpu/drm/panel/Makefile
  20. 340 0
      drivers/gpu/drm/panel/panel-innolux-p079zca.c
  21. 57 7
      drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
  22. 90 0
      drivers/gpu/drm/panel/panel-simple.c
  23. 2 13
      drivers/gpu/drm/pl111/pl111_display.c
  24. 1 1
      drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
  25. 1 1
      drivers/gpu/drm/vc4/Kconfig
  26. 4 8
      drivers/gpu/drm/vc4/vc4_bo.c
  27. 30 134
      drivers/gpu/drm/vc4/vc4_dpi.c
  28. 20 134
      drivers/gpu/drm/vc4/vc4_dsi.c
  29. 8 5
      drivers/gpu/drm/vc4/vc4_gem.c
  30. 1 1
      drivers/gpu/drm/zte/zx_drm_drv.c
  31. 1 1
      include/drm/bridge/dw_hdmi.h
  32. 7 0
      include/drm/drm_bridge.h
  33. 0 2
      include/drm/drm_connector.h
  34. 1 1
      include/drm/drm_property.h

+ 8 - 0
Documentation/devicetree/bindings/display/panel/auo,p320hvn03.txt

@@ -0,0 +1,8 @@
+AU Optronics Corporation 31.5" FHD (1920x1080) TFT LCD panel
+
+Required properties:
+- compatible: should be "auo,p320hvn03"
+- power-supply: as specified in the base binding
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.

+ 23 - 0
Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt

@@ -0,0 +1,23 @@
+Innolux P079ZCA 7.85" 768x1024 TFT LCD panel
+
+Required properties:
+- compatible: should be "innolux,p079zca"
+- reg: DSI virtual channel of the peripheral
+- power-supply: phandle of the regulator that provides the supply voltage
+- enable-gpios: panel enable gpio
+
+Optional properties:
+- backlight: phandle of the backlight device attached to the panel
+
+Example:
+
+	&mipi_dsi {
+		panel {
+			compatible = "innolux,p079zca";
+			reg = <0>;
+			power-supply = <...>;
+			backlight = <&backlight>;
+			enable-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>;
+			status = "okay";
+		};
+	};

+ 8 - 0
Documentation/devicetree/bindings/display/panel/nec,nl12880b20-05.txt

@@ -0,0 +1,8 @@
+NEC LCD Technologies, Ltd. 12.1" WXGA (1280x800) LVDS TFT LCD panel
+
+Required properties:
+- compatible: should be "nec,nl12880bc20-05"
+- power-supply: as specified in the base binding
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.

+ 8 - 0
Documentation/devicetree/bindings/display/panel/nlt,nl192108ac18-02d.txt

@@ -0,0 +1,8 @@
+NLT Technologies, Ltd. 15.6" FHD (1920x1080) LVDS TFT LCD panel
+
+Required properties:
+- compatible: should be "nlt,nl192108ac18-02d"
+- power-supply: as specified in the base binding
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.

+ 4 - 1
Documentation/devicetree/bindings/display/panel/samsung,s6e3ha2.txt

@@ -1,7 +1,10 @@
 Samsung S6E3HA2 5.7" 1440x2560 AMOLED panel
+Samsung S6E3HF2 5.65" 1600x2560 AMOLED panel
 
 Required properties:
-  - compatible: "samsung,s6e3ha2"
+  - compatible: should be one of:
+    "samsung,s6e3ha2",
+    "samsung,s6e3hf2".
   - reg: the virtual channel number of a DSI peripheral
   - vdd3-supply: I/O voltage supply
   - vci-supply: voltage supply for analog circuits

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

@@ -219,6 +219,7 @@ nexbox	Nexbox
 newhaven	Newhaven Display International
 ni	National Instruments
 nintendo	Nintendo
+nlt	NLT Technologies, Ltd.
 nokia	Nokia
 nordic	Nordic Semiconductor
 nuvoton	Nuvoton Technology Corporation

+ 6 - 0
Documentation/gpu/drm-kms-helpers.rst

@@ -143,6 +143,12 @@ Bridge Helper Reference
 .. kernel-doc:: drivers/gpu/drm/drm_bridge.c
    :export:
 
+Panel-Bridge Helper Reference
+-----------------------------
+
+.. kernel-doc:: drivers/gpu/drm/bridge/panel.c
+   :export:
+
 .. _drm_panel_helper:
 
 Panel Helper Reference

+ 1 - 0
drivers/gpu/drm/Makefile

@@ -35,6 +35,7 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
 		drm_simple_kms_helper.o drm_modeset_helper.o \
 		drm_scdc_helper.o
 
+drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
 drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
 drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
 drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o

+ 15 - 158
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c

@@ -23,139 +23,17 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_of.h>
+#include <drm/drm_bridge.h>
 
 #include "atmel_hlcdc_dc.h"
 
-/**
- * Atmel HLCDC RGB connector structure
- *
- * This structure stores RGB slave device information.
- *
- * @connector: DRM connector
- * @encoder: DRM encoder
- * @dc: pointer to the atmel_hlcdc_dc structure
- * @panel: panel connected on the RGB output
- */
-struct atmel_hlcdc_rgb_output {
-	struct drm_connector connector;
-	struct drm_encoder encoder;
-	struct atmel_hlcdc_dc *dc;
-	struct drm_panel *panel;
-};
-
-static inline struct atmel_hlcdc_rgb_output *
-drm_connector_to_atmel_hlcdc_rgb_output(struct drm_connector *connector)
-{
-	return container_of(connector, struct atmel_hlcdc_rgb_output,
-			    connector);
-}
-
-static inline struct atmel_hlcdc_rgb_output *
-drm_encoder_to_atmel_hlcdc_rgb_output(struct drm_encoder *encoder)
-{
-	return container_of(encoder, struct atmel_hlcdc_rgb_output, encoder);
-}
-
-static void atmel_hlcdc_rgb_encoder_enable(struct drm_encoder *encoder)
-{
-	struct atmel_hlcdc_rgb_output *rgb =
-			drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
-
-	if (rgb->panel) {
-		drm_panel_prepare(rgb->panel);
-		drm_panel_enable(rgb->panel);
-	}
-}
-
-static void atmel_hlcdc_rgb_encoder_disable(struct drm_encoder *encoder)
-{
-	struct atmel_hlcdc_rgb_output *rgb =
-			drm_encoder_to_atmel_hlcdc_rgb_output(encoder);
-
-	if (rgb->panel) {
-		drm_panel_disable(rgb->panel);
-		drm_panel_unprepare(rgb->panel);
-	}
-}
-
-static const struct drm_encoder_helper_funcs atmel_hlcdc_panel_encoder_helper_funcs = {
-	.disable = atmel_hlcdc_rgb_encoder_disable,
-	.enable = atmel_hlcdc_rgb_encoder_enable,
-};
-
-static void atmel_hlcdc_rgb_encoder_destroy(struct drm_encoder *encoder)
-{
-	drm_encoder_cleanup(encoder);
-	memset(encoder, 0, sizeof(*encoder));
-}
-
 static const struct drm_encoder_funcs atmel_hlcdc_panel_encoder_funcs = {
-	.destroy = atmel_hlcdc_rgb_encoder_destroy,
-};
-
-static int atmel_hlcdc_panel_get_modes(struct drm_connector *connector)
-{
-	struct atmel_hlcdc_rgb_output *rgb =
-			drm_connector_to_atmel_hlcdc_rgb_output(connector);
-
-	if (rgb->panel)
-		return rgb->panel->funcs->get_modes(rgb->panel);
-
-	return 0;
-}
-
-static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector,
-				      struct drm_display_mode *mode)
-{
-	struct atmel_hlcdc_rgb_output *rgb =
-			drm_connector_to_atmel_hlcdc_rgb_output(connector);
-
-	return atmel_hlcdc_dc_mode_valid(rgb->dc, mode);
-}
-
-static const struct drm_connector_helper_funcs atmel_hlcdc_panel_connector_helper_funcs = {
-	.get_modes = atmel_hlcdc_panel_get_modes,
-	.mode_valid = atmel_hlcdc_rgb_mode_valid,
-};
-
-static enum drm_connector_status
-atmel_hlcdc_panel_connector_detect(struct drm_connector *connector, bool force)
-{
-	struct atmel_hlcdc_rgb_output *rgb =
-			drm_connector_to_atmel_hlcdc_rgb_output(connector);
-
-	if (rgb->panel)
-		return connector_status_connected;
-
-	return connector_status_disconnected;
-}
-
-static void
-atmel_hlcdc_panel_connector_destroy(struct drm_connector *connector)
-{
-	struct atmel_hlcdc_rgb_output *rgb =
-			drm_connector_to_atmel_hlcdc_rgb_output(connector);
-
-	if (rgb->panel)
-		drm_panel_detach(rgb->panel);
-
-	drm_connector_cleanup(connector);
-}
-
-static const struct drm_connector_funcs atmel_hlcdc_panel_connector_funcs = {
-	.dpms = drm_atomic_helper_connector_dpms,
-	.detect = atmel_hlcdc_panel_connector_detect,
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = atmel_hlcdc_panel_connector_destroy,
-	.reset = drm_atomic_helper_connector_reset,
-	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+	.destroy = drm_encoder_cleanup,
 };
 
 static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
 {
-	struct atmel_hlcdc_dc *dc = dev->dev_private;
-	struct atmel_hlcdc_rgb_output *output;
+	struct drm_encoder *encoder;
 	struct drm_panel *panel;
 	struct drm_bridge *bridge;
 	int ret;
@@ -165,55 +43,34 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
 	if (ret)
 		return ret;
 
-	output = devm_kzalloc(dev->dev, sizeof(*output), GFP_KERNEL);
-	if (!output)
+	encoder = devm_kzalloc(dev->dev, sizeof(*encoder), GFP_KERNEL);
+	if (!encoder)
 		return -EINVAL;
 
-	output->dc = dc;
-
-	drm_encoder_helper_add(&output->encoder,
-			       &atmel_hlcdc_panel_encoder_helper_funcs);
-	ret = drm_encoder_init(dev, &output->encoder,
+	ret = drm_encoder_init(dev, encoder,
 			       &atmel_hlcdc_panel_encoder_funcs,
 			       DRM_MODE_ENCODER_NONE, NULL);
 	if (ret)
 		return ret;
 
-	output->encoder.possible_crtcs = 0x1;
+	encoder->possible_crtcs = 0x1;
 
 	if (panel) {
-		output->connector.dpms = DRM_MODE_DPMS_OFF;
-		output->connector.polled = DRM_CONNECTOR_POLL_CONNECT;
-		drm_connector_helper_add(&output->connector,
-				&atmel_hlcdc_panel_connector_helper_funcs);
-		ret = drm_connector_init(dev, &output->connector,
-					 &atmel_hlcdc_panel_connector_funcs,
-					 DRM_MODE_CONNECTOR_Unknown);
-		if (ret)
-			goto err_encoder_cleanup;
-
-		drm_mode_connector_attach_encoder(&output->connector,
-						  &output->encoder);
-
-		ret = drm_panel_attach(panel, &output->connector);
-		if (ret) {
-			drm_connector_cleanup(&output->connector);
-			goto err_encoder_cleanup;
-		}
-
-		output->panel = panel;
-
-		return 0;
+		bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_Unknown);
+		if (IS_ERR(bridge))
+			return PTR_ERR(bridge);
 	}
 
 	if (bridge) {
-		ret = drm_bridge_attach(&output->encoder, bridge, NULL);
+		ret = drm_bridge_attach(encoder, bridge, NULL);
 		if (!ret)
 			return 0;
+
+		if (panel)
+			drm_panel_bridge_remove(bridge);
 	}
 
-err_encoder_cleanup:
-	drm_encoder_cleanup(&output->encoder);
+	drm_encoder_cleanup(encoder);
 
 	return ret;
 }

+ 9 - 2
drivers/gpu/drm/bridge/Kconfig

@@ -4,6 +4,14 @@ config DRM_BRIDGE
 	help
 	  Bridge registration and lookup framework.
 
+config DRM_PANEL_BRIDGE
+	def_bool y
+	depends on DRM_BRIDGE
+	depends on DRM_KMS_HELPER
+	select DRM_PANEL
+	help
+	  DRM bridge wrapper of DRM panels
+
 menu "Display Interface Bridges"
 	depends on DRM && DRM_BRIDGE
 
@@ -27,8 +35,7 @@ config DRM_DUMB_VGA_DAC
 config DRM_LVDS_ENCODER
 	tristate "Transparent parallel to LVDS encoder support"
 	depends on OF
-	select DRM_KMS_HELPER
-	select DRM_PANEL
+	select DRM_PANEL_BRIDGE
 	help
 	  Support for transparent parallel to LVDS encoders that don't require
 	  any configuration.

+ 18 - 139
drivers/gpu/drm/bridge/lvds-encoder.c

@@ -8,144 +8,18 @@
  */
 
 #include <drm/drmP.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_connector.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_encoder.h>
-#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_bridge.h>
 #include <drm/drm_panel.h>
 
 #include <linux/of_graph.h>
 
-struct lvds_encoder {
-	struct device *dev;
-
-	struct drm_bridge bridge;
-	struct drm_connector connector;
-	struct drm_panel *panel;
-};
-
-static inline struct lvds_encoder *
-drm_bridge_to_lvds_encoder(struct drm_bridge *bridge)
-{
-	return container_of(bridge, struct lvds_encoder, bridge);
-}
-
-static inline struct lvds_encoder *
-drm_connector_to_lvds_encoder(struct drm_connector *connector)
-{
-	return container_of(connector, struct lvds_encoder, connector);
-}
-
-static int lvds_connector_get_modes(struct drm_connector *connector)
-{
-	struct lvds_encoder *lvds = drm_connector_to_lvds_encoder(connector);
-
-	return drm_panel_get_modes(lvds->panel);
-}
-
-static const struct drm_connector_helper_funcs lvds_connector_helper_funcs = {
-	.get_modes = lvds_connector_get_modes,
-};
-
-static const struct drm_connector_funcs lvds_connector_funcs = {
-	.dpms = drm_atomic_helper_connector_dpms,
-	.reset = drm_atomic_helper_connector_reset,
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = drm_connector_cleanup,
-	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static int lvds_encoder_attach(struct drm_bridge *bridge)
-{
-	struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
-	struct drm_connector *connector = &lvds->connector;
-	int ret;
-
-	if (!bridge->encoder) {
-		DRM_ERROR("Missing encoder\n");
-		return -ENODEV;
-	}
-
-	drm_connector_helper_add(connector, &lvds_connector_helper_funcs);
-
-	ret = drm_connector_init(bridge->dev, connector, &lvds_connector_funcs,
-				 DRM_MODE_CONNECTOR_LVDS);
-	if (ret) {
-		DRM_ERROR("Failed to initialize connector\n");
-		return ret;
-	}
-
-	drm_mode_connector_attach_encoder(&lvds->connector, bridge->encoder);
-
-	ret = drm_panel_attach(lvds->panel, &lvds->connector);
-	if (ret < 0)
-		return ret;
-
-	return 0;
-}
-
-static void lvds_encoder_detach(struct drm_bridge *bridge)
-{
-	struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
-
-	drm_panel_detach(lvds->panel);
-}
-
-static void lvds_encoder_pre_enable(struct drm_bridge *bridge)
-{
-	struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
-
-	drm_panel_prepare(lvds->panel);
-}
-
-static void lvds_encoder_enable(struct drm_bridge *bridge)
-{
-	struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
-
-	drm_panel_enable(lvds->panel);
-}
-
-static void lvds_encoder_disable(struct drm_bridge *bridge)
-{
-	struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
-
-	drm_panel_disable(lvds->panel);
-}
-
-static void lvds_encoder_post_disable(struct drm_bridge *bridge)
-{
-	struct lvds_encoder *lvds = drm_bridge_to_lvds_encoder(bridge);
-
-	drm_panel_unprepare(lvds->panel);
-}
-
-static const struct drm_bridge_funcs lvds_encoder_bridge_funcs = {
-	.attach = lvds_encoder_attach,
-	.detach = lvds_encoder_detach,
-	.pre_enable = lvds_encoder_pre_enable,
-	.enable = lvds_encoder_enable,
-	.disable = lvds_encoder_disable,
-	.post_disable = lvds_encoder_post_disable,
-};
-
 static int lvds_encoder_probe(struct platform_device *pdev)
 {
-	struct lvds_encoder *lvds;
 	struct device_node *port;
 	struct device_node *endpoint;
-	struct device_node *panel;
-
-	lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
-	if (!lvds)
-		return -ENOMEM;
-
-	lvds->dev = &pdev->dev;
-	platform_set_drvdata(pdev, lvds);
-
-	lvds->bridge.funcs = &lvds_encoder_bridge_funcs;
-	lvds->bridge.of_node = pdev->dev.of_node;
+	struct device_node *panel_node;
+	struct drm_panel *panel;
+	struct drm_bridge *bridge;
 
 	/* Locate the panel DT node. */
 	port = of_graph_get_port_by_id(pdev->dev.of_node, 1);
@@ -161,29 +35,34 @@ static int lvds_encoder_probe(struct platform_device *pdev)
 		return -ENXIO;
 	}
 
-	panel = of_graph_get_remote_port_parent(endpoint);
+	panel_node = of_graph_get_remote_port_parent(endpoint);
 	of_node_put(endpoint);
-	if (!panel) {
+	if (!panel_node) {
 		dev_dbg(&pdev->dev, "no remote endpoint for port 1\n");
 		return -ENXIO;
 	}
 
-	lvds->panel = of_drm_find_panel(panel);
-	of_node_put(panel);
-	if (!lvds->panel) {
+	panel = of_drm_find_panel(panel_node);
+	of_node_put(panel_node);
+	if (!panel) {
 		dev_dbg(&pdev->dev, "panel not found, deferring probe\n");
 		return -EPROBE_DEFER;
 	}
 
-	/* Register the bridge. */
-	return drm_bridge_add(&lvds->bridge);
+	bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_LVDS);
+	if (IS_ERR(bridge))
+		return PTR_ERR(bridge);
+
+	platform_set_drvdata(pdev, bridge);
+
+	return 0;
 }
 
 static int lvds_encoder_remove(struct platform_device *pdev)
 {
-	struct lvds_encoder *encoder = platform_get_drvdata(pdev);
+	struct drm_bridge *bridge = platform_get_drvdata(pdev);
 
-	drm_bridge_remove(&encoder->bridge);
+	drm_bridge_remove(bridge);
 
 	return 0;
 }

+ 200 - 0
drivers/gpu/drm/bridge/panel.c

@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Copyright (C) 2017 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_panel.h>
+
+struct panel_bridge {
+	struct drm_bridge bridge;
+	struct drm_connector connector;
+	struct drm_panel *panel;
+	u32 connector_type;
+};
+
+static inline struct panel_bridge *
+drm_bridge_to_panel_bridge(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct panel_bridge, bridge);
+}
+
+static inline struct panel_bridge *
+drm_connector_to_panel_bridge(struct drm_connector *connector)
+{
+	return container_of(connector, struct panel_bridge, connector);
+}
+
+static int panel_bridge_connector_get_modes(struct drm_connector *connector)
+{
+	struct panel_bridge *panel_bridge =
+		drm_connector_to_panel_bridge(connector);
+
+	return drm_panel_get_modes(panel_bridge->panel);
+}
+
+static const struct drm_connector_helper_funcs
+panel_bridge_connector_helper_funcs = {
+	.get_modes = panel_bridge_connector_get_modes,
+};
+
+static const struct drm_connector_funcs panel_bridge_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = drm_connector_cleanup,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int panel_bridge_attach(struct drm_bridge *bridge)
+{
+	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+	struct drm_connector *connector = &panel_bridge->connector;
+	int ret;
+
+	if (!bridge->encoder) {
+		DRM_ERROR("Missing encoder\n");
+		return -ENODEV;
+	}
+
+	drm_connector_helper_add(connector,
+				 &panel_bridge_connector_helper_funcs);
+
+	ret = drm_connector_init(bridge->dev, connector,
+				 &panel_bridge_connector_funcs,
+				 panel_bridge->connector_type);
+	if (ret) {
+		DRM_ERROR("Failed to initialize connector\n");
+		return ret;
+	}
+
+	drm_mode_connector_attach_encoder(&panel_bridge->connector,
+					  bridge->encoder);
+
+	ret = drm_panel_attach(panel_bridge->panel, &panel_bridge->connector);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void panel_bridge_detach(struct drm_bridge *bridge)
+{
+	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+
+	drm_panel_detach(panel_bridge->panel);
+}
+
+static void panel_bridge_pre_enable(struct drm_bridge *bridge)
+{
+	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+
+	drm_panel_prepare(panel_bridge->panel);
+}
+
+static void panel_bridge_enable(struct drm_bridge *bridge)
+{
+	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+
+	drm_panel_enable(panel_bridge->panel);
+}
+
+static void panel_bridge_disable(struct drm_bridge *bridge)
+{
+	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+
+	drm_panel_disable(panel_bridge->panel);
+}
+
+static void panel_bridge_post_disable(struct drm_bridge *bridge)
+{
+	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+
+	drm_panel_unprepare(panel_bridge->panel);
+}
+
+static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
+	.attach = panel_bridge_attach,
+	.detach = panel_bridge_detach,
+	.pre_enable = panel_bridge_pre_enable,
+	.enable = panel_bridge_enable,
+	.disable = panel_bridge_disable,
+	.post_disable = panel_bridge_post_disable,
+};
+
+/**
+ * drm_panel_bridge_add - Creates a drm_bridge and drm_connector that
+ * just calls the appropriate functions from drm_panel.
+ *
+ * @panel: The drm_panel being wrapped.  Must be non-NULL.
+ * @connector_type: The DRM_MODE_CONNECTOR_* for the connector to be
+ * created.
+ *
+ * For drivers converting from directly using drm_panel: The expected
+ * usage pattern is that during either encoder module probe or DSI
+ * host attach, a drm_panel will be looked up through
+ * drm_of_find_panel_or_bridge().  drm_panel_bridge_add() is used to
+ * wrap that panel in the new bridge, and the result can then be
+ * passed to drm_bridge_attach().  The drm_panel_prepare() and related
+ * functions can be dropped from the encoder driver (they're now
+ * called by the KMS helpers before calling into the encoder), along
+ * with connector creation.  When done with the bridge,
+ * drm_bridge_detach() should be called as normal, then
+ * drm_panel_bridge_remove() to free it.
+ */
+struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel,
+					u32 connector_type)
+{
+	struct panel_bridge *panel_bridge;
+	int ret;
+
+	if (!panel)
+		return ERR_PTR(EINVAL);
+
+	panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge),
+				    GFP_KERNEL);
+	if (!panel_bridge)
+		return ERR_PTR(-ENOMEM);
+
+	panel_bridge->connector_type = connector_type;
+	panel_bridge->panel = panel;
+
+	panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs;
+#ifdef CONFIG_OF
+	panel_bridge->bridge.of_node = panel->dev->of_node;
+#endif
+
+	ret = drm_bridge_add(&panel_bridge->bridge);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return &panel_bridge->bridge;
+}
+EXPORT_SYMBOL(drm_panel_bridge_add);
+
+/**
+ * drm_panel_bridge_remove - Unregisters and frees a drm_bridge
+ * created by drm_panel_bridge_add().
+ *
+ * @bridge: The drm_bridge being freed.
+ */
+void drm_panel_bridge_remove(struct drm_bridge *bridge)
+{
+	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+
+	drm_bridge_remove(bridge);
+	devm_kfree(panel_bridge->panel->dev, bridge);
+}
+EXPORT_SYMBOL(drm_panel_bridge_remove);

+ 13 - 28
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c

@@ -1907,24 +1907,6 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
 	return ret;
 }
 
-static enum drm_mode_status
-dw_hdmi_connector_mode_valid(struct drm_connector *connector,
-			     struct drm_display_mode *mode)
-{
-	struct dw_hdmi *hdmi = container_of(connector,
-					   struct dw_hdmi, connector);
-	enum drm_mode_status mode_status = MODE_OK;
-
-	/* We don't support double-clocked modes */
-	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
-		return MODE_BAD;
-
-	if (hdmi->plat_data->mode_valid)
-		mode_status = hdmi->plat_data->mode_valid(connector, mode);
-
-	return mode_status;
-}
-
 static void dw_hdmi_connector_force(struct drm_connector *connector)
 {
 	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
@@ -1950,7 +1932,6 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
 
 static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
 	.get_modes = dw_hdmi_connector_get_modes,
-	.mode_valid = dw_hdmi_connector_mode_valid,
 	.best_encoder = drm_atomic_helper_best_encoder,
 };
 
@@ -1973,18 +1954,22 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
 	return 0;
 }
 
-static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
-				      const struct drm_display_mode *orig_mode,
-				      struct drm_display_mode *mode)
+static enum drm_mode_status
+dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
+			  const struct drm_display_mode *mode)
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
 	struct drm_connector *connector = &hdmi->connector;
-	enum drm_mode_status status;
+	enum drm_mode_status mode_status = MODE_OK;
 
-	status = dw_hdmi_connector_mode_valid(connector, mode);
-	if (status != MODE_OK)
-		return false;
-	return true;
+	/* We don't support double-clocked modes */
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+		return MODE_BAD;
+
+	if (hdmi->plat_data->mode_valid)
+		mode_status = hdmi->plat_data->mode_valid(connector, mode);
+
+	return mode_status;
 }
 
 static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
@@ -2028,7 +2013,7 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
 	.enable = dw_hdmi_bridge_enable,
 	.disable = dw_hdmi_bridge_disable,
 	.mode_set = dw_hdmi_bridge_mode_set,
-	.mode_fixup = dw_hdmi_bridge_mode_fixup,
+	.mode_valid = dw_hdmi_bridge_mode_valid,
 };
 
 static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi)

+ 2 - 1
drivers/gpu/drm/drm_atomic.c

@@ -109,9 +109,10 @@ struct drm_atomic_state *
 drm_atomic_state_alloc(struct drm_device *dev)
 {
 	struct drm_mode_config *config = &dev->mode_config;
-	struct drm_atomic_state *state;
 
 	if (!config->funcs->atomic_state_alloc) {
+		struct drm_atomic_state *state;
+
 		state = kzalloc(sizeof(*state), GFP_KERNEL);
 		if (!state)
 			return NULL;

+ 6 - 4
drivers/gpu/drm/imx/dw_hdmi-imx.c

@@ -147,8 +147,9 @@ static const struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = {
 	.destroy = drm_encoder_cleanup,
 };
 
-static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con,
-						  struct drm_display_mode *mode)
+static enum drm_mode_status
+imx6q_hdmi_mode_valid(struct drm_connector *con,
+		      const struct drm_display_mode *mode)
 {
 	if (mode->clock < 13500)
 		return MODE_CLOCK_LOW;
@@ -159,8 +160,9 @@ static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con,
 	return MODE_OK;
 }
 
-static enum drm_mode_status imx6dl_hdmi_mode_valid(struct drm_connector *con,
-						   struct drm_display_mode *mode)
+static enum drm_mode_status
+imx6dl_hdmi_mode_valid(struct drm_connector *con,
+		       const struct drm_display_mode *mode)
 {
 	if (mode->clock < 13500)
 		return MODE_CLOCK_LOW;

+ 3 - 2
drivers/gpu/drm/meson/meson_dw_hdmi.c

@@ -536,8 +536,9 @@ static irqreturn_t dw_hdmi_top_thread_irq(int irq, void *dev_id)
 }
 
 /* TOFIX Enable support for non-vic modes */
-static enum drm_mode_status dw_hdmi_mode_valid(struct drm_connector *connector,
-					       struct drm_display_mode *mode)
+static enum drm_mode_status
+dw_hdmi_mode_valid(struct drm_connector *connector,
+		   const struct drm_display_mode *mode)
 {
 	unsigned int vclk_freq;
 	unsigned int venc_freq;

+ 42 - 0
drivers/gpu/drm/mxsfb/mxsfb_crtc.c

@@ -35,6 +35,13 @@
 #include "mxsfb_drv.h"
 #include "mxsfb_regs.h"
 
+#define MXS_SET_ADDR		0x4
+#define MXS_CLR_ADDR		0x8
+#define MODULE_CLKGATE		BIT(30)
+#define MODULE_SFTRST		BIT(31)
+/* 1 second delay should be plenty of time for block reset */
+#define RESET_TIMEOUT		1000000
+
 static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val)
 {
 	return (val & mxsfb->devdata->hs_wdth_mask) <<
@@ -159,6 +166,36 @@ static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
 		clk_disable_unprepare(mxsfb->clk_disp_axi);
 }
 
+/*
+ * Clear the bit and poll it cleared.  This is usually called with
+ * a reset address and mask being either SFTRST(bit 31) or CLKGATE
+ * (bit 30).
+ */
+static int clear_poll_bit(void __iomem *addr, u32 mask)
+{
+	u32 reg;
+
+	writel(mask, addr + MXS_CLR_ADDR);
+	return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT);
+}
+
+static int mxsfb_reset_block(void __iomem *reset_addr)
+{
+	int ret;
+
+	ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
+	if (ret)
+		return ret;
+
+	writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR);
+
+	ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
+	if (ret)
+		return ret;
+
+	return clear_poll_bit(reset_addr, MODULE_CLKGATE);
+}
+
 static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
 {
 	struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode;
@@ -173,6 +210,11 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
 	 */
 	mxsfb_enable_axi_clk(mxsfb);
 
+	/* Mandatory eLCDIF reset as per the Reference Manual */
+	err = mxsfb_reset_block(mxsfb->base);
+	if (err)
+		return;
+
 	/* Clear the FIFOs */
 	writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
 

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

@@ -28,6 +28,17 @@ config DRM_PANEL_SIMPLE
 	  that it can be automatically turned off when the panel goes into a
 	  low power state.
 
+config DRM_PANEL_INNOLUX_P079ZCA
+	tristate "Innolux P079ZCA panel"
+	depends on OF
+	depends on DRM_MIPI_DSI
+	depends on BACKLIGHT_CLASS_DEVICE
+	help
+	  Say Y here if you want to enable support for Innolux P079ZCA
+	  TFT-LCD modules. The panel has a 1024x768 resolution and uses
+	  24 bit RGB per pixel. It provides a MIPI DSI interface to
+	  the host and has a built-in LED backlight.
+
 config DRM_PANEL_JDI_LT070ME05000
 	tristate "JDI LT070ME05000 WUXGA DSI panel"
 	depends on OF
@@ -66,6 +77,7 @@ config DRM_PANEL_SAMSUNG_S6E3HA2
 	tristate "Samsung S6E3HA2 DSI video mode panel"
 	depends on OF
 	depends on DRM_MIPI_DSI
+	depends on BACKLIGHT_CLASS_DEVICE
 	select VIDEOMODE_HELPERS
 
 config DRM_PANEL_SAMSUNG_S6E8AA0
@@ -100,6 +112,7 @@ config DRM_PANEL_SHARP_LS043T1LE01
 config DRM_PANEL_SITRONIX_ST7789V
 	tristate "Sitronix ST7789V panel"
 	depends on OF && SPI
+	depends on BACKLIGHT_CLASS_DEVICE
 	help
 	  Say Y here if you want to enable support for the Sitronix
 	  ST7789V controller for 240x320 LCD panels

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

@@ -1,5 +1,6 @@
 obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o
 obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
+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_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o

+ 340 - 0
drivers/gpu/drm/panel/panel-innolux-p079zca.c

@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+struct innolux_panel {
+	struct drm_panel base;
+	struct mipi_dsi_device *link;
+
+	struct backlight_device *backlight;
+	struct regulator *supply;
+	struct gpio_desc *enable_gpio;
+
+	bool prepared;
+	bool enabled;
+};
+
+static inline struct innolux_panel *to_innolux_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct innolux_panel, base);
+}
+
+static int innolux_panel_disable(struct drm_panel *panel)
+{
+	struct innolux_panel *innolux = to_innolux_panel(panel);
+	int err;
+
+	if (!innolux->enabled)
+		return 0;
+
+	innolux->backlight->props.power = FB_BLANK_POWERDOWN;
+	backlight_update_status(innolux->backlight);
+
+	err = mipi_dsi_dcs_set_display_off(innolux->link);
+	if (err < 0)
+		DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
+			      err);
+
+	innolux->enabled = false;
+
+	return 0;
+}
+
+static int innolux_panel_unprepare(struct drm_panel *panel)
+{
+	struct innolux_panel *innolux = to_innolux_panel(panel);
+	int err;
+
+	if (!innolux->prepared)
+		return 0;
+
+	err = mipi_dsi_dcs_enter_sleep_mode(innolux->link);
+	if (err < 0) {
+		DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
+			      err);
+		return err;
+	}
+
+	gpiod_set_value_cansleep(innolux->enable_gpio, 0);
+
+	/* T8: 80ms - 1000ms */
+	msleep(80);
+
+	err = regulator_disable(innolux->supply);
+	if (err < 0)
+		return err;
+
+	innolux->prepared = false;
+
+	return 0;
+}
+
+static int innolux_panel_prepare(struct drm_panel *panel)
+{
+	struct innolux_panel *innolux = to_innolux_panel(panel);
+	int err, regulator_err;
+
+	if (innolux->prepared)
+		return 0;
+
+	gpiod_set_value_cansleep(innolux->enable_gpio, 0);
+
+	err = regulator_enable(innolux->supply);
+	if (err < 0)
+		return err;
+
+	/* T2: 15ms - 1000ms */
+	usleep_range(15000, 16000);
+
+	gpiod_set_value_cansleep(innolux->enable_gpio, 1);
+
+	/* T4: 15ms - 1000ms */
+	usleep_range(15000, 16000);
+
+	err = mipi_dsi_dcs_exit_sleep_mode(innolux->link);
+	if (err < 0) {
+		DRM_DEV_ERROR(panel->dev, "failed to exit sleep mode: %d\n",
+			      err);
+		goto poweroff;
+	}
+
+	/* T6: 120ms - 1000ms*/
+	msleep(120);
+
+	err = mipi_dsi_dcs_set_display_on(innolux->link);
+	if (err < 0) {
+		DRM_DEV_ERROR(panel->dev, "failed to set display on: %d\n",
+			      err);
+		goto poweroff;
+	}
+
+	/* T7: 5ms */
+	usleep_range(5000, 6000);
+
+	innolux->prepared = true;
+
+	return 0;
+
+poweroff:
+	regulator_err = regulator_disable(innolux->supply);
+	if (regulator_err)
+		DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n",
+			      regulator_err);
+
+	gpiod_set_value_cansleep(innolux->enable_gpio, 0);
+	return err;
+}
+
+static int innolux_panel_enable(struct drm_panel *panel)
+{
+	struct innolux_panel *innolux = to_innolux_panel(panel);
+	int ret;
+
+	if (innolux->enabled)
+		return 0;
+
+	innolux->backlight->props.power = FB_BLANK_UNBLANK;
+	ret = backlight_update_status(innolux->backlight);
+	if (ret) {
+		DRM_DEV_ERROR(panel->drm->dev,
+			      "Failed to enable backlight %d\n", ret);
+		return ret;
+	}
+
+	innolux->enabled = true;
+
+	return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+	.clock = 56900,
+	.hdisplay = 768,
+	.hsync_start = 768 + 40,
+	.hsync_end = 768 + 40 + 40,
+	.htotal = 768 + 40 + 40 + 40,
+	.vdisplay = 1024,
+	.vsync_start = 1024 + 20,
+	.vsync_end = 1024 + 20 + 4,
+	.vtotal = 1024 + 20 + 4 + 20,
+	.vrefresh = 60,
+};
+
+static int innolux_panel_get_modes(struct drm_panel *panel)
+{
+	struct drm_display_mode *mode;
+
+	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	if (!mode) {
+		DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n",
+			      default_mode.hdisplay, default_mode.vdisplay,
+			      default_mode.vrefresh);
+		return -ENOMEM;
+	}
+
+	drm_mode_set_name(mode);
+
+	drm_mode_probed_add(panel->connector, mode);
+
+	panel->connector->display_info.width_mm = 120;
+	panel->connector->display_info.height_mm = 160;
+	panel->connector->display_info.bpc = 8;
+
+	return 1;
+}
+
+static const struct drm_panel_funcs innolux_panel_funcs = {
+	.disable = innolux_panel_disable,
+	.unprepare = innolux_panel_unprepare,
+	.prepare = innolux_panel_prepare,
+	.enable = innolux_panel_enable,
+	.get_modes = innolux_panel_get_modes,
+};
+
+static const struct of_device_id innolux_of_match[] = {
+	{ .compatible = "innolux,p079zca", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, innolux_of_match);
+
+static int innolux_panel_add(struct innolux_panel *innolux)
+{
+	struct device *dev = &innolux->link->dev;
+	struct device_node *np;
+	int err;
+
+	innolux->supply = devm_regulator_get(dev, "power");
+	if (IS_ERR(innolux->supply))
+		return PTR_ERR(innolux->supply);
+
+	innolux->enable_gpio = devm_gpiod_get_optional(dev, "enable",
+						       GPIOD_OUT_HIGH);
+	if (IS_ERR(innolux->enable_gpio)) {
+		err = PTR_ERR(innolux->enable_gpio);
+		dev_dbg(dev, "failed to get enable gpio: %d\n", err);
+		innolux->enable_gpio = NULL;
+	}
+
+	np = of_parse_phandle(dev->of_node, "backlight", 0);
+	if (np) {
+		innolux->backlight = of_find_backlight_by_node(np);
+		of_node_put(np);
+
+		if (!innolux->backlight)
+			return -EPROBE_DEFER;
+	}
+
+	drm_panel_init(&innolux->base);
+	innolux->base.funcs = &innolux_panel_funcs;
+	innolux->base.dev = &innolux->link->dev;
+
+	err = drm_panel_add(&innolux->base);
+	if (err < 0)
+		goto put_backlight;
+
+	return 0;
+
+put_backlight:
+	put_device(&innolux->backlight->dev);
+
+	return err;
+}
+
+static void innolux_panel_del(struct innolux_panel *innolux)
+{
+	if (innolux->base.dev)
+		drm_panel_remove(&innolux->base);
+
+	put_device(&innolux->backlight->dev);
+}
+
+static int innolux_panel_probe(struct mipi_dsi_device *dsi)
+{
+	struct innolux_panel *innolux;
+	int err;
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+			  MIPI_DSI_MODE_LPM;
+
+	innolux = devm_kzalloc(&dsi->dev, sizeof(*innolux), GFP_KERNEL);
+	if (!innolux)
+		return -ENOMEM;
+
+	mipi_dsi_set_drvdata(dsi, innolux);
+
+	innolux->link = dsi;
+
+	err = innolux_panel_add(innolux);
+	if (err < 0)
+		return err;
+
+	err = mipi_dsi_attach(dsi);
+	return err;
+}
+
+static int innolux_panel_remove(struct mipi_dsi_device *dsi)
+{
+	struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
+	int err;
+
+	err = innolux_panel_unprepare(&innolux->base);
+	if (err < 0)
+		DRM_DEV_ERROR(&dsi->dev, "failed to unprepare panel: %d\n",
+			      err);
+
+	err = innolux_panel_disable(&innolux->base);
+	if (err < 0)
+		DRM_DEV_ERROR(&dsi->dev, "failed to disable panel: %d\n", err);
+
+	err = mipi_dsi_detach(dsi);
+	if (err < 0)
+		DRM_DEV_ERROR(&dsi->dev, "failed to detach from DSI host: %d\n",
+			      err);
+
+	drm_panel_detach(&innolux->base);
+	innolux_panel_del(innolux);
+
+	return 0;
+}
+
+static void innolux_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+	struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi);
+
+	innolux_panel_unprepare(&innolux->base);
+	innolux_panel_disable(&innolux->base);
+}
+
+static struct mipi_dsi_driver innolux_panel_driver = {
+	.driver = {
+		.name = "panel-innolux-p079zca",
+		.of_match_table = innolux_of_match,
+	},
+	.probe = innolux_panel_probe,
+	.remove = innolux_panel_remove,
+	.shutdown = innolux_panel_shutdown,
+};
+module_mipi_dsi_driver(innolux_panel_driver);
+
+MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
+MODULE_DESCRIPTION("Innolux P079ZCA panel driver");
+MODULE_LICENSE("GPL v2");

+ 57 - 7
drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c

@@ -16,6 +16,7 @@
 #include <drm/drm_panel.h>
 #include <linux/backlight.h>
 #include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
 #include <linux/regulator/consumer.h>
 
 #define S6E3HA2_MIN_BRIGHTNESS		0
@@ -218,6 +219,16 @@ unsigned char vint_table[S6E3HA2_VINT_STATUS_MAX] = {
 	0x1d, 0x1e, 0x1f, 0x20, 0x21
 };
 
+enum s6e3ha2_type {
+	HA2_TYPE,
+	HF2_TYPE,
+};
+
+struct s6e3ha2_panel_desc {
+	const struct drm_display_mode *mode;
+	enum s6e3ha2_type type;
+};
+
 struct s6e3ha2 {
 	struct device *dev;
 	struct drm_panel panel;
@@ -226,6 +237,8 @@ struct s6e3ha2 {
 	struct regulator_bulk_data supplies[2];
 	struct gpio_desc *reset_gpio;
 	struct gpio_desc *enable_gpio;
+
+	const struct s6e3ha2_panel_desc *desc;
 };
 
 static int s6e3ha2_dcs_write(struct s6e3ha2 *ctx, const void *data, size_t len)
@@ -283,11 +296,21 @@ static int s6e3ha2_single_dsi_set(struct s6e3ha2 *ctx)
 static int s6e3ha2_freq_calibration(struct s6e3ha2 *ctx)
 {
 	s6e3ha2_dcs_write_seq_static(ctx, 0xfd, 0x1c);
+	if (ctx->desc->type == HF2_TYPE)
+		s6e3ha2_dcs_write_seq_static(ctx, 0xf2, 0x67, 0x40, 0xc5);
 	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20, 0x39);
 	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0xa0);
 	s6e3ha2_dcs_write_seq_static(ctx, 0xfe, 0x20);
-	s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x12, 0x62, 0x40,
-				0x80, 0xc0, 0x28, 0x28, 0x28, 0x28, 0x39, 0xc5);
+
+	if (ctx->desc->type == HA2_TYPE)
+		s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x12, 0x62,
+						  0x40, 0x80, 0xc0, 0x28, 0x28,
+						  0x28, 0x28, 0x39, 0xc5);
+	else
+		s6e3ha2_dcs_write_seq_static(ctx, 0xce, 0x03, 0x3b, 0x14, 0x6d,
+						  0x40, 0x80, 0xc0, 0x28, 0x28,
+						  0x28, 0x28, 0x39, 0xc5);
+
 	return 0;
 }
 
@@ -583,7 +606,7 @@ static int s6e3ha2_enable(struct drm_panel *panel)
 	return 0;
 }
 
-static const struct drm_display_mode default_mode = {
+static const struct drm_display_mode s6e3ha2_mode = {
 	.clock = 222372,
 	.hdisplay = 1440,
 	.hsync_start = 1440 + 1,
@@ -597,16 +620,41 @@ static const struct drm_display_mode default_mode = {
 	.flags = 0,
 };
 
+static const struct s6e3ha2_panel_desc samsung_s6e3ha2 = {
+	.mode = &s6e3ha2_mode,
+	.type = HA2_TYPE,
+};
+
+static const struct drm_display_mode s6e3hf2_mode = {
+	.clock = 247856,
+	.hdisplay = 1600,
+	.hsync_start = 1600 + 1,
+	.hsync_end = 1600 + 1 + 1,
+	.htotal = 1600 + 1 + 1 + 1,
+	.vdisplay = 2560,
+	.vsync_start = 2560 + 1,
+	.vsync_end = 2560 + 1 + 1,
+	.vtotal = 2560 + 1 + 1 + 15,
+	.vrefresh = 60,
+	.flags = 0,
+};
+
+static const struct s6e3ha2_panel_desc samsung_s6e3hf2 = {
+	.mode = &s6e3hf2_mode,
+	.type = HF2_TYPE,
+};
+
 static int s6e3ha2_get_modes(struct drm_panel *panel)
 {
 	struct drm_connector *connector = panel->connector;
+	struct s6e3ha2 *ctx = container_of(panel, struct s6e3ha2, panel);
 	struct drm_display_mode *mode;
 
-	mode = drm_mode_duplicate(panel->drm, &default_mode);
+	mode = drm_mode_duplicate(panel->drm, ctx->desc->mode);
 	if (!mode) {
 		DRM_ERROR("failed to add mode %ux%ux@%u\n",
-				default_mode.hdisplay, default_mode.vdisplay,
-				default_mode.vrefresh);
+			ctx->desc->mode->hdisplay, ctx->desc->mode->vdisplay,
+			ctx->desc->mode->vrefresh);
 		return -ENOMEM;
 	}
 
@@ -642,6 +690,7 @@ static int s6e3ha2_probe(struct mipi_dsi_device *dsi)
 	mipi_dsi_set_drvdata(dsi, ctx);
 
 	ctx->dev = dev;
+	ctx->desc = of_device_get_match_data(dev);
 
 	dsi->lanes = 4;
 	dsi->format = MIPI_DSI_FMT_RGB888;
@@ -717,7 +766,8 @@ static int s6e3ha2_remove(struct mipi_dsi_device *dsi)
 }
 
 static const struct of_device_id s6e3ha2_of_match[] = {
-	{ .compatible = "samsung,s6e3ha2" },
+	{ .compatible = "samsung,s6e3ha2", .data = &samsung_s6e3ha2 },
+	{ .compatible = "samsung,s6e3hf2", .data = &samsung_s6e3hf2 },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, s6e3ha2_of_match);

+ 90 - 0
drivers/gpu/drm/panel/panel-simple.c

@@ -638,6 +638,34 @@ static const struct panel_desc auo_g185han01 = {
 	.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
 };
 
+static const struct display_timing auo_p320hvn03_timings = {
+	.pixelclock = { 106000000, 148500000, 164000000 },
+	.hactive = { 1920, 1920, 1920 },
+	.hfront_porch = { 25, 50, 130 },
+	.hback_porch = { 25, 50, 130 },
+	.hsync_len = { 20, 40, 105 },
+	.vactive = { 1080, 1080, 1080 },
+	.vfront_porch = { 8, 17, 150 },
+	.vback_porch = { 8, 17, 150 },
+	.vsync_len = { 4, 11, 100 },
+};
+
+static const struct panel_desc auo_p320hvn03 = {
+	.timings = &auo_p320hvn03_timings,
+	.num_timings = 1,
+	.bpc = 8,
+	.size = {
+		.width = 698,
+		.height = 393,
+	},
+	.delay = {
+		.prepare = 1,
+		.enable = 450,
+		.unprepare = 500,
+	},
+	.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,
+};
+
 static const struct drm_display_mode auo_t215hvn01_mode = {
 	.clock = 148800,
 	.hdisplay = 1920,
@@ -1322,6 +1350,33 @@ static const struct panel_desc lg_lp129qe = {
 	},
 };
 
+static const struct display_timing nec_nl12880bc20_05_timing = {
+	.pixelclock = { 67000000, 71000000, 75000000 },
+	.hactive = { 1280, 1280, 1280 },
+	.hfront_porch = { 2, 30, 30 },
+	.hback_porch = { 6, 100, 100 },
+	.hsync_len = { 2, 30, 30 },
+	.vactive = { 800, 800, 800 },
+	.vfront_porch = { 5, 5, 5 },
+	.vback_porch = { 11, 11, 11 },
+	.vsync_len = { 7, 7, 7 },
+};
+
+static const struct panel_desc nec_nl12880bc20_05 = {
+	.timings = &nec_nl12880bc20_05_timing,
+	.num_timings = 1,
+	.bpc = 8,
+	.size = {
+		.width = 261,
+		.height = 163,
+	},
+	.delay = {
+		.enable = 50,
+		.disable = 50,
+	},
+	.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+};
+
 static const struct drm_display_mode nec_nl4827hc19_05b_mode = {
 	.clock = 10870,
 	.hdisplay = 480,
@@ -1371,6 +1426,32 @@ static const struct panel_desc netron_dy_e231732 = {
 	.bus_format = MEDIA_BUS_FMT_RGB666_1X18,
 };
 
+static const struct display_timing nlt_nl192108ac18_02d_timing = {
+	.pixelclock = { 130000000, 148350000, 163000000 },
+	.hactive = { 1920, 1920, 1920 },
+	.hfront_porch = { 80, 100, 100 },
+	.hback_porch = { 100, 120, 120 },
+	.hsync_len = { 50, 60, 60 },
+	.vactive = { 1080, 1080, 1080 },
+	.vfront_porch = { 12, 30, 30 },
+	.vback_porch = { 4, 10, 10 },
+	.vsync_len = { 4, 5, 5 },
+};
+
+static const struct panel_desc nlt_nl192108ac18_02d = {
+	.timings = &nlt_nl192108ac18_02d_timing,
+	.num_timings = 1,
+	.bpc = 8,
+	.size = {
+		.width = 344,
+		.height = 194,
+	},
+	.delay = {
+		.unprepare = 500,
+	},
+	.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+};
+
 static const struct drm_display_mode nvd_9128_mode = {
 	.clock = 29500,
 	.hdisplay = 800,
@@ -1887,6 +1968,9 @@ static const struct of_device_id platform_of_match[] = {
 	}, {
 		.compatible = "auo,g185han01",
 		.data = &auo_g185han01,
+	}, {
+		.compatible = "auo,p320hvn03",
+		.data = &auo_p320hvn03,
 	}, {
 		.compatible = "auo,t215hvn01",
 		.data = &auo_t215hvn01,
@@ -1971,12 +2055,18 @@ static const struct of_device_id platform_of_match[] = {
 	}, {
 		.compatible = "lg,lp129qe",
 		.data = &lg_lp129qe,
+	}, {
+		.compatible = "nec,nl12880bc20-05",
+		.data = &nec_nl12880bc20_05,
 	}, {
 		.compatible = "nec,nl4827hc19-05b",
 		.data = &nec_nl4827hc19_05b,
 	}, {
 		.compatible = "netron-dy,e231732",
 		.data = &netron_dy_e231732,
+	}, {
+		.compatible = "nlt,nl192108ac18-02d",
+		.data = &nlt_nl192108ac18_02d,
 	}, {
 		.compatible = "nvd,9128",
 		.data = &nvd_9128,

+ 2 - 13
drivers/gpu/drm/pl111/pl111_display.c

@@ -50,17 +50,6 @@ irqreturn_t pl111_irq(int irq, void *data)
 	return status;
 }
 
-static u32 pl111_get_fb_offset(struct drm_plane_state *pstate)
-{
-	struct drm_framebuffer *fb = pstate->fb;
-	struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0);
-
-	return (obj->paddr +
-		fb->offsets[0] +
-		fb->format->cpp[0] * pstate->src_x +
-		fb->pitches[0] * pstate->src_y);
-}
-
 static int pl111_display_check(struct drm_simple_display_pipe *pipe,
 			       struct drm_plane_state *pstate,
 			       struct drm_crtc_state *cstate)
@@ -73,7 +62,7 @@ static int pl111_display_check(struct drm_simple_display_pipe *pipe,
 		return -EINVAL;
 
 	if (fb) {
-		u32 offset = pl111_get_fb_offset(pstate);
+		u32 offset = drm_fb_cma_get_gem_addr(fb, pstate, 0);
 
 		/* FB base address must be dword aligned. */
 		if (offset & 3)
@@ -249,7 +238,7 @@ static void pl111_display_update(struct drm_simple_display_pipe *pipe,
 	struct drm_framebuffer *fb = pstate->fb;
 
 	if (fb) {
-		u32 addr = pl111_get_fb_offset(pstate);
+		u32 addr = drm_fb_cma_get_gem_addr(fb, pstate, 0);
 
 		writel(addr, priv->regs + CLCD_UBAS);
 	}

+ 1 - 1
drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c

@@ -155,7 +155,7 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 
 static enum drm_mode_status
 dw_hdmi_rockchip_mode_valid(struct drm_connector *connector,
-			    struct drm_display_mode *mode)
+			    const struct drm_display_mode *mode)
 {
 	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
 	int pclk = mode->clock * 1000;

+ 1 - 1
drivers/gpu/drm/vc4/Kconfig

@@ -7,7 +7,7 @@ config DRM_VC4
 	select DRM_KMS_HELPER
 	select DRM_KMS_CMA_HELPER
 	select DRM_GEM_CMA_HELPER
-	select DRM_PANEL
+	select DRM_PANEL_BRIDGE
 	select SND_PCM
 	select SND_PCM_ELD
 	select SND_SOC_GENERIC_DMAENGINE_PCM

+ 4 - 8
drivers/gpu/drm/vc4/vc4_bo.c

@@ -91,8 +91,7 @@ static void vc4_bo_destroy(struct vc4_bo *bo)
 	vc4->bo_stats.num_allocated--;
 	vc4->bo_stats.size_allocated -= obj->size;
 
-	if (bo->resv == &bo->_resv)
-		reservation_object_fini(bo->resv);
+	reservation_object_fini(&bo->_resv);
 
 	drm_gem_cma_free_object(obj);
 }
@@ -212,6 +211,8 @@ struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size)
 	vc4->bo_stats.num_allocated++;
 	vc4->bo_stats.size_allocated += size;
 	mutex_unlock(&vc4->bo_lock);
+	bo->resv = &bo->_resv;
+	reservation_object_init(bo->resv);
 
 	return &bo->base.base;
 }
@@ -250,12 +251,7 @@ struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size,
 			return ERR_PTR(-ENOMEM);
 		}
 	}
-	bo = to_vc4_bo(&cma_obj->base);
-
-	bo->resv = &bo->_resv;
-	reservation_object_init(bo->resv);
-
-	return bo;
+	return to_vc4_bo(&cma_obj->base);
 }
 
 int vc4_dumb_create(struct drm_file *file_priv,

+ 30 - 134
drivers/gpu/drm/vc4/vc4_dpi.c

@@ -23,8 +23,10 @@
  */
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
+#include <drm/drm_of.h>
 #include <drm/drm_panel.h>
 #include <linux/clk.h>
 #include <linux/component.h>
@@ -95,7 +97,8 @@ struct vc4_dpi {
 
 	struct drm_encoder *encoder;
 	struct drm_connector *connector;
-	struct drm_panel *panel;
+	struct drm_bridge *bridge;
+	bool is_panel_bridge;
 
 	void __iomem *regs;
 
@@ -118,24 +121,6 @@ to_vc4_dpi_encoder(struct drm_encoder *encoder)
 	return container_of(encoder, struct vc4_dpi_encoder, base.base);
 }
 
-/* VC4 DPI connector KMS struct */
-struct vc4_dpi_connector {
-	struct drm_connector base;
-	struct vc4_dpi *dpi;
-
-	/* Since the connector is attached to just the one encoder,
-	 * this is the reference to it so we can do the best_encoder()
-	 * hook.
-	 */
-	struct drm_encoder *encoder;
-};
-
-static inline struct vc4_dpi_connector *
-to_vc4_dpi_connector(struct drm_connector *connector)
-{
-	return container_of(connector, struct vc4_dpi_connector, base);
-}
-
 #define DPI_REG(reg) { reg, #reg }
 static const struct {
 	u32 reg;
@@ -167,80 +152,6 @@ int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused)
 }
 #endif
 
-static enum drm_connector_status
-vc4_dpi_connector_detect(struct drm_connector *connector, bool force)
-{
-	struct vc4_dpi_connector *vc4_connector =
-		to_vc4_dpi_connector(connector);
-	struct vc4_dpi *dpi = vc4_connector->dpi;
-
-	if (dpi->panel)
-		return connector_status_connected;
-	else
-		return connector_status_disconnected;
-}
-
-static void vc4_dpi_connector_destroy(struct drm_connector *connector)
-{
-	drm_connector_unregister(connector);
-	drm_connector_cleanup(connector);
-}
-
-static int vc4_dpi_connector_get_modes(struct drm_connector *connector)
-{
-	struct vc4_dpi_connector *vc4_connector =
-		to_vc4_dpi_connector(connector);
-	struct vc4_dpi *dpi = vc4_connector->dpi;
-
-	if (dpi->panel)
-		return drm_panel_get_modes(dpi->panel);
-
-	return 0;
-}
-
-static const struct drm_connector_funcs vc4_dpi_connector_funcs = {
-	.dpms = drm_atomic_helper_connector_dpms,
-	.detect = vc4_dpi_connector_detect,
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = vc4_dpi_connector_destroy,
-	.reset = drm_atomic_helper_connector_reset,
-	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static const struct drm_connector_helper_funcs vc4_dpi_connector_helper_funcs = {
-	.get_modes = vc4_dpi_connector_get_modes,
-};
-
-static struct drm_connector *vc4_dpi_connector_init(struct drm_device *dev,
-						    struct vc4_dpi *dpi)
-{
-	struct drm_connector *connector = NULL;
-	struct vc4_dpi_connector *dpi_connector;
-
-	dpi_connector = devm_kzalloc(dev->dev, sizeof(*dpi_connector),
-				     GFP_KERNEL);
-	if (!dpi_connector)
-		return ERR_PTR(-ENOMEM);
-
-	connector = &dpi_connector->base;
-
-	dpi_connector->encoder = dpi->encoder;
-	dpi_connector->dpi = dpi;
-
-	drm_connector_init(dev, connector, &vc4_dpi_connector_funcs,
-			   DRM_MODE_CONNECTOR_DPI);
-	drm_connector_helper_add(connector, &vc4_dpi_connector_helper_funcs);
-
-	connector->polled = 0;
-	connector->interlace_allowed = 0;
-	connector->doublescan_allowed = 0;
-
-	drm_mode_connector_attach_encoder(connector, dpi->encoder);
-
-	return connector;
-}
-
 static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = {
 	.destroy = drm_encoder_cleanup,
 };
@@ -250,11 +161,7 @@ static void vc4_dpi_encoder_disable(struct drm_encoder *encoder)
 	struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder);
 	struct vc4_dpi *dpi = vc4_encoder->dpi;
 
-	drm_panel_disable(dpi->panel);
-
 	clk_disable_unprepare(dpi->pixel_clock);
-
-	drm_panel_unprepare(dpi->panel);
 }
 
 static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
@@ -265,12 +172,6 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
 	u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE;
 	int ret;
 
-	ret = drm_panel_prepare(dpi->panel);
-	if (ret) {
-		DRM_ERROR("Panel failed to prepare\n");
-		return;
-	}
-
 	if (dpi->connector->display_info.num_bus_formats) {
 		u32 bus_format = dpi->connector->display_info.bus_formats[0];
 
@@ -321,13 +222,6 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
 	ret = clk_prepare_enable(dpi->pixel_clock);
 	if (ret)
 		DRM_ERROR("Failed to set clock rate: %d\n", ret);
-
-	ret = drm_panel_enable(dpi->panel);
-	if (ret) {
-		DRM_ERROR("Panel failed to enable\n");
-		drm_panel_unprepare(dpi->panel);
-		return;
-	}
 }
 
 static bool vc4_dpi_encoder_mode_fixup(struct drm_encoder *encoder,
@@ -351,24 +245,34 @@ static const struct of_device_id vc4_dpi_dt_match[] = {
 	{}
 };
 
-/* Walks the OF graph to find the panel node and then asks DRM to look
- * up the panel.
+/* Sets up the next link in the display chain, whether it's a panel or
+ * a bridge.
  */
-static struct drm_panel *vc4_dpi_get_panel(struct device *dev)
+static int vc4_dpi_init_bridge(struct vc4_dpi *dpi)
 {
-	struct device_node *panel_node;
-	struct device_node *np = dev->of_node;
+	struct device *dev = &dpi->pdev->dev;
 	struct drm_panel *panel;
+	int ret;
 
-	/* don't proceed if we have an endpoint but no panel_node tied to it */
-	panel_node = of_graph_get_remote_node(np, 0, 0);
-	if (!panel_node)
-		return NULL;
+	ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0,
+					  &panel, &dpi->bridge);
+	if (ret) {
+		/* If nothing was connected in the DT, that's not an
+		 * error.
+		 */
+		if (ret == -ENODEV)
+			return 0;
+		else
+			return ret;
+	}
 
-	panel = of_drm_find_panel(panel_node);
-	of_node_put(panel_node);
+	if (panel) {
+		dpi->bridge = drm_panel_bridge_add(panel,
+						   DRM_MODE_CONNECTOR_DPI);
+		dpi->is_panel_bridge = true;
+	}
 
-	return panel;
+	return drm_bridge_attach(dpi->encoder, dpi->bridge, NULL);
 }
 
 static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
@@ -422,20 +326,13 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		DRM_ERROR("Failed to turn on core clock: %d\n", ret);
 
-	dpi->panel = vc4_dpi_get_panel(dev);
-
 	drm_encoder_init(drm, dpi->encoder, &vc4_dpi_encoder_funcs,
 			 DRM_MODE_ENCODER_DPI, NULL);
 	drm_encoder_helper_add(dpi->encoder, &vc4_dpi_encoder_helper_funcs);
 
-	dpi->connector = vc4_dpi_connector_init(drm, dpi);
-	if (IS_ERR(dpi->connector)) {
-		ret = PTR_ERR(dpi->connector);
+	ret = vc4_dpi_init_bridge(dpi);
+	if (ret)
 		goto err_destroy_encoder;
-	}
-
-	if (dpi->panel)
-		drm_panel_attach(dpi->panel, dpi->connector);
 
 	dev_set_drvdata(dev, dpi);
 
@@ -456,10 +353,9 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master,
 	struct vc4_dev *vc4 = to_vc4_dev(drm);
 	struct vc4_dpi *dpi = dev_get_drvdata(dev);
 
-	if (dpi->panel)
-		drm_panel_detach(dpi->panel);
+	if (dpi->is_panel_bridge)
+		drm_panel_bridge_remove(dpi->bridge);
 
-	vc4_dpi_connector_destroy(dpi->connector);
 	drm_encoder_cleanup(dpi->encoder);
 
 	clk_disable_unprepare(dpi->core_clock);

+ 20 - 134
drivers/gpu/drm/vc4/vc4_dsi.c

@@ -503,8 +503,8 @@ struct vc4_dsi {
 
 	struct mipi_dsi_host dsi_host;
 	struct drm_encoder *encoder;
-	struct drm_connector *connector;
-	struct drm_panel *panel;
+	struct drm_bridge *bridge;
+	bool is_panel_bridge;
 
 	void __iomem *regs;
 
@@ -605,18 +605,6 @@ to_vc4_dsi_encoder(struct drm_encoder *encoder)
 	return container_of(encoder, struct vc4_dsi_encoder, base.base);
 }
 
-/* VC4 DSI connector KMS struct */
-struct vc4_dsi_connector {
-	struct drm_connector base;
-	struct vc4_dsi *dsi;
-};
-
-static inline struct vc4_dsi_connector *
-to_vc4_dsi_connector(struct drm_connector *connector)
-{
-	return container_of(connector, struct vc4_dsi_connector, base);
-}
-
 #define DSI_REG(reg) { reg, #reg }
 static const struct {
 	u32 reg;
@@ -724,79 +712,6 @@ int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused)
 }
 #endif
 
-static enum drm_connector_status
-vc4_dsi_connector_detect(struct drm_connector *connector, bool force)
-{
-	struct vc4_dsi_connector *vc4_connector =
-		to_vc4_dsi_connector(connector);
-	struct vc4_dsi *dsi = vc4_connector->dsi;
-
-	if (dsi->panel)
-		return connector_status_connected;
-	else
-		return connector_status_disconnected;
-}
-
-static void vc4_dsi_connector_destroy(struct drm_connector *connector)
-{
-	drm_connector_unregister(connector);
-	drm_connector_cleanup(connector);
-}
-
-static int vc4_dsi_connector_get_modes(struct drm_connector *connector)
-{
-	struct vc4_dsi_connector *vc4_connector =
-		to_vc4_dsi_connector(connector);
-	struct vc4_dsi *dsi = vc4_connector->dsi;
-
-	if (dsi->panel)
-		return drm_panel_get_modes(dsi->panel);
-
-	return 0;
-}
-
-static const struct drm_connector_funcs vc4_dsi_connector_funcs = {
-	.dpms = drm_atomic_helper_connector_dpms,
-	.detect = vc4_dsi_connector_detect,
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = vc4_dsi_connector_destroy,
-	.reset = drm_atomic_helper_connector_reset,
-	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static const struct drm_connector_helper_funcs vc4_dsi_connector_helper_funcs = {
-	.get_modes = vc4_dsi_connector_get_modes,
-};
-
-static struct drm_connector *vc4_dsi_connector_init(struct drm_device *dev,
-						    struct vc4_dsi *dsi)
-{
-	struct drm_connector *connector;
-	struct vc4_dsi_connector *dsi_connector;
-
-	dsi_connector = devm_kzalloc(dev->dev, sizeof(*dsi_connector),
-				     GFP_KERNEL);
-	if (!dsi_connector)
-		return ERR_PTR(-ENOMEM);
-
-	connector = &dsi_connector->base;
-
-	dsi_connector->dsi = dsi;
-
-	drm_connector_init(dev, connector, &vc4_dsi_connector_funcs,
-			   DRM_MODE_CONNECTOR_DSI);
-	drm_connector_helper_add(connector, &vc4_dsi_connector_helper_funcs);
-
-	connector->polled = 0;
-	connector->interlace_allowed = 0;
-	connector->doublescan_allowed = 0;
-
-	drm_mode_connector_attach_encoder(connector, dsi->encoder);
-
-	return connector;
-}
-
 static void vc4_dsi_encoder_destroy(struct drm_encoder *encoder)
 {
 	drm_encoder_cleanup(encoder);
@@ -894,12 +809,8 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
 	struct vc4_dsi *dsi = vc4_encoder->dsi;
 	struct device *dev = &dsi->pdev->dev;
 
-	drm_panel_disable(dsi->panel);
-
 	vc4_dsi_ulps(dsi, true);
 
-	drm_panel_unprepare(dsi->panel);
-
 	clk_disable_unprepare(dsi->pll_phy_clock);
 	clk_disable_unprepare(dsi->escape_clock);
 	clk_disable_unprepare(dsi->pixel_clock);
@@ -984,12 +895,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
 		return;
 	}
 
-	ret = drm_panel_prepare(dsi->panel);
-	if (ret) {
-		DRM_ERROR("Panel failed to prepare\n");
-		return;
-	}
-
 	if (debug_dump_regs) {
 		DRM_INFO("DSI regs before:\n");
 		vc4_dsi_dump_regs(dsi);
@@ -1211,13 +1116,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
 		DRM_INFO("DSI regs after:\n");
 		vc4_dsi_dump_regs(dsi);
 	}
-
-	ret = drm_panel_enable(dsi->panel);
-	if (ret) {
-		DRM_ERROR("Panel failed to enable\n");
-		drm_panel_unprepare(dsi->panel);
-		return;
-	}
 }
 
 static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host,
@@ -1415,17 +1313,22 @@ static int vc4_dsi_host_attach(struct mipi_dsi_host *host,
 		return 0;
 	}
 
-	dsi->panel = of_drm_find_panel(device->dev.of_node);
-	if (!dsi->panel)
-		return 0;
-
-	ret = drm_panel_attach(dsi->panel, dsi->connector);
-	if (ret != 0)
-		return ret;
+	dsi->bridge = of_drm_find_bridge(device->dev.of_node);
+	if (!dsi->bridge) {
+		struct drm_panel *panel =
+			of_drm_find_panel(device->dev.of_node);
 
-	drm_helper_hpd_irq_event(dsi->connector->dev);
+		dsi->bridge = drm_panel_bridge_add(panel,
+						   DRM_MODE_CONNECTOR_DSI);
+		if (IS_ERR(dsi->bridge)) {
+			ret = PTR_ERR(dsi->bridge);
+			dsi->bridge = NULL;
+			return ret;
+		}
+		dsi->is_panel_bridge = true;
+	}
 
-	return 0;
+	return drm_bridge_attach(dsi->encoder, dsi->bridge, NULL);
 }
 
 static int vc4_dsi_host_detach(struct mipi_dsi_host *host,
@@ -1433,15 +1336,9 @@ static int vc4_dsi_host_detach(struct mipi_dsi_host *host,
 {
 	struct vc4_dsi *dsi = host_to_dsi(host);
 
-	if (dsi->panel) {
-		int ret = drm_panel_detach(dsi->panel);
-
-		if (ret)
-			return ret;
-
-		dsi->panel = NULL;
-
-		drm_helper_hpd_irq_event(dsi->connector->dev);
+	if (dsi->is_panel_bridge) {
+		drm_panel_bridge_remove(dsi->bridge);
+		dsi->bridge = NULL;
 	}
 
 	return 0;
@@ -1708,12 +1605,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 			 DRM_MODE_ENCODER_DSI, NULL);
 	drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs);
 
-	dsi->connector = vc4_dsi_connector_init(drm, dsi);
-	if (IS_ERR(dsi->connector)) {
-		ret = PTR_ERR(dsi->connector);
-		goto err_destroy_encoder;
-	}
-
 	dsi->dsi_host.ops = &vc4_dsi_host_ops;
 	dsi->dsi_host.dev = dev;
 
@@ -1724,11 +1615,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 	pm_runtime_enable(dev);
 
 	return 0;
-
-err_destroy_encoder:
-	vc4_dsi_encoder_destroy(dsi->encoder);
-
-	return ret;
 }
 
 static void vc4_dsi_unbind(struct device *dev, struct device *master,
@@ -1740,7 +1626,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
 
 	pm_runtime_disable(dev);
 
-	vc4_dsi_connector_destroy(dsi->connector);
+	drm_bridge_remove(dsi->bridge);
 	vc4_dsi_encoder_destroy(dsi->encoder);
 
 	mipi_dsi_host_unregister(&dsi->dsi_host);

+ 8 - 5
drivers/gpu/drm/vc4/vc4_gem.c

@@ -111,8 +111,8 @@ vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
 					    &handle);
 
 		if (ret) {
-			state->bo_count = i - 1;
-			goto err;
+			state->bo_count = i;
+			goto err_delete_handle;
 		}
 		bo_state[i].handle = handle;
 		bo_state[i].paddr = vc4_bo->base.paddr;
@@ -124,13 +124,16 @@ vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
 			 state->bo_count * sizeof(*bo_state)))
 		ret = -EFAULT;
 
-	kfree(bo_state);
+err_delete_handle:
+	if (ret) {
+		for (i = 0; i < state->bo_count; i++)
+			drm_gem_handle_delete(file_priv, bo_state[i].handle);
+	}
 
 err_free:
-
 	vc4_free_hang_state(dev, kernel_state);
+	kfree(bo_state);
 
-err:
 	return ret;
 }
 

+ 1 - 1
drivers/gpu/drm/zte/zx_drm_drv.c

@@ -196,7 +196,7 @@ static int zx_drm_probe(struct platform_device *pdev)
 	struct component_match *match = NULL;
 	int ret;
 
-	ret = of_platform_populate(parent, NULL, NULL, dev);
+	ret = devm_of_platform_populate(dev);
 	if (ret)
 		return ret;
 

+ 1 - 1
include/drm/bridge/dw_hdmi.h

@@ -125,7 +125,7 @@ struct dw_hdmi_phy_ops {
 struct dw_hdmi_plat_data {
 	struct regmap *regm;
 	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
-					   struct drm_display_mode *mode);
+					   const struct drm_display_mode *mode);
 	unsigned long input_bus_format;
 	unsigned long input_bus_encoding;
 

+ 7 - 0
include/drm/drm_bridge.h

@@ -29,6 +29,7 @@
 #include <drm/drm_modes.h>
 
 struct drm_bridge;
+struct drm_panel;
 
 /**
  * struct drm_bridge_funcs - drm_bridge control functions
@@ -263,4 +264,10 @@ void drm_bridge_mode_set(struct drm_bridge *bridge,
 void drm_bridge_pre_enable(struct drm_bridge *bridge);
 void drm_bridge_enable(struct drm_bridge *bridge);
 
+#ifdef CONFIG_DRM_PANEL_BRIDGE
+struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel,
+					u32 connector_type);
+void drm_panel_bridge_remove(struct drm_bridge *bridge);
+#endif
+
 #endif

+ 0 - 2
include/drm/drm_connector.h

@@ -30,8 +30,6 @@
 
 #include <uapi/drm/drm_mode.h>
 
-struct drm_device;
-
 struct drm_connector_helper_funcs;
 struct drm_modeset_acquire_ctx;
 struct drm_device;

+ 1 - 1
include/drm/drm_property.h

@@ -214,7 +214,7 @@ struct drm_property_blob {
 
 struct drm_prop_enum_list {
 	int type;
-	char *name;
+	const char *name;
 };
 
 #define obj_to_property(x) container_of(x, struct drm_property, base)