[alsa-devel] [PATCH 0/4] extend ad1938 codec/machine driver to ad193x supporting ad1936/7/8/9
Hi Mark, The following patch series extends ad1938 codec/machine driver to support ad193x(including ad1936/ad1937/ad1938/ad1939). Some codecs use i2c bus and others use spi bus. patch 1: extend ad1938 codec driver to ad193x supporting ad1936/7/8/9 patch 2: change bf5xx-ad1938 machine driver to bf5xx-ad193x machine driver patch 3: soc-cache: add i2c read entry for 8_8 mode patch 4: soc-cache: let reg be AND'ed by 0xff instead of data buffer for 8_8 mode
Thanks Barry
This patch extends ad1938 codec driver to support ad1936/7/8/9, ad1936/7 use i2c bus, ad1938/9 use spi bus. The driver is splitted into three: ad193x.h and ad193x.c: common codec driver ad193x-spi.c: ad193x codec spi driver ad193x-i2c.c: ad193x codec i2c driver
Signed-off-by: Barry Song barry.song@analog.com --- sound/soc/codecs/Kconfig | 12 +- sound/soc/codecs/Makefile | 8 +- sound/soc/codecs/ad1938.c | 521 ----------------------------------------- sound/soc/codecs/ad1938.h | 100 -------- sound/soc/codecs/ad193x-i2c.c | 56 +++++ sound/soc/codecs/ad193x-spi.c | 49 ++++ sound/soc/codecs/ad193x.c | 475 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/ad193x.h | 82 +++++++ 8 files changed, 678 insertions(+), 625 deletions(-) delete mode 100644 sound/soc/codecs/ad1938.c delete mode 100644 sound/soc/codecs/ad1938.h create mode 100644 sound/soc/codecs/ad193x-i2c.c create mode 100644 sound/soc/codecs/ad193x-spi.c create mode 100644 sound/soc/codecs/ad193x.c create mode 100644 sound/soc/codecs/ad193x.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 16c47ed..42a565f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -13,7 +13,9 @@ config SND_SOC_ALL_CODECS select SND_SOC_L3 select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS select SND_SOC_AD1836 if SPI_MASTER - select SND_SOC_AD1938 if SPI_MASTER + select SND_SOC_AD193X + select SND_SOC_AD193X_SPI if SPI_MASTER + select SND_SOC_AD193X_I2C if I2C select SND_SOC_AD1980 if SND_SOC_AC97_BUS select SND_SOC_ADS117X select SND_SOC_AD73311 if I2C @@ -91,7 +93,13 @@ config SND_SOC_AC97_CODEC config SND_SOC_AD1836 tristate
-config SND_SOC_AD1938 +config SND_SOC_AD193X + tristate + +config SND_SOC_AD193X_SPI + tristate + +config SND_SOC_AD193X_I2C tristate
config SND_SOC_AD1980 diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 6981777..478464b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -1,6 +1,8 @@ snd-soc-ac97-objs := ac97.o snd-soc-ad1836-objs := ad1836.o -snd-soc-ad1938-objs := ad1938.o +snd-soc-ad193x-objs := ad193x.o +snd-soc-ad193x-spi-objs := ad193x-spi.o +snd-soc-ad193x-i2c-objs := ad193x-i2c.o snd-soc-ad1980-objs := ad1980.o snd-soc-ad73311-objs := ad73311.o snd-soc-ads117x-objs := ads117x.o @@ -63,7 +65,9 @@ snd-soc-wm2000-objs := wm2000.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o -obj-$(CONFIG_SND_SOC_AD1938) += snd-soc-ad1938.o +obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o +obj-$(CONFIG_SND_SOC_AD193X_SPI)+= snd-soc-ad193x-spi.o +obj-$(CONFIG_SND_SOC_AD193X_I2C)+= snd-soc-ad193x-i2c.o obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c deleted file mode 100644 index c233810..0000000 --- a/sound/soc/codecs/ad1938.c +++ /dev/null @@ -1,521 +0,0 @@ -/* - * File: sound/soc/codecs/ad1938.c - * Author: Barry Song Barry.Song@analog.com - * - * Created: June 04 2009 - * Description: Driver for AD1938 sound chip - * - * Modified: - * Copyright 2009 Analog Devices Inc. - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/device.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/initval.h> -#include <sound/soc.h> -#include <sound/tlv.h> -#include <sound/soc-dapm.h> -#include <linux/spi/spi.h> -#include "ad1938.h" - -/* codec private data */ -struct ad1938_priv { - struct snd_soc_codec codec; - u8 reg_cache[AD1938_NUM_REGS]; -}; - -/* ad1938 register cache & default register settings */ -static const u8 ad1938_reg[AD1938_NUM_REGS] = { - 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, -}; - -static struct snd_soc_codec *ad1938_codec; -struct snd_soc_codec_device soc_codec_dev_ad1938; -static int ad1938_register(struct ad1938_priv *ad1938); -static void ad1938_unregister(struct ad1938_priv *ad1938); - -/* - * AD1938 volume/mute/de-emphasis etc. controls - */ -static const char *ad1938_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"}; - -static const struct soc_enum ad1938_deemp_enum = - SOC_ENUM_SINGLE(AD1938_DAC_CTRL2, 1, 4, ad1938_deemp); - -static const struct snd_kcontrol_new ad1938_snd_controls[] = { - /* DAC volume control */ - SOC_DOUBLE_R("DAC1 Volume", AD1938_DAC_L1_VOL, - AD1938_DAC_R1_VOL, 0, 0xFF, 1), - SOC_DOUBLE_R("DAC2 Volume", AD1938_DAC_L2_VOL, - AD1938_DAC_R2_VOL, 0, 0xFF, 1), - SOC_DOUBLE_R("DAC3 Volume", AD1938_DAC_L3_VOL, - AD1938_DAC_R3_VOL, 0, 0xFF, 1), - SOC_DOUBLE_R("DAC4 Volume", AD1938_DAC_L4_VOL, - AD1938_DAC_R4_VOL, 0, 0xFF, 1), - - /* ADC switch control */ - SOC_DOUBLE("ADC1 Switch", AD1938_ADC_CTRL0, AD1938_ADCL1_MUTE, - AD1938_ADCR1_MUTE, 1, 1), - SOC_DOUBLE("ADC2 Switch", AD1938_ADC_CTRL0, AD1938_ADCL2_MUTE, - AD1938_ADCR2_MUTE, 1, 1), - - /* DAC switch control */ - SOC_DOUBLE("DAC1 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL1_MUTE, - AD1938_DACR1_MUTE, 1, 1), - SOC_DOUBLE("DAC2 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL2_MUTE, - AD1938_DACR2_MUTE, 1, 1), - SOC_DOUBLE("DAC3 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL3_MUTE, - AD1938_DACR3_MUTE, 1, 1), - SOC_DOUBLE("DAC4 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL4_MUTE, - AD1938_DACR4_MUTE, 1, 1), - - /* ADC high-pass filter */ - SOC_SINGLE("ADC High Pass Filter Switch", AD1938_ADC_CTRL0, - AD1938_ADC_HIGHPASS_FILTER, 1, 0), - - /* DAC de-emphasis */ - SOC_ENUM("Playback Deemphasis", ad1938_deemp_enum), -}; - -static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = { - SND_SOC_DAPM_DAC("DAC", "Playback", AD1938_DAC_CTRL0, 0, 1), - SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_SUPPLY("PLL_PWR", AD1938_PLL_CLK_CTRL0, 0, 1, NULL, 0), - SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1938_ADC_CTRL0, 0, 1, NULL, 0), - SND_SOC_DAPM_OUTPUT("DAC1OUT"), - SND_SOC_DAPM_OUTPUT("DAC2OUT"), - SND_SOC_DAPM_OUTPUT("DAC3OUT"), - SND_SOC_DAPM_OUTPUT("DAC4OUT"), - SND_SOC_DAPM_INPUT("ADC1IN"), - SND_SOC_DAPM_INPUT("ADC2IN"), -}; - -static const struct snd_soc_dapm_route audio_paths[] = { - { "DAC", NULL, "PLL_PWR" }, - { "ADC", NULL, "PLL_PWR" }, - { "DAC", NULL, "ADC_PWR" }, - { "ADC", NULL, "ADC_PWR" }, - { "DAC1OUT", "DAC1 Switch", "DAC" }, - { "DAC2OUT", "DAC2 Switch", "DAC" }, - { "DAC3OUT", "DAC3 Switch", "DAC" }, - { "DAC4OUT", "DAC4 Switch", "DAC" }, - { "ADC", "ADC1 Switch", "ADC1IN" }, - { "ADC", "ADC2 Switch", "ADC2IN" }, -}; - -/* - * DAI ops entries - */ - -static int ad1938_mute(struct snd_soc_dai *dai, int mute) -{ - struct snd_soc_codec *codec = dai->codec; - int reg; - - reg = snd_soc_read(codec, AD1938_DAC_CTRL2); - reg = (mute > 0) ? reg | AD1938_DAC_MASTER_MUTE : reg & - (~AD1938_DAC_MASTER_MUTE); - snd_soc_write(codec, AD1938_DAC_CTRL2, reg); - - return 0; -} - -static int ad1938_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, - unsigned int rx_mask, int slots, int width) -{ - struct snd_soc_codec *codec = dai->codec; - int dac_reg = snd_soc_read(codec, AD1938_DAC_CTRL1); - int adc_reg = snd_soc_read(codec, AD1938_ADC_CTRL2); - - dac_reg &= ~AD1938_DAC_CHAN_MASK; - adc_reg &= ~AD1938_ADC_CHAN_MASK; - - switch (slots) { - case 2: - dac_reg |= AD1938_DAC_2_CHANNELS << AD1938_DAC_CHAN_SHFT; - adc_reg |= AD1938_ADC_2_CHANNELS << AD1938_ADC_CHAN_SHFT; - break; - case 4: - dac_reg |= AD1938_DAC_4_CHANNELS << AD1938_DAC_CHAN_SHFT; - adc_reg |= AD1938_ADC_4_CHANNELS << AD1938_ADC_CHAN_SHFT; - break; - case 8: - dac_reg |= AD1938_DAC_8_CHANNELS << AD1938_DAC_CHAN_SHFT; - adc_reg |= AD1938_ADC_8_CHANNELS << AD1938_ADC_CHAN_SHFT; - break; - case 16: - dac_reg |= AD1938_DAC_16_CHANNELS << AD1938_DAC_CHAN_SHFT; - adc_reg |= AD1938_ADC_16_CHANNELS << AD1938_ADC_CHAN_SHFT; - break; - default: - return -EINVAL; - } - - snd_soc_write(codec, AD1938_DAC_CTRL1, dac_reg); - snd_soc_write(codec, AD1938_ADC_CTRL2, adc_reg); - - return 0; -} - -static int ad1938_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) -{ - struct snd_soc_codec *codec = codec_dai->codec; - int adc_reg, dac_reg; - - adc_reg = snd_soc_read(codec, AD1938_ADC_CTRL2); - dac_reg = snd_soc_read(codec, AD1938_DAC_CTRL1); - - /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S - * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) - */ - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - adc_reg &= ~AD1938_ADC_SERFMT_MASK; - adc_reg |= AD1938_ADC_SERFMT_TDM; - break; - case SND_SOC_DAIFMT_DSP_A: - adc_reg &= ~AD1938_ADC_SERFMT_MASK; - adc_reg |= AD1938_ADC_SERFMT_AUX; - break; - default: - return -EINVAL; - } - - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */ - adc_reg &= ~AD1938_ADC_LEFT_HIGH; - adc_reg &= ~AD1938_ADC_BCLK_INV; - dac_reg &= ~AD1938_DAC_LEFT_HIGH; - dac_reg &= ~AD1938_DAC_BCLK_INV; - break; - case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */ - adc_reg |= AD1938_ADC_LEFT_HIGH; - adc_reg &= ~AD1938_ADC_BCLK_INV; - dac_reg |= AD1938_DAC_LEFT_HIGH; - dac_reg &= ~AD1938_DAC_BCLK_INV; - break; - case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */ - adc_reg &= ~AD1938_ADC_LEFT_HIGH; - adc_reg |= AD1938_ADC_BCLK_INV; - dac_reg &= ~AD1938_DAC_LEFT_HIGH; - dac_reg |= AD1938_DAC_BCLK_INV; - break; - - case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */ - adc_reg |= AD1938_ADC_LEFT_HIGH; - adc_reg |= AD1938_ADC_BCLK_INV; - dac_reg |= AD1938_DAC_LEFT_HIGH; - dac_reg |= AD1938_DAC_BCLK_INV; - break; - default: - return -EINVAL; - } - - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */ - adc_reg |= AD1938_ADC_LCR_MASTER; - adc_reg |= AD1938_ADC_BCLK_MASTER; - dac_reg |= AD1938_DAC_LCR_MASTER; - dac_reg |= AD1938_DAC_BCLK_MASTER; - break; - case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */ - adc_reg |= AD1938_ADC_LCR_MASTER; - adc_reg &= ~AD1938_ADC_BCLK_MASTER; - dac_reg |= AD1938_DAC_LCR_MASTER; - dac_reg &= ~AD1938_DAC_BCLK_MASTER; - break; - case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */ - adc_reg &= ~AD1938_ADC_LCR_MASTER; - adc_reg |= AD1938_ADC_BCLK_MASTER; - dac_reg &= ~AD1938_DAC_LCR_MASTER; - dac_reg |= AD1938_DAC_BCLK_MASTER; - break; - case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */ - adc_reg &= ~AD1938_ADC_LCR_MASTER; - adc_reg &= ~AD1938_ADC_BCLK_MASTER; - dac_reg &= ~AD1938_DAC_LCR_MASTER; - dac_reg &= ~AD1938_DAC_BCLK_MASTER; - break; - default: - return -EINVAL; - } - - snd_soc_write(codec, AD1938_ADC_CTRL2, adc_reg); - snd_soc_write(codec, AD1938_DAC_CTRL1, dac_reg); - - return 0; -} - -static int ad1938_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - int word_len = 0, reg = 0; - - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_codec *codec = socdev->card->codec; - - /* bit size */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - word_len = 3; - break; - case SNDRV_PCM_FORMAT_S20_3LE: - word_len = 1; - break; - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S32_LE: - word_len = 0; - break; - } - - reg = snd_soc_read(codec, AD1938_DAC_CTRL2); - reg = (reg & (~AD1938_DAC_WORD_LEN_MASK)) | word_len; - snd_soc_write(codec, AD1938_DAC_CTRL2, reg); - - reg = snd_soc_read(codec, AD1938_ADC_CTRL1); - reg = (reg & (~AD1938_ADC_WORD_LEN_MASK)) | word_len; - snd_soc_write(codec, AD1938_ADC_CTRL1, reg); - - return 0; -} - -static int __devinit ad1938_spi_probe(struct spi_device *spi) -{ - struct snd_soc_codec *codec; - struct ad1938_priv *ad1938; - - ad1938 = kzalloc(sizeof(struct ad1938_priv), GFP_KERNEL); - if (ad1938 == NULL) - return -ENOMEM; - - codec = &ad1938->codec; - codec->control_data = spi; - codec->dev = &spi->dev; - - dev_set_drvdata(&spi->dev, ad1938); - - return ad1938_register(ad1938); -} - -static int __devexit ad1938_spi_remove(struct spi_device *spi) -{ - struct ad1938_priv *ad1938 = dev_get_drvdata(&spi->dev); - - ad1938_unregister(ad1938); - return 0; -} - -static struct spi_driver ad1938_spi_driver = { - .driver = { - .name = "ad1938", - .owner = THIS_MODULE, - }, - .probe = ad1938_spi_probe, - .remove = __devexit_p(ad1938_spi_remove), -}; - -static struct snd_soc_dai_ops ad1938_dai_ops = { - .hw_params = ad1938_hw_params, - .digital_mute = ad1938_mute, - .set_tdm_slot = ad1938_set_tdm_slot, - .set_fmt = ad1938_set_dai_fmt, -}; - -/* codec DAI instance */ -struct snd_soc_dai ad1938_dai = { - .name = "AD1938", - .playback = { - .stream_name = "Playback", - .channels_min = 2, - .channels_max = 8, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, - }, - .capture = { - .stream_name = "Capture", - .channels_min = 2, - .channels_max = 4, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, - }, - .ops = &ad1938_dai_ops, -}; -EXPORT_SYMBOL_GPL(ad1938_dai); - -static int ad1938_register(struct ad1938_priv *ad1938) -{ - int ret; - struct snd_soc_codec *codec = &ad1938->codec; - - if (ad1938_codec) { - dev_err(codec->dev, "Another ad1938 is registered\n"); - return -EINVAL; - } - - mutex_init(&codec->mutex); - INIT_LIST_HEAD(&codec->dapm_widgets); - INIT_LIST_HEAD(&codec->dapm_paths); - codec->private_data = ad1938; - codec->reg_cache = ad1938->reg_cache; - codec->reg_cache_size = AD1938_NUM_REGS; - codec->name = "AD1938"; - codec->owner = THIS_MODULE; - codec->dai = &ad1938_dai; - codec->num_dai = 1; - INIT_LIST_HEAD(&codec->dapm_widgets); - INIT_LIST_HEAD(&codec->dapm_paths); - - ad1938_dai.dev = codec->dev; - ad1938_codec = codec; - - memcpy(codec->reg_cache, ad1938_reg, AD1938_NUM_REGS); - - ret = snd_soc_codec_set_cache_io(codec, 16, 8, SND_SOC_SPI); - if (ret < 0) { - dev_err(codec->dev, "failed to set cache I/O: %d\n", - ret); - kfree(ad1938); - return ret; - } - - /* default setting for ad1938 */ - - /* unmute dac channels */ - snd_soc_write(codec, AD1938_DAC_CHNL_MUTE, 0x0); - /* de-emphasis: 48kHz, powedown dac */ - snd_soc_write(codec, AD1938_DAC_CTRL2, 0x1A); - /* powerdown dac, dac in tdm mode */ - snd_soc_write(codec, AD1938_DAC_CTRL0, 0x41); - /* high-pass filter enable */ - snd_soc_write(codec, AD1938_ADC_CTRL0, 0x3); - /* sata delay=1, adc aux mode */ - snd_soc_write(codec, AD1938_ADC_CTRL1, 0x43); - /* pll input: mclki/xi */ - snd_soc_write(codec, AD1938_PLL_CLK_CTRL0, 0x9D); - snd_soc_write(codec, AD1938_PLL_CLK_CTRL1, 0x04); - - ret = snd_soc_register_codec(codec); - if (ret != 0) { - dev_err(codec->dev, "Failed to register codec: %d\n", ret); - kfree(ad1938); - return ret; - } - - ret = snd_soc_register_dai(&ad1938_dai); - if (ret != 0) { - dev_err(codec->dev, "Failed to register DAI: %d\n", ret); - snd_soc_unregister_codec(codec); - kfree(ad1938); - return ret; - } - - return 0; -} - -static void ad1938_unregister(struct ad1938_priv *ad1938) -{ - snd_soc_unregister_dai(&ad1938_dai); - snd_soc_unregister_codec(&ad1938->codec); - kfree(ad1938); - ad1938_codec = NULL; -} - -static int ad1938_probe(struct platform_device *pdev) -{ - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec; - int ret = 0; - - if (ad1938_codec == NULL) { - dev_err(&pdev->dev, "Codec device not registered\n"); - return -ENODEV; - } - - socdev->card->codec = ad1938_codec; - codec = ad1938_codec; - - /* register pcms */ - ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); - if (ret < 0) { - dev_err(codec->dev, "failed to create pcms: %d\n", ret); - goto pcm_err; - } - - snd_soc_add_controls(codec, ad1938_snd_controls, - ARRAY_SIZE(ad1938_snd_controls)); - snd_soc_dapm_new_controls(codec, ad1938_dapm_widgets, - ARRAY_SIZE(ad1938_dapm_widgets)); - snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); - - -pcm_err: - return ret; -} - -/* power down chip */ -static int ad1938_remove(struct platform_device *pdev) -{ - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); - - return 0; -} - -struct snd_soc_codec_device soc_codec_dev_ad1938 = { - .probe = ad1938_probe, - .remove = ad1938_remove, -}; -EXPORT_SYMBOL_GPL(soc_codec_dev_ad1938); - -static int __init ad1938_init(void) -{ - int ret; - - ret = spi_register_driver(&ad1938_spi_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register ad1938 SPI driver: %d\n", - ret); - } - - return ret; -} -module_init(ad1938_init); - -static void __exit ad1938_exit(void) -{ - spi_unregister_driver(&ad1938_spi_driver); -} -module_exit(ad1938_exit); - -MODULE_DESCRIPTION("ASoC ad1938 driver"); -MODULE_AUTHOR("Barry Song "); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad1938.h b/sound/soc/codecs/ad1938.h deleted file mode 100644 index fe3c48c..0000000 --- a/sound/soc/codecs/ad1938.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * File: sound/soc/codecs/ad1836.h - * Based on: - * Author: Barry Song Barry.Song@analog.com - * - * Created: May 25, 2009 - * Description: definitions for AD1938 registers - * - * Modified: - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __AD1938_H__ -#define __AD1938_H__ - -#define AD1938_PLL_CLK_CTRL0 0 -#define AD1938_PLL_POWERDOWN 0x01 -#define AD1938_PLL_CLK_CTRL1 1 -#define AD1938_DAC_CTRL0 2 -#define AD1938_DAC_POWERDOWN 0x01 -#define AD1938_DAC_SERFMT_MASK 0xC0 -#define AD1938_DAC_SERFMT_STEREO (0 << 6) -#define AD1938_DAC_SERFMT_TDM (1 << 6) -#define AD1938_DAC_CTRL1 3 -#define AD1938_DAC_2_CHANNELS 0 -#define AD1938_DAC_4_CHANNELS 1 -#define AD1938_DAC_8_CHANNELS 2 -#define AD1938_DAC_16_CHANNELS 3 -#define AD1938_DAC_CHAN_SHFT 1 -#define AD1938_DAC_CHAN_MASK (3 << AD1938_DAC_CHAN_SHFT) -#define AD1938_DAC_LCR_MASTER (1 << 4) -#define AD1938_DAC_BCLK_MASTER (1 << 5) -#define AD1938_DAC_LEFT_HIGH (1 << 3) -#define AD1938_DAC_BCLK_INV (1 << 7) -#define AD1938_DAC_CTRL2 4 -#define AD1938_DAC_WORD_LEN_MASK 0xC -#define AD1938_DAC_MASTER_MUTE 1 -#define AD1938_DAC_CHNL_MUTE 5 -#define AD1938_DACL1_MUTE 0 -#define AD1938_DACR1_MUTE 1 -#define AD1938_DACL2_MUTE 2 -#define AD1938_DACR2_MUTE 3 -#define AD1938_DACL3_MUTE 4 -#define AD1938_DACR3_MUTE 5 -#define AD1938_DACL4_MUTE 6 -#define AD1938_DACR4_MUTE 7 -#define AD1938_DAC_L1_VOL 6 -#define AD1938_DAC_R1_VOL 7 -#define AD1938_DAC_L2_VOL 8 -#define AD1938_DAC_R2_VOL 9 -#define AD1938_DAC_L3_VOL 10 -#define AD1938_DAC_R3_VOL 11 -#define AD1938_DAC_L4_VOL 12 -#define AD1938_DAC_R4_VOL 13 -#define AD1938_ADC_CTRL0 14 -#define AD1938_ADC_POWERDOWN 0x01 -#define AD1938_ADC_HIGHPASS_FILTER 1 -#define AD1938_ADCL1_MUTE 2 -#define AD1938_ADCR1_MUTE 3 -#define AD1938_ADCL2_MUTE 4 -#define AD1938_ADCR2_MUTE 5 -#define AD1938_ADC_CTRL1 15 -#define AD1938_ADC_SERFMT_MASK 0x60 -#define AD1938_ADC_SERFMT_STEREO (0 << 5) -#define AD1938_ADC_SERFMT_TDM (1 << 2) -#define AD1938_ADC_SERFMT_AUX (2 << 5) -#define AD1938_ADC_WORD_LEN_MASK 0x3 -#define AD1938_ADC_CTRL2 16 -#define AD1938_ADC_2_CHANNELS 0 -#define AD1938_ADC_4_CHANNELS 1 -#define AD1938_ADC_8_CHANNELS 2 -#define AD1938_ADC_16_CHANNELS 3 -#define AD1938_ADC_CHAN_SHFT 4 -#define AD1938_ADC_CHAN_MASK (3 << AD1938_ADC_CHAN_SHFT) -#define AD1938_ADC_LCR_MASTER (1 << 3) -#define AD1938_ADC_BCLK_MASTER (1 << 6) -#define AD1938_ADC_LEFT_HIGH (1 << 2) -#define AD1938_ADC_BCLK_INV (1 << 1) - -#define AD1938_NUM_REGS 17 - -extern struct snd_soc_dai ad1938_dai; -extern struct snd_soc_codec_device soc_codec_dev_ad1938; -#endif diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c new file mode 100644 index 0000000..1965ff2 --- /dev/null +++ b/sound/soc/codecs/ad193x-i2c.c @@ -0,0 +1,56 @@ +/* + * AD193X Audio Codec I2C bus driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <sound/soc.h> +#include "ad193x.h" + +static int __devinit ad193x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return ad193x_bus_probe(&client->dev, client, SND_SOC_I2C); +} + +static int __devexit ad193x_i2c_remove(struct i2c_client *client) +{ + return ad193x_bus_remove(&client->dev); +} + +static const struct i2c_device_id ad193x_id[] = { + { "ad1937", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad193x_id); + +static struct i2c_driver ad193x_i2c_driver = { + .driver = { + .name = "ad193x", + }, + .probe = ad193x_i2c_probe, + .remove = __devexit_p(ad193x_i2c_remove), + .id_table = ad193x_id, +}; + +static __init int ad193x_i2c_init(void) +{ + return i2c_add_driver(&ad193x_i2c_driver); +} +module_init(ad193x_i2c_init); + +static __exit void ad193x_i2c_exit(void) +{ + i2c_del_driver(&ad193x_i2c_driver); +} +module_exit(ad193x_i2c_exit); + +MODULE_DESCRIPTION("ASoC ad193x i2c driver"); +MODULE_AUTHOR("Barry Song 21cnbao@gmail.com"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c new file mode 100644 index 0000000..213e927 --- /dev/null +++ b/sound/soc/codecs/ad193x-spi.c @@ -0,0 +1,49 @@ +/* + * AD193X Audio Codec SPI bus driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <sound/soc.h> +#include "ad193x.h" + +static int __devinit ad193x_spi_probe(struct spi_device *spi) +{ + return ad193x_bus_probe(&spi->dev, spi, SND_SOC_SPI); +} + +static int __devexit ad193x_spi_remove(struct spi_device *spi) +{ + return ad193x_bus_remove(&spi->dev); +} + +static struct spi_driver ad193x_spi_driver = { + .driver = { + .name = "ad193x", + .owner = THIS_MODULE, + }, + .probe = ad193x_spi_probe, + .remove = __devexit_p(ad193x_spi_remove), +}; + +static int __init ad193x_spi_init(void) +{ + return spi_register_driver(&ad193x_spi_driver); +} +module_init(ad193x_spi_init); + +static void __exit ad193x_spi_exit(void) +{ + spi_unregister_driver(&ad193x_spi_driver); +} +module_exit(ad193x_spi_exit); + +MODULE_DESCRIPTION("ASoC ad193x spi driver"); +MODULE_AUTHOR("Barry Song 21cnbao@gmail.com"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c new file mode 100644 index 0000000..18531e2 --- /dev/null +++ b/sound/soc/codecs/ad193x.c @@ -0,0 +1,475 @@ +/* + * AD193X Audio Codec driver supporting AD1936/7/8/9 + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <sound/soc-dapm.h> +#include "ad193x.h" + +/* codec private data */ +struct ad193x_priv { + struct snd_soc_codec codec; + u8 reg_cache[AD193X_NUM_REGS]; +}; + +/* ad193x register cache & default register settings */ +static const u8 ad193x_reg[AD193X_NUM_REGS] = { + 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, +}; + +static struct snd_soc_codec *ad193x_codec; +struct snd_soc_codec_device soc_codec_dev_ad193x; +static int ad193x_register(struct ad193x_priv *ad193x, int bus_type); +static void ad193x_unregister(struct ad193x_priv *ad193x); + +/* + * AD193X volume/mute/de-emphasis etc. controls + */ +static const char *ad193x_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"}; + +static const struct soc_enum ad193x_deemp_enum = + SOC_ENUM_SINGLE(AD193X_DAC_CTRL2, 1, 4, ad193x_deemp); + +static const struct snd_kcontrol_new ad193x_snd_controls[] = { + /* DAC volume control */ + SOC_DOUBLE_R("DAC1 Volume", AD193X_DAC_L1_VOL, + AD193X_DAC_R1_VOL, 0, 0xFF, 1), + SOC_DOUBLE_R("DAC2 Volume", AD193X_DAC_L2_VOL, + AD193X_DAC_R2_VOL, 0, 0xFF, 1), + SOC_DOUBLE_R("DAC3 Volume", AD193X_DAC_L3_VOL, + AD193X_DAC_R3_VOL, 0, 0xFF, 1), + SOC_DOUBLE_R("DAC4 Volume", AD193X_DAC_L4_VOL, + AD193X_DAC_R4_VOL, 0, 0xFF, 1), + + /* ADC switch control */ + SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE, + AD193X_ADCR1_MUTE, 1, 1), + SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE, + AD193X_ADCR2_MUTE, 1, 1), + + /* DAC switch control */ + SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE, + AD193X_DACR1_MUTE, 1, 1), + SOC_DOUBLE("DAC2 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL2_MUTE, + AD193X_DACR2_MUTE, 1, 1), + SOC_DOUBLE("DAC3 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL3_MUTE, + AD193X_DACR3_MUTE, 1, 1), + SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE, + AD193X_DACR4_MUTE, 1, 1), + + /* ADC high-pass filter */ + SOC_SINGLE("ADC High Pass Filter Switch", AD193X_ADC_CTRL0, + AD193X_ADC_HIGHPASS_FILTER, 1, 0), + + /* DAC de-emphasis */ + SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum), +}; + +static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", AD193X_DAC_CTRL0, 0, 1), + SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0), + SND_SOC_DAPM_OUTPUT("DAC1OUT"), + SND_SOC_DAPM_OUTPUT("DAC2OUT"), + SND_SOC_DAPM_OUTPUT("DAC3OUT"), + SND_SOC_DAPM_OUTPUT("DAC4OUT"), + SND_SOC_DAPM_INPUT("ADC1IN"), + SND_SOC_DAPM_INPUT("ADC2IN"), +}; + +static const struct snd_soc_dapm_route audio_paths[] = { + { "DAC", NULL, "PLL_PWR" }, + { "ADC", NULL, "PLL_PWR" }, + { "DAC", NULL, "ADC_PWR" }, + { "ADC", NULL, "ADC_PWR" }, + { "DAC1OUT", "DAC1 Switch", "DAC" }, + { "DAC2OUT", "DAC2 Switch", "DAC" }, + { "DAC3OUT", "DAC3 Switch", "DAC" }, + { "DAC4OUT", "DAC4 Switch", "DAC" }, + { "ADC", "ADC1 Switch", "ADC1IN" }, + { "ADC", "ADC2 Switch", "ADC2IN" }, +}; + +/* + * DAI ops entries + */ + +static int ad193x_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + int reg; + + reg = snd_soc_read(codec, AD193X_DAC_CTRL2); + reg = (mute > 0) ? reg | AD193X_DAC_MASTER_MUTE : reg & + (~AD193X_DAC_MASTER_MUTE); + snd_soc_write(codec, AD193X_DAC_CTRL2, reg); + + return 0; +} + +static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct snd_soc_codec *codec = dai->codec; + int dac_reg = snd_soc_read(codec, AD193X_DAC_CTRL1); + int adc_reg = snd_soc_read(codec, AD193X_ADC_CTRL2); + + dac_reg &= ~AD193X_DAC_CHAN_MASK; + adc_reg &= ~AD193X_ADC_CHAN_MASK; + + switch (slots) { + case 2: + dac_reg |= AD193X_DAC_2_CHANNELS << AD193X_DAC_CHAN_SHFT; + adc_reg |= AD193X_ADC_2_CHANNELS << AD193X_ADC_CHAN_SHFT; + break; + case 4: + dac_reg |= AD193X_DAC_4_CHANNELS << AD193X_DAC_CHAN_SHFT; + adc_reg |= AD193X_ADC_4_CHANNELS << AD193X_ADC_CHAN_SHFT; + break; + case 8: + dac_reg |= AD193X_DAC_8_CHANNELS << AD193X_DAC_CHAN_SHFT; + adc_reg |= AD193X_ADC_8_CHANNELS << AD193X_ADC_CHAN_SHFT; + break; + case 16: + dac_reg |= AD193X_DAC_16_CHANNELS << AD193X_DAC_CHAN_SHFT; + adc_reg |= AD193X_ADC_16_CHANNELS << AD193X_ADC_CHAN_SHFT; + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, AD193X_DAC_CTRL1, dac_reg); + snd_soc_write(codec, AD193X_ADC_CTRL2, adc_reg); + + return 0; +} + +static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + int adc_reg, dac_reg; + + adc_reg = snd_soc_read(codec, AD193X_ADC_CTRL2); + dac_reg = snd_soc_read(codec, AD193X_DAC_CTRL1); + + /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S + * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) + */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + adc_reg &= ~AD193X_ADC_SERFMT_MASK; + adc_reg |= AD193X_ADC_SERFMT_TDM; + break; + case SND_SOC_DAIFMT_DSP_A: + adc_reg &= ~AD193X_ADC_SERFMT_MASK; + adc_reg |= AD193X_ADC_SERFMT_AUX; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */ + adc_reg &= ~AD193X_ADC_LEFT_HIGH; + adc_reg &= ~AD193X_ADC_BCLK_INV; + dac_reg &= ~AD193X_DAC_LEFT_HIGH; + dac_reg &= ~AD193X_DAC_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */ + adc_reg |= AD193X_ADC_LEFT_HIGH; + adc_reg &= ~AD193X_ADC_BCLK_INV; + dac_reg |= AD193X_DAC_LEFT_HIGH; + dac_reg &= ~AD193X_DAC_BCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */ + adc_reg &= ~AD193X_ADC_LEFT_HIGH; + adc_reg |= AD193X_ADC_BCLK_INV; + dac_reg &= ~AD193X_DAC_LEFT_HIGH; + dac_reg |= AD193X_DAC_BCLK_INV; + break; + + case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */ + adc_reg |= AD193X_ADC_LEFT_HIGH; + adc_reg |= AD193X_ADC_BCLK_INV; + dac_reg |= AD193X_DAC_LEFT_HIGH; + dac_reg |= AD193X_DAC_BCLK_INV; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */ + adc_reg |= AD193X_ADC_LCR_MASTER; + adc_reg |= AD193X_ADC_BCLK_MASTER; + dac_reg |= AD193X_DAC_LCR_MASTER; + dac_reg |= AD193X_DAC_BCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */ + adc_reg |= AD193X_ADC_LCR_MASTER; + adc_reg &= ~AD193X_ADC_BCLK_MASTER; + dac_reg |= AD193X_DAC_LCR_MASTER; + dac_reg &= ~AD193X_DAC_BCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */ + adc_reg &= ~AD193X_ADC_LCR_MASTER; + adc_reg |= AD193X_ADC_BCLK_MASTER; + dac_reg &= ~AD193X_DAC_LCR_MASTER; + dac_reg |= AD193X_DAC_BCLK_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */ + adc_reg &= ~AD193X_ADC_LCR_MASTER; + adc_reg &= ~AD193X_ADC_BCLK_MASTER; + dac_reg &= ~AD193X_DAC_LCR_MASTER; + dac_reg &= ~AD193X_DAC_BCLK_MASTER; + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, AD193X_ADC_CTRL2, adc_reg); + snd_soc_write(codec, AD193X_DAC_CTRL1, dac_reg); + + return 0; +} + +static int ad193x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + int word_len = 0, reg = 0; + + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->card->codec; + + /* bit size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + word_len = 3; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + word_len = 1; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + word_len = 0; + break; + } + + reg = snd_soc_read(codec, AD193X_DAC_CTRL2); + reg = (reg & (~AD193X_DAC_WORD_LEN_MASK)) | word_len; + snd_soc_write(codec, AD193X_DAC_CTRL2, reg); + + reg = snd_soc_read(codec, AD193X_ADC_CTRL1); + reg = (reg & (~AD193X_ADC_WORD_LEN_MASK)) | word_len; + snd_soc_write(codec, AD193X_ADC_CTRL1, reg); + + return 0; +} + +int ad193x_bus_probe(struct device *dev, void *ctrl_data, int bus_type) +{ + struct snd_soc_codec *codec; + struct ad193x_priv *ad193x; + + ad193x = kzalloc(sizeof(struct ad193x_priv), GFP_KERNEL); + if (ad193x == NULL) + return -ENOMEM; + + codec = &ad193x->codec; + codec->control_data = ctrl_data; + codec->dev = dev; + + dev_set_drvdata(dev, ad193x); + + return ad193x_register(ad193x, bus_type); +} +EXPORT_SYMBOL_GPL(ad193x_bus_probe); + +int ad193x_bus_remove(struct device *dev) +{ + struct ad193x_priv *ad193x = dev_get_drvdata(dev); + + ad193x_unregister(ad193x); + return 0; +} +EXPORT_SYMBOL_GPL(ad193x_bus_remove); + +static struct snd_soc_dai_ops ad193x_dai_ops = { + .hw_params = ad193x_hw_params, + .digital_mute = ad193x_mute, + .set_tdm_slot = ad193x_set_tdm_slot, + .set_fmt = ad193x_set_dai_fmt, +}; + +/* codec DAI instance */ +struct snd_soc_dai ad193x_dai = { + .name = "AD193X", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &ad193x_dai_ops, +}; +EXPORT_SYMBOL_GPL(ad193x_dai); + +static int ad193x_register(struct ad193x_priv *ad193x, int bus_type) +{ + int ret; + struct snd_soc_codec *codec = &ad193x->codec; + + if (ad193x_codec) { + dev_err(codec->dev, "Another ad193x is registered\n"); + return -EINVAL; + } + + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + codec->private_data = ad193x; + codec->reg_cache = ad193x->reg_cache; + codec->reg_cache_size = AD193X_NUM_REGS; + codec->name = "AD193X"; + codec->owner = THIS_MODULE; + codec->dai = &ad193x_dai; + codec->num_dai = 1; + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + ad193x_dai.dev = codec->dev; + ad193x_codec = codec; + + memcpy(codec->reg_cache, ad193x_reg, AD193X_NUM_REGS); + + if (bus_type == SND_SOC_I2C) + ret = snd_soc_codec_set_cache_io(codec, 8, 8, bus_type); + else + ret = snd_soc_codec_set_cache_io(codec, 16, 8, bus_type); + if (ret < 0) { + dev_err(codec->dev, "failed to set cache I/O: %d\n", + ret); + kfree(ad193x); + return ret; + } + + /* default setting for ad193x */ + + /* unmute dac channels */ + snd_soc_write(codec, AD193X_DAC_CHNL_MUTE, 0x0); + /* de-emphasis: 48kHz, powedown dac */ + snd_soc_write(codec, AD193X_DAC_CTRL2, 0x1A); + /* powerdown dac, dac in tdm mode */ + snd_soc_write(codec, AD193X_DAC_CTRL0, 0x41); + /* high-pass filter enable */ + snd_soc_write(codec, AD193X_ADC_CTRL0, 0x3); + /* sata delay=1, adc aux mode */ + snd_soc_write(codec, AD193X_ADC_CTRL1, 0x43); + /* pll input: mclki/xi */ + snd_soc_write(codec, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */ + snd_soc_write(codec, AD193X_PLL_CLK_CTRL1, 0x04); + + ret = snd_soc_register_codec(codec); + if (ret != 0) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + kfree(ad193x); + return ret; + } + + ret = snd_soc_register_dai(&ad193x_dai); + if (ret != 0) { + dev_err(codec->dev, "Failed to register DAI: %d\n", ret); + snd_soc_unregister_codec(codec); + kfree(ad193x); + return ret; + } + + return 0; +} + +static void ad193x_unregister(struct ad193x_priv *ad193x) +{ + snd_soc_unregister_dai(&ad193x_dai); + snd_soc_unregister_codec(&ad193x->codec); + kfree(ad193x); + ad193x_codec = NULL; +} + +static int ad193x_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + if (ad193x_codec == NULL) { + dev_err(&pdev->dev, "Codec device not registered\n"); + return -ENODEV; + } + + socdev->card->codec = ad193x_codec; + codec = ad193x_codec; + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + dev_err(codec->dev, "failed to create pcms: %d\n", ret); + goto pcm_err; + } + + snd_soc_add_controls(codec, ad193x_snd_controls, + ARRAY_SIZE(ad193x_snd_controls)); + snd_soc_dapm_new_controls(codec, ad193x_dapm_widgets, + ARRAY_SIZE(ad193x_dapm_widgets)); + snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths)); + +pcm_err: + return ret; +} + +/* power down chip */ +static int ad193x_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_ad193x = { + .probe = ad193x_probe, + .remove = ad193x_remove, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_ad193x); + +MODULE_DESCRIPTION("ASoC ad193x driver"); +MODULE_AUTHOR("Barry Song 21cnbao@gmail.com"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h new file mode 100644 index 0000000..781353c --- /dev/null +++ b/sound/soc/codecs/ad193x.h @@ -0,0 +1,82 @@ +/* + * AD193X Audio Codec driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __AD193X_H__ +#define __AD193X_H__ + +#define AD193X_PLL_CLK_CTRL0 0x800 +#define AD193X_PLL_POWERDOWN 0x01 +#define AD193X_PLL_CLK_CTRL1 0x801 +#define AD193X_DAC_CTRL0 0x802 +#define AD193X_DAC_POWERDOWN 0x01 +#define AD193X_DAC_SERFMT_MASK 0xC0 +#define AD193X_DAC_SERFMT_STEREO (0 << 6) +#define AD193X_DAC_SERFMT_TDM (1 << 6) +#define AD193X_DAC_CTRL1 0x803 +#define AD193X_DAC_2_CHANNELS 0 +#define AD193X_DAC_4_CHANNELS 1 +#define AD193X_DAC_8_CHANNELS 2 +#define AD193X_DAC_16_CHANNELS 3 +#define AD193X_DAC_CHAN_SHFT 1 +#define AD193X_DAC_CHAN_MASK (3 << AD193X_DAC_CHAN_SHFT) +#define AD193X_DAC_LCR_MASTER (1 << 4) +#define AD193X_DAC_BCLK_MASTER (1 << 5) +#define AD193X_DAC_LEFT_HIGH (1 << 3) +#define AD193X_DAC_BCLK_INV (1 << 7) +#define AD193X_DAC_CTRL2 0x804 +#define AD193X_DAC_WORD_LEN_MASK 0xC +#define AD193X_DAC_MASTER_MUTE 1 +#define AD193X_DAC_CHNL_MUTE 0x805 +#define AD193X_DACL1_MUTE 0 +#define AD193X_DACR1_MUTE 1 +#define AD193X_DACL2_MUTE 2 +#define AD193X_DACR2_MUTE 3 +#define AD193X_DACL3_MUTE 4 +#define AD193X_DACR3_MUTE 5 +#define AD193X_DACL4_MUTE 6 +#define AD193X_DACR4_MUTE 7 +#define AD193X_DAC_L1_VOL 0x806 +#define AD193X_DAC_R1_VOL 0x807 +#define AD193X_DAC_L2_VOL 0x808 +#define AD193X_DAC_R2_VOL 0x809 +#define AD193X_DAC_L3_VOL 0x80a +#define AD193X_DAC_R3_VOL 0x80b +#define AD193X_DAC_L4_VOL 0x80c +#define AD193X_DAC_R4_VOL 0x80d +#define AD193X_ADC_CTRL0 0x80e +#define AD193X_ADC_POWERDOWN 0x01 +#define AD193X_ADC_HIGHPASS_FILTER 1 +#define AD193X_ADCL1_MUTE 2 +#define AD193X_ADCR1_MUTE 3 +#define AD193X_ADCL2_MUTE 4 +#define AD193X_ADCR2_MUTE 5 +#define AD193X_ADC_CTRL1 0x80f +#define AD193X_ADC_SERFMT_MASK 0x60 +#define AD193X_ADC_SERFMT_STEREO (0 << 5) +#define AD193X_ADC_SERFMT_TDM (1 << 2) +#define AD193X_ADC_SERFMT_AUX (2 << 5) +#define AD193X_ADC_WORD_LEN_MASK 0x3 +#define AD193X_ADC_CTRL2 0x810 +#define AD193X_ADC_2_CHANNELS 0 +#define AD193X_ADC_4_CHANNELS 1 +#define AD193X_ADC_8_CHANNELS 2 +#define AD193X_ADC_16_CHANNELS 3 +#define AD193X_ADC_CHAN_SHFT 4 +#define AD193X_ADC_CHAN_MASK (3 << AD193X_ADC_CHAN_SHFT) +#define AD193X_ADC_LCR_MASTER (1 << 3) +#define AD193X_ADC_BCLK_MASTER (1 << 6) +#define AD193X_ADC_LEFT_HIGH (1 << 2) +#define AD193X_ADC_BCLK_INV (1 << 1) + +#define AD193X_NUM_REGS 17 + +extern struct snd_soc_dai ad193x_dai; +extern struct snd_soc_codec_device soc_codec_dev_ad193x; +extern int ad193x_bus_probe(struct device *dev, void *ctrl_data, int bus_type); +extern int ad193x_bus_remove(struct device *dev); +#endif
Signed-off-by: Barry Song barry.song@analog.com --- sound/soc/blackfin/Kconfig | 13 ++- sound/soc/blackfin/Makefile | 4 +- sound/soc/blackfin/bf5xx-ad1938.c | 149 ------------------------------------- sound/soc/blackfin/bf5xx-ad193x.c | 149 +++++++++++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 156 deletions(-) delete mode 100644 sound/soc/blackfin/bf5xx-ad1938.c create mode 100644 sound/soc/blackfin/bf5xx-ad193x.c
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig index 97f1a25..f04c2de 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig @@ -49,13 +49,16 @@ config SND_BF5XX_SOC_AD1836 help Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
-config SND_BF5XX_SOC_AD1938 - tristate "SoC AD1938 Audio support for Blackfin" - depends on SND_BF5XX_TDM +config SND_BF5XX_SOC_AD193X + tristate "SoC AD193X Audio support for Blackfin" + depends on SND_BF5XX_TDM && (SPI_MASTER || I2C) select SND_BF5XX_SOC_TDM - select SND_SOC_AD1938 + select SND_SOC_AD193X + select SND_SOC_AD193X_SPI if SPI_MASTER + select SND_SOC_AD193X_I2C if I2C help - Say Y if you want to add support for AD1938 codec on Blackfin. + Say Y if you want to add support for AD193X codec on Blackfin. + This driver supports AD1936, AD1937, AD1938 and AD1939.
config SND_BF5XX_AC97 tristate "SoC AC97 Audio for the ADI BF5xx chip" diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile index 87e3042..49af3f3 100644 --- a/sound/soc/blackfin/Makefile +++ b/sound/soc/blackfin/Makefile @@ -20,10 +20,10 @@ snd-ad1836-objs := bf5xx-ad1836.o snd-ad1980-objs := bf5xx-ad1980.o snd-ssm2602-objs := bf5xx-ssm2602.o snd-ad73311-objs := bf5xx-ad73311.o -snd-ad1938-objs := bf5xx-ad1938.o +snd-ad193x-objs := bf5xx-ad193x.o
obj-$(CONFIG_SND_BF5XX_SOC_AD1836) += snd-ad1836.o obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o -obj-$(CONFIG_SND_BF5XX_SOC_AD1938) += snd-ad1938.o +obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o diff --git a/sound/soc/blackfin/bf5xx-ad1938.c b/sound/soc/blackfin/bf5xx-ad1938.c deleted file mode 100644 index 2ef1e50..0000000 --- a/sound/soc/blackfin/bf5xx-ad1938.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * File: sound/soc/blackfin/bf5xx-ad1938.c - * Author: Barry Song Barry.Song@analog.com - * - * Created: Thur June 4 2009 - * Description: Board driver for ad1938 sound chip - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/device.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/soc.h> -#include <sound/soc-dapm.h> -#include <sound/pcm_params.h> - -#include <asm/blackfin.h> -#include <asm/cacheflush.h> -#include <asm/irq.h> -#include <asm/dma.h> -#include <asm/portmux.h> - -#include "../codecs/ad1938.h" -#include "bf5xx-sport.h" - -#include "bf5xx-tdm-pcm.h" -#include "bf5xx-tdm.h" - -static struct snd_soc_card bf5xx_ad1938; - -static int bf5xx_ad1938_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - - cpu_dai->private_data = sport_handle; - return 0; -} - -static int bf5xx_ad1938_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_dai *cpu_dai = rtd->dai->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; - unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7}; - int ret = 0; - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - /* set codec DAI slots, 8 channels, all channels are enabled */ - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32); - if (ret < 0) - return ret; - - /* set cpu DAI channel mapping */ - ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map), - channel_map, ARRAY_SIZE(channel_map), channel_map); - if (ret < 0) - return ret; - - return 0; -} - -static struct snd_soc_ops bf5xx_ad1938_ops = { - .startup = bf5xx_ad1938_startup, - .hw_params = bf5xx_ad1938_hw_params, -}; - -static struct snd_soc_dai_link bf5xx_ad1938_dai = { - .name = "ad1938", - .stream_name = "AD1938", - .cpu_dai = &bf5xx_tdm_dai, - .codec_dai = &ad1938_dai, - .ops = &bf5xx_ad1938_ops, -}; - -static struct snd_soc_card bf5xx_ad1938 = { - .name = "bf5xx_ad1938", - .platform = &bf5xx_tdm_soc_platform, - .dai_link = &bf5xx_ad1938_dai, - .num_links = 1, -}; - -static struct snd_soc_device bf5xx_ad1938_snd_devdata = { - .card = &bf5xx_ad1938, - .codec_dev = &soc_codec_dev_ad1938, -}; - -static struct platform_device *bfxx_ad1938_snd_device; - -static int __init bf5xx_ad1938_init(void) -{ - int ret; - - bfxx_ad1938_snd_device = platform_device_alloc("soc-audio", -1); - if (!bfxx_ad1938_snd_device) - return -ENOMEM; - - platform_set_drvdata(bfxx_ad1938_snd_device, &bf5xx_ad1938_snd_devdata); - bf5xx_ad1938_snd_devdata.dev = &bfxx_ad1938_snd_device->dev; - ret = platform_device_add(bfxx_ad1938_snd_device); - - if (ret) - platform_device_put(bfxx_ad1938_snd_device); - - return ret; -} - -static void __exit bf5xx_ad1938_exit(void) -{ - platform_device_unregister(bfxx_ad1938_snd_device); -} - -module_init(bf5xx_ad1938_init); -module_exit(bf5xx_ad1938_exit); - -/* Module information */ -MODULE_AUTHOR("Barry Song"); -MODULE_DESCRIPTION("ALSA SoC AD1938 board driver"); -MODULE_LICENSE("GPL"); - diff --git a/sound/soc/blackfin/bf5xx-ad193x.c b/sound/soc/blackfin/bf5xx-ad193x.c new file mode 100644 index 0000000..b8c9060 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ad193x.c @@ -0,0 +1,149 @@ +/* + * File: sound/soc/blackfin/bf5xx-ad193x.c + * Author: Barry Song Barry.Song@analog.com + * + * Created: Thur June 4 2009 + * Description: Board driver for ad193x sound chip + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/pcm_params.h> + +#include <asm/blackfin.h> +#include <asm/cacheflush.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +#include "../codecs/ad193x.h" +#include "bf5xx-sport.h" + +#include "bf5xx-tdm-pcm.h" +#include "bf5xx-tdm.h" + +static struct snd_soc_card bf5xx_ad193x; + +static int bf5xx_ad193x_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + + cpu_dai->private_data = sport_handle; + return 0; +} + +static int bf5xx_ad193x_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_dai *cpu_dai = rtd->dai->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7}; + int ret = 0; + /* set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + /* set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + /* set codec DAI slots, 8 channels, all channels are enabled */ + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32); + if (ret < 0) + return ret; + + /* set cpu DAI channel mapping */ + ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map), + channel_map, ARRAY_SIZE(channel_map), channel_map); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops bf5xx_ad193x_ops = { + .startup = bf5xx_ad193x_startup, + .hw_params = bf5xx_ad193x_hw_params, +}; + +static struct snd_soc_dai_link bf5xx_ad193x_dai = { + .name = "ad193x", + .stream_name = "AD193X", + .cpu_dai = &bf5xx_tdm_dai, + .codec_dai = &ad193x_dai, + .ops = &bf5xx_ad193x_ops, +}; + +static struct snd_soc_card bf5xx_ad193x = { + .name = "bf5xx_ad193x", + .platform = &bf5xx_tdm_soc_platform, + .dai_link = &bf5xx_ad193x_dai, + .num_links = 1, +}; + +static struct snd_soc_device bf5xx_ad193x_snd_devdata = { + .card = &bf5xx_ad193x, + .codec_dev = &soc_codec_dev_ad193x, +}; + +static struct platform_device *bfxx_ad193x_snd_device; + +static int __init bf5xx_ad193x_init(void) +{ + int ret; + + bfxx_ad193x_snd_device = platform_device_alloc("soc-audio", -1); + if (!bfxx_ad193x_snd_device) + return -ENOMEM; + + platform_set_drvdata(bfxx_ad193x_snd_device, &bf5xx_ad193x_snd_devdata); + bf5xx_ad193x_snd_devdata.dev = &bfxx_ad193x_snd_device->dev; + ret = platform_device_add(bfxx_ad193x_snd_device); + + if (ret) + platform_device_put(bfxx_ad193x_snd_device); + + return ret; +} + +static void __exit bf5xx_ad193x_exit(void) +{ + platform_device_unregister(bfxx_ad193x_snd_device); +} + +module_init(bf5xx_ad193x_init); +module_exit(bf5xx_ad193x_exit); + +/* Module information */ +MODULE_AUTHOR("Barry Song"); +MODULE_DESCRIPTION("ALSA SoC AD193X board driver"); +MODULE_LICENSE("GPL"); +
From: Cliff Cai cliff.cai@analog.com
Signed-off-by: Cliff Cai cliff.cai@analog.com Signed-off-by: Barry Song barry.song@analog.com --- sound/soc/soc-cache.c | 35 +++++++++++++++++++++++++++++++++++ 1 files changed, 35 insertions(+), 0 deletions(-)
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index bf593a8..91db7af 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -226,6 +226,40 @@ static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec, }
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec, + unsigned int r) +{ + struct i2c_msg xfer[2]; + u8 reg = r; + u8 data; + int ret; + struct i2c_client *client = codec->control_data; + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = ® + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 1; + xfer[1].buf = &data; + + ret = i2c_transfer(client->adapter, xfer, 2); + if (ret != 2) { + dev_err(&client->dev, "i2c_transfer() returned %d\n", ret); + return 0; + } + + return data; +} +#else +#define snd_soc_8_8_read_i2c NULL +#endif + +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec, unsigned int r) { @@ -466,6 +500,7 @@ static struct { { .addr_bits = 8, .data_bits = 8, .write = snd_soc_8_8_write, .read = snd_soc_8_8_read, + .i2c_read = snd_soc_8_8_read_i2c, }, { .addr_bits = 8, .data_bits = 16,
The registers for AD193X are defined as 0x800-0x810 for spi which uses 16_8 mode, for i2c to support AD1937, we will use 8_8 mode, only the low byte of 0x800-0x810 is valid. The patch will not destory other codecs, but make soc cache interface more useful.
Signed-off-by: Barry Song barry.song@analog.com --- sound/soc/soc-cache.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 91db7af..9dfe9a5 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -159,7 +159,8 @@ static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
BUG_ON(codec->volatile_register);
- data[0] = reg & 0xff; + reg &= 0xff; + data[0] = reg; data[1] = value & 0xff;
if (reg < codec->reg_cache_size) @@ -180,6 +181,7 @@ static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec, unsigned int reg) { u8 *cache = codec->reg_cache; + reg &= 0xff; if (reg >= codec->reg_cache_size) return -1; return cache[reg];
On Thu, 2010-03-18 at 16:17 +0800, Barry Song wrote:
The registers for AD193X are defined as 0x800-0x810 for spi which uses 16_8 mode, for i2c to support AD1937, we will use 8_8 mode, only the low byte of 0x800-0x810 is valid. The patch will not destory other codecs, but make soc cache interface more useful.
Signed-off-by: Barry Song barry.song@analog.com
Acked-by: Liam Girdwood lrg@slimlogic.co.uk
On Thu, Mar 18, 2010 at 09:00:29AM +0000, Liam Girdwood wrote:
On Thu, 2010-03-18 at 16:17 +0800, Barry Song wrote:
The registers for AD193X are defined as 0x800-0x810 for spi which uses 16_8 mode, for i2c to support AD1937, we will use 8_8 mode, only the low byte of 0x800-0x810 is valid. The patch will not destory other codecs, but make soc cache interface more useful.
Signed-off-by: Barry Song barry.song@analog.com
Acked-by: Liam Girdwood lrg@slimlogic.co.uk
Applied, thanks.
On Thu, 2010-03-18 at 16:17 +0800, Barry Song wrote:
From: Cliff Cai cliff.cai@analog.com
Signed-off-by: Cliff Cai cliff.cai@analog.com Signed-off-by: Barry Song barry.song@analog.com
Acked-by: Liam Girdwood lrg@slimlogic.co.uk
On Thu, Mar 18, 2010 at 08:51:55AM +0000, Liam Girdwood wrote:
On Thu, 2010-03-18 at 16:17 +0800, Barry Song wrote:
From: Cliff Cai cliff.cai@analog.com
Signed-off-by: Cliff Cai cliff.cai@analog.com Signed-off-by: Barry Song barry.song@analog.com
Acked-by: Liam Girdwood lrg@slimlogic.co.uk
Applied, thanks.
On Thu, Mar 18, 2010 at 04:16:59PM +0800, Barry Song wrote:
Signed-off-by: Barry Song barry.song@analog.com
Might be worth trying to use 'git format-patch -M' to see if you can produce a more useful view of this.
On Thu, 2010-03-18 at 16:16 +0800, Barry Song wrote:
This patch extends ad1938 codec driver to support ad1936/7/8/9, ad1936/7 use i2c bus, ad1938/9 use spi bus. The driver is splitted into three: ad193x.h and ad193x.c: common codec driver ad193x-spi.c: ad193x codec spi driver ad193x-i2c.c: ad193x codec i2c driver
Is the new common driver code the same as the original driver minus any I2C/SPI changes ? It's difficult to see if anything else has been changed.
Btw, have a look at wm8731.c. This driver supports both I2C and SPI and is less churn than splitting out the code into three files.
Thanks
Liam
On Thu, Mar 18, 2010 at 4:48 PM, Liam Girdwood lrg@slimlogic.co.uk wrote:
On Thu, 2010-03-18 at 16:16 +0800, Barry Song wrote:
This patch extends ad1938 codec driver to support ad1936/7/8/9, ad1936/7 use i2c bus, ad1938/9 use spi bus. The driver is splitted into three: ad193x.h and ad193x.c: common codec driver ad193x-spi.c: ad193x codec spi driver ad193x-i2c.c: ad193x codec i2c driver
Is the new common driver code the same as the original driver minus any I2C/SPI changes ? It's difficult to see if anything else has been changed.
Basically, the codes are same except renaming ad1938 to ad193x.
Btw, have a look at wm8731.c. This driver supports both I2C and SPI and is less churn than splitting out the code into three files.
We have many discussion about a driver to support both spi and i2c in another device(ad714x). At the beginning, the way in wm8731.c is used, then after many comments, people agree splitting the driver to three modules is the best way. If you are interested in it, you may take a look at "input/misc: add Analog Devices AD714x captouch input driver" in linux-input maillist.
Thanks
Liam
-- Freelance Developer, SlimLogic Ltd ASoC and Voltage Regulator Maintainer. http://www.slimlogic.co.uk
On Thu, Mar 18, 2010 at 05:08:01PM +0800, Barry Song wrote:
On Thu, Mar 18, 2010 at 4:48 PM, Liam Girdwood lrg@slimlogic.co.uk wrote:
Btw, have a look at wm8731.c. This driver supports both I2C and SPI and is less churn than splitting out the code into three files.
We have many discussion about a driver to support both spi and i2c in another device(ad714x). At the beginning, the way in wm8731.c is used, then after many comments, people agree splitting the driver to three modules is the best way. If you are interested in it, you may take a look at "input/misc: add Analog Devices AD714x captouch input driver" in linux-input maillist.
This was a discussion for the input subsystem and what makes sense here. For ASoC we've been using the approach that WM8731 and other similar drivers are using, please follow that approach with ASoC drivers. Adding files per driver per bus type isn't helpful here.
On Thu, Mar 18, 2010 at 07:18, Mark Brown wrote:
On Thu, Mar 18, 2010 at 05:08:01PM +0800, Barry Song wrote:
On Thu, Mar 18, 2010 at 4:48 PM, Liam Girdwood wrote:
Btw, have a look at wm8731.c. This driver supports both I2C and SPI and is less churn than splitting out the code into three files.
We have many discussion about a driver to support both spi and i2c in another device(ad714x). At the beginning, the way in wm8731.c is used, then after many comments, people agree splitting the driver to three modules is the best way. If you are interested in it, you may take a look at "input/misc: add Analog Devices AD714x captouch input driver" in linux-input maillist.
This was a discussion for the input subsystem and what makes sense here. For ASoC we've been using the approach that WM8731 and other similar drivers are using, please follow that approach with ASoC drivers. Adding files per driver per bus type isn't helpful here.
that isnt really true. the tact taken in the input subsystem wrt multiple busses had nothing to do with the input subsystem. it was designed to provide flexibility -- the core is not tied to the busses, and any of the pieces could be loaded/unloaded on the fly instead of forcing the core to be rebuilt just because of bus changes.
i'm not suggesting the ASoC codecs shouldnt use the same style across the board (and thus we shouldnt change the AD193x driver), just pointing out your basic premise here is invalid and that there are pros/cons to each method and that they're independent of the subsystem. you simply selected a different solution. -mike
On Thu, Mar 18, 2010 at 11:57:43AM -0400, Mike Frysinger wrote:
i'm not suggesting the ASoC codecs shouldnt use the same style across the board (and thus we shouldnt change the AD193x driver), just pointing out your basic premise here is invalid and that there are pros/cons to each method and that they're independent of the subsystem. you simply selected a different solution.
My point is that the fact that the input subsystem has made a choice is pretty much irrelevant for ASoC, which has different considerations and therefore made a different choice. Barry had simply said there had been a big discussion and this is what had been agreed, but that discussion was held for the input subsystem and when the equivalent discussion was held for ASoC a different conclusion was reached.
The subsystem dependency here come from the fact that ASoC has machine drivers and relies on them selecting the CODEC drivers to get them built in the first place so if you're trying to change something like this you'll most likely not only have to rebuild your kernel but also have to write code. This isn't something that the input layer has (input layer drivers are pretty much standalone, usually only need platform data for any per machine hookup and for I2C and SPI can even be registered from user space IIRC) and it changes the considerations noticably.
On Thu, Mar 18, 2010 at 12:20, Mark Brown wrote:
On Thu, Mar 18, 2010 at 11:57:43AM -0400, Mike Frysinger wrote:
i'm not suggesting the ASoC codecs shouldnt use the same style across the board (and thus we shouldnt change the AD193x driver), just pointing out your basic premise here is invalid and that there are pros/cons to each method and that they're independent of the subsystem. you simply selected a different solution.
My point is that the fact that the input subsystem has made a choice is pretty much irrelevant for ASoC, which has different considerations and therefore made a different choice. Barry had simply said there had been a big discussion and this is what had been agreed, but that discussion was held for the input subsystem and when the equivalent discussion was held for ASoC a different conclusion was reached.
i dont think that's entirely true ... one of the major points of getting drivers into mainline is so that when common paradigms are observed, they can be unified across everyone. subsystems rarely are special ... most of the time, people just think they're special and so can ignore the common behavior.
as devices get more complicated and hook up to more arbitrary busses, i'm sure something will arise in the future to address this.
The subsystem dependency here come from the fact that ASoC has machine drivers and relies on them selecting the CODEC drivers to get them built in the first place so if you're trying to change something like this you'll most likely not only have to rebuild your kernel but also have to write code. This isn't something that the input layer has (input layer drivers are pretty much standalone, usually only need platform data for any per machine hookup and for I2C and SPI can even be registered from user space IIRC) and it changes the considerations noticably.
the machine driver selects the codec, it doesnt select the bus. the codec worries about that. so i dont quite follow the logic here. -mike
On Thu, Mar 18, 2010 at 01:17:11PM -0400, Mike Frysinger wrote:
observed, they can be unified across everyone. subsystems rarely are special ... most of the time, people just think they're special and so can ignore the common behavior.
I really don't see much of that, to be honest. Bitrot, lack of time and poor advertising of new core features seem to be more the issue.
The subsystem dependency here come from the fact that ASoC has machine drivers and relies on them selecting the CODEC drivers to get them built in the first place so if you're trying to change something like this you'll most likely not only have to rebuild your kernel but also have to write code. This isn't something that the input layer has (input layer drivers are pretty much standalone, usually only need platform data for any per machine hookup and for I2C and SPI can even be registered from user space IIRC) and it changes the considerations noticably.
the machine driver selects the codec, it doesnt select the bus. the codec worries about that. so i dont quite follow the logic here.
That's not the case, CODEC drivers do absolutely nothing to ensure that they have the buses they need. Multi bus CODECs should all be perfectly happy to build with no bus at all, though sparse and/or GCC will warn.
Kconfig will cheerfully ignore any dependencies of things that are selected - unless it's been updated recently all a select does is force the selected symbol on, it doesn't recurse through dependencies and selects of that symbol. Besides, multi bus CODEC drivers can't know which of their possible buses are actually needed on a given system - they just build support for any bus types that are configured and let something else worry about the resulting configuration actually being useful.
On Thu, Mar 18, 2010 at 14:05, Mark Brown wrote:
On Thu, Mar 18, 2010 at 01:17:11PM -0400, Mike Frysinger wrote:
The subsystem dependency here come from the fact that ASoC has machine drivers and relies on them selecting the CODEC drivers to get them built in the first place so if you're trying to change something like this you'll most likely not only have to rebuild your kernel but also have to write code. This isn't something that the input layer has (input layer drivers are pretty much standalone, usually only need platform data for any per machine hookup and for I2C and SPI can even be registered from user space IIRC) and it changes the considerations noticably.
the machine driver selects the codec, it doesnt select the bus. the codec worries about that. so i dont quite follow the logic here.
That's not the case, CODEC drivers do absolutely nothing to ensure that they have the buses they need. Multi bus CODECs should all be perfectly happy to build with no bus at all, though sparse and/or GCC will warn.
Kconfig will cheerfully ignore any dependencies of things that are selected - unless it's been updated recently all a select does is force the selected symbol on, it doesn't recurse through dependencies and selects of that symbol. Besides, multi bus CODEC drivers can't know which of their possible buses are actually needed on a given system - they just build support for any bus types that are configured and let something else worry about the resulting configuration actually being useful.
sure, on the Kconfig side, i can see that. i was thinking the pure C drivers though. thanks for the clarification. -mike
I think this issue has nothing to do with subsystems, anyway, both are acceptable to me. Since there has been an example in ASoC, I will follow it and send v2. thanks -barry
On Fri, Mar 19, 2010 at 2:08 AM, Mike Frysinger vapier.adi@gmail.com wrote:
On Thu, Mar 18, 2010 at 14:05, Mark Brown wrote:
On Thu, Mar 18, 2010 at 01:17:11PM -0400, Mike Frysinger wrote:
The subsystem dependency here come from the fact that ASoC has machine drivers and relies on them selecting the CODEC drivers to get them built in the first place so if you're trying to change something like this you'll most likely not only have to rebuild your kernel but also have to write code. This isn't something that the input layer has (input layer drivers are pretty much standalone, usually only need platform data for any per machine hookup and for I2C and SPI can even be registered from user space IIRC) and it changes the considerations noticably.
the machine driver selects the codec, it doesnt select the bus. the codec worries about that. so i dont quite follow the logic here.
That's not the case, CODEC drivers do absolutely nothing to ensure that they have the buses they need. Multi bus CODECs should all be perfectly happy to build with no bus at all, though sparse and/or GCC will warn.
Kconfig will cheerfully ignore any dependencies of things that are selected - unless it's been updated recently all a select does is force the selected symbol on, it doesn't recurse through dependencies and selects of that symbol. Besides, multi bus CODEC drivers can't know which of their possible buses are actually needed on a given system - they just build support for any bus types that are configured and let something else worry about the resulting configuration actually being useful.
sure, on the Kconfig side, i can see that. i was thinking the pure C drivers though. thanks for the clarification. -mike
It seems "git-format-patch -M" fails to detect this patch as a rename(maybe due to too many changed lines?), so I commit two times to get a readable patch and attach them. 1. rename ad1938 to ad193x 2. extend ad1938 codec driver to ad193x supporting ad1936/7/8/9 -barry
On Fri, Mar 19, 2010 at 11:30 AM, Barry Song 21cnbao@gmail.com wrote:
I think this issue has nothing to do with subsystems, anyway, both are acceptable to me. Since there has been an example in ASoC, I will follow it and send v2. thanks -barry
On Fri, Mar 19, 2010 at 2:08 AM, Mike Frysinger vapier.adi@gmail.com wrote:
On Thu, Mar 18, 2010 at 14:05, Mark Brown wrote:
On Thu, Mar 18, 2010 at 01:17:11PM -0400, Mike Frysinger wrote:
The subsystem dependency here come from the fact that ASoC has machine drivers and relies on them selecting the CODEC drivers to get them built in the first place so if you're trying to change something like this you'll most likely not only have to rebuild your kernel but also have to write code. This isn't something that the input layer has (input layer drivers are pretty much standalone, usually only need platform data for any per machine hookup and for I2C and SPI can even be registered from user space IIRC) and it changes the considerations noticably.
the machine driver selects the codec, it doesnt select the bus. the codec worries about that. so i dont quite follow the logic here.
That's not the case, CODEC drivers do absolutely nothing to ensure that they have the buses they need. Multi bus CODECs should all be perfectly happy to build with no bus at all, though sparse and/or GCC will warn.
Kconfig will cheerfully ignore any dependencies of things that are selected - unless it's been updated recently all a select does is force the selected symbol on, it doesn't recurse through dependencies and selects of that symbol. Besides, multi bus CODEC drivers can't know which of their possible buses are actually needed on a given system - they just build support for any bus types that are configured and let something else worry about the resulting configuration actually being useful.
sure, on the Kconfig side, i can see that. i was thinking the pure C drivers though. thanks for the clarification. -mike
On Fri, 2010-03-19 at 15:07 +0800, Barry Song wrote:
It seems "git-format-patch -M" fails to detect this patch as a rename(maybe due to too many changed lines?), so I commit two times to get a readable patch and attach them.
- rename ad1938 to ad193x
- extend ad1938 codec driver to ad193x supporting ad1936/7/8/9
-barry
Attached patches look fine.
Acked-by: Liam Girdwood lrg@slimlogic.co.uk
On Fri, Mar 19, 2010 at 03:07:33PM +0800, Barry Song wrote:
It seems "git-format-patch -M" fails to detect this patch as a rename(maybe due to too many changed lines?), so I commit two times to get a readable patch and attach them.
- rename ad1938 to ad193x
- extend ad1938 codec driver to ad193x supporting ad1936/7/8/9
Please don't top post and please always follow the patch submission procedure documented in Documentation/SubmittingPatches. Even if problems with your MUA make it difficult to submit patches without using attachments you should always send one patch per message. Not following these rules makes your patches much more difficult to handle.
Since the patches aren't in line it's difficult to quote things when commenting but a few issues:
- Your rename patch didn't update the Makefile and Kconfig, which would cause build breakage if applied alone. You should always try to ensure that builds work even with a partially applied patch series since this allows things like bisection which step through the history to work.
- You've left the bus_probe() functions exported - now you've merged everything into one file this is no longer needed.
- The way you've factored out the bus probe and removal functions so that there's no code in the individual I2C and SPI functions means that the register() and unregister() functions could just be squashed into the bus_probe() and bus_remove() functions - all that the register and unregister functions are is the code that's shared between the bus
I've fixed the first issue by squashing the two patches together and the last one with a patch. The last issue isn't important, it's more for information.
On Fri, Mar 19, 2010 at 8:24 PM, Mark Brown broonie@opensource.wolfsonmicro.com wrote:
On Fri, Mar 19, 2010 at 03:07:33PM +0800, Barry Song wrote:
It seems "git-format-patch -M" fails to detect this patch as a rename(maybe due to too many changed lines?), so I commit two times to get a readable patch and attach them.
- rename ad1938 to ad193x
- extend ad1938 codec driver to ad193x supporting ad1936/7/8/9
Please don't top post and please always follow the patch submission procedure documented in Documentation/SubmittingPatches. Even if problems with your MUA make it difficult to submit patches without using attachments you should always send one patch per message. Not following these rules makes your patches much more difficult to handle.
Since the patches aren't in line it's difficult to quote things when commenting but a few issues:
- Your rename patch didn't update the Makefile and Kconfig, which would cause build breakage if applied alone. You should always try to ensure that builds work even with a partially applied patch series since this allows things like bisection which step through the history to work.
Yes. if i split into two patches, i should change Makefile and Kconfig for the 1st one.
- You've left the bus_probe() functions exported - now you've merged everything into one file this is no longer needed.
Yes. i noticed this too after i sent the patch.
- The way you've factored out the bus probe and removal functions so that there's no code in the individual I2C and SPI functions means that the register() and unregister() functions could just be squashed into the bus_probe() and bus_remove() functions - all that the register and unregister functions are is the code that's shared between the bus
if so, it likes register/unregister should be moved into probe/remove even for codecs with single codec?
I've fixed the first issue by squashing the two patches together and the last one with a patch. The last issue isn't important, it's more for information.
Thanks for your help to fix issues.
On Mon, Mar 22, 2010 at 01:50:08PM +0800, Barry Song wrote:
On Fri, Mar 19, 2010 at 8:24 PM, Mark Brown
- The way you've factored out the bus probe and removal functions so that there's no code in the individual I2C and SPI functions means that the register() and unregister() functions could just be squashed into the bus_probe() and bus_remove() functions - all that the register and unregister functions are is the code that's shared between the bus
if so, it likes register/unregister should be moved into probe/remove even for codecs with single codec?
I'm assuming you mean single bus? If the code is structured like yours with nothing in the per-bus probe/remove then definitely. Note that sometimes drivers do keep a split for multiple buses since the hardware supports multiple buses but they've not all been implemented yet.
participants (4)
-
Barry Song
-
Liam Girdwood
-
Mark Brown
-
Mike Frysinger