Lepotato. Added NEXT, added patches from C2 NEXT, au, config update, removed deprecated patches, ...

This commit is contained in:
Igor Pecovnik 2017-09-18 12:06:30 +02:00
parent 38a7bcf8c2
commit 2c59bb9934
66 changed files with 50058 additions and 8 deletions

View file

@ -0,0 +1,51 @@
From 8ede929172f118c43ef1664fe66b56dadff3310f Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Tue, 28 Feb 2017 15:22:54 +0100
Subject: [PATCH 01/79] Add HDMI support for Odroid-C2
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
.../arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts | 23 ++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
index 54a9c6a..0066b1a 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
@@ -135,6 +135,17 @@
compatible = "mmc-pwrseq-emmc";
reset-gpios = <&gpio BOOT_9 GPIO_ACTIVE_LOW>;
};
+
+ hdmi-connector {
+ compatible = "hdmi-connector";
+ type = "a";
+
+ port {
+ hdmi_connector_in: endpoint {
+ remote-endpoint = <&hdmi_tx_tmds_out>;
+ };
+ };
+ };
};
&scpi_clocks {
@@ -309,3 +320,15 @@
vmmc-supply = <&vcc3v3>;
vqmmc-supply = <&vcc1v8>;
};
+
+&hdmi_tx {
+ status = "okay";
+ pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>;
+ pinctrl-names = "default";
+};
+
+&hdmi_tx_tmds_port {
+ hdmi_tx_tmds_out: endpoint {
+ remote-endpoint = <&hdmi_connector_in>;
+ };
+};
--
1.9.1

View file

@ -0,0 +1,23 @@
From aa7bd7ebd01c4a7a1e7f1cdca431e5447313fefc Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Tue, 14 Feb 2017 19:18:04 +0100
Subject: [PATCH 02/79] drm/meson: select dw-hdmi i2s audio for meson hdmi
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/gpu/drm/meson/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig
index 3ce51d8..02d400b 100644
--- a/drivers/gpu/drm/meson/Kconfig
+++ b/drivers/gpu/drm/meson/Kconfig
@@ -13,3 +13,4 @@ config DRM_MESON_DW_HDMI
depends on DRM_MESON
default y if DRM_MESON
select DRM_DW_HDMI
+ select DRM_DW_HDMI_I2S_AUDIO
--
1.9.1

View file

@ -0,0 +1,79 @@
From 095d92c205f588fbeb207a124eb860ea57dd568a Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Mon, 27 Mar 2017 11:08:24 +0200
Subject: [PATCH 03/79] drm: bridge: dw-hdmi: Use AUTO CTS setup mode when
non-AHB audio
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 43 +++++++++++++++++++------------
1 file changed, 27 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 4e1f54a..ee788ca 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -420,8 +420,12 @@ static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
/* nshift factor = 0 */
hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
- hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
- HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
+ /* Use Auto CTS mode with CTS is unknown */
+ if (cts)
+ hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
+ HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
+ else
+ hdmi_writeb(hdmi, 0, HDMI_AUD_CTS3);
hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
@@ -491,24 +495,31 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
{
unsigned long ftdms = pixel_clk;
unsigned int n, cts;
+ u8 config3;
u64 tmp;
n = hdmi_compute_n(sample_rate, pixel_clk);
- /*
- * Compute the CTS value from the N value. Note that CTS and N
- * can be up to 20 bits in total, so we need 64-bit math. Also
- * note that our TDMS clock is not fully accurate; it is accurate
- * to kHz. This can introduce an unnecessary remainder in the
- * calculation below, so we don't try to warn about that.
- */
- tmp = (u64)ftdms * n;
- do_div(tmp, 128 * sample_rate);
- cts = tmp;
-
- dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n",
- __func__, sample_rate, ftdms / 1000000, (ftdms / 1000) % 1000,
- n, cts);
+ config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID);
+
+ if (config3 & HDMI_CONFIG3_AHBAUDDMA) {
+ /*
+ * Compute the CTS value from the N value. Note that CTS and N
+ * can be up to 20 bits in total, so we need 64-bit math. Also
+ * note that our TDMS clock is not fully accurate; it is
+ * accurate to kHz. This can introduce an unnecessary remainder
+ * in the calculation below, so we don't try to warn about that.
+ */
+ tmp = (u64)ftdms * n;
+ do_div(tmp, 128 * sample_rate);
+ cts = tmp;
+
+ dev_dbg(hdmi->dev, "%s: fs=%uHz ftdms=%lu.%03luMHz N=%d cts=%d\n",
+ __func__, sample_rate,
+ ftdms / 1000000, (ftdms / 1000) % 1000,
+ n, cts);
+ } else
+ cts = 0;
spin_lock_irq(&hdmi->audio_lock);
hdmi->audio_n = n;
--
1.9.1

View file

@ -0,0 +1,26 @@
From 4923e7dd3aa1d072bb63ee46bdfc59f82de89a9c Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Tue, 30 May 2017 16:45:06 +0200
Subject: [PATCH 04/79] dt-bindings: clock: gxbb-aoclk: Add CEC 32k clock
This patchadds the clock binding entry for the CEC 32K AO Clock.
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
include/dt-bindings/clock/gxbb-aoclkc.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/dt-bindings/clock/gxbb-aoclkc.h b/include/dt-bindings/clock/gxbb-aoclkc.h
index 3175148..9d15e22 100644
--- a/include/dt-bindings/clock/gxbb-aoclkc.h
+++ b/include/dt-bindings/clock/gxbb-aoclkc.h
@@ -62,5 +62,6 @@
#define CLKID_AO_UART1 3
#define CLKID_AO_UART2 4
#define CLKID_AO_IR_BLASTER 5
+#define CLKID_AO_CEC_32K 6
#endif
--
1.9.1

View file

@ -0,0 +1,376 @@
From 7e59aa215674f39cebb870b68bc0dc54c73cd407 Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Tue, 30 May 2017 16:45:28 +0200
Subject: [PATCH 05/79] clk: meson: gxbb-aoclk: Add CEC 32k clock
The CEC 32K AO Clock is a dual divider with dual counter to provide a more
precise 32768Hz clock for the CEC subsystem from the external xtal.
The AO clocks management registers are spread among the AO register space,
so this patch also adds management of these registers mappings then uses them
for the CEC 32K AO clock management.
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/clk/meson/Makefile | 2 +-
drivers/clk/meson/gxbb-aoclk-32k.c | 188 +++++++++++++++++++++++++++++++++++++
drivers/clk/meson/gxbb-aoclk.c | 59 ++++++++++--
drivers/clk/meson/gxbb-aoclk.h | 23 +++++
4 files changed, 265 insertions(+), 7 deletions(-)
create mode 100644 drivers/clk/meson/gxbb-aoclk-32k.c
create mode 100644 drivers/clk/meson/gxbb-aoclk.h
diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
index 83b6d9d..e315057 100644
--- a/drivers/clk/meson/Makefile
+++ b/drivers/clk/meson/Makefile
@@ -4,4 +4,4 @@
obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-cpu.o clk-mpll.o clk-audio-divider.o
obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o
-obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
+obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-32k.o
diff --git a/drivers/clk/meson/gxbb-aoclk-32k.c b/drivers/clk/meson/gxbb-aoclk-32k.c
new file mode 100644
index 0000000..3c06413
--- /dev/null
+++ b/drivers/clk/meson/gxbb-aoclk-32k.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2017 BayLibre, SAS.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/bitfield.h>
+#include <linux/regmap.h>
+#include "gxbb-aoclk.h"
+
+/*
+ * The AO Domain embeds a dual/divider to generate a more precise
+ * 32,768KHz clock for low-power suspend mode and CEC.
+ */
+
+#define AO_RTC_ALT_CLK_CNTL0 0x0
+#define AO_RTC_ALT_CLK_CNTL1 0x4
+#define AO_CRT_CLK_CNTL1 0x0
+#define AO_RTI_PWR_CNTL_REG0 0x4
+
+#define CLK_CNTL0_N1_MASK GENMASK(11, 0)
+#define CLK_CNTL0_N2_MASK GENMASK(23, 12)
+#define CLK_CNTL0_DUALDIV_EN BIT(28)
+#define CLK_CNTL0_OUT_GATE_EN BIT(30)
+#define CLK_CNTL0_IN_GATE_EN BIT(31)
+
+#define CLK_CNTL1_M1_MASK GENMASK(11, 0)
+#define CLK_CNTL1_M2_MASK GENMASK(23, 12)
+#define CLK_CNTL1_BYPASS_EN BIT(24)
+#define CLK_CNTL1_SELECT_OSC BIT(27)
+
+#define PWR_CNTL_ALT_32K_SEL GENMASK(13, 10)
+
+struct cec_32k_freq_table {
+ unsigned long parent_rate;
+ unsigned long target_rate;
+ bool dualdiv;
+ unsigned int n1;
+ unsigned int n2;
+ unsigned int m1;
+ unsigned int m2;
+};
+
+static const struct cec_32k_freq_table aoclk_cec_32k_table[] = {
+ [0] = {
+ .parent_rate = 24000000,
+ .target_rate = 32768,
+ .dualdiv = true,
+ .n1 = 733,
+ .n2 = 732,
+ .m1 = 8,
+ .m2 = 11,
+ },
+};
+
+/*
+ * If CLK_CNTL0_DUALDIV_EN == 0
+ * - will use N1 divider only
+ * If CLK_CNTL0_DUALDIV_EN == 1
+ * - hold M1 cycles of N1 divider then changes to N2
+ * - hold M2 cycles of N2 divider then changes to N1
+ * Then we can get more accurate division.
+ */
+static unsigned long aoclk_cec_32k_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw);
+ u32 reg0, reg1;
+
+ reg0 = readl_relaxed(cec_32k->rtc_base + AO_RTC_ALT_CLK_CNTL0);
+ reg1 = readl_relaxed(cec_32k->rtc_base + AO_RTC_ALT_CLK_CNTL1);
+
+ if (reg1 & CLK_CNTL1_BYPASS_EN)
+ return parent_rate;
+
+ if (reg0 & CLK_CNTL0_DUALDIV_EN) {
+ unsigned long n1, n2, m1, m2, f1, f2, p1, p2;
+
+ n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1;
+ n2 = FIELD_GET(CLK_CNTL0_N2_MASK, reg0) + 1;
+
+ m1 = FIELD_GET(CLK_CNTL1_M1_MASK, reg1) + 1;
+ m2 = FIELD_GET(CLK_CNTL1_M2_MASK, reg1) + 1;
+
+ f1 = DIV_ROUND_CLOSEST(parent_rate, n1);
+ f2 = DIV_ROUND_CLOSEST(parent_rate, n2);
+
+ p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2));
+ p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2));
+
+ return DIV_ROUND_UP(100000000, p1 + p2);
+ }
+ else {
+ unsigned long n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1;
+
+ return DIV_ROUND_CLOSEST(parent_rate, n1);
+ }
+}
+
+static const struct cec_32k_freq_table *find_cec_32k_freq(unsigned long rate,
+ unsigned long prate)
+{
+ int i;
+
+ for (i = 0 ; i < ARRAY_SIZE(aoclk_cec_32k_table) ; ++i)
+ if (aoclk_cec_32k_table[i].parent_rate == prate &&
+ aoclk_cec_32k_table[i].target_rate == rate)
+ return &aoclk_cec_32k_table[i];
+
+ return NULL;
+}
+
+static long aoclk_cec_32k_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate,
+ *prate);
+
+ /* If invalid return first one */
+ if (!freq)
+ return freq[0].target_rate;
+
+ return freq->target_rate;
+}
+
+static int aoclk_cec_32k_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate,
+ parent_rate);
+ struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw);
+ u32 reg = 0;
+
+ if (!freq)
+ return -EINVAL;
+
+ /* Disable clock */
+ reg = readl(cec_32k->rtc_base + AO_RTC_ALT_CLK_CNTL0);
+ reg &= ~(CLK_CNTL0_IN_GATE_EN | CLK_CNTL0_OUT_GATE_EN);
+ writel(reg, cec_32k->rtc_base + AO_RTC_ALT_CLK_CNTL0);
+
+ if (freq->dualdiv)
+ reg = CLK_CNTL0_DUALDIV_EN |
+ FIELD_PREP(CLK_CNTL0_N1_MASK, freq->n1 - 1) |
+ FIELD_PREP(CLK_CNTL0_N2_MASK, freq->n2 - 1);
+ else
+ reg = FIELD_PREP(CLK_CNTL0_N1_MASK, freq->n1 - 1);
+
+ writel_relaxed(reg, cec_32k->rtc_base + AO_RTC_ALT_CLK_CNTL0);
+
+ if (freq->dualdiv)
+ reg = FIELD_PREP(CLK_CNTL1_M1_MASK, freq->m1 - 1) |
+ FIELD_PREP(CLK_CNTL1_M2_MASK, freq->m2 - 1);
+ else
+ reg = FIELD_PREP(CLK_CNTL1_M1_MASK, freq->m1 - 1);
+
+ writel_relaxed(reg, cec_32k->rtc_base + AO_RTC_ALT_CLK_CNTL1);
+
+ /* Enable clock */
+ reg = readl(cec_32k->rtc_base + AO_RTC_ALT_CLK_CNTL0);
+ reg |= CLK_CNTL0_IN_GATE_EN;
+ writel(reg, cec_32k->rtc_base + AO_RTC_ALT_CLK_CNTL0);
+
+ udelay(200);
+
+ reg |= CLK_CNTL0_OUT_GATE_EN;
+ writel(reg, cec_32k->rtc_base + AO_RTC_ALT_CLK_CNTL0);
+
+ reg = readl(cec_32k->crt_base + AO_CRT_CLK_CNTL1);
+ reg |= CLK_CNTL1_SELECT_OSC; /* select cts_rtc_oscin_clk */
+ writel(reg, cec_32k->crt_base + AO_CRT_CLK_CNTL1);
+
+ /* Select 32k from XTAL */
+ regmap_write_bits(cec_32k->pwr_regmap,
+ AO_RTI_PWR_CNTL_REG0,
+ PWR_CNTL_ALT_32K_SEL,
+ FIELD_PREP(PWR_CNTL_ALT_32K_SEL, 4));
+
+ return 0;
+}
+
+const struct clk_ops meson_aoclk_cec_32k_ops = {
+ .recalc_rate = aoclk_cec_32k_recalc_rate,
+ .round_rate = aoclk_cec_32k_round_rate,
+ .set_rate = aoclk_cec_32k_set_rate,
+};
diff --git a/drivers/clk/meson/gxbb-aoclk.c b/drivers/clk/meson/gxbb-aoclk.c
index b45c5fb..e47f021 100644
--- a/drivers/clk/meson/gxbb-aoclk.c
+++ b/drivers/clk/meson/gxbb-aoclk.c
@@ -56,9 +56,13 @@
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
#include <linux/init.h>
+#include <linux/delay.h>
#include <dt-bindings/clock/gxbb-aoclkc.h>
#include <dt-bindings/reset/gxbb-aoclkc.h>
+#include "gxbb-aoclk.h"
static DEFINE_SPINLOCK(gxbb_aoclk_lock);
@@ -104,6 +108,17 @@ static int gxbb_aoclk_do_reset(struct reset_controller_dev *rcdev,
GXBB_AO_GATE(uart2, 5);
GXBB_AO_GATE(ir_blaster, 6);
+static struct aoclk_cec_32k cec_32k_ao = {
+ .lock = &gxbb_aoclk_lock,
+ .hw.init = &(struct clk_init_data) {
+ .name = "cec_32k_ao",
+ .ops = &meson_aoclk_cec_32k_ops,
+ .parent_names = (const char *[]){ "xtal" },
+ .num_parents = 1,
+ .flags = CLK_IGNORE_UNUSED,
+ },
+};
+
static unsigned int gxbb_aoclk_reset[] = {
[RESET_AO_REMOTE] = 16,
[RESET_AO_I2C_MASTER] = 18,
@@ -130,28 +145,52 @@ static int gxbb_aoclk_do_reset(struct reset_controller_dev *rcdev,
[CLKID_AO_UART1] = &uart1_ao.hw,
[CLKID_AO_UART2] = &uart2_ao.hw,
[CLKID_AO_IR_BLASTER] = &ir_blaster_ao.hw,
+ [CLKID_AO_CEC_32K] = &cec_32k_ao.hw,
},
- .num = ARRAY_SIZE(gxbb_aoclk_gate),
+ .num = 7,
};
static int gxbb_aoclkc_probe(struct platform_device *pdev)
{
- struct resource *res;
+ struct gxbb_aoclk_reset_controller *rstc;
+ struct device *dev = &pdev->dev;
+ struct regmap *regmap_pwr;
+ void __iomem *base_crt;
+ void __iomem *base_rtc;
void __iomem *base;
+ struct resource *res;
int ret, clkid;
- struct device *dev = &pdev->dev;
- struct gxbb_aoclk_reset_controller *rstc;
rstc = devm_kzalloc(dev, sizeof(*rstc), GFP_KERNEL);
if (!rstc)
return -ENOMEM;
/* Generic clocks */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aoclk");
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
+ /* CRT base */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aocrt");
+ base_crt = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base_crt))
+ return PTR_ERR(base_crt);
+
+ /* RTC base */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aortc");
+ base_rtc = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base_rtc))
+ return PTR_ERR(base_rtc);
+
+ /* PWR regmap */
+ regmap_pwr = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "amlogic,pwr-ctrl");
+ if (IS_ERR(regmap_pwr)) {
+ dev_err(dev, "failed to get PWR regmap\n");
+ return -ENODEV;
+ }
+
/* Reset Controller */
rstc->base = base;
rstc->data = gxbb_aoclk_reset;
@@ -163,7 +202,7 @@ static int gxbb_aoclkc_probe(struct platform_device *pdev)
/*
* Populate base address and register all clks
*/
- for (clkid = 0; clkid < gxbb_aoclk_onecell_data.num; clkid++) {
+ for (clkid = 0; clkid < ARRAY_SIZE(gxbb_aoclk_gate); clkid++) {
gxbb_aoclk_gate[clkid]->reg = base;
ret = devm_clk_hw_register(dev,
@@ -172,6 +211,14 @@ static int gxbb_aoclkc_probe(struct platform_device *pdev)
return ret;
}
+ /* Specific clocks */
+ cec_32k_ao.crt_base = base_crt;
+ cec_32k_ao.rtc_base = base_rtc;
+ cec_32k_ao.pwr_regmap = regmap_pwr;
+ ret = devm_clk_hw_register(dev, &cec_32k_ao.hw);
+ if (ret)
+ return ret;
+
return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
&gxbb_aoclk_onecell_data);
}
diff --git a/drivers/clk/meson/gxbb-aoclk.h b/drivers/clk/meson/gxbb-aoclk.h
new file mode 100644
index 0000000..5925a6b
--- /dev/null
+++ b/drivers/clk/meson/gxbb-aoclk.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2017 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __GXBB_AOCLKC_H
+#define __GXBB_AOCLKC_H
+
+struct aoclk_cec_32k {
+ struct clk_hw hw;
+ void __iomem *crt_base;
+ void __iomem *rtc_base;
+ struct regmap *pwr_regmap;
+ spinlock_t *lock;
+};
+
+#define to_aoclk_cec_32k(_hw) container_of(_hw, struct aoclk_cec_32k, hw)
+
+extern const struct clk_ops meson_aoclk_cec_32k_ops;
+
+#endif /* __GXBB_AOCLKC_H */
--
1.9.1

View file

