Devicetree
 help / color / mirror / Atom feed
* [PATCH v5 00/11] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface
@ 2026-10-29 15:28 Svyatoslav Ryhel
  2026-10-29 15:28 ` [PATCH v5 01/11] drm: Add MIPI DBI bus support Svyatoslav Ryhel
                   ` (10 more replies)
  0 siblings, 11 replies; 15+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-29 15:28 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
  Cc: dri-devel, devicetree, linux-kernel, linux-tegra

The display controller in Tegra20/30 SoCs features an 8-bit SPI interface
that closely resembles the MIPI DBI Type B protocol and is referred to as
'8-bit CPU'.

Add MIPI DBI bus support along with 2 DBI Type B panels used in
LG Optimus 2X which work with the 8-bit CPU interface.

Convert rgb, dsi, hdmi and sor to use atomic versions of
drm_encoder_helper_funcs needed for bridge state requesting.
Refactor and expand DC header. Add DC parametrization options
needed for fine tuning by bridges. Make panel parsing by
tegra_output_probe() optional and document optional DSI port. 
Add 8-bit CPU interface in form of a bridge linking to RGB port.

---
Changes in v2:
- convert enums in the dc header into definitions list
- add check in RGB if bridge_state is not NULL, if yes, use default flags
- inverted FLAG_PIXDATA_SAMPLE flag setup logic since default value is set to
  be on NEGEDGE while v1 set default to be on POSEDGE
- 8bit-cpu schema renamed to dbi, removed compaible and set as DC child
- expanded list of required properties
- 8bit-cpu bridge remodelled into dbi interface as DC child
- improve gpio handling
- rework attach to get drvdata from parent
- add panel supplies description
- split panel driver into per-panel drivers
- get DBI interface from the patent directly

I have deliberately left GPIO parsing from device tree since it seems to
be the most convenient and mainstream method of working with GPIOs.


Changes in v3:
- rgb: fix default bus_flags to match original behavior
- dbi-schema: remove compatible from required props
  remove dc@54200000 wrapper from example
  restrict dbi nodes for tegra20/tegra30 only
- dbi: remove unused idx from tegra_dbi_priv
  wrap dbi registration in dc with devm_add_action_or_reset()
  separate dbi into separate object
  export tegra_dc_commit() and trace_dc_readl()/dc_readl()

Changes in v4:
- reorder patches to avoid errors on checking dbi schema
- dbi schema renamed to nvidia,tegra20-dbi for consistency
- fix DC_DISP_V_PULSE0_POSITION_* > DC_DISP_V_PULSE1_POSITION_*
- add MODULE_ALIAS
- swap width and height in panels
- dropped use of init-sequence and gpios
- dbi driver reworked to use only DC to operate pins

Changes in v5:
- added commit modelling DBI bus (suggested by Mikko Perttunen)
- hitachi and lg panels converted to mipi_dbi_driver
- dc header refactored and completed
- added commit documenting DSI controller port
- added commit which converts drm_encoder_helper_funcs to use atomic
  enable/disable
- added commit which makes panel parsing in tegra_output_probe() optional
- dbi command/data converted to enum
- dc com pins are now backed up into drivers private structure
- magic numbers replaced with pin defines
- dropped tegra_dc_readl() from tegra_dbi_pin_set
- tegra_dbi_pin_set() accepts boolean third argument
- tegra_dbi_write() second argument converted to use tegra_dbi_dc_signal enum
- used field_get() to extract pin value
- extended comment regarding init sequence
- nvidia,index > generic reg property
- adjusted tegra_dbi_atomic_enable() to properly handle shadowed registers
- switched to use of_drm_get_bridge_by_endpoint() since panels are auto-wrapped
  into bridge now
- added support for mipi_dbi_host_ops
- added resource managed helper to register DBI device and provided to DC via
  header
- dbi.o built with all other tegra drm files
---

Mikko Perttunen (1):
  drm: Add MIPI DBI bus support

Svyatoslav Ryhel (10):
  dt-bindings: display: panel: Document Hitachi TX10D07VM0BAA and LG
    LH400WV3 panels
  drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver
  drm/panel: Add LG LH400WV3-SD04 MIPI DBI Type B panel driver
  drm/tegra: dc: Refactor and expand header
  dt-bindings: display: tegra: Document Tegra20 DSI port
  drm/tegra: Make panel parsing by tegra_output_probe() optional
  drm/tegra: Convert to use atomic versions of drm_encoder_helper_funcs
  drm/tegra: rgb: Parameterize configuration based on bus flags
  dt-bindings: display: tegra: Document 8-bit CPU parallel interface
  drm/tegra: Add support for 8-bit CPU interface

 .../display/panel/hit,tx10d07vm0baa.yaml      |  58 ++
 .../display/tegra/nvidia,tegra20-dbi.yaml     | 113 +++
 .../display/tegra/nvidia,tegra20-dc.yaml      |  22 +
 .../display/tegra/nvidia,tegra20-dsi.yaml     |   6 +
 drivers/gpu/drm/Makefile                      |   2 +-
 drivers/gpu/drm/drm_mipi_dbi_bus.c            | 232 +++++
 drivers/gpu/drm/panel/Kconfig                 |  27 +
 drivers/gpu/drm/panel/Makefile                |   2 +
 .../drm/panel/panel-hitachi-tx10d07vm0baa.c   | 294 ++++++
 drivers/gpu/drm/panel/panel-lg-lh400wv3.c     | 233 +++++
 drivers/gpu/drm/tegra/Makefile                |   1 +
 drivers/gpu/drm/tegra/dbi.c                   | 632 +++++++++++++
 drivers/gpu/drm/tegra/dc.c                    |  15 +-
 drivers/gpu/drm/tegra/dc.h                    | 859 +++++++++++-------
 drivers/gpu/drm/tegra/drm.h                   |   2 +-
 drivers/gpu/drm/tegra/dsi.c                   |  22 +-
 drivers/gpu/drm/tegra/hdmi.c                  |  22 +-
 drivers/gpu/drm/tegra/output.c                |  52 +-
 drivers/gpu/drm/tegra/rgb.c                   |  72 +-
 drivers/gpu/drm/tegra/sor.c                   |  20 +-
 include/drm/drm_mipi_dbi_bus.h                | 103 +++
 21 files changed, 2372 insertions(+), 417 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/panel/hit,tx10d07vm0baa.yaml
 create mode 100644 Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dbi.yaml
 create mode 100644 drivers/gpu/drm/drm_mipi_dbi_bus.c
 create mode 100644 drivers/gpu/drm/panel/panel-hitachi-tx10d07vm0baa.c
 create mode 100644 drivers/gpu/drm/panel/panel-lg-lh400wv3.c
 create mode 100644 drivers/gpu/drm/tegra/dbi.c
 create mode 100644 include/drm/drm_mipi_dbi_bus.h

-- 
2.53.0


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

* [PATCH v5 01/11] drm: Add MIPI DBI bus support
  2026-10-29 15:28 [PATCH v5 00/11] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
@ 2026-10-29 15:28 ` Svyatoslav Ryhel
  2026-10-29 15:29 ` [PATCH v5 02/11] dt-bindings: display: panel: Document Hitachi TX10D07VM0BAA and LG LH400WV3 panels Svyatoslav Ryhel
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 15+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-29 15:28 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
  Cc: dri-devel, devicetree, linux-kernel, linux-tegra

From: Mikko Perttunen <mperttunen@nvidia.com>

The MIPI DBI bus allows modeling DBI hosts and peripherals using the Linux
driver model. DBI hosts are registered by their respective drivers. During
registration, DBI peripherals are created from the device tree nodes
provided by the host.

DBI hosts expose operations that can be used by peripheral drivers to
access associated devices.

The original code was authored by Mikko Perttunen; I have aligned it with
the existing MIPI DSI implementation.

Co-developed-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/gpu/drm/Makefile           |   2 +-
 drivers/gpu/drm/drm_mipi_dbi_bus.c | 232 +++++++++++++++++++++++++++++
 include/drm/drm_mipi_dbi_bus.h     | 103 +++++++++++++
 3 files changed, 336 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/drm_mipi_dbi_bus.c
 create mode 100644 include/drm/drm_mipi_dbi_bus.h

diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 2c6bc093a923a..880f0dc930b85 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -166,7 +166,7 @@ obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
 
 obj-y			+= tests/
 
-obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o
+obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o drm_mipi_dbi_bus.o
 obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o
 obj-y			+= arm/
 obj-y			+= clients/
diff --git a/drivers/gpu/drm/drm_mipi_dbi_bus.c b/drivers/gpu/drm/drm_mipi_dbi_bus.c
new file mode 100644
index 0000000000000..afad1f1508064
--- /dev/null
+++ b/drivers/gpu/drm/drm_mipi_dbi_bus.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MIPI DBI devices with command transport through parallel interface
+ *
+ * Copyright 2026 Mikko Perttunen <mperttunen@nvidia.com>
+ * Copyright 2026 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_mipi_dbi_bus.h>
+
+static int mipi_dbi_match(struct device *dev, const struct device_driver *driver)
+{
+	return of_driver_match_device(dev, driver);
+}
+
+static int mipi_dbi_uevent(const struct device *dev, struct kobj_uevent_env *env)
+{
+	return of_device_uevent_modalias(dev, env);
+}
+
+static const struct dev_pm_ops mipi_dbi_device_pm_ops = {
+	.runtime_suspend = pm_generic_runtime_suspend,
+	.runtime_resume = pm_generic_runtime_resume,
+	.suspend = pm_generic_suspend,
+	.resume = pm_generic_resume,
+	.freeze = pm_generic_freeze,
+	.thaw = pm_generic_thaw,
+	.poweroff = pm_generic_poweroff,
+	.restore = pm_generic_restore,
+};
+
+static const struct bus_type mipi_dbi_bus_type = {
+	.name = "mipi-dbi",
+	.match = mipi_dbi_match,
+	.uevent = mipi_dbi_uevent,
+	.pm = &mipi_dbi_device_pm_ops,
+};
+
+static void mipi_dbi_device_release(struct device *dev)
+{
+	of_node_put(dev->of_node);
+	kfree(to_mipi_dbi_device(dev));
+}
+
+static void mipi_dbi_device_unregister(void *data)
+{
+	struct device *dev = data;
+
+	of_node_clear_flag(dev->of_node, OF_POPULATED);
+	device_unregister(dev);
+}
+
+/**
+ * devm_mipi_dbi_device_register() - create a managed MIPI DBI device
+ * @dev: device to tie the MIPI-DBI device lifetime to
+ * @host: DBI host to which this device is connected
+ * @np: device tree node associated with the panel MIPI DBI device
+ *
+ * This function is called by DBI host driver to create panel device.
+ * It automatically calls mipi_dbi_device_unregister() when @dev is
+ * unbound. It does not automatically free passed device node pointer,
+ * it must be put manually.
+ *
+ * Returns:
+ * A pointer to the newly created MIPI DBI device, or, a pointer encoded
+ * with an error.
+ */
+struct mipi_dbi_device *
+devm_mipi_dbi_device_register(struct device *dev, struct mipi_dbi_host *host,
+			      struct device_node *np)
+{
+	struct mipi_dbi_device *dbidev;
+	int ret;
+
+	if (!host || !host->dev || !host->dbi || !host->dbi->command ||
+	    !host->ops || !host->ops->attach || !host->ops->detach || !np)
+		return ERR_PTR(-EINVAL);
+
+	if (!of_device_is_available(np))
+		return ERR_PTR(-ENODEV);
+
+	dbidev = kzalloc_obj(*dbidev);
+	if (!dbidev)
+		return ERR_PTR(-ENOMEM);
+
+	if (of_node_test_and_set_flag(np, OF_POPULATED)) {
+		kfree(dbidev);
+		return ERR_PTR(-EBUSY);
+	}
+
+	dbidev->host = host;
+	dbidev->dbi = host->dbi;
+	device_initialize(&dbidev->dev);
+	dbidev->dev.parent = host->dev;
+	dbidev->dev.bus = &mipi_dbi_bus_type;
+	dbidev->dev.release = mipi_dbi_device_release;
+	device_set_node(&dbidev->dev, of_fwnode_handle(of_node_get(np)));
+
+	ret = dev_set_name(&dbidev->dev, "%s:%s", dev_name(host->dev), np->full_name);
+	if (ret)
+		goto put_device;
+
+	ret = device_add(&dbidev->dev);
+	if (ret)
+		goto put_device;
+
+	ret = devm_add_action_or_reset(dev, mipi_dbi_device_unregister,
+				       &dbidev->dev);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return dbidev;
+
+put_device:
+	of_node_clear_flag(np, OF_POPULATED);
+	put_device(&dbidev->dev);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(devm_mipi_dbi_device_register);
+
+static void mipi_dbi_detach(void *data)
+{
+	struct mipi_dbi_device *dbidev = data;
+
+	dbidev->host->ops->detach(dbidev->host, dbidev);
+	dbidev->attached = false;
+}
+
+/**
+ * devm_mipi_dbi_attach() - managed attach of DBI device to its DBI host
+ * @dev: device to tie the MIPI-DBI device lifetime to
+ * @dbidev: DBI peripheral
+ */
+int devm_mipi_dbi_attach(struct device *dev, struct mipi_dbi_device *dbidev)
+{
+	int ret;
+
+	if (dbidev->attached)
+		return -EBUSY;
+
+	ret = dbidev->host->ops->attach(dbidev->host, dbidev);
+	if (ret)
+		return ret;
+
+	dbidev->attached = true;
+	return devm_add_action_or_reset(dev, mipi_dbi_detach, dbidev);
+}
+EXPORT_SYMBOL_GPL(devm_mipi_dbi_attach);
+
+static int mipi_dbi_drv_probe(struct device *dev)
+{
+	struct mipi_dbi_driver *drv = to_mipi_dbi_driver(dev->driver);
+	struct mipi_dbi_device *dbidev = to_mipi_dbi_device(dev);
+
+	return drv->probe(dbidev);
+}
+
+static int mipi_dbi_drv_remove(struct device *dev)
+{
+	struct mipi_dbi_driver *drv = to_mipi_dbi_driver(dev->driver);
+	struct mipi_dbi_device *dbidev = to_mipi_dbi_device(dev);
+
+	drv->remove(dbidev);
+
+	return 0;
+}
+
+static void mipi_dbi_drv_shutdown(struct device *dev)
+{
+	struct mipi_dbi_driver *drv = to_mipi_dbi_driver(dev->driver);
+	struct mipi_dbi_device *dbidev = to_mipi_dbi_device(dev);
+
+	drv->shutdown(dbidev);
+}
+
+/**
+ * __mipi_dbi_driver_register() - register a driver for DBI devices
+ * @drv: DBI driver structure
+ * @owner: owner module
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int __mipi_dbi_driver_register(struct mipi_dbi_driver *drv, struct module *owner)
+{
+	drv->driver.bus = &mipi_dbi_bus_type;
+	drv->driver.owner = owner;
+
+	if (drv->probe)
+		drv->driver.probe = mipi_dbi_drv_probe;
+	if (drv->remove)
+		drv->driver.remove = mipi_dbi_drv_remove;
+	if (drv->shutdown)
+		drv->driver.shutdown = mipi_dbi_drv_shutdown;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(__mipi_dbi_driver_register);
+
+/**
+ * mipi_dbi_driver_unregister() - unregister a driver for DBI devices
+ * @drv: DBI driver structure
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+void mipi_dbi_driver_unregister(struct mipi_dbi_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_driver_unregister);
+
+static int __init mipi_dbi_bus_init(void)
+{
+	return bus_register(&mipi_dbi_bus_type);
+}
+subsys_initcall(mipi_dbi_bus_init);
+
+static void __exit mipi_dbi_bus_exit(void)
+{
+	bus_unregister(&mipi_dbi_bus_type);
+}
+module_exit(mipi_dbi_bus_exit);
+
+MODULE_DESCRIPTION("MIPI DBI devices with command transport through parallel interface");
+MODULE_LICENSE("GPL");
diff --git a/include/drm/drm_mipi_dbi_bus.h b/include/drm/drm_mipi_dbi_bus.h
new file mode 100644
index 0000000000000..2b8b59bffd0b8
--- /dev/null
+++ b/include/drm/drm_mipi_dbi_bus.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * MIPI DBI devices with command transport through parallel interface
+ *
+ * Copyright 2026 Mikko Perttunen <mperttunen@nvidia.com>
+ * Copyright 2026 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#ifndef __DRM_MIPI_DBI_BUS_H__
+#define __DRM_MIPI_DBI_BUS_H__
+
+#include <linux/device.h>
+#include <linux/module.h>
+
+struct device_node;
+struct mipi_dbi;
+struct mipi_dbi_device;
+struct mipi_dbi_host;
+
+/**
+ * struct mipi_dbi_host_ops - DBI bus operations
+ * @attach: attach DBI device to DBI host
+ * @detach: detach DBI device from DBI host
+ */
+struct mipi_dbi_host_ops {
+	int (*attach)(struct mipi_dbi_host *host, struct mipi_dbi_device *dev);
+	void (*detach)(struct mipi_dbi_host *host, struct mipi_dbi_device *dev);
+};
+
+/**
+ * struct mipi_dbi_host - DBI host device
+ * @dev: driver model device node for this DBI host
+ * @dbi: MIPI DBI interface
+ * @ops: DBI host operations
+ */
+struct mipi_dbi_host {
+	struct device *dev;
+	struct mipi_dbi *dbi;
+	const struct mipi_dbi_host_ops *ops;
+};
+
+/**
+ * struct mipi_dbi_device - DBI peripheral device
+ * @dev: driver model device node for this peripheral
+ * @dbi: MIPI DBI interface
+ * @host: DBI host for this peripheral
+ * @attached: the DBI device has been successfully attached
+ */
+struct mipi_dbi_device {
+	struct device dev;
+	struct mipi_dbi *dbi;
+	struct mipi_dbi_host *host;
+	bool attached;
+};
+
+#define to_mipi_dbi_device(__dev)	container_of_const(__dev, struct mipi_dbi_device, dev)
+
+struct mipi_dbi_device *
+devm_mipi_dbi_device_register(struct device *dev, struct mipi_dbi_host *host,
+			      struct device_node *np);
+int devm_mipi_dbi_attach(struct device *dev, struct mipi_dbi_device *dbidev);
+
+/**
+ * struct mipi_dbi_driver - DBI driver
+ * @driver: device driver model driver
+ * @probe: callback for device binding
+ * @remove: callback for device unbinding
+ * @shutdown: called at shutdown time to quiesce the device
+ */
+struct mipi_dbi_driver {
+	struct device_driver driver;
+	int (*probe)(struct mipi_dbi_device *dbi);
+	void (*remove)(struct mipi_dbi_device *dbi);
+	void (*shutdown)(struct mipi_dbi_device *dbi);
+};
+
+static inline struct mipi_dbi_driver *
+to_mipi_dbi_driver(struct device_driver *driver)
+{
+	return container_of(driver, struct mipi_dbi_driver, driver);
+}
+
+static inline void *mipi_dbi_get_drvdata(const struct mipi_dbi_device *dbi)
+{
+	return dev_get_drvdata(&dbi->dev);
+}
+
+static inline void mipi_dbi_set_drvdata(struct mipi_dbi_device *dbi, void *data)
+{
+	dev_set_drvdata(&dbi->dev, data);
+}
+
+int __mipi_dbi_driver_register(struct mipi_dbi_driver *drv, struct module *owner);
+void mipi_dbi_driver_unregister(struct mipi_dbi_driver *drv);
+
+#define mipi_dbi_driver_register(driver) \
+	__mipi_dbi_driver_register(driver, THIS_MODULE)
+
+#define module_mipi_dbi_driver(__mipi_dbi_driver) \
+	module_driver(__mipi_dbi_driver, mipi_dbi_driver_register, \
+			mipi_dbi_driver_unregister)
+
+#endif /* __DRM_MIPI_DBI_BUS__ */
-- 
2.53.0


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

* [PATCH v5 02/11] dt-bindings: display: panel: Document Hitachi TX10D07VM0BAA and LG LH400WV3 panels
  2026-10-29 15:28 [PATCH v5 00/11] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
  2026-10-29 15:28 ` [PATCH v5 01/11] drm: Add MIPI DBI bus support Svyatoslav Ryhel
@ 2026-10-29 15:29 ` Svyatoslav Ryhel
  2026-10-29 15:29 ` [PATCH v5 03/11] drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver Svyatoslav Ryhel
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 15+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-29 15:29 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
  Cc: dri-devel, devicetree, linux-kernel, linux-tegra

Document Hitachi TX10D07VM0BAA and LG LH400WV3 MIPI DBI 4" WVGA panels
found in LG Optimus 2X P990.

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 .../display/panel/hit,tx10d07vm0baa.yaml      | 58 +++++++++++++++++++
 1 file changed, 58 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/panel/hit,tx10d07vm0baa.yaml

diff --git a/Documentation/devicetree/bindings/display/panel/hit,tx10d07vm0baa.yaml b/Documentation/devicetree/bindings/display/panel/hit,tx10d07vm0baa.yaml
new file mode 100644
index 0000000000000..4642dc14c42eb
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/hit,tx10d07vm0baa.yaml
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/hit,tx10d07vm0baa.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Hitachi/LG 4" WVGA TFT LCD panel
+
+maintainers:
+  - Svyatoslav Ryhel <clamor95@gmail.com>
+
+allOf:
+  - $ref: panel-common.yaml#
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - hit,tx10d07vm0baa
+          - lg,lh400wv3-sd04
+
+  avci-supply:
+    description: Panel power supply
+
+  iovcc-supply:
+    description: I/O voltage supply
+
+  reset-gpios: true
+
+  backlight: true
+  port: true
+
+required:
+  - compatible
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    panel {
+        compatible = "lg,lh400wv3-sd04";
+
+        reset-gpios = <&gpio 175 GPIO_ACTIVE_LOW>;
+
+        avci-supply = <&vcc_2v8_lcd>;
+        iovcc-supply = <&iovcc_1v8_lcd>;
+
+        backlight = <&backlight>;
+
+        port {
+            endpoint {
+                remote-endpoint = <&dbi_out>;
+            };
+        };
+    };
+...
-- 
2.53.0


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

* [PATCH v5 03/11] drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver
  2026-10-29 15:28 [PATCH v5 00/11] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
  2026-10-29 15:28 ` [PATCH v5 01/11] drm: Add MIPI DBI bus support Svyatoslav Ryhel
  2026-10-29 15:29 ` [PATCH v5 02/11] dt-bindings: display: panel: Document Hitachi TX10D07VM0BAA and LG LH400WV3 panels Svyatoslav Ryhel
@ 2026-10-29 15:29 ` Svyatoslav Ryhel
  2026-10-29 15:40   ` sashiko-bot
  2026-10-29 15:29 ` [PATCH v5 04/11] drm/panel: Add LG LH400WV3-SD04 " Svyatoslav Ryhel
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 15+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-29 15:29 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
  Cc: dri-devel, devicetree, linux-kernel, linux-tegra

Add a driver for panel used in LG Optimus 2X P990. Panel is 4" WVGA MIPI
DBI Type B linked to DRM encoder via RGB to DBI bridge.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/gpu/drm/panel/Kconfig                 |  14 +
 drivers/gpu/drm/panel/Makefile                |   1 +
 .../drm/panel/panel-hitachi-tx10d07vm0baa.c   | 294 ++++++++++++++++++
 3 files changed, 309 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-hitachi-tx10d07vm0baa.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 7a520b4d60fef..0e86628bcdb80 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -274,6 +274,20 @@ config DRM_PANEL_HIMAX_HX8394
 
 	  If M is selected the module will be called panel-himax-hx8394.
 
+config DRM_PANEL_HITACHI_TX10D07VM0BAA
+	tristate "Hitachi TX10D07VM0BAA MIPI DBI Type B panel"
+	depends on OF
+	depends on BACKLIGHT_CLASS_DEVICE
+	select DRM_MIPI_DBI
+	select VIDEOMODE_HELPERS
+	help
+	  Say Y here if you want to enable support for the HITACHI
+	  TX10D07VM0BAA MIPI DBI Type B panel found in the LG Optimus 2X
+	  P990 smartphone.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called panel-hitachi-tx10d07vm0baa.
+
 config DRM_PANEL_HYDIS_HV101HD1
 	tristate "Hydis HV101HD1 panel"
 	depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index da5175909d540..2f57e350311f3 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112A) += panel-himax-hx83112a.o
 obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112B) += panel-himax-hx83112b.o
 obj-$(CONFIG_DRM_PANEL_HIMAX_HX83121A) += panel-himax-hx83121a.o
 obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o
+obj-$(CONFIG_DRM_PANEL_HITACHI_TX10D07VM0BAA) += panel-hitachi-tx10d07vm0baa.o
 obj-$(CONFIG_DRM_PANEL_HYDIS_HV101HD1) += panel-hydis-hv101hd1.o
 obj-$(CONFIG_DRM_PANEL_ILITEK_ILI7807S) += panel-ilitek-ili7807s.o
 obj-$(CONFIG_DRM_PANEL_ILITEK_ILI7836A) += panel-ilitek-ili7836a.o
