Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 2/2] drivers: cci: add missing CCI port availability firmware check
From: Lorenzo Pieralisi @ 2016-09-23 13:09 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160923130907.4187-1-lorenzo.pieralisi@arm.com>

The CCI ports programming interface is available to the kernel
only when booted in secure mode (or when firmware enables
non-secure access to override CCI ports control). In both cases,
firmware reports the CCI ports availability through the device
tree CCI ports nodes, which must be parsed and their status checked
by the kernel probing path.

This check is currently missing and may cause the kernel to
erroneously believe it is free to take control of CCI ports
where in practice CCI ports control is forbidden.

Add the missing CCI port availability check to the CCI driver
in order to guarantee sane CCI usage.

Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Liviu Dudau <liviu.dudau@arm.com>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Nicolas Pitre <nicolas.pitre@linaro.org>
Cc: Suzuki K Poulose <suzuki.poulose@arm.com>
Cc: Marc Zyngier <marc.zyngier@arm.com>
---
 drivers/bus/arm-cci.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c
index 5755907f..3c7a1c7 100644
--- a/drivers/bus/arm-cci.c
+++ b/drivers/bus/arm-cci.c
@@ -2199,6 +2199,9 @@ static int cci_probe_ports(struct device_node *np)
 		if (!of_match_node(arm_cci_ctrl_if_matches, cp))
 			continue;
 
+		if (!of_device_is_available(cp))
+			continue;
+
 		i = nb_ace + nb_ace_lite;
 
 		if (i >= nb_cci_ports)
@@ -2241,6 +2244,13 @@ static int cci_probe_ports(struct device_node *np)
 		ports[i].dn = cp;
 	}
 
+	/*
+	 * If there is no CCI port that is under kernel control
+	 * return early and report probe status.
+	 */
+	if (!nb_ace && !nb_ace_lite)
+		return -ENODEV;
+
 	 /* initialize a stashed array of ACE ports to speed-up look-up */
 	cci_ace_init_ports();
 
-- 
2.10.0

^ permalink raw reply related

* [PATCH 1/2] ARM: vexpress: refine MCPM smp operations override criteria
From: Lorenzo Pieralisi @ 2016-09-23 13:09 UTC (permalink / raw)
  To: linux-arm-kernel

Current vexpress smp init code detects whether to override the
default smp ops with MCPM smp ops by matching the "cci-400"
compatible string, in that MCPM requires control over CCI ports
to manage low-power states entry/exit.

The "cci-400" compatible string check is a necessary but not
sufficient condition for MCPM to work, because the cci-400
can be made visible to the kernel, but firmware can nonetheless
disable non-secure CCI ports control, while still allowing PMU
access; if booted in non-secure world, the kernel would still
blindly override smp operations with MCPM operations, resulting
in kernel faults when the CCI ports programming interface is
accessed from non-secure world.

This means that the "cci-400" compatible string check would
result in a false positive in systems that eg boot in HYP mode,
where CCI ports non-secure access is explicitly not allowed,
and it is reported in the respective device tree nodes with
CCI ports marked as disabled.

Refactor the smp operations initialization to make sure that
the kernel is actually allowed to take control over CCI ports
(by enabling MCPM smp operations) before overriding default
vexpress smp operations.

Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Liviu Dudau <liviu.dudau@arm.com>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Nicolas Pitre <nicolas.pitre@linaro.org>
Cc: Marc Zyngier <marc.zyngier@arm.com>
---
 arch/arm/mach-vexpress/platsmp.c | 31 ++++++++++++++++++++++++-------
 1 file changed, 24 insertions(+), 7 deletions(-)

diff --git a/arch/arm/mach-vexpress/platsmp.c b/arch/arm/mach-vexpress/platsmp.c
index 8b8d072..6cfd782 100644
--- a/arch/arm/mach-vexpress/platsmp.c
+++ b/arch/arm/mach-vexpress/platsmp.c
@@ -26,17 +26,34 @@
 bool __init vexpress_smp_init_ops(void)
 {
 #ifdef CONFIG_MCPM
+	int cpu;
+	struct device_node *cpu_node, *cci_node;
+
 	/*
-	 * The best way to detect a multi-cluster configuration at the moment
-	 * is to look for the presence of a CCI in the system.
+	 * The best way to detect a multi-cluster configuration
+	 * is to detect if the kernel can take over CCI ports
+	 * control. Loop over possible CPUs and check if CCI
+	 * port control is available.
 	 * Override the default vexpress_smp_ops if so.
 	 */
-	struct device_node *node;
-	node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
-	if (node && of_device_is_available(node)) {
-		mcpm_smp_set_ops();
-		return true;
+	for_each_possible_cpu(cpu) {
+		bool available;
+
+		cpu_node = of_get_cpu_node(cpu, NULL);
+		if (WARN(!cpu_node, "Missing cpu device node!"))
+			return false;
+
+		cci_node = of_parse_phandle(cpu_node, "cci-control-port", 0);
+		available = cci_node && of_device_is_available(cci_node);
+		of_node_put(cci_node);
+		of_node_put(cpu_node);
+
+		if (!available)
+			return false;
 	}
+
+	mcpm_smp_set_ops();
+	return true;
 #endif
 	return false;
 }
-- 
2.10.0

^ permalink raw reply related

* [GIT PULL] arm64 fixes for 4.8
From: Catalin Marinas @ 2016-09-23 13:08 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Linus,

Please pull a couple of last-minute arm64 fixes for 4.8. Thanks.

The following changes since commit 3be7988674ab33565700a37b210f502563d932e6:

  Linux 4.8-rc7 (2016-09-18 17:27:41 -0700)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux tags/arm64-fixes

for you to fetch changes up to 67787b68ec48c239d5ec12f9bf5adaf5c459517a:

  arm64: kgdb: handle read-only text / modules (2016-09-23 11:25:01 +0100)

----------------------------------------------------------------
- Fix secondary CPU to NUMA node assignment

- Fix kgdb breakpoint insertion in read-only text sections (when
  CONFIG_DEBUG_RODATA or CONFIG_DEBUG_SET_MODULE_RONX are enabled)

----------------------------------------------------------------
AKASHI Takahiro (1):
      arm64: kgdb: handle read-only text / modules

David Daney (1):
      arm64: Call numa_store_cpu_info() earlier.

 arch/arm64/include/asm/debug-monitors.h |  2 --
 arch/arm64/kernel/kgdb.c                | 36 ++++++++++++++++++++++-----------
 arch/arm64/kernel/smp.c                 | 14 ++++++-------
 3 files changed, 30 insertions(+), 22 deletions(-)

-- 
Catalin

^ permalink raw reply

* [PATCH 1/3] dt-bindings: Add a binding for the RPi firmware GPIO driver.
From: Eric Anholt @ 2016-09-23 13:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CACRpkdb-hRjD+FmyZ4SKurp94V=HR3L90OLNc-6eUKuy9AK_Rg@mail.gmail.com>

Linus Walleij <linus.walleij@linaro.org> writes:

> On Mon, Sep 19, 2016 at 6:13 PM, Eric Anholt <eric@anholt.net> wrote:
>
>> The RPi firmware exposes all of the board's GPIO lines through
>> property calls.  Linux chooses to control most lines directly through
>> the pinctrl driver, but for the FXL6408 GPIO expander on the Pi3, we
>> need to access them through the firmware.
>>
>> Signed-off-by: Eric Anholt <eric@anholt.net>
>
> Aha
>
>> +++ b/Documentation/devicetree/bindings/gpio/gpio-raspberrypi-firmware.txt
>> @@ -0,0 +1,22 @@
>> +Raspberry Pi power domain driver
>
> Really? :)

Thanks.

>> +Required properties:
>> +
>> +- compatible:          Should be "raspberrypi,firmware-gpio"
>
> Usually this is vendor,compat, is the vendors name "raspberrypi"?

Yes, this driver is for part of the Raspberry Pi Foundation's firmware
code (you can find the same pattern in the firmware and firmware power
domain drivers).

>> +- gpio-controller:     Marks the device node as a gpio controller
>> +- #gpio-cells:         Should be <2> for GPIO number and flags
>> +- ngpios:              Number of GPIO lines to control.  See gpio.txt
>
> Is this ever anything else than 8? Else omit it and hardcode
> 8 in the driver instead.

(see below)

>> +- firmware:            Reference to the RPi firmware device node
>
> Reference the DT binding for this.
>
>> +- raspberrypi,firmware-gpio-offset:
>> +                       Number the firmware uses for the first GPIO line
>> +                         controlled by this driver
>
> Does this differ between different instances of this hardware or
> can it just be open coded in the driver instead?

