Skip to content

Commit

Permalink
Merge remote-tracking branches 'asoc/topic/sunxi', 'asoc/topic/topolo…
Browse files Browse the repository at this point in the history
…gy' and 'asoc/topic/wm8974' into asoc-next
  • Loading branch information
broonie committed Mar 13, 2016
4 parents b25d280 + f8260af + b6b6e4d + 51b2bb3 commit d4a6360
Show file tree
Hide file tree
Showing 8 changed files with 856 additions and 98 deletions.
39 changes: 39 additions & 0 deletions Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Allwinner Sony/Philips Digital Interface Format (S/PDIF) Controller

The Allwinner S/PDIF audio block is a transceiver that allows the
processor to receive and transmit digital audio via an coaxial cable or
a fibre cable.
For now only playback is supported.

Required properties:

- compatible : should be one of the following:
- "allwinner,sun4i-a10-spdif": for the Allwinner A10 SoC

- reg : Offset and length of the register set for the device.

- interrupts : Contains the spdif interrupt.

- dmas : Generic dma devicetree binding as described in
Documentation/devicetree/bindings/dma/dma.txt.

- dma-names : Two dmas have to be defined, "tx" and "rx".

- clocks : Contains an entry for each entry in clock-names.

- clock-names : Includes the following entries:
"apb" clock for the spdif bus.
"spdif" clock for spdif controller.

Example:

spdif: spdif@01c21000 {
compatible = "allwinner,sun4i-a10-spdif";
reg = <0x01c21000 0x40>;
interrupts = <13>;
clocks = <&apb0_gates 1>, <&spdif_clk>;
clock-names = "apb", "spdif";
dmas = <&dma 0 2>, <&dma 0 2>;
dma-names = "rx", "tx";
status = "okay";
};
21 changes: 10 additions & 11 deletions include/sound/soc-topology.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,6 @@ struct snd_soc_dobj_widget {
unsigned int kcontrol_enum:1; /* this widget is an enum kcontrol */
};

/* dynamic PCM DAI object */
struct snd_soc_dobj_pcm_dai {
struct snd_soc_tplg_pcm_dai *pd;
unsigned int count;
};

/* generic dynamic object - all dynamic objects belong to this struct */
struct snd_soc_dobj {
enum snd_soc_dobj_type type;
Expand All @@ -71,7 +65,6 @@ struct snd_soc_dobj {
union {
struct snd_soc_dobj_control control;
struct snd_soc_dobj_widget widget;
struct snd_soc_dobj_pcm_dai pcm_dai;
};
void *private; /* core does not touch this */
};
Expand Down Expand Up @@ -126,10 +119,16 @@ struct snd_soc_tplg_ops {
int (*widget_unload)(struct snd_soc_component *,
struct snd_soc_dobj *);

/* FE - used for any driver specific init */
int (*pcm_dai_load)(struct snd_soc_component *,
struct snd_soc_tplg_pcm_dai *pcm_dai, int num_fe);
int (*pcm_dai_unload)(struct snd_soc_component *,
/* FE DAI - used for any driver specific init */
int (*dai_load)(struct snd_soc_component *,
struct snd_soc_dai_driver *dai_drv);
int (*dai_unload)(struct snd_soc_component *,
struct snd_soc_dobj *);

/* DAI link - used for any driver specific init */
int (*link_load)(struct snd_soc_component *,
struct snd_soc_dai_link *link);
int (*link_unload)(struct snd_soc_component *,
struct snd_soc_dobj *);

/* callback to handle vendor bespoke data */
Expand Down
2 changes: 1 addition & 1 deletion include/sound/soc.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
#include <sound/compress_driver.h>
#include <sound/control.h>
#include <sound/ac97_codec.h>
#include <sound/soc-topology.h>

/*
* Convenience kcontrol builders
Expand Down Expand Up @@ -404,6 +403,7 @@ struct snd_soc_jack_zone;
struct snd_soc_jack_pin;
#include <sound/soc-dapm.h>
#include <sound/soc-dpcm.h>
#include <sound/soc-topology.h>

struct snd_soc_jack_gpio;

Expand Down
93 changes: 93 additions & 0 deletions sound/soc/codecs/wm8974.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@

#include "wm8974.h"

struct wm8974_priv {
unsigned int mclk;
unsigned int fs;
};

static const struct reg_default wm8974_reg_defaults[] = {
{ 0, 0x0000 }, { 1, 0x0000 }, { 2, 0x0000 }, { 3, 0x0000 },
{ 4, 0x0050 }, { 5, 0x0000 }, { 6, 0x0140 }, { 7, 0x0000 },
Expand Down Expand Up @@ -379,6 +384,79 @@ static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
return 0;
}

static unsigned int wm8974_get_mclkdiv(unsigned int f_in, unsigned int f_out,
int *mclkdiv)
{
unsigned int ratio = 2 * f_in / f_out;

if (ratio <= 2) {
*mclkdiv = WM8974_MCLKDIV_1;
ratio = 2;
} else if (ratio == 3) {
*mclkdiv = WM8974_MCLKDIV_1_5;
} else if (ratio == 4) {
*mclkdiv = WM8974_MCLKDIV_2;
} else if (ratio <= 6) {
*mclkdiv = WM8974_MCLKDIV_3;
ratio = 6;
} else if (ratio <= 8) {
*mclkdiv = WM8974_MCLKDIV_4;
ratio = 8;
} else if (ratio <= 12) {
*mclkdiv = WM8974_MCLKDIV_6;
ratio = 12;
} else if (ratio <= 16) {
*mclkdiv = WM8974_MCLKDIV_8;
ratio = 16;
} else {
*mclkdiv = WM8974_MCLKDIV_12;
ratio = 24;
}

return f_out * ratio / 2;
}

static int wm8974_update_clocks(struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec);
unsigned int fs256;
unsigned int fpll = 0;
unsigned int f;
int mclkdiv;

if (!priv->mclk || !priv->fs)
return 0;

fs256 = 256 * priv->fs;

f = wm8974_get_mclkdiv(priv->mclk, fs256, &mclkdiv);

if (f != priv->mclk) {
/* The PLL performs best around 90MHz */
fpll = wm8974_get_mclkdiv(22500000, fs256, &mclkdiv);
}

wm8974_set_dai_pll(dai, 0, 0, priv->mclk, fpll);
wm8974_set_dai_clkdiv(dai, WM8974_MCLKDIV, mclkdiv);

return 0;
}

static int wm8974_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec);

if (dir != SND_SOC_CLOCK_IN)
return -EINVAL;

priv->mclk = freq;

return wm8974_update_clocks(dai);
}

static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
Expand Down Expand Up @@ -441,8 +519,15 @@ static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec);
u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
int err;

priv->fs = params_rate(params);
err = wm8974_update_clocks(dai);
if (err)
return err;

/* bit size */
switch (params_width(params)) {
Expand Down Expand Up @@ -547,6 +632,7 @@ static const struct snd_soc_dai_ops wm8974_ops = {
.set_fmt = wm8974_set_dai_fmt,
.set_clkdiv = wm8974_set_dai_clkdiv,
.set_pll = wm8974_set_dai_pll,
.set_sysclk = wm8974_set_dai_sysclk,
};

static struct snd_soc_dai_driver wm8974_dai = {
Expand Down Expand Up @@ -606,9 +692,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
static int wm8974_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8974_priv *priv;
struct regmap *regmap;
int ret;

priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;

i2c_set_clientdata(i2c, priv);

regmap = devm_regmap_init_i2c(i2c, &wm8974_regmap);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
Expand Down
Loading

0 comments on commit d4a6360

Please sign in to comment.