@ -0,0 +1,45 @@
From 9fa0d1f1e56768773b58eb486bf59561b1cc6e18 Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Fri, 23 Jun 2017 15:27:38 +0200
Subject: [PATCH 06/79] ARM64: dts: meson-gx: Add PWR and CRT/RTC nodes and
adresses
The AO 32KHz generation registers are split among multiple registers :
- The CRT Control
- The RTC Clock Control
- The AO Domain PWR Control
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
index 436b875..4fd2926 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
@@ -358,11 +358,20 @@
#size-cells = <2>;
ranges = <0x0 0x0 0x0 0xc8100000 0x0 0x100000>;
+ pwr_AO: power-control@00c {
+ compatible = "amlogic,gx-pwr-ctrl", "syscon";
+ reg = <0x0 0x0000c 0x0 0x8>;
+ };
+
clkc_AO: clock-controller@040 {
compatible = "amlogic,gx-aoclkc", "amlogic,gxbb-aoclkc";
- reg = <0x0 0x00040 0x0 0x4>;
+ reg = <0x0 0x00040 0x0 0x4>,
+ <0x0 0x00068 0x0 0x4>,
+ <0x0 0x00094 0x0 0x8>;
+ reg-names = "aoclk", "aocrt", "aortc";
#clock-cells = <1>;
#reset-cells = <1>;
+ amlogic,pwr-ctrl = <&pwr_AO>;
};
uart_AO: serial@4c0 {
--
1.9.1

View file

@ -0,0 +1,208 @@
From abcaac146da294f437f1a8589b27aced142b4da5 Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Tue, 30 May 2017 16:46:16 +0200
Subject: [PATCH 07/79] ARM64: dts: meson-gx: Add AO CEC nodes
This patch adds the AO CEC node in all the HDMI enabled boards DTS.
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi | 7 +++++++
arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 6 ++++++
arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts | 7 +++++++
arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi | 7 +++++++
arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 6 ++++++
arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts | 7 +++++++
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts | 7 +++++++
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts | 7 +++++++
arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 6 ++++++
arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts | 7 +++++++
10 files changed, 67 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
index a84e276..54718ee 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
@@ -210,6 +210,13 @@
status = "okay";
};
+&cec_AO {
+ status = "okay";
+ pinctrl-0 = <&ao_cec_pins>;
+ pinctrl-names = "default";
+ hdmi-phandle = <&hdmi_tx>;
+};
+
&cvbs_vdac_port {
cvbs_vdac_out: endpoint {
remote-endpoint = <&cvbs_connector_in>;
diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
index 4fd2926..8901eeb 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
@@ -374,6 +374,12 @@
amlogic,pwr-ctrl = <&pwr_AO>;
};
+ cec_AO: cec@100 {
+ compatible = "amlogic,meson-gx-ao-cec";
+ reg = <0x0 0x00100 0x0 0x14>;
+ interrupts = <GIC_SPI 199 IRQ_TYPE_EDGE_RISING>;
+ };
+
uart_AO: serial@4c0 {
compatible = "amlogic,meson-uart";
reg = <0x0 0x004c0 0x0 0x14>;
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts
index 87198ea..9af807e 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts
@@ -171,6 +171,13 @@
pinctrl-names = "default";
};
+&cec_AO {
+ status = "okay";
+ pinctrl-0 = <&ao_cec_pins>;
+ pinctrl-names = "default";
+ hdmi-phandle = <&hdmi_tx>;
+};
+
&ethmac {
status = "okay";
pinctrl-0 = <&eth_rmii_pins>;
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
index 3c6c0b7..3ed5c99 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
@@ -250,6 +250,13 @@
clock-names = "clkin0";
};
+&cec_AO {
+ status = "okay";
+ pinctrl-0 = <&ao_cec_pins>;
+ pinctrl-names = "default";
+ hdmi-phandle = <&hdmi_tx>;
+};
+
&cvbs_vdac_port {
cvbs_vdac_out: endpoint {
remote-endpoint = <&cvbs_connector_in>;
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
index 86105a6..d760d93 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
@@ -97,6 +97,12 @@
};
};
+
+&cec_AO {
+ clocks = <&clkc_AO CLKID_AO_CEC_32K>;
+ clock-names = "core";
+};
+
&ethmac {
clocks = <&clkc CLKID_ETH>,
<&clkc CLKID_FCLK_DIV2>,
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
index f9fbfda..49e19aa 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
@@ -86,6 +86,13 @@
};
};
+&cec_AO {
+ status = "okay";
+ pinctrl-0 = <&ao_cec_pins>;
+ pinctrl-names = "default";
+ hdmi-phandle = <&hdmi_tx>;
+};
+
/* P230 has exclusive choice between internal or external PHY */
&ethmac {
pinctrl-0 = <&eth_pins>;
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts
index 8873c05..55ec11a 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts
@@ -225,6 +225,13 @@
clock-names = "clkin0";
};
+&cec_AO {
+ status = "okay";
+ pinctrl-0 = <&ao_cec_pins>;
+ pinctrl-names = "default";
+ hdmi-phandle = <&hdmi_tx>;
+};
+
&cvbs_vdac_port {
cvbs_vdac_out: endpoint {
remote-endpoint = <&cvbs_connector_in>;
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
index db31e09..99a01ff 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
@@ -60,6 +60,13 @@
};
};
+&cec_AO {
+ status = "okay";
+ pinctrl-0 = <&ao_cec_pins>;
+ pinctrl-names = "default";
+ hdmi-phandle = <&hdmi_tx>;
+};
+
&cvbs_vdac_port {
cvbs_vdac_out: endpoint {
remote-endpoint = <&cvbs_connector_in>;
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
index d8e096d..3a1ccf5 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
@@ -43,6 +43,7 @@
#include "meson-gx.dtsi"
#include <dt-bindings/clock/gxbb-clkc.h>
+#include <dt-bindings/clock/gxbb-aoclkc.h>
#include <dt-bindings/gpio/meson-gxl-gpio.h>
#include <dt-bindings/reset/amlogic,meson-gxbb-reset.h>
@@ -564,6 +565,11 @@
compatible = "amlogic,meson-gxl-vpu", "amlogic,meson-gx-vpu";
};
+&cec_AO {
+ clocks = <&clkc_AO CLKID_AO_CEC_32K>;
+ clock-names = "core";
+};
+
&hdmi_tx {
compatible = "amlogic,meson-gxl-dw-hdmi", "amlogic,meson-gx-dw-hdmi";
resets = <&reset RESET_HDMITX_CAPB3>,
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
index 11b0bf4..3a327dd 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
@@ -191,6 +191,13 @@
};
};
+&cec_AO {
+ status = "okay";
+ pinctrl-0 = <&ao_cec_pins>;
+ pinctrl-names = "default";
+ hdmi-phandle = <&hdmi_tx>;
+};
+
&cvbs_vdac_port {
cvbs_vdac_out: endpoint {
remote-endpoint = <&cvbs_connector_in>;
--
1.9.1

View file

@ -0,0 +1,80 @@
From ab3f6488b7ea751456324f57d27179599c151136 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@armlinux.org.uk>
Date: Fri, 4 Dec 2015 10:47:35 +0000
Subject: [PATCH 08/79] drm/bridge: dw_hdmi: add cec notifier support
Add CEC notifier support to the HDMI bridge driver, so that the CEC
part of the IP can receive its physical address.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index ee788ca..5eff1e5 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -33,6 +33,8 @@
#include <uapi/linux/media-bus-format.h>
#include <uapi/linux/videodev2.h>
+#include <media/cec-notifier.h>
+
#include "dw-hdmi.h"
#include "dw-hdmi-audio.h"
@@ -175,6 +175,7 @@ struct dw_hdmi {
unsigned int reg_shift;
struct regmap *regm;
+ struct cec_notifier *cec_notifier;
void (*enable_audio)(struct dw_hdmi *hdmi);
void (*disable_audio)(struct dw_hdmi *hdmi);
};
@@ -1881,6 +1885,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
drm_mode_connector_update_edid_property(connector, edid);
+ cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid);
ret = drm_add_edid_modes(connector, edid);
/* Store the ELD */
drm_edid_to_eld(connector, edid);
@@ -2077,6 +2082,10 @@ void __dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense)
dw_hdmi_update_phy_mask(hdmi);
}
mutex_unlock(&hdmi->mutex);
+
+ if (!rx_sense && !hpd)
+ cec_notifier_set_phys_addr(hdmi->cec_notifier,
+ CEC_PHYS_ADDR_INVALID);
}
void dw_hdmi_setup_rx_sense(struct device *dev, bool hpd, bool rx_sense)
@@ -2376,6 +2385,12 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi)
if (ret)
goto err_iahb;
+ hdmi->cec_notifier = cec_notifier_get(dev);
+ if (!hdmi->cec_notifier) {
+ ret = -ENOMEM;
+ goto err_iahb;
+ }
+
/*
* To prevent overflows in HDMI_IH_FC_STAT2, set the clk regenerator
* N and cts values before enabling phy
@@ -2448,6 +2463,9 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi)
hdmi->ddc = NULL;
}
+ if (hdmi->cec_notifier)
+ cec_notifier_put(hdmi->cec_notifier);
+
clk_disable_unprepare(hdmi->iahb_clk);
err_isfr:
clk_disable_unprepare(hdmi->isfr_clk);
--
1.9.1

View file

@ -0,0 +1,725 @@
From c3be57d7fef20314fffd81e74b70ea75e35a4d26 Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Tue, 30 May 2017 15:50:21 +0200
Subject: [PATCH 09/79] platform: Add Amlogic Meson AO CEC Controller driver
The Amlogic SoC embeds a standalone CEC controller, this patch adds a driver
for such controller.
The controller does not need HPD to be active, and could support up to max
5 logical addresses, but only 1 is handled since the Suspend firmware can
make use of this unique logical address to wake up the device.
The Suspend firmware configuration will be added in an other patchset.
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/media/platform/Kconfig | 11 +
drivers/media/platform/Makefile | 2 +
drivers/media/platform/meson/Makefile | 1 +
drivers/media/platform/meson/ao-cec.c | 653 ++++++++++++++++++++++++++++++++++
4 files changed, 667 insertions(+)
create mode 100644 drivers/media/platform/meson/Makefile
create mode 100644 drivers/media/platform/meson/ao-cec.c
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 041cb80..857e068 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -499,6 +499,17 @@ menuconfig CEC_PLATFORM_DRIVERS
if CEC_PLATFORM_DRIVERS
+config VIDEO_MESON_AO_CEC
+ tristate "Amlogic Meson AO CEC driver"
+ depends on ARCH_MESON || COMPILE_TEST
+ select CEC_CORE
+ select CEC_NOTIFIER
+ ---help---
+ This is a driver for Amlogic Meson SoCs AO CEC interface. It uses the
+ generic CEC framework interface.
+ CEC bus is present in the HDMI connector and enables communication
+ between compatible devices.
+
config VIDEO_SAMSUNG_S5P_CEC
tristate "Samsung S5P CEC driver"
depends on PLAT_S5P || ARCH_EXYNOS || COMPILE_TEST
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 63303d6..db15c59 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -85,4 +85,6 @@ obj-$(CONFIG_VIDEO_MEDIATEK_MDP) += mtk-mdp/
obj-$(CONFIG_VIDEO_MEDIATEK_JPEG) += mtk-jpeg/
+obj-y += meson/
+
obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/
diff --git a/drivers/media/platform/meson/Makefile b/drivers/media/platform/meson/Makefile
new file mode 100644
index 0000000..597beb8
--- /dev/null
+++ b/drivers/media/platform/meson/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_MESON_AO_CEC) += ao-cec.o
diff --git a/drivers/media/platform/meson/ao-cec.c b/drivers/media/platform/meson/ao-cec.c
new file mode 100644
index 0000000..26d7c3e8
--- /dev/null
+++ b/drivers/media/platform/meson/ao-cec.c
@@ -0,0 +1,653 @@
+/*
+ * Driver for Amlogic Meson AO CEC Controller
+ *
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved
+ * Copyright (C) 2017 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/reset.h>
+#include <media/cec.h>
+#include <media/cec-notifier.h>
+
+/* CEC Registers */
+
+/*
+ * [2:1] cntl_clk
+ * - 0 = Disable clk (Power-off mode)
+ * - 1 = Enable gated clock (Normal mode)
+ * - 2 = Enable free-run clk (Debug mode)
+ */
+#define CEC_GEN_CNTL_REG 0x00
+
+#define CEC_GEN_CNTL_RESET BIT(0)
+#define CEC_GEN_CNTL_CLK_DISABLE 0
+#define CEC_GEN_CNTL_CLK_ENABLE 1
+#define CEC_GEN_CNTL_CLK_ENABLE_DBG 2
+#define CEC_GEN_CNTL_CLK_CTRL_MASK GENMASK(2, 1)
+
+/*
+ * [7:0] cec_reg_addr
+ * [15:8] cec_reg_wrdata
+ * [16] cec_reg_wr
+ * - 0 = Read
+ * - 1 = Write
+ * [23] bus free
+ * [31:24] cec_reg_rddata
+ */
+#define CEC_RW_REG 0x04
+
+#define CEC_RW_ADDR GENMASK(7, 0)
+#define CEC_RW_WR_DATA GENMASK(15, 8)
+#define CEC_RW_WRITE_EN BIT(16)
+#define CEC_RW_BUS_BUSY BIT(23)
+#define CEC_RW_RD_DATA GENMASK(31, 24)
+
+/*
+ * [1] tx intr
+ * [2] rx intr
+ */
+#define CEC_INTR_MASKN_REG 0x08
+#define CEC_INTR_CLR_REG 0x0c
+#define CEC_INTR_STAT_REG 0x10
+
+#define CEC_INTR_TX BIT(1)
+#define CEC_INTR_RX BIT(2)
+
+/* CEC Commands */
+
+#define CEC_TX_MSG_0_HEADER 0x00
+#define CEC_TX_MSG_1_OPCODE 0x01
+#define CEC_TX_MSG_2_OP1 0x02
+#define CEC_TX_MSG_3_OP2 0x03
+#define CEC_TX_MSG_4_OP3 0x04
+#define CEC_TX_MSG_5_OP4 0x05
+#define CEC_TX_MSG_6_OP5 0x06
+#define CEC_TX_MSG_7_OP6 0x07
+#define CEC_TX_MSG_8_OP7 0x08
+#define CEC_TX_MSG_9_OP8 0x09
+#define CEC_TX_MSG_A_OP9 0x0A
+#define CEC_TX_MSG_B_OP10 0x0B
+#define CEC_TX_MSG_C_OP11 0x0C
+#define CEC_TX_MSG_D_OP12 0x0D
+#define CEC_TX_MSG_E_OP13 0x0E
+#define CEC_TX_MSG_F_OP14 0x0F
+#define CEC_TX_MSG_LENGTH 0x10
+#define CEC_TX_MSG_CMD 0x11
+#define CEC_TX_WRITE_BUF 0x12
+#define CEC_TX_CLEAR_BUF 0x13
+#define CEC_RX_MSG_CMD 0x14
+#define CEC_RX_CLEAR_BUF 0x15
+#define CEC_LOGICAL_ADDR0 0x16
+#define CEC_LOGICAL_ADDR1 0x17
+#define CEC_LOGICAL_ADDR2 0x18
+#define CEC_LOGICAL_ADDR3 0x19
+#define CEC_LOGICAL_ADDR4 0x1A
+#define CEC_CLOCK_DIV_H 0x1B
+#define CEC_CLOCK_DIV_L 0x1C
+#define CEC_QUIESCENT_25MS_BIT7_0 0x20
+#define CEC_QUIESCENT_25MS_BIT11_8 0x21
+#define CEC_STARTBITMINL2H_3MS5_BIT7_0 0x22
+#define CEC_STARTBITMINL2H_3MS5_BIT8 0x23
+#define CEC_STARTBITMAXL2H_3MS9_BIT7_0 0x24
+#define CEC_STARTBITMAXL2H_3MS9_BIT8 0x25
+#define CEC_STARTBITMINH_0MS6_BIT7_0 0x26
+#define CEC_STARTBITMINH_0MS6_BIT8 0x27
+#define CEC_STARTBITMAXH_1MS0_BIT7_0 0x28
+#define CEC_STARTBITMAXH_1MS0_BIT8 0x29
+#define CEC_STARTBITMINTOT_4MS3_BIT7_0 0x2A
+#define CEC_STARTBITMINTOT_4MS3_BIT9_8 0x2B
+#define CEC_STARTBITMAXTOT_4MS7_BIT7_0 0x2C
+#define CEC_STARTBITMAXTOT_4MS7_BIT9_8 0x2D
+#define CEC_LOGIC1MINL2H_0MS4_BIT7_0 0x2E
+#define CEC_LOGIC1MINL2H_0MS4_BIT8 0x2F
+#define CEC_LOGIC1MAXL2H_0MS8_BIT7_0 0x30
+#define CEC_LOGIC1MAXL2H_0MS8_BIT8 0x31
+#define CEC_LOGIC0MINL2H_1MS3_BIT7_0 0x32
+#define CEC_LOGIC0MINL2H_1MS3_BIT8 0x33
+#define CEC_LOGIC0MAXL2H_1MS7_BIT7_0 0x34
+#define CEC_LOGIC0MAXL2H_1MS7_BIT8 0x35
+#define CEC_LOGICMINTOTAL_2MS05_BIT7_0 0x36
+#define CEC_LOGICMINTOTAL_2MS05_BIT9_8 0x37
+#define CEC_LOGICMAXHIGH_2MS8_BIT7_0 0x38
+#define CEC_LOGICMAXHIGH_2MS8_BIT8 0x39
+#define CEC_LOGICERRLOW_3MS4_BIT7_0 0x3A
+#define CEC_LOGICERRLOW_3MS4_BIT8 0x3B
+#define CEC_NOMSMPPOINT_1MS05 0x3C
+#define CEC_DELCNTR_LOGICERR 0x3E
+#define CEC_TXTIME_17MS_BIT7_0 0x40
+#define CEC_TXTIME_17MS_BIT10_8 0x41
+#define CEC_TXTIME_2BIT_BIT7_0 0x42
+#define CEC_TXTIME_2BIT_BIT10_8 0x43
+#define CEC_TXTIME_4BIT_BIT7_0 0x44
+#define CEC_TXTIME_4BIT_BIT10_8 0x45
+#define CEC_STARTBITNOML2H_3MS7_BIT7_0 0x46
+#define CEC_STARTBITNOML2H_3MS7_BIT8 0x47
+#define CEC_STARTBITNOMH_0MS8_BIT7_0 0x48
+#define CEC_STARTBITNOMH_0MS8_BIT8 0x49
+#define CEC_LOGIC1NOML2H_0MS6_BIT7_0 0x4A
+#define CEC_LOGIC1NOML2H_0MS6_BIT8 0x4B
+#define CEC_LOGIC0NOML2H_1MS5_BIT7_0 0x4C
+#define CEC_LOGIC0NOML2H_1MS5_BIT8 0x4D
+#define CEC_LOGIC1NOMH_1MS8_BIT7_0 0x4E
+#define CEC_LOGIC1NOMH_1MS8_BIT8 0x4F
+#define CEC_LOGIC0NOMH_0MS9_BIT7_0 0x50
+#define CEC_LOGIC0NOMH_0MS9_BIT8 0x51
+#define CEC_LOGICERRLOW_3MS6_BIT7_0 0x52
+#define CEC_LOGICERRLOW_3MS6_BIT8 0x53
+#define CEC_CHKCONTENTION_0MS1 0x54
+#define CEC_PREPARENXTBIT_0MS05_BIT7_0 0x56
+#define CEC_PREPARENXTBIT_0MS05_BIT8 0x57
+#define CEC_NOMSMPACKPOINT_0MS45 0x58
+#define CEC_ACK0NOML2H_1MS5_BIT7_0 0x5A
+#define CEC_ACK0NOML2H_1MS5_BIT8 0x5B
+#define CEC_BUGFIX_DISABLE_0 0x60
+#define CEC_BUGFIX_DISABLE_1 0x61
+#define CEC_RX_MSG_0_HEADER 0x80
+#define CEC_RX_MSG_1_OPCODE 0x81
+#define CEC_RX_MSG_2_OP1 0x82
+#define CEC_RX_MSG_3_OP2 0x83
+#define CEC_RX_MSG_4_OP3 0x84
+#define CEC_RX_MSG_5_OP4 0x85
+#define CEC_RX_MSG_6_OP5 0x86
+#define CEC_RX_MSG_7_OP6 0x87
+#define CEC_RX_MSG_8_OP7 0x88
+#define CEC_RX_MSG_9_OP8 0x89
+#define CEC_RX_MSG_A_OP9 0x8A
+#define CEC_RX_MSG_B_OP10 0x8B
+#define CEC_RX_MSG_C_OP11 0x8C
+#define CEC_RX_MSG_D_OP12 0x8D
+#define CEC_RX_MSG_E_OP13 0x8E
+#define CEC_RX_MSG_F_OP14 0x8F
+#define CEC_RX_MSG_LENGTH 0x90
+#define CEC_RX_MSG_STATUS 0x91
+#define CEC_RX_NUM_MSG 0x92
+#define CEC_TX_MSG_STATUS 0x93
+#define CEC_TX_NUM_MSG 0x94
+
+
+/* CEC_TX_MSG_CMD definition */
+#define TX_NO_OP 0 /* No transaction */
+#define TX_REQ_CURRENT 1 /* Transmit earliest message in buffer */
+#define TX_ABORT 2 /* Abort transmitting earliest message */
+#define TX_REQ_NEXT 3 /* Overwrite earliest msg, transmit next */
+
+/* tx_msg_status definition */
+#define TX_IDLE 0 /* No transaction */
+#define TX_BUSY 1 /* Transmitter is busy */
+#define TX_DONE 2 /* Message successfully transmitted */
+#define TX_ERROR 3 /* Message transmitted with error */
+
+/* rx_msg_cmd */
+#define RX_NO_OP 0 /* No transaction */
+#define RX_ACK_CURRENT 1 /* Read earliest message in buffer */
+#define RX_DISABLE 2 /* Disable receiving latest message */
+#define RX_ACK_NEXT 3 /* Clear earliest msg, read next */
+
+/* rx_msg_status */
+#define RX_IDLE 0 /* No transaction */
+#define RX_BUSY 1 /* Receiver is busy */
+#define RX_DONE 2 /* Message has been received successfully */
+#define RX_ERROR 3 /* Message has been received with error */
+
+/* RX_CLEAR_BUF options */
+#define CLEAR_START 1
+#define CLEAR_STOP 0
+
+/* CEC_LOGICAL_ADDRx options */
+#define LOGICAL_ADDR_MASK 0xf
+#define LOGICAL_ADDR_VALID BIT(4)
+#define LOGICAL_ADDR_DISABLE 0
+
+#define CEC_CLK_RATE 32768
+
+struct meson_ao_cec_device {
+ struct platform_device *pdev;
+ void __iomem *base;
+ struct clk *core;
+ spinlock_t cec_reg_lock;
+ struct cec_notifier *notify;
+ struct cec_adapter *adap;
+ struct cec_msg rx_msg;
+};
+
+#define writel_bits_relaxed(mask, val, addr) \
+ writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr)
+
+static inline void meson_ao_cec_wait_busy(struct meson_ao_cec_device *ao_cec)
+{
+ while (readl_relaxed(ao_cec->base + CEC_RW_REG) &
+ CEC_RW_BUS_BUSY)
+ ;
+}
+
+static u8 meson_ao_cec_read(struct meson_ao_cec_device *ao_cec,
+ unsigned long address)
+{
+ unsigned long flags;
+ u32 reg = FIELD_PREP(CEC_RW_ADDR, address);
+ u8 data;
+
+ spin_lock_irqsave(&ao_cec->cec_reg_lock, flags);
+
+ meson_ao_cec_wait_busy(ao_cec);
+
+ writel_relaxed(reg, ao_cec->base + CEC_RW_REG);
+
+ meson_ao_cec_wait_busy(ao_cec);
+
+ data = FIELD_GET(CEC_RW_RD_DATA,
+ readl_relaxed(ao_cec->base + CEC_RW_REG));
+
+ spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags);
+
+ return data;
+}
+
+static void meson_ao_cec_write(struct meson_ao_cec_device *ao_cec,
+ unsigned long address, u8 data)
+{
+ unsigned long flags;
+ u32 reg = FIELD_PREP(CEC_RW_ADDR, address) |
+ FIELD_PREP(CEC_RW_WR_DATA, data) |
+ CEC_RW_WRITE_EN;
+
+ spin_lock_irqsave(&ao_cec->cec_reg_lock, flags);
+
+ meson_ao_cec_wait_busy(ao_cec);
+
+ writel_relaxed(reg, ao_cec->base + CEC_RW_REG);
+
+ spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags);
+}
+
+static inline void meson_ao_cec_irq_setup(struct meson_ao_cec_device *ao_cec,
+ bool enable)
+{
+ u32 cfg = CEC_INTR_TX | CEC_INTR_RX;
+
+ writel_bits_relaxed(cfg, enable ? cfg : 0,
+ ao_cec->base + CEC_INTR_MASKN_REG);
+}
+
+static inline void meson_ao_cec_clear(struct meson_ao_cec_device *ao_cec)
+{
+ meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_DISABLE);
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT);
+ meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, 1);
+ meson_ao_cec_write(ao_cec, CEC_TX_CLEAR_BUF, 1);
+
+ udelay(100);
+
+ meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, 0);
+ meson_ao_cec_write(ao_cec, CEC_TX_CLEAR_BUF, 0);
+
+ udelay(100);
+
+ meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_NO_OP);
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_NO_OP);
+}
+
+static void meson_ao_cec_arbit_bit_time_set(struct meson_ao_cec_device *ao_cec,
+ unsigned int bit_set,
+ unsigned int time_set)
+{
+ switch (bit_set) {
+ case 3:
+ meson_ao_cec_write(ao_cec, CEC_TXTIME_4BIT_BIT7_0,
+ time_set & 0xff);
+ meson_ao_cec_write(ao_cec, CEC_TXTIME_4BIT_BIT10_8,
+ (time_set >> 8) & 0x7);
+ break;
+
+ case 5:
+ meson_ao_cec_write(ao_cec, CEC_TXTIME_2BIT_BIT7_0,
+ time_set & 0xff);
+ meson_ao_cec_write(ao_cec, CEC_TXTIME_2BIT_BIT10_8,
+ (time_set >> 8) & 0x7);
+ break;
+
+ case 7:
+ meson_ao_cec_write(ao_cec, CEC_TXTIME_17MS_BIT7_0,
+ time_set & 0xff);
+ meson_ao_cec_write(ao_cec, CEC_TXTIME_17MS_BIT10_8,
+ (time_set >> 8) & 0x7);
+ break;
+ }
+}
+
+static irqreturn_t meson_ao_cec_irq(int irq, void *data)
+{
+ struct meson_ao_cec_device *ao_cec = data;
+ u32 stat = readl_relaxed(ao_cec->base + CEC_INTR_STAT_REG);
+
+ if (stat)
+ return IRQ_WAKE_THREAD;
+
+ return IRQ_NONE;
+}
+
+static void meson_ao_cec_irq_tx(struct meson_ao_cec_device *ao_cec)
+{
+ unsigned long tx_status = 0;
+ u8 stat = meson_ao_cec_read(ao_cec, CEC_TX_MSG_STATUS);
+
+ switch (stat) {
+ case TX_DONE:
+ tx_status = CEC_TX_STATUS_OK;
+ break;
+
+ case TX_BUSY:
+ tx_status = CEC_TX_STATUS_ARB_LOST;
+ break;
+
+ case TX_IDLE:
+ tx_status = CEC_TX_STATUS_LOW_DRIVE;
+ break;
+
+ case TX_ERROR:
+ default:
+ tx_status = CEC_TX_STATUS_NACK;
+ }
+
+ /* Clear Interruption */
+ writel_relaxed(CEC_INTR_TX, ao_cec->base + CEC_INTR_CLR_REG);
+
+ /* Stop TX */
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_NO_OP);
+
+ cec_transmit_attempt_done(ao_cec->adap, tx_status);
+}
+
+static void meson_ao_cec_irq_rx(struct meson_ao_cec_device *ao_cec)
+{
+ u8 stat = meson_ao_cec_read(ao_cec, CEC_RX_MSG_STATUS);
+ int i;
+
+ /* RX Error */
+ if (stat != RX_DONE ||
+ meson_ao_cec_read(ao_cec, CEC_RX_NUM_MSG) != 1)
+ goto rx_out;
+
+ ao_cec->rx_msg.len = meson_ao_cec_read(ao_cec, CEC_RX_MSG_LENGTH) + 1;
+ if (ao_cec->rx_msg.len > CEC_MAX_MSG_SIZE)
+ ao_cec->rx_msg.len = CEC_MAX_MSG_SIZE;
+
+ for (i = 0; i < ao_cec->rx_msg.len; i++)
+ ao_cec->rx_msg.msg[i] =
+ meson_ao_cec_read(ao_cec, CEC_RX_MSG_0_HEADER + i);
+
+ cec_received_msg(ao_cec->adap, &ao_cec->rx_msg);
+
+rx_out:
+ /* Clear Interruption */
+ writel_relaxed(CEC_INTR_RX, ao_cec->base + CEC_INTR_CLR_REG);
+
+ /* Ack RX message */
+ meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_ACK_CURRENT);
+ meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_NO_OP);
+
+ /* Clear RX buffer */
+ meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, CLEAR_START);
+ meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, CLEAR_STOP);
+}
+
+static irqreturn_t meson_ao_cec_irq_thread(int irq, void *data)
+{
+ struct meson_ao_cec_device *ao_cec = data;
+ u32 stat = readl_relaxed(ao_cec->base + CEC_INTR_STAT_REG);
+
+ if (stat & CEC_INTR_TX)
+ meson_ao_cec_irq_tx(ao_cec);
+
+ meson_ao_cec_irq_rx(ao_cec);
+
+ return IRQ_HANDLED;
+}
+
+static int meson_ao_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct meson_ao_cec_device *ao_cec = adap->priv;
+
+ meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0, LOGICAL_ADDR_DISABLE);
+
+ meson_ao_cec_clear(ao_cec);
+
+ if (logical_addr == CEC_LOG_ADDR_INVALID)
+ return 0;
+
+ meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0,
+ logical_addr & LOGICAL_ADDR_MASK);
+
+ udelay(100);
+
+ meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0,
+ (logical_addr & LOGICAL_ADDR_MASK) |
+ LOGICAL_ADDR_VALID);
+
+ return 0;
+}
+
+static int meson_ao_cec_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct meson_ao_cec_device *ao_cec = adap->priv;
+ u8 reg;
+ int i;
+
+ reg = meson_ao_cec_read(ao_cec, CEC_TX_MSG_STATUS);
+ if (reg == TX_BUSY) {
+ dev_err(&ao_cec->pdev->dev, "%s: busy TX\n", __func__);
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT);
+ }
+
+ for (i = 0; i < msg->len; i++)
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_0_HEADER + i,
+ msg->msg[i]);
+
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_LENGTH, msg->len - 1);
+ meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_REQ_CURRENT);
+
+ return 0;
+}
+
+static int meson_ao_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct meson_ao_cec_device *ao_cec = adap->priv;
+
+ meson_ao_cec_irq_setup(ao_cec, false);
+
+ writel_bits_relaxed(CEC_GEN_CNTL_RESET, CEC_GEN_CNTL_RESET,
+ ao_cec->base + CEC_GEN_CNTL_REG);
+
+ if (!enable)
+ return 0;
+
+ /* Enable gated clock (Normal mode). */
+ writel_bits_relaxed(CEC_GEN_CNTL_CLK_CTRL_MASK,
+ FIELD_PREP(CEC_GEN_CNTL_CLK_CTRL_MASK,
+ CEC_GEN_CNTL_CLK_ENABLE),
+ ao_cec->base + CEC_GEN_CNTL_REG);
+
+ udelay(100);
+
+ /* Release Reset */
+ writel_bits_relaxed(CEC_GEN_CNTL_RESET, 0,
+ ao_cec->base + CEC_GEN_CNTL_REG);
+
+ /* Clear buffers */
+ meson_ao_cec_clear(ao_cec);
+
+ /* CEC arbitration 3/5/7 bit time set. */
+ meson_ao_cec_arbit_bit_time_set(ao_cec, 3, 0x118);
+ meson_ao_cec_arbit_bit_time_set(ao_cec, 5, 0x000);
+ meson_ao_cec_arbit_bit_time_set(ao_cec, 7, 0x2aa);
+
+ meson_ao_cec_irq_setup(ao_cec, true);
+
+ return 0;
+}
+
+static const struct cec_adap_ops meson_ao_cec_ops = {
+ .adap_enable = meson_ao_cec_adap_enable,
+ .adap_log_addr = meson_ao_cec_set_log_addr,
+ .adap_transmit = meson_ao_cec_transmit,
+};
+
+static int meson_ao_cec_probe(struct platform_device *pdev)
+{
+ struct meson_ao_cec_device *ao_cec;
+ struct platform_device *hdmi_dev;
+ struct device_node *np;
+ struct resource *res;
+ int ret, irq;
+
+ np = of_parse_phandle(pdev->dev.of_node, "hdmi-phandle", 0);
+ if (!np) {
+ dev_err(&pdev->dev, "Failed to find hdmi node\n");
+ return -ENODEV;
+ }
+
+ hdmi_dev = of_find_device_by_node(np);
+ if (hdmi_dev == NULL)
+ return -EPROBE_DEFER;
+
+ ao_cec = devm_kzalloc(&pdev->dev, sizeof(*ao_cec), GFP_KERNEL);
+ if (!ao_cec)
+ return -ENOMEM;
+
+ spin_lock_init(&ao_cec->cec_reg_lock);
+
+ ao_cec->notify = cec_notifier_get(&hdmi_dev->dev);
+ if (!ao_cec->notify)
+ return -ENOMEM;
+
+ ao_cec->adap = cec_allocate_adapter(&meson_ao_cec_ops, ao_cec,
+ "meson_ao_cec",
+ CEC_CAP_LOG_ADDRS |
+ CEC_CAP_TRANSMIT |
+ CEC_CAP_RC,
+ 1); /* Use 1 for now */
+ if (IS_ERR(ao_cec->adap)) {
+ ret = PTR_ERR(ao_cec->adap);
+ goto out_probe_notify;
+ }
+
+ ao_cec->adap->owner = THIS_MODULE;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ao_cec->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ao_cec->base)) {
+ ret = PTR_ERR(ao_cec->base);
+ goto out_probe_adapter;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_threaded_irq(&pdev->dev, irq,
+ meson_ao_cec_irq,
+ meson_ao_cec_irq_thread,
+ 0, NULL, ao_cec);
+ if (ret) {
+ dev_err(&pdev->dev, "irq request failed\n");
+ goto out_probe_adapter;
+ }
+
+ ao_cec->core = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(ao_cec->core)) {
+ dev_err(&pdev->dev, "core clock request failed\n");
+ ret = PTR_ERR(ao_cec->core);
+ goto out_probe_adapter;
+ }
+
+ ret = clk_prepare_enable(ao_cec->core);
+ if (ret) {
+ dev_err(&pdev->dev, "core clock enable failed\n");
+ goto out_probe_adapter;
+ }
+
+ ret = clk_set_rate(ao_cec->core, CEC_CLK_RATE);
+ if (ret) {
+ dev_err(&pdev->dev, "core clock set rate failed\n");
+ goto out_probe_clk;
+ }
+
+ device_reset_optional(&pdev->dev);
+
+ ao_cec->pdev = pdev;
+ platform_set_drvdata(pdev, ao_cec);
+
+ ret = cec_register_adapter(ao_cec->adap, &pdev->dev);
+ if (ret < 0) {
+ cec_notifier_put(ao_cec->notify);
+ goto out_probe_clk;
+ }
+
+ /* Setup Hardware */
+ writel_relaxed(CEC_GEN_CNTL_RESET,
+ ao_cec->base + CEC_GEN_CNTL_REG);
+
+ cec_register_cec_notifier(ao_cec->adap, ao_cec->notify);
+
+ return 0;
+
+out_probe_clk:
+ clk_disable_unprepare(ao_cec->core);
+
+out_probe_adapter:
+ cec_delete_adapter(ao_cec->adap);
+
+out_probe_notify:
+ cec_notifier_put(ao_cec->notify);
+
+ dev_err(&pdev->dev, "CEC controller registration failed\n");
+
+ return ret;
+}
+
+static int meson_ao_cec_remove(struct platform_device *pdev)
+{
+ struct meson_ao_cec_device *ao_cec = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(ao_cec->core);
+
+ cec_unregister_adapter(ao_cec->adap);
+
+ cec_notifier_put(ao_cec->notify);
+
+ return 0;
+}
+
+static const struct of_device_id meson_ao_cec_of_match[] = {
+ { .compatible = "amlogic,meson-gx-ao-cec", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, meson_ao_cec_of_match);
+
+static struct platform_driver meson_ao_cec_driver = {
+ .probe = meson_ao_cec_probe,
+ .remove = meson_ao_cec_remove,
+ .driver = {
+ .name = "meson-ao-cec",
+ .of_match_table = of_match_ptr(meson_ao_cec_of_match),
+ },
+};
+
+module_platform_driver(meson_ao_cec_driver);
+
+MODULE_DESCRIPTION("Meson AO CEC Controller driver");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_LICENSE("GPL");
--
1.9.1

View file

@ -0,0 +1,42 @@
From f620469d663615a805ca21169013ba79089e2339 Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Mon, 22 May 2017 15:14:45 +0200
Subject: [PATCH 13/79] ARM64: dts: meson-gxbb: Add SPI pinctrl nodes
This patch adds the SPICC Controller pins nodes for Amlogic GXBB SoCs.
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
index daa49ae..1d4c861 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
@@ -310,6 +310,22 @@
};
};
+ spi_pins: spi {
+ mux {
+ groups = "spi_miso",
+ "spi_mosi",
+ "spi_sclk";
+ function = "spi";
+ };
+ };
+
+ spi_ss0_pins: spi-ss0 {
+ mux {
+ groups = "spi_ss0";
+ function = "spi";
+ };
+ };
+
sdcard_pins: sdcard {
mux {
groups = "sdcard_d0",
--
1.9.1

View file

@ -0,0 +1,42 @@
From d83151a6b97f039c3efa13d70d9697ef1fd887ac Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Mon, 22 May 2017 15:15:13 +0200
Subject: [PATCH 14/79] ARM64: dts: meson-gxl: Add SPI pinctrl nodes
This patch adds the SPICC Controller pins nodes for Amlogic GXL SoCs.
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
index dc7c53f..6584e46 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
@@ -246,6 +246,22 @@
};
};
+ spi_pins: spi {
+ mux {
+ groups = "spi_miso",
+ "spi_mosi",
+ "spi_sclk";
+ function = "spi";
+ };
+ };
+
+ spi_ss0_pins: spi-ss0 {
+ mux {
+ groups = "spi_ss0";
+ function = "spi";
+ };
+ };
+
sdcard_pins: sdcard {
mux {
groups = "sdcard_d0",
--
1.9.1

View file

@ -0,0 +1,42 @@
From 5337b8bcf8f92396880e95094f639a343ef5453a Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 15:11:42 +0200
Subject: [PATCH 26/79] dt-bindings: clock: gxbb: expose audin clock gate
Expose the clock gate required to power AUDIN block
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/clk/meson/gxbb.h | 2 +-
include/dt-bindings/clock/gxbb-clkc.h | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/meson/gxbb.h b/drivers/clk/meson/gxbb.h
index 93b8f07..5c24285 100644
--- a/drivers/clk/meson/gxbb.h
+++ b/drivers/clk/meson/gxbb.h
@@ -205,7 +205,7 @@
#define CLKID_HIU_IFACE 32
#define CLKID_ASSIST_MISC 33
/* CLKID_SPI */
-#define CLKID_I2S_SPDIF 35
+/* CLKID_I2S_SPDIF */
/* CLKID_ETH */
#define CLKID_DEMUX 37
/* CLKID_AIU_GLUE */
diff --git a/include/dt-bindings/clock/gxbb-clkc.h b/include/dt-bindings/clock/gxbb-clkc.h
index 3190e30..ba8a9be 100644
--- a/include/dt-bindings/clock/gxbb-clkc.h
+++ b/include/dt-bindings/clock/gxbb-clkc.h
@@ -17,6 +17,7 @@
#define CLKID_SAR_ADC 23
#define CLKID_RNG0 25
#define CLKID_SPI 34
+#define CLKID_I2S_SPDIF 35
#define CLKID_ETH 36
#define CLKID_AIU_GLUE 38
#define CLKID_I2S_OUT 40
--
1.9.1

View file

@ -0,0 +1,312 @@
From d4b067ab4833a89ccbd6c67bf5245621794dbe73 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 11:49:55 +0200
Subject: [PATCH 27/79] ASoC: meson: add meson audio core driver
This patch adds support for the audio core driver for the Amlogic Meson SoC
family. The purpose of this driver is to properly reset the audio block and
provide register access for the different devices scattered in this address
space. This includes output and input DMAs, pcm, i2s and spdif dai, card
level routing, internal codec for the gxl variant
For more information, please refer to the section 5 of the public datasheet
of the S905 (gxbb). This datasheet is available here: [0].
[0]: http://dn.odroid.com/S905/DataSheet/S905_Public_Datasheet_V1.1.4.pdf
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
sound/soc/Kconfig | 1 +
sound/soc/Makefile | 1 +
sound/soc/meson/Kconfig | 9 ++
sound/soc/meson/Makefile | 3 +
sound/soc/meson/audio-core.c | 190 +++++++++++++++++++++++++++++++++++++++++++
sound/soc/meson/audio-core.h | 28 +++++++
6 files changed, 232 insertions(+)
create mode 100644 sound/soc/meson/Kconfig
create mode 100644 sound/soc/meson/Makefile
create mode 100644 sound/soc/meson/audio-core.c
create mode 100644 sound/soc/meson/audio-core.h
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index c0abad2..7db316f 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -55,6 +55,7 @@ source "sound/soc/kirkwood/Kconfig"
source "sound/soc/img/Kconfig"
source "sound/soc/intel/Kconfig"
source "sound/soc/mediatek/Kconfig"
+source "sound/soc/meson/Kconfig"
source "sound/soc/mxs/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/qcom/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 39c27a5..d9892c3 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_SND_SOC) += jz4740/
obj-$(CONFIG_SND_SOC) += img/
obj-$(CONFIG_SND_SOC) += intel/
obj-$(CONFIG_SND_SOC) += mediatek/
+obj-$(CONFIG_SND_SOC) += meson/
obj-$(CONFIG_SND_SOC) += mxs/
obj-$(CONFIG_SND_SOC) += nuc900/
obj-$(CONFIG_SND_SOC) += omap/
diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
new file mode 100644
index 0000000..216c850
--- /dev/null
+++ b/sound/soc/meson/Kconfig
@@ -0,0 +1,9 @@
+menuconfig SND_SOC_MESON
+ tristate "ASoC support for Amlogic Meson SoCs"
+ depends on ARCH_MESON || COMPILE_TEST
+ select MFD_CORE
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the Amlogic Meson SoCs Audio interfaces. You will also need to
+ select the audio interfaces to support below.
diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
new file mode 100644
index 0000000..22028ab
--- /dev/null
+++ b/sound/soc/meson/Makefile
@@ -0,0 +1,3 @@
+snd-soc-meson-audio-core-objs := audio-core.o
+
+obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o
diff --git a/sound/soc/meson/audio-core.c b/sound/soc/meson/audio-core.c
new file mode 100644
index 0000000..99993ec
--- /dev/null
+++ b/sound/soc/meson/audio-core.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 BayLibre, SAS
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * 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/clk.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include "audio-core.h"
+
+#define DRV_NAME "meson-audio-core"
+
+static const char * const acore_clock_names[] = { "aiu_top",
+ "aiu_glue",
+ "audin" };
+
+static int meson_acore_init_clocks(struct device *dev)
+{
+ struct clk *clock;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(acore_clock_names); i++) {
+ clock = devm_clk_get(dev, acore_clock_names[i]);
+ if (IS_ERR(clock)) {
+ if (PTR_ERR(clock) != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get %s clock\n",
+ acore_clock_names[i]);
+ return PTR_ERR(clock);
+ }
+
+ ret = clk_prepare_enable(clock);
+ if (ret) {
+ dev_err(dev, "Failed to enable %s clock\n",
+ acore_clock_names[i]);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev,
+ (void(*)(void *))clk_disable_unprepare,
+ clock);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const char * const acore_reset_names[] = { "aiu",
+ "audin" };
+
+static int meson_acore_init_resets(struct device *dev)
+{
+ struct reset_control *reset;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(acore_reset_names); i++) {
+ reset = devm_reset_control_get_exclusive(dev,
+ acore_reset_names[i]);
+ if (IS_ERR(reset)) {
+ if (PTR_ERR(reset) != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get %s reset\n",
+ acore_reset_names[i]);
+ return PTR_ERR(reset);
+ }
+
+ ret = reset_control_reset(reset);
+ if (ret) {
+ dev_err(dev, "Failed to pulse %s reset\n",
+ acore_reset_names[i]);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct regmap_config meson_acore_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static const struct mfd_cell meson_acore_devs[] = {
+ {
+ .name = "meson-i2s-dai",
+ .of_compatible = "amlogic,meson-i2s-dai",
+ },
+ {
+ .name = "meson-spdif-dai",
+ .of_compatible = "amlogic,meson-spdif-dai",
+ },
+ {
+ .name = "meson-aiu-i2s-dma",
+ .of_compatible = "amlogic,meson-aiu-i2s-dma",
+ },
+ {
+ .name = "meson-aiu-spdif-dma",
+ .of_compatible = "amlogic,meson-aiu-spdif-dma",
+ },
+};
+
+static int meson_acore_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct meson_audio_core_data *data;
+ struct resource *res;
+ void __iomem *regs;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, data);
+
+ ret = meson_acore_init_clocks(dev);
+ if (ret)
+ return ret;
+
+ ret = meson_acore_init_resets(dev);
+ if (ret)
+ return ret;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aiu");
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ data->aiu = devm_regmap_init_mmio(dev, regs,
+ &meson_acore_regmap_config);
+ if (IS_ERR(data->aiu)) {
+ dev_err(dev, "Couldn't create the AIU regmap\n");
+ return PTR_ERR(data->aiu);
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audin");
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ data->audin = devm_regmap_init_mmio(dev, regs,
+ &meson_acore_regmap_config);
+ if (IS_ERR(data->audin)) {
+ dev_err(dev, "Couldn't create the AUDIN regmap\n");
+ return PTR_ERR(data->audin);
+ }
+
+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, meson_acore_devs,
+ ARRAY_SIZE(meson_acore_devs), NULL, 0,
+ NULL);
+}
+
+static const struct of_device_id meson_acore_of_match[] = {
+ { .compatible = "amlogic,meson-audio-core", },
+ { .compatible = "amlogic,meson-gxbb-audio-core", },
+ { .compatible = "amlogic,meson-gxl-audio-core", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, meson_acore_of_match);
+
+static struct platform_driver meson_acore_pdrv = {
+ .probe = meson_acore_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = meson_acore_of_match,
+ },
+};
+module_platform_driver(meson_acore_pdrv);
+
+MODULE_DESCRIPTION("Meson Audio Core Driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/audio-core.h b/sound/soc/meson/audio-core.h
new file mode 100644
index 0000000..6e7a24c
--- /dev/null
+++ b/sound/soc/meson/audio-core.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 BayLibre, SAS
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MESON_AUDIO_CORE_H_
+#define _MESON_AUDIO_CORE_H_
+
+struct meson_audio_core_data {
+ struct regmap *aiu;
+ struct regmap *audin;
+};
+
+#endif /* _MESON_AUDIO_CORE_H_ */
--
1.9.1

View file

@ -0,0 +1,361 @@
From 45ed0fea98d308d6cae17be657dd01d5ee729d87 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 12:00:10 +0200
Subject: [PATCH 28/79] ASoC: meson: add register definitions
Add the register definition for the AIU and AUDIN blocks
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
sound/soc/meson/aiu-regs.h | 182 +++++++++++++++++++++++++++++++++++++++++++
sound/soc/meson/audin-regs.h | 148 +++++++++++++++++++++++++++++++++++
2 files changed, 330 insertions(+)
create mode 100644 sound/soc/meson/aiu-regs.h
create mode 100644 sound/soc/meson/audin-regs.h
diff --git a/sound/soc/meson/aiu-regs.h b/sound/soc/meson/aiu-regs.h
new file mode 100644
index 0000000..67391e6
--- /dev/null
+++ b/sound/soc/meson/aiu-regs.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2017 BayLibre, SAS
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _AIU_REGS_H_
+#define _AIU_REGS_H_
+
+#define AIU_958_BPF 0x000
+#define AIU_958_BRST 0x004
+#define AIU_958_LENGTH 0x008
+#define AIU_958_PADDSIZE 0x00C
+#define AIU_958_MISC 0x010
+#define AIU_958_FORCE_LEFT 0x014 /* Unknown */
+#define AIU_958_DISCARD_NUM 0x018
+#define AIU_958_DCU_FF_CTRL 0x01C
+#define AIU_958_CHSTAT_L0 0x020
+#define AIU_958_CHSTAT_L1 0x024
+#define AIU_958_CTRL 0x028
+#define AIU_958_RPT 0x02C
+#define AIU_I2S_MUTE_SWAP 0x030
+#define AIU_I2S_SOURCE_DESC 0x034
+#define AIU_I2S_MED_CTRL 0x038
+#define AIU_I2S_MED_THRESH 0x03C
+#define AIU_I2S_DAC_CFG 0x040
+#define AIU_I2S_SYNC 0x044 /* Unknown */
+#define AIU_I2S_MISC 0x048
+#define AIU_I2S_OUT_CFG 0x04C
+#define AIU_I2S_FF_CTRL 0x050 /* Unknown */
+#define AIU_RST_SOFT 0x054
+#define AIU_CLK_CTRL 0x058
+#define AIU_MIX_ADCCFG 0x05C
+#define AIU_MIX_CTRL 0x060
+#define AIU_CLK_CTRL_MORE 0x064
+#define AIU_958_POP 0x068
+#define AIU_MIX_GAIN 0x06C
+#define AIU_958_SYNWORD1 0x070
+#define AIU_958_SYNWORD2 0x074
+#define AIU_958_SYNWORD3 0x078
+#define AIU_958_SYNWORD1_MASK 0x07C
+#define AIU_958_SYNWORD2_MASK 0x080
+#define AIU_958_SYNWORD3_MASK 0x084
+#define AIU_958_FFRDOUT_THD 0x088
+#define AIU_958_LENGTH_PER_PAUSE 0x08C
+#define AIU_958_PAUSE_NUM 0x090
+#define AIU_958_PAUSE_PAYLOAD 0x094
+#define AIU_958_AUTO_PAUSE 0x098
+#define AIU_958_PAUSE_PD_LENGTH 0x09C
+#define AIU_CODEC_DAC_LRCLK_CTRL 0x0A0
+#define AIU_CODEC_ADC_LRCLK_CTRL 0x0A4
+#define AIU_HDMI_CLK_DATA_CTRL 0x0A8
+#define AIU_CODEC_CLK_DATA_CTRL 0x0AC
+#define AIU_ACODEC_CTRL 0x0B0
+#define AIU_958_CHSTAT_R0 0x0C0
+#define AIU_958_CHSTAT_R1 0x0C4
+#define AIU_958_VALID_CTRL 0x0C8
+#define AIU_AUDIO_AMP_REG0 0x0F0 /* Unknown */
+#define AIU_AUDIO_AMP_REG1 0x0F4 /* Unknown */
+#define AIU_AUDIO_AMP_REG2 0x0F8 /* Unknown */
+#define AIU_AUDIO_AMP_REG3 0x0FC /* Unknown */
+#define AIU_AIFIFO2_CTRL 0x100
+#define AIU_AIFIFO2_STATUS 0x104
+#define AIU_AIFIFO2_GBIT 0x108
+#define AIU_AIFIFO2_CLB 0x10C
+#define AIU_CRC_CTRL 0x110
+#define AIU_CRC_STATUS 0x114
+#define AIU_CRC_SHIFT_REG 0x118
+#define AIU_CRC_IREG 0x11C
+#define AIU_CRC_CAL_REG1 0x120
+#define AIU_CRC_CAL_REG0 0x124
+#define AIU_CRC_POLY_COEF1 0x128
+#define AIU_CRC_POLY_COEF0 0x12C
+#define AIU_CRC_BIT_SIZE1 0x130
+#define AIU_CRC_BIT_SIZE0 0x134
+#define AIU_CRC_BIT_CNT1 0x138
+#define AIU_CRC_BIT_CNT0 0x13C
+#define AIU_AMCLK_GATE_HI 0x140
+#define AIU_AMCLK_GATE_LO 0x144
+#define AIU_AMCLK_MSR 0x148
+#define AIU_AUDAC_CTRL0 0x14C /* Unknown */
+#define AIU_DELTA_SIGMA0 0x154 /* Unknown */
+#define AIU_DELTA_SIGMA1 0x158 /* Unknown */
+#define AIU_DELTA_SIGMA2 0x15C /* Unknown */
+#define AIU_DELTA_SIGMA3 0x160 /* Unknown */
+#define AIU_DELTA_SIGMA4 0x164 /* Unknown */
+#define AIU_DELTA_SIGMA5 0x168 /* Unknown */
+#define AIU_DELTA_SIGMA6 0x16C /* Unknown */
+#define AIU_DELTA_SIGMA7 0x170 /* Unknown */
+#define AIU_DELTA_SIGMA_LCNTS 0x174 /* Unknown */
+#define AIU_DELTA_SIGMA_RCNTS 0x178 /* Unknown */
+#define AIU_MEM_I2S_START_PTR 0x180
+#define AIU_MEM_I2S_RD_PTR 0x184
+#define AIU_MEM_I2S_END_PTR 0x188
+#define AIU_MEM_I2S_MASKS 0x18C
+#define AIU_MEM_I2S_CONTROL 0x190
+#define AIU_MEM_IEC958_START_PTR 0x194
+#define AIU_MEM_IEC958_RD_PTR 0x198
+#define AIU_MEM_IEC958_END_PTR 0x19C
+#define AIU_MEM_IEC958_MASKS 0x1A0
+#define AIU_MEM_IEC958_CONTROL 0x1A4
+#define AIU_MEM_AIFIFO2_START_PTR 0x1A8
+#define AIU_MEM_AIFIFO2_CURR_PTR 0x1AC
+#define AIU_MEM_AIFIFO2_END_PTR 0x1B0
+#define AIU_MEM_AIFIFO2_BYTES_AVAIL 0x1B4
+#define AIU_MEM_AIFIFO2_CONTROL 0x1B8
+#define AIU_MEM_AIFIFO2_MAN_WP 0x1BC
+#define AIU_MEM_AIFIFO2_MAN_RP 0x1C0
+#define AIU_MEM_AIFIFO2_LEVEL 0x1C4
+#define AIU_MEM_AIFIFO2_BUF_CNTL 0x1C8
+#define AIU_MEM_I2S_MAN_WP 0x1CC
+#define AIU_MEM_I2S_MAN_RP 0x1D0
+#define AIU_MEM_I2S_LEVEL 0x1D4
+#define AIU_MEM_I2S_BUF_CNTL 0x1D8
+#define AIU_MEM_I2S_BUF_WRAP_COUNT 0x1DC
+#define AIU_MEM_I2S_MEM_CTL 0x1E0
+#define AIU_MEM_IEC958_MEM_CTL 0x1E4
+#define AIU_MEM_IEC958_WRAP_COUNT 0x1E8
+#define AIU_MEM_IEC958_IRQ_LEVEL 0x1EC
+#define AIU_MEM_IEC958_MAN_WP 0x1F0
+#define AIU_MEM_IEC958_MAN_RP 0x1F4
+#define AIU_MEM_IEC958_LEVEL 0x1F8
+#define AIU_MEM_IEC958_BUF_CNTL 0x1FC
+#define AIU_AIFIFO_CTRL 0x200
+#define AIU_AIFIFO_STATUS 0x204
+#define AIU_AIFIFO_GBIT 0x208
+#define AIU_AIFIFO_CLB 0x20C
+#define AIU_MEM_AIFIFO_START_PTR 0x210
+#define AIU_MEM_AIFIFO_CURR_PTR 0x214
+#define AIU_MEM_AIFIFO_END_PTR 0x218
+#define AIU_MEM_AIFIFO_BYTES_AVAIL 0x21C
+#define AIU_MEM_AIFIFO_CONTROL 0x220
+#define AIU_MEM_AIFIFO_MAN_WP 0x224
+#define AIU_MEM_AIFIFO_MAN_RP 0x228
+#define AIU_MEM_AIFIFO_LEVEL 0x22C
+#define AIU_MEM_AIFIFO_BUF_CNTL 0x230
+#define AIU_MEM_AIFIFO_BUF_WRAP_COUNT 0x234
+#define AIU_MEM_AIFIFO2_BUF_WRAP_COUNT 0x238
+#define AIU_MEM_AIFIFO_MEM_CTL 0x23C
+#define AIFIFO_TIME_STAMP_CNTL 0x240
+#define AIFIFO_TIME_STAMP_SYNC_0 0x244
+#define AIFIFO_TIME_STAMP_SYNC_1 0x248
+#define AIFIFO_TIME_STAMP_0 0x24C
+#define AIFIFO_TIME_STAMP_1 0x250
+#define AIFIFO_TIME_STAMP_2 0x254
+#define AIFIFO_TIME_STAMP_3 0x258
+#define AIFIFO_TIME_STAMP_LENGTH 0x25C
+#define AIFIFO2_TIME_STAMP_CNTL 0x260
+#define AIFIFO2_TIME_STAMP_SYNC_0 0x264
+#define AIFIFO2_TIME_STAMP_SYNC_1 0x268
+#define AIFIFO2_TIME_STAMP_0 0x26C
+#define AIFIFO2_TIME_STAMP_1 0x270
+#define AIFIFO2_TIME_STAMP_2 0x274
+#define AIFIFO2_TIME_STAMP_3 0x278
+#define AIFIFO2_TIME_STAMP_LENGTH 0x27C
+#define IEC958_TIME_STAMP_CNTL 0x280
+#define IEC958_TIME_STAMP_SYNC_0 0x284
+#define IEC958_TIME_STAMP_SYNC_1 0x288
+#define IEC958_TIME_STAMP_0 0x28C
+#define IEC958_TIME_STAMP_1 0x290
+#define IEC958_TIME_STAMP_2 0x294
+#define IEC958_TIME_STAMP_3 0x298
+#define IEC958_TIME_STAMP_LENGTH 0x29C
+#define AIU_MEM_AIFIFO2_MEM_CTL 0x2A0
+#define AIU_I2S_CBUS_DDR_CNTL 0x2A4
+#define AIU_I2S_CBUS_DDR_WDATA 0x2A8
+#define AIU_I2S_CBUS_DDR_ADDR 0x2AC
+
+#endif /* _AIU_REGS_H_ */
diff --git a/sound/soc/meson/audin-regs.h b/sound/soc/meson/audin-regs.h
new file mode 100644
index 0000000..f224610
--- /dev/null
+++ b/sound/soc/meson/audin-regs.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2017 BayLibre, SAS
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _AUDIN_REGS_H_
+#define _AUDIN_REGS_H_
+
+/*
+ * Note :
+ * Datasheet issue page 196
+ * AUDIN_MUTE_VAL 0x35 => impossible: Already assigned to AUDIN_FIFO1_PTR
+ * AUDIN_FIFO1_PTR is more likely to be correct here since surrounding registers
+ * also deal with AUDIN_FIFO1
+ *
+ * Clarification needed from Amlogic
+ */
+
+#define AUDIN_SPDIF_MODE 0x000
+#define AUDIN_SPDIF_FS_CLK_RLTN 0x004
+#define AUDIN_SPDIF_CHNL_STS_A 0x008
+#define AUDIN_SPDIF_CHNL_STS_B 0x00C
+#define AUDIN_SPDIF_MISC 0x010
+#define AUDIN_SPDIF_NPCM_PCPD 0x014
+#define AUDIN_SPDIF_END 0x03C /* Unknown */
+#define AUDIN_I2SIN_CTRL 0x040
+#define AUDIN_SOURCE_SEL 0x044
+#define AUDIN_DECODE_FORMAT 0x048
+#define AUDIN_DECODE_CONTROL_STATUS 0x04C
+#define AUDIN_DECODE_CHANNEL_STATUS_A_0 0x050
+#define AUDIN_DECODE_CHANNEL_STATUS_A_1 0x054
+#define AUDIN_DECODE_CHANNEL_STATUS_A_2 0x058
+#define AUDIN_DECODE_CHANNEL_STATUS_A_3 0x05C
+#define AUDIN_DECODE_CHANNEL_STATUS_A_4 0x060
+#define AUDIN_DECODE_CHANNEL_STATUS_A_5 0x064
+#define AUDIN_FIFO0_START 0x080
+#define AUDIN_FIFO0_END 0x084
+#define AUDIN_FIFO0_PTR 0x088
+#define AUDIN_FIFO0_INTR 0x08C
+#define AUDIN_FIFO0_RDPTR 0x090
+#define AUDIN_FIFO0_CTRL 0x094
+#define AUDIN_FIFO0_CTRL1 0x098
+#define AUDIN_FIFO0_LVL0 0x09C
+#define AUDIN_FIFO0_LVL1 0x0A0
+#define AUDIN_FIFO0_LVL2 0x0A4
+#define AUDIN_FIFO0_REQID 0x0C0
+#define AUDIN_FIFO0_WRAP 0x0C4
+#define AUDIN_FIFO1_START 0x0CC
+#define AUDIN_FIFO1_END 0x0D0
+#define AUDIN_FIFO1_PTR 0x0D4
+#define AUDIN_FIFO1_INTR 0x0D8
+#define AUDIN_FIFO1_RDPTR 0x0DC
+#define AUDIN_FIFO1_CTRL 0x0E0
+#define AUDIN_FIFO1_CTRL1 0x0E4
+#define AUDIN_FIFO1_LVL0 0x100
+#define AUDIN_FIFO1_LVL1 0x104
+#define AUDIN_FIFO1_LVL2 0x108
+#define AUDIN_FIFO1_REQID 0x10C
+#define AUDIN_FIFO1_WRAP 0x110
+#define AUDIN_FIFO2_START 0x114
+#define AUDIN_FIFO2_END 0x118
+#define AUDIN_FIFO2_PTR 0x11C
+#define AUDIN_FIFO2_INTR 0x120
+#define AUDIN_FIFO2_RDPTR 0x124
+#define AUDIN_FIFO2_CTRL 0x128
+#define AUDIN_FIFO2_CTRL1 0x12C
+#define AUDIN_FIFO2_LVL0 0x130
+#define AUDIN_FIFO2_LVL1 0x134
+#define AUDIN_FIFO2_LVL2 0x138
+#define AUDIN_FIFO2_REQID 0x13C
+#define AUDIN_FIFO2_WRAP 0x140
+#define AUDIN_INT_CTRL 0x144
+#define AUDIN_FIFO_INT 0x148
+#define PCMIN_CTRL0 0x180
+#define PCMIN_CTRL1 0x184
+#define PCMIN1_CTRL0 0x188
+#define PCMIN1_CTRL1 0x18C
+#define PCMOUT_CTRL0 0x1C0
+#define PCMOUT_CTRL1 0x1C4
+#define PCMOUT_CTRL2 0x1C8
+#define PCMOUT_CTRL3 0x1CC
+#define PCMOUT1_CTRL0 0x1D0
+#define PCMOUT1_CTRL1 0x1D4
+#define PCMOUT1_CTRL2 0x1D8
+#define PCMOUT1_CTRL3 0x1DC
+#define AUDOUT_CTRL 0x200
+#define AUDOUT_CTRL1 0x204
+#define AUDOUT_BUF0_STA 0x208
+#define AUDOUT_BUF0_EDA 0x20C
+#define AUDOUT_BUF0_WPTR 0x210
+#define AUDOUT_BUF1_STA 0x214
+#define AUDOUT_BUF1_EDA 0x218
+#define AUDOUT_BUF1_WPTR 0x21C
+#define AUDOUT_FIFO_RPTR 0x220
+#define AUDOUT_INTR_PTR 0x224
+#define AUDOUT_FIFO_STS 0x228
+#define AUDOUT1_CTRL 0x240
+#define AUDOUT1_CTRL1 0x244
+#define AUDOUT1_BUF0_STA 0x248
+#define AUDOUT1_BUF0_EDA 0x24C
+#define AUDOUT1_BUF0_WPTR 0x250
+#define AUDOUT1_BUF1_STA 0x254
+#define AUDOUT1_BUF1_EDA 0x258
+#define AUDOUT1_BUF1_WPTR 0x25C
+#define AUDOUT1_FIFO_RPTR 0x260
+#define AUDOUT1_INTR_PTR 0x264
+#define AUDOUT1_FIFO_STS 0x268
+#define AUDIN_HDMI_MEAS_CTRL 0x280
+#define AUDIN_HDMI_MEAS_CYCLES_M1 0x284
+#define AUDIN_HDMI_MEAS_INTR_MASKN 0x288
+#define AUDIN_HDMI_MEAS_INTR_STAT 0x28C
+#define AUDIN_HDMI_REF_CYCLES_STAT_0 0x290
+#define AUDIN_HDMI_REF_CYCLES_STAT_1 0x294
+#define AUDIN_HDMIRX_AFIFO_STAT 0x298
+#define AUDIN_FIFO0_PIO_STS 0x2C0
+#define AUDIN_FIFO0_PIO_RDL 0x2C4
+#define AUDIN_FIFO0_PIO_RDH 0x2C8
+#define AUDIN_FIFO1_PIO_STS 0x2CC
+#define AUDIN_FIFO1_PIO_RDL 0x2D0
+#define AUDIN_FIFO1_PIO_RDH 0x2D4
+#define AUDIN_FIFO2_PIO_STS 0x2D8
+#define AUDIN_FIFO2_PIO_RDL 0x2DC
+#define AUDIN_FIFO2_PIO_RDH 0x2E0
+#define AUDOUT_FIFO_PIO_STS 0x2E4
+#define AUDOUT_FIFO_PIO_WRL 0x2E8
+#define AUDOUT_FIFO_PIO_WRH 0x2EC
+#define AUDOUT1_FIFO_PIO_STS 0x2F0 /* Unknown */
+#define AUDOUT1_FIFO_PIO_WRL 0x2F4 /* Unknown */
+#define AUDOUT1_FIFO_PIO_WRH 0x2F8 /* Unknown */
+#define AUD_RESAMPLE_CTRL0 0x2FC
+#define AUD_RESAMPLE_CTRL1 0x300
+#define AUD_RESAMPLE_STATUS 0x304
+
+#endif /* _AUDIN_REGS_H_ */
--
1.9.1

View file

@ -0,0 +1,417 @@
From 98fd2d06e53a52a033317864b317549e6f3e9245 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 12:14:40 +0200
Subject: [PATCH 29/79] ASoC: meson: add aiu i2s dma support
Add support for the i2s output dma which is part of the AIU block
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
sound/soc/meson/Kconfig | 7 +
sound/soc/meson/Makefile | 2 +
sound/soc/meson/aiu-i2s-dma.c | 367 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 376 insertions(+)
create mode 100644 sound/soc/meson/aiu-i2s-dma.c
diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
index 216c850..ad31a11 100644
--- a/sound/soc/meson/Kconfig
+++ b/sound/soc/meson/Kconfig
@@ -7,3 +7,10 @@ menuconfig SND_SOC_MESON
Say Y or M if you want to add support for codecs attached to
the Amlogic Meson SoCs Audio interfaces. You will also need to
select the audio interfaces to support below.
+
+config SND_SOC_MESON_I2S
+ tristate "Meson i2s interface"
+ depends on SND_SOC_MESON
+ help
+ Say Y or M if you want to add support for i2s dma driver for Amlogic
+ Meson SoCs.
diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
index 22028ab..273f275 100644
--- a/sound/soc/meson/Makefile
+++ b/sound/soc/meson/Makefile
@@ -1,3 +1,5 @@
snd-soc-meson-audio-core-objs := audio-core.o
+snd-soc-meson-aiu-i2s-dma-objs := aiu-i2s-dma.o
obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o
+obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-aiu-i2s-dma.o
diff --git a/sound/soc/meson/aiu-i2s-dma.c b/sound/soc/meson/aiu-i2s-dma.c
new file mode 100644
index 0000000..bab950d
--- /dev/null
+++ b/sound/soc/meson/aiu-i2s-dma.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2017 BayLibre, SAS
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * 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/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "aiu-regs.h"
+#include "audio-core.h"
+
+#define DRV_NAME "meson-aiu-i2s-dma"
+
+struct aiu_i2s_dma {
+ struct meson_audio_core_data *core;
+ struct clk *fast;
+ int irq;
+};
+
+#define AIU_MEM_I2S_BUF_CNTL_INIT BIT(0)
+#define AIU_MEM_I2S_CONTROL_INIT BIT(0)
+#define AIU_MEM_I2S_CONTROL_FILL_EN BIT(1)
+#define AIU_MEM_I2S_CONTROL_EMPTY_EN BIT(2)
+#define AIU_MEM_I2S_CONTROL_MODE_16BIT BIT(6)
+#define AIU_MEM_I2S_CONTROL_BUSY BIT(7)
+#define AIU_MEM_I2S_CONTROL_DATA_READY BIT(8)
+#define AIU_MEM_I2S_CONTROL_LEVEL_CNTL BIT(9)
+#define AIU_MEM_I2S_MASKS_IRQ_BLOCK_MASK GENMASK(31, 16)
+#define AIU_MEM_I2S_MASKS_IRQ_BLOCK(n) ((n) << 16)
+#define AIU_MEM_I2S_MASKS_CH_MEM_MASK GENMASK(15, 8)
+#define AIU_MEM_I2S_MASKS_CH_MEM(ch) ((ch) << 8)
+#define AIU_MEM_I2S_MASKS_CH_RD_MASK GENMASK(7, 0)
+#define AIU_MEM_I2S_MASKS_CH_RD(ch) ((ch) << 0)
+#define AIU_RST_SOFT_I2S_FAST_DOMAIN BIT(0)
+#define AIU_RST_SOFT_I2S_SLOW_DOMAIN BIT(1)
+
+/*
+ * The DMA works by i2s "blocks" (or DMA burst). The burst size and the memory
+ * layout expected depends on the mode of operation.
+ *
+ * - Normal mode: The channels are expected to be packed in 32 bytes groups
+ * interleaved the buffer. AIU_MEM_I2S_MASKS_CH_MEM is a bitfield representing
+ * the channels present in memory. AIU_MEM_I2S_MASKS_CH_MEM represents the
+ * channels read by the DMA. This is very flexible but the unsual memory layout
+ * makes it less easy to deal with. The burst size is 32 bytes times the number
+ * of channels read.
+ *
+ * - Split mode:
+ * Classical channel interleaved frame organisation. In this mode,
+ * AIU_MEM_I2S_MASKS_CH_MEM and AIU_MEM_I2S_MASKS_CH_MEM must be set to 0xff and
+ * the burst size is fixed to 256 bytes. The input can be either 2 or 8
+ * channels.
+ *
+ * The following driver implements the split mode.
+ */
+
+#define AIU_I2S_DMA_BURST 256
+
+static struct snd_pcm_hardware aiu_i2s_dma_hw = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+
+ /*
+ * TODO: The DMA can change the endianness, the msb position
+ * and deal with unsigned - support this later on
+ */
+
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 2,
+ .channels_max = 8,
+ .period_bytes_min = AIU_I2S_DMA_BURST,
+ .period_bytes_max = AIU_I2S_DMA_BURST * 65535,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+ .buffer_bytes_max = 1 * 1024 * 1024,
+ .fifo_size = 0,
+};
+
+static struct aiu_i2s_dma *aiu_i2s_dma_priv(struct snd_pcm_substream *s)
+{
+ struct snd_soc_pcm_runtime *rtd = s->private_data;
+
+ return snd_soc_platform_get_drvdata(rtd->platform);
+}
+
+static snd_pcm_uframes_t
+aiu_i2s_dma_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct aiu_i2s_dma *priv = aiu_i2s_dma_priv(substream);
+ unsigned int addr;
+ int ret;
+
+ ret = regmap_read(priv->core->aiu, AIU_MEM_I2S_RD_PTR,
+ &addr);
+ if (ret)
+ return 0;
+
+ return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr);
+}
+
+static void __dma_enable(struct aiu_i2s_dma *priv, bool enable)
+{
+ unsigned int en_mask = (AIU_MEM_I2S_CONTROL_FILL_EN |
+ AIU_MEM_I2S_CONTROL_EMPTY_EN);
+
+ regmap_update_bits(priv->core->aiu, AIU_MEM_I2S_CONTROL, en_mask,
+ enable ? en_mask : 0);
+
+}
+
+static int aiu_i2s_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct aiu_i2s_dma *priv = aiu_i2s_dma_priv(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ __dma_enable(priv, true);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ __dma_enable(priv, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void __dma_init_mem(struct aiu_i2s_dma *priv)
+{
+ regmap_update_bits(priv->core->aiu, AIU_MEM_I2S_CONTROL,
+ AIU_MEM_I2S_CONTROL_INIT,
+ AIU_MEM_I2S_CONTROL_INIT);
+ regmap_update_bits(priv->core->aiu, AIU_MEM_I2S_BUF_CNTL,
+ AIU_MEM_I2S_BUF_CNTL_INIT,
+ AIU_MEM_I2S_BUF_CNTL_INIT);
+
+ regmap_update_bits(priv->core->aiu, AIU_MEM_I2S_CONTROL,
+ AIU_MEM_I2S_CONTROL_INIT,
+ 0);
+ regmap_update_bits(priv->core->aiu, AIU_MEM_I2S_BUF_CNTL,
+ AIU_MEM_I2S_BUF_CNTL_INIT,
+ 0);
+}
+
+static int aiu_i2s_dma_prepare(struct snd_pcm_substream *substream)
+{
+ struct aiu_i2s_dma *priv = aiu_i2s_dma_priv(substream);
+
+ __dma_init_mem(priv);
+
+ return 0;
+}
+
+static int aiu_i2s_dma_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct aiu_i2s_dma *priv = aiu_i2s_dma_priv(substream);
+ int ret;
+ u32 burst_num, mem_ctl;
+ dma_addr_t end_ptr;
+
+ ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ if (ret < 0)
+ return ret;
+
+ /* Setup memory layout */
+ if (params_physical_width(params) == 16)
+ mem_ctl = AIU_MEM_I2S_CONTROL_MODE_16BIT;
+ else
+ mem_ctl = 0;
+
+ regmap_update_bits(priv->core->aiu, AIU_MEM_I2S_CONTROL,
+ AIU_MEM_I2S_CONTROL_MODE_16BIT,
+ mem_ctl);
+
+ /* Initialize memory pointers */
+ regmap_write(priv->core->aiu, AIU_MEM_I2S_START_PTR, runtime->dma_addr);
+ regmap_write(priv->core->aiu, AIU_MEM_I2S_RD_PTR, runtime->dma_addr);
+
+ /* The end pointer is the address of the last valid block */
+ end_ptr = runtime->dma_addr + runtime->dma_bytes - AIU_I2S_DMA_BURST;
+ regmap_write(priv->core->aiu, AIU_MEM_I2S_END_PTR, end_ptr);
+
+ /* Memory masks */
+ burst_num = params_period_bytes(params) / AIU_I2S_DMA_BURST;
+ regmap_write(priv->core->aiu, AIU_MEM_I2S_MASKS,
+ AIU_MEM_I2S_MASKS_CH_RD(0xff) |
+ AIU_MEM_I2S_MASKS_CH_MEM(0xff) |
+ AIU_MEM_I2S_MASKS_IRQ_BLOCK(burst_num));
+
+ return 0;
+}
+
+static int aiu_i2s_dma_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+
+static irqreturn_t aiu_i2s_dma_irq_block(int irq, void *dev_id)
+{
+ struct snd_pcm_substream *playback = dev_id;
+
+ snd_pcm_period_elapsed(playback);
+
+ return IRQ_HANDLED;
+}
+
+static int aiu_i2s_dma_open(struct snd_pcm_substream *substream)
+{
+ struct aiu_i2s_dma *priv = aiu_i2s_dma_priv(substream);
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, &aiu_i2s_dma_hw);
+
+ /*
+ * Make sure the buffer and period size are multiple of the DMA burst
+ * size
+ */
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ AIU_I2S_DMA_BURST);
+ if (ret)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ AIU_I2S_DMA_BURST);
+ if (ret)
+ return ret;
+
+ /* Request the I2S DDR irq */
+ ret = request_irq(priv->irq, aiu_i2s_dma_irq_block, 0,
+ DRV_NAME, substream);
+ if (ret)
+ return ret;
+
+ /* Power up the i2s fast domain - can't write the registers w/o it */
+ ret = clk_prepare_enable(priv->fast);
+ if (ret)
+ return ret;
+
+ /* Make sure the dma is initially disabled */
+ __dma_enable(priv, false);
+
+ return 0;
+}
+
+static int aiu_i2s_dma_close(struct snd_pcm_substream *substream)
+{
+ struct aiu_i2s_dma *priv = aiu_i2s_dma_priv(substream);
+
+ clk_disable_unprepare(priv->fast);
+ free_irq(priv->irq, substream);
+
+ return 0;
+}
+
+static const struct snd_pcm_ops aiu_i2s_dma_ops = {
+ .open = aiu_i2s_dma_open,
+ .close = aiu_i2s_dma_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = aiu_i2s_dma_hw_params,
+ .hw_free = aiu_i2s_dma_hw_free,
+ .prepare = aiu_i2s_dma_prepare,
+ .pointer = aiu_i2s_dma_pointer,
+ .trigger = aiu_i2s_dma_trigger,
+};
+
+static int aiu_i2s_dma_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ size_t size = aiu_i2s_dma_hw.buffer_bytes_max;
+
+ return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
+ SNDRV_DMA_TYPE_DEV,
+ card->dev, size, size);
+}
+
+struct snd_soc_platform_driver aiu_i2s_platform = {
+ .ops = &aiu_i2s_dma_ops,
+ .pcm_new = aiu_i2s_dma_new,
+};
+
+static int aiu_i2s_dma_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct aiu_i2s_dma *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+ priv->core = dev_get_drvdata(dev->parent);
+
+ priv->fast = devm_clk_get(dev, "fast");
+ if (IS_ERR(priv->fast)) {
+ if (PTR_ERR(priv->fast) != -EPROBE_DEFER)
+ dev_err(dev, "Can't get i2s fast domain clock\n");
+ return PTR_ERR(priv->fast);
+ }
+
+ priv->irq = platform_get_irq(pdev, 0);
+ if (priv->irq <= 0) {
+ dev_err(dev, "Can't get i2s ddr irq\n");
+ return priv->irq;
+ }
+
+ return snd_soc_register_platform(dev, &aiu_i2s_platform);
+}
+
+static const struct of_device_id aiu_i2s_dma_of_match[] = {
+ { .compatible = "amlogic,meson-aiu-i2s-dma", },
+ { .compatible = "amlogic,meson-gxbb-aiu-i2s-dma", },
+ { .compatible = "amlogic,meson-gxl-aiu-i2s-dma", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, aiu_i2s_dma_of_match);
+
+static struct platform_driver aiu_i2s_dma_pdrv = {
+ .probe = aiu_i2s_dma_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = aiu_i2s_dma_of_match,
+ },
+};
+module_platform_driver(aiu_i2s_dma_pdrv);
+
+MODULE_DESCRIPTION("Meson AIU i2s DMA ASoC Driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
--
1.9.1

View file

@ -0,0 +1,515 @@
From e976fda45f89573b89d118ded17112aae8a0f8f5 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 12:17:27 +0200
Subject: [PATCH 30/79] ASoC: meson: add initial i2s dai support
Add support for the i2s dai found on Amlogic Meson SoC family.
With this initial implementation, only playback is supported.
Capture will be part of furture work.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
sound/soc/meson/Kconfig | 2 +-
sound/soc/meson/Makefile | 2 +
sound/soc/meson/i2s-dai.c | 465 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 468 insertions(+), 1 deletion(-)
create mode 100644 sound/soc/meson/i2s-dai.c
diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
index ad31a11..604c9e2 100644
--- a/sound/soc/meson/Kconfig
+++ b/sound/soc/meson/Kconfig
@@ -12,5 +12,5 @@ config SND_SOC_MESON_I2S
tristate "Meson i2s interface"
depends on SND_SOC_MESON
help
- Say Y or M if you want to add support for i2s dma driver for Amlogic
+ Say Y or M if you want to add support for i2s driver for Amlogic
Meson SoCs.
diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
index 273f275..ea06dde 100644
--- a/sound/soc/meson/Makefile
+++ b/sound/soc/meson/Makefile
@@ -1,5 +1,7 @@
snd-soc-meson-audio-core-objs := audio-core.o
snd-soc-meson-aiu-i2s-dma-objs := aiu-i2s-dma.o
+snd-soc-meson-i2s-dai-objs := i2s-dai.o
obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o
obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-aiu-i2s-dma.o
+obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-i2s-dai.o
diff --git a/sound/soc/meson/i2s-dai.c b/sound/soc/meson/i2s-dai.c
new file mode 100644
index 0000000..1008af8
--- /dev/null
+++ b/sound/soc/meson/i2s-dai.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2017 BayLibre, SAS
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * 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/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "aiu-regs.h"
+#include "audio-core.h"
+
+#define DRV_NAME "meson-i2s-dai"
+
+struct meson_i2s_dai {
+ struct meson_audio_core_data *core;
+ struct clk *mclk;
+ struct clk *bclks;
+ struct clk *iface;
+ struct clk *fast;
+ bool bclks_idle;
+};
+
+#define AIU_CLK_CTRL_I2S_DIV_EN BIT(0)
+#define AIU_CLK_CTRL_I2S_DIV_MASK GENMASK(3, 2)
+#define AIU_CLK_CTRL_AOCLK_POLARITY_MASK BIT(6)
+#define AIU_CLK_CTRL_AOCLK_POLARITY_NORMAL (0 << 6)
+#define AIU_CLK_CTRL_AOCLK_POLARITY_INVERTED (1 << 6)
+#define AIU_CLK_CTRL_ALRCLK_POLARITY_MASK BIT(7)
+#define AIU_CLK_CTRL_ALRCLK_POLARITY_NORMAL (0 << 7)
+#define AIU_CLK_CTRL_ALRCLK_POLARITY_INVERTED (1 << 7)
+#define AIU_CLK_CTRL_ALRCLK_SKEW_MASK GENMASK(9, 8)
+#define AIU_CLK_CTRL_ALRCLK_LEFT_J (0 << 8)
+#define AIU_CLK_CTRL_ALRCLK_I2S (1 << 8)
+#define AIU_CLK_CTRL_ALRCLK_RIGHT_J (2 << 8)
+#define AIU_CLK_CTRL_MORE_I2S_DIV_MASK GENMASK(5, 0)
+#define AIU_CLK_CTRL_MORE_I2S_DIV(div) (((div) - 1) << 0)
+#define AIU_CODEC_DAC_LRCLK_CTRL_DIV_MASK GENMASK(11, 0)
+#define AIU_CODEC_DAC_LRCLK_CTRL_DIV(div) (((div) - 1) << 0)
+#define AIU_I2S_DAC_CFG_PAYLOAD_SIZE_MASK GENMASK(1, 0)
+#define AIU_I2S_DAC_CFG_AOCLK_32 (0 << 0)
+#define AIU_I2S_DAC_CFG_AOCLK_48 (2 << 0)
+#define AIU_I2S_DAC_CFG_AOCLK_64 (3 << 0)
+#define AIU_I2S_MISC_HOLD_EN BIT(2)
+#define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0)
+#define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5)
+#define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9)
+#define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11)
+
+static void __hold(struct meson_i2s_dai *priv, bool enable)
+{
+ regmap_update_bits(priv->core->aiu, AIU_I2S_MISC,
+ AIU_I2S_MISC_HOLD_EN,
+ enable ? AIU_I2S_MISC_HOLD_EN : 0);
+}
+
+static void __divider_enable(struct meson_i2s_dai *priv, bool enable)
+{
+ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL,
+ AIU_CLK_CTRL_I2S_DIV_EN,
+ enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0);
+}
+
+static void __playback_start(struct meson_i2s_dai *priv)
+{
+ __divider_enable(priv, true);
+ __hold(priv, false);
+}
+
+static void __playback_stop(struct meson_i2s_dai *priv, bool clk_force)
+{
+ __hold(priv, true);
+ /* Disable the bit clks if necessary */
+ if (clk_force || !priv->bclks_idle)
+ __divider_enable(priv, false);
+}
+
+static int meson_i2s_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct meson_i2s_dai *priv = snd_soc_dai_get_drvdata(dai);
+ bool clk_force_stop = false;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ __playback_start(priv);
+ return 0;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ clk_force_stop = true;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ __playback_stop(priv, clk_force_stop);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int __bclks_set_rate(struct meson_i2s_dai *priv, unsigned int srate,
+ unsigned int width)
+{
+ unsigned int fs;
+
+ /* Get the oversampling factor */
+ fs = DIV_ROUND_CLOSEST(clk_get_rate(priv->mclk), srate);
+
+ /*
+ * This DAI is usually connected to the dw-hdmi which does not support
+ * bclk being 32 * lrclk or 48 * lrclk
+ * Restrict to blck = 64 * lrclk
+ */
+ if (fs % 64)
+ return -EINVAL;
+
+ /* Set the divider between lrclk and bclk */
+ regmap_update_bits(priv->core->aiu, AIU_I2S_DAC_CFG,
+ AIU_I2S_DAC_CFG_PAYLOAD_SIZE_MASK,
+ AIU_I2S_DAC_CFG_AOCLK_64);
+
+ regmap_update_bits(priv->core->aiu, AIU_CODEC_DAC_LRCLK_CTRL,
+ AIU_CODEC_DAC_LRCLK_CTRL_DIV_MASK,
+ AIU_CODEC_DAC_LRCLK_CTRL_DIV(64));
+
+ /* Use CLK_MORE for the i2s divider */
+ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL,
+ AIU_CLK_CTRL_I2S_DIV_MASK,
+ 0);
+
+ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL_MORE,
+ AIU_CLK_CTRL_MORE_I2S_DIV_MASK,
+ AIU_CLK_CTRL_MORE_I2S_DIV(fs / 64));
+
+ return 0;
+}
+
+static int __setup_desc(struct meson_i2s_dai *priv, unsigned int width,
+ unsigned int channels)
+{
+ u32 desc = 0;
+
+ switch (width) {
+ case 24:
+ /*
+ * For some reason, 24 bits wide audio don't play well
+ * if the 32 bits mode is not set
+ */
+ desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT |
+ AIU_I2S_SOURCE_DESC_MODE_32BIT);
+ break;
+ case 16:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (channels) {
+ case 2: /* Nothing to do */
+ break;
+ case 8:
+ /* TODO: Still requires testing ... */
+ desc |= AIU_I2S_SOURCE_DESC_MODE_8CH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(priv->core->aiu, AIU_I2S_SOURCE_DESC,
+ AIU_I2S_SOURCE_DESC_MODE_8CH |
+ AIU_I2S_SOURCE_DESC_MODE_24BIT |
+ AIU_I2S_SOURCE_DESC_MODE_32BIT,
+ desc);
+
+ return 0;
+}
+
+static int meson_i2s_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct meson_i2s_dai *priv = snd_soc_dai_get_drvdata(dai);
+ unsigned int width = params_width(params);
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ int ret;
+
+ ret = __setup_desc(priv, width, channels);
+ if (ret) {
+ dev_err(dai->dev, "Unable set to set i2s description\n");
+ return ret;
+ }
+
+ ret = __bclks_set_rate(priv, rate, width);
+ if (ret) {
+ dev_err(dai->dev, "Unable set to the i2s clock rates\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int meson_i2s_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct meson_i2s_dai *priv = snd_soc_dai_get_drvdata(dai);
+ u32 val;
+
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+ return -EINVAL;
+
+ /* DAI output mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val = AIU_CLK_CTRL_ALRCLK_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = AIU_CLK_CTRL_ALRCLK_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = AIU_CLK_CTRL_ALRCLK_RIGHT_J;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL,
+ AIU_CLK_CTRL_ALRCLK_SKEW_MASK,
+ val);
+
+ /* DAI clock polarity */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ /* Invert both clocks */
+ val = AIU_CLK_CTRL_ALRCLK_POLARITY_INVERTED |
+ AIU_CLK_CTRL_AOCLK_POLARITY_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ /* Invert bit clock */
+ val = AIU_CLK_CTRL_ALRCLK_POLARITY_NORMAL |
+ AIU_CLK_CTRL_AOCLK_POLARITY_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ /* Invert frame clock */
+ val = AIU_CLK_CTRL_ALRCLK_POLARITY_INVERTED |
+ AIU_CLK_CTRL_AOCLK_POLARITY_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ /* Normal clocks */
+ val = AIU_CLK_CTRL_ALRCLK_POLARITY_NORMAL |
+ AIU_CLK_CTRL_AOCLK_POLARITY_NORMAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL,
+ AIU_CLK_CTRL_ALRCLK_POLARITY_MASK |
+ AIU_CLK_CTRL_AOCLK_POLARITY_MASK,
+ val);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
+ case SND_SOC_DAIFMT_CONT:
+ priv->bclks_idle = true;
+ break;
+ case SND_SOC_DAIFMT_GATED:
+ priv->bclks_idle = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int meson_i2s_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct meson_i2s_dai *priv = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ if (WARN_ON(clk_id != 0))
+ return -EINVAL;
+
+ if (dir == SND_SOC_CLOCK_IN)
+ return 0;
+
+ ret = clk_set_rate(priv->mclk, freq);
+ if (ret) {
+ dev_err(dai->dev, "Failed to set sysclk to %uHz", freq);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int meson_i2s_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct meson_i2s_dai *priv = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ /* Power up the i2s fast domain - can't write the registers w/o it */
+ ret = clk_prepare_enable(priv->fast);
+ if (ret)
+ goto out_clk_fast;
+
+ /* Make sure nothing gets out of the DAI yet */
+ __hold(priv, true);
+
+ /* I2S encoder needs the mixer interface gate */
+ ret = clk_prepare_enable(priv->iface);
+ if (ret)
+ goto out_clk_iface;
+
+ /* Enable the i2s master clock */
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret)
+ goto out_mclk;
+
+ /* Enable the bit clock gate */
+ ret = clk_prepare_enable(priv->bclks);
+ if (ret)
+ goto out_bclks;
+
+ /* Make sure the interface expect a memory layout we can work with */
+ regmap_update_bits(priv->core->aiu, AIU_I2S_SOURCE_DESC,
+ AIU_I2S_SOURCE_DESC_MODE_SPLIT,
+ AIU_I2S_SOURCE_DESC_MODE_SPLIT);
+
+ return 0;
+
+out_bclks:
+ clk_disable_unprepare(priv->mclk);
+out_mclk:
+ clk_disable_unprepare(priv->iface);
+out_clk_iface:
+ clk_disable_unprepare(priv->fast);
+out_clk_fast:
+ return ret;
+}
+
+static void meson_i2s_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct meson_i2s_dai *priv = snd_soc_dai_get_drvdata(dai);
+
+ clk_disable_unprepare(priv->bclks);
+ clk_disable_unprepare(priv->mclk);
+ clk_disable_unprepare(priv->iface);
+ clk_disable_unprepare(priv->fast);
+}
+
+static const struct snd_soc_dai_ops meson_i2s_dai_ops = {
+ .startup = meson_i2s_dai_startup,
+ .shutdown = meson_i2s_dai_shutdown,
+ .trigger = meson_i2s_dai_trigger,
+ .hw_params = meson_i2s_dai_hw_params,
+ .set_fmt = meson_i2s_dai_set_fmt,
+ .set_sysclk = meson_i2s_dai_set_sysclk,
+};
+
+static struct snd_soc_dai_driver meson_i2s_dai = {
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE)
+ },
+ .ops = &meson_i2s_dai_ops,
+};
+
+static const struct snd_soc_component_driver meson_i2s_dai_component = {
+ .name = DRV_NAME,
+};
+
+static int meson_i2s_dai_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct meson_i2s_dai *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+ priv->core = dev_get_drvdata(dev->parent);
+
+ priv->fast = devm_clk_get(dev, "fast");
+ if (IS_ERR(priv->fast)) {
+ if (PTR_ERR(priv->fast) != -EPROBE_DEFER)
+ dev_err(dev, "Can't get the i2s fast domain clock\n");
+ return PTR_ERR(priv->fast);
+ }
+
+ priv->iface = devm_clk_get(dev, "iface");
+ if (IS_ERR(priv->iface)) {
+ if (PTR_ERR(priv->iface) != -EPROBE_DEFER)
+ dev_err(dev, "Can't get i2s dai clock gate\n");
+ return PTR_ERR(priv->iface);
+ }
+
+ priv->bclks = devm_clk_get(dev, "bclks");
+ if (IS_ERR(priv->bclks)) {
+ if (PTR_ERR(priv->bclks) != -EPROBE_DEFER)
+ dev_err(dev, "Can't get bit clocks gate\n");
+ return PTR_ERR(priv->bclks);
+ }
+
+ priv->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(priv->mclk)) {
+ if (PTR_ERR(priv->mclk) != -EPROBE_DEFER)
+ dev_err(dev, "failed to get the i2s master clock\n");
+ return PTR_ERR(priv->mclk);
+ }
+
+ return devm_snd_soc_register_component(dev, &meson_i2s_dai_component,
+ &meson_i2s_dai, 1);
+}
+
+static const struct of_device_id meson_i2s_dai_of_match[] = {
+ { .compatible = "amlogic,meson-i2s-dai", },
+ { .compatible = "amlogic,meson-gxbb-i2s-dai", },
+ { .compatible = "amlogic,meson-gxl-i2s-dai", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, meson_i2s_dai_of_match);
+
+static struct platform_driver meson_i2s_dai_pdrv = {
+ .probe = meson_i2s_dai_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = meson_i2s_dai_of_match,
+ },
+};
+module_platform_driver(meson_i2s_dai_pdrv);
+
+MODULE_DESCRIPTION("Meson i2s DAI ASoC Driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
--
1.9.1

View file

@ -0,0 +1,439 @@
From e3ad81320cb0d2282f7a31bceab130bf4105a711 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 13:43:52 +0200
Subject: [PATCH 31/79] ASoC: meson: add aiu spdif dma support
Add support for the spdif output dma which is part of the AIU block
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
sound/soc/meson/Kconfig | 7 +
sound/soc/meson/Makefile | 2 +
sound/soc/meson/aiu-spdif-dma.c | 385 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 394 insertions(+)
create mode 100644 sound/soc/meson/aiu-spdif-dma.c
diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
index 604c9e2..8af3258 100644
--- a/sound/soc/meson/Kconfig
+++ b/sound/soc/meson/Kconfig
@@ -14,3 +14,10 @@ config SND_SOC_MESON_I2S
help
Say Y or M if you want to add support for i2s driver for Amlogic
Meson SoCs.
+
+config SND_SOC_MESON_SPDIF
+ tristate "Meson spdif interface"
+ depends on SND_SOC_MESON
+ help
+ Say Y or M if you want to add support for spdif dma driver for Amlogic
+ Meson SoCs.
diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
index ea06dde..cef9a9d 100644
--- a/sound/soc/meson/Makefile
+++ b/sound/soc/meson/Makefile
@@ -1,7 +1,9 @@
snd-soc-meson-audio-core-objs := audio-core.o
snd-soc-meson-aiu-i2s-dma-objs := aiu-i2s-dma.o
+snd-soc-meson-aiu-spdif-dma-objs := aiu-spdif-dma.o
snd-soc-meson-i2s-dai-objs := i2s-dai.o
obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o
obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-aiu-i2s-dma.o
obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-i2s-dai.o
+obj-$(CONFIG_SND_SOC_MESON_SPDIF) += snd-soc-meson-aiu-spdif-dma.o
diff --git a/sound/soc/meson/aiu-spdif-dma.c b/sound/soc/meson/aiu-spdif-dma.c
new file mode 100644
index 0000000..e3ff74b
--- /dev/null
+++ b/sound/soc/meson/aiu-spdif-dma.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2017 BayLibre, SAS
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * 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/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "aiu-regs.h"
+#include "audio-core.h"
+
+#define DRV_NAME "meson-aiu-spdif-dma"
+
+struct aiu_spdif_dma {
+ struct meson_audio_core_data *core;
+ struct clk *fast;
+ int irq;
+};
+
+#define AIU_958_DCU_FF_CTRL_EN BIT(0)
+#define AIU_958_DCU_FF_CTRL_AUTO_DISABLE BIT(1)
+#define AIU_958_DCU_FF_CTRL_IRQ_MODE_MASK GENMASK(3, 2)
+#define AIU_958_DCU_FF_CTRL_IRQ_OUT_THD BIT(2)
+#define AIU_958_DCU_FF_CTRL_IRQ_FRAME_READ BIT(3)
+#define AIU_958_DCU_FF_CTRL_SYNC_HEAD_EN BIT(4)
+#define AIU_958_DCU_FF_CTRL_BYTE_SEEK BIT(5)
+#define AIU_958_DCU_FF_CTRL_CONTINUE BIT(6)
+#define AIU_MEM_IEC958_BUF_CNTL_INIT BIT(0)
+#define AIU_MEM_IEC958_CONTROL_INIT BIT(0)
+#define AIU_MEM_IEC958_CONTROL_FILL_EN BIT(1)
+#define AIU_MEM_IEC958_CONTROL_EMPTY_EN BIT(2)
+#define AIU_MEM_IEC958_CONTROL_ENDIAN_MASK GENMASK(5, 3)
+#define AIU_MEM_IEC958_CONTROL_RD_DDR BIT(6)
+#define AIU_MEM_IEC958_CONTROL_MODE_16BIT BIT(7)
+#define AIU_MEM_IEC958_MASKS_CH_MEM_MASK GENMASK(15, 8)
+#define AIU_MEM_IEC958_MASKS_CH_MEM(ch) ((ch) << 8)
+#define AIU_MEM_IEC958_MASKS_CH_RD_MASK GENMASK(7, 0)
+#define AIU_MEM_IEC958_MASKS_CH_RD(ch) ((ch) << 0)
+
+#define AIU_SPDIF_DMA_BURST 8
+#define AIU_SPDIF_BPF_MAX USHRT_MAX
+
+static struct snd_pcm_hardware aiu_spdif_dma_hw = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+
+ .rates = (SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000),
+ /*
+ * TODO: The DMA can change the endianness, the msb position
+ * and deal with unsigned - support this later on
+ */
+
+ .channels_min = 2,
+ .channels_max = 2,
+ .period_bytes_min = AIU_SPDIF_DMA_BURST,
+ .period_bytes_max = AIU_SPDIF_BPF_MAX,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+ .buffer_bytes_max = 1 * 1024 * 1024,
+ .fifo_size = 0,
+};
+
+static struct aiu_spdif_dma *aiu_spdif_dma_priv(struct snd_pcm_substream *s)
+{
+ struct snd_soc_pcm_runtime *rtd = s->private_data;
+
+ return snd_soc_platform_get_drvdata(rtd->platform);
+}
+
+static snd_pcm_uframes_t
+aiu_spdif_dma_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct aiu_spdif_dma *priv = aiu_spdif_dma_priv(substream);
+ unsigned int addr;
+ int ret;
+
+ ret = regmap_read(priv->core->aiu, AIU_MEM_IEC958_RD_PTR,
+ &addr);
+ if (ret)
+ return 0;
+
+ return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr);
+}
+
+static void __dma_enable(struct aiu_spdif_dma *priv, bool enable)
+{
+ unsigned int en_mask = (AIU_MEM_IEC958_CONTROL_FILL_EN |
+ AIU_MEM_IEC958_CONTROL_EMPTY_EN);
+
+ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_CONTROL, en_mask,
+ enable ? en_mask : 0);
+}
+
+static void __dcu_fifo_enable(struct aiu_spdif_dma *priv, bool enable)
+{
+ regmap_update_bits(priv->core->aiu, AIU_958_DCU_FF_CTRL,
+ AIU_958_DCU_FF_CTRL_EN,
+ enable ? AIU_958_DCU_FF_CTRL_EN : 0);
+}
+
+static int aiu_spdif_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct aiu_spdif_dma *priv = aiu_spdif_dma_priv(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ __dcu_fifo_enable(priv, true);
+ __dma_enable(priv, true);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ __dma_enable(priv, false);
+ __dcu_fifo_enable(priv, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void __dma_init_mem(struct aiu_spdif_dma *priv)
+{
+ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_CONTROL,
+ AIU_MEM_IEC958_CONTROL_INIT,
+ AIU_MEM_IEC958_CONTROL_INIT);
+ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_BUF_CNTL,
+ AIU_MEM_IEC958_BUF_CNTL_INIT,
+ AIU_MEM_IEC958_BUF_CNTL_INIT);
+
+ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_CONTROL,
+ AIU_MEM_IEC958_CONTROL_INIT,
+ 0);
+ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_BUF_CNTL,
+ AIU_MEM_IEC958_BUF_CNTL_INIT,
+ 0);
+}
+
+static int aiu_spdif_dma_prepare(struct snd_pcm_substream *substream)
+{
+ struct aiu_spdif_dma *priv = aiu_spdif_dma_priv(substream);
+
+ __dma_init_mem(priv);
+
+ return 0;
+}
+
+static int __setup_memory_layout(struct aiu_spdif_dma *priv,
+ unsigned int width)
+{
+ u32 mem_ctl = AIU_MEM_IEC958_CONTROL_RD_DDR;
+
+ if (width == 16)
+ mem_ctl |= AIU_MEM_IEC958_CONTROL_MODE_16BIT;
+
+ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_CONTROL,
+ AIU_MEM_IEC958_CONTROL_ENDIAN_MASK |
+ AIU_MEM_IEC958_CONTROL_MODE_16BIT |
+ AIU_MEM_IEC958_CONTROL_RD_DDR,
+ mem_ctl);
+
+ return 0;
+}
+
+static int aiu_spdif_dma_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct aiu_spdif_dma *priv = aiu_spdif_dma_priv(substream);
+ int ret;
+ dma_addr_t end_ptr;
+
+ ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ if (ret < 0)
+ return ret;
+
+ ret = __setup_memory_layout(priv, params_physical_width(params));
+ if (ret)
+ return ret;
+
+ /* Initialize memory pointers */
+ regmap_write(priv->core->aiu,
+ AIU_MEM_IEC958_START_PTR, runtime->dma_addr);
+ regmap_write(priv->core->aiu,
+ AIU_MEM_IEC958_RD_PTR, runtime->dma_addr);
+
+ /* The end pointer is the address of the last valid block */
+ end_ptr = runtime->dma_addr + runtime->dma_bytes - AIU_SPDIF_DMA_BURST;
+ regmap_write(priv->core->aiu, AIU_MEM_IEC958_END_PTR, end_ptr);
+
+ /* Memory masks */
+ regmap_write(priv->core->aiu, AIU_MEM_IEC958_MASKS,
+ AIU_MEM_IEC958_MASKS_CH_RD(0xff) |
+ AIU_MEM_IEC958_MASKS_CH_MEM(0xff));
+
+ /* Setup the number bytes read by the FIFO between each IRQ */
+ regmap_write(priv->core->aiu, AIU_958_BPF, params_period_bytes(params));
+
+ /*
+ * AUTO_DISABLE and SYNC_HEAD are enabled by default but
+ * this should be disabled in PCM (uncompressed) mode
+ */
+ regmap_update_bits(priv->core->aiu, AIU_958_DCU_FF_CTRL,
+ AIU_958_DCU_FF_CTRL_AUTO_DISABLE |
+ AIU_958_DCU_FF_CTRL_IRQ_MODE_MASK |
+ AIU_958_DCU_FF_CTRL_SYNC_HEAD_EN,
+ AIU_958_DCU_FF_CTRL_IRQ_FRAME_READ);
+
+ return 0;
+}
+
+static int aiu_spdif_dma_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static irqreturn_t aiu_spdif_dma_irq(int irq, void *dev_id)
+{
+ struct snd_pcm_substream *playback = dev_id;
+
+ snd_pcm_period_elapsed(playback);
+
+ return IRQ_HANDLED;
+}
+
+static int aiu_spdif_dma_open(struct snd_pcm_substream *substream)
+{
+ struct aiu_spdif_dma *priv = aiu_spdif_dma_priv(substream);
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, &aiu_spdif_dma_hw);
+
+ /*
+ * Make sure the buffer and period size are multiple of the DMA burst
+ * size
+ */
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ AIU_SPDIF_DMA_BURST);
+ if (ret)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ AIU_SPDIF_DMA_BURST);
+ if (ret)
+ return ret;
+
+ /* Request the SPDIF DDR irq */
+ ret = request_irq(priv->irq, aiu_spdif_dma_irq, 0,
+ DRV_NAME, substream);
+ if (ret)
+ return ret;
+
+ /* Power up the spdif fast domain - can't write the register w/o it */
+ ret = clk_prepare_enable(priv->fast);
+ if (ret)
+ return ret;
+
+ /* Make sure the dma is initially halted */
+ __dma_enable(priv, false);
+ __dcu_fifo_enable(priv, false);
+
+ return 0;
+}
+
+static int aiu_spdif_dma_close(struct snd_pcm_substream *substream)
+{
+ struct aiu_spdif_dma *priv = aiu_spdif_dma_priv(substream);
+
+ clk_disable_unprepare(priv->fast);
+ free_irq(priv->irq, substream);
+
+ return 0;
+}
+
+static const struct snd_pcm_ops aiu_spdif_dma_ops = {
+ .open = aiu_spdif_dma_open,
+ .close = aiu_spdif_dma_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = aiu_spdif_dma_hw_params,
+ .hw_free = aiu_spdif_dma_hw_free,
+ .prepare = aiu_spdif_dma_prepare,
+ .pointer = aiu_spdif_dma_pointer,
+ .trigger = aiu_spdif_dma_trigger,
+};
+
+static int aiu_spdif_dma_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ size_t size = aiu_spdif_dma_hw.buffer_bytes_max;
+
+ return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
+ SNDRV_DMA_TYPE_DEV,
+ card->dev, size, size);
+}
+
+struct snd_soc_platform_driver aiu_spdif_platform = {
+ .ops = &aiu_spdif_dma_ops,
+ .pcm_new = aiu_spdif_dma_new,
+};
+
+static int aiu_spdif_dma_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct aiu_spdif_dma *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+ priv->core = dev_get_drvdata(dev->parent);
+
+ priv->fast = devm_clk_get(dev, "fast");
+ if (IS_ERR(priv->fast)) {
+ if (PTR_ERR(priv->fast) != -EPROBE_DEFER)
+ dev_err(dev, "Can't get spdif fast domain clock\n");
+ return PTR_ERR(priv->fast);
+ }
+
+ priv->irq = platform_get_irq(pdev, 0);
+ if (priv->irq <= 0) {
+ dev_err(dev, "Can't get spdif ddr irq\n");
+ return priv->irq;
+ }
+
+ return snd_soc_register_platform(dev, &aiu_spdif_platform);
+}
+
+static const struct of_device_id aiu_spdif_dma_of_match[] = {
+ { .compatible = "amlogic,meson-aiu-spdif-dma", },
+ { .compatible = "amlogic,meson-gxbb-aiu-spdif-dma", },
+ { .compatible = "amlogic,meson-gxl-aiu-spdif-dma", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, aiu_spdif_dma_of_match);
+
+static struct platform_driver aiu_spdif_dma_pdrv = {
+ .probe = aiu_spdif_dma_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = aiu_spdif_dma_of_match,
+ },
+};
+module_platform_driver(aiu_spdif_dma_pdrv);
+
+MODULE_DESCRIPTION("Meson AIU spdif DMA ASoC Driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
--
1.9.1

View file

@ -0,0 +1,430 @@
From f526e3ef0f6b52f591d93f2a68f25dd6062b592c Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 13:46:03 +0200
Subject: [PATCH 32/79] ASoC: meson: add initial spdif dai support
Add support for the spdif dai found on Amlogic Meson SoC family.
With this initial implementation, only uncompressed pcm playback
from the spdif dma is supported. Future work will add compressed
support, pcm playback from i2s dma and capture.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
sound/soc/meson/Kconfig | 3 +-
sound/soc/meson/Makefile | 2 +
sound/soc/meson/spdif-dai.c | 374 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 378 insertions(+), 1 deletion(-)
create mode 100644 sound/soc/meson/spdif-dai.c
diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
index 8af3258..92422e7 100644
--- a/sound/soc/meson/Kconfig
+++ b/sound/soc/meson/Kconfig
@@ -18,6 +18,7 @@ config SND_SOC_MESON_I2S
config SND_SOC_MESON_SPDIF
tristate "Meson spdif interface"
depends on SND_SOC_MESON
+ select SND_PCM_IEC958
help
- Say Y or M if you want to add support for spdif dma driver for Amlogic
+ Say Y or M if you want to add support for spdif driver for Amlogic
Meson SoCs.
diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
index cef9a9d..bc4391c 100644
--- a/sound/soc/meson/Makefile
+++ b/sound/soc/meson/Makefile
@@ -2,8 +2,10 @@ snd-soc-meson-audio-core-objs := audio-core.o
snd-soc-meson-aiu-i2s-dma-objs := aiu-i2s-dma.o
snd-soc-meson-aiu-spdif-dma-objs := aiu-spdif-dma.o
snd-soc-meson-i2s-dai-objs := i2s-dai.o
+snd-soc-meson-spdif-dai-objs := spdif-dai.o
obj-$(CONFIG_SND_SOC_MESON) += snd-soc-meson-audio-core.o
obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-aiu-i2s-dma.o
obj-$(CONFIG_SND_SOC_MESON_I2S) += snd-soc-meson-i2s-dai.o
obj-$(CONFIG_SND_SOC_MESON_SPDIF) += snd-soc-meson-aiu-spdif-dma.o
+obj-$(CONFIG_SND_SOC_MESON_SPDIF) += snd-soc-meson-spdif-dai.o
diff --git a/sound/soc/meson/spdif-dai.c b/sound/soc/meson/spdif-dai.c
new file mode 100644
index 0000000..e763000
--- /dev/null
+++ b/sound/soc/meson/spdif-dai.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2017 BayLibre, SAS
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * 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/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/pcm_iec958.h>
+
+#include "aiu-regs.h"
+#include "audio-core.h"
+
+#define DRV_NAME "meson-spdif-dai"
+
+struct meson_spdif_dai {
+ struct meson_audio_core_data *core;
+ struct clk *iface;
+ struct clk *fast;
+ struct clk *mclk_i958;
+ struct clk *mclk;
+};
+
+#define AIU_CLK_CTRL_958_DIV_EN BIT(1)
+#define AIU_CLK_CTRL_958_DIV_MASK GENMASK(5, 4)
+#define AIU_CLK_CTRL_958_DIV_MORE BIT(12)
+#define AIU_MEM_IEC958_CONTROL_MODE_LINEAR BIT(8)
+#define AIU_958_CTRL_HOLD_EN BIT(0)
+#define AIU_958_MISC_NON_PCM BIT(0)
+#define AIU_958_MISC_MODE_16BITS BIT(1)
+#define AIU_958_MISC_16BITS_ALIGN_MASK GENMASK(6, 5)
+#define AIU_958_MISC_16BITS_ALIGN(val) ((val) << 5)
+#define AIU_958_MISC_MODE_32BITS BIT(7)
+#define AIU_958_MISC_32BITS_SHIFT_MASK GENMASK(10, 8)
+#define AIU_958_MISC_32BITS_SHIFT(val) ((val) << 8)
+#define AIU_958_MISC_U_FROM_STREAM BIT(12)
+#define AIU_958_MISC_FORCE_LR BIT(13)
+
+#define AIU_CS_WORD_LEN 4
+
+static void __hold(struct meson_spdif_dai *priv, bool enable)
+{
+ regmap_update_bits(priv->core->aiu, AIU_958_CTRL,
+ AIU_958_CTRL_HOLD_EN,
+ enable ? AIU_958_CTRL_HOLD_EN : 0);
+}
+
+static void __divider_enable(struct meson_spdif_dai *priv, bool enable)
+{
+ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL,
+ AIU_CLK_CTRL_958_DIV_EN,
+ enable ? AIU_CLK_CTRL_958_DIV_EN : 0);
+}
+
+static void __playback_start(struct meson_spdif_dai *priv)
+{
+ __divider_enable(priv, true);
+ __hold(priv, false);
+}
+
+static void __playback_stop(struct meson_spdif_dai *priv)
+{
+ __hold(priv, true);
+ __divider_enable(priv, false);
+}
+
+static int meson_spdif_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct meson_spdif_dai *priv = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ __playback_start(priv);
+ return 0;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ __playback_stop(priv);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int __setup_spdif_clk(struct meson_spdif_dai *priv, unsigned int rate)
+{
+ unsigned int mrate;
+
+ /* Leave the internal divisor alone */
+ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL,
+ AIU_CLK_CTRL_958_DIV_MASK |
+ AIU_CLK_CTRL_958_DIV_MORE,
+ 0);
+
+ /* 2 * 32bits per subframe * 2 channels = 128 */
+ mrate = rate * 128;
+ return clk_set_rate(priv->mclk, mrate);
+}
+
+static int __setup_cs_word(struct meson_spdif_dai *priv,
+ struct snd_pcm_hw_params *params)
+{
+ u8 cs[AIU_CS_WORD_LEN];
+ u32 val;
+ int ret;
+
+ ret = snd_pcm_create_iec958_consumer_hw_params(params, cs,
+ AIU_CS_WORD_LEN);
+ if (ret < 0)
+ return -EINVAL;
+
+ /* Write the 1st half word */
+ val = cs[1] | cs[0] << 8;
+ regmap_write(priv->core->aiu, AIU_958_CHSTAT_L0, val);
+ regmap_write(priv->core->aiu, AIU_958_CHSTAT_R0, val);
+
+ /* Write the 2nd half word */
+ val = cs[3] | cs[2] << 8;
+ regmap_write(priv->core->aiu, AIU_958_CHSTAT_L1, val);
+ regmap_write(priv->core->aiu, AIU_958_CHSTAT_R1, val);
+
+ return 0;
+}
+
+static int __setup_pcm_fmt(struct meson_spdif_dai *priv,
+ unsigned int width)
+{
+ u32 val = 0;
+
+ switch (width) {
+ case 16:
+ val |= AIU_958_MISC_MODE_16BITS;
+ val |= AIU_958_MISC_16BITS_ALIGN(2);
+ break;
+ case 32:
+ case 24:
+ /*
+ * Looks like this should only be set for 32bits mode, but the
+ * vendor kernel sets it like this for 24bits as well, let's
+ * try and see
+ */
+ val |= AIU_958_MISC_MODE_32BITS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* No idea what this actually does, copying the vendor kernel for now */
+ val |= AIU_958_MISC_FORCE_LR;
+ val |= AIU_958_MISC_U_FROM_STREAM;
+
+ regmap_update_bits(priv->core->aiu, AIU_958_MISC,
+ AIU_958_MISC_NON_PCM |
+ AIU_958_MISC_MODE_16BITS |
+ AIU_958_MISC_16BITS_ALIGN_MASK |
+ AIU_958_MISC_MODE_32BITS |
+ AIU_958_MISC_FORCE_LR,
+ val);
+
+ return 0;
+}
+
+static int meson_spdif_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct meson_spdif_dai *priv = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = __setup_spdif_clk(priv, params_rate(params));
+ if (ret) {
+ dev_err(dai->dev, "Unable to set the spdif clock\n");
+ return ret;
+ }
+
+ ret = __setup_cs_word(priv, params);
+ if (ret) {
+ dev_err(dai->dev, "Unable to set the channel status word\n");
+ return ret;
+ }
+
+ ret = __setup_pcm_fmt(priv, params_width(params));
+ if (ret) {
+ dev_err(dai->dev, "Unable to set the pcm format\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int meson_spdif_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct meson_spdif_dai *priv = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ /* Power up the spdif fast domain - can't write the registers w/o it */
+ ret = clk_prepare_enable(priv->fast);
+ if (ret)
+ goto out_clk_fast;
+
+ /* Make sure nothing gets out of the DAI yet*/
+ __hold(priv, true);
+
+ ret = clk_set_parent(priv->mclk, priv->mclk_i958);
+ if (ret)
+ return ret;
+
+ /* Enable the clock gate */
+ ret = clk_prepare_enable(priv->iface);
+ if (ret)
+ goto out_clk_iface;
+
+ /* Enable the spdif clock */
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret)
+ goto out_mclk;
+
+ /*
+ * Make sure the interface expect a memory layout we can work with
+ * MEM prefixed register usually belong to the DMA, but when the spdif
+ * DAI takes data from the i2s buffer, we need to make sure it works in
+ * split mode and not the "normal mode" (channel samples packed in
+ * 32 bytes groups)
+ */
+ regmap_update_bits(priv->core->aiu, AIU_MEM_IEC958_CONTROL,
+ AIU_MEM_IEC958_CONTROL_MODE_LINEAR,
+ AIU_MEM_IEC958_CONTROL_MODE_LINEAR);
+
+ return 0;
+
+out_mclk:
+ clk_disable_unprepare(priv->iface);
+out_clk_iface:
+ clk_disable_unprepare(priv->fast);
+out_clk_fast:
+ return ret;
+}
+
+static void meson_spdif_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct meson_spdif_dai *priv = snd_soc_dai_get_drvdata(dai);
+
+ clk_disable_unprepare(priv->iface);
+ clk_disable_unprepare(priv->mclk);
+ clk_disable_unprepare(priv->fast);
+}
+
+static const struct snd_soc_dai_ops meson_spdif_dai_ops = {
+ .startup = meson_spdif_dai_startup,
+ .shutdown = meson_spdif_dai_shutdown,
+ .trigger = meson_spdif_dai_trigger,
+ .hw_params = meson_spdif_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver meson_spdif_dai = {
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = (SNDRV_PCM_RATE_32000 |
+ SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE)
+ },
+ .ops = &meson_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver meson_spdif_dai_component = {
+ .name = DRV_NAME,
+};
+
+static int meson_spdif_dai_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct meson_spdif_dai *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+ priv->core = dev_get_drvdata(dev->parent);
+
+ priv->fast = devm_clk_get(dev, "fast");
+ if (IS_ERR(priv->fast)) {
+ if (PTR_ERR(priv->fast) != -EPROBE_DEFER)
+ dev_err(dev, "Can't get spdif fast domain clockt\n");
+ return PTR_ERR(priv->fast);
+ }
+
+ priv->iface = devm_clk_get(dev, "iface");
+ if (IS_ERR(priv->iface)) {
+ if (PTR_ERR(priv->iface) != -EPROBE_DEFER)
+ dev_err(dev,
+ "Can't get the dai clock gate\n");
+ return PTR_ERR(priv->iface);
+ }
+
+ priv->mclk_i958 = devm_clk_get(dev, "mclk_i958");
+ if (IS_ERR(priv->mclk_i958)) {
+ if (PTR_ERR(priv->mclk_i958) != -EPROBE_DEFER)
+ dev_err(dev, "Can't get the spdif master clock\n");
+ return PTR_ERR(priv->mclk_i958);
+ }
+
+ /*
+ * TODO: the spdif dai can also get its data from the i2s fifo.
+ * For this use-case, the DAI driver will need to get the i2s master
+ * clock in order to reparent the spdif clock from cts_mclk_i958 to
+ * cts_amclk
+ */
+
+ priv->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(priv->mclk)) {
+ if (PTR_ERR(priv->mclk) != -EPROBE_DEFER)
+ dev_err(dev, "Can't get the spdif input mux clock\n");
+ return PTR_ERR(priv->mclk);
+ }
+
+ return devm_snd_soc_register_component(dev, &meson_spdif_dai_component,
+ &meson_spdif_dai, 1);
+}
+
+static const struct of_device_id meson_spdif_dai_of_match[] = {
+ { .compatible = "amlogic,meson-spdif-dai", },
+ { .compatible = "amlogic,meson-gxbb-spdif-dai", },
+ { .compatible = "amlogic,meson-gxl-spdif-dai", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, meson_spdif_dai_of_match);
+
+static struct platform_driver meson_spdif_dai_pdrv = {
+ .probe = meson_spdif_dai_probe,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = meson_spdif_dai_of_match,
+ },
+};
+module_platform_driver(meson_spdif_dai_pdrv);
+
+MODULE_DESCRIPTION("Meson spdif DAI ASoC Driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL v2");
--
1.9.1