This is which range (128-135) of the firmware's GPIOs we're controlling.
If another GPIO expander appears later (quite believable, I think
they're down to 1 spare line on this expander), then we would just make
another node with a new offset and ngpios for that expander.

Sort of related: I also worry that we have races with the firmware for
the platform GPIO bits, since both ARM and firmware are doing RMWs (or,
even worse, maybe just Ws?) of the registers controlled by the pinctrl
driver.  Hopefully I can get the firmware to pass control of devices
like this over to Linux, with firmware making requests to us, but I
don't know if that will happen and we may need to access other GPIOs
using this interface :(
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 800 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160923/2ef4c585/attachment.sig>

^ permalink raw reply

* [PATCH v5 3/3] pci:aer: add support aer interrupt with none MSI/MSI-X/INTx mode
From: Rob Herring @ 2016-09-23 13:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <VI1PR0401MB1709F91B0C1EB6C80D741E4492F50@VI1PR0401MB1709.eurprd04.prod.outlook.com>

On Sun, Sep 18, 2016 at 03:37:27AM +0000, Po Liu wrote:
> Hi Shawn,
> 
> 
> >  -----Original Message-----
> >  From: Shawn Guo [mailto:shawnguo at kernel.org]
> >  Sent: Sunday, September 18, 2016 8:52 AM
> >  To: Po Liu
> >  Cc: linux-pci at vger.kernel.org; linux-arm-kernel at lists.infradead.org;
> >  linux-kernel at vger.kernel.org; devicetree at vger.kernel.org; Roy Zang; Arnd
> >  Bergmann; Marc Zyngier; Stuart Yoder; Leo Li; M.H. Lian; Murali
> >  Karicheri; Bjorn Helgaas; Mingkai Hu
> >  Subject: Re: [PATCH v5 3/3] pci:aer: add support aer interrupt with none
> >  MSI/MSI-X/INTx mode
> >  
> >  On Tue, Sep 13, 2016 at 12:40:59PM +0800, Po Liu wrote:
> >  > On some platforms, root port doesn't support MSI/MSI-X/INTx in RC mode.
> >  > When chip support the aer interrupt with none MSI/MSI-X/INTx mode,
> >  > maybe there is interrupt line for aer pme etc. Search the interrupt
> >  > number in the fdt file. Then fixup the dev->irq with it.
> >  >
> >  > Signed-off-by: Po Liu <po.liu@nxp.com>
> >  
> >  Will the new kernel work with existing/old DTB?  I'm trying to
> >  understand the dependency between driver and DTS changes.
> 
> Yes, We've never use name 'intr' before. So we remove it is ok. 
> 'aer' is a dts name for researching it's true interrupt number by 
> kernel. This patch is first time to use name 'aer'. So it must be 
> compatible with existing/old DTB.

Please explain why you are not breaking compatibility in the commit 
message. I asked for this on v2.

> >  > ---
> >  > changes for v5:
> >  > 	- Add clear 'aer' interrup-names description
> >  >
> >  >  .../devicetree/bindings/pci/layerscape-pci.txt     | 11 +++++---
> >  >  drivers/pci/pcie/portdrv_core.c                    | 31
> >  +++++++++++++++++++---
> >  >  2 files changed, 35 insertions(+), 7 deletions(-)
> >  >
> >  > diff --git a/Documentation/devicetree/bindings/pci/layerscape-pci.txt
> >  > b/Documentation/devicetree/bindings/pci/layerscape-pci.txt
> >  > index 41e9f55..101d0a7 100644
> >  > --- a/Documentation/devicetree/bindings/pci/layerscape-pci.txt
> >  > +++ b/Documentation/devicetree/bindings/pci/layerscape-pci.txt
> >  > @@ -18,8 +18,10 @@ Required properties:
> >  >  - reg: base addresses and lengths of the PCIe controller
> >  >  - interrupts: A list of interrupt outputs of the controller. Must
> >  contain an
> >  >    entry for each entry in the interrupt-names property.
> >  > -- interrupt-names: Must include the following entries:
> >  > -  "intr": The interrupt that is asserted for controller interrupts
> >  > +- interrupt-names: It may be include the following entries:

"may be" is not okay. It should be "must" or explain when an interrupt 
would not be present. Really, differences in interrupts means you need 
different compatible strings.

Rob

> >  > +  "aer": The interrupt that is asserted for aer interrupt
> >  > +  "pme": The interrupt that is asserted for pme interrupt
> >  > +  ......

^ permalink raw reply

* [PATCH 5/5] arm64: Add uprobe support
From: Catalin Marinas @ 2016-09-23 13:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160923041230.GC29470@localhost.localdomain>

On Fri, Sep 23, 2016 at 09:42:30AM +0530, Pratyush Anand wrote:
> On 22/09/2016:05:50:30 PM, Catalin Marinas wrote:
> > On Thu, Sep 22, 2016 at 08:53:28AM +0530, Pratyush Anand wrote:
> > > On 21/09/2016:06:04:04 PM, Catalin Marinas wrote:
> > > > On Wed, Sep 21, 2016 at 04:30:47PM +0530, Pratyush Anand wrote:
> > > > > On 20/09/2016:05:59:46 PM, Catalin Marinas wrote:
> > > > > > > +int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
> > > > > > > +		unsigned long addr)
> > > > > > > +{
> > > > > > > +	probe_opcode_t insn;
> > > > > > > +
> > > > > > > +	/* TODO: Currently we do not support AARCH32 instruction probing */
> > > > > > 
> > > > > > Is there a way to check (not necessarily in this file) that we don't
> > > > > > probe 32-bit tasks?
> > > > > 
> > > > > - Well, I do not have complete idea about it that, how it can be done. I think
> > > > >   we can not check that just by looking a single bit in an instruction.
> > > > >   My understanding is that, we can only know about it when we are executing the
> > > > >   instruction, by reading pstate, but that would not be useful for uprobe
> > > > >   instruction analysis.
> > > > >   
> > > > >   I hope, instruction encoding for aarch32 and aarch64 are different, and by
> > > > >   analyzing for all types of aarch32 instructions, we will be able to decide
> > > > >   that whether instruction is 32 bit trace-able or not.  Accordingly, we can use
> > > > >   either BRK or BKPT instruction for breakpoint generation.
> > > > 
> > > > We may have some unrelated instruction encoding overlapping but I
> > > > haven't checked. I was more thinking about whether we know which task is
> > > > being probed and check is_compat_task() or maybe using
> > > > compat_user_mode(regs).
> > > 
> > > I had thought of this, but problem is that we might not have task in existence
> > > when we enable uprobes.  For example: Lets say we are inserting a trace probe at
> > > offset 0x690 in a executable binary.
> > > 
> > > echo "p test:0x690" > /sys/kernel/debug/tracing/uprobe_events
> > > echo 1 > /sys/kernel/debug/tracing/events/uprobes/enable
> > > 
> > > In the 'enable' step, it is decided that whether instruction is traceable or
> > > not. 
> > > 
> > > (1) But at this point 'test' executable might not be running.
> 
> Let me correct myself first here. When executable is not running, then,
> arch_uprobe_analyze_insn() is not called while uprobes enabling (ie writing '1'
> to 'enable'). In that case, it is called when binary is executed and task is
> created.

I kind of figured this out. This function needs to read the actual
instruction from memory, so that won't be possible until at least the
executable file has an address_space in the kernel. What's not clear to
me is how early this is done, do we have the mm_struct fully populated?

> > > (2) Even if it is running, is_compat_task() or compat_user_mode() might not be
> > > usable, as they work with 'current' task.
> > 
> > What I find strange is that uprobes allows you to insert a breakpoint
> > instruction that's not even compatible with the task (so it would
> > SIGILL rather than generate a debug exception).
> > 
> > > What I was thinking that, let it go with 'TODO' as of now. 
> > 
> > Only that I don't have any guarantee that someone is going to fix it ;).
> > 
> > As a quick workaround you could check mm->task_size > TASK_SIZE_32 in
> > the arch_uprobe_analyze_insn() function.
> 
> It would be doable. TASK_SIZE_32 is defined only for COMPAT. So, may be I can
> return -EINVAL when mm->task_size < TASK_SIZE_64.

That's just a temporary workaround. If we ever merge ILP32, this test
would no longer be enough (as the ISA is AArch64 but with TASK_SIZE_32).

Looking@prepare_uprobe(), we have a weak is_trap_insn() function.
This check is meaningless without knowing which instruction set we
target. A false positive here, however, is not that bad as we wouldn't
end up inserting the wrong breakpoint in the executable. But it looks to
me like the core uprobe code needs to pass some additional information
like the type of task or ELF format to the arch code to make a useful
choice of breakpoint type.

-- 
Catalin

^ permalink raw reply

* [PATCH v5 1/3] ASoC: sun4i-codec: Rename sun4i_codec_widgets for consistency
From: Maxime Ripard @ 2016-09-23 13:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160922071313.12891-2-dannym@scratchpost.org>

Hi,

On Thu, Sep 22, 2016 at 09:13:11AM +0200, Danny Milosavljevic wrote:
> ASoC: sun4i-codec: Rename "sun4i_codec_widgets" to "sun4i_codec_controls" for
> consistency with the struct field name.

Your commit log still has the flaws your other patches had in the
previous version.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.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-arm-kernel/attachments/20160923/3e8272c4/attachment.sig>

^ permalink raw reply

* [PATCH v6] KVM: arm/arm64: Route vtimer events to user space
From: Paolo Bonzini @ 2016-09-23 12:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <D82BA95A-A89E-46EB-93D2-197DF44433EC@suse.de>



On 23/09/2016 14:36, Alexander Graf wrote:
>>>
>>> +        if ((ret == 1) && timer_ret) {
>>> +            /*
>>> +             * We have to exit straight away to ensure that we only
>>> +             * ever notify user space once about a level change
>>> +             */
>>
>> Is this really a requirement?  It complicates the logic noticeably.
> 
> The alternative would be to track the state on ioctl entry, which
> Christoffer disliked :).

Ok, I see.  Maybe he's convinced by the ugly code. :)

It's not an architectural concept, it's a KVM API concept.  Tracking the
state on entry just the equivalent of making this vmexit edge-triggered
(both rising and falling edge).

Paolo

^ permalink raw reply

* [PATCH v6] KVM: arm/arm64: Route vtimer events to user space
From: Christoffer Dall @ 2016-09-23 12:40 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <dd9267b6-2323-2d74-0553-71a89d05a654@redhat.com>

On Fri, Sep 23, 2016 at 02:11:41PM +0200, Paolo Bonzini wrote:
> 
> 
> On 23/09/2016 13:07, Alexander Graf wrote:
> > +		timer_ret = kvm_timer_sync_hwstate(vcpu);
> >  
> >  		kvm_vgic_sync_hwstate(vcpu);
> >  
> >  		preempt_enable();
> >  
> >  		ret = handle_exit(vcpu, run, ret);
> > +
> > +		if ((ret == 1) && timer_ret) {
> > +			/*
> > +			 * We have to exit straight away to ensure that we only
> > +			 * ever notify user space once about a level change
> > +			 */
> 
> Is this really a requirement?  It complicates the logic noticeably.
> 

If we skip this, then we have to somehow remember that the sync may have
updated the line level when we reach the flush state (and didn't exit),
and I would like maintaining that state even less than doing this check.

What we could do is to compare the timer->irq.level with the kvm_run
state outside of the run loop (on the way to userspace) and update the
kvm_run state if there's a discrepancy.  That way, we can maintain the
'timer->irq.level != kvm_run' means, we have to exit, and whenever we
are exiting, we are reporting the most recent state to user space
anyway.

Would that work?

(By the way, the check should be via a call into the arch timer code,
kvm_timer_update_user_state() or something like that).

Also, the comment above is confusing, because that's not why this check
is here, the check is here so that we don't loose track of the timer
output level change if there's no exit to userspace.

Thanks,
-Christoffer

^ permalink raw reply

* [PATCH V11 0/8] Introduce ACPI world to ITS irqchip
From: Auger Eric @ 2016-09-23 12:30 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1473705147-6588-1-git-send-email-tn@semihalf.com>

Hi,

On 12/09/2016 20:32, Tomasz Nowicki wrote:
> The series builds the PCI/MSI domain stack based on initial IORT driver
> which is added in first place. As a reference please see IORT spec:
> http://infocenter.arm.com/help/topic/com.arm.doc.den0049b/DEN0049B_IO_Remapping_Table.pdf
> 
> Tested on Cavium ThunderX server and Qualcomm Technologies server platform QDF2XXX.
> The patches can be found here:
> https://github.com/semihalf-nowicki-tomasz/linux.git (its-acpi-v11)

Tested-by: Eric Auger <eric.auger@redhat.com>
on a Cavium QEMU guest with mach-virt.

Thanks

Eric

> 
> v10 -> v11
> - rebased against v4.8-rc6
> - bug fix for iort_match_node_callback() error handling
> - patch 1-2 changelog updates
> 
> v9 -> v10
> - error handling improvements for iort_match_node_callback() but no functional changes
> - iort.h -> acpi_iort.h header name change
> - style improvements
> 
> v8 -> v9
> - rebased against v4.8-rc4
> - move root IORT pointer validation to one place iort_scan_node()
> - function names & Kconfig option name improvements
> - remove annoying dev_err() appearances for non-IORT users
> - for debug purpose print full ITS resource range
> 
> v7 -> v8
> - rebased against v4.8-rc1
> - move IORT support under new drivers/acpi/arm64 directory
> - memory leak fix for iort_match_node_callback()
> - iort_node_map_rid() algorithm improvements
> - fix RID match check in iort_id_map()
> - fix typos and white spaces
> 
> v6 -> v7
> - rebased against irq/irqchip-4.8
> - improvements for IORT ID mapping
> - call IORT init call from acpi_init() instead of arch_initcall()
> - split IORT driver in to two parts: core and ITS/MSI
> 
> v5 -> v6
> - added locking mechanism for IORT list with domain token
> - IORT function name improvements
> - extended IORT RID mapping helper
> - reworked IORT to be more SMMU friendly
> - IORT functions which map RID and find corresponding domain are not
>   PCI specific any more (struct pci_dev -> struct device)
> - bug fixes
> 
> v4 -> v5
> - rebased against v4.7-rc1
> - drop generic layer and call IORT functions directly
> - improve resource abstraction and string formatting
> 
> v3 -> v4
> - rebased against v4.5
> - add ACPI support for IRQ domain handling on a per-device basis
> - reorder domain setup step
> - improve error handling
> - code style improvements
> 
> v2 -> v3
> - rebased on top of 4.4
> - fixes and improvements for redistributor init via GICC structures
> - fixes as per kbuild reports
> 
> v1 -> v2
> - rebased on top of 4.4-rc4
> - use pci_msi_domain_get_msi_rid for requester ID to device ID translation
> 
> Tomasz Nowicki (8):
>   ACPI: I/O Remapping Table (IORT) initial support
>   ACPI: Add new IORT functions to support MSI domain handling
>   PCI/MSI: Setup MSI domain on a per-device basis using IORT ACPI table
>   irqchip/gicv3-its: Cleanup for ITS domain initialization
>   irqchip/gicv3-its: Refactor ITS DT init code to prepare for ACPI
>   irqchip/gicv3-its: Probe ITS in the ACPI way
>   irqchip/gicv3-its: Factor out PCI-MSI part that might be reused for
>     ACPI
>   irqchip/gicv3-its: Use MADT ITS subtable to do PCI/MSI domain
>     initialization
> 
>  drivers/acpi/Kconfig                     |   4 +
>  drivers/acpi/Makefile                    |   2 +
>  drivers/acpi/arm64/Kconfig               |   6 +
>  drivers/acpi/arm64/Makefile              |   1 +
>  drivers/acpi/arm64/iort.c                | 366 +++++++++++++++++++++++++++++++
>  drivers/acpi/bus.c                       |   2 +
>  drivers/irqchip/Kconfig                  |   1 +
>  drivers/irqchip/irq-gic-v3-its-pci-msi.c |  88 ++++++--
>  drivers/irqchip/irq-gic-v3-its.c         | 171 +++++++++++----
>  drivers/irqchip/irq-gic-v3.c             |   7 +-
>  drivers/pci/msi.c                        |  11 +-
>  include/linux/acpi_iort.h                |  42 ++++
>  include/linux/irqchip/arm-gic-v3.h       |   4 +-
>  13 files changed, 635 insertions(+), 70 deletions(-)
>  create mode 100644 drivers/acpi/arm64/Kconfig
>  create mode 100644 drivers/acpi/arm64/Makefile
>  create mode 100644 drivers/acpi/arm64/iort.c
>  create mode 100644 include/linux/acpi_iort.h
> 

^ permalink raw reply

* [PATCH 2/2] armv8: aarch32: Add SMP support for 32-bit Linux kernel
From: Mark Rutland @ 2016-09-23 12:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474597146-33312-2-git-send-email-b18965@freescale.com>

On Fri, Sep 23, 2016 at 10:19:06AM +0800, Alison Wang wrote:
> The patch adds SMP support for running 32-bit Linux kernel for
> Layerscape platforms. Spin-table method is used for SMP support.

So far, spin-table has not been defined for 32-bit, and the code below,
while mostly a copy of the 64-bit code, is somewhat different.

If you want a common enable method, I would strongly advise that you use
PSCI, which is well-defined for both 32-bit and 64-bit. There are a
number of problems with spin-table, and I would not recommend extending
it to 32-bit ARM.

Thanks,
Mark.

^ permalink raw reply

* [PATCH net-next] drivers: net: xgene: Fix MSS programming
From: David Miller @ 2016-09-23 12:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474584453-9071-1-git-send-email-isubramanian@apm.com>

From: Iyappan Subramanian <isubramanian@apm.com>
Date: Thu, 22 Sep 2016 15:47:33 -0700

> Current driver programs static value of MSS in hardware register for TSO
> offload engine to segment the TCP payload regardless the MSS value
> provided by network stack.
> 
> This patch fixes this by programming hardware registers with the
> stack provided MSS value.
> 
> Since the hardware has the limitation of having only 4 MSS registers,
> this patch uses reference count of mss values being used.
> 
> Signed-off-by: Iyappan Subramanian <isubramanian@apm.com>
> Signed-off-by: Toan Le <toanle@apm.com>

Applied.

^ permalink raw reply

* [PATCH 1/2] armv8: aarch32: Execute 32-bit Linux for LayerScape platforms
From: Robin Murphy @ 2016-09-23 12:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474597146-33312-1-git-send-email-b18965@freescale.com>

Hi Alison,

On 23/09/16 03:19, Alison Wang wrote:
> The ARMv8 architecture supports:
> 1. 64-bit execution state, AArch64.
> 2. 32-bit execution state, AArch32, that is compatible with previous
> versions of the ARM architecture.
> 
> LayerScape platforms are compliant with ARMv8 architecture. This patch
> is to support running 32-bit Linux kernel for LayerScape platforms.
> 
> Verified on LayerScape LS1043ARDB, LS1012ARDB, LS1046ARDB boards.
> 
> Signed-off-by: Ebony Zhu <ebony.zhu@nxp.com>
> Signed-off-by: Alison Wang <alison.wang@nxp.com>
> ---
>  arch/arm/Kconfig                    |  9 +++++++++
>  arch/arm/mach-imx/Kconfig           | 14 ++++++++++++++
>  arch/arm/mach-imx/Makefile          |  4 +++-
>  arch/arm/mach-imx/mach-layerscape.c | 23 +++++++++++++++++++++++
>  4 files changed, 49 insertions(+), 1 deletion(-)
>  create mode 100644 arch/arm/mach-imx/mach-layerscape.c
> 
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index f0c8068..e8d470e 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -294,6 +294,15 @@ config PGTABLE_LEVELS
>  	default 3 if ARM_LPAE
>  	default 2
>  
> +config ARCH_AARCH32_ES_SUPPORT
> +	def_bool n
> +	help
> +	 The ARMv8 architecture supports 64-bit execution state, AArch64
> +	 and 32-bit execution state, AArch32, that is compatible with
> +	 previous versions of the ARM architecture.
> +
> +	 Enable AArch32 execution state support for ARMv8 architecture.

What's this supposed to do, exactly? I've been running 32-bit kernels on
my Juno with very little issue (beyond a couple of DT tweaks, and some
firmware hacks with a corresponding bit of A64 assembly tacked on the
front of the zImage to switch into AArch32 state).

Robin.

> +
>  source "init/Kconfig"
>  
>  source "kernel/Kconfig.freezer"
> diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
> index 0ac05a0..fda4f5f 100644
> --- a/arch/arm/mach-imx/Kconfig
> +++ b/arch/arm/mach-imx/Kconfig
> @@ -549,6 +549,20 @@ config SOC_LS1021A
>  	help
>  	  This enables support for Freescale LS1021A processor.
>  
> +config ARCH_LAYERSCAPE_AARCH32
> +	bool "Freescale Layerscape SoC AArch32 ES support"
> +	select ARCH_AARCH32_ES_SUPPORT
> +	select ARM_AMBA
> +	select ARM_GIC
> +	select ARM_ARCH_TIMER
> +	select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
> +	select PCI_LAYERSCAPE if PCI
> +	select PCI_DOMAINS if PCI
> +
> +	help
> +	  This enables support for Freescale Layerscape SoC family in
> +	  in AArch32 execution state.
> +
>  endif
>  
>  comment "Cortex-A/Cortex-M asymmetric multiprocessing platforms"
> diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
> index 737450f..7ded4fa 100644
> --- a/arch/arm/mach-imx/Makefile
> +++ b/arch/arm/mach-imx/Makefile
> @@ -69,7 +69,7 @@ obj-$(CONFIG_HAVE_IMX_ANATOP) += anatop.o
>  obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
>  obj-$(CONFIG_HAVE_IMX_MMDC) += mmdc.o
>  obj-$(CONFIG_HAVE_IMX_SRC) += src.o
> -ifneq ($(CONFIG_SOC_IMX6)$(CONFIG_SOC_LS1021A),)
> +ifneq ($(CONFIG_SOC_IMX6)$(CONFIG_SOC_LS1021A)$(CONFIG_ARCH_LAYERSCAPE_AARCH32),)
>  AFLAGS_headsmp.o :=-Wa,-march=armv7-a
>  obj-$(CONFIG_SMP) += headsmp.o platsmp.o
>  obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
> @@ -96,4 +96,6 @@ obj-$(CONFIG_SOC_VF610) += mach-vf610.o
>  
>  obj-$(CONFIG_SOC_LS1021A) += mach-ls1021a.o
>  
> +obj-$(CONFIG_ARCH_LAYERSCAPE_AARCH32) += mach-layerscape.o
> +
>  obj-y += devices/
> diff --git a/arch/arm/mach-imx/mach-layerscape.c b/arch/arm/mach-imx/mach-layerscape.c
> new file mode 100644
> index 0000000..acfb2a2
> --- /dev/null
> +++ b/arch/arm/mach-imx/mach-layerscape.c
> @@ -0,0 +1,23 @@
> +/*
> + * Copyright 2015-2016 Freescale Semiconductor, Inc.
> + *
> + * 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 <asm/mach/arch.h>
> +
> +#include "common.h"
> +
> +static const char * const layerscape_dt_compat[] __initconst = {
> +	"fsl,ls1012a",
> +	"fsl,ls1043a",
> +	"fsl,ls1046a",
> +	NULL,
> +};
> +
> +DT_MACHINE_START(LAYERSCAPE_AARCH32, "Freescale LAYERSCAPE")
> +	.dt_compat	= layerscape_dt_compat,
> +MACHINE_END
> 

^ permalink raw reply

* [PATCH] i2c: uniphier-f: fix misdetection of incomplete STOP condition
From: Wolfram Sang @ 2016-09-23 12:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474632241-10440-1-git-send-email-yamada.masahiro@socionext.com>

On Fri, Sep 23, 2016 at 09:04:01PM +0900, Masahiro Yamada wrote:
> Currently, the status register FI2C_SR is checked immediately after
> a STOP condition is issued in case of the deferred STOP condition.
> It takes typically 5-10 usec until the corresponding bits in the
> register are set, so the error check for "stop condition was not
> completed" is very likely to be false positive.
> 
> Add wait code to relax the status register check.
> 
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>

Use readl_poll_timeout?

-------------- 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-arm-kernel/attachments/20160923/fdca9ba0/attachment.sig>

^ permalink raw reply

* [PATCH 1/2] armv8: aarch32: Execute 32-bit Linux for LayerScape platforms
From: Arnd Bergmann @ 2016-09-23 12:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474597146-33312-1-git-send-email-b18965@freescale.com>

On Friday, September 23, 2016 10:19:05 AM CEST Alison Wang wrote:
> The ARMv8 architecture supports:
> 1. 64-bit execution state, AArch64.
> 2. 32-bit execution state, AArch32, that is compatible with previous
> versions of the ARM architecture.
> 
> LayerScape platforms are compliant with ARMv8 architecture. This patch
> is to support running 32-bit Linux kernel for LayerScape platforms.
> 
> Verified on LayerScape LS1043ARDB, LS1012ARDB, LS1046ARDB boards.
> 
> Signed-off-by: Ebony Zhu <ebony.zhu@nxp.com>
> Signed-off-by: Alison Wang <alison.wang@nxp.com>

I'm not too happy about adding random 64-bit platforms in arch/arm/.
We have done this twice already (ARCH_VIRT and ARCH_BCM2835), which
are both platforms that can come with ARMv6, ARMv7 or ARMv8 cores
and are otherwise almost identical. However, there are two problem
I see with the general approach:

a) We don't actually support ARMv8 as a target architecture, in fact
   we don't even support ARMv7VE (Cortex-A7/A12/A15/A17, Krait-400,
   PJ4B-MP and Brahma-B15) properly. For an ARMv8-only kernel
   we should pass the correct compiler flags. We should also have
   this for any upcoming Cortex-A32 and Cortex-R52 platforms.

b) Generally speaking it's a bad idea to run an platform that
   supports aarch64 in aarch32 mode, you should use a native kernel
   with syscall emulation for 32-bit user space unless you have a
   very strong reason not to do that. If you have a strong reason
   why your platform is different from all the others, please at
   least explain it in the changelog.
   On the other hand, any platform support both aarch64 and aarch32
   mode should in theory run with a 32-bit kernel, and it might be
   nice to allow that for *all* platforms that we support on a 64-bit
   kernel. If we decide to do this, we should come up with a way to
   include the platform configuration from arch/arm64 in the arm
   Kconfig file.

	Arnd

^ permalink raw reply

* [PATCH v6] KVM: arm/arm64: Route vtimer events to user space
From: Paolo Bonzini @ 2016-09-23 12:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474628854-69945-1-git-send-email-agraf@suse.de>



On 23/09/2016 13:07, Alexander Graf wrote:
> +		timer_ret = kvm_timer_sync_hwstate(vcpu);
>  
>  		kvm_vgic_sync_hwstate(vcpu);
>  
>  		preempt_enable();
>  
>  		ret = handle_exit(vcpu, run, ret);
> +
> +		if ((ret == 1) && timer_ret) {
> +			/*
> +			 * We have to exit straight away to ensure that we only
> +			 * ever notify user space once about a level change
> +			 */

Is this really a requirement?  It complicates the logic noticeably.

Paolo

> +			ret = -EINTR;
> +			run->exit_reason = KVM_EXIT_INTR;
> +		}
>  	}

^ permalink raw reply

* [PATCH] i2c: uniphier-f: fix misdetection of incomplete STOP condition
From: Masahiro Yamada @ 2016-09-23 12:04 UTC (permalink / raw)
  To: linux-arm-kernel

Currently, the status register FI2C_SR is checked immediately after
a STOP condition is issued in case of the deferred STOP condition.
It takes typically 5-10 usec until the corresponding bits in the
register are set, so the error check for "stop condition was not
completed" is very likely to be false positive.

Add wait code to relax the status register check.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

 drivers/i2c/busses/i2c-uniphier-f.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/i2c/busses/i2c-uniphier-f.c b/drivers/i2c/busses/i2c-uniphier-f.c
index 829df91..e5886eb 100644
--- a/drivers/i2c/busses/i2c-uniphier-f.c
+++ b/drivers/i2c/busses/i2c-uniphier-f.c
@@ -13,6 +13,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
@@ -348,10 +349,19 @@ static int uniphier_fi2c_master_xfer_one(struct i2c_adapter *adap,
 	dev_dbg(&adap->dev, "complete\n");
 
 	if (unlikely(priv->flags & UNIPHIER_FI2C_DEFER_STOP_COMP)) {
-		u32 status = readl(priv->membase + UNIPHIER_FI2C_SR);
+		time_left = 20;
 
-		if (!(status & UNIPHIER_FI2C_SR_STS) ||
-		    status & UNIPHIER_FI2C_SR_BB) {
+		while (--time_left) {
+			u32 status = readl(priv->membase + UNIPHIER_FI2C_SR);
+
+			if ((status & UNIPHIER_FI2C_SR_STS) &&
+			    !(status & UNIPHIER_FI2C_SR_BB))
+				break;
+
+			udelay(1);
+		}
+
+		if (!time_left) {
 			dev_err(&adap->dev,
 				"stop condition was not completed.\n");
 			uniphier_fi2c_recover(priv);
-- 
1.9.1

^ permalink raw reply related

* [PATCH 2/2] armv8: aarch32: Add SMP support for 32-bit Linux kernel
From: Arnd Bergmann @ 2016-09-23 11:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474597146-33312-2-git-send-email-b18965@freescale.com>

On Friday, September 23, 2016 10:19:06 AM CEST Alison Wang wrote:
> The patch adds SMP support for running 32-bit Linux kernel for
> Layerscape platforms. Spin-table method is used for SMP support.
> 
> Signed-off-by: Alison Wang <alison.wang@nxp.com>
> Signed-off-by: Chenhui Zhao <chenhui.zhao@nxp.com>
> ---
>  arch/arm/mach-imx/common.h          |  1 +
>  arch/arm/mach-imx/mach-layerscape.c |  1 +
>  arch/arm/mach-imx/platsmp.c         | 49 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 51 insertions(+)
> 

This really shouldn't be needed: if it works on a 64-bit kernel
without platform specific SMP operations, why do you need it on 32-bit
kernels?

	Arnd

^ permalink raw reply

* [PATCH V3 RFT 3/5] ARM64: dts: bcm283x: Use dtsi for USB host mode
From: Eric Anholt @ 2016-09-23 11:55 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4cda98b0-03a6-f43d-0eee-29b7702eac8c@i2se.com>

Stefan Wahren <stefan.wahren@i2se.com> writes:

> Am 23.09.2016 um 10:15 schrieb Eric Anholt:
>> Stefan Wahren <stefan.wahren@i2se.com> writes:
>>
>>> Am 07.09.2016 um 16:17 schrieb Gerd Hoffmann:
>>>>   Hi,
>>>>
>>>>> ARM: dts: Remove use of skeleton.dtsi from bcm283x.dtsi
>>>>> ARM64: dts: bcm: Use a symlink to R-Pi dtsi files from arch=arm
>>>> Picked them up.
>>>>
>>>>> In case of a multiplatform config with following settings the driver
>>>>> isn't able to "detect" the role:
>>>>>
>>>>> CONFIG_USB=y
>>>>> CONFIG_USB_OTG=y
>>>>> CONFIG_USB_DWC2=y
>>>>> CONFIG_USB_DWC2_DUAL_ROLE=y
>>>>> CONFIG_USB_PHY=y
>>>>> CONFIG_NOP_USB_XCEIV=y
>>>>> CONFIG_USB_GADGET=y
>>>>> CONFIG_USB_ZERO=y
>>>> Configured my kernel that way so the test actually tests something ;)
>>>>
>>>> And ... everything looks fine.  usb ethernet works, and so does the usb
>>>> keyboard.  /proc/device-tree/soc/usb at 7e980000/dr_mode exists and
>>>> contains "host".
>>>>
>>>> Tested-by: Gerd Hoffmann <kraxel@redhat.com>
>>>>
>>> This patch hasn't been merged yet. Is there something wrong?
>> I hadn't bumped it back to todo from RFT.  Also, is this a priority
>> patch, since submitting merges is a pain? 
> No
>> Does USB break in some way
>> without it?
>
> No. I only want know if this is still in queue and does someone care
> about this patch.

It's in my queue now.  Sorry for missing it!
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 800 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160923/03281913/attachment.sig>

^ permalink raw reply

* [PATCH 2/2] drm: zte: add initial vou drm driver
From: Daniel Vetter @ 2016-09-23 11:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474615449-16893-2-git-send-email-shawn.guo@linaro.org>

Please resubmit with dri-devel on cc.

Thanks, Daniel

On Fri, Sep 23, 2016 at 9:24 AM, Shawn Guo <shawn.guo@linaro.org> wrote:
> It adds the initial ZTE VOU display controller DRM driver.  There are
> still some features to be added, like overlay plane, scaling, and more
> output devices support.  But it's already useful with dual CRTCs and
> HDMI monitor working.
>
> It's been tested on Debian Jessie LXDE desktop with modesetting driver.
>
> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> ---
>  drivers/gpu/drm/Kconfig          |   2 +
>  drivers/gpu/drm/Makefile         |   1 +
>  drivers/gpu/drm/zte/Kconfig      |   8 +
>  drivers/gpu/drm/zte/Makefile     |   8 +
>  drivers/gpu/drm/zte/zx_crtc.c    | 711 +++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/zte/zx_crtc.h    |  47 +++
>  drivers/gpu/drm/zte/zx_drm_drv.c | 258 ++++++++++++++
>  drivers/gpu/drm/zte/zx_drm_drv.h |  22 ++
>  drivers/gpu/drm/zte/zx_hdmi.c    | 540 +++++++++++++++++++++++++++++
>  drivers/gpu/drm/zte/zx_plane.c   | 362 ++++++++++++++++++++
>  drivers/gpu/drm/zte/zx_plane.h   |  26 ++
>  11 files changed, 1985 insertions(+)
>  create mode 100644 drivers/gpu/drm/zte/Kconfig
>  create mode 100644 drivers/gpu/drm/zte/Makefile
>  create mode 100644 drivers/gpu/drm/zte/zx_crtc.c
>  create mode 100644 drivers/gpu/drm/zte/zx_crtc.h
>  create mode 100644 drivers/gpu/drm/zte/zx_drm_drv.c
>  create mode 100644 drivers/gpu/drm/zte/zx_drm_drv.h
>  create mode 100644 drivers/gpu/drm/zte/zx_hdmi.c
>  create mode 100644 drivers/gpu/drm/zte/zx_plane.c
>  create mode 100644 drivers/gpu/drm/zte/zx_plane.h
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 483059a22b1b..a91f8cecbe0f 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -223,6 +223,8 @@ source "drivers/gpu/drm/hisilicon/Kconfig"
>
>  source "drivers/gpu/drm/mediatek/Kconfig"
>
> +source "drivers/gpu/drm/zte/Kconfig"
> +
>  # Keep legacy drivers last
>
>  menuconfig DRM_LEGACY
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 439d89b25ae0..fe461c94d266 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -85,3 +85,4 @@ obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/
>  obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/
>  obj-$(CONFIG_DRM_ARCPGU)+= arc/
>  obj-y                  += hisilicon/
> +obj-$(CONFIG_DRM_ZTE)  += zte/
> diff --git a/drivers/gpu/drm/zte/Kconfig b/drivers/gpu/drm/zte/Kconfig
> new file mode 100644
> index 000000000000..4065b2840f1c
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/Kconfig
> @@ -0,0 +1,8 @@
> +config DRM_ZTE
> +       tristate "DRM Support for ZTE SoCs"
> +       depends on DRM && ARCH_ZX
> +       select DRM_KMS_CMA_HELPER
> +       select DRM_KMS_FB_HELPER
> +       select DRM_KMS_HELPER
> +       help
> +         Choose this option to enable DRM on ZTE ZX SoCs.
> diff --git a/drivers/gpu/drm/zte/Makefile b/drivers/gpu/drm/zte/Makefile
> new file mode 100644
> index 000000000000..b40968dc749f
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/Makefile
> @@ -0,0 +1,8 @@
> +zxdrm-y := \
> +       zx_drm_drv.o \
> +       zx_crtc.o \
> +       zx_plane.o \
> +       zx_hdmi.o
> +
> +obj-$(CONFIG_DRM_ZTE) += zxdrm.o
> +
> diff --git a/drivers/gpu/drm/zte/zx_crtc.c b/drivers/gpu/drm/zte/zx_crtc.c
> new file mode 100644
> index 000000000000..531541482e1f
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_crtc.c
> @@ -0,0 +1,711 @@
> +/*
> + * Copyright 2016 Linaro Ltd.
> + * Copyright 2016 ZTE Corporation.
> + *
> + * 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 <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_plane_helper.h>
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/of_address.h>
> +#include <video/videomode.h>
> +
> +#include "zx_drm_drv.h"
> +#include "zx_crtc.h"
> +#include "zx_plane.h"
> +
> +/* OSD (GPC_GLOBAL) registers */
> +#define OSD_INT_STA                    0x04
> +#define OSD_INT_CLRSTA                 0x08
> +#define OSD_INT_MSK                    0x0c
> +#define OSD_INT_AUX_UPT                        BIT(14)
> +#define OSD_INT_MAIN_UPT               BIT(13)
> +#define OSD_INT_GL1_LBW                        BIT(10)
> +#define OSD_INT_GL0_LBW                        BIT(9)
> +#define OSD_INT_VL2_LBW                        BIT(8)
> +#define OSD_INT_VL1_LBW                        BIT(7)
> +#define OSD_INT_VL0_LBW                        BIT(6)
> +#define OSD_INT_BUS_ERR                        BIT(3)
> +#define OSD_INT_CFG_ERR                        BIT(2)
> +#define OSD_INT_ERROR (\
> +       OSD_INT_GL1_LBW | OSD_INT_GL0_LBW | \
> +       OSD_INT_VL2_LBW | OSD_INT_VL1_LBW | OSD_INT_VL0_LBW | \
> +       OSD_INT_BUS_ERR | OSD_INT_CFG_ERR \
> +)
> +#define OSD_INT_ENABLE (OSD_INT_ERROR | OSD_INT_AUX_UPT | OSD_INT_MAIN_UPT)
> +#define OSD_CTRL0                      0x10
> +#define OSD_CTRL0_GL0_EN               BIT(7)
> +#define OSD_CTRL0_GL0_SEL              BIT(6)
> +#define OSD_CTRL0_GL1_EN               BIT(5)
> +#define OSD_CTRL0_GL1_SEL              BIT(4)
> +#define OSD_RST_CLR                    0x1c
> +#define RST_PER_FRAME                  BIT(19)
> +
> +/* Main/Aux channel registers */
> +#define OSD_MAIN_CHN                   0x470
> +#define OSD_AUX_CHN                    0x4d0
> +#define CHN_CTRL0                      0x00
> +#define CHN_ENABLE                     BIT(0)
> +#define CHN_CTRL1                      0x04
> +#define CHN_SCREEN_W_SHIFT             18
> +#define CHN_SCREEN_W_MASK              (0x1fff << CHN_SCREEN_W_SHIFT)
> +#define CHN_SCREEN_H_SHIFT             5
> +#define CHN_SCREEN_H_MASK              (0x1fff << CHN_SCREEN_H_SHIFT)
> +#define CHN_UPDATE                     0x08
> +
> +/* TIMING_CTRL registers */
> +#define VOU_TIMING_CTRL                        0x1000
> +#define TIMING_TC_ENABLE               0x04
> +#define AUX_TC_EN                      BIT(1)
> +#define MAIN_TC_EN                     BIT(0)
> +#define FIR_MAIN_ACTIVE                        0x08
> +#define FIR_AUX_ACTIVE                 0x0c
> +#define FIR_MAIN_H_TIMING              0x10
> +#define FIR_MAIN_V_TIMING              0x14
> +#define FIR_AUX_H_TIMING               0x18
> +#define FIR_AUX_V_TIMING               0x1c
> +#define SYNC_WIDE_SHIFT                        22
> +#define SYNC_WIDE_MASK                 (0x3ff << SYNC_WIDE_SHIFT)
> +#define BACK_PORCH_SHIFT               11
> +#define BACK_PORCH_MASK                        (0x7ff << BACK_PORCH_SHIFT)
> +#define FRONT_PORCH_SHIFT              0
> +#define FRONT_PORCH_MASK               (0x7ff << FRONT_PORCH_SHIFT)
> +#define TIMING_CTRL                    0x20
> +#define AUX_POL_SHIFT                  3
> +#define AUX_POL_MASK                   (0x7 << AUX_POL_SHIFT)
> +#define MAIN_POL_SHIFT                 0
> +#define MAIN_POL_MASK                  (0x7 << MAIN_POL_SHIFT)
> +#define POL_DE_SHIFT                   2
> +#define POL_VSYNC_SHIFT                        1
> +#define POL_HSYNC_SHIFT                        0
> +#define TIMING_INT_CTRL                        0x24
> +#define TIMING_INT_STATE               0x28
> +#define TIMING_INT_AUX_FRAME           BIT(3)
> +#define TIMING_INT_MAIN_FRAME          BIT(1)
> +#define TIMING_INT_AUX_FRAME_SEL_VSW   (0x2 << 10)
> +#define TIMING_INT_MAIN_FRAME_SEL_VSW  (0x2 << 6)
> +#define TIMING_INT_ENABLE (\
> +       TIMING_INT_MAIN_FRAME_SEL_VSW | TIMING_INT_AUX_FRAME_SEL_VSW | \
> +       TIMING_INT_MAIN_FRAME | TIMING_INT_AUX_FRAME \
> +)
> +#define TIMING_MAIN_SHIFT              0x2c
> +#define TIMING_AUX_SHIFT               0x30
> +#define H_SHIFT_VAL                    0x0048
> +#define TIMING_MAIN_PI_SHIFT           0x68
> +#define TIMING_AUX_PI_SHIFT            0x6c
> +#define H_PI_SHIFT_VAL                 0x000f
> +
> +/* DTRC registers */
> +#define DTRC                           0x5000
> +#define DTRC_F0_CTRL                   0x2c
> +#define DTRC_F1_CTRL                   0x5c
> +#define DTRC_DECOMPRESS_BYPASS         BIT(17)
> +#define DTRC_DETILE_CTRL               0x68
> +#define TILE2RASTESCAN_BYPASS_MODE     BIT(30)
> +#define DETILE_ARIDR_MODE_MASK         (0x3 << 0)
> +#define DETILE_ARID_ALL                        0
> +#define DETILE_ARID_IN_ARIDR           1
> +#define DETILE_ARID_BYP_BUT_ARIDR      2
> +#define DETILE_ARID_IN_ARIDR2          3
> +#define DTRC_ARID                      0x6c
> +#define DTRC_DEC2DDR_ARID              0x70
> +
> +/* VOU_CTRL registers */
> +#define VOU_CTRL                       0x6000
> +#define VOU_INF_EN                     0x00
> +#define VOU_INF_CH_SEL                 0x04
> +#define VOU_INF_DATA_SEL               0x08
> +#define VOU_SOFT_RST                   0x14
> +#define VOU_CLK_SEL                    0x18
> +#define VOU_CLK_GL1_SEL                        BIT(5)
> +#define VOU_CLK_GL0_SEL                        BIT(4)
> +#define VOU_CLK_REQEN                  0x20
> +#define VOU_CLK_EN                     0x24
> +
> +/* OTFPPU_CTRL registers */
> +#define OTFPPU_CTRL                    0xa000
> +#define OTFPPU_RSZ_DATA_SOURCE         0x04
> +
> +#define GL_NUM                         2
> +#define VL_NUM                         3
> +
> +enum vou_chn_type {
> +       VOU_CHN_MAIN,
> +       VOU_CHN_AUX,
> +};
> +
> +struct zx_crtc {
> +       struct drm_crtc crtc;
> +       struct drm_plane *primary;
> +       struct device *dev;
> +       void __iomem *chnreg;
> +       enum vou_chn_type chn_type;
> +       struct clk *pixclk;
> +};
> +
> +#define to_zx_crtc(x)  container_of(crtc, struct zx_crtc, crtc)
> +
> +struct zx_vou {
> +       struct device *dev;
> +       void __iomem *mmio;     /* Same as OSD address */
> +       void __iomem *timing;
> +       void __iomem *vouctl;
> +       void __iomem *otfppu;
> +       void __iomem *dtrc;
> +       struct clk *axi_clk;
> +       struct clk *ppu_clk;
> +       struct clk *main_clk;
> +       struct clk *aux_clk;
> +       struct zx_crtc *main_crtc;
> +       struct zx_crtc *aux_crtc;
> +};
> +
> +struct zx_layer_data gl_data[GL_NUM] = {
> +       {
> +               .layer_offset = 0x130,
> +               .csc_offset = 0x580,
> +               .hbsc_offset = 0x820,
> +               .rsz_offset = 0xa600,
> +       }, {
> +               .layer_offset = 0x200,
> +               .csc_offset = 0x5d0,
> +               .hbsc_offset = 0x860,
> +               .rsz_offset = 0xa800,
> +       },
> +};
> +
> +/* Video layers have no CSC block */
> +struct zx_layer_data vl_data[VL_NUM] = {
> +       {
> +               .layer_offset = 0x040,
> +               .hbsc_offset = 0x760,
> +               .rsz_offset = 0xaa00,
> +       }, {
> +               .layer_offset = 0x090,
> +               .hbsc_offset = 0x7a0,
> +               .rsz_offset = 0xac00,
> +       }, {
> +               .layer_offset = 0x0e0,
> +               .hbsc_offset = 0x7e0,
> +               .rsz_offset = 0xae00,
> +       },
> +};
> +
> +static inline bool is_main_crtc(struct drm_crtc *crtc)
> +{
> +       struct zx_crtc *zcrtc = to_zx_crtc(crtc);
> +
> +       return zcrtc->chn_type == VOU_CHN_MAIN;
> +}
> +
> +void vou_inf_enable(struct vou_inf *inf)
> +{
> +       struct drm_encoder *encoder = inf->encoder;
> +       struct drm_device *drm = encoder->dev;
> +       struct zx_drm_private *priv = drm->dev_private;
> +       struct zx_vou *vou = priv->vou;
> +       bool is_main = is_main_crtc(encoder->crtc);
> +       u32 data_sel_shift = inf->id << 1;
> +       u32 val;
> +
> +       /* Select data format */
> +       val = readl(vou->vouctl + VOU_INF_DATA_SEL);
> +       val &= ~(0x3 << data_sel_shift);
> +       val |= inf->data_sel << data_sel_shift;
> +       writel(val, vou->vouctl + VOU_INF_DATA_SEL);
> +
> +       /* Select channel */
> +       val = readl(vou->vouctl + VOU_INF_CH_SEL);
> +       if (is_main)
> +               val &= ~(1 << inf->id);
> +       else
> +               val |= 1 << inf->id;
> +       writel(val, vou->vouctl + VOU_INF_CH_SEL);
> +
> +       /* Select interface clocks */
> +       val = readl(vou->vouctl + VOU_CLK_SEL);
> +       if (is_main)
> +               val &= ~inf->clocks_sel_bits;
> +       else
> +               val |= inf->clocks_sel_bits;
> +       writel(val, vou->vouctl + VOU_CLK_SEL);
> +
> +       /* Enable interface clocks */
> +       val = readl(vou->vouctl + VOU_CLK_EN);
> +       val |= inf->clocks_en_bits;
> +       writel(val, vou->vouctl + VOU_CLK_EN);
> +
> +       /* Enable the device */
> +       val = readl(vou->vouctl + VOU_INF_EN);
> +       val |= 1 << inf->id;
> +       writel(val, vou->vouctl + VOU_INF_EN);
> +}
> +
> +void vou_inf_disable(struct vou_inf *inf)
> +{
> +       struct drm_encoder *encoder = inf->encoder;
> +       struct drm_device *drm = encoder->dev;
> +       struct zx_drm_private *priv = drm->dev_private;
> +       struct zx_vou *vou = priv->vou;
> +       u32 val;
> +
> +       /* Disable the device */
> +       val = readl(vou->vouctl + VOU_INF_EN);
> +       val &= ~(1 << inf->id);
> +       writel(val, vou->vouctl + VOU_INF_EN);
> +
> +       /* Disable interface clocks */
> +       val = readl(vou->vouctl + VOU_CLK_EN);
> +       val &= ~inf->clocks_en_bits;
> +       writel(val, vou->vouctl + VOU_CLK_EN);
> +}
> +
> +static inline void vou_chn_set_update(struct zx_crtc *zcrtc)
> +{
> +       writel(1, zcrtc->chnreg + CHN_UPDATE);
> +}
> +
> +static void zx_crtc_enable(struct drm_crtc *crtc)
> +{
> +       struct drm_display_mode *mode = &crtc->state->adjusted_mode;
> +       struct zx_crtc *zcrtc = to_zx_crtc(crtc);
> +       struct zx_vou *vou = dev_get_drvdata(zcrtc->dev);
> +       bool is_main = is_main_crtc(crtc);
> +       struct videomode vm;
> +       u32 pol = 0;
> +       u32 val;
> +
> +       drm_display_mode_to_videomode(mode, &vm);
> +
> +       /* Set up timing parameters */
> +       val = (vm.vactive - 1) << 16;
> +       val |= (vm.hactive - 1) & 0xffff;
> +       writel(val, vou->timing + (is_main ? FIR_MAIN_ACTIVE : FIR_AUX_ACTIVE));
> +
> +       val = ((vm.hsync_len - 1) << SYNC_WIDE_SHIFT) & SYNC_WIDE_MASK;
> +       val |= ((vm.hback_porch - 1) << BACK_PORCH_SHIFT) & BACK_PORCH_MASK;
> +       val |= ((vm.hfront_porch - 1) << FRONT_PORCH_SHIFT) & FRONT_PORCH_MASK;
> +       writel(val, vou->timing + (is_main ? FIR_MAIN_H_TIMING :
> +                                            FIR_AUX_H_TIMING));
> +
> +       val = ((vm.vsync_len - 1) << SYNC_WIDE_SHIFT) & SYNC_WIDE_MASK;
> +       val |= ((vm.vback_porch - 1) << BACK_PORCH_SHIFT) & BACK_PORCH_MASK;
> +       val |= ((vm.vfront_porch - 1) << FRONT_PORCH_SHIFT) & FRONT_PORCH_MASK;
> +       writel(val, vou->timing + (is_main ? FIR_MAIN_V_TIMING :
> +                                            FIR_AUX_V_TIMING));
> +
> +       /* Set up polarities */
> +       if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW)
> +               pol |= 1 << POL_VSYNC_SHIFT;
> +       if (vm.flags & DISPLAY_FLAGS_HSYNC_LOW)
> +               pol |= 1 << POL_HSYNC_SHIFT;
> +
> +       val = readl(vou->timing + TIMING_CTRL);
> +       val &= ~(is_main ? MAIN_POL_MASK : AUX_POL_MASK);
> +       val |= pol << (is_main ? MAIN_POL_SHIFT : AUX_POL_SHIFT);
> +       writel(val, vou->timing + TIMING_CTRL);
> +
> +       /* Setup SHIFT register by following what ZTE BSP does */
> +       writel(H_SHIFT_VAL, vou->timing + (is_main ? TIMING_MAIN_SHIFT :
> +                                                    TIMING_AUX_SHIFT));
> +       writel(H_PI_SHIFT_VAL, vou->timing + (is_main ? TIMING_MAIN_PI_SHIFT :
> +                                                       TIMING_AUX_PI_SHIFT));
> +
> +       /* Enable TIMING_CTRL */
> +       val = readl(vou->timing + TIMING_TC_ENABLE);
> +       val |= is_main ? MAIN_TC_EN : AUX_TC_EN;
> +       writel(val, vou->timing + TIMING_TC_ENABLE);
> +
> +       /* Configure channel screen size */
> +       val = readl(zcrtc->chnreg + CHN_CTRL1);
> +       val &= ~(CHN_SCREEN_W_MASK | CHN_SCREEN_H_MASK);
> +       val |= (vm.hactive << CHN_SCREEN_W_SHIFT) & CHN_SCREEN_W_MASK;
> +       val |= (vm.vactive << CHN_SCREEN_H_SHIFT) & CHN_SCREEN_H_MASK;
> +       writel(val, zcrtc->chnreg + CHN_CTRL1);
> +
> +       /* Update channel */
> +       vou_chn_set_update(zcrtc);
> +
> +       /* Enable channel */
> +       val = readl(zcrtc->chnreg + CHN_CTRL0);
> +       val |= CHN_ENABLE;
> +       writel(val, zcrtc->chnreg + CHN_CTRL0);
> +
> +       /* Enable Graphic Layer */
> +       val = readl(vou->mmio + OSD_CTRL0);
> +       val |= is_main ? OSD_CTRL0_GL0_EN : OSD_CTRL0_GL1_EN;
> +       writel(val, vou->mmio + OSD_CTRL0);
> +
> +       drm_crtc_vblank_on(crtc);
> +
> +       /* Enable pixel clock */
> +       clk_set_rate(zcrtc->pixclk, mode->clock * 1000);
> +       clk_prepare_enable(zcrtc->pixclk);
> +}
> +
> +static void zx_crtc_disable(struct drm_crtc *crtc)
> +{
> +       struct zx_crtc *zcrtc = to_zx_crtc(crtc);
> +       struct zx_vou *vou = dev_get_drvdata(zcrtc->dev);
> +       bool is_main = is_main_crtc(crtc);
> +       u32 val;
> +
> +       clk_disable_unprepare(zcrtc->pixclk);
> +
> +       drm_crtc_vblank_off(crtc);
> +
> +       /* Disable Graphic Layer */
> +       val = readl(vou->mmio + OSD_CTRL0);
> +       val &= ~(is_main ? OSD_CTRL0_GL0_EN : OSD_CTRL0_GL1_EN);
> +       writel(val, vou->mmio + OSD_CTRL0);
> +
> +       /* Disable channel */
> +       val = readl(zcrtc->chnreg + CHN_CTRL0);
> +       val &= ~CHN_ENABLE;
> +       writel(val, zcrtc->chnreg + CHN_CTRL0);
> +
> +       /* Disable TIMING_CTRL */
> +       val = readl(vou->timing + TIMING_TC_ENABLE);
> +       val &= ~(is_main ? MAIN_TC_EN : AUX_TC_EN);
> +       writel(val, vou->timing + TIMING_TC_ENABLE);
> +}
> +
> +static void zx_crtc_atomic_begin(struct drm_crtc *crtc,
> +                                struct drm_crtc_state *state)
> +{
> +       struct drm_pending_vblank_event *event = crtc->state->event;
> +
> +       if (event) {
> +               crtc->state->event = NULL;
> +
> +               spin_lock_irq(&crtc->dev->event_lock);
> +               if (drm_crtc_vblank_get(crtc) == 0)
> +                       drm_crtc_arm_vblank_event(crtc, event);
> +               else
> +                       drm_crtc_send_vblank_event(crtc, event);
> +               spin_unlock_irq(&crtc->dev->event_lock);
> +       }
> +}
> +
> +static const struct drm_crtc_helper_funcs zx_crtc_helper_funcs = {
> +       .enable = zx_crtc_enable,
> +       .disable = zx_crtc_disable,
> +       .atomic_begin = zx_crtc_atomic_begin,
> +};
> +
> +static const struct drm_crtc_funcs zx_crtc_funcs = {
> +       .destroy = drm_crtc_cleanup,
> +       .set_config = drm_atomic_helper_set_config,
> +       .page_flip = drm_atomic_helper_page_flip,
> +       .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 struct zx_crtc *zx_crtc_init(struct drm_device *drm,
> +                                   enum vou_chn_type chn_type)
> +{
> +       struct zx_drm_private *priv = drm->dev_private;
> +       struct zx_vou *vou = priv->vou;
> +       struct device *dev = vou->dev;
> +       struct zx_layer_data *data;
> +       struct zx_crtc *zcrtc;
> +       int ret;
> +
> +       zcrtc = devm_kzalloc(dev, sizeof(*zcrtc), GFP_KERNEL);
> +       if (!zcrtc)
> +               return ERR_PTR(-ENOMEM);
> +
> +       zcrtc->dev = dev;
> +       zcrtc->chn_type = chn_type;
> +
> +       if (chn_type == VOU_CHN_MAIN) {
> +               data = &gl_data[0];
> +               zcrtc->chnreg = vou->mmio + OSD_MAIN_CHN;
> +       } else {
> +               data = &gl_data[1];
> +               zcrtc->chnreg = vou->mmio + OSD_AUX_CHN;
> +       }
> +
> +       zcrtc->pixclk = devm_clk_get(dev, (chn_type == VOU_CHN_MAIN) ?
> +                                         "main_wclk" : "aux_wclk");
> +       if (IS_ERR(zcrtc->pixclk))
> +               return ERR_PTR(PTR_ERR(zcrtc->pixclk));
> +
> +       zcrtc->primary = zx_plane_init(drm, dev, vou->mmio, data,
> +                                      DRM_PLANE_TYPE_PRIMARY);
> +       if (IS_ERR(zcrtc->primary))
> +               return ERR_PTR(PTR_ERR(zcrtc->primary));
> +
> +       ret = drm_crtc_init_with_planes(drm, &zcrtc->crtc, zcrtc->primary, NULL,
> +                                       &zx_crtc_funcs, NULL);
> +       if (ret)
> +               return ERR_PTR(ret);
> +
> +       drm_crtc_helper_add(&zcrtc->crtc, &zx_crtc_helper_funcs);
> +
> +       return zcrtc;
> +}
> +
> +int zx_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe)
> +{
> +       struct zx_drm_private *priv = drm->dev_private;
> +       struct zx_vou *vou = priv->vou;
> +       u32 intctl;
> +
> +       intctl = readl(vou->timing + TIMING_INT_CTRL);
> +       if (pipe == 0)
> +               intctl |= TIMING_INT_MAIN_FRAME;
> +       else
> +               intctl |= TIMING_INT_AUX_FRAME;
> +       writel(intctl, vou->timing + TIMING_INT_CTRL);
> +
> +       return 0;
> +}
> +
> +void zx_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe)
> +{
> +       struct zx_drm_private *priv = drm->dev_private;
> +       struct zx_vou *vou = priv->vou;
> +       u32 intctl;
> +
> +       intctl = readl(vou->timing + TIMING_INT_CTRL);
> +       if (pipe == 0)
> +               intctl &= ~TIMING_INT_MAIN_FRAME;
> +       else
> +               intctl &= ~TIMING_INT_AUX_FRAME;
> +       writel(intctl, vou->timing + TIMING_INT_CTRL);
> +}
> +
> +static irqreturn_t vou_irq_handler(int irq, void *dev_id)
> +{
> +       struct zx_vou *vou = dev_id;
> +       u32 state;
> +
> +       /* Handle TIMING_CTRL frame interrupts */
> +       state = readl(vou->timing + TIMING_INT_STATE);
> +       writel(state, vou->timing + TIMING_INT_STATE);
> +
> +       if (state & TIMING_INT_MAIN_FRAME)
> +               drm_crtc_handle_vblank(&vou->main_crtc->crtc);
> +
> +       if (state & TIMING_INT_AUX_FRAME)
> +               drm_crtc_handle_vblank(&vou->aux_crtc->crtc);
> +
> +       /* Handle OSD interrupts */
> +       state = readl(vou->mmio + OSD_INT_STA);
> +       writel(state, vou->mmio + OSD_INT_CLRSTA);
> +
> +       if (state & OSD_INT_MAIN_UPT) {
> +               vou_chn_set_update(vou->main_crtc);
> +               zx_plane_set_update(vou->main_crtc->primary);
> +       }
> +
> +       if (state & OSD_INT_AUX_UPT) {
> +               vou_chn_set_update(vou->aux_crtc);
> +               zx_plane_set_update(vou->aux_crtc->primary);
> +       }
> +
> +       if (state & OSD_INT_ERROR)
> +               dev_err(vou->dev, "OSD ERROR: 0x%08x!\n", state);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static void vou_dtrc_init(struct zx_vou *vou)
> +{
> +       u32 val;
> +
> +       val = readl(vou->dtrc + DTRC_DETILE_CTRL);
> +       /* Clear bit for bypass by ID */
> +       val &= ~TILE2RASTESCAN_BYPASS_MODE;
> +       /* Select ARIDR mode */
> +       val &= ~DETILE_ARIDR_MODE_MASK;
> +       val |= DETILE_ARID_IN_ARIDR;
> +       writel(val, vou->dtrc + DTRC_DETILE_CTRL);
> +
> +       /* Bypass decompression for both frames */
> +       val = readl(vou->dtrc + DTRC_F0_CTRL);
> +       val |= DTRC_DECOMPRESS_BYPASS;
> +       writel(val, vou->dtrc + DTRC_F0_CTRL);
> +
> +       val = readl(vou->dtrc + DTRC_F1_CTRL);
> +       val |= DTRC_DECOMPRESS_BYPASS;
> +       writel(val, vou->dtrc + DTRC_F1_CTRL);
> +
> +       /* Set up ARID register */
> +       val = 0x0e;
> +       val |= 0x0f << 8;
> +       val |= 0x0e << 16;
> +       val |= 0x0f << 24;
> +       writel(val, vou->dtrc + DTRC_ARID);
> +
> +       /* Set up DEC2DDR_ARID register */
> +       val = 0x0e;
> +       val |= 0x0f << 8;
> +       writel(val, vou->dtrc + DTRC_DEC2DDR_ARID);
> +}
> +
> +static void vou_hw_init(struct zx_vou *vou)
> +{
> +       u32 val;
> +
> +       /* Set GL0 to main channel and GL1 to aux channel */
> +       val = readl(vou->mmio + OSD_CTRL0);
> +       val &= ~OSD_CTRL0_GL0_SEL;
> +       val |= OSD_CTRL0_GL1_SEL;
> +       writel(val, vou->mmio + OSD_CTRL0);
> +
> +       /* Release reset for all VOU modules */
> +       writel(~0, vou->vouctl + VOU_SOFT_RST);
> +
> +       /* Select main clock for GL0 and aux clock for GL1 module */
> +       val = readl(vou->vouctl + VOU_CLK_SEL);
> +       val &= ~VOU_CLK_GL0_SEL;
> +       val |= VOU_CLK_GL1_SEL;
> +       writel(val, vou->vouctl + VOU_CLK_SEL);
> +
> +       /* Enable clock auto-gating for all VOU modules */
> +       writel(~0, vou->vouctl + VOU_CLK_REQEN);
> +
> +       /* Enable all VOU module clocks */
> +       writel(~0, vou->vouctl + VOU_CLK_EN);
> +
> +       /* Clear both OSD and TIMING_CTRL interrupt state */
> +       writel(~0, vou->mmio + OSD_INT_CLRSTA);
> +       writel(~0, vou->timing + TIMING_INT_STATE);
> +
> +       /* Enable OSD and TIMING_CTRL interrrupts */
> +       writel(OSD_INT_ENABLE, vou->mmio + OSD_INT_MSK);
> +       writel(TIMING_INT_ENABLE, vou->timing + TIMING_INT_CTRL);
> +
> +       /* Select GPC as input to gl/vl scaler as a sane default setting */
> +       writel(0x2a, vou->otfppu + OTFPPU_RSZ_DATA_SOURCE);
> +
> +       /*
> +        * Needs to reset channel and layer logic per frame when frame starts
> +        * to get VOU work properly.
> +        */
> +       val = readl(vou->mmio + OSD_RST_CLR);
> +       val |= RST_PER_FRAME;
> +       writel(val, vou->mmio + OSD_RST_CLR);
> +
> +       vou_dtrc_init(vou);
> +}
> +
> +static int zx_crtc_bind(struct device *dev, struct device *master, void *data)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct device_node *np = dev->of_node;
> +       struct drm_device *drm = data;
> +       struct zx_drm_private *priv = drm->dev_private;
> +       struct resource res;
> +       struct zx_vou *vou;
> +       int irq;
> +       int ret;
> +
> +       vou = devm_kzalloc(dev, sizeof(*vou), GFP_KERNEL);
> +       if (!vou)
> +               return -ENOMEM;
> +
> +       /*
> +        * Skip mem region request to avoid overlapping on output device
> +        * mem regions, as the regions will be requested by connector drivers.
> +        */
> +       of_address_to_resource(np, 0, &res);
> +       vou->mmio = devm_ioremap(dev, res.start, resource_size(&res));
> +       if (!vou->mmio)
> +               return -ENOMEM;
> +
> +       vou->timing = vou->mmio + VOU_TIMING_CTRL;
> +       vou->vouctl = vou->mmio + VOU_CTRL;
> +       vou->otfppu = vou->mmio + OTFPPU_CTRL;
> +       vou->dtrc = vou->mmio + DTRC;
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0)
> +               return irq;
> +
> +       vou->axi_clk = devm_clk_get(dev, "aclk");
> +       if (IS_ERR(vou->axi_clk))
> +               return PTR_ERR(vou->axi_clk);
> +
> +       vou->ppu_clk = devm_clk_get(dev, "ppu_wclk");
> +       if (IS_ERR(vou->ppu_clk))
> +               return PTR_ERR(vou->ppu_clk);
> +
> +       clk_prepare_enable(vou->axi_clk);
> +       clk_prepare_enable(vou->ppu_clk);
> +
> +       vou->dev = dev;
> +       priv->vou = vou;
> +       dev_set_drvdata(dev, vou);
> +
> +       vou_hw_init(vou);
> +
> +       ret = devm_request_irq(dev, irq, vou_irq_handler, 0, "zx_vou", vou);
> +       if (ret < 0)
> +               return ret;
> +
> +       vou->main_crtc = zx_crtc_init(drm, VOU_CHN_MAIN);
> +       if (IS_ERR(vou->main_crtc))
> +               return PTR_ERR(vou->main_crtc);
> +
> +       vou->aux_crtc = zx_crtc_init(drm, VOU_CHN_AUX);
> +       if (IS_ERR(vou->aux_crtc))
> +               return PTR_ERR(vou->aux_crtc);
> +
> +       return 0;
> +}
> +
> +static void zx_crtc_unbind(struct device *dev, struct device *master,
> +                          void *data)
> +{
> +       struct zx_vou *vou = dev_get_drvdata(dev);
> +
> +       clk_disable_unprepare(vou->axi_clk);
> +       clk_disable_unprepare(vou->ppu_clk);
> +}
> +
> +static const struct component_ops zx_crtc_component_ops = {
> +       .bind = zx_crtc_bind,
> +       .unbind = zx_crtc_unbind,
> +};
> +
> +static int zx_crtc_probe(struct platform_device *pdev)
> +{
> +       return component_add(&pdev->dev, &zx_crtc_component_ops);
> +}
> +
> +static int zx_crtc_remove(struct platform_device *pdev)
> +{
> +       component_del(&pdev->dev, &zx_crtc_component_ops);
> +       return 0;
> +}
> +
> +static const struct of_device_id zx_crtc_of_match[] = {
> +       { .compatible = "zte,zx296718-vou", },
> +       { /* end */ },
> +};
> +MODULE_DEVICE_TABLE(of, zx_crtc_of_match);
> +
> +struct platform_driver zx_crtc_driver = {
> +       .probe = zx_crtc_probe,
> +       .remove = zx_crtc_remove,
> +       .driver = {
> +               .name = "zx-crtc",
> +               .of_match_table = zx_crtc_of_match,
> +       },
> +};
> diff --git a/drivers/gpu/drm/zte/zx_crtc.h b/drivers/gpu/drm/zte/zx_crtc.h
> new file mode 100644
> index 000000000000..f889208054ce
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_crtc.h
> @@ -0,0 +1,47 @@
> +/*
> + * Copyright 2016 Linaro Ltd.
> + * Copyright 2016 ZTE Corporation.
> + *
> + * 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 __ZX_CRTC_H__
> +#define __ZX_CRTC_H__
> +
> +#define VOU_CRTC_MASK          0x3
> +
> +/* VOU output interfaces */
> +enum vou_inf_id {
> +       VOU_HDMI        = 0,
> +       VOU_RGB_LCD     = 1,
> +       VOU_TV_ENC      = 2,
> +       VOU_MIPI_DSI    = 3,
> +       VOU_LVDS        = 4,
> +       VOU_VGA         = 5,
> +};
> +
> +enum vou_inf_data_sel {
> +       VOU_YUV444      = 0,
> +       VOU_RGB_101010  = 1,
> +       VOU_RGB_888     = 2,
> +       VOU_RGB_666     = 3,
> +};
> +
> +struct vou_inf {
> +       struct drm_encoder *encoder;
> +       enum vou_inf_id id;
> +       enum vou_inf_data_sel data_sel;
> +       u32 clocks_en_bits;
> +       u32 clocks_sel_bits;
> +};
> +
> +void vou_inf_enable(struct vou_inf *inf);
> +void vou_inf_disable(struct vou_inf *inf);
> +
> +int zx_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe);
> +void zx_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe);
> +
> +#endif /* __ZX_CRTC_H__ */
> diff --git a/drivers/gpu/drm/zte/zx_drm_drv.c b/drivers/gpu/drm/zte/zx_drm_drv.c
> new file mode 100644
> index 000000000000..a45ecaa7007b
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_drm_drv.c
> @@ -0,0 +1,258 @@
> +/*
> + * Copyright 2016 Linaro Ltd.
> + * Copyright 2016 ZTE Corporation.
> + *
> + * 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/module.h>
> +#include <linux/spinlock.h>
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/list.h>
> +#include <linux/of_graph.h>
> +#include <linux/of_platform.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_of.h>
> +
> +#include "zx_drm_drv.h"
> +#include "zx_crtc.h"
> +
> +static void zx_drm_fb_output_poll_changed(struct drm_device *drm)
> +{
> +       struct zx_drm_private *priv = drm->dev_private;
> +
> +       drm_fbdev_cma_hotplug_event(priv->fbdev);
> +}
> +
> +static const struct drm_mode_config_funcs zx_drm_mode_config_funcs = {
> +       .fb_create = drm_fb_cma_create,
> +       .output_poll_changed = zx_drm_fb_output_poll_changed,
> +       .atomic_check = drm_atomic_helper_check,
> +       .atomic_commit = drm_atomic_helper_commit,
> +};
> +
> +static void zx_drm_lastclose(struct drm_device *drm)
> +{
> +       struct zx_drm_private *priv = drm->dev_private;
> +
> +       drm_fbdev_cma_restore_mode(priv->fbdev);
> +}
> +
> +static const struct file_operations zx_drm_fops = {
> +       .owner = THIS_MODULE,
> +       .open = drm_open,
> +       .release = drm_release,
> +       .unlocked_ioctl = drm_ioctl,
> +#ifdef CONFIG_COMPAT
> +       .compat_ioctl = drm_compat_ioctl,
> +#endif
> +       .poll = drm_poll,
> +       .read = drm_read,
> +       .llseek = noop_llseek,
> +       .mmap = drm_gem_cma_mmap,
> +};
> +
> +static struct drm_driver zx_drm_driver = {
> +       .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
> +                          DRIVER_ATOMIC,
> +       .lastclose = zx_drm_lastclose,
> +       .get_vblank_counter = drm_vblank_no_hw_counter,
> +       .enable_vblank = zx_crtc_enable_vblank,
> +       .disable_vblank = zx_crtc_disable_vblank,
> +       .gem_free_object = drm_gem_cma_free_object,
> +       .gem_vm_ops = &drm_gem_cma_vm_ops,
> +       .dumb_create = drm_gem_cma_dumb_create,
> +       .dumb_map_offset = drm_gem_cma_dumb_map_offset,
> +       .dumb_destroy = drm_gem_dumb_destroy,
> +       .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> +       .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> +       .gem_prime_export = drm_gem_prime_export,
> +       .gem_prime_import = drm_gem_prime_import,
> +       .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,
> +       .fops = &zx_drm_fops,
> +       .name = "zx-vou",
> +       .desc = "ZTE VOU Controller DRM",
> +       .date = "20160811",
> +       .major = 1,
> +       .minor = 0,
> +};
> +
> +static int zx_drm_bind(struct device *dev)
> +{
> +       struct drm_device *drm;
> +       struct zx_drm_private *priv;
> +       int ret;
> +
> +       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +       if (!priv)
> +               return -ENOMEM;
> +
> +       drm = drm_dev_alloc(&zx_drm_driver, dev);
> +       if (!drm)
> +               return -ENOMEM;
> +
> +       drm->dev_private = priv;
> +       dev_set_drvdata(dev, drm);
> +
> +       drm_mode_config_init(drm);
> +       drm->mode_config.min_width = 16;
> +       drm->mode_config.min_height = 16;
> +       drm->mode_config.max_width = 4096;
> +       drm->mode_config.max_height = 4096;
> +       drm->mode_config.funcs = &zx_drm_mode_config_funcs;
> +
> +       ret = drm_dev_register(drm, 0);
> +       if (ret)
> +               goto out_free;
> +
> +       ret = component_bind_all(dev, drm);
> +       if (ret) {
> +               DRM_ERROR("Failed to bind all components\n");
> +               goto out_unregister;
> +       }
> +
> +       ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
> +       if (ret < 0) {
> +               DRM_ERROR("failed to initialise vblank\n");
> +               goto out_unbind;
> +       }
> +
> +       /*
> +        * We will manage irq handler on our own.  In this case, irq_enabled
> +        * need to be true for using vblank core support.
> +        */
> +       drm->irq_enabled = true;
> +
> +       drm_mode_config_reset(drm);
> +       drm_kms_helper_poll_init(drm);
> +
> +       priv->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc,
> +                                        drm->mode_config.num_connector);
> +       if (IS_ERR(priv->fbdev)) {
> +               ret = PTR_ERR(priv->fbdev);
> +               priv->fbdev = NULL;
> +               goto out_fini;
> +       }
> +
> +       return 0;
> +
> +out_fini:
> +       drm_kms_helper_poll_fini(drm);
> +       drm_mode_config_cleanup(drm);
> +       drm_vblank_cleanup(drm);
> +out_unbind:
> +       component_unbind_all(dev, drm);
> +out_unregister:
> +       drm_dev_unregister(drm);
> +out_free:
> +       dev_set_drvdata(dev, NULL);
> +       drm_dev_unref(drm);
> +       return ret;
> +}
> +
> +static void zx_drm_unbind(struct device *dev)
> +{
> +       struct drm_device *drm = dev_get_drvdata(dev);
> +       struct zx_drm_private *priv = drm->dev_private;
> +
> +       if (priv->fbdev) {
> +               drm_fbdev_cma_fini(priv->fbdev);
> +               priv->fbdev = NULL;
> +       }
> +       drm_kms_helper_poll_fini(drm);
> +       component_unbind_all(dev, drm);
> +       drm_vblank_cleanup(drm);
> +       drm_mode_config_cleanup(drm);
> +       drm_dev_unregister(drm);
> +       drm_dev_unref(drm);
> +       drm->dev_private = NULL;
> +       dev_set_drvdata(dev, NULL);
> +}
> +
> +static const struct component_master_ops zx_drm_master_ops = {
> +       .bind = zx_drm_bind,
> +       .unbind = zx_drm_unbind,
> +};
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> +       return dev->of_node == data;
> +}
> +
> +static int zx_drm_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct device_node *parent = dev->of_node;
> +       struct device_node *child;
> +       struct component_match *match = NULL;
> +       int ret;
> +
> +       ret = of_platform_populate(parent, NULL, NULL, dev);
> +       if (ret)
> +               return ret;
> +
> +       for_each_available_child_of_node(parent, child) {
> +               component_match_add(dev, &match, compare_of, child);
> +               of_node_put(child);
> +       }
> +
> +       return component_master_add_with_match(dev, &zx_drm_master_ops, match);
> +}
> +
> +static int zx_drm_remove(struct platform_device *pdev)
> +{
> +       component_master_del(&pdev->dev, &zx_drm_master_ops);
> +       return 0;
> +}
> +
> +static const struct of_device_id zx_drm_of_match[] = {
> +       { .compatible = "zte,zx-display-subsystem", },
> +       { /* end */ },
> +};
> +MODULE_DEVICE_TABLE(of, zx_drm_of_match);
> +
> +static struct platform_driver zx_drm_platform_driver = {
> +       .probe = zx_drm_probe,
> +       .remove = zx_drm_remove,
> +       .driver = {
> +               .name = "zx-drm",
> +               .of_match_table = zx_drm_of_match,
> +       },
> +};
> +
> +static struct platform_driver *drivers[] = {
> +       &zx_crtc_driver,
> +       &zx_hdmi_driver,
> +       &zx_drm_platform_driver,
> +};
> +
> +static int zx_drm_init(void)
> +{
> +       return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
> +}
> +module_init(zx_drm_init);
> +
> +static void zx_drm_exit(void)
> +{
> +       platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
> +}
> +module_exit(zx_drm_exit);
> +
> +MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
> +MODULE_DESCRIPTION("ZTE ZX VOU DRM driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/zte/zx_drm_drv.h b/drivers/gpu/drm/zte/zx_drm_drv.h
> new file mode 100644
> index 000000000000..6aac303de6aa
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_drm_drv.h
> @@ -0,0 +1,22 @@
> +/*
> + * Copyright 2016 Linaro Ltd.
> + * Copyright 2016 ZTE Corporation.
> + *
> + * 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 __ZX_DRM_DRV_H__
> +#define __ZX_DRM_DRV_H__
> +
> +struct zx_drm_private {
> +       struct drm_fbdev_cma *fbdev;
> +       struct zx_vou *vou;
> +};
> +
> +extern struct platform_driver zx_crtc_driver;
> +extern struct platform_driver zx_hdmi_driver;
> +
> +#endif /* __ZX_DRM_DRV_H__ */
> diff --git a/drivers/gpu/drm/zte/zx_hdmi.c b/drivers/gpu/drm/zte/zx_hdmi.c
> new file mode 100644
> index 000000000000..5aaab8493b1b
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_hdmi.c
> @@ -0,0 +1,540 @@
> +/*
> + * Copyright 2016 Linaro Ltd.
> + * Copyright 2016 ZTE Corporation.
> + *
> + * 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 <drm/drm_of.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_edid.h>
> +#include <linux/irq.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/hdmi.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_device.h>
> +#include <linux/component.h>
> +
> +#include "zx_crtc.h"
> +
> +#define FUNC_SEL                       0x000b
> +#define FUNC_HDMI_EN                   BIT(0)
> +#define CLKPWD                         0x000d
> +#define CLKPWD_PDIDCK                  BIT(2)
> +#define PWD_SRST                       0x0010
> +#define P2T_CTRL                       0x0066
> +#define P2T_DC_PKT_EN                  BIT(7)
> +#define L1_INTR_STAT                   0x007e
> +#define L1_INTR_STAT_INTR1             BIT(0)
> +#define INTR1_STAT                     0x008f
> +#define INTR1_MASK                     0x0095
> +#define INTR1_MONITOR_DETECT           (BIT(5) | BIT(6))
> +#define ZX_DDC_ADDR                    0x00ed
> +#define ZX_DDC_SEGM                    0x00ee
> +#define ZX_DDC_OFFSET                  0x00ef
> +#define ZX_DDC_DIN_CNT1                        0x00f0
> +#define ZX_DDC_DIN_CNT2                        0x00f1
> +#define ZX_DDC_CMD                     0x00f3
> +#define DDC_CMD_MASK                   0xf
> +#define DDC_CMD_CLEAR_FIFO             0x9
> +#define DDC_CMD_SEQUENTIAL_READ                0x2
> +#define ZX_DDC_DATA                    0x00f4
> +#define ZX_DDC_DOUT_CNT                        0x00f5
> +#define DDC_DOUT_CNT_MASK              0x1f
> +#define TEST_TXCTRL                    0x00f7
> +#define TEST_TXCTRL_HDMI_MODE          BIT(1)
> +#define HDMICTL4                       0x0235
> +#define TPI_HPD_RSEN                   0x063b
> +#define TPI_HPD_CONNECTION             (BIT(1) | BIT(2))
> +#define TPI_INFO_FSEL                  0x06bf
> +#define FSEL_AVI                       0
> +#define FSEL_GBD                       1
> +#define FSEL_AUDIO                     2
> +#define FSEL_SPD                       3
> +#define FSEL_MPEG                      4
> +#define FSEL_VSIF                      5
> +#define TPI_INFO_B0                    0x06c0
> +#define TPI_INFO_EN                    0x06df
> +#define TPI_INFO_TRANS_EN              BIT(7)
> +#define TPI_INFO_TRANS_RPT             BIT(6)
> +#define TPI_DDC_MASTER_EN              0x06f8
> +#define HW_DDC_MASTER                  BIT(7)
> +
> +#define ZX_HDMI_INFOFRAME_SIZE         31
> +
> +struct zx_hdmi {
> +       struct drm_connector connector;
> +       struct drm_encoder encoder;
> +       struct device *dev;
> +       struct drm_device *drm;
> +       void __iomem *mmio;
> +       struct clk *cec_clk;
> +       struct clk *osc_clk;
> +       struct clk *xclk;
> +       bool sink_is_hdmi;
> +       bool sink_has_audio;
> +       struct vou_inf *inf;
> +};
> +
> +#define to_zx_hdmi(x)  container_of(x, struct zx_hdmi, x)
> +
> +static struct vou_inf vou_inf_hdmi = {
> +       .id = VOU_HDMI,
> +       .data_sel = VOU_YUV444,
> +       .clocks_en_bits = BIT(24) | BIT(18) | BIT(6),
> +       .clocks_sel_bits = BIT(13) | BIT(2),
> +};
> +
> +static inline u8 hdmi_readb(struct zx_hdmi *hdmi, u16 offset)
> +{
> +       return readl_relaxed(hdmi->mmio + offset * 4);
> +}
> +
> +static inline void hdmi_writeb(struct zx_hdmi *hdmi, u16 offset, u8 val)
> +{
> +       writel_relaxed(val, hdmi->mmio + offset * 4);
> +}
> +
> +static int zx_hdmi_infoframe_trans(struct zx_hdmi *hdmi,
> +                                  union hdmi_infoframe *frame, u8 fsel)
> +{
> +       u8 buffer[ZX_HDMI_INFOFRAME_SIZE];
> +       u8 val;
> +       ssize_t num;
> +       int i;
> +
> +       hdmi_writeb(hdmi, TPI_INFO_FSEL, fsel);
> +
> +       num = hdmi_infoframe_pack(frame, buffer, ZX_HDMI_INFOFRAME_SIZE);
> +       if (num < 0)
> +               return num;
> +
> +       for (i = 0; i < num; i++)
> +               hdmi_writeb(hdmi, TPI_INFO_B0 + i, buffer[i]);
> +
> +       val = hdmi_readb(hdmi, TPI_INFO_EN);
> +       val |= TPI_INFO_TRANS_EN | TPI_INFO_TRANS_RPT;
> +       hdmi_writeb(hdmi, TPI_INFO_EN, val);
> +
> +       return num;
> +}
> +
> +static int zx_hdmi_config_video_vsi(struct zx_hdmi *hdmi,
> +                                   struct drm_display_mode *mode)
> +{
> +       union hdmi_infoframe frame;
> +       int ret;
> +
> +       ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
> +                                                         mode);
> +       if (ret)
> +               return ret;
> +
> +       return zx_hdmi_infoframe_trans(hdmi, &frame, FSEL_VSIF);
> +}
> +
> +static int zx_hdmi_config_video_avi(struct zx_hdmi *hdmi,
> +                                   struct drm_display_mode *mode)
> +{
> +       union hdmi_infoframe frame;
> +       int ret;
> +
> +       ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode);
> +       if (ret)
> +               return ret;
> +
> +       /* We always use YUV444 for HDMI output. */
> +       frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
> +
> +       return zx_hdmi_infoframe_trans(hdmi, &frame, FSEL_AVI);
> +}
> +
> +static void zx_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> +                                    struct drm_display_mode *mode,
> +                                    struct drm_display_mode *adj_mode)
> +{
> +       struct zx_hdmi *hdmi = to_zx_hdmi(encoder);
> +
> +       if (hdmi->sink_is_hdmi) {
> +               zx_hdmi_config_video_avi(hdmi, mode);
> +               zx_hdmi_config_video_vsi(hdmi, mode);
> +       }
> +}
> +
> +static void zx_hdmi_encoder_enable(struct drm_encoder *encoder)
> +{
> +       struct zx_hdmi *hdmi = to_zx_hdmi(encoder);
> +
> +       vou_inf_enable(hdmi->inf);
> +}
> +
> +static void zx_hdmi_encoder_disable(struct drm_encoder *encoder)
> +{
> +       struct zx_hdmi *hdmi = to_zx_hdmi(encoder);
> +
> +       vou_inf_disable(hdmi->inf);
> +}
> +
> +static const struct drm_encoder_helper_funcs zx_hdmi_encoder_helper_funcs = {
> +       .enable = zx_hdmi_encoder_enable,
> +       .disable = zx_hdmi_encoder_disable,
> +       .mode_set = zx_hdmi_encoder_mode_set,
> +};
> +
> +static const struct drm_encoder_funcs zx_hdmi_encoder_funcs = {
> +       .destroy = drm_encoder_cleanup,
> +};
> +
> +static int zx_hdmi_get_edid_block(void *data, u8 *buf, unsigned int block,
> +                                 size_t len)
> +{
> +       struct zx_hdmi *hdmi = data;
> +       int retry = 0;
> +       int ret = 0;
> +       int i = 0;
> +       u8 val;
> +
> +       /* Enable DDC master access */
> +       val = hdmi_readb(hdmi, TPI_DDC_MASTER_EN);
> +       val |= HW_DDC_MASTER;
> +       hdmi_writeb(hdmi, TPI_DDC_MASTER_EN, val);
> +
> +       hdmi_writeb(hdmi, ZX_DDC_ADDR, 0xa0);
> +       hdmi_writeb(hdmi, ZX_DDC_OFFSET, block * EDID_LENGTH);
> +       /* Bits [9:8] of bytes */
> +       hdmi_writeb(hdmi, ZX_DDC_DIN_CNT2, (len >> 8) & 0xff);
> +       /* Bits [7:0] of bytes */
> +       hdmi_writeb(hdmi, ZX_DDC_DIN_CNT1, len & 0xff);
> +
> +       /* Clear FIFO */
> +       val = hdmi_readb(hdmi, ZX_DDC_CMD);
> +       val &= ~DDC_CMD_MASK;
> +       val |= DDC_CMD_CLEAR_FIFO;
> +       hdmi_writeb(hdmi, ZX_DDC_CMD, val);
> +
> +       /* Kick off the read */
> +       val = hdmi_readb(hdmi, ZX_DDC_CMD);
> +       val &= ~DDC_CMD_MASK;
> +       val |= DDC_CMD_SEQUENTIAL_READ;
> +       hdmi_writeb(hdmi, ZX_DDC_CMD, val);
> +
> +       while (len > 0) {
> +               int cnt, j;
> +
> +               /* FIFO needs some time to get ready */
> +               usleep_range(500, 1000);
> +
> +               cnt = hdmi_readb(hdmi, ZX_DDC_DOUT_CNT) & DDC_DOUT_CNT_MASK;
> +               if (cnt == 0) {
> +                       if (++retry > 5) {
> +                               dev_err(hdmi->dev, "DDC read timed out!");
> +                               ret = -ETIMEDOUT;
> +                               break;
> +                       }
> +                       continue;
> +               }
> +
> +               for (j = 0; j < cnt; j++)
> +                       buf[i++] = hdmi_readb(hdmi, ZX_DDC_DATA);
> +               len -= cnt;
> +       }
> +
> +       /* Disable DDC master access */
> +       val = hdmi_readb(hdmi, TPI_DDC_MASTER_EN);
> +       val &= ~HW_DDC_MASTER;
> +       hdmi_writeb(hdmi, TPI_DDC_MASTER_EN, val);
> +
> +       return ret;
> +}
> +
> +static int zx_hdmi_connector_get_modes(struct drm_connector *connector)
> +{
> +       struct zx_hdmi *hdmi = to_zx_hdmi(connector);
> +       struct edid *edid;
> +       int ret = 0;
> +
> +       edid = drm_do_get_edid(connector, zx_hdmi_get_edid_block, hdmi);
> +       if (edid) {
> +               hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
> +               hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
> +               drm_mode_connector_update_edid_property(connector, edid);
> +               ret = drm_add_edid_modes(connector, edid);
> +               kfree(edid);
> +       }
> +
> +       return ret;
> +}
> +
> +static enum drm_mode_status
> +zx_hdmi_connector_mode_valid(struct drm_connector *connector,
> +                            struct drm_display_mode *mode)
> +{
> +       return MODE_OK;
> +}
> +
> +static struct drm_connector_helper_funcs zx_hdmi_connector_helper_funcs = {
> +       .get_modes = zx_hdmi_connector_get_modes,
> +       .mode_valid = zx_hdmi_connector_mode_valid,
> +};
> +
> +static enum drm_connector_status
> +zx_hdmi_connector_detect(struct drm_connector *connector, bool force)
> +{
> +       struct zx_hdmi *hdmi = to_zx_hdmi(connector);
> +
> +       return (hdmi_readb(hdmi, TPI_HPD_RSEN) & TPI_HPD_CONNECTION) ?
> +               connector_status_connected : connector_status_disconnected;
> +}
> +
> +static void zx_hdmi_connector_destroy(struct drm_connector *connector)
> +{
> +       drm_connector_unregister(connector);
> +       drm_connector_cleanup(connector);
> +}
> +
> +static const struct drm_connector_funcs zx_hdmi_connector_funcs = {
> +       .dpms = drm_atomic_helper_connector_dpms,
> +       .fill_modes = drm_helper_probe_single_connector_modes,
> +       .detect = zx_hdmi_connector_detect,
> +       .destroy = zx_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 int zx_hdmi_register(struct drm_device *drm, struct zx_hdmi *hdmi)
> +{
> +       struct drm_encoder *encoder = &hdmi->encoder;
> +
> +       encoder->possible_crtcs = VOU_CRTC_MASK;
> +
> +       drm_encoder_init(drm, encoder, &zx_hdmi_encoder_funcs,
> +                        DRM_MODE_ENCODER_TMDS, NULL);
> +       drm_encoder_helper_add(encoder, &zx_hdmi_encoder_helper_funcs);
> +
> +       hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
> +
> +       drm_connector_init(drm, &hdmi->connector, &zx_hdmi_connector_funcs,
> +                          DRM_MODE_CONNECTOR_HDMIA);
> +       drm_connector_helper_add(&hdmi->connector,
> +                                &zx_hdmi_connector_helper_funcs);
> +
> +       drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
> +       drm_connector_register(&hdmi->connector);
> +
> +       return 0;
> +}
> +
> +static irqreturn_t zx_hdmi_irq_thread(int irq, void *dev_id)
> +{
> +       struct zx_hdmi *hdmi = dev_id;
> +
> +       drm_helper_hpd_irq_event(hdmi->connector.dev);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t zx_hdmi_irq_handler(int irq, void *dev_id)
> +{
> +       struct zx_hdmi *hdmi = dev_id;
> +       u8 lstat;
> +
> +       lstat = hdmi_readb(hdmi, L1_INTR_STAT);
> +
> +       /* Monitor detect/HPD interrupt */
> +       if (lstat & L1_INTR_STAT_INTR1) {
> +               u8 stat = hdmi_readb(hdmi, INTR1_STAT);
> +
> +               hdmi_writeb(hdmi, INTR1_STAT, stat);
> +               if (stat & INTR1_MONITOR_DETECT)
> +                       return IRQ_WAKE_THREAD;
> +       }
> +
> +       return IRQ_NONE;
> +}
> +
> +static void zx_hdmi_phy_start(struct zx_hdmi *hdmi)
> +{
> +       /* Copy from ZTE BSP code */
> +       hdmi_writeb(hdmi, 0x222, 0x0);
> +       hdmi_writeb(hdmi, 0x224, 0x4);
> +       hdmi_writeb(hdmi, 0x909, 0x0);
> +       hdmi_writeb(hdmi, 0x7b0, 0x90);
> +       hdmi_writeb(hdmi, 0x7b1, 0x00);
> +       hdmi_writeb(hdmi, 0x7b2, 0xa7);
> +       hdmi_writeb(hdmi, 0x7b8, 0xaa);
> +       hdmi_writeb(hdmi, 0x7b2, 0xa7);
> +       hdmi_writeb(hdmi, 0x7b3, 0x0f);
> +       hdmi_writeb(hdmi, 0x7b4, 0x0f);
> +       hdmi_writeb(hdmi, 0x7b5, 0x55);
> +       hdmi_writeb(hdmi, 0x7b7, 0x03);
> +       hdmi_writeb(hdmi, 0x7b9, 0x12);
> +       hdmi_writeb(hdmi, 0x7ba, 0x32);
> +       hdmi_writeb(hdmi, 0x7bc, 0x68);
> +       hdmi_writeb(hdmi, 0x7be, 0x40);
> +       hdmi_writeb(hdmi, 0x7bf, 0x84);
> +       hdmi_writeb(hdmi, 0x7c1, 0x0f);
> +       hdmi_writeb(hdmi, 0x7c8, 0x02);
> +       hdmi_writeb(hdmi, 0x7c9, 0x03);
> +       hdmi_writeb(hdmi, 0x7ca, 0x40);
> +       hdmi_writeb(hdmi, 0x7dc, 0x31);
> +       hdmi_writeb(hdmi, 0x7e2, 0x04);
> +       hdmi_writeb(hdmi, 0x7e0, 0x06);
> +       hdmi_writeb(hdmi, 0x7cb, 0x68);
> +       hdmi_writeb(hdmi, 0x7f9, 0x02);
> +       hdmi_writeb(hdmi, 0x7b6, 0x02);
> +       hdmi_writeb(hdmi, 0x7f3, 0x0);
> +}
> +
> +static void zx_hdmi_hw_init(struct zx_hdmi *hdmi)
> +{
> +       u8 val;
> +
> +       /* Software reset */
> +       hdmi_writeb(hdmi, PWD_SRST, 1);
> +
> +       /* Enable pclk */
> +       val = hdmi_readb(hdmi, CLKPWD);
> +       val |= CLKPWD_PDIDCK;
> +       hdmi_writeb(hdmi, CLKPWD, val);
> +
> +       /* Enable HDMI for TX */
> +       val = hdmi_readb(hdmi, FUNC_SEL);
> +       val |= FUNC_HDMI_EN;
> +       hdmi_writeb(hdmi, FUNC_SEL, val);
> +
> +       /* Enable deep color packet */
> +       val = hdmi_readb(hdmi, P2T_CTRL);
> +       val |= P2T_DC_PKT_EN;
> +       hdmi_writeb(hdmi, P2T_CTRL, val);
> +
> +       /* Enable HDMI/MHL mode for output */
> +       val = hdmi_readb(hdmi, TEST_TXCTRL);
> +       val |= TEST_TXCTRL_HDMI_MODE;
> +       hdmi_writeb(hdmi, TEST_TXCTRL, val);
> +
> +       /* Configure reg_qc_sel */
> +       hdmi_writeb(hdmi, HDMICTL4, 0x3);
> +
> +       /* Enable interrupt */
> +       val = hdmi_readb(hdmi, INTR1_MASK);
> +       val |= INTR1_MONITOR_DETECT;
> +       hdmi_writeb(hdmi, INTR1_MASK, val);
> +
> +       /* Clear reset for normal operation */
> +       hdmi_writeb(hdmi, PWD_SRST, 0);
> +
> +       /* Start up phy */
> +       zx_hdmi_phy_start(hdmi);
> +}
> +
> +static int zx_hdmi_bind(struct device *dev, struct device *master, void *data)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct drm_device *drm = data;
> +       struct resource *res;
> +       struct zx_hdmi *hdmi;
> +       struct vou_inf *inf;
> +       int irq;
> +       int ret;
> +
> +       hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
> +       if (!hdmi)
> +               return -ENOMEM;
> +
> +       hdmi->dev = dev;
> +       hdmi->drm = drm;
> +
> +       inf = &vou_inf_hdmi;
> +       inf->encoder = &hdmi->encoder;
> +       hdmi->inf = inf;
> +
> +       dev_set_drvdata(dev, hdmi);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       hdmi->mmio = devm_ioremap_resource(dev, res);
> +       if (IS_ERR(hdmi->mmio))
> +               return PTR_ERR(hdmi->mmio);
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0)
> +               return irq;
> +
> +       hdmi->cec_clk = devm_clk_get(hdmi->dev, "osc_cec");
> +       if (IS_ERR(hdmi->cec_clk))
> +               return PTR_ERR(hdmi->cec_clk);
> +
> +       hdmi->osc_clk = devm_clk_get(hdmi->dev, "osc_clk");
> +       if (IS_ERR(hdmi->osc_clk))
> +               return PTR_ERR(hdmi->osc_clk);
> +
> +       hdmi->xclk = devm_clk_get(hdmi->dev, "xclk");
> +       if (IS_ERR(hdmi->xclk))
> +               return PTR_ERR(hdmi->xclk);
> +
> +       zx_hdmi_hw_init(hdmi);
> +
> +       clk_prepare_enable(hdmi->cec_clk);
> +       clk_prepare_enable(hdmi->osc_clk);
> +       clk_prepare_enable(hdmi->xclk);
> +
> +       ret = zx_hdmi_register(drm, hdmi);
> +       if (ret)
> +               return ret;
> +
> +       ret = devm_request_threaded_irq(dev, irq, zx_hdmi_irq_handler,
> +                                       zx_hdmi_irq_thread, IRQF_SHARED,
> +                                       dev_name(dev), hdmi);
> +
> +       return 0;
> +}
> +
> +static void zx_hdmi_unbind(struct device *dev, struct device *master,
> +                          void *data)
> +{
> +       struct zx_hdmi *hdmi = dev_get_drvdata(dev);
> +
> +       clk_disable_unprepare(hdmi->cec_clk);
> +       clk_disable_unprepare(hdmi->osc_clk);
> +       clk_disable_unprepare(hdmi->xclk);
> +}
> +
> +static const struct component_ops zx_hdmi_component_ops = {
> +       .bind = zx_hdmi_bind,
> +       .unbind = zx_hdmi_unbind,
> +};
> +
> +static int zx_hdmi_probe(struct platform_device *pdev)
> +{
> +       return component_add(&pdev->dev, &zx_hdmi_component_ops);
> +}
> +
> +static int zx_hdmi_remove(struct platform_device *pdev)
> +{
> +       component_del(&pdev->dev, &zx_hdmi_component_ops);
> +       return 0;
> +}
> +
> +static const struct of_device_id zx_hdmi_of_match[] = {
> +       { .compatible = "zte,zx296718-hdmi", },
> +       { /* end */ },
> +};
> +MODULE_DEVICE_TABLE(of, zx_hdmi_of_match);
> +
> +struct platform_driver zx_hdmi_driver = {
> +       .probe = zx_hdmi_probe,
> +       .remove = zx_hdmi_remove,
> +       .driver = {
> +               .name = "zx-hdmi",
> +               .of_match_table = zx_hdmi_of_match,
> +       },
> +};
> diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c
> new file mode 100644
> index 000000000000..7d1f3bbed210
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_plane.c
> @@ -0,0 +1,362 @@
> +/*
> + * Copyright 2016 Linaro Ltd.
> + * Copyright 2016 ZTE Corporation.
> + *
> + * 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 <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_modeset_helper_vtables.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "zx_crtc.h"
> +#include "zx_plane.h"
> +
> +/* GL registers */
> +#define GL_CTRL0                       0x00
> +#define GL_UPDATE                      BIT(5)
> +#define GL_CTRL1                       0x04
> +#define GL_DATA_FMT_SHIFT              0
> +#define GL_DATA_FMT_MASK               (0xf << GL_DATA_FMT_SHIFT)
> +#define GL_FMT_ARGB8888                        0
> +#define GL_FMT_RGB888                  1
> +#define GL_FMT_RGB565                  2
> +#define GL_FMT_ARGB1555                        3
> +#define GL_FMT_ARGB4444                        4
> +#define GL_CTRL2                       0x08
> +#define GL_GLOBAL_ALPHA_SHIFT          8
> +#define GL_GLOBAL_ALPHA_MASK           (0xff << GL_GLOBAL_ALPHA_SHIFT)
> +#define GL_CTRL3                       0x0c
> +#define GL_SCALER_BYPASS_MODE          BIT(0)
> +#define GL_STRIDE                      0x18
> +#define GL_ADDR                                0x1c
> +#define GL_SRC_SIZE                    0x38
> +#define GL_SRC_W_SHIFT                 16
> +#define GL_SRC_W_MASK                  (0x3fff << GL_SRC_W_SHIFT)
> +#define GL_SRC_H_SHIFT                 0
> +#define GL_SRC_H_MASK                  (0x3fff << GL_SRC_H_SHIFT)
> +#define GL_POS_START                   0x9c
> +#define GL_POS_END                     0xa0
> +#define GL_POS_X_SHIFT                 16
> +#define GL_POS_X_MASK                  (0x1fff << GL_POS_X_SHIFT)
> +#define GL_POS_Y_SHIFT                 0
> +#define GL_POS_Y_MASK                  (0x1fff << GL_POS_Y_SHIFT)
> +
> +/* CSC registers */
> +#define CSC_CTRL0                      0x30
> +#define CSC_COV_MODE_SHIFT             16
> +#define CSC_COV_MODE_MASK              (0xffff << CSC_COV_MODE_SHIFT)
> +#define CSC_BT601_IMAGE_RGB2YCBCR      0
> +#define CSC_BT601_IMAGE_YCBCR2RGB      1
> +#define CSC_BT601_VIDEO_RGB2YCBCR      2
> +#define CSC_BT601_VIDEO_YCBCR2RGB      3
> +#define CSC_BT709_IMAGE_RGB2YCBCR      4
> +#define CSC_BT709_IMAGE_YCBCR2RGB      5
> +#define CSC_BT709_VIDEO_RGB2YCBCR      6
> +#define CSC_BT709_VIDEO_YCBCR2RGB      7
> +#define CSC_BT2020_IMAGE_RGB2YCBCR     8
> +#define CSC_BT2020_IMAGE_YCBCR2RGB     9
> +#define CSC_BT2020_VIDEO_RGB2YCBCR     10
> +#define CSC_BT2020_VIDEO_YCBCR2RGB     11
> +#define CSC_WORK_ENABLE                        BIT(0)
> +
> +/* RSZ registers */
> +#define RSZ_SRC_CFG                    0x00
> +#define RSZ_DEST_CFG                   0x04
> +#define RSZ_ENABLE_CFG                 0x14
> +
> +/* HBSC registers */
> +#define HBSC_SATURATION                        0x00
> +#define HBSC_HUE                       0x04
> +#define HBSC_BRIGHT                    0x08
> +#define HBSC_CONTRAST                  0x0c
> +#define HBSC_THRESHOLD_COL1            0x10
> +#define HBSC_THRESHOLD_COL2            0x14
> +#define HBSC_THRESHOLD_COL3            0x18
> +#define HBSC_CTRL0                     0x28
> +#define HBSC_CTRL_EN                   BIT(2)
> +
> +struct zx_plane {
> +       struct drm_plane plane;
> +       void __iomem *layer;
> +       void __iomem *csc;
> +       void __iomem *hbsc;
> +       void __iomem *rsz;
> +};
> +
> +#define to_zx_plane(plane)     container_of(plane, struct zx_plane, plane)
> +
> +static const uint32_t gl_formats[] = {
> +       DRM_FORMAT_ARGB8888,
> +       DRM_FORMAT_XRGB8888,
> +       DRM_FORMAT_RGB888,
> +       DRM_FORMAT_RGB565,
> +       DRM_FORMAT_ARGB1555,
> +       DRM_FORMAT_ARGB4444,
> +};
> +
> +static int zx_gl_plane_atomic_check(struct drm_plane *plane,
> +                                   struct drm_plane_state *state)
> +{
> +       u32 src_w, src_h;
> +
> +       src_w = state->src_w >> 16;
> +       src_h = state->src_h >> 16;
> +
> +       /* TODO: support scaling of the plane source */
> +       if ((src_w != state->crtc_w) || (src_h != state->crtc_h))
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +static int zx_gl_get_fmt(uint32_t format)
> +{
> +       switch (format) {
> +       case DRM_FORMAT_ARGB8888:
> +       case DRM_FORMAT_XRGB8888:
> +               return GL_FMT_ARGB8888;
> +       case DRM_FORMAT_RGB888:
> +               return GL_FMT_RGB888;
> +       case DRM_FORMAT_RGB565:
> +               return GL_FMT_RGB565;
> +       case DRM_FORMAT_ARGB1555:
> +               return GL_FMT_ARGB1555;
> +       case DRM_FORMAT_ARGB4444:
> +               return GL_FMT_ARGB4444;
> +       default:
> +               WARN_ONCE(1, "invalid pixel format %d\n", format);
> +               return -EINVAL;
> +       }
> +}
> +
> +static inline void zx_gl_set_update(struct zx_plane *zplane)
> +{
> +       void __iomem *layer = zplane->layer;
> +       u32 val;
> +
> +       val = readl(layer + GL_CTRL0);
> +       val |= GL_UPDATE;
> +       writel(val, layer + GL_CTRL0);
> +}
> +
> +static inline void zx_gl_rsz_set_update(struct zx_plane *zplane)
> +{
> +       writel(1, zplane->rsz + RSZ_ENABLE_CFG);
> +}
> +
> +void zx_plane_set_update(struct drm_plane *plane)
> +{
> +       struct zx_plane *zplane = to_zx_plane(plane);
> +
> +       zx_gl_rsz_set_update(zplane);
> +       zx_gl_set_update(zplane);
> +}
> +
> +static void zx_gl_rsz_setup(struct zx_plane *zplane, u32 src_w, u32 src_h,
> +                           u32 dst_w, u32 dst_h)
> +{
> +       void __iomem *rsz = zplane->rsz;
> +       u32 val;
> +
> +       val = ((src_h - 1) & 0xffff) << 16;
> +       val |= (src_w - 1) & 0xffff;
> +       writel(val, rsz + RSZ_SRC_CFG);
> +
> +       val = ((dst_h - 1) & 0xffff) << 16;
> +       val |= (dst_w - 1) & 0xffff;
> +       writel(val, rsz + RSZ_DEST_CFG);
> +
> +       zx_gl_rsz_set_update(zplane);
> +}
> +
> +static void zx_gl_plane_atomic_update(struct drm_plane *plane,
> +                                     struct drm_plane_state *old_state)
> +{
> +       struct zx_plane *zplane = to_zx_plane(plane);
> +       struct drm_framebuffer *fb = plane->state->fb;
> +       struct drm_gem_cma_object *cma_obj;
> +       void __iomem *layer = zplane->layer;
> +       void __iomem *csc = zplane->csc;
> +       void __iomem *hbsc = zplane->hbsc;
> +       u32 src_x, src_y, src_w, src_h;
> +       u32 dst_x, dst_y, dst_w, dst_h;
> +       unsigned int depth, bpp;
> +       uint32_t format;
> +       dma_addr_t paddr;
> +       u32 stride;
> +       int fmt;
> +       u32 val;
> +
> +       if (!fb)
> +               return;
> +
> +       format = fb->pixel_format;
> +       stride = fb->pitches[0];
> +
> +       src_x = plane->state->src_x >> 16;
> +       src_y = plane->state->src_y >> 16;
> +       src_w = plane->state->src_w >> 16;
> +       src_h = plane->state->src_h >> 16;
> +
> +       dst_x = plane->state->crtc_x;
> +       dst_y = plane->state->crtc_y;
> +       dst_w = plane->state->crtc_w;
> +       dst_h = plane->state->crtc_h;
> +
> +       drm_fb_get_bpp_depth(format, &depth, &bpp);
> +
> +       cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
> +       paddr = cma_obj->paddr + fb->offsets[0];
> +       paddr += src_y * stride + src_x * bpp / 8;
> +       writel(paddr, layer + GL_ADDR);
> +
> +       /* Set up source height/width register */
> +       val = (src_w << GL_SRC_W_SHIFT) & GL_SRC_W_MASK;
> +       val |= (src_h << GL_SRC_H_SHIFT) & GL_SRC_H_MASK;
> +       writel(val, layer + GL_SRC_SIZE);
> +
> +       /* Set up start position register */
> +       val = (dst_x << GL_POS_X_SHIFT) & GL_POS_X_MASK;
> +       val |= (dst_y << GL_POS_Y_SHIFT) & GL_POS_Y_MASK;
> +       writel(val, layer + GL_POS_START);
> +
> +       /* Set up end position register */
> +       val = ((dst_x + dst_w) << GL_POS_X_SHIFT) & GL_POS_X_MASK;
> +       val |= ((dst_y + dst_h) << GL_POS_Y_SHIFT) & GL_POS_Y_MASK;
> +       writel(val, layer + GL_POS_END);
> +
> +       /* Set up stride register */
> +       writel(stride & 0xffff, layer + GL_STRIDE);
> +
> +       /* Set up graphic layer data format */
> +       fmt = zx_gl_get_fmt(format);
> +       if (fmt >= 0) {
> +               val = readl(layer + GL_CTRL1);
> +               val &= ~GL_DATA_FMT_MASK;
> +               val |= fmt << GL_DATA_FMT_SHIFT;
> +               writel(val, layer + GL_CTRL1);
> +       }
> +
> +       /* Initialize global alpha with a sane value */
> +       val = readl(layer + GL_CTRL2);
> +       val &= ~GL_GLOBAL_ALPHA_MASK;
> +       val |= 0xff << GL_GLOBAL_ALPHA_SHIFT;
> +       writel(val, layer + GL_CTRL2);
> +
> +       /* Setup CSC for the GL */
> +       val = readl(csc + CSC_CTRL0);
> +       val &= ~CSC_COV_MODE_MASK;
> +       if (dst_h > 720)
> +               val |= CSC_BT709_IMAGE_RGB2YCBCR << CSC_COV_MODE_SHIFT;
> +       else
> +               val |= CSC_BT601_IMAGE_RGB2YCBCR << CSC_COV_MODE_SHIFT;
> +       val |= CSC_WORK_ENABLE;
> +       writel(val, csc + CSC_CTRL0);
> +
> +       /* Always use scaler since it exists */
> +       val = readl(layer + GL_CTRL3);
> +       val |= GL_SCALER_BYPASS_MODE;   /* set for not bypass */
> +       writel(val, layer + GL_CTRL3);
> +
> +       zx_gl_rsz_setup(zplane, src_w, src_h, dst_w, dst_h);
> +
> +       /* Enable HBSC block */
> +       val = readl(hbsc + HBSC_CTRL0);
> +       val |= HBSC_CTRL_EN;
> +       writel(val, hbsc + HBSC_CTRL0);
> +
> +       zx_gl_set_update(zplane);
> +}
> +
> +static const struct drm_plane_helper_funcs zx_gl_plane_helper_funcs = {
> +       .atomic_check = zx_gl_plane_atomic_check,
> +       .atomic_update = zx_gl_plane_atomic_update,
> +};
> +
> +static void zx_plane_destroy(struct drm_plane *plane)
> +{
> +       drm_plane_helper_disable(plane);
> +       drm_plane_cleanup(plane);
> +}
> +
> +static const struct drm_plane_funcs zx_plane_funcs = {
> +       .update_plane = drm_atomic_helper_update_plane,
> +       .disable_plane = drm_atomic_helper_disable_plane,
> +       .destroy = zx_plane_destroy,
> +       .reset = drm_atomic_helper_plane_reset,
> +       .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +};
> +
> +static void zx_plane_hbsc_init(struct zx_plane *zplane)
> +{
> +       void __iomem *hbsc = zplane->hbsc;
> +
> +       /*
> +        *  Initialize HBSC block with a sane configuration per recommedation
> +        *  from ZTE BSP code.
> +        */
> +       writel(0x200, hbsc + HBSC_SATURATION);
> +       writel(0x0, hbsc + HBSC_HUE);
> +       writel(0x0, hbsc + HBSC_BRIGHT);
> +       writel(0x200, hbsc + HBSC_CONTRAST);
> +
> +       writel((0x3ac << 16) | 0x40, hbsc + HBSC_THRESHOLD_COL1);
> +       writel((0x3c0 << 16) | 0x40, hbsc + HBSC_THRESHOLD_COL2);
> +       writel((0x3c0 << 16) | 0x40, hbsc + HBSC_THRESHOLD_COL3);
> +}
> +
> +struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev,
> +                               void __iomem *mmio, struct zx_layer_data *data,
> +                               enum drm_plane_type type)
> +{
> +       const struct drm_plane_helper_funcs *helper;
> +       struct zx_plane *zplane;
> +       struct drm_plane *plane;
> +       const uint32_t *formats;
> +       unsigned int format_count;
> +       int ret;
> +
> +       zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL);
> +       if (!zplane)
> +               return ERR_PTR(-ENOMEM);
> +
> +       plane = &zplane->plane;
> +
> +       zplane->layer = mmio + data->layer_offset;
> +       zplane->hbsc = mmio + data->hbsc_offset;
> +       zplane->csc = mmio + data->csc_offset;
> +       zplane->rsz = mmio + data->rsz_offset;
> +
> +       zx_plane_hbsc_init(zplane);
> +
> +       switch (type) {
> +       case DRM_PLANE_TYPE_PRIMARY:
> +               helper = &zx_gl_plane_helper_funcs;
> +               formats = gl_formats;
> +               format_count = ARRAY_SIZE(gl_formats);
> +               break;
> +       case DRM_PLANE_TYPE_OVERLAY:
> +               /* TODO: add video layer (vl) support */
> +               break;
> +       default:
> +               return ERR_PTR(-ENODEV);
> +       }
> +
> +       ret = drm_universal_plane_init(drm, plane, VOU_CRTC_MASK,
> +                                      &zx_plane_funcs, formats, format_count,
> +                                      type, NULL);
> +       if (ret)
> +               return ERR_PTR(ret);
> +
> +       drm_plane_helper_add(plane, helper);
> +
> +       return plane;
> +}
> diff --git a/drivers/gpu/drm/zte/zx_plane.h b/drivers/gpu/drm/zte/zx_plane.h
> new file mode 100644
> index 000000000000..84a4e32cfc24
> --- /dev/null
> +++ b/drivers/gpu/drm/zte/zx_plane.h
> @@ -0,0 +1,26 @@
> +/*
> + * Copyright 2016 Linaro Ltd.
> + * Copyright 2016 ZTE Corporation.
> + *
> + * 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 __ZX_PLANE_H__
> +#define __ZX_PLANE_H__
> +
> +struct zx_layer_data {
> +       u16 layer_offset;
> +       u16 csc_offset;
> +       u16 hbsc_offset;
> +       u16 rsz_offset;
> +};
> +
> +struct drm_plane *zx_plane_init(struct drm_device *drm, struct device *dev,
> +                               void __iomem *mmio, struct zx_layer_data *data,
> +                               enum drm_plane_type type);
> +void zx_plane_set_update(struct drm_plane *plane);
> +
> +#endif /* __ZX_PLANE_H__ */
> --
> 1.9.1
>