diff --git a/drivers/gpu/drm/panel/panel-hitachi-tx10d07vm0baa.c b/drivers/gpu/drm/panel/panel-hitachi-tx10d07vm0baa.c
new file mode 100644
index 0000000000000..5ccc8d4725bc6
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-hitachi-tx10d07vm0baa.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/array_size.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_mipi_dbi_bus.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+static const struct regulator_bulk_data hitachi_tx10d07vm0baa_supplies[] = {
+	{ .supply = "avci" }, { .supply = "iovcc" },
+};
+
+struct hitachi_tx10d07vm0baa {
+	struct drm_panel panel;
+	struct mipi_dbi_device *dbi;
+
+	struct regulator_bulk_data *supplies;
+	struct gpio_desc *reset_gpio;
+};
+
+static inline struct hitachi_tx10d07vm0baa *to_hitachi_tx10d07vm0baa(struct drm_panel *panel)
+{
+	return container_of(panel, struct hitachi_tx10d07vm0baa, panel);
+}
+
+#define panel_dbi_command(dbidev, cmd, seq...) \
+({ \
+	const u8 d[] = { seq }; \
+	struct device *dev = &(dbidev)->dev; \
+	int ret; \
+	ret = mipi_dbi_command_stackbuf((dbidev)->dbi, cmd, d, ARRAY_SIZE(d)); \
+	if (ret) \
+		dev_err_ratelimited(dev, "error %d when sending command %#02x\n", ret, cmd); \
+	ret; \
+})
+
+static int hitachi_tx10d07vm0baa_prepare(struct drm_panel *panel)
+{
+	struct hitachi_tx10d07vm0baa *priv = to_hitachi_tx10d07vm0baa(panel);
+	struct device *dev = panel->dev;
+	int ret;
+
+	gpiod_set_value_cansleep(priv->reset_gpio, 1);
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(hitachi_tx10d07vm0baa_supplies), priv->supplies);
+	if (ret) {
+		dev_err(dev, "failed to enable power supplies: %d\n", ret);
+		return ret;
+	}
+
+	usleep_range(1000, 2000);
+
+	gpiod_set_value_cansleep(priv->reset_gpio, 0);
+
+	usleep_range(10000, 11000);
+
+	return 0;
+};
+
+static int hitachi_tx10d07vm0baa_enable(struct drm_panel *panel)
+{
+	struct hitachi_tx10d07vm0baa *priv = to_hitachi_tx10d07vm0baa(panel);
+
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_PARTIAL_ROWS, 0x00, 0x00, 0x03, 0x1f);
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_SCROLL_AREA, 0x00, 0x00, 0x03, 0x20,
+			  0x00, 0x00);
+
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_ADDRESS_MODE, 0x0a);
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_SCROLL_START, 0x00, 0x00);
+
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_24BIT);
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_TEAR_SCANLINE, 0x00, 0x00);
+
+	panel_dbi_command(priv->dbi, 0x71, 0x00);		/* Ex_Vsync_en */
+
+	panel_dbi_command(priv->dbi, 0xb2, 0x00);		/* VCSEL */
+	panel_dbi_command(priv->dbi, 0xb4, 0xaa);		/* setvgmpm */
+	panel_dbi_command(priv->dbi, 0xb5, 0x33);		/* rbias1 */
+	panel_dbi_command(priv->dbi, 0xb6, 0x03);		/* rbias2 */
+
+	panel_dbi_command(priv->dbi, 0xb7, 0x1a, 0x33, 0x03, 0x03,
+			  0x03, 0x00, 0x00, 0x01, 0x02, 0x00,
+			  0x00, 0x04, 0x00, 0x01, 0x01, 0x01);	/* set_ddvdhp */
+	panel_dbi_command(priv->dbi, 0xb8, 0x1c, 0x53, 0x03, 0x03,
+			  0x00, 0x01, 0x02, 0x00, 0x00, 0x04,
+			  0x00, 0x01, 0x01);			/* set_ddvdhm */
+
+	panel_dbi_command(priv->dbi, 0xb9, 0x0a, 0x01, 0x01, 0x00,
+			  0x00, 0x00, 0x02, 0x00, 0x02, 0x01);	/* set_vgh */
+	panel_dbi_command(priv->dbi, 0xba, 0x0f, 0x01, 0x01, 0x00,
+			  0x00, 0x00, 0x02, 0x00, 0x02, 0x01);	/* set_vgl */
+	panel_dbi_command(priv->dbi, 0xbb, 0x00, 0x00, 0x00, 0x00,
+			  0x01, 0x02, 0x01);			/* set_vcl */
+
+	panel_dbi_command(priv->dbi, 0xc1, 0x01);		/* number of lines */
+	panel_dbi_command(priv->dbi, 0xc2, 0x08);		/* number of fp lines */
+	panel_dbi_command(priv->dbi, 0xc3, 0x04);		/* gateset(1) */
+	panel_dbi_command(priv->dbi, 0xc4, 0x4c);		/* 1h period */
+	panel_dbi_command(priv->dbi, 0xc5, 0x03);		/* source precharge */
+	panel_dbi_command(priv->dbi, 0xc6, 0xc4, 0x04);	/* source precharge timing */
+	panel_dbi_command(priv->dbi, 0xc7, 0x00);		/* source level */
+	panel_dbi_command(priv->dbi, 0xc8, 0x02);		/* number of bp lines */
+	panel_dbi_command(priv->dbi, 0xc9, 0x10);		/* gateset(2) */
+	panel_dbi_command(priv->dbi, 0xca, 0x04, 0x04);	/* gateset(3) */
+	panel_dbi_command(priv->dbi, 0xcb, 0x03);		/* gateset(4) */
+	panel_dbi_command(priv->dbi, 0xcc, 0x12);		/* gateset(5) */
+	panel_dbi_command(priv->dbi, 0xcd, 0x12);		/* gateset(6) */
+	panel_dbi_command(priv->dbi, 0xce, 0x30);		/* gateset(7) */
+	panel_dbi_command(priv->dbi, 0xcf, 0x30);		/* gateset(8) */
+	panel_dbi_command(priv->dbi, 0xd0, 0x40);		/* gateset(9) */
+	panel_dbi_command(priv->dbi, 0xd1, 0x22);		/* flhw */
+	panel_dbi_command(priv->dbi, 0xd2, 0x22);		/* vckhw */
+	panel_dbi_command(priv->dbi, 0xd3, 0x04);		/* flt */
+	panel_dbi_command(priv->dbi, 0xd4, 0x14);		/* tctrl */
+	panel_dbi_command(priv->dbi, 0xd6, 0x02);		/* dotinv */
+	panel_dbi_command(priv->dbi, 0xd7, 0x00);		/* on/off sequence period */
+
+	panel_dbi_command(priv->dbi, 0xd8, 0x01, 0x05, 0x06, 0x0d,
+			  0x18, 0x09, 0x22, 0x23, 0x00);		/* ponseqa */
+	panel_dbi_command(priv->dbi, 0xd9, 0x24, 0x01);			/* ponseqb */
+	panel_dbi_command(priv->dbi, 0xde, 0x09, 0x0f, 0x21, 0x12,
+			  0x04);					/* ponseqc */
+
+	panel_dbi_command(priv->dbi, 0xdf, 0x02, 0x06, 0x06, 0x06,
+			  0x06, 0x00);					/* pofseqa */
+	panel_dbi_command(priv->dbi, 0xe0, 0x01);				/* pofseqb */
+
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0xff);
+	panel_dbi_command(priv->dbi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x40);
+
+	panel_dbi_command(priv->dbi, 0xe2, 0x00, 0x00);		/* cabc pwm */
+	panel_dbi_command(priv->dbi, 0xe3, 0x03);			/* cabc */
+	panel_dbi_command(priv->dbi, 0xe4, 0x66, 0x7b, 0x90, 0xa5,
+			  0xbb, 0xc7, 0xe1, 0xe5);		/* cabc brightness */
+	panel_dbi_command(priv->dbi, 0xe5, 0xc5, 0xc5, 0xc9, 0xc9,
+			  0xd1, 0xe1, 0xf1, 0xfe);		/* cabc brightness */
+	panel_dbi_command(priv->dbi, 0xe7, 0x2a);			/* cabc */
+	panel_dbi_command(priv->dbi, 0xe8, 0x00);			/* brt_rev */
+	panel_dbi_command(priv->dbi, 0xe9, 0x00);			/* tefreq */
+
+	panel_dbi_command(priv->dbi, 0xea, 0x01);			/* high speed ram */
+
+	panel_dbi_command(priv->dbi, 0xeb, 0x00, 0x33, 0x0e, 0x15,
+			  0xb7, 0x78, 0x88, 0x0f);		/* gamma setting r pos */
+	panel_dbi_command(priv->dbi, 0xec, 0x00, 0x33, 0x0e, 0x15,
+			  0xb7, 0x78, 0x88, 0x0f);		/* gamma setting r neg */
+	panel_dbi_command(priv->dbi, 0xed, 0x00, 0x33, 0x0e, 0x15,
+			  0xb7, 0x78, 0x88, 0x0f);		/* gamma setting g pos */
+	panel_dbi_command(priv->dbi, 0xee, 0x00, 0x33, 0x0e, 0x15,
+			  0xb7, 0x78, 0x88, 0x0f);		/* gamma setting g neg */
+	panel_dbi_command(priv->dbi, 0xef, 0x00, 0x33, 0x0e, 0x15,
+			  0xb7, 0x78, 0x88, 0x0f);		/* gamma setting b pos */
+	panel_dbi_command(priv->dbi, 0xf0, 0x00, 0x33, 0x0e, 0x15,
+			  0xb7, 0x78, 0x88, 0x0f);		/* gamma setting b neg */
+
+	panel_dbi_command(priv->dbi, MIPI_DCS_EXIT_SLEEP_MODE);
+	msleep(110);
+
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_DISPLAY_ON);
+
+	return 0;
+}
+
+static int hitachi_tx10d07vm0baa_disable(struct drm_panel *panel)
+{
+	struct hitachi_tx10d07vm0baa *priv = to_hitachi_tx10d07vm0baa(panel);
+
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_DISPLAY_OFF);
+	msleep(35);
+
+	panel_dbi_command(priv->dbi, MIPI_DCS_ENTER_SLEEP_MODE);
+	msleep(50);
+
+	return 0;
+}
+
+static int hitachi_tx10d07vm0baa_unprepare(struct drm_panel *panel)
+{
+	struct hitachi_tx10d07vm0baa *priv = to_hitachi_tx10d07vm0baa(panel);
+
+	gpiod_set_value_cansleep(priv->reset_gpio, 1);
+	regulator_bulk_disable(ARRAY_SIZE(hitachi_tx10d07vm0baa_supplies), priv->supplies);
+
+	msleep(50);
+
+	return 0;
+}
+
+static const struct drm_display_mode hitachi_tx10d07vm0baa_mode = {
+	.clock = (480 + 10 + 10 + 10) * (800 + 4 + 4 + 4) * 60 / 1000,
+	.hdisplay = 480,
+	.hsync_start = 480 + 10,
+	.hsync_end = 480 + 10 + 10,
+	.htotal = 480 + 10 + 10 + 10,
+	.vdisplay = 800,
+	.vsync_start = 800 + 4,
+	.vsync_end = 800 + 4 + 4,
+	.vtotal = 800 + 4 + 4 + 4,
+	.width_mm = 52,
+	.height_mm = 86,
+	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static int hitachi_tx10d07vm0baa_get_modes(struct drm_panel *panel,
+					   struct drm_connector *connector)
+{
+	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+	drm_display_info_set_bus_formats(&connector->display_info,
+					 &bus_format, 1);
+
+	connector->display_info.bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE |
+					    DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE |
+					    DRM_BUS_FLAG_DE_HIGH |
+					    DRM_BUS_FLAG_DATA_LSB_TO_MSB;
+
+	return drm_connector_helper_get_modes_fixed(connector, &hitachi_tx10d07vm0baa_mode);
+}
+
+static const struct drm_panel_funcs hitachi_tx10d07vm0baa_panel_funcs = {
+	.prepare = hitachi_tx10d07vm0baa_prepare,
+	.enable = hitachi_tx10d07vm0baa_enable,
+	.disable = hitachi_tx10d07vm0baa_disable,
+	.unprepare = hitachi_tx10d07vm0baa_unprepare,
+	.get_modes = hitachi_tx10d07vm0baa_get_modes,
+};
+
+static int hitachi_tx10d07vm0baa_probe(struct mipi_dbi_device *dbi)
+{
+	struct hitachi_tx10d07vm0baa *priv;
+	struct device *dev = &dbi->dev;
+	int ret;
+
+	priv = devm_drm_panel_alloc(dev, struct hitachi_tx10d07vm0baa, panel,
+				    &hitachi_tx10d07vm0baa_panel_funcs, DRM_MODE_CONNECTOR_DPI);
+	if (IS_ERR(priv))
+		return PTR_ERR(priv);
+
+	ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(hitachi_tx10d07vm0baa_supplies),
+					    hitachi_tx10d07vm0baa_supplies, &priv->supplies);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get supplies\n");
+
+	priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(priv->reset_gpio))
+		return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),
+				     "Failed to get reset gpio\n");
+
+	priv->dbi = dbi;
+	mipi_dbi_set_drvdata(dbi, priv);
+
+	ret = drm_panel_of_backlight(&priv->panel);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get backlight\n");
+
+	ret = devm_drm_panel_add(dev, &priv->panel);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to add panel\n");
+
+	return devm_mipi_dbi_attach(dev, dbi);
+}
+
+static const struct of_device_id hitachi_tx10d07vm0baa_of_match[] = {
+	{ .compatible = "hit,tx10d07vm0baa" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, hitachi_tx10d07vm0baa_of_match);
+
+static struct mipi_dbi_driver hitachi_tx10d07vm0baa_driver = {
+	.driver = {
+		.name = "panel-hitachi-tx10d07vm0baa",
+		.of_match_table = hitachi_tx10d07vm0baa_of_match,
+	},
+	.probe = hitachi_tx10d07vm0baa_probe,
+};
+module_mipi_dbi_driver(hitachi_tx10d07vm0baa_driver);
+
+MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("HITACHI TX10D07VM0BAA MIPI DBI Type B panel driver");
+MODULE_LICENSE("GPL");
-- 
2.53.0


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

* [PATCH v5 04/11] drm/panel: Add LG LH400WV3-SD04 MIPI DBI Type B panel driver
  2026-10-29 15:28 [PATCH v5 00/11] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
                   ` (2 preceding siblings ...)
  2026-10-29 15:29 ` [PATCH v5 03/11] drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver Svyatoslav Ryhel
@ 2026-10-29 15:29 ` Svyatoslav Ryhel
  2026-10-29 15:29 ` [PATCH v5 05/11] drm/tegra: dc: Refactor and expand header Svyatoslav Ryhel
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 15+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-29 15:29 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
  Cc: dri-devel, devicetree, linux-kernel, linux-tegra

Add a driver for panel used in LG Optimus 2X P990. Panel is 4" WVGA MIPI
DBI Type B linked to DRM encoder via RGB to DBI bridge.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/gpu/drm/panel/Kconfig             |  13 ++
 drivers/gpu/drm/panel/Makefile            |   1 +
 drivers/gpu/drm/panel/panel-lg-lh400wv3.c | 233 ++++++++++++++++++++++
 3 files changed, 247 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-lg-lh400wv3.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 0e86628bcdb80..036602e577953 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -554,6 +554,19 @@ config DRM_PANEL_LG_LG4573
 	  Say Y here if you want to enable support for LG4573 RGB panel.
 	  To compile this driver as a module, choose M here.
 
+config DRM_PANEL_LG_LH400WV3
+	tristate "LG LH400WV3 MIPI DBI Type B panel"
+	depends on OF
+	depends on BACKLIGHT_CLASS_DEVICE
+	select DRM_MIPI_DBI
+	select VIDEOMODE_HELPERS
+	help
+	  Say Y here if you want to enable support for the LG LH400WV3 MIPI
+	  DBI Type B panel found in the LG Optimus 2X P990 smartphone.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called panel-lg-lh400wv3.
+
 config DRM_PANEL_LG_SW43408
 	tristate "LG SW43408 panel"
 	depends on GPIOLIB
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 2f57e350311f3..bed77ae90b660 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_DRM_PANEL_LINCOLNTECH_LCD197) += panel-lincolntech-lcd197.o
 obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o
 obj-$(CONFIG_DRM_PANEL_LG_LD070WX3) += panel-lg-ld070wx3.o
 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
+obj-$(CONFIG_DRM_PANEL_LG_LH400WV3) += panel-lg-lh400wv3.o
 obj-$(CONFIG_DRM_PANEL_LG_SW43408) += panel-lg-sw43408.o
 obj-$(CONFIG_DRM_PANEL_LXD_M9189A) += panel-lxd-m9189a.o
 obj-$(CONFIG_DRM_PANEL_MAGNACHIP_D53E6EA8966) += panel-magnachip-d53e6ea8966.o
diff --git a/drivers/gpu/drm/panel/panel-lg-lh400wv3.c b/drivers/gpu/drm/panel/panel-lg-lh400wv3.c
new file mode 100644
index 0000000000000..8a11049948635
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-lg-lh400wv3.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/array_size.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_mipi_dbi_bus.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+static const struct regulator_bulk_data lg_lh400wv3_supplies[] = {
+	{ .supply = "avci" }, { .supply = "iovcc" },
+};
+
+struct lg_lh400wv3 {
+	struct drm_panel panel;
+	struct mipi_dbi_device *dbi;
+
+	struct regulator_bulk_data *supplies;
+	struct gpio_desc *reset_gpio;
+};
+
+static inline struct lg_lh400wv3 *to_lg_lh400wv3(struct drm_panel *panel)
+{
+	return container_of(panel, struct lg_lh400wv3, panel);
+}
+
+#define panel_dbi_command(dbidev, cmd, seq...) \
+({ \
+	const u8 d[] = { seq }; \
+	struct device *dev = &(dbidev)->dev; \
+	int ret; \
+	ret = mipi_dbi_command_stackbuf((dbidev)->dbi, cmd, d, ARRAY_SIZE(d)); \
+	if (ret) \
+		dev_err_ratelimited(dev, "error %d when sending command %#02x\n", ret, cmd); \
+	ret; \
+})
+
+static int lg_lh400wv3_prepare(struct drm_panel *panel)
+{
+	struct lg_lh400wv3 *priv = to_lg_lh400wv3(panel);
+	struct device *dev = panel->dev;
+	int ret;
+
+	gpiod_set_value_cansleep(priv->reset_gpio, 1);
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(lg_lh400wv3_supplies), priv->supplies);
+	if (ret) {
+		dev_err(dev, "failed to enable power supplies: %d\n", ret);
+		return ret;
+	}
+
+	usleep_range(1000, 2000);
+
+	gpiod_set_value_cansleep(priv->reset_gpio, 0);
+
+	usleep_range(10000, 11000);
+
+	return 0;
+};
+
+static int lg_lh400wv3_enable(struct drm_panel *panel)
+{
+	struct lg_lh400wv3 *priv = to_lg_lh400wv3(panel);
+
+	panel_dbi_command(priv->dbi, MIPI_DCS_EXIT_INVERT_MODE);
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_TEAR_ON);
+
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_24BIT |
+			  MIPI_DCS_PIXEL_FMT_24BIT << 4);
+
+	panel_dbi_command(priv->dbi, 0xb2, 0x00, 0xc8);
+	panel_dbi_command(priv->dbi, 0xb3, 0x00);
+	panel_dbi_command(priv->dbi, 0xb4, 0x04);
+	panel_dbi_command(priv->dbi, 0xb5, 0x42, 0x10, 0x10, 0x00, 0x20);
+	panel_dbi_command(priv->dbi, 0xb6, 0x0b, 0x0f, 0x3c, 0x13, 0x13, 0xe8);
+	panel_dbi_command(priv->dbi, 0xb7, 0x4c, 0x06, 0x0c, 0x00, 0x00);
+
+	panel_dbi_command(priv->dbi, 0xc0, 0x01, 0x11);
+	panel_dbi_command(priv->dbi, 0xc3, 0x07, 0x03, 0x04, 0x04, 0x04);
+	panel_dbi_command(priv->dbi, 0xc4, 0x12, 0x24, 0x18, 0x18, 0x02, 0x49);
+	panel_dbi_command(priv->dbi, 0xc5, 0x65);
+	panel_dbi_command(priv->dbi, 0xc6, 0x41, 0x63);
+
+	panel_dbi_command(priv->dbi, 0xd0, 0x00, 0x46, 0x74, 0x32, 0x1d, 0x03, 0x51, 0x15, 0x04);
+	panel_dbi_command(priv->dbi, 0xd1, 0x00, 0x46, 0x74, 0x32, 0x1d, 0x03, 0x51, 0x15, 0x04);
+	panel_dbi_command(priv->dbi, 0xd2, 0x00, 0x46, 0x74, 0x32, 0x1f, 0x03, 0x51, 0x15, 0x04);
+	panel_dbi_command(priv->dbi, 0xd3, 0x00, 0x46, 0x74, 0x32, 0x1f, 0x03, 0x51, 0x15, 0x04);
+	panel_dbi_command(priv->dbi, 0xd4, 0x01, 0x46, 0x74, 0x25, 0x00, 0x03, 0x51, 0x15, 0x04);
+	panel_dbi_command(priv->dbi, 0xd5, 0x01, 0x46, 0x74, 0x25, 0x00, 0x03, 0x51, 0x15, 0x04);
+
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0xdf);
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_PAGE_ADDRESS, 0x00, 0x00, 0x03, 0x1f);
+
+	panel_dbi_command(priv->dbi, MIPI_DCS_EXIT_SLEEP_MODE);
+	msleep(120);
+
+	panel_dbi_command(priv->dbi, MIPI_DCS_WRITE_MEMORY_START);
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_DISPLAY_ON);
+
+	return 0;
+}
+
+static int lg_lh400wv3_disable(struct drm_panel *panel)
+{
+	struct lg_lh400wv3 *priv = to_lg_lh400wv3(panel);
+
+	panel_dbi_command(priv->dbi, MIPI_DCS_SET_DISPLAY_OFF);
+	panel_dbi_command(priv->dbi, MIPI_DCS_ENTER_SLEEP_MODE);
+	msleep(150);
+
+	panel_dbi_command(priv->dbi, 0xc1, 0x01);
+	usleep_range(10000, 11000);
+
+	return 0;
+};
+
+static int lg_lh400wv3_unprepare(struct drm_panel *panel)
+{
+	struct lg_lh400wv3 *priv = to_lg_lh400wv3(panel);
+
+	gpiod_set_value_cansleep(priv->reset_gpio, 1);
+	regulator_bulk_disable(ARRAY_SIZE(lg_lh400wv3_supplies), priv->supplies);
+
+	msleep(50);
+
+	return 0;
+}
+
+static const struct drm_display_mode lg_lh400wv3_mode = {
+	.clock = (480 + 10 + 10 + 10) * (800 + 4 + 4 + 4) * 60 / 1000,
+	.hdisplay = 480,
+	.hsync_start = 480 + 10,
+	.hsync_end = 480 + 10 + 10,
+	.htotal = 480 + 10 + 10 + 10,
+	.vdisplay = 800,
+	.vsync_start = 800 + 4,
+	.vsync_end = 800 + 4 + 4,
+	.vtotal = 800 + 4 + 4 + 4,
+	.width_mm = 52,
+	.height_mm = 86,
+	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static int lg_lh400wv3_get_modes(struct drm_panel *panel,
+				 struct drm_connector *connector)
+{
+	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+	drm_display_info_set_bus_formats(&connector->display_info,
+					 &bus_format, 1);
+
+	connector->display_info.bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE |
+					    DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE |
+					    DRM_BUS_FLAG_DE_HIGH |
+					    DRM_BUS_FLAG_DATA_LSB_TO_MSB;
+
+	return drm_connector_helper_get_modes_fixed(connector, &lg_lh400wv3_mode);
+}
+
+static const struct drm_panel_funcs lg_lh400wv3_panel_funcs = {
+	.prepare = lg_lh400wv3_prepare,
+	.enable = lg_lh400wv3_enable,
+	.disable = lg_lh400wv3_disable,
+	.unprepare = lg_lh400wv3_unprepare,
+	.get_modes = lg_lh400wv3_get_modes,
+};
+
+static int lg_lh400wv3_probe(struct mipi_dbi_device *dbi)
+{
+	struct device *dev = &dbi->dev;
+	struct lg_lh400wv3 *priv;
+	int ret;
+
+	priv = devm_drm_panel_alloc(dev, struct lg_lh400wv3, panel,
+				    &lg_lh400wv3_panel_funcs, DRM_MODE_CONNECTOR_DPI);
+	if (IS_ERR(priv))
+		return PTR_ERR(priv);
+
+	ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(lg_lh400wv3_supplies),
+					    lg_lh400wv3_supplies, &priv->supplies);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get supplies\n");
+
+	priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(priv->reset_gpio))
+		return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),
+				     "Failed to get reset gpio\n");
+
+	priv->dbi = dbi;
+	mipi_dbi_set_drvdata(dbi, priv);
+
+	ret = drm_panel_of_backlight(&priv->panel);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get backlight\n");
+
+	ret = devm_drm_panel_add(dev, &priv->panel);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to add panel\n");
+
+	return devm_mipi_dbi_attach(dev, dbi);
+}
+
+static const struct of_device_id lg_lh400wv3_of_match[] = {
+	{ .compatible = "lg,lh400wv3-sd04" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lg_lh400wv3_of_match);
+
+static struct mipi_dbi_driver lg_lh400wv3_driver = {
+	.driver = {
+		.name = "panel-lg-lh400wv3",
+		.of_match_table = lg_lh400wv3_of_match,
+	},
+	.probe = lg_lh400wv3_probe,
+};
+module_mipi_dbi_driver(lg_lh400wv3_driver);
+
+MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("LG LH400WV3-SD04 MIPI DBI Type B panel driver");
+MODULE_LICENSE("GPL");
-- 
2.53.0


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

* [PATCH v5 05/11] drm/tegra: dc: Refactor and expand header
  2026-10-29 15:28 [PATCH v5 00/11] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
                   ` (3 preceding siblings ...)
  2026-10-29 15:29 ` [PATCH v5 04/11] drm/panel: Add LG LH400WV3-SD04 " Svyatoslav Ryhel
@ 2026-10-29 15:29 ` Svyatoslav Ryhel
  2026-10-29 15:29 ` [PATCH v5 06/11] dt-bindings: display: tegra: Document Tegra20 DSI port Svyatoslav Ryhel
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 15+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-29 15:29 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
  Cc: dri-devel, devicetree, linux-kernel, linux-tegra

Refactor and expand existing DC register definitions with additional
fields in preparation for adding the 8-bit CPU interface.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/gpu/drm/tegra/dc.c  |  11 +-
 drivers/gpu/drm/tegra/dc.h  | 856 +++++++++++++++++++++---------------
 drivers/gpu/drm/tegra/rgb.c |   8 +-
 3 files changed, 514 insertions(+), 361 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index b0bfa946e6979..5bbf2c028edd8 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1591,10 +1591,10 @@ static const struct debugfs_reg32 tegra_dc_regs[] = {
 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)),
 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)),
 	DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL),
