[U-Boot] [PATCH 0/4] rockchip: sound: Add support for sound on chromebook_jerry

Sound support for jerry was never included in the initial port. Now that sound has been converted to driver model it seems like a good opportunity to fill this gap. Jerry uses an audio codec that is already supported by U-Boot.
Add the required drivers and enable the config so that 'sound play 500 400' can be used to play a beep. Also enable logging for this board.
Simon Glass (4): rockchip: rk3288: Add i2s pinctrl and clock support rockchip: Add an I2S driver rockchip: Add a sound driver sound: rockchip: Add sound support for jerry
arch/arm/dts/rk3288-veyron-jerry.dts | 12 ++ arch/arm/dts/rk3288-veyron.dtsi | 1 + arch/arm/dts/rk3288.dtsi | 1 + .../include/asm/arch-rockchip/cru_rk3288.h | 8 + .../include/asm/arch-rockchip/grf_rk3288.h | 96 +++++++++++ arch/arm/include/asm/arch-rockchip/periph.h | 1 + configs/chromebook_jerry_defconfig | 7 + drivers/clk/rockchip/clk_rk3288.c | 48 ++++++ drivers/pinctrl/rockchip/pinctrl_rk3288.c | 14 ++ drivers/sound/Kconfig | 9 ++ drivers/sound/Makefile | 1 + drivers/sound/rockchip_i2s.c | 149 ++++++++++++++++++ drivers/sound/rockchip_sound.c | 132 ++++++++++++++++ 13 files changed, 479 insertions(+) create mode 100644 drivers/sound/rockchip_i2s.c create mode 100644 drivers/sound/rockchip_sound.c