-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply

* [PATCH v3] gpio: Added zynq specific check for special pins on bank zero
From: Nava kishore Manne @ 2016-09-23 11:26 UTC (permalink / raw)
  To: linux-arm-kernel

From: Nava kishore Manne <nava.manne@xilinx.com>

This patch adds zynq specific check for bank 0 pins 7 and 8
are special and cannot be used as inputs

Signed-off-by: Nava kishore Manne <navam@xilinx.com>
Reported-by: Jonas Karlsson <Jonas.d.karlsson@gmail.com>
Acked-by: S?ren Brinkmann <soren.brinkmann@xilinx.com>
Acked-by: Michal Simek <michal.simek@xilinx.com>
---
Changes for v3:
		-Fixed some minor comments.
Changes for v2:
                -Removed un-used quirks for zynqmp.

 drivers/gpio/gpio-zynq.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index e72794e..6b4d10d 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -96,6 +96,9 @@
 /* GPIO upper 16 bit mask */
 #define ZYNQ_GPIO_UPPER_MASK 0xFFFF0000
 
+/* For GPIO quirks */
+#define ZYNQ_GPIO_QUIRK_FOO	BIT(0)
+
 /**
  * struct zynq_gpio - gpio device private data structure
  * @chip:	instance of the gpio_chip
@@ -122,6 +125,7 @@ struct zynq_gpio {
 */
 struct zynq_platform_data {
 	const char *label;
+	u32 quirks;
 	u16 ngpio;
 	int max_bank;
 	int bank_min[ZYNQMP_GPIO_MAX_BANK];
@@ -238,13 +242,19 @@ static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin,
 static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
 {
 	u32 reg;
+	bool is_zynq_gpio;
 	unsigned int bank_num, bank_pin_num;
 	struct zynq_gpio *gpio = gpiochip_get_data(chip);
 
+	is_zynq_gpio = gpio->p_data->quirks & ZYNQ_GPIO_QUIRK_FOO;
 	zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio);
 
-	/* bank 0 pins 7 and 8 are special and cannot be used as inputs */
-	if (bank_num == 0 && (bank_pin_num == 7 || bank_pin_num == 8))
+	/*
+	 * On zynq bank 0 pins 7 and 8 are special and cannot be used
+	 * as inputs.
+	 */
+	if (is_zynq_gpio && bank_num == 0 &&
+		(bank_pin_num == 7 || bank_pin_num == 8))
 		return -EINVAL;
 
 	/* clear the bit in direction mode reg to set the pin as input */
@@ -627,6 +637,7 @@ static const struct zynq_platform_data zynqmp_gpio_def = {
 
 static const struct zynq_platform_data zynq_gpio_def = {
 	.label = "zynq_gpio",
+	.quirks = ZYNQ_GPIO_QUIRK_FOO,
 	.ngpio = ZYNQ_GPIO_NR_GPIOS,
 	.max_bank = ZYNQ_GPIO_MAX_BANK,
 	.bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(),
-- 
2.1.2

^ permalink raw reply related

* [PATCH v4] devicetree: bindings: uart: Add new compatible string for ZynqMP
From: Nava kishore Manne @ 2016-09-23 11:25 UTC (permalink / raw)
  To: linux-arm-kernel

From: Nava kishore Manne <nava.manne@xilinx.com>

This patch Adds the new compatible string for ZynqMP.

Signed-off-by: Nava kishore Manne <navam@xilinx.com>
---
Changes for v4:
		-Modified the ChangeLog comment.
Changes for v3:
		-Added changeLog comment.
Changes for v2:
                -None

 Documentation/devicetree/bindings/serial/cdns,uart.txt | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/serial/cdns,uart.txt b/Documentation/devicetree/bindings/serial/cdns,uart.txt
index a3eb154..4fe7aae 100644
--- a/Documentation/devicetree/bindings/serial/cdns,uart.txt
+++ b/Documentation/devicetree/bindings/serial/cdns,uart.txt
@@ -1,7 +1,8 @@
 Binding for Cadence UART Controller
 
 Required properties:
-- compatible : should be "cdns,uart-r1p8", or "xlnx,xuartps"
+- compatible : should be "cdns,uart-r1p8", or "xlnx,xuartps" for Zynq and
+               "cdns,uart-r1p12" for Zynq Ultrascale+ MPSoC uart controller.
 - reg: Should contain UART controller registers location and length.
 - interrupts: Should contain UART controller interrupts.
 - clocks: Must contain phandles to the UART clocks
-- 
2.1.2

^ permalink raw reply related

* [PATCH v6 1/7] arm/arm64: vgic-new: Implement support for userspace access
From: Christoffer Dall @ 2016-09-23 11:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <57E4FAEE.3070003@arm.com>

On Fri, Sep 23, 2016 at 10:50:38AM +0100, Marc Zyngier wrote:
> On 22/09/16 15:01, Vijay Kilari wrote:
> > On Thu, Sep 22, 2016 at 5:38 PM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> >> On 20/09/16 07:12, vijay.kilari at gmail.com wrote:
> >>> From: Vijaya Kumar K <Vijaya.Kumar@cavium.com>
> >>> +static int vgic_uaccess_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
> >>> +                          gpa_t addr, u32 *val)
> >>> +{
> >>> +     struct vgic_io_device *iodev = kvm_to_vgic_iodev(dev);
> >>> +     const struct vgic_register_region *region;
> >>> +     struct kvm_vcpu *r_vcpu;
> >>> +
> >>> +     region = vgic_get_mmio_region(iodev, addr, sizeof(u32));
> >>> +     if (!region) {
> >>> +             *val = 0;
> >>> +             return 0;
> >>
> >> This is not the previous semantic of vgic_uaccess, and I cannot see why
> >> blindly ignoring an access to an undefined region would be acceptable.
> >> What am I missing?
> > 
> > AFAIK, the vgic_uaccess is not making any check on undefined region/register.
> > However, dispatch_mmio_read/write are returning 0 if check of region is failed
> 
> Hmmm. Fair enough. I don't really like it, but that's something for
> another day.
> 
Agreed, we should raise an error in that case, but it's independent of
this series.

-Christoffer

^ permalink raw reply

* [PATCH v6] KVM: arm/arm64: Route vtimer events to user space
From: Alexander Graf @ 2016-09-23 11:07 UTC (permalink / raw)
  To: linux-arm-kernel

We have 2 modes for dealing with interrupts in the ARM world. We can either
handle them all using hardware acceleration through the vgic or we can emulate
a gic in user space and only drive CPU IRQ pins from there.

Unfortunately, when driving IRQs from user space, we never tell user space
about timer events that may result in interrupt line state changes, so we
lose out on timer events if we run with user space gic emulation.

This patch fixes that by exposing kvm's view of the vtimer irq line to user
space and giving it a chance to sync that when it changes.

With this patch I can successfully run edk2 and Linux with user space gic
emulation.

Signed-off-by: Alexander Graf <agraf@suse.de>

---

v1 -> v2:

  - Add back curly brace that got lost

v2 -> v3:

  - Split into patch set

v3 -> v4:

  - Improve documentation

v4 -> v5:

  - Rewrite to use pending state sync in sregs (marc)
  - Remove redundant checks of vgic_initialized()

v5 -> v6:

  - s/pending/asserted/
  - split kvm_timer_flush_hwstate function
  - remove user_timer_asserted
  - rename kernel_timer_asserted to timer_irq_level
  - exit only once per level change
  - qemu tree to try this out: https://github.com/agraf/qemu.git no-kvm-irqchip-for-v6
---
 Documentation/virtual/kvm/api.txt |  27 ++++++++
 arch/arm/include/uapi/asm/kvm.h   |   2 +
 arch/arm/kvm/arm.c                |  26 ++++----
 arch/arm64/include/uapi/asm/kvm.h |   2 +
 include/kvm/arm_arch_timer.h      |   4 +-
 include/uapi/linux/kvm.h          |   6 ++
 virt/kvm/arm/arch_timer.c         | 133 +++++++++++++++++++++++++++++++-------
 7 files changed, 164 insertions(+), 36 deletions(-)

diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 739db9a..20ab558 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -3928,3 +3928,30 @@ In order to use SynIC, it has to be activated by setting this
 capability via KVM_ENABLE_CAP ioctl on the vcpu fd. Note that this
 will disable the use of APIC hardware virtualization even if supported
 by the CPU, as it's incompatible with SynIC auto-EOI behavior.
+
+8.3 KVM_CAP_ARM_TIMER
+
+Architectures: arm, arm64
+This capability, if KVM_CHECK_EXTENSION indicates that it is available and no
+in-kernel interrupt controller is in use, means that that the kernel populates
+the vcpu's run->s.regs.timer_irq_level field with timers that are currently
+considered asserted by kvm.
+
+If active, kvm guarantees at least one exit happens after it detects an
+assertion change. This exit could either be a KVM_EXIT_INTR or any other exit
+event, like KVM_EXIT_MMIO. This way kvm gives user space a chance to update its
+own interrupt asserted status. This usually involves triggering an interrupt
+line on a user space emulated interrupt controller, so user space should always
+check the state of run->s.regs.timer_irq_level on every kvm exit.
+
+The field run->s.regs.timer_irq_level is available independent of
+run->kvm_valid_regs or run->kvm_dirty_regs bits. If no in-kernel interrupt
+controller is used and the capability exists, it will always be available and
+populated.
+
+Currently the following bits are defined for the timer_irq_level bitmap:
+
+    KVM_ARM_TIMER_VTIMER  -  virtual timer
+
+Future versions of kvm may implement additional timer events. These will get
+indicated by additional KVM_CAP extensions.
diff --git a/arch/arm/include/uapi/asm/kvm.h b/arch/arm/include/uapi/asm/kvm.h
index a2b3eb3..ad61b4f 100644
--- a/arch/arm/include/uapi/asm/kvm.h
+++ b/arch/arm/include/uapi/asm/kvm.h
@@ -105,6 +105,8 @@ struct kvm_debug_exit_arch {
 };
 
 struct kvm_sync_regs {
+	/* Used with KVM_CAP_ARM_TIMER */
+	u8 timer_irq_level;
 };
 
 struct kvm_arch_memory_slot {
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index 75f130e..4bb1a22 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -187,6 +187,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_ARM_PSCI_0_2:
 	case KVM_CAP_READONLY_MEM:
 	case KVM_CAP_MP_STATE:
+	case KVM_CAP_ARM_TIMER:
 		r = 1;
 		break;
 	case KVM_CAP_COALESCED_MMIO:
@@ -474,13 +475,7 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
 			return ret;
 	}
 
-	/*
-	 * Enable the arch timers only if we have an in-kernel VGIC
-	 * and it has been properly initialized, since we cannot handle
-	 * interrupts from the virtual timer with a userspace gic.
-	 */
-	if (irqchip_in_kernel(kvm) && vgic_initialized(kvm))
-		ret = kvm_timer_enable(vcpu);
+	ret = kvm_timer_enable(vcpu);
 
 	return ret;
 }
@@ -549,7 +544,7 @@ static int kvm_vcpu_initialized(struct kvm_vcpu *vcpu)
  */
 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
-	int ret;
+	int ret, timer_ret;
 	sigset_t sigsaved;
 
 	if (unlikely(!kvm_vcpu_initialized(vcpu)))
@@ -588,7 +583,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 		 */
 		preempt_disable();
 		kvm_pmu_flush_hwstate(vcpu);
-		kvm_timer_flush_hwstate(vcpu);
+		timer_ret = kvm_timer_flush_hwstate(vcpu);
 		kvm_vgic_flush_hwstate(vcpu);
 
 		local_irq_disable();
@@ -596,7 +591,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 		/*
 		 * Re-check atomic conditions
 		 */
-		if (signal_pending(current)) {
+		if (timer_ret || signal_pending(current)) {
 			ret = -EINTR;
 			run->exit_reason = KVM_EXIT_INTR;
 		}
@@ -659,13 +654,22 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 		 * interrupt line.
 		 */
 		kvm_pmu_sync_hwstate(vcpu);
-		kvm_timer_sync_hwstate(vcpu);
+		timer_ret = kvm_timer_sync_hwstate(vcpu);
 
 		kvm_vgic_sync_hwstate(vcpu);
 
 		preempt_enable();
 
 		ret = handle_exit(vcpu, run, ret);
+
+		if ((ret == 1) && timer_ret) {
+			/*
+			 * We have to exit straight away to ensure that we only
+			 * ever notify user space once about a level change
+			 */
+			ret = -EINTR;
+			run->exit_reason = KVM_EXIT_INTR;
+		}
 	}
 
 	if (vcpu->sigset_active)
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index 3051f86..411d62a 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -143,6 +143,8 @@ struct kvm_debug_exit_arch {
 #define KVM_GUESTDBG_USE_HW		(1 << 17)
 
 struct kvm_sync_regs {
+	/* Used with KVM_CAP_ARM_TIMER */
+	u8 timer_irq_level;
 };
 
 struct kvm_arch_memory_slot {
diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h
index dda39d8..dd0d649 100644
--- a/include/kvm/arm_arch_timer.h
+++ b/include/kvm/arm_arch_timer.h
@@ -63,8 +63,8 @@ void kvm_timer_init(struct kvm *kvm);
 int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,
 			 const struct kvm_irq_level *irq);
 void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu);
-void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu);
-void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu);
+int kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu);
+int kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu);
 void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu);
 
 u64 kvm_arm_timer_get_reg(struct kvm_vcpu *, u64 regid);
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 300ef25..10d63c5 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -870,6 +870,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_S390_USER_INSTR0 130
 #define KVM_CAP_MSI_DEVID 131
 #define KVM_CAP_PPC_HTM 132
+#define KVM_CAP_ARM_TIMER 133
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -1327,4 +1328,9 @@ struct kvm_assigned_msix_entry {
 #define KVM_X2APIC_API_USE_32BIT_IDS            (1ULL << 0)
 #define KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK  (1ULL << 1)
 
+/* Available with KVM_CAP_ARM_TIMER */
+
+/* Bits for run->s.regs.{user,kernel}_timer_asserted */
+#define KVM_ARM_TIMER_VTIMER		(1 << 0)
+
 #endif /* __LINUX_KVM_H */
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index 4309b60..3612fa0 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -166,26 +166,57 @@ bool kvm_timer_should_fire(struct kvm_vcpu *vcpu)
 	return cval <= now;
 }
 
-static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level)
+/*
+ * Synchronize the timer IRQ state with the interrupt controller.
+ *
+ * Returns:
+ *
+ *    0  - success
+ *    1  - need exit to user space
+ */
+static int kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level)
 {
 	int ret;
 	struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
 
-	BUG_ON(!vgic_initialized(vcpu->kvm));
-
 	timer->active_cleared_last = false;
 	timer->irq.level = new_level;
-	trace_kvm_timer_update_irq(vcpu->vcpu_id, timer->irq.irq,
+	trace_kvm_timer_update_irq(vcpu->vcpu_id, host_vtimer_irq,
 				   timer->irq.level);
-	ret = kvm_vgic_inject_mapped_irq(vcpu->kvm, vcpu->vcpu_id,
-					 timer->irq.irq,
-					 timer->irq.level);
-	WARN_ON(ret);
+
+	if (irqchip_in_kernel(vcpu->kvm)) {
+		BUG_ON(!vgic_initialized(vcpu->kvm));
+
+		/* Fire the timer in the VGIC */
+		ret = kvm_vgic_inject_mapped_irq(vcpu->kvm, vcpu->vcpu_id,
+						 timer->irq.irq,
+						 timer->irq.level);
+
+		WARN_ON(ret);
+	} else {
+		struct kvm_sync_regs *regs = &vcpu->run->s.regs;
+
+		/* Populate the timer bitmap for user space */
+		regs->timer_irq_level &= ~KVM_ARM_TIMER_VTIMER;
+		if (new_level)
+			regs->timer_irq_level |= KVM_ARM_TIMER_VTIMER;
+
+		/* Exit to user space to notify it about the line change */
+		return 1;
+	}
+
+	return 0;
 }
 
 /*
  * Check if there was a change in the timer state (should we raise or lower
  * the line level to the GIC).
+ *
+ * Returns:
+ *
+ *   <0  - error
+ *    0  - success
+ *    1  - need exit to user space
  */
 static int kvm_timer_update_state(struct kvm_vcpu *vcpu)
 {
@@ -197,11 +228,12 @@ static int kvm_timer_update_state(struct kvm_vcpu *vcpu)
 	 * because the guest would never see the interrupt.  Instead wait
 	 * until we call this function from kvm_timer_flush_hwstate.
 	 */
-	if (!vgic_initialized(vcpu->kvm) || !timer->enabled)
+	if ((irqchip_in_kernel(vcpu->kvm) && !vgic_initialized(vcpu->kvm)) ||
+	    !timer->enabled)
 		return -ENODEV;
 
 	if (kvm_timer_should_fire(vcpu) != timer->irq.level)
-		kvm_timer_update_irq(vcpu, !timer->irq.level);
+		return kvm_timer_update_irq(vcpu, !timer->irq.level);
 
 	return 0;
 }
@@ -242,22 +274,12 @@ void kvm_timer_unschedule(struct kvm_vcpu *vcpu)
 	timer_disarm(timer);
 }
 
