[alsa-devel] [PATCH] ASoC: Analog Devices AD193X I2S codec family driver.

Manuel Lauss mano at roarinelk.homelinux.net
Thu Sep 18 15:03:09 CEST 2008


Driver for the Analog Devices AD1935/AD1937 I2C and
AD1938/AD1939 SPI codecs.

Signed-off-by: Manuel Lauss <mano at roarinelk.homelinux.net>
---
 sound/soc/codecs/Kconfig  |    3 +
 sound/soc/codecs/Makefile |    2 +
 sound/soc/codecs/ad1939.c |  897 +++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/ad1939.h |  122 ++++++
 4 files changed, 1024 insertions(+), 0 deletions(-)
 create mode 100644 sound/soc/codecs/ad1939.c
 create mode 100644 sound/soc/codecs/ad1939.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 1db04a2..466d7cb 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -2,6 +2,9 @@ config SND_SOC_AC97_CODEC
 	tristate
 	select SND_AC97_CODEC
 
+config SND_SOC_AD1939
+	tristate
+
 config SND_SOC_AK4535
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index d7b97ab..cfb5d22 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,4 +1,5 @@
 snd-soc-ac97-objs := ac97.o
+snd-soc-ad1939-objs := ad1939.o
 snd-soc-ak4535-objs := ak4535.o
 snd-soc-uda1380-objs := uda1380.o
 snd-soc-wm8510-objs := wm8510.o
@@ -12,6 +13,7 @@ snd-soc-cs4270-objs := cs4270.o
 snd-soc-tlv320aic3x-objs := tlv320aic3x.o
 
 obj-$(CONFIG_SND_SOC_AC97_CODEC)	+= snd-soc-ac97.o
+obj-$(CONFIG_SND_SOC_AD1939)	+= snd-soc-ad1939.o
 obj-$(CONFIG_SND_SOC_AK4535)	+= snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_UDA1380)	+= snd-soc-uda1380.o
 obj-$(CONFIG_SND_SOC_WM8510)	+= snd-soc-wm8510.o
