dm: sound: Add conversion to driver model
authorSimon Glass <sjg@chromium.org>
Mon, 10 Dec 2018 17:37:39 +0000 (10:37 -0700)
committerSimon Glass <sjg@chromium.org>
Thu, 13 Dec 2018 23:36:29 +0000 (16:36 -0700)
Move the existing hardware drivers over to use driver model.

Signed-off-by: Simon Glass <sjg@chromium.org>
arch/arm/dts/exynos5250-smdk5250.dts
arch/arm/dts/exynos5420-smdk5420.dts
drivers/sound/Makefile
drivers/sound/max98095.c
drivers/sound/samsung-i2s.c
drivers/sound/samsung_sound.c [new file with mode: 0644]
drivers/sound/sandbox.c
drivers/sound/wm8994.c

index 8b695442b1a29dec4e353ebd3cfee0ff89f218a6..bf60b82d4492366696dac5d3cd0786ae8975d530 100644 (file)
@@ -62,6 +62,7 @@
        i2c@12C70000 {
                soundcodec@1a {
                        reg = <0x1a>;
+                       u-boot,i2c-offset-len = <2>;
                        compatible = "wolfson,wm8994-codec";
                };
        };
index cab5ddb61fa961d56d3d83c9a8a4aceb6ee878d0..daaa4666964273b0ce3d376243a13b877fd741b6 100644 (file)
@@ -84,6 +84,7 @@
        i2c@12C70000 {
                soundcodec@1a {
                        reg = <0x1a>;
+                       u-boot,i2c-offset-len = <2>;
                        compatible = "wolfson,wm8994-codec";
                };
        };
index 70d32c6d6f660a8fe3936a5fe8dbb0abc47eb2cd..75fa31ec5344fd5482480a08b4897d65b62c6489 100644 (file)
@@ -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
index 7a3dbd098403041fb39c7149255628ebd8597d8f..d6710dfaa7acba6d662609f872558ba8c036115b 100644 (file)
@@ -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 @@ 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),
+};
index 5cd585808ab93ede3b1ba3b1e41b4c851eb2818e..c19e08e7e30c2e9e01ff280ae54167a5fe2653bc 100644 (file)
@@ -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 @@ 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 (file)
index 0000000..23b467c
--- /dev/null
@@ -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,
+};
index ee2635f41d256ad402d0e17ac399fe7ca584f24a..83b3295f0d4fbf3199c0b8b4bb7c8fe43f87f099 100644 (file)
@@ -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)
index 1714f430f395a29ba822b61cac1b92dc26b55aa4..d731a0dd863c6201a27da2ef944b13eb06a33751 100644 (file)
@@ -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 @@ 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),
+};