All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH v3 1/8] drivers: Add AXI uclass
@ 2018-08-09 12:51 Mario Six
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 2/8] axi: Add ihs_axi driver Mario Six
                   ` (7 more replies)
  0 siblings, 8 replies; 11+ messages in thread
From: Mario Six @ 2018-08-09 12:51 UTC (permalink / raw)
  To: u-boot

Add a uclass for AXI (Advanced eXtensible Interface) busses, and a
driver for the gdsys IHS AXI bus on IHS FPGAs.

Signed-off-by: Mario Six <mario.six@gdsys.cc>
Reviewed-by: Simon Glass <sjg@chromium.org>

---

v2 -> v3:
* Fixed the AXI uclass comment ('busses' -> 'bus')
* Fixed style violations

v1 -> v2:
* Spelled out all abbreviations in the Kconfig help
* Split commit into uclass addition and driver addition

---
 drivers/Kconfig          |  2 ++
 drivers/Makefile         |  1 +
 drivers/axi/Kconfig      | 13 ++++++++
 drivers/axi/Makefile     |  8 +++++
 drivers/axi/axi-uclass.c | 39 ++++++++++++++++++++++++
 include/axi.h            | 78 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/dm/uclass-id.h   |  1 +
 7 files changed, 142 insertions(+)
 create mode 100644 drivers/axi/Kconfig
 create mode 100644 drivers/axi/Makefile
 create mode 100644 drivers/axi/axi-uclass.c
 create mode 100644 include/axi.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index c72abf89329..56536c4b191 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -8,6 +8,8 @@ source "drivers/adc/Kconfig"

 source "drivers/ata/Kconfig"

+source "drivers/axi/Kconfig"
+
 source "drivers/block/Kconfig"

 source "drivers/bootcount/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index d53208540ea..d296354b3c0 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -103,6 +103,7 @@ obj-y += smem/
 obj-y += soc/
 obj-$(CONFIG_REMOTEPROC) += remoteproc/
 obj-y += thermal/
+obj-y += axi/

 obj-$(CONFIG_MACH_PIC32) += ddr/microchip/
 endif
diff --git a/drivers/axi/Kconfig b/drivers/axi/Kconfig
new file mode 100644
index 00000000000..4e4153b4283
--- /dev/null
+++ b/drivers/axi/Kconfig
@@ -0,0 +1,13 @@
+menuconfig AXI
+	bool "AXI bus drivers"
+	help
+	  Support AXI (Advanced eXtensible Interface) busses, a on-chip
+	  interconnect specification for managing functional blocks in SoC
+	  designs, which is also often used in designs involving FPGAs (e.g.
+	  communication with IP cores in Xilinx FPGAs).
+
+	  These types of busses expose a virtual address space that can be
+	  accessed using different address widths (8, 16, and 32 are supported
+	  for now).
+
+	  Other similar bus architectures may be compatible as well.
diff --git a/drivers/axi/Makefile b/drivers/axi/Makefile
new file mode 100644
index 00000000000..100a77788a2
--- /dev/null
+++ b/drivers/axi/Makefile
@@ -0,0 +1,8 @@
+#
+# (C) Copyright 2017
+# Mario Six,  Guntermann & Drunck GmbH, mario.six at gdsys.cc
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-$(CONFIG_AXI) += axi-uclass.o
diff --git a/drivers/axi/axi-uclass.c b/drivers/axi/axi-uclass.c
new file mode 100644
index 00000000000..af8acd9f88b
--- /dev/null
+++ b/drivers/axi/axi-uclass.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2017
+ * Mario Six,  Guntermann & Drunck GmbH, mario.six at gdsys.cc
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <axi.h>
+
+int axi_read(struct udevice *dev, ulong address, void *data,
+	     enum axi_size_t size)
+{
+	struct axi_ops *ops = axi_get_ops(dev);
+
+	if (!ops->read)
+		return -ENOSYS;
+
+	return ops->read(dev, address, data, size);
+}
+
+int axi_write(struct udevice *dev, ulong address, void *data,
+	      enum axi_size_t size)
+{
+	struct axi_ops *ops = axi_get_ops(dev);
+
+	if (!ops->write)
+		return -ENOSYS;
+
+	return ops->write(dev, address, data, size);
+}
+
+UCLASS_DRIVER(axi) = {
+	.id		= UCLASS_AXI,
+	.name		= "axi",
+	.post_bind	= dm_scan_fdt_dev,
+	.flags		= DM_UC_FLAG_SEQ_ALIAS,
+};
+
diff --git a/include/axi.h b/include/axi.h
new file mode 100644
index 00000000000..2ff81c16af6
--- /dev/null
+++ b/include/axi.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2017
+ * Mario Six,  Guntermann & Drunck GmbH, mario.six at gdsys.cc
+ */
+
+#ifndef _AXI_H_
+#define _AXI_H_
+
+enum axi_size_t {
+	AXI_SIZE_8,
+	AXI_SIZE_16,
+	AXI_SIZE_32,
+};
+
+/**
+ * struct axi_ops - driver operations for AXI uclass
+ *
+ * Drivers should support these operations unless otherwise noted. These
+ * operations are intended to be used by uclass code, not directly from
+ * other code.
+ */
+struct axi_ops {
+	/**
+	 * read() - Read a single value from a specified address on a AXI bus
+	 *
+	 * @dev:	AXI bus to read from.
+	 * @address:	The address to read from.
+	 * @data:	Pointer to a variable that takes the data value read
+	 *		from the address on the AXI bus.
+	 * @size:	The size of the data to be read.
+	 * @return 0 if OK, -ve on error.
+	 */
+	int (*read)(struct udevice *dev, ulong address, void *data,
+		    enum axi_size_t size);
+
+	/**
+	 * write() - Write a single value to a specified address on a AXI bus
+	 *
+	 * @dev:	AXI bus to write to.
+	 * @address:	The address to write to.
+	 * @data:	Pointer to the data value to be written to the address
+	 *		on the AXI bus.
+	 * @size:	The size of the data to write.
+	 * @return 0 if OK, -ve on error.
+	 */
+	int (*write)(struct udevice *dev, ulong address, void *data,
+		     enum axi_size_t size);
+};
+
+#define axi_get_ops(dev)	((struct axi_ops *)(dev)->driver->ops)
+
+/**
+ * axi_read() - Read a single value from a specified address on a AXI bus
+ *
+ * @dev:	AXI bus to read from.
+ * @address:	The address to read from.
+ * @data:	Pointer to a variable that takes the data value read from the
+ *              address on the AXI bus.
+ * @size:	The size of the data to write.
+ * @return 0 if OK, -ve on error.
+ */
+int axi_read(struct udevice *dev, ulong address, void *data,
+	     enum axi_size_t size);
+
+/**
+ * axi_write() - Write a single value to a specified address on a AXI bus
+ *
+ * @dev:	AXI bus to write to.
+ * @address:	The address to write to.
+ * @data:	Pointer to the data value to be written to the address on the
+ *		AXI bus.
+ * @size:	The size of the data to write.
+ * @return 0 if OK, -ve on error.
+ */
+int axi_write(struct udevice *dev, ulong address, void *data,
+	      enum axi_size_t size);
+#endif
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index a39643ec5ee..618f43ad458 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -43,6 +43,7 @@ enum uclass_id {
 	UCLASS_I2C_GENERIC,	/* Generic I2C device */
 	UCLASS_I2C_MUX,		/* I2C multiplexer */
 	UCLASS_IDE,		/* IDE device */
+	UCLASS_AXI,		/* AXI bus */
 	UCLASS_IRQ,		/* Interrupt controller */
 	UCLASS_KEYBOARD,	/* Keyboard input device */
 	UCLASS_LED,		/* Light-emitting diode (LED) */
--
2.11.0

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

* [U-Boot] [PATCH v3 2/8] axi: Add ihs_axi driver
  2018-08-09 12:51 [U-Boot] [PATCH v3 1/8] drivers: Add AXI uclass Mario Six
@ 2018-08-09 12:51 ` Mario Six
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 3/8] axi: Add AXI sandbox driver and simple emulator Mario Six
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Mario Six @ 2018-08-09 12:51 UTC (permalink / raw)
  To: u-boot

Add a driver for the gdsys IHS AXI bus used on IHS FPGAs.

Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Mario Six <mario.six@gdsys.cc>

---

v2 -> v3:
* Added bindings file
* Added full documentation
* Added debug output in error cases
* Merged last two parameter of ihs_axi_transfer
* Added explaining comments

v1 -> v2:
New in v2

---
 .../devicetree/bindings/axi/gdsys,ihs_axi.txt      |  22 ++
 drivers/axi/Kconfig                                |  12 +
 drivers/axi/Makefile                               |   1 +
 drivers/axi/ihs_axi.c                              | 293 +++++++++++++++++++++
 4 files changed, 328 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/axi/gdsys,ihs_axi.txt
 create mode 100644 drivers/axi/ihs_axi.c

diff --git a/Documentation/devicetree/bindings/axi/gdsys,ihs_axi.txt b/Documentation/devicetree/bindings/axi/gdsys,ihs_axi.txt
new file mode 100644
index 00000000000..110788fa918
--- /dev/null
+++ b/Documentation/devicetree/bindings/axi/gdsys,ihs_axi.txt
@@ -0,0 +1,22 @@
+gdsys AXI busses of IHS FPGA devices
+
+Certain gdsys IHS FPGAs offer a interface to their built-in AXI bus with which
+the connected devices (usually IP cores) can be controlled via software.
+
+Required properties:
+- compatible: must be "gdsys,ihs_axi"
+- reg: describes the address and length of the AXI bus's register map (within
+  the FPGA's register space)
+
+Example:
+
+fpga0_axi_video0 {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible = "gdsys,ihs_axi";
+	reg = <0x170 0x10>;
+
+	axi_dev_1 {
+        ...
+	};
+};
diff --git a/drivers/axi/Kconfig b/drivers/axi/Kconfig
index 4e4153b4283..ae80c98af8a 100644
--- a/drivers/axi/Kconfig
+++ b/drivers/axi/Kconfig
@@ -11,3 +11,15 @@ menuconfig AXI
 	  for now).

 	  Other similar bus architectures may be compatible as well.
+
+if AXI
+
+config IHS_AXI
+	bool "Enable IHS AXI driver"
+	depends on DM
+	help
+	  Support for gdsys Integrated Hardware Systems Advanced eXtensible
+	  Interface (IHS AXI) bus on a gdsys IHS FPGA used to communicate with
+	  IP cores in the FPGA (e.g. video transmitter cores).
+
+endif
diff --git a/drivers/axi/Makefile b/drivers/axi/Makefile
index 100a77788a2..18d9380e9ba 100644
--- a/drivers/axi/Makefile
+++ b/drivers/axi/Makefile
@@ -6,3 +6,4 @@
 #

 obj-$(CONFIG_AXI) += axi-uclass.o
