From e96fa6c91177d0aa81119d1d24cc0aa2dd663581 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 10 Dec 2018 10:37:34 -0700 Subject: [PATCH] dm: sound: Create a uclass for i2s The i2s bus is commonly used with audio codecs. It provides a way to stream digital data sychronously in both directions. U-Boot only supports audio output, so this uclass is very simple, with a single tx_data() method. Add a uclass and a test for i2s. Signed-off-by: Simon Glass --- arch/sandbox/dts/test.dts | 7 +++- arch/sandbox/include/asm/test.h | 10 ++++++ drivers/sound/Makefile | 1 + drivers/sound/i2s-uclass.c | 25 ++++++++++++++ drivers/sound/sandbox.c | 60 ++++++++++++++++++++++++++++++++- include/dm/uclass-id.h | 1 + include/i2s.h | 32 +++++++++++++++--- test/dm/Makefile | 1 + test/dm/i2s.c | 32 ++++++++++++++++++ 9 files changed, 163 insertions(+), 6 deletions(-) create mode 100644 drivers/sound/i2s-uclass.c create mode 100644 test/dm/i2s.c diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 9ced5beee8..5c2910c583 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -47,7 +47,7 @@ #sound-dai-cells = <1>; }; - cros_ec: cros-ec { + cros_ec: cros-ec { reg = <0 0>; compatible = "google,cros-ec-sandbox"; @@ -378,6 +378,11 @@ u-boot,dm-pre-reloc; }; + i2s: i2s { + compatible = "sandbox,i2s"; + #sound-dai-cells = <1>; + }; + misc-test { compatible = "sandbox,misc_sandbox"; }; diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index f70e0d8417..71bd50bd5b 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -131,4 +131,14 @@ void sandbox_get_codec_params(struct udevice *dev, int *interfacep, int *ratep, int *mclk_freqp, int *bits_per_samplep, uint *channelsp); +/** + * sandbox_get_i2s_sum() - Read back the sum of the audio data so far + * + * This data is provided to the sandbox driver by the I2S tx_data() method. + * + * @dev: Device to check + * @return sum of audio data + */ +int sandbox_get_i2s_sum(struct udevice *dev); + #endif diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index ae5fabed84..4aced9d22b 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_SOUND) += sound.o obj-$(CONFIG_DM_SOUND) += codec-uclass.o +obj-$(CONFIG_DM_SOUND) += i2s-uclass.o obj-$(CONFIG_I2S) += sound-i2s.o obj-$(CONFIG_I2S_SAMSUNG) += samsung-i2s.o obj-$(CONFIG_SOUND_SANDBOX) += sandbox.o diff --git a/drivers/sound/i2s-uclass.c b/drivers/sound/i2s-uclass.c new file mode 100644 index 0000000000..b741e3952d --- /dev/null +++ b/drivers/sound/i2s-uclass.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google LLC + * Written by Simon Glass + */ + +#include +#include +#include + +int i2s_tx_data(struct udevice *dev, void *data, uint data_size) +{ + struct i2s_ops *ops = i2s_get_ops(dev); + + if (!ops->tx_data) + return -ENOSYS; + + return ops->tx_data(dev, data, data_size); +} + +UCLASS_DRIVER(i2s) = { + .id = UCLASS_I2S, + .name = "i2s", + .per_device_auto_alloc_size = sizeof(struct i2s_uc_priv), +}; diff --git a/drivers/sound/sandbox.c b/drivers/sound/sandbox.c index d24eb9ae9c..2f7c68be0c 100644 --- a/drivers/sound/sandbox.c +++ b/drivers/sound/sandbox.c @@ -4,8 +4,9 @@ */ #include -#include #include +#include +#include #include #include @@ -17,6 +18,10 @@ struct sandbox_codec_priv { uint channels; }; +struct sandbox_i2s_priv { + int sum; /* Use to sum the provided audio data */ +}; + int sound_play(uint32_t msec, uint32_t frequency) { sandbox_sdl_sound_start(frequency); @@ -44,6 +49,13 @@ void sandbox_get_codec_params(struct udevice *dev, int *interfacep, int *ratep, *channelsp = priv->channels; } +int sandbox_get_i2s_sum(struct udevice *dev) +{ + struct sandbox_i2s_priv *priv = dev_get_priv(dev); + + return priv->sum; +} + static int sandbox_codec_set_params(struct udevice *dev, int interface, int rate, int mclk_freq, int bits_per_sample, uint channels) @@ -59,6 +71,34 @@ static int sandbox_codec_set_params(struct udevice *dev, int interface, return 0; } +static int sandbox_i2s_tx_data(struct udevice *dev, void *data, + uint data_size) +{ + struct sandbox_i2s_priv *priv = dev_get_priv(dev); + int i; + + for (i = 0; i < data_size; i++) + priv->sum += ((uint8_t *)data)[i]; + + return 0; +} + +static int sandbox_i2s_probe(struct udevice *dev) +{ + struct i2s_uc_priv *uc_priv = dev_get_uclass_priv(dev); + + /* Use hard-coded values here */ + uc_priv->rfs = 256; + uc_priv->bfs = 32; + uc_priv->audio_pll_clk = 192000000; + uc_priv->samplingrate = 48000; + uc_priv->bitspersample = 16; + uc_priv->channels = 2; + uc_priv->id = 1; + + return 0; +} + static const struct audio_codec_ops sandbox_codec_ops = { .set_params = sandbox_codec_set_params, }; @@ -75,3 +115,21 @@ U_BOOT_DRIVER(sandbox_codec) = { .ops = &sandbox_codec_ops, .priv_auto_alloc_size = sizeof(struct sandbox_codec_priv), }; + +static const struct i2s_ops sandbox_i2s_ops = { + .tx_data = sandbox_i2s_tx_data, +}; + +static const struct udevice_id sandbox_i2s_ids[] = { + { .compatible = "sandbox,i2s" }, + { } +}; + +U_BOOT_DRIVER(sandbox_i2s) = { + .name = "sandbox_i2s", + .id = UCLASS_I2S, + .of_match = sandbox_i2s_ids, + .ops = &sandbox_i2s_ops, + .probe = sandbox_i2s_probe, + .priv_auto_alloc_size = sizeof(struct sandbox_i2s_priv), +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index c3c18356ab..1dffb66932 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -49,6 +49,7 @@ enum uclass_id { UCLASS_I2C_EEPROM, /* I2C EEPROM device */ UCLASS_I2C_GENERIC, /* Generic I2C device */ UCLASS_I2C_MUX, /* I2C multiplexer */ + UCLASS_I2S, /* I2S bus */ UCLASS_IDE, /* IDE device */ UCLASS_IRQ, /* Interrupt controller */ UCLASS_KEYBOARD, /* Keyboard input device */ diff --git a/include/i2s.h b/include/i2s.h index f23862ca04..28f6184811 100644 --- a/include/i2s.h +++ b/include/i2s.h @@ -87,17 +87,41 @@ struct i2s_uc_priv { unsigned int id; /* I2S controller id */ }; +/* Operations for i2s devices */ +struct i2s_ops { + /** + * tx_data() - Transmit audio data + * + * @dev: I2C device + * @data: Data buffer to play + * @data_size: Size of data buffer in bytes + * @return 0 if OK, -ve on error + */ + int (*tx_data)(struct udevice *dev, void *data, uint data_size); +}; + +#define i2s_get_ops(dev) ((struct i2s_ops *)(dev)->driver->ops) + +/** + * i2s_tx_data() - Transmit audio data + * + * @dev: I2C device + * @data: Data buffer to play + * @data_size: Size of data buffer in bytes + * @return 0 if OK, -ve on error + */ +int i2s_tx_data(struct udevice *dev, void *data, uint data_size); + /* * Sends the given data through i2s tx * * @param pi2s_tx pointer of i2s transmitter parameter structure. * @param data address of the data buffer - * @param data_size array size of the int buffer (total size / size of int) - * + * @param data_size size of the data (in bytes) * @return int value 0 for success, -1 in case of error */ -int i2s_transfer_tx_data(struct i2s_uc_priv *pi2s_tx, unsigned int *data, - unsigned long data_size); +int i2s_transfer_tx_data(struct i2s_uc_priv *pi2s_tx, void *data, + uint data_size); /* * Initialise i2s transmiter diff --git a/test/dm/Makefile b/test/dm/Makefile index ef450b7ed8..28ede9a669 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_FIRMWARE) += firmware.o obj-$(CONFIG_DM_GPIO) += gpio.o obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock.o obj-$(CONFIG_DM_I2C) += i2c.o +obj-$(CONFIG_DM_SOUND) += i2s.o obj-$(CONFIG_LED) += led.o obj-$(CONFIG_DM_MAILBOX) += mailbox.o obj-$(CONFIG_DM_MMC) += mmc.o diff --git a/test/dm/i2s.c b/test/dm/i2s.c new file mode 100644 index 0000000000..49ebc3523c --- /dev/null +++ b/test/dm/i2s.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google LLC + * Written by Simon Glass + */ + +#include +#include +#include +#include +#include +#include + +/* Basic test of the i2s codec uclass */ +static int dm_test_i2s(struct unit_test_state *uts) +{ + struct udevice *dev; + u8 data[3]; + + /* check probe success */ + ut_assertok(uclass_first_device_err(UCLASS_I2S, &dev)); + data[0] = 1; + data[1] = 4; + data[2] = 6; + ut_assertok(i2s_tx_data(dev, data, ARRAY_SIZE(data))); + ut_asserteq(11, sandbox_get_i2s_sum(dev)); + ut_assertok(i2s_tx_data(dev, data, 1)); + ut_asserteq(12, sandbox_get_i2s_sum(dev)); + + return 0; +} +DM_TEST(dm_test_i2s, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- 2.30.2