-/**
- * kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu
- * @vcpu: The vcpu pointer
- *
- * Check if the virtual timer has expired while we were running in the host,
- * and inject an interrupt if that was the case.
- */
-void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
+static void kvm_timer_flush_hwstate_vgic(struct kvm_vcpu *vcpu)
 {
 	struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
 	bool phys_active;
 	int ret;
 
-	if (kvm_timer_update_state(vcpu))
-		return;
-
 	/*
 	* If we enter the guest with the virtual input level to the VGIC
 	* asserted, then we have already told the VGIC what we need to, and
@@ -309,14 +331,69 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
 	timer->active_cleared_last = !phys_active;
 }
 
+static void kvm_timer_flush_hwstate_user(struct kvm_vcpu *vcpu)
+{
+	struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+
+	/*
+	 * As long as the timer is asserted, we do not need to get new host
+	 * timer events to know whether the line is still up. Since the GIC
+	 * is handled in user space, we will get an exit on EOI, so we can
+	 * resynchronize the status there.
+	 */
+	if (timer->irq.level)
+		disable_percpu_irq(host_vtimer_irq);
+	else
+		enable_percpu_irq(host_vtimer_irq, 0);
+}
+
+/**
+ * kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu
+ * @vcpu: The vcpu pointer
+ *
+ * Check if the virtual timer has expired while we were running in the host,
+ * and inject an interrupt if that was the case.
+ *
+ * Returns:
+ *
+ *    0  - success
+ *    1  - need exit to user space
+ */
+int kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
+{
+	switch (kvm_timer_update_state(vcpu)) {
+	case 0:
+		/* All is good, we can sync */
+		break;
+	case 1:
+		/* We need to exit to user space */
+		return 1;
+	default:
+		/* An error occured, skip the sync */
+		return 0;
+	}
+
+	if (irqchip_in_kernel(vcpu->kvm))
+		kvm_timer_flush_hwstate_vgic(vcpu);
+	else
+		kvm_timer_flush_hwstate_user(vcpu);
+
+	return 0;
+}
+
 /**
  * kvm_timer_sync_hwstate - sync timer state from cpu
  * @vcpu: The vcpu pointer
  *
  * Check if the virtual timer has expired while we were running in the guest,
  * and inject an interrupt if that was the case.
+ *
+ * Returns:
+ *
+ *    0  - success
+ *    1  - need exit to user space
  */