+obj-$(CONFIG_IHS_AXI) += ihs_axi.o
diff --git a/drivers/axi/ihs_axi.c b/drivers/axi/ihs_axi.c
new file mode 100644
index 00000000000..690aa7796b5
--- /dev/null
+++ b/drivers/axi/ihs_axi.c
@@ -0,0 +1,293 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2016
+ * Dirk Eibach,  Guntermann & Drunck GmbH, dirk.eibach at gdsys.cc
+ *
+ * (C) Copyright 2017, 2018
+ * Mario Six,  Guntermann & Drunck GmbH, mario.six at gdsys.cc
+ */
+
+#include <common.h>
+#include <axi.h>
+#include <dm.h>
+#include <regmap.h>
+
+/**
+ * struct ihs_axi_regs - Structure for the register map of a IHS AXI device
+ * @interrupt_status:         Status register to indicate certain events (e.g.
+ *			      error during transfer, transfer complete, etc.)
+ * @interrupt_enable_control: Register to both control which statuses will be
+ *			      indicated in the interrupt_status register, and
+ *			      to change bus settings
+ * @address_lsb:              Least significant 16-bit word of the address of a
+ *			      device to transfer data from/to
+ * @address_msb:              Most significant 16-bit word of the address of a
+ *			      device to transfer data from/to
+ * @write_data_lsb:           Least significant 16-bit word of the data to be
+ *			      written to a device
+ * @write_data_msb:           Most significant 16-bit word of the data to be
+ *			      written to a device
+ * @read_data_lsb:            Least significant 16-bit word of the data read
+ *			      from a device
+ * @read_data_msb:            Most significant 16-bit word of the data read
+ *			      from a device
+ */
+struct ihs_axi_regs {
+	u16 interrupt_status;
+	u16 interrupt_enable_control;
+	u16 address_lsb;
+	u16 address_msb;
+	u16 write_data_lsb;
+	u16 write_data_msb;
+	u16 read_data_lsb;
+	u16 read_data_msb;
+};
+
+/**
+ * ihs_axi_set() - Convenience macro to set values in register map
+ * @map:    The register map to write to
+ * @member: The member of the ihs_axi_regs structure to write
+ * @val:    The value to write to the register map
+ */
+#define ihs_axi_set(map, member, val) \
+	regmap_set(map, struct ihs_axi_regs, member, val)
+
+/**
+ * ihs_axi_get() - Convenience macro to read values from register map
+ * @map:    The register map to read from
+ * @member: The member of the ihs_axi_regs structure to read
+ * @valp:   Pointer to a buffer to receive the value read
+ */
+#define ihs_axi_get(map, member, valp) \
+	regmap_get(map, struct ihs_axi_regs, member, valp)
+
+/**
+ * struct ihs_axi_priv - Private data structure of IHS AXI devices
+ * @map: Register map for the IHS AXI device
+ */
+struct ihs_axi_priv {
+	struct regmap *map;
+};
+
+/**
+ * enum status_reg - Description of bits in the interrupt_status register
+ * @STATUS_READ_COMPLETE_EVENT:  A read transfer was completed
+ * @STATUS_WRITE_COMPLETE_EVENT: A write transfer was completed
+ * @STATUS_TIMEOUT_EVENT:        A timeout has occurred during the transfer
+ * @STATUS_ERROR_EVENT:          A error has occurred during the transfer
+ * @STATUS_AXI_INT:              A AXI interrupt has occurred
+ * @STATUS_READ_DATA_AVAILABLE:  Data is available to be read
+ * @STATUS_BUSY:                 The bus is busy
+ * @STATUS_INIT_DONE:            The bus has finished initializing
+ */
+enum status_reg {
+	STATUS_READ_COMPLETE_EVENT = BIT(15),
+	STATUS_WRITE_COMPLETE_EVENT = BIT(14),
+	STATUS_TIMEOUT_EVENT = BIT(13),
+	STATUS_ERROR_EVENT = BIT(12),
+	STATUS_AXI_INT = BIT(11),
+	STATUS_READ_DATA_AVAILABLE = BIT(7),
+	STATUS_BUSY = BIT(6),
+	STATUS_INIT_DONE = BIT(5),
+};
+
+/**
+ * enum control_reg - Description of bit fields in the interrupt_enable_control
+ *		      register
+ * @CONTROL_READ_COMPLETE_EVENT_ENABLE:  STATUS_READ_COMPLETE_EVENT will be
+ *					 raised in the interrupt_status register
+ * @CONTROL_WRITE_COMPLETE_EVENT_ENABLE: STATUS_WRITE_COMPLETE_EVENT will be
+ *					 raised in the interrupt_status register
+ * @CONTROL_TIMEOUT_EVENT_ENABLE:        STATUS_TIMEOUT_EVENT will be raised in
+ *					 the interrupt_status register
+ * @CONTROL_ERROR_EVENT_ENABLE:          STATUS_ERROR_EVENT will be raised in
+ *					 the interrupt_status register
+ * @CONTROL_AXI_INT_ENABLE:              STATUS_AXI_INT will be raised in the
+ *					 interrupt_status register
+ * @CONTROL_CMD_NOP:                     Configure bus to send a NOP command
+ *					 for the next transfer
+ * @CONTROL_CMD_WRITE:                   Configure bus to do a write transfer
+ * @CONTROL_CMD_WRITE_POST_INC:          Auto-increment address after write
+ *					 transfer
+ * @CONTROL_CMD_READ:                    Configure bus to do a read transfer
+ * @CONTROL_CMD_READ_POST_INC:           Auto-increment address after read
+ *					 transfer
+ */
+enum control_reg {
+	CONTROL_READ_COMPLETE_EVENT_ENABLE = BIT(15),
+	CONTROL_WRITE_COMPLETE_EVENT_ENABLE = BIT(14),
+	CONTROL_TIMEOUT_EVENT_ENABLE = BIT(13),
+	CONTROL_ERROR_EVENT_ENABLE = BIT(12),
+	CONTROL_AXI_INT_ENABLE = BIT(11),
+
+	CONTROL_CMD_NOP = 0x0,
+	CONTROL_CMD_WRITE = 0x8,
+	CONTROL_CMD_WRITE_POST_INC = 0x9,
+	CONTROL_CMD_READ = 0xa,
+	CONTROL_CMD_READ_POST_INC = 0xb,
+};
+
+/**
+ * enum axi_cmd - Determine if transfer is read or write transfer
+ * @AXI_CMD_READ:  The transfer should be a read transfer
+ * @AXI_CMD_WRITE: The transfer should be a write transfer
+ */
+enum axi_cmd {
+	AXI_CMD_READ,
+	AXI_CMD_WRITE,
+};
+
+/**
+ * ihs_axi_transfer() - Run transfer on the AXI bus
+ * @bus:           The AXI bus device on which to run the transfer on
+ * @address:       The address to use in the transfer (i.e. which address to
+ *		   read/write from/to)
+ * @cmd:           Should the transfer be a read or write transfer?
+ *
+ * Return: 0 if OK, -ve on error
+ */
+static int ihs_axi_transfer(struct udevice *bus, ulong address,
+			    enum axi_cmd cmd)
+{
+	struct ihs_axi_priv *priv = dev_get_priv(bus);
+	/* Try waiting for events up to 10 times */
+	const uint WAIT_TRIES = 10;
+	u16 wait_mask = STATUS_TIMEOUT_EVENT |
+			STATUS_ERROR_EVENT;
+	u16 complete_flag;
+	u16 status;
+	uint k;
+
+	if (cmd == AXI_CMD_READ) {
+		complete_flag = STATUS_READ_COMPLETE_EVENT;
+		cmd = CONTROL_CMD_READ;
+	} else {
+		complete_flag = STATUS_WRITE_COMPLETE_EVENT;
+		cmd = CONTROL_CMD_WRITE;
+	}
+
+	wait_mask |= complete_flag;
+
+	/* Lower 16 bit */
+	ihs_axi_set(priv->map, address_lsb, address & 0xffff);
+	/* Upper 16 bit */
+	ihs_axi_set(priv->map, address_msb, (address >> 16) & 0xffff);
+
+	ihs_axi_set(priv->map, interrupt_status, wait_mask);
+	ihs_axi_set(priv->map, interrupt_enable_control, cmd);
+
+	for (k = WAIT_TRIES; k > 0; --k) {
+		ihs_axi_get(priv->map, interrupt_status, &status);
+		if (status & wait_mask)
+			break;
+		udelay(1);
+	}
+
+	/*
+	 * k == 0 -> Tries ran out with no event we were waiting for actually
+	 * occurring.
+	 */
+	if (!k)
+		ihs_axi_get(priv->map, interrupt_status, &status);
+
+	if (status & complete_flag)
+		return 0;
+
+	if (status & STATUS_ERROR_EVENT) {
+		debug("%s: Error occurred during transfer\n", bus->name);
+		return -EIO;
+	}
+
+	debug("%s: Transfer timed out\n", bus->name);
+	return -ETIMEDOUT;
+}
+
+/*
+ * API
+ */
+
+static int ihs_axi_read(struct udevice *dev, ulong address, void *data,
+			enum axi_size_t size)
+{
+	struct ihs_axi_priv *priv = dev_get_priv(dev);
+	int ret;
+	u16 data_lsb, data_msb;
+	u32 *p = data;
+
+	if (size != AXI_SIZE_32) {
+		debug("%s: transfer size '%d' not supported\n",
+		      dev->name, size);
+		return -ENOSYS;
+	}
+
+	ret = ihs_axi_transfer(dev, address, AXI_CMD_READ);
+	if (ret < 0) {
+		debug("%s: Error during AXI transfer (err = %d)\n",
+		      dev->name, ret);
+		return ret;
+	}
+
+	ihs_axi_get(priv->map, read_data_lsb, &data_lsb);
+	ihs_axi_get(priv->map, read_data_msb, &data_msb);
+
+	/* Assemble data from two 16-bit words */
+	*p = (data_msb << 16) | data_lsb;
+
+	return 0;
+}
+
+static int ihs_axi_write(struct udevice *dev, ulong address, void *data,
+			 enum axi_size_t size)
+{
+	struct ihs_axi_priv *priv = dev_get_priv(dev);
+	int ret;
+	u32 *p = data;
+
+	if (size != AXI_SIZE_32) {
+		debug("%s: transfer size '%d' not supported\n",
+		      dev->name, size);
+		return -ENOSYS;
+	}
+
+	/* Lower 16 bit */
+	ihs_axi_set(priv->map, write_data_lsb, *p & 0xffff);
+	/* Upper 16 bit */
+	ihs_axi_set(priv->map, write_data_msb, (*p >> 16) & 0xffff);
+
+	ret = ihs_axi_transfer(dev, address, AXI_CMD_WRITE);
+	if (ret < 0) {
+		debug("%s: Error during AXI transfer (err = %d)\n",
+		      dev->name, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct udevice_id ihs_axi_ids[] = {
+	{ .compatible = "gdsys,ihs_axi" },
+	{ /* sentinel */ }
+};
+
+static const struct axi_ops ihs_axi_ops = {
+	.read = ihs_axi_read,
+	.write = ihs_axi_write,
+};
+
+static int ihs_axi_probe(struct udevice *dev)
+{
+	struct ihs_axi_priv *priv = dev_get_priv(dev);
+
+	regmap_init_mem(dev_ofnode(dev), &priv->map);
+
+	return 0;
+}
+
+U_BOOT_DRIVER(ihs_axi_bus) = {
+	.name           = "ihs_axi_bus",
+	.id             = UCLASS_AXI,
+	.of_match       = ihs_axi_ids,
+	.ops		= &ihs_axi_ops,
+	.priv_auto_alloc_size = sizeof(struct ihs_axi_priv),
+	.probe          = ihs_axi_probe,
+};
--
2.11.0

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

* [U-Boot] [PATCH v3 3/8] axi: Add AXI sandbox driver and simple emulator
  2018-08-09 12:51 [U-Boot] [PATCH v3 1/8] drivers: Add AXI uclass Mario Six
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 2/8] axi: Add ihs_axi driver Mario Six
@ 2018-08-09 12:51 ` Mario Six
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 4/8] sandbox: Add and build AXI bus and device Mario Six
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Mario Six @ 2018-08-09 12:51 UTC (permalink / raw)
  To: u-boot

Add test infrastructure and tests for the AXI uclass.

Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Mario Six <mario.six@gdsys.cc>

---

v2 -> v3:
* Fixed style violations
* Made read/write functions static
* Reworked pointer casts for different data sizes (8/16/32 bits)
* Moved functions used only by sandbox to distinct include file
* Added full documentation
* Added debug output in error case
* Added some inline documentation
* Marked some more functions as static

v1 -> v2:
* Spelled out abbreviations in Kconfig help
* Expanded emulation documentation
* Renamed storage pointer to storep
* Expanded AXI emulator usage documentation
* Switched AXI emulator to aligned access

---
 arch/sandbox/include/asm/axi.h |  66 ++++++++++++++++++++++
 drivers/axi/Kconfig            |   7 +++
 drivers/axi/Makefile           |   3 +
 drivers/axi/axi-emul-uclass.c  |  85 ++++++++++++++++++++++++++++
 drivers/axi/axi_sandbox.c      |  77 ++++++++++++++++++++++++++
 drivers/axi/sandbox_store.c    | 123 +++++++++++++++++++++++++++++++++++++++++
 include/axi.h                  |  74 +++++++++++++++++++------
 include/dm/uclass-id.h         |   1 +
 8 files changed, 419 insertions(+), 17 deletions(-)
 create mode 100644 arch/sandbox/include/asm/axi.h
 create mode 100644 drivers/axi/axi-emul-uclass.c
 create mode 100644 drivers/axi/axi_sandbox.c
 create mode 100644 drivers/axi/sandbox_store.c

diff --git a/arch/sandbox/include/asm/axi.h b/arch/sandbox/include/asm/axi.h
new file mode 100644
index 00000000000..d483f7b65a0
--- /dev/null
+++ b/arch/sandbox/include/asm/axi.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six at gdsys.cc
+ */
+
+#ifndef __asm_axi_h
+#define __asm_axi_h
+
+#define axi_emul_get_ops(dev)	((struct axi_emul_ops *)(dev)->driver->ops)
+
+/**
+ * axi_sandbox_get_emul() - Retrieve a pointer to a AXI emulation device
+ * @bus:     The AXI bus from which to retrieve a emulation device
+ * @address: The address of a transfer that should be handled by a emulation
+ *	     device
+ * @length:  The data width of a transfer that should be handled by a emulation
+ *	     device
+ * @emulp:   Pointer to a buffer receiving the emulation device that handles
+ *	     the transfer specified by the address and length parameters
+ *
+ * To test the AXI uclass, we implement a simple AXI emulation device, which is
+ * a virtual device on a AXI bus that exposes a simple storage interface: When
+ * reading and writing from the device, the addresses are translated to offsets
+ * within the device's storage. For write accesses the data is written to the
+ * specified storage offset, and for read accesses the data is read from the
+ * specified storage offset.
+ *
+ * A DTS entry might look like this:
+ *
+ * axi: axi at 0 {
+ *	compatible = "sandbox,axi";
+ *	#address-cells = <0x1>;
+ *	#size-cells = <0x1>;
+ *	store at 0 {
+ *		compatible = "sandbox,sandbox_store";
+ *		reg = <0x0 0x400>;
+ *	};
+ * };
+ *
+ * This function may then be used to retrieve the pointer to the sandbox_store
+ * emulation device given the AXI bus device, and the data (address, data
+ * width) of a AXI transfer which should be handled by a emulation device.
+ *
+ * Return: 0 of OK, -ENODEV if no device capable of handling the specified
+ *	   transfer exists or the device could not be retrieved
+ */
+int axi_sandbox_get_emul(struct udevice *bus, ulong address, uint length,
+			 struct udevice **emulp);
+/**
+ * axi_get_store() - Get address of internal storage of a emulated AXI device
+ * @dev:	Emulated AXI device to get the pointer of the internal storage
+ *		for.
+ * @storep:	Pointer to the internal storage of the emulated AXI device.
+ *
+ * To preset or read back the contents internal storage of the emulated AXI
+ * device, this function returns the pointer to the storage. Changes to the
+ * contents of the storage are reflected when using the AXI read/write API
+ * methods, and vice versa, so by using this method expected read data can be
+ * set up in advance, and written data can be checked in unit tests.
+ *
+ * Return: 0 if OK, -ve on error.
+ */
+int axi_get_store(struct udevice *dev, u8 **storep);
+
+#endif /* __asm_axi_h */
diff --git a/drivers/axi/Kconfig b/drivers/axi/Kconfig
index ae80c98af8a..f81d843f894 100644
--- a/drivers/axi/Kconfig
+++ b/drivers/axi/Kconfig
@@ -22,4 +22,11 @@ config IHS_AXI
 	  Interface (IHS AXI) bus on a gdsys IHS FPGA used to communicate with
 	  IP cores in the FPGA (e.g. video transmitter cores).

+config AXI_SANDBOX
+	bool "Enable AXI sandbox driver"
+	depends on DM
+	help
+	  Support AXI (Advanced eXtensible Interface) emulation for the sandbox
+	  environment.
+
 endif
diff --git a/drivers/axi/Makefile b/drivers/axi/Makefile
index 18d9380e9ba..66b6c5a28f4 100644
--- a/drivers/axi/Makefile
+++ b/drivers/axi/Makefile
@@ -7,3 +7,6 @@

 obj-$(CONFIG_AXI) += axi-uclass.o
 obj-$(CONFIG_IHS_AXI) += ihs_axi.o
+obj-$(CONFIG_SANDBOX) += axi-emul-uclass.o
+obj-$(CONFIG_SANDBOX) += sandbox_store.o
+obj-$(CONFIG_AXI_SANDBOX) += axi_sandbox.o
diff --git a/drivers/axi/axi-emul-uclass.c b/drivers/axi/axi-emul-uclass.c
new file mode 100644
index 00000000000..06c42006ee8
--- /dev/null
+++ b/drivers/axi/axi-emul-uclass.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six at gdsys.cc
+ */
+
+#include <common.h>
+#include <axi.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <asm/axi.h>
+
+int axi_sandbox_get_emul(struct udevice *bus, ulong address,
+			 enum axi_size_t size, struct udevice **emulp)
+{
+	struct udevice *dev;
+	u32 reg[2];
+	uint offset;
+
+	switch (size) {
+	case AXI_SIZE_8:
+		offset = 1;
+		break;
+	case AXI_SIZE_16:
+		offset = 2;
+		break;
+	case AXI_SIZE_32:
+		offset = 4;
+		break;
+	default:
+		debug("%s: Unknown AXI transfer size '%d'", bus->name, size);
+		offset = 0;
+	}
+
+	/*
+	 * Note: device_find_* don't activate the devices; they're activated
+	 *	 as-needed below.
+	 */
+	for (device_find_first_child(bus, &dev);
+	     dev;
+	     device_find_next_child(&dev)) {
+		int ret;
+
+		ret = dev_read_u32_array(dev, "reg", reg, ARRAY_SIZE(reg));
+		if (ret) {
+			debug("%s: Could not read 'reg' property of %s\n",
+			      bus->name, dev->name);
+			continue;
+		}
+
+		/*
+		 * Does the transfer's address fall into this device's address
+		 * space?
+		 */
+		if (address >= reg[0] && address <= reg[0] + reg[1] - offset) {
+			/* If yes, activate it... */
+			if (device_probe(dev)) {
+				debug("%s: Could not activate %s\n",
+				      bus->name, dev->name);
+				return -ENODEV;
+			}
+
+			/* ...and return it */
+			*emulp = dev;
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
+int axi_get_store(struct udevice *dev, u8 **storep)
+{
+	struct axi_emul_ops *ops = axi_emul_get_ops(dev);
+
+	if (!ops->get_store)
+		return -ENOSYS;
+
+	return ops->get_store(dev, storep);
+}
+
+UCLASS_DRIVER(axi_emul) = {
+	.id		= UCLASS_AXI_EMUL,
+	.name		= "axi_emul",
+};
diff --git a/drivers/axi/axi_sandbox.c b/drivers/axi/axi_sandbox.c
new file mode 100644
index 00000000000..b91c91f6b3b
--- /dev/null
+++ b/drivers/axi/axi_sandbox.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six at gdsys.cc
+ */
+
+#include <common.h>
+#include <axi.h>
+#include <dm.h>
+#include <asm/axi.h>
+
+/*
+ * This driver implements a AXI bus for the sandbox architecture for testing
+ * purposes.
+ *
+ * The bus forwards every access to it to a special AXI emulation device (which
+ * it gets via the axi_emul_get_ops function) that implements a simple
+ * read/write storage.
+ *
+ * The emulator device must still be contained in the device tree in the usual
+ * way, since configuration data for the storage is read from the DT.
+ */
+
+static int axi_sandbox_read(struct udevice *bus, ulong address, void *data,
+			    enum axi_size_t size)
+{
+	struct axi_emul_ops *ops;
+	struct udevice *emul;
+	int ret;
+
+	/* Get emulator device */
+	ret = axi_sandbox_get_emul(bus, address, size, &emul);
+	if (ret)
+		return ret == -ENODEV ? 0 : ret;
+	/* Forward all reads to the AXI emulator */
+	ops = axi_emul_get_ops(emul);
+	if (!ops || !ops->read)
+		return -ENOSYS;
+
+	return ops->read(emul, address, data, size);
+}
+
+static int axi_sandbox_write(struct udevice *bus, ulong address, void *data,
+			     enum axi_size_t size)
+{
+	struct axi_emul_ops *ops;
+	struct udevice *emul;
+	int ret;
+
+	/* Get emulator device */
+	ret = axi_sandbox_get_emul(bus, address, size, &emul);
+	if (ret)
+		return ret == -ENODEV ? 0 : ret;
+	/* Forward all writes to the AXI emulator */
+	ops = axi_emul_get_ops(emul);
+	if (!ops || !ops->write)
+		return -ENOSYS;
+
+	return ops->write(emul, address, data, size);
+}
+
+static const struct udevice_id axi_sandbox_ids[] = {
+	{ .compatible = "sandbox,axi" },
+	{ /* sentinel */ }
+};
+
+static const struct axi_ops axi_sandbox_ops = {
+	.read = axi_sandbox_read,
+	.write = axi_sandbox_write,
+};
+
+U_BOOT_DRIVER(axi_sandbox_bus) = {
+	.name           = "axi_sandbox_bus",
+	.id             = UCLASS_AXI,
+	.of_match       = axi_sandbox_ids,
+	.ops		= &axi_sandbox_ops,
+};
diff --git a/drivers/axi/sandbox_store.c b/drivers/axi/sandbox_store.c
new file mode 100644
index 00000000000..d724f190798
--- /dev/null
+++ b/drivers/axi/sandbox_store.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six at gdsys.cc
+ */
+
+#include <common.h>
+#include <axi.h>
+#include <dm.h>
+
+/**
+ * struct sandbox_store_priv - Private data structure of a AXI store device
+ * @store: The buffer holding the device's internal memory, which is read from
+ *	   and written to using the driver's methods
+ */
+struct sandbox_store_priv {
+	u8 *store;
+};
+
+/**
+ * copy_axi_data() - Copy data from source to destination with a given AXI
+ *		     transfer width
+ * @src:  Pointer to the data source from where data will be read
+ * @dst:  Pointer to the data destination where data will be written to
+ * @size: Size of the data to be copied given by a axi_size_t enum value
+ *
+ * Return: 0 if OK, -ve on error
+ */
+static int copy_axi_data(void *src, void *dst, enum axi_size_t size)
+{
+	switch (size) {
+	case AXI_SIZE_8:
+		*((u8 *)dst) = *((u8 *)src);
+		return 0;
+	case AXI_SIZE_16:
+		*((u16 *)dst) = be16_to_cpu(*((u16 *)src));
+		return 0;
+	case AXI_SIZE_32:
+		*((u32 *)dst) = be32_to_cpu(*((u32 *)src));
+		return 0;
+	default:
+		debug("%s: Unknown AXI transfer size '%d'\n", __func__, size);
+		return -EINVAL;
+	}
+}
+
+static int sandbox_store_read(struct udevice *dev, ulong address, void *data,
+			      enum axi_size_t size)
+{
+	struct sandbox_store_priv *priv = dev_get_priv(dev);
+
+	return copy_axi_data(priv->store + address, data, size);
+}
+
+static int sandbox_store_write(struct udevice *dev, ulong address, void *data,
+			       enum axi_size_t size)
+{
+	struct sandbox_store_priv *priv = dev_get_priv(dev);
+
+	return copy_axi_data(data, priv->store + address, size);
+}
+
+static int sandbox_store_get_store(struct udevice *dev, u8 **store)
+{
+	struct sandbox_store_priv *priv = dev_get_priv(dev);
+
+	*store = priv->store;
+
+	return 0;
+}
+
+static const struct udevice_id sandbox_store_ids[] = {
+	{ .compatible = "sandbox,sandbox_store" },
+	{ /* sentinel */ }
+};
+
+static const struct axi_emul_ops sandbox_store_ops = {
+	.read = sandbox_store_read,
+	.write = sandbox_store_write,
+	.get_store = sandbox_store_get_store,
+};
+
+static int sandbox_store_probe(struct udevice *dev)
+{
+	struct sandbox_store_priv *priv = dev_get_priv(dev);
+	u32 reg[2];
+	int ret;
+
+	ret = dev_read_u32_array(dev, "reg", reg, ARRAY_SIZE(reg));
+	if (ret) {
+		debug("%s: Could not read 'reg' property\n", dev->name);
+		return -EINVAL;
+	}
+
+	/*
+	 * Allocate the device's internal storage that will be read
+	 * from/written to
+	 */
+	priv->store = calloc(reg[1], 1);
+	if (!priv->store)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int sandbox_store_remove(struct udevice *dev)
+{
+	struct sandbox_store_priv *priv = dev_get_priv(dev);
+
+	free(priv->store);
+
+	return 0;
+}
+
+U_BOOT_DRIVER(sandbox_axi_store) = {
+	.name           = "sandbox_axi_store",
+	.id             = UCLASS_AXI_EMUL,
+	.of_match       = sandbox_store_ids,
+	.ops		= &sandbox_store_ops,
+	.priv_auto_alloc_size = sizeof(struct sandbox_store_priv),
+	.probe          = sandbox_store_probe,
+	.remove		= sandbox_store_remove,
+};
diff --git a/include/axi.h b/include/axi.h
index 2ff81c16af6..3e40692cdfd 100644
--- a/include/axi.h
+++ b/include/axi.h
@@ -1,48 +1,47 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
- * (C) Copyright 2017
- * Mario Six,  Guntermann & Drunck GmbH, mario.six at gdsys.cc
+ * (C) Copyright 2017, 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six at gdsys.cc
  */

 #ifndef _AXI_H_
 #define _AXI_H_

+/**
+ * enum axi_size_t - Determine size of AXI transfer
+ * @AXI_SIZE_8:  AXI sransfer is 8-bit wide
+ * @AXI_SIZE_16: AXI sransfer is 16-bit wide
+ * @AXI_SIZE_32: AXI sransfer is 32-bit wide
+ */
 enum axi_size_t {
 	AXI_SIZE_8,
 	AXI_SIZE_16,
 	AXI_SIZE_32,
 };

-/**
- * struct axi_ops - driver operations for AXI uclass
- *
- * Drivers should support these operations unless otherwise noted. These
- * operations are intended to be used by uclass code, not directly from
- * other code.
- */
 struct axi_ops {
 	/**
 	 * read() - Read a single value from a specified address on a AXI bus
-	 *
 	 * @dev:	AXI bus to read from.
 	 * @address:	The address to read from.
 	 * @data:	Pointer to a variable that takes the data value read
 	 *		from the address on the AXI bus.
 	 * @size:	The size of the data to be read.
-	 * @return 0 if OK, -ve on error.
+	 *
+	 * Return: 0 if OK, -ve on error.
 	 */
 	int (*read)(struct udevice *dev, ulong address, void *data,
 		    enum axi_size_t size);

 	/**
 	 * write() - Write a single value to a specified address on a AXI bus
-	 *
 	 * @dev:	AXI bus to write to.
 	 * @address:	The address to write to.
 	 * @data:	Pointer to the data value to be written to the address
 	 *		on the AXI bus.
 	 * @size:	The size of the data to write.
-	 * @return 0 if OK, -ve on error.
+	 *
+	 * Return 0 if OK, -ve on error.
 	 */
 	int (*write)(struct udevice *dev, ulong address, void *data,
 		     enum axi_size_t size);
@@ -52,27 +51,68 @@ struct axi_ops {

 /**
  * axi_read() - Read a single value from a specified address on a AXI bus
- *
  * @dev:	AXI bus to read from.
  * @address:	The address to read from.
  * @data:	Pointer to a variable that takes the data value read from the
  *              address on the AXI bus.
  * @size:	The size of the data to write.
- * @return 0 if OK, -ve on error.
+ *
+ * Return: 0 if OK, -ve on error.
  */
 int axi_read(struct udevice *dev, ulong address, void *data,
 	     enum axi_size_t size);

 /**
  * axi_write() - Write a single value to a specified address on a AXI bus
- *
  * @dev:	AXI bus to write to.
  * @address:	The address to write to.
  * @data:	Pointer to the data value to be written to the address on the
  *		AXI bus.
  * @size:	The size of the data to write.
- * @return 0 if OK, -ve on error.
+ *
+ * Return: 0 if OK, -ve on error.
  */
 int axi_write(struct udevice *dev, ulong address, void *data,
 	      enum axi_size_t size);
+
+struct axi_emul_ops {
+	/**
+	 * read() - Read a single value from a specified address on a AXI bus
+	 * @dev:	AXI bus to read from.
+	 * @address:	The address to read from.
+	 * @data:	Pointer to a variable that takes the data value read
+	 *		from the address on the AXI bus.
+	 * @size:	The size of the data to be read.
+	 *
+	 * Return: 0 if OK, -ve on error.
+	 */
+	int (*read)(struct udevice *dev, ulong address, void *data,
+		    enum axi_size_t size);
+
+	/**
+	 * write() - Write a single value to a specified address on a AXI bus
+	 * @dev:	AXI bus to write to.
+	 * @address:	The address to write to.
+	 * @data:	Pointer to the data value to be written to the address
+	 *		on the AXI bus.
+	 * @size:	The size of the data to write.
+	 *
+	 * Return: 0 if OK, -ve on error.
+	 */
+	int (*write)(struct udevice *dev, ulong address, void *data,
+		     enum axi_size_t size);
+
+	/**
+	 * get_store() - Get address of internal storage of a emulated AXI
+	 *		 device
+	 * @dev:	Emulated AXI device to get the pointer of the internal
+	 *		storage for.
+	 * @storep:	Pointer to the internal storage of the emulated AXI
+	 *		device.
+	 *
+	 * Return: 0 if OK, -ve on error.
+	 */
+	int (*get_store)(struct udevice *dev, u8 **storep);
+};
+
 #endif
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 618f43ad458..7027ea076db 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -23,6 +23,7 @@ enum uclass_id {
 	UCLASS_I2C_EMUL,	/* sandbox I2C device emulator */
 	UCLASS_PCI_EMUL,	/* sandbox PCI device emulator */
 	UCLASS_USB_EMUL,	/* sandbox USB bus device emulator */
+	UCLASS_AXI_EMUL,	/* sandbox AXI bus device emulator */
 	UCLASS_SIMPLE_BUS,	/* bus with child devices */

 	/* U-Boot uclasses start here - in alphabetical order */
--
2.11.0

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

* [U-Boot] [PATCH v3 4/8] sandbox: Add and build AXI bus and device
  2018-08-09 12:51 [U-Boot] [PATCH v3 1/8] drivers: Add AXI uclass Mario Six
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 2/8] axi: Add ihs_axi driver Mario Six
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 3/8] axi: Add AXI sandbox driver and simple emulator Mario Six
@ 2018-08-09 12:51 ` Mario Six
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 5/8] test: Add AXI test Mario Six
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Mario Six @ 2018-08-09 12:51 UTC (permalink / raw)
  To: u-boot

Add test AXI drivers to the sandbox.

Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Mario Six <mario.six@gdsys.cc>

---

v2 -> v3:
No changes

v1 -> v2:
No changes

---
 arch/sandbox/dts/sandbox.dts | 11 +++++++++++
 arch/sandbox/dts/test.dts    | 11 +++++++++++
 configs/sandbox_defconfig    |  3 +++
 3 files changed, 25 insertions(+)

diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts
index 9f444c96a9e..6ac37f1ed7b 100644
--- a/arch/sandbox/dts/sandbox.dts
+++ b/arch/sandbox/dts/sandbox.dts
@@ -11,6 +11,7 @@
 		i2c0 = &i2c_0;
 		pci0 = &pci;
 		rtc0 = &rtc_0;
+		axi0 = &axi;
 	};

 	chosen {
@@ -311,6 +312,16 @@
 			};
 		};
 	};
+
+	axi: axi at 0 {
+		compatible = "sandbox,axi";
+		#address-cells = <0x1>;
+		#size-cells = <0x1>;
+		store at 0 {
+			compatible = "sandbox,sandbox_store";
+			reg = <0x0 0x400>;
+		};
+	};
 };

 #include "cros-ec-keyboard.dtsi"
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 137679abea9..20543b9e5ee 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -34,6 +34,7 @@
 		usb0 = &usb_0;
 		usb1 = &usb_1;
 		usb2 = &usb_2;
+		axi0 = &axi;
 	};

 	a-test {
@@ -515,6 +516,16 @@
 		compatible = "sandbox,wdt";
 	};

+	axi: axi at 0 {
+		compatible = "sandbox,axi";
+		#address-cells = <0x1>;
+		#size-cells = <0x1>;
+		store at 0 {
+			compatible = "sandbox,sandbox_store";
+			reg = <0x0 0x400>;
+		};
+	};
+
 	chosen {
 		#address-cells = <1>;
 		#size-cells = <1>;
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index afc34298ed9..c8bd15477d3 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -45,6 +45,7 @@ CONFIG_CMD_REMOTEPROC=y
 CONFIG_CMD_SF=y
 CONFIG_CMD_SPI=y
 CONFIG_CMD_USB=y
+CONFIG_CMD_AXI=y
 CONFIG_CMD_TFTPPUT=y
 CONFIG_CMD_TFTPSRV=y
 CONFIG_CMD_RARP=y
@@ -80,6 +81,8 @@ CONFIG_DEVRES=y
 CONFIG_DEBUG_DEVRES=y
 CONFIG_ADC=y
 CONFIG_ADC_SANDBOX=y
+CONFIG_AXI=y
+CONFIG_AXI_SANDBOX=y
 CONFIG_CLK=y
 CONFIG_CPU=y
 CONFIG_DM_DEMO=y
--
2.11.0

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

* [U-Boot] [PATCH v3 5/8] test: Add AXI test
  2018-08-09 12:51 [U-Boot] [PATCH v3 1/8] drivers: Add AXI uclass Mario Six
                   ` (2 preceding siblings ...)
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 4/8] sandbox: Add and build AXI bus and device Mario Six
@ 2018-08-09 12:51 ` Mario Six
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 6/8] cmd: Add axi command Mario Six
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Mario Six @ 2018-08-09 12:51 UTC (permalink / raw)
  To: u-boot

Add tests for the AXI uclass.

Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Mario Six <mario.six@gdsys.cc>

---

v2 -> v3:
* Fixed style violations
* Switched to lower-case hex literals

v1 -> v2:
* Fixed asserts (moved expected values first)

---
 test/dm/Makefile |  1 +
 test/dm/axi.c    | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+)
 create mode 100644 test/dm/axi.c

diff --git a/test/dm/Makefile b/test/dm/Makefile
index d2ed96c6153..564e8460bd2 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -44,4 +44,5 @@ obj-$(CONFIG_DM_VIDEO) += video.o
 obj-$(CONFIG_ADC) += adc.o
 obj-$(CONFIG_SPMI) += spmi.o
 obj-$(CONFIG_WDT) += wdt.o
+obj-$(CONFIG_AXI) += axi.o
 endif