-	DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL),
-	DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE),
-	DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL),
-	DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE),
+	DEBUGFS_REG32(DC_COM_PM_CONTROL(0)),
+	DEBUGFS_REG32(DC_COM_PM_DUTY_CYCLE(0)),
+	DEBUGFS_REG32(DC_COM_PM_CONTROL(1)),
+	DEBUGFS_REG32(DC_COM_PM_DUTY_CYCLE(1)),
 	DEBUGFS_REG32(DC_COM_SPI_CONTROL),
 	DEBUGFS_REG32(DC_COM_SPI_START_BYTE),
 	DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB),
@@ -2384,7 +2384,8 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
 
 	if (dc->rgb) {
 		/* XXX: parameterize? */
-		value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE;
+		value = SC0_H_QUALIFIER(SC_H_QUALIFIER_NONE) |
+			SC1_H_QUALIFIER(SC_H_QUALIFIER_NONE);
 		tegra_dc_writel(dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
 	}
 
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index 0cb0515968b35..2b87bb675d4cf 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -178,9 +178,11 @@ int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc);
 int tegra_dc_rgb_exit(struct tegra_dc *dc);
 
 #define DC_CMD_GENERAL_INCR_SYNCPT		0x000
+
 #define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL	0x001
-#define  SYNCPT_CNTRL_NO_STALL   (1 << 8)
-#define  SYNCPT_CNTRL_SOFT_RESET (1 << 0)
+#define  SYNCPT_CNTRL_NO_STALL			BIT(8)
+#define  SYNCPT_CNTRL_SOFT_RESET		BIT(0)
+
 #define DC_CMD_GENERAL_INCR_SYNCPT_ERROR	0x002
 #define DC_CMD_WIN_A_INCR_SYNCPT		0x008
 #define DC_CMD_WIN_A_INCR_SYNCPT_CNTRL		0x009
@@ -191,101 +193,226 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define DC_CMD_WIN_C_INCR_SYNCPT		0x018
 #define DC_CMD_WIN_C_INCR_SYNCPT_CNTRL		0x019
 #define DC_CMD_WIN_C_INCR_SYNCPT_ERROR		0x01a
+
 #define DC_CMD_CONT_SYNCPT_VSYNC		0x028
-#define  SYNCPT_VSYNC_ENABLE (1 << 8)
+#define  SYNCPT_VSYNC_ENABLE			BIT(8)
+
 #define DC_CMD_DISPLAY_COMMAND_OPTION0		0x031
+
 #define DC_CMD_DISPLAY_COMMAND			0x032
-#define DISP_CTRL_MODE_STOP (0 << 5)
-#define DISP_CTRL_MODE_C_DISPLAY (1 << 5)
-#define DISP_CTRL_MODE_NC_DISPLAY (2 << 5)
-#define DISP_CTRL_MODE_MASK (3 << 5)
+#define  DISP_CTRL_MODE_STOP			(0 << 5)
+#define  DISP_CTRL_MODE_C_DISPLAY		(1 << 5)
+#define  DISP_CTRL_MODE_NC_DISPLAY		(2 << 5)
+#define  DISP_CTRL_MODE_MASK			(3 << 5)
+
 #define DC_CMD_SIGNAL_RAISE			0x033
+
 #define DC_CMD_DISPLAY_POWER_CONTROL		0x036
-#define PW0_ENABLE (1 <<  0)
-#define PW1_ENABLE (1 <<  2)
-#define PW2_ENABLE (1 <<  4)
-#define PW3_ENABLE (1 <<  6)
-#define PW4_ENABLE (1 <<  8)
-#define PM0_ENABLE (1 << 16)
-#define PM1_ENABLE (1 << 18)
+#define  PW0_ENABLE				BIT(0)
+#define  PW1_ENABLE				BIT(2)
+#define  PW2_ENABLE				BIT(4)
+#define  PW3_ENABLE				BIT(6)
+#define  PW4_ENABLE				BIT(8)
+#define  PM0_ENABLE				BIT(16)
+#define  PM1_ENABLE				BIT(18)
 
 #define DC_CMD_INT_STATUS			0x037
 #define DC_CMD_INT_MASK				0x038
 #define DC_CMD_INT_ENABLE			0x039
 #define DC_CMD_INT_TYPE				0x03a
+
 #define DC_CMD_INT_POLARITY			0x03b
-#define CTXSW_INT                (1 << 0)
-#define FRAME_END_INT            (1 << 1)
-#define VBLANK_INT               (1 << 2)
-#define V_PULSE3_INT             (1 << 4)
-#define V_PULSE2_INT             (1 << 5)
-#define REGION_CRC_INT           (1 << 6)
-#define REG_TMOUT_INT            (1 << 7)
-#define WIN_A_UF_INT             (1 << 8)
-#define WIN_B_UF_INT             (1 << 9)
-#define WIN_C_UF_INT             (1 << 10)
-#define MSF_INT                  (1 << 12)
-#define WIN_A_OF_INT             (1 << 14)
-#define WIN_B_OF_INT             (1 << 15)
-#define WIN_C_OF_INT             (1 << 16)
-#define HEAD_UF_INT              (1 << 23)
-#define SD3_BUCKET_WALK_DONE_INT (1 << 24)
-#define DSC_OBUF_UF_INT          (1 << 26)
-#define DSC_RBUF_UF_INT          (1 << 27)
-#define DSC_BBUF_UF_INT          (1 << 28)
-#define DSC_TO_UF_INT            (1 << 29)
+#define  CTXSW_INT				BIT(0)
+#define  FRAME_END_INT				BIT(1)
+#define  VBLANK_INT				BIT(2)
+#define  V_PULSE3_INT				BIT(4)
+#define  V_PULSE2_INT				BIT(5)
+#define  REGION_CRC_INT				BIT(6)
+#define  REG_TMOUT_INT				BIT(7)
+#define  WIN_A_UF_INT				BIT(8)
+#define  WIN_B_UF_INT				BIT(9)
+#define  WIN_C_UF_INT				BIT(10)
+#define  MSF_INT				BIT(12)
+#define  WIN_A_OF_INT				BIT(14)
+#define  WIN_B_OF_INT				BIT(15)
+#define  WIN_C_OF_INT				BIT(16)
+#define  HEAD_UF_INT				BIT(23)
+#define  SD3_BUCKET_WALK_DONE_INT		BIT(24)
+#define  DSC_OBUF_UF_INT			BIT(26)
+#define  DSC_RBUF_UF_INT			BIT(27)
+#define  DSC_BBUF_UF_INT			BIT(28)
+#define  DSC_TO_UF_INT				BIT(29)
 
 #define DC_CMD_SIGNAL_RAISE1			0x03c
 #define DC_CMD_SIGNAL_RAISE2			0x03d
 #define DC_CMD_SIGNAL_RAISE3			0x03e
 
 #define DC_CMD_STATE_ACCESS			0x040
-#define READ_MUX  (1 << 0)
-#define WRITE_MUX (1 << 2)
+#define  READ_MUX				BIT(0)
+#define  WRITE_MUX				BIT(2)
 
 #define DC_CMD_STATE_CONTROL			0x041
-#define GENERAL_ACT_REQ (1 <<  0)
-#define WIN_A_ACT_REQ   (1 <<  1)
-#define WIN_B_ACT_REQ   (1 <<  2)
-#define WIN_C_ACT_REQ   (1 <<  3)
-#define CURSOR_ACT_REQ  (1 <<  7)
-#define GENERAL_UPDATE  (1 <<  8)
-#define WIN_A_UPDATE    (1 <<  9)
-#define WIN_B_UPDATE    (1 << 10)
-#define WIN_C_UPDATE    (1 << 11)
-#define CURSOR_UPDATE   (1 << 15)
-#define COMMON_ACTREQ   (1 << 16)
-#define COMMON_UPDATE   (1 << 17)
-#define NC_HOST_TRIG    (1 << 24)
+#define  GENERAL_ACT_REQ			BIT(0)
+#define  WIN_A_ACT_REQ				BIT(1)
+#define  WIN_B_ACT_REQ				BIT(2)
+#define  WIN_C_ACT_REQ				BIT(3)
+#define  CURSOR_ACT_REQ				BIT(7)
+#define  GENERAL_UPDATE				BIT(8)
+#define  WIN_A_UPDATE				BIT(9)
+#define  WIN_B_UPDATE				BIT(10)
+#define  WIN_C_UPDATE				BIT(11)
+#define  CURSOR_UPDATE				BIT(15)
+#define  COMMON_ACTREQ				BIT(16)
+#define  COMMON_UPDATE				BIT(17)
+#define  NC_HOST_TRIG				BIT(24)
 
 #define DC_CMD_DISPLAY_WINDOW_HEADER		0x042
-#define WINDOW_A_SELECT (1 << 4)
-#define WINDOW_B_SELECT (1 << 5)
-#define WINDOW_C_SELECT (1 << 6)
+#define  WINDOW_A_SELECT			BIT(4)
+#define  WINDOW_B_SELECT			BIT(5)
+#define  WINDOW_C_SELECT			BIT(6)
 
 #define DC_CMD_REG_ACT_CONTROL			0x043
 
 #define DC_COM_CRC_CONTROL			0x300
-#define  DC_COM_CRC_CONTROL_ALWAYS (1 << 3)
-#define  DC_COM_CRC_CONTROL_FULL_FRAME  (0 << 2)
-#define  DC_COM_CRC_CONTROL_ACTIVE_DATA (1 << 2)
-#define  DC_COM_CRC_CONTROL_WAIT (1 << 1)
-#define  DC_COM_CRC_CONTROL_ENABLE (1 << 0)
+#define  DC_COM_CRC_CONTROL_ALWAYS		BIT(3)
+#define  DC_COM_CRC_CONTROL_FULL_FRAME		(0 << 2)
+#define  DC_COM_CRC_CONTROL_ACTIVE_DATA		BIT(2)
+#define  DC_COM_CRC_CONTROL_WAIT		BIT(1)
+#define  DC_COM_CRC_CONTROL_ENABLE		BIT(0)
+
 #define DC_COM_CRC_CHECKSUM			0x301
-#define DC_COM_PIN_OUTPUT_ENABLE(x) (0x302 + (x))
-#define DC_COM_PIN_OUTPUT_POLARITY(x) (0x306 + (x))
-#define LVS_OUTPUT_POLARITY_LOW (1 << 28)
-#define LHS_OUTPUT_POLARITY_LOW (1 << 30)
-#define DC_COM_PIN_OUTPUT_DATA(x) (0x30a + (x))
-#define DC_COM_PIN_INPUT_ENABLE(x) (0x30e + (x))
-#define DC_COM_PIN_INPUT_DATA(x) (0x312 + (x))
-#define DC_COM_PIN_OUTPUT_SELECT(x) (0x314 + (x))
+
+/* Defines in PIN_OUTPUT/INPUT_* describe pin position, not state */
+#define DC_COM_PIN_OUTPUT_ENABLE(x)		(0x302 + (x))
+#define DC_COM_PIN_OUTPUT_POLARITY(x)		(0x306 + (x))
+#define DC_COM_PIN_OUTPUT_DATA(x)		(0x30a + (x))
+#define  PIN_UNMASK(x)				BIT((x) + 1)
+#define DC_COM_PIN_INPUT_ENABLE(x)		(0x30e + (x))
+
+/* DC_COM_PIN_OUTPUT/INPUT_*_0 */
+#define PIN_LD0					0
+#define PIN_LD1					2
+#define PIN_LD2					4
+#define PIN_LD3					6
+#define PIN_LD4					8
+#define PIN_LD5					10
+#define PIN_LD6					12
+#define PIN_LD7					14
+#define PIN_LD8					16
+#define PIN_LD9					18
+#define PIN_LD10				20
+#define PIN_LD11				22
+#define PIN_LD12				24
+#define PIN_LD13				26
+#define PIN_LD14				28
+#define PIN_LD15				30
+
+/* DC_COM_PIN_OUTPUT/INPUT_*_1 */
+#define PIN_LD16				0
+#define PIN_LD17				2
+#define PIN_LPW0				16
+#define PIN_LPW1				18
+#define PIN_LPW2				20
+#define PIN_LSC0				24
+#define PIN_LSC1				26
+#define PIN_LVS					28
+#define PIN_LHS					30
+
+/* DC_COM_PIN_OUTPUT/INPUT_*_2 */
+#define PIN_LHP0				0
+#define PIN_LHP1				2
+#define PIN_LHP2				4
+#define PIN_LVP0				8
+#define PIN_LVP1				10
+#define PIN_LM0					16
+#define PIN_LM1					18
+#define PIN_LDI					20
+#define PIN_LPP					22
+
+/* DC_COM_PIN_OUTPUT/INPUT_*_3 */
+#define PIN_LSCK				0
+#define PIN_LSDA				2
+#define PIN_LCSN				4
+#define PIN_LDC					6
+#define PIN_LSPI				8
+#define PIN_LSDI				10
+
+#define DC_COM_PIN_INPUT_DATA(x)		(0x312 + (x))
+#define DC_COM_PIN_OUTPUT_SELECT(x)		(0x314 + (x))
+#define  PIN_OUTPUT_SELECT_MASK(x)		(0x7 << (x))
+#define  PIN_OUTPUT_SELECT_PRIM(x)		(0 << (x))
+#define  PIN_OUTPUT_SELECT_OUT(x)		(1 << (x))
+#define  PIN_OUTPUT_SELECT_F2(x)		(2 << (x))
+#define  PIN_OUTPUT_SELECT_F3(x)		(3 << (x))
+#define  PIN_OUTPUT_SELECT_F4(x)		(4 << (x))
+#define  PIN_OUTPUT_SELECT_F5(x)		(5 << (x))
+#define  PIN_OUTPUT_SELECT_F6(x)		(6 << (x))
+#define  PIN_OUTPUT_SELECT_F7(x)		(7 << (x))
+
+/* DC_COM_PIN_OUTPUT_SELECT0 */
+#define PIN_OUTPUT_SELECT_LD0			0
+#define PIN_OUTPUT_SELECT_LD1			4
+#define PIN_OUTPUT_SELECT_LD2			8
+#define PIN_OUTPUT_SELECT_LD3			12
+#define PIN_OUTPUT_SELECT_LD4			16
+#define PIN_OUTPUT_SELECT_LD5			20
+#define PIN_OUTPUT_SELECT_LD6			24
+#define PIN_OUTPUT_SELECT_LD7			28
+
+/* DC_COM_PIN_OUTPUT_SELECT1 */
+#define PIN_OUTPUT_SELECT_LD8			0
+#define PIN_OUTPUT_SELECT_LD9			4
+#define PIN_OUTPUT_SELECT_LD10			8
+#define PIN_OUTPUT_SELECT_LD11			12
+#define PIN_OUTPUT_SELECT_LD12			16
+#define PIN_OUTPUT_SELECT_LD13			20
+#define PIN_OUTPUT_SELECT_LD14			24
+#define PIN_OUTPUT_SELECT_LD15			28
+
+/* DC_COM_PIN_OUTPUT_SELECT2 */
+#define PIN_OUTPUT_SELECT_LD16			0
+#define PIN_OUTPUT_SELECT_LD17			4
+
+/* DC_COM_PIN_OUTPUT_SELECT3 */
+#define PIN_OUTPUT_SELECT_LPW0			0
+#define PIN_OUTPUT_SELECT_LPW1			4
+#define PIN_OUTPUT_SELECT_LPW2			8
+#define PIN_OUTPUT_SELECT_LSC0			16
+#define PIN_OUTPUT_SELECT_LSC1			20
+#define PIN_OUTPUT_SELECT_LVS			24
+#define PIN_OUTPUT_SELECT_LHS			28
+
+/* DC_COM_PIN_OUTPUT_SELECT4 */
+#define PIN_OUTPUT_SELECT_LHP0			0
+#define PIN_OUTPUT_SELECT_LHP1			4
+#define PIN_OUTPUT_SELECT_LHP2			8
+#define PIN_OUTPUT_SELECT_LVP0			16
+#define PIN_OUTPUT_SELECT_LVP1			20
+
+/* DC_COM_PIN_OUTPUT_SELECT5 */
+#define PIN_OUTPUT_SELECT_LM0			0
+#define PIN_OUTPUT_SELECT_LM1			4
+#define PIN_OUTPUT_SELECT_LDI			8
+#define PIN_OUTPUT_SELECT_LPP			12
+
+/* DC_COM_PIN_OUTPUT_SELECT6 */
+#define PIN_OUTPUT_SELECT_LSCK			0
+#define PIN_OUTPUT_SELECT_LSDA			4
+#define PIN_OUTPUT_SELECT_LCSN			8
+#define PIN_OUTPUT_SELECT_LDC			12
+#define PIN_OUTPUT_SELECT_LSPI			16
+#define PIN_OUTPUT_SELECT_LSDI			20
 
 #define DC_COM_PIN_MISC_CONTROL			0x31b
-#define DC_COM_PIN_PM0_CONTROL			0x31c
-#define DC_COM_PIN_PM0_DUTY_CYCLE		0x31d
-#define DC_COM_PIN_PM1_CONTROL			0x31e
-#define DC_COM_PIN_PM1_DUTY_CYCLE		0x31f
+#define DC_COM_PM_CONTROL(x)			(0x31c + (x) * 2)
+#define   PM_CLOCK_SELECT_SHIFT_DIV		0
+#define   PM_CLOCK_SELECT_PIX_CLK		1
+#define   PM_CLOCK_SELECT_LINE_CLK		2
+#define   PM_CLOCK_SELECT_FRAME_CLK		3
+#define   PM_CLOCK_DIVIDER(x)			(((x) & 0x3f) << 4)
+#define   PM_PERIOD(x)				(((x) & 0x3f) << 18)
+#define DC_COM_PM_DUTY_CYCLE(x)			(0x31d + (x) * 2)
+#define   PM_DUTY_CYCLE_MASK(x)			((x) & 0xff)
 
 #define DC_COM_SPI_CONTROL			0x320
 #define DC_COM_SPI_START_BYTE			0x321
@@ -299,38 +426,43 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define DC_COM_CRC_CHECKSUM_LATCHED		0x329
 
 #define DC_COM_RG_UNDERFLOW			0x365
-#define  UNDERFLOW_MODE_RED      (1 << 8)
-#define  UNDERFLOW_REPORT_ENABLE (1 << 0)
+#define  UNDERFLOW_MODE_RED			BIT(8)
+#define  UNDERFLOW_REPORT_ENABLE		BIT(0)
 
 #define DC_DISP_DISP_SIGNAL_OPTIONS0		0x400
-#define H_PULSE0_ENABLE (1 <<  8)
-#define H_PULSE1_ENABLE (1 << 10)
-#define H_PULSE2_ENABLE (1 << 12)
+#define  H_PULSE0_ENABLE			BIT(8)
+#define  H_PULSE1_ENABLE			BIT(10)
+#define  H_PULSE2_ENABLE			BIT(12)
+#define  V_PULSE0_ENABLE			BIT(16)
+#define  V_PULSE1_ENABLE			BIT(18)
+#define  V_PULSE2_ENABLE			BIT(19)
+#define  V_PULSE3_ENABLE			BIT(20)
+#define  M0_ENABLE				BIT(24)
+#define  M1_ENABLE				BIT(26)
 
 #define DC_DISP_DISP_SIGNAL_OPTIONS1		0x401
 
 #define DC_DISP_DISP_WIN_OPTIONS		0x402
-#define HDMI_ENABLE	(1 << 30)
-#define DSI_ENABLE	(1 << 29)
-#define SOR1_TIMING_CYA	(1 << 27)
-#define CURSOR_ENABLE	(1 << 16)
-
-#define SOR_ENABLE(x)	(1 << (25 + (((x) > 1) ? ((x) + 1) : (x))))
+#define  HDMI_ENABLE				BIT(30)
+#define  DSI_ENABLE				BIT(29)
+#define  SOR1_TIMING_CYA			BIT(27)
+#define  SOR_ENABLE(x)				BIT((25 + (((x) > 1) ? ((x) + 1) : (x))))
+#define  CURSOR_ENABLE				BIT(16)
 
 #define DC_DISP_DISP_MEM_HIGH_PRIORITY		0x403
-#define CURSOR_THRESHOLD(x)   (((x) & 0x03) << 24)
-#define WINDOW_A_THRESHOLD(x) (((x) & 0x7f) << 16)
-#define WINDOW_B_THRESHOLD(x) (((x) & 0x7f) <<  8)
-#define WINDOW_C_THRESHOLD(x) (((x) & 0xff) <<  0)
+#define  CURSOR_THRESHOLD(x)			(((x) & 0x03) << 24)
+#define  WINDOW_A_THRESHOLD(x)			(((x) & 0x7f) << 16)
+#define  WINDOW_B_THRESHOLD(x)			(((x) & 0x7f) <<  8)
+#define  WINDOW_C_THRESHOLD(x)			(((x) & 0xff) <<  0)
 
 #define DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER	0x404
-#define CURSOR_DELAY(x)   (((x) & 0x3f) << 24)
-#define WINDOW_A_DELAY(x) (((x) & 0x3f) << 16)
-#define WINDOW_B_DELAY(x) (((x) & 0x3f) <<  8)
-#define WINDOW_C_DELAY(x) (((x) & 0x3f) <<  0)
+#define  CURSOR_DELAY(x)			(((x) & 0x3f) << 24)
+#define  WINDOW_A_DELAY(x)			(((x) & 0x3f) << 16)
+#define  WINDOW_B_DELAY(x)			(((x) & 0x3f) <<  8)
+#define  WINDOW_C_DELAY(x)			(((x) & 0x3f) <<  0)
 
 #define DC_DISP_DISP_TIMING_OPTIONS		0x405
-#define VSYNC_H_POSITION(x) ((x) & 0xfff)
+#define  VSYNC_H_POSITION(x)			((x) & 0xfff)
 
 #define DC_DISP_REF_TO_SYNC			0x406
 #define DC_DISP_SYNC_WIDTH			0x407
@@ -373,99 +505,105 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define DC_DISP_PP_SELECT_C			0x42c
 #define DC_DISP_PP_SELECT_D			0x42d
 