-void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
+int kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
 {
 	struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
 
@@ -326,7 +403,12 @@ void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
 	 * The guest could have modified the timer registers or the timer
 	 * could have expired, update the timer state.
 	 */
-	kvm_timer_update_state(vcpu);
+	if (kvm_timer_update_state(vcpu) == 1) {
+		/* We should go back to user space */
+		return 1;
+	}
+
+	return 0;
 }
 
 int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,
@@ -479,6 +561,10 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
 	if (timer->enabled)
 		return 0;
 
+	/* No need to route physical IRQs when we don't use the vgic */
+	if (!irqchip_in_kernel(vcpu->kvm))
+		goto no_vgic;
+
 	/*
 	 * Find the physical IRQ number corresponding to the host_vtimer_irq
 	 */
@@ -502,6 +588,7 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
 	if (ret)
 		return ret;
 
+no_vgic:
 
 	/*
 	 * There is a potential race here between VCPUs starting for the first
-- 
1.8.5.6

^ permalink raw reply related

* [PATCH V6 3/5] PCI: thunder-pem: Allow to probe PEM-specific register range for ACPI case
From: Gabriele Paoloni @ 2016-09-23 10:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20160923101145.GA31120@red-moon>

Hi Lorenzo

> -----Original Message-----
> From: linux-kernel-owner at vger.kernel.org [mailto:linux-kernel-
> owner at vger.kernel.org] On Behalf Of Lorenzo Pieralisi
> Sent: 23 September 2016 11:12
> To: Bjorn Helgaas
> Cc: Gabriele Paoloni; Ard Biesheuvel; Tomasz Nowicki; David Daney; Will
> Deacon; Catalin Marinas; Rafael Wysocki; Arnd Bergmann; Hanjun Guo;
> Sinan Kaya; Jayachandran C; Christopher Covington; Duc Dang; Robert
> Richter; Marcin Wojtas; Liviu Dudau; Wangyijing; Mark Salter; linux-
> pci at vger.kernel.org; linux-arm-kernel at lists.infradead.org; Linaro ACPI
> Mailman List; Jon Masters; Andrea Gallo; Jeremy Linton; liudongdong
> (C); Jeff Hugo; linux-acpi at vger.kernel.org; linux-
> kernel at vger.kernel.org; Rafael J. Wysocki; rui.zhang at intel.com
> Subject: Re: [PATCH V6 3/5] PCI: thunder-pem: Allow to probe PEM-
> specific register range for ACPI case
> 
> [+ Zhang Rui]
> 
> On Thu, Sep 22, 2016 at 05:10:42PM -0500, Bjorn Helgaas wrote:
> > On Thu, Sep 22, 2016 at 01:31:01PM -0500, Bjorn Helgaas wrote:
> > > On Thu, Sep 22, 2016 at 01:44:46PM +0100, Lorenzo Pieralisi wrote:
> > > > On Thu, Sep 22, 2016 at 11:10:13AM +0000, Gabriele Paoloni wrote:
> > > > > Hi Lorenzo, Bjorn
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Lorenzo Pieralisi [mailto:lorenzo.pieralisi at arm.com]
> > > > > > Sent: 22 September 2016 10:50
> > > > > > To: Bjorn Helgaas
> > > > > > Cc: Ard Biesheuvel; Tomasz Nowicki; David Daney; Will Deacon;
> Catalin
> > > > > > Marinas; Rafael Wysocki; Arnd Bergmann; Hanjun Guo; Sinan
> Kaya;
> > > > > > Jayachandran C; Christopher Covington; Duc Dang; Robert
> Richter; Marcin
> > > > > > Wojtas; Liviu Dudau; Wangyijing; Mark Salter; linux-
> > > > > > pci at vger.kernel.org; linux-arm-kernel at lists.infradead.org;
> Linaro ACPI
> > > > > > Mailman List; Jon Masters; Andrea Gallo; Jeremy Linton;
> liudongdong
> > > > > > (C); Gabriele Paoloni; Jeff Hugo; linux-acpi at vger.kernel.org;
> linux-
> > > > > > kernel at vger.kernel.org; Rafael J. Wysocki
> > > > > > Subject: Re: [PATCH V6 3/5] PCI: thunder-pem: Allow to probe
> PEM-
> > > > > > specific register range for ACPI case
> > > > > >
> > > > > > On Wed, Sep 21, 2016 at 01:04:57PM -0500, Bjorn Helgaas
> wrote:
> > > > > > > On Wed, Sep 21, 2016 at 03:05:49PM +0100, Lorenzo Pieralisi
> wrote:
> > > > > > > > On Tue, Sep 20, 2016 at 02:17:44PM -0500, Bjorn Helgaas
> wrote:
> > > > > > > > > On Tue, Sep 20, 2016 at 04:09:25PM +0100, Ard
> Biesheuvel wrote:
> > > > > > > >
> > > > > > > > [...]
> > > > > > > >
> > > > > > > > > > None of these platforms can be fixed entirely in
> software, and
> > > > > > given
> > > > > > > > > > that we will not be adding quirks for new broken
> hardware, we
> > > > > > should
> > > > > > > > > > ask ourselves whether having two versions of a quirk,
> i.e., one
> > > > > > for
> > > > > > > > > > broken hardware + currently shipping firmware, and
> one for the
> > > > > > same
> > > > > > > > > > broken hardware with fixed firmware is really an
> improvement
> > > > > > over what
> > > > > > > > > > has been proposed here.
> > > > > > > > >
> > > > > > > > > We're talking about two completely different types of
> quirks:
> > > > > > > > >
> > > > > > > > >   1) MCFG quirks to use memory-mapped config space that
> doesn't
> > > > > > quite
> > > > > > > > >      conform to the ECAM model in the PCIe spec, and
> > > > > > > > >
> > > > > > > > >   2) Some yet-to-be-determined method to describe
> address space
> > > > > > > > >      consumed by a bridge.
> > > > > > > > >
> > > > > > > > > The first two patches of this series are a nice
> implementation
> > > > > > for 1).
> > > > > > > > > The third patch (ThunderX-specific) is one possibility
> for 2),
> > > > > > but I
> > > > > > > > > don't like it because there's no way for generic
> software like
> > > > > > the
> > > > > > > > > ACPI core to discover these resources.
> > > > > > > >
> > > > > > > > Ok, so basically this means that to implement (2) we need
> to assign
> > > > > > > > some sort of _HID to these quirky PCI bridges (so that we
> know what
> > > > > > > > device they represent and we can retrieve their _CRS). I
> take from
> > > > > > > > this discussion that the goal is to make sure that all
> non-config
> > > > > > > > resources have to be declared through _CRS device
> objects, which is
> > > > > > > > fine but that requires a FW update (unless we can
> fabricate ACPI
> > > > > > > > devices and corresponding _CRS in the kernel whenever we
> match a
> > > > > > > > given MCFG table signature).
> > > > > > >
> > > > > > > All resources consumed by ACPI devices should be declared
> through
> > > > > > > _CRS.  If you want to fabricate ACPI devices or _CRS via
> kernel
> > > > > > > quirks, that's fine with me.  This could be triggered via
> MCFG
> > > > > > > signature, DMI info, host bridge _HID, etc.
> > > > > >
> > > > > > I think the PNP quirk approach + PNP0c02 resource put forward
> by Gab
> > > > > > is enough.
> > > > >
> > > > > Great thanks as we take a final decision I will ask Dogndgong
> to submit
> > > > > another RFC based on this approach
> > > > >
> > > > > >
> > > > > > > > We discussed this already and I think we should make a
> decision:
> > > > > > > >
> > > > > > > > http://lists.infradead.org/pipermail/linux-arm-
> kernel/2016-
> > > > > > March/414722.html
> > > > > > > >
> > > > > > > > > > > I'd like to step back and come up with some
> understanding of
> > > > > > how
> > > > > > > > > > > non-broken firmware *should* deal with this issue.
> Then, if
> > > > > > we *do*
> > > > > > > > > > > work around this particular broken firmware in the
> kernel, it
> > > > > > would be
> > > > > > > > > > > nice to do it in a way that fits in with that
> understanding.
> > > > > > > > > > >
> > > > > > > > > > > For example, if a companion ACPI device is the
> preferred
> > > > > > solution, an
> > > > > > > > > > > ACPI quirk could fabricate a device with the
> required
> > > > > > resources.  That
> > > > > > > > > > > would address the problem closer to the source and
> make it
> > > > > > more likely
> > > > > > > > > > > that the rest of the system will work correctly:
> /proc/iomem
> > > > > > could
> > > > > > > > > > > make sense, things that look at _CRS generically
> would work
> > > > > > (e.g,
> > > > > > > > > > > /sys/, an admittedly hypothetical "lsacpi", etc.)
> > > > > > > > > > >
> > > > > > > > > > > Hard-coding stuff in drivers is a point solution
> that doesn't
> > > > > > provide
> > > > > > > > > > > any guidance for future platforms and makes it
> likely that
> > > > > > the hack
> > > > > > > > > > > will get copied into even more drivers.
> > > > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > OK, I see. But the guidance for future platforms
> should be 'do
> > > > > > not
> > > > > > > > > > rely on quirks', and what I am arguing here is that
> the more we
> > > > > > polish
> > > > > > > > > > up this code and make it clean and reusable, the more
> likely it
> > > > > > is
> > > > > > > > > > that will end up getting abused by new broken
> hardware that we
> > > > > > set out
> > > > > > > > > > to reject entirely in the first place.
> > > > > > > > > >
> > > > > > > > > > So of course, if the quirk involves claiming
> resources, let's
> > > > > > make
> > > > > > > > > > sure that this occurs in the cleanest and most
> compliant way
> > > > > > possible.
> > > > > > > > > > But any factoring/reuse concerns other than for the
> current
> > > > > > crop of
> > > > > > > > > > broken hardware should be avoided imo.
> > > > > > > > >
> > > > > > > > > If future hardware is completely ECAM-compliant and we
> don't need
> > > > > > any
> > > > > > > > > more MCFG quirks, that would be great.
> > > > > > > >
> > > > > > > > Yes.
> > > > > > > >
> > > > > > > > > But we'll still need to describe that memory-mapped
> config space
> > > > > > > > > somewhere.  If that's done with PNP0C02 or similar
> devices (as is
> > > > > > done
> > > > > > > > > on my x86 laptop), we'd be all set.
> > > > > > > >
> > > > > > > > I am not sure I understand what you mean here. Are you
> referring
> > > > > > > > to MCFG regions reported as PNP0c02 resources through its
> _CRS ?
> > > > > > >
> > > > > > > Yes.  PCI Firmware Spec r3.0, Table 4-2, note 2 says
> address ranges
> > > > > > > reported via MCFG or _CBA should be reserved by _CRS of a
> PNP0C02
> > > > > > > device.
> > > > > >
> > > > > > Ok, that's agreed. It goes without saying that since you are
> quoting
> > > > > > the PCI spec, if FW fails to report MCFG regions in a PNP0c02
> device
> > > > > > _CRS I will consider that a FW bug.
> > > > > >
> > > > > > > > IIUC PNP0C02 is a reservation mechanism, but it does not
> help us
> > > > > > > > associate its _CRS to a specific PCI host bridge
> instance, right ?
> > > > > > >
> > > > > > > Gab proposed a hierarchy that *would* associate a PNP0C02
> device with
> > > > > > > a PCI bridge:
> > > > > > >
> > > > > > >   Device (PCI1) {
> > > > > > >     Name (_HID, "HISI0080") // PCI Express Root Bridge
> > > > > > >     Name (_CID, "PNP0A03") // Compatible PCI Root Bridge
> > > > > > >     Method (_CRS, 0, Serialized) { // Root complex
> resources
> > > > > > (windows) }
> > > > > > >     Device (RES0) {
> > > > > > >       Name (_HID, "HISI0081") // HiSi PCIe RC config base
> address
> > > > > > >       Name (_CID, "PNP0C02")  // Motherboard reserved
> resource
> > > > > > >       Name (_CRS, ResourceTemplate () { ... }
> > > > > > >     }
> > > > > > >   }
> > > > > > >
> > > > > > > That's a possibility.  The PCI Firmware Spec suggests
> putting RES0 at
> > > > > > > the root (under \_SB), but I don't know why.
> > > > > > >
> > > > > > > Putting it at the root means we couldn't generically
> associate it
> > > > > > with
> > > > > > > a bridge, although I could imagine something like this:
> > > > > > >
> > > > > > >   Device (RES1) {
> > > > > > >     Name (_HID, "HISI0081") // HiSi PCIe RC config base
> address
> > > > > > >     Name (_CID, "PNP0C02")  // Motherboard reserved
> resource
> > > > > > >     Name (_CRS, ResourceTemplate () { ... }
> > > > > > >     Method (BRDG) { "PCI1" }  // hand-wavy ASL
> > > > > > >   }
> > > > > > >   Device (PCI1) {
> > > > > > >     Name (_HID, "HISI0080") // PCI Express Root Bridge
> > > > > > >     Name (_CID, "PNP0A03") // Compatible PCI Root Bridge
> > > > > > >     Method (_CRS, 0, Serialized) { // Root complex
> resources
> > > > > > (windows) }
> > > > > > >   }
> > > > > > >
> > > > > > > Where you could search PNP0C02 devices for a cookie that
> matched the
> > > > > > > host bridge.o
> > > > > >
> > > > > > Ok, I am fine with both and I think we are converging, but
> the way
> > > > > > to solve this problem has to be uniform for all ARM partners
> (and
> > > > > > not only ARM). Two points here:
> > > > > >
> > > > > > 1) Adding a device/subdevice allows people to add a _CRS
> reporting the
> > > > > >    non-window bridge resources. Fine. It also allows people
> to chuck in
> > > > > >    there all sorts of _DSD properties to describe their PCI
> host bridge
> > > > > >    as it is done with DT properties (those _DSD can contain
> eg clocks
> > > > > >    etc.), this may be tempting (so that they can reuse the
> same DT
> > > > > >    driver and do not have to update their firmware) but I
> want to be
> > > > > >    clear here: that must not happen. So, a subdevice with a
> _CRS to
> > > > > >    report resources, yes, but it will stop there.
> > > > > > 2) It is unclear to me how to formalize the above. People
> should not
> > > > > >    write FW by reading the PCI mailing list, so these
> guidelines have
> > > > > > to
> > > > > >    be written, somehow. I do not want to standardize quirks,
> I want
> > > > > >    to prevent random ACPI table content, which is different.
> > > > > >    Should I report this to the ACPI spec working group ? If
> we do
> > > > > >    not do that everyone will go solve this problem as they
> deem fit.
> > > > > >
> > > > >
> > > > > Do we really need to formalize this?
> > > > >
> > > > > As we discussed in the Linaro call at the moment we have few
> vendors
> > > > > that need quirks and we want to avoid promoting/accepting
> quirks for
> > > > > the future.
> > > > >
> > > > > At the time of the call I think we decided to informally accept
> a set
> > > > > of quirks for the current platforms and reject any other quirk
> coming
> > > > > after a certain date/kernel version (this to be decided).
> > > > >
> > > > > I am not sure if there is a way to document/formalize a
> temporary
> > > > > exception from the rule...
> > > >
> > > > - (1) will be enforced.
> > >
> > > I'm not sure it's necessary or possible to enforce a "no future
> > > quirks" rule.  For one thing, there's already a pretty strong
> > > incentive to avoid quirks: if your hardware doesn't require quirks,
> > > it works with OSes already in the field.
> > >
> > > MCFG quirks allow us to use the generic ACPI pci_root.c driver even
> if
> > > the hardware doesn't support ECAM quite according to the spec.
> > >
> > > PNP0C02 usage is a workaround for the failure of the
> Consumer/Producer
> > > bit.  PNP0C02 quirks compensate for firmware that doesn't describe
> > > resource usage accurately.  It's possible the ACPI spec folks could
> > > come up with a better Consumer/Producer workaround, if that's
> needed.
> > > Apparently x86 hasn't needed it yet.
> > >
> > > If people add _DSD methods for clocks or whatnot, the hardware
> won't
> > > work with the generic pci_root.c driver, so there's already an
> > > incentive for avoiding them.  x86 has managed without such methods;
> > > arm64 should be able to do the same.
> >
> > Re-reading this, I'm afraid my response sounds a little dismissive,
> > and I feel like I'm missing some important information.  So I
> > apologize if I missed your whole point, Lorenzo.
> 
> No you are spot on, I just wanted to emphasize, given that we are
> adding an _HID and a subdevice, that developer should not be tempted
> to use it to match against a PCI host driver to reuse the DT code,
> we should not use the quirk mechanism as a backdoor to re-using DT
> drivers in ACPI context.
> 
> Anyway, there is a review process to spot these possible misuses,
> mine was just a heads-up, quirks will happen, I just do not want
> to wreak the standard ACPI PCI firmware model to support them.
> 
> Given that there are already PNP0c02 bindings out there where the
> PNP0c02 is used as in Gab's example:
> 
> https://patchwork.kernel.org/patch/4757111/
> 
> I think the only pending question I have is whether we are allowed
> to define a PNP0A03 subdevice with a _CRS resource space that is
> not contained in its parent _CRS, if we answer this question I
> think we are done.

FMU part of your question is answered in the PCI Firmware specs
https://members.pcisig.com/wg/PCI-SIG/document/download/8232

Where from note 2 of 4.1.2 I quote: 
"For most systems, the motherboard resource would appear at the root
of the ACPI namespace (under \_SB) in a node with a _HID of EISAID 
(PNP0C02), and the resources in this case should not be claimed in the
root PCI bus's _CRS"

My interpretation is that the resource claimed in the PNP0C02 node
must never be in the PNP0A03 _CRS.

Now about having the PNP0C02 node under \_SB or as a sub-device we see
that the note above points out that most of system have it under \_SB
but I read it as a quite relaxed condition....

BTW this is just my interpretation...

Thanks

Gab 


> 
> I will raise the PNP0c02 usage issue with the ASWG anyway.
> 
> Thanks !
> Lorenzo

^ permalink raw reply


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