View file

@ -0,0 +1,33 @@
From f0bd8f7b16c1d75d562e0731704e96efa6ea3966 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Fri, 31 Mar 2017 15:55:03 +0200
Subject: [PATCH 33/79] ARM64: defconfig: enable audio support for meson SoCs
as module
Add audio support for meson SoCs. This includes the audio core
driver and the i2s and spdif output interfaces
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/configs/defconfig | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 97c123e..cd4114f 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -365,6 +365,10 @@ CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_SOC=y
CONFIG_SND_BCM2835_SOC_I2S=m
+CONFIG_SND_SOC_MESON=m
+CONFIG_SND_SOC_MESON_I2S=m
+CONFIG_SND_SOC_MESON_SPDIF=m
+CONFIG_SND_SOC_RCAR=y
CONFIG_SND_SOC_SAMSUNG=y
CONFIG_SND_SOC_RCAR=y
CONFIG_SND_SOC_AK4613=y
--
1.9.1

View file

@ -0,0 +1,194 @@
From 76c374731322248f9e48241e0debdfed6d794a44 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 15:19:04 +0200
Subject: [PATCH 37/79] ARM64: dts: meson-gx: add audio controller nodes
Add audio controller nodes for Amlogic meson gxbb and gxl.
This includes the audio-core node, the i2s and spdif DAIs, i2s and spdif
aiu DMAs.
Audio on this SoC family is still a work in progress. More nodes are likely
to be added later on (pcm DAIs, input DMAs, etc ...)
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 35 ++++++++++++++++++++++++++
arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 38 +++++++++++++++++++++++++++++
arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 37 ++++++++++++++++++++++++++++
3 files changed, 110 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
index 8901eeb..2251881 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
@@ -224,6 +224,41 @@
#reset-cells = <1>;
};
+ audio: audio@5400 {
+ compatible = "amlogic,meson-audio-core";
+ reg = <0x0 0x5400 0x0 0x2ac>,
+ <0x0 0xa000 0x0 0x304>;
+ reg-names = "aiu", "audin";
+ status = "disabled";
+
+ aiu_i2s_dma: aiu_i2s_dma {
+ #sound-dai-cells = <0>;
+ compatible = "amlogic,meson-aiu-i2s-dma";
+ interrupts = <GIC_SPI 48 IRQ_TYPE_EDGE_RISING>;
+ status = "disabled";
+ };
+
+ aiu_spdif_dma: aiu_spdif_dma {
+ #sound-dai-cells = <0>;
+ compatible = "amlogic,meson-aiu-spdif-dma";
+ interrupts = <GIC_SPI 50 IRQ_TYPE_EDGE_RISING>;
+ status = "disabled";
+ };
+
+ i2s_dai: i2s_dai {
+ #sound-dai-cells = <0>;
+ compatible = "amlogic,meson-i2s-dai";
+ status = "disabled";
+ };
+
+ spdif_dai: spdif_dai {
+ #sound-dai-cells = <0>;
+ compatible = "amlogic,meson-spdif-dai";
+ status = "disabled";
+ };
+
+ };
+
uart_A: serial@84c0 {
compatible = "amlogic,meson-uart";
reg = <0x0 0x84c0 0x0 0x14>;
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
index 1d4c861..92d2b91 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
@@ -602,6 +602,27 @@
};
};
+&audio {
+ clocks = <&clkc CLKID_AIU>,
+ <&clkc CLKID_AIU_GLUE>,
+ <&clkc CLKID_I2S_SPDIF>;
+ clock-names = "aiu_top", "aiu_glue", "audin";
+ resets = <&reset RESET_AIU>,
+ <&reset RESET_AUDIN>;
+ reset-names = "aiu", "audin";
+};
+
+&aiu_i2s_dma {
+ clocks = <&clkc CLKID_I2S_OUT>;
+ clock-names = "fast";
+};
+
+&aiu_spdif_dma {
+ clocks = <&clkc CLKID_IEC958>;
+ clock-names = "fast";
+
+};
+
&i2c_A {
clocks = <&clkc CLKID_I2C>;
};
@@ -618,6 +639,14 @@
clocks = <&clkc CLKID_I2C>;
};
+&i2s_dai {
+ clocks = <&clkc CLKID_I2S_OUT>,
+ <&clkc CLKID_MIXER_IFACE>,
+ <&clkc CLKID_AOCLK_GATE>,
+ <&clkc CLKID_CTS_AMCLK>;
+ clock-names = "fast", "iface", "bclks", "mclk";
+};
+
&saradc {
compatible = "amlogic,meson-gxbb-saradc", "amlogic,meson-saradc";
clocks = <&xtal>,
@@ -649,6 +678,15 @@
clock-names = "core", "clkin0", "clkin1";
};
+&spdif_dai {
+ clocks = <&clkc CLKID_IEC958>,
+ <&clkc CLKID_IEC958_GATE>,
+ <&clkc CLKID_CTS_MCLK_I958>,
+ <&clkc CLKID_CTS_AMCLK>,
+ <&clkc CLKID_CTS_I958>;
+ clock-names = "fast", "iface", "mclk_i958", "mclk_i2s", "mclk";
+};
+
&spifc {
clocks = <&clkc CLKID_SPI>;
};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
index 6584e46..de0a26b 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
@@ -208,6 +208,26 @@
};
};
+&audio {
+ clocks = <&clkc CLKID_AIU>,
+ <&clkc CLKID_AIU_GLUE>,
+ <&clkc CLKID_I2S_SPDIF>;
+ clock-names = "aiu_top", "aiu_glue", "audin";
+ resets = <&reset RESET_AIU>,
+ <&reset RESET_AUDIN>;
+ reset-names = "aiu", "audin";
+};
+
+&aiu_i2s_dma {
+ clocks = <&clkc CLKID_I2S_OUT>;
+ clock-names = "fast";
+};
+
+&aiu_spdif_dma {
+ clocks = <&clkc CLKID_IEC958>;
+ clock-names = "fast";
+};
+
&periphs {
pinctrl_periphs: pinctrl@4b0 {
compatible = "amlogic,meson-gxl-periphs-pinctrl";
@@ -570,6 +590,14 @@
clocks = <&clkc CLKID_I2C>;
};
+&i2s_dai {
+ clocks = <&clkc CLKID_I2S_OUT>,
+ <&clkc CLKID_MIXER_IFACE>,
+ <&clkc CLKID_AOCLK_GATE>,
+ <&clkc CLKID_CTS_AMCLK>;
+ clock-names = "fast", "iface", "bclks", "mclk";
+};
+
&saradc {
compatible = "amlogic,meson-gxl-saradc", "amlogic,meson-saradc";
clocks = <&xtal>,
@@ -601,6 +629,15 @@
clock-names = "core", "clkin0", "clkin1";
};
+&spdif_dai {
+ clocks = <&clkc CLKID_IEC958>,
+ <&clkc CLKID_IEC958_GATE>,
+ <&clkc CLKID_CTS_MCLK_I958>,
+ <&clkc CLKID_CTS_AMCLK>,
+ <&clkc CLKID_CTS_I958>;
+ clock-names = "fast", "iface", "mclk_i958", "mclk_i2s", "mclk";
+};
+
&spifc {
clocks = <&clkc CLKID_SPI>;
};
--
1.9.1

View file

@ -0,0 +1,34 @@
From fb22697ac3dfd363e9d51307d1bb168bd6cf1c41 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 15:28:24 +0200
Subject: [PATCH 38/79] ARM64: dts: meson-gxbb-p20x: add i2s codec node
Add the node for the i2s codec Everest 7134 found on the P20x reference
design
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
index 3ed5c99..07b0f21 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
@@ -146,6 +146,12 @@
};
};
};
+
+ i2s_codec: external-codec {
+ #sound-dai-cells = <0>;
+ compatible = "everest,es7134";
+ status = "okay";
+ };
};
/* This UART is brought out to the DB9 connector */
--
1.9.1