diff --git a/test/dm/axi.c b/test/dm/axi.c
new file mode 100644
index 00000000000..e234ab82e65
--- /dev/null
+++ b/test/dm/axi.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six at gdsys.cc
+ */
+
+#include <common.h>
+#include <axi.h>
+#include <dm.h>
+#include <dm/test.h>
+#include <test/ut.h>
+#include <asm/axi.h>
+
+/* Test that sandbox AXI works correctly */
+static int dm_test_axi_base(struct unit_test_state *uts)
+{
+	struct udevice *bus;
+
+	ut_assertok(uclass_get_device(UCLASS_AXI, 0, &bus));
+
+	return 0;
+}
+
+DM_TEST(dm_test_axi_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test that sandbox PCI bus numbering works correctly */
+static int dm_test_axi_busnum(struct unit_test_state *uts)
+{
+	struct udevice *bus;
+
+	ut_assertok(uclass_get_device_by_seq(UCLASS_AXI, 0, &bus));
+
+	return 0;
+}
+
+DM_TEST(dm_test_axi_busnum, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test that we can use the store device correctly */
+static int dm_test_axi_store(struct unit_test_state *uts)
+{
+	struct udevice *store;
+	u8 tdata1[] = {0x55, 0x66, 0x77, 0x88};
+	u8 tdata2[] = {0xaa, 0xbb, 0xcc, 0xdd};
+	u32 val;
+	u8 *data;
+
+	/* Check that asking for the device automatically fires up AXI */
+	ut_assertok(uclass_get_device(UCLASS_AXI_EMUL, 0, &store));
+	ut_assert(device_active(store));
+
+	axi_get_store(store, &data);
+
+	/* Test reading */
+	memcpy(data, tdata1, ARRAY_SIZE(tdata1));
+	axi_read(store, 0, &val, AXI_SIZE_32);
+	ut_asserteq(0x55667788, val);
+
+	memcpy(data + 3, tdata2, ARRAY_SIZE(tdata2));
+	axi_read(store, 3, &val, AXI_SIZE_32);
+	ut_asserteq(0xaabbccdd, val);
+
+	/* Reset data store */
+	memset(data, 0, 16);
+
+	/* Test writing */
+	val = 0x55667788;
+	axi_write(store, 0, &val, AXI_SIZE_32);
+	ut_asserteq(0, memcmp(data, tdata1, ARRAY_SIZE(tdata1)));
+
+	val = 0xaabbccdd;
+	axi_write(store, 3, &val, AXI_SIZE_32);
+	ut_asserteq(0, memcmp(data + 3, tdata2, ARRAY_SIZE(tdata1)));
+
+	return 0;
+}
+
+DM_TEST(dm_test_axi_store, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
--
2.11.0

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

* [U-Boot] [PATCH v3 6/8] cmd: Add axi command
  2018-08-09 12:51 [U-Boot] [PATCH v3 1/8] drivers: Add AXI uclass Mario Six
                   ` (3 preceding siblings ...)
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 5/8] test: Add AXI test Mario Six
@ 2018-08-09 12:51 ` Mario Six
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 7/8] video: Sort Makefile entries Mario Six
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: Mario Six @ 2018-08-09 12:51 UTC (permalink / raw)
  To: u-boot

Add a command to debug the AXI bus.

Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Mario Six <mario.six@gdsys.cc>

---

v2 -> v3:
* Fixed style violations
* Fixed bus activation (activate in all cases)
* Added full documentation
* Re-worked function names
* Fixed printing issues with transfer widths other than 32 bit
* Streamlined naming of return value variables
* Expanded help for commands a bit

v1 -> v2:
No changes

---
 cmd/Kconfig  |   8 ++
 cmd/Makefile |   1 +
 cmd/axi.c    | 352 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 361 insertions(+)
 create mode 100644 cmd/axi.c

diff --git a/cmd/Kconfig b/cmd/Kconfig
index ef43ed8dda4..bd90946667f 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1027,6 +1027,14 @@ config CMD_USB_MASS_STORAGE
 	help
 	  USB mass storage support

+config CMD_AXI
+	bool "axi"
+	depends on AXI
+	help
+	  Enable the command "axi" for accessing AXI (Advanced eXtensible
+	  Interface) busses, a on-chip interconnect specification for managing
+	  functional blocks in SoC designs, which is also often used in designs
+	  involving FPGAs (e.g.  communication with IP cores in Xilinx FPGAs).
 endmenu


diff --git a/cmd/Makefile b/cmd/Makefile
index 323f1fd2c77..12d2118f1d3 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -149,6 +149,7 @@ obj-$(CONFIG_CMD_ZFS) += zfs.o
 obj-$(CONFIG_CMD_DFU) += dfu.o
 obj-$(CONFIG_CMD_GPT) += gpt.o
 obj-$(CONFIG_CMD_ETHSW) += ethsw.o
+obj-$(CONFIG_CMD_AXI) += axi.o

 # Power
 obj-$(CONFIG_CMD_PMIC) += pmic.o
diff --git a/cmd/axi.c b/cmd/axi.c
new file mode 100644
index 00000000000..588098fddd4
--- /dev/null
+++ b/cmd/axi.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2016
+ * Dirk Eibach,  Guntermann & Drunck GmbH, dirk.eibach at gdsys.cc
+ *
+ * (C) Copyright 2017, 2018
+ * Mario Six,  Guntermann & Drunck GmbH, mario.six at gdsys.cc
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <axi.h>
+#include <command.h>
+#include <console.h>
+#include <dm.h>
+
+/* Currently selected AXI bus device */
+static struct udevice *axi_cur_bus;
+/* Transmission size from last command */
+static uint dp_last_size;
+/* Address from last command */
+static uint dp_last_addr;
+/* Number of bytes to display from last command; default = 64 */
+static uint dp_last_length = 0x40;
+
+/**
+ * show_bus() - Show devices on a single AXI bus
+ * @bus: The AXI bus device to printt information for
+ */
+static void show_bus(struct udevice *bus)
+{
+	struct udevice *dev;
+
+	printf("Bus %d:\t%s", bus->req_seq, bus->name);
+	if (device_active(bus))
+		printf("  (active %d)", bus->seq);
+	printf("\n");
+	for (device_find_first_child(bus, &dev);
+	     dev;
+	     device_find_next_child(&dev))
+		printf("  %s\n", dev->name);
+}
+
+/**
+ * axi_set_cur_bus() - Set the currently active AXI bus
+ * @busnum: The number of the bus (i.e. its sequence number) that should be
+ *	    made active
+ *
+ * The operations supplied by this command operate only on the currently active
+ * bus.
+ *
+ * Return: 0 if OK, -ve on error
+ */
+static int axi_set_cur_bus(unsigned int busnum)
+{
+	struct udevice *bus;
+	struct udevice *dummy;
+	int ret;
+
+	/* Make sure that all sequence numbers are initialized */
+	for (uclass_first_device(UCLASS_AXI, &dummy);
+	     dummy;
+	     uclass_next_device(&dummy))
+		;
+
+	ret = uclass_get_device_by_seq(UCLASS_AXI, busnum, &bus);
+	if (ret) {
+		debug("%s: No bus %d\n", __func__, busnum);
+		return ret;
+	}
+	axi_cur_bus = bus;
+
+	return 0;
+}
+
+/**
+ * axi_get_cur_bus() - Retrieve the currently active AXI bus device
+ * @busp: Pointer to a struct udevice that receives the currently active bus
+ *	  device
+ *
+ * Return: 0 if OK, -ve on error
+ */
+static int axi_get_cur_bus(struct udevice **busp)
+{
+	if (!axi_cur_bus) {
+		puts("No AXI bus selected\n");
+		return -ENODEV;
+	}
+	*busp = axi_cur_bus;
+
+	return 0;
+}
+
+/*
+ * Command handlers
+ */
+
+static int do_axi_show_bus(cmd_tbl_t *cmdtp, int flag, int argc,
+			   char * const argv[])
+{
+	struct udevice *dummy;
+
+	/* Make sure that all sequence numbers are initialized */
+	for (uclass_first_device(UCLASS_AXI, &dummy);
+	     dummy;
+	     uclass_next_device(&dummy))
+		;
+
+	if (argc == 1) {
+		/* show all busses */
+		struct udevice *bus;
+
+		for (uclass_first_device(UCLASS_AXI, &bus);
+		     bus;
+		     uclass_next_device(&bus))
+			show_bus(bus);
+	} else {
+		int i;
+
+		/* show specific bus */
+		i = simple_strtoul(argv[1], NULL, 10);
+
+		struct udevice *bus;
+		int ret;
+
+		ret = uclass_get_device_by_seq(UCLASS_AXI, i, &bus);
+		if (ret) {
+			printf("Invalid bus %d: err=%d\n", i, ret);
+			return CMD_RET_FAILURE;
+		}
+		show_bus(bus);
+	}
+
+	return 0;
+}
+
+static int do_axi_bus_num(cmd_tbl_t *cmdtp, int flag, int argc,
+			  char * const argv[])
+{
+	int ret = 0;
+	int bus_no;
+
+	if (argc == 1) {
+		/* querying current setting */
+		struct udevice *bus;
+
+		if (!axi_get_cur_bus(&bus))
+			bus_no = bus->seq;
+		else
+			bus_no = -1;
+
+		printf("Current bus is %d\n", bus_no);
+	} else {
+		bus_no = simple_strtoul(argv[1], NULL, 10);
+		printf("Setting bus to %d\n", bus_no);
+
+		ret = axi_set_cur_bus(bus_no);
+		if (ret)
+			printf("Failure changing bus number (%d)\n", ret);
+	}
+
+	return ret ? CMD_RET_FAILURE : 0;
+}
+
+static int do_axi_md(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	/* Print that many bytes per line */
+	const uint DISP_LINE_LEN = 16;
+	u8 linebuf[DISP_LINE_LEN];
+	unsigned int k;
+	ulong addr, length, size;
+	ulong nbytes;
+	enum axi_size_t axisize;
+	int unitsize;
+
+	/*
+	 * We use the last specified parameters, unless new ones are
+	 * entered.
+	 */
+	size = dp_last_size;
+	addr = dp_last_addr;
+	length = dp_last_length;
+
+	if (argc < 3)
+		return CMD_RET_USAGE;
+
+	if (!axi_cur_bus) {
+		puts("No AXI bus selected\n");
+		return CMD_RET_FAILURE;
+	}
+
+	if ((flag & CMD_FLAG_REPEAT) == 0) {
+		size = simple_strtoul(argv[1], NULL, 10);
+
+		/*
+		 * Address is specified since argc >= 3
+		 */
+		addr = simple_strtoul(argv[2], NULL, 16);
+
+		/*
+		 * If there's another parameter, it is the length to display;
+		 * length is the number of objects, not number of bytes
+		 */
+		if (argc > 3)
+			length = simple_strtoul(argv[3], NULL, 16);
+	}
+
+	switch (size) {
+	case 8:
+		axisize = AXI_SIZE_8;
+		unitsize = 1;
+		break;
+	case 16:
+		axisize = AXI_SIZE_16;
+		unitsize = 2;
+		break;
+	case 32:
+		axisize = AXI_SIZE_32;
+		unitsize = 4;
+		break;
+	default:
+		printf("Unknown read size '%lu'\n", size);
+		return CMD_RET_USAGE;
+	};
+
+	nbytes = length * unitsize;
+	do {
+		ulong linebytes = (nbytes > DISP_LINE_LEN) ?
+				  DISP_LINE_LEN : nbytes;
+
+		for (k = 0; k < linebytes / unitsize; ++k) {
+			int ret = axi_read(axi_cur_bus, addr + k * unitsize,
+					   linebuf + k * unitsize, axisize);
+
+			if (!ret) /* Continue if axi_read was successful */
+				continue;
+
+			if (ret == -ENOSYS)
+				printf("axi_read failed; read size not supported?\n");
+			else
+				printf("axi_read failed: err = %d\n", ret);
+
+			return CMD_RET_FAILURE;
+		}
+		print_buffer(addr, (void *)linebuf, unitsize,
+			     linebytes / unitsize,
+			     DISP_LINE_LEN / unitsize);
+
+		nbytes -= max(linebytes, 1UL);
+		addr += linebytes;
+
+		if (ctrlc())
+			break;
+	} while (nbytes > 0);
+
+	dp_last_size = size;
+	dp_last_addr = addr;
+	dp_last_length = length;
+
+	return 0;
+}
+
+static int do_axi_mw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	u32 writeval;
+	ulong addr, count, size;
+	enum axi_size_t axisize;
+
+	if (argc <= 3 || argc >= 6)
+		return CMD_RET_USAGE;
+
+	size = simple_strtoul(argv[1], NULL, 10);
+
+	switch (size) {
+	case 8:
+		axisize = AXI_SIZE_8;
+		break;
+	case 16:
+		axisize = AXI_SIZE_16;
+		break;
+	case 32:
+		axisize = AXI_SIZE_32;
+		break;
+	default:
+		printf("Unknown write size '%lu'\n", size);
+		return CMD_RET_USAGE;
+	};
+
+	/* Address is specified since argc > 4 */
+	addr = simple_strtoul(argv[2], NULL, 16);
+
+	/* Get the value to write */
+	writeval = simple_strtoul(argv[3], NULL, 16);
+
+	/* Count ? */
+	if (argc == 5)
+		count = simple_strtoul(argv[4], NULL, 16);
+	else
+		count = 1;
+
+	while (count-- > 0) {
+		int ret = axi_write(axi_cur_bus, addr + count * sizeof(u32),
+				    &writeval, axisize);
+
+		if (ret) {
+			printf("axi_write failed: err = %d\n", ret);
+			return CMD_RET_FAILURE;
+		}
+	}
+
+	return 0;
+}
+
+static cmd_tbl_t cmd_axi_sub[] = {
+	U_BOOT_CMD_MKENT(bus, 1, 1, do_axi_show_bus, "", ""),
+	U_BOOT_CMD_MKENT(dev, 1, 1, do_axi_bus_num, "", ""),
+	U_BOOT_CMD_MKENT(md, 4, 1, do_axi_md, "", ""),
+	U_BOOT_CMD_MKENT(mw, 5, 1, do_axi_mw, "", ""),
+};
+
+static int do_ihs_axi(cmd_tbl_t *cmdtp, int flag, int argc,
+		      char * const argv[])
+{
+	cmd_tbl_t *c;
+
+	if (argc < 2)
+		return CMD_RET_USAGE;
+
+	/* Strip off leading 'axi' command argument */
+	argc--;
+	argv++;
+
+	/* Hand off rest of command line to sub-commands */
+	c = find_cmd_tbl(argv[0], &cmd_axi_sub[0], ARRAY_SIZE(cmd_axi_sub));
+
+	if (c)
+		return c->cmd(cmdtp, flag, argc, argv);
+	else
+		return CMD_RET_USAGE;
+}
+
+static char axi_help_text[] =
+	"bus  - show AXI bus info\n"
+	"axi dev [bus] - show or set current AXI bus to bus number [bus]\n"
+	"axi md size addr [# of objects] - read from AXI device at address [addr] and data width [size] (one of 8, 16, 32)\n"
+	"axi mw size addr value [count] - write data [value] to AXI device at address [addr] and data width [size] (one of 8, 16, 32)\n";
+
+U_BOOT_CMD(axi, 7, 1, do_ihs_axi,
+	   "AXI sub-system",
+	   axi_help_text
+);
--
2.11.0

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

* [U-Boot] [PATCH v3 7/8] video: Sort Makefile entries
  2018-08-09 12:51 [U-Boot] [PATCH v3 1/8] drivers: Add AXI uclass Mario Six
                   ` (4 preceding siblings ...)
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 6/8] cmd: Add axi command Mario Six
@ 2018-08-09 12:51 ` Mario Six
  2018-08-17 12:49   ` Simon Glass
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 8/8] video_display: Add Xilinx LogiCore DP TX Mario Six
  2018-08-11 14:13 ` [U-Boot] [PATCH v3 1/8] drivers: Add AXI uclass Anatolij Gustschin
  7 siblings, 1 reply; 11+ messages in thread
From: Mario Six @ 2018-08-09 12:51 UTC (permalink / raw)
  To: u-boot

The entries of Makefiles should be sorted, which is not the case in the
video driver Makefile.

Sort the entries alphabetically as far as this makes sense.

Signed-off-by: Mario Six <mario.six@gdsys.cc>

---

v2 -> v3:
New in v3

---
 drivers/video/Makefile | 42 +++++++++++++++++++++---------------------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 7c89c67dcea..28421c74f6a 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -4,57 +4,57 @@
 # Wolfgang Denk, DENX Software Engineering, wd at denx.de.

 ifdef CONFIG_DM
+obj-$(CONFIG_BACKLIGHT_GPIO) += backlight_gpio.o
+obj-$(CONFIG_BACKLIGHT_PWM) += pwm_backlight.o
+obj-$(CONFIG_CONSOLE_NORMAL) += console_normal.o
+obj-$(CONFIG_CONSOLE_ROTATION) += console_rotate.o
+obj-$(CONFIG_CONSOLE_TRUETYPE) += console_truetype.o fonts/
 obj-$(CONFIG_DISPLAY) += display-uclass.o
 obj-$(CONFIG_DM_VIDEO) += backlight-uclass.o
 obj-$(CONFIG_DM_VIDEO) += panel-uclass.o simple_panel.o
 obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o
 obj-$(CONFIG_DM_VIDEO) += video_bmp.o
-obj-$(CONFIG_BACKLIGHT_PWM) += pwm_backlight.o
-obj-$(CONFIG_BACKLIGHT_GPIO) += backlight_gpio.o
-obj-$(CONFIG_CONSOLE_NORMAL) += console_normal.o
-obj-$(CONFIG_CONSOLE_ROTATION) += console_rotate.o
-obj-$(CONFIG_CONSOLE_TRUETYPE) += console_truetype.o fonts/
 endif

-obj-$(CONFIG_VIDEO_BROADWELL_IGD) += broadwell_igd.o
-obj-$(CONFIG_VIDEO_IVYBRIDGE_IGD) += ivybridge_igd.o
+obj-${CONFIG_EXYNOS_FB} += exynos/
+obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/
+obj-${CONFIG_VIDEO_STM32} += stm32/
+obj-${CONFIG_VIDEO_TEGRA124} += tegra124/

+obj-$(CONFIG_AM335X_LCD) += am335x-fb.o
 obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
 obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o
 obj-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o
 obj-$(CONFIG_CFB_CONSOLE) += cfb_console.o
+obj-$(CONFIG_FORMIKE) += formike.o
 obj-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o
-obj-$(CONFIG_VIDEO_FSL_DCU_FB) += fsl_dcu_fb.o videomodes.o
+obj-$(CONFIG_LD9040) += ld9040.o
+obj-$(CONFIG_LG4573) += lg4573.o
 obj-$(CONFIG_PXA_LCD) += pxa_lcd.o
-obj-$(CONFIG_SCF0403_LCD) += scf0403_lcd.o
 obj-$(CONFIG_S6E8AX0) += s6e8ax0.o
-obj-$(CONFIG_LD9040) += ld9040.o
+obj-$(CONFIG_SCF0403_LCD) += scf0403_lcd.o
 obj-$(CONFIG_VIDEO_BCM2835) += bcm2835.o
+obj-$(CONFIG_VIDEO_BROADWELL_IGD) += broadwell_igd.o
 obj-$(CONFIG_VIDEO_COREBOOT) += coreboot.o
 obj-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o
+obj-$(CONFIG_VIDEO_DW_HDMI) += dw_hdmi.o
 obj-$(CONFIG_VIDEO_EFI) += efi.o
+obj-$(CONFIG_VIDEO_FSL_DCU_FB) += fsl_dcu_fb.o videomodes.o
+obj-$(CONFIG_VIDEO_IPUV3) += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o
+obj-$(CONFIG_VIDEO_IVYBRIDGE_IGD) += ivybridge_igd.o
 obj-$(CONFIG_VIDEO_LCD_ANX9804) += anx9804.o
 obj-$(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM) += hitachi_tx18d42vm_lcd.o
 obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o
 obj-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o
-obj-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o
-obj-$(CONFIG_VIDEO_IPUV3) += mxc_ipuv3_fb.o ipu_common.o ipu_disp.o
 obj-$(CONFIG_VIDEO_MVEBU) += mvebu_lcd.o
+obj-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o
 obj-$(CONFIG_VIDEO_MXS) += mxsfb.o videomodes.o
 obj-$(CONFIG_VIDEO_OMAP3) += omap3_dss.o
 obj-$(CONFIG_VIDEO_SANDBOX_SDL) += sandbox_sdl.o
+obj-$(CONFIG_VIDEO_SIMPLE) += simplefb.o
 obj-$(CONFIG_VIDEO_TEGRA20) += tegra.o
 obj-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o
 obj-$(CONFIG_VIDEO_VESA) += vesa.o
-obj-$(CONFIG_FORMIKE) += formike.o
-obj-$(CONFIG_LG4573) += lg4573.o
-obj-$(CONFIG_AM335X_LCD) += am335x-fb.o
-obj-$(CONFIG_VIDEO_DW_HDMI) += dw_hdmi.o
-obj-$(CONFIG_VIDEO_SIMPLE) += simplefb.o
-obj-${CONFIG_VIDEO_TEGRA124} += tegra124/
-obj-${CONFIG_EXYNOS_FB} += exynos/
-obj-${CONFIG_VIDEO_ROCKCHIP} += rockchip/
-obj-${CONFIG_VIDEO_STM32} += stm32/

 obj-y += bridge/
 obj-y += sunxi/
--
2.11.0

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

* [U-Boot] [PATCH v3 8/8] video_display: Add Xilinx LogiCore DP TX
  2018-08-09 12:51 [U-Boot] [PATCH v3 1/8] drivers: Add AXI uclass Mario Six
                   ` (5 preceding siblings ...)
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 7/8] video: Sort Makefile entries Mario Six
@ 2018-08-09 12:51 ` Mario Six
  2018-08-17 12:49   ` Simon Glass
  2018-08-11 14:13 ` [U-Boot] [PATCH v3 1/8] drivers: Add AXI uclass Anatolij Gustschin
  7 siblings, 1 reply; 11+ messages in thread
From: Mario Six @ 2018-08-09 12:51 UTC (permalink / raw)
  To: u-boot

Add a driver for the Xilinx LogiCORE DisplayPort IP core, which is a
pure DP transmitter core for Xiling FPGA (no display capabilities).

Signed-off-by: Mario Six <mario.six@gdsys.cc>

---

v2 -> v3:
* Fix style errors
* Added full documentation

v1 -> v2:
* Switch to display_enable
* Mentioned that the LogiCORE has no display capabilities

---
 drivers/video/Kconfig                |   11 +
 drivers/video/Makefile               |    1 +
 drivers/video/logicore_dp_dpcd.h     |  341 +++++
 drivers/video/logicore_dp_tx.c       | 2296 ++++++++++++++++++++++++++++++++++
 drivers/video/logicore_dp_tx.h       |   54 +
 drivers/video/logicore_dp_tx_regif.h |  396 ++++++
 6 files changed, 3099 insertions(+)
 create mode 100644 drivers/video/logicore_dp_dpcd.h
 create mode 100644 drivers/video/logicore_dp_tx.c
 create mode 100644 drivers/video/logicore_dp_tx.h
 create mode 100644 drivers/video/logicore_dp_tx_regif.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index a2d7e109384..ecb57d80d58 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -431,6 +431,17 @@ config ATMEL_HLCD
 	help
 	   HLCDC supports video output to an attached LCD panel.

+config LOGICORE_DP_TX
+	bool "Enable Logicore DP TX driver"
+	depends on DISPLAY
+	help
+	  Enable the driver for the transmitter part of the Xilinx LogiCORE
+	  DisplayPort, a IP core for Xilinx FPGAs that implements a DisplayPort
+	  video interface as defined by VESA DisplayPort v1.2.
+
+	  Note that this is a pure transmitter device, and has no display
+	  capabilities by itself.
+
 config VIDEO_BROADWELL_IGD
 	bool "Enable Intel Broadwell integrated graphics device"
 	depends on X86
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 28421c74f6a..0f41a23193a 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_FORMIKE) += formike.o
 obj-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o
 obj-$(CONFIG_LD9040) += ld9040.o
 obj-$(CONFIG_LG4573) += lg4573.o
+obj-$(CONFIG_LOGICORE_DP_TX) += logicore_dp_tx.o
 obj-$(CONFIG_PXA_LCD) += pxa_lcd.o
 obj-$(CONFIG_S6E8AX0) += s6e8ax0.o
 obj-$(CONFIG_SCF0403_LCD) += scf0403_lcd.o
diff --git a/drivers/video/logicore_dp_dpcd.h b/drivers/video/logicore_dp_dpcd.h
new file mode 100644
index 00000000000..858bbd609ae
--- /dev/null
+++ b/drivers/video/logicore_dp_dpcd.h
@@ -0,0 +1,341 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * logicore_dp_dpcd.h
+ *
+ * DPCD interface definition for XILINX LogiCore DisplayPort v6.1
+ * based on Xilinx dp_v3_1 driver sources
+ *
+ * (C) Copyright 2016
+ * Dirk Eibach,  Guntermann & Drunck GmbH, dirk.eibach at gdsys.cc
+ */
+
+#ifndef __GDSYS_LOGICORE_DP_DPCD_H__
+#define __GDSYS_LOGICORE_DP_DPCD_H__
+
+/* receiver capability field */
+#define DPCD_REV						0x00000
+#define DPCD_MAX_LINK_RATE					0x00001
+#define DPCD_MAX_LANE_COUNT					0x00002
+#define DPCD_MAX_DOWNSPREAD					0x00003
+#define DPCD_NORP_PWR_V_CAP					0x00004
+#define DPCD_DOWNSP_PRESENT					0x00005
+#define DPCD_ML_CH_CODING_CAP					0x00006
+#define DPCD_DOWNSP_COUNT_MSA_OUI				0x00007
+#define	DPCD_RX_PORT0_CAP_0					0x00008
+#define	DPCD_RX_PORT0_CAP_1					0x00009
+#define	DPCD_RX_PORT1_CAP_0					0x0000A
+#define	DPCD_RX_PORT1_CAP_1					0x0000B
+#define DPCD_I2C_SPEED_CTL_CAP					0x0000C
+#define DPCD_EDP_CFG_CAP					0x0000D
+#define DPCD_TRAIN_AUX_RD_INTERVAL				0x0000E
+#define DPCD_ADAPTER_CAP					0x0000F
+#define DPCD_FAUX_CAP						0x00020
+#define DPCD_MSTM_CAP						0x00021
+#define DPCD_NUM_AUDIO_EPS					0x00022
+#define	DPCD_AV_GRANULARITY					0x00023
+#define DPCD_AUD_DEC_LAT_7_0					0x00024
+#define DPCD_AUD_DEC_LAT_15_8					0x00025
+#define DPCD_AUD_PP_LAT_7_0					0x00026
+#define DPCD_AUD_PP_LAT_15_8					0x00027
+#define DPCD_VID_INTER_LAT					0x00028
+#define DPCD_VID_PROG_LAT					0x00029
+#define DPCD_REP_LAT						0x0002A
+#define DPCD_AUD_DEL_INS_7_0					0x0002B
+#define DPCD_AUD_DEL_INS_15_8					0x0002C
+#define DPCD_AUD_DEL_INS_23_16					0x0002D
+#define DPCD_GUID						0x00030
+#define DPCD_RX_GTC_VALUE_7_0					0x00054
+#define DPCD_RX_GTC_VALUE_15_8					0x00055
+#define DPCD_RX_GTC_VALUE_23_16					0x00056
+#define DPCD_RX_GTC_VALUE_31_24					0x00057
+#define DPCD_RX_GTC_MSTR_REQ					0x00058
+#define DPCD_RX_GTC_FREQ_LOCK_DONE				0x00059
+#define DPCD_DOWNSP_0_CAP					0x00080
+#define DPCD_DOWNSP_1_CAP					0x00081
+#define DPCD_DOWNSP_2_CAP					0x00082
+#define DPCD_DOWNSP_3_CAP					0x00083
+#define DPCD_DOWNSP_0_DET_CAP					0x00080
+#define DPCD_DOWNSP_1_DET_CAP					0x00084
+#define DPCD_DOWNSP_2_DET_CAP					0x00088
+#define DPCD_DOWNSP_3_DET_CAP					0x0008C
+
+/* link configuration field */
+#define DPCD_LINK_BW_SET					0x00100
+#define DPCD_LANE_COUNT_SET					0x00101
+#define DPCD_TP_SET						0x00102
+#define DPCD_TRAINING_LANE0_SET					0x00103
+#define DPCD_TRAINING_LANE1_SET					0x00104
+#define DPCD_TRAINING_LANE2_SET					0x00105
+#define DPCD_TRAINING_LANE3_SET					0x00106
+#define DPCD_DOWNSPREAD_CTRL					0x00107
+#define DPCD_ML_CH_CODING_SET					0x00108
+#define DPCD_I2C_SPEED_CTL_SET					0x00109
+#define DPCD_EDP_CFG_SET					0x0010A
+#define DPCD_LINK_QUAL_LANE0_SET				0x0010B
+#define DPCD_LINK_QUAL_LANE1_SET				0x0010C
+#define DPCD_LINK_QUAL_LANE2_SET				0x0010D
+#define DPCD_LINK_QUAL_LANE3_SET				0x0010E
+#define DPCD_TRAINING_LANE0_1_SET2				0x0010F
+#define DPCD_TRAINING_LANE2_3_SET2				0x00110
+#define DPCD_MSTM_CTRL						0x00111
+#define DPCD_AUDIO_DELAY_7_0					0x00112
+#define DPCD_AUDIO_DELAY_15_8					0x00113
+#define DPCD_AUDIO_DELAY_23_6					0x00114
+#define DPCD_UPSTREAM_DEVICE_DP_PWR_NEED			0x00118
+#define DPCD_FAUX_MODE_CTRL					0x00120
+#define DPCD_FAUX_FORWARD_CH_DRIVE_SET				0x00121
+#define DPCD_BACK_CH_STATUS					0x00122
+#define DPCD_FAUX_BACK_CH_SYMBOL_ERROR_COUNT			0x00123
+#define DPCD_FAUX_BACK_CH_TRAINING_PATTERN_TIME			0x00125
+#define DPCD_TX_GTC_VALUE_7_0					0x00154
+#define DPCD_TX_GTC_VALUE_15_8					0x00155
+#define DPCD_TX_GTC_VALUE_23_16					0x00156
+#define DPCD_TX_GTC_VALUE_31_24					0x00157
+#define DPCD_RX_GTC_VALUE_PHASE_SKEW_EN				0x00158
+#define DPCD_TX_GTC_FREQ_LOCK_DONE				0x00159
+#define DPCD_ADAPTER_CTRL					0x001A0
+#define DPCD_BRANCH_DEVICE_CTRL					0x001A1
+#define DPCD_PAYLOAD_ALLOCATE_SET				0x001C0
+#define DPCD_PAYLOAD_ALLOCATE_START_TIME_SLOT			0x001C1
+#define DPCD_PAYLOAD_ALLOCATE_TIME_SLOT_COUNT			0x001C2
+
+/* link/sink status field */
+#define DPCD_SINK_COUNT						0x00200
+#define DPCD_DEVICE_SERVICE_IRQ					0x00201
+#define DPCD_STATUS_LANE_0_1					0x00202
+#define DPCD_STATUS_LANE_2_3					0x00203
+#define DPCD_LANE_ALIGN_STATUS_UPDATED				0x00204
+#define DPCD_SINK_STATUS					0x00205
+#define DPCD_ADJ_REQ_LANE_0_1					0x00206
+#define DPCD_ADJ_REQ_LANE_2_3					0x00207
+#define DPCD_TRAINING_SCORE_LANE_0				0x00208
+#define DPCD_TRAINING_SCORE_LANE_1				0x00209
+#define DPCD_TRAINING_SCORE_LANE_2				0x0020A
+#define DPCD_TRAINING_SCORE_LANE_3				0x0020B
+#define DPCD_ADJ_REQ_PC2					0x0020C
+#define DPCD_FAUX_FORWARD_CH_SYMBOL_ERROR_COUNT			0x0020D
+#define DPCD_SYMBOL_ERROR_COUNT_LANE_0				0x00210
+#define DPCD_SYMBOL_ERROR_COUNT_LANE_1				0x00212
+#define DPCD_SYMBOL_ERROR_COUNT_LANE_2				0x00214
+#define DPCD_SYMBOL_ERROR_COUNT_LANE_3				0x00216
+
+/* automated testing sub-field */
+#define DPCD_FAUX_FORWARD_CH_STATUS				0x00280
+#define DPCD_FAUX_BACK_CH_DRIVE_SET				0x00281
+#define DPCD_FAUX_BACK_CH_SYM_ERR_COUNT_CTRL			0x00282
+#define DPCD_PAYLOAD_TABLE_UPDATE_STATUS			0x002C0
+#define DPCD_VC_PAYLOAD_ID_SLOT(slotnum) \
+			(DPCD_PAYLOAD_TABLE_UPDATE_STATUS + slotnum)
+
+/* sink control field */
+#define DPCD_SET_POWER_DP_PWR_VOLTAGE				0x00600
+
+/* sideband message buffers */
+#define DPCD_DOWN_REQ						0x01000
+#define DPCD_UP_REP						0x01200
+#define DPCD_DOWN_REP						0x01400
+#define DPCD_UP_REQ						0x01600
+
+/* event status indicator field */
+#define DPCD_SINK_COUNT_ESI					0x02002
+#define DPCD_SINK_DEVICE_SERVICE_IRQ_VECTOR_ESI0		0x02003
+#define DPCD_SINK_DEVICE_SERVICE_IRQ_VECTOR_ESI1		0x02004
+#define DPCD_SINK_LINK_SERVICE_IRQ_VECTOR_ESI0			0x02005
+#define DPCD_SINK_LANE0_1_STATUS				0x0200C
+#define DPCD_SINK_LANE2_3_STATUS				0x0200D
+#define DPCD_SINK_ALIGN_STATUS_UPDATED_ESI			0x0200E
+#define DPCD_SINK_STATUS_ESI					0x0200F
+
+/*
+ * field addresses and sizes.
+ */
+#define DPCD_RECEIVER_CAP_FIELD_START				DPCD_REV
+#define DPCD_RECEIVER_CAP_FIELD_SIZE				0x100
+#define DPCD_LINK_CFG_FIELD_START				DPCD_LINK_BW_SET
+#define DPCD_LINK_CFG_FIELD_SIZE				0x100
+#define DPCD_LINK_SINK_STATUS_FIELD_START			DPCD_SINK_COUNT
+#define DPCD_LINK_SINK_STATUS_FIELD_SIZE			0x17
+/* 0x00000: DPCD_REV */
+#define DPCD_REV_MNR_MASK					0x0F
+#define DPCD_REV_MJR_MASK					0xF0
+#define DPCD_REV_MJR_SHIFT					4
+/* 0x00001: MAX_LINK_RATE */
+#define DPCD_MAX_LINK_RATE_162GBPS				0x06
+#define DPCD_MAX_LINK_RATE_270GBPS				0x0A
+#define DPCD_MAX_LINK_RATE_540GBPS				0x14
+/* 0x00002: MAX_LANE_COUNT */
+#define DPCD_MAX_LANE_COUNT_MASK				0x1F
+#define DPCD_MAX_LANE_COUNT_1					0x01
+#define DPCD_MAX_LANE_COUNT_2					0x02
+#define DPCD_MAX_LANE_COUNT_4					0x04
+#define DPCD_TPS3_SUPPORT_MASK					0x40
+#define DPCD_ENHANCED_FRAME_SUPPORT_MASK			0x80
+/* 0x00003: MAX_DOWNSPREAD */
+#define DPCD_MAX_DOWNSPREAD_MASK				0x01
+#define DPCD_NO_AUX_HANDSHAKE_LINK_TRAIN_MASK			0x40
+/* 0x00005: DOWNSP_PRESENT */
+#define DPCD_DOWNSP_PRESENT_MASK				0x01
+#define DPCD_DOWNSP_TYPE_MASK					0x06
+#define DPCD_DOWNSP_TYPE_SHIFT					1
+#define DPCD_DOWNSP_TYPE_DP					0x0
+#define DPCD_DOWNSP_TYPE_AVGA_ADVII				0x1
+#define DPCD_DOWNSP_TYPE_DVI_HDMI_DPPP				0x2
+#define DPCD_DOWNSP_TYPE_OTHERS					0x3
+#define DPCD_DOWNSP_FORMAT_CONV_MASK				0x08
+#define DPCD_DOWNSP_DCAP_INFO_AVAIL_MASK			0x10
+/* 0x00006, 0x00108: ML_CH_CODING_SUPPORT, ML_CH_CODING_SET */
+#define DPCD_ML_CH_CODING_MASK					0x01
+/* 0x00007: DOWNSP_COUNT_MSA_OUI */
+#define DPCD_DOWNSP_COUNT_MASK					0x0F
+#define DPCD_MSA_TIMING_PAR_IGNORED_MASK			0x40
+#define DPCD_OUI_SUPPORT_MASK					0x80
+/* 0x00008, 0x0000A: RX_PORT[0-1]_CAP_0 */
+#define DPCD_RX_PORTX_CAP_0_LOCAL_EDID_PRESENT_MASK		0x02
+#define DPCD_RX_PORTX_CAP_0_ASSOC_TO_PRECEDING_PORT_MASK	0x04
+/* 0x0000C, 0x00109: I2C_SPEED_CTL_CAP, I2C_SPEED_CTL_SET */
+#define DPCD_I2C_SPEED_CTL_NONE					0x00
+#define DPCD_I2C_SPEED_CTL_1KBIPS				0x01
+#define DPCD_I2C_SPEED_CTL_5KBIPS				0x02
+#define DPCD_I2C_SPEED_CTL_10KBIPS				0x04
+#define DPCD_I2C_SPEED_CTL_100KBIPS				0x08
+#define DPCD_I2C_SPEED_CTL_400KBIPS				0x10
+#define DPCD_I2C_SPEED_CTL_1MBIPS				0x20
+/* 0x0000E: TRAIN_AUX_RD_INTERVAL */
+#define DPCD_TRAIN_AUX_RD_INT_100_400US				0x00
+#define DPCD_TRAIN_AUX_RD_INT_4MS				0x01
+#define DPCD_TRAIN_AUX_RD_INT_8MS				0x02
+#define DPCD_TRAIN_AUX_RD_INT_12MS				0x03
+#define DPCD_TRAIN_AUX_RD_INT_16MS				0x04
+/* 0x00020: DPCD_FAUX_CAP */
+#define DPCD_FAUX_CAP_MASK					0x01
+/* 0x00021: MSTM_CAP */
+#define DPCD_MST_CAP_MASK					0x01
+/* 0x00080, 0x00081|4, 0x00082|8, 0x00083|C: DOWNSP_X_(DET_)CAP */
+#define DPCD_DOWNSP_X_CAP_TYPE_MASK				0x07
+#define DPCD_DOWNSP_X_CAP_TYPE_DP				0x0
+#define DPCD_DOWNSP_X_CAP_TYPE_AVGA				0x1
+#define DPCD_DOWNSP_X_CAP_TYPE_DVI				0x2
+#define DPCD_DOWNSP_X_CAP_TYPE_HDMI				0x3
+#define DPCD_DOWNSP_X_CAP_TYPE_OTHERS				0x4
+#define DPCD_DOWNSP_X_CAP_TYPE_DPPP				0x5
+#define DPCD_DOWNSP_X_CAP_HPD_MASK				0x80
+#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_MASK			0xF0
+#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_SHIFT			4
+#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_720_480_I_60		0x1
+#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_720_480_I_50		0x2
+#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_1920_1080_I_60		0x3
+#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_1920_1080_I_50		0x4
+#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_1280_720_P_60		0x5
+#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_1280_720_P_50		0x7
+/* 0x00082, 0x00086, 0x0008A, 0x0008E: DOWNSP_X_(DET_)CAP2 */
+#define DPCD_DOWNSP_X_DCAP_MAX_BPC_MASK				0x03
+#define DPCD_DOWNSP_X_DCAP_MAX_BPC_8				0x0
+#define DPCD_DOWNSP_X_DCAP_MAX_BPC_10				0x1
+#define DPCD_DOWNSP_X_DCAP_MAX_BPC_12				0x2
+#define DPCD_DOWNSP_X_DCAP_MAX_BPC_16				0x3
+/* 0x00082, 0x00086, 0x0008A, 0x0008E: DOWNSP_X_(DET_)CAP2 */
+#define DPCD_DOWNSP_X_DCAP_HDMI_DPPP_FS2FP_MASK			0x01
+#define DPCD_DOWNSP_X_DCAP_DVI_DL_MASK				0x02
+#define DPCD_DOWNSP_X_DCAP_DVI_HCD_MASK				0x04
+
+/* link configuration field masks, shifts, and register values */
+/* 0x00100: DPCD_LINK_BW_SET */
+#define DPCD_LINK_BW_SET_162GBPS				0x06
+#define DPCD_LINK_BW_SET_270GBPS				0x0A
+#define DPCD_LINK_BW_SET_540GBPS				0x14
+/* 0x00101: LANE_COUNT_SET */
+#define DPCD_LANE_COUNT_SET_MASK				0x1F
+#define DPCD_LANE_COUNT_SET_1					0x01
+#define DPCD_LANE_COUNT_SET_2					0x02
+#define DPCD_LANE_COUNT_SET_4					0x04
+#define DPCD_ENHANCED_FRAME_EN_MASK				0x80
+/* 0x00102: TP_SET */
+#define DPCD_TP_SEL_MASK					0x03
+#define DPCD_TP_SEL_OFF						0x0
+#define DPCD_TP_SEL_TP1						0x1
+#define DPCD_TP_SEL_TP2						0x2
+#define DPCD_TP_SEL_TP3						0x3
+#define DPCD_TP_SET_LQP_MASK					0x06
+#define DPCD_TP_SET_LQP_SHIFT					2
+#define DPCD_TP_SET_LQP_OFF					0x0
+#define DPCD_TP_SET_LQP_D102_TEST				0x1
+#define DPCD_TP_SET_LQP_SER_MES					0x2
+#define DPCD_TP_SET_LQP_PRBS7					0x3
+#define DPCD_TP_SET_REC_CLK_OUT_EN_MASK				0x10
+#define DPCD_TP_SET_SCRAMB_DIS_MASK				0x20
+#define DPCD_TP_SET_SE_COUNT_SEL_MASK				0xC0
+#define DPCD_TP_SET_SE_COUNT_SEL_SHIFT				6
+#define DPCD_TP_SET_SE_COUNT_SEL_DE_ISE				0x0
+#define DPCD_TP_SET_SE_COUNT_SEL_DE				0x1
+#define DPCD_TP_SET_SE_COUNT_SEL_ISE				0x2
+/* 0x00103-0x00106: TRAINING_LANE[0-3]_SET */
+#define DPCD_TRAINING_LANEX_SET_VS_MASK				0x03
+#define DPCD_TRAINING_LANEX_SET_MAX_VS_MASK			0x04
+#define DPCD_TRAINING_LANEX_SET_PE_MASK				0x18
+#define DPCD_TRAINING_LANEX_SET_PE_SHIFT			3
+#define DPCD_TRAINING_LANEX_SET_MAX_PE_MASK			0x20
+/* 0x00107: DOWNSPREAD_CTRL */
+#define DPCD_SPREAD_AMP_MASK					0x10
+#define DPCD_MSA_TIMING_PAR_IGNORED_EN_MASK			0x80
+/* 0x00108: ML_CH_CODING_SET - Same as 0x00006: ML_CH_CODING_SUPPORT */
+/* 0x00109: I2C_SPEED_CTL_SET - Same as 0x0000C: I2C_SPEED_CTL_CAP */
+/* 0x0010F-0x00110: TRAINING_LANE[0_1-2_3]_SET2 */
+#define DPCD_TRAINING_LANE_0_2_SET_PC2_MASK			0x03
+#define DPCD_TRAINING_LANE_0_2_SET_MAX_PC2_MASK			0x04
+#define DPCD_TRAINING_LANE_1_3_SET_PC2_MASK			0x30
+#define DPCD_TRAINING_LANE_1_3_SET_PC2_SHIFT			4
+#define DPCD_TRAINING_LANE_1_3_SET_MAX_PC2_MASK			0x40
+/* 0x00111: MSTM_CTRL */
+#define DPCD_MST_EN_MASK					0x01
+#define DPCD_UP_REQ_EN_MASK					0x02
+#define DPCD_UP_IS_SRC_MASK					0x03
+
+/* link/sink status field masks, shifts, and register values */
+/* 0x00200: SINK_COUNT */
+#define DPCD_SINK_COUNT_LOW_MASK				0x3F
+#define DPCD_SINK_CP_READY_MASK					0x40
+#define DPCD_SINK_COUNT_HIGH_MASK				0x80
+#define DPCD_SINK_COUNT_HIGH_LOW_SHIFT				1
+/* 0x00202: STATUS_LANE_0_1 */
+#define DPCD_STATUS_LANE_0_CR_DONE_MASK				0x01
+#define DPCD_STATUS_LANE_0_CE_DONE_MASK				0x02
+#define DPCD_STATUS_LANE_0_SL_DONE_MASK				0x04
+#define DPCD_STATUS_LANE_1_CR_DONE_MASK				0x10
+#define DPCD_STATUS_LANE_1_CE_DONE_MASK				0x20
+#define DPCD_STATUS_LANE_1_SL_DONE_MASK				0x40
+/* 0x00202: STATUS_LANE_2_3 */
+#define DPCD_STATUS_LANE_2_CR_DONE_MASK				0x01
+#define DPCD_STATUS_LANE_2_CE_DONE_MASK				0x02
+#define DPCD_STATUS_LANE_2_SL_DONE_MASK				0x04
+#define DPCD_STATUS_LANE_3_CR_DONE_MASK				0x10
+#define DPCD_STATUS_LANE_3_CE_DONE_MASK				0x20
+#define DPCD_STATUS_LANE_3_SL_DONE_MASK				0x40
+/* 0x00204: LANE_ALIGN_STATUS_UPDATED */
+#define DPCD_LANE_ALIGN_STATUS_UPDATED_IA_DONE_MASK \
+								0x01
+#define DPCD_LANE_ALIGN_STATUS_UPDATED_DOWNSP_STATUS_CHANGED_MASK \
+								0x40
+#define DPCD_LANE_ALIGN_STATUS_UPDATED_LINK_STATUS_UPDATED_MASK \
+								0x80
+/* 0x00205: SINK_STATUS */
+#define DPCD_SINK_STATUS_RX_PORT0_SYNC_STATUS_MASK		0x01
+#define DPCD_SINK_STATUS_RX_PORT1_SYNC_STATUS_MASK		0x02
+
+/* 0x00206, 0x00207: ADJ_REQ_LANE_[0,2]_[1,3] */
+#define DPCD_ADJ_REQ_LANE_0_2_VS_MASK				0x03
+#define DPCD_ADJ_REQ_LANE_0_2_PE_MASK				0x0C
+#define DPCD_ADJ_REQ_LANE_0_2_PE_SHIFT				2
+#define DPCD_ADJ_REQ_LANE_1_3_VS_MASK				0x30
+#define DPCD_ADJ_REQ_LANE_1_3_VS_SHIFT				4
+#define DPCD_ADJ_REQ_LANE_1_3_PE_MASK				0xC0
+#define DPCD_ADJ_REQ_LANE_1_3_PE_SHIFT				6
+/* 0x0020C: ADJ_REQ_PC2 */
+#define DPCD_ADJ_REQ_PC2_LANE_0_MASK				0x03
+#define DPCD_ADJ_REQ_PC2_LANE_1_MASK				0x0C
+#define DPCD_ADJ_REQ_PC2_LANE_1_SHIFT				2
+#define DPCD_ADJ_REQ_PC2_LANE_2_MASK				0x30
+#define DPCD_ADJ_REQ_PC2_LANE_2_SHIFT				4
+#define DPCD_ADJ_REQ_PC2_LANE_3_MASK				0xC0
+#define DPCD_ADJ_REQ_PC2_LANE_3_SHIFT				6
+
+#endif /* __GDSYS_LOGICORE_DP_DPCD_H__ */
diff --git a/drivers/video/logicore_dp_tx.c b/drivers/video/logicore_dp_tx.c
new file mode 100644
index 00000000000..84fafe43410
--- /dev/null
+++ b/drivers/video/logicore_dp_tx.c
@@ -0,0 +1,2296 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * logicore_dp_tx.c
+ *
+ * Driver for XILINX LogiCore DisplayPort v6.1 TX (Source)
+ * based on Xilinx dp_v3_1 driver sources, updated to dp_v4_0
+ *
+ * (C) Copyright 2016
+ * Dirk Eibach,  Guntermann & Drunck GmbH, dirk.eibach at gdsys.cc
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <display.h>
+#include <dm.h>
+#include <errno.h>
+
+#include "axi.h"
+#include "logicore_dp_dpcd.h"
+#include "logicore_dp_tx.h"
+#include "logicore_dp_tx_regif.h"
+
+/* Default AXI clock frequency value */
+#define S_AXI_CLK_DEFAULT 100000000
+
+/* Default DP phy clock value */
+#define PHY_CLOCK_SELECT_DEFAULT PHY_CLOCK_SELECT_540GBPS
+
+/* The maximum voltage swing level is 3 */
+#define MAXIMUM_VS_LEVEL 3
+/* The maximum pre-emphasis level is 3 */
+#define MAXIMUM_PE_LEVEL 3
+
+/* Error out if an AUX request yields a defer reply more than 50 times */
+#define AUX_MAX_DEFER_COUNT 50
+/* Error out if an AUX request times out more than 50 times awaiting a reply */
+#define AUX_MAX_TIMEOUT_COUNT 50
+/* Error out if checking for a connected device times out more than 50 times */
+#define IS_CONNECTED_MAX_TIMEOUT_COUNT 50
+
+/**
+ * enum link_training_states - States for link training state machine
+ * @TS_CLOCK_RECOVERY:       State for clock recovery
+ * @TS_CHANNEL_EQUALIZATION: State for channel equalization
+ * @TS_ADJUST_LINK_RATE:     State where link rate is reduced in reaction to
+ *			     failed link training
+ * @TS_ADJUST_LANE_COUNT:    State where lane count is reduced in reaction to
+ *			     failed link training
+ * @TS_FAILURE:              State of link training failure
+ * @TS_SUCCESS::             State for successfully completed link training
+ */
+enum link_training_states {
+	TS_CLOCK_RECOVERY,
+	TS_CHANNEL_EQUALIZATION,
+	TS_ADJUST_LINK_RATE,
+	TS_ADJUST_LANE_COUNT,
+	TS_FAILURE,
+	TS_SUCCESS
+};
+
+/**
+ * struct aux_transaction - Description of an AUX channel transaction
+ * @cmd_code:  Command code of the transaction
+ * @num_bytes: The number of bytes in the transaction's payload data
+ * @address:   The DPCD address of the transaction
+ * @data:      Payload data of the AUX channel transaction
+ */
+struct aux_transaction {
+	u16 cmd_code;
+	u8 num_bytes;
+	u32 address;
+	u8 *data;
+};
+
+/**
+ * struct main_stream_attributes - Main stream attributes
+ * @pixel_clock_hz: Pixel clock of the stream (in Hz)
+ * @misc_0:                    Miscellaneous stream attributes 0 as specified
+ *			       by the DisplayPort 1.2 specification
+ * @misc_1:                    Miscellaneous stream attributes 1 as specified
+ *			       by the DisplayPort 1.2 specification
+ * @n_vid:                     N value for the video stream
+ * @m_vid:                     M value used to recover the video clock from the
+ *			       link clock
+ * @user_pixel_width:          Width of the user data input port
+ * @data_per_lane:             Used to translate the number of pixels per line
+ *			       to the native internal 16-bit datapath
+ * @avg_bytes_per_tu:          Average number of bytes per transfer unit,
+ *			       scaled up by a factor of 1000
+ * @transfer_unit_size:        Size of the transfer unit in the framing logic
+ *			       In MST mode, this is also the number of time
+ *			       slots that are alloted in the payload ID table
+ * @init_wait:                 Number of initial wait cycles at the start of a
+ *			       new line by the framing logic
+ * @bits_per_color:            Bits per color component
+ * @component_format:          The component format currently in use by the
+ *			       video stream
+ * @dynamic_range:             The dynamic range currently in use by the video
+ *			       stream
+ * @y_cb_cr_colorimetry:       The YCbCr colorimetry currently in use by the
+ *			       video stream
+ * @synchronous_clock_mode:    Synchronous clock mode is currently in use by
+ *			       the video stream
+ * @override_user_pixel_width: If set to 1, the value stored for
+ *			       user_pixel_width will be used as the pixel width
+ * @h_start:                   Horizontal blank start (pixels)
+ * @h_active:                  Horizontal active resolution (pixels)
+ * @h_sync_width:              Horizontal sync width (pixels)
+ * @h_total:                   Horizontal total (pixels)
+ * @h_sync_polarity:           Horizontal sync polarity (0=neg|1=pos)
+ * @v_start:                   Vertical blank start (in lines)
+ * @v_active:                  Vertical active resolution (lines)
+ * @v_sync_width:              Vertical sync width (lines)
+ * @v_total:                   Vertical total (lines)
+ * @v_sync_polarity:           Vertical sync polarity (0=neg|1=pos)
+ *
+ * All porch parameters have been removed, because our videodata is
+ * hstart/vstart based, and there is no benefit in keeping the porches
+ */
+struct main_stream_attributes {
+	u32 pixel_clock_hz;
+	u32 misc_0;
+	u32 misc_1;
+	u32 n_vid;
+	//u32 m_vid;
+	u32 user_pixel_width;
+	u32 data_per_lane;
+	u32 avg_bytes_per_tu;
+	u32 transfer_unit_size;
+	u32 init_wait;
+	u32 bits_per_color;
+	u8 component_format;
+	u8 dynamic_range;
+	u8 y_cb_cr_colorimetry;
+	u8 synchronous_clock_mode;
+	u8 override_user_pixel_width;
+	u32 h_start;
+	u16 h_active;
+	u16 h_sync_width;
+	u16 h_total;
+	bool h_sync_polarity;
+	u32 v_start;
+	u16 v_active;
+	u16 v_sync_width;
+	u16 v_total;
+	bool v_sync_polarity;
+};
+
+/**
+ * struct link_config - Description of link configuration
+ * @lane_count:                    Currently selected lane count for this link
+ * @link_rate:                     Currently selected link rate for this link
+ * @scrambler_en:                  Flag to determine whether the scrambler is
+ *				   enabled for this link
+ * @enhanced_framing_mode:         Flag to determine whether enhanced framing
+ *				   mode is active for this link
+ * @max_lane_count:                Maximum lane count for this link
+ * @max_link_rate:                 Maximum link rate for this link
+ * @support_enhanced_framing_mode: Flag to indicate whether the link supports
+ *				   enhanced framing mode
+ * @vs_level:                      Voltage swing for each lane
+ * @pe_level:                      Pre-emphasis/cursor level for each lane
+ */
+struct link_config {
+	u8 lane_count;
+	u8 link_rate;
+	bool scrambler_en;
+	bool enhanced_framing_mode;
+	u8 max_lane_count;
+	u8 max_link_rate;
+	bool support_enhanced_framing_mode;
+	u8 vs_level;
+	u8 pe_level;
+};
+
+/**
+ * struct dp_tx - Private data structure of LogiCore DP TX devices
+ *
+ * @base:                   Address of register base of device
+ * @s_axi_clk:              The AXI clock frequency in Hz
+ * @train_adaptive:         Use adaptive link trainig (i.e. successively reduce
+ *			    link rate and/or lane count) for this device
+ * @max_link_rate:          Maximum link rate for this device
+ * @max_lane_count:         Maximum lane count for this device
+ * @dpcd_rx_caps:           RX device's status registers, see below
+ * @lane_status_ajd_reqs:   Lane status and adjustment requests information for
+ *			    this device
+ * @link_config:            The link configuration for this device
+ * @main_stream_attributes: MSA set for this device
+ *
+ * dpcd_rx_caps is a raw read of the RX device's status registers. The first 4
+ * bytes correspond to the lane status associated with clock recovery, channel
+ * equalization, symbol lock, and interlane alignment. The remaining 2 bytes
+ * represent the pre-emphasis and voltage swing level adjustments requested by
+ * the RX device.
+ */
+struct dp_tx {
+	u32 base;
+	u32 s_axi_clk;
+	bool train_adaptive;
+	u8 max_link_rate;
+	u8 max_lane_count;
+	u8 dpcd_rx_caps[16];
+	u8 lane_status_ajd_reqs[6];
+	struct link_config link_config;
+	struct main_stream_attributes main_stream_attributes;
+};
+
+/*
+ * Internal API
+ */
+
+/**
+ * get_reg() - Read a register of a LogiCore DP TX device
+ * @dev: The LogiCore DP TX device in question
+ * @reg: The offset of the register to read
+ *
+ * Return: The read register value
+ */
+static u32 get_reg(struct udevice *dev, u32 reg)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	u32 value = 0;
+	int res;
+
+	/* TODO(mario.six at gdsys.cc): error handling */
+	res = axi_read(dev->parent, dp_tx->base + reg, &value, AXI_SIZE_32);
+	if (res < 0)
+		printf("%s() failed; res = %d\n", __func__, res);
+
+	return value;
+}
+
+/**
+ * set_reg() - Write a register of a LogiCore DP TX device
+ * @dev:   The LogiCore DP TX device in question
+ * @reg:   The offset of the register to write
+ * @value: The value to write to the register
+ */
+static void set_reg(struct udevice *dev, u32 reg, u32 value)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+
+	axi_write(dev->parent, dp_tx->base + reg, &value, AXI_SIZE_32);
+}
+
+/**
+ * is_connected() - Check if there is a connected RX device
+ * @dev: The LogiCore DP TX device in question
+ *
+ * The Xilinx original calls msleep_interruptible at least once, ignoring
+ * status.
+ *
+ * Return: true if a connected RX device was detected, false otherwise
+ */
+static bool is_connected(struct udevice *dev)
+{
+	u8 retries = 0;
+
+	do {
+		int status = get_reg(dev, REG_INTERRUPT_SIG_STATE) &
+			     INTERRUPT_SIG_STATE_HPD_STATE_MASK;
+		if (status)
+			return true;
+
+		udelay(1000);
+	} while (retries++ < IS_CONNECTED_MAX_TIMEOUT_COUNT);
+
+	return false;
+}
+
+/**
+ * wait_phy_ready() - Wait for the DisplayPort PHY to come out of reset
+ * @dev:  The LogiCore DP TX device in question
+ * @mask: Bit mask specifying which bit in the status register should be waited
+ *	  for
+ *
+ * Return: 0 if wait succeeded, -ve if error occurred
+ */
+static int wait_phy_ready(struct udevice *dev, u32 mask)
+{
+	u16 timeout = 20000;
+	u32 phy_status;
+
+	/* Wait until the PHY is ready. */
+	do {
+		phy_status = get_reg(dev, REG_PHY_STATUS) & mask;
+
+		/* Protect against an infinite loop. */
+		if (!timeout--)
+			return -ETIMEDOUT;
+
+		udelay(20);
+	} while (phy_status != mask);
+
+	return 0;
+}
+
+/* AUX channel access */
+
+/**
+ * aux_wait_ready() -  Wait until another request is no longer in progress
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Return: 0 if wait succeeded, -ve if error occurred
+ */
+static int aux_wait_ready(struct udevice *dev)
+{
+	int status;
+	u32 timeout = 100;
+
+	/* Wait until the DisplayPort TX core is ready. */
+	do {
+		status = get_reg(dev, REG_INTERRUPT_SIG_STATE);
+
+		/* Protect against an infinite loop. */
+		if (!timeout--)
+			return -ETIMEDOUT;
+		udelay(20);
+	} while (status & REPLY_STATUS_REPLY_IN_PROGRESS_MASK);
+
+	return 0;
+}
+
+/**
+ * aux_wait_reply() - Wait for reply on AUX channel
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Wait for a reply indicating that the most recent AUX request
+ * has been received by the RX device.
+ *
+ * Return: 0 if wait succeeded, -ve if error occurred
+ */
+static int aux_wait_reply(struct udevice *dev)
+{
+	u32 timeout = 100;
+
+	while (timeout > 0) {
+		int status = get_reg(dev, REG_REPLY_STATUS);
+
+		/* Check for error. */
+		if (status & REPLY_STATUS_REPLY_ERROR_MASK)
+			return -ETIMEDOUT;
+
+		/* Check for a reply. */
+		if ((status & REPLY_STATUS_REPLY_RECEIVED_MASK) &&
+		    !(status &
+		      REPLY_STATUS_REQUEST_IN_PROGRESS_MASK) &&
+		    !(status &
+		      REPLY_STATUS_REPLY_IN_PROGRESS_MASK)) {
+			return 0;
+		}
+
+		timeout--;
+		udelay(20);
+	}
+
+	return -ETIMEDOUT;
+}
+
+/**
+ * aux_request_send() - Send request on the AUX channel
+ * @dev:     The LogiCore DP TX device in question
+ * @request: The request to send
+ *
+ * Submit the supplied AUX request to the RX device over the AUX
+ * channel by writing the command, the destination address, (the write buffer
+ * for write commands), and the data size to the DisplayPort TX core.
+ *
+ * This is the lower-level sending routine, which is called by aux_request().
+ *
+ * Return: 0 if request was sent successfully, -ve on error
+ */
+static int aux_request_send(struct udevice *dev,
+			    struct aux_transaction *request)
+{
+	u32 timeout_count;
+	int status;
+	u8 index;
+
+	/* Ensure that any pending AUX transactions have completed. */
+	timeout_count = 0;
+	do {
+		status = get_reg(dev, REG_REPLY_STATUS);
+
+		udelay(20);
+		timeout_count++;
+		if (timeout_count >= AUX_MAX_TIMEOUT_COUNT)
+			return -ETIMEDOUT;
+	} while ((status & REPLY_STATUS_REQUEST_IN_PROGRESS_MASK) ||
+		 (status & REPLY_STATUS_REPLY_IN_PROGRESS_MASK));
+
+	set_reg(dev, REG_AUX_ADDRESS, request->address);
+
+	if (request->cmd_code == AUX_CMD_WRITE ||
+	    request->cmd_code == AUX_CMD_I2C_WRITE ||
+	    request->cmd_code == AUX_CMD_I2C_WRITE_MOT) {
+		/* Feed write data into the DisplayPort TX core's write FIFO. */
+		for (index = 0; index < request->num_bytes; index++) {
+			set_reg(dev,
+				REG_AUX_WRITE_FIFO, request->data[index]);
+		}
+	}
+
+	/* Submit the command and the data size. */
+	set_reg(dev, REG_AUX_CMD,
+		((request->cmd_code << AUX_CMD_SHIFT) |
+		 ((request->num_bytes - 1) &
+		  AUX_CMD_NBYTES_TRANSFER_MASK)));
+
+	/* Check for a reply from the RX device to the submitted request. */
+	status = aux_wait_reply(dev);
+	if (status)
+		/* Waiting for a reply timed out. */
+		return -ETIMEDOUT;
+
+	/* Analyze the reply. */
+	status = get_reg(dev, REG_AUX_REPLY_CODE);
+	if (status == AUX_REPLY_CODE_DEFER ||
+	    status == AUX_REPLY_CODE_I2C_DEFER) {
+		/* The request was deferred. */
+		return -EAGAIN;
+	} else if ((status == AUX_REPLY_CODE_NACK) ||
+		   (status == AUX_REPLY_CODE_I2C_NACK)) {
+		/* The request was not acknowledged. */
+		return -EIO;
+	}
+
+	/* The request was acknowledged. */
+
+	if (request->cmd_code == AUX_CMD_READ ||
+	    request->cmd_code == AUX_CMD_I2C_READ ||
+	    request->cmd_code == AUX_CMD_I2C_READ_MOT) {
+		/* Wait until all data has been received. */
+		timeout_count = 0;
+		do {
+			status = get_reg(dev, REG_REPLY_DATA_COUNT);
+
+			udelay(100);
+			timeout_count++;
+			if (timeout_count >= AUX_MAX_TIMEOUT_COUNT)
+				return -ETIMEDOUT;
+		} while (status != request->num_bytes);
+
+		/* Obtain the read data from the reply FIFO. */
+		for (index = 0; index < request->num_bytes; index++)
+			request->data[index] = get_reg(dev, REG_AUX_REPLY_DATA);
+	}
+
+	return 0;
+}
+
+/**
+ * aux_request() - Submit request on the AUX channel
+ * @dev:     The LogiCore DP TX device in question
+ * @request: The request to submit
+ *
+ * Submit the supplied AUX request to the RX device over the AUX
+ * channel. If waiting for a reply times out, or if the DisplayPort TX core
+ * indicates that the request was deferred, the request is sent again (up to a
+ * maximum specified by AUX_MAX_DEFER_COUNT|AUX_MAX_TIMEOUT_COUNT).
+ *
+ * Return: 0 if request was submitted successfully, -ve on error
+ */
+static int aux_request(struct udevice *dev, struct aux_transaction *request)
+{
+	u32 defer_count = 0;
+	u32 timeout_count = 0;
+
+	while ((defer_count < AUX_MAX_DEFER_COUNT) &&
+	       (timeout_count < AUX_MAX_TIMEOUT_COUNT)) {
+		int status = aux_wait_ready(dev);
+
+		if (status) {
+			/* The RX device isn't ready yet. */
+			timeout_count++;
+			continue;
+		}
+
+		status = aux_request_send(dev, request);
+		if (status == -EAGAIN) {
+			/* The request was deferred. */
+			defer_count++;
+		} else if (status == -ETIMEDOUT) {
+			/* Waiting for a reply timed out. */
+			timeout_count++;
+		} else {
+			/*
+			 * -EIO indicates that the request was NACK'ed,
+			 * 0 indicates that the request was ACK'ed.
+			 */
+			return status;
+		}
+
+		udelay(100);
+	}
+
+	/* The request was not successfully received by the RX device. */
+	return -ETIMEDOUT;
+}
+
+/**
+ * aux_common() - Common (read/write) AUX communication transmission
+ * @dev:       The LogiCore DP TX device in question
+ * @cmd_type:  Command code of the transaction
+ * @address:   The DPCD address of the transaction
+ * @num_bytes: Number of bytes in the payload data
+ * @data:      The payload data of the AUX command
+ *
+ * Common sequence of submitting an AUX command for AUX read, AUX write,
+ * I2C-over-AUX read, and I2C-over-AUX write transactions. If required, the
+ * reads and writes are split into multiple requests, each acting on a maximum
+ * of 16 bytes.
+ *
+ * Return: 0 if OK, -ve on error
+ */
+static int aux_common(struct udevice *dev, u32 cmd_type, u32 address,
+		      u32 num_bytes, u8 *data)
+{
+	struct aux_transaction request;
+	u32 bytes_left;
+
+	/*
+	 * Set the start address for AUX transactions. For I2C transactions,
+	 * this is the address of the I2C bus.
+	 */
+	request.address = address;
+
+	bytes_left = num_bytes;
+	while (bytes_left) {
+		int status;
+
+		request.cmd_code = cmd_type;
+
+		if (cmd_type == AUX_CMD_READ ||
+		    cmd_type == AUX_CMD_WRITE) {
+			/* Increment address for normal AUX transactions. */
+			request.address = address + (num_bytes - bytes_left);
+		}
+
+		/* Increment the pointer to the supplied data buffer. */
+		request.data = &data[num_bytes - bytes_left];
+
+		request.num_bytes = (bytes_left > 16) ? 16 : bytes_left;
+		bytes_left -= request.num_bytes;
+
+		if (cmd_type == AUX_CMD_I2C_READ && bytes_left) {
+			/*
+			 * Middle of a transaction I2C read request. Override
+			 * the command code that was set to cmd_type.
+			 */
+			request.cmd_code = AUX_CMD_I2C_READ_MOT;
+		} else if ((cmd_type == AUX_CMD_I2C_WRITE) && bytes_left) {
+			/*
+			 * Middle of a transaction I2C write request. Override
+			 * the command code that was set to cmd_type.
+			 */
+			request.cmd_code = AUX_CMD_I2C_WRITE_MOT;
+		}
+
+		status = aux_request(dev, &request);
+		if (status)
+			return status;
+	}
+
+	return 0;
+}
+
+/**
+ * aux_read() - Issue AUX read request
+ * @dev:           The LogiCore DP TX device in question
+ * @dpcd_address:  The DPCD address to read from
+ * @bytes_to_read: Number of bytes to read
+ * @read_data:     Buffer to receive the read data
+ *
+ * Issue a read request over the AUX channel that will read from the RX
+ * device's DisplayPort Configuration data (DPCD) address space. The read
+ * message will be divided into multiple transactions which read a maximum of
+ * 16 bytes each.
+ *
+ * Return: 0 if read operation was successful, -ve on error
+ */
+static int aux_read(struct udevice *dev, u32 dpcd_address, u32 bytes_to_read,
+		    void *read_data)
+{
+	int status;
+
+	if (!is_connected(dev))
+		return -ENODEV;
+
+	/* Send AUX read transaction. */
+	status = aux_common(dev, AUX_CMD_READ, dpcd_address,
+			    bytes_to_read, (u8 *)read_data);
+
+	return status;
+}
+
+/**
+ * aux_write() - Issue AUX write request
+ * @dev:            The LogiCore DP TX device in question
+ * @dpcd_address:   The DPCD address to write to
+ * @bytes_to_write: Number of bytes to write
+ * @write_data:     Buffer containig data to be written
+ *
+ * Issue a write request over the AUX channel that will write to
+ * the RX device's DisplayPort Configuration data (DPCD) address space. The
+ * write message will be divided into multiple transactions which write a
+ * maximum of 16 bytes each.
+ *
+ * Return: 0 if write operation was successful, -ve on error
+ */
+static int aux_write(struct udevice *dev, u32 dpcd_address, u32 bytes_to_write,
+		     void *write_data)
+{
+	int status;
+
+	if (!is_connected(dev))
+		return -ENODEV;
+
+	/* Send AUX write transaction. */
+	status = aux_common(dev, AUX_CMD_WRITE, dpcd_address,
+			    bytes_to_write, (u8 *)write_data);
+
+	return status;
+}
+
+/* Core initialization */
+
+/**
+ * initialize() - Initialize a LogiCore DP TX device
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Return: Always 0
+ */
+static int initialize(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	u32 val;
+	u32 phy_config;
+	unsigned int k;
+
+	/* place the PHY (and GTTXRESET) into reset. */
+	phy_config = get_reg(dev, REG_PHY_CONFIG);
+	set_reg(dev, REG_PHY_CONFIG, phy_config | PHY_CONFIG_GT_ALL_RESET_MASK);
+
+	/* reset the video streams and AUX logic. */
+	set_reg(dev, REG_SOFT_RESET,
+		SOFT_RESET_VIDEO_STREAM_ALL_MASK |
+		SOFT_RESET_AUX_MASK);
+
+	/* disable the DisplayPort TX core. */
+	set_reg(dev, REG_ENABLE, 0);
+
+	/* set the clock divider. */
+	val = get_reg(dev, REG_AUX_CLK_DIVIDER);
+	val &= ~AUX_CLK_DIVIDER_VAL_MASK;
+	val |= dp_tx->s_axi_clk / 1000000;
+	set_reg(dev, REG_AUX_CLK_DIVIDER, val);
+
+	/* set the DisplayPort TX core's clock speed. */
+	set_reg(dev, REG_PHY_CLOCK_SELECT, PHY_CLOCK_SELECT_DEFAULT);
+
+	/* bring the PHY (and GTTXRESET) out of reset. */
+	set_reg(dev, REG_PHY_CONFIG,
+		phy_config & ~PHY_CONFIG_GT_ALL_RESET_MASK);
+
+	/* enable the DisplayPort TX core. */
+	set_reg(dev, REG_ENABLE, 1);
+
+	/* Unmask Hot-Plug-Detect (HPD) interrupts. */
+	set_reg(dev, REG_INTERRUPT_MASK,
+		~INTERRUPT_MASK_HPD_PULSE_DETECTED_MASK &
+		~INTERRUPT_MASK_HPD_EVENT_MASK &
+		~INTERRUPT_MASK_HPD_IRQ_MASK);
+
+	for (k = 0; k < 4; k++) {
+		/* Disable pre-cursor levels. */
+		set_reg(dev, REG_PHY_PRECURSOR_LANE_0 + 4 * k, 0);
+
+		/* Write default voltage swing levels to the TX registers. */
+		set_reg(dev, REG_PHY_VOLTAGE_DIFF_LANE_0 + 4 * k, 0);
+
+		/* Write default pre-emphasis levels to the TX registers. */
+		set_reg(dev, REG_PHY_POSTCURSOR_LANE_0 + 4 * k, 0);
+	}
+
+	return 0;
+}
+
+/**
+ * is_link_rate_valid() - Check if given link rate is valif for device
+ * @dev:       The LogiCore DP TX device in question
+ * @link_rate: The link rate to be checked for validity
+ *
+ * Return: true if he supplied link rate is valid, false otherwise
+ */
+static bool is_link_rate_valid(struct udevice *dev, u8 link_rate)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	bool valid = true;
+
+	if (link_rate != LINK_BW_SET_162GBPS &&
+	    link_rate != LINK_BW_SET_270GBPS &&
+	    link_rate != LINK_BW_SET_540GBPS)
+		valid = false;
+	else if (link_rate > dp_tx->link_config.max_link_rate)
+		valid = false;
+
+	return valid;
+}
+
+/**
+ * is_lane_count_valid() - Check if given lane count is valif for device
+ * @dev:        The LogiCore DP TX device in question
+ * @lane_count: The lane count to be checked for validity
+ *
+ * Return: true if he supplied lane count is valid, false otherwise
+ */
+static bool is_lane_count_valid(struct udevice *dev, u8 lane_count)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	bool valid = true;
+
+	if (lane_count != LANE_COUNT_SET_1 &&
+	    lane_count != LANE_COUNT_SET_2 &&
+	    lane_count != LANE_COUNT_SET_4)
+		valid = false;
+	else if (lane_count > dp_tx->link_config.max_lane_count)
+		valid = false;
+
+	return valid;
+}
+
+/**
+ * get_rx_capabilities() - Check if capabilities of RX device are valid for TX
+ *			   device
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Return: 0 if the capabilities of the RX device are valid for the TX device,
+ *	   -ve if not, of an error occurred during capability determination
+ */
+static int get_rx_capabilities(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+	u8 rx_max_link_rate;
+	u8 rx_max_lane_count;
+
+	if (!is_connected(dev))
+		return -ENODEV;
+
+	status = aux_read(dev, DPCD_RECEIVER_CAP_FIELD_START, 16,
+			  dp_tx->dpcd_rx_caps);
+	if (status)
+		return -EIO;
+
+	rx_max_link_rate = dp_tx->dpcd_rx_caps[DPCD_MAX_LINK_RATE];
+	rx_max_lane_count = dp_tx->dpcd_rx_caps[DPCD_MAX_LANE_COUNT] &
+			    DPCD_MAX_LANE_COUNT_MASK;
+
+	dp_tx->link_config.max_link_rate =
+		(rx_max_link_rate > dp_tx->max_link_rate) ?
+		dp_tx->max_link_rate : rx_max_link_rate;
+	if (!is_link_rate_valid(dev, rx_max_link_rate))
+		return -EINVAL;
+
+	dp_tx->link_config.max_lane_count =
+		(rx_max_lane_count > dp_tx->max_lane_count) ?
+		dp_tx->max_lane_count : rx_max_lane_count;
+	if (!is_lane_count_valid(dev, rx_max_lane_count))
+		return -EINVAL;
+
+	dp_tx->link_config.support_enhanced_framing_mode =
+		dp_tx->dpcd_rx_caps[DPCD_MAX_LANE_COUNT] &
+		DPCD_ENHANCED_FRAME_SUPPORT_MASK;
+
+	return 0;
+}
+
+/**
+ * enable_main_link() - Switch on main link for a device
+ * @dev: The LogiCore DP TX device in question
+ */
+static void enable_main_link(struct udevice *dev)
+{
+	/* reset the scrambler. */
+	set_reg(dev, REG_FORCE_SCRAMBLER_RESET, 0x1);
+
+	/* enable the main stream. */
+	set_reg(dev, REG_ENABLE_MAIN_STREAM, 0x1);
+}
+
+/**
+ * disable_main_link() - Switch off main link for a device
+ * @dev: The LogiCore DP TX device in question
+ */
+static void disable_main_link(struct udevice *dev)
+{
+	/* reset the scrambler. */
+	set_reg(dev, REG_FORCE_SCRAMBLER_RESET, 0x1);
+
+	/* Disable the main stream. */
+	set_reg(dev, REG_ENABLE_MAIN_STREAM, 0x0);
+}
+
+/**
+ * reset_dp_phy() - Reset a device
+ * @dev:   The LogiCore DP TX device in question
+ * @reset: Bit mask determining which bits in the device's config register
+ *	   should be set for the reset
+ */
+static void reset_dp_phy(struct udevice *dev, u32 reset)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	u32 val;
+
+	set_reg(dev, REG_ENABLE, 0x0);
+
+	val = get_reg(dev, REG_PHY_CONFIG);
+
+	/* Apply reset. */
+	set_reg(dev, REG_PHY_CONFIG, val | reset);
+
+	/* Remove reset. */
+	set_reg(dev, REG_PHY_CONFIG, val);
+
+	/* Wait for the PHY to be ready. */
+	wait_phy_ready(dev, phy_status_lanes_ready_mask(dp_tx->max_lane_count));
+
+	set_reg(dev, REG_ENABLE, 0x1);
+}
+
+/**
+ * set_enhanced_frame_mode() - Enable/Disable enhanced frame mode
+ * @dev:    The LogiCore DP TX device in question
+ * @enable: Flag to determine whether to enable (1) or disable (0) the enhanced
+ *	    frame mode
+ *
+ * Enable or disable the enhanced framing symbol sequence for
+ * both the DisplayPort TX core and the RX device.
+ *
+ * Return: 0 if enabling/disabling the enhanced frame mode was successful, -ve
+ *	   on error
+ */
+static int set_enhanced_frame_mode(struct udevice *dev, u8 enable)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+	u8 val;
+
+	if (!is_connected(dev))
+		return -ENODEV;
+
+	if (dp_tx->link_config.support_enhanced_framing_mode)
+		dp_tx->link_config.enhanced_framing_mode = enable;
+	else
+		dp_tx->link_config.enhanced_framing_mode = false;
+
+	/* Write enhanced frame mode enable to the DisplayPort TX core. */
+	set_reg(dev, REG_ENHANCED_FRAME_EN,
+		dp_tx->link_config.enhanced_framing_mode);
+
+	/* Write enhanced frame mode enable to the RX device. */
+	status = aux_read(dev, DPCD_LANE_COUNT_SET, 0x1, &val);
+	if (status)
+		return -EIO;
+
+	if (dp_tx->link_config.enhanced_framing_mode)
+		val |= DPCD_ENHANCED_FRAME_EN_MASK;
+	else
+		val &= ~DPCD_ENHANCED_FRAME_EN_MASK;
+
+	status = aux_write(dev, DPCD_LANE_COUNT_SET, 0x1, &val);
+	if (status)
+		return -EIO;
+
+	return 0;
+}
+
+/**
+ * set_lane_count() - Set the lane count
+ * @dev:        The LogiCore DP TX device in question
+ * @lane_count: Lane count to set
+ *
+ * Set the number of lanes to be used by the main link for both
+ * the DisplayPort TX core and the RX device.
+ *
+ * Return: 0 if setting the lane count was successful, -ve on error
+ */
+static int set_lane_count(struct udevice *dev, u8 lane_count)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+	u8 val;
+
+	if (!is_connected(dev))
+		return -ENODEV;
+
+	printf("       set lane count to %u\n", lane_count);
+
+	dp_tx->link_config.lane_count = lane_count;
+
+	/* Write the new lane count to the DisplayPort TX core. */
+	set_reg(dev, REG_LANE_COUNT_SET, dp_tx->link_config.lane_count);
+
+	/* Write the new lane count to the RX device. */
+	status = aux_read(dev, DPCD_LANE_COUNT_SET, 0x1, &val);
+	if (status)
+		return -EIO;
+	val &= ~DPCD_LANE_COUNT_SET_MASK;
+	val |= dp_tx->link_config.lane_count;
+
+	status = aux_write(dev, DPCD_LANE_COUNT_SET, 0x1, &val);
+	if (status)
+		return -EIO;
+
+	return 0;
+}
+
+/**
+ * set_clk_speed() - Set DP phy clock speed
+ * @dev:   The LogiCore DP TX device in question
+ * @speed: The clock frquency to set (one of PHY_CLOCK_SELECT_*)
+ *
+ * Set the clock frequency for the DisplayPort PHY corresponding to a desired
+ * data rate.
+ *
+ * Return: 0 if setting the DP phy clock speed was successful, -ve on error
+ */
+static int set_clk_speed(struct udevice *dev, u32 speed)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+	u32 val;
+	u32 mask;
+
+	/* Disable the DisplayPort TX core first. */
+	val = get_reg(dev, REG_ENABLE);
+	set_reg(dev, REG_ENABLE, 0x0);
+
+	/* Change speed of the feedback clock. */
+	set_reg(dev, REG_PHY_CLOCK_SELECT, speed);
+
+	/* Re-enable the DisplayPort TX core if it was previously enabled. */
+	if (val)
+		set_reg(dev, REG_ENABLE, 0x1);
+
+	/* Wait until the PHY is ready. */
+	mask = phy_status_lanes_ready_mask(dp_tx->max_lane_count);
+	status = wait_phy_ready(dev, mask);
+	if (status)
+		return -EIO;
+
+	return 0;
+}
+
+/**
+ * set_link_rate() - Set the link rate
+ * @dev:       The LogiCore DP TX device in question
+ * @link_rate: The link rate to set (one of LINK_BW_SET_*)
+ *
+ * Set the data rate to be used by the main link for both the DisplayPort TX
+ * core and the RX device.
+ *
+ * Return: 0 if setting the link rate was successful, -ve on error
+ */
+static int set_link_rate(struct udevice *dev, u8 link_rate)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+
+	/* Write a corresponding clock frequency to the DisplayPort TX core. */
+	switch (link_rate) {
+	case LINK_BW_SET_162GBPS:
+		printf("       set link rate to 1.62 Gb/s\n");
+		status = set_clk_speed(dev, PHY_CLOCK_SELECT_162GBPS);
+		break;
+	case LINK_BW_SET_270GBPS:
+		printf("       set link rate to 2.70 Gb/s\n");
+		status = set_clk_speed(dev, PHY_CLOCK_SELECT_270GBPS);
+		break;
+	case LINK_BW_SET_540GBPS:
+		printf("       set link rate to 5.40 Gb/s\n");
+		status = set_clk_speed(dev, PHY_CLOCK_SELECT_540GBPS);
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (status)
+		return -EIO;
+
+	dp_tx->link_config.link_rate = link_rate;
+
+	/* Write new link rate to the DisplayPort TX core. */
+	set_reg(dev, REG_LINK_BW_SET, dp_tx->link_config.link_rate);
+
+	/* Write new link rate to the RX device. */
+	status = aux_write(dev, DPCD_LINK_BW_SET, 1,
+			   &dp_tx->link_config.link_rate);
+	if (status)
+		return -EIO;
+
+	return 0;
+}
+
+/* Link training */
+
+/**
+ * get_training_delay() - Get training delay
+ * @dev:            The LogiCore DP TX device in question
+ * @training_state: The training state for which the required training delay
+ *		    should be queried
+ *
+ * Determine what the RX device's required training delay is for
+ * link training.
+ *
+ * Return: The training delay in us
+ */
+static int get_training_delay(struct udevice *dev, int training_state)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	u16 delay;
+
+	switch (dp_tx->dpcd_rx_caps[DPCD_TRAIN_AUX_RD_INTERVAL]) {
+	case DPCD_TRAIN_AUX_RD_INT_100_400US:
+		if (training_state == TS_CLOCK_RECOVERY)
+			/* delay for the clock recovery phase. */
+			delay = 100;
+		else
+			/* delay for the channel equalization phase. */
+			delay = 400;
+		break;
+	case DPCD_TRAIN_AUX_RD_INT_4MS:
+		delay = 4000;
+		break;
+	case DPCD_TRAIN_AUX_RD_INT_8MS:
+		delay = 8000;
+		break;
+	case DPCD_TRAIN_AUX_RD_INT_12MS:
+		delay = 12000;
+		break;
+	case DPCD_TRAIN_AUX_RD_INT_16MS:
+		delay = 16000;
+		break;
+	default:
+		/* Default to 20 ms. */
+		delay = 20000;
+		break;
+	}
+
+	return delay;
+}
+
+/**
+ * set_vswing_preemp() - Build AUX data to set voltage swing and pre-emphasis
+ * @dev:      The LogiCore DP TX device in question
+ * @aux_data: Buffer to receive the built AUX data
+ *
+ * Build AUX data to set current voltage swing and pre-emphasis level settings;
+ * the necessary data is taken from the link_config structure.
+ */
+static void set_vswing_preemp(struct udevice *dev, u8 *aux_data)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	u8 data;
+	u8 vs_level_rx = dp_tx->link_config.vs_level;
+	u8 pe_level_rx = dp_tx->link_config.pe_level;
+
+	/* Set up the data buffer for writing to the RX device. */
+	data = (pe_level_rx << DPCD_TRAINING_LANEX_SET_PE_SHIFT) | vs_level_rx;
+	/* The maximum voltage swing has been reached. */
+	if (vs_level_rx == MAXIMUM_VS_LEVEL)
+		data |= DPCD_TRAINING_LANEX_SET_MAX_VS_MASK;
+
+	/* The maximum pre-emphasis level has been reached. */
+	if (pe_level_rx == MAXIMUM_PE_LEVEL)
+		data |= DPCD_TRAINING_LANEX_SET_MAX_PE_MASK;
+	memset(aux_data, data, 4);
+}
+
+/**
+ * adj_vswing_preemp() - Adjust voltage swing and pre-emphasis
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Set new voltage swing and pre-emphasis levels using the
+ * adjustment requests obtained from the RX device.
+ *
+ * Return: 0 if voltage swing and pre-emphasis could be adjusted successfully,
+ *	   -ve on error
+ */
+static int adj_vswing_preemp(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+	u8 index;
+	u8 vs_level_adj_req[4];
+	u8 pe_level_adj_req[4];
+	u8 aux_data[4];
+	u8 *ajd_reqs = &dp_tx->lane_status_ajd_reqs[4];
+
+	/*
+	 * Analyze the adjustment requests for changes in voltage swing and
+	 * pre-emphasis levels.
+	 */
+	vs_level_adj_req[0] = ajd_reqs[0] & DPCD_ADJ_REQ_LANE_0_2_VS_MASK;
+	vs_level_adj_req[1] = (ajd_reqs[0] & DPCD_ADJ_REQ_LANE_1_3_VS_MASK) >>
+			      DPCD_ADJ_REQ_LANE_1_3_VS_SHIFT;
+	vs_level_adj_req[2] = ajd_reqs[1] & DPCD_ADJ_REQ_LANE_0_2_VS_MASK;
+	vs_level_adj_req[3] = (ajd_reqs[1] & DPCD_ADJ_REQ_LANE_1_3_VS_MASK) >>
+			      DPCD_ADJ_REQ_LANE_1_3_VS_SHIFT;
+	pe_level_adj_req[0] = (ajd_reqs[0] & DPCD_ADJ_REQ_LANE_0_2_PE_MASK) >>
+			      DPCD_ADJ_REQ_LANE_0_2_PE_SHIFT;
+	pe_level_adj_req[1] = (ajd_reqs[0] & DPCD_ADJ_REQ_LANE_1_3_PE_MASK) >>
+			      DPCD_ADJ_REQ_LANE_1_3_PE_SHIFT;
+	pe_level_adj_req[2] = (ajd_reqs[1] & DPCD_ADJ_REQ_LANE_0_2_PE_MASK) >>
+			      DPCD_ADJ_REQ_LANE_0_2_PE_SHIFT;
+	pe_level_adj_req[3] = (ajd_reqs[1] & DPCD_ADJ_REQ_LANE_1_3_PE_MASK) >>
+			      DPCD_ADJ_REQ_LANE_1_3_PE_SHIFT;
+
+	/*
+	 * Change the drive settings to match the adjustment requests. Use the
+	 * greatest level requested.
+	 */
+	dp_tx->link_config.vs_level = 0;
+	dp_tx->link_config.pe_level = 0;
+	for (index = 0; index < dp_tx->link_config.lane_count; index++) {
+		if (vs_level_adj_req[index] > dp_tx->link_config.vs_level)
+			dp_tx->link_config.vs_level = vs_level_adj_req[index];
+		if (pe_level_adj_req[index] > dp_tx->link_config.pe_level)
+			dp_tx->link_config.pe_level = pe_level_adj_req[index];
+	}
+
+	/*
+	 * Verify that the voltage swing and pre-emphasis combination is
+	 * allowed. Some combinations will result in a differential peak-to-peak
+	 * voltage that is outside the permissible range. See the VESA
+	 * DisplayPort v1.2 Specification, section 3.1.5.2.
+	 * The valid combinations are:
+	 *      PE=0    PE=1    PE=2    PE=3
+	 * VS=0 valid   valid   valid   valid
+	 * VS=1 valid   valid   valid
+	 * VS=2 valid   valid
+	 * VS=3 valid
+	 *
+	 * NOTE:
+	 * Xilinix dp_v3_1 driver seems to have an off by one error when
+	 * limiting pe_level which is fixed here.
+	 */
+	if (dp_tx->link_config.pe_level > (3 - dp_tx->link_config.vs_level))
+		dp_tx->link_config.pe_level = 3 - dp_tx->link_config.vs_level;
+
+	/*
+	 * Make the adjustments to both the DisplayPort TX core and the RX
+	 * device.
+	 */
+	set_vswing_preemp(dev, aux_data);
+	/*
+	 * Write the voltage swing and pre-emphasis levels for each lane to the
+	 * RX device.
+	 */
+	status = aux_write(dev, DPCD_TRAINING_LANE0_SET, 4, aux_data);
+	if (status)
+		return -EIO;
+
+	return 0;
+}
+
+/**
+ * get_lane_status_adj_reqs() - Read lane status and adjustment requests
+ *				information from the device
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Do a burst AUX read from the RX device over the AUX channel. The contents of
+ * the status registers will be stored for later use by check_clock_recovery,
+ * check_channel_equalization, and adj_vswing_preemp.
+ *
+ * Return: 0 if the status information were read successfully, -ve on error
+ */
+static int get_lane_status_adj_reqs(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+
+	/*
+	 * Read and store 4 bytes of lane status and 2 bytes of adjustment
+	 * requests.
+	 */
+	status = aux_read(dev, DPCD_STATUS_LANE_0_1, 6,
+			  dp_tx->lane_status_ajd_reqs);
+	if (status)
+		return -EIO;
+
+	return 0;
+}
+
+/**
+ * check_clock_recovery() - Check clock recovery success
+ * @dev:        The LogiCore DP TX device in question
+ * @lane_count: The number of lanes for which to check clock recovery success
+ *
+ * Check if the RX device's DisplayPort Configuration data (DPCD) indicates
+ * that the clock recovery sequence during link training was successful - the
+ * RX device's link clock and data recovery unit has realized and maintained
+ * the frequency lock for all lanes currently in use.
+ *
+ * Return: 0 if clock recovery was successful on all lanes in question, -ve if
+ *	   not
+ */
+static int check_clock_recovery(struct udevice *dev, u8 lane_count)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	u8 *lane_status = dp_tx->lane_status_ajd_reqs;
+
+	/* Check that all LANEx_CR_DONE bits are set. */
+	switch (lane_count) {
+	case LANE_COUNT_SET_4:
+		if (!(lane_status[1] & DPCD_STATUS_LANE_3_CR_DONE_MASK))
+			goto out_fail;
+		if (!(lane_status[1] & DPCD_STATUS_LANE_2_CR_DONE_MASK))
+			goto out_fail;
+	/* Drop through and check lane 1. */
+	case LANE_COUNT_SET_2:
+		if (!(lane_status[0] & DPCD_STATUS_LANE_1_CR_DONE_MASK))
+			goto out_fail;
+	/* Drop through and check lane 0. */
+	case LANE_COUNT_SET_1:
+		if (!(lane_status[0] & DPCD_STATUS_LANE_0_CR_DONE_MASK))
+			goto out_fail;
+	default:
+		/* All (lane_count) lanes have achieved clock recovery. */
+		break;
+	}
+
+	return 0;
+
+out_fail:
+	return -EIO;
+}
+
+/**
+ * check_channel_equalization() - Check channel equalization success
+ * @dev:        The LogiCore DP TX device in question
+ * @lane_count: The number of lanes for which to check channel equalization
+ *		success
+ *
+ * Check if the RX device's DisplayPort Configuration data (DPCD) indicates
+ * that the channel equalization sequence during link training was successful -
+ * the RX device has achieved channel equalization, symbol lock, and interlane
+ * alignment for all lanes currently in use.
+ *
+ * Return: 0 if channel equalization was successful on all lanes in question,
+ *	   -ve if not
+ */
+static int check_channel_equalization(struct udevice *dev, u8 lane_count)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	u8 *lane_status = dp_tx->lane_status_ajd_reqs;
+
+	/* Check that all LANEx_CHANNEL_EQ_DONE bits are set. */
+	switch (lane_count) {
+	case LANE_COUNT_SET_4:
+		if (!(lane_status[1] & DPCD_STATUS_LANE_3_CE_DONE_MASK))
+			goto out_fail;
+		if (!(lane_status[1] & DPCD_STATUS_LANE_2_CE_DONE_MASK))
+			goto out_fail;
+	/* Drop through and check lane 1. */
+	case LANE_COUNT_SET_2:
+		if (!(lane_status[0] & DPCD_STATUS_LANE_1_CE_DONE_MASK))
+			goto out_fail;
+	/* Drop through and check lane 0. */
+	case LANE_COUNT_SET_1:
+		if (!(lane_status[0] & DPCD_STATUS_LANE_0_CE_DONE_MASK))
+			goto out_fail;
+	default:
+		/* All (lane_count) lanes have achieved channel equalization. */
+		break;
+	}
+
+	/* Check that all LANEx_SYMBOL_LOCKED bits are set. */
+	switch (lane_count) {
+	case LANE_COUNT_SET_4:
+		if (!(lane_status[1] & DPCD_STATUS_LANE_3_SL_DONE_MASK))
+			goto out_fail;
+		if (!(lane_status[1] & DPCD_STATUS_LANE_2_SL_DONE_MASK))
+			goto out_fail;
+	/* Drop through and check lane 1. */
+	case LANE_COUNT_SET_2:
+		if (!(lane_status[0] & DPCD_STATUS_LANE_1_SL_DONE_MASK))
+			goto out_fail;
+	/* Drop through and check lane 0. */
+	case LANE_COUNT_SET_1:
+		if (!(lane_status[0] & DPCD_STATUS_LANE_0_SL_DONE_MASK))
+			goto out_fail;
+	default:
+		/* All (lane_count) lanes have achieved symbol lock. */
+		break;
+	}
+
+	/* Check that interlane alignment is done. */
+	if (!(lane_status[2] & DPCD_LANE_ALIGN_STATUS_UPDATED_IA_DONE_MASK))
+		goto out_fail;
+
+	return 0;
+
+out_fail:
+	return -EIO;
+}
+
+/**
+ * set_training_pattern() - Set training pattern for link training
+ * @dev:     The LogiCore DP TX device in question
+ * @pattern: The training pattern to set
+ *
+ * Set the training pattern to be used during link training for both the
+ * DisplayPort TX core and the RX device.
+ *
+ * Return: 0 if the training pattern could be set successfully, -ve if not
+ */
+static int set_training_pattern(struct udevice *dev, u32 pattern)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+	u8 aux_data[5];
+
+	/* Write to the DisplayPort TX core. */
+	set_reg(dev, REG_TRAINING_PATTERN_SET, pattern);
+
+	aux_data[0] = pattern;
+
+	/* Write scrambler disable to the DisplayPort TX core. */
+	switch (pattern) {
+	case TRAINING_PATTERN_SET_OFF:
+		set_reg(dev, REG_SCRAMBLING_DISABLE, 0);
+		dp_tx->link_config.scrambler_en = 1;
+		break;
+	case TRAINING_PATTERN_SET_TP1:
+	case TRAINING_PATTERN_SET_TP2:
+	case TRAINING_PATTERN_SET_TP3:
+		aux_data[0] |= DPCD_TP_SET_SCRAMB_DIS_MASK;
+		set_reg(dev, REG_SCRAMBLING_DISABLE, 1);
+		dp_tx->link_config.scrambler_en = 0;
+		break;
+	default:
+		break;
+	}
+
+	/*
+	 * Make the adjustments to both the DisplayPort TX core and the RX
+	 * device.
+	 */
+	set_vswing_preemp(dev, &aux_data[1]);
+	/*
+	 * Write the voltage swing and pre-emphasis levels for each lane to the
+	 * RX device.
+	 */
+	if  (pattern == TRAINING_PATTERN_SET_OFF)
+		status = aux_write(dev, DPCD_TP_SET, 1, aux_data);
+	else
+		status = aux_write(dev, DPCD_TP_SET, 5, aux_data);
+	if (status)
+		return -EIO;
+
+	return 0;
+}
+
+/**
+ * training_state_clock_recovery() - Run clock recovery part of link training
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Run the clock recovery sequence as part of link training. The
+ * sequence is as follows:
+ *
+ *	0) Start signaling at the minimum voltage swing, pre-emphasis, and
+ *	   post- cursor levels.
+ *	1) Transmit training pattern 1 over the main link with symbol
+ *	   scrambling disabled.
+ *	2) The clock recovery loop. If clock recovery is unsuccessful after
+ *	   MaxIterations loop iterations, return.
+ *	2a) Wait for at least the period of time specified in the RX device's
+ *	    DisplayPort Configuration data (DPCD) register,
+ *	    TRAINING_AUX_RD_INTERVAL.
+ *	2b) Check if all lanes have achieved clock recovery lock. If so,
+ *	    return.
+ *	2c) Check if the same voltage swing level has been used 5 consecutive
+ *	    times or if the maximum level has been reached. If so, return.
+ *	2d) Adjust the voltage swing, pre-emphasis, and post-cursor levels as
+ *	    requested by the RX device.
+ *	2e) Loop back to 2a.
+ *
+ * For a more detailed description of the clock recovery sequence, see section
+ * 3.5.1.2.1 of the DisplayPort 1.2a specification document.
+ *
+ * Return: The next state machine state to advance to
+ */
+static unsigned int training_state_clock_recovery(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+	u32 delay_us;
+	u8 prev_vs_level = 0;
+	u8 same_vs_level_count = 0;
+
+	/*
+	 * Obtain the required delay for clock recovery as specified by the
+	 * RX device.
+	 */
+	delay_us = get_training_delay(dev, TS_CLOCK_RECOVERY);
+
+	/* Start CRLock. */
+
+	/* Transmit training pattern 1. */
+	/* Disable the scrambler. */
+	/* Start from minimal voltage swing and pre-emphasis levels. */
+	dp_tx->link_config.vs_level = 0;
+	dp_tx->link_config.pe_level = 0;
+	status = set_training_pattern(dev, TRAINING_PATTERN_SET_TP1);
+	if (status)
+		return TS_FAILURE;
+
+	while (1) {
+		/* Wait delay specified in TRAINING_AUX_RD_INTERVAL. */
+		udelay(delay_us);
+
+		/* Get lane and adjustment requests. */
+		status = get_lane_status_adj_reqs(dev);
+		if (status)
+			return TS_FAILURE;
+
+		/*
+		 * Check if all lanes have realized and maintained the frequency
+		 * lock and get adjustment requests.
+		 */
+		status = check_clock_recovery(dev,
+					      dp_tx->link_config.lane_count);
+		if (!status)
+			return TS_CHANNEL_EQUALIZATION;
+
+		/*
+		 * Check if the same voltage swing for each lane has been used 5
+		 * consecutive times.
+		 */
+		if (prev_vs_level == dp_tx->link_config.vs_level) {
+			same_vs_level_count++;
+		} else {
+			same_vs_level_count = 0;
+			prev_vs_level = dp_tx->link_config.vs_level;
+		}
+		if (same_vs_level_count >= 5)
+			break;
+
+		/* Only try maximum voltage swing once. */
+		if (dp_tx->link_config.vs_level == MAXIMUM_VS_LEVEL)
+			break;
+
+		/* Adjust the drive settings as requested by the RX device. */
+		status = adj_vswing_preemp(dev);
+		if (status)
+			/* The AUX write failed. */
+			return TS_FAILURE;
+	}
+
+	return TS_ADJUST_LINK_RATE;
+}
+
+/**
+ * training_state_channel_equalization() - Run channel equalization part of
+ *					   link training
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Run the channel equalization sequence as part of link
+ * training. The sequence is as follows:
+ *
+ *	0) Start signaling with the same drive settings used at the end of the
+ *	   clock recovery sequence.
+ *	1) Transmit training pattern 2 (or 3) over the main link with symbol
+ *	   scrambling disabled.
+ *	2) The channel equalization loop. If channel equalization is
+ *	   unsuccessful after 5 loop iterations, return.
+ *	2a) Wait for at least the period of time specified in the RX device's
+ *	    DisplayPort Configuration data (DPCD) register,
+ *	    TRAINING_AUX_RD_INTERVAL.
+ *	2b) Check if all lanes have achieved channel equalization, symbol lock,
+ *	    and interlane alignment. If so, return.
+ *	2c) Check if the same voltage swing level has been used 5 consecutive
+ *	    times or if the maximum level has been reached. If so, return.
+ *	2d) Adjust the voltage swing, pre-emphasis, and post-cursor levels as
+ *	    requested by the RX device.
+ *	2e) Loop back to 2a.
+ *
+ * For a more detailed description of the channel equalization sequence, see
+ * section 3.5.1.2.2 of the DisplayPort 1.2a specification document.
+ *
+ * Return: The next state machine state to advance to
+ */
+static int training_state_channel_equalization(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+	u32 delay_us;
+	u32 iteration_count = 0;
+
+	/*
+	 * Obtain the required delay for channel equalization as specified by
+	 * the RX device.
+	 */
+	delay_us = get_training_delay(dev, TS_CHANNEL_EQUALIZATION);
+
+	/* Start channel equalization. */
+
+	/* Write the current drive settings. */
+	/* Transmit training pattern 2/3. */
+	if (dp_tx->dpcd_rx_caps[DPCD_MAX_LANE_COUNT] & DPCD_TPS3_SUPPORT_MASK)
+		status = set_training_pattern(dev, TRAINING_PATTERN_SET_TP3);
+	else
+		status = set_training_pattern(dev, TRAINING_PATTERN_SET_TP2);
+
+	if (status)
+		return TS_FAILURE;
+
+	while (iteration_count < 5) {
+		/* Wait delay specified in TRAINING_AUX_RD_INTERVAL. */
+		udelay(delay_us);
+
+		/* Get lane and adjustment requests. */
+		status = get_lane_status_adj_reqs(dev);
+		if (status)
+			/* The AUX read failed. */
+			return TS_FAILURE;
+
+		/* Check that all lanes still have their clocks locked. */
+		status = check_clock_recovery(dev,
+					      dp_tx->link_config.lane_count);
+		if (status)
+			break;
+
+		/*
+		 * Check if all lanes have accomplished channel equalization,
+		 * symbol lock, and interlane alignment.
+		 */
+		status =
+		    check_channel_equalization(dev,
+					       dp_tx->link_config.lane_count);
+		if (!status)
+			return TS_SUCCESS;
+
+		/* Adjust the drive settings as requested by the RX device. */
+		status = adj_vswing_preemp(dev);
+		if (status)
+			/* The AUX write failed. */
+			return TS_FAILURE;
+
+		iteration_count++;
+	}
+
+	/*
+	 * Tried 5 times with no success. Try a reduced bitrate first, then
+	 * reduce the number of lanes.
+	 */
+	return TS_ADJUST_LINK_RATE;
+}
+
+/**
+ * training_state_adjust_link_rate() - Downshift data rate and/or lane count
+ * @dev: The LogiCore DP TX device in question
+ *
+ * This function is reached if either the clock recovery or the channel
+ * equalization process failed during training. As a result, the data rate will
+ * be downshifted, and training will be re-attempted (starting with clock
+ * recovery) at the reduced data rate. If the data rate is already at 1.62
+ * Gbps, a downshift in lane count will be attempted.
+ *
+ * Return: The next state machine state to advance to
+ */
+static int training_state_adjust_link_rate(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+
+	switch (dp_tx->link_config.link_rate) {
+	case LINK_BW_SET_540GBPS:
+		status = set_link_rate(dev, LINK_BW_SET_270GBPS);
+		if (status) {
+			status = TS_FAILURE;
+			break;
+		}
+		status = TS_CLOCK_RECOVERY;
+		break;
+	case LINK_BW_SET_270GBPS:
+		status = set_link_rate(dev, LINK_BW_SET_162GBPS);
+		if (status) {
+			status = TS_FAILURE;
+			break;
+		}
+		status = TS_CLOCK_RECOVERY;
+		break;
+	default:
+		/*
+		 * Already at the lowest link rate. Try reducing the lane
+		 * count next.
+		 */
+		status = TS_ADJUST_LANE_COUNT;
+		break;
+	}
+
+	return status;
+}
+
+/**
+ * trainig_state_adjust_lane_count - Downshift lane count
+ * @dev: The LogiCore DP TX device in question
+ *
+ * This function is reached if either the clock recovery or the channel
+ * equalization process failed during training, and a minimal data rate of 1.62
+ * Gbps was being used. As a result, the number of lanes in use will be
+ * reduced, and training will be re-attempted (starting with clock recovery) at
+ * this lower lane count.
+ *
+ * Return: The next state machine state to advance to
+ */
+static int trainig_state_adjust_lane_count(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+
+	switch (dp_tx->link_config.lane_count) {
+	case LANE_COUNT_SET_4:
+		status = set_lane_count(dev, LANE_COUNT_SET_2);
+		if (status) {
+			status = TS_FAILURE;
+			break;
+		}
+
+		status = set_link_rate(dev, dp_tx->link_config.max_link_rate);
+		if (status) {
+			status = TS_FAILURE;
+			break;
+		}
+		status = TS_CLOCK_RECOVERY;
+		break;
+	case LANE_COUNT_SET_2:
+		status = set_lane_count(dev, LANE_COUNT_SET_1);
+		if (status) {
+			status = TS_FAILURE;
+			break;
+		}
+
+		status = set_link_rate(dev, dp_tx->link_config.max_link_rate);
+		if (status) {
+			status = TS_FAILURE;
+			break;
+		}
+		status = TS_CLOCK_RECOVERY;
+		break;
+	default:
+		/*
+		 * Already at the lowest lane count. Training has failed at the
+		 * lowest lane count and link rate.
+		 */
+		status = TS_FAILURE;
+		break;
+	}
+
+	return status;
+}
+
+/**
+ * check_link_status() - Check status of link
+ * @dev:        The LogiCore DP TX device in question
+ * @lane_count: The lane count to use for the check
+ *
+ * Check if the receiver's DisplayPort Configuration data (DPCD) indicates the
+ * receiver has achieved and maintained clock recovery, channel equalization,
+ * symbol lock, and interlane alignment for all lanes currently in use.
+ *
+ * Return: 0 if the link status is OK, -ve if a error occurred during checking
+ */
+static int check_link_status(struct udevice *dev, u8 lane_count)
+{
+	u8 retry_count = 0;
+
+	if (!is_connected(dev))
+		return -ENODEV;
+
+	/* Retrieve AUX info. */
+	do {
+		int status;
+
+		/* Get lane and adjustment requests. */
+		status = get_lane_status_adj_reqs(dev);
+		if (status)
+			return -EIO;
+
+		/* Check if the link needs training. */
+		if ((check_clock_recovery(dev, lane_count) == 0) &&
+		    (check_channel_equalization(dev, lane_count) == 0))
+			return 0;
+
+		retry_count++;
+	} while (retry_count < 5); /* Retry up to 5 times. */
+
+	return -EIO;
+}
+
+/**
+ * run_training() - Run link training
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Run the link training process. It is implemented as a state machine, with
+ * each state returning the next state. First, the clock recovery sequence will
+ * be run; if successful, the channel equalization sequence will run. If either
+ * the clock recovery or channel equalization sequence failed, the link rate or
+ * the number of lanes used will be reduced and training will be re-attempted.
+ * If training fails@the minimal data rate, 1.62 Gbps with a single lane,
+ * training will no longer re-attempt and fail.
+ *
+ * ### Here be dragons ###
+ * There are undocumented timeout constraints in the link training process. In
+ * DP v1.2a spec, Chapter 3.5.1.2.2 a 10ms limit for the complete training
+ * process is mentioned. Which individual timeouts are derived and implemented
+ * by sink manufacturers is unknown. So each step should be as short as
+ * possible and link training should start as soon as possible after HPD.
+ *
+ * Return: 0 if the training sequence ran successfully, -ve if a error occurred
+ *	   or the training failed
+ */
+static int run_training(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+	int training_state = TS_CLOCK_RECOVERY;
+
+	while (1) {
+		switch (training_state) {
+		case TS_CLOCK_RECOVERY:
+			training_state =
+				training_state_clock_recovery(dev);
+			break;
+		case TS_CHANNEL_EQUALIZATION:
+			training_state =
+				training_state_channel_equalization(dev);
+			break;
+		case TS_ADJUST_LINK_RATE:
+			training_state =
+				training_state_adjust_link_rate(dev);
+			break;
+		case TS_ADJUST_LANE_COUNT:
+			training_state =
+				trainig_state_adjust_lane_count(dev);
+			break;
+		default:
+			break;
+		}
+
+		if (training_state == TS_SUCCESS)
+			break;
+		else if (training_state == TS_FAILURE)
+			return -EIO;
+
+		if (training_state == TS_ADJUST_LINK_RATE ||
+		    training_state == TS_ADJUST_LANE_COUNT) {
+			if (!dp_tx->train_adaptive)
+				return -EIO;
+
+			status = set_training_pattern(dev,
+						      TRAINING_PATTERN_SET_OFF);
+			if (status)
+				return -EIO;
+		}
+	}
+
+	/* Final status check. */
+	status = check_link_status(dev, dp_tx->link_config.lane_count);
+	if (status)
+		return -EIO;
+
+	return 0;
+}
+
+/* Link policy maker */
+
+/**
+ * cfg_main_link_max() - Determine best common capabilities
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Determine the common capabilities between the DisplayPort TX core and the RX
+ * device.
+ *
+ * Return: 0 if the determination succeeded, -ve on error
+ */
+static int cfg_main_link_max(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+
+	if (!is_connected(dev))
+		return -ENODEV;
+
+	/*
+	 * Configure the main link to the maximum common link rate between the
+	 * DisplayPort TX core and the RX device.
+	 */
+	status = set_link_rate(dev, dp_tx->link_config.max_link_rate);
+	if (status)
+		return status;
+
+	/*
+	 * Configure the main link to the maximum common lane count between the
+	 * DisplayPort TX core and the RX device.
+	 */
+	status = set_lane_count(dev, dp_tx->link_config.max_lane_count);
+	if (status)
+		return status;
+
+	return 0;
+}
+
+/**
+ * establish_link() - Establish a link
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Check if the link needs training and run the training sequence if training
+ * is required.
+ *
+ * Return: 0 if the link was established successfully, -ve on error
+ */
+static int establish_link(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int status;
+	int status2;
+	u32 mask;
+
+	reset_dp_phy(dev, PHY_CONFIG_PHY_RESET_MASK);
+
+	/* Disable main link during training. */
+	disable_main_link(dev);
+
+	/* Wait for the PHY to be ready. */
+	mask = phy_status_lanes_ready_mask(dp_tx->max_lane_count);
+	status = wait_phy_ready(dev, mask);
+	if (status)
+		return -EIO;
+
+	/* Train main link. */
+	status = run_training(dev);
+
+	/* Turn off the training pattern and enable scrambler. */
+	status2 = set_training_pattern(dev, TRAINING_PATTERN_SET_OFF);
+	if (status || status2)
+		return -EIO;
+
+	return 0;
+}
+
+/*
+ * Stream policy maker
+ */
+
+/**
+ * cfg_msa_recalculate() - Calculate MSA parameters
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Calculate the following Main Stream Attributes (MSA):
+ * - Transfer unit size
+ * - User pixel width
+ * - Horizontal total clock
+ * - Vertical total clock
+ * - misc_0
+ * - misc_1
+ * - Data per lane
+ * - Average number of bytes per transfer unit
+ * - Number of initial wait cycles
+ *
+ * These values are derived from:
+ * - Bits per color
+ * - Horizontal resolution
+ * - Vertical resolution
+ * - Horizontal blank start
+ * - Vertical blank start
+ * - Pixel clock (in KHz)
+ * - Horizontal sync polarity
+ * - Vertical sync polarity
+ * - Horizontal sync pulse width
+ * - Vertical sync pulse width
+ */
+static void cfg_msa_recalculate(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	u32 video_bw;
+	u32 link_bw;
+	u32 words_per_line;
+	u8 bits_per_pixel;
+	struct main_stream_attributes *msa_config;
+	struct link_config *link_config;
+
+	msa_config = &dp_tx->main_stream_attributes;
+	link_config = &dp_tx->link_config;
+
+	/*
+	 * Set the user pixel width to handle clocks that exceed the
+	 * capabilities of the DisplayPort TX core.
+	 */
+	if (msa_config->override_user_pixel_width == 0) {
+		if (msa_config->pixel_clock_hz > 300000000 &&
+		    link_config->lane_count == LANE_COUNT_SET_4) {
+			msa_config->user_pixel_width = 4;
+		} /*
+		   * Xilinx driver used 75 MHz as a limit here, 150 MHZ should
+		   * be more sane
+		   */
+		else if ((msa_config->pixel_clock_hz > 150000000) &&
+			 (link_config->lane_count != LANE_COUNT_SET_1)) {
+			msa_config->user_pixel_width = 2;
+		} else {
+			msa_config->user_pixel_width = 1;
+		}
+	}
+
+	/* Compute the rest of the MSA values. */
+	msa_config->n_vid = 27 * 1000 * link_config->link_rate;
+
+	/* Miscellaneous attributes. */
+	if (msa_config->bits_per_color == 6)
+		msa_config->misc_0 = MAIN_STREAMX_MISC0_BDC_6BPC;
+	else if (msa_config->bits_per_color == 8)
+		msa_config->misc_0 = MAIN_STREAMX_MISC0_BDC_8BPC;
+	else if (msa_config->bits_per_color == 10)
+		msa_config->misc_0 = MAIN_STREAMX_MISC0_BDC_10BPC;
+	else if (msa_config->bits_per_color == 12)
+		msa_config->misc_0 = MAIN_STREAMX_MISC0_BDC_12BPC;
+	else if (msa_config->bits_per_color == 16)
+		msa_config->misc_0 = MAIN_STREAMX_MISC0_BDC_16BPC;
+
+	msa_config->misc_0 = (msa_config->misc_0 <<
+			      MAIN_STREAMX_MISC0_BDC_SHIFT) |
+			     (msa_config->y_cb_cr_colorimetry <<
+			      MAIN_STREAMX_MISC0_YCBCR_COLORIMETRY_SHIFT) |
+			     (msa_config->dynamic_range <<
+			      MAIN_STREAMX_MISC0_DYNAMIC_RANGE_SHIFT) |
+			     (msa_config->component_format <<
+			      MAIN_STREAMX_MISC0_COMPONENT_FORMAT_SHIFT) |
+			     (msa_config->synchronous_clock_mode);
+
+	msa_config->misc_1 = 0;
+
+	/*
+	 * Determine the number of bits per pixel for the specified color
+	 * component format.
+	 */
+	if (msa_config->component_format ==
+	    MAIN_STREAMX_MISC0_COMPONENT_FORMAT_YCBCR422)
+		/* YCbCr422 color component format. */
+		bits_per_pixel = msa_config->bits_per_color * 2;
+	else
+		/* RGB or YCbCr 4:4:4 color component format. */
+		bits_per_pixel = msa_config->bits_per_color * 3;
+
+	/* Calculate the data per lane. */
+	words_per_line = (msa_config->h_active * bits_per_pixel);
+	if (words_per_line % 16)
+		words_per_line += 16;
+	words_per_line /= 16;
+
+	msa_config->data_per_lane = words_per_line - link_config->lane_count;
+	if (words_per_line % link_config->lane_count)
+		msa_config->data_per_lane += (words_per_line %
+					      link_config->lane_count);
+
+	/*
+	 * Allocate a fixed size for single-stream transport (SST)
+	 * operation.
+	 */
+	msa_config->transfer_unit_size = 64;
+
+	/*
+	 * Calculate the average number of bytes per transfer unit.
+	 * Note: Both the integer and the fractional part is stored in
+	 * avg_bytes_per_tu.
+	 */
+	video_bw = ((msa_config->pixel_clock_hz / 1000) * bits_per_pixel) / 8;
+	link_bw = (link_config->lane_count * link_config->link_rate * 27);
+	msa_config->avg_bytes_per_tu = (video_bw *
+					msa_config->transfer_unit_size) /
+					link_bw;
+
+	/*
+	 * The number of initial wait cycles at the start of a new line
+	 * by the framing logic. This allows enough data to be buffered
+	 * in the input FIFO before video is sent.
+	 */
+	if ((msa_config->avg_bytes_per_tu / 1000) <= 4)
+		msa_config->init_wait = 64;
+	else
+		msa_config->init_wait = msa_config->transfer_unit_size -
+					(msa_config->avg_bytes_per_tu / 1000);
+}
+
+/**
+ * set_line_reset() - Enable/Disable end-of-line-reset
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Disable/enable the end-of-line-reset to the internal video pipe in case of
+ * reduced blanking as required.
+ */
+static void set_line_reset(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	u32 reg_val;
+	u16 h_blank;
+	u16 h_reduced_blank;
+	struct main_stream_attributes *msa_config =
+		&dp_tx->main_stream_attributes;
+
+	h_blank = msa_config->h_total - msa_config->h_active;
+	/* Reduced blanking starts at ceil(0.2 * HTotal). */
+	h_reduced_blank = 2 * msa_config->h_total;
+	if (h_reduced_blank % 10)
+		h_reduced_blank += 10;
+	h_reduced_blank /= 10;
+
+	/* CVT spec. states h_blank is either 80 or 160 for reduced blanking. */
+	reg_val = get_reg(dev, REG_LINE_RESET_DISABLE);
+	if (h_blank < h_reduced_blank &&
+	    (h_blank == 80 || h_blank == 160)) {
+		reg_val |= LINE_RESET_DISABLE_MASK;
+	} else {
+		reg_val &= ~LINE_RESET_DISABLE_MASK;
+	}
+	set_reg(dev, REG_LINE_RESET_DISABLE, reg_val);
+}
+
+/**
+ * clear_msa_values() - Clear MSA values
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Clear the main stream attributes registers of the DisplayPort TX core.
+ */
+static void clear_msa_values(struct udevice *dev)
+{
+	set_reg(dev, REG_MAIN_STREAM_HTOTAL, 0);
+	set_reg(dev, REG_MAIN_STREAM_VTOTAL, 0);
+	set_reg(dev, REG_MAIN_STREAM_POLARITY, 0);
+	set_reg(dev, REG_MAIN_STREAM_HSWIDTH, 0);
+	set_reg(dev, REG_MAIN_STREAM_VSWIDTH, 0);
+	set_reg(dev, REG_MAIN_STREAM_HRES, 0);
+	set_reg(dev, REG_MAIN_STREAM_VRES, 0);
+	set_reg(dev, REG_MAIN_STREAM_HSTART, 0);
+	set_reg(dev, REG_MAIN_STREAM_VSTART, 0);
+	set_reg(dev, REG_MAIN_STREAM_MISC0, 0);
+	set_reg(dev, REG_MAIN_STREAM_MISC1, 0);
+	set_reg(dev, REG_USER_PIXEL_WIDTH, 0);
+	set_reg(dev, REG_USER_DATA_COUNT_PER_LANE, 0);
+	set_reg(dev, REG_M_VID, 0);
+	set_reg(dev, REG_N_VID, 0);
+
+	set_reg(dev, REG_STREAM1, 0);
+	set_reg(dev, REG_TU_SIZE, 0);
+	set_reg(dev, REG_MIN_BYTES_PER_TU, 0);
+	set_reg(dev, REG_FRAC_BYTES_PER_TU, 0);
+	set_reg(dev, REG_INIT_WAIT, 0);
+}
+
+/**
+ * set_msa_values() - Set MSA values
+ * @dev: The LogiCore DP TX device in question
+ *
+ * Set the main stream attributes registers of the DisplayPort TX
+ * core with the values specified in the main stream attributes configuration
+ * structure.
+ */
+static void set_msa_values(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	struct main_stream_attributes *msa_config =
+		&dp_tx->main_stream_attributes;
+
+	printf("       set MSA %u x %u\n", msa_config->h_active,
+	       msa_config->v_active);
+
+	set_reg(dev, REG_MAIN_STREAM_HTOTAL, msa_config->h_total);
+	set_reg(dev, REG_MAIN_STREAM_VTOTAL, msa_config->v_total);
+	set_reg(dev, REG_MAIN_STREAM_POLARITY,
+		msa_config->h_sync_polarity |
+		(msa_config->v_sync_polarity <<
+		 MAIN_STREAMX_POLARITY_VSYNC_POL_SHIFT));
+	set_reg(dev, REG_MAIN_STREAM_HSWIDTH, msa_config->h_sync_width);
+	set_reg(dev, REG_MAIN_STREAM_VSWIDTH, msa_config->v_sync_width);
+	set_reg(dev, REG_MAIN_STREAM_HRES, msa_config->h_active);
+	set_reg(dev, REG_MAIN_STREAM_VRES, msa_config->v_active);
+	set_reg(dev, REG_MAIN_STREAM_HSTART, msa_config->h_start);
+	set_reg(dev, REG_MAIN_STREAM_VSTART, msa_config->v_start);
+	set_reg(dev, REG_MAIN_STREAM_MISC0, msa_config->misc_0);
+	set_reg(dev, REG_MAIN_STREAM_MISC1, msa_config->misc_1);
+	set_reg(dev, REG_USER_PIXEL_WIDTH, msa_config->user_pixel_width);
+
+	set_reg(dev, REG_M_VID, msa_config->pixel_clock_hz / 1000);
+	set_reg(dev, REG_N_VID, msa_config->n_vid);
+	set_reg(dev, REG_USER_DATA_COUNT_PER_LANE, msa_config->data_per_lane);
+
+	set_line_reset(dev);
+
+	set_reg(dev, REG_TU_SIZE, msa_config->transfer_unit_size);
+	set_reg(dev, REG_MIN_BYTES_PER_TU, msa_config->avg_bytes_per_tu / 1000);
+	set_reg(dev, REG_FRAC_BYTES_PER_TU,
+		(msa_config->avg_bytes_per_tu % 1000) * 1024 / 1000);
+	set_reg(dev, REG_INIT_WAIT, msa_config->init_wait);
+}
+
+/*
+ * external API
+ */
+
+/**
+ * logicore_dp_tx_set_msa() - Set given MSA values on device
+ * @dev: The LogiCore DP TX device in question
+ * @msa: The MSA values to set for the device
+ */
+static void logicore_dp_tx_set_msa(struct udevice *dev,
+				   struct logicore_dp_tx_msa *msa)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+
+	memset(&dp_tx->main_stream_attributes, 0,
+	       sizeof(struct main_stream_attributes));
+
+	dp_tx->main_stream_attributes.pixel_clock_hz = msa->pixel_clock_hz;
+	dp_tx->main_stream_attributes.bits_per_color = msa->bits_per_color;
+	dp_tx->main_stream_attributes.h_active = msa->h_active;
+	dp_tx->main_stream_attributes.h_start = msa->h_start;
+	dp_tx->main_stream_attributes.h_sync_polarity = msa->h_sync_polarity;
+	dp_tx->main_stream_attributes.h_sync_width = msa->h_sync_width;
+	dp_tx->main_stream_attributes.h_total = msa->h_total;
+	dp_tx->main_stream_attributes.v_active = msa->v_active;
+	dp_tx->main_stream_attributes.v_start = msa->v_start;
+	dp_tx->main_stream_attributes.v_sync_polarity = msa->v_sync_polarity;
+	dp_tx->main_stream_attributes.v_sync_width = msa->v_sync_width;
+	dp_tx->main_stream_attributes.v_total = msa->v_total;
+	dp_tx->main_stream_attributes.override_user_pixel_width =
+		msa->override_user_pixel_width;
+	dp_tx->main_stream_attributes.user_pixel_width = msa->user_pixel_width;
+	dp_tx->main_stream_attributes.synchronous_clock_mode = 0;
+}
+
+/**
+ * logicore_dp_tx_video_enable() - Enable video output
+ * @dev: The LogiCore DP TX device in question
+ * @msa: The MSA values to set for the device
+ *
+ * Return: 0 if the video was enabled successfully, -ve on error
+ */
+static int logicore_dp_tx_video_enable(struct udevice *dev,
+				       struct logicore_dp_tx_msa *msa)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+	int res;
+	u8 power = 0x01;
+
+	if (!is_connected(dev)) {
+		printf("       no DP sink connected\n");
+		return -EIO;
+	}
+
+	initialize(dev);
+
+	disable_main_link(dev);
+
+	logicore_dp_tx_set_msa(dev, msa);
+
+	get_rx_capabilities(dev);
+
+	printf("       DP sink connected\n");
+	aux_write(dev, DPCD_SET_POWER_DP_PWR_VOLTAGE, 1, &power);
+	set_enhanced_frame_mode(dev, true);
+	cfg_main_link_max(dev);
+	res = establish_link(dev);
+	printf("       establish_link: %s, vs: %d, pe: %d\n",
+	       res ? "failed" : "ok", dp_tx->link_config.vs_level,
+	       dp_tx->link_config.pe_level);
+
+	cfg_msa_recalculate(dev);
+
+	clear_msa_values(dev);
+	set_msa_values(dev);
+
+	enable_main_link(dev);
+
+	return 0;
+}
+
+/*
+ * Driver functions
+ */
+
+static int logicore_dp_tx_enable(struct udevice *dev, int panel_bpp,
+				 const struct display_timing *timing)
+{
+	struct clk pixclock;
+	struct logicore_dp_tx_msa *msa;
+	struct logicore_dp_tx_msa mode_640_480_60 = {
+		.pixel_clock_hz = 25175000,
+		.bits_per_color = 8,
+		.h_active = 640,
+		.h_start = 144,
+		.h_sync_polarity = false,
+		.h_sync_width = 96,
+		.h_total = 800,
+		.v_active = 480,
+		.v_start = 35,
+		.v_sync_polarity = false,
+		.v_sync_width = 2,
+		.v_total = 525,
+		.override_user_pixel_width = false,
+		.user_pixel_width = 0,
+	};
+
+	struct logicore_dp_tx_msa mode_720_400_70 = {
+		.pixel_clock_hz = 28300000,
+		.bits_per_color = 8,
+		.h_active = 720,
+		.h_start = 162,
+		.h_sync_polarity = false,
+		.h_sync_width = 108,
+		.h_total = 900,
+		.v_active = 400,
+		.v_start = 37,
+		.v_sync_polarity = true,
+		.v_sync_width = 2,
+		.v_total = 449,
+		.override_user_pixel_width = false,
+		.user_pixel_width = 0,
+	};
+
+	struct logicore_dp_tx_msa mode_1024_768_60 = {
+		.pixel_clock_hz = 65000000,
+		.bits_per_color = 8,
+		.h_active = 1024,
+		.h_start = 296,
+		.h_sync_polarity = false,
+		.h_sync_width = 136,
+		.h_total = 1344,
+		.v_active = 768,
+		.v_start = 35,
+		.v_sync_polarity = false,
+		.v_sync_width = 2,
+		.v_total = 806,
+		.override_user_pixel_width = false,
+		.user_pixel_width = 0,
+	};
+
+	if (timing->hactive.typ == 1024 && timing->vactive.typ == 768)
+		msa = &mode_1024_768_60;
+	else if (timing->hactive.typ == 720 && timing->vactive.typ == 400)
+		msa = &mode_720_400_70;
+	else
+		msa = &mode_640_480_60;
+
+	if (clk_get_by_index(dev, 0, &pixclock)) {
+		printf("%s: Could not get pixelclock\n", dev->name);
+		return -1;
+	}
+	clk_set_rate(&pixclock, msa->pixel_clock_hz);
+
+	return logicore_dp_tx_video_enable(dev, msa);
+}
+
+static int logicore_dp_tx_probe(struct udevice *dev)
+{
+	struct dp_tx *dp_tx = dev_get_priv(dev);
+
+	dp_tx->s_axi_clk = S_AXI_CLK_DEFAULT;
+	dp_tx->train_adaptive = false;
+	dp_tx->max_link_rate = DPCD_MAX_LINK_RATE_540GBPS;
+	dp_tx->max_lane_count = DPCD_MAX_LANE_COUNT_4;
+
+	dp_tx->base = dev_read_u32_default(dev, "reg", -1);
+
+	return 0;
+}
+
+static const struct dm_display_ops logicore_dp_tx_ops = {
+	.enable = logicore_dp_tx_enable,
+};
+
+static const struct udevice_id logicore_dp_tx_ids[] = {
+	{ .compatible = "gdsys,logicore_dp_tx" },
+	{ /* sentinel */ }
+};
+
+U_BOOT_DRIVER(logicore_dp_tx) = {
+	.name			= "logicore_dp_tx",
+	.id			= UCLASS_DISPLAY,
+	.of_match		= logicore_dp_tx_ids,
+	.probe			= logicore_dp_tx_probe,
+	.priv_auto_alloc_size	= sizeof(struct dp_tx),
+	.ops			= &logicore_dp_tx_ops,
+};
diff --git a/drivers/video/logicore_dp_tx.h b/drivers/video/logicore_dp_tx.h
new file mode 100644
index 00000000000..d8d82b2b13f
--- /dev/null
+++ b/drivers/video/logicore_dp_tx.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * logicore_dp_tx.h
+ *
+ * Driver for XILINX LogiCore DisplayPort v6.1 TX (Source)
+ *
+ * (C) Copyright 2016
+ * Dirk Eibach,  Guntermann & Drunck GmbH, dirk.eibach at gdsys.cc
+ */
+
+#ifndef __GDSYS_LOGICORE_DP_TX_H__
+#define __GDSYS_LOGICORE_DP_TX_H__
+
+/*
+ * struct logicore_dp_tx_msa - Main Stream Attributes (MSA)
+ * @pixel_clock_hz:            The pixel clock of the stream (in Hz)
+ * @bits_per_color:            Number of bits per color component
+ * @h_active:                  Horizontal active resolution (pixels)
+ * @h_start:                   Horizontal blank start (in pixels)
+ * @h_sync_polarity:           Horizontal sync polarity
+ *			       (0 = negative | 1 = positive)
+ * @h_sync_width:              Horizontal sync width (pixels)
+ * @h_total:                   Horizontal total (pixels)
+ * @v_active:                  Vertical active resolution (lines)
+ * @v_start:                   Vertical blank start (in lines).
+ * @v_sync_polarity:           Vertical sync polarity
+ *			       (0 = negative | 1 = positive)
+ * @v_sync_width:              Vertical sync width (lines)
+ * @v_total:                   Vertical total (lines)
+ * @override_user_pixel_width: If true, the value stored for user_pixel_width
+ *			       will be used as the pixel width.
+ * @user_pixel_width:          The width of the user data input port.
+ *
+ * This is a stripped down version of struct main_stream_attributes that
+ * contains only the parameters that are not set by cfg_msa_recalculate()
+ */
+struct logicore_dp_tx_msa {
+	u32 pixel_clock_hz;
+	u32 bits_per_color;
+	u16 h_active;
+	u32 h_start;
+	bool h_sync_polarity;
+	u16 h_sync_width;
+	u16 h_total;
+	u16 v_active;
+	u32 v_start;
+	bool v_sync_polarity;
+	u16 v_sync_width;
+	u16 v_total;
+	bool override_user_pixel_width;
+	u32 user_pixel_width;
+};
+
+#endif /* __GDSYS_LOGICORE_DP_TX_H__ */
diff --git a/drivers/video/logicore_dp_tx_regif.h b/drivers/video/logicore_dp_tx_regif.h
new file mode 100644
index 00000000000..e1affd2b67a
--- /dev/null
+++ b/drivers/video/logicore_dp_tx_regif.h
@@ -0,0 +1,396 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * logicore_dp_tx_regif.h
+ *
+ * Register interface definition for XILINX LogiCore DisplayPort v6.1 TX
+ * (Source) based on Xilinx dp_v3_1 driver sources
+ *
+ * (C) Copyright 2016
+ * Dirk Eibach,  Guntermann & Drunck GmbH, dirk.eibach at gdsys.cc
+ */
+
+#ifndef __GDSYS_LOGICORE_DP_TX_REGIF_H__
+#define __GDSYS_LOGICORE_DP_TX_REGIF_H__
+
+enum {
+	/* link configuration field */
+	REG_LINK_BW_SET =		0x000,
+	REG_LANE_COUNT_SET =		0x004,
+	REG_ENHANCED_FRAME_EN =		0x008,
+	REG_TRAINING_PATTERN_SET =	0x00C,
+	REG_LINK_QUAL_PATTERN_SET =	0x010,
+	REG_SCRAMBLING_DISABLE =	0x014,
+	REG_DOWNSPREAD_CTRL =		0x018,
+	REG_SOFT_RESET =		0x01C,
+};
+
+enum {
+	/* core enables */
+	REG_ENABLE =			0x080,
+	REG_ENABLE_MAIN_STREAM =	0x084,
+	REG_ENABLE_SEC_STREAM =		0x088,
+	REG_FORCE_SCRAMBLER_RESET =	0x0C0,
+	REG_MST_CONFIG =		0x0D0,
+	REG_LINE_RESET_DISABLE =	0x0F0,
+};
+
+enum {
+	/* core ID */
+	REG_VERSION =			0x0F8,
+	REG_CORE_ID =			0x0FC,
+};
+
+enum {
+	/* AUX channel interface */
+	REG_AUX_CMD =			0x100,
+	REG_AUX_WRITE_FIFO =		0x104,
+	REG_AUX_ADDRESS =		0x108,
+	REG_AUX_CLK_DIVIDER =		0x10C,
+	REG_USER_FIFO_OVERFLOW =	0x110,
+	REG_INTERRUPT_SIG_STATE =	0x130,
+	REG_AUX_REPLY_DATA =		0x134,
+	REG_AUX_REPLY_CODE =		0x138,
+	REG_AUX_REPLY_COUNT =		0x13C,
+	REG_INTERRUPT_STATUS =		0x140,
+	REG_INTERRUPT_MASK =		0x144,
+	REG_REPLY_DATA_COUNT =		0x148,
+	REG_REPLY_STATUS =		0x14C,
+	REG_HPD_DURATION =		0x150,
+};
+
+enum {
+	/* main stream attributes for SST / MST STREAM1 */
+	REG_STREAM1_MSA_START =		0x180,
+	REG_MAIN_STREAM_HTOTAL =	0x180,
+	REG_MAIN_STREAM_VTOTAL =	0x184,
+	REG_MAIN_STREAM_POLARITY =	0x188,
+	REG_MAIN_STREAM_HSWIDTH =	0x18C,
+	REG_MAIN_STREAM_VSWIDTH =	0x190,
+	REG_MAIN_STREAM_HRES =		0x194,
+	REG_MAIN_STREAM_VRES =		0x198,
+	REG_MAIN_STREAM_HSTART =	0x19C,
+	REG_MAIN_STREAM_VSTART =	0x1A0,
+	REG_MAIN_STREAM_MISC0 =		0x1A4,
+	REG_MAIN_STREAM_MISC1 =		0x1A8,
+	REG_M_VID =			0x1AC,
+	REG_TU_SIZE =			0x1B0,
+	REG_N_VID =			0x1B4,
+	REG_USER_PIXEL_WIDTH =		0x1B8,
+	REG_USER_DATA_COUNT_PER_LANE =	0x1BC,
+	REG_MAIN_STREAM_INTERLACED =	0x1C0,
+	REG_MIN_BYTES_PER_TU =		0x1C4,
+	REG_FRAC_BYTES_PER_TU =		0x1C8,
+	REG_INIT_WAIT =			0x1CC,
+	REG_STREAM1 =			0x1D0,
+	REG_STREAM2 =			0x1D4,
+	REG_STREAM3 =			0x1D8,
+	REG_STREAM4 =			0x1DC,
+};
+
+enum {
+	/* PHY configuration status */
+	REG_PHY_CONFIG =		0x200,
+	REG_PHY_VOLTAGE_DIFF_LANE_0 =	0x220,
+	REG_PHY_VOLTAGE_DIFF_LANE_1 =	0x224,
+	REG_PHY_VOLTAGE_DIFF_LANE_2 =	0x228,
+	REG_PHY_VOLTAGE_DIFF_LANE_3 =	0x22C,
+	REG_PHY_TRANSMIT_PRBS7 =	0x230,
+	REG_PHY_CLOCK_SELECT =		0x234,
+	REG_PHY_POWER_DOWN =		0x238,
+	REG_PHY_PRECURSOR_LANE_0 =	0x23C,
+	REG_PHY_PRECURSOR_LANE_1 =	0x240,
+	REG_PHY_PRECURSOR_LANE_2 =	0x244,
+	REG_PHY_PRECURSOR_LANE_3 =	0x248,
+	REG_PHY_POSTCURSOR_LANE_0 =	0x24C,
+	REG_PHY_POSTCURSOR_LANE_1 =	0x250,
+	REG_PHY_POSTCURSOR_LANE_2 =	0x254,
+	REG_PHY_POSTCURSOR_LANE_3 =	0x258,
+	REG_PHY_STATUS =		0x280,
+	REG_GT_DRP_COMMAND =		0x2A0,
+	REG_GT_DRP_READ_DATA =		0x2A4,
+	REG_GT_DRP_CHANNEL_STATUS =	0x2A8,
+};
+
+enum {
+	/* DisplayPort audio */
+	REG_AUDIO_CONTROL =		0x300,
+	REG_AUDIO_CHANNELS =		0x304,
+	REG_AUDIO_INFO_DATA =		0x308,
+	REG_AUDIO_MAUD =		0x328,
+	REG_AUDIO_NAUD =		0x32C,
+	REG_AUDIO_EXT_DATA =		0x330,
+};
+
+enum {
+	/* HDCP */
+	REG_HDCP_ENABLE =		0x400,
+};
+
+enum {
+	/* main stream attributes for MST STREAM2, 3, and 4 */
+	REG_STREAM2_MSA_START =		0x500,
+	REG_STREAM3_MSA_START =		0x550,
+	REG_STREAM4_MSA_START =		0x5A0,
+
+	REG_VC_PAYLOAD_BUFFER_ADDR =	0x800,
+};
+
+enum {
+	LINK_BW_SET_162GBPS = 0x06,
+	LINK_BW_SET_270GBPS = 0x0A,
+	LINK_BW_SET_540GBPS = 0x14,
+};
+
+enum {
+	LANE_COUNT_SET_1 = 0x1,
+	LANE_COUNT_SET_2 = 0x2,
+	LANE_COUNT_SET_4 = 0x4,
+};
+
+enum {
+	TRAINING_PATTERN_SET_OFF =	0x0,
+	/* training pattern 1 used for clock recovery */
+	TRAINING_PATTERN_SET_TP1 =	0x1,
+	/* training pattern 2 used for channel equalization */
+	TRAINING_PATTERN_SET_TP2 =	0x2,
+	/*
+	 * training pattern 3 used for channel equalization for cores with DP
+	 * v1.2
+	 */
+	TRAINING_PATTERN_SET_TP3 =	0x3,
+};
+
+enum {
+	LINK_QUAL_PATTERN_SET_OFF =		0x0,
+	/* D10.2 unscrambled test pattern transmitted */
+	LINK_QUAL_PATTERN_SET_D102_TEST =	0x1,
+	/* symbol error rate measurement pattern transmitted */
+	LINK_QUAL_PATTERN_SET_SER_MES =		0x2,
+	/* pseudo random bit sequence 7 transmitted */
+	LINK_QUAL_PATTERN_SET_PRBS7 =		0x3,
+};
+
+enum {
+	SOFT_RESET_VIDEO_STREAM1_MASK =		0x00000001,
+	SOFT_RESET_VIDEO_STREAM2_MASK =		0x00000002,
+	SOFT_RESET_VIDEO_STREAM3_MASK =		0x00000004,
+	SOFT_RESET_VIDEO_STREAM4_MASK =		0x00000008,
+	SOFT_RESET_AUX_MASK =			0x00000080,
+	SOFT_RESET_VIDEO_STREAM_ALL_MASK =	0x0000000F,
+};
+
+enum {
+	MST_CONFIG_MST_EN_MASK =	0x00000001,
+};
+
+enum {
+	LINE_RESET_DISABLE_MASK =	0x1,
+};
+
+#define AUX_CMD_NBYTES_TRANSFER_MASK	0x0000000F
+
+#define AUX_CMD_SHIFT		8
+#define AUX_CMD_MASK			0x00000F00
+enum {
+	AUX_CMD_I2C_WRITE =		0x0,
+	AUX_CMD_I2C_READ =		0x1,
+	AUX_CMD_I2C_WRITE_STATUS =	0x2,
+	AUX_CMD_I2C_WRITE_MOT =		0x4,
+	AUX_CMD_I2C_READ_MOT =		0x5,
+	AUX_CMD_I2C_WRITE_STATUS_MOT =	0x6,
+	AUX_CMD_WRITE =			0x8,
+	AUX_CMD_READ =			0x9,
+};
+
+#define AUX_CLK_DIVIDER_VAL_MASK		0x00FF
+
+#define AUX_CLK_DIVIDER_AUX_SIG_WIDTH_FILT_SHIFT 8
+#define AUX_CLK_DIVIDER_AUX_SIG_WIDTH_FILT_MASK 0xFF00
+
+enum {
+	INTERRUPT_SIG_STATE_HPD_STATE_MASK =		0x00000001,
+	INTERRUPT_SIG_STATE_REQUEST_STATE_MASK =	0x00000002,
+	INTERRUPT_SIG_STATE_REPLY_STATE_MASK =		0x00000004,
+	INTERRUPT_SIG_STATE_REPLY_TIMEOUT_MASK =	0x00000008,
+};
+
+enum {
+	AUX_REPLY_CODE_ACK =		0x0,
+	AUX_REPLY_CODE_I2C_ACK =	0x0,
+	AUX_REPLY_CODE_NACK =		0x1,
+	AUX_REPLY_CODE_DEFER =		0x2,
+	AUX_REPLY_CODE_I2C_NACK =	0x4,
+	AUX_REPLY_CODE_I2C_DEFER =	0x8,
+};
+
+enum {
+	INTERRUPT_STATUS_HPD_IRQ_MASK =			0x00000001,
+	INTERRUPT_STATUS_HPD_EVENT_MASK =		0x00000002,
+	INTERRUPT_STATUS_REPLY_RECEIVED_MASK =		0x00000004,
+	INTERRUPT_STATUS_REPLY_TIMEOUT_MASK =		0x00000008,
+	INTERRUPT_STATUS_HPD_PULSE_DETECTED_MASK =	0x00000010,
+	INTERRUPT_STATUS_EXT_PKT_TXD_MASK =		0x00000020,
+};
+
+enum {
+	INTERRUPT_MASK_HPD_IRQ_MASK =			0x00000001,
+	INTERRUPT_MASK_HPD_EVENT_MASK =			0x00000002,
+	INTERRUPT_MASK_REPLY_RECEIVED_MASK =		0x00000004,
+	INTERRUPT_MASK_REPLY_TIMEOUT_MASK =		0x00000008,
+	INTERRUPT_MASK_HPD_PULSE_DETECTED_MASK =	0x00000010,
+	INTERRUPT_MASK_EXT_PKT_TXD_MASK =		0x00000020,
+};
+
+#define REPLY_STATUS_REPLY_STATUS_STATE_SHIFT 4
+#define REPLY_STATUS_REPLY_STATUS_STATE_MASK	0x00000FF0
+enum {
+	REPLY_STATUS_REPLY_RECEIVED_MASK =	0x00000001,
+	REPLY_STATUS_REPLY_IN_PROGRESS_MASK =	0x00000002,
+	REPLY_STATUS_REQUEST_IN_PROGRESS_MASK =	0x00000004,
+	REPLY_STATUS_REPLY_ERROR_MASK =		0x00000008,
+};
+
+#define MAIN_STREAMX_POLARITY_VSYNC_POL_SHIFT 1
+enum {
+	MAIN_STREAMX_POLARITY_HSYNC_POL_MASK =	0x00000001,
+	MAIN_STREAMX_POLARITY_VSYNC_POL_MASK =	0x00000002,
+};
+
+enum {
+	MAIN_STREAMX_MISC0_SYNC_CLK_MASK = 0x00000001,
+};
+
+#define MAIN_STREAMX_MISC0_COMPONENT_FORMAT_SHIFT 1
+#define MAIN_STREAMX_MISC0_COMPONENT_FORMAT_MASK 0x00000006
+enum {
+	MAIN_STREAMX_MISC0_COMPONENT_FORMAT_RGB =	0x0,
+	MAIN_STREAMX_MISC0_COMPONENT_FORMAT_YCBCR422 =	0x1,
+	MAIN_STREAMX_MISC0_COMPONENT_FORMAT_YCBCR444 =	0x2,
+};
+
+#define MAIN_STREAMX_MISC0_DYNAMIC_RANGE_SHIFT 3
+#define MAIN_STREAMX_MISC0_DYNAMIC_RANGE_MASK 0x00000008
+
+#define MAIN_STREAMX_MISC0_YCBCR_COLORIMETRY_SHIFT 4
+#define MAIN_STREAMX_MISC0_YCBCR_COLORIMETRY_MASK 0x00000010
+
+#define MAIN_STREAMX_MISC0_BDC_SHIFT 5
+#define MAIN_STREAMX_MISC0_BDC_MASK 0x000000E0
+enum {
+	MAIN_STREAMX_MISC0_BDC_6BPC =	0x0,
+	MAIN_STREAMX_MISC0_BDC_8BPC =	0x1,
+	MAIN_STREAMX_MISC0_BDC_10BPC =	0x2,
+	MAIN_STREAMX_MISC0_BDC_12BPC =	0x3,
+	MAIN_STREAMX_MISC0_BDC_16BPC =	0x4,
+};
+
+enum {
+	PHY_CONFIG_PHY_RESET_ENABLE_MASK =		0x0000000,
+	PHY_CONFIG_PHY_RESET_MASK =			0x0000001,
+	PHY_CONFIG_GTTX_RESET_MASK =			0x0000002,
+	PHY_CONFIG_GT_ALL_RESET_MASK =			0x0000003,
+	PHY_CONFIG_TX_PHY_PMA_RESET_MASK =		0x0000100,
+	PHY_CONFIG_TX_PHY_PCS_RESET_MASK =		0x0000200,
+	PHY_CONFIG_TX_PHY_POLARITY_MASK =		0x0000800,
+	PHY_CONFIG_TX_PHY_PRBSFORCEERR_MASK =		0x0001000,
+	PHY_CONFIG_TX_PHY_POLARITY_IND_LANE_MASK =	0x0010000,
+	PHY_CONFIG_TX_PHY_POLARITY_LANE0_MASK =		0x0020000,
+	PHY_CONFIG_TX_PHY_POLARITY_LANE1_MASK =		0x0040000,
+	PHY_CONFIG_TX_PHY_POLARITY_LANE2_MASK =		0x0080000,
+	PHY_CONFIG_TX_PHY_POLARITY_LANE3_MASK =		0x0100000,
+	PHY_CONFIG_TX_PHY_8B10BEN_MASK =		0x0200000,
+};
+
+#define PHY_CONFIG_TX_PHY_LOOPBACK_SHIFT 13
+#define PHY_CONFIG_TX_PHY_LOOPBACK_MASK 0x000E000
+
+enum {
+	PHY_CLOCK_SELECT_162GBPS =	0x1,
+	PHY_CLOCK_SELECT_270GBPS =	0x3,
+	PHY_CLOCK_SELECT_540GBPS =	0x5,
+};
+
+enum {
+	VS_LEVEL_0	= 0x2,
+	VS_LEVEL_1	= 0x5,
+	VS_LEVEL_2	= 0x8,
+	VS_LEVEL_3	= 0xF,
+	VS_LEVEL_OFFSET	= 0x4,
+};
+
+enum {
+	PE_LEVEL_0 =	0x00,
+	PE_LEVEL_1 =	0x0E,
+	PE_LEVEL_2 =	0x14,
+	PE_LEVEL_3 =	0x1B,
+};
+
+enum {
+	PHY_STATUS_RESET_LANE_2_3_DONE_SHIFT =		2,
+	PHY_STATUS_TX_ERROR_LANE_0_SHIFT =		18,
+	PHY_STATUS_TX_BUFFER_STATUS_LANE_1_SHIFT =	20,
+	PHY_STATUS_TX_ERROR_LANE_1_SHIFT =		22,
+	PHY_STATUS_TX_BUFFER_STATUS_LANE_0_SHIFT =	16,
+	PHY_STATUS_TX_BUFFER_STATUS_LANE_2_SHIFT =	24,
+	PHY_STATUS_TX_ERROR_LANE_2_SHIFT =		26,
+	PHY_STATUS_TX_BUFFER_STATUS_LANE_3_SHIFT =	28,
+	PHY_STATUS_TX_ERROR_LANE_3_SHIFT =		30,
+};
+
+enum {
+	PHY_STATUS_RESET_LANE_0_DONE_MASK =		0x00000001,
+	PHY_STATUS_RESET_LANE_1_DONE_MASK =		0x00000002,
+	PHY_STATUS_RESET_LANE_2_3_DONE_MASK =		0x0000000C,
+	PHY_STATUS_PLL_LANE0_1_LOCK_MASK =		0x00000010,
+	PHY_STATUS_PLL_LANE2_3_LOCK_MASK =		0x00000020,
+	PHY_STATUS_PLL_FABRIC_LOCK_MASK =		0x00000040,
+	PHY_STATUS_TX_BUFFER_STATUS_LANE_0_MASK =	0x00030000,
+	PHY_STATUS_TX_ERROR_LANE_0_MASK =		0x000C0000,
+	PHY_STATUS_TX_BUFFER_STATUS_LANE_1_MASK =	0x00300000,
+	PHY_STATUS_TX_ERROR_LANE_1_MASK =		0x00C00000,
+	PHY_STATUS_TX_BUFFER_STATUS_LANE_2_MASK =	0x03000000,
+	PHY_STATUS_TX_ERROR_LANE_2_MASK =		0x0C000000,
+	PHY_STATUS_TX_BUFFER_STATUS_LANE_3_MASK =	0x30000000,
+	PHY_STATUS_TX_ERROR_LANE_3_MASK =		0xC0000000,
+};
+
+#define PHY_STATUS_LANE_0_READY_MASK \
+	(PHY_STATUS_RESET_LANE_0_DONE_MASK | \
+	PHY_STATUS_PLL_LANE0_1_LOCK_MASK)
+#define PHY_STATUS_LANES_0_1_READY_MASK \
+	(PHY_STATUS_LANE_0_READY_MASK | \
+	PHY_STATUS_RESET_LANE_1_DONE_MASK)
+/*
+ * PHY_STATUS_ALL_LANES_READY_MASK seems to be missing lanes 0 and 1 in
+ * Xilinx dp_v3_0 implementation
+ */
+#define PHY_STATUS_ALL_LANES_READY_MASK \
+	(PHY_STATUS_LANES_0_1_READY_MASK | \
+	PHY_STATUS_RESET_LANE_2_3_DONE_MASK | \
+	PHY_STATUS_PLL_LANE2_3_LOCK_MASK)
+
+/**
+ * phy_status_lanes_ready_mask() - Generate phy status ready mask
+ * @lane_count: Number of lanes for which to generate a mask
+ *
+ * Return: The generated phy status ready mask
+ */
+static inline u32 phy_status_lanes_ready_mask(u8 lane_count)
+{
+	if (lane_count > 2)
+		return PHY_STATUS_ALL_LANES_READY_MASK;
+
+	if (lane_count == 2)
+		return PHY_STATUS_LANES_0_1_READY_MASK;
+
+	return PHY_STATUS_LANE_0_READY_MASK;
+}
+
+#define GT_DRP_COMMAND_DRP_ADDR_MASK	0x000F
+#define GT_DRP_COMMAND_DRP_RW_CMD_MASK	0x0080
+#define GT_DRP_COMMAND_DRP_W_DATA_SHIFT 16
+#define GT_DRP_COMMAND_DRP_W_DATA_MASK	0xFF00
+
+#define HDCP_ENABLE_BYPASS_DISABLE_MASK	0x0001
+
+#endif /* __GDSYS_LOGICORE_DP_TX_REGIF_H__ */
--
2.11.0

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

* [U-Boot] [PATCH v3 1/8] drivers: Add AXI uclass
  2018-08-09 12:51 [U-Boot] [PATCH v3 1/8] drivers: Add AXI uclass Mario Six
                   ` (6 preceding siblings ...)
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 8/8] video_display: Add Xilinx LogiCore DP TX Mario Six
@ 2018-08-11 14:13 ` Anatolij Gustschin
  7 siblings, 0 replies; 11+ messages in thread
From: Anatolij Gustschin @ 2018-08-11 14:13 UTC (permalink / raw)
  To: u-boot

Hi Mario,

On Thu,  9 Aug 2018 14:51:16 +0200
Mario Six mario.six at gdsys.cc wrote:

> Add a uclass for AXI (Advanced eXtensible Interface) busses, and a
> driver for the gdsys IHS AXI bus on IHS FPGAs.
> 
> Signed-off-by: Mario Six <mario.six@gdsys.cc>
> Reviewed-by: Simon Glass <sjg@chromium.org>
> 
> ---
> 
> v2 -> v3:
> * Fixed the AXI uclass comment ('busses' -> 'bus')
> * Fixed style violations
> 
> v1 -> v2:
> * Spelled out all abbreviations in the Kconfig help
> * Split commit into uclass addition and driver addition
> 
> ---
>  drivers/Kconfig          |  2 ++
>  drivers/Makefile         |  1 +
>  drivers/axi/Kconfig      | 13 ++++++++
>  drivers/axi/Makefile     |  8 +++++
>  drivers/axi/axi-uclass.c | 39 ++++++++++++++++++++++++
>  include/axi.h            | 78 ++++++++++++++++++++++++++++++++++++++++++++++++
>  include/dm/uclass-id.h   |  1 +
>  7 files changed, 142 insertions(+)
>  create mode 100644 drivers/axi/Kconfig
>  create mode 100644 drivers/axi/Makefile
>  create mode 100644 drivers/axi/axi-uclass.c
>  create mode 100644 include/axi.h