-#define PULSE_MODE_NORMAL    (0 << 3)
-#define PULSE_MODE_ONE_CLOCK (1 << 3)
-#define PULSE_POLARITY_HIGH  (0 << 4)
-#define PULSE_POLARITY_LOW   (1 << 4)
-#define PULSE_QUAL_ALWAYS    (0 << 6)
-#define PULSE_QUAL_VACTIVE   (2 << 6)
-#define PULSE_QUAL_VACTIVE1  (3 << 6)
-#define PULSE_LAST_START_A   (0 << 8)
-#define PULSE_LAST_END_A     (1 << 8)
-#define PULSE_LAST_START_B   (2 << 8)
-#define PULSE_LAST_END_B     (3 << 8)
-#define PULSE_LAST_START_C   (4 << 8)
-#define PULSE_LAST_END_C     (5 << 8)
-#define PULSE_LAST_START_D   (6 << 8)
-#define PULSE_LAST_END_D     (7 << 8)
-
-#define PULSE_START(x) (((x) & 0xfff) <<  0)
-#define PULSE_END(x)   (((x) & 0xfff) << 16)
+/* DC_DISP_H/V_PULSE_CONTROL */
+#define  PULSE_MODE_NORMAL			(0 << 3)
+#define  PULSE_MODE_ONE_CLOCK			BIT(3)
+#define  PULSE_POLARITY_HIGH			(0 << 4)
+#define  PULSE_POLARITY_LOW			BIT(4)
+#define  PULSE_QUAL_ALWAYS			(0 << 6)
+#define  PULSE_QUAL_VACTIVE			(2 << 6)
+#define  PULSE_QUAL_VACTIVE1			(3 << 6)
+#define  PULSE_LAST_START_A			(0 << 8)
+#define  PULSE_LAST_END_A			(1 << 8)
+#define  PULSE_LAST_START_B			(2 << 8)
+#define  PULSE_LAST_END_B			(3 << 8)
+#define  PULSE_LAST_START_C			(4 << 8)
+#define  PULSE_LAST_END_C			(5 << 8)
+#define  PULSE_LAST_START_D			(6 << 8)
+#define  PULSE_LAST_END_D			(7 << 8)
+
+/* DC_DISP_H/V_PULSE_POSITION */
+#define  PULSE_START(x)				(((x) & 0xfff) <<  0)
+#define  PULSE_END(x)				(((x) & 0xfff) << 16)
 
 #define DC_DISP_DISP_CLOCK_CONTROL		0x42e
-#define PIXEL_CLK_DIVIDER_PCD1  (0 << 8)
-#define PIXEL_CLK_DIVIDER_PCD1H (1 << 8)
-#define PIXEL_CLK_DIVIDER_PCD2  (2 << 8)
-#define PIXEL_CLK_DIVIDER_PCD3  (3 << 8)
-#define PIXEL_CLK_DIVIDER_PCD4  (4 << 8)
-#define PIXEL_CLK_DIVIDER_PCD6  (5 << 8)
-#define PIXEL_CLK_DIVIDER_PCD8  (6 << 8)
-#define PIXEL_CLK_DIVIDER_PCD9  (7 << 8)
-#define PIXEL_CLK_DIVIDER_PCD12 (8 << 8)
-#define PIXEL_CLK_DIVIDER_PCD16 (9 << 8)
-#define PIXEL_CLK_DIVIDER_PCD18 (10 << 8)
-#define PIXEL_CLK_DIVIDER_PCD24 (11 << 8)
-#define PIXEL_CLK_DIVIDER_PCD13 (12 << 8)
-#define SHIFT_CLK_DIVIDER(x)    ((x) & 0xff)
+#define  PIXEL_CLK_DIVIDER_PCD1			(0 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD1H		(1 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD2			(2 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD3			(3 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD4			(4 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD6			(5 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD8			(6 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD9			(7 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD12		(8 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD16		(9 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD18		(10 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD24		(11 << 8)
+#define  PIXEL_CLK_DIVIDER_PCD13		(12 << 8)
+#define  SHIFT_CLK_DIVIDER(x)			((x) & 0xff)
 
 #define DC_DISP_DISP_INTERFACE_CONTROL		0x42f
-#define DISP_DATA_FORMAT_DF1P1C    (0 << 0)
-#define DISP_DATA_FORMAT_DF1P2C24B (1 << 0)
-#define DISP_DATA_FORMAT_DF1P2C18B (2 << 0)
-#define DISP_DATA_FORMAT_DF1P2C16B (3 << 0)
-#define DISP_DATA_FORMAT_DF2S      (4 << 0)
-#define DISP_DATA_FORMAT_DF3S      (5 << 0)
-#define DISP_DATA_FORMAT_DFSPI     (6 << 0)
-#define DISP_DATA_FORMAT_DF1P3C24B (7 << 0)
-#define DISP_DATA_FORMAT_DF1P3C18B (8 << 0)
-#define DISP_ALIGNMENT_MSB         (0 << 8)
-#define DISP_ALIGNMENT_LSB         (1 << 8)
-#define DISP_ORDER_RED_BLUE        (0 << 9)
-#define DISP_ORDER_BLUE_RED        (1 << 9)
+#define  DISP_DATA_FORMAT_DF1P1C		0
+#define  DISP_DATA_FORMAT_DF1P2C24B		1
+#define  DISP_DATA_FORMAT_DF1P2C18B		2
+#define  DISP_DATA_FORMAT_DF1P2C16B		3
+#define  DISP_DATA_FORMAT_DF2S			4
+#define  DISP_DATA_FORMAT_DF3S			5
+#define  DISP_DATA_FORMAT_DFSPI			6
+#define  DISP_DATA_FORMAT_DF1P3C24B		7
+#define  DISP_DATA_FORMAT_DF1P3C18B		8
+#define  DISP_ALIGNMENT_MSB			(0 << 8)
+#define  DISP_ALIGNMENT_LSB			BIT(8)
+#define  DISP_ORDER_RED_BLUE			(0 << 9)
+#define  DISP_ORDER_BLUE_RED			BIT(9)
 
 #define DC_DISP_DISP_COLOR_CONTROL		0x430
-#define BASE_COLOR_SIZE666     ( 0 << 0)
-#define BASE_COLOR_SIZE111     ( 1 << 0)
-#define BASE_COLOR_SIZE222     ( 2 << 0)
-#define BASE_COLOR_SIZE333     ( 3 << 0)
-#define BASE_COLOR_SIZE444     ( 4 << 0)
-#define BASE_COLOR_SIZE555     ( 5 << 0)
-#define BASE_COLOR_SIZE565     ( 6 << 0)
-#define BASE_COLOR_SIZE332     ( 7 << 0)
-#define BASE_COLOR_SIZE888     ( 8 << 0)
-#define BASE_COLOR_SIZE101010  (10 << 0)
-#define BASE_COLOR_SIZE121212  (12 << 0)
-#define DITHER_CONTROL_MASK    (3 << 8)
-#define DITHER_CONTROL_DISABLE (0 << 8)
-#define DITHER_CONTROL_ORDERED (2 << 8)
-#define DITHER_CONTROL_ERRDIFF (3 << 8)
-#define BASE_COLOR_SIZE_MASK   (0xf << 0)
-#define BASE_COLOR_SIZE_666    (  0 << 0)
-#define BASE_COLOR_SIZE_111    (  1 << 0)
-#define BASE_COLOR_SIZE_222    (  2 << 0)
-#define BASE_COLOR_SIZE_333    (  3 << 0)
-#define BASE_COLOR_SIZE_444    (  4 << 0)
-#define BASE_COLOR_SIZE_555    (  5 << 0)
-#define BASE_COLOR_SIZE_565    (  6 << 0)
-#define BASE_COLOR_SIZE_332    (  7 << 0)
-#define BASE_COLOR_SIZE_888    (  8 << 0)
-#define BASE_COLOR_SIZE_101010 ( 10 << 0)
-#define BASE_COLOR_SIZE_121212 ( 12 << 0)
-#define CMU_ENABLE_ENABLE      (1 << 20)
+#define  BASE_COLOR_SIZE_MASK			0xf
+#define  BASE_COLOR_SIZE_666			0
+#define  BASE_COLOR_SIZE_111			1
+#define  BASE_COLOR_SIZE_222			2
+#define  BASE_COLOR_SIZE_333			3
+#define  BASE_COLOR_SIZE_444			4
+#define  BASE_COLOR_SIZE_555			5
+#define  BASE_COLOR_SIZE_565			6
+#define  BASE_COLOR_SIZE_332			7
+#define  BASE_COLOR_SIZE_888			8
+#define  BASE_COLOR_SIZE_101010			10
+#define  BASE_COLOR_SIZE_121212			12
+#define  DITHER_CONTROL_MASK			(3 << 8)
+#define  DITHER_CONTROL_DISABLE			(0 << 8)
+#define  DITHER_CONTROL_ORDERED			(2 << 8)
+#define  DITHER_CONTROL_ERRDIFF			(3 << 8)
+#define  DISP_COLOR_SWAP_BGR			BIT(16)
+#define  CMU_ENABLE_ENABLE			BIT(20)
 
 #define DC_DISP_SHIFT_CLOCK_OPTIONS		0x431
-#define  SC1_H_QUALIFIER_NONE	(1 << 16)
-#define  SC0_H_QUALIFIER_NONE	(1 <<  0)
+#define  SC0_H_QUALIFIER(x)			(((x) & 0x7) <<  0)
+#define  SC1_H_QUALIFIER(x)			(((x) & 0x7) << 16)
+#define  SC_H_QUALIFIER_DISABLE			0
+#define  SC_H_QUALIFIER_NONE			1
+#define  SC_H_QUALIFIER_HACTIVE			2
+#define  SC_H_QUALIFIER_EXT_HACTIVE		3
+#define  SC_H_QUALIFIER_HPULSE			4
+#define  SC_H_QUALIFIER_EXT_HPULSE		5
+#define  SC0_V_QUALIFIER(x)			(((x) & 0x7) <<  3)
+#define  SC1_V_QUALIFIER(x)			(((x) & 0x7) <<  19)
+#define  SC_V_QUALIFIER_NONE			0
+#define  SC_V_QUALIFIER_RSVD			1
+#define  SC_V_QUALIFIER_VACTIVE			2
+#define  SC_V_QUALIFIER_EXT_VACTIVE		3
+#define  SC_V_QUALIFIER_VPULSE			4
+#define  SC_V_QUALIFIER_EXT_VPULSE		5
 
 #define DC_DISP_DATA_ENABLE_OPTIONS		0x432
-#define DE_SELECT_ACTIVE_BLANK  (0 << 0)
-#define DE_SELECT_ACTIVE        (1 << 0)
-#define DE_SELECT_ACTIVE_IS     (2 << 0)
-#define DE_CONTROL_ONECLK       (0 << 2)
-#define DE_CONTROL_NORMAL       (1 << 2)
-#define DE_CONTROL_EARLY_EXT    (2 << 2)
-#define DE_CONTROL_EARLY        (3 << 2)
-#define DE_CONTROL_ACTIVE_BLANK (4 << 2)
+#define  DE_SELECT_ACTIVE_BLANK			0
+#define  DE_SELECT_ACTIVE			1
+#define  DE_SELECT_ACTIVE_IS			2
+#define  DE_CONTROL_ONECLK			(0 << 2)
+#define  DE_CONTROL_NORMAL			(1 << 2)
+#define  DE_CONTROL_EARLY_EXT			(2 << 2)
+#define  DE_CONTROL_EARLY			(3 << 2)
+#define  DE_CONTROL_ACTIVE_BLANK		(4 << 2)
 
 #define DC_DISP_SERIAL_INTERFACE_OPTIONS	0x433
 #define DC_DISP_LCD_SPI_OPTIONS			0x434
@@ -479,20 +617,34 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define DC_DISP_CURSOR_BACKGROUND		0x43d
 
 #define DC_DISP_CURSOR_START_ADDR		0x43e
-#define CURSOR_CLIP_DISPLAY	(0 << 28)
-#define CURSOR_CLIP_WIN_A	(1 << 28)
-#define CURSOR_CLIP_WIN_B	(2 << 28)
-#define CURSOR_CLIP_WIN_C	(3 << 28)
-#define CURSOR_SIZE_32x32	(0 << 24)
-#define CURSOR_SIZE_64x64	(1 << 24)
-#define CURSOR_SIZE_128x128	(2 << 24)
-#define CURSOR_SIZE_256x256	(3 << 24)
+#define  CURSOR_CLIP_DISPLAY			(0 << 28)
+#define  CURSOR_CLIP_WIN_A			(1 << 28)
+#define  CURSOR_CLIP_WIN_B			(2 << 28)
+#define  CURSOR_CLIP_WIN_C			(3 << 28)
+#define  CURSOR_SIZE_32x32			(0 << 24)
+#define  CURSOR_SIZE_64x64			(1 << 24)
+#define  CURSOR_SIZE_128x128			(2 << 24)
+#define  CURSOR_SIZE_256x256			(3 << 24)
 #define DC_DISP_CURSOR_START_ADDR_NS		0x43f
 
 #define DC_DISP_CURSOR_POSITION			0x440
 #define DC_DISP_CURSOR_POSITION_NS		0x441
 
 #define DC_DISP_INIT_SEQ_CONTROL		0x442
+#define  SEND_INIT_SEQUENCE			BIT(0)
+#define  INIT_SEQUENCE_MODE_SPI			BIT(1)
+#define  INIT_SEQUENCE_MODE_PLCD		0
+#define  INIT_SEQ_DC_SIGNAL_SHIFT		4
+#define  INIT_SEQ_DC_SIGNAL_MASK		(0x7 << INIT_SEQ_DC_SIGNAL_SHIFT)
+#define  NO_DC_SIGNAL				0
+#define  DC_SIGNAL_VSYNC			1
+#define  DC_SIGNAL_VPULSE0			2
+#define  DC_SIGNAL_VPULSE1			3
+#define  DC_SIGNAL_VPULSE2			4
+#define  DC_SIGNAL_VPULSE3			5
+#define  INIT_SEQ_DC_CONTROL_SHIFT		7
+#define  FRAME_INIT_SEQ_CYCLES_SHIFT		8
+
 #define DC_DISP_SPI_INIT_SEQ_DATA_A		0x443
 #define DC_DISP_SPI_INIT_SEQ_DATA_B		0x444
 #define DC_DISP_SPI_INIT_SEQ_DATA_C		0x445
@@ -519,22 +671,22 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define DC_DISP_SD_MAN_K_VALUES			0x4de
 
 #define DC_DISP_BLEND_BACKGROUND_COLOR		0x4e4
-#define  BACKGROUND_COLOR_ALPHA(x) (((x) & 0xff) << 24)
-#define  BACKGROUND_COLOR_BLUE(x)  (((x) & 0xff) << 16)
-#define  BACKGROUND_COLOR_GREEN(x) (((x) & 0xff) << 8)
-#define  BACKGROUND_COLOR_RED(x)   (((x) & 0xff) << 0)
+#define  BACKGROUND_COLOR_ALPHA(x)		(((x) & 0xff) << 24)
+#define  BACKGROUND_COLOR_BLUE(x)		(((x) & 0xff) << 16)
+#define  BACKGROUND_COLOR_GREEN(x)		(((x) & 0xff) << 8)
+#define  BACKGROUND_COLOR_RED(x)		((x) & 0xff)
 
 #define DC_DISP_INTERLACE_CONTROL		0x4e5
-#define  INTERLACE_STATUS (1 << 2)
-#define  INTERLACE_START  (1 << 1)
-#define  INTERLACE_ENABLE (1 << 0)
+#define  INTERLACE_STATUS			BIT(2)
+#define  INTERLACE_START			BIT(1)
+#define  INTERLACE_ENABLE			BIT(0)
 
 #define DC_DISP_CURSOR_START_ADDR_HI		0x4ec
 #define DC_DISP_BLEND_CURSOR_CONTROL		0x4f1
 #define CURSOR_COMPOSITION_MODE_BLEND		(0 << 25)
-#define CURSOR_COMPOSITION_MODE_XOR		(1 << 25)
+#define CURSOR_COMPOSITION_MODE_XOR		BIT(25)
 #define CURSOR_MODE_LEGACY			(0 << 24)
-#define CURSOR_MODE_NORMAL			(1 << 24)
+#define CURSOR_MODE_NORMAL			BIT(24)
 #define CURSOR_DST_BLEND_ZERO			(0 << 16)
 #define CURSOR_DST_BLEND_K1			(1 << 16)
 #define CURSOR_DST_BLEND_NEG_K1_TIMES_SRC	(2 << 16)
@@ -544,34 +696,34 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define CURSOR_SRC_BLEND_MASK			(3 << 8)
 #define CURSOR_ALPHA				0xff
 
-#define DC_WIN_CORE_ACT_CONTROL 0x50e
-#define  VCOUNTER (0 << 0)
-#define  HCOUNTER (1 << 0)
+#define DC_WIN_CORE_ACT_CONTROL			0x50e
+#define  VCOUNTER				0
+#define  HCOUNTER				BIT(0)
 
-#define DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA 0x543
-#define  LATENCY_CTL_MODE_ENABLE (1 << 2)
+#define DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA	0x543
+#define  LATENCY_CTL_MODE_ENABLE		BIT(2)
 
-#define DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB 0x544
-#define  WATERMARK_MASK 0x1fffffff
+#define DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB	0x544
+#define  WATERMARK_MASK				0x1fffffff
 
-#define DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER 0x560
-#define  PIPE_METER_INT(x)  (((x) & 0xff) << 8)
-#define  PIPE_METER_FRAC(x) (((x) & 0xff) << 0)
+#define DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER	0x560
+#define  PIPE_METER_INT(x)			(((x) & 0xff) << 8)
+#define  PIPE_METER_FRAC(x)			((x) & 0xff)
 
-#define DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG 0x561
-#define  MEMPOOL_ENTRIES(x) (((x) & 0xffff) << 0)
+#define DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG	0x561
+#define  MEMPOOL_ENTRIES(x)			((x) & 0xffff)
 
-#define DC_WIN_CORE_IHUB_WGRP_FETCH_METER 0x562
-#define  SLOTS(x) (((x) & 0xff) << 0)
+#define DC_WIN_CORE_IHUB_WGRP_FETCH_METER	0x562
+#define  SLOTS(x)				((x) & 0xff)
 
-#define DC_WIN_CORE_IHUB_LINEBUF_CONFIG 0x563
-#define  MODE_TWO_LINES  (0 << 14)
-#define  MODE_FOUR_LINES (1 << 14)
+#define DC_WIN_CORE_IHUB_LINEBUF_CONFIG		0x563
+#define  MODE_TWO_LINES				(0 << 14)
+#define  MODE_FOUR_LINES			BIT(14)
 
-#define DC_WIN_CORE_IHUB_THREAD_GROUP 0x568
-#define  THREAD_NUM_MASK (0x1f << 1)
-#define  THREAD_NUM(x) (((x) & 0x1f) << 1)
-#define  THREAD_GROUP_ENABLE (1 << 0)
+#define DC_WIN_CORE_IHUB_THREAD_GROUP		0x568
+#define  THREAD_NUM_MASK			(0x1f << 1)
+#define  THREAD_NUM(x)				(((x) & 0x1f) << 1)
+#define  THREAD_GROUP_ENABLE			BIT(0)
 
 #define DC_WIN_H_FILTER_P(p)			(0x601 + (p))
 #define DC_WIN_V_FILTER_P(p)			(0x619 + (p))
@@ -586,115 +738,115 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define DC_WIN_CSC_KVB				0x618
 
 #define DC_WIN_WIN_OPTIONS			0x700
-#define H_DIRECTION  (1 <<  0)
-#define V_DIRECTION  (1 <<  2)
-#define COLOR_EXPAND (1 <<  6)
-#define H_FILTER     (1 <<  8)
-#define V_FILTER     (1 << 10)
-#define CSC_ENABLE   (1 << 18)
-#define WIN_ENABLE   (1 << 30)
+#define  H_DIRECTION				BIT(0)
+#define  V_DIRECTION				BIT(2)
+#define  COLOR_EXPAND				BIT(6)
+#define  H_FILTER				BIT(8)
+#define  V_FILTER				BIT(10)
+#define  CSC_ENABLE				BIT(18)
+#define  WIN_ENABLE				BIT(30)
 
 #define DC_WIN_BYTE_SWAP			0x701
-#define BYTE_SWAP_NOSWAP  (0 << 0)
-#define BYTE_SWAP_SWAP2   (1 << 0)
-#define BYTE_SWAP_SWAP4   (2 << 0)
-#define BYTE_SWAP_SWAP4HW (3 << 0)
+#define  BYTE_SWAP_NOSWAP			0
+#define  BYTE_SWAP_SWAP2			1
+#define  BYTE_SWAP_SWAP4			2
+#define  BYTE_SWAP_SWAP4HW			3
 
 #define DC_WIN_BUFFER_CONTROL			0x702
-#define BUFFER_CONTROL_HOST  (0 << 0)
-#define BUFFER_CONTROL_VI    (1 << 0)
-#define BUFFER_CONTROL_EPP   (2 << 0)
-#define BUFFER_CONTROL_MPEGE (3 << 0)
-#define BUFFER_CONTROL_SB2D  (4 << 0)
+#define  BUFFER_CONTROL_HOST			0
+#define  BUFFER_CONTROL_VI			1
+#define  BUFFER_CONTROL_EPP			2
+#define  BUFFER_CONTROL_MPEGE			3
+#define  BUFFER_CONTROL_SB2D			4
 
 #define DC_WIN_COLOR_DEPTH			0x703
-#define WIN_COLOR_DEPTH_P1              0
-#define WIN_COLOR_DEPTH_P2              1
-#define WIN_COLOR_DEPTH_P4              2
-#define WIN_COLOR_DEPTH_P8              3
-#define WIN_COLOR_DEPTH_B4G4R4A4        4
-#define WIN_COLOR_DEPTH_B5G5R5A1        5
-#define WIN_COLOR_DEPTH_B5G6R5          6
-#define WIN_COLOR_DEPTH_A1B5G5R5        7
-#define WIN_COLOR_DEPTH_B8G8R8A8       12
-#define WIN_COLOR_DEPTH_R8G8B8A8       13
-#define WIN_COLOR_DEPTH_B6x2G6x2R6x2A8 14
-#define WIN_COLOR_DEPTH_R6x2G6x2B6x2A8 15
-#define WIN_COLOR_DEPTH_YCbCr422       16
-#define WIN_COLOR_DEPTH_YUV422         17
-#define WIN_COLOR_DEPTH_YCbCr420P      18
-#define WIN_COLOR_DEPTH_YUV420P        19
-#define WIN_COLOR_DEPTH_YCbCr422P      20
-#define WIN_COLOR_DEPTH_YUV422P        21
-#define WIN_COLOR_DEPTH_YCbCr422R      22
-#define WIN_COLOR_DEPTH_YUV422R        23
-#define WIN_COLOR_DEPTH_YCbCr422RA     24
-#define WIN_COLOR_DEPTH_YUV422RA       25
-#define WIN_COLOR_DEPTH_R4G4B4A4       27
-#define WIN_COLOR_DEPTH_R5G5B5A        28
-#define WIN_COLOR_DEPTH_AR5G5B5        29
-#define WIN_COLOR_DEPTH_B5G5R5X1       30
-#define WIN_COLOR_DEPTH_X1B5G5R5       31
-#define WIN_COLOR_DEPTH_R5G5B5X1       32
-#define WIN_COLOR_DEPTH_X1R5G5B5       33
-#define WIN_COLOR_DEPTH_R5G6B5         34
-#define WIN_COLOR_DEPTH_A8R8G8B8       35
-#define WIN_COLOR_DEPTH_A8B8G8R8       36
-#define WIN_COLOR_DEPTH_B8G8R8X8       37
-#define WIN_COLOR_DEPTH_R8G8B8X8       38
-#define WIN_COLOR_DEPTH_YCbCr444P      41
-#define WIN_COLOR_DEPTH_YCrCb420SP     42
-#define WIN_COLOR_DEPTH_YCbCr420SP     43
-#define WIN_COLOR_DEPTH_YCrCb422SP     44
-#define WIN_COLOR_DEPTH_YCbCr422SP     45
-#define WIN_COLOR_DEPTH_YCrCb444SP     48
-#define WIN_COLOR_DEPTH_YCbCr444SP     49
-#define WIN_COLOR_DEPTH_X8B8G8R8       65
-#define WIN_COLOR_DEPTH_X8R8G8B8       66
+#define  WIN_COLOR_DEPTH_P1			0
+#define  WIN_COLOR_DEPTH_P2			1
+#define  WIN_COLOR_DEPTH_P4			2
+#define  WIN_COLOR_DEPTH_P8			3
+#define  WIN_COLOR_DEPTH_B4G4R4A4		4
+#define  WIN_COLOR_DEPTH_B5G5R5A1		5
+#define  WIN_COLOR_DEPTH_B5G6R5			6
+#define  WIN_COLOR_DEPTH_A1B5G5R5		7
+#define  WIN_COLOR_DEPTH_B8G8R8A8		12
+#define  WIN_COLOR_DEPTH_R8G8B8A8		13
+#define  WIN_COLOR_DEPTH_B6x2G6x2R6x2A8		14
+#define  WIN_COLOR_DEPTH_R6x2G6x2B6x2A8		15
+#define  WIN_COLOR_DEPTH_YCbCr422		16
+#define  WIN_COLOR_DEPTH_YUV422			17
+#define  WIN_COLOR_DEPTH_YCbCr420P		18
+#define  WIN_COLOR_DEPTH_YUV420P		19
+#define  WIN_COLOR_DEPTH_YCbCr422P		20
+#define  WIN_COLOR_DEPTH_YUV422P		21
+#define  WIN_COLOR_DEPTH_YCbCr422R		22
+#define  WIN_COLOR_DEPTH_YUV422R		23
+#define  WIN_COLOR_DEPTH_YCbCr422RA		24
+#define  WIN_COLOR_DEPTH_YUV422RA		25
+#define  WIN_COLOR_DEPTH_R4G4B4A4		27
+#define  WIN_COLOR_DEPTH_R5G5B5A		28
+#define  WIN_COLOR_DEPTH_AR5G5B5		29
+#define  WIN_COLOR_DEPTH_B5G5R5X1		30
+#define  WIN_COLOR_DEPTH_X1B5G5R5		31
+#define  WIN_COLOR_DEPTH_R5G5B5X1		32
+#define  WIN_COLOR_DEPTH_X1R5G5B5		33
+#define  WIN_COLOR_DEPTH_R5G6B5			34
+#define  WIN_COLOR_DEPTH_A8R8G8B8		35
+#define  WIN_COLOR_DEPTH_A8B8G8R8		36
+#define  WIN_COLOR_DEPTH_B8G8R8X8		37
+#define  WIN_COLOR_DEPTH_R8G8B8X8		38
+#define  WIN_COLOR_DEPTH_YCbCr444P		41
+#define  WIN_COLOR_DEPTH_YCrCb420SP		42
+#define  WIN_COLOR_DEPTH_YCbCr420SP		43
+#define  WIN_COLOR_DEPTH_YCrCb422SP		44
+#define  WIN_COLOR_DEPTH_YCbCr422SP		45
+#define  WIN_COLOR_DEPTH_YCrCb444SP		48
+#define  WIN_COLOR_DEPTH_YCbCr444SP		49
+#define  WIN_COLOR_DEPTH_X8B8G8R8		65
+#define  WIN_COLOR_DEPTH_X8R8G8B8		66
 
 #define DC_WIN_POSITION				0x704