View file

@ -0,0 +1,34 @@
From c834ddb8c5ca2957b7adb99a2a864f1892884e5e Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 15:32:17 +0200
Subject: [PATCH 39/79] ARM64: dts: meson-gxbb-p20x: add analog amplifier node
Add the node for the analog sound amplifier Dioo 2125 found on the p20x
reference design
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
index 07b0f21..b9fc07c 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
@@ -152,6 +152,12 @@
compatible = "everest,es7134";
status = "okay";
};
+
+ amp: analog-amplifier {
+ compatible = "dioo,dio2125";
+ enable-gpios = <&gpio GPIOH_3 0>;
+ status = "okay";
+ };
};
/* This UART is brought out to the DB9 connector */
--
1.9.1

View file

@ -0,0 +1,33 @@
From 6f0475d8b6b9792d1aee758a1674aa77a6c4232e Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 17:49:00 +0200
Subject: [PATCH 40/79] ARM64: dts: meson-gxbb-p20x: add spdif codec node
Add spdif-dit node to the meson gxbb reference design
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
index b9fc07c..c8559be 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
@@ -158,6 +158,12 @@
enable-gpios = <&gpio GPIOH_3 0>;
status = "okay";
};
+
+ spdif_out: spdif-out {
+ #sound-dai-cells = <0>;
+ compatible = "linux,spdif-dit";
+ status = "okay";
+ };
};
/* This UART is brought out to the DB9 connector */
--
1.9.1