Series applied to u-boot-video/master, thanks!

--
Anatolij

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

* [U-Boot] [PATCH v3 8/8] video_display: Add Xilinx LogiCore DP TX
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 8/8] video_display: Add Xilinx LogiCore DP TX Mario Six
@ 2018-08-17 12:49   ` Simon Glass
  0 siblings, 0 replies; 11+ messages in thread
From: Simon Glass @ 2018-08-17 12:49 UTC (permalink / raw)
  To: u-boot

On 9 August 2018 at 06:51, Mario Six <mario.six@gdsys.cc> wrote:
> Add a driver for the Xilinx LogiCORE DisplayPort IP core, which is a
> pure DP transmitter core for Xiling FPGA (no display capabilities).
>
> Signed-off-by: Mario Six <mario.six@gdsys.cc>
>
> ---
>
> v2 -> v3:
> * Fix style errors
> * Added full documentation
>
> v1 -> v2:
> * Switch to display_enable
> * Mentioned that the LogiCORE has no display capabilities
>
> ---
>  drivers/video/Kconfig                |   11 +
>  drivers/video/Makefile               |    1 +
>  drivers/video/logicore_dp_dpcd.h     |  341 +++++
>  drivers/video/logicore_dp_tx.c       | 2296 ++++++++++++++++++++++++++++++++++
>  drivers/video/logicore_dp_tx.h       |   54 +
>  drivers/video/logicore_dp_tx_regif.h |  396 ++++++
>  6 files changed, 3099 insertions(+)
>  create mode 100644 drivers/video/logicore_dp_dpcd.h
>  create mode 100644 drivers/video/logicore_dp_tx.c
>  create mode 100644 drivers/video/logicore_dp_tx.h
>  create mode 100644 drivers/video/logicore_dp_tx_regif.h

Reviewed-by: Simon Glass <sjg@chromium.org>

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

* [U-Boot] [PATCH v3 7/8] video: Sort Makefile entries
  2018-08-09 12:51 ` [U-Boot] [PATCH v3 7/8] video: Sort Makefile entries Mario Six
