diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2011-06-21 12:12:29 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2011-06-21 12:12:29 +1000 |
commit | 58facd522b7689b41896fd0d36f46a2a34ffc52b (patch) | |
tree | 83550a5cb1513f1fcc626bdc0ad971b147288a4d | |
parent | eafc09304b3d4b69d11238fddd189d945b690c9a (diff) | |
parent | c20974090e9093b8b69b37543cba381336c41ab7 (diff) |
Merge remote-tracking branch 'sound-asoc/for-next'
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | include/sound/soc-dapm.h | 3 | ||||
-rw-r--r-- | include/sound/soc.h | 3 | ||||
-rw-r--r-- | sound/soc/Makefile | 3 | ||||
-rw-r--r-- | sound/soc/blackfin/Kconfig | 14 | ||||
-rw-r--r-- | sound/soc/blackfin/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/blackfin/bf5xx-i2s-pcm.c | 6 | ||||
-rw-r--r-- | sound/soc/blackfin/bfin-eval-adau1701.c | 139 | ||||
-rw-r--r-- | sound/soc/codecs/Kconfig | 6 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/ad1836.c | 78 | ||||
-rw-r--r-- | sound/soc/codecs/ad1836.h | 21 | ||||
-rw-r--r-- | sound/soc/codecs/adau1701.c | 549 | ||||
-rw-r--r-- | sound/soc/codecs/adau1701.h | 17 | ||||
-rw-r--r-- | sound/soc/soc-cache.c | 416 | ||||
-rw-r--r-- | sound/soc/soc-dapm.c | 142 | ||||
-rw-r--r-- | sound/soc/soc-io.c | 396 |
17 files changed, 1304 insertions, 494 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 66bc668c3bab..2f912db7776a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -533,6 +533,7 @@ L: device-drivers-devel@blackfin.uclinux.org L: alsa-devel@alsa-project.org (moderated for non-subscribers) W: http://wiki.analog.com/ S: Supported +F: sound/soc/codecs/adau* F: sound/soc/codecs/ad1* F: sound/soc/codecs/ssm* diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 7415878c8b37..602024d4c459 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -348,6 +348,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm); void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); +int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route, int num); /* dapm events */ int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, @@ -429,6 +431,7 @@ struct snd_soc_dapm_path { /* status */ u32 connect:1; /* source and sink widgets are connected */ u32 walked:1; /* path has been walked */ + u32 weak:1; /* path ignored for power management */ int (*connected)(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink); diff --git a/include/sound/soc.h b/include/sound/soc.h index 4334ab25c5d5..6424b10ac29c 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -258,8 +258,7 @@ typedef int (*hw_write_t)(void *,const char* ,int); extern struct snd_ac97_bus_ops soc_ac97_ops; enum snd_soc_control_type { - SND_SOC_CUSTOM = 1, - SND_SOC_I2C, + SND_SOC_I2C = 1, SND_SOC_SPI, }; diff --git a/sound/soc/Makefile b/sound/soc/Makefile index adb5719cb7d2..4f913876f332 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,4 +1,5 @@ -snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o soc-pcm.o +snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o +snd-soc-core-objs += soc-pcm.o soc-io.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += codecs/ diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig index ae403597fd31..fa6a6d2abffc 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig @@ -10,13 +10,23 @@ config SND_BF5XX_I2S config SND_BF5XX_SOC_SSM2602 tristate "SoC SSM2602 Audio support for BF52x ezkit" - depends on SND_BF5XX_I2S + depends on SND_BF5XX_I2S && (SPI_MASTER || I2C) select SND_BF5XX_SOC_I2S select SND_SOC_SSM2602 - select I2C help Say Y if you want to add support for SoC audio on BF527-EZKIT. +config SND_SOC_BFIN_EVAL_ADAU1701 + tristate "Support for the EVAL-ADAU1701MINIZ board on Blackfin eval boards" + depends on SND_BF5XX_I2S + select SND_BF5XX_SOC_I2S + select SND_SOC_ADAU1701 + select I2C + help + Say Y if you want to add support for the Analog Devices EVAL-ADAU1701MINIZ + board connected to one of the Blackfin evaluation boards like the + BF5XX-STAMP or BF5XX-EZKIT. + config SND_BF5XX_SOC_AD73311 tristate "SoC AD73311 Audio support for Blackfin" depends on SND_BF5XX_I2S diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile index 49af3f32aec8..f01bff63177d 100644 --- a/sound/soc/blackfin/Makefile +++ b/sound/soc/blackfin/Makefile @@ -21,9 +21,11 @@ snd-ad1980-objs := bf5xx-ad1980.o snd-ssm2602-objs := bf5xx-ssm2602.o snd-ad73311-objs := bf5xx-ad73311.o snd-ad193x-objs := bf5xx-ad193x.o +snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.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_AD193X) += snd-ad193x.o +obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index 96d0d9060768..4a805a859723 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c @@ -159,7 +159,7 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream) snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); - ret = snd_pcm_hw_constraint_integer(runtime, \ + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) goto out; @@ -297,8 +297,8 @@ static int __devexit bfin_i2s_soc_platform_remove(struct platform_device *pdev) static struct platform_driver bfin_i2s_pcm_driver = { .driver = { - .name = "bfin-i2s-pcm-audio", - .owner = THIS_MODULE, + .name = "bfin-i2s-pcm-audio", + .owner = THIS_MODULE, }, .probe = bfin_i2s_soc_platform_probe, diff --git a/sound/soc/blackfin/bfin-eval-adau1701.c b/sound/soc/blackfin/bfin-eval-adau1701.c new file mode 100644 index 000000000000..e5550acba2c2 --- /dev/null +++ b/sound/soc/blackfin/bfin-eval-adau1701.c @@ -0,0 +1,139 @@ +/* + * Machine driver for EVAL-ADAU1701MINIZ on Analog Devices bfin + * evaluation boards. + * + * Copyright 2011 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/pcm_params.h> + +#include "../codecs/adau1701.h" + +static const struct snd_soc_dapm_widget bfin_eval_adau1701_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), +}; + +static const struct snd_soc_dapm_route bfin_eval_adau1701_dapm_routes[] = { + { "Speaker", NULL, "OUT0" }, + { "Speaker", NULL, "OUT1" }, + { "Line Out", NULL, "OUT2" }, + { "Line Out", NULL, "OUT3" }, + + { "IN0", NULL, "Line In" }, + { "IN1", NULL, "Line In" }, +}; + +static int bfin_eval_adau1701_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->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + if (ret) + return ret; + + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + if (ret) + return ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1701_CLK_SRC_OSC, 12288000, + SND_SOC_CLOCK_IN); + + return ret; +} + +static struct snd_soc_ops bfin_eval_adau1701_ops = { + .hw_params = bfin_eval_adau1701_hw_params, +}; + +static struct snd_soc_dai_link bfin_eval_adau1701_dai[] = { + { + .name = "adau1701", + .stream_name = "adau1701", + .cpu_dai_name = "bfin-i2s.0", + .codec_dai_name = "adau1701", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "adau1701.0-0034", + .ops = &bfin_eval_adau1701_ops, + }, + { + .name = "adau1701", + .stream_name = "adau1701", + .cpu_dai_name = "bfin-i2s.1", + .codec_dai_name = "adau1701", + .platform_name = "bfin-i2s-pcm-audio", + .codec_name = "adau1701.0-0034", + .ops = &bfin_eval_adau1701_ops, + }, +}; + +static struct snd_soc_card bfin_eval_adau1701 = { + .name = "bfin-eval-adau1701", + .dai_link = &bfin_eval_adau1701_dai[CONFIG_SND_BF5XX_SPORT_NUM], + .num_links = 1, + + .dapm_widgets = bfin_eval_adau1701_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(bfin_eval_adau1701_dapm_widgets), + .dapm_routes = bfin_eval_adau1701_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(bfin_eval_adau1701_dapm_routes), +}; + +static int bfin_eval_adau1701_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &bfin_eval_adau1701; + + card->dev = &pdev->dev; + + return snd_soc_register_card(&bfin_eval_adau1701); +} + +static int __devexit bfin_eval_adau1701_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static struct platform_driver bfin_eval_adau1701_driver = { + .driver = { + .name = "bfin-eval-adau1701", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + }, + .probe = bfin_eval_adau1701_probe, + .remove = __devexit_p(bfin_eval_adau1701_remove), +}; + +static int __init bfin_eval_adau1701_init(void) +{ + return platform_driver_register(&bfin_eval_adau1701_driver); +} +module_init(bfin_eval_adau1701_init); + +static void __exit bfin_eval_adau1701_exit(void) +{ + platform_driver_unregister(&bfin_eval_adau1701_driver); +} +module_exit(bfin_eval_adau1701_exit); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ALSA SoC bfin ADAU1701 driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bfin-eval-adau1701"); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 98175a096df2..7a2e4269b255 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -130,7 +130,11 @@ config SND_SOC_AD1980 config SND_SOC_AD73311 tristate - + +config SND_SOC_ADAU1701 + select SIGMA + tristate + config SND_SOC_ADS117X tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index fd8558406ef0..30a4c631aef4 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -4,6 +4,7 @@ snd-soc-ad1836-objs := ad1836.o snd-soc-ad193x-objs := ad193x.o snd-soc-ad1980-objs := ad1980.o snd-soc-ad73311-objs := ad73311.o +snd-soc-adau1701-objs := adau1701.o snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o snd-soc-ak4535-objs := ak4535.o @@ -95,6 +96,7 @@ obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o +obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c index e3a9493e3ced..4e5c5726366b 100644 --- a/sound/soc/codecs/ad1836.c +++ b/sound/soc/codecs/ad1836.c @@ -1,19 +1,10 @@ -/* - * File: sound/soc/codecs/ad1836.c - * Author: Barry Song <Barry.Song@analog.com> - * - * Created: Aug 04 2009 - * Description: Driver for AD1836 sound chip - * - * Modified: - * Copyright 2009 Analog Devices Inc. + /* + * Audio Codec driver supporting: + * AD1835A, AD1836, AD1837A, AD1838A, AD1839A * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * Copyright 2009-2011 Analog Devices Inc. * - * 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. + * Licensed under the GPL-2 or later. */ #include <linux/init.h> @@ -198,26 +189,6 @@ static int ad1836_hw_params(struct snd_pcm_substream *substream, return 0; } -#ifdef CONFIG_PM -static int ad1836_soc_suspend(struct snd_soc_codec *codec, - pm_message_t state) -{ - /* reset clock control mode */ - return snd_soc_update_bits(codec, AD1836_ADC_CTRL2, - AD1836_ADC_SERFMT_MASK, 0); -} - -static int ad1836_soc_resume(struct snd_soc_codec *codec) -{ - /* restore clock control mode */ - return snd_soc_update_bits(codec, AD1836_ADC_CTRL2, - AD1836_ADC_SERFMT_MASK, AD1836_ADC_AUX); -} -#else -#define ad1836_soc_suspend NULL -#define ad1836_soc_resume NULL -#endif - static struct snd_soc_dai_ops ad1836_dai_ops = { .hw_params = ad1836_hw_params, .set_fmt = ad1836_set_dai_fmt, @@ -251,6 +222,25 @@ static struct snd_soc_dai_driver ad183x_dais[] = { [AD1838] = AD183X_DAI("ad1838", 3, 1), }; +#ifdef CONFIG_PM +static int ad1836_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + /* reset clock control mode */ + return snd_soc_update_bits(codec, AD1836_ADC_CTRL2, + AD1836_ADC_SERFMT_MASK, 0); +} + +static int ad1836_resume(struct snd_soc_codec *codec) +{ + /* restore clock control mode */ + return snd_soc_update_bits(codec, AD1836_ADC_CTRL2, + AD1836_ADC_SERFMT_MASK, AD1836_ADC_AUX); +} +#else +#define ad1836_suspend NULL +#define ad1836_resume NULL +#endif + static int ad1836_probe(struct snd_soc_codec *codec) { struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec); @@ -331,10 +321,10 @@ static int ad1836_remove(struct snd_soc_codec *codec) } static struct snd_soc_codec_driver soc_codec_dev_ad1836 = { - .probe = ad1836_probe, - .remove = ad1836_remove, - .suspend = ad1836_soc_suspend, - .resume = ad1836_soc_resume, + .probe = ad1836_probe, + .remove = ad1836_remove, + .suspend = ad1836_suspend, + .resume = ad1836_resume, .reg_cache_size = AD1836_NUM_REGS, .reg_word_size = sizeof(u16), @@ -384,7 +374,7 @@ MODULE_DEVICE_TABLE(spi, ad1836_ids); static struct spi_driver ad1836_spi_driver = { .driver = { - .name = "ad1836-codec", + .name = "ad1836", .owner = THIS_MODULE, }, .probe = ad1836_spi_probe, @@ -394,15 +384,7 @@ static struct spi_driver ad1836_spi_driver = { static int __init ad1836_init(void) { - int ret; - - ret = spi_register_driver(&ad1836_spi_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register ad1836 SPI driver: %d\n", - ret); - } - - return ret; + return spi_register_driver(&ad1836_spi_driver); } module_init(ad1836_init); diff --git a/sound/soc/codecs/ad1836.h b/sound/soc/codecs/ad1836.h index f13402fe7333..444747f0db26 100644 --- a/sound/soc/codecs/ad1836.h +++ b/sound/soc/codecs/ad1836.h @@ -1,19 +1,10 @@ /* - * File: sound/soc/codecs/ad1836.h - * Based on: - * Author: Barry Song <Barry.Song@analog.com> + * Audio Codec driver supporting: + * AD1835A, AD1836, AD1837A, AD1838A, AD1839A * - * Created: Aug 04, 2009 - * Description: definitions for AD1836 registers + * Copyright 2009-2011 Analog Devices Inc. * - * 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. + * Licensed under the GPL-2 or later. */ #ifndef __AD1836_H__ @@ -21,7 +12,7 @@ #define AD1836_DAC_CTRL1 0 #define AD1836_DAC_POWERDOWN 2 -#define AD1836_DAC_SERFMT_MASK 0xE0 +#define AD1836_DAC_SERFMT_MASK 0xE0 #define AD1836_DAC_SERFMT_PCK256 (0x4 << 5) #define AD1836_DAC_SERFMT_PCK128 (0x5 << 5) #define AD1836_DAC_WORD_LEN_MASK 0x18 @@ -44,7 +35,7 @@ #define AD1836_ADC_CTRL2 13 #define AD1836_ADC_WORD_LEN_MASK 0x30 #define AD1836_ADC_WORD_OFFSET 5 -#define AD1836_ADC_SERFMT_MASK (7 << 6) +#define AD1836_ADC_SERFMT_MASK (7 << 6) #define AD1836_ADC_SERFMT_PCK256 (0x4 << 6) #define AD1836_ADC_SERFMT_PCK128 (0x5 << 6) #define AD1836_ADC_AUX (0x6 << 6) diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c new file mode 100644 index 000000000000..2758d5fc60d6 --- /dev/null +++ b/sound/soc/codecs/adau1701.c @@ -0,0 +1,549 @@ +/* + * Driver for ADAU1701 SigmaDSP processor + * + * Copyright 2011 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * based on an inital version by Cliff Cai <cliff.cai@analog.com> + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/sigma.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "adau1701.h" + +#define ADAU1701_DSPCTRL 0x1c +#define ADAU1701_SEROCTL 0x1e +#define ADAU1701_SERICTL 0x1f + +#define ADAU1701_AUXNPOW 0x22 + +#define ADAU1701_OSCIPOW 0x26 +#define ADAU1701_DACSET 0x27 + +#define ADAU1701_NUM_REGS 0x28 + +#define ADAU1701_DSPCTRL_CR (1 << 2) +#define ADAU1701_DSPCTRL_DAM (1 << 3) +#define ADAU1701_DSPCTRL_ADM (1 << 4) +#define ADAU1701_DSPCTRL_SR_48 0x00 +#define ADAU1701_DSPCTRL_SR_96 0x01 +#define ADAU1701_DSPCTRL_SR_192 0x02 +#define ADAU1701_DSPCTRL_SR_MASK 0x03 + +#define ADAU1701_SEROCTL_INV_LRCLK 0x2000 +#define ADAU1701_SEROCTL_INV_BCLK 0x1000 +#define ADAU1701_SEROCTL_MASTER 0x0800 + +#define ADAU1701_SEROCTL_OBF16 0x0000 +#define ADAU1701_SEROCTL_OBF8 0x0200 +#define ADAU1701_SEROCTL_OBF4 0x0400 +#define ADAU1701_SEROCTL_OBF2 0x0600 +#define ADAU1701_SEROCTL_OBF_MASK 0x0600 + +#define ADAU1701_SEROCTL_OLF1024 0x0000 +#define ADAU1701_SEROCTL_OLF512 0x0080 +#define ADAU1701_SEROCTL_OLF256 0x0100 +#define ADAU1701_SEROCTL_OLF_MASK 0x0180 + +#define ADAU1701_SEROCTL_MSB_DEALY1 0x0000 +#define ADAU1701_SEROCTL_MSB_DEALY0 0x0004 +#define ADAU1701_SEROCTL_MSB_DEALY8 0x0008 +#define ADAU1701_SEROCTL_MSB_DEALY12 0x000c +#define ADAU1701_SEROCTL_MSB_DEALY16 0x0010 +#define ADAU1701_SEROCTL_MSB_DEALY_MASK 0x001c + +#define ADAU1701_SEROCTL_WORD_LEN_24 0x0000 +#define ADAU1701_SEROCTL_WORD_LEN_20 0x0001 +#define ADAU1701_SEROCTL_WORD_LEN_16 0x0010 +#define ADAU1701_SEROCTL_WORD_LEN_MASK 0x0003 + +#define ADAU1701_AUXNPOW_VBPD 0x40 +#define ADAU1701_AUXNPOW_VRPD 0x20 + +#define ADAU1701_SERICTL_I2S 0 +#define ADAU1701_SERICTL_LEFTJ 1 +#define ADAU1701_SERICTL_TDM 2 +#define ADAU1701_SERICTL_RIGHTJ_24 3 +#define ADAU1701_SERICTL_RIGHTJ_20 4 +#define ADAU1701_SERICTL_RIGHTJ_18 5 +#define ADAU1701_SERICTL_RIGHTJ_16 6 +#define ADAU1701_SERICTL_MODE_MASK 7 +#define ADAU1701_SERICTL_INV_BCLK BIT(3) +#define ADAU1701_SERICTL_INV_LRCLK BIT(4) + +#define ADAU1701_OSCIPOW_OPD 0x04 +#define ADAU1701_DACSET_DACINIT 1 + +#define ADAU1701_FIRMWARE "adau1701.bin" + +struct adau1701 { + unsigned int dai_fmt; +}; + +static const struct snd_kcontrol_new adau1701_controls[] = { + SOC_SINGLE("Master Capture Switch", ADAU1701_DSPCTRL, 4, 1, 0), +}; + +static const struct snd_soc_dapm_widget adau1701_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC0", "Playback", ADAU1701_AUXNPOW, 3, 1), + SND_SOC_DAPM_DAC("DAC1", "Playback", ADAU1701_AUXNPOW, 2, 1), + SND_SOC_DAPM_DAC("DAC2", "Playback", ADAU1701_AUXNPOW, 1, 1), + SND_SOC_DAPM_DAC("DAC3", "Playback", ADAU1701_AUXNPOW, 0, 1), + SND_SOC_DAPM_ADC("ADC", "Capture", ADAU1701_AUXNPOW, 7, 1), + + SND_SOC_DAPM_OUTPUT("OUT0"), + SND_SOC_DAPM_OUTPUT("OUT1"), + SND_SOC_DAPM_OUTPUT("OUT2"), + SND_SOC_DAPM_OUTPUT("OUT3"), + SND_SOC_DAPM_INPUT("IN0"), + SND_SOC_DAPM_INPUT("IN1"), +}; + +static const struct snd_soc_dapm_route adau1701_dapm_routes[] = { + { "OUT0", NULL, "DAC0" }, + { "OUT1", NULL, "DAC1" }, + { "OUT2", NULL, "DAC2" }, + { "OUT3", NULL, "DAC3" }, + + { "ADC", NULL, "IN0" }, + { "ADC", NULL, "IN1" }, +}; + +static unsigned int adau1701_register_size(struct snd_soc_codec *codec, + unsigned int reg) +{ + switch (reg) { + case ADAU1701_DSPCTRL: + case ADAU1701_SEROCTL: + case ADAU1701_AUXNPOW: + case ADAU1701_OSCIPOW: + case ADAU1701_DACSET: + return 2; + case ADAU1701_SERICTL: + return 1; + } + + dev_err(codec->dev, "Unsupported register address: %d\n", reg); + return 0; +} + +static int adau1701_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + unsigned int i; + unsigned int size; + uint8_t buf[4]; + int ret; + + size = adau1701_register_size(codec, reg); + if (size == 0) + return -EINVAL; + + snd_soc_cache_write(codec, reg, value); + + buf[0] = 0x08; + buf[1] = reg; + + for (i = size + 1; i >= 2; --i) { + buf[i] = value; + value >>= 8; + } + + ret = i2c_master_send(to_i2c_client(codec->dev), buf, size + 2); + if (ret == size + 2) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static unsigned int adau1701_read(struct snd_soc_codec *codec, unsigned int reg) +{ + unsigned int value; + unsigned int ret; + + ret = snd_soc_cache_read(codec, reg, &value); + if (ret) + return ret; + + return value; +} + +static int adau1701_load_firmware(struct snd_soc_codec *codec) +{ + return process_sigma_firmware(codec->control_data, ADAU1701_FIRMWARE); +} + +static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec, + snd_pcm_format_t format) +{ + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK; + unsigned int val; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + val = ADAU1701_SEROCTL_WORD_LEN_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val = ADAU1701_SEROCTL_WORD_LEN_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = ADAU1701_SEROCTL_WORD_LEN_24; + break; + default: + return -EINVAL; + } + + if (adau1701->dai_fmt == SND_SOC_DAIFMT_RIGHT_J) { + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + val |= ADAU1701_SEROCTL_MSB_DEALY16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val |= ADAU1701_SEROCTL_MSB_DEALY12; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val |= ADAU1701_SEROCTL_MSB_DEALY8; + break; + } + mask |= ADAU1701_SEROCTL_MSB_DEALY_MASK; + } + + snd_soc_update_bits(codec, ADAU1701_SEROCTL, mask, val); + + return 0; +} + +static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec, + snd_pcm_format_t format) +{ + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + unsigned int val; + + if (adau1701->dai_fmt != SND_SOC_DAIFMT_RIGHT_J) + return 0; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + val = ADAU1701_SERICTL_RIGHTJ_16; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val = ADAU1701_SERICTL_RIGHTJ_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val = ADAU1701_SERICTL_RIGHTJ_24; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, ADAU1701_SERICTL, + ADAU1701_SERICTL_MODE_MASK, val); + + return 0; +} + +static int adau1701_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_codec *codec = rtd->codec; + snd_pcm_format_t format; + unsigned int val; + + switch (params_rate(params)) { + case 192000: + val = ADAU1701_DSPCTRL_SR_192; + break; + case 96000: + val = ADAU1701_DSPCTRL_SR_96; + break; + case 48000: + val = ADAU1701_DSPCTRL_SR_48; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, ADAU1701_DSPCTRL, + ADAU1701_DSPCTRL_SR_MASK, val); + + format = params_format(params); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return adau1701_set_playback_pcm_format(codec, format); + else + return adau1701_set_capture_pcm_format(codec, format); +} + +static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec); + unsigned int serictl = 0x00, seroctl = 0x00; + bool invert_lrclk; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + /* master, 64-bits per sample, 1 frame per sample */ + seroctl |= ADAU1701_SEROCTL_MASTER | ADAU1701_SEROCTL_OBF16 + | ADAU1701_SEROCTL_OLF1024; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + invert_lrclk = false; + break; + case SND_SOC_DAIFMT_NB_IF: + invert_lrclk = true; + break; + case SND_SOC_DAIFMT_IB_NF: + invert_lrclk = false; + serictl |= ADAU1701_SERICTL_INV_BCLK; + seroctl |= ADAU1701_SEROCTL_INV_BCLK; + break; + case SND_SOC_DAIFMT_IB_IF: + invert_lrclk = true; + serictl |= ADAU1701_SERICTL_INV_BCLK; + seroctl |= ADAU1701_SEROCTL_INV_BCLK; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_LEFT_J: + serictl |= ADAU1701_SERICTL_LEFTJ; + seroctl |= ADAU1701_SEROCTL_MSB_DEALY0; + invert_lrclk = !invert_lrclk; + break; + case SND_SOC_DAIFMT_RIGHT_J: + serictl |= ADAU1701_SERICTL_RIGHTJ_24; + seroctl |= ADAU1701_SEROCTL_MSB_DEALY8; + invert_lrclk = !invert_lrclk; + break; + default: + return -EINVAL; + } + + if (invert_lrclk) { + seroctl |= ADAU1701_SEROCTL_INV_LRCLK; + serictl |= ADAU1701_SERICTL_INV_LRCLK; + } + + adau1701->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + snd_soc_write(codec, ADAU1701_SERICTL, serictl); + snd_soc_update_bits(codec, ADAU1701_SEROCTL, + ~ADAU1701_SEROCTL_WORD_LEN_MASK, seroctl); + + return 0; +} + +static int adau1701_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + unsigned int mask = ADAU1701_AUXNPOW_VBPD | ADAU1701_AUXNPOW_VRPD; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* Enable VREF and VREF buffer */ + snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, 0x00); + break; + case SND_SOC_BIAS_OFF: + /* Disable VREF and VREF buffer */ + snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, mask); + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + unsigned int mask = ADAU1701_DSPCTRL_DAM; + unsigned int val; + + if (mute) + val = 0; + else + val = mask; + + snd_soc_update_bits(codec, ADAU1701_DSPCTRL, mask, val); + + return 0; +} + +static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id, + unsigned int freq, int dir) +{ + unsigned int val; + + switch (clk_id) { + case ADAU1701_CLK_SRC_OSC: + val = 0x0; + break; + case ADAU1701_CLK_SRC_MCLK: + val = ADAU1701_OSCIPOW_OPD; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, ADAU1701_OSCIPOW, ADAU1701_OSCIPOW_OPD, val); + + return 0; +} + +#define ADAU1701_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000) + +#define ADAU1701_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops adau1701_dai_ops = { + .set_fmt = adau1701_set_dai_fmt, + .hw_params = adau1701_hw_params, + .digital_mute = adau1701_digital_mute, +}; + +static struct snd_soc_dai_driver adau1701_dai = { + .name = "adau1701", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = ADAU1701_RATES, + .formats = ADAU1701_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 8, + .rates = ADAU1701_RATES, + .formats = ADAU1701_FORMATS, + }, + .ops = &adau1701_dai_ops, + .symmetric_rates = 1, +}; + +static int adau1701_probe(struct snd_soc_codec *codec) +{ + int ret; + + codec->dapm.idle_bias_off = 1; + + ret = adau1701_load_firmware(codec); + if (ret) + dev_warn(codec->dev, "Failed to load firmware\n"); + + snd_soc_write(codec, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT); + snd_soc_write(codec, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR); + + return 0; +} + +static struct snd_soc_codec_driver adau1701_codec_drv = { + .probe = adau1701_probe, + .set_bias_level = adau1701_set_bias_level, + + .reg_cache_size = ADAU1701_NUM_REGS, + .reg_word_size = sizeof(u16), + + .controls = adau1701_controls, + .num_controls = ARRAY_SIZE(adau1701_controls), + .dapm_widgets = adau1701_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(adau1701_dapm_widgets), + .dapm_routes = adau1701_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(adau1701_dapm_routes), + + .write = adau1701_write, + .read = adau1701_read, + + .set_sysclk = adau1701_set_sysclk, +}; + +static __devinit int adau1701_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adau1701 *adau1701; + int ret; + + adau1701 = kzalloc(sizeof(*adau1701), GFP_KERNEL); + if (!adau1701) + return -ENOMEM; + + i2c_set_clientdata(client, adau1701); + ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv, + &adau1701_dai, 1); + if (ret < 0) + kfree(adau1701); + + return ret; +} + +static __devexit int adau1701_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + kfree(i2c_get_clientdata(client)); + return 0; +} + +static const struct i2c_device_id adau1701_i2c_id[] = { + { "adau1701", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id); + +static struct i2c_driver adau1701_i2c_driver = { + .driver = { + .name = "adau1701", + .owner = THIS_MODULE, + }, + .probe = adau1701_i2c_probe, + .remove = __devexit_p(adau1701_i2c_remove), + .id_table = adau1701_i2c_id, +}; + +static int __init adau1701_init(void) +{ + return i2c_add_driver(&adau1701_i2c_driver); +} +module_init(adau1701_init); + +static void __exit adau1701_exit(void) +{ + i2c_del_driver(&adau1701_i2c_driver); +} +module_exit(adau1701_exit); + +MODULE_DESCRIPTION("ASoC ADAU1701 SigmaDSP driver"); +MODULE_AUTHOR("Cliff Cai <cliff.cai@analog.com>"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/adau1701.h b/sound/soc/codecs/adau1701.h new file mode 100644 index 000000000000..8d0949a2aec9 --- /dev/null +++ b/sound/soc/codecs/adau1701.h @@ -0,0 +1,17 @@ +/* + * header file for ADAU1701 SigmaDSP processor + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef _ADAU1701_H +#define _ADAU1701_H + +enum adau1701_clk_src { + ADAU1701_CLK_SRC_OSC, + ADAU1701_CLK_SRC_MCLK, +}; + +#endif diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 9a88a276a0ab..d9f8aded51f3 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -20,422 +20,6 @@ #include <trace/events/asoc.h> -#ifdef CONFIG_SPI_MASTER -static int do_spi_write(void *control, const char *data, int len) -{ - struct spi_device *spi = control; - int ret; - - ret = spi_write(spi, data, len); - if (ret < 0) - return ret; - - return len; -} -#endif - -static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value, const void *data, int len) -{ - int ret; - - if (!snd_soc_codec_volatile_register(codec, reg) && - reg < codec->driver->reg_cache_size && - !codec->cache_bypass) { - ret = snd_soc_cache_write(codec, reg, value); - if (ret < 0) - return -1; - } - - if (codec->cache_only) { - codec->cache_sync = 1; - return 0; - } - - ret = codec->hw_write(codec->control_data, data, len); - if (ret == len) - return 0; - if (ret < 0) - return ret; - else - return -EIO; -} - -static unsigned int do_hw_read(struct snd_soc_codec *codec, unsigned int reg) -{ - int ret; - unsigned int val; - - if (reg >= codec->driver->reg_cache_size || - snd_soc_codec_volatile_register(codec, reg) || - codec->cache_bypass) { - if (codec->cache_only) - return -1; - - BUG_ON(!codec->hw_read); - return codec->hw_read(codec, reg); - } - - ret = snd_soc_cache_read(codec, reg, &val); - if (ret < 0) - return -1; - return val; -} - -static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - return do_hw_read(codec, reg); -} - -static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u16 data; - - data = cpu_to_be16((reg << 12) | (value & 0xffffff)); - - return do_hw_write(codec, reg, value, &data, 2); -} - -static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - return do_hw_read(codec, reg); -} - -static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u16 data; - - data = cpu_to_be16((reg << 9) | (value & 0x1ff)); - - return do_hw_write(codec, reg, value, &data, 2); -} - -static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 data[2]; - - reg &= 0xff; - data[0] = reg; - data[1] = value & 0xff; - - return do_hw_write(codec, reg, value, data, 2); -} - -static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - return do_hw_read(codec, reg); -} - -static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 data[3]; - u16 val = cpu_to_be16(value); - - data[0] = reg; - memcpy(&data[1], &val, sizeof(val)); - - return do_hw_write(codec, reg, value, data, 3); -} - -static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - return do_hw_read(codec, reg); -} - -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -static unsigned int do_i2c_read(struct snd_soc_codec *codec, - void *reg, int reglen, - void *data, int datalen) -{ - struct i2c_msg xfer[2]; - int ret; - struct i2c_client *client = codec->control_data; - - /* Write register */ - xfer[0].addr = client->addr; - xfer[0].flags = 0; - xfer[0].len = reglen; - xfer[0].buf = reg; - - /* Read data */ - xfer[1].addr = client->addr; - xfer[1].flags = I2C_M_RD; - xfer[1].len = datalen; - xfer[1].buf = data; - - ret = i2c_transfer(client->adapter, xfer, 2); - if (ret == 2) - return 0; - else if (ret < 0) - return ret; - else - return -EIO; -} -#endif - -#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) -{ - u8 reg = r; - u8 data; - int ret; - - ret = do_i2c_read(codec, ®, 1, &data, 1); - if (ret < 0) - 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) -{ - u8 reg = r; - u16 data; - int ret; - - ret = do_i2c_read(codec, ®, 1, &data, 2); - if (ret < 0) - return 0; - return (data >> 8) | ((data & 0xff) << 8); -} -#else -#define snd_soc_8_16_read_i2c NULL -#endif - -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec, - unsigned int r) -{ - u16 reg = r; - u8 data; - int ret; - - ret = do_i2c_read(codec, ®, 2, &data, 1); - if (ret < 0) - return 0; - return data; -} -#else -#define snd_soc_16_8_read_i2c NULL -#endif - -static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - return do_hw_read(codec, reg); -} - -static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 data[3]; - u16 rval = cpu_to_be16(reg); - - memcpy(data, &rval, sizeof(rval)); - data[2] = value; - - return do_hw_write(codec, reg, value, data, 3); -} - -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec, - unsigned int r) -{ - u16 reg = cpu_to_be16(r); - u16 data; - int ret; - - ret = do_i2c_read(codec, ®, 2, &data, 2); - if (ret < 0) - return 0; - return be16_to_cpu(data); -} -#else -#define snd_soc_16_16_read_i2c NULL -#endif - -static unsigned int snd_soc_16_16_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - return do_hw_read(codec, reg); -} - -static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u16 data[2]; - - data[0] = cpu_to_be16(reg); - data[1] = cpu_to_be16(value); - - return do_hw_write(codec, reg, value, data, sizeof(data)); -} - -/* Primitive bulk write support for soc-cache. The data pointed to by - * `data' needs to already be in the form the hardware expects - * including any leading register specific data. Any data written - * through this function will not go through the cache as it only - * handles writing to volatile or out of bounds registers. - */ -static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg, - const void *data, size_t len) -{ - int ret; - - /* To ensure that we don't get out of sync with the cache, check - * whether the base register is volatile or if we've directly asked - * to bypass the cache. Out of bounds registers are considered - * volatile. - */ - if (!codec->cache_bypass - && !snd_soc_codec_volatile_register(codec, reg) - && reg < codec->driver->reg_cache_size) - return -EINVAL; - - switch (codec->control_type) { -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) - case SND_SOC_I2C: - ret = i2c_master_send(codec->control_data, data, len); - break; -#endif -#if defined(CONFIG_SPI_MASTER) - case SND_SOC_SPI: - ret = spi_write(codec->control_data, data, len); - break; -#endif - default: - BUG(); - } - - if (ret == len) - return 0; - if (ret < 0) - return ret; - else - return -EIO; -} - -static struct { - int addr_bits; - int data_bits; - int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int); - unsigned int (*read)(struct snd_soc_codec *, unsigned int); - unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int); -} io_types[] = { - { - .addr_bits = 4, .data_bits = 12, - .write = snd_soc_4_12_write, .read = snd_soc_4_12_read, - }, - { - .addr_bits = 7, .data_bits = 9, - .write = snd_soc_7_9_write, .read = snd_soc_7_9_read, - }, - { - .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, - .write = snd_soc_8_16_write, .read = snd_soc_8_16_read, - .i2c_read = snd_soc_8_16_read_i2c, - }, - { - .addr_bits = 16, .data_bits = 8, - .write = snd_soc_16_8_write, .read = snd_soc_16_8_read, - .i2c_read = snd_soc_16_8_read_i2c, - }, - { - .addr_bits = 16, .data_bits = 16, - .write = snd_soc_16_16_write, .read = snd_soc_16_16_read, - .i2c_read = snd_soc_16_16_read_i2c, - }, -}; - -/** - * snd_soc_codec_set_cache_io: Set up standard I/O functions. - * - * @codec: CODEC to configure. - * @addr_bits: Number of bits of register address data. - * @data_bits: Number of bits of data per register. - * @control: Control bus used. - * - * Register formats are frequently shared between many I2C and SPI - * devices. In order to promote code reuse the ASoC core provides - * some standard implementations of CODEC read and write operations - * which can be set up using this function. - * - * The caller is responsible for allocating and initialising the - * actual cache. - * - * Note that at present this code cannot be used by CODECs with - * volatile registers. - */ -int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, - int addr_bits, int data_bits, - enum snd_soc_control_type control) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(io_types); i++) - if (io_types[i].addr_bits == addr_bits && - io_types[i].data_bits == data_bits) - break; - if (i == ARRAY_SIZE(io_types)) { - printk(KERN_ERR - "No I/O functions for %d bit address %d bit data\n", - addr_bits, data_bits); - return -EINVAL; - } - - codec->write = io_types[i].write; - codec->read = io_types[i].read; - codec->bulk_write_raw = snd_soc_hw_bulk_write_raw; - - switch (control) { - case SND_SOC_CUSTOM: - break; - - case SND_SOC_I2C: -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) - codec->hw_write = (hw_write_t)i2c_master_send; -#endif - if (io_types[i].i2c_read) - codec->hw_read = io_types[i].i2c_read; - - codec->control_data = container_of(codec->dev, - struct i2c_client, - dev); - break; - - case SND_SOC_SPI: -#ifdef CONFIG_SPI_MASTER - codec->hw_write = do_spi_write; -#endif - - codec->control_data = container_of(codec->dev, - struct spi_device, - dev); - break; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); - static bool snd_soc_set_cache_val(void *base, unsigned int idx, unsigned int val, unsigned int word_size) { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index fd2d774797bb..ceb2ba44fd3f 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -124,6 +124,43 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); } +static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg) +{ + if (w->codec) + return snd_soc_read(w->codec, reg); + return 0; +} + +static int soc_widget_write(struct snd_soc_dapm_widget *w, int reg, int val) +{ + if (w->codec) + return snd_soc_write(w->codec, reg, val); + return 0; +} + +static int soc_widget_update_bits(struct snd_soc_dapm_widget *w, + unsigned short reg, unsigned int mask, unsigned int value) +{ + int change; + unsigned int old, new; + int ret; + + ret = soc_widget_read(w, reg); + if (ret < 0) + return ret; + + old = ret; + new = (old & ~mask) | (value & mask); + change = old != new; + if (change) { + ret = soc_widget_write(w, reg, new); + if (ret < 0) + return ret; + } + + return change; +} + /** * snd_soc_dapm_set_bias_level - set the bias level for the system * @dapm: DAPM context @@ -181,7 +218,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; - val = snd_soc_read(w->codec, reg); + val = soc_widget_read(w, reg); val = (val >> shift) & mask; if ((invert && !val) || (!invert && val)) @@ -197,7 +234,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, for (bitmask = 1; bitmask < e->max; bitmask <<= 1) ; - val = snd_soc_read(w->codec, e->reg); + val = soc_widget_read(w, e->reg); item = (val >> e->shift_l) & (bitmask - 1); p->connect = 0; @@ -227,7 +264,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, w->kcontrol_news[i].private_value; int val, item; - val = snd_soc_read(w->codec, e->reg); + val = soc_widget_read(w, e->reg); val = (val >> e->shift_l) & e->mask; for (item = 0; item < e->max; item++) { if (val == e->values[item]) @@ -593,6 +630,9 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) } list_for_each_entry(path, &widget->sinks, list_source) { + if (path->weak) + continue; + if (path->walked) continue; @@ -643,6 +683,9 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) } list_for_each_entry(path, &widget->sources, list_sink) { + if (path->weak) + continue; + if (path->walked) continue; @@ -668,7 +711,7 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w, else val = w->off_val; - snd_soc_update_bits(w->codec, -(w->reg + 1), + soc_widget_update_bits(w, -(w->reg + 1), w->mask << w->shift, val << w->shift); return 0; @@ -724,6 +767,9 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) /* Check if one of our outputs is connected */ list_for_each_entry(path, &w->sinks, list_source) { + if (path->weak) + continue; + if (path->connected && !path->connected(path->source, path->sink)) continue; @@ -872,11 +918,17 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, } if (reg >= 0) { + /* Any widget will do, they should all be updating the + * same register. + */ + w = list_first_entry(pending, struct snd_soc_dapm_widget, + power_list); + pop_dbg(dapm->dev, card->pop_time, "pop test : Applying 0x%x/0x%x to %x in %dms\n", value, mask, reg, card->pop_time); pop_wait(card->pop_time); - snd_soc_update_bits(dapm->codec, reg, mask, value); + soc_widget_update_bits(w, reg, mask, value); } list_for_each_entry(w, pending, power_list) { @@ -1806,6 +1858,84 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, } EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); +static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route) +{ + struct snd_soc_dapm_widget *source = dapm_find_widget(dapm, + route->source, + true); + struct snd_soc_dapm_widget *sink = dapm_find_widget(dapm, + route->sink, + true); + struct snd_soc_dapm_path *path; + int count = 0; + + if (!source) { + dev_err(dapm->dev, "Unable to find source %s for weak route\n", + route->source); + return -ENODEV; + } + + if (!sink) { + dev_err(dapm->dev, "Unable to find sink %s for weak route\n", + route->sink); + return -ENODEV; + } + + if (route->control || route->connected) + dev_warn(dapm->dev, "Ignoring control for weak route %s->%s\n", + route->source, route->sink); + + list_for_each_entry(path, &source->sinks, list_source) { + if (path->sink == sink) { + path->weak = 1; + count++; + } + } + + if (count == 0) + dev_err(dapm->dev, "No path found for weak route %s->%s\n", + route->source, route->sink); + if (count > 1) + dev_warn(dapm->dev, "%d paths found for weak route %s->%s\n", + count, route->source, route->sink); + + return 0; +} + +/** + * snd_soc_dapm_weak_routes - Mark routes between DAPM widgets as weak + * @dapm: DAPM context + * @route: audio routes + * @num: number of routes + * + * Mark existing routes matching those specified in the passed array + * as being weak, meaning that they are ignored for the purpose of + * power decisions. The main intended use case is for sidetone paths + * which couple audio between other independent paths if they are both + * active in order to make the combination work better at the user + * level but which aren't intended to be "used". + * + * Note that CODEC drivers should not use this as sidetone type paths + * can frequently also be used as bypass paths. + */ +int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_route *route, int num) +{ + int i, err; + int ret = 0; + + for (i = 0; i < num; i++) { + err = snd_soc_dapm_weak_route(dapm, route); + if (err) + ret = err; + route++; + } + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes); + /** * snd_soc_dapm_new_widgets - add new dapm widgets * @dapm: DAPM context @@ -1877,7 +2007,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) /* Read the initial power state from the device */ if (w->reg >= 0) { - val = snd_soc_read(w->codec, w->reg); + val = soc_widget_read(w, w->reg); val &= 1 << w->shift; if (w->invert) val = !val; diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c new file mode 100644 index 000000000000..cca490c80589 --- /dev/null +++ b/sound/soc/soc-io.c @@ -0,0 +1,396 @@ +/* + * soc-io.c -- ASoC register I/O helpers + * + * Copyright 2009-2011 Wolfson Microelectronics PLC. + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * 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. + */ + +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <sound/soc.h> + +#include <trace/events/asoc.h> + +#ifdef CONFIG_SPI_MASTER +static int do_spi_write(void *control, const char *data, int len) +{ + struct spi_device *spi = control; + int ret; + + ret = spi_write(spi, data, len); + if (ret < 0) + return ret; + + return len; +} +#endif + +static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value, const void *data, int len) +{ + int ret; + + if (!snd_soc_codec_volatile_register(codec, reg) && + reg < codec->driver->reg_cache_size && + !codec->cache_bypass) { + ret = snd_soc_cache_write(codec, reg, value); + if (ret < 0) + return -1; + } + + if (codec->cache_only) { + codec->cache_sync = 1; + return 0; + } + + ret = codec->hw_write(codec->control_data, data, len); + if (ret == len) + return 0; + if (ret < 0) + return ret; + else + return -EIO; +} + +static unsigned int hw_read(struct snd_soc_codec *codec, unsigned int reg) +{ + int ret; + unsigned int val; + + if (reg >= codec->driver->reg_cache_size || + snd_soc_codec_volatile_register(codec, reg) || + codec->cache_bypass) { + if (codec->cache_only) + return -1; + + BUG_ON(!codec->hw_read); + return codec->hw_read(codec, reg); + } + + ret = snd_soc_cache_read(codec, reg, &val); + if (ret < 0) + return -1; + return val; +} + +static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u16 data; + + data = cpu_to_be16((reg << 12) | (value & 0xffffff)); + + return do_hw_write(codec, reg, value, &data, 2); +} + +static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u16 data; + + data = cpu_to_be16((reg << 9) | (value & 0x1ff)); + + return do_hw_write(codec, reg, value, &data, 2); +} + +static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[2]; + + reg &= 0xff; + data[0] = reg; + data[1] = value & 0xff; + + return do_hw_write(codec, reg, value, data, 2); +} + +static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[3]; + u16 val = cpu_to_be16(value); + + data[0] = reg; + memcpy(&data[1], &val, sizeof(val)); + + return do_hw_write(codec, reg, value, data, 3); +} + +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +static unsigned int do_i2c_read(struct snd_soc_codec *codec, + void *reg, int reglen, + void *data, int datalen) +{ + struct i2c_msg xfer[2]; + int ret; + struct i2c_client *client = codec->control_data; + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = reglen; + xfer[0].buf = reg; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = datalen; + xfer[1].buf = data; + + ret = i2c_transfer(client->adapter, xfer, 2); + if (ret == 2) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} +#endif + +#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) +{ + u8 reg = r; + u8 data; + int ret; + + ret = do_i2c_read(codec, ®, 1, &data, 1); + if (ret < 0) + 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) +{ + u8 reg = r; + u16 data; + int ret; + + ret = do_i2c_read(codec, ®, 1, &data, 2); + if (ret < 0) + return 0; + return (data >> 8) | ((data & 0xff) << 8); +} +#else +#define snd_soc_8_16_read_i2c NULL +#endif + +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec, + unsigned int r) +{ + u16 reg = r; + u8 data; + int ret; + + ret = do_i2c_read(codec, ®, 2, &data, 1); + if (ret < 0) + return 0; + return data; +} +#else +#define snd_soc_16_8_read_i2c NULL +#endif + +static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u8 data[3]; + u16 rval = cpu_to_be16(reg); + + memcpy(data, &rval, sizeof(rval)); + data[2] = value; + + return do_hw_write(codec, reg, value, data, 3); +} + +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec, + unsigned int r) +{ + u16 reg = cpu_to_be16(r); + u16 data; + int ret; + + ret = do_i2c_read(codec, ®, 2, &data, 2); + if (ret < 0) + return 0; + return be16_to_cpu(data); +} +#else +#define snd_soc_16_16_read_i2c NULL +#endif + +static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + u16 data[2]; + + data[0] = cpu_to_be16(reg); + data[1] = cpu_to_be16(value); + + return do_hw_write(codec, reg, value, data, sizeof(data)); +} + +/* Primitive bulk write support for soc-cache. The data pointed to by + * `data' needs to already be in the form the hardware expects + * including any leading register specific data. Any data written + * through this function will not go through the cache as it only + * handles writing to volatile or out of bounds registers. + */ +static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg, + const void *data, size_t len) +{ + int ret; + + /* To ensure that we don't get out of sync with the cache, check + * whether the base register is volatile or if we've directly asked + * to bypass the cache. Out of bounds registers are considered + * volatile. + */ + if (!codec->cache_bypass + && !snd_soc_codec_volatile_register(codec, reg) + && reg < codec->driver->reg_cache_size) + return -EINVAL; + + switch (codec->control_type) { +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) + case SND_SOC_I2C: + ret = i2c_master_send(to_i2c_client(codec->dev), data, len); + break; +#endif +#if defined(CONFIG_SPI_MASTER) + case SND_SOC_SPI: + ret = spi_write(to_spi_device(codec->dev), data, len); + break; +#endif + default: + BUG(); + } + + if (ret == len) + return 0; + if (ret < 0) + return ret; + else + return -EIO; +} + +static struct { + int addr_bits; + int data_bits; + int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int); + unsigned int (*read)(struct snd_soc_codec *, unsigned int); + unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int); +} io_types[] = { + { + .addr_bits = 4, .data_bits = 12, + .write = snd_soc_4_12_write, + }, + { + .addr_bits = 7, .data_bits = 9, + .write = snd_soc_7_9_write, + }, + { + .addr_bits = 8, .data_bits = 8, + .write = snd_soc_8_8_write, + .i2c_read = snd_soc_8_8_read_i2c, + }, + { + .addr_bits = 8, .data_bits = 16, + .write = snd_soc_8_16_write, + .i2c_read = snd_soc_8_16_read_i2c, + }, + { + .addr_bits = 16, .data_bits = 8, + .write = snd_soc_16_8_write, + .i2c_read = snd_soc_16_8_read_i2c, + }, + { + .addr_bits = 16, .data_bits = 16, + .write = snd_soc_16_16_write, + .i2c_read = snd_soc_16_16_read_i2c, + }, +}; + +/** + * snd_soc_codec_set_cache_io: Set up standard I/O functions. + * + * @codec: CODEC to configure. + * @addr_bits: Number of bits of register address data. + * @data_bits: Number of bits of data per register. + * @control: Control bus used. + * + * Register formats are frequently shared between many I2C and SPI + * devices. In order to promote code reuse the ASoC core provides + * some standard implementations of CODEC read and write operations + * which can be set up using this function. + * + * The caller is responsible for allocating and initialising the + * actual cache. + * + * Note that at present this code cannot be used by CODECs with + * volatile registers. + */ +int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, + int addr_bits, int data_bits, + enum snd_soc_control_type control) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(io_types); i++) + if (io_types[i].addr_bits == addr_bits && + io_types[i].data_bits == data_bits) + break; + if (i == ARRAY_SIZE(io_types)) { + printk(KERN_ERR + "No I/O functions for %d bit address %d bit data\n", + addr_bits, data_bits); + return -EINVAL; + } + + codec->write = io_types[i].write; + codec->read = hw_read; + codec->bulk_write_raw = snd_soc_hw_bulk_write_raw; + + switch (control) { + case SND_SOC_I2C: +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) + codec->hw_write = (hw_write_t)i2c_master_send; +#endif + if (io_types[i].i2c_read) + codec->hw_read = io_types[i].i2c_read; + + codec->control_data = container_of(codec->dev, + struct i2c_client, + dev); + break; + + case SND_SOC_SPI: +#ifdef CONFIG_SPI_MASTER + codec->hw_write = do_spi_write; +#endif + + codec->control_data = container_of(codec->dev, + struct spi_device, + dev); + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io); + |