View file

@ -0,0 +1,51 @@
From 9f335bad0a74dc58e5450260dbfaa59911dfaa31 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 15:40:20 +0200
Subject: [PATCH 41/79] ARM64: dts: meson-gxbb-p20x: add i2s and spdif output
interfaces
Add the necessary pins for the i2s and spdif audio interface output and
enable these interfaces
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi | 25 ++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
index c8559be..d36f82b0 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
@@ -292,3 +292,28 @@
remote-endpoint = <&hdmi_connector_in>;
};
};
+
+&audio {
+ status = "okay";
+};
+
+&aiu_i2s_dma {
+ status = "okay";
+};
+
+&aiu_spdif_dma {
+ status = "okay";
+};
+
+&i2s_dai {
+ pinctrl-0 = <&i2s_am_clk_pins>, <&i2s_out_ao_clk_pins>,
+ <&i2s_out_lr_clk_pins>, <&i2s_out_ch01_ao_pins>;
+ pinctrl-names = "default";
+ status = "okay";
+};
+
+&spdif_dai {
+ pinctrl-0 = <&spdif_out_ao_6_pins>;
+ pinctrl-names = "default";
+ status = "okay";
+};
--
1.9.1

View file

@ -0,0 +1,33 @@
From bb66443e46eab9d08082e98b1c53c4ed9699e8b4 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 17:53:57 +0200
Subject: [PATCH 42/79] ARM64: dts: meson-gxl-p230: add spdif codec node
Add spdif-dit node to the meson gxl reference design
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
index 49e19aa..3837560 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
@@ -84,6 +84,12 @@
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
};
+
+ spdif_out: spdif-out {
+ #sound-dai-cells = <0>;
+ compatible = "linux,spdif-dit";
+ status = "okay";
+ };
};
&cec_AO {
--
1.9.1

View file

@ -0,0 +1,38 @@
From 01ff36940e53cd410abec88ec2eb282c397a3a77 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 16:04:39 +0200
Subject: [PATCH 43/79] ARM64: dts: meson-gxl-p230: add spdif output interface
Add the necessary pin for the spdif audio interface output and enable it
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
index 3837560..770a332 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
@@ -130,3 +130,17 @@
status = "okay";
vref-supply = <&vddio_ao18>;
};
+
+&audio {
+ status = "okay";
+};
+
+&aiu_spdif_dma {
+ status = "okay";
+};
+
+&spdif_dai {
+ pinctrl-0 = <&spdif_out_h_pins>;
+ pinctrl-names = "default";
+ status = "okay";
+};
--
1.9.1

