summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/wm8580.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/wm8580.c')
-rw-r--r--sound/soc/codecs/wm8580.c135
1 files changed, 129 insertions, 6 deletions
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index c3571ee5c11b..75a92347718c 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -23,6 +23,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
+#include <linux/spi/spi.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -163,6 +164,8 @@
#define WM8580_DAC_CONTROL5_MUTEALL 0x10
+#define WM8580_REG_DATA_LENGTH 9
+
/*
* wm8580 register cache
* We can't read the WM8580 register space when we
@@ -190,11 +193,8 @@ struct pll_state {
unsigned int out;
};
-#define WM8580_NUM_SUPPLIES 3
+#define WM8580_NUM_SUPPLIES 0
static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = {
- "AVDD",
- "DVDD",
- "PVDD",
};
/* codec private data */
@@ -259,7 +259,6 @@ SOC_WM8580_OUT_DOUBLE_R_TLV("DAC3 Playback Volume",
WM8580_DIGITAL_ATTENUATION_DACL3,
WM8580_DIGITAL_ATTENUATION_DACR3,
0, 0xff, 0, dac_tlv),
-
SOC_SINGLE("DAC1 Deemphasis Switch", WM8580_DAC_CONTROL3, 0, 1, 0),
SOC_SINGLE("DAC2 Deemphasis Switch", WM8580_DAC_CONTROL3, 1, 1, 0),
SOC_SINGLE("DAC3 Deemphasis Switch", WM8580_DAC_CONTROL3, 2, 1, 0),
@@ -463,7 +462,7 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
(pll_div.k >> 18 & 0xf) | (pll_div.n << 4));
reg = snd_soc_read(codec, WM8580_PLLA4 + offset);
- reg &= ~0x1b;
+ reg &= ~0x3b;
reg |= pll_div.prescale | pll_div.postscale << 1 |
pll_div.freqmode << 3;
@@ -679,6 +678,33 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
return 0;
}
+static int wm8580_set_paif_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ unsigned int reg;
+
+ switch (clk_id) {
+ case WM8580_BCLK_CLKDIV:
+ reg = snd_soc_read(codec, WM8580_PAIF1) &
+ ~WM8580_AIF_BCLKSEL_MASK;
+ snd_soc_write(codec, WM8580_PAIF1, reg | freq);
+ reg = snd_soc_read(codec, WM8580_PAIF2) &
+ ~WM8580_AIF_BCLKSEL_MASK;
+ snd_soc_write(codec, WM8580_PAIF2, reg | freq);
+ break;
+ case WM8580_LRCLK_CLKDIV:
+ reg = snd_soc_read(codec, WM8580_PAIF1) & ~0x07;
+ snd_soc_write(codec, WM8580_PAIF1, reg | freq);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute)
{
struct snd_soc_codec *codec = codec_dai->codec;
@@ -734,6 +760,7 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec,
static struct snd_soc_dai_ops wm8580_dai_ops_playback = {
.hw_params = wm8580_paif_hw_params,
.set_fmt = wm8580_set_paif_dai_fmt,
+ .set_sysclk = wm8580_set_paif_dai_sysclk,
.set_clkdiv = wm8580_set_dai_clkdiv,
.set_pll = wm8580_set_dai_pll,
.digital_mute = wm8580_digital_mute,
@@ -970,6 +997,91 @@ static struct i2c_driver wm8580_i2c_driver = {
};
#endif
+#if defined(CONFIG_SPI) || defined(CONFIG_SPI_MODULE)
+ /*!
+ * This function is called to transfer data to WM8580 on SPI.
+ *
+ * @param spi the SPI slave device(WM8580)
+ * @param buf the pointer to the data buffer
+ * @param len the length of the data to be transferred
+ *
+ * @return Returns 0 on success -1 on failure.
+ */
+static inline int spi_rw(void *control_data, char *data, int length)
+{
+ int ret;
+ struct spi_transfer t = {
+ .tx_buf = (const void *)data,
+ .rx_buf = (void *)data,
+ .len = length / 2,
+ .cs_change = 0,
+ .delay_usecs = 0,
+ };
+ struct spi_message m;
+ u8 temp = data[0];
+ data[0] = data[1];
+ data[1] = temp;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ ret = spi_sync((struct spi_device *)control_data, &m);
+ if (unlikely(ret < 0))
+ return ret;
+ else
+ return length;
+
+}
+
+/*!
+ * This function is called whenever the SPI slave device is detected.
+ *
+ * @param spi the SPI slave device
+ *
+ * @return Returns 0 on SUCCESS and error on FAILURE.
+ */
+static int __devinit wm8580_spi_probe(struct spi_device *spi)
+{
+ struct wm8580_priv *wm8580;
+ struct snd_soc_codec *codec;
+
+ wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL);
+ if (wm8580 == NULL)
+ return -ENOMEM;
+
+ codec = &wm8580->codec;
+
+ spi->bits_per_word = 16;
+ spi->mode = SPI_MODE_2;
+
+ spi_setup(spi);
+
+ spi_set_drvdata(spi, wm8580);
+ codec->hw_write = (hw_write_t) spi_rw;
+ codec->control_data = spi;
+
+ codec->dev = &spi->dev;
+
+ return wm8580_register(wm8580, SND_SOC_SPI);
+}
+
+static int __devinit wm8580_spi_remove(struct spi_device *spi)
+{
+ struct wm8580_priv *wm8580 = spi_get_drvdata(spi);
+ wm8580_unregister(wm8580);
+ return 0;
+}
+
+static struct spi_driver wm8580_spi_driver = {
+ .driver = {
+ .name = "wm8580_spi",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = wm8580_spi_probe,
+ .remove = __devexit_p(wm8580_spi_remove),
+};
+#endif
+
static int __init wm8580_modinit(void)
{
int ret;
@@ -981,6 +1093,13 @@ static int __init wm8580_modinit(void)
}
#endif
+#if defined(CONFIG_SPI) || defined(CONFIG_SPI_MODULE)
+ spi_register_driver(&wm8580_spi_driver);
+ if (ret != 0) {
+ pr_err("Failed to register WM8580 SPI driver: %d\n", ret);
+ }
+#endif
+
return 0;
}
module_init(wm8580_modinit);
@@ -990,6 +1109,10 @@ static void __exit wm8580_exit(void)
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&wm8580_i2c_driver);
#endif
+
+#if defined(CONFIG_SPI) || defined(CONFIG_SPI_MODULE)
+ spi_unregister_driver(&wm8580_spi_driver);
+#endif
}
module_exit(wm8580_exit);