From tglx at linutronix.de  Sun Aug  2 03:22:24 2015
From: tglx at linutronix.de (Thomas Gleixner)
Date: Sun, 2 Aug 2015 12:22:24 +0200 (CEST)
Subject: [PATCH] irqchip: bcm2836: Use a CPU notifier enable IPIs.
In-Reply-To: <1438027789-1637-1-git-send-email-eric@anholt.net>
References: <55B67C81.1010900@gmail.com>
 <1438027789-1637-1-git-send-email-eric@anholt.net>
Message-ID: <alpine.DEB.2.11.1508021221370.3825@nanos>

On Mon, 27 Jul 2015, Eric Anholt wrote:
> +/* Unmasks the IPI on the CPU wen it's first brought online. */

when

> +static int bcm2836_arm_irqchip_cpu_notify(struct notifier_block *nfb,
> +					  unsigned long action, void *hcpu)
> +{
> +	unsigned int cpu = (unsigned long)hcpu;
> +	unsigned int int_reg = LOCAL_MAILBOX_INT_CONTROL0;
> +	unsigned int mailbox = 0;
> +
> +	if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
> +		bcm2836_arm_irqchip_unmask_per_cpu_irq(int_reg, mailbox, cpu);

Shouldn't you mask the irq on CPU_DYING?

Thanks,

	tglx


From sjg at chromium.org  Sun Aug  2 14:29:32 2015
From: sjg at chromium.org (Simon Glass)
Date: Sun, 2 Aug 2015 15:29:32 -0600
Subject: [U-Boot] [PATCH U-Boot] ARM: rpi_b: detect board revision
In-Reply-To: <55B1DA79.8060401@wwwdotorg.org>
References: <1416372021-17997-1-git-send-email-swarren@wwwdotorg.org>
 <CAPnjgZ3M0efTEpBiSPVLJRZgXQN-n6rebT6hB=W_skPf1yw_1A@mail.gmail.com>
 <55B1DA79.8060401@wwwdotorg.org>
Message-ID: <CAPnjgZ0FX0P8TSckyTKqypSVPEnkZt3XMFyyv0vOQYXO7FRZKw@mail.gmail.com>

Hi Stephen,

On 24 July 2015 at 00:26, Stephen Warren <swarren at wwwdotorg.org> wrote:
> On 11/24/2014 08:50 AM, Simon Glass wrote:
>> Hi Stephen,
>>
>> On 18 November 2014 at 21:40, Stephen Warren <swarren at wwwdotorg.org> wrote:
>>> Detect the board revision early during boot, and print the decoded
>>> model name.
>>>
>>> Eventually, this information can be used for tasks such as:
>>> - Allowing/preventing USB device mode; some models have a USB device on-
>>>   board so only host mode makes sense. Others connect the SoC directly
>>>   to the USB connector, so device-mode might make sense.
>>> - The on-board USB hub/Ethernet requires different GPIOs to enable it,
>>>   although luckily the default appears to be fine so far.
>>> - The compute module contains an on-board eMMC device, so we could store
>>>   the environment there. Other models use an SD card and so don't support
>>>   saving the environment (unless we store it in a file on the FAT boot
>>>   partition...)
>>>
>>> Set $fdtfile based on this information. At present, the mainline Linux
>>> kernel doesn't contain a separate DTB for most models, but I hope that
>>> will change soon.
>>>
>>> Signed-off-by: Stephen Warren <swarren at wwwdotorg.org>
>>> ---
>>> I'm considering renaming rpi_b -> rpi in U-Boot since it supports many
>>> models. Hopefully I can persuade U-Boot to load the environment from
>>> different places at run-time, so we won't need different builds based
>>> on whether the environment is in eMMC or not for example.
>>>
>>>  arch/arm/include/asm/arch-bcm2835/mbox.h |  33 +++++++++
>>>  board/raspberrypi/rpi_b/rpi_b.c          | 122 ++++++++++++++++++++++++++++++-
>>>  include/configs/rpi_b.h                  |   1 -
>>>  3 files changed, 152 insertions(+), 4 deletions(-)
>>
>> I tried this out. It worked OK for me except that it can't find the
>> device tree file bcm2835-rpi-b-rev2.dtb.
>>
>> Oddly I can fatload it from /bcm2835-rpi-b-rev2.dtb but when I try
>> from /syslinux/..//bcm2835-rpi-b-rev2.dtb it fails and cannot find the
>> file. Reducing the filename length to 8 chars works. I wonder what
>> year of my life FAT will stop plaguing me?
>
> Did you ever find a solution to this issue? It's hitting me now.
>
> I found a thread about this topic:
>
> http://lists.denx.de/pipermail/u-boot/2014-December/198471.html
> [U-Boot] [PATCH] fs: fat: read: fix fat16 ls/read issue
>
> However, there didn't seem to be any conclusion there. I did look at the
> FAT code a bit, but didn't make much headway yet. Having turned on
> #define DEBUG, I did notice that the code path for running ls on the
> root directory appears completely different to the code path for running
> ls on a sub-directory, and I think that latter path is being used to
> parse the root directory via the path /extlinux/../xxx. Judging purely
> by debug output, the code for the root directory appears to read n
> sectors (3 in my case) and dump directory entries from all of those
> sectors (my filesystem has quite a few files in the root), whereas the
> code for sub-directories appears to read and dump only a single sector,
> even when run on the root directory that needs 3 sectors dumped. That's
> the cause of the missing results from ls, but I haven't worked out
> what's triggering that in the code yet.

I think I saw a patch from you go by so it looks like you figured it out!

Regards,
Simon


From eric at anholt.net  Mon Aug  3 16:23:09 2015
From: eric at anholt.net (Eric Anholt)
Date: Mon, 03 Aug 2015 16:23:09 -0700
Subject: [PATCH] irqchip: bcm2836: Use a CPU notifier enable IPIs.
In-Reply-To: <alpine.DEB.2.11.1508021221370.3825@nanos>
References: <55B67C81.1010900@gmail.com>
 <1438027789-1637-1-git-send-email-eric@anholt.net>
 <alpine.DEB.2.11.1508021221370.3825@nanos>
Message-ID: <87fv40lzxe.fsf@eliezer.anholt.net>

Thomas Gleixner <tglx at linutronix.de> writes:

> On Mon, 27 Jul 2015, Eric Anholt wrote:
>> +/* Unmasks the IPI on the CPU wen it's first brought online. */
>
> when
>
>> +static int bcm2836_arm_irqchip_cpu_notify(struct notifier_block *nfb,
>> +					  unsigned long action, void *hcpu)
>> +{
>> +	unsigned int cpu = (unsigned long)hcpu;
>> +	unsigned int int_reg = LOCAL_MAILBOX_INT_CONTROL0;
>> +	unsigned int mailbox = 0;
>> +
>> +	if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
>> +		bcm2836_arm_irqchip_unmask_per_cpu_irq(int_reg, mailbox, cpu);
>
> Shouldn't you mask the irq on CPU_DYING?

I was just following what other drivers were doing.  Is CPU_DYING the
only thing that needs masking?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150803/f573d5f4/attachment.sig>

From tglx at linutronix.de  Tue Aug  4 00:14:34 2015
From: tglx at linutronix.de (Thomas Gleixner)
Date: Tue, 4 Aug 2015 09:14:34 +0200 (CEST)
Subject: [PATCH] irqchip: bcm2836: Use a CPU notifier enable IPIs.
In-Reply-To: <87fv40lzxe.fsf@eliezer.anholt.net>
References: <55B67C81.1010900@gmail.com>
 <1438027789-1637-1-git-send-email-eric@anholt.net>
 <alpine.DEB.2.11.1508021221370.3825@nanos>
 <87fv40lzxe.fsf@eliezer.anholt.net>
Message-ID: <alpine.DEB.2.11.1508040913490.3959@nanos>

On Mon, 3 Aug 2015, Eric Anholt wrote:

> Thomas Gleixner <tglx at linutronix.de> writes:
> 
> > On Mon, 27 Jul 2015, Eric Anholt wrote:
> >> +/* Unmasks the IPI on the CPU wen it's first brought online. */
> >
> > when
> >
> >> +static int bcm2836_arm_irqchip_cpu_notify(struct notifier_block *nfb,
> >> +					  unsigned long action, void *hcpu)
> >> +{
> >> +	unsigned int cpu = (unsigned long)hcpu;
> >> +	unsigned int int_reg = LOCAL_MAILBOX_INT_CONTROL0;
> >> +	unsigned int mailbox = 0;
> >> +
> >> +	if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
> >> +		bcm2836_arm_irqchip_unmask_per_cpu_irq(int_reg, mailbox, cpu);
> >
> > Shouldn't you mask the irq on CPU_DYING?
> 
> I was just following what other drivers were doing.  Is CPU_DYING the
> only thing that needs masking?

CPPU_DYING is the counterpart of CPU_STARTING. It's called from the
CPU which goes down.

Thanks,

	tglx



From kernel at martin.sperl.org  Tue Aug  4 01:41:57 2015
From: kernel at martin.sperl.org (kernel at martin.sperl.org)
Date: Tue,  4 Aug 2015 08:41:57 +0000
Subject: [RFC PATCH] soc: bcm2835: auxiliar devices enable infrastructure
In-Reply-To: <55BC353F.1000906@wwwdotorg.org>
References: <55BC353F.1000906@wwwdotorg.org>
Message-ID: <1438677718-19612-1-git-send-email-kernel@martin.sperl.org>

From: Martin Sperl <kernel at martin.sperl.org>

The bcm2835 soc contains 3 auxiliar devices (spi1, spi2 and uart1)
that all are enabled via a shared register.

To serialize access this soc-driver is created that implements
  bcm2835aux_enable(struct device *dev, const char *property);
  bcm2835aux_disable(struct device *dev, const char *property);

Ehich will read the property from the device tree of the device
and enable/disable that specific device as per device tree.

First use of this api will be spi-bcm2835aux, which will
set the following property in the device tree for the devices:
  brcm,aux-enable = <&aux_enable 4>;  

And call:
  bcm2835aux_enable(&spi->dev, "brcm,aux-enable");

This allows to define different property names based on the
driver.

Signed-off-by: Martin Sperl <kernel at martin.sperl.org>
---
 .../bindings/soc/bcm/brcm,bcm2835-aux.txt          |   16 ++
 drivers/soc/Kconfig                                |    1 +
 drivers/soc/Makefile                               |    1 +
 drivers/soc/bcm/Kconfig                            |   10 ++
 drivers/soc/bcm/Makefile                           |    1 +
 drivers/soc/bcm/bcm2835-aux.c                      |  155 ++++++++++++++++++++
 include/linux/soc/bcm/bcm2835-aux.h                |   24 +++
 7 files changed, 208 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt
 create mode 100644 drivers/soc/bcm/Kconfig
 create mode 100644 drivers/soc/bcm/Makefile
 create mode 100644 drivers/soc/bcm/bcm2835-aux.c
 create mode 100644 include/linux/soc/bcm/bcm2835-aux.h

Note that earlier discussions on the rpi list the use of
the following existing frameworks were rejected:
* syscon - device tree should describe HW not drivers to use
           ('compatiblity = "brcm,bcm2835-aux-enable", "syscon";')
            is not acceptable)
* regulator - the HW does not necessarily a regulator
              or a power gate, so it is no valid use-case
              this framework.

A new "minimal" framework was recommended instead.

Note that this is only a RFC containing everything in one patch -
documentation would get separated out to a separate patch in the
final version then containing also the spi-driver itself in the
patch-set.

diff --git a/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt b/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt
new file mode 100644
index 0000000..894ff5c
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt
@@ -0,0 +1,16 @@
+Broadcom BCM2835 auxiliar device enable register
+
+the BCM2835 contains 3 auxiliar SPI devices
+which need to get enabled via a shared register.
+
+Required properties:
+- compatible: Should be "brcm,bcm2835-aux-spi".
+- reg: Should contain register location and length for the enabl
+       register
+
+Example:
+
+aux_enable: aux_enable at 0x7e215004 {
+	compatible = "bcrm,bcm2835-aux-enable";
+	reg = <0x7e215004 0x04>;
+};
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 96ddecb..5506e39 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -1,5 +1,6 @@
 menu "SOC (System On Chip) specific Drivers"
 
+source "drivers/soc/bcm/Kconfig"
 source "drivers/soc/mediatek/Kconfig"
 source "drivers/soc/qcom/Kconfig"
 source "drivers/soc/sunxi/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 7dc7c0d..c5744e1 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -2,6 +2,7 @@
 # Makefile for the Linux Kernel SOC specific device drivers.
 #
 
+obj-$(CONFIG_ARCH_BCM)		+= bcm/
 obj-$(CONFIG_ARCH_MEDIATEK)	+= mediatek/
 obj-$(CONFIG_ARCH_QCOM)		+= qcom/
 obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig
new file mode 100644
index 0000000..b26060d
--- /dev/null
+++ b/drivers/soc/bcm/Kconfig
@@ -0,0 +1,10 @@
+#
+# MediaTek SoC drivers
+#
+config SOC_BCM2835_AUX
+	tristate "Broadcom BCM2835 aux"
+	depends on OF
+	depends on ARCH_BCM2835 || COMPILE_TEST
+
+	help
+	  Support for the BCM2835 auxiliar devices
diff --git a/drivers/soc/bcm/Makefile b/drivers/soc/bcm/Makefile
new file mode 100644
index 0000000..370a872
--- /dev/null
+++ b/drivers/soc/bcm/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SOC_BCM2835_AUX) += bcm2835-aux.o
diff --git a/drivers/soc/bcm/bcm2835-aux.c b/drivers/soc/bcm/bcm2835-aux.c
new file mode 100644
index 0000000..19e50f8
--- /dev/null
+++ b/drivers/soc/bcm/bcm2835-aux.c
@@ -0,0 +1,155 @@
+/*
+ * bcm2835-aux
+ *
+ * Copyright (C) 2015 Martin Sperl
+ *
+ * Author: Martin Sperl <kernel at martin.sperl.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/soc/bcm/bcm2835-aux.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+static DEFINE_SPINLOCK(bcm2835aux_lock);
+
+static struct platform_driver bcm2835aux_driver;
+
+static int bcm2835aux_dev_match(struct device *dev, void *data)
+{
+	struct device_node *dn = data;
+
+	return (dev->of_node == dn) ? 1 : 0;
+}
+
+static void *bcm2835aux_find_base(struct device *dev, const char *property)
+{
+	struct device *found = NULL;
+	struct device_node *np;
+
+	/* get the phandle of the device */
+	np = of_parse_phandle(dev->of_node, property, 0);
+	if (!np) {
+		dev_err(dev, "missing property %s\n", property);
+		return ERR_PTR(-ENODEV);
+	}
+
+	/* now find the device it points to */
+	found = driver_find_device(&bcm2835aux_driver.driver, NULL,
+				   np, bcm2835aux_dev_match);
+	if (!found) {
+		dev_err(dev, "device for phandle of %s not found\n",
+			property);
+		return ERR_PTR(-ENODEV);
+	}
+
+	/* now we got the device, so return the pointer */
+	return dev_get_drvdata(found);
+}
+
+static u32 bcm2835aux_find_mask(struct device *dev, const char *property)
+{
+	int err;
+	u32 mask;
+
+	err = of_property_read_u32_index(dev->of_node, property, 1, &mask);
+	if (err) {
+		dev_err(dev, "missing argument to %s: %d\n",
+			property, err);
+		return 0;
+	}
+
+	return mask;
+}
+
+static int bcm2835aux_bitset(struct device *dev, const char *property,
+			     bool set)
+{
+	u32 v, mask;
+	unsigned long flags;
+	void __iomem *base;
+
+	/* find the device */
+	base = bcm2835aux_find_base(dev, property);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	/* and extract the mask */
+	mask = bcm2835aux_find_mask(dev, property);
+	if (!mask)
+		return -ENOENT;
+
+	spin_lock_irqsave(&bcm2835aux_lock, flags);
+
+	v = readl(base);
+	if (set)
+		v |= mask;
+	else
+		v &= ~mask;
+
+	writel(v, base);
+
+	spin_unlock_irqrestore(&bcm2835aux_lock, flags);
+
+	return 0;
+}
+
+int bcm2835aux_enable(struct device *dev, const char *property)
+{
+	return bcm2835aux_bitset(dev, property, true);
+}
+EXPORT_SYMBOL_GPL(bcm2835aux_enable);
+
+int bcm2835aux_disable(struct device *dev, const char *property)
+{
+	return bcm2835aux_bitset(dev, property, false);
+}
+EXPORT_SYMBOL_GPL(bcm2835aux_disable);
+
+static int bcm2835aux_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	void __iomem *base;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENOENT;
+
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	platform_set_drvdata(pdev, base);
+
+	return 0;
+}
+
+static const struct of_device_id bcm2835aux_match[] = {
+	{ .compatible = "brcm,bcm2835-aux", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, bcm2835aux_match);
+
+static struct platform_driver bcm2835aux_driver = {
+	.driver = {
+		.name           = "bcm2835-aux",
+		.of_match_table	= bcm2835aux_match,
+	},
+	.probe			= bcm2835aux_probe,
+};
+module_platform_driver(bcm2835aux_driver);
+
+MODULE_DESCRIPTION("enable/disable driver for hw-blocks for Broadcom BCM2835 aux");
+MODULE_AUTHOR("Martin Sperl <kernel at martin.sperl.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/soc/bcm/bcm2835-aux.h b/include/linux/soc/bcm/bcm2835-aux.h
new file mode 100644
index 0000000..685232c
--- /dev/null
+++ b/include/linux/soc/bcm/bcm2835-aux.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 Martin Sperl
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __BCM2835_AUX_H__
+#define __BCM2835_AUX_H__
+
+#include <linux/device.h>
+#include <linux/types.h>
+
+int bcm2835aux_enable(struct device *dev, const char *property);
+int bcm2835aux_disable(struct device *dev, const char *property);
+
+#endif
-- 
1.7.10.4



From kernel at martin.sperl.org  Tue Aug  4 02:52:09 2015
From: kernel at martin.sperl.org (Martin Sperl)
Date: Tue, 04 Aug 2015 11:52:09 +0200
Subject: [PATCH 1/2] spi: bcm2835: add spi-bcm2835aux driver for the
 auxiliar spi1 and spi2
In-Reply-To: <55BC353F.1000906@wwwdotorg.org>
References: <1434980408-4086-1-git-send-email-kernel@martin.sperl.org>
 <55A0A150.3060809@wwwdotorg.org>
 <ECFBB7BF-1316-4565-B1F2-FB2651131882@martin.sperl.org>
 <55A49662.3000706@wwwdotorg.org>
 <2768BFA9-7FE9-4EDC-8692-AC3F070BD874@martin.sperl.org>
 <55AEF828.20908@wwwdotorg.org>
 <0125992E-40F4-4702-8404-2943FF9A8788@martin.sperl.org>
 <55B1BA7A.1090104@wwwdotorg.org>
 <9005ABEC-C60A-4814-AD60-AD5BB09808F2@martin.sperl.org>
 <55B6EE1A.2070605@wwwdotorg.org>
 <836DA157-3072-441A-A160-E1FDBB119E88@martin.sperl.org>
 <55B75E07.1080104@martin.sperl.org> <55BC353F.1000906@wwwdotorg.org>
Message-ID: <55C08B49.2010307@martin.sperl.org>

On 01.08.2015 04:55, Stephen Warren wrote:
> On 07/28/2015 04:48 AM, Martin Sperl wrote:
> The HW docs aren't very detailed on this point, but they certainly don't
> state that these bits control a regulator (or power domain as mentioned
> later in the thread). I guess it's possible, but it feels like an
> assumption to me. The HW modules seem small enough that it doesn't feel
> likely anyone would have bothered with power-gating them.

Sent an initial RFC-patch for the bcm2835-aux enable framework.

Only contains 2 API functions:
   bcm2835aux_enable(struct device *dev, const char *property);
   bcm2835aux_disable(struct device *dev, const char *property);

Device is the device of the real HW driver for the aux block (spi or
uart) and property is the name of the property in the device tree
of that device.

Please review if this is acceptable, so that I then can send
a complete patch-set including this API as well as the spi driver.

Thanks


From kernel at martin.sperl.org  Tue Aug  4 03:12:59 2015
From: kernel at martin.sperl.org (Martin Sperl)
Date: Tue, 04 Aug 2015 12:12:59 +0200
Subject: [PATCH] spi: bcm2835: set up spi-mode before asserting cs-gpio
In-Reply-To: <1438092193-31799-1-git-send-email-kernel@martin.sperl.org>
References: <1438092193-31799-1-git-send-email-kernel@martin.sperl.org>
Message-ID: <55C0902B.2040006@martin.sperl.org>

Any feedback on this patch? Can this get merged?

Noralf (via private communication) has confirmed that with
this patch his fbtft device (which uses spi-cpol and spi-cpha)
works.

Thanks,
	Martin

On 28.07.2015 16:03, kernel at martin.sperl.org wrote:
> From: Martin Sperl <kernel at martin.sperl.org>
>
> When using reverse polarity for clock (spi-cpol) on a device
> the clock line gets altered after chip-select has been asserted
> resulting in an additional clock beat, which confuses hardware.
>
> This did not show when using native-CS, as the same register
> is used to control cs as well as polarity, so the changes came
> into effect at the same time. Unfortunately this is not true
> with gpio-cs.
>
> To avoid this situation this patch moves the setup of polarity
> (spi-cpol and spi-cpha) outside of the chip-select into
> prepare_message, which is run prior to asserting chip-select.
>
> Also fixes resetting 3-wire mode after use of rx-mode, so that
> a 3-Wire sequence TX, RX, TX works as well (right now it runs
> TX, RX, RX instead)
>
> Reported-by: Noralf Tronnes <noralf at tronnes.org>
> Signed-off-by: Martin Sperl <kernel at martin.sperl.org>
> ---
>   drivers/spi/spi-bcm2835.c |   28 +++++++++++++++++++++++-----
>   1 file changed, 23 insertions(+), 5 deletions(-)
>
> See also the images for the effect that this patch has on the signals:
> https://github.com/raspberrypi/linux/pull/1077#issuecomment-125484500
>
> diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c
> index 59705ab..c9357bb 100644
> --- a/drivers/spi/spi-bcm2835.c
> +++ b/drivers/spi/spi-bcm2835.c
> @@ -553,13 +553,11 @@ static int bcm2835_spi_transfer_one(struct spi_master *master,
>   	spi_used_hz = cdiv ? (clk_hz / cdiv) : (clk_hz / 65536);
>   	bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv);
>
> -	/* handle all the modes */
> +	/* handle all the 3-wire mode */
>   	if ((spi->mode & SPI_3WIRE) && (tfr->rx_buf))
>   		cs |= BCM2835_SPI_CS_REN;
> -	if (spi->mode & SPI_CPOL)
> -		cs |= BCM2835_SPI_CS_CPOL;
> -	if (spi->mode & SPI_CPHA)
> -		cs |= BCM2835_SPI_CS_CPHA;
> +	else
> +		cs &= ~BCM2835_SPI_CS_REN;
>
>   	/* for gpio_cs set dummy CS so that no HW-CS get changed
>   	 * we can not run this in bcm2835_spi_set_cs, as it does
> @@ -592,6 +590,25 @@ static int bcm2835_spi_transfer_one(struct spi_master *master,
>   	return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs);
>   }
>
> +static int bcm2835_spi_prepare_message(struct spi_master *master,
> +				       struct spi_message *msg)
> +{
> +	struct spi_device *spi = msg->spi;
> +	struct bcm2835_spi *bs = spi_master_get_devdata(master);
> +	u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
> +
> +	cs &= ~(BCM2835_SPI_CS_CPOL | BCM2835_SPI_CS_CPHA);
> +
> +	if (spi->mode & SPI_CPOL)
> +		cs |= BCM2835_SPI_CS_CPOL;
> +	if (spi->mode & SPI_CPHA)
> +		cs |= BCM2835_SPI_CS_CPHA;
> +
> +	bcm2835_wr(bs, BCM2835_SPI_CS, cs);
> +
> +	return 0;
> +}
> +
>   static void bcm2835_spi_handle_err(struct spi_master *master,
>   				   struct spi_message *msg)
>   {
> @@ -739,6 +756,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
>   	master->set_cs = bcm2835_spi_set_cs;
>   	master->transfer_one = bcm2835_spi_transfer_one;
>   	master->handle_err = bcm2835_spi_handle_err;
> +	master->prepare_message = bcm2835_spi_prepare_message;
>   	master->dev.of_node = pdev->dev.of_node;
>
>   	bs = spi_master_get_devdata(master);
>


From broonie at kernel.org  Tue Aug  4 04:08:59 2015
From: broonie at kernel.org (Mark Brown)
Date: Tue, 4 Aug 2015 12:08:59 +0100
Subject: [PATCH] spi: bcm2835: set up spi-mode before asserting cs-gpio
In-Reply-To: <55C0902B.2040006@martin.sperl.org>
References: <1438092193-31799-1-git-send-email-kernel@martin.sperl.org>
 <55C0902B.2040006@martin.sperl.org>
Message-ID: <20150804110859.GK20873@sirena.org.uk>

On Tue, Aug 04, 2015 at 12:12:59PM +0200, Martin Sperl wrote:
> Any feedback on this patch? Can this get merged?
> 
> Noralf (via private communication) has confirmed that with
> this patch his fbtft device (which uses spi-cpol and spi-cpha)
> works.

> On 28.07.2015 16:03, kernel at martin.sperl.org wrote:

Please don't top post or send content free pings, and please allow a
reasonable time for review - you only posted this last week.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150804/8c124f08/attachment.sig>

From f.fainelli at gmail.com  Wed Aug  5 10:14:40 2015
From: f.fainelli at gmail.com (Florian Fainelli)
Date: Wed, 05 Aug 2015 10:14:40 -0700
Subject: [PATCH 1/2] MAINTAINERS: Add myself as a bcm2835 co-maintainer.
In-Reply-To: <1437594937-28583-1-git-send-email-eric@anholt.net>
References: <1437594937-28583-1-git-send-email-eric@anholt.net>
Message-ID: <55C24480.7080102@gmail.com>

On 22/07/15 12:55, Eric Anholt wrote:
> The current maintainers have limited free time to work on the
> architecture, and I'm motivated to do so for my work on graphics for
> Broadcom.  Arnd and Florian suggested to me that this might be the way
> forward.
> 
> Signed-off-by: Eric Anholt <eric at anholt.net>

Applied to maintainers/next with Stephen's Ack at
github.com/Broadcom/stblinux, will submit this shortly with other
Broadcom SoC changes, thanks!

> ---
>  MAINTAINERS | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 2d3d55c..1c51379 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2217,6 +2217,7 @@ F:	drivers/clocksource/bcm_kona_timer.c
>  BROADCOM BCM2835 ARM ARCHITECTURE
>  M:	Stephen Warren <swarren at wwwdotorg.org>
>  M:	Lee Jones <lee at kernel.org>
> +M:	Eric Anholt <eric at anholt.net>
>  L:	linux-rpi-kernel at lists.infradead.org (moderated for non-subscribers)
>  T:	git git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git
>  S:	Maintained
> 


-- 
Florian


From f.fainelli at gmail.com  Wed Aug  5 10:15:12 2015
From: f.fainelli at gmail.com (Florian Fainelli)
Date: Wed, 05 Aug 2015 10:15:12 -0700
Subject: [PATCH 2/2] MAINTAINERS: Explicitly add linux-arm-kernel for
 bcm2835
In-Reply-To: <1437594937-28583-2-git-send-email-eric@anholt.net>
References: <1437594937-28583-1-git-send-email-eric@anholt.net>
 <1437594937-28583-2-git-send-email-eric@anholt.net>
Message-ID: <55C244A0.1060101@gmail.com>

On 22/07/15 12:55, Eric Anholt wrote:
> get_maintainers.pl would list linux-arm-kernel for most bcm2835
> changes because it's under arch/arm, but might not for other bcm2835
> drivers.  Most ARM architectures appear to list linux-arm-kernel as an
> appropriate list as well.
> 
> Signed-off-by: Eric Anholt <eric at anholt.net>
> Suggested-by: Stephen Warren <swarren at wwwdotorg.org>

Also applied, thanks!
-- 
Florian


From eric at anholt.net  Thu Aug  6 16:00:29 2015
From: eric at anholt.net (Eric Anholt)
Date: Thu,  6 Aug 2015 16:00:29 -0700
Subject: Raspberry Pi 2 interrupt controller support (v4)
Message-ID: <1438902033-31477-1-git-send-email-eric@anholt.net>

https://github.com/anholt/linux/tree/bcm2836-irqchip



From eric at anholt.net  Thu Aug  6 16:00:31 2015
From: eric at anholt.net (Eric Anholt)
Date: Thu,  6 Aug 2015 16:00:31 -0700
Subject: [PATCH v4 2/4] irqchip: bcm2835: Add support for being used as a
 second level controller.
In-Reply-To: <1438902033-31477-1-git-send-email-eric@anholt.net>
References: <1438902033-31477-1-git-send-email-eric@anholt.net>
Message-ID: <1438902033-31477-3-git-send-email-eric@anholt.net>

The BCM2836 (Raspberry Pi 2) uses two levels of interrupt handling
with the CPU-local interrupts being the root, so we need to register
ours as chained off of the CPU's local interrupt.

Signed-off-by: Eric Anholt <eric at anholt.net>
Acked-by: Stephen Warren <swarren at wwwdotorg.org>
---

v3: Use a separate compatible value to indicate that the parent
    interrupt controller irq is required (Requested by Stephen Warren)

 .../brcm,bcm2835-armctrl-ic.txt                    | 25 ++++++++++++-
 drivers/irqchip/irq-bcm2835.c                      | 43 ++++++++++++++++++++--
 2 files changed, 64 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
index 7da578d..2d6c8bb 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
@@ -5,9 +5,14 @@ The BCM2835 contains a custom top-level interrupt controller, which supports
 controller, or the HW block containing it, is referred to occasionally
 as "armctrl" in the SoC documentation, hence naming of this binding.
 
+The BCM2836 contains the same interrupt controller with the same
+interrupts, but the per-CPU interrupt controller is the root, and an
+interrupt there indicates that the ARMCTRL has an interrupt to handle.
+
 Required properties:
 
-- compatible : should be "brcm,bcm2835-armctrl-ic"
+- compatible : should be "brcm,bcm2835-armctrl-ic" or
+                 "brcm,bcm2836-armctrl-ic"
 - reg : Specifies base physical address and size of the registers.
 - interrupt-controller : Identifies the node as an interrupt controller
 - #interrupt-cells : Specifies the number of cells needed to encode an
@@ -20,6 +25,12 @@ Required properties:
   The 2nd cell contains the interrupt number within the bank. Valid values
   are 0..7 for bank 0, and 0..31 for bank 1.
 
+Additional required properties for brcm,bcm2836-armctrl-ic:
+- interrupt-parent : Specifies the parent interrupt controller when this
+  controller is the second level.
+- interrupts : Specifies the interrupt on the parent for this interrupt
+  controller to handle.
+
 The interrupt sources are as follows:
 
 Bank 0:
@@ -102,9 +113,21 @@ Bank 2:
 
 Example:
 
+/* BCM2835, first level */
 intc: interrupt-controller {
 	compatible = "brcm,bcm2835-armctrl-ic";
 	reg = <0x7e00b200 0x200>;
 	interrupt-controller;
 	#interrupt-cells = <2>;
 };
+
+/* BCM2836, second level */
+intc: interrupt-controller {
+	compatible = "brcm,bcm2836-armctrl-ic";
+	reg = <0x7e00b200 0x200>;
+	interrupt-controller;
+	#interrupt-cells = <2>;
+
+	interrupt-parent = <&local_intc>;
+	interrupts = <8>;
+};
diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c
index 382450a..f968d3f 100644
--- a/drivers/irqchip/irq-bcm2835.c
+++ b/drivers/irqchip/irq-bcm2835.c
@@ -97,6 +97,7 @@ struct armctrl_ic {
 static struct armctrl_ic intc __read_mostly;
 static void __exception_irq_entry bcm2835_handle_irq(
 	struct pt_regs *regs);
+static void bcm2836_chained_handle_irq(unsigned int irq, struct irq_desc *desc);
 
 static void armctrl_mask_irq(struct irq_data *d)
 {
@@ -140,7 +141,8 @@ static const struct irq_domain_ops armctrl_ops = {
 };
 
 static int __init armctrl_of_init(struct device_node *node,
-	struct device_node *parent)
+				  struct device_node *parent,
+				  bool is_2836)
 {
 	void __iomem *base;
 	int irq, b, i;
@@ -169,10 +171,34 @@ static int __init armctrl_of_init(struct device_node *node,
 		}
 	}
 
-	set_handle_irq(bcm2835_handle_irq);
+	if (is_2836) {
+		int parent_irq = irq_of_parse_and_map(node, 0);
+
+		if (!parent_irq) {
+			panic("%s: unable to get parent interrupt.\n",
+			      node->full_name);
+		}
+		irq_set_chained_handler(parent_irq, bcm2836_chained_handle_irq);
+	} else {
+		set_handle_irq(bcm2835_handle_irq);
+	}
+
 	return 0;
 }
 
+static int __init bcm2835_armctrl_of_init(struct device_node *node,
+					  struct device_node *parent)
+{
+	return armctrl_of_init(node, parent, false);
+}
+
+static int __init bcm2836_armctrl_of_init(struct device_node *node,
+					  struct device_node *parent)
+{
+	return armctrl_of_init(node, parent, true);
+}
+
+
 /*
  * Handle each interrupt across the entire interrupt controller.  This reads the
  * status register before handling each interrupt, which is necessary given that
@@ -220,4 +246,15 @@ static void __exception_irq_entry bcm2835_handle_irq(
 		handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs);
 }
 
-IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic", armctrl_of_init);
+static void bcm2836_chained_handle_irq(unsigned int irq, struct irq_desc *desc)
+{
+	u32 hwirq;
+
+	while ((hwirq = get_next_armctrl_hwirq()) != ~0)
+		generic_handle_irq(irq_linear_revmap(intc.domain, hwirq));
+}
+
+IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic",
+		bcm2835_armctrl_of_init);
+IRQCHIP_DECLARE(bcm2836_armctrl_ic, "brcm,bcm2836-armctrl-ic",
+		bcm2836_armctrl_of_init);
-- 
2.1.4



From eric at anholt.net  Thu Aug  6 16:00:32 2015
From: eric at anholt.net (Eric Anholt)
Date: Thu,  6 Aug 2015 16:00:32 -0700
Subject: [PATCH v4 3/4] irqchip: Add documentation for the bcm2836 interrupt
 controller.
In-Reply-To: <1438902033-31477-1-git-send-email-eric@anholt.net>
References: <1438902033-31477-1-git-send-email-eric@anholt.net>
Message-ID: <1438902033-31477-4-git-send-email-eric@anholt.net>

This is a new per-cpu root interrupt controller on the Raspberry Pi 2,
which will chain to the bcm2835 interrupt controller for peripheral
interrupts.

Signed-off-by: Eric Anholt <eric at anholt.net>
Acked-by: Stephen Warren <swarren at wwwdotorg.org>
---
 .../interrupt-controller/brcm,bcm2836-l1-intc.txt  | 37 ++++++++++++++++++++++
 1 file changed, 37 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2836-l1-intc.txt

diff --git a/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2836-l1-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2836-l1-intc.txt
new file mode 100644
index 0000000..f320dcd
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2836-l1-intc.txt
@@ -0,0 +1,37 @@
+BCM2836 per-CPU interrupt controller
+
+The BCM2836 has a per-cpu interrupt controller for the timer, PMU
+events, and SMP IPIs.  One of the CPUs may receive interrupts for the
+peripheral (GPU) events, which chain to the BCM2835-style interrupt
+controller.
+
+Required properties:
+
+- compatible:	 	Should be "brcm,bcm2836-l1-intc"
+- reg:			Specifies base physical address and size of the
+			  registers
+- interrupt-controller:	Identifies the node as an interrupt controller
+- #interrupt-cells:	Specifies the number of cells needed to encode an
+			  interrupt source. The value shall be 1
+
+Please refer to interrupts.txt in this directory for details of the common
+Interrupt Controllers bindings used by client devices.
+
+The interrupt sources are as follows:
+
+0: CNTPSIRQ
+1: CNTPNSIRQ
+2: CNTHPIRQ
+3: CNTVIRQ
+8: GPU_FAST
+9: PMU_FAST
+
+Example:
+
+local_intc: local_intc {
+	compatible = "brcm,bcm2836-l1-intc";
+	reg = <0x40000000 0x100>;
+	interrupt-controller;
+	#interrupt-cells = <1>;
+	interrupt-parent = <&local_intc>;
+};
-- 
2.1.4



From eric at anholt.net  Thu Aug  6 16:00:30 2015
From: eric at anholt.net (Eric Anholt)
Date: Thu,  6 Aug 2015 16:00:30 -0700
Subject: [PATCH v4 1/4] irqchip: bcm2835: Refactor handle_IRQ() calls out of
 MAKE_HWIRQ.
In-Reply-To: <1438902033-31477-1-git-send-email-eric@anholt.net>
References: <1438902033-31477-1-git-send-email-eric@anholt.net>
Message-ID: <1438902033-31477-2-git-send-email-eric@anholt.net>

For BCM2836, we want to chain into this IRQ chip from the root
controller, and for chaining we need to do something else instead of
handle_IRQ() once we have decoded the IRQ.

Note that this changes the behavior a little bit: Previously for a
non-shortcut IRQ, we'd loop reading and handling the second level IRQ
status until it was cleared before returning to the loop reading the
top level IRQ status (Note that the top level bit is just an OR of the
low level bits).  For the expected case of just one interrupt to be
handled, this was an extra register read, so we're down from 4 to 3
reads.

Signed-off-by: Eric Anholt <eric at anholt.net>
Acked-by: Stephen Warren <swarren at wwwdotorg.org>
---
 drivers/irqchip/irq-bcm2835.c | 57 ++++++++++++++++++++++---------------------
 1 file changed, 29 insertions(+), 28 deletions(-)

diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c
index e68c3b6..382450a 100644
--- a/drivers/irqchip/irq-bcm2835.c
+++ b/drivers/irqchip/irq-bcm2835.c
@@ -179,44 +179,45 @@ static int __init armctrl_of_init(struct device_node *node,
  * handle_IRQ may briefly re-enable interrupts for soft IRQ handling.
  */
 
-static void armctrl_handle_bank(int bank, struct pt_regs *regs)
+static u32 armctrl_translate_bank(int bank)
 {
-	u32 stat, irq;
+	u32 stat = readl_relaxed(intc.pending[bank]);
 
-	while ((stat = readl_relaxed(intc.pending[bank]))) {
-		irq = MAKE_HWIRQ(bank, ffs(stat) - 1);
-		handle_IRQ(irq_linear_revmap(intc.domain, irq), regs);
-	}
+	return MAKE_HWIRQ(bank, ffs(stat) - 1);
+}
+
+static u32 armctrl_translate_shortcut(int bank, u32 stat)
+{
+	return MAKE_HWIRQ(bank, shortcuts[ffs(stat >> SHORTCUT_SHIFT) - 1]);
 }
 
-static void armctrl_handle_shortcut(int bank, struct pt_regs *regs,
-	u32 stat)
+static u32 get_next_armctrl_hwirq(void)
 {
-	u32 irq = MAKE_HWIRQ(bank, shortcuts[ffs(stat >> SHORTCUT_SHIFT) - 1]);
-	handle_IRQ(irq_linear_revmap(intc.domain, irq), regs);
+	u32 stat = readl_relaxed(intc.pending[0]) & BANK0_VALID_MASK;
+
+	if (stat == 0)
+		return ~0;
+	else if (stat & BANK0_HWIRQ_MASK)
+		return MAKE_HWIRQ(0, ffs(stat & BANK0_HWIRQ_MASK) - 1);
+	else if (stat & SHORTCUT1_MASK)
+		return armctrl_translate_shortcut(1, stat & SHORTCUT1_MASK);
+	else if (stat & SHORTCUT2_MASK)
+		return armctrl_translate_shortcut(2, stat & SHORTCUT2_MASK);
+	else if (stat & BANK1_HWIRQ)
+		return armctrl_translate_bank(1);
+	else if (stat & BANK2_HWIRQ)
+		return armctrl_translate_bank(2);
+	else
+		BUG();
 }
 
 static void __exception_irq_entry bcm2835_handle_irq(
 	struct pt_regs *regs)
 {
-	u32 stat, irq;
-
-	while ((stat = readl_relaxed(intc.pending[0]) & BANK0_VALID_MASK)) {
-		if (stat & BANK0_HWIRQ_MASK) {
-			irq = MAKE_HWIRQ(0, ffs(stat & BANK0_HWIRQ_MASK) - 1);
-			handle_IRQ(irq_linear_revmap(intc.domain, irq), regs);
-		} else if (stat & SHORTCUT1_MASK) {
-			armctrl_handle_shortcut(1, regs, stat & SHORTCUT1_MASK);
-		} else if (stat & SHORTCUT2_MASK) {
-			armctrl_handle_shortcut(2, regs, stat & SHORTCUT2_MASK);
-		} else if (stat & BANK1_HWIRQ) {
-			armctrl_handle_bank(1, regs);
-		} else if (stat & BANK2_HWIRQ) {
-			armctrl_handle_bank(2, regs);
-		} else {
-			BUG();
-		}
-	}
+	u32 hwirq;
+
+	while ((hwirq = get_next_armctrl_hwirq()) != ~0)
+		handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs);
 }
 
 IRQCHIP_DECLARE(bcm2835_armctrl_ic, "brcm,bcm2835-armctrl-ic", armctrl_of_init);
-- 
2.1.4



From eric at anholt.net  Thu Aug  6 16:00:33 2015
From: eric at anholt.net (Eric Anholt)
Date: Thu,  6 Aug 2015 16:00:33 -0700
Subject: [PATCH v4 4/4] irqchip: Add bcm2836 interrupt controller for
 Raspberry Pi 2.
In-Reply-To: <1438902033-31477-1-git-send-email-eric@anholt.net>
References: <1438902033-31477-1-git-send-email-eric@anholt.net>
Message-ID: <1438902033-31477-5-git-send-email-eric@anholt.net>

This interrupt controller is the new root interrupt controller with
the timer, PMU events, and IPIs, and the bcm2835's interrupt
controller is chained off of it to handle the peripherals.

I wrote the interrupt chip support, while Andrea Merello wrote the IPI
code.

Signed-off-by: Eric Anholt <eric at anholt.net>
Signed-off-by: Andrea Merello <andrea.merello at gmail.com>
Acked-by: Stephen Warren <swarren at wwwdotorg.org>
---

v2: Rename functions/globals to bcm2836_arm_local.  Treat per-cpu
    interrupts as per-cpu in mask/unmask.  Add comments about how
    registers work.
v3: Drop debug printfs from PMU interrupt masking.
v4: Use cpu notifiers to unmask IPIs only when Linux wants the CPU up.

 drivers/irqchip/Makefile      |   1 +
 drivers/irqchip/irq-bcm2836.c | 275 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 276 insertions(+)
 create mode 100644 drivers/irqchip/irq-bcm2836.c

diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index b8d4e96..9727681 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -1,6 +1,7 @@
 obj-$(CONFIG_IRQCHIP)			+= irqchip.o
 
 obj-$(CONFIG_ARCH_BCM2835)		+= irq-bcm2835.o
+obj-$(CONFIG_ARCH_BCM2835)		+= irq-bcm2836.o
 obj-$(CONFIG_ARCH_EXYNOS)		+= exynos-combiner.o
 obj-$(CONFIG_ARCH_HIP04)		+= irq-hip04.o
 obj-$(CONFIG_ARCH_MMP)			+= irq-mmp.o
diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c
new file mode 100644
index 0000000..052bd83
--- /dev/null
+++ b/drivers/irqchip/irq-bcm2836.c
@@ -0,0 +1,275 @@
+/*
+ * Root interrupt controller for the BCM2836 (Raspberry Pi 2).
+ *
+ * Copyright 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <asm/exception.h>
+
+/*
+ * The low 2 bits identify the CPU that the GPU IRQ goes to, and the
+ * next 2 bits identify the CPU that the GPU FIQ goes to.
+ */
+#define LOCAL_GPU_ROUTING		0x00c
+/* When setting bits 0-3, enables PMU interrupts on that CPU. */
+#define LOCAL_PM_ROUTING_SET		0x010
+/* When setting bits 0-3, disables PMU interrupts on that CPU. */
+#define LOCAL_PM_ROUTING_CLR		0x014
+/*
+ * The low 4 bits of this are the CPU's timer IRQ enables, and the
+ * next 4 bits are the CPU's timer FIQ enables (which override the IRQ
+ * bits).
+ */
+#define LOCAL_TIMER_INT_CONTROL0	0x040
+/*
+ * The low 4 bits of this are the CPU's per-mailbox IRQ enables, and
+ * the next 4 bits are the CPU's per-mailbox FIQ enables (which
+ * override the IRQ bits).
+ */
+#define LOCAL_MAILBOX_INT_CONTROL0	0x050
+/*
+ * The CPU's interrupt status register.  Bits are defined by the the
+ * LOCAL_IRQ_* bits below.
+ */
+#define LOCAL_IRQ_PENDING0		0x060
+/* Same status bits as above, but for FIQ. */
+#define LOCAL_FIQ_PENDING0		0x070
+/*
+ * Mailbox0 write-to-set bits.  There are 16 mailboxes, 4 per CPU, and
+ * these bits are organized by mailbox number and then CPU number.  We
+ * use mailbox 0 for IPIs.  The mailbox's interrupt is raised while
+ * any bit is set.
+ */
+#define LOCAL_MAILBOX0_SET0		0x080
+/* Mailbox0 write-to-clear bits. */
+#define LOCAL_MAILBOX0_CLR0		0x0c0
+
+#define LOCAL_IRQ_CNTPSIRQ	0
+#define LOCAL_IRQ_CNTPNSIRQ	1
+#define LOCAL_IRQ_CNTHPIRQ	2
+#define LOCAL_IRQ_CNTVIRQ	3
+#define LOCAL_IRQ_MAILBOX0	4
+#define LOCAL_IRQ_MAILBOX1	5
+#define LOCAL_IRQ_MAILBOX2	6
+#define LOCAL_IRQ_MAILBOX3	7
+#define LOCAL_IRQ_GPU_FAST	8
+#define LOCAL_IRQ_PMU_FAST	9
+#define LAST_IRQ		LOCAL_IRQ_PMU_FAST
+
+struct bcm2836_arm_irqchip_intc {
+	struct irq_domain *domain;
+	void __iomem *base;
+};
+
+static struct bcm2836_arm_irqchip_intc intc  __read_mostly;
+
+static void bcm2836_arm_irqchip_mask_per_cpu_irq(unsigned int reg_offset,
+						 unsigned int bit,
+						 int cpu)
+{
+	void __iomem *reg = intc.base + reg_offset + 4 * cpu;
+
+	writel(readl(reg) & ~BIT(bit), reg);
+}
+
+static void bcm2836_arm_irqchip_unmask_per_cpu_irq(unsigned int reg_offset,
+						   unsigned int bit,
+						 int cpu)
+{
+	void __iomem *reg = intc.base + reg_offset + 4 * cpu;
+
+	writel(readl(reg) | BIT(bit), reg);
+}
+
+static void bcm2836_arm_irqchip_mask_timer_irq(struct irq_data *d)
+{
+	bcm2836_arm_irqchip_mask_per_cpu_irq(LOCAL_TIMER_INT_CONTROL0,
+					     d->hwirq - LOCAL_IRQ_CNTPSIRQ,
+					     smp_processor_id());
+}
+
+static void bcm2836_arm_irqchip_unmask_timer_irq(struct irq_data *d)
+{
+	bcm2836_arm_irqchip_unmask_per_cpu_irq(LOCAL_TIMER_INT_CONTROL0,
+					       d->hwirq - LOCAL_IRQ_CNTPSIRQ,
+					       smp_processor_id());
+}
+
+static struct irq_chip bcm2836_arm_irqchip_timer = {
+	.name = "bcm2836-timer",
+	.irq_mask = bcm2836_arm_irqchip_mask_timer_irq,
+	.irq_unmask = bcm2836_arm_irqchip_unmask_timer_irq
+};
+
+static void bcm2836_arm_irqchip_mask_pmu_irq(struct irq_data *d)
+{
+	writel(1 << smp_processor_id(), intc.base + LOCAL_PM_ROUTING_CLR);
+}
+
+static void bcm2836_arm_irqchip_unmask_pmu_irq(struct irq_data *d)
+{
+	writel(1 << smp_processor_id(), intc.base + LOCAL_PM_ROUTING_SET);
+}
+
+static struct irq_chip bcm2836_arm_irqchip_pmu = {
+	.name = "bcm2836-pmu",
+	.irq_mask = bcm2836_arm_irqchip_mask_pmu_irq,
+	.irq_unmask = bcm2836_arm_irqchip_unmask_pmu_irq
+};
+
+static void bcm2836_arm_irqchip_mask_gpu_irq(struct irq_data *d)
+{
+}
+
+static void bcm2836_arm_irqchip_unmask_gpu_irq(struct irq_data *d)
+{
+}
+
+static struct irq_chip bcm2836_arm_irqchip_gpu = {
+	.name = "bcm2836-gpu",
+	.irq_mask = bcm2836_arm_irqchip_mask_gpu_irq,
+	.irq_unmask = bcm2836_arm_irqchip_unmask_gpu_irq
+};
+
+static void bcm2836_arm_irqchip_register_irq(int hwirq, struct irq_chip *chip)
+{
+	int irq = irq_create_mapping(intc.domain, hwirq);
+
+	irq_set_percpu_devid(irq);
+	irq_set_chip_and_handler(irq, chip, handle_percpu_devid_irq);
+	irq_set_status_flags(irq, IRQ_NOAUTOEN);
+}
+
+static void __exception_irq_entry bcm2836_arm_irqchip_handle_irq(
+	struct pt_regs *regs)
+{
+	int cpu = smp_processor_id();
+	u32 stat;
+
+	stat = readl_relaxed(intc.base + LOCAL_IRQ_PENDING0 + 4 * cpu);
+	if (stat & 0x10) {
+#ifdef CONFIG_SMP
+		void __iomem *mailbox0 = (intc.base +
+					  LOCAL_MAILBOX0_CLR0 + 16 * cpu);
+		u32 mbox_val = readl(mailbox0);
+		u32 ipi = ffs(mbox_val) - 1;
+
+		writel(1 << ipi, mailbox0);
+		handle_IPI(ipi, regs);
+#endif
+	} else {
+		u32 hwirq = ffs(stat) - 1;
+
+		handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs);
+	}
+}
+
+#ifdef CONFIG_SMP
+static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask,
+					 unsigned int ipi)
+{
+	int cpu;
+	void __iomem *mailbox0_base = intc.base + LOCAL_MAILBOX0_SET0;
+
+	/*
+	 * Ensure that stores to normal memory are visible to the
+	 * other CPUs before issuing the IPI.
+	 */
+	dsb();
+
+	for_each_cpu(cpu, mask)	{
+		writel(1 << ipi, mailbox0_base + 16 * cpu);
+	}
+}
+
+/* Unmasks the IPI on the CPU when it's online. */
+static int bcm2836_arm_irqchip_cpu_notify(struct notifier_block *nfb,
+					  unsigned long action, void *hcpu)
+{
+	unsigned int cpu = (unsigned long)hcpu;
+	unsigned int int_reg = LOCAL_MAILBOX_INT_CONTROL0;
+	unsigned int mailbox = 0;
+
+	if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
+		bcm2836_arm_irqchip_unmask_per_cpu_irq(int_reg, mailbox, cpu);
+	else if (action == CPU_DYING)
+		bcm2836_arm_irqchip_mask_per_cpu_irq(int_reg, mailbox, cpu);
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block bcm2836_arm_irqchip_cpu_notifier = {
+	.notifier_call = bcm2836_arm_irqchip_cpu_notify,
+	.priority = 100,
+};
+#endif
+
+static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = {
+	.xlate = irq_domain_xlate_onecell
+};
+
+static void
+bcm2836_arm_irqchip_smp_init(void)
+{
+#ifdef CONFIG_SMP
+	/* Unmask IPIs to the boot CPU. */
+	bcm2836_arm_irqchip_cpu_notify(&bcm2836_arm_irqchip_cpu_notifier,
+				       CPU_STARTING,
+				       (void *)smp_processor_id());
+	register_cpu_notifier(&bcm2836_arm_irqchip_cpu_notifier);
+
+	set_smp_cross_call(bcm2836_arm_irqchip_send_ipi);
+#endif
+}
+
+static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node,
+						      struct device_node *parent)
+{
+	intc.base = of_iomap(node, 0);
+	if (!intc.base) {
+		panic("%s: unable to map local interrupt registers\n",
+			node->full_name);
+	}
+
+	intc.domain = irq_domain_add_linear(node, LAST_IRQ + 1,
+					    &bcm2836_arm_irqchip_intc_ops,
+					    NULL);
+	if (!intc.domain)
+		panic("%s: unable to create IRQ domain\n", node->full_name);
+
+	bcm2836_arm_irqchip_register_irq(LOCAL_IRQ_CNTPSIRQ,
+					 &bcm2836_arm_irqchip_timer);
+	bcm2836_arm_irqchip_register_irq(LOCAL_IRQ_CNTPNSIRQ,
+					 &bcm2836_arm_irqchip_timer);
+	bcm2836_arm_irqchip_register_irq(LOCAL_IRQ_CNTHPIRQ,
+					 &bcm2836_arm_irqchip_timer);
+	bcm2836_arm_irqchip_register_irq(LOCAL_IRQ_CNTVIRQ,
+					 &bcm2836_arm_irqchip_timer);
+	bcm2836_arm_irqchip_register_irq(LOCAL_IRQ_GPU_FAST,
+					 &bcm2836_arm_irqchip_gpu);
+	bcm2836_arm_irqchip_register_irq(LOCAL_IRQ_PMU_FAST,
+					 &bcm2836_arm_irqchip_pmu);
+
+	bcm2836_arm_irqchip_smp_init();
+
+	set_handle_irq(bcm2836_arm_irqchip_handle_irq);
+	return 0;
+}
+
+IRQCHIP_DECLARE(bcm2836_arm_irqchip_l1_intc, "brcm,bcm2836-l1-intc",
+		bcm2836_arm_irqchip_l1_intc_of_init);
-- 
2.1.4



From f.fainelli at gmail.com  Wed Aug  5 10:44:41 2015
From: f.fainelli at gmail.com (Florian Fainelli)
Date: Wed,  5 Aug 2015 10:44:41 -0700
Subject: [GIT PULL] Broadcom Device Tree changes for 4.3 (part 2)
Message-ID: <1438796683-18581-1-git-send-email-f.fainelli@gmail.com>

The following changes since commit ee224e175e9d837b3d572e863ac96109b58707ac:

  Merge tag 'mvebu-dt-4.3-2' of git://git.infradead.org/linux-mvebu into next/dt (2015-08-05 11:10:30 +0200)

are available in the git repository at:

  http://github.com/broadcom/stblinux tags/arm-soc/for-4.3/dts-part2

for you to fetch changes up to 62c5fd2c319ae72e82abc3de689d8a8a4df5a3e9:

  ARM: dts: brcmstb: add BCM7445 GPIO nodes (2015-08-05 10:20:23 -0700)

----------------------------------------------------------------
This pull requests contains the following changes:

- Hauke enables additional I and D cache line prefetching for the BCM5301X L2
  cache controller (PL310)

- Felix adds the Cortex-A9 PMU DT nodes to allow profiling on BCM53101X

- Gregory adds L2 interrupt controllers and GPIO nodes to the reference BCM7445
  Device Tree include file

----------------------------------------------------------------
Felix Fietkau (1):
      ARM: BCM5301X: Add profiling support

Gregory Fong (1):
      ARM: dts: brcmstb: add BCM7445 GPIO nodes

Hauke Mehrtens (1):
      ARM: BCM5301X: activate some additional options in pl310 cache controller

 arch/arm/boot/dts/bcm5301x.dtsi | 10 +++++++++
 arch/arm/boot/dts/bcm7445.dtsi  | 50 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 60 insertions(+)


From f.fainelli at gmail.com  Wed Aug  5 10:44:42 2015
From: f.fainelli at gmail.com (Florian Fainelli)
Date: Wed,  5 Aug 2015 10:44:42 -0700
Subject: [GIT PULL] Broadcom MAINTAINERS file updates for 4.3
In-Reply-To: <1438796683-18581-1-git-send-email-f.fainelli@gmail.com>
References: <1438796683-18581-1-git-send-email-f.fainelli@gmail.com>
Message-ID: <1438796683-18581-2-git-send-email-f.fainelli@gmail.com>

The following changes since commit d770e558e21961ad6cfdf0ff7df0eb5d7d4f0754:

  Linux 4.2-rc1 (2015-07-05 11:01:52 -0700)

are available in the git repository at:

  http://github.com/broadcom/stblinux tags/arm-soc/for-4.3/maintainers

for you to fetch changes up to 824811293538f5cafaa6d19e3458a13eafa92a32:

  MAINTAINERS: Explicitly add linux-arm-kernel for bcm2835 (2015-08-05 10:14:00 -0700)

----------------------------------------------------------------
This pull request contains the following changes to the MAINTAINERS file:

- Add Eric Anholt as a 3rd maintainer for the Raspberry Pi platform code/drivers
- Add linux-arm-kernel for all bcm2835 related drivers

----------------------------------------------------------------
Eric Anholt (2):
      MAINTAINERS: Add myself as a bcm2835 co-maintainer.
      MAINTAINERS: Explicitly add linux-arm-kernel for bcm2835

 MAINTAINERS | 2 ++
 1 file changed, 2 insertions(+)


From f.fainelli at gmail.com  Wed Aug  5 10:44:43 2015
From: f.fainelli at gmail.com (Florian Fainelli)
Date: Wed,  5 Aug 2015 10:44:43 -0700
Subject: [GIT PULL] Broadcom SoC changes for 4.3 (part 2)
In-Reply-To: <1438796683-18581-1-git-send-email-f.fainelli@gmail.com>
References: <1438796683-18581-1-git-send-email-f.fainelli@gmail.com>
Message-ID: <1438796683-18581-3-git-send-email-f.fainelli@gmail.com>

The following changes since commit 39aa437e18cac7acae55481ed73feea0de497399:

  Merge branch 'queue/irq/arm' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip into next/cleanup (2015-08-05 17:24:11 +0200)

are available in the git repository at:

  http://github.com/broadcom/stblinux tags/arm-soc/for-4.3/soc-part2

for you to fetch changes up to 661e59d30168331e5e1c7386d23ea8fd14965270:

  ARM: BCM: Enable ARM erratum 798181 for BRCMSTB (2015-08-05 10:30:59 -0700)

----------------------------------------------------------------
This pull request contains the following changes:

- Gregory updates the BRCMSTB Kconfig to select the ARM erratum 798181 which
  affects the chips covered by this platform

----------------------------------------------------------------
Gregory Fong (1):
      ARM: BCM: Enable ARM erratum 798181 for BRCMSTB

 arch/arm/mach-bcm/Kconfig | 1 +
 1 file changed, 1 insertion(+)


From fainelli at broadcom.com  Fri Aug  7 10:20:08 2015
From: fainelli at broadcom.com (Florian Fainelli)
Date: Fri, 7 Aug 2015 10:20:08 -0700
Subject: [GIT PULL] Broadcom SoC changes for 4.3 (part 2)
In-Reply-To: <1438796683-18581-3-git-send-email-f.fainelli@gmail.com>
References: <1438796683-18581-1-git-send-email-f.fainelli@gmail.com>
 <1438796683-18581-3-git-send-email-f.fainelli@gmail.com>
Message-ID: <55C4E8C8.7030503@broadcom.com>

Arnd, Kevin, Olof,

Can you drop this pull request, or if you already merged it, there will
be a second one for a single patch coming in shortly. Thank you!

On 05/08/15 10:44, Florian Fainelli wrote:
> The following changes since commit 39aa437e18cac7acae55481ed73feea0de497399:
> 
>   Merge branch 'queue/irq/arm' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip into next/cleanup (2015-08-05 17:24:11 +0200)
> 
> are available in the git repository at:
> 
>   http://github.com/broadcom/stblinux tags/arm-soc/for-4.3/soc-part2
> 
> for you to fetch changes up to 661e59d30168331e5e1c7386d23ea8fd14965270:
> 
>   ARM: BCM: Enable ARM erratum 798181 for BRCMSTB (2015-08-05 10:30:59 -0700)
> 
> ----------------------------------------------------------------
> This pull request contains the following changes:
> 
> - Gregory updates the BRCMSTB Kconfig to select the ARM erratum 798181 which
>   affects the chips covered by this platform
> 
> ----------------------------------------------------------------
> Gregory Fong (1):
>       ARM: BCM: Enable ARM erratum 798181 for BRCMSTB
> 
>  arch/arm/mach-bcm/Kconfig | 1 +
>  1 file changed, 1 insertion(+)
> 


-- 
Florian


From info at lategoodbye.de  Sun Aug  9 16:52:23 2015
From: info at lategoodbye.de (Stefan Wahren)
Date: Mon, 10 Aug 2015 01:52:23 +0200
Subject: [RFC PATCH] soc: bcm2835: auxiliar devices enable infrastructure
In-Reply-To: <1438677718-19612-1-git-send-email-kernel@martin.sperl.org>
References: <55BC353F.1000906@wwwdotorg.org>
 <1438677718-19612-1-git-send-email-kernel@martin.sperl.org>
Message-ID: <55C7E7B7.3040102@lategoodbye.de>

Hi Martin,

Am 04.08.2015 um 10:41 schrieb kernel at martin.sperl.org:
> From: Martin Sperl <kernel at martin.sperl.org>
>
> [...]
> diff --git a/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt b/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt
> new file mode 100644
> index 0000000..894ff5c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt
> @@ -0,0 +1,16 @@
> +Broadcom BCM2835 auxiliar device enable register
> +
> +the BCM2835 contains 3 auxiliar SPI devices

3 auxiliar devices (spi1, spi2 and uart1) ?

> +which need to get enabled via a shared register.
> +
> +Required properties:
> +- compatible: Should be "brcm,bcm2835-aux-spi".

brcm,bcm2835-aux ?

> +- reg: Should contain register location and length for the enabl

enable ?

> +       register
> +
> +Example:
> +
> +aux_enable: aux_enable at 0x7e215004 {
> +	compatible = "bcrm,bcm2835-aux-enable";
> +	reg = <0x7e215004 0x04>;
> +};
> [...]
> diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
> index 7dc7c0d..c5744e1 100644
> --- a/drivers/soc/Makefile
> +++ b/drivers/soc/Makefile
> @@ -2,6 +2,7 @@
>   # Makefile for the Linux Kernel SOC specific device drivers.
>   #
>
> +obj-$(CONFIG_ARCH_BCM)		+= bcm/
>   obj-$(CONFIG_ARCH_MEDIATEK)	+= mediatek/
>   obj-$(CONFIG_ARCH_QCOM)		+= qcom/
>   obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
> diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig
> new file mode 100644
> index 0000000..b26060d
> --- /dev/null
> +++ b/drivers/soc/bcm/Kconfig
> @@ -0,0 +1,10 @@
> +#
> +# MediaTek SoC drivers

Broadcom ?

> +#
> +config SOC_BCM2835_AUX
> +	tristate "Broadcom BCM2835 aux"
> +	depends on OF
> +	depends on ARCH_BCM2835 || COMPILE_TEST
> +
> +	help
> +	  Support for the BCM2835 auxiliar devices

Specify the type of those devices (SPI/UART) would be very helpful.

> diff --git a/drivers/soc/bcm/Makefile b/drivers/soc/bcm/Makefile
> new file mode 100644
> index 0000000..370a872
> --- /dev/null
> +++ b/drivers/soc/bcm/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_SOC_BCM2835_AUX) += bcm2835-aux.o
> diff --git a/drivers/soc/bcm/bcm2835-aux.c b/drivers/soc/bcm/bcm2835-aux.c
> new file mode 100644
> index 0000000..19e50f8
> --- /dev/null
> +++ b/drivers/soc/bcm/bcm2835-aux.c
> @@ -0,0 +1,155 @@
> +/*
> + * bcm2835-aux
> + *
> + * Copyright (C) 2015 Martin Sperl
> + *
> + * Author: Martin Sperl <kernel at martin.sperl.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/list.h>

This header shouldn't be necessary.

> [...]
> diff --git a/include/linux/soc/bcm/bcm2835-aux.h b/include/linux/soc/bcm/bcm2835-aux.h
> new file mode 100644
> index 0000000..685232c
> --- /dev/null
> +++ b/include/linux/soc/bcm/bcm2835-aux.h
> @@ -0,0 +1,24 @@
> +/*
> + * Copyright (C) 2015 Martin Sperl
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef __BCM2835_AUX_H__
> +#define __BCM2835_AUX_H__
> +
> +#include <linux/device.h>
> +#include <linux/types.h>

I'm not sure but maybe a forward declaration of struct device would be a 
better solution instead of both includes.

> +
> +int bcm2835aux_enable(struct device *dev, const char *property);
> +int bcm2835aux_disable(struct device *dev, const char *property);
> +
> +#endif
>

Regards Stefan


From kernel at martin.sperl.org  Mon Aug 10 00:10:22 2015
From: kernel at martin.sperl.org (Martin Sperl)
Date: Mon, 10 Aug 2015 09:10:22 +0200
Subject: [RFC PATCH] soc: bcm2835: auxiliar devices enable infrastructure
In-Reply-To: <55C7E7B7.3040102@lategoodbye.de>
References: <55BC353F.1000906@wwwdotorg.org>
 <1438677718-19612-1-git-send-email-kernel@martin.sperl.org>
 <55C7E7B7.3040102@lategoodbye.de>
Message-ID: <55C84E5E.6030203@martin.sperl.org>



On 10.08.2015 01:52, Stefan Wahren wrote:
> Hi Martin,
...
> 3 auxiliar devices (spi1, spi2 and uart1) ?
...
> brcm,bcm2835-aux ?
...
> enable ?
...
> Broadcom ?
...
> Specify the type of those devices (SPI/UART) would be very helpful.
...
> This header shouldn't be necessary.
...
>> +#include <linux/device.h>
>> +#include <linux/types.h>
>
> I'm not sure but maybe a forward declaration of struct device would be a
> better solution instead of both includes.
>

Will apply and post a patch...

Thanks,
	Martin


From f.fainelli at gmail.com  Sat Aug  8 12:50:44 2015
From: f.fainelli at gmail.com (Florian Fainelli)
Date: Sat,  8 Aug 2015 12:50:44 -0700
Subject: [GIT PULL] Broadcom SoC changes for 4.3 (part 2, try2)
In-Reply-To: <55C4E8C8.7030503@broadcom.com>
References: <55C4E8C8.7030503@broadcom.com>
Message-ID: <1439063444-12466-1-git-send-email-f.fainelli@gmail.com>

The following changes since commit 39aa437e18cac7acae55481ed73feea0de497399:

  Merge branch 'queue/irq/arm' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip into next/cleanup (2015-08-05 17:24:11 +0200)

are available in the git repository at:

  http://github.com/broadcom/stblinux tags/arm-soc/for-4.3/soc-part2

for you to fetch changes up to cf1140515b3b94ef5aa06a085e94ddd814a8cd78:

  ARM: brcmstb: select ARCH_DMA_ADDR_T_64BIT for LPAE (2015-08-06 21:38:13 -0700)

----------------------------------------------------------------
This pull request contains two changes for brcmstb SoCs from Gregory:

- Update the BRCMSTB Kconfig to select the ARM erratum 798181 which
  affects the chips covered by this platform

- Update the BRCMST Kconfig to select 64-bits DMA address types when LPAE
  is enabled

----------------------------------------------------------------
Gregory Fong (2):
      ARM: BCM: Enable ARM erratum 798181 for BRCMSTB
      ARM: brcmstb: select ARCH_DMA_ADDR_T_64BIT for LPAE

 arch/arm/mach-bcm/Kconfig | 2 ++
 1 file changed, 2 insertions(+)


From kernel at martin.sperl.org  Mon Aug 10 01:41:02 2015
From: kernel at martin.sperl.org (Martin Sperl)
Date: Mon, 10 Aug 2015 10:41:02 +0200
Subject: devicetree: bcm2835:
Message-ID: <55C8639E.4@martin.sperl.org>

Hi!

When adding a gpio section to enable the auxiliar spi device pins
with the correct mode to the bcm2835 device treesimilar to this:
         /* SPI1 pins */
         spi1_pins: spi1_pins {
                 brcm,pins = <19 20 21>;
                 brcm,function = <BCM2835_FSEL_ALT4>
         };
         /* SPI1 GPIO-chipselects */
         spi1_cs_pins: spi1_cs_pins {
                 brcm,pins = <18 17 16>;
                 brcm,function = <BCM2835_FSEL_OUT>;
         };

         /* SPI2 pins */
         spi2_pins: spi2_pins {
                 brcm,pins = <40 41 42>;
                 brcm,function = <BCM2835_FSEL_ALT4>;
         };
         /* SPI2 GPIO-chipselects */
         spi2_cs_pins: spi2_cs_pins {
                 brcm,pins = <43 44 45>;
                 brcm,function = <BCM2835_FSEL_OUT>
         };

and reference them inside the spi devices, then I get the following
messages in dmesg:
[   54.970048] pinctrl-bcm2835 20200000.gpio: pin gpio21 already 
requested by 20200000.gpio; cannot claim for 20215080.spi
[   54.970088] pinctrl-bcm2835 20200000.gpio: pin-21 (20215080.spi) 
status -22
[   54.970111] pinctrl-bcm2835 20200000.gpio: could not request pin 21 
(gpio21) from group gpio21  on device pinctrl-bcm2835
[   54.977495] pinctrl-bcm2835 20200000.gpio: pin gpio40 already 
requested by 20200000.gpio; cannot claim for 202150c0.spi
[   54.977543] pinctrl-bcm2835 20200000.gpio: pin-40 (202150c0.spi) 
status -22
[   54.977568] pinctrl-bcm2835 20200000.gpio: could not request pin 40 
(gpio40) from group gpio40  on device pinctrl-bcm2835


Looking into it it is related to the fact that we also have the
following definitions in arch/arm/boot/dts/bcm2835-rpi-b-plus.dts:
&gpio {
...
         /* I2S interface */
         i2s_alt0: i2s_alt0 {
                 brcm,pins = <18 19 20 21>;
                 brcm,function = <BCM2835_FSEL_ALT0>;
         };
...
};
and in arch/arm/boot/dts/bcm2835-rpi.dtsi:
&gpio {
...
         alt0: alt0 {
                 brcm,pins = <0 1 2 3 4 5 7 8 9 10 11 14 15 40 45>;
                 brcm,function = <BCM2835_FSEL_ALT0>;
         };
...
};

There are GPIO pins (18, 19, 20, 21, 40) defined in multiple GPIO
sections with different functions (ALT0, ALT4 and OUT).

Obviously there is an issue when both i2s and spi drivers are loaded,
but if only one is loaded it should work, but the framework does not
allow it.

So I wonder how we can solve this in a sensible way allowing for either
of those devices to be enabled - do we need to go the
"device-tree-overlay" route?

Thanks,
         Martin


From daniel.lezcano at linaro.org  Mon Aug 10 02:51:20 2015
From: daniel.lezcano at linaro.org (Daniel Lezcano)
Date: Mon, 10 Aug 2015 11:51:20 +0200
Subject: [PATCH 03/74] clockevents/drivers/bcm2835: Migrate to new 'set-state'
 interface
In-Reply-To: <1439200352-26767-1-git-send-email-daniel.lezcano@linaro.org>
References: <55C87382.4070309@linaro.org>
 <1439200352-26767-1-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <1439200352-26767-3-git-send-email-daniel.lezcano@linaro.org>

From: Viresh Kumar <viresh.kumar at linaro.org>

Migrate bcm2835 driver to the new 'set-state' interface provided by
the clockevents core, the earlier 'set-mode' interface is marked
obsolete now.

This also enables us to implement callbacks for new states of clockevent
devices, for example: ONESHOT_STOPPED.

We weren't doing anything in the ->set_mode() callback. So, this patch
doesn't provide any set-state callbacks.

Acked-by: Daniel Lezcano <daniel.lezcano at linaro.org>
Tested-by: Stephen Warren <swarren at wwwdotorg.org>
Cc: Stephen Warren <swarren at wwwdotorg.org>
Cc: Lee Jones <lee at kernel.org>
Signed-off-by: Viresh Kumar <viresh.kumar at linaro.org>
Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
---
 drivers/clocksource/bcm2835_timer.c | 16 ----------------
 1 file changed, 16 deletions(-)

diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c
index 26ed331..6f28229 100644
--- a/drivers/clocksource/bcm2835_timer.c
+++ b/drivers/clocksource/bcm2835_timer.c
@@ -54,21 +54,6 @@ static u64 notrace bcm2835_sched_read(void)
 	return readl_relaxed(system_clock);
 }
 
-static void bcm2835_time_set_mode(enum clock_event_mode mode,
-	struct clock_event_device *evt_dev)
-{
-	switch (mode) {
-	case CLOCK_EVT_MODE_ONESHOT:
-	case CLOCK_EVT_MODE_UNUSED:
-	case CLOCK_EVT_MODE_SHUTDOWN:
-	case CLOCK_EVT_MODE_RESUME:
-		break;
-	default:
-		WARN(1, "%s: unhandled event mode %d\n", __func__, mode);
-		break;
-	}
-}
-
 static int bcm2835_time_set_next_event(unsigned long event,
 	struct clock_event_device *evt_dev)
 {
@@ -129,7 +114,6 @@ static void __init bcm2835_timer_init(struct device_node *node)
 	timer->evt.name = node->name;
 	timer->evt.rating = 300;
 	timer->evt.features = CLOCK_EVT_FEAT_ONESHOT;
-	timer->evt.set_mode = bcm2835_time_set_mode;
 	timer->evt.set_next_event = bcm2835_time_set_next_event;
 	timer->evt.cpumask = cpumask_of(0);
 	timer->act.name = node->name;
-- 
1.9.1



From swarren at wwwdotorg.org  Mon Aug 10 21:19:40 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Mon, 10 Aug 2015 22:19:40 -0600
Subject: [RFC PATCH] soc: bcm2835: auxiliar devices enable infrastructure
In-Reply-To: <1438677718-19612-1-git-send-email-kernel@martin.sperl.org>
References: <55BC353F.1000906@wwwdotorg.org>
 <1438677718-19612-1-git-send-email-kernel@martin.sperl.org>
Message-ID: <55C977DC.5060608@wwwdotorg.org>

On 08/04/2015 02:41 AM, kernel at martin.sperl.org wrote:
> From: Martin Sperl <kernel at martin.sperl.org>
> 
> The bcm2835 soc contains 3 auxiliar devices (spi1, spi2 and uart1)
> that all are enabled via a shared register.
> 
> To serialize access this soc-driver is created that implements
>   bcm2835aux_enable(struct device *dev, const char *property);
>   bcm2835aux_disable(struct device *dev, const char *property);
> 
> Ehich will read the property from the device tree of the device
> and enable/disable that specific device as per device tree.
> 
> First use of this api will be spi-bcm2835aux, which will
> set the following property in the device tree for the devices:
>   brcm,aux-enable = <&aux_enable 4>;  
> 
> And call:
>   bcm2835aux_enable(&spi->dev, "brcm,aux-enable");
> 
> This allows to define different property names based on the
> driver.

You need to send patches like this to a broader audience, such as CCing
the ARM kernel mailing list.

> diff --git a/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt b/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt

> +Broadcom BCM2835 auxiliar device enable register

auxiliary. Same elsewhere.

> +- reg: Should contain register location and length for the enabl
> +       register

> +	reg = <0x7e215004 0x04>;

I think both registers 0x7e215000 and 0x7e215004 are part of the
"global" auxiliary register set. As such, I'd expect:

	reg = <0x7e215000 0x08>;

> diff --git a/drivers/soc/bcm/bcm2835-aux.c b/drivers/soc/bcm/bcm2835-aux.c

> +static void *bcm2835aux_find_base(struct device *dev, const char *property)

> +	/* now find the device it points to */
> +	found = driver_find_device(&bcm2835aux_driver.driver, NULL,
> +				   np, bcm2835aux_dev_match);
> +	if (!found) {
> +		dev_err(dev, "device for phandle of %s not found\n",
> +			property);
> +		return ERR_PTR(-ENODEV);

That should probably be -EPROBE_DEFER, so that client drivers can defer
their own probe if this driver isn't yet probed but the client driver
will need its services.

> diff --git a/include/linux/soc/bcm/bcm2835-aux.h b/include/linux/soc/bcm/bcm2835-aux.h

> +int bcm2835aux_enable(struct device *dev, const char *property);
> +int bcm2835aux_disable(struct device *dev, const char *property);

Will client drivers call bcm2835aux_enable() only from their probe()? If
so, that set of APIs is fine. Otherwise, I would expect more functions:

struct bcm2835aux;
struct bcm2835aux *bcm2835aux_get_handle(struct device *dev,
		const char *property);
int bcm2835aux_enable(struct bcm2835aux *aux);
int bcm2835aux_disable(struct bcm2835aux *aux);


From swarren at wwwdotorg.org  Mon Aug 10 21:21:48 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Mon, 10 Aug 2015 22:21:48 -0600
Subject: devicetree: bcm2835:
In-Reply-To: <55C8639E.4@martin.sperl.org>
References: <55C8639E.4@martin.sperl.org>
Message-ID: <55C9785C.30203@wwwdotorg.org>

On 08/10/2015 02:41 AM, Martin Sperl wrote:
> Hi!
> 
> When adding a gpio section to enable the auxiliar spi device pins
> with the correct mode to the bcm2835 device treesimilar to this:
>         /* SPI1 pins */
>         spi1_pins: spi1_pins {
>                 brcm,pins = <19 20 21>;
>                 brcm,function = <BCM2835_FSEL_ALT4>
>         };
>         /* SPI1 GPIO-chipselects */
>         spi1_cs_pins: spi1_cs_pins {
>                 brcm,pins = <18 17 16>;
>                 brcm,function = <BCM2835_FSEL_OUT>;
>         };
> 
>         /* SPI2 pins */
>         spi2_pins: spi2_pins {
>                 brcm,pins = <40 41 42>;
>                 brcm,function = <BCM2835_FSEL_ALT4>;
>         };
>         /* SPI2 GPIO-chipselects */
>         spi2_cs_pins: spi2_cs_pins {
>                 brcm,pins = <43 44 45>;
>                 brcm,function = <BCM2835_FSEL_OUT>
>         };

You don't need to add all that to the SPI node. Just set up the existing
pinctrl configurations in the pinctrl node as you need them. Per-device
DT configuration nodes are useful if you need to do dynamic pinmuxing,
but there's no need for that to simply set up the usage of the SPI/I2C
pins at boot.


From kernel at martin.sperl.org  Mon Aug 10 22:53:22 2015
From: kernel at martin.sperl.org (Martin Sperl)
Date: Tue, 11 Aug 2015 07:53:22 +0200
Subject: devicetree: bcm2835:
In-Reply-To: <55C9785C.30203@wwwdotorg.org>
References: <55C8639E.4@martin.sperl.org> <55C9785C.30203@wwwdotorg.org>
Message-ID: <55C98DD2.5080402@martin.sperl.org>



On 11.08.2015 06:21, Stephen Warren wrote:
> On 08/10/2015 02:41 AM, Martin Sperl wrote:
>> Hi!
>>
>> When adding a gpio section to enable the auxiliar spi device pins
>> with the correct mode to the bcm2835 device treesimilar to this:
>>          /* SPI1 pins */
>>          spi1_pins: spi1_pins {
>>                  brcm,pins = <19 20 21>;
>>                  brcm,function = <BCM2835_FSEL_ALT4>
>>          };
>>          /* SPI1 GPIO-chipselects */
>>          spi1_cs_pins: spi1_cs_pins {
>>                  brcm,pins = <18 17 16>;
>>                  brcm,function = <BCM2835_FSEL_OUT>;
>>          };
>>
>>          /* SPI2 pins */
>>          spi2_pins: spi2_pins {
>>                  brcm,pins = <40 41 42>;
>>                  brcm,function = <BCM2835_FSEL_ALT4>;
>>          };
>>          /* SPI2 GPIO-chipselects */
>>          spi2_cs_pins: spi2_cs_pins {
>>                  brcm,pins = <43 44 45>;
>>                  brcm,function = <BCM2835_FSEL_OUT>
>>          };
>
> You don't need to add all that to the SPI node. Just set up the existing
> pinctrl configurations in the pinctrl node as you need them. Per-device
> DT configuration nodes are useful if you need to do dynamic pinmuxing,
> but there's no need for that to simply set up the usage of the SPI/I2C
> pins at boot.
>
So does this mean I should NOT provide some default dt-configs for the 
device tree pin-mux when sending the patch?

So why do we have PWM0 pin-modes configured in 
arch/arm/boot/dts/bcm2835-rpi.dtsi
and I2S in arch/arm/boot/dts/bcm2835-rpi-b.dts and in
arch/arm/boot/dts/bcm2835-rpi-b-plus.dts.

Would we need separate device-trees (dts) to use spi1/2?
e.g arch/arm/boot/dts/bcm2835-rpi-b-auxspi.dts
Note that this would also require that PIN 40 configuration gets moved
out ?

Just wanting to make sure that there is nothing missing when I submit
the patches for spi-bcm2835aux (plus the bcm2835-aux enable driver).

Thanks,
	Martin


From lee at kernel.org  Tue Aug 11 08:31:50 2015
From: lee at kernel.org (Lee Jones)
Date: Tue, 11 Aug 2015 16:31:50 +0100
Subject: [PATCH 03/74] clockevents/drivers/bcm2835: Migrate to new
 'set-state' interface
In-Reply-To: <1439200352-26767-3-git-send-email-daniel.lezcano@linaro.org>
References: <55C87382.4070309@linaro.org>
 <1439200352-26767-1-git-send-email-daniel.lezcano@linaro.org>
 <1439200352-26767-3-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <20150811153150.GP18282@x1>

On Mon, 10 Aug 2015, Daniel Lezcano wrote:

> From: Viresh Kumar <viresh.kumar at linaro.org>
> 
> Migrate bcm2835 driver to the new 'set-state' interface provided by
> the clockevents core, the earlier 'set-mode' interface is marked
> obsolete now.
> 
> This also enables us to implement callbacks for new states of clockevent
> devices, for example: ONESHOT_STOPPED.
> 
> We weren't doing anything in the ->set_mode() callback. So, this patch
> doesn't provide any set-state callbacks.
> 
> Acked-by: Daniel Lezcano <daniel.lezcano at linaro.org>
> Tested-by: Stephen Warren <swarren at wwwdotorg.org>
> Cc: Stephen Warren <swarren at wwwdotorg.org>
> Cc: Lee Jones <lee at kernel.org>
> Signed-off-by: Viresh Kumar <viresh.kumar at linaro.org>
> Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
> ---
>  drivers/clocksource/bcm2835_timer.c | 16 ----------------
>  1 file changed, 16 deletions(-)

Looks fine.

Acked-by: Lee Jones <lee at kernel.org>

> diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c
> index 26ed331..6f28229 100644
> --- a/drivers/clocksource/bcm2835_timer.c
> +++ b/drivers/clocksource/bcm2835_timer.c
> @@ -54,21 +54,6 @@ static u64 notrace bcm2835_sched_read(void)
>  	return readl_relaxed(system_clock);
>  }
>  
> -static void bcm2835_time_set_mode(enum clock_event_mode mode,
> -	struct clock_event_device *evt_dev)
> -{
> -	switch (mode) {
> -	case CLOCK_EVT_MODE_ONESHOT:
> -	case CLOCK_EVT_MODE_UNUSED:
> -	case CLOCK_EVT_MODE_SHUTDOWN:
> -	case CLOCK_EVT_MODE_RESUME:
> -		break;
> -	default:
> -		WARN(1, "%s: unhandled event mode %d\n", __func__, mode);
> -		break;
> -	}
> -}
> -
>  static int bcm2835_time_set_next_event(unsigned long event,
>  	struct clock_event_device *evt_dev)
>  {
> @@ -129,7 +114,6 @@ static void __init bcm2835_timer_init(struct device_node *node)
>  	timer->evt.name = node->name;
>  	timer->evt.rating = 300;
>  	timer->evt.features = CLOCK_EVT_FEAT_ONESHOT;
> -	timer->evt.set_mode = bcm2835_time_set_mode;
>  	timer->evt.set_next_event = bcm2835_time_set_next_event;
>  	timer->evt.cpumask = cpumask_of(0);
>  	timer->act.name = node->name;


From lee at kernel.org  Tue Aug 11 09:06:42 2015
From: lee at kernel.org (Lee Jones)
Date: Tue, 11 Aug 2015 17:06:42 +0100
Subject: [GIT PULL] RPi DT changes due for v4.3
Message-ID: <20150811160642.GQ18282@x1>

ARM SoC Chaps,

Please find a couple of simple RPi changes pertaining to Firmware.

The following changes since commit bc0195aad0daa2ad5b0d76cce22b167bc3435590:

  Linux 4.2-rc2 (2015-07-12 15:10:30 -0700)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git tags/rpi-dt-for-armsoc-4.3

for you to fetch changes up to fd26f8830979de48eb3f1c253eb9d2ee2e468eb6:

  dt/bindings: Add binding for the Raspberry Pi firmware driver (2015-08-11 16:56:41 +0100)

----------------------------------------------------------------
 - New Firmware node and accompanying binding document

----------------------------------------------------------------
Eric Anholt (2):
      ARM: bcm2835: Add the firmware driver information to the RPi DT
      dt/bindings: Add binding for the Raspberry Pi firmware driver

 Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.txt | 14 ++++++++++++++
 arch/arm/boot/dts/bcm2835-rpi.dtsi                                         |  7 +++++++
 2 files changed, 21 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.txt


From mturquette at linaro.org  Tue Aug 11 14:17:33 2015
From: mturquette at linaro.org (Michael Turquette)
Date: Tue, 11 Aug 2015 14:17:33 -0700
Subject: [PATCH v4 2/8] clk: Add a Raspberry Pi-specific clock driver.
In-Reply-To: <1437420787-32137-3-git-send-email-eric@anholt.net>
References: <1437420787-32137-1-git-send-email-eric@anholt.net>
 <1437420787-32137-3-git-send-email-eric@anholt.net>
Message-ID: <20150811211733.31346.11122@quantum>

Hi Eric,

Quoting Eric Anholt (2015-07-20 12:33:01)
> +void __init rpi_firmware_init_clock_provider(struct device_node *node)
> +{
> +       /* We delay construction of our struct clks until get time,
> +        * because we need to be able to return -EPROBE_DEFER if the
> +        * firmware driver isn't up yet.  clk core doesn't support
> +        * re-probing on -EPROBE_DEFER, but callers of clk_get can.
> +        */
> +       of_clk_add_provider(node, rpi_firmware_delayed_get_clk, node);
> +}
> +
> +CLK_OF_DECLARE(rpi_firmware_clocks, "raspberrypi,bcm2835-firmware-clocks",
> +              rpi_firmware_init_clock_provider);

Do you require CLK_OF_DECLARE here? Could this be a platform driver
instead?

Regards,
Mike

> -- 
> 2.1.4
> 


From mturquette at linaro.org  Tue Aug 11 14:18:40 2015
From: mturquette at linaro.org (Michael Turquette)
Date: Tue, 11 Aug 2015 14:18:40 -0700
Subject: [PATCH v4 1/8] clk: bcm2835: Add binding docs for the Raspberry
 Pi clock provider
In-Reply-To: <1437420787-32137-2-git-send-email-eric@anholt.net>
References: <1437420787-32137-1-git-send-email-eric@anholt.net>
 <1437420787-32137-2-git-send-email-eric@anholt.net>
Message-ID: <20150811211840.31346.28666@quantum>

Quoting Eric Anholt (2015-07-20 12:33:00)
> The hardware clocks are not controllable by the ARM, so we have to
> make requests to the firmware to do so from the VPU side.  This will
> let us replace fixed clocks in our DT with actual clock control (and
> correct frequency information).
> 
> Signed-off-by: Eric Anholt <eric at anholt.net>
> Acked-by: Stephen Warren <swarren at wwwdotorg.org>
> Acked-by: Lee Jones <lee at kernel.org>

Looks good to me. Do you want the clk patches to go through the clk tree
or do you want them all together? For this patch feel free to add:

Acked-by: Michael Turquette <mturquette at baylibre.com>

Regards,
Mike

> ---
> 
> v2: Include the dt-bindings header in this commit instead of the next
>     one.  Make the clock indices match the firmware clock IDs.  Rename
>     the binding's compat string.  Move the firmware phandle to be
>     under a vendor-specific namespace.
> v4: Mention 'clk' in the subject instead of the more generic
>     dt/bindings.
> 
>  .../clock/raspberrypi,bcm2835-firmware-clocks.txt  | 25 ++++++++++++++++++++++
>  include/dt-bindings/clk/raspberrypi.h              | 23 ++++++++++++++++++++
>  2 files changed, 48 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/raspberrypi,bcm2835-firmware-clocks.txt
>  create mode 100644 include/dt-bindings/clk/raspberrypi.h
> 
> diff --git a/Documentation/devicetree/bindings/clock/raspberrypi,bcm2835-firmware-clocks.txt b/Documentation/devicetree/bindings/clock/raspberrypi,bcm2835-firmware-clocks.txt
> new file mode 100644
> index 0000000..0972602
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/raspberrypi,bcm2835-firmware-clocks.txt
> @@ -0,0 +1,25 @@
> +Raspberry Pi firmware clock provider.
> +
> +The Raspberry Pi architecture doesn't provide direct access to the
> +CLOCKMAN peripheral from the ARM side, so Linux has to make requests
> +to the VPU firmware to program them.
> +
> +This binding uses the common clock binding:
> +Documentation/devicetree/bindings/clock/clock-bindings.txt
> +
> +Required properties:
> +- compatible:          Should be "raspberrypi,bcm2835-firmware-clocks"
> +
> +- #clock-cells:                Shall have value <1>.  The permitted clock-specifier
> +                         values can be found in
> +                         include/dt-bindings/clk/raspberrypi.h.
> +
> +- raspberrypi,firmware:        Phandle to the firmware driver node.
> +
> +Example:
> +
> +firmware_clocks: firmware-clocks {
> +       compatible = "raspberrypi,bcm2835-firmware-clocks";
> +       #clock-cells = <1>;
> +       raspberrypi,firmware = <&firmware>;
> +};
> diff --git a/include/dt-bindings/clk/raspberrypi.h b/include/dt-bindings/clk/raspberrypi.h
> new file mode 100644
> index 0000000..ceec90f
> --- /dev/null
> +++ b/include/dt-bindings/clk/raspberrypi.h
> @@ -0,0 +1,23 @@
> +#/*
> + *  Copyright ?? 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef _DT_BINDINGS_CLK_RASPBERRYPI_H
> +#define _DT_BINDINGS_CLK_RASPBERRYPI_H
> +
> +#define RPI_CLOCK_EMMC 1
> +#define RPI_CLOCK_UART0        2
> +#define RPI_CLOCK_ARM  3
> +#define RPI_CLOCK_CORE 4
> +#define RPI_CLOCK_V3D  5
> +#define RPI_CLOCK_H264 6
> +#define RPI_CLOCK_ISP  7
> +#define RPI_CLOCK_SDRAM        8
> +#define RPI_CLOCK_PIXEL        9
> +#define RPI_CLOCK_PWM  10
> +
> +#endif
> -- 
> 2.1.4
> 


From mturquette at linaro.org  Tue Aug 11 14:19:46 2015
From: mturquette at linaro.org (Michael Turquette)
Date: Tue, 11 Aug 2015 14:19:46 -0700
Subject: [PATCH v4 5/8] clk: bcm2835: Drop the fixed sys_pclk.
In-Reply-To: <1437420787-32137-6-git-send-email-eric@anholt.net>
References: <1437420787-32137-1-git-send-email-eric@anholt.net>
 <1437420787-32137-6-git-send-email-eric@anholt.net>
Message-ID: <20150811211946.31346.8021@quantum>

Quoting Eric Anholt (2015-07-20 12:33:04)
> Nothing uses it, and I can't find any evidence that anything ever has.
> Its role is now filled by the core clock in the firmware driver.
> 
> Signed-off-by: Eric Anholt <eric at anholt.net>
> Acked-by: Stephen Warren <swarren at wwwdotorg.org>

Applied to clk-next.

Regards,
Mike

> ---
>  drivers/clk/clk-bcm2835.c | 5 -----
>  1 file changed, 5 deletions(-)
> 
> diff --git a/drivers/clk/clk-bcm2835.c b/drivers/clk/clk-bcm2835.c
> index 6b950ca..dd295e4 100644
> --- a/drivers/clk/clk-bcm2835.c
> +++ b/drivers/clk/clk-bcm2835.c
> @@ -32,11 +32,6 @@ void __init bcm2835_init_clocks(void)
>         struct clk *clk;
>         int ret;
>  
> -       clk = clk_register_fixed_rate(NULL, "sys_pclk", NULL, CLK_IS_ROOT,
> -                                       250000000);
> -       if (IS_ERR(clk))
> -               pr_err("sys_pclk not registered\n");
> -
>         clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT,
>                                         126000000);
>         if (IS_ERR(clk))
> -- 
> 2.1.4
> 


From olof at lixom.net  Tue Aug 11 06:13:23 2015
From: olof at lixom.net (Olof Johansson)
Date: Tue, 11 Aug 2015 15:13:23 +0200
Subject: [GIT PULL] Broadcom MAINTAINERS file updates for 4.3
In-Reply-To: <1438796683-18581-2-git-send-email-f.fainelli@gmail.com>
References: <1438796683-18581-1-git-send-email-f.fainelli@gmail.com>
 <1438796683-18581-2-git-send-email-f.fainelli@gmail.com>
Message-ID: <20150811131323.GG30181@localhost>

On Wed, Aug 05, 2015 at 10:44:42AM -0700, Florian Fainelli wrote:
> The following changes since commit d770e558e21961ad6cfdf0ff7df0eb5d7d4f0754:
> 
>   Linux 4.2-rc1 (2015-07-05 11:01:52 -0700)
> 
> are available in the git repository at:
> 
>   http://github.com/broadcom/stblinux tags/arm-soc/for-4.3/maintainers
> 
> for you to fetch changes up to 824811293538f5cafaa6d19e3458a13eafa92a32:
> 
>   MAINTAINERS: Explicitly add linux-arm-kernel for bcm2835 (2015-08-05 10:14:00 -0700)
> 
> ----------------------------------------------------------------
> This pull request contains the following changes to the MAINTAINERS file:
> 
> - Add Eric Anholt as a 3rd maintainer for the Raspberry Pi platform code/drivers
> - Add linux-arm-kernel for all bcm2835 related drivers

Merged, thanks.


-Olof


From olof at lixom.net  Tue Aug 11 06:16:53 2015
From: olof at lixom.net (Olof Johansson)
Date: Tue, 11 Aug 2015 15:16:53 +0200
Subject: [GIT PULL] Broadcom Device Tree changes for 4.3 (part 2)
In-Reply-To: <1438796683-18581-1-git-send-email-f.fainelli@gmail.com>
References: <1438796683-18581-1-git-send-email-f.fainelli@gmail.com>
Message-ID: <20150811131653.GH30181@localhost>

Hi,

On Wed, Aug 05, 2015 at 10:44:41AM -0700, Florian Fainelli wrote:
> The following changes since commit ee224e175e9d837b3d572e863ac96109b58707ac:
> 
>   Merge tag 'mvebu-dt-4.3-2' of git://git.infradead.org/linux-mvebu into next/dt (2015-08-05 11:10:30 +0200)

This isn't quite right. Please don't base your branches on top of our
next/* branches. Instead they should be based on a plain -rc from upstream
or, if needed, preceding versions of your own branches.

In this case I've just taken the three patches and applied them directly, so
no need to respin the branch this time.


-Olof



From olof at lixom.net  Tue Aug 11 06:17:56 2015
From: olof at lixom.net (Olof Johansson)
Date: Tue, 11 Aug 2015 15:17:56 +0200
Subject: [GIT PULL] Broadcom SoC changes for 4.3 (part 2, try2)
In-Reply-To: <1439063444-12466-1-git-send-email-f.fainelli@gmail.com>
References: <55C4E8C8.7030503@broadcom.com>
 <1439063444-12466-1-git-send-email-f.fainelli@gmail.com>
Message-ID: <20150811131756.GI30181@localhost>

Hi,


On Sat, Aug 08, 2015 at 12:50:44PM -0700, Florian Fainelli wrote:
> The following changes since commit 39aa437e18cac7acae55481ed73feea0de497399:
> 
>   Merge branch 'queue/irq/arm' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip into next/cleanup (2015-08-05 17:24:11 +0200)
> 
> are available in the git repository at:
> 
>   http://github.com/broadcom/stblinux tags/arm-soc/for-4.3/soc-part2
> 
> for you to fetch changes up to cf1140515b3b94ef5aa06a085e94ddd814a8cd78:
> 
>   ARM: brcmstb: select ARCH_DMA_ADDR_T_64BIT for LPAE (2015-08-06 21:38:13 -0700)

Same thing here, please don't base your branch on top of our next/*. I've also
cherry-picked these patches over, so no need to respin this time.


Thanks,

-Olof



From sjg at chromium.org  Tue Aug 11 07:25:53 2015
From: sjg at chromium.org (Simon Glass)
Date: Tue, 11 Aug 2015 08:25:53 -0600
Subject: [PATCH] arm: rpi: Device tree modifications for U-Boot
Message-ID: <1439303153-12171-1-git-send-email-sjg@chromium.org>

This updates the device tree from the kernel version to something suitable
for U-Boot:

- Add stdout-path alias for console
- Mark the /soc node to be available pre-relocation so that the early
serial console works (we need the 'ranges' property to be available)

Signed-off-by: Simon Glass <sjg at chromium.org>
---

 arch/arm/boot/dts/bcm2835.dtsi | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
index 301c73f..bd6bff6 100644
--- a/arch/arm/boot/dts/bcm2835.dtsi
+++ b/arch/arm/boot/dts/bcm2835.dtsi
@@ -8,6 +8,7 @@
 
 	chosen {
 		bootargs = "earlyprintk console=ttyAMA0";
+		stdout-path = &uart;
 	};
 
 	soc {
@@ -16,6 +17,7 @@
 		#size-cells = <1>;
 		ranges = <0x7e000000 0x20000000 0x02000000>;
 		dma-ranges = <0x40000000 0x00000000 0x20000000>;
+		u-boot,dm-pre-reloc;
 
 		timer at 7e003000 {
 			compatible = "brcm,bcm2835-system-timer";
@@ -92,7 +94,7 @@
 			#interrupt-cells = <2>;
 		};
 
-		uart at 7e201000 {
+		uart: uart at 7e201000 {
 			compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell";
 			reg = <0x7e201000 0x1000>;
 			interrupts = <2 25>;
-- 
2.5.0.rc2.392.g76e840b



From dev at lynxeye.de  Tue Aug 11 10:05:01 2015
From: dev at lynxeye.de (Lucas Stach)
Date: Tue, 11 Aug 2015 19:05:01 +0200
Subject: [PATCH] arm: rpi: Device tree modifications for U-Boot
In-Reply-To: <1439303153-12171-1-git-send-email-sjg@chromium.org>
References: <1439303153-12171-1-git-send-email-sjg@chromium.org>
Message-ID: <1439312701.2451.3.camel@lynxeye.de>

Hi Simon,

why did you send this to the Tegra ML?

Am Dienstag, den 11.08.2015, 08:25 -0600 schrieb Simon Glass:
> This updates the device tree from the kernel version to something suitable
> for U-Boot:
> 
> - Add stdout-path alias for console
> - Mark the /soc node to be available pre-relocation so that the early
> serial console works (we need the 'ranges' property to be available)
> 
> Signed-off-by: Simon Glass <sjg at chromium.org>
> ---
> 
>  arch/arm/boot/dts/bcm2835.dtsi | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
> index 301c73f..bd6bff6 100644
> --- a/arch/arm/boot/dts/bcm2835.dtsi
> +++ b/arch/arm/boot/dts/bcm2835.dtsi
> @@ -8,6 +8,7 @@
>  
>  	chosen {
>  		bootargs = "earlyprintk console=ttyAMA0";
> +		stdout-path = &uart;
>  	};
>  
>  	soc {
> @@ -16,6 +17,7 @@
>  		#size-cells = <1>;
>  		ranges = <0x7e000000 0x20000000 0x02000000>;
>  		dma-ranges = <0x40000000 0x00000000 0x20000000>;
> +		u-boot,dm-pre-reloc;

Why do you need this and why should upstream carry your favourite
bootloaders configuration? This is in no way hardware description.

Regards,
Lucas



From swarren at wwwdotorg.org  Tue Aug 11 10:29:55 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Tue, 11 Aug 2015 11:29:55 -0600
Subject: [PATCH] arm: rpi: Device tree modifications for U-Boot
In-Reply-To: <1439312701.2451.3.camel@lynxeye.de>
References: <1439303153-12171-1-git-send-email-sjg@chromium.org>
 <1439312701.2451.3.camel@lynxeye.de>
Message-ID: <55CA3113.1010601@wwwdotorg.org>

On 08/11/2015 11:05 AM, Lucas Stach wrote:
> Hi Simon,
>
> why did you send this to the Tegra ML?

At my request, so that the DT changes could be reviewed by the kernel DT 
reviewers and added to the copy of the DT files in the kernel source 
tree if approved.

My assertion is that DT content should be independent of SW stack. Or 
put another way, all SW stacks should use the same DT content. As such, 
if these properties are needed by U-Boot, and contained in the copy of 
the DT files in the U-Boot source tree, they should also be present in 
the copy of the DT files in the kernel source tree, so the two do not 
become out of sync.

> Am Dienstag, den 11.08.2015, 08:25 -0600 schrieb Simon Glass:
>> This updates the device tree from the kernel version to something suitable
>> for U-Boot:
>>
>> - Add stdout-path alias for console
>> - Mark the /soc node to be available pre-relocation so that the early
>> serial console works (we need the 'ranges' property to be available)

>> diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
>> index 301c73f..bd6bff6 100644
>> --- a/arch/arm/boot/dts/bcm2835.dtsi
>> +++ b/arch/arm/boot/dts/bcm2835.dtsi
>> @@ -8,6 +8,7 @@
>>
>>   	chosen {
>>   		bootargs = "earlyprintk console=ttyAMA0";
>> +		stdout-path = &uart;
>>   	};
>>
>>   	soc {
>> @@ -16,6 +17,7 @@
>>   		#size-cells = <1>;
>>   		ranges = <0x7e000000 0x20000000 0x02000000>;
>>   		dma-ranges = <0x40000000 0x00000000 0x20000000>;
>> +		u-boot,dm-pre-reloc;
>
> Why do you need this and why should upstream carry your favourite
> bootloaders configuration? This is in no way hardware description.




From dev at lynxeye.de  Tue Aug 11 12:38:38 2015
From: dev at lynxeye.de (Lucas Stach)
Date: Tue, 11 Aug 2015 21:38:38 +0200
Subject: [PATCH] arm: rpi: Device tree modifications for U-Boot
In-Reply-To: <55CA3113.1010601@wwwdotorg.org>
References: <1439303153-12171-1-git-send-email-sjg@chromium.org>
 <1439312701.2451.3.camel@lynxeye.de> <55CA3113.1010601@wwwdotorg.org>
Message-ID: <1439321918.2214.6.camel@lynxeye.de>

Am Dienstag, den 11.08.2015, 11:29 -0600 schrieb Stephen Warren:
> On 08/11/2015 11:05 AM, Lucas Stach wrote:
> > Hi Simon,
> >
> > why did you send this to the Tegra ML?
> 
> At my request, so that the DT changes could be reviewed by the kernel DT 
> reviewers and added to the copy of the DT files in the kernel source 
> tree if approved.
> 
This doesn't really answer the question why it was sent to the Tegra
list if it is an RPi change, but that's just a side note.

> My assertion is that DT content should be independent of SW stack. Or 
> put another way, all SW stacks should use the same DT content. As such, 
> if these properties are needed by U-Boot, and contained in the copy of 
> the DT files in the U-Boot source tree, they should also be present in 
> the copy of the DT files in the kernel source tree, so the two do not 
> become out of sync.
> 
I agree with that overall goal to have a common DT base between
different software stacks. After all I lobbied for keeping the Tegra
pinctrl setting in the DT even though Linux doesn't use them any more.

For my question below I honestly want an answer as to why U-Boot needs
those DT changes and why it can't work with the current DT contents.
It's really just the same question we also have to answer for each and
every DT addition that we do for Linux.

Regards,
Lucas

> > Am Dienstag, den 11.08.2015, 08:25 -0600 schrieb Simon Glass:
> >> This updates the device tree from the kernel version to something suitable
> >> for U-Boot:
> >>
> >> - Add stdout-path alias for console
> >> - Mark the /soc node to be available pre-relocation so that the early
> >> serial console works (we need the 'ranges' property to be available)
> 
> >> diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
> >> index 301c73f..bd6bff6 100644
> >> --- a/arch/arm/boot/dts/bcm2835.dtsi
> >> +++ b/arch/arm/boot/dts/bcm2835.dtsi
> >> @@ -8,6 +8,7 @@
> >>
> >>   	chosen {
> >>   		bootargs = "earlyprintk console=ttyAMA0";
> >> +		stdout-path = &uart;
> >>   	};
> >>
> >>   	soc {
> >> @@ -16,6 +17,7 @@
> >>   		#size-cells = <1>;
> >>   		ranges = <0x7e000000 0x20000000 0x02000000>;
> >>   		dma-ranges = <0x40000000 0x00000000 0x20000000>;
> >> +		u-boot,dm-pre-reloc;
> >
> > Why do you need this and why should upstream carry your favourite
> > bootloaders configuration? This is in no way hardware description.
> 
> 




From eric at anholt.net  Wed Aug 12 10:56:58 2015
From: eric at anholt.net (Eric Anholt)
Date: Wed, 12 Aug 2015 10:56:58 -0700
Subject: [PATCH v4 1/8] clk: bcm2835: Add binding docs for the Raspberry
 Pi clock provider
In-Reply-To: <20150811211840.31346.28666@quantum>
References: <1437420787-32137-1-git-send-email-eric@anholt.net>
 <1437420787-32137-2-git-send-email-eric@anholt.net>
 <20150811211840.31346.28666@quantum>
Message-ID: <87zj1w4cgl.fsf@eliezer.anholt.net>

Michael Turquette <mturquette at linaro.org> writes:

> Quoting Eric Anholt (2015-07-20 12:33:00)
>> The hardware clocks are not controllable by the ARM, so we have to
>> make requests to the firmware to do so from the VPU side.  This will
>> let us replace fixed clocks in our DT with actual clock control (and
>> correct frequency information).
>> 
>> Signed-off-by: Eric Anholt <eric at anholt.net>
>> Acked-by: Stephen Warren <swarren at wwwdotorg.org>
>> Acked-by: Lee Jones <lee at kernel.org>
>
> Looks good to me. Do you want the clk patches to go through the clk tree
> or do you want them all together? For this patch feel free to add:
>
> Acked-by: Michael Turquette <mturquette at baylibre.com>

It would be nice to do them all together in the arm-soc tree, since the
later DT changes require that the clock driver be present.  (And, since
this is a dependency of the RPi2 work, it would let us stack that on
top, too).
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150812/0d1a7c0d/attachment.sig>

From eric at anholt.net  Wed Aug 12 11:04:05 2015
From: eric at anholt.net (Eric Anholt)
Date: Wed, 12 Aug 2015 11:04:05 -0700
Subject: [PATCH v4 2/8] clk: Add a Raspberry Pi-specific clock driver.
In-Reply-To: <20150811211733.31346.11122@quantum>
References: <1437420787-32137-1-git-send-email-eric@anholt.net>
 <1437420787-32137-3-git-send-email-eric@anholt.net>
 <20150811211733.31346.11122@quantum>
Message-ID: <87wpx04c4q.fsf@eliezer.anholt.net>

Michael Turquette <mturquette at linaro.org> writes:

> Hi Eric,
>
> Quoting Eric Anholt (2015-07-20 12:33:01)
>> +void __init rpi_firmware_init_clock_provider(struct device_node *node)
>> +{
>> +       /* We delay construction of our struct clks until get time,
>> +        * because we need to be able to return -EPROBE_DEFER if the
>> +        * firmware driver isn't up yet.  clk core doesn't support
>> +        * re-probing on -EPROBE_DEFER, but callers of clk_get can.
>> +        */
>> +       of_clk_add_provider(node, rpi_firmware_delayed_get_clk, node);
>> +}
>> +
>> +CLK_OF_DECLARE(rpi_firmware_clocks, "raspberrypi,bcm2835-firmware-clocks",
>> +              rpi_firmware_init_clock_provider);
>
> Do you require CLK_OF_DECLARE here? Could this be a platform driver
> instead?

I'm not actually sure.  The common pattern seemed to be using
CLK_OF_DECLARE (130 files using it versus declaring a struct
platform_driver), and it seems to avoid a whole lot of boilerplate.
What would the advantage be?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150812/82521ee8/attachment.sig>

From eric at anholt.net  Wed Aug 12 11:45:13 2015
From: eric at anholt.net (Eric Anholt)
Date: Wed, 12 Aug 2015 11:45:13 -0700
Subject: [GIT PULL] RPi SOC changes for v4.3
Message-ID: <87oaic8hxi.fsf@eliezer.anholt.net>

Following Lee's forwarding of the DT changes for the firmware driver for
4.3, here's the associated driver for the brcm arm-soc tree.

I think this is the first time RPi changes have been forwarded through
Florian.  It was suggested by Florian when I was discussing RPi
upstreaming with him and Arnd.  I think it makes sense, since general
-next trees for Broadcom are maintained there, and he's willing to
accept pull requests at any point.

The following changes since commit d770e558e21961ad6cfdf0ff7df0eb5d7d4f0754:

  Linux 4.2-rc1 (2015-07-05 11:01:52 -0700)

are available in the git repository at:

  http://github.com/anholt/linux tags/rpi-soc-for-4.3-v1

for you to fetch changes up to d39ae82e65dc538295eea0384ed1ad8752dabbae:

  ARM: bcm2835: Add the Raspberry Pi firmware driver (2015-08-11 11:27:13 -0700)

----------------------------------------------------------------
This pull request contains the Raspberry Pi firmware driver, for
communicating with the VPU which has exclusive control of some of the
peripherals.

----------------------------------------------------------------
Eric Anholt (1):
      ARM: bcm2835: Add the Raspberry Pi firmware driver

 drivers/firmware/Kconfig                   |   7 +
 drivers/firmware/Makefile                  |   1 +
 drivers/firmware/raspberrypi.c             | 260 +++++++++++++++++++++++++++++
 include/soc/bcm2835/raspberrypi-firmware.h | 115 +++++++++++++
 4 files changed, 383 insertions(+)
 create mode 100644 drivers/firmware/raspberrypi.c
 create mode 100644 include/soc/bcm2835/raspberrypi-firmware.h
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150812/b876ccd9/attachment.sig>

From mturquette at baylibre.com  Wed Aug 12 12:00:14 2015
From: mturquette at baylibre.com (Michael Turquette)
Date: Wed, 12 Aug 2015 12:00:14 -0700
Subject: [PATCH v4 2/8] clk: Add a Raspberry Pi-specific clock driver.
In-Reply-To: <87wpx04c4q.fsf@eliezer.anholt.net>
References: <1437420787-32137-1-git-send-email-eric@anholt.net>
 <1437420787-32137-3-git-send-email-eric@anholt.net>
 <20150811211733.31346.11122@quantum> <87wpx04c4q.fsf@eliezer.anholt.net>
Message-ID: <20150812190014.31346.89987@quantum>

Quoting Eric Anholt (2015-08-12 11:04:05)
> Michael Turquette <mturquette at linaro.org> writes:
> 
> > Hi Eric,
> >
> > Quoting Eric Anholt (2015-07-20 12:33:01)
> >> +void __init rpi_firmware_init_clock_provider(struct device_node *node)
> >> +{
> >> +       /* We delay construction of our struct clks until get time,
> >> +        * because we need to be able to return -EPROBE_DEFER if the
> >> +        * firmware driver isn't up yet.  clk core doesn't support
> >> +        * re-probing on -EPROBE_DEFER, but callers of clk_get can.
> >> +        */
> >> +       of_clk_add_provider(node, rpi_firmware_delayed_get_clk, node);
> >> +}
> >> +
> >> +CLK_OF_DECLARE(rpi_firmware_clocks, "raspberrypi,bcm2835-firmware-clocks",
> >> +              rpi_firmware_init_clock_provider);
> >
> > Do you require CLK_OF_DECLARE here? Could this be a platform driver
> > instead?
> 
> I'm not actually sure.  The common pattern seemed to be using
> CLK_OF_DECLARE (130 files using it versus declaring a struct
> platform_driver), and it seems to avoid a whole lot of boilerplate.
> What would the advantage be?

Correctly using the Linux driver model, having a nice struct device and
things like supsend and resume handlers, etc.

There is a lot of CLK_OF_DECLARE going on in drivers/clk, but I am
hoping we can fix that. The main reason for CLK_OF_DECLARE is when you
need to register clocks very early (e.g. timers). This is often not the
case.

Regards,
Mike


From eric at anholt.net  Wed Aug 12 17:56:13 2015
From: eric at anholt.net (Eric Anholt)
Date: Wed, 12 Aug 2015 17:56:13 -0700
Subject: Raspberry Pi KMS-only driver
Message-ID: <1439427380-2436-1-git-send-email-eric@anholt.net>

Here's the first patch series for graphics on the Raspberry Pi.  It
brings up fbcon on KMS, and the xf86-video-modesetting driver runs so
you can do X with no further userspace drivers.

Right now this series doesn't support changing video modes away from
the firmware boot setup, because when I set the pixel clock using the
rpi-dt-clocks series I end up with a black display. even on no-op
modesets.

I've dropped all of the 3D bits from this series, to simplify this
review stage.  See vc4-kms-v3d-rpi2 for a series that works with the
current Mesa driver, based on downstream 2709.  I squashed all of my
messy KMS series down, but kept Derek's patches separate to make sure
he gets credit.

This code can also be found at:

https://github.com/anholt/linux/tree/vc4-kms-squash



From eric at anholt.net  Wed Aug 12 17:56:15 2015
From: eric at anholt.net (Eric Anholt)
Date: Wed, 12 Aug 2015 17:56:15 -0700
Subject: [PATCH 2/7] MAINTAINERS: Add myself for the new VC4 (RPi GPU)
 graphics driver.
In-Reply-To: <1439427380-2436-1-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
Message-ID: <1439427380-2436-3-git-send-email-eric@anholt.net>

Signed-off-by: Eric Anholt <eric at anholt.net>
---
 MAINTAINERS | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 8133cef..d0dc42c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3573,6 +3573,12 @@ S:	Maintained
 F:	drivers/gpu/drm/rockchip/
 F:	Documentation/devicetree/bindings/video/rockchip*
 
+DRM DRIVERS FOR VC4
+M:	Eric Anholt <eric at anholt.net>
+T:	git git://github.com/anholt/linux
+S:	Maintained
+F:	drivers/gpu/drm/vc4/*
+
 DSBR100 USB FM RADIO DRIVER
 M:	Alexey Klimov <klimov.linux at gmail.com>
 L:	linux-media at vger.kernel.org
-- 
2.1.4



From eric at anholt.net  Wed Aug 12 17:56:14 2015
From: eric at anholt.net (Eric Anholt)
Date: Wed, 12 Aug 2015 17:56:14 -0700
Subject: [PATCH 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <1439427380-2436-1-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
Message-ID: <1439427380-2436-2-git-send-email-eric@anholt.net>

Signed-off-by: Eric Anholt <eric at anholt.net>
---
 .../devicetree/bindings/gpu/brcm,bcm-vc4.txt       | 83 ++++++++++++++++++++++
 1 file changed, 83 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt

diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
new file mode 100644
index 0000000..2b13e61
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
@@ -0,0 +1,83 @@
+Broadcom VC4 GPU
+
+The VC4 device present on the Raspberry Pi includes a display system
+with HDMI output and the HVS scaler for compositing display planes.
+
+Required properties for VC4:
+- compatible:	Should be "brcm,vc4"
+- crtcs:	List of references to pixelvalve scanout engines
+- hvss:		List of references to HVS video scalers
+- encoders:	List of references to output encoders (HDMI, SDTV)
+
+Required properties for Pixel Valve:
+- compatible:	Should be "brcm,vc4-pixelvalve"
+- reg:		Physical base address and length of the PV's registers
+- interrupts:	The interrupt number
+		  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+
+Required properties for HVS:
+- compatible:	Should be "brcm,vc4-hvs"
+- reg:		Physical base address and length of the HVS's registers
+- interrupts:	The interrupt number
+		  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+
+Required properties for HDMI
+- compatible:	Should be "brcm,vc4-hdmi"
+- reg:		Physical base address and length of the two register ranges
+		  ("HDMI" and "HD")
+- interrupts:	The interrupt numbers
+		  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+- ddc:		phandle of the I2C controller used for DDC EDID probing
+- crtc:		phandle to the pixelvalve CRTC the HDMI encoder is attached to
+
+Optional properties for HDMI:
+- hpd-gpio:	The GPIO pin for HDMI hotplug detect (if it doesn't appear
+		  as an interrupt/status bit in the HDMI controller
+		  itself).  See bindings/pinctrl/brcm,bcm2835-gpio.txt
+
+Example:
+/ {
+	soc {
+		pv0: brcm,vc4-pixelvalve at 7e206000 {
+			compatible = "brcm,vc4-pixelvalve";
+			reg = <0x7e206000 0x100>;
+			interrupts = <2 13>; /* pwa2 */
+		};
+
+		pv1: brcm,vc4-pixelvalve at 7e207000 {
+			compatible = "brcm,vc4-pixelvalve";
+			reg = <0x7e207000 0x100>;
+			interrupts = <2 14>; /* pwa1 */
+		};
+
+		pv2: brcm,vc4-pixelvalve at 7e807000 {
+			compatible = "brcm,vc4-pixelvalve";
+			reg = <0x7e807000 0x100>;
+			interrupts = <2 10>; /* pixelvalve */
+		};
+
+		hvs: brcm,hvs at 7e400000 {
+			compatible = "brcm,vc4-hvs";
+			reg = <0x7e400000 0x6000>;
+			interrupts = <2 1>;
+		};
+
+		hdmi: brcm,vc4-hdmi at 7e902000 {
+			compatible = "brcm,vc4-hdmi";
+			reg = <0x7e902000 0x600>,
+			      <0x7e808000 0x100>;
+			interrupts = <2 8>, <2 9>;
+			ddc = <&i2c2>;
+			hpd-gpio = <&gpio 46 GPIO_ACTIVE_HIGH>;
+			crtc = <&pv2>;
+		};
+
+		vc4: vc4 at 0x7e4c0000 {
+			compatible = "brcm,vc4";
+
+			crtcs = <&pv0>, <&pv1>, <&pv2>;
+			encoders = <&hdmi>;
+			hvss = <&hvs>;
+		};
+	};
+};
-- 
2.1.4



From eric at anholt.net  Wed Aug 12 17:56:17 2015
From: eric at anholt.net (Eric Anholt)
Date: Wed, 12 Aug 2015 17:56:17 -0700
Subject: [PATCH 4/7] drm/vc4: Use the fbdev_cma helpers
In-Reply-To: <1439427380-2436-1-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
Message-ID: <1439427380-2436-5-git-send-email-eric@anholt.net>

From: Derek Foreman <derekf at osg.samsung.com>

Keep the fbdev_cma pointer around so we can use it on hotplog and close
to ensure the frame buffer console is in a useful state.

Signed-off-by: Derek Foreman <derekf at osg.samsung.com>
Signed-off-by: Eric Anholt <eric at anholt.net>
---
 drivers/gpu/drm/vc4/vc4_drv.c | 15 +++++++++++++++
 drivers/gpu/drm/vc4/vc4_drv.h |  2 ++
 drivers/gpu/drm/vc4/vc4_kms.c | 18 +++++++++++++++---
 3 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index 9ecf4b8b..90414d1 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
+#include "drm_fb_cma_helper.h"
 
 #include "vc4_drv.h"
 #include "vc4_regs.h"
@@ -70,6 +71,11 @@ static int vc4_drm_load(struct drm_device *dev, unsigned long flags)
 
 static int vc4_drm_unload(struct drm_device *dev)
 {
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+	if (vc4->fbdev)
+		drm_fbdev_cma_fini(vc4->fbdev);
+
 	drm_mode_config_cleanup(dev);
 
 	component_unbind_all(dev->dev, dev);
@@ -85,6 +91,14 @@ static void vc4_drm_preclose(struct drm_device *dev, struct drm_file *file)
 		vc4_cancel_page_flip(crtc, file);
 }
 
+static void vc4_lastclose(struct drm_device *dev)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+	if (vc4->fbdev)
+		drm_fbdev_cma_restore_mode(vc4->fbdev);
+}
+
 static const struct file_operations vc4_drm_fops = {
 	.owner = THIS_MODULE,
 	.open = drm_open,
@@ -109,6 +123,7 @@ static struct drm_driver vc4_drm_driver = {
 			    DRIVER_PRIME),
 	.load = vc4_drm_load,
 	.unload = vc4_drm_unload,
+	.lastclose = vc4_lastclose,
 	.set_busid = drm_platform_set_busid,
 	.preclose = vc4_drm_preclose,
 
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 1b0a0b0..6edc14b 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -15,6 +15,8 @@ struct vc4_dev {
 	struct vc4_hdmi *hdmi;
 	struct vc4_hvs *hvs;
 	struct vc4_crtc *crtc[3];
+
+	struct drm_fbdev_cma *fbdev;
 };
 
 static inline struct vc4_dev *
diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
index e5e96bc..a34439f 100644
--- a/drivers/gpu/drm/vc4/vc4_kms.c
+++ b/drivers/gpu/drm/vc4/vc4_kms.c
@@ -21,7 +21,16 @@
 #include "drm_fb_cma_helper.h"
 #include "vc4_drv.h"
 
+static void vc4_output_poll_changed(struct drm_device *dev)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+	if (vc4->fbdev)
+		drm_fbdev_cma_hotplug_event(vc4->fbdev);
+}
+
 static const struct drm_mode_config_funcs vc4_mode_funcs = {
+	.output_poll_changed = vc4_output_poll_changed,
 	.atomic_check = drm_atomic_helper_check,
 	.atomic_commit = drm_atomic_helper_commit,
 	.fb_create = drm_fb_cma_create,
@@ -54,6 +63,7 @@ fail:
 
 int vc4_kms_load(struct drm_device *dev)
 {
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	int ret;
 
 	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
@@ -73,9 +83,11 @@ int vc4_kms_load(struct drm_device *dev)
 
 	drm_mode_config_reset(dev);
 
-	drm_fbdev_cma_init(dev, 32,
-			   dev->mode_config.num_crtc,
-			   dev->mode_config.num_connector);
+	vc4->fbdev = drm_fbdev_cma_init(dev, 32,
+					dev->mode_config.num_crtc,
+					dev->mode_config.num_connector);
+	if (IS_ERR(vc4->fbdev))
+		vc4->fbdev = NULL;
 
 	drm_kms_helper_poll_init(dev);
 
-- 
2.1.4



From eric at anholt.net  Wed Aug 12 17:56:18 2015
From: eric at anholt.net (Eric Anholt)
Date: Wed, 12 Aug 2015 17:56:18 -0700
Subject: [PATCH 5/7] drm/vc4: Allow vblank to be disabled
In-Reply-To: <1439427380-2436-1-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
Message-ID: <1439427380-2436-6-git-send-email-eric@anholt.net>

From: Derek Foreman <derekf at osg.samsung.com>

Signed-off-by: Derek Foreman <derekf at osg.samsung.com>
Signed-off-by: Eric Anholt <eric at anholt.net>
---
 drivers/gpu/drm/vc4/vc4_kms.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
index a34439f..f881cd4 100644
--- a/drivers/gpu/drm/vc4/vc4_kms.c
+++ b/drivers/gpu/drm/vc4/vc4_kms.c
@@ -77,6 +77,8 @@ int vc4_kms_load(struct drm_device *dev)
 	dev->mode_config.funcs = &vc4_mode_funcs;
 	dev->mode_config.preferred_depth = 24;
 
+	dev->vblank_disable_allowed = true;
+
 	ret = vc4_init_modeset_objects(dev);
 	if (ret)
 		goto fail;
-- 
2.1.4



From eric at anholt.net  Wed Aug 12 17:56:20 2015
From: eric at anholt.net (Eric Anholt)
Date: Wed, 12 Aug 2015 17:56:20 -0700
Subject: [PATCH 7/7] ARM: bcm2835: Add VC4 to the device tree.
In-Reply-To: <1439427380-2436-1-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
Message-ID: <1439427380-2436-8-git-send-email-eric@anholt.net>

Signed-off-by: Eric Anholt <eric at anholt.net>
---
 arch/arm/boot/dts/bcm2835.dtsi | 43 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
index 8f3ba88..dda67aa 100644
--- a/arch/arm/boot/dts/bcm2835.dtsi
+++ b/arch/arm/boot/dts/bcm2835.dtsi
@@ -1,4 +1,5 @@
 #include <dt-bindings/pinctrl/bcm2835.h>
+#include <dt-bindings/gpio/gpio.h>
 #include "skeleton.dtsi"
 
 / {
@@ -167,6 +168,48 @@
 		arm-pmu {
 			compatible = "arm,arm1176-pmu";
 		};
+
+		hdmi: brcm,vc4-hdmi at 7e902000 {
+			compatible = "brcm,vc4-hdmi";
+			reg = <0x7e902000 0x600>,
+			      <0x7e808000 0x100>;
+			interrupts = <2 8>, <2 9>;
+			ddc = <&i2c2>;
+			hpd-gpio = <&gpio 46 GPIO_ACTIVE_HIGH>;
+			crtc = <&pv2>;
+		};
+
+		pv0: brcm,vc4-pixelvalve at 7e206000 {
+			compatible = "brcm,vc4-pixelvalve";
+			reg = <0x7e206000 0x100>;
+			interrupts = <2 13>; /* pwa2 */
+		};
+
+		pv1: brcm,vc4-pixelvalve at 7e207000 {
+			compatible = "brcm,vc4-pixelvalve";
+			reg = <0x7e207000 0x100>;
+			interrupts = <2 14>; /* pwa1 */
+		};
+
+		pv2: brcm,vc4-pixelvalve at 7e807000 {
+			compatible = "brcm,vc4-pixelvalve";
+			reg = <0x7e807000 0x100>;
+			interrupts = <2 10>; /* pixelvalve */
+		};
+
+		hvs: brcm,hvs at 7e400000 {
+			compatible = "brcm,vc4-hvs";
+			reg = <0x7e400000 0x6000>;
+			interrupts = <2 1>;
+		};
+
+		vc4: vc4 at 0x7e4c0000 {
+			compatible = "brcm,vc4";
+
+			crtcs = <&pv0>, <&pv1>, <&pv2>;
+			encoders = <&hdmi>;
+			hvss = <&hvs>;
+		};
 	};
 
 	clocks {
-- 
2.1.4



From eric at anholt.net  Wed Aug 12 17:56:19 2015
From: eric at anholt.net (Eric Anholt)
Date: Wed, 12 Aug 2015 17:56:19 -0700
Subject: [PATCH 6/7] ARM: bcm2835: Add the DDC I2C controller to the device
 tree.
In-Reply-To: <1439427380-2436-1-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
Message-ID: <1439427380-2436-7-git-send-email-eric@anholt.net>

We need to use it for getting video modes over HDMI.

Signed-off-by: Eric Anholt <eric at anholt.net>
---
 arch/arm/boot/dts/bcm2835.dtsi | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
index 301c73f..8f3ba88 100644
--- a/arch/arm/boot/dts/bcm2835.dtsi
+++ b/arch/arm/boot/dts/bcm2835.dtsi
@@ -149,6 +149,15 @@
 			status = "disabled";
 		};
 
+		i2c2: i2c at 7e805000 {
+			compatible = "brcm,bcm2835-i2c";
+			reg = <0x7e805000 0x1000>;
+			interrupts = <2 21>;
+			clocks = <&clk_i2c>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
 		usb at 7e980000 {
 			compatible = "brcm,bcm2835-usb";
 			reg = <0x7e980000 0x10000>;
-- 
2.1.4



From eric at anholt.net  Wed Aug 12 17:56:16 2015
From: eric at anholt.net (Eric Anholt)
Date: Wed, 12 Aug 2015 17:56:16 -0700
Subject: [PATCH 3/7] drm/vc4: Add KMS support for Raspberry Pi.
In-Reply-To: <1439427380-2436-1-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
Message-ID: <1439427380-2436-4-git-send-email-eric@anholt.net>

This is the start of a full VC4 driver.  Right now this just supports
configuring the display using a pre-existing video mode (because
changing the pixel clock isn't available yet, and doesn't work when it
is).  However, this is enough for fbcon and bringing up X using
xf86-video-modesetting.

Signed-off-by: Eric Anholt <eric at anholt.net>
---
 drivers/gpu/drm/Kconfig           |   2 +
 drivers/gpu/drm/Makefile          |   1 +
 drivers/gpu/drm/vc4/Kconfig       |  14 +
 drivers/gpu/drm/vc4/Makefile      |  18 ++
 drivers/gpu/drm/vc4/vc4_bo.c      |  54 ++++
 drivers/gpu/drm/vc4/vc4_crtc.c    | 583 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/vc4/vc4_debugfs.c |  38 +++
 drivers/gpu/drm/vc4/vc4_drv.c     | 249 +++++++++++++++
 drivers/gpu/drm/vc4/vc4_drv.h     | 123 +++++++
 drivers/gpu/drm/vc4/vc4_hdmi.c    | 651 ++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/vc4/vc4_hvs.c     | 172 ++++++++++
 drivers/gpu/drm/vc4/vc4_kms.c     |  84 +++++
 drivers/gpu/drm/vc4/vc4_plane.c   | 320 +++++++++++++++++++
 drivers/gpu/drm/vc4/vc4_regs.h    | 562 ++++++++++++++++++++++++++++++++
 14 files changed, 2871 insertions(+)
 create mode 100644 drivers/gpu/drm/vc4/Kconfig
 create mode 100644 drivers/gpu/drm/vc4/Makefile
 create mode 100644 drivers/gpu/drm/vc4/vc4_bo.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_crtc.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_debugfs.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_drv.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_drv.h
 create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_hvs.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_kms.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_plane.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_regs.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index c46ca31..1730a76 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -240,3 +240,5 @@ source "drivers/gpu/drm/sti/Kconfig"
 source "drivers/gpu/drm/amd/amdkfd/Kconfig"
 
 source "drivers/gpu/drm/imx/Kconfig"
+
+source "drivers/gpu/drm/vc4/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 5713d05..b991ac5 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_DRM_MGA)	+= mga/
 obj-$(CONFIG_DRM_I810)	+= i810/
 obj-$(CONFIG_DRM_I915)  += i915/
 obj-$(CONFIG_DRM_MGAG200) += mgag200/
+obj-$(CONFIG_DRM_VC4)  += vc4/
 obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/
 obj-$(CONFIG_DRM_SIS)   += sis/
 obj-$(CONFIG_DRM_SAVAGE)+= savage/
diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig
new file mode 100644
index 0000000..130cc94
--- /dev/null
+++ b/drivers/gpu/drm/vc4/Kconfig
@@ -0,0 +1,14 @@
+config DRM_VC4
+	tristate "Broadcom VC4 Graphics"
+	depends on ARCH_BCM2835
+	depends on DRM
+	select DRM_KMS_HELPER
+	select DRM_KMS_FB_HELPER
+	select DRM_KMS_CMA_HELPER
+	help
+	  Choose this option if you have a system that has a Broadcom
+	  VC4 GPU, such as the Raspberry Pi or other BCM2708/BCM2835.
+
+	  This driver requires that "avoid_warnings=2" be present in
+	  the config.txt for the firmware, to keep it from smashing
+	  our display setup.
diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile
new file mode 100644
index 0000000..4aa07ca
--- /dev/null
+++ b/drivers/gpu/drm/vc4/Makefile
@@ -0,0 +1,18 @@
+ccflags-y := -Iinclude/drm
+
+# Please keep these build lists sorted!
+
+# core driver code
+vc4-y := \
+	vc4_bo.o \
+	vc4_crtc.o \
+	vc4_drv.o \
+	vc4_kms.o \
+	vc4_hdmi.o \
+	vc4_hvs.o \
+	vc4_plane.o \
+	$()
+
+vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o
+
+obj-$(CONFIG_DRM_VC4)  += vc4.o
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
new file mode 100644
index 0000000..fee8cac
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -0,0 +1,54 @@
+/*
+ *  Copyright ? 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* DOC: VC4 GEM BO management support.
+ *
+ * The VC4 GPU architecture (both scanout and rendering) has direct
+ * access to system memory with no MMU in between.  To support it, we
+ * use the GEM CMA helper functions to allocate contiguous ranges of
+ * physical memory for our BOs.
+ */
+
+#include "vc4_drv.h"
+
+struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size)
+{
+	struct drm_gem_cma_object *cma_obj;
+
+	cma_obj = drm_gem_cma_create(dev, size);
+	if (IS_ERR(cma_obj))
+		return NULL;
+	else
+		return to_vc4_bo(&cma_obj->base);
+}
+
+int vc4_dumb_create(struct drm_file *file_priv,
+		    struct drm_device *dev,
+		    struct drm_mode_create_dumb *args)
+{
+	int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+	struct vc4_bo *bo = NULL;
+	int ret;
+
+	if (args->pitch < min_pitch)
+		args->pitch = min_pitch;
+
+	if (args->size < args->pitch * args->height)
+		args->size = args->pitch * args->height;
+
+	mutex_lock(&dev->struct_mutex);
+	bo = vc4_bo_create(dev, roundup(args->size, PAGE_SIZE));
+	mutex_unlock(&dev->struct_mutex);
+	if (!bo)
+		return -ENOMEM;
+
+	ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle);
+	drm_gem_object_unreference_unlocked(&bo->base.base);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
new file mode 100644
index 0000000..6f127f0
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/**
+ * DOC: VC4 CRTC module
+ *
+ * In VC4, the Pixel Valve is what most closely corresponds to the
+ * DRM's concept of a CRTC.  The PV generates video timings from the
+ * pixel clock and its configuration.
+ *
+ * However, the DRM CRTC also collects the configuration of all the
+ * DRM planes attached to it.  As a result, this file also manages
+ * setup of the VC4 HVS's display elements on the CRTC.
+ */
+
+#include "drm_atomic_helper.h"
+#include "drm_crtc_helper.h"
+#include "linux/clk.h"
+#include "linux/component.h"
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+
+#define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset))
+#define CRTC_READ(offset) readl(vc4_crtc->regs + (offset))
+
+#define CRTC_REG(reg) { reg, #reg }
+static const struct {
+	u32 reg;
+	const char *name;
+} crtc_regs[] = {
+	CRTC_REG(PV_CONTROL),
+	CRTC_REG(PV_V_CONTROL),
+	CRTC_REG(PV_VSYNCD),
+	CRTC_REG(PV_HORZA),
+	CRTC_REG(PV_HORZB),
+	CRTC_REG(PV_VERTA),
+	CRTC_REG(PV_VERTB),
+	CRTC_REG(PV_VERTA_EVEN),
+	CRTC_REG(PV_VERTB_EVEN),
+	CRTC_REG(PV_INTEN),
+	CRTC_REG(PV_INTSTAT),
+	CRTC_REG(PV_STAT),
+	CRTC_REG(PV_HACT_ACT),
+};
+
+static void vc4_crtc_dump_regs(struct vc4_crtc *vc4_crtc)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(crtc_regs); i++) {
+		DRM_INFO("0x%04x (%s): 0x%08x\n",
+			 crtc_regs[i].reg, crtc_regs[i].name,
+			 CRTC_READ(crtc_regs[i].reg));
+	}
+}
+
+#ifdef CONFIG_DEBUG_FS
+int vc4_crtc_debugfs_regs(struct seq_file *m, void *unused)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_device *dev = node->minor->dev;
+	int crtc_index = (uintptr_t)node->info_ent->data;
+	struct drm_crtc *crtc;
+	struct vc4_crtc *vc4_crtc;
+	int i;
+
+	i = 0;
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+		if (i == crtc_index)
+			break;
+		i++;
+	}
+	if (!crtc)
+		return 0;
+	vc4_crtc = to_vc4_crtc(crtc);
+
+	for (i = 0; i < ARRAY_SIZE(crtc_regs); i++) {
+		seq_printf(m, "%s (0x%04x): 0x%08x\n",
+			   crtc_regs[i].name, crtc_regs[i].reg,
+			   CRTC_READ(crtc_regs[i].reg));
+	}
+
+	return 0;
+}
+#endif
+
+static void vc4_crtc_destroy(struct drm_crtc *crtc)
+{
+	drm_crtc_cleanup(crtc);
+}
+
+static bool vc4_crtc_mode_fixup(struct drm_crtc *crtc,
+				const struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static u32 vc4_get_fifo_full_level(u32 format)
+{
+	static const u32 fifo_len_bytes = 64;
+	static const u32 hvs_latency_pix = 6;
+
+	switch (format) {
+	case PV_CONTROL_FORMAT_DSIV_16:
+	case PV_CONTROL_FORMAT_DSIC_16:
+		return fifo_len_bytes - 2 * hvs_latency_pix;
+	case PV_CONTROL_FORMAT_DSIV_18:
+		return fifo_len_bytes - 14;
+	case PV_CONTROL_FORMAT_24:
+	case PV_CONTROL_FORMAT_DSIV_24:
+	default:
+		return fifo_len_bytes - 3 * hvs_latency_pix;
+	}
+}
+
+static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	struct drm_crtc_state *state = crtc->state;
+	struct drm_display_mode *mode = &state->adjusted_mode;
+	u32 vactive = (mode->vdisplay >>
+		       ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0));
+	u32 format = PV_CONTROL_FORMAT_24;
+	bool debug_dump_regs = false;
+
+	if (debug_dump_regs) {
+		DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc));
+		vc4_crtc_dump_regs(vc4_crtc);
+	}
+
+	/* This is where we would set the pixel clock. */
+
+	/* Reset the PV fifo. */
+	CRTC_WRITE(PV_CONTROL, 0);
+	CRTC_WRITE(PV_CONTROL, PV_CONTROL_FIFO_CLR | PV_CONTROL_EN);
+	CRTC_WRITE(PV_CONTROL, 0);
+
+	CRTC_WRITE(PV_HORZA,
+		   VC4_SET_FIELD(mode->htotal - mode->hsync_end,
+				 PV_HORZA_HBP) |
+		   VC4_SET_FIELD(mode->hsync_end - mode->hsync_start,
+				 PV_HORZA_HSYNC));
+	CRTC_WRITE(PV_HORZB,
+		   VC4_SET_FIELD(mode->hsync_start - mode->hdisplay,
+				 PV_HORZB_HFP) |
+		   VC4_SET_FIELD(mode->hdisplay, PV_HORZB_HACTIVE));
+
+	CRTC_WRITE(PV_VERTA,
+		   VC4_SET_FIELD(mode->vtotal - mode->vsync_end,
+				 PV_VERTA_VBP) |
+		   VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
+				 PV_VERTA_VSYNC));
+	CRTC_WRITE(PV_VERTB,
+		   VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
+				 PV_VERTB_VFP) |
+		   VC4_SET_FIELD(vactive, PV_VERTB_VACTIVE));
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		/* Write PV_VERTA_EVEN/VERTB_EVEN */
+	}
+
+	CRTC_WRITE(PV_HACT_ACT, mode->hdisplay);
+
+	CRTC_WRITE(PV_V_CONTROL,
+		   PV_VCONTROL_CONTINUOUS);
+
+	CRTC_WRITE(PV_CONTROL,
+		   VC4_SET_FIELD(format, PV_CONTROL_FORMAT) |
+		   VC4_SET_FIELD(vc4_get_fifo_full_level(format),
+				 PV_CONTROL_FIFO_LEVEL) |
+		   PV_CONTROL_CLR_AT_START |
+		   PV_CONTROL_TRIGGER_UNDERFLOW |
+		   PV_CONTROL_WAIT_HSTART |
+		   VC4_SET_FIELD(PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI,
+				 PV_CONTROL_CLK_SELECT) |
+		   PV_CONTROL_FIFO_CLR |
+		   PV_CONTROL_EN);
+
+	if (debug_dump_regs) {
+		DRM_INFO("CRTC %d regs after:\n", drm_crtc_index(crtc));
+		vc4_crtc_dump_regs(vc4_crtc);
+	}
+}
+
+static void
+require_hvs_enabled(struct drm_device *dev)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+	WARN_ON_ONCE((HVS_READ(SCALER_DISPCTRL) & SCALER_DISPCTRL_ENABLE) !=
+		     SCALER_DISPCTRL_ENABLE);
+}
+
+static void vc4_crtc_disable(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+
+	require_hvs_enabled(dev);
+
+	CRTC_WRITE(PV_V_CONTROL,
+		   CRTC_READ(PV_V_CONTROL) & ~PV_VCONTROL_VIDEN);
+	while (CRTC_READ(PV_V_CONTROL) & PV_VCONTROL_VIDEN)
+		cpu_relax();
+
+	/* Without a wait here, we end up with a black screen. */
+	msleep(30);
+
+	if (HVS_READ(SCALER_DISPCTRLX(vc4_crtc->channel)) &
+	    SCALER_DISPCTRLX_ENABLE) {
+		HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel),
+			  SCALER_DISPCTRLX_RESET);
+
+		/* While the docs say that reset is self-clearing, it
+		 * seems it doesn't actually.
+		 */
+		HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), 0);
+	}
+
+	/* Once we leave, the scaler should be disabled and its fifo empty. */
+
+	WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(vc4_crtc->channel)) &
+		     SCALER_DISPCTRLX_RESET);
+
+	WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(vc4_crtc->channel)),
+				   SCALER_DISPSTATX_MODE) !=
+		     SCALER_DISPSTATX_MODE_DISABLED);
+
+	WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(vc4_crtc->channel)) &
+		      (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) !=
+		     SCALER_DISPSTATX_EMPTY);
+}
+
+static void vc4_crtc_enable(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	struct drm_crtc_state *state = crtc->state;
+	struct drm_display_mode *mode = &state->adjusted_mode;
+
+	require_hvs_enabled(dev);
+
+	/* Turn on the scaler, which will wait for vstart to start
+	 * compositing.
+	 */
+	HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel),
+		  VC4_SET_FIELD(mode->hdisplay, SCALER_DISPCTRLX_WIDTH) |
+		  VC4_SET_FIELD(mode->vdisplay, SCALER_DISPCTRLX_HEIGHT) |
+		  SCALER_DISPCTRLX_ENABLE);
+
+	/* Turn on the pixel valve, which will emit the vstart signal. */
+	CRTC_WRITE(PV_V_CONTROL,
+		   CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN);
+}
+
+static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
+				 struct drm_crtc_state *state)
+{
+	struct drm_device *dev = crtc->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct drm_plane *plane;
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	u32 dlist_count = 0;
+
+	drm_atomic_crtc_state_for_each_plane(plane, state) {
+		struct drm_plane_state *plane_state =
+			state->state->plane_states[drm_plane_index(plane)];
+
+		/* plane might not have changed, in which case take
+		 * current state:
+		 */
+		if (!plane_state)
+			plane_state = plane->state;
+
+		dlist_count += vc4_plane_dlist_size(plane_state);
+	}
+
+	dlist_count++; /* Account for SCALER_CTL0_END. */
+
+	if (!vc4_crtc->dlist || dlist_count > vc4_crtc->dlist_size) {
+		vc4_crtc->dlist = ((u32 __iomem *)vc4->hvs->dlist +
+				   HVS_BOOTLOADER_DLIST_END);
+		vc4_crtc->dlist_size = ((SCALER_DLIST_SIZE >> 2) -
+					HVS_BOOTLOADER_DLIST_END);
+
+		if (dlist_count > vc4_crtc->dlist_size) {
+			DRM_DEBUG_KMS("dlist too large for CRTC (%d > %d).\n",
+				      dlist_count, vc4_crtc->dlist_size);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static void vc4_crtc_atomic_begin(struct drm_crtc *crtc)
+{
+}
+
+static void vc4_crtc_atomic_flush(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	struct drm_plane *plane;
+	bool debug_dump_regs = false;
+	u32 __iomem *dlist_next = vc4_crtc->dlist;
+
+	if (debug_dump_regs) {
+		DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
+		vc4_hvs_dump_state(dev);
+	}
+
+	/* Copy all the active planes' dlist contents to the hardware dlist.
+	 *
+	 * XXX: If the new display list was large enough that it
+	 * overlapped a currently-read display list, we need to do
+	 * something like disable scanout before putting in the new
+	 * list.  For now, we're safe because we only have the two
+	 * planes.
+	 */
+	drm_atomic_crtc_for_each_plane(plane, crtc) {
+		dlist_next += vc4_plane_write_dlist(plane, dlist_next);
+	}
+
+	if (dlist_next == vc4_crtc->dlist) {
+		/* If no planes were enabled, use the SCALER_CTL0_END
+		 * at the start of the display list memory (in the
+		 * bootloader section).  We'll rewrite that
+		 * SCALER_CTL0_END, just in case, though.
+		 */
+		writel(SCALER_CTL0_END, vc4->hvs->dlist);
+		HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), 0);
+	} else {
+		writel(SCALER_CTL0_END, dlist_next);
+		dlist_next++;
+
+		HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
+			  (u32 *)vc4_crtc->dlist - (u32 *)vc4->hvs->dlist);
+
+		/* Make the next display list start after ours. */
+		vc4_crtc->dlist_size -= (dlist_next - vc4_crtc->dlist);
+		vc4_crtc->dlist = dlist_next;
+	}
+
+	if (debug_dump_regs) {
+		DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
+		vc4_hvs_dump_state(dev);
+	}
+
+	if (crtc->state->event) {
+		unsigned long flags;
+
+		crtc->state->event->pipe = drm_crtc_index(crtc);
+
+		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+		spin_lock_irqsave(&dev->event_lock, flags);
+		vc4_crtc->event = crtc->state->event;
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+		crtc->state->event = NULL;
+	}
+}
+
+int vc4_enable_vblank(struct drm_device *dev, int crtc_id)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
+
+	CRTC_WRITE(PV_INTEN, PV_INT_VFP_START);
+
+	return 0;
+}
+
+void vc4_disable_vblank(struct drm_device *dev, int crtc_id)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
+
+	CRTC_WRITE(PV_INTEN, 0);
+}
+
+static void
+vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
+{
+	struct drm_crtc *crtc = &vc4_crtc->base;
+	struct drm_device *dev = crtc->dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	if (vc4_crtc->event) {
+		drm_crtc_send_vblank_event(crtc, vc4_crtc->event);
+		vc4_crtc->event = NULL;
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
+{
+	struct vc4_crtc *vc4_crtc = data;
+	u32 stat = CRTC_READ(PV_INTSTAT);
+	irqreturn_t ret = IRQ_NONE;
+
+	if (stat & PV_INT_VFP_START) {
+		CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
+		drm_crtc_handle_vblank(&vc4_crtc->base);
+		vc4_crtc_handle_page_flip(vc4_crtc);
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+static const struct drm_crtc_funcs vc4_crtc_funcs = {
+	.set_config = drm_atomic_helper_set_config,
+	.destroy = vc4_crtc_destroy,
+	.page_flip = drm_atomic_helper_page_flip,
+	.set_property = NULL,
+	.cursor_set = NULL, /* handled by drm_mode_cursor_universal */
+	.cursor_move = NULL, /* handled by drm_mode_cursor_universal */
+	.reset = drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
+	.mode_fixup = vc4_crtc_mode_fixup,
+	.mode_set_nofb = vc4_crtc_mode_set_nofb,
+	.disable = vc4_crtc_disable,
+	.enable = vc4_crtc_enable,
+	.atomic_check = vc4_crtc_atomic_check,
+	.atomic_begin = vc4_crtc_atomic_begin,
+	.atomic_flush = vc4_crtc_atomic_flush,
+};
+
+/* Frees the page flip event when the DRM device is closed with the
+ * event still outstanding.
+ */
+void vc4_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file)
+{
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	if (vc4_crtc->event && vc4_crtc->event->base.file_priv == file) {
+		vc4_crtc->event->base.destroy(&vc4_crtc->event->base);
+		drm_crtc_vblank_put(crtc);
+		vc4_crtc->event = NULL;
+	}
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct vc4_dev *vc4 = to_vc4_dev(drm);
+	struct vc4_crtc *vc4_crtc;
+	struct drm_crtc *crtc;
+	struct drm_plane *primary_plane, *cursor_plane;
+	int ret;
+
+	primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY);
+	if (!primary_plane) {
+		dev_err(dev, "failed to construct primary plane\n");
+		ret = PTR_ERR(primary_plane);
+		goto fail;
+	}
+
+	cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
+	if (!cursor_plane) {
+		dev_err(dev, "failed to construct cursor plane\n");
+		ret = PTR_ERR(cursor_plane);
+		goto fail;
+	}
+
+	vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
+	if (!vc4_crtc)
+		return -ENOMEM;
+	crtc = &vc4_crtc->base;
+
+	vc4_crtc->regs = vc4_ioremap_regs(pdev, 0);
+	if (IS_ERR(vc4_crtc->regs))
+		return PTR_ERR(vc4_crtc->regs);
+
+	drm_crtc_init_with_planes(drm, crtc, primary_plane, cursor_plane,
+				  &vc4_crtc_funcs);
+	drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
+	primary_plane->crtc = crtc;
+	cursor_plane->crtc = crtc;
+	vc4->crtc[drm_crtc_index(crtc)] = vc4_crtc;
+
+	/* Until we have full scanout setup to route things through to
+	 * encoders, line things up like the firmware did.
+	 */
+	switch (drm_crtc_index(crtc)) {
+	case 0:
+		vc4_crtc->channel = 0;
+		break;
+	case 1:
+		vc4_crtc->channel = 2;
+		break;
+	default:
+	case 2:
+		vc4_crtc->channel = 1;
+		break;
+	}
+
+	CRTC_WRITE(PV_INTEN, 0);
+	CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
+	ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
+			       vc4_crtc_irq_handler, 0, "vc4 crtc", vc4_crtc);
+
+	platform_set_drvdata(pdev, vc4_crtc);
+
+	return 0;
+
+fail:
+	return ret;
+}
+
+static void vc4_crtc_unbind(struct device *dev, struct device *master,
+			    void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vc4_crtc *vc4_crtc = dev_get_drvdata(dev);
+
+	vc4_crtc_destroy(&vc4_crtc->base);
+
+	CRTC_WRITE(PV_INTEN, 0);
+
+	platform_set_drvdata(pdev, NULL);
+}
+
+static const struct component_ops vc4_crtc_ops = {
+	.bind   = vc4_crtc_bind,
+	.unbind = vc4_crtc_unbind,
+};
+
+static int vc4_crtc_dev_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &vc4_crtc_ops);
+}
+
+static int vc4_crtc_dev_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &vc4_crtc_ops);
+	return 0;
+}
+
+static const struct of_device_id vc4_crtc_dt_match[] = {
+	{ .compatible = "brcm,vc4-pixelvalve" },
+	{}
+};
+
+static struct platform_driver vc4_crtc_driver = {
+	.probe = vc4_crtc_dev_probe,
+	.remove = vc4_crtc_dev_remove,
+	.driver = {
+		.name = "vc4_crtc",
+		.of_match_table = vc4_crtc_dt_match,
+	},
+};
+
+void __init vc4_crtc_register(void)
+{
+	platform_driver_register(&vc4_crtc_driver);
+}
+
+void __exit vc4_crtc_unregister(void)
+{
+	platform_driver_unregister(&vc4_crtc_driver);
+}
diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c
new file mode 100644
index 0000000..c557039
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_debugfs.c
@@ -0,0 +1,38 @@
+/*
+ *  Copyright ? 2014 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/seq_file.h>
+#include <linux/circ_buf.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <drm/drmP.h>
+
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+
+static const struct drm_info_list vc4_debugfs_list[] = {
+	{"hdmi_regs", vc4_hdmi_debugfs_regs, 0},
+	{"hvs_regs", vc4_hvs_debugfs_regs, 0},
+	{"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0},
+	{"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1},
+	{"crtc2_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)2},
+};
+#define VC4_DEBUGFS_ENTRIES ARRAY_SIZE(vc4_debugfs_list)
+
+int
+vc4_debugfs_init(struct drm_minor *minor)
+{
+	return drm_debugfs_create_files(vc4_debugfs_list, VC4_DEBUGFS_ENTRIES,
+					minor->debugfs_root, minor);
+}
+
+void
+vc4_debugfs_cleanup(struct drm_minor *minor)
+{
+	drm_debugfs_remove_files(vc4_debugfs_list, VC4_DEBUGFS_ENTRIES, minor);
+}
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
new file mode 100644
index 0000000..9ecf4b8b
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2014-2015 Broadcom
+ * Copyright (C) 2013 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+
+#define DRIVER_NAME "vc4"
+#define DRIVER_DESC "Broadcom VC4 graphics"
+#define DRIVER_DATE "20140616"
+#define DRIVER_MAJOR 0
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 0
+
+/* Helper function for mapping the regs on a platform device.
+ *
+ * We assume only one register range per device, so we use index 0.
+ */
+void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index)
+{
+	struct resource *res;
+	void __iomem *map;
+
+	res = platform_get_resource(dev, IORESOURCE_MEM, index);
+	map = devm_ioremap_resource(&dev->dev, res);
+	if (IS_ERR(map)) {
+		DRM_ERROR("Failed to map registers: %ld\n", PTR_ERR(map));
+		return map;
+	}
+
+	return map;
+}
+
+static int vc4_drm_load(struct drm_device *dev, unsigned long flags)
+{
+	struct vc4_dev *vc4;
+	int ret;
+
+	vc4 = devm_kzalloc(dev->dev, sizeof(*vc4), GFP_KERNEL);
+	if (!vc4)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev->dev, dev);
+	vc4->dev = dev;
+	dev->dev_private = vc4;
+
+	drm_mode_config_init(dev);
+
+	ret = component_bind_all(dev->dev, dev);
+	if (ret)
+		return ret;
+
+	vc4_kms_load(dev);
+
+	return 0;
+}
+
+static int vc4_drm_unload(struct drm_device *dev)
+{
+	drm_mode_config_cleanup(dev);
+
+	component_unbind_all(dev->dev, dev);
+
+	return 0;
+}
+
+static void vc4_drm_preclose(struct drm_device *dev, struct drm_file *file)
+{
+	struct drm_crtc *crtc;
+
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+		vc4_cancel_page_flip(crtc, file);
+}
+
+static const struct file_operations vc4_drm_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.release = drm_release,
+	.unlocked_ioctl = drm_ioctl,
+	.mmap = drm_gem_cma_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = drm_compat_ioctl,
+#endif
+	.llseek = noop_llseek,
+};
+
+static const struct drm_ioctl_desc vc4_drm_ioctls[] = {
+};
+
+static struct drm_driver vc4_drm_driver = {
+	.driver_features = (DRIVER_MODESET |
+			    DRIVER_ATOMIC |
+			    DRIVER_GEM |
+			    DRIVER_PRIME),
+	.load = vc4_drm_load,
+	.unload = vc4_drm_unload,
+	.set_busid = drm_platform_set_busid,
+	.preclose = vc4_drm_preclose,
+
+	.enable_vblank = vc4_enable_vblank,
+	.disable_vblank = vc4_disable_vblank,
+	.get_vblank_counter = drm_vblank_count,
+
+#if defined(CONFIG_DEBUG_FS)
+	.debugfs_init = vc4_debugfs_init,
+	.debugfs_cleanup = vc4_debugfs_cleanup,
+#endif
+
+	.gem_free_object = drm_gem_cma_free_object,
+	.gem_vm_ops = &drm_gem_cma_vm_ops,
+
+	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.gem_prime_import = drm_gem_prime_import,
+	.gem_prime_export = drm_gem_prime_export,
+	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
+	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+	.gem_prime_vmap		= drm_gem_cma_prime_vmap,
+	.gem_prime_vunmap	= drm_gem_cma_prime_vunmap,
+	.gem_prime_mmap		= drm_gem_cma_prime_mmap,
+
+	.dumb_create = vc4_dumb_create,
+	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
+	.dumb_destroy = drm_gem_dumb_destroy,
+
+	.ioctls = vc4_drm_ioctls,
+	.num_ioctls = ARRAY_SIZE(vc4_drm_ioctls),
+	.fops = &vc4_drm_fops,
+
+	.name = DRIVER_NAME,
+	.desc = DRIVER_DESC,
+	.date = DRIVER_DATE,
+	.major = DRIVER_MAJOR,
+	.minor = DRIVER_MINOR,
+	.patchlevel = DRIVER_PATCHLEVEL,
+};
+
+static int vc4_drm_bind(struct device *dev)
+{
+	return drm_platform_init(&vc4_drm_driver, to_platform_device(dev));
+}
+
+static void vc4_drm_unbind(struct device *dev)
+{
+	drm_put_dev(platform_get_drvdata(to_platform_device(dev)));
+}
+
+static const struct component_master_ops vc4_drm_ops = {
+	.bind = vc4_drm_bind,
+	.unbind = vc4_drm_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int add_components(struct device *dev, struct component_match **matchptr,
+			  const char *name)
+{
+	struct device_node *np = dev->of_node;
+	unsigned i;
+
+	for (i = 0; ; i++) {
+		struct device_node *node;
+
+		node = of_parse_phandle(np, name, i);
+		if (!node)
+			break;
+
+		component_match_add(dev, matchptr, compare_of, node);
+	}
+
+	return 0;
+}
+
+static int vc4_platform_drm_probe(struct platform_device *pdev)
+{
+	struct component_match *match = NULL;
+
+	add_components(&pdev->dev, &match, "crtcs");
+	add_components(&pdev->dev, &match, "encoders");
+	add_components(&pdev->dev, &match, "hvss");
+
+	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+	return component_master_add_with_match(&pdev->dev, &vc4_drm_ops, match);
+}
+
+static int vc4_platform_drm_remove(struct platform_device *pdev)
+{
+	drm_put_dev(platform_get_drvdata(pdev));
+
+	return 0;
+}
+
+static const struct of_device_id vc4_of_match[] = {
+	{ .compatible = "brcm,vc4", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, vc4_of_match);
+
+static struct platform_driver vc4_platform_driver = {
+	.probe		= vc4_platform_drm_probe,
+	.remove		= vc4_platform_drm_remove,
+	.driver		= {
+		.name	= "vc4-drm",
+		.owner	= THIS_MODULE,
+		.of_match_table = vc4_of_match,
+	},
+};
+
+static int __init vc4_drm_register(void)
+{
+	vc4_hdmi_register();
+	vc4_crtc_register();
+	vc4_hvs_register();
+	return platform_driver_register(&vc4_platform_driver);
+}
+
+static void __exit vc4_drm_unregister(void)
+{
+	platform_driver_unregister(&vc4_platform_driver);
+	vc4_hvs_unregister();
+	vc4_crtc_unregister();
+	vc4_hdmi_unregister();
+}
+
+module_init(vc4_drm_register);
+module_exit(vc4_drm_unregister);
+
+MODULE_ALIAS("platform:vc4-drm");
+MODULE_DESCRIPTION("Broadcom VC4 DRM Driver");
+MODULE_AUTHOR("Eric Anholt <eric at anholt.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
new file mode 100644
index 0000000..1b0a0b0
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "drmP.h"
+#include "drm_gem_cma_helper.h"
+
+struct vc4_dev {
+	struct drm_device *dev;
+
+	struct vc4_hdmi *hdmi;
+	struct vc4_hvs *hvs;
+	struct vc4_crtc *crtc[3];
+};
+
+static inline struct vc4_dev *
+to_vc4_dev(struct drm_device *dev)
+{
+	return (struct vc4_dev *)dev->dev_private;
+}
+
+struct vc4_bo {
+	struct drm_gem_cma_object base;
+};
+
+static inline struct vc4_bo *
+to_vc4_bo(struct drm_gem_object *bo)
+{
+	return (struct vc4_bo *)bo;
+}
+
+struct vc4_hvs {
+	struct platform_device *pdev;
+	void __iomem *regs;
+	void __iomem *dlist;
+};
+
+struct vc4_crtc {
+	struct drm_crtc base;
+	void __iomem *regs;
+
+	/* Which HVS channel we're using for our CRTC. */
+	int channel;
+
+	/* Pointer to the actual hardware display list memory for the
+	 * crtc.
+	 */
+	u32 __iomem *dlist;
+
+	u32 dlist_size; /* in dwords */
+
+	struct drm_pending_vblank_event *event;
+};
+
+static inline struct vc4_crtc *
+to_vc4_crtc(struct drm_crtc *crtc)
+{
+	return (struct vc4_crtc *)crtc;
+}
+
+struct vc4_plane {
+	struct drm_plane base;
+};
+
+static inline struct vc4_plane *
+to_vc4_plane(struct drm_plane *plane)
+{
+	return (struct vc4_plane *)plane;
+}
+
+#define HVS_READ(offset) readl(vc4->hvs->regs + offset)
+#define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset)
+
+/* vc4_bo.c */
+void vc4_free_object(struct drm_gem_object *gem_obj);
+struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size);
+int vc4_dumb_create(struct drm_file *file_priv,
+		    struct drm_device *dev,
+		    struct drm_mode_create_dumb *args);
+struct dma_buf *vc4_prime_export(struct drm_device *dev,
+				 struct drm_gem_object *obj, int flags);
+
+/* vc4_crtc.c */
+void vc4_crtc_register(void);
+void vc4_crtc_unregister(void);
+int vc4_enable_vblank(struct drm_device *dev, int crtc_id);
+void vc4_disable_vblank(struct drm_device *dev, int crtc_id);
+void vc4_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
+int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg);
+
+/* vc4_debugfs.c */
+int vc4_debugfs_init(struct drm_minor *minor);
+void vc4_debugfs_cleanup(struct drm_minor *minor);
+
+/* vc4_drv.c */
+void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index);
+
+/* vc4_hdmi.c */
+void vc4_hdmi_register(void);
+void vc4_hdmi_unregister(void);
+struct drm_encoder *vc4_hdmi_encoder_init(struct drm_device *dev);
+struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
+					      struct drm_encoder *encoder);
+int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused);
+
+/* vc4_hvs.c */
+void vc4_hvs_register(void);
+void vc4_hvs_unregister(void);
+void vc4_hvs_dump_state(struct drm_device *dev);
+int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused);
+
+/* vc4_kms.c */
+int vc4_kms_load(struct drm_device *dev);
+
+/* vc4_plane.c */
+struct drm_plane *vc4_plane_init(struct drm_device *dev,
+				 enum drm_plane_type type);
+u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist);
+u32 vc4_plane_dlist_size(struct drm_plane_state *state);
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
new file mode 100644
index 0000000..c1ac96e
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "drm_atomic_helper.h"
+#include "drm_crtc_helper.h"
+#include "drm_edid.h"
+#include "linux/clk.h"
+#include "linux/component.h"
+#include "linux/of_gpio.h"
+#include "linux/of_platform.h"
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+
+/* General HDMI hardware state. */
+struct vc4_hdmi {
+	struct platform_device *pdev;
+	struct i2c_adapter *ddc;
+	void __iomem *hdmicore_regs;
+	void __iomem *hd_regs;
+	int hpd_gpio;
+
+	/* Probed video mode at boot time, used to filter display
+	 * modes to only allow equivalent ones (since we can't set the
+	 * pixel clock yet).
+	 */
+	struct drm_display_mode boot_mode;
+};
+#define HDMI_READ(offset) readl(vc4->hdmi->hdmicore_regs + offset)
+#define HDMI_WRITE(offset, val) writel(val, vc4->hdmi->hdmicore_regs + offset)
+#define HD_READ(offset) readl(vc4->hdmi->hd_regs + offset)
+#define HD_WRITE(offset, val) writel(val, vc4->hdmi->hd_regs + offset)
+
+/* VC4 HDMI encoder KMS struct */
+struct vc4_hdmi_encoder {
+	struct drm_encoder base;
+	bool hdmi_monitor;
+};
+static inline struct vc4_hdmi_encoder *
+to_vc4_hdmi_encoder(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct vc4_hdmi_encoder, base);
+}
+
+/* VC4 HDMI connector KMS struct */
+struct vc4_hdmi_connector {
+	struct drm_connector base;
+
+	/* Since the connector is attached to just the one encoder,
+	 * this is the reference to it so we can do the best_encoder()
+	 * hook.
+	 */
+	struct drm_encoder *encoder;
+};
+static inline struct vc4_hdmi_connector *
+to_vc4_hdmi_connector(struct drm_connector *connector)
+{
+	return container_of(connector, struct vc4_hdmi_connector, base);
+}
+
+#define HDMI_REG(reg) { reg, #reg }
+static const struct {
+	u32 reg;
+	const char *name;
+} hdmi_regs[] = {
+	HDMI_REG(VC4_HDMI_CORE_REV),
+	HDMI_REG(VC4_HDMI_SW_RESET_CONTROL),
+	HDMI_REG(VC4_HDMI_HOTPLUG_INT),
+	HDMI_REG(VC4_HDMI_HOTPLUG),
+	HDMI_REG(VC4_HDMI_HORZA),
+	HDMI_REG(VC4_HDMI_HORZB),
+	HDMI_REG(VC4_HDMI_FIFO_CTL),
+	HDMI_REG(VC4_HDMI_SCHEDULER_CONTROL),
+	HDMI_REG(VC4_HDMI_VERTA0),
+	HDMI_REG(VC4_HDMI_VERTA1),
+	HDMI_REG(VC4_HDMI_VERTB0),
+	HDMI_REG(VC4_HDMI_VERTB1),
+	HDMI_REG(VC4_HDMI_TX_PHY_RESET_CTL),
+};
+static const struct {
+	u32 reg;
+	const char *name;
+} hd_regs[] = {
+	HDMI_REG(VC4_HD_M_CTL),
+	HDMI_REG(VC4_HD_MAI_CTL),
+	HDMI_REG(VC4_HD_VID_CTL),
+	HDMI_REG(VC4_HD_CSC_CTL),
+	HDMI_REG(VC4_HD_FRAME_COUNT),
+};
+
+#ifdef CONFIG_DEBUG_FS
+int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_device *dev = node->minor->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hdmi_regs); i++) {
+		seq_printf(m, "%s (0x%04x): 0x%08x\n",
+			   hdmi_regs[i].name, hdmi_regs[i].reg,
+			   HDMI_READ(hdmi_regs[i].reg));
+	}
+
+	for (i = 0; i < ARRAY_SIZE(hd_regs); i++) {
+		seq_printf(m, "%s (0x%04x): 0x%08x\n",
+			   hd_regs[i].name, hd_regs[i].reg,
+			   HD_READ(hd_regs[i].reg));
+	}
+
+	return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static void vc4_hdmi_dump_regs(struct drm_device *dev)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hdmi_regs); i++) {
+		DRM_INFO("0x%04x (%s): 0x%08x\n",
+			 hdmi_regs[i].reg, hdmi_regs[i].name,
+			 HDMI_READ(hdmi_regs[i].reg));
+	}
+	for (i = 0; i < ARRAY_SIZE(hd_regs); i++) {
+		DRM_INFO("0x%04x (%s): 0x%08x\n",
+			 hd_regs[i].reg, hd_regs[i].name,
+			 HD_READ(hd_regs[i].reg));
+	}
+}
+
+static enum drm_connector_status
+vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct drm_device *dev = connector->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+	if (vc4->hdmi->hpd_gpio) {
+		if (gpio_get_value(vc4->hdmi->hpd_gpio))
+			return connector_status_connected;
+		else
+			return connector_status_disconnected;
+	}
+
+	if (HDMI_READ(VC4_HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED)
+		return connector_status_connected;
+	else
+		return connector_status_disconnected;
+}
+
+static void vc4_hdmi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct vc4_hdmi_connector *vc4_connector =
+		to_vc4_hdmi_connector(connector);
+	struct drm_encoder *encoder = vc4_connector->encoder;
+	struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+	struct drm_device *dev = connector->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	int ret = 0;
+	struct edid *edid;
+
+	edid = drm_get_edid(connector, vc4->hdmi->ddc);
+	if (!edid)
+		return -ENODEV;
+
+	vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
+	drm_mode_connector_update_edid_property(connector, edid);
+	ret = drm_add_edid_modes(connector, edid);
+
+	return ret;
+}
+
+/* Since we can't set the pixel clock yet, filter out all the EDID
+ * modes that don't match what was set up by the firmware.
+ */
+int vc4_hdmi_mode_valid(struct drm_connector *connector,
+			struct drm_display_mode *mode)
+{
+	struct drm_device *dev = connector->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct drm_display_mode *boot_mode = &vc4->hdmi->boot_mode;
+
+	if (mode->hdisplay != boot_mode->hdisplay ||
+	    mode->hsync_start != boot_mode->hsync_start ||
+	    mode->hsync_end != boot_mode->hsync_end ||
+	    mode->htotal != boot_mode->htotal) {
+		return MODE_ERROR;
+	}
+
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+vc4_hdmi_connector_best_encoder(struct drm_connector *connector)
+{
+	struct vc4_hdmi_connector *hdmi_connector =
+		to_vc4_hdmi_connector(connector);
+	return hdmi_connector->encoder;
+}
+
+static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.detect = vc4_hdmi_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = vc4_hdmi_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = {
+	.get_modes = vc4_hdmi_connector_get_modes,
+	.mode_valid = vc4_hdmi_mode_valid,
+	.best_encoder = vc4_hdmi_connector_best_encoder,
+};
+
+struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
+					      struct drm_encoder *encoder)
+{
+	struct drm_connector *connector = NULL;
+	struct vc4_hdmi_connector *hdmi_connector;
+	int ret = 0;
+
+	hdmi_connector = devm_kzalloc(dev->dev, sizeof(*hdmi_connector),
+				      GFP_KERNEL);
+	if (!hdmi_connector) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	connector = &hdmi_connector->base;
+
+	hdmi_connector->encoder = encoder;
+
+	drm_connector_init(dev, connector, &vc4_hdmi_connector_funcs,
+			   DRM_MODE_CONNECTOR_HDMIA);
+	drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs);
+
+	connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
+			     DRM_CONNECTOR_POLL_DISCONNECT);
+
+	connector->interlace_allowed = 0;
+	connector->doublescan_allowed = 0;
+
+	drm_connector_register(connector);
+
+	drm_mode_connector_attach_encoder(connector, encoder);
+
+	return connector;
+
+ fail:
+	if (connector)
+		vc4_hdmi_connector_destroy(connector);
+
+	return ERR_PTR(ret);
+}
+
+static void vc4_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs vc4_hdmi_encoder_funcs = {
+	.destroy = vc4_encoder_destroy,
+};
+
+static bool vc4_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
+					const struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+				      struct drm_display_mode *unadjusted_mode,
+				      struct drm_display_mode *mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	bool debug_dump_regs = false;
+	bool hsync_pos = !(mode->flags & DRM_MODE_FLAG_NHSYNC);
+	bool vsync_pos = !(mode->flags & DRM_MODE_FLAG_NVSYNC);
+	u32 vactive = (mode->vdisplay >>
+		       ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0));
+	u32 verta = (VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
+				   VC4_HDMI_VERTA_VSP) |
+		     VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
+				   VC4_HDMI_VERTA_VFP) |
+		     VC4_SET_FIELD(vactive, VC4_HDMI_VERTA_VAL));
+	u32 vertb = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) |
+		     VC4_SET_FIELD(mode->vtotal - mode->vsync_end,
+				   VC4_HDMI_VERTB_VBP));
+
+	if (debug_dump_regs) {
+		DRM_INFO("HDMI regs before:\n");
+		vc4_hdmi_dump_regs(dev);
+	}
+
+	HD_WRITE(VC4_HD_VID_CTL, 0);
+
+	/* XXX: This is where we would set the HDMI state machine
+	 * clock, if we had an interface for it.
+	 */
+
+	HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
+		   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
+		   VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
+		   VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS);
+
+	HDMI_WRITE(VC4_HDMI_HORZA,
+		   (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) |
+		   (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) |
+		   VC4_SET_FIELD(mode->hdisplay, VC4_HDMI_HORZA_HAP));
+
+	HDMI_WRITE(VC4_HDMI_HORZB,
+		   VC4_SET_FIELD(mode->htotal - mode->hsync_end,
+				 VC4_HDMI_HORZB_HBP) |
+		   VC4_SET_FIELD(mode->hsync_end - mode->hsync_start,
+				 VC4_HDMI_HORZB_HSP) |
+		   VC4_SET_FIELD(mode->hsync_start - mode->hdisplay,
+				 VC4_HDMI_HORZB_HFP));
+
+	HDMI_WRITE(VC4_HDMI_VERTA0, verta);
+	HDMI_WRITE(VC4_HDMI_VERTA1, verta);
+
+	HDMI_WRITE(VC4_HDMI_VERTB0, vertb);
+	HDMI_WRITE(VC4_HDMI_VERTB1, vertb);
+
+	HD_WRITE(VC4_HD_VID_CTL,
+		 (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
+		 (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
+
+	/* The RGB order applies even when CSC is disabled. */
+	HD_WRITE(VC4_HD_CSC_CTL, VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR,
+					       VC4_HD_CSC_CTL_ORDER));
+
+	HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
+
+	if (debug_dump_regs) {
+		DRM_INFO("HDMI regs after:\n");
+		vc4_hdmi_dump_regs(dev);
+	}
+}
+
+static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+	HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+	HD_WRITE(VC4_HD_VID_CTL,
+		 HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
+}
+
+static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+	HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
+
+	HD_WRITE(VC4_HD_VID_CTL,
+		 HD_READ(VC4_HD_VID_CTL) |
+		 VC4_HD_VID_CTL_ENABLE |
+		 VC4_HD_VID_CTL_UNDERFLOW_ENABLE |
+		 VC4_HD_VID_CTL_FRAME_COUNTER_RESET);
+
+	if (vc4_encoder->hdmi_monitor) {
+		HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
+			   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
+			   VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
+
+		while (!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
+			 VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE)) {
+			cpu_relax();
+		}
+	} else {
+		HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
+			   HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) &
+			   ~(VC4_HDMI_RAM_PACKET_ENABLE));
+		HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
+			   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
+			   ~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
+
+		while (HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
+		       VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE) {
+			cpu_relax();
+		}
+	}
+
+	if (vc4_encoder->hdmi_monitor) {
+		u32 drift;
+
+		WARN_ON(!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
+			  VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE));
+		HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
+			   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
+			   VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT);
+
+		/* XXX: Set HDMI_RAM_PACKET_CONFIG (1 << 16) and set
+		 * up the infoframe.
+		 */
+
+		drift = HDMI_READ(VC4_HDMI_FIFO_CTL);
+		drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK;
+
+		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
+			   drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
+		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
+			   drift | VC4_HDMI_FIFO_CTL_RECENTER);
+		udelay(1000);
+		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
+			   drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
+		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
+			   drift | VC4_HDMI_FIFO_CTL_RECENTER);
+
+		while ((HDMI_READ(VC4_HDMI_FIFO_CTL) &
+			VC4_HDMI_FIFO_CTL_RECENTER_DONE) == 0) {
+			cpu_relax();
+		}
+	}
+}
+
+static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
+	.mode_fixup = vc4_hdmi_encoder_mode_fixup,
+	.mode_set = vc4_hdmi_encoder_mode_set,
+	.disable = vc4_hdmi_encoder_disable,
+	.enable = vc4_hdmi_encoder_enable,
+};
+
+static struct drm_crtc *
+vc4_get_crtc_node(struct platform_device *pdev)
+{
+	struct device_node *crtc_node;
+	struct platform_device *crtc_pdev;
+
+	crtc_node = of_parse_phandle(pdev->dev.of_node, "crtc", 0);
+	if (!crtc_node) {
+		DRM_ERROR("No CRTC for hdmi in DT\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	crtc_pdev = of_find_device_by_node(crtc_node);
+	if (!crtc_pdev) {
+		DRM_ERROR("No CRTC device attached to OF node\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	return platform_get_drvdata(crtc_pdev);
+}
+
+struct drm_encoder *vc4_hdmi_encoder_init(struct drm_device *dev)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct drm_encoder *encoder = NULL;
+	struct vc4_hdmi_encoder *vc4_hdmi_encoder;
+	struct drm_crtc *crtc;
+	int ret;
+
+	vc4_hdmi_encoder = devm_kzalloc(dev->dev, sizeof(*vc4_hdmi_encoder),
+					GFP_KERNEL);
+	if (!vc4_hdmi_encoder) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	encoder = &vc4_hdmi_encoder->base;
+
+	crtc = vc4_get_crtc_node(vc4->hdmi->pdev);
+	if (IS_ERR(crtc)) {
+		ret = PTR_ERR(crtc);
+		goto fail;
+	}
+
+	drm_encoder_init(dev, encoder, &vc4_hdmi_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS);
+	drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs);
+
+	encoder->possible_crtcs = drm_crtc_mask(crtc);
+
+	return encoder;
+
+fail:
+	if (encoder)
+		vc4_encoder_destroy(encoder);
+
+	return ERR_PTR(ret);
+}
+
+/* Reads out the current HDMI mode progreamming at driver load time.
+ *
+ * This is currently used for later filtering out of the DDC-probed
+ * video modes, since we can't actually change modes due to not having
+ * control of the necessary clocks.  Later on, we may end up reusing
+ * this for skipping modesets at boot time.
+ */
+static void
+vc4_hdmi_get_boot_display_mode(struct vc4_dev *vc4)
+{
+	struct drm_display_mode *mode = &vc4->hdmi->boot_mode;
+	u32 horza = HDMI_READ(VC4_HDMI_HORZA);
+	u32 horzb = HDMI_READ(VC4_HDMI_HORZB);
+	u32 verta = HDMI_READ(VC4_HDMI_VERTA0);
+	u32 vertb = HDMI_READ(VC4_HDMI_VERTB0);
+
+	memset(mode, 0, sizeof(*mode));
+
+	if (!(horza & VC4_HDMI_HORZA_VPOS))
+		mode->flags |= DRM_MODE_FLAG_NVSYNC;
+	if (!(horza & VC4_HDMI_HORZA_HPOS))
+		mode->flags |= DRM_MODE_FLAG_NHSYNC;
+	mode->hdisplay = VC4_GET_FIELD(horza, VC4_HDMI_HORZA_HAP);
+	mode->hsync_start = (mode->hdisplay +
+			     VC4_GET_FIELD(horzb, VC4_HDMI_HORZB_HFP));
+	mode->hsync_end = (mode->hsync_start +
+			   VC4_GET_FIELD(horzb, VC4_HDMI_HORZB_HSP));
+	mode->htotal = (mode->hsync_end +
+			VC4_GET_FIELD(horzb, VC4_HDMI_HORZB_HBP));
+
+	mode->vdisplay = VC4_GET_FIELD(verta, VC4_HDMI_VERTA_VAL);
+	mode->vsync_start = (mode->vdisplay +
+			     VC4_GET_FIELD(verta, VC4_HDMI_VERTA_VFP));
+	mode->vsync_end = (mode->vsync_start +
+			   VC4_GET_FIELD(verta, VC4_HDMI_VERTA_VSP));
+	mode->vtotal = (mode->vsync_end +
+			VC4_GET_FIELD(vertb, VC4_HDMI_VERTB_VBP));
+}
+
+static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct vc4_dev *vc4 = drm->dev_private;
+	struct vc4_hdmi *hdmi;
+	struct device_node *ddc_node;
+	u32 value;
+
+	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi->pdev = pdev;
+	hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0);
+	if (IS_ERR(hdmi->hdmicore_regs))
+		return PTR_ERR(hdmi->hdmicore_regs);
+
+	hdmi->hd_regs = vc4_ioremap_regs(pdev, 1);
+	if (IS_ERR(hdmi->hd_regs))
+		return PTR_ERR(hdmi->hd_regs);
+
+	/* DDC i2c driver */
+	ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
+	if (!ddc_node) {
+		DRM_ERROR("Failed to find ddc node in device tree\n");
+		return -ENODEV;
+	}
+
+	hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
+	if (!hdmi->ddc) {
+		DRM_ERROR("Failed to get ddc i2c adapter by node\n");
+		return -EPROBE_DEFER;
+	}
+
+	/* Only use the GPIO HPD pin if present in the DT, otherwise
+	 * we'll use the HDMI core's register.
+	 */
+	if (of_find_property(dev->of_node, "hpd-gpio", &value)) {
+		hdmi->hpd_gpio = of_get_named_gpio(dev->of_node, "hpd-gpio", 0);
+		if (hdmi->hpd_gpio < 0)
+			return hdmi->hpd_gpio;
+	}
+
+	vc4->hdmi = hdmi;
+
+	/* HDMI core must be enabled. */
+	WARN_ON_ONCE((HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE) == 0);
+
+	vc4_hdmi_get_boot_display_mode(vc4);
+
+	return 0;
+}
+
+static void vc4_hdmi_unbind(struct device *dev, struct device *master,
+			    void *data)
+{
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct vc4_dev *vc4 = drm->dev_private;
+
+	put_device(&vc4->hdmi->ddc->dev);
+
+	vc4->hdmi = NULL;
+}
+
+static const struct component_ops vc4_hdmi_ops = {
+	.bind   = vc4_hdmi_bind,
+	.unbind = vc4_hdmi_unbind,
+};
+
+static int vc4_hdmi_dev_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &vc4_hdmi_ops);
+}
+
+static int vc4_hdmi_dev_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &vc4_hdmi_ops);
+	return 0;
+}
+
+static const struct of_device_id vc4_hdmi_dt_match[] = {
+	{ .compatible = "brcm,vc4-hdmi" },
+	{}
+};
+
+static struct platform_driver vc4_hdmi_driver = {
+	.probe = vc4_hdmi_dev_probe,
+	.remove = vc4_hdmi_dev_remove,
+	.driver = {
+		.name = "vc4_hdmi",
+		.of_match_table = vc4_hdmi_dt_match,
+	},
+};
+
+void __init vc4_hdmi_register(void)
+{
+	platform_driver_register(&vc4_hdmi_driver);
+}
+
+void __exit vc4_hdmi_unregister(void)
+{
+	platform_driver_unregister(&vc4_hdmi_driver);
+}
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
new file mode 100644
index 0000000..dcf4f78
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/**
+ * DOC: VC4 HVS module.
+ *
+ * The HVS is the piece of hardware that does translation, scaling,
+ * colorspace conversion, and compositing of pixels stored in
+ * framebuffers into a FIFO of pixels going out to the Pixel Valve
+ * (CRTC).
+ *
+ * There is a single global HVS, with multiple output FIFOs that can
+ * be consumed by the PVs.  This file just manages the resources for
+ * the HVS, while the vc4_crtc.c code actually drives HVS setup for
+ * each CRTC.
+ */
+
+#include "linux/component.h"
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+
+#define HVS_REG(reg) { reg, #reg }
+static const struct {
+	u32 reg;
+	const char *name;
+} hvs_regs[] = {
+	HVS_REG(SCALER_DISPCTRL),
+	HVS_REG(SCALER_DISPSTAT),
+	HVS_REG(SCALER_DISPID),
+	HVS_REG(SCALER_DISPECTRL),
+	HVS_REG(SCALER_DISPPROF),
+	HVS_REG(SCALER_DISPDITHER),
+	HVS_REG(SCALER_DISPEOLN),
+	HVS_REG(SCALER_DISPLIST0),
+	HVS_REG(SCALER_DISPLIST1),
+	HVS_REG(SCALER_DISPLIST2),
+	HVS_REG(SCALER_DISPLSTAT),
+	HVS_REG(SCALER_DISPLACT0),
+	HVS_REG(SCALER_DISPLACT1),
+	HVS_REG(SCALER_DISPLACT2),
+	HVS_REG(SCALER_DISPCTRL0),
+	HVS_REG(SCALER_DISPBKGND0),
+	HVS_REG(SCALER_DISPSTAT0),
+	HVS_REG(SCALER_DISPBASE0),
+	HVS_REG(SCALER_DISPCTRL1),
+	HVS_REG(SCALER_DISPBKGND1),
+	HVS_REG(SCALER_DISPSTAT1),
+	HVS_REG(SCALER_DISPBASE1),
+	HVS_REG(SCALER_DISPCTRL2),
+	HVS_REG(SCALER_DISPBKGND2),
+	HVS_REG(SCALER_DISPSTAT2),
+	HVS_REG(SCALER_DISPBASE2),
+	HVS_REG(SCALER_DISPALPHA2),
+};
+
+void
+vc4_hvs_dump_state(struct drm_device *dev)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hvs_regs); i++) {
+		DRM_INFO("0x%04x (%s): 0x%08x\n",
+			 hvs_regs[i].reg, hvs_regs[i].name,
+			 HVS_READ(hvs_regs[i].reg));
+	}
+
+	DRM_INFO("HVS ctx:\n");
+	for (i = 0; i < 64; i += 4) {
+		DRM_INFO("0x%08x (%s): 0x%08x 0x%08x 0x%08x 0x%08x\n",
+			 i * 4, i < HVS_BOOTLOADER_DLIST_END ? "B" : "D",
+			 ((uint32_t *)vc4->hvs->dlist)[i + 0],
+			 ((uint32_t *)vc4->hvs->dlist)[i + 1],
+			 ((uint32_t *)vc4->hvs->dlist)[i + 2],
+			 ((uint32_t *)vc4->hvs->dlist)[i + 3]);
+	}
+}
+
+#ifdef CONFIG_DEBUG_FS
+int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_device *dev = node->minor->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hvs_regs); i++) {
+		seq_printf(m, "%s (0x%04x): 0x%08x\n",
+			   hvs_regs[i].name, hvs_regs[i].reg,
+			   HVS_READ(hvs_regs[i].reg));
+	}
+
+	return 0;
+}
+#endif
+
+static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct vc4_dev *vc4 = drm->dev_private;
+	struct vc4_hvs *hvs = NULL;
+
+	hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL);
+	if (!hvs)
+		return -ENOMEM;
+
+	hvs->pdev = pdev;
+
+	hvs->regs = vc4_ioremap_regs(pdev, 0);
+	if (IS_ERR(hvs->regs))
+		return PTR_ERR(hvs->regs);
+
+	hvs->dlist = hvs->regs + SCALER_DLIST_START;
+
+	vc4->hvs = hvs;
+	return 0;
+}
+
+static void vc4_hvs_unbind(struct device *dev, struct device *master,
+			   void *data)
+{
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct vc4_dev *vc4 = drm->dev_private;
+
+	vc4->hvs = NULL;
+}
+
+static const struct component_ops vc4_hvs_ops = {
+	.bind   = vc4_hvs_bind,
+	.unbind = vc4_hvs_unbind,
+};
+
+static int vc4_hvs_dev_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &vc4_hvs_ops);
+}
+
+static int vc4_hvs_dev_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &vc4_hvs_ops);
+	return 0;
+}
+
+static const struct of_device_id vc4_hvs_dt_match[] = {
+	{ .compatible = "brcm,vc4-hvs" },
+	{}
+};
+
+static struct platform_driver vc4_hvs_driver = {
+	.probe = vc4_hvs_dev_probe,
+	.remove = vc4_hvs_dev_remove,
+	.driver = {
+		.name = "vc4_hvs",
+		.of_match_table = vc4_hvs_dt_match,
+	},
+};
+
+void __init vc4_hvs_register(void)
+{
+	platform_driver_register(&vc4_hvs_driver);
+}
+
+void __exit vc4_hvs_unregister(void)
+{
+	platform_driver_unregister(&vc4_hvs_driver);
+}
diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
new file mode 100644
index 0000000..e5e96bc
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_kms.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/**
+ * DOC: VC4 KMS
+ *
+ * This is the general code for implementing KMS mode setting that
+ * doesn't clearly associate with any of the other objects (plane,
+ * crtc, HDMI encoder).
+ */
+
+#include "drm_crtc.h"
+#include "drm_atomic_helper.h"
+#include "drm_crtc_helper.h"
+#include "drm_plane_helper.h"
+#include "drm_fb_cma_helper.h"
+#include "vc4_drv.h"
+
+static const struct drm_mode_config_funcs vc4_mode_funcs = {
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+	.fb_create = drm_fb_cma_create,
+};
+
+/* Calls out to initialize all of the VC4 KMS objects. */
+static int vc4_init_modeset_objects(struct drm_device *dev)
+{
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	int ret = 0;
+
+	encoder = vc4_hdmi_encoder_init(dev);
+	if (IS_ERR(encoder)) {
+		dev_err(dev->dev, "failed to construct HDMI encoder\n");
+		ret = PTR_ERR(encoder);
+		goto fail;
+	}
+
+	connector = vc4_hdmi_connector_init(dev, encoder);
+	if (IS_ERR(connector)) {
+		ret = PTR_ERR(connector);
+		dev_err(dev->dev, "failed to initialize HDMI connector\n");
+		goto fail;
+	}
+
+fail:
+	return ret;
+}
+
+int vc4_kms_load(struct drm_device *dev)
+{
+	int ret;
+
+	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
+	if (ret < 0) {
+		dev_err(dev->dev, "failed to initialize vblank\n");
+		return ret;
+	}
+
+	dev->mode_config.max_width = 2048;
+	dev->mode_config.max_height = 2048;
+	dev->mode_config.funcs = &vc4_mode_funcs;
+	dev->mode_config.preferred_depth = 24;
+
+	ret = vc4_init_modeset_objects(dev);
+	if (ret)
+		goto fail;
+
+	drm_mode_config_reset(dev);
+
+	drm_fbdev_cma_init(dev, 32,
+			   dev->mode_config.num_crtc,
+			   dev->mode_config.num_connector);
+
+	drm_kms_helper_poll_init(dev);
+
+fail:
+	return ret;
+}
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
new file mode 100644
index 0000000..cdd8b10
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/**
+ * DOC: VC4 plane module
+ *
+ * Each DRM plane is a layer of pixels being scanned out by the HVS.
+ *
+ * At atomic modeset check time, we compute the HVS display element
+ * state that would be necessary for displaying the plane (giving us a
+ * chance to figure out if a plane configuration is invalid), then at
+ * atomic flush time the CRTC will ask us to write our element state
+ * into the region of the HVS that it has allocated for us.
+ */
+
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+#include "drm_atomic_helper.h"
+#include "drm_fb_cma_helper.h"
+#include "drm_plane_helper.h"
+
+struct vc4_plane_state {
+	struct drm_plane_state base;
+	u32 *dlist;
+	u32 dlist_size; /* Number of dwords in allocated for the display list */
+	u32 dlist_count; /* Number of used dwords in the display list. */
+};
+
+static inline struct vc4_plane_state *
+to_vc4_plane_state(struct drm_plane_state *state)
+{
+	return (struct vc4_plane_state *)state;
+}
+
+static const struct hvs_format {
+	u32 drm; /* DRM_FORMAT_* */
+	u32 hvs; /* HVS_FORMAT_* */
+	u32 pixel_order;
+	bool has_alpha;
+} hvs_formats[] = {
+	{
+		.drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
+		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
+	},
+	{
+		.drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
+		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
+	},
+};
+
+static const struct hvs_format *vc4_get_hvs_format(u32 drm_format)
+{
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
+		if (hvs_formats[i].drm == drm_format)
+			return &hvs_formats[i];
+	}
+
+	return NULL;
+}
+
+static bool plane_enabled(struct drm_plane_state *state)
+{
+	return state->fb && state->crtc;
+}
+
+struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
+{
+	struct vc4_plane_state *vc4_state;
+
+	if (WARN_ON(!plane->state))
+		return NULL;
+
+	vc4_state = kmemdup(plane->state, sizeof(*vc4_state), GFP_KERNEL);
+	if (!vc4_state)
+		return NULL;
+
+	__drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base);
+
+	if (vc4_state->dlist) {
+		vc4_state->dlist = kmemdup(vc4_state->dlist,
+					   vc4_state->dlist_count * 4,
+					   GFP_KERNEL);
+		if (!vc4_state->dlist) {
+			kfree(vc4_state);
+			return NULL;
+		}
+		vc4_state->dlist_size = vc4_state->dlist_count;
+	}
+
+	return &vc4_state->base;
+}
+
+void vc4_plane_destroy_state(struct drm_plane *plane,
+			     struct drm_plane_state *state)
+{
+	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+
+	kfree(vc4_state->dlist);
+	__drm_atomic_helper_plane_destroy_state(plane, &vc4_state->base);
+	kfree(state);
+}
+
+/* Called during init to allocate the plane's atomic state. */
+void vc4_plane_reset(struct drm_plane *plane)
+{
+	struct vc4_plane_state *vc4_state;
+
+	WARN_ON(plane->state);
+
+	vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
+	if (!vc4_state)
+		return;
+
+	plane->state = &vc4_state->base;
+	vc4_state->base.plane = plane;
+}
+
+static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val)
+{
+	if (vc4_state->dlist_count == vc4_state->dlist_size) {
+		u32 new_size = max(4u, vc4_state->dlist_count * 2);
+		u32 *new_dlist = kmalloc(new_size * 4, GFP_KERNEL);
+
+		if (!new_dlist)
+			return;
+		memcpy(new_dlist, vc4_state->dlist, vc4_state->dlist_count * 4);
+
+		kfree(vc4_state->dlist);
+		vc4_state->dlist = new_dlist;
+		vc4_state->dlist_size = new_size;
+	}
+
+	vc4_state->dlist[vc4_state->dlist_count++] = val;
+}
+
+/* Writes out a full display list for an active plane to the plane's
+ * private dlist state.
+ */
+static int vc4_plane_mode_set(struct drm_plane *plane,
+			      struct drm_plane_state *state)
+{
+	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
+	u32 ctl0_offset = vc4_state->dlist_count;
+	const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format);
+	uint32_t offset = fb->offsets[0];
+	int crtc_x = state->crtc_x;
+	int crtc_y = state->crtc_y;
+	int crtc_w = state->crtc_w;
+	int crtc_h = state->crtc_h;
+
+	if (crtc_x < 0) {
+		offset += drm_format_plane_cpp(fb->pixel_format, 0) * -crtc_x;
+		crtc_w += crtc_x;
+		crtc_x = 0;
+	}
+
+	if (crtc_y < 0) {
+		offset += fb->pitches[0] * -crtc_y;
+		crtc_h += crtc_y;
+		crtc_y = 0;
+	}
+
+	vc4_dlist_write(vc4_state,
+			SCALER_CTL0_VALID |
+			(format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |
+			(format->hvs << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
+			SCALER_CTL0_UNITY);
+
+	/* Position Word 0: Image Positions and Alpha Value */
+	vc4_dlist_write(vc4_state,
+			VC4_SET_FIELD(0xff, SCALER_POS0_FIXED_ALPHA) |
+			VC4_SET_FIELD(crtc_x, SCALER_POS0_START_X) |
+			VC4_SET_FIELD(crtc_y, SCALER_POS0_START_Y));
+
+	/* Position Word 1: Scaled Image Dimensions.
+	 * Skipped due to SCALER_CTL0_UNITY scaling.
+	 */
+
+	/* Position Word 2: Source Image Size, Alpha Mode */
+	vc4_dlist_write(vc4_state,
+			VC4_SET_FIELD(format->has_alpha ?
+				      SCALER_POS2_ALPHA_MODE_PIPELINE :
+				      SCALER_POS2_ALPHA_MODE_FIXED,
+				      SCALER_POS2_ALPHA_MODE) |
+			VC4_SET_FIELD(crtc_w, SCALER_POS2_WIDTH) |
+			VC4_SET_FIELD(crtc_h, SCALER_POS2_HEIGHT));
+
+	/* Position Word 3: Context.  Written by the HVS. */
+	vc4_dlist_write(vc4_state, 0xc0c0c0c0);
+
+	/* Pointer Word 0: RGB / Y Pointer */
+	vc4_dlist_write(vc4_state, bo->paddr + offset);
+
+	/* Pointer Context Word 0: Written by the HVS */
+	vc4_dlist_write(vc4_state, 0xc0c0c0c0);
+
+	/* Pitch word 0: Pointer 0 Pitch */
+	vc4_dlist_write(vc4_state,
+			VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH));
+
+	vc4_state->dlist[ctl0_offset] |=
+		VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE);
+
+	return 0;
+}
+
+/* If a modeset involves changing the setup of a plane, the atomic
+ * infrastructure will call this to validate a proposed plane setup.
+ * However, if a plane isn't getting updated, this (and the
+ * corresponding vc4_plane_atomic_update) won't get called.  Thus, we
+ * compute the dlist here and have all active plane dlists get updated
+ * in the CRTC's flush.
+ */
+static int vc4_plane_atomic_check(struct drm_plane *plane,
+				  struct drm_plane_state *state)
+{
+	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+
+	vc4_state->dlist_count = 0;
+
+	if (plane_enabled(state))
+		return vc4_plane_mode_set(plane, state);
+	else
+		return 0;
+}
+
+static void vc4_plane_atomic_update(struct drm_plane *plane,
+				    struct drm_plane_state *old_state)
+{
+	/* No contents here.  Since we don't know where in the CRTC's
+	 * dlist we should be stored, our dlist is uploaded to the
+	 * hardware with vc4_plane_write_dlist() at CRTC atomic_flush
+	 * time.
+	 */
+}
+
+u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist)
+{
+	struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
+	int i;
+
+	/* Can't memcpy_toio() because it needs to be 32-bit writes. */
+	for (i = 0; i < vc4_state->dlist_count; i++)
+		writel(vc4_state->dlist[i], &dlist[i]);
+
+	return vc4_state->dlist_count;
+}
+
+u32 vc4_plane_dlist_size(struct drm_plane_state *state)
+{
+	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+
+	return vc4_state->dlist_count;
+}
+
+static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
+	.prepare_fb = NULL,
+	.cleanup_fb = NULL,
+	.atomic_check = vc4_plane_atomic_check,
+	.atomic_update = vc4_plane_atomic_update,
+};
+
+static void vc4_plane_destroy(struct drm_plane *plane)
+{
+	drm_plane_helper_disable(plane);
+	drm_plane_cleanup(plane);
+}
+
+static const struct drm_plane_funcs vc4_plane_funcs = {
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
+	.destroy = vc4_plane_destroy,
+	.set_property = NULL,
+	.reset = vc4_plane_reset,
+	.atomic_duplicate_state = vc4_plane_duplicate_state,
+	.atomic_destroy_state = vc4_plane_destroy_state,
+};
+
+struct drm_plane *vc4_plane_init(struct drm_device *dev,
+				 enum drm_plane_type type)
+{
+	struct drm_plane *plane = NULL;
+	struct vc4_plane *vc4_plane;
+	u32 formats[ARRAY_SIZE(hvs_formats)];
+	int ret = 0;
+	unsigned i;
+
+	vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
+				 GFP_KERNEL);
+	if (!vc4_plane) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++)
+		formats[i] = hvs_formats[i].drm;
+	plane = &vc4_plane->base;
+	ret = drm_universal_plane_init(dev, plane, 0xff,
+				       &vc4_plane_funcs,
+				       formats, ARRAY_SIZE(formats),
+				       type);
+
+	drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
+
+	return plane;
+fail:
+	if (plane)
+		vc4_plane_destroy(plane);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h
new file mode 100644
index 0000000..0eff631
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_regs.h
@@ -0,0 +1,562 @@
+/*
+ *  Copyright ? 2014-2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define VC4_MASK(high, low) (((1 << ((high) - (low) + 1)) - 1) << (low))
+/* Using the GNU statement expression extension */
+#define VC4_SET_FIELD(value, field)					\
+	({								\
+		uint32_t fieldval = (value) << field##_SHIFT;		\
+		WARN_ON((fieldval & ~field##_MASK) != 0);		\
+		fieldval & field##_MASK;				\
+	 })
+
+#define VC4_GET_FIELD(word, field) (((word) & field##_MASK) >>		\
+				    field##_SHIFT)
+
+#define V3D_IDENT0   0x00000
+# define V3D_EXPECTED_IDENT0 \
+	((2 << 24) | \
+	('V' << 0) | \
+	('3' << 8) | \
+	 ('D' << 16))
+
+#define V3D_IDENT1   0x00004
+/* Multiples of 1kb */
+# define V3D_IDENT1_VPM_SIZE_MASK                      VC4_MASK(31, 28)
+# define V3D_IDENT1_VPM_SIZE_SHIFT                     28
+# define V3D_IDENT1_NSEM_MASK                          VC4_MASK(23, 16)
+# define V3D_IDENT1_NSEM_SHIFT                         16
+# define V3D_IDENT1_TUPS_MASK                          VC4_MASK(15, 12)
+# define V3D_IDENT1_TUPS_SHIFT                         12
+# define V3D_IDENT1_QUPS_MASK                          VC4_MASK(11, 8)
+# define V3D_IDENT1_QUPS_SHIFT                         8
+# define V3D_IDENT1_NSLC_MASK                          VC4_MASK(7, 4)
+# define V3D_IDENT1_NSLC_SHIFT                         4
+# define V3D_IDENT1_REV_MASK                           VC4_MASK(3, 0)
+# define V3D_IDENT1_REV_SHIFT                          0
+
+#define V3D_IDENT2   0x00008
+#define V3D_SCRATCH  0x00010
+#define V3D_L2CACTL  0x00020
+# define V3D_L2CACTL_L2CCLR                            (1 << 2)
+# define V3D_L2CACTL_L2CDIS                            (1 << 1)
+# define V3D_L2CACTL_L2CENA                            (1 << 0)
+
+#define V3D_SLCACTL  0x00024
+# define V3D_SLCACTL_T1CC_MASK                         VC4_MASK(27, 24)
+# define V3D_SLCACTL_T1CC_SHIFT                        24
+# define V3D_SLCACTL_T0CC_MASK                         VC4_MASK(19, 16)
+# define V3D_SLCACTL_T0CC_SHIFT                        16
+# define V3D_SLCACTL_UCC_MASK                          VC4_MASK(11, 8)
+# define V3D_SLCACTL_UCC_SHIFT                         8
+# define V3D_SLCACTL_ICC_MASK                          VC4_MASK(3, 0)
+# define V3D_SLCACTL_ICC_SHIFT                         0
+
+#define V3D_INTCTL   0x00030
+#define V3D_INTENA   0x00034
+#define V3D_INTDIS   0x00038
+# define V3D_INT_SPILLUSE                              (1 << 3)
+# define V3D_INT_OUTOMEM                               (1 << 2)
+# define V3D_INT_FLDONE                                (1 << 1)
+# define V3D_INT_FRDONE                                (1 << 0)
+
+#define V3D_CT0CS    0x00100
+#define V3D_CT1CS    0x00104
+#define V3D_CTNCS(n) (V3D_CT0CS + 4 * n)
+# define V3D_CTRSTA      (1 << 15)
+# define V3D_CTSEMA      (1 << 12)
+# define V3D_CTRTSD      (1 << 8)
+# define V3D_CTRUN       (1 << 5)
+# define V3D_CTSUBS      (1 << 4)
+# define V3D_CTERR       (1 << 3)
+# define V3D_CTMODE      (1 << 0)
+
+#define V3D_CT0EA    0x00108
+#define V3D_CT1EA    0x0010c
+#define V3D_CTNEA(n) (V3D_CT0EA + 4 * (n))
+#define V3D_CT0CA    0x00110
+#define V3D_CT1CA    0x00114
+#define V3D_CTNCA(n) (V3D_CT0CA + 4 * (n))
+#define V3D_CT00RA0  0x00118
+#define V3D_CT01RA0  0x0011c
+#define V3D_CTNRA0(n) (V3D_CT00RA0 + 4 * (n))
+#define V3D_CT0LC    0x00120
+#define V3D_CT1LC    0x00124
+#define V3D_CTNLC(n) (V3D_CT0LC + 4 * (n))
+#define V3D_CT0PC    0x00128
+#define V3D_CT1PC    0x0012c
+#define V3D_CTNPC(n) (V3D_CT0PC + 4 * (n))
+
+#define V3D_PCS      0x00130
+# define V3D_BMOOM       (1 << 8)
+# define V3D_RMBUSY      (1 << 3)
+# define V3D_RMACTIVE    (1 << 2)
+# define V3D_BMBUSY      (1 << 1)
+# define V3D_BMACTIVE    (1 << 0)
+
+#define V3D_BFC      0x00134
+#define V3D_RFC      0x00138
+#define V3D_BPCA     0x00300
+#define V3D_BPCS     0x00304
+#define V3D_BPOA     0x00308
+#define V3D_BPOS     0x0030c
+#define V3D_BXCF     0x00310
+#define V3D_SQRSV0   0x00410
+#define V3D_SQRSV1   0x00414
+#define V3D_SQCNTL   0x00418
+#define V3D_SRQPC    0x00430
+#define V3D_SRQUA    0x00434
+#define V3D_SRQUL    0x00438
+#define V3D_SRQCS    0x0043c
+#define V3D_VPACNTL  0x00500
+#define V3D_VPMBASE  0x00504
+#define V3D_PCTRC    0x00670
+#define V3D_PCTRE    0x00674
+#define V3D_PCTR0    0x00680
+#define V3D_PCTRS0   0x00684
+#define V3D_PCTR1    0x00688
+#define V3D_PCTRS1   0x0068c
+#define V3D_PCTR2    0x00690
+#define V3D_PCTRS2   0x00694
+#define V3D_PCTR3    0x00698
+#define V3D_PCTRS3   0x0069c
+#define V3D_PCTR4    0x006a0
+#define V3D_PCTRS4   0x006a4
+#define V3D_PCTR5    0x006a8
+#define V3D_PCTRS5   0x006ac
+#define V3D_PCTR6    0x006b0
+#define V3D_PCTRS6   0x006b4
+#define V3D_PCTR7    0x006b8
+#define V3D_PCTRS7   0x006bc
+#define V3D_PCTR8    0x006c0
+#define V3D_PCTRS8   0x006c4
+#define V3D_PCTR9    0x006c8
+#define V3D_PCTRS9   0x006cc
+#define V3D_PCTR10   0x006d0
+#define V3D_PCTRS10  0x006d4
+#define V3D_PCTR11   0x006d8
+#define V3D_PCTRS11  0x006dc
+#define V3D_PCTR12   0x006e0
+#define V3D_PCTRS12  0x006e4
+#define V3D_PCTR13   0x006e8
+#define V3D_PCTRS13  0x006ec
+#define V3D_PCTR14   0x006f0
+#define V3D_PCTRS14  0x006f4
+#define V3D_PCTR15   0x006f8
+#define V3D_PCTRS15  0x006fc
+#define V3D_BGE      0x00f00
+#define V3D_FDBGO    0x00f04
+#define V3D_FDBGB    0x00f08
+#define V3D_FDBGR    0x00f0c
+#define V3D_FDBGS    0x00f10
+#define V3D_ERRSTAT  0x00f20
+
+#define PV_CONTROL				0x00
+# define PV_CONTROL_FORMAT_MASK			VC4_MASK(23, 21)
+# define PV_CONTROL_FORMAT_SHIFT		21
+# define PV_CONTROL_FORMAT_24			0
+# define PV_CONTROL_FORMAT_DSIV_16		1
+# define PV_CONTROL_FORMAT_DSIC_16		2
+# define PV_CONTROL_FORMAT_DSIV_18		3
+# define PV_CONTROL_FORMAT_DSIV_24		4
+
+# define PV_CONTROL_FIFO_LEVEL_MASK		VC4_MASK(20, 15)
+# define PV_CONTROL_FIFO_LEVEL_SHIFT		15
+# define PV_CONTROL_CLR_AT_START		(1 << 14)
+# define PV_CONTROL_TRIGGER_UNDERFLOW		(1 << 13)
+# define PV_CONTROL_WAIT_HSTART			(1 << 12)
+# define PV_CONTROL_CLK_SELECT_DSI		0
+# define PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI	1
+# define PV_CONTROL_CLK_SELECT_MASK		VC4_MASK(3, 2)
+# define PV_CONTROL_CLK_SELECT_SHIFT		2
+# define PV_CONTROL_FIFO_CLR			(1 << 1)
+# define PV_CONTROL_EN				(1 << 0)
+
+#define PV_V_CONTROL				0x04
+# define PV_VCONTROL_CONTINUOUS			(1 << 1)
+# define PV_VCONTROL_VIDEN			(1 << 0)
+
+#define PV_VSYNCD				0x08
+
+#define PV_HORZA				0x0c
+# define PV_HORZA_HBP_MASK			VC4_MASK(31, 16)
+# define PV_HORZA_HBP_SHIFT			16
+# define PV_HORZA_HSYNC_MASK			VC4_MASK(15, 0)
+# define PV_HORZA_HSYNC_SHIFT			0
+
+#define PV_HORZB				0x10
+# define PV_HORZB_HFP_MASK			VC4_MASK(31, 16)
+# define PV_HORZB_HFP_SHIFT			16
+# define PV_HORZB_HACTIVE_MASK			VC4_MASK(15, 0)
+# define PV_HORZB_HACTIVE_SHIFT			0
+
+#define PV_VERTA				0x14
+# define PV_VERTA_VBP_MASK			VC4_MASK(31, 16)
+# define PV_VERTA_VBP_SHIFT			16
+# define PV_VERTA_VSYNC_MASK			VC4_MASK(15, 0)
+# define PV_VERTA_VSYNC_SHIFT			0
+
+#define PV_VERTB				0x18
+# define PV_VERTB_VFP_MASK			VC4_MASK(31, 16)
+# define PV_VERTB_VFP_SHIFT			16
+# define PV_VERTB_VACTIVE_MASK			VC4_MASK(15, 0)
+# define PV_VERTB_VACTIVE_SHIFT			0
+
+#define PV_VERTA_EVEN				0x1c
+#define PV_VERTB_EVEN				0x20
+
+#define PV_INTEN				0x24
+#define PV_INTSTAT				0x28
+# define PV_INT_VID_IDLE			(1 << 9)
+# define PV_INT_VFP_END				(1 << 8)
+# define PV_INT_VFP_START			(1 << 7)
+# define PV_INT_VACT_START			(1 << 6)
+# define PV_INT_VBP_START			(1 << 5)
+# define PV_INT_VSYNC_START			(1 << 4)
+# define PV_INT_HFP_START			(1 << 3)
+# define PV_INT_HACT_START			(1 << 2)
+# define PV_INT_HBP_START			(1 << 1)
+# define PV_INT_HSYNC_START			(1 << 0)
+
+#define PV_STAT					0x2c
+
+#define PV_HACT_ACT				0x30
+
+#define SCALER_DISPCTRL                         0x00000000
+/* Global register for clock gating the HVS */
+# define SCALER_DISPCTRL_ENABLE			(1 << 31)
+# define SCALER_DISPCTRL_DSP2EISLUR		(1 << 15)
+# define SCALER_DISPCTRL_DSP1EISLUR		(1 << 14)
+/* Enables Display 0 short line and underrun contribution to
+ * SCALER_DISPSTAT_IRQDISP0.  Note that short frame contributions are
+ * always enabled.
+ */
+# define SCALER_DISPCTRL_DSP0EISLUR		(1 << 13)
+# define SCALER_DISPCTRL_DSP2EIEOLN		(1 << 12)
+# define SCALER_DISPCTRL_DSP2EIEOF		(1 << 11)
+# define SCALER_DISPCTRL_DSP1EIEOLN		(1 << 10)
+# define SCALER_DISPCTRL_DSP1EIEOF		(1 << 9)
+/* Enables Display 0 end-of-line-N contribution to
+ * SCALER_DISPSTAT_IRQDISP0
+ */
+# define SCALER_DISPCTRL_DSP0EIEOLN		(1 << 8)
+/* Enables Display 0 EOF contribution to SCALER_DISPSTAT_IRQDISP0 */
+# define SCALER_DISPCTRL_DSP0EIEOF		(1 << 7)
+
+# define SCALER_DISPCTRL_SLVRDEIRQ		(1 << 6)
+# define SCALER_DISPCTRL_SLVWREIRQ		(1 << 5)
+# define SCALER_DISPCTRL_DMAEIRQ		(1 << 4)
+# define SCALER_DISPCTRL_DISP2EIRQ		(1 << 3)
+# define SCALER_DISPCTRL_DISP1EIRQ		(1 << 2)
+/* Enables interrupt generation on the enabled EOF/EOLN/EISLUR
+ * bits and short frames..
+ */
+# define SCALER_DISPCTRL_DISP0EIRQ		(1 << 1)
+/* Enables interrupt generation on scaler profiler interrupt. */
+# define SCALER_DISPCTRL_SCLEIRQ		(1 << 0)
+
+#define SCALER_DISPSTAT                         0x00000004
+# define SCALER_DISPSTAT_COBLOW2		(1 << 29)
+# define SCALER_DISPSTAT_EOLN2			(1 << 28)
+# define SCALER_DISPSTAT_ESFRAME2		(1 << 27)
+# define SCALER_DISPSTAT_ESLINE2		(1 << 26)
+# define SCALER_DISPSTAT_EUFLOW2		(1 << 25)
+# define SCALER_DISPSTAT_EOF2			(1 << 24)
+
+# define SCALER_DISPSTAT_COBLOW1		(1 << 21)
+# define SCALER_DISPSTAT_EOLN1			(1 << 20)
+# define SCALER_DISPSTAT_ESFRAME1		(1 << 19)
+# define SCALER_DISPSTAT_ESLINE1		(1 << 18)
+# define SCALER_DISPSTAT_EUFLOW1		(1 << 17)
+# define SCALER_DISPSTAT_EOF1			(1 << 16)
+
+# define SCALER_DISPSTAT_RESP_MASK		VC4_MASK(15, 14)
+# define SCALER_DISPSTAT_RESP_SHIFT		14
+# define SCALER_DISPSTAT_RESP_OKAY		0
+# define SCALER_DISPSTAT_RESP_EXOKAY		1
+# define SCALER_DISPSTAT_RESP_SLVERR		2
+# define SCALER_DISPSTAT_RESP_DECERR		3
+
+# define SCALER_DISPSTAT_COBLOW0		(1 << 13)
+/* Set when the DISPEOLN line is done compositing. */
+# define SCALER_DISPSTAT_EOLN0			(1 << 12)
+/* Set when VSTART is seen but there are still pixels in the current
+ * output line.
+ */
+# define SCALER_DISPSTAT_ESFRAME0		(1 << 11)
+/* Set when HSTART is seen but there are still pixels in the current
+ * output line.
+ */
+# define SCALER_DISPSTAT_ESLINE0		(1 << 10)
+/* Set when the the downstream tries to read from the display FIFO
+ * while it's empty.
+ */
+# define SCALER_DISPSTAT_EUFLOW0		(1 << 9)
+/* Set when the display mode changes from RUN to EOF */
+# define SCALER_DISPSTAT_EOF0			(1 << 8)
+
+/* Set on AXI invalid DMA ID error. */
+# define SCALER_DISPSTAT_DMA_ERROR		(1 << 7)
+/* Set on AXI slave read decode error */
+# define SCALER_DISPSTAT_IRQSLVRD		(1 << 6)
+/* Set on AXI slave write decode error */
+# define SCALER_DISPSTAT_IRQSLVWR		(1 << 5)
+/* Set when SCALER_DISPSTAT_DMA_ERROR is set, or
+ * SCALER_DISPSTAT_RESP_ERROR is not SCALER_DISPSTAT_RESP_OKAY.
+ */
+# define SCALER_DISPSTAT_IRQDMA			(1 << 4)
+# define SCALER_DISPSTAT_IRQDISP2		(1 << 3)
+# define SCALER_DISPSTAT_IRQDISP1		(1 << 2)
+/* Set when any of the EOF/EOLN/ESFRAME/ESLINE bits are set and their
+ * corresponding interrupt bit is enabled in DISPCTRL.
+ */
+# define SCALER_DISPSTAT_IRQDISP0		(1 << 1)
+/* On read, the profiler interrupt.  On write, clear *all* interrupt bits. */
+# define SCALER_DISPSTAT_IRQSCL			(1 << 0)
+
+#define SCALER_DISPID                           0x00000008
+#define SCALER_DISPECTRL                        0x0000000c
+#define SCALER_DISPPROF                         0x00000010
+#define SCALER_DISPDITHER                       0x00000014
+#define SCALER_DISPEOLN                         0x00000018
+#define SCALER_DISPLIST0                        0x00000020
+#define SCALER_DISPLIST1                        0x00000024
+#define SCALER_DISPLIST2                        0x00000028
+#define SCALER_DISPLSTAT                        0x0000002c
+#define SCALER_DISPLISTX(x)			(SCALER_DISPLIST0 +	\
+						 (x) * (SCALER_DISPLIST1 - \
+							SCALER_DISPLIST0))
+
+#define SCALER_DISPLACT0                        0x00000030
+#define SCALER_DISPLACT1                        0x00000034
+#define SCALER_DISPLACT2                        0x00000038
+#define SCALER_DISPCTRL0                        0x00000040
+# define SCALER_DISPCTRLX_ENABLE		(1 << 31)
+# define SCALER_DISPCTRLX_RESET			(1 << 30)
+# define SCALER_DISPCTRLX_WIDTH_MASK		VC4_MASK(23, 12)
+# define SCALER_DISPCTRLX_WIDTH_SHIFT		12
+# define SCALER_DISPCTRLX_HEIGHT_MASK		VC4_MASK(11, 0)
+# define SCALER_DISPCTRLX_HEIGHT_SHIFT		0
+
+#define SCALER_DISPBKGND0                       0x00000044
+#define SCALER_DISPSTAT0                        0x00000048
+#define SCALER_DISPBASE0                        0x0000004c
+# define SCALER_DISPSTATX_MODE_MASK		VC4_MASK(31, 30)
+# define SCALER_DISPSTATX_MODE_SHIFT		30
+# define SCALER_DISPSTATX_MODE_DISABLED		0
+# define SCALER_DISPSTATX_MODE_INIT		1
+# define SCALER_DISPSTATX_MODE_RUN		2
+# define SCALER_DISPSTATX_MODE_EOF		3
+# define SCALER_DISPSTATX_FULL			(1 << 29)
+# define SCALER_DISPSTATX_EMPTY			(1 << 28)
+#define SCALER_DISPCTRL1                        0x00000050
+#define SCALER_DISPBKGND1                       0x00000054
+#define SCALER_DISPSTAT1                        0x00000058
+#define SCALER_DISPSTATX(x)			(SCALER_DISPSTAT0 +        \
+						 (x) * (SCALER_DISPSTAT1 - \
+							SCALER_DISPSTAT0))
+#define SCALER_DISPBASE1                        0x0000005c
+#define SCALER_DISPCTRL2                        0x00000060
+#define SCALER_DISPCTRLX(x)			(SCALER_DISPCTRL0 +        \
+						 (x) * (SCALER_DISPCTRL1 - \
+							SCALER_DISPCTRL0))
+#define SCALER_DISPBKGND2                       0x00000064
+#define SCALER_DISPSTAT2                        0x00000068
+#define SCALER_DISPBASE2                        0x0000006c
+#define SCALER_DISPALPHA2                       0x00000070
+#define SCALER_GAMADDR                          0x00000078
+#define SCALER_GAMDATA                          0x000000e0
+#define SCALER_DLIST_START                      0x00002000
+#define SCALER_DLIST_SIZE                       0x00004000
+
+#define VC4_HDMI_CORE_REV			0x000
+
+#define VC4_HDMI_SW_RESET_CONTROL		0x004
+# define VC4_HDMI_SW_RESET_FORMAT_DETECT	(1 << 1)
+# define VC4_HDMI_SW_RESET_HDMI			(1 << 0)
+
+#define VC4_HDMI_HOTPLUG_INT			0x008
+
+#define VC4_HDMI_HOTPLUG			0x00c
+# define VC4_HDMI_HOTPLUG_CONNECTED		(1 << 0)
+
+#define VC4_HDMI_RAM_PACKET_CONFIG		0x0a0
+# define VC4_HDMI_RAM_PACKET_ENABLE		(1 << 16)
+
+#define VC4_HDMI_HORZA				0x0c4
+# define VC4_HDMI_HORZA_VPOS			(1 << 14)
+# define VC4_HDMI_HORZA_HPOS			(1 << 13)
+/* Horizontal active pixels (hdisplay). */
+# define VC4_HDMI_HORZA_HAP_MASK		VC4_MASK(12, 0)
+# define VC4_HDMI_HORZA_HAP_SHIFT		0
+
+#define VC4_HDMI_HORZB				0x0c8
+/* Horizontal pack porch (htotal - hsync_end). */
+# define VC4_HDMI_HORZB_HBP_MASK		VC4_MASK(29, 20)
+# define VC4_HDMI_HORZB_HBP_SHIFT		20
+/* Horizontal sync pulse (hsync_end - hsync_start). */
+# define VC4_HDMI_HORZB_HSP_MASK		VC4_MASK(19, 10)
+# define VC4_HDMI_HORZB_HSP_SHIFT		10
+/* Horizontal front porch (hsync_start - hdisplay). */
+# define VC4_HDMI_HORZB_HFP_MASK		VC4_MASK(9, 0)
+# define VC4_HDMI_HORZB_HFP_SHIFT		0
+
+#define VC4_HDMI_FIFO_CTL			0x05c
+# define VC4_HDMI_FIFO_CTL_RECENTER_DONE	(1 << 14)
+# define VC4_HDMI_FIFO_CTL_USE_EMPTY		(1 << 13)
+# define VC4_HDMI_FIFO_CTL_ON_VB		(1 << 7)
+# define VC4_HDMI_FIFO_CTL_RECENTER		(1 << 6)
+# define VC4_HDMI_FIFO_CTL_FIFO_RESET		(1 << 5)
+# define VC4_HDMI_FIFO_CTL_USE_PLL_LOCK		(1 << 4)
+# define VC4_HDMI_FIFO_CTL_INV_CLK_XFR		(1 << 3)
+# define VC4_HDMI_FIFO_CTL_CAPTURE_PTR		(1 << 2)
+# define VC4_HDMI_FIFO_CTL_USE_FULL		(1 << 1)
+# define VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N	(1 << 0)
+# define VC4_HDMI_FIFO_VALID_WRITE_MASK		0xefff
+
+#define VC4_HDMI_SCHEDULER_CONTROL		0x0c0
+# define VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT (1 << 15)
+# define VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS (1 << 5)
+# define VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT	(1 << 3)
+# define VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE	(1 << 1)
+# define VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI	(1 << 0)
+
+#define VC4_HDMI_VERTA0				0x0cc
+#define VC4_HDMI_VERTA1				0x0d4
+/* Vertical sync pulse (vsync_end - vsync_start). */
+# define VC4_HDMI_VERTA_VSP_MASK		VC4_MASK(24, 20)
+# define VC4_HDMI_VERTA_VSP_SHIFT		20
+/* Vertical front porch (vsync_start - vdisplay). */
+# define VC4_HDMI_VERTA_VFP_MASK		VC4_MASK(19, 13)
+# define VC4_HDMI_VERTA_VFP_SHIFT		13
+/* Vertical active lines (vdisplay). */
+# define VC4_HDMI_VERTA_VAL_MASK		VC4_MASK(12, 0)
+# define VC4_HDMI_VERTA_VAL_SHIFT		0
+
+#define VC4_HDMI_VERTB0				0x0d0
+#define VC4_HDMI_VERTB1				0x0d8
+/* Vertical sync pulse offset (for interlaced) */
+# define VC4_HDMI_VERTB_VSPO_MASK		VC4_MASK(21, 9)
+# define VC4_HDMI_VERTB_VSPO_SHIFT		9
+/* Vertical pack porch (vtotal - vsync_end). */
+# define VC4_HDMI_VERTB_VBP_MASK		VC4_MASK(8, 0)
+# define VC4_HDMI_VERTB_VBP_SHIFT		0
+
+#define VC4_HDMI_TX_PHY_RESET_CTL		0x2c0
+
+#define VC4_HD_M_CTL				0x00c
+# define VC4_HD_M_SW_RST			(1 << 2)
+# define VC4_HD_M_ENABLE			(1 << 0)
+
+#define VC4_HD_MAI_CTL				0x014
+
+#define VC4_HD_VID_CTL				0x038
+# define VC4_HD_VID_CTL_ENABLE			(1 << 31)
+# define VC4_HD_VID_CTL_UNDERFLOW_ENABLE	(1 << 30)
+# define VC4_HD_VID_CTL_FRAME_COUNTER_RESET	(1 << 29)
+# define VC4_HD_VID_CTL_VSYNC_LOW		(1 << 28)
+# define VC4_HD_VID_CTL_HSYNC_LOW		(1 << 27)
+
+#define VC4_HD_CSC_CTL				0x040
+# define VC4_HD_CSC_CTL_ORDER_MASK		VC4_MASK(7, 5)
+# define VC4_HD_CSC_CTL_ORDER_SHIFT		5
+# define VC4_HD_CSC_CTL_ORDER_RGB		0
+# define VC4_HD_CSC_CTL_ORDER_BGR		1
+# define VC4_HD_CSC_CTL_ORDER_BRG		2
+# define VC4_HD_CSC_CTL_ORDER_GRB		3
+# define VC4_HD_CSC_CTL_ORDER_GBR		4
+# define VC4_HD_CSC_CTL_ORDER_RBG		5
+# define VC4_HD_CSC_CTL_PADMSB			(1 << 4)
+# define VC4_HD_CSC_CTL_MODE_MASK		VC4_MASK(3, 2)
+# define VC4_HD_CSC_CTL_MODE_SHIFT		2
+# define VC4_HD_CSC_CTL_MODE_RGB_TO_SD_YPRPB	0
+# define VC4_HD_CSC_CTL_MODE_RGB_TO_HD_YPRPB	1
+# define VC4_HD_CSC_CTL_MODE_CUSTOM		2
+# define VC4_HD_CSC_CTL_RGB2YCC			(1 << 1)
+# define VC4_HD_CSC_CTL_ENABLE			(1 << 0)
+
+#define VC4_HD_FRAME_COUNT			0x068
+
+/* HVS display list information. */
+#define HVS_BOOTLOADER_DLIST_END                32
+
+enum hvs_pixel_format {
+	/* 8bpp */
+	HVS_PIXEL_FORMAT_RGB332 = 0,
+	/* 16bpp */
+	HVS_PIXEL_FORMAT_RGBA4444 = 1,
+	HVS_PIXEL_FORMAT_RGB555 = 2,
+	HVS_PIXEL_FORMAT_RGBA5551 = 3,
+	HVS_PIXEL_FORMAT_RGB565 = 4,
+	/* 24bpp */
+	HVS_PIXEL_FORMAT_RGB888 = 5,
+	HVS_PIXEL_FORMAT_RGBA6666 = 6,
+	/* 32bpp */
+	HVS_PIXEL_FORMAT_RGBA8888 = 7
+};
+
+/* Note: the LSB is the rightmost character shown.  Only valid for
+ * HVS_PIXEL_FORMAT_RGB8888, not RGB888.
+ */
+#define HVS_PIXEL_ORDER_RGBA			0
+#define HVS_PIXEL_ORDER_BGRA			1
+#define HVS_PIXEL_ORDER_ARGB			2
+#define HVS_PIXEL_ORDER_ABGR			3
+
+#define HVS_PIXEL_ORDER_XBRG			0
+#define HVS_PIXEL_ORDER_XRBG			1
+#define HVS_PIXEL_ORDER_XRGB			2
+#define HVS_PIXEL_ORDER_XBGR			3
+
+#define HVS_PIXEL_ORDER_XYCBCR			0
+#define HVS_PIXEL_ORDER_XYCRCB			1
+#define HVS_PIXEL_ORDER_YXCBCR			2
+#define HVS_PIXEL_ORDER_YXCRCB			3
+
+#define SCALER_CTL0_END				(1 << 31)
+#define SCALER_CTL0_VALID			(1 << 30)
+
+#define SCALER_CTL0_SIZE_MASK			VC4_MASK(29, 24)
+#define SCALER_CTL0_SIZE_SHIFT			24
+
+#define SCALER_CTL0_HFLIP                       (1 << 16)
+#define SCALER_CTL0_VFLIP                       (1 << 15)
+
+#define SCALER_CTL0_ORDER_MASK			VC4_MASK(14, 13)
+#define SCALER_CTL0_ORDER_SHIFT			13
+
+/* Set to indicate no scaling. */
+#define SCALER_CTL0_UNITY			(1 << 4)
+
+#define SCALER_CTL0_PIXEL_FORMAT_MASK		VC4_MASK(3, 0)
+#define SCALER_CTL0_PIXEL_FORMAT_SHIFT		0
+
+#define SCALER_POS0_FIXED_ALPHA_MASK		VC4_MASK(31, 24)
+#define SCALER_POS0_FIXED_ALPHA_SHIFT		24
+
+#define SCALER_POS0_START_Y_MASK		VC4_MASK(23, 12)
+#define SCALER_POS0_START_Y_SHIFT		12
+
+#define SCALER_POS0_START_X_MASK		VC4_MASK(11, 0)
+#define SCALER_POS0_START_X_SHIFT		0
+
+#define SCALER_POS2_ALPHA_MODE_MASK		VC4_MASK(31, 30)
+#define SCALER_POS2_ALPHA_MODE_SHIFT		30
+#define SCALER_POS2_ALPHA_MODE_PIPELINE		0
+#define SCALER_POS2_ALPHA_MODE_FIXED		1
+#define SCALER_POS2_ALPHA_MODE_FIXED_NONZERO	2
+#define SCALER_POS2_ALPHA_MODE_FIXED_OVER_0x07	3
+
+#define SCALER_POS2_HEIGHT_MASK			VC4_MASK(27, 16)
+#define SCALER_POS2_HEIGHT_SHIFT		16
+
+#define SCALER_POS2_WIDTH_MASK			VC4_MASK(11, 0)
+#define SCALER_POS2_WIDTH_SHIFT			0
+
+#define SCALER_SRC_PITCH_MASK			VC4_MASK(15, 0)
+#define SCALER_SRC_PITCH_SHIFT			0
-- 
2.1.4



From daniel at ffwll.ch  Thu Aug 13 00:51:41 2015
From: daniel at ffwll.ch (Daniel Vetter)
Date: Thu, 13 Aug 2015 09:51:41 +0200
Subject: [PATCH 3/7] drm/vc4: Add KMS support for Raspberry Pi.
In-Reply-To: <1439427380-2436-4-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-4-git-send-email-eric@anholt.net>
Message-ID: <20150813075141.GX17734@phenom.ffwll.local>

On Wed, Aug 12, 2015 at 05:56:16PM -0700, Eric Anholt wrote:
> This is the start of a full VC4 driver.  Right now this just supports
> configuring the display using a pre-existing video mode (because
> changing the pixel clock isn't available yet, and doesn't work when it
> is).  However, this is enough for fbcon and bringing up X using
> xf86-video-modesetting.
> 
> Signed-off-by: Eric Anholt <eric at anholt.net>
> ---
>  drivers/gpu/drm/Kconfig           |   2 +
>  drivers/gpu/drm/Makefile          |   1 +
>  drivers/gpu/drm/vc4/Kconfig       |  14 +
>  drivers/gpu/drm/vc4/Makefile      |  18 ++
>  drivers/gpu/drm/vc4/vc4_bo.c      |  54 ++++
>  drivers/gpu/drm/vc4/vc4_crtc.c    | 583 ++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/vc4/vc4_debugfs.c |  38 +++
>  drivers/gpu/drm/vc4/vc4_drv.c     | 249 +++++++++++++++
>  drivers/gpu/drm/vc4/vc4_drv.h     | 123 +++++++
>  drivers/gpu/drm/vc4/vc4_hdmi.c    | 651 ++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/vc4/vc4_hvs.c     | 172 ++++++++++
>  drivers/gpu/drm/vc4/vc4_kms.c     |  84 +++++
>  drivers/gpu/drm/vc4/vc4_plane.c   | 320 +++++++++++++++++++
>  drivers/gpu/drm/vc4/vc4_regs.h    | 562 ++++++++++++++++++++++++++++++++
>  14 files changed, 2871 insertions(+)
>  create mode 100644 drivers/gpu/drm/vc4/Kconfig
>  create mode 100644 drivers/gpu/drm/vc4/Makefile
>  create mode 100644 drivers/gpu/drm/vc4/vc4_bo.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_crtc.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_debugfs.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_drv.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_drv.h
>  create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_hvs.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_kms.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_plane.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_regs.h

Made a quick pass and found a few things to update to latest drm
developments. Of course didn't look at the hardware details since no clue,
but looks really nice overall.
-Daniel

> 
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index c46ca31..1730a76 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -240,3 +240,5 @@ source "drivers/gpu/drm/sti/Kconfig"
>  source "drivers/gpu/drm/amd/amdkfd/Kconfig"
>  
>  source "drivers/gpu/drm/imx/Kconfig"
> +
> +source "drivers/gpu/drm/vc4/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 5713d05..b991ac5 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -42,6 +42,7 @@ obj-$(CONFIG_DRM_MGA)	+= mga/
>  obj-$(CONFIG_DRM_I810)	+= i810/
>  obj-$(CONFIG_DRM_I915)  += i915/
>  obj-$(CONFIG_DRM_MGAG200) += mgag200/
> +obj-$(CONFIG_DRM_VC4)  += vc4/
>  obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/
>  obj-$(CONFIG_DRM_SIS)   += sis/
>  obj-$(CONFIG_DRM_SAVAGE)+= savage/
> diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig
> new file mode 100644
> index 0000000..130cc94
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/Kconfig
> @@ -0,0 +1,14 @@
> +config DRM_VC4
> +	tristate "Broadcom VC4 Graphics"
> +	depends on ARCH_BCM2835
> +	depends on DRM
> +	select DRM_KMS_HELPER
> +	select DRM_KMS_FB_HELPER
> +	select DRM_KMS_CMA_HELPER

drm-misc/linux-next already has Archit's patches to enable/disable fbdev
in the core code, so you don't need to bother about these selects here any
more, it'll no-op out if drm fbdev emulation isn't enabled. Since you're
reusing cma fbdev helpers I don't think there's any need for other changes
because of this.

> +	help
> +	  Choose this option if you have a system that has a Broadcom
> +	  VC4 GPU, such as the Raspberry Pi or other BCM2708/BCM2835.
> +
> +	  This driver requires that "avoid_warnings=2" be present in
> +	  the config.txt for the firmware, to keep it from smashing
> +	  our display setup.
> diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile
> new file mode 100644
> index 0000000..4aa07ca
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/Makefile
> @@ -0,0 +1,18 @@
> +ccflags-y := -Iinclude/drm
> +
> +# Please keep these build lists sorted!
> +
> +# core driver code
> +vc4-y := \
> +	vc4_bo.o \
> +	vc4_crtc.o \
> +	vc4_drv.o \
> +	vc4_kms.o \
> +	vc4_hdmi.o \
> +	vc4_hvs.o \
> +	vc4_plane.o \
> +	$()
> +
> +vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o
> +
> +obj-$(CONFIG_DRM_VC4)  += vc4.o
> diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
> new file mode 100644
> index 0000000..fee8cac
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_bo.c
> @@ -0,0 +1,54 @@
> +/*
> + *  Copyright ? 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +/* DOC: VC4 GEM BO management support.
> + *
> + * The VC4 GPU architecture (both scanout and rendering) has direct
> + * access to system memory with no MMU in between.  To support it, we
> + * use the GEM CMA helper functions to allocate contiguous ranges of
> + * physical memory for our BOs.
> + */

Since you're doing kerneldoc considered pulling it all into a new vc4
section in the drm docbook template?

> +
> +#include "vc4_drv.h"
> +
> +struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size)
> +{
> +	struct drm_gem_cma_object *cma_obj;
> +
> +	cma_obj = drm_gem_cma_create(dev, size);
> +	if (IS_ERR(cma_obj))
> +		return NULL;
> +	else
> +		return to_vc4_bo(&cma_obj->base);
> +}
> +
> +int vc4_dumb_create(struct drm_file *file_priv,
> +		    struct drm_device *dev,
> +		    struct drm_mode_create_dumb *args)
> +{
> +	int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
> +	struct vc4_bo *bo = NULL;
> +	int ret;
> +
> +	if (args->pitch < min_pitch)
> +		args->pitch = min_pitch;
> +
> +	if (args->size < args->pitch * args->height)
> +		args->size = args->pitch * args->height;
> +
> +	mutex_lock(&dev->struct_mutex);
> +	bo = vc4_bo_create(dev, roundup(args->size, PAGE_SIZE));
> +	mutex_unlock(&dev->struct_mutex);

I'm on a struct_mutex crusade (trying to get rid of it in core and allow
drivers to live without it). On a quick look there doesn't seem to be
anything that needs struct_mutex here, so please just remove it. If there
is indeed something vc4-internal you want to protect, please use your own
driver-internal mutex (e.g. for drm_mm or command submission or whatever).

btw the last bit in the drm core for modern drivers that needs
struct_mutex is mmap_offset gem object lookup. I plan to replace that with
kref_get_unless_zero trickery, which would make the core and a lot of
drivers struct_mutex free and so relegate it mostly to a legacy role (and
can be forgotten).

> +	if (!bo)
> +		return -ENOMEM;
> +
> +	ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle);
> +	drm_gem_object_unreference_unlocked(&bo->base.base);
> +
> +	return ret;
> +}
> diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
> new file mode 100644
> index 0000000..6f127f0
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
> @@ -0,0 +1,583 @@
> +/*
> + * Copyright (C) 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +/**
> + * DOC: VC4 CRTC module
> + *
> + * In VC4, the Pixel Valve is what most closely corresponds to the
> + * DRM's concept of a CRTC.  The PV generates video timings from the
> + * pixel clock and its configuration.
> + *
> + * However, the DRM CRTC also collects the configuration of all the
> + * DRM planes attached to it.  As a result, this file also manages
> + * setup of the VC4 HVS's display elements on the CRTC.
> + */
> +
> +#include "drm_atomic_helper.h"
> +#include "drm_crtc_helper.h"
> +#include "linux/clk.h"
> +#include "linux/component.h"
> +#include "vc4_drv.h"
> +#include "vc4_regs.h"
> +
> +#define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset))
> +#define CRTC_READ(offset) readl(vc4_crtc->regs + (offset))
> +
> +#define CRTC_REG(reg) { reg, #reg }
> +static const struct {
> +	u32 reg;
> +	const char *name;
> +} crtc_regs[] = {
> +	CRTC_REG(PV_CONTROL),
> +	CRTC_REG(PV_V_CONTROL),
> +	CRTC_REG(PV_VSYNCD),
> +	CRTC_REG(PV_HORZA),
> +	CRTC_REG(PV_HORZB),
> +	CRTC_REG(PV_VERTA),
> +	CRTC_REG(PV_VERTB),
> +	CRTC_REG(PV_VERTA_EVEN),
> +	CRTC_REG(PV_VERTB_EVEN),
> +	CRTC_REG(PV_INTEN),
> +	CRTC_REG(PV_INTSTAT),
> +	CRTC_REG(PV_STAT),
> +	CRTC_REG(PV_HACT_ACT),
> +};
> +
> +static void vc4_crtc_dump_regs(struct vc4_crtc *vc4_crtc)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(crtc_regs); i++) {
> +		DRM_INFO("0x%04x (%s): 0x%08x\n",
> +			 crtc_regs[i].reg, crtc_regs[i].name,
> +			 CRTC_READ(crtc_regs[i].reg));
> +	}
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +int vc4_crtc_debugfs_regs(struct seq_file *m, void *unused)
> +{
> +	struct drm_info_node *node = (struct drm_info_node *) m->private;
> +	struct drm_device *dev = node->minor->dev;
> +	int crtc_index = (uintptr_t)node->info_ent->data;
> +	struct drm_crtc *crtc;
> +	struct vc4_crtc *vc4_crtc;
> +	int i;
> +
> +	i = 0;
> +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> +		if (i == crtc_index)
> +			break;
> +		i++;
> +	}
> +	if (!crtc)
> +		return 0;
> +	vc4_crtc = to_vc4_crtc(crtc);
> +
> +	for (i = 0; i < ARRAY_SIZE(crtc_regs); i++) {
> +		seq_printf(m, "%s (0x%04x): 0x%08x\n",
> +			   crtc_regs[i].name, crtc_regs[i].reg,
> +			   CRTC_READ(crtc_regs[i].reg));
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static void vc4_crtc_destroy(struct drm_crtc *crtc)
> +{
> +	drm_crtc_cleanup(crtc);
> +}
> +
> +static bool vc4_crtc_mode_fixup(struct drm_crtc *crtc,
> +				const struct drm_display_mode *mode,
> +				struct drm_display_mode *adjusted_mode)
> +{
> +	return true;
> +}

mode_fixup on crtcs is optional since 840bfe953384a and I just merged a
patch to make it optional for encoders too (when using atomic helpers
which you do). You can remove them both.

> +
> +static u32 vc4_get_fifo_full_level(u32 format)
> +{
> +	static const u32 fifo_len_bytes = 64;
> +	static const u32 hvs_latency_pix = 6;
> +
> +	switch (format) {
> +	case PV_CONTROL_FORMAT_DSIV_16:
> +	case PV_CONTROL_FORMAT_DSIC_16:
> +		return fifo_len_bytes - 2 * hvs_latency_pix;
> +	case PV_CONTROL_FORMAT_DSIV_18:
> +		return fifo_len_bytes - 14;
> +	case PV_CONTROL_FORMAT_24:
> +	case PV_CONTROL_FORMAT_DSIV_24:
> +	default:
> +		return fifo_len_bytes - 3 * hvs_latency_pix;
> +	}
> +}
> +
> +static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
> +{
> +	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> +	struct drm_crtc_state *state = crtc->state;
> +	struct drm_display_mode *mode = &state->adjusted_mode;
> +	u32 vactive = (mode->vdisplay >>
> +		       ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0));
> +	u32 format = PV_CONTROL_FORMAT_24;
> +	bool debug_dump_regs = false;
> +
> +	if (debug_dump_regs) {
> +		DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc));
> +		vc4_crtc_dump_regs(vc4_crtc);
> +	}
> +
> +	/* This is where we would set the pixel clock. */
> +
> +	/* Reset the PV fifo. */
> +	CRTC_WRITE(PV_CONTROL, 0);
> +	CRTC_WRITE(PV_CONTROL, PV_CONTROL_FIFO_CLR | PV_CONTROL_EN);
> +	CRTC_WRITE(PV_CONTROL, 0);
> +
> +	CRTC_WRITE(PV_HORZA,
> +		   VC4_SET_FIELD(mode->htotal - mode->hsync_end,
> +				 PV_HORZA_HBP) |
> +		   VC4_SET_FIELD(mode->hsync_end - mode->hsync_start,
> +				 PV_HORZA_HSYNC));
> +	CRTC_WRITE(PV_HORZB,
> +		   VC4_SET_FIELD(mode->hsync_start - mode->hdisplay,
> +				 PV_HORZB_HFP) |
> +		   VC4_SET_FIELD(mode->hdisplay, PV_HORZB_HACTIVE));
> +
> +	CRTC_WRITE(PV_VERTA,
> +		   VC4_SET_FIELD(mode->vtotal - mode->vsync_end,
> +				 PV_VERTA_VBP) |
> +		   VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
> +				 PV_VERTA_VSYNC));
> +	CRTC_WRITE(PV_VERTB,
> +		   VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
> +				 PV_VERTB_VFP) |
> +		   VC4_SET_FIELD(vactive, PV_VERTB_VACTIVE));
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		/* Write PV_VERTA_EVEN/VERTB_EVEN */
> +	}
> +
> +	CRTC_WRITE(PV_HACT_ACT, mode->hdisplay);
> +
> +	CRTC_WRITE(PV_V_CONTROL,
> +		   PV_VCONTROL_CONTINUOUS);
> +
> +	CRTC_WRITE(PV_CONTROL,
> +		   VC4_SET_FIELD(format, PV_CONTROL_FORMAT) |
> +		   VC4_SET_FIELD(vc4_get_fifo_full_level(format),
> +				 PV_CONTROL_FIFO_LEVEL) |
> +		   PV_CONTROL_CLR_AT_START |
> +		   PV_CONTROL_TRIGGER_UNDERFLOW |
> +		   PV_CONTROL_WAIT_HSTART |
> +		   VC4_SET_FIELD(PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI,
> +				 PV_CONTROL_CLK_SELECT) |
> +		   PV_CONTROL_FIFO_CLR |
> +		   PV_CONTROL_EN);
> +
> +	if (debug_dump_regs) {
> +		DRM_INFO("CRTC %d regs after:\n", drm_crtc_index(crtc));
> +		vc4_crtc_dump_regs(vc4_crtc);
> +	}
> +}
> +
> +static void
> +require_hvs_enabled(struct drm_device *dev)
> +{
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +
> +	WARN_ON_ONCE((HVS_READ(SCALER_DISPCTRL) & SCALER_DISPCTRL_ENABLE) !=
> +		     SCALER_DISPCTRL_ENABLE);
> +}
> +
> +static void vc4_crtc_disable(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> +
> +	require_hvs_enabled(dev);
> +
> +	CRTC_WRITE(PV_V_CONTROL,
> +		   CRTC_READ(PV_V_CONTROL) & ~PV_VCONTROL_VIDEN);
> +	while (CRTC_READ(PV_V_CONTROL) & PV_VCONTROL_VIDEN)
> +		cpu_relax();
> +
> +	/* Without a wait here, we end up with a black screen. */
> +	msleep(30);
> +
> +	if (HVS_READ(SCALER_DISPCTRLX(vc4_crtc->channel)) &
> +	    SCALER_DISPCTRLX_ENABLE) {
> +		HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel),
> +			  SCALER_DISPCTRLX_RESET);
> +
> +		/* While the docs say that reset is self-clearing, it
> +		 * seems it doesn't actually.
> +		 */
> +		HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), 0);
> +	}
> +
> +	/* Once we leave, the scaler should be disabled and its fifo empty. */
> +
> +	WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(vc4_crtc->channel)) &
> +		     SCALER_DISPCTRLX_RESET);
> +
> +	WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(vc4_crtc->channel)),
> +				   SCALER_DISPSTATX_MODE) !=
> +		     SCALER_DISPSTATX_MODE_DISABLED);
> +
> +	WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(vc4_crtc->channel)) &
> +		      (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) !=
> +		     SCALER_DISPSTATX_EMPTY);
> +}
> +
> +static void vc4_crtc_enable(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> +	struct drm_crtc_state *state = crtc->state;
> +	struct drm_display_mode *mode = &state->adjusted_mode;
> +
> +	require_hvs_enabled(dev);
> +
> +	/* Turn on the scaler, which will wait for vstart to start
> +	 * compositing.
> +	 */
> +	HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel),
> +		  VC4_SET_FIELD(mode->hdisplay, SCALER_DISPCTRLX_WIDTH) |
> +		  VC4_SET_FIELD(mode->vdisplay, SCALER_DISPCTRLX_HEIGHT) |
> +		  SCALER_DISPCTRLX_ENABLE);
> +
> +	/* Turn on the pixel valve, which will emit the vstart signal. */
> +	CRTC_WRITE(PV_V_CONTROL,
> +		   CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN);
> +}
> +
> +static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
> +				 struct drm_crtc_state *state)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct drm_plane *plane;
> +	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> +	u32 dlist_count = 0;
> +
> +	drm_atomic_crtc_state_for_each_plane(plane, state) {
> +		struct drm_plane_state *plane_state =
> +			state->state->plane_states[drm_plane_index(plane)];
> +
> +		/* plane might not have changed, in which case take
> +		 * current state:
> +		 */
> +		if (!plane_state)
> +			plane_state = plane->state;
> +
> +		dlist_count += vc4_plane_dlist_size(plane_state);
> +	}
> +
> +	dlist_count++; /* Account for SCALER_CTL0_END. */
> +
> +	if (!vc4_crtc->dlist || dlist_count > vc4_crtc->dlist_size) {
> +		vc4_crtc->dlist = ((u32 __iomem *)vc4->hvs->dlist +
> +				   HVS_BOOTLOADER_DLIST_END);
> +		vc4_crtc->dlist_size = ((SCALER_DLIST_SIZE >> 2) -
> +					HVS_BOOTLOADER_DLIST_END);
> +
> +		if (dlist_count > vc4_crtc->dlist_size) {
> +			DRM_DEBUG_KMS("dlist too large for CRTC (%d > %d).\n",
> +				      dlist_count, vc4_crtc->dlist_size);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void vc4_crtc_atomic_begin(struct drm_crtc *crtc)
> +{
> +}
> +
> +static void vc4_crtc_atomic_flush(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> +	struct drm_plane *plane;
> +	bool debug_dump_regs = false;
> +	u32 __iomem *dlist_next = vc4_crtc->dlist;
> +
> +	if (debug_dump_regs) {
> +		DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
> +		vc4_hvs_dump_state(dev);
> +	}
> +
> +	/* Copy all the active planes' dlist contents to the hardware dlist.
> +	 *
> +	 * XXX: If the new display list was large enough that it
> +	 * overlapped a currently-read display list, we need to do
> +	 * something like disable scanout before putting in the new
> +	 * list.  For now, we're safe because we only have the two
> +	 * planes.
> +	 */
> +	drm_atomic_crtc_for_each_plane(plane, crtc) {
> +		dlist_next += vc4_plane_write_dlist(plane, dlist_next);
> +	}
> +
> +	if (dlist_next == vc4_crtc->dlist) {
> +		/* If no planes were enabled, use the SCALER_CTL0_END
> +		 * at the start of the display list memory (in the
> +		 * bootloader section).  We'll rewrite that
> +		 * SCALER_CTL0_END, just in case, though.
> +		 */
> +		writel(SCALER_CTL0_END, vc4->hvs->dlist);
> +		HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), 0);
> +	} else {
> +		writel(SCALER_CTL0_END, dlist_next);
> +		dlist_next++;
> +
> +		HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
> +			  (u32 *)vc4_crtc->dlist - (u32 *)vc4->hvs->dlist);
> +
> +		/* Make the next display list start after ours. */
> +		vc4_crtc->dlist_size -= (dlist_next - vc4_crtc->dlist);
> +		vc4_crtc->dlist = dlist_next;
> +	}
> +
> +	if (debug_dump_regs) {
> +		DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
> +		vc4_hvs_dump_state(dev);
> +	}
> +
> +	if (crtc->state->event) {
> +		unsigned long flags;
> +
> +		crtc->state->event->pipe = drm_crtc_index(crtc);
> +
> +		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
> +
> +		spin_lock_irqsave(&dev->event_lock, flags);
> +		vc4_crtc->event = crtc->state->event;
> +		spin_unlock_irqrestore(&dev->event_lock, flags);
> +		crtc->state->event = NULL;
> +	}
> +}
> +
> +int vc4_enable_vblank(struct drm_device *dev, int crtc_id)
> +{
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
> +
> +	CRTC_WRITE(PV_INTEN, PV_INT_VFP_START);
> +
> +	return 0;
> +}
> +
> +void vc4_disable_vblank(struct drm_device *dev, int crtc_id)
> +{
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
> +
> +	CRTC_WRITE(PV_INTEN, 0);
> +}
> +
> +static void
> +vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
> +{
> +	struct drm_crtc *crtc = &vc4_crtc->base;
> +	struct drm_device *dev = crtc->dev;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&dev->event_lock, flags);
> +	if (vc4_crtc->event) {
> +		drm_crtc_send_vblank_event(crtc, vc4_crtc->event);
> +		vc4_crtc->event = NULL;
> +	}
> +	spin_unlock_irqrestore(&dev->event_lock, flags);
> +}
> +
> +static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
> +{
> +	struct vc4_crtc *vc4_crtc = data;
> +	u32 stat = CRTC_READ(PV_INTSTAT);
> +	irqreturn_t ret = IRQ_NONE;
> +
> +	if (stat & PV_INT_VFP_START) {
> +		CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
> +		drm_crtc_handle_vblank(&vc4_crtc->base);
> +		vc4_crtc_handle_page_flip(vc4_crtc);
> +		ret = IRQ_HANDLED;
> +	}
> +
> +	return ret;
> +}
> +
> +static const struct drm_crtc_funcs vc4_crtc_funcs = {
> +	.set_config = drm_atomic_helper_set_config,
> +	.destroy = vc4_crtc_destroy,
> +	.page_flip = drm_atomic_helper_page_flip,
> +	.set_property = NULL,
> +	.cursor_set = NULL, /* handled by drm_mode_cursor_universal */
> +	.cursor_move = NULL, /* handled by drm_mode_cursor_universal */
> +	.reset = drm_atomic_helper_crtc_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
> +};
> +
> +static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
> +	.mode_fixup = vc4_crtc_mode_fixup,
> +	.mode_set_nofb = vc4_crtc_mode_set_nofb,
> +	.disable = vc4_crtc_disable,
> +	.enable = vc4_crtc_enable,
> +	.atomic_check = vc4_crtc_atomic_check,
> +	.atomic_begin = vc4_crtc_atomic_begin,
> +	.atomic_flush = vc4_crtc_atomic_flush,
> +};
> +
> +/* Frees the page flip event when the DRM device is closed with the
> + * event still outstanding.
> + */
> +void vc4_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file)
> +{
> +	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> +	struct drm_device *dev = crtc->dev;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&dev->event_lock, flags);
> +
> +	if (vc4_crtc->event && vc4_crtc->event->base.file_priv == file) {
> +		vc4_crtc->event->base.destroy(&vc4_crtc->event->base);
> +		drm_crtc_vblank_put(crtc);
> +		vc4_crtc->event = NULL;
> +	}
> +
> +	spin_unlock_irqrestore(&dev->event_lock, flags);
> +}
> +
> +static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = dev_get_drvdata(master);
> +	struct vc4_dev *vc4 = to_vc4_dev(drm);
> +	struct vc4_crtc *vc4_crtc;
> +	struct drm_crtc *crtc;
> +	struct drm_plane *primary_plane, *cursor_plane;
> +	int ret;
> +
> +	primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY);
> +	if (!primary_plane) {
> +		dev_err(dev, "failed to construct primary plane\n");
> +		ret = PTR_ERR(primary_plane);
> +		goto fail;
> +	}
> +
> +	cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
> +	if (!cursor_plane) {
> +		dev_err(dev, "failed to construct cursor plane\n");
> +		ret = PTR_ERR(cursor_plane);
> +		goto fail;
> +	}
> +
> +	vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
> +	if (!vc4_crtc)
> +		return -ENOMEM;
> +	crtc = &vc4_crtc->base;
> +
> +	vc4_crtc->regs = vc4_ioremap_regs(pdev, 0);
> +	if (IS_ERR(vc4_crtc->regs))
> +		return PTR_ERR(vc4_crtc->regs);
> +
> +	drm_crtc_init_with_planes(drm, crtc, primary_plane, cursor_plane,
> +				  &vc4_crtc_funcs);
> +	drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
> +	primary_plane->crtc = crtc;
> +	cursor_plane->crtc = crtc;
> +	vc4->crtc[drm_crtc_index(crtc)] = vc4_crtc;
> +
> +	/* Until we have full scanout setup to route things through to
> +	 * encoders, line things up like the firmware did.
> +	 */
> +	switch (drm_crtc_index(crtc)) {
> +	case 0:
> +		vc4_crtc->channel = 0;
> +		break;
> +	case 1:
> +		vc4_crtc->channel = 2;
> +		break;
> +	default:
> +	case 2:
> +		vc4_crtc->channel = 1;
> +		break;
> +	}
> +
> +	CRTC_WRITE(PV_INTEN, 0);
> +	CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
> +	ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
> +			       vc4_crtc_irq_handler, 0, "vc4 crtc", vc4_crtc);
> +
> +	platform_set_drvdata(pdev, vc4_crtc);
> +
> +	return 0;
> +
> +fail:
> +	return ret;
> +}
> +
> +static void vc4_crtc_unbind(struct device *dev, struct device *master,
> +			    void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct vc4_crtc *vc4_crtc = dev_get_drvdata(dev);
> +
> +	vc4_crtc_destroy(&vc4_crtc->base);
> +
> +	CRTC_WRITE(PV_INTEN, 0);
> +
> +	platform_set_drvdata(pdev, NULL);
> +}
> +
> +static const struct component_ops vc4_crtc_ops = {
> +	.bind   = vc4_crtc_bind,
> +	.unbind = vc4_crtc_unbind,
> +};
> +
> +static int vc4_crtc_dev_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &vc4_crtc_ops);
> +}
> +
> +static int vc4_crtc_dev_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &vc4_crtc_ops);
> +	return 0;
> +}
> +
> +static const struct of_device_id vc4_crtc_dt_match[] = {
> +	{ .compatible = "brcm,vc4-pixelvalve" },
> +	{}
> +};
> +
> +static struct platform_driver vc4_crtc_driver = {
> +	.probe = vc4_crtc_dev_probe,
> +	.remove = vc4_crtc_dev_remove,
> +	.driver = {
> +		.name = "vc4_crtc",
> +		.of_match_table = vc4_crtc_dt_match,
> +	},
> +};
> +
> +void __init vc4_crtc_register(void)
> +{
> +	platform_driver_register(&vc4_crtc_driver);
> +}
> +
> +void __exit vc4_crtc_unregister(void)
> +{
> +	platform_driver_unregister(&vc4_crtc_driver);
> +}
> diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c
> new file mode 100644
> index 0000000..c557039
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c
> @@ -0,0 +1,38 @@
> +/*
> + *  Copyright ? 2014 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/seq_file.h>
> +#include <linux/circ_buf.h>
> +#include <linux/ctype.h>
> +#include <linux/debugfs.h>
> +#include <drm/drmP.h>
> +
> +#include "vc4_drv.h"
> +#include "vc4_regs.h"
> +
> +static const struct drm_info_list vc4_debugfs_list[] = {
> +	{"hdmi_regs", vc4_hdmi_debugfs_regs, 0},
> +	{"hvs_regs", vc4_hvs_debugfs_regs, 0},
> +	{"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0},
> +	{"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1},
> +	{"crtc2_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)2},
> +};
> +#define VC4_DEBUGFS_ENTRIES ARRAY_SIZE(vc4_debugfs_list)
> +
> +int
> +vc4_debugfs_init(struct drm_minor *minor)
> +{
> +	return drm_debugfs_create_files(vc4_debugfs_list, VC4_DEBUGFS_ENTRIES,
> +					minor->debugfs_root, minor);
> +}
> +
> +void
> +vc4_debugfs_cleanup(struct drm_minor *minor)
> +{
> +	drm_debugfs_remove_files(vc4_debugfs_list, VC4_DEBUGFS_ENTRIES, minor);
> +}
> diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
> new file mode 100644
> index 0000000..9ecf4b8b
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_drv.c
> @@ -0,0 +1,249 @@
> +/*
> + * Copyright (C) 2014-2015 Broadcom
> + * Copyright (C) 2013 Red Hat
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +
> +#include "vc4_drv.h"
> +#include "vc4_regs.h"
> +
> +#define DRIVER_NAME "vc4"
> +#define DRIVER_DESC "Broadcom VC4 graphics"
> +#define DRIVER_DATE "20140616"
> +#define DRIVER_MAJOR 0
> +#define DRIVER_MINOR 0
> +#define DRIVER_PATCHLEVEL 0
> +
> +/* Helper function for mapping the regs on a platform device.
> + *
> + * We assume only one register range per device, so we use index 0.
> + */
> +void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index)
> +{
> +	struct resource *res;
> +	void __iomem *map;
> +
> +	res = platform_get_resource(dev, IORESOURCE_MEM, index);
> +	map = devm_ioremap_resource(&dev->dev, res);
> +	if (IS_ERR(map)) {
> +		DRM_ERROR("Failed to map registers: %ld\n", PTR_ERR(map));
> +		return map;
> +	}
> +
> +	return map;
> +}
> +
> +static int vc4_drm_load(struct drm_device *dev, unsigned long flags)
> +{
> +	struct vc4_dev *vc4;
> +	int ret;
> +
> +	vc4 = devm_kzalloc(dev->dev, sizeof(*vc4), GFP_KERNEL);
> +	if (!vc4)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(dev->dev, dev);
> +	vc4->dev = dev;
> +	dev->dev_private = vc4;
> +
> +	drm_mode_config_init(dev);
> +
> +	ret = component_bind_all(dev->dev, dev);
> +	if (ret)
> +		return ret;
> +
> +	vc4_kms_load(dev);
> +
> +	return 0;
> +}

->load has backwards init ordering (we register public interfaces before
calling it, yay) because backwards compat. Hence deprecated, please use
drm_dev_alloc(); ... driver init including drm_dev_set_unique();
drm_dev_register(); instead and drop ->load.

> +
> +static int vc4_drm_unload(struct drm_device *dev)
> +{
> +	drm_mode_config_cleanup(dev);
> +
> +	component_unbind_all(dev->dev, dev);
> +
> +	return 0;
> +}
> +
> +static void vc4_drm_preclose(struct drm_device *dev, struct drm_file *file)
> +{
> +	struct drm_crtc *crtc;
> +
> +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
> +		vc4_cancel_page_flip(crtc, file);
> +}
> +
> +static const struct file_operations vc4_drm_fops = {
> +	.owner = THIS_MODULE,
> +	.open = drm_open,
> +	.release = drm_release,
> +	.unlocked_ioctl = drm_ioctl,
> +	.mmap = drm_gem_cma_mmap,
> +	.poll = drm_poll,
> +	.read = drm_read,
> +#ifdef CONFIG_COMPAT
> +	.compat_ioctl = drm_compat_ioctl,
> +#endif
> +	.llseek = noop_llseek,
> +};
> +
> +static const struct drm_ioctl_desc vc4_drm_ioctls[] = {
> +};
> +
> +static struct drm_driver vc4_drm_driver = {
> +	.driver_features = (DRIVER_MODESET |
> +			    DRIVER_ATOMIC |
> +			    DRIVER_GEM |
> +			    DRIVER_PRIME),
> +	.load = vc4_drm_load,
> +	.unload = vc4_drm_unload,
> +	.set_busid = drm_platform_set_busid,

with ->load dropp and set_unique called directly you can drop this one
too. tegra is a nice example for the new sequence.

> +	.preclose = vc4_drm_preclose,
> +
> +	.enable_vblank = vc4_enable_vblank,
> +	.disable_vblank = vc4_disable_vblank,
> +	.get_vblank_counter = drm_vblank_count,
> +
> +#if defined(CONFIG_DEBUG_FS)
> +	.debugfs_init = vc4_debugfs_init,
> +	.debugfs_cleanup = vc4_debugfs_cleanup,
> +#endif
> +
> +	.gem_free_object = drm_gem_cma_free_object,
> +	.gem_vm_ops = &drm_gem_cma_vm_ops,
> +
> +	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> +	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> +	.gem_prime_import = drm_gem_prime_import,
> +	.gem_prime_export = drm_gem_prime_export,
> +	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
> +	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
> +	.gem_prime_vmap		= drm_gem_cma_prime_vmap,
> +	.gem_prime_vunmap	= drm_gem_cma_prime_vunmap,
> +	.gem_prime_mmap		= drm_gem_cma_prime_mmap,
> +
> +	.dumb_create = vc4_dumb_create,
> +	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
> +	.dumb_destroy = drm_gem_dumb_destroy,
> +
> +	.ioctls = vc4_drm_ioctls,
> +	.num_ioctls = ARRAY_SIZE(vc4_drm_ioctls),
> +	.fops = &vc4_drm_fops,
> +
> +	.name = DRIVER_NAME,
> +	.desc = DRIVER_DESC,
> +	.date = DRIVER_DATE,
> +	.major = DRIVER_MAJOR,
> +	.minor = DRIVER_MINOR,
> +	.patchlevel = DRIVER_PATCHLEVEL,
> +};
> +
> +static int vc4_drm_bind(struct device *dev)
> +{
> +	return drm_platform_init(&vc4_drm_driver, to_platform_device(dev));
> +}
> +
> +static void vc4_drm_unbind(struct device *dev)
> +{
> +	drm_put_dev(platform_get_drvdata(to_platform_device(dev)));
> +}
> +
> +static const struct component_master_ops vc4_drm_ops = {
> +	.bind = vc4_drm_bind,
> +	.unbind = vc4_drm_unbind,
> +};
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> +	return dev->of_node == data;
> +}
> +
> +static int add_components(struct device *dev, struct component_match **matchptr,
> +			  const char *name)
> +{
> +	struct device_node *np = dev->of_node;
> +	unsigned i;
> +
> +	for (i = 0; ; i++) {
> +		struct device_node *node;
> +
> +		node = of_parse_phandle(np, name, i);
> +		if (!node)
> +			break;
> +
> +		component_match_add(dev, matchptr, compare_of, node);
> +	}
> +
> +	return 0;
> +}
> +
> +static int vc4_platform_drm_probe(struct platform_device *pdev)
> +{
> +	struct component_match *match = NULL;
> +
> +	add_components(&pdev->dev, &match, "crtcs");
> +	add_components(&pdev->dev, &match, "encoders");
> +	add_components(&pdev->dev, &match, "hvss");
> +
> +	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
> +	return component_master_add_with_match(&pdev->dev, &vc4_drm_ops, match);
> +}
> +
> +static int vc4_platform_drm_remove(struct platform_device *pdev)
> +{
> +	drm_put_dev(platform_get_drvdata(pdev));
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id vc4_of_match[] = {
> +	{ .compatible = "brcm,vc4", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, vc4_of_match);
> +
> +static struct platform_driver vc4_platform_driver = {
> +	.probe		= vc4_platform_drm_probe,
> +	.remove		= vc4_platform_drm_remove,
> +	.driver		= {
> +		.name	= "vc4-drm",
> +		.owner	= THIS_MODULE,
> +		.of_match_table = vc4_of_match,
> +	},
> +};
> +
> +static int __init vc4_drm_register(void)
> +{
> +	vc4_hdmi_register();
> +	vc4_crtc_register();
> +	vc4_hvs_register();
> +	return platform_driver_register(&vc4_platform_driver);
> +}
> +
> +static void __exit vc4_drm_unregister(void)
> +{
> +	platform_driver_unregister(&vc4_platform_driver);
> +	vc4_hvs_unregister();
> +	vc4_crtc_unregister();
> +	vc4_hdmi_unregister();
> +}
> +
> +module_init(vc4_drm_register);
> +module_exit(vc4_drm_unregister);
> +
> +MODULE_ALIAS("platform:vc4-drm");
> +MODULE_DESCRIPTION("Broadcom VC4 DRM Driver");
> +MODULE_AUTHOR("Eric Anholt <eric at anholt.net>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
> new file mode 100644
> index 0000000..1b0a0b0
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_drv.h
> @@ -0,0 +1,123 @@
> +/*
> + * Copyright (C) 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include "drmP.h"
> +#include "drm_gem_cma_helper.h"
> +
> +struct vc4_dev {
> +	struct drm_device *dev;
> +
> +	struct vc4_hdmi *hdmi;
> +	struct vc4_hvs *hvs;
> +	struct vc4_crtc *crtc[3];
> +};
> +
> +static inline struct vc4_dev *
> +to_vc4_dev(struct drm_device *dev)
> +{
> +	return (struct vc4_dev *)dev->dev_private;
> +}
> +
> +struct vc4_bo {
> +	struct drm_gem_cma_object base;
> +};
> +
> +static inline struct vc4_bo *
> +to_vc4_bo(struct drm_gem_object *bo)
> +{
> +	return (struct vc4_bo *)bo;
> +}
> +
> +struct vc4_hvs {
> +	struct platform_device *pdev;
> +	void __iomem *regs;
> +	void __iomem *dlist;
> +};
> +
> +struct vc4_crtc {
> +	struct drm_crtc base;
> +	void __iomem *regs;
> +
> +	/* Which HVS channel we're using for our CRTC. */
> +	int channel;
> +
> +	/* Pointer to the actual hardware display list memory for the
> +	 * crtc.
> +	 */
> +	u32 __iomem *dlist;
> +
> +	u32 dlist_size; /* in dwords */
> +
> +	struct drm_pending_vblank_event *event;
> +};
> +
> +static inline struct vc4_crtc *
> +to_vc4_crtc(struct drm_crtc *crtc)
> +{
> +	return (struct vc4_crtc *)crtc;
> +}
> +
> +struct vc4_plane {
> +	struct drm_plane base;
> +};
> +
> +static inline struct vc4_plane *
> +to_vc4_plane(struct drm_plane *plane)
> +{
> +	return (struct vc4_plane *)plane;
> +}
> +
> +#define HVS_READ(offset) readl(vc4->hvs->regs + offset)
> +#define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset)
> +
> +/* vc4_bo.c */
> +void vc4_free_object(struct drm_gem_object *gem_obj);
> +struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size);
> +int vc4_dumb_create(struct drm_file *file_priv,
> +		    struct drm_device *dev,
> +		    struct drm_mode_create_dumb *args);
> +struct dma_buf *vc4_prime_export(struct drm_device *dev,
> +				 struct drm_gem_object *obj, int flags);
> +
> +/* vc4_crtc.c */
> +void vc4_crtc_register(void);
> +void vc4_crtc_unregister(void);
> +int vc4_enable_vblank(struct drm_device *dev, int crtc_id);
> +void vc4_disable_vblank(struct drm_device *dev, int crtc_id);
> +void vc4_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
> +int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg);
> +
> +/* vc4_debugfs.c */
> +int vc4_debugfs_init(struct drm_minor *minor);
> +void vc4_debugfs_cleanup(struct drm_minor *minor);
> +
> +/* vc4_drv.c */
> +void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index);
> +
> +/* vc4_hdmi.c */
> +void vc4_hdmi_register(void);
> +void vc4_hdmi_unregister(void);
> +struct drm_encoder *vc4_hdmi_encoder_init(struct drm_device *dev);
> +struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
> +					      struct drm_encoder *encoder);
> +int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused);
> +
> +/* vc4_hvs.c */
> +void vc4_hvs_register(void);
> +void vc4_hvs_unregister(void);
> +void vc4_hvs_dump_state(struct drm_device *dev);
> +int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused);
> +
> +/* vc4_kms.c */
> +int vc4_kms_load(struct drm_device *dev);
> +
> +/* vc4_plane.c */
> +struct drm_plane *vc4_plane_init(struct drm_device *dev,
> +				 enum drm_plane_type type);
> +u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist);
> +u32 vc4_plane_dlist_size(struct drm_plane_state *state);
> diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
> new file mode 100644
> index 0000000..c1ac96e
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
> @@ -0,0 +1,651 @@
> +/*
> + * Copyright (c) 2014 The Linux Foundation. All rights reserved.
> + * Copyright (C) 2013 Red Hat
> + * Author: Rob Clark <robdclark at gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "drm_atomic_helper.h"
> +#include "drm_crtc_helper.h"
> +#include "drm_edid.h"
> +#include "linux/clk.h"
> +#include "linux/component.h"
> +#include "linux/of_gpio.h"
> +#include "linux/of_platform.h"
> +#include "vc4_drv.h"
> +#include "vc4_regs.h"
> +
> +/* General HDMI hardware state. */
> +struct vc4_hdmi {
> +	struct platform_device *pdev;
> +	struct i2c_adapter *ddc;
> +	void __iomem *hdmicore_regs;
> +	void __iomem *hd_regs;
> +	int hpd_gpio;
> +
> +	/* Probed video mode at boot time, used to filter display
> +	 * modes to only allow equivalent ones (since we can't set the
> +	 * pixel clock yet).
> +	 */
> +	struct drm_display_mode boot_mode;
> +};
> +#define HDMI_READ(offset) readl(vc4->hdmi->hdmicore_regs + offset)
> +#define HDMI_WRITE(offset, val) writel(val, vc4->hdmi->hdmicore_regs + offset)
> +#define HD_READ(offset) readl(vc4->hdmi->hd_regs + offset)
> +#define HD_WRITE(offset, val) writel(val, vc4->hdmi->hd_regs + offset)
> +
> +/* VC4 HDMI encoder KMS struct */
> +struct vc4_hdmi_encoder {
> +	struct drm_encoder base;
> +	bool hdmi_monitor;
> +};
> +static inline struct vc4_hdmi_encoder *
> +to_vc4_hdmi_encoder(struct drm_encoder *encoder)
> +{
> +	return container_of(encoder, struct vc4_hdmi_encoder, base);
> +}
> +
> +/* VC4 HDMI connector KMS struct */
> +struct vc4_hdmi_connector {
> +	struct drm_connector base;
> +
> +	/* Since the connector is attached to just the one encoder,
> +	 * this is the reference to it so we can do the best_encoder()
> +	 * hook.
> +	 */
> +	struct drm_encoder *encoder;
> +};
> +static inline struct vc4_hdmi_connector *
> +to_vc4_hdmi_connector(struct drm_connector *connector)
> +{
> +	return container_of(connector, struct vc4_hdmi_connector, base);
> +}
> +
> +#define HDMI_REG(reg) { reg, #reg }
> +static const struct {
> +	u32 reg;
> +	const char *name;
> +} hdmi_regs[] = {
> +	HDMI_REG(VC4_HDMI_CORE_REV),
> +	HDMI_REG(VC4_HDMI_SW_RESET_CONTROL),
> +	HDMI_REG(VC4_HDMI_HOTPLUG_INT),
> +	HDMI_REG(VC4_HDMI_HOTPLUG),
> +	HDMI_REG(VC4_HDMI_HORZA),
> +	HDMI_REG(VC4_HDMI_HORZB),
> +	HDMI_REG(VC4_HDMI_FIFO_CTL),
> +	HDMI_REG(VC4_HDMI_SCHEDULER_CONTROL),
> +	HDMI_REG(VC4_HDMI_VERTA0),
> +	HDMI_REG(VC4_HDMI_VERTA1),
> +	HDMI_REG(VC4_HDMI_VERTB0),
> +	HDMI_REG(VC4_HDMI_VERTB1),
> +	HDMI_REG(VC4_HDMI_TX_PHY_RESET_CTL),
> +};
> +static const struct {
> +	u32 reg;
> +	const char *name;
> +} hd_regs[] = {
> +	HDMI_REG(VC4_HD_M_CTL),
> +	HDMI_REG(VC4_HD_MAI_CTL),
> +	HDMI_REG(VC4_HD_VID_CTL),
> +	HDMI_REG(VC4_HD_CSC_CTL),
> +	HDMI_REG(VC4_HD_FRAME_COUNT),
> +};
> +
> +#ifdef CONFIG_DEBUG_FS
> +int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
> +{
> +	struct drm_info_node *node = (struct drm_info_node *) m->private;
> +	struct drm_device *dev = node->minor->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hdmi_regs); i++) {
> +		seq_printf(m, "%s (0x%04x): 0x%08x\n",
> +			   hdmi_regs[i].name, hdmi_regs[i].reg,
> +			   HDMI_READ(hdmi_regs[i].reg));
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(hd_regs); i++) {
> +		seq_printf(m, "%s (0x%04x): 0x%08x\n",
> +			   hd_regs[i].name, hd_regs[i].reg,
> +			   HD_READ(hd_regs[i].reg));
> +	}
> +
> +	return 0;
> +}
> +#endif /* CONFIG_DEBUG_FS */
> +
> +static void vc4_hdmi_dump_regs(struct drm_device *dev)
> +{
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hdmi_regs); i++) {
> +		DRM_INFO("0x%04x (%s): 0x%08x\n",
> +			 hdmi_regs[i].reg, hdmi_regs[i].name,
> +			 HDMI_READ(hdmi_regs[i].reg));
> +	}
> +	for (i = 0; i < ARRAY_SIZE(hd_regs); i++) {
> +		DRM_INFO("0x%04x (%s): 0x%08x\n",
> +			 hd_regs[i].reg, hd_regs[i].name,
> +			 HD_READ(hd_regs[i].reg));
> +	}
> +}
> +
> +static enum drm_connector_status
> +vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	struct drm_device *dev = connector->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +
> +	if (vc4->hdmi->hpd_gpio) {
> +		if (gpio_get_value(vc4->hdmi->hpd_gpio))
> +			return connector_status_connected;
> +		else
> +			return connector_status_disconnected;
> +	}
> +
> +	if (HDMI_READ(VC4_HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED)
> +		return connector_status_connected;
> +	else
> +		return connector_status_disconnected;
> +}
> +
> +static void vc4_hdmi_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +}
> +
> +static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct vc4_hdmi_connector *vc4_connector =
> +		to_vc4_hdmi_connector(connector);
> +	struct drm_encoder *encoder = vc4_connector->encoder;
> +	struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
> +	struct drm_device *dev = connector->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	int ret = 0;
> +	struct edid *edid;
> +
> +	edid = drm_get_edid(connector, vc4->hdmi->ddc);
> +	if (!edid)
> +		return -ENODEV;
> +
> +	vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
> +	drm_mode_connector_update_edid_property(connector, edid);
> +	ret = drm_add_edid_modes(connector, edid);
> +
> +	return ret;
> +}
> +
> +/* Since we can't set the pixel clock yet, filter out all the EDID
> + * modes that don't match what was set up by the firmware.
> + */
> +int vc4_hdmi_mode_valid(struct drm_connector *connector,
> +			struct drm_display_mode *mode)
> +{
> +	struct drm_device *dev = connector->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct drm_display_mode *boot_mode = &vc4->hdmi->boot_mode;
> +
> +	if (mode->hdisplay != boot_mode->hdisplay ||
> +	    mode->hsync_start != boot_mode->hsync_start ||
> +	    mode->hsync_end != boot_mode->hsync_end ||
> +	    mode->htotal != boot_mode->htotal) {
> +		return MODE_ERROR;
> +	}
> +
> +	return MODE_OK;
> +}
> +
> +static struct drm_encoder *
> +vc4_hdmi_connector_best_encoder(struct drm_connector *connector)
> +{
> +	struct vc4_hdmi_connector *hdmi_connector =
> +		to_vc4_hdmi_connector(connector);
> +	return hdmi_connector->encoder;
> +}
> +
> +static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
> +	.dpms = drm_atomic_helper_connector_dpms,
> +	.detect = vc4_hdmi_connector_detect,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.destroy = vc4_hdmi_connector_destroy,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = {
> +	.get_modes = vc4_hdmi_connector_get_modes,
> +	.mode_valid = vc4_hdmi_mode_valid,
> +	.best_encoder = vc4_hdmi_connector_best_encoder,
> +};
> +
> +struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
> +					      struct drm_encoder *encoder)
> +{
> +	struct drm_connector *connector = NULL;
> +	struct vc4_hdmi_connector *hdmi_connector;
> +	int ret = 0;
> +
> +	hdmi_connector = devm_kzalloc(dev->dev, sizeof(*hdmi_connector),
> +				      GFP_KERNEL);
> +	if (!hdmi_connector) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +	connector = &hdmi_connector->base;
> +
> +	hdmi_connector->encoder = encoder;
> +
> +	drm_connector_init(dev, connector, &vc4_hdmi_connector_funcs,
> +			   DRM_MODE_CONNECTOR_HDMIA);
> +	drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs);
> +
> +	connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
> +			     DRM_CONNECTOR_POLL_DISCONNECT);
> +
> +	connector->interlace_allowed = 0;
> +	connector->doublescan_allowed = 0;
> +
> +	drm_connector_register(connector);
> +
> +	drm_mode_connector_attach_encoder(connector, encoder);
> +
> +	return connector;
> +
> + fail:
> +	if (connector)
> +		vc4_hdmi_connector_destroy(connector);
> +
> +	return ERR_PTR(ret);
> +}
> +
> +static void vc4_encoder_destroy(struct drm_encoder *encoder)
> +{
> +	drm_encoder_cleanup(encoder);
> +}
> +
> +static const struct drm_encoder_funcs vc4_hdmi_encoder_funcs = {
> +	.destroy = vc4_encoder_destroy,
> +};
> +
> +static bool vc4_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
> +					const struct drm_display_mode *mode,
> +					struct drm_display_mode *adjusted_mode)
> +{
> +	return true;
> +}

As mentioned above, you can drop this one here.

> +
> +static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> +				      struct drm_display_mode *unadjusted_mode,
> +				      struct drm_display_mode *mode)
> +{
> +	struct drm_device *dev = encoder->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	bool debug_dump_regs = false;
> +	bool hsync_pos = !(mode->flags & DRM_MODE_FLAG_NHSYNC);
> +	bool vsync_pos = !(mode->flags & DRM_MODE_FLAG_NVSYNC);
> +	u32 vactive = (mode->vdisplay >>
> +		       ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0));
> +	u32 verta = (VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
> +				   VC4_HDMI_VERTA_VSP) |
> +		     VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
> +				   VC4_HDMI_VERTA_VFP) |
> +		     VC4_SET_FIELD(vactive, VC4_HDMI_VERTA_VAL));
> +	u32 vertb = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) |
> +		     VC4_SET_FIELD(mode->vtotal - mode->vsync_end,
> +				   VC4_HDMI_VERTB_VBP));
> +
> +	if (debug_dump_regs) {
> +		DRM_INFO("HDMI regs before:\n");
> +		vc4_hdmi_dump_regs(dev);
> +	}
> +
> +	HD_WRITE(VC4_HD_VID_CTL, 0);
> +
> +	/* XXX: This is where we would set the HDMI state machine
> +	 * clock, if we had an interface for it.
> +	 */
> +
> +	HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
> +		   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
> +		   VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
> +		   VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS);
> +
> +	HDMI_WRITE(VC4_HDMI_HORZA,
> +		   (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) |
> +		   (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) |
> +		   VC4_SET_FIELD(mode->hdisplay, VC4_HDMI_HORZA_HAP));
> +
> +	HDMI_WRITE(VC4_HDMI_HORZB,
> +		   VC4_SET_FIELD(mode->htotal - mode->hsync_end,
> +				 VC4_HDMI_HORZB_HBP) |
> +		   VC4_SET_FIELD(mode->hsync_end - mode->hsync_start,
> +				 VC4_HDMI_HORZB_HSP) |
> +		   VC4_SET_FIELD(mode->hsync_start - mode->hdisplay,
> +				 VC4_HDMI_HORZB_HFP));
> +
> +	HDMI_WRITE(VC4_HDMI_VERTA0, verta);
> +	HDMI_WRITE(VC4_HDMI_VERTA1, verta);
> +
> +	HDMI_WRITE(VC4_HDMI_VERTB0, vertb);
> +	HDMI_WRITE(VC4_HDMI_VERTB1, vertb);
> +
> +	HD_WRITE(VC4_HD_VID_CTL,
> +		 (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
> +		 (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
> +
> +	/* The RGB order applies even when CSC is disabled. */
> +	HD_WRITE(VC4_HD_CSC_CTL, VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR,
> +					       VC4_HD_CSC_CTL_ORDER));
> +
> +	HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
> +
> +	if (debug_dump_regs) {
> +		DRM_INFO("HDMI regs after:\n");
> +		vc4_hdmi_dump_regs(dev);
> +	}
> +}
> +
> +static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct drm_device *dev = encoder->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +
> +	HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
> +	HD_WRITE(VC4_HD_VID_CTL,
> +		 HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
> +}
> +
> +static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
> +{
> +	struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
> +	struct drm_device *dev = encoder->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +
> +	HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
> +
> +	HD_WRITE(VC4_HD_VID_CTL,
> +		 HD_READ(VC4_HD_VID_CTL) |
> +		 VC4_HD_VID_CTL_ENABLE |
> +		 VC4_HD_VID_CTL_UNDERFLOW_ENABLE |
> +		 VC4_HD_VID_CTL_FRAME_COUNTER_RESET);
> +
> +	if (vc4_encoder->hdmi_monitor) {
> +		HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
> +			   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
> +			   VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
> +
> +		while (!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
> +			 VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE)) {
> +			cpu_relax();
> +		}
> +	} else {
> +		HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
> +			   HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) &
> +			   ~(VC4_HDMI_RAM_PACKET_ENABLE));
> +		HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
> +			   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
> +			   ~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
> +
> +		while (HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
> +		       VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE) {
> +			cpu_relax();
> +		}
> +	}
> +
> +	if (vc4_encoder->hdmi_monitor) {
> +		u32 drift;
> +
> +		WARN_ON(!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
> +			  VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE));
> +		HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
> +			   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
> +			   VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT);
> +
> +		/* XXX: Set HDMI_RAM_PACKET_CONFIG (1 << 16) and set
> +		 * up the infoframe.
> +		 */
> +
> +		drift = HDMI_READ(VC4_HDMI_FIFO_CTL);
> +		drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK;
> +
> +		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
> +			   drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
> +		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
> +			   drift | VC4_HDMI_FIFO_CTL_RECENTER);
> +		udelay(1000);
> +		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
> +			   drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
> +		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
> +			   drift | VC4_HDMI_FIFO_CTL_RECENTER);
> +
> +		while ((HDMI_READ(VC4_HDMI_FIFO_CTL) &
> +			VC4_HDMI_FIFO_CTL_RECENTER_DONE) == 0) {
> +			cpu_relax();
> +		}
> +	}
> +}
> +
> +static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
> +	.mode_fixup = vc4_hdmi_encoder_mode_fixup,
> +	.mode_set = vc4_hdmi_encoder_mode_set,
> +	.disable = vc4_hdmi_encoder_disable,
> +	.enable = vc4_hdmi_encoder_enable,
> +};
> +
> +static struct drm_crtc *
> +vc4_get_crtc_node(struct platform_device *pdev)
> +{
> +	struct device_node *crtc_node;
> +	struct platform_device *crtc_pdev;
> +
> +	crtc_node = of_parse_phandle(pdev->dev.of_node, "crtc", 0);
> +	if (!crtc_node) {
> +		DRM_ERROR("No CRTC for hdmi in DT\n");
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	crtc_pdev = of_find_device_by_node(crtc_node);
> +	if (!crtc_pdev) {
> +		DRM_ERROR("No CRTC device attached to OF node\n");
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	return platform_get_drvdata(crtc_pdev);
> +}
> +
> +struct drm_encoder *vc4_hdmi_encoder_init(struct drm_device *dev)
> +{
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct drm_encoder *encoder = NULL;
> +	struct vc4_hdmi_encoder *vc4_hdmi_encoder;
> +	struct drm_crtc *crtc;
> +	int ret;
> +
> +	vc4_hdmi_encoder = devm_kzalloc(dev->dev, sizeof(*vc4_hdmi_encoder),
> +					GFP_KERNEL);
> +	if (!vc4_hdmi_encoder) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +	encoder = &vc4_hdmi_encoder->base;
> +
> +	crtc = vc4_get_crtc_node(vc4->hdmi->pdev);
> +	if (IS_ERR(crtc)) {
> +		ret = PTR_ERR(crtc);
> +		goto fail;
> +	}
> +
> +	drm_encoder_init(dev, encoder, &vc4_hdmi_encoder_funcs,
> +			 DRM_MODE_ENCODER_TMDS);
> +	drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs);
> +
> +	encoder->possible_crtcs = drm_crtc_mask(crtc);
> +
> +	return encoder;
> +
> +fail:
> +	if (encoder)
> +		vc4_encoder_destroy(encoder);
> +
> +	return ERR_PTR(ret);
> +}
> +
> +/* Reads out the current HDMI mode progreamming at driver load time.
> + *
> + * This is currently used for later filtering out of the DDC-probed
> + * video modes, since we can't actually change modes due to not having
> + * control of the necessary clocks.  Later on, we may end up reusing
> + * this for skipping modesets at boot time.
> + */
> +static void
> +vc4_hdmi_get_boot_display_mode(struct vc4_dev *vc4)
> +{
> +	struct drm_display_mode *mode = &vc4->hdmi->boot_mode;
> +	u32 horza = HDMI_READ(VC4_HDMI_HORZA);
> +	u32 horzb = HDMI_READ(VC4_HDMI_HORZB);
> +	u32 verta = HDMI_READ(VC4_HDMI_VERTA0);
> +	u32 vertb = HDMI_READ(VC4_HDMI_VERTB0);
> +
> +	memset(mode, 0, sizeof(*mode));
> +
> +	if (!(horza & VC4_HDMI_HORZA_VPOS))
> +		mode->flags |= DRM_MODE_FLAG_NVSYNC;
> +	if (!(horza & VC4_HDMI_HORZA_HPOS))
> +		mode->flags |= DRM_MODE_FLAG_NHSYNC;
> +	mode->hdisplay = VC4_GET_FIELD(horza, VC4_HDMI_HORZA_HAP);
> +	mode->hsync_start = (mode->hdisplay +
> +			     VC4_GET_FIELD(horzb, VC4_HDMI_HORZB_HFP));
> +	mode->hsync_end = (mode->hsync_start +
> +			   VC4_GET_FIELD(horzb, VC4_HDMI_HORZB_HSP));
> +	mode->htotal = (mode->hsync_end +
> +			VC4_GET_FIELD(horzb, VC4_HDMI_HORZB_HBP));
> +
> +	mode->vdisplay = VC4_GET_FIELD(verta, VC4_HDMI_VERTA_VAL);
> +	mode->vsync_start = (mode->vdisplay +
> +			     VC4_GET_FIELD(verta, VC4_HDMI_VERTA_VFP));
> +	mode->vsync_end = (mode->vsync_start +
> +			   VC4_GET_FIELD(verta, VC4_HDMI_VERTA_VSP));
> +	mode->vtotal = (mode->vsync_end +
> +			VC4_GET_FIELD(vertb, VC4_HDMI_VERTB_VBP));
> +}
> +
> +static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = dev_get_drvdata(master);
> +	struct vc4_dev *vc4 = drm->dev_private;
> +	struct vc4_hdmi *hdmi;
> +	struct device_node *ddc_node;
> +	u32 value;
> +
> +	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
> +	if (!hdmi)
> +		return -ENOMEM;
> +
> +	hdmi->pdev = pdev;
> +	hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0);
> +	if (IS_ERR(hdmi->hdmicore_regs))
> +		return PTR_ERR(hdmi->hdmicore_regs);
> +
> +	hdmi->hd_regs = vc4_ioremap_regs(pdev, 1);
> +	if (IS_ERR(hdmi->hd_regs))
> +		return PTR_ERR(hdmi->hd_regs);
> +
> +	/* DDC i2c driver */
> +	ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
> +	if (!ddc_node) {
> +		DRM_ERROR("Failed to find ddc node in device tree\n");
> +		return -ENODEV;
> +	}
> +
> +	hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
> +	if (!hdmi->ddc) {
> +		DRM_ERROR("Failed to get ddc i2c adapter by node\n");
> +		return -EPROBE_DEFER;
> +	}
> +
> +	/* Only use the GPIO HPD pin if present in the DT, otherwise
> +	 * we'll use the HDMI core's register.
> +	 */
> +	if (of_find_property(dev->of_node, "hpd-gpio", &value)) {
> +		hdmi->hpd_gpio = of_get_named_gpio(dev->of_node, "hpd-gpio", 0);
> +		if (hdmi->hpd_gpio < 0)
> +			return hdmi->hpd_gpio;
> +	}
> +
> +	vc4->hdmi = hdmi;
> +
> +	/* HDMI core must be enabled. */
> +	WARN_ON_ONCE((HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE) == 0);
> +
> +	vc4_hdmi_get_boot_display_mode(vc4);
> +
> +	return 0;
> +}
> +
> +static void vc4_hdmi_unbind(struct device *dev, struct device *master,
> +			    void *data)
> +{
> +	struct drm_device *drm = dev_get_drvdata(master);
> +	struct vc4_dev *vc4 = drm->dev_private;
> +
> +	put_device(&vc4->hdmi->ddc->dev);
> +
> +	vc4->hdmi = NULL;
> +}
> +
> +static const struct component_ops vc4_hdmi_ops = {
> +	.bind   = vc4_hdmi_bind,
> +	.unbind = vc4_hdmi_unbind,
> +};
> +
> +static int vc4_hdmi_dev_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &vc4_hdmi_ops);
> +}
> +
> +static int vc4_hdmi_dev_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &vc4_hdmi_ops);
> +	return 0;
> +}
> +
> +static const struct of_device_id vc4_hdmi_dt_match[] = {
> +	{ .compatible = "brcm,vc4-hdmi" },
> +	{}
> +};
> +
> +static struct platform_driver vc4_hdmi_driver = {
> +	.probe = vc4_hdmi_dev_probe,
> +	.remove = vc4_hdmi_dev_remove,
> +	.driver = {
> +		.name = "vc4_hdmi",
> +		.of_match_table = vc4_hdmi_dt_match,
> +	},
> +};
> +
> +void __init vc4_hdmi_register(void)
> +{
> +	platform_driver_register(&vc4_hdmi_driver);
> +}
> +
> +void __exit vc4_hdmi_unregister(void)
> +{
> +	platform_driver_unregister(&vc4_hdmi_driver);
> +}
> diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
> new file mode 100644
> index 0000000..dcf4f78
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
> @@ -0,0 +1,172 @@
> +/*
> + * Copyright (C) 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +/**
> + * DOC: VC4 HVS module.
> + *
> + * The HVS is the piece of hardware that does translation, scaling,
> + * colorspace conversion, and compositing of pixels stored in
> + * framebuffers into a FIFO of pixels going out to the Pixel Valve
> + * (CRTC).
> + *
> + * There is a single global HVS, with multiple output FIFOs that can
> + * be consumed by the PVs.  This file just manages the resources for
> + * the HVS, while the vc4_crtc.c code actually drives HVS setup for
> + * each CRTC.
> + */
> +
> +#include "linux/component.h"
> +#include "vc4_drv.h"
> +#include "vc4_regs.h"
> +
> +#define HVS_REG(reg) { reg, #reg }
> +static const struct {
> +	u32 reg;
> +	const char *name;
> +} hvs_regs[] = {
> +	HVS_REG(SCALER_DISPCTRL),
> +	HVS_REG(SCALER_DISPSTAT),
> +	HVS_REG(SCALER_DISPID),
> +	HVS_REG(SCALER_DISPECTRL),
> +	HVS_REG(SCALER_DISPPROF),
> +	HVS_REG(SCALER_DISPDITHER),
> +	HVS_REG(SCALER_DISPEOLN),
> +	HVS_REG(SCALER_DISPLIST0),
> +	HVS_REG(SCALER_DISPLIST1),
> +	HVS_REG(SCALER_DISPLIST2),
> +	HVS_REG(SCALER_DISPLSTAT),
> +	HVS_REG(SCALER_DISPLACT0),
> +	HVS_REG(SCALER_DISPLACT1),
> +	HVS_REG(SCALER_DISPLACT2),
> +	HVS_REG(SCALER_DISPCTRL0),
> +	HVS_REG(SCALER_DISPBKGND0),
> +	HVS_REG(SCALER_DISPSTAT0),
> +	HVS_REG(SCALER_DISPBASE0),
> +	HVS_REG(SCALER_DISPCTRL1),
> +	HVS_REG(SCALER_DISPBKGND1),
> +	HVS_REG(SCALER_DISPSTAT1),
> +	HVS_REG(SCALER_DISPBASE1),
> +	HVS_REG(SCALER_DISPCTRL2),
> +	HVS_REG(SCALER_DISPBKGND2),
> +	HVS_REG(SCALER_DISPSTAT2),
> +	HVS_REG(SCALER_DISPBASE2),
> +	HVS_REG(SCALER_DISPALPHA2),
> +};
> +
> +void
> +vc4_hvs_dump_state(struct drm_device *dev)
> +{
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hvs_regs); i++) {
> +		DRM_INFO("0x%04x (%s): 0x%08x\n",
> +			 hvs_regs[i].reg, hvs_regs[i].name,
> +			 HVS_READ(hvs_regs[i].reg));
> +	}
> +
> +	DRM_INFO("HVS ctx:\n");
> +	for (i = 0; i < 64; i += 4) {
> +		DRM_INFO("0x%08x (%s): 0x%08x 0x%08x 0x%08x 0x%08x\n",
> +			 i * 4, i < HVS_BOOTLOADER_DLIST_END ? "B" : "D",
> +			 ((uint32_t *)vc4->hvs->dlist)[i + 0],
> +			 ((uint32_t *)vc4->hvs->dlist)[i + 1],
> +			 ((uint32_t *)vc4->hvs->dlist)[i + 2],
> +			 ((uint32_t *)vc4->hvs->dlist)[i + 3]);
> +	}
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused)
> +{
> +	struct drm_info_node *node = (struct drm_info_node *) m->private;
> +	struct drm_device *dev = node->minor->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hvs_regs); i++) {
> +		seq_printf(m, "%s (0x%04x): 0x%08x\n",
> +			   hvs_regs[i].name, hvs_regs[i].reg,
> +			   HVS_READ(hvs_regs[i].reg));
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = dev_get_drvdata(master);
> +	struct vc4_dev *vc4 = drm->dev_private;
> +	struct vc4_hvs *hvs = NULL;
> +
> +	hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL);
> +	if (!hvs)
> +		return -ENOMEM;
> +
> +	hvs->pdev = pdev;
> +
> +	hvs->regs = vc4_ioremap_regs(pdev, 0);
> +	if (IS_ERR(hvs->regs))
> +		return PTR_ERR(hvs->regs);
> +
> +	hvs->dlist = hvs->regs + SCALER_DLIST_START;
> +
> +	vc4->hvs = hvs;
> +	return 0;
> +}
> +
> +static void vc4_hvs_unbind(struct device *dev, struct device *master,
> +			   void *data)
> +{
> +	struct drm_device *drm = dev_get_drvdata(master);
> +	struct vc4_dev *vc4 = drm->dev_private;
> +
> +	vc4->hvs = NULL;
> +}
> +
> +static const struct component_ops vc4_hvs_ops = {
> +	.bind   = vc4_hvs_bind,
> +	.unbind = vc4_hvs_unbind,
> +};
> +
> +static int vc4_hvs_dev_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &vc4_hvs_ops);
> +}
> +
> +static int vc4_hvs_dev_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &vc4_hvs_ops);
> +	return 0;
> +}
> +
> +static const struct of_device_id vc4_hvs_dt_match[] = {
> +	{ .compatible = "brcm,vc4-hvs" },
> +	{}
> +};
> +
> +static struct platform_driver vc4_hvs_driver = {
> +	.probe = vc4_hvs_dev_probe,
> +	.remove = vc4_hvs_dev_remove,
> +	.driver = {
> +		.name = "vc4_hvs",
> +		.of_match_table = vc4_hvs_dt_match,
> +	},
> +};
> +
> +void __init vc4_hvs_register(void)
> +{
> +	platform_driver_register(&vc4_hvs_driver);
> +}
> +
> +void __exit vc4_hvs_unregister(void)
> +{
> +	platform_driver_unregister(&vc4_hvs_driver);
> +}
> diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
> new file mode 100644
> index 0000000..e5e96bc
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_kms.c
> @@ -0,0 +1,84 @@
> +/*
> + * Copyright (C) 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +/**
> + * DOC: VC4 KMS
> + *
> + * This is the general code for implementing KMS mode setting that
> + * doesn't clearly associate with any of the other objects (plane,
> + * crtc, HDMI encoder).
> + */
> +
> +#include "drm_crtc.h"
> +#include "drm_atomic_helper.h"
> +#include "drm_crtc_helper.h"
> +#include "drm_plane_helper.h"
> +#include "drm_fb_cma_helper.h"
> +#include "vc4_drv.h"
> +
> +static const struct drm_mode_config_funcs vc4_mode_funcs = {
> +	.atomic_check = drm_atomic_helper_check,
> +	.atomic_commit = drm_atomic_helper_commit,
> +	.fb_create = drm_fb_cma_create,
> +};
> +
> +/* Calls out to initialize all of the VC4 KMS objects. */
> +static int vc4_init_modeset_objects(struct drm_device *dev)
> +{
> +	struct drm_encoder *encoder;
> +	struct drm_connector *connector;
> +	int ret = 0;
> +
> +	encoder = vc4_hdmi_encoder_init(dev);
> +	if (IS_ERR(encoder)) {
> +		dev_err(dev->dev, "failed to construct HDMI encoder\n");
> +		ret = PTR_ERR(encoder);
> +		goto fail;
> +	}
> +
> +	connector = vc4_hdmi_connector_init(dev, encoder);
> +	if (IS_ERR(connector)) {
> +		ret = PTR_ERR(connector);
> +		dev_err(dev->dev, "failed to initialize HDMI connector\n");
> +		goto fail;
> +	}
> +
> +fail:
> +	return ret;
> +}
> +
> +int vc4_kms_load(struct drm_device *dev)
> +{
> +	int ret;
> +
> +	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
> +	if (ret < 0) {
> +		dev_err(dev->dev, "failed to initialize vblank\n");
> +		return ret;
> +	}
> +
> +	dev->mode_config.max_width = 2048;
> +	dev->mode_config.max_height = 2048;
> +	dev->mode_config.funcs = &vc4_mode_funcs;
> +	dev->mode_config.preferred_depth = 24;
> +
> +	ret = vc4_init_modeset_objects(dev);
> +	if (ret)
> +		goto fail;
> +
> +	drm_mode_config_reset(dev);
> +
> +	drm_fbdev_cma_init(dev, 32,
> +			   dev->mode_config.num_crtc,
> +			   dev->mode_config.num_connector);
> +
> +	drm_kms_helper_poll_init(dev);
> +
> +fail:
> +	return ret;
> +}
> diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
> new file mode 100644
> index 0000000..cdd8b10
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_plane.c
> @@ -0,0 +1,320 @@
> +/*
> + * Copyright (C) 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +/**
> + * DOC: VC4 plane module
> + *
> + * Each DRM plane is a layer of pixels being scanned out by the HVS.
> + *
> + * At atomic modeset check time, we compute the HVS display element
> + * state that would be necessary for displaying the plane (giving us a
> + * chance to figure out if a plane configuration is invalid), then at
> + * atomic flush time the CRTC will ask us to write our element state
> + * into the region of the HVS that it has allocated for us.
> + */
> +
> +#include "vc4_drv.h"
> +#include "vc4_regs.h"
> +#include "drm_atomic_helper.h"
> +#include "drm_fb_cma_helper.h"
> +#include "drm_plane_helper.h"
> +
> +struct vc4_plane_state {
> +	struct drm_plane_state base;
> +	u32 *dlist;
> +	u32 dlist_size; /* Number of dwords in allocated for the display list */
> +	u32 dlist_count; /* Number of used dwords in the display list. */
> +};
> +
> +static inline struct vc4_plane_state *
> +to_vc4_plane_state(struct drm_plane_state *state)
> +{
> +	return (struct vc4_plane_state *)state;
> +}
> +
> +static const struct hvs_format {
> +	u32 drm; /* DRM_FORMAT_* */
> +	u32 hvs; /* HVS_FORMAT_* */
> +	u32 pixel_order;
> +	bool has_alpha;
> +} hvs_formats[] = {
> +	{
> +		.drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
> +		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
> +	},
> +	{
> +		.drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
> +		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
> +	},
> +};
> +
> +static const struct hvs_format *vc4_get_hvs_format(u32 drm_format)
> +{
> +	unsigned i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
> +		if (hvs_formats[i].drm == drm_format)
> +			return &hvs_formats[i];
> +	}
> +
> +	return NULL;
> +}
> +
> +static bool plane_enabled(struct drm_plane_state *state)
> +{
> +	return state->fb && state->crtc;
> +}
> +
> +struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
> +{
> +	struct vc4_plane_state *vc4_state;
> +
> +	if (WARN_ON(!plane->state))
> +		return NULL;
> +
> +	vc4_state = kmemdup(plane->state, sizeof(*vc4_state), GFP_KERNEL);
> +	if (!vc4_state)
> +		return NULL;
> +
> +	__drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base);
> +
> +	if (vc4_state->dlist) {
> +		vc4_state->dlist = kmemdup(vc4_state->dlist,
> +					   vc4_state->dlist_count * 4,
> +					   GFP_KERNEL);
> +		if (!vc4_state->dlist) {
> +			kfree(vc4_state);
> +			return NULL;
> +		}
> +		vc4_state->dlist_size = vc4_state->dlist_count;
> +	}
> +
> +	return &vc4_state->base;
> +}
> +
> +void vc4_plane_destroy_state(struct drm_plane *plane,
> +			     struct drm_plane_state *state)
> +{
> +	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
> +
> +	kfree(vc4_state->dlist);
> +	__drm_atomic_helper_plane_destroy_state(plane, &vc4_state->base);
> +	kfree(state);
> +}
> +
> +/* Called during init to allocate the plane's atomic state. */
> +void vc4_plane_reset(struct drm_plane *plane)
> +{
> +	struct vc4_plane_state *vc4_state;
> +
> +	WARN_ON(plane->state);
> +
> +	vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
> +	if (!vc4_state)
> +		return;
> +
> +	plane->state = &vc4_state->base;
> +	vc4_state->base.plane = plane;
> +}
> +
> +static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val)
> +{
> +	if (vc4_state->dlist_count == vc4_state->dlist_size) {
> +		u32 new_size = max(4u, vc4_state->dlist_count * 2);
> +		u32 *new_dlist = kmalloc(new_size * 4, GFP_KERNEL);
> +
> +		if (!new_dlist)
> +			return;
> +		memcpy(new_dlist, vc4_state->dlist, vc4_state->dlist_count * 4);
> +
> +		kfree(vc4_state->dlist);
> +		vc4_state->dlist = new_dlist;
> +		vc4_state->dlist_size = new_size;
> +	}
> +
> +	vc4_state->dlist[vc4_state->dlist_count++] = val;
> +}
> +
> +/* Writes out a full display list for an active plane to the plane's
> + * private dlist state.
> + */
> +static int vc4_plane_mode_set(struct drm_plane *plane,
> +			      struct drm_plane_state *state)
> +{
> +	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
> +	u32 ctl0_offset = vc4_state->dlist_count;
> +	const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format);
> +	uint32_t offset = fb->offsets[0];
> +	int crtc_x = state->crtc_x;
> +	int crtc_y = state->crtc_y;
> +	int crtc_w = state->crtc_w;
> +	int crtc_h = state->crtc_h;
> +
> +	if (crtc_x < 0) {
> +		offset += drm_format_plane_cpp(fb->pixel_format, 0) * -crtc_x;
> +		crtc_w += crtc_x;
> +		crtc_x = 0;
> +	}
> +
> +	if (crtc_y < 0) {
> +		offset += fb->pitches[0] * -crtc_y;
> +		crtc_h += crtc_y;
> +		crtc_y = 0;
> +	}
> +
> +	vc4_dlist_write(vc4_state,
> +			SCALER_CTL0_VALID |
> +			(format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |
> +			(format->hvs << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
> +			SCALER_CTL0_UNITY);
> +
> +	/* Position Word 0: Image Positions and Alpha Value */
> +	vc4_dlist_write(vc4_state,
> +			VC4_SET_FIELD(0xff, SCALER_POS0_FIXED_ALPHA) |
> +			VC4_SET_FIELD(crtc_x, SCALER_POS0_START_X) |
> +			VC4_SET_FIELD(crtc_y, SCALER_POS0_START_Y));
> +
> +	/* Position Word 1: Scaled Image Dimensions.
> +	 * Skipped due to SCALER_CTL0_UNITY scaling.
> +	 */
> +
> +	/* Position Word 2: Source Image Size, Alpha Mode */
> +	vc4_dlist_write(vc4_state,
> +			VC4_SET_FIELD(format->has_alpha ?
> +				      SCALER_POS2_ALPHA_MODE_PIPELINE :
> +				      SCALER_POS2_ALPHA_MODE_FIXED,
> +				      SCALER_POS2_ALPHA_MODE) |
> +			VC4_SET_FIELD(crtc_w, SCALER_POS2_WIDTH) |
> +			VC4_SET_FIELD(crtc_h, SCALER_POS2_HEIGHT));
> +
> +	/* Position Word 3: Context.  Written by the HVS. */
> +	vc4_dlist_write(vc4_state, 0xc0c0c0c0);
> +
> +	/* Pointer Word 0: RGB / Y Pointer */
> +	vc4_dlist_write(vc4_state, bo->paddr + offset);
> +
> +	/* Pointer Context Word 0: Written by the HVS */
> +	vc4_dlist_write(vc4_state, 0xc0c0c0c0);
> +
> +	/* Pitch word 0: Pointer 0 Pitch */
> +	vc4_dlist_write(vc4_state,
> +			VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH));
> +
> +	vc4_state->dlist[ctl0_offset] |=
> +		VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE);
> +
> +	return 0;
> +}
> +
> +/* If a modeset involves changing the setup of a plane, the atomic
> + * infrastructure will call this to validate a proposed plane setup.
> + * However, if a plane isn't getting updated, this (and the
> + * corresponding vc4_plane_atomic_update) won't get called.  Thus, we
> + * compute the dlist here and have all active plane dlists get updated
> + * in the CRTC's flush.
> + */
> +static int vc4_plane_atomic_check(struct drm_plane *plane,
> +				  struct drm_plane_state *state)
> +{
> +	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
> +
> +	vc4_state->dlist_count = 0;
> +
> +	if (plane_enabled(state))
> +		return vc4_plane_mode_set(plane, state);
> +	else
> +		return 0;
> +}
> +
> +static void vc4_plane_atomic_update(struct drm_plane *plane,
> +				    struct drm_plane_state *old_state)
> +{
> +	/* No contents here.  Since we don't know where in the CRTC's
> +	 * dlist we should be stored, our dlist is uploaded to the
> +	 * hardware with vc4_plane_write_dlist() at CRTC atomic_flush
> +	 * time.
> +	 */
> +}
> +
> +u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist)
> +{
> +	struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
> +	int i;
> +
> +	/* Can't memcpy_toio() because it needs to be 32-bit writes. */
> +	for (i = 0; i < vc4_state->dlist_count; i++)
> +		writel(vc4_state->dlist[i], &dlist[i]);
> +
> +	return vc4_state->dlist_count;
> +}
> +
> +u32 vc4_plane_dlist_size(struct drm_plane_state *state)
> +{
> +	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
> +
> +	return vc4_state->dlist_count;
> +}
> +
> +static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
> +	.prepare_fb = NULL,
> +	.cleanup_fb = NULL,
> +	.atomic_check = vc4_plane_atomic_check,
> +	.atomic_update = vc4_plane_atomic_update,
> +};
> +
> +static void vc4_plane_destroy(struct drm_plane *plane)
> +{
> +	drm_plane_helper_disable(plane);
> +	drm_plane_cleanup(plane);
> +}
> +
> +static const struct drm_plane_funcs vc4_plane_funcs = {
> +	.update_plane = drm_atomic_helper_update_plane,
> +	.disable_plane = drm_atomic_helper_disable_plane,
> +	.destroy = vc4_plane_destroy,
> +	.set_property = NULL,
> +	.reset = vc4_plane_reset,
> +	.atomic_duplicate_state = vc4_plane_duplicate_state,
> +	.atomic_destroy_state = vc4_plane_destroy_state,
> +};
> +
> +struct drm_plane *vc4_plane_init(struct drm_device *dev,
> +				 enum drm_plane_type type)
> +{
> +	struct drm_plane *plane = NULL;
> +	struct vc4_plane *vc4_plane;
> +	u32 formats[ARRAY_SIZE(hvs_formats)];
> +	int ret = 0;
> +	unsigned i;
> +
> +	vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
> +				 GFP_KERNEL);
> +	if (!vc4_plane) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++)
> +		formats[i] = hvs_formats[i].drm;
> +	plane = &vc4_plane->base;
> +	ret = drm_universal_plane_init(dev, plane, 0xff,
> +				       &vc4_plane_funcs,
> +				       formats, ARRAY_SIZE(formats),
> +				       type);
> +
> +	drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
> +
> +	return plane;
> +fail:
> +	if (plane)
> +		vc4_plane_destroy(plane);
> +
> +	return ERR_PTR(ret);
> +}
> diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h
> new file mode 100644
> index 0000000..0eff631
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_regs.h
> @@ -0,0 +1,562 @@
> +/*
> + *  Copyright ? 2014-2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#define VC4_MASK(high, low) (((1 << ((high) - (low) + 1)) - 1) << (low))
> +/* Using the GNU statement expression extension */
> +#define VC4_SET_FIELD(value, field)					\
> +	({								\
> +		uint32_t fieldval = (value) << field##_SHIFT;		\
> +		WARN_ON((fieldval & ~field##_MASK) != 0);		\
> +		fieldval & field##_MASK;				\
> +	 })
> +
> +#define VC4_GET_FIELD(word, field) (((word) & field##_MASK) >>		\
> +				    field##_SHIFT)
> +
> +#define V3D_IDENT0   0x00000
> +# define V3D_EXPECTED_IDENT0 \
> +	((2 << 24) | \
> +	('V' << 0) | \
> +	('3' << 8) | \
> +	 ('D' << 16))
> +
> +#define V3D_IDENT1   0x00004
> +/* Multiples of 1kb */
> +# define V3D_IDENT1_VPM_SIZE_MASK                      VC4_MASK(31, 28)
> +# define V3D_IDENT1_VPM_SIZE_SHIFT                     28
> +# define V3D_IDENT1_NSEM_MASK                          VC4_MASK(23, 16)
> +# define V3D_IDENT1_NSEM_SHIFT                         16
> +# define V3D_IDENT1_TUPS_MASK                          VC4_MASK(15, 12)
> +# define V3D_IDENT1_TUPS_SHIFT                         12
> +# define V3D_IDENT1_QUPS_MASK                          VC4_MASK(11, 8)
> +# define V3D_IDENT1_QUPS_SHIFT                         8
> +# define V3D_IDENT1_NSLC_MASK                          VC4_MASK(7, 4)
> +# define V3D_IDENT1_NSLC_SHIFT                         4
> +# define V3D_IDENT1_REV_MASK                           VC4_MASK(3, 0)
> +# define V3D_IDENT1_REV_SHIFT                          0
> +
> +#define V3D_IDENT2   0x00008
> +#define V3D_SCRATCH  0x00010
> +#define V3D_L2CACTL  0x00020
> +# define V3D_L2CACTL_L2CCLR                            (1 << 2)
> +# define V3D_L2CACTL_L2CDIS                            (1 << 1)
> +# define V3D_L2CACTL_L2CENA                            (1 << 0)
> +
> +#define V3D_SLCACTL  0x00024
> +# define V3D_SLCACTL_T1CC_MASK                         VC4_MASK(27, 24)
> +# define V3D_SLCACTL_T1CC_SHIFT                        24
> +# define V3D_SLCACTL_T0CC_MASK                         VC4_MASK(19, 16)
> +# define V3D_SLCACTL_T0CC_SHIFT                        16
> +# define V3D_SLCACTL_UCC_MASK                          VC4_MASK(11, 8)
> +# define V3D_SLCACTL_UCC_SHIFT                         8
> +# define V3D_SLCACTL_ICC_MASK                          VC4_MASK(3, 0)
> +# define V3D_SLCACTL_ICC_SHIFT                         0
> +
> +#define V3D_INTCTL   0x00030
> +#define V3D_INTENA   0x00034
> +#define V3D_INTDIS   0x00038
> +# define V3D_INT_SPILLUSE                              (1 << 3)
> +# define V3D_INT_OUTOMEM                               (1 << 2)
> +# define V3D_INT_FLDONE                                (1 << 1)
> +# define V3D_INT_FRDONE                                (1 << 0)
> +
> +#define V3D_CT0CS    0x00100
> +#define V3D_CT1CS    0x00104
> +#define V3D_CTNCS(n) (V3D_CT0CS + 4 * n)
> +# define V3D_CTRSTA      (1 << 15)
> +# define V3D_CTSEMA      (1 << 12)
> +# define V3D_CTRTSD      (1 << 8)
> +# define V3D_CTRUN       (1 << 5)
> +# define V3D_CTSUBS      (1 << 4)
> +# define V3D_CTERR       (1 << 3)
> +# define V3D_CTMODE      (1 << 0)
> +
> +#define V3D_CT0EA    0x00108
> +#define V3D_CT1EA    0x0010c
> +#define V3D_CTNEA(n) (V3D_CT0EA + 4 * (n))
> +#define V3D_CT0CA    0x00110
> +#define V3D_CT1CA    0x00114
> +#define V3D_CTNCA(n) (V3D_CT0CA + 4 * (n))
> +#define V3D_CT00RA0  0x00118
> +#define V3D_CT01RA0  0x0011c
> +#define V3D_CTNRA0(n) (V3D_CT00RA0 + 4 * (n))
> +#define V3D_CT0LC    0x00120
> +#define V3D_CT1LC    0x00124
> +#define V3D_CTNLC(n) (V3D_CT0LC + 4 * (n))
> +#define V3D_CT0PC    0x00128
> +#define V3D_CT1PC    0x0012c
> +#define V3D_CTNPC(n) (V3D_CT0PC + 4 * (n))
> +
> +#define V3D_PCS      0x00130
> +# define V3D_BMOOM       (1 << 8)
> +# define V3D_RMBUSY      (1 << 3)
> +# define V3D_RMACTIVE    (1 << 2)
> +# define V3D_BMBUSY      (1 << 1)
> +# define V3D_BMACTIVE    (1 << 0)
> +
> +#define V3D_BFC      0x00134
> +#define V3D_RFC      0x00138
> +#define V3D_BPCA     0x00300
> +#define V3D_BPCS     0x00304
> +#define V3D_BPOA     0x00308
> +#define V3D_BPOS     0x0030c
> +#define V3D_BXCF     0x00310
> +#define V3D_SQRSV0   0x00410
> +#define V3D_SQRSV1   0x00414
> +#define V3D_SQCNTL   0x00418
> +#define V3D_SRQPC    0x00430
> +#define V3D_SRQUA    0x00434
> +#define V3D_SRQUL    0x00438
> +#define V3D_SRQCS    0x0043c
> +#define V3D_VPACNTL  0x00500
> +#define V3D_VPMBASE  0x00504
> +#define V3D_PCTRC    0x00670
> +#define V3D_PCTRE    0x00674
> +#define V3D_PCTR0    0x00680
> +#define V3D_PCTRS0   0x00684
> +#define V3D_PCTR1    0x00688
> +#define V3D_PCTRS1   0x0068c
> +#define V3D_PCTR2    0x00690
> +#define V3D_PCTRS2   0x00694
> +#define V3D_PCTR3    0x00698
> +#define V3D_PCTRS3   0x0069c
> +#define V3D_PCTR4    0x006a0
> +#define V3D_PCTRS4   0x006a4
> +#define V3D_PCTR5    0x006a8
> +#define V3D_PCTRS5   0x006ac
> +#define V3D_PCTR6    0x006b0
> +#define V3D_PCTRS6   0x006b4
> +#define V3D_PCTR7    0x006b8
> +#define V3D_PCTRS7   0x006bc
> +#define V3D_PCTR8    0x006c0
> +#define V3D_PCTRS8   0x006c4
> +#define V3D_PCTR9    0x006c8
> +#define V3D_PCTRS9   0x006cc
> +#define V3D_PCTR10   0x006d0
> +#define V3D_PCTRS10  0x006d4
> +#define V3D_PCTR11   0x006d8
> +#define V3D_PCTRS11  0x006dc
> +#define V3D_PCTR12   0x006e0
> +#define V3D_PCTRS12  0x006e4
> +#define V3D_PCTR13   0x006e8
> +#define V3D_PCTRS13  0x006ec
> +#define V3D_PCTR14   0x006f0
> +#define V3D_PCTRS14  0x006f4
> +#define V3D_PCTR15   0x006f8
> +#define V3D_PCTRS15  0x006fc
> +#define V3D_BGE      0x00f00
> +#define V3D_FDBGO    0x00f04
> +#define V3D_FDBGB    0x00f08
> +#define V3D_FDBGR    0x00f0c
> +#define V3D_FDBGS    0x00f10
> +#define V3D_ERRSTAT  0x00f20
> +
> +#define PV_CONTROL				0x00
> +# define PV_CONTROL_FORMAT_MASK			VC4_MASK(23, 21)
> +# define PV_CONTROL_FORMAT_SHIFT		21
> +# define PV_CONTROL_FORMAT_24			0
> +# define PV_CONTROL_FORMAT_DSIV_16		1
> +# define PV_CONTROL_FORMAT_DSIC_16		2
> +# define PV_CONTROL_FORMAT_DSIV_18		3
> +# define PV_CONTROL_FORMAT_DSIV_24		4
> +
> +# define PV_CONTROL_FIFO_LEVEL_MASK		VC4_MASK(20, 15)
> +# define PV_CONTROL_FIFO_LEVEL_SHIFT		15
> +# define PV_CONTROL_CLR_AT_START		(1 << 14)
> +# define PV_CONTROL_TRIGGER_UNDERFLOW		(1 << 13)
> +# define PV_CONTROL_WAIT_HSTART			(1 << 12)
> +# define PV_CONTROL_CLK_SELECT_DSI		0
> +# define PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI	1
> +# define PV_CONTROL_CLK_SELECT_MASK		VC4_MASK(3, 2)
> +# define PV_CONTROL_CLK_SELECT_SHIFT		2
> +# define PV_CONTROL_FIFO_CLR			(1 << 1)
> +# define PV_CONTROL_EN				(1 << 0)
> +
> +#define PV_V_CONTROL				0x04
> +# define PV_VCONTROL_CONTINUOUS			(1 << 1)
> +# define PV_VCONTROL_VIDEN			(1 << 0)
> +
> +#define PV_VSYNCD				0x08
> +
> +#define PV_HORZA				0x0c
> +# define PV_HORZA_HBP_MASK			VC4_MASK(31, 16)
> +# define PV_HORZA_HBP_SHIFT			16
> +# define PV_HORZA_HSYNC_MASK			VC4_MASK(15, 0)
> +# define PV_HORZA_HSYNC_SHIFT			0
> +
> +#define PV_HORZB				0x10
> +# define PV_HORZB_HFP_MASK			VC4_MASK(31, 16)
> +# define PV_HORZB_HFP_SHIFT			16
> +# define PV_HORZB_HACTIVE_MASK			VC4_MASK(15, 0)
> +# define PV_HORZB_HACTIVE_SHIFT			0
> +
> +#define PV_VERTA				0x14
> +# define PV_VERTA_VBP_MASK			VC4_MASK(31, 16)
> +# define PV_VERTA_VBP_SHIFT			16
> +# define PV_VERTA_VSYNC_MASK			VC4_MASK(15, 0)
> +# define PV_VERTA_VSYNC_SHIFT			0
> +
> +#define PV_VERTB				0x18
> +# define PV_VERTB_VFP_MASK			VC4_MASK(31, 16)
> +# define PV_VERTB_VFP_SHIFT			16
> +# define PV_VERTB_VACTIVE_MASK			VC4_MASK(15, 0)
> +# define PV_VERTB_VACTIVE_SHIFT			0
> +
> +#define PV_VERTA_EVEN				0x1c
> +#define PV_VERTB_EVEN				0x20
> +
> +#define PV_INTEN				0x24
> +#define PV_INTSTAT				0x28
> +# define PV_INT_VID_IDLE			(1 << 9)
> +# define PV_INT_VFP_END				(1 << 8)
> +# define PV_INT_VFP_START			(1 << 7)
> +# define PV_INT_VACT_START			(1 << 6)
> +# define PV_INT_VBP_START			(1 << 5)
> +# define PV_INT_VSYNC_START			(1 << 4)
> +# define PV_INT_HFP_START			(1 << 3)
> +# define PV_INT_HACT_START			(1 << 2)
> +# define PV_INT_HBP_START			(1 << 1)
> +# define PV_INT_HSYNC_START			(1 << 0)
> +
> +#define PV_STAT					0x2c
> +
> +#define PV_HACT_ACT				0x30
> +
> +#define SCALER_DISPCTRL                         0x00000000
> +/* Global register for clock gating the HVS */
> +# define SCALER_DISPCTRL_ENABLE			(1 << 31)
> +# define SCALER_DISPCTRL_DSP2EISLUR		(1 << 15)
> +# define SCALER_DISPCTRL_DSP1EISLUR		(1 << 14)
> +/* Enables Display 0 short line and underrun contribution to
> + * SCALER_DISPSTAT_IRQDISP0.  Note that short frame contributions are
> + * always enabled.
> + */
> +# define SCALER_DISPCTRL_DSP0EISLUR		(1 << 13)
> +# define SCALER_DISPCTRL_DSP2EIEOLN		(1 << 12)
> +# define SCALER_DISPCTRL_DSP2EIEOF		(1 << 11)
> +# define SCALER_DISPCTRL_DSP1EIEOLN		(1 << 10)
> +# define SCALER_DISPCTRL_DSP1EIEOF		(1 << 9)
> +/* Enables Display 0 end-of-line-N contribution to
> + * SCALER_DISPSTAT_IRQDISP0
> + */
> +# define SCALER_DISPCTRL_DSP0EIEOLN		(1 << 8)
> +/* Enables Display 0 EOF contribution to SCALER_DISPSTAT_IRQDISP0 */
> +# define SCALER_DISPCTRL_DSP0EIEOF		(1 << 7)
> +
> +# define SCALER_DISPCTRL_SLVRDEIRQ		(1 << 6)
> +# define SCALER_DISPCTRL_SLVWREIRQ		(1 << 5)
> +# define SCALER_DISPCTRL_DMAEIRQ		(1 << 4)
> +# define SCALER_DISPCTRL_DISP2EIRQ		(1 << 3)
> +# define SCALER_DISPCTRL_DISP1EIRQ		(1 << 2)
> +/* Enables interrupt generation on the enabled EOF/EOLN/EISLUR
> + * bits and short frames..
> + */
> +# define SCALER_DISPCTRL_DISP0EIRQ		(1 << 1)
> +/* Enables interrupt generation on scaler profiler interrupt. */
> +# define SCALER_DISPCTRL_SCLEIRQ		(1 << 0)
> +
> +#define SCALER_DISPSTAT                         0x00000004
> +# define SCALER_DISPSTAT_COBLOW2		(1 << 29)
> +# define SCALER_DISPSTAT_EOLN2			(1 << 28)
> +# define SCALER_DISPSTAT_ESFRAME2		(1 << 27)
> +# define SCALER_DISPSTAT_ESLINE2		(1 << 26)
> +# define SCALER_DISPSTAT_EUFLOW2		(1 << 25)
> +# define SCALER_DISPSTAT_EOF2			(1 << 24)
> +
> +# define SCALER_DISPSTAT_COBLOW1		(1 << 21)
> +# define SCALER_DISPSTAT_EOLN1			(1 << 20)
> +# define SCALER_DISPSTAT_ESFRAME1		(1 << 19)
> +# define SCALER_DISPSTAT_ESLINE1		(1 << 18)
> +# define SCALER_DISPSTAT_EUFLOW1		(1 << 17)
> +# define SCALER_DISPSTAT_EOF1			(1 << 16)
> +
> +# define SCALER_DISPSTAT_RESP_MASK		VC4_MASK(15, 14)
> +# define SCALER_DISPSTAT_RESP_SHIFT		14
> +# define SCALER_DISPSTAT_RESP_OKAY		0
> +# define SCALER_DISPSTAT_RESP_EXOKAY		1
> +# define SCALER_DISPSTAT_RESP_SLVERR		2
> +# define SCALER_DISPSTAT_RESP_DECERR		3
> +
> +# define SCALER_DISPSTAT_COBLOW0		(1 << 13)
> +/* Set when the DISPEOLN line is done compositing. */
> +# define SCALER_DISPSTAT_EOLN0			(1 << 12)
> +/* Set when VSTART is seen but there are still pixels in the current
> + * output line.
> + */
> +# define SCALER_DISPSTAT_ESFRAME0		(1 << 11)
> +/* Set when HSTART is seen but there are still pixels in the current
> + * output line.
> + */
> +# define SCALER_DISPSTAT_ESLINE0		(1 << 10)
> +/* Set when the the downstream tries to read from the display FIFO
> + * while it's empty.
> + */
> +# define SCALER_DISPSTAT_EUFLOW0		(1 << 9)
> +/* Set when the display mode changes from RUN to EOF */
> +# define SCALER_DISPSTAT_EOF0			(1 << 8)
> +
> +/* Set on AXI invalid DMA ID error. */
> +# define SCALER_DISPSTAT_DMA_ERROR		(1 << 7)
> +/* Set on AXI slave read decode error */
> +# define SCALER_DISPSTAT_IRQSLVRD		(1 << 6)
> +/* Set on AXI slave write decode error */
> +# define SCALER_DISPSTAT_IRQSLVWR		(1 << 5)
> +/* Set when SCALER_DISPSTAT_DMA_ERROR is set, or
> + * SCALER_DISPSTAT_RESP_ERROR is not SCALER_DISPSTAT_RESP_OKAY.
> + */
> +# define SCALER_DISPSTAT_IRQDMA			(1 << 4)
> +# define SCALER_DISPSTAT_IRQDISP2		(1 << 3)
> +# define SCALER_DISPSTAT_IRQDISP1		(1 << 2)
> +/* Set when any of the EOF/EOLN/ESFRAME/ESLINE bits are set and their
> + * corresponding interrupt bit is enabled in DISPCTRL.
> + */
> +# define SCALER_DISPSTAT_IRQDISP0		(1 << 1)
> +/* On read, the profiler interrupt.  On write, clear *all* interrupt bits. */
> +# define SCALER_DISPSTAT_IRQSCL			(1 << 0)
> +
> +#define SCALER_DISPID                           0x00000008
> +#define SCALER_DISPECTRL                        0x0000000c
> +#define SCALER_DISPPROF                         0x00000010
> +#define SCALER_DISPDITHER                       0x00000014
> +#define SCALER_DISPEOLN                         0x00000018
> +#define SCALER_DISPLIST0                        0x00000020
> +#define SCALER_DISPLIST1                        0x00000024
> +#define SCALER_DISPLIST2                        0x00000028
> +#define SCALER_DISPLSTAT                        0x0000002c
> +#define SCALER_DISPLISTX(x)			(SCALER_DISPLIST0 +	\
> +						 (x) * (SCALER_DISPLIST1 - \
> +							SCALER_DISPLIST0))
> +
> +#define SCALER_DISPLACT0                        0x00000030
> +#define SCALER_DISPLACT1                        0x00000034
> +#define SCALER_DISPLACT2                        0x00000038
> +#define SCALER_DISPCTRL0                        0x00000040
> +# define SCALER_DISPCTRLX_ENABLE		(1 << 31)
> +# define SCALER_DISPCTRLX_RESET			(1 << 30)
> +# define SCALER_DISPCTRLX_WIDTH_MASK		VC4_MASK(23, 12)
> +# define SCALER_DISPCTRLX_WIDTH_SHIFT		12
> +# define SCALER_DISPCTRLX_HEIGHT_MASK		VC4_MASK(11, 0)
> +# define SCALER_DISPCTRLX_HEIGHT_SHIFT		0
> +
> +#define SCALER_DISPBKGND0                       0x00000044
> +#define SCALER_DISPSTAT0                        0x00000048
> +#define SCALER_DISPBASE0                        0x0000004c
> +# define SCALER_DISPSTATX_MODE_MASK		VC4_MASK(31, 30)
> +# define SCALER_DISPSTATX_MODE_SHIFT		30
> +# define SCALER_DISPSTATX_MODE_DISABLED		0
> +# define SCALER_DISPSTATX_MODE_INIT		1
> +# define SCALER_DISPSTATX_MODE_RUN		2
> +# define SCALER_DISPSTATX_MODE_EOF		3
> +# define SCALER_DISPSTATX_FULL			(1 << 29)
> +# define SCALER_DISPSTATX_EMPTY			(1 << 28)
> +#define SCALER_DISPCTRL1                        0x00000050
> +#define SCALER_DISPBKGND1                       0x00000054
> +#define SCALER_DISPSTAT1                        0x00000058
> +#define SCALER_DISPSTATX(x)			(SCALER_DISPSTAT0 +        \
> +						 (x) * (SCALER_DISPSTAT1 - \
> +							SCALER_DISPSTAT0))
> +#define SCALER_DISPBASE1                        0x0000005c
> +#define SCALER_DISPCTRL2                        0x00000060
> +#define SCALER_DISPCTRLX(x)			(SCALER_DISPCTRL0 +        \
> +						 (x) * (SCALER_DISPCTRL1 - \
> +							SCALER_DISPCTRL0))
> +#define SCALER_DISPBKGND2                       0x00000064
> +#define SCALER_DISPSTAT2                        0x00000068
> +#define SCALER_DISPBASE2                        0x0000006c
> +#define SCALER_DISPALPHA2                       0x00000070
> +#define SCALER_GAMADDR                          0x00000078
> +#define SCALER_GAMDATA                          0x000000e0
> +#define SCALER_DLIST_START                      0x00002000
> +#define SCALER_DLIST_SIZE                       0x00004000
> +
> +#define VC4_HDMI_CORE_REV			0x000
> +
> +#define VC4_HDMI_SW_RESET_CONTROL		0x004
> +# define VC4_HDMI_SW_RESET_FORMAT_DETECT	(1 << 1)
> +# define VC4_HDMI_SW_RESET_HDMI			(1 << 0)
> +
> +#define VC4_HDMI_HOTPLUG_INT			0x008
> +
> +#define VC4_HDMI_HOTPLUG			0x00c
> +# define VC4_HDMI_HOTPLUG_CONNECTED		(1 << 0)
> +
> +#define VC4_HDMI_RAM_PACKET_CONFIG		0x0a0
> +# define VC4_HDMI_RAM_PACKET_ENABLE		(1 << 16)
> +
> +#define VC4_HDMI_HORZA				0x0c4
> +# define VC4_HDMI_HORZA_VPOS			(1 << 14)
> +# define VC4_HDMI_HORZA_HPOS			(1 << 13)
> +/* Horizontal active pixels (hdisplay). */
> +# define VC4_HDMI_HORZA_HAP_MASK		VC4_MASK(12, 0)
> +# define VC4_HDMI_HORZA_HAP_SHIFT		0
> +
> +#define VC4_HDMI_HORZB				0x0c8
> +/* Horizontal pack porch (htotal - hsync_end). */
> +# define VC4_HDMI_HORZB_HBP_MASK		VC4_MASK(29, 20)
> +# define VC4_HDMI_HORZB_HBP_SHIFT		20
> +/* Horizontal sync pulse (hsync_end - hsync_start). */
> +# define VC4_HDMI_HORZB_HSP_MASK		VC4_MASK(19, 10)
> +# define VC4_HDMI_HORZB_HSP_SHIFT		10
> +/* Horizontal front porch (hsync_start - hdisplay). */
> +# define VC4_HDMI_HORZB_HFP_MASK		VC4_MASK(9, 0)
> +# define VC4_HDMI_HORZB_HFP_SHIFT		0
> +
> +#define VC4_HDMI_FIFO_CTL			0x05c
> +# define VC4_HDMI_FIFO_CTL_RECENTER_DONE	(1 << 14)
> +# define VC4_HDMI_FIFO_CTL_USE_EMPTY		(1 << 13)
> +# define VC4_HDMI_FIFO_CTL_ON_VB		(1 << 7)
> +# define VC4_HDMI_FIFO_CTL_RECENTER		(1 << 6)
> +# define VC4_HDMI_FIFO_CTL_FIFO_RESET		(1 << 5)
> +# define VC4_HDMI_FIFO_CTL_USE_PLL_LOCK		(1 << 4)
> +# define VC4_HDMI_FIFO_CTL_INV_CLK_XFR		(1 << 3)
> +# define VC4_HDMI_FIFO_CTL_CAPTURE_PTR		(1 << 2)
> +# define VC4_HDMI_FIFO_CTL_USE_FULL		(1 << 1)
> +# define VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N	(1 << 0)
> +# define VC4_HDMI_FIFO_VALID_WRITE_MASK		0xefff
> +
> +#define VC4_HDMI_SCHEDULER_CONTROL		0x0c0
> +# define VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT (1 << 15)
> +# define VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS (1 << 5)
> +# define VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT	(1 << 3)
> +# define VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE	(1 << 1)
> +# define VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI	(1 << 0)
> +
> +#define VC4_HDMI_VERTA0				0x0cc
> +#define VC4_HDMI_VERTA1				0x0d4
> +/* Vertical sync pulse (vsync_end - vsync_start). */
> +# define VC4_HDMI_VERTA_VSP_MASK		VC4_MASK(24, 20)
> +# define VC4_HDMI_VERTA_VSP_SHIFT		20
> +/* Vertical front porch (vsync_start - vdisplay). */
> +# define VC4_HDMI_VERTA_VFP_MASK		VC4_MASK(19, 13)
> +# define VC4_HDMI_VERTA_VFP_SHIFT		13
> +/* Vertical active lines (vdisplay). */
> +# define VC4_HDMI_VERTA_VAL_MASK		VC4_MASK(12, 0)
> +# define VC4_HDMI_VERTA_VAL_SHIFT		0
> +
> +#define VC4_HDMI_VERTB0				0x0d0
> +#define VC4_HDMI_VERTB1				0x0d8
> +/* Vertical sync pulse offset (for interlaced) */
> +# define VC4_HDMI_VERTB_VSPO_MASK		VC4_MASK(21, 9)
> +# define VC4_HDMI_VERTB_VSPO_SHIFT		9
> +/* Vertical pack porch (vtotal - vsync_end). */
> +# define VC4_HDMI_VERTB_VBP_MASK		VC4_MASK(8, 0)
> +# define VC4_HDMI_VERTB_VBP_SHIFT		0
> +
> +#define VC4_HDMI_TX_PHY_RESET_CTL		0x2c0
> +
> +#define VC4_HD_M_CTL				0x00c
> +# define VC4_HD_M_SW_RST			(1 << 2)
> +# define VC4_HD_M_ENABLE			(1 << 0)
> +
> +#define VC4_HD_MAI_CTL				0x014
> +
> +#define VC4_HD_VID_CTL				0x038
> +# define VC4_HD_VID_CTL_ENABLE			(1 << 31)
> +# define VC4_HD_VID_CTL_UNDERFLOW_ENABLE	(1 << 30)
> +# define VC4_HD_VID_CTL_FRAME_COUNTER_RESET	(1 << 29)
> +# define VC4_HD_VID_CTL_VSYNC_LOW		(1 << 28)
> +# define VC4_HD_VID_CTL_HSYNC_LOW		(1 << 27)
> +
> +#define VC4_HD_CSC_CTL				0x040
> +# define VC4_HD_CSC_CTL_ORDER_MASK		VC4_MASK(7, 5)
> +# define VC4_HD_CSC_CTL_ORDER_SHIFT		5
> +# define VC4_HD_CSC_CTL_ORDER_RGB		0
> +# define VC4_HD_CSC_CTL_ORDER_BGR		1
> +# define VC4_HD_CSC_CTL_ORDER_BRG		2
> +# define VC4_HD_CSC_CTL_ORDER_GRB		3
> +# define VC4_HD_CSC_CTL_ORDER_GBR		4
> +# define VC4_HD_CSC_CTL_ORDER_RBG		5
> +# define VC4_HD_CSC_CTL_PADMSB			(1 << 4)
> +# define VC4_HD_CSC_CTL_MODE_MASK		VC4_MASK(3, 2)
> +# define VC4_HD_CSC_CTL_MODE_SHIFT		2
> +# define VC4_HD_CSC_CTL_MODE_RGB_TO_SD_YPRPB	0
> +# define VC4_HD_CSC_CTL_MODE_RGB_TO_HD_YPRPB	1
> +# define VC4_HD_CSC_CTL_MODE_CUSTOM		2
> +# define VC4_HD_CSC_CTL_RGB2YCC			(1 << 1)
> +# define VC4_HD_CSC_CTL_ENABLE			(1 << 0)
> +
> +#define VC4_HD_FRAME_COUNT			0x068
> +
> +/* HVS display list information. */
> +#define HVS_BOOTLOADER_DLIST_END                32
> +
> +enum hvs_pixel_format {
> +	/* 8bpp */
> +	HVS_PIXEL_FORMAT_RGB332 = 0,
> +	/* 16bpp */
> +	HVS_PIXEL_FORMAT_RGBA4444 = 1,
> +	HVS_PIXEL_FORMAT_RGB555 = 2,
> +	HVS_PIXEL_FORMAT_RGBA5551 = 3,
> +	HVS_PIXEL_FORMAT_RGB565 = 4,
> +	/* 24bpp */
> +	HVS_PIXEL_FORMAT_RGB888 = 5,
> +	HVS_PIXEL_FORMAT_RGBA6666 = 6,
> +	/* 32bpp */
> +	HVS_PIXEL_FORMAT_RGBA8888 = 7
> +};
> +
> +/* Note: the LSB is the rightmost character shown.  Only valid for
> + * HVS_PIXEL_FORMAT_RGB8888, not RGB888.
> + */
> +#define HVS_PIXEL_ORDER_RGBA			0
> +#define HVS_PIXEL_ORDER_BGRA			1
> +#define HVS_PIXEL_ORDER_ARGB			2
> +#define HVS_PIXEL_ORDER_ABGR			3
> +
> +#define HVS_PIXEL_ORDER_XBRG			0
> +#define HVS_PIXEL_ORDER_XRBG			1
> +#define HVS_PIXEL_ORDER_XRGB			2
> +#define HVS_PIXEL_ORDER_XBGR			3
> +
> +#define HVS_PIXEL_ORDER_XYCBCR			0
> +#define HVS_PIXEL_ORDER_XYCRCB			1
> +#define HVS_PIXEL_ORDER_YXCBCR			2
> +#define HVS_PIXEL_ORDER_YXCRCB			3
> +
> +#define SCALER_CTL0_END				(1 << 31)
> +#define SCALER_CTL0_VALID			(1 << 30)
> +
> +#define SCALER_CTL0_SIZE_MASK			VC4_MASK(29, 24)
> +#define SCALER_CTL0_SIZE_SHIFT			24
> +
> +#define SCALER_CTL0_HFLIP                       (1 << 16)
> +#define SCALER_CTL0_VFLIP                       (1 << 15)
> +
> +#define SCALER_CTL0_ORDER_MASK			VC4_MASK(14, 13)
> +#define SCALER_CTL0_ORDER_SHIFT			13
> +
> +/* Set to indicate no scaling. */
> +#define SCALER_CTL0_UNITY			(1 << 4)
> +
> +#define SCALER_CTL0_PIXEL_FORMAT_MASK		VC4_MASK(3, 0)
> +#define SCALER_CTL0_PIXEL_FORMAT_SHIFT		0
> +
> +#define SCALER_POS0_FIXED_ALPHA_MASK		VC4_MASK(31, 24)
> +#define SCALER_POS0_FIXED_ALPHA_SHIFT		24
> +
> +#define SCALER_POS0_START_Y_MASK		VC4_MASK(23, 12)
> +#define SCALER_POS0_START_Y_SHIFT		12
> +
> +#define SCALER_POS0_START_X_MASK		VC4_MASK(11, 0)
> +#define SCALER_POS0_START_X_SHIFT		0
> +
> +#define SCALER_POS2_ALPHA_MODE_MASK		VC4_MASK(31, 30)
> +#define SCALER_POS2_ALPHA_MODE_SHIFT		30
> +#define SCALER_POS2_ALPHA_MODE_PIPELINE		0
> +#define SCALER_POS2_ALPHA_MODE_FIXED		1
> +#define SCALER_POS2_ALPHA_MODE_FIXED_NONZERO	2
> +#define SCALER_POS2_ALPHA_MODE_FIXED_OVER_0x07	3
> +
> +#define SCALER_POS2_HEIGHT_MASK			VC4_MASK(27, 16)
> +#define SCALER_POS2_HEIGHT_SHIFT		16
> +
> +#define SCALER_POS2_WIDTH_MASK			VC4_MASK(11, 0)
> +#define SCALER_POS2_WIDTH_SHIFT			0
> +
> +#define SCALER_SRC_PITCH_MASK			VC4_MASK(15, 0)
> +#define SCALER_SRC_PITCH_SHIFT			0
> -- 
> 2.1.4
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From olof at lixom.net  Thu Aug 13 03:30:56 2015
From: olof at lixom.net (Olof Johansson)
Date: Thu, 13 Aug 2015 12:30:56 +0200
Subject: [GIT PULL] RPi DT changes due for v4.3
In-Reply-To: <20150811160642.GQ18282@x1>
References: <20150811160642.GQ18282@x1>
Message-ID: <20150813103056.GY30160@localhost>

Hi,

On Tue, Aug 11, 2015 at 05:06:42PM +0100, Lee Jones wrote:
> ARM SoC Chaps,
> 
> Please find a couple of simple RPi changes pertaining to Firmware.
> 
> The following changes since commit bc0195aad0daa2ad5b0d76cce22b167bc3435590:
> 
>   Linux 4.2-rc2 (2015-07-12 15:10:30 -0700)
> 
> are available in the git repository at:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git tags/rpi-dt-for-armsoc-4.3
> 
> for you to fetch changes up to fd26f8830979de48eb3f1c253eb9d2ee2e468eb6:
> 
>   dt/bindings: Add binding for the Raspberry Pi firmware driver (2015-08-11 16:56:41 +0100)

I've merged this but I'm a little confused about bcm2835 maintainership:
I got a MAINTAINERS update from Florian earlier grouped in a set of
other bcm changes (adding Eric), and now this directly from you.

In general the bcm platforms are quite diverse and we've asked Florian
to be the funnel for all of the new ones, but I don't think we talked
much about 2835 at that time. Should we expect to see merges directly
from you and the other maintainers there or will it go through Florian?

Either way is OK with me, just looking to clarify.


Thanks!

-Olof



From emil.l.velikov at gmail.com  Thu Aug 13 04:45:09 2015
From: emil.l.velikov at gmail.com (Emil Velikov)
Date: Thu, 13 Aug 2015 12:45:09 +0100
Subject: [PATCH 3/7] drm/vc4: Add KMS support for Raspberry Pi.
In-Reply-To: <1439427380-2436-4-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-4-git-send-email-eric@anholt.net>
Message-ID: <CACvgo527EEmC-iMf3waUHpBXVZzu2fNjk_UtHQENUGmd8fepYQ@mail.gmail.com>

On 13 August 2015 at 01:56, Eric Anholt <eric at anholt.net> wrote:
> This is the start of a full VC4 driver.  Right now this just supports
> configuring the display using a pre-existing video mode (because
> changing the pixel clock isn't available yet, and doesn't work when it
> is).  However, this is enough for fbcon and bringing up X using
> xf86-video-modesetting.
>
> Signed-off-by: Eric Anholt <eric at anholt.net>
...
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/Makefile
> @@ -0,0 +1,18 @@
> +ccflags-y := -Iinclude/drm
> +
> +# Please keep these build lists sorted!
> +
> +# core driver code
> +vc4-y := \
> +       vc4_bo.o \
> +       vc4_crtc.o \
> +       vc4_drv.o \
> +       vc4_kms.o \
> +       vc4_hdmi.o \
> +       vc4_hvs.o \
> +       vc4_plane.o \
> +       $()
> +
In case anyone is curious - this is the first sentinel in the whole
kernel (some 2000+ Makefiles) :-)

-Emil


From eric at anholt.net  Thu Aug 13 13:44:03 2015
From: eric at anholt.net (Eric Anholt)
Date: Thu, 13 Aug 2015 13:44:03 -0700
Subject: [PATCH 3/7] drm/vc4: Add KMS support for Raspberry Pi.
In-Reply-To: <20150813075141.GX17734@phenom.ffwll.local>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-4-git-send-email-eric@anholt.net>
 <20150813075141.GX17734@phenom.ffwll.local>
Message-ID: <87d1yqq5po.fsf@eliezer.anholt.net>

Daniel Vetter <daniel at ffwll.ch> writes:

> On Wed, Aug 12, 2015 at 05:56:16PM -0700, Eric Anholt wrote:
>> This is the start of a full VC4 driver.  Right now this just supports
>> configuring the display using a pre-existing video mode (because
>> changing the pixel clock isn't available yet, and doesn't work when it
>> is).  However, this is enough for fbcon and bringing up X using
>> xf86-video-modesetting.
>> 
>> Signed-off-by: Eric Anholt <eric at anholt.net>
>> ---
>>  drivers/gpu/drm/Kconfig           |   2 +
>>  drivers/gpu/drm/Makefile          |   1 +
>>  drivers/gpu/drm/vc4/Kconfig       |  14 +
>>  drivers/gpu/drm/vc4/Makefile      |  18 ++
>>  drivers/gpu/drm/vc4/vc4_bo.c      |  54 ++++
>>  drivers/gpu/drm/vc4/vc4_crtc.c    | 583 ++++++++++++++++++++++++++++++++++
>>  drivers/gpu/drm/vc4/vc4_debugfs.c |  38 +++
>>  drivers/gpu/drm/vc4/vc4_drv.c     | 249 +++++++++++++++
>>  drivers/gpu/drm/vc4/vc4_drv.h     | 123 +++++++
>>  drivers/gpu/drm/vc4/vc4_hdmi.c    | 651 ++++++++++++++++++++++++++++++++++++++
>>  drivers/gpu/drm/vc4/vc4_hvs.c     | 172 ++++++++++
>>  drivers/gpu/drm/vc4/vc4_kms.c     |  84 +++++
>>  drivers/gpu/drm/vc4/vc4_plane.c   | 320 +++++++++++++++++++
>>  drivers/gpu/drm/vc4/vc4_regs.h    | 562 ++++++++++++++++++++++++++++++++
>>  14 files changed, 2871 insertions(+)
>>  create mode 100644 drivers/gpu/drm/vc4/Kconfig
>>  create mode 100644 drivers/gpu/drm/vc4/Makefile
>>  create mode 100644 drivers/gpu/drm/vc4/vc4_bo.c
>>  create mode 100644 drivers/gpu/drm/vc4/vc4_crtc.c
>>  create mode 100644 drivers/gpu/drm/vc4/vc4_debugfs.c
>>  create mode 100644 drivers/gpu/drm/vc4/vc4_drv.c
>>  create mode 100644 drivers/gpu/drm/vc4/vc4_drv.h
>>  create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi.c
>>  create mode 100644 drivers/gpu/drm/vc4/vc4_hvs.c
>>  create mode 100644 drivers/gpu/drm/vc4/vc4_kms.c
>>  create mode 100644 drivers/gpu/drm/vc4/vc4_plane.c
>>  create mode 100644 drivers/gpu/drm/vc4/vc4_regs.h
>
> Made a quick pass and found a few things to update to latest drm
> developments. Of course didn't look at the hardware details since no clue,
> but looks really nice overall.
> -Daniel

If you have anything about the hardware that you were curious about, I'd
be interested in trying to explain them in the comments to the extent
that I can.  It's unfortunate that we haven't shipped docs for the
display side of things, but had to do a lot of reading of the verilog
just to get this far, anyway.

>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>> index c46ca31..1730a76 100644
>> --- a/drivers/gpu/drm/Kconfig
>> +++ b/drivers/gpu/drm/Kconfig
>> @@ -240,3 +240,5 @@ source "drivers/gpu/drm/sti/Kconfig"
>>  source "drivers/gpu/drm/amd/amdkfd/Kconfig"
>>  
>>  source "drivers/gpu/drm/imx/Kconfig"
>> +
>> +source "drivers/gpu/drm/vc4/Kconfig"
>> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
>> index 5713d05..b991ac5 100644
>> --- a/drivers/gpu/drm/Makefile
>> +++ b/drivers/gpu/drm/Makefile
>> @@ -42,6 +42,7 @@ obj-$(CONFIG_DRM_MGA)	+= mga/
>>  obj-$(CONFIG_DRM_I810)	+= i810/
>>  obj-$(CONFIG_DRM_I915)  += i915/
>>  obj-$(CONFIG_DRM_MGAG200) += mgag200/
>> +obj-$(CONFIG_DRM_VC4)  += vc4/
>>  obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/
>>  obj-$(CONFIG_DRM_SIS)   += sis/
>>  obj-$(CONFIG_DRM_SAVAGE)+= savage/
>> diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig
>> new file mode 100644
>> index 0000000..130cc94
>> --- /dev/null
>> +++ b/drivers/gpu/drm/vc4/Kconfig
>> @@ -0,0 +1,14 @@
>> +config DRM_VC4
>> +	tristate "Broadcom VC4 Graphics"
>> +	depends on ARCH_BCM2835
>> +	depends on DRM
>> +	select DRM_KMS_HELPER
>> +	select DRM_KMS_FB_HELPER
>> +	select DRM_KMS_CMA_HELPER
>
> drm-misc/linux-next already has Archit's patches to enable/disable fbdev
> in the core code, so you don't need to bother about these selects here any
> more, it'll no-op out if drm fbdev emulation isn't enabled. Since you're
> reusing cma fbdev helpers I don't think there's any need for other changes
> because of this.

It sounds like I should rebase on that, then?

>> +	help
>> +	  Choose this option if you have a system that has a Broadcom
>> +	  VC4 GPU, such as the Raspberry Pi or other BCM2708/BCM2835.
>> +
>> +	  This driver requires that "avoid_warnings=2" be present in
>> +	  the config.txt for the firmware, to keep it from smashing
>> +	  our display setup.
>> diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile
>> new file mode 100644
>> index 0000000..4aa07ca
>> --- /dev/null
>> +++ b/drivers/gpu/drm/vc4/Makefile
>> @@ -0,0 +1,18 @@
>> +ccflags-y := -Iinclude/drm
>> +
>> +# Please keep these build lists sorted!
>> +
>> +# core driver code
>> +vc4-y := \
>> +	vc4_bo.o \
>> +	vc4_crtc.o \
>> +	vc4_drv.o \
>> +	vc4_kms.o \
>> +	vc4_hdmi.o \
>> +	vc4_hvs.o \
>> +	vc4_plane.o \
>> +	$()
>> +
>> +vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o
>> +
>> +obj-$(CONFIG_DRM_VC4)  += vc4.o
>> diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
>> new file mode 100644
>> index 0000000..fee8cac
>> --- /dev/null
>> +++ b/drivers/gpu/drm/vc4/vc4_bo.c
>> @@ -0,0 +1,54 @@
>> +/*
>> + *  Copyright ? 2015 Broadcom
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +/* DOC: VC4 GEM BO management support.
>> + *
>> + * The VC4 GPU architecture (both scanout and rendering) has direct
>> + * access to system memory with no MMU in between.  To support it, we
>> + * use the GEM CMA helper functions to allocate contiguous ranges of
>> + * physical memory for our BOs.
>> + */
>
> Since you're doing kerneldoc considered pulling it all into a new vc4
> section in the drm docbook template?

I hadn't found the docbook template.  Interesting.  I'll try to cook up
some general vc4 docs for that.  I think that could be a separate
commit, though?

>> +
>> +#include "vc4_drv.h"
>> +
>> +struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size)
>> +{
>> +	struct drm_gem_cma_object *cma_obj;
>> +
>> +	cma_obj = drm_gem_cma_create(dev, size);
>> +	if (IS_ERR(cma_obj))
>> +		return NULL;
>> +	else
>> +		return to_vc4_bo(&cma_obj->base);
>> +}
>> +
>> +int vc4_dumb_create(struct drm_file *file_priv,
>> +		    struct drm_device *dev,
>> +		    struct drm_mode_create_dumb *args)
>> +{
>> +	int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
>> +	struct vc4_bo *bo = NULL;
>> +	int ret;
>> +
>> +	if (args->pitch < min_pitch)
>> +		args->pitch = min_pitch;
>> +
>> +	if (args->size < args->pitch * args->height)
>> +		args->size = args->pitch * args->height;
>> +
>> +	mutex_lock(&dev->struct_mutex);
>> +	bo = vc4_bo_create(dev, roundup(args->size, PAGE_SIZE));
>> +	mutex_unlock(&dev->struct_mutex);
>
> I'm on a struct_mutex crusade (trying to get rid of it in core and allow
> drivers to live without it). On a quick look there doesn't seem to be
> anything that needs struct_mutex here, so please just remove it. If there
> is indeed something vc4-internal you want to protect, please use your own
> driver-internal mutex (e.g. for drm_mm or command submission or whatever).
>
> btw the last bit in the drm core for modern drivers that needs
> struct_mutex is mmap_offset gem object lookup. I plan to replace that with
> kref_get_unless_zero trickery, which would make the core and a lot of
> drivers struct_mutex free and so relegate it mostly to a legacy role (and
> can be forgotten).

Struct mutex is here because this code is from the V3D series, with the
in-kernel BO cache ripped out (it turns out that the CMA allocator is
slow, and you can't just userspace cache since we have to do allocations
within the kernel to the tune of a couple per draw and that's too much).

I'll pull the mutex calls out for now until the cache stuff is
submitted.

>> +static bool vc4_crtc_mode_fixup(struct drm_crtc *crtc,
>> +				const struct drm_display_mode *mode,
>> +				struct drm_display_mode *adjusted_mode)
>> +{
>> +	return true;
>> +}
>
> mode_fixup on crtcs is optional since 840bfe953384a and I just merged a
> patch to make it optional for encoders too (when using atomic helpers
> which you do). You can remove them both.

Great!  It felt like there was a *lot* of boilerplate when I was first
writing this stuff, and things are way better than they used to be.

>> +static int vc4_drm_load(struct drm_device *dev, unsigned long flags)
>> +{
>> +	struct vc4_dev *vc4;
>> +	int ret;
>> +
>> +	vc4 = devm_kzalloc(dev->dev, sizeof(*vc4), GFP_KERNEL);
>> +	if (!vc4)
>> +		return -ENOMEM;
>> +
>> +	dev_set_drvdata(dev->dev, dev);
>> +	vc4->dev = dev;
>> +	dev->dev_private = vc4;
>> +
>> +	drm_mode_config_init(dev);
>> +
>> +	ret = component_bind_all(dev->dev, dev);
>> +	if (ret)
>> +		return ret;
>> +
>> +	vc4_kms_load(dev);
>> +
>> +	return 0;
>> +}
>
> ->load has backwards init ordering (we register public interfaces before
> calling it, yay) because backwards compat. Hence deprecated, please use
> drm_dev_alloc(); ... driver init including drm_dev_set_unique();
> drm_dev_register(); instead and drop ->load.

Will do.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150813/dca1b430/attachment.sig>

From daniel at ffwll.ch  Thu Aug 13 14:17:55 2015
From: daniel at ffwll.ch (Daniel Vetter)
Date: Thu, 13 Aug 2015 23:17:55 +0200
Subject: [PATCH 3/7] drm/vc4: Add KMS support for Raspberry Pi.
In-Reply-To: <87d1yqq5po.fsf@eliezer.anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-4-git-send-email-eric@anholt.net>
 <20150813075141.GX17734@phenom.ffwll.local>
 <87d1yqq5po.fsf@eliezer.anholt.net>
Message-ID: <20150813211755.GE17734@phenom.ffwll.local>

On Thu, Aug 13, 2015 at 01:44:03PM -0700, Eric Anholt wrote:
> Daniel Vetter <daniel at ffwll.ch> writes:
> 
> > On Wed, Aug 12, 2015 at 05:56:16PM -0700, Eric Anholt wrote:
> >> This is the start of a full VC4 driver.  Right now this just supports
> >> configuring the display using a pre-existing video mode (because
> >> changing the pixel clock isn't available yet, and doesn't work when it
> >> is).  However, this is enough for fbcon and bringing up X using
> >> xf86-video-modesetting.
> >> 
> >> Signed-off-by: Eric Anholt <eric at anholt.net>
> >> ---
> >>  drivers/gpu/drm/Kconfig           |   2 +
> >>  drivers/gpu/drm/Makefile          |   1 +
> >>  drivers/gpu/drm/vc4/Kconfig       |  14 +
> >>  drivers/gpu/drm/vc4/Makefile      |  18 ++
> >>  drivers/gpu/drm/vc4/vc4_bo.c      |  54 ++++
> >>  drivers/gpu/drm/vc4/vc4_crtc.c    | 583 ++++++++++++++++++++++++++++++++++
> >>  drivers/gpu/drm/vc4/vc4_debugfs.c |  38 +++
> >>  drivers/gpu/drm/vc4/vc4_drv.c     | 249 +++++++++++++++
> >>  drivers/gpu/drm/vc4/vc4_drv.h     | 123 +++++++
> >>  drivers/gpu/drm/vc4/vc4_hdmi.c    | 651 ++++++++++++++++++++++++++++++++++++++
> >>  drivers/gpu/drm/vc4/vc4_hvs.c     | 172 ++++++++++
> >>  drivers/gpu/drm/vc4/vc4_kms.c     |  84 +++++
> >>  drivers/gpu/drm/vc4/vc4_plane.c   | 320 +++++++++++++++++++
> >>  drivers/gpu/drm/vc4/vc4_regs.h    | 562 ++++++++++++++++++++++++++++++++
> >>  14 files changed, 2871 insertions(+)
> >>  create mode 100644 drivers/gpu/drm/vc4/Kconfig
> >>  create mode 100644 drivers/gpu/drm/vc4/Makefile
> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_bo.c
> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_crtc.c
> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_debugfs.c
> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_drv.c
> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_drv.h
> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi.c
> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_hvs.c
> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_kms.c
> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_plane.c
> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_regs.h
> >
> > Made a quick pass and found a few things to update to latest drm
> > developments. Of course didn't look at the hardware details since no clue,
> > but looks really nice overall.
> 
> If you have anything about the hardware that you were curious about, I'd
> be interested in trying to explain them in the comments to the extent
> that I can.  It's unfortunate that we haven't shipped docs for the
> display side of things, but had to do a lot of reading of the verilog
> just to get this far, anyway.

The only thing I spotted is that you right now only register a primary and
cursor plane. I guess the plan we once discussed about exposing piles of
planes for -modesetting accel isn't there yet?

But otherwise I really didn't go into the hardware details.

> >> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> >> index c46ca31..1730a76 100644
> >> --- a/drivers/gpu/drm/Kconfig
> >> +++ b/drivers/gpu/drm/Kconfig
> >> @@ -240,3 +240,5 @@ source "drivers/gpu/drm/sti/Kconfig"
> >>  source "drivers/gpu/drm/amd/amdkfd/Kconfig"
> >>  
> >>  source "drivers/gpu/drm/imx/Kconfig"
> >> +
> >> +source "drivers/gpu/drm/vc4/Kconfig"
> >> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> >> index 5713d05..b991ac5 100644
> >> --- a/drivers/gpu/drm/Makefile
> >> +++ b/drivers/gpu/drm/Makefile
> >> @@ -42,6 +42,7 @@ obj-$(CONFIG_DRM_MGA)	+= mga/
> >>  obj-$(CONFIG_DRM_I810)	+= i810/
> >>  obj-$(CONFIG_DRM_I915)  += i915/
> >>  obj-$(CONFIG_DRM_MGAG200) += mgag200/
> >> +obj-$(CONFIG_DRM_VC4)  += vc4/
> >>  obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/
> >>  obj-$(CONFIG_DRM_SIS)   += sis/
> >>  obj-$(CONFIG_DRM_SAVAGE)+= savage/
> >> diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig
> >> new file mode 100644
> >> index 0000000..130cc94
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/vc4/Kconfig
> >> @@ -0,0 +1,14 @@
> >> +config DRM_VC4
> >> +	tristate "Broadcom VC4 Graphics"
> >> +	depends on ARCH_BCM2835
> >> +	depends on DRM
> >> +	select DRM_KMS_HELPER
> >> +	select DRM_KMS_FB_HELPER
> >> +	select DRM_KMS_CMA_HELPER
> >
> > drm-misc/linux-next already has Archit's patches to enable/disable fbdev
> > in the core code, so you don't need to bother about these selects here any
> > more, it'll no-op out if drm fbdev emulation isn't enabled. Since you're
> > reusing cma fbdev helpers I don't think there's any need for other changes
> > because of this.
> 
> It sounds like I should rebase on that, then?

Yeah probably simplest. I made a pull request for drm-misc and a tag and
cc'ed you on it so you have a baseline.

> >> +	help
> >> +	  Choose this option if you have a system that has a Broadcom
> >> +	  VC4 GPU, such as the Raspberry Pi or other BCM2708/BCM2835.
> >> +
> >> +	  This driver requires that "avoid_warnings=2" be present in
> >> +	  the config.txt for the firmware, to keep it from smashing
> >> +	  our display setup.
> >> diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile
> >> new file mode 100644
> >> index 0000000..4aa07ca
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/vc4/Makefile
> >> @@ -0,0 +1,18 @@
> >> +ccflags-y := -Iinclude/drm
> >> +
> >> +# Please keep these build lists sorted!
> >> +
> >> +# core driver code
> >> +vc4-y := \
> >> +	vc4_bo.o \
> >> +	vc4_crtc.o \
> >> +	vc4_drv.o \
> >> +	vc4_kms.o \
> >> +	vc4_hdmi.o \
> >> +	vc4_hvs.o \
> >> +	vc4_plane.o \
> >> +	$()
> >> +
> >> +vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o
> >> +
> >> +obj-$(CONFIG_DRM_VC4)  += vc4.o
> >> diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
> >> new file mode 100644
> >> index 0000000..fee8cac
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/vc4/vc4_bo.c
> >> @@ -0,0 +1,54 @@
> >> +/*
> >> + *  Copyright ? 2015 Broadcom
> >> + *
> >> + * This program is free software; you can redistribute it and/or modify
> >> + * it under the terms of the GNU General Public License version 2 as
> >> + * published by the Free Software Foundation.
> >> + */
> >> +
> >> +/* DOC: VC4 GEM BO management support.
> >> + *
> >> + * The VC4 GPU architecture (both scanout and rendering) has direct
> >> + * access to system memory with no MMU in between.  To support it, we
> >> + * use the GEM CMA helper functions to allocate contiguous ranges of
> >> + * physical memory for our BOs.
> >> + */
> >
> > Since you're doing kerneldoc considered pulling it all into a new vc4
> > section in the drm docbook template?
> 
> I hadn't found the docbook template.  Interesting.  I'll try to cook up
> some general vc4 docs for that.  I think that could be a separate
> commit, though?

Sure. Really just for yourself and other people hacking on this. btw
there's some work intel sponsors from collabora to improve kerneldoc
comments with automated hyperlinking, markdown and a few other things. But
unfortunately not yet merged.

> >> +
> >> +#include "vc4_drv.h"
> >> +
> >> +struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size)
> >> +{
> >> +	struct drm_gem_cma_object *cma_obj;
> >> +
> >> +	cma_obj = drm_gem_cma_create(dev, size);
> >> +	if (IS_ERR(cma_obj))
> >> +		return NULL;
> >> +	else
> >> +		return to_vc4_bo(&cma_obj->base);
> >> +}
> >> +
> >> +int vc4_dumb_create(struct drm_file *file_priv,
> >> +		    struct drm_device *dev,
> >> +		    struct drm_mode_create_dumb *args)
> >> +{
> >> +	int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
> >> +	struct vc4_bo *bo = NULL;
> >> +	int ret;
> >> +
> >> +	if (args->pitch < min_pitch)
> >> +		args->pitch = min_pitch;
> >> +
> >> +	if (args->size < args->pitch * args->height)
> >> +		args->size = args->pitch * args->height;
> >> +
> >> +	mutex_lock(&dev->struct_mutex);
> >> +	bo = vc4_bo_create(dev, roundup(args->size, PAGE_SIZE));
> >> +	mutex_unlock(&dev->struct_mutex);
> >
> > I'm on a struct_mutex crusade (trying to get rid of it in core and allow
> > drivers to live without it). On a quick look there doesn't seem to be
> > anything that needs struct_mutex here, so please just remove it. If there
> > is indeed something vc4-internal you want to protect, please use your own
> > driver-internal mutex (e.g. for drm_mm or command submission or whatever).
> >
> > btw the last bit in the drm core for modern drivers that needs
> > struct_mutex is mmap_offset gem object lookup. I plan to replace that with
> > kref_get_unless_zero trickery, which would make the core and a lot of
> > drivers struct_mutex free and so relegate it mostly to a legacy role (and
> > can be forgotten).
> 
> Struct mutex is here because this code is from the V3D series, with the
> in-kernel BO cache ripped out (it turns out that the CMA allocator is
> slow, and you can't just userspace cache since we have to do allocations
> within the kernel to the tune of a couple per draw and that's too much).
> 
> I'll pull the mutex calls out for now until the cache stuff is
> submitted.

Yeah I suspected that's for later. If feasible it'd be great if you could
rearchtect it to use a driver-private lock, just to not grow another place
using it.

> >> +static bool vc4_crtc_mode_fixup(struct drm_crtc *crtc,
> >> +				const struct drm_display_mode *mode,
> >> +				struct drm_display_mode *adjusted_mode)
> >> +{
> >> +	return true;
> >> +}
> >
> > mode_fixup on crtcs is optional since 840bfe953384a and I just merged a
> > patch to make it optional for encoders too (when using atomic helpers
> > which you do). You can remove them both.
> 
> Great!  It felt like there was a *lot* of boilerplate when I was first
> writing this stuff, and things are way better than they used to be.

Just noticed that crtc->atomic_begin is optional too. btw if you spot
boilerplate somewhere else please raise it on irc, there's still a lot of
room for improvement for atomic helpers.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From linux at arm.linux.org.uk  Thu Aug 13 14:29:36 2015
From: linux at arm.linux.org.uk (Russell King - ARM Linux)
Date: Thu, 13 Aug 2015 22:29:36 +0100
Subject: [PATCH 3/7] drm/vc4: Add KMS support for Raspberry Pi.
In-Reply-To: <87d1yqq5po.fsf@eliezer.anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-4-git-send-email-eric@anholt.net>
 <20150813075141.GX17734@phenom.ffwll.local>
 <87d1yqq5po.fsf@eliezer.anholt.net>
Message-ID: <20150813212936.GT7557@n2100.arm.linux.org.uk>

On Thu, Aug 13, 2015 at 01:44:03PM -0700, Eric Anholt wrote:
> Struct mutex is here because this code is from the V3D series, with the
> in-kernel BO cache ripped out (it turns out that the CMA allocator is
> slow, and you can't just userspace cache since we have to do allocations
> within the kernel to the tune of a couple per draw and that's too much).

The CMA allocator is fast until you have pinned pages in its region,
where it becomes _very_ slow to do allocations, sometimes getting up
to the order of seconds.

The main culpret of this are GFP_HIGHUSER_MOVABLE allocations which
then pin the page.  It doesn't take many of those to make CMA really
inefficient.

The problem is that CMA doesn't get any information back from the
internal page migration about which pages couldn't be moved, so it
dumbly just tries incrementing the allocation by one page (subject
to alignment constraints) and retrying again - repeating over the
entire CMA region.  The bigger the region, the more time this takes.

-- 
FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up
according to speedtest.net.


From eric at anholt.net  Thu Aug 13 16:03:00 2015
From: eric at anholt.net (Eric Anholt)
Date: Thu, 13 Aug 2015 16:03:00 -0700
Subject: [PATCH 3/7] drm/vc4: Add KMS support for Raspberry Pi.
In-Reply-To: <20150813212936.GT7557@n2100.arm.linux.org.uk>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-4-git-send-email-eric@anholt.net>
 <20150813075141.GX17734@phenom.ffwll.local>
 <87d1yqq5po.fsf@eliezer.anholt.net>
 <20150813212936.GT7557@n2100.arm.linux.org.uk>
Message-ID: <87zj1uiyfv.fsf@eliezer.anholt.net>

Russell King - ARM Linux <linux at arm.linux.org.uk> writes:

> On Thu, Aug 13, 2015 at 01:44:03PM -0700, Eric Anholt wrote:
>> Struct mutex is here because this code is from the V3D series, with the
>> in-kernel BO cache ripped out (it turns out that the CMA allocator is
>> slow, and you can't just userspace cache since we have to do allocations
>> within the kernel to the tune of a couple per draw and that's too much).
>
> The CMA allocator is fast until you have pinned pages in its region,
> where it becomes _very_ slow to do allocations, sometimes getting up
> to the order of seconds.
>
> The main culpret of this are GFP_HIGHUSER_MOVABLE allocations which
> then pin the page.  It doesn't take many of those to make CMA really
> inefficient.
>
> The problem is that CMA doesn't get any information back from the
> internal page migration about which pages couldn't be moved, so it
> dumbly just tries incrementing the allocation by one page (subject
> to alignment constraints) and retrying again - repeating over the
> entire CMA region.  The bigger the region, the more time this takes.

Ouch.

Since I can workaround the allocation cost, the main problem I have
right now is that I've got a set of small allocations for 3D that all
need to have the same high 4 bits of paddr, because someone cleverly
packed some address bits in a GPU-managed structure.  Any
recommendations for ways to handle this with CMA?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150813/771987cb/attachment.sig>

From eric at anholt.net  Thu Aug 13 16:05:39 2015
From: eric at anholt.net (Eric Anholt)
Date: Thu, 13 Aug 2015 16:05:39 -0700
Subject: Rasperry Pi clock support
Message-ID: <1439507142-2965-1-git-send-email-eric@anholt.net>

I'm also avoiding requests to set rate/state when they're a no-op, as
apparently no-op changes to pixel clock break the firmware-configured
display setup.  It *shouldn't* change anything, but I've confirmed
that setting the CPU clock works fine, and after a couple of weeks of
prodding pixel clock now, I'm at a loss for what's going on with it.



From gottfried.haider at gmail.com  Fri Aug 14 06:56:53 2015
From: gottfried.haider at gmail.com (Gottfried Haider)
Date: Fri, 14 Aug 2015 15:56:53 +0200
Subject: pinctrl-bcm2835: Runtime configuration of pull-ups?
Message-ID: <CAPoCmYJxpxQ8Tc8J37x1xEvrUsVxc0NZR5AQPohDudNOUkmxtw@mail.gmail.com>

Hello all,

I understand that the pinctrl driver can enable the pins' internal
pull-ups (or pull-downs) based on device tree configuration. Are there
plans to expose this functionality to user-space, so that one could
dynamically enable or disable the pull-up of exported GPIO pins?

I haven't done any coding in the kernel before, but I would be happy
to give it a try, if its feasible for me to do, and relevant for other
people as well?

Best
Gottfried


From eric at anholt.net  Fri Aug 14 11:29:02 2015
From: eric at anholt.net (Eric Anholt)
Date: Fri, 14 Aug 2015 11:29:02 -0700
Subject: [GIT PULL] RPi DT changes due for v4.3
In-Reply-To: <20150813103056.GY30160@localhost>
References: <20150811160642.GQ18282@x1> <20150813103056.GY30160@localhost>
Message-ID: <878u9dhggh.fsf@eliezer.anholt.net>

Olof Johansson <olof at lixom.net> writes:

> Hi,
>
> On Tue, Aug 11, 2015 at 05:06:42PM +0100, Lee Jones wrote:
>> ARM SoC Chaps,
>> 
>> Please find a couple of simple RPi changes pertaining to Firmware.
>> 
>> The following changes since commit bc0195aad0daa2ad5b0d76cce22b167bc3435590:
>> 
>>   Linux 4.2-rc2 (2015-07-12 15:10:30 -0700)
>> 
>> are available in the git repository at:
>> 
>>   git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git tags/rpi-dt-for-armsoc-4.3
>> 
>> for you to fetch changes up to fd26f8830979de48eb3f1c253eb9d2ee2e468eb6:
>> 
>>   dt/bindings: Add binding for the Raspberry Pi firmware driver (2015-08-11 16:56:41 +0100)
>
> I've merged this but I'm a little confused about bcm2835 maintainership:
> I got a MAINTAINERS update from Florian earlier grouped in a set of
> other bcm changes (adding Eric), and now this directly from you.
>
> In general the bcm platforms are quite diverse and we've asked Florian
> to be the funnel for all of the new ones, but I don't think we talked
> much about 2835 at that time. Should we expect to see merges directly
> from you and the other maintainers there or will it go through Florian?

I'd been talking with Arnd and Florian about the delays in RPi patch
acceptance (Stephen and Lee have limited time to dedicate to 2835), and
Florian suggested me becoming a maintainer and routing patches through
him.  He's been great to work with and has had useful feedback on my
patches.

I was kind of surprised to see Lee send this pull request -- these
patches had been sitting un-merged for 2 months and pings on irc didn't
get replies, so I thought I was stepping in where the others definitely
didn't have time.  It was also odd to see just these two patches, but
not the actual driver.

I'm willing to do the merging process for 2835, and if I'm doing so, I
like the idea of feeding it through a more experienced maintainer who's
ready to look at my pull requests whenever, without bothering the main
arm-soc folks.  My goal here, besides my actual job of getting open
source graphics working for Broadcom, is to hopefully push the rpi
foundation toward getting their code merged, so that this hardware fully
works on stock upstream.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150814/a6508819/attachment.sig>

From swarren at wwwdotorg.org  Fri Aug 14 20:04:22 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Fri, 14 Aug 2015 21:04:22 -0600
Subject: devicetree: bcm2835:
In-Reply-To: <55C98DD2.5080402@martin.sperl.org>
References: <55C8639E.4@martin.sperl.org> <55C9785C.30203@wwwdotorg.org>
 <55C98DD2.5080402@martin.sperl.org>
Message-ID: <55CEAC36.6070201@wwwdotorg.org>

On 08/10/2015 11:53 PM, Martin Sperl wrote:
> 
> 
> On 11.08.2015 06:21, Stephen Warren wrote:
>> On 08/10/2015 02:41 AM, Martin Sperl wrote:
>>> Hi!
>>>
>>> When adding a gpio section to enable the auxiliar spi device pins
>>> with the correct mode to the bcm2835 device treesimilar to this:
>>>          /* SPI1 pins */
>>>          spi1_pins: spi1_pins {
>>>                  brcm,pins = <19 20 21>;
>>>                  brcm,function = <BCM2835_FSEL_ALT4>
>>>          };
>>>          /* SPI1 GPIO-chipselects */
>>>          spi1_cs_pins: spi1_cs_pins {
>>>                  brcm,pins = <18 17 16>;
>>>                  brcm,function = <BCM2835_FSEL_OUT>;
>>>          };
>>>
>>>          /* SPI2 pins */
>>>          spi2_pins: spi2_pins {
>>>                  brcm,pins = <40 41 42>;
>>>                  brcm,function = <BCM2835_FSEL_ALT4>;
>>>          };
>>>          /* SPI2 GPIO-chipselects */
>>>          spi2_cs_pins: spi2_cs_pins {
>>>                  brcm,pins = <43 44 45>;
>>>                  brcm,function = <BCM2835_FSEL_OUT>
>>>          };
>>
>> You don't need to add all that to the SPI node. Just set up the existing
>> pinctrl configurations in the pinctrl node as you need them. Per-device
>> DT configuration nodes are useful if you need to do dynamic pinmuxing,
>> but there's no need for that to simply set up the usage of the SPI/I2C
>> pins at boot.
>
> So does this mean I should NOT provide some default dt-configs for the
> device tree pin-mux when sending the patch?

I would expect a patch that adds the SPI device into bcm2835.dtsi, and
sets it to disabled by default, since it's board- and user-specific
whether the SPI controller is actually used or not.

> So why do we have PWM0 pin-modes configured in
> arch/arm/boot/dts/bcm2835-rpi.dtsi
> and I2S in arch/arm/boot/dts/bcm2835-rpi-b.dts and in
> arch/arm/boot/dts/bcm2835-rpi-b-plus.dts.

I don't see any pins set to any PWM function.

The RPi pinmux content in the DT files is a bit of a mess right now. The
default DTs should not configure any pins that are on the expansion
header, since there is no way to know what a user has attached to those
pins. All expansion pins should be left set to the HW default, i.e. GPIO
in. Unfortunately that's not the case right now.

The end-user is the only person who knows what they've attached to their
system. They should edit the device tree (or perhaps supply DT overlays
or similar) to describe the pin usage they want.

Of course, for pins that connect to on-board peripherals, the default DT
can and should set up those pins to the appropriate configuration. That
makes sense since there's only one way those pins can possibly be used,
so we can hard-coded that one configuration.

> Would we need separate device-trees (dts) to use spi1/2?
> e.g arch/arm/boot/dts/bcm2835-rpi-b-auxspi.dts
> Note that this would also require that PIN 40 configuration gets moved
> out ?

Yes, a cleanup of the DT files is something that should happen.

> Just wanting to make sure that there is nothing missing when I submit
> the patches for spi-bcm2835aux (plus the bcm2835-aux enable driver).



From swarren at wwwdotorg.org  Fri Aug 14 20:16:50 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Fri, 14 Aug 2015 21:16:50 -0600
Subject: pinctrl-bcm2835: Runtime configuration of pull-ups?
In-Reply-To: <CAPoCmYJxpxQ8Tc8J37x1xEvrUsVxc0NZR5AQPohDudNOUkmxtw@mail.gmail.com>
References: <CAPoCmYJxpxQ8Tc8J37x1xEvrUsVxc0NZR5AQPohDudNOUkmxtw@mail.gmail.com>
Message-ID: <55CEAF22.5030505@wwwdotorg.org>

On 08/14/2015 07:56 AM, Gottfried Haider wrote:
> Hello all,
> 
> I understand that the pinctrl driver can enable the pins' internal
> pull-ups (or pull-downs) based on device tree configuration.

Yes.

> Are there
> plans to expose this functionality to user-space, so that one could
> dynamically enable or disable the pull-up of exported GPIO pins?

I'm not aware of any plans for a user-space pinctrl API. However, your
best bet to get up-to-date information is to ask the pinctrl subsystem
maintainer; see the "PIN CONTROL SUBSYSTEM" entry in the MAINTAINERS
file in the Linux source.

I suspect you could already make this work by dynamically
loading/unloading a device tree overlay that contained the desired
pinctrl configuration? You might need the DT overlay to trigger the
loading of a new driver in order to actually activate any pinctrl
configuration in the DT overlay though.

> I haven't done any coding in the kernel before, but I would be happy
> to give it a try, if its feasible for me to do, and relevant for other
> people as well?

I'd suggest asking the pinctrl maintainers whether this is a feature
they'd accept. For background on writing kernel patches, see the
following files in the kernel source tree:

Documentation/SubmittingPatches
Documentation/CodingStyle
(and likely more!)


From swarren at wwwdotorg.org  Fri Aug 14 21:13:31 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Fri, 14 Aug 2015 22:13:31 -0600
Subject: [PATCH v5 3/3] ARM: bcm2835: Add DT for the firmware clocks
 driver.
In-Reply-To: <1439507142-2965-4-git-send-email-eric@anholt.net>
References: <1439507142-2965-1-git-send-email-eric@anholt.net>
 <1439507142-2965-4-git-send-email-eric@anholt.net>
Message-ID: <55CEBC6B.9070001@wwwdotorg.org>

On 08/13/2015 05:05 PM, Eric Anholt wrote:
> Signed-off-by: Eric Anholt <eric at anholt.net>
> Acked-by: Lee Jones <lee at kernel.org>

A patch description might be nice, although admittedly the subject seems
clear enough.

> diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi

> +		firmware_clocks: firmware-clocks {
> +			compatible = "raspberrypi,bcm2835-firmware-clocks";
> +			#clock-cells = <1>;
> +			raspberrypi,firmware = <&firmware>;
> +		};

No need to resend for this, but just a background note: DT node names
usually contain a device type not a device identity, so "clocks" not
"firmware-clocks" would be typical.

This patch,
Acked-by: Stephen Warren <swarren at wwwdotorg.org>


From swarren at wwwdotorg.org  Fri Aug 14 21:28:26 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Fri, 14 Aug 2015 22:28:26 -0600
Subject: [PATCH v5 2/3] clk: Add a Raspberry Pi-specific clock driver.
In-Reply-To: <1439507142-2965-3-git-send-email-eric@anholt.net>
References: <1439507142-2965-1-git-send-email-eric@anholt.net>
 <1439507142-2965-3-git-send-email-eric@anholt.net>
Message-ID: <55CEBFEA.7080505@wwwdotorg.org>

On 08/13/2015 05:05 PM, Eric Anholt wrote:
> Unfortunately, the clock manager's registers are not accessible by the
> ARM, so we have to request that the firmware modify our clocks for us.
> 
> This driver only registers the clocks at the point they are requested
> by a client driver.  This is partially to support returning
> -EPROBE_DEFER when the firmware driver isn't supported yet, but it
> also avoids issues with disabling "unused" clocks due to them not yet
> being connected to their consumers in the DT.

> diff --git a/drivers/clk/clk-raspberrypi.c b/drivers/clk/clk-raspberrypi.c

> +static const struct {
> +	const char *name;
> +	int flags;
> +} rpi_clocks[] = {
> +	[RPI_CLOCK_EMMC] = { "emmc", CLK_IS_ROOT },
> +	[RPI_CLOCK_UART0] = { "uart0", CLK_IS_ROOT },
> +	[RPI_CLOCK_ARM] = { "arm", CLK_IS_ROOT | CLK_IGNORE_UNUSED },
> +	[RPI_CLOCK_CORE] = { "core", CLK_IS_ROOT | CLK_IGNORE_UNUSED },
> +	[RPI_CLOCK_V3D] = { "v3d", CLK_IS_ROOT },
> +	[RPI_CLOCK_H264] = { "h264", CLK_IS_ROOT },
> +	[RPI_CLOCK_ISP] = { "isp", CLK_IS_ROOT },
> +	[RPI_CLOCK_SDRAM] = { "sdram", CLK_IS_ROOT | CLK_IGNORE_UNUSED },
> +	[RPI_CLOCK_PIXEL] = { "pixel", CLK_IS_ROOT | CLK_IGNORE_UNUSED },
> +	[RPI_CLOCK_PWM] = { "pwm", CLK_IS_ROOT },
> +};
> +
> +struct rpi_firmware_clock {
> +	/* Clock definitions in our static struct. */
> +	const char *name;
> +	int flags;

Are these duplicates of the values in rpi_clocks[]? Why not just store a
pointer to or index of the entry in that array?

> +static int rpi_clk_set_state(struct clk_hw *hw, bool on)
> +{
> +	struct rpi_firmware_clock *rpi_clk =
> +		container_of(hw, struct rpi_firmware_clock, hw);
> +	u32 packet[2];
> +	int ret;
> +
> +	if (on == (rpi_clk->last_rate != 0))
> +		return 0;

The overloading of last_rate to represent both rate information and
on/off status is slightly confusing. I would have expected this function
to clear last_rate to 0 when switching the clock off, and some specific
rate when turning a clock on. Is there some guarantee that the clock
core will always call recalc_rate() at certain times, thus ensuring that
last_rate is always accurate?

Wouldn't it be simpler to let last_rate always represent that actual
rate, and have a separate last_on or is_on field to represent the
enable/disable state?

> +static unsigned long rpi_clk_get_rate(struct clk_hw *hw,
> +				      unsigned long parent_rate)
...
> +	rpi_clk->last_rate = packet[1];

Since this is a query API, I wouldn't have expected it to have
side-effects like this. Don't we know what rate the clock runs at based
on the firmware's response in set_rate()?

> +static int rpi_clk_probe(struct platform_device *pdev)

> +	onecell = devm_kmalloc(dev, sizeof(*onecell), GFP_KERNEL);
> +	if (!onecell)
> +		return -ENOMEM;
> +	onecell->clk_num = ARRAY_SIZE(rpi_clocks);
> +	onecell->clks = devm_kzalloc(dev, sizeof(*onecell->clks), GFP_KERNEL);

Don't you need to multiply the size by ARRAY_SIZE(rpi_clocks)? I assume
onecell->clks is an array with one entry per each of onecell->clk_num?
Yes, the for loop right after that allocation confirms this.


From swarren at wwwdotorg.org  Fri Aug 14 21:38:54 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Fri, 14 Aug 2015 22:38:54 -0600
Subject: [PATCH 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <1439427380-2436-2-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-2-git-send-email-eric@anholt.net>
Message-ID: <55CEC25E.7070303@wwwdotorg.org>

On 08/12/2015 06:56 PM, Eric Anholt wrote:
> Signed-off-by: Eric Anholt <eric at anholt.net>

This one definitely needs a patch description, since someone might not
know what a VC4 is, and "git log" won't show the text from the binding
doc itself. I'd suggest adding the initial paragraph of the binding doc
as the patch description, or more.

> diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt

> +Required properties for VC4:
> +- compatible:	Should be "brcm,vc4"
> +- crtcs:	List of references to pixelvalve scanout engines

s/references to/phandles of/ would be more typical DT language.

> +- hvss:		List of references to HVS video scalers
> +- encoders:	List of references to output encoders (HDMI, SDTV)

Would it make sense to make all those nodes child node of the vc4
object. That way, there's no need to have these lists of objects; they
can be automatically built up as the DT is enumerated. I know that e.g.
the NVIDIA Tegra host1x binding works this way, and I think it may have
been inspired by other similar cases.

Of course, this is only appropriate if the HW modules really are
logically children of the VC4 HW module. Perhaps they aren't. If they
aren't though, I wonder what this "vc4" module actually represents in HW?

> +Required properties for HDMI
> +- compatible:	Should be "brcm,vc4-hdmi"
> +- reg:		Physical base address and length of the two register ranges
> +		  ("HDMI" and "HD")

I'd add "in that order" right before ")".

> +Example:
> +/ {
> +	soc {

Minor nit: Examples often don't include any nodes "above" the nodes
whose bindings are being documented.


From swarren at wwwdotorg.org  Fri Aug 14 21:39:56 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Fri, 14 Aug 2015 22:39:56 -0600
Subject: [PATCH 2/7] MAINTAINERS: Add myself for the new VC4 (RPi GPU)
 graphics driver.
In-Reply-To: <1439427380-2436-3-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-3-git-send-email-eric@anholt.net>
Message-ID: <55CEC29C.806@wwwdotorg.org>

On 08/12/2015 06:56 PM, Eric Anholt wrote:

> diff --git a/MAINTAINERS b/MAINTAINERS

> +DRM DRIVERS FOR VC4
> +M:	Eric Anholt <eric at anholt.net>
> +T:	git git://github.com/anholt/linux
> +S:	Maintained
> +F:	drivers/gpu/drm/vc4/*

S: Supported

?


From swarren at wwwdotorg.org  Fri Aug 14 21:45:12 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Fri, 14 Aug 2015 22:45:12 -0600
Subject: [PATCH 3/7] drm/vc4: Add KMS support for Raspberry Pi.
In-Reply-To: <1439427380-2436-4-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-4-git-send-email-eric@anholt.net>
Message-ID: <55CEC3D8.7060103@wwwdotorg.org>

On 08/12/2015 06:56 PM, Eric Anholt wrote:
> This is the start of a full VC4 driver.  Right now this just supports
> configuring the display using a pre-existing video mode (because
> changing the pixel clock isn't available yet, and doesn't work when it
> is).  However, this is enough for fbcon and bringing up X using
> xf86-video-modesetting.

> diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig

> +config DRM_VC4
> +	tristate "Broadcom VC4 Graphics"

> +	help
> +	  Choose this option if you have a system that has a Broadcom
> +	  VC4 GPU, such as the Raspberry Pi or other BCM2708/BCM2835.
> +
> +	  This driver requires that "avoid_warnings=2" be present in
> +	  the config.txt for the firmware, to keep it from smashing
> +	  our display setup.

The need for "avoid_warnings=2" seems like it will trip people up. I
don't think it's in any config.txt I've seen. Can you expand more on that?


From swarren at wwwdotorg.org  Fri Aug 14 21:51:51 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Fri, 14 Aug 2015 22:51:51 -0600
Subject: [PATCH 6/7] ARM: bcm2835: Add the DDC I2C controller to the device
 tree.
In-Reply-To: <1439427380-2436-7-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-7-git-send-email-eric@anholt.net>
Message-ID: <55CEC567.109@wwwdotorg.org>

On 08/12/2015 06:56 PM, Eric Anholt wrote:
> We need to use it for getting video modes over HDMI.

> diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi

> +		i2c2: i2c at 7e805000 {
> +			compatible = "brcm,bcm2835-i2c";
> +			reg = <0x7e805000 0x1000>;
> +			interrupts = <2 21>;
> +			clocks = <&clk_i2c>;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +		};

In an SoC .dtsi file, you'd typically write:

	status = "disabled";

... in all nodes that represent IO controllers that interface to
external HW, so that board DT files can/must explicitly choose to enable
the device if it's actually in use on the board. Some systems might not
have HDMI and hence might not hook up the HDMI_SCL/SDA pads.

BCM2835-ARM-Peripherals.pdf states "Note that the BSC2 master is used
dedicated with the HDMI interface and should not be accessed by user
programs.". Does this imply the Linux kernel shouldn't be touching this
I2C controller; that the VC4 firmware might also be attempting to use
it? I wonder how any such sharing of the HW would work.


From swarren at wwwdotorg.org  Fri Aug 14 21:54:00 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Fri, 14 Aug 2015 22:54:00 -0600
Subject: [PATCH 7/7] ARM: bcm2835: Add VC4 to the device tree.
In-Reply-To: <1439427380-2436-8-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-8-git-send-email-eric@anholt.net>
Message-ID: <55CEC5E8.6090203@wwwdotorg.org>

On 08/12/2015 06:56 PM, Eric Anholt wrote:
> Signed-off-by: Eric Anholt <eric at anholt.net>

Patch description?


> diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi

>  		arm-pmu {
>  			compatible = "arm,arm1176-pmu";
>  		};
> +
> +		hdmi: brcm,vc4-hdmi at 7e902000 {

It'd be nice to keep all the DT nodes with a reg property together, and
sorted in reg order.

As before, I think any DT node for a HW block that may-or-may-not-be
used based on board connectivity/features should be disabled in the SoC
.dtsi file, and enabled in the board's DT file if the feature is useful
for that board.


From eric at anholt.net  Mon Aug 17 10:56:23 2015
From: eric at anholt.net (Eric Anholt)
Date: Mon, 17 Aug 2015 10:56:23 -0700
Subject: [PATCH 3/7] drm/vc4: Add KMS support for Raspberry Pi.
In-Reply-To: <55CEC3D8.7060103@wwwdotorg.org>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-4-git-send-email-eric@anholt.net>
 <55CEC3D8.7060103@wwwdotorg.org>
Message-ID: <877fotx0hk.fsf@eliezer.anholt.net>

r<#secure method=pgpmime mode=sign>
Stephen Warren <swarren at wwwdotorg.org> writes:

> On 08/12/2015 06:56 PM, Eric Anholt wrote:
>> This is the start of a full VC4 driver.  Right now this just supports
>> configuring the display using a pre-existing video mode (because
>> changing the pixel clock isn't available yet, and doesn't work when it
>> is).  However, this is enough for fbcon and bringing up X using
>> xf86-video-modesetting.
>
>> diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig
>
>> +config DRM_VC4
>> +	tristate "Broadcom VC4 Graphics"
>
>> +	help
>> +	  Choose this option if you have a system that has a Broadcom
>> +	  VC4 GPU, such as the Raspberry Pi or other BCM2708/BCM2835.
>> +
>> +	  This driver requires that "avoid_warnings=2" be present in
>> +	  the config.txt for the firmware, to keep it from smashing
>> +	  our display setup.
>
> The need for "avoid_warnings=2" seems like it will trip people up. I
> don't think it's in any config.txt I've seen. Can you expand more on that?

The warnings thing is the firmware watching for undervoltage and then it
calls into dispmanx to overlay a little rainbow box on the screen.  This
of course interferes with our display setup.

I think we'll be able to wire up notifications to Linux for
undervoltage, at which point we can do something useful with that
information, ourselves.


From eric at anholt.net  Mon Aug 17 11:30:14 2015
From: eric at anholt.net (Eric Anholt)
Date: Mon, 17 Aug 2015 11:30:14 -0700
Subject: [PATCH 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <55CEC25E.7070303@wwwdotorg.org>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-2-git-send-email-eric@anholt.net>
 <55CEC25E.7070303@wwwdotorg.org>
Message-ID: <874mjxwyx5.fsf@eliezer.anholt.net>

Stephen Warren <swarren at wwwdotorg.org> writes:

> On 08/12/2015 06:56 PM, Eric Anholt wrote:
>> Signed-off-by: Eric Anholt <eric at anholt.net>
>
> This one definitely needs a patch description, since someone might not
> know what a VC4 is, and "git log" won't show the text from the binding
> doc itself. I'd suggest adding the initial paragraph of the binding doc
> as the patch description, or more.
>
>> diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt

>> +- hvss:		List of references to HVS video scalers
>> +- encoders:	List of references to output encoders (HDMI, SDTV)
>
> Would it make sense to make all those nodes child node of the vc4
> object. That way, there's no need to have these lists of objects; they
> can be automatically built up as the DT is enumerated. I know that e.g.
> the NVIDIA Tegra host1x binding works this way, and I think it may have
> been inspired by other similar cases.

I've looked at tegra, and the component system used by msm appears to be
nicer than it.  To follow tegra's model, it looks like I need to build
this extra bus thing corresponding to host1x that is effectively the
drivers/base/component.c code, so that I can get at vc4's structure from
the component drivers.

> Of course, this is only appropriate if the HW modules really are
> logically children of the VC4 HW module. Perhaps they aren't. If they
> aren't though, I wonder what this "vc4" module actually represents in HW?

It's the subsystem, same as we use a subsystem node for msm, sti,
rockchip, imx, and exynos.  This appears to be the common model of how
the collection of graphics-related components is represented in the DT.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150817/0b3f40da/attachment.sig>

From eric at anholt.net  Mon Aug 17 11:35:30 2015
From: eric at anholt.net (Eric Anholt)
Date: Mon, 17 Aug 2015 11:35:30 -0700
Subject: [PATCH 6/7] ARM: bcm2835: Add the DDC I2C controller to the
 device tree.
In-Reply-To: <55CEC567.109@wwwdotorg.org>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-7-git-send-email-eric@anholt.net>
 <55CEC567.109@wwwdotorg.org>
Message-ID: <871tf1wyod.fsf@eliezer.anholt.net>

Stephen Warren <swarren at wwwdotorg.org> writes:

> On 08/12/2015 06:56 PM, Eric Anholt wrote:
>> We need to use it for getting video modes over HDMI.
>
>> diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
>
>> +		i2c2: i2c at 7e805000 {
>> +			compatible = "brcm,bcm2835-i2c";
>> +			reg = <0x7e805000 0x1000>;
>> +			interrupts = <2 21>;
>> +			clocks = <&clk_i2c>;
>> +			#address-cells = <1>;
>> +			#size-cells = <0>;
>> +		};
>
> In an SoC .dtsi file, you'd typically write:
>
> 	status = "disabled";
>
> ... in all nodes that represent IO controllers that interface to
> external HW, so that board DT files can/must explicitly choose to enable
> the device if it's actually in use on the board. Some systems might not
> have HDMI and hence might not hook up the HDMI_SCL/SDA pads.
>
> BCM2835-ARM-Peripherals.pdf states "Note that the BSC2 master is used
> dedicated with the HDMI interface and should not be accessed by user
> programs.". Does this imply the Linux kernel shouldn't be touching this
> I2C controller; that the VC4 firmware might also be attempting to use
> it? I wonder how any such sharing of the HW would work.

In order for *any* of this driver to work, we need to ensure that the
firmware doesn't try to write to the corresponding part of the hardware.
DDC I2C is no different.  All that will cause the firmware to do
anything with display is generating mbox/dispmanx requests (through the
firmware driver), and the undervoltage warnings.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150817/39e1921f/attachment.sig>

From eric at anholt.net  Mon Aug 17 11:47:55 2015
From: eric at anholt.net (Eric Anholt)
Date: Mon, 17 Aug 2015 11:47:55 -0700
Subject: [PATCH 2/7] MAINTAINERS: Add myself for the new VC4 (RPi GPU)
 graphics driver.
In-Reply-To: <55CEC29C.806@wwwdotorg.org>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-3-git-send-email-eric@anholt.net>
 <55CEC29C.806@wwwdotorg.org>
Message-ID: <871tf1iwf8.fsf@eliezer.anholt.net>

Stephen Warren <swarren at wwwdotorg.org> writes:

> On 08/12/2015 06:56 PM, Eric Anholt wrote:
>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>
>> +DRM DRIVERS FOR VC4
>> +M:	Eric Anholt <eric at anholt.net>
>> +T:	git git://github.com/anholt/linux
>> +S:	Maintained
>> +F:	drivers/gpu/drm/vc4/*
>
> S: Supported

Fixed.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150817/20d212eb/attachment.sig>

From eric at anholt.net  Thu Aug 13 16:05:40 2015
From: eric at anholt.net (Eric Anholt)
Date: Thu, 13 Aug 2015 16:05:40 -0700
Subject: [PATCH v5 1/3] clk: bcm2835: Add binding docs for the Raspberry Pi
 clock provider
In-Reply-To: <1439507142-2965-1-git-send-email-eric@anholt.net>
References: <1439507142-2965-1-git-send-email-eric@anholt.net>
Message-ID: <1439507142-2965-2-git-send-email-eric@anholt.net>

The hardware clocks are not controllable by the ARM, so we have to
make requests to the firmware to do so from the VPU side.  This will
let us replace fixed clocks in our DT with actual clock control (and
correct frequency information).

Signed-off-by: Eric Anholt <eric at anholt.net>
Acked-by: Stephen Warren <swarren at wwwdotorg.org>
Acked-by: Michael Turquette <mturquette at baylibre.com>
---

v2: Include the dt-bindings header in this commit instead of the next
    one.  Make the clock indices match the firmware clock IDs.  Rename
    the binding's compat string.  Move the firmware phandle to be
    under a vendor-specific namespace.
v3: Mention 'clk' in the subject instead of the more generic
    dt/bindings.

 .../clock/raspberrypi,bcm2835-firmware-clocks.txt  | 25 ++++++++++++++++++++++
 include/dt-bindings/clk/raspberrypi.h              | 23 ++++++++++++++++++++
 2 files changed, 48 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/raspberrypi,bcm2835-firmware-clocks.txt
 create mode 100644 include/dt-bindings/clk/raspberrypi.h

diff --git a/Documentation/devicetree/bindings/clock/raspberrypi,bcm2835-firmware-clocks.txt b/Documentation/devicetree/bindings/clock/raspberrypi,bcm2835-firmware-clocks.txt
new file mode 100644
index 0000000..0972602
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/raspberrypi,bcm2835-firmware-clocks.txt
@@ -0,0 +1,25 @@
+Raspberry Pi firmware clock provider.
+
+The Raspberry Pi architecture doesn't provide direct access to the
+CLOCKMAN peripheral from the ARM side, so Linux has to make requests
+to the VPU firmware to program them.
+
+This binding uses the common clock binding:
+Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible:		Should be "raspberrypi,bcm2835-firmware-clocks"
+
+- #clock-cells:		Shall have value <1>.  The permitted clock-specifier
+			  values can be found in
+			  include/dt-bindings/clk/raspberrypi.h.
+
+- raspberrypi,firmware:	Phandle to the firmware driver node.
+
+Example:
+
+firmware_clocks: firmware-clocks {
+	compatible = "raspberrypi,bcm2835-firmware-clocks";
+	#clock-cells = <1>;
+	raspberrypi,firmware = <&firmware>;
+};
diff --git a/include/dt-bindings/clk/raspberrypi.h b/include/dt-bindings/clk/raspberrypi.h
new file mode 100644
index 0000000..ceec90f
--- /dev/null
+++ b/include/dt-bindings/clk/raspberrypi.h
@@ -0,0 +1,23 @@
+#/*
+ *  Copyright ? 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _DT_BINDINGS_CLK_RASPBERRYPI_H
+#define _DT_BINDINGS_CLK_RASPBERRYPI_H
+
+#define RPI_CLOCK_EMMC	1
+#define RPI_CLOCK_UART0	2
+#define RPI_CLOCK_ARM	3
+#define RPI_CLOCK_CORE	4
+#define RPI_CLOCK_V3D	5
+#define RPI_CLOCK_H264	6
+#define RPI_CLOCK_ISP	7
+#define RPI_CLOCK_SDRAM	8
+#define RPI_CLOCK_PIXEL	9
+#define RPI_CLOCK_PWM	10
+
+#endif
-- 
2.1.4



From eric at anholt.net  Thu Aug 13 16:05:42 2015
From: eric at anholt.net (Eric Anholt)
Date: Thu, 13 Aug 2015 16:05:42 -0700
Subject: [PATCH v5 3/3] ARM: bcm2835: Add DT for the firmware clocks driver.
In-Reply-To: <1439507142-2965-1-git-send-email-eric@anholt.net>
References: <1439507142-2965-1-git-send-email-eric@anholt.net>
Message-ID: <1439507142-2965-4-git-send-email-eric@anholt.net>

Signed-off-by: Eric Anholt <eric at anholt.net>
Acked-by: Lee Jones <lee at kernel.org>
---

v2: Rename our compat string to mention bcm2835, and make our firmware
    phandle be under a vendor-namespaced property.

v3: Squashed in the patches to reference the other clocks, to avoid
    regressions now that we register all clocks at boot.  Dropped
    Stephen's ack, since he hadn't acked the new EMMC change yet.

 arch/arm/boot/dts/bcm2835-rpi.dtsi | 18 ++++++++++++++++++
 arch/arm/boot/dts/bcm2835.dtsi     |  3 +--
 2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi
index ab5474e..5d370cb 100644
--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi
+++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi
@@ -1,3 +1,4 @@
+#include <dt-bindings/clk/raspberrypi.h>
 #include "bcm2835.dtsi"
 
 / {
@@ -20,6 +21,12 @@
 			compatible = "raspberrypi,bcm2835-firmware";
 			mboxes = <&mailbox>;
 		};
+
+		firmware_clocks: firmware-clocks {
+			compatible = "raspberrypi,bcm2835-firmware-clocks";
+			#clock-cells = <1>;
+			raspberrypi,firmware = <&firmware>;
+		};
 	};
 };
 
@@ -55,4 +62,15 @@
 &sdhci {
 	status = "okay";
 	bus-width = <4>;
+	clocks = <&firmware_clocks RPI_CLOCK_EMMC>;
+};
+
+&uart0 {
+	clocks = <&firmware_clocks RPI_CLOCK_UART0>,
+		 <&firmware_clocks RPI_CLOCK_CORE>;
+	clock-names = "uartclk", "apb_pclk";
+};
+
+&spi {
+	clocks = <&firmware_clocks RPI_CLOCK_CORE>;
 };
diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
index 301c73f..5be2862 100644
--- a/arch/arm/boot/dts/bcm2835.dtsi
+++ b/arch/arm/boot/dts/bcm2835.dtsi
@@ -92,11 +92,10 @@
 			#interrupt-cells = <2>;
 		};
 
-		uart at 7e201000 {
+		uart0: uart at 7e201000 {
 			compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell";
 			reg = <0x7e201000 0x1000>;
 			interrupts = <2 25>;
-			clock-frequency = <3000000>;
 			arm,primecell-periphid = <0x00241011>;
 		};
 
-- 
2.1.4



From eric at anholt.net  Thu Aug 13 16:05:41 2015
From: eric at anholt.net (Eric Anholt)
Date: Thu, 13 Aug 2015 16:05:41 -0700
Subject: [PATCH v5 2/3] clk: Add a Raspberry Pi-specific clock driver.
In-Reply-To: <1439507142-2965-1-git-send-email-eric@anholt.net>
References: <1439507142-2965-1-git-send-email-eric@anholt.net>
Message-ID: <1439507142-2965-3-git-send-email-eric@anholt.net>

Unfortunately, the clock manager's registers are not accessible by the
ARM, so we have to request that the firmware modify our clocks for us.

This driver only registers the clocks at the point they are requested
by a client driver.  This is partially to support returning
-EPROBE_DEFER when the firmware driver isn't supported yet, but it
also avoids issues with disabling "unused" clocks due to them not yet
being connected to their consumers in the DT.

Signed-off-by: Eric Anholt <eric at anholt.net>
---

v2: Declare the mutex static (from review by Baruch Siach), merge
    description and copyright comments.

v3: Update for new rpi firmware API.  Update the compatible string.
    Make the firmware handle be under a vendor-namespaced property.
    Make the DT indices match the firmware clock IDs.  Move the driver
    under the firmware driver's Kconfig, since it requires it.  Move a
    container_of() from 2 callers into the callee.

v4: Update commit message to mention subsystem.  Add #defines for a
    couple of bitfield checks, and use the bitfield check for
    rpi_clk_is_on().  Return an error when appropriate in
    rpi_clk_is_on(), and don't return specific rpi_firmware_property()
    errors in other places when we were only returning -EINVAL.  Add
    some comment text describing how return values work from
    the firmware.

v5: Restructure as a platform_driver on suggestion from Michael
    Turquette.  This works around the -EPROBE_DEFER problem, so now we
    can register all of our clocks at probe time.  Store the current
    clock rate in the struct, and avoid asking for no-op changes.  It
    appears that a no-op change of the pixel clock while the clock is
    being used (at boot time when the KMS driver calls
    clk_prepare_enable()) actually breaks the display.  Dropped
    Stephen's ack since this is a pretty significant rewrite.

 drivers/clk/Makefile          |   1 +
 drivers/clk/clk-raspberrypi.c | 286 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 287 insertions(+)
 create mode 100644 drivers/clk/clk-raspberrypi.c

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index c4cf075..ad0a4a5 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_MACH_ASM9260)		+= clk-asm9260.o
 obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN)	+= clk-axi-clkgen.o
 obj-$(CONFIG_ARCH_AXXIA)		+= clk-axm5516.o
 obj-$(CONFIG_ARCH_BCM2835)		+= clk-bcm2835.o
+obj-$(CONFIG_RASPBERRYPI_FIRMWARE)	+= clk-raspberrypi.o
 obj-$(CONFIG_COMMON_CLK_CDCE706)	+= clk-cdce706.o
 obj-$(CONFIG_ARCH_CLPS711X)		+= clk-clps711x.o
 obj-$(CONFIG_ARCH_EFM32)		+= clk-efm32gg.o
diff --git a/drivers/clk/clk-raspberrypi.c b/drivers/clk/clk-raspberrypi.c
new file mode 100644
index 0000000..a65a028
--- /dev/null
+++ b/drivers/clk/clk-raspberrypi.c
@@ -0,0 +1,286 @@
+/*
+ * Implements a clock provider for the clocks controlled by the
+ * firmware on Raspberry Pi.
+ *
+ * These clocks are controlled by the CLOCKMAN peripheral in the
+ * hardware, but the ARM doesn't have access to the registers for
+ * them.  As a result, we have to call into the firmware to get it to
+ * enable, disable, and set their frequencies.
+ *
+ * We don't have an interface for getting the set of frequencies
+ * available from the hardware.  We can request a min/max, but other
+ * than that we have to request a frequency and take what it gives us.
+ *
+ * Copyright ? 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <dt-bindings/clk/raspberrypi.h>
+#include <soc/bcm2835/raspberrypi-firmware.h>
+
+#define RPI_FIRMWARE_CLOCK_STATE_ENABLED		(1 << 0)
+#define RPI_FIRMWARE_CLOCK_STATE_ERROR			(1 << 1)
+#define RPI_FIRMWARE_SET_CLOCK_RATE_ERROR		0
+
+static const struct {
+	const char *name;
+	int flags;
+} rpi_clocks[] = {
+	[RPI_CLOCK_EMMC] = { "emmc", CLK_IS_ROOT },
+	[RPI_CLOCK_UART0] = { "uart0", CLK_IS_ROOT },
+	[RPI_CLOCK_ARM] = { "arm", CLK_IS_ROOT | CLK_IGNORE_UNUSED },
+	[RPI_CLOCK_CORE] = { "core", CLK_IS_ROOT | CLK_IGNORE_UNUSED },
+	[RPI_CLOCK_V3D] = { "v3d", CLK_IS_ROOT },
+	[RPI_CLOCK_H264] = { "h264", CLK_IS_ROOT },
+	[RPI_CLOCK_ISP] = { "isp", CLK_IS_ROOT },
+	[RPI_CLOCK_SDRAM] = { "sdram", CLK_IS_ROOT | CLK_IGNORE_UNUSED },
+	[RPI_CLOCK_PIXEL] = { "pixel", CLK_IS_ROOT | CLK_IGNORE_UNUSED },
+	[RPI_CLOCK_PWM] = { "pwm", CLK_IS_ROOT },
+};
+
+struct rpi_firmware_clock {
+	/* Clock definitions in our static struct. */
+	const char *name;
+	int flags;
+
+	/* The rest are filled in at init time. */
+	struct clk_hw hw;
+	struct device *dev;
+	struct rpi_firmware *firmware;
+	uint32_t last_rate;
+	int id;
+};
+
+static int rpi_clk_is_on(struct clk_hw *hw)
+{
+	struct rpi_firmware_clock *rpi_clk =
+		container_of(hw, struct rpi_firmware_clock, hw);
+	u32 packet[2];
+	int ret;
+
+	packet[0] = rpi_clk->id;
+	packet[1] = 0;
+	ret = rpi_firmware_property(rpi_clk->firmware,
+				    RPI_FIRMWARE_GET_CLOCK_STATE,
+				    &packet, sizeof(packet));
+	/*
+	 * The second packet field has the new clock state returned in
+	 * the low bit, or an error flag in the second bit.
+	 */
+	if (ret || (packet[1] & RPI_FIRMWARE_CLOCK_STATE_ERROR)) {
+		dev_err(rpi_clk->dev, "Failed to get clock state\n");
+		return ret ? ret : -EINVAL;
+	}
+
+	return packet[1] & RPI_FIRMWARE_CLOCK_STATE_ENABLED;
+}
+
+static int rpi_clk_set_state(struct clk_hw *hw, bool on)
+{
+	struct rpi_firmware_clock *rpi_clk =
+		container_of(hw, struct rpi_firmware_clock, hw);
+	u32 packet[2];
+	int ret;
+
+	if (on == (rpi_clk->last_rate != 0))
+		return 0;
+
+	packet[0] = rpi_clk->id;
+	packet[1] = on;
+	ret = rpi_firmware_property(rpi_clk->firmware,
+				    RPI_FIRMWARE_SET_CLOCK_STATE,
+				    &packet, sizeof(packet));
+	/*
+	 * The second packet field has the new clock state returned in
+	 * the low bit, or an error flag in the second bit.
+	 */
+	if (ret || (packet[1] & RPI_FIRMWARE_CLOCK_STATE_ERROR)) {
+		dev_err(rpi_clk->dev, "Failed to set clock state\n");
+		return ret ? ret : -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rpi_clk_on(struct clk_hw *hw)
+{
+	return rpi_clk_set_state(hw, true);
+}
+
+static void rpi_clk_off(struct clk_hw *hw)
+{
+	rpi_clk_set_state(hw, false);
+}
+
+static unsigned long rpi_clk_get_rate(struct clk_hw *hw,
+				      unsigned long parent_rate)
+{
+	struct rpi_firmware_clock *rpi_clk =
+		container_of(hw, struct rpi_firmware_clock, hw);
+	u32 packet[2];
+	int ret;
+
+	packet[0] = rpi_clk->id;
+	packet[1] = 0;
+	ret = rpi_firmware_property(rpi_clk->firmware,
+				    RPI_FIRMWARE_GET_CLOCK_RATE,
+				    &packet, sizeof(packet));
+	/*
+	 * Note that the second packet field returns 0 on an unknown
+	 * clock error, which would also be a reasonable value for a
+	 * clock that's off.
+	 */
+	if (ret) {
+		dev_err(rpi_clk->dev, "Failed to get clock rate\n");
+		return 0;
+	}
+
+	rpi_clk->last_rate = packet[1];
+
+	return packet[1];
+}
+
+static int rpi_clk_set_rate(struct clk_hw *hw,
+			    unsigned long rate, unsigned long parent_rate)
+{
+	struct rpi_firmware_clock *rpi_clk =
+		container_of(hw, struct rpi_firmware_clock, hw);
+	u32 packet[2];
+	int ret;
+
+	if (rate == rpi_clk->last_rate)
+		return 0;
+
+	packet[0] = rpi_clk->id;
+	packet[1] = rate;
+	ret = rpi_firmware_property(rpi_clk->firmware,
+				    RPI_FIRMWARE_SET_CLOCK_RATE,
+				    &packet, sizeof(packet));
+	/*
+	 * The second packet field has the new clock rate returned, or
+	 * 0 on error.
+	 */
+	if (ret || packet[1] == RPI_FIRMWARE_SET_CLOCK_RATE_ERROR) {
+		dev_err(rpi_clk->dev, "Failed to set clock rate\n");
+		return ret;
+	}
+
+	rpi_clk->last_rate = packet[1];
+
+	return 0;
+}
+
+static long rpi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+			       unsigned long *parent_rate)
+{
+	/*
+	 * The firmware will end up rounding our rate to something,
+	 * but we don't have an interface for it.  Just return the
+	 * requested value, and it'll get updated after the clock gets
+	 * set.
+	 */
+	return rate;
+}
+
+static struct clk_ops rpi_clk_ops = {
+	.is_prepared = rpi_clk_is_on,
+	.prepare = rpi_clk_on,
+	.unprepare = rpi_clk_off,
+	.recalc_rate = rpi_clk_get_rate,
+	.set_rate = rpi_clk_set_rate,
+	.round_rate = rpi_clk_round_rate,
+};
+
+static int rpi_clk_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *firmware_node;
+	struct rpi_firmware *firmware;
+	struct clk_init_data init;
+	int i;
+	struct clk_onecell_data *onecell;
+
+	firmware_node = of_parse_phandle(dev->of_node,
+					 "raspberrypi,firmware", 0);
+	if (!firmware_node) {
+		dev_err(dev, "Missing firmware node\n");
+		return -ENODEV;
+	}
+	firmware = rpi_firmware_get(firmware_node);
+	if (!firmware)
+		return -EPROBE_DEFER;
+
+	onecell = devm_kmalloc(dev, sizeof(*onecell), GFP_KERNEL);
+	if (!onecell)
+		return -ENOMEM;
+	onecell->clk_num = ARRAY_SIZE(rpi_clocks);
+	onecell->clks = devm_kzalloc(dev, sizeof(*onecell->clks), GFP_KERNEL);
+	if (!onecell->clks)
+		return -ENOMEM;
+
+	memset(&init, 0, sizeof(init));
+	init.ops = &rpi_clk_ops;
+
+	for (i = 0; i < ARRAY_SIZE(rpi_clocks); i++) {
+		struct rpi_firmware_clock *rpi_clk;
+
+		if (!rpi_clocks[i].name)
+			continue;
+
+		rpi_clk = devm_kzalloc(dev, sizeof(*rpi_clk), GFP_KERNEL);
+		if (!rpi_clk)
+			return -ENOMEM;
+
+		rpi_clk->name = rpi_clocks[i].name;
+		rpi_clk->firmware = firmware;
+		rpi_clk->dev = dev;
+		rpi_clk->hw.init = &init;
+		rpi_clk->id = i;
+		init.name = rpi_clocks[i].name;
+		init.flags = rpi_clocks[i].flags;
+
+		onecell->clks[i] = devm_clk_register(&pdev->dev, &rpi_clk->hw);
+		if (IS_ERR(onecell->clks[i]))
+			return PTR_ERR(onecell->clks[i]);
+
+		/* Get the current clock rate/state, to avoid extra on
+		 * to on transitions at boot.
+		 */
+		rpi_clk_get_rate(&rpi_clk->hw, 0);
+	}
+
+	return of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get,
+				   onecell);
+}
+
+static int rpi_clk_remove(struct platform_device *pdev)
+{
+	of_clk_del_provider(pdev->dev.of_node);
+
+	return 0;
+}
+
+static const struct of_device_id rpi_clk_of_match[] = {
+	{ .compatible = "raspberrypi,bcm2835-firmware-clocks", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, rpi_clk_of_match);
+
+static struct platform_driver rpi_clk_driver = {
+	.driver = {
+		.name = "raspberrypi-clk",
+		.of_match_table = rpi_clk_of_match,
+	},
+	.probe		= rpi_clk_probe,
+	.remove		= rpi_clk_remove,
+};
+module_platform_driver(rpi_clk_driver);
+
+MODULE_AUTHOR("Eric Anholt <eric at anholt.net>");
+MODULE_DESCRIPTION("Raspberry Pi clock driver");
+MODULE_LICENSE("GPL v2");
-- 
2.1.4



From robherring2 at gmail.com  Fri Aug 14 13:29:03 2015
From: robherring2 at gmail.com (Rob Herring)
Date: Fri, 14 Aug 2015 15:29:03 -0500
Subject: [PATCH] arm: rpi: Device tree modifications for U-Boot
In-Reply-To: <CAPnjgZ0hzyjy=8o8ocaGCoOC7Js4CirhystidbvtabzbhRi4RQ@mail.gmail.com>
References: <1439303153-12171-1-git-send-email-sjg@chromium.org>
 <1439312701.2451.3.camel@lynxeye.de>
 <CAPnjgZ3ztXnkxZJvitiXiKMzq5+x46hbv0PW4HcF7yGgsXUV+Q@mail.gmail.com>
 <CAPnjgZ0hzyjy=8o8ocaGCoOC7Js4CirhystidbvtabzbhRi4RQ@mail.gmail.com>
Message-ID: <CAL_Jsq++ihz6svJAKWwT7-=KyJvyWXjdf=tZb4aDGgV_Dc=-Nw@mail.gmail.com>

On Fri, Aug 14, 2015 at 1:34 PM, Simon Glass <sjg at chromium.org> wrote:
> -linux-tegra
>
> Hi,
>
> On 12 August 2015 at 07:21, Simon Glass <sjg at chromium.org> wrote:
>> Hi Lucas,
>>
>> On 11 August 2015 at 11:05, Lucas Stach <dev at lynxeye.de> wrote:
>>> Hi Simon,
>>>
>>> why did you send this to the Tegra ML?
>>>
>>> Am Dienstag, den 11.08.2015, 08:25 -0600 schrieb Simon Glass:
>>>> This updates the device tree from the kernel version to something suitable
>>>> for U-Boot:
>>>>
>>>> - Add stdout-path alias for console
>>>> - Mark the /soc node to be available pre-relocation so that the early
>>>> serial console works (we need the 'ranges' property to be available)

I find it quite strange that you must explicitly enable the parent
node, but not the uart node.

>>>>
>>>> Signed-off-by: Simon Glass <sjg at chromium.org>
>>>> ---
>>>>
>>>>  arch/arm/boot/dts/bcm2835.dtsi | 4 +++-
>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
>>>> index 301c73f..bd6bff6 100644
>>>> --- a/arch/arm/boot/dts/bcm2835.dtsi
>>>> +++ b/arch/arm/boot/dts/bcm2835.dtsi
>>>> @@ -8,6 +8,7 @@
>>>>
>>>>       chosen {
>>>>               bootargs = "earlyprintk console=ttyAMA0";
>>>> +             stdout-path = &uart;
>>>>       };
>>>>
>>>>       soc {
>>>> @@ -16,6 +17,7 @@
>>>>               #size-cells = <1>;
>>>>               ranges = <0x7e000000 0x20000000 0x02000000>;
>>>>               dma-ranges = <0x40000000 0x00000000 0x20000000>;
>>>> +             u-boot,dm-pre-reloc;
>>>
>>> Why do you need this and why should upstream carry your favourite
>>> bootloaders configuration? This is in no way hardware description.
>>
>> I'm not sure how much you know about U-Boot, so let me know if you
>> need more info.
>>
>> U-Boot normally starts up by setting up its serial UART and displaying
>> a banner message. At this stage typically only a few devices are
>> initialised (e.g. maybe just the UART). It then relocates itself to
>> the top of memory and starts up all the devices. It throws away any
>> previous devices that it set up before relocation and starts again.
>>
>> U-Boot uses a thing called driver model (dm) which handles driver
>> binding and probing. Driver model has the device tree and would
>> normally scan through it and create devices for everything it finds.

How do you debug the DM itself? It seems like you still would need
something earlier for debug like earlycon in the kernel. u-boot DM is
probably simple enough you can get away with using it early, but you
often can't as the complexity increases. Ultimately you need something
simple that just hits all the registers needed to get characters out.
What happens when you add pinmux, clocks, PMIC, power domains, etc. to
the DM and they all become dependencies for the UART?

>> Before relocation we don't need every device. Also the CPU is often
>> running slowly, perhaps without the cache enabled. SDRAM may not be
>> available yet so space is short. We want to avoid starting up things
>> that will not be used.
>>
>> So this property indicates that the device is needed before relocation
>> and should be set up by driver model. We need it to avoid a very slow
>> and memory-hungry startup.

Can't the need for that property change over time? Either as more
drivers are converted to DM you need to add this or you add some
feature that depends on a driver (e.g. get a board rev or boot mode
from GPIO). You would have backwards compatibility issues with this.
I'm somewhat less worried about that for u-boot as we should be
bundling the dtb and bootloader rather than kernel and dtb. For the
UART, you can just get which UART to initialize early from
stdout-path. But for other cases, couldn't you just have the platform
provide the list of needed drivers. Then when u-boot needs change, you
just change u-boot.

>> As to why upstream should accept it, my understanding of upstream is
>> that people can send patches to it and in fact are encouraged to do
>> so, to avoid misunderstandings and duplication. The device tree files
>> are stored in Linux so any binding or source file changes should end
>> up there. Otherwise the files tend to diverge and we end up with
>> multiple bindings and multiple versions of the same source file.
>
> Is the above explanation sufficient? Will this patch be applied?

Well, for starters bindings need to be documented, so you need to send
this as a binding doc with this detail.

Rob


From swarren at wwwdotorg.org  Fri Aug 14 20:46:15 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Fri, 14 Aug 2015 21:46:15 -0600
Subject: [PATCH] arm: rpi: Device tree modifications for U-Boot
In-Reply-To: <CAPnjgZ3ztXnkxZJvitiXiKMzq5+x46hbv0PW4HcF7yGgsXUV+Q@mail.gmail.com>
References: <1439303153-12171-1-git-send-email-sjg@chromium.org>
 <1439312701.2451.3.camel@lynxeye.de>
 <CAPnjgZ3ztXnkxZJvitiXiKMzq5+x46hbv0PW4HcF7yGgsXUV+Q@mail.gmail.com>
Message-ID: <55CEB607.7060908@wwwdotorg.org>

On 08/12/2015 07:21 AM, Simon Glass wrote:
> Hi Lucas,
> 
> On 11 August 2015 at 11:05, Lucas Stach <dev at lynxeye.de> wrote:
>> Hi Simon,
>>
>> why did you send this to the Tegra ML?
>>
>> Am Dienstag, den 11.08.2015, 08:25 -0600 schrieb Simon Glass:
>>> This updates the device tree from the kernel version to something suitable
>>> for U-Boot:
>>>
>>> - Add stdout-path alias for console
>>> - Mark the /soc node to be available pre-relocation so that the early
>>> serial console works (we need the 'ranges' property to be available)
>>>
>>> Signed-off-by: Simon Glass <sjg at chromium.org>
>>> ---
>>>
>>>  arch/arm/boot/dts/bcm2835.dtsi | 4 +++-
>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
>>> index 301c73f..bd6bff6 100644
>>> --- a/arch/arm/boot/dts/bcm2835.dtsi
>>> +++ b/arch/arm/boot/dts/bcm2835.dtsi
>>> @@ -8,6 +8,7 @@
>>>
>>>       chosen {
>>>               bootargs = "earlyprintk console=ttyAMA0";
>>> +             stdout-path = &uart;
>>>       };
>>>
>>>       soc {
>>> @@ -16,6 +17,7 @@
>>>               #size-cells = <1>;
>>>               ranges = <0x7e000000 0x20000000 0x02000000>;
>>>               dma-ranges = <0x40000000 0x00000000 0x20000000>;
>>> +             u-boot,dm-pre-reloc;
>>
>> Why do you need this and why should upstream carry your favourite
>> bootloaders configuration? This is in no way hardware description.
> 
> I'm not sure how much you know about U-Boot, so let me know if you
> need more info.
> 
> U-Boot normally starts up by setting up its serial UART and displaying
> a banner message. At this stage typically only a few devices are
> initialised (e.g. maybe just the UART). It then relocates itself to
> the top of memory and starts up all the devices. It throws away any
> previous devices that it set up before relocation and starts again.
> 
> U-Boot uses a thing called driver model (dm) which handles driver
> binding and probing. Driver model has the device tree and would
> normally scan through it and create devices for everything it finds.
> 
> Before relocation we don't need every device. Also the CPU is often
> running slowly, perhaps without the cache enabled. SDRAM may not be
> available yet so space is short. We want to avoid starting up things
> that will not be used.
> 
> So this property indicates that the device is needed before relocation
> and should be set up by driver model. We need it to avoid a very slow
> and memory-hungry startup.
> 
> As to why upstream should accept it, my understanding of upstream is
> that people can send patches to it and in fact are encouraged to do
> so, to avoid misunderstandings and duplication. The device tree files
> are stored in Linux so any binding or source file changes should end
> up there. Otherwise the files tend to diverge and we end up with
> multiple bindings and multiple versions of the same source file.

On many platforms, we have U-Boot SPL running first, then the main
U-Boot. The main U-Boot binary contains both the code to do the
relocation and the binary that runs after relocation. It seems like it'd
be simpler to split these up into 3 binaries that each do a single job:

1) SPL, roughly as-is today (varying jobs depending on platform)

2) Relocator, which does nothing but work out where to copy U-Boot,
memcpy()s it there, relocates the image (if not PIE), and jumps to it.

3) The main U-Boot.

Item (2) above should be simple enough that it can use a very simple
debug mechanism rather like DEBUG_LL in the Linux kernel. Similar to
what Rob mentioned in his email.

Item (3) could use DM and DT/ACPI/... to get device information in a
complete non-hard-coded manner.


From sjg at chromium.org  Wed Aug 12 06:21:32 2015
From: sjg at chromium.org (Simon Glass)
Date: Wed, 12 Aug 2015 07:21:32 -0600
Subject: [PATCH] arm: rpi: Device tree modifications for U-Boot
In-Reply-To: <1439312701.2451.3.camel@lynxeye.de>
References: <1439303153-12171-1-git-send-email-sjg@chromium.org>
 <1439312701.2451.3.camel@lynxeye.de>
Message-ID: <CAPnjgZ3ztXnkxZJvitiXiKMzq5+x46hbv0PW4HcF7yGgsXUV+Q@mail.gmail.com>

Hi Lucas,

On 11 August 2015 at 11:05, Lucas Stach <dev at lynxeye.de> wrote:
> Hi Simon,
>
> why did you send this to the Tegra ML?
>
> Am Dienstag, den 11.08.2015, 08:25 -0600 schrieb Simon Glass:
>> This updates the device tree from the kernel version to something suitable
>> for U-Boot:
>>
>> - Add stdout-path alias for console
>> - Mark the /soc node to be available pre-relocation so that the early
>> serial console works (we need the 'ranges' property to be available)
>>
>> Signed-off-by: Simon Glass <sjg at chromium.org>
>> ---
>>
>>  arch/arm/boot/dts/bcm2835.dtsi | 4 +++-
>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
>> index 301c73f..bd6bff6 100644
>> --- a/arch/arm/boot/dts/bcm2835.dtsi
>> +++ b/arch/arm/boot/dts/bcm2835.dtsi
>> @@ -8,6 +8,7 @@
>>
>>       chosen {
>>               bootargs = "earlyprintk console=ttyAMA0";
>> +             stdout-path = &uart;
>>       };
>>
>>       soc {
>> @@ -16,6 +17,7 @@
>>               #size-cells = <1>;
>>               ranges = <0x7e000000 0x20000000 0x02000000>;
>>               dma-ranges = <0x40000000 0x00000000 0x20000000>;
>> +             u-boot,dm-pre-reloc;
>
> Why do you need this and why should upstream carry your favourite
> bootloaders configuration? This is in no way hardware description.

I'm not sure how much you know about U-Boot, so let me know if you
need more info.

U-Boot normally starts up by setting up its serial UART and displaying
a banner message. At this stage typically only a few devices are
initialised (e.g. maybe just the UART). It then relocates itself to
the top of memory and starts up all the devices. It throws away any
previous devices that it set up before relocation and starts again.

U-Boot uses a thing called driver model (dm) which handles driver
binding and probing. Driver model has the device tree and would
normally scan through it and create devices for everything it finds.

Before relocation we don't need every device. Also the CPU is often
running slowly, perhaps without the cache enabled. SDRAM may not be
available yet so space is short. We want to avoid starting up things
that will not be used.

So this property indicates that the device is needed before relocation
and should be set up by driver model. We need it to avoid a very slow
and memory-hungry startup.

As to why upstream should accept it, my understanding of upstream is
that people can send patches to it and in fact are encouraged to do
so, to avoid misunderstandings and duplication. The device tree files
are stored in Linux so any binding or source file changes should end
up there. Otherwise the files tend to diverge and we end up with
multiple bindings and multiple versions of the same source file.

Regards,
Simon


From sjg at chromium.org  Fri Aug 14 11:34:41 2015
From: sjg at chromium.org (Simon Glass)
Date: Fri, 14 Aug 2015 12:34:41 -0600
Subject: [PATCH] arm: rpi: Device tree modifications for U-Boot
In-Reply-To: <CAPnjgZ3ztXnkxZJvitiXiKMzq5+x46hbv0PW4HcF7yGgsXUV+Q@mail.gmail.com>
References: <1439303153-12171-1-git-send-email-sjg@chromium.org>
 <1439312701.2451.3.camel@lynxeye.de>
 <CAPnjgZ3ztXnkxZJvitiXiKMzq5+x46hbv0PW4HcF7yGgsXUV+Q@mail.gmail.com>
Message-ID: <CAPnjgZ0hzyjy=8o8ocaGCoOC7Js4CirhystidbvtabzbhRi4RQ@mail.gmail.com>

-linux-tegra

Hi,

On 12 August 2015 at 07:21, Simon Glass <sjg at chromium.org> wrote:
> Hi Lucas,
>
> On 11 August 2015 at 11:05, Lucas Stach <dev at lynxeye.de> wrote:
>> Hi Simon,
>>
>> why did you send this to the Tegra ML?
>>
>> Am Dienstag, den 11.08.2015, 08:25 -0600 schrieb Simon Glass:
>>> This updates the device tree from the kernel version to something suitable
>>> for U-Boot:
>>>
>>> - Add stdout-path alias for console
>>> - Mark the /soc node to be available pre-relocation so that the early
>>> serial console works (we need the 'ranges' property to be available)
>>>
>>> Signed-off-by: Simon Glass <sjg at chromium.org>
>>> ---
>>>
>>>  arch/arm/boot/dts/bcm2835.dtsi | 4 +++-
>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
>>> index 301c73f..bd6bff6 100644
>>> --- a/arch/arm/boot/dts/bcm2835.dtsi
>>> +++ b/arch/arm/boot/dts/bcm2835.dtsi
>>> @@ -8,6 +8,7 @@
>>>
>>>       chosen {
>>>               bootargs = "earlyprintk console=ttyAMA0";
>>> +             stdout-path = &uart;
>>>       };
>>>
>>>       soc {
>>> @@ -16,6 +17,7 @@
>>>               #size-cells = <1>;
>>>               ranges = <0x7e000000 0x20000000 0x02000000>;
>>>               dma-ranges = <0x40000000 0x00000000 0x20000000>;
>>> +             u-boot,dm-pre-reloc;
>>
>> Why do you need this and why should upstream carry your favourite
>> bootloaders configuration? This is in no way hardware description.
>
> I'm not sure how much you know about U-Boot, so let me know if you
> need more info.
>
> U-Boot normally starts up by setting up its serial UART and displaying
> a banner message. At this stage typically only a few devices are
> initialised (e.g. maybe just the UART). It then relocates itself to
> the top of memory and starts up all the devices. It throws away any
> previous devices that it set up before relocation and starts again.
>
> U-Boot uses a thing called driver model (dm) which handles driver
> binding and probing. Driver model has the device tree and would
> normally scan through it and create devices for everything it finds.
>
> Before relocation we don't need every device. Also the CPU is often
> running slowly, perhaps without the cache enabled. SDRAM may not be
> available yet so space is short. We want to avoid starting up things
> that will not be used.
>
> So this property indicates that the device is needed before relocation
> and should be set up by driver model. We need it to avoid a very slow
> and memory-hungry startup.
>
> As to why upstream should accept it, my understanding of upstream is
> that people can send patches to it and in fact are encouraged to do
> so, to avoid misunderstandings and duplication. The device tree files
> are stored in Linux so any binding or source file changes should end
> up there. Otherwise the files tend to diverge and we end up with
> multiple bindings and multiple versions of the same source file.

Is the above explanation sufficient? Will this patch be applied?

Regards,
Simon


From sjg at chromium.org  Sat Aug 15 06:46:38 2015
From: sjg at chromium.org (Simon Glass)
Date: Sat, 15 Aug 2015 07:46:38 -0600
Subject: [PATCH] arm: rpi: Device tree modifications for U-Boot
In-Reply-To: <CAL_Jsq++ihz6svJAKWwT7-=KyJvyWXjdf=tZb4aDGgV_Dc=-Nw@mail.gmail.com>
References: <1439303153-12171-1-git-send-email-sjg@chromium.org>
 <1439312701.2451.3.camel@lynxeye.de>
 <CAPnjgZ3ztXnkxZJvitiXiKMzq5+x46hbv0PW4HcF7yGgsXUV+Q@mail.gmail.com>
 <CAPnjgZ0hzyjy=8o8ocaGCoOC7Js4CirhystidbvtabzbhRi4RQ@mail.gmail.com>
 <CAL_Jsq++ihz6svJAKWwT7-=KyJvyWXjdf=tZb4aDGgV_Dc=-Nw@mail.gmail.com>
Message-ID: <CAPnjgZ3ubK_5j4AV4wO6xgoRf8iN+G8YSQm0pSW5fVzwpxYu5Q@mail.gmail.com>

Hi Rob,

On 14 August 2015 at 14:29, Rob Herring <robherring2 at gmail.com> wrote:
> On Fri, Aug 14, 2015 at 1:34 PM, Simon Glass <sjg at chromium.org> wrote:
>> -linux-tegra
>>
>> Hi,
>>
>> On 12 August 2015 at 07:21, Simon Glass <sjg at chromium.org> wrote:
>>> Hi Lucas,
>>>
>>> On 11 August 2015 at 11:05, Lucas Stach <dev at lynxeye.de> wrote:
>>>> Hi Simon,
>>>>
>>>> why did you send this to the Tegra ML?
>>>>
>>>> Am Dienstag, den 11.08.2015, 08:25 -0600 schrieb Simon Glass:
>>>>> This updates the device tree from the kernel version to something suitable
>>>>> for U-Boot:
>>>>>
>>>>> - Add stdout-path alias for console
>>>>> - Mark the /soc node to be available pre-relocation so that the early
>>>>> serial console works (we need the 'ranges' property to be available)
>
> I find it quite strange that you must explicitly enable the parent
> node, but not the uart node.

U-Boot tries hard to find and bind the UART using the stdout-path
link. I would like to add it in the UART node also but we were able to
work around it so far.

>
>>>>>
>>>>> Signed-off-by: Simon Glass <sjg at chromium.org>
>>>>> ---
>>>>>
>>>>>  arch/arm/boot/dts/bcm2835.dtsi | 4 +++-
>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
>>>>> index 301c73f..bd6bff6 100644
>>>>> --- a/arch/arm/boot/dts/bcm2835.dtsi
>>>>> +++ b/arch/arm/boot/dts/bcm2835.dtsi
>>>>> @@ -8,6 +8,7 @@
>>>>>
>>>>>       chosen {
>>>>>               bootargs = "earlyprintk console=ttyAMA0";
>>>>> +             stdout-path = &uart;
>>>>>       };
>>>>>
>>>>>       soc {
>>>>> @@ -16,6 +17,7 @@
>>>>>               #size-cells = <1>;
>>>>>               ranges = <0x7e000000 0x20000000 0x02000000>;
>>>>>               dma-ranges = <0x40000000 0x00000000 0x20000000>;
>>>>> +             u-boot,dm-pre-reloc;
>>>>
>>>> Why do you need this and why should upstream carry your favourite
>>>> bootloaders configuration? This is in no way hardware description.
>>>
>>> I'm not sure how much you know about U-Boot, so let me know if you
>>> need more info.
>>>
>>> U-Boot normally starts up by setting up its serial UART and displaying
>>> a banner message. At this stage typically only a few devices are
>>> initialised (e.g. maybe just the UART). It then relocates itself to
>>> the top of memory and starts up all the devices. It throws away any
>>> previous devices that it set up before relocation and starts again.
>>>
>>> U-Boot uses a thing called driver model (dm) which handles driver
>>> binding and probing. Driver model has the device tree and would
>>> normally scan through it and create devices for everything it finds.
>
> How do you debug the DM itself? It seems like you still would need
> something earlier for debug like earlycon in the kernel. u-boot DM is
> probably simple enough you can get away with using it early, but you
> often can't as the complexity increases. Ultimately you need something
> simple that just hits all the registers needed to get characters out.
> What happens when you add pinmux, clocks, PMIC, power domains, etc. to
> the DM and they all become dependencies for the UART?

This doesn't seem like a device tree binding question but let me try
to answer it.

This is a problem - one of the challenges of driver model is that the
UART gets further away from the reset vector.

In U-Boot there is a single debug UART which can be supported by the
various serial drivers (only one can be selected on each platform).
There is a special debug_uart_init() function which is supposed to set
up the UART for that platform. After that, and until driver model UART
is up, console output can go through the debug UART.

>
>>> Before relocation we don't need every device. Also the CPU is often
>>> running slowly, perhaps without the cache enabled. SDRAM may not be
>>> available yet so space is short. We want to avoid starting up things
>>> that will not be used.
>>>
>>> So this property indicates that the device is needed before relocation
>>> and should be set up by driver model. We need it to avoid a very slow
>>> and memory-hungry startup.
>
> Can't the need for that property change over time? Either as more
> drivers are converted to DM you need to add this or you add some
> feature that depends on a driver (e.g. get a board rev or boot mode
> from GPIO). You would have backwards compatibility issues with this.
> I'm somewhat less worried about that for u-boot as we should be
> bundling the dtb and bootloader rather than kernel and dtb. For the
> UART, you can just get which UART to initialize early from
> stdout-path. But for other cases, couldn't you just have the platform
> provide the list of needed drivers. Then when u-boot needs change, you
> just change u-boot.

Yes U-Boot and its device tree are normally built from the same tree
at the same time. We don't expect to have to support an older or newer
device tree with the same U-Boot binary. So I don't see a problem
here.

The UART should have as few dependencies as possible. But if the
particular platform needs to check a GPIO to find out the UART number
(i.e. stdout-path cannot be used) then we would need to support that
case. It hasn't come up yet but it might. I certainly see platforms
where we need some other nodes before relocation and would like to
mark them as such. But I hope that for UART we can generally use
stdout-path.

The list of drivers doesn't help that much. What would I do with such
a list, and where would I keep such platform-specific information
other than in the device tree? It seems much simpler to hold the
information about what the platform needs to get through early boot in
one place. Then we have just a few lines of generic code to deal with
it.

>
>>> As to why upstream should accept it, my understanding of upstream is
>>> that people can send patches to it and in fact are encouraged to do
>>> so, to avoid misunderstandings and duplication. The device tree files
>>> are stored in Linux so any binding or source file changes should end
>>> up there. Otherwise the files tend to diverge and we end up with
>>> multiple bindings and multiple versions of the same source file.
>>
>> Is the above explanation sufficient? Will this patch be applied?
>
> Well, for starters bindings need to be documented, so you need to send
> this as a binding doc with this detail.

OK I'll update the patch and resend it.

Regards.
Simon


From sjg at chromium.org  Sat Aug 15 06:47:00 2015
From: sjg at chromium.org (Simon Glass)
Date: Sat, 15 Aug 2015 07:47:00 -0600
Subject: [PATCH] arm: rpi: Device tree modifications for U-Boot
In-Reply-To: <55CEB607.7060908@wwwdotorg.org>
References: <1439303153-12171-1-git-send-email-sjg@chromium.org>
 <1439312701.2451.3.camel@lynxeye.de>
 <CAPnjgZ3ztXnkxZJvitiXiKMzq5+x46hbv0PW4HcF7yGgsXUV+Q@mail.gmail.com>
 <55CEB607.7060908@wwwdotorg.org>
Message-ID: <CAPnjgZ1KxqDf2o_UnsmbWs9DZJpupj6D2trBACrJm-eT1g_xVA@mail.gmail.com>

Hi Stephen,

On 14 August 2015 at 21:46, Stephen Warren <swarren at wwwdotorg.org> wrote:
> On 08/12/2015 07:21 AM, Simon Glass wrote:
>> Hi Lucas,
>>
>> On 11 August 2015 at 11:05, Lucas Stach <dev at lynxeye.de> wrote:
>>> Hi Simon,
>>>
>>> why did you send this to the Tegra ML?
>>>
>>> Am Dienstag, den 11.08.2015, 08:25 -0600 schrieb Simon Glass:
>>>> This updates the device tree from the kernel version to something suitable
>>>> for U-Boot:
>>>>
>>>> - Add stdout-path alias for console
>>>> - Mark the /soc node to be available pre-relocation so that the early
>>>> serial console works (we need the 'ranges' property to be available)
>>>>
>>>> Signed-off-by: Simon Glass <sjg at chromium.org>
>>>> ---
>>>>
>>>>  arch/arm/boot/dts/bcm2835.dtsi | 4 +++-
>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
>>>> index 301c73f..bd6bff6 100644
>>>> --- a/arch/arm/boot/dts/bcm2835.dtsi
>>>> +++ b/arch/arm/boot/dts/bcm2835.dtsi
>>>> @@ -8,6 +8,7 @@
>>>>
>>>>       chosen {
>>>>               bootargs = "earlyprintk console=ttyAMA0";
>>>> +             stdout-path = &uart;
>>>>       };
>>>>
>>>>       soc {
>>>> @@ -16,6 +17,7 @@
>>>>               #size-cells = <1>;
>>>>               ranges = <0x7e000000 0x20000000 0x02000000>;
>>>>               dma-ranges = <0x40000000 0x00000000 0x20000000>;
>>>> +             u-boot,dm-pre-reloc;
>>>
>>> Why do you need this and why should upstream carry your favourite
>>> bootloaders configuration? This is in no way hardware description.
>>
>> I'm not sure how much you know about U-Boot, so let me know if you
>> need more info.
>>
>> U-Boot normally starts up by setting up its serial UART and displaying
>> a banner message. At this stage typically only a few devices are
>> initialised (e.g. maybe just the UART). It then relocates itself to
>> the top of memory and starts up all the devices. It throws away any
>> previous devices that it set up before relocation and starts again.
>>
>> U-Boot uses a thing called driver model (dm) which handles driver
>> binding and probing. Driver model has the device tree and would
>> normally scan through it and create devices for everything it finds.
>>
>> Before relocation we don't need every device. Also the CPU is often
>> running slowly, perhaps without the cache enabled. SDRAM may not be
>> available yet so space is short. We want to avoid starting up things
>> that will not be used.
>>
>> So this property indicates that the device is needed before relocation
>> and should be set up by driver model. We need it to avoid a very slow
>> and memory-hungry startup.
>>
>> As to why upstream should accept it, my understanding of upstream is
>> that people can send patches to it and in fact are encouraged to do
>> so, to avoid misunderstandings and duplication. The device tree files
>> are stored in Linux so any binding or source file changes should end
>> up there. Otherwise the files tend to diverge and we end up with
>> multiple bindings and multiple versions of the same source file.
>
> On many platforms, we have U-Boot SPL running first, then the main
> U-Boot. The main U-Boot binary contains both the code to do the
> relocation and the binary that runs after relocation. It seems like it'd
> be simpler to split these up into 3 binaries that each do a single job:
>
> 1) SPL, roughly as-is today (varying jobs depending on platform)
>
> 2) Relocator, which does nothing but work out where to copy U-Boot,
> memcpy()s it there, relocates the image (if not PIE), and jumps to it.
>
> 3) The main U-Boot.
>
> Item (2) above should be simple enough that it can use a very simple
> debug mechanism rather like DEBUG_LL in the Linux kernel. Similar to
> what Rob mentioned in his email.
>
> Item (3) could use DM and DT/ACPI/... to get device information in a
> complete non-hard-coded manner.

This comment does no seem to relate to my patch. We could certainly
re-architect U-Boot to work this way. There are  lot of reasons why
U-Boot works as it does and many platforms don't have SPL.

Relating what you said to the current U-Boot, your item (2) is
analogous to us not setting up driver model before relocation at all,
and just having a debug UART. That's a huge topic though, well beyond
the scope of my original patch. I think it would be better for you to
start a thread on the U-Boot mailing list with your proposal. At least
on x86 (which has no SPL) there are all sorts of things that currently
happen before relocation.

Regards,
Simon


From eric at anholt.net  Tue Aug 18 13:56:24 2015
From: eric at anholt.net (Eric Anholt)
Date: Tue, 18 Aug 2015 13:56:24 -0700
Subject: [PATCH 3/7] drm/vc4: Add KMS support for Raspberry Pi.
In-Reply-To: <20150813211755.GE17734@phenom.ffwll.local>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-4-git-send-email-eric@anholt.net>
 <20150813075141.GX17734@phenom.ffwll.local>
 <87d1yqq5po.fsf@eliezer.anholt.net>
 <20150813211755.GE17734@phenom.ffwll.local>
Message-ID: <87y4h8iadj.fsf@eliezer.anholt.net>

Daniel Vetter <daniel at ffwll.ch> writes:

> On Thu, Aug 13, 2015 at 01:44:03PM -0700, Eric Anholt wrote:
>> Daniel Vetter <daniel at ffwll.ch> writes:
>> 
>> > On Wed, Aug 12, 2015 at 05:56:16PM -0700, Eric Anholt wrote:
>> >> This is the start of a full VC4 driver.  Right now this just supports
>> >> configuring the display using a pre-existing video mode (because
>> >> changing the pixel clock isn't available yet, and doesn't work when it
>> >> is).  However, this is enough for fbcon and bringing up X using
>> >> xf86-video-modesetting.
>> >> 
>> >> Signed-off-by: Eric Anholt <eric at anholt.net>
>> >> ---
>> >>  drivers/gpu/drm/Kconfig           |   2 +
>> >>  drivers/gpu/drm/Makefile          |   1 +
>> >>  drivers/gpu/drm/vc4/Kconfig       |  14 +
>> >>  drivers/gpu/drm/vc4/Makefile      |  18 ++
>> >>  drivers/gpu/drm/vc4/vc4_bo.c      |  54 ++++
>> >>  drivers/gpu/drm/vc4/vc4_crtc.c    | 583 ++++++++++++++++++++++++++++++++++
>> >>  drivers/gpu/drm/vc4/vc4_debugfs.c |  38 +++
>> >>  drivers/gpu/drm/vc4/vc4_drv.c     | 249 +++++++++++++++
>> >>  drivers/gpu/drm/vc4/vc4_drv.h     | 123 +++++++
>> >>  drivers/gpu/drm/vc4/vc4_hdmi.c    | 651 ++++++++++++++++++++++++++++++++++++++
>> >>  drivers/gpu/drm/vc4/vc4_hvs.c     | 172 ++++++++++
>> >>  drivers/gpu/drm/vc4/vc4_kms.c     |  84 +++++
>> >>  drivers/gpu/drm/vc4/vc4_plane.c   | 320 +++++++++++++++++++
>> >>  drivers/gpu/drm/vc4/vc4_regs.h    | 562 ++++++++++++++++++++++++++++++++
>> >>  14 files changed, 2871 insertions(+)
>> >>  create mode 100644 drivers/gpu/drm/vc4/Kconfig
>> >>  create mode 100644 drivers/gpu/drm/vc4/Makefile
>> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_bo.c
>> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_crtc.c
>> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_debugfs.c
>> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_drv.c
>> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_drv.h
>> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi.c
>> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_hvs.c
>> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_kms.c
>> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_plane.c
>> >>  create mode 100644 drivers/gpu/drm/vc4/vc4_regs.h
>> >
>> > Made a quick pass and found a few things to update to latest drm
>> > developments. Of course didn't look at the hardware details since no clue,
>> > but looks really nice overall.
>> 
>> If you have anything about the hardware that you were curious about, I'd
>> be interested in trying to explain them in the comments to the extent
>> that I can.  It's unfortunate that we haven't shipped docs for the
>> display side of things, but had to do a lot of reading of the verilog
>> just to get this far, anyway.
>
> The only thing I spotted is that you right now only register a primary and
> cursor plane. I guess the plan we once discussed about exposing piles of
> planes for -modesetting accel isn't there yet?
>
> But otherwise I really didn't go into the hardware details.

Yeah, I haven't done piles of planes because what we have right now
means I don't have to do the bandwidth limits tracking yet.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150818/efaeaf36/attachment.sig>

From eric at anholt.net  Tue Aug 18 14:54:03 2015
From: eric at anholt.net (Eric Anholt)
Date: Tue, 18 Aug 2015 14:54:03 -0700
Subject: [PATCH v2 2/7] MAINTAINERS: Add myself for the new VC4 (RPi GPU)
 graphics driver.
In-Reply-To: <1439934848-7196-1-git-send-email-eric@anholt.net>
References: <1439934848-7196-1-git-send-email-eric@anholt.net>
Message-ID: <1439934848-7196-3-git-send-email-eric@anholt.net>

Signed-off-by: Eric Anholt <eric at anholt.net>
---

v2: Mark it Supported, not Maintained.

 MAINTAINERS | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 9c9dd5f..5292087 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3592,6 +3592,12 @@ S:	Maintained
 F:	drivers/gpu/drm/rockchip/
 F:	Documentation/devicetree/bindings/video/rockchip*
 
+DRM DRIVERS FOR VC4
+M:	Eric Anholt <eric at anholt.net>
+T:	git git://github.com/anholt/linux
+S:	Supported
+F:	drivers/gpu/drm/vc4/*
+
 DSBR100 USB FM RADIO DRIVER
 M:	Alexey Klimov <klimov.linux at gmail.com>
 L:	linux-media at vger.kernel.org
-- 
2.1.4



From eric at anholt.net  Tue Aug 18 14:54:01 2015
From: eric at anholt.net (Eric Anholt)
Date: Tue, 18 Aug 2015 14:54:01 -0700
Subject: Raspberry Pi KMS-only driver V2
Message-ID: <1439934848-7196-1-git-send-email-eric@anholt.net>

Here's a respin of the RPi KMS series, taking a bunch of review
feedback into account.  It's also rebased on danvet's drm-misc now,
which cut some stub code.  The new series can still be found at:

https://github.com/anholt/linux/tree/vc4-kms-squash



From eric at anholt.net  Tue Aug 18 14:54:02 2015
From: eric at anholt.net (Eric Anholt)
Date: Tue, 18 Aug 2015 14:54:02 -0700
Subject: [PATCH v2 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <1439934848-7196-1-git-send-email-eric@anholt.net>
References: <1439934848-7196-1-git-send-email-eric@anholt.net>
Message-ID: <1439934848-7196-2-git-send-email-eric@anholt.net>

VC4 is the GPU (display and 3D) subsystem present on the 2835 and some
other Broadcom SoCs.

This binding follows the model of msm, imx, sti, and others, where
there is a subsystem node for the whole GPU, with nodes for the
individual HW components within it.

Signed-off-by: Eric Anholt <eric at anholt.net>
---

v2: Extend the commit message, fix several nits from Stephen Warren.

 .../devicetree/bindings/gpu/brcm,bcm-vc4.txt       | 79 ++++++++++++++++++++++
 1 file changed, 79 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt

diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
new file mode 100644
index 0000000..1b9fedc
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
@@ -0,0 +1,79 @@
+Broadcom VC4 GPU
+
+The VC4 device present on the Raspberry Pi includes a display system
+with HDMI output and the HVS scaler for compositing display planes.
+
+Required properties for VC4:
+- compatible:	Should be "brcm,vc4"
+- crtcs:	List of phandles of pixelvalve scanout engines
+- hvss:		List of phandles of HVS video scalers
+- encoders:	List of phandles of output encoders (HDMI, SDTV)
+
+Required properties for Pixel Valve:
+- compatible:	Should be "brcm,vc4-pixelvalve"
+- reg:		Physical base address and length of the PV's registers
+- interrupts:	The interrupt number
+		  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+
+Required properties for HVS:
+- compatible:	Should be "brcm,vc4-hvs"
+- reg:		Physical base address and length of the HVS's registers
+- interrupts:	The interrupt number
+		  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+
+Required properties for HDMI
+- compatible:	Should be "brcm,vc4-hdmi"
+- reg:		Physical base address and length of the two register ranges
+		  ("HDMI" and "HD", in that order)
+- interrupts:	The interrupt numbers
+		  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
+- ddc:		phandle of the I2C controller used for DDC EDID probing
+- crtc:		phandle to the pixelvalve CRTC the HDMI encoder is attached to
+
+Optional properties for HDMI:
+- hpd-gpio:	The GPIO pin for HDMI hotplug detect (if it doesn't appear
+		  as an interrupt/status bit in the HDMI controller
+		  itself).  See bindings/pinctrl/brcm,bcm2835-gpio.txt
+
+Example:
+pv0: brcm,vc4-pixelvalve at 7e206000 {
+	compatible = "brcm,vc4-pixelvalve";
+	reg = <0x7e206000 0x100>;
+	interrupts = <2 13>; /* pwa2 */
+};
+
+pv1: brcm,vc4-pixelvalve at 7e207000 {
+	compatible = "brcm,vc4-pixelvalve";
+	reg = <0x7e207000 0x100>;
+	interrupts = <2 14>; /* pwa1 */
+};
+
+pv2: brcm,vc4-pixelvalve at 7e807000 {
+	compatible = "brcm,vc4-pixelvalve";
+	reg = <0x7e807000 0x100>;
+	interrupts = <2 10>; /* pixelvalve */
+};
+
+hvs: brcm,hvs at 7e400000 {
+	compatible = "brcm,vc4-hvs";
+	reg = <0x7e400000 0x6000>;
+	interrupts = <2 1>;
+};
+
+hdmi: brcm,vc4-hdmi at 7e902000 {
+	compatible = "brcm,vc4-hdmi";
+	reg = <0x7e902000 0x600>,
+	      <0x7e808000 0x100>;
+	interrupts = <2 8>, <2 9>;
+	ddc = <&i2c2>;
+	hpd-gpio = <&gpio 46 GPIO_ACTIVE_HIGH>;
+	crtc = <&pv2>;
+};
+
+vc4: vc4 at 0x7e4c0000 {
+	compatible = "brcm,vc4";
+
+	crtcs = <&pv0>, <&pv1>, <&pv2>;
+	encoders = <&hdmi>;
+	hvss = <&hvs>;
+};
-- 
2.1.4



From eric at anholt.net  Tue Aug 18 14:54:07 2015
From: eric at anholt.net (Eric Anholt)
Date: Tue, 18 Aug 2015 14:54:07 -0700
Subject: [PATCH v2 6/7] ARM: bcm2835: Add the DDC I2C controller to the device
 tree.
In-Reply-To: <1439934848-7196-1-git-send-email-eric@anholt.net>
References: <1439934848-7196-1-git-send-email-eric@anholt.net>
Message-ID: <1439934848-7196-7-git-send-email-eric@anholt.net>

We need to use it for getting video modes over HDMI.

Signed-off-by: Eric Anholt <eric at anholt.net>
---

v2: Mark it as disabled by default, and enable it in bcm2835-rpi.

 arch/arm/boot/dts/bcm2835-rpi.dtsi |  4 ++++
 arch/arm/boot/dts/bcm2835.dtsi     | 10 ++++++++++
 2 files changed, 14 insertions(+)

diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi
index 46780bb..29cc7ba 100644
--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi
+++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi
@@ -45,6 +45,10 @@
 	clock-frequency = <100000>;
 };
 
+&i2c2 {
+	status = "okay";
+};
+
 &sdhci {
 	status = "okay";
 	bus-width = <4>;
diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
index 301c73f..20574cc 100644
--- a/arch/arm/boot/dts/bcm2835.dtsi
+++ b/arch/arm/boot/dts/bcm2835.dtsi
@@ -149,6 +149,16 @@
 			status = "disabled";
 		};
 
+		i2c2: i2c at 7e805000 {
+			compatible = "brcm,bcm2835-i2c";
+			reg = <0x7e805000 0x1000>;
+			interrupts = <2 21>;
+			clocks = <&clk_i2c>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+		};
+
 		usb at 7e980000 {
 			compatible = "brcm,bcm2835-usb";
 			reg = <0x7e980000 0x10000>;
-- 
2.1.4



From eric at anholt.net  Tue Aug 18 14:54:08 2015
From: eric at anholt.net (Eric Anholt)
Date: Tue, 18 Aug 2015 14:54:08 -0700
Subject: [PATCH v2 7/7] ARM: bcm2835: Add VC4 to the device tree.
In-Reply-To: <1439934848-7196-1-git-send-email-eric@anholt.net>
References: <1439934848-7196-1-git-send-email-eric@anholt.net>
Message-ID: <1439934848-7196-8-git-send-email-eric@anholt.net>

VC4 is the GPU (display and 3D) present on the 2835.

Signed-off-by: Eric Anholt <eric at anholt.net>
---

v2: Sort by register address, mark HDMI as disabled by default in the
    SoC file and enable it from -rpi.

 arch/arm/boot/dts/bcm2835-rpi.dtsi |  4 ++++
 arch/arm/boot/dts/bcm2835.dtsi     | 44 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+)

diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi
index 29cc7ba..f7217906 100644
--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi
+++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi
@@ -49,6 +49,10 @@
 	status = "okay";
 };
 
+&hdmi {
+	status = "okay";
+};
+
 &sdhci {
 	status = "okay";
 	bus-width = <4>;
diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
index 20574cc..e625a21 100644
--- a/arch/arm/boot/dts/bcm2835.dtsi
+++ b/arch/arm/boot/dts/bcm2835.dtsi
@@ -1,4 +1,5 @@
 #include <dt-bindings/pinctrl/bcm2835.h>
+#include <dt-bindings/gpio/gpio.h>
 #include "skeleton.dtsi"
 
 / {
@@ -131,6 +132,18 @@
 			status = "disabled";
 		};
 
+		pv0: brcm,vc4-pixelvalve at 7e206000 {
+			compatible = "brcm,vc4-pixelvalve";
+			reg = <0x7e206000 0x100>;
+			interrupts = <2 13>; /* pwa2 */
+		};
+
+		pv1: brcm,vc4-pixelvalve at 7e207000 {
+			compatible = "brcm,vc4-pixelvalve";
+			reg = <0x7e207000 0x100>;
+			interrupts = <2 14>; /* pwa1 */
+		};
+
 		sdhci: sdhci at 7e300000 {
 			compatible = "brcm,bcm2835-sdhci";
 			reg = <0x7e300000 0x100>;
@@ -139,6 +152,12 @@
 			status = "disabled";
 		};
 
+		hvs: brcm,hvs at 7e400000 {
+			compatible = "brcm,vc4-hvs";
+			reg = <0x7e400000 0x6000>;
+			interrupts = <2 1>;
+		};
+
 		i2c1: i2c at 7e804000 {
 			compatible = "brcm,bcm2835-i2c";
 			reg = <0x7e804000 0x1000>;
@@ -159,6 +178,23 @@
 			status = "disabled";
 		};
 
+		pv2: brcm,vc4-pixelvalve at 7e807000 {
+			compatible = "brcm,vc4-pixelvalve";
+			reg = <0x7e807000 0x100>;
+			interrupts = <2 10>; /* pixelvalve */
+		};
+
+		hdmi: brcm,vc4-hdmi at 7e902000 {
+			compatible = "brcm,vc4-hdmi";
+			reg = <0x7e902000 0x600>,
+			      <0x7e808000 0x100>;
+			interrupts = <2 8>, <2 9>;
+			ddc = <&i2c2>;
+			hpd-gpio = <&gpio 46 GPIO_ACTIVE_HIGH>;
+			crtc = <&pv2>;
+			status = "disabled";
+		};
+
 		usb at 7e980000 {
 			compatible = "brcm,bcm2835-usb";
 			reg = <0x7e980000 0x10000>;
@@ -168,6 +204,14 @@
 		arm-pmu {
 			compatible = "arm,arm1176-pmu";
 		};
+
+		vc4: vc4 at 0x7e4c0000 {
+			compatible = "brcm,vc4";
+
+			crtcs = <&pv0>, <&pv1>, <&pv2>;
+			encoders = <&hdmi>;
+			hvss = <&hvs>;
+		};
 	};
 
 	clocks {
-- 
2.1.4



From eric at anholt.net  Tue Aug 18 14:54:06 2015
From: eric at anholt.net (Eric Anholt)
Date: Tue, 18 Aug 2015 14:54:06 -0700
Subject: [PATCH v2 5/7] drm/vc4: Allow vblank to be disabled
In-Reply-To: <1439934848-7196-1-git-send-email-eric@anholt.net>
References: <1439934848-7196-1-git-send-email-eric@anholt.net>
Message-ID: <1439934848-7196-6-git-send-email-eric@anholt.net>

From: Derek Foreman <derekf at osg.samsung.com>

Signed-off-by: Derek Foreman <derekf at osg.samsung.com>
Signed-off-by: Eric Anholt <eric at anholt.net>
---
 drivers/gpu/drm/vc4/vc4_kms.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
index a34439f..f881cd4 100644
--- a/drivers/gpu/drm/vc4/vc4_kms.c
+++ b/drivers/gpu/drm/vc4/vc4_kms.c
@@ -77,6 +77,8 @@ int vc4_kms_load(struct drm_device *dev)
 	dev->mode_config.funcs = &vc4_mode_funcs;
 	dev->mode_config.preferred_depth = 24;
 
+	dev->vblank_disable_allowed = true;
+
 	ret = vc4_init_modeset_objects(dev);
 	if (ret)
 		goto fail;
-- 
2.1.4



From eric at anholt.net  Tue Aug 18 14:54:05 2015
From: eric at anholt.net (Eric Anholt)
Date: Tue, 18 Aug 2015 14:54:05 -0700
Subject: [PATCH v2 4/7] drm/vc4: Use the fbdev_cma helpers
In-Reply-To: <1439934848-7196-1-git-send-email-eric@anholt.net>
References: <1439934848-7196-1-git-send-email-eric@anholt.net>
Message-ID: <1439934848-7196-5-git-send-email-eric@anholt.net>

From: Derek Foreman <derekf at osg.samsung.com>

Keep the fbdev_cma pointer around so we can use it on hotplog and close
to ensure the frame buffer console is in a useful state.

Signed-off-by: Derek Foreman <derekf at osg.samsung.com>
Signed-off-by: Eric Anholt <eric at anholt.net>
---
 drivers/gpu/drm/vc4/vc4_drv.c | 14 ++++++++++++++
 drivers/gpu/drm/vc4/vc4_drv.h |  2 ++
 drivers/gpu/drm/vc4/vc4_kms.c | 18 +++++++++++++++---
 3 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index d30aefa..8ebd238 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
+#include "drm_fb_cma_helper.h"
 
 #include "vc4_drv.h"
 #include "vc4_regs.h"
@@ -49,6 +50,14 @@ static void vc4_drm_preclose(struct drm_device *dev, struct drm_file *file)
 		vc4_cancel_page_flip(crtc, file);
 }
 
+static void vc4_lastclose(struct drm_device *dev)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+	if (vc4->fbdev)
+		drm_fbdev_cma_restore_mode(vc4->fbdev);
+}
+
 static const struct file_operations vc4_drm_fops = {
 	.owner = THIS_MODULE,
 	.open = drm_open,
@@ -71,6 +80,7 @@ static struct drm_driver vc4_drm_driver = {
 			    DRIVER_ATOMIC |
 			    DRIVER_GEM |
 			    DRIVER_PRIME),
+	.lastclose = vc4_lastclose,
 	.preclose = vc4_drm_preclose,
 
 	.enable_vblank = vc4_enable_vblank,
@@ -161,6 +171,10 @@ static void vc4_drm_unbind(struct device *dev)
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	struct drm_device *drm = platform_get_drvdata(pdev);
+	struct vc4_dev *vc4 = to_vc4_dev(drm);
+
+	if (vc4->fbdev)
+		drm_fbdev_cma_fini(vc4->fbdev);
 
 	drm_mode_config_cleanup(drm);
 
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 282ce0b..24a1cb5 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -15,6 +15,8 @@ struct vc4_dev {
 	struct vc4_hdmi *hdmi;
 	struct vc4_hvs *hvs;
 	struct vc4_crtc *crtc[3];
+
+	struct drm_fbdev_cma *fbdev;
 };
 
 static inline struct vc4_dev *
diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
index e5e96bc..a34439f 100644
--- a/drivers/gpu/drm/vc4/vc4_kms.c
+++ b/drivers/gpu/drm/vc4/vc4_kms.c
@@ -21,7 +21,16 @@
 #include "drm_fb_cma_helper.h"
 #include "vc4_drv.h"
 
+static void vc4_output_poll_changed(struct drm_device *dev)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+	if (vc4->fbdev)
+		drm_fbdev_cma_hotplug_event(vc4->fbdev);
+}
+
 static const struct drm_mode_config_funcs vc4_mode_funcs = {
+	.output_poll_changed = vc4_output_poll_changed,
 	.atomic_check = drm_atomic_helper_check,
 	.atomic_commit = drm_atomic_helper_commit,
 	.fb_create = drm_fb_cma_create,
@@ -54,6 +63,7 @@ fail:
 
 int vc4_kms_load(struct drm_device *dev)
 {
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	int ret;
 
 	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
@@ -73,9 +83,11 @@ int vc4_kms_load(struct drm_device *dev)
 
 	drm_mode_config_reset(dev);
 
-	drm_fbdev_cma_init(dev, 32,
-			   dev->mode_config.num_crtc,
-			   dev->mode_config.num_connector);
+	vc4->fbdev = drm_fbdev_cma_init(dev, 32,
+					dev->mode_config.num_crtc,
+					dev->mode_config.num_connector);
+	if (IS_ERR(vc4->fbdev))
+		vc4->fbdev = NULL;
 
 	drm_kms_helper_poll_init(dev);
 
-- 
2.1.4



From eric at anholt.net  Tue Aug 18 14:54:04 2015
From: eric at anholt.net (Eric Anholt)
Date: Tue, 18 Aug 2015 14:54:04 -0700
Subject: [PATCH v2 3/7] drm/vc4: Add KMS support for Raspberry Pi.
In-Reply-To: <1439934848-7196-1-git-send-email-eric@anholt.net>
References: <1439934848-7196-1-git-send-email-eric@anholt.net>
Message-ID: <1439934848-7196-4-git-send-email-eric@anholt.net>

This is the start of a full VC4 driver.  Right now this just supports
configuring the display using a pre-existing video mode (because
changing the pixel clock isn't available yet, and doesn't work when it
is).  However, this is enough for fbcon and bringing up X using
xf86-video-modesetting.

Signed-off-by: Eric Anholt <eric at anholt.net>
---

v2: Drop FB_HELPER select thanks to Archit's patches.  Do manual init
    ordering instead of using the .load hook.  Structure registration
    more like tegra's, but still using the typical "component" code.
    Drop no-op hooks for atomic_begin and mode_fixup() now that
    they're optional.  Drop sentinel in Makefile.  Fix minor style
    nits I noticed on another reread.

 drivers/gpu/drm/Kconfig           |   2 +
 drivers/gpu/drm/Makefile          |   1 +
 drivers/gpu/drm/vc4/Kconfig       |  13 +
 drivers/gpu/drm/vc4/Makefile      |  17 +
 drivers/gpu/drm/vc4/vc4_bo.c      |  52 ++++
 drivers/gpu/drm/vc4/vc4_crtc.c    | 565 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/vc4/vc4_debugfs.c |  38 +++
 drivers/gpu/drm/vc4/vc4_drv.c     | 271 ++++++++++++++++
 drivers/gpu/drm/vc4/vc4_drv.h     | 120 ++++++++
 drivers/gpu/drm/vc4/vc4_hdmi.c    | 633 ++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/vc4/vc4_hvs.c     | 161 ++++++++++
 drivers/gpu/drm/vc4/vc4_kms.c     |  84 +++++
 drivers/gpu/drm/vc4/vc4_plane.c   | 320 +++++++++++++++++++
 drivers/gpu/drm/vc4/vc4_regs.h    | 562 +++++++++++++++++++++++++++++++++
 14 files changed, 2839 insertions(+)
 create mode 100644 drivers/gpu/drm/vc4/Kconfig
 create mode 100644 drivers/gpu/drm/vc4/Makefile
 create mode 100644 drivers/gpu/drm/vc4/vc4_bo.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_crtc.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_debugfs.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_drv.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_drv.h
 create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_hvs.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_kms.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_plane.c
 create mode 100644 drivers/gpu/drm/vc4/vc4_regs.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 06ae500..19bb0db 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -260,3 +260,5 @@ source "drivers/gpu/drm/sti/Kconfig"
 source "drivers/gpu/drm/amd/amdkfd/Kconfig"
 
 source "drivers/gpu/drm/imx/Kconfig"
+
+source "drivers/gpu/drm/vc4/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 8858510..283ed8e 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_DRM_MGA)	+= mga/
 obj-$(CONFIG_DRM_I810)	+= i810/
 obj-$(CONFIG_DRM_I915)  += i915/
 obj-$(CONFIG_DRM_MGAG200) += mgag200/
+obj-$(CONFIG_DRM_VC4)  += vc4/
 obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/
 obj-$(CONFIG_DRM_SIS)   += sis/
 obj-$(CONFIG_DRM_SAVAGE)+= savage/
diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig
new file mode 100644
index 0000000..e810ef7
--- /dev/null
+++ b/drivers/gpu/drm/vc4/Kconfig
@@ -0,0 +1,13 @@
+config DRM_VC4
+	tristate "Broadcom VC4 Graphics"
+	depends on ARCH_BCM2835
+	depends on DRM
+	select DRM_KMS_HELPER
+	select DRM_KMS_CMA_HELPER
+	help
+	  Choose this option if you have a system that has a Broadcom
+	  VC4 GPU, such as the Raspberry Pi or other BCM2708/BCM2835.
+
+	  This driver requires that "avoid_warnings=2" be present in
+	  the config.txt for the firmware, to keep it from smashing
+	  our display setup.
diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile
new file mode 100644
index 0000000..32b4f9c
--- /dev/null
+++ b/drivers/gpu/drm/vc4/Makefile
@@ -0,0 +1,17 @@
+ccflags-y := -Iinclude/drm
+
+# Please keep these build lists sorted!
+
+# core driver code
+vc4-y := \
+	vc4_bo.o \
+	vc4_crtc.o \
+	vc4_drv.o \
+	vc4_kms.o \
+	vc4_hdmi.o \
+	vc4_hvs.o \
+	vc4_plane.o
+
+vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o
+
+obj-$(CONFIG_DRM_VC4)  += vc4.o
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
new file mode 100644
index 0000000..ab9f510
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -0,0 +1,52 @@
+/*
+ *  Copyright ? 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* DOC: VC4 GEM BO management support.
+ *
+ * The VC4 GPU architecture (both scanout and rendering) has direct
+ * access to system memory with no MMU in between.  To support it, we
+ * use the GEM CMA helper functions to allocate contiguous ranges of
+ * physical memory for our BOs.
+ */
+
+#include "vc4_drv.h"
+
+struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size)
+{
+	struct drm_gem_cma_object *cma_obj;
+
+	cma_obj = drm_gem_cma_create(dev, size);
+	if (IS_ERR(cma_obj))
+		return NULL;
+	else
+		return to_vc4_bo(&cma_obj->base);
+}
+
+int vc4_dumb_create(struct drm_file *file_priv,
+		    struct drm_device *dev,
+		    struct drm_mode_create_dumb *args)
+{
+	int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+	struct vc4_bo *bo = NULL;
+	int ret;
+
+	if (args->pitch < min_pitch)
+		args->pitch = min_pitch;
+
+	if (args->size < args->pitch * args->height)
+		args->size = args->pitch * args->height;
+
+	bo = vc4_bo_create(dev, roundup(args->size, PAGE_SIZE));
+	if (!bo)
+		return -ENOMEM;
+
+	ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle);
+	drm_gem_object_unreference_unlocked(&bo->base.base);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
new file mode 100644
index 0000000..2c70faf
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/**
+ * DOC: VC4 CRTC module
+ *
+ * In VC4, the Pixel Valve is what most closely corresponds to the
+ * DRM's concept of a CRTC.  The PV generates video timings from the
+ * pixel clock and its configuration.
+ *
+ * However, the DRM CRTC also collects the configuration of all the
+ * DRM planes attached to it.  As a result, this file also manages
+ * setup of the VC4 HVS's display elements on the CRTC.
+ */
+
+#include "drm_atomic_helper.h"
+#include "drm_crtc_helper.h"
+#include "linux/clk.h"
+#include "linux/component.h"
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+
+#define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset))
+#define CRTC_READ(offset) readl(vc4_crtc->regs + (offset))
+
+#define CRTC_REG(reg) { reg, #reg }
+static const struct {
+	u32 reg;
+	const char *name;
+} crtc_regs[] = {
+	CRTC_REG(PV_CONTROL),
+	CRTC_REG(PV_V_CONTROL),
+	CRTC_REG(PV_VSYNCD),
+	CRTC_REG(PV_HORZA),
+	CRTC_REG(PV_HORZB),
+	CRTC_REG(PV_VERTA),
+	CRTC_REG(PV_VERTB),
+	CRTC_REG(PV_VERTA_EVEN),
+	CRTC_REG(PV_VERTB_EVEN),
+	CRTC_REG(PV_INTEN),
+	CRTC_REG(PV_INTSTAT),
+	CRTC_REG(PV_STAT),
+	CRTC_REG(PV_HACT_ACT),
+};
+
+static void vc4_crtc_dump_regs(struct vc4_crtc *vc4_crtc)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(crtc_regs); i++) {
+		DRM_INFO("0x%04x (%s): 0x%08x\n",
+			 crtc_regs[i].reg, crtc_regs[i].name,
+			 CRTC_READ(crtc_regs[i].reg));
+	}
+}
+
+#ifdef CONFIG_DEBUG_FS
+int vc4_crtc_debugfs_regs(struct seq_file *m, void *unused)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_device *dev = node->minor->dev;
+	int crtc_index = (uintptr_t)node->info_ent->data;
+	struct drm_crtc *crtc;
+	struct vc4_crtc *vc4_crtc;
+	int i;
+
+	i = 0;
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+		if (i == crtc_index)
+			break;
+		i++;
+	}
+	if (!crtc)
+		return 0;
+	vc4_crtc = to_vc4_crtc(crtc);
+
+	for (i = 0; i < ARRAY_SIZE(crtc_regs); i++) {
+		seq_printf(m, "%s (0x%04x): 0x%08x\n",
+			   crtc_regs[i].name, crtc_regs[i].reg,
+			   CRTC_READ(crtc_regs[i].reg));
+	}
+
+	return 0;
+}
+#endif
+
+static void vc4_crtc_destroy(struct drm_crtc *crtc)
+{
+	drm_crtc_cleanup(crtc);
+}
+
+static u32 vc4_get_fifo_full_level(u32 format)
+{
+	static const u32 fifo_len_bytes = 64;
+	static const u32 hvs_latency_pix = 6;
+
+	switch (format) {
+	case PV_CONTROL_FORMAT_DSIV_16:
+	case PV_CONTROL_FORMAT_DSIC_16:
+		return fifo_len_bytes - 2 * hvs_latency_pix;
+	case PV_CONTROL_FORMAT_DSIV_18:
+		return fifo_len_bytes - 14;
+	case PV_CONTROL_FORMAT_24:
+	case PV_CONTROL_FORMAT_DSIV_24:
+	default:
+		return fifo_len_bytes - 3 * hvs_latency_pix;
+	}
+}
+
+static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	struct drm_crtc_state *state = crtc->state;
+	struct drm_display_mode *mode = &state->adjusted_mode;
+	u32 vactive = (mode->vdisplay >>
+		       ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0));
+	u32 format = PV_CONTROL_FORMAT_24;
+	bool debug_dump_regs = false;
+
+	if (debug_dump_regs) {
+		DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc));
+		vc4_crtc_dump_regs(vc4_crtc);
+	}
+
+	/* This is where we would set the pixel clock. */
+
+	/* Reset the PV fifo. */
+	CRTC_WRITE(PV_CONTROL, 0);
+	CRTC_WRITE(PV_CONTROL, PV_CONTROL_FIFO_CLR | PV_CONTROL_EN);
+	CRTC_WRITE(PV_CONTROL, 0);
+
+	CRTC_WRITE(PV_HORZA,
+		   VC4_SET_FIELD(mode->htotal - mode->hsync_end,
+				 PV_HORZA_HBP) |
+		   VC4_SET_FIELD(mode->hsync_end - mode->hsync_start,
+				 PV_HORZA_HSYNC));
+	CRTC_WRITE(PV_HORZB,
+		   VC4_SET_FIELD(mode->hsync_start - mode->hdisplay,
+				 PV_HORZB_HFP) |
+		   VC4_SET_FIELD(mode->hdisplay, PV_HORZB_HACTIVE));
+
+	CRTC_WRITE(PV_VERTA,
+		   VC4_SET_FIELD(mode->vtotal - mode->vsync_end,
+				 PV_VERTA_VBP) |
+		   VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
+				 PV_VERTA_VSYNC));
+	CRTC_WRITE(PV_VERTB,
+		   VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
+				 PV_VERTB_VFP) |
+		   VC4_SET_FIELD(vactive, PV_VERTB_VACTIVE));
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		/* Write PV_VERTA_EVEN/VERTB_EVEN */
+	}
+
+	CRTC_WRITE(PV_HACT_ACT, mode->hdisplay);
+
+	CRTC_WRITE(PV_V_CONTROL,
+		   PV_VCONTROL_CONTINUOUS);
+
+	CRTC_WRITE(PV_CONTROL,
+		   VC4_SET_FIELD(format, PV_CONTROL_FORMAT) |
+		   VC4_SET_FIELD(vc4_get_fifo_full_level(format),
+				 PV_CONTROL_FIFO_LEVEL) |
+		   PV_CONTROL_CLR_AT_START |
+		   PV_CONTROL_TRIGGER_UNDERFLOW |
+		   PV_CONTROL_WAIT_HSTART |
+		   VC4_SET_FIELD(PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI,
+				 PV_CONTROL_CLK_SELECT) |
+		   PV_CONTROL_FIFO_CLR |
+		   PV_CONTROL_EN);
+
+	if (debug_dump_regs) {
+		DRM_INFO("CRTC %d regs after:\n", drm_crtc_index(crtc));
+		vc4_crtc_dump_regs(vc4_crtc);
+	}
+}
+
+static void require_hvs_enabled(struct drm_device *dev)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+	WARN_ON_ONCE((HVS_READ(SCALER_DISPCTRL) & SCALER_DISPCTRL_ENABLE) !=
+		     SCALER_DISPCTRL_ENABLE);
+}
+
+static void vc4_crtc_disable(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+
+	require_hvs_enabled(dev);
+
+	CRTC_WRITE(PV_V_CONTROL,
+		   CRTC_READ(PV_V_CONTROL) & ~PV_VCONTROL_VIDEN);
+	while (CRTC_READ(PV_V_CONTROL) & PV_VCONTROL_VIDEN)
+		cpu_relax();
+
+	/* Without a wait here, we end up with a black screen. */
+	msleep(30);
+
+	if (HVS_READ(SCALER_DISPCTRLX(vc4_crtc->channel)) &
+	    SCALER_DISPCTRLX_ENABLE) {
+		HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel),
+			  SCALER_DISPCTRLX_RESET);
+
+		/* While the docs say that reset is self-clearing, it
+		 * seems it doesn't actually.
+		 */
+		HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), 0);
+	}
+
+	/* Once we leave, the scaler should be disabled and its fifo empty. */
+
+	WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(vc4_crtc->channel)) &
+		     SCALER_DISPCTRLX_RESET);
+
+	WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(vc4_crtc->channel)),
+				   SCALER_DISPSTATX_MODE) !=
+		     SCALER_DISPSTATX_MODE_DISABLED);
+
+	WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(vc4_crtc->channel)) &
+		      (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) !=
+		     SCALER_DISPSTATX_EMPTY);
+}
+
+static void vc4_crtc_enable(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	struct drm_crtc_state *state = crtc->state;
+	struct drm_display_mode *mode = &state->adjusted_mode;
+
+	require_hvs_enabled(dev);
+
+	/* Turn on the scaler, which will wait for vstart to start
+	 * compositing.
+	 */
+	HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel),
+		  VC4_SET_FIELD(mode->hdisplay, SCALER_DISPCTRLX_WIDTH) |
+		  VC4_SET_FIELD(mode->vdisplay, SCALER_DISPCTRLX_HEIGHT) |
+		  SCALER_DISPCTRLX_ENABLE);
+
+	/* Turn on the pixel valve, which will emit the vstart signal. */
+	CRTC_WRITE(PV_V_CONTROL,
+		   CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN);
+}
+
+static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
+				 struct drm_crtc_state *state)
+{
+	struct drm_device *dev = crtc->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct drm_plane *plane;
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	u32 dlist_count = 0;
+
+	drm_atomic_crtc_state_for_each_plane(plane, state) {
+		struct drm_plane_state *plane_state =
+			state->state->plane_states[drm_plane_index(plane)];
+
+		/* plane might not have changed, in which case take
+		 * current state:
+		 */
+		if (!plane_state)
+			plane_state = plane->state;
+
+		dlist_count += vc4_plane_dlist_size(plane_state);
+	}
+
+	dlist_count++; /* Account for SCALER_CTL0_END. */
+
+	if (!vc4_crtc->dlist || dlist_count > vc4_crtc->dlist_size) {
+		vc4_crtc->dlist = ((u32 __iomem *)vc4->hvs->dlist +
+				   HVS_BOOTLOADER_DLIST_END);
+		vc4_crtc->dlist_size = ((SCALER_DLIST_SIZE >> 2) -
+					HVS_BOOTLOADER_DLIST_END);
+
+		if (dlist_count > vc4_crtc->dlist_size) {
+			DRM_DEBUG_KMS("dlist too large for CRTC (%d > %d).\n",
+				      dlist_count, vc4_crtc->dlist_size);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
+				  struct drm_crtc_state *old_state)
+{
+	struct drm_device *dev = crtc->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	struct drm_plane *plane;
+	bool debug_dump_regs = false;
+	u32 __iomem *dlist_next = vc4_crtc->dlist;
+
+	if (debug_dump_regs) {
+		DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
+		vc4_hvs_dump_state(dev);
+	}
+
+	/* Copy all the active planes' dlist contents to the hardware dlist.
+	 *
+	 * XXX: If the new display list was large enough that it
+	 * overlapped a currently-read display list, we need to do
+	 * something like disable scanout before putting in the new
+	 * list.  For now, we're safe because we only have the two
+	 * planes.
+	 */
+	drm_atomic_crtc_for_each_plane(plane, crtc) {
+		dlist_next += vc4_plane_write_dlist(plane, dlist_next);
+	}
+
+	if (dlist_next == vc4_crtc->dlist) {
+		/* If no planes were enabled, use the SCALER_CTL0_END
+		 * at the start of the display list memory (in the
+		 * bootloader section).  We'll rewrite that
+		 * SCALER_CTL0_END, just in case, though.
+		 */
+		writel(SCALER_CTL0_END, vc4->hvs->dlist);
+		HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), 0);
+	} else {
+		writel(SCALER_CTL0_END, dlist_next);
+		dlist_next++;
+
+		HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
+			  (u32 *)vc4_crtc->dlist - (u32 *)vc4->hvs->dlist);
+
+		/* Make the next display list start after ours. */
+		vc4_crtc->dlist_size -= (dlist_next - vc4_crtc->dlist);
+		vc4_crtc->dlist = dlist_next;
+	}
+
+	if (debug_dump_regs) {
+		DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
+		vc4_hvs_dump_state(dev);
+	}
+
+	if (crtc->state->event) {
+		unsigned long flags;
+
+		crtc->state->event->pipe = drm_crtc_index(crtc);
+
+		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+		spin_lock_irqsave(&dev->event_lock, flags);
+		vc4_crtc->event = crtc->state->event;
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+		crtc->state->event = NULL;
+	}
+}
+
+int vc4_enable_vblank(struct drm_device *dev, int crtc_id)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
+
+	CRTC_WRITE(PV_INTEN, PV_INT_VFP_START);
+
+	return 0;
+}
+
+void vc4_disable_vblank(struct drm_device *dev, int crtc_id)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
+
+	CRTC_WRITE(PV_INTEN, 0);
+}
+
+static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
+{
+	struct drm_crtc *crtc = &vc4_crtc->base;
+	struct drm_device *dev = crtc->dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	if (vc4_crtc->event) {
+		drm_crtc_send_vblank_event(crtc, vc4_crtc->event);
+		vc4_crtc->event = NULL;
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
+{
+	struct vc4_crtc *vc4_crtc = data;
+	u32 stat = CRTC_READ(PV_INTSTAT);
+	irqreturn_t ret = IRQ_NONE;
+
+	if (stat & PV_INT_VFP_START) {
+		CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
+		drm_crtc_handle_vblank(&vc4_crtc->base);
+		vc4_crtc_handle_page_flip(vc4_crtc);
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+static const struct drm_crtc_funcs vc4_crtc_funcs = {
+	.set_config = drm_atomic_helper_set_config,
+	.destroy = vc4_crtc_destroy,
+	.page_flip = drm_atomic_helper_page_flip,
+	.set_property = NULL,
+	.cursor_set = NULL, /* handled by drm_mode_cursor_universal */
+	.cursor_move = NULL, /* handled by drm_mode_cursor_universal */
+	.reset = drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
+	.mode_set_nofb = vc4_crtc_mode_set_nofb,
+	.disable = vc4_crtc_disable,
+	.enable = vc4_crtc_enable,
+	.atomic_check = vc4_crtc_atomic_check,
+	.atomic_flush = vc4_crtc_atomic_flush,
+};
+
+/* Frees the page flip event when the DRM device is closed with the
+ * event still outstanding.
+ */
+void vc4_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file)
+{
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	if (vc4_crtc->event && vc4_crtc->event->base.file_priv == file) {
+		vc4_crtc->event->base.destroy(&vc4_crtc->event->base);
+		drm_crtc_vblank_put(crtc);
+		vc4_crtc->event = NULL;
+	}
+
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct vc4_dev *vc4 = to_vc4_dev(drm);
+	struct vc4_crtc *vc4_crtc;
+	struct drm_crtc *crtc;
+	struct drm_plane *primary_plane, *cursor_plane;
+	int ret;
+
+	/* For now, we create just the primary and the legacy cursor
+	 * planes.  We should be able to stack more planes on easily,
+	 * but to do that we would need to compute the bandwidth
+	 * requirement of the plane configuration, and reject ones
+	 * that will take too much.
+	 */
+	primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY);
+	if (!primary_plane) {
+		dev_err(dev, "failed to construct primary plane\n");
+		ret = PTR_ERR(primary_plane);
+		goto fail;
+	}
+
+	cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
+	if (!cursor_plane) {
+		dev_err(dev, "failed to construct cursor plane\n");
+		ret = PTR_ERR(cursor_plane);
+		goto fail;
+	}
+
+	vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
+	if (!vc4_crtc)
+		return -ENOMEM;
+	crtc = &vc4_crtc->base;
+
+	vc4_crtc->regs = vc4_ioremap_regs(pdev, 0);
+	if (IS_ERR(vc4_crtc->regs))
+		return PTR_ERR(vc4_crtc->regs);
+
+	drm_crtc_init_with_planes(drm, crtc, primary_plane, cursor_plane,
+				  &vc4_crtc_funcs);
+	drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
+	primary_plane->crtc = crtc;
+	cursor_plane->crtc = crtc;
+	vc4->crtc[drm_crtc_index(crtc)] = vc4_crtc;
+
+	/* Until we have full scanout setup to route things through to
+	 * encoders, line things up like the firmware did.
+	 */
+	switch (drm_crtc_index(crtc)) {
+	case 0:
+		vc4_crtc->channel = 0;
+		break;
+	case 1:
+		vc4_crtc->channel = 2;
+		break;
+	default:
+	case 2:
+		vc4_crtc->channel = 1;
+		break;
+	}
+
+	CRTC_WRITE(PV_INTEN, 0);
+	CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
+	ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
+			       vc4_crtc_irq_handler, 0, "vc4 crtc", vc4_crtc);
+
+	platform_set_drvdata(pdev, vc4_crtc);
+
+	return 0;
+
+fail:
+	return ret;
+}
+
+static void vc4_crtc_unbind(struct device *dev, struct device *master,
+			    void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct vc4_crtc *vc4_crtc = dev_get_drvdata(dev);
+
+	vc4_crtc_destroy(&vc4_crtc->base);
+
+	CRTC_WRITE(PV_INTEN, 0);
+
+	platform_set_drvdata(pdev, NULL);
+}
+
+static const struct component_ops vc4_crtc_ops = {
+	.bind   = vc4_crtc_bind,
+	.unbind = vc4_crtc_unbind,
+};
+
+static int vc4_crtc_dev_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &vc4_crtc_ops);
+}
+
+static int vc4_crtc_dev_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &vc4_crtc_ops);
+	return 0;
+}
+
+static const struct of_device_id vc4_crtc_dt_match[] = {
+	{ .compatible = "brcm,vc4-pixelvalve" },
+	{}
+};
+
+struct platform_driver vc4_crtc_driver = {
+	.probe = vc4_crtc_dev_probe,
+	.remove = vc4_crtc_dev_remove,
+	.driver = {
+		.name = "vc4_crtc",
+		.of_match_table = vc4_crtc_dt_match,
+	},
+};
diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c
new file mode 100644
index 0000000..c557039
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_debugfs.c
@@ -0,0 +1,38 @@
+/*
+ *  Copyright ? 2014 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/seq_file.h>
+#include <linux/circ_buf.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <drm/drmP.h>
+
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+
+static const struct drm_info_list vc4_debugfs_list[] = {
+	{"hdmi_regs", vc4_hdmi_debugfs_regs, 0},
+	{"hvs_regs", vc4_hvs_debugfs_regs, 0},
+	{"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0},
+	{"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1},
+	{"crtc2_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)2},
+};
+#define VC4_DEBUGFS_ENTRIES ARRAY_SIZE(vc4_debugfs_list)
+
+int
+vc4_debugfs_init(struct drm_minor *minor)
+{
+	return drm_debugfs_create_files(vc4_debugfs_list, VC4_DEBUGFS_ENTRIES,
+					minor->debugfs_root, minor);
+}
+
+void
+vc4_debugfs_cleanup(struct drm_minor *minor)
+{
+	drm_debugfs_remove_files(vc4_debugfs_list, VC4_DEBUGFS_ENTRIES, minor);
+}
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
new file mode 100644
index 0000000..d30aefa
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2014-2015 Broadcom
+ * Copyright (C) 2013 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+
+#define DRIVER_NAME "vc4"
+#define DRIVER_DESC "Broadcom VC4 graphics"
+#define DRIVER_DATE "20140616"
+#define DRIVER_MAJOR 0
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 0
+
+/* Helper function for mapping the regs on a platform device. */
+void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index)
+{
+	struct resource *res;
+	void __iomem *map;
+
+	res = platform_get_resource(dev, IORESOURCE_MEM, index);
+	map = devm_ioremap_resource(&dev->dev, res);
+	if (IS_ERR(map)) {
+		DRM_ERROR("Failed to map registers: %ld\n", PTR_ERR(map));
+		return map;
+	}
+
+	return map;
+}
+
+static void vc4_drm_preclose(struct drm_device *dev, struct drm_file *file)
+{
+	struct drm_crtc *crtc;
+
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+		vc4_cancel_page_flip(crtc, file);
+}
+
+static const struct file_operations vc4_drm_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.release = drm_release,
+	.unlocked_ioctl = drm_ioctl,
+	.mmap = drm_gem_cma_mmap,
+	.poll = drm_poll,
+	.read = drm_read,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = drm_compat_ioctl,
+#endif
+	.llseek = noop_llseek,
+};
+
+static const struct drm_ioctl_desc vc4_drm_ioctls[] = {
+};
+
+static struct drm_driver vc4_drm_driver = {
+	.driver_features = (DRIVER_MODESET |
+			    DRIVER_ATOMIC |
+			    DRIVER_GEM |
+			    DRIVER_PRIME),
+	.preclose = vc4_drm_preclose,
+
+	.enable_vblank = vc4_enable_vblank,
+	.disable_vblank = vc4_disable_vblank,
+	.get_vblank_counter = drm_vblank_count,
+
+#if defined(CONFIG_DEBUG_FS)
+	.debugfs_init = vc4_debugfs_init,
+	.debugfs_cleanup = vc4_debugfs_cleanup,
+#endif
+
+	.gem_free_object = drm_gem_cma_free_object,
+	.gem_vm_ops = &drm_gem_cma_vm_ops,
+
+	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.gem_prime_import = drm_gem_prime_import,
+	.gem_prime_export = drm_gem_prime_export,
+	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
+	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+	.gem_prime_vmap = drm_gem_cma_prime_vmap,
+	.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+	.gem_prime_mmap = drm_gem_cma_prime_mmap,
+
+	.dumb_create = vc4_dumb_create,
+	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
+	.dumb_destroy = drm_gem_dumb_destroy,
+
+	.ioctls = vc4_drm_ioctls,
+	.num_ioctls = ARRAY_SIZE(vc4_drm_ioctls),
+	.fops = &vc4_drm_fops,
+
+	.name = DRIVER_NAME,
+	.desc = DRIVER_DESC,
+	.date = DRIVER_DATE,
+	.major = DRIVER_MAJOR,
+	.minor = DRIVER_MINOR,
+	.patchlevel = DRIVER_PATCHLEVEL,
+};
+
+static int vc4_drm_bind(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm;
+	struct vc4_dev *vc4;
+	int ret = 0;
+
+	dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	vc4 = devm_kzalloc(dev, sizeof(*vc4), GFP_KERNEL);
+	if (!vc4)
+		return -ENOMEM;
+
+	drm = drm_dev_alloc(&vc4_drm_driver, dev);
+	if (!drm)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, drm);
+	vc4->dev = drm;
+	drm->dev_private = vc4;
+
+	drm_dev_set_unique(drm, dev_name(dev));
+
+	drm_mode_config_init(drm);
+	if (ret)
+		goto unref;
+
+	ret = component_bind_all(dev, drm);
+	if (ret)
+		goto unref;
+
+	ret = drm_dev_register(drm, 0);
+	if (ret < 0)
+		goto unbind_all;
+
+	vc4_kms_load(drm);
+
+	return 0;
+unbind_all:
+	component_unbind_all(dev, drm);
+unref:
+	drm_dev_unref(drm);
+	return ret;
+
+	return 0;
+}
+
+static void vc4_drm_unbind(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = platform_get_drvdata(pdev);
+
+	drm_mode_config_cleanup(drm);
+
+	drm_put_dev(drm);
+}
+
+static const struct component_master_ops vc4_drm_ops = {
+	.bind = vc4_drm_bind,
+	.unbind = vc4_drm_unbind,
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int add_components(struct device *dev, struct component_match **matchptr,
+			  const char *name)
+{
+	struct device_node *np = dev->of_node;
+	unsigned i;
+
+	for (i = 0; ; i++) {
+		struct device_node *node;
+
+		node = of_parse_phandle(np, name, i);
+		if (!node)
+			break;
+
+		component_match_add(dev, matchptr, compare_of, node);
+	}
+
+	return 0;
+}
+
+static int vc4_platform_drm_probe(struct platform_device *pdev)
+{
+	struct component_match *match = NULL;
+	struct device *dev = &pdev->dev;
+
+	add_components(dev, &match, "crtcs");
+	add_components(dev, &match, "encoders");
+	add_components(dev, &match, "hvss");
+
+	return component_master_add_with_match(dev, &vc4_drm_ops, match);
+}
+
+static int vc4_platform_drm_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &vc4_drm_ops);
+
+	return 0;
+}
+
+static const struct of_device_id vc4_of_match[] = {
+	{ .compatible = "brcm,vc4", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, vc4_of_match);
+
+static struct platform_driver vc4_platform_driver = {
+	.probe		= vc4_platform_drm_probe,
+	.remove		= vc4_platform_drm_remove,
+	.driver		= {
+		.name	= "vc4-drm",
+		.owner	= THIS_MODULE,
+		.of_match_table = vc4_of_match,
+	},
+};
+
+static struct platform_driver *component_drivers[] = {
+	&vc4_hdmi_driver,
+	&vc4_crtc_driver,
+	&vc4_hvs_driver,
+};
+
+static int __init vc4_drm_register(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(component_drivers); i++) {
+		int ret = platform_driver_register(component_drivers[i]);
+		if (ret) {
+			while (--i >= 0)
+				platform_driver_unregister(component_drivers[i]);
+			return ret;
+		}
+	}
+	return platform_driver_register(&vc4_platform_driver);
+}
+
+static void __exit vc4_drm_unregister(void)
+{
+	int i;
+
+	for (i = ARRAY_SIZE(component_drivers) - 1; i >= 0; i--)
+		platform_driver_unregister(component_drivers[i]);
+
+	platform_driver_unregister(&vc4_platform_driver);
+}
+
+module_init(vc4_drm_register);
+module_exit(vc4_drm_unregister);
+
+MODULE_ALIAS("platform:vc4-drm");
+MODULE_DESCRIPTION("Broadcom VC4 DRM Driver");
+MODULE_AUTHOR("Eric Anholt <eric at anholt.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
new file mode 100644
index 0000000..282ce0b
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "drmP.h"
+#include "drm_gem_cma_helper.h"
+
+struct vc4_dev {
+	struct drm_device *dev;
+
+	struct vc4_hdmi *hdmi;
+	struct vc4_hvs *hvs;
+	struct vc4_crtc *crtc[3];
+};
+
+static inline struct vc4_dev *
+to_vc4_dev(struct drm_device *dev)
+{
+	return (struct vc4_dev *)dev->dev_private;
+}
+
+struct vc4_bo {
+	struct drm_gem_cma_object base;
+};
+
+static inline struct vc4_bo *
+to_vc4_bo(struct drm_gem_object *bo)
+{
+	return (struct vc4_bo *)bo;
+}
+
+struct vc4_hvs {
+	struct platform_device *pdev;
+	void __iomem *regs;
+	void __iomem *dlist;
+};
+
+struct vc4_crtc {
+	struct drm_crtc base;
+	void __iomem *regs;
+
+	/* Which HVS channel we're using for our CRTC. */
+	int channel;
+
+	/* Pointer to the actual hardware display list memory for the
+	 * crtc.
+	 */
+	u32 __iomem *dlist;
+
+	u32 dlist_size; /* in dwords */
+
+	struct drm_pending_vblank_event *event;
+};
+
+static inline struct vc4_crtc *
+to_vc4_crtc(struct drm_crtc *crtc)
+{
+	return (struct vc4_crtc *)crtc;
+}
+
+struct vc4_plane {
+	struct drm_plane base;
+};
+
+static inline struct vc4_plane *
+to_vc4_plane(struct drm_plane *plane)
+{
+	return (struct vc4_plane *)plane;
+}
+
+#define HVS_READ(offset) readl(vc4->hvs->regs + offset)
+#define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset)
+
+/* vc4_bo.c */
+void vc4_free_object(struct drm_gem_object *gem_obj);
+struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size);
+int vc4_dumb_create(struct drm_file *file_priv,
+		    struct drm_device *dev,
+		    struct drm_mode_create_dumb *args);
+struct dma_buf *vc4_prime_export(struct drm_device *dev,
+				 struct drm_gem_object *obj, int flags);
+
+/* vc4_crtc.c */
+extern struct platform_driver vc4_crtc_driver;
+int vc4_enable_vblank(struct drm_device *dev, int crtc_id);
+void vc4_disable_vblank(struct drm_device *dev, int crtc_id);
+void vc4_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
+int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg);
+
+/* vc4_debugfs.c */
+int vc4_debugfs_init(struct drm_minor *minor);
+void vc4_debugfs_cleanup(struct drm_minor *minor);
+
+/* vc4_drv.c */
+void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index);
+
+/* vc4_hdmi.c */
+extern struct platform_driver vc4_hdmi_driver;
+struct drm_encoder *vc4_hdmi_encoder_init(struct drm_device *dev);
+struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
+					      struct drm_encoder *encoder);
+int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused);
+
+/* vc4_hvs.c */
+extern struct platform_driver vc4_hvs_driver;
+void vc4_hvs_dump_state(struct drm_device *dev);
+int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused);
+
+/* vc4_kms.c */
+int vc4_kms_load(struct drm_device *dev);
+
+/* vc4_plane.c */
+struct drm_plane *vc4_plane_init(struct drm_device *dev,
+				 enum drm_plane_type type);
+u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist);
+u32 vc4_plane_dlist_size(struct drm_plane_state *state);
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
new file mode 100644
index 0000000..cd3301b
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -0,0 +1,633 @@
+/*
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "drm_atomic_helper.h"
+#include "drm_crtc_helper.h"
+#include "drm_edid.h"
+#include "linux/clk.h"
+#include "linux/component.h"
+#include "linux/of_gpio.h"
+#include "linux/of_platform.h"
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+
+/* General HDMI hardware state. */
+struct vc4_hdmi {
+	struct platform_device *pdev;
+	struct i2c_adapter *ddc;
+	void __iomem *hdmicore_regs;
+	void __iomem *hd_regs;
+	int hpd_gpio;
+
+	/* Probed video mode at boot time, used to filter display
+	 * modes to only allow equivalent ones (since we can't set the
+	 * pixel clock yet).
+	 */
+	struct drm_display_mode boot_mode;
+};
+#define HDMI_READ(offset) readl(vc4->hdmi->hdmicore_regs + offset)
+#define HDMI_WRITE(offset, val) writel(val, vc4->hdmi->hdmicore_regs + offset)
+#define HD_READ(offset) readl(vc4->hdmi->hd_regs + offset)
+#define HD_WRITE(offset, val) writel(val, vc4->hdmi->hd_regs + offset)
+
+/* VC4 HDMI encoder KMS struct */
+struct vc4_hdmi_encoder {
+	struct drm_encoder base;
+	bool hdmi_monitor;
+};
+static inline struct vc4_hdmi_encoder *
+to_vc4_hdmi_encoder(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct vc4_hdmi_encoder, base);
+}
+
+/* VC4 HDMI connector KMS struct */
+struct vc4_hdmi_connector {
+	struct drm_connector base;
+
+	/* Since the connector is attached to just the one encoder,
+	 * this is the reference to it so we can do the best_encoder()
+	 * hook.
+	 */
+	struct drm_encoder *encoder;
+};
+static inline struct vc4_hdmi_connector *
+to_vc4_hdmi_connector(struct drm_connector *connector)
+{
+	return container_of(connector, struct vc4_hdmi_connector, base);
+}
+
+#define HDMI_REG(reg) { reg, #reg }
+static const struct {
+	u32 reg;
+	const char *name;
+} hdmi_regs[] = {
+	HDMI_REG(VC4_HDMI_CORE_REV),
+	HDMI_REG(VC4_HDMI_SW_RESET_CONTROL),
+	HDMI_REG(VC4_HDMI_HOTPLUG_INT),
+	HDMI_REG(VC4_HDMI_HOTPLUG),
+	HDMI_REG(VC4_HDMI_HORZA),
+	HDMI_REG(VC4_HDMI_HORZB),
+	HDMI_REG(VC4_HDMI_FIFO_CTL),
+	HDMI_REG(VC4_HDMI_SCHEDULER_CONTROL),
+	HDMI_REG(VC4_HDMI_VERTA0),
+	HDMI_REG(VC4_HDMI_VERTA1),
+	HDMI_REG(VC4_HDMI_VERTB0),
+	HDMI_REG(VC4_HDMI_VERTB1),
+	HDMI_REG(VC4_HDMI_TX_PHY_RESET_CTL),
+};
+static const struct {
+	u32 reg;
+	const char *name;
+} hd_regs[] = {
+	HDMI_REG(VC4_HD_M_CTL),
+	HDMI_REG(VC4_HD_MAI_CTL),
+	HDMI_REG(VC4_HD_VID_CTL),
+	HDMI_REG(VC4_HD_CSC_CTL),
+	HDMI_REG(VC4_HD_FRAME_COUNT),
+};
+
+#ifdef CONFIG_DEBUG_FS
+int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_device *dev = node->minor->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hdmi_regs); i++) {
+		seq_printf(m, "%s (0x%04x): 0x%08x\n",
+			   hdmi_regs[i].name, hdmi_regs[i].reg,
+			   HDMI_READ(hdmi_regs[i].reg));
+	}
+
+	for (i = 0; i < ARRAY_SIZE(hd_regs); i++) {
+		seq_printf(m, "%s (0x%04x): 0x%08x\n",
+			   hd_regs[i].name, hd_regs[i].reg,
+			   HD_READ(hd_regs[i].reg));
+	}
+
+	return 0;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static void vc4_hdmi_dump_regs(struct drm_device *dev)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hdmi_regs); i++) {
+		DRM_INFO("0x%04x (%s): 0x%08x\n",
+			 hdmi_regs[i].reg, hdmi_regs[i].name,
+			 HDMI_READ(hdmi_regs[i].reg));
+	}
+	for (i = 0; i < ARRAY_SIZE(hd_regs); i++) {
+		DRM_INFO("0x%04x (%s): 0x%08x\n",
+			 hd_regs[i].reg, hd_regs[i].name,
+			 HD_READ(hd_regs[i].reg));
+	}
+}
+
+static enum drm_connector_status
+vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct drm_device *dev = connector->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+	if (vc4->hdmi->hpd_gpio) {
+		if (gpio_get_value(vc4->hdmi->hpd_gpio))
+			return connector_status_connected;
+		else
+			return connector_status_disconnected;
+	}
+
+	if (HDMI_READ(VC4_HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED)
+		return connector_status_connected;
+	else
+		return connector_status_disconnected;
+}
+
+static void vc4_hdmi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct vc4_hdmi_connector *vc4_connector =
+		to_vc4_hdmi_connector(connector);
+	struct drm_encoder *encoder = vc4_connector->encoder;
+	struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+	struct drm_device *dev = connector->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	int ret = 0;
+	struct edid *edid;
+
+	edid = drm_get_edid(connector, vc4->hdmi->ddc);
+	if (!edid)
+		return -ENODEV;
+
+	vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
+	drm_mode_connector_update_edid_property(connector, edid);
+	ret = drm_add_edid_modes(connector, edid);
+
+	return ret;
+}
+
+/* Since we can't set the pixel clock yet, filter out all the EDID
+ * modes that don't match what was set up by the firmware.
+ */
+int vc4_hdmi_mode_valid(struct drm_connector *connector,
+			struct drm_display_mode *mode)
+{
+	struct drm_device *dev = connector->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct drm_display_mode *boot_mode = &vc4->hdmi->boot_mode;
+
+	if (mode->hdisplay != boot_mode->hdisplay ||
+	    mode->hsync_start != boot_mode->hsync_start ||
+	    mode->hsync_end != boot_mode->hsync_end ||
+	    mode->htotal != boot_mode->htotal) {
+		return MODE_ERROR;
+	}
+
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+vc4_hdmi_connector_best_encoder(struct drm_connector *connector)
+{
+	struct vc4_hdmi_connector *hdmi_connector =
+		to_vc4_hdmi_connector(connector);
+	return hdmi_connector->encoder;
+}
+
+static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.detect = vc4_hdmi_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = vc4_hdmi_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = {
+	.get_modes = vc4_hdmi_connector_get_modes,
+	.mode_valid = vc4_hdmi_mode_valid,
+	.best_encoder = vc4_hdmi_connector_best_encoder,
+};
+
+struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
+					      struct drm_encoder *encoder)
+{
+	struct drm_connector *connector = NULL;
+	struct vc4_hdmi_connector *hdmi_connector;
+	int ret = 0;
+
+	hdmi_connector = devm_kzalloc(dev->dev, sizeof(*hdmi_connector),
+				      GFP_KERNEL);
+	if (!hdmi_connector) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	connector = &hdmi_connector->base;
+
+	hdmi_connector->encoder = encoder;
+
+	drm_connector_init(dev, connector, &vc4_hdmi_connector_funcs,
+			   DRM_MODE_CONNECTOR_HDMIA);
+	drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs);
+
+	connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
+			     DRM_CONNECTOR_POLL_DISCONNECT);
+
+	connector->interlace_allowed = 0;
+	connector->doublescan_allowed = 0;
+
+	drm_connector_register(connector);
+
+	drm_mode_connector_attach_encoder(connector, encoder);
+
+	return connector;
+
+ fail:
+	if (connector)
+		vc4_hdmi_connector_destroy(connector);
+
+	return ERR_PTR(ret);
+}
+
+static void vc4_encoder_destroy(struct drm_encoder *encoder)
+{
+	drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs vc4_hdmi_encoder_funcs = {
+	.destroy = vc4_encoder_destroy,
+};
+
+static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+				      struct drm_display_mode *unadjusted_mode,
+				      struct drm_display_mode *mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	bool debug_dump_regs = false;
+	bool hsync_pos = !(mode->flags & DRM_MODE_FLAG_NHSYNC);
+	bool vsync_pos = !(mode->flags & DRM_MODE_FLAG_NVSYNC);
+	u32 vactive = (mode->vdisplay >>
+		       ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0));
+	u32 verta = (VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
+				   VC4_HDMI_VERTA_VSP) |
+		     VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
+				   VC4_HDMI_VERTA_VFP) |
+		     VC4_SET_FIELD(vactive, VC4_HDMI_VERTA_VAL));
+	u32 vertb = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) |
+		     VC4_SET_FIELD(mode->vtotal - mode->vsync_end,
+				   VC4_HDMI_VERTB_VBP));
+
+	if (debug_dump_regs) {
+		DRM_INFO("HDMI regs before:\n");
+		vc4_hdmi_dump_regs(dev);
+	}
+
+	HD_WRITE(VC4_HD_VID_CTL, 0);
+
+	/* XXX: This is where we would set the HDMI state machine
+	 * clock, if we had an interface for it.
+	 */
+
+	HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
+		   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
+		   VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
+		   VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS);
+
+	HDMI_WRITE(VC4_HDMI_HORZA,
+		   (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) |
+		   (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) |
+		   VC4_SET_FIELD(mode->hdisplay, VC4_HDMI_HORZA_HAP));
+
+	HDMI_WRITE(VC4_HDMI_HORZB,
+		   VC4_SET_FIELD(mode->htotal - mode->hsync_end,
+				 VC4_HDMI_HORZB_HBP) |
+		   VC4_SET_FIELD(mode->hsync_end - mode->hsync_start,
+				 VC4_HDMI_HORZB_HSP) |
+		   VC4_SET_FIELD(mode->hsync_start - mode->hdisplay,
+				 VC4_HDMI_HORZB_HFP));
+
+	HDMI_WRITE(VC4_HDMI_VERTA0, verta);
+	HDMI_WRITE(VC4_HDMI_VERTA1, verta);
+
+	HDMI_WRITE(VC4_HDMI_VERTB0, vertb);
+	HDMI_WRITE(VC4_HDMI_VERTB1, vertb);
+
+	HD_WRITE(VC4_HD_VID_CTL,
+		 (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
+		 (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
+
+	/* The RGB order applies even when CSC is disabled. */
+	HD_WRITE(VC4_HD_CSC_CTL, VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR,
+					       VC4_HD_CSC_CTL_ORDER));
+
+	HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
+
+	if (debug_dump_regs) {
+		DRM_INFO("HDMI regs after:\n");
+		vc4_hdmi_dump_regs(dev);
+	}
+}
+
+static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+	HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+	HD_WRITE(VC4_HD_VID_CTL,
+		 HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
+}
+
+static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+	struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+	HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
+
+	HD_WRITE(VC4_HD_VID_CTL,
+		 HD_READ(VC4_HD_VID_CTL) |
+		 VC4_HD_VID_CTL_ENABLE |
+		 VC4_HD_VID_CTL_UNDERFLOW_ENABLE |
+		 VC4_HD_VID_CTL_FRAME_COUNTER_RESET);
+
+	if (vc4_encoder->hdmi_monitor) {
+		HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
+			   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
+			   VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
+
+		while (!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
+			 VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE)) {
+			cpu_relax();
+		}
+	} else {
+		HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
+			   HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) &
+			   ~(VC4_HDMI_RAM_PACKET_ENABLE));
+		HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
+			   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
+			   ~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
+
+		while (HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
+		       VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE) {
+			cpu_relax();
+		}
+	}
+
+	if (vc4_encoder->hdmi_monitor) {
+		u32 drift;
+
+		WARN_ON(!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
+			  VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE));
+		HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
+			   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
+			   VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT);
+
+		/* XXX: Set HDMI_RAM_PACKET_CONFIG (1 << 16) and set
+		 * up the infoframe.
+		 */
+
+		drift = HDMI_READ(VC4_HDMI_FIFO_CTL);
+		drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK;
+
+		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
+			   drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
+		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
+			   drift | VC4_HDMI_FIFO_CTL_RECENTER);
+		udelay(1000);
+		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
+			   drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
+		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
+			   drift | VC4_HDMI_FIFO_CTL_RECENTER);
+
+		while ((HDMI_READ(VC4_HDMI_FIFO_CTL) &
+			VC4_HDMI_FIFO_CTL_RECENTER_DONE) == 0) {
+			cpu_relax();
+		}
+	}
+}
+
+static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
+	.mode_set = vc4_hdmi_encoder_mode_set,
+	.disable = vc4_hdmi_encoder_disable,
+	.enable = vc4_hdmi_encoder_enable,
+};
+
+static struct drm_crtc *
+vc4_get_crtc_node(struct platform_device *pdev)
+{
+	struct device_node *crtc_node;
+	struct platform_device *crtc_pdev;
+
+	crtc_node = of_parse_phandle(pdev->dev.of_node, "crtc", 0);
+	if (!crtc_node) {
+		DRM_ERROR("No CRTC for hdmi in DT\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	crtc_pdev = of_find_device_by_node(crtc_node);
+	if (!crtc_pdev) {
+		DRM_ERROR("No CRTC device attached to OF node\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	return platform_get_drvdata(crtc_pdev);
+}
+
+struct drm_encoder *vc4_hdmi_encoder_init(struct drm_device *dev)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct drm_encoder *encoder = NULL;
+	struct vc4_hdmi_encoder *vc4_hdmi_encoder;
+	struct drm_crtc *crtc;
+	int ret;
+
+	vc4_hdmi_encoder = devm_kzalloc(dev->dev, sizeof(*vc4_hdmi_encoder),
+					GFP_KERNEL);
+	if (!vc4_hdmi_encoder) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	encoder = &vc4_hdmi_encoder->base;
+
+	crtc = vc4_get_crtc_node(vc4->hdmi->pdev);
+	if (IS_ERR(crtc)) {
+		ret = PTR_ERR(crtc);
+		goto fail;
+	}
+
+	drm_encoder_init(dev, encoder, &vc4_hdmi_encoder_funcs,
+			 DRM_MODE_ENCODER_TMDS);
+	drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs);
+
+	encoder->possible_crtcs = drm_crtc_mask(crtc);
+
+	return encoder;
+
+fail:
+	if (encoder)
+		vc4_encoder_destroy(encoder);
+
+	return ERR_PTR(ret);
+}
+
+/* Reads out the current HDMI mode progreamming at driver load time.
+ *
+ * This is currently used for later filtering out of the DDC-probed
+ * video modes, since we can't actually change modes due to not having
+ * control of the necessary clocks.  Later on, we may end up reusing
+ * this for skipping modesets at boot time.
+ */
+static void
+vc4_hdmi_get_boot_display_mode(struct vc4_dev *vc4)
+{
+	struct drm_display_mode *mode = &vc4->hdmi->boot_mode;
+	u32 horza = HDMI_READ(VC4_HDMI_HORZA);
+	u32 horzb = HDMI_READ(VC4_HDMI_HORZB);
+	u32 verta = HDMI_READ(VC4_HDMI_VERTA0);
+	u32 vertb = HDMI_READ(VC4_HDMI_VERTB0);
+
+	memset(mode, 0, sizeof(*mode));
+
+	if (!(horza & VC4_HDMI_HORZA_VPOS))
+		mode->flags |= DRM_MODE_FLAG_NVSYNC;
+	if (!(horza & VC4_HDMI_HORZA_HPOS))
+		mode->flags |= DRM_MODE_FLAG_NHSYNC;
+	mode->hdisplay = VC4_GET_FIELD(horza, VC4_HDMI_HORZA_HAP);
+	mode->hsync_start = (mode->hdisplay +
+			     VC4_GET_FIELD(horzb, VC4_HDMI_HORZB_HFP));
+	mode->hsync_end = (mode->hsync_start +
+			   VC4_GET_FIELD(horzb, VC4_HDMI_HORZB_HSP));
+	mode->htotal = (mode->hsync_end +
+			VC4_GET_FIELD(horzb, VC4_HDMI_HORZB_HBP));
+
+	mode->vdisplay = VC4_GET_FIELD(verta, VC4_HDMI_VERTA_VAL);
+	mode->vsync_start = (mode->vdisplay +
+			     VC4_GET_FIELD(verta, VC4_HDMI_VERTA_VFP));
+	mode->vsync_end = (mode->vsync_start +
+			   VC4_GET_FIELD(verta, VC4_HDMI_VERTA_VSP));
+	mode->vtotal = (mode->vsync_end +
+			VC4_GET_FIELD(vertb, VC4_HDMI_VERTB_VBP));
+}
+
+static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct vc4_dev *vc4 = drm->dev_private;
+	struct vc4_hdmi *hdmi;
+	struct device_node *ddc_node;
+	u32 value;
+
+	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi->pdev = pdev;
+	hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0);
+	if (IS_ERR(hdmi->hdmicore_regs))
+		return PTR_ERR(hdmi->hdmicore_regs);
+
+	hdmi->hd_regs = vc4_ioremap_regs(pdev, 1);
+	if (IS_ERR(hdmi->hd_regs))
+		return PTR_ERR(hdmi->hd_regs);
+
+	/* DDC i2c driver */
+	ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
+	if (!ddc_node) {
+		DRM_ERROR("Failed to find ddc node in device tree\n");
+		return -ENODEV;
+	}
+
+	hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
+	if (!hdmi->ddc) {
+		DRM_ERROR("Failed to get ddc i2c adapter by node\n");
+		return -EPROBE_DEFER;
+	}
+
+	/* Only use the GPIO HPD pin if present in the DT, otherwise
+	 * we'll use the HDMI core's register.
+	 */
+	if (of_find_property(dev->of_node, "hpd-gpio", &value)) {
+		hdmi->hpd_gpio = of_get_named_gpio(dev->of_node, "hpd-gpio", 0);
+		if (hdmi->hpd_gpio < 0)
+			return hdmi->hpd_gpio;
+	}
+
+	vc4->hdmi = hdmi;
+
+	/* HDMI core must be enabled. */
+	WARN_ON_ONCE((HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE) == 0);
+
+	vc4_hdmi_get_boot_display_mode(vc4);
+
+	return 0;
+}
+
+static void vc4_hdmi_unbind(struct device *dev, struct device *master,
+			    void *data)
+{
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct vc4_dev *vc4 = drm->dev_private;
+
+	put_device(&vc4->hdmi->ddc->dev);
+
+	vc4->hdmi = NULL;
+}
+
+static const struct component_ops vc4_hdmi_ops = {
+	.bind   = vc4_hdmi_bind,
+	.unbind = vc4_hdmi_unbind,
+};
+
+static int vc4_hdmi_dev_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &vc4_hdmi_ops);
+}
+
+static int vc4_hdmi_dev_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &vc4_hdmi_ops);
+	return 0;
+}
+
+static const struct of_device_id vc4_hdmi_dt_match[] = {
+	{ .compatible = "brcm,vc4-hdmi" },
+	{}
+};
+
+struct platform_driver vc4_hdmi_driver = {
+	.probe = vc4_hdmi_dev_probe,
+	.remove = vc4_hdmi_dev_remove,
+	.driver = {
+		.name = "vc4_hdmi",
+		.of_match_table = vc4_hdmi_dt_match,
+	},
+};
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
new file mode 100644
index 0000000..34906fd
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/**
+ * DOC: VC4 HVS module.
+ *
+ * The HVS is the piece of hardware that does translation, scaling,
+ * colorspace conversion, and compositing of pixels stored in
+ * framebuffers into a FIFO of pixels going out to the Pixel Valve
+ * (CRTC).
+ *
+ * There is a single global HVS, with multiple output FIFOs that can
+ * be consumed by the PVs.  This file just manages the resources for
+ * the HVS, while the vc4_crtc.c code actually drives HVS setup for
+ * each CRTC.
+ */
+
+#include "linux/component.h"
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+
+#define HVS_REG(reg) { reg, #reg }
+static const struct {
+	u32 reg;
+	const char *name;
+} hvs_regs[] = {
+	HVS_REG(SCALER_DISPCTRL),
+	HVS_REG(SCALER_DISPSTAT),
+	HVS_REG(SCALER_DISPID),
+	HVS_REG(SCALER_DISPECTRL),
+	HVS_REG(SCALER_DISPPROF),
+	HVS_REG(SCALER_DISPDITHER),
+	HVS_REG(SCALER_DISPEOLN),
+	HVS_REG(SCALER_DISPLIST0),
+	HVS_REG(SCALER_DISPLIST1),
+	HVS_REG(SCALER_DISPLIST2),
+	HVS_REG(SCALER_DISPLSTAT),
+	HVS_REG(SCALER_DISPLACT0),
+	HVS_REG(SCALER_DISPLACT1),
+	HVS_REG(SCALER_DISPLACT2),
+	HVS_REG(SCALER_DISPCTRL0),
+	HVS_REG(SCALER_DISPBKGND0),
+	HVS_REG(SCALER_DISPSTAT0),
+	HVS_REG(SCALER_DISPBASE0),
+	HVS_REG(SCALER_DISPCTRL1),
+	HVS_REG(SCALER_DISPBKGND1),
+	HVS_REG(SCALER_DISPSTAT1),
+	HVS_REG(SCALER_DISPBASE1),
+	HVS_REG(SCALER_DISPCTRL2),
+	HVS_REG(SCALER_DISPBKGND2),
+	HVS_REG(SCALER_DISPSTAT2),
+	HVS_REG(SCALER_DISPBASE2),
+	HVS_REG(SCALER_DISPALPHA2),
+};
+
+void vc4_hvs_dump_state(struct drm_device *dev)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hvs_regs); i++) {
+		DRM_INFO("0x%04x (%s): 0x%08x\n",
+			 hvs_regs[i].reg, hvs_regs[i].name,
+			 HVS_READ(hvs_regs[i].reg));
+	}
+
+	DRM_INFO("HVS ctx:\n");
+	for (i = 0; i < 64; i += 4) {
+		DRM_INFO("0x%08x (%s): 0x%08x 0x%08x 0x%08x 0x%08x\n",
+			 i * 4, i < HVS_BOOTLOADER_DLIST_END ? "B" : "D",
+			 ((uint32_t *)vc4->hvs->dlist)[i + 0],
+			 ((uint32_t *)vc4->hvs->dlist)[i + 1],
+			 ((uint32_t *)vc4->hvs->dlist)[i + 2],
+			 ((uint32_t *)vc4->hvs->dlist)[i + 3]);
+	}
+}
+
+#ifdef CONFIG_DEBUG_FS
+int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_device *dev = node->minor->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(hvs_regs); i++) {
+		seq_printf(m, "%s (0x%04x): 0x%08x\n",
+			   hvs_regs[i].name, hvs_regs[i].reg,
+			   HVS_READ(hvs_regs[i].reg));
+	}
+
+	return 0;
+}
+#endif
+
+static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct vc4_dev *vc4 = drm->dev_private;
+	struct vc4_hvs *hvs = NULL;
+
+	hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL);
+	if (!hvs)
+		return -ENOMEM;
+
+	hvs->pdev = pdev;
+
+	hvs->regs = vc4_ioremap_regs(pdev, 0);
+	if (IS_ERR(hvs->regs))
+		return PTR_ERR(hvs->regs);
+
+	hvs->dlist = hvs->regs + SCALER_DLIST_START;
+
+	vc4->hvs = hvs;
+	return 0;
+}
+
+static void vc4_hvs_unbind(struct device *dev, struct device *master,
+			   void *data)
+{
+	struct drm_device *drm = dev_get_drvdata(master);
+	struct vc4_dev *vc4 = drm->dev_private;
+
+	vc4->hvs = NULL;
+}
+
+static const struct component_ops vc4_hvs_ops = {
+	.bind   = vc4_hvs_bind,
+	.unbind = vc4_hvs_unbind,
+};
+
+static int vc4_hvs_dev_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &vc4_hvs_ops);
+}
+
+static int vc4_hvs_dev_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &vc4_hvs_ops);
+	return 0;
+}
+
+static const struct of_device_id vc4_hvs_dt_match[] = {
+	{ .compatible = "brcm,vc4-hvs" },
+	{}
+};
+
+struct platform_driver vc4_hvs_driver = {
+	.probe = vc4_hvs_dev_probe,
+	.remove = vc4_hvs_dev_remove,
+	.driver = {
+		.name = "vc4_hvs",
+		.of_match_table = vc4_hvs_dt_match,
+	},
+};
diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
new file mode 100644
index 0000000..e5e96bc
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_kms.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/**
+ * DOC: VC4 KMS
+ *
+ * This is the general code for implementing KMS mode setting that
+ * doesn't clearly associate with any of the other objects (plane,
+ * crtc, HDMI encoder).
+ */
+
+#include "drm_crtc.h"
+#include "drm_atomic_helper.h"
+#include "drm_crtc_helper.h"
+#include "drm_plane_helper.h"
+#include "drm_fb_cma_helper.h"
+#include "vc4_drv.h"
+
+static const struct drm_mode_config_funcs vc4_mode_funcs = {
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+	.fb_create = drm_fb_cma_create,
+};
+
+/* Calls out to initialize all of the VC4 KMS objects. */
+static int vc4_init_modeset_objects(struct drm_device *dev)
+{
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	int ret = 0;
+
+	encoder = vc4_hdmi_encoder_init(dev);
+	if (IS_ERR(encoder)) {
+		dev_err(dev->dev, "failed to construct HDMI encoder\n");
+		ret = PTR_ERR(encoder);
+		goto fail;
+	}
+
+	connector = vc4_hdmi_connector_init(dev, encoder);
+	if (IS_ERR(connector)) {
+		ret = PTR_ERR(connector);
+		dev_err(dev->dev, "failed to initialize HDMI connector\n");
+		goto fail;
+	}
+
+fail:
+	return ret;
+}
+
+int vc4_kms_load(struct drm_device *dev)
+{
+	int ret;
+
+	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
+	if (ret < 0) {
+		dev_err(dev->dev, "failed to initialize vblank\n");
+		return ret;
+	}
+
+	dev->mode_config.max_width = 2048;
+	dev->mode_config.max_height = 2048;
+	dev->mode_config.funcs = &vc4_mode_funcs;
+	dev->mode_config.preferred_depth = 24;
+
+	ret = vc4_init_modeset_objects(dev);
+	if (ret)
+		goto fail;
+
+	drm_mode_config_reset(dev);
+
+	drm_fbdev_cma_init(dev, 32,
+			   dev->mode_config.num_crtc,
+			   dev->mode_config.num_connector);
+
+	drm_kms_helper_poll_init(dev);
+
+fail:
+	return ret;
+}
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
new file mode 100644
index 0000000..cdd8b10
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/**
+ * DOC: VC4 plane module
+ *
+ * Each DRM plane is a layer of pixels being scanned out by the HVS.
+ *
+ * At atomic modeset check time, we compute the HVS display element
+ * state that would be necessary for displaying the plane (giving us a
+ * chance to figure out if a plane configuration is invalid), then at
+ * atomic flush time the CRTC will ask us to write our element state
+ * into the region of the HVS that it has allocated for us.
+ */
+
+#include "vc4_drv.h"
+#include "vc4_regs.h"
+#include "drm_atomic_helper.h"
+#include "drm_fb_cma_helper.h"
+#include "drm_plane_helper.h"
+
+struct vc4_plane_state {
+	struct drm_plane_state base;
+	u32 *dlist;
+	u32 dlist_size; /* Number of dwords in allocated for the display list */
+	u32 dlist_count; /* Number of used dwords in the display list. */
+};
+
+static inline struct vc4_plane_state *
+to_vc4_plane_state(struct drm_plane_state *state)
+{
+	return (struct vc4_plane_state *)state;
+}
+
+static const struct hvs_format {
+	u32 drm; /* DRM_FORMAT_* */
+	u32 hvs; /* HVS_FORMAT_* */
+	u32 pixel_order;
+	bool has_alpha;
+} hvs_formats[] = {
+	{
+		.drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
+		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
+	},
+	{
+		.drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
+		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
+	},
+};
+
+static const struct hvs_format *vc4_get_hvs_format(u32 drm_format)
+{
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
+		if (hvs_formats[i].drm == drm_format)
+			return &hvs_formats[i];
+	}
+
+	return NULL;
+}
+
+static bool plane_enabled(struct drm_plane_state *state)
+{
+	return state->fb && state->crtc;
+}
+
+struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
+{
+	struct vc4_plane_state *vc4_state;
+
+	if (WARN_ON(!plane->state))
+		return NULL;
+
+	vc4_state = kmemdup(plane->state, sizeof(*vc4_state), GFP_KERNEL);
+	if (!vc4_state)
+		return NULL;
+
+	__drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base);
+
+	if (vc4_state->dlist) {
+		vc4_state->dlist = kmemdup(vc4_state->dlist,
+					   vc4_state->dlist_count * 4,
+					   GFP_KERNEL);
+		if (!vc4_state->dlist) {
+			kfree(vc4_state);
+			return NULL;
+		}
+		vc4_state->dlist_size = vc4_state->dlist_count;
+	}
+
+	return &vc4_state->base;
+}
+
+void vc4_plane_destroy_state(struct drm_plane *plane,
+			     struct drm_plane_state *state)
+{
+	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+
+	kfree(vc4_state->dlist);
+	__drm_atomic_helper_plane_destroy_state(plane, &vc4_state->base);
+	kfree(state);
+}
+
+/* Called during init to allocate the plane's atomic state. */
+void vc4_plane_reset(struct drm_plane *plane)
+{
+	struct vc4_plane_state *vc4_state;
+
+	WARN_ON(plane->state);
+
+	vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
+	if (!vc4_state)
+		return;
+
+	plane->state = &vc4_state->base;
+	vc4_state->base.plane = plane;
+}
+
+static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val)
+{
+	if (vc4_state->dlist_count == vc4_state->dlist_size) {
+		u32 new_size = max(4u, vc4_state->dlist_count * 2);
+		u32 *new_dlist = kmalloc(new_size * 4, GFP_KERNEL);
+
+		if (!new_dlist)
+			return;
+		memcpy(new_dlist, vc4_state->dlist, vc4_state->dlist_count * 4);
+
+		kfree(vc4_state->dlist);
+		vc4_state->dlist = new_dlist;
+		vc4_state->dlist_size = new_size;
+	}
+
+	vc4_state->dlist[vc4_state->dlist_count++] = val;
+}
+
+/* Writes out a full display list for an active plane to the plane's
+ * private dlist state.
+ */
+static int vc4_plane_mode_set(struct drm_plane *plane,
+			      struct drm_plane_state *state)
+{
+	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
+	u32 ctl0_offset = vc4_state->dlist_count;
+	const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format);
+	uint32_t offset = fb->offsets[0];
+	int crtc_x = state->crtc_x;
+	int crtc_y = state->crtc_y;
+	int crtc_w = state->crtc_w;
+	int crtc_h = state->crtc_h;
+
+	if (crtc_x < 0) {
+		offset += drm_format_plane_cpp(fb->pixel_format, 0) * -crtc_x;
+		crtc_w += crtc_x;
+		crtc_x = 0;
+	}
+
+	if (crtc_y < 0) {
+		offset += fb->pitches[0] * -crtc_y;
+		crtc_h += crtc_y;
+		crtc_y = 0;
+	}
+
+	vc4_dlist_write(vc4_state,
+			SCALER_CTL0_VALID |
+			(format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |
+			(format->hvs << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
+			SCALER_CTL0_UNITY);
+
+	/* Position Word 0: Image Positions and Alpha Value */
+	vc4_dlist_write(vc4_state,
+			VC4_SET_FIELD(0xff, SCALER_POS0_FIXED_ALPHA) |
+			VC4_SET_FIELD(crtc_x, SCALER_POS0_START_X) |
+			VC4_SET_FIELD(crtc_y, SCALER_POS0_START_Y));
+
+	/* Position Word 1: Scaled Image Dimensions.
+	 * Skipped due to SCALER_CTL0_UNITY scaling.
+	 */
+
+	/* Position Word 2: Source Image Size, Alpha Mode */
+	vc4_dlist_write(vc4_state,
+			VC4_SET_FIELD(format->has_alpha ?
+				      SCALER_POS2_ALPHA_MODE_PIPELINE :
+				      SCALER_POS2_ALPHA_MODE_FIXED,
+				      SCALER_POS2_ALPHA_MODE) |
+			VC4_SET_FIELD(crtc_w, SCALER_POS2_WIDTH) |
+			VC4_SET_FIELD(crtc_h, SCALER_POS2_HEIGHT));
+
+	/* Position Word 3: Context.  Written by the HVS. */
+	vc4_dlist_write(vc4_state, 0xc0c0c0c0);
+
+	/* Pointer Word 0: RGB / Y Pointer */
+	vc4_dlist_write(vc4_state, bo->paddr + offset);
+
+	/* Pointer Context Word 0: Written by the HVS */
+	vc4_dlist_write(vc4_state, 0xc0c0c0c0);
+
+	/* Pitch word 0: Pointer 0 Pitch */
+	vc4_dlist_write(vc4_state,
+			VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH));
+
+	vc4_state->dlist[ctl0_offset] |=
+		VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE);
+
+	return 0;
+}
+
+/* If a modeset involves changing the setup of a plane, the atomic
+ * infrastructure will call this to validate a proposed plane setup.
+ * However, if a plane isn't getting updated, this (and the
+ * corresponding vc4_plane_atomic_update) won't get called.  Thus, we
+ * compute the dlist here and have all active plane dlists get updated
+ * in the CRTC's flush.
+ */
+static int vc4_plane_atomic_check(struct drm_plane *plane,
+				  struct drm_plane_state *state)
+{
+	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+
+	vc4_state->dlist_count = 0;
+
+	if (plane_enabled(state))
+		return vc4_plane_mode_set(plane, state);
+	else
+		return 0;
+}
+
+static void vc4_plane_atomic_update(struct drm_plane *plane,
+				    struct drm_plane_state *old_state)
+{
+	/* No contents here.  Since we don't know where in the CRTC's
+	 * dlist we should be stored, our dlist is uploaded to the
+	 * hardware with vc4_plane_write_dlist() at CRTC atomic_flush
+	 * time.
+	 */
+}
+
+u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist)
+{
+	struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
+	int i;
+
+	/* Can't memcpy_toio() because it needs to be 32-bit writes. */
+	for (i = 0; i < vc4_state->dlist_count; i++)
+		writel(vc4_state->dlist[i], &dlist[i]);
+
+	return vc4_state->dlist_count;
+}
+
+u32 vc4_plane_dlist_size(struct drm_plane_state *state)
+{
+	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+
+	return vc4_state->dlist_count;
+}
+
+static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
+	.prepare_fb = NULL,
+	.cleanup_fb = NULL,
+	.atomic_check = vc4_plane_atomic_check,
+	.atomic_update = vc4_plane_atomic_update,
+};
+
+static void vc4_plane_destroy(struct drm_plane *plane)
+{
+	drm_plane_helper_disable(plane);
+	drm_plane_cleanup(plane);
+}
+
+static const struct drm_plane_funcs vc4_plane_funcs = {
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
+	.destroy = vc4_plane_destroy,
+	.set_property = NULL,
+	.reset = vc4_plane_reset,
+	.atomic_duplicate_state = vc4_plane_duplicate_state,
+	.atomic_destroy_state = vc4_plane_destroy_state,
+};
+
+struct drm_plane *vc4_plane_init(struct drm_device *dev,
+				 enum drm_plane_type type)
+{
+	struct drm_plane *plane = NULL;
+	struct vc4_plane *vc4_plane;
+	u32 formats[ARRAY_SIZE(hvs_formats)];
+	int ret = 0;
+	unsigned i;
+
+	vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
+				 GFP_KERNEL);
+	if (!vc4_plane) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++)
+		formats[i] = hvs_formats[i].drm;
+	plane = &vc4_plane->base;
+	ret = drm_universal_plane_init(dev, plane, 0xff,
+				       &vc4_plane_funcs,
+				       formats, ARRAY_SIZE(formats),
+				       type);
+
+	drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
+
+	return plane;
+fail:
+	if (plane)
+		vc4_plane_destroy(plane);
+
+	return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h
new file mode 100644
index 0000000..0eff631
--- /dev/null
+++ b/drivers/gpu/drm/vc4/vc4_regs.h
@@ -0,0 +1,562 @@
+/*
+ *  Copyright ? 2014-2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define VC4_MASK(high, low) (((1 << ((high) - (low) + 1)) - 1) << (low))
+/* Using the GNU statement expression extension */
+#define VC4_SET_FIELD(value, field)					\
+	({								\
+		uint32_t fieldval = (value) << field##_SHIFT;		\
+		WARN_ON((fieldval & ~field##_MASK) != 0);		\
+		fieldval & field##_MASK;				\
+	 })
+
+#define VC4_GET_FIELD(word, field) (((word) & field##_MASK) >>		\
+				    field##_SHIFT)
+
+#define V3D_IDENT0   0x00000
+# define V3D_EXPECTED_IDENT0 \
+	((2 << 24) | \
+	('V' << 0) | \
+	('3' << 8) | \
+	 ('D' << 16))
+
+#define V3D_IDENT1   0x00004
+/* Multiples of 1kb */
+# define V3D_IDENT1_VPM_SIZE_MASK                      VC4_MASK(31, 28)
+# define V3D_IDENT1_VPM_SIZE_SHIFT                     28
+# define V3D_IDENT1_NSEM_MASK                          VC4_MASK(23, 16)
+# define V3D_IDENT1_NSEM_SHIFT                         16
+# define V3D_IDENT1_TUPS_MASK                          VC4_MASK(15, 12)
+# define V3D_IDENT1_TUPS_SHIFT                         12
+# define V3D_IDENT1_QUPS_MASK                          VC4_MASK(11, 8)
+# define V3D_IDENT1_QUPS_SHIFT                         8
+# define V3D_IDENT1_NSLC_MASK                          VC4_MASK(7, 4)
+# define V3D_IDENT1_NSLC_SHIFT                         4
+# define V3D_IDENT1_REV_MASK                           VC4_MASK(3, 0)
+# define V3D_IDENT1_REV_SHIFT                          0
+
+#define V3D_IDENT2   0x00008
+#define V3D_SCRATCH  0x00010
+#define V3D_L2CACTL  0x00020
+# define V3D_L2CACTL_L2CCLR                            (1 << 2)
+# define V3D_L2CACTL_L2CDIS                            (1 << 1)
+# define V3D_L2CACTL_L2CENA                            (1 << 0)
+
+#define V3D_SLCACTL  0x00024
+# define V3D_SLCACTL_T1CC_MASK                         VC4_MASK(27, 24)
+# define V3D_SLCACTL_T1CC_SHIFT                        24
+# define V3D_SLCACTL_T0CC_MASK                         VC4_MASK(19, 16)
+# define V3D_SLCACTL_T0CC_SHIFT                        16
+# define V3D_SLCACTL_UCC_MASK                          VC4_MASK(11, 8)
+# define V3D_SLCACTL_UCC_SHIFT                         8
+# define V3D_SLCACTL_ICC_MASK                          VC4_MASK(3, 0)
+# define V3D_SLCACTL_ICC_SHIFT                         0
+
+#define V3D_INTCTL   0x00030
+#define V3D_INTENA   0x00034
+#define V3D_INTDIS   0x00038
+# define V3D_INT_SPILLUSE                              (1 << 3)
+# define V3D_INT_OUTOMEM                               (1 << 2)
+# define V3D_INT_FLDONE                                (1 << 1)
+# define V3D_INT_FRDONE                                (1 << 0)
+
+#define V3D_CT0CS    0x00100
+#define V3D_CT1CS    0x00104
+#define V3D_CTNCS(n) (V3D_CT0CS + 4 * n)
+# define V3D_CTRSTA      (1 << 15)
+# define V3D_CTSEMA      (1 << 12)
+# define V3D_CTRTSD      (1 << 8)
+# define V3D_CTRUN       (1 << 5)
+# define V3D_CTSUBS      (1 << 4)
+# define V3D_CTERR       (1 << 3)
+# define V3D_CTMODE      (1 << 0)
+
+#define V3D_CT0EA    0x00108
+#define V3D_CT1EA    0x0010c
+#define V3D_CTNEA(n) (V3D_CT0EA + 4 * (n))
+#define V3D_CT0CA    0x00110
+#define V3D_CT1CA    0x00114
+#define V3D_CTNCA(n) (V3D_CT0CA + 4 * (n))
+#define V3D_CT00RA0  0x00118
+#define V3D_CT01RA0  0x0011c
+#define V3D_CTNRA0(n) (V3D_CT00RA0 + 4 * (n))
+#define V3D_CT0LC    0x00120
+#define V3D_CT1LC    0x00124
+#define V3D_CTNLC(n) (V3D_CT0LC + 4 * (n))
+#define V3D_CT0PC    0x00128
+#define V3D_CT1PC    0x0012c
+#define V3D_CTNPC(n) (V3D_CT0PC + 4 * (n))
+
+#define V3D_PCS      0x00130
+# define V3D_BMOOM       (1 << 8)
+# define V3D_RMBUSY      (1 << 3)
+# define V3D_RMACTIVE    (1 << 2)
+# define V3D_BMBUSY      (1 << 1)
+# define V3D_BMACTIVE    (1 << 0)
+
+#define V3D_BFC      0x00134
+#define V3D_RFC      0x00138
+#define V3D_BPCA     0x00300
+#define V3D_BPCS     0x00304
+#define V3D_BPOA     0x00308
+#define V3D_BPOS     0x0030c
+#define V3D_BXCF     0x00310
+#define V3D_SQRSV0   0x00410
+#define V3D_SQRSV1   0x00414
+#define V3D_SQCNTL   0x00418
+#define V3D_SRQPC    0x00430
+#define V3D_SRQUA    0x00434
+#define V3D_SRQUL    0x00438
+#define V3D_SRQCS    0x0043c
+#define V3D_VPACNTL  0x00500
+#define V3D_VPMBASE  0x00504
+#define V3D_PCTRC    0x00670
+#define V3D_PCTRE    0x00674
+#define V3D_PCTR0    0x00680
+#define V3D_PCTRS0   0x00684
+#define V3D_PCTR1    0x00688
+#define V3D_PCTRS1   0x0068c
+#define V3D_PCTR2    0x00690
+#define V3D_PCTRS2   0x00694
+#define V3D_PCTR3    0x00698
+#define V3D_PCTRS3   0x0069c
+#define V3D_PCTR4    0x006a0
+#define V3D_PCTRS4   0x006a4
+#define V3D_PCTR5    0x006a8
+#define V3D_PCTRS5   0x006ac
+#define V3D_PCTR6    0x006b0
+#define V3D_PCTRS6   0x006b4
+#define V3D_PCTR7    0x006b8
+#define V3D_PCTRS7   0x006bc
+#define V3D_PCTR8    0x006c0
+#define V3D_PCTRS8   0x006c4
+#define V3D_PCTR9    0x006c8
+#define V3D_PCTRS9   0x006cc
+#define V3D_PCTR10   0x006d0
+#define V3D_PCTRS10  0x006d4
+#define V3D_PCTR11   0x006d8
+#define V3D_PCTRS11  0x006dc
+#define V3D_PCTR12   0x006e0
+#define V3D_PCTRS12  0x006e4
+#define V3D_PCTR13   0x006e8
+#define V3D_PCTRS13  0x006ec
+#define V3D_PCTR14   0x006f0
+#define V3D_PCTRS14  0x006f4
+#define V3D_PCTR15   0x006f8
+#define V3D_PCTRS15  0x006fc
+#define V3D_BGE      0x00f00
+#define V3D_FDBGO    0x00f04
+#define V3D_FDBGB    0x00f08
+#define V3D_FDBGR    0x00f0c
+#define V3D_FDBGS    0x00f10
+#define V3D_ERRSTAT  0x00f20
+
+#define PV_CONTROL				0x00
+# define PV_CONTROL_FORMAT_MASK			VC4_MASK(23, 21)
+# define PV_CONTROL_FORMAT_SHIFT		21
+# define PV_CONTROL_FORMAT_24			0
+# define PV_CONTROL_FORMAT_DSIV_16		1
+# define PV_CONTROL_FORMAT_DSIC_16		2
+# define PV_CONTROL_FORMAT_DSIV_18		3
+# define PV_CONTROL_FORMAT_DSIV_24		4
+
+# define PV_CONTROL_FIFO_LEVEL_MASK		VC4_MASK(20, 15)
+# define PV_CONTROL_FIFO_LEVEL_SHIFT		15
+# define PV_CONTROL_CLR_AT_START		(1 << 14)
+# define PV_CONTROL_TRIGGER_UNDERFLOW		(1 << 13)
+# define PV_CONTROL_WAIT_HSTART			(1 << 12)
+# define PV_CONTROL_CLK_SELECT_DSI		0
+# define PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI	1
+# define PV_CONTROL_CLK_SELECT_MASK		VC4_MASK(3, 2)
+# define PV_CONTROL_CLK_SELECT_SHIFT		2
+# define PV_CONTROL_FIFO_CLR			(1 << 1)
+# define PV_CONTROL_EN				(1 << 0)
+
+#define PV_V_CONTROL				0x04
+# define PV_VCONTROL_CONTINUOUS			(1 << 1)
+# define PV_VCONTROL_VIDEN			(1 << 0)
+
+#define PV_VSYNCD				0x08
+
+#define PV_HORZA				0x0c
+# define PV_HORZA_HBP_MASK			VC4_MASK(31, 16)
+# define PV_HORZA_HBP_SHIFT			16
+# define PV_HORZA_HSYNC_MASK			VC4_MASK(15, 0)
+# define PV_HORZA_HSYNC_SHIFT			0
+
+#define PV_HORZB				0x10
+# define PV_HORZB_HFP_MASK			VC4_MASK(31, 16)
+# define PV_HORZB_HFP_SHIFT			16
+# define PV_HORZB_HACTIVE_MASK			VC4_MASK(15, 0)
+# define PV_HORZB_HACTIVE_SHIFT			0
+
+#define PV_VERTA				0x14
+# define PV_VERTA_VBP_MASK			VC4_MASK(31, 16)
+# define PV_VERTA_VBP_SHIFT			16
+# define PV_VERTA_VSYNC_MASK			VC4_MASK(15, 0)
+# define PV_VERTA_VSYNC_SHIFT			0
+
+#define PV_VERTB				0x18
+# define PV_VERTB_VFP_MASK			VC4_MASK(31, 16)
+# define PV_VERTB_VFP_SHIFT			16
+# define PV_VERTB_VACTIVE_MASK			VC4_MASK(15, 0)
+# define PV_VERTB_VACTIVE_SHIFT			0
+
+#define PV_VERTA_EVEN				0x1c
+#define PV_VERTB_EVEN				0x20
+
+#define PV_INTEN				0x24
+#define PV_INTSTAT				0x28
+# define PV_INT_VID_IDLE			(1 << 9)
+# define PV_INT_VFP_END				(1 << 8)
+# define PV_INT_VFP_START			(1 << 7)
+# define PV_INT_VACT_START			(1 << 6)
+# define PV_INT_VBP_START			(1 << 5)
+# define PV_INT_VSYNC_START			(1 << 4)
+# define PV_INT_HFP_START			(1 << 3)
+# define PV_INT_HACT_START			(1 << 2)
+# define PV_INT_HBP_START			(1 << 1)
+# define PV_INT_HSYNC_START			(1 << 0)
+
+#define PV_STAT					0x2c
+
+#define PV_HACT_ACT				0x30
+
+#define SCALER_DISPCTRL                         0x00000000
+/* Global register for clock gating the HVS */
+# define SCALER_DISPCTRL_ENABLE			(1 << 31)
+# define SCALER_DISPCTRL_DSP2EISLUR		(1 << 15)
+# define SCALER_DISPCTRL_DSP1EISLUR		(1 << 14)
+/* Enables Display 0 short line and underrun contribution to
+ * SCALER_DISPSTAT_IRQDISP0.  Note that short frame contributions are
+ * always enabled.
+ */
+# define SCALER_DISPCTRL_DSP0EISLUR		(1 << 13)
+# define SCALER_DISPCTRL_DSP2EIEOLN		(1 << 12)
+# define SCALER_DISPCTRL_DSP2EIEOF		(1 << 11)
+# define SCALER_DISPCTRL_DSP1EIEOLN		(1 << 10)
+# define SCALER_DISPCTRL_DSP1EIEOF		(1 << 9)
+/* Enables Display 0 end-of-line-N contribution to
+ * SCALER_DISPSTAT_IRQDISP0
+ */
+# define SCALER_DISPCTRL_DSP0EIEOLN		(1 << 8)
+/* Enables Display 0 EOF contribution to SCALER_DISPSTAT_IRQDISP0 */
+# define SCALER_DISPCTRL_DSP0EIEOF		(1 << 7)
+
+# define SCALER_DISPCTRL_SLVRDEIRQ		(1 << 6)
+# define SCALER_DISPCTRL_SLVWREIRQ		(1 << 5)
+# define SCALER_DISPCTRL_DMAEIRQ		(1 << 4)
+# define SCALER_DISPCTRL_DISP2EIRQ		(1 << 3)
+# define SCALER_DISPCTRL_DISP1EIRQ		(1 << 2)
+/* Enables interrupt generation on the enabled EOF/EOLN/EISLUR
+ * bits and short frames..
+ */
+# define SCALER_DISPCTRL_DISP0EIRQ		(1 << 1)
+/* Enables interrupt generation on scaler profiler interrupt. */
+# define SCALER_DISPCTRL_SCLEIRQ		(1 << 0)
+
+#define SCALER_DISPSTAT                         0x00000004
+# define SCALER_DISPSTAT_COBLOW2		(1 << 29)
+# define SCALER_DISPSTAT_EOLN2			(1 << 28)
+# define SCALER_DISPSTAT_ESFRAME2		(1 << 27)
+# define SCALER_DISPSTAT_ESLINE2		(1 << 26)
+# define SCALER_DISPSTAT_EUFLOW2		(1 << 25)
+# define SCALER_DISPSTAT_EOF2			(1 << 24)
+
+# define SCALER_DISPSTAT_COBLOW1		(1 << 21)
+# define SCALER_DISPSTAT_EOLN1			(1 << 20)
+# define SCALER_DISPSTAT_ESFRAME1		(1 << 19)
+# define SCALER_DISPSTAT_ESLINE1		(1 << 18)
+# define SCALER_DISPSTAT_EUFLOW1		(1 << 17)
+# define SCALER_DISPSTAT_EOF1			(1 << 16)
+
+# define SCALER_DISPSTAT_RESP_MASK		VC4_MASK(15, 14)
+# define SCALER_DISPSTAT_RESP_SHIFT		14
+# define SCALER_DISPSTAT_RESP_OKAY		0
+# define SCALER_DISPSTAT_RESP_EXOKAY		1
+# define SCALER_DISPSTAT_RESP_SLVERR		2
+# define SCALER_DISPSTAT_RESP_DECERR		3
+
+# define SCALER_DISPSTAT_COBLOW0		(1 << 13)
+/* Set when the DISPEOLN line is done compositing. */
+# define SCALER_DISPSTAT_EOLN0			(1 << 12)
+/* Set when VSTART is seen but there are still pixels in the current
+ * output line.
+ */
+# define SCALER_DISPSTAT_ESFRAME0		(1 << 11)
+/* Set when HSTART is seen but there are still pixels in the current
+ * output line.
+ */
+# define SCALER_DISPSTAT_ESLINE0		(1 << 10)
+/* Set when the the downstream tries to read from the display FIFO
+ * while it's empty.
+ */
+# define SCALER_DISPSTAT_EUFLOW0		(1 << 9)
+/* Set when the display mode changes from RUN to EOF */
+# define SCALER_DISPSTAT_EOF0			(1 << 8)
+
+/* Set on AXI invalid DMA ID error. */
+# define SCALER_DISPSTAT_DMA_ERROR		(1 << 7)
+/* Set on AXI slave read decode error */
+# define SCALER_DISPSTAT_IRQSLVRD		(1 << 6)
+/* Set on AXI slave write decode error */
+# define SCALER_DISPSTAT_IRQSLVWR		(1 << 5)
+/* Set when SCALER_DISPSTAT_DMA_ERROR is set, or
+ * SCALER_DISPSTAT_RESP_ERROR is not SCALER_DISPSTAT_RESP_OKAY.
+ */
+# define SCALER_DISPSTAT_IRQDMA			(1 << 4)
+# define SCALER_DISPSTAT_IRQDISP2		(1 << 3)
+# define SCALER_DISPSTAT_IRQDISP1		(1 << 2)
+/* Set when any of the EOF/EOLN/ESFRAME/ESLINE bits are set and their
+ * corresponding interrupt bit is enabled in DISPCTRL.
+ */
+# define SCALER_DISPSTAT_IRQDISP0		(1 << 1)
+/* On read, the profiler interrupt.  On write, clear *all* interrupt bits. */
+# define SCALER_DISPSTAT_IRQSCL			(1 << 0)
+
+#define SCALER_DISPID                           0x00000008
+#define SCALER_DISPECTRL                        0x0000000c
+#define SCALER_DISPPROF                         0x00000010
+#define SCALER_DISPDITHER                       0x00000014
+#define SCALER_DISPEOLN                         0x00000018
+#define SCALER_DISPLIST0                        0x00000020
+#define SCALER_DISPLIST1                        0x00000024
+#define SCALER_DISPLIST2                        0x00000028
+#define SCALER_DISPLSTAT                        0x0000002c
+#define SCALER_DISPLISTX(x)			(SCALER_DISPLIST0 +	\
+						 (x) * (SCALER_DISPLIST1 - \
+							SCALER_DISPLIST0))
+
+#define SCALER_DISPLACT0                        0x00000030
+#define SCALER_DISPLACT1                        0x00000034
+#define SCALER_DISPLACT2                        0x00000038
+#define SCALER_DISPCTRL0                        0x00000040
+# define SCALER_DISPCTRLX_ENABLE		(1 << 31)
+# define SCALER_DISPCTRLX_RESET			(1 << 30)
+# define SCALER_DISPCTRLX_WIDTH_MASK		VC4_MASK(23, 12)
+# define SCALER_DISPCTRLX_WIDTH_SHIFT		12
+# define SCALER_DISPCTRLX_HEIGHT_MASK		VC4_MASK(11, 0)
+# define SCALER_DISPCTRLX_HEIGHT_SHIFT		0
+
+#define SCALER_DISPBKGND0                       0x00000044
+#define SCALER_DISPSTAT0                        0x00000048
+#define SCALER_DISPBASE0                        0x0000004c
+# define SCALER_DISPSTATX_MODE_MASK		VC4_MASK(31, 30)
+# define SCALER_DISPSTATX_MODE_SHIFT		30
+# define SCALER_DISPSTATX_MODE_DISABLED		0
+# define SCALER_DISPSTATX_MODE_INIT		1
+# define SCALER_DISPSTATX_MODE_RUN		2
+# define SCALER_DISPSTATX_MODE_EOF		3
+# define SCALER_DISPSTATX_FULL			(1 << 29)
+# define SCALER_DISPSTATX_EMPTY			(1 << 28)
+#define SCALER_DISPCTRL1                        0x00000050
+#define SCALER_DISPBKGND1                       0x00000054
+#define SCALER_DISPSTAT1                        0x00000058
+#define SCALER_DISPSTATX(x)			(SCALER_DISPSTAT0 +        \
+						 (x) * (SCALER_DISPSTAT1 - \
+							SCALER_DISPSTAT0))
+#define SCALER_DISPBASE1                        0x0000005c
+#define SCALER_DISPCTRL2                        0x00000060
+#define SCALER_DISPCTRLX(x)			(SCALER_DISPCTRL0 +        \
+						 (x) * (SCALER_DISPCTRL1 - \
+							SCALER_DISPCTRL0))
+#define SCALER_DISPBKGND2                       0x00000064
+#define SCALER_DISPSTAT2                        0x00000068
+#define SCALER_DISPBASE2                        0x0000006c
+#define SCALER_DISPALPHA2                       0x00000070
+#define SCALER_GAMADDR                          0x00000078
+#define SCALER_GAMDATA                          0x000000e0
+#define SCALER_DLIST_START                      0x00002000
+#define SCALER_DLIST_SIZE                       0x00004000
+
+#define VC4_HDMI_CORE_REV			0x000
+
+#define VC4_HDMI_SW_RESET_CONTROL		0x004
+# define VC4_HDMI_SW_RESET_FORMAT_DETECT	(1 << 1)
+# define VC4_HDMI_SW_RESET_HDMI			(1 << 0)
+
+#define VC4_HDMI_HOTPLUG_INT			0x008
+
+#define VC4_HDMI_HOTPLUG			0x00c
+# define VC4_HDMI_HOTPLUG_CONNECTED		(1 << 0)
+
+#define VC4_HDMI_RAM_PACKET_CONFIG		0x0a0
+# define VC4_HDMI_RAM_PACKET_ENABLE		(1 << 16)
+
+#define VC4_HDMI_HORZA				0x0c4
+# define VC4_HDMI_HORZA_VPOS			(1 << 14)
+# define VC4_HDMI_HORZA_HPOS			(1 << 13)
+/* Horizontal active pixels (hdisplay). */
+# define VC4_HDMI_HORZA_HAP_MASK		VC4_MASK(12, 0)
+# define VC4_HDMI_HORZA_HAP_SHIFT		0
+
+#define VC4_HDMI_HORZB				0x0c8
+/* Horizontal pack porch (htotal - hsync_end). */
+# define VC4_HDMI_HORZB_HBP_MASK		VC4_MASK(29, 20)
+# define VC4_HDMI_HORZB_HBP_SHIFT		20
+/* Horizontal sync pulse (hsync_end - hsync_start). */
+# define VC4_HDMI_HORZB_HSP_MASK		VC4_MASK(19, 10)
+# define VC4_HDMI_HORZB_HSP_SHIFT		10
+/* Horizontal front porch (hsync_start - hdisplay). */
+# define VC4_HDMI_HORZB_HFP_MASK		VC4_MASK(9, 0)
+# define VC4_HDMI_HORZB_HFP_SHIFT		0
+
+#define VC4_HDMI_FIFO_CTL			0x05c
+# define VC4_HDMI_FIFO_CTL_RECENTER_DONE	(1 << 14)
+# define VC4_HDMI_FIFO_CTL_USE_EMPTY		(1 << 13)
+# define VC4_HDMI_FIFO_CTL_ON_VB		(1 << 7)
+# define VC4_HDMI_FIFO_CTL_RECENTER		(1 << 6)
+# define VC4_HDMI_FIFO_CTL_FIFO_RESET		(1 << 5)
+# define VC4_HDMI_FIFO_CTL_USE_PLL_LOCK		(1 << 4)
+# define VC4_HDMI_FIFO_CTL_INV_CLK_XFR		(1 << 3)
+# define VC4_HDMI_FIFO_CTL_CAPTURE_PTR		(1 << 2)
+# define VC4_HDMI_FIFO_CTL_USE_FULL		(1 << 1)
+# define VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N	(1 << 0)
+# define VC4_HDMI_FIFO_VALID_WRITE_MASK		0xefff
+
+#define VC4_HDMI_SCHEDULER_CONTROL		0x0c0
+# define VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT (1 << 15)
+# define VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS (1 << 5)
+# define VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT	(1 << 3)
+# define VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE	(1 << 1)
+# define VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI	(1 << 0)
+
+#define VC4_HDMI_VERTA0				0x0cc
+#define VC4_HDMI_VERTA1				0x0d4
+/* Vertical sync pulse (vsync_end - vsync_start). */
+# define VC4_HDMI_VERTA_VSP_MASK		VC4_MASK(24, 20)
+# define VC4_HDMI_VERTA_VSP_SHIFT		20
+/* Vertical front porch (vsync_start - vdisplay). */
+# define VC4_HDMI_VERTA_VFP_MASK		VC4_MASK(19, 13)
+# define VC4_HDMI_VERTA_VFP_SHIFT		13
+/* Vertical active lines (vdisplay). */
+# define VC4_HDMI_VERTA_VAL_MASK		VC4_MASK(12, 0)
+# define VC4_HDMI_VERTA_VAL_SHIFT		0
+
+#define VC4_HDMI_VERTB0				0x0d0
+#define VC4_HDMI_VERTB1				0x0d8
+/* Vertical sync pulse offset (for interlaced) */
+# define VC4_HDMI_VERTB_VSPO_MASK		VC4_MASK(21, 9)
+# define VC4_HDMI_VERTB_VSPO_SHIFT		9
+/* Vertical pack porch (vtotal - vsync_end). */
+# define VC4_HDMI_VERTB_VBP_MASK		VC4_MASK(8, 0)
+# define VC4_HDMI_VERTB_VBP_SHIFT		0
+
+#define VC4_HDMI_TX_PHY_RESET_CTL		0x2c0
+
+#define VC4_HD_M_CTL				0x00c
+# define VC4_HD_M_SW_RST			(1 << 2)
+# define VC4_HD_M_ENABLE			(1 << 0)
+
+#define VC4_HD_MAI_CTL				0x014
+
+#define VC4_HD_VID_CTL				0x038
+# define VC4_HD_VID_CTL_ENABLE			(1 << 31)
+# define VC4_HD_VID_CTL_UNDERFLOW_ENABLE	(1 << 30)
+# define VC4_HD_VID_CTL_FRAME_COUNTER_RESET	(1 << 29)
+# define VC4_HD_VID_CTL_VSYNC_LOW		(1 << 28)
+# define VC4_HD_VID_CTL_HSYNC_LOW		(1 << 27)
+
+#define VC4_HD_CSC_CTL				0x040
+# define VC4_HD_CSC_CTL_ORDER_MASK		VC4_MASK(7, 5)
+# define VC4_HD_CSC_CTL_ORDER_SHIFT		5
+# define VC4_HD_CSC_CTL_ORDER_RGB		0
+# define VC4_HD_CSC_CTL_ORDER_BGR		1
+# define VC4_HD_CSC_CTL_ORDER_BRG		2
+# define VC4_HD_CSC_CTL_ORDER_GRB		3
+# define VC4_HD_CSC_CTL_ORDER_GBR		4
+# define VC4_HD_CSC_CTL_ORDER_RBG		5
+# define VC4_HD_CSC_CTL_PADMSB			(1 << 4)
+# define VC4_HD_CSC_CTL_MODE_MASK		VC4_MASK(3, 2)
+# define VC4_HD_CSC_CTL_MODE_SHIFT		2
+# define VC4_HD_CSC_CTL_MODE_RGB_TO_SD_YPRPB	0
+# define VC4_HD_CSC_CTL_MODE_RGB_TO_HD_YPRPB	1
+# define VC4_HD_CSC_CTL_MODE_CUSTOM		2
+# define VC4_HD_CSC_CTL_RGB2YCC			(1 << 1)
+# define VC4_HD_CSC_CTL_ENABLE			(1 << 0)
+
+#define VC4_HD_FRAME_COUNT			0x068
+
+/* HVS display list information. */
+#define HVS_BOOTLOADER_DLIST_END                32
+
+enum hvs_pixel_format {
+	/* 8bpp */
+	HVS_PIXEL_FORMAT_RGB332 = 0,
+	/* 16bpp */
+	HVS_PIXEL_FORMAT_RGBA4444 = 1,
+	HVS_PIXEL_FORMAT_RGB555 = 2,
+	HVS_PIXEL_FORMAT_RGBA5551 = 3,
+	HVS_PIXEL_FORMAT_RGB565 = 4,
+	/* 24bpp */
+	HVS_PIXEL_FORMAT_RGB888 = 5,
+	HVS_PIXEL_FORMAT_RGBA6666 = 6,
+	/* 32bpp */
+	HVS_PIXEL_FORMAT_RGBA8888 = 7
+};
+
+/* Note: the LSB is the rightmost character shown.  Only valid for
+ * HVS_PIXEL_FORMAT_RGB8888, not RGB888.
+ */
+#define HVS_PIXEL_ORDER_RGBA			0
+#define HVS_PIXEL_ORDER_BGRA			1
+#define HVS_PIXEL_ORDER_ARGB			2
+#define HVS_PIXEL_ORDER_ABGR			3
+
+#define HVS_PIXEL_ORDER_XBRG			0
+#define HVS_PIXEL_ORDER_XRBG			1
+#define HVS_PIXEL_ORDER_XRGB			2
+#define HVS_PIXEL_ORDER_XBGR			3
+
+#define HVS_PIXEL_ORDER_XYCBCR			0
+#define HVS_PIXEL_ORDER_XYCRCB			1
+#define HVS_PIXEL_ORDER_YXCBCR			2
+#define HVS_PIXEL_ORDER_YXCRCB			3
+
+#define SCALER_CTL0_END				(1 << 31)
+#define SCALER_CTL0_VALID			(1 << 30)
+
+#define SCALER_CTL0_SIZE_MASK			VC4_MASK(29, 24)
+#define SCALER_CTL0_SIZE_SHIFT			24
+
+#define SCALER_CTL0_HFLIP                       (1 << 16)
+#define SCALER_CTL0_VFLIP                       (1 << 15)
+
+#define SCALER_CTL0_ORDER_MASK			VC4_MASK(14, 13)
+#define SCALER_CTL0_ORDER_SHIFT			13
+
+/* Set to indicate no scaling. */
+#define SCALER_CTL0_UNITY			(1 << 4)
+
+#define SCALER_CTL0_PIXEL_FORMAT_MASK		VC4_MASK(3, 0)
+#define SCALER_CTL0_PIXEL_FORMAT_SHIFT		0
+
+#define SCALER_POS0_FIXED_ALPHA_MASK		VC4_MASK(31, 24)
+#define SCALER_POS0_FIXED_ALPHA_SHIFT		24
+
+#define SCALER_POS0_START_Y_MASK		VC4_MASK(23, 12)
+#define SCALER_POS0_START_Y_SHIFT		12
+
+#define SCALER_POS0_START_X_MASK		VC4_MASK(11, 0)
+#define SCALER_POS0_START_X_SHIFT		0
+
+#define SCALER_POS2_ALPHA_MODE_MASK		VC4_MASK(31, 30)
+#define SCALER_POS2_ALPHA_MODE_SHIFT		30
+#define SCALER_POS2_ALPHA_MODE_PIPELINE		0
+#define SCALER_POS2_ALPHA_MODE_FIXED		1
+#define SCALER_POS2_ALPHA_MODE_FIXED_NONZERO	2
+#define SCALER_POS2_ALPHA_MODE_FIXED_OVER_0x07	3
+
+#define SCALER_POS2_HEIGHT_MASK			VC4_MASK(27, 16)
+#define SCALER_POS2_HEIGHT_SHIFT		16
+
+#define SCALER_POS2_WIDTH_MASK			VC4_MASK(11, 0)
+#define SCALER_POS2_WIDTH_SHIFT			0
+
+#define SCALER_SRC_PITCH_MASK			VC4_MASK(15, 0)
+#define SCALER_SRC_PITCH_SHIFT			0
-- 
2.1.4



From info at lategoodbye.de  Wed Aug 19 13:38:35 2015
From: info at lategoodbye.de (Stefan Wahren)
Date: Wed, 19 Aug 2015 22:38:35 +0200
Subject: [PATCH v2 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <1439934848-7196-2-git-send-email-eric@anholt.net>
References: <1439934848-7196-1-git-send-email-eric@anholt.net>
 <1439934848-7196-2-git-send-email-eric@anholt.net>
Message-ID: <55D4E94B.8080103@lategoodbye.de>

Hi Eric,

Am 18.08.2015 um 23:54 schrieb Eric Anholt:
> VC4 is the GPU (display and 3D) subsystem present on the 2835 and some
> other Broadcom SoCs.
>
> This binding follows the model of msm, imx, sti, and others, where
> there is a subsystem node for the whole GPU, with nodes for the
> individual HW components within it.
>
> Signed-off-by: Eric Anholt <eric at anholt.net>
> ---
>
> v2: Extend the commit message, fix several nits from Stephen Warren.
>
>   .../devicetree/bindings/gpu/brcm,bcm-vc4.txt       | 79 ++++++++++++++++++++++
>   1 file changed, 79 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
>
> diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
> new file mode 100644
> index 0000000..1b9fedc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
> @@ -0,0 +1,79 @@
> +Broadcom VC4 GPU
> +
> +The VC4 device present on the Raspberry Pi includes a display system
> +with HDMI output and the HVS scaler for compositing display planes.
> +
> +Required properties for VC4:
> +- compatible:	Should be "brcm,vc4"
> +- crtcs:	List of phandles of pixelvalve scanout engines
> +- hvss:		List of phandles of HVS video scalers
> +- encoders:	List of phandles of output encoders (HDMI, SDTV)
> +
> +Required properties for Pixel Valve:
> +- compatible:	Should be "brcm,vc4-pixelvalve"
> +- reg:		Physical base address and length of the PV's registers
> +- interrupts:	The interrupt number
> +		  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
> +
> +Required properties for HVS:
> +- compatible:	Should be "brcm,vc4-hvs"
> +- reg:		Physical base address and length of the HVS's registers
> +- interrupts:	The interrupt number
> +		  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
> +
> +Required properties for HDMI
> +- compatible:	Should be "brcm,vc4-hdmi"
> +- reg:		Physical base address and length of the two register ranges
> +		  ("HDMI" and "HD", in that order)
> +- interrupts:	The interrupt numbers
> +		  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
> +- ddc:		phandle of the I2C controller used for DDC EDID probing
> +- crtc:		phandle to the pixelvalve CRTC the HDMI encoder is attached to
> +
> +Optional properties for HDMI:
> +- hpd-gpio:	The GPIO pin for HDMI hotplug detect (if it doesn't appear
> +		  as an interrupt/status bit in the HDMI controller
> +		  itself).  See bindings/pinctrl/brcm,bcm2835-gpio.txt
> +
> +Example:
> +pv0: brcm,vc4-pixelvalve at 7e206000 {

AFAIK the DT node name should describe the function and usually don't 
have a vendor prefix. Here some possible suggestions without deeper 
knowledge of the hardware function:

pv0: pixelvalve

> +	compatible = "brcm,vc4-pixelvalve";
> +	reg = <0x7e206000 0x100>;
> +	interrupts = <2 13>; /* pwa2 */
> +};
> +
> +pv1: brcm,vc4-pixelvalve at 7e207000 {

pv1: pixelvalve

> +	compatible = "brcm,vc4-pixelvalve";
> +	reg = <0x7e207000 0x100>;
> +	interrupts = <2 14>; /* pwa1 */
> +};
> +
> +pv2: brcm,vc4-pixelvalve at 7e807000 {

pv2: pixelvalve

> +	compatible = "brcm,vc4-pixelvalve";
> +	reg = <0x7e807000 0x100>;
> +	interrupts = <2 10>; /* pixelvalve */
> +};
> +
> +hvs: brcm,hvs at 7e400000 {

hvs: hvs

> +	compatible = "brcm,vc4-hvs";
> +	reg = <0x7e400000 0x6000>;
> +	interrupts = <2 1>;
> +};
> +
> +hdmi: brcm,vc4-hdmi at 7e902000 {

hdmi: hdmi

> +	compatible = "brcm,vc4-hdmi";
> +	reg = <0x7e902000 0x600>,
> +	      <0x7e808000 0x100>;
> +	interrupts = <2 8>, <2 9>;
> +	ddc = <&i2c2>;
> +	hpd-gpio = <&gpio 46 GPIO_ACTIVE_HIGH>;
> +	crtc = <&pv2>;
> +};
> +
> +vc4: vc4 at 0x7e4c0000 {

vc4: gpu

Regards
Stefan

> +	compatible = "brcm,vc4";
> +
> +	crtcs = <&pv0>, <&pv1>, <&pv2>;
> +	encoders = <&hdmi>;
> +	hvss = <&hvs>;
> +};
>



From info at lategoodbye.de  Wed Aug 19 14:37:14 2015
From: info at lategoodbye.de (Stefan Wahren)
Date: Wed, 19 Aug 2015 23:37:14 +0200
Subject: [PATCH v2 3/7] drm/vc4: Add KMS support for Raspberry Pi.
In-Reply-To: <1439934848-7196-4-git-send-email-eric@anholt.net>
References: <1439934848-7196-1-git-send-email-eric@anholt.net>
 <1439934848-7196-4-git-send-email-eric@anholt.net>
Message-ID: <55D4F70A.30508@lategoodbye.de>

Hi Eric,

only a few nits.

Am 18.08.2015 um 23:54 schrieb Eric Anholt:
> This is the start of a full VC4 driver.  Right now this just supports
> configuring the display using a pre-existing video mode (because
> changing the pixel clock isn't available yet, and doesn't work when it
> is).  However, this is enough for fbcon and bringing up X using
> xf86-video-modesetting.
>
> Signed-off-by: Eric Anholt <eric at anholt.net>
> ---
>
> v2: Drop FB_HELPER select thanks to Archit's patches.  Do manual init
>      ordering instead of using the .load hook.  Structure registration
>      more like tegra's, but still using the typical "component" code.
>      Drop no-op hooks for atomic_begin and mode_fixup() now that
>      they're optional.  Drop sentinel in Makefile.  Fix minor style
>      nits I noticed on another reread.
>
>   drivers/gpu/drm/Kconfig           |   2 +
>   drivers/gpu/drm/Makefile          |   1 +
>   drivers/gpu/drm/vc4/Kconfig       |  13 +
>   drivers/gpu/drm/vc4/Makefile      |  17 +
>   drivers/gpu/drm/vc4/vc4_bo.c      |  52 ++++
>   drivers/gpu/drm/vc4/vc4_crtc.c    | 565 ++++++++++++++++++++++++++++++++++
>   drivers/gpu/drm/vc4/vc4_debugfs.c |  38 +++
>   drivers/gpu/drm/vc4/vc4_drv.c     | 271 ++++++++++++++++
>   drivers/gpu/drm/vc4/vc4_drv.h     | 120 ++++++++
>   drivers/gpu/drm/vc4/vc4_hdmi.c    | 633 ++++++++++++++++++++++++++++++++++++++
>   drivers/gpu/drm/vc4/vc4_hvs.c     | 161 ++++++++++
>   drivers/gpu/drm/vc4/vc4_kms.c     |  84 +++++
>   drivers/gpu/drm/vc4/vc4_plane.c   | 320 +++++++++++++++++++
>   drivers/gpu/drm/vc4/vc4_regs.h    | 562 +++++++++++++++++++++++++++++++++
>   14 files changed, 2839 insertions(+)
>   create mode 100644 drivers/gpu/drm/vc4/Kconfig
>   create mode 100644 drivers/gpu/drm/vc4/Makefile
>   create mode 100644 drivers/gpu/drm/vc4/vc4_bo.c
>   create mode 100644 drivers/gpu/drm/vc4/vc4_crtc.c
>   create mode 100644 drivers/gpu/drm/vc4/vc4_debugfs.c
>   create mode 100644 drivers/gpu/drm/vc4/vc4_drv.c
>   create mode 100644 drivers/gpu/drm/vc4/vc4_drv.h
>   create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi.c
>   create mode 100644 drivers/gpu/drm/vc4/vc4_hvs.c
>   create mode 100644 drivers/gpu/drm/vc4/vc4_kms.c
>   create mode 100644 drivers/gpu/drm/vc4/vc4_plane.c
>   create mode 100644 drivers/gpu/drm/vc4/vc4_regs.h
>
> [...]
> +static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
> +{
> +	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> +	struct drm_crtc_state *state = crtc->state;
> +	struct drm_display_mode *mode = &state->adjusted_mode;
> +	u32 vactive = (mode->vdisplay >>
> +		       ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0));
> +	u32 format = PV_CONTROL_FORMAT_24;
> +	bool debug_dump_regs = false;
> +
> +	if (debug_dump_regs) {
> +		DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc));
> +		vc4_crtc_dump_regs(vc4_crtc);
> +	}
> +
> +	/* This is where we would set the pixel clock. */
> +
> +	/* Reset the PV fifo. */
> +	CRTC_WRITE(PV_CONTROL, 0);
> +	CRTC_WRITE(PV_CONTROL, PV_CONTROL_FIFO_CLR | PV_CONTROL_EN);
> +	CRTC_WRITE(PV_CONTROL, 0);
> +
> +	CRTC_WRITE(PV_HORZA,
> +		   VC4_SET_FIELD(mode->htotal - mode->hsync_end,
> +				 PV_HORZA_HBP) |
> +		   VC4_SET_FIELD(mode->hsync_end - mode->hsync_start,
> +				 PV_HORZA_HSYNC));
> +	CRTC_WRITE(PV_HORZB,
> +		   VC4_SET_FIELD(mode->hsync_start - mode->hdisplay,
> +				 PV_HORZB_HFP) |
> +		   VC4_SET_FIELD(mode->hdisplay, PV_HORZB_HACTIVE));
> +
> +	CRTC_WRITE(PV_VERTA,
> +		   VC4_SET_FIELD(mode->vtotal - mode->vsync_end,
> +				 PV_VERTA_VBP) |
> +		   VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
> +				 PV_VERTA_VSYNC));
> +	CRTC_WRITE(PV_VERTB,
> +		   VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
> +				 PV_VERTB_VFP) |
> +		   VC4_SET_FIELD(vactive, PV_VERTB_VACTIVE));
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		/* Write PV_VERTA_EVEN/VERTB_EVEN */
> +	}

checkpatch complains here. Is this intended to have only a comment? Is 
it a TODO?

> [...]
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_drv.h
> @@ -0,0 +1,120 @@
> +/*
> + * Copyright (C) 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include "drmP.h"
> +#include "drm_gem_cma_helper.h"
> +
> +struct vc4_dev {
> +	struct drm_device *dev;
> +
> +	struct vc4_hdmi *hdmi;
> +	struct vc4_hvs *hvs;
> +	struct vc4_crtc *crtc[3];
> +};
> +
> +static inline struct vc4_dev *
> +to_vc4_dev(struct drm_device *dev)
> +{
> +	return (struct vc4_dev *)dev->dev_private;
> +}
> +
> +struct vc4_bo {
> +	struct drm_gem_cma_object base;
> +};
> +
> +static inline struct vc4_bo *
> +to_vc4_bo(struct drm_gem_object *bo)
> +{
> +	return (struct vc4_bo *)bo;
> +}
> +
> +struct vc4_hvs {
> +	struct platform_device *pdev;
> +	void __iomem *regs;
> +	void __iomem *dlist;
> +};
> +
> +struct vc4_crtc {
> +	struct drm_crtc base;
> +	void __iomem *regs;
> +
> +	/* Which HVS channel we're using for our CRTC. */
> +	int channel;
> +
> +	/* Pointer to the actual hardware display list memory for the
> +	 * crtc.
> +	 */
> +	u32 __iomem *dlist;
> +
> +	u32 dlist_size; /* in dwords */
> +
> +	struct drm_pending_vblank_event *event;
> +};
> +
> +static inline struct vc4_crtc *
> +to_vc4_crtc(struct drm_crtc *crtc)
> +{
> +	return (struct vc4_crtc *)crtc;
> +}
> +
> +struct vc4_plane {
> +	struct drm_plane base;
> +};
> +
> +static inline struct vc4_plane *
> +to_vc4_plane(struct drm_plane *plane)
> +{
> +	return (struct vc4_plane *)plane;
> +}
> +
> +#define HVS_READ(offset) readl(vc4->hvs->regs + offset)
> +#define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset)
> +
> +/* vc4_bo.c */

I'm not sure about these filename references.

> +void vc4_free_object(struct drm_gem_object *gem_obj);
> +struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size);
> +int vc4_dumb_create(struct drm_file *file_priv,
> +		    struct drm_device *dev,
> +		    struct drm_mode_create_dumb *args);
> +struct dma_buf *vc4_prime_export(struct drm_device *dev,
> +				 struct drm_gem_object *obj, int flags);
> +
> +/* vc4_crtc.c */
> +extern struct platform_driver vc4_crtc_driver;
> +int vc4_enable_vblank(struct drm_device *dev, int crtc_id);
> +void vc4_disable_vblank(struct drm_device *dev, int crtc_id);
> +void vc4_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
> +int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg);
> +
> +/* vc4_debugfs.c */
> +int vc4_debugfs_init(struct drm_minor *minor);
> +void vc4_debugfs_cleanup(struct drm_minor *minor);
> +
> +/* vc4_drv.c */
> +void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index);
> +
> +/* vc4_hdmi.c */
> +extern struct platform_driver vc4_hdmi_driver;
> +struct drm_encoder *vc4_hdmi_encoder_init(struct drm_device *dev);
> +struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
> +					      struct drm_encoder *encoder);
> +int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused);
> +
> +/* vc4_hvs.c */
> +extern struct platform_driver vc4_hvs_driver;
> +void vc4_hvs_dump_state(struct drm_device *dev);
> +int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused);
> +
> +/* vc4_kms.c */
> +int vc4_kms_load(struct drm_device *dev);
> +
> +/* vc4_plane.c */
> +struct drm_plane *vc4_plane_init(struct drm_device *dev,
> +				 enum drm_plane_type type);
> +u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist);
> +u32 vc4_plane_dlist_size(struct drm_plane_state *state);
>
> [...]
> diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h
> new file mode 100644
> index 0000000..0eff631
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_regs.h
> @@ -0,0 +1,562 @@
> +/*
> + *  Copyright ? 2014-2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#define VC4_MASK(high, low) (((1 << ((high) - (low) + 1)) - 1) << (low))
> +/* Using the GNU statement expression extension */
> +#define VC4_SET_FIELD(value, field)					\
> +	({								\
> +		uint32_t fieldval = (value) << field##_SHIFT;		\
> +		WARN_ON((fieldval & ~field##_MASK) != 0);		\
> +		fieldval & field##_MASK;				\
> +	 })
> +
> +#define VC4_GET_FIELD(word, field) (((word) & field##_MASK) >>		\
> +				    field##_SHIFT)
> +
> +#define V3D_IDENT0   0x00000
> +# define V3D_EXPECTED_IDENT0 \
> +	((2 << 24) | \
> +	('V' << 0) | \
> +	('3' << 8) | \
> +	 ('D' << 16))
> +
> +#define V3D_IDENT1   0x00004
> +/* Multiples of 1kb */
> +# define V3D_IDENT1_VPM_SIZE_MASK                      VC4_MASK(31, 28)
> +# define V3D_IDENT1_VPM_SIZE_SHIFT                     28
> +# define V3D_IDENT1_NSEM_MASK                          VC4_MASK(23, 16)
> +# define V3D_IDENT1_NSEM_SHIFT                         16
> +# define V3D_IDENT1_TUPS_MASK                          VC4_MASK(15, 12)
> +# define V3D_IDENT1_TUPS_SHIFT                         12
> +# define V3D_IDENT1_QUPS_MASK                          VC4_MASK(11, 8)
> +# define V3D_IDENT1_QUPS_SHIFT                         8
> +# define V3D_IDENT1_NSLC_MASK                          VC4_MASK(7, 4)
> +# define V3D_IDENT1_NSLC_SHIFT                         4
> +# define V3D_IDENT1_REV_MASK                           VC4_MASK(3, 0)
> +# define V3D_IDENT1_REV_SHIFT                          0
> +
> +#define V3D_IDENT2   0x00008
> +#define V3D_SCRATCH  0x00010
> +#define V3D_L2CACTL  0x00020
> +# define V3D_L2CACTL_L2CCLR                            (1 << 2)

Maybe you could use the BIT macro?

Thanks Stefan




From f.fainelli at gmail.com  Fri Aug 21 16:27:23 2015
From: f.fainelli at gmail.com (Florian Fainelli)
Date: Fri, 21 Aug 2015 16:27:23 -0700
Subject: [GIT PULL] RPi DT changes due for v4.3
In-Reply-To: <20150813103056.GY30160@localhost>
References: <20150811160642.GQ18282@x1> <20150813103056.GY30160@localhost>
Message-ID: <55D7B3DB.8070405@gmail.com>

On 13/08/15 03:30, Olof Johansson wrote:
> Hi,
> 
> On Tue, Aug 11, 2015 at 05:06:42PM +0100, Lee Jones wrote:
>> ARM SoC Chaps,
>>
>> Please find a couple of simple RPi changes pertaining to Firmware.
>>
>> The following changes since commit bc0195aad0daa2ad5b0d76cce22b167bc3435590:
>>
>>   Linux 4.2-rc2 (2015-07-12 15:10:30 -0700)
>>
>> are available in the git repository at:
>>
>>   git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git tags/rpi-dt-for-armsoc-4.3
>>
>> for you to fetch changes up to fd26f8830979de48eb3f1c253eb9d2ee2e468eb6:
>>
>>   dt/bindings: Add binding for the Raspberry Pi firmware driver (2015-08-11 16:56:41 +0100)
> 
> I've merged this but I'm a little confused about bcm2835 maintainership:
> I got a MAINTAINERS update from Florian earlier grouped in a set of
> other bcm changes (adding Eric), and now this directly from you.
> 
> In general the bcm platforms are quite diverse and we've asked Florian
> to be the funnel for all of the new ones, but I don't think we talked
> much about 2835 at that time. Should we expect to see merges directly
> from you and the other maintainers there or will it go through Florian?
> 
> Either way is OK with me, just looking to clarify.

Could you merge this pull request directly? Eric and I met recently and
we agreed on a more formal process for the next submissions which will
be having me route the RPi changes through the Broadcom pull requests.

Thanks!
-- 
Florian


From olof at lixom.net  Fri Aug 21 16:32:47 2015
From: olof at lixom.net (Olof Johansson)
Date: Fri, 21 Aug 2015 16:32:47 -0700
Subject: [GIT PULL] RPi DT changes due for v4.3
In-Reply-To: <55D7B3DB.8070405@gmail.com>
References: <20150811160642.GQ18282@x1> <20150813103056.GY30160@localhost>
 <55D7B3DB.8070405@gmail.com>
Message-ID: <CAOesGMjpR01rLHpj5ZSJq6eze4wU8KDS3wBmZdqKOAJmH0U5UQ@mail.gmail.com>

On Fri, Aug 21, 2015 at 4:27 PM, Florian Fainelli <f.fainelli at gmail.com> wrote:
> On 13/08/15 03:30, Olof Johansson wrote:
>> Hi,
>>
>> On Tue, Aug 11, 2015 at 05:06:42PM +0100, Lee Jones wrote:
>>> ARM SoC Chaps,
>>>
>>> Please find a couple of simple RPi changes pertaining to Firmware.
>>>
>>> The following changes since commit bc0195aad0daa2ad5b0d76cce22b167bc3435590:
>>>
>>>   Linux 4.2-rc2 (2015-07-12 15:10:30 -0700)
>>>
>>> are available in the git repository at:
>>>
>>>   git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git tags/rpi-dt-for-armsoc-4.3
>>>
>>> for you to fetch changes up to fd26f8830979de48eb3f1c253eb9d2ee2e468eb6:
>>>
>>>   dt/bindings: Add binding for the Raspberry Pi firmware driver (2015-08-11 16:56:41 +0100)
>>
>> I've merged this but I'm a little confused about bcm2835 maintainership:
>> I got a MAINTAINERS update from Florian earlier grouped in a set of
>> other bcm changes (adding Eric), and now this directly from you.
>>
>> In general the bcm platforms are quite diverse and we've asked Florian
>> to be the funnel for all of the new ones, but I don't think we talked
>> much about 2835 at that time. Should we expect to see merges directly
>> from you and the other maintainers there or will it go through Florian?
>>
>> Either way is OK with me, just looking to clarify.
>
> Could you merge this pull request directly? Eric and I met recently and
> we agreed on a more formal process for the next submissions which will
> be having me route the RPi changes through the Broadcom pull requests.

Yeah, as I said above I merged it already, I was just looking for clarification.

Merging through you sounds like a good way forward, and keeps the brcm
platforms consistent.


Thanks!

-Olof


From panand at redhat.com  Tue Aug 18 09:34:09 2015
From: panand at redhat.com (Pratyush Anand)
Date: Tue, 18 Aug 2015 22:04:09 +0530
Subject: [PATCH V2] Watchdog: Fix parent of watchdog_devices
Message-ID: <9fcc2a2cc8273f461baa13c6c9992f3ca56b508a.1439915117.git.panand@redhat.com>

/sys/class/watchdog/watchdogn/device/modalias can help to identify the
driver/module for a given watchdog node. However, many wdt devices does not set
its parent and so, we do not see an entry for device in sysfs for such devices.

This patch fixes parent of watchdog_device so that
/sys/class/watchdog/watchdogn/device is populated.

Exceptions: booke, diag288, mpc8xxx, octeon, softdog and w83627hf -- They do not
have any parent. Not sure, how we can we identify driver for these devices.

Signed-off-by: Pratyush Anand <panand at redhat.com>
---
Changes since v1:
Squash all commits of V1 into a single patch.
V1 was here:
http://www.spinics.net/lists/linux-watchdog/msg07220.html

 drivers/misc/mei/wd.c               | 1 +
 drivers/watchdog/bcm2835_wdt.c      | 1 +
 drivers/watchdog/bcm47xx_wdt.c      | 1 +
 drivers/watchdog/bcm_kona_wdt.c     | 1 +
 drivers/watchdog/coh901327_wdt.c    | 1 +
 drivers/watchdog/da9052_wdt.c       | 1 +
 drivers/watchdog/da9055_wdt.c       | 1 +
 drivers/watchdog/da9062_wdt.c       | 1 +
 drivers/watchdog/da9063_wdt.c       | 1 +
 drivers/watchdog/davinci_wdt.c      | 1 +
 drivers/watchdog/digicolor_wdt.c    | 1 +
 drivers/watchdog/ep93xx_wdt.c       | 1 +
 drivers/watchdog/gpio_wdt.c         | 1 +
 drivers/watchdog/ie6xx_wdt.c        | 1 +
 drivers/watchdog/intel-mid_wdt.c    | 1 +
 drivers/watchdog/jz4740_wdt.c       | 1 +
 drivers/watchdog/mena21_wdt.c       | 1 +
 drivers/watchdog/menf21bmc_wdt.c    | 1 +
 drivers/watchdog/omap_wdt.c         | 1 +
 drivers/watchdog/orion_wdt.c        | 1 +
 drivers/watchdog/pnx4008_wdt.c      | 1 +
 drivers/watchdog/qcom-wdt.c         | 1 +
 drivers/watchdog/retu_wdt.c         | 1 +
 drivers/watchdog/rt2880_wdt.c       | 1 +
 drivers/watchdog/s3c2410_wdt.c      | 1 +
 drivers/watchdog/shwdt.c            | 1 +
 drivers/watchdog/sirfsoc_wdt.c      | 1 +
 drivers/watchdog/sp805_wdt.c        | 1 +
 drivers/watchdog/st_lpc_wdt.c       | 1 +
 drivers/watchdog/stmp3xxx_rtc_wdt.c | 1 +
 drivers/watchdog/tegra_wdt.c        | 1 +
 drivers/watchdog/twl4030_wdt.c      | 1 +
 drivers/watchdog/txx9wdt.c          | 1 +
 drivers/watchdog/ux500_wdt.c        | 1 +
 drivers/watchdog/via_wdt.c          | 1 +
 drivers/watchdog/wm831x_wdt.c       | 1 +
 drivers/watchdog/wm8350_wdt.c       | 1 +
 37 files changed, 37 insertions(+)

diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c
index 2bc0f5089f82..b346638833b0 100644
--- a/drivers/misc/mei/wd.c
+++ b/drivers/misc/mei/wd.c
@@ -364,6 +364,7 @@ int mei_watchdog_register(struct mei_device *dev)
 
 	int ret;
 
+	amt_wd_dev.parent = dev->dev;
 	/* unlock to perserve correct locking order */
 	mutex_unlock(&dev->device_lock);
 	ret = watchdog_register_device(&amt_wd_dev);
diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c
index 7116968dee12..66c3e656a616 100644
--- a/drivers/watchdog/bcm2835_wdt.c
+++ b/drivers/watchdog/bcm2835_wdt.c
@@ -182,6 +182,7 @@ static int bcm2835_wdt_probe(struct platform_device *pdev)
 	watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt);
 	watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev);
 	watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout);
+	bcm2835_wdt_wdd.parent = &pdev->dev;
 	err = watchdog_register_device(&bcm2835_wdt_wdd);
 	if (err) {
 		dev_err(dev, "Failed to register watchdog device");
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c
index b28a072abf78..4064a43f1360 100644
--- a/drivers/watchdog/bcm47xx_wdt.c
+++ b/drivers/watchdog/bcm47xx_wdt.c
@@ -209,6 +209,7 @@ static int bcm47xx_wdt_probe(struct platform_device *pdev)
 
 	wdt->wdd.info = &bcm47xx_wdt_info;
 	wdt->wdd.timeout = WDT_DEFAULT_TIME;
+	wdt->wdd.parent = &pdev->dev;
 	ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout);
 	if (ret)
 		goto err_timer;
diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c
index 22d8ae65772a..e0c98423f2c9 100644
--- a/drivers/watchdog/bcm_kona_wdt.c
+++ b/drivers/watchdog/bcm_kona_wdt.c
@@ -319,6 +319,7 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev)
 	spin_lock_init(&wdt->lock);
 	platform_set_drvdata(pdev, wdt);
 	watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt);
+	bcm_kona_wdt_wdd.parent = &pdev->dev;
 
 	ret = bcm_kona_wdt_set_timeout_reg(&bcm_kona_wdt_wdd, 0);
 	if (ret) {
diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c
index ce12f437f195..a099b77fc0b9 100644
--- a/drivers/watchdog/coh901327_wdt.c
+++ b/drivers/watchdog/coh901327_wdt.c
@@ -358,6 +358,7 @@ static int __init coh901327_probe(struct platform_device *pdev)
 	if (ret < 0)
 		coh901327_wdt.timeout = 60;
 
+	coh901327_wdt.parent = &pdev->dev;
 	ret = watchdog_register_device(&coh901327_wdt);
 	if (ret == 0)
 		dev_info(&pdev->dev,
diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c
index 2e9589652e1e..67e67977bd29 100644
--- a/drivers/watchdog/da9052_wdt.c
+++ b/drivers/watchdog/da9052_wdt.c
@@ -195,6 +195,7 @@ static int da9052_wdt_probe(struct platform_device *pdev)
 	da9052_wdt->timeout = DA9052_DEF_TIMEOUT;
 	da9052_wdt->info = &da9052_wdt_info;
 	da9052_wdt->ops = &da9052_wdt_ops;
+	da9052_wdt->parent = &pdev->dev;
 	watchdog_set_drvdata(da9052_wdt, driver_data);
 
 	kref_init(&driver_data->kref);
diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c
index 495089d8dbfe..04d1430d93d2 100644
--- a/drivers/watchdog/da9055_wdt.c
+++ b/drivers/watchdog/da9055_wdt.c
@@ -161,6 +161,7 @@ static int da9055_wdt_probe(struct platform_device *pdev)
 	da9055_wdt->timeout = DA9055_DEF_TIMEOUT;
 	da9055_wdt->info = &da9055_wdt_info;
 	da9055_wdt->ops = &da9055_wdt_ops;
+	da9055_wdt->parent = &pdev->dev;
 	watchdog_set_nowayout(da9055_wdt, nowayout);
 	watchdog_set_drvdata(da9055_wdt, driver_data);
 
diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c
index b3a870ce85be..7386111220d5 100644
--- a/drivers/watchdog/da9062_wdt.c
+++ b/drivers/watchdog/da9062_wdt.c
@@ -210,6 +210,7 @@ static int da9062_wdt_probe(struct platform_device *pdev)
 	wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
 	wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
 	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
+	wdt->wdtdev.parent = &pdev->dev;
 
 	watchdog_set_drvdata(&wdt->wdtdev, wdt);
 	dev_set_drvdata(&pdev->dev, wdt);
diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c
index e2fe2ebdebd4..6bf130bd863d 100644
--- a/drivers/watchdog/da9063_wdt.c
+++ b/drivers/watchdog/da9063_wdt.c
@@ -175,6 +175,7 @@ static int da9063_wdt_probe(struct platform_device *pdev)
 	wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT;
 	wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT;
 	wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT;
+	wdt->wdtdev.parent = &pdev->dev;
 
 	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
 
diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c
index cfdf8a408aea..17454ca653f4 100644
--- a/drivers/watchdog/davinci_wdt.c
+++ b/drivers/watchdog/davinci_wdt.c
@@ -179,6 +179,7 @@ static int davinci_wdt_probe(struct platform_device *pdev)
 	wdd->min_timeout	= 1;
 	wdd->max_timeout	= MAX_HEARTBEAT;
 	wdd->timeout		= DEFAULT_HEARTBEAT;
+	wdd->parent		= &pdev->dev;
 
 	watchdog_init_timeout(wdd, heartbeat, dev);
 
diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c
index 31d8e4936611..50abe1bf62a5 100644
--- a/drivers/watchdog/digicolor_wdt.c
+++ b/drivers/watchdog/digicolor_wdt.c
@@ -143,6 +143,7 @@ static int dc_wdt_probe(struct platform_device *pdev)
 	}
 	dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk);
 	dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout;
+	dc_wdt_wdd.parent = &pdev->dev;
 
 	spin_lock_init(&wdt->lock);
 
diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c
index 7a2cc7191c58..0a4d7cc05d54 100644
--- a/drivers/watchdog/ep93xx_wdt.c
+++ b/drivers/watchdog/ep93xx_wdt.c
@@ -132,6 +132,7 @@ static int ep93xx_wdt_probe(struct platform_device *pdev)
 	val = readl(mmio_base + EP93XX_WATCHDOG);
 	ep93xx_wdt_wdd.bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0;
 	ep93xx_wdt_wdd.timeout = timeout;
+	ep93xx_wdt_wdd.parent = &pdev->dev;
 
 	watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout);
 
diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c
index 1687cc2d7122..336bddd84809 100644
--- a/drivers/watchdog/gpio_wdt.c
+++ b/drivers/watchdog/gpio_wdt.c
@@ -217,6 +217,7 @@ static int gpio_wdt_probe(struct platform_device *pdev)
 	priv->wdd.ops		= &gpio_wdt_ops;
 	priv->wdd.min_timeout	= SOFT_TIMEOUT_MIN;
 	priv->wdd.max_timeout	= SOFT_TIMEOUT_MAX;
+	priv->wdd.parent	= &pdev->dev;
 
 	if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0)
 		priv->wdd.timeout = SOFT_TIMEOUT_DEF;
diff --git a/drivers/watchdog/ie6xx_wdt.c b/drivers/watchdog/ie6xx_wdt.c
index 9bc39ae51624..78c2541f5d52 100644
--- a/drivers/watchdog/ie6xx_wdt.c
+++ b/drivers/watchdog/ie6xx_wdt.c
@@ -267,6 +267,7 @@ static int ie6xx_wdt_probe(struct platform_device *pdev)
 
 	ie6xx_wdt_dev.timeout = timeout;
 	watchdog_set_nowayout(&ie6xx_wdt_dev, nowayout);
+	ie6xx_wdt_dev.parent = &pdev->dev;
 
 	spin_lock_init(&ie6xx_wdt_data.unlock_sequence);
 
diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c
index 84f6701c391f..0a436b5d1e84 100644
--- a/drivers/watchdog/intel-mid_wdt.c
+++ b/drivers/watchdog/intel-mid_wdt.c
@@ -137,6 +137,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
 	wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
 	wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX;
 	wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT;
+	wdt_dev->parent = &pdev->dev;
 
 	watchdog_set_drvdata(wdt_dev, &pdev->dev);
 	platform_set_drvdata(pdev, wdt_dev);
diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c
index 4c2cc09c0c57..6a7d5c365438 100644
--- a/drivers/watchdog/jz4740_wdt.c
+++ b/drivers/watchdog/jz4740_wdt.c
@@ -174,6 +174,7 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
 	jz4740_wdt->timeout = heartbeat;
 	jz4740_wdt->min_timeout = 1;
 	jz4740_wdt->max_timeout = MAX_HEARTBEAT;
+	jz4740_wdt->parent = &pdev->dev;
 	watchdog_set_nowayout(jz4740_wdt, nowayout);
 	watchdog_set_drvdata(jz4740_wdt, drvdata);
 
diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c
index d193a5e79c38..69013007dc47 100644
--- a/drivers/watchdog/mena21_wdt.c
+++ b/drivers/watchdog/mena21_wdt.c
@@ -197,6 +197,7 @@ static int a21_wdt_probe(struct platform_device *pdev)
 	watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
 	watchdog_set_nowayout(&a21_wdt, nowayout);
 	watchdog_set_drvdata(&a21_wdt, drv);
+	a21_wdt.parent = &pdev->dev;
 
 	reset = a21_wdt_get_bootstatus(drv);
 	if (reset == 2)
diff --git a/drivers/watchdog/menf21bmc_wdt.c b/drivers/watchdog/menf21bmc_wdt.c
index 59f0913c7341..3aefddebb386 100644
--- a/drivers/watchdog/menf21bmc_wdt.c
+++ b/drivers/watchdog/menf21bmc_wdt.c
@@ -130,6 +130,7 @@ static int menf21bmc_wdt_probe(struct platform_device *pdev)
 	drv_data->wdt.info = &menf21bmc_wdt_info;
 	drv_data->wdt.min_timeout = BMC_WD_TIMEOUT_MIN;
 	drv_data->wdt.max_timeout = BMC_WD_TIMEOUT_MAX;
+	drv_data->wdt.parent = &pdev->dev;
 	drv_data->i2c_client = i2c_client;
 
 	/*
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index de911c7e477c..d96bee017fd3 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -253,6 +253,7 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	wdev->wdog.ops = &omap_wdt_ops;
 	wdev->wdog.min_timeout = TIMER_MARGIN_MIN;
 	wdev->wdog.max_timeout = TIMER_MARGIN_MAX;
+	wdev->wdog.parent = &pdev->dev;
 
 	if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0)
 		wdev->wdog.timeout = TIMER_MARGIN_DEFAULT;
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index ef0c628d5037..c6b8f4a43bde 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -567,6 +567,7 @@ static int orion_wdt_probe(struct platform_device *pdev)
 
 	dev->wdt.timeout = wdt_max_duration;
 	dev->wdt.max_timeout = wdt_max_duration;
+	dev->wdt.parent = &pdev->dev;
 	watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev);
 
 	platform_set_drvdata(pdev, &dev->wdt);
diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c
index b9c6049c3e78..4224b3ec83a5 100644
--- a/drivers/watchdog/pnx4008_wdt.c
+++ b/drivers/watchdog/pnx4008_wdt.c
@@ -167,6 +167,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
 
 	pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
 			WDIOF_CARDRESET : 0;
+	pnx4008_wdd.parent = &pdev->dev;
 	watchdog_set_nowayout(&pnx4008_wdd, nowayout);
 
 	pnx4008_wdt_stop(&pnx4008_wdd);	/* disable for now */
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c
index aa03ca8f2d9b..773dcfaee7b2 100644
--- a/drivers/watchdog/qcom-wdt.c
+++ b/drivers/watchdog/qcom-wdt.c
@@ -171,6 +171,7 @@ static int qcom_wdt_probe(struct platform_device *pdev)
 	wdt->wdd.ops = &qcom_wdt_ops;
 	wdt->wdd.min_timeout = 1;
 	wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
+	wdt->wdd.parent = &pdev->dev;
 
 	/*
 	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
diff --git a/drivers/watchdog/retu_wdt.c b/drivers/watchdog/retu_wdt.c
index b7c68e275aeb..39cd51df2ffc 100644
--- a/drivers/watchdog/retu_wdt.c
+++ b/drivers/watchdog/retu_wdt.c
@@ -127,6 +127,7 @@ static int retu_wdt_probe(struct platform_device *pdev)
 	retu_wdt->timeout	= RETU_WDT_MAX_TIMER;
 	retu_wdt->min_timeout	= 0;
 	retu_wdt->max_timeout	= RETU_WDT_MAX_TIMER;
+	retu_wdt->parent	= &pdev->dev;
 
 	watchdog_set_drvdata(retu_wdt, wdev);
 	watchdog_set_nowayout(retu_wdt, nowayout);
diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c
index a6f7e2e29beb..1967919ae743 100644
--- a/drivers/watchdog/rt2880_wdt.c
+++ b/drivers/watchdog/rt2880_wdt.c
@@ -161,6 +161,7 @@ static int rt288x_wdt_probe(struct platform_device *pdev)
 	rt288x_wdt_dev.dev = &pdev->dev;
 	rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
 	rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
+	rt288x_wdt_dev.parent = &pdev->dev;
 
 	watchdog_init_timeout(&rt288x_wdt_dev, rt288x_wdt_dev.max_timeout,
 			      &pdev->dev);
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index e89ae027c91d..d781000c7825 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -607,6 +607,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
 	watchdog_set_nowayout(&wdt->wdt_device, nowayout);
 
 	wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
+	wdt->wdt_device.parent = &pdev->dev;
 
 	ret = watchdog_register_device(&wdt->wdt_device);
 	if (ret) {
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c
index 567458b137a6..f90812170657 100644
--- a/drivers/watchdog/shwdt.c
+++ b/drivers/watchdog/shwdt.c
@@ -252,6 +252,7 @@ static int sh_wdt_probe(struct platform_device *pdev)
 
 	watchdog_set_nowayout(&sh_wdt_dev, nowayout);
 	watchdog_set_drvdata(&sh_wdt_dev, wdt);
+	sh_wdt_dev.parent = &pdev->dev;
 
 	spin_lock_init(&wdt->lock);
 
diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c
index 42fa5c0c518a..d0578ab2e636 100644
--- a/drivers/watchdog/sirfsoc_wdt.c
+++ b/drivers/watchdog/sirfsoc_wdt.c
@@ -154,6 +154,7 @@ static int sirfsoc_wdt_probe(struct platform_device *pdev)
 
 	watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev);
 	watchdog_set_nowayout(&sirfsoc_wdd, nowayout);
+	sirfsoc_wdd.parent = &pdev->dev;
 
 	ret = watchdog_register_device(&sirfsoc_wdd);
 	if (ret)
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
index 4e7fec36f5c3..01d816251302 100644
--- a/drivers/watchdog/sp805_wdt.c
+++ b/drivers/watchdog/sp805_wdt.c
@@ -226,6 +226,7 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
 	wdt->adev = adev;
 	wdt->wdd.info = &wdt_info;
 	wdt->wdd.ops = &wdt_ops;
+	wdt->wdd.parent = &adev->dev;
 
 	spin_lock_init(&wdt->lock);
 	watchdog_set_nowayout(&wdt->wdd, nowayout);
diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c
index 6785afdc0fca..14e9badf2bfa 100644
--- a/drivers/watchdog/st_lpc_wdt.c
+++ b/drivers/watchdog/st_lpc_wdt.c
@@ -241,6 +241,7 @@ static int st_wdog_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 	st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
+	st_wdog_dev.parent = &pdev->dev;
 
 	ret = clk_prepare_enable(clk);
 	if (ret) {
diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c b/drivers/watchdog/stmp3xxx_rtc_wdt.c
index e7f0d5b60d3d..3ee6128a540e 100644
--- a/drivers/watchdog/stmp3xxx_rtc_wdt.c
+++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c
@@ -76,6 +76,7 @@ static int stmp3xxx_wdt_probe(struct platform_device *pdev)
 	watchdog_set_drvdata(&stmp3xxx_wdd, &pdev->dev);
 
 	stmp3xxx_wdd.timeout = clamp_t(unsigned, heartbeat, 1, STMP3XXX_MAX_TIMEOUT);
+	stmp3xxx_wdd.parent = &pdev->dev;
 
 	ret = watchdog_register_device(&stmp3xxx_wdd);
 	if (ret < 0) {
diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c
index 30451ea46902..7f97cdd53f29 100644
--- a/drivers/watchdog/tegra_wdt.c
+++ b/drivers/watchdog/tegra_wdt.c
@@ -218,6 +218,7 @@ static int tegra_wdt_probe(struct platform_device *pdev)
 	wdd->ops = &tegra_wdt_ops;
 	wdd->min_timeout = MIN_WDT_TIMEOUT;
 	wdd->max_timeout = MAX_WDT_TIMEOUT;
+	wdd->parent = &pdev->dev;
 
 	watchdog_set_drvdata(wdd, wdt);
 
diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c
index 2c1db6fa9a27..9bf3cc0f3961 100644
--- a/drivers/watchdog/twl4030_wdt.c
+++ b/drivers/watchdog/twl4030_wdt.c
@@ -83,6 +83,7 @@ static int twl4030_wdt_probe(struct platform_device *pdev)
 	wdt->timeout		= 30;
 	wdt->min_timeout	= 1;
 	wdt->max_timeout	= 30;
+	wdt->parent = &pdev->dev;
 
 	watchdog_set_nowayout(wdt, nowayout);
 	platform_set_drvdata(pdev, wdt);
diff --git a/drivers/watchdog/txx9wdt.c b/drivers/watchdog/txx9wdt.c
index 7f615933d31a..c4fa6b55c53d 100644
--- a/drivers/watchdog/txx9wdt.c
+++ b/drivers/watchdog/txx9wdt.c
@@ -131,6 +131,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
 	txx9wdt.timeout = timeout;
 	txx9wdt.min_timeout = 1;
 	txx9wdt.max_timeout = WD_MAX_TIMEOUT;
+	txx9wdt.parent = &pdev->dev;
 	watchdog_set_nowayout(&txx9wdt, nowayout);
 
 	ret = watchdog_register_device(&txx9wdt);
diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c
index 9de09ab00838..37c084353cce 100644
--- a/drivers/watchdog/ux500_wdt.c
+++ b/drivers/watchdog/ux500_wdt.c
@@ -96,6 +96,7 @@ static int ux500_wdt_probe(struct platform_device *pdev)
 			ux500_wdt.max_timeout = WATCHDOG_MAX28;
 	}
 
+	ux500_wdt.parent = &pdev->dev;
 	watchdog_set_nowayout(&ux500_wdt, nowayout);
 
 	/* disable auto off on sleep */
diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c
index 56369c4f1961..5f9cbc37520d 100644
--- a/drivers/watchdog/via_wdt.c
+++ b/drivers/watchdog/via_wdt.c
@@ -206,6 +206,7 @@ static int wdt_probe(struct pci_dev *pdev,
 		timeout = WDT_TIMEOUT;
 
 	wdt_dev.timeout = timeout;
+	wdt_dev.parent = &pdev->dev;
 	watchdog_set_nowayout(&wdt_dev, nowayout);
 	if (readl(wdt_mem) & VIA_WDT_FIRED)
 		wdt_dev.bootstatus |= WDIOF_CARDRESET;
diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c
index 2fa17e746ff6..8d1184aee932 100644
--- a/drivers/watchdog/wm831x_wdt.c
+++ b/drivers/watchdog/wm831x_wdt.c
@@ -215,6 +215,7 @@ static int wm831x_wdt_probe(struct platform_device *pdev)
 
 	wm831x_wdt->info = &wm831x_wdt_info;
 	wm831x_wdt->ops = &wm831x_wdt_ops;
+	wm831x_wdt->parent = &pdev->dev;
 	watchdog_set_nowayout(wm831x_wdt, nowayout);
 	watchdog_set_drvdata(wm831x_wdt, driver_data);
 
diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c
index 34d272ada23d..4ab4b8347d45 100644
--- a/drivers/watchdog/wm8350_wdt.c
+++ b/drivers/watchdog/wm8350_wdt.c
@@ -151,6 +151,7 @@ static int wm8350_wdt_probe(struct platform_device *pdev)
 
 	watchdog_set_nowayout(&wm8350_wdt, nowayout);
 	watchdog_set_drvdata(&wm8350_wdt, wm8350);
+	wm8350_wdt.parent = &pdev->dev;
 
 	/* Default to 4s timeout */
 	wm8350_wdt_set_timeout(&wm8350_wdt, 4);
-- 
2.4.3



From HartleyS at visionengravers.com  Tue Aug 18 09:45:44 2015
From: HartleyS at visionengravers.com (Hartley Sweeten)
Date: Tue, 18 Aug 2015 16:45:44 +0000
Subject: [PATCH V2] Watchdog: Fix parent of watchdog_devices
In-Reply-To: <9fcc2a2cc8273f461baa13c6c9992f3ca56b508a.1439915117.git.panand@redhat.com>
References: <9fcc2a2cc8273f461baa13c6c9992f3ca56b508a.1439915117.git.panand@redhat.com>
Message-ID: <BN3PR0101MB1057C3FE09F00EEC55D2C9B1D0780@BN3PR0101MB1057.prod.exchangelabs.com>

On Tuesday, August 18, 2015 9:34 AM, Pratyush Anand wrote:
> 
> /sys/class/watchdog/watchdogn/device/modalias can help to identify the
> driver/module for a given watchdog node. However, many wdt devices does not set
> its parent and so, we do not see an entry for device in sysfs for such devices.
>
> This patch fixes parent of watchdog_device so that
> /sys/class/watchdog/watchdogn/device is populated.
>
> Exceptions: booke, diag288, mpc8xxx, octeon, softdog and w83627hf -- They do not
> have any parent. Not sure, how we can we identify driver for these devices.
>
> Signed-off-by: Pratyush Anand <panand at redhat.com>
> ---
[snip]

drivers/watchdog/ep93xx_wdt.c       | 1 +

[snip]

diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c
index 7a2cc7191c58..0a4d7cc05d54 100644
--- a/drivers/watchdog/ep93xx_wdt.c
+++ b/drivers/watchdog/ep93xx_wdt.c
@@ -132,6 +132,7 @@ static int ep93xx_wdt_probe(struct platform_device *pdev)
 	val = readl(mmio_base + EP93XX_WATCHDOG);
 	ep93xx_wdt_wdd.bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0;
 	ep93xx_wdt_wdd.timeout = timeout;
+	ep93xx_wdt_wdd.parent = &pdev->dev;
 
 	watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout);

For ep93xx_wdt.c, use whichever tag you prefer:

Reviewed-by: H Hartley Sweeten <hsweeten at visionengravers.com>
Acked-by: H Hartley Sweeten <hsweeten at visionengravers.com>

Thanks,
Hartley



From panand at redhat.com  Tue Aug 18 20:28:24 2015
From: panand at redhat.com (Pratyush Anand)
Date: Wed, 19 Aug 2015 08:58:24 +0530
Subject: [PATCH V3] Watchdog: Fix parent of watchdog_devices
Message-ID: <db7db9102158b87a4a7169e84e2272b3ba03f9f7.1439954581.git.panand@redhat.com>

/sys/class/watchdog/watchdogn/device/modalias can help to identify the
driver/module for a given watchdog node. However, many wdt devices does not set
its parent and so, we do not see an entry for device in sysfs for such devices.

This patch fixes parent of watchdog_device so that
/sys/class/watchdog/watchdogn/device is populated.

Exceptions: booke, diag288, mpc8xxx, octeon, softdog and w83627hf -- They do not
have any parent. Not sure, how we can we identify driver for these devices.

Signed-off-by: Pratyush Anand <panand at redhat.com>
---
Changes since v2:
Fixed drivers/watchdog/txx9wdt.c:134:20: error: 'pdev' undeclared
Reported-by: kbuild test robot <fengguang.wu at intel.com>
Changes since v1:
Squash all commits of V1 into a single patch.
V1 was here:
http://www.spinics.net/lists/linux-watchdog/msg07220.html

 drivers/misc/mei/wd.c               | 1 +
 drivers/watchdog/bcm2835_wdt.c      | 1 +
 drivers/watchdog/bcm47xx_wdt.c      | 1 +
 drivers/watchdog/bcm_kona_wdt.c     | 1 +
 drivers/watchdog/coh901327_wdt.c    | 1 +
 drivers/watchdog/da9052_wdt.c       | 1 +
 drivers/watchdog/da9055_wdt.c       | 1 +
 drivers/watchdog/da9062_wdt.c       | 1 +
 drivers/watchdog/da9063_wdt.c       | 1 +
 drivers/watchdog/davinci_wdt.c      | 1 +
 drivers/watchdog/digicolor_wdt.c    | 1 +
 drivers/watchdog/ep93xx_wdt.c       | 1 +
 drivers/watchdog/gpio_wdt.c         | 1 +
 drivers/watchdog/ie6xx_wdt.c        | 1 +
 drivers/watchdog/intel-mid_wdt.c    | 1 +
 drivers/watchdog/jz4740_wdt.c       | 1 +
 drivers/watchdog/mena21_wdt.c       | 1 +
 drivers/watchdog/menf21bmc_wdt.c    | 1 +
 drivers/watchdog/omap_wdt.c         | 1 +
 drivers/watchdog/orion_wdt.c        | 1 +
 drivers/watchdog/pnx4008_wdt.c      | 1 +
 drivers/watchdog/qcom-wdt.c         | 1 +
 drivers/watchdog/retu_wdt.c         | 1 +
 drivers/watchdog/rt2880_wdt.c       | 1 +
 drivers/watchdog/s3c2410_wdt.c      | 1 +
 drivers/watchdog/shwdt.c            | 1 +
 drivers/watchdog/sirfsoc_wdt.c      | 1 +
 drivers/watchdog/sp805_wdt.c        | 1 +
 drivers/watchdog/st_lpc_wdt.c       | 1 +
 drivers/watchdog/stmp3xxx_rtc_wdt.c | 1 +
 drivers/watchdog/tegra_wdt.c        | 1 +
 drivers/watchdog/twl4030_wdt.c      | 1 +
 drivers/watchdog/txx9wdt.c          | 1 +
 drivers/watchdog/ux500_wdt.c        | 1 +
 drivers/watchdog/via_wdt.c          | 1 +
 drivers/watchdog/wm831x_wdt.c       | 1 +
 drivers/watchdog/wm8350_wdt.c       | 1 +
 37 files changed, 37 insertions(+)

diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c
index 2bc0f5089f82..b346638833b0 100644
--- a/drivers/misc/mei/wd.c
+++ b/drivers/misc/mei/wd.c
@@ -364,6 +364,7 @@ int mei_watchdog_register(struct mei_device *dev)
 
 	int ret;
 
+	amt_wd_dev.parent = dev->dev;
 	/* unlock to perserve correct locking order */
 	mutex_unlock(&dev->device_lock);
 	ret = watchdog_register_device(&amt_wd_dev);
diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c
index 7116968dee12..66c3e656a616 100644
--- a/drivers/watchdog/bcm2835_wdt.c
+++ b/drivers/watchdog/bcm2835_wdt.c
@@ -182,6 +182,7 @@ static int bcm2835_wdt_probe(struct platform_device *pdev)
 	watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt);
 	watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev);
 	watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout);
+	bcm2835_wdt_wdd.parent = &pdev->dev;
 	err = watchdog_register_device(&bcm2835_wdt_wdd);
 	if (err) {
 		dev_err(dev, "Failed to register watchdog device");
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c
index b28a072abf78..4064a43f1360 100644
--- a/drivers/watchdog/bcm47xx_wdt.c
+++ b/drivers/watchdog/bcm47xx_wdt.c
@@ -209,6 +209,7 @@ static int bcm47xx_wdt_probe(struct platform_device *pdev)
 
 	wdt->wdd.info = &bcm47xx_wdt_info;
 	wdt->wdd.timeout = WDT_DEFAULT_TIME;
+	wdt->wdd.parent = &pdev->dev;
 	ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout);
 	if (ret)
 		goto err_timer;
diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c
index 22d8ae65772a..e0c98423f2c9 100644
--- a/drivers/watchdog/bcm_kona_wdt.c
+++ b/drivers/watchdog/bcm_kona_wdt.c
@@ -319,6 +319,7 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev)
 	spin_lock_init(&wdt->lock);
 	platform_set_drvdata(pdev, wdt);
 	watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt);
+	bcm_kona_wdt_wdd.parent = &pdev->dev;
 
 	ret = bcm_kona_wdt_set_timeout_reg(&bcm_kona_wdt_wdd, 0);
 	if (ret) {
diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c
index ce12f437f195..a099b77fc0b9 100644
--- a/drivers/watchdog/coh901327_wdt.c
+++ b/drivers/watchdog/coh901327_wdt.c
@@ -358,6 +358,7 @@ static int __init coh901327_probe(struct platform_device *pdev)
 	if (ret < 0)
 		coh901327_wdt.timeout = 60;
 
+	coh901327_wdt.parent = &pdev->dev;
 	ret = watchdog_register_device(&coh901327_wdt);
 	if (ret == 0)
 		dev_info(&pdev->dev,
diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c
index 2e9589652e1e..67e67977bd29 100644
--- a/drivers/watchdog/da9052_wdt.c
+++ b/drivers/watchdog/da9052_wdt.c
@@ -195,6 +195,7 @@ static int da9052_wdt_probe(struct platform_device *pdev)
 	da9052_wdt->timeout = DA9052_DEF_TIMEOUT;
 	da9052_wdt->info = &da9052_wdt_info;
 	da9052_wdt->ops = &da9052_wdt_ops;
+	da9052_wdt->parent = &pdev->dev;
 	watchdog_set_drvdata(da9052_wdt, driver_data);
 
 	kref_init(&driver_data->kref);
diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c
index 495089d8dbfe..04d1430d93d2 100644
--- a/drivers/watchdog/da9055_wdt.c
+++ b/drivers/watchdog/da9055_wdt.c
@@ -161,6 +161,7 @@ static int da9055_wdt_probe(struct platform_device *pdev)
 	da9055_wdt->timeout = DA9055_DEF_TIMEOUT;
 	da9055_wdt->info = &da9055_wdt_info;
 	da9055_wdt->ops = &da9055_wdt_ops;
+	da9055_wdt->parent = &pdev->dev;
 	watchdog_set_nowayout(da9055_wdt, nowayout);
 	watchdog_set_drvdata(da9055_wdt, driver_data);
 
diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c
index b3a870ce85be..7386111220d5 100644
--- a/drivers/watchdog/da9062_wdt.c
+++ b/drivers/watchdog/da9062_wdt.c
@@ -210,6 +210,7 @@ static int da9062_wdt_probe(struct platform_device *pdev)
 	wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
 	wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
 	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
+	wdt->wdtdev.parent = &pdev->dev;
 
 	watchdog_set_drvdata(&wdt->wdtdev, wdt);
 	dev_set_drvdata(&pdev->dev, wdt);
diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c
index e2fe2ebdebd4..6bf130bd863d 100644
--- a/drivers/watchdog/da9063_wdt.c
+++ b/drivers/watchdog/da9063_wdt.c
@@ -175,6 +175,7 @@ static int da9063_wdt_probe(struct platform_device *pdev)
 	wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT;
 	wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT;
 	wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT;
+	wdt->wdtdev.parent = &pdev->dev;
 
 	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
 
diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c
index cfdf8a408aea..17454ca653f4 100644
--- a/drivers/watchdog/davinci_wdt.c
+++ b/drivers/watchdog/davinci_wdt.c
@@ -179,6 +179,7 @@ static int davinci_wdt_probe(struct platform_device *pdev)
 	wdd->min_timeout	= 1;
 	wdd->max_timeout	= MAX_HEARTBEAT;
 	wdd->timeout		= DEFAULT_HEARTBEAT;
+	wdd->parent		= &pdev->dev;
 
 	watchdog_init_timeout(wdd, heartbeat, dev);
 
diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c
index 31d8e4936611..50abe1bf62a5 100644
--- a/drivers/watchdog/digicolor_wdt.c
+++ b/drivers/watchdog/digicolor_wdt.c
@@ -143,6 +143,7 @@ static int dc_wdt_probe(struct platform_device *pdev)
 	}
 	dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk);
 	dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout;
+	dc_wdt_wdd.parent = &pdev->dev;
 
 	spin_lock_init(&wdt->lock);
 
diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c
index 7a2cc7191c58..0a4d7cc05d54 100644
--- a/drivers/watchdog/ep93xx_wdt.c
+++ b/drivers/watchdog/ep93xx_wdt.c
@@ -132,6 +132,7 @@ static int ep93xx_wdt_probe(struct platform_device *pdev)
 	val = readl(mmio_base + EP93XX_WATCHDOG);
 	ep93xx_wdt_wdd.bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0;
 	ep93xx_wdt_wdd.timeout = timeout;
+	ep93xx_wdt_wdd.parent = &pdev->dev;
 
 	watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout);
 
diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c
index 1687cc2d7122..336bddd84809 100644
--- a/drivers/watchdog/gpio_wdt.c
+++ b/drivers/watchdog/gpio_wdt.c
@@ -217,6 +217,7 @@ static int gpio_wdt_probe(struct platform_device *pdev)
 	priv->wdd.ops		= &gpio_wdt_ops;
 	priv->wdd.min_timeout	= SOFT_TIMEOUT_MIN;
 	priv->wdd.max_timeout	= SOFT_TIMEOUT_MAX;
+	priv->wdd.parent	= &pdev->dev;
 
 	if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0)
 		priv->wdd.timeout = SOFT_TIMEOUT_DEF;
diff --git a/drivers/watchdog/ie6xx_wdt.c b/drivers/watchdog/ie6xx_wdt.c
index 9bc39ae51624..78c2541f5d52 100644
--- a/drivers/watchdog/ie6xx_wdt.c
+++ b/drivers/watchdog/ie6xx_wdt.c
@@ -267,6 +267,7 @@ static int ie6xx_wdt_probe(struct platform_device *pdev)
 
 	ie6xx_wdt_dev.timeout = timeout;
 	watchdog_set_nowayout(&ie6xx_wdt_dev, nowayout);
+	ie6xx_wdt_dev.parent = &pdev->dev;
 
 	spin_lock_init(&ie6xx_wdt_data.unlock_sequence);
 
diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c
index 84f6701c391f..0a436b5d1e84 100644
--- a/drivers/watchdog/intel-mid_wdt.c
+++ b/drivers/watchdog/intel-mid_wdt.c
@@ -137,6 +137,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
 	wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
 	wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX;
 	wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT;
+	wdt_dev->parent = &pdev->dev;
 
 	watchdog_set_drvdata(wdt_dev, &pdev->dev);
 	platform_set_drvdata(pdev, wdt_dev);
diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c
index 4c2cc09c0c57..6a7d5c365438 100644
--- a/drivers/watchdog/jz4740_wdt.c
+++ b/drivers/watchdog/jz4740_wdt.c
@@ -174,6 +174,7 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
 	jz4740_wdt->timeout = heartbeat;
 	jz4740_wdt->min_timeout = 1;
 	jz4740_wdt->max_timeout = MAX_HEARTBEAT;
+	jz4740_wdt->parent = &pdev->dev;
 	watchdog_set_nowayout(jz4740_wdt, nowayout);
 	watchdog_set_drvdata(jz4740_wdt, drvdata);
 
diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c
index d193a5e79c38..69013007dc47 100644
--- a/drivers/watchdog/mena21_wdt.c
+++ b/drivers/watchdog/mena21_wdt.c
@@ -197,6 +197,7 @@ static int a21_wdt_probe(struct platform_device *pdev)
 	watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
 	watchdog_set_nowayout(&a21_wdt, nowayout);
 	watchdog_set_drvdata(&a21_wdt, drv);
+	a21_wdt.parent = &pdev->dev;
 
 	reset = a21_wdt_get_bootstatus(drv);
 	if (reset == 2)
diff --git a/drivers/watchdog/menf21bmc_wdt.c b/drivers/watchdog/menf21bmc_wdt.c
index 59f0913c7341..3aefddebb386 100644
--- a/drivers/watchdog/menf21bmc_wdt.c
+++ b/drivers/watchdog/menf21bmc_wdt.c
@@ -130,6 +130,7 @@ static int menf21bmc_wdt_probe(struct platform_device *pdev)
 	drv_data->wdt.info = &menf21bmc_wdt_info;
 	drv_data->wdt.min_timeout = BMC_WD_TIMEOUT_MIN;
 	drv_data->wdt.max_timeout = BMC_WD_TIMEOUT_MAX;
+	drv_data->wdt.parent = &pdev->dev;
 	drv_data->i2c_client = i2c_client;
 
 	/*
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index de911c7e477c..d96bee017fd3 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -253,6 +253,7 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	wdev->wdog.ops = &omap_wdt_ops;
 	wdev->wdog.min_timeout = TIMER_MARGIN_MIN;
 	wdev->wdog.max_timeout = TIMER_MARGIN_MAX;
+	wdev->wdog.parent = &pdev->dev;
 
 	if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0)
 		wdev->wdog.timeout = TIMER_MARGIN_DEFAULT;
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index ef0c628d5037..c6b8f4a43bde 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -567,6 +567,7 @@ static int orion_wdt_probe(struct platform_device *pdev)
 
 	dev->wdt.timeout = wdt_max_duration;
 	dev->wdt.max_timeout = wdt_max_duration;
+	dev->wdt.parent = &pdev->dev;
 	watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev);
 
 	platform_set_drvdata(pdev, &dev->wdt);
diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c
index b9c6049c3e78..4224b3ec83a5 100644
--- a/drivers/watchdog/pnx4008_wdt.c
+++ b/drivers/watchdog/pnx4008_wdt.c
@@ -167,6 +167,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
 
 	pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
 			WDIOF_CARDRESET : 0;
+	pnx4008_wdd.parent = &pdev->dev;
 	watchdog_set_nowayout(&pnx4008_wdd, nowayout);
 
 	pnx4008_wdt_stop(&pnx4008_wdd);	/* disable for now */
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c
index aa03ca8f2d9b..773dcfaee7b2 100644
--- a/drivers/watchdog/qcom-wdt.c
+++ b/drivers/watchdog/qcom-wdt.c
@@ -171,6 +171,7 @@ static int qcom_wdt_probe(struct platform_device *pdev)
 	wdt->wdd.ops = &qcom_wdt_ops;
 	wdt->wdd.min_timeout = 1;
 	wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
+	wdt->wdd.parent = &pdev->dev;
 
 	/*
 	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
diff --git a/drivers/watchdog/retu_wdt.c b/drivers/watchdog/retu_wdt.c
index b7c68e275aeb..39cd51df2ffc 100644
--- a/drivers/watchdog/retu_wdt.c
+++ b/drivers/watchdog/retu_wdt.c
@@ -127,6 +127,7 @@ static int retu_wdt_probe(struct platform_device *pdev)
 	retu_wdt->timeout	= RETU_WDT_MAX_TIMER;
 	retu_wdt->min_timeout	= 0;
 	retu_wdt->max_timeout	= RETU_WDT_MAX_TIMER;
+	retu_wdt->parent	= &pdev->dev;
 
 	watchdog_set_drvdata(retu_wdt, wdev);
 	watchdog_set_nowayout(retu_wdt, nowayout);
diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c
index a6f7e2e29beb..1967919ae743 100644
--- a/drivers/watchdog/rt2880_wdt.c
+++ b/drivers/watchdog/rt2880_wdt.c
@@ -161,6 +161,7 @@ static int rt288x_wdt_probe(struct platform_device *pdev)
 	rt288x_wdt_dev.dev = &pdev->dev;
 	rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
 	rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
+	rt288x_wdt_dev.parent = &pdev->dev;
 
 	watchdog_init_timeout(&rt288x_wdt_dev, rt288x_wdt_dev.max_timeout,
 			      &pdev->dev);
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index e89ae027c91d..d781000c7825 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -607,6 +607,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
 	watchdog_set_nowayout(&wdt->wdt_device, nowayout);
 
 	wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
+	wdt->wdt_device.parent = &pdev->dev;
 
 	ret = watchdog_register_device(&wdt->wdt_device);
 	if (ret) {
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c
index 567458b137a6..f90812170657 100644
--- a/drivers/watchdog/shwdt.c
+++ b/drivers/watchdog/shwdt.c
@@ -252,6 +252,7 @@ static int sh_wdt_probe(struct platform_device *pdev)
 
 	watchdog_set_nowayout(&sh_wdt_dev, nowayout);
 	watchdog_set_drvdata(&sh_wdt_dev, wdt);
+	sh_wdt_dev.parent = &pdev->dev;
 
 	spin_lock_init(&wdt->lock);
 
diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c
index 42fa5c0c518a..d0578ab2e636 100644
--- a/drivers/watchdog/sirfsoc_wdt.c
+++ b/drivers/watchdog/sirfsoc_wdt.c
@@ -154,6 +154,7 @@ static int sirfsoc_wdt_probe(struct platform_device *pdev)
 
 	watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev);
 	watchdog_set_nowayout(&sirfsoc_wdd, nowayout);
+	sirfsoc_wdd.parent = &pdev->dev;
 
 	ret = watchdog_register_device(&sirfsoc_wdd);
 	if (ret)
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
index 4e7fec36f5c3..01d816251302 100644
--- a/drivers/watchdog/sp805_wdt.c
+++ b/drivers/watchdog/sp805_wdt.c
@@ -226,6 +226,7 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
 	wdt->adev = adev;
 	wdt->wdd.info = &wdt_info;
 	wdt->wdd.ops = &wdt_ops;
+	wdt->wdd.parent = &adev->dev;
 
 	spin_lock_init(&wdt->lock);
 	watchdog_set_nowayout(&wdt->wdd, nowayout);
diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c
index 6785afdc0fca..14e9badf2bfa 100644
--- a/drivers/watchdog/st_lpc_wdt.c
+++ b/drivers/watchdog/st_lpc_wdt.c
@@ -241,6 +241,7 @@ static int st_wdog_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 	st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
+	st_wdog_dev.parent = &pdev->dev;
 
 	ret = clk_prepare_enable(clk);
 	if (ret) {
diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c b/drivers/watchdog/stmp3xxx_rtc_wdt.c
index e7f0d5b60d3d..3ee6128a540e 100644
--- a/drivers/watchdog/stmp3xxx_rtc_wdt.c
+++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c
@@ -76,6 +76,7 @@ static int stmp3xxx_wdt_probe(struct platform_device *pdev)
 	watchdog_set_drvdata(&stmp3xxx_wdd, &pdev->dev);
 
 	stmp3xxx_wdd.timeout = clamp_t(unsigned, heartbeat, 1, STMP3XXX_MAX_TIMEOUT);
+	stmp3xxx_wdd.parent = &pdev->dev;
 
 	ret = watchdog_register_device(&stmp3xxx_wdd);
 	if (ret < 0) {
diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c
index 30451ea46902..7f97cdd53f29 100644
--- a/drivers/watchdog/tegra_wdt.c
+++ b/drivers/watchdog/tegra_wdt.c
@@ -218,6 +218,7 @@ static int tegra_wdt_probe(struct platform_device *pdev)
 	wdd->ops = &tegra_wdt_ops;
 	wdd->min_timeout = MIN_WDT_TIMEOUT;
 	wdd->max_timeout = MAX_WDT_TIMEOUT;
+	wdd->parent = &pdev->dev;
 
 	watchdog_set_drvdata(wdd, wdt);
 
diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c
index 2c1db6fa9a27..9bf3cc0f3961 100644
--- a/drivers/watchdog/twl4030_wdt.c
+++ b/drivers/watchdog/twl4030_wdt.c
@@ -83,6 +83,7 @@ static int twl4030_wdt_probe(struct platform_device *pdev)
 	wdt->timeout		= 30;
 	wdt->min_timeout	= 1;
 	wdt->max_timeout	= 30;
+	wdt->parent = &pdev->dev;
 
 	watchdog_set_nowayout(wdt, nowayout);
 	platform_set_drvdata(pdev, wdt);
diff --git a/drivers/watchdog/txx9wdt.c b/drivers/watchdog/txx9wdt.c
index 7f615933d31a..c2da880292bc 100644
--- a/drivers/watchdog/txx9wdt.c
+++ b/drivers/watchdog/txx9wdt.c
@@ -131,6 +131,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
 	txx9wdt.timeout = timeout;
 	txx9wdt.min_timeout = 1;
 	txx9wdt.max_timeout = WD_MAX_TIMEOUT;
+	txx9wdt.parent = &dev->dev;
 	watchdog_set_nowayout(&txx9wdt, nowayout);
 
 	ret = watchdog_register_device(&txx9wdt);
diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c
index 9de09ab00838..37c084353cce 100644
--- a/drivers/watchdog/ux500_wdt.c
+++ b/drivers/watchdog/ux500_wdt.c
@@ -96,6 +96,7 @@ static int ux500_wdt_probe(struct platform_device *pdev)
 			ux500_wdt.max_timeout = WATCHDOG_MAX28;
 	}
 
+	ux500_wdt.parent = &pdev->dev;
 	watchdog_set_nowayout(&ux500_wdt, nowayout);
 
 	/* disable auto off on sleep */
diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c
index 56369c4f1961..5f9cbc37520d 100644
--- a/drivers/watchdog/via_wdt.c
+++ b/drivers/watchdog/via_wdt.c
@@ -206,6 +206,7 @@ static int wdt_probe(struct pci_dev *pdev,
 		timeout = WDT_TIMEOUT;
 
 	wdt_dev.timeout = timeout;
+	wdt_dev.parent = &pdev->dev;
 	watchdog_set_nowayout(&wdt_dev, nowayout);
 	if (readl(wdt_mem) & VIA_WDT_FIRED)
 		wdt_dev.bootstatus |= WDIOF_CARDRESET;
diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c
index 2fa17e746ff6..8d1184aee932 100644
--- a/drivers/watchdog/wm831x_wdt.c
+++ b/drivers/watchdog/wm831x_wdt.c
@@ -215,6 +215,7 @@ static int wm831x_wdt_probe(struct platform_device *pdev)
 
 	wm831x_wdt->info = &wm831x_wdt_info;
 	wm831x_wdt->ops = &wm831x_wdt_ops;
+	wm831x_wdt->parent = &pdev->dev;
 	watchdog_set_nowayout(wm831x_wdt, nowayout);
 	watchdog_set_drvdata(wm831x_wdt, driver_data);
 
diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c
index 34d272ada23d..4ab4b8347d45 100644
--- a/drivers/watchdog/wm8350_wdt.c
+++ b/drivers/watchdog/wm8350_wdt.c
@@ -151,6 +151,7 @@ static int wm8350_wdt_probe(struct platform_device *pdev)
 
 	watchdog_set_nowayout(&wm8350_wdt, nowayout);
 	watchdog_set_drvdata(&wm8350_wdt, wm8350);
+	wm8350_wdt.parent = &pdev->dev;
 
 	/* Default to 4s timeout */
 	wm8350_wdt_set_timeout(&wm8350_wdt, 4);
-- 
2.4.3



From u.kleine-koenig at pengutronix.de  Tue Aug 18 23:44:19 2015
From: u.kleine-koenig at pengutronix.de (Uwe =?iso-8859-1?Q?Kleine-K=F6nig?=)
Date: Wed, 19 Aug 2015 08:44:19 +0200
Subject: [PATCH V3] Watchdog: Fix parent of watchdog_devices
In-Reply-To: <db7db9102158b87a4a7169e84e2272b3ba03f9f7.1439954581.git.panand@redhat.com>
References: <db7db9102158b87a4a7169e84e2272b3ba03f9f7.1439954581.git.panand@redhat.com>
Message-ID: <20150819064419.GC9999@pengutronix.de>

Hello,

On Wed, Aug 19, 2015 at 08:58:24AM +0530, Pratyush Anand wrote:
> /sys/class/watchdog/watchdogn/device/modalias can help to identify the
> driver/module for a given watchdog node. However, many wdt devices does not set

s/es//

> its parent and so, we do not see an entry for device in sysfs for such devices.
> 
> This patch fixes parent of watchdog_device so that
> /sys/class/watchdog/watchdogn/device is populated.
> 
> Exceptions: booke, diag288, mpc8xxx, octeon, softdog and w83627hf -- They do not
> have any parent. Not sure, how we can we identify driver for these devices.

I don't see why you don't use the platform device as parent for mpc8xxx.

Note however that I did several updates to the mpc8xxx driver which
probably conflict with setting the parent. I'll take care of that.

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-K?nig            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |


From thierry.reding at gmail.com  Wed Aug 19 03:09:38 2015
From: thierry.reding at gmail.com (Thierry Reding)
Date: Wed, 19 Aug 2015 12:09:38 +0200
Subject: [PATCH V3] Watchdog: Fix parent of watchdog_devices
In-Reply-To: <db7db9102158b87a4a7169e84e2272b3ba03f9f7.1439954581.git.panand@redhat.com>
References: <db7db9102158b87a4a7169e84e2272b3ba03f9f7.1439954581.git.panand@redhat.com>
Message-ID: <20150819100937.GD26627@ulmo>

On Wed, Aug 19, 2015 at 08:58:24AM +0530, Pratyush Anand wrote:
[...]
> diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c
> index 30451ea46902..7f97cdd53f29 100644
> --- a/drivers/watchdog/tegra_wdt.c
> +++ b/drivers/watchdog/tegra_wdt.c
> @@ -218,6 +218,7 @@ static int tegra_wdt_probe(struct platform_device *pdev)
>  	wdd->ops = &tegra_wdt_ops;
>  	wdd->min_timeout = MIN_WDT_TIMEOUT;
>  	wdd->max_timeout = MAX_WDT_TIMEOUT;
> +	wdd->parent = &pdev->dev;
>  
>  	watchdog_set_drvdata(wdd, wdt);
>  

Acked-by: Thierry Reding <treding at nvidia.com>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150819/f971965d/attachment.sig>

From maxime.coquelin at st.com  Wed Aug 19 03:55:05 2015
From: maxime.coquelin at st.com (Maxime Coquelin)
Date: Wed, 19 Aug 2015 12:55:05 +0200
Subject: [PATCH V3] Watchdog: Fix parent of watchdog_devices
In-Reply-To: <db7db9102158b87a4a7169e84e2272b3ba03f9f7.1439954581.git.panand@redhat.com>
References: <db7db9102158b87a4a7169e84e2272b3ba03f9f7.1439954581.git.panand@redhat.com>
Message-ID: <55D46089.8030801@st.com>

Hi Pratyush,

On 08/19/2015 05:28 AM, Pratyush Anand wrote:
> diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c
> index 6785afdc0fca..14e9badf2bfa 100644
> --- a/drivers/watchdog/st_lpc_wdt.c
> +++ b/drivers/watchdog/st_lpc_wdt.c
> @@ -241,6 +241,7 @@ static int st_wdog_probe(struct platform_device *pdev)
>   		return -EINVAL;
>   	}
>   	st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
> +	st_wdog_dev.parent = &pdev->dev;
>   
>   	ret = clk_prepare_enable(clk);
>   	if (ret) {

Acked-by: Maxime Coquelin <maxime.coquelin at st.com>

Thanks!
Maxime


From linux at roeck-us.net  Wed Aug 19 08:38:17 2015
From: linux at roeck-us.net (Guenter Roeck)
Date: Wed, 19 Aug 2015 08:38:17 -0700
Subject: [PATCH V3] Watchdog: Fix parent of watchdog_devices
In-Reply-To: <db7db9102158b87a4a7169e84e2272b3ba03f9f7.1439954581.git.panand@redhat.com>
References: <db7db9102158b87a4a7169e84e2272b3ba03f9f7.1439954581.git.panand@redhat.com>
Message-ID: <20150819153817.GA27186@roeck-us.net>

On Wed, Aug 19, 2015 at 08:58:24AM +0530, Pratyush Anand wrote:
> /sys/class/watchdog/watchdogn/device/modalias can help to identify the
> driver/module for a given watchdog node. However, many wdt devices does not set
> its parent and so, we do not see an entry for device in sysfs for such devices.
> 
> This patch fixes parent of watchdog_device so that
> /sys/class/watchdog/watchdogn/device is populated.
> 
> Exceptions: booke, diag288, mpc8xxx, octeon, softdog and w83627hf -- They do not
> have any parent. Not sure, how we can we identify driver for these devices.
> 
If you happen to send out another revision, please reduce the number of columns
in the description to 75 or less to avoid the checkpatch warning.
Also, it would help if you can add all the Reviewed-by: and Acked-by: tags.

> Signed-off-by: Pratyush Anand <panand at redhat.com>

Passes my build and qemu tests, and the kbuild test robot is happy this time.

Acked-by: Guenter Roeck <linux at roeck-us.net>

Guenter

> ---
> Changes since v2:
> Fixed drivers/watchdog/txx9wdt.c:134:20: error: 'pdev' undeclared
> Reported-by: kbuild test robot <fengguang.wu at intel.com>
> Changes since v1:
> Squash all commits of V1 into a single patch.
> V1 was here:
> http://www.spinics.net/lists/linux-watchdog/msg07220.html
> 
>  drivers/misc/mei/wd.c               | 1 +
>  drivers/watchdog/bcm2835_wdt.c      | 1 +
>  drivers/watchdog/bcm47xx_wdt.c      | 1 +
>  drivers/watchdog/bcm_kona_wdt.c     | 1 +
>  drivers/watchdog/coh901327_wdt.c    | 1 +
>  drivers/watchdog/da9052_wdt.c       | 1 +
>  drivers/watchdog/da9055_wdt.c       | 1 +
>  drivers/watchdog/da9062_wdt.c       | 1 +
>  drivers/watchdog/da9063_wdt.c       | 1 +
>  drivers/watchdog/davinci_wdt.c      | 1 +
>  drivers/watchdog/digicolor_wdt.c    | 1 +
>  drivers/watchdog/ep93xx_wdt.c       | 1 +
>  drivers/watchdog/gpio_wdt.c         | 1 +
>  drivers/watchdog/ie6xx_wdt.c        | 1 +
>  drivers/watchdog/intel-mid_wdt.c    | 1 +
>  drivers/watchdog/jz4740_wdt.c       | 1 +
>  drivers/watchdog/mena21_wdt.c       | 1 +
>  drivers/watchdog/menf21bmc_wdt.c    | 1 +
>  drivers/watchdog/omap_wdt.c         | 1 +
>  drivers/watchdog/orion_wdt.c        | 1 +
>  drivers/watchdog/pnx4008_wdt.c      | 1 +
>  drivers/watchdog/qcom-wdt.c         | 1 +
>  drivers/watchdog/retu_wdt.c         | 1 +
>  drivers/watchdog/rt2880_wdt.c       | 1 +
>  drivers/watchdog/s3c2410_wdt.c      | 1 +
>  drivers/watchdog/shwdt.c            | 1 +
>  drivers/watchdog/sirfsoc_wdt.c      | 1 +
>  drivers/watchdog/sp805_wdt.c        | 1 +
>  drivers/watchdog/st_lpc_wdt.c       | 1 +
>  drivers/watchdog/stmp3xxx_rtc_wdt.c | 1 +
>  drivers/watchdog/tegra_wdt.c        | 1 +
>  drivers/watchdog/twl4030_wdt.c      | 1 +
>  drivers/watchdog/txx9wdt.c          | 1 +
>  drivers/watchdog/ux500_wdt.c        | 1 +
>  drivers/watchdog/via_wdt.c          | 1 +
>  drivers/watchdog/wm831x_wdt.c       | 1 +
>  drivers/watchdog/wm8350_wdt.c       | 1 +
>  37 files changed, 37 insertions(+)
> 
> diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c
> index 2bc0f5089f82..b346638833b0 100644
> --- a/drivers/misc/mei/wd.c
> +++ b/drivers/misc/mei/wd.c
> @@ -364,6 +364,7 @@ int mei_watchdog_register(struct mei_device *dev)
>  
>  	int ret;
>  
> +	amt_wd_dev.parent = dev->dev;
>  	/* unlock to perserve correct locking order */
>  	mutex_unlock(&dev->device_lock);
>  	ret = watchdog_register_device(&amt_wd_dev);
> diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c
> index 7116968dee12..66c3e656a616 100644
> --- a/drivers/watchdog/bcm2835_wdt.c
> +++ b/drivers/watchdog/bcm2835_wdt.c
> @@ -182,6 +182,7 @@ static int bcm2835_wdt_probe(struct platform_device *pdev)
>  	watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt);
>  	watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev);
>  	watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout);
> +	bcm2835_wdt_wdd.parent = &pdev->dev;
>  	err = watchdog_register_device(&bcm2835_wdt_wdd);
>  	if (err) {
>  		dev_err(dev, "Failed to register watchdog device");
> diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c
> index b28a072abf78..4064a43f1360 100644
> --- a/drivers/watchdog/bcm47xx_wdt.c
> +++ b/drivers/watchdog/bcm47xx_wdt.c
> @@ -209,6 +209,7 @@ static int bcm47xx_wdt_probe(struct platform_device *pdev)
>  
>  	wdt->wdd.info = &bcm47xx_wdt_info;
>  	wdt->wdd.timeout = WDT_DEFAULT_TIME;
> +	wdt->wdd.parent = &pdev->dev;
>  	ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout);
>  	if (ret)
>  		goto err_timer;
> diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c
> index 22d8ae65772a..e0c98423f2c9 100644
> --- a/drivers/watchdog/bcm_kona_wdt.c
> +++ b/drivers/watchdog/bcm_kona_wdt.c
> @@ -319,6 +319,7 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev)
>  	spin_lock_init(&wdt->lock);
>  	platform_set_drvdata(pdev, wdt);
>  	watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt);
> +	bcm_kona_wdt_wdd.parent = &pdev->dev;
>  
>  	ret = bcm_kona_wdt_set_timeout_reg(&bcm_kona_wdt_wdd, 0);
>  	if (ret) {
> diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c
> index ce12f437f195..a099b77fc0b9 100644
> --- a/drivers/watchdog/coh901327_wdt.c
> +++ b/drivers/watchdog/coh901327_wdt.c
> @@ -358,6 +358,7 @@ static int __init coh901327_probe(struct platform_device *pdev)
>  	if (ret < 0)
>  		coh901327_wdt.timeout = 60;
>  
> +	coh901327_wdt.parent = &pdev->dev;
>  	ret = watchdog_register_device(&coh901327_wdt);
>  	if (ret == 0)
>  		dev_info(&pdev->dev,
> diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c
> index 2e9589652e1e..67e67977bd29 100644
> --- a/drivers/watchdog/da9052_wdt.c
> +++ b/drivers/watchdog/da9052_wdt.c
> @@ -195,6 +195,7 @@ static int da9052_wdt_probe(struct platform_device *pdev)
>  	da9052_wdt->timeout = DA9052_DEF_TIMEOUT;
>  	da9052_wdt->info = &da9052_wdt_info;
>  	da9052_wdt->ops = &da9052_wdt_ops;
> +	da9052_wdt->parent = &pdev->dev;
>  	watchdog_set_drvdata(da9052_wdt, driver_data);
>  
>  	kref_init(&driver_data->kref);
> diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c
> index 495089d8dbfe..04d1430d93d2 100644
> --- a/drivers/watchdog/da9055_wdt.c
> +++ b/drivers/watchdog/da9055_wdt.c
> @@ -161,6 +161,7 @@ static int da9055_wdt_probe(struct platform_device *pdev)
>  	da9055_wdt->timeout = DA9055_DEF_TIMEOUT;
>  	da9055_wdt->info = &da9055_wdt_info;
>  	da9055_wdt->ops = &da9055_wdt_ops;
> +	da9055_wdt->parent = &pdev->dev;
>  	watchdog_set_nowayout(da9055_wdt, nowayout);
>  	watchdog_set_drvdata(da9055_wdt, driver_data);
>  
> diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c
> index b3a870ce85be..7386111220d5 100644
> --- a/drivers/watchdog/da9062_wdt.c
> +++ b/drivers/watchdog/da9062_wdt.c
> @@ -210,6 +210,7 @@ static int da9062_wdt_probe(struct platform_device *pdev)
>  	wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
>  	wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
>  	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
> +	wdt->wdtdev.parent = &pdev->dev;
>  
>  	watchdog_set_drvdata(&wdt->wdtdev, wdt);
>  	dev_set_drvdata(&pdev->dev, wdt);
> diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c
> index e2fe2ebdebd4..6bf130bd863d 100644
> --- a/drivers/watchdog/da9063_wdt.c
> +++ b/drivers/watchdog/da9063_wdt.c
> @@ -175,6 +175,7 @@ static int da9063_wdt_probe(struct platform_device *pdev)
>  	wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT;
>  	wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT;
>  	wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT;
> +	wdt->wdtdev.parent = &pdev->dev;
>  
>  	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
>  
> diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c
> index cfdf8a408aea..17454ca653f4 100644
> --- a/drivers/watchdog/davinci_wdt.c
> +++ b/drivers/watchdog/davinci_wdt.c
> @@ -179,6 +179,7 @@ static int davinci_wdt_probe(struct platform_device *pdev)
>  	wdd->min_timeout	= 1;
>  	wdd->max_timeout	= MAX_HEARTBEAT;
>  	wdd->timeout		= DEFAULT_HEARTBEAT;
> +	wdd->parent		= &pdev->dev;
>  
>  	watchdog_init_timeout(wdd, heartbeat, dev);
>  
> diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c
> index 31d8e4936611..50abe1bf62a5 100644
> --- a/drivers/watchdog/digicolor_wdt.c
> +++ b/drivers/watchdog/digicolor_wdt.c
> @@ -143,6 +143,7 @@ static int dc_wdt_probe(struct platform_device *pdev)
>  	}
>  	dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk);
>  	dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout;
> +	dc_wdt_wdd.parent = &pdev->dev;
>  
>  	spin_lock_init(&wdt->lock);
>  
> diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c
> index 7a2cc7191c58..0a4d7cc05d54 100644
> --- a/drivers/watchdog/ep93xx_wdt.c
> +++ b/drivers/watchdog/ep93xx_wdt.c
> @@ -132,6 +132,7 @@ static int ep93xx_wdt_probe(struct platform_device *pdev)
>  	val = readl(mmio_base + EP93XX_WATCHDOG);
>  	ep93xx_wdt_wdd.bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0;
>  	ep93xx_wdt_wdd.timeout = timeout;
> +	ep93xx_wdt_wdd.parent = &pdev->dev;
>  
>  	watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout);
>  
> diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c
> index 1687cc2d7122..336bddd84809 100644
> --- a/drivers/watchdog/gpio_wdt.c
> +++ b/drivers/watchdog/gpio_wdt.c
> @@ -217,6 +217,7 @@ static int gpio_wdt_probe(struct platform_device *pdev)
>  	priv->wdd.ops		= &gpio_wdt_ops;
>  	priv->wdd.min_timeout	= SOFT_TIMEOUT_MIN;
>  	priv->wdd.max_timeout	= SOFT_TIMEOUT_MAX;
> +	priv->wdd.parent	= &pdev->dev;
>  
>  	if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0)
>  		priv->wdd.timeout = SOFT_TIMEOUT_DEF;
> diff --git a/drivers/watchdog/ie6xx_wdt.c b/drivers/watchdog/ie6xx_wdt.c
> index 9bc39ae51624..78c2541f5d52 100644
> --- a/drivers/watchdog/ie6xx_wdt.c
> +++ b/drivers/watchdog/ie6xx_wdt.c
> @@ -267,6 +267,7 @@ static int ie6xx_wdt_probe(struct platform_device *pdev)
>  
>  	ie6xx_wdt_dev.timeout = timeout;
>  	watchdog_set_nowayout(&ie6xx_wdt_dev, nowayout);
> +	ie6xx_wdt_dev.parent = &pdev->dev;
>  
>  	spin_lock_init(&ie6xx_wdt_data.unlock_sequence);
>  
> diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c
> index 84f6701c391f..0a436b5d1e84 100644
> --- a/drivers/watchdog/intel-mid_wdt.c
> +++ b/drivers/watchdog/intel-mid_wdt.c
> @@ -137,6 +137,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
>  	wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
>  	wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX;
>  	wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT;
> +	wdt_dev->parent = &pdev->dev;
>  
>  	watchdog_set_drvdata(wdt_dev, &pdev->dev);
>  	platform_set_drvdata(pdev, wdt_dev);
> diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c
> index 4c2cc09c0c57..6a7d5c365438 100644
> --- a/drivers/watchdog/jz4740_wdt.c
> +++ b/drivers/watchdog/jz4740_wdt.c
> @@ -174,6 +174,7 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
>  	jz4740_wdt->timeout = heartbeat;
>  	jz4740_wdt->min_timeout = 1;
>  	jz4740_wdt->max_timeout = MAX_HEARTBEAT;
> +	jz4740_wdt->parent = &pdev->dev;
>  	watchdog_set_nowayout(jz4740_wdt, nowayout);
>  	watchdog_set_drvdata(jz4740_wdt, drvdata);
>  
> diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c
> index d193a5e79c38..69013007dc47 100644
> --- a/drivers/watchdog/mena21_wdt.c
> +++ b/drivers/watchdog/mena21_wdt.c
> @@ -197,6 +197,7 @@ static int a21_wdt_probe(struct platform_device *pdev)
>  	watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
>  	watchdog_set_nowayout(&a21_wdt, nowayout);
>  	watchdog_set_drvdata(&a21_wdt, drv);
> +	a21_wdt.parent = &pdev->dev;
>  
>  	reset = a21_wdt_get_bootstatus(drv);
>  	if (reset == 2)
> diff --git a/drivers/watchdog/menf21bmc_wdt.c b/drivers/watchdog/menf21bmc_wdt.c
> index 59f0913c7341..3aefddebb386 100644
> --- a/drivers/watchdog/menf21bmc_wdt.c
> +++ b/drivers/watchdog/menf21bmc_wdt.c
> @@ -130,6 +130,7 @@ static int menf21bmc_wdt_probe(struct platform_device *pdev)
>  	drv_data->wdt.info = &menf21bmc_wdt_info;
>  	drv_data->wdt.min_timeout = BMC_WD_TIMEOUT_MIN;
>  	drv_data->wdt.max_timeout = BMC_WD_TIMEOUT_MAX;
> +	drv_data->wdt.parent = &pdev->dev;
>  	drv_data->i2c_client = i2c_client;
>  
>  	/*
> diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
> index de911c7e477c..d96bee017fd3 100644
> --- a/drivers/watchdog/omap_wdt.c
> +++ b/drivers/watchdog/omap_wdt.c
> @@ -253,6 +253,7 @@ static int omap_wdt_probe(struct platform_device *pdev)
>  	wdev->wdog.ops = &omap_wdt_ops;
>  	wdev->wdog.min_timeout = TIMER_MARGIN_MIN;
>  	wdev->wdog.max_timeout = TIMER_MARGIN_MAX;
> +	wdev->wdog.parent = &pdev->dev;
>  
>  	if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0)
>  		wdev->wdog.timeout = TIMER_MARGIN_DEFAULT;
> diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
> index ef0c628d5037..c6b8f4a43bde 100644
> --- a/drivers/watchdog/orion_wdt.c
> +++ b/drivers/watchdog/orion_wdt.c
> @@ -567,6 +567,7 @@ static int orion_wdt_probe(struct platform_device *pdev)
>  
>  	dev->wdt.timeout = wdt_max_duration;
>  	dev->wdt.max_timeout = wdt_max_duration;
> +	dev->wdt.parent = &pdev->dev;
>  	watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev);
>  
>  	platform_set_drvdata(pdev, &dev->wdt);
> diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c
> index b9c6049c3e78..4224b3ec83a5 100644
> --- a/drivers/watchdog/pnx4008_wdt.c
> +++ b/drivers/watchdog/pnx4008_wdt.c
> @@ -167,6 +167,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
>  
>  	pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
>  			WDIOF_CARDRESET : 0;
> +	pnx4008_wdd.parent = &pdev->dev;
>  	watchdog_set_nowayout(&pnx4008_wdd, nowayout);
>  
>  	pnx4008_wdt_stop(&pnx4008_wdd);	/* disable for now */
> diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c
> index aa03ca8f2d9b..773dcfaee7b2 100644
> --- a/drivers/watchdog/qcom-wdt.c
> +++ b/drivers/watchdog/qcom-wdt.c
> @@ -171,6 +171,7 @@ static int qcom_wdt_probe(struct platform_device *pdev)
>  	wdt->wdd.ops = &qcom_wdt_ops;
>  	wdt->wdd.min_timeout = 1;
>  	wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
> +	wdt->wdd.parent = &pdev->dev;
>  
>  	/*
>  	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
> diff --git a/drivers/watchdog/retu_wdt.c b/drivers/watchdog/retu_wdt.c
> index b7c68e275aeb..39cd51df2ffc 100644
> --- a/drivers/watchdog/retu_wdt.c
> +++ b/drivers/watchdog/retu_wdt.c
> @@ -127,6 +127,7 @@ static int retu_wdt_probe(struct platform_device *pdev)
>  	retu_wdt->timeout	= RETU_WDT_MAX_TIMER;
>  	retu_wdt->min_timeout	= 0;
>  	retu_wdt->max_timeout	= RETU_WDT_MAX_TIMER;
> +	retu_wdt->parent	= &pdev->dev;
>  
>  	watchdog_set_drvdata(retu_wdt, wdev);
>  	watchdog_set_nowayout(retu_wdt, nowayout);
> diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c
> index a6f7e2e29beb..1967919ae743 100644
> --- a/drivers/watchdog/rt2880_wdt.c
> +++ b/drivers/watchdog/rt2880_wdt.c
> @@ -161,6 +161,7 @@ static int rt288x_wdt_probe(struct platform_device *pdev)
>  	rt288x_wdt_dev.dev = &pdev->dev;
>  	rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
>  	rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
> +	rt288x_wdt_dev.parent = &pdev->dev;
>  
>  	watchdog_init_timeout(&rt288x_wdt_dev, rt288x_wdt_dev.max_timeout,
>  			      &pdev->dev);
> diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
> index e89ae027c91d..d781000c7825 100644
> --- a/drivers/watchdog/s3c2410_wdt.c
> +++ b/drivers/watchdog/s3c2410_wdt.c
> @@ -607,6 +607,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
>  	watchdog_set_nowayout(&wdt->wdt_device, nowayout);
>  
>  	wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
> +	wdt->wdt_device.parent = &pdev->dev;
>  
>  	ret = watchdog_register_device(&wdt->wdt_device);
>  	if (ret) {
> diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c
> index 567458b137a6..f90812170657 100644
> --- a/drivers/watchdog/shwdt.c
> +++ b/drivers/watchdog/shwdt.c
> @@ -252,6 +252,7 @@ static int sh_wdt_probe(struct platform_device *pdev)
>  
>  	watchdog_set_nowayout(&sh_wdt_dev, nowayout);
>  	watchdog_set_drvdata(&sh_wdt_dev, wdt);
> +	sh_wdt_dev.parent = &pdev->dev;
>  
>  	spin_lock_init(&wdt->lock);
>  
> diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c
> index 42fa5c0c518a..d0578ab2e636 100644
> --- a/drivers/watchdog/sirfsoc_wdt.c
> +++ b/drivers/watchdog/sirfsoc_wdt.c
> @@ -154,6 +154,7 @@ static int sirfsoc_wdt_probe(struct platform_device *pdev)
>  
>  	watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev);
>  	watchdog_set_nowayout(&sirfsoc_wdd, nowayout);
> +	sirfsoc_wdd.parent = &pdev->dev;
>  
>  	ret = watchdog_register_device(&sirfsoc_wdd);
>  	if (ret)
> diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
> index 4e7fec36f5c3..01d816251302 100644
> --- a/drivers/watchdog/sp805_wdt.c
> +++ b/drivers/watchdog/sp805_wdt.c
> @@ -226,6 +226,7 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
>  	wdt->adev = adev;
>  	wdt->wdd.info = &wdt_info;
>  	wdt->wdd.ops = &wdt_ops;
> +	wdt->wdd.parent = &adev->dev;
>  
>  	spin_lock_init(&wdt->lock);
>  	watchdog_set_nowayout(&wdt->wdd, nowayout);
> diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c
> index 6785afdc0fca..14e9badf2bfa 100644
> --- a/drivers/watchdog/st_lpc_wdt.c
> +++ b/drivers/watchdog/st_lpc_wdt.c
> @@ -241,6 +241,7 @@ static int st_wdog_probe(struct platform_device *pdev)
>  		return -EINVAL;
>  	}
>  	st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
> +	st_wdog_dev.parent = &pdev->dev;
>  
>  	ret = clk_prepare_enable(clk);
>  	if (ret) {
> diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c b/drivers/watchdog/stmp3xxx_rtc_wdt.c
> index e7f0d5b60d3d..3ee6128a540e 100644
> --- a/drivers/watchdog/stmp3xxx_rtc_wdt.c
> +++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c
> @@ -76,6 +76,7 @@ static int stmp3xxx_wdt_probe(struct platform_device *pdev)
>  	watchdog_set_drvdata(&stmp3xxx_wdd, &pdev->dev);
>  
>  	stmp3xxx_wdd.timeout = clamp_t(unsigned, heartbeat, 1, STMP3XXX_MAX_TIMEOUT);
> +	stmp3xxx_wdd.parent = &pdev->dev;
>  
>  	ret = watchdog_register_device(&stmp3xxx_wdd);
>  	if (ret < 0) {
> diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c
> index 30451ea46902..7f97cdd53f29 100644
> --- a/drivers/watchdog/tegra_wdt.c
> +++ b/drivers/watchdog/tegra_wdt.c
> @@ -218,6 +218,7 @@ static int tegra_wdt_probe(struct platform_device *pdev)
>  	wdd->ops = &tegra_wdt_ops;
>  	wdd->min_timeout = MIN_WDT_TIMEOUT;
>  	wdd->max_timeout = MAX_WDT_TIMEOUT;
> +	wdd->parent = &pdev->dev;
>  
>  	watchdog_set_drvdata(wdd, wdt);
>  
> diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c
> index 2c1db6fa9a27..9bf3cc0f3961 100644
> --- a/drivers/watchdog/twl4030_wdt.c
> +++ b/drivers/watchdog/twl4030_wdt.c
> @@ -83,6 +83,7 @@ static int twl4030_wdt_probe(struct platform_device *pdev)
>  	wdt->timeout		= 30;
>  	wdt->min_timeout	= 1;
>  	wdt->max_timeout	= 30;
> +	wdt->parent = &pdev->dev;
>  
>  	watchdog_set_nowayout(wdt, nowayout);
>  	platform_set_drvdata(pdev, wdt);
> diff --git a/drivers/watchdog/txx9wdt.c b/drivers/watchdog/txx9wdt.c
> index 7f615933d31a..c2da880292bc 100644
> --- a/drivers/watchdog/txx9wdt.c
> +++ b/drivers/watchdog/txx9wdt.c
> @@ -131,6 +131,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
>  	txx9wdt.timeout = timeout;
>  	txx9wdt.min_timeout = 1;
>  	txx9wdt.max_timeout = WD_MAX_TIMEOUT;
> +	txx9wdt.parent = &dev->dev;
>  	watchdog_set_nowayout(&txx9wdt, nowayout);
>  
>  	ret = watchdog_register_device(&txx9wdt);
> diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c
> index 9de09ab00838..37c084353cce 100644
> --- a/drivers/watchdog/ux500_wdt.c
> +++ b/drivers/watchdog/ux500_wdt.c
> @@ -96,6 +96,7 @@ static int ux500_wdt_probe(struct platform_device *pdev)
>  			ux500_wdt.max_timeout = WATCHDOG_MAX28;
>  	}
>  
> +	ux500_wdt.parent = &pdev->dev;
>  	watchdog_set_nowayout(&ux500_wdt, nowayout);
>  
>  	/* disable auto off on sleep */
> diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c
> index 56369c4f1961..5f9cbc37520d 100644
> --- a/drivers/watchdog/via_wdt.c
> +++ b/drivers/watchdog/via_wdt.c
> @@ -206,6 +206,7 @@ static int wdt_probe(struct pci_dev *pdev,
>  		timeout = WDT_TIMEOUT;
>  
>  	wdt_dev.timeout = timeout;
> +	wdt_dev.parent = &pdev->dev;
>  	watchdog_set_nowayout(&wdt_dev, nowayout);
>  	if (readl(wdt_mem) & VIA_WDT_FIRED)
>  		wdt_dev.bootstatus |= WDIOF_CARDRESET;
> diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c
> index 2fa17e746ff6..8d1184aee932 100644
> --- a/drivers/watchdog/wm831x_wdt.c
> +++ b/drivers/watchdog/wm831x_wdt.c
> @@ -215,6 +215,7 @@ static int wm831x_wdt_probe(struct platform_device *pdev)
>  
>  	wm831x_wdt->info = &wm831x_wdt_info;
>  	wm831x_wdt->ops = &wm831x_wdt_ops;
> +	wm831x_wdt->parent = &pdev->dev;
>  	watchdog_set_nowayout(wm831x_wdt, nowayout);
>  	watchdog_set_drvdata(wm831x_wdt, driver_data);
>  
> diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c
> index 34d272ada23d..4ab4b8347d45 100644
> --- a/drivers/watchdog/wm8350_wdt.c
> +++ b/drivers/watchdog/wm8350_wdt.c
> @@ -151,6 +151,7 @@ static int wm8350_wdt_probe(struct platform_device *pdev)
>  
>  	watchdog_set_nowayout(&wm8350_wdt, nowayout);
>  	watchdog_set_drvdata(&wm8350_wdt, wm8350);
> +	wm8350_wdt.parent = &pdev->dev;
>  
>  	/* Default to 4s timeout */
>  	wm8350_wdt_set_timeout(&wm8350_wdt, 4);
> -- 
> 2.4.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


From Adam.Thomson.Opensource at diasemi.com  Thu Aug 20 01:30:35 2015
From: Adam.Thomson.Opensource at diasemi.com (Opensource [Adam Thomson])
Date: Thu, 20 Aug 2015 08:30:35 +0000
Subject: [PATCH V3] Watchdog: Fix parent of watchdog_devices
In-Reply-To: <db7db9102158b87a4a7169e84e2272b3ba03f9f7.1439954581.git.panand@redhat.com>
References: <db7db9102158b87a4a7169e84e2272b3ba03f9f7.1439954581.git.panand@redhat.com>
Message-ID: <2E89032DDAA8B9408CB92943514A0337AB56C694@SW-EX-MBX01.diasemi.com>

On August 19, 2015 04:28, Pratyush Anand wrote:

> diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c
> index 2e9589652e1e..67e67977bd29 100644
> --- a/drivers/watchdog/da9052_wdt.c
> +++ b/drivers/watchdog/da9052_wdt.c
> @@ -195,6 +195,7 @@ static int da9052_wdt_probe(struct platform_device *pdev)
>  	da9052_wdt->timeout = DA9052_DEF_TIMEOUT;
>  	da9052_wdt->info = &da9052_wdt_info;
>  	da9052_wdt->ops = &da9052_wdt_ops;
> +	da9052_wdt->parent = &pdev->dev;
>  	watchdog_set_drvdata(da9052_wdt, driver_data);
> 
>  	kref_init(&driver_data->kref);
> diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c
> index 495089d8dbfe..04d1430d93d2 100644
> --- a/drivers/watchdog/da9055_wdt.c
> +++ b/drivers/watchdog/da9055_wdt.c
> @@ -161,6 +161,7 @@ static int da9055_wdt_probe(struct platform_device *pdev)
>  	da9055_wdt->timeout = DA9055_DEF_TIMEOUT;
>  	da9055_wdt->info = &da9055_wdt_info;
>  	da9055_wdt->ops = &da9055_wdt_ops;
> +	da9055_wdt->parent = &pdev->dev;
>  	watchdog_set_nowayout(da9055_wdt, nowayout);
>  	watchdog_set_drvdata(da9055_wdt, driver_data);
> 
> diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c
> index b3a870ce85be..7386111220d5 100644
> --- a/drivers/watchdog/da9062_wdt.c
> +++ b/drivers/watchdog/da9062_wdt.c
> @@ -210,6 +210,7 @@ static int da9062_wdt_probe(struct platform_device *pdev)
>  	wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
>  	wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
>  	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
> +	wdt->wdtdev.parent = &pdev->dev;
> 
>  	watchdog_set_drvdata(&wdt->wdtdev, wdt);
>  	dev_set_drvdata(&pdev->dev, wdt);
> diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c
> index e2fe2ebdebd4..6bf130bd863d 100644
> --- a/drivers/watchdog/da9063_wdt.c
> +++ b/drivers/watchdog/da9063_wdt.c
> @@ -175,6 +175,7 @@ static int da9063_wdt_probe(struct platform_device *pdev)
>  	wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT;
>  	wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT;
>  	wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT;
> +	wdt->wdtdev.parent = &pdev->dev;
> 
>  	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
> 

Acked-by: Adam Thomson <Adam.Thomson.Opensource at diasemi.com>

From tomas.winkler at intel.com  Thu Aug 20 01:35:22 2015
From: tomas.winkler at intel.com (Winkler, Tomas)
Date: Thu, 20 Aug 2015 08:35:22 +0000
Subject: [PATCH V3] Watchdog: Fix parent of watchdog_devices
In-Reply-To: <db7db9102158b87a4a7169e84e2272b3ba03f9f7.1439954581.git.panand@redhat.com>
References: <db7db9102158b87a4a7169e84e2272b3ba03f9f7.1439954581.git.panand@redhat.com>
Message-ID: <5B8DA87D05A7694D9FA63FD143655C1B3D42A60A@hasmsx108.ger.corp.intel.com>

> diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c
> index 2bc0f5089f82..b346638833b0 100644
> --- a/drivers/misc/mei/wd.c
> +++ b/drivers/misc/mei/wd.c
> @@ -364,6 +364,7 @@ int mei_watchdog_register(struct mei_device *dev)
> 
>  	int ret;
> 
> +	amt_wd_dev.parent = dev->dev;
>  	/* unlock to perserve correct locking order */
>  	mutex_unlock(&dev->device_lock);
>  	ret = watchdog_register_device(&amt_wd_dev);

Acked-by: Tomas Winkler <tomas.winkler at intel.com>

Should this be got to stable as well?



From panand at redhat.com  Thu Aug 20 01:35:01 2015
From: panand at redhat.com (Pratyush Anand)
Date: Thu, 20 Aug 2015 14:05:01 +0530
Subject: [PATCH V4] Watchdog: Fix parent of watchdog_devices
Message-ID: <d035c08984bb06bd81e49fa587fab1da44533bcc.1440059435.git.panand@redhat.com>

/sys/class/watchdog/watchdogn/device/modalias can help to identify the
driver/module for a given watchdog node. However, many wdt devices do not
set their parent and so, we do not see an entry for device in sysfs for
such devices.

This patch fixes parent of watchdog_device so that
/sys/class/watchdog/watchdogn/device is populated.

Exceptions: booke, diag288, octeon, softdog and w83627hf -- They do not
have any parent. Not sure, how we can identify driver for these devices.

Signed-off-by: Pratyush Anand <panand at redhat.com>
Reviewed-by: Johannes Thumshirn <jthumshirn at suse.de>
Acked-by: Guenter Roeck <linux at roeck-us.net>
Acked-by: H Hartley Sweeten <hsweeten at visionengravers.com>
Acked-by: Lee Jones <lee.jones at linaro.org>
Acked-by: Lubomir Rintel <lkundrak at v3.sk>
Acked-by: Maxime Coquelin <maxime.coquelin at st.com>
Acked-by: Thierry Reding <treding at nvidia.com>
Acked-by: Viresh Kumar <viresh.kumar at linaro.org>
---
* Changes since v3:
- Fixed max no number of columns in the description to 75 
- Fixed grammatical mistake in patch description reported by Uwe
Kleine-K?nig.
- Fixed for mpc8xxx as well.
* Changes since v2:
- Fixed drivers/watchdog/txx9wdt.c:134:20: error: 'pdev' undeclared
Reported-by: kbuild test robot <fengguang.wu at intel.com>
* Changes since v1:
- Squash all commits of V1 into a single patch.
- V1 was here:
	http://www.spinics.net/lists/linux-watchdog/msg07220.html

 drivers/misc/mei/wd.c               | 1 +
 drivers/watchdog/bcm2835_wdt.c      | 1 +
 drivers/watchdog/bcm47xx_wdt.c      | 1 +
 drivers/watchdog/bcm_kona_wdt.c     | 1 +
 drivers/watchdog/coh901327_wdt.c    | 1 +
 drivers/watchdog/da9052_wdt.c       | 1 +
 drivers/watchdog/da9055_wdt.c       | 1 +
 drivers/watchdog/da9062_wdt.c       | 1 +
 drivers/watchdog/da9063_wdt.c       | 1 +
 drivers/watchdog/davinci_wdt.c      | 1 +
 drivers/watchdog/digicolor_wdt.c    | 1 +
 drivers/watchdog/ep93xx_wdt.c       | 1 +
 drivers/watchdog/gpio_wdt.c         | 1 +
 drivers/watchdog/ie6xx_wdt.c        | 1 +
 drivers/watchdog/intel-mid_wdt.c    | 1 +
 drivers/watchdog/jz4740_wdt.c       | 1 +
 drivers/watchdog/mena21_wdt.c       | 1 +
 drivers/watchdog/menf21bmc_wdt.c    | 1 +
 drivers/watchdog/mpc8xxx_wdt.c      | 1 +
 drivers/watchdog/omap_wdt.c         | 1 +
 drivers/watchdog/orion_wdt.c        | 1 +
 drivers/watchdog/pnx4008_wdt.c      | 1 +
 drivers/watchdog/qcom-wdt.c         | 1 +
 drivers/watchdog/retu_wdt.c         | 1 +
 drivers/watchdog/rt2880_wdt.c       | 1 +
 drivers/watchdog/s3c2410_wdt.c      | 1 +
 drivers/watchdog/shwdt.c            | 1 +
 drivers/watchdog/sirfsoc_wdt.c      | 1 +
 drivers/watchdog/sp805_wdt.c        | 1 +
 drivers/watchdog/st_lpc_wdt.c       | 1 +
 drivers/watchdog/stmp3xxx_rtc_wdt.c | 1 +
 drivers/watchdog/tegra_wdt.c        | 1 +
 drivers/watchdog/twl4030_wdt.c      | 1 +
 drivers/watchdog/txx9wdt.c          | 1 +
 drivers/watchdog/ux500_wdt.c        | 1 +
 drivers/watchdog/via_wdt.c          | 1 +
 drivers/watchdog/wm831x_wdt.c       | 1 +
 drivers/watchdog/wm8350_wdt.c       | 1 +
 38 files changed, 38 insertions(+)

diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c
index 2bc0f5089f82..b346638833b0 100644
--- a/drivers/misc/mei/wd.c
+++ b/drivers/misc/mei/wd.c
@@ -364,6 +364,7 @@ int mei_watchdog_register(struct mei_device *dev)
 
 	int ret;
 
+	amt_wd_dev.parent = dev->dev;
 	/* unlock to perserve correct locking order */
 	mutex_unlock(&dev->device_lock);
 	ret = watchdog_register_device(&amt_wd_dev);
diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c
index 7116968dee12..66c3e656a616 100644
--- a/drivers/watchdog/bcm2835_wdt.c
+++ b/drivers/watchdog/bcm2835_wdt.c
@@ -182,6 +182,7 @@ static int bcm2835_wdt_probe(struct platform_device *pdev)
 	watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt);
 	watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev);
 	watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout);
+	bcm2835_wdt_wdd.parent = &pdev->dev;
 	err = watchdog_register_device(&bcm2835_wdt_wdd);
 	if (err) {
 		dev_err(dev, "Failed to register watchdog device");
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c
index b28a072abf78..4064a43f1360 100644
--- a/drivers/watchdog/bcm47xx_wdt.c
+++ b/drivers/watchdog/bcm47xx_wdt.c
@@ -209,6 +209,7 @@ static int bcm47xx_wdt_probe(struct platform_device *pdev)
 
 	wdt->wdd.info = &bcm47xx_wdt_info;
 	wdt->wdd.timeout = WDT_DEFAULT_TIME;
+	wdt->wdd.parent = &pdev->dev;
 	ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout);
 	if (ret)
 		goto err_timer;
diff --git a/drivers/watchdog/bcm_kona_wdt.c b/drivers/watchdog/bcm_kona_wdt.c
index 22d8ae65772a..e0c98423f2c9 100644
--- a/drivers/watchdog/bcm_kona_wdt.c
+++ b/drivers/watchdog/bcm_kona_wdt.c
@@ -319,6 +319,7 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev)
 	spin_lock_init(&wdt->lock);
 	platform_set_drvdata(pdev, wdt);
 	watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt);
+	bcm_kona_wdt_wdd.parent = &pdev->dev;
 
 	ret = bcm_kona_wdt_set_timeout_reg(&bcm_kona_wdt_wdd, 0);
 	if (ret) {
diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c
index ce12f437f195..a099b77fc0b9 100644
--- a/drivers/watchdog/coh901327_wdt.c
+++ b/drivers/watchdog/coh901327_wdt.c
@@ -358,6 +358,7 @@ static int __init coh901327_probe(struct platform_device *pdev)
 	if (ret < 0)
 		coh901327_wdt.timeout = 60;
 
+	coh901327_wdt.parent = &pdev->dev;
 	ret = watchdog_register_device(&coh901327_wdt);
 	if (ret == 0)
 		dev_info(&pdev->dev,
diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c
index 2e9589652e1e..67e67977bd29 100644
--- a/drivers/watchdog/da9052_wdt.c
+++ b/drivers/watchdog/da9052_wdt.c
@@ -195,6 +195,7 @@ static int da9052_wdt_probe(struct platform_device *pdev)
 	da9052_wdt->timeout = DA9052_DEF_TIMEOUT;
 	da9052_wdt->info = &da9052_wdt_info;
 	da9052_wdt->ops = &da9052_wdt_ops;
+	da9052_wdt->parent = &pdev->dev;
 	watchdog_set_drvdata(da9052_wdt, driver_data);
 
 	kref_init(&driver_data->kref);
diff --git a/drivers/watchdog/da9055_wdt.c b/drivers/watchdog/da9055_wdt.c
index 495089d8dbfe..04d1430d93d2 100644
--- a/drivers/watchdog/da9055_wdt.c
+++ b/drivers/watchdog/da9055_wdt.c
@@ -161,6 +161,7 @@ static int da9055_wdt_probe(struct platform_device *pdev)
 	da9055_wdt->timeout = DA9055_DEF_TIMEOUT;
 	da9055_wdt->info = &da9055_wdt_info;
 	da9055_wdt->ops = &da9055_wdt_ops;
+	da9055_wdt->parent = &pdev->dev;
 	watchdog_set_nowayout(da9055_wdt, nowayout);
 	watchdog_set_drvdata(da9055_wdt, driver_data);
 
diff --git a/drivers/watchdog/da9062_wdt.c b/drivers/watchdog/da9062_wdt.c
index b3a870ce85be..7386111220d5 100644
--- a/drivers/watchdog/da9062_wdt.c
+++ b/drivers/watchdog/da9062_wdt.c
@@ -210,6 +210,7 @@ static int da9062_wdt_probe(struct platform_device *pdev)
 	wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
 	wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
 	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
+	wdt->wdtdev.parent = &pdev->dev;
 
 	watchdog_set_drvdata(&wdt->wdtdev, wdt);
 	dev_set_drvdata(&pdev->dev, wdt);
diff --git a/drivers/watchdog/da9063_wdt.c b/drivers/watchdog/da9063_wdt.c
index e2fe2ebdebd4..6bf130bd863d 100644
--- a/drivers/watchdog/da9063_wdt.c
+++ b/drivers/watchdog/da9063_wdt.c
@@ -175,6 +175,7 @@ static int da9063_wdt_probe(struct platform_device *pdev)
 	wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT;
 	wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT;
 	wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT;
+	wdt->wdtdev.parent = &pdev->dev;
 
 	wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
 
diff --git a/drivers/watchdog/davinci_wdt.c b/drivers/watchdog/davinci_wdt.c
index cfdf8a408aea..17454ca653f4 100644
--- a/drivers/watchdog/davinci_wdt.c
+++ b/drivers/watchdog/davinci_wdt.c
@@ -179,6 +179,7 @@ static int davinci_wdt_probe(struct platform_device *pdev)
 	wdd->min_timeout	= 1;
 	wdd->max_timeout	= MAX_HEARTBEAT;
 	wdd->timeout		= DEFAULT_HEARTBEAT;
+	wdd->parent		= &pdev->dev;
 
 	watchdog_init_timeout(wdd, heartbeat, dev);
 
diff --git a/drivers/watchdog/digicolor_wdt.c b/drivers/watchdog/digicolor_wdt.c
index 31d8e4936611..50abe1bf62a5 100644
--- a/drivers/watchdog/digicolor_wdt.c
+++ b/drivers/watchdog/digicolor_wdt.c
@@ -143,6 +143,7 @@ static int dc_wdt_probe(struct platform_device *pdev)
 	}
 	dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk);
 	dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout;
+	dc_wdt_wdd.parent = &pdev->dev;
 
 	spin_lock_init(&wdt->lock);
 
diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c
index 7a2cc7191c58..0a4d7cc05d54 100644
--- a/drivers/watchdog/ep93xx_wdt.c
+++ b/drivers/watchdog/ep93xx_wdt.c
@@ -132,6 +132,7 @@ static int ep93xx_wdt_probe(struct platform_device *pdev)
 	val = readl(mmio_base + EP93XX_WATCHDOG);
 	ep93xx_wdt_wdd.bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0;
 	ep93xx_wdt_wdd.timeout = timeout;
+	ep93xx_wdt_wdd.parent = &pdev->dev;
 
 	watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout);
 
diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c
index 1687cc2d7122..336bddd84809 100644
--- a/drivers/watchdog/gpio_wdt.c
+++ b/drivers/watchdog/gpio_wdt.c
@@ -217,6 +217,7 @@ static int gpio_wdt_probe(struct platform_device *pdev)
 	priv->wdd.ops		= &gpio_wdt_ops;
 	priv->wdd.min_timeout	= SOFT_TIMEOUT_MIN;
 	priv->wdd.max_timeout	= SOFT_TIMEOUT_MAX;
+	priv->wdd.parent	= &pdev->dev;
 
 	if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0)
 		priv->wdd.timeout = SOFT_TIMEOUT_DEF;
diff --git a/drivers/watchdog/ie6xx_wdt.c b/drivers/watchdog/ie6xx_wdt.c
index 9bc39ae51624..78c2541f5d52 100644
--- a/drivers/watchdog/ie6xx_wdt.c
+++ b/drivers/watchdog/ie6xx_wdt.c
@@ -267,6 +267,7 @@ static int ie6xx_wdt_probe(struct platform_device *pdev)
 
 	ie6xx_wdt_dev.timeout = timeout;
 	watchdog_set_nowayout(&ie6xx_wdt_dev, nowayout);
+	ie6xx_wdt_dev.parent = &pdev->dev;
 
 	spin_lock_init(&ie6xx_wdt_data.unlock_sequence);
 
diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c
index 84f6701c391f..0a436b5d1e84 100644
--- a/drivers/watchdog/intel-mid_wdt.c
+++ b/drivers/watchdog/intel-mid_wdt.c
@@ -137,6 +137,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
 	wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
 	wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX;
 	wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT;
+	wdt_dev->parent = &pdev->dev;
 
 	watchdog_set_drvdata(wdt_dev, &pdev->dev);
 	platform_set_drvdata(pdev, wdt_dev);
diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c
index 4c2cc09c0c57..6a7d5c365438 100644
--- a/drivers/watchdog/jz4740_wdt.c
+++ b/drivers/watchdog/jz4740_wdt.c
@@ -174,6 +174,7 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
 	jz4740_wdt->timeout = heartbeat;
 	jz4740_wdt->min_timeout = 1;
 	jz4740_wdt->max_timeout = MAX_HEARTBEAT;
+	jz4740_wdt->parent = &pdev->dev;
 	watchdog_set_nowayout(jz4740_wdt, nowayout);
 	watchdog_set_drvdata(jz4740_wdt, drvdata);
 
diff --git a/drivers/watchdog/mena21_wdt.c b/drivers/watchdog/mena21_wdt.c
index d193a5e79c38..69013007dc47 100644
--- a/drivers/watchdog/mena21_wdt.c
+++ b/drivers/watchdog/mena21_wdt.c
@@ -197,6 +197,7 @@ static int a21_wdt_probe(struct platform_device *pdev)
 	watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
 	watchdog_set_nowayout(&a21_wdt, nowayout);
 	watchdog_set_drvdata(&a21_wdt, drv);
+	a21_wdt.parent = &pdev->dev;
 
 	reset = a21_wdt_get_bootstatus(drv);
 	if (reset == 2)
diff --git a/drivers/watchdog/menf21bmc_wdt.c b/drivers/watchdog/menf21bmc_wdt.c
index 59f0913c7341..3aefddebb386 100644
--- a/drivers/watchdog/menf21bmc_wdt.c
+++ b/drivers/watchdog/menf21bmc_wdt.c
@@ -130,6 +130,7 @@ static int menf21bmc_wdt_probe(struct platform_device *pdev)
 	drv_data->wdt.info = &menf21bmc_wdt_info;
 	drv_data->wdt.min_timeout = BMC_WD_TIMEOUT_MIN;
 	drv_data->wdt.max_timeout = BMC_WD_TIMEOUT_MAX;
+	drv_data->wdt.parent = &pdev->dev;
 	drv_data->i2c_client = i2c_client;
 
 	/*
diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c
index 689381a24887..21eda27686c5 100644
--- a/drivers/watchdog/mpc8xxx_wdt.c
+++ b/drivers/watchdog/mpc8xxx_wdt.c
@@ -185,6 +185,7 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
 		timeout_sec = timeout / freq;
 
 	mpc8xxx_wdt_dev.timeout = timeout_sec;
+	mpc8xxx_wdt_dev.parent = &ofdev->dev;
 #ifdef MODULE
 	ret = mpc8xxx_wdt_init_late();
 	if (ret)
diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c
index de911c7e477c..d96bee017fd3 100644
--- a/drivers/watchdog/omap_wdt.c
+++ b/drivers/watchdog/omap_wdt.c
@@ -253,6 +253,7 @@ static int omap_wdt_probe(struct platform_device *pdev)
 	wdev->wdog.ops = &omap_wdt_ops;
 	wdev->wdog.min_timeout = TIMER_MARGIN_MIN;
 	wdev->wdog.max_timeout = TIMER_MARGIN_MAX;
+	wdev->wdog.parent = &pdev->dev;
 
 	if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0)
 		wdev->wdog.timeout = TIMER_MARGIN_DEFAULT;
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index ef0c628d5037..c6b8f4a43bde 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -567,6 +567,7 @@ static int orion_wdt_probe(struct platform_device *pdev)
 
 	dev->wdt.timeout = wdt_max_duration;
 	dev->wdt.max_timeout = wdt_max_duration;
+	dev->wdt.parent = &pdev->dev;
 	watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev);
 
 	platform_set_drvdata(pdev, &dev->wdt);
diff --git a/drivers/watchdog/pnx4008_wdt.c b/drivers/watchdog/pnx4008_wdt.c
index b9c6049c3e78..4224b3ec83a5 100644
--- a/drivers/watchdog/pnx4008_wdt.c
+++ b/drivers/watchdog/pnx4008_wdt.c
@@ -167,6 +167,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
 
 	pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
 			WDIOF_CARDRESET : 0;
+	pnx4008_wdd.parent = &pdev->dev;
 	watchdog_set_nowayout(&pnx4008_wdd, nowayout);
 
 	pnx4008_wdt_stop(&pnx4008_wdd);	/* disable for now */
diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c
index aa03ca8f2d9b..773dcfaee7b2 100644
--- a/drivers/watchdog/qcom-wdt.c
+++ b/drivers/watchdog/qcom-wdt.c
@@ -171,6 +171,7 @@ static int qcom_wdt_probe(struct platform_device *pdev)
 	wdt->wdd.ops = &qcom_wdt_ops;
 	wdt->wdd.min_timeout = 1;
 	wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
+	wdt->wdd.parent = &pdev->dev;
 
 	/*
 	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
diff --git a/drivers/watchdog/retu_wdt.c b/drivers/watchdog/retu_wdt.c
index b7c68e275aeb..39cd51df2ffc 100644
--- a/drivers/watchdog/retu_wdt.c
+++ b/drivers/watchdog/retu_wdt.c
@@ -127,6 +127,7 @@ static int retu_wdt_probe(struct platform_device *pdev)
 	retu_wdt->timeout	= RETU_WDT_MAX_TIMER;
 	retu_wdt->min_timeout	= 0;
 	retu_wdt->max_timeout	= RETU_WDT_MAX_TIMER;
+	retu_wdt->parent	= &pdev->dev;
 
 	watchdog_set_drvdata(retu_wdt, wdev);
 	watchdog_set_nowayout(retu_wdt, nowayout);
diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c
index a6f7e2e29beb..1967919ae743 100644
--- a/drivers/watchdog/rt2880_wdt.c
+++ b/drivers/watchdog/rt2880_wdt.c
@@ -161,6 +161,7 @@ static int rt288x_wdt_probe(struct platform_device *pdev)
 	rt288x_wdt_dev.dev = &pdev->dev;
 	rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
 	rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
+	rt288x_wdt_dev.parent = &pdev->dev;
 
 	watchdog_init_timeout(&rt288x_wdt_dev, rt288x_wdt_dev.max_timeout,
 			      &pdev->dev);
diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
index e89ae027c91d..d781000c7825 100644
--- a/drivers/watchdog/s3c2410_wdt.c
+++ b/drivers/watchdog/s3c2410_wdt.c
@@ -607,6 +607,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
 	watchdog_set_nowayout(&wdt->wdt_device, nowayout);
 
 	wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
+	wdt->wdt_device.parent = &pdev->dev;
 
 	ret = watchdog_register_device(&wdt->wdt_device);
 	if (ret) {
diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c
index 567458b137a6..f90812170657 100644
--- a/drivers/watchdog/shwdt.c
+++ b/drivers/watchdog/shwdt.c
@@ -252,6 +252,7 @@ static int sh_wdt_probe(struct platform_device *pdev)
 
 	watchdog_set_nowayout(&sh_wdt_dev, nowayout);
 	watchdog_set_drvdata(&sh_wdt_dev, wdt);
+	sh_wdt_dev.parent = &pdev->dev;
 
 	spin_lock_init(&wdt->lock);
 
diff --git a/drivers/watchdog/sirfsoc_wdt.c b/drivers/watchdog/sirfsoc_wdt.c
index 42fa5c0c518a..d0578ab2e636 100644
--- a/drivers/watchdog/sirfsoc_wdt.c
+++ b/drivers/watchdog/sirfsoc_wdt.c
@@ -154,6 +154,7 @@ static int sirfsoc_wdt_probe(struct platform_device *pdev)
 
 	watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev);
 	watchdog_set_nowayout(&sirfsoc_wdd, nowayout);
+	sirfsoc_wdd.parent = &pdev->dev;
 
 	ret = watchdog_register_device(&sirfsoc_wdd);
 	if (ret)
diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c
index 4e7fec36f5c3..01d816251302 100644
--- a/drivers/watchdog/sp805_wdt.c
+++ b/drivers/watchdog/sp805_wdt.c
@@ -226,6 +226,7 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
 	wdt->adev = adev;
 	wdt->wdd.info = &wdt_info;
 	wdt->wdd.ops = &wdt_ops;
+	wdt->wdd.parent = &adev->dev;
 
 	spin_lock_init(&wdt->lock);
 	watchdog_set_nowayout(&wdt->wdd, nowayout);
diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c
index 6785afdc0fca..14e9badf2bfa 100644
--- a/drivers/watchdog/st_lpc_wdt.c
+++ b/drivers/watchdog/st_lpc_wdt.c
@@ -241,6 +241,7 @@ static int st_wdog_probe(struct platform_device *pdev)
 		return -EINVAL;
 	}
 	st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
+	st_wdog_dev.parent = &pdev->dev;
 
 	ret = clk_prepare_enable(clk);
 	if (ret) {
diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c b/drivers/watchdog/stmp3xxx_rtc_wdt.c
index e7f0d5b60d3d..3ee6128a540e 100644
--- a/drivers/watchdog/stmp3xxx_rtc_wdt.c
+++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c
@@ -76,6 +76,7 @@ static int stmp3xxx_wdt_probe(struct platform_device *pdev)
 	watchdog_set_drvdata(&stmp3xxx_wdd, &pdev->dev);
 
 	stmp3xxx_wdd.timeout = clamp_t(unsigned, heartbeat, 1, STMP3XXX_MAX_TIMEOUT);
+	stmp3xxx_wdd.parent = &pdev->dev;
 
 	ret = watchdog_register_device(&stmp3xxx_wdd);
 	if (ret < 0) {
diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c
index 30451ea46902..7f97cdd53f29 100644
--- a/drivers/watchdog/tegra_wdt.c
+++ b/drivers/watchdog/tegra_wdt.c
@@ -218,6 +218,7 @@ static int tegra_wdt_probe(struct platform_device *pdev)
 	wdd->ops = &tegra_wdt_ops;
 	wdd->min_timeout = MIN_WDT_TIMEOUT;
 	wdd->max_timeout = MAX_WDT_TIMEOUT;
+	wdd->parent = &pdev->dev;
 
 	watchdog_set_drvdata(wdd, wdt);
 
diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c
index 2c1db6fa9a27..9bf3cc0f3961 100644
--- a/drivers/watchdog/twl4030_wdt.c
+++ b/drivers/watchdog/twl4030_wdt.c
@@ -83,6 +83,7 @@ static int twl4030_wdt_probe(struct platform_device *pdev)
 	wdt->timeout		= 30;
 	wdt->min_timeout	= 1;
 	wdt->max_timeout	= 30;
+	wdt->parent = &pdev->dev;
 
 	watchdog_set_nowayout(wdt, nowayout);
 	platform_set_drvdata(pdev, wdt);
diff --git a/drivers/watchdog/txx9wdt.c b/drivers/watchdog/txx9wdt.c
index 7f615933d31a..c2da880292bc 100644
--- a/drivers/watchdog/txx9wdt.c
+++ b/drivers/watchdog/txx9wdt.c
@@ -131,6 +131,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
 	txx9wdt.timeout = timeout;
 	txx9wdt.min_timeout = 1;
 	txx9wdt.max_timeout = WD_MAX_TIMEOUT;
+	txx9wdt.parent = &dev->dev;
 	watchdog_set_nowayout(&txx9wdt, nowayout);
 
 	ret = watchdog_register_device(&txx9wdt);
diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c
index 9de09ab00838..37c084353cce 100644
--- a/drivers/watchdog/ux500_wdt.c
+++ b/drivers/watchdog/ux500_wdt.c
@@ -96,6 +96,7 @@ static int ux500_wdt_probe(struct platform_device *pdev)
 			ux500_wdt.max_timeout = WATCHDOG_MAX28;
 	}
 
+	ux500_wdt.parent = &pdev->dev;
 	watchdog_set_nowayout(&ux500_wdt, nowayout);
 
 	/* disable auto off on sleep */
diff --git a/drivers/watchdog/via_wdt.c b/drivers/watchdog/via_wdt.c
index 56369c4f1961..5f9cbc37520d 100644
--- a/drivers/watchdog/via_wdt.c
+++ b/drivers/watchdog/via_wdt.c
@@ -206,6 +206,7 @@ static int wdt_probe(struct pci_dev *pdev,
 		timeout = WDT_TIMEOUT;
 
 	wdt_dev.timeout = timeout;
+	wdt_dev.parent = &pdev->dev;
 	watchdog_set_nowayout(&wdt_dev, nowayout);
 	if (readl(wdt_mem) & VIA_WDT_FIRED)
 		wdt_dev.bootstatus |= WDIOF_CARDRESET;
diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c
index 2fa17e746ff6..8d1184aee932 100644
--- a/drivers/watchdog/wm831x_wdt.c
+++ b/drivers/watchdog/wm831x_wdt.c
@@ -215,6 +215,7 @@ static int wm831x_wdt_probe(struct platform_device *pdev)
 
 	wm831x_wdt->info = &wm831x_wdt_info;
 	wm831x_wdt->ops = &wm831x_wdt_ops;
+	wm831x_wdt->parent = &pdev->dev;
 	watchdog_set_nowayout(wm831x_wdt, nowayout);
 	watchdog_set_drvdata(wm831x_wdt, driver_data);
 
diff --git a/drivers/watchdog/wm8350_wdt.c b/drivers/watchdog/wm8350_wdt.c
index 34d272ada23d..4ab4b8347d45 100644
--- a/drivers/watchdog/wm8350_wdt.c
+++ b/drivers/watchdog/wm8350_wdt.c
@@ -151,6 +151,7 @@ static int wm8350_wdt_probe(struct platform_device *pdev)
 
 	watchdog_set_nowayout(&wm8350_wdt, nowayout);
 	watchdog_set_drvdata(&wm8350_wdt, wm8350);
+	wm8350_wdt.parent = &pdev->dev;
 
 	/* Default to 4s timeout */
 	wm8350_wdt_set_timeout(&wm8350_wdt, 4);
-- 
2.4.3



From kernel at martin.sperl.org  Mon Aug 24 01:40:03 2015
From: kernel at martin.sperl.org (kernel at martin.sperl.org)
Date: Mon, 24 Aug 2015 08:40:03 +0000
Subject: [PATCH v4 0/5] bcm2835: auxiliar device support for spi
Message-ID: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>

From: Martin Sperl <kernel at martin.sperl.org>

The BCM2835 contains 3 auxiliar devices:
* spi1
* spi2
* uart1

All of those 3 devices are enabled/disabled via a shared register,
which is set by default to be disabled.

Access to this register needs to get serialized.

So fter several iterations of discussions with the following idea:
* syscon - device tree should describe HW not drivers to use -
           'compatiblity = "brcm,bcm2835-aux-enable", "syscon";'
           is not acceptable
* regulator - it is not necessarily a regulator or a power gate
              that is implemented in HW, so it is not valid to use
              this framework

The recommendation was made to create a new minimal API in soc
just for access to this shared enable/disable register.

This patch-series implements:
* the bcm2835-auxiliar device enable/disable api in soc.
* the bcm2835-auxiliar spi device driver

The uart1 device driver (ns16550 based) is not implemented so far
but should be using the same API.

Martin Sperl (5):
  soc: bcm2835: auxiliar devices enable infrastructure
  ARM: bcm2835: add DT for the bcm2835 auxiliar devices
  dt/bindings: bcm2835: add binding documentation for bcm2835-aux
  spi: bcm2835: new driver implementing auxiliar spi1/spi2 on the
    bcm2835 soc
  dt/bindings: bcm2835: Add binding documentation for auxiliar spi
    devices

 .../bindings/soc/bcm/brcm,bcm2835-aux.txt          |   27 ++
 .../bindings/spi/brcm,bcm2835-aux-spi.txt          |   61 +++
 arch/arm/boot/dts/bcm2835.dtsi                     |   37 ++
 arch/arm/configs/bcm2835_defconfig                 |    1 +
 drivers/soc/Kconfig                                |    1 +
 drivers/soc/Makefile                               |    1 +
 drivers/soc/bcm/Kconfig                            |   11 +
 drivers/soc/bcm/Makefile                           |    1 +
 drivers/soc/bcm/bcm2835-aux.c                      |  154 ++++++
 drivers/spi/Kconfig                                |   12 +
 drivers/spi/Makefile                               |    1 +
 drivers/spi/spi-bcm2835aux.c                       |  506 ++++++++++++++++++++
 include/linux/soc/bcm/bcm2835-aux.h                |   23 +
 13 files changed, 836 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt
 create mode 100644 Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.txt
 create mode 100644 drivers/soc/bcm/Kconfig
 create mode 100644 drivers/soc/bcm/Makefile
 create mode 100644 drivers/soc/bcm/bcm2835-aux.c
 create mode 100644 drivers/spi/spi-bcm2835aux.c
 create mode 100644 include/linux/soc/bcm/bcm2835-aux.h

--
1.7.10.4



From kernel at martin.sperl.org  Mon Aug 24 01:40:04 2015
From: kernel at martin.sperl.org (kernel at martin.sperl.org)
Date: Mon, 24 Aug 2015 08:40:04 +0000
Subject: [PATCH v4 1/5] soc: bcm2835: auxiliar devices enable infrastructure
In-Reply-To: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
References: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
Message-ID: <1440405608-3995-2-git-send-email-kernel@martin.sperl.org>

From: Martin Sperl <kernel at martin.sperl.org>

The bcm2835 SOC contains 3 auxiliar devices (spi1, spi2 and uart1)
that all are enabled via a shared register.

To serialize access to this shared register this soc-driver
is created that implements:
  bcm2835aux_enable(struct device *dev, const char *property);
  bcm2835aux_disable(struct device *dev, const char *property);

Which will read the property from the device tree of the device
and enable/disable that specific device as per device tree.

First use of this api will be spi-bcm2835aux.

Signed-off-by: Martin Sperl <kernel at martin.sperl.org>
---
 drivers/soc/Kconfig                 |    1 +
 drivers/soc/Makefile                |    1 +
 drivers/soc/bcm/Kconfig             |   11 +++
 drivers/soc/bcm/Makefile            |    1 +
 drivers/soc/bcm/bcm2835-aux.c       |  154 +++++++++++++++++++++++++++++++++++
 include/linux/soc/bcm/bcm2835-aux.h |   23 ++++++
 6 files changed, 191 insertions(+)
 create mode 100644 drivers/soc/bcm/Kconfig
 create mode 100644 drivers/soc/bcm/Makefile
 create mode 100644 drivers/soc/bcm/bcm2835-aux.c
 create mode 100644 include/linux/soc/bcm/bcm2835-aux.h

diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index 96ddecb..5506e39 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -1,5 +1,6 @@
 menu "SOC (System On Chip) specific Drivers"

+source "drivers/soc/bcm/Kconfig"
 source "drivers/soc/mediatek/Kconfig"
 source "drivers/soc/qcom/Kconfig"
 source "drivers/soc/sunxi/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 7dc7c0d..c5744e1 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -2,6 +2,7 @@
 # Makefile for the Linux Kernel SOC specific device drivers.
 #

+obj-$(CONFIG_ARCH_BCM)		+= bcm/
 obj-$(CONFIG_ARCH_MEDIATEK)	+= mediatek/
 obj-$(CONFIG_ARCH_QCOM)		+= qcom/
 obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
diff --git a/drivers/soc/bcm/Kconfig b/drivers/soc/bcm/Kconfig
new file mode 100644
index 0000000..b0af34b3
--- /dev/null
+++ b/drivers/soc/bcm/Kconfig
@@ -0,0 +1,11 @@
+#
+# Broadcom SoC drivers
+#
+config SOC_BCM2835_AUX
+	tristate "Broadcom BCM2835 aux"
+	depends on OF
+	depends on ARCH_BCM2835 || COMPILE_TEST
+
+	help
+	  Support to enable/disable the BCM2835 auxiliar
+	  devices spi1, spi2, uart1
diff --git a/drivers/soc/bcm/Makefile b/drivers/soc/bcm/Makefile
new file mode 100644
index 0000000..370a872
--- /dev/null
+++ b/drivers/soc/bcm/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SOC_BCM2835_AUX) += bcm2835-aux.o
diff --git a/drivers/soc/bcm/bcm2835-aux.c b/drivers/soc/bcm/bcm2835-aux.c
new file mode 100644
index 0000000..887508d
--- /dev/null
+++ b/drivers/soc/bcm/bcm2835-aux.c
@@ -0,0 +1,154 @@
+/*
+ * bcm2835-aux
+ *
+ * Copyright (C) 2015 Martin Sperl
+ *
+ * Author: Martin Sperl <kernel at martin.sperl.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/soc/bcm/bcm2835-aux.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+static DEFINE_SPINLOCK(bcm2835aux_lock);
+
+static struct platform_driver bcm2835aux_driver;
+
+static int bcm2835aux_dev_match(struct device *dev, void *data)
+{
+	struct device_node *dn = data;
+
+	return (dev->of_node == dn) ? 1 : 0;
+}
+
+static void *bcm2835aux_find_base(struct device *dev, const char *property)
+{
+	struct device *found = NULL;
+	struct device_node *np;
+
+	/* get the phandle of the device */
+	np = of_parse_phandle(dev->of_node, property, 0);
+	if (!np) {
+		dev_err(dev, "missing property %s\n", property);
+		return ERR_PTR(-ENODEV);
+	}
+
+	/* now find the device it points to */
+	found = driver_find_device(&bcm2835aux_driver.driver, NULL,
+				   np, bcm2835aux_dev_match);
+	if (!found) {
+		dev_err(dev, "device for phandle of %s not found\n",
+			property);
+		return ERR_PTR(-ENODEV);
+	}
+
+	/* now we got the device, so return the pointer */
+	return dev_get_drvdata(found);
+}
+
+static u32 bcm2835aux_find_mask(struct device *dev, const char *property)
+{
+	int err;
+	u32 mask;
+
+	err = of_property_read_u32_index(dev->of_node, property, 1, &mask);
+	if (err) {
+		dev_err(dev, "missing argument to %s: %d\n",
+			property, err);
+		return 0;
+	}
+
+	return mask;
+}
+
+static int bcm2835aux_bitset(struct device *dev, const char *property,
+			     bool set)
+{
+	u32 v, mask;
+	unsigned long flags;
+	void __iomem *base;
+
+	/* find the device */
+	base = bcm2835aux_find_base(dev, property);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	/* and extract the mask */
+	mask = bcm2835aux_find_mask(dev, property);
+	if (!mask)
+		return -ENOENT;
+
+	spin_lock_irqsave(&bcm2835aux_lock, flags);
+
+	v = readl(base);
+	if (set)
+		v |= mask;
+	else
+		v &= ~mask;
+
+	writel(v, base);
+
+	spin_unlock_irqrestore(&bcm2835aux_lock, flags);
+
+	return 0;
+}
+
+int bcm2835aux_enable(struct device *dev, const char *property)
+{
+	return bcm2835aux_bitset(dev, property, true);
+}
+EXPORT_SYMBOL_GPL(bcm2835aux_enable);
+
+int bcm2835aux_disable(struct device *dev, const char *property)
+{
+	return bcm2835aux_bitset(dev, property, false);
+}
+EXPORT_SYMBOL_GPL(bcm2835aux_disable);
+
+static int bcm2835aux_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	void __iomem *base;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENOENT;
+
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	platform_set_drvdata(pdev, base);
+
+	return 0;
+}
+
+static const struct of_device_id bcm2835aux_match[] = {
+	{ .compatible = "brcm,bcm2835-aux", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, bcm2835aux_match);
+
+static struct platform_driver bcm2835aux_driver = {
+	.driver = {
+		.name           = "bcm2835-aux",
+		.of_match_table	= bcm2835aux_match,
+	},
+	.probe			= bcm2835aux_probe,
+};
+module_platform_driver(bcm2835aux_driver);
+
+MODULE_DESCRIPTION("enable/disable driver for aux-spi1/spi2/uart1 on Broadcom BCM2835");
+MODULE_AUTHOR("Martin Sperl <kernel at martin.sperl.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/soc/bcm/bcm2835-aux.h b/include/linux/soc/bcm/bcm2835-aux.h
new file mode 100644
index 0000000..17a64c6
--- /dev/null
+++ b/include/linux/soc/bcm/bcm2835-aux.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 Martin Sperl
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __BCM2835_AUX_H__
+#define __BCM2835_AUX_H__
+
+struct device;
+
+int bcm2835aux_enable(struct device *dev, const char *property);
+int bcm2835aux_disable(struct device *dev, const char *property);
+
+#endif
--
1.7.10.4



From kernel at martin.sperl.org  Mon Aug 24 01:40:05 2015
From: kernel at martin.sperl.org (kernel at martin.sperl.org)
Date: Mon, 24 Aug 2015 08:40:05 +0000
Subject: [PATCH v4 2/5] ARM: bcm2835: add DT for the bcm2835 auxiliar devices
In-Reply-To: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
References: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
Message-ID: <1440405608-3995-3-git-send-email-kernel@martin.sperl.org>

From: Martin Sperl <kernel at martin.sperl.org>

Add device tree definitions for auxiliar bcm2835 devices:
* spi1
* spi2
* uart1

This also include a device to get used by the relevant
device-drivers (via a shared register) to enable/disable
the HW-block.

Signed-off-by: Martin Sperl <kernel at martin.sperl.org>
---
 arch/arm/boot/dts/bcm2835.dtsi |   37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
index 301c73f..4e6fc61 100644
--- a/arch/arm/boot/dts/bcm2835.dtsi
+++ b/arch/arm/boot/dts/bcm2835.dtsi
@@ -158,6 +158,43 @@
 		arm-pmu {
 			compatible = "arm,arm1176-pmu";
 		};
+
+		aux_enable: aux_enable at 0x7e215004 {
+			compatible = "brcm,bcm2835-aux";
+			reg = <0x7e215004 0x04>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+		};
+
+		uart1: uart at 7e215040 {
+			compatible = "brcm,bcm2835-aux-uart";
+			reg = <0x7e215040 0x40>;
+			interrupts = <1 29>;
+			brcm,aux-enable = <&aux_enable 1>;
+			status = "disabled";
+		};
+
+		spi1: spi at 7e215080 {
+			compatible = "brcm,bcm2835-aux-spi";
+			reg = <0x7e215080 0x40>;
+			brcm,aux-enable = <&aux_enable 2>;
+			interrupts = <1 29>;
+			clocks = <&clk_spi>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+		};
+
+		spi2: spi at 7e2150c0 {
+			compatible = "brcm,bcm2835-aux-spi";
+			reg = <0x7e2150c0 0x40>;
+			brcm,aux-enable = <&aux_enable 4>;
+			interrupts = <1 29>;
+			clocks = <&clk_spi>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+		};
 	};

 	clocks {
--
1.7.10.4



From kernel at martin.sperl.org  Mon Aug 24 01:40:06 2015
From: kernel at martin.sperl.org (kernel at martin.sperl.org)
Date: Mon, 24 Aug 2015 08:40:06 +0000
Subject: [PATCH v4 3/5] dt/bindings: bcm2835: add binding documentation for
 bcm2835-aux
In-Reply-To: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
References: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
Message-ID: <1440405608-3995-4-git-send-email-kernel@martin.sperl.org>

From: Martin Sperl <kernel at martin.sperl.org>

Signed-off-by: Martin Sperl <kernel at martin.sperl.org>
---
 .../bindings/soc/bcm/brcm,bcm2835-aux.txt          |   27 ++++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt

diff --git a/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt b/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt
new file mode 100644
index 0000000..8b79cf3
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt
@@ -0,0 +1,27 @@
+Broadcom BCM2835 auxiliar device enable register
+
+the BCM2835 contains 3 auxiliar devices (spi1/spi2/uart1)
+which need to get enabled via a shared register.
+
+Required properties:
+- compatible: Should be "brcm,bcm2835-aux".
+- reg: Should contain register location and length for the
+       enable register
+
+Example:
+
+aux_enable: aux_enable at 0x7e215004 {
+	compatible = "bcrm,bcm2835-aux";
+	reg = <0x7e215004 0x04>;
+};
+
+Typically used in the respective auxiliar device descriptions
+like this:
+
+uart1: uart at 7e215040 {
+	...
+	brcm,aux-enable = <&aux_enable 1>;
+	...
+};
+
+The name of the property can be device/driver specific.
--
1.7.10.4



From kernel at martin.sperl.org  Mon Aug 24 01:40:07 2015
From: kernel at martin.sperl.org (kernel at martin.sperl.org)
Date: Mon, 24 Aug 2015 08:40:07 +0000
Subject: [PATCH v4 4/5] spi: bcm2835: new driver implementing auxiliar
 spi1/spi2 on the bcm2835 soc
In-Reply-To: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
References: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
Message-ID: <1440405608-3995-5-git-send-email-kernel@martin.sperl.org>

From: Martin Sperl <kernel at martin.sperl.org>

Signed-off-by: Martin Sperl <kernel at martin.sperl.org>
---
 arch/arm/configs/bcm2835_defconfig |    1 +
 drivers/spi/Kconfig                |   12 +
 drivers/spi/Makefile               |    1 +
 drivers/spi/spi-bcm2835aux.c       |  506 ++++++++++++++++++++++++++++++++++++
 4 files changed, 520 insertions(+)
 create mode 100644 drivers/spi/spi-bcm2835aux.c

diff --git a/arch/arm/configs/bcm2835_defconfig b/arch/arm/configs/bcm2835_defconfig
index 31cb073..a205c8b 100644
--- a/arch/arm/configs/bcm2835_defconfig
+++ b/arch/arm/configs/bcm2835_defconfig
@@ -75,6 +75,7 @@ CONFIG_I2C_CHARDEV=y
 CONFIG_I2C_BCM2835=y
 CONFIG_SPI=y
 CONFIG_SPI_BCM2835=y
+CONFIG_SPI_BCM2835AUX=y
 CONFIG_GPIO_SYSFS=y
 # CONFIG_HWMON is not set
 CONFIG_FB=y
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 43f6d3d..133f5ca 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -88,6 +88,18 @@ config SPI_BCM2835
 	  is for the regular SPI controller. Slave mode operation is not also
 	  not supported.

+config SPI_BCM2835AUX
+	tristate "BCM2835 SPI auxiliar controller"
+	depends on ARCH_BCM2835 || COMPILE_TEST
+	depends on GPIOLIB
+	select SOC_BCM2835AUX
+	help
+	  This selects a driver for the Broadcom BCM2835 SPI aux master.
+
+	  The BCM2835 contains two types of SPI master controller; the
+	  "universal SPI master", and the regular SPI controller.
+	  This driver is for the universal/auxiliar SPI controller.
+
 config SPI_BFIN5XX
 	tristate "SPI controller driver for ADI Blackfin5xx"
 	depends on BLACKFIN && !BF60x
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 9746beb2..4e9cba3 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_SPI_ATMEL)			+= spi-atmel.o
 obj-$(CONFIG_SPI_ATH79)			+= spi-ath79.o
 obj-$(CONFIG_SPI_AU1550)		+= spi-au1550.o
 obj-$(CONFIG_SPI_BCM2835)		+= spi-bcm2835.o
+obj-$(CONFIG_SPI_BCM2835AUX)		+= spi-bcm2835aux.o
 obj-$(CONFIG_SPI_BCM53XX)		+= spi-bcm53xx.o
 obj-$(CONFIG_SPI_BCM63XX)		+= spi-bcm63xx.o
 obj-$(CONFIG_SPI_BCM63XX_HSSPI)		+= spi-bcm63xx-hsspi.o
diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c
new file mode 100644
index 0000000..9e056f6
--- /dev/null
+++ b/drivers/spi/spi-bcm2835aux.c
@@ -0,0 +1,506 @@
+/*
+ * Driver for Broadcom BCM2835 SPI Controllers
+ *
+ * the driver does not rely on the native chipselects at all
+ * but only uses the gpio type chipselects
+ *
+ * Based on: spi-bcm2835.c
+ *
+ * Copyright (C) 2015 Martin Sperl
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/soc/bcm/bcm2835-aux.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/spinlock.h>
+
+/*
+ * shared aux registers between spi1/spi2 and uart1
+ *
+ * these defines could go to a separate module if needed
+ * so that it can also get used with the uart1 implementation
+ * when it materializes.
+ */
+
+/* the AUX register offsets */
+#define BCM2835_AUX_IRQ		0x00
+#define BCM2835_AUX_ENABLE	0x04
+
+/* the AUX Bitfield identical for both register */
+#define BCM2835_AUX_BIT_UART	0x00000001
+#define BCM2835_AUX_BIT_SPI1	0x00000002
+#define BCM2835_AUX_BIT_SPI2	0x00000004
+
+/*
+ * spi register defines
+ *
+ * note there is garbage in the "official" documentation,
+ * so somedata taken from the file:
+ *   brcm_usrlib/dag/vmcsx/vcinclude/bcm2708_chip/aux_io.h
+ * inside of:
+ *   http://www.broadcom.com/docs/support/videocore/Brcm_Android_ICS_Graphics_Stack.tar.gz
+ */
+
+/* SPI register offsets */
+#define BCM2835_AUX_SPI_CNTL0	0x00
+#define BCM2835_AUX_SPI_CNTL1	0x04
+#define BCM2835_AUX_SPI_STAT	0x08
+#define BCM2835_AUX_SPI_PEEK	0x0C
+#define BCM2835_AUX_SPI_IO	0x20
+#define BCM2835_AUX_SPI_TXHOLD	0x30
+
+/* Bitfields in CNTL0 */
+#define BCM2835_AUX_SPI_CNTL0_SPEED	0xFFF00000
+#define BCM2835_AUX_SPI_CNTL0_SPEED_MAX	0xFFF
+#define BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT	20
+#define BCM2835_AUX_SPI_CNTL0_CS	0x000E0000
+#define BCM2835_AUX_SPI_CNTL0_POSTINPUT	0x00010000
+#define BCM2835_AUX_SPI_CNTL0_VAR_CS	0x00008000
+#define BCM2835_AUX_SPI_CNTL0_VAR_WIDTH	0x00004000
+#define BCM2835_AUX_SPI_CNTL0_DOUTHOLD	0x00003000
+#define BCM2835_AUX_SPI_CNTL0_ENABLE	0x00000800
+#define BCM2835_AUX_SPI_CNTL0_CPHA_IN	0x00000400
+#define BCM2835_AUX_SPI_CNTL0_CLEARFIFO	0x00000200
+#define BCM2835_AUX_SPI_CNTL0_CPHA_OUT	0x00000100
+#define BCM2835_AUX_SPI_CNTL0_CPOL	0x00000080
+#define BCM2835_AUX_SPI_CNTL0_MSBF_OUT	0x00000040
+#define BCM2835_AUX_SPI_CNTL0_SHIFTLEN	0x0000003F
+
+/* Bitfields in CNTL1 */
+#define BCM2835_AUX_SPI_CNTL1_CSHIGH	0x00000700
+#define BCM2835_AUX_SPI_CNTL1_IDLE	0x00000080
+#define BCM2835_AUX_SPI_CNTL1_TXEMPTY	0x00000040
+#define BCM2835_AUX_SPI_CNTL1_MSBF_IN	0x00000002
+#define BCM2835_AUX_SPI_CNTL1_KEEP_IN	0x00000001
+
+/* Bitfields in STAT */
+#define BCM2835_AUX_SPI_STAT_TX_LVL	0xFF000000
+#define BCM2835_AUX_SPI_STAT_RX_LVL	0x00FF0000
+#define BCM2835_AUX_SPI_STAT_TX_FULL	0x00000400
+#define BCM2835_AUX_SPI_STAT_TX_EMPTY	0x00000200
+#define BCM2835_AUX_SPI_STAT_RX_FULL	0x00000100
+#define BCM2835_AUX_SPI_STAT_RX_EMPTY	0x00000080
+#define BCM2835_AUX_SPI_STAT_BUSY	0x00000040
+#define BCM2835_AUX_SPI_STAT_BITCOUNT	0x0000003F
+
+/* timeout values */
+#define BCM2835_AUX_SPI_POLLING_LIMIT_US	30
+#define BCM2835_AUX_SPI_POLLING_JIFFIES		2
+
+#define BCM2835_AUX_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
+				  | SPI_NO_CS)
+
+#define DRV_NAME	"spi-bcm2835aux"
+#define ENABLE_PROPERTY "brcm,aux-enable"
+
+struct bcm2835aux_spi {
+	void __iomem *regs;
+	struct clk *clk;
+	int irq;
+	u32 cntl[2];
+	const u8 *tx_buf;
+	u8 *rx_buf;
+	int tx_len;
+	int rx_len;
+};
+
+static inline u32 bcm2835aux_rd(struct bcm2835aux_spi *bs, unsigned reg)
+{
+	return readl(bs->regs + reg);
+}
+
+static inline void bcm2835aux_wr(struct bcm2835aux_spi *bs, unsigned reg,
+				 u32 val)
+{
+	writel(val, bs->regs + reg);
+}
+
+static inline void bcm2835aux_rd_fifo(struct bcm2835aux_spi *bs)
+{
+	u32 data;
+	int i;
+	int count = min(bs->rx_len, 3);
+
+	data = bcm2835aux_rd(bs, BCM2835_AUX_SPI_IO);
+	if (bs->rx_buf) {
+		for (i = 0; i < count; i++)
+			*bs->rx_buf++ = (data >> (8 * (2 - i))) & 0xff;
+	}
+	bs->rx_len -= count;
+}
+
+static inline void bcm2835aux_wr_fifo(struct bcm2835aux_spi *bs)
+{
+	u32 data;
+	u8 byte;
+	int count;
+	int i;
+
+	/* gather up to 3 bytes to write to the FIFO */
+	count = min(bs->tx_len, 3);
+	data = 0;
+	for (i = 0; i < count; i++) {
+		byte = bs->tx_buf ? *bs->tx_buf++ : 0;
+		data |= byte << (8 * (2 - i));
+	}
+
+	/* and set the variable bit-length */
+	data |= (count * 8) << 24;
+
+	/* and decrement length */
+	bs->tx_len -= count;
+
+	/* write to the correct TX-register */
+	if (bs->tx_len)
+		bcm2835aux_wr(bs, BCM2835_AUX_SPI_TXHOLD, data);
+	else
+		bcm2835aux_wr(bs, BCM2835_AUX_SPI_IO, data);
+}
+
+static void bcm2835aux_spi_reset_hw(struct bcm2835aux_spi *bs)
+{
+	/* disable spi clearing fifo and interrupts */
+	bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, 0);
+	bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0,
+		      BCM2835_AUX_SPI_CNTL0_CLEARFIFO);
+}
+
+static irqreturn_t bcm2835aux_spi_interrupt(int irq, void *dev_id)
+{
+	struct spi_master *master = dev_id;
+	struct bcm2835aux_spi *bs = spi_master_get_devdata(master);
+	irqreturn_t ret = IRQ_NONE;
+
+	/* check if we have data to read */
+	while (bs->rx_len &&
+	       (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) &
+		  BCM2835_AUX_SPI_STAT_RX_EMPTY))) {
+		bcm2835aux_rd_fifo(bs);
+		ret = IRQ_HANDLED;
+	}
+
+	/* check if we have data to write */
+	while (bs->tx_len &&
+	       (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) &
+		  BCM2835_AUX_SPI_STAT_TX_FULL))) {
+		bcm2835aux_wr_fifo(bs);
+		ret = IRQ_HANDLED;
+	}
+
+	/* and check if we have reached "done" */
+	while (bs->rx_len &&
+	       (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) &
+		  BCM2835_AUX_SPI_STAT_BUSY))) {
+		bcm2835aux_rd_fifo(bs);
+		ret = IRQ_HANDLED;
+	}
+
+	/* and if rx_len is 0 then wake up completion and disable spi */
+	if (!bs->rx_len) {
+		bcm2835aux_spi_reset_hw(bs);
+		complete(&master->xfer_completion);
+	}
+
+	/* and return */
+	return ret;
+}
+
+static int __bcm2835aux_spi_transfer_one_irq(struct spi_master *master,
+					     struct spi_device *spi,
+					     struct spi_transfer *tfr)
+{
+	struct bcm2835aux_spi *bs = spi_master_get_devdata(master);
+
+	/* enable interrupts */
+	bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1] |
+		BCM2835_AUX_SPI_CNTL1_TXEMPTY |
+		BCM2835_AUX_SPI_CNTL1_IDLE);
+
+	/* and wait for finish... */
+	return 1;
+}
+
+static int bcm2835aux_spi_transfer_one_irq(struct spi_master *master,
+					   struct spi_device *spi,
+					   struct spi_transfer *tfr)
+{
+	struct bcm2835aux_spi *bs = spi_master_get_devdata(master);
+
+	/* fill in registers and fifos before enabling interrupts */
+	bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]);
+	bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, bs->cntl[0]);
+
+	/* fill in tx fifo with data before enabling interrupts */
+	while ((bs->tx_len) &&
+	       (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) &
+		  BCM2835_AUX_SPI_STAT_TX_FULL))) {
+		bcm2835aux_wr_fifo(bs);
+	}
+
+	/* now run the interrupt mode */
+	return __bcm2835aux_spi_transfer_one_irq(master, spi, tfr);
+}
+
+static int bcm2835aux_spi_transfer_one_poll(struct spi_master *master,
+					    struct spi_device *spi,
+					    struct spi_transfer *tfr,
+					    unsigned long xfer_time_us)
+{
+	struct bcm2835aux_spi *bs = spi_master_get_devdata(master);
+	unsigned long timeout;
+	u32 stat;
+
+	/* configure spi */
+	bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]);
+	bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, bs->cntl[0]);
+
+	/* set the timeout */
+	timeout = jiffies + BCM2835_AUX_SPI_POLLING_JIFFIES;
+
+	/* loop until finished the transfer */
+	while (bs->rx_len) {
+		/* read status */
+		stat = bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT);
+
+		/* fill in tx fifo with remaining data */
+		if ((bs->tx_len) && (!(stat & BCM2835_AUX_SPI_STAT_TX_FULL))) {
+			bcm2835aux_wr_fifo(bs);
+			continue;
+		}
+
+		/* read data from fifo for both cases */
+		if (!(stat & BCM2835_AUX_SPI_STAT_RX_EMPTY)) {
+			bcm2835aux_rd_fifo(bs);
+			continue;
+		}
+		if (!(stat & BCM2835_AUX_SPI_STAT_BUSY)) {
+			bcm2835aux_rd_fifo(bs);
+			continue;
+		}
+
+		/* there is still data pending to read check the timeout */
+		if (bs->rx_len && time_after(jiffies, timeout)) {
+			dev_dbg_ratelimited(&spi->dev,
+					    "timeout period reached: jiffies: %lu remaining tx/rx: %d/%d - falling back to interrupt mode\n",
+					    jiffies - timeout,
+					    bs->tx_len, bs->rx_len);
+			/* forward to interrupt handler */
+			return __bcm2835aux_spi_transfer_one_irq(master,
+							       spi, tfr);
+		}
+	}
+
+	/* Transfer complete - reset SPI HW */
+	bcm2835aux_spi_reset_hw(bs);
+
+	/* and return without waiting for completion */
+	return 0;
+}
+
+static int bcm2835aux_spi_transfer_one(struct spi_master *master,
+				       struct spi_device *spi,
+				       struct spi_transfer *tfr)
+{
+	struct bcm2835aux_spi *bs = spi_master_get_devdata(master);
+	unsigned long spi_hz, clk_hz, speed;
+	unsigned long spi_used_hz, xfer_time_us;
+
+	/* calculate the registers to handle
+	 *
+	 * note that we use the variable data mode, which
+	 * is not optimal for longer transfers as we waste registers
+	 * resulting (potentially) in more interrupts when transferring
+	 * more than 12 bytes
+	 */
+	bs->cntl[0] = BCM2835_AUX_SPI_CNTL0_ENABLE |
+		      BCM2835_AUX_SPI_CNTL0_VAR_WIDTH |
+		      BCM2835_AUX_SPI_CNTL0_MSBF_OUT;
+	bs->cntl[1] = BCM2835_AUX_SPI_CNTL1_MSBF_IN;
+
+	/* set clock */
+	spi_hz = tfr->speed_hz;
+	clk_hz = clk_get_rate(bs->clk);
+
+	if (spi_hz >= clk_hz / 2) {
+		speed = 0;
+	} else if (spi_hz) {
+		speed = DIV_ROUND_UP(clk_hz, 2 * spi_hz) - 1;
+		if (speed >  BCM2835_AUX_SPI_CNTL0_SPEED_MAX)
+			speed = BCM2835_AUX_SPI_CNTL0_SPEED_MAX;
+	} else { /* the slowest we can go */
+		speed = BCM2835_AUX_SPI_CNTL0_SPEED_MAX;
+	}
+	bs->cntl[0] |= speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT;
+	spi_used_hz = clk_hz / (2 * (speed + 1));
+
+	/* handle all the modes */
+	if (spi->mode & SPI_CPOL)
+		bs->cntl[0] |= BCM2835_AUX_SPI_CNTL0_CPOL;
+	if (spi->mode & SPI_CPHA)
+		bs->cntl[0] |= BCM2835_AUX_SPI_CNTL0_CPHA_OUT |
+			       BCM2835_AUX_SPI_CNTL0_CPHA_IN;
+
+	/* set transmit buffers and length */
+	bs->tx_buf = tfr->tx_buf;
+	bs->rx_buf = tfr->rx_buf;
+	bs->tx_len = tfr->len;
+	bs->rx_len = tfr->len;
+
+	/* calculate the estimated time in us the transfer runs */
+	xfer_time_us = tfr->len
+		* 9 /* clocks/byte - SPI-HW waits 1 clock after each byte */
+		* 1000000 / spi_used_hz;
+
+	/* run in polling mode for short transfers */
+	if (xfer_time_us < BCM2835_AUX_SPI_POLLING_LIMIT_US)
+		return bcm2835aux_spi_transfer_one_poll(master, spi, tfr,
+							xfer_time_us);
+
+	/* run in interrupt mode for all others */
+	return bcm2835aux_spi_transfer_one_irq(master, spi, tfr);
+}
+
+static void bcm2835aux_spi_handle_err(struct spi_master *master,
+				      struct spi_message *msg)
+{
+	struct bcm2835aux_spi *bs = spi_master_get_devdata(master);
+
+	bcm2835aux_spi_reset_hw(bs);
+}
+
+static int bcm2835aux_spi_probe(struct platform_device *pdev)
+{
+	struct spi_master *master;
+	struct bcm2835aux_spi *bs;
+	struct resource *res;
+	int err;
+
+	master = spi_alloc_master(&pdev->dev, sizeof(*bs));
+	if (!master) {
+		dev_err(&pdev->dev, "spi_alloc_master() failed\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, master);
+	master->mode_bits = BCM2835_AUX_SPI_MODE_BITS;
+	master->bits_per_word_mask = SPI_BPW_MASK(8);
+	master->num_chipselect = -1;
+	master->transfer_one = bcm2835aux_spi_transfer_one;
+	master->handle_err = bcm2835aux_spi_handle_err;
+	master->dev.of_node = pdev->dev.of_node;
+
+	bs = spi_master_get_devdata(master);
+
+	/* the main area */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	bs->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(bs->regs)) {
+		err = PTR_ERR(bs->regs);
+		goto out_master_put;
+	}
+
+	bs->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(bs->clk)) {
+		err = PTR_ERR(bs->clk);
+		dev_err(&pdev->dev, "could not get clk: %d\n", err);
+		goto out_master_put;
+	}
+	bs->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+	if (bs->irq <= 0) {
+		dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq);
+		err = bs->irq ? bs->irq : -ENODEV;
+		goto out_master_put;
+	}
+
+	clk_prepare_enable(bs->clk);
+
+	err = devm_request_irq(&pdev->dev, bs->irq,
+			       bcm2835aux_spi_interrupt,
+			       IRQF_SHARED,
+			       dev_name(&pdev->dev), master);
+	if (err) {
+		dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
+		goto out_clk_disable;
+	}
+
+	/* enable HW block */
+	bcm2835aux_enable(&pdev->dev, ENABLE_PROPERTY);
+
+	/* reset SPI-HW block */
+	bcm2835aux_spi_reset_hw(bs);
+
+	err = devm_spi_register_master(&pdev->dev, master);
+	if (err) {
+		dev_err(&pdev->dev, "could not register SPI master: %d\n", err);
+		goto out_hw_disable;
+	}
+
+	return 0;
+
+out_hw_disable:
+	bcm2835aux_disable(&pdev->dev, ENABLE_PROPERTY);
+out_clk_disable:
+	clk_disable_unprepare(bs->clk);
+out_master_put:
+	spi_master_put(master);
+	return err;
+}
+
+static int bcm2835aux_spi_remove(struct platform_device *pdev)
+{
+	struct spi_master *master = platform_get_drvdata(pdev);
+	struct bcm2835aux_spi *bs = spi_master_get_devdata(master);
+
+	/* Clear FIFOs, and disable the HW block */
+	clk_disable_unprepare(bs->clk);
+
+	bcm2835aux_spi_reset_hw(bs);
+
+	/* disable HW block */
+	bcm2835aux_disable(&pdev->dev, ENABLE_PROPERTY);
+
+	return 0;
+}
+
+static const struct of_device_id bcm2835aux_spi_match[] = {
+	{ .compatible = "brcm,bcm2835-aux-spi", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, bcm2835aux_spi_match);
+
+static struct platform_driver bcm2835aux_spi_driver = {
+	.driver		= {
+		.name		= DRV_NAME,
+		.of_match_table	= bcm2835aux_spi_match,
+	},
+	.probe		= bcm2835aux_spi_probe,
+	.remove		= bcm2835aux_spi_remove,
+};
+module_platform_driver(bcm2835aux_spi_driver);
+
+MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2835 aux");
+MODULE_AUTHOR("Martin Sperl <kernel at martin.sperl.org>");
+MODULE_LICENSE("GPL v2");
--
1.7.10.4



From kernel at martin.sperl.org  Mon Aug 24 01:40:08 2015
From: kernel at martin.sperl.org (kernel at martin.sperl.org)
Date: Mon, 24 Aug 2015 08:40:08 +0000
Subject: [PATCH v4 5/5] dt/bindings: bcm2835: Add binding documentation for
 auxiliar spi devices
In-Reply-To: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
References: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
Message-ID: <1440405608-3995-6-git-send-email-kernel@martin.sperl.org>

From: Martin Sperl <kernel at martin.sperl.org>

Signed-off-by: Martin Sperl <kernel at martin.sperl.org>
---
 .../bindings/spi/brcm,bcm2835-aux-spi.txt          |   61 ++++++++++++++++++++
 1 file changed, 61 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.txt

diff --git a/Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.txt b/Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.txt
new file mode 100644
index 0000000..2a69a3b
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.txt
@@ -0,1 +1,61 @@
+Broadcom BCM2835 auxiliar SPI1/2 controller
+
+The BCM2835 contains two forms of SPI master controller, one known simply as
+SPI0, and the other known as the "Universal SPI Master"; part of the
+auxiliary block. This binding applies to the SPI1/2 controller.
+
+Required properties:
+- compatible: Should be "brcm,bcm2835-aux-spi".
+- reg: Should contain register location and length for the spi block
+       as well as for the common aux block control
+- interrupts: Should contain shared interrupt of the aux block
+- clocks: The clock feeding the SPI controller.
+- cs-gpios: the cs-gpios (native cs is NOT supported)
+	    see also spi-bus.txt
+- bcrm,aux-enable: the bcrm,bcm2835-aux-enable config entry to handle
+		  enabling/disabling of the spi1/spi2/uart1 HW block
+		  second "argument" is the mask to apply to the
+		  enable register
+
+Example:
+
+spi1 at 7e215080 {
+	compatible = "brcm,bcm2835-aux-spi";
+	reg = <0x7e215080 0x40>;
+	interrupts = <1 29>;
+	clocks = <&clk_spi>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	cs-gpios = <&gpio 18>, <&gpio 17>, <&gpio 16>;
+	bcrm,aux-enable = <&aux_enable 2>;
+};
+
+spi2 at 7e2150c0 {
+	compatible = "brcm,bcm2835-aux-spi";
+	reg = <0x7e2150c0 0x40>;
+	interrupts = <1 29>;
+	clocks = <&clk_spi>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	cs-gpios = <&gpio 43>, <&gpio 44>, <&gpio 45>;
+	bcrm,aux-enable = <&aux_enable 4>;
+};
+
+/* the necessary syscon config referenced above*/
+aux_enable: aux_enable at 0x7e215004 {
+	compatible = "bcrm,bcm2835-aux";
+	reg = <0x7e215004 0x04>;
+};
+
+Note that it also requires the GPIOs to be set up with the
+correct ALT-functions.
+
+For spi1 the following pins need to be set as:
+* ALT4: 19, 20, 21 (MISO, MOSI, SCK)
+
+For spi2 the following pins need to be set as:
+* ALT4: 40, 41, 42 (MISO, MOSI, SCK)
+
+CS-GPIOS need to get set as output - typically:
+* spi1: 18, 17, 16 (CS0, CS1, CS2)
+* spi2: 43, 44, 45 (CS0, CS1, CS2)
--
1.7.10.4



From robherring2 at gmail.com  Mon Aug 24 06:47:00 2015
From: robherring2 at gmail.com (Rob Herring)
Date: Mon, 24 Aug 2015 08:47:00 -0500
Subject: [PATCH 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <874mjxwyx5.fsf@eliezer.anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-2-git-send-email-eric@anholt.net>
 <55CEC25E.7070303@wwwdotorg.org>
 <874mjxwyx5.fsf@eliezer.anholt.net>
Message-ID: <CAL_JsqJxiGUR5BxBPyR3-0NxqWsOxMGrWQA6cMXe2pjTom__nw@mail.gmail.com>

On Mon, Aug 17, 2015 at 1:30 PM, Eric Anholt <eric at anholt.net> wrote:
> Stephen Warren <swarren at wwwdotorg.org> writes:
>
>> On 08/12/2015 06:56 PM, Eric Anholt wrote:
>>> Signed-off-by: Eric Anholt <eric at anholt.net>
>>
>> This one definitely needs a patch description, since someone might not
>> know what a VC4 is, and "git log" won't show the text from the binding
>> doc itself. I'd suggest adding the initial paragraph of the binding doc
>> as the patch description, or more.
>>
>>> diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
>
>>> +- hvss:             List of references to HVS video scalers
>>> +- encoders: List of references to output encoders (HDMI, SDTV)
>>
>> Would it make sense to make all those nodes child node of the vc4
>> object. That way, there's no need to have these lists of objects; they
>> can be automatically built up as the DT is enumerated. I know that e.g.
>> the NVIDIA Tegra host1x binding works this way, and I think it may have
>> been inspired by other similar cases.
>
> I've looked at tegra, and the component system used by msm appears to be
> nicer than it.  To follow tegra's model, it looks like I need to build
> this extra bus thing corresponding to host1x that is effectively the
> drivers/base/component.c code, so that I can get at vc4's structure from
> the component drivers.
>
>> Of course, this is only appropriate if the HW modules really are
>> logically children of the VC4 HW module. Perhaps they aren't. If they
>> aren't though, I wonder what this "vc4" module actually represents in HW?
>
> It's the subsystem, same as we use a subsystem node for msm, sti,
> rockchip, imx, and exynos.  This appears to be the common model of how
> the collection of graphics-related components is represented in the DT.

I think most of these bindings are wrong. They are grouped together
because that is what DRM wants not because that reflects the h/w. So
convince me this is one block, not that it is what other people do.

Rob


From robherring2 at gmail.com  Mon Aug 24 06:56:14 2015
From: robherring2 at gmail.com (Rob Herring)
Date: Mon, 24 Aug 2015 08:56:14 -0500
Subject: [PATCH 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <1439427380-2436-2-git-send-email-eric@anholt.net>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-2-git-send-email-eric@anholt.net>
Message-ID: <CAL_JsqKhfQS46NwbTq0PNDpnNuztnRkcK+WDhFMaayE_8dDt3Q@mail.gmail.com>

On Wed, Aug 12, 2015 at 7:56 PM, Eric Anholt <eric at anholt.net> wrote:
> Signed-off-by: Eric Anholt <eric at anholt.net>
> ---
>  .../devicetree/bindings/gpu/brcm,bcm-vc4.txt       | 83 ++++++++++++++++++++++
>  1 file changed, 83 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
>
> diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
> new file mode 100644
> index 0000000..2b13e61
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
> @@ -0,0 +1,83 @@
> +Broadcom VC4 GPU
> +
> +The VC4 device present on the Raspberry Pi includes a display system
> +with HDMI output and the HVS scaler for compositing display planes.
> +
> +Required properties for VC4:
> +- compatible:  Should be "brcm,vc4"
> +- crtcs:       List of references to pixelvalve scanout engines
> +- hvss:                List of references to HVS video scalers
> +- encoders:    List of references to output encoders (HDMI, SDTV)

Creating these links is what the OF graph binding is for. Please use
it. Plus this is a DRMism in the binding.

> +
> +Required properties for Pixel Valve:
> +- compatible:  Should be "brcm,vc4-pixelvalve"

There's only one version of IP and all chips have the same bugs? You
should have chip names in the compatible strings.

> +- reg:         Physical base address and length of the PV's registers
> +- interrupts:  The interrupt number
> +                 See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
> +
> +Required properties for HVS:
> +- compatible:  Should be "brcm,vc4-hvs"
> +- reg:         Physical base address and length of the HVS's registers
> +- interrupts:  The interrupt number
> +                 See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
> +
> +Required properties for HDMI
> +- compatible:  Should be "brcm,vc4-hdmi"
> +- reg:         Physical base address and length of the two register ranges
> +                 ("HDMI" and "HD")
> +- interrupts:  The interrupt numbers
> +                 See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
> +- ddc:         phandle of the I2C controller used for DDC EDID probing
> +- crtc:                phandle to the pixelvalve CRTC the HDMI encoder is attached to

Same comment about OF graph.

> +
> +Optional properties for HDMI:
> +- hpd-gpio:    The GPIO pin for HDMI hotplug detect (if it doesn't appear
> +                 as an interrupt/status bit in the HDMI controller
> +                 itself).  See bindings/pinctrl/brcm,bcm2835-gpio.txt

Use the hdmi-connector binding. This doesn't belong here. The clue is
your comment in parenthesis.

Rob


From eric at anholt.net  Mon Aug 24 17:31:38 2015
From: eric at anholt.net (Eric Anholt)
Date: Mon, 24 Aug 2015 17:31:38 -0700
Subject: [PATCH v5 1/3] clk: bcm2835: Add binding docs for the Raspberry
 Pi clock provider
In-Reply-To: <1439507142-2965-2-git-send-email-eric@anholt.net>
References: <1439507142-2965-1-git-send-email-eric@anholt.net>
 <1439507142-2965-2-git-send-email-eric@anholt.net>
Message-ID: <87vbc4us2d.fsf@eliezer.anholt.net>

Eric Anholt <eric at anholt.net> writes:

> The hardware clocks are not controllable by the ARM, so we have to
> make requests to the firmware to do so from the VPU side.  This will
> let us replace fixed clocks in our DT with actual clock control (and
> correct frequency information).

Gordon from the Raspberry Pi Foundation just asked me "what do you mean,
you can't access the clocks from the ARM?"  I'd been assured I couldn't
by another developer (who was originally going to write a Linux driver
for this), and my own testing had also indicated I couldn't, but after a
new round of hacking together some tests, I see things that look a lot
like clockman registers.

Looks like we're going to get a native driver, instead.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150824/8ff89ce0/attachment.sig>

From daniel at ffwll.ch  Tue Aug 25 01:44:23 2015
From: daniel at ffwll.ch (Daniel Vetter)
Date: Tue, 25 Aug 2015 10:44:23 +0200
Subject: [PATCH v2 3/7] drm/vc4: Add KMS support for Raspberry Pi.
In-Reply-To: <1439934848-7196-4-git-send-email-eric@anholt.net>
References: <1439934848-7196-1-git-send-email-eric@anholt.net>
 <1439934848-7196-4-git-send-email-eric@anholt.net>
Message-ID: <20150825084423.GU20434@phenom.ffwll.local>

On Tue, Aug 18, 2015 at 02:54:04PM -0700, Eric Anholt wrote:
> This is the start of a full VC4 driver.  Right now this just supports
> configuring the display using a pre-existing video mode (because
> changing the pixel clock isn't available yet, and doesn't work when it
> is).  However, this is enough for fbcon and bringing up X using
> xf86-video-modesetting.
> 
> Signed-off-by: Eric Anholt <eric at anholt.net>
> ---
> 
> v2: Drop FB_HELPER select thanks to Archit's patches.  Do manual init
>     ordering instead of using the .load hook.  Structure registration
>     more like tegra's, but still using the typical "component" code.
>     Drop no-op hooks for atomic_begin and mode_fixup() now that
>     they're optional.  Drop sentinel in Makefile.  Fix minor style
>     nits I noticed on another reread.

fwiw Acked-by: Daniel Vetter <daniel.vetter at ffwll.ch> since Eric has done
all the changes I requested - simply forgot to add that in my earlier
review.

No full r-b since I don't have a clue about the hw (and didn't take a
close look at the hw touching code hence).

Cheers, Daniel

> 
>  drivers/gpu/drm/Kconfig           |   2 +
>  drivers/gpu/drm/Makefile          |   1 +
>  drivers/gpu/drm/vc4/Kconfig       |  13 +
>  drivers/gpu/drm/vc4/Makefile      |  17 +
>  drivers/gpu/drm/vc4/vc4_bo.c      |  52 ++++
>  drivers/gpu/drm/vc4/vc4_crtc.c    | 565 ++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/vc4/vc4_debugfs.c |  38 +++
>  drivers/gpu/drm/vc4/vc4_drv.c     | 271 ++++++++++++++++
>  drivers/gpu/drm/vc4/vc4_drv.h     | 120 ++++++++
>  drivers/gpu/drm/vc4/vc4_hdmi.c    | 633 ++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/vc4/vc4_hvs.c     | 161 ++++++++++
>  drivers/gpu/drm/vc4/vc4_kms.c     |  84 +++++
>  drivers/gpu/drm/vc4/vc4_plane.c   | 320 +++++++++++++++++++
>  drivers/gpu/drm/vc4/vc4_regs.h    | 562 +++++++++++++++++++++++++++++++++
>  14 files changed, 2839 insertions(+)
>  create mode 100644 drivers/gpu/drm/vc4/Kconfig
>  create mode 100644 drivers/gpu/drm/vc4/Makefile
>  create mode 100644 drivers/gpu/drm/vc4/vc4_bo.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_crtc.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_debugfs.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_drv.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_drv.h
>  create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_hvs.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_kms.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_plane.c
>  create mode 100644 drivers/gpu/drm/vc4/vc4_regs.h
> 
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 06ae500..19bb0db 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -260,3 +260,5 @@ source "drivers/gpu/drm/sti/Kconfig"
>  source "drivers/gpu/drm/amd/amdkfd/Kconfig"
>  
>  source "drivers/gpu/drm/imx/Kconfig"
> +
> +source "drivers/gpu/drm/vc4/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 8858510..283ed8e 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -42,6 +42,7 @@ obj-$(CONFIG_DRM_MGA)	+= mga/
>  obj-$(CONFIG_DRM_I810)	+= i810/
>  obj-$(CONFIG_DRM_I915)  += i915/
>  obj-$(CONFIG_DRM_MGAG200) += mgag200/
> +obj-$(CONFIG_DRM_VC4)  += vc4/
>  obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/
>  obj-$(CONFIG_DRM_SIS)   += sis/
>  obj-$(CONFIG_DRM_SAVAGE)+= savage/
> diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig
> new file mode 100644
> index 0000000..e810ef7
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/Kconfig
> @@ -0,0 +1,13 @@
> +config DRM_VC4
> +	tristate "Broadcom VC4 Graphics"
> +	depends on ARCH_BCM2835
> +	depends on DRM
> +	select DRM_KMS_HELPER
> +	select DRM_KMS_CMA_HELPER
> +	help
> +	  Choose this option if you have a system that has a Broadcom
> +	  VC4 GPU, such as the Raspberry Pi or other BCM2708/BCM2835.
> +
> +	  This driver requires that "avoid_warnings=2" be present in
> +	  the config.txt for the firmware, to keep it from smashing
> +	  our display setup.
> diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile
> new file mode 100644
> index 0000000..32b4f9c
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/Makefile
> @@ -0,0 +1,17 @@
> +ccflags-y := -Iinclude/drm
> +
> +# Please keep these build lists sorted!
> +
> +# core driver code
> +vc4-y := \
> +	vc4_bo.o \
> +	vc4_crtc.o \
> +	vc4_drv.o \
> +	vc4_kms.o \
> +	vc4_hdmi.o \
> +	vc4_hvs.o \
> +	vc4_plane.o
> +
> +vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o
> +
> +obj-$(CONFIG_DRM_VC4)  += vc4.o
> diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
> new file mode 100644
> index 0000000..ab9f510
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_bo.c
> @@ -0,0 +1,52 @@
> +/*
> + *  Copyright ? 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +/* DOC: VC4 GEM BO management support.
> + *
> + * The VC4 GPU architecture (both scanout and rendering) has direct
> + * access to system memory with no MMU in between.  To support it, we
> + * use the GEM CMA helper functions to allocate contiguous ranges of
> + * physical memory for our BOs.
> + */
> +
> +#include "vc4_drv.h"
> +
> +struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size)
> +{
> +	struct drm_gem_cma_object *cma_obj;
> +
> +	cma_obj = drm_gem_cma_create(dev, size);
> +	if (IS_ERR(cma_obj))
> +		return NULL;
> +	else
> +		return to_vc4_bo(&cma_obj->base);
> +}
> +
> +int vc4_dumb_create(struct drm_file *file_priv,
> +		    struct drm_device *dev,
> +		    struct drm_mode_create_dumb *args)
> +{
> +	int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
> +	struct vc4_bo *bo = NULL;
> +	int ret;
> +
> +	if (args->pitch < min_pitch)
> +		args->pitch = min_pitch;
> +
> +	if (args->size < args->pitch * args->height)
> +		args->size = args->pitch * args->height;
> +
> +	bo = vc4_bo_create(dev, roundup(args->size, PAGE_SIZE));
> +	if (!bo)
> +		return -ENOMEM;
> +
> +	ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle);
> +	drm_gem_object_unreference_unlocked(&bo->base.base);
> +
> +	return ret;
> +}
> diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
> new file mode 100644
> index 0000000..2c70faf
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
> @@ -0,0 +1,565 @@
> +/*
> + * Copyright (C) 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +/**
> + * DOC: VC4 CRTC module
> + *
> + * In VC4, the Pixel Valve is what most closely corresponds to the
> + * DRM's concept of a CRTC.  The PV generates video timings from the
> + * pixel clock and its configuration.
> + *
> + * However, the DRM CRTC also collects the configuration of all the
> + * DRM planes attached to it.  As a result, this file also manages
> + * setup of the VC4 HVS's display elements on the CRTC.
> + */
> +
> +#include "drm_atomic_helper.h"
> +#include "drm_crtc_helper.h"
> +#include "linux/clk.h"
> +#include "linux/component.h"
> +#include "vc4_drv.h"
> +#include "vc4_regs.h"
> +
> +#define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset))
> +#define CRTC_READ(offset) readl(vc4_crtc->regs + (offset))
> +
> +#define CRTC_REG(reg) { reg, #reg }
> +static const struct {
> +	u32 reg;
> +	const char *name;
> +} crtc_regs[] = {
> +	CRTC_REG(PV_CONTROL),
> +	CRTC_REG(PV_V_CONTROL),
> +	CRTC_REG(PV_VSYNCD),
> +	CRTC_REG(PV_HORZA),
> +	CRTC_REG(PV_HORZB),
> +	CRTC_REG(PV_VERTA),
> +	CRTC_REG(PV_VERTB),
> +	CRTC_REG(PV_VERTA_EVEN),
> +	CRTC_REG(PV_VERTB_EVEN),
> +	CRTC_REG(PV_INTEN),
> +	CRTC_REG(PV_INTSTAT),
> +	CRTC_REG(PV_STAT),
> +	CRTC_REG(PV_HACT_ACT),
> +};
> +
> +static void vc4_crtc_dump_regs(struct vc4_crtc *vc4_crtc)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(crtc_regs); i++) {
> +		DRM_INFO("0x%04x (%s): 0x%08x\n",
> +			 crtc_regs[i].reg, crtc_regs[i].name,
> +			 CRTC_READ(crtc_regs[i].reg));
> +	}
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +int vc4_crtc_debugfs_regs(struct seq_file *m, void *unused)
> +{
> +	struct drm_info_node *node = (struct drm_info_node *) m->private;
> +	struct drm_device *dev = node->minor->dev;
> +	int crtc_index = (uintptr_t)node->info_ent->data;
> +	struct drm_crtc *crtc;
> +	struct vc4_crtc *vc4_crtc;
> +	int i;
> +
> +	i = 0;
> +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> +		if (i == crtc_index)
> +			break;
> +		i++;
> +	}
> +	if (!crtc)
> +		return 0;
> +	vc4_crtc = to_vc4_crtc(crtc);
> +
> +	for (i = 0; i < ARRAY_SIZE(crtc_regs); i++) {
> +		seq_printf(m, "%s (0x%04x): 0x%08x\n",
> +			   crtc_regs[i].name, crtc_regs[i].reg,
> +			   CRTC_READ(crtc_regs[i].reg));
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static void vc4_crtc_destroy(struct drm_crtc *crtc)
> +{
> +	drm_crtc_cleanup(crtc);
> +}
> +
> +static u32 vc4_get_fifo_full_level(u32 format)
> +{
> +	static const u32 fifo_len_bytes = 64;
> +	static const u32 hvs_latency_pix = 6;
> +
> +	switch (format) {
> +	case PV_CONTROL_FORMAT_DSIV_16:
> +	case PV_CONTROL_FORMAT_DSIC_16:
> +		return fifo_len_bytes - 2 * hvs_latency_pix;
> +	case PV_CONTROL_FORMAT_DSIV_18:
> +		return fifo_len_bytes - 14;
> +	case PV_CONTROL_FORMAT_24:
> +	case PV_CONTROL_FORMAT_DSIV_24:
> +	default:
> +		return fifo_len_bytes - 3 * hvs_latency_pix;
> +	}
> +}
> +
> +static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
> +{
> +	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> +	struct drm_crtc_state *state = crtc->state;
> +	struct drm_display_mode *mode = &state->adjusted_mode;
> +	u32 vactive = (mode->vdisplay >>
> +		       ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0));
> +	u32 format = PV_CONTROL_FORMAT_24;
> +	bool debug_dump_regs = false;
> +
> +	if (debug_dump_regs) {
> +		DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc));
> +		vc4_crtc_dump_regs(vc4_crtc);
> +	}
> +
> +	/* This is where we would set the pixel clock. */
> +
> +	/* Reset the PV fifo. */
> +	CRTC_WRITE(PV_CONTROL, 0);
> +	CRTC_WRITE(PV_CONTROL, PV_CONTROL_FIFO_CLR | PV_CONTROL_EN);
> +	CRTC_WRITE(PV_CONTROL, 0);
> +
> +	CRTC_WRITE(PV_HORZA,
> +		   VC4_SET_FIELD(mode->htotal - mode->hsync_end,
> +				 PV_HORZA_HBP) |
> +		   VC4_SET_FIELD(mode->hsync_end - mode->hsync_start,
> +				 PV_HORZA_HSYNC));
> +	CRTC_WRITE(PV_HORZB,
> +		   VC4_SET_FIELD(mode->hsync_start - mode->hdisplay,
> +				 PV_HORZB_HFP) |
> +		   VC4_SET_FIELD(mode->hdisplay, PV_HORZB_HACTIVE));
> +
> +	CRTC_WRITE(PV_VERTA,
> +		   VC4_SET_FIELD(mode->vtotal - mode->vsync_end,
> +				 PV_VERTA_VBP) |
> +		   VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
> +				 PV_VERTA_VSYNC));
> +	CRTC_WRITE(PV_VERTB,
> +		   VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
> +				 PV_VERTB_VFP) |
> +		   VC4_SET_FIELD(vactive, PV_VERTB_VACTIVE));
> +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
> +		/* Write PV_VERTA_EVEN/VERTB_EVEN */
> +	}
> +
> +	CRTC_WRITE(PV_HACT_ACT, mode->hdisplay);
> +
> +	CRTC_WRITE(PV_V_CONTROL,
> +		   PV_VCONTROL_CONTINUOUS);
> +
> +	CRTC_WRITE(PV_CONTROL,
> +		   VC4_SET_FIELD(format, PV_CONTROL_FORMAT) |
> +		   VC4_SET_FIELD(vc4_get_fifo_full_level(format),
> +				 PV_CONTROL_FIFO_LEVEL) |
> +		   PV_CONTROL_CLR_AT_START |
> +		   PV_CONTROL_TRIGGER_UNDERFLOW |
> +		   PV_CONTROL_WAIT_HSTART |
> +		   VC4_SET_FIELD(PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI,
> +				 PV_CONTROL_CLK_SELECT) |
> +		   PV_CONTROL_FIFO_CLR |
> +		   PV_CONTROL_EN);
> +
> +	if (debug_dump_regs) {
> +		DRM_INFO("CRTC %d regs after:\n", drm_crtc_index(crtc));
> +		vc4_crtc_dump_regs(vc4_crtc);
> +	}
> +}
> +
> +static void require_hvs_enabled(struct drm_device *dev)
> +{
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +
> +	WARN_ON_ONCE((HVS_READ(SCALER_DISPCTRL) & SCALER_DISPCTRL_ENABLE) !=
> +		     SCALER_DISPCTRL_ENABLE);
> +}
> +
> +static void vc4_crtc_disable(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> +
> +	require_hvs_enabled(dev);
> +
> +	CRTC_WRITE(PV_V_CONTROL,
> +		   CRTC_READ(PV_V_CONTROL) & ~PV_VCONTROL_VIDEN);
> +	while (CRTC_READ(PV_V_CONTROL) & PV_VCONTROL_VIDEN)
> +		cpu_relax();
> +
> +	/* Without a wait here, we end up with a black screen. */
> +	msleep(30);
> +
> +	if (HVS_READ(SCALER_DISPCTRLX(vc4_crtc->channel)) &
> +	    SCALER_DISPCTRLX_ENABLE) {
> +		HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel),
> +			  SCALER_DISPCTRLX_RESET);
> +
> +		/* While the docs say that reset is self-clearing, it
> +		 * seems it doesn't actually.
> +		 */
> +		HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel), 0);
> +	}
> +
> +	/* Once we leave, the scaler should be disabled and its fifo empty. */
> +
> +	WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(vc4_crtc->channel)) &
> +		     SCALER_DISPCTRLX_RESET);
> +
> +	WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(vc4_crtc->channel)),
> +				   SCALER_DISPSTATX_MODE) !=
> +		     SCALER_DISPSTATX_MODE_DISABLED);
> +
> +	WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(vc4_crtc->channel)) &
> +		      (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) !=
> +		     SCALER_DISPSTATX_EMPTY);
> +}
> +
> +static void vc4_crtc_enable(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> +	struct drm_crtc_state *state = crtc->state;
> +	struct drm_display_mode *mode = &state->adjusted_mode;
> +
> +	require_hvs_enabled(dev);
> +
> +	/* Turn on the scaler, which will wait for vstart to start
> +	 * compositing.
> +	 */
> +	HVS_WRITE(SCALER_DISPCTRLX(vc4_crtc->channel),
> +		  VC4_SET_FIELD(mode->hdisplay, SCALER_DISPCTRLX_WIDTH) |
> +		  VC4_SET_FIELD(mode->vdisplay, SCALER_DISPCTRLX_HEIGHT) |
> +		  SCALER_DISPCTRLX_ENABLE);
> +
> +	/* Turn on the pixel valve, which will emit the vstart signal. */
> +	CRTC_WRITE(PV_V_CONTROL,
> +		   CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN);
> +}
> +
> +static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
> +				 struct drm_crtc_state *state)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct drm_plane *plane;
> +	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> +	u32 dlist_count = 0;
> +
> +	drm_atomic_crtc_state_for_each_plane(plane, state) {
> +		struct drm_plane_state *plane_state =
> +			state->state->plane_states[drm_plane_index(plane)];
> +
> +		/* plane might not have changed, in which case take
> +		 * current state:
> +		 */
> +		if (!plane_state)
> +			plane_state = plane->state;
> +
> +		dlist_count += vc4_plane_dlist_size(plane_state);
> +	}
> +
> +	dlist_count++; /* Account for SCALER_CTL0_END. */
> +
> +	if (!vc4_crtc->dlist || dlist_count > vc4_crtc->dlist_size) {
> +		vc4_crtc->dlist = ((u32 __iomem *)vc4->hvs->dlist +
> +				   HVS_BOOTLOADER_DLIST_END);
> +		vc4_crtc->dlist_size = ((SCALER_DLIST_SIZE >> 2) -
> +					HVS_BOOTLOADER_DLIST_END);
> +
> +		if (dlist_count > vc4_crtc->dlist_size) {
> +			DRM_DEBUG_KMS("dlist too large for CRTC (%d > %d).\n",
> +				      dlist_count, vc4_crtc->dlist_size);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void vc4_crtc_atomic_flush(struct drm_crtc *crtc,
> +				  struct drm_crtc_state *old_state)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> +	struct drm_plane *plane;
> +	bool debug_dump_regs = false;
> +	u32 __iomem *dlist_next = vc4_crtc->dlist;
> +
> +	if (debug_dump_regs) {
> +		DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
> +		vc4_hvs_dump_state(dev);
> +	}
> +
> +	/* Copy all the active planes' dlist contents to the hardware dlist.
> +	 *
> +	 * XXX: If the new display list was large enough that it
> +	 * overlapped a currently-read display list, we need to do
> +	 * something like disable scanout before putting in the new
> +	 * list.  For now, we're safe because we only have the two
> +	 * planes.
> +	 */
> +	drm_atomic_crtc_for_each_plane(plane, crtc) {
> +		dlist_next += vc4_plane_write_dlist(plane, dlist_next);
> +	}
> +
> +	if (dlist_next == vc4_crtc->dlist) {
> +		/* If no planes were enabled, use the SCALER_CTL0_END
> +		 * at the start of the display list memory (in the
> +		 * bootloader section).  We'll rewrite that
> +		 * SCALER_CTL0_END, just in case, though.
> +		 */
> +		writel(SCALER_CTL0_END, vc4->hvs->dlist);
> +		HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), 0);
> +	} else {
> +		writel(SCALER_CTL0_END, dlist_next);
> +		dlist_next++;
> +
> +		HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel),
> +			  (u32 *)vc4_crtc->dlist - (u32 *)vc4->hvs->dlist);
> +
> +		/* Make the next display list start after ours. */
> +		vc4_crtc->dlist_size -= (dlist_next - vc4_crtc->dlist);
> +		vc4_crtc->dlist = dlist_next;
> +	}
> +
> +	if (debug_dump_regs) {
> +		DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
> +		vc4_hvs_dump_state(dev);
> +	}
> +
> +	if (crtc->state->event) {
> +		unsigned long flags;
> +
> +		crtc->state->event->pipe = drm_crtc_index(crtc);
> +
> +		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
> +
> +		spin_lock_irqsave(&dev->event_lock, flags);
> +		vc4_crtc->event = crtc->state->event;
> +		spin_unlock_irqrestore(&dev->event_lock, flags);
> +		crtc->state->event = NULL;
> +	}
> +}
> +
> +int vc4_enable_vblank(struct drm_device *dev, int crtc_id)
> +{
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
> +
> +	CRTC_WRITE(PV_INTEN, PV_INT_VFP_START);
> +
> +	return 0;
> +}
> +
> +void vc4_disable_vblank(struct drm_device *dev, int crtc_id)
> +{
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id];
> +
> +	CRTC_WRITE(PV_INTEN, 0);
> +}
> +
> +static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
> +{
> +	struct drm_crtc *crtc = &vc4_crtc->base;
> +	struct drm_device *dev = crtc->dev;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&dev->event_lock, flags);
> +	if (vc4_crtc->event) {
> +		drm_crtc_send_vblank_event(crtc, vc4_crtc->event);
> +		vc4_crtc->event = NULL;
> +	}
> +	spin_unlock_irqrestore(&dev->event_lock, flags);
> +}
> +
> +static irqreturn_t vc4_crtc_irq_handler(int irq, void *data)
> +{
> +	struct vc4_crtc *vc4_crtc = data;
> +	u32 stat = CRTC_READ(PV_INTSTAT);
> +	irqreturn_t ret = IRQ_NONE;
> +
> +	if (stat & PV_INT_VFP_START) {
> +		CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
> +		drm_crtc_handle_vblank(&vc4_crtc->base);
> +		vc4_crtc_handle_page_flip(vc4_crtc);
> +		ret = IRQ_HANDLED;
> +	}
> +
> +	return ret;
> +}
> +
> +static const struct drm_crtc_funcs vc4_crtc_funcs = {
> +	.set_config = drm_atomic_helper_set_config,
> +	.destroy = vc4_crtc_destroy,
> +	.page_flip = drm_atomic_helper_page_flip,
> +	.set_property = NULL,
> +	.cursor_set = NULL, /* handled by drm_mode_cursor_universal */
> +	.cursor_move = NULL, /* handled by drm_mode_cursor_universal */
> +	.reset = drm_atomic_helper_crtc_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
> +};
> +
> +static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
> +	.mode_set_nofb = vc4_crtc_mode_set_nofb,
> +	.disable = vc4_crtc_disable,
> +	.enable = vc4_crtc_enable,
> +	.atomic_check = vc4_crtc_atomic_check,
> +	.atomic_flush = vc4_crtc_atomic_flush,
> +};
> +
> +/* Frees the page flip event when the DRM device is closed with the
> + * event still outstanding.
> + */
> +void vc4_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file)
> +{
> +	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> +	struct drm_device *dev = crtc->dev;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&dev->event_lock, flags);
> +
> +	if (vc4_crtc->event && vc4_crtc->event->base.file_priv == file) {
> +		vc4_crtc->event->base.destroy(&vc4_crtc->event->base);
> +		drm_crtc_vblank_put(crtc);
> +		vc4_crtc->event = NULL;
> +	}
> +
> +	spin_unlock_irqrestore(&dev->event_lock, flags);
> +}
> +
> +static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = dev_get_drvdata(master);
> +	struct vc4_dev *vc4 = to_vc4_dev(drm);
> +	struct vc4_crtc *vc4_crtc;
> +	struct drm_crtc *crtc;
> +	struct drm_plane *primary_plane, *cursor_plane;
> +	int ret;
> +
> +	/* For now, we create just the primary and the legacy cursor
> +	 * planes.  We should be able to stack more planes on easily,
> +	 * but to do that we would need to compute the bandwidth
> +	 * requirement of the plane configuration, and reject ones
> +	 * that will take too much.
> +	 */
> +	primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY);
> +	if (!primary_plane) {
> +		dev_err(dev, "failed to construct primary plane\n");
> +		ret = PTR_ERR(primary_plane);
> +		goto fail;
> +	}
> +
> +	cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
> +	if (!cursor_plane) {
> +		dev_err(dev, "failed to construct cursor plane\n");
> +		ret = PTR_ERR(cursor_plane);
> +		goto fail;
> +	}
> +
> +	vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
> +	if (!vc4_crtc)
> +		return -ENOMEM;
> +	crtc = &vc4_crtc->base;
> +
> +	vc4_crtc->regs = vc4_ioremap_regs(pdev, 0);
> +	if (IS_ERR(vc4_crtc->regs))
> +		return PTR_ERR(vc4_crtc->regs);
> +
> +	drm_crtc_init_with_planes(drm, crtc, primary_plane, cursor_plane,
> +				  &vc4_crtc_funcs);
> +	drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs);
> +	primary_plane->crtc = crtc;
> +	cursor_plane->crtc = crtc;
> +	vc4->crtc[drm_crtc_index(crtc)] = vc4_crtc;
> +
> +	/* Until we have full scanout setup to route things through to
> +	 * encoders, line things up like the firmware did.
> +	 */
> +	switch (drm_crtc_index(crtc)) {
> +	case 0:
> +		vc4_crtc->channel = 0;
> +		break;
> +	case 1:
> +		vc4_crtc->channel = 2;
> +		break;
> +	default:
> +	case 2:
> +		vc4_crtc->channel = 1;
> +		break;
> +	}
> +
> +	CRTC_WRITE(PV_INTEN, 0);
> +	CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START);
> +	ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
> +			       vc4_crtc_irq_handler, 0, "vc4 crtc", vc4_crtc);
> +
> +	platform_set_drvdata(pdev, vc4_crtc);
> +
> +	return 0;
> +
> +fail:
> +	return ret;
> +}
> +
> +static void vc4_crtc_unbind(struct device *dev, struct device *master,
> +			    void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct vc4_crtc *vc4_crtc = dev_get_drvdata(dev);
> +
> +	vc4_crtc_destroy(&vc4_crtc->base);
> +
> +	CRTC_WRITE(PV_INTEN, 0);
> +
> +	platform_set_drvdata(pdev, NULL);
> +}
> +
> +static const struct component_ops vc4_crtc_ops = {
> +	.bind   = vc4_crtc_bind,
> +	.unbind = vc4_crtc_unbind,
> +};
> +
> +static int vc4_crtc_dev_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &vc4_crtc_ops);
> +}
> +
> +static int vc4_crtc_dev_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &vc4_crtc_ops);
> +	return 0;
> +}
> +
> +static const struct of_device_id vc4_crtc_dt_match[] = {
> +	{ .compatible = "brcm,vc4-pixelvalve" },
> +	{}
> +};
> +
> +struct platform_driver vc4_crtc_driver = {
> +	.probe = vc4_crtc_dev_probe,
> +	.remove = vc4_crtc_dev_remove,
> +	.driver = {
> +		.name = "vc4_crtc",
> +		.of_match_table = vc4_crtc_dt_match,
> +	},
> +};
> diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c
> new file mode 100644
> index 0000000..c557039
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c
> @@ -0,0 +1,38 @@
> +/*
> + *  Copyright ? 2014 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/seq_file.h>
> +#include <linux/circ_buf.h>
> +#include <linux/ctype.h>
> +#include <linux/debugfs.h>
> +#include <drm/drmP.h>
> +
> +#include "vc4_drv.h"
> +#include "vc4_regs.h"
> +
> +static const struct drm_info_list vc4_debugfs_list[] = {
> +	{"hdmi_regs", vc4_hdmi_debugfs_regs, 0},
> +	{"hvs_regs", vc4_hvs_debugfs_regs, 0},
> +	{"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0},
> +	{"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1},
> +	{"crtc2_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)2},
> +};
> +#define VC4_DEBUGFS_ENTRIES ARRAY_SIZE(vc4_debugfs_list)
> +
> +int
> +vc4_debugfs_init(struct drm_minor *minor)
> +{
> +	return drm_debugfs_create_files(vc4_debugfs_list, VC4_DEBUGFS_ENTRIES,
> +					minor->debugfs_root, minor);
> +}
> +
> +void
> +vc4_debugfs_cleanup(struct drm_minor *minor)
> +{
> +	drm_debugfs_remove_files(vc4_debugfs_list, VC4_DEBUGFS_ENTRIES, minor);
> +}
> diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
> new file mode 100644
> index 0000000..d30aefa
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_drv.c
> @@ -0,0 +1,271 @@
> +/*
> + * Copyright (C) 2014-2015 Broadcom
> + * Copyright (C) 2013 Red Hat
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +
> +#include "vc4_drv.h"
> +#include "vc4_regs.h"
> +
> +#define DRIVER_NAME "vc4"
> +#define DRIVER_DESC "Broadcom VC4 graphics"
> +#define DRIVER_DATE "20140616"
> +#define DRIVER_MAJOR 0
> +#define DRIVER_MINOR 0
> +#define DRIVER_PATCHLEVEL 0
> +
> +/* Helper function for mapping the regs on a platform device. */
> +void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index)
> +{
> +	struct resource *res;
> +	void __iomem *map;
> +
> +	res = platform_get_resource(dev, IORESOURCE_MEM, index);
> +	map = devm_ioremap_resource(&dev->dev, res);
> +	if (IS_ERR(map)) {
> +		DRM_ERROR("Failed to map registers: %ld\n", PTR_ERR(map));
> +		return map;
> +	}
> +
> +	return map;
> +}
> +
> +static void vc4_drm_preclose(struct drm_device *dev, struct drm_file *file)
> +{
> +	struct drm_crtc *crtc;
> +
> +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
> +		vc4_cancel_page_flip(crtc, file);
> +}
> +
> +static const struct file_operations vc4_drm_fops = {
> +	.owner = THIS_MODULE,
> +	.open = drm_open,
> +	.release = drm_release,
> +	.unlocked_ioctl = drm_ioctl,
> +	.mmap = drm_gem_cma_mmap,
> +	.poll = drm_poll,
> +	.read = drm_read,
> +#ifdef CONFIG_COMPAT
> +	.compat_ioctl = drm_compat_ioctl,
> +#endif
> +	.llseek = noop_llseek,
> +};
> +
> +static const struct drm_ioctl_desc vc4_drm_ioctls[] = {
> +};
> +
> +static struct drm_driver vc4_drm_driver = {
> +	.driver_features = (DRIVER_MODESET |
> +			    DRIVER_ATOMIC |
> +			    DRIVER_GEM |
> +			    DRIVER_PRIME),
> +	.preclose = vc4_drm_preclose,
> +
> +	.enable_vblank = vc4_enable_vblank,
> +	.disable_vblank = vc4_disable_vblank,
> +	.get_vblank_counter = drm_vblank_count,
> +
> +#if defined(CONFIG_DEBUG_FS)
> +	.debugfs_init = vc4_debugfs_init,
> +	.debugfs_cleanup = vc4_debugfs_cleanup,
> +#endif
> +
> +	.gem_free_object = drm_gem_cma_free_object,
> +	.gem_vm_ops = &drm_gem_cma_vm_ops,
> +
> +	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> +	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> +	.gem_prime_import = drm_gem_prime_import,
> +	.gem_prime_export = drm_gem_prime_export,
> +	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
> +	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
> +	.gem_prime_vmap = drm_gem_cma_prime_vmap,
> +	.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
> +	.gem_prime_mmap = drm_gem_cma_prime_mmap,
> +
> +	.dumb_create = vc4_dumb_create,
> +	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
> +	.dumb_destroy = drm_gem_dumb_destroy,
> +
> +	.ioctls = vc4_drm_ioctls,
> +	.num_ioctls = ARRAY_SIZE(vc4_drm_ioctls),
> +	.fops = &vc4_drm_fops,
> +
> +	.name = DRIVER_NAME,
> +	.desc = DRIVER_DESC,
> +	.date = DRIVER_DATE,
> +	.major = DRIVER_MAJOR,
> +	.minor = DRIVER_MINOR,
> +	.patchlevel = DRIVER_PATCHLEVEL,
> +};
> +
> +static int vc4_drm_bind(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm;
> +	struct vc4_dev *vc4;
> +	int ret = 0;
> +
> +	dev->coherent_dma_mask = DMA_BIT_MASK(32);
> +
> +	vc4 = devm_kzalloc(dev, sizeof(*vc4), GFP_KERNEL);
> +	if (!vc4)
> +		return -ENOMEM;
> +
> +	drm = drm_dev_alloc(&vc4_drm_driver, dev);
> +	if (!drm)
> +		return -ENOMEM;
> +	platform_set_drvdata(pdev, drm);
> +	vc4->dev = drm;
> +	drm->dev_private = vc4;
> +
> +	drm_dev_set_unique(drm, dev_name(dev));
> +
> +	drm_mode_config_init(drm);
> +	if (ret)
> +		goto unref;
> +
> +	ret = component_bind_all(dev, drm);
> +	if (ret)
> +		goto unref;
> +
> +	ret = drm_dev_register(drm, 0);
> +	if (ret < 0)
> +		goto unbind_all;
> +
> +	vc4_kms_load(drm);
> +
> +	return 0;
> +unbind_all:
> +	component_unbind_all(dev, drm);
> +unref:
> +	drm_dev_unref(drm);
> +	return ret;
> +
> +	return 0;
> +}
> +
> +static void vc4_drm_unbind(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = platform_get_drvdata(pdev);
> +
> +	drm_mode_config_cleanup(drm);
> +
> +	drm_put_dev(drm);
> +}
> +
> +static const struct component_master_ops vc4_drm_ops = {
> +	.bind = vc4_drm_bind,
> +	.unbind = vc4_drm_unbind,
> +};
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> +	return dev->of_node == data;
> +}
> +
> +static int add_components(struct device *dev, struct component_match **matchptr,
> +			  const char *name)
> +{
> +	struct device_node *np = dev->of_node;
> +	unsigned i;
> +
> +	for (i = 0; ; i++) {
> +		struct device_node *node;
> +
> +		node = of_parse_phandle(np, name, i);
> +		if (!node)
> +			break;
> +
> +		component_match_add(dev, matchptr, compare_of, node);
> +	}
> +
> +	return 0;
> +}
> +
> +static int vc4_platform_drm_probe(struct platform_device *pdev)
> +{
> +	struct component_match *match = NULL;
> +	struct device *dev = &pdev->dev;
> +
> +	add_components(dev, &match, "crtcs");
> +	add_components(dev, &match, "encoders");
> +	add_components(dev, &match, "hvss");
> +
> +	return component_master_add_with_match(dev, &vc4_drm_ops, match);
> +}
> +
> +static int vc4_platform_drm_remove(struct platform_device *pdev)
> +{
> +	component_master_del(&pdev->dev, &vc4_drm_ops);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id vc4_of_match[] = {
> +	{ .compatible = "brcm,vc4", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, vc4_of_match);
> +
> +static struct platform_driver vc4_platform_driver = {
> +	.probe		= vc4_platform_drm_probe,
> +	.remove		= vc4_platform_drm_remove,
> +	.driver		= {
> +		.name	= "vc4-drm",
> +		.owner	= THIS_MODULE,
> +		.of_match_table = vc4_of_match,
> +	},
> +};
> +
> +static struct platform_driver *component_drivers[] = {
> +	&vc4_hdmi_driver,
> +	&vc4_crtc_driver,
> +	&vc4_hvs_driver,
> +};
> +
> +static int __init vc4_drm_register(void)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(component_drivers); i++) {
> +		int ret = platform_driver_register(component_drivers[i]);
> +		if (ret) {
> +			while (--i >= 0)
> +				platform_driver_unregister(component_drivers[i]);
> +			return ret;
> +		}
> +	}
> +	return platform_driver_register(&vc4_platform_driver);
> +}
> +
> +static void __exit vc4_drm_unregister(void)
> +{
> +	int i;
> +
> +	for (i = ARRAY_SIZE(component_drivers) - 1; i >= 0; i--)
> +		platform_driver_unregister(component_drivers[i]);
> +
> +	platform_driver_unregister(&vc4_platform_driver);
> +}
> +
> +module_init(vc4_drm_register);
> +module_exit(vc4_drm_unregister);
> +
> +MODULE_ALIAS("platform:vc4-drm");
> +MODULE_DESCRIPTION("Broadcom VC4 DRM Driver");
> +MODULE_AUTHOR("Eric Anholt <eric at anholt.net>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
> new file mode 100644
> index 0000000..282ce0b
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_drv.h
> @@ -0,0 +1,120 @@
> +/*
> + * Copyright (C) 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include "drmP.h"
> +#include "drm_gem_cma_helper.h"
> +
> +struct vc4_dev {
> +	struct drm_device *dev;
> +
> +	struct vc4_hdmi *hdmi;
> +	struct vc4_hvs *hvs;
> +	struct vc4_crtc *crtc[3];
> +};
> +
> +static inline struct vc4_dev *
> +to_vc4_dev(struct drm_device *dev)
> +{
> +	return (struct vc4_dev *)dev->dev_private;
> +}
> +
> +struct vc4_bo {
> +	struct drm_gem_cma_object base;
> +};
> +
> +static inline struct vc4_bo *
> +to_vc4_bo(struct drm_gem_object *bo)
> +{
> +	return (struct vc4_bo *)bo;
> +}
> +
> +struct vc4_hvs {
> +	struct platform_device *pdev;
> +	void __iomem *regs;
> +	void __iomem *dlist;
> +};
> +
> +struct vc4_crtc {
> +	struct drm_crtc base;
> +	void __iomem *regs;
> +
> +	/* Which HVS channel we're using for our CRTC. */
> +	int channel;
> +
> +	/* Pointer to the actual hardware display list memory for the
> +	 * crtc.
> +	 */
> +	u32 __iomem *dlist;
> +
> +	u32 dlist_size; /* in dwords */
> +
> +	struct drm_pending_vblank_event *event;
> +};
> +
> +static inline struct vc4_crtc *
> +to_vc4_crtc(struct drm_crtc *crtc)
> +{
> +	return (struct vc4_crtc *)crtc;
> +}
> +
> +struct vc4_plane {
> +	struct drm_plane base;
> +};
> +
> +static inline struct vc4_plane *
> +to_vc4_plane(struct drm_plane *plane)
> +{
> +	return (struct vc4_plane *)plane;
> +}
> +
> +#define HVS_READ(offset) readl(vc4->hvs->regs + offset)
> +#define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset)
> +
> +/* vc4_bo.c */
> +void vc4_free_object(struct drm_gem_object *gem_obj);
> +struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size);
> +int vc4_dumb_create(struct drm_file *file_priv,
> +		    struct drm_device *dev,
> +		    struct drm_mode_create_dumb *args);
> +struct dma_buf *vc4_prime_export(struct drm_device *dev,
> +				 struct drm_gem_object *obj, int flags);
> +
> +/* vc4_crtc.c */
> +extern struct platform_driver vc4_crtc_driver;
> +int vc4_enable_vblank(struct drm_device *dev, int crtc_id);
> +void vc4_disable_vblank(struct drm_device *dev, int crtc_id);
> +void vc4_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file);
> +int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg);
> +
> +/* vc4_debugfs.c */
> +int vc4_debugfs_init(struct drm_minor *minor);
> +void vc4_debugfs_cleanup(struct drm_minor *minor);
> +
> +/* vc4_drv.c */
> +void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index);
> +
> +/* vc4_hdmi.c */
> +extern struct platform_driver vc4_hdmi_driver;
> +struct drm_encoder *vc4_hdmi_encoder_init(struct drm_device *dev);
> +struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
> +					      struct drm_encoder *encoder);
> +int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused);
> +
> +/* vc4_hvs.c */
> +extern struct platform_driver vc4_hvs_driver;
> +void vc4_hvs_dump_state(struct drm_device *dev);
> +int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused);
> +
> +/* vc4_kms.c */
> +int vc4_kms_load(struct drm_device *dev);
> +
> +/* vc4_plane.c */
> +struct drm_plane *vc4_plane_init(struct drm_device *dev,
> +				 enum drm_plane_type type);
> +u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist);
> +u32 vc4_plane_dlist_size(struct drm_plane_state *state);
> diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
> new file mode 100644
> index 0000000..cd3301b
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
> @@ -0,0 +1,633 @@
> +/*
> + * Copyright (c) 2014 The Linux Foundation. All rights reserved.
> + * Copyright (C) 2013 Red Hat
> + * Author: Rob Clark <robdclark at gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "drm_atomic_helper.h"
> +#include "drm_crtc_helper.h"
> +#include "drm_edid.h"
> +#include "linux/clk.h"
> +#include "linux/component.h"
> +#include "linux/of_gpio.h"
> +#include "linux/of_platform.h"
> +#include "vc4_drv.h"
> +#include "vc4_regs.h"
> +
> +/* General HDMI hardware state. */
> +struct vc4_hdmi {
> +	struct platform_device *pdev;
> +	struct i2c_adapter *ddc;
> +	void __iomem *hdmicore_regs;
> +	void __iomem *hd_regs;
> +	int hpd_gpio;
> +
> +	/* Probed video mode at boot time, used to filter display
> +	 * modes to only allow equivalent ones (since we can't set the
> +	 * pixel clock yet).
> +	 */
> +	struct drm_display_mode boot_mode;
> +};
> +#define HDMI_READ(offset) readl(vc4->hdmi->hdmicore_regs + offset)
> +#define HDMI_WRITE(offset, val) writel(val, vc4->hdmi->hdmicore_regs + offset)
> +#define HD_READ(offset) readl(vc4->hdmi->hd_regs + offset)
> +#define HD_WRITE(offset, val) writel(val, vc4->hdmi->hd_regs + offset)
> +
> +/* VC4 HDMI encoder KMS struct */
> +struct vc4_hdmi_encoder {
> +	struct drm_encoder base;
> +	bool hdmi_monitor;
> +};
> +static inline struct vc4_hdmi_encoder *
> +to_vc4_hdmi_encoder(struct drm_encoder *encoder)
> +{
> +	return container_of(encoder, struct vc4_hdmi_encoder, base);
> +}
> +
> +/* VC4 HDMI connector KMS struct */
> +struct vc4_hdmi_connector {
> +	struct drm_connector base;
> +
> +	/* Since the connector is attached to just the one encoder,
> +	 * this is the reference to it so we can do the best_encoder()
> +	 * hook.
> +	 */
> +	struct drm_encoder *encoder;
> +};
> +static inline struct vc4_hdmi_connector *
> +to_vc4_hdmi_connector(struct drm_connector *connector)
> +{
> +	return container_of(connector, struct vc4_hdmi_connector, base);
> +}
> +
> +#define HDMI_REG(reg) { reg, #reg }
> +static const struct {
> +	u32 reg;
> +	const char *name;
> +} hdmi_regs[] = {
> +	HDMI_REG(VC4_HDMI_CORE_REV),
> +	HDMI_REG(VC4_HDMI_SW_RESET_CONTROL),
> +	HDMI_REG(VC4_HDMI_HOTPLUG_INT),
> +	HDMI_REG(VC4_HDMI_HOTPLUG),
> +	HDMI_REG(VC4_HDMI_HORZA),
> +	HDMI_REG(VC4_HDMI_HORZB),
> +	HDMI_REG(VC4_HDMI_FIFO_CTL),
> +	HDMI_REG(VC4_HDMI_SCHEDULER_CONTROL),
> +	HDMI_REG(VC4_HDMI_VERTA0),
> +	HDMI_REG(VC4_HDMI_VERTA1),
> +	HDMI_REG(VC4_HDMI_VERTB0),
> +	HDMI_REG(VC4_HDMI_VERTB1),
> +	HDMI_REG(VC4_HDMI_TX_PHY_RESET_CTL),
> +};
> +static const struct {
> +	u32 reg;
> +	const char *name;
> +} hd_regs[] = {
> +	HDMI_REG(VC4_HD_M_CTL),
> +	HDMI_REG(VC4_HD_MAI_CTL),
> +	HDMI_REG(VC4_HD_VID_CTL),
> +	HDMI_REG(VC4_HD_CSC_CTL),
> +	HDMI_REG(VC4_HD_FRAME_COUNT),
> +};
> +
> +#ifdef CONFIG_DEBUG_FS
> +int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
> +{
> +	struct drm_info_node *node = (struct drm_info_node *) m->private;
> +	struct drm_device *dev = node->minor->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hdmi_regs); i++) {
> +		seq_printf(m, "%s (0x%04x): 0x%08x\n",
> +			   hdmi_regs[i].name, hdmi_regs[i].reg,
> +			   HDMI_READ(hdmi_regs[i].reg));
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(hd_regs); i++) {
> +		seq_printf(m, "%s (0x%04x): 0x%08x\n",
> +			   hd_regs[i].name, hd_regs[i].reg,
> +			   HD_READ(hd_regs[i].reg));
> +	}
> +
> +	return 0;
> +}
> +#endif /* CONFIG_DEBUG_FS */
> +
> +static void vc4_hdmi_dump_regs(struct drm_device *dev)
> +{
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hdmi_regs); i++) {
> +		DRM_INFO("0x%04x (%s): 0x%08x\n",
> +			 hdmi_regs[i].reg, hdmi_regs[i].name,
> +			 HDMI_READ(hdmi_regs[i].reg));
> +	}
> +	for (i = 0; i < ARRAY_SIZE(hd_regs); i++) {
> +		DRM_INFO("0x%04x (%s): 0x%08x\n",
> +			 hd_regs[i].reg, hd_regs[i].name,
> +			 HD_READ(hd_regs[i].reg));
> +	}
> +}
> +
> +static enum drm_connector_status
> +vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	struct drm_device *dev = connector->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +
> +	if (vc4->hdmi->hpd_gpio) {
> +		if (gpio_get_value(vc4->hdmi->hpd_gpio))
> +			return connector_status_connected;
> +		else
> +			return connector_status_disconnected;
> +	}
> +
> +	if (HDMI_READ(VC4_HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED)
> +		return connector_status_connected;
> +	else
> +		return connector_status_disconnected;
> +}
> +
> +static void vc4_hdmi_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +}
> +
> +static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct vc4_hdmi_connector *vc4_connector =
> +		to_vc4_hdmi_connector(connector);
> +	struct drm_encoder *encoder = vc4_connector->encoder;
> +	struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
> +	struct drm_device *dev = connector->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	int ret = 0;
> +	struct edid *edid;
> +
> +	edid = drm_get_edid(connector, vc4->hdmi->ddc);
> +	if (!edid)
> +		return -ENODEV;
> +
> +	vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
> +	drm_mode_connector_update_edid_property(connector, edid);
> +	ret = drm_add_edid_modes(connector, edid);
> +
> +	return ret;
> +}
> +
> +/* Since we can't set the pixel clock yet, filter out all the EDID
> + * modes that don't match what was set up by the firmware.
> + */
> +int vc4_hdmi_mode_valid(struct drm_connector *connector,
> +			struct drm_display_mode *mode)
> +{
> +	struct drm_device *dev = connector->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct drm_display_mode *boot_mode = &vc4->hdmi->boot_mode;
> +
> +	if (mode->hdisplay != boot_mode->hdisplay ||
> +	    mode->hsync_start != boot_mode->hsync_start ||
> +	    mode->hsync_end != boot_mode->hsync_end ||
> +	    mode->htotal != boot_mode->htotal) {
> +		return MODE_ERROR;
> +	}
> +
> +	return MODE_OK;
> +}
> +
> +static struct drm_encoder *
> +vc4_hdmi_connector_best_encoder(struct drm_connector *connector)
> +{
> +	struct vc4_hdmi_connector *hdmi_connector =
> +		to_vc4_hdmi_connector(connector);
> +	return hdmi_connector->encoder;
> +}
> +
> +static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
> +	.dpms = drm_atomic_helper_connector_dpms,
> +	.detect = vc4_hdmi_connector_detect,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.destroy = vc4_hdmi_connector_destroy,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = {
> +	.get_modes = vc4_hdmi_connector_get_modes,
> +	.mode_valid = vc4_hdmi_mode_valid,
> +	.best_encoder = vc4_hdmi_connector_best_encoder,
> +};
> +
> +struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
> +					      struct drm_encoder *encoder)
> +{
> +	struct drm_connector *connector = NULL;
> +	struct vc4_hdmi_connector *hdmi_connector;
> +	int ret = 0;
> +
> +	hdmi_connector = devm_kzalloc(dev->dev, sizeof(*hdmi_connector),
> +				      GFP_KERNEL);
> +	if (!hdmi_connector) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +	connector = &hdmi_connector->base;
> +
> +	hdmi_connector->encoder = encoder;
> +
> +	drm_connector_init(dev, connector, &vc4_hdmi_connector_funcs,
> +			   DRM_MODE_CONNECTOR_HDMIA);
> +	drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs);
> +
> +	connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
> +			     DRM_CONNECTOR_POLL_DISCONNECT);
> +
> +	connector->interlace_allowed = 0;
> +	connector->doublescan_allowed = 0;
> +
> +	drm_connector_register(connector);
> +
> +	drm_mode_connector_attach_encoder(connector, encoder);
> +
> +	return connector;
> +
> + fail:
> +	if (connector)
> +		vc4_hdmi_connector_destroy(connector);
> +
> +	return ERR_PTR(ret);
> +}
> +
> +static void vc4_encoder_destroy(struct drm_encoder *encoder)
> +{
> +	drm_encoder_cleanup(encoder);
> +}
> +
> +static const struct drm_encoder_funcs vc4_hdmi_encoder_funcs = {
> +	.destroy = vc4_encoder_destroy,
> +};
> +
> +static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> +				      struct drm_display_mode *unadjusted_mode,
> +				      struct drm_display_mode *mode)
> +{
> +	struct drm_device *dev = encoder->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	bool debug_dump_regs = false;
> +	bool hsync_pos = !(mode->flags & DRM_MODE_FLAG_NHSYNC);
> +	bool vsync_pos = !(mode->flags & DRM_MODE_FLAG_NVSYNC);
> +	u32 vactive = (mode->vdisplay >>
> +		       ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0));
> +	u32 verta = (VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
> +				   VC4_HDMI_VERTA_VSP) |
> +		     VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
> +				   VC4_HDMI_VERTA_VFP) |
> +		     VC4_SET_FIELD(vactive, VC4_HDMI_VERTA_VAL));
> +	u32 vertb = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) |
> +		     VC4_SET_FIELD(mode->vtotal - mode->vsync_end,
> +				   VC4_HDMI_VERTB_VBP));
> +
> +	if (debug_dump_regs) {
> +		DRM_INFO("HDMI regs before:\n");
> +		vc4_hdmi_dump_regs(dev);
> +	}
> +
> +	HD_WRITE(VC4_HD_VID_CTL, 0);
> +
> +	/* XXX: This is where we would set the HDMI state machine
> +	 * clock, if we had an interface for it.
> +	 */
> +
> +	HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
> +		   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
> +		   VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
> +		   VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS);
> +
> +	HDMI_WRITE(VC4_HDMI_HORZA,
> +		   (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) |
> +		   (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) |
> +		   VC4_SET_FIELD(mode->hdisplay, VC4_HDMI_HORZA_HAP));
> +
> +	HDMI_WRITE(VC4_HDMI_HORZB,
> +		   VC4_SET_FIELD(mode->htotal - mode->hsync_end,
> +				 VC4_HDMI_HORZB_HBP) |
> +		   VC4_SET_FIELD(mode->hsync_end - mode->hsync_start,
> +				 VC4_HDMI_HORZB_HSP) |
> +		   VC4_SET_FIELD(mode->hsync_start - mode->hdisplay,
> +				 VC4_HDMI_HORZB_HFP));
> +
> +	HDMI_WRITE(VC4_HDMI_VERTA0, verta);
> +	HDMI_WRITE(VC4_HDMI_VERTA1, verta);
> +
> +	HDMI_WRITE(VC4_HDMI_VERTB0, vertb);
> +	HDMI_WRITE(VC4_HDMI_VERTB1, vertb);
> +
> +	HD_WRITE(VC4_HD_VID_CTL,
> +		 (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
> +		 (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
> +
> +	/* The RGB order applies even when CSC is disabled. */
> +	HD_WRITE(VC4_HD_CSC_CTL, VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR,
> +					       VC4_HD_CSC_CTL_ORDER));
> +
> +	HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
> +
> +	if (debug_dump_regs) {
> +		DRM_INFO("HDMI regs after:\n");
> +		vc4_hdmi_dump_regs(dev);
> +	}
> +}
> +
> +static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct drm_device *dev = encoder->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +
> +	HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
> +	HD_WRITE(VC4_HD_VID_CTL,
> +		 HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
> +}
> +
> +static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
> +{
> +	struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
> +	struct drm_device *dev = encoder->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +
> +	HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
> +
> +	HD_WRITE(VC4_HD_VID_CTL,
> +		 HD_READ(VC4_HD_VID_CTL) |
> +		 VC4_HD_VID_CTL_ENABLE |
> +		 VC4_HD_VID_CTL_UNDERFLOW_ENABLE |
> +		 VC4_HD_VID_CTL_FRAME_COUNTER_RESET);
> +
> +	if (vc4_encoder->hdmi_monitor) {
> +		HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
> +			   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
> +			   VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
> +
> +		while (!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
> +			 VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE)) {
> +			cpu_relax();
> +		}
> +	} else {
> +		HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
> +			   HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) &
> +			   ~(VC4_HDMI_RAM_PACKET_ENABLE));
> +		HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
> +			   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
> +			   ~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
> +
> +		while (HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
> +		       VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE) {
> +			cpu_relax();
> +		}
> +	}
> +
> +	if (vc4_encoder->hdmi_monitor) {
> +		u32 drift;
> +
> +		WARN_ON(!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
> +			  VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE));
> +		HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
> +			   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
> +			   VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT);
> +
> +		/* XXX: Set HDMI_RAM_PACKET_CONFIG (1 << 16) and set
> +		 * up the infoframe.
> +		 */
> +
> +		drift = HDMI_READ(VC4_HDMI_FIFO_CTL);
> +		drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK;
> +
> +		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
> +			   drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
> +		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
> +			   drift | VC4_HDMI_FIFO_CTL_RECENTER);
> +		udelay(1000);
> +		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
> +			   drift & ~VC4_HDMI_FIFO_CTL_RECENTER);
> +		HDMI_WRITE(VC4_HDMI_FIFO_CTL,
> +			   drift | VC4_HDMI_FIFO_CTL_RECENTER);
> +
> +		while ((HDMI_READ(VC4_HDMI_FIFO_CTL) &
> +			VC4_HDMI_FIFO_CTL_RECENTER_DONE) == 0) {
> +			cpu_relax();
> +		}
> +	}
> +}
> +
> +static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
> +	.mode_set = vc4_hdmi_encoder_mode_set,
> +	.disable = vc4_hdmi_encoder_disable,
> +	.enable = vc4_hdmi_encoder_enable,
> +};
> +
> +static struct drm_crtc *
> +vc4_get_crtc_node(struct platform_device *pdev)
> +{
> +	struct device_node *crtc_node;
> +	struct platform_device *crtc_pdev;
> +
> +	crtc_node = of_parse_phandle(pdev->dev.of_node, "crtc", 0);
> +	if (!crtc_node) {
> +		DRM_ERROR("No CRTC for hdmi in DT\n");
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	crtc_pdev = of_find_device_by_node(crtc_node);
> +	if (!crtc_pdev) {
> +		DRM_ERROR("No CRTC device attached to OF node\n");
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	return platform_get_drvdata(crtc_pdev);
> +}
> +
> +struct drm_encoder *vc4_hdmi_encoder_init(struct drm_device *dev)
> +{
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	struct drm_encoder *encoder = NULL;
> +	struct vc4_hdmi_encoder *vc4_hdmi_encoder;
> +	struct drm_crtc *crtc;
> +	int ret;
> +
> +	vc4_hdmi_encoder = devm_kzalloc(dev->dev, sizeof(*vc4_hdmi_encoder),
> +					GFP_KERNEL);
> +	if (!vc4_hdmi_encoder) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +	encoder = &vc4_hdmi_encoder->base;
> +
> +	crtc = vc4_get_crtc_node(vc4->hdmi->pdev);
> +	if (IS_ERR(crtc)) {
> +		ret = PTR_ERR(crtc);
> +		goto fail;
> +	}
> +
> +	drm_encoder_init(dev, encoder, &vc4_hdmi_encoder_funcs,
> +			 DRM_MODE_ENCODER_TMDS);
> +	drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs);
> +
> +	encoder->possible_crtcs = drm_crtc_mask(crtc);
> +
> +	return encoder;
> +
> +fail:
> +	if (encoder)
> +		vc4_encoder_destroy(encoder);
> +
> +	return ERR_PTR(ret);
> +}
> +
> +/* Reads out the current HDMI mode progreamming at driver load time.
> + *
> + * This is currently used for later filtering out of the DDC-probed
> + * video modes, since we can't actually change modes due to not having
> + * control of the necessary clocks.  Later on, we may end up reusing
> + * this for skipping modesets at boot time.
> + */
> +static void
> +vc4_hdmi_get_boot_display_mode(struct vc4_dev *vc4)
> +{
> +	struct drm_display_mode *mode = &vc4->hdmi->boot_mode;
> +	u32 horza = HDMI_READ(VC4_HDMI_HORZA);
> +	u32 horzb = HDMI_READ(VC4_HDMI_HORZB);
> +	u32 verta = HDMI_READ(VC4_HDMI_VERTA0);
> +	u32 vertb = HDMI_READ(VC4_HDMI_VERTB0);
> +
> +	memset(mode, 0, sizeof(*mode));
> +
> +	if (!(horza & VC4_HDMI_HORZA_VPOS))
> +		mode->flags |= DRM_MODE_FLAG_NVSYNC;
> +	if (!(horza & VC4_HDMI_HORZA_HPOS))
> +		mode->flags |= DRM_MODE_FLAG_NHSYNC;
> +	mode->hdisplay = VC4_GET_FIELD(horza, VC4_HDMI_HORZA_HAP);
> +	mode->hsync_start = (mode->hdisplay +
> +			     VC4_GET_FIELD(horzb, VC4_HDMI_HORZB_HFP));
> +	mode->hsync_end = (mode->hsync_start +
> +			   VC4_GET_FIELD(horzb, VC4_HDMI_HORZB_HSP));
> +	mode->htotal = (mode->hsync_end +
> +			VC4_GET_FIELD(horzb, VC4_HDMI_HORZB_HBP));
> +
> +	mode->vdisplay = VC4_GET_FIELD(verta, VC4_HDMI_VERTA_VAL);
> +	mode->vsync_start = (mode->vdisplay +
> +			     VC4_GET_FIELD(verta, VC4_HDMI_VERTA_VFP));
> +	mode->vsync_end = (mode->vsync_start +
> +			   VC4_GET_FIELD(verta, VC4_HDMI_VERTA_VSP));
> +	mode->vtotal = (mode->vsync_end +
> +			VC4_GET_FIELD(vertb, VC4_HDMI_VERTB_VBP));
> +}
> +
> +static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = dev_get_drvdata(master);
> +	struct vc4_dev *vc4 = drm->dev_private;
> +	struct vc4_hdmi *hdmi;
> +	struct device_node *ddc_node;
> +	u32 value;
> +
> +	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
> +	if (!hdmi)
> +		return -ENOMEM;
> +
> +	hdmi->pdev = pdev;
> +	hdmi->hdmicore_regs = vc4_ioremap_regs(pdev, 0);
> +	if (IS_ERR(hdmi->hdmicore_regs))
> +		return PTR_ERR(hdmi->hdmicore_regs);
> +
> +	hdmi->hd_regs = vc4_ioremap_regs(pdev, 1);
> +	if (IS_ERR(hdmi->hd_regs))
> +		return PTR_ERR(hdmi->hd_regs);
> +
> +	/* DDC i2c driver */
> +	ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
> +	if (!ddc_node) {
> +		DRM_ERROR("Failed to find ddc node in device tree\n");
> +		return -ENODEV;
> +	}
> +
> +	hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
> +	if (!hdmi->ddc) {
> +		DRM_ERROR("Failed to get ddc i2c adapter by node\n");
> +		return -EPROBE_DEFER;
> +	}
> +
> +	/* Only use the GPIO HPD pin if present in the DT, otherwise
> +	 * we'll use the HDMI core's register.
> +	 */
> +	if (of_find_property(dev->of_node, "hpd-gpio", &value)) {
> +		hdmi->hpd_gpio = of_get_named_gpio(dev->of_node, "hpd-gpio", 0);
> +		if (hdmi->hpd_gpio < 0)
> +			return hdmi->hpd_gpio;
> +	}
> +
> +	vc4->hdmi = hdmi;
> +
> +	/* HDMI core must be enabled. */
> +	WARN_ON_ONCE((HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE) == 0);
> +
> +	vc4_hdmi_get_boot_display_mode(vc4);
> +
> +	return 0;
> +}
> +
> +static void vc4_hdmi_unbind(struct device *dev, struct device *master,
> +			    void *data)
> +{
> +	struct drm_device *drm = dev_get_drvdata(master);
> +	struct vc4_dev *vc4 = drm->dev_private;
> +
> +	put_device(&vc4->hdmi->ddc->dev);
> +
> +	vc4->hdmi = NULL;
> +}
> +
> +static const struct component_ops vc4_hdmi_ops = {
> +	.bind   = vc4_hdmi_bind,
> +	.unbind = vc4_hdmi_unbind,
> +};
> +
> +static int vc4_hdmi_dev_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &vc4_hdmi_ops);
> +}
> +
> +static int vc4_hdmi_dev_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &vc4_hdmi_ops);
> +	return 0;
> +}
> +
> +static const struct of_device_id vc4_hdmi_dt_match[] = {
> +	{ .compatible = "brcm,vc4-hdmi" },
> +	{}
> +};
> +
> +struct platform_driver vc4_hdmi_driver = {
> +	.probe = vc4_hdmi_dev_probe,
> +	.remove = vc4_hdmi_dev_remove,
> +	.driver = {
> +		.name = "vc4_hdmi",
> +		.of_match_table = vc4_hdmi_dt_match,
> +	},
> +};
> diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
> new file mode 100644
> index 0000000..34906fd
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
> @@ -0,0 +1,161 @@
> +/*
> + * Copyright (C) 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +/**
> + * DOC: VC4 HVS module.
> + *
> + * The HVS is the piece of hardware that does translation, scaling,
> + * colorspace conversion, and compositing of pixels stored in
> + * framebuffers into a FIFO of pixels going out to the Pixel Valve
> + * (CRTC).
> + *
> + * There is a single global HVS, with multiple output FIFOs that can
> + * be consumed by the PVs.  This file just manages the resources for
> + * the HVS, while the vc4_crtc.c code actually drives HVS setup for
> + * each CRTC.
> + */
> +
> +#include "linux/component.h"
> +#include "vc4_drv.h"
> +#include "vc4_regs.h"
> +
> +#define HVS_REG(reg) { reg, #reg }
> +static const struct {
> +	u32 reg;
> +	const char *name;
> +} hvs_regs[] = {
> +	HVS_REG(SCALER_DISPCTRL),
> +	HVS_REG(SCALER_DISPSTAT),
> +	HVS_REG(SCALER_DISPID),
> +	HVS_REG(SCALER_DISPECTRL),
> +	HVS_REG(SCALER_DISPPROF),
> +	HVS_REG(SCALER_DISPDITHER),
> +	HVS_REG(SCALER_DISPEOLN),
> +	HVS_REG(SCALER_DISPLIST0),
> +	HVS_REG(SCALER_DISPLIST1),
> +	HVS_REG(SCALER_DISPLIST2),
> +	HVS_REG(SCALER_DISPLSTAT),
> +	HVS_REG(SCALER_DISPLACT0),
> +	HVS_REG(SCALER_DISPLACT1),
> +	HVS_REG(SCALER_DISPLACT2),
> +	HVS_REG(SCALER_DISPCTRL0),
> +	HVS_REG(SCALER_DISPBKGND0),
> +	HVS_REG(SCALER_DISPSTAT0),
> +	HVS_REG(SCALER_DISPBASE0),
> +	HVS_REG(SCALER_DISPCTRL1),
> +	HVS_REG(SCALER_DISPBKGND1),
> +	HVS_REG(SCALER_DISPSTAT1),
> +	HVS_REG(SCALER_DISPBASE1),
> +	HVS_REG(SCALER_DISPCTRL2),
> +	HVS_REG(SCALER_DISPBKGND2),
> +	HVS_REG(SCALER_DISPSTAT2),
> +	HVS_REG(SCALER_DISPBASE2),
> +	HVS_REG(SCALER_DISPALPHA2),
> +};
> +
> +void vc4_hvs_dump_state(struct drm_device *dev)
> +{
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hvs_regs); i++) {
> +		DRM_INFO("0x%04x (%s): 0x%08x\n",
> +			 hvs_regs[i].reg, hvs_regs[i].name,
> +			 HVS_READ(hvs_regs[i].reg));
> +	}
> +
> +	DRM_INFO("HVS ctx:\n");
> +	for (i = 0; i < 64; i += 4) {
> +		DRM_INFO("0x%08x (%s): 0x%08x 0x%08x 0x%08x 0x%08x\n",
> +			 i * 4, i < HVS_BOOTLOADER_DLIST_END ? "B" : "D",
> +			 ((uint32_t *)vc4->hvs->dlist)[i + 0],
> +			 ((uint32_t *)vc4->hvs->dlist)[i + 1],
> +			 ((uint32_t *)vc4->hvs->dlist)[i + 2],
> +			 ((uint32_t *)vc4->hvs->dlist)[i + 3]);
> +	}
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused)
> +{
> +	struct drm_info_node *node = (struct drm_info_node *) m->private;
> +	struct drm_device *dev = node->minor->dev;
> +	struct vc4_dev *vc4 = to_vc4_dev(dev);
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hvs_regs); i++) {
> +		seq_printf(m, "%s (0x%04x): 0x%08x\n",
> +			   hvs_regs[i].name, hvs_regs[i].reg,
> +			   HVS_READ(hvs_regs[i].reg));
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct drm_device *drm = dev_get_drvdata(master);
> +	struct vc4_dev *vc4 = drm->dev_private;
> +	struct vc4_hvs *hvs = NULL;
> +
> +	hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL);
> +	if (!hvs)
> +		return -ENOMEM;
> +
> +	hvs->pdev = pdev;
> +
> +	hvs->regs = vc4_ioremap_regs(pdev, 0);
> +	if (IS_ERR(hvs->regs))
> +		return PTR_ERR(hvs->regs);
> +
> +	hvs->dlist = hvs->regs + SCALER_DLIST_START;
> +
> +	vc4->hvs = hvs;
> +	return 0;
> +}
> +
> +static void vc4_hvs_unbind(struct device *dev, struct device *master,
> +			   void *data)
> +{
> +	struct drm_device *drm = dev_get_drvdata(master);
> +	struct vc4_dev *vc4 = drm->dev_private;
> +
> +	vc4->hvs = NULL;
> +}
> +
> +static const struct component_ops vc4_hvs_ops = {
> +	.bind   = vc4_hvs_bind,
> +	.unbind = vc4_hvs_unbind,
> +};
> +
> +static int vc4_hvs_dev_probe(struct platform_device *pdev)
> +{
> +	return component_add(&pdev->dev, &vc4_hvs_ops);
> +}
> +
> +static int vc4_hvs_dev_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &vc4_hvs_ops);
> +	return 0;
> +}
> +
> +static const struct of_device_id vc4_hvs_dt_match[] = {
> +	{ .compatible = "brcm,vc4-hvs" },
> +	{}
> +};
> +
> +struct platform_driver vc4_hvs_driver = {
> +	.probe = vc4_hvs_dev_probe,
> +	.remove = vc4_hvs_dev_remove,
> +	.driver = {
> +		.name = "vc4_hvs",
> +		.of_match_table = vc4_hvs_dt_match,
> +	},
> +};
> diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
> new file mode 100644
> index 0000000..e5e96bc
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_kms.c
> @@ -0,0 +1,84 @@
> +/*
> + * Copyright (C) 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +/**
> + * DOC: VC4 KMS
> + *
> + * This is the general code for implementing KMS mode setting that
> + * doesn't clearly associate with any of the other objects (plane,
> + * crtc, HDMI encoder).
> + */
> +
> +#include "drm_crtc.h"
> +#include "drm_atomic_helper.h"
> +#include "drm_crtc_helper.h"
> +#include "drm_plane_helper.h"
> +#include "drm_fb_cma_helper.h"
> +#include "vc4_drv.h"
> +
> +static const struct drm_mode_config_funcs vc4_mode_funcs = {
> +	.atomic_check = drm_atomic_helper_check,
> +	.atomic_commit = drm_atomic_helper_commit,
> +	.fb_create = drm_fb_cma_create,
> +};
> +
> +/* Calls out to initialize all of the VC4 KMS objects. */
> +static int vc4_init_modeset_objects(struct drm_device *dev)
> +{
> +	struct drm_encoder *encoder;
> +	struct drm_connector *connector;
> +	int ret = 0;
> +
> +	encoder = vc4_hdmi_encoder_init(dev);
> +	if (IS_ERR(encoder)) {
> +		dev_err(dev->dev, "failed to construct HDMI encoder\n");
> +		ret = PTR_ERR(encoder);
> +		goto fail;
> +	}
> +
> +	connector = vc4_hdmi_connector_init(dev, encoder);
> +	if (IS_ERR(connector)) {
> +		ret = PTR_ERR(connector);
> +		dev_err(dev->dev, "failed to initialize HDMI connector\n");
> +		goto fail;
> +	}
> +
> +fail:
> +	return ret;
> +}
> +
> +int vc4_kms_load(struct drm_device *dev)
> +{
> +	int ret;
> +
> +	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
> +	if (ret < 0) {
> +		dev_err(dev->dev, "failed to initialize vblank\n");
> +		return ret;
> +	}
> +
> +	dev->mode_config.max_width = 2048;
> +	dev->mode_config.max_height = 2048;
> +	dev->mode_config.funcs = &vc4_mode_funcs;
> +	dev->mode_config.preferred_depth = 24;
> +
> +	ret = vc4_init_modeset_objects(dev);
> +	if (ret)
> +		goto fail;
> +
> +	drm_mode_config_reset(dev);
> +
> +	drm_fbdev_cma_init(dev, 32,
> +			   dev->mode_config.num_crtc,
> +			   dev->mode_config.num_connector);
> +
> +	drm_kms_helper_poll_init(dev);
> +
> +fail:
> +	return ret;
> +}
> diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
> new file mode 100644
> index 0000000..cdd8b10
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_plane.c
> @@ -0,0 +1,320 @@
> +/*
> + * Copyright (C) 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +/**
> + * DOC: VC4 plane module
> + *
> + * Each DRM plane is a layer of pixels being scanned out by the HVS.
> + *
> + * At atomic modeset check time, we compute the HVS display element
> + * state that would be necessary for displaying the plane (giving us a
> + * chance to figure out if a plane configuration is invalid), then at
> + * atomic flush time the CRTC will ask us to write our element state
> + * into the region of the HVS that it has allocated for us.
> + */
> +
> +#include "vc4_drv.h"
> +#include "vc4_regs.h"
> +#include "drm_atomic_helper.h"
> +#include "drm_fb_cma_helper.h"
> +#include "drm_plane_helper.h"
> +
> +struct vc4_plane_state {
> +	struct drm_plane_state base;
> +	u32 *dlist;
> +	u32 dlist_size; /* Number of dwords in allocated for the display list */
> +	u32 dlist_count; /* Number of used dwords in the display list. */
> +};
> +
> +static inline struct vc4_plane_state *
> +to_vc4_plane_state(struct drm_plane_state *state)
> +{
> +	return (struct vc4_plane_state *)state;
> +}
> +
> +static const struct hvs_format {
> +	u32 drm; /* DRM_FORMAT_* */
> +	u32 hvs; /* HVS_FORMAT_* */
> +	u32 pixel_order;
> +	bool has_alpha;
> +} hvs_formats[] = {
> +	{
> +		.drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
> +		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
> +	},
> +	{
> +		.drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
> +		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
> +	},
> +};
> +
> +static const struct hvs_format *vc4_get_hvs_format(u32 drm_format)
> +{
> +	unsigned i;
> +
> +	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
> +		if (hvs_formats[i].drm == drm_format)
> +			return &hvs_formats[i];
> +	}
> +
> +	return NULL;
> +}
> +
> +static bool plane_enabled(struct drm_plane_state *state)
> +{
> +	return state->fb && state->crtc;
> +}
> +
> +struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
> +{
> +	struct vc4_plane_state *vc4_state;
> +
> +	if (WARN_ON(!plane->state))
> +		return NULL;
> +
> +	vc4_state = kmemdup(plane->state, sizeof(*vc4_state), GFP_KERNEL);
> +	if (!vc4_state)
> +		return NULL;
> +
> +	__drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base);
> +
> +	if (vc4_state->dlist) {
> +		vc4_state->dlist = kmemdup(vc4_state->dlist,
> +					   vc4_state->dlist_count * 4,
> +					   GFP_KERNEL);
> +		if (!vc4_state->dlist) {
> +			kfree(vc4_state);
> +			return NULL;
> +		}
> +		vc4_state->dlist_size = vc4_state->dlist_count;
> +	}
> +
> +	return &vc4_state->base;
> +}
> +
> +void vc4_plane_destroy_state(struct drm_plane *plane,
> +			     struct drm_plane_state *state)
> +{
> +	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
> +
> +	kfree(vc4_state->dlist);
> +	__drm_atomic_helper_plane_destroy_state(plane, &vc4_state->base);
> +	kfree(state);
> +}
> +
> +/* Called during init to allocate the plane's atomic state. */
> +void vc4_plane_reset(struct drm_plane *plane)
> +{
> +	struct vc4_plane_state *vc4_state;
> +
> +	WARN_ON(plane->state);
> +
> +	vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
> +	if (!vc4_state)
> +		return;
> +
> +	plane->state = &vc4_state->base;
> +	vc4_state->base.plane = plane;
> +}
> +
> +static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val)
> +{
> +	if (vc4_state->dlist_count == vc4_state->dlist_size) {
> +		u32 new_size = max(4u, vc4_state->dlist_count * 2);
> +		u32 *new_dlist = kmalloc(new_size * 4, GFP_KERNEL);
> +
> +		if (!new_dlist)
> +			return;
> +		memcpy(new_dlist, vc4_state->dlist, vc4_state->dlist_count * 4);
> +
> +		kfree(vc4_state->dlist);
> +		vc4_state->dlist = new_dlist;
> +		vc4_state->dlist_size = new_size;
> +	}
> +
> +	vc4_state->dlist[vc4_state->dlist_count++] = val;
> +}
> +
> +/* Writes out a full display list for an active plane to the plane's
> + * private dlist state.
> + */
> +static int vc4_plane_mode_set(struct drm_plane *plane,
> +			      struct drm_plane_state *state)
> +{
> +	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
> +	u32 ctl0_offset = vc4_state->dlist_count;
> +	const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format);
> +	uint32_t offset = fb->offsets[0];
> +	int crtc_x = state->crtc_x;
> +	int crtc_y = state->crtc_y;
> +	int crtc_w = state->crtc_w;
> +	int crtc_h = state->crtc_h;
> +
> +	if (crtc_x < 0) {
> +		offset += drm_format_plane_cpp(fb->pixel_format, 0) * -crtc_x;
> +		crtc_w += crtc_x;
> +		crtc_x = 0;
> +	}
> +
> +	if (crtc_y < 0) {
> +		offset += fb->pitches[0] * -crtc_y;
> +		crtc_h += crtc_y;
> +		crtc_y = 0;
> +	}
> +
> +	vc4_dlist_write(vc4_state,
> +			SCALER_CTL0_VALID |
> +			(format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |
> +			(format->hvs << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
> +			SCALER_CTL0_UNITY);
> +
> +	/* Position Word 0: Image Positions and Alpha Value */
> +	vc4_dlist_write(vc4_state,
> +			VC4_SET_FIELD(0xff, SCALER_POS0_FIXED_ALPHA) |
> +			VC4_SET_FIELD(crtc_x, SCALER_POS0_START_X) |
> +			VC4_SET_FIELD(crtc_y, SCALER_POS0_START_Y));
> +
> +	/* Position Word 1: Scaled Image Dimensions.
> +	 * Skipped due to SCALER_CTL0_UNITY scaling.
> +	 */
> +
> +	/* Position Word 2: Source Image Size, Alpha Mode */
> +	vc4_dlist_write(vc4_state,
> +			VC4_SET_FIELD(format->has_alpha ?
> +				      SCALER_POS2_ALPHA_MODE_PIPELINE :
> +				      SCALER_POS2_ALPHA_MODE_FIXED,
> +				      SCALER_POS2_ALPHA_MODE) |
> +			VC4_SET_FIELD(crtc_w, SCALER_POS2_WIDTH) |
> +			VC4_SET_FIELD(crtc_h, SCALER_POS2_HEIGHT));
> +
> +	/* Position Word 3: Context.  Written by the HVS. */
> +	vc4_dlist_write(vc4_state, 0xc0c0c0c0);
> +
> +	/* Pointer Word 0: RGB / Y Pointer */
> +	vc4_dlist_write(vc4_state, bo->paddr + offset);
> +
> +	/* Pointer Context Word 0: Written by the HVS */
> +	vc4_dlist_write(vc4_state, 0xc0c0c0c0);
> +
> +	/* Pitch word 0: Pointer 0 Pitch */
> +	vc4_dlist_write(vc4_state,
> +			VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH));
> +
> +	vc4_state->dlist[ctl0_offset] |=
> +		VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE);
> +
> +	return 0;
> +}
> +
> +/* If a modeset involves changing the setup of a plane, the atomic
> + * infrastructure will call this to validate a proposed plane setup.
> + * However, if a plane isn't getting updated, this (and the
> + * corresponding vc4_plane_atomic_update) won't get called.  Thus, we
> + * compute the dlist here and have all active plane dlists get updated
> + * in the CRTC's flush.
> + */
> +static int vc4_plane_atomic_check(struct drm_plane *plane,
> +				  struct drm_plane_state *state)
> +{
> +	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
> +
> +	vc4_state->dlist_count = 0;
> +
> +	if (plane_enabled(state))
> +		return vc4_plane_mode_set(plane, state);
> +	else
> +		return 0;
> +}
> +
> +static void vc4_plane_atomic_update(struct drm_plane *plane,
> +				    struct drm_plane_state *old_state)
> +{
> +	/* No contents here.  Since we don't know where in the CRTC's
> +	 * dlist we should be stored, our dlist is uploaded to the
> +	 * hardware with vc4_plane_write_dlist() at CRTC atomic_flush
> +	 * time.
> +	 */
> +}
> +
> +u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist)
> +{
> +	struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
> +	int i;
> +
> +	/* Can't memcpy_toio() because it needs to be 32-bit writes. */
> +	for (i = 0; i < vc4_state->dlist_count; i++)
> +		writel(vc4_state->dlist[i], &dlist[i]);
> +
> +	return vc4_state->dlist_count;
> +}
> +
> +u32 vc4_plane_dlist_size(struct drm_plane_state *state)
> +{
> +	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
> +
> +	return vc4_state->dlist_count;
> +}
> +
> +static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
> +	.prepare_fb = NULL,
> +	.cleanup_fb = NULL,
> +	.atomic_check = vc4_plane_atomic_check,
> +	.atomic_update = vc4_plane_atomic_update,
> +};
> +
> +static void vc4_plane_destroy(struct drm_plane *plane)
> +{
> +	drm_plane_helper_disable(plane);
> +	drm_plane_cleanup(plane);
> +}
> +
> +static const struct drm_plane_funcs vc4_plane_funcs = {
> +	.update_plane = drm_atomic_helper_update_plane,
> +	.disable_plane = drm_atomic_helper_disable_plane,
> +	.destroy = vc4_plane_destroy,
> +	.set_property = NULL,
> +	.reset = vc4_plane_reset,
> +	.atomic_duplicate_state = vc4_plane_duplicate_state,
> +	.atomic_destroy_state = vc4_plane_destroy_state,
> +};
> +
> +struct drm_plane *vc4_plane_init(struct drm_device *dev,
> +				 enum drm_plane_type type)
> +{
> +	struct drm_plane *plane = NULL;
> +	struct vc4_plane *vc4_plane;
> +	u32 formats[ARRAY_SIZE(hvs_formats)];
> +	int ret = 0;
> +	unsigned i;
> +
> +	vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
> +				 GFP_KERNEL);
> +	if (!vc4_plane) {
> +		ret = -ENOMEM;
> +		goto fail;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++)
> +		formats[i] = hvs_formats[i].drm;
> +	plane = &vc4_plane->base;
> +	ret = drm_universal_plane_init(dev, plane, 0xff,
> +				       &vc4_plane_funcs,
> +				       formats, ARRAY_SIZE(formats),
> +				       type);
> +
> +	drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
> +
> +	return plane;
> +fail:
> +	if (plane)
> +		vc4_plane_destroy(plane);
> +
> +	return ERR_PTR(ret);
> +}
> diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h
> new file mode 100644
> index 0000000..0eff631
> --- /dev/null
> +++ b/drivers/gpu/drm/vc4/vc4_regs.h
> @@ -0,0 +1,562 @@
> +/*
> + *  Copyright ? 2014-2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#define VC4_MASK(high, low) (((1 << ((high) - (low) + 1)) - 1) << (low))
> +/* Using the GNU statement expression extension */
> +#define VC4_SET_FIELD(value, field)					\
> +	({								\
> +		uint32_t fieldval = (value) << field##_SHIFT;		\
> +		WARN_ON((fieldval & ~field##_MASK) != 0);		\
> +		fieldval & field##_MASK;				\
> +	 })
> +
> +#define VC4_GET_FIELD(word, field) (((word) & field##_MASK) >>		\
> +				    field##_SHIFT)
> +
> +#define V3D_IDENT0   0x00000
> +# define V3D_EXPECTED_IDENT0 \
> +	((2 << 24) | \
> +	('V' << 0) | \
> +	('3' << 8) | \
> +	 ('D' << 16))
> +
> +#define V3D_IDENT1   0x00004
> +/* Multiples of 1kb */
> +# define V3D_IDENT1_VPM_SIZE_MASK                      VC4_MASK(31, 28)
> +# define V3D_IDENT1_VPM_SIZE_SHIFT                     28
> +# define V3D_IDENT1_NSEM_MASK                          VC4_MASK(23, 16)
> +# define V3D_IDENT1_NSEM_SHIFT                         16
> +# define V3D_IDENT1_TUPS_MASK                          VC4_MASK(15, 12)
> +# define V3D_IDENT1_TUPS_SHIFT                         12
> +# define V3D_IDENT1_QUPS_MASK                          VC4_MASK(11, 8)
> +# define V3D_IDENT1_QUPS_SHIFT                         8
> +# define V3D_IDENT1_NSLC_MASK                          VC4_MASK(7, 4)
> +# define V3D_IDENT1_NSLC_SHIFT                         4
> +# define V3D_IDENT1_REV_MASK                           VC4_MASK(3, 0)
> +# define V3D_IDENT1_REV_SHIFT                          0
> +
> +#define V3D_IDENT2   0x00008
> +#define V3D_SCRATCH  0x00010
> +#define V3D_L2CACTL  0x00020
> +# define V3D_L2CACTL_L2CCLR                            (1 << 2)
> +# define V3D_L2CACTL_L2CDIS                            (1 << 1)
> +# define V3D_L2CACTL_L2CENA                            (1 << 0)
> +
> +#define V3D_SLCACTL  0x00024
> +# define V3D_SLCACTL_T1CC_MASK                         VC4_MASK(27, 24)
> +# define V3D_SLCACTL_T1CC_SHIFT                        24
> +# define V3D_SLCACTL_T0CC_MASK                         VC4_MASK(19, 16)
> +# define V3D_SLCACTL_T0CC_SHIFT                        16
> +# define V3D_SLCACTL_UCC_MASK                          VC4_MASK(11, 8)
> +# define V3D_SLCACTL_UCC_SHIFT                         8
> +# define V3D_SLCACTL_ICC_MASK                          VC4_MASK(3, 0)
> +# define V3D_SLCACTL_ICC_SHIFT                         0
> +
> +#define V3D_INTCTL   0x00030
> +#define V3D_INTENA   0x00034
> +#define V3D_INTDIS   0x00038
> +# define V3D_INT_SPILLUSE                              (1 << 3)
> +# define V3D_INT_OUTOMEM                               (1 << 2)
> +# define V3D_INT_FLDONE                                (1 << 1)
> +# define V3D_INT_FRDONE                                (1 << 0)
> +
> +#define V3D_CT0CS    0x00100
> +#define V3D_CT1CS    0x00104
> +#define V3D_CTNCS(n) (V3D_CT0CS + 4 * n)
> +# define V3D_CTRSTA      (1 << 15)
> +# define V3D_CTSEMA      (1 << 12)
> +# define V3D_CTRTSD      (1 << 8)
> +# define V3D_CTRUN       (1 << 5)
> +# define V3D_CTSUBS      (1 << 4)
> +# define V3D_CTERR       (1 << 3)
> +# define V3D_CTMODE      (1 << 0)
> +
> +#define V3D_CT0EA    0x00108
> +#define V3D_CT1EA    0x0010c
> +#define V3D_CTNEA(n) (V3D_CT0EA + 4 * (n))
> +#define V3D_CT0CA    0x00110
> +#define V3D_CT1CA    0x00114
> +#define V3D_CTNCA(n) (V3D_CT0CA + 4 * (n))
> +#define V3D_CT00RA0  0x00118
> +#define V3D_CT01RA0  0x0011c
> +#define V3D_CTNRA0(n) (V3D_CT00RA0 + 4 * (n))
> +#define V3D_CT0LC    0x00120
> +#define V3D_CT1LC    0x00124
> +#define V3D_CTNLC(n) (V3D_CT0LC + 4 * (n))
> +#define V3D_CT0PC    0x00128
> +#define V3D_CT1PC    0x0012c
> +#define V3D_CTNPC(n) (V3D_CT0PC + 4 * (n))
> +
> +#define V3D_PCS      0x00130
> +# define V3D_BMOOM       (1 << 8)
> +# define V3D_RMBUSY      (1 << 3)
> +# define V3D_RMACTIVE    (1 << 2)
> +# define V3D_BMBUSY      (1 << 1)
> +# define V3D_BMACTIVE    (1 << 0)
> +
> +#define V3D_BFC      0x00134
> +#define V3D_RFC      0x00138
> +#define V3D_BPCA     0x00300
> +#define V3D_BPCS     0x00304
> +#define V3D_BPOA     0x00308
> +#define V3D_BPOS     0x0030c
> +#define V3D_BXCF     0x00310
> +#define V3D_SQRSV0   0x00410
> +#define V3D_SQRSV1   0x00414
> +#define V3D_SQCNTL   0x00418
> +#define V3D_SRQPC    0x00430
> +#define V3D_SRQUA    0x00434
> +#define V3D_SRQUL    0x00438
> +#define V3D_SRQCS    0x0043c
> +#define V3D_VPACNTL  0x00500
> +#define V3D_VPMBASE  0x00504
> +#define V3D_PCTRC    0x00670
> +#define V3D_PCTRE    0x00674
> +#define V3D_PCTR0    0x00680
> +#define V3D_PCTRS0   0x00684
> +#define V3D_PCTR1    0x00688
> +#define V3D_PCTRS1   0x0068c
> +#define V3D_PCTR2    0x00690
> +#define V3D_PCTRS2   0x00694
> +#define V3D_PCTR3    0x00698
> +#define V3D_PCTRS3   0x0069c
> +#define V3D_PCTR4    0x006a0
> +#define V3D_PCTRS4   0x006a4
> +#define V3D_PCTR5    0x006a8
> +#define V3D_PCTRS5   0x006ac
> +#define V3D_PCTR6    0x006b0
> +#define V3D_PCTRS6   0x006b4
> +#define V3D_PCTR7    0x006b8
> +#define V3D_PCTRS7   0x006bc
> +#define V3D_PCTR8    0x006c0
> +#define V3D_PCTRS8   0x006c4
> +#define V3D_PCTR9    0x006c8
> +#define V3D_PCTRS9   0x006cc
> +#define V3D_PCTR10   0x006d0
> +#define V3D_PCTRS10  0x006d4
> +#define V3D_PCTR11   0x006d8
> +#define V3D_PCTRS11  0x006dc
> +#define V3D_PCTR12   0x006e0
> +#define V3D_PCTRS12  0x006e4
> +#define V3D_PCTR13   0x006e8
> +#define V3D_PCTRS13  0x006ec
> +#define V3D_PCTR14   0x006f0
> +#define V3D_PCTRS14  0x006f4
> +#define V3D_PCTR15   0x006f8
> +#define V3D_PCTRS15  0x006fc
> +#define V3D_BGE      0x00f00
> +#define V3D_FDBGO    0x00f04
> +#define V3D_FDBGB    0x00f08
> +#define V3D_FDBGR    0x00f0c
> +#define V3D_FDBGS    0x00f10
> +#define V3D_ERRSTAT  0x00f20
> +
> +#define PV_CONTROL				0x00
> +# define PV_CONTROL_FORMAT_MASK			VC4_MASK(23, 21)
> +# define PV_CONTROL_FORMAT_SHIFT		21
> +# define PV_CONTROL_FORMAT_24			0
> +# define PV_CONTROL_FORMAT_DSIV_16		1
> +# define PV_CONTROL_FORMAT_DSIC_16		2
> +# define PV_CONTROL_FORMAT_DSIV_18		3
> +# define PV_CONTROL_FORMAT_DSIV_24		4
> +
> +# define PV_CONTROL_FIFO_LEVEL_MASK		VC4_MASK(20, 15)
> +# define PV_CONTROL_FIFO_LEVEL_SHIFT		15
> +# define PV_CONTROL_CLR_AT_START		(1 << 14)
> +# define PV_CONTROL_TRIGGER_UNDERFLOW		(1 << 13)
> +# define PV_CONTROL_WAIT_HSTART			(1 << 12)
> +# define PV_CONTROL_CLK_SELECT_DSI		0
> +# define PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI	1
> +# define PV_CONTROL_CLK_SELECT_MASK		VC4_MASK(3, 2)
> +# define PV_CONTROL_CLK_SELECT_SHIFT		2
> +# define PV_CONTROL_FIFO_CLR			(1 << 1)
> +# define PV_CONTROL_EN				(1 << 0)
> +
> +#define PV_V_CONTROL				0x04
> +# define PV_VCONTROL_CONTINUOUS			(1 << 1)
> +# define PV_VCONTROL_VIDEN			(1 << 0)
> +
> +#define PV_VSYNCD				0x08
> +
> +#define PV_HORZA				0x0c
> +# define PV_HORZA_HBP_MASK			VC4_MASK(31, 16)
> +# define PV_HORZA_HBP_SHIFT			16
> +# define PV_HORZA_HSYNC_MASK			VC4_MASK(15, 0)
> +# define PV_HORZA_HSYNC_SHIFT			0
> +
> +#define PV_HORZB				0x10
> +# define PV_HORZB_HFP_MASK			VC4_MASK(31, 16)
> +# define PV_HORZB_HFP_SHIFT			16
> +# define PV_HORZB_HACTIVE_MASK			VC4_MASK(15, 0)
> +# define PV_HORZB_HACTIVE_SHIFT			0
> +
> +#define PV_VERTA				0x14
> +# define PV_VERTA_VBP_MASK			VC4_MASK(31, 16)
> +# define PV_VERTA_VBP_SHIFT			16
> +# define PV_VERTA_VSYNC_MASK			VC4_MASK(15, 0)
> +# define PV_VERTA_VSYNC_SHIFT			0
> +
> +#define PV_VERTB				0x18
> +# define PV_VERTB_VFP_MASK			VC4_MASK(31, 16)
> +# define PV_VERTB_VFP_SHIFT			16
> +# define PV_VERTB_VACTIVE_MASK			VC4_MASK(15, 0)
> +# define PV_VERTB_VACTIVE_SHIFT			0
> +
> +#define PV_VERTA_EVEN				0x1c
> +#define PV_VERTB_EVEN				0x20
> +
> +#define PV_INTEN				0x24
> +#define PV_INTSTAT				0x28
> +# define PV_INT_VID_IDLE			(1 << 9)
> +# define PV_INT_VFP_END				(1 << 8)
> +# define PV_INT_VFP_START			(1 << 7)
> +# define PV_INT_VACT_START			(1 << 6)
> +# define PV_INT_VBP_START			(1 << 5)
> +# define PV_INT_VSYNC_START			(1 << 4)
> +# define PV_INT_HFP_START			(1 << 3)
> +# define PV_INT_HACT_START			(1 << 2)
> +# define PV_INT_HBP_START			(1 << 1)
> +# define PV_INT_HSYNC_START			(1 << 0)
> +
> +#define PV_STAT					0x2c
> +
> +#define PV_HACT_ACT				0x30
> +
> +#define SCALER_DISPCTRL                         0x00000000
> +/* Global register for clock gating the HVS */
> +# define SCALER_DISPCTRL_ENABLE			(1 << 31)
> +# define SCALER_DISPCTRL_DSP2EISLUR		(1 << 15)
> +# define SCALER_DISPCTRL_DSP1EISLUR		(1 << 14)
> +/* Enables Display 0 short line and underrun contribution to
> + * SCALER_DISPSTAT_IRQDISP0.  Note that short frame contributions are
> + * always enabled.
> + */
> +# define SCALER_DISPCTRL_DSP0EISLUR		(1 << 13)
> +# define SCALER_DISPCTRL_DSP2EIEOLN		(1 << 12)
> +# define SCALER_DISPCTRL_DSP2EIEOF		(1 << 11)
> +# define SCALER_DISPCTRL_DSP1EIEOLN		(1 << 10)
> +# define SCALER_DISPCTRL_DSP1EIEOF		(1 << 9)
> +/* Enables Display 0 end-of-line-N contribution to
> + * SCALER_DISPSTAT_IRQDISP0
> + */
> +# define SCALER_DISPCTRL_DSP0EIEOLN		(1 << 8)
> +/* Enables Display 0 EOF contribution to SCALER_DISPSTAT_IRQDISP0 */
> +# define SCALER_DISPCTRL_DSP0EIEOF		(1 << 7)
> +
> +# define SCALER_DISPCTRL_SLVRDEIRQ		(1 << 6)
> +# define SCALER_DISPCTRL_SLVWREIRQ		(1 << 5)
> +# define SCALER_DISPCTRL_DMAEIRQ		(1 << 4)
> +# define SCALER_DISPCTRL_DISP2EIRQ		(1 << 3)
> +# define SCALER_DISPCTRL_DISP1EIRQ		(1 << 2)
> +/* Enables interrupt generation on the enabled EOF/EOLN/EISLUR
> + * bits and short frames..
> + */
> +# define SCALER_DISPCTRL_DISP0EIRQ		(1 << 1)
> +/* Enables interrupt generation on scaler profiler interrupt. */
> +# define SCALER_DISPCTRL_SCLEIRQ		(1 << 0)
> +
> +#define SCALER_DISPSTAT                         0x00000004
> +# define SCALER_DISPSTAT_COBLOW2		(1 << 29)
> +# define SCALER_DISPSTAT_EOLN2			(1 << 28)
> +# define SCALER_DISPSTAT_ESFRAME2		(1 << 27)
> +# define SCALER_DISPSTAT_ESLINE2		(1 << 26)
> +# define SCALER_DISPSTAT_EUFLOW2		(1 << 25)
> +# define SCALER_DISPSTAT_EOF2			(1 << 24)
> +
> +# define SCALER_DISPSTAT_COBLOW1		(1 << 21)
> +# define SCALER_DISPSTAT_EOLN1			(1 << 20)
> +# define SCALER_DISPSTAT_ESFRAME1		(1 << 19)
> +# define SCALER_DISPSTAT_ESLINE1		(1 << 18)
> +# define SCALER_DISPSTAT_EUFLOW1		(1 << 17)
> +# define SCALER_DISPSTAT_EOF1			(1 << 16)
> +
> +# define SCALER_DISPSTAT_RESP_MASK		VC4_MASK(15, 14)
> +# define SCALER_DISPSTAT_RESP_SHIFT		14
> +# define SCALER_DISPSTAT_RESP_OKAY		0
> +# define SCALER_DISPSTAT_RESP_EXOKAY		1
> +# define SCALER_DISPSTAT_RESP_SLVERR		2
> +# define SCALER_DISPSTAT_RESP_DECERR		3
> +
> +# define SCALER_DISPSTAT_COBLOW0		(1 << 13)
> +/* Set when the DISPEOLN line is done compositing. */
> +# define SCALER_DISPSTAT_EOLN0			(1 << 12)
> +/* Set when VSTART is seen but there are still pixels in the current
> + * output line.
> + */
> +# define SCALER_DISPSTAT_ESFRAME0		(1 << 11)
> +/* Set when HSTART is seen but there are still pixels in the current
> + * output line.
> + */
> +# define SCALER_DISPSTAT_ESLINE0		(1 << 10)
> +/* Set when the the downstream tries to read from the display FIFO
> + * while it's empty.
> + */
> +# define SCALER_DISPSTAT_EUFLOW0		(1 << 9)
> +/* Set when the display mode changes from RUN to EOF */
> +# define SCALER_DISPSTAT_EOF0			(1 << 8)
> +
> +/* Set on AXI invalid DMA ID error. */
> +# define SCALER_DISPSTAT_DMA_ERROR		(1 << 7)
> +/* Set on AXI slave read decode error */
> +# define SCALER_DISPSTAT_IRQSLVRD		(1 << 6)
> +/* Set on AXI slave write decode error */
> +# define SCALER_DISPSTAT_IRQSLVWR		(1 << 5)
> +/* Set when SCALER_DISPSTAT_DMA_ERROR is set, or
> + * SCALER_DISPSTAT_RESP_ERROR is not SCALER_DISPSTAT_RESP_OKAY.
> + */
> +# define SCALER_DISPSTAT_IRQDMA			(1 << 4)
> +# define SCALER_DISPSTAT_IRQDISP2		(1 << 3)
> +# define SCALER_DISPSTAT_IRQDISP1		(1 << 2)
> +/* Set when any of the EOF/EOLN/ESFRAME/ESLINE bits are set and their
> + * corresponding interrupt bit is enabled in DISPCTRL.
> + */
> +# define SCALER_DISPSTAT_IRQDISP0		(1 << 1)
> +/* On read, the profiler interrupt.  On write, clear *all* interrupt bits. */
> +# define SCALER_DISPSTAT_IRQSCL			(1 << 0)
> +
> +#define SCALER_DISPID                           0x00000008
> +#define SCALER_DISPECTRL                        0x0000000c
> +#define SCALER_DISPPROF                         0x00000010
> +#define SCALER_DISPDITHER                       0x00000014
> +#define SCALER_DISPEOLN                         0x00000018
> +#define SCALER_DISPLIST0                        0x00000020
> +#define SCALER_DISPLIST1                        0x00000024
> +#define SCALER_DISPLIST2                        0x00000028
> +#define SCALER_DISPLSTAT                        0x0000002c
> +#define SCALER_DISPLISTX(x)			(SCALER_DISPLIST0 +	\
> +						 (x) * (SCALER_DISPLIST1 - \
> +							SCALER_DISPLIST0))
> +
> +#define SCALER_DISPLACT0                        0x00000030
> +#define SCALER_DISPLACT1                        0x00000034
> +#define SCALER_DISPLACT2                        0x00000038
> +#define SCALER_DISPCTRL0                        0x00000040
> +# define SCALER_DISPCTRLX_ENABLE		(1 << 31)
> +# define SCALER_DISPCTRLX_RESET			(1 << 30)
> +# define SCALER_DISPCTRLX_WIDTH_MASK		VC4_MASK(23, 12)
> +# define SCALER_DISPCTRLX_WIDTH_SHIFT		12
> +# define SCALER_DISPCTRLX_HEIGHT_MASK		VC4_MASK(11, 0)
> +# define SCALER_DISPCTRLX_HEIGHT_SHIFT		0
> +
> +#define SCALER_DISPBKGND0                       0x00000044
> +#define SCALER_DISPSTAT0                        0x00000048
> +#define SCALER_DISPBASE0                        0x0000004c
> +# define SCALER_DISPSTATX_MODE_MASK		VC4_MASK(31, 30)
> +# define SCALER_DISPSTATX_MODE_SHIFT		30
> +# define SCALER_DISPSTATX_MODE_DISABLED		0
> +# define SCALER_DISPSTATX_MODE_INIT		1
> +# define SCALER_DISPSTATX_MODE_RUN		2
> +# define SCALER_DISPSTATX_MODE_EOF		3
> +# define SCALER_DISPSTATX_FULL			(1 << 29)
> +# define SCALER_DISPSTATX_EMPTY			(1 << 28)
> +#define SCALER_DISPCTRL1                        0x00000050
> +#define SCALER_DISPBKGND1                       0x00000054
> +#define SCALER_DISPSTAT1                        0x00000058
> +#define SCALER_DISPSTATX(x)			(SCALER_DISPSTAT0 +        \
> +						 (x) * (SCALER_DISPSTAT1 - \
> +							SCALER_DISPSTAT0))
> +#define SCALER_DISPBASE1                        0x0000005c
> +#define SCALER_DISPCTRL2                        0x00000060
> +#define SCALER_DISPCTRLX(x)			(SCALER_DISPCTRL0 +        \
> +						 (x) * (SCALER_DISPCTRL1 - \
> +							SCALER_DISPCTRL0))
> +#define SCALER_DISPBKGND2                       0x00000064
> +#define SCALER_DISPSTAT2                        0x00000068
> +#define SCALER_DISPBASE2                        0x0000006c
> +#define SCALER_DISPALPHA2                       0x00000070
> +#define SCALER_GAMADDR                          0x00000078
> +#define SCALER_GAMDATA                          0x000000e0
> +#define SCALER_DLIST_START                      0x00002000
> +#define SCALER_DLIST_SIZE                       0x00004000
> +
> +#define VC4_HDMI_CORE_REV			0x000
> +
> +#define VC4_HDMI_SW_RESET_CONTROL		0x004
> +# define VC4_HDMI_SW_RESET_FORMAT_DETECT	(1 << 1)
> +# define VC4_HDMI_SW_RESET_HDMI			(1 << 0)
> +
> +#define VC4_HDMI_HOTPLUG_INT			0x008
> +
> +#define VC4_HDMI_HOTPLUG			0x00c
> +# define VC4_HDMI_HOTPLUG_CONNECTED		(1 << 0)
> +
> +#define VC4_HDMI_RAM_PACKET_CONFIG		0x0a0
> +# define VC4_HDMI_RAM_PACKET_ENABLE		(1 << 16)
> +
> +#define VC4_HDMI_HORZA				0x0c4
> +# define VC4_HDMI_HORZA_VPOS			(1 << 14)
> +# define VC4_HDMI_HORZA_HPOS			(1 << 13)
> +/* Horizontal active pixels (hdisplay). */
> +# define VC4_HDMI_HORZA_HAP_MASK		VC4_MASK(12, 0)
> +# define VC4_HDMI_HORZA_HAP_SHIFT		0
> +
> +#define VC4_HDMI_HORZB				0x0c8
> +/* Horizontal pack porch (htotal - hsync_end). */
> +# define VC4_HDMI_HORZB_HBP_MASK		VC4_MASK(29, 20)
> +# define VC4_HDMI_HORZB_HBP_SHIFT		20
> +/* Horizontal sync pulse (hsync_end - hsync_start). */
> +# define VC4_HDMI_HORZB_HSP_MASK		VC4_MASK(19, 10)
> +# define VC4_HDMI_HORZB_HSP_SHIFT		10
> +/* Horizontal front porch (hsync_start - hdisplay). */
> +# define VC4_HDMI_HORZB_HFP_MASK		VC4_MASK(9, 0)
> +# define VC4_HDMI_HORZB_HFP_SHIFT		0
> +
> +#define VC4_HDMI_FIFO_CTL			0x05c
> +# define VC4_HDMI_FIFO_CTL_RECENTER_DONE	(1 << 14)
> +# define VC4_HDMI_FIFO_CTL_USE_EMPTY		(1 << 13)
> +# define VC4_HDMI_FIFO_CTL_ON_VB		(1 << 7)
> +# define VC4_HDMI_FIFO_CTL_RECENTER		(1 << 6)
> +# define VC4_HDMI_FIFO_CTL_FIFO_RESET		(1 << 5)
> +# define VC4_HDMI_FIFO_CTL_USE_PLL_LOCK		(1 << 4)
> +# define VC4_HDMI_FIFO_CTL_INV_CLK_XFR		(1 << 3)
> +# define VC4_HDMI_FIFO_CTL_CAPTURE_PTR		(1 << 2)
> +# define VC4_HDMI_FIFO_CTL_USE_FULL		(1 << 1)
> +# define VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N	(1 << 0)
> +# define VC4_HDMI_FIFO_VALID_WRITE_MASK		0xefff
> +
> +#define VC4_HDMI_SCHEDULER_CONTROL		0x0c0
> +# define VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT (1 << 15)
> +# define VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS (1 << 5)
> +# define VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT	(1 << 3)
> +# define VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE	(1 << 1)
> +# define VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI	(1 << 0)
> +
> +#define VC4_HDMI_VERTA0				0x0cc
> +#define VC4_HDMI_VERTA1				0x0d4
> +/* Vertical sync pulse (vsync_end - vsync_start). */
> +# define VC4_HDMI_VERTA_VSP_MASK		VC4_MASK(24, 20)
> +# define VC4_HDMI_VERTA_VSP_SHIFT		20
> +/* Vertical front porch (vsync_start - vdisplay). */
> +# define VC4_HDMI_VERTA_VFP_MASK		VC4_MASK(19, 13)
> +# define VC4_HDMI_VERTA_VFP_SHIFT		13
> +/* Vertical active lines (vdisplay). */
> +# define VC4_HDMI_VERTA_VAL_MASK		VC4_MASK(12, 0)
> +# define VC4_HDMI_VERTA_VAL_SHIFT		0
> +
> +#define VC4_HDMI_VERTB0				0x0d0
> +#define VC4_HDMI_VERTB1				0x0d8
> +/* Vertical sync pulse offset (for interlaced) */
> +# define VC4_HDMI_VERTB_VSPO_MASK		VC4_MASK(21, 9)
> +# define VC4_HDMI_VERTB_VSPO_SHIFT		9
> +/* Vertical pack porch (vtotal - vsync_end). */
> +# define VC4_HDMI_VERTB_VBP_MASK		VC4_MASK(8, 0)
> +# define VC4_HDMI_VERTB_VBP_SHIFT		0
> +
> +#define VC4_HDMI_TX_PHY_RESET_CTL		0x2c0
> +
> +#define VC4_HD_M_CTL				0x00c
> +# define VC4_HD_M_SW_RST			(1 << 2)
> +# define VC4_HD_M_ENABLE			(1 << 0)
> +
> +#define VC4_HD_MAI_CTL				0x014
> +
> +#define VC4_HD_VID_CTL				0x038
> +# define VC4_HD_VID_CTL_ENABLE			(1 << 31)
> +# define VC4_HD_VID_CTL_UNDERFLOW_ENABLE	(1 << 30)
> +# define VC4_HD_VID_CTL_FRAME_COUNTER_RESET	(1 << 29)
> +# define VC4_HD_VID_CTL_VSYNC_LOW		(1 << 28)
> +# define VC4_HD_VID_CTL_HSYNC_LOW		(1 << 27)
> +
> +#define VC4_HD_CSC_CTL				0x040
> +# define VC4_HD_CSC_CTL_ORDER_MASK		VC4_MASK(7, 5)
> +# define VC4_HD_CSC_CTL_ORDER_SHIFT		5
> +# define VC4_HD_CSC_CTL_ORDER_RGB		0
> +# define VC4_HD_CSC_CTL_ORDER_BGR		1
> +# define VC4_HD_CSC_CTL_ORDER_BRG		2
> +# define VC4_HD_CSC_CTL_ORDER_GRB		3
> +# define VC4_HD_CSC_CTL_ORDER_GBR		4
> +# define VC4_HD_CSC_CTL_ORDER_RBG		5
> +# define VC4_HD_CSC_CTL_PADMSB			(1 << 4)
> +# define VC4_HD_CSC_CTL_MODE_MASK		VC4_MASK(3, 2)
> +# define VC4_HD_CSC_CTL_MODE_SHIFT		2
> +# define VC4_HD_CSC_CTL_MODE_RGB_TO_SD_YPRPB	0
> +# define VC4_HD_CSC_CTL_MODE_RGB_TO_HD_YPRPB	1
> +# define VC4_HD_CSC_CTL_MODE_CUSTOM		2
> +# define VC4_HD_CSC_CTL_RGB2YCC			(1 << 1)
> +# define VC4_HD_CSC_CTL_ENABLE			(1 << 0)
> +
> +#define VC4_HD_FRAME_COUNT			0x068
> +
> +/* HVS display list information. */
> +#define HVS_BOOTLOADER_DLIST_END                32
> +
> +enum hvs_pixel_format {
> +	/* 8bpp */
> +	HVS_PIXEL_FORMAT_RGB332 = 0,
> +	/* 16bpp */
> +	HVS_PIXEL_FORMAT_RGBA4444 = 1,
> +	HVS_PIXEL_FORMAT_RGB555 = 2,
> +	HVS_PIXEL_FORMAT_RGBA5551 = 3,
> +	HVS_PIXEL_FORMAT_RGB565 = 4,
> +	/* 24bpp */
> +	HVS_PIXEL_FORMAT_RGB888 = 5,
> +	HVS_PIXEL_FORMAT_RGBA6666 = 6,
> +	/* 32bpp */
> +	HVS_PIXEL_FORMAT_RGBA8888 = 7
> +};
> +
> +/* Note: the LSB is the rightmost character shown.  Only valid for
> + * HVS_PIXEL_FORMAT_RGB8888, not RGB888.
> + */
> +#define HVS_PIXEL_ORDER_RGBA			0
> +#define HVS_PIXEL_ORDER_BGRA			1
> +#define HVS_PIXEL_ORDER_ARGB			2
> +#define HVS_PIXEL_ORDER_ABGR			3
> +
> +#define HVS_PIXEL_ORDER_XBRG			0
> +#define HVS_PIXEL_ORDER_XRBG			1
> +#define HVS_PIXEL_ORDER_XRGB			2
> +#define HVS_PIXEL_ORDER_XBGR			3
> +
> +#define HVS_PIXEL_ORDER_XYCBCR			0
> +#define HVS_PIXEL_ORDER_XYCRCB			1
> +#define HVS_PIXEL_ORDER_YXCBCR			2
> +#define HVS_PIXEL_ORDER_YXCRCB			3
> +
> +#define SCALER_CTL0_END				(1 << 31)
> +#define SCALER_CTL0_VALID			(1 << 30)
> +
> +#define SCALER_CTL0_SIZE_MASK			VC4_MASK(29, 24)
> +#define SCALER_CTL0_SIZE_SHIFT			24
> +
> +#define SCALER_CTL0_HFLIP                       (1 << 16)
> +#define SCALER_CTL0_VFLIP                       (1 << 15)
> +
> +#define SCALER_CTL0_ORDER_MASK			VC4_MASK(14, 13)
> +#define SCALER_CTL0_ORDER_SHIFT			13
> +
> +/* Set to indicate no scaling. */
> +#define SCALER_CTL0_UNITY			(1 << 4)
> +
> +#define SCALER_CTL0_PIXEL_FORMAT_MASK		VC4_MASK(3, 0)
> +#define SCALER_CTL0_PIXEL_FORMAT_SHIFT		0
> +
> +#define SCALER_POS0_FIXED_ALPHA_MASK		VC4_MASK(31, 24)
> +#define SCALER_POS0_FIXED_ALPHA_SHIFT		24
> +
> +#define SCALER_POS0_START_Y_MASK		VC4_MASK(23, 12)
> +#define SCALER_POS0_START_Y_SHIFT		12
> +
> +#define SCALER_POS0_START_X_MASK		VC4_MASK(11, 0)
> +#define SCALER_POS0_START_X_SHIFT		0
> +
> +#define SCALER_POS2_ALPHA_MODE_MASK		VC4_MASK(31, 30)
> +#define SCALER_POS2_ALPHA_MODE_SHIFT		30
> +#define SCALER_POS2_ALPHA_MODE_PIPELINE		0
> +#define SCALER_POS2_ALPHA_MODE_FIXED		1
> +#define SCALER_POS2_ALPHA_MODE_FIXED_NONZERO	2
> +#define SCALER_POS2_ALPHA_MODE_FIXED_OVER_0x07	3
> +
> +#define SCALER_POS2_HEIGHT_MASK			VC4_MASK(27, 16)
> +#define SCALER_POS2_HEIGHT_SHIFT		16
> +
> +#define SCALER_POS2_WIDTH_MASK			VC4_MASK(11, 0)
> +#define SCALER_POS2_WIDTH_SHIFT			0
> +
> +#define SCALER_SRC_PITCH_MASK			VC4_MASK(15, 0)
> +#define SCALER_SRC_PITCH_SHIFT			0
> -- 
> 2.1.4
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From robdclark at gmail.com  Tue Aug 25 13:42:18 2015
From: robdclark at gmail.com (Rob Clark)
Date: Tue, 25 Aug 2015 16:42:18 -0400
Subject: [PATCH 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <CAL_JsqJxiGUR5BxBPyR3-0NxqWsOxMGrWQA6cMXe2pjTom__nw@mail.gmail.com>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-2-git-send-email-eric@anholt.net>
 <55CEC25E.7070303@wwwdotorg.org>
 <874mjxwyx5.fsf@eliezer.anholt.net>
 <CAL_JsqJxiGUR5BxBPyR3-0NxqWsOxMGrWQA6cMXe2pjTom__nw@mail.gmail.com>
Message-ID: <CAF6AEGu2fi0M7DsicHz+5SDHi1DyDRmvWjkmYPPgY6pZ423WcA@mail.gmail.com>

On Mon, Aug 24, 2015 at 9:47 AM, Rob Herring <robherring2 at gmail.com> wrote:
> On Mon, Aug 17, 2015 at 1:30 PM, Eric Anholt <eric at anholt.net> wrote:
>> Stephen Warren <swarren at wwwdotorg.org> writes:
>>
>>> On 08/12/2015 06:56 PM, Eric Anholt wrote:
>>>> Signed-off-by: Eric Anholt <eric at anholt.net>
>>>
>>> This one definitely needs a patch description, since someone might not
>>> know what a VC4 is, and "git log" won't show the text from the binding
>>> doc itself. I'd suggest adding the initial paragraph of the binding doc
>>> as the patch description, or more.
>>>
>>>> diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
>>
>>>> +- hvss:             List of references to HVS video scalers
>>>> +- encoders: List of references to output encoders (HDMI, SDTV)
>>>
>>> Would it make sense to make all those nodes child node of the vc4
>>> object. That way, there's no need to have these lists of objects; they
>>> can be automatically built up as the DT is enumerated. I know that e.g.
>>> the NVIDIA Tegra host1x binding works this way, and I think it may have
>>> been inspired by other similar cases.
>>
>> I've looked at tegra, and the component system used by msm appears to be
>> nicer than it.  To follow tegra's model, it looks like I need to build
>> this extra bus thing corresponding to host1x that is effectively the
>> drivers/base/component.c code, so that I can get at vc4's structure from
>> the component drivers.
>>
>>> Of course, this is only appropriate if the HW modules really are
>>> logically children of the VC4 HW module. Perhaps they aren't. If they
>>> aren't though, I wonder what this "vc4" module actually represents in HW?
>>
>> It's the subsystem, same as we use a subsystem node for msm, sti,
>> rockchip, imx, and exynos.  This appears to be the common model of how
>> the collection of graphics-related components is represented in the DT.
>
> I think most of these bindings are wrong. They are grouped together
> because that is what DRM wants not because that reflects the h/w. So
> convince me this is one block, not that it is what other people do.

I think, when it comes to more complex driver subsystems (like drm in
particular) we have a bit of mismatch between how things look from the
"pure hw ignoring sw" perspective, and the "how sw and in particular
userspace expects things" perspective.  Maybe it is less a problem in
other subsystems, where bindings map to things that are only visible
in the kernel, or well defined devices like uart or sata controller.
But when given the choice, I'm going to err on the side of not
confusing userspace and the large software stack that sits above
drm/kms, over dt purity.

Maybe it would be nice to have a sort of dt overlay that adds the bits
needed to tie together hw blocks that should be assembled into a
single logical device for linux and userspace (but maybe not some
other hypothetical operating system).  But so far that doesn't exist.
All we have is a hammer (devicetree), everything looks like a nail.
End result is we end up adding some things in the bindings which
aren't purely about the hw.  Until someone invents a screwdriver, I'm
not sure what else to do.  In the end, other hypothetical OS is free
to ignore those extra fields in the bindings if it doesn't need them.
So meh?

BR,
-R


> Rob
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel


From robherring2 at gmail.com  Tue Aug 25 16:22:29 2015
From: robherring2 at gmail.com (Rob Herring)
Date: Tue, 25 Aug 2015 18:22:29 -0500
Subject: [PATCH 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <CAF6AEGu2fi0M7DsicHz+5SDHi1DyDRmvWjkmYPPgY6pZ423WcA@mail.gmail.com>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-2-git-send-email-eric@anholt.net>
 <55CEC25E.7070303@wwwdotorg.org>
 <874mjxwyx5.fsf@eliezer.anholt.net>
 <CAL_JsqJxiGUR5BxBPyR3-0NxqWsOxMGrWQA6cMXe2pjTom__nw@mail.gmail.com>
 <CAF6AEGu2fi0M7DsicHz+5SDHi1DyDRmvWjkmYPPgY6pZ423WcA@mail.gmail.com>
Message-ID: <CAL_JsqJpWmEpOZK5-HG5Xz8J-h7SX8ViytagjxGLdeMwXeONow@mail.gmail.com>

On Tue, Aug 25, 2015 at 3:42 PM, Rob Clark <robdclark at gmail.com> wrote:
> On Mon, Aug 24, 2015 at 9:47 AM, Rob Herring <robherring2 at gmail.com> wrote:
>> On Mon, Aug 17, 2015 at 1:30 PM, Eric Anholt <eric at anholt.net> wrote:
>>> Stephen Warren <swarren at wwwdotorg.org> writes:
>>>
>>>> On 08/12/2015 06:56 PM, Eric Anholt wrote:
>>>>> Signed-off-by: Eric Anholt <eric at anholt.net>
>>>>
>>>> This one definitely needs a patch description, since someone might not
>>>> know what a VC4 is, and "git log" won't show the text from the binding
>>>> doc itself. I'd suggest adding the initial paragraph of the binding doc
>>>> as the patch description, or more.
>>>>
>>>>> diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
>>>
>>>>> +- hvss:             List of references to HVS video scalers
>>>>> +- encoders: List of references to output encoders (HDMI, SDTV)
>>>>
>>>> Would it make sense to make all those nodes child node of the vc4
>>>> object. That way, there's no need to have these lists of objects; they
>>>> can be automatically built up as the DT is enumerated. I know that e.g.
>>>> the NVIDIA Tegra host1x binding works this way, and I think it may have
>>>> been inspired by other similar cases.
>>>
>>> I've looked at tegra, and the component system used by msm appears to be
>>> nicer than it.  To follow tegra's model, it looks like I need to build
>>> this extra bus thing corresponding to host1x that is effectively the
>>> drivers/base/component.c code, so that I can get at vc4's structure from
>>> the component drivers.
>>>
>>>> Of course, this is only appropriate if the HW modules really are
>>>> logically children of the VC4 HW module. Perhaps they aren't. If they
>>>> aren't though, I wonder what this "vc4" module actually represents in HW?
>>>
>>> It's the subsystem, same as we use a subsystem node for msm, sti,
>>> rockchip, imx, and exynos.  This appears to be the common model of how
>>> the collection of graphics-related components is represented in the DT.
>>
>> I think most of these bindings are wrong. They are grouped together
>> because that is what DRM wants not because that reflects the h/w. So
>> convince me this is one block, not that it is what other people do.
>
> I think, when it comes to more complex driver subsystems (like drm in
> particular) we have a bit of mismatch between how things look from the
> "pure hw ignoring sw" perspective, and the "how sw and in particular
> userspace expects things" perspective.  Maybe it is less a problem in
> other subsystems, where bindings map to things that are only visible
> in the kernel, or well defined devices like uart or sata controller.
> But when given the choice, I'm going to err on the side of not
> confusing userspace and the large software stack that sits above
> drm/kms, over dt purity.

I wasn't implying that this should get exposed to userspace as
components. V4L2 has gone that route with media controller and
sub-devs. Perhaps that is needed for DRM, perhaps not. For the moment,
I definitely agree the kernel should hide most/all of those details,
but I don't think that means DT has to hide the details or know what
components are handled by a single driver. My point was that on the DT
side we have a mixture of OF graph usage, parent-child nodes or custom
phandles (this case) to describe the relationships between h/w
components. That's not necessarily wrong, but we should have some
rules around how certain relationships are described. Then in the
drivers we have a mixture of deferred probe, component API, and custom
inter-module APIs to control init order. We then have a mixture of all
those which leads to very few if any drivers having the same overall
structure that could be shared. Should we mandate using the component
API for h/w that is discrete blocks? Should we throw out the component
API for something else? Can we tie the graph parsing and component API
together with common code?


> Maybe it would be nice to have a sort of dt overlay that adds the bits
> needed to tie together hw blocks that should be assembled into a
> single logical device for linux and userspace (but maybe not some
> other hypothetical operating system).  But so far that doesn't exist.

OF graph is supposed to do this. OF graph is a double edged sword. It
is very flexible, but then each platform can do something different.
We need to have some level of requirements around how the OF graph is
used. As an example, any system with an HDMI connector should have an
"hdmi-connector" compatible node or encoder/bridge chips/blocks must
have certain ports defined.


> All we have is a hammer (devicetree), everything looks like a nail.
> End result is we end up adding some things in the bindings which
> aren't purely about the hw.  Until someone invents a screwdriver, I'm
> not sure what else to do.  In the end, other hypothetical OS is free
> to ignore those extra fields in the bindings if it doesn't need them.
> So meh?

We really want to err on the side of fewer bindings, not more as once
used they are an ABI.

Rob


From swarren at wwwdotorg.org  Tue Aug 25 19:02:59 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Tue, 25 Aug 2015 20:02:59 -0600
Subject: [PATCH v2 6/7] ARM: bcm2835: Add the DDC I2C controller to the
 device tree.
In-Reply-To: <1439934848-7196-7-git-send-email-eric@anholt.net>
References: <1439934848-7196-1-git-send-email-eric@anholt.net>
 <1439934848-7196-7-git-send-email-eric@anholt.net>
Message-ID: <55DD1E53.1050608@wwwdotorg.org>

On 08/18/2015 03:54 PM, Eric Anholt wrote:
> We need to use it for getting video modes over HDMI.

This patch,
Acked-by: Stephen Warren <swarren at wwwdotorg.org>


From swarren at wwwdotorg.org  Tue Aug 25 19:04:36 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Tue, 25 Aug 2015 20:04:36 -0600
Subject: [PATCH v2 7/7] ARM: bcm2835: Add VC4 to the device tree.
In-Reply-To: <1439934848-7196-8-git-send-email-eric@anholt.net>
References: <1439934848-7196-1-git-send-email-eric@anholt.net>
 <1439934848-7196-8-git-send-email-eric@anholt.net>
Message-ID: <55DD1EB4.9000401@wwwdotorg.org>

On 08/18/2015 03:54 PM, Eric Anholt wrote:
> VC4 is the GPU (display and 3D) present on the 2835.

This patch and patch 1 seem OK to me, although I'll withhold any ack
until the DT binding design discussion with Rob has been resolved. I
haven't looked at the OF graph bindings he mentioned so have no clue
about his suggestions.


From libv at skynet.be  Wed Aug 26 02:44:39 2015
From: libv at skynet.be (Luc Verhaegen)
Date: Wed, 26 Aug 2015 11:44:39 +0200
Subject: [PATCH v2 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <1439934848-7196-2-git-send-email-eric@anholt.net>
References: <1439934848-7196-1-git-send-email-eric@anholt.net>
 <1439934848-7196-2-git-send-email-eric@anholt.net>
Message-ID: <20150826094438.GB2032@skynet.be>

On Tue, Aug 18, 2015 at 02:54:02PM -0700, Eric Anholt wrote:
> VC4 is the GPU (display and 3D) subsystem present on the 2835 and some
> other Broadcom SoCs.
> ...
> +Broadcom VC4 GPU
> +
> +The VC4 device present on the Raspberry Pi includes a display system
> +with HDMI output and the HVS scaler for compositing display planes.

Is this a direct hw driver implementation, banging registers, or is this 
talking to the os/firmware/binary blob running on the videocore dsp?

Luc Verhaegen.


From kernel at martin.sperl.org  Wed Aug 26 03:28:29 2015
From: kernel at martin.sperl.org (Martin Sperl)
Date: Wed, 26 Aug 2015 12:28:29 +0200
Subject: [PATCH v4 0/5] bcm2835: auxiliar device support for spi
In-Reply-To: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
References: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
Message-ID: <55DD94CD.9070705@martin.sperl.org>


On 24.08.2015 10:40, kernel at martin.sperl.org wrote:
> From: Martin Sperl <kernel at martin.sperl.org>
>
> This patch-series implements:
> * the bcm2835-auxiliar device enable/disable api in soc.
> * the bcm2835-auxiliar spi device driver
>
> The uart1 device driver (ns16550 based) is not implemented so far
> but should be using the same API.

As I need to implement another version (see reviews by Stephen)
here a question related to the uart1 and device-tree (as I now got
a compute module to test it)

The uart1 is register-wise compatible with a ns16550, so the
8250 driver can get used almost directly.

Looking at implementations for different platforms I often find:
	compatible = "xlnx,axi-uart16550-1.01.a",
			 "xlnx,xps-uart16550-2.00.a",
			 "ns16550";

Also the foundation kernel does something similar.

The question here is: Would the following device-tree be acceptable?
uart1: uart at 7e215040 {
	...
	compatible = "bcrm,bcm2835-aux-uart", "ns16550";
	brcm,aux-enable = <&aux_enable 1>;
	...
};

So that the driver for "bcrm,bcm2835-aux-uart" would only need to
contain the "probe" and "release" code to enable/disable the HW block.

Is this acceptable or would I need to implement a "more" in depth
driver (so that "ns16550" is not needed in compatiblity) to be accepted
on the device-tree side?

Thanks,
	Martin


From daniel at ffwll.ch  Wed Aug 26 04:52:29 2015
From: daniel at ffwll.ch (Daniel Vetter)
Date: Wed, 26 Aug 2015 13:52:29 +0200
Subject: [PATCH 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <CAF6AEGu2fi0M7DsicHz+5SDHi1DyDRmvWjkmYPPgY6pZ423WcA@mail.gmail.com>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-2-git-send-email-eric@anholt.net>
 <55CEC25E.7070303@wwwdotorg.org>
 <874mjxwyx5.fsf@eliezer.anholt.net>
 <CAL_JsqJxiGUR5BxBPyR3-0NxqWsOxMGrWQA6cMXe2pjTom__nw@mail.gmail.com>
 <CAF6AEGu2fi0M7DsicHz+5SDHi1DyDRmvWjkmYPPgY6pZ423WcA@mail.gmail.com>
Message-ID: <20150826115229.GI1367@phenom.ffwll.local>

On Tue, Aug 25, 2015 at 04:42:18PM -0400, Rob Clark wrote:
> On Mon, Aug 24, 2015 at 9:47 AM, Rob Herring <robherring2 at gmail.com> wrote:
> > On Mon, Aug 17, 2015 at 1:30 PM, Eric Anholt <eric at anholt.net> wrote:
> >> Stephen Warren <swarren at wwwdotorg.org> writes:
> >>
> >>> On 08/12/2015 06:56 PM, Eric Anholt wrote:
> >>>> Signed-off-by: Eric Anholt <eric at anholt.net>
> >>>
> >>> This one definitely needs a patch description, since someone might not
> >>> know what a VC4 is, and "git log" won't show the text from the binding
> >>> doc itself. I'd suggest adding the initial paragraph of the binding doc
> >>> as the patch description, or more.
> >>>
> >>>> diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
> >>
> >>>> +- hvss:             List of references to HVS video scalers
> >>>> +- encoders: List of references to output encoders (HDMI, SDTV)
> >>>
> >>> Would it make sense to make all those nodes child node of the vc4
> >>> object. That way, there's no need to have these lists of objects; they
> >>> can be automatically built up as the DT is enumerated. I know that e.g.
> >>> the NVIDIA Tegra host1x binding works this way, and I think it may have
> >>> been inspired by other similar cases.
> >>
> >> I've looked at tegra, and the component system used by msm appears to be
> >> nicer than it.  To follow tegra's model, it looks like I need to build
> >> this extra bus thing corresponding to host1x that is effectively the
> >> drivers/base/component.c code, so that I can get at vc4's structure from
> >> the component drivers.
> >>
> >>> Of course, this is only appropriate if the HW modules really are
> >>> logically children of the VC4 HW module. Perhaps they aren't. If they
> >>> aren't though, I wonder what this "vc4" module actually represents in HW?
> >>
> >> It's the subsystem, same as we use a subsystem node for msm, sti,
> >> rockchip, imx, and exynos.  This appears to be the common model of how
> >> the collection of graphics-related components is represented in the DT.
> >
> > I think most of these bindings are wrong. They are grouped together
> > because that is what DRM wants not because that reflects the h/w. So
> > convince me this is one block, not that it is what other people do.
> 
> I think, when it comes to more complex driver subsystems (like drm in
> particular) we have a bit of mismatch between how things look from the
> "pure hw ignoring sw" perspective, and the "how sw and in particular
> userspace expects things" perspective.  Maybe it is less a problem in
> other subsystems, where bindings map to things that are only visible
> in the kernel, or well defined devices like uart or sata controller.
> But when given the choice, I'm going to err on the side of not
> confusing userspace and the large software stack that sits above
> drm/kms, over dt purity.
> 
> Maybe it would be nice to have a sort of dt overlay that adds the bits
> needed to tie together hw blocks that should be assembled into a
> single logical device for linux and userspace (but maybe not some
> other hypothetical operating system).  But so far that doesn't exist.
> All we have is a hammer (devicetree), everything looks like a nail.
> End result is we end up adding some things in the bindings which
> aren't purely about the hw.  Until someone invents a screwdriver, I'm
> not sure what else to do.  In the end, other hypothetical OS is free
> to ignore those extra fields in the bindings if it doesn't need them.
> So meh?

I thought we agreed a while back that these kind of "pull everything for
the logical device together" dt nodes which just have piles of phandles
are totally accepted? At least that's the point behind the component
helpers, and Eric even suggested to create dt-specific component helpers
to cut down a bit on the usual boilerplate. dt maintainers are also fine
with this approach afaik. From my understanding tegra with the host1x bus
really is the odd one out and not the norm.

Given that and with the hope that we'll eventually see a dt-enabled
component functions to standardize this even more the overall concept is

Acked-by: Daniel Vetter <daniel.vetter at ffwll.ch>

Cheers, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From thierry.reding at gmail.com  Wed Aug 26 04:51:50 2015
From: thierry.reding at gmail.com (Thierry Reding)
Date: Wed, 26 Aug 2015 13:51:50 +0200
Subject: [PATCH 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <55CEC25E.7070303@wwwdotorg.org>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-2-git-send-email-eric@anholt.net>
 <55CEC25E.7070303@wwwdotorg.org>
Message-ID: <20150826115148.GB320@ulmo.nvidia.com>

On Fri, Aug 14, 2015 at 10:38:54PM -0600, Stephen Warren wrote:
> On 08/12/2015 06:56 PM, Eric Anholt wrote:
> > Signed-off-by: Eric Anholt <eric at anholt.net>
> 
> This one definitely needs a patch description, since someone might not
> know what a VC4 is, and "git log" won't show the text from the binding
> doc itself. I'd suggest adding the initial paragraph of the binding doc
> as the patch description, or more.
> 
> > diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
> 
> > +Required properties for VC4:
> > +- compatible:	Should be "brcm,vc4"
> > +- crtcs:	List of references to pixelvalve scanout engines
> 
> s/references to/phandles of/ would be more typical DT language.
> 
> > +- hvss:		List of references to HVS video scalers
> > +- encoders:	List of references to output encoders (HDMI, SDTV)
> 
> Would it make sense to make all those nodes child node of the vc4
> object. That way, there's no need to have these lists of objects; they
> can be automatically built up as the DT is enumerated. I know that e.g.
> the NVIDIA Tegra host1x binding works this way, and I think it may have
> been inspired by other similar cases.

Actually the host1x binding was the first of its kind. Unfortunately for
the purposes of this discussion (but fortunately otherwise) Tegra is the
odd-ball it seems. host1x is indeed a physical parent of all the devices
pertaining to the DRM driver, so the DT description is accurate from a
hardware point of view while at the same time giving us a top-level
device that we can bind against.

Now for most other cases it seems like the central piece that they are
missing is this top-level device, hence why the "virtual DRM subsystem
device" is instantiated. I tried to argue in the past that it wasn't a
proper description and proposed alternatives, but I was always pretty
much the only one with this viewpoint, so my comments ended up being
ignored.

Technically there is nothing that would prevent other drivers from doing
without the lists of phandles. On Tegra, again this might be special for
this particular hardware, we've never had a need to describe these kinds
of relationships. Each display controller can essentially drive each of
the outputs, which we deal with elegantly by setting the .possible_crtcs
mask of the encoders.

Also, to pull together all devices that are needed to make up the DRM
device, we use a list of compatible strings in the driver to find these
devices. Then as each of them registers with the host1x bus we wait for
the subdevice list to become empty and ->probe() the component host1x
device.

Note that while this predates component/master, this is all very similar
in principle (Russell and I did have some discussions about this back at
the time, but I'm not sure how much, if anything, he took as inspiration
from the host1x infrastructure). After component/master was merged I did
try to convert Tegra DRM to use it. Things looked pretty good, but ended
up not working because each componentized device must have a unique
master device. This poses a problem because on Tegra we needed the top-
level (i.e. master) device to be shared among multiple drivers.

I posted patches at some point to try and fix remedy the situation but
wasn't able to elicit any reactions, and since I had something that was
working did not pursue this any further.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150826/6ac61672/attachment.sig>

From thierry.reding at gmail.com  Wed Aug 26 05:09:36 2015
From: thierry.reding at gmail.com (Thierry Reding)
Date: Wed, 26 Aug 2015 14:09:36 +0200
Subject: [PATCH 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <20150826115229.GI1367@phenom.ffwll.local>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-2-git-send-email-eric@anholt.net>
 <55CEC25E.7070303@wwwdotorg.org>
 <874mjxwyx5.fsf@eliezer.anholt.net>
 <CAL_JsqJxiGUR5BxBPyR3-0NxqWsOxMGrWQA6cMXe2pjTom__nw@mail.gmail.com>
 <CAF6AEGu2fi0M7DsicHz+5SDHi1DyDRmvWjkmYPPgY6pZ423WcA@mail.gmail.com>
 <20150826115229.GI1367@phenom.ffwll.local>
Message-ID: <20150826120934.GC320@ulmo.nvidia.com>

On Wed, Aug 26, 2015 at 01:52:29PM +0200, Daniel Vetter wrote:
> On Tue, Aug 25, 2015 at 04:42:18PM -0400, Rob Clark wrote:
> > On Mon, Aug 24, 2015 at 9:47 AM, Rob Herring <robherring2 at gmail.com> wrote:
> > > On Mon, Aug 17, 2015 at 1:30 PM, Eric Anholt <eric at anholt.net> wrote:
> > >> Stephen Warren <swarren at wwwdotorg.org> writes:
> > >>
> > >>> On 08/12/2015 06:56 PM, Eric Anholt wrote:
> > >>>> Signed-off-by: Eric Anholt <eric at anholt.net>
> > >>>
> > >>> This one definitely needs a patch description, since someone might not
> > >>> know what a VC4 is, and "git log" won't show the text from the binding
> > >>> doc itself. I'd suggest adding the initial paragraph of the binding doc
> > >>> as the patch description, or more.
> > >>>
> > >>>> diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
> > >>
> > >>>> +- hvss:             List of references to HVS video scalers
> > >>>> +- encoders: List of references to output encoders (HDMI, SDTV)
> > >>>
> > >>> Would it make sense to make all those nodes child node of the vc4
> > >>> object. That way, there's no need to have these lists of objects; they
> > >>> can be automatically built up as the DT is enumerated. I know that e.g.
> > >>> the NVIDIA Tegra host1x binding works this way, and I think it may have
> > >>> been inspired by other similar cases.
> > >>
> > >> I've looked at tegra, and the component system used by msm appears to be
> > >> nicer than it.  To follow tegra's model, it looks like I need to build
> > >> this extra bus thing corresponding to host1x that is effectively the
> > >> drivers/base/component.c code, so that I can get at vc4's structure from
> > >> the component drivers.
> > >>
> > >>> Of course, this is only appropriate if the HW modules really are
> > >>> logically children of the VC4 HW module. Perhaps they aren't. If they
> > >>> aren't though, I wonder what this "vc4" module actually represents in HW?
> > >>
> > >> It's the subsystem, same as we use a subsystem node for msm, sti,
> > >> rockchip, imx, and exynos.  This appears to be the common model of how
> > >> the collection of graphics-related components is represented in the DT.
> > >
> > > I think most of these bindings are wrong. They are grouped together
> > > because that is what DRM wants not because that reflects the h/w. So
> > > convince me this is one block, not that it is what other people do.
> > 
> > I think, when it comes to more complex driver subsystems (like drm in
> > particular) we have a bit of mismatch between how things look from the
> > "pure hw ignoring sw" perspective, and the "how sw and in particular
> > userspace expects things" perspective.  Maybe it is less a problem in
> > other subsystems, where bindings map to things that are only visible
> > in the kernel, or well defined devices like uart or sata controller.
> > But when given the choice, I'm going to err on the side of not
> > confusing userspace and the large software stack that sits above
> > drm/kms, over dt purity.
> > 
> > Maybe it would be nice to have a sort of dt overlay that adds the bits
> > needed to tie together hw blocks that should be assembled into a
> > single logical device for linux and userspace (but maybe not some
> > other hypothetical operating system).  But so far that doesn't exist.
> > All we have is a hammer (devicetree), everything looks like a nail.
> > End result is we end up adding some things in the bindings which
> > aren't purely about the hw.  Until someone invents a screwdriver, I'm
> > not sure what else to do.  In the end, other hypothetical OS is free
> > to ignore those extra fields in the bindings if it doesn't need them.
> > So meh?
> 
> I thought we agreed a while back that these kind of "pull everything for
> the logical device together" dt nodes which just have piles of phandles
> are totally accepted? At least that's the point behind the component
> helpers, and Eric even suggested to create dt-specific component helpers
> to cut down a bit on the usual boilerplate. dt maintainers are also fine
> with this approach afaik. From my understanding tegra with the host1x bus
> really is the odd one out and not the norm.

I agree that in many aspects Tegra is somewhat special. But the same
principles that the host1x infrastructure uses could be implemented in a
similar way for other DRM drivers. You can easily collect information
about subdevices by walking the device tree and matching on known
compatible strings. And you can also instantiate the top-level device
from driver code rather than have it in DT. It should still be possible
to make this work without an artificial device node in DT. The component
and master infrastructure is largely orthogonal to that, and as far as I
remember the only blocker is the need for a top-level device. I wonder
if perhaps this could be made to work by binding the master to the top-
level SoC device.

Obviously adding the node in DT is easier, but to my knowledge easy has
never been a good excuse for mangling DT. Perhaps that's different these
days...

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20150826/23936e24/attachment.sig>

From martin at sperl.org  Wed Aug 26 06:57:46 2015
From: martin at sperl.org (Martin Sperl)
Date: Wed, 26 Aug 2015 15:57:46 +0200
Subject: Raspberry Pi Compute Module
In-Reply-To: <55B1B7D1.2030608@wwwdotorg.org>
References: <55A93694.9060004@lategoodbye.de> <55AF10E5.304@wwwdotorg.org>
 <55AFCBC2.5030808@lategoodbye.de> <55B1B7D1.2030608@wwwdotorg.org>
Message-ID: <C99F5FC0-D9B6-4DF1-AD2D-F706F55A5AF8@sperl.org>


> On 24.07.2015, at 05:58, Stephen Warren <swarren at wwwdotorg.org> wrote:
> 
> On 07/22/2015 10:58 AM, Stefan Wahren wrote:
>> Hi Stephen,
>> 
>> Am 22.07.2015 um 05:41 schrieb Stephen Warren:
>>> On 07/17/2015 11:08 AM, Stefan Wahren wrote:
>>>> Hi all,
>>>> 
>>>> is there anyone working on Mainline support for the Compute Module?
>>>> 
>>>> I noticed the downstream kernel already has a bcm2708-rpi-cm.dtsi
>>> 
>>> It is probably close enough that you can simply use bcm2835-rpi-b.dtb
>>> for the CM too, for testing at least.
>>> 
>>> I just tried this with latest U-Boot and a 4.2.0-rc3 kernel. The eMMC
>>> works fine in U-Boot, but the kernel can't access it for some reason,
>>> failing with e.g.:
>>> 
>>> [    3.110503] Waiting for root device /dev/mmcblk0p2...
>>> [    3.123895] mmc0: MAN_BKOPS_EN bit is not set
>>> [    3.132264] mmc0: error -110 whilst initialising MMC card
>>> [    3.788447] cfg80211: Calling CRDA to update world regulatory domain
>>> [    4.204078] mmc0: MAN_BKOPS_EN bit is not set
>>> [    4.212748] mmc0: error -110 whilst initialising MMC card
>>> [    5.283981] mmc0: MAN_BKOPS_EN bit is not set
>>> [    5.292657] mmc0: error -110 whilst initialising MMC card
>>> 
>>> I had hoped that the kernel fixes for the VC MMU high address bits (via
>>> the dma-ranges property in DT) would fix this, but I guess not since it
>>> still doesn't work.
>>> 
>> 
>> thank your for testing.
>> 
>> Does defining the bus width as in bcm2708-rpi-cm.dtsi change anything?
>> 
>> &mmc {
>>    status = "okay";
>>    bus-width = <4>;
>> };
> 
> Unfortunately not.
> 

I am seeing the same thing - maybe the GPIO47 needs to get toggled at some point to output?

See the comment in the CMIO PDF:

BCM2835 BootROM boot from USB:
- J4 set to enable USB boot
- Plug host into micro USB socket J15 (VBUSB=5V)
- GPIO47_1V8 high at boot (input with 50k pullup)
- EMMC_DISABLE_N therefore LOW
- On power up BCM2835 can't access eMMC so boots from USB
- Once booted, 2835 USB boot SW forces GPIO47_1V8 LOW to enable access to eMMC C

BCM2835 BootROM boot from eMMC:
- Nothing plugged into micro USB socket J15 (VBUSB=0V) OR J4 set to disable USB Boot
- EMMC_DISABLE_N therefore HIGH
- On power up BCM2835 boots from eMMC
- GPIO_1B8 used as status LED

BCM2835 BootROM boot from eMMC with GPIO boot select:
- Set J4 to disable USB boot
- EMMC_DISABLE_N therefore always HIGH
- On power up BCM2835 boots from eMMC
- BCM2835 boot SW reads GPIO47_1V8 (GPIO47_CTL_1V8) if low perform 'alternate? boot
  (e.g. can boot into safe mode, or USB mass storage...)
- Once booted GPIO47_1V8 used as status LED

On the CM PDF there is als a NC7SB3157p6X that switches EMMC_CMD to be connected either
SD_CMD (GPIO49) or to 1V8 - depending on the state of EMMC_DISABLE_N (which is on the CMIO
board connected to GPIO47_1V8 via the above mentioned circuitry).

Note that if you boot from USB via u-boot, then everything works (but MMC is not available)

Martin





From robherring2 at gmail.com  Wed Aug 26 07:30:17 2015
From: robherring2 at gmail.com (Rob Herring)
Date: Wed, 26 Aug 2015 09:30:17 -0500
Subject: [PATCH 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <20150826120934.GC320@ulmo.nvidia.com>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-2-git-send-email-eric@anholt.net>
 <55CEC25E.7070303@wwwdotorg.org>
 <874mjxwyx5.fsf@eliezer.anholt.net>
 <CAL_JsqJxiGUR5BxBPyR3-0NxqWsOxMGrWQA6cMXe2pjTom__nw@mail.gmail.com>
 <CAF6AEGu2fi0M7DsicHz+5SDHi1DyDRmvWjkmYPPgY6pZ423WcA@mail.gmail.com>
 <20150826115229.GI1367@phenom.ffwll.local>
 <20150826120934.GC320@ulmo.nvidia.com>
Message-ID: <CAL_JsqJ2v=wBaUe_gN_UcpQKis+kRH6oa0NTN24Lp9w9LVWtbQ@mail.gmail.com>

On Wed, Aug 26, 2015 at 7:09 AM, Thierry Reding
<thierry.reding at gmail.com> wrote:
> On Wed, Aug 26, 2015 at 01:52:29PM +0200, Daniel Vetter wrote:
>> On Tue, Aug 25, 2015 at 04:42:18PM -0400, Rob Clark wrote:
>> > On Mon, Aug 24, 2015 at 9:47 AM, Rob Herring <robherring2 at gmail.com> wrote:
>> > > On Mon, Aug 17, 2015 at 1:30 PM, Eric Anholt <eric at anholt.net> wrote:
>> > >> Stephen Warren <swarren at wwwdotorg.org> writes:
>> > >>
>> > >>> On 08/12/2015 06:56 PM, Eric Anholt wrote:
>> > >>>> Signed-off-by: Eric Anholt <eric at anholt.net>
>> > >>>
>> > >>> This one definitely needs a patch description, since someone might not
>> > >>> know what a VC4 is, and "git log" won't show the text from the binding
>> > >>> doc itself. I'd suggest adding the initial paragraph of the binding doc
>> > >>> as the patch description, or more.
>> > >>>
>> > >>>> diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
>> > >>
>> > >>>> +- hvss:             List of references to HVS video scalers
>> > >>>> +- encoders: List of references to output encoders (HDMI, SDTV)
>> > >>>
>> > >>> Would it make sense to make all those nodes child node of the vc4
>> > >>> object. That way, there's no need to have these lists of objects; they
>> > >>> can be automatically built up as the DT is enumerated. I know that e.g.
>> > >>> the NVIDIA Tegra host1x binding works this way, and I think it may have
>> > >>> been inspired by other similar cases.
>> > >>
>> > >> I've looked at tegra, and the component system used by msm appears to be
>> > >> nicer than it.  To follow tegra's model, it looks like I need to build
>> > >> this extra bus thing corresponding to host1x that is effectively the
>> > >> drivers/base/component.c code, so that I can get at vc4's structure from
>> > >> the component drivers.
>> > >>
>> > >>> Of course, this is only appropriate if the HW modules really are
>> > >>> logically children of the VC4 HW module. Perhaps they aren't. If they
>> > >>> aren't though, I wonder what this "vc4" module actually represents in HW?
>> > >>
>> > >> It's the subsystem, same as we use a subsystem node for msm, sti,
>> > >> rockchip, imx, and exynos.  This appears to be the common model of how
>> > >> the collection of graphics-related components is represented in the DT.
>> > >
>> > > I think most of these bindings are wrong. They are grouped together
>> > > because that is what DRM wants not because that reflects the h/w. So
>> > > convince me this is one block, not that it is what other people do.
>> >
>> > I think, when it comes to more complex driver subsystems (like drm in
>> > particular) we have a bit of mismatch between how things look from the
>> > "pure hw ignoring sw" perspective, and the "how sw and in particular
>> > userspace expects things" perspective.  Maybe it is less a problem in
>> > other subsystems, where bindings map to things that are only visible
>> > in the kernel, or well defined devices like uart or sata controller.
>> > But when given the choice, I'm going to err on the side of not
>> > confusing userspace and the large software stack that sits above
>> > drm/kms, over dt purity.
>> >
>> > Maybe it would be nice to have a sort of dt overlay that adds the bits
>> > needed to tie together hw blocks that should be assembled into a
>> > single logical device for linux and userspace (but maybe not some
>> > other hypothetical operating system).  But so far that doesn't exist.
>> > All we have is a hammer (devicetree), everything looks like a nail.
>> > End result is we end up adding some things in the bindings which
>> > aren't purely about the hw.  Until someone invents a screwdriver, I'm
>> > not sure what else to do.  In the end, other hypothetical OS is free
>> > to ignore those extra fields in the bindings if it doesn't need them.
>> > So meh?
>>
>> I thought we agreed a while back that these kind of "pull everything for
>> the logical device together" dt nodes which just have piles of phandles
>> are totally accepted? At least that's the point behind the component
>> helpers, and Eric even suggested to create dt-specific component helpers
>> to cut down a bit on the usual boilerplate. dt maintainers are also fine
>> with this approach afaik. From my understanding tegra with the host1x bus
>> really is the odd one out and not the norm.
>
> I agree that in many aspects Tegra is somewhat special. But the same
> principles that the host1x infrastructure uses could be implemented in a
> similar way for other DRM drivers. You can easily collect information
> about subdevices by walking the device tree and matching on known
> compatible strings. And you can also instantiate the top-level device
> from driver code rather than have it in DT. It should still be possible
> to make this work without an artificial device node in DT. The component
> and master infrastructure is largely orthogonal to that, and as far as I
> remember the only blocker is the need for a top-level device. I wonder
> if perhaps this could be made to work by binding the master to the top-
> level SoC device.
>
> Obviously adding the node in DT is easier, but to my knowledge easy has
> never been a good excuse for mangling DT. Perhaps that's different these
> days...

I agree we should avoid the virtual node if possible. It is certainly
possible as I started out with one and removed it. At least in my
case, it essentially required the drm_device and crtc to be a single
driver rather than 2 components. However, I'm more concerned that we
are consistent from platform to platform where it makes sense than
whether we have a somewhat questionable node or not.

Rob


From lee at kernel.org  Thu Aug 27 03:48:54 2015
From: lee at kernel.org (Lee Jones)
Date: Thu, 27 Aug 2015 11:48:54 +0100
Subject: [PATCH v5 3/3] ARM: bcm2835: Add DT for the firmware clocks
 driver.
In-Reply-To: <55CEBC6B.9070001@wwwdotorg.org>
References: <1439507142-2965-1-git-send-email-eric@anholt.net>
 <1439507142-2965-4-git-send-email-eric@anholt.net>
 <55CEBC6B.9070001@wwwdotorg.org>
Message-ID: <20150827104854.GA4796@x1>

On Fri, 14 Aug 2015, Stephen Warren wrote:

> On 08/13/2015 05:05 PM, Eric Anholt wrote:
> > Signed-off-by: Eric Anholt <eric at anholt.net>
> > Acked-by: Lee Jones <lee at kernel.org>
> 
> A patch description might be nice, although admittedly the subject seems
> clear enough.

Agreed that the subject is clear enough on this occasion.

> > diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi
> 
> > +		firmware_clocks: firmware-clocks {
> > +			compatible = "raspberrypi,bcm2835-firmware-clocks";
> > +			#clock-cells = <1>;
> > +			raspberrypi,firmware = <&firmware>;
> > +		};
> 
> No need to resend for this, but just a background note: DT node names
> usually contain a device type not a device identity, so "clocks" not
> "firmware-clocks" would be typical.

Please fix this up when you re-send the set.

> This patch,
> Acked-by: Stephen Warren <swarren at wwwdotorg.org>


From lee at kernel.org  Thu Aug 27 04:10:46 2015
From: lee at kernel.org (Lee Jones)
Date: Thu, 27 Aug 2015 12:10:46 +0100
Subject: [GIT PULL] RPi DT changes due for v4.3
In-Reply-To: <878u9dhggh.fsf@eliezer.anholt.net>
References: <20150811160642.GQ18282@x1> <20150813103056.GY30160@localhost>
 <878u9dhggh.fsf@eliezer.anholt.net>
Message-ID: <20150827111046.GB4796@x1>

On Fri, 14 Aug 2015, Eric Anholt wrote:
> Olof Johansson <olof at lixom.net> writes:
> > On Tue, Aug 11, 2015 at 05:06:42PM +0100, Lee Jones wrote:
> >> ARM SoC Chaps,
> >> 
> >> Please find a couple of simple RPi changes pertaining to Firmware.
> >> 
> >> The following changes since commit bc0195aad0daa2ad5b0d76cce22b167bc3435590:
> >> 
> >>   Linux 4.2-rc2 (2015-07-12 15:10:30 -0700)
> >> 
> >> are available in the git repository at:
> >> 
> >>   git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git tags/rpi-dt-for-armsoc-4.3
> >> 
> >> for you to fetch changes up to fd26f8830979de48eb3f1c253eb9d2ee2e468eb6:
> >> 
> >>   dt/bindings: Add binding for the Raspberry Pi firmware driver (2015-08-11 16:56:41 +0100)
> >
> > I've merged this but I'm a little confused about bcm2835 maintainership:
> > I got a MAINTAINERS update from Florian earlier grouped in a set of
> > other bcm changes (adding Eric), and now this directly from you.
> >
> > In general the bcm platforms are quite diverse and we've asked Florian
> > to be the funnel for all of the new ones, but I don't think we talked
> > much about 2835 at that time. Should we expect to see merges directly
> > from you and the other maintainers there or will it go through Florian?
> 
> I'd been talking with Arnd and Florian about the delays in RPi patch
> acceptance (Stephen and Lee have limited time to dedicate to 2835), and
> Florian suggested me becoming a maintainer and routing patches through
> him.  He's been great to work with and has had useful feedback on my
> patches.
> 
> I was kind of surprised to see Lee send this pull request -- these
> patches had been sitting un-merged for 2 months and pings on irc didn't
> get replies, so I thought I was stepping in where the others definitely
> didn't have time.  It was also odd to see just these two patches, but
> not the actual driver.
> 
> I'm willing to do the merging process for 2835, and if I'm doing so, I
> like the idea of feeding it through a more experienced maintainer who's
> ready to look at my pull requests whenever, without bothering the main
> arm-soc folks.  My goal here, besides my actual job of getting open
> source graphics working for Broadcom, is to hopefully push the rpi
> foundation toward getting their code merged, so that this hardware fully
> works on stock upstream.

Apologies for the confusion caused by this pull-request.  After
speaking with Florian last cycle the plan was to send the request to
him prior to him sending to ARM-SoC; however, I didn't realise how
early in the cycle he was going to send that request and I missed the
boat.  So rather than miss the cycle, I decided just to send directly
to ARM-SoC instead.

I do resent the IRQ comment though, as I'm always on IRC during
working hours and (unless something has gone wrong) I have a proxy
which collects missed messages and replays them back to me, so in
theory I shouldn't miss a message.  All messages you've sent to me
I've replied to.

The Firmware patches wouldn't have ever been part of this set without
explicit request/permission from the Maintainer.  So that's why the
driver patches weren't part of this set.

How would you like to move forward?  Do you still want me to apply
patches to the RPi repo and send Florian a pull-request early in the
cycle (say -rc4 for example), or would you rather Florian picked the
patches form the list directly?


From martin at sperl.org  Thu Aug 27 05:44:16 2015
From: martin at sperl.org (Martin Sperl)
Date: Thu, 27 Aug 2015 14:44:16 +0200
Subject: Raspberry Pi Compute Module
In-Reply-To: <C99F5FC0-D9B6-4DF1-AD2D-F706F55A5AF8@sperl.org>
References: <55A93694.9060004@lategoodbye.de> <55AF10E5.304@wwwdotorg.org>
 <55AFCBC2.5030808@lategoodbye.de> <55B1B7D1.2030608@wwwdotorg.org>
 <C99F5FC0-D9B6-4DF1-AD2D-F706F55A5AF8@sperl.org>
Message-ID: <55DF0620.2070801@sperl.org>



On 26.08.2015 15:57, Martin Sperl wrote:
>> On 24.07.2015, at 05:58, Stephen Warren <swarren at wwwdotorg.org> wrote:
>>
>> On 07/22/2015 10:58 AM, Stefan Wahren wrote:
>>> Hi Stephen,
>>>
>>> Am 22.07.2015 um 05:41 schrieb Stephen Warren:
>>>> On 07/17/2015 11:08 AM, Stefan Wahren wrote:
>>>>> Hi all,
>>>>>
>>>>> is there anyone working on Mainline support for the Compute Module?
>>>>>
>>>>> I noticed the downstream kernel already has a bcm2708-rpi-cm.dtsi
>>>> It is probably close enough that you can simply use bcm2835-rpi-b.dtb
>>>> for the CM too, for testing at least.
>>>>
>>>> I just tried this with latest U-Boot and a 4.2.0-rc3 kernel. The eMMC
>>>> works fine in U-Boot, but the kernel can't access it for some reason,
>>>> failing with e.g.:
>>>>
>>>> [    3.110503] Waiting for root device /dev/mmcblk0p2...
>>>> [    3.123895] mmc0: MAN_BKOPS_EN bit is not set
>>>> [    3.132264] mmc0: error -110 whilst initialising MMC card
>>>> [    3.788447] cfg80211: Calling CRDA to update world regulatory domain
>>>> [    4.204078] mmc0: MAN_BKOPS_EN bit is not set
>>>> [    4.212748] mmc0: error -110 whilst initialising MMC card
>>>> [    5.283981] mmc0: MAN_BKOPS_EN bit is not set
>>>> [    5.292657] mmc0: error -110 whilst initialising MMC card
>>>>
>>>> I had hoped that the kernel fixes for the VC MMU high address bits (via
>>>> the dma-ranges property in DT) would fix this, but I guess not since it
>>>> still doesn't work.
>>>> ...

Compiling the kernel with mmc as module plus mmc-debug enabled gives the 
below:
after loading the driver:
root at raspcm:~# dmesg -C; modprobe sdhci_bcm2835; sleep 5; dmesg -c
[   69.376261] sdhci: Secure Digital Host Controller Interface driver
[   69.376291] sdhci: Copyright(c) Pierre Ossman
[   69.389386] sdhci-pltfm: SDHCI platform and OF driver helper
[   69.401402] sdhci [sdhci_add_host()]: Controller doesn't have SDMA 
capability
[   69.401464] sdhci [sdhci_add_host()]: mmc0: Auto-CMD23 available
[   69.401513] sdhci-bcm2835 20300000.sdhci: No vmmc regulator found
[   69.401534] sdhci-bcm2835 20300000.sdhci: No vqmmc regulator found
[   69.401916] sdhci: =========== REGISTER DUMP (mmc0)===========
[   69.401957] sdhci: Sys addr: 0x00000000 | Version:  0x00009902
[   69.401975] sdhci: Blk size: 0x00000000 | Blk cnt:  0x00000000
[   69.401991] sdhci: Argument: 0x00000000 | Trn mode: 0x00000000
[   69.402005] sdhci: Present:  0x01ff0000 | Host ctl: 0x00000000
[   69.402020] sdhci: Power:    0x00000000 | Blk gap:  0x00000000
[   69.402034] sdhci: Wake-up:  0x00000000 | Clock:    0x00000000
[   69.402047] sdhci: Timeout:  0x00000000 | Int stat: 0x00000000
[   69.402073] sdhci: Int enab: 0x00ff0003 | Sig enab: 0x00ff0003
[   69.402087] sdhci: AC12 err: 0x00000000 | Slot int: 0x00000000
[   69.402101] sdhci: Caps:     0x01000000 | Caps_1:   0x00000000
[   69.402114] sdhci: Cmd:      0x00000000 | Max curr: 0x00000001
[   69.402125] sdhci: Host ctl2: 0x00000000
[   69.402136] sdhci: ===========================================
[   69.408285] mmc0: clock 0Hz busmode 2 powermode 1 cs 0 Vdd 21 width 0 
timing 0
[   69.414027] sdhci-bcm2835 20300000.sdhci: Initial signal voltage of 3.3v
[   69.432782] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   69.452773] mmc0: SDHCI controller on 20300000.sdhci [20300000.sdhci] 
using PIO
[   69.454504] mmc0: mmc_rescan_try_freq: trying to init card at 400000 Hz
[   69.454550] mmc0: starting CMD52 arg 00000c00 flags 00000195
[   69.454767] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   69.454846] mmc0: req done (CMD52): -110: 00000000 00000000 00000000 
00000000
[   69.454914] mmc0: starting CMD52 arg 80000c08 flags 00000195
[   69.455117] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   69.455178] mmc0: req done (CMD52): -110: 00000000 00000000 00000000 
00000000
[   69.455268] mmc0: clock 400000Hz busmode 2 powermode 2 cs 1 Vdd 21 
width 0 timing 0
[   69.456456] mmc0: starting CMD0 arg 00000000 flags 000000c0
[   69.456577] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   69.456635] mmc0: req done (CMD0): 0: 00000000 00000000 00000000 00000000
[   69.460455] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   69.461629] mmc0: starting CMD8 arg 000001aa flags 000002f5
[   69.461831] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   69.461911] mmc0: req done (CMD8): -110: 00000000 00000000 00000000 
00000000
[   69.465687] mmc0: starting CMD5 arg 00000000 flags 000002e1
[   69.465925] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   69.467198] mmc0: req failed (CMD5): -110, retrying...
[   69.467416] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   69.467534] mmc0: req failed (CMD5): -110, retrying...
[   69.467726] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   69.473396] mmc0: req failed (CMD5): -110, retrying...
[   69.473603] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   69.473673] mmc0: req done (CMD5): -110: 00000000 00000000 00000000 
00000000
[   69.473751] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   69.473946] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   69.474022] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   69.474091] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   69.474281] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   69.474354] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   69.474411] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   69.474601] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   69.474661] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   69.474739] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   69.474929] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   69.475005] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   69.475088] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   69.475211] mmc0: starting CMD1 arg 00000000 flags 000000e1
[   69.475387] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   69.475437] mmc0: req done (CMD1): 0: 40ff8080 00000000 00000000 00000000
[   69.475529] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   69.475652] mmc0: clock 400000Hz busmode 1 powermode 2 cs 1 Vdd 21 
width 0 timing 0
[   69.476790] mmc0: starting CMD0 arg 00000000 flags 000000c0
[   69.476915] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   69.476974] mmc0: req done (CMD0): 0: 00000000 00000000 00000000 00000000
[   69.479301] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   69.480472] mmc0: starting CMD1 arg 40200000 flags 000000e1
[   69.480660] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   69.480725] mmc0: req done (CMD1): 0: 40ff8080 00000000 00000000 00000000
[   69.492780] mmc0: starting CMD1 arg 40200000 flags 000000e1
[   69.492997] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   69.493047] mmc0: req done (CMD1): 0: c0ff8080 00000000 00000000 00000000
[   69.493110] mmc0: starting CMD2 arg 00000000 flags 00000067
[   69.493366] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   69.493413] mmc0: req done (CMD2): 0: 15010034 46454143 420039f5 b6653100
[   69.493480] mmc0: starting CMD3 arg 00010000 flags 00000015
[   69.493653] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   69.493695] mmc0: req done (CMD3): 0: 00000500 00000000 00000000 00000000
[   69.493755] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   69.493881] mmc0: starting CMD9 arg 00010000 flags 00000007
[   69.494140] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   69.494187] mmc0: req done (CMD9): 0: d0270132 0f5903ff f6dbffef 8e404000
[   69.494251] mmc0: starting CMD7 arg 00010000 flags 00000015
[   69.494422] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   69.494475] mmc0: req done (CMD7): 0: 00000700 00000000 00000000 00000000
[   69.494527] mmc0: starting CMD8 arg 00000000 flags 000000b5
[   69.494550] mmc0:     blksz 512 blocks 1 flags 00000200 tsac 150 ms 
nsac 1000
[   69.494778] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   69.498909] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000020
[   69.498930] sdhci [sdhci_read_block_pio()]: PIO reading
[   69.498974] sdhci [sdhci_transfer_pio()]: PIO transfer complete.
[   69.498998] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000002
[   69.499045] mmc0: req done (CMD8): 0: 00000900 00000000 00000000 00000000
[   69.499063] mmc0:     512 bytes transferred: 0
[   69.499126] mmc0: MAN_BKOPS_EN bit is not set
[   69.499160] mmc0: starting CMD6 arg 03220101 flags 0000049d
[   69.499346] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000003
[   69.499390] mmc0: req done (CMD6): 0: 00000900 00000000 00000000 00000000
[   69.499439] mmc0: starting CMD13 arg 00010000 flags 00000195
[   69.499624] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   69.499668] mmc0: req done (CMD13): 0: 00000900 00000000 00000000 
00000000
[   69.499732] mmc0: clock 25000000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   69.499854] mmc0: starting CMD6 arg 03a10101 flags 0000049d
[   69.499924] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   69.499982] mmc0: req failed (CMD6): -110, retrying...
[   69.500041] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   69.500091] mmc0: req failed (CMD6): -110, retrying...
[   69.500146] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   69.500188] mmc0: req failed (CMD6): -110, retrying...
[   69.500251] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   69.500300] mmc0: req done (CMD6): -110: 00000000 00000000 00000000 
00000000
[   69.500345] mmc0: error -110 whilst initialising MMC card
[   69.500371] mmc0: clock 0Hz busmode 2 powermode 0 cs 0 Vdd 0 width 0 
timing 0
[   70.492773] mmc0: mmc_rescan_try_freq: trying to init card at 400000 Hz
[   70.492820] mmc0: clock 0Hz busmode 2 powermode 1 cs 0 Vdd 21 width 0 
timing 0
[   70.498493] sdhci-bcm2835 20300000.sdhci: Initial signal voltage of 3.3v
[   70.512773] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   70.532756] mmc0: starting CMD52 arg 00000c00 flags 00000195
[   70.532957] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   70.533031] mmc0: req done (CMD52): -110: 00000000 00000000 00000000 
00000000
[   70.533083] mmc0: starting CMD52 arg 80000c08 flags 00000195
[   70.533278] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   70.533333] mmc0: req done (CMD52): -110: 00000000 00000000 00000000 
00000000
[   70.533394] mmc0: clock 400000Hz busmode 2 powermode 2 cs 1 Vdd 21 
width 0 timing 0
[   70.534539] mmc0: starting CMD0 arg 00000000 flags 000000c0
[   70.534654] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   70.534698] mmc0: req done (CMD0): 0: 00000000 00000000 00000000 00000000
[   70.535793] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   70.536934] mmc0: starting CMD8 arg 000001aa flags 000002f5
[   70.537122] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   70.537178] mmc0: req done (CMD8): -110: 00000000 00000000 00000000 
00000000
[   70.537234] mmc0: starting CMD5 arg 00000000 flags 000002e1
[   70.537414] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   70.537490] mmc0: req failed (CMD5): -110, retrying...
[   70.537662] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   70.537735] mmc0: req failed (CMD5): -110, retrying...
[   70.537907] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   70.537982] mmc0: req failed (CMD5): -110, retrying...
[   70.538154] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   70.538208] mmc0: req done (CMD5): -110: 00000000 00000000 00000000 
00000000
[   70.538270] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   70.538450] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   70.538515] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   70.538561] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   70.538754] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   70.538816] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   70.538872] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   70.539051] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   70.539117] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   70.539162] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   70.539347] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   70.539411] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   70.539462] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   70.539579] mmc0: starting CMD1 arg 00000000 flags 000000e1
[   70.539757] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   70.539802] mmc0: req done (CMD1): 0: 40ff8080 00000000 00000000 00000000
[   70.539864] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   70.539983] mmc0: clock 400000Hz busmode 1 powermode 2 cs 1 Vdd 21 
width 0 timing 0
[   70.541119] mmc0: starting CMD0 arg 00000000 flags 000000c0
[   70.541241] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   70.541282] mmc0: req done (CMD0): 0: 00000000 00000000 00000000 00000000
[   70.542371] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   70.543583] mmc0: starting CMD1 arg 40200000 flags 000000e1
[   70.543766] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   70.543809] mmc0: req done (CMD1): 0: 40ff8080 00000000 00000000 00000000
[   70.562765] mmc0: starting CMD1 arg 40200000 flags 000000e1
[   70.562954] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   70.563012] mmc0: req done (CMD1): 0: c0ff8080 00000000 00000000 00000000
[   70.563061] mmc0: starting CMD2 arg 00000000 flags 00000067
[   70.563333] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   70.563390] mmc0: req done (CMD2): 0: 15010034 46454143 420039f5 b6653100
[   70.563445] mmc0: starting CMD3 arg 00010000 flags 00000015
[   70.563629] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   70.563672] mmc0: req done (CMD3): 0: 00000500 00000000 00000000 00000000
[   70.563721] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   70.563854] mmc0: starting CMD9 arg 00010000 flags 00000007
[   70.564113] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   70.564159] mmc0: req done (CMD9): 0: d0270132 0f5903ff f6dbffef 8e404000
[   70.564212] mmc0: starting CMD7 arg 00010000 flags 00000015
[   70.564398] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   70.564442] mmc0: req done (CMD7): 0: 00000700 00000000 00000000 00000000
[   70.564505] mmc0: starting CMD8 arg 00000000 flags 000000b5
[   70.564529] mmc0:     blksz 512 blocks 1 flags 00000200 tsac 150 ms 
nsac 1000
[   70.564744] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   70.568875] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000020
[   70.568895] sdhci [sdhci_read_block_pio()]: PIO reading
[   70.568938] sdhci [sdhci_transfer_pio()]: PIO transfer complete.
[   70.568960] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000002
[   70.569015] mmc0: req done (CMD8): 0: 00000900 00000000 00000000 00000000
[   70.569032] mmc0:     512 bytes transferred: 0
[   70.569082] mmc0: MAN_BKOPS_EN bit is not set
[   70.569130] mmc0: starting CMD6 arg 03220101 flags 0000049d
[   70.569309] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   70.569338] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000002
[   70.569384] mmc0: req done (CMD6): 0: 00000900 00000000 00000000 00000000
[   70.569433] mmc0: starting CMD13 arg 00010000 flags 00000195
[   70.569606] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   70.569660] mmc0: req done (CMD13): 0: 00000900 00000000 00000000 
00000000
[   70.569713] mmc0: clock 25000000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   70.569835] mmc0: starting CMD6 arg 03a10101 flags 0000049d
[   70.569915] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   70.569963] mmc0: req failed (CMD6): -110, retrying...
[   70.570034] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   70.570078] mmc0: req failed (CMD6): -110, retrying...
[   70.570143] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   70.570187] mmc0: req failed (CMD6): -110, retrying...
[   70.570249] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   70.570298] mmc0: req done (CMD6): -110: 00000000 00000000 00000000 
00000000
[   70.570331] mmc0: error -110 whilst initialising MMC card
[   70.570368] mmc0: clock 0Hz busmode 2 powermode 0 cs 0 Vdd 0 width 0 
timing 0
[   71.562799] mmc0: mmc_rescan_try_freq: trying to init card at 400000 Hz
[   71.562846] mmc0: clock 0Hz busmode 2 powermode 1 cs 0 Vdd 21 width 0 
timing 0
[   71.568518] sdhci-bcm2835 20300000.sdhci: Initial signal voltage of 3.3v
[   71.582768] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   71.602774] mmc0: starting CMD52 arg 00000c00 flags 00000195
[   71.602975] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   71.603048] mmc0: req done (CMD52): -110: 00000000 00000000 00000000 
00000000
[   71.603095] mmc0: starting CMD52 arg 80000c08 flags 00000195
[   71.603291] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   71.603346] mmc0: req done (CMD52): -110: 00000000 00000000 00000000 
00000000
[   71.603413] mmc0: clock 400000Hz busmode 2 powermode 2 cs 1 Vdd 21 
width 0 timing 0
[   71.604558] mmc0: starting CMD0 arg 00000000 flags 000000c0
[   71.604670] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   71.604714] mmc0: req done (CMD0): 0: 00000000 00000000 00000000 00000000
[   71.605806] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   71.606969] mmc0: starting CMD8 arg 000001aa flags 000002f5
[   71.607218] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   71.607287] mmc0: req done (CMD8): -110: 00000000 00000000 00000000 
00000000
[   71.607336] mmc0: starting CMD5 arg 00000000 flags 000002e1
[   71.607531] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   71.607598] mmc0: req failed (CMD5): -110, retrying...
[   71.607786] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   71.607850] mmc0: req failed (CMD5): -110, retrying...
[   71.608035] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   71.608098] mmc0: req failed (CMD5): -110, retrying...
[   71.608281] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   71.608337] mmc0: req done (CMD5): -110: 00000000 00000000 00000000 
00000000
[   71.608398] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   71.608592] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   71.608691] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   71.608768] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   71.608953] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   71.609020] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   71.609070] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   71.609257] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   71.609312] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   71.609357] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   71.609551] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   71.609606] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   71.609667] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   71.609788] mmc0: starting CMD1 arg 00000000 flags 000000e1
[   71.609958] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   71.610021] mmc0: req done (CMD1): 0: 40ff8080 00000000 00000000 00000000
[   71.610073] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   71.610193] mmc0: clock 400000Hz busmode 1 powermode 2 cs 1 Vdd 21 
width 0 timing 0
[   71.611328] mmc0: starting CMD0 arg 00000000 flags 000000c0
[   71.611451] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   71.611505] mmc0: req done (CMD0): 0: 00000000 00000000 00000000 00000000
[   71.612590] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   71.615639] mmc0: starting CMD1 arg 40200000 flags 000000e1
[   71.615823] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   71.615881] mmc0: req done (CMD1): 0: 40ff8080 00000000 00000000 00000000
[   71.632770] mmc0: starting CMD1 arg 40200000 flags 000000e1
[   71.632952] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   71.633009] mmc0: req done (CMD1): 0: c0ff8080 00000000 00000000 00000000
[   71.633059] mmc0: starting CMD2 arg 00000000 flags 00000067
[   71.633315] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   71.633362] mmc0: req done (CMD2): 0: 15010034 46454143 420039f5 b6653100
[   71.633426] mmc0: starting CMD3 arg 00010000 flags 00000015
[   71.633599] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   71.633652] mmc0: req done (CMD3): 0: 00000500 00000000 00000000 00000000
[   71.633702] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   71.633823] mmc0: starting CMD9 arg 00010000 flags 00000007
[   71.634082] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   71.634138] mmc0: req done (CMD9): 0: d0270132 0f5903ff f6dbffef 8e404000
[   71.634191] mmc0: starting CMD7 arg 00010000 flags 00000015
[   71.634365] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   71.634418] mmc0: req done (CMD7): 0: 00000700 00000000 00000000 00000000
[   71.634471] mmc0: starting CMD8 arg 00000000 flags 000000b5
[   71.634509] mmc0:     blksz 512 blocks 1 flags 00000200 tsac 150 ms 
nsac 1000
[   71.634727] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   71.638857] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000020
[   71.638877] sdhci [sdhci_read_block_pio()]: PIO reading
[   71.638920] sdhci [sdhci_transfer_pio()]: PIO transfer complete.
[   71.638942] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000002
[   71.638997] mmc0: req done (CMD8): 0: 00000900 00000000 00000000 00000000
[   71.639015] mmc0:     512 bytes transferred: 0
[   71.639065] mmc0: MAN_BKOPS_EN bit is not set
[   71.639097] mmc0: starting CMD6 arg 03220101 flags 0000049d
[   71.639290] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   71.639320] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000002
[   71.639358] mmc0: req done (CMD6): 0: 00000900 00000000 00000000 00000000
[   71.639417] mmc0: starting CMD13 arg 00010000 flags 00000195
[   71.639588] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   71.639640] mmc0: req done (CMD13): 0: 00000900 00000000 00000000 
00000000
[   71.639692] mmc0: clock 25000000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   71.639813] mmc0: starting CMD6 arg 03a10101 flags 0000049d
[   71.639891] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   71.639938] mmc0: req failed (CMD6): -110, retrying...
[   71.639994] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   71.640054] mmc0: req failed (CMD6): -110, retrying...
[   71.640110] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   71.640161] mmc0: req failed (CMD6): -110, retrying...
[   71.640216] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   71.640276] mmc0: req done (CMD6): -110: 00000000 00000000 00000000 
00000000
[   71.640311] mmc0: error -110 whilst initialising MMC card
[   71.640335] mmc0: clock 0Hz busmode 2 powermode 0 cs 0 Vdd 0 width 0 
timing 0
[   72.632812] mmc0: mmc_rescan_try_freq: trying to init card at 400000 Hz
[   72.632861] mmc0: clock 0Hz busmode 2 powermode 1 cs 0 Vdd 21 width 0 
timing 0
[   72.638527] sdhci-bcm2835 20300000.sdhci: Initial signal voltage of 3.3v
[   72.652787] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   72.672849] mmc0: starting CMD52 arg 00000c00 flags 00000195
[   72.673063] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   72.673127] mmc0: req done (CMD52): -110: 00000000 00000000 00000000 
00000000
[   72.673190] mmc0: starting CMD52 arg 80000c08 flags 00000195
[   72.673370] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   72.673436] mmc0: req done (CMD52): -110: 00000000 00000000 00000000 
00000000
[   72.673486] mmc0: clock 400000Hz busmode 2 powermode 2 cs 1 Vdd 21 
width 0 timing 0
[   72.674645] mmc0: starting CMD0 arg 00000000 flags 000000c0
[   72.674761] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   72.674814] mmc0: req done (CMD0): 0: 00000000 00000000 00000000 00000000
[   72.675903] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   72.677046] mmc0: starting CMD8 arg 000001aa flags 000002f5
[   72.677225] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   72.677291] mmc0: req done (CMD8): -110: 00000000 00000000 00000000 
00000000
[   72.677337] mmc0: starting CMD5 arg 00000000 flags 000002e1
[   72.677529] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   72.677596] mmc0: req failed (CMD5): -110, retrying...
[   72.677777] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   72.677842] mmc0: req failed (CMD5): -110, retrying...
[   72.678015] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   72.678090] mmc0: req failed (CMD5): -110, retrying...
[   72.678263] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   72.678330] mmc0: req done (CMD5): -110: 00000000 00000000 00000000 
00000000
[   72.678392] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   72.678573] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   72.678628] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   72.678685] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   72.678866] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   72.678930] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   72.678974] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   72.679166] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   72.679221] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   72.679278] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   72.679459] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   72.679524] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   72.679574] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   72.679691] mmc0: starting CMD1 arg 00000000 flags 000000e1
[   72.679859] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   72.679912] mmc0: req done (CMD1): 0: 40ff8080 00000000 00000000 00000000
[   72.679963] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   72.680083] mmc0: clock 400000Hz busmode 1 powermode 2 cs 1 Vdd 21 
width 0 timing 0
[   72.681220] mmc0: starting CMD0 arg 00000000 flags 000000c0
[   72.681335] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   72.681393] mmc0: req done (CMD0): 0: 00000000 00000000 00000000 00000000
[   72.682545] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   72.683771] mmc0: starting CMD1 arg 40200000 flags 000000e1
[   72.683947] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   72.683992] mmc0: req done (CMD1): 0: 40ff8080 00000000 00000000 00000000
[   72.702800] mmc0: starting CMD1 arg 40200000 flags 000000e1
[   72.702992] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   72.703052] mmc0: req done (CMD1): 0: c0ff8080 00000000 00000000 00000000
[   72.703100] mmc0: starting CMD2 arg 00000000 flags 00000067
[   72.703370] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   72.703425] mmc0: req done (CMD2): 0: 15010034 46454143 420039f5 b6653100
[   72.703479] mmc0: starting CMD3 arg 00010000 flags 00000015
[   72.703663] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   72.703706] mmc0: req done (CMD3): 0: 00000500 00000000 00000000 00000000
[   72.703754] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   72.703876] mmc0: starting CMD9 arg 00010000 flags 00000007
[   72.704151] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   72.704197] mmc0: req done (CMD9): 0: d0270132 0f5903ff f6dbffef 8e404000
[   72.704250] mmc0: starting CMD7 arg 00010000 flags 00000015
[   72.704443] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   72.704487] mmc0: req done (CMD7): 0: 00000700 00000000 00000000 00000000
[   72.704551] mmc0: starting CMD8 arg 00000000 flags 000000b5
[   72.704575] mmc0:     blksz 512 blocks 1 flags 00000200 tsac 150 ms 
nsac 1000
[   72.704790] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   72.708919] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000020
[   72.708938] sdhci [sdhci_read_block_pio()]: PIO reading
[   72.708982] sdhci [sdhci_transfer_pio()]: PIO transfer complete.
[   72.709003] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000002
[   72.709051] mmc0: req done (CMD8): 0: 00000900 00000000 00000000 00000000
[   72.709068] mmc0:     512 bytes transferred: 0
[   72.709119] mmc0: MAN_BKOPS_EN bit is not set
[   72.709168] mmc0: starting CMD6 arg 03220101 flags 0000049d
[   72.709347] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   72.709377] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000002
[   72.709424] mmc0: req done (CMD6): 0: 00000900 00000000 00000000 00000000
[   72.709472] mmc0: starting CMD13 arg 00010000 flags 00000195
[   72.709645] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   72.709700] mmc0: req done (CMD13): 0: 00000900 00000000 00000000 
00000000
[   72.709752] mmc0: clock 25000000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   72.709872] mmc0: starting CMD6 arg 03a10101 flags 0000049d
[   72.709953] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   72.710001] mmc0: req failed (CMD6): -110, retrying...
[   72.710067] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   72.710110] mmc0: req failed (CMD6): -110, retrying...
[   72.710171] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   72.710214] mmc0: req failed (CMD6): -110, retrying...
[   72.710277] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   72.710328] mmc0: req done (CMD6): -110: 00000000 00000000 00000000 
00000000
[   72.710361] mmc0: error -110 whilst initialising MMC card
[   72.710397] mmc0: clock 0Hz busmode 2 powermode 0 cs 0 Vdd 0 width 0 
timing 0
[   73.702816] mmc0: mmc_rescan_try_freq: trying to init card at 400000 Hz
[   73.702863] mmc0: clock 0Hz busmode 2 powermode 1 cs 0 Vdd 21 width 0 
timing 0
[   73.708534] sdhci-bcm2835 20300000.sdhci: Initial signal voltage of 3.3v
[   73.722825] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   73.742828] mmc0: starting CMD52 arg 00000c00 flags 00000195
[   73.743041] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   73.743116] mmc0: req done (CMD52): -110: 00000000 00000000 00000000 
00000000
[   73.743178] mmc0: starting CMD52 arg 80000c08 flags 00000195
[   73.743361] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   73.743428] mmc0: req done (CMD52): -110: 00000000 00000000 00000000 
00000000
[   73.743478] mmc0: clock 400000Hz busmode 2 powermode 2 cs 1 Vdd 21 
width 0 timing 0
[   73.744629] mmc0: starting CMD0 arg 00000000 flags 000000c0
[   73.744752] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   73.744806] mmc0: req done (CMD0): 0: 00000000 00000000 00000000 00000000
[   73.745899] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   73.747047] mmc0: starting CMD8 arg 000001aa flags 000002f5
[   73.747228] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   73.747294] mmc0: req done (CMD8): -110: 00000000 00000000 00000000 
00000000
[   73.747342] mmc0: starting CMD5 arg 00000000 flags 000002e1
[   73.747523] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   73.747599] mmc0: req failed (CMD5): -110, retrying...
[   73.747772] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   73.747846] mmc0: req failed (CMD5): -110, retrying...
[   73.748018] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   73.748091] mmc0: req failed (CMD5): -110, retrying...
[   73.748262] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   73.748327] mmc0: req done (CMD5): -110: 00000000 00000000 00000000 
00000000
[   73.748375] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   73.748569] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   73.748625] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   73.748681] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   73.748862] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   73.748926] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   73.748971] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   73.749153] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   73.749219] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   73.749264] mmc0: starting CMD55 arg 00000000 flags 000000f5
[   73.749458] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00018000
[   73.749513] mmc0: req done (CMD55): -110: 00000000 00000000 00000000 
00000000
[   73.749573] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   73.749692] mmc0: starting CMD1 arg 00000000 flags 000000e1
[   73.749860] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   73.749914] mmc0: req done (CMD1): 0: 40ff8080 00000000 00000000 00000000
[   73.749966] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   73.750086] mmc0: clock 400000Hz busmode 1 powermode 2 cs 1 Vdd 21 
width 0 timing 0
[   73.751220] mmc0: starting CMD0 arg 00000000 flags 000000c0
[   73.751332] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   73.751374] mmc0: req done (CMD0): 0: 00000000 00000000 00000000 00000000
[   73.752470] mmc0: clock 400000Hz busmode 1 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   73.753690] mmc0: starting CMD1 arg 40200000 flags 000000e1
[   73.753867] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   73.753921] mmc0: req done (CMD1): 0: 40ff8080 00000000 00000000 00000000
[   73.772825] mmc0: starting CMD1 arg 40200000 flags 000000e1
[   73.773025] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   73.773087] mmc0: req done (CMD1): 0: c0ff8080 00000000 00000000 00000000
[   73.773137] mmc0: starting CMD2 arg 00000000 flags 00000067
[   73.773407] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   73.773464] mmc0: req done (CMD2): 0: 15010034 46454143 420039f5 b6653100
[   73.773518] mmc0: starting CMD3 arg 00010000 flags 00000015
[   73.773703] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   73.773746] mmc0: req done (CMD3): 0: 00000500 00000000 00000000 00000000
[   73.773805] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   73.773927] mmc0: starting CMD9 arg 00010000 flags 00000007
[   73.774185] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   73.774230] mmc0: req done (CMD9): 0: d0270132 0f5903ff f6dbffef 8e404000
[   73.774296] mmc0: starting CMD7 arg 00010000 flags 00000015
[   73.774468] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   73.774511] mmc0: req done (CMD7): 0: 00000700 00000000 00000000 00000000
[   73.774574] mmc0: starting CMD8 arg 00000000 flags 000000b5
[   73.774598] mmc0:     blksz 512 blocks 1 flags 00000200 tsac 150 ms 
nsac 1000
[   73.774813] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   73.778944] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000020
[   73.778965] sdhci [sdhci_read_block_pio()]: PIO reading
[   73.779009] sdhci [sdhci_transfer_pio()]: PIO transfer complete.
[   73.779032] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000002
[   73.779080] mmc0: req done (CMD8): 0: 00000900 00000000 00000000 00000000
[   73.779097] mmc0:     512 bytes transferred: 0
[   73.779161] mmc0: MAN_BKOPS_EN bit is not set
[   73.779195] mmc0: starting CMD6 arg 03220101 flags 0000049d
[   73.779373] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   73.779402] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000002
[   73.779449] mmc0: req done (CMD6): 0: 00000900 00000000 00000000 00000000
[   73.779497] mmc0: starting CMD13 arg 00010000 flags 00000195
[   73.779680] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00000001
[   73.779723] mmc0: req done (CMD13): 0: 00000900 00000000 00000000 
00000000
[   73.779774] mmc0: clock 25000000Hz busmode 2 powermode 2 cs 0 Vdd 21 
width 0 timing 0
[   73.779896] mmc0: starting CMD6 arg 03a10101 flags 0000049d
[   73.779976] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   73.780025] mmc0: req failed (CMD6): -110, retrying...
[   73.780093] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   73.780135] mmc0: req failed (CMD6): -110, retrying...
[   73.780200] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   73.780243] mmc0: req failed (CMD6): -110, retrying...
[   73.780306] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
[   73.780356] mmc0: req done (CMD6): -110: 00000000 00000000 00000000 
00000000
[   73.780390] mmc0: error -110 whilst initialising MMC card
[   73.780429] mmc0: clock 0Hz busmode 2 powermode 0 cs 0 Vdd 0 width 0 
timing 0

Seems to be related to some SdCMD6 timing out...

Maybe this helps identify the issue...

Martin




From swarren at wwwdotorg.org  Thu Aug 27 08:13:57 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Thu, 27 Aug 2015 09:13:57 -0600
Subject: Raspberry Pi Compute Module
In-Reply-To: <C99F5FC0-D9B6-4DF1-AD2D-F706F55A5AF8@sperl.org>
References: <55A93694.9060004@lategoodbye.de> <55AF10E5.304@wwwdotorg.org>
 <55AFCBC2.5030808@lategoodbye.de> <55B1B7D1.2030608@wwwdotorg.org>
 <C99F5FC0-D9B6-4DF1-AD2D-F706F55A5AF8@sperl.org>
Message-ID: <55DF2935.2030903@wwwdotorg.org>

On 08/26/2015 07:57 AM, Martin Sperl wrote:
>
>> On 24.07.2015, at 05:58, Stephen Warren <swarren at wwwdotorg.org> wrote:
>>
>> On 07/22/2015 10:58 AM, Stefan Wahren wrote:
>>> Hi Stephen,
>>>
>>> Am 22.07.2015 um 05:41 schrieb Stephen Warren:
>>>> On 07/17/2015 11:08 AM, Stefan Wahren wrote:
>>>>> Hi all,
>>>>>
>>>>> is there anyone working on Mainline support for the Compute Module?
>>>>>
>>>>> I noticed the downstream kernel already has a bcm2708-rpi-cm.dtsi
>>>>
>>>> It is probably close enough that you can simply use bcm2835-rpi-b.dtb
>>>> for the CM too, for testing at least.
>>>>
>>>> I just tried this with latest U-Boot and a 4.2.0-rc3 kernel. The eMMC
>>>> works fine in U-Boot, but the kernel can't access it for some reason,
>>>> failing with e.g.:
>>>>
>>>> [    3.110503] Waiting for root device /dev/mmcblk0p2...
>>>> [    3.123895] mmc0: MAN_BKOPS_EN bit is not set
>>>> [    3.132264] mmc0: error -110 whilst initialising MMC card
>>>> [    3.788447] cfg80211: Calling CRDA to update world regulatory domain
>>>> [    4.204078] mmc0: MAN_BKOPS_EN bit is not set
>>>> [    4.212748] mmc0: error -110 whilst initialising MMC card
>>>> [    5.283981] mmc0: MAN_BKOPS_EN bit is not set
>>>> [    5.292657] mmc0: error -110 whilst initialising MMC card
>>>>
>>>> I had hoped that the kernel fixes for the VC MMU high address bits (via
>>>> the dma-ranges property in DT) would fix this, but I guess not since it
>>>> still doesn't work.
>>>>
>>>
>>> thank your for testing.
>>>
>>> Does defining the bus width as in bcm2708-rpi-cm.dtsi change anything?
>>>
>>> &mmc {
>>>     status = "okay";
>>>     bus-width = <4>;
>>> };
>>
>> Unfortunately not.
>>
>
> I am seeing the same thing - maybe the GPIO47 needs to get toggled at some point to output?
>
> See the comment in the CMIO PDF:
>
> BCM2835 BootROM boot from USB:
> - J4 set to enable USB boot
> - Plug host into micro USB socket J15 (VBUSB=5V)
> - GPIO47_1V8 high at boot (input with 50k pullup)
> - EMMC_DISABLE_N therefore LOW
> - On power up BCM2835 can't access eMMC so boots from USB
> - Once booted, 2835 USB boot SW forces GPIO47_1V8 LOW to enable access to eMMC C
>
> BCM2835 BootROM boot from eMMC:
> - Nothing plugged into micro USB socket J15 (VBUSB=0V) OR J4 set to disable USB Boot
> - EMMC_DISABLE_N therefore HIGH

This is the case I'm in; I have nothing plugged into the micro USB 
socket so the value of GPIO47 should be irrelevant. Besides, the eMMC 
works fine in U-Boot just not in the upstream kernel. Most likely is 
something to do with low-speed MMC signalling modes working fine (which 
no doubt U-Boot uses) but the kernel probably tries to switch to some 
more advanced mode which doesn't work for some reason.


From martin at sperl.org  Thu Aug 27 08:36:12 2015
From: martin at sperl.org (Martin Sperl)
Date: Thu, 27 Aug 2015 17:36:12 +0200
Subject: Raspberry Pi Compute Module
In-Reply-To: <55DF2935.2030903@wwwdotorg.org>
References: <55A93694.9060004@lategoodbye.de> <55AF10E5.304@wwwdotorg.org>
 <55AFCBC2.5030808@lategoodbye.de> <55B1B7D1.2030608@wwwdotorg.org>
 <C99F5FC0-D9B6-4DF1-AD2D-F706F55A5AF8@sperl.org>
 <55DF2935.2030903@wwwdotorg.org>
Message-ID: <C691EB46-B16D-436D-BE1B-026DFD33CF90@sperl.org>



> On 27.08.2015, at 17:13, Stephen Warren <swarren at wwwdotorg.org> wrote:
> 
>> On 08/26/2015 07:57 AM, Martin Sperl wrote:
>> 
>>> On 24.07.2015, at 05:58, Stephen Warren <swarren at wwwdotorg.org> wrote:
>>> 
>>> On 07/22/2015 10:58 AM, Stefan Wahren wrote:
>>>> Hi Stephen,
>>>> 
>>>>> Am 22.07.2015 um 05:41 schrieb Stephen Warren:
>>>>>> On 07/17/2015 11:08 AM, Stefan Wahren wrote:
>>>>>> Hi all,
>>>>>> 
>>>>>> is there anyone working on Mainline support for the Compute Module?
>>>>>> 
>>>>>> I noticed the downstream kernel already has a bcm2708-rpi-cm.dtsi
>>>>> 
>>>>> It is probably close enough that you can simply use bcm2835-rpi-b.dtb
>>>>> for the CM too, for testing at least.
>>>>> 
>>>>> I just tried this with latest U-Boot and a 4.2.0-rc3 kernel. The eMMC
>>>>> works fine in U-Boot, but the kernel can't access it for some reason,
>>>>> failing with e.g.:
>>>>> 
>>>>> [    3.110503] Waiting for root device /dev/mmcblk0p2...
>>>>> [    3.123895] mmc0: MAN_BKOPS_EN bit is not set
>>>>> [    3.132264] mmc0: error -110 whilst initialising MMC card
>>>>> [    3.788447] cfg80211: Calling CRDA to update world regulatory domain
>>>>> [    4.204078] mmc0: MAN_BKOPS_EN bit is not set
>>>>> [    4.212748] mmc0: error -110 whilst initialising MMC card
>>>>> [    5.283981] mmc0: MAN_BKOPS_EN bit is not set
>>>>> [    5.292657] mmc0: error -110 whilst initialising MMC card
>>>>> 
> 
> This is the case I'm in; I have nothing plugged into the micro USB socket so the value of GPIO47 should be irrelevant. Besides, the eMMC works fine in U-Boot just not in the upstream kernel. Most likely is something to do with low-speed MMC signalling modes working fine (which no doubt U-Boot uses) but the kernel probably tries to switch to some more advanced mode which doesn't work for some reason.
> 
See my second email with the details of mmc debug enabled and loaded via a module -  seems as if sdCMD6 does not work as expected at 400khz...

Unfortunately it is hard to attach a logic analyzer to the eMMC on the back side of the compute module - I guess it would help...

From swarren at wwwdotorg.org  Thu Aug 27 09:11:01 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Thu, 27 Aug 2015 10:11:01 -0600
Subject: Raspberry Pi Compute Module
In-Reply-To: <55DF0620.2070801@sperl.org>
References: <55A93694.9060004@lategoodbye.de> <55AF10E5.304@wwwdotorg.org>
 <55AFCBC2.5030808@lategoodbye.de> <55B1B7D1.2030608@wwwdotorg.org>
 <C99F5FC0-D9B6-4DF1-AD2D-F706F55A5AF8@sperl.org> <55DF0620.2070801@sperl.org>
Message-ID: <55DF3695.6080302@wwwdotorg.org>

On 08/27/2015 06:44 AM, Martin Sperl wrote:
> On 26.08.2015 15:57, Martin Sperl wrote:
>>> On 24.07.2015, at 05:58, Stephen Warren <swarren at wwwdotorg.org> wrote:
>>>
>>> On 07/22/2015 10:58 AM, Stefan Wahren wrote:
>>>> Hi Stephen,
>>>>
>>>> Am 22.07.2015 um 05:41 schrieb Stephen Warren:
>>>>> On 07/17/2015 11:08 AM, Stefan Wahren wrote:
>>>>>> Hi all,
>>>>>>
>>>>>> is there anyone working on Mainline support for the Compute Module?
>>>>>>
>>>>>> I noticed the downstream kernel already has a bcm2708-rpi-cm.dtsi
>>>>> It is probably close enough that you can simply use bcm2835-rpi-b.dtb
>>>>> for the CM too, for testing at least.
>>>>>
>>>>> I just tried this with latest U-Boot and a 4.2.0-rc3 kernel. The eMMC
>>>>> works fine in U-Boot, but the kernel can't access it for some reason,
>>>>> failing with e.g.:
...
> Compiling the kernel with mmc as module plus mmc-debug enabled gives the
> below:
> after loading the driver:
...
> [   69.432782] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21
> width 0 timing 0

... lot of working commands ...

> [   69.499732] mmc0: clock 25000000Hz busmode 2 powermode 2 cs 0 Vdd 21
> width 0 timing 0

(up until now, the clock has always be 400KHz, not it's 25MHz)

> [   69.499854] mmc0: starting CMD6 arg 03a10101 flags 0000049d
> [   69.499924] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
> [   69.499982] mmc0: req failed (CMD6): -110, retrying...
> [   69.500041] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
> [   69.500091] mmc0: req failed (CMD6): -110, retrying...
> [   69.500146] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
> [   69.500188] mmc0: req failed (CMD6): -110, retrying...
> [   69.500251] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
> [   69.500300] mmc0: req done (CMD6): -110: 00000000 00000000 00000000
> 00000000
> [   69.500345] mmc0: error -110 whilst initialising MMC card

My guess it that the clock rate change caused this, not the CMD6 itself?


From martin at sperl.org  Thu Aug 27 11:04:10 2015
From: martin at sperl.org (Martin Sperl)
Date: Thu, 27 Aug 2015 20:04:10 +0200
Subject: Raspberry Pi Compute Module
In-Reply-To: <55DF3695.6080302@wwwdotorg.org>
References: <55A93694.9060004@lategoodbye.de> <55AF10E5.304@wwwdotorg.org>
 <55AFCBC2.5030808@lategoodbye.de> <55B1B7D1.2030608@wwwdotorg.org>
 <C99F5FC0-D9B6-4DF1-AD2D-F706F55A5AF8@sperl.org> <55DF0620.2070801@sperl.org>
 <55DF3695.6080302@wwwdotorg.org>
Message-ID: <CCBAFE96-14FD-4997-8417-A1CDD58CFD7B@sperl.org>



> On 27.08.2015, at 18:11, Stephen Warren <swarren at wwwdotorg.org> 
>> Compiling the kernel with mmc as module plus mmc-debug enabled gives the
>> below:
>> after loading the driver:
> ...
>> [   69.432782] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21
>> width 0 timing 0
> 
> ... lot of working commands ...
> 
>> [   69.499732] mmc0: clock 25000000Hz busmode 2 powermode 2 cs 0 Vdd 21
>> width 0 timing 0
> 
> (up until now, the clock has always be 400KHz, not it's 25MHz)
> 
>> [   69.499854] mmc0: starting CMD6 arg 03a10101 flags 0000049d
>> [   69.499924] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
>> [   69.499982] mmc0: req failed (CMD6): -110, retrying...
>> [   69.500041] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
>> [   69.500091] mmc0: req failed (CMD6): -110, retrying...
>> [   69.500146] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
>> [   69.500188] mmc0: req failed (CMD6): -110, retrying...
>> [   69.500251] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
>> [   69.500300] mmc0: req done (CMD6): -110: 00000000 00000000 00000000
>> 00000000
>> [   69.500345] mmc0: error -110 whilst initialising MMC card
> 
> My guess it that the clock rate change caused this, not the CMD6 itself?

I saw a patch for one device that required more delay (in the order of 100us) between cmd13 and cmd6 - can't pinpoint it right now...

But then logging would add some delays, but maybe not enough...

Maybe changing clock to 400khz would allow access to the device - even if it is slow...

Can we configure that with the device tree, just to see if it makes a difference?

Martin

From martin at sperl.org  Thu Aug 27 11:09:35 2015
From: martin at sperl.org (Martin Sperl)
Date: Thu, 27 Aug 2015 20:09:35 +0200
Subject: Raspberry Pi Compute Module
In-Reply-To: <CCBAFE96-14FD-4997-8417-A1CDD58CFD7B@sperl.org>
References: <55A93694.9060004@lategoodbye.de> <55AF10E5.304@wwwdotorg.org>
 <55AFCBC2.5030808@lategoodbye.de> <55B1B7D1.2030608@wwwdotorg.org>
 <C99F5FC0-D9B6-4DF1-AD2D-F706F55A5AF8@sperl.org> <55DF0620.2070801@sperl.org>
 <55DF3695.6080302@wwwdotorg.org>
 <CCBAFE96-14FD-4997-8417-A1CDD58CFD7B@sperl.org>
Message-ID: <659525E1-D58B-49A1-BCCB-12488C8CA279@sperl.org>



> On 27.08.2015, at 20:04, Martin Sperl <martin at sperl.org> wrote:
> 
> 
> 
>> On 27.08.2015, at 18:11, Stephen Warren <swarren at wwwdotorg.org> 
>>> Compiling the kernel with mmc as module plus mmc-debug enabled gives the
>>> below:
>>> after loading the driver:
>> ...
>>> [   69.432782] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21
>>> width 0 timing 0
>> 
>> ... lot of working commands ...
>> 
>>> [   69.499732] mmc0: clock 25000000Hz busmode 2 powermode 2 cs 0 Vdd 21
>>> width 0 timing 0
>> 
>> (up until now, the clock has always be 400KHz, not it's 25MHz)
>> 
>>> [   69.499854] mmc0: starting CMD6 arg 03a10101 flags 0000049d
>>> [   69.499924] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
>>> [   69.499982] mmc0: req failed (CMD6): -110, retrying...
>>> [   69.500041] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
>>> [   69.500091] mmc0: req failed (CMD6): -110, retrying...
>>> [   69.500146] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
>>> [   69.500188] mmc0: req failed (CMD6): -110, retrying...
>>> [   69.500251] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
>>> [   69.500300] mmc0: req done (CMD6): -110: 00000000 00000000 00000000
>>> 00000000
>>> [   69.500345] mmc0: error -110 whilst initialising MMC card
>> 
>> My guess it that the clock rate change caused this, not the CMD6 itself?
> 
> I saw a patch for one device that required more delay (in the order of 100us) between cmd13 and cmd6 - can't pinpoint it right now...
> 
> But then logging would add some delays, but maybe not enough...
> 
> Maybe changing clock to 400khz would allow access to the device - even if it is slow...
> 
> Can we configure that with the device tree, just to see if it makes a difference?
> 
See here: https://lkml.org/lkml/2014/9/9/382
for one device that required it...

Martin

From robherring2 at gmail.com  Tue Aug 25 09:22:36 2015
From: robherring2 at gmail.com (Rob Herring)
Date: Tue, 25 Aug 2015 11:22:36 -0500
Subject: [PATCH] arm: rpi: Device tree modifications for U-Boot
In-Reply-To: <CAPnjgZ3ubK_5j4AV4wO6xgoRf8iN+G8YSQm0pSW5fVzwpxYu5Q@mail.gmail.com>
References: <1439303153-12171-1-git-send-email-sjg@chromium.org>
 <1439312701.2451.3.camel@lynxeye.de>
 <CAPnjgZ3ztXnkxZJvitiXiKMzq5+x46hbv0PW4HcF7yGgsXUV+Q@mail.gmail.com>
 <CAPnjgZ0hzyjy=8o8ocaGCoOC7Js4CirhystidbvtabzbhRi4RQ@mail.gmail.com>
 <CAL_Jsq++ihz6svJAKWwT7-=KyJvyWXjdf=tZb4aDGgV_Dc=-Nw@mail.gmail.com>
 <CAPnjgZ3ubK_5j4AV4wO6xgoRf8iN+G8YSQm0pSW5fVzwpxYu5Q@mail.gmail.com>
Message-ID: <CAL_JsqJ-N+nFEMEEmg4O45DVF0g382fUvjGTH=fP_HbgK38aKw@mail.gmail.com>

On Sat, Aug 15, 2015 at 8:46 AM, Simon Glass <sjg at chromium.org> wrote:
> Hi Rob,
>
> On 14 August 2015 at 14:29, Rob Herring <robherring2 at gmail.com> wrote:
>> On Fri, Aug 14, 2015 at 1:34 PM, Simon Glass <sjg at chromium.org> wrote:
>>> -linux-tegra
>>>
>>> Hi,
>>>
>>> On 12 August 2015 at 07:21, Simon Glass <sjg at chromium.org> wrote:
>>>> Hi Lucas,
>>>>
>>>> On 11 August 2015 at 11:05, Lucas Stach <dev at lynxeye.de> wrote:
>>>>> Hi Simon,
>>>>>
>>>>> why did you send this to the Tegra ML?
>>>>>
>>>>> Am Dienstag, den 11.08.2015, 08:25 -0600 schrieb Simon Glass:
>>>>>> This updates the device tree from the kernel version to something suitable
>>>>>> for U-Boot:
>>>>>>
>>>>>> - Add stdout-path alias for console
>>>>>> - Mark the /soc node to be available pre-relocation so that the early
>>>>>> serial console works (we need the 'ranges' property to be available)
>>
>> I find it quite strange that you must explicitly enable the parent
>> node, but not the uart node.
>
> U-Boot tries hard to find and bind the UART using the stdout-path
> link. I would like to add it in the UART node also but we were able to
> work around it so far.
>
>>
>>>>>>
>>>>>> Signed-off-by: Simon Glass <sjg at chromium.org>
>>>>>> ---
>>>>>>
>>>>>>  arch/arm/boot/dts/bcm2835.dtsi | 4 +++-
>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>>>
>>>>>> diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
>>>>>> index 301c73f..bd6bff6 100644
>>>>>> --- a/arch/arm/boot/dts/bcm2835.dtsi
>>>>>> +++ b/arch/arm/boot/dts/bcm2835.dtsi
>>>>>> @@ -8,6 +8,7 @@
>>>>>>
>>>>>>       chosen {
>>>>>>               bootargs = "earlyprintk console=ttyAMA0";
>>>>>> +             stdout-path = &uart;
>>>>>>       };
>>>>>>
>>>>>>       soc {
>>>>>> @@ -16,6 +17,7 @@
>>>>>>               #size-cells = <1>;
>>>>>>               ranges = <0x7e000000 0x20000000 0x02000000>;
>>>>>>               dma-ranges = <0x40000000 0x00000000 0x20000000>;
>>>>>> +             u-boot,dm-pre-reloc;
>>>>>
>>>>> Why do you need this and why should upstream carry your favourite
>>>>> bootloaders configuration? This is in no way hardware description.
>>>>
>>>> I'm not sure how much you know about U-Boot, so let me know if you
>>>> need more info.
>>>>
>>>> U-Boot normally starts up by setting up its serial UART and displaying
>>>> a banner message. At this stage typically only a few devices are
>>>> initialised (e.g. maybe just the UART). It then relocates itself to
>>>> the top of memory and starts up all the devices. It throws away any
>>>> previous devices that it set up before relocation and starts again.
>>>>
>>>> U-Boot uses a thing called driver model (dm) which handles driver
>>>> binding and probing. Driver model has the device tree and would
>>>> normally scan through it and create devices for everything it finds.
>>
>> How do you debug the DM itself? It seems like you still would need
>> something earlier for debug like earlycon in the kernel. u-boot DM is
>> probably simple enough you can get away with using it early, but you
>> often can't as the complexity increases. Ultimately you need something
>> simple that just hits all the registers needed to get characters out.
>> What happens when you add pinmux, clocks, PMIC, power domains, etc. to
>> the DM and they all become dependencies for the UART?
>
> This doesn't seem like a device tree binding question but let me try
> to answer it.
>
> This is a problem - one of the challenges of driver model is that the
> UART gets further away from the reset vector.

This is a common problem for any complex embedded system. Nothing
special about u-boot here. So either we don't need anything because
everyone else has dealt with the problem in some way or we need a
common solution because we all have this problem.

> In U-Boot there is a single debug UART which can be supported by the
> various serial drivers (only one can be selected on each platform).
> There is a special debug_uart_init() function which is supposed to set
> up the UART for that platform. After that, and until driver model UART
> is up, console output can go through the debug UART.

So you don't need this for the common case of an early console, but
only for platforms needing some other platform specific setup?

>>>> Before relocation we don't need every device. Also the CPU is often
>>>> running slowly, perhaps without the cache enabled. SDRAM may not be
>>>> available yet so space is short. We want to avoid starting up things
>>>> that will not be used.
>>>>
>>>> So this property indicates that the device is needed before relocation
>>>> and should be set up by driver model. We need it to avoid a very slow
>>>> and memory-hungry startup.
>>
>> Can't the need for that property change over time? Either as more
>> drivers are converted to DM you need to add this or you add some
>> feature that depends on a driver (e.g. get a board rev or boot mode
>> from GPIO). You would have backwards compatibility issues with this.
>> I'm somewhat less worried about that for u-boot as we should be
>> bundling the dtb and bootloader rather than kernel and dtb. For the
>> UART, you can just get which UART to initialize early from
>> stdout-path. But for other cases, couldn't you just have the platform
>> provide the list of needed drivers. Then when u-boot needs change, you
>> just change u-boot.
>
> Yes U-Boot and its device tree are normally built from the same tree
> at the same time. We don't expect to have to support an older or newer
> device tree with the same U-Boot binary. So I don't see a problem
> here.

My point is that if I had to pick how bootloader+dtb+kernel are
bundled or not, I would rather see the dtb in sync with the bootloader
rather than the kernel. But I can't decide that for everyone and
neither can you. You still have a compatibility problem and that has
to be addressed.

> The UART should have as few dependencies as possible. But if the
> particular platform needs to check a GPIO to find out the UART number
> (i.e. stdout-path cannot be used) then we would need to support that
> case. It hasn't come up yet but it might. I certainly see platforms
> where we need some other nodes before relocation and would like to
> mark them as such. But I hope that for UART we can generally use
> stdout-path.
>
> The list of drivers doesn't help that much. What would I do with such
> a list, and where would I keep such platform-specific information
> other than in the device tree? It seems much simpler to hold the
> information about what the platform needs to get through early boot in
> one place. Then we have just a few lines of generic code to deal with
> it.

If you need certain drivers such as gpio, then you obviously have some
board specific code using those drivers already. If you can tell the
DM which devices to enumerate via DT, you can also tell the DM with C
code.

While doing everything generically without board code is nice, we
should only do that for the common cases and handle the screwy cases
with board code.

Rob


From swarren at wwwdotorg.org  Tue Aug 25 18:44:51 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Tue, 25 Aug 2015 19:44:51 -0600
Subject: [PATCH v4 3/5] dt/bindings: bcm2835: add binding documentation
 for bcm2835-aux
In-Reply-To: <1440405608-3995-4-git-send-email-kernel@martin.sperl.org>
References: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
 <1440405608-3995-4-git-send-email-kernel@martin.sperl.org>
Message-ID: <55DD1A13.7010705@wwwdotorg.org>

On 08/24/2015 02:40 AM, kernel at martin.sperl.org wrote:
> From: Martin Sperl <kernel at martin.sperl.org>

Patch description?

> diff --git a/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt b/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-aux.txt

> +Required properties:
> +- compatible: Should be "brcm,bcm2835-aux".
> +- reg: Should contain register location and length for the
> +       enable register

As I mentioned before, why not all the aux registers (that aren't part
of a sub-device like SPI).

> +Example:
> +
> +aux_enable: aux_enable at 0x7e215004 {
> +	compatible = "bcrm,bcm2835-aux";
> +	reg = <0x7e215004 0x04>;

I'd expect that to be <0x7e215000 0x8>;


From swarren at wwwdotorg.org  Tue Aug 25 18:49:05 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Tue, 25 Aug 2015 19:49:05 -0600
Subject: [PATCH v4 5/5] dt/bindings: bcm2835: Add binding documentation
 for auxiliar spi devices
In-Reply-To: <1440405608-3995-6-git-send-email-kernel@martin.sperl.org>
References: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
 <1440405608-3995-6-git-send-email-kernel@martin.sperl.org>
Message-ID: <55DD1B11.30709@wwwdotorg.org>

On 08/24/2015 02:40 AM, kernel at martin.sperl.org wrote:
> From: Martin Sperl <kernel at martin.sperl.org>

Patch description?

> diff --git a/Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.txt b/Documentation/devicetree/bindings/spi/brcm,bcm2835-aux-spi.txt

> +Required properties:
> +- compatible: Should be "brcm,bcm2835-aux-spi".
> +- reg: Should contain register location and length for the spi block
> +       as well as for the common aux block control

Is that meant to imply that reg should contain a single value that
covers both the common aux registers and the SPI device, or two separate
values, one for the aux common registers and another for the SPI device?
Neither of those options sound correct. I would expect only a single
entry which covered solely the SPI registers. The common aux registers
are owned by the other brcm,bcm2835-aux binding.

> +Example:
> +
> +spi1 at 7e215080 {
> +	compatible = "brcm,bcm2835-aux-spi";
> +	reg = <0x7e215080 0x40>;

That seems to match what I'd expect, but doesn't correspond to the
description above.

> +/* the necessary syscon config referenced above*/
> +aux_enable: aux_enable at 0x7e215004 {

It's not a "syscon"...

> +Note that it also requires the GPIOs to be set up with the
> +correct ALT-functions.
> +
> +For spi1 the following pins need to be set as:
> +* ALT4: 19, 20, 21 (MISO, MOSI, SCK)
> +
> +For spi2 the following pins need to be set as:
> +* ALT4: 40, 41, 42 (MISO, MOSI, SCK)
> +
> +CS-GPIOS need to get set as output - typically:
> +* spi1: 18, 17, 16 (CS0, CS1, CS2)
> +* spi2: 43, 44, 45 (CS0, CS1, CS2)

That's generally true of any HW block, and has nothing to do with the
binding for the device. I would suggest removing that chunk of text.



From swarren at wwwdotorg.org  Tue Aug 25 18:52:15 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Tue, 25 Aug 2015 19:52:15 -0600
Subject: [PATCH v4 1/5] soc: bcm2835: auxiliar devices enable
 infrastructure
In-Reply-To: <1440405608-3995-2-git-send-email-kernel@martin.sperl.org>
References: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
 <1440405608-3995-2-git-send-email-kernel@martin.sperl.org>
Message-ID: <55DD1BCF.7040200@wwwdotorg.org>

On 08/24/2015 02:40 AM, kernel at martin.sperl.org wrote:
> From: Martin Sperl <kernel at martin.sperl.org>
> 
> The bcm2835 SOC contains 3 auxiliar devices (spi1, spi2 and uart1)
> that all are enabled via a shared register.
> 
> To serialize access to this shared register this soc-driver
> is created that implements:
>   bcm2835aux_enable(struct device *dev, const char *property);
>   bcm2835aux_disable(struct device *dev, const char *property);
> 
> Which will read the property from the device tree of the device
> and enable/disable that specific device as per device tree.
> 
> First use of this api will be spi-bcm2835aux.

> diff --git a/drivers/soc/bcm/bcm2835-aux.c b/drivers/soc/bcm/bcm2835-aux.c

> +static void *bcm2835aux_find_base(struct device *dev, const char *property)
> +{
> +	struct device *found = NULL;
> +	struct device_node *np;
> +
> +	/* get the phandle of the device */
> +	np = of_parse_phandle(dev->of_node, property, 0);
> +	if (!np) {
> +		dev_err(dev, "missing property %s\n", property);
> +		return ERR_PTR(-ENODEV);
> +	}
> +
> +	/* now find the device it points to */
> +	found = driver_find_device(&bcm2835aux_driver.driver, NULL,
> +				   np, bcm2835aux_dev_match);
> +	if (!found) {
> +		dev_err(dev, "device for phandle of %s not found\n",
> +			property);
> +		return ERR_PTR(-ENODEV);

That should return ERR_PTR(-EPROBE_DEFER) so that client drivers know
when to defer their own probe, and not print an error. This is an
expected condition during probing. I could have sworn this was correct
in a previous patch revision.


From swarren at wwwdotorg.org  Tue Aug 25 18:56:31 2015
From: swarren at wwwdotorg.org (Stephen Warren)
Date: Tue, 25 Aug 2015 19:56:31 -0600
Subject: [PATCH v4 4/5] spi: bcm2835: new driver implementing auxiliar
 spi1/spi2 on the bcm2835 soc
In-Reply-To: <1440405608-3995-5-git-send-email-kernel@martin.sperl.org>
References: <1440405608-3995-1-git-send-email-kernel@martin.sperl.org>
 <1440405608-3995-5-git-send-email-kernel@martin.sperl.org>
Message-ID: <55DD1CCF.4080808@wwwdotorg.org>

On 08/24/2015 02:40 AM, kernel at martin.sperl.org wrote:
> From: Martin Sperl <kernel at martin.sperl.org>

Patch description?

>  arch/arm/configs/bcm2835_defconfig |    1 +
>  drivers/spi/Kconfig                |   12 +
>  drivers/spi/Makefile               |    1 +
>  drivers/spi/spi-bcm2835aux.c       |  506 ++++++++++++++++++++++++++++++++++++

A change to the defconfig would be applied by the RPi maintainers, and a
change to drivers/spi by the SPI maintainers. Those need to be in
different patches.

> diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c

> +static int bcm2835aux_spi_probe(struct platform_device *pdev)

> +	clk_prepare_enable(bs->clk);

Error checking?

> +	/* enable HW block */
> +	bcm2835aux_enable(&pdev->dev, ENABLE_PROPERTY);

The return value needs to be error-checked, so that deferred probe can
work, and so other kinds of errors can be detected. Wasn't this correct
in a previous patch version?

Note that I didn't review any code besides probe(), remove() and the
driver boiler-plate that refers to those functions.



From linus.walleij at linaro.org  Wed Aug 26 04:38:19 2015
From: linus.walleij at linaro.org (Linus Walleij)
Date: Wed, 26 Aug 2015 13:38:19 +0200
Subject: [PATCH V2] Watchdog: Fix parent of watchdog_devices
In-Reply-To: <9fcc2a2cc8273f461baa13c6c9992f3ca56b508a.1439915117.git.panand@redhat.com>
References: <9fcc2a2cc8273f461baa13c6c9992f3ca56b508a.1439915117.git.panand@redhat.com>
Message-ID: <CACRpkdbT0Ew_LXys++EDykM-gp08dxKQ4HYp0CfAmk7EoCET9A@mail.gmail.com>

On Tue, Aug 18, 2015 at 6:34 PM, Pratyush Anand <panand at redhat.com> wrote:

> /sys/class/watchdog/watchdogn/device/modalias can help to identify the
> driver/module for a given watchdog node. However, many wdt devices does not set
> its parent and so, we do not see an entry for device in sysfs for such devices.
>
> This patch fixes parent of watchdog_device so that
> /sys/class/watchdog/watchdogn/device is populated.
>
> Exceptions: booke, diag288, mpc8xxx, octeon, softdog and w83627hf -- They do not
> have any parent. Not sure, how we can we identify driver for these devices.
>
> Signed-off-by: Pratyush Anand <panand at redhat.com>

Acked-by: Linus Walleij <linus.walleij at linaro.org>

Yours,
Linus Walleij


From robh at kernel.org  Wed Aug 26 06:47:38 2015
From: robh at kernel.org (Rob Herring)
Date: Wed, 26 Aug 2015 08:47:38 -0500
Subject: [PATCH v3] irqchip: kill off set_irq_flags usage
In-Reply-To: <1438030523-15853-6-git-send-email-robh@kernel.org>
References: <1438030523-15853-1-git-send-email-robh@kernel.org>
 <1438030523-15853-6-git-send-email-robh@kernel.org>
Message-ID: <CAL_JsqL9EsFpE3j_ud6bvoJ38di+2428mBD2VgQPX8fba=BpdQ@mail.gmail.com>

On Mon, Jul 27, 2015 at 3:55 PM, Rob Herring <robh at kernel.org> wrote:
> set_irq_flags is ARM specific with custom flags which have genirq
> equivalents. Convert drivers to use the genirq interfaces directly, so we
> can kill off set_irq_flags. The translation of flags is as follows:
>
> IRQF_VALID -> !IRQ_NOREQUEST
> IRQF_PROBE -> !IRQ_NOPROBE
> IRQF_NOAUTOEN -> IRQ_NOAUTOEN
>
> For IRQs managed by an irqdomain, the irqdomain core code handles clearing
> and setting IRQ_NOREQUEST already, so there is no need to do this in
> .map() functions and we can simply remove the set_irq_flags calls. Some
> users also modify IRQ_NOPROBE and this has been maintained although it
> is not clear that is really needed. There appears to be a great deal of
> blind copy and paste of this code.
>
> Signed-off-by: Rob Herring <robh at kernel.org>
> Cc: Thomas Gleixner <tglx at linutronix.de>
> Cc: Jason Cooper <jason at lakedaemon.net>
> Cc: Kukjin Kim <kgene at kernel.org>
> Cc: Krzysztof Kozlowski <k.kozlowski at samsung.com>
> Cc: Stephen Warren <swarren at wwwdotorg.org>
> Cc: Lee Jones <lee at kernel.org>
> Cc: Alexander Shiyan <shc_work at mail.ru>
> Cc: Maxime Ripard <maxime.ripard at free-electrons.com>
> Cc: linux-arm-kernel at lists.infradead.org
> Cc: linux-samsung-soc at vger.kernel.org
> Cc: linux-rpi-kernel at lists.infradead.org
> ---
> Thomas asked that this be merged thru subsystem trees instead of arm-soc,
> so please apply this to your tree.

Thomas, Jason,

Everything except this patch, sh, gpu, and final removal of
set_irq_flags in arm/arm64 are in -next. There don't appear to be any
new users either.

Can you apply this patch and these 2 now so they have some time in -next:

http://lkml.kernel.org/r/1438030523-15853-11-git-send-email-robh at kernel.org
http://lkml.kernel.org/r/1438030523-15853-5-git-send-email-robh at kernel.org

And these 2 toward the end of the merge window once there is no more
set_irq_flags:

http://lkml.kernel.org/r/1436711211-18223-13-git-send-email-robh at kernel.org
http://lkml.kernel.org/r/1436711211-18223-14-git-send-email-robh at kernel.org

Rob

>  drivers/irqchip/exynos-combiner.c         |  2 +-
>  drivers/irqchip/irq-armada-370-xp.c       |  3 +--
>  drivers/irqchip/irq-bcm2835.c             |  2 +-
>  drivers/irqchip/irq-clps711x.c            |  6 +++---
>  drivers/irqchip/irq-gic-v3.c              |  5 ++---
>  drivers/irqchip/irq-gic.c                 |  4 ++--
>  drivers/irqchip/irq-hip04.c               |  4 ++--
>  drivers/irqchip/irq-keystone.c            |  2 +-
>  drivers/irqchip/irq-mmp.c                 |  3 ---
>  drivers/irqchip/irq-mxs.c                 |  1 -
>  drivers/irqchip/irq-renesas-intc-irqpin.c |  1 -
>  drivers/irqchip/irq-renesas-irqc.c        |  1 -
>  drivers/irqchip/irq-s3c24xx.c             | 14 ++------------
>  drivers/irqchip/irq-sun4i.c               |  2 +-
>  drivers/irqchip/irq-versatile-fpga.c      |  2 +-
>  drivers/irqchip/irq-vic.c                 |  2 +-
>  drivers/irqchip/irq-vt8500.c              |  1 -
>  drivers/irqchip/spear-shirq.c             |  1 -
>  18 files changed, 18 insertions(+), 38 deletions(-)
>
> diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c
> index 5c82e3b..a62cfd3 100644
> --- a/drivers/irqchip/exynos-combiner.c
> +++ b/drivers/irqchip/exynos-combiner.c
> @@ -165,7 +165,7 @@ static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
>
>         irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq);
>         irq_set_chip_data(irq, &combiner_data[hw >> 3]);
> -       set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
> +       irq_set_probe(irq);
>
>         return 0;
>  }
> diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
> index 0d3b0fe..017f881 100644
> --- a/drivers/irqchip/irq-armada-370-xp.c
> +++ b/drivers/irqchip/irq-armada-370-xp.c
> @@ -201,7 +201,6 @@ static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq,
>  {
>         irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip,
>                                  handle_simple_irq);
> -       set_irq_flags(virq, IRQF_VALID);
>
>         return 0;
>  }
> @@ -318,7 +317,7 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
>                 irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
>                                         handle_level_irq);
>         }
> -       set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
> +       irq_set_probe(virq);
>
>         return 0;
>  }
> diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c
> index e68c3b6..9c4ba16 100644
> --- a/drivers/irqchip/irq-bcm2835.c
> +++ b/drivers/irqchip/irq-bcm2835.c
> @@ -165,7 +165,7 @@ static int __init armctrl_of_init(struct device_node *node,
>                         BUG_ON(irq <= 0);
>                         irq_set_chip_and_handler(irq, &armctrl_chip,
>                                 handle_level_irq);
> -                       set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
> +                       irq_set_probe(irq);
>                 }
>         }
>
> diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c
> index 33127f1..2e74e81 100644
> --- a/drivers/irqchip/irq-clps711x.c
> +++ b/drivers/irqchip/irq-clps711x.c
> @@ -133,14 +133,14 @@ static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq,
>                                         irq_hw_number_t hw)
>  {
>         irq_flow_handler_t handler = handle_level_irq;
> -       unsigned int flags = IRQF_VALID | IRQF_PROBE;
> +       unsigned int flags = 0;
>
>         if (!clps711x_irqs[hw].flags)
>                 return 0;
>
>         if (clps711x_irqs[hw].flags & CLPS711X_FLAG_FIQ) {
>                 handler = handle_bad_irq;
> -               flags |= IRQF_NOAUTOEN;
> +               flags |= IRQ_NOAUTOEN;
>         } else if (clps711x_irqs[hw].eoi) {
>                 handler = handle_fasteoi_irq;
>         }
> @@ -150,7 +150,7 @@ static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq,
>                 writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hw].eoi);
>
>         irq_set_chip_and_handler(virq, &clps711x_intc_chip, handler);
> -       set_irq_flags(virq, flags);
> +       irq_modify_status(virq, IRQ_NOPROBE, flags);
>
>         return 0;
>  }
> diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
> index c52f7ba..16f9028 100644
> --- a/drivers/irqchip/irq-gic-v3.c
> +++ b/drivers/irqchip/irq-gic-v3.c
> @@ -681,13 +681,13 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
>                 irq_set_percpu_devid(irq);
>                 irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
>                                     handle_percpu_devid_irq, NULL, NULL);
> -               set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
> +               irq_set_status_flags(irq, IRQ_NOAUTOEN);
>         }
>         /* SPIs */
>         if (hw >= 32 && hw < gic_data.irq_nr) {
>                 irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
>                                     handle_fasteoi_irq, NULL, NULL);
> -               set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
> +               irq_set_probe(irq);
>         }
>         /* LPIs */
>         if (hw >= 8192 && hw < GIC_ID_NR) {
> @@ -695,7 +695,6 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
>                         return -EPERM;
>                 irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
>                                     handle_fasteoi_irq, NULL, NULL);
> -               set_irq_flags(irq, IRQF_VALID);
>         }
>
>         return 0;
> diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
> index 4dd8826..d6a1ba1 100644
> --- a/drivers/irqchip/irq-gic.c
> +++ b/drivers/irqchip/irq-gic.c
> @@ -793,11 +793,11 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
>                 irq_set_percpu_devid(irq);
>                 irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
>                                     handle_percpu_devid_irq, NULL, NULL);
> -               set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
> +               irq_set_status_flags(irq, IRQ_NOAUTOEN);
>         } else {
>                 irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
>                                     handle_fasteoi_irq, NULL, NULL);
> -               set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
> +               irq_set_probe(irq);
>         }
>         return 0;
>  }
> diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c
> index 0cae45d..469fba6 100644
> --- a/drivers/irqchip/irq-hip04.c
> +++ b/drivers/irqchip/irq-hip04.c
> @@ -305,11 +305,11 @@ static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq,
>                 irq_set_percpu_devid(irq);
>                 irq_set_chip_and_handler(irq, &hip04_irq_chip,
>                                          handle_percpu_devid_irq);
> -               set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
> +               irq_set_status_flags(irq, IRQ_NOAUTOEN);
>         } else {
>                 irq_set_chip_and_handler(irq, &hip04_irq_chip,
>                                          handle_fasteoi_irq);
> -               set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
> +               irq_set_probe(irq);
>         }
>         irq_set_chip_data(irq, d->host_data);
>         return 0;
> diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c
> index 81e3cf5..e7090ec 100644
> --- a/drivers/irqchip/irq-keystone.c
> +++ b/drivers/irqchip/irq-keystone.c
> @@ -127,7 +127,7 @@ static int keystone_irq_map(struct irq_domain *h, unsigned int virq,
>
>         irq_set_chip_data(virq, kirq);
>         irq_set_chip_and_handler(virq, &kirq->chip, handle_level_irq);
> -       set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
> +       irq_set_probe(virq);
>         return 0;
>  }
>
> diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c
> index c0da57b..3dfef63 100644
> --- a/drivers/irqchip/irq-mmp.c
> +++ b/drivers/irqchip/irq-mmp.c
> @@ -164,7 +164,6 @@ static int mmp_irq_domain_map(struct irq_domain *d, unsigned int irq,
>                               irq_hw_number_t hw)
>  {
>         irq_set_chip_and_handler(irq, &icu_irq_chip, handle_level_irq);
> -       set_irq_flags(irq, IRQF_VALID);
>         return 0;
>  }
>
> @@ -234,7 +233,6 @@ void __init icu_init_irq(void)
>         for (irq = 0; irq < 64; irq++) {
>                 icu_mask_irq(irq_get_irq_data(irq));
>                 irq_set_chip_and_handler(irq, &icu_irq_chip, handle_level_irq);
> -               set_irq_flags(irq, IRQF_VALID);
>         }
>         irq_set_default_host(icu_data[0].domain);
>         set_handle_irq(mmp_handle_irq);
> @@ -337,7 +335,6 @@ void __init mmp2_init_icu(void)
>                         irq_set_chip_and_handler(irq, &icu_irq_chip,
>                                                  handle_level_irq);
>                 }
> -               set_irq_flags(irq, IRQF_VALID);
>         }
>         irq_set_default_host(icu_data[0].domain);
>         set_handle_irq(mmp2_handle_irq);
> diff --git a/drivers/irqchip/irq-mxs.c b/drivers/irqchip/irq-mxs.c
> index 04bf97b..ffe85d6 100644
> --- a/drivers/irqchip/irq-mxs.c
> +++ b/drivers/irqchip/irq-mxs.c
> @@ -85,7 +85,6 @@ static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq,
>                                 irq_hw_number_t hw)
>  {
>         irq_set_chip_and_handler(virq, &mxs_icoll_chip, handle_level_irq);
> -       set_irq_flags(virq, IRQF_VALID);
>
>         return 0;
>  }
> diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c
> index 0670ab4..20a86de 100644
> --- a/drivers/irqchip/irq-renesas-intc-irqpin.c
> +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c
> @@ -343,7 +343,6 @@ static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq,
>         intc_irqpin_dbg(&p->irq[hw], "map");
>         irq_set_chip_data(virq, h->host_data);
>         irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
> -       set_irq_flags(virq, IRQF_VALID); /* kill me now */
>         return 0;
>  }
>
> diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c
> index 778bd07..74e980f 100644
> --- a/drivers/irqchip/irq-renesas-irqc.c
> +++ b/drivers/irqchip/irq-renesas-irqc.c
> @@ -162,7 +162,6 @@ static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq,
>         irqc_dbg(&p->irq[hw], "map");
>         irq_set_chip_data(virq, h->host_data);
>         irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
> -       set_irq_flags(virq, IRQF_VALID); /* kill me now */
>         return 0;
>  }
>
> diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c
> index e96717f..19d1fc4 100644
> --- a/drivers/irqchip/irq-s3c24xx.c
> +++ b/drivers/irqchip/irq-s3c24xx.c
> @@ -469,13 +469,11 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq,
>
>         irq_set_chip_data(virq, irq_data);
>
> -       set_irq_flags(virq, IRQF_VALID);
> -
>         if (parent_intc && irq_data->type != S3C_IRQTYPE_NONE) {
>                 if (irq_data->parent_irq > 31) {
>                         pr_err("irq-s3c24xx: parent irq %lu is out of range\n",
>                                irq_data->parent_irq);
> -                       goto err;
> +                       return -EINVAL;
>                 }
>
>                 parent_irq_data = &parent_intc->irqs[irq_data->parent_irq];
> @@ -488,18 +486,12 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq,
>                 if (!irqno) {
>                         pr_err("irq-s3c24xx: could not find mapping for parent irq %lu\n",
>                                irq_data->parent_irq);
> -                       goto err;
> +                       return -EINVAL;
>                 }
>                 irq_set_chained_handler(irqno, s3c_irq_demux);
>         }
>
>         return 0;
> -
> -err:
> -       set_irq_flags(virq, 0);
> -
> -       /* the only error can result from bad mapping data*/
> -       return -EINVAL;
>  }
>
>  static const struct irq_domain_ops s3c24xx_irq_ops = {
> @@ -1177,8 +1169,6 @@ static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq,
>
>         irq_set_chip_data(virq, irq_data);
>
> -       set_irq_flags(virq, IRQF_VALID);
> -
>         return 0;
>  }
>
> diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c
> index 83d6aa6..f1f401c 100644
> --- a/drivers/irqchip/irq-sun4i.c
> +++ b/drivers/irqchip/irq-sun4i.c
> @@ -84,7 +84,7 @@ static int sun4i_irq_map(struct irq_domain *d, unsigned int virq,
>                          irq_hw_number_t hw)
>  {
>         irq_set_chip_and_handler(virq, &sun4i_irq_chip, handle_fasteoi_irq);
> -       set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
> +       irq_set_probe(virq);
>
>         return 0;
>  }
> diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c
> index 888111b..e96b4fe 100644
> --- a/drivers/irqchip/irq-versatile-fpga.c
> +++ b/drivers/irqchip/irq-versatile-fpga.c
> @@ -128,7 +128,7 @@ static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq,
>         irq_set_chip_data(irq, f);
>         irq_set_chip_and_handler(irq, &f->chip,
>                                 handle_level_irq);
> -       set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
> +       irq_set_probe(irq);
>         return 0;
>  }
>
> diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c
> index d4ce331..401561c 100644
> --- a/drivers/irqchip/irq-vic.c
> +++ b/drivers/irqchip/irq-vic.c
> @@ -202,7 +202,7 @@ static int vic_irqdomain_map(struct irq_domain *d, unsigned int irq,
>                 return -EPERM;
>         irq_set_chip_and_handler(irq, &vic_chip, handle_level_irq);
>         irq_set_chip_data(irq, v->base);
> -       set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
> +       irq_set_probe(irq);
>         return 0;
>  }
>
> diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c
> index 0b29700..8b235c9 100644
> --- a/drivers/irqchip/irq-vt8500.c
> +++ b/drivers/irqchip/irq-vt8500.c
> @@ -168,7 +168,6 @@ static int vt8500_irq_map(struct irq_domain *h, unsigned int virq,
>                                                         irq_hw_number_t hw)
>  {
>         irq_set_chip_and_handler(virq, &vt8500_irq_chip, handle_level_irq);
> -       set_irq_flags(virq, IRQF_VALID);
>
>         return 0;
>  }
> diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c
> index acb721b..e2b4e99 100644
> --- a/drivers/irqchip/spear-shirq.c
> +++ b/drivers/irqchip/spear-shirq.c
> @@ -212,7 +212,6 @@ static void __init spear_shirq_register(struct spear_shirq *shirq,
>         for (i = 0; i < shirq->nr_irqs; i++) {
>                 irq_set_chip_and_handler(shirq->virq_base + i,
>                                          shirq->irq_chip, handle_simple_irq);
> -               set_irq_flags(shirq->virq_base + i, IRQF_VALID);
>                 irq_set_chip_data(shirq->virq_base + i, shirq);
>         }
>  }
> --
> 2.1.0
>


From airlied at gmail.com  Wed Aug 26 13:59:36 2015
From: airlied at gmail.com (Dave Airlie)
Date: Thu, 27 Aug 2015 06:59:36 +1000
Subject: [PATCH 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <CAL_JsqJ2v=wBaUe_gN_UcpQKis+kRH6oa0NTN24Lp9w9LVWtbQ@mail.gmail.com>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-2-git-send-email-eric@anholt.net>
 <55CEC25E.7070303@wwwdotorg.org>
 <874mjxwyx5.fsf@eliezer.anholt.net>
 <CAL_JsqJxiGUR5BxBPyR3-0NxqWsOxMGrWQA6cMXe2pjTom__nw@mail.gmail.com>
 <CAF6AEGu2fi0M7DsicHz+5SDHi1DyDRmvWjkmYPPgY6pZ423WcA@mail.gmail.com>
 <20150826115229.GI1367@phenom.ffwll.local>
 <20150826120934.GC320@ulmo.nvidia.com>
 <CAL_JsqJ2v=wBaUe_gN_UcpQKis+kRH6oa0NTN24Lp9w9LVWtbQ@mail.gmail.com>
Message-ID: <CAPM=9tw=3yGQE3_G+=ian-+iV-OCRatbFc-stoMczmXeM2Ko+Q@mail.gmail.com>

On 27 August 2015 at 00:30, Rob Herring <robherring2 at gmail.com> wrote:
> On Wed, Aug 26, 2015 at 7:09 AM, Thierry Reding
> <thierry.reding at gmail.com> wrote:
>> On Wed, Aug 26, 2015 at 01:52:29PM +0200, Daniel Vetter wrote:
>>> On Tue, Aug 25, 2015 at 04:42:18PM -0400, Rob Clark wrote:
>>> > On Mon, Aug 24, 2015 at 9:47 AM, Rob Herring <robherring2 at gmail.com> wrote:
>>> > > On Mon, Aug 17, 2015 at 1:30 PM, Eric Anholt <eric at anholt.net> wrote:
>>> > >> Stephen Warren <swarren at wwwdotorg.org> writes:
>>> > >>
>>> > >>> On 08/12/2015 06:56 PM, Eric Anholt wrote:
>>> > >>>> Signed-off-by: Eric Anholt <eric at anholt.net>
>>> > >>>
>>> > >>> This one definitely needs a patch description, since someone might not
>>> > >>> know what a VC4 is, and "git log" won't show the text from the binding
>>> > >>> doc itself. I'd suggest adding the initial paragraph of the binding doc
>>> > >>> as the patch description, or more.
>>> > >>>
>>> > >>>> diff --git a/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/gpu/brcm,bcm-vc4.txt
>>> > >>
>>> > >>>> +- hvss:             List of references to HVS video scalers
>>> > >>>> +- encoders: List of references to output encoders (HDMI, SDTV)
>>> > >>>
>>> > >>> Would it make sense to make all those nodes child node of the vc4
>>> > >>> object. That way, there's no need to have these lists of objects; they
>>> > >>> can be automatically built up as the DT is enumerated. I know that e.g.
>>> > >>> the NVIDIA Tegra host1x binding works this way, and I think it may have
>>> > >>> been inspired by other similar cases.
>>> > >>
>>> > >> I've looked at tegra, and the component system used by msm appears to be
>>> > >> nicer than it.  To follow tegra's model, it looks like I need to build
>>> > >> this extra bus thing corresponding to host1x that is effectively the
>>> > >> drivers/base/component.c code, so that I can get at vc4's structure from
>>> > >> the component drivers.
>>> > >>
>>> > >>> Of course, this is only appropriate if the HW modules really are
>>> > >>> logically children of the VC4 HW module. Perhaps they aren't. If they
>>> > >>> aren't though, I wonder what this "vc4" module actually represents in HW?
>>> > >>
>>> > >> It's the subsystem, same as we use a subsystem node for msm, sti,
>>> > >> rockchip, imx, and exynos.  This appears to be the common model of how
>>> > >> the collection of graphics-related components is represented in the DT.
>>> > >
>>> > > I think most of these bindings are wrong. They are grouped together
>>> > > because that is what DRM wants not because that reflects the h/w. So
>>> > > convince me this is one block, not that it is what other people do.
>>> >
>>> > I think, when it comes to more complex driver subsystems (like drm in
>>> > particular) we have a bit of mismatch between how things look from the
>>> > "pure hw ignoring sw" perspective, and the "how sw and in particular
>>> > userspace expects things" perspective.  Maybe it is less a problem in
>>> > other subsystems, where bindings map to things that are only visible
>>> > in the kernel, or well defined devices like uart or sata controller.
>>> > But when given the choice, I'm going to err on the side of not
>>> > confusing userspace and the large software stack that sits above
>>> > drm/kms, over dt purity.
>>> >
>>> > Maybe it would be nice to have a sort of dt overlay that adds the bits
>>> > needed to tie together hw blocks that should be assembled into a
>>> > single logical device for linux and userspace (but maybe not some
>>> > other hypothetical operating system).  But so far that doesn't exist.
>>> > All we have is a hammer (devicetree), everything looks like a nail.
>>> > End result is we end up adding some things in the bindings which
>>> > aren't purely about the hw.  Until someone invents a screwdriver, I'm
>>> > not sure what else to do.  In the end, other hypothetical OS is free
>>> > to ignore those extra fields in the bindings if it doesn't need them.
>>> > So meh?
>>>
>>> I thought we agreed a while back that these kind of "pull everything for
>>> the logical device together" dt nodes which just have piles of phandles
>>> are totally accepted? At least that's the point behind the component
>>> helpers, and Eric even suggested to create dt-specific component helpers
>>> to cut down a bit on the usual boilerplate. dt maintainers are also fine
>>> with this approach afaik. From my understanding tegra with the host1x bus
>>> really is the odd one out and not the norm.
>>
>> I agree that in many aspects Tegra is somewhat special. But the same
>> principles that the host1x infrastructure uses could be implemented in a
>> similar way for other DRM drivers. You can easily collect information
>> about subdevices by walking the device tree and matching on known
>> compatible strings. And you can also instantiate the top-level device
>> from driver code rather than have it in DT. It should still be possible
>> to make this work without an artificial device node in DT. The component
>> and master infrastructure is largely orthogonal to that, and as far as I
>> remember the only blocker is the need for a top-level device. I wonder
>> if perhaps this could be made to work by binding the master to the top-
>> level SoC device.
>>
>> Obviously adding the node in DT is easier, but to my knowledge easy has
>> never been a good excuse for mangling DT. Perhaps that's different these
>> days...
>
> I agree we should avoid the virtual node if possible. It is certainly
> possible as I started out with one and removed it. At least in my
> case, it essentially required the drm_device and crtc to be a single
> driver rather than 2 components. However, I'm more concerned that we
> are consistent from platform to platform where it makes sense than
> whether we have a somewhat questionable node or not.
>
http://lists.freedesktop.org/archives/dri-devel/2013-July/041159.html

So can we at least have some continuity of decision making,
bikeshedding this every time we submit a driver isn't giving me any
hope going forward.

Dave.


From robherring2 at gmail.com  Wed Aug 26 17:35:50 2015
From: robherring2 at gmail.com (Rob Herring)
Date: Wed, 26 Aug 2015 19:35:50 -0500
Subject: [PATCH 1/7] drm/vc4: Add devicetree bindings for VC4.
In-Reply-To: <CAPM=9tw=3yGQE3_G+=ian-+iV-OCRatbFc-stoMczmXeM2Ko+Q@mail.gmail.com>
References: <1439427380-2436-1-git-send-email-eric@anholt.net>
 <1439427380-2436-2-git-send-email-eric@anholt.net>
 <55CEC25E.7070303@wwwdotorg.org>
 <874mjxwyx5.fsf@eliezer.anholt.net>
 <CAL_JsqJxiGUR5BxBPyR3-0NxqWsOxMGrWQA6cMXe2pjTom__nw@mail.gmail.com>
 <CAF6AEGu2fi0M7DsicHz+5SDHi1DyDRmvWjkmYPPgY6pZ423WcA@mail.gmail.com>
 <20150826115229.GI1367@phenom.ffwll.local>
 <20150826120934.GC320@ulmo.nvidia.com>
 <CAL_JsqJ2v=wBaUe_gN_UcpQKis+kRH6oa0NTN24Lp9w9LVWtbQ@mail.gmail.com>
 <CAPM=9tw=3yGQE3_G+=ian-+iV-OCRatbFc-stoMczmXeM2Ko+Q@mail.gmail.com>
Message-ID: <CAL_JsqLPHuzvt-AsSVKLFGbxMQJ6JM4gz1M8UOamSBagHi=vXA@mail.gmail.com>

On Wed, Aug 26, 2015 at 3:59 PM, Dave Airlie <airlied at gmail.com> wrote:
> On 27 August 2015 at 00:30, Rob Herring <robherring2 at gmail.com> wrote:
>> On Wed, Aug 26, 2015 at 7:09 AM, Thierry Reding
>> <thierry.reding at gmail.com> wrote:
>>> On Wed, Aug 26, 2015 at 01:52:29PM +0200, Daniel Vetter wrote:
>>>> On Tue, Aug 25, 2015 at 04:42:18PM -0400, Rob Clark wrote:
>>>> > On Mon, Aug 24, 2015 at 9:47 AM, Rob Herring <robherring2 at gmail.com> wrote:
>>>> > > On Mon, Aug 17, 2015 at 1:30 PM, Eric Anholt <eric at anholt.net> wrote:
>>>> > >> Stephen Warren <swarren at wwwdotorg.org> writes:
>>>> > >>
>>>> > >>> On 08/12/2015 06:56 PM, Eric Anholt wrote:
>>>> > >>>> Signed-off-by: Eric Anholt <eric at anholt.net>
[...]
>>>> > >>>> +- hvss:             List of references to HVS video scalers
>>>> > >>>> +- encoders: List of references to output encoders (HDMI, SDTV)
[...]
>>>> > >>> Of course, this is only appropriate if the HW modules really are
>>>> > >>> logically children of the VC4 HW module. Perhaps they aren't. If they
>>>> > >>> aren't though, I wonder what this "vc4" module actually represents in HW?
>>>> > >>
>>>> > >> It's the subsystem, same as we use a subsystem node for msm, sti,
>>>> > >> rockchip, imx, and exynos.  This appears to be the common model of how
>>>> > >> the collection of graphics-related components is represented in the DT.
>>>> > >
>>>> > > I think most of these bindings are wrong. They are grouped together
>>>> > > because that is what DRM wants not because that reflects the h/w. So
>>>> > > convince me this is one block, not that it is what other people do.
>>>> >
>>>> > I think, when it comes to more complex driver subsystems (like drm in
>>>> > particular) we have a bit of mismatch between how things look from the
>>>> > "pure hw ignoring sw" perspective, and the "how sw and in particular
>>>> > userspace expects things" perspective.  Maybe it is less a problem in
>>>> > other subsystems, where bindings map to things that are only visible
>>>> > in the kernel, or well defined devices like uart or sata controller.
>>>> > But when given the choice, I'm going to err on the side of not
>>>> > confusing userspace and the large software stack that sits above
>>>> > drm/kms, over dt purity.
>>>> >
>>>> > Maybe it would be nice to have a sort of dt overlay that adds the bits
>>>> > needed to tie together hw blocks that should be assembled into a
>>>> > single logical device for linux and userspace (but maybe not some
>>>> > other hypothetical operating system).  But so far that doesn't exist.
>>>> > All we have is a hammer (devicetree), everything looks like a nail.
>>>> > End result is we end up adding some things in the bindings which
>>>> > aren't purely about the hw.  Until someone invents a screwdriver, I'm
>>>> > not sure what else to do.  In the end, other hypothetical OS is free
>>>> > to ignore those extra fields in the bindings if it doesn't need them.
>>>> > So meh?
>>>>
>>>> I thought we agreed a while back that these kind of "pull everything for
>>>> the logical device together" dt nodes which just have piles of phandles
>>>> are totally accepted? At least that's the point behind the component
>>>> helpers, and Eric even suggested to create dt-specific component helpers
>>>> to cut down a bit on the usual boilerplate. dt maintainers are also fine
>>>> with this approach afaik. From my understanding tegra with the host1x bus
>>>> really is the odd one out and not the norm.
>>>
>>> I agree that in many aspects Tegra is somewhat special. But the same
>>> principles that the host1x infrastructure uses could be implemented in a
>>> similar way for other DRM drivers. You can easily collect information
>>> about subdevices by walking the device tree and matching on known
>>> compatible strings. And you can also instantiate the top-level device
>>> from driver code rather than have it in DT. It should still be possible
>>> to make this work without an artificial device node in DT. The component
>>> and master infrastructure is largely orthogonal to that, and as far as I
>>> remember the only blocker is the need for a top-level device. I wonder
>>> if perhaps this could be made to work by binding the master to the top-
>>> level SoC device.
>>>
>>> Obviously adding the node in DT is easier, but to my knowledge easy has
>>> never been a good excuse for mangling DT. Perhaps that's different these
>>> days...
>>
>> I agree we should avoid the virtual node if possible. It is certainly
>> possible as I started out with one and removed it. At least in my
>> case, it essentially required the drm_device and crtc to be a single
>> driver rather than 2 components. However, I'm more concerned that we
>> are consistent from platform to platform where it makes sense than
>> whether we have a somewhat questionable node or not.
>>
> http://lists.freedesktop.org/archives/dri-devel/2013-July/041159.html
>
> So can we at least have some continuity of decision making,
> bikeshedding this every time we submit a driver isn't giving me any
> hope going forward.

As I said, whether we have a virtual node or not is a minor part of
having some consistency in bindings and was the only part Grant
discussed. We have though diverged from the specific problems with
this binding which is that it invents yet another way to describe the
relationship between components. The use of DRM component names in the
binding properties is the first clue. As to what is the "right" way,
well if that is known or documented I've seen no evidence. Defining
what that is and having the infrastructure in place to support it is
what I'm interested in.

Rob


From tglx at linutronix.de  Thu Aug 27 07:56:18 2015
From: tglx at linutronix.de (Thomas Gleixner)
Date: Thu, 27 Aug 2015 16:56:18 +0200 (CEST)
Subject: [PATCH v3] irqchip: kill off set_irq_flags usage
In-Reply-To: <CAL_JsqL9EsFpE3j_ud6bvoJ38di+2428mBD2VgQPX8fba=BpdQ@mail.gmail.com>
References: <1438030523-15853-1-git-send-email-robh@kernel.org>
 <1438030523-15853-6-git-send-email-robh@kernel.org>
 <CAL_JsqL9EsFpE3j_ud6bvoJ38di+2428mBD2VgQPX8fba=BpdQ@mail.gmail.com>
Message-ID: <alpine.DEB.2.11.1508271655220.15006@nanos>

On Wed, 26 Aug 2015, Rob Herring wrote:
> Everything except this patch, sh, gpu, and final removal of
> set_irq_flags in arm/arm64 are in -next. There don't appear to be any
> new users either.
> 
> Can you apply this patch and these 2 now so they have some time in -next:
> 
> http://lkml.kernel.org/r/1438030523-15853-11-git-send-email-robh at kernel.org
> http://lkml.kernel.org/r/1438030523-15853-5-git-send-email-robh at kernel.org
> 
> And these 2 toward the end of the merge window once there is no more
> set_irq_flags:
> 
> http://lkml.kernel.org/r/1436711211-18223-13-git-send-email-robh at kernel.org
> http://lkml.kernel.org/r/1436711211-18223-14-git-send-email-robh at kernel.org

Could you please resend these 5 patches to me. Seems I lost some of them.

Thanks,

	tglx


From luis at debethencourt.com  Thu Aug 27 08:04:01 2015
From: luis at debethencourt.com (Luis de Bethencourt)
Date: Thu, 27 Aug 2015 17:04:01 +0200
Subject: [PATCH] ASoC: bcm2835-i2s: Fix module autoload for OF platform drivers
Message-ID: <20150827150401.GA32653@goodgumbo.baconseed.org>

These platform drivers have a OF device ID table but the OF module
alias information is not created so module autoloading won't work.

Signed-off-by: Luis de Bethencourt <luis at debethencourt.com>
---
 sound/soc/bcm/bcm2835-i2s.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c
index 03fa1cb..8c435be 100644
--- a/sound/soc/bcm/bcm2835-i2s.c
+++ b/sound/soc/bcm/bcm2835-i2s.c
@@ -862,6 +862,8 @@ static const struct of_device_id bcm2835_i2s_of_match[] = {
 	{},
 };
 
+MODULE_DEVICE_TABLE(of, bcm2835_i2s_of_match);
+
 static struct platform_driver bcm2835_i2s_driver = {
 	.probe		= bcm2835_i2s_probe,
 	.driver		= {
-- 
2.4.6



From martin at sperl.org  Fri Aug 28 02:14:54 2015
From: martin at sperl.org (Martin Sperl)
Date: Fri, 28 Aug 2015 11:14:54 +0200
Subject: Raspberry Pi Compute Module
In-Reply-To: <659525E1-D58B-49A1-BCCB-12488C8CA279@sperl.org>
References: <55A93694.9060004@lategoodbye.de> <55AF10E5.304@wwwdotorg.org>
 <55AFCBC2.5030808@lategoodbye.de> <55B1B7D1.2030608@wwwdotorg.org>
 <C99F5FC0-D9B6-4DF1-AD2D-F706F55A5AF8@sperl.org> <55DF0620.2070801@sperl.org>
 <55DF3695.6080302@wwwdotorg.org>
 <CCBAFE96-14FD-4997-8417-A1CDD58CFD7B@sperl.org>
 <659525E1-D58B-49A1-BCCB-12488C8CA279@sperl.org>
Message-ID: <0B8A58F1-760C-4033-8FE1-72DB3155F23C@sperl.org>


> On 27.08.2015, at 20:09, Martin Sperl <martin at sperl.org> wrote:
> 
> 
> 
>> On 27.08.2015, at 20:04, Martin Sperl <martin at sperl.org> wrote:
>> 
>> 
>> 
>>> On 27.08.2015, at 18:11, Stephen Warren <swarren at wwwdotorg.org> 
>>>> Compiling the kernel with mmc as module plus mmc-debug enabled gives the
>>>> below:
>>>> after loading the driver:
>>> ...
>>>> [   69.432782] mmc0: clock 400000Hz busmode 2 powermode 2 cs 0 Vdd 21
>>>> width 0 timing 0
>>> 
>>> ... lot of working commands ...
>>> 
>>>> [   69.499732] mmc0: clock 25000000Hz busmode 2 powermode 2 cs 0 Vdd 21
>>>> width 0 timing 0
>>> 
>>> (up until now, the clock has always be 400KHz, not it's 25MHz)
>>> 
>>>> [   69.499854] mmc0: starting CMD6 arg 03a10101 flags 0000049d
>>>> [   69.499924] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
>>>> [   69.499982] mmc0: req failed (CMD6): -110, retrying...
>>>> [   69.500041] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
>>>> [   69.500091] mmc0: req failed (CMD6): -110, retrying...
>>>> [   69.500146] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
>>>> [   69.500188] mmc0: req failed (CMD6): -110, retrying...
>>>> [   69.500251] sdhci [sdhci_irq()]: *** mmc0 got interrupt: 0x00038000
>>>> [   69.500300] mmc0: req done (CMD6): -110: 00000000 00000000 00000000
>>>> 00000000
>>>> [   69.500345] mmc0: error -110 whilst initialising MMC card
>>> 
>>> My guess it that the clock rate change caused this, not the CMD6 itself?
>> 
>> I saw a patch for one device that required more delay (in the order of 100us) between cmd13 and cmd6 - can't pinpoint it right now...
>> 
>> But then logging would add some delays, but maybe not enough...
>> 
>> Maybe changing clock to 400khz would allow access to the device - even if it is slow...
>> 
>> Can we configure that with the device tree, just to see if it makes a difference?
>> 
> See here: https://lkml.org/lkml/2014/9/9/382
> for one device that required it?

I tried to add a delay in that location mentioned in the above patch, but it does not help!
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 0e9ae1c..2a6a912 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -530,6 +530,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8
        if (!use_busy_signal)
                goto out;

+       msleep(1);
+
        /*
         * CRC errors shall only be ignored in cases were CMD13 is used to poll
         * to detect busy completion.


Also note that I have tried to force a slow transfer speed via the device-tree,
but it did not help either - it still uses 25MHz...

Here the device-tree for sdhci - taken after all the modifications:
                sdhci at 7e300000 {
                        reg = <0x7e300000 0x100>;
                        interrupts = <0x2 0x1e>;

                        compatible = "brcm,bcm2835-sdhci";
                        bus-width = <0x4>;
                        clocks = <0xc>;
                        status = "okay?;
			/* local modifications after it did not work */
                        max-frequency = <0x61a80>; /* 400000 */
                        mmc-hs200-1_8v;
                        mmc-hs400-1_8v;
                        non-removable;
                };

Martin

From sjg at chromium.org  Fri Aug 28 11:27:03 2015
From: sjg at chromium.org (Simon Glass)
Date: Fri, 28 Aug 2015 12:27:03 -0600
Subject: [PATCH] arm: rpi: Device tree modifications for U-Boot
In-Reply-To: <CAL_JsqJ-N+nFEMEEmg4O45DVF0g382fUvjGTH=fP_HbgK38aKw@mail.gmail.com>
References: <1439303153-12171-1-git-send-email-sjg@chromium.org>
 <1439312701.2451.3.camel@lynxeye.de>
 <CAPnjgZ3ztXnkxZJvitiXiKMzq5+x46hbv0PW4HcF7yGgsXUV+Q@mail.gmail.com>
 <CAPnjgZ0hzyjy=8o8ocaGCoOC7Js4CirhystidbvtabzbhRi4RQ@mail.gmail.com>
 <CAL_Jsq++ihz6svJAKWwT7-=KyJvyWXjdf=tZb4aDGgV_Dc=-Nw@mail.gmail.com>
 <CAPnjgZ3ubK_5j4AV4wO6xgoRf8iN+G8YSQm0pSW5fVzwpxYu5Q@mail.gmail.com>
 <CAL_JsqJ-N+nFEMEEmg4O45DVF0g382fUvjGTH=fP_HbgK38aKw@mail.gmail.com>
Message-ID: <CAPnjgZ2L1TOOmiUSKk-=gpAUt8tvZU1SkGBmdbHJR1tBhE2nTA@mail.gmail.com>

Hi Rob,

On 25 August 2015 at 10:22, Rob Herring <robherring2 at gmail.com> wrote:
> On Sat, Aug 15, 2015 at 8:46 AM, Simon Glass <sjg at chromium.org> wrote:
>> Hi Rob,
>>
>> On 14 August 2015 at 14:29, Rob Herring <robherring2 at gmail.com> wrote:
>>> On Fri, Aug 14, 2015 at 1:34 PM, Simon Glass <sjg at chromium.org> wrote:
>>>> -linux-tegra
>>>>
>>>> Hi,
>>>>
>>>> On 12 August 2015 at 07:21, Simon Glass <sjg at chromium.org> wrote:
>>>>> Hi Lucas,
>>>>>
>>>>> On 11 August 2015 at 11:05, Lucas Stach <dev at lynxeye.de> wrote:
>>>>>> Hi Simon,
>>>>>>
>>>>>> why did you send this to the Tegra ML?
>>>>>>
>>>>>> Am Dienstag, den 11.08.2015, 08:25 -0600 schrieb Simon Glass:
>>>>>>> This updates the device tree from the kernel version to something suitable
>>>>>>> for U-Boot:
>>>>>>>
>>>>>>> - Add stdout-path alias for console
>>>>>>> - Mark the /soc node to be available pre-relocation so that the early
>>>>>>> serial console works (we need the 'ranges' property to be available)
>>>
>>> I find it quite strange that you must explicitly enable the parent
>>> node, but not the uart node.
>>
>> U-Boot tries hard to find and bind the UART using the stdout-path
>> link. I would like to add it in the UART node also but we were able to
>> work around it so far.
>>
>>>
>>>>>>>
>>>>>>> Signed-off-by: Simon Glass <sjg at chromium.org>
>>>>>>> ---
>>>>>>>
>>>>>>>  arch/arm/boot/dts/bcm2835.dtsi | 4 +++-
>>>>>>>  1 file changed, 3 insertions(+), 1 deletion(-)
>>>>>>>
>>>>>>> diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
>>>>>>> index 301c73f..bd6bff6 100644
>>>>>>> --- a/arch/arm/boot/dts/bcm2835.dtsi
>>>>>>> +++ b/arch/arm/boot/dts/bcm2835.dtsi
>>>>>>> @@ -8,6 +8,7 @@
>>>>>>>
>>>>>>>       chosen {
>>>>>>>               bootargs = "earlyprintk console=ttyAMA0";
>>>>>>> +             stdout-path = &uart;
>>>>>>>       };
>>>>>>>
>>>>>>>       soc {
>>>>>>> @@ -16,6 +17,7 @@
>>>>>>>               #size-cells = <1>;
>>>>>>>               ranges = <0x7e000000 0x20000000 0x02000000>;
>>>>>>>               dma-ranges = <0x40000000 0x00000000 0x20000000>;
>>>>>>> +             u-boot,dm-pre-reloc;
>>>>>>
>>>>>> Why do you need this and why should upstream carry your favourite
>>>>>> bootloaders configuration? This is in no way hardware description.
>>>>>
>>>>> I'm not sure how much you know about U-Boot, so let me know if you
>>>>> need more info.
>>>>>
>>>>> U-Boot normally starts up by setting up its serial UART and displaying
>>>>> a banner message. At this stage typically only a few devices are
>>>>> initialised (e.g. maybe just the UART). It then relocates itself to
>>>>> the top of memory and starts up all the devices. It throws away any
>>>>> previous devices that it set up before relocation and starts again.
>>>>>
>>>>> U-Boot uses a thing called driver model (dm) which handles driver
>>>>> binding and probing. Driver model has the device tree and would
>>>>> normally scan through it and create devices for everything it finds.
>>>
>>> How do you debug the DM itself? It seems like you still would need
>>> something earlier for debug like earlycon in the kernel. u-boot DM is
>>> probably simple enough you can get away with using it early, but you
>>> often can't as the complexity increases. Ultimately you need something
>>> simple that just hits all the registers needed to get characters out.
>>> What happens when you add pinmux, clocks, PMIC, power domains, etc. to
>>> the DM and they all become dependencies for the UART?
>>
>> This doesn't seem like a device tree binding question but let me try
>> to answer it.
>>
>> This is a problem - one of the challenges of driver model is that the
>> UART gets further away from the reset vector.
>
> This is a common problem for any complex embedded system. Nothing
> special about u-boot here. So either we don't need anything because
> everyone else has dealt with the problem in some way or we need a
> common solution because we all have this problem.

I'm struggling to understand the value of this statement - is it just
philosophizing? I do have a real problem and would like to solve it.
Please make a concrete suggestion.

>
>> In U-Boot there is a single debug UART which can be supported by the
>> various serial drivers (only one can be selected on each platform).
>> There is a special debug_uart_init() function which is supposed to set
>> up the UART for that platform. After that, and until driver model UART
>> is up, console output can go through the debug UART.
>
> So you don't need this for the common case of an early console, but
> only for platforms needing some other platform specific setup?

The debug UART is generally disabled and has very limited use. It is
hard-coded and does not use the device tree. I mentioned it because
you asked "How do you debug the DM itself". You dropped that from the
context so this thread is now quite confusing.

Let's ignore the debug UART.

>
>>>>> Before relocation we don't need every device. Also the CPU is often
>>>>> running slowly, perhaps without the cache enabled. SDRAM may not be
>>>>> available yet so space is short. We want to avoid starting up things
>>>>> that will not be used.
>>>>>
>>>>> So this property indicates that the device is needed before relocation
>>>>> and should be set up by driver model. We need it to avoid a very slow
>>>>> and memory-hungry startup.
>>>
>>> Can't the need for that property change over time? Either as more
>>> drivers are converted to DM you need to add this or you add some
>>> feature that depends on a driver (e.g. get a board rev or boot mode
>>> from GPIO). You would have backwards compatibility issues with this.
>>> I'm somewhat less worried about that for u-boot as we should be
>>> bundling the dtb and bootloader rather than kernel and dtb. For the
>>> UART, you can just get which UART to initialize early from
>>> stdout-path. But for other cases, couldn't you just have the platform
>>> provide the list of needed drivers. Then when u-boot needs change, you
>>> just change u-boot.
>>
>> Yes U-Boot and its device tree are normally built from the same tree
>> at the same time. We don't expect to have to support an older or newer
>> device tree with the same U-Boot binary. So I don't see a problem
>> here.
>
> My point is that if I had to pick how bootloader+dtb+kernel are
> bundled or not, I would rather see the dtb in sync with the bootloader
> rather than the kernel. But I can't decide that for everyone and
> neither can you. You still have a compatibility problem and that has
> to be addressed.

What are you getting at here? If I move to a new kernel and still use
an old device tree I may be missing features, or fail to boot. Don't
do that!

But do we need to talk about this? How does this affect my patch?

>
>> The UART should have as few dependencies as possible. But if the
>> particular platform needs to check a GPIO to find out the UART number
>> (i.e. stdout-path cannot be used) then we would need to support that
>> case. It hasn't come up yet but it might. I certainly see platforms
>> where we need some other nodes before relocation and would like to
>> mark them as such. But I hope that for UART we can generally use
>> stdout-path.
>>
>> The list of drivers doesn't help that much. What would I do with such
>> a list, and where would I keep such platform-specific information
>> other than in the device tree? It seems much simpler to hold the
>> information about what the platform needs to get through early boot in
>> one place. Then we have just a few lines of generic code to deal with
>> it.
>
> If you need certain drivers such as gpio, then you obviously have some
> board specific code using those drivers already. If you can tell the
> DM which devices to enumerate via DT, you can also tell the DM with C
> code.
>
> While doing everything generically without board code is nice, we
> should only do that for the common cases and handle the screwy cases
> with board code.

OK so let's ignore the GPIO example.

After reading your email I am none the wiser about what you are suggesting.

This is not a screwy case. Every board will have a console. In some
cases it is inside a 'soc {' node and in some cases it is not. The
pure solution would be to mark every UART node with
u-boot,dm-pre-reloc and we can do that if you prefer. It isn't
necessary though for the reasons I previously explained.

It seems reasonable that U-Boot should be able to add private
properties to the device tree, intended for U-Boot, just as Linux
does. What is the problem here?

If are you asking for a generic property that any bootloader could
use, to locate what is needed to bring up the serial console then
please propose something and I can take a look. But I know that not
all bootloaders work like U-Boot (small space-constrained SPL,
relocatable main loader, try to output something on a serial port as
early as possible).

How can we resolve this so that the device tree files in U-Boot and
Linux can stay in sync?

Regards,
Simon


From robh at kernel.org  Sat Aug 29 16:01:22 2015
From: robh at kernel.org (Rob Herring)
Date: Sat, 29 Aug 2015 18:01:22 -0500
Subject: [RESEND PATCH v3 2/5] irqchip: kill off set_irq_flags usage
In-Reply-To: <1440889285-5637-1-git-send-email-robh@kernel.org>
References: <1440889285-5637-1-git-send-email-robh@kernel.org>
Message-ID: <1440889285-5637-3-git-send-email-robh@kernel.org>

set_irq_flags is ARM specific with custom flags which have genirq
equivalents. Convert drivers to use the genirq interfaces directly, so we
can kill off set_irq_flags. The translation of flags is as follows:

IRQF_VALID -> !IRQ_NOREQUEST
IRQF_PROBE -> !IRQ_NOPROBE
IRQF_NOAUTOEN -> IRQ_NOAUTOEN

For IRQs managed by an irqdomain, the irqdomain core code handles clearing
and setting IRQ_NOREQUEST already, so there is no need to do this in
.map() functions and we can simply remove the set_irq_flags calls. Some
users also modify IRQ_NOPROBE and this has been maintained although it
is not clear that is really needed. There appears to be a great deal of
blind copy and paste of this code.

Signed-off-by: Rob Herring <robh at kernel.org>
Cc: Thomas Gleixner <tglx at linutronix.de>
Cc: Jason Cooper <jason at lakedaemon.net>
Cc: Kukjin Kim <kgene at kernel.org>
Cc: Krzysztof Kozlowski <k.kozlowski at samsung.com>
Cc: Stephen Warren <swarren at wwwdotorg.org>
Cc: Lee Jones <lee at kernel.org>
Cc: Alexander Shiyan <shc_work at mail.ru>
Cc: Maxime Ripard <maxime.ripard at free-electrons.com>
Cc: linux-arm-kernel at lists.infradead.org
Cc: linux-samsung-soc at vger.kernel.org
Cc: linux-rpi-kernel at lists.infradead.org
---
 drivers/irqchip/exynos-combiner.c         |  2 +-
 drivers/irqchip/irq-armada-370-xp.c       |  3 +--
 drivers/irqchip/irq-bcm2835.c             |  2 +-
 drivers/irqchip/irq-clps711x.c            |  6 +++---
 drivers/irqchip/irq-gic-v3.c              |  5 ++---
 drivers/irqchip/irq-gic.c                 |  4 ++--
 drivers/irqchip/irq-hip04.c               |  4 ++--
 drivers/irqchip/irq-keystone.c            |  2 +-
 drivers/irqchip/irq-mmp.c                 |  3 ---
 drivers/irqchip/irq-mxs.c                 |  1 -
 drivers/irqchip/irq-renesas-intc-irqpin.c |  1 -
 drivers/irqchip/irq-renesas-irqc.c        |  1 -
 drivers/irqchip/irq-s3c24xx.c             | 14 ++------------
 drivers/irqchip/irq-sun4i.c               |  2 +-
 drivers/irqchip/irq-versatile-fpga.c      |  2 +-
 drivers/irqchip/irq-vic.c                 |  2 +-
 drivers/irqchip/irq-vt8500.c              |  1 -
 drivers/irqchip/spear-shirq.c             |  1 -
 18 files changed, 18 insertions(+), 38 deletions(-)

diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c
index 5c82e3b..a62cfd3 100644
--- a/drivers/irqchip/exynos-combiner.c
+++ b/drivers/irqchip/exynos-combiner.c
@@ -165,7 +165,7 @@ static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
 
 	irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq);
 	irq_set_chip_data(irq, &combiner_data[hw >> 3]);
-	set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	irq_set_probe(irq);
 
 	return 0;
 }
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
index 0d3b0fe..017f881 100644
--- a/drivers/irqchip/irq-armada-370-xp.c
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -201,7 +201,6 @@ static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq,
 {
 	irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip,
 				 handle_simple_irq);
-	set_irq_flags(virq, IRQF_VALID);
 
 	return 0;
 }
@@ -318,7 +317,7 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
 		irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
 					handle_level_irq);
 	}
-	set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
+	irq_set_probe(virq);
 
 	return 0;
 }
diff --git a/drivers/irqchip/irq-bcm2835.c b/drivers/irqchip/irq-bcm2835.c
index e68c3b6..9c4ba16 100644
--- a/drivers/irqchip/irq-bcm2835.c
+++ b/drivers/irqchip/irq-bcm2835.c
@@ -165,7 +165,7 @@ static int __init armctrl_of_init(struct device_node *node,
 			BUG_ON(irq <= 0);
 			irq_set_chip_and_handler(irq, &armctrl_chip,
 				handle_level_irq);
-			set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+			irq_set_probe(irq);
 		}
 	}
 
diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c
index 33127f1..2e74e81 100644
--- a/drivers/irqchip/irq-clps711x.c
+++ b/drivers/irqchip/irq-clps711x.c
@@ -133,14 +133,14 @@ static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq,
 					irq_hw_number_t hw)
 {
 	irq_flow_handler_t handler = handle_level_irq;
-	unsigned int flags = IRQF_VALID | IRQF_PROBE;
+	unsigned int flags = 0;
 
 	if (!clps711x_irqs[hw].flags)
 		return 0;
 
 	if (clps711x_irqs[hw].flags & CLPS711X_FLAG_FIQ) {
 		handler = handle_bad_irq;
-		flags |= IRQF_NOAUTOEN;
+		flags |= IRQ_NOAUTOEN;
 	} else if (clps711x_irqs[hw].eoi) {
 		handler = handle_fasteoi_irq;
 	}
@@ -150,7 +150,7 @@ static int __init clps711x_intc_irq_map(struct irq_domain *h, unsigned int virq,
 		writel_relaxed(0, clps711x_intc->base + clps711x_irqs[hw].eoi);
 
 	irq_set_chip_and_handler(virq, &clps711x_intc_chip, handler);
-	set_irq_flags(virq, flags);
+	irq_modify_status(virq, IRQ_NOPROBE, flags);
 
 	return 0;
 }
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index c52f7ba..16f9028 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -681,13 +681,13 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
 		irq_set_percpu_devid(irq);
 		irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
 				    handle_percpu_devid_irq, NULL, NULL);
-		set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
+		irq_set_status_flags(irq, IRQ_NOAUTOEN);
 	}
 	/* SPIs */
 	if (hw >= 32 && hw < gic_data.irq_nr) {
 		irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
 				    handle_fasteoi_irq, NULL, NULL);
-		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+		irq_set_probe(irq);
 	}
 	/* LPIs */
 	if (hw >= 8192 && hw < GIC_ID_NR) {
@@ -695,7 +695,6 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
 			return -EPERM;
 		irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
 				    handle_fasteoi_irq, NULL, NULL);
-		set_irq_flags(irq, IRQF_VALID);
 	}
 
 	return 0;
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 4dd8826..d6a1ba1 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -793,11 +793,11 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
 		irq_set_percpu_devid(irq);
 		irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
 				    handle_percpu_devid_irq, NULL, NULL);
-		set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
+		irq_set_status_flags(irq, IRQ_NOAUTOEN);
 	} else {
 		irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
 				    handle_fasteoi_irq, NULL, NULL);
-		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+		irq_set_probe(irq);
 	}
 	return 0;
 }
diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c
index 0cae45d..469fba6 100644
--- a/drivers/irqchip/irq-hip04.c
+++ b/drivers/irqchip/irq-hip04.c
@@ -305,11 +305,11 @@ static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq,
 		irq_set_percpu_devid(irq);
 		irq_set_chip_and_handler(irq, &hip04_irq_chip,
 					 handle_percpu_devid_irq);
-		set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN);
+		irq_set_status_flags(irq, IRQ_NOAUTOEN);
 	} else {
 		irq_set_chip_and_handler(irq, &hip04_irq_chip,
 					 handle_fasteoi_irq);
-		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+		irq_set_probe(irq);
 	}
 	irq_set_chip_data(irq, d->host_data);
 	return 0;
diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c
index 81e3cf5..e7090ec 100644
--- a/drivers/irqchip/irq-keystone.c
+++ b/drivers/irqchip/irq-keystone.c
@@ -127,7 +127,7 @@ static int keystone_irq_map(struct irq_domain *h, unsigned int virq,
 
 	irq_set_chip_data(virq, kirq);
 	irq_set_chip_and_handler(virq, &kirq->chip, handle_level_irq);
-	set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
+	irq_set_probe(virq);
 	return 0;
 }
 
diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c
index c0da57b..3dfef63 100644
--- a/drivers/irqchip/irq-mmp.c
+++ b/drivers/irqchip/irq-mmp.c
@@ -164,7 +164,6 @@ static int mmp_irq_domain_map(struct irq_domain *d, unsigned int irq,
 			      irq_hw_number_t hw)
 {
 	irq_set_chip_and_handler(irq, &icu_irq_chip, handle_level_irq);
-	set_irq_flags(irq, IRQF_VALID);
 	return 0;
 }
 
@@ -234,7 +233,6 @@ void __init icu_init_irq(void)
 	for (irq = 0; irq < 64; irq++) {
 		icu_mask_irq(irq_get_irq_data(irq));
 		irq_set_chip_and_handler(irq, &icu_irq_chip, handle_level_irq);
-		set_irq_flags(irq, IRQF_VALID);
 	}
 	irq_set_default_host(icu_data[0].domain);
 	set_handle_irq(mmp_handle_irq);
@@ -337,7 +335,6 @@ void __init mmp2_init_icu(void)
 			irq_set_chip_and_handler(irq, &icu_irq_chip,
 						 handle_level_irq);
 		}
-		set_irq_flags(irq, IRQF_VALID);
 	}
 	irq_set_default_host(icu_data[0].domain);
 	set_handle_irq(mmp2_handle_irq);
diff --git a/drivers/irqchip/irq-mxs.c b/drivers/irqchip/irq-mxs.c
index 04bf97b..ffe85d6 100644
--- a/drivers/irqchip/irq-mxs.c
+++ b/drivers/irqchip/irq-mxs.c
@@ -85,7 +85,6 @@ static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq,
 				irq_hw_number_t hw)
 {
 	irq_set_chip_and_handler(virq, &mxs_icoll_chip, handle_level_irq);
-	set_irq_flags(virq, IRQF_VALID);
 
 	return 0;
 }
diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c
index 0670ab4..20a86de 100644
--- a/drivers/irqchip/irq-renesas-intc-irqpin.c
+++ b/drivers/irqchip/irq-renesas-intc-irqpin.c
@@ -343,7 +343,6 @@ static int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq,
 	intc_irqpin_dbg(&p->irq[hw], "map");
 	irq_set_chip_data(virq, h->host_data);
 	irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
-	set_irq_flags(virq, IRQF_VALID); /* kill me now */
 	return 0;
 }
 
diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c
index 778bd07..74e980f 100644
--- a/drivers/irqchip/irq-renesas-irqc.c
+++ b/drivers/irqchip/irq-renesas-irqc.c
@@ -162,7 +162,6 @@ static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq,
 	irqc_dbg(&p->irq[hw], "map");
 	irq_set_chip_data(virq, h->host_data);
 	irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
-	set_irq_flags(virq, IRQF_VALID); /* kill me now */
 	return 0;
 }
 
diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c
index e96717f..19d1fc4 100644
--- a/drivers/irqchip/irq-s3c24xx.c
+++ b/drivers/irqchip/irq-s3c24xx.c
@@ -469,13 +469,11 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq,
 
 	irq_set_chip_data(virq, irq_data);
 
-	set_irq_flags(virq, IRQF_VALID);
-
 	if (parent_intc && irq_data->type != S3C_IRQTYPE_NONE) {
 		if (irq_data->parent_irq > 31) {
 			pr_err("irq-s3c24xx: parent irq %lu is out of range\n",
 			       irq_data->parent_irq);
-			goto err;
+			return -EINVAL;
 		}
 
 		parent_irq_data = &parent_intc->irqs[irq_data->parent_irq];
@@ -488,18 +486,12 @@ static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq,
 		if (!irqno) {
 			pr_err("irq-s3c24xx: could not find mapping for parent irq %lu\n",
 			       irq_data->parent_irq);
-			goto err;
+			return -EINVAL;
 		}
 		irq_set_chained_handler(irqno, s3c_irq_demux);
 	}
 
 	return 0;
-
-err:
-	set_irq_flags(virq, 0);
-
-	/* the only error can result from bad mapping data*/
-	return -EINVAL;
 }
 
 static const struct irq_domain_ops s3c24xx_irq_ops = {
@@ -1177,8 +1169,6 @@ static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq,
 
 	irq_set_chip_data(virq, irq_data);
 
-	set_irq_flags(virq, IRQF_VALID);
-
 	return 0;
 }
 
diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c
index 83d6aa6..f1f401c 100644
--- a/drivers/irqchip/irq-sun4i.c
+++ b/drivers/irqchip/irq-sun4i.c
@@ -84,7 +84,7 @@ static int sun4i_irq_map(struct irq_domain *d, unsigned int virq,
 			 irq_hw_number_t hw)
 {
 	irq_set_chip_and_handler(virq, &sun4i_irq_chip, handle_fasteoi_irq);
-	set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
+	irq_set_probe(virq);
 
 	return 0;
 }
diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c
index 888111b..e96b4fe 100644
--- a/drivers/irqchip/irq-versatile-fpga.c
+++ b/drivers/irqchip/irq-versatile-fpga.c
@@ -128,7 +128,7 @@ static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq,
 	irq_set_chip_data(irq, f);
 	irq_set_chip_and_handler(irq, &f->chip,
 				handle_level_irq);
-	set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	irq_set_probe(irq);
 	return 0;
 }
 
diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c
index d4ce331..401561c 100644
--- a/drivers/irqchip/irq-vic.c
+++ b/drivers/irqchip/irq-vic.c
@@ -202,7 +202,7 @@ static int vic_irqdomain_map(struct irq_domain *d, unsigned int irq,
 		return -EPERM;
 	irq_set_chip_and_handler(irq, &vic_chip, handle_level_irq);
 	irq_set_chip_data(irq, v->base);
-	set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	irq_set_probe(irq);
 	return 0;
 }
 
diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c
index 0b29700..8b235c9 100644
--- a/drivers/irqchip/irq-vt8500.c
+++ b/drivers/irqchip/irq-vt8500.c
@@ -168,7 +168,6 @@ static int vt8500_irq_map(struct irq_domain *h, unsigned int virq,
 							irq_hw_number_t hw)
 {
 	irq_set_chip_and_handler(virq, &vt8500_irq_chip, handle_level_irq);
-	set_irq_flags(virq, IRQF_VALID);
 
 	return 0;
 }
diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c
index acb721b..e2b4e99 100644
--- a/drivers/irqchip/spear-shirq.c
+++ b/drivers/irqchip/spear-shirq.c
@@ -212,7 +212,6 @@ static void __init spear_shirq_register(struct spear_shirq *shirq,
 	for (i = 0; i < shirq->nr_irqs; i++) {
 		irq_set_chip_and_handler(shirq->virq_base + i,
 					 shirq->irq_chip, handle_simple_irq);
-		set_irq_flags(shirq->virq_base + i, IRQF_VALID);
 		irq_set_chip_data(shirq->virq_base + i, shirq);
 	}
 }
-- 
2.1.4