View file

@ -0,0 +1,84 @@
From bb5cf409583f04045ea97c7a039cb0935bbd6bfc Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 15:53:47 +0200
Subject: [PATCH 44/79] WIP: ARM64: dts: meson-gxbb-p20x: add sound card
support
This commit add sound card support to the meson-gxbb-p20x
This patch is not intended to be merged upstream the architecture of
the sound card has not been determined yet. To handle the routing caps
of meson SoCs, we will probably have to make our own card.
This implementation using simple-card is provided for testing purpose only
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi | 51 ++++++++++++++++++++++++
1 file changed, 51 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
index d36f82b0..88e3713 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
@@ -164,6 +164,57 @@
compatible = "linux,spdif-dit";
status = "okay";
};
+
+ soc {
+ sound {
+ compatible = "simple-audio-card";
+ simple-audio-card,name = "meson-gxbb-p20x";
+ simple-audio-card,aux-devs = <&amp>;
+ simple-audio-card,widgets =
+ "Line", "Analog Left Output",
+ "Line", "Analog Right Output";
+ simple-audio-card,routing =
+ "Analog Left Output", "OUTL",
+ "Analog Right Output", "OUTR",
+ "INL", "AOUTL",
+ "INR", "AOUTR";
+ status = "okay";
+
+ simple-audio-card,dai-link@0 {
+ /* RCA Output */
+ format = "i2s";
+ mclk-fs = <256>;
+ bitclock-master = <&i2s_dai>;
+ frame-master = <&i2s_dai>;
+
+ plat {
+ sound-dai = <&aiu_i2s_dma>;
+ };
+
+ cpu {
+ sound-dai = <&i2s_dai>;
+ };
+
+ codec {
+ sound-dai = <&i2s_codec>;
+ };
+ };
+
+ simple-audio-card,dai-link@1 {
+ plat {
+ sound-dai = <&aiu_spdif_dma>;
+ };
+
+ cpu {
+ sound-dai = <&spdif_dai>;
+ };
+
+ codec {
+ sound-dai = <&spdif_out>;
+ };
+ };
+ };
+ };
};
/* This UART is brought out to the DB9 connector */
--
1.9.1

View file

@ -0,0 +1,53 @@
From 24739afc3e8549f7361dd2ac7f8f5368f8fe0f79 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 30 Mar 2017 16:06:10 +0200
Subject: [PATCH 45/79] WIP: ARM64: dts: meson-gxl-p230: add sound card support
This commit add sound card support to the meson-gxbb-p230
his patch is not intended to be merged upstream. The architecture of
the sound card has not been determined yet. To handle the routing caps
of meson SoCs, we will probably have to make our own card.
This implementation using simple-card is provided for testing purpose only
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
.../arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
index 770a332..5da5206 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
@@ -90,6 +90,27 @@
compatible = "linux,spdif-dit";
status = "okay";
};
+
+ soc {
+ sound {
+ compatible = "simple-audio-card";
+ simple-audio-card,name = "meson-gxl-p230";
+
+ simple-audio-card,dai-link@0 {
+ plat {
+ sound-dai = <&aiu_spdif_dma>;
+ };
+
+ cpu {
+ sound-dai = <&spdif_dai>;
+ };
+
+ codec {
+ sound-dai = <&spdif_out>;
+ };
+ };
+ };
+ };
};
&cec_AO {
--
1.9.1

View file

@ -0,0 +1,120 @@
From 8fb6f1d768e64d2df3f4effdb90a953ba0080a76 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Mon, 20 Mar 2017 12:04:14 +0100
Subject: [PATCH 46/79] clk: take the prepare lock out of clk_core_set_parent
Rework set_parent core function so it can be called when the prepare lock
is already held by the caller.
This rework is done to ease the integration of the "protected" clock
functionality.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/clk/clk.c | 39 ++++++++++++++++++---------------------
1 file changed, 18 insertions(+), 21 deletions(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index fc58c52..f5c3715 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1787,7 +1787,8 @@ bool clk_has_parent(struct clk *clk, struct clk *parent)
}
EXPORT_SYMBOL_GPL(clk_has_parent);
-static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
+static int clk_core_set_parent_nolock(struct clk_core *core,
+ struct clk_core *parent)
{
int ret = 0;
int p_index = 0;
@@ -1796,23 +1797,16 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
if (!core)
return 0;
- /* prevent racing with updates to the clock topology */
- clk_prepare_lock();
-
if (core->parent == parent)
- goto out;
+ return 0;
/* verify ops for for multi-parent clks */
- if ((core->num_parents > 1) && (!core->ops->set_parent)) {
- ret = -ENOSYS;
- goto out;
- }
+ if ((core->num_parents > 1) && (!core->ops->set_parent))
+ return -ENOSYS;
/* check that we are allowed to re-parent if the clock is in use */
- if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) {
- ret = -EBUSY;
- goto out;
- }
+ if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count)
+ return -EBUSY;
/* try finding the new parent index */
if (parent) {
@@ -1820,8 +1814,7 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
if (p_index < 0) {
pr_debug("%s: clk %s can not be parent of clk %s\n",
__func__, parent->name, core->name);
- ret = p_index;
- goto out;
+ return p_index;
}
p_rate = parent->rate;
}
@@ -1831,7 +1824,7 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
/* abort if a driver objects */
if (ret & NOTIFY_STOP_MASK)
- goto out;
+ return ret;
/* do the re-parent */
ret = __clk_set_parent(core, parent, p_index);
@@ -1844,9 +1837,6 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
__clk_recalc_accuracies(core);
}
-out:
- clk_prepare_unlock();
-
return ret;
}
@@ -1869,10 +1859,17 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
*/
int clk_set_parent(struct clk *clk, struct clk *parent)
{
+ int ret;
+
if (!clk)
return 0;
- return clk_core_set_parent(clk->core, parent ? parent->core : NULL);
+ clk_prepare_lock();
+ ret = clk_core_set_parent_nolock(clk->core,
+ parent ? parent->core : NULL);
+ clk_prepare_unlock();
+
+ return ret;
}
EXPORT_SYMBOL_GPL(clk_set_parent);
@@ -2753,7 +2750,7 @@ void clk_unregister(struct clk *clk)
/* Reparent all children to the orphan list. */
hlist_for_each_entry_safe(child, t, &clk->core->children,
child_node)
- clk_core_set_parent(child, NULL);
+ clk_core_set_parent_nolock(child, NULL);
}
hlist_del_init(&clk->core->child_node);
--
1.9.1

View file

@ -0,0 +1,76 @@
From 12e852dedde5d635713d095df5e83cc3ac54c64a Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Mon, 20 Mar 2017 12:20:41 +0100
Subject: [PATCH 47/79] clk: add clk_core_set_phase_nolock function
Create a core function for set_phase, as it is done for set_rate and
set_parent.
This rework is done to ease the integration of "protected" clock
functionality.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/clk/clk.c | 31 +++++++++++++++++++------------
1 file changed, 19 insertions(+), 12 deletions(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index f5c3715..dceaf0f 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1873,6 +1873,23 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
}
EXPORT_SYMBOL_GPL(clk_set_parent);
+static int clk_core_set_phase_nolock(struct clk_core *core, int degrees)
+{
+ int ret = -EINVAL;
+
+ if (!core)
+ return 0;
+
+ trace_clk_set_phase(core, degrees);
+
+ if (core->ops->set_phase)
+ ret = core->ops->set_phase(core->hw, degrees);
+
+ trace_clk_set_phase_complete(core, degrees);
+
+ return ret;
+}
+
/**
* clk_set_phase - adjust the phase shift of a clock signal
* @clk: clock signal source
@@ -1895,7 +1912,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
*/
int clk_set_phase(struct clk *clk, int degrees)
{
- int ret = -EINVAL;
+ int ret;
if (!clk)
return 0;
@@ -1906,17 +1923,7 @@ int clk_set_phase(struct clk *clk, int degrees)
degrees += 360;
clk_prepare_lock();
-
- trace_clk_set_phase(clk->core, degrees);
-
- if (clk->core->ops->set_phase)
- ret = clk->core->ops->set_phase(clk->core->hw, degrees);
-
- trace_clk_set_phase_complete(clk->core, degrees);
-
- if (!ret)
- clk->core->phase = degrees;
-
+ ret = clk_core_set_phase_nolock(clk->core, degrees);
clk_prepare_unlock();
return ret;
--
1.9.1

View file

@ -0,0 +1,146 @@
From c87711484070e9fb733330662a19f7fc1de9b368 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Mon, 20 Mar 2017 14:35:47 +0100
Subject: [PATCH 48/79] clk: rework calls to round and determine rate callbacks
Rework the way the callbacks round_rate and determine_rate are called. The
goal is to do this at a single point and make it easier to add conditions
before calling them.
This rework is done to ease the integration of "protected" clock
functionality.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/clk/clk.c | 78 +++++++++++++++++++++++++++++++------------------------
1 file changed, 44 insertions(+), 34 deletions(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index dceaf0f..8cc4672 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -833,16 +833,34 @@ static int clk_disable_unused(void)
}
late_initcall_sync(clk_disable_unused);
-static int clk_core_round_rate_nolock(struct clk_core *core,
- struct clk_rate_request *req)
+static int clk_core_determine_round(struct clk_core *core,
+ struct clk_rate_request *req)
{
- struct clk_core *parent;
long rate;
- lockdep_assert_held(&prepare_lock);
+ if (core->ops->determine_rate) {
+ return core->ops->determine_rate(core->hw, req);
+ } else if (core->ops->round_rate) {
+ rate = core->ops->round_rate(core->hw, req->rate,
+ &req->best_parent_rate);
+ if (rate < 0)
+ return rate;
- if (!core)
- return 0;
+ req->rate = rate;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void clk_core_init_rate_req(struct clk_core *core,
+ struct clk_rate_request *req)
+{
+ struct clk_core *parent;
+
+ if (WARN_ON(!core || !req))
+ return;
parent = core->parent;
if (parent) {
@@ -852,22 +870,24 @@ static int clk_core_round_rate_nolock(struct clk_core *core,
req->best_parent_hw = NULL;
req->best_parent_rate = 0;
}
+}
- if (core->ops->determine_rate) {
- return core->ops->determine_rate(core->hw, req);
- } else if (core->ops->round_rate) {
- rate = core->ops->round_rate(core->hw, req->rate,
- &req->best_parent_rate);
- if (rate < 0)
- return rate;
+static int clk_core_round_rate_nolock(struct clk_core *core,
+ struct clk_rate_request *req)
+{
+ lockdep_assert_held(&prepare_lock);
- req->rate = rate;
- } else if (core->flags & CLK_SET_RATE_PARENT) {
- return clk_core_round_rate_nolock(parent, req);
- } else {
- req->rate = core->rate;
- }
+ if (!core)
+ return 0;
+
+ clk_core_init_rate_req(core, req);
+
+ if (core->ops->determine_rate || core->ops->round_rate)
+ return clk_core_determine_round(core, req);
+ else if (core->flags & CLK_SET_RATE_PARENT)
+ return clk_core_round_rate_nolock(core->parent, req);
+ req->rate = core->rate;
return 0;
}
@@ -1356,36 +1376,26 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core,
clk_core_get_boundaries(core, &min_rate, &max_rate);
/* find the closest rate and parent clk/rate */
- if (core->ops->determine_rate) {
+ if (core->ops->determine_rate || core->ops->round_rate) {
struct clk_rate_request req;
req.rate = rate;
req.min_rate = min_rate;
req.max_rate = max_rate;
- if (parent) {
- req.best_parent_hw = parent->hw;
- req.best_parent_rate = parent->rate;
- } else {
- req.best_parent_hw = NULL;
- req.best_parent_rate = 0;
- }
- ret = core->ops->determine_rate(core->hw, &req);
+ clk_core_init_rate_req(core, &req);
+
+ ret = clk_core_determine_round(core, &req);
if (ret < 0)
return NULL;
best_parent_rate = req.best_parent_rate;
new_rate = req.rate;
parent = req.best_parent_hw ? req.best_parent_hw->core : NULL;
- } else if (core->ops->round_rate) {
- ret = core->ops->round_rate(core->hw, rate,
- &best_parent_rate);
- if (ret < 0)
- return NULL;
- new_rate = ret;
if (new_rate < min_rate || new_rate > max_rate)
return NULL;
+
} else if (!parent || !(core->flags & CLK_SET_RATE_PARENT)) {
/* pass-through clock without adjustable parent */
core->new_rate = core->rate;
--
1.9.1

View file

@ -0,0 +1,77 @@
From 0e588289da294fb71f7873c2781758a7ba19dc7f Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Fri, 19 May 2017 11:44:22 +0200
Subject: [PATCH 49/79] clk: use round rate to bail out early in set_rate
The current implementation of clk_core_set_rate_nolock bails out early if
the requested rate is exactly the same as the one set. It should bail out
if the request would not result in rate a change. This important when rate
is not exactly what is requested, which is fairly common with PLLs.
Ex: provider able to give any rate with steps of 100Hz
- 1st consumer request 48000Hz and gets it.
- 2nd consumer request 48010Hz as well. If we were to perform the usual
mechanism, we would get 48000Hz as well. The clock would not change so
there is no point performing any checks to make sure the clock can change,
we know it won't.
This is important to prepare the addition of the clock protection mechanism
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/clk/clk.c | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 8cc4672..163cb98 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1570,15 +1570,34 @@ static void clk_change_rate(struct clk_core *core)
clk_change_rate(core->new_child);
}
+static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core,
+ unsigned long req_rate)
+{
+ int ret;
+ struct clk_rate_request req;
+
+ if (!core)
+ return 0;
+
+ clk_core_get_boundaries(core, &req.min_rate, &req.max_rate);
+ req.rate = req_rate;
+
+ ret = clk_core_round_rate_nolock(core, &req);
+
+ return ret ? 0 : req.rate;
+}
+
static int clk_core_set_rate_nolock(struct clk_core *core,
unsigned long req_rate)
{
struct clk_core *top, *fail_clk;
- unsigned long rate = req_rate;
+ unsigned long rate;
if (!core)
return 0;
+ rate = clk_core_req_round_rate_nolock(core, req_rate);
+
/* bail early if nothing to do */
if (rate == clk_core_get_rate_nolock(core))
return 0;
@@ -1587,7 +1606,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
return -EBUSY;
/* calculate new rates and get the topmost changed clock */
- top = clk_calc_new_rates(core, rate);
+ top = clk_calc_new_rates(core, req_rate);
if (!top)
return -EINVAL;
--
1.9.1

View file

@ -0,0 +1,462 @@
From ae5671893c76164ddbc7b9b0f6ea4361a121090f Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Mon, 20 Mar 2017 16:48:17 +0100
Subject: [PATCH 50/79] clk: add support for clock protection
The patch adds clk_protect and clk_unprotect to the CCF API. These
functions allow a consumer to inform the system that the rate of clock is
critical to for its operations and it can't tolerate other consumers
changing the rate or introducing glitches while the clock is protected.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/clk/clk.c | 205 +++++++++++++++++++++++++++++++++++++++++--
include/linux/clk-provider.h | 1 +
include/linux/clk.h | 29 ++++++
3 files changed, 229 insertions(+), 6 deletions(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 163cb98..d688b8f 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -60,6 +60,7 @@ struct clk_core {
bool orphan;
unsigned int enable_count;
unsigned int prepare_count;
+ unsigned int protect_count;
unsigned long min_rate;
unsigned long max_rate;
unsigned long accuracy;
@@ -84,6 +85,7 @@ struct clk {
const char *con_id;
unsigned long min_rate;
unsigned long max_rate;
+ unsigned long protect_count;
struct hlist_node clks_node;
};
@@ -148,6 +150,11 @@ static void clk_enable_unlock(unsigned long flags)
spin_unlock_irqrestore(&enable_lock, flags);
}
+static bool clk_core_rate_is_protected(struct clk_core *core)
+{
+ return core->protect_count;
+}
+
static bool clk_core_is_prepared(struct clk_core *core)
{
/*
@@ -328,6 +335,11 @@ bool clk_hw_is_prepared(const struct clk_hw *hw)
return clk_core_is_prepared(hw->core);
}
+bool clk_hw_rate_is_protected(const struct clk_hw *hw)
+{
+ return clk_core_rate_is_protected(hw->core);
+}
+
bool clk_hw_is_enabled(const struct clk_hw *hw)
{
return clk_core_is_enabled(hw->core);
@@ -466,6 +478,102 @@ int __clk_mux_determine_rate_closest(struct clk_hw *hw,
/*** clk api ***/
+static void clk_core_rate_unprotect(struct clk_core *core)
+{
+ lockdep_assert_held(&prepare_lock);
+
+ if (!core)
+ return;
+
+ if (WARN_ON(core->protect_count == 0))
+ return;
+
+ if (--core->protect_count > 0)
+ return;
+
+ clk_core_rate_unprotect(core->parent);
+}
+
+/**
+ * clk_rate_unprotect - unprotect the rate of a clock source
+ * @clk: the clk being unprotected
+ *
+ * clk_unprotect completes a critical section during which the clock
+ * consumer cannot tolerate any change to the clock rate. If no other clock
+ * consumers have protected clocks in the parent chain, then calls to this
+ * function will allow the clocks in the parent chain to change rates
+ * freely.
+ *
+ * Unlike the clk_set_rate_range method, which allows the rate to change
+ * within a given range, protected clocks cannot have their rate changed,
+ * either directly or indirectly due to changes further up the parent chain
+ * of clocks.
+ *
+ * Calls to clk_unprotect must be balanced with calls to clk_protect. Calls
+ * to this function may sleep, and do not return error status.
+ */
+void clk_rate_unprotect(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ clk_prepare_lock();
+
+ /*
+ * if there is something wrong with this consumer protect count, stop
+ * here before messing with the provider
+ */
+ if (WARN_ON(clk->protect_count <= 0))
+ goto out;
+
+ clk_core_rate_unprotect(clk->core);
+ clk->protect_count--;
+out:
+ clk_prepare_unlock();
+}
+EXPORT_SYMBOL_GPL(clk_rate_unprotect);
+
+static void clk_core_rate_protect(struct clk_core *core)
+{
+ lockdep_assert_held(&prepare_lock);
+
+ if (!core)
+ return;
+
+ if (core->protect_count == 0)
+ clk_core_rate_protect(core->parent);
+
+ core->protect_count++;
+}
+
+/**
+ * clk_rate_protect - protect a clock source
+ * @clk: the clk being protected
+ *
+ * clk_protect begins a critical section during which the clock consumer
+ * cannot tolerate any change to the clock rate. This results in all clocks
+ * up the parent chain to also be rate-protected.
+ *
+ * Unlike the clk_set_rate_range method, which allows the rate to change
+ * within a given range, protected clocks cannot have their rate changed,
+ * either directly or indirectly due to changes further up the parent chain
+ * of clocks.
+ *
+ * Calls to clk_protect should be balanced with calls to clk_unprotect.
+ * Calls to this function may sleep, and do not return error status.
+ */
+void clk_rate_protect(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ clk_prepare_lock();
+ clk_core_rate_protect(clk->core);
+ clk->protect_count++;
+ clk_prepare_unlock();
+}
+EXPORT_SYMBOL_GPL(clk_rate_protect);
+
static void clk_core_unprepare(struct clk_core *core)
{
lockdep_assert_held(&prepare_lock);
@@ -838,7 +946,15 @@ static int clk_core_determine_round(struct clk_core *core,
{
long rate;
- if (core->ops->determine_rate) {
+ /*
+ * At this point, core protection will be disabled if
+ * - if the provider is not protected at all
+ * - if the calling consumer is the only one protecting the
+ * provider (and only once)
+ */
+ if (clk_core_rate_is_protected(core)) {
+ req->rate = core->rate;
+ } else if (core->ops->determine_rate) {
return core->ops->determine_rate(core->hw, req);
} else if (core->ops->round_rate) {
rate = core->ops->round_rate(core->hw, req->rate,
@@ -944,10 +1060,17 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
clk_prepare_lock();
+ if (clk->protect_count)
+ clk_core_rate_unprotect(clk->core);
+
clk_core_get_boundaries(clk->core, &req.min_rate, &req.max_rate);
req.rate = rate;
ret = clk_core_round_rate_nolock(clk->core, &req);
+
+ if (clk->protect_count)
+ clk_core_rate_protect(clk->core);
+
clk_prepare_unlock();
if (ret)
@@ -1575,15 +1698,24 @@ static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core,
{
int ret;
struct clk_rate_request req;
+ unsigned int cnt = core->protect_count;
if (!core)
return 0;
+ /* simulate what the rate would be if it could be freely set */
+ while (core->protect_count)
+ clk_core_rate_unprotect(core);
+
clk_core_get_boundaries(core, &req.min_rate, &req.max_rate);
req.rate = req_rate;
ret = clk_core_round_rate_nolock(core, &req);
+ /* restore the protection */
+ while (core->protect_count < cnt)
+ clk_core_rate_protect(core);
+
return ret ? 0 : req.rate;
}
@@ -1602,6 +1734,10 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
if (rate == clk_core_get_rate_nolock(core))
return 0;
+ /* fail on a direct rate set of a protected provider */
+ if (clk_core_rate_is_protected(core))
+ return -EBUSY;
+
if ((core->flags & CLK_SET_RATE_GATE) && core->prepare_count)
return -EBUSY;
@@ -1658,8 +1794,14 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
/* prevent racing with updates to the clock topology */
clk_prepare_lock();
+ if (clk->protect_count)
+ clk_core_rate_unprotect(clk->core);
+
ret = clk_core_set_rate_nolock(clk->core, rate);
+ if (clk->protect_count)
+ clk_core_rate_protect(clk->core);
+
clk_prepare_unlock();
return ret;
@@ -1690,12 +1832,18 @@ int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
clk_prepare_lock();
+ if (clk->protect_count)
+ clk_core_rate_unprotect(clk->core);
+
if (min != clk->min_rate || max != clk->max_rate) {
clk->min_rate = min;
clk->max_rate = max;
ret = clk_core_set_rate_nolock(clk->core, clk->core->req_rate);
}
+ if (clk->protect_count)
+ clk_core_rate_protect(clk->core);
+
clk_prepare_unlock();
return ret;
@@ -1837,6 +1985,9 @@ static int clk_core_set_parent_nolock(struct clk_core *core,
if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count)
return -EBUSY;
+ if (clk_core_rate_is_protected(core))
+ return -EBUSY;
+
/* try finding the new parent index */
if (parent) {
p_index = clk_fetch_parent_index(core, parent);
@@ -1894,8 +2045,16 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
return 0;
clk_prepare_lock();
+
+ if (clk->protect_count)
+ clk_core_rate_unprotect(clk->core);
+
ret = clk_core_set_parent_nolock(clk->core,
parent ? parent->core : NULL);
+
+ if (clk->protect_count)
+ clk_core_rate_protect(clk->core);
+
clk_prepare_unlock();
return ret;
@@ -1909,6 +2068,9 @@ static int clk_core_set_phase_nolock(struct clk_core *core, int degrees)
if (!core)
return 0;
+ if (clk_core_rate_is_protected(core))
+ return -EBUSY;
+
trace_clk_set_phase(core, degrees);
if (core->ops->set_phase)
@@ -1952,7 +2114,15 @@ int clk_set_phase(struct clk *clk, int degrees)
degrees += 360;
clk_prepare_lock();
+
+ if (clk->protect_count)
+ clk_core_rate_unprotect(clk->core);
+
ret = clk_core_set_phase_nolock(clk->core, degrees);
+
+ if (clk->protect_count)
+ clk_core_rate_protect(clk->core);
+
clk_prepare_unlock();
return ret;
@@ -2039,11 +2209,12 @@ static void clk_summary_show_one(struct seq_file *s, struct clk_core *c,
if (!c)
return;
- seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n",
+ seq_printf(s, "%*s%-*s %11d %12d %12d %11lu %10lu %-3d\n",
level * 3 + 1, "",
30 - level * 3, c->name,
- c->enable_count, c->prepare_count, clk_core_get_rate(c),
- clk_core_get_accuracy(c), clk_core_get_phase(c));
+ c->enable_count, c->prepare_count, c->protect_count,
+ clk_core_get_rate(c), clk_core_get_accuracy(c),
+ clk_core_get_phase(c));
}
static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c,
@@ -2065,8 +2236,8 @@ static int clk_summary_show(struct seq_file *s, void *data)
struct clk_core *c;
struct hlist_head **lists = (struct hlist_head **)s->private;
- seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n");
- seq_puts(s, "----------------------------------------------------------------------------------------\n");
+ seq_puts(s, " clock enable_cnt prepare_cnt protect_cnt rate accuracy phase\n");
+ seq_puts(s, "----------------------------------------------------------------------------------------------------\n");
clk_prepare_lock();
@@ -2101,6 +2272,7 @@ static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level)
seq_printf(s, "\"%s\": { ", c->name);
seq_printf(s, "\"enable_count\": %d,", c->enable_count);
seq_printf(s, "\"prepare_count\": %d,", c->prepare_count);
+ seq_printf(s, "\"protect_count\": %d,", c->protect_count);
seq_printf(s, "\"rate\": %lu,", clk_core_get_rate(c));
seq_printf(s, "\"accuracy\": %lu,", clk_core_get_accuracy(c));
seq_printf(s, "\"phase\": %d", clk_core_get_phase(c));
@@ -2231,6 +2403,11 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)
if (!d)
goto err_out;
+ d = debugfs_create_u32("clk_protect_count", S_IRUGO, core->dentry,
+ (u32 *)&core->protect_count);
+ if (!d)
+ goto err_out;
+
d = debugfs_create_u32("clk_notifier_count", S_IRUGO, core->dentry,
(u32 *)&core->notifier_count);
if (!d)
@@ -2794,6 +2971,11 @@ void clk_unregister(struct clk *clk)
if (clk->core->prepare_count)
pr_warn("%s: unregistering prepared clock: %s\n",
__func__, clk->core->name);
+
+ if (clk->core->protect_count)
+ pr_warn("%s: unregistering protected clock: %s\n",
+ __func__, clk->core->name);
+
kref_put(&clk->core->ref, __clk_release);
unlock:
clk_prepare_unlock();
@@ -2952,6 +3134,17 @@ void __clk_put(struct clk *clk)
clk_prepare_lock();
+ /*
+ * Before calling clk_put, all calls to clk_rate_protect from a given
+ * user must be balanced with calls to clk_rate_unprotect and by that
+ * same user
+ */
+ WARN_ON(clk->protect_count);
+
+ /* We voiced our concern, let's sanitize the situation */
+ for (; clk->protect_count; clk->protect_count--)
+ clk_core_rate_unprotect(clk->core);
+
hlist_del(&clk->clks_node);
if (clk->min_rate > clk->core->req_rate ||
clk->max_rate < clk->core->req_rate)
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index a428aec..ebd7df5 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -739,6 +739,7 @@ struct clk_hw *clk_hw_get_parent_by_index(const struct clk_hw *hw,
unsigned long __clk_get_flags(struct clk *clk);
unsigned long clk_hw_get_flags(const struct clk_hw *hw);
bool clk_hw_is_prepared(const struct clk_hw *hw);
+bool clk_hw_rate_is_protected(const struct clk_hw *hw);
bool clk_hw_is_enabled(const struct clk_hw *hw);
bool __clk_is_enabled(struct clk *clk);
struct clk *__clk_lookup(const char *name);
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 024cd07..85d73e0 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -265,6 +265,30 @@ static inline void clk_unprepare(struct clk *clk)
*/
struct clk *devm_get_clk_from_child(struct device *dev,
struct device_node *np, const char *con_id);
+/**
+ * clk_rate_protect - inform the system when the clock rate must be protected.
+ * @clk: clock source
+ *
+ * This function informs the system that the consumer protecting the clock
+ * depends on the rate of the clock source and can't tolerate any glitches
+ * introduced by further clock rate change or re-parenting of the clock source.
+ *
+ * Must not be called from within atomic context.
+ */
+void clk_rate_protect(struct clk *clk);
+
+/**
+ * clk_rate_unprotect - release the protection of the clock source.
+ * @clk: clock source
+ *
+ * This function informs the system that the consumer previously protecting the
+ * clock rate can now deal with other consumer altering the clock source rate
+ *
+ * The caller must balance the number of rate_protect and rate_unprotect calls.
+ *
+ * Must not be called from within atomic context.
+ */
+void clk_rate_unprotect(struct clk *clk);
/**
* clk_enable - inform the system when the clock source should be running.
@@ -460,6 +484,11 @@ static inline void clk_put(struct clk *clk) {}
static inline void devm_clk_put(struct device *dev, struct clk *clk) {}
+
+static inline void clk_protect(struct clk *clk) {}
+
+static inline void clk_unprotect(struct clk *clk) {}
+
static inline int clk_enable(struct clk *clk)
{
return 0;
--
1.9.1

View file

@ -0,0 +1,112 @@
From 88cb11f99ca3a209274faa0da15ac27bcda99959 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 18 May 2017 13:42:12 +0200
Subject: [PATCH 51/79] clk: add clk_set_rate_protect
clk_set_rate_protect is a combination of clk_set_rate and clk_rate_protect
within a critical section. In case where several protecting consumers
compete to set the rate of the same provider, it provides a way to make
sure that at least one of them will be satisfied before the resource is
locked.
This is to avoid the unlikely situation where several consumers protect a
clock provider and none actually get a rate it can work with.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/clk/clk.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/clk.h | 14 ++++++++++++++
2 files changed, 59 insertions(+)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index d688b8f..d91236e 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1809,6 +1809,51 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
EXPORT_SYMBOL_GPL(clk_set_rate);
/**
+ * clk_set_rate_protect - specify a new rate and protect it
+ * @clk: the clk whose rate is being changed
+ * @rate: the new rate for clk
+ *
+ * This is a combination of clk_set_rate and clk_rate_protect within
+ * a critical section
+ *
+ * This can be used initially to ensure that at least 1 consumers is
+ * statisfied when several protecting consummers are competing for the
+ * same clock provider.
+ *
+ * The protection is not applied if setting the rate failed.
+ *
+ * Returns 0 on success, -EERROR otherwise.
+ */
+int clk_set_rate_protect(struct clk *clk, unsigned long rate)
+{
+ int ret;
+
+ if (!clk)
+ return 0;
+
+ /* prevent racing with updates to the clock topology */
+ clk_prepare_lock();
+
+ /*
+ * The temporary protection removal is not here, on purpose
+ * This function is meant to be used in instead of clk_rate_protect,
+ * so before the consumer code path protect the clock provider
+ */
+
+ ret = clk_core_set_rate_nolock(clk->core, rate);
+
+ if (!ret) {
+ clk_core_rate_protect(clk->core);
+ clk->protect_count++;
+ }
+
+ clk_prepare_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_rate_protect);
+
+/**
* clk_set_rate_range - set a rate range for a clock source
* @clk: clock source
* @min: desired minimum clock rate in Hz, inclusive
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 85d73e0..d3c299d 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -388,6 +388,15 @@ struct clk *devm_get_clk_from_child(struct device *dev,
int clk_set_rate(struct clk *clk, unsigned long rate);
/**
+ * clk_set_rate_protect - set and protect the clock rate for a clock source
+ * @clk: clock source
+ * @rate: desired clock rate in Hz
+ *
+ * Returns success (0) or negative errno.
+ */
+int clk_set_rate_protect(struct clk *clk, unsigned long rate);
+
+/**
* clk_has_parent - check if a clock is a possible parent for another
* @clk: clock source
* @parent: parent clock source
@@ -506,6 +515,11 @@ static inline int clk_set_rate(struct clk *clk, unsigned long rate)
return 0;
}
+static inline int clk_set_rate_protect(struct clk *clk, unsigned long rate)
+{
+ return 0;
+}
+
static inline long clk_round_rate(struct clk *clk, unsigned long rate)
{
return 0;
--
1.9.1

View file

@ -0,0 +1,43 @@
From fe7933da09cfadd75af72256a1d11f41ea5e46f0 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Mon, 20 Mar 2017 15:02:19 +0100
Subject: [PATCH 52/79] clk: rollback set_rate_range changes on failure
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/clk/clk.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index d91236e..253f1d3 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1864,6 +1864,7 @@ int clk_set_rate_protect(struct clk *clk, unsigned long rate)
int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
{
int ret = 0;
+ unsigned int old_min, old_max;
if (!clk)
return 0;
@@ -1881,9 +1882,16 @@ int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
clk_core_rate_unprotect(clk->core);
if (min != clk->min_rate || max != clk->max_rate) {
+ old_min = clk->min_rate;
+ old_max = clk->max_rate;
clk->min_rate = min;
clk->max_rate = max;
ret = clk_core_set_rate_nolock(clk->core, clk->core->req_rate);
+ if (ret) {
+ /* undo changes */
+ clk->min_rate = old_min;
+ clk->max_rate = old_max;
+ }
}
if (clk->protect_count)
--
1.9.1

View file

@ -0,0 +1,43 @@
From 29c6ab33e59640b8e09619be779091efc5953dc5 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Tue, 21 Mar 2017 19:24:35 +0100
Subject: [PATCH 53/79] clk: cosmetic changes to clk_summary debugfs entry
clk_summary debugfs entry was already well over the traditional 80
characters per line limit but it grew even larger with the addition of
clock protection.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/clk/clk.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 253f1d3..8d57e13 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2262,7 +2262,7 @@ static void clk_summary_show_one(struct seq_file *s, struct clk_core *c,
if (!c)
return;
- seq_printf(s, "%*s%-*s %11d %12d %12d %11lu %10lu %-3d\n",
+ seq_printf(s, "%*s%-*s %7d %8d %8d %11lu %10lu %-3d\n",
level * 3 + 1, "",
30 - level * 3, c->name,
c->enable_count, c->prepare_count, c->protect_count,
@@ -2289,8 +2289,9 @@ static int clk_summary_show(struct seq_file *s, void *data)
struct clk_core *c;
struct hlist_head **lists = (struct hlist_head **)s->private;
- seq_puts(s, " clock enable_cnt prepare_cnt protect_cnt rate accuracy phase\n");
- seq_puts(s, "----------------------------------------------------------------------------------------------------\n");
+ seq_puts(s, " enable prepare protect \n");
+ seq_puts(s, " clock count count count rate accuracy phase\n");
+ seq_puts(s, "----------------------------------------------------------------------------------------\n");
clk_prepare_lock();
--
1.9.1

View file

@ -0,0 +1,32 @@
From 71696c295bdbc1624d6204c01070518cba0451b5 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Tue, 21 Mar 2017 16:03:55 +0100
Subject: [PATCH 54/79] clk: fix incorrect usage of ENOSYS
ENOSYS is special and should only be used for incorrect syscall number.
It does not seem to be the case here.
Reported by checkpatch.pl while working on clock protection.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/clk/clk.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 8d57e13..5d54595 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2032,7 +2032,7 @@ static int clk_core_set_parent_nolock(struct clk_core *core,
/* verify ops for for multi-parent clks */
if ((core->num_parents > 1) && (!core->ops->set_parent))
- return -ENOSYS;
+ return -EPERM;
/* check that we are allowed to re-parent if the clock is in use */
if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count)
--
1.9.1

View file

@ -0,0 +1,56 @@
From 62400daae8f31d68035f4c8e3ead19962b9ecde9 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 18 May 2017 15:36:38 +0200
Subject: [PATCH 55/79] clk: fix CLK_SET_RATE_GATE with clock rate protection
Using clock rate protection, we can now enforce CLK_SET_RATE_GATE along the
clock tree
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
drivers/clk/clk.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 5d54595..971f39d 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -587,6 +587,9 @@ static void clk_core_unprepare(struct clk_core *core)
if (WARN_ON(core->prepare_count == 1 && core->flags & CLK_IS_CRITICAL))
return;
+ if (core->flags & CLK_SET_RATE_GATE)
+ clk_core_rate_unprotect(core);
+
if (--core->prepare_count > 0)
return;
@@ -657,6 +660,14 @@ static int clk_core_prepare(struct clk_core *core)
core->prepare_count++;
+ /*
+ * CLK_SET_RATE_GATE is a special case of clock protection
+ * Instead of a consumer protection, the provider is protecting
+ * itself when prepared
+ */
+ if (core->flags & CLK_SET_RATE_GATE)
+ clk_core_rate_protect(core);
+
return 0;
}
@@ -1738,9 +1749,6 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
if (clk_core_rate_is_protected(core))
return -EBUSY;
- if ((core->flags & CLK_SET_RATE_GATE) && core->prepare_count)
- return -EBUSY;
-
/* calculate new rates and get the topmost changed clock */
top = clk_calc_new_rates(core, req_rate);
if (!top)
--
1.9.1

View file

@ -0,0 +1,59 @@
From 73fcf1ce438f0952253ceee84e8a99510332a064 Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Thu, 23 Mar 2017 12:00:17 +0100
Subject: [PATCH 56/79] ASoC: meson: protect dai output clock rates
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
sound/soc/meson/i2s-dai.c | 4 ++++
sound/soc/meson/spdif-dai.c | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/sound/soc/meson/i2s-dai.c b/sound/soc/meson/i2s-dai.c
index 1008af8..d698aa0 100644
--- a/sound/soc/meson/i2s-dai.c
+++ b/sound/soc/meson/i2s-dai.c
@@ -351,6 +351,9 @@ static int meson_i2s_dai_startup(struct snd_pcm_substream *substream,
AIU_I2S_SOURCE_DESC_MODE_SPLIT,
AIU_I2S_SOURCE_DESC_MODE_SPLIT);
+ /* We can't tolerate glitches or rate change on this clock */
+ clk_rate_protect(priv->mclk);
+
return 0;
out_bclks:
@@ -368,6 +371,7 @@ static void meson_i2s_dai_shutdown(struct snd_pcm_substream *substream,
{
struct meson_i2s_dai *priv = snd_soc_dai_get_drvdata(dai);
+ clk_rate_unprotect(priv->mclk);
clk_disable_unprepare(priv->bclks);
clk_disable_unprepare(priv->mclk);
clk_disable_unprepare(priv->iface);
diff --git a/sound/soc/meson/spdif-dai.c b/sound/soc/meson/spdif-dai.c
index e763000..1d27f19 100644
--- a/sound/soc/meson/spdif-dai.c
+++ b/sound/soc/meson/spdif-dai.c
@@ -253,6 +253,9 @@ static int meson_spdif_dai_startup(struct snd_pcm_substream *substream,
AIU_MEM_IEC958_CONTROL_MODE_LINEAR,
AIU_MEM_IEC958_CONTROL_MODE_LINEAR);
+ /* We can't tolerate glitches or rate change on this clock */
+ clk_rate_protect(priv->mclk);
+
return 0;
out_mclk:
@@ -268,6 +271,7 @@ static void meson_spdif_dai_shutdown(struct snd_pcm_substream *substream,
{
struct meson_spdif_dai *priv = snd_soc_dai_get_drvdata(dai);
+ clk_rate_unprotect(priv->mclk);
clk_disable_unprepare(priv->iface);
clk_disable_unprepare(priv->mclk);
clk_disable_unprepare(priv->fast);
--
1.9.1

View file

@ -0,0 +1,37 @@
From a1619e301a0bdc1c90d505c740aeada54d229eaf Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Fri, 7 Jul 2017 17:38:21 +0200
Subject: [PATCH 57/79] snd: meson: add hdmi control bits
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
sound/soc/meson/i2s-dai.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/sound/soc/meson/i2s-dai.c b/sound/soc/meson/i2s-dai.c
index d698aa0..271bc14 100644
--- a/sound/soc/meson/i2s-dai.c
+++ b/sound/soc/meson/i2s-dai.c
@@ -56,8 +56,19 @@ struct meson_i2s_dai {
#define AIU_CLK_CTRL_ALRCLK_RIGHT_J (2 << 8)
#define AIU_CLK_CTRL_MORE_I2S_DIV_MASK GENMASK(5, 0)
#define AIU_CLK_CTRL_MORE_I2S_DIV(div) (((div) - 1) << 0)
+#define AIU_CLK_CTRL_MORE_HDMI_TX_SEL_MASK BIT(6)
+#define AIU_CLK_CTRL_MORE_HDMI_TX_I958_CLK (0 << 6)
+#define AIU_CLK_CTRL_MORE_HDMI_TX_INT_CLK (1 << 6)
#define AIU_CODEC_DAC_LRCLK_CTRL_DIV_MASK GENMASK(11, 0)
#define AIU_CODEC_DAC_LRCLK_CTRL_DIV(div) (((div) - 1) << 0)
+#define AIU_HDMI_CLK_DATA_CTRL_CLK_SEL_MASK GENMASK(1, 0)
+#define AIU_HDMI_CLK_DATA_CTRL_CLK_DISABLE (0 << 0)
+#define AIU_HDMI_CLK_DATA_CTRL_CLK_PCM (1 << 0)
+#define AIU_HDMI_CLK_DATA_CTRL_CLK_I2S (2 << 0)
+#define AIU_HDMI_CLK_DATA_CTRL_DATA_SEL_MASK GENMASK(5, 4)
+#define AIU_HDMI_CLK_DATA_CTRL_DATA_MUTE (0 << 4)
+#define AIU_HDMI_CLK_DATA_CTRL_DATA_PCM (1 << 4)
+#define AIU_HDMI_CLK_DATA_CTRL_DATA_I2S (2 << 4)
#define AIU_I2S_DAC_CFG_PAYLOAD_SIZE_MASK GENMASK(1, 0)
#define AIU_I2S_DAC_CFG_AOCLK_32 (0 << 0)
#define AIU_I2S_DAC_CFG_AOCLK_48 (2 << 0)
--
1.9.1

View file

@ -0,0 +1,35 @@
From 967a58cb6eeb223553058ea15083d755df70b5ec Mon Sep 17 00:00:00 2001
From: Jerome Brunet <jbrunet@baylibre.com>
Date: Fri, 7 Jul 2017 17:39:21 +0200
Subject: [PATCH 58/79] snd: meson: activate HDMI audio path
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
sound/soc/meson/i2s-dai.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/sound/soc/meson/i2s-dai.c b/sound/soc/meson/i2s-dai.c
index 271bc14..85a5023 100644
--- a/sound/soc/meson/i2s-dai.c
+++ b/sound/soc/meson/i2s-dai.c
@@ -232,6 +232,17 @@ static int meson_i2s_dai_hw_params(struct snd_pcm_substream *substream,
return ret;
}
+ /* Quick and dirty hack for HDMI */
+ regmap_update_bits(priv->core->aiu, AIU_HDMI_CLK_DATA_CTRL,
+ AIU_HDMI_CLK_DATA_CTRL_CLK_SEL_MASK |
+ AIU_HDMI_CLK_DATA_CTRL_DATA_SEL_MASK,
+ AIU_HDMI_CLK_DATA_CTRL_CLK_I2S |
+ AIU_HDMI_CLK_DATA_CTRL_DATA_I2S);
+
+ regmap_update_bits(priv->core->aiu, AIU_CLK_CTRL_MORE,
+ AIU_CLK_CTRL_MORE_HDMI_TX_SEL_MASK,
+ AIU_CLK_CTRL_MORE_HDMI_TX_INT_CLK);
+
return 0;
}
--
1.9.1

View file

@ -0,0 +1,153 @@
From 47ca3bb9f820024cc182e0f46db659e59e3f6318 Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Fri, 7 Jul 2017 17:47:31 +0200
Subject: [PATCH 59/79] ARM64: dts: meson-gx: Switch p20x and p230 to HDMI
output only
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi | 47 +---------------------
.../boot/dts/amlogic/meson-gxl-s905d-p230.dts | 25 ++++++------
2 files changed, 14 insertions(+), 58 deletions(-)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
index 88e3713..4ff313a 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
@@ -153,31 +153,10 @@
status = "okay";
};
- amp: analog-amplifier {
- compatible = "dioo,dio2125";
- enable-gpios = <&gpio GPIOH_3 0>;
- status = "okay";
- };
-
- spdif_out: spdif-out {
- #sound-dai-cells = <0>;
- compatible = "linux,spdif-dit";
- status = "okay";
- };
-
soc {
sound {
compatible = "simple-audio-card";
simple-audio-card,name = "meson-gxbb-p20x";
- simple-audio-card,aux-devs = <&amp>;
- simple-audio-card,widgets =
- "Line", "Analog Left Output",
- "Line", "Analog Right Output";
- simple-audio-card,routing =
- "Analog Left Output", "OUTL",
- "Analog Right Output", "OUTR",
- "INL", "AOUTL",
- "INR", "AOUTR";
status = "okay";
simple-audio-card,dai-link@0 {
@@ -196,21 +175,7 @@
};
codec {
- sound-dai = <&i2s_codec>;
- };
- };
-
- simple-audio-card,dai-link@1 {
- plat {
- sound-dai = <&aiu_spdif_dma>;
- };
-
- cpu {
- sound-dai = <&spdif_dai>;
- };
-
- codec {
- sound-dai = <&spdif_out>;
+ sound-dai = <&hdmi_tx>;
};
};
};
@@ -352,19 +317,9 @@
status = "okay";
};
-&aiu_spdif_dma {
- status = "okay";
-};
-
&i2s_dai {
pinctrl-0 = <&i2s_am_clk_pins>, <&i2s_out_ao_clk_pins>,
<&i2s_out_lr_clk_pins>, <&i2s_out_ch01_ao_pins>;
pinctrl-names = "default";
status = "okay";
};
-
-&spdif_dai {
- pinctrl-0 = <&spdif_out_ao_6_pins>;
- pinctrl-names = "default";
- status = "okay";
-};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
index 5da5206..f95978c 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
@@ -85,28 +85,28 @@
regulator-max-microvolt = <1800000>;
};
- spdif_out: spdif-out {
- #sound-dai-cells = <0>;
- compatible = "linux,spdif-dit";
- status = "okay";
- };
-
soc {
sound {
compatible = "simple-audio-card";
simple-audio-card,name = "meson-gxl-p230";
simple-audio-card,dai-link@0 {
+ /* RCA Output */
+ format = "i2s";
+ mclk-fs = <256>;
+ bitclock-master = <&i2s_dai>;
+ frame-master = <&i2s_dai>;
+
plat {
- sound-dai = <&aiu_spdif_dma>;
+ sound-dai = <&aiu_i2s_dma>;
};
cpu {
- sound-dai = <&spdif_dai>;
+ sound-dai = <&i2s_dai>;
};
codec {
- sound-dai = <&spdif_out>;
+ sound-dai = <&hdmi_tx>;
};
};
};
@@ -156,12 +156,13 @@
status = "okay";
};
-&aiu_spdif_dma {
+&aiu_i2s_dma {
status = "okay";
};
-&spdif_dai {
- pinctrl-0 = <&spdif_out_h_pins>;
+&i2s_dai {
+ pinctrl-0 = <&i2s_am_clk_pins>, <&i2s_out_ao_clk_pins>,
+ <&i2s_out_lr_clk_pins>, <&i2s_out_ch01_pins>;
pinctrl-names = "default";
status = "okay";
};
--
1.9.1

View file

@ -0,0 +1,71 @@
From 2836b35f50d8027974c9fc9d258911243f4815e9 Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Fri, 7 Jul 2017 17:47:47 +0200
Subject: [PATCH 60/79] ARM64: dts: meson-gxbb-odroic2: Add HDMI audio output
nodes
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
.../arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts | 42 ++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
index 0066b1a..6c09bf7 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
@@ -146,6 +146,33 @@
};
};
};
+
+ soc {
+ sound {
+ compatible = "simple-audio-card";
+ simple-audio-card,name = "meson-gxbb-odroidc2";
+ status = "okay";
+
+ simple-audio-card,dai-link@0 {
+ format = "i2s";
+ mclk-fs = <256>;
+ bitclock-master = <&i2s_dai>;
+ frame-master = <&i2s_dai>;
+
+ plat {
+ sound-dai = <&aiu_i2s_dma>;
+ };
+
+ cpu {
+ sound-dai = <&i2s_dai>;
+ };
+
+ codec {
+ sound-dai = <&hdmi_tx>;
+ };
+ };
+ };
+ };
};
&scpi_clocks {
@@ -332,3 +359,18 @@
remote-endpoint = <&hdmi_connector_in>;
};
};
+
+&audio {
+ status = "okay";
+};
+
+&aiu_i2s_dma {
+ status = "okay";
+};
+
+&i2s_dai {
+ pinctrl-0 = <&i2s_am_clk_pins>, <&i2s_out_ao_clk_pins>,
+ <&i2s_out_lr_clk_pins>, <&i2s_out_ch01_ao_pins>;
+ pinctrl-names = "default";
+ status = "okay";
+};
--
1.9.1

View file

@ -0,0 +1,36 @@
From 48620a0b478fbf467f86b7bf019e6a53a8eed08e Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Fri, 7 Jul 2017 17:49:19 +0200
Subject: [PATCH 61/79] ARM64: dts: meson: add sound-dai-cells to HDMI node
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 1 +
arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
index 92d2b91..dc0eb44 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
@@ -710,4 +710,5 @@
<&clkc CLKID_CLK81>,
<&clkc CLKID_GCLK_VENCI_INT0>;
clock-names = "isfr", "iahb", "venci";
+ #sound-dai-cells = <0>;
};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
index de0a26b..1e65b0f 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
@@ -660,5 +660,6 @@
clocks = <&clkc CLKID_HDMI_PCLK>,
<&clkc CLKID_CLK81>,
<&clkc CLKID_GCLK_VENCI_INT0>;
- clock-names = "isfr", "iahb", "venci";
+ clock-names = "isfr", "iahb", "venci", "iahb", "venci";
+ #sound-dai-cells = <0>;
};
--
1.9.1

View file

@ -0,0 +1,31 @@
From b2e9388561f99c5ac8abc8de8b282f755fa43376 Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Fri, 7 Jul 2017 18:08:36 +0200
Subject: [PATCH 65/79] Add CEC support for Odroid-C2
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
index 6c09bf7..b4f21d1 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
@@ -348,6 +348,13 @@
vqmmc-supply = <&vcc1v8>;
};
+&cec_AO {
+ status = "okay";
+ pinctrl-0 = <&ao_cec_pins>;
+ pinctrl-names = "default";
+ hdmi-phandle = <&hdmi_tx>;
+};
+
&hdmi_tx {
status = "okay";
pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>;
--
1.9.1

View file

@ -0,0 +1,57 @@
From 13df956e16be2e1a645f01ba589c4a9af25fc5cf Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Thu, 8 Jun 2017 17:21:44 +0200
Subject: [PATCH 67/79] ARM64: dts: meson-gxl-s905d-p230: Add HDMI nodes
Add HDMI nodes for the Amlogic P230 board.
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
.../boot/dts/amlogic/meson-gxl-s905d-p230.dts | 24 ++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
index f95978c..5aed4e6 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dts
@@ -111,6 +111,17 @@
};
};
};
+
+ hdmi-connector {
+ compatible = "hdmi-connector";
+ type = "a";
+
+ port {
+ hdmi_connector_in: endpoint {
+ remote-endpoint = <&hdmi_tx_tmds_out>;
+ };
+ };
+ };
};
&cec_AO {
@@ -147,6 +158,19 @@
};
};
+
+&hdmi_tx {
+ status = "okay";
+ pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>;
+ pinctrl-names = "default";
+};
+
+&hdmi_tx_tmds_port {
+ hdmi_tx_tmds_out: endpoint {
+ remote-endpoint = <&hdmi_connector_in>;
+ };
+};
+
&saradc {
status = "okay";
vref-supply = <&vddio_ao18>;
--
1.9.1

View file

@ -0,0 +1,57 @@
From 4fc4090651c1d5450283935e4b0e674bd36abeb7 Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Thu, 8 Jun 2017 17:23:46 +0200
Subject: [PATCH 69/79] ARM64: dts: meson-gxl-s905x-p212: Add HDMI and CVBS
nodes
Add HDMI and CVBS nodes for the Amlogic P212 reference board.
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
.../boot/dts/amlogic/meson-gxl-s905x-p212.dts | 23 ++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
index 99a01ff..6e2bf85 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
@@ -58,6 +58,17 @@
};
};
};
+
+ hdmi-connector {
+ compatible = "hdmi-connector";
+ type = "a";
+
+ port {
+ hdmi_connector_in: endpoint {
+ remote-endpoint = <&hdmi_tx_tmds_out>;
+ };
+ };
+ };
};
&cec_AO {
@@ -73,6 +84,18 @@
};
};
+&hdmi_tx {
+ status = "okay";
+ pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>;
+ pinctrl-names = "default";
+};
+
+&hdmi_tx_tmds_port {
+ hdmi_tx_tmds_out: endpoint {
+ remote-endpoint = <&hdmi_connector_in>;
+ };
+};
+
/* This UART is brought out to the DB9 connector */
&uart_AO {
status = "okay";
--
1.9.1

View file

@ -0,0 +1,50 @@
From 41c79ce5a32601b5853f97a0186fbc2406d14f06 Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Mon, 10 Jul 2017 12:24:14 +0200
Subject: [PATCH 70/79] ARM64: dts: meson-gx: Add CEC support for Wetek Play2
and Khadas Vim
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-play2.dts | 6 ++++++
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts | 7 +++++++
2 files changed, 13 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-play2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-play2.dts
index e76ac31..f7144fd 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-play2.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-play2.dts
@@ -108,6 +108,12 @@
};
};
+&cec_AO {
+ status = "okay";
+ pinctrl-0 = <&ao_cec_pins>;
+ pinctrl-names = "default";
+ hdmi-phandle = <&hdmi_tx>;
+};
&cvbs_vdac_port {
cvbs_vdac_out: endpoint {
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts
index a133ed8..da12721 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dts
@@ -67,6 +67,13 @@
};
};
+&cec_AO {
+ status = "okay";
+ pinctrl-0 = <&ao_cec_pins>;
+ pinctrl-names = "default";
+ hdmi-phandle = <&hdmi_tx>;
+};
+
&hdmi_tx {
status = "okay";
pinctrl-0 = <&hdmi_hpd_pins>, <&hdmi_i2c_pins>;
--
1.9.1

View file

@ -0,0 +1,41 @@
From 40979f4df1a3bbe110b3fe2ace1524dc559ad4e8 Mon Sep 17 00:00:00 2001
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Date: Thu, 12 Jan 2017 01:38:26 +0100
Subject: [PATCH 71/79] ARM64: dts: meson-gxbb: allow child devices on the USB
controller
Add the size and adress cells to the USB controllers to allow specifying
child devices (for example the USB hub on the Odroid-C2 which must be
taken out of reset to work).
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
index dc0eb44..912c4dc 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
@@ -73,6 +73,8 @@
usb0: usb@c9000000 {
compatible = "amlogic,meson-gxbb-usb", "snps,dwc2";
+ #address-cells = <1>;
+ #size-cells = <0>;
reg = <0x0 0xc9000000 0x0 0x40000>;
interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clkc CLKID_USB0_DDR_BRIDGE>;
@@ -85,6 +87,8 @@
usb1: usb@c9100000 {
compatible = "amlogic,meson-gxbb-usb", "snps,dwc2";
+ #address-cells = <1>;
+ #size-cells = <0>;
reg = <0x0 0xc9100000 0x0 0x40000>;
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clkc CLKID_USB1_DDR_BRIDGE>;
--
1.9.1

View file

@ -0,0 +1,36 @@
From ce69e6bd727706aacc7fd5fa3d79b92c552fad79 Mon Sep 17 00:00:00 2001
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Date: Thu, 12 Jan 2017 01:39:20 +0100
Subject: [PATCH 72/79] ARM64: dts: meson-gxbb-odroidc2: take USB hub out of
reset
This takes the USB hub out of reset, otherwise the hub is not working.
Fixes: 5a0803bd5ae ("ARM64: dts: meson-gxbb-odroidc2: Enable USB Nodes")
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
index b4f21d1..e5e5300 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
@@ -303,6 +303,13 @@
&usb1 {
status = "okay";
+
+ hub@1 {
+ compatible = "usb5e3,610";
+ reg = <1>;
+ reset-gpios = <&gpio GPIOAO_4 GPIO_ACTIVE_LOW>;
+ reset-duration-us = <3000>;
+ };
};
&saradc {
--
1.9.1

View file

@ -0,0 +1,90 @@
From 6006726ba262f50d61f3a001cc87fbc82dd8318a Mon Sep 17 00:00:00 2001
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Date: Sun, 20 Nov 2016 00:23:52 +0100
Subject: [PATCH 73/79] ARM64: dts: meson-gxl: add USB host support
This adds USB host support to the Meson GXL SoC. A dwc3 controller is
used for host-mode, while a dwc2 controller is used for device-mode only.
The dwc3 controller's internal roothub has two USB2 ports enabled but no
USB3 port. Each of the ports is supplied by a separate PHY. The USB pins
are connected to the SoC's USBHOST_A and USBOTG_B pins.
Due to the way the roothub works internally the USB PHYs are left
enabled. When the dwc3 controller is disabled the PHY is never powered on
so it does not draw any extra power. However, when the dwc3 host
controller is enabled then all PHYs also have to be enabled, otherwise
USB devices will not be detected (regardless of whether they are plugged
into an enabled port or not). This means that only the dwc3 controller
has to be enabled on boards with USB support (instead of requiring all
boards to enable the PHYs additionally with the chance of forgetting to
enable one and breaking all other ports with that as well).
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 50 ++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
index 1e65b0f..c33c29f 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
@@ -49,6 +49,56 @@
/ {
compatible = "amlogic,meson-gxl";
+
+ soc {
+
+ usb0: usb@c9000000 {
+ compatible = "snps,dwc3";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x0 0xc9000000 0x0 0x100000>;
+ interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+ dr_mode = "host";
+ maximum-speed = "high-speed";
+ snps,dis_u2_susphy_quirk;
+ status = "disabled";
+
+ dwc3_roothub: roothub@0 {
+ compatible = "usb1d6b,3", "usb1d6b,2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ port@1 {
+ reg = <1>;
+ phys = <&usb2_phy0>;
+ phy-names = "usb2-phy";
+ };
+
+ port@2 {
+ reg = <2>;
+ phys = <&usb2_phy1>;
+ phy-names = "usb2-phy";
+ };
+ };
+ };
+ };
+};
+
+&apb {
+ usb2_phy0: phy@78000 {
+ compatible = "amlogic,meson-gxl-usb2-phy";
+ #phy-cells = <0>;
+ reg = <0x0 0x78000 0x0 0x20>;
+ status = "okay";
+ };
+
+ usb2_phy1: phy@78020 {
+ compatible = "amlogic,meson-gxl-usb2-phy";
+ #phy-cells = <0>;
+ reg = <0x0 0x78020 0x0 0x20>;
+ status = "okay";
+ };
};
&ethmac {
--
1.9.1

View file

@ -0,0 +1,49 @@
From c6b2bca6dbd55fbdd2c5cdf3d214c982a215e6a7 Mon Sep 17 00:00:00 2001
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Date: Sat, 26 Nov 2016 00:17:22 +0100
Subject: [PATCH 74/79] ARM64: dts: meson-gxm: add GXM specific USB host
configuration
The USB configuration on GXM is slightly different than on GXL. The dwc3
controller's internal hub has three USB2 ports (instead of 2 on GXL)
along with a dedicated USB2 PHY for this port. However, it seems that
there are no pins on GXM which would allow connecting the third port to
a physical USB port.
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxm.dtsi | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
index fe451cc..4089f04 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
@@ -117,6 +117,23 @@
};
};
+&apb {
+ usb2_phy2: phy@78040 {
+ compatible = "amlogic,meson-gxl-usb2-phy";
+ #phy-cells = <0>;
+ reg = <0x0 0x78040 0x0 0x20>;
+ status = "disabled";
+ };
+};
+
+&dwc3_roothub {
+ port@3 {
+ reg = <3>;
+ phys = <&usb2_phy2>;
+ phy-names = "usb2-phy";
+ };
+};
+
&saradc {
compatible = "amlogic,meson-gxm-saradc", "amlogic,meson-saradc";
};
--
1.9.1

View file

@ -0,0 +1,293 @@
From acfe799c4e00a726cdb0bbd37aaaa123070ed59e Mon Sep 17 00:00:00 2001
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Date: Tue, 10 Jan 2017 18:59:43 +0100
Subject: [PATCH 75/79] usb: host: add a generic platform USB roothub driver
Many SoC platforms have separate devices for the USB PHY which are
registered through the generic PHY framework. These PHYs have to be
enabled to make the USB controller actually work. They also have to be
disabled again on shutdown/suspend.
Currently (at least) the following HCI platform drivers are using custom
code to obtain all PHYs via devicetree for the roothub/controller and
disable/enable them when required:
- ehci-platform.c has ehci_platform_power_{on,off}
- xhci-mtk.c has xhci_mtk_phy_{init,exit,power_on,power_off}
- ohci-platform.c has ohci_platform_power_{on,off}
These drivers are not using the generic devicetree USB device bindings
yet which were only introduced recently (documentation is available in
devicetree/bindings/usb/usb-device.txt).
With this new driver the usb2-phy and usb3-phy can be specified directly
in the child-node of the corresponding port of the roothub via
devicetree. This can be extended by not just parsing PHYs (some of the
other drivers listed above are for example also parsing a list of clocks
as well) when required.
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
.../devicetree/bindings/usb/usb-roothub.txt | 46 +++++++
drivers/usb/host/Kconfig | 3 +
drivers/usb/host/Makefile | 2 +
drivers/usb/host/platform-roothub.c | 146 +++++++++++++++++++++
drivers/usb/host/platform-roothub.h | 14 ++
5 files changed, 211 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/usb-roothub.txt
create mode 100644 drivers/usb/host/platform-roothub.c
create mode 100644 drivers/usb/host/platform-roothub.h
diff --git a/Documentation/devicetree/bindings/usb/usb-roothub.txt b/Documentation/devicetree/bindings/usb/usb-roothub.txt
new file mode 100644
index 0000000..23b24b6
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/usb-roothub.txt
@@ -0,0 +1,46 @@
+Generic USB root-hub Properties
+
+similar to the USB device bindings (documented in usb-device.txt from the
+current directory) this provides support for configuring the root-hub.
+
+Required properties:
+- compatible: should be at least one of "usb1d6b,3", "usb1d6b,2"
+- reg: must be 0.
+- address-cells: must be 1
+- size-cells: must be 0
+
+Required sub-nodes:
+a sub-node per actual USB port is required. each sub-node supports the
+following properties:
+ Required properties:
+ - reg: the port number on the root-hub (mandatory)
+ Optional properties:
+ - phys: optional, from the *Generic PHY* bindings (mandatory needed
+ when phy-names is given)
+ - phy-names: optional, from the *Generic PHY* bindings; supported names
+ are "usb2-phy" or "usb3-phy"
+
+Example:
+ &usb1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ roothub@0 {
+ compatible = "usb1d6b,3", "usb1d6b,2";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ port@1 {
+ reg = <1>;
+ usb-phy = <&usb2_phy1>, <&usb3_phy1>;
+ phy-names = "usb2-phy", "usb3-phy";
+ };
+
+ port@2 {
+ reg = <2>;
+ usb-phy = <&usb2_phy2>, <&usb3_phy2>;
+ phy-names = "usb2-phy", "usb3-phy";
+ };
+ };
+ }
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index ababb91..3f450e0 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -797,6 +797,9 @@ config USB_HCD_SSB
If unsure, say N.
+config USB_PLATFORM_ROOTHUB
+ bool
+
config USB_HCD_TEST_MODE
bool "HCD test mode support"
---help---
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index c77b0a3..93f0dd2 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -29,6 +29,8 @@ obj-$(CONFIG_USB_WHCI_HCD) += whci/
obj-$(CONFIG_USB_PCI) += pci-quirks.o
+obj-$(CONFIG_USB_PLATFORM_ROOTHUB) += platform-roothub.o
+
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o
obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o
diff --git a/drivers/usb/host/platform-roothub.c b/drivers/usb/host/platform-roothub.c
new file mode 100644
index 0000000..84837e4
--- /dev/null
+++ b/drivers/usb/host/platform-roothub.c
@@ -0,0 +1,146 @@
+/*
+ * platform roothub driver - a virtual PHY device which passes all phy_*
+ * function calls to multiple (actual) PHY devices. This is comes handy when
+ * initializing all PHYs on a root-hub (to keep them all in the same state).
+ *
+ * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/list.h>
+#include <linux/phy/phy.h>
+#include <linux/of.h>
+#include <linux/usb/of.h>
+
+#include "platform-roothub.h"
+
+#define ROOTHUB_PORTNUM 0
+
+struct platform_roothub {
+ struct phy *phy;
+ struct list_head list;
+};
+
+static struct platform_roothub *platform_roothub_alloc(struct device *dev)
+{
+ struct platform_roothub *roothub_entry;
+
+ roothub_entry = devm_kzalloc(dev, sizeof(*roothub_entry), GFP_KERNEL);
+ if (!roothub_entry)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&roothub_entry->list);
+
+ return roothub_entry;
+}
+
+static int platform_roothub_add_phy(struct device *dev,
+ struct device_node *port_np,
+ const char *con_id, struct list_head *list)
+{
+ struct platform_roothub *roothub_entry;
+ struct phy *phy = devm_of_phy_get(dev, port_np, con_id);
+
+ if (IS_ERR_OR_NULL(phy)) {
+ if (!phy || PTR_ERR(phy) == -ENODEV)
+ return 0;
+ else
+ return PTR_ERR(phy);
+ }
+
+ roothub_entry = platform_roothub_alloc(dev);
+ if (IS_ERR(roothub_entry))
+ return PTR_ERR(roothub_entry);
+
+ roothub_entry->phy = phy;
+
+ list_add_tail(&roothub_entry->list, list);
+
+ return 0;
+}
+
+struct platform_roothub *platform_roothub_init(struct device *dev)
+{
+ struct device_node *roothub_np, *port_np;
+ struct platform_roothub *plat_roothub;
+ int err;
+
+ roothub_np = usb_of_get_child_node(dev->of_node, ROOTHUB_PORTNUM);
+ if (!of_device_is_available(roothub_np))
+ return NULL;
+
+ plat_roothub = platform_roothub_alloc(dev);
+ if (IS_ERR(plat_roothub))
+ return plat_roothub;
+
+ for_each_available_child_of_node(roothub_np, port_np) {
+ err = platform_roothub_add_phy(dev, port_np, "usb2-phy",
+ &plat_roothub->list);
+ if (err)
+ return ERR_PTR(err);
+
+ err = platform_roothub_add_phy(dev, port_np, "usb3-phy",
+ &plat_roothub->list);
+ if (err)
+ return ERR_PTR(err);
+ }
+
+ return plat_roothub;
+}
+EXPORT_SYMBOL_GPL(platform_roothub_init);
+
+int platform_roothub_power_on(struct platform_roothub *plat_roothub)
+{
+ struct platform_roothub *roothub_entry;
+ struct list_head *head;
+ int err;
+
+ if (!plat_roothub)
+ return 0;
+
+ head = &plat_roothub->list;
+
+ list_for_each_entry(roothub_entry, head, list) {
+ err = phy_init(roothub_entry->phy);
+ if (err)
+ goto err_out;
+
+ err = phy_power_on(roothub_entry->phy);
+ if (err) {
+ phy_exit(roothub_entry->phy);
+ goto err_out;
+ }
+ }
+
+ return 0;
+
+err_out:
+ list_for_each_entry_continue_reverse(roothub_entry, head, list) {
+ phy_power_off(roothub_entry->phy);
+ phy_exit(roothub_entry->phy);
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(platform_roothub_power_on);
+
+void platform_roothub_power_off(struct platform_roothub *plat_roothub)
+{
+ struct platform_roothub *roothub_entry;
+
+ if (!plat_roothub)
+ return;
+
+ list_for_each_entry_reverse(roothub_entry, &plat_roothub->list, list) {
+ phy_power_off(roothub_entry->phy);
+ phy_exit(roothub_entry->phy);
+ }
+}
+EXPORT_SYMBOL_GPL(platform_roothub_power_off);
diff --git a/drivers/usb/host/platform-roothub.h b/drivers/usb/host/platform-roothub.h
new file mode 100644
index 0000000..bde0bf2
--- /dev/null
+++ b/drivers/usb/host/platform-roothub.h
@@ -0,0 +1,14 @@
+#ifndef USB_HOST_PLATFORM_ROOTHUB_H
+#define USB_HOST_PLATFORM_ROOTHUB_H
+
+struct phy;
+struct device_node;
+
+struct platform_roothub;
+
+struct platform_roothub *platform_roothub_init(struct device *dev);
+
+int platform_roothub_power_on(struct platform_roothub *plat_roothub);
+void platform_roothub_power_off(struct platform_roothub *plat_roothub);
+
+#endif /* USB_HOST_PLATFORM_ROOTHUB_H */
--
1.9.1

View file

@ -0,0 +1,146 @@
From 9dfd5d48fbcd31b12825e81d532aca76c2b769f8 Mon Sep 17 00:00:00 2001
From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Date: Wed, 11 Jan 2017 11:34:59 +0100
Subject: [PATCH 76/79] usb: host: xhci: plat: integrate the platform-roothub
This enables the platform-roothub for the xhci-plat driver. This allows
specifying a PHY for each port via devicetree. All PHYs will then be
enabled/disabled by the platform-roothub driver.
One example where this is required is the Amlogic GXL and GXM SoCs:
They are using a dwc3 USB controller with up to three ports enabled on
the internal roothub. Using only the top-level "phy" properties does not
work here since one can only specify one "usb2-phy" and one "usb3-phy",
while actually at least two "usb2-phy" have to be specified.
Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
Documentation/devicetree/bindings/usb/usb-xhci.txt | 7 ++++++
drivers/usb/host/Kconfig | 1 +
drivers/usb/host/xhci-plat.c | 26 +++++++++++++++++++++-
drivers/usb/host/xhci.h | 3 +++
4 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt
index 2d80b60..31b4f68 100644
--- a/Documentation/devicetree/bindings/usb/usb-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt
@@ -29,6 +29,13 @@ Optional properties:
- usb3-lpm-capable: determines if platform is USB3 LPM capable
- quirk-broken-port-ped: set if the controller has broken port disable mechanism
+sub-nodes:
+- optionally there can be a node for the root-hub, see usb-roothub.txt in the
+ current directory
+- one or more nodes with reg 1-31 for each port to which a device is connected.
+ See usb-device.txt in the current directory for more information.
+
+
Example:
usb@f0931000 {
compatible = "generic-xhci";
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 3f450e0..d6279d7 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -36,6 +36,7 @@ config USB_XHCI_PCI
config USB_XHCI_PLATFORM
tristate "Generic xHCI driver for a platform device"
select USB_XHCI_RCAR if ARCH_RENESAS
+ select USB_PLATFORM_ROOTHUB
---help---
Adds an xHCI host driver for a generic platform device, which
provides a memory space and an irq.
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index c04144b..4de20b5 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -285,10 +285,20 @@ static int xhci_plat_probe(struct platform_device *pdev)
goto put_usb3_hcd;
}
- ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ xhci->platform_roothub = platform_roothub_init(sysdev);
+ if (IS_ERR(xhci->platform_roothub)) {
+ ret = PTR_ERR(xhci->platform_roothub);
+ goto disable_clk;
+ }
+
+ ret = platform_roothub_power_on(xhci->platform_roothub);
if (ret)
goto disable_usb_phy;
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (ret)
+ goto disable_platform_roothub;
+
if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
xhci->shared_hcd->can_do_streams = 1;
@@ -311,6 +321,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
dealloc_usb2_hcd:
usb_remove_hcd(hcd);
+disable_platform_roothub:
+ platform_roothub_power_off(xhci->platform_roothub);
+
disable_usb_phy:
usb_phy_shutdown(hcd->usb_phy);
@@ -342,6 +355,8 @@ static int xhci_plat_remove(struct platform_device *dev)
usb_remove_hcd(xhci->shared_hcd);
usb_phy_shutdown(hcd->usb_phy);
+ platform_roothub_power_off(xhci->platform_roothub);
+
usb_remove_hcd(hcd);
usb_put_hcd(xhci->shared_hcd);
@@ -374,6 +389,11 @@ static int __maybe_unused xhci_plat_suspend(struct device *dev)
if (!device_may_wakeup(dev) && !IS_ERR(xhci->clk))
clk_disable_unprepare(xhci->clk);
+ if (ret)
+ return ret;
+
+ platform_roothub_power_off(xhci->platform_roothub);
+
return ret;
}
@@ -386,6 +406,10 @@ static int __maybe_unused xhci_plat_resume(struct device *dev)
if (!device_may_wakeup(dev) && !IS_ERR(xhci->clk))
clk_prepare_enable(xhci->clk);
+ ret = platform_roothub_power_on(xhci->platform_roothub);
+ if (ret)
+ return ret;
+
ret = xhci_priv_resume_quirk(hcd);
if (ret)
return ret;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 73a28a9..7f3d15e 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -34,6 +34,8 @@
#include "xhci-ext-caps.h"
#include "pci-quirks.h"
+#include "platform-roothub.h"
+
/* xHCI PCI Configuration Registers */
#define XHCI_SBRN_OFFSET (0x60)
@@ -1725,6 +1727,7 @@ struct xhci_hcd {
int msix_count;
/* optional clock */
struct clk *clk;
+ struct platform_roothub *platform_roothub;
/* data structures */
struct xhci_device_context_array *dcbaa;
struct xhci_ring *cmd_ring;
--
1.9.1

View file

@ -0,0 +1,64 @@
From 7079f88717e54f9ea48c1cbfba09b53cc8b9aefd Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Thu, 13 Jul 2017 15:02:33 +0200
Subject: [PATCH 77/79] ARM64: dts: meson-gx: Enable USB on GXL and GXM boards
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi | 4 ++++
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts | 4 ++++
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi | 4 ++++
arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts | 4 ++++
4 files changed, 16 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
index 54718ee..bf49f6f 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
@@ -234,3 +234,7 @@
remote-endpoint = <&hdmi_connector_in>;
};
};
+
+&usb0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts
index 55ec11a..19bb39b 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dts
@@ -249,3 +249,7 @@
remote-endpoint = <&hdmi_connector_in>;
};
};
+
+&usb0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi
index f3eea8e..a07c34d 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi
@@ -171,3 +171,7 @@
pinctrl-0 = <&uart_ao_a_pins>;
pinctrl-names = "default";
};
+
+&usb0 {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
index 3a327dd..1b7038d 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
@@ -215,3 +215,7 @@
remote-endpoint = <&hdmi_connector_in>;
};
};
+
+&usb0 {
+ status = "okay";
+};
--
1.9.1