Add support for setting pinctrl and clock for I2S on rk3288. This allows the sound driver to operate. These settings were created by rkmux.py
Signed-off-by: Simon Glass sjg@chromium.org ---
.../include/asm/arch-rockchip/cru_rk3288.h | 8 ++ .../include/asm/arch-rockchip/grf_rk3288.h | 96 +++++++++++++++++++ arch/arm/include/asm/arch-rockchip/periph.h | 1 + drivers/clk/rockchip/clk_rk3288.c | 48 ++++++++++ drivers/pinctrl/rockchip/pinctrl_rk3288.c | 14 +++ 5 files changed, 167 insertions(+)
diff --git a/arch/arm/include/asm/arch-rockchip/cru_rk3288.h b/arch/arm/include/asm/arch-rockchip/cru_rk3288.h index 0475598b77b..e891f20b373 100644 --- a/arch/arm/include/asm/arch-rockchip/cru_rk3288.h +++ b/arch/arm/include/asm/arch-rockchip/cru_rk3288.h @@ -75,6 +75,14 @@ enum { MMC0_DIV_MASK = 0x3f << MMC0_DIV_SHIFT, };
+/* CRU_CLKSEL8_CON */ +enum { + I2S0_FRAC_DENOM_SHIFT = 0, + I2S0_FRAC_DENOM_MASK = 0xffff << I2S0_FRAC_DENOM_SHIFT, + I2S0_FRAC_NUMER_SHIFT = 16, + I2S0_FRAC_NUMER_MASK = 0xffffu << I2S0_FRAC_NUMER_SHIFT, +}; + /* CRU_CLKSEL12_CON */ enum { EMMC_PLL_SHIFT = 0xe, diff --git a/arch/arm/include/asm/arch-rockchip/grf_rk3288.h b/arch/arm/include/asm/arch-rockchip/grf_rk3288.h index c235607cee5..77295446c63 100644 --- a/arch/arm/include/asm/arch-rockchip/grf_rk3288.h +++ b/arch/arm/include/asm/arch-rockchip/grf_rk3288.h @@ -561,6 +561,49 @@ enum { GPIO5C0_TS0_SYNC, };
+/* GRF_GPIO6A_IOMUX */ +enum { + GPIO6A7_SHIFT = 0xe, + GPIO6A7_MASK = 1, + GPIO6A7_GPIO = 0, + GPIO6A7_I2S_SDO3, + + GPIO6A6_SHIFT = 0xc, + GPIO6A6_MASK = 1, + GPIO6A6_GPIO = 0, + GPIO6A6_I2S_SDO2, + + GPIO6A5_SHIFT = 0xa, + GPIO6A5_MASK = 1, + GPIO6A5_GPIO = 0, + GPIO6A5_I2S_SDO1, + + GPIO6A4_SHIFT = 8, + GPIO6A4_MASK = 1, + GPIO6A4_GPIO = 0, + GPIO6A4_I2S_SDO0, + + GPIO6A3_SHIFT = 6, + GPIO6A3_MASK = 1, + GPIO6A3_GPIO = 0, + GPIO6A3_I2S_SDI, + + GPIO6A2_SHIFT = 4, + GPIO6A2_MASK = 1, + GPIO6A2_GPIO = 0, + GPIO6A2_I2S_LRCKTX, + + GPIO6A1_SHIFT = 2, + GPIO6A1_MASK = 1, + GPIO6A1_GPIO = 0, + GPIO6A1_I2S_LRCKRX, + + GPIO6A0_SHIFT = 0, + GPIO6A0_MASK = 1, + GPIO6A0_GPIO = 0, + GPIO6A0_I2S_SCLK, +}; + /* GRF_GPIO6B_IOMUX */ enum { GPIO6B3_SHIFT = 6, @@ -1042,6 +1085,59 @@ enum GRF_SOC_CON8 { RK3288_DPHY_TX0_TURNREQUEST_DIS = 0, };
+/* GRF_IO_VSEL */ +enum { + GPIO1830_V18SEL_SHIFT = 9, + GPIO1830_V18SEL_MASK = 1, + GPIO1830_V18SEL_3_3V = 0, + GPIO1830_V18SEL_1_8V, + + GPIO30_V18SEL_SHIFT = 8, + GPIO30_V18SEL_MASK = 1, + GPIO30_V18SEL_3_3V = 0, + GPIO30_V18SEL_1_8V, + + SDCARD_V18SEL_SHIFT = 7, + SDCARD_V18SEL_MASK = 1, + SDCARD_V18SEL_3_3V = 0, + SDCARD_V18SEL_1_8V, + + AUDIO_V18SEL_SHIFT = 6, + AUDIO_V18SEL_MASK = 1, + AUDIO_V18SEL_3_3V = 0, + AUDIO_V18SEL_1_8V, + + BB_V18SEL_SHIFT = 5, + BB_V18SEL_MASK = 1, + BB_V18SEL_3_3V = 0, + BB_V18SEL_1_8V, + + WIFI_V18SEL_SHIFT = 4, + WIFI_V18SEL_MASK = 1, + WIFI_V18SEL_3_3V = 0, + WIFI_V18SEL_1_8V, + + FLASH1_V18SEL_SHIFT = 3, + FLASH1_V18SEL_MASK = 1, + FLASH1_V18SEL_3_3V = 0, + FLASH1_V18SEL_1_8V, + + FLASH0_V18SEL_SHIFT = 2, + FLASH0_V18SEL_MASK = 1, + FLASH0_V18SEL_3_3V = 0, + FLASH0_V18SEL_1_8V, + + DVP_V18SEL_SHIFT = 1, + DVP_V18SEL_MASK = 1, + DVP_V18SEL_3_3V = 0, + DVP_V18SEL_1_8V, + + LCDC_V18SEL_SHIFT = 0, + LCDC_V18SEL_MASK = 1, + LCDC_V18SEL_3_3V = 0, + LCDC_V18SEL_1_8V, +}; + /* GPIO Bias settings */ enum GPIO_BIAS { GPIO_BIAS_2MA = 0, diff --git a/arch/arm/include/asm/arch-rockchip/periph.h b/arch/arm/include/asm/arch-rockchip/periph.h index 514baf6a535..2191b7d43a8 100644 --- a/arch/arm/include/asm/arch-rockchip/periph.h +++ b/arch/arm/include/asm/arch-rockchip/periph.h @@ -45,6 +45,7 @@ enum periph_id { PERIPH_ID_HDMI, PERIPH_ID_GMAC, PERIPH_ID_SFC, + PERIPH_ID_I2S,
PERIPH_ID_COUNT,
diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c index 4a6e5c7113d..930c99f4d9f 100644 --- a/drivers/clk/rockchip/clk_rk3288.c +++ b/drivers/clk/rockchip/clk_rk3288.c @@ -6,6 +6,7 @@ #include <common.h> #include <bitfield.h> #include <clk-uclass.h> +#include <div64.h> #include <dm.h> #include <dt-structs.h> #include <errno.h> @@ -371,6 +372,50 @@ static int rockchip_vop_set_clk(struct rk3288_cru *cru, struct rk3288_grf *grf,
return 0; } + +static u32 rockchip_clk_gcd(u32 a, u32 b) +{ + while (b != 0) { + int r = b; + + b = a % b; + a = r; + } + return a; +} + +static ulong rockchip_i2s_get_clk(struct rk3288_cru *cru, uint gclk_rate) +{ + unsigned long long rate; + uint val; + int n, d; + + val = readl(&cru->cru_clksel_con[8]); + n = (val & I2S0_FRAC_NUMER_MASK) >> I2S0_FRAC_NUMER_SHIFT; + d = (val & I2S0_FRAC_DENOM_MASK) >> I2S0_FRAC_DENOM_SHIFT; + + rate = (unsigned long long)gclk_rate * n; + do_div(rate, d); + + return (ulong)rate; +} + +static ulong rockchip_i2s_set_clk(struct rk3288_cru *cru, uint gclk_rate, + uint freq) +{ + int n, d; + int v; + + /* set frac divider */ + v = rockchip_clk_gcd(gclk_rate, freq); + n = gclk_rate / v; + d = freq / v; + assert(freq == gclk_rate / n * d); + writel(d << I2S0_FRAC_NUMER_SHIFT | n << I2S0_FRAC_DENOM_SHIFT, + &cru->cru_clksel_con[8]); + + return rockchip_i2s_get_clk(cru, gclk_rate); +} #endif /* CONFIG_SPL_BUILD */
static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf) @@ -769,6 +814,9 @@ static ulong rk3288_clk_set_rate(struct clk *clk, ulong rate) new_rate = rockchip_spi_set_clk(cru, gclk_rate, clk->id, rate); break; #ifndef CONFIG_SPL_BUILD + case SCLK_I2S0: + new_rate = rockchip_i2s_set_clk(cru, gclk_rate, rate); + break; case SCLK_MAC: new_rate = rockchip_mac_set_clk(priv->cru, rate); break; diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3288.c b/drivers/pinctrl/rockchip/pinctrl_rk3288.c index 3e01cfd98f4..fd19cfb51cc 100644 --- a/drivers/pinctrl/rockchip/pinctrl_rk3288.c +++ b/drivers/pinctrl/rockchip/pinctrl_rk3288.c @@ -531,6 +531,17 @@ static void pinctrl_rk3288_hdmi_config(struct rk3288_grf *grf, int hdmi_id) break; } } + +static void pinctrl_rk3288_i2s_config(struct rk3288_grf *grf) +{ + rk_setreg(&grf->gpio6a_iomux, GPIO6A4_I2S_SDO0 << GPIO6A4_SHIFT | + GPIO6A3_I2S_SDI << GPIO6A3_SHIFT | + GPIO6A2_I2S_LRCKTX << GPIO6A2_SHIFT | + GPIO6A1_I2S_LRCKRX << GPIO6A1_SHIFT | + GPIO6A0_I2S_SCLK << GPIO6A0_SHIFT); + rk_setreg(&grf->gpio6b_iomux, GPIO6B0_I2S_CLK << GPIO6B0_SHIFT); + rk_setreg(&grf->io_vsel, AUDIO_V18SEL_1_8V << AUDIO_V18SEL_SHIFT); +} #endif
static int rk3288_pinctrl_request(struct udevice *dev, int func, int flags) @@ -567,6 +578,9 @@ static int rk3288_pinctrl_request(struct udevice *dev, int func, int flags) pinctrl_rk3288_uart_config(priv->grf, func); break; #ifndef CONFIG_SPL_BUILD + case PERIPH_ID_I2S: + pinctrl_rk3288_i2s_config(priv->grf); + break; case PERIPH_ID_LCDC0: case PERIPH_ID_LCDC1: pinctrl_rk3288_lcdc_config(priv->grf, func);

Add support for setting pinctrl and clock for I2S on rk3288. This allows the sound driver to operate. These settings were created by rkmux.py
Signed-off-by: Simon Glass sjg@chromium.org
.../include/asm/arch-rockchip/cru_rk3288.h | 8 ++ .../include/asm/arch-rockchip/grf_rk3288.h | 96 +++++++++++++++++++ arch/arm/include/asm/arch-rockchip/periph.h | 1 + drivers/clk/rockchip/clk_rk3288.c | 48 ++++++++++ drivers/pinctrl/rockchip/pinctrl_rk3288.c | 14 +++ 5 files changed, 167 insertions(+)
Reviewed-by: Philipp Tomsich philipp.tomsich@theobroma-systems.com

Add support for setting pinctrl and clock for I2S on rk3288. This allows the sound driver to operate. These settings were created by rkmux.py
Signed-off-by: Simon Glass sjg@chromium.org Reviewed-by: Philipp Tomsich philipp.tomsich@theobroma-systems.com
.../include/asm/arch-rockchip/cru_rk3288.h | 8 ++ .../include/asm/arch-rockchip/grf_rk3288.h | 96 +++++++++++++++++++ arch/arm/include/asm/arch-rockchip/periph.h | 1 + drivers/clk/rockchip/clk_rk3288.c | 48 ++++++++++ drivers/pinctrl/rockchip/pinctrl_rk3288.c | 14 +++ 5 files changed, 167 insertions(+)
Applied to u-boot-rockchip, thanks!

Add a driver for I2S which allows audio data to be sent from the SoC to the audio codec. The sample rate and other settings are hard-coded for now as there is no suitable device-tree binding available.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/sound/Kconfig | 9 +++ drivers/sound/Makefile | 1 + drivers/sound/rockchip_i2s.c | 149 +++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 drivers/sound/rockchip_i2s.c
diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig index cdd8d0f046c..fef6403a86f 100644 --- a/drivers/sound/Kconfig +++ b/drivers/sound/Kconfig @@ -21,6 +21,15 @@ config I2S I2S. It calls either of the two supported codecs (no use is made of driver model at present).
+config I2S_ROCKCHIP + bool "Enable I2S support for Rockchip SoCs" + depends on I2S + help + Rockchip SoCs support an I2S interface for sending audio data to an + audio codec. This option enables support for this, using one of the + available audio codec drivers. This driver does not make use of + DMA, but writes each word directly to the hardware. + config I2S_SAMSUNG bool "Enable I2C support for Samsung SoCs" depends on I2S diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 6925f0b6ec2..061f5f7052a 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_SOUND) += i2s-uclass.o obj-$(CONFIG_SOUND) += sound-uclass.o obj-$(CONFIG_I2S_SAMSUNG) += samsung-i2s.o obj-$(CONFIG_SOUND_SANDBOX) += sandbox.o +obj-$(CONFIG_I2S_ROCKCHIP) += rockchip_i2s.o obj-$(CONFIG_I2S_SAMSUNG) += samsung_sound.o obj-$(CONFIG_I2S_TEGRA) += tegra_ahub.o tegra_i2s.o tegra_sound.o obj-$(CONFIG_SOUND_WM8994) += wm8994.o diff --git a/drivers/sound/rockchip_i2s.c b/drivers/sound/rockchip_i2s.c new file mode 100644 index 00000000000..e5df8ca0d2b --- /dev/null +++ b/drivers/sound/rockchip_i2s.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google LLC + * Copyright 2014 Rockchip Electronics Co., Ltd. + * Taken from dc i2s/rockchip.c + */ + +#define LOG_CATEGORY UCLASS_I2S + +#include <common.h> +#include <dm.h> +#include <i2s.h> +#include <sound.h> +#include <asm/io.h> + +struct rk_i2s_regs { + u32 txcr; /* I2S_TXCR, 0x00 */ + u32 rxcr; /* I2S_RXCR, 0x04 */ + u32 ckr; /* I2S_CKR, 0x08 */ + u32 fifolr; /* I2S_FIFOLR, 0x0C */ + u32 dmacr; /* I2S_DMACR, 0x10 */ + u32 intcr; /* I2S_INTCR, 0x14 */ + u32 intsr; /* I2S_INTSR, 0x18 */ + u32 xfer; /* I2S_XFER, 0x1C */ + u32 clr; /* I2S_CLR, 0x20 */ + u32 txdr; /* I2S_TXDR, 0x24 */ + u32 rxdr; /* I2S_RXDR, 0x28 */ +}; + +enum { + /* I2S_XFER */ + I2S_RX_TRAN_BIT = BIT(1), + I2S_TX_TRAN_BIT = BIT(0), + I2S_TRAN_MASK = 3 << 0, + + /* I2S_TXCKR */ + I2S_MCLK_DIV_SHIFT = 16, + I2S_MCLK_DIV_MASK = (0xff << I2S_MCLK_DIV_SHIFT), + + I2S_RX_SCLK_DIV_SHIFT = 8, + I2S_RX_SCLK_DIV_MASK = 0xff << I2S_RX_SCLK_DIV_SHIFT, + I2S_TX_SCLK_DIV_SHIFT = 0, + I2S_TX_SCLK_DIV_MASK = 0xff << I2S_TX_SCLK_DIV_SHIFT, + + I2S_DATA_WIDTH_SHIFT = 0, + I2S_DATA_WIDTH_MASK = 0x1f << I2S_DATA_WIDTH_SHIFT, +}; + +static int rockchip_i2s_init(struct i2s_uc_priv *priv) +{ + struct rk_i2s_regs *regs = (struct rk_i2s_regs *)priv->base_address; + u32 bps = priv->bitspersample; + u32 lrf = priv->rfs; + u32 chn = priv->channels; + u32 mode = 0; + + clrbits_le32(®s->xfer, I2S_TX_TRAN_BIT); + mode = readl(®s->txcr) & ~0x1f; + switch (priv->bitspersample) { + case 16: + case 24: + mode |= (priv->bitspersample - 1) << I2S_DATA_WIDTH_SHIFT; + break; + default: + log_err("Invalid sample size input %d\n", priv->bitspersample); + return -EINVAL; + } + writel(mode, ®s->txcr); + + mode = readl(®s->ckr) & ~I2S_MCLK_DIV_MASK; + mode |= (lrf / (bps * chn) - 1) << I2S_MCLK_DIV_SHIFT; + + mode &= ~I2S_TX_SCLK_DIV_MASK; + mode |= (priv->bitspersample * priv->channels - 1) << + I2S_TX_SCLK_DIV_SHIFT; + writel(mode, ®s->ckr); + + return 0; +} + +static int i2s_send_data(struct rk_i2s_regs *regs, u32 *data, uint length) +{ + for (int i = 0; i < min(32u, length); i++) + writel(*data++, ®s->txdr); + + length -= min(32u, length); + + /* enable both tx and rx */ + setbits_le32(®s->xfer, I2S_TRAN_MASK); + while (length) { + if ((readl(®s->fifolr) & 0x3f) < 0x20) { + writel(*data++, ®s->txdr); + length--; + } + } + while (readl(®s->fifolr) & 0x3f) + /* wait until FIFO empty */; + clrbits_le32(®s->xfer, I2S_TRAN_MASK); + writel(0, ®s->clr); + + return 0; +} + +static int rockchip_i2s_tx_data(struct udevice *dev, void *data, uint data_size) +{ + struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); + struct rk_i2s_regs *regs = (struct rk_i2s_regs *)priv->base_address; + + return i2s_send_data(regs, data, data_size / sizeof(u32)); +} + +static int rockchip_i2s_probe(struct udevice *dev) +{ + struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); + ulong base; + + base = dev_read_addr(dev); + if (base == FDT_ADDR_T_NONE) { + log_debug("Missing i2s base\n"); + return -EINVAL; + } + priv->base_address = base; + priv->id = 1; + priv->audio_pll_clk = 4800000; + priv->samplingrate = 48000; + priv->bitspersample = 16; + priv->channels = 2; + priv->rfs = 256; + priv->bfs = 32; + + return rockchip_i2s_init(priv); +} + +static const struct i2s_ops rockchip_i2s_ops = { + .tx_data = rockchip_i2s_tx_data, +}; + +static const struct udevice_id rockchip_i2s_ids[] = { + { .compatible = "rockchip,rk3288-i2s" }, + { } +}; + +U_BOOT_DRIVER(rockchip_i2s) = { + .name = "rockchip_i2s", + .id = UCLASS_I2S, + .of_match = rockchip_i2s_ids, + .probe = rockchip_i2s_probe, + .ops = &rockchip_i2s_ops, +};

Add a driver for I2S which allows audio data to be sent from the SoC to the audio codec. The sample rate and other settings are hard-coded for now as there is no suitable device-tree binding available.
Signed-off-by: Simon Glass sjg@chromium.org
drivers/sound/Kconfig | 9 +++ drivers/sound/Makefile | 1 + drivers/sound/rockchip_i2s.c | 149 +++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 drivers/sound/rockchip_i2s.c
Reviewed-by: Philipp Tomsich philipp.tomsich@theobroma-systems.com

Add a driver for I2S which allows audio data to be sent from the SoC to the audio codec. The sample rate and other settings are hard-coded for now as there is no suitable device-tree binding available.
Signed-off-by: Simon Glass sjg@chromium.org Reviewed-by: Philipp Tomsich philipp.tomsich@theobroma-systems.com
drivers/sound/Kconfig | 9 +++ drivers/sound/Makefile | 1 + drivers/sound/rockchip_i2s.c | 149 +++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 drivers/sound/rockchip_i2s.c
Applied to u-boot-rockchip, thanks!

Add a sound driver for rk3288 supporting chromebook_jerry. This uses the I2S driver, and existing audio codec and the clock/pinmux support.
Signed-off-by: Simon Glass sjg@chromium.org ---
drivers/sound/Makefile | 2 +- drivers/sound/rockchip_sound.c | 132 +++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 drivers/sound/rockchip_sound.c
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 061f5f7052a..15c3745b416 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -9,7 +9,7 @@ obj-$(CONFIG_SOUND) += i2s-uclass.o obj-$(CONFIG_SOUND) += sound-uclass.o obj-$(CONFIG_I2S_SAMSUNG) += samsung-i2s.o obj-$(CONFIG_SOUND_SANDBOX) += sandbox.o -obj-$(CONFIG_I2S_ROCKCHIP) += rockchip_i2s.o +obj-$(CONFIG_I2S_ROCKCHIP) += rockchip_i2s.o rockchip_sound.o obj-$(CONFIG_I2S_SAMSUNG) += samsung_sound.o obj-$(CONFIG_I2S_TEGRA) += tegra_ahub.o tegra_i2s.o tegra_sound.o obj-$(CONFIG_SOUND_WM8994) += wm8994.o diff --git a/drivers/sound/rockchip_sound.c b/drivers/sound/rockchip_sound.c new file mode 100644 index 00000000000..e7fb9fb1646 --- /dev/null +++ b/drivers/sound/rockchip_sound.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google, LLC + * Written by Simon Glass sjg@chromium.org + */ + +#define LOG_CATEGORY UCLASS_SOUND + +#include <common.h> +#include <audio_codec.h> +#include <clk.h> +#include <dm.h> +#include <i2s.h> +#include <misc.h> +#include <sound.h> +#include <asm/arch/periph.h> +#include <dm/pinctrl.h> + +static int rockchip_sound_setup(struct udevice *dev) +{ + struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct i2s_uc_priv *i2c_priv = dev_get_uclass_priv(uc_priv->i2s); + int ret; + + if (uc_priv->setup_done) + return -EALREADY; + ret = audio_codec_set_params(uc_priv->codec, i2c_priv->id, + i2c_priv->samplingrate, + i2c_priv->samplingrate * i2c_priv->rfs, + i2c_priv->bitspersample, + i2c_priv->channels); + if (ret) + return ret; + uc_priv->setup_done = true; + + return 0; +} + +static int rockchip_sound_play(struct udevice *dev, void *data, uint data_size) +{ + struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev); + + return i2s_tx_data(uc_priv->i2s, data, data_size); +} + +static int rockchip_sound_probe(struct udevice *dev) +{ + struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct ofnode_phandle_args args; + struct udevice *pinctrl; + struct clk clk; + ofnode node; + int ret; + + node = ofnode_find_subnode(dev_ofnode(dev), "cpu"); + if (!ofnode_valid(node)) { + log_debug("Failed to find /cpu subnode\n"); + return -EINVAL; + } + ret = ofnode_parse_phandle_with_args(node, "sound-dai", + "#sound-dai-cells", 0, 0, &args); + if (ret) { + log_debug("Cannot find i2s phandle: %d\n", ret); + return ret; + } + ret = uclass_get_device_by_ofnode(UCLASS_I2S, args.node, &uc_priv->i2s); + if (ret) { + log_debug("Cannot find i2s: %d\n", ret); + return ret; + } + + node = ofnode_find_subnode(dev_ofnode(dev), "codec"); + if (!ofnode_valid(node)) { + log_debug("Failed to find /codec subnode\n"); + return -EINVAL; + } + ret = ofnode_parse_phandle_with_args(node, "sound-dai", + "#sound-dai-cells", 0, 0, &args); + if (ret) { + log_debug("Cannot find codec phandle: %d\n", ret); + return ret; + } + ret = uclass_get_device_by_ofnode(UCLASS_AUDIO_CODEC, args.node, + &uc_priv->codec); + if (ret) { + log_debug("Cannot find audio codec: %d\n", ret); + return ret; + } + ret = clk_get_by_index(uc_priv->i2s, 1, &clk); + if (ret) { + log_debug("Cannot find clock: %d\n", ret); + return ret; + } + ret = clk_set_rate(&clk, 12288000); + if (ret < 0) { + log_debug("Cannot find clock: %d\n", ret); + return ret; + } + ret = uclass_get_device(UCLASS_PINCTRL, 0, &pinctrl); + if (ret) { + debug("%s: Cannot find pinctrl device\n", __func__); + return ret; + } + ret = pinctrl_request(pinctrl, PERIPH_ID_I2S, 0); + if (ret) { + debug("%s: Cannot select I2C pinctrl\n", __func__); + return ret; + } + + log_debug("Probed sound '%s' with codec '%s' and i2s '%s'\n", dev->name, + uc_priv->codec->name, uc_priv->i2s->name); + + return 0; +} + +static const struct sound_ops rockchip_sound_ops = { + .setup = rockchip_sound_setup, + .play = rockchip_sound_play, +}; + +static const struct udevice_id rockchip_sound_ids[] = { + { .compatible = "rockchip,audio-max98090-jerry" }, + { } +}; + +U_BOOT_DRIVER(rockchip_sound) = { + .name = "rockchip_sound", + .id = UCLASS_SOUND, + .of_match = rockchip_sound_ids, + .probe = rockchip_sound_probe, + .ops = &rockchip_sound_ops, +};

Add a sound driver for rk3288 supporting chromebook_jerry. This uses the I2S driver, and existing audio codec and the clock/pinmux support.
Signed-off-by: Simon Glass sjg@chromium.org
drivers/sound/Makefile | 2 +- drivers/sound/rockchip_sound.c | 132 +++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 drivers/sound/rockchip_sound.c
Reviewed-by: Philipp Tomsich philipp.tomsich@theobroma-systems.com

Add a sound driver for rk3288 supporting chromebook_jerry. This uses the I2S driver, and existing audio codec and the clock/pinmux support.
Signed-off-by: Simon Glass sjg@chromium.org Reviewed-by: Philipp Tomsich philipp.tomsich@theobroma-systems.com
drivers/sound/Makefile | 2 +- drivers/sound/rockchip_sound.c | 132 +++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 drivers/sound/rockchip_sound.c
Applied to u-boot-rockchip, thanks!

Jerry uses a max98090 audio codec and the internal SoC I2S peripheral. Enable sound support and add the required device-tree pieces.
Signed-off-by: Simon Glass sjg@chromium.org ---
arch/arm/dts/rk3288-veyron-jerry.dts | 12 ++++++++++++ arch/arm/dts/rk3288-veyron.dtsi | 1 + arch/arm/dts/rk3288.dtsi | 1 + configs/chromebook_jerry_defconfig | 7 +++++++ 4 files changed, 21 insertions(+)
diff --git a/arch/arm/dts/rk3288-veyron-jerry.dts b/arch/arm/dts/rk3288-veyron-jerry.dts index 42f52d4d99a..c251d9d5942 100644 --- a/arch/arm/dts/rk3288-veyron-jerry.dts +++ b/arch/arm/dts/rk3288-veyron-jerry.dts @@ -52,6 +52,18 @@ vin-supply = <&vcc33_sys>; startup-delay-us = <15000>; }; + + sound { + compatible = "rockchip,audio-max98090-jerry"; + + cpu { + sound-dai = <&i2s 0>; + }; + + codec { + sound-dai = <&max98090 0>; + }; + }; };
&dmc { diff --git a/arch/arm/dts/rk3288-veyron.dtsi b/arch/arm/dts/rk3288-veyron.dtsi index 92b68878fd0..49ba3f3f143 100644 --- a/arch/arm/dts/rk3288-veyron.dtsi +++ b/arch/arm/dts/rk3288-veyron.dtsi @@ -484,6 +484,7 @@ max98090: max98090@10 { compatible = "maxim,max98090"; reg = <0x10>; + #sound-dai-cells = <0>; interrupt-parent = <&gpio6>; interrupts = <7 IRQ_TYPE_EDGE_FALLING>; pinctrl-names = "default"; diff --git a/arch/arm/dts/rk3288.dtsi b/arch/arm/dts/rk3288.dtsi index 273d38c84f5..487d22c9b01 100644 --- a/arch/arm/dts/rk3288.dtsi +++ b/arch/arm/dts/rk3288.dtsi @@ -649,6 +649,7 @@ interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + #sound-dai-cells = <1>; dmas = <&dmac_bus_s 0>, <&dmac_bus_s 1>; dma-names = "tx", "rx"; clock-names = "i2s_hclk", "i2s_clk"; diff --git a/configs/chromebook_jerry_defconfig b/configs/chromebook_jerry_defconfig index 94a1af01be0..b1e119f9a26 100644 --- a/configs/chromebook_jerry_defconfig +++ b/configs/chromebook_jerry_defconfig @@ -14,6 +14,8 @@ CONFIG_DEBUG_UART=y CONFIG_NR_DRAM_BANKS=1 # CONFIG_ANDROID_BOOT_IMAGE is not set CONFIG_SILENT_CONSOLE=y +CONFIG_LOG=y +CONFIG_LOG_DEFAULT_LEVEL=7 CONFIG_DEFAULT_FDT_FILE="rk3288-veyron-jerry.dtb" # CONFIG_DISPLAY_CPUINFO is not set CONFIG_DISPLAY_BOARDINFO_LATE=y @@ -32,6 +34,7 @@ CONFIG_CMD_USB=y # CONFIG_CMD_SETEXPR is not set CONFIG_CMD_CACHE=y CONFIG_CMD_TIME=y +CONFIG_CMD_SOUND=y CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y # CONFIG_SPL_DOS_PARTITION is not set @@ -76,6 +79,10 @@ CONFIG_RAM=y CONFIG_SPL_RAM=y CONFIG_DEBUG_UART_SHIFT=2 CONFIG_ROCKCHIP_SERIAL=y +CONFIG_SOUND=y +CONFIG_I2S=y +CONFIG_I2S_ROCKCHIP=y +CONFIG_SOUND_MAX98090=y CONFIG_ROCKCHIP_SPI=y CONFIG_SYSRESET=y CONFIG_USB=y

Jerry uses a max98090 audio codec and the internal SoC I2S peripheral. Enable sound support and add the required device-tree pieces.
Signed-off-by: Simon Glass sjg@chromium.org
arch/arm/dts/rk3288-veyron-jerry.dts | 12 ++++++++++++ arch/arm/dts/rk3288-veyron.dtsi | 1 + arch/arm/dts/rk3288.dtsi | 1 + configs/chromebook_jerry_defconfig | 7 +++++++ 4 files changed, 21 insertions(+)
Reviewed-by: Philipp Tomsich philipp.tomsich@theobroma-systems.com

Jerry uses a max98090 audio codec and the internal SoC I2S peripheral. Enable sound support and add the required device-tree pieces.
Signed-off-by: Simon Glass sjg@chromium.org Reviewed-by: Philipp Tomsich philipp.tomsich@theobroma-systems.com
arch/arm/dts/rk3288-veyron-jerry.dts | 12 ++++++++++++ arch/arm/dts/rk3288-veyron.dtsi | 1 + arch/arm/dts/rk3288.dtsi | 1 + configs/chromebook_jerry_defconfig | 7 +++++++ 4 files changed, 21 insertions(+)
Applied to u-boot-rockchip, thanks!

On 28.12.2018, at 04:15, Simon Glass sjg@chromium.org wrote:
Sound support for jerry was never included in the initial port. Now that sound has been converted to driver model it seems like a good opportunity to fill this gap. Jerry uses an audio codec that is already supported by U-Boot.
Add the required drivers and enable the config so that 'sound play 500 400' can be used to play a beep. Also enable logging for this board.
I hope to find time to add support for the RK3399 (which has a sufficiently closely related I2S controller)—and possibly even the RK3368—and for the SGTL5000 codec that we have on the Haikou reference baseboard.
If you don’t hear anything by mid-January, feel free to poke me. Philipp.

Hi Philipp,
On Fri, 28 Dec 2018 at 12:07, Philipp Tomsich philipp.tomsich@theobroma-systems.com wrote:
On 28.12.2018, at 04:15, Simon Glass sjg@chromium.org wrote:
Sound support for jerry was never included in the initial port. Now that sound has been converted to driver model it seems like a good opportunity to fill this gap. Jerry uses an audio codec that is already supported by U-Boot.
Add the required drivers and enable the config so that 'sound play 500 400' can be used to play a beep. Also enable logging for this board.
I hope to find time to add support for the RK3399 (which has a sufficiently closely related I2S controller)—and possibly even the RK3368—and for the SGTL5000 codec that we have on the Haikou reference baseboard.
Sounds good! Really interested to see how the API works out.
I have been meaning to add support for kevin but have not got to it.
- Simon
If you don’t hear anything by mid-January, feel free to poke me. Philipp.
participants (2)
-
Philipp Tomsich
-
Simon Glass