-#define H_POSITION(x) (((x) & 0x1fff) <<  0) /* XXX 0x7fff on Tegra186 */
-#define V_POSITION(x) (((x) & 0x1fff) << 16) /* XXX 0x7fff on Tegra186 */
+#define  H_POSITION(x)				(((x) & 0x1fff) <<  0) /* XXX 0x7fff on Tegra186 */
+#define  V_POSITION(x)				(((x) & 0x1fff) << 16) /* XXX 0x7fff on Tegra186 */
 
 #define DC_WIN_SIZE				0x705
-#define H_SIZE(x) (((x) & 0x1fff) <<  0) /* XXX 0x7fff on Tegra186 */
-#define V_SIZE(x) (((x) & 0x1fff) << 16) /* XXX 0x7fff on Tegra186 */
+#define  H_SIZE(x)				(((x) & 0x1fff) <<  0) /* XXX 0x7fff on Tegra186 */
+#define  V_SIZE(x)				(((x) & 0x1fff) << 16) /* XXX 0x7fff on Tegra186 */
 
 #define DC_WIN_PRESCALED_SIZE			0x706
-#define H_PRESCALED_SIZE(x) (((x) & 0x7fff) <<  0)
-#define V_PRESCALED_SIZE(x) (((x) & 0x1fff) << 16) /* XXX 0x7fff on Tegra186 */
+#define  H_PRESCALED_SIZE(x)			(((x) & 0x7fff) <<  0)
+#define  V_PRESCALED_SIZE(x)			(((x) & 0x1fff) << 16) /* XXX 0x7fff on Tegra186 */
 
 #define DC_WIN_H_INITIAL_DDA			0x707
 #define DC_WIN_V_INITIAL_DDA			0x708
 #define DC_WIN_DDA_INC				0x709
-#define H_DDA_INC(x) (((x) & 0xffff) <<  0)
-#define V_DDA_INC(x) (((x) & 0xffff) << 16)
+#define  H_DDA_INC(x)				(((x) & 0xffff) <<  0)
+#define  V_DDA_INC(x)				(((x) & 0xffff) << 16)
 
 #define DC_WIN_LINE_STRIDE			0x70a
 #define DC_WIN_BUF_STRIDE			0x70b
 #define DC_WIN_UV_BUF_STRIDE			0x70c
 #define DC_WIN_BUFFER_ADDR_MODE			0x70d
-#define DC_WIN_BUFFER_ADDR_MODE_LINEAR		(0 <<  0)
-#define DC_WIN_BUFFER_ADDR_MODE_TILE		(1 <<  0)
-#define DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV	(0 << 16)
-#define DC_WIN_BUFFER_ADDR_MODE_TILE_UV		(1 << 16)
+#define  DC_WIN_BUFFER_ADDR_MODE_LINEAR		(0 << 0)
+#define  DC_WIN_BUFFER_ADDR_MODE_TILE		BIT(0)
+#define  DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV	(0 << 16)
+#define  DC_WIN_BUFFER_ADDR_MODE_TILE_UV	BIT(16)
 
 #define DC_WIN_DV_CONTROL			0x70e
 
 #define DC_WIN_BLEND_NOKEY			0x70f
-#define  BLEND_WEIGHT1(x) (((x) & 0xff) << 16)
-#define  BLEND_WEIGHT0(x) (((x) & 0xff) <<  8)
+#define  BLEND_WEIGHT1(x)			(((x) & 0xff) << 16)
+#define  BLEND_WEIGHT0(x)			(((x) & 0xff) <<  8)
 
 #define DC_WIN_BLEND_1WIN			0x710
-#define  BLEND_CONTROL_FIX    (0 << 2)
-#define  BLEND_CONTROL_ALPHA  (1 << 2)
-#define  BLEND_COLOR_KEY_NONE (0 << 0)
-#define  BLEND_COLOR_KEY_0    (1 << 0)
-#define  BLEND_COLOR_KEY_1    (2 << 0)
-#define  BLEND_COLOR_KEY_BOTH (3 << 0)
+#define  BLEND_CONTROL_FIX			(0 << 2)
+#define  BLEND_CONTROL_ALPHA			BIT(2)
+#define  BLEND_COLOR_KEY_NONE			0
+#define  BLEND_COLOR_KEY_0			1
+#define  BLEND_COLOR_KEY_1			2
+#define  BLEND_COLOR_KEY_BOTH			3
 
 #define DC_WIN_BLEND_2WIN_X			0x711
-#define  BLEND_CONTROL_DEPENDENT (2 << 2)
+#define  BLEND_CONTROL_DEPENDENT		(2 << 2)
 
 #define DC_WIN_BLEND_2WIN_Y			0x712
 #define DC_WIN_BLEND_3WIN_XY			0x713
@@ -715,10 +867,10 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 
 #define DC_WINBUF_UFLOW_STATUS			0x80a
 #define DC_WINBUF_SURFACE_KIND			0x80b
-#define DC_WINBUF_SURFACE_KIND_PITCH	(0 << 0)
-#define DC_WINBUF_SURFACE_KIND_TILED	(1 << 0)
-#define DC_WINBUF_SURFACE_KIND_BLOCK	(2 << 0)
-#define DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(x) (((x) & 0x7) << 4)
+#define DC_WINBUF_SURFACE_KIND_PITCH		0
+#define DC_WINBUF_SURFACE_KIND_TILED		1
+#define DC_WINBUF_SURFACE_KIND_BLOCK		2
+#define DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(x)	(((x) & 0x7) << 4)
 
 #define DC_WINBUF_START_ADDR_HI			0x80d
 
@@ -726,7 +878,7 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define DC_WINBUF_START_ADDR_HI_V		0x811
 
 #define DC_WINBUF_CDE_CONTROL			0x82f
-#define  ENABLE_SURFACE (1 << 0)
+#define  ENABLE_SURFACE				BIT(0)
 
 #define DC_WINBUF_AD_UFLOW_STATUS		0xbca
 #define DC_WINBUF_BD_UFLOW_STATUS		0xdca
@@ -734,14 +886,14 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 
 /* Tegra186 and later */
 #define DC_DISP_CORE_SOR_SET_CONTROL(x)		(0x403 + (x))
-#define PROTOCOL_MASK (0xf << 8)
-#define PROTOCOL_SINGLE_TMDS_A (0x1 << 8)
+#define PROTOCOL_MASK				(0xf << 8)
+#define PROTOCOL_SINGLE_TMDS_A			(0x1 << 8)
 
 #define DC_DISP_CORE_HEAD_SET_CONTROL_OUTPUT_LUT	0x431
-#define  OUTPUT_LUT_MODE_MASK        (3 << 5)
-#define  OUTPUT_LUT_MODE_INTERPOLATE (1 << 5)
-#define  OUTPUT_LUT_SIZE_MASK        (3 << 1)
-#define  OUTPUT_LUT_SIZE_SIZE_1025   (2 << 1)
+#define  OUTPUT_LUT_MODE_MASK			(3 << 5)
+#define  OUTPUT_LUT_MODE_INTERPOLATE		(1 << 5)
+#define  OUTPUT_LUT_SIZE_MASK			(3 << 1)
+#define  OUTPUT_LUT_SIZE_SIZE_1025		(2 << 1)
 
 #define DC_DISP_COREPVT_HEAD_SET_OUTPUT_LUT_BASE	0x432
 #define DC_DISP_COREPVT_HEAD_SET_OUTPUT_LUT_BASE_HI	0x433
@@ -749,18 +901,18 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR	0x442
 #define DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR	0x446
 
-#define DC_WINC_PRECOMP_WGRP_PIPE_CAPA 0x500
-#define DC_WINC_PRECOMP_WGRP_PIPE_CAPB 0x501
-#define DC_WINC_PRECOMP_WGRP_PIPE_CAPC 0x502
-#define  MAX_PIXELS_5TAP444(x) ((x) & 0xffff)
-#define DC_WINC_PRECOMP_WGRP_PIPE_CAPD 0x503
-#define DC_WINC_PRECOMP_WGRP_PIPE_CAPE 0x504
-#define  MAX_PIXELS_2TAP444(x) ((x) & 0xffff)
-#define DC_WINC_PRECOMP_WGRP_PIPE_CAPF 0x505
+#define DC_WINC_PRECOMP_WGRP_PIPE_CAPA		0x500
+#define DC_WINC_PRECOMP_WGRP_PIPE_CAPB		0x501
+#define DC_WINC_PRECOMP_WGRP_PIPE_CAPC		0x502
+#define  MAX_PIXELS_5TAP444(x)			((x) & 0xffff)
+#define DC_WINC_PRECOMP_WGRP_PIPE_CAPD		0x503
+#define DC_WINC_PRECOMP_WGRP_PIPE_CAPE		0x504
+#define  MAX_PIXELS_2TAP444(x)			((x) & 0xffff)
+#define DC_WINC_PRECOMP_WGRP_PIPE_CAPF		0x505
 
 #define DC_WIN_CORE_WINDOWGROUP_SET_CONTROL	0x702
-#define OWNER_MASK (0xf << 0)
-#define OWNER(x) (((x) & 0xf) << 0)
+#define OWNER_MASK				0xf
+#define OWNER(x)				((x) & 0xf)
 
 #define DC_WIN_CROPPED_SIZE			0x706
 
@@ -768,52 +920,52 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define DC_WIN_SET_INPUT_SCALER_V_START_PHASE	0x708
 
 #define DC_WIN_PLANAR_STORAGE			0x709
-#define PITCH(x) (((x) >> 6) & 0x1fff)
+#define PITCH(x)				(((x) >> 6) & 0x1fff)
 
 #define DC_WIN_PLANAR_STORAGE_UV		0x70a
-#define  PITCH_U(x) ((((x) >> 6) & 0x1fff) <<  0)
-#define  PITCH_V(x) ((((x) >> 6) & 0x1fff) << 16)
+#define  PITCH_U(x)				((((x) >> 6) & 0x1fff) <<  0)
+#define  PITCH_V(x)				((((x) >> 6) & 0x1fff) << 16)
 
 #define DC_WIN_SET_INPUT_SCALER_HPHASE_INCR	0x70b
 #define DC_WIN_SET_INPUT_SCALER_VPHASE_INCR	0x70c
 
 #define DC_WIN_SET_PARAMS			0x70d
-#define  CLAMP_BEFORE_BLEND (1 << 15)
-#define  DEGAMMA_NONE (0 << 13)
-#define  DEGAMMA_SRGB (1 << 13)
-#define  DEGAMMA_YUV8_10 (2 << 13)
-#define  DEGAMMA_YUV12 (3 << 13)
-#define  INPUT_RANGE_BYPASS (0 << 10)
-#define  INPUT_RANGE_LIMITED (1 << 10)
-#define  INPUT_RANGE_FULL (2 << 10)
-#define  COLOR_SPACE_RGB (0 << 8)
-#define  COLOR_SPACE_YUV_601 (1 << 8)
-#define  COLOR_SPACE_YUV_709 (2 << 8)
-#define  COLOR_SPACE_YUV_2020 (3 << 8)
+#define  CLAMP_BEFORE_BLEND			BIT(15)
+#define  DEGAMMA_NONE				(0 << 13)
+#define  DEGAMMA_SRGB				(1 << 13)
+#define  DEGAMMA_YUV8_10			(2 << 13)
+#define  DEGAMMA_YUV12				(3 << 13)
+#define  INPUT_RANGE_BYPASS			(0 << 10)
+#define  INPUT_RANGE_LIMITED			(1 << 10)
+#define  INPUT_RANGE_FULL			(2 << 10)
+#define  COLOR_SPACE_RGB			(0 << 8)
+#define  COLOR_SPACE_YUV_601			(1 << 8)
+#define  COLOR_SPACE_YUV_709			(2 << 8)
+#define  COLOR_SPACE_YUV_2020			(3 << 8)
 
 #define DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER	0x70e
-#define  HORIZONTAL_TAPS_2 (1 << 3)
-#define  HORIZONTAL_TAPS_5 (4 << 3)
-#define  VERTICAL_TAPS_2 (1 << 0)
-#define  VERTICAL_TAPS_5 (4 << 0)
+#define  HORIZONTAL_TAPS_2			(1 << 3)
+#define  HORIZONTAL_TAPS_5			(4 << 3)
+#define  VERTICAL_TAPS_2			1
+#define  VERTICAL_TAPS_5			4
 
 #define DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_COEFF 0x70f
-#define  COEFF_INDEX(x) (((x) & 0xff) << 15)
-#define  COEFF_DATA(x) (((x) & 0x3ff) << 0)
+#define  COEFF_INDEX(x)				(((x) & 0xff) << 15)
+#define  COEFF_DATA(x)				((x) & 0x3ff)
 
 #define DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE	0x711
-#define  INPUT_SCALER_USE422  (1 << 2)
-#define  INPUT_SCALER_VBYPASS (1 << 1)
-#define  INPUT_SCALER_HBYPASS (1 << 0)
+#define  INPUT_SCALER_USE422			BIT(2)
+#define  INPUT_SCALER_VBYPASS			BIT(1)
+#define  INPUT_SCALER_HBYPASS			BIT(0)
 
 #define DC_WIN_BLEND_LAYER_CONTROL		0x716
-#define  COLOR_KEY_NONE (0 << 25)
-#define  COLOR_KEY_SRC (1 << 25)
-#define  COLOR_KEY_DST (2 << 25)
-#define  BLEND_BYPASS (1 << 24)
-#define  K2(x) (((x) & 0xff) << 16)
-#define  K1(x) (((x) & 0xff) << 8)
-#define  WINDOW_LAYER_DEPTH(x) (((x) & 0xff) << 0)
+#define  COLOR_KEY_NONE				(0 << 25)
+#define  COLOR_KEY_SRC				(1 << 25)
+#define  COLOR_KEY_DST				(2 << 25)
+#define  BLEND_BYPASS				BIT(24)
+#define  K2(x)					(((x) & 0xff) << 16)
+#define  K1(x)					(((x) & 0xff) << 8)
+#define  WINDOW_LAYER_DEPTH(x)			((x) & 0xff)
 
 #define DC_WIN_BLEND_MATCH_SELECT		0x717
 #define  BLEND_FACTOR_DST_ALPHA_ZERO			(0 << 12)
@@ -832,23 +984,23 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
 #define  BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_DST	(5 << 4)
 #define  BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC	(6 << 4)
 #define  BLEND_FACTOR_DST_COLOR_NEG_K1			(7 << 4)
-#define  BLEND_FACTOR_SRC_COLOR_ZERO			(0 << 0)
-#define  BLEND_FACTOR_SRC_COLOR_ONE			(1 << 0)
-#define  BLEND_FACTOR_SRC_COLOR_K1			(2 << 0)
-#define  BLEND_FACTOR_SRC_COLOR_K1_TIMES_DST		(3 << 0)
-#define  BLEND_FACTOR_SRC_COLOR_NEG_K1_TIMES_DST	(4 << 0)
-#define  BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC		(5 << 0)
+#define  BLEND_FACTOR_SRC_COLOR_ZERO			0
+#define  BLEND_FACTOR_SRC_COLOR_ONE			1
+#define  BLEND_FACTOR_SRC_COLOR_K1			2
+#define  BLEND_FACTOR_SRC_COLOR_K1_TIMES_DST		3
+#define  BLEND_FACTOR_SRC_COLOR_NEG_K1_TIMES_DST	4
+#define  BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC		5
 
 #define DC_WIN_BLEND_NOMATCH_SELECT		0x718
 
 #define DC_WIN_PRECOMP_WGRP_PARAMS		0x724
-#define  SWAP_UV (1 << 0)
+#define  SWAP_UV				BIT(0)
 
 #define DC_WIN_WINDOW_SET_CONTROL		0x730
-#define  CONTROL_CSC_ENABLE (1 << 5)
+#define  CONTROL_CSC_ENABLE			BIT(5)
 
 #define DC_WINBUF_CROPPED_POINT			0x806
-#define OFFSET_Y(x) (((x) & 0xffff) << 16)
-#define OFFSET_X(x) (((x) & 0xffff) << 0)
+#define  OFFSET_Y(x)				(((x) & 0xffff) << 16)
+#define  OFFSET_X(x)				((x) & 0xffff)
 
 #endif /* TEGRA_DC_H */
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index b6d09a8d8e5ea..addcecf8592b1 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -115,14 +115,14 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)
 	value = tegra_dc_readl(rgb->dc, DC_COM_PIN_OUTPUT_POLARITY(1));
 
 	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
-		value |= LHS_OUTPUT_POLARITY_LOW;
+		value |= BIT(PIN_LHS);
 	else
-		value &= ~LHS_OUTPUT_POLARITY_LOW;
+		value &= ~BIT(PIN_LHS);
 
 	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
-		value |= LVS_OUTPUT_POLARITY_LOW;
+		value |= BIT(PIN_LVS);
 	else
-		value &= ~LVS_OUTPUT_POLARITY_LOW;
+		value &= ~BIT(PIN_LVS);
 
 	tegra_dc_writel(rgb->dc, value, DC_COM_PIN_OUTPUT_POLARITY(1));
 
-- 
2.53.0


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

* [PATCH v5 06/11] dt-bindings: display: tegra: Document Tegra20 DSI port
  2026-10-29 15:28 [PATCH v5 00/11] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
                   ` (4 preceding siblings ...)
  2026-10-29 15:29 ` [PATCH v5 05/11] drm/tegra: dc: Refactor and expand header Svyatoslav Ryhel
@ 2026-10-29 15:29 ` Svyatoslav Ryhel
  2026-10-29 15:29 ` [PATCH v5 07/11] drm/tegra: Make panel parsing by tegra_output_probe() optional Svyatoslav Ryhel
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 15+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-29 15:29 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
  Cc: dri-devel, devicetree, linux-kernel, linux-tegra

Document the optional port node, which is required for correctly modeling
a dual-mode panel.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 .../bindings/display/tegra/nvidia,tegra20-dsi.yaml          | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dsi.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dsi.yaml
index 59e1dc0813e73..0f4837648d179 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dsi.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dsi.yaml
@@ -83,6 +83,12 @@ properties:
       gang up with in order to support up to 8 data lanes
     $ref: /schemas/types.yaml#/definitions/phandle
 
+  port:
+    description: specifies the connection port for the dual-mode panel
+      and is required only when the panel is operating in dual-mode
+      configuration.
+    $ref: /schemas/graph.yaml#/properties/port
+
 allOf:
   - $ref: ../dsi-controller.yaml#
   - if:
-- 
2.53.0


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

* [PATCH v5 07/11] drm/tegra: Make panel parsing by tegra_output_probe() optional
  2026-10-29 15:28 [PATCH v5 00/11] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
                   ` (5 preceding siblings ...)
  2026-10-29 15:29 ` [PATCH v5 06/11] dt-bindings: display: tegra: Document Tegra20 DSI port Svyatoslav Ryhel
