dm: sound: Add conversion to driver model
Move the existing hardware drivers over to use driver model.
Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile
index 70d32c6..75fa31e 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 7a3dbd0..d6710df 100644
--- a/drivers/sound/max98095.c
+++ b/drivers/sound/max98095.c
@@ -11,6 +11,8 @@
*/
#include <common.h>
+#include <audio_codec.h>
+#include <dm.h>
#include <div64.h>
#include <fdtdec.h>
#include <i2c.h>
@@ -28,6 +30,7 @@
unsigned int rate;
unsigned int fmt;
int i2c_addr;
+ struct udevice *dev;
};
/* Index 0 is reserved. */
@@ -48,7 +51,12 @@
{
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 @@
{
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 @@
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 @@
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 @@
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 5cd5858..c19e08e 100644
--- a/drivers/sound/samsung-i2s.c
+++ b/drivers/sound/samsung-i2s.c
@@ -5,6 +5,7 @@
*/
#include <common.h>
+#include <dm.h>
#include <i2s.h>
#include <sound.h>
#include <asm/arch/clk.h>
@@ -255,13 +256,13 @@
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 @@
}
/* 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_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 @@
}
/* 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 @@
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 0000000..23b467c
--- /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 <sjg@chromium.org>
+ */
+
+#include <common.h>
+#include <audio_codec.h>
+#include <dm.h>
+#include <i2s.h>
+#include <sound.h>
+#include <asm/gpio.h>
+
+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 ee2635f..83b3295 100644
--- a/drivers/sound/sandbox.c
+++ b/drivers/sound/sandbox.c
@@ -118,7 +118,10 @@
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 1714f43..d731a0d 100644
--- a/drivers/sound/wm8994.c
+++ b/drivers/sound/wm8994.c
@@ -4,6 +4,8 @@
* R. Chandrasekar <rcsekar@samsung.com>
*/
#include <common.h>
+#include <audio_codec.h>
+#include <dm.h>
#include <div64.h>
#include <fdtdec.h>
#include <i2c.h>
@@ -39,6 +41,7 @@
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 @@
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 @@
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 @@
return -1;
}
+#ifndef CONFIG_DM_SOUND
/*
* Gets fdt values for wm8994 config parameters
*
@@ -859,6 +872,7 @@
return 0;
}
+#endif
static int _wm8994_init(struct wm8994_priv *priv,
enum en_audio_interface aif_id, int sampling_rate,
@@ -873,7 +887,7 @@
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 @@
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 @@
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),
+};