[1/2,v5] drm/pl111: Support the Versatile Express

Message ID 20180502134719.8388-1-linus.walleij@linaro.org
State New
Headers show
Series
  • [1/2,v5] drm/pl111: Support the Versatile Express
Related show

Commit Message

Linus Walleij May 2, 2018, 1:47 p.m.
The Versatile Express uses a special configuration controller
deeply embedded in the system motherboard FPGA to multiplex the
two to three (!) display controller instances out to the single
SiI9022 bridge.

Set up an extra file with the logic to probe to the FPGA mux
register on the system controller bus, then parse the device
tree to see if there is a CLCD or HDLCD instance on the core
tile (also known as the daughterboard) by looking in the
root of the device tree for compatible nodes.

- If there is a HDLCD on the core tile, and there is a driver
  for it, we exit probe and deactivate the motherboard CLCD.
  We do not touch the DVI mux in this case, to make sure we
  don't break HDLCD.

- If there is a CLCD on both the motherboard and the core tile
  (only the CA9 has this) the core tile CLCD takes precedence
  and get muxed to the DVI connector.

- Only if there is no working graphics on the core tile, the
  motherboard CLCD is probed and muxed to the DVI connector.

Core tile graphics should always take precedence as it can
address all memory and is also faster, however the motherboard
CLCD is good to have around for diagnostics and testing.

It is possible to test the motherboard CLCD by setting the
status = "disabled" property on the core tile CLCD or
HDLCD.

Scale down the Versatile Express to 16BPP so we can support a
1024x768 display despite the bus bandwidth restrictions on this
platform. (The motherboard CLCD supports slightly lower
resolution.)

Cc: Liviu Dudau <liviu.dudau@arm.com>
Cc: Pawel Moll <pawel.moll@arm.com>
Acked-by: Eric Anholt <eric@anholt.net>
Tested-by: Robin Murphy <robin.murphy@arm.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v4->v5:
- Collect Robin's Tested-by
- Switch to builtin_platform_driver() for the muxfpga as this
  file is always built as "-y" for the arch and we need to
  avoid module collisions with the main PL111 driver.
ChangeLog v3->v4:
- Add a missing platform_device_put()
- Simplify code with for_each_available_child_of_node()
- Collect Eric's ACK.
ChangeLog v2->v3:
- Rewrite CLCD detection and mux priority logic, look in
  the device tree root for core tile graphics.
ChangeLog v1->v2:
- No changes just reposting rebased on mainline changes.
---
 drivers/gpu/drm/pl111/Makefile          |   1 +
 drivers/gpu/drm/pl111/pl111_versatile.c |  49 +++++++++-
 drivers/gpu/drm/pl111/pl111_vexpress.c  | 125 ++++++++++++++++++++++++
 drivers/gpu/drm/pl111/pl111_vexpress.h  |  22 +++++
 4 files changed, 196 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/pl111/pl111_vexpress.c
 create mode 100644 drivers/gpu/drm/pl111/pl111_vexpress.h

Comments

Daniel Vetter May 3, 2018, 9:41 a.m. | #1
On Wed, May 02, 2018 at 03:47:18PM +0200, Linus Walleij wrote:
> The Versatile Express uses a special configuration controller
> deeply embedded in the system motherboard FPGA to multiplex the
> two to three (!) display controller instances out to the single
> SiI9022 bridge.
> 
> Set up an extra file with the logic to probe to the FPGA mux
> register on the system controller bus, then parse the device
> tree to see if there is a CLCD or HDLCD instance on the core
> tile (also known as the daughterboard) by looking in the
> root of the device tree for compatible nodes.
> 
> - If there is a HDLCD on the core tile, and there is a driver
>   for it, we exit probe and deactivate the motherboard CLCD.
>   We do not touch the DVI mux in this case, to make sure we
>   don't break HDLCD.
> 
> - If there is a CLCD on both the motherboard and the core tile
>   (only the CA9 has this) the core tile CLCD takes precedence
>   and get muxed to the DVI connector.
> 
> - Only if there is no working graphics on the core tile, the
>   motherboard CLCD is probed and muxed to the DVI connector.
> 
> Core tile graphics should always take precedence as it can
> address all memory and is also faster, however the motherboard
> CLCD is good to have around for diagnostics and testing.
> 
> It is possible to test the motherboard CLCD by setting the
> status = "disabled" property on the core tile CLCD or
> HDLCD.
> 
> Scale down the Versatile Express to 16BPP so we can support a
> 1024x768 display despite the bus bandwidth restrictions on this
> platform. (The motherboard CLCD supports slightly lower
> resolution.)
> 
> Cc: Liviu Dudau <liviu.dudau@arm.com>
> Cc: Pawel Moll <pawel.moll@arm.com>
> Acked-by: Eric Anholt <eric@anholt.net>
> Tested-by: Robin Murphy <robin.murphy@arm.com>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---
> ChangeLog v4->v5:
> - Collect Robin's Tested-by
> - Switch to builtin_platform_driver() for the muxfpga as this
>   file is always built as "-y" for the arch and we need to
>   avoid module collisions with the main PL111 driver.
> ChangeLog v3->v4:
> - Add a missing platform_device_put()
> - Simplify code with for_each_available_child_of_node()
> - Collect Eric's ACK.
> ChangeLog v2->v3:
> - Rewrite CLCD detection and mux priority logic, look in
>   the device tree root for core tile graphics.
> ChangeLog v1->v2:
> - No changes just reposting rebased on mainline changes.
> ---
>  drivers/gpu/drm/pl111/Makefile          |   1 +
>  drivers/gpu/drm/pl111/pl111_versatile.c |  49 +++++++++-
>  drivers/gpu/drm/pl111/pl111_vexpress.c  | 125 ++++++++++++++++++++++++
>  drivers/gpu/drm/pl111/pl111_vexpress.h  |  22 +++++
>  4 files changed, 196 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/pl111/pl111_vexpress.c
>  create mode 100644 drivers/gpu/drm/pl111/pl111_vexpress.h
> 
> diff --git a/drivers/gpu/drm/pl111/Makefile b/drivers/gpu/drm/pl111/Makefile
> index 9c5e8dba8ac6..19a8189dc54f 100644
> --- a/drivers/gpu/drm/pl111/Makefile
> +++ b/drivers/gpu/drm/pl111/Makefile
> @@ -3,6 +3,7 @@ pl111_drm-y +=	pl111_display.o \
>  		pl111_versatile.o \
>  		pl111_drv.o
>  
> +pl111_drm-$(CONFIG_ARCH_VEXPRESS) += pl111_vexpress.o
>  pl111_drm-$(CONFIG_DEBUG_FS) += pl111_debugfs.o
>  
>  obj-$(CONFIG_DRM_PL111) += pl111_drm.o
> diff --git a/drivers/gpu/drm/pl111/pl111_versatile.c b/drivers/gpu/drm/pl111/pl111_versatile.c
> index 9302f516045e..78ddf8534fd2 100644
> --- a/drivers/gpu/drm/pl111/pl111_versatile.c
> +++ b/drivers/gpu/drm/pl111/pl111_versatile.c
> @@ -1,12 +1,14 @@
>  #include <linux/amba/clcd-regs.h>
>  #include <linux/device.h>
>  #include <linux/of.h>
> +#include <linux/of_platform.h>
>  #include <linux/regmap.h>
>  #include <linux/mfd/syscon.h>
>  #include <linux/bitops.h>
>  #include <linux/module.h>
>  #include <drm/drmP.h>
>  #include "pl111_versatile.h"
> +#include "pl111_vexpress.h"
>  #include "pl111_drm.h"
>  
>  static struct regmap *versatile_syscon_map;
> @@ -22,6 +24,7 @@ enum versatile_clcd {
>  	REALVIEW_CLCD_PB11MP,
>  	REALVIEW_CLCD_PBA8,
>  	REALVIEW_CLCD_PBX,
> +	VEXPRESS_CLCD_V2M,
>  };
>  
>  static const struct of_device_id versatile_clcd_of_match[] = {
> @@ -53,6 +56,10 @@ static const struct of_device_id versatile_clcd_of_match[] = {
>  		.compatible = "arm,realview-pbx-syscon",
>  		.data = (void *)REALVIEW_CLCD_PBX,
>  	},
> +	{
> +		.compatible = "arm,vexpress-muxfpga",
> +		.data = (void *)VEXPRESS_CLCD_V2M,
> +	},
>  	{},
>  };
>  
> @@ -286,12 +293,26 @@ static const struct pl111_variant_data pl111_realview = {
>  	.fb_bpp = 16,
>  };
>  
> +/*
> + * Versatile Express PL111 variant, again we just push the maximum
> + * BPP to 16 to be able to get 1024x768 without saturating the memory
> + * bus. The clockdivider also seems broken on the Versatile Express.
> + */
> +static const struct pl111_variant_data pl111_vexpress = {
> +	.name = "PL111 Versatile Express",
> +	.formats = pl111_realview_pixel_formats,
> +	.nformats = ARRAY_SIZE(pl111_realview_pixel_formats),
> +	.fb_bpp = 16,
> +	.broken_clockdivider = true,
> +};
> +
>  int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv)
>  {
>  	const struct of_device_id *clcd_id;
>  	enum versatile_clcd versatile_clcd_type;
>  	struct device_node *np;
>  	struct regmap *map;
> +	int ret;
>  
>  	np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match,
>  					     &clcd_id);
> @@ -301,7 +322,26 @@ int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv)
>  	}
>  	versatile_clcd_type = (enum versatile_clcd)clcd_id->data;
>  
> -	map = syscon_node_to_regmap(np);
> +	/* Versatile Express special handling */
> +	if (versatile_clcd_type == VEXPRESS_CLCD_V2M) {
> +		struct platform_device *pdev;
> +
> +		/* Call into deep Vexpress configuration API */
> +		pdev = of_find_device_by_node(np);
> +		if (!pdev) {
> +			dev_err(dev, "can't find the sysreg device, deferring\n");
> +			return -EPROBE_DEFER;
> +		}
> +		map = dev_get_drvdata(&pdev->dev);
> +		if (!map) {
> +			dev_err(dev, "sysreg has not yet probed\n");
> +			platform_device_put(pdev);
> +			return -EPROBE_DEFER;
> +		}
> +	} else {
> +		map = syscon_node_to_regmap(np);
> +	}
> +
>  	if (IS_ERR(map)) {
>  		dev_err(dev, "no Versatile syscon regmap\n");
>  		return PTR_ERR(map);
> @@ -340,6 +380,13 @@ int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv)
>  		priv->variant_display_disable = pl111_realview_clcd_disable;
>  		dev_info(dev, "set up callbacks for RealView PL111\n");
>  		break;
> +	case VEXPRESS_CLCD_V2M:
> +		priv->variant = &pl111_vexpress;
> +		dev_info(dev, "initializing Versatile Express PL111\n");
> +		ret = pl111_vexpress_clcd_init(dev, priv, map);
> +		if (ret)
> +			return ret;
> +		break;
>  	default:
>  		dev_info(dev, "unknown Versatile system controller\n");
>  		break;
> diff --git a/drivers/gpu/drm/pl111/pl111_vexpress.c b/drivers/gpu/drm/pl111/pl111_vexpress.c
> new file mode 100644
> index 000000000000..c9fee625faf1
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_vexpress.c
> @@ -0,0 +1,125 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Versatile Express PL111 handling
> + * Copyright (C) 2018 Linus Walleij
> + *
> + * This module binds to the "arm,vexpress-muxfpga" device on the
> + * Versatile Express configuration bus and sets up which CLCD instance
> + * gets muxed out on the DVI bridge.
> + */
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/vexpress.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include "pl111_drm.h"
> +#include "pl111_vexpress.h"
> +
> +#define VEXPRESS_FPGAMUX_MOTHERBOARD		0x00
> +#define VEXPRESS_FPGAMUX_DAUGHTERBOARD_1	0x01
> +#define VEXPRESS_FPGAMUX_DAUGHTERBOARD_2	0x02
> +
> +int pl111_vexpress_clcd_init(struct device *dev,
> +			     struct pl111_drm_dev_private *priv,
> +			     struct regmap *map)
> +{
> +	struct device_node *root;
> +	struct device_node *child;
> +	struct device_node *ct_clcd = NULL;
> +	bool has_coretile_clcd = false;
> +	bool has_coretile_hdlcd = false;
> +	bool mux_motherboard = true;
> +	u32 val;
> +	int ret;
> +
> +	/*
> +	 * Check if we have a CLCD or HDLCD on the core tile by checking if a
> +	 * CLCD or HDLCD is available in the root of the device tree.
> +	 */
> +	root = of_find_node_by_path("/");
> +	if (!root)
> +		return -EINVAL;
> +
> +	for_each_available_child_of_node(root, child) {
> +		if (of_device_is_compatible(child, "arm,pl111")) {
> +			has_coretile_clcd = true;
> +			ct_clcd = child;
> +			break;
> +		}
> +		if (of_device_is_compatible(child, "arm,hdlcd")) {
> +			has_coretile_hdlcd = true;
> +			break;
> +		}
> +	}
> +
> +	/*
> +	 * If there is a coretile HDLCD and it has a driver,
> +	 * do not mux the CLCD on the motherboard to the DVI.
> +	 */
> +	if (has_coretile_hdlcd && IS_ENABLED(CONFIG_DRM_HDLCD))
> +		mux_motherboard = false;
> +
> +	/*
> +	 * On the Vexpress CA9 we let the CLCD on the coretile
> +	 * take precedence, so also in this case do not mux the
> +	 * motherboard to the DVI.
> +	 */
> +	if (has_coretile_clcd)
> +		mux_motherboard = false;
> +
> +	if (mux_motherboard) {
> +		dev_info(dev, "DVI muxed to motherboard CLCD\n");
> +		val = VEXPRESS_FPGAMUX_MOTHERBOARD;
> +	} else if (ct_clcd == dev->of_node) {
> +		dev_info(dev,
> +			 "DVI muxed to daughterboard 1 (core tile) CLCD\n");
> +		val = VEXPRESS_FPGAMUX_DAUGHTERBOARD_1;
> +	} else {
> +		dev_info(dev, "core tile graphics present\n");
> +		dev_info(dev, "this device will be deactivated\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = regmap_write(map, 0, val);
> +	if (ret) {
> +		dev_err(dev, "error setting DVI muxmode\n");
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * This sets up the regmap pointer that will then be retrieved by
> + * the detection code in pl111_versatile.c and passed in to the
> + * pl111_vexpress_clcd_init() function above.
> + */
> +static int vexpress_muxfpga_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct regmap *map;
> +
> +	map = devm_regmap_init_vexpress_config(&pdev->dev);
> +	if (IS_ERR(map))
> +		return PTR_ERR(map);
> +	dev_set_drvdata(dev, map);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id vexpress_muxfpga_match[] = {
> +	{ .compatible = "arm,vexpress-muxfpga", }
> +};
> +
> +static struct platform_driver vexpress_muxfpga_driver = {
> +	.driver = {
> +		.name = "vexpress-muxfpga",
> +		.of_match_table = of_match_ptr(vexpress_muxfpga_match),
> +	},
> +	.probe = vexpress_muxfpga_probe,
> +};
> +
> +builtin_platform_driver(vexpress_muxfpga_driver);

  LD [M]  drivers/gpu/drm/pl111/pl111_drm.o
drivers/gpu/drm/pl111/pl111_vexpress.o: In function `vexpress_muxfpga_driver_init':
/home/daniel/linux/aarch64/drivers/gpu/drm/pl111/pl111_vexpress.c:125: multiple definition of `init_module'
drivers/gpu/drm/pl111/pl111_drv.o:/home/daniel/linux/aarch64/drivers/gpu/drm/pl111/pl111_drv.c:427: first defined here
make[4]: *** [scripts/Makefile.build:570: drivers/gpu/drm/pl111/pl111_drm.o] Error 1
make[3]: *** [scripts/Makefile.build:583: drivers/gpu/drm/pl111] Error 2
make[3]: *** Waiting for unfinished jobs....

Using the default drm-misc configs.

Please fix asap, and please compile-test before pushing using these
default configs so others aren't blocked. I've disabled the pl111 driver
in there for now, please also re-enable.

Thanks, Daniel
> diff --git a/drivers/gpu/drm/pl111/pl111_vexpress.h b/drivers/gpu/drm/pl111/pl111_vexpress.h
> new file mode 100644
> index 000000000000..49876417f7b6
> --- /dev/null
> +++ b/drivers/gpu/drm/pl111/pl111_vexpress.h
> @@ -0,0 +1,22 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +struct device;
> +struct pl111_drm_dev_private;
> +struct regmap;
> +
> +#ifdef CONFIG_ARCH_VEXPRESS
> +
> +int pl111_vexpress_clcd_init(struct device *dev,
> +			     struct pl111_drm_dev_private *priv,
> +			     struct regmap *map);
> +
> +#else
> +
> +static int inline pl111_vexpress_clcd_init(struct device *dev,
> +					   struct pl111_drm_dev_private *priv,
> +					   struct regmap *map)
> +{
> +	return -ENODEV;
> +}
> +
> +#endif
> -- 
> 2.17.0
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
Linus Walleij May 3, 2018, 9:58 a.m. | #2
On Thu, May 3, 2018 at 11:41 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
>Me:
(...)
>> ChangeLog v4->v5:
>> - Collect Robin's Tested-by
>> - Switch to builtin_platform_driver() for the muxfpga as this
>>   file is always built as "-y" for the arch and we need to
>>   avoid module collisions with the main PL111 driver.
>> +builtin_platform_driver(vexpress_muxfpga_driver);
>
>   LD [M]  drivers/gpu/drm/pl111/pl111_drm.o
> drivers/gpu/drm/pl111/pl111_vexpress.o: In function `vexpress_muxfpga_driver_init':
> /home/daniel/linux/aarch64/drivers/gpu/drm/pl111/pl111_vexpress.c:125: multiple definition of `init_module'
> drivers/gpu/drm/pl111/pl111_drv.o:/home/daniel/linux/aarch64/drivers/gpu/drm/pl111/pl111_drv.c:427: first defined here
> make[4]: *** [scripts/Makefile.build:570: drivers/gpu/drm/pl111/pl111_drm.o] Error 1
> make[3]: *** [scripts/Makefile.build:583: drivers/gpu/drm/pl111] Error 2
> make[3]: *** Waiting for unfinished jobs....
>
> Using the default drm-misc configs.
>
> Please fix asap, and please compile-test before pushing using these
> default configs so others aren't blocked. I've disabled the pl111 driver
> in there for now, please also re-enable.

That's typical. As you see in the ChangeLog I really tried to address
exactly this. I got that build error from the zeroday build with v4,
changed it to a builtin driver and pushed, then it did not give me
the problem again.

I guess the zeroday build robot isn't using the same configs...

Hm, I will take suggestions on how to actually solve it, I guess
this is due to the initcall infrastructure only allowing one initcall
per module?

In that case I guess I should either:
- Break out the vexpress file into its own module
- Call the vexpress init hooks from the initcalls in the
  main module

I guess I will go for the latter even if it litters the driver with
more cross-calls.

Yours,
Linus Walleij
Daniel Vetter May 3, 2018, 10:41 a.m. | #3
On Thu, May 3, 2018 at 11:58 AM, Linus Walleij <linus.walleij@linaro.org> wrote:
> On Thu, May 3, 2018 at 11:41 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
>>Me:
> (...)
>>> ChangeLog v4->v5:
>>> - Collect Robin's Tested-by
>>> - Switch to builtin_platform_driver() for the muxfpga as this
>>>   file is always built as "-y" for the arch and we need to
>>>   avoid module collisions with the main PL111 driver.
>>> +builtin_platform_driver(vexpress_muxfpga_driver);
>>
>>   LD [M]  drivers/gpu/drm/pl111/pl111_drm.o
>> drivers/gpu/drm/pl111/pl111_vexpress.o: In function `vexpress_muxfpga_driver_init':
>> /home/daniel/linux/aarch64/drivers/gpu/drm/pl111/pl111_vexpress.c:125: multiple definition of `init_module'
>> drivers/gpu/drm/pl111/pl111_drv.o:/home/daniel/linux/aarch64/drivers/gpu/drm/pl111/pl111_drv.c:427: first defined here
>> make[4]: *** [scripts/Makefile.build:570: drivers/gpu/drm/pl111/pl111_drm.o] Error 1
>> make[3]: *** [scripts/Makefile.build:583: drivers/gpu/drm/pl111] Error 2
>> make[3]: *** Waiting for unfinished jobs....
>>
>> Using the default drm-misc configs.
>>
>> Please fix asap, and please compile-test before pushing using these
>> default configs so others aren't blocked. I've disabled the pl111 driver
>> in there for now, please also re-enable.
>
> That's typical. As you see in the ChangeLog I really tried to address
> exactly this. I got that build error from the zeroday build with v4,
> changed it to a builtin driver and pushed, then it did not give me
> the problem again.
>
> I guess the zeroday build robot isn't using the same configs...
>
> Hm, I will take suggestions on how to actually solve it, I guess
> this is due to the initcall infrastructure only allowing one initcall
> per module?
>
> In that case I guess I should either:
> - Break out the vexpress file into its own module
> - Call the vexpress init hooks from the initcalls in the
>   main module
>
> I guess I will go for the latter even if it litters the driver with
> more cross-calls.

Yeah, that's what we generally do. Including #ifdef mess and all that,
since separate kmod tends to be even worse (you need piles of
EXPORT_SYMBOL all over). It's a bit silly that the module/initcall
stuff can't allow multiple entries ...
-Daniel

Patch

diff --git a/drivers/gpu/drm/pl111/Makefile b/drivers/gpu/drm/pl111/Makefile
index 9c5e8dba8ac6..19a8189dc54f 100644
--- a/drivers/gpu/drm/pl111/Makefile
+++ b/drivers/gpu/drm/pl111/Makefile
@@ -3,6 +3,7 @@  pl111_drm-y +=	pl111_display.o \
 		pl111_versatile.o \
 		pl111_drv.o
 
+pl111_drm-$(CONFIG_ARCH_VEXPRESS) += pl111_vexpress.o
 pl111_drm-$(CONFIG_DEBUG_FS) += pl111_debugfs.o
 
 obj-$(CONFIG_DRM_PL111) += pl111_drm.o
diff --git a/drivers/gpu/drm/pl111/pl111_versatile.c b/drivers/gpu/drm/pl111/pl111_versatile.c
index 9302f516045e..78ddf8534fd2 100644
--- a/drivers/gpu/drm/pl111/pl111_versatile.c
+++ b/drivers/gpu/drm/pl111/pl111_versatile.c
@@ -1,12 +1,14 @@ 
 #include <linux/amba/clcd-regs.h>
 #include <linux/device.h>
 #include <linux/of.h>
+#include <linux/of_platform.h>
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
 #include <linux/bitops.h>
 #include <linux/module.h>
 #include <drm/drmP.h>
 #include "pl111_versatile.h"
+#include "pl111_vexpress.h"
 #include "pl111_drm.h"
 
 static struct regmap *versatile_syscon_map;
@@ -22,6 +24,7 @@  enum versatile_clcd {
 	REALVIEW_CLCD_PB11MP,
 	REALVIEW_CLCD_PBA8,
 	REALVIEW_CLCD_PBX,
+	VEXPRESS_CLCD_V2M,
 };
 
 static const struct of_device_id versatile_clcd_of_match[] = {
@@ -53,6 +56,10 @@  static const struct of_device_id versatile_clcd_of_match[] = {
 		.compatible = "arm,realview-pbx-syscon",
 		.data = (void *)REALVIEW_CLCD_PBX,
 	},
+	{
+		.compatible = "arm,vexpress-muxfpga",
+		.data = (void *)VEXPRESS_CLCD_V2M,
+	},
 	{},
 };
 
@@ -286,12 +293,26 @@  static const struct pl111_variant_data pl111_realview = {
 	.fb_bpp = 16,
 };
 
+/*
+ * Versatile Express PL111 variant, again we just push the maximum
+ * BPP to 16 to be able to get 1024x768 without saturating the memory
+ * bus. The clockdivider also seems broken on the Versatile Express.
+ */
+static const struct pl111_variant_data pl111_vexpress = {
+	.name = "PL111 Versatile Express",
+	.formats = pl111_realview_pixel_formats,
+	.nformats = ARRAY_SIZE(pl111_realview_pixel_formats),
+	.fb_bpp = 16,
+	.broken_clockdivider = true,
+};
+
 int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv)
 {
 	const struct of_device_id *clcd_id;
 	enum versatile_clcd versatile_clcd_type;
 	struct device_node *np;
 	struct regmap *map;
+	int ret;
 
 	np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match,
 					     &clcd_id);
@@ -301,7 +322,26 @@  int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv)
 	}
 	versatile_clcd_type = (enum versatile_clcd)clcd_id->data;
 
-	map = syscon_node_to_regmap(np);
+	/* Versatile Express special handling */
+	if (versatile_clcd_type == VEXPRESS_CLCD_V2M) {
+		struct platform_device *pdev;
+
+		/* Call into deep Vexpress configuration API */
+		pdev = of_find_device_by_node(np);
+		if (!pdev) {
+			dev_err(dev, "can't find the sysreg device, deferring\n");
+			return -EPROBE_DEFER;
+		}
+		map = dev_get_drvdata(&pdev->dev);
+		if (!map) {
+			dev_err(dev, "sysreg has not yet probed\n");
+			platform_device_put(pdev);
+			return -EPROBE_DEFER;
+		}
+	} else {
+		map = syscon_node_to_regmap(np);
+	}
+
 	if (IS_ERR(map)) {
 		dev_err(dev, "no Versatile syscon regmap\n");
 		return PTR_ERR(map);
@@ -340,6 +380,13 @@  int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv)
 		priv->variant_display_disable = pl111_realview_clcd_disable;
 		dev_info(dev, "set up callbacks for RealView PL111\n");
 		break;
+	case VEXPRESS_CLCD_V2M:
+		priv->variant = &pl111_vexpress;
+		dev_info(dev, "initializing Versatile Express PL111\n");
+		ret = pl111_vexpress_clcd_init(dev, priv, map);
+		if (ret)
+			return ret;
+		break;
 	default:
 		dev_info(dev, "unknown Versatile system controller\n");
 		break;
diff --git a/drivers/gpu/drm/pl111/pl111_vexpress.c b/drivers/gpu/drm/pl111/pl111_vexpress.c
new file mode 100644
index 000000000000..c9fee625faf1
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_vexpress.c
@@ -0,0 +1,125 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Versatile Express PL111 handling
+ * Copyright (C) 2018 Linus Walleij
+ *
+ * This module binds to the "arm,vexpress-muxfpga" device on the
+ * Versatile Express configuration bus and sets up which CLCD instance
+ * gets muxed out on the DVI bridge.
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/vexpress.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include "pl111_drm.h"
+#include "pl111_vexpress.h"
+
+#define VEXPRESS_FPGAMUX_MOTHERBOARD		0x00
+#define VEXPRESS_FPGAMUX_DAUGHTERBOARD_1	0x01
+#define VEXPRESS_FPGAMUX_DAUGHTERBOARD_2	0x02
+
+int pl111_vexpress_clcd_init(struct device *dev,
+			     struct pl111_drm_dev_private *priv,
+			     struct regmap *map)
+{
+	struct device_node *root;
+	struct device_node *child;
+	struct device_node *ct_clcd = NULL;
+	bool has_coretile_clcd = false;
+	bool has_coretile_hdlcd = false;
+	bool mux_motherboard = true;
+	u32 val;
+	int ret;
+
+	/*
+	 * Check if we have a CLCD or HDLCD on the core tile by checking if a
+	 * CLCD or HDLCD is available in the root of the device tree.
+	 */
+	root = of_find_node_by_path("/");
+	if (!root)
+		return -EINVAL;
+
+	for_each_available_child_of_node(root, child) {
+		if (of_device_is_compatible(child, "arm,pl111")) {
+			has_coretile_clcd = true;
+			ct_clcd = child;
+			break;
+		}
+		if (of_device_is_compatible(child, "arm,hdlcd")) {
+			has_coretile_hdlcd = true;
+			break;
+		}
+	}
+
+	/*
+	 * If there is a coretile HDLCD and it has a driver,
+	 * do not mux the CLCD on the motherboard to the DVI.
+	 */
+	if (has_coretile_hdlcd && IS_ENABLED(CONFIG_DRM_HDLCD))
+		mux_motherboard = false;
+
+	/*
+	 * On the Vexpress CA9 we let the CLCD on the coretile
+	 * take precedence, so also in this case do not mux the
+	 * motherboard to the DVI.
+	 */
+	if (has_coretile_clcd)
+		mux_motherboard = false;
+
+	if (mux_motherboard) {
+		dev_info(dev, "DVI muxed to motherboard CLCD\n");
+		val = VEXPRESS_FPGAMUX_MOTHERBOARD;
+	} else if (ct_clcd == dev->of_node) {
+		dev_info(dev,
+			 "DVI muxed to daughterboard 1 (core tile) CLCD\n");
+		val = VEXPRESS_FPGAMUX_DAUGHTERBOARD_1;
+	} else {
+		dev_info(dev, "core tile graphics present\n");
+		dev_info(dev, "this device will be deactivated\n");
+		return -ENODEV;
+	}
+
+	ret = regmap_write(map, 0, val);
+	if (ret) {
+		dev_err(dev, "error setting DVI muxmode\n");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/*
+ * This sets up the regmap pointer that will then be retrieved by
+ * the detection code in pl111_versatile.c and passed in to the
+ * pl111_vexpress_clcd_init() function above.
+ */
+static int vexpress_muxfpga_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct regmap *map;
+
+	map = devm_regmap_init_vexpress_config(&pdev->dev);
+	if (IS_ERR(map))
+		return PTR_ERR(map);
+	dev_set_drvdata(dev, map);
+
+	return 0;
+}
+
+static const struct of_device_id vexpress_muxfpga_match[] = {
+	{ .compatible = "arm,vexpress-muxfpga", }
+};
+
+static struct platform_driver vexpress_muxfpga_driver = {
+	.driver = {
+		.name = "vexpress-muxfpga",
+		.of_match_table = of_match_ptr(vexpress_muxfpga_match),
+	},
+	.probe = vexpress_muxfpga_probe,
+};
+
+builtin_platform_driver(vexpress_muxfpga_driver);
diff --git a/drivers/gpu/drm/pl111/pl111_vexpress.h b/drivers/gpu/drm/pl111/pl111_vexpress.h
new file mode 100644
index 000000000000..49876417f7b6
--- /dev/null
+++ b/drivers/gpu/drm/pl111/pl111_vexpress.h
@@ -0,0 +1,22 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+struct device;
+struct pl111_drm_dev_private;
+struct regmap;
+
+#ifdef CONFIG_ARCH_VEXPRESS
+
+int pl111_vexpress_clcd_init(struct device *dev,
+			     struct pl111_drm_dev_private *priv,
+			     struct regmap *map);
+
+#else
+
+static int inline pl111_vexpress_clcd_init(struct device *dev,
+					   struct pl111_drm_dev_private *priv,
+					   struct regmap *map)
+{
+	return -ENODEV;
+}
+
+#endif