@ 2026-10-29 15:29 ` Svyatoslav Ryhel
  2026-10-29 15:41   ` sashiko-bot
  2026-10-29 15:29 ` [PATCH v5 08/11] drm/tegra: Convert to use atomic versions of drm_encoder_helper_funcs Svyatoslav Ryhel
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 15+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-29 15:29 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
  Cc: dri-devel, devicetree, linux-kernel, linux-tegra

The tegra_output_probe() performs panel parsing unconditionally, even for
devices that do not require it (such as HDMI) or that handle panel parsing
on their own (like DSI). Make panel parsing configurable.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/gpu/drm/tegra/drm.h    |  2 +-
 drivers/gpu/drm/tegra/dsi.c    |  2 +-
 drivers/gpu/drm/tegra/hdmi.c   |  2 +-
 drivers/gpu/drm/tegra/output.c | 52 ++++++++++++++++++----------------
 drivers/gpu/drm/tegra/rgb.c    |  2 +-
 drivers/gpu/drm/tegra/sor.c    |  2 +-
 6 files changed, 32 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index bc720ae8d95e1..e53fe68161cb8 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -156,7 +156,7 @@ static inline struct tegra_output *connector_to_output(struct drm_connector *c)
 }
 
 /* from output.c */
-int tegra_output_probe(struct tegra_output *output);
+int tegra_output_probe(struct tegra_output *output, bool get_panel);
 void tegra_output_remove(struct tegra_output *output);
 int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
 void tegra_output_exit(struct tegra_output *output);
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 7b595310e7379..a4c0c63bbe567 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -1618,7 +1618,7 @@ static int tegra_dsi_probe(struct platform_device *pdev)
 	if (err < 0)
 		return err;
 
-	err = tegra_output_probe(&dsi->output);
+	err = tegra_output_probe(&dsi->output, false);
 	if (err < 0)
 		return err;
 
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 0d2c0ab0a63e9..c0845bb45bb15 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1855,7 +1855,7 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
 
 	hdmi->output.dev = &pdev->dev;
 
-	err = tegra_output_probe(&hdmi->output);
+	err = tegra_output_probe(&hdmi->output, false);
 	if (err < 0)
 		return err;
 
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index d0ba3bd0aab8f..ff2e28a1e00b7 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -94,7 +94,7 @@ static irqreturn_t hpd_irq(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
-int tegra_output_probe(struct tegra_output *output)
+int tegra_output_probe(struct tegra_output *output, bool get_panel)
 {
 	struct device_node *ddc, *panel;
 	const void *edid;
@@ -104,31 +104,33 @@ int tegra_output_probe(struct tegra_output *output)
 	if (!output->of_node)
 		output->of_node = output->dev->of_node;
 
-	err = drm_of_find_panel_or_bridge(output->of_node, -1, -1,
-					  &output->panel, &output->bridge);
-	if (err && err != -ENODEV)
-		return err;
-
-	panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
-	if (panel) {
-		/*
-		 * Don't mix nvidia,panel phandle with the graph in a
-		 * device-tree.
-		 */
-		WARN_ON(output->panel || output->bridge);
-
-		if (output->panel) {
-			drm_panel_put(output->panel);
-			output->panel = NULL;
-		}
-
-		output->panel = of_drm_find_panel(panel);
-		of_node_put(panel);
-
-		if (IS_ERR(output->panel)) {
-			err = PTR_ERR(output->panel);
-			output->panel = NULL;
+	if (get_panel) {
+		err = drm_of_find_panel_or_bridge(output->of_node, -1, -1,
+						  &output->panel, &output->bridge);
+		if (err && err != -ENODEV)
 			return err;
+
+		panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
+		if (panel) {
+			/*
+			 * Don't mix nvidia,panel phandle with the graph in a
+			 * device-tree.
+			 */
+			WARN_ON(output->panel || output->bridge);
+
+			if (output->panel) {
+				drm_panel_put(output->panel);
+				output->panel = NULL;
+			}
+
+			output->panel = of_drm_find_panel(panel);
+			of_node_put(panel);
+
+			if (IS_ERR(output->panel)) {
+				err = PTR_ERR(output->panel);
+				output->panel = NULL;
+				return err;
+			}
 		}
 	}
 
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index addcecf8592b1..01475995d9cc8 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -236,7 +236,7 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc)
 	rgb->output.of_node = np;
 	rgb->dc = dc;
 
-	err = tegra_output_probe(&rgb->output);
+	err = tegra_output_probe(&rgb->output, true);
 	if (err < 0)
 		return err;
 
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index e384cbd0cbf72..ab81e412e05f1 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -3806,7 +3806,7 @@ static int tegra_sor_probe(struct platform_device *pdev)
 	if (err < 0)
 		goto put_aux;
 
-	err = tegra_output_probe(&sor->output);
+	err = tegra_output_probe(&sor->output, true);
 	if (err < 0) {
 		dev_err_probe(&pdev->dev, err, "failed to probe output\n");
 		goto put_aux;
-- 
2.53.0


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

* [PATCH v5 08/11] drm/tegra: Convert to use atomic versions of drm_encoder_helper_funcs
  2026-10-29 15:28 [PATCH v5 00/11] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
                   ` (6 preceding siblings ...)
  2026-10-29 15:29 ` [PATCH v5 07/11] drm/tegra: Make panel parsing by tegra_output_probe() optional Svyatoslav Ryhel
@ 2026-10-29 15:29 ` Svyatoslav Ryhel
  2026-10-29 15:29 ` [PATCH v5 09/11] drm/tegra: rgb: Parameterize configuration based on bus flags Svyatoslav Ryhel
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 15+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-29 15:29 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
  Cc: dri-devel, devicetree, linux-kernel, linux-tegra

Switch to using the atomic versions of the enable() and disable()
operations.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/gpu/drm/tegra/dsi.c  | 20 +++++++++++++++-----
 drivers/gpu/drm/tegra/hdmi.c | 20 +++++++++++++++-----
 drivers/gpu/drm/tegra/rgb.c  | 23 +++++++++++++++++------
 drivers/gpu/drm/tegra/sor.c  | 18 +++++++++++++-----
 4 files changed, 60 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index a4c0c63bbe567..202aed6a6fc12 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -856,7 +856,8 @@ static void tegra_dsi_unprepare(struct tegra_dsi *dsi)
 		dev_err(dsi->dev, "failed to suspend: %d\n", err);
 }
 
-static void tegra_dsi_encoder_disable(struct drm_encoder *encoder)
+static void tegra_dsi_encoder_disable(struct drm_encoder *encoder,
+				      struct drm_atomic_commit *comm_state)
 {
 	struct tegra_output *output = encoder_to_output(encoder);
 	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
@@ -920,16 +921,25 @@ static int tegra_dsi_prepare(struct tegra_dsi *dsi)
 	return 0;
 }
 
-static void tegra_dsi_encoder_enable(struct drm_encoder *encoder)
+static void tegra_dsi_encoder_enable(struct drm_encoder *encoder,
+				     struct drm_atomic_commit *comm_state)
 {
-	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
 	struct tegra_output *output = encoder_to_output(encoder);
 	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
+	struct drm_connector_state *conn_state;
 	struct tegra_dsi *dsi = to_dsi(output);
+	struct drm_crtc_state *crtc_state;
+	struct drm_connector *connector;
 	struct tegra_dsi_state *state;
+	struct drm_display_mode *mode;
 	u32 value;
 	int err;
 
+	connector = drm_atomic_get_new_connector_for_encoder(comm_state, encoder);
+	conn_state = drm_atomic_get_new_connector_state(comm_state, connector);
+	crtc_state = drm_atomic_get_new_crtc_state(comm_state, conn_state->crtc);
+	mode = &crtc_state->adjusted_mode;
+
 	err = tegra_dsi_prepare(dsi);
 	if (err < 0) {
 		dev_err(dsi->dev, "failed to prepare: %d\n", err);
@@ -1060,8 +1070,8 @@ static const struct drm_encoder_funcs tegra_dsi_encoder_funcs = {
 };
 
 static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = {
-	.disable = tegra_dsi_encoder_disable,
-	.enable = tegra_dsi_encoder_enable,
+	.atomic_disable = tegra_dsi_encoder_disable,
+	.atomic_enable = tegra_dsi_encoder_enable,
 	.atomic_check = tegra_dsi_encoder_atomic_check,
 };
 
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index c0845bb45bb15..0a2420095d061 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1166,7 +1166,8 @@ tegra_hdmi_connector_helper_funcs = {
 	.mode_valid = tegra_hdmi_connector_mode_valid,
 };
 
-static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder)
+static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder,
+				       struct drm_atomic_commit *comm_state)
 {
 	struct tegra_output *output = encoder_to_output(encoder);
 	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
@@ -1209,18 +1210,27 @@ static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder)
 		dev_err(hdmi->dev, "failed to suspend: %d\n", err);
 }
 
-static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder)
+static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder,
+				      struct drm_atomic_commit *comm_state)
 {
-	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
 	unsigned int h_sync_width, h_front_porch, h_back_porch, i, rekey;
 	struct tegra_output *output = encoder_to_output(encoder);
 	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
 	struct tegra_hdmi *hdmi = to_hdmi(output);
+	struct drm_connector_state *conn_state;
+	struct drm_crtc_state *crtc_state;
 	unsigned int pulse_start, div82;
+	struct drm_connector *connector;
+	struct drm_display_mode *mode;
 	int retries = 1000;
 	u32 value;
 	int err;
 
+	connector = drm_atomic_get_new_connector_for_encoder(comm_state, encoder);
+	conn_state = drm_atomic_get_new_connector_state(comm_state, connector);
+	crtc_state = drm_atomic_get_new_crtc_state(comm_state, conn_state->crtc);
+	mode = &crtc_state->adjusted_mode;
+
 	err = host1x_client_resume(&hdmi->client);
 	if (err < 0) {
 		dev_err(hdmi->dev, "failed to resume: %d\n", err);
@@ -1460,8 +1470,8 @@ tegra_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
 }
 
 static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = {
-	.disable = tegra_hdmi_encoder_disable,
-	.enable = tegra_hdmi_encoder_enable,
+	.atomic_disable = tegra_hdmi_encoder_disable,
+	.atomic_enable = tegra_hdmi_encoder_enable,
 	.atomic_check = tegra_hdmi_encoder_atomic_check,
 };
 
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 01475995d9cc8..7dea0cac61233 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -90,7 +90,8 @@ static void tegra_dc_write_regs(struct tegra_dc *dc,
 		tegra_dc_writel(dc, table[i].value, table[i].offset);
 }
 
-static void tegra_rgb_encoder_disable(struct drm_encoder *encoder)
+static void tegra_rgb_encoder_disable(struct drm_encoder *encoder,
+				      struct drm_atomic_commit *state)
 {
 	struct tegra_output *output = encoder_to_output(encoder);
 	struct tegra_rgb *rgb = to_rgb(output);
@@ -99,12 +100,22 @@ static void tegra_rgb_encoder_disable(struct drm_encoder *encoder)
 	tegra_dc_commit(rgb->dc);
 }
 
-static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)
+static void tegra_rgb_encoder_enable(struct drm_encoder *encoder,
+				     struct drm_atomic_commit *state)
 {
-	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
 	struct tegra_output *output = encoder_to_output(encoder);
 	struct tegra_rgb *rgb = to_rgb(output);
-	u32 value;
+	struct drm_connector_state *conn_state;
+	struct drm_crtc_state *crtc_state;
+	struct drm_connector *connector;
+	struct drm_display_mode *mode;
+	u32 bus_flags, value;
+
+	/* Get mode flags */
+	connector = drm_atomic_get_new_connector_for_encoder(state, encoder);
+	conn_state = drm_atomic_get_new_connector_state(state, connector);
+	crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
+	mode = &crtc_state->adjusted_mode;
 
 	tegra_dc_write_regs(rgb->dc, rgb_enable, ARRAY_SIZE(rgb_enable));
 
@@ -201,8 +212,8 @@ static const struct drm_encoder_funcs tegra_rgb_encoder_funcs = {
 };
 
 static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = {
-	.disable = tegra_rgb_encoder_disable,
-	.enable = tegra_rgb_encoder_enable,
+	.atomic_disable = tegra_rgb_encoder_disable,
+	.atomic_enable = tegra_rgb_encoder_enable,
 	.atomic_check = tegra_rgb_encoder_atomic_check,
 };
 
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index ab81e412e05f1..85cd5c8cbef4e 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -2206,7 +2206,8 @@ static void tegra_sor_hdmi_scdc_start(struct tegra_sor *sor)
 	}
 }
 
-static void tegra_sor_hdmi_disable(struct drm_encoder *encoder)
+static void tegra_sor_hdmi_disable(struct drm_encoder *encoder,
+				   struct drm_atomic_commit *comm_state)
 {
 	struct tegra_output *output = encoder_to_output(encoder);
 	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
@@ -2247,13 +2248,17 @@ static void tegra_sor_hdmi_disable(struct drm_encoder *encoder)
 	host1x_client_suspend(&sor->client);
 }
 
-static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
+static void tegra_sor_hdmi_enable(struct drm_encoder *encoder,
+				  struct drm_atomic_commit *comm_state)
 {
 	struct tegra_output *output = encoder_to_output(encoder);
 	unsigned int h_ref_to_sync = 1, pulse_start, max_ac;
 	struct tegra_dc *dc = to_tegra_dc(encoder->crtc);
 	struct tegra_sor_hdmi_settings *settings;
+	struct drm_connector_state *conn_state;
 	struct tegra_sor *sor = to_sor(output);
+	struct drm_crtc_state *crtc_state;
+	struct drm_connector *connector;
 	struct tegra_sor_state *state;
 	struct drm_display_mode *mode;
 	unsigned long rate, pclk;
@@ -2262,7 +2267,10 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
 	int err;
 
 	state = to_sor_state(output->connector.state);
-	mode = &encoder->crtc->state->adjusted_mode;
+	connector = drm_atomic_get_new_connector_for_encoder(comm_state, encoder);
+	conn_state = drm_atomic_get_new_connector_state(comm_state, connector);
+	crtc_state = drm_atomic_get_new_crtc_state(comm_state, conn_state->crtc);
+	mode = &crtc_state->adjusted_mode;
 	pclk = mode->clock * 1000;
 
 	err = host1x_client_resume(&sor->client);
@@ -2660,8 +2668,8 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
 }
 
 static const struct drm_encoder_helper_funcs tegra_sor_hdmi_helpers = {
-	.disable = tegra_sor_hdmi_disable,
-	.enable = tegra_sor_hdmi_enable,
+	.atomic_disable = tegra_sor_hdmi_disable,
+	.atomic_enable = tegra_sor_hdmi_enable,
 	.atomic_check = tegra_sor_encoder_atomic_check,
 };
 
-- 
2.53.0


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

* [PATCH v5 09/11] drm/tegra: rgb: Parameterize configuration based on bus flags
  2026-10-29 15:28 [PATCH v5 00/11] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
                   ` (7 preceding siblings ...)
  2026-10-29 15:29 ` [PATCH v5 08/11] drm/tegra: Convert to use atomic versions of drm_encoder_helper_funcs Svyatoslav Ryhel
@ 2026-10-29 15:29 ` Svyatoslav Ryhel
  2026-10-29 15:29 ` [PATCH v5 10/11] dt-bindings: display: tegra: Document 8-bit CPU parallel interface Svyatoslav Ryhel
  2026-10-29 15:29 ` [PATCH v5 11/11] drm/tegra: Add support for 8-bit CPU interface Svyatoslav Ryhel
  10 siblings, 0 replies; 15+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-29 15:29 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
  Cc: dri-devel, devicetree, linux-kernel, linux-tegra

Parameterize configuration based on bus flags passed from the bridge or
panel. The list of supported flags includes now pixel clock polarity,
display enable (DE) polarity, and data alignment.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/gpu/drm/tegra/rgb.c | 39 +++++++++++++++++++++++++++++++++----
 1 file changed, 35 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 7dea0cac61233..d8f41d1ca350e 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -5,6 +5,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/media-bus-format.h>
 #include <linux/of.h>
 
 #include <drm/drm_atomic_helper.h>
@@ -106,6 +107,7 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder,
 	struct tegra_output *output = encoder_to_output(encoder);
 	struct tegra_rgb *rgb = to_rgb(output);
 	struct drm_connector_state *conn_state;
+	struct drm_bridge_state *bridge_state;
 	struct drm_crtc_state *crtc_state;
 	struct drm_connector *connector;
 	struct drm_display_mode *mode;
@@ -117,12 +119,21 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder,
 	crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
 	mode = &crtc_state->adjusted_mode;
 
+	/* Get but flags from the bridge state. */
+	bridge_state = drm_atomic_get_new_bridge_state(state, output->bridge);
+	if (bridge_state)
+		bus_flags = bridge_state->input_bus_cfg.flags;
+	else
+		bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE |
+			    DRM_BUS_FLAG_DE_HIGH |
+			    DRM_BUS_FLAG_DATA_MSB_TO_LSB;
+
 	tegra_dc_write_regs(rgb->dc, rgb_enable, ARRAY_SIZE(rgb_enable));
 
 	value = DE_SELECT_ACTIVE | DE_CONTROL_NORMAL;
 	tegra_dc_writel(rgb->dc, value, DC_DISP_DATA_ENABLE_OPTIONS);
 
-	/* configure H- and V-sync signal polarities */
+	/* configure H- and V-sync and pixel clock signal polarities */
 	value = tegra_dc_readl(rgb->dc, DC_COM_PIN_OUTPUT_POLARITY(1));
 
 	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
@@ -135,11 +146,31 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder,
 	else
 		value &= ~BIT(PIN_LVS);
 
+	if (bus_flags & DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE)
+		value &= ~BIT(PIN_LSC0);
+	else
+		value |= BIT(PIN_LSC0);
+
 	tegra_dc_writel(rgb->dc, value, DC_COM_PIN_OUTPUT_POLARITY(1));
 
-	/* XXX: parameterize? */
-	value = DISP_DATA_FORMAT_DF1P1C | DISP_ALIGNMENT_MSB |
-		DISP_ORDER_RED_BLUE;
+	/* configure DE signal polarities */
+	value = tegra_dc_readl(rgb->dc, DC_COM_PIN_OUTPUT_POLARITY(3));
+
+	if (bus_flags & DRM_BUS_FLAG_DE_LOW)
+		value |= BIT(PIN_LSPI);
+	else
+		value &= ~BIT(PIN_LSPI);
+
+	tegra_dc_writel(rgb->dc, value, DC_COM_PIN_OUTPUT_POLARITY(3));
+
+	/* configure DATA order and alignment */
+	value = DISP_DATA_FORMAT_DF1P1C | DISP_ORDER_RED_BLUE;
+
+	if (bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB)
+		value |= DISP_ALIGNMENT_LSB;
+	else
+		value &= ~DISP_ALIGNMENT_LSB;
+
 	tegra_dc_writel(rgb->dc, value, DC_DISP_DISP_INTERFACE_CONTROL);
 
 	tegra_dc_commit(rgb->dc);
-- 
2.53.0


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

* [PATCH v5 10/11] dt-bindings: display: tegra: Document 8-bit CPU parallel interface
  2026-10-29 15:28 [PATCH v5 00/11] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
                   ` (8 preceding siblings ...)
  2026-10-29 15:29 ` [PATCH v5 09/11] drm/tegra: rgb: Parameterize configuration based on bus flags Svyatoslav Ryhel
@ 2026-10-29 15:29 ` Svyatoslav Ryhel
  2026-10-29 15:29 ` [PATCH v5 11/11] drm/tegra: Add support for 8-bit CPU interface Svyatoslav Ryhel
  10 siblings, 0 replies; 15+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-29 15:29 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
  Cc: dri-devel, devicetree, linux-kernel, linux-tegra

Document 8-bit CPU parallel MIPI DBI Type B interface provided by
Tegra20/30 SoCs display controller.

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 .../display/tegra/nvidia,tegra20-dbi.yaml     | 113 ++++++++++++++++++
 .../display/tegra/nvidia,tegra20-dc.yaml      |  22 ++++
 2 files changed, 135 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dbi.yaml

diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dbi.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dbi.yaml
new file mode 100644
index 0000000000000..cd568d19c363a
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dbi.yaml
@@ -0,0 +1,113 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/tegra/nvidia,tegra20-dbi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nvidia Tegra Display Controller based MIPI DBI Type B interface
+
+maintainers:
+  - Svyatoslav Ryhel <clamor95@gmail.com>
+
+description: The display controller in Tegra20/30 SoCs features an
+  8-bit SPI interface that closely resembles the MIPI DBI Type B
+  protocol and is referred to as '8-bit CPU'. Each display controller
+  provides two such interfaces, which can be used to send MIPI DCS
+  commands to initialize and control the panel while image data is
+  transmitted via 16/18/24-line RGB.
+
+properties:
+  reg:
+    maximum: 1
+    description: DBI interface used 0 - primary, 1 - secondary.
+
+  panel:
+    type: object
+    description: Node of supported panel driven by the bridge.
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/$defs/port-base
+        unevaluatedProperties: false
+        description: Video port for RGB input.
+
+        properties:
+          endpoint:
+            $ref: /schemas/graph.yaml#/$defs/endpoint-base
+            unevaluatedProperties: false
+
+            properties:
+              bus-width:
+                enum: [ 16, 18, 24 ]
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Video port for DBI output (panel or connector).
+
+    required:
+      - port@0
+      - port@1
+
+required:
+  - reg
+  - panel
+  - ports
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    dc@54200000 {
+        reg = <0x54200000 0x00040000>;
+
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        dbi@0 {
+            reg = <0>;
+
+            panel {
+                compatible = "hit,tx10d07vm0baa";
+
+                reset-gpios = <&gpio 175 GPIO_ACTIVE_LOW>;
+
+                avci-supply = <&vcc_2v8_lcd>;
+                iovcc-supply = <&iovcc_1v8_lcd>;
+
+                backlight = <&backlight>;
+
+                port {
+                    panel_input: endpoint {
+                        remote-endpoint = <&bridge_output>;
+                    };
+                };
+            };
+
+            ports {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                port@0 {
+                    reg = <0>;
+
+                    bridge_input: endpoint {
+                        remote-endpoint = <&dpi_output>;
+                        bus-width = <24>;
+                    };
+                };
+
+                port@1 {
+                    reg = <1>;
+
+                    bridge_output: endpoint {
+                        remote-endpoint = <&panel_input>;
+                    };
+                };
+            };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml
index 69be95afd5627..ffeb124905dbf 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml
@@ -79,6 +79,16 @@ properties:
   rgb:
     type: object
 
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+patternProperties:
+  "^dbi@[0-1]$":
+    $ref: /schemas/display/tegra/nvidia,tegra20-dbi.yaml#
+
 allOf:
   - if:
       properties:
@@ -155,6 +165,18 @@ allOf:
             - const: wind
             - const: wint
 
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - nvidia,tegra114-dc
+              - nvidia,tegra124-dc
+              - nvidia,tegra210-dc
+    then:
+      patternProperties:
+        "^dbi@[0-1]$": false
+
 additionalProperties: false
 
 required:
-- 
2.53.0


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

* [PATCH v5 11/11] drm/tegra: Add support for 8-bit CPU interface
  2026-10-29 15:28 [PATCH v5 00/11] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
                   ` (9 preceding siblings ...)
  2026-10-29 15:29 ` [PATCH v5 10/11] dt-bindings: display: tegra: Document 8-bit CPU parallel interface Svyatoslav Ryhel
@ 2026-10-29 15:29 ` Svyatoslav Ryhel
  2026-10-29 15:55   ` sashiko-bot
  10 siblings, 1 reply; 15+ messages in thread
From: Svyatoslav Ryhel @ 2026-10-29 15:29 UTC (permalink / raw)
  To: Neil Armstrong, Jessica Zhang, David Airlie, Simona Vetter,
	Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, Mikko Perttunen, Svyatoslav Ryhel
  Cc: dri-devel, devicetree, linux-kernel, linux-tegra

The display controller in Tegra20/30 SoCs features an 8-bit SPI interface
that closely resembles the MIPI DBI Type B protocol and is referred to as
'8-bit CPU'. Each display controller provides two such interfaces, which
can be used to send MIPI DCS commands to initialize and control the panel
while image data is transmitted via 16/18/24-line RGB.

The '8-bit CPU', although being the DC interface, is modeled using a
bridge framework for flexibility and convenience. This implementation does
not change the existing codebase besides registering the bus.

Suggested-by: Mikko Perttunen <mperttunen@nvidia.com> # DC command transfer
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/gpu/drm/tegra/Makefile |   1 +
 drivers/gpu/drm/tegra/dbi.c    | 632 +++++++++++++++++++++++++++++++++
 drivers/gpu/drm/tegra/dc.c     |   4 +
 drivers/gpu/drm/tegra/dc.h     |   3 +
 4 files changed, 640 insertions(+)
 create mode 100644 drivers/gpu/drm/tegra/dbi.c

diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile
index e399b40d64a1d..ae7fccb3d6421 100644
--- a/drivers/gpu/drm/tegra/Makefile
+++ b/drivers/gpu/drm/tegra/Makefile
@@ -18,6 +18,7 @@ tegra-drm-y := \
 	hdmi.o \
 	mipi-phy.o \
 	dsi.o \
+	dbi.o \
 	sor.o \
 	dpaux.o \
 	gr2d.o \
diff --git a/drivers/gpu/drm/tegra/dbi.c b/drivers/gpu/drm/tegra/dbi.c
new file mode 100644
index 0000000000000..e129be0f21da5
--- /dev/null
+++ b/drivers/gpu/drm/tegra/dbi.c
@@ -0,0 +1,632 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Nvidia Tegra20/30 MIPI-DBI Type B host driver
+ *
+ * Copyright (C) 2026 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/property.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_mipi_dbi_bus.h>
+#include <drm/drm_of.h>
+
+#include <video/mipi_display.h>
+
+#include "dc.h"
+
+enum tegra_dbi_dc_signal {
+	TEGRA_DBI_COMMAND,
+	TEGRA_DBI_DATA
+};
+
+#define TEGRA_DBI_DATA_PINS_MAX			8
+#define TEGRA_DBI_INTERFACE_MAX			2
+
+struct reg_entry {
+	u32 offset;
+	u32 value;
+};
+
+static const struct reg_entry com_pin_output[] = {
+	{ DC_COM_PIN_OUTPUT_ENABLE(0),   0 },
+	{ DC_COM_PIN_OUTPUT_ENABLE(1),   0 },
+	{ DC_COM_PIN_OUTPUT_ENABLE(2),   0 },
+	{ DC_COM_PIN_OUTPUT_ENABLE(3),   0 },
+	{ DC_COM_PIN_OUTPUT_POLARITY(0), 0 },
+	{ DC_COM_PIN_OUTPUT_POLARITY(1), 0 },
+	{ DC_COM_PIN_OUTPUT_POLARITY(2), 0 },
+	{ DC_COM_PIN_OUTPUT_POLARITY(3), 0 },
+	{ DC_COM_PIN_OUTPUT_DATA(0),     0 },
+	{ DC_COM_PIN_OUTPUT_DATA(1),     0 },
+	{ DC_COM_PIN_OUTPUT_DATA(2),     0 },
+	{ DC_COM_PIN_OUTPUT_DATA(3),     0 },
+	{ DC_COM_PIN_OUTPUT_SELECT(0),   0 },
+	{ DC_COM_PIN_OUTPUT_SELECT(1),   0 },
+	{ DC_COM_PIN_OUTPUT_SELECT(2),   0 },
+	{ DC_COM_PIN_OUTPUT_SELECT(3),   0 },
+	{ DC_COM_PIN_OUTPUT_SELECT(4),   0 },
+	{ DC_COM_PIN_OUTPUT_SELECT(5),   0 },
+	{ DC_COM_PIN_OUTPUT_SELECT(6),   0 },
+};
+
+struct tegra_dbi_priv {
+	struct device *dev;
+	struct tegra_dc *dc;
+
+	struct mipi_dbi dbi;
+	struct mipi_dbi_host dbi_host;
+
+	struct drm_bridge bridge;
+	struct drm_bridge *next_bridge;
+
+	const struct tegra_dbi_interface *index;
+	struct reg_entry com_pin_backup[ARRAY_SIZE(com_pin_output)];
+
+	u32 bus_width;
+	u32 reg;
+	bool active;
+};
+
+struct pin {
+	u32 reg;
+	u32 pos;
+};
+
+struct tegra_dbi_interface {
+	struct pin dc;
+	struct pin rw;
+	struct pin cs;
+	struct pin data[TEGRA_DBI_DATA_PINS_MAX];
+};
+
+static const struct tegra_dbi_interface tegra_dbi_idx[TEGRA_DBI_INTERFACE_MAX] = {
+	/* Nvidia Primary 8-bit CPU interface configuration */
+	{
+		.dc = { DC_COM_PIN_OUTPUT_DATA(3), PIN_LDC  },
+		.rw = { DC_COM_PIN_OUTPUT_DATA(1), PIN_LSC0 },
+		.cs = { DC_COM_PIN_OUTPUT_DATA(3), PIN_LCSN },
+		.data = {
+			{ DC_COM_PIN_OUTPUT_DATA(0), PIN_LD0 },
+			{ DC_COM_PIN_OUTPUT_DATA(0), PIN_LD1 },
+			{ DC_COM_PIN_OUTPUT_DATA(0), PIN_LD2 },
+			{ DC_COM_PIN_OUTPUT_DATA(0), PIN_LD3 },
+			{ DC_COM_PIN_OUTPUT_DATA(0), PIN_LD4 },
+			{ DC_COM_PIN_OUTPUT_DATA(0), PIN_LD5 },
+			{ DC_COM_PIN_OUTPUT_DATA(0), PIN_LD6 },
+			{ DC_COM_PIN_OUTPUT_DATA(0), PIN_LD7 },
+		},
+	},
+	/* Nvidia Secondary 8-bit CPU interface configuration */
+	{
+		.dc = { DC_COM_PIN_OUTPUT_DATA(2), PIN_LVP0 },
+		.rw = { DC_COM_PIN_OUTPUT_DATA(1), PIN_LSC1 },
+		.cs = { DC_COM_PIN_OUTPUT_DATA(2), PIN_LM0  },
+		.data = {
+			{ DC_COM_PIN_OUTPUT_DATA(2), PIN_LHP1 },
+			{ DC_COM_PIN_OUTPUT_DATA(2), PIN_LM1  },
+			{ DC_COM_PIN_OUTPUT_DATA(2), PIN_LDI  },
+			{ DC_COM_PIN_OUTPUT_DATA(2), PIN_LPP  },
+			{ DC_COM_PIN_OUTPUT_DATA(1), PIN_LPW0 },
+			{ DC_COM_PIN_OUTPUT_DATA(1), PIN_LPW2 },
+			{ DC_COM_PIN_OUTPUT_DATA(2), PIN_LVP1 },
+			{ DC_COM_PIN_OUTPUT_DATA(2), PIN_LHP0 },
+		},
+	},
+};
+
+static inline struct tegra_dbi_priv *mipi_dbi_to_tegra_dbi(struct mipi_dbi *dbi)
+{
+	return container_of(dbi, struct tegra_dbi_priv, dbi);
+}
+
+static inline struct tegra_dbi_priv *dbi_host_to_tegra_dbi(struct mipi_dbi_host *host)
+{
+	return container_of(host, struct tegra_dbi_priv, dbi_host);
+}
+
+static inline struct tegra_dbi_priv *bridge_to_tegra_dbi(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct tegra_dbi_priv, bridge);
+}
+
+static void tegra_dbi_pin_set(struct tegra_dc *dc, const struct pin *pin, bool value)
+{
+	u32 val = BIT(pin->pos + 1); /* unmask is 1 bit above position */
+
+	if (value)
+		val |= BIT(pin->pos);
+
+	tegra_dc_writel(dc, val, pin->reg);
+}
+
+static void tegra_dbi_write(struct tegra_dbi_priv *priv,
+			    enum tegra_dbi_dc_signal signal, u8 value)
+{
+	struct tegra_dc *dc = priv->dc;
+	const struct tegra_dbi_interface *idx = priv->index;
+
+	tegra_dbi_pin_set(dc, &idx->dc, signal);
+
+	tegra_dbi_pin_set(dc, &idx->cs, 0);
+	tegra_dbi_pin_set(dc, &idx->rw, 0);
+
+	for (int i = 0; i < TEGRA_DBI_DATA_PINS_MAX; i++)
+		tegra_dbi_pin_set(dc, &idx->data[i], field_get(BIT(i), value));
+
+	tegra_dbi_pin_set(dc, &idx->cs, 1);
+	tegra_dbi_pin_set(dc, &idx->rw, 1);
+
+	udelay(10);
+}
+
+static int tegra_dbi_dbi_command(struct mipi_dbi *dbi, u8 *cmd, u8 *param, size_t num)
+{
+	struct tegra_dbi_priv *priv = mipi_dbi_to_tegra_dbi(dbi);
+	u8 command = *cmd;
+
+	/* Blocks all commands when interface is not active */
+	if (!priv->active)
+		return -ENODEV;
+
+	tegra_dbi_write(priv, TEGRA_DBI_COMMAND, command);
+
+	for (int i = 0; i < num; i++)
+		tegra_dbi_write(priv, TEGRA_DBI_DATA, param[i]);
+
+	return 0;
+}
+
+static int tegra_dbi_host_attach(struct mipi_dbi_host *host, struct mipi_dbi_device *dbidev)
+{
+	struct tegra_dbi_priv *priv = dbi_host_to_tegra_dbi(host);
+	struct device *dev = priv->dev;
+	struct drm_bridge *bridge;
+	struct device_node *ep;
+	int ret;
+
+	bridge = of_drm_get_bridge_by_endpoint(dev_of_node(dev), 1, -1);
+	if (IS_ERR(bridge))
+		return PTR_ERR(bridge);
+
+	bridge->type = DRM_MODE_CONNECTOR_DPI;
+	priv->next_bridge = bridge;
+
+	/* get input ep (port0/endpoint0) */
+	ret = -EINVAL;
+	ep = of_graph_get_endpoint_by_regs(dev_of_node(dev), 0, 0);
+	if (ep) {
+		ret = of_property_read_u32(ep, "bus-width", &priv->bus_width);
+		of_node_put(ep);
+	}
+
+	if (ret)
+		priv->bus_width = 24;
+
+	return devm_drm_bridge_add(dev, &priv->bridge);
+}
+
+static void tegra_dbi_host_detach(struct mipi_dbi_host *host, struct mipi_dbi_device *dbidev)
+{
+	struct tegra_dbi_priv *priv = dbi_host_to_tegra_dbi(host);
+
+	drm_bridge_remove(&priv->bridge);
+	drm_bridge_put(priv->next_bridge);
+}
+
+static const struct mipi_dbi_host_ops tegra_dbi_host_ops = {
+	.attach = tegra_dbi_host_attach,
+	.detach = tegra_dbi_host_detach,
+};
+
+static void tegra_dbi_init_primary_interface(struct tegra_dbi_priv *priv)
+{
+	struct tegra_dc *dc = priv->dc;
+	u32 value;
+
+	value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_ENABLE(3));
+	value &= ~(BIT(PIN_LCSN) | BIT(PIN_LDC));
+	tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_ENABLE(3));
+
+	/* Set LSC0/LCSN/LDC output polarity HIGH */
+	value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_POLARITY(1));
+	value &= ~BIT(PIN_LSC0);
+	tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_POLARITY(1));
+
+	value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_POLARITY(3));
+	value &= ~(BIT(PIN_LCSN) | BIT(PIN_LDC));
+	tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_POLARITY(3));
+
+	value = PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LD0) |
+		PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LD1) |
+		PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LD2) |
+		PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LD3) |
+		PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LD4) |
+		PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LD5) |
+		PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LD6) |
+		PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LD7);
+	tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_SELECT(0));
+
+	value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_SELECT(3));
+	value &= ~PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LSC0);
+	value |= PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LSC0);
+	tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_SELECT(3));
+
+	value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_SELECT(6));
+	value &= ~PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LCSN);
+	value &= ~PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LDC);
+	value |= PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LCSN) |
+		 PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LDC);
+	tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_SELECT(6));
+}
+
+static void tegra_dbi_init_secondary_interface(struct tegra_dbi_priv *priv)
+{
+	struct tegra_dc *dc = priv->dc;
+	u32 value;
+
+	value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_ENABLE(2));
+	value &= ~(BIT(PIN_LDI) | BIT(PIN_LPP));
+	tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_ENABLE(2));
+
+	value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_SELECT(3));
+	value &= ~PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LPW0);
+	value &= ~PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LPW2);
+	value &= ~PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LSC1);
+	value |= PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LPW0) |
+		 PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LPW2) |
+		 PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LSC1);
+	tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_SELECT(3));
+
+	value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_SELECT(4));
+	value &= ~PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LHP0);
+	value &= ~PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LHP1);
+	value &= ~PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LVP0);
+	value &= ~PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LVP1);
+	value |= PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LHP0) |
+		 PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LHP1) |
+		 PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LVP0) |
+		 PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LVP1);
+	tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_SELECT(4));
+
+	value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_SELECT(5));
+	value &= ~PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LM0);
+	value &= ~PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LM1);
+	value &= ~PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LDI);
+	value &= ~PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LPP);
+	value |= PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LM0) |
+		 PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LM1) |
+		 PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LDI) |
+		 PIN_OUTPUT_SELECT_OUT(PIN_OUTPUT_SELECT_LPP);
+	tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_SELECT(5));
+}
+
+static void tegra_dbi_atomic_enable(struct drm_bridge *bridge,
+				    struct drm_atomic_commit *state)
+{
+	struct tegra_dbi_priv *priv = bridge_to_tegra_dbi(bridge);
+	struct drm_bridge *next_bridge = priv->next_bridge;
+	struct tegra_dc *dc = priv->dc;
+	u32 value;
+
+	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
+	value &= ~DISP_CTRL_MODE_MASK;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+
+	/* Disable all options except cursor if enabled */
+	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
+	value &= CURSOR_ENABLE;
+	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
+
+	tegra_dc_commit(dc);
+
+	tegra_dc_writel(dc, V_PULSE1_ENABLE, DC_DISP_DISP_SIGNAL_OPTIONS0);
+	tegra_dc_writel(dc, PULSE_POLARITY_LOW, DC_DISP_V_PULSE1_CONTROL);
+
+	tegra_dc_writel(dc, PULSE_END(1), DC_DISP_V_PULSE1_POSITION_A);
+	tegra_dc_writel(dc, 0, DC_DISP_V_PULSE1_POSITION_B);
+	tegra_dc_writel(dc, 0, DC_DISP_V_PULSE1_POSITION_C);
+
+	value = 1 << FRAME_INIT_SEQ_CYCLES_SHIFT |
+		DC_SIGNAL_VPULSE1 << INIT_SEQ_DC_SIGNAL_SHIFT |
+		INIT_SEQUENCE_MODE_PLCD | SEND_INIT_SEQUENCE;
+	tegra_dc_writel(dc, value, DC_DISP_INIT_SEQ_CONTROL);
+
+	 /* Init cycle 1: WRITE_MEMORY_START, set LCD_PCLK and COMMAND */
+	tegra_dc_writel(dc, MIPI_DCS_WRITE_MEMORY_START, DC_DISP_SPI_INIT_SEQ_DATA_A);
+	tegra_dc_writel(dc, 0x0, DC_DISP_SPI_INIT_SEQ_DATA_B);
+	tegra_dc_writel(dc, 0x0, DC_DISP_SPI_INIT_SEQ_DATA_C);
+	tegra_dc_writel(dc, BIT(12) | BIT(14), DC_DISP_SPI_INIT_SEQ_DATA_D);
+
+	value = SC1_H_QUALIFIER(SC_H_QUALIFIER_NONE) |
+		SC0_V_QUALIFIER(SC_V_QUALIFIER_VACTIVE) |
+		SC0_H_QUALIFIER(SC_H_QUALIFIER_HACTIVE);
+	tegra_dc_writel(dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
+
+	value = tegra_dc_readl(dc, DC_DISP_DISP_COLOR_CONTROL);
+	value &= ~BASE_COLOR_SIZE_MASK;
+
+	switch (priv->bus_width) {
+	case 16:
+		value |= BASE_COLOR_SIZE_565;
+		break;
+	case 18:
+		value |= BASE_COLOR_SIZE_666;
+		break;
+	case 24:
+	default:
+		value |= BASE_COLOR_SIZE_888;
+		break;
+	}
+
+	tegra_dc_writel(dc, value, DC_DISP_DISP_COLOR_CONTROL);
+
+	/*
+	 * Backup registers before unshadowing since actual register values
+	 * may differ and their state backed up now is the correct one.
+	 */
+	for (int i = 0; i < ARRAY_SIZE(com_pin_output); i++)
+		priv->com_pin_backup[i].value = tegra_dc_readl(dc,
+			priv->com_pin_backup[i].offset);
+
+	/* Unshadow to set up command and data pins */
+	tegra_dc_writel(dc, WRITE_MUX | READ_MUX, DC_CMD_STATE_ACCESS);
+
+	if (priv->reg)
+		tegra_dbi_init_secondary_interface(priv);
+	else
+		tegra_dbi_init_primary_interface(priv);
+
+	priv->active = true;
+
+	/*
+	 * DBI requires that the next bridge be enabled here in order to
+	 * send DCS commands, since the interface pins will subsequently be
+	 * reconfigured for video data.
+	 */
+	if (next_bridge->funcs && next_bridge->funcs->atomic_enable)
+		next_bridge->funcs->atomic_enable(next_bridge, state);
+
+	tegra_dbi_pin_set(dc, &priv->index->cs, 0);
+
+	priv->active = false;
+
+	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
+
+	for (int i = 0; i <  ARRAY_SIZE(com_pin_output); i++)
+		tegra_dc_writel(dc, priv->com_pin_backup[i].value,
+				priv->com_pin_backup[i].offset);
+
+	tegra_dc_commit(dc);
+
+	/* Set LDC pin to V Pulse 1 */
+	value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_SELECT(6));
+	value |= PIN_OUTPUT_SELECT_F4(PIN_OUTPUT_SELECT_LDC);
+	tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_SELECT(6));
+
+	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
+	value &= ~DISP_CTRL_MODE_MASK;
+	value |= DISP_CTRL_MODE_C_DISPLAY;
+	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
+}
+
+static void tegra_dbi_atomic_disable(struct drm_bridge *bridge,
+				     struct drm_atomic_commit *state)
+{
+	struct tegra_dbi_priv *priv = bridge_to_tegra_dbi(bridge);
+	struct drm_bridge *next_bridge = priv->next_bridge;
+	struct tegra_dc *dc = priv->dc;
+
+	for (int i = 0; i < ARRAY_SIZE(com_pin_output); i++)
+		priv->com_pin_backup[i].value = tegra_dc_readl(dc,
+			priv->com_pin_backup[i].offset);
+
+	tegra_dc_writel(dc, WRITE_MUX, DC_CMD_STATE_ACCESS);
+
+	if (priv->reg)
+		tegra_dbi_init_secondary_interface(priv);
+	else
+		tegra_dbi_init_primary_interface(priv);
+
+	priv->active = true;
+
+	/*
+	 * DBI requires that the next bridge be disabled here in order to
+	 * send DCS commands, since the interface pins will subsequently be
+	 * reconfigured for video data.
+	 */
+	if (next_bridge->funcs && next_bridge->funcs->atomic_disable)
+		next_bridge->funcs->atomic_disable(next_bridge, state);
+
+	priv->active = false;
+
+	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
+
+	for (int i = 0; i <  ARRAY_SIZE(com_pin_output); i++)
+		tegra_dc_writel(dc, priv->com_pin_backup[i].value,
+				priv->com_pin_backup[i].offset);
+
+	tegra_dc_commit(dc);
+}
+
+static int tegra_dbi_atomic_check(struct drm_bridge *bridge,
+				  struct drm_bridge_state *bridge_state,
+				  struct drm_crtc_state *crtc_state,
+				  struct drm_connector_state *conn_state)
+{
+	struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
+
+	/* Set safe default bus flags if panel does not specify them */
+	if (!bridge_state->input_bus_cfg.flags)
+		bridge_state->input_bus_cfg.flags = bridge->timings->input_bus_flags;
+
+	/* Default to positive sync */
+	if (!(adjusted_mode->flags &
+	      (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC)))
+		adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC;
+
+	if (!(adjusted_mode->flags &
+	      (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC)))
+		adjusted_mode->flags |= DRM_MODE_FLAG_PVSYNC;
+
+	return 0;
+}
+
+static int tegra_dbi_attach(struct drm_bridge *bridge,
+			    struct drm_encoder *encoder,
+			    enum drm_bridge_attach_flags flags)
+{
+	struct tegra_dbi_priv *priv = bridge_to_tegra_dbi(bridge);
+	struct device *dev = priv->dev;
+
+	priv->dc = dev_get_drvdata(dev->parent);
+	if (!priv->dc)
+		return -ENODEV;
+
+	return drm_bridge_attach(bridge->encoder, priv->next_bridge, bridge,
+				 flags);
+}
+
+static enum drm_mode_status tegra_dbi_mode_valid(struct drm_bridge *bridge,
+						 const struct drm_display_info *info,
+						 const struct drm_display_mode *mode)
+{
+	if (mode->hdisplay > 800)
+		return MODE_H_ILLEGAL;
+
+	if (mode->vdisplay > 800)
+		return MODE_V_ILLEGAL;
+
+	return MODE_OK;
+}
+
+static const struct drm_bridge_funcs tegra_dbi_funcs = {
+	.attach = tegra_dbi_attach,
+	.mode_valid = tegra_dbi_mode_valid,
+
+	.atomic_enable = tegra_dbi_atomic_enable,
+	.atomic_disable = tegra_dbi_atomic_disable,
+	.atomic_check = tegra_dbi_atomic_check,
+
+	.atomic_create_state = drm_atomic_helper_bridge_create_state,
+	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+};
+
+static const struct drm_bridge_timings default_tegra_dbi_timings = {
+	.input_bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE |
+			   DRM_BUS_FLAG_SYNC_SAMPLE_NEGEDGE |
+			   DRM_BUS_FLAG_DE_HIGH,
+};
+
+static int tegra_dbi_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mipi_dbi_device *dbidev;
+	struct tegra_dbi_priv *priv;
+	struct device_node *np;
+	int ret;
+
+	priv = devm_drm_bridge_alloc(dev, struct tegra_dbi_priv, bridge,
+				     &tegra_dbi_funcs);
+	if (IS_ERR(priv))
+		return PTR_ERR(priv);
+
+	priv->dev = dev;
+
+	/* Default to primaty */
+	ret = device_property_read_u32(dev, "reg", &priv->reg);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get reg\n");
+
+	if (priv->reg != 0 && priv->reg != 1)
+		return dev_err_probe(dev, -EINVAL, "Wrong reg value: %d\n", priv->reg);
+
+	priv->index = &tegra_dbi_idx[priv->reg];
+	memcpy(priv->com_pin_backup, com_pin_output, sizeof(com_pin_output));
+
+	/* Initialize MIPI DBI interface */
+	mutex_init(&priv->dbi.cmdlock);
+	priv->dbi.command = tegra_dbi_dbi_command;
+
+	priv->dbi_host.dev = dev;
+	priv->dbi_host.dbi = &priv->dbi;
+	priv->dbi_host.ops = &tegra_dbi_host_ops;
+
+	priv->bridge.of_node = dev_of_node(dev);
+	priv->bridge.timings = &default_tegra_dbi_timings;
+
+	np = of_graph_get_remote_node(dev_of_node(dev), 1, -1);
+	if (!np)
+		return -EINVAL;
+
+	dbidev = devm_mipi_dbi_device_register(dev, &priv->dbi_host, np);
+	of_node_put(np);
+	if (IS_ERR(dbidev))
+		return dev_err_probe(dev, PTR_ERR(dbidev),
+				     "Failed to register DBI device\n");
+
+	return 0;
+}
+
+static struct platform_driver tegra_dbi_driver = {
+	.driver.name = "tegra-dbi",
+	.probe = tegra_dbi_probe,
+};
+
+static void tegra_dbi_release(void *pdev)
+{
+	platform_device_unregister(pdev);
+	platform_driver_unregister(&tegra_dbi_driver);
+}
+
+int devm_tegra_dc_dbi_init(struct device *dev)
+{
+	struct device_node *dbi_np __free(device_node) =
+		of_get_available_child_by_name(dev_of_node(dev), "dbi");
+	struct platform_device_info dbi_info = { 0 };
+	struct platform_device *dbi;
+	int ret;
+
+	/* Missing node means that DBI is not used, continue */
+	if (!dbi_np)
+		return 0;
+
+	ret = platform_driver_register(&tegra_dbi_driver);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "Failed to register DBI driver\n");
+
+	dbi_info.parent = dev;
+	dbi_info.name = "tegra-dbi";
+	dbi_info.id = PLATFORM_DEVID_AUTO;
+	dbi_info.fwnode = of_fwnode_handle(dbi_np);
+	dbi_info.of_node_reused = true;
+
+	dbi = platform_device_register_full(&dbi_info);
+	if (IS_ERR(dbi)) {
+		platform_driver_unregister(&tegra_dbi_driver);
+		return dev_err_probe(dev, PTR_ERR(dbi),
+				     "Failed to create DBI device\n");
+	}
+
+	ret = devm_add_action_or_reset(dev, tegra_dbi_release, dbi);
+	if (ret)
+		return ret;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 5bbf2c028edd8..768cf82776632 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -3382,6 +3382,10 @@ static int tegra_dc_probe(struct platform_device *pdev)
 		}
 	}
 
+	err = devm_tegra_dc_dbi_init(&pdev->dev);
+	if (err)
+		return dev_err_probe(&pdev->dev, err, "failed to init DBI\n");
+
 	err = tegra_dc_rgb_probe(dc);
 	if (err < 0 && err != -ENODEV)
 		return dev_err_probe(&pdev->dev, err,
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index 2b87bb675d4cf..3df49422a2bd1 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -177,6 +177,9 @@ void tegra_dc_rgb_remove(struct tegra_dc *dc);
 int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc);
 int tegra_dc_rgb_exit(struct tegra_dc *dc);
 
+/* from dbi.c */
+int devm_tegra_dc_dbi_init(struct device *dev);
+
 #define DC_CMD_GENERAL_INCR_SYNCPT		0x000
 
 #define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL	0x001
-- 
2.53.0


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

* Re: [PATCH v5 03/11] drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver
  2026-10-29 15:29 ` [PATCH v5 03/11] drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver Svyatoslav Ryhel
@ 2026-10-29 15:40   ` sashiko-bot
  0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-10-29 15:40 UTC (permalink / raw)
  To: Svyatoslav Ryhel; +Cc: dri-devel, devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] drm/panel: selecting DRM_MIPI_DBI without SPI breaks CONFIG_SPI=n build
--

commit 6d2fecb3d7209826695a1566f8263dc71e26e02f
Author: Svyatoslav Ryhel <clamor95@gmail.com>

drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver

This commit adds a DRM panel driver for the Hitachi TX10D07VM0BAA 4" WVGA MIPI
DBI Type B panel used in the LG Optimus 2X P990 smartphone.

> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 7a520b4d60fef..0e86628bcdb80 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -274,6 +274,20 @@ config DRM_PANEL_HIMAX_HX8394
>  
>  	  If M is selected the module will be called panel-himax-hx8394.
>  
> +config DRM_PANEL_HITACHI_TX10D07VM0BAA
> +	tristate "Hitachi TX10D07VM0BAA MIPI DBI Type B panel"
> +	depends on OF
> +	depends on BACKLIGHT_CLASS_DEVICE
> +	select DRM_MIPI_DBI

[Severity: Low]
Does selecting DRM_MIPI_DBI here without a dependency on SPI cause a build
regression when CONFIG_SPI is disabled?

Previously, every Kconfig symbol selecting DRM_MIPI_DBI either depended on SPI
or used conditional selection on SPI. With DRM_PANEL_HITACHI_TX10D07VM0BAA
selecting DRM_MIPI_DBI unconditionally, drivers/gpu/drm/drm_mipi_dbi.c can now
be built with CONFIG_SPI=n.

In drivers/gpu/drm/drm_mipi_dbi.c, mipi_dbi_dcs_read_commands is defined
outside the CONFIG_SPI guard:

static const u8 mipi_dbi_dcs_read_commands[] = {
	MIPI_DCS_GET_DISPLAY_ID,
	...
};

However, its only user is mipi_dbi_spi_init(), which is inside the
#if IS_ENABLED(CONFIG_SPI) block:

int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi,
		      struct gpio_desc *dc)
{
	...
	dbi->read_commands = mipi_dbi_dcs_read_commands;

Because drivers/gpu/drm/Makefile enables -Wunused-const-variable:

subdir-ccflags-y += $(call cc-option, -Wunused-const-variable)

building with CONFIG_DRM_PANEL_HITACHI_TX10D07VM0BAA enabled and CONFIG_SPI=n
triggers an unused-const-variable warning on mipi_dbi_dcs_read_commands (which
becomes a build error with CONFIG_DRM_WERROR or CONFIG_WERROR).

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261029152909.234584-1-clamor95@gmail.com?part=3

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

* Re: [PATCH v5 07/11] drm/tegra: Make panel parsing by tegra_output_probe() optional
  2026-10-29 15:29 ` [PATCH v5 07/11] drm/tegra: Make panel parsing by tegra_output_probe() optional Svyatoslav Ryhel
@ 2026-10-29 15:41   ` sashiko-bot
  0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-10-29 15:41 UTC (permalink / raw)
  To: Svyatoslav Ryhel; +Cc: dri-devel, devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] drm/tegra: skipped bridge lookup in tegra_output_probe() breaks HDMI
--

commit 7887570371c7e1244731285458cba8facf234cb7
Author: Svyatoslav Ryhel <clamor95@gmail.com>

drm/tegra: Make panel parsing by tegra_output_probe() optional

This commit adds a boolean get_panel parameter to tegra_output_probe() so
callers can skip panel and bridge lookup in the common output probe path.
It passes false from tegra_dsi_probe() and tegra_hdmi_probe(), and true
from tegra_dc_rgb_probe() and tegra_sor_probe().

> diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
> index 0d2c0ab0a63e9..c0845bb45bb15 100644
> --- a/drivers/gpu/drm/tegra/hdmi.c
> +++ b/drivers/gpu/drm/tegra/hdmi.c
> @@ -1855,7 +1855,7 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
>  
>  	hdmi->output.dev = &pdev->dev;
>  
> -	err = tegra_output_probe(&hdmi->output);
> +	err = tegra_output_probe(&hdmi->output, false);
>  	if (err < 0)
>  		return err;
>  
> diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
> index d0ba3bd0aab8f..ff2e28a1e00b7 100644
> --- a/drivers/gpu/drm/tegra/output.c
> +++ b/drivers/gpu/drm/tegra/output.c
> @@ -94,7 +94,7 @@ static irqreturn_t hpd_irq(int irq, void *data)
>  	return IRQ_HANDLED;
>  }
>  
> -int tegra_output_probe(struct tegra_output *output)
> +int tegra_output_probe(struct tegra_output *output, bool get_panel)
>  {
>  	struct device_node *ddc, *panel;
>  	const void *edid;
> @@ -104,31 +104,33 @@ int tegra_output_probe(struct tegra_output *output)
>  	if (!output->of_node)
>  		output->of_node = output->dev->of_node;
>  
> -	err = drm_of_find_panel_or_bridge(output->of_node, -1, -1,
> -					  &output->panel, &output->bridge);
> -	if (err && err != -ENODEV)
> -		return err;
> -
> -	panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
> -	if (panel) {
> -		/*
> -		 * Don't mix nvidia,panel phandle with the graph in a
> -		 * device-tree.
> -		 */
> -		WARN_ON(output->panel || output->bridge);
> -
> -		if (output->panel) {
> -			drm_panel_put(output->panel);
> -			output->panel = NULL;
> -		}
> -
> -		output->panel = of_drm_find_panel(panel);
> -		of_node_put(panel);
> -
> -		if (IS_ERR(output->panel)) {
> -			err = PTR_ERR(output->panel);
> -			output->panel = NULL;
> +	if (get_panel) {
> +		err = drm_of_find_panel_or_bridge(output->of_node, -1, -1,
> +						  &output->panel, &output->bridge);
> +		if (err && err != -ENODEV)
>  			return err;

[Severity: High]
Does guarding drm_of_find_panel_or_bridge() behind get_panel in
tegra_output_probe() while passing false from tegra_hdmi_probe() break
HDMI outputs that use an OF graph bridge or connector?

In tegra_output_probe(), drm_of_find_panel_or_bridge() populates both
output->panel and output->bridge. Because tegra_output_probe() is the only
place in the driver that resolves output->bridge from the device tree OF
graph, calling tegra_output_probe(&hdmi->output, false) from
tegra_hdmi_probe() leaves hdmi->output.bridge permanently NULL and skips
probe deferral when a downstream bridge has not probed yet.

When tegra_hdmi_init() later runs:

drivers/gpu/drm/tegra/hdmi.c:tegra_hdmi_init() {
	...
	if (hdmi->output.bridge) {
		err = drm_bridge_attach(&hdmi->output.encoder, hdmi->output.bridge,
					NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
		...
		connector = drm_bridge_connector_init(drm, &hdmi->output.encoder);
		...
	} else {
		drm_connector_init_with_ddc(drm, &hdmi->output.connector,
					    &tegra_hdmi_connector_funcs,
					    DRM_MODE_CONNECTOR_HDMIA,
					    hdmi->output.ddc);
		...
	}
}

the if (hdmi->output.bridge) branch becomes unreachable. On Tegra20,
Tegra30, and Tegra114 boards that describe HDMI via an OF graph port
connected to an hdmi-connector or external bridge (where DDC and HPD
belong to the remote node rather than legacy properties on the HDMI node),
falling back to the non-bridge connector path leaves output->ddc and
output->hpd_gpio NULL, causing tegra_output_connector_detect() to
permanently report connector_status_disconnected.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261029152909.234584-1-clamor95@gmail.com?part=7

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

* Re: [PATCH v5 11/11] drm/tegra: Add support for 8-bit CPU interface
  2026-10-29 15:29 ` [PATCH v5 11/11] drm/tegra: Add support for 8-bit CPU interface Svyatoslav Ryhel
@ 2026-10-29 15:55   ` sashiko-bot
  0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-10-29 15:55 UTC (permalink / raw)
  To: Svyatoslav Ryhel; +Cc: dri-devel, devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] drm/tegra: manual next_bridge calls in tegra_dbi_atomic_enable() break disable
- [High] drm/tegra: double drm_bridge_remove() and leak in tegra_dbi_host_attach()
- [High] drm/tegra: driver conflict and deferral deadlock in devm_tegra_dc_dbi_init()
- [Medium] drm/tegra: misconfigured pin select and mux in tegra_dbi_atomic_enable()
- [Low] drm/tegra: missing select DRM_MIPI_DBI causes link error in tegra_dbi_probe()
--

commit 38b0aba6a8c5c45d84b4bce85bd75f2063d5eedc
Author: Svyatoslav Ryhel <clamor95@gmail.com>

drm/tegra: Add support for 8-bit CPU interface

This commit adds a MIPI DBI Type B host and DRM bridge driver to support the
8-bit CPU interface on Tegra20/30 display controllers.

> diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile
> index e399b40d64a1d..ae7fccb3d6421 100644
> --- a/drivers/gpu/drm/tegra/Makefile
> +++ b/drivers/gpu/drm/tegra/Makefile
> @@ -18,6 +18,7 @@ tegra-drm-y := \
>  	hdmi.o \
>  	mipi-phy.o \
>  	dsi.o \
> +	dbi.o \
>  	sor.o \
>  	dpaux.o \
>  	gr2d.o \

[Severity: Low]
Can building tegra-drm fail with an undefined reference to
devm_mipi_dbi_device_register() when CONFIG_DRM_MIPI_DBI is disabled or built
as a module while CONFIG_DRM_TEGRA is built-in?

Here dbi.o is unconditionally added to tegra-drm-y whenever CONFIG_DRM_TEGRA
is enabled, and tegra_dbi_probe() in drivers/gpu/drm/tegra/dbi.c calls
devm_mipi_dbi_device_register():

	dbidev = devm_mipi_dbi_device_register(dev, &priv->dbi_host, np);

However, devm_mipi_dbi_device_register() is defined in
drivers/gpu/drm/drm_mipi_dbi_bus.c, which is only compiled when
CONFIG_DRM_MIPI_DBI is enabled:

	obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o drm_mipi_dbi_bus.o

and include/drm/drm_mipi_dbi_bus.h does not provide fallback stubs.

Should config DRM_TEGRA in drivers/gpu/drm/tegra/Kconfig select
DRM_MIPI_DBI alongside DRM_MIPI_DSI and DRM_PANEL?

> diff --git a/drivers/gpu/drm/tegra/dbi.c b/drivers/gpu/drm/tegra/dbi.c
> new file mode 100644
> index 0000000000000..e129be0f21da5
> --- /dev/null
> +++ b/drivers/gpu/drm/tegra/dbi.c

[ ... ]

> +static int tegra_dbi_dbi_command(struct mipi_dbi *dbi, u8 *cmd, u8 *param, size_t num)
> +{
> +	struct tegra_dbi_priv *priv = mipi_dbi_to_tegra_dbi(dbi);
> +	u8 command = *cmd;
> +
> +	/* Blocks all commands when interface is not active */
> +	if (!priv->active)
> +		return -ENODEV;
> +
> +	tegra_dbi_write(priv, TEGRA_DBI_COMMAND, command);
> +
> +	for (int i = 0; i < num; i++)
> +		tegra_dbi_write(priv, TEGRA_DBI_DATA, param[i]);
> +
> +	return 0;
> +}
> +
> +static int tegra_dbi_host_attach(struct mipi_dbi_host *host, struct mipi_dbi_device *dbidev)
> +{
> +	struct tegra_dbi_priv *priv = dbi_host_to_tegra_dbi(host);
> +	struct device *dev = priv->dev;
> +	struct drm_bridge *bridge;
> +	struct device_node *ep;
> +	int ret;
> +
> +	bridge = of_drm_get_bridge_by_endpoint(dev_of_node(dev), 1, -1);
> +	if (IS_ERR(bridge))
> +		return PTR_ERR(bridge);
> +
> +	bridge->type = DRM_MODE_CONNECTOR_DPI;
> +	priv->next_bridge = bridge;
> +
> +	/* get input ep (port0/endpoint0) */
> +	ret = -EINVAL;
> +	ep = of_graph_get_endpoint_by_regs(dev_of_node(dev), 0, 0);
> +	if (ep) {
> +		ret = of_property_read_u32(ep, "bus-width", &priv->bus_width);
> +		of_node_put(ep);
> +	}
> +
> +	if (ret)
> +		priv->bus_width = 24;
> +
> +	return devm_drm_bridge_add(dev, &priv->bridge);
> +}
> +
> +static void tegra_dbi_host_detach(struct mipi_dbi_host *host, struct mipi_dbi_device *dbidev)
> +{
> +	struct tegra_dbi_priv *priv = dbi_host_to_tegra_dbi(host);
> +
> +	drm_bridge_remove(&priv->bridge);
> +	drm_bridge_put(priv->next_bridge);
> +}

[Severity: High]
Won't using devm_drm_bridge_add() in tegra_dbi_host_attach() alongside an
explicit drm_bridge_remove() in tegra_dbi_host_detach() cause
drm_bridge_remove() to run twice on priv->bridge during unbind, as well as
leak priv->next_bridge if devm_drm_bridge_add() fails?

In tegra_dbi_probe(), priv is allocated with devm_drm_bridge_alloc(), which
initializes priv->bridge.refcount to 1 and registers a devres action on
priv->dev to call drm_bridge_put_void(&priv->bridge).

When tegra_dbi_host_attach() calls devm_drm_bridge_add(dev, &priv->bridge),
it increments priv->bridge.refcount to 2 and registers a devres action on
priv->dev to call drm_bridge_remove_void(&priv->bridge).

When the DBI device or tegra-dbi unbinds, mipi_dbi_device_unregister()
invokes:

mipi_dbi_device_unregister()
  mipi_dbi_detach()
    tegra_dbi_host_detach()
      drm_bridge_remove(&priv->bridge)

which destroys bridge->hpd_mutex and bridge->hpd_state_mutex and drops
priv->bridge.refcount from 2 to 1.

When priv->dev's devres then runs drm_bridge_remove_void(&priv->bridge), it
calls drm_bridge_remove(&priv->bridge) a second time, destroying both mutexes
again and dropping priv->bridge.refcount from 1 to 0, which frees priv via
__drm_bridge_free(). Immediately afterward, the drm_bridge_put_void() devres
action from devm_drm_bridge_alloc() runs on the already-freed priv->bridge.

Also, if devm_drm_bridge_add() fails in tegra_dbi_host_attach(), the
reference on priv->next_bridge acquired by of_drm_get_bridge_by_endpoint() is
never released with drm_bridge_put(). Should tegra_dbi_host_attach() use
drm_bridge_add() instead of devm_drm_bridge_add()?

[ ... ]

> +static void tegra_dbi_atomic_enable(struct drm_bridge *bridge,
> +				    struct drm_atomic_commit *state)
> +{
> +	struct tegra_dbi_priv *priv = bridge_to_tegra_dbi(bridge);
> +	struct drm_bridge *next_bridge = priv->next_bridge;
> +	struct tegra_dc *dc = priv->dc;
> +	u32 value;

[ ... ]

> +	/* Unshadow to set up command and data pins */
> +	tegra_dc_writel(dc, WRITE_MUX | READ_MUX, DC_CMD_STATE_ACCESS);
> +
> +	if (priv->reg)
> +		tegra_dbi_init_secondary_interface(priv);
> +	else
> +		tegra_dbi_init_primary_interface(priv);
> +
> +	priv->active = true;
> +
> +	/*
> +	 * DBI requires that the next bridge be enabled here in order to
> +	 * send DCS commands, since the interface pins will subsequently be
> +	 * reconfigured for video data.
> +	 */
> +	if (next_bridge->funcs && next_bridge->funcs->atomic_enable)
> +		next_bridge->funcs->atomic_enable(next_bridge, state);
> +
> +	tegra_dbi_pin_set(dc, &priv->index->cs, 0);
> +
> +	priv->active = false;
> +
> +	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
> +
> +	for (int i = 0; i <  ARRAY_SIZE(com_pin_output); i++)
> +		tegra_dc_writel(dc, priv->com_pin_backup[i].value,
> +				priv->com_pin_backup[i].offset);
> +
> +	tegra_dc_commit(dc);
> +
> +	/* Set LDC pin to V Pulse 1 */
> +	value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_SELECT(6));
> +	value |= PIN_OUTPUT_SELECT_F4(PIN_OUTPUT_SELECT_LDC);
> +	tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_SELECT(6));
> +
> +	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
> +	value &= ~DISP_CTRL_MODE_MASK;
> +	value |= DISP_CTRL_MODE_C_DISPLAY;
> +	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
> +}
> +
> +static void tegra_dbi_atomic_disable(struct drm_bridge *bridge,
> +				     struct drm_atomic_commit *state)
> +{
> +	struct tegra_dbi_priv *priv = bridge_to_tegra_dbi(bridge);
> +	struct drm_bridge *next_bridge = priv->next_bridge;
> +	struct tegra_dc *dc = priv->dc;
> +
> +	for (int i = 0; i < ARRAY_SIZE(com_pin_output); i++)
> +		priv->com_pin_backup[i].value = tegra_dc_readl(dc,
> +			priv->com_pin_backup[i].offset);
> +
> +	tegra_dc_writel(dc, WRITE_MUX, DC_CMD_STATE_ACCESS);
> +
> +	if (priv->reg)
> +		tegra_dbi_init_secondary_interface(priv);
> +	else
> +		tegra_dbi_init_primary_interface(priv);
> +
> +	priv->active = true;
> +
> +	/*
> +	 * DBI requires that the next bridge be disabled here in order to
> +	 * send DCS commands, since the interface pins will subsequently be
> +	 * reconfigured for video data.
> +	 */
> +	if (next_bridge->funcs && next_bridge->funcs->atomic_disable)
> +		next_bridge->funcs->atomic_disable(next_bridge, state);
> +
> +	priv->active = false;
> +
> +	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
> +
> +	for (int i = 0; i <  ARRAY_SIZE(com_pin_output); i++)
> +		tegra_dc_writel(dc, priv->com_pin_backup[i].value,
> +				priv->com_pin_backup[i].offset);
> +
> +	tegra_dc_commit(dc);
> +}

[Severity: High]
Because tegra_dbi_attach() attaches priv->next_bridge to the encoder's
bridge_chain via drm_bridge_attach(), won't the DRM bridge chain helpers
already invoke next_bridge->funcs->atomic_enable() and atomic_disable(),
causing panel DCS commands during disable to fail with -ENODEV?

When disabling the display, drm_atomic_bridge_chain_disable() iterates
encoder->bridge_chain in reverse order, so it calls
priv->next_bridge->funcs->atomic_disable() (for example,
panel_bridge_atomic_disable() -> drm_panel_disable()) before
tegra_dbi_atomic_disable() runs. At that point priv->active is still false
and the DC pins are still in video mode, so every MIPI DCS command issued by
the panel's disable callback is rejected by tegra_dbi_dbi_command() with
-ENODEV, while drm_panel_disable() still marks panel->enabled = false.

When drm_atomic_bridge_chain_disable() subsequently calls
tegra_dbi_atomic_disable(), it sets priv->active = true and manually calls
next_bridge->funcs->atomic_disable(next_bridge, state) a second time, which
sees panel->enabled == false, logs "Skipping disable of already disabled
panel", and returns without sending the panel's DCS disable sequence.

Similarly, during enable, drm_atomic_bridge_chain_enable() calls
tegra_dbi_atomic_enable(), which manually calls
next_bridge->funcs->atomic_enable(next_bridge, state), and then
drm_atomic_bridge_chain_enable() advances to priv->next_bridge in
encoder->bridge_chain and calls next_bridge->funcs->atomic_enable() a second
time while priv->active is false.

[Severity: Medium]
In tegra_dbi_atomic_enable() and tegra_dbi_atomic_disable(), there also
appear to be a couple of register configuration issues:

First, at the end of tegra_dbi_atomic_enable():

	/* Set LDC pin to V Pulse 1 */
	value = tegra_dc_readl(dc, DC_COM_PIN_OUTPUT_SELECT(6));
	value |= PIN_OUTPUT_SELECT_F4(PIN_OUTPUT_SELECT_LDC);
	tegra_dc_writel(dc, value, DC_COM_PIN_OUTPUT_SELECT(6));

This unconditionally modifies PIN_LDC in DC_COM_PIN_OUTPUT_SELECT(6) without
clearing PIN_OUTPUT_SELECT_MASK(PIN_OUTPUT_SELECT_LDC) first, and without
checking priv->reg. When the secondary interface (priv->reg == 1) is used,
tegra_dbi_idx[1] uses PIN_LVP0 in DC_COM_PIN_OUTPUT_SELECT(4) as the D/C
pin rather than PIN_LDC. Does the secondary interface need V_PULSE1 routed to
its own D/C pin instead of PIN_LDC?

Second, tegra_dbi_atomic_disable() writes only WRITE_MUX to
DC_CMD_STATE_ACCESS:

	tegra_dc_writel(dc, WRITE_MUX, DC_CMD_STATE_ACCESS);

whereas tegra_dbi_atomic_enable() writes WRITE_MUX | READ_MUX before calling
tegra_dbi_init_primary_interface() or tegra_dbi_init_secondary_interface().
Without READ_MUX set, won't the tegra_dc_readl() calls in those init
functions read from the shadowed assembly registers while tegra_dc_writel()
writes to the active registers?

[ ... ]

> +static struct platform_driver tegra_dbi_driver = {
> +	.driver.name = "tegra-dbi",
> +	.probe = tegra_dbi_probe,
> +};
> +
> +static void tegra_dbi_release(void *pdev)
> +{
> +	platform_device_unregister(pdev);
> +	platform_driver_unregister(&tegra_dbi_driver);
> +}
> +
> +int devm_tegra_dc_dbi_init(struct device *dev)
> +{
> +	struct device_node *dbi_np __free(device_node) =
> +		of_get_available_child_by_name(dev_of_node(dev), "dbi");
> +	struct platform_device_info dbi_info = { 0 };
> +	struct platform_device *dbi;
> +	int ret;
> +
> +	/* Missing node means that DBI is not used, continue */
> +	if (!dbi_np)
> +		return 0;
> +
> +	ret = platform_driver_register(&tegra_dbi_driver);
> +	if (ret)
> +		return dev_err_probe(dev, ret,
> +				     "Failed to register DBI driver\n");
> +
> +	dbi_info.parent = dev;
> +	dbi_info.name = "tegra-dbi";
> +	dbi_info.id = PLATFORM_DEVID_AUTO;
> +	dbi_info.fwnode = of_fwnode_handle(dbi_np);
> +	dbi_info.of_node_reused = true;
> +
> +	dbi = platform_device_register_full(&dbi_info);
> +	if (IS_ERR(dbi)) {
> +		platform_driver_unregister(&tegra_dbi_driver);
> +		return dev_err_probe(dev, PTR_ERR(dbi),
> +				     "Failed to create DBI device\n");
> +	}
> +
> +	ret = devm_add_action_or_reset(dev, tegra_dbi_release, dbi);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
> index 5bbf2c028edd8..768cf82776632 100644
> --- a/drivers/gpu/drm/tegra/dc.c
> +++ b/drivers/gpu/drm/tegra/dc.c
> @@ -3382,6 +3382,10 @@ static int tegra_dc_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> +	err = devm_tegra_dc_dbi_init(&pdev->dev);
> +	if (err)
> +		return dev_err_probe(&pdev->dev, err, "failed to init DBI\n");
> +
>  	err = tegra_dc_rgb_probe(dc);
>  	if (err < 0 && err != -ENODEV)
>  		return dev_err_probe(&pdev->dev, err,

[Severity: High]
Can devm_tegra_dc_dbi_init() deadlock on probe deferral if the child MIPI DBI
panel driver is not yet bound when tegra_dc_rgb_probe() runs, and also fail
with -EBUSY if both display controllers have an available "dbi" child node?

In tegra_dc_probe(), devm_tegra_dc_dbi_init() is called immediately before
tegra_dc_rgb_probe(dc). In tegra_dbi_probe(), devm_mipi_dbi_device_register()
creates the child mipi_dbi_device, but priv->bridge is only registered later
in tegra_dbi_host_attach() when the panel driver binds to that DBI device and
calls devm_mipi_dbi_attach().

If the panel driver is built as a module, has not registered yet, or defers
its own probe (for example waiting on a regulator, GPIO, or backlight),
devm_tegra_dc_dbi_init() still returns 0, and then tegra_dc_rgb_probe(dc) ->
tegra_output_probe() -> drm_of_find_panel_or_bridge() returns -EPROBE_DEFER
because priv->bridge has not been added yet. When tegra_dc_probe() fails with
-EPROBE_DEFER, devres on &pdev->dev runs tegra_dbi_release(), which
unregisters the tegra-dbi platform device and destroys the child
mipi_dbi_device. With the mipi_dbi_device removed, how can the panel driver
ever bind to it and call tegra_dbi_host_attach() to resolve the deferral?

Additionally, tegra_dbi_driver is a single static platform_driver registered
inside devm_tegra_dc_dbi_init(). If both display controllers on Tegra20/30
have an available "dbi" child node, won't the second DC's call to
platform_driver_register(&tegra_dbi_driver) fail with -EBUSY, and unbinding
one DC unregister tegra_dbi_driver while the other DC is still using it?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261029152909.234584-1-clamor95@gmail.com?part=11

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

end of thread, other threads:[~2026-10-29 15:55 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29 15:28 [PATCH v5 00/11] drm/tegra: Add support for Tegra20/Tegra30 8-bit CPU interface Svyatoslav Ryhel
2026-10-29 15:28 ` [PATCH v5 01/11] drm: Add MIPI DBI bus support Svyatoslav Ryhel
2026-10-29 15:29 ` [PATCH v5 02/11] dt-bindings: display: panel: Document Hitachi TX10D07VM0BAA and LG LH400WV3 panels Svyatoslav Ryhel
2026-10-29 15:29 ` [PATCH v5 03/11] drm/panel: Add Hitachi TX10D07VM0BAA MIPI DBI Type B panel driver Svyatoslav Ryhel
2026-10-29 15:40   ` sashiko-bot
2026-10-29 15:29 ` [PATCH v5 04/11] drm/panel: Add LG LH400WV3-SD04 " Svyatoslav Ryhel
2026-10-29 15:29 ` [PATCH v5 05/11] drm/tegra: dc: Refactor and expand header Svyatoslav Ryhel
2026-10-29 15:29 ` [PATCH v5 06/11] dt-bindings: display: tegra: Document Tegra20 DSI port Svyatoslav Ryhel
2026-10-29 15:29 ` [PATCH v5 07/11] drm/tegra: Make panel parsing by tegra_output_probe() optional Svyatoslav Ryhel
2026-10-29 15:41   ` sashiko-bot
2026-10-29 15:29 ` [PATCH v5 08/11] drm/tegra: Convert to use atomic versions of drm_encoder_helper_funcs Svyatoslav Ryhel
2026-10-29 15:29 ` [PATCH v5 09/11] drm/tegra: rgb: Parameterize configuration based on bus flags Svyatoslav Ryhel
2026-10-29 15:29 ` [PATCH v5 10/11] dt-bindings: display: tegra: Document 8-bit CPU parallel interface Svyatoslav Ryhel
2026-10-29 15:29 ` [PATCH v5 11/11] drm/tegra: Add support for 8-bit CPU interface Svyatoslav Ryhel
2026-10-29 15:55   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox