summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/display/panel/auo,p320hvn03.txt8
-rw-r--r--Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt23
-rw-r--r--Documentation/devicetree/bindings/display/panel/nec,nl12880b20-05.txt8
-rw-r--r--Documentation/devicetree/bindings/display/panel/nlt,nl192108ac18-02d.txt8
-rw-r--r--Documentation/devicetree/bindings/display/panel/samsung,s6e3ha2.txt5
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt1
-rw-r--r--Documentation/gpu/drm-kms-helpers.rst6
-rw-r--r--drivers/gpu/drm/Makefile1
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c173
-rw-r--r--drivers/gpu/drm/bridge/Kconfig11
-rw-r--r--drivers/gpu/drm/bridge/lvds-encoder.c157
-rw-r--r--drivers/gpu/drm/bridge/panel.c200
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c41
-rw-r--r--drivers/gpu/drm/drm_atomic.c3
-rw-r--r--drivers/gpu/drm/imx/dw_hdmi-imx.c10
-rw-r--r--drivers/gpu/drm/meson/meson_dw_hdmi.c5
-rw-r--r--drivers/gpu/drm/mxsfb/mxsfb_crtc.c42
-rw-r--r--drivers/gpu/drm/panel/Kconfig13
-rw-r--r--drivers/gpu/drm/panel/Makefile1
-rw-r--r--drivers/gpu/drm/panel/panel-innolux-p079zca.c340
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c64
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c90
-rw-r--r--drivers/gpu/drm/pl111/pl111_display.c15
-rw-r--r--drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c2
-rw-r--r--drivers/gpu/drm/vc4/Kconfig2
-rw-r--r--drivers/gpu/drm/vc4/vc4_bo.c12
-rw-r--r--drivers/gpu/drm/vc4/vc4_dpi.c164
-rw-r--r--drivers/gpu/drm/vc4/vc4_dsi.c154
-rw-r--r--drivers/gpu/drm/vc4/vc4_gem.c13
-rw-r--r--drivers/gpu/drm/zte/zx_drm_drv.c2
-rw-r--r--include/drm/bridge/dw_hdmi.h2
-rw-r--r--include/drm/drm_bridge.h7
-rw-r--r--include/drm/drm_connector.h2
-rw-r--r--include/drm/drm_property.h2
34 files changed, 944 insertions, 643 deletions
diff --git a/Documentation/devicetree/bindings/display/panel/auo,p320hvn03.txt b/Documentation/devicetree/bindings/display/panel/auo,p320hvn03.txt
new file mode 100644
index 000000000000..59bb6cd8aa75
--- /dev/null
+++ b/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.
diff --git a/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt b/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt
new file mode 100644
index 000000000000..5c70a8380e58
--- /dev/null
+++ b/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";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/display/panel/nec,nl12880b20-05.txt b/Documentation/devicetree/bindings/display/panel/nec,nl12880b20-05.txt
new file mode 100644
index 000000000000..71cbc49ecfab
--- /dev/null
+++ b/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.
diff --git a/Documentation/devicetree/bindings/display/panel/nlt,nl192108ac18-02d.txt b/Documentation/devicetree/bindings/display/panel/nlt,nl192108ac18-02d.txt
new file mode 100644
index 000000000000..1a639fd8778d
--- /dev/null
+++ b/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.
diff --git a/Documentation/devicetree/bindings/display/panel/samsung,s6e3ha2.txt b/Documentation/devicetree/bindings/display/panel/samsung,s6e3ha2.txt
index 18854f4c8376..4acea25c244b 100644
--- a/Documentation/devicetree/bindings/display/panel/samsung,s6e3ha2.txt
+++ b/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
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index c03d20140366..f08284e7439b 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/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
diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst
index c075aadd7078..7c5e2549a58a 100644
--- a/Documentation/gpu/drm-kms-helpers.rst
+++ b/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
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 0f527a763fde..24a066e1841c 100644
--- a/drivers/gpu/drm/Makefile
+++ b/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
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
index 423dda2785d4..8db51fb131db 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
+++ b/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;
}
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index f6968d3b4b41..adf9ae0e0b7c 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/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.
diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c
index f1f67a279426..0903ba574f61 100644
--- a/drivers/gpu/drm/bridge/lvds-encoder.c
+++ b/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;
}
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
new file mode 100644
index 000000000000..99f9a4beb859
--- /dev/null
+++ b/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);
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 8737de8c1c52..ead11242c4b9 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/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)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 77dcef00998c..c0f336d23f9c 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/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;
diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c
index f039641070ac..b62763aa8706 100644
--- a/drivers/gpu/drm/imx/dw_hdmi-imx.c
+++ b/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;
diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
index 7b86eb7776b3..cef414466f9f 100644
--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
+++ b/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;
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c
index 1144e0c9e894..0abe77675b76 100644
--- a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c
+++ b/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);
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 3e29a9903303..d84a031fae24 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/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
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 292b3c77aede..9f6610d08b00 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/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
diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c
new file mode 100644
index 000000000000..6ba93449fcfb
--- /dev/null
+++ b/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");
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
index 4cc08d7b3de4..797bbc7a264e 100644
--- a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c
+++ b/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);
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index c4566ce8fda7..474fa759e06e 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/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,
@@ -1888,6 +1969,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,
}, {
@@ -1972,12 +2056,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,
}, {
diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c
index 3e0a4fa73ddb..c6ca4f1bbd49 100644
--- a/drivers/gpu/drm/pl111/pl111_display.c
+++ b/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);
}
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 63dab6f1b191..f8208489724e 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/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;
diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig
index b16aefe4a8d3..4361bdcfd28a 100644
--- a/drivers/gpu/drm/vc4/Kconfig
+++ b/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
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index 80b2f9e55c5c..590c0912afc1 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/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,
diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index 39d68080873c..2e0fe46aeb2e 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/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);
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index 0ef41df3915f..5e8b81eaa168 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/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);
diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c
index 9dc7646d49ed..d5b821ad06af 100644
--- a/drivers/gpu/drm/vc4/vc4_gem.c
+++ b/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;
}
diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c
index 490aafc99610..f46c855d274b 100644
--- a/drivers/gpu/drm/zte/zx_drm_drv.c
+++ b/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;
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index ed599bea3f6c..4c8d4c81a0e8 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/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;
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 5b106eca6d57..1dc94d5392e2 100644
--- a/include/drm/drm_bridge.h
+++ b/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
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index d8bb25f38eba..ae5b7dc316c8 100644
--- a/include/drm/drm_connector.h
+++ b/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;
diff --git a/include/drm/drm_property.h b/include/drm/drm_property.h
index 13e8c17d1c79..619868dc08d8 100644
--- a/include/drm/drm_property.h
+++ b/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)