From d6cadd591879ba9d2d8a4375bb65f5032b5fb987 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 10 Dec 2018 10:37:39 -0700 Subject: [PATCH] dm: sound: Add conversion to driver model Move the existing hardware drivers over to use driver model. Signed-off-by: Simon Glass --- arch/arm/dts/exynos5250-smdk5250.dts | 1 + arch/arm/dts/exynos5420-smdk5420.dts | 1 + drivers/sound/Makefile | 6 +- drivers/sound/max98095.c | 59 +++++++++++++- drivers/sound/samsung-i2s.c | 111 +++++++++++++++++++++++---- drivers/sound/samsung_sound.c | 101 ++++++++++++++++++++++++ drivers/sound/sandbox.c | 5 +- drivers/sound/wm8994.c | 53 ++++++++++++- 8 files changed, 320 insertions(+), 17 deletions(-) create mode 100644 drivers/sound/samsung_sound.c diff --git a/arch/arm/dts/exynos5250-smdk5250.dts b/arch/arm/dts/exynos5250-smdk5250.dts index 8b695442b1..bf60b82d44 100644 --- a/arch/arm/dts/exynos5250-smdk5250.dts +++ b/arch/arm/dts/exynos5250-smdk5250.dts @@ -62,6 +62,7 @@ i2c@12C70000 { soundcodec@1a { reg = <0x1a>; + u-boot,i2c-offset-len = <2>; compatible = "wolfson,wm8994-codec"; }; }; diff --git a/arch/arm/dts/exynos5420-smdk5420.dts b/arch/arm/dts/exynos5420-smdk5420.dts index cab5ddb61f..daaa466696 100644 --- a/arch/arm/dts/exynos5420-smdk5420.dts +++ b/arch/arm/dts/exynos5420-smdk5420.dts @@ -84,6 +84,7 @@ i2c@12C70000 { soundcodec@1a { reg = <0x1a>; + u-boot,i2c-offset-len = <2>; compatible = "wolfson,wm8994-codec"; }; }; diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 70d32c6d6f..75fa31ec53 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -6,9 +6,13 @@ 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_DM_SOUND) += sound-uclass.o obj-$(CONFIG_I2S_SAMSUNG) += samsung-i2s.o obj-$(CONFIG_SOUND_SANDBOX) += sandbox.o +ifdef CONFIG_DM_SOUND +obj-$(CONFIG_I2S_SAMSUNG) += samsung_sound.o +else +obj-$(CONFIG_I2S) += sound-i2s.o +endif obj-$(CONFIG_SOUND_WM8994) += wm8994.o obj-$(CONFIG_SOUND_MAX98095) += max98095.o diff --git a/drivers/sound/max98095.c b/drivers/sound/max98095.c index 7a3dbd0984..d6710dfaa7 100644 --- a/drivers/sound/max98095.c +++ b/drivers/sound/max98095.c @@ -11,6 +11,8 @@ */ #include +#include +#include #include #include #include @@ -28,6 +30,7 @@ struct max98095_priv { unsigned int rate; unsigned int fmt; int i2c_addr; + struct udevice *dev; }; /* Index 0 is reserved. */ @@ -48,7 +51,12 @@ static int max98095_i2c_write(struct max98095_priv *priv, unsigned int reg, { debug("%s: Write Addr : 0x%02X, Data : 0x%02X\n", __func__, reg, data); +#ifdef CONFIG_DM_SOUND + debug("dev = %s\n", priv->dev->name); + return dm_i2c_write(priv->dev, reg, &data, 1); +#else return i2c_write(priv->i2c_addr, reg, 1, &data, 1); +#endif } /* @@ -65,7 +73,11 @@ static unsigned int max98095_i2c_read(struct max98095_priv *priv, { int ret; +#ifdef CONFIG_DM_SOUND + return dm_i2c_read(priv->dev, reg, data, 1); +#else ret = i2c_read(priv->i2c_addr, reg, 1, data, 1); +#endif if (ret != 0) { debug("%s: Error while reading register %#04x\n", __func__, reg); @@ -484,7 +496,7 @@ static int max98095_do_init(struct max98095_priv *priv, ret = max98095_setup_interface(priv, aif_id); if (ret < 0) { - debug("%s: max98095 codec chip init failed\n", __func__); + debug("%s: max98095 setup interface failed\n", __func__); return ret; } @@ -507,6 +519,7 @@ static int max98095_do_init(struct max98095_priv *priv, return ret; } +#ifndef CONFIG_DM_SOUND static int get_max98095_codec_values(struct sound_codec_info *pcodec_info, const void *blob) { @@ -582,3 +595,47 @@ int max98095_init(const void *blob, enum en_max_audio_interface aif_id, return ret; } +#endif + +static int max98095_set_params(struct udevice *dev, int interface, int rate, + int mclk_freq, int bits_per_sample, + uint channels) +{ + struct max98095_priv *priv = dev_get_priv(dev); + + return max98095_do_init(priv, interface, rate, mclk_freq, + bits_per_sample); +} + +static int max98095_probe(struct udevice *dev) +{ + struct max98095_priv *priv = dev_get_priv(dev); + int ret; + + priv->dev = dev; + ret = max98095_device_init(priv); + if (ret < 0) { + debug("%s: max98095 codec chip init failed\n", __func__); + return ret; + } + + return 0; +} + +static const struct audio_codec_ops max98095_ops = { + .set_params = max98095_set_params, +}; + +static const struct udevice_id max98095_ids[] = { + { .compatible = "maxim,max98095" }, + { } +}; + +U_BOOT_DRIVER(max98095) = { + .name = "max98095", + .id = UCLASS_AUDIO_CODEC, + .of_match = max98095_ids, + .probe = max98095_probe, + .ops = &max98095_ops, + .priv_auto_alloc_size = sizeof(struct max98095_priv), +}; diff --git a/drivers/sound/samsung-i2s.c b/drivers/sound/samsung-i2s.c index 5cd585808a..c19e08e7e3 100644 --- a/drivers/sound/samsung-i2s.c +++ b/drivers/sound/samsung-i2s.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -255,13 +256,13 @@ static int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc) return 0; } -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) { + struct i2s_reg *i2s_reg = (struct i2s_reg *)pi2s_tx->base_address; + u32 *ptr; int i; int start; - struct i2s_reg *i2s_reg = - (struct i2s_reg *)pi2s_tx->base_address; if (data_size < FIFO_LENGTH) { debug("%s : Invalid data size\n", __func__); @@ -269,17 +270,17 @@ int i2s_transfer_tx_data(struct i2s_uc_priv *pi2s_tx, unsigned int *data, } /* fill the tx buffer before stating the tx transmit */ - for (i = 0; i < FIFO_LENGTH; i++) - writel(*data++, &i2s_reg->txd); + for (i = 0, ptr = data; i < FIFO_LENGTH; i++) + writel(*ptr++, &i2s_reg->txd); - data_size -= FIFO_LENGTH; + data_size -= sizeof(*ptr) * FIFO_LENGTH; i2s_txctrl(i2s_reg, I2S_TX_ON); while (data_size > 0) { start = get_timer(0); if (!(CON_TXFIFO_FULL & (readl(&i2s_reg->con)))) { - writel(*data++, &i2s_reg->txd); - data_size--; + writel(*ptr++, &i2s_reg->txd); + data_size -= sizeof(*ptr); } else { if (get_timer(start) > TIMEOUT_I2S_TX) { i2s_txctrl(i2s_reg, I2S_TX_OFF); @@ -296,8 +297,8 @@ int i2s_transfer_tx_data(struct i2s_uc_priv *pi2s_tx, unsigned int *data, int i2s_tx_init(struct i2s_uc_priv *pi2s_tx) { int ret; - struct i2s_reg *i2s_reg = - (struct i2s_reg *)pi2s_tx->base_address; + struct i2s_reg *i2s_reg = (struct i2s_reg *)pi2s_tx->base_address; + if (pi2s_tx->id == 0) { /* Initialize GPIO for I2S-0 */ exynos_pinmux_config(PERIPH_ID_I2S0, 0); @@ -348,8 +349,8 @@ int i2s_tx_init(struct i2s_uc_priv *pi2s_tx) } /* Configure I2s format */ - ret = i2s_set_fmt(i2s_reg, (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM)); + ret = i2s_set_fmt(i2s_reg, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); if (ret == 0) { i2s_set_lr_framesize(i2s_reg, pi2s_tx->rfs); ret = i2s_set_samplesize(i2s_reg, pi2s_tx->bitspersample); @@ -368,3 +369,87 @@ int i2s_tx_init(struct i2s_uc_priv *pi2s_tx) return ret; } + +static int samsung_i2s_tx_data(struct udevice *dev, void *data, uint data_size) +{ + struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); + + return i2s_transfer_tx_data(priv, data, data_size); +} + +static int samsung_i2s_probe(struct udevice *dev) +{ + struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); + + return i2s_tx_init(priv); +} + +static int samsung_i2s_ofdata_to_platdata(struct udevice *dev) +{ + struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); + ulong base; + + /* + * Get the pre-defined sound specific values from FDT. + * All of these are expected to be correct otherwise + * wrong register values in i2s setup parameters + * may result in no sound play. + */ + base = dev_read_addr(dev); + if (base == FDT_ADDR_T_NONE) { + debug("%s: Missing i2s base\n", __func__); + return -EINVAL; + } + priv->base_address = base; + + if (dev_read_u32u(dev, "samsung,i2s-epll-clock-frequency", + &priv->audio_pll_clk)) + goto err; + debug("audio_pll_clk = %d\n", priv->audio_pll_clk); + if (dev_read_u32u(dev, "samsung,i2s-sampling-rate", + &priv->samplingrate)) + goto err; + debug("samplingrate = %d\n", priv->samplingrate); + if (dev_read_u32u(dev, "samsung,i2s-bits-per-sample", + &priv->bitspersample)) + goto err; + debug("bitspersample = %d\n", priv->bitspersample); + if (dev_read_u32u(dev, "samsung,i2s-channels", &priv->channels)) + goto err; + debug("channels = %d\n", priv->channels); + if (dev_read_u32u(dev, "samsung,i2s-lr-clk-framesize", &priv->rfs)) + goto err; + debug("rfs = %d\n", priv->rfs); + if (dev_read_u32u(dev, "samsung,i2s-bit-clk-framesize", &priv->bfs)) + goto err; + debug("bfs = %d\n", priv->bfs); + + if (dev_read_u32u(dev, "samsung,i2s-id", &priv->id)) + goto err; + debug("id = %d\n", priv->id); + + return 0; + +err: + debug("fail to get sound i2s node properties\n"); + + return -EINVAL; +} + +static const struct i2s_ops samsung_i2s_ops = { + .tx_data = samsung_i2s_tx_data, +}; + +static const struct udevice_id samsung_i2s_ids[] = { + { .compatible = "samsung,s5pv210-i2s" }, + { } +}; + +U_BOOT_DRIVER(samsung_i2s) = { + .name = "samsung_i2s", + .id = UCLASS_I2S, + .of_match = samsung_i2s_ids, + .probe = samsung_i2s_probe, + .ofdata_to_platdata = samsung_i2s_ofdata_to_platdata, + .ops = &samsung_i2s_ops, +}; diff --git a/drivers/sound/samsung_sound.c b/drivers/sound/samsung_sound.c new file mode 100644 index 0000000000..23b467c1de --- /dev/null +++ b/drivers/sound/samsung_sound.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google, LLC + * Written by Simon Glass + */ + +#include +#include +#include +#include +#include +#include + +static int samsung_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 samsung_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 samsung_sound_probe(struct udevice *dev) +{ + struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct ofnode_phandle_args args; + struct gpio_desc en_gpio; + ofnode node; + int ret; + + ret = gpio_request_by_name(dev, "codec-enable-gpio", 0, &en_gpio, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + + /* Turn on the GPIO which connects to the codec's "enable" line. */ + if (!ret) + gpio_set_pull(gpio_get_number(&en_gpio), S5P_GPIO_PULL_NONE); + + ret = uclass_get_device_by_phandle(UCLASS_AUDIO_CODEC, dev, + "samsung,audio-codec", + &uc_priv->codec); + if (ret) { + debug("Failed to probe audio codec\n"); + return ret; + } + node = ofnode_find_subnode(dev_ofnode(dev), "cpu"); + if (!ofnode_valid(node)) { + 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) { + debug("Cannot find phandle: %d\n", ret); + return ret; + } + ret = uclass_get_device_by_ofnode(UCLASS_I2S, args.node, &uc_priv->i2s); + if (ret) { + debug("Cannot find i2s: %d\n", ret); + return ret; + } + 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 samsung_sound_ops = { + .setup = samsung_sound_setup, + .play = samsung_sound_play, +}; + +static const struct udevice_id samsung_sound_ids[] = { + { .compatible = "google,snow-audio-max98095" }, + { } +}; + +U_BOOT_DRIVER(samsung_sound) = { + .name = "samsung_sound", + .id = UCLASS_SOUND, + .of_match = samsung_sound_ids, + .probe = samsung_sound_probe, + .ops = &samsung_sound_ops, +}; diff --git a/drivers/sound/sandbox.c b/drivers/sound/sandbox.c index ee2635f41d..83b3295f0d 100644 --- a/drivers/sound/sandbox.c +++ b/drivers/sound/sandbox.c @@ -118,7 +118,10 @@ static int sandbox_i2s_probe(struct udevice *dev) uc_priv->channels = 2; uc_priv->id = 1; - return sandbox_sdl_sound_init(); + /* Ignore any error here - we'll just have no sound */ + sandbox_sdl_sound_init(); + + return 0; } static int sandbox_sound_setup(struct udevice *dev) diff --git a/drivers/sound/wm8994.c b/drivers/sound/wm8994.c index 1714f430f3..d731a0dd86 100644 --- a/drivers/sound/wm8994.c +++ b/drivers/sound/wm8994.c @@ -4,6 +4,8 @@ * R. Chandrasekar */ #include +#include +#include #include #include #include @@ -39,6 +41,7 @@ struct wm8994_priv { int aifclk[WM8994_MAX_AIF]; /* audio interface clock in Hz */ struct wm8994_fll_config fll[2]; /* fll config to configure fll */ int i2c_addr; + struct udevice *dev; }; /* wm 8994 supported sampling rate values */ @@ -79,7 +82,12 @@ static int wm8994_i2c_write(struct wm8994_priv *priv, unsigned int reg, val[1] = (unsigned char)(data & 0xff); debug("Write Addr : 0x%04X, Data : 0x%04X\n", reg, data); +#ifdef CONFIG_DM_SOUND + debug("dev = %s\n", priv->dev->name); + return dm_i2c_write(priv->dev, reg, val, 2); +#else return i2c_write(priv->i2c_addr, reg, 2, val, 2); +#endif } /* @@ -97,7 +105,11 @@ static unsigned int wm8994_i2c_read(struct wm8994_priv *priv, unsigned int reg, unsigned char val[2]; int ret; +#ifdef CONFIG_DM_SOUND + ret = dm_i2c_read(priv->dev, reg, val, 1); +#else ret = i2c_read(priv->i2c_addr, reg, 2, val, 2); +#endif if (ret != 0) { debug("%s: Error while reading register %#04x\n", __func__, reg); @@ -807,6 +819,7 @@ err: return -1; } +#ifndef CONFIG_DM_SOUND /* * Gets fdt values for wm8994 config parameters * @@ -859,6 +872,7 @@ static int get_codec_values(struct sound_codec_info *pcodec_info, return 0; } +#endif static int _wm8994_init(struct wm8994_priv *priv, enum en_audio_interface aif_id, int sampling_rate, @@ -873,7 +887,7 @@ static int _wm8994_init(struct wm8994_priv *priv, return ret; } - ret = wm8994_set_sysclk(priv, aif_id, WM8994_SYSCLK_MCLK1, mclk_freq); + ret = wm8994_set_sysclk(priv, aif_id, WM8994_SYSCLK_MCLK1, mclk_freq); if (ret < 0) { debug("%s: wm8994 codec set sys clock failed\n", __func__); return ret; @@ -891,6 +905,7 @@ static int _wm8994_init(struct wm8994_priv *priv, return ret; } +#ifndef CONFIG_DM_SOUND /* WM8994 Device Initialisation */ int wm8994_init(const void *blob, enum en_audio_interface aif_id, int sampling_rate, int mclk_freq, int bits_per_sample, @@ -918,3 +933,39 @@ int wm8994_init(const void *blob, enum en_audio_interface aif_id, return _wm8994_init(&wm8994_info, aif_id, sampling_rate, mclk_freq, bits_per_sample, channels); } +#endif + +static int wm8994_set_params(struct udevice *dev, int interface, int rate, + int mclk_freq, int bits_per_sample, uint channels) +{ + struct wm8994_priv *priv = dev_get_priv(dev); + + return _wm8994_init(priv, interface, rate, mclk_freq, bits_per_sample, + channels); +} + +static int wm8994_probe(struct udevice *dev) +{ + struct wm8994_priv *priv = dev_get_priv(dev); + + priv->dev = dev; + return wm8994_device_init(priv); +} + +static const struct audio_codec_ops wm8994_ops = { + .set_params = wm8994_set_params, +}; + +static const struct udevice_id wm8994_ids[] = { + { .compatible = "wolfson,wm8994" }, + { } +}; + +U_BOOT_DRIVER(wm8994) = { + .name = "wm8994", + .id = UCLASS_AUDIO_CODEC, + .of_match = wm8994_ids, + .probe = wm8994_probe, + .ops = &wm8994_ops, + .priv_auto_alloc_size = sizeof(struct wm8994_priv), +}; -- 2.39.5