Skip to content

Commit

Permalink
dm: sound: Add conversion to driver model
Browse files Browse the repository at this point in the history
Move the existing hardware drivers over to use driver model.

Signed-off-by: Simon Glass <[email protected]>
  • Loading branch information
sjg20 committed Dec 13, 2018
1 parent 6c986cf commit d6cadd5
Show file tree
Hide file tree
Showing 8 changed files with 320 additions and 17 deletions.
1 change: 1 addition & 0 deletions arch/arm/dts/exynos5250-smdk5250.dts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
i2c@12C70000 {
soundcodec@1a {
reg = <0x1a>;
u-boot,i2c-offset-len = <2>;
compatible = "wolfson,wm8994-codec";
};
};
Expand Down
1 change: 1 addition & 0 deletions arch/arm/dts/exynos5420-smdk5420.dts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
i2c@12C70000 {
soundcodec@1a {
reg = <0x1a>;
u-boot,i2c-offset-len = <2>;
compatible = "wolfson,wm8994-codec";
};
};
Expand Down
6 changes: 5 additions & 1 deletion drivers/sound/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
59 changes: 58 additions & 1 deletion drivers/sound/max98095.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
*/

#include <common.h>
#include <audio_codec.h>
#include <dm.h>
#include <div64.h>
#include <fdtdec.h>
#include <i2c.h>
Expand All @@ -28,6 +30,7 @@ struct max98095_priv {
unsigned int rate;
unsigned int fmt;
int i2c_addr;
struct udevice *dev;
};

/* Index 0 is reserved. */
Expand All @@ -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
}

/*
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}

Expand All @@ -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)
{
Expand Down Expand Up @@ -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),
};
111 changes: 98 additions & 13 deletions drivers/sound/samsung-i2s.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

#include <common.h>
#include <dm.h>
#include <i2s.h>
#include <sound.h>
#include <asm/arch/clk.h>
Expand Down Expand Up @@ -255,31 +256,31 @@ 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__);
return -ENODATA; /* invalid pcm data size */
}

/* 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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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,
};
101 changes: 101 additions & 0 deletions drivers/sound/samsung_sound.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 Google, LLC
* Written by Simon Glass <[email protected]>
*/

#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,
};
Loading

0 comments on commit d6cadd5

Please sign in to comment.