* [PATCH] arm64: mm: fix __page_to_voff definition
From: Neeraj Upadhyay @ 2016-10-21 8:58 UTC (permalink / raw)
To: linux-arm-kernel
Fix parameter name for __page_to_voff, to match its definition.
At present, we don't see any issue, as page_to_virt's caller
declares 'page'.
Fixes: 9f2875912dac ("arm64: mm: restrict virt_to_page() to the linear mapping")
Signed-off-by: Neeraj Upadhyay <neeraju@codeaurora.org>
---
arch/arm64/include/asm/memory.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index ba62df8..b71086d 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -217,7 +217,7 @@ static inline void *phys_to_virt(phys_addr_t x)
#define _virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
#else
#define __virt_to_pgoff(kaddr) (((u64)(kaddr) & ~PAGE_OFFSET) / PAGE_SIZE * sizeof(struct page))
-#define __page_to_voff(kaddr) (((u64)(page) & ~VMEMMAP_START) * PAGE_SIZE / sizeof(struct page))
+#define __page_to_voff(page) (((u64)(page) & ~VMEMMAP_START) * PAGE_SIZE / sizeof(struct page))
#define page_to_virt(page) ((void *)((__page_to_voff(page)) | PAGE_OFFSET))
#define virt_to_page(vaddr) ((struct page *)((__virt_to_pgoff(vaddr)) | VMEMMAP_START))
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a
member of the Code Aurora Forum, hosted by The Linux Foundation
^ permalink raw reply related
* [PATCH v2 0/2] ARM: oxnas: Add SMP support for OX820
From: Neil Armstrong @ 2016-10-21 8:58 UTC (permalink / raw)
To: linux-arm-kernel
In order to support the SMP feature of the Oxford Semiconductor OX820 SoC,
add the necessary code to handle the wake-up, hotplug and cpu entry.
The OX820 has an ARM11MPCORE cluster with 2 cores and has proper hardware
support for secondary core booting.
Changes since v1 at http://lkml.kernel.org/r/20161017084303.20078-1-narmstrong at baylibre.com
- Remove useless holding pen loops and spinlock in boot_secondary
Neil Armstrong (2):
ARM: oxnas: Add OX820 SMP support
ARM: oxnas: Add OX820 config and makefile entry
arch/arm/Makefile | 1 +
arch/arm/mach-oxnas/Kconfig | 30 ++++++++----
arch/arm/mach-oxnas/Makefile | 2 +
arch/arm/mach-oxnas/headsmp.S | 28 +++++++++++
arch/arm/mach-oxnas/hotplug.c | 111 ++++++++++++++++++++++++++++++++++++++++++
arch/arm/mach-oxnas/platsmp.c | 104 +++++++++++++++++++++++++++++++++++++++
6 files changed, 267 insertions(+), 9 deletions(-)
create mode 100644 arch/arm/mach-oxnas/Makefile
create mode 100644 arch/arm/mach-oxnas/headsmp.S
create mode 100644 arch/arm/mach-oxnas/hotplug.c
create mode 100644 arch/arm/mach-oxnas/platsmp.c
--
2.7.0
^ permalink raw reply
* [PATCH] [media] c8sectpfe: Remove clk_disable_unprepare hacks
From: Peter Griffin @ 2016-10-21 8:55 UTC (permalink / raw)
To: linux-arm-kernel
Now that CLK_PROC_STFE is defined as a critical clock in
DT, we can remove the commented clk_disable_unprepare from
the c8sectpfe driver. This means we now have balanced
clk*enable/disable calls in the driver, but on STiH407
family the clock in reality will never actually be disabled.
This is due to a HW bug where once the IP has been configured
and the SLIM core is running, disabling the clock causes a
unrecoverable bus lockup.
Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
---
drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
index 30c148b..79d793b 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
@@ -888,8 +888,7 @@ static int c8sectpfe_probe(struct platform_device *pdev)
return 0;
err_clk_disable:
- /* TODO uncomment when upstream has taken a reference on this clk */
- /*clk_disable_unprepare(fei->c8sectpfeclk);*/
+ clk_disable_unprepare(fei->c8sectpfeclk);
return ret;
}
@@ -924,11 +923,8 @@ static int c8sectpfe_remove(struct platform_device *pdev)
if (readl(fei->io + SYS_OTHER_CLKEN))
writel(0, fei->io + SYS_OTHER_CLKEN);
- /* TODO uncomment when upstream has taken a reference on this clk */
- /*
if (fei->c8sectpfeclk)
clk_disable_unprepare(fei->c8sectpfeclk);
- */
return 0;
}
--
1.9.1
^ permalink raw reply related
* [PATCH v2 6/6] powerpc: dts: acadia: DT fix s/#interrupts-parent/#interrupt-parent/
From: Geert Uytterhoeven @ 2016-10-21 8:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477039877-20227-1-git-send-email-geert+renesas@glider.be>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Rob Herring <robh@kernel.org>
---
v2:
- Add Acked-by.
---
arch/powerpc/boot/dts/acadia.dts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/powerpc/boot/dts/acadia.dts b/arch/powerpc/boot/dts/acadia.dts
index 57291f61ffe7021a..86266159521edac2 100644
--- a/arch/powerpc/boot/dts/acadia.dts
+++ b/arch/powerpc/boot/dts/acadia.dts
@@ -183,7 +183,7 @@
usb at ef603000 {
compatible = "ohci-be";
reg = <0xef603000 0x80>;
- interrupts-parent = <&UIC0>;
+ interrupt-parent = <&UIC0>;
interrupts = <0xd 0x4 0xe 0x4>;
};
--
1.9.1
^ permalink raw reply related
* [PATCH v2 5/6] dt: booting-without-of: DT fix s/#interrupt-cell/#interrupt-cells/
From: Geert Uytterhoeven @ 2016-10-21 8:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477039877-20227-1-git-send-email-geert+renesas@glider.be>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Rob Herring <robh@kernel.org>
---
v2:
- Add Acked-by.
---
Documentation/devicetree/booting-without-of.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/devicetree/booting-without-of.txt b/Documentation/devicetree/booting-without-of.txt
index 3f1437fbca6b49f3..5bcea91c0cc65af2 100644
--- a/Documentation/devicetree/booting-without-of.txt
+++ b/Documentation/devicetree/booting-without-of.txt
@@ -1302,7 +1302,7 @@ number and level/sense information. All interrupt children in an
OpenPIC interrupt domain use 2 cells per interrupt in their interrupts
property.
-The PCI bus binding specifies a #interrupt-cell value of 1 to encode
+The PCI bus binding specifies a #interrupt-cells value of 1 to encode
which interrupt pin (INTA,INTB,INTC,INTD) is used.
2) interrupt-parent property
--
1.9.1
^ permalink raw reply related
* [PATCH v2 4/6] ASoC: davinci-mcbsp: DT fix s/interrupts-names/interrupt-names/
From: Geert Uytterhoeven @ 2016-10-21 8:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477039877-20227-1-git-send-email-geert+renesas@glider.be>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Rob Herring <robh@kernel.org>
---
v2:
- Add Acked-by,
- Split off from a completely unrelated patch.
---
Documentation/devicetree/bindings/sound/davinci-mcbsp.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/sound/davinci-mcbsp.txt b/Documentation/devicetree/bindings/sound/davinci-mcbsp.txt
index 55b53e1fd72c9d6e..e0b6165c9cfcec19 100644
--- a/Documentation/devicetree/bindings/sound/davinci-mcbsp.txt
+++ b/Documentation/devicetree/bindings/sound/davinci-mcbsp.txt
@@ -43,7 +43,7 @@ mcbsp0: mcbsp at 1d10000 {
<0x00310000 0x1000>;
reg-names = "mpu", "dat";
interrupts = <97 98>;
- interrupts-names = "rx", "tx";
+ interrupt-names = "rx", "tx";
dmas = <&edma0 3 1
&edma0 2 1>;
dma-names = "tx", "rx";
--
1.9.1
^ permalink raw reply related
* [PATCH v2 3/6] arm64: dts: lg1313: DT fix s/#interrupts-cells/#interrupt-cells/
From: Geert Uytterhoeven @ 2016-10-21 8:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477039877-20227-1-git-send-email-geert+renesas@glider.be>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v2:
- New.
---
arch/arm64/boot/dts/lg/lg1313.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/lg/lg1313.dtsi b/arch/arm64/boot/dts/lg/lg1313.dtsi
index e703e1149c757082..abb2162228e82d39 100644
--- a/arch/arm64/boot/dts/lg/lg1313.dtsi
+++ b/arch/arm64/boot/dts/lg/lg1313.dtsi
@@ -123,7 +123,7 @@
amba {
#address-cells = <2>;
#size-cells = <1>;
- #interrupts-cells = <3>;
+ #interrupt-cells = <3>;
compatible = "simple-bus";
interrupt-parent = <&gic>;
--
1.9.1
^ permalink raw reply related
* [PATCH v2 2/6] arm64: dts: lg1312: DT fix s/#interrupts-cells/#interrupt-cells/
From: Geert Uytterhoeven @ 2016-10-21 8:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477039877-20227-1-git-send-email-geert+renesas@glider.be>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Rob Herring <robh@kernel.org>
---
v2:
- Add Acked-by,
- Rebased.
---
arch/arm64/boot/dts/lg/lg1312.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/lg/lg1312.dtsi b/arch/arm64/boot/dts/lg/lg1312.dtsi
index fbafa24cd5335b90..647606a83c517448 100644
--- a/arch/arm64/boot/dts/lg/lg1312.dtsi
+++ b/arch/arm64/boot/dts/lg/lg1312.dtsi
@@ -123,7 +123,7 @@
amba {
#address-cells = <2>;
#size-cells = <1>;
- #interrupts-cells = <3>;
+ #interrupt-cells = <3>;
compatible = "simple-bus";
interrupt-parent = <&gic>;
--
1.9.1
^ permalink raw reply related
* [PATCH v2 1/6] ARM: dts: STiH407: DT fix s/interrupts-names/interrupt-names/
From: Geert Uytterhoeven @ 2016-10-21 8:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477039877-20227-1-git-send-email-geert+renesas@glider.be>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Acked-by: Rob Herring <robh@kernel.org>
---
v2:
- Add Acked-by.
---
arch/arm/boot/dts/stih407-pinctrl.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/stih407-pinctrl.dtsi b/arch/arm/boot/dts/stih407-pinctrl.dtsi
index c325cc059ae4bc80..daab16b5ae645cee 100644
--- a/arch/arm/boot/dts/stih407-pinctrl.dtsi
+++ b/arch/arm/boot/dts/stih407-pinctrl.dtsi
@@ -1157,7 +1157,7 @@
reg = <0x0923f080 0x4>;
reg-names = "irqmux";
interrupts = <GIC_SPI 192 IRQ_TYPE_NONE>;
- interrupts-names = "irqmux";
+ interrupt-names = "irqmux";
ranges = <0 0x09230000 0x3000>;
pio40: gpio at 09230000 {
--
1.9.1
^ permalink raw reply related
* [PATCH v2 0/6] DT: Fix spelling of standard properties
From: Geert Uytterhoeven @ 2016-10-21 8:51 UTC (permalink / raw)
To: linux-arm-kernel
Hi all,
This patch series fixes misspellings of various standard DT properties
in DT binding documentation, DTS files, and error messages.
While most of these are harmless, some of them may cause hard-to-debug
failures.
Changes compared to v2:
- Dropped patches that have been applied already,
- Add Rob Herring's Acked-by,
- Split off "[PATCH v2 4/6] ASoC: davinci-mcbsp: DT fix
s/interrupts-names/interrupt-names/" from the unrelated "[PATCH 06/14]
dmaengine: bcm2835: DT spelling s/interrupts-names/interrupt-names/",
- Add new patch "[PATCH v2 3/6] arm64: dts: lg1313: DT fix
s/#interrupts-cells/#interrupt-cells/".
Please apply where appropriate.
Thanks!
P.S. I used the following to detect misspellings:
words="(address|clock|cooling|dma|gpio|index|interrupt|mbox|msi|nvmem|phy|phys|power-domain|pwm|reset|size|sleep|sound-dai|thermal-sensor)"
git grep -Ew "${words}s-names"
git grep -E "[^-]\<${words}-name\>[^-]"
git grep -Ew "#${words}s-cells" # false positive phys-cells
git grep -E "#${words}-cell\>[^-]"
git grep -w adress-cells
git grep -Ew "interrupts-(map|parent)"
Geert Uytterhoeven (6):
ARM: dts: STiH407: DT fix s/interrupts-names/interrupt-names/
arm64: dts: lg1312: DT fix s/#interrupts-cells/#interrupt-cells/
arm64: dts: lg1313: DT fix s/#interrupts-cells/#interrupt-cells/
ASoC: davinci-mcbsp: DT fix s/interrupts-names/interrupt-names/
dt: booting-without-of: DT fix s/#interrupt-cell/#interrupt-cells/
powerpc: dts: acadia: DT fix s/#interrupts-parent/#interrupt-parent/
Documentation/devicetree/bindings/sound/davinci-mcbsp.txt | 2 +-
Documentation/devicetree/booting-without-of.txt | 2 +-
arch/arm/boot/dts/stih407-pinctrl.dtsi | 2 +-
arch/arm64/boot/dts/lg/lg1312.dtsi | 2 +-
arch/arm64/boot/dts/lg/lg1313.dtsi | 2 +-
arch/powerpc/boot/dts/acadia.dts | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
--
1.9.1
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* [PATCH v2 1/9] irqchip: meson: add support for gpio interrupt controller
From: Jerome Brunet @ 2016-10-21 8:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <ec297647-1ab7-7e6b-5945-be8360f92421@arm.com>
On Thu, 2016-10-20 at 17:33 +0100, Marc Zyngier wrote:
> Jerome,
>
> On 19/10/16 16:21, Jerome Brunet wrote:
> >
> > Add support for the interrupt gpio controller found on Amlogic's
> > meson
> > SoC family.
> >
> > Unlike what the IP name suggest, it is not directly linked to the
> > gpio
> > subsystem. It is actually an independent IP that is able to spy on
> > the
> > SoC pad. For that purpose, it can mux and filter (edge or level and
> > polarity) any single SoC pad to one of the 8 GIC's interrupts it
> > owns.
> >
> > Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
> > ---
> > ?drivers/irqchip/Kconfig??????????|???9 +
> > ?drivers/irqchip/Makefile?????????|???1 +
> > ?drivers/irqchip/irq-meson-gpio.c | 423
> > +++++++++++++++++++++++++++++++++++++++
> > ?3 files changed, 433 insertions(+)
> > ?create mode 100644 drivers/irqchip/irq-meson-gpio.c
> >
> > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> > index 82b0b5daf3f5..168837263e80 100644
> > --- a/drivers/irqchip/Kconfig
> > +++ b/drivers/irqchip/Kconfig
> > @@ -279,3 +279,12 @@ config EZNPS_GIC
> > ?config STM32_EXTI
> > ? bool
> > ? select IRQ_DOMAIN
> > +
> > +config MESON_GPIO_IRQ
> > +???????bool "Meson GPIO Interrupt Multiplexer"
> > +???????depends on ARCH_MESON || COMPILE_TEST
> > +???????select IRQ_DOMAIN
> > +???????select IRQ_DOMAIN_HIERARCHY
> > +???????help
> > +?????????Support Meson SoC Family GPIO Interrupt Multiplexer
> > +
> > diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> > index e4dbfc85abdb..33f913d037d0 100644
> > --- a/drivers/irqchip/Makefile
> > +++ b/drivers/irqchip/Makefile
> > @@ -74,3 +74,4 @@ obj-$(CONFIG_LS_SCFG_MSI) += irq-
> > ls-scfg-msi.o
> > ?obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o
> > ?obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o
> > ?obj-$(CONFIG_STM32_EXTI)? += irq-stm32-exti.o
> > +obj-$(CONFIG_MESON_GPIO_IRQ) += irq-meson-gpio.o
> > diff --git a/drivers/irqchip/irq-meson-gpio.c
> > b/drivers/irqchip/irq-meson-gpio.c
> > new file mode 100644
> > index 000000000000..869b4df8c483
> > --- /dev/null
> > +++ b/drivers/irqchip/irq-meson-gpio.c
> > @@ -0,0 +1,423 @@
> > +/*
> > + * Copyright (c) 2015 Endless Mobile, Inc.
> > + * Author: Carlo Caione <carlo@endlessm.com>
> > + * Copyright (c) 2016 BayLibre, SAS.
> > + * Author: Jerome Brunet <jbrunet@baylibre.com>
> > + *
> > + * This program is free software; you can redistribute it and/or
> > modify
> > + * it under the terms of version 2 of the GNU General Public
> > License as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > but
> > + * WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.??See the
> > GNU
> > + * General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public
> > License
> > + * along with this program; if not, see <http://www.gnu.org/licens
> > es/>.
> > + * The full GNU General Public License is included in this
> > distribution
> > + * in the file called COPYING.
> > + */
> > +
> > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> > +
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/irq.h>
> > +#include <linux/irqdomain.h>
> > +#include <linux/irqchip.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +
> > +#define IRQ_FREE (-1)
> > +
> > +#define REG_EDGE_POL 0x00
> > +#define REG_PIN_03_SEL 0x04
> > +#define REG_PIN_47_SEL 0x08
> > +#define REG_FILTER_SEL 0x0c
> > +
> > +#define REG_EDGE_POL_MASK(x) (BIT(x) | BIT(16 + (x)))
> > +#define REG_EDGE_POL_EDGE(x) BIT(x)
> > +#define REG_EDGE_POL_LOW(x) BIT(16 + (x))
> > +#define REG_PIN_SEL_SHIFT(x) (((x) % 4) * 8)
> > +#define REG_FILTER_SEL_SHIFT(x) ((x) * 4)
> > +
> > +struct meson_gpio_irq_params {
> > + unsigned int nhwirq;
> > + irq_hw_number_t *source;
> > + int nsource;
> > +};
> > +
> > +struct meson_gpio_irq_domain {
>
> The name of that structure is utterly confusing, as it doesn't
> contain
> anything related to an IRQ domain. Can you please clarify its usage?
This structure is holding the data of the controller. The name is
indeed misleading, I will change this. What about
'meson_gpio_irq_pdata' or 'meson_gpio_irq_controller' ?
>
> >
> > + void __iomem *base;
> > + int *map;
> > + const struct meson_gpio_irq_params *params;
> > +};
> > +
> > +struct meson_gpio_irq_chip_data {
> > + void __iomem *base;
> > + int index;
> > +};
> > +
> > +static irq_hw_number_t meson_parent_hwirqs[] = {
> > + 64, 65, 66, 67, 68, 69, 70, 71,
> > +};
>
> If that a guarantee that these numbers will always represent the
> parent interrupt?
At the moment, the 3 supported SoC use these parent interrupts, but we
have absolutely no idea (or guarantee) that is will remain the same, or
even contiguous, in the upcoming SoC (like the GXM or GXL)
I reckon, it is likely that manufacturer will keep on using these
parent irqs for a while but I would prefer not make an assumption about
it in the driver.
If a SoC get a different set of interrupts I would have added a new
table like this and passed it to the appropriate params :
static irq_hw_number_t meson_new_parent_hwirqs[] = {
143, 144, 150, 151, 152, 173, 178, 179,
};
> It feels a bit odd not to get that information directly from
> the device tree, in the form of a device specific property. Something
> like:
>
> upstream-interrupts = <64 65 66 ... >;
>
I wondered about putting this information in DT or in the driver for a
while. Maybe DT would be a more suitable place holder for these data
(parent irq and number of provided hwirq) but I was under the
understanding that we should now put these information in the driver
and use the compatible property to get the appropriate parameters.
I'd love to get the view of the DT guys on this.
Also, since we already need to put some information about the pin the
pinctrl driver (to get the appropriate mux value of each pad), I
thought It would make sense to have the same approach here.
If there is some kind of policy saying this should be in DT, I'd be
happy to make the change.
> or even as a base/range:
>
> upstream-interrupts = <64 8>;
I would prefer to avoid using ranges as we have no guarantee the
manufacturer will keep the parent irqs contiguous in the future.
>
> Also, how does it work if you have more than a single device like
> this?
> This driver seems a do a great deal of dynamic allocation, and yet
> its
> core resource is completely static... Please pick a side! ;-)
I don't think we could have more than one instance per device and I
certainly did not have this case in mind while writing the code. If it
was the case someday, I guess having two compatibles would do the trick
:)
>
> >
> > +
> > +static const struct meson_gpio_irq_params meson8_params = {
> > + .nhwirq??= 134,
> > + .source??= meson_parent_hwirqs,
> > + .nsource = ARRAY_SIZE(meson_parent_hwirqs),
>
> I find it utterly confusing that you are calling source something
> that is an output for this controller.
I had the call sequence (GIC->GPIO_IRQ->handler) in mind while writing
it. I see your point and it is indeed confusing, I'll find something
better
> It makes my brain melt, and I don't like the feeling.
Ohhhh !! it's Halloween season and you don't need a costume anymore ;)
>
> >
> > +};
> > +
> > +static const struct meson_gpio_irq_params meson8b_params = {
> > + .nhwirq??= 119,
> > + .source??= meson_parent_hwirqs,
> > + .nsource = ARRAY_SIZE(meson_parent_hwirqs),
> > +};
> > +
> > +static const struct meson_gpio_irq_params meson_gxbb_params = {
> > + .nhwirq??= 133,
> > + .source??= meson_parent_hwirqs,
> > + .nsource = ARRAY_SIZE(meson_parent_hwirqs),
> > +};
>
> Same thing. How big is the variability of these structures? Are we
> going to see more of those? or is that now set into stone?
The number of pad mapped to the controller seems to change with every
SoC version. The parent irqs have not changed so far, but as explained
above, there is no guarantee it will keep on being this way.
So i'd say probably more of those ...
>
> +Mark: what's the policy to describe this kind of things?
Very interested about this question as well.
>
> >
> > +
> > +static const struct of_device_id meson_irq_gpio_matches[] = {
> > + {
> > + .compatible = "amlogic,meson8-gpio-intc",
>
> If it's an independent IP (as described in the commit message),
> shouldn't in be rescribed in a more SoC-independent way?
Ok, I was probably not very clear (again). What I meant in the cover
letter is that the interrupt gpio controller is independent for the
pad/gpio controller. For example, it could work even you did not setup
anything in pinmux or you did not instantiate pinctrl at all.
But, at least from my point of view, it is SoC dependent since the
number of pad routed to it is changing with every SoC version.
>
> >
> > + .data = &meson8_params
> > + },
> > + {
> > + .compatible = "amlogic,meson8b-gpio-intc",
> > + .data = &meson8b_params
> > + },
> > + {
> > + .compatible = "amlogic,meson-gxbb-gpio-intc",
> > + .data = &meson_gxbb_params
> > + },
> > + {}
> > +};
> > +
> > +static void meson_gpio_irq_update_bits(void __iomem *base,
> > unsigned int reg,
> > + ???????u32 mask, u32 val)
> > +{
> > + u32 tmp;
> > +
> > + tmp = readl(base + reg);
> > + tmp &= ~mask;
> > + tmp |= val;
> > +
> > + writel(tmp, base + reg);
>
> Can't you use the relaxed accessors?
Indeed, this will be fixed.
>
> >
> > +}
> > +
> > +static int meson_gpio_irq_get_index(struct meson_gpio_irq_domain
> > *domain_data,
> > + ????int hwirq)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < domain_data->params->nsource; i++) {
> > + if (domain_data->map[i] == hwirq)
> > + return i;
> > + }
> > +
> > + return -1;
>
> I'm a bit worried by this function. If you need an allocator, then
> having a simple bitmap is much better than iterating over an array.
>
> If you're using this to go from a hwirq to the structure describing
> your
> interrupt, this is wrong. You should never have to look-up something
> based on a hwirq, because that's what irq domains are for.
OK
>
> >
> > +}
> > +
> > +static int mesion_gpio_irq_map_source(struct meson_gpio_irq_domain
> > *domain_data,
> > + ??????irq_hw_number_t hwirq,
> > + ??????irq_hw_number_t *source)
> > +{
> > + int index;
> > + unsigned int reg;
> > +
> > + index = meson_gpio_irq_get_index(domain_data, IRQ_FREE);
>
> So assuming you turn this into a proper bitmap driven allocator...
>
> >
> > + if (index < 0) {
> > + pr_err("No irq available\n");
> > + return -ENOSPC;
> > + }
> > +
> > + domain_data->map[index] = hwirq;
>
> this can go away, as there is hardly any point in tracking the hwirq
> on
> its own. Actually, the whole map[] array looks totally useless.
>
> >
> > +
> > + reg = (index < 4) ? REG_PIN_03_SEL : REG_PIN_47_SEL;
> > + meson_gpio_irq_update_bits(domain_data->base, reg,
> > + ???0xff <<
> > REG_PIN_SEL_SHIFT(index),
> > + ???hwirq <<
> > REG_PIN_SEL_SHIFT(index));
> > +
> > + *source = domain_data->params->source[index];
> > +
> > + pr_debug("hwirq %lu assigned to channel %d - source
> > %lu\n",
> > + ?hwirq, index, *source);
> > +
> > + return index;
> > +}
> > +
> > +static int meson_gpio_irq_type_setup(unsigned int type, void
> > __iomem *base,
> > + ?????int index)
> > +{
> > + u32 val = 0;
> > +
> > + type &= IRQ_TYPE_SENSE_MASK;
> > +
> > + if (type == IRQ_TYPE_EDGE_BOTH)
> > + return -EINVAL;
> > +
> > + if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
> > + val |= REG_EDGE_POL_EDGE(index);
> > +
> > + if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING))
> > + val |= REG_EDGE_POL_LOW(index);
> > +
> > + meson_gpio_irq_update_bits(base, REG_EDGE_POL,
> > + ???REG_EDGE_POL_MASK(index), val);
> > +
> > + return 0;
> > +}
> > +
> > +static unsigned int meson_gpio_irq_type_output(unsigned int type)
> > +{
> > + unsigned int sense = type & IRQ_TYPE_SENSE_MASK;
> > +
> > + type &= ~IRQ_TYPE_SENSE_MASK;
> > +
> > + /*
> > + ?* If the polarity of interrupt is low, the controller
> > will
> > + ?* invert the signal for gic
> > + ?*/
> > + if (sense & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
> > + type |= IRQ_TYPE_LEVEL_HIGH;
> > + else if (sense & (IRQ_TYPE_EDGE_RISING |
> > IRQ_TYPE_EDGE_FALLING))
> > + type |= IRQ_TYPE_EDGE_RISING;
> > +
> > + return type;
> > +}
> > +
> > +static int meson_gpio_irq_set_type(struct irq_data *data, unsigned
> > int type)
> > +{
> > + struct meson_gpio_irq_chip_data *cd =
> > irq_data_get_irq_chip_data(data);
> > + int ret;
> > +
> > + pr_debug("set type of hwirq %lu to %u\n", data->hwirq,
> > type);
> > +
> > + ret = meson_gpio_irq_type_setup(type, cd->base, cd-
> > >index);
> > + if (ret)
> > + return ret;
> > +
> > + return irq_chip_set_type_parent(data,
> > + meson_gpio_irq_type_output
> > (type));
> > +}
> > +
> > +static struct irq_chip meson_gpio_irq_chip = {
> > + .name = "meson-gpio-irqchip",
> > + .irq_mask = irq_chip_mask_parent,
> > + .irq_unmask = irq_chip_unmask_parent,
> > + .irq_eoi = irq_chip_eoi_parent,
> > + .irq_set_type = meson_gpio_irq_set_type,
> > + .irq_retrigger =
> > irq_chip_retrigger_hierarchy,
> > +#ifdef CONFIG_SMP
> > + .irq_set_affinity = irq_chip_set_affinity_parent,
> > +#endif
> > +};
> > +
> > +static int meson_gpio_irq_domain_translate(struct irq_domain
> > *domain,
> > + ???struct irq_fwspec
> > *fwspec,
> > + ???unsigned long *hwirq,
> > + ???unsigned int *type)
> > +{
> > + if (is_of_node(fwspec->fwnode)) {
> > + if (fwspec->param_count != 2)
> > + return -EINVAL;
>
> You can write this as:
>
> if (is_of_node() && fwspec->... == 2) {
OK
>
> >
> > +
> > + *hwirq = fwspec->param[0];
> > + *type = fwspec->param[1];
> > +
> > + return 0;
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static int meson_gpio_irq_allocate_gic_irq(struct irq_domain
> > *domain,
> > + ???unsigned int virq,
> > + ???irq_hw_number_t source,
> > + ???unsigned int type)
> > +{
> > + struct irq_fwspec fwspec;
> > +
> > + if (!irq_domain_get_of_node(domain->parent))
> > + return -EINVAL;
>
> How can you end-up here if the translate operation has failed?
You can't, will be removed
>
> >
> > +
> > + fwspec.fwnode = domain->parent->fwnode;
> > + fwspec.param_count = 3;
> > + fwspec.param[0] = 0; /* SPI */
> > + fwspec.param[1] = source;
> > + fwspec.param[2] = meson_gpio_irq_type_output(type);
> > +
> > + return irq_domain_alloc_irqs_parent(domain, virq, 1,
> > &fwspec);
> > +}
> > +
> > +static int meson_gpio_irq_domain_alloc(struct irq_domain *domain,
> > + ???????unsigned int virq,
> > + ???????unsigned int nr_irqs,
> > + ???????void *data)
> > +{
> > + struct irq_fwspec *fwspec = data;
> > + struct meson_gpio_irq_domain *domain_data = domain-
> > >host_data;
> > + struct meson_gpio_irq_chip_data *cd;
> > + unsigned long hwirq, source;
> > + unsigned int type;
> > + int i, index, ret;
> > +
> > + ret = meson_gpio_irq_domain_translate(domain, fwspec,
> > &hwirq, &type);
> > + if (ret)
> > + return ret;
> > +
> > + pr_debug("irq %d, nr_irqs %d, hwirqs %lu\n", virq,
> > nr_irqs, hwirq);
> > +
> > + for (i = 0; i < nr_irqs; i++) {
>
> This is a pattern that gets repeated over and over, for no good
> reason.
> The only reason we have this nr_irqs thing is to handle things like
> multi-MSI, where we have to *guarantee* that the hwirqs are allocated
> in
> a contiguous manner.
>
> Here, you don't enforce that guarantee, so you'd rather have a big
> fat:
>
> if (WARN_ON(nr_irqs != 1))
> return -EINVAL;
>
> and get rid of that loop, because I cannot imagine a case where you'd
> allocate more than a single interrupt at a time.
Thanks for this clarification. I was actually very confused about
getting a single fwspec and this 'nr_irqs' parameters. I could not
figure a case where one would want multiple irqs in one call, and
looking at the other irqchip drivers did not really help. In the end, I
tried to implement the API the best I could, thinking that somebody
probably had a very good reason for this.
I'm perfectly happy using your solution, it makes more sense.
>
> >
> > + index = mesion_gpio_irq_map_source(domain_data,
> > hwirq + i,
> > + ???&source);
> > + if (index < 0)
> > + return index;
> > +
> > + ret = meson_gpio_irq_type_setup(type, domain_data-
> > >base,
> > + index);
> > + if (ret)
> > + return ret;
>
> Why do you have to to this here? This should be handled by the core
> code already.
OK
>
> >
> > +
> > + cd = kzalloc(sizeof(*cd), GFP_KERNEL);
> > + if (!cd)
> > + return -ENOMEM;
> > +
> > + cd->base = domain_data->base;
> > + cd->index = index;
>
> So what is the exact purpose of this structure? The base address is
> essentially a global, or could be directly derived from the domain.
> The
> index is only used in set_type, and is the index of the pin connected
> to
> the GIC. It looks to me like you could have:
>
> irq_hw_number_t *out_line =
> &meson_parent_hwirqs[index];
meson_parent_hwirq is only declared this way to avoid writing the
parent irqs 3 times (in each SoC params).?
Using it this way would make it global and imply it is the same
whatever the SoC. This something I can't guarantee and I would prefer
to avoid that.
>
> >
> > +
> > + irq_domain_set_hwirq_and_chip(domain, virq + i,
> > hwirq + i,
> > + ??????&meson_gpio_irq_chip
> > , cd);
>
> and this written as
>
> irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
> ??????out_line);
>
> In your set_type function, you just compute the index back:
>
> irq_hw_number_t *out_line = irq_data_get_irq_chip_data(data);
> irq_hw_number_t index = out_line - meson_parent_hwirqs;
>
> and you're done.
>
Since I can derive the base address, I only need the index in set_type.
I'll rework this to get rid of this structure.
> >
> > +
> > + ret = meson_gpio_irq_allocate_gic_irq(domain, virq
> > + i,
> > + ??????source,
> > type);
>
> Resource leak on error.
Ok, Thx.
>
> >
> > + if (ret < 0)
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void meson_gpio_irq_domain_free(struct irq_domain *domain,
> > + ???????unsigned int virq,
> > + ???????unsigned int nr_irqs)
> > +{
> > + struct meson_gpio_irq_domain *domain_data = domain-
> > >host_data;
> > + struct meson_gpio_irq_chip_data *cd;
> > + struct irq_data *irq_data;
> > + int i;
> > +
> > + for (i = 0; i < nr_irqs; i++) {
>
> Same comment as above.
OK
>
> >
> > + irq_data = irq_domain_get_irq_data(domain, virq +
> > i);
> > + cd = irq_data_get_irq_chip_data(irq_data);
> > +
> > + domain_data->map[cd->index] = IRQ_FREE;
> > + kfree(cd);
> > + }
> > +
> > + irq_domain_free_irqs_parent(domain, virq, nr_irqs);
> > +
> > +}
> > +
> > +static const struct irq_domain_ops meson_gpio_irq_domain_ops = {
> > + .alloc = meson_gpio_irq_domain_alloc,
> > + .free = meson_gpio_irq_domain_free,
> > + .translate = meson_gpio_irq_domain_translate,
> > +};
> > +
> > +static int __init
> > +meson_gpio_irq_init_domain(struct device_node *node,
> > + ???struct meson_gpio_irq_domain
> > *domain_data,
> > + ???const struct meson_gpio_irq_params
> > *params)
> > +{
> > + int i;
> > + int nsource = params->nsource;
> > + int *map;
> > +
> > + map = kcalloc(nsource, sizeof(*map), GFP_KERNEL);
> > + if (!map)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < nsource; i++)
> > + map[i] = IRQ_FREE;
> > +
> > + domain_data->map = map;
>
> You should now be able to kill most or all of this.
>
> >
> > + domain_data->params = params;
> > +
> > + return 0;
> > +}
> > +
> > +static int __init meson_gpio_irq_of_init(struct device_node *node,
> > + ?struct device_node
> > *parent)
> > +{
> > + struct irq_domain *domain, *parent_domain;
> > + const struct of_device_id *match;
> > + const struct meson_gpio_irq_params *params;
> > + struct meson_gpio_irq_domain *domain_data;
> > + int ret;
> > +
> > + match = of_match_node(meson_irq_gpio_matches, node);
> > + if (!match)
> > + return -ENODEV;
> > + params = match->data;
> > +
> > + if (!parent) {
> > + pr_err("missing parent interrupt node\n");
> > + return -ENODEV;
> > + }
> > +
> > + parent_domain = irq_find_host(parent);
> > + if (!parent_domain) {
> > + pr_err("unable to obtain parent domain\n");
> > + return -ENXIO;
> > + }
> > +
> > + domain_data = kzalloc(sizeof(*domain_data), GFP_KERNEL);
> > + if (!domain_data)
> > + return -ENOMEM;
> > +
> > + domain_data->base = of_iomap(node, 0);
> > + if (!domain_data->base) {
> > + ret = -ENOMEM;
> > + goto out_free_dev;
> > + }
> > +
> > + ret = meson_gpio_irq_init_domain(node, domain_data,
> > params);
> > + if (ret < 0)
> > + goto out_free_dev_content;
> > +
> > + domain = irq_domain_add_hierarchy(parent_domain, 0,
> > params->nhwirq,
> > + ??node,
> > &meson_gpio_irq_domain_ops,
> > + ??domain_data);
>
> Please be consistent in using the fwnode API instead of the of_node
> one.
OK
>
> >
> > +
> > + if (!domain) {
> > + pr_err("failed to allocated domain\n");
> > + ret = -ENOMEM;
> > + goto out_free_dev_content;
> > + }
> > +
> > + pr_info("%d to %d gpio interrupt mux initialized\n",
> > + params->nhwirq, params->nsource);
> > +
> > + return 0;
> > +
> > +out_free_dev_content:
> > + kfree(domain_data->map);
> > + iounmap(domain_data->base);
> > +
> > +out_free_dev:
> > + kfree(domain_data);
> > +
> > + return ret;
> > +}
> > +
> > +IRQCHIP_DECLARE(meson8_gpio_intc, "amlogic,meson8-gpio-intc",
> > + meson_gpio_irq_of_init);
> > +IRQCHIP_DECLARE(meson8b_gpio_intc, "amlogic,meson8b-gpio-intc",
> > + meson_gpio_irq_of_init);
> > +IRQCHIP_DECLARE(gxbb_gpio_intc, "amlogic,meson-gxbb-gpio-intc",
> > + meson_gpio_irq_of_init);
> >
>
> Overall, this driver is a bit of a mess. Tons of structures that
> don't
> make much sense, and a false sense of being able to support multiple
> instances of the device.
>
> I'll let Mark comment on the DT side of things.
>
> Thanks,
>
> M.
^ permalink raw reply
* [PATCH] net: stmmac: Add OXNAS Glue Driver
From: Neil Armstrong @ 2016-10-21 8:44 UTC (permalink / raw)
To: linux-arm-kernel
Add Synopsys Designware MAC Glue layer for the Oxford Semiconductor OX820.
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
.../devicetree/bindings/net/oxnas-dwmac.txt | 44 +++++
drivers/net/ethernet/stmicro/stmmac/Kconfig | 11 ++
drivers/net/ethernet/stmicro/stmmac/Makefile | 1 +
drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c | 219 +++++++++++++++++++++
4 files changed, 275 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/oxnas-dwmac.txt
create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c
Changes since RFC at https://patchwork.kernel.org/patch/9387257 :
- Drop init/exit callbacks
- Implement proper remove and PM callback
- Call init from probe
- Disable/Unprepare clock if stmmac probe fails
diff --git a/Documentation/devicetree/bindings/net/oxnas-dwmac.txt b/Documentation/devicetree/bindings/net/oxnas-dwmac.txt
new file mode 100644
index 0000000..5d2696c
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/oxnas-dwmac.txt
@@ -0,0 +1,44 @@
+* Oxford Semiconductor OXNAS DWMAC Ethernet controller
+
+The device inherits all the properties of the dwmac/stmmac devices
+described in the file stmmac.txt in the current directory with the
+following changes.
+
+Required properties on all platforms:
+
+- compatible: Depending on the platform this should be one of:
+ - "oxsemi,ox820-dwmac"
+ Additionally "snps,dwmac" and any applicable more
+ detailed version number described in net/stmmac.txt
+ should be used.
+
+- reg: The first register range should be the one of the DWMAC
+ controller.
+
+- clocks: Should contain phandles to the following clocks
+- clock-names: Should contain the following:
+ - "stmmaceth" - see stmmac.txt
+ - "gmac" - peripheral gate clock
+
+- oxsemi,sys-ctrl: a phandle to the system controller syscon node
+
+Example :
+
+etha: ethernet at 40400000 {
+ compatible = "oxsemi,ox820-dwmac", "snps,dwmac";
+ reg = <0x40400000 0x2000>;
+ interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "macirq", "eth_wake_irq";
+ mac-address = [000000000000]; /* Filled in by U-Boot */
+ phy-mode = "rgmii";
+
+ clocks = <&stdclk CLK_820_ETHA>, <&gmacclk>;
+ clock-names = "gmac", "stmmaceth";
+ resets = <&reset RESET_MAC>;
+
+ /* Regmap for sys registers */
+ oxsemi,sys-ctrl = <&sys>;
+
+ status = "disabled";
+};
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index 3818c5e..27ed913 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -62,6 +62,7 @@ config DWMAC_MESON
tristate "Amlogic Meson dwmac support"
default ARCH_MESON
depends on OF && COMMON_CLK && (ARCH_MESON || COMPILE_TEST)
+ select MFD_SYSCON
help
Support for Ethernet controller on Amlogic Meson SoCs.
@@ -69,6 +70,16 @@ config DWMAC_MESON
the stmmac device driver. This driver is used for Meson6,
Meson8, Meson8b and GXBB SoCs.
+config DWMAC_OXNAS
+ tristate "Oxford Semiconductor OXNAS dwmac support"
+ default ARCH_OXNAS
+ depends on OF && COMMON_CLK && (ARCH_OXNAS || COMPILE_TEST)
+ help
+ Support for Ethernet controller on Oxford Semiconductor OXNAS SoCs.
+
+ This selects the Oxford Semiconductor OXNASSoC glue layer support for
+ the stmmac device driver. This driver is used for OX820.
+
config DWMAC_ROCKCHIP
tristate "Rockchip dwmac support"
default ARCH_ROCKCHIP
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index 5d6ece5..8f83a86 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o
obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o
obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o
+obj-$(CONFIG_DWMAC_OXNAS) += dwmac-oxnas.o
obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o
obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-altr-socfpga.o
obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c
new file mode 100644
index 0000000..40fd845
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c
@@ -0,0 +1,219 @@
+/*
+ * Oxford Semiconductor OXNAS DWMAC glue layer
+ *
+ * Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2014 Daniel Golle <daniel@makrotopia.org>
+ * Copyright (C) 2013 Ma Haijun <mahaijuns@gmail.com>
+ * Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/stmmac.h>
+
+#include "stmmac_platform.h"
+
+/* System Control regmap offsets */
+#define OXNAS_DWMAC_CTRL_REGOFFSET 0x78
+#define OXNAS_DWMAC_DELAY_REGOFFSET 0x100
+
+/* Control Register */
+#define DWMAC_CKEN_RX_IN 14
+#define DWMAC_CKEN_RXN_OUT 13
+#define DWMAC_CKEN_RX_OUT 12
+#define DWMAC_CKEN_TX_IN 10
+#define DWMAC_CKEN_TXN_OUT 9
+#define DWMAC_CKEN_TX_OUT 8
+#define DWMAC_RX_SOURCE 7
+#define DWMAC_TX_SOURCE 6
+#define DWMAC_LOW_TX_SOURCE 4
+#define DWMAC_AUTO_TX_SOURCE 3
+#define DWMAC_RGMII 2
+#define DWMAC_SIMPLE_MUX 1
+#define DWMAC_CKEN_GTX 0
+
+/* Delay register */
+#define DWMAC_TX_VARDELAY_SHIFT 0
+#define DWMAC_TXN_VARDELAY_SHIFT 8
+#define DWMAC_RX_VARDELAY_SHIFT 16
+#define DWMAC_RXN_VARDELAY_SHIFT 24
+#define DWMAC_TX_VARDELAY(d) ((d) << DWMAC_TX_VARDELAY_SHIFT)
+#define DWMAC_TXN_VARDELAY(d) ((d) << DWMAC_TXN_VARDELAY_SHIFT)
+#define DWMAC_RX_VARDELAY(d) ((d) << DWMAC_RX_VARDELAY_SHIFT)
+#define DWMAC_RXN_VARDELAY(d) ((d) << DWMAC_RXN_VARDELAY_SHIFT)
+
+struct oxnas_dwmac {
+ struct device *dev;
+ struct clk *clk;
+ struct regmap *regmap;
+};
+
+static int oxnas_dwmac_init(struct oxnas_dwmac *dwmac)
+{
+ unsigned int value;
+ int ret;
+
+ /* Reset HW here before changing the glue configuration */
+ ret = device_reset(dwmac->dev);
+ if (ret)
+ return ret;
+
+ clk_prepare_enable(dwmac->clk);
+
+ ret = regmap_read(dwmac->regmap, OXNAS_DWMAC_CTRL_REGOFFSET, &value);
+ if (ret < 0)
+ return ret;
+
+ /* Enable GMII_GTXCLK to follow GMII_REFCLK, required for gigabit PHY */
+ value |= BIT(DWMAC_CKEN_GTX);
+ /* Use simple mux for 25/125 Mhz clock switching */
+ value |= BIT(DWMAC_SIMPLE_MUX);
+ /* set auto switch tx clock source */
+ value |= BIT(DWMAC_AUTO_TX_SOURCE);
+ /* enable tx & rx vardelay */
+ value |= BIT(DWMAC_CKEN_TX_OUT);
+ value |= BIT(DWMAC_CKEN_TXN_OUT);
+ value |= BIT(DWMAC_CKEN_TX_IN);
+ value |= BIT(DWMAC_CKEN_RX_OUT);
+ value |= BIT(DWMAC_CKEN_RXN_OUT);
+ value |= BIT(DWMAC_CKEN_RX_IN);
+ regmap_write(dwmac->regmap, OXNAS_DWMAC_CTRL_REGOFFSET, value);
+
+ /* set tx & rx vardelay */
+ value = DWMAC_TX_VARDELAY(4);
+ value |= DWMAC_TXN_VARDELAY(2);
+ value |= DWMAC_RX_VARDELAY(10);
+ value |= DWMAC_RXN_VARDELAY(8);
+ regmap_write(dwmac->regmap, OXNAS_DWMAC_DELAY_REGOFFSET, value);
+
+ return 0;
+}
+
+static int oxnas_dwmac_probe(struct platform_device *pdev)
+{
+ struct plat_stmmacenet_data *plat_dat;
+ struct stmmac_resources stmmac_res;
+ struct device_node *sysctrl;
+ struct oxnas_dwmac *dwmac;
+ int ret;
+
+ sysctrl = of_parse_phandle(pdev->dev.of_node, "oxsemi,sys-ctrl", 0);
+ if (!sysctrl) {
+ dev_err(&pdev->dev, "failed to get sys-ctrl node\n");
+ return -EINVAL;
+ }
+
+ ret = stmmac_get_platform_resources(pdev, &stmmac_res);
+ if (ret)
+ return ret;
+
+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return PTR_ERR(plat_dat);
+
+ dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
+ if (!dwmac)
+ return -ENOMEM;
+
+ dwmac->dev = &pdev->dev;
+ plat_dat->bsp_priv = dwmac;
+
+ dwmac->regmap = syscon_node_to_regmap(sysctrl);
+ if (IS_ERR(dwmac->regmap)) {
+ dev_err(&pdev->dev, "failed to have sysctrl regmap\n");
+ return PTR_ERR(dwmac->regmap);
+ }
+
+ dwmac->clk = devm_clk_get(&pdev->dev, "gmac");
+ if (IS_ERR(dwmac->clk))
+ return PTR_ERR(dwmac->clk);
+
+ ret = oxnas_dwmac_init(dwmac);
+ if (ret)
+ return ret;
+
+ ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+ if (ret)
+ clk_disable_unprepare(dwmac->clk);
+
+ return ret;
+}
+
+static int oxnas_dwmac_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct stmmac_priv *priv = netdev_priv(ndev);
+ struct oxnas_dwmac *dwmac = priv->plat->bsp_priv;
+ int ret = stmmac_dvr_remove(&pdev->dev);
+
+ clk_disable_unprepare(dwmac->clk);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int oxnas_dwmac_suspend(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct stmmac_priv *priv = netdev_priv(ndev);
+ struct oxnas_dwmac *dwmac = priv->plat->bsp_priv;
+ int ret;
+
+ ret = stmmac_suspend(dev);
+ clk_disable_unprepare(dwmac->clk);
+
+ return ret;
+}
+
+static int oxnas_dwmac_resume(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct stmmac_priv *priv = netdev_priv(ndev);
+ struct oxnas_dwmac *dwmac = priv->plat->bsp_priv;
+ int ret;
+
+ ret = oxnas_dwmac_init(dwmac);
+ if (ret)
+ return ret;
+
+ ret = stmmac_resume(dev);
+
+ return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(oxnas_dwmac_pm_ops,
+ oxnas_dwmac_suspend, oxnas_dwmac_resume);
+
+static const struct of_device_id oxnas_dwmac_match[] = {
+ { .compatible = "oxsemi,ox820-dwmac" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, oxnas_dwmac_match);
+
+static struct platform_driver oxnas_dwmac_driver = {
+ .probe = oxnas_dwmac_probe,
+ .remove = oxnas_dwmac_remove,
+ .driver = {
+ .name = "oxnas-dwmac",
+ .pm = &oxnas_dwmac_pm_ops,
+ .of_match_table = oxnas_dwmac_match,
+ },
+};
+module_platform_driver(oxnas_dwmac_driver);
+
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_DESCRIPTION("Oxford Semiconductor OXNAS DWMAC glue layer");
+MODULE_LICENSE("GPL v2");
--
2.7.0
^ permalink raw reply related
* [PATCH] dmaengine: st_fdma: Update st_fdma to 'depends on REMOTEPROC'.
From: Peter Griffin @ 2016-10-21 8:44 UTC (permalink / raw)
To: linux-arm-kernel
During randconfig builds you can get the following warning
"warning: (ST_FDMA) selects ST_SLIM_REMOTEPROC which has unmet direct
dependencies (REMOTEPROC)"
randconfig builds should always build without any warnings so
update fdma to depend on REMOTEPROC so this can not happen.
Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
Reported-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Arnd Bergmann <arnd@arndb.de>
---
drivers/dma/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 661f217..6b96710 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -438,6 +438,7 @@ config STE_DMA40
config ST_FDMA
tristate "ST FDMA dmaengine support"
depends on ARCH_STI
+ depends on REMOTEPROC
select ST_SLIM_REMOTEPROC
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
--
1.9.1
^ permalink raw reply related
* [PATCH v5 4/7] ASoC: sunxi: Add sun8i I2S driver
From: Jean-Francois Moine @ 2016-10-21 8:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1477142934.git.moinejf@free.fr>
This patch adds I2S support to sun8i SoCs as the A83T and H3.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
Note: This driver is closed to the sun4i-i2s except that:
- it handles the H3
- it creates the sound card (with sun4i-i2s, the sound card is created
by the CODECs)
---
.../devicetree/bindings/sound/sun4i-i2s.txt | 38 +-
sound/soc/sunxi/Kconfig | 8 +
sound/soc/sunxi/Makefile | 3 +
sound/soc/sunxi/sun8i-i2s.c | 700 +++++++++++++++++++++
4 files changed, 744 insertions(+), 5 deletions(-)
create mode 100644 sound/soc/sunxi/sun8i-i2s.c
diff --git a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt
index 7b526ec..2fb0a7a 100644
--- a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt
+++ b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt
@@ -1,4 +1,4 @@
-* Allwinner A10 I2S controller
+* Allwinner A10/A38T/H3 I2S controller
The I2S bus (Inter-IC sound bus) is a serial link for digital
audio data transfer between devices in the system.
@@ -6,20 +6,30 @@ audio data transfer between devices in the system.
Required properties:
- compatible: should be one of the followings
- - "allwinner,sun4i-a10-i2s"
+ - "allwinner,sun4i-a10-i2s"
+ "allwinner,sun8i-a83t-i2s"
+ "allwinner,sun8i-h3-i2s"
- reg: physical base address of the controller and length of memory mapped
region.
-- interrupts: should contain the I2S interrupt.
- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
Documentation/devicetree/bindings/dma/dma.txt
-- dma-names: should include "tx" and "rx".
+- dma-names: must include "tx" and/or "rx".
- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names.
- clock-names: should contain followings:
- "apb" : clock for the I2S bus interface
- "mod" : module clock for the I2S controller
- #sound-dai-cells : Must be equal to 0
-Example:
+Optional properties:
+
+- interrupts: I2S interrupt
+- resets: phandle to the reset of the device
+
+Required nodes:
+
+ - port: link to the associated CODEC (DAC, HDMI...)
+
+Example 1:
i2s0: i2s at 01c22400 {
#sound-dai-cells = <0>;
@@ -32,3 +42,21 @@ i2s0: i2s at 01c22400 {
<&dma SUN4I_DMA_NORMAL 3>;
dma-names = "rx", "tx";
};
+
+Example 2:
+
+i2s2: i2s at 1c22800 {
+ compatible = "allwinner,sun8i-a83t-i2s";
+ reg = <0x01c22800 0x60>;
+ clocks = <&ccu CLK_BUS_I2S2>, <&ccu CLK_I2S2>;
+ clock-names = "apb", "mod";
+ resets = <&ccu RST_I2S2>;
+ dmas = <&dma 27>;
+ dma-names = "tx";
+ status = "disabled";
+ port {
+ i2s2_hdmi: endpoint {
+ remote-endpoint = <&hdmi_i2s2>;
+ };
+ };
+};
diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
index dd23682..d89b2da 100644
--- a/sound/soc/sunxi/Kconfig
+++ b/sound/soc/sunxi/Kconfig
@@ -26,4 +26,12 @@ config SND_SUN4I_SPDIF
help
Say Y or M to add support for the S/PDIF audio block in the Allwinner
A10 and affiliated SoCs.
+
+config SND_SUN8I_I2S
+ tristate "Allwinner sun8i I2S Support"
+ depends on OF
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for SoC audio on
+ Allwinner sun8i boards.
endmenu
diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
index 604c7b84..bcb871b 100644
--- a/sound/soc/sunxi/Makefile
+++ b/sound/soc/sunxi/Makefile
@@ -1,3 +1,6 @@
obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o
obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
+
+snd-soc-sun8i-i2s-objs := sun8i-i2s.o
+obj-$(CONFIG_SND_SUN8I_I2S) += snd-soc-sun8i-i2s.o
diff --git a/sound/soc/sunxi/sun8i-i2s.c b/sound/soc/sunxi/sun8i-i2s.c
new file mode 100644
index 0000000..ba15d62
--- /dev/null
+++ b/sound/soc/sunxi/sun8i-i2s.c
@@ -0,0 +1,700 @@
+/*
+ * Allwinner sun8i I2S sound card
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+
+/* --- hardware --- */
+
+#define I2S_CTL 0x00
+ /* common */
+ #define I2S_CTL_SDO3EN BIT(11)
+ #define I2S_CTL_SDO2EN BIT(10)
+ #define I2S_CTL_SDO1EN BIT(9)
+ #define I2S_CTL_SDO0EN BIT(8)
+ #define I2S_CTL_TXEN BIT(2)
+ #define I2S_CTL_RXEN BIT(1)
+ #define I2S_CTL_GEN BIT(0)
+ /* a83t */
+ #define I2S_CTL_A83T_MS BIT(5)
+ #define I2S_CTL_A83T_PCM BIT(4)
+ /* h3 */
+ #define I2S_CTL_H3_BCLKOUT BIT(18)
+ #define I2S_CTL_H3_LRCKOUT BIT(17)
+ #define I2S_CTL_H3_MODE_MSK (3 << 4)
+ #define I2S_CTL_H3_MODE_I2S (1 << 4)
+
+#define I2S_FAT0 0x04
+ /* common */
+ /* a83t */
+ #define I2S_FAT0_A83T_LRCP BIT(7)
+ #define I2S_FAT0_A83T_BCP BIT(6)
+ #define I2S_FAT0_A83T_SR_16BIT (0 << 4)
+ #define I2S_FAT0_A83T_SR_24BIT (2 << 4)
+ #define I2S_FAT0_A83T_SR_MSK (3 << 4)
+ #define I2S_FAT0_A83T_WSS_32BCLK (3 << 2)
+ #define I2S_FAT0_A83T_FMT_I2S1 (0 << 0)
+ #define I2S_FAT0_A83T_FMT_LFT (1 << 0)
+ #define I2S_FAT0_A83T_FMT_RGT (2 << 0)
+ #define I2S_FAT0_A83T_FMT_MSK (3 << 0)
+ /* h3 */
+ #define I2S_FAT0_H3_LRCKR_PERIOD(v) ((v) << 20)
+ #define I2S_FAT0_H3_LRCKR_PERIOD_MSK (0x3ff << 20)
+ #define I2S_FAT0_H3_LRCK_POLARITY BIT(19)
+ #define I2S_FAT0_H3_LRCK_PERIOD(v) ((v) << 8)
+ #define I2S_FAT0_H3_LRCK_PERIOD_MSK (0x3ff << 8)
+ #define I2S_FAT0_H3_BCLK_POLARITY BIT(7)
+ #define I2S_FAT0_H3_SR_16 (3 << 4)
+ #define I2S_FAT0_H3_SR_24 (5 << 4)
+ #define I2S_FAT0_H3_SR_MSK (7 << 4)
+ #define I2S_FAT0_H3_SW_16 (3 << 0)
+ #define I2S_FAT0_H3_SW_32 (7 << 0)
+ #define I2S_FAT0_H3_SW_MSK (7 << 0)
+
+#define I2S_FAT1 0x08
+
+#define I2S_FCTL 0x14
+ #define I2S_FCTL_FTX BIT(25)
+ #define I2S_FCTL_FRX BIT(24)
+ #define I2S_FCTL_TXTL(v) ((v) << 12)
+ #define I2S_FCTL_TXIM BIT(2)
+
+#define I2S_INT 0x1c
+ #define I2S_INT_TXDRQEN BIT(7)
+
+#define I2S_TXFIFO 0x20
+
+#define I2S_CLKD 0x24
+ /* common */
+ #define I2S_CLKD_BCLKDIV(v) ((v) << 4)
+ #define I2S_CLKD_MCLKDIV(v) ((v) << 0)
+ /* a83t */
+ #define I2S_CLKD_A83T_MCLKOEN BIT(7)
+ /* h3 */
+ #define I2S_CLKD_H3_MCLKOEN BIT(8)
+
+#define I2S_TXCNT 0x28
+
+#define I2S_RXCNT 0x2c
+
+/* --- A83T --- */
+#define I2S_TXCHSEL_A83T 0x30
+ #define I2S_TXCHSEL_A83T_CHNUM(v) (((v) - 1) << 0)
+ #define I2S_TXCHSEL_A83T_CHNUM_MSK (7 << 0)
+
+#define I2S_TXCHMAP_A83T 0x34
+
+/* --- H3 --- */
+#define I2S_TXCHCFG_H3 0x30
+ #define I2S_TXCHCFG_H3_TX_SLOT_NUM_MSK (7 << 0)
+ #define I2S_TXCHCFG_H3_TX_SLOT_NUM(v) ((v) << 0)
+
+#define I2S_TX0CHSEL_H3 0x34 /* 0..3 */
+ #define I2S_TXn_H3_OFFSET_MSK (3 << 12)
+ #define I2S_TXn_H3_OFFSET(v) ((v) << 12)
+ #define I2S_TXn_H3_CHEN_MSK (0xff << 4)
+ #define I2S_TXn_H3_CHEN(v) ((v) << 4)
+ #define I2S_TXn_H3_CHSEL_MSK (7 << 0)
+ #define I2S_TXn_H3_CHSEL(v) ((v) << 0)
+
+#define I2S_TX0CHMAP_H3 0x44 /* 0..3 */
+
+/* --- driver --- */
+
+#define DRV_NAME "sun8i-audio"
+
+#define I2S_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define PCM_LRCK_PERIOD 32
+#define PCM_LRCKR_PERIOD 1
+
+struct priv {
+ void __iomem *mmio;
+ struct clk *clk;
+ struct clk *gate;
+ struct reset_control *rstc;
+ int type;
+#define SOC_A83T 0
+#define SOC_H3 1
+ struct snd_dmaengine_dai_dma_data dma_data;
+};
+
+static const struct of_device_id sun8i_i2s_of_match[] = {
+ { .compatible = "allwinner,sun8i-a83t-i2s",
+ .data = (void *) SOC_A83T },
+ { .compatible = "allwinner,sun8i-h3-i2s",
+ .data = (void *) SOC_H3 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sun8i_i2s_of_match);
+
+/* --- CPU DAI --- */
+
+static void sun8i_i2s_init(struct priv *priv)
+{
+ u32 reg;
+
+ /* disable global */
+ reg = readl(priv->mmio + I2S_CTL);
+ reg &= ~(I2S_CTL_GEN |
+ I2S_CTL_RXEN |
+ I2S_CTL_TXEN);
+ writel(reg, priv->mmio + I2S_CTL);
+
+ /* A83T */
+ if (priv->type == SOC_A83T) {
+ reg &= ~(I2S_CTL_A83T_MS | /* codec clk & FRM slave */
+ I2S_CTL_A83T_PCM); /* I2S mode */
+ writel(reg, priv->mmio + I2S_CTL);
+
+ reg = readl(priv->mmio + I2S_FAT0);
+ reg &= ~I2S_FAT0_A83T_FMT_MSK;
+ reg |= I2S_FAT0_A83T_FMT_I2S1;
+
+ reg &= ~(I2S_FAT0_A83T_LRCP | I2S_FAT0_A83T_BCP);
+ writel(reg, priv->mmio + I2S_FAT0);
+
+ reg = I2S_FCTL_TXIM | /* fifo */
+ I2S_FCTL_TXTL(0x40);
+ writel(reg, priv->mmio + I2S_FCTL);
+
+ reg = readl(priv->mmio + I2S_FAT0);
+ reg &= ~(I2S_FAT0_A83T_LRCP | /* normal bit clock + frame */
+ I2S_FAT0_A83T_BCP);
+ writel(reg, priv->mmio + I2S_FAT0);
+
+ /* H3 */
+ } else {
+ reg = readl(priv->mmio + I2S_FCTL);
+ reg &= ~(I2S_FCTL_FRX | I2S_FCTL_FTX); /* clear the FIFOs */
+ writel(reg, priv->mmio + I2S_FCTL);
+
+ writel(0, priv->mmio + I2S_TXCNT); /* FIFO counters */
+ writel(0, priv->mmio + I2S_RXCNT);
+
+ reg = readl(priv->mmio + I2S_CTL);
+ reg |= I2S_CTL_H3_LRCKOUT | I2S_CTL_H3_BCLKOUT;
+ /* codec clk & FRM slave */
+
+ reg &= ~I2S_CTL_H3_MODE_MSK;
+ reg |= I2S_CTL_H3_MODE_I2S; /* I2S mode */
+ writel(reg, priv->mmio + I2S_CTL);
+
+ reg = readl(priv->mmio + I2S_TX0CHSEL_H3) &
+ ~I2S_TXn_H3_OFFSET_MSK;
+ reg |= I2S_TXn_H3_OFFSET(1);
+ writel(reg, priv->mmio + I2S_TX0CHSEL_H3);
+
+ reg = readl(priv->mmio + I2S_FAT0);
+ reg &= ~(I2S_FAT0_H3_BCLK_POLARITY | /* normal bclk & frame */
+ I2S_FAT0_H3_LRCK_POLARITY);
+ writel(reg, priv->mmio + I2S_FAT0);
+ }
+}
+
+static int sun8i_i2s_set_clock(struct priv *priv,
+ unsigned long rate)
+{
+ unsigned long freq;
+ int ret, i, div;
+ u32 reg;
+ static const u8 div_tb[] = {
+ 1, 2, 4, 6, 8, 12, 16, 24,
+ };
+
+ /* compute the sys clock rate and divide values */
+ if (rate % 1000 == 0)
+ freq = 24576000;
+ else
+ freq = 22579200;
+ div = freq / 2 / PCM_LRCK_PERIOD / rate;
+ if (priv->type == SOC_A83T)
+ div /= 2; /* bclk_div==0 => mclk/2 */
+ for (i = 0; i < ARRAY_SIZE(div_tb) - 1; i++)
+ if (div_tb[i] >= div)
+ break;
+
+ ret = clk_set_rate(priv->clk, freq);
+ if (ret) {
+ pr_info("Setting sysclk rate failed %d\n", ret);
+ return ret;
+ }
+
+ /* set the mclk and bclk dividor register */
+ if (priv->type == SOC_A83T) {
+ reg = I2S_CLKD_A83T_MCLKOEN | I2S_CLKD_MCLKDIV(i);
+ } else {
+ reg = I2S_CLKD_H3_MCLKOEN | I2S_CLKD_MCLKDIV(1) |
+ I2S_CLKD_BCLKDIV(i + 1);
+ }
+ writel(reg, priv->mmio + I2S_CLKD);
+
+ /* format */
+ reg = readl(priv->mmio + I2S_FAT0);
+ if (priv->type == SOC_A83T) {
+ reg |= I2S_FAT0_A83T_WSS_32BCLK;
+ reg &= ~I2S_FAT0_A83T_SR_MSK;
+ reg |= I2S_FAT0_A83T_SR_16BIT;
+ } else {
+ reg &= ~(I2S_FAT0_H3_LRCKR_PERIOD_MSK |
+ I2S_FAT0_H3_LRCK_PERIOD_MSK);
+ reg |= I2S_FAT0_H3_LRCK_PERIOD(PCM_LRCK_PERIOD - 1) |
+ I2S_FAT0_H3_LRCKR_PERIOD(PCM_LRCKR_PERIOD - 1);
+
+ reg &= ~I2S_FAT0_H3_SW_MSK;
+ reg |= I2S_FAT0_H3_SW_16;
+
+ reg &= ~I2S_FAT0_H3_SR_MSK;
+ reg |= I2S_FAT0_H3_SR_16;
+ }
+ writel(reg, priv->mmio + I2S_FAT0);
+
+ writel(0, priv->mmio + I2S_FAT1);
+
+ return 0;
+}
+
+static int sun8i_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
+ struct priv *priv = snd_soc_card_get_drvdata(card);
+ int nchan = substream->runtime->channels;
+ u32 reg;
+
+ if (priv->type == SOC_A83T) {
+ reg = readl(priv->mmio + I2S_TXCHSEL_A83T);
+ reg &= ~I2S_TXCHSEL_A83T_CHNUM_MSK;
+ reg |= I2S_TXCHSEL_A83T_CHNUM(substream->runtime->channels);
+ writel(reg, priv->mmio + I2S_TXCHSEL_A83T);
+
+ switch (substream->runtime->channels) {
+ case 1:
+ reg = 0x76543200;
+ break;
+ case 8:
+ reg = 0x54762310;
+ break;
+ default:
+/* left/right inversion of channels 0 and 1 */
+ reg = 0x76543201;
+ break;
+ }
+ writel(reg, priv->mmio + I2S_TXCHMAP_A83T);
+ } else {
+ reg = readl(priv->mmio + I2S_TXCHCFG_H3) &
+ ~I2S_TXCHCFG_H3_TX_SLOT_NUM_MSK;
+ if (nchan != 1)
+ reg |= I2S_TXCHCFG_H3_TX_SLOT_NUM(1);
+ writel(reg, priv->mmio + I2S_TXCHCFG_H3);
+
+ reg = readl(priv->mmio + I2S_TX0CHSEL_H3);
+ reg &= ~(I2S_TXn_H3_CHEN_MSK |
+ I2S_TXn_H3_CHSEL_MSK);
+ reg |= I2S_TXn_H3_CHEN(3) |
+ I2S_TXn_H3_CHSEL(1);
+ writel(reg, priv->mmio + I2S_TX0CHSEL_H3);
+
+ reg = nchan == 1 ? 0 : 0x10;
+ writel(reg, priv->mmio + I2S_TX0CHMAP_H3);
+ }
+
+ reg = readl(priv->mmio + I2S_CTL);
+ reg &= ~(I2S_CTL_SDO3EN |
+ I2S_CTL_SDO2EN |
+ I2S_CTL_SDO1EN);
+ if (nchan >= 7)
+ reg |= I2S_CTL_SDO3EN;
+ if (nchan >= 5)
+ reg |= I2S_CTL_SDO2EN;
+ if (nchan >= 3)
+ reg |= I2S_CTL_SDO1EN;
+ reg |= I2S_CTL_SDO0EN;
+ writel(reg, priv->mmio + I2S_CTL);
+
+ writel(0, priv->mmio + I2S_TXCNT);
+
+ return 0;
+}
+
+static void sun8i_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
+ struct priv *priv = snd_soc_card_get_drvdata(card);
+ u32 reg;
+
+ reg = readl(priv->mmio + I2S_CTL);
+ reg &= ~I2S_CTL_GEN;
+ writel(reg, priv->mmio + I2S_CTL);
+}
+
+static int sun8i_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
+ struct priv *priv = snd_soc_card_get_drvdata(card);
+ u32 reg, reg2;
+ int sample_resolution;
+ int ret;
+
+ ret = sun8i_i2s_set_clock(priv, params_rate(params));
+ if (ret)
+ return ret;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ priv->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ sample_resolution = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ priv->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ sample_resolution = 24;
+ break;
+ default:
+ return -EINVAL;
+ }
+ reg = readl(priv->mmio + I2S_FAT0);
+ reg2 = readl(priv->mmio + I2S_FCTL);
+ if (priv->type == SOC_A83T) {
+ reg &= ~I2S_FAT0_A83T_SR_MSK;
+ if (sample_resolution == 16) {
+ reg |= I2S_FAT0_A83T_SR_16BIT;
+ reg2 |= I2S_FCTL_TXIM;
+ } else {
+ reg |= I2S_FAT0_A83T_SR_24BIT;
+ reg2 &= ~I2S_FCTL_TXIM;
+ }
+ } else {
+ reg &= ~(I2S_FAT0_H3_SR_MSK | I2S_FAT0_H3_SW_MSK);
+ if (sample_resolution == 16) {
+ reg |= I2S_FAT0_H3_SR_16 |
+ I2S_FAT0_H3_SW_16;
+ reg2 |= I2S_FCTL_TXIM;
+ } else {
+ reg |= I2S_FAT0_H3_SR_24 |
+ I2S_FAT0_H3_SW_32;
+ reg2 &= ~I2S_FCTL_TXIM;
+ }
+ }
+ writel(reg, priv->mmio + I2S_FAT0);
+ writel(reg2, priv->mmio + I2S_FCTL);
+
+ /* enable audio interface */
+ reg = readl(priv->mmio + I2S_CTL);
+ reg |= I2S_CTL_GEN;
+ writel(reg, priv->mmio + I2S_CTL);
+ msleep(10);
+
+ /* flush TX FIFO */
+ reg = readl(priv->mmio + I2S_FCTL);
+ reg |= I2S_FCTL_FTX;
+ writel(reg, priv->mmio + I2S_FCTL);
+
+ return 0;
+}
+
+static int sun8i_i2s_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
+ struct priv *priv = snd_soc_card_get_drvdata(card);
+ u32 reg;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ reg = readl(priv->mmio + I2S_CTL);
+ reg |= I2S_CTL_TXEN;
+ writel(reg, priv->mmio + I2S_CTL);
+
+ /* enable DMA DRQ mode */
+ reg = readl(priv->mmio + I2S_INT);
+ reg |= I2S_INT_TXDRQEN;
+ writel(reg, priv->mmio + I2S_INT);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ reg = readl(priv->mmio + I2S_INT);
+ reg &= ~I2S_INT_TXDRQEN;
+ writel(reg, priv->mmio + I2S_INT);
+
+ reg = readl(priv->mmio + I2S_CTL);
+ reg &= ~I2S_CTL_TXEN;
+ writel(reg, priv->mmio + I2S_CTL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sun8i_i2s_dai_ops = {
+ .hw_params = sun8i_i2s_hw_params,
+ .prepare = sun8i_i2s_prepare,
+ .trigger = sun8i_i2s_trigger,
+ .shutdown = sun8i_i2s_shutdown,
+};
+
+static int sun8i_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
+ struct priv *priv = snd_soc_card_get_drvdata(card);
+
+ snd_soc_dai_init_dma_data(dai, &priv->dma_data, NULL);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver sun8i_i2s_dai = {
+ .probe = sun8i_i2s_dai_probe,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 32000,
+ .rate_max = 192000,
+ .formats = I2S_FORMATS,
+ },
+ .ops = &sun8i_i2s_dai_ops,
+};
+
+static const struct snd_soc_component_driver i2s_component = {
+ .name = DRV_NAME,
+};
+
+/* --- dma --- */
+
+static const struct snd_pcm_hardware sun8i_i2s_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+ .formats = I2S_FORMATS,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 32000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 8,
+ .buffer_bytes_max = 1024 * 1024,
+ .period_bytes_min = 156,
+ .period_bytes_max = 1024 * 1024,
+ .periods_min = 1,
+ .periods_max = 8,
+ .fifo_size = 128,
+};
+
+static const struct snd_dmaengine_pcm_config sun8i_i2s_config = {
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+ .pcm_hardware = &sun8i_i2s_pcm_hardware,
+ .prealloc_buffer_size = 1024 * 1024,
+};
+
+/* --- audio card --- */
+
+static struct device_node *sun8i_get_codec(struct device *dev)
+{
+ struct device_node *ep, *remote;
+
+ ep = of_graph_get_next_endpoint(dev->of_node, NULL);
+ if (!ep)
+ return NULL;
+ remote = of_graph_get_remote_port_parent(ep);
+ of_node_put(ep);
+
+ return remote;
+}
+
+static int sun8i_card_create(struct device *dev, struct priv *priv)
+{
+ struct snd_soc_card *card;
+ struct snd_soc_dai_link *dai_link;
+ struct snd_soc_dai_link_component *codec;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+ dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
+ if (!dai_link)
+ return -ENOMEM;
+ codec = devm_kzalloc(dev, sizeof(*codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+
+ codec->of_node = sun8i_get_codec(dev);
+ if (!codec->of_node) {
+ dev_err(dev, "no port node\n");
+ return -ENXIO;
+ }
+
+ card->name = codec->of_node->name;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ dai_link->name = codec->of_node->name;
+ dai_link->stream_name = codec->of_node->name;
+ dai_link->platform_name = dev_name(dev);
+ dai_link->cpu_name = dev_name(dev);
+
+ dai_link->codecs = codec;
+ dai_link->num_codecs = 1;
+
+ /* the DAI name must be the name of codec node */
+ codec->dai_name = codec->of_node->name;
+
+ card->dev = dev;
+ dev_set_drvdata(dev, card);
+ snd_soc_card_set_drvdata(card, priv);
+
+ return devm_snd_soc_register_card(dev, card);
+}
+
+/* --- module init --- */
+
+static int sun8i_i2s_dev_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct priv *priv;
+ struct resource *mem;
+ int ret;
+
+ if (!dev->of_node) {
+ dev_err(dev, "no DT!\n");
+ return -EINVAL;
+ }
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* get the resources */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->mmio = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(priv->mmio))
+ return PTR_ERR(priv->mmio);
+
+ /* get SoC type */
+ priv->type = (int) of_match_device(sun8i_i2s_of_match,
+ &pdev->dev)->data;
+
+ /* get and enable the clocks */
+ priv->gate = devm_clk_get(dev, "apb"); /* optional */
+ priv->clk = devm_clk_get(dev, "mod");
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev, "no clock\n");
+ return PTR_ERR(priv->clk);
+ }
+ ret = clk_set_rate(priv->clk, 24576000);
+ if (ret) {
+ dev_err(dev, "cannot set rate of i2s clock %d\n", ret);
+ return ret;
+ }
+
+ priv->rstc = devm_reset_control_get_optional(dev, NULL);
+ if (!IS_ERR(priv->rstc)) {
+ ret = reset_control_deassert(priv->rstc);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (!IS_ERR(priv->gate)) {
+ ret = clk_prepare_enable(priv->gate);
+ if (ret < 0)
+ goto err_gate;
+ }
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret < 0)
+ goto err_enable;
+
+ /* activate the audio subsystem */
+ sun8i_i2s_init(priv);
+
+ ret = devm_snd_soc_register_component(dev, &i2s_component,
+ &sun8i_i2s_dai, 1);
+ if (ret) {
+ dev_err(dev, "snd_soc_register_component failed %d\n", ret);
+ goto err_register;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(dev, &sun8i_i2s_config, 0);
+ if (ret) {
+ dev_err(dev, "pcm_register failed %d\n", ret);
+ goto err_register;
+ }
+
+ priv->dma_data.maxburst = priv->type == SOC_A83T ? 8 : 4;
+ priv->dma_data.addr = mem->start + I2S_TXFIFO;
+ priv->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+ ret = sun8i_card_create(dev, priv);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "register card failed %d\n", ret);
+ goto err_register;
+ }
+
+ return 0;
+
+err_register:
+ clk_disable_unprepare(priv->clk);
+err_enable:
+ clk_disable_unprepare(priv->gate);
+err_gate:
+ if (!IS_ERR(priv->rstc))
+ reset_control_assert(priv->rstc);
+
+ return ret;
+}
+
+static int sun8i_i2s_dev_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = dev_get_drvdata(&pdev->dev);
+ struct priv *priv = snd_soc_card_get_drvdata(card);
+
+ clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->gate);
+ if (!IS_ERR_OR_NULL(priv->rstc))
+ reset_control_assert(priv->rstc);
+
+ return 0;
+}
+
+static struct platform_driver sun8i_i2s_driver = {
+ .probe = sun8i_i2s_dev_probe,
+ .remove = sun8i_i2s_dev_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(sun8i_i2s_of_match),
+ },
+};
+
+module_platform_driver(sun8i_i2s_driver);
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("Allwinner sun8i I2S ASoC Interface");
+MODULE_LICENSE("GPL v2");
--
2.10.1
^ permalink raw reply related
* mbox-name vs. mbox-names (was: Re: [PATCH v4 1/5] mailbox: dt: Supply bindings for ST's Mailbox IP)
From: Geert Uytterhoeven @ 2016-10-21 8:30 UTC (permalink / raw)
To: linux-arm-kernel
On Fri, Oct 16, 2015 at 9:21 AM, Lee Jones <lee.jones@linaro.org> wrote:
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
> .../devicetree/bindings/mailbox/sti-mailbox.txt | 51 ++++++++++++++++++++++
> 1 file changed, 51 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mailbox/sti-mailbox.txt
>
> diff --git a/Documentation/devicetree/bindings/mailbox/sti-mailbox.txt b/Documentation/devicetree/bindings/mailbox/sti-mailbox.txt
> new file mode 100644
> index 0000000..b61eec9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mailbox/sti-mailbox.txt
> @@ -0,0 +1,51 @@
> +ST Microelectronics Mailbox Driver
> +
> +Each ST Mailbox IP currently consists of 4 instances of 32 channels. Messages
> +are passed between Application and Remote processors using shared memory.
> +
> +Controller
> +----------
> +
> +Required properties:
> +- compatible : Should be "st,stih407-mailbox"
> +- reg : Offset and length of the device's register set
> +- mbox-name : Name of the mailbox
All other mailbox drivers use "mbox-names". Oops, it's in v4.9-rc1...
Can we still fix that?
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* [RFC PATCH] net: stmmac: Add OXNAS Glue Driver
From: Neil Armstrong @ 2016-10-21 8:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAGhQ9VxJ-gQTX1xjMOJ4spiL_RnhryyQ0=gUEQCHm3sHecTVuA@mail.gmail.com>
On 10/20/2016 06:26 PM, Joachim Eastwood wrote:
> Hi Neil,
>
> On 20 October 2016 at 17:54, Neil Armstrong <narmstrong@baylibre.com> wrote:
>> Add Synopsys Designware MAC Glue layer for the Oxford Semiconductor OX820.
>>
>> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
>> ---
>> .../devicetree/bindings/net/oxnas-dwmac.txt | 44 ++++++
>> drivers/net/ethernet/stmicro/stmmac/Kconfig | 11 ++
>> drivers/net/ethernet/stmicro/stmmac/Makefile | 1 +
>> drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c | 173 +++++++++++++++++++++
>> 4 files changed, 229 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/net/oxnas-dwmac.txt
>> create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-oxnas.c
> <snip>
>> +
>> +static int oxnas_dwmac_probe(struct platform_device *pdev)
>> +{
>> + struct plat_stmmacenet_data *plat_dat;
>> + struct stmmac_resources stmmac_res;
>> + struct device_node *sysctrl;
>> + struct oxnas_dwmac *dwmac;
>> + int ret;
>> +
>> + sysctrl = of_parse_phandle(pdev->dev.of_node, "oxsemi,sys-ctrl", 0);
>> + if (!sysctrl) {
>> + dev_err(&pdev->dev, "failed to get sys-ctrl node\n");
>> + return -EINVAL;
>> + }
>> +
>> + ret = stmmac_get_platform_resources(pdev, &stmmac_res);
>> + if (ret)
>> + return ret;
>> +
>> + plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
>> + if (IS_ERR(plat_dat))
>> + return PTR_ERR(plat_dat);
>> +
>> + dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
>> + if (!dwmac)
>> + return -ENOMEM;
>> +
>> + dwmac->regmap = syscon_node_to_regmap(sysctrl);
>> + if (IS_ERR(dwmac->regmap)) {
>> + dev_err(&pdev->dev, "failed to have sysctrl regmap\n");
>> + return PTR_ERR(dwmac->regmap);
>> + }
>> +
>> + dwmac->clk = devm_clk_get(&pdev->dev, "gmac");
>> + if (IS_ERR(dwmac->clk))
>> + return PTR_ERR(dwmac->clk);
>> +
>> + plat_dat->bsp_priv = dwmac;
>> + plat_dat->init = oxnas_dwmac_init;
>> + plat_dat->exit = oxnas_dwmac_exit;
>
> Please do not use the init/exit callbacks. Implement proper driver
> callbacks instead. I.e: PM resume/suspend and driver remove.
>
> Shouldn't you call oxnas_dwmac_init() from probe as well?
> As it is now it will only be called during PM resume and that can't be right.
>
>
>> +
>> + return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
>
> If stmmac_dvr_probe() fails you should disable your clocks.
>
>
> regards,
> Joachim Eastwood
>
Hi Joachim,
Thanks for the hints, stm32 glue does that and I will sync on it.
Neil
^ permalink raw reply
* [PATCH] ARM: dts: rockchip: add i2c-bus subnode to edp
From: Heiko Stuebner @ 2016-10-21 8:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <6083453f-08e1-643a-9638-1db38c704aa5@collabora.com>
Am Donnerstag, 20. Oktober 2016, 15:47:56 CEST schrieb Tomeu Vizoso:
> On 10/20/2016 03:45 PM, Heiko St?bner wrote:
> > Am Donnerstag, 20. Oktober 2016, 10:07:25 schrieb Tomeu Vizoso:
> >> Add an empty 'i2c-bus' subnode to the edp node just so that the I2C core
> >> doesn't attemp to parse the 'ports' subnode as containing i2c devices.
> >>
> >> This is to avoid spurious failure messages such as:
> >>
> >> i2c i2c-6: of_i2c: modalias failure on /dp at ff970000/ports
> >
> > On the one hand, the edp really has an i2c bus - with its only client the
> > EDID listening at 0x50 (and maybe 0x30).
> >
> > On the other hand, adding an empty bus to the (implementation independent)
> > devicetree just to make the Linux i2c subsystem happy sounds heavily like
> > a
> > implementation-specific hack, as the edp i2c bus doesn't leak into the
> > outside world otherwise.
> >
> > I guess this empty i2c bus not being part of the binding document points
> > heavily into the implementation-specific corner :-) .
> >
> > My short search on other patches touching this didn't reveal anything but
> > maybe this was already discussed somewhere and found to be ok?
>
> Here it is:
>
> http://www.spinics.net/lists/linux-tegra/msg27862.html
thanks ... I'm still not sure about the placeholder though, aka needing an
undocumented subnode to make a Linux error message silent.
In the thread you pointed to I also did not see any dt-maintainer involvement
pointing one way or another, but spinics is often not easy to navigate
threads, so I may have missed that.
> > Another option could be to just make of_i2c_register_device silent if
> > of_modalias_node returns -ENODEV?
Heiko
> >> Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
> >> Cc: Randy Li <randy.li@rock-chips.com>
> >> Cc: Jon Hunter <jonathanh@nvidia.com>
> >> ---
> >>
> >> arch/arm/boot/dts/rk3288.dtsi | 5 +++++
> >> 1 file changed, 5 insertions(+)
> >>
> >> diff --git a/arch/arm/boot/dts/rk3288.dtsi
> >> b/arch/arm/boot/dts/rk3288.dtsi
> >> index 2f814ffeb605..94f4b7eecca2 100644
> >> --- a/arch/arm/boot/dts/rk3288.dtsi
> >> +++ b/arch/arm/boot/dts/rk3288.dtsi
> >> @@ -1075,6 +1075,11 @@
> >>
> >> };
> >>
> >> };
> >>
> >> };
> >>
> >> +
> >> + i2c-bus {
> >> + #address-cells = <1>;
> >> + #size-cells = <0>;
> >> + };
> >>
> >> };
> >>
> >> hdmi: hdmi at ff980000 {
^ permalink raw reply
* [PATCH 1/2] mmc: mediatek: Fix module autoload
From: Ulf Hansson @ 2016-10-21 8:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1476720825-30442-1-git-send-email-javier@osg.samsung.com>
On 17 October 2016 at 18:13, Javier Martinez Canillas
<javier@osg.samsung.com> wrote:
> If the driver is built as a module, autoload won't work because the module
> alias information is not filled. So user-space can't match the registered
> device with the corresponding module.
>
> Export the module alias information using the MODULE_DEVICE_TABLE() macro.
>
> Before this patch:
>
> $ modinfo drivers/mmc/host/mtk-sd.ko | grep alias
> $
>
> After this patch:
>
> $ modinfo drivers/mmc/host/mtk-sd.ko | grep alias
> alias: of:N*T*Cmediatek,mt8135-mmcC*
> alias: of:N*T*Cmediatek,mt8135-mmc
>
> Signed-off-by: Javier Martinez Canillas <javier@osg.samsung.com>
Thanks, applied for next!
Kind regards
Uffe
> ---
>
> drivers/mmc/host/mtk-sd.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
> index 84e9afcb5c09..86af0b199a54 100644
> --- a/drivers/mmc/host/mtk-sd.c
> +++ b/drivers/mmc/host/mtk-sd.c
> @@ -1713,6 +1713,7 @@ static const struct of_device_id msdc_of_ids[] = {
> { .compatible = "mediatek,mt8135-mmc", },
> {}
> };
> +MODULE_DEVICE_TABLE(of, msdc_of_ids);
>
> static struct platform_driver mt_msdc_driver = {
> .probe = msdc_drv_probe,
> --
> 2.7.4
>
^ permalink raw reply
* [PATCH v2] mmc: sunxi: Prevent against null dereference for vmmc
From: Ulf Hansson @ 2016-10-21 8:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161019133304.22915-1-maxime.ripard@free-electrons.com>
On 19 October 2016 at 15:33, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> VMMC is an optional regulator, which means that mmc_regulator_get_supply
> will only return an error in case of a deferred probe, but not when the
> regulator is not set in the DT.
>
> However, the sunxi driver assumes that VMMC is always there, and doesn't
> check the value of the regulator pointer before using it, which obviously
> leads to a (close to) null pointer dereference.
>
> Add proper checks to prevent that.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Thanks, applied for next!
Kind regards
Uffe
>
> ---
> Changes from v1:
> - remove redundant error message
> ---
> drivers/mmc/host/sunxi-mmc.c | 15 ++++++++++-----
> 1 file changed, 10 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
> index c0a5c676d0e8..b1d1303389a7 100644
> --- a/drivers/mmc/host/sunxi-mmc.c
> +++ b/drivers/mmc/host/sunxi-mmc.c
> @@ -822,10 +822,13 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> break;
>
> case MMC_POWER_UP:
> - host->ferror = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
> - ios->vdd);
> - if (host->ferror)
> - return;
> + if (!IS_ERR(mmc->supply.vmmc)) {
> + host->ferror = mmc_regulator_set_ocr(mmc,
> + mmc->supply.vmmc,
> + ios->vdd);
> + if (host->ferror)
> + return;
> + }
>
> if (!IS_ERR(mmc->supply.vqmmc)) {
> host->ferror = regulator_enable(mmc->supply.vqmmc);
> @@ -847,7 +850,9 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> case MMC_POWER_OFF:
> dev_dbg(mmc_dev(mmc), "power off!\n");
> sunxi_mmc_reset_host(host);
> - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> + if (!IS_ERR(mmc->supply.vmmc))
> + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> +
> if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled)
> regulator_disable(mmc->supply.vqmmc);
> host->vqmmc_enabled = false;
> --
> 2.9.3
>
^ permalink raw reply
* [PATCH v2 2/2] arm64/numa: fix incorrect log for memory-less node
From: Hanjun Guo @ 2016-10-21 8:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477037636-10077-1-git-send-email-guohanjun@huawei.com>
From: Hanjun Guo <hanjun.guo@linaro.org>
When booting on NUMA system with memory-less node (no
memory dimm on this memory controller), the print
for setup_node_data() is incorrect:
NUMA: Initmem setup node 2 [mem 0x00000000-0xffffffffffffffff]
It can be fixed by printing [mem 0x00000000-0x00000000] when
end_pfn is 0, but print <memory-less node> will be more useful.
Fixes: 1a2db300348b ("arm64, numa: Add NUMA support for arm64 platforms.")
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Ganapatrao Kulkarni <gkulkarni@caviumnetworks.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Yisheng Xie <xieyisheng1@huawei.com>
---
arch/arm64/mm/numa.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c
index 9a71d06..4b32168 100644
--- a/arch/arm64/mm/numa.c
+++ b/arch/arm64/mm/numa.c
@@ -223,8 +223,11 @@ static void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn)
void *nd;
int tnid;
- pr_info("Initmem setup node %d [mem %#010Lx-%#010Lx]\n",
- nid, start_pfn << PAGE_SHIFT, (end_pfn << PAGE_SHIFT) - 1);
+ if (start_pfn < end_pfn)
+ pr_info("Initmem setup node %d [mem %#010Lx-%#010Lx]\n", nid,
+ start_pfn << PAGE_SHIFT, (end_pfn << PAGE_SHIFT) - 1);
+ else
+ pr_info("Initmem setup node %d [<memory-less node>]\n", nid);
nd_pa = memblock_alloc_try_nid(nd_size, SMP_CACHE_BYTES, nid);
nd = __va(nd_pa);
--
1.7.12.4
^ permalink raw reply related
* [PATCH v2 1/2] arm64/numa: fix pcpu_cpu_distance() to get correct CPU proximity
From: Hanjun Guo @ 2016-10-21 8:13 UTC (permalink / raw)
To: linux-arm-kernel
From: Yisheng Xie <xieyisheng1@huawei.com>
The pcpu_build_alloc_info() function group CPUs according to their
proximity, by call callback function @cpu_distance_fn from different
ARCHs.
For arm64 the callback of @cpu_distance_fn is
pcpu_cpu_distance(from, to)
-> node_distance(from, to)
The @from and @to for function node_distance() should be nid.
However, pcpu_cpu_distance() in arch/arm64/mm/numa.c just past the
cpu id for @from and @to, and didn't convert to numa node id.
For this incorrect cpu proximity get from ARCH, it may cause each CPU
in one group and make group_cnt out of bound:
setup_per_cpu_areas()
pcpu_embed_first_chunk()
pcpu_build_alloc_info()
in pcpu_build_alloc_info, since cpu_distance_fn will return
REMOTE_DISTANCE if we pass cpu ids (0,1,2...), so
cpu_distance_fn(cpu, tcpu) > LOCAL_DISTANCE will wrongly be ture.
This may results in triggering the BUG_ON(unit != nr_units) later:
[ 0.000000] kernel BUG at mm/percpu.c:1916!
[ 0.000000] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
[ 0.000000] Modules linked in:
[ 0.000000] CPU: 0 PID: 0 Comm: swapper Not tainted 4.9.0-rc1-00003-g14155ca-dirty #26
[ 0.000000] Hardware name: Hisilicon Hi1616 Evaluation Board (DT)
[ 0.000000] task: ffff000008d6e900 task.stack: ffff000008d60000
[ 0.000000] PC is at pcpu_embed_first_chunk+0x420/0x704
[ 0.000000] LR is at pcpu_embed_first_chunk+0x3bc/0x704
[ 0.000000] pc : [<ffff000008c754f4>] lr : [<ffff000008c75490>] pstate: 800000c5
[ 0.000000] sp : ffff000008d63eb0
[ 0.000000] x29: ffff000008d63eb0 [ 0.000000] x28: 0000000000000000
[ 0.000000] x27: 0000000000000040 [ 0.000000] x26: ffff8413fbfcef00
[ 0.000000] x25: 0000000000000042 [ 0.000000] x24: 0000000000000042
[ 0.000000] x23: 0000000000001000 [ 0.000000] x22: 0000000000000046
[ 0.000000] x21: 0000000000000001 [ 0.000000] x20: ffff000008cb3bc8
[ 0.000000] x19: ffff8413fbfcf570 [ 0.000000] x18: 0000000000000000
[ 0.000000] x17: ffff000008e49ae0 [ 0.000000] x16: 0000000000000003
[ 0.000000] x15: 000000000000001e [ 0.000000] x14: 0000000000000004
[ 0.000000] x13: 0000000000000000 [ 0.000000] x12: 000000000000006f
[ 0.000000] x11: 00000413fbffff00 [ 0.000000] x10: 0000000000000004
[ 0.000000] x9 : 0000000000000000 [ 0.000000] x8 : 0000000000000001
[ 0.000000] x7 : ffff8413fbfcf63c [ 0.000000] x6 : ffff000008d65d28
[ 0.000000] x5 : ffff000008d65e50 [ 0.000000] x4 : 0000000000000000
[ 0.000000] x3 : ffff000008cb3cc8 [ 0.000000] x2 : 0000000000000040
[ 0.000000] x1 : 0000000000000040 [ 0.000000] x0 : 0000000000000000
[...]
[ 0.000000] Call trace:
[ 0.000000] Exception stack(0xffff000008d63ce0 to 0xffff000008d63e10)
[ 0.000000] 3ce0: ffff8413fbfcf570 0001000000000000 ffff000008d63eb0 ffff000008c754f4
[ 0.000000] 3d00: ffff000008d63d50 ffff0000081af210 00000413fbfff010 0000000000001000
[ 0.000000] 3d20: ffff000008d63d50 ffff0000081af220 00000413fbfff010 0000000000001000
[ 0.000000] 3d40: 00000413fbfcef00 0000000000000004 ffff000008d63db0 ffff0000081af390
[ 0.000000] 3d60: 00000413fbfcef00 0000000000001000 0000000000000000 0000000000001000
[ 0.000000] 3d80: 0000000000000000 0000000000000040 0000000000000040 ffff000008cb3cc8
[ 0.000000] 3da0: 0000000000000000 ffff000008d65e50 ffff000008d65d28 ffff8413fbfcf63c
[ 0.000000] 3dc0: 0000000000000001 0000000000000000 0000000000000004 00000413fbffff00
[ 0.000000] 3de0: 000000000000006f 0000000000000000 0000000000000004 000000000000001e
[ 0.000000] 3e00: 0000000000000003 ffff000008e49ae0
[ 0.000000] [<ffff000008c754f4>] pcpu_embed_first_chunk+0x420/0x704
[ 0.000000] [<ffff000008c6658c>] setup_per_cpu_areas+0x38/0xc8
[ 0.000000] [<ffff000008c608d8>] start_kernel+0x10c/0x390
[ 0.000000] [<ffff000008c601d8>] __primary_switched+0x5c/0x64
[ 0.000000] Code: b8018660 17ffffd7 6b16037f 54000080 (d4210000)
[ 0.000000] ---[ end trace 0000000000000000 ]---
[ 0.000000] Kernel panic - not syncing: Attempted to kill the idle task!
Fix by getting cpu's node id with early_cpu_to_node() then pass it
to node_distance() as the original intention.
Fixes: 7af3a0a99252 ("arm64/numa: support HAVE_SETUP_PER_CPU_AREA")
Signed-off-by: Yisheng Xie <xieyisheng1@huawei.com>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Zhen Lei <thunder.leizhen@huawei.com>
---
arch/arm64/mm/numa.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c
index 778a985..9a71d06 100644
--- a/arch/arm64/mm/numa.c
+++ b/arch/arm64/mm/numa.c
@@ -147,7 +147,7 @@ static int __init early_cpu_to_node(int cpu)
static int __init pcpu_cpu_distance(unsigned int from, unsigned int to)
{
- return node_distance(from, to);
+ return node_distance(early_cpu_to_node(from), early_cpu_to_node(to));
}
static void * __init pcpu_fc_alloc(unsigned int cpu, size_t size,
--
1.7.12.4
^ permalink raw reply related
* [PATCH v5 3/7] drm: sunxi: add DE2 HDMI support
From: Jean-Francois Moine @ 2016-10-21 8:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.1477142934.git.moinejf@free.fr>
This patch adds a HDMI driver to the DE2 based Allwinner's SoCs
as A83T and H3.
Audio and video are supported.
Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
.../devicetree/bindings/display/sunxi/hdmi.txt | 52 ++
drivers/gpu/drm/sunxi/Kconfig | 8 +
drivers/gpu/drm/sunxi/Makefile | 2 +
drivers/gpu/drm/sunxi/de2_hdmi.c | 396 +++++++++
drivers/gpu/drm/sunxi/de2_hdmi.h | 40 +
drivers/gpu/drm/sunxi/de2_hdmi_io.c | 927 +++++++++++++++++++++
drivers/gpu/drm/sunxi/de2_hdmi_io.h | 25 +
7 files changed, 1450 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/sunxi/hdmi.txt
create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi.c
create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi.h
create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi_io.c
create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi_io.h
diff --git a/Documentation/devicetree/bindings/display/sunxi/hdmi.txt b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
new file mode 100644
index 0000000..0558c07
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/sunxi/hdmi.txt
@@ -0,0 +1,52 @@
+Allwinner HDMI Transmitter
+==========================
+
+The Allwinner HDMI transmitters are included in the SoCs.
+They support audio and video.
+
+Required properties:
+ - #address-cells : should be <1>
+ - #size-cells : should be <0>
+ - compatible : should be
+ "allwinner,sun8i-a83t-hdmi" or
+ "allwinner,sun8i-h3-hdmi"
+ - clocks : phandles to the HDMI clocks as described in
+ Documentation/devicetree/bindings/clock/clock-bindings.txt
+ - clock-names : must be
+ "gate" : bus gate
+ "clock" : streaming clock
+ "ddc-clock" : DDC clock
+ - resets : One or two phandles to the HDMI resets
+ - reset-names : must be
+ "hdmi0" and "hdmi1"
+
+Required nodes:
+ - port: Audio and video input port nodes with endpoint definitions
+ as defined in Documentation/devicetree/bindings/graph.txt.
+
+Example:
+
+ hdmi: hdmi at 01ee0000 {
+ compatible = "allwinner,sun8i-a83t-hdmi";
+ reg = <0x01ee0000 0x20000>;
+ clocks = <&ccu CLK_BUS_HDMI>, <&ccu CLK_HDMI>,
+ <&ccu CLK_HDMI_DDC>;
+ clock-names = "gate", "clock", "ddc-clock";
+ resets = <&ccu RST_HDMI0>, <&ccu RST_HDMI1>;
+ reset-names = "hdmi0", "hdmi1";
+ ...
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port at 0 { /* video */
+ reg = <0>;
+ hdmi_lcd1: endpoint {
+ remote-endpoint = <&lcd1_hdmi>;
+ };
+ };
+ port at 1 { /* audio */
+ reg = <1>;
+ hdmi_i2s2: endpoint {
+ remote-endpoint = <&i2s2_hdmi>;
+ };
+ };
+ };
diff --git a/drivers/gpu/drm/sunxi/Kconfig b/drivers/gpu/drm/sunxi/Kconfig
index 56bde2e..4c82153 100644
--- a/drivers/gpu/drm/sunxi/Kconfig
+++ b/drivers/gpu/drm/sunxi/Kconfig
@@ -19,3 +19,11 @@ config DRM_SUNXI_DE2
Choose this option if your Allwinner chipset has the DE2 interface
as the A64, A83T and H3. If M is selected the module will be called
sunxi-de2-drm.
+
+config DRM_SUNXI_DE2_HDMI
+ tristate "Support for DE2 HDMI"
+ depends on DRM_SUNXI_DE2
+ select SND_SOC_SUNXI_HDMI if SND_SOC
+ help
+ Choose this option if you use want HDMI on DE2.
+ If M is selected the module will be called sunxi-de2-hdmi.
diff --git a/drivers/gpu/drm/sunxi/Makefile b/drivers/gpu/drm/sunxi/Makefile
index 62220cb..a268069 100644
--- a/drivers/gpu/drm/sunxi/Makefile
+++ b/drivers/gpu/drm/sunxi/Makefile
@@ -3,5 +3,7 @@
#
sunxi-de2-drm-objs := de2_drv.o de2_de.o de2_crtc.o de2_plane.o
+sunxi-de2-hdmi-objs := de2_hdmi.o de2_hdmi_io.o
obj-$(CONFIG_DRM_SUNXI_DE2) += sunxi-de2-drm.o
+obj-$(CONFIG_DRM_SUNXI_DE2_HDMI) += sunxi-de2-hdmi.o
diff --git a/drivers/gpu/drm/sunxi/de2_hdmi.c b/drivers/gpu/drm/sunxi/de2_hdmi.c
new file mode 100644
index 0000000..a2324cc
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/de2_hdmi.c
@@ -0,0 +1,396 @@
+/*
+ * Allwinner DRM driver - HDMI
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/component.h>
+#include <linux/clk.h>
+#include <linux/hdmi.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include "de2_hdmi.h"
+#include "de2_hdmi_io.h"
+
+static const struct of_device_id de2_hdmi_dt_ids[] = {
+ { .compatible = "allwinner,sun8i-a83t-hdmi",
+ .data = (void *) SOC_A83T },
+ { .compatible = "allwinner,sun8i-h3-hdmi",
+ .data = (void *) SOC_H3 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, de2_hdmi_dt_ids);
+
+#define conn_to_priv(x) \
+ container_of(x, struct de2_hdmi_priv, connector)
+
+#define enc_to_priv(x) \
+ container_of(x, struct de2_hdmi_priv, encoder)
+
+#define codec_to_priv(x) \
+ container_of(x, struct de2_hdmi_priv, codec)
+
+#if IS_ENABLED(CONFIG_SND_SOC_SUNXI_HDMI)
+/* --- audio functions --- */
+static int de2_hdmi_set_audio_input(struct device *dev,
+ int enable,
+ unsigned sample_rate,
+ unsigned sample_bit)
+{
+ struct de2_hdmi_priv *priv = codec_to_priv(dev_get_drvdata(dev));
+ int ret;
+
+ if (!enable)
+ return 0;
+
+ mutex_lock(&priv->mutex);
+ ret = hdmi_io_audio(priv, sample_rate, sample_bit);
+ mutex_unlock(&priv->mutex);
+
+ return ret;
+}
+#endif
+
+/* --- encoder functions --- */
+
+static void de2_hdmi_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+ priv->cea_mode = drm_match_cea_mode(mode);
+
+ DRM_DEBUG_DRIVER("cea_mode %d\n", priv->cea_mode);
+
+ clk_set_rate(priv->clk, mode->clock * 1000);
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_video_mode(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void de2_hdmi_encoder_enable(struct drm_encoder *encoder)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_video_on(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static void de2_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+ struct de2_hdmi_priv *priv = enc_to_priv(encoder);
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_video_off(priv);
+ mutex_unlock(&priv->mutex);
+}
+
+static const struct drm_encoder_helper_funcs de2_hdmi_encoder_helper_funcs = {
+ .mode_set = de2_hdmi_encoder_mode_set,
+ .enable = de2_hdmi_encoder_enable,
+ .disable = de2_hdmi_encoder_disable,
+};
+
+static const struct drm_encoder_funcs de2_hdmi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+/* --- connector functions --- */
+static int de2_hdmi_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct de2_hdmi_priv *priv = conn_to_priv(connector);
+ int cea_mode = drm_match_cea_mode(mode);
+
+ if (hdmi_io_mode_valid(cea_mode) < 0)
+ return MODE_NOMODE;
+
+ if (connector->eld[0])
+ priv->codec.eld = connector->eld; /* audio parameters */
+
+ return MODE_OK;
+}
+
+static enum drm_connector_status de2_hdmi_connector_detect(
+ struct drm_connector *connector, bool force)
+{
+ struct de2_hdmi_priv *priv = conn_to_priv(connector);
+ int ret;
+
+ mutex_lock(&priv->mutex);
+ ret = hdmi_io_get_hpd(priv);
+ mutex_unlock(&priv->mutex);
+
+ return ret ? connector_status_connected :
+ connector_status_disconnected;
+}
+
+static int read_edid_block(void *data, u8 *buf,
+ unsigned int blk, size_t length)
+{
+ struct de2_hdmi_priv *priv = data;
+ int ret;
+
+ mutex_lock(&priv->mutex);
+ ret = hdmi_io_ddc_read(priv,
+ blk / 2, (blk & 1) ? 128 : 0,
+ length, buf);
+ mutex_unlock(&priv->mutex);
+
+ return ret;
+}
+
+static int de2_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+ struct de2_hdmi_priv *priv = conn_to_priv(connector);
+ struct edid *edid;
+ int n;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ priv->codec.eld = NULL;
+
+ edid = drm_do_get_edid(connector, read_edid_block, priv);
+
+ if (!edid) {
+ dev_warn(priv->dev, "failed to read EDID\n");
+ if (!connector->cmdline_mode.specified)
+ return 0;
+
+ return drm_add_modes_noedid(connector,
+ connector->cmdline_mode.xres,
+ connector->cmdline_mode.yres);
+ }
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ n = drm_add_edid_modes(connector, edid);
+
+ drm_edid_to_eld(connector, edid);
+
+ kfree(edid);
+
+ DRM_DEBUG_DRIVER("%s EDID ok %d modes\n",
+ connector->eld[0] ? "HDMI" : "DVI", n);
+
+ return n;
+}
+
+static const
+struct drm_connector_helper_funcs de2_hdmi_connector_helper_funcs = {
+ .get_modes = de2_hdmi_connector_get_modes,
+ .mode_valid = de2_hdmi_connector_mode_valid,
+};
+
+static const struct drm_connector_funcs de2_hdmi_connector_funcs = {
+ .dpms = drm_atomic_helper_connector_dpms,
+ .reset = drm_atomic_helper_connector_reset,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = de2_hdmi_connector_detect,
+ .destroy = drm_connector_cleanup,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static void de2_hdmi_cleanup(struct de2_hdmi_priv *priv)
+{
+ clk_disable_unprepare(priv->clk_ddc);
+ clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->gate);
+ if (!IS_ERR_OR_NULL(priv->rstc1))
+ reset_control_assert(priv->rstc1);
+ if (!IS_ERR_OR_NULL(priv->rstc0))
+ reset_control_assert(priv->rstc0);
+}
+
+static int de2_hdmi_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm = data;
+ struct de2_hdmi_priv *priv = codec_to_priv(dev_get_drvdata(dev));
+ struct drm_encoder *encoder = &priv->encoder;
+ struct drm_connector *connector = &priv->connector;
+ int ret;
+
+ encoder->possible_crtcs =
+ drm_of_find_possible_crtcs(drm, dev->of_node);
+
+ /* if no CRTC, delay */
+ if (encoder->possible_crtcs == 0)
+ return -EPROBE_DEFER;
+
+ /* HDMI init */
+ ret = reset_control_deassert(priv->rstc0);
+ if (ret)
+ goto err;
+ ret = reset_control_deassert(priv->rstc1);
+ if (ret)
+ goto err;
+ if (!IS_ERR(priv->gate)) {
+ ret = clk_prepare_enable(priv->gate);
+ if (ret)
+ goto err;
+ }
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ goto err;
+ ret = clk_prepare_enable(priv->clk_ddc);
+ if (ret)
+ goto err;
+
+ mutex_lock(&priv->mutex);
+ hdmi_io_init(priv);
+ hdmi_io_reset(priv); /* hpd reset */
+ mutex_unlock(&priv->mutex);
+
+ /* encoder init */
+ ret = drm_encoder_init(drm, encoder, &de2_hdmi_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ goto err;
+
+ drm_encoder_helper_add(encoder, &de2_hdmi_encoder_helper_funcs);
+
+ /* connector init */
+ ret = drm_connector_init(drm, connector,
+ &de2_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+ if (ret)
+ goto err_connector;
+
+ connector->interlace_allowed = 1;
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
+ drm_connector_helper_add(connector,
+ &de2_hdmi_connector_helper_funcs);
+
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ return 0;
+
+err_connector:
+ drm_encoder_cleanup(encoder);
+err:
+ de2_hdmi_cleanup(priv);
+ DRM_DEBUG_DRIVER("err %d\n", ret);
+ return ret;
+}
+
+static void de2_hdmi_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct de2_hdmi_priv *priv = codec_to_priv(dev_get_drvdata(dev));
+
+ drm_connector_cleanup(&priv->connector);
+ drm_encoder_cleanup(&priv->encoder);
+ de2_hdmi_cleanup(priv);
+}
+
+static const struct component_ops de2_hdmi_ops = {
+ .bind = de2_hdmi_bind,
+ .unbind = de2_hdmi_unbind,
+};
+
+static int de2_hdmi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct de2_hdmi_priv *priv;
+ struct resource *res;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, &priv->codec);
+ priv->dev = dev;
+
+ mutex_init(&priv->mutex);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get memory resource\n");
+ return -ENXIO;
+ }
+ priv->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->mmio)) {
+ dev_err(dev, "failed to map registers\n");
+ return PTR_ERR(priv->mmio);
+ }
+
+ priv->gate = devm_clk_get(dev, "gate"); /* optional */
+
+ priv->clk = devm_clk_get(dev, "clock");
+ if (IS_ERR(priv->clk)) {
+ dev_err(dev, "hdmi clock err %d\n",
+ (int) PTR_ERR(priv->clk));
+ return PTR_ERR(priv->clk);
+ }
+ priv->clk_ddc = devm_clk_get(dev, "ddc-clock");
+ if (IS_ERR(priv->clk_ddc)) {
+ dev_err(dev, "hdmi-ddc clock err %d\n",
+ (int) PTR_ERR(priv->clk_ddc));
+ return PTR_ERR(priv->clk_ddc);
+ }
+
+ priv->rstc0 = devm_reset_control_get(dev, "hdmi0");
+ if (IS_ERR(priv->rstc0)) {
+ dev_err(dev, "reset controller err %d\n",
+ (int) PTR_ERR(priv->rstc0));
+ return PTR_ERR(priv->rstc0);
+ }
+
+ priv->rstc1 = devm_reset_control_get(dev, "hdmi1");
+ if (IS_ERR(priv->rstc1)) {
+ dev_err(dev, "reset controller err %d\n",
+ (int) PTR_ERR(priv->rstc1));
+ return PTR_ERR(priv->rstc1);
+ }
+
+ priv->soc_type = (int) of_match_device(de2_hdmi_dt_ids,
+ &pdev->dev)->data;
+
+ if (IS_ENABLED(CONFIG_SND_SOC_SUNXI_HDMI)) {
+ priv->codec.set_audio_input = de2_hdmi_set_audio_input;
+ sunxi_hdmi_codec_register(dev);
+ }
+
+ return component_add(dev, &de2_hdmi_ops);
+}
+
+static int de2_hdmi_remove(struct platform_device *pdev)
+{
+ if (IS_ENABLED(CONFIG_SND_SOC_SUNXI_HDMI))
+ sunxi_hdmi_codec_unregister(&pdev->dev);
+ component_del(&pdev->dev, &de2_hdmi_ops);
+
+ return 0;
+}
+
+static struct platform_driver de2_hdmi_driver = {
+ .probe = de2_hdmi_probe,
+ .remove = de2_hdmi_remove,
+ .driver = {
+ .name = "sunxi-de2-hdmi",
+ .of_match_table = of_match_ptr(de2_hdmi_dt_ids),
+ },
+};
+
+module_platform_driver(de2_hdmi_driver);
+
+MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
+MODULE_DESCRIPTION("Allwinner DE2 HDMI encoder/connector");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sunxi/de2_hdmi.h b/drivers/gpu/drm/sunxi/de2_hdmi.h
new file mode 100644
index 0000000..915d609
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/de2_hdmi.h
@@ -0,0 +1,40 @@
+#ifndef __DE2_HDMI_H__
+#define __DE2_HDMI_H__
+/*
+ * Copyright (C) 2016 Jean-Fran??ois Moine
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <drm/drmP.h>
+#include <sound/sunxi_hdmi.h>
+
+/* SoC types */
+#define SOC_A83T 0
+#define SOC_H3 1
+
+struct de2_hdmi_priv {
+ struct device *dev;
+ void __iomem *mmio;
+
+ struct drm_encoder encoder;
+ struct drm_connector connector;
+
+ struct clk *clk;
+ struct clk *clk_ddc;
+ struct clk *gate;
+ struct reset_control *rstc0;
+ struct reset_control *rstc1;
+
+ struct mutex mutex;
+ u8 soc_type;
+ u8 cea_mode;
+ struct sunxi_hdmi_codec codec;
+};
+
+#endif /* __DE2_HDMI_H__ */
diff --git a/drivers/gpu/drm/sunxi/de2_hdmi_io.c b/drivers/gpu/drm/sunxi/de2_hdmi_io.c
new file mode 100644
index 0000000..127b220
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/de2_hdmi_io.c
@@ -0,0 +1,927 @@
+/*
+ * Allwinner A83T and H3 HDMI lowlevel functions
+ *
+ * Copyright (C) 2016 Jean-Francois Moine <moinejf@free.fr>
+ * Copyright (c) 2016 Allwinnertech Co., Ltd.
+ *
+ * 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.
+ */
+
+/*
+ * The HDMI controller in the A83T and H3 seems to be a
+ * Synopsys DesignWare HDMI controller.
+ * The PHYs are unknown.
+ * Documentation:
+ * https://linux-sunxi.org/DWC_HDMI_Controller
+ * https://www.synopsys.com/dw/doc.php/ds/c/dwc_hdmi_tx_csds.pdf
+ */
+
+#include <linux/hdmi.h>
+
+#include "de2_hdmi.h"
+#include "de2_hdmi_io.h"
+
+/* DW registers (obfuscated addresses) */
+
+/* Interrupt Registers */
+#define R_0100_HDMI_IH_FC_STAT0 0x0010
+#define R_0101_HDMI_IH_FC_STAT1 0x0011
+#define R_0102_HDMI_IH_FC_STAT2 0x8010
+#define R_0103_HDMI_IH_AS_STAT0 0x8011
+#define R_0104_HDMI_IH_PHY_STAT0 0x0012
+#define R_0105_HDMI_IH_I2CM_STAT0 0x0013
+#define R_0106_HDMI_IH_CEC_STAT0 0x8012
+#define R_0107_HDMI_IH_VP_STAT0 0x8013
+#define R_0108_HDMI_IH_I2CMPHY_STAT0 0x4010
+#define R_01ff_HDMI_IH_MUTE 0xf01f
+
+/* Video Sample Registers */
+#define R_0200_HDMI_TX_INVID0 0x0800
+#define R_0201_HDMI_TX_INSTUFFING 0x0801
+#define R_0202_HDMI_TX_GYDATA0 0x8800
+#define R_0203_HDMI_TX_GYDATA1 0x8801
+#define R_0204_HDMI_TX_RCRDATA0 0x0802
+#define R_0205_HDMI_TX_RCRDATA1 0x0803
+#define R_0206_HDMI_TX_BCBDATA0 0x8802
+#define R_0207_HDMI_TX_BCBDATA1 0x8803
+
+/* Video Packetizer Registers */
+#define R_0801_HDMI_VP_PR_CD 0x0401
+#define R_0802_HDMI_VP_STUFF 0x8400
+#define R_0803_HDMI_VP_REMAP 0x8401
+#define R_0804_HDMI_VP_CONF 0x0402
+#define R_0807_HDMI_VP_MASK 0x8403
+
+/* Frame Composer Registers */
+#define R_1000_HDMI_FC_INVIDCONF 0x0040
+#define HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH 0x10
+#define R_1001_HDMI_FC_INHACTV0 0x0041
+#define R_1002_HDMI_FC_INHACTV1 0x8040
+#define R_1003_HDMI_FC_INHBLANK0 0x8041
+#define R_1004_HDMI_FC_INHBLANK1 0x0042
+#define R_1005_HDMI_FC_INVACTV0 0x0043
+#define R_1006_HDMI_FC_INVACTV1 0x8042
+#define R_1007_HDMI_FC_INVBLANK 0x8043
+#define R_1008_HDMI_FC_HSYNCINDELAY0 0x4040
+#define R_1009_HDMI_FC_HSYNCINDELAY1 0x4041
+#define R_100a_HDMI_FC_HSYNCINWIDTH1 0xc040
+#define R_100b_HDMI_FC_HSYNCINWIDTH1 0xc041
+#define R_100c_HDMI_FC_VSYNCINDELAY 0x4042
+#define R_100d_HDMI_FC_VSYNCINWIDTH 0x4043
+#define R_1011_HDMI_FC_CTRLDUR 0x0045
+#define R_1012_HDMI_FC_EXCTRLDUR 0x8044
+#define R_1013_HDMI_FC_EXCTRLSPAC 0x8045
+#define R_1014_HDMI_FC_CH0PREAM 0x0046
+#define R_1015_HDMI_FC_CH1PREAM 0x0047
+#define R_1016_HDMI_FC_CH2PREAM 0x8046
+#define R_1018_HDMI_FC_GCP 0x4044
+#define R_1019_HDMI_FC_AVICONF0 0x4045
+#define HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN 0x20
+#define R_101a_HDMI_FC_AVICONF1 0xc044
+#define R_101b_HDMI_FC_AVICONF2 0xc045
+#define R_101c_HDMI_FC_AVIVID 0x4046
+#define R_1025_HDMI_FC_AUDICONF0 0x2043
+#define R_1026_HDMI_FC_AUDICONF1 0xa042
+#define R_1027_HDMI_FC_AUDICONF2 0xa043
+#define R_1028_HDMI_FC_AUDICONF3 0x6040
+#define R_1029_HDMI_FC_VSDIEEEID0 0x6041
+#define R_1030_HDMI_FC_VSDIEEEID1 0x2044
+#define R_1031_HDMI_FC_VSDIEEEID2 0x2045
+#define R_1032_HDMI_FC_VSDPAYLOAD0 0xa044
+#define R_1033_HDMI_FC_VSDPAYLOAD1 0xa045
+#define R_1034_HDMI_FC_VSDPAYLOAD2 0x2046
+#define R_1063_HDMI_FC_AUDSCONF 0xa049
+#define R_1065_HDMI_FC_AUDSV 0x204b
+#define R_1066_HDMI_FC_AUDSU 0xa04a
+#define R_1067_HDMI_FC_AUDSCHNLS0 0xa04b
+#define HDMI_FC_AUDSCHNLS0_CGMSA 0x30
+#define R_1068_HDMI_FC_AUDSCHNLS1 0x6048
+#define R_1069_HDMI_FC_AUDSCHNLS2 0x6049
+#define R_106a_HDMI_FC_AUDSCHNLS3 0xe048
+#define HDMI_FC_AUDSCHNLS3_OIEC_CH0(v) (v)
+#define HDMI_FC_AUDSCHNLS3_OIEC_CH1(v) (v << 4)
+#define R_106b_HDMI_FC_AUDSCHNLS4 0xe049
+#define HDMI_FC_AUDSCHNLS4_OIEC_CH2(v) (v)
+#define HDMI_FC_AUDSCHNLS4_OIEC_CH3(v) (v << 4)
+#define R_106c_HDMI_FC_AUDSCHNLS5 0x604a
+#define HDMI_FC_AUDSCHNLS5_OIEC_CH0(v) (v)
+#define HDMI_FC_AUDSCHNLS5_OIEC_CH1(v) (v << 4)
+#define R_106d_HDMI_FC_AUDSCHNLS6 0x604b
+#define HDMI_FC_AUDSCHNLS6_OIEC_CH2(v) (v)
+#define HDMI_FC_AUDSCHNLS6_OIEC_CH3(v) (v << 4)
+#define R_106e_HDMI_FC_AUDSCHNLS7 0xe04a
+#define R_106f_HDMI_FC_AUDSCHNLS8 0xe04b
+#define HDMI_FC_AUDSCHNLS8_WORDLENGTH(v) (v)
+#define R_10b3_HDMI_FC_DATAUTO0 0xb045
+#define R_10b4_HDMI_FC_DATAUTO1 0x3046
+#define R_10b5_HDMI_FC_DATAUTO2 0x3047
+#define R_10d2_HDMI_FC_MASK0 0x904c
+#define R_10d6_HDMI_FC_MASK1 0x904e
+#define R_10da_HDMI_FC_MASK2 0xd04c
+#define R_10e0_HDMI_FC_PRCONF 0x3048
+#define R_1103_HDMI_FC_GMD_CONF 0x8051
+#define R_1104_HDMI_FC_GMD_HB 0x0052
+#define R_1200_HDMI_FC_DBGFORCE 0x0840
+#define HDMI_FC_DBGFORCE_FORCEAUDIO BIT(4)
+#define HDMI_FC_DBGFORCE_FORCEVIDEO BIT(0)
+#define R_1219_HDMI_FC_DBGTMDS0 0x4845
+
+/* HDMI Source PHY Registers */
+#define R_3000_HDMI_PHY_CONF0 0x0240
+#define HDMI_PHY_CONF0_PDZ BIT(7)
+#define HDMI_PHY_CONF0_ENTMDS BIT(6)
+#define HDMI_PHY_CONF0_SPARECTRL BIT(5)
+#define HDMI_PHY_CONF0_GEN2_PDDQ BIT(4)
+#define HDMI_PHY_CONF0_GEN2_TXPWRON BIT(3)
+#define HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE BIT(2)
+#define HDMI_PHY_CONF0_SELDATAENPOL BIT(1)
+#define HDMI_PHY_CONF0_SELDIPIF BIT(0)
+#define R_3001_HDMI_PHY_TST0 0x0241
+#define HDMI_PHY_TST0_TSTCLR BIT(5)
+#define R_3005_HDMI_PHY_INT0 0x0243
+#define R_3006_HDMI_PHY_MASK0 0x8242
+
+/* HDMI Master PHY Registers */
+#define R_3020_HDMI_PHY_I2CM_SLAVE_ADDR 0x2240
+#define HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 0x69
+#define R_3021_HDMI_PHY_I2CM_ADDRESS_ADDR 0x2241
+#define R_3022_HDMI_PHY_I2CM_DATAO_1_ADDR 0xa240
+#define R_3023_HDMI_PHY_I2CM_DATAO_0_ADDR 0xa241
+#define R_3026_HDMI_PHY_I2CM_OPERATION_ADDR 0xa242
+#define HDMI_PHY_I2CM_OPERATION_ADDR_WRITE 0x10
+#define R_3027_HDMI_PHY_I2CM_INT_ADDR 0xa243
+#define R_3028_HDMI_PHY_I2CM_CTLINT_ADDR 0x6240
+
+/* Audio Sampler Registers */
+#define R_3100_HDMI_AUD_CONF0 0x0250
+#define R_3101_HDMI_AUD_CONF1 0x0251
+#define R_3102_HDMI_AUD_INT 0x8250
+#define R_3103_HDMI_AUD_CONF2 0x8251
+#define R_3200_HDMI_AUD_N1 0x0a40
+#define R_3201_HDMI_AUD_N2 0x0a41
+#define R_3202_HDMI_AUD_N3 0x8a40
+#define R_3205_HDMI_AUD_CTS3 0x0a43
+#define R_3206_HDMI_AUD_INPUTCLKFS 0x8a42
+#define R_3302_HDMI_AUD_SPDIFINT 0x8a50
+
+/* Generic Parallel Audio Interface Registers */
+#define R_3506_HDMI_GP_POL 0x8272
+
+/* Main Controller Registers */
+#define R_4001_HDMI_MC_CLKDIS 0x0081
+#define HDMI_MC_CLKDIS_HDCPCLK_DISABLE BIT(6)
+#define HDMI_MC_CLKDIS_AUDCLK_DISABLE BIT(3)
+#define HDMI_MC_CLKDIS_TMDSCLK_DISABLE BIT(1)
+#define R_4002_HDMI_MC_SWRSTZ 0x8080
+#define R_4004_HDMI_MC_FLOWCTRL 0x0082
+#define R_4005_HDMI_MC_PHYRSTZ 0x0083
+#define HDMI_MC_PHYRSTZ_DEASSERT BIT(0)
+
+/* HDCP Encryption Engine Registers */
+#define R_5000_HDMI_A_HDCPCFG0 0x00c0
+#define R_5001_HDMI_A_HDCPCFG1 0x00c1
+#define HDMI_A_HDCPCFG1_PH2UPSHFTENC BIT(2)
+#define HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE BIT(1)
+#define HDMI_A_HDCPCFG1_SWRESET BIT(0)
+#define R_5008_HDMI_A_APIINTMSK 0x40c0
+#define R_5009_HDMI_A_VIDPOLCFG 0x40c1
+#define HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH BIT(4)
+
+/* CEC Engine Registers */
+#define R_7d02_HDMI_CEC_MASK 0x86f0
+
+/* I2C Master Registers (E-DDC) */
+#define R_7e00_HDMI_I2CM_SLAVE 0x0ee0
+#define R_7e01_HDMI_I2CM_ADDRESS 0x0ee1
+#define R_7e03_HDMI_I2CM_DATAI 0x8ee1
+#define R_7e04_HDMI_I2CM_OPERATION 0x0ee2
+#define HDMI_I2CM_OPERATION_DDC_READ 0x02
+#define R_7e05_HDMI_I2CM_INT 0x0ee3
+#define R_7e06_HDMI_I2CM_CTLINT 0x8ee2
+#define R_7e07_HDMI_I2CM_DIV 0x8ee3
+#define R_7e08_HDMI_I2CM_SEGADDR 0x4ee0
+#define R_7e09_HDMI_I2CM_SOFTRSTZ 0x4ee1
+#define R_7e0a_HDMI_I2CM_SEGPTR 0xcee0
+#define R_7e0b_HDMI_I2CM_SS_SCL_HCNT_1_ADDR 0xcee1
+#define R_7e0c_HDMI_I2CM_SS_SCL_HCNT_0_ADDR 0x4ee2
+#define R_7e0d_HDMI_I2CM_SS_SCL_LCNT_1_ADDR 0x4ee3
+#define R_7e0e_HDMI_I2CM_SS_SCL_LCNT_0_ADDR 0xcee2
+#define R_7e0f_HDMI_I2CM_FS_SCL_HCNT_1_ADDR 0xcee3
+#define R_7e10_HDMI_I2CM_FS_SCL_HCNT_0_ADDR 0x0ee4
+#define R_7e11_HDMI_I2CM_FS_SCL_LCNT_1_ADDR 0x0ee5
+
+/*
+ * [0] = vic (CEA Video ID)
+ * [1] used in hdmi_phy_set / hdmi_io_audio
+ * [2..16] used in hdmi_io_video_mode
+ */
+static const struct para_tab {
+ u32 para[17];
+} ptbl[] = {
+ {{ 6, 1, 1, 1, 5, 3, 0, 1, 4, 0, 0, 160, 20, 38, 124, 240, 22}},
+ {{ 21, 11, 1, 1, 5, 3, 1, 1, 2, 0, 0, 160, 32, 24, 126, 32, 24}},
+ {{ 2, 11, 0, 0, 2, 6, 1, 0, 9, 0, 0, 208, 138, 16, 62, 224, 45}},
+ {{ 17, 11, 0, 0, 2, 5, 2, 0, 5, 0, 0, 208, 144, 12, 64, 64, 49}},
+ {{ 19, 4, 0, 0x60, 5, 5, 2, 2, 5, 1, 0, 0, 188, 184, 40, 208, 30}},
+ {{ 4, 4, 0, 0x60, 5, 5, 2, 1, 5, 0, 0, 0, 114, 110, 40, 208, 30}},
+ {{ 20, 4, 0, 0x61, 7, 5, 4, 2, 2, 2, 0, 128, 208, 16, 44, 56, 22}},
+ {{ 5, 4, 0, 0x60, 7, 5, 4, 1, 2, 0, 0, 128, 24, 88, 44, 56, 22}},
+ {{ 31, 2, 0, 0x60, 7, 5, 4, 2, 4, 2, 0, 128, 208, 16, 44, 56, 45}},
+ {{ 16, 2, 0, 0x60, 7, 5, 4, 1, 4, 0, 0, 128, 24, 88, 44, 56, 45}},
+ {{ 32, 4, 0, 0x60, 7, 5, 4, 3, 4, 2, 0, 128, 62, 126, 44, 56, 45}},
+ {{ 33, 4, 0, 0, 7, 5, 4, 2, 4, 2, 0, 128, 208, 16, 44, 56, 45}},
+ {{ 34, 4, 0, 0, 7, 5, 4, 1, 4, 0, 0, 128, 24, 88, 44, 56, 45}},
+#if 0
+ {{160, 2, 0, 0x60, 7, 5, 8, 3, 4, 1, 0, 128, 62, 126, 44, 157, 45}},
+ {{147, 2, 0, 0x60, 5, 5, 5, 2, 5, 1, 0, 0, 188, 184, 40, 190, 30}},
+ {{132, 2, 0, 0x60, 5, 5, 5, 1, 5, 0, 0, 0, 114, 110, 40, 160, 30}},
+ {{257, 1, 0, 0x60, 15, 10, 8, 2, 8, 0, 0, 0, 48, 176, 88, 112, 90}},
+ {{258, 1, 0, 0x60, 15, 10, 8, 5, 8, 4, 0, 0, 160, 32, 88, 112, 90}},
+#endif
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_SUNXI_HDMI)
+/* HDMI_FC_AUDSCHNLS7 values */
+static const struct pcm_sf {
+ u32 sf;
+ unsigned char cs_sf;
+} sf[] = {
+ {44100, 0x00},
+ {48000, 0x02},
+ {96000, 0x0a},
+ {192000,0x0e},
+ {22050, 0x04},
+ {24000, 0x06},
+ {32000, 0x03},
+ {88200, 0x08},
+ {768000,0x09},
+ {176400,0x0c},
+};
+
+static const struct {
+ int rate;
+ unsigned short n1, n2;
+} n_table[] = {
+ {32000, 3072, 4096},
+ {44100, 4704, 6272},
+ {88200, 4704*2, 6272*2},
+ {176400,4704*4, 6272*4},
+ {48000, 5120, 6144},
+ {96000, 5120*2, 6144*2},
+ {192000,5120*4, 6144*4},
+};
+#endif
+
+static inline void hdmi_writeb(struct de2_hdmi_priv *priv, u32 addr, u8 data)
+{
+ writeb_relaxed(data, priv->mmio + addr);
+}
+
+static inline void hdmi_writel(struct de2_hdmi_priv *priv, u32 addr, u32 data)
+{
+ writel_relaxed(data, priv->mmio + addr);
+}
+
+static inline u8 hdmi_readb(struct de2_hdmi_priv *priv, u32 addr)
+{
+ return readb_relaxed(priv->mmio + addr);
+}
+
+static inline u32 hdmi_readl(struct de2_hdmi_priv *priv, u32 addr)
+{
+ return readl_relaxed(priv->mmio + addr);
+}
+
+static void hdmi_lock(struct de2_hdmi_priv *priv)
+{
+ hdmi_writeb(priv, 0x10010, 0x45);
+ hdmi_writeb(priv, 0x10011, 0x45);
+ hdmi_writeb(priv, 0x10012, 0x52);
+ hdmi_writeb(priv, 0x10013, 0x54);
+}
+static void hdmi_unlock(struct de2_hdmi_priv *priv)
+{
+ hdmi_writeb(priv, 0x10010, 0x52);
+ hdmi_writeb(priv, 0x10011, 0x54);
+ hdmi_writeb(priv, 0x10012, 0x41);
+ hdmi_writeb(priv, 0x10013, 0x57);
+}
+
+static void hdmi_inner_init(struct de2_hdmi_priv *priv)
+{
+ u8 clkdis = priv->soc_type == SOC_H3 ?
+ ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE : 0xff;
+
+ hdmi_lock(priv);
+
+ /* software reset */
+ hdmi_writeb(priv, R_4002_HDMI_MC_SWRSTZ, 0x00);
+ udelay(2);
+
+ /* mask all interrupts */
+ hdmi_writeb(priv, R_01ff_HDMI_IH_MUTE, 0x00);
+ hdmi_writeb(priv, R_0807_HDMI_VP_MASK, 0xff);
+ hdmi_writeb(priv, R_10d2_HDMI_FC_MASK0, 0xff);
+ hdmi_writeb(priv, R_10d6_HDMI_FC_MASK1, 0xff);
+ hdmi_writeb(priv, R_10da_HDMI_FC_MASK2, 0xff);
+ hdmi_writeb(priv, R_3102_HDMI_AUD_INT, 0xff);
+ hdmi_writeb(priv, R_3302_HDMI_AUD_SPDIFINT, 0xff);
+ hdmi_writeb(priv, R_3506_HDMI_GP_POL, 0xff);
+ hdmi_writeb(priv, R_5008_HDMI_A_APIINTMSK, 0xff);
+ hdmi_writeb(priv, R_7d02_HDMI_CEC_MASK, 0xff);
+ hdmi_writeb(priv, R_7e05_HDMI_I2CM_INT, 0xff);
+ hdmi_writeb(priv, R_7e06_HDMI_I2CM_CTLINT, 0xff);
+
+ hdmi_writeb(priv, R_1063_HDMI_FC_AUDSCONF, 0xf0);
+ hdmi_writeb(priv, R_10b3_HDMI_FC_DATAUTO0, 0x1e);
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1, 0x00);
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1,
+ HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE |
+ HDMI_A_HDCPCFG1_SWRESET);
+ hdmi_writeb(priv, R_5000_HDMI_A_HDCPCFG0, 0x00);
+ hdmi_writeb(priv, R_5009_HDMI_A_VIDPOLCFG,
+ HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, clkdis);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, 0x00);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, clkdis);
+ hdmi_writeb(priv, R_0100_HDMI_IH_FC_STAT0, 0xff);
+ hdmi_writeb(priv, R_0101_HDMI_IH_FC_STAT1, 0xff);
+ hdmi_writeb(priv, R_0102_HDMI_IH_FC_STAT2, 0xff);
+ hdmi_writeb(priv, R_0103_HDMI_IH_AS_STAT0, 0xff);
+ hdmi_writeb(priv, R_0105_HDMI_IH_I2CM_STAT0, 0xff);
+ hdmi_writeb(priv, R_0106_HDMI_IH_CEC_STAT0, 0xff);
+ hdmi_writeb(priv, R_0107_HDMI_IH_VP_STAT0, 0xff);
+}
+
+static void hdmi_phy_init_a83t(struct de2_hdmi_priv *priv)
+{
+ hdmi_inner_init(priv);
+
+ hdmi_writeb(priv, 0x10000, 0x01);
+ hdmi_writeb(priv, 0x10001, 0x00);
+ hdmi_writeb(priv, 0x10002, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
+ hdmi_writeb(priv, 0x10003, 0x00);
+ hdmi_writeb(priv, 0x10007, 0xa0);
+ hdmi_writeb(priv, R_4005_HDMI_MC_PHYRSTZ,
+ HDMI_MC_PHYRSTZ_DEASSERT);
+ udelay(1);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3006_HDMI_PHY_MASK0, 0xf0);
+ hdmi_writeb(priv, R_3027_HDMI_PHY_I2CM_INT_ADDR, 0xff);
+ hdmi_writeb(priv, R_3028_HDMI_PHY_I2CM_CTLINT_ADDR, 0xff);
+ hdmi_writeb(priv, R_0104_HDMI_IH_PHY_STAT0, 0xff);
+ hdmi_writeb(priv, R_0108_HDMI_IH_I2CMPHY_STAT0, 0xff);
+ hdmi_writeb(priv, R_4005_HDMI_MC_PHYRSTZ, 0x00);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_PDDQ |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+ hdmi_writeb(priv, R_3001_HDMI_PHY_TST0, HDMI_PHY_TST0_TSTCLR);
+ hdmi_writeb(priv, R_3020_HDMI_PHY_I2CM_SLAVE_ADDR,
+ HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2);
+ hdmi_writeb(priv, R_3001_HDMI_PHY_TST0, 0x00);
+}
+
+static void hdmi_phy_init_h3(struct de2_hdmi_priv *priv)
+{
+ int to_cnt;
+ u32 tmp;
+
+ hdmi_writel(priv, 0x10020, 0);
+ hdmi_writel(priv, 0x10020, 1 << 0);
+ udelay(5);
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (1 << 16));
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (1 << 1));
+ udelay(10);
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (1 << 2));
+ udelay(5);
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (1 << 3));
+ usleep_range(40, 50);
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (1 << 19));
+ usleep_range(100, 120);
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (1 << 18));
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (7 << 4));
+
+ to_cnt = 10;
+ while (1) {
+ if (hdmi_readl(priv, 0x10038) & 0x80)
+ break;
+ usleep_range(200, 250);
+ if (--to_cnt == 0) {
+ pr_warn("hdmi phy init timeout\n");
+ break;
+ }
+ }
+
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (0xf << 8));
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) | (1 << 7));
+
+ hdmi_writel(priv, 0x1002c, 0x39dc5040);
+ hdmi_writel(priv, 0x10030, 0x80084343);
+ msleep(10);
+ hdmi_writel(priv, 0x10034, 0x00000001);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) | 0x02000000);
+ msleep(100);
+ tmp = hdmi_readl(priv, 0x10038);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) | 0xc0000000);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ ((tmp >> 11) & 0x3f));
+ hdmi_writel(priv, 0x10020, 0x01ff0f7f);
+ hdmi_writel(priv, 0x10024, 0x80639000);
+ hdmi_writel(priv, 0x10028, 0x0f81c405);
+
+ hdmi_inner_init(priv);
+}
+
+void hdmi_io_init(struct de2_hdmi_priv *priv)
+{
+ if (priv->soc_type == SOC_H3)
+ hdmi_phy_init_h3(priv);
+ else
+ hdmi_phy_init_a83t(priv);
+}
+
+static int get_vid(u32 id)
+{
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(ptbl); i++) {
+ if (id == ptbl[i].para[0])
+ return i;
+ }
+
+ return -1;
+}
+
+static void hdmi_i2cm_write(struct de2_hdmi_priv *priv,
+ int addr, u8 valh, u8 vall)
+{
+ hdmi_writeb(priv, R_3021_HDMI_PHY_I2CM_ADDRESS_ADDR, addr);
+ hdmi_writeb(priv, R_3022_HDMI_PHY_I2CM_DATAO_1_ADDR, valh);
+ hdmi_writeb(priv, R_3023_HDMI_PHY_I2CM_DATAO_0_ADDR, vall);
+ hdmi_writeb(priv, R_3026_HDMI_PHY_I2CM_OPERATION_ADDR,
+ HDMI_PHY_I2CM_OPERATION_ADDR_WRITE);
+ usleep_range(2000, 2500);
+}
+
+static int hdmi_phy_set_a83t(struct de2_hdmi_priv *priv, int i)
+{
+ switch (ptbl[i].para[1]) {
+ case 1:
+ hdmi_i2cm_write(priv, 0x06, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x0f);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x02);
+ hdmi_i2cm_write(priv, 0x0e, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x2b);
+ break;
+ case 2: /* 1080P @ 60 & 50 */
+ hdmi_i2cm_write(priv, 0x06, 0x04, 0xa0);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x0a);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x02);
+ hdmi_i2cm_write(priv, 0x0e, 0x00, 0x21);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x29);
+ break;
+ case 4: /* 720P @ 50 & 60, 1080I, 1080P */
+ hdmi_i2cm_write(priv, 0x06, 0x05, 0x40);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x05);
+ hdmi_i2cm_write(priv, 0x10, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x07);
+ hdmi_i2cm_write(priv, 0x0e, 0x02, 0xb5);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x09);
+ break;
+ case 11: /* 480P/576P */
+ hdmi_i2cm_write(priv, 0x06, 0x01,
+ ptbl[i].para[2] ? 0xe3 : 0xe0);
+ hdmi_i2cm_write(priv, 0x15, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x10, 0x08, 0xda);
+ hdmi_i2cm_write(priv, 0x19, 0x00, 0x07);
+ hdmi_i2cm_write(priv, 0x0e, 0x03, 0x18);
+ hdmi_i2cm_write(priv, 0x09, 0x80, 0x09);
+ break;
+ default:
+ return -1;
+ }
+ hdmi_i2cm_write(priv, 0x1e, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x13, 0x00, 0x00);
+ hdmi_i2cm_write(priv, 0x17, 0x00, 0x00);
+ hdmi_writeb(priv, R_3000_HDMI_PHY_CONF0,
+ HDMI_PHY_CONF0_GEN2_TXPWRON |
+ HDMI_PHY_CONF0_GEN2_ENHPDRXSENSE |
+ HDMI_PHY_CONF0_SELDATAENPOL);
+
+ return 0;
+}
+
+static int hdmi_phy_set_h3(struct de2_hdmi_priv *priv, int i)
+{
+ u32 tmp;
+
+ hdmi_writel(priv, 0x10020, hdmi_readl(priv, 0x10020) & ~0xf000);
+ switch (ptbl[i].para[1]) {
+ case 1:
+ hdmi_writel(priv, 0x1002c, 0x31dc5fc0); /* or 0x30dc5fc0 ? */
+ hdmi_writel(priv, 0x10030, 0x800863c0);
+ msleep(10);
+ hdmi_writel(priv, 0x10034, 0x00000001);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0x02000000);
+ msleep(200);
+ tmp = (hdmi_readl(priv, 0x10038) >> 11) & 0x3f;
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0xc0000000);
+ if (tmp < 0x3d)
+ tmp += 2;
+ else
+ tmp = 0x3f;
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) | tmp);
+ msleep(100);
+ hdmi_writel(priv, 0x10020, 0x01ffff7f);
+ hdmi_writel(priv, 0x10024, 0x8063b000);
+ hdmi_writel(priv, 0x10028, 0x0f8246b5);
+ break;
+ case 2: /* 1080P @ 60 & 50 */
+ hdmi_writel(priv, 0x1002c, 0x39dc5040);
+ hdmi_writel(priv, 0x10030, 0x80084381);
+ msleep(10);
+ hdmi_writel(priv, 0x10034, 0x00000001);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, 0x10038) >> 11) & 0x3f;
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0xc0000000);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) | tmp);
+ hdmi_writel(priv, 0x10020, 0x01ffff7f);
+ hdmi_writel(priv, 0x10024, 0x8063a800);
+ hdmi_writel(priv, 0x10028, 0x0f81c485);
+ break;
+ case 4: /* 720P @ 50 & 60, 1080I, 1080P */
+ hdmi_writel(priv, 0x1002c, 0x39dc5040);
+ hdmi_writel(priv, 0x10030, 0x80084343);
+ msleep(10);
+ hdmi_writel(priv, 0x10034, 0x00000001);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, 0x10038) >> 11) & 0x3f;
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0xc0000000);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) | tmp);
+ hdmi_writel(priv, 0x10020, 0x01ffff7f);
+ hdmi_writel(priv, 0x10024, 0x8063b000);
+ hdmi_writel(priv, 0x10028, 0x0f81c405);
+ break;
+ case 11: /* 480P/576P */
+ hdmi_writel(priv, 0x1002c, 0x39dc5040);
+ hdmi_writel(priv, 0x10030, 0x8008430a);
+ msleep(10);
+ hdmi_writel(priv, 0x10034, 0x00000001);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0x02000000);
+ msleep(100);
+ tmp = (hdmi_readl(priv, 0x10038) >> 11) & 0x3f;
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) |
+ 0xc0000000);
+ hdmi_writel(priv, 0x1002c, hdmi_readl(priv, 0x1002c) | tmp);
+ hdmi_writel(priv, 0x10020, 0x01ffff7f);
+ hdmi_writel(priv, 0x10024, 0x8063b000);
+ hdmi_writel(priv, 0x10028, 0x0f81c405);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+void hdmi_io_video_on(struct de2_hdmi_priv *priv)
+{
+ if (priv->soc_type == SOC_H3)
+ hdmi_writel(priv, 0x10020,
+ hdmi_readl(priv, 0x10020) | (0x0f << 12));
+#if 0
+ else
+ hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE, 0);
+#endif
+}
+
+void hdmi_io_video_off(struct de2_hdmi_priv *priv)
+{
+ if (priv->soc_type == SOC_H3)
+ hdmi_writel(priv, 0x10020,
+ hdmi_readl(priv, 0x10020) & ~(0x0f << 12));
+#if 0
+ else
+ hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE,
+ HDMI_FC_DBGFORCE_FORCEVIDEO);
+#endif
+}
+
+#if IS_ENABLED(CONFIG_SND_SOC_SUNXI_HDMI)
+/* start audio */
+int hdmi_io_audio(struct de2_hdmi_priv *priv,
+ int sample_rate, int sample_bit)
+{
+ int id = get_vid(priv->cea_mode); /* ptbl index */
+ unsigned int i, n;
+
+ if (id < 0)
+ return id;
+
+ /* (2 channels) */
+ hdmi_writeb(priv, R_1063_HDMI_FC_AUDSCONF, 0xf0);
+
+ hdmi_writeb(priv, R_1065_HDMI_FC_AUDSV, ~0x11);
+
+ hdmi_writeb(priv, R_1066_HDMI_FC_AUDSU, 0x00);
+ hdmi_writeb(priv, R_1067_HDMI_FC_AUDSCHNLS0,
+ HDMI_FC_AUDSCHNLS0_CGMSA);
+ hdmi_writeb(priv, R_1068_HDMI_FC_AUDSCHNLS1, 0x00);
+ hdmi_writeb(priv, R_1069_HDMI_FC_AUDSCHNLS2, 0x01);
+ hdmi_writeb(priv, R_106a_HDMI_FC_AUDSCHNLS3,
+ HDMI_FC_AUDSCHNLS3_OIEC_CH0(2) |
+ HDMI_FC_AUDSCHNLS3_OIEC_CH1(4));
+ hdmi_writeb(priv, R_106b_HDMI_FC_AUDSCHNLS4,
+ HDMI_FC_AUDSCHNLS4_OIEC_CH2(6) |
+ HDMI_FC_AUDSCHNLS4_OIEC_CH3(8));
+ hdmi_writeb(priv, R_106c_HDMI_FC_AUDSCHNLS5,
+ HDMI_FC_AUDSCHNLS5_OIEC_CH0(1) |
+ HDMI_FC_AUDSCHNLS5_OIEC_CH1(3));
+ hdmi_writeb(priv, R_106d_HDMI_FC_AUDSCHNLS6,
+ HDMI_FC_AUDSCHNLS6_OIEC_CH2(5) |
+ HDMI_FC_AUDSCHNLS6_OIEC_CH3(7));
+ hdmi_writeb(priv, R_106e_HDMI_FC_AUDSCHNLS7, 0x01);
+ for (i = 0; i < ARRAY_SIZE(sf); i++) {
+ if (sample_rate == sf[i].sf) {
+ hdmi_writeb(priv, R_106e_HDMI_FC_AUDSCHNLS7,
+ sf[i].cs_sf);
+ break;
+ }
+ }
+ hdmi_writeb(priv, R_106f_HDMI_FC_AUDSCHNLS8,
+ (sample_bit == 16) ? HDMI_FC_AUDSCHNLS8_WORDLENGTH(2) :
+ (sample_bit == 24 ? HDMI_FC_AUDSCHNLS8_WORDLENGTH(11) :
+ HDMI_FC_AUDSCHNLS8_WORDLENGTH(0)));
+
+ hdmi_writeb(priv, R_3101_HDMI_AUD_CONF1, sample_bit);
+
+ n = 6272;
+ for (i = 0; i < ARRAY_SIZE(n_table); i++) {
+ if (sample_rate == n_table[i].rate) {
+ if (ptbl[id].para[1] == 1)
+ n = n_table[i].n1;
+ else
+ n = n_table[i].n2;
+ break;
+ }
+ }
+
+ hdmi_writeb(priv, R_3200_HDMI_AUD_N1, n);
+ hdmi_writeb(priv, R_3201_HDMI_AUD_N2, n >> 8);
+ hdmi_writeb(priv, R_3202_HDMI_AUD_N3, n >> 16);
+ hdmi_writeb(priv, R_3205_HDMI_AUD_CTS3, 0x00);
+ hdmi_writeb(priv, R_3206_HDMI_AUD_INPUTCLKFS, 0x04);
+ hdmi_writeb(priv, R_1063_HDMI_FC_AUDSCONF, 0x00); /* layout0 */
+ hdmi_writeb(priv, R_1025_HDMI_FC_AUDICONF0, 2 * 16); /* 2 channels */
+ hdmi_writeb(priv, R_1026_HDMI_FC_AUDICONF1, 0x00);
+ hdmi_writeb(priv, R_1027_HDMI_FC_AUDICONF2, 0x00);
+ hdmi_writeb(priv, R_1028_HDMI_FC_AUDICONF3, 0x00);
+
+ hdmi_writeb(priv, R_3103_HDMI_AUD_CONF2, 0x00); /* PCM only */
+
+ hdmi_writeb(priv, R_3100_HDMI_AUD_CONF0, 0x00);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS,
+ HDMI_MC_CLKDIS_AUDCLK_DISABLE);
+ hdmi_writeb(priv, R_4002_HDMI_MC_SWRSTZ, 0xf7);
+ usleep_range(100, 120);
+ hdmi_writeb(priv, R_3100_HDMI_AUD_CONF0, 0xaf);
+ usleep_range(100, 120);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, 0x00); /* enable all clocks */
+
+ return 0;
+}
+#endif
+
+
+/* initialize */
+int hdmi_io_video_mode(struct de2_hdmi_priv *priv)
+{
+ int i = get_vid(priv->cea_mode); /* ptbl index */
+ u8 avi_d2; /* AVI InfoFrame Data Byte 2 */
+
+ if (i < 0)
+ return i;
+
+ /* colorimetry and aspect ratio */
+ switch (priv->cea_mode) {
+ case 2: /* 480P */
+ case 6: /* 1440x480I */
+ case 17: /* 576P */
+ case 21: /* 1440x576I */
+ avi_d2 = (HDMI_COLORIMETRY_ITU_601 << 6) |
+ (HDMI_PICTURE_ASPECT_4_3 << 4) | 0x08;
+ break;
+ default:
+ avi_d2 = (HDMI_COLORIMETRY_ITU_709 << 6) |
+ (HDMI_PICTURE_ASPECT_16_9 << 4) | 0x08;
+ break;
+ }
+ if (priv->soc_type == SOC_H3) {
+ if (hdmi_phy_set_h3(priv, i) != 0)
+ return -1;
+ hdmi_inner_init(priv);
+ } else {
+ hdmi_io_init(priv);
+ }
+
+ hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE,
+ HDMI_FC_DBGFORCE_FORCEVIDEO);
+ hdmi_writeb(priv, R_1219_HDMI_FC_DBGTMDS0, 0x00);
+ hdmi_writeb(priv, R_1000_HDMI_FC_INVIDCONF, ptbl[i].para[3] |
+ HDMI_FC_INVIDCONF_DE_IN_POLARITY_ACTIVE_HIGH);
+ hdmi_writeb(priv, 0x10001, ptbl[i].para[3] < 0x60 ? 0x03 : 0x00);
+ hdmi_writeb(priv, R_1002_HDMI_FC_INHACTV1, ptbl[i].para[4]);
+ hdmi_writeb(priv, R_100d_HDMI_FC_VSYNCINWIDTH, ptbl[i].para[5]);
+ hdmi_writeb(priv, R_1006_HDMI_FC_INVACTV1, ptbl[i].para[6]);
+ hdmi_writeb(priv, R_1004_HDMI_FC_INHBLANK1, ptbl[i].para[7]);
+ hdmi_writeb(priv, R_100c_HDMI_FC_VSYNCINDELAY, ptbl[i].para[8]);
+ hdmi_writeb(priv, R_1009_HDMI_FC_HSYNCINDELAY1, ptbl[i].para[9]);
+ hdmi_writeb(priv, R_100b_HDMI_FC_HSYNCINWIDTH1, ptbl[i].para[10]);
+ hdmi_writeb(priv, R_1001_HDMI_FC_INHACTV0, ptbl[i].para[11]);
+ hdmi_writeb(priv, R_1003_HDMI_FC_INHBLANK0, ptbl[i].para[12]);
+ hdmi_writeb(priv, R_1008_HDMI_FC_HSYNCINDELAY0, ptbl[i].para[13]);
+ hdmi_writeb(priv, R_100a_HDMI_FC_HSYNCINWIDTH1, ptbl[i].para[14]);
+ hdmi_writeb(priv, R_1005_HDMI_FC_INVACTV0, ptbl[i].para[15]);
+ hdmi_writeb(priv, R_1007_HDMI_FC_INVBLANK, ptbl[i].para[16]);
+ hdmi_writeb(priv, R_1011_HDMI_FC_CTRLDUR, 12);
+ hdmi_writeb(priv, R_1012_HDMI_FC_EXCTRLDUR, 32);
+ hdmi_writeb(priv, R_1013_HDMI_FC_EXCTRLSPAC, 1);
+ hdmi_writeb(priv, R_1014_HDMI_FC_CH0PREAM, 0x0b);
+ hdmi_writeb(priv, R_1015_HDMI_FC_CH1PREAM, 0x16);
+ hdmi_writeb(priv, R_1016_HDMI_FC_CH2PREAM, 0x21);
+ hdmi_writeb(priv, R_10e0_HDMI_FC_PRCONF, ptbl[i].para[2] ? 0x21 : 0x10);
+ hdmi_writeb(priv, R_0801_HDMI_VP_PR_CD, ptbl[i].para[2] ? 0x41 : 0x40);
+ hdmi_writeb(priv, R_0802_HDMI_VP_STUFF, 0x07);
+ hdmi_writeb(priv, R_0803_HDMI_VP_REMAP, 0x00);
+ hdmi_writeb(priv, R_0804_HDMI_VP_CONF, 0x47);
+ hdmi_writeb(priv, R_0200_HDMI_TX_INVID0, 0x01);
+ hdmi_writeb(priv, R_0201_HDMI_TX_INSTUFFING, 0x07);
+ hdmi_writeb(priv, R_0202_HDMI_TX_GYDATA0, 0x00);
+ hdmi_writeb(priv, R_0203_HDMI_TX_GYDATA1, 0x00);
+ hdmi_writeb(priv, R_0204_HDMI_TX_RCRDATA0, 0x00);
+ hdmi_writeb(priv, R_0205_HDMI_TX_RCRDATA1, 0x00);
+ hdmi_writeb(priv, R_0206_HDMI_TX_BCBDATA0, 0x00);
+ hdmi_writeb(priv, R_0207_HDMI_TX_BCBDATA1, 0x00);
+
+ if (priv->codec.eld) { /* if audio/HDMI */
+ hdmi_writeb(priv, R_10b3_HDMI_FC_DATAUTO0, 0x08);
+ hdmi_writeb(priv, R_1031_HDMI_FC_VSDIEEEID2, 0x00);
+ hdmi_writeb(priv, R_1030_HDMI_FC_VSDIEEEID1,
+ HDMI_IEEE_OUI >> 8);
+ hdmi_writeb(priv, R_1029_HDMI_FC_VSDIEEEID0,
+ HDMI_IEEE_OUI & 0xff);
+ hdmi_writeb(priv, R_1032_HDMI_FC_VSDPAYLOAD0, 0x00);
+ hdmi_writeb(priv, R_1033_HDMI_FC_VSDPAYLOAD1, 0x00);
+ hdmi_writeb(priv, R_1034_HDMI_FC_VSDPAYLOAD2, 0x00);
+ hdmi_writeb(priv, R_10b4_HDMI_FC_DATAUTO1, 0x01);
+ hdmi_writeb(priv, R_10b5_HDMI_FC_DATAUTO2, 0x11);
+ hdmi_writeb(priv, R_1018_HDMI_FC_GCP, 0x00);
+ hdmi_writeb(priv, R_1104_HDMI_FC_GMD_HB, 0x00);
+ hdmi_writeb(priv, R_1103_HDMI_FC_GMD_CONF, 0x11);
+
+ hdmi_lock(priv);
+ hdmi_writeb(priv, R_1000_HDMI_FC_INVIDCONF,
+ hdmi_readb(priv, R_1000_HDMI_FC_INVIDCONF) | 0x08);
+ hdmi_unlock(priv);
+
+ /* AVI */
+ hdmi_writeb(priv, R_1019_HDMI_FC_AVICONF0,
+ HDMI_FC_AVICONF0_SCAN_INFO_UNDERSCAN);
+ hdmi_writeb(priv, R_101a_HDMI_FC_AVICONF1, avi_d2);
+ hdmi_writeb(priv, R_101b_HDMI_FC_AVICONF2, 0x08);
+ hdmi_writeb(priv, R_101c_HDMI_FC_AVIVID, priv->cea_mode);
+ }
+
+ hdmi_writeb(priv, R_4004_HDMI_MC_FLOWCTRL, 0x00);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS, 0x00); /* enable all clocks */
+
+ if (priv->soc_type != SOC_H3) {
+ if (hdmi_phy_set_a83t(priv, i) != 0)
+ return -1;
+ }
+
+ hdmi_writeb(priv, R_1200_HDMI_FC_DBGFORCE, 0x00);
+
+ return 0;
+}
+
+/* get a block of EDID */
+int hdmi_io_ddc_read(struct de2_hdmi_priv *priv,
+ char pointer, char off,
+ int nbyte, char *pbuf)
+{
+ unsigned to_cnt;
+ u8 reg;
+ int ret = 0;
+
+ hdmi_lock(priv);
+ hdmi_writeb(priv, R_7e09_HDMI_I2CM_SOFTRSTZ, 0x00);
+ to_cnt = 50;
+ while (!(hdmi_readb(priv, R_7e09_HDMI_I2CM_SOFTRSTZ) & 0x01)) {
+ udelay(10);
+ if (--to_cnt == 0) { /* wait for 500us for timeout */
+ pr_warn("hdmi ddc reset timeout\n");
+ break;
+ }
+ }
+
+ hdmi_writeb(priv, R_7e07_HDMI_I2CM_DIV, 0x05);
+ hdmi_writeb(priv, R_7e05_HDMI_I2CM_INT, 0x08);
+ hdmi_writeb(priv, R_7e0c_HDMI_I2CM_SS_SCL_HCNT_0_ADDR, 0xd8);
+ hdmi_writeb(priv, R_7e0e_HDMI_I2CM_SS_SCL_LCNT_0_ADDR, 0xfe);
+
+ while (nbyte > 0) {
+ hdmi_writeb(priv, R_7e00_HDMI_I2CM_SLAVE, 0xa0 >> 1);
+ hdmi_writeb(priv, R_7e01_HDMI_I2CM_ADDRESS, off);
+ hdmi_writeb(priv, R_7e08_HDMI_I2CM_SEGADDR, 0x60 >> 1);
+ hdmi_writeb(priv, R_7e0a_HDMI_I2CM_SEGPTR, pointer);
+ hdmi_writeb(priv, R_7e04_HDMI_I2CM_OPERATION,
+ HDMI_I2CM_OPERATION_DDC_READ);
+
+ to_cnt = 200; /* timeout 100ms */
+ while (1) {
+ reg = hdmi_readb(priv, R_0105_HDMI_IH_I2CM_STAT0);
+ hdmi_writeb(priv, R_0105_HDMI_IH_I2CM_STAT0, reg);
+ if (reg & 0x02) {
+ *pbuf++ = hdmi_readb(priv, R_7e03_HDMI_I2CM_DATAI);
+ break;
+ }
+ if (reg & 0x01) {
+ pr_warn("hdmi ddc read error\n");
+ ret = -1;
+ break;
+ }
+ if (--to_cnt == 0) {
+ if (!ret) {
+ pr_warn("hdmi ddc read timeout\n");
+ ret = -1;
+ }
+ break;
+ }
+ usleep_range(800, 1000);
+ }
+ if (ret)
+ break;
+ nbyte--;
+ off++;
+ }
+ hdmi_unlock(priv);
+
+ return ret;
+}
+
+int hdmi_io_get_hpd(struct de2_hdmi_priv *priv)
+{
+ int ret;
+
+ hdmi_lock(priv);
+
+ if (priv->soc_type == SOC_H3)
+ ret = hdmi_readl(priv, 0x10038) & 0x80000;
+ else
+ ret = hdmi_readb(priv, R_3005_HDMI_PHY_INT0) & 0x02;
+
+ hdmi_unlock(priv);
+
+ return ret != 0;
+}
+
+void hdmi_io_reset(struct de2_hdmi_priv *priv)
+{
+ hdmi_writeb(priv, R_5001_HDMI_A_HDCPCFG1,
+ HDMI_A_HDCPCFG1_PH2UPSHFTENC);
+ hdmi_writeb(priv, R_4001_HDMI_MC_CLKDIS,
+ HDMI_MC_CLKDIS_HDCPCLK_DISABLE);
+}
+
+int hdmi_io_mode_valid(int cea_mode)
+{
+ return get_vid(cea_mode);
+}
diff --git a/drivers/gpu/drm/sunxi/de2_hdmi_io.h b/drivers/gpu/drm/sunxi/de2_hdmi_io.h
new file mode 100644
index 0000000..2577f0b
--- /dev/null
+++ b/drivers/gpu/drm/sunxi/de2_hdmi_io.h
@@ -0,0 +1,25 @@
+#ifndef __DE2_HDMI_IO_H__
+#define __DE2_HDMI_IO_H__
+/*
+ * Copyright (C) 2016 Jean-Fran??ois Moine
+ *
+ * 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.
+ */
+
+void hdmi_io_video_on(struct de2_hdmi_priv *priv);
+void hdmi_io_video_off(struct de2_hdmi_priv *priv);
+int hdmi_io_audio(struct de2_hdmi_priv *priv,
+ int sample_rate, int sample_bit);
+int hdmi_io_video_mode(struct de2_hdmi_priv *priv);
+int hdmi_io_ddc_read(struct de2_hdmi_priv *priv,
+ char pointer, char offset,
+ int nbyte, char *pbuf);
+int hdmi_io_get_hpd(struct de2_hdmi_priv *priv);
+void hdmi_io_init(struct de2_hdmi_priv *priv);
+void hdmi_io_reset(struct de2_hdmi_priv *priv);
+int hdmi_io_mode_valid(int cea_mode);
+
+#endif /* __DE2_HDMI_IO_H__ */
--
2.10.1
^ permalink raw reply related
* [PATCH] kernel: irq: fix build failure
From: Thomas Gleixner @ 2016-10-21 8:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161021120020.52b3fa35@canb.auug.org.au>
On Fri, 21 Oct 2016, Stephen Rothwell wrote:
> On Thu, 20 Oct 2016 14:55:45 +0200 (CEST) Thomas Gleixner <tglx@linutronix.de> wrote:
> > I know. This is under discussion with the driver folks as we are not going
> > to blindly export stuff just because someone slapped a irq_set_parent()
> > into the code w/o knowing why.
>
> Do we have any idea if a resolution is close. This was first reported
> in linux-next in September 14/15. :-(
Grr. Yes. As much as I hate it, I'll go and export it for now. Should be
able to get it into rc2.
Thanks,
tglx
^ permalink raw reply
* [PATCH v8 3/3] ARM: dts: imx6qdl-icore: Add FEC support
From: Jagan Teki @ 2016-10-21 8:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477037153-20484-1-git-send-email-jteki@openedev.com>
From: Jagan Teki <jagan@amarulasolutions.com>
Add FEC support for Engicam i.CoreM6 dql modules.
Observed similar 'eth0: link is not ready' issue which was
discussed in [1] due rmii mode with external ref_clk, so added
clock node along with the properties mentioned by Shawn in [2]
FEC link log:
------------
$ ifconfig eth0 up
[ 27.905187] SMSC LAN8710/LAN8720 2188000.ethernet:00: attached PHY driver
[SMSC LAN8710/LAN8720] (mii_bus:phy_addr=2188000.ethernet:00, irq=-1)
[ 27.918982] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready
[1] https://patchwork.kernel.org/patch/3491061/
[2] https://patchwork.kernel.org/patch/3490511/
Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Matteo Lisi <matteo.lisi@engicam.com>
Cc: Michael Trimarchi <michael@amarulasolutions.com>
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
Changes for v8:
- s/117/IMX6QDL_CLK_ENET in &fec/clocks
- Add rmii_clk fixed-clock node instead of adding container node like clock
Changes for v7:
- none
Changes for v6:
- none
Changes for v5:
- new patch
arch/arm/boot/dts/imx6qdl-icore.dtsi | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/arch/arm/boot/dts/imx6qdl-icore.dtsi b/arch/arm/boot/dts/imx6qdl-icore.dtsi
index 0a1b60a..023839a 100644
--- a/arch/arm/boot/dts/imx6qdl-icore.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-icore.dtsi
@@ -74,6 +74,12 @@
regulator-boot-on;
regulator-always-on;
};
+
+ rmii_clk: clock-rmii-clk {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <25000000>; /* 25MHz for example */
+ };
};
&can1 {
@@ -93,6 +99,15 @@
assigned-clock-parents = <&clks IMX6QDL_CLK_OSC>;
};
+&fec {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_enet>;
+ phy-reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
+ clocks = <&clks IMX6QDL_CLK_ENET>, <&clks IMX6QDL_CLK_ENET>, <&rmii_clk>;
+ phy-mode = "rmii";
+ status = "okay";
+};
+
&gpmi {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpmi_nand>;
@@ -150,6 +165,22 @@
};
&iomuxc {
+ pinctrl_enet: enetgrp {
+ fsl,pins = <
+ MX6QDL_PAD_ENET_CRS_DV__ENET_RX_EN 0x1b0b0
+ MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x1b0b1
+ MX6QDL_PAD_ENET_TX_EN__ENET_TX_EN 0x1b0b0
+ MX6QDL_PAD_ENET_RXD1__ENET_RX_DATA1 0x1b0b0
+ MX6QDL_PAD_ENET_RXD0__ENET_RX_DATA0 0x1b0b0
+ MX6QDL_PAD_ENET_TXD1__ENET_TX_DATA1 0x1b0b0
+ MX6QDL_PAD_ENET_TXD0__ENET_TX_DATA0 0x1b0b0
+ MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0
+ MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0
+ MX6QDL_PAD_ENET_REF_CLK__GPIO1_IO23 0x1b0b0
+ MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x1b0b0
+ >;
+ };
+
pinctrl_flexcan1: flexcan1grp {
fsl,pins = <
MX6QDL_PAD_KEY_ROW2__FLEXCAN1_RX 0x1b020
--
2.7.4
^ permalink raw reply related
* [PATCH v8 2/3] ARM: dts: imx6q: Add Engicam i.CoreM6 DualLite/Solo initial support
From: Jagan Teki @ 2016-10-21 8:05 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1477037153-20484-1-git-send-email-jteki@openedev.com>
From: Jagan Teki <jagan@amarulasolutions.com>
i.CoreM6 DualLite/Solo modules are system on module solutions manufactured
by Engicam with following characteristics:
CPU NXP i.MX6 DL, 800MHz
RAM 1GB, 32, 64 bit, DDR3-800/1066
NAND SLC,512MB
Power supply Single 5V
MAX LCD RES FULLHD
and more info at
http://www.engicam.com/en/products/embedded/som/sodimm/i-core-m6s-dl-d-q
Cc: Sascha Hauer <kernel@pengutronix.de>
Cc: Fabio Estevam <fabio.estevam@nxp.com>
Cc: Shawn Guo <shawnguo@kernel.org>
Cc: Matteo Lisi <matteo.lisi@engicam.com>
Cc: Michael Trimarchi <michael@amarulasolutions.com>
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
Changes for v8:
- none
Changes for v7:
- none
Changes for v6:
- none
Changes for v5:
- none
Changes for v4:
- new patch
Changes for v3:
- Use compatible as engicam,imx6-icore instead of fsl,imx6-icore
Changes for v2:
- s/oaky/okay/g
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/imx6dl-icore.dts | 59 ++++++++++++++++++++++++++++++++++++++
2 files changed, 60 insertions(+)
create mode 100644 arch/arm/boot/dts/imx6dl-icore.dts
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 89a3eea..8f8b14b 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -340,6 +340,7 @@ dtb-$(CONFIG_SOC_IMX6Q) += \
imx6dl-gw552x.dtb \
imx6dl-gw553x.dtb \
imx6dl-hummingboard.dtb \
+ imx6dl-icore.dtb \
imx6dl-nit6xlite.dtb \
imx6dl-nitrogen6x.dtb \
imx6dl-phytec-pbab01.dtb \
diff --git a/arch/arm/boot/dts/imx6dl-icore.dts b/arch/arm/boot/dts/imx6dl-icore.dts
new file mode 100644
index 0000000..aec332c
--- /dev/null
+++ b/arch/arm/boot/dts/imx6dl-icore.dts
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 Amarula Solutions B.V.
+ * Copyright (C) 2016 Engicam S.r.l.
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This file is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Or, alternatively
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+
+#include "imx6dl.dtsi"
+#include "imx6qdl-icore.dtsi"
+
+/ {
+ model = "Engicam i.CoreM6 DualLite/Solo Starter Kit";
+ compatible = "engicam,imx6-icore", "fsl,imx6dl";
+};
+
+&can1 {
+ status = "okay";
+};
+
+&can2 {
+ status = "okay";
+};
--
2.7.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox