diff options
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 c0d97cca338..22e379681d3 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 1de4346ec7a..d0bf51bab66 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 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, +}; 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, +}; | 
