diff options
author | Tom Rini <trini@konsulko.com> | 2019-02-02 10:11:20 -0500 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2019-02-02 10:11:20 -0500 |
commit | e5fd39c886485e3dec77f4438a6e364c2987cf5f (patch) | |
tree | 635a4987f759207efd147ff628d683f7389ab1a1 /drivers/sound | |
parent | 544d5e98f3657e4ac1966be8971586aa42dad8c4 (diff) | |
parent | 73ced87e9af70cba35c4374055dca56e5f9c460d (diff) |
Merge tag 'for-master-20190201' of git://git.denx.de/u-boot-rockchip
u-boot-rockchip changes for 2019.04-rc1:
* support for Chromebook Bob
* full pinctrl driver using DTS properties
* documentation improvements
* I2S support for some Rockchip SoCs
Diffstat (limited to 'drivers/sound')
-rw-r--r-- | drivers/sound/Kconfig | 9 | ||||
-rw-r--r-- | drivers/sound/Makefile | 1 | ||||
-rw-r--r-- | drivers/sound/rockchip_i2s.c | 149 | ||||
-rw-r--r-- | drivers/sound/rockchip_sound.c | 132 |
4 files changed, 291 insertions, 0 deletions
diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig index c0d97cca33..22e379681d 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 SOUND diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 1de4346ec7..d0bf51bab6 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 rockchip_sound.o obj-$(CONFIG_I2S_SAMSUNG) += samsung_sound.o obj-$(CONFIG_SOUND_WM8994) += wm8994.o obj-$(CONFIG_SOUND_MAX98090) += max98090.o maxim_codec.o diff --git a/drivers/sound/rockchip_i2s.c b/drivers/sound/rockchip_i2s.c new file mode 100644 index 0000000000..e5df8ca0d2 --- /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, +}; diff --git a/drivers/sound/rockchip_sound.c b/drivers/sound/rockchip_sound.c new file mode 100644 index 0000000000..e7fb9fb164 --- /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, +}; |