diff --git a/sound/soc/codecs/ad1939.c b/sound/soc/codecs/ad1939.c
new file mode 100644
index 0000000..092f09c
--- /dev/null
+++ b/sound/soc/codecs/ad1939.c
@@ -0,0 +1,897 @@
+/*
+ * AD1935/AD1936/AD1937/AD1938/AD1939 I2S ASoC Codec driver
+ *
+ * Copyright (c) 2007-2008 MSC Vertriebsges.m.b.H,	www.exm32.com
+ *	Manuel Lauss <mano at roarinelk.homelinux.net>
+ *
+ * licensed under the GPLv2
+ *
+ * Code for the AD193X family of I2S codecs with I2C and SPI control
+ * interface.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "ad1939.h"
+
+#define CODEC_NAME "ad1939"
+
+struct ad1939_private {
+	unsigned char tdm_mode;
+	unsigned char dev_addr;
+	unsigned char drvflags;
+	unsigned char mixpairs;
+};
+
+/* default register contents after reset */
+static const u16 ad1939_regcache[AD1939_REGCOUNT] = {
+	0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0
+};
+
+/* Does the codec have power?  In case of STANDBY/OFF BIAS levels, the
+ * hardware access functions below assume the codec is without power
+ * to avoid futile bus transactions.
+ */
+static int codec_is_powered(struct snd_soc_codec *c)
+{
+	return (c->bias_level == SND_SOC_BIAS_PREPARE) ||
+		(c->bias_level == SND_SOC_BIAS_ON);
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int ad1939_i2c_write(struct snd_soc_codec *codec, unsigned int r,
+			    unsigned int v)
+{
+	struct i2c_msg msg;
+	struct i2c_client *c;
+	u16 *cache = codec->reg_cache;
+	u8 data[2];
+	int ret;
+
+	c = (struct i2c_client *)codec->control_data;
+	data[0] = r & 0xff;
+	data[1] = v & 0xff;
+	msg.addr = c->addr;
+	msg.flags = 0;	/* write */
+	msg.buf = &data[0];
+	msg.len = 2;
+
+	/* no power? just update the cache then */
+	if (codec_is_powered(codec))
+		ret = i2c_transfer(c->adapter, &msg, 1);
+	else
+		ret = 1;
+
+	if (ret == 1)
+		cache[r] = v;
+	return (ret == 1) ? 0 : -EIO;
+}
+
+static unsigned int ad1939_i2c_read(struct snd_soc_codec *codec,
+				    unsigned int r)
+{
+	struct i2c_msg msg[2];
+	struct i2c_client *c;
+	u16 *cache = codec->reg_cache;
+	u8 data[2];
+	int ret;
+
+	/* no power? cached value is all we have */
+	if (!codec_is_powered(codec))
+		return cache[r];
+
+	c = (struct i2c_client *)codec->control_data;
+	data[0] = r & 0xff;
+	msg[0].addr = c->addr;
+	msg[0].flags = 0;
+	msg[0].buf = &data[0];
+	msg[0].len = 1;
+
+	msg[1].addr = c->addr;
+	msg[1].flags = I2C_M_RD;
+	msg[1].buf = &data[1];
+	msg[1].len = 1;
+
+	ret = i2c_transfer(c->adapter, &msg[0], 2);
+	if (ret == 2)
+		cache[r] = data[1];
+
+	return (ret == 2) ? data[1] : -EIO;
+}
+#endif
+
+#if defined(CONFIG_SPI) || defined(CONFIG_SPI_MODULE)
+/* SPI communications for AD1938/AD1939:
+ * 24 bit data, LSB first; (I2C style, including R/W bit)
+ * <7bit global address|R/W#><8bit register address><8bit reg data>
+ */
+static int ad1939_spi_write(struct snd_soc_codec *codec, unsigned int r,
+			    unsigned int v)
+{
+	struct spi_device *spi = codec->control_data;
+	struct ad1939_private *ad = codec->private_data;
+	u16 *cache = codec->reg_cache;
+	int ret;
+	u8 data[3];
+
+	data[0] = ad->dev_addr << 1;
+	data[1] = r;
+	data[2] = v;
+
+	/* no power? just update the cache then */
+	if (codec_is_powered(codec))
+		ret = spi_write(spi, &data[0], 3);
+	else
+		ret = 0;
+
+	if (ret == 0)
+		cache[r] = v;
+
+	return ret;
+}
+
+static unsigned int ad1939_spi_read(struct snd_soc_codec *codec,
+				    unsigned int r)
+{
+	struct spi_device *spi = codec->control_data;
+	struct ad1939_private *ad = codec->private_data;
+	u16 *cache = codec->reg_cache;
+	u8 data_w[3], data_r[3];
+	int ret;
+
+	/* no power? cached value is all we have */
+	if (!codec_is_powered(codec))
+		return cache[r];
+
+	data_r[0] = data_w[0] = (ad->dev_addr << 1) | 1;
+	data_r[1] = data_w[1] = r;
+	data_r[2] = data_w[2] = cache[r];
+
+	ret = spi_write_then_read(spi, data_w, 3, data_r, 3);
+
+	if (ret == 0)
+		cache[r] = data_r[2];
+	return (ret) ? ret : data_r[2];
+}
+#endif
+
+/* standard accessor functions.  Reads can generally be satisfied by
+ * the cached value, writes don't hit the chip if the cached value
+ * is identical.
+ */
+static inline unsigned int ad1939_read(struct snd_soc_codec *codec,
+				       unsigned int r)
+{
+	unsigned int v;
+	u16 *cache = codec->reg_cache;
+
+	/* the PLLCTL1 has one read-only bit: PLL lock indicator.
+	 * all other regs keep what was set.
+	 * If powered-down the chip can't be reached so just return
+	 * the cached value.
+	 */
+	if (unlikely(r == AD1939_PLLCTL1))
+		v = codec->read(codec, r);
+	else
+		v = cache[r];
+
+	return v;
+}
+
+static inline int ad1939_write(struct snd_soc_codec *codec,
+			       unsigned int r, unsigned int v)
+{
+	u16 *cache = codec->reg_cache;
+	if (cache[r] == v)
+		return 0;
+	return codec->write(codec, r, v);
+}
+
+/***** controls ******/
+
+static const char *dac_deemph[] = {"Flat", "48kHz", "44.1kHz", "32kHz"};
+static const char *dac_outpol[] = {"Normal", "Inverted"};
+
+static const struct soc_enum ad1939_enum[] = {
+      /*SOC_ENUM_SINGLE(register, startbit, choices, choices-texts) */
+	SOC_ENUM_SINGLE(AD1939_DACCTL2, 1, 4, dac_deemph),
+	SOC_ENUM_SINGLE(AD1939_DACCTL2, 5, 2, dac_outpol),
+};
+
+/* Mixer controls. Keep the Playback Attenuation controls at the top,
+ * or the limiter breaks (see ad1939_add_controls())
+ */
+static const struct snd_kcontrol_new ad1939_snd_ctls[] = {
+SOC_DOUBLE_R("Master Playback", AD1939_VOL1L, AD1939_VOL1R, 0, 255, 1),
+SOC_DOUBLE_R("Channel 2 Playback", AD1939_VOL2L, AD1939_VOL2R, 0, 255, 1),
+SOC_DOUBLE_R("Channel 3 Playback", AD1939_VOL3L, AD1939_VOL3R, 0, 255, 1),
+SOC_DOUBLE_R("Channel 4 Playback", AD1939_VOL4L, AD1939_VOL4R, 0, 255, 1),
+SOC_ENUM("DAC Deemphasis", ad1939_enum[0]),
+SOC_ENUM("DAC output polarity", ad1939_enum[1]),
+};
+
+/* add non dapm controls */
+static int ad1939_add_controls(struct snd_soc_codec *codec)
+{
+	struct ad1939_private *ad = codec->private_data;
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(ad1939_snd_ctls); i++) {
+		if ((i <= 3) && (i >= ad->mixpairs))
+			continue;
+		err = snd_ctl_add(codec->card,
+			snd_soc_cnew(&ad1939_snd_ctls[i], codec, NULL));
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+/***** chip interface config ******/
+
+static int ad1939_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->codec;
+	struct ad1939_private *ad = codec->private_data;
+	unsigned char dac0, dac1, dac2, adc0, adc1, adc2;
+	unsigned long rate;
+	unsigned int bits;
+
+	dac0 = ad1939_read(codec, AD1939_DACCTL0);
+	dac1 = ad1939_read(codec, AD1939_DACCTL1);
+	dac2 = ad1939_read(codec, AD1939_DACCTL2);
+	adc0 = ad1939_read(codec, AD1939_ADCCTL0);
+	adc1 = ad1939_read(codec, AD1939_ADCCTL1);
+	adc2 = ad1939_read(codec, AD1939_ADCCTL2);
+
+	rate = params_rate(params);
+	bits = params->msbits;
+
+	/* sample rate */
+	dac0 &= ~(3<<1);	/* 48kHz */
+	adc0 &= ~(3<<6);	/* 48kHz */
+	switch (rate) {
+	case 32000 ... 48000:
+		break;
+	case 64000 ... 96000:
+		dac0 |= (1<<1);
+		adc0 |= (1<<6);
+		break;
+	case 128000 ... 192000:
+		dac0 |= (2<<1);
+		adc0 |= (2<<6);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* sample width (bits) */
+	dac2 &= ~(3<<3);	/* 24 bits */
+	adc1 &= ~(3<<0);	/* 24 bits */
+
+
+	/* channels */
+	dac0 &= ~(3<<6);	/* DAC I2S stereo */
+	dac1 &= ~(3<<1);	/* 2 channels */
+	adc1 &= ~(3<<5);	/* ADC I2S stereo */
+	adc2 &= ~(3<<4);	/* 2 channels */
+	switch (params_channels(params)) {
+	case 2:	/* I2S stereo mode */
+		if (ad->drvflags & AD1939_DRV_TDM_STEREO) {
+			dac0 |= (ad->tdm_mode & 3) << 6;
+			adc1 |= (ad->tdm_mode & 3) << 5;
+		}
+		break;
+	case 4:	/* TDM mode */
+		dac0 |= (ad->tdm_mode & 3) << 6;
+		dac1 |= (1<<1);
+		adc1 |= (ad->tdm_mode & 3) << 5;
+		adc2 |= (1<<4);
+		break;
+	case 8:	/* TDM mode */
+		dac0 |= (ad->tdm_mode & 3) << 6;
+		dac1 |= (2<<1);
+		adc1 |= (ad->tdm_mode & 3) << 5;
+		adc2 |= (2<<4);
+		break;
+	case 16: /* TDM mode */
+		dac0 |= (ad->tdm_mode & 3) << 6;
+		dac1 |= (3<<1);
+		adc1 |= (ad->tdm_mode & 3) << 5;
+		adc2 |= (3<<4);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ad1939_write(codec, AD1939_DACCTL0, dac0);
+	ad1939_write(codec, AD1939_DACCTL1, dac1);
+	ad1939_write(codec, AD1939_DACCTL2, dac2);
+	ad1939_write(codec, AD1939_ADCCTL0, adc0);
+	ad1939_write(codec, AD1939_ADCCTL1, adc1);
+	ad1939_write(codec, AD1939_ADCCTL2, adc2);
+
+	return 0;
+}
+
+static int ad1939_set_dai_fmt(struct snd_soc_dai *codec_dai,
+			      unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct ad1939_private *ad = codec->private_data;
+	unsigned char dac0, dac1, adc1, adc2;
+
+	dac0 = ad1939_read(codec, AD1939_DACCTL0);
+	dac1 = ad1939_read(codec, AD1939_DACCTL1);
+	adc1 = ad1939_read(codec, AD1939_ADCCTL1);
+	adc2 = ad1939_read(codec, AD1939_ADCCTL2);
+
+	/* codec clocks master/slave setup */
+	dac1 &= ~((1<<4) | (1<<5)); /* LRCK BCK slave */
+	adc2 &= ~((1<<3) | (1<<6)); /* LRCK BCK slave */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:	 /* BCK/LCK master */
+		dac1 |= (1<<4) | (1<<5); /* LRCK BCK master */
+		adc2 |= (1<<3) | (1<<6); /* LRCK BCK master */
+		if (ad->drvflags & AD1939_DRV_ADCDAC_COMMON_BCK) {
+			if (ad->drvflags & AD1939_DRV_ADC_BCK_MASTER)
+				dac1 &= ~(1<<5); /* DAC BCLK slave */
+			else
+				adc2 &= ~(1<<6); /* ADC BCLK slave */
+		}
+		if (ad->drvflags & AD1939_DRV_ADCDAC_COMMON_LRCK) {
+			if (ad->drvflags & AD1939_DRV_ADC_LRCK_MASTER)
+				dac1 &= ~(1<<4); /* DAC LRCK slave */
+			else
+				adc2 &= ~(1<<3); /* ADC LRCK slave */
+		}
+		break;
+
+	case SND_SOC_DAIFMT_CBS_CFS:	/* BCK/LCK slave */
+		break;
+
+	case SND_SOC_DAIFMT_CBM_CFS:	/* BCK master, LRCK slave */
+		dac1 &= ~(1<<4);	/* DAC LRCK slave */
+		adc2 &= ~(1<<3);	/* ADC LRCK slave */
+		dac1 |= (1<<5);		/* DAC BCK master */
+		adc2 |= (1<<6);		/* ADC BCK master */
+		if (ad->drvflags & AD1939_DRV_ADCDAC_COMMON_BCK) {
+			if (ad->drvflags & AD1939_DRV_ADC_BCK_MASTER)
+				dac1 &= ~(1<<5); /* DAC BCLK slave */
+			else
+				adc2 &= ~(1<<6); /* ADC BCLK slave */
+		}
+		break;
+
+	case SND_SOC_DAIFMT_CBS_CFM:
+		dac1 &= ~(1<<5);	/* DAC BCK slave */
+		adc2 &= ~(1<<6);	/* ADC BCK slave */
+		dac1 |= (1<<4);		/* DAC LRCK master */
+		adc2 |= (1<<3);		/* ADC LRCK master */
+		if (ad->drvflags & AD1939_DRV_ADCDAC_COMMON_LRCK) {
+			if (ad->drvflags & AD1939_DRV_ADC_LRCK_MASTER)
+				dac1 &= ~(1<<4); /* DAC LRCK slave */
+			else
+				adc2 &= ~(1<<3); /* ADC LRCK slave */
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	dac0 &= ~(7<<3); /* DAC: SDATA delay 1 (I2S) */
+	adc1 &= ~(7<<2); /* ADC: SDATA delay 1 (I2S) */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		break;
+
+	case SND_SOC_DAIFMT_MSB: /* LEFT_J */
+		dac0 |= (1<<3);	/* no SDATA delay */
+		adc1 |= (1<<2); /* no SDATA delay */
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* clock format */
+	dac1 &= ~((1<<7) | (1<<3)); /* norm BCK LRCK */
+	adc2 &= ~((1<<1) | (1<<2)); /* norm BCK LRCK */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+
+	case SND_SOC_DAIFMT_NB_IF:
+		dac1 |= (1<<3);		/* inv LRCK */
+		adc2 |= (1<<2);
+		break;
+
+	case SND_SOC_DAIFMT_IB_NF:
+		dac1 |= (1<<7);		/* inv BCK */
+		adc2 |= (1<<1);
+		break;
+
+	case SND_SOC_DAIFMT_IB_IF:
+		dac1 |= (1<<3) | (1<<7); /* inv LRCK BCK */
+		adc2 |= (1<<1) | (1<<2);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	ad1939_write(codec, AD1939_DACCTL0, dac0);
+	ad1939_write(codec, AD1939_DACCTL1, dac1);
+	ad1939_write(codec, AD1939_ADCCTL1, adc1);
+	ad1939_write(codec, AD1939_ADCCTL2, adc2);
+
+	return 0;
+}
+
+static int ad1939_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	unsigned char pll0, adc0, dac0;
+	u16 *cache = codec->reg_cache;
+	int i;
+
+	pll0 = ad1939_read(codec, AD1939_PLLCTL0) & 0xfe;
+	dac0 = ad1939_read(codec, AD1939_DACCTL0) & 0xfe;
+	adc0 = ad1939_read(codec, AD1939_ADCCTL0) & 0xfe;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:			/* power up */
+	case SND_SOC_BIAS_PREPARE:
+		if (!codec_is_powered(codec)) {
+			/* writes need to hit the chip */
+			codec->bias_level = level;
+
+			for (i = 0; i < AD1939_REGCOUNT; i++)
+				codec->write(codec, i, cache[i]);
+
+			ad1939_write(codec, AD1939_PLLCTL0, pll0);
+			ad1939_write(codec, AD1939_DACCTL0, dac0);
+			ad1939_write(codec, AD1939_ADCCTL0, adc0);
+		}
+		break;
+
+	case SND_SOC_BIAS_STANDBY:		/* power down */
+	case SND_SOC_BIAS_OFF:
+		if (codec_is_powered(codec)) {
+			/* turn off internal PLL and DAC/ADCs */
+			ad1939_write(codec, AD1939_PLLCTL0, pll0 | 1);
+			ad1939_write(codec, AD1939_DACCTL0, dac0 | 1);
+			ad1939_write(codec, AD1939_ADCCTL0, adc0 | 1);
+		}
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+static int ad1939_digmute(struct snd_soc_dai *dai, int mute)
+{
+	ad1939_write(dai->codec, AD1939_DACMUTE, mute ? 0xff : 0);
+	return 0;
+}
+
+static int ad1939_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	int ret;
+
+	switch (freq) {
+	case 12288000:
+		ret = 0;	/* I know this one works okay */
+	default:
+		ret = -EINVAL;	/* don't know about others */
+	}
+	return ret;
+}
+
+#define AD1939_RATES	\
+	(SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
+	 SNDRV_PCM_RATE_192000)
+
+#define AD1939_FORMATS	\
+	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+struct snd_soc_dai ad1939_dai = {
+	.name			= CODEC_NAME,
+	.playback = {
+		.stream_name	= "Playback",
+		.channels_min	= 2,
+		.channels_max	= 4,	/* 4/8 in single/dualline TDM */
+		.rates		= AD1939_RATES,
+		.formats	= AD1939_FORMATS,
+	},
+	.capture = {
+		.stream_name	= "Capture",
+		.channels_min	= 2,
+		.channels_max	= 4,	/* 2 ADCs */
+		.rates		= AD1939_RATES,
+		.formats	= AD1939_FORMATS,
+	},
+	.ops = {
+		.hw_params	= ad1939_hw_params,
+	},
+	.dai_ops = {
+		.digital_mute	= ad1939_digmute,
+		.set_sysclk	= ad1939_set_dai_sysclk,
+		.set_fmt	= ad1939_set_dai_fmt,
+	}
+};
+EXPORT_SYMBOL_GPL(ad1939_dai);
+
+static int ad1939_init(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->codec;
+	struct ad1939_setup_data *setup = socdev->codec_data;
+	struct ad1939_private *ad = codec->private_data;
+	unsigned char r0, r1;
+	int ret;
+
+	codec->name = CODEC_NAME;
+	codec->owner = THIS_MODULE;
+	codec->set_bias_level = ad1939_set_bias_level;
+	codec->dai = &ad1939_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = sizeof(ad1939_regcache);
+	codec->reg_cache = kmemdup(ad1939_regcache,
+				   sizeof(ad1939_regcache),
+				   GFP_KERNEL);
+	if (!codec->reg_cache)
+		return -ENOMEM;
+
+	/* assume chip has no power. This way, all writes go straight
+	 * to the cache.  Once ASoC decides to wake us up, the bias
+	 * handler will write the cache straight to the chip.
+	 */
+	codec->bias_level = SND_SOC_BIAS_OFF;
+
+	/* remember TDM mode and set up internal clock routing */
+	ad->tdm_mode = setup->tdm_mode;
+	ad->drvflags = setup->drvflags;
+
+	/* limit output attenuation controls to requested number */
+	ad->mixpairs = setup->mixpairs;
+	if ((ad->mixpairs < 1) || (ad->mixpairs > 4))
+		ad->mixpairs = 4;
+
+	/* use default TDM mode if noone wants one */
+	if ((ad->tdm_mode > AD1939_TDM_MODE_DUALLINE) || (ad->tdm_mode < 1))
+		ad->tdm_mode = AD1939_TDM_MODE_TDM;
+
+	/* setup clocks */
+	r0 = ad1939_read(codec, AD1939_PLLCTL0) & ~(0xf << 3);
+	r1 = ad1939_read(codec, AD1939_PLLCTL1) & 3;
+
+	r0 |= (setup->pll_src & 3) << 5;
+	r0 |= (1<<7);	/* enable internal master clock */
+	r0 |= (setup->mclk_xo & 3) << 3;
+	r1 |= setup->dac_adc_clksrc & 7; /* DACclk/ADCclk/VREF */
+	r1 ^= AD1939_CLKSRC_ENABLE_ONCHIP_VREF;	/* VREF bit is inverted */
+
+	ad1939_write(codec, AD1939_PLLCTL0, r0);
+	ad1939_write(codec, AD1939_PLLCTL1, r1);
+
+	/* Bitclock sources for the ADC and DAC I2S interfaces */
+	r0 = ad1939_read(codec, AD1939_DACCTL1);
+	r1 = ad1939_read(codec, AD1939_ADCCTL2);
+	r0 &= ~AD1939_BCLKSRC_DAC_PLL;
+	r1 &= ~AD1939_BCLKSRC_ADC_PLL;
+	r0 |= setup->dac_adc_clksrc & AD1939_BCLKSRC_DAC_PLL;
+	r1 |= setup->dac_adc_clksrc & AD1939_BCLKSRC_ADC_PLL;
+	ad1939_write(codec, AD1939_DACCTL1, r0);
+	ad1939_write(codec, AD1939_ADCCTL2, r1);
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1,
+			       SNDRV_DEFAULT_STR1);
+	if (unlikely(ret < 0)) {
+		printk(KERN_ERR CODEC_NAME ": cannot create pcms\n");
+		goto pcm_err;
+	}
+
+	ad1939_add_controls(codec);
+
+	ret = snd_soc_register_card(socdev);
+	if (ret < 0) {
+		printk(KERN_ERR CODEC_NAME ": cannot register card\n");
+		goto card_err;
+	}
+
+	return 0;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	kfree(codec->reg_cache);
+	return ret;
+}
+
+static void ad1939_deinit(struct snd_soc_device *socdev)
+{
+	if (socdev) {
+		ad1939_set_bias_level(socdev->codec, SND_SOC_BIAS_OFF);
+		snd_soc_free_pcms(socdev);
+		snd_soc_dapm_free(socdev);
+		/* free memory allocated in ad1939_probe() */
+		kfree(socdev->codec->private_data);
+		kfree(socdev->codec->reg_cache);
+		kfree(socdev->codec);
+	}
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int ad1939_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	struct snd_soc_device *socdev = client->dev.platform_data;
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret;
+
+	codec->control_data = client;
+	i2c_set_clientdata(client, socdev);
+
+	codec->read = ad1939_i2c_read;
+	codec->write = ad1939_i2c_write;
+
+	ret = ad1939_init(socdev);
+	if (ret == 0)
+		return 0;
+
+	printk(KERN_ERR CODEC_NAME ": i2c codec init failed\n");
+
+	/* undo allocations made by platform probe */
+	kfree(codec->private_data);
+	kfree(codec);
+	return ret;
+}
+
+static int ad1939_i2c_remove(struct i2c_client *client)
+{
+	struct snd_soc_device *socdev = i2c_get_clientdata(client);
+
+	ad1939_deinit(socdev);
+
+	return 0;
+}
+
+#define ad1939_i2c_suspend NULL
+#define ad1939_i2c_resume NULL
+
+static const struct i2c_device_id ad1939_i2c_id[] = {
+	{ "ad1939", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ad1939_i2c_id);
+
+static struct i2c_driver ad1939_i2c_driver = {
+	.driver = {
+		.name	= "ad1939",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ad1939_i2c_probe,
+	.remove		= ad1939_i2c_remove,
+	.suspend	= ad1939_i2c_suspend,
+	.resume		= ad1939_i2c_resume,
+	.id_table	= ad1939_i2c_id,
+};
+
+static int ad1939_add_i2c_device(struct platform_device *pdev,
+				 struct ad1939_setup_data *setup)
+{
+	struct i2c_board_info info;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
+	int ret;
+
+	ret = i2c_add_driver(&ad1939_i2c_driver);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "can't add i2c driver\n");
+		return ret;
+	}
+
+	memset(&info, 0, sizeof(struct i2c_board_info));
+	info.addr = setup->dev_address;
+	info.platform_data = platform_get_drvdata(pdev);
+	strlcpy(info.type, "ad1939", I2C_NAME_SIZE);
+
+	adapter = i2c_get_adapter(setup->i2c_bus);
+	if (!adapter) {
+		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
+			setup->i2c_bus);
+		goto err_driver;
+	}
+
+	client = i2c_new_device(adapter, &info);
+	i2c_put_adapter(adapter);
+	if (!client) {
+		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
+			(unsigned int)info.addr);
+		goto err_driver;
+	}
+
+	return 0;
+
+err_driver:
+	i2c_del_driver(&ad1939_i2c_driver);
+	return -ENODEV;
+}
+#endif	/* I2C */
+
+#if defined(CONFIG_SPI) && defined(CONFIG_SPI_MASTER)
+static int ad1939_spi_probe(struct spi_device *spi)
+{
+	struct snd_soc_device *socdev = spi->dev.platform_data;
+	struct snd_soc_codec *codec = socdev->codec;
+	int ret;
+
+	codec->control_data = spi;
+	codec->read = ad1939_spi_read;
+	codec->write = ad1939_spi_write;
+
+	spi_set_drvdata(spi, socdev);
+
+	ret = ad1939_init(socdev);
+	if (ret == 0)
+		return 0;
+
+	printk(KERN_ERR CODEC_NAME ": SPI codec init failed\n");
+
+	/* undo allocations made by platform probe */
+	kfree(codec->private_data);
+	kfree(codec);
+	return ret;
+}
+
+static int ad1939_spi_remove(struct spi_device *spi)
+{
+	struct snd_soc_device *socdev = spi_get_drvdata(spi);
+	ad1939_deinit(socdev);
+	return 0;
+}
+
+#define ad1939_spi_suspend NULL
+#define ad1939_spi_resume NULL
+
+static struct spi_driver ad1939_spi_driver = {
+	.driver =	{
+		.name	= "ad1939",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ad1939_spi_probe,
+	.remove		= ad1939_spi_remove,
+	.suspend	= ad1939_spi_suspend,
+	.resume		= ad1939_spi_resume,
+};
+
+static int ad1939_add_spi_device(struct platform_device *pdev,
+				 struct ad1939_setup_data *setup)
+{
+	struct spi_master *master;
+	struct spi_device *spidev;
+	int ret;
+
+	ret = spi_register_driver(&ad1939_spi_driver);
+	if (ret) {
+		dev_err(&pdev->dev, "can't add spi driver\n");
+		return ret;
+	}
+
+	ret = -ENODEV;
+
+	/* pass on socdev to spi probe (spi->dev.platform_data) */
+	setup->spi_info.platform_data = platform_get_drvdata(pdev);
+
+	master = spi_busnum_to_master(setup->spi_info.bus_num);
+	if (!master) {
+		dev_err(&pdev->dev, "can't get spi master\n");
+		goto out;
+	}
+
+	spidev = spi_new_device(master, &setup->spi_info);
+	spi_master_put(master);
+	if (!spidev) {
+		dev_err(&pdev->dev, "can't register spi device\n");
+		goto out;
+	}
+
+	return 0;
+
+out:
+	spi_unregister_driver(&ad1939_spi_driver);
+	return ret;
+}
+#endif	/* SPI */
+
+static int ad1939_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct ad1939_setup_data *setup = socdev->codec_data;
+	struct snd_soc_codec *codec;
+	struct ad1939_private *ad;
+	int ret;
+
+	ret = -ENOMEM;
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		goto out;
+
+	ad = kzalloc(sizeof(struct ad1939_private), GFP_KERNEL);
+	if (ad == NULL)
+		goto out1;
+
+	codec->private_data = ad;
+	socdev->codec = codec;
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	ad->dev_addr = setup->dev_address;	/* I2C/SPI device addr */
+
+	ret = -ENODEV;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	if (setup->bus_mode == AD1939_BUS_MODE_I2C)
+		ret = ad1939_add_i2c_device(pdev, setup);
+#endif
+#if defined(CONFIG_SPI) && defined(CONFIG_SPI_MASTER)
+	if (setup->bus_mode == AD1939_BUS_MODE_SPI)
+		ret = ad1939_add_spi_device(pdev, setup);
+#endif
+
+	if (!ret)
+		return 0;
+
+	kfree(ad);
+out1:
+	kfree(codec);
+out:
+	return ret;
+}
+
+static int ad1939_remove(struct platform_device *pdev)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&ad1939_i2c_driver);
+#endif
+#if defined(CONFIG_SPI) || defined(CONFIG_SPI_MODULE)
+	spi_unregister_driver(&ad1939_spi_driver);
+#endif
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ad1939 = {
+	.probe		= ad1939_probe,
+	.remove		= ad1939_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ad1939);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ASoC AD1935-AD1939 I2S Codec family driver");
+MODULE_AUTHOR("Manuel Lauss");
diff --git a/sound/soc/codecs/ad1939.h b/sound/soc/codecs/ad1939.h
new file mode 100644
index 0000000..72931d5
--- /dev/null
+++ b/sound/soc/codecs/ad1939.h
@@ -0,0 +1,122 @@
+/*
+ * AD1935/AD1936/AD1937/AD1938/AD1939 I2S ASoC Codec driver
+ *
+ * Copyright (c) 2007-2008 MSC Vertriebsges.m.b.H,	www.exm32.com
+ *	Manuel Lauss <mano at roarinelk.homelinux.net>
+ *
+ * licensed under the GPLv2
+ *
+ */
+
+#ifndef _AD1939_H_
+#define _AD1939_H_
+
+#include <linux/spi/spi.h>
+
+#define AD1939_PLLCTL0	0x00
+#define AD1939_PLLCTL1	0x01
+#define AD1939_DACCTL0	0x02
+#define AD1939_DACCTL1	0x03
+#define AD1939_DACCTL2	0x04
+#define AD1939_DACMUTE	0x05
+#define AD1939_VOL1L	0x06
+#define AD1939_VOL1R	0x07
+#define AD1939_VOL2L	0x08
+#define AD1939_VOL2R	0x09
+#define AD1939_VOL3L	0x0A
+#define AD1939_VOL3R	0x0B
+#define AD1939_VOL4L	0x0C
+#define AD1939_VOL4R	0x0D
+#define AD1939_ADCCTL0	0x0E
+#define AD1939_ADCCTL1	0x0F
+#define AD1939_ADCCTL2	0x10
+
+#define AD1939_REGCOUNT	0x11
+
+/*
+ * AD1939 setup data
+ */
+
+/* TDM modes. Have a look at the manual to understand what these do. */
+#define AD1939_TDM_MODE_TDM		1
+#define AD1939_TDM_MODE_AUX		2
+#define AD1939_TDM_MODE_DUALLINE	3
+
+/* Master PLL clock source, select one */
+#define AD1939_PLL_SRC_MCLK		0	/* external clock */
+#define AD1939_PLL_SRC_DACLRCK		1	/* get from DAC LRCLK */
+#define AD1939_PLL_SRC_ADCLRCK		2	/* get from ADC LRCLK */
+
+/* clock sources for ADC, DAC. Refer to the manual for more information
+ * (for 192000kHz modes, internal PLL _MUST_ be used). Select one for ADC
+ * and DAC.
+ */
+#define AD1939_CLKSRC_DAC_PLL		0	/* DAC clocked by int. PLL */
+#define AD1939_CLKSRC_DAC_MCLK		(1<<0)	/* DAC clocked by ext. MCK */
+#define AD1939_CLKSRC_ADC_PLL		0	/* ADC clocked by int. PLL */
+#define AD1939_CLKSRC_ADC_MCLK		(1<<1)	/* ADC clocked by ext. MCK */
+#define AD1939_CLKSRC_ENABLE_ONCHIP_VREF	(1<<2)
+
+/* I2S Bitclock sources for DAC and ADC I2S interfaces.
+ * OR it to ad1939_setup_data.dac_adc_clksrc. Select one for ADC and DAC.
+ */
+#define AD1939_BCLKSRC_DAC_EXT		0	/* DAC I2SCLK from DBCLK pin */
+#define AD1939_BCLKSRC_DAC_PLL		(1<<6)	/* DAC I2SCLK from int. PLL */
+#define AD1939_BCLKSRC_ADC_EXT		0	/* DAC I2SCLK from DBCLK pin */
+#define AD1939_BCLKSRC_ADC_PLL		(1<<7)	/* DAC I2SCLK from int. PLL */
+
+/* MCLK_XO pin configuration */
+#define AD1939_MCLKXO_MCLKXI		0	/* mirror MCLK_XI pin */
+#define AD1939_MCLKXO_256FS		1
+#define AD1939_MCLKXO_512FS		2
+#define AD1939_MCLKXO_OFF		3	/* disable MCLK_XO output */
+
+/* driver specific flags */
+/* specify these flags if the LRCK and/or BCK pins of the ADC and DAC
+ * parts are wired together on the PCB; to prevent both units from driving
+ * the pin and resulting bad signals.  You then have to specify WHICH
+ * unit gets to be the Master (clock generator) and which is slave.
+ * NOTE: this is only used if the CODEC is configured as either BCK or
+ * LRCK master; if the codec is BCK/LRCK slave (BCK and LRCK are driven
+ * by external components) these settings are ignored!
+ */
+#define AD1939_DRV_ADCDAC_COMMON_BCK	(1<<0)
+#define AD1939_DRV_ADCDAC_COMMON_LRCK	(1<<1)
+
+/* define which unit gets to drive the BCK/LRCK pins if the codec is
+ * required to be either BCK or LRCK master
+ */
+#define AD1939_DRV_DAC_LRCK_MASTER	0
+#define AD1939_DRV_ADC_LRCK_MASTER	(1<<2)
+#define AD1939_DRV_DAC_BCK_MASTER	0
+#define AD1939_DRV_ADC_BCK_MASTER	(1<<3)
+
+/* use TDM mode even for stereo (2-channel) signals */
+#define AD1939_DRV_TDM_STEREO		(1<<4)
+
+#define AD1939_BUS_MODE_I2C		1
+#define AD1939_BUS_MODE_SPI		2
+
+struct ad1939_setup_data {
+	/* control interface configuration */
+	/* device address, WITHOUT the R/W bit! (default 0x04) */
+	unsigned char dev_address;	/* I2C or SPI device address */
+	unsigned char bus_mode;		/* which framework to register with */
+
+	unsigned char i2c_bus;		/* I2C bus the device is on */
+
+	struct spi_board_info	spi_info;	/* SPI conn info */
+
+	/* configuration */
+	unsigned char tdm_mode;		/* one of AD1939_TDM_MODE_* */
+	unsigned char pll_src;		/* one of AD1939_PLL_SRC_* */
+	unsigned char dac_adc_clksrc;	/* AD1939_{B,}CLKSRC_* or'ed */
+	unsigned char mclk_xo;		/* one of AD1939_MCLKXO_* */
+	unsigned char drvflags;		/* driver flags */
+	unsigned char mixpairs;		/* mixer pairs (L-R) to advertise */
+};
+
+extern struct snd_soc_codec_device	soc_codec_dev_ad1939;
+extern struct snd_soc_dai		ad1939_dai;
+
+#endif
-- 
1.6.0.1



More information about the Alsa-devel mailing list