View file

@ -0,0 +1,66 @@
From 8718f9cf5d8df7fd800d4f7692eb1982cfa88a17 Mon Sep 17 00:00:00 2001
From: Neil Armstrong <narmstrong@baylibre.com>
Date: Thu, 13 Jul 2017 15:08:22 +0200
Subject: [PATCH 78/79] ARM64: dts: meson-gxl: Enable HDMI audio on p212 based
boards
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
.../boot/dts/amlogic/meson-gxl-s905x-p212.dtsi | 41 ++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi
index a07c34d..1372ea2 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtsi
@@ -74,6 +74,47 @@
clocks = <&wifi32k>;
clock-names = "ext_clock";
};
+
+ soc {
+ sound {
+ compatible = "simple-audio-card";
+ simple-audio-card,name = "meson-gxl";
+
+ simple-audio-card,dai-link@0 {
+ format = "i2s";
+ mclk-fs = <256>;
+ bitclock-master = <&i2s_dai>;
+ frame-master = <&i2s_dai>;
+
+ plat {
+ sound-dai = <&aiu_i2s_dma>;
+ };
+
+ cpu {
+ sound-dai = <&i2s_dai>;
+ };
+
+ codec {
+ sound-dai = <&hdmi_tx>;
+ };
+ };
+ };
+ };
+};
+
+&audio {
+ status = "okay";
+};
+
+&aiu_i2s_dma {
+ status = "okay";
+};
+
+&i2s_dai {
+ pinctrl-0 = <&i2s_am_clk_pins>, <&i2s_out_ao_clk_pins>,
+ <&i2s_out_lr_clk_pins>, <&i2s_out_ch01_pins>;
+ pinctrl-names = "default";
+ status = "okay";
};
&ethmac {
--
1.9.1

View file

@ -48,11 +48,14 @@ diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index f8047b4..bea10c6 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -64,3 +64,4 @@ obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o
obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o
@@ -13,6 +13,7 @@ obj-$(CONFIG_ARCH_MESON) += amlogic/
obj-$(CONFIG_ARCH_RENESAS) += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
+obj-$(CONFIG_PHY_MESON_GXL_USB) += phy-meson-gxl-usb2.o
obj-y += broadcom/ \
hisilicon/ \
marvell/ \
diff --git a/drivers/phy/phy-meson-gxl-usb2.c b/drivers/phy/phy-meson-gxl-usb2.c
new file mode 100644
index 0000000..f5fbd3c

View file

@ -0,0 +1,28 @@
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
index 3f0d83b..7590383 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dts
@@ -129,6 +129,10 @@
};
};
+&usb0 {
+ status = "okay";
+};
+
/* SD card */
&sd_emmc_b {
status = "okay";
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index f252201..10b2165 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o
obj-$(CONFIG_PHY_MT65XX_USB3) += phy-mt65xx-usb3.o
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
-
+obj-$(CONFIG_PHY_MESON_GXL_USB) += phy-meson-gxl-usb2.o
obj-$(CONFIG_ARCH_SUNXI) += allwinner/
obj-$(CONFIG_ARCH_MESON) += amlogic/
obj-$(CONFIG_ARCH_RENESAS) += renesas/

View file

@ -0,0 +1,15 @@
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
old mode 100644
new mode 100755
index 3216e09..21bce28
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -44,7 +44,7 @@ static pgprot_t __get_dma_pgprot(unsigned long attrs, pgprot_t prot,
static struct gen_pool *atomic_pool;
-#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K
+#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_2M
static size_t atomic_pool_size __initdata = DEFAULT_DMA_COHERENT_POOL_SIZE;
static int __init early_coherent_pool(char *p)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
--- a/scripts/package/builddeb
+++ b/scripts/package/builddeb
@@ -218,7 +218,7 @@
for script in postinst postrm preinst prerm ; do
mkdir -p "$tmpdir$debhookdir/$script.d"
cat <<EOF > "$tmpdir/DEBIAN/$script"
-#!/bin/sh
+#!/bin/bash
set -e

View file

@ -0,0 +1,198 @@
diff --git a/scripts/package/builddeb b/scripts/package/builddeb
index 807c9cd..9b9435f 100755
--- a/scripts/package/builddeb
+++ b/scripts/package/builddeb
@@ -29,6 +29,28 @@ create_package() {
# in case we are in a restrictive umask environment like 0077
chmod -R a+rX "$pdir"
+ # Create preinstall and post install script to remove dtb
+ if [[ "$1" == *dtb* ]]; then
+ echo "if [ -d /boot/dtb-$version ]; then mv /boot/dtb-$version /boot/dtb-$version.old; fi" >> $pdir/DEBIAN/preinst
+ echo "if [ -d /boot/dtb.old ]; then rm -rf /boot/dtb.old; fi" >> $pdir/DEBIAN/preinst
+ echo "if [ -d /dtb ]; then mv /dtb /dtb.old; fi" >> $pdir/DEBIAN/preinst
+ echo "if [ -d /boot/dtb ]; then mv /boot/dtb /boot/dtb.old; fi" >> $pdir/DEBIAN/preinst
+ echo "exit 0" >> $pdir/DEBIAN/preinst
+ chmod 775 $pdir/DEBIAN/preinst
+ #
+ echo "if [ -d /boot/dtb-$version.old ]; then rm -rf /boot/dtb-$version.old; fi" >> $pdir/DEBIAN/postinst
+ echo "ln -sf dtb-$version /boot/dtb > /dev/null 2>&1 || mv /boot/dtb-$version /boot/dtb" >> $pdir/DEBIAN/postinst
+ echo "exit 0" >> $pdir/DEBIAN/postinst
+ chmod 775 $pdir/DEBIAN/postinst
+ fi
+
+ # Create postinstall script for headers
+ if [[ "$1" == *headers* ]]; then
+ echo "cd /usr/src/linux-headers-$version; echo \"Compiling headers - please wait ...\"; make -s scripts >/dev/null 2>&1" >> $pdir/DEBIAN/postinst
+ echo "exit 0" >> $pdir/DEBIAN/postinst
+ chmod 775 $pdir/DEBIAN/postinst
+ fi
+
# Create the package
dpkg-gencontrol $forcearch -Vkernel:debarch="${debarch}" -p$pname -P"$pdir"
dpkg --build "$pdir" ..
@@ -95,11 +117,13 @@ tmpdir="$objtree/debian/tmp"
fwdir="$objtree/debian/fwtmp"
kernel_headers_dir="$objtree/debian/hdrtmp"
libc_headers_dir="$objtree/debian/headertmp"
+dtb_dir="$objtree/debian/dtbtmp"
dbg_dir="$objtree/debian/dbgtmp"
-packagename=linux-image-$version
-fwpackagename=linux-firmware-image-$version
-kernel_headers_packagename=linux-headers-$version
-libc_headers_packagename=linux-libc-dev
+packagename=linux-image-next"$LOCALVERSION"
+fwpackagename=linux-firmware-image-next"$LOCALVERSION"
+kernel_headers_packagename=linux-headers-next"$LOCALVERSION"
+dtb_packagename=linux-dtb-next"$LOCALVERSION"
+libc_headers_packagename=linux-libc-next"$LOCALVERSION"
dbg_packagename=$packagename-dbg
debarch=
forcearch=
@@ -126,7 +150,9 @@ esac
BUILD_DEBUG="$(grep -s '^CONFIG_DEBUG_INFO=y' $KCONFIG_CONFIG || true)"
# Setup the directory structure
-rm -rf "$tmpdir" "$fwdir" "$kernel_headers_dir" "$libc_headers_dir" "$dbg_dir" $objtree/debian/files
+rm -rf "$tmpdir" "$fwdir" "$kernel_headers_dir" "$libc_headers_dir" "$dbg_dir" "$dtb_dir" $objtree/debian/files
+mkdir -m 755 -p "$dtb_dir/DEBIAN"
+mkdir -p "$dtb_dir/boot/dtb-$version" "$dtb_dir/usr/share/doc/$dtb_packagename"
mkdir -m 755 -p "$tmpdir/DEBIAN"
mkdir -p "$tmpdir/lib" "$tmpdir/boot"
mkdir -p "$fwdir/lib/firmware/$version/"
@@ -180,6 +206,11 @@ if grep -q '^CONFIG_MODULES=y' $KCONFIG_CONFIG ; then
fi
fi
+if grep -q '^CONFIG_OF=y' $KCONFIG_CONFIG ; then
+ #mkdir -p "$tmpdir/boot/dtb"
+ INSTALL_DTBS_PATH="$dtb_dir/boot/dtb-$version" $MAKE KBUILD_SRC= dtbs_install
+fi
+
if [ "$ARCH" != "um" ]; then
$MAKE headers_check KBUILD_SRC=
$MAKE headers_install KBUILD_SRC= INSTALL_HDR_PATH="$libc_headers_dir/usr"
@@ -192,7 +223,7 @@ fi
# so do we; recent versions of dracut and initramfs-tools will obey this.
debhookdir=${KDEB_HOOKDIR:-/etc/kernel}
if grep -q '^CONFIG_BLK_DEV_INITRD=y' $KCONFIG_CONFIG; then
- want_initrd=Yes
+ want_initrd=Yes
else
want_initrd=No
fi
@@ -204,9 +235,11 @@ for script in postinst postrm preinst prerm ; do
set -e
# Pass maintainer script parameters to hook scripts
+
export DEB_MAINT_PARAMS="\$*"
# Tell initramfs builder whether it's wanted
+
export INITRD=$want_initrd
test -d $debhookdir/$script.d && run-parts --arg="$version" --arg="/$installed_image_path" $debhookdir/$script.d
@@ -215,6 +248,55 @@ EOF
chmod 755 "$tmpdir/DEBIAN/$script"
done
+##
+## Create sym link to kernel image
+##
+sed -e "s/set -e//g" -i $tmpdir/DEBIAN/postinst
+sed -e "s/exit 0//g" -i $tmpdir/DEBIAN/postinst
+cat >> $tmpdir/DEBIAN/postinst <<EOT
+if [ "\$(grep nand /proc/partitions)" != "" ] && [ "\$(grep mmc /proc/partitions)" = "" ]; then
+mkimage -A arm -O linux -T kernel -C none -a "0x40008000" -e "0x40008000" -n "Linux kernel" -d /$installed_image_path /boot/uImage > /dev/null 2>&1
+cp /boot/uImage /tmp/uImage
+sync
+mountpoint -q /boot || mount /boot
+cp /tmp/uImage /boot/uImage
+rm -f /$installed_image_path
+else
+ln -sf $(basename $installed_image_path) /boot/zImage > /dev/null 2>&1 || mv /$installed_image_path /boot/zImage
+fi
+touch /boot/.next
+exit 0
+EOT
+##
+## FAT install workaround
+##
+sed -e "s/set -e//g" -i $tmpdir/DEBIAN/preinst
+sed -e "s/exit 0//g" -i $tmpdir/DEBIAN/preinst
+cat >> $tmpdir/DEBIAN/preinst <<EOT
+# exit if we are running chroot
+if [ "\$(stat -c %d:%i /)" != "\$(stat -c %d:%i /proc/1/root/.)" ]; then exit 0; fi
+
+check_and_unmount (){
+boot_device=\$(mountpoint -d /boot)
+
+for file in /dev/* ; do
+ CURRENT_DEVICE=\$(printf "%d:%d" \$(stat --printf="0x%t 0x%T" \$file))
+ if [[ "\$CURRENT_DEVICE" = "\$boot_device" ]]; then
+ boot_partition=\$file
+ break;
+ fi
+done
+
+bootfstype=\$(blkid -s TYPE -o value \$boot_partition)
+if [ "\$bootfstype" = "vfat" ]; then
+umount /boot;
+rm -f /boot/System.map* /boot/config* /boot/vmlinuz* /boot/zImage /boot/uImage
+fi
+}
+mountpoint -q /boot && check_and_unmount
+EOT
+echo "exit 0" >> $tmpdir/DEBIAN/preinst
+
# Try to determine maintainer and email values
if [ -n "$DEBEMAIL" ]; then
email=$DEBEMAIL
@@ -329,12 +411,20 @@ if grep -q '^CONFIG_GCC_PLUGINS=y' $KCONFIG_CONFIG ; then
fi
destdir=$kernel_headers_dir/usr/src/linux-headers-$version
mkdir -p "$destdir"
+######################## headers patch
+ZACNI=$(pwd)
+cd $destdir
+patch -p1 < /tmp/headers-debian-byteshift.patch
+cd $ZACNI
+######################## headers patch
(cd $srctree; tar -c -f - -T -) < "$objtree/debian/hdrsrcfiles" | (cd $destdir; tar -xf -)
(cd $objtree; tar -c -f - -T -) < "$objtree/debian/hdrobjfiles" | (cd $destdir; tar -xf -)
(cd $objtree; cp $KCONFIG_CONFIG $destdir/.config) # copy .config manually to be where it's expected to be
ln -sf "/usr/src/linux-headers-$version" "$kernel_headers_dir/lib/modules/$version/build"
rm -f "$objtree/debian/hdrsrcfiles" "$objtree/debian/hdrobjfiles"
+(cd "$destdir"; make M=scripts clean)
+
cat <<EOF >> debian/control
Package: $kernel_headers_packagename
@@ -363,6 +453,16 @@ fi
cat <<EOF >> debian/control
+Package: $dtb_packagename
+Architecture: any
+Description: Linux DTB, version $version
+ This package contains device blobs from the Linux kernel, version $version.
+EOF
+
+create_package "$dtb_packagename" "$dtb_dir"
+
+cat <<EOF >> debian/control
+
Package: $libc_headers_packagename
Section: devel
Provides: linux-kernel-headers
@@ -374,7 +474,7 @@ EOF
if [ "$ARCH" != "um" ]; then
create_package "$kernel_headers_packagename" "$kernel_headers_dir"
- create_package "$libc_headers_packagename" "$libc_headers_dir"
+# create_package "$libc_headers_packagename" "$libc_headers_dir"
fi
create_package "$packagename" "$tmpdir"

View file

@ -0,0 +1,14 @@
diff --git a/scripts/package/builddeb b/scripts/package/builddeb
index 750d70e..8fc16c9 100755
--- a/scripts/package/builddeb
+++ b/scripts/package/builddeb
@@ -267,7 +267,8 @@ mountpoint -q /boot || mount /boot
cp /tmp/uImage /boot/uImage
rm -f /$installed_image_path
else
-ln -sf $(basename $installed_image_path) /boot/zImage > /dev/null 2>&1 || mv /$installed_image_path /boot/zImage
+rm -f /boot/zImage;gunzip -c /boot/$(basename $installed_image_path) > /boot/xImage
+mkimage -A arm64 -O linux -T kernel -C none -a 0x1080000 -e 0x1080000 -n "Linux kernel" -d /boot/xImage /boot/uImage; rm -f /boot/xImage
fi
touch /boot/.next
exit 0