@ 2018-08-17 12:49   ` Simon Glass
  0 siblings, 0 replies; 11+ messages in thread
From: Simon Glass @ 2018-08-17 12:49 UTC (permalink / raw)
  To: u-boot

On 9 August 2018 at 06:51, Mario Six <mario.six@gdsys.cc> wrote:
> The entries of Makefiles should be sorted, which is not the case in the
> video driver Makefile.
>
> Sort the entries alphabetically as far as this makes sense.
>
> Signed-off-by: Mario Six <mario.six@gdsys.cc>
>
> ---
>
> v2 -> v3:
> New in v3
>
> ---
>  drivers/video/Makefile | 42 +++++++++++++++++++++---------------------
>  1 file changed, 21 insertions(+), 21 deletions(-)
>

Reviewed-by: Simon Glass <sjg@chromium.org>

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

end of thread, other threads:[~2018-08-17 12:49 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-08-09 12:51 [U-Boot] [PATCH v3 1/8] drivers: Add AXI uclass Mario Six
2018-08-09 12:51 ` [U-Boot] [PATCH v3 2/8] axi: Add ihs_axi driver Mario Six
2018-08-09 12:51 ` [U-Boot] [PATCH v3 3/8] axi: Add AXI sandbox driver and simple emulator Mario Six
2018-08-09 12:51 ` [U-Boot] [PATCH v3 4/8] sandbox: Add and build AXI bus and device Mario Six
2018-08-09 12:51 ` [U-Boot] [PATCH v3 5/8] test: Add AXI test Mario Six
2018-08-09 12:51 ` [U-Boot] [PATCH v3 6/8] cmd: Add axi command Mario Six
2018-08-09 12:51 ` [U-Boot] [PATCH v3 7/8] video: Sort Makefile entries Mario Six
2018-08-17 12:49   ` Simon Glass
2018-08-09 12:51 ` [U-Boot] [PATCH v3 8/8] video_display: Add Xilinx LogiCore DP TX Mario Six
2018-08-17 12:49   ` Simon Glass
2018-08-11 14:13 ` [U-Boot] [PATCH v3 1/8] drivers: Add AXI uclass Anatolij Gustschin

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