diff options
Diffstat (limited to 'sound/soc')
83 files changed, 6112 insertions, 1979 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index e239345a4d5..6943e24a74a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -29,16 +29,21 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI select SND_SOC_CX20442 select SND_SOC_DA7210 if I2C + select SND_SOC_DFBMCS320 select SND_SOC_JZ4740_CODEC if SOC_JZ4740 + select SND_SOC_LM4857 if I2C select SND_SOC_MAX98088 if I2C + select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9877 if I2C select SND_SOC_PCM3008 + select SND_SOC_SGTL5000 if I2C select SND_SOC_SN95031 if INTEL_SCU_IPC select SND_SOC_SPDIF select SND_SOC_SSM2602 if I2C select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TLV320AIC23 if I2C select SND_SOC_TLV320AIC26 if SPI_MASTER + select SND_SOC_TVL320AIC32X4 if I2C select SND_SOC_TLV320AIC3X if I2C select SND_SOC_TPA6130A2 if I2C select SND_SOC_TLV320DAC33 if I2C @@ -46,7 +51,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TWL6040 if TWL4030_CORE select SND_SOC_UDA134X select SND_SOC_UDA1380 if I2C - select SND_SOC_WL1273 if RADIO_WL1273 + select SND_SOC_WL1273 if MFD_WL1273_CORE select SND_SOC_WM2000 if I2C select SND_SOC_WM8350 if MFD_WM8350 select SND_SOC_WM8400 if MFD_WM8400 @@ -173,15 +178,25 @@ config SND_SOC_L3 config SND_SOC_DA7210 tristate +config SND_SOC_DFBMCS320 + tristate + config SND_SOC_DMIC tristate config SND_SOC_MAX98088 tristate +config SND_SOC_MAX9850 + tristate + config SND_SOC_PCM3008 tristate +#Freescale sgtl5000 codec +config SND_SOC_SGTL5000 + tristate + config SND_SOC_SN95031 tristate @@ -201,6 +216,9 @@ config SND_SOC_TLV320AIC26 tristate "TI TLV320AIC26 Codec support" if SND_SOC_OF_SIMPLE depends on SPI +config SND_SOC_TVL320AIC32X4 + tristate + config SND_SOC_TLV320AIC3X tristate @@ -338,6 +356,9 @@ config SND_SOC_WM9713 tristate # Amp +config SND_SOC_LM4857 + tristate + config SND_SOC_MAX9877 tristate @@ -349,4 +370,3 @@ config SND_SOC_WM2000 config SND_SOC_WM9090 tristate - diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 83b7accd703..379bc55f072 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -15,10 +15,13 @@ snd-soc-cs4270-objs := cs4270.o snd-soc-cs4271-objs := cs4271.o snd-soc-cx20442-objs := cx20442.o snd-soc-da7210-objs := da7210.o +snd-soc-dfbmcs320-objs := dfbmcs320.o snd-soc-dmic-objs := dmic.o snd-soc-l3-objs := l3.o snd-soc-max98088-objs := max98088.o +snd-soc-max9850-objs := max9850.o snd-soc-pcm3008-objs := pcm3008.o +snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-sn95031-objs := sn95031.o snd-soc-spdif-objs := spdif_transciever.o @@ -27,6 +30,7 @@ snd-soc-stac9766-objs := stac9766.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o +snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o snd-soc-tlv320dac33-objs := tlv320dac33.o snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o @@ -75,6 +79,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-jz4740-codec-objs := jz4740.o # Amp +snd-soc-lm4857-objs := lm4857.o snd-soc-max9877-objs := max9877.o snd-soc-tpa6130a2-objs := tpa6130a2.o snd-soc-wm2000-objs := wm2000.o @@ -91,18 +96,21 @@ obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4642) += snd-soc-ak4642.o obj-$(CONFIG_SND_SOC_AK4671) += snd-soc-ak4671.o +obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o +obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o +obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o -obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o +obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o @@ -110,6 +118,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o +obj-$(CONFIG_SND_SOC_TVL320AIC32X4) += snd-soc-tlv320aic32x4.o obj-$(CONFIG_SND_SOC_TLV320DAC33) += snd-soc-tlv320dac33.o obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o @@ -157,6 +166,7 @@ obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o # Amp +obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index c27f8f59dc6..cbf0b6d400b 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -294,7 +294,6 @@ static struct spi_driver ak4104_spi_driver = { static int __init ak4104_init(void) { - pr_info("Asahi Kasei AK4104 ALSA SoC Codec Driver\n"); return spi_register_driver(&ak4104_spi_driver); } module_init(ak4104_init); diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c index 347a567b01e..b8066ef10bb 100644 --- a/sound/soc/codecs/cq93vc.c +++ b/sound/soc/codecs/cq93vc.c @@ -153,7 +153,8 @@ static int cq93vc_resume(struct snd_soc_codec *codec) static int cq93vc_probe(struct snd_soc_codec *codec) { - struct davinci_vc *davinci_vc = snd_soc_codec_get_drvdata(codec); + struct davinci_vc *davinci_vc = + mfd_get_data(to_platform_device(codec->dev)); davinci_vc->cq93vc.codec = codec; codec->control_data = davinci_vc; diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index c0fccadaea9..0206a17d728 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -719,7 +719,7 @@ static int cs4270_i2c_remove(struct i2c_client *i2c_client) /* * cs4270_id - I2C device IDs supported by this driver */ -static struct i2c_device_id cs4270_id[] = { +static const struct i2c_device_id cs4270_id[] = { {"cs4270", 0}, {} }; @@ -743,8 +743,6 @@ static struct i2c_driver cs4270_i2c_driver = { static int __init cs4270_init(void) { - pr_info("Cirrus Logic CS4270 ALSA SoC Codec Driver\n"); - return i2c_add_driver(&cs4270_i2c_driver); } module_init(cs4270_init); diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 9c5b7db0ce6..083aab96ca8 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -33,6 +33,7 @@ #define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE) +#define CS4271_PCM_RATES SNDRV_PCM_RATE_8000_192000 /* * CS4271 registers @@ -167,27 +168,6 @@ struct cs4271_private { int gpio_disable; }; -struct cs4271_clk_cfg { - unsigned int ratio; /* MCLK / sample rate */ - u8 speed_mode; /* codec speed mode: 1x, 2x, 4x */ - u8 mclk_master; /* ratio bit mask for Master mode */ - u8 mclk_slave; /* ratio bit mask for Slave mode */ -}; - -static struct cs4271_clk_cfg cs4271_clk_tab[] = { - {64, CS4271_MODE1_MODE_4X, CS4271_MODE1_DIV_1, CS4271_MODE1_DIV_1}, - {96, CS4271_MODE1_MODE_4X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1}, - {128, CS4271_MODE1_MODE_2X, CS4271_MODE1_DIV_1, CS4271_MODE1_DIV_1}, - {192, CS4271_MODE1_MODE_2X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1}, - {256, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_1, CS4271_MODE1_DIV_1}, - {384, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1}, - {512, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_2, CS4271_MODE1_DIV_1}, - {768, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_3, CS4271_MODE1_DIV_3}, - {1024, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_3, CS4271_MODE1_DIV_3} -}; - -#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab) - /* * @freq is the desired MCLK rate * MCLK rate should (c) be the sample rate, multiplied by one of the @@ -296,6 +276,45 @@ static int cs4271_put_deemph(struct snd_kcontrol *kcontrol, return cs4271_set_deemph(codec); } +struct cs4271_clk_cfg { + bool master; /* codec mode */ + u8 speed_mode; /* codec speed mode: 1x, 2x, 4x */ + unsigned short ratio; /* MCLK / sample rate */ + u8 ratio_mask; /* ratio bit mask for Master mode */ +}; + +static struct cs4271_clk_cfg cs4271_clk_tab[] = { + {1, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1}, + {1, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_15}, + {1, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_2}, + {1, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_3}, + {1, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1}, + {1, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_15}, + {1, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_2}, + {1, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_3}, + {1, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1}, + {1, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_15}, + {1, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_2}, + {1, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_3}, + {0, CS4271_MODE1_MODE_1X, 256, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_1X, 384, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_1X, 512, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_1X, 768, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_1X, 1024, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_2X, 128, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_2X, 192, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_2X, 256, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_2X, 384, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_2X, 512, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_4X, 64, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_4X, 96, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_4X, 128, CS4271_MODE1_DIV_1}, + {0, CS4271_MODE1_MODE_4X, 192, CS4271_MODE1_DIV_2}, + {0, CS4271_MODE1_MODE_4X, 256, CS4271_MODE1_DIV_2}, +}; + +#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab) + static int cs4271_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -307,23 +326,28 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream, unsigned int ratio, val; cs4271->rate = params_rate(params); + + /* Configure DAC */ + if (cs4271->rate < 50000) + val = CS4271_MODE1_MODE_1X; + else if (cs4271->rate < 100000) + val = CS4271_MODE1_MODE_2X; + else + val = CS4271_MODE1_MODE_4X; + ratio = cs4271->mclk / cs4271->rate; for (i = 0; i < CS4171_NR_RATIOS; i++) - if (cs4271_clk_tab[i].ratio == ratio) + if ((cs4271_clk_tab[i].master == cs4271->master) && + (cs4271_clk_tab[i].speed_mode == val) && + (cs4271_clk_tab[i].ratio == ratio)) break; - if ((i == CS4171_NR_RATIOS) || ((ratio == 1024) && cs4271->master)) { + if (i == CS4171_NR_RATIOS) { dev_err(codec->dev, "Invalid sample rate\n"); return -EINVAL; } - /* Configure DAC */ - val = cs4271_clk_tab[i].speed_mode; - - if (cs4271->master) - val |= cs4271_clk_tab[i].mclk_master; - else - val |= cs4271_clk_tab[i].mclk_slave; + val |= cs4271_clk_tab[i].ratio_mask; ret = snd_soc_update_bits(codec, CS4271_MODE1, CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val); @@ -392,14 +416,14 @@ static struct snd_soc_dai_driver cs4271_dai = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, + .rates = CS4271_PCM_RATES, .formats = CS4271_PCM_FORMATS, }, .capture = { .stream_name = "Capture", .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, + .rates = CS4271_PCM_RATES, .formats = CS4271_PCM_FORMATS, }, .ops = &cs4271_dai_ops, @@ -441,22 +465,11 @@ static int cs4271_probe(struct snd_soc_codec *codec) struct cs4271_platform_data *cs4271plat = codec->dev->platform_data; int ret; int gpio_nreset = -EINVAL; - int gpio_disable = -EINVAL; codec->control_data = cs4271->control_data; - if (cs4271plat) { - if (gpio_is_valid(cs4271plat->gpio_nreset)) - gpio_nreset = cs4271plat->gpio_nreset; - if (gpio_is_valid(cs4271plat->gpio_disable)) - gpio_disable = cs4271plat->gpio_disable; - } - - if (gpio_disable >= 0) - if (gpio_request(gpio_disable, "CS4271 Disable")) - gpio_disable = -EINVAL; - if (gpio_disable >= 0) - gpio_direction_output(gpio_disable, 0); + if (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset)) + gpio_nreset = cs4271plat->gpio_nreset; if (gpio_nreset >= 0) if (gpio_request(gpio_nreset, "CS4271 Reset")) @@ -471,7 +484,6 @@ static int cs4271_probe(struct snd_soc_codec *codec) } cs4271->gpio_nreset = gpio_nreset; - cs4271->gpio_disable = gpio_disable; /* * In case of I2C, chip address specified in board data. @@ -509,10 +521,9 @@ static int cs4271_probe(struct snd_soc_codec *codec) static int cs4271_remove(struct snd_soc_codec *codec) { struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); - int gpio_nreset, gpio_disable; + int gpio_nreset; gpio_nreset = cs4271->gpio_nreset; - gpio_disable = cs4271->gpio_disable; if (gpio_is_valid(gpio_nreset)) { /* Set codec to the reset state */ @@ -520,9 +531,6 @@ static int cs4271_remove(struct snd_soc_codec *codec) gpio_free(gpio_nreset); } - if (gpio_is_valid(gpio_disable)) - gpio_free(gpio_disable); - return 0; }; @@ -571,7 +579,7 @@ static struct spi_driver cs4271_spi_driver = { #endif /* defined(CONFIG_SPI_MASTER) */ #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) -static struct i2c_device_id cs4271_i2c_id[] = { +static const struct i2c_device_id cs4271_i2c_id[] = { {"cs4271", 0}, {} }; diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index bb4bf65b9e7..0bb424af956 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -367,7 +367,7 @@ static int cx20442_codec_remove(struct snd_soc_codec *codec) return 0; } -static const u8 cx20442_reg = CX20442_TELOUT | CX20442_MIC; +static const u8 cx20442_reg; static struct snd_soc_codec_driver cx20442_codec_dev = { .probe = cx20442_codec_probe, diff --git a/sound/soc/codecs/dfbmcs320.c b/sound/soc/codecs/dfbmcs320.c new file mode 100644 index 00000000000..704bbde6573 --- /dev/null +++ b/sound/soc/codecs/dfbmcs320.c @@ -0,0 +1,72 @@ +/* + * Driver for the DFBM-CS320 bluetooth module + * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de> + * + * 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/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <sound/soc.h> + +static struct snd_soc_dai_driver dfbmcs320_dai = { + .name = "dfbmcs320-pcm", + .playback = { + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}; + +static struct snd_soc_codec_driver soc_codec_dev_dfbmcs320; + +static int __devinit dfbmcs320_probe(struct platform_device *pdev) +{ + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_dfbmcs320, + &dfbmcs320_dai, 1); +} + +static int __devexit dfbmcs320_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + + return 0; +} + +static struct platform_driver dfmcs320_driver = { + .driver = { + .name = "dfbmcs320", + .owner = THIS_MODULE, + }, + .probe = dfbmcs320_probe, + .remove = __devexit_p(dfbmcs320_remove), +}; + +static int __init dfbmcs320_init(void) +{ + return platform_driver_register(&dfmcs320_driver); +} +module_init(dfbmcs320_init); + +static void __exit dfbmcs320_exit(void) +{ + platform_driver_unregister(&dfmcs320_driver); +} +module_exit(dfbmcs320_exit); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("ASoC DFBM-CS320 bluethooth module driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c new file mode 100644 index 00000000000..72de47e5d04 --- /dev/null +++ b/sound/soc/codecs/lm4857.c @@ -0,0 +1,276 @@ +/* + * LM4857 AMP driver + * + * Copyright 2007 Wolfson Microelectronics PLC. + * Author: Graeme Gregory + * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com + * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de> + * + * 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/init.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/slab.h> + +#include <sound/core.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +struct lm4857 { + struct i2c_client *i2c; + uint8_t mode; +}; + +static const uint8_t lm4857_default_regs[] = { + 0x00, 0x00, 0x00, 0x00, +}; + +/* The register offsets in the cache array */ +#define LM4857_MVOL 0 +#define LM4857_LVOL 1 +#define LM4857_RVOL 2 +#define LM4857_CTRL 3 + +/* the shifts required to set these bits */ +#define LM4857_3D 5 +#define LM4857_WAKEUP 5 +#define LM4857_EPGAIN 4 + +static int lm4857_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + uint8_t data; + int ret; + + ret = snd_soc_cache_write(codec, reg, value); + if (ret < 0) + return ret; + + data = (reg << 6) | value; + ret = i2c_master_send(codec->control_data, &data, 1); + if (ret != 1) { + dev_err(codec->dev, "Failed to write register: %d\n", ret); + return ret; + } + + return 0; +} + +static unsigned int lm4857_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + unsigned int val; + int ret; + + ret = snd_soc_cache_read(codec, reg, &val); + if (ret) + return -1; + + return val; +} + +static int lm4857_get_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = lm4857->mode; + + return 0; +} + +static int lm4857_set_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); + uint8_t value = ucontrol->value.integer.value[0]; + + lm4857->mode = value; + + if (codec->dapm.bias_level == SND_SOC_BIAS_ON) + snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, value + 6); + + return 1; +} + +static int lm4857_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, lm4857->mode + 6); + break; + case SND_SOC_BIAS_STANDBY: + snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, 0); + break; + default: + break; + } + + codec->dapm.bias_level = level; + + return 0; +} + +static const char *lm4857_mode[] = { + "Earpiece", + "Loudspeaker", + "Loudspeaker + Headphone", + "Headphone", +}; + +static const struct soc_enum lm4857_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode); + +static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + + SND_SOC_DAPM_OUTPUT("LS"), + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("EP"), +}; + +static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0); +static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0); + +static const struct snd_kcontrol_new lm4857_controls[] = { + SOC_SINGLE_TLV("Left Playback Volume", LM4857_LVOL, 0, 31, 0, + stereo_tlv), + SOC_SINGLE_TLV("Right Playback Volume", LM4857_RVOL, 0, 31, 0, + stereo_tlv), + SOC_SINGLE_TLV("Mono Playback Volume", LM4857_MVOL, 0, 31, 0, + mono_tlv), + SOC_SINGLE("Spk 3D Playback Switch", LM4857_LVOL, LM4857_3D, 1, 0), + SOC_SINGLE("HP 3D Playback Switch", LM4857_RVOL, LM4857_3D, 1, 0), + SOC_SINGLE("Fast Wakeup Playback Switch", LM4857_CTRL, + LM4857_WAKEUP, 1, 0), + SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL, + LM4857_EPGAIN, 1, 0), + + SOC_ENUM_EXT("Mode", lm4857_mode_enum, + lm4857_get_mode, lm4857_set_mode), +}; + +/* There is a demux inbetween the the input signal and the output signals. + * Currently there is no easy way to model it in ASoC and since it does not make + * much of a difference in practice simply connect the input direclty to the + * outputs. */ +static const struct snd_soc_dapm_route lm4857_routes[] = { + {"LS", NULL, "IN"}, + {"HP", NULL, "IN"}, + {"EP", NULL, "IN"}, +}; + +static int lm4857_probe(struct snd_soc_codec *codec) +{ + struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + codec->control_data = lm4857->i2c; + + ret = snd_soc_add_controls(codec, lm4857_controls, + ARRAY_SIZE(lm4857_controls)); + if (ret) + return ret; + + ret = snd_soc_dapm_new_controls(dapm, lm4857_dapm_widgets, + ARRAY_SIZE(lm4857_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, lm4857_routes, + ARRAY_SIZE(lm4857_routes)); + if (ret) + return ret; + + snd_soc_dapm_new_widgets(dapm); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_lm4857 = { + .write = lm4857_write, + .read = lm4857_read, + .probe = lm4857_probe, + .reg_cache_size = ARRAY_SIZE(lm4857_default_regs), + .reg_word_size = sizeof(uint8_t), + .reg_cache_default = lm4857_default_regs, + .set_bias_level = lm4857_set_bias_level, +}; + +static int __devinit lm4857_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct lm4857 *lm4857; + int ret; + + lm4857 = kzalloc(sizeof(*lm4857), GFP_KERNEL); + if (!lm4857) + return -ENOMEM; + + i2c_set_clientdata(i2c, lm4857); + + lm4857->i2c = i2c; + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0); + + if (ret) { + kfree(lm4857); + return ret; + } + + return 0; +} + +static int __devexit lm4857_i2c_remove(struct i2c_client *i2c) +{ + struct lm4857 *lm4857 = i2c_get_clientdata(i2c); + + snd_soc_unregister_codec(&i2c->dev); + kfree(lm4857); + + return 0; +} + +static const struct i2c_device_id lm4857_i2c_id[] = { + { "lm4857", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id); + +static struct i2c_driver lm4857_i2c_driver = { + .driver = { + .name = "lm4857", + .owner = THIS_MODULE, + }, + .probe = lm4857_i2c_probe, + .remove = __devexit_p(lm4857_i2c_remove), + .id_table = lm4857_i2c_id, +}; + +static int __init lm4857_init(void) +{ + return i2c_add_driver(&lm4857_i2c_driver); +} +module_init(lm4857_init); + +static void __exit lm4857_exit(void) +{ + i2c_del_driver(&lm4857_i2c_driver); +} +module_exit(lm4857_exit); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("LM4857 amplifier driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c new file mode 100644 index 00000000000..208d2ee6185 --- /dev/null +++ b/sound/soc/codecs/max9850.c @@ -0,0 +1,389 @@ +/* + * max9850.c -- codec driver for max9850 + * + * Copyright (C) 2011 taskit GmbH + * + * Author: Christian Glindkamp <christian.glindkamp@taskit.de> + * + * Initial development of this code was funded by + * MICRONIC Computer Systeme GmbH, http://www.mcsberlin.de/ + * + * 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/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "max9850.h" + +struct max9850_priv { + unsigned int sysclk; +}; + +/* max9850 register cache */ +static const u8 max9850_reg[MAX9850_CACHEREGNUM] = { + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* these registers are not used at the moment but provided for the sake of + * completeness */ +static int max9850_volatile_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + switch (reg) { + case MAX9850_STATUSA: + case MAX9850_STATUSB: + return 1; + default: + return 0; + } +} + +static const unsigned int max9850_tlv[] = { + TLV_DB_RANGE_HEAD(4), + 0x18, 0x1f, TLV_DB_SCALE_ITEM(-7450, 400, 0), + 0x20, 0x33, TLV_DB_SCALE_ITEM(-4150, 200, 0), + 0x34, 0x37, TLV_DB_SCALE_ITEM(-150, 100, 0), + 0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0), +}; + +static const struct snd_kcontrol_new max9850_controls[] = { +SOC_SINGLE_TLV("Headphone Volume", MAX9850_VOLUME, 0, 0x3f, 1, max9850_tlv), +SOC_SINGLE("Headphone Switch", MAX9850_VOLUME, 7, 1, 1), +SOC_SINGLE("Mono Switch", MAX9850_GENERAL_PURPOSE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new max9850_mixer_controls[] = { + SOC_DAPM_SINGLE("Line In Switch", MAX9850_ENABLE, 1, 1, 0), +}; + +static const struct snd_soc_dapm_widget max9850_dapm_widgets[] = { +SND_SOC_DAPM_SUPPLY("Charge Pump 1", MAX9850_ENABLE, 4, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("Charge Pump 2", MAX9850_ENABLE, 5, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("MCLK", MAX9850_ENABLE, 6, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("SHDN", MAX9850_ENABLE, 7, 0, NULL, 0), +SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", MAX9850_ENABLE, 2, 0, + &max9850_mixer_controls[0], + ARRAY_SIZE(max9850_mixer_controls)), +SND_SOC_DAPM_PGA("Headphone Output", MAX9850_ENABLE, 3, 0, NULL, 0), +SND_SOC_DAPM_DAC("DAC", "HiFi Playback", MAX9850_ENABLE, 0, 0), +SND_SOC_DAPM_OUTPUT("OUTL"), +SND_SOC_DAPM_OUTPUT("HPL"), +SND_SOC_DAPM_OUTPUT("OUTR"), +SND_SOC_DAPM_OUTPUT("HPR"), +SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0), +SND_SOC_DAPM_INPUT("INL"), +SND_SOC_DAPM_INPUT("INR"), +}; + +static const struct snd_soc_dapm_route intercon[] = { + /* output mixer */ + {"Output Mixer", NULL, "DAC"}, + {"Output Mixer", "Line In Switch", "Line Input"}, + + /* outputs */ + {"Headphone Output", NULL, "Output Mixer"}, + {"HPL", NULL, "Headphone Output"}, + {"HPR", NULL, "Headphone Output"}, + {"OUTL", NULL, "Output Mixer"}, + {"OUTR", NULL, "Output Mixer"}, + + /* inputs */ + {"Line Input", NULL, "INL"}, + {"Line Input", NULL, "INR"}, + + /* supplies */ + {"Output Mixer", NULL, "Charge Pump 1"}, + {"Output Mixer", NULL, "Charge Pump 2"}, + {"Output Mixer", NULL, "SHDN"}, + {"DAC", NULL, "MCLK"}, +}; + +static int max9850_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec); + u64 lrclk_div; + u8 sf, da; + + if (!max9850->sysclk) + return -EINVAL; + + /* lrclk_div = 2^22 * rate / iclk with iclk = mclk / sf */ + sf = (snd_soc_read(codec, MAX9850_CLOCK) >> 2) + 1; + lrclk_div = (1 << 22); + lrclk_div *= params_rate(params); + lrclk_div *= sf; + do_div(lrclk_div, max9850->sysclk); + + snd_soc_write(codec, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f); + snd_soc_write(codec, MAX9850_LRCLK_LSB, lrclk_div & 0xff); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + da = 0; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + da = 0x2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + da = 0x3; + break; + default: + return -EINVAL; + } + snd_soc_update_bits(codec, MAX9850_DIGITAL_AUDIO, 0x3, da); + + return 0; +} + +static int max9850_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec); + + /* calculate mclk -> iclk divider */ + if (freq <= 13000000) + snd_soc_write(codec, MAX9850_CLOCK, 0x0); + else if (freq <= 26000000) + snd_soc_write(codec, MAX9850_CLOCK, 0x4); + else if (freq <= 40000000) + snd_soc_write(codec, MAX9850_CLOCK, 0x8); + else + return -EINVAL; + + max9850->sysclk = freq; + return 0; +} + +static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u8 da = 0; + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + da |= MAX9850_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + da |= MAX9850_DLY; + break; + case SND_SOC_DAIFMT_RIGHT_J: + da |= MAX9850_RTJ; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + default: + return -EINVAL; + } + + /* clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + da |= MAX9850_BCINV | MAX9850_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + da |= MAX9850_BCINV; + break; + case SND_SOC_DAIFMT_NB_IF: + da |= MAX9850_INV; + break; + default: + return -EINVAL; + } + + /* set da */ + snd_soc_write(codec, MAX9850_DIGITAL_AUDIO, da); + + return 0; +} + +static int max9850_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = snd_soc_cache_sync(codec); + if (ret) { + dev_err(codec->dev, + "Failed to sync cache: %d\n", ret); + return ret; + } + } + break; + case SND_SOC_BIAS_OFF: + break; + } + codec->dapm.bias_level = level; + return 0; +} + +#define MAX9850_RATES SNDRV_PCM_RATE_8000_48000 + +#define MAX9850_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops max9850_dai_ops = { + .hw_params = max9850_hw_params, + .set_sysclk = max9850_set_dai_sysclk, + .set_fmt = max9850_set_dai_fmt, +}; + +static struct snd_soc_dai_driver max9850_dai = { + .name = "max9850-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX9850_RATES, + .formats = MAX9850_FORMATS + }, + .ops = &max9850_dai_ops, +}; + +#ifdef CONFIG_PM +static int max9850_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + max9850_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int max9850_resume(struct snd_soc_codec *codec) +{ + max9850_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + return 0; +} +#else +#define max9850_suspend NULL +#define max9850_resume NULL +#endif + +static int max9850_probe(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; + + ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + /* enable zero-detect */ + snd_soc_update_bits(codec, MAX9850_GENERAL_PURPOSE, 1, 1); + /* enable slew-rate control */ + snd_soc_update_bits(codec, MAX9850_VOLUME, 0x40, 0x40); + /* set slew-rate 125ms */ + snd_soc_update_bits(codec, MAX9850_CHARGE_PUMP, 0xff, 0xc0); + + snd_soc_dapm_new_controls(dapm, max9850_dapm_widgets, + ARRAY_SIZE(max9850_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); + + snd_soc_add_controls(codec, max9850_controls, + ARRAY_SIZE(max9850_controls)); + + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_max9850 = { + .probe = max9850_probe, + .suspend = max9850_suspend, + .resume = max9850_resume, + .set_bias_level = max9850_set_bias_level, + .reg_cache_size = ARRAY_SIZE(max9850_reg), + .reg_word_size = sizeof(u8), + .reg_cache_default = max9850_reg, + .volatile_register = max9850_volatile_register, +}; + +static int __devinit max9850_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max9850_priv *max9850; + int ret; + + max9850 = kzalloc(sizeof(struct max9850_priv), GFP_KERNEL); + if (max9850 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, max9850); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_max9850, &max9850_dai, 1); + if (ret < 0) + kfree(max9850); + return ret; +} + +static __devexit int max9850_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 max9850_i2c_id[] = { + { "max9850", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max9850_i2c_id); + +static struct i2c_driver max9850_i2c_driver = { + .driver = { + .name = "max9850", + .owner = THIS_MODULE, + }, + .probe = max9850_i2c_probe, + .remove = __devexit_p(max9850_i2c_remove), + .id_table = max9850_i2c_id, +}; + +static int __init max9850_init(void) +{ + return i2c_add_driver(&max9850_i2c_driver); +} +module_init(max9850_init); + +static void __exit max9850_exit(void) +{ + i2c_del_driver(&max9850_i2c_driver); +} +module_exit(max9850_exit); + +MODULE_AUTHOR("Christian Glindkamp <christian.glindkamp@taskit.de>"); +MODULE_DESCRIPTION("ASoC MAX9850 codec driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max9850.h b/sound/soc/codecs/max9850.h new file mode 100644 index 00000000000..72b1ddb04b0 --- /dev/null +++ b/sound/soc/codecs/max9850.h @@ -0,0 +1,38 @@ +/* + * max9850.h -- codec driver for max9850 + * + * Copyright (C) 2011 taskit GmbH + * Author: Christian Glindkamp <christian.glindkamp@taskit.de> + * + * 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. + * + */ + +#ifndef _MAX9850_H +#define _MAX9850_H + +#define MAX9850_STATUSA 0x00 +#define MAX9850_STATUSB 0x01 +#define MAX9850_VOLUME 0x02 +#define MAX9850_GENERAL_PURPOSE 0x03 +#define MAX9850_INTERRUPT 0x04 +#define MAX9850_ENABLE 0x05 +#define MAX9850_CLOCK 0x06 +#define MAX9850_CHARGE_PUMP 0x07 +#define MAX9850_LRCLK_MSB 0x08 +#define MAX9850_LRCLK_LSB 0x09 +#define MAX9850_DIGITAL_AUDIO 0x0a + +#define MAX9850_CACHEREGNUM 11 + +/* MAX9850_DIGITAL_AUDIO */ +#define MAX9850_MASTER (1<<7) +#define MAX9850_INV (1<<6) +#define MAX9850_BCINV (1<<5) +#define MAX9850_DLY (1<<3) +#define MAX9850_RTJ (1<<2) + +#endif diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c new file mode 100644 index 00000000000..ff29380c9ed --- /dev/null +++ b/sound/soc/codecs/sgtl5000.c @@ -0,0 +1,1527 @@ +/* + * sgtl5000.c -- SGTL5000 ALSA SoC Audio driver + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/consumer.h> +#include <sound/core.h> +#include <sound/tlv.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> + +#include "sgtl5000.h" + +#define SGTL5000_DAP_REG_OFFSET 0x0100 +#define SGTL5000_MAX_REG_OFFSET 0x013A + +/* default value of sgtl5000 registers except DAP */ +static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET >> 1] = { + 0xa011, /* 0x0000, CHIP_ID. 11 stand for revison 17 */ + 0x0000, /* 0x0002, CHIP_DIG_POWER. */ + 0x0008, /* 0x0004, CHIP_CKL_CTRL */ + 0x0010, /* 0x0006, CHIP_I2S_CTRL */ + 0x0000, /* 0x0008, reserved */ + 0x0008, /* 0x000A, CHIP_SSS_CTRL */ + 0x0000, /* 0x000C, reserved */ + 0x020c, /* 0x000E, CHIP_ADCDAC_CTRL */ + 0x3c3c, /* 0x0010, CHIP_DAC_VOL */ + 0x0000, /* 0x0012, reserved */ + 0x015f, /* 0x0014, CHIP_PAD_STRENGTH */ + 0x0000, /* 0x0016, reserved */ + 0x0000, /* 0x0018, reserved */ + 0x0000, /* 0x001A, reserved */ + 0x0000, /* 0x001E, reserved */ + 0x0000, /* 0x0020, CHIP_ANA_ADC_CTRL */ + 0x1818, /* 0x0022, CHIP_ANA_HP_CTRL */ + 0x0111, /* 0x0024, CHIP_ANN_CTRL */ + 0x0000, /* 0x0026, CHIP_LINREG_CTRL */ + 0x0000, /* 0x0028, CHIP_REF_CTRL */ + 0x0000, /* 0x002A, CHIP_MIC_CTRL */ + 0x0000, /* 0x002C, CHIP_LINE_OUT_CTRL */ + 0x0404, /* 0x002E, CHIP_LINE_OUT_VOL */ + 0x7060, /* 0x0030, CHIP_ANA_POWER */ + 0x5000, /* 0x0032, CHIP_PLL_CTRL */ + 0x0000, /* 0x0034, CHIP_CLK_TOP_CTRL */ + 0x0000, /* 0x0036, CHIP_ANA_STATUS */ + 0x0000, /* 0x0038, reserved */ + 0x0000, /* 0x003A, CHIP_ANA_TEST2 */ + 0x0000, /* 0x003C, CHIP_SHORT_CTRL */ + 0x0000, /* reserved */ +}; + +/* default value of dap registers */ +static const u16 sgtl5000_dap_regs[] = { + 0x0000, /* 0x0100, DAP_CONTROL */ + 0x0000, /* 0x0102, DAP_PEQ */ + 0x0040, /* 0x0104, DAP_BASS_ENHANCE */ + 0x051f, /* 0x0106, DAP_BASS_ENHANCE_CTRL */ + 0x0000, /* 0x0108, DAP_AUDIO_EQ */ + 0x0040, /* 0x010A, DAP_SGTL_SURROUND */ + 0x0000, /* 0x010C, DAP_FILTER_COEF_ACCESS */ + 0x0000, /* 0x010E, DAP_COEF_WR_B0_MSB */ + 0x0000, /* 0x0110, DAP_COEF_WR_B0_LSB */ + 0x0000, /* 0x0112, reserved */ + 0x0000, /* 0x0114, reserved */ + 0x002f, /* 0x0116, DAP_AUDIO_EQ_BASS_BAND0 */ + 0x002f, /* 0x0118, DAP_AUDIO_EQ_BAND0 */ + 0x002f, /* 0x011A, DAP_AUDIO_EQ_BAND2 */ + 0x002f, /* 0x011C, DAP_AUDIO_EQ_BAND3 */ + 0x002f, /* 0x011E, DAP_AUDIO_EQ_TREBLE_BAND4 */ + 0x8000, /* 0x0120, DAP_MAIN_CHAN */ + 0x0000, /* 0x0122, DAP_MIX_CHAN */ + 0x0510, /* 0x0124, DAP_AVC_CTRL */ + 0x1473, /* 0x0126, DAP_AVC_THRESHOLD */ + 0x0028, /* 0x0128, DAP_AVC_ATTACK */ + 0x0050, /* 0x012A, DAP_AVC_DECAY */ + 0x0000, /* 0x012C, DAP_COEF_WR_B1_MSB */ + 0x0000, /* 0x012E, DAP_COEF_WR_B1_LSB */ + 0x0000, /* 0x0130, DAP_COEF_WR_B2_MSB */ + 0x0000, /* 0x0132, DAP_COEF_WR_B2_LSB */ + 0x0000, /* 0x0134, DAP_COEF_WR_A1_MSB */ + 0x0000, /* 0x0136, DAP_COEF_WR_A1_LSB */ + 0x0000, /* 0x0138, DAP_COEF_WR_A2_MSB */ + 0x0000, /* 0x013A, DAP_COEF_WR_A2_LSB */ +}; + +/* regulator supplies for sgtl5000, VDDD is an optional external supply */ +enum sgtl5000_regulator_supplies { + VDDA, + VDDIO, + VDDD, + SGTL5000_SUPPLY_NUM +}; + +/* vddd is optional supply */ +static const char *supply_names[SGTL5000_SUPPLY_NUM] = { + "VDDA", + "VDDIO", + "VDDD" +}; + +#define LDO_CONSUMER_NAME "VDDD_LDO" +#define LDO_VOLTAGE 1200000 + +static struct regulator_consumer_supply ldo_consumer[] = { + REGULATOR_SUPPLY(LDO_CONSUMER_NAME, NULL), +}; + +static struct regulator_init_data ldo_init_data = { + .constraints = { + .min_uV = 850000, + .max_uV = 1600000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &ldo_consumer[0], +}; + +/* + * sgtl5000 internal ldo regulator, + * enabled when VDDD not provided + */ +struct ldo_regulator { + struct regulator_desc desc; + struct regulator_dev *dev; + int voltage; + void *codec_data; + bool enabled; +}; + +/* sgtl5000 private structure in codec */ +struct sgtl5000_priv { + int sysclk; /* sysclk rate */ + int master; /* i2s master or not */ + int fmt; /* i2s data format */ + struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM]; + struct ldo_regulator *ldo; +}; + +/* + * mic_bias power on/off share the same register bits with + * output impedance of mic bias, when power on mic bias, we + * need reclaim it to impedance value. + * 0x0 = Powered off + * 0x1 = 2Kohm + * 0x2 = 4Kohm + * 0x3 = 8Kohm + */ +static int mic_bias_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* change mic bias resistor to 4Kohm */ + snd_soc_update_bits(w->codec, SGTL5000_CHIP_MIC_CTRL, + SGTL5000_BIAS_R_4k, SGTL5000_BIAS_R_4k); + break; + + case SND_SOC_DAPM_PRE_PMD: + /* + * SGTL5000_BIAS_R_8k as mask to clean the two bits + * of mic bias and output impedance + */ + snd_soc_update_bits(w->codec, SGTL5000_CHIP_MIC_CTRL, + SGTL5000_BIAS_R_8k, 0); + break; + } + return 0; +} + +/* + * using codec assist to small pop, hp_powerup or lineout_powerup + * should stay setting until vag_powerup is fully ramped down, + * vag fully ramped down require 400ms. + */ +static int small_pop_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP); + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VAG_POWERUP, 0); + msleep(400); + break; + default: + break; + } + + return 0; +} + +/* input sources for ADC */ +static const char *adc_mux_text[] = { + "MIC_IN", "LINE_IN" +}; + +static const struct soc_enum adc_enum = +SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 2, 2, adc_mux_text); + +static const struct snd_kcontrol_new adc_mux = +SOC_DAPM_ENUM("Capture Mux", adc_enum); + +/* input sources for DAC */ +static const char *dac_mux_text[] = { + "DAC", "LINE_IN" +}; + +static const struct soc_enum dac_enum = +SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 6, 2, dac_mux_text); + +static const struct snd_kcontrol_new dac_mux = +SOC_DAPM_ENUM("Headphone Mux", dac_enum); + +static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("LINE_IN"), + SND_SOC_DAPM_INPUT("MIC_IN"), + + SND_SOC_DAPM_OUTPUT("HP_OUT"), + SND_SOC_DAPM_OUTPUT("LINE_OUT"), + + SND_SOC_DAPM_MICBIAS_E("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0, + mic_bias_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_PGA_E("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0, + small_pop_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("LO", SGTL5000_CHIP_ANA_POWER, 0, 0, NULL, 0, + small_pop_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0, &adc_mux), + SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &dac_mux), + + /* aif for i2s input */ + SND_SOC_DAPM_AIF_IN("AIFIN", "Playback", + 0, SGTL5000_CHIP_DIG_POWER, + 0, 0), + + /* aif for i2s output */ + SND_SOC_DAPM_AIF_OUT("AIFOUT", "Capture", + 0, SGTL5000_CHIP_DIG_POWER, + 1, 0), + + SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0), + + SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0), +}; + +/* routes for sgtl5000 */ +static const struct snd_soc_dapm_route audio_map[] = { + {"Capture Mux", "LINE_IN", "LINE_IN"}, /* line_in --> adc_mux */ + {"Capture Mux", "MIC_IN", "MIC_IN"}, /* mic_in --> adc_mux */ + + {"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */ + {"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */ + + {"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */ + {"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */ + {"LO", NULL, "DAC"}, /* dac --> line_out */ + + {"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */ + {"HP", NULL, "Headphone Mux"}, /* hp_mux --> hp */ + + {"LINE_OUT", NULL, "LO"}, + {"HP_OUT", NULL, "HP"}, +}; + +/* custom function to fetch info of PCM playback volume */ +static int dac_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xfc - 0x3c; + return 0; +} + +/* + * custom function to get of PCM playback volume + * + * dac volume register + * 15-------------8-7--------------0 + * | R channel vol | L channel vol | + * ------------------------------- + * + * PCM volume with 0.5017 dB steps from 0 to -90 dB + * + * register values map to dB + * 0x3B and less = Reserved + * 0x3C = 0 dB + * 0x3D = -0.5 dB + * 0xF0 = -90 dB + * 0xFC and greater = Muted + * + * register value map to userspace value + * + * register value 0x3c(0dB) 0xf0(-90dB)0xfc + * ------------------------------ + * userspace value 0xc0 0 + */ +static int dac_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg; + int l; + int r; + + reg = snd_soc_read(codec, SGTL5000_CHIP_DAC_VOL); + + /* get left channel volume */ + l = (reg & SGTL5000_DAC_VOL_LEFT_MASK) >> SGTL5000_DAC_VOL_LEFT_SHIFT; + + /* get right channel volume */ + r = (reg & SGTL5000_DAC_VOL_RIGHT_MASK) >> SGTL5000_DAC_VOL_RIGHT_SHIFT; + + /* make sure value fall in (0x3c,0xfc) */ + l = clamp(l, 0x3c, 0xfc); + r = clamp(r, 0x3c, 0xfc); + + /* invert it and map to userspace value */ + l = 0xfc - l; + r = 0xfc - r; + + ucontrol->value.integer.value[0] = l; + ucontrol->value.integer.value[1] = r; + + return 0; +} + +/* + * custom function to put of PCM playback volume + * + * dac volume register + * 15-------------8-7--------------0 + * | R channel vol | L channel vol | + * ------------------------------- + * + * PCM volume with 0.5017 dB steps from 0 to -90 dB + * + * register values map to dB + * 0x3B and less = Reserved + * 0x3C = 0 dB + * 0x3D = -0.5 dB + * 0xF0 = -90 dB + * 0xFC and greater = Muted + * + * userspace value map to register value + * + * userspace value 0xc0 0 + * ------------------------------ + * register value 0x3c(0dB) 0xf0(-90dB)0xfc + */ +static int dac_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg; + int l; + int r; + + l = ucontrol->value.integer.value[0]; + r = ucontrol->value.integer.value[1]; + + /* make sure userspace volume fall in (0, 0xfc-0x3c) */ + l = clamp(l, 0, 0xfc - 0x3c); + r = clamp(r, 0, 0xfc - 0x3c); + + /* invert it, get the value can be set to register */ + l = 0xfc - l; + r = 0xfc - r; + + /* shift to get the register value */ + reg = l << SGTL5000_DAC_VOL_LEFT_SHIFT | + r << SGTL5000_DAC_VOL_RIGHT_SHIFT; + + snd_soc_write(codec, SGTL5000_CHIP_DAC_VOL, reg); + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0); + +/* tlv for mic gain, 0db 20db 30db 40db */ +static const unsigned int mic_gain_tlv[] = { + TLV_DB_RANGE_HEAD(4), + 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0), + 1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0), +}; + +/* tlv for hp volume, -51.5db to 12.0db, step .5db */ +static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0); + +static const struct snd_kcontrol_new sgtl5000_snd_controls[] = { + /* SOC_DOUBLE_S8_TLV with invert */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = dac_info_volsw, + .get = dac_get_volsw, + .put = dac_put_volsw, + }, + + SOC_DOUBLE("Capture Volume", SGTL5000_CHIP_ANA_ADC_CTRL, 0, 4, 0xf, 0), + SOC_SINGLE_TLV("Capture Attenuate Switch (-6dB)", + SGTL5000_CHIP_ANA_ADC_CTRL, + 8, 2, 0, capture_6db_attenuate), + SOC_SINGLE("Capture ZC Switch", SGTL5000_CHIP_ANA_CTRL, 1, 1, 0), + + SOC_DOUBLE_TLV("Headphone Playback Volume", + SGTL5000_CHIP_ANA_HP_CTRL, + 0, 8, + 0x7f, 1, + headphone_volume), + SOC_SINGLE("Headphone Playback ZC Switch", SGTL5000_CHIP_ANA_CTRL, + 5, 1, 0), + + SOC_SINGLE_TLV("Mic Volume", SGTL5000_CHIP_MIC_CTRL, + 0, 4, 0, mic_gain_tlv), +}; + +/* mute the codec used by alsa core */ +static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 adcdac_ctrl = SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT; + + snd_soc_update_bits(codec, SGTL5000_CHIP_ADCDAC_CTRL, + adcdac_ctrl, mute ? adcdac_ctrl : 0); + + return 0; +} + +/* set codec format */ +static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + u16 i2sctl = 0; + + sgtl5000->master = 0; + /* + * i2s clock and frame master setting. + * ONLY support: + * - clock and frame slave, + * - clock and frame master + */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + i2sctl |= SGTL5000_I2S_MASTER; + sgtl5000->master = 1; + break; + default: + return -EINVAL; + } + + /* setting i2s data format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + i2sctl |= SGTL5000_I2S_MODE_PCM; + break; + case SND_SOC_DAIFMT_DSP_B: + i2sctl |= SGTL5000_I2S_MODE_PCM; + i2sctl |= SGTL5000_I2S_LRALIGN; + break; + case SND_SOC_DAIFMT_I2S: + i2sctl |= SGTL5000_I2S_MODE_I2S_LJ; + break; + case SND_SOC_DAIFMT_RIGHT_J: + i2sctl |= SGTL5000_I2S_MODE_RJ; + i2sctl |= SGTL5000_I2S_LRPOL; + break; + case SND_SOC_DAIFMT_LEFT_J: + i2sctl |= SGTL5000_I2S_MODE_I2S_LJ; + i2sctl |= SGTL5000_I2S_LRALIGN; + break; + default: + return -EINVAL; + } + + sgtl5000->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + /* Clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + i2sctl |= SGTL5000_I2S_SCLK_INV; + break; + default: + return -EINVAL; + } + + snd_soc_write(codec, SGTL5000_CHIP_I2S_CTRL, i2sctl); + + return 0; +} + +/* set codec sysclk */ +static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case SGTL5000_SYSCLK: + sgtl5000->sysclk = freq; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * set clock according to i2s frame clock, + * sgtl5000 provide 2 clock sources. + * 1. sys_mclk. sample freq can only configure to + * 1/256, 1/384, 1/512 of sys_mclk. + * 2. pll. can derive any audio clocks. + * + * clock setting rules: + * 1. in slave mode, only sys_mclk can use. + * 2. as constraint by sys_mclk, sample freq should + * set to 32k, 44.1k and above. + * 3. using sys_mclk prefer to pll to save power. + */ +static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate) +{ + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + int clk_ctl = 0; + int sys_fs; /* sample freq */ + + /* + * sample freq should be divided by frame clock, + * if frame clock lower than 44.1khz, sample feq should set to + * 32khz or 44.1khz. + */ + switch (frame_rate) { + case 8000: + case 16000: + sys_fs = 32000; + break; + case 11025: + case 22050: + sys_fs = 44100; + break; + default: + sys_fs = frame_rate; + break; + } + + /* set divided factor of frame clock */ + switch (sys_fs / frame_rate) { + case 4: + clk_ctl |= SGTL5000_RATE_MODE_DIV_4 << SGTL5000_RATE_MODE_SHIFT; + break; + case 2: + clk_ctl |= SGTL5000_RATE_MODE_DIV_2 << SGTL5000_RATE_MODE_SHIFT; + break; + case 1: + clk_ctl |= SGTL5000_RATE_MODE_DIV_1 << SGTL5000_RATE_MODE_SHIFT; + break; + default: + return -EINVAL; + } + + /* set the sys_fs according to frame rate */ + switch (sys_fs) { + case 32000: + clk_ctl |= SGTL5000_SYS_FS_32k << SGTL5000_SYS_FS_SHIFT; + break; + case 44100: + clk_ctl |= SGTL5000_SYS_FS_44_1k << SGTL5000_SYS_FS_SHIFT; + break; + case 48000: + clk_ctl |= SGTL5000_SYS_FS_48k << SGTL5000_SYS_FS_SHIFT; + break; + case 96000: + clk_ctl |= SGTL5000_SYS_FS_96k << SGTL5000_SYS_FS_SHIFT; + break; + default: + dev_err(codec->dev, "frame rate %d not supported\n", + frame_rate); + return -EINVAL; + } + + /* + * calculate the divider of mclk/sample_freq, + * factor of freq =96k can only be 256, since mclk in range (12m,27m) + */ + switch (sgtl5000->sysclk / sys_fs) { + case 256: + clk_ctl |= SGTL5000_MCLK_FREQ_256FS << + SGTL5000_MCLK_FREQ_SHIFT; + break; + case 384: + clk_ctl |= SGTL5000_MCLK_FREQ_384FS << + SGTL5000_MCLK_FREQ_SHIFT; + break; + case 512: + clk_ctl |= SGTL5000_MCLK_FREQ_512FS << + SGTL5000_MCLK_FREQ_SHIFT; + break; + default: + /* if mclk not satisify the divider, use pll */ + if (sgtl5000->master) { + clk_ctl |= SGTL5000_MCLK_FREQ_PLL << + SGTL5000_MCLK_FREQ_SHIFT; + } else { + dev_err(codec->dev, + "PLL not supported in slave mode\n"); + return -EINVAL; + } + } + + /* if using pll, please check manual 6.4.2 for detail */ + if ((clk_ctl & SGTL5000_MCLK_FREQ_MASK) == SGTL5000_MCLK_FREQ_PLL) { + u64 out, t; + int div2; + int pll_ctl; + unsigned int in, int_div, frac_div; + + if (sgtl5000->sysclk > 17000000) { + div2 = 1; + in = sgtl5000->sysclk / 2; + } else { + div2 = 0; + in = sgtl5000->sysclk; + } + if (sys_fs == 44100) + out = 180633600; + else + out = 196608000; + t = do_div(out, in); + int_div = out; + t *= 2048; + do_div(t, in); + frac_div = t; + pll_ctl = int_div << SGTL5000_PLL_INT_DIV_SHIFT | + frac_div << SGTL5000_PLL_FRAC_DIV_SHIFT; + + snd_soc_write(codec, SGTL5000_CHIP_PLL_CTRL, pll_ctl); + if (div2) + snd_soc_update_bits(codec, + SGTL5000_CHIP_CLK_TOP_CTRL, + SGTL5000_INPUT_FREQ_DIV2, + SGTL5000_INPUT_FREQ_DIV2); + else + snd_soc_update_bits(codec, + SGTL5000_CHIP_CLK_TOP_CTRL, + SGTL5000_INPUT_FREQ_DIV2, + 0); + + /* power up pll */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, + SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP); + } else { + /* power down pll */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP, + 0); + } + + /* if using pll, clk_ctrl must be set after pll power up */ + snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl); + + return 0; +} + +/* + * Set PCM DAI bit size and sample rate. + * input: params_rate, params_fmt + */ +static int sgtl5000_pcm_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; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + int channels = params_channels(params); + int i2s_ctl = 0; + int stereo; + int ret; + + /* sysclk should already set */ + if (!sgtl5000->sysclk) { + dev_err(codec->dev, "%s: set sysclk first!\n", __func__); + return -EFAULT; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + stereo = SGTL5000_DAC_STEREO; + else + stereo = SGTL5000_ADC_STEREO; + + /* set mono to save power */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, stereo, + channels == 1 ? 0 : stereo); + + /* set codec clock base on lrclk */ + ret = sgtl5000_set_clock(codec, params_rate(params)); + if (ret) + return ret; + + /* set i2s data format */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J) + return -EINVAL; + i2s_ctl |= SGTL5000_I2S_DLEN_16 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_32FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + case SNDRV_PCM_FORMAT_S20_3LE: + i2s_ctl |= SGTL5000_I2S_DLEN_20 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + case SNDRV_PCM_FORMAT_S24_LE: + i2s_ctl |= SGTL5000_I2S_DLEN_24 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + case SNDRV_PCM_FORMAT_S32_LE: + if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J) + return -EINVAL; + i2s_ctl |= SGTL5000_I2S_DLEN_32 << SGTL5000_I2S_DLEN_SHIFT; + i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS << + SGTL5000_I2S_SCLKFREQ_SHIFT; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, SGTL5000_CHIP_I2S_CTRL, i2s_ctl, i2s_ctl); + + return 0; +} + +#ifdef CONFIG_REGULATOR +static int ldo_regulator_is_enabled(struct regulator_dev *dev) +{ + struct ldo_regulator *ldo = rdev_get_drvdata(dev); + + return ldo->enabled; +} + +static int ldo_regulator_enable(struct regulator_dev *dev) +{ + struct ldo_regulator *ldo = rdev_get_drvdata(dev); + struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data; + int reg; + + if (ldo_regulator_is_enabled(dev)) + return 0; + + /* set regulator value firstly */ + reg = (1600 - ldo->voltage / 1000) / 50; + reg = clamp(reg, 0x0, 0xf); + + /* amend the voltage value, unit: uV */ + ldo->voltage = (1600 - reg * 50) * 1000; + + /* set voltage to register */ + snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL, + (0x1 << 4) - 1, reg); + + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINEREG_D_POWERUP, + SGTL5000_LINEREG_D_POWERUP); + + /* when internal ldo enabled, simple digital power can be disabled */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINREG_SIMPLE_POWERUP, + 0); + + ldo->enabled = 1; + return 0; +} + +static int ldo_regulator_disable(struct regulator_dev *dev) +{ + struct ldo_regulator *ldo = rdev_get_drvdata(dev); + struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data; + + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINEREG_D_POWERUP, + 0); + + /* clear voltage info */ + snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL, + (0x1 << 4) - 1, 0); + + ldo->enabled = 0; + + return 0; +} + +static int ldo_regulator_get_voltage(struct regulator_dev *dev) +{ + struct ldo_regulator *ldo = rdev_get_drvdata(dev); + + return ldo->voltage; +} + +static struct regulator_ops ldo_regulator_ops = { + .is_enabled = ldo_regulator_is_enabled, + .enable = ldo_regulator_enable, + .disable = ldo_regulator_disable, + .get_voltage = ldo_regulator_get_voltage, +}; + +static int ldo_regulator_register(struct snd_soc_codec *codec, + struct regulator_init_data *init_data, + int voltage) +{ + struct ldo_regulator *ldo; + + ldo = kzalloc(sizeof(struct ldo_regulator), GFP_KERNEL); + + if (!ldo) { + dev_err(codec->dev, "failed to allocate ldo_regulator\n"); + return -ENOMEM; + } + + ldo->desc.name = kstrdup(dev_name(codec->dev), GFP_KERNEL); + if (!ldo->desc.name) { + kfree(ldo); + dev_err(codec->dev, "failed to allocate decs name memory\n"); + return -ENOMEM; + } + + ldo->desc.type = REGULATOR_VOLTAGE; + ldo->desc.owner = THIS_MODULE; + ldo->desc.ops = &ldo_regulator_ops; + ldo->desc.n_voltages = 1; + + ldo->codec_data = codec; + ldo->voltage = voltage; + + ldo->dev = regulator_register(&ldo->desc, codec->dev, + init_data, ldo); + if (IS_ERR(ldo->dev)) { + int ret = PTR_ERR(ldo->dev); + + dev_err(codec->dev, "failed to register regulator\n"); + kfree(ldo->desc.name); + kfree(ldo); + + return ret; + } + + return 0; +} + +static int ldo_regulator_remove(struct snd_soc_codec *codec) +{ + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + struct ldo_regulator *ldo = sgtl5000->ldo; + + if (!ldo) + return 0; + + regulator_unregister(ldo->dev); + kfree(ldo->desc.name); + kfree(ldo); + + return 0; +} +#else +static int ldo_regulator_register(struct snd_soc_codec *codec, + struct regulator_init_data *init_data, + int voltage) +{ + return -EINVAL; +} + +static int ldo_regulator_remove(struct snd_soc_codec *codec) +{ + return 0; +} +#endif + +/* + * set dac bias + * common state changes: + * startup: + * off --> standby --> prepare --> on + * standby --> prepare --> on + * + * stop: + * on --> prepare --> standby + */ +static int sgtl5000_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + int ret; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = regulator_bulk_enable( + ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (ret) + return ret; + udelay(10); + } + + break; + case SND_SOC_BIAS_OFF: + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + break; + } + + codec->dapm.bias_level = level; + return 0; +} + +#define SGTL5000_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops sgtl5000_ops = { + .hw_params = sgtl5000_pcm_hw_params, + .digital_mute = sgtl5000_digital_mute, + .set_fmt = sgtl5000_set_dai_fmt, + .set_sysclk = sgtl5000_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver sgtl5000_dai = { + .name = "sgtl5000", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + /* + * only support 8~48K + 96K, + * TODO modify hw_param to support more + */ + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000, + .formats = SGTL5000_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000, + .formats = SGTL5000_FORMATS, + }, + .ops = &sgtl5000_ops, + .symmetric_rates = 1, +}; + +static int sgtl5000_volatile_register(struct snd_soc_codec *codec, + unsigned int reg) +{ + switch (reg) { + case SGTL5000_CHIP_ID: + case SGTL5000_CHIP_ADCDAC_CTRL: + case SGTL5000_CHIP_ANA_STATUS: + return 1; + } + + return 0; +} + +#ifdef CONFIG_SUSPEND +static int sgtl5000_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +/* + * restore all sgtl5000 registers, + * since a big hole between dap and regular registers, + * we will restore them respectively. + */ +static int sgtl5000_restore_regs(struct snd_soc_codec *codec) +{ + u16 *cache = codec->reg_cache; + int i; + int regular_regs = SGTL5000_CHIP_SHORT_CTRL >> 1; + + /* restore regular registers */ + for (i = 0; i < regular_regs; i++) { + int reg = i << 1; + + /* this regs depends on the others */ + if (reg == SGTL5000_CHIP_ANA_POWER || + reg == SGTL5000_CHIP_CLK_CTRL || + reg == SGTL5000_CHIP_LINREG_CTRL || + reg == SGTL5000_CHIP_LINE_OUT_CTRL || + reg == SGTL5000_CHIP_CLK_CTRL) + continue; + + snd_soc_write(codec, reg, cache[i]); + } + + /* restore dap registers */ + for (i = SGTL5000_DAP_REG_OFFSET >> 1; + i < SGTL5000_MAX_REG_OFFSET >> 1; i++) { + int reg = i << 1; + + snd_soc_write(codec, reg, cache[i]); + } + + /* + * restore power and other regs according + * to set_power() and set_clock() + */ + snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, + cache[SGTL5000_CHIP_LINREG_CTRL >> 1]); + + snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, + cache[SGTL5000_CHIP_ANA_POWER >> 1]); + + snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, + cache[SGTL5000_CHIP_CLK_CTRL >> 1]); + + snd_soc_write(codec, SGTL5000_CHIP_REF_CTRL, + cache[SGTL5000_CHIP_REF_CTRL >> 1]); + + snd_soc_write(codec, SGTL5000_CHIP_LINE_OUT_CTRL, + cache[SGTL5000_CHIP_LINE_OUT_CTRL >> 1]); + return 0; +} + +static int sgtl5000_resume(struct snd_soc_codec *codec) +{ + /* Bring the codec back up to standby to enable regulators */ + sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + /* Restore registers by cached in memory */ + sgtl5000_restore_regs(codec); + return 0; +} +#else +#define sgtl5000_suspend NULL +#define sgtl5000_resume NULL +#endif /* CONFIG_SUSPEND */ + +/* + * sgtl5000 has 3 internal power supplies: + * 1. VAG, normally set to vdda/2 + * 2. chargepump, set to different value + * according to voltage of vdda and vddio + * 3. line out VAG, normally set to vddio/2 + * + * and should be set according to: + * 1. vddd provided by external or not + * 2. vdda and vddio voltage value. > 3.1v or not + * 3. chip revision >=0x11 or not. If >=0x11, not use external vddd. + */ +static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) +{ + int vddd; + int vdda; + int vddio; + u16 ana_pwr; + u16 lreg_ctrl; + int vag; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + vdda = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer); + vddio = regulator_get_voltage(sgtl5000->supplies[VDDIO].consumer); + vddd = regulator_get_voltage(sgtl5000->supplies[VDDD].consumer); + + vdda = vdda / 1000; + vddio = vddio / 1000; + vddd = vddd / 1000; + + if (vdda <= 0 || vddio <= 0 || vddd < 0) { + dev_err(codec->dev, "regulator voltage not set correctly\n"); + + return -EINVAL; + } + + /* according to datasheet, maximum voltage of supplies */ + if (vdda > 3600 || vddio > 3600 || vddd > 1980) { + dev_err(codec->dev, + "exceed max voltage vdda %dmv vddio %dma vddd %dma\n", + vdda, vddio, vddd); + + return -EINVAL; + } + + /* reset value */ + ana_pwr = snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER); + ana_pwr |= SGTL5000_DAC_STEREO | + SGTL5000_ADC_STEREO | + SGTL5000_REFTOP_POWERUP; + lreg_ctrl = snd_soc_read(codec, SGTL5000_CHIP_LINREG_CTRL); + + if (vddio < 3100 && vdda < 3100) { + /* enable internal oscillator used for charge pump */ + snd_soc_update_bits(codec, SGTL5000_CHIP_CLK_TOP_CTRL, + SGTL5000_INT_OSC_EN, + SGTL5000_INT_OSC_EN); + /* Enable VDDC charge pump */ + ana_pwr |= SGTL5000_VDDC_CHRGPMP_POWERUP; + } else if (vddio >= 3100 && vdda >= 3100) { + /* + * if vddio and vddd > 3.1v, + * charge pump should be clean before set ana_pwr + */ + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_VDDC_CHRGPMP_POWERUP, 0); + + /* VDDC use VDDIO rail */ + lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD; + lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO << + SGTL5000_VDDC_MAN_ASSN_SHIFT; + } + + snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl); + + snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr); + + /* set voltage to register */ + snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL, + (0x1 << 4) - 1, 0x8); + + /* + * if vddd linear reg has been enabled, + * simple digital supply should be clear to get + * proper VDDD voltage. + */ + if (ana_pwr & SGTL5000_LINEREG_D_POWERUP) + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINREG_SIMPLE_POWERUP, + 0); + else + snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, + SGTL5000_LINREG_SIMPLE_POWERUP | + SGTL5000_STARTUP_POWERUP, + 0); + + /* + * set ADC/DAC VAG to vdda / 2, + * should stay in range (0.8v, 1.575v) + */ + vag = vdda / 2; + if (vag <= SGTL5000_ANA_GND_BASE) + vag = 0; + else if (vag >= SGTL5000_ANA_GND_BASE + SGTL5000_ANA_GND_STP * + (SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT)) + vag = SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT; + else + vag = (vag - SGTL5000_ANA_GND_BASE) / SGTL5000_ANA_GND_STP; + + snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL, + vag << SGTL5000_ANA_GND_SHIFT, + vag << SGTL5000_ANA_GND_SHIFT); + + /* set line out VAG to vddio / 2, in range (0.8v, 1.675v) */ + vag = vddio / 2; + if (vag <= SGTL5000_LINE_OUT_GND_BASE) + vag = 0; + else if (vag >= SGTL5000_LINE_OUT_GND_BASE + + SGTL5000_LINE_OUT_GND_STP * SGTL5000_LINE_OUT_GND_MAX) + vag = SGTL5000_LINE_OUT_GND_MAX; + else + vag = (vag - SGTL5000_LINE_OUT_GND_BASE) / + SGTL5000_LINE_OUT_GND_STP; + + snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_CTRL, + vag << SGTL5000_LINE_OUT_GND_SHIFT | + SGTL5000_LINE_OUT_CURRENT_360u << + SGTL5000_LINE_OUT_CURRENT_SHIFT, + vag << SGTL5000_LINE_OUT_GND_SHIFT | + SGTL5000_LINE_OUT_CURRENT_360u << + SGTL5000_LINE_OUT_CURRENT_SHIFT); + + return 0; +} + +static int sgtl5000_enable_regulators(struct snd_soc_codec *codec) +{ + u16 reg; + int ret; + int rev; + int i; + int external_vddd = 0; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + for (i = 0; i < ARRAY_SIZE(sgtl5000->supplies); i++) + sgtl5000->supplies[i].supply = supply_names[i]; + + ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (!ret) + external_vddd = 1; + else { + /* set internal ldo to 1.2v */ + int voltage = LDO_VOLTAGE; + + ret = ldo_regulator_register(codec, &ldo_init_data, voltage); + if (ret) { + dev_err(codec->dev, + "Failed to register vddd internal supplies: %d\n", + ret); + return ret; + } + + sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME; + + ret = regulator_bulk_get(codec->dev, + ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + + if (ret) { + ldo_regulator_remove(codec); + dev_err(codec->dev, + "Failed to request supplies: %d\n", ret); + + return ret; + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (ret) + goto err_regulator_free; + + /* wait for all power rails bring up */ + udelay(10); + + /* read chip information */ + reg = snd_soc_read(codec, SGTL5000_CHIP_ID); + if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) != + SGTL5000_PARTID_PART_ID) { + dev_err(codec->dev, + "Device with ID register %x is not a sgtl5000\n", reg); + ret = -ENODEV; + goto err_regulator_disable; + } + + rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT; + dev_info(codec->dev, "sgtl5000 revision %d\n", rev); + + /* + * workaround for revision 0x11 and later, + * roll back to use internal LDO + */ + if (external_vddd && rev >= 0x11) { + int voltage = LDO_VOLTAGE; + /* disable all regulator first */ + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + /* free VDDD regulator */ + regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + + ret = ldo_regulator_register(codec, &ldo_init_data, voltage); + if (ret) + return ret; + + sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME; + + ret = regulator_bulk_get(codec->dev, + ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (ret) { + ldo_regulator_remove(codec); + dev_err(codec->dev, + "Failed to request supplies: %d\n", ret); + + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (ret) + goto err_regulator_free; + + /* wait for all power rails bring up */ + udelay(10); + } + + return 0; + +err_regulator_disable: + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); +err_regulator_free: + regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + if (external_vddd) + ldo_regulator_remove(codec); + return ret; + +} + +static int sgtl5000_probe(struct snd_soc_codec *codec) +{ + int ret; + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + /* setup i2c data ops */ + ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } + + ret = sgtl5000_enable_regulators(codec); + if (ret) + return ret; + + /* power up sgtl5000 */ + ret = sgtl5000_set_power_regs(codec); + if (ret) + goto err; + + /* enable small pop, introduce 400ms delay in turning off */ + snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL, + SGTL5000_SMALL_POP, + SGTL5000_SMALL_POP); + + /* disable short cut detector */ + snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0); + + /* + * set i2s as default input of sound switch + * TODO: add sound switch to control and dapm widge. + */ + snd_soc_write(codec, SGTL5000_CHIP_SSS_CTRL, + SGTL5000_DAC_SEL_I2S_IN << SGTL5000_DAC_SEL_SHIFT); + snd_soc_write(codec, SGTL5000_CHIP_DIG_POWER, + SGTL5000_ADC_EN | SGTL5000_DAC_EN); + + /* enable dac volume ramp by default */ + snd_soc_write(codec, SGTL5000_CHIP_ADCDAC_CTRL, + SGTL5000_DAC_VOL_RAMP_EN | + SGTL5000_DAC_MUTE_RIGHT | + SGTL5000_DAC_MUTE_LEFT); + + snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, 0x015f); + + snd_soc_write(codec, SGTL5000_CHIP_ANA_CTRL, + SGTL5000_HP_ZCD_EN | + SGTL5000_ADC_ZCD_EN); + + snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 0); + + /* + * disable DAP + * TODO: + * Enable DAP in kcontrol and dapm. + */ + snd_soc_write(codec, SGTL5000_DAP_CTRL, 0); + + /* leading to standby state */ + ret = sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + if (ret) + goto err; + + snd_soc_add_controls(codec, sgtl5000_snd_controls, + ARRAY_SIZE(sgtl5000_snd_controls)); + + snd_soc_dapm_new_controls(&codec->dapm, sgtl5000_dapm_widgets, + ARRAY_SIZE(sgtl5000_dapm_widgets)); + + snd_soc_dapm_add_routes(&codec->dapm, audio_map, + ARRAY_SIZE(audio_map)); + + snd_soc_dapm_new_widgets(&codec->dapm); + + return 0; + +err: + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + ldo_regulator_remove(codec); + + return ret; +} + +static int sgtl5000_remove(struct snd_soc_codec *codec) +{ + struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec); + + sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF); + + regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies), + sgtl5000->supplies); + ldo_regulator_remove(codec); + + return 0; +} + +static struct snd_soc_codec_driver sgtl5000_driver = { + .probe = sgtl5000_probe, + .remove = sgtl5000_remove, + .suspend = sgtl5000_suspend, + .resume = sgtl5000_resume, + .set_bias_level = sgtl5000_set_bias_level, + .reg_cache_size = ARRAY_SIZE(sgtl5000_regs), + .reg_word_size = sizeof(u16), + .reg_cache_step = 2, + .reg_cache_default = sgtl5000_regs, + .volatile_register = sgtl5000_volatile_register, +}; + +static __devinit int sgtl5000_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sgtl5000_priv *sgtl5000; + int ret; + + sgtl5000 = kzalloc(sizeof(struct sgtl5000_priv), GFP_KERNEL); + if (!sgtl5000) + return -ENOMEM; + + /* + * copy DAP default values to default value array. + * sgtl5000 register space has a big hole, merge it + * at init phase makes life easy. + * FIXME: should we drop 'const' of sgtl5000_regs? + */ + memcpy((void *)(&sgtl5000_regs[0] + (SGTL5000_DAP_REG_OFFSET >> 1)), + sgtl5000_dap_regs, + SGTL5000_MAX_REG_OFFSET - SGTL5000_DAP_REG_OFFSET); + + i2c_set_clientdata(client, sgtl5000); + + ret = snd_soc_register_codec(&client->dev, + &sgtl5000_driver, &sgtl5000_dai, 1); + if (ret) { + dev_err(&client->dev, "Failed to register codec: %d\n", ret); + kfree(sgtl5000); + return ret; + } + + return 0; +} + +static __devexit int sgtl5000_i2c_remove(struct i2c_client *client) +{ + struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + + kfree(sgtl5000); + return 0; +} + +static const struct i2c_device_id sgtl5000_id[] = { + {"sgtl5000", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, sgtl5000_id); + +static struct i2c_driver sgtl5000_i2c_driver = { + .driver = { + .name = "sgtl5000", + .owner = THIS_MODULE, + }, + .probe = sgtl5000_i2c_probe, + .remove = __devexit_p(sgtl5000_i2c_remove), + .id_table = sgtl5000_id, +}; + +static int __init sgtl5000_modinit(void) +{ + return i2c_add_driver(&sgtl5000_i2c_driver); +} +module_init(sgtl5000_modinit); + +static void __exit sgtl5000_exit(void) +{ + i2c_del_driver(&sgtl5000_i2c_driver); +} +module_exit(sgtl5000_exit); + +MODULE_DESCRIPTION("Freescale SGTL5000 ALSA SoC Codec Driver"); +MODULE_AUTHOR("Zeng Zhaoming <zhaoming.zeng@freescale.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h new file mode 100644 index 00000000000..eec3ab368f3 --- /dev/null +++ b/sound/soc/codecs/sgtl5000.h @@ -0,0 +1,400 @@ +/* + * sgtl5000.h - SGTL5000 audio codec interface + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _SGTL5000_H +#define _SGTL5000_H + +/* + * Register values. + */ +#define SGTL5000_CHIP_ID 0x0000 +#define SGTL5000_CHIP_DIG_POWER 0x0002 +#define SGTL5000_CHIP_CLK_CTRL 0x0004 +#define SGTL5000_CHIP_I2S_CTRL 0x0006 +#define SGTL5000_CHIP_SSS_CTRL 0x000a +#define SGTL5000_CHIP_ADCDAC_CTRL 0x000e +#define SGTL5000_CHIP_DAC_VOL 0x0010 +#define SGTL5000_CHIP_PAD_STRENGTH 0x0014 +#define SGTL5000_CHIP_ANA_ADC_CTRL 0x0020 +#define SGTL5000_CHIP_ANA_HP_CTRL 0x0022 +#define SGTL5000_CHIP_ANA_CTRL 0x0024 +#define SGTL5000_CHIP_LINREG_CTRL 0x0026 +#define SGTL5000_CHIP_REF_CTRL 0x0028 +#define SGTL5000_CHIP_MIC_CTRL 0x002a +#define SGTL5000_CHIP_LINE_OUT_CTRL 0x002c +#define SGTL5000_CHIP_LINE_OUT_VOL 0x002e +#define SGTL5000_CHIP_ANA_POWER 0x0030 +#define SGTL5000_CHIP_PLL_CTRL 0x0032 +#define SGTL5000_CHIP_CLK_TOP_CTRL 0x0034 +#define SGTL5000_CHIP_ANA_STATUS 0x0036 +#define SGTL5000_CHIP_SHORT_CTRL 0x003c +#define SGTL5000_CHIP_ANA_TEST2 0x003a +#define SGTL5000_DAP_CTRL 0x0100 +#define SGTL5000_DAP_PEQ 0x0102 +#define SGTL5000_DAP_BASS_ENHANCE 0x0104 +#define SGTL5000_DAP_BASS_ENHANCE_CTRL 0x0106 +#define SGTL5000_DAP_AUDIO_EQ 0x0108 +#define SGTL5000_DAP_SURROUND 0x010a +#define SGTL5000_DAP_FLT_COEF_ACCESS 0x010c +#define SGTL5000_DAP_COEF_WR_B0_MSB 0x010e +#define SGTL5000_DAP_COEF_WR_B0_LSB 0x0110 +#define SGTL5000_DAP_EQ_BASS_BAND0 0x0116 +#define SGTL5000_DAP_EQ_BASS_BAND1 0x0118 +#define SGTL5000_DAP_EQ_BASS_BAND2 0x011a +#define SGTL5000_DAP_EQ_BASS_BAND3 0x011c +#define SGTL5000_DAP_EQ_BASS_BAND4 0x011e +#define SGTL5000_DAP_MAIN_CHAN 0x0120 +#define SGTL5000_DAP_MIX_CHAN 0x0122 +#define SGTL5000_DAP_AVC_CTRL 0x0124 +#define SGTL5000_DAP_AVC_THRESHOLD 0x0126 +#define SGTL5000_DAP_AVC_ATTACK 0x0128 +#define SGTL5000_DAP_AVC_DECAY 0x012a +#define SGTL5000_DAP_COEF_WR_B1_MSB 0x012c +#define SGTL5000_DAP_COEF_WR_B1_LSB 0x012e +#define SGTL5000_DAP_COEF_WR_B2_MSB 0x0130 +#define SGTL5000_DAP_COEF_WR_B2_LSB 0x0132 +#define SGTL5000_DAP_COEF_WR_A1_MSB 0x0134 +#define SGTL5000_DAP_COEF_WR_A1_LSB 0x0136 +#define SGTL5000_DAP_COEF_WR_A2_MSB 0x0138 +#define SGTL5000_DAP_COEF_WR_A2_LSB 0x013a + +/* + * Field Definitions. + */ + +/* + * SGTL5000_CHIP_ID + */ +#define SGTL5000_PARTID_MASK 0xff00 +#define SGTL5000_PARTID_SHIFT 8 +#define SGTL5000_PARTID_WIDTH 8 +#define SGTL5000_PARTID_PART_ID 0xa0 +#define SGTL5000_REVID_MASK 0x00ff +#define SGTL5000_REVID_SHIFT 0 +#define SGTL5000_REVID_WIDTH 8 + +/* + * SGTL5000_CHIP_DIG_POWER + */ +#define SGTL5000_ADC_EN 0x0040 +#define SGTL5000_DAC_EN 0x0020 +#define SGTL5000_DAP_POWERUP 0x0010 +#define SGTL5000_I2S_OUT_POWERUP 0x0002 +#define SGTL5000_I2S_IN_POWERUP 0x0001 + +/* + * SGTL5000_CHIP_CLK_CTRL + */ +#define SGTL5000_RATE_MODE_MASK 0x0030 +#define SGTL5000_RATE_MODE_SHIFT 4 +#define SGTL5000_RATE_MODE_WIDTH 2 +#define SGTL5000_RATE_MODE_DIV_1 0 +#define SGTL5000_RATE_MODE_DIV_2 1 +#define SGTL5000_RATE_MODE_DIV_4 2 +#define SGTL5000_RATE_MODE_DIV_6 3 +#define SGTL5000_SYS_FS_MASK 0x000c +#define SGTL5000_SYS_FS_SHIFT 2 +#define SGTL5000_SYS_FS_WIDTH 2 +#define SGTL5000_SYS_FS_32k 0x0 +#define SGTL5000_SYS_FS_44_1k 0x1 +#define SGTL5000_SYS_FS_48k 0x2 +#define SGTL5000_SYS_FS_96k 0x3 +#define SGTL5000_MCLK_FREQ_MASK 0x0003 +#define SGTL5000_MCLK_FREQ_SHIFT 0 +#define SGTL5000_MCLK_FREQ_WIDTH 2 +#define SGTL5000_MCLK_FREQ_256FS 0x0 +#define SGTL5000_MCLK_FREQ_384FS 0x1 +#define SGTL5000_MCLK_FREQ_512FS 0x2 +#define SGTL5000_MCLK_FREQ_PLL 0x3 + +/* + * SGTL5000_CHIP_I2S_CTRL + */ +#define SGTL5000_I2S_SCLKFREQ_MASK 0x0100 +#define SGTL5000_I2S_SCLKFREQ_SHIFT 8 +#define SGTL5000_I2S_SCLKFREQ_WIDTH 1 +#define SGTL5000_I2S_SCLKFREQ_64FS 0x0 +#define SGTL5000_I2S_SCLKFREQ_32FS 0x1 /* Not for RJ mode */ +#define SGTL5000_I2S_MASTER 0x0080 +#define SGTL5000_I2S_SCLK_INV 0x0040 +#define SGTL5000_I2S_DLEN_MASK 0x0030 +#define SGTL5000_I2S_DLEN_SHIFT 4 +#define SGTL5000_I2S_DLEN_WIDTH 2 +#define SGTL5000_I2S_DLEN_32 0x0 +#define SGTL5000_I2S_DLEN_24 0x1 +#define SGTL5000_I2S_DLEN_20 0x2 +#define SGTL5000_I2S_DLEN_16 0x3 +#define SGTL5000_I2S_MODE_MASK 0x000c +#define SGTL5000_I2S_MODE_SHIFT 2 +#define SGTL5000_I2S_MODE_WIDTH 2 +#define SGTL5000_I2S_MODE_I2S_LJ 0x0 +#define SGTL5000_I2S_MODE_RJ 0x1 +#define SGTL5000_I2S_MODE_PCM 0x2 +#define SGTL5000_I2S_LRALIGN 0x0002 +#define SGTL5000_I2S_LRPOL 0x0001 /* set for which mode */ + +/* + * SGTL5000_CHIP_SSS_CTRL + */ +#define SGTL5000_DAP_MIX_LRSWAP 0x4000 +#define SGTL5000_DAP_LRSWAP 0x2000 +#define SGTL5000_DAC_LRSWAP 0x1000 +#define SGTL5000_I2S_OUT_LRSWAP 0x0400 +#define SGTL5000_DAP_MIX_SEL_MASK 0x0300 +#define SGTL5000_DAP_MIX_SEL_SHIFT 8 +#define SGTL5000_DAP_MIX_SEL_WIDTH 2 +#define SGTL5000_DAP_MIX_SEL_ADC 0x0 +#define SGTL5000_DAP_MIX_SEL_I2S_IN 0x1 +#define SGTL5000_DAP_SEL_MASK 0x00c0 +#define SGTL5000_DAP_SEL_SHIFT 6 +#define SGTL5000_DAP_SEL_WIDTH 2 +#define SGTL5000_DAP_SEL_ADC 0x0 +#define SGTL5000_DAP_SEL_I2S_IN 0x1 +#define SGTL5000_DAC_SEL_MASK 0x0030 +#define SGTL5000_DAC_SEL_SHIFT 4 +#define SGTL5000_DAC_SEL_WIDTH 2 +#define SGTL5000_DAC_SEL_ADC 0x0 +#define SGTL5000_DAC_SEL_I2S_IN 0x1 +#define SGTL5000_DAC_SEL_DAP 0x3 +#define SGTL5000_I2S_OUT_SEL_MASK 0x0003 +#define SGTL5000_I2S_OUT_SEL_SHIFT 0 +#define SGTL5000_I2S_OUT_SEL_WIDTH 2 +#define SGTL5000_I2S_OUT_SEL_ADC 0x0 +#define SGTL5000_I2S_OUT_SEL_I2S_IN 0x1 +#define SGTL5000_I2S_OUT_SEL_DAP 0x3 + +/* + * SGTL5000_CHIP_ADCDAC_CTRL + */ +#define SGTL5000_VOL_BUSY_DAC_RIGHT 0x2000 +#define SGTL5000_VOL_BUSY_DAC_LEFT 0x1000 +#define SGTL5000_DAC_VOL_RAMP_EN 0x0200 +#define SGTL5000_DAC_VOL_RAMP_EXPO 0x0100 +#define SGTL5000_DAC_MUTE_RIGHT 0x0008 +#define SGTL5000_DAC_MUTE_LEFT 0x0004 +#define SGTL5000_ADC_HPF_FREEZE 0x0002 +#define SGTL5000_ADC_HPF_BYPASS 0x0001 + +/* + * SGTL5000_CHIP_DAC_VOL + */ +#define SGTL5000_DAC_VOL_RIGHT_MASK 0xff00 +#define SGTL5000_DAC_VOL_RIGHT_SHIFT 8 +#define SGTL5000_DAC_VOL_RIGHT_WIDTH 8 +#define SGTL5000_DAC_VOL_LEFT_MASK 0x00ff +#define SGTL5000_DAC_VOL_LEFT_SHIFT 0 +#define SGTL5000_DAC_VOL_LEFT_WIDTH 8 + +/* + * SGTL5000_CHIP_PAD_STRENGTH + */ +#define SGTL5000_PAD_I2S_LRCLK_MASK 0x0300 +#define SGTL5000_PAD_I2S_LRCLK_SHIFT 8 +#define SGTL5000_PAD_I2S_LRCLK_WIDTH 2 +#define SGTL5000_PAD_I2S_SCLK_MASK 0x00c0 +#define SGTL5000_PAD_I2S_SCLK_SHIFT 6 +#define SGTL5000_PAD_I2S_SCLK_WIDTH 2 +#define SGTL5000_PAD_I2S_DOUT_MASK 0x0030 +#define SGTL5000_PAD_I2S_DOUT_SHIFT 4 +#define SGTL5000_PAD_I2S_DOUT_WIDTH 2 +#define SGTL5000_PAD_I2C_SDA_MASK 0x000c +#define SGTL5000_PAD_I2C_SDA_SHIFT 2 +#define SGTL5000_PAD_I2C_SDA_WIDTH 2 +#define SGTL5000_PAD_I2C_SCL_MASK 0x0003 +#define SGTL5000_PAD_I2C_SCL_SHIFT 0 +#define SGTL5000_PAD_I2C_SCL_WIDTH 2 + +/* + * SGTL5000_CHIP_ANA_ADC_CTRL + */ +#define SGTL5000_ADC_VOL_M6DB 0x0100 +#define SGTL5000_ADC_VOL_RIGHT_MASK 0x00f0 +#define SGTL5000_ADC_VOL_RIGHT_SHIFT 4 +#define SGTL5000_ADC_VOL_RIGHT_WIDTH 4 +#define SGTL5000_ADC_VOL_LEFT_MASK 0x000f +#define SGTL5000_ADC_VOL_LEFT_SHIFT 0 +#define SGTL5000_ADC_VOL_LEFT_WIDTH 4 + +/* + * SGTL5000_CHIP_ANA_HP_CTRL + */ +#define SGTL5000_HP_VOL_RIGHT_MASK 0x7f00 +#define SGTL5000_HP_VOL_RIGHT_SHIFT 8 +#define SGTL5000_HP_VOL_RIGHT_WIDTH 7 +#define SGTL5000_HP_VOL_LEFT_MASK 0x007f +#define SGTL5000_HP_VOL_LEFT_SHIFT 0 +#define SGTL5000_HP_VOL_LEFT_WIDTH 7 + +/* + * SGTL5000_CHIP_ANA_CTRL + */ +#define SGTL5000_LINE_OUT_MUTE 0x0100 +#define SGTL5000_HP_SEL_MASK 0x0040 +#define SGTL5000_HP_SEL_SHIFT 6 +#define SGTL5000_HP_SEL_WIDTH 1 +#define SGTL5000_HP_SEL_DAC 0x0 +#define SGTL5000_HP_SEL_LINE_IN 0x1 +#define SGTL5000_HP_ZCD_EN 0x0020 +#define SGTL5000_HP_MUTE 0x0010 +#define SGTL5000_ADC_SEL_MASK 0x0004 +#define SGTL5000_ADC_SEL_SHIFT 2 +#define SGTL5000_ADC_SEL_WIDTH 1 +#define SGTL5000_ADC_SEL_MIC 0x0 +#define SGTL5000_ADC_SEL_LINE_IN 0x1 +#define SGTL5000_ADC_ZCD_EN 0x0002 +#define SGTL5000_ADC_MUTE 0x0001 + +/* + * SGTL5000_CHIP_LINREG_CTRL + */ +#define SGTL5000_VDDC_MAN_ASSN_MASK 0x0040 +#define SGTL5000_VDDC_MAN_ASSN_SHIFT 6 +#define SGTL5000_VDDC_MAN_ASSN_WIDTH 1 +#define SGTL5000_VDDC_MAN_ASSN_VDDA 0x0 +#define SGTL5000_VDDC_MAN_ASSN_VDDIO 0x1 +#define SGTL5000_VDDC_ASSN_OVRD 0x0020 +#define SGTL5000_LINREG_VDDD_MASK 0x000f +#define SGTL5000_LINREG_VDDD_SHIFT 0 +#define SGTL5000_LINREG_VDDD_WIDTH 4 + +/* + * SGTL5000_CHIP_REF_CTRL + */ +#define SGTL5000_ANA_GND_MASK 0x01f0 +#define SGTL5000_ANA_GND_SHIFT 4 +#define SGTL5000_ANA_GND_WIDTH 5 +#define SGTL5000_ANA_GND_BASE 800 /* mv */ +#define SGTL5000_ANA_GND_STP 25 /*mv */ +#define SGTL5000_BIAS_CTRL_MASK 0x000e +#define SGTL5000_BIAS_CTRL_SHIFT 1 +#define SGTL5000_BIAS_CTRL_WIDTH 3 +#define SGTL5000_SMALL_POP 0x0001 + +/* + * SGTL5000_CHIP_MIC_CTRL + */ +#define SGTL5000_BIAS_R_MASK 0x0200 +#define SGTL5000_BIAS_R_SHIFT 8 +#define SGTL5000_BIAS_R_WIDTH 2 +#define SGTL5000_BIAS_R_off 0x0 +#define SGTL5000_BIAS_R_2K 0x1 +#define SGTL5000_BIAS_R_4k 0x2 +#define SGTL5000_BIAS_R_8k 0x3 +#define SGTL5000_BIAS_VOLT_MASK 0x0070 +#define SGTL5000_BIAS_VOLT_SHIFT 4 +#define SGTL5000_BIAS_VOLT_WIDTH 3 +#define SGTL5000_MIC_GAIN_MASK 0x0003 +#define SGTL5000_MIC_GAIN_SHIFT 0 +#define SGTL5000_MIC_GAIN_WIDTH 2 + +/* + * SGTL5000_CHIP_LINE_OUT_CTRL + */ +#define SGTL5000_LINE_OUT_CURRENT_MASK 0x0f00 +#define SGTL5000_LINE_OUT_CURRENT_SHIFT 8 +#define SGTL5000_LINE_OUT_CURRENT_WIDTH 4 +#define SGTL5000_LINE_OUT_CURRENT_180u 0x0 +#define SGTL5000_LINE_OUT_CURRENT_270u 0x1 +#define SGTL5000_LINE_OUT_CURRENT_360u 0x3 +#define SGTL5000_LINE_OUT_CURRENT_450u 0x7 +#define SGTL5000_LINE_OUT_CURRENT_540u 0xf +#define SGTL5000_LINE_OUT_GND_MASK 0x003f +#define SGTL5000_LINE_OUT_GND_SHIFT 0 +#define SGTL5000_LINE_OUT_GND_WIDTH 6 +#define SGTL5000_LINE_OUT_GND_BASE 800 /* mv */ +#define SGTL5000_LINE_OUT_GND_STP 25 +#define SGTL5000_LINE_OUT_GND_MAX 0x23 + +/* + * SGTL5000_CHIP_LINE_OUT_VOL + */ +#define SGTL5000_LINE_OUT_VOL_RIGHT_MASK 0x1f00 +#define SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT 8 +#define SGTL5000_LINE_OUT_VOL_RIGHT_WIDTH 5 +#define SGTL5000_LINE_OUT_VOL_LEFT_MASK 0x001f +#define SGTL5000_LINE_OUT_VOL_LEFT_SHIFT 0 +#define SGTL5000_LINE_OUT_VOL_LEFT_WIDTH 5 + +/* + * SGTL5000_CHIP_ANA_POWER + */ +#define SGTL5000_DAC_STEREO 0x4000 +#define SGTL5000_LINREG_SIMPLE_POWERUP 0x2000 +#define SGTL5000_STARTUP_POWERUP 0x1000 +#define SGTL5000_VDDC_CHRGPMP_POWERUP 0x0800 +#define SGTL5000_PLL_POWERUP 0x0400 +#define SGTL5000_LINEREG_D_POWERUP 0x0200 +#define SGTL5000_VCOAMP_POWERUP 0x0100 +#define SGTL5000_VAG_POWERUP 0x0080 +#define SGTL5000_ADC_STEREO 0x0040 +#define SGTL5000_REFTOP_POWERUP 0x0020 +#define SGTL5000_HP_POWERUP 0x0010 +#define SGTL5000_DAC_POWERUP 0x0008 +#define SGTL5000_CAPLESS_HP_POWERUP 0x0004 +#define SGTL5000_ADC_POWERUP 0x0002 +#define SGTL5000_LINE_OUT_POWERUP 0x0001 + +/* + * SGTL5000_CHIP_PLL_CTRL + */ +#define SGTL5000_PLL_INT_DIV_MASK 0xf800 +#define SGTL5000_PLL_INT_DIV_SHIFT 11 +#define SGTL5000_PLL_INT_DIV_WIDTH 5 +#define SGTL5000_PLL_FRAC_DIV_MASK 0x0700 +#define SGTL5000_PLL_FRAC_DIV_SHIFT 0 +#define SGTL5000_PLL_FRAC_DIV_WIDTH 11 + +/* + * SGTL5000_CHIP_CLK_TOP_CTRL + */ +#define SGTL5000_INT_OSC_EN 0x0800 +#define SGTL5000_INPUT_FREQ_DIV2 0x0008 + +/* + * SGTL5000_CHIP_ANA_STATUS + */ +#define SGTL5000_HP_LRSHORT 0x0200 +#define SGTL5000_CAPLESS_SHORT 0x0100 +#define SGTL5000_PLL_LOCKED 0x0010 + +/* + * SGTL5000_CHIP_SHORT_CTRL + */ +#define SGTL5000_LVLADJR_MASK 0x7000 +#define SGTL5000_LVLADJR_SHIFT 12 +#define SGTL5000_LVLADJR_WIDTH 3 +#define SGTL5000_LVLADJL_MASK 0x0700 +#define SGTL5000_LVLADJL_SHIFT 8 +#define SGTL5000_LVLADJL_WIDTH 3 +#define SGTL5000_LVLADJC_MASK 0x0070 +#define SGTL5000_LVLADJC_SHIFT 4 +#define SGTL5000_LVLADJC_WIDTH 3 +#define SGTL5000_LR_SHORT_MOD_MASK 0x000c +#define SGTL5000_LR_SHORT_MOD_SHIFT 2 +#define SGTL5000_LR_SHORT_MOD_WIDTH 2 +#define SGTL5000_CM_SHORT_MOD_MASK 0x0003 +#define SGTL5000_CM_SHORT_MOD_SHIFT 0 +#define SGTL5000_CM_SHORT_MOD_WIDTH 2 + +/* + *SGTL5000_CHIP_ANA_TEST2 + */ +#define SGTL5000_MONO_DAC 0x1000 + +/* + * SGTL5000_DAP_CTRL + */ +#define SGTL5000_DAP_MIX_EN 0x0010 +#define SGTL5000_DAP_EN 0x0001 + +#define SGTL5000_SYSCLK 0x00 +#define SGTL5000_LRCLK 0x01 + +#endif diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index 40e285df9ae..2a30eae1881 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -34,17 +34,135 @@ #include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> +#include <sound/jack.h> #include "sn95031.h" #define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100) #define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) +/* adc helper functions */ + +/* enables mic bias voltage */ +static void sn95031_enable_mic_bias(struct snd_soc_codec *codec) +{ + snd_soc_write(codec, SN95031_VAUD, BIT(2)|BIT(1)|BIT(0)); + snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(2), BIT(2)); +} + +/* Enable/Disable the ADC depending on the argument */ +static void configure_adc(struct snd_soc_codec *sn95031_codec, int val) +{ + int value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1); + + if (val) { + /* Enable and start the ADC */ + value |= (SN95031_ADC_ENBL | SN95031_ADC_START); + value &= (~SN95031_ADC_NO_LOOP); + } else { + /* Just stop the ADC */ + value &= (~SN95031_ADC_START); + } + snd_soc_write(sn95031_codec, SN95031_ADC1CNTL1, value); +} + /* - * todo: - * capture paths - * jack detection - * PM functions + * finds an empty channel for conversion + * If the ADC is not enabled then start using 0th channel + * itself. Otherwise find an empty channel by looking for a + * channel in which the stopbit is set to 1. returns the index + * of the first free channel if succeeds or an error code. + * + * Context: can sleep + * */ +static int find_free_channel(struct snd_soc_codec *sn95031_codec) +{ + int ret = 0, i, value; + + /* check whether ADC is enabled */ + value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1); + + if ((value & SN95031_ADC_ENBL) == 0) + return 0; + + /* ADC is already enabled; Looking for an empty channel */ + for (i = 0; i < SN95031_ADC_CHANLS_MAX; i++) { + value = snd_soc_read(sn95031_codec, + SN95031_ADC_CHNL_START_ADDR + i); + if (value & SN95031_STOPBIT_MASK) { + ret = i; + break; + } + } + return (ret > SN95031_ADC_LOOP_MAX) ? (-EINVAL) : ret; +} + +/* Initialize the ADC for reading micbias values. Can sleep. */ +static int sn95031_initialize_adc(struct snd_soc_codec *sn95031_codec) +{ + int base_addr, chnl_addr; + int value; + static int channel_index; + + /* Index of the first channel in which the stop bit is set */ + channel_index = find_free_channel(sn95031_codec); + if (channel_index < 0) { + pr_err("No free ADC channels"); + return channel_index; + } + + base_addr = SN95031_ADC_CHNL_START_ADDR + channel_index; + + if (!(channel_index == 0 || channel_index == SN95031_ADC_LOOP_MAX)) { + /* Reset stop bit for channels other than 0 and 12 */ + value = snd_soc_read(sn95031_codec, base_addr); + /* Set the stop bit to zero */ + snd_soc_write(sn95031_codec, base_addr, value & 0xEF); + /* Index of the first free channel */ + base_addr++; + channel_index++; + } + + /* Since this is the last channel, set the stop bit + to 1 by ORing the DIE_SENSOR_CODE with 0x10 */ + snd_soc_write(sn95031_codec, base_addr, + SN95031_AUDIO_DETECT_CODE | 0x10); + + chnl_addr = SN95031_ADC_DATA_START_ADDR + 2 * channel_index; + pr_debug("mid_initialize : %x", chnl_addr); + configure_adc(sn95031_codec, 1); + return chnl_addr; +} + + +/* reads the ADC registers and gets the mic bias value in mV. */ +static unsigned int sn95031_get_mic_bias(struct snd_soc_codec *codec) +{ + u16 adc_adr = sn95031_initialize_adc(codec); + u16 adc_val1, adc_val2; + unsigned int mic_bias; + + sn95031_enable_mic_bias(codec); + + /* Enable the sound card for conversion before reading */ + snd_soc_write(codec, SN95031_ADC1CNTL3, 0x05); + /* Re-toggle the RRDATARD bit */ + snd_soc_write(codec, SN95031_ADC1CNTL3, 0x04); + + /* Read the higher bits of data */ + msleep(1000); + adc_val1 = snd_soc_read(codec, adc_adr); + adc_adr++; + adc_val2 = snd_soc_read(codec, adc_adr); + + /* Adding lower two bits to the higher bits */ + mic_bias = (adc_val1 << 2) + (adc_val2 & 3); + mic_bias = (mic_bias * SN95031_ADC_ONE_LSB_MULTIPLIER) / 1000; + pr_debug("mic bias = %dmV\n", mic_bias); + return mic_bias; +} +EXPORT_SYMBOL_GPL(sn95031_get_mic_bias); +/*end - adc helper functions */ static inline unsigned int sn95031_read(struct snd_soc_codec *codec, unsigned int reg) @@ -241,7 +359,7 @@ static const struct snd_kcontrol_new sn95031_input4_mux_control = static const char *sn95031_micmode_text[] = {"Single Ended", "Differential"}; /* 0dB to 30dB in 10dB steps */ -static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 10, 30); +static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 10, 0); static const struct soc_enum sn95031_micmode1_enum = SOC_ENUM_SINGLE(SN95031_MICAMP1, 1, 2, sn95031_micmode_text); @@ -401,6 +519,8 @@ static const struct snd_soc_dapm_widget sn95031_dapm_widgets[] = { static const struct snd_soc_dapm_route sn95031_audio_map[] = { /* headset and earpiece map */ + { "HPOUTL", NULL, "Headset Rail"}, + { "HPOUTR", NULL, "Headset Rail"}, { "HPOUTL", NULL, "Headset Left Playback" }, { "HPOUTR", NULL, "Headset Right Playback" }, { "EPOUT", NULL, "Earpiece Playback" }, @@ -409,18 +529,16 @@ static const struct snd_soc_dapm_route sn95031_audio_map[] = { { "Earpiece Playback", NULL, "Headset Left Filter"}, { "Headset Left Filter", NULL, "HSDAC Left"}, { "Headset Right Filter", NULL, "HSDAC Right"}, - { "HSDAC Left", NULL, "Headset Rail"}, - { "HSDAC Right", NULL, "Headset Rail"}, /* speaker map */ + { "IHFOUTL", NULL, "Speaker Rail"}, + { "IHFOUTR", NULL, "Speaker Rail"}, { "IHFOUTL", "NULL", "Speaker Left Playback"}, { "IHFOUTR", "NULL", "Speaker Right Playback"}, { "Speaker Left Playback", NULL, "Speaker Left Filter"}, { "Speaker Right Playback", NULL, "Speaker Right Filter"}, { "Speaker Left Filter", NULL, "IHFDAC Left"}, { "Speaker Right Filter", NULL, "IHFDAC Right"}, - { "IHFDAC Left", NULL, "Speaker Rail"}, - { "IHFDAC Right", NULL, "Speaker Rail"}, /* vibra map */ { "VIB1OUT", NULL, "Vibra1 Playback"}, @@ -484,30 +602,30 @@ static const struct snd_soc_dapm_route sn95031_audio_map[] = { { "Txpath2 Capture Route", "ADC Right", "ADC Right"}, { "Txpath3 Capture Route", "ADC Right", "ADC Right"}, { "Txpath4 Capture Route", "ADC Right", "ADC Right"}, - { "Txpath1 Capture Route", NULL, "DMIC1"}, - { "Txpath2 Capture Route", NULL, "DMIC1"}, - { "Txpath3 Capture Route", NULL, "DMIC1"}, - { "Txpath4 Capture Route", NULL, "DMIC1"}, - { "Txpath1 Capture Route", NULL, "DMIC2"}, - { "Txpath2 Capture Route", NULL, "DMIC2"}, - { "Txpath3 Capture Route", NULL, "DMIC2"}, - { "Txpath4 Capture Route", NULL, "DMIC2"}, - { "Txpath1 Capture Route", NULL, "DMIC3"}, - { "Txpath2 Capture Route", NULL, "DMIC3"}, - { "Txpath3 Capture Route", NULL, "DMIC3"}, - { "Txpath4 Capture Route", NULL, "DMIC3"}, - { "Txpath1 Capture Route", NULL, "DMIC4"}, - { "Txpath2 Capture Route", NULL, "DMIC4"}, - { "Txpath3 Capture Route", NULL, "DMIC4"}, - { "Txpath4 Capture Route", NULL, "DMIC4"}, - { "Txpath1 Capture Route", NULL, "DMIC5"}, - { "Txpath2 Capture Route", NULL, "DMIC5"}, - { "Txpath3 Capture Route", NULL, "DMIC5"}, - { "Txpath4 Capture Route", NULL, "DMIC5"}, - { "Txpath1 Capture Route", NULL, "DMIC6"}, - { "Txpath2 Capture Route", NULL, "DMIC6"}, - { "Txpath3 Capture Route", NULL, "DMIC6"}, - { "Txpath4 Capture Route", NULL, "DMIC6"}, + { "Txpath1 Capture Route", "DMIC1", "DMIC1"}, + { "Txpath2 Capture Route", "DMIC1", "DMIC1"}, + { "Txpath3 Capture Route", "DMIC1", "DMIC1"}, + { "Txpath4 Capture Route", "DMIC1", "DMIC1"}, + { "Txpath1 Capture Route", "DMIC2", "DMIC2"}, + { "Txpath2 Capture Route", "DMIC2", "DMIC2"}, + { "Txpath3 Capture Route", "DMIC2", "DMIC2"}, + { "Txpath4 Capture Route", "DMIC2", "DMIC2"}, + { "Txpath1 Capture Route", "DMIC3", "DMIC3"}, + { "Txpath2 Capture Route", "DMIC3", "DMIC3"}, + { "Txpath3 Capture Route", "DMIC3", "DMIC3"}, + { "Txpath4 Capture Route", "DMIC3", "DMIC3"}, + { "Txpath1 Capture Route", "DMIC4", "DMIC4"}, + { "Txpath2 Capture Route", "DMIC4", "DMIC4"}, + { "Txpath3 Capture Route", "DMIC4", "DMIC4"}, + { "Txpath4 Capture Route", "DMIC4", "DMIC4"}, + { "Txpath1 Capture Route", "DMIC5", "DMIC5"}, + { "Txpath2 Capture Route", "DMIC5", "DMIC5"}, + { "Txpath3 Capture Route", "DMIC5", "DMIC5"}, + { "Txpath4 Capture Route", "DMIC5", "DMIC5"}, + { "Txpath1 Capture Route", "DMIC6", "DMIC6"}, + { "Txpath2 Capture Route", "DMIC6", "DMIC6"}, + { "Txpath3 Capture Route", "DMIC6", "DMIC6"}, + { "Txpath4 Capture Route", "DMIC6", "DMIC6"}, /* tx path */ { "TX1 Enable", NULL, "Txpath1 Capture Route"}, @@ -649,6 +767,61 @@ struct snd_soc_dai_driver sn95031_dais[] = { }, }; +static inline void sn95031_disable_jack_btn(struct snd_soc_codec *codec) +{ + snd_soc_write(codec, SN95031_BTNCTRL2, 0x00); +} + +static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec) +{ + snd_soc_write(codec, SN95031_BTNCTRL1, 0x77); + snd_soc_write(codec, SN95031_BTNCTRL2, 0x01); +} + +static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack) +{ + int micbias = sn95031_get_mic_bias(mfld_jack->codec); + + int jack_type = snd_soc_jack_get_type(mfld_jack, micbias); + + pr_debug("jack type detected = %d\n", jack_type); + if (jack_type == SND_JACK_HEADSET) + sn95031_enable_jack_btn(mfld_jack->codec); + return jack_type; +} + +void sn95031_jack_detection(struct mfld_jack_data *jack_data) +{ + unsigned int status; + unsigned int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET; + + pr_debug("interrupt id read in sram = 0x%x\n", jack_data->intr_id); + if (jack_data->intr_id & 0x1) { + pr_debug("short_push detected\n"); + status = SND_JACK_HEADSET | SND_JACK_BTN_0; + } else if (jack_data->intr_id & 0x2) { + pr_debug("long_push detected\n"); + status = SND_JACK_HEADSET | SND_JACK_BTN_1; + } else if (jack_data->intr_id & 0x4) { + pr_debug("headset or headphones inserted\n"); + status = sn95031_get_headset_state(jack_data->mfld_jack); + } else if (jack_data->intr_id & 0x8) { + pr_debug("headset or headphones removed\n"); + status = 0; + sn95031_disable_jack_btn(jack_data->mfld_jack->codec); + } else { + pr_err("unidentified interrupt\n"); + return; + } + + snd_soc_jack_report(jack_data->mfld_jack, status, mask); + /*button pressed and released so we send explicit button release */ + if ((status & SND_JACK_BTN_0) | (status & SND_JACK_BTN_1)) + snd_soc_jack_report(jack_data->mfld_jack, + SND_JACK_HEADSET, mask); +} +EXPORT_SYMBOL_GPL(sn95031_jack_detection); + /* codec registration */ static int sn95031_codec_probe(struct snd_soc_codec *codec) { diff --git a/sound/soc/codecs/sn95031.h b/sound/soc/codecs/sn95031.h index e2b17d908ae..20376d234fb 100644 --- a/sound/soc/codecs/sn95031.h +++ b/sound/soc/codecs/sn95031.h @@ -96,4 +96,37 @@ #define SN95031_SSR5 0x384 #define SN95031_SSR6 0x385 +/* ADC registers */ + +#define SN95031_ADC1CNTL1 0x1C0 +#define SN95031_ADC_ENBL 0x10 +#define SN95031_ADC_START 0x08 +#define SN95031_ADC1CNTL3 0x1C2 +#define SN95031_ADCTHERM_ENBL 0x04 +#define SN95031_ADCRRDATA_ENBL 0x05 +#define SN95031_STOPBIT_MASK 16 +#define SN95031_ADCTHERM_MASK 4 +#define SN95031_ADC_CHANLS_MAX 15 /* Number of ADC channels */ +#define SN95031_ADC_LOOP_MAX (SN95031_ADC_CHANLS_MAX - 1) +#define SN95031_ADC_NO_LOOP 0x07 +#define SN95031_AUDIO_GPIO_CTRL 0x070 + +/* ADC channel code values */ +#define SN95031_AUDIO_DETECT_CODE 0x06 + +/* ADC base addresses */ +#define SN95031_ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */ +#define SN95031_ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */ +/* multipier to convert to mV */ +#define SN95031_ADC_ONE_LSB_MULTIPLIER 2346 + + +struct mfld_jack_data { + int intr_id; + int micbias_vol; + struct snd_soc_jack *mfld_jack; +}; + +extern void sn95031_jack_detection(struct mfld_jack_data *jack_data); + #endif diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c new file mode 100644 index 00000000000..e93b9d1ae1d --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -0,0 +1,794 @@ +/* + * linux/sound/soc/codecs/tlv320aic32x4.c + * + * Copyright 2011 Vista Silicon S.L. + * + * Author: Javier Martin <javier.martin@vista-silicon.com> + * + * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/cdev.h> +#include <linux/slab.h> + +#include <sound/tlv320aic32x4.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "tlv320aic32x4.h" + +struct aic32x4_rate_divs { + u32 mclk; + u32 rate; + u8 p_val; + u8 pll_j; + u16 pll_d; + u16 dosr; + u8 ndac; + u8 mdac; + u8 aosr; + u8 nadc; + u8 madc; + u8 blck_N; +}; + +struct aic32x4_priv { + u32 sysclk; + s32 master; + u8 page_no; + void *control_data; + u32 power_cfg; + u32 micpga_routing; + bool swapdacs; +}; + +/* 0dB min, 1dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_step_1, 0, 100, 0); +/* 0dB min, 0.5dB steps */ +static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0); + +static const struct snd_kcontrol_new aic32x4_snd_controls[] = { + SOC_DOUBLE_R_TLV("PCM Playback Volume", AIC32X4_LDACVOL, + AIC32X4_RDACVOL, 0, 0x30, 0, tlv_step_0_5), + SOC_DOUBLE_R_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN, + AIC32X4_HPRGAIN, 0, 0x1D, 0, tlv_step_1), + SOC_DOUBLE_R_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN, + AIC32X4_LORGAIN, 0, 0x1D, 0, tlv_step_1), + SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN, + AIC32X4_HPRGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN, + AIC32X4_LORGAIN, 6, 0x01, 1), + SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL, + AIC32X4_RMICPGAVOL, 7, 0x01, 1), + + SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0), + SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0), + + SOC_DOUBLE_R_TLV("ADC Level Volume", AIC32X4_LADCVOL, + AIC32X4_RADCVOL, 0, 0x28, 0, tlv_step_0_5), + SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL, + AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5), + + SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0), + + SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0), + SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0), + SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1, + 4, 0x07, 0), + SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1, + 0, 0x03, 0), + SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2, + 6, 0x03, 0), + SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2, + 1, 0x1F, 0), + SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3, + 0, 0x7F, 0), + SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4, + 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5, + 3, 0x1F, 0), + SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6, + 0, 0x1F, 0), + SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7, + 0, 0x0F, 0), +}; + +static const struct aic32x4_rate_divs aic32x4_divs[] = { + /* 8k rate */ + {AIC32X4_FREQ_12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24}, + {AIC32X4_FREQ_24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24}, + {AIC32X4_FREQ_25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24}, + /* 11.025k rate */ + {AIC32X4_FREQ_12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16}, + {AIC32X4_FREQ_24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16}, + /* 16k rate */ + {AIC32X4_FREQ_12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12}, + {AIC32X4_FREQ_24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12}, + {AIC32X4_FREQ_25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12}, + /* 22.05k rate */ + {AIC32X4_FREQ_12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8}, + {AIC32X4_FREQ_24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8}, + {AIC32X4_FREQ_25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8}, + /* 32k rate */ + {AIC32X4_FREQ_12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6}, + {AIC32X4_FREQ_24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6}, + /* 44.1k rate */ + {AIC32X4_FREQ_12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4}, + {AIC32X4_FREQ_24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4}, + {AIC32X4_FREQ_25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4}, + /* 48k rate */ + {AIC32X4_FREQ_12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4}, + {AIC32X4_FREQ_24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4}, + {AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4} +}; + +static const struct snd_kcontrol_new hpl_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_L Switch", AIC32X4_HPLROUTE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new hpr_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_HPRROUTE, 3, 1, 0), + SOC_DAPM_SINGLE("IN1_R Switch", AIC32X4_HPRROUTE, 2, 1, 0), +}; + +static const struct snd_kcontrol_new lol_output_mixer_controls[] = { + SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_LOLROUTE, 3, 1, 0), +}; + +static const struct snd_kcontrol_new lor_output_mixer_controls[] = { + SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_LORROUTE, 3, 1, 0), +}; + +static const struct snd_kcontrol_new left_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1_L P Switch", AIC32X4_LMICPGAPIN, 6, 1, 0), + SOC_DAPM_SINGLE("IN2_L P Switch", AIC32X4_LMICPGAPIN, 4, 1, 0), + SOC_DAPM_SINGLE("IN3_L P Switch", AIC32X4_LMICPGAPIN, 2, 1, 0), +}; + +static const struct snd_kcontrol_new right_input_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1_R P Switch", AIC32X4_RMICPGAPIN, 6, 1, 0), + SOC_DAPM_SINGLE("IN2_R P Switch", AIC32X4_RMICPGAPIN, 4, 1, 0), + SOC_DAPM_SINGLE("IN3_R P Switch", AIC32X4_RMICPGAPIN, 2, 1, 0), +}; + +static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { + SND_SOC_DAPM_DAC("Left DAC", "Left Playback", AIC32X4_DACSETUP, 7, 0), + SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0, + &hpl_output_mixer_controls[0], + ARRAY_SIZE(hpl_output_mixer_controls)), + SND_SOC_DAPM_PGA("HPL Power", AIC32X4_OUTPWRCTL, 5, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("LOL Output Mixer", SND_SOC_NOPM, 0, 0, + &lol_output_mixer_controls[0], + ARRAY_SIZE(lol_output_mixer_controls)), + SND_SOC_DAPM_PGA("LOL Power", AIC32X4_OUTPWRCTL, 3, 0, NULL, 0), + + SND_SOC_DAPM_DAC("Right DAC", "Right Playback", AIC32X4_DACSETUP, 6, 0), + SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0, + &hpr_output_mixer_controls[0], + ARRAY_SIZE(hpr_output_mixer_controls)), + SND_SOC_DAPM_PGA("HPR Power", AIC32X4_OUTPWRCTL, 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("LOR Output Mixer", SND_SOC_NOPM, 0, 0, + &lor_output_mixer_controls[0], + ARRAY_SIZE(lor_output_mixer_controls)), + SND_SOC_DAPM_PGA("LOR Power", AIC32X4_OUTPWRCTL, 2, 0, NULL, 0), + SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0, + &left_input_mixer_controls[0], + ARRAY_SIZE(left_input_mixer_controls)), + SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0, + &right_input_mixer_controls[0], + ARRAY_SIZE(right_input_mixer_controls)), + SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0), + SND_SOC_DAPM_MICBIAS("Mic Bias", AIC32X4_MICBIAS, 6, 0), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("LOL"), + SND_SOC_DAPM_OUTPUT("LOR"), + SND_SOC_DAPM_INPUT("IN1_L"), + SND_SOC_DAPM_INPUT("IN1_R"), + SND_SOC_DAPM_INPUT("IN2_L"), + SND_SOC_DAPM_INPUT("IN2_R"), + SND_SOC_DAPM_INPUT("IN3_L"), + SND_SOC_DAPM_INPUT("IN3_R"), +}; + +static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { + /* Left Output */ + {"HPL Output Mixer", "L_DAC Switch", "Left DAC"}, + {"HPL Output Mixer", "IN1_L Switch", "IN1_L"}, + + {"HPL Power", NULL, "HPL Output Mixer"}, + {"HPL", NULL, "HPL Power"}, + + {"LOL Output Mixer", "L_DAC Switch", "Left DAC"}, + + {"LOL Power", NULL, "LOL Output Mixer"}, + {"LOL", NULL, "LOL Power"}, + + /* Right Output */ + {"HPR Output Mixer", "R_DAC Switch", "Right DAC"}, + {"HPR Output Mixer", "IN1_R Switch", "IN1_R"}, + + {"HPR Power", NULL, "HPR Output Mixer"}, + {"HPR", NULL, "HPR Power"}, + + {"LOR Output Mixer", "R_DAC Switch", "Right DAC"}, + + {"LOR Power", NULL, "LOR Output Mixer"}, + {"LOR", NULL, "LOR Power"}, + + /* Left input */ + {"Left Input Mixer", "IN1_L P Switch", "IN1_L"}, + {"Left Input Mixer", "IN2_L P Switch", "IN2_L"}, + {"Left Input Mixer", "IN3_L P Switch", "IN3_L"}, + + {"Left ADC", NULL, "Left Input Mixer"}, + + /* Right Input */ + {"Right Input Mixer", "IN1_R P Switch", "IN1_R"}, + {"Right Input Mixer", "IN2_R P Switch", "IN2_R"}, + {"Right Input Mixer", "IN3_R P Switch", "IN3_R"}, + + {"Right ADC", NULL, "Right Input Mixer"}, +}; + +static inline int aic32x4_change_page(struct snd_soc_codec *codec, + unsigned int new_page) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 data[2]; + int ret; + + data[0] = 0x00; + data[1] = new_page & 0xff; + + ret = codec->hw_write(codec->control_data, data, 2); + if (ret == 2) { + aic32x4->page_no = new_page; + return 0; + } else { + return ret; + } +} + +static int aic32x4_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + unsigned int page = reg / 128; + unsigned int fixed_reg = reg % 128; + u8 data[2]; + int ret; + + /* A write to AIC32X4_PSEL is really a non-explicit page change */ + if (reg == AIC32X4_PSEL) + return aic32x4_change_page(codec, val); + + if (aic32x4->page_no != page) { + ret = aic32x4_change_page(codec, page); + if (ret != 0) + return ret; + } + + data[0] = fixed_reg & 0xff; + data[1] = val & 0xff; + + if (codec->hw_write(codec->control_data, data, 2) == 2) + return 0; + else + return -EIO; +} + +static unsigned int aic32x4_read(struct snd_soc_codec *codec, unsigned int reg) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + unsigned int page = reg / 128; + unsigned int fixed_reg = reg % 128; + int ret; + + if (aic32x4->page_no != page) { + ret = aic32x4_change_page(codec, page); + if (ret != 0) + return ret; + } + return i2c_smbus_read_byte_data(codec->control_data, fixed_reg & 0xff); +} + +static inline int aic32x4_get_divs(int mclk, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(aic32x4_divs); i++) { + if ((aic32x4_divs[i].rate == rate) + && (aic32x4_divs[i].mclk == mclk)) { + return i; + } + } + printk(KERN_ERR "aic32x4: master clock and sample rate is not supported\n"); + return -EINVAL; +} + +static int aic32x4_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(&codec->dapm, aic32x4_dapm_widgets, + ARRAY_SIZE(aic32x4_dapm_widgets)); + + snd_soc_dapm_add_routes(&codec->dapm, aic32x4_dapm_routes, + ARRAY_SIZE(aic32x4_dapm_routes)); + + snd_soc_dapm_new_widgets(&codec->dapm); + return 0; +} + +static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + + switch (freq) { + case AIC32X4_FREQ_12000000: + case AIC32X4_FREQ_24000000: + case AIC32X4_FREQ_25000000: + aic32x4->sysclk = freq; + return 0; + } + printk(KERN_ERR "aic32x4: invalid frequency to set DAI system clock\n"); + return -EINVAL; +} + +static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 iface_reg_1; + u8 iface_reg_2; + u8 iface_reg_3; + + iface_reg_1 = snd_soc_read(codec, AIC32X4_IFACE1); + iface_reg_1 = iface_reg_1 & ~(3 << 6 | 3 << 2); + iface_reg_2 = snd_soc_read(codec, AIC32X4_IFACE2); + iface_reg_2 = 0; + iface_reg_3 = snd_soc_read(codec, AIC32X4_IFACE3); + iface_reg_3 = iface_reg_3 & ~(1 << 3); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + aic32x4->master = 1; + iface_reg_1 |= AIC32X4_BCLKMASTER | AIC32X4_WCLKMASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + aic32x4->master = 0; + break; + default: + printk(KERN_ERR "aic32x4: invalid DAI master/slave interface\n"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + break; + case SND_SOC_DAIFMT_DSP_A: + iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT); + iface_reg_3 |= (1 << 3); /* invert bit clock */ + iface_reg_2 = 0x01; /* add offset 1 */ + break; + case SND_SOC_DAIFMT_DSP_B: + iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT); + iface_reg_3 |= (1 << 3); /* invert bit clock */ + break; + case SND_SOC_DAIFMT_RIGHT_J: + iface_reg_1 |= + (AIC32X4_RIGHT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT); + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg_1 |= + (AIC32X4_LEFT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT); + break; + default: + printk(KERN_ERR "aic32x4: invalid DAI interface format\n"); + return -EINVAL; + } + + snd_soc_write(codec, AIC32X4_IFACE1, iface_reg_1); + snd_soc_write(codec, AIC32X4_IFACE2, iface_reg_2); + snd_soc_write(codec, AIC32X4_IFACE3, iface_reg_3); + return 0; +} + +static int aic32x4_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 data; + int i; + + i = aic32x4_get_divs(aic32x4->sysclk, params_rate(params)); + if (i < 0) { + printk(KERN_ERR "aic32x4: sampling rate not supported\n"); + return i; + } + + /* Use PLL as CODEC_CLKIN and DAC_MOD_CLK as BDIV_CLKIN */ + snd_soc_write(codec, AIC32X4_CLKMUX, AIC32X4_PLLCLKIN); + snd_soc_write(codec, AIC32X4_IFACE3, AIC32X4_DACMOD2BCLK); + + /* We will fix R value to 1 and will make P & J=K.D as varialble */ + data = snd_soc_read(codec, AIC32X4_PLLPR); + data &= ~(7 << 4); + snd_soc_write(codec, AIC32X4_PLLPR, + (data | (aic32x4_divs[i].p_val << 4) | 0x01)); + + snd_soc_write(codec, AIC32X4_PLLJ, aic32x4_divs[i].pll_j); + + snd_soc_write(codec, AIC32X4_PLLDMSB, (aic32x4_divs[i].pll_d >> 8)); + snd_soc_write(codec, AIC32X4_PLLDLSB, + (aic32x4_divs[i].pll_d & 0xff)); + + /* NDAC divider value */ + data = snd_soc_read(codec, AIC32X4_NDAC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_NDAC, data | aic32x4_divs[i].ndac); + + /* MDAC divider value */ + data = snd_soc_read(codec, AIC32X4_MDAC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_MDAC, data | aic32x4_divs[i].mdac); + + /* DOSR MSB & LSB values */ + snd_soc_write(codec, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8); + snd_soc_write(codec, AIC32X4_DOSRLSB, + (aic32x4_divs[i].dosr & 0xff)); + + /* NADC divider value */ + data = snd_soc_read(codec, AIC32X4_NADC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_NADC, data | aic32x4_divs[i].nadc); + + /* MADC divider value */ + data = snd_soc_read(codec, AIC32X4_MADC); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_MADC, data | aic32x4_divs[i].madc); + + /* AOSR value */ + snd_soc_write(codec, AIC32X4_AOSR, aic32x4_divs[i].aosr); + + /* BCLK N divider */ + data = snd_soc_read(codec, AIC32X4_BCLKN); + data &= ~(0x7f); + snd_soc_write(codec, AIC32X4_BCLKN, data | aic32x4_divs[i].blck_N); + + data = snd_soc_read(codec, AIC32X4_IFACE1); + data = data & ~(3 << 4); + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + data |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_DOSRMSB_SHIFT); + break; + case SNDRV_PCM_FORMAT_S24_LE: + data |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_DOSRMSB_SHIFT); + break; + case SNDRV_PCM_FORMAT_S32_LE: + data |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_DOSRMSB_SHIFT); + break; + } + snd_soc_write(codec, AIC32X4_IFACE1, data); + + return 0; +} + +static int aic32x4_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u8 dac_reg; + + dac_reg = snd_soc_read(codec, AIC32X4_DACMUTE) & ~AIC32X4_MUTEON; + if (mute) + snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg | AIC32X4_MUTEON); + else + snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg); + return 0; +} + +static int aic32x4_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u8 value; + + switch (level) { + case SND_SOC_BIAS_ON: + if (aic32x4->master) { + /* Switch on PLL */ + value = snd_soc_read(codec, AIC32X4_PLLPR); + snd_soc_write(codec, AIC32X4_PLLPR, + (value | AIC32X4_PLLEN)); + + /* Switch on NDAC Divider */ + value = snd_soc_read(codec, AIC32X4_NDAC); + snd_soc_write(codec, AIC32X4_NDAC, + value | AIC32X4_NDACEN); + + /* Switch on MDAC Divider */ + value = snd_soc_read(codec, AIC32X4_MDAC); + snd_soc_write(codec, AIC32X4_MDAC, + value | AIC32X4_MDACEN); + + /* Switch on NADC Divider */ + value = snd_soc_read(codec, AIC32X4_NADC); + snd_soc_write(codec, AIC32X4_NADC, + value | AIC32X4_MDACEN); + + /* Switch on MADC Divider */ + value = snd_soc_read(codec, AIC32X4_MADC); + snd_soc_write(codec, AIC32X4_MADC, + value | AIC32X4_MDACEN); + + /* Switch on BCLK_N Divider */ + value = snd_soc_read(codec, AIC32X4_BCLKN); + snd_soc_write(codec, AIC32X4_BCLKN, + value | AIC32X4_BCLKEN); + } + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + if (aic32x4->master) { + /* Switch off PLL */ + value = snd_soc_read(codec, AIC32X4_PLLPR); + snd_soc_write(codec, AIC32X4_PLLPR, + (value & ~AIC32X4_PLLEN)); + + /* Switch off NDAC Divider */ + value = snd_soc_read(codec, AIC32X4_NDAC); + snd_soc_write(codec, AIC32X4_NDAC, + value & ~AIC32X4_NDACEN); + + /* Switch off MDAC Divider */ + value = snd_soc_read(codec, AIC32X4_MDAC); + snd_soc_write(codec, AIC32X4_MDAC, + value & ~AIC32X4_MDACEN); + + /* Switch off NADC Divider */ + value = snd_soc_read(codec, AIC32X4_NADC); + snd_soc_write(codec, AIC32X4_NADC, + value & ~AIC32X4_NDACEN); + + /* Switch off MADC Divider */ + value = snd_soc_read(codec, AIC32X4_MADC); + snd_soc_write(codec, AIC32X4_MADC, + value & ~AIC32X4_MDACEN); + value = snd_soc_read(codec, AIC32X4_BCLKN); + + /* Switch off BCLK_N Divider */ + snd_soc_write(codec, AIC32X4_BCLKN, + value & ~AIC32X4_BCLKEN); + } + break; + case SND_SOC_BIAS_OFF: + break; + } + codec->dapm.bias_level = level; + return 0; +} + +#define AIC32X4_RATES SNDRV_PCM_RATE_8000_48000 +#define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ + | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_ops aic32x4_ops = { + .hw_params = aic32x4_hw_params, + .digital_mute = aic32x4_mute, + .set_fmt = aic32x4_set_dai_fmt, + .set_sysclk = aic32x4_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver aic32x4_dai = { + .name = "tlv320aic32x4-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AIC32X4_RATES, + .formats = AIC32X4_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AIC32X4_RATES, + .formats = AIC32X4_FORMATS,}, + .ops = &aic32x4_ops, + .symmetric_rates = 1, +}; + +static int aic32x4_suspend(struct snd_soc_codec *codec, pm_message_t state) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int aic32x4_resume(struct snd_soc_codec *codec) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} + +static int aic32x4_probe(struct snd_soc_codec *codec) +{ + struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec); + u32 tmp_reg; + + codec->hw_write = (hw_write_t) i2c_master_send; + codec->control_data = aic32x4->control_data; + + snd_soc_write(codec, AIC32X4_RESET, 0x01); + + /* Power platform configuration */ + if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) { + snd_soc_write(codec, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN | + AIC32X4_MICBIAS_2075V); + } + if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) { + snd_soc_write(codec, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE); + } + if (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) { + snd_soc_write(codec, AIC32X4_LDOCTL, AIC32X4_LDOCTLEN); + } + tmp_reg = snd_soc_read(codec, AIC32X4_CMMODE); + if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36) { + tmp_reg |= AIC32X4_LDOIN_18_36; + } + if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED) { + tmp_reg |= AIC32X4_LDOIN2HP; + } + snd_soc_write(codec, AIC32X4_CMMODE, tmp_reg); + + /* Do DACs need to be swapped? */ + if (aic32x4->swapdacs) { + snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2RCHN | AIC32X4_RDAC2LCHN); + } else { + snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN); + } + + /* Mic PGA routing */ + if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) { + snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_IN2R_10K); + } + if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) { + snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_IN1L_10K); + } + + aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + snd_soc_add_controls(codec, aic32x4_snd_controls, + ARRAY_SIZE(aic32x4_snd_controls)); + aic32x4_add_widgets(codec); + + return 0; +} + +static int aic32x4_remove(struct snd_soc_codec *codec) +{ + aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = { + .read = aic32x4_read, + .write = aic32x4_write, + .probe = aic32x4_probe, + .remove = aic32x4_remove, + .suspend = aic32x4_suspend, + .resume = aic32x4_resume, + .set_bias_level = aic32x4_set_bias_level, +}; + +static __devinit int aic32x4_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct aic32x4_pdata *pdata = i2c->dev.platform_data; + struct aic32x4_priv *aic32x4; + int ret; + + aic32x4 = kzalloc(sizeof(struct aic32x4_priv), GFP_KERNEL); + if (aic32x4 == NULL) + return -ENOMEM; + + aic32x4->control_data = i2c; + i2c_set_clientdata(i2c, aic32x4); + + if (pdata) { + aic32x4->power_cfg = pdata->power_cfg; + aic32x4->swapdacs = pdata->swapdacs; + aic32x4->micpga_routing = pdata->micpga_routing; + } else { + aic32x4->power_cfg = 0; + aic32x4->swapdacs = false; + aic32x4->micpga_routing = 0; + } + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_aic32x4, &aic32x4_dai, 1); + if (ret < 0) + kfree(aic32x4); + return ret; +} + +static __devexit int aic32x4_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 aic32x4_i2c_id[] = { + { "tlv320aic32x4", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id); + +static struct i2c_driver aic32x4_i2c_driver = { + .driver = { + .name = "tlv320aic32x4", + .owner = THIS_MODULE, + }, + .probe = aic32x4_i2c_probe, + .remove = __devexit_p(aic32x4_i2c_remove), + .id_table = aic32x4_i2c_id, +}; + +static int __init aic32x4_modinit(void) +{ + int ret = 0; + + ret = i2c_add_driver(&aic32x4_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register aic32x4 I2C driver: %d\n", + ret); + } + return ret; +} +module_init(aic32x4_modinit); + +static void __exit aic32x4_exit(void) +{ + i2c_del_driver(&aic32x4_i2c_driver); +} +module_exit(aic32x4_exit); + +MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver"); +MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h new file mode 100644 index 00000000000..aae2b244039 --- /dev/null +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -0,0 +1,143 @@ +/* + * tlv320aic32x4.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef _TLV320AIC32X4_H +#define _TLV320AIC32X4_H + +/* tlv320aic32x4 register space (in decimal to match datasheet) */ + +#define AIC32X4_PAGE1 128 + +#define AIC32X4_PSEL 0 +#define AIC32X4_RESET 1 +#define AIC32X4_CLKMUX 4 +#define AIC32X4_PLLPR 5 +#define AIC32X4_PLLJ 6 +#define AIC32X4_PLLDMSB 7 +#define AIC32X4_PLLDLSB 8 +#define AIC32X4_NDAC 11 +#define AIC32X4_MDAC 12 +#define AIC32X4_DOSRMSB 13 +#define AIC32X4_DOSRLSB 14 +#define AIC32X4_NADC 18 +#define AIC32X4_MADC 19 +#define AIC32X4_AOSR 20 +#define AIC32X4_CLKMUX2 25 +#define AIC32X4_CLKOUTM 26 +#define AIC32X4_IFACE1 27 +#define AIC32X4_IFACE2 28 +#define AIC32X4_IFACE3 29 +#define AIC32X4_BCLKN 30 +#define AIC32X4_IFACE4 31 +#define AIC32X4_IFACE5 32 +#define AIC32X4_IFACE6 33 +#define AIC32X4_DOUTCTL 53 +#define AIC32X4_DINCTL 54 +#define AIC32X4_DACSPB 60 +#define AIC32X4_ADCSPB 61 +#define AIC32X4_DACSETUP 63 +#define AIC32X4_DACMUTE 64 +#define AIC32X4_LDACVOL 65 +#define AIC32X4_RDACVOL 66 +#define AIC32X4_ADCSETUP 81 +#define AIC32X4_ADCFGA 82 +#define AIC32X4_LADCVOL 83 +#define AIC32X4_RADCVOL 84 +#define AIC32X4_LAGC1 86 +#define AIC32X4_LAGC2 87 +#define AIC32X4_LAGC3 88 +#define AIC32X4_LAGC4 89 +#define AIC32X4_LAGC5 90 +#define AIC32X4_LAGC6 91 +#define AIC32X4_LAGC7 92 +#define AIC32X4_RAGC1 94 +#define AIC32X4_RAGC2 95 +#define AIC32X4_RAGC3 96 +#define AIC32X4_RAGC4 97 +#define AIC32X4_RAGC5 98 +#define AIC32X4_RAGC6 99 +#define AIC32X4_RAGC7 100 +#define AIC32X4_PWRCFG (AIC32X4_PAGE1 + 1) +#define AIC32X4_LDOCTL (AIC32X4_PAGE1 + 2) +#define AIC32X4_OUTPWRCTL (AIC32X4_PAGE1 + 9) +#define AIC32X4_CMMODE (AIC32X4_PAGE1 + 10) +#define AIC32X4_HPLROUTE (AIC32X4_PAGE1 + 12) +#define AIC32X4_HPRROUTE (AIC32X4_PAGE1 + 13) +#define AIC32X4_LOLROUTE (AIC32X4_PAGE1 + 14) +#define AIC32X4_LORROUTE (AIC32X4_PAGE1 + 15) +#define AIC32X4_HPLGAIN (AIC32X4_PAGE1 + 16) +#define AIC32X4_HPRGAIN (AIC32X4_PAGE1 + 17) +#define AIC32X4_LOLGAIN (AIC32X4_PAGE1 + 18) +#define AIC32X4_LORGAIN (AIC32X4_PAGE1 + 19) +#define AIC32X4_HEADSTART (AIC32X4_PAGE1 + 20) +#define AIC32X4_MICBIAS (AIC32X4_PAGE1 + 51) +#define AIC32X4_LMICPGAPIN (AIC32X4_PAGE1 + 52) +#define AIC32X4_LMICPGANIN (AIC32X4_PAGE1 + 54) +#define AIC32X4_RMICPGAPIN (AIC32X4_PAGE1 + 55) +#define AIC32X4_RMICPGANIN (AIC32X4_PAGE1 + 57) +#define AIC32X4_FLOATINGINPUT (AIC32X4_PAGE1 + 58) +#define AIC32X4_LMICPGAVOL (AIC32X4_PAGE1 + 59) +#define AIC32X4_RMICPGAVOL (AIC32X4_PAGE1 + 60) + +#define AIC32X4_FREQ_12000000 12000000 +#define AIC32X4_FREQ_24000000 24000000 +#define AIC32X4_FREQ_25000000 25000000 + +#define AIC32X4_WORD_LEN_16BITS 0x00 +#define AIC32X4_WORD_LEN_20BITS 0x01 +#define AIC32X4_WORD_LEN_24BITS 0x02 +#define AIC32X4_WORD_LEN_32BITS 0x03 + +#define AIC32X4_I2S_MODE 0x00 +#define AIC32X4_DSP_MODE 0x01 +#define AIC32X4_RIGHT_JUSTIFIED_MODE 0x02 +#define AIC32X4_LEFT_JUSTIFIED_MODE 0x03 + +#define AIC32X4_AVDDWEAKDISABLE 0x08 +#define AIC32X4_LDOCTLEN 0x01 + +#define AIC32X4_LDOIN_18_36 0x01 +#define AIC32X4_LDOIN2HP 0x02 + +#define AIC32X4_DACSPBLOCK_MASK 0x1f +#define AIC32X4_ADCSPBLOCK_MASK 0x1f + +#define AIC32X4_PLLJ_SHIFT 6 +#define AIC32X4_DOSRMSB_SHIFT 4 + +#define AIC32X4_PLLCLKIN 0x03 + +#define AIC32X4_MICBIAS_LDOIN 0x08 +#define AIC32X4_MICBIAS_2075V 0x60 + +#define AIC32X4_LMICPGANIN_IN2R_10K 0x10 +#define AIC32X4_RMICPGANIN_IN1L_10K 0x10 + +#define AIC32X4_LMICPGAVOL_NOGAIN 0x80 +#define AIC32X4_RMICPGAVOL_NOGAIN 0x80 + +#define AIC32X4_BCLKMASTER 0x08 +#define AIC32X4_WCLKMASTER 0x04 +#define AIC32X4_PLLEN (0x01 << 7) +#define AIC32X4_NDACEN (0x01 << 7) +#define AIC32X4_MDACEN (0x01 << 7) +#define AIC32X4_NADCEN (0x01 << 7) +#define AIC32X4_MADCEN (0x01 << 7) +#define AIC32X4_BCLKEN (0x01 << 7) +#define AIC32X4_DACEN (0x03 << 6) +#define AIC32X4_RDAC2LCHN (0x02 << 2) +#define AIC32X4_LDAC2RCHN (0x02 << 4) +#define AIC32X4_LDAC2LCHN (0x01 << 4) +#define AIC32X4_RDAC2RCHN (0x01 << 2) + +#define AIC32X4_SSTEP2WCLK 0x01 +#define AIC32X4_MUTEON 0x0C +#define AIC32X4_DACMOD2BCLK 0x01 + +#endif /* _TLV320AIC32X4_H */ diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 6c3735dcb05..eb1a0b4e09b 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -1623,6 +1623,7 @@ static const struct i2c_device_id tlv320dac33_i2c_id[] = { }, { }, }; +MODULE_DEVICE_TABLE(i2c, tlv320dac33_i2c_id); static struct i2c_driver tlv320dac33_i2c_driver = { .driver = { diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index e4d464b937d..8512800f632 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -26,6 +26,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> +#include <linux/mfd/core.h> #include <linux/i2c/twl.h> #include <linux/slab.h> #include <sound/core.h> @@ -732,7 +733,8 @@ static int aif_event(struct snd_soc_dapm_widget *w, static void headset_ramp(struct snd_soc_codec *codec, int ramp) { - struct twl4030_codec_audio_data *pdata = codec->dev->platform_data; + struct twl4030_codec_audio_data *pdata = + mfd_get_data(to_platform_device(codec->dev)); unsigned char hs_gain, hs_pop; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); /* Base values for ramp delay calculation: 2^19 - 2^26 */ @@ -2297,7 +2299,7 @@ static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { static int __devinit twl4030_codec_probe(struct platform_device *pdev) { - struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data; + struct twl4030_codec_audio_data *pdata = mfd_get_data(pdev); if (!pdata) { dev_err(&pdev->dev, "platform_data is missing\n"); diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c index e76847a9438..48ffd406a71 100644 --- a/sound/soc/codecs/uda134x.c +++ b/sound/soc/codecs/uda134x.c @@ -486,7 +486,8 @@ static struct snd_soc_dai_driver uda134x_dai = { static int uda134x_soc_probe(struct snd_soc_codec *codec) { struct uda134x_priv *uda134x; - struct uda134x_platform_data *pd = dev_get_drvdata(codec->card->dev); + struct uda134x_platform_data *pd = codec->card->dev->platform_data; + int ret; printk(KERN_INFO "UDA134X SoC Audio Codec\n"); diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index 861b28f543d..c8a874d0d4c 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -3,7 +3,7 @@ * * Author: Matti Aaltonen, <matti.j.aaltonen@nokia.com> * - * Copyright: (C) 2010 Nokia Corporation + * Copyright: (C) 2010, 2011 Nokia Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -179,7 +179,12 @@ static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol, return 0; } -static const char *wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" }; +/* + * TODO: Implement the audio routing in the driver. Now this control + * only indicates the setting that has been done elsewhere (in the user + * space). + */ +static const char * const wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" }; static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -239,7 +244,7 @@ static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol, return 1; } -static const char *wl1273_audio_strings[] = { "Digital", "Analog" }; +static const char * const wl1273_audio_strings[] = { "Digital", "Analog" }; static const struct soc_enum wl1273_audio_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wl1273_audio_strings), @@ -436,7 +441,8 @@ EXPORT_SYMBOL_GPL(wl1273_get_format); static int wl1273_probe(struct snd_soc_codec *codec) { - struct wl1273_core **core = codec->dev->platform_data; + struct wl1273_core **core = + mfd_get_data(to_platform_device(codec->dev)); struct wl1273_priv *wl1273; int r; diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 80ddf4fd23d..a3b9cbb20ee 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -836,24 +836,25 @@ static void wm2000_i2c_shutdown(struct i2c_client *i2c) } #ifdef CONFIG_PM -static int wm2000_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg) +static int wm2000_i2c_suspend(struct device *dev) { + struct i2c_client *i2c = to_i2c_client(dev); struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); return wm2000_anc_transition(wm2000, ANC_OFF); } -static int wm2000_i2c_resume(struct i2c_client *i2c) +static int wm2000_i2c_resume(struct device *dev) { + struct i2c_client *i2c = to_i2c_client(dev); struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); return wm2000_anc_set_mode(wm2000); } -#else -#define wm2000_i2c_suspend NULL -#define wm2000_i2c_resume NULL #endif +static SIMPLE_DEV_PM_OPS(wm2000_pm, wm2000_i2c_suspend, wm2000_i2c_resume); + static const struct i2c_device_id wm2000_i2c_id[] = { { "wm2000", 0 }, { } @@ -864,11 +865,10 @@ static struct i2c_driver wm2000_i2c_driver = { .driver = { .name = "wm2000", .owner = THIS_MODULE, + .pm = &wm2000_pm, }, .probe = wm2000_i2c_probe, .remove = __devexit_p(wm2000_i2c_remove), - .suspend = wm2000_i2c_suspend, - .resume = wm2000_i2c_resume, .shutdown = wm2000_i2c_shutdown, .id_table = wm2000_i2c_id, }; diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 3c3bc079167..736b785e375 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -22,6 +22,7 @@ #include <linux/regulator/consumer.h> #include <linux/mfd/wm8400-audio.h> #include <linux/mfd/wm8400-private.h> +#include <linux/mfd/core.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -1377,7 +1378,7 @@ static void wm8400_probe_deferred(struct work_struct *work) static int wm8400_codec_probe(struct snd_soc_codec *codec) { - struct wm8400 *wm8400 = dev_get_platdata(codec->dev); + struct wm8400 *wm8400 = mfd_get_data(to_platform_device(codec->dev)); struct wm8400_priv *priv; int ret; u16 reg; diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 79b02ae125c..3f09deea8d9 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -55,8 +55,10 @@ static int caps_charge = 2000; module_param(caps_charge, int, 0); MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)"); -static void wm8753_set_dai_mode(struct snd_soc_codec *codec, - struct snd_soc_dai *dai, unsigned int hifi); +static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec, + unsigned int fmt); +static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec, + unsigned int fmt); /* * wm8753 register cache @@ -87,6 +89,10 @@ struct wm8753_priv { enum snd_soc_control_type control_type; unsigned int sysclk; unsigned int pcmclk; + + unsigned int voice_fmt; + unsigned int hifi_fmt; + int dai_func; }; @@ -170,9 +176,9 @@ static int wm8753_get_dai(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - int mode = snd_soc_read(codec, WM8753_IOCTL); + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); - ucontrol->value.integer.value[0] = (mode & 0xc) >> 2; + ucontrol->value.integer.value[0] = wm8753->dai_func; return 0; } @@ -180,16 +186,26 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - int mode = snd_soc_read(codec, WM8753_IOCTL); struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + u16 ioctl; + + if (codec->active) + return -EBUSY; + + ioctl = snd_soc_read(codec, WM8753_IOCTL); + + wm8753->dai_func = ucontrol->value.integer.value[0]; + + if (((ioctl >> 2) & 0x3) == wm8753->dai_func) + return 1; + + ioctl = (ioctl & 0x1f3) | (wm8753->dai_func << 2); + snd_soc_write(codec, WM8753_IOCTL, ioctl); - if (((mode & 0xc) >> 2) == ucontrol->value.integer.value[0]) - return 0; - mode &= 0xfff3; - mode |= (ucontrol->value.integer.value[0] << 2); + wm8753_hifi_write_dai_fmt(codec, wm8753->hifi_fmt); + wm8753_voice_write_dai_fmt(codec, wm8753->voice_fmt); - wm8753->dai_func = ucontrol->value.integer.value[0]; return 1; } @@ -828,10 +844,9 @@ static int wm8753_set_dai_sysclk(struct snd_soc_dai *codec_dai, /* * Set's ADC and Voice DAC format. */ -static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 voice = snd_soc_read(codec, WM8753_PCM) & 0x01ec; /* interface format */ @@ -858,13 +873,6 @@ static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai, return 0; } -static int wm8753_pcm_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - wm8753_set_dai_mode(dai->codec, dai, 0); - return 0; -} - /* * Set PCM DAI bit size and sample rate. */ @@ -905,10 +913,9 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream, /* * Set's PCM dai fmt and BCLK. */ -static int wm8753_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 voice, ioctl; voice = snd_soc_read(codec, WM8753_PCM) & 0x011f; @@ -999,10 +1006,9 @@ static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai, /* * Set's HiFi DAC format. */ -static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 hifi = snd_soc_read(codec, WM8753_HIFI) & 0x01e0; /* interface format */ @@ -1032,10 +1038,9 @@ static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai, /* * Set's I2S DAI format. */ -static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 ioctl, hifi; hifi = snd_soc_read(codec, WM8753_HIFI) & 0x011f; @@ -1098,13 +1103,6 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai, return 0; } -static int wm8753_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - wm8753_set_dai_mode(dai->codec, dai, 1); - return 0; -} - /* * Set PCM DAI bit size and sample rate. */ @@ -1147,61 +1145,117 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream, return 0; } -static int wm8753_mode1v_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 clock; /* set clk source as pcmclk */ clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb; snd_soc_write(codec, WM8753_CLOCK, clock); - if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0) - return -EINVAL; - return wm8753_pcm_set_dai_fmt(codec_dai, fmt); + return wm8753_vdac_adc_set_dai_fmt(codec, fmt); } -static int wm8753_mode1h_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0) - return -EINVAL; - return wm8753_i2s_set_dai_fmt(codec_dai, fmt); + return wm8753_hdac_set_dai_fmt(codec, fmt); } -static int wm8753_mode2_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 clock; /* set clk source as pcmclk */ clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb; snd_soc_write(codec, WM8753_CLOCK, clock); - if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0) - return -EINVAL; - return wm8753_i2s_set_dai_fmt(codec_dai, fmt); + return wm8753_vdac_adc_set_dai_fmt(codec, fmt); } -static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_dai *codec_dai, +static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec *codec, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; u16 clock; /* set clk source as mclk */ clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb; snd_soc_write(codec, WM8753_CLOCK, clock | 0x4); - if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0) + if (wm8753_hdac_set_dai_fmt(codec, fmt) < 0) return -EINVAL; - if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0) - return -EINVAL; - return wm8753_i2s_set_dai_fmt(codec_dai, fmt); + return wm8753_vdac_adc_set_dai_fmt(codec, fmt); } +static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec, + unsigned int fmt) +{ + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + switch (wm8753->dai_func) { + case 0: + ret = wm8753_mode1h_set_dai_fmt(codec, fmt); + break; + case 1: + ret = wm8753_mode2_set_dai_fmt(codec, fmt); + break; + case 2: + case 3: + ret = wm8753_mode3_4_set_dai_fmt(codec, fmt); + break; + default: + break; + } + if (ret) + return ret; + + return wm8753_i2s_set_dai_fmt(codec, fmt); +} + +static int wm8753_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + + wm8753->hifi_fmt = fmt; + + return wm8753_hifi_write_dai_fmt(codec, fmt); +}; + +static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec, + unsigned int fmt) +{ + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (wm8753->dai_func != 0) + return 0; + + ret = wm8753_mode1v_set_dai_fmt(codec, fmt); + if (ret) + return ret; + ret = wm8753_pcm_set_dai_fmt(codec, fmt); + if (ret) + return ret; + + return 0; +}; + +static int wm8753_voice_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); + + wm8753->voice_fmt = fmt; + + return wm8753_voice_write_dai_fmt(codec, fmt); +}; + static int wm8753_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; @@ -1268,57 +1322,25 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec, * 3. Voice disabled - HIFI over HIFI * 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture */ -static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode1 = { - .startup = wm8753_i2s_startup, +static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode = { .hw_params = wm8753_i2s_hw_params, .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode1h_set_dai_fmt, - .set_clkdiv = wm8753_set_dai_clkdiv, - .set_pll = wm8753_set_dai_pll, - .set_sysclk = wm8753_set_dai_sysclk, -}; - -static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode1 = { - .startup = wm8753_pcm_startup, - .hw_params = wm8753_pcm_hw_params, - .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode1v_set_dai_fmt, + .set_fmt = wm8753_hifi_set_dai_fmt, .set_clkdiv = wm8753_set_dai_clkdiv, .set_pll = wm8753_set_dai_pll, .set_sysclk = wm8753_set_dai_sysclk, }; -static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode2 = { - .startup = wm8753_pcm_startup, +static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode = { .hw_params = wm8753_pcm_hw_params, .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode2_set_dai_fmt, - .set_clkdiv = wm8753_set_dai_clkdiv, - .set_pll = wm8753_set_dai_pll, - .set_sysclk = wm8753_set_dai_sysclk, -}; - -static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode3 = { - .startup = wm8753_i2s_startup, - .hw_params = wm8753_i2s_hw_params, - .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode3_4_set_dai_fmt, - .set_clkdiv = wm8753_set_dai_clkdiv, - .set_pll = wm8753_set_dai_pll, - .set_sysclk = wm8753_set_dai_sysclk, -}; - -static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode4 = { - .startup = wm8753_i2s_startup, - .hw_params = wm8753_i2s_hw_params, - .digital_mute = wm8753_mute, - .set_fmt = wm8753_mode3_4_set_dai_fmt, + .set_fmt = wm8753_voice_set_dai_fmt, .set_clkdiv = wm8753_set_dai_clkdiv, .set_pll = wm8753_set_dai_pll, .set_sysclk = wm8753_set_dai_sysclk, }; -static struct snd_soc_dai_driver wm8753_all_dai[] = { +static struct snd_soc_dai_driver wm8753_dai[] = { /* DAI HiFi mode 1 */ { .name = "wm8753-hifi", .playback = { @@ -1326,14 +1348,16 @@ static struct snd_soc_dai_driver wm8753_all_dai[] = { .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, - .formats = WM8753_FORMATS}, + .formats = WM8753_FORMATS + }, .capture = { /* dummy for fast DAI switching */ .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, - .formats = WM8753_FORMATS}, - .ops = &wm8753_dai_ops_hifi_mode1, + .formats = WM8753_FORMATS + }, + .ops = &wm8753_dai_ops_hifi_mode, }, /* DAI Voice mode 1 */ { .name = "wm8753-voice", @@ -1342,97 +1366,19 @@ static struct snd_soc_dai_driver wm8753_all_dai[] = { .channels_min = 1, .channels_max = 1, .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .ops = &wm8753_dai_ops_voice_mode1, -}, -/* DAI HiFi mode 2 - dummy */ -{ .name = "wm8753-hifi", -}, -/* DAI Voice mode 2 */ -{ .name = "wm8753-voice", - .playback = { - .stream_name = "Voice Playback", - .channels_min = 1, - .channels_max = 1, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .ops = &wm8753_dai_ops_voice_mode2, -}, -/* DAI HiFi mode 3 */ -{ .name = "wm8753-hifi", - .playback = { - .stream_name = "HiFi Playback", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .ops = &wm8753_dai_ops_hifi_mode3, -}, -/* DAI Voice mode 3 - dummy */ -{ .name = "wm8753-voice", -}, -/* DAI HiFi mode 4 */ -{ .name = "wm8753-hifi", - .playback = { - .stream_name = "HiFi Playback", - .channels_min = 1, - .channels_max = 2, - .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, + .formats = WM8753_FORMATS, + }, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8753_RATES, - .formats = WM8753_FORMATS,}, - .ops = &wm8753_dai_ops_hifi_mode4, -}, -/* DAI Voice mode 4 - dummy */ -{ .name = "wm8753-voice", -}, -}; - -static struct snd_soc_dai_driver wm8753_dai[] = { - { - .name = "wm8753-aif0", - }, - { - .name = "wm8753-aif1", + .formats = WM8753_FORMATS, }, + .ops = &wm8753_dai_ops_voice_mode, +}, }; -static void wm8753_set_dai_mode(struct snd_soc_codec *codec, - struct snd_soc_dai *dai, unsigned int hifi) -{ - struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); - - if (wm8753->dai_func < 4) { - if (hifi) - dai->driver = &wm8753_all_dai[wm8753->dai_func << 1]; - else - dai->driver = &wm8753_all_dai[(wm8753->dai_func << 1) + 1]; - } - snd_soc_write(codec, WM8753_IOCTL, wm8753->dai_func); -} - static void wm8753_work(struct work_struct *work) { struct snd_soc_dapm_context *dapm = diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 3d4c55f3c7b..ae1cadfae84 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -223,11 +223,12 @@ struct wm8903_priv { int fs; int deemph; + int dcs_pending; + int dcs_cache[4]; + /* Reference count */ int class_w_users; - struct completion wseq; - struct snd_soc_jack *mic_jack; int mic_det; int mic_short; @@ -246,6 +247,12 @@ static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int re case WM8903_REVISION_NUMBER: case WM8903_INTERRUPT_STATUS_1: case WM8903_WRITE_SEQUENCER_4: + case WM8903_POWER_MANAGEMENT_3: + case WM8903_POWER_MANAGEMENT_2: + case WM8903_DC_SERVO_READBACK_1: + case WM8903_DC_SERVO_READBACK_2: + case WM8903_DC_SERVO_READBACK_3: + case WM8903_DC_SERVO_READBACK_4: return 1; default: @@ -253,50 +260,6 @@ static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int re } } -static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start) -{ - u16 reg[5]; - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - - BUG_ON(start > 48); - - /* Enable the sequencer if it's not already on */ - reg[0] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_0); - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, - reg[0] | WM8903_WSEQ_ENA); - - dev_dbg(codec->dev, "Starting sequence at %d\n", start); - - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_3, - start | WM8903_WSEQ_START); - - /* Wait for it to complete. If we have the interrupt wired up then - * that will break us out of the poll early. - */ - do { - wait_for_completion_timeout(&wm8903->wseq, - msecs_to_jiffies(10)); - - reg[4] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_4); - } while (reg[4] & WM8903_WSEQ_BUSY); - - dev_dbg(codec->dev, "Sequence complete\n"); - - /* Disable the sequencer again if we enabled it */ - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]); - - return 0; -} - -static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache) -{ - int i; - - /* There really ought to be something better we can do here :/ */ - for (i = 0; i < ARRAY_SIZE(wm8903_reg_defaults); i++) - cache[i] = codec->hw_read(codec, i); -} - static void wm8903_reset(struct snd_soc_codec *codec) { snd_soc_write(codec, WM8903_SW_RESET_AND_ID, 0); @@ -304,11 +267,6 @@ static void wm8903_reset(struct snd_soc_codec *codec) sizeof(wm8903_reg_defaults)); } -#define WM8903_OUTPUT_SHORT 0x8 -#define WM8903_OUTPUT_OUT 0x4 -#define WM8903_OUTPUT_INT 0x2 -#define WM8903_OUTPUT_IN 0x1 - static int wm8903_cp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -318,97 +276,101 @@ static int wm8903_cp_event(struct snd_soc_dapm_widget *w, return 0; } -/* - * Event for headphone and line out amplifier power changes. Special - * power up/down sequences are required in order to maximise pop/click - * performance. - */ -static int wm8903_output_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) +static int wm8903_dcs_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = w->codec; - u16 val; - u16 reg; - u16 dcs_reg; - u16 dcs_bit; - int shift; + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - switch (w->reg) { - case WM8903_POWER_MANAGEMENT_2: - reg = WM8903_ANALOGUE_HP_0; - dcs_bit = 0 + w->shift; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wm8903->dcs_pending |= 1 << w->shift; break; - case WM8903_POWER_MANAGEMENT_3: - reg = WM8903_ANALOGUE_LINEOUT_0; - dcs_bit = 2 + w->shift; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WM8903_DC_SERVO_0, + 1 << w->shift, 0); break; - default: - BUG(); - return -EINVAL; /* Spurious warning from some compilers */ } - switch (w->shift) { - case 0: - shift = 0; - break; - case 1: - shift = 4; - break; - default: - BUG(); - return -EINVAL; /* Spurious warning from some compilers */ - } + return 0; +} - if (event & SND_SOC_DAPM_PRE_PMU) { - val = snd_soc_read(codec, reg); +#define WM8903_DCS_MODE_WRITE_STOP 0 +#define WM8903_DCS_MODE_START_STOP 2 - /* Short the output */ - val &= ~(WM8903_OUTPUT_SHORT << shift); - snd_soc_write(codec, reg, val); - } +static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm, + enum snd_soc_dapm_type event, int subseq) +{ + struct snd_soc_codec *codec = container_of(dapm, + struct snd_soc_codec, dapm); + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + int dcs_mode = WM8903_DCS_MODE_WRITE_STOP; + int i, val; - if (event & SND_SOC_DAPM_POST_PMU) { - val = snd_soc_read(codec, reg); + /* Complete any pending DC servo starts */ + if (wm8903->dcs_pending) { + dev_dbg(codec->dev, "Starting DC servo for %x\n", + wm8903->dcs_pending); - val |= (WM8903_OUTPUT_IN << shift); - snd_soc_write(codec, reg, val); + /* If we've no cached values then we need to do startup */ + for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) { + if (!(wm8903->dcs_pending & (1 << i))) + continue; - val |= (WM8903_OUTPUT_INT << shift); - snd_soc_write(codec, reg, val); + if (wm8903->dcs_cache[i]) { + dev_dbg(codec->dev, + "Restore DC servo %d value %x\n", + 3 - i, wm8903->dcs_cache[i]); + + snd_soc_write(codec, WM8903_DC_SERVO_4 + i, + wm8903->dcs_cache[i] & 0xff); + } else { + dev_dbg(codec->dev, + "Calibrate DC servo %d\n", 3 - i); + dcs_mode = WM8903_DCS_MODE_START_STOP; + } + } - /* Turn on the output ENA_OUTP */ - val |= (WM8903_OUTPUT_OUT << shift); - snd_soc_write(codec, reg, val); + /* Don't trust the cache for analogue */ + if (wm8903->class_w_users) + dcs_mode = WM8903_DCS_MODE_START_STOP; - /* Enable the DC servo */ - dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0); - dcs_reg |= dcs_bit; - snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg); + snd_soc_update_bits(codec, WM8903_DC_SERVO_2, + WM8903_DCS_MODE_MASK, dcs_mode); - /* Remove the short */ - val |= (WM8903_OUTPUT_SHORT << shift); - snd_soc_write(codec, reg, val); - } + snd_soc_update_bits(codec, WM8903_DC_SERVO_0, + WM8903_DCS_ENA_MASK, wm8903->dcs_pending); - if (event & SND_SOC_DAPM_PRE_PMD) { - val = snd_soc_read(codec, reg); + switch (dcs_mode) { + case WM8903_DCS_MODE_WRITE_STOP: + break; - /* Short the output */ - val &= ~(WM8903_OUTPUT_SHORT << shift); - snd_soc_write(codec, reg, val); + case WM8903_DCS_MODE_START_STOP: + msleep(270); - /* Disable the DC servo */ - dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0); - dcs_reg &= ~dcs_bit; - snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg); + /* Cache the measured offsets for digital */ + if (wm8903->class_w_users) + break; - /* Then disable the intermediate and output stages */ - val &= ~((WM8903_OUTPUT_OUT | WM8903_OUTPUT_INT | - WM8903_OUTPUT_IN) << shift); - snd_soc_write(codec, reg, val); - } + for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) { + if (!(wm8903->dcs_pending & (1 << i))) + continue; - return 0; + val = snd_soc_read(codec, + WM8903_DC_SERVO_READBACK_1 + i); + dev_dbg(codec->dev, "DC servo %d: %x\n", + 3 - i, val); + wm8903->dcs_cache[i] = val; + } + break; + + default: + pr_warn("DCS mode %d delay not set\n", dcs_mode); + break; + } + + wm8903->dcs_pending = 0; + } } /* @@ -674,6 +636,22 @@ static const struct soc_enum lsidetone_enum = static const struct soc_enum rsidetone_enum = SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text); +static const char *aif_text[] = { + "Left", "Right" +}; + +static const struct soc_enum lcapture_enum = + SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 7, 2, aif_text); + +static const struct soc_enum rcapture_enum = + SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 6, 2, aif_text); + +static const struct soc_enum lplay_enum = + SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 5, 2, aif_text); + +static const struct soc_enum rplay_enum = + SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 4, 2, aif_text); + static const struct snd_kcontrol_new wm8903_snd_controls[] = { /* Input PGAs - No TLV since the scale depends on PGA mode */ @@ -791,6 +769,18 @@ static const struct snd_kcontrol_new lsidetone_mux = static const struct snd_kcontrol_new rsidetone_mux = SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum); +static const struct snd_kcontrol_new lcapture_mux = + SOC_DAPM_ENUM("Left Capture Mux", lcapture_enum); + +static const struct snd_kcontrol_new rcapture_mux = + SOC_DAPM_ENUM("Right Capture Mux", rcapture_enum); + +static const struct snd_kcontrol_new lplay_mux = + SOC_DAPM_ENUM("Left Playback Mux", lplay_enum); + +static const struct snd_kcontrol_new rplay_mux = + SOC_DAPM_ENUM("Right Playback Mux", rplay_enum); + static const struct snd_kcontrol_new left_output_mixer[] = { SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0), SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0), @@ -854,14 +844,26 @@ SND_SOC_DAPM_MUX("Right Input Mode Mux", SND_SOC_NOPM, 0, 0, &rinput_mode_mux), SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0), SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0), -SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8903_POWER_MANAGEMENT_6, 1, 0), -SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8903_POWER_MANAGEMENT_6, 0, 0), +SND_SOC_DAPM_ADC("ADCL", NULL, WM8903_POWER_MANAGEMENT_6, 1, 0), +SND_SOC_DAPM_ADC("ADCR", NULL, WM8903_POWER_MANAGEMENT_6, 0, 0), + +SND_SOC_DAPM_MUX("Left Capture Mux", SND_SOC_NOPM, 0, 0, &lcapture_mux), +SND_SOC_DAPM_MUX("Right Capture Mux", SND_SOC_NOPM, 0, 0, &rcapture_mux), + +SND_SOC_DAPM_AIF_OUT("AIFTXL", "Left HiFi Capture", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIFTXR", "Right HiFi Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &lsidetone_mux), SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &rsidetone_mux), -SND_SOC_DAPM_DAC("DACL", "Left Playback", WM8903_POWER_MANAGEMENT_6, 3, 0), -SND_SOC_DAPM_DAC("DACR", "Right Playback", WM8903_POWER_MANAGEMENT_6, 2, 0), +SND_SOC_DAPM_AIF_IN("AIFRXL", "Left Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIFRXR", "Right Playback", 0, SND_SOC_NOPM, 0, 0), + +SND_SOC_DAPM_MUX("Left Playback Mux", SND_SOC_NOPM, 0, 0, &lplay_mux), +SND_SOC_DAPM_MUX("Right Playback Mux", SND_SOC_NOPM, 0, 0, &rplay_mux), + +SND_SOC_DAPM_DAC("DACL", NULL, WM8903_POWER_MANAGEMENT_6, 3, 0), +SND_SOC_DAPM_DAC("DACR", NULL, WM8903_POWER_MANAGEMENT_6, 2, 0), SND_SOC_DAPM_MIXER("Left Output Mixer", WM8903_POWER_MANAGEMENT_1, 1, 0, left_output_mixer, ARRAY_SIZE(left_output_mixer)), @@ -873,23 +875,45 @@ SND_SOC_DAPM_MIXER("Left Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 1, 0, SND_SOC_DAPM_MIXER("Right Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 0, 0, right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), -SND_SOC_DAPM_PGA_E("Left Headphone Output PGA", WM8903_POWER_MANAGEMENT_2, - 1, 0, NULL, 0, wm8903_output_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), -SND_SOC_DAPM_PGA_E("Right Headphone Output PGA", WM8903_POWER_MANAGEMENT_2, - 0, 0, NULL, 0, wm8903_output_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), - -SND_SOC_DAPM_PGA_E("Left Line Output PGA", WM8903_POWER_MANAGEMENT_3, 1, 0, - NULL, 0, wm8903_output_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), -SND_SOC_DAPM_PGA_E("Right Line Output PGA", WM8903_POWER_MANAGEMENT_3, 0, 0, - NULL, 0, wm8903_output_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("Left Headphone Output PGA", 0, WM8903_ANALOGUE_HP_0, + 4, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("Right Headphone Output PGA", 0, WM8903_ANALOGUE_HP_0, + 0, 0, NULL, 0), + +SND_SOC_DAPM_PGA_S("Left Line Output PGA", 0, WM8903_ANALOGUE_LINEOUT_0, 4, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("Right Line Output PGA", 0, WM8903_ANALOGUE_LINEOUT_0, 0, 0, + NULL, 0), + +SND_SOC_DAPM_PGA_S("HPL_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 7, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPL_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPL_ENA_DLY", 1, WM8903_ANALOGUE_HP_0, 5, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPR_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 3, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPR_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPR_ENA_DLY", 1, WM8903_ANALOGUE_HP_0, 1, 0, NULL, 0), + +SND_SOC_DAPM_PGA_S("LINEOUTL_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 7, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 6, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_DLY", 1, WM8903_ANALOGUE_LINEOUT_0, 5, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTR_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 3, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 2, 0, + NULL, 0), +SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_DLY", 1, WM8903_ANALOGUE_LINEOUT_0, 1, 0, + NULL, 0), + +SND_SOC_DAPM_SUPPLY("DCS Master", WM8903_DC_SERVO_0, 4, 0, NULL, 0), +SND_SOC_DAPM_PGA_S("HPL_DCS", 3, SND_SOC_NOPM, 3, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("HPR_DCS", 3, SND_SOC_NOPM, 2, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("LINEOUTL_DCS", 3, SND_SOC_NOPM, 1, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA_S("LINEOUTR_DCS", 3, SND_SOC_NOPM, 0, 0, wm8903_dcs_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0, NULL, 0), @@ -899,10 +923,18 @@ SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0, SND_SOC_DAPM_SUPPLY("Charge Pump", WM8903_CHARGE_PUMP_0, 0, 0, wm8903_cp_event, SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8903_CLOCK_RATES_2, 1, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0), }; static const struct snd_soc_dapm_route intercon[] = { + { "CLK_DSP", NULL, "CLK_SYS" }, + { "Mic Bias", NULL, "CLK_SYS" }, + { "HPL_DCS", NULL, "CLK_SYS" }, + { "HPR_DCS", NULL, "CLK_SYS" }, + { "LINEOUTL_DCS", NULL, "CLK_SYS" }, + { "LINEOUTR_DCS", NULL, "CLK_SYS" }, + { "Left Input Mux", "IN1L", "IN1L" }, { "Left Input Mux", "IN2L", "IN2L" }, { "Left Input Mux", "IN3L", "IN3L" }, @@ -943,18 +975,36 @@ static const struct snd_soc_dapm_route intercon[] = { { "Left Input PGA", NULL, "Left Input Mode Mux" }, { "Right Input PGA", NULL, "Right Input Mode Mux" }, + { "Left Capture Mux", "Left", "ADCL" }, + { "Left Capture Mux", "Right", "ADCR" }, + + { "Right Capture Mux", "Left", "ADCL" }, + { "Right Capture Mux", "Right", "ADCR" }, + + { "AIFTXL", NULL, "Left Capture Mux" }, + { "AIFTXR", NULL, "Right Capture Mux" }, + { "ADCL", NULL, "Left Input PGA" }, { "ADCL", NULL, "CLK_DSP" }, { "ADCR", NULL, "Right Input PGA" }, { "ADCR", NULL, "CLK_DSP" }, + { "Left Playback Mux", "Left", "AIFRXL" }, + { "Left Playback Mux", "Right", "AIFRXR" }, + + { "Right Playback Mux", "Left", "AIFRXL" }, + { "Right Playback Mux", "Right", "AIFRXR" }, + { "DACL Sidetone", "Left", "ADCL" }, { "DACL Sidetone", "Right", "ADCR" }, { "DACR Sidetone", "Left", "ADCL" }, { "DACR Sidetone", "Right", "ADCR" }, + { "DACL", NULL, "Left Playback Mux" }, { "DACL", NULL, "DACL Sidetone" }, { "DACL", NULL, "CLK_DSP" }, + + { "DACR", NULL, "Right Playback Mux" }, { "DACR", NULL, "DACR Sidetone" }, { "DACR", NULL, "CLK_DSP" }, @@ -987,11 +1037,35 @@ static const struct snd_soc_dapm_route intercon[] = { { "Left Speaker PGA", NULL, "Left Speaker Mixer" }, { "Right Speaker PGA", NULL, "Right Speaker Mixer" }, - { "HPOUTL", NULL, "Left Headphone Output PGA" }, - { "HPOUTR", NULL, "Right Headphone Output PGA" }, + { "HPL_ENA_DLY", NULL, "Left Headphone Output PGA" }, + { "HPR_ENA_DLY", NULL, "Right Headphone Output PGA" }, + { "LINEOUTL_ENA_DLY", NULL, "Left Line Output PGA" }, + { "LINEOUTR_ENA_DLY", NULL, "Right Line Output PGA" }, + + { "HPL_DCS", NULL, "DCS Master" }, + { "HPR_DCS", NULL, "DCS Master" }, + { "LINEOUTL_DCS", NULL, "DCS Master" }, + { "LINEOUTR_DCS", NULL, "DCS Master" }, - { "LINEOUTL", NULL, "Left Line Output PGA" }, - { "LINEOUTR", NULL, "Right Line Output PGA" }, + { "HPL_DCS", NULL, "HPL_ENA_DLY" }, + { "HPR_DCS", NULL, "HPR_ENA_DLY" }, + { "LINEOUTL_DCS", NULL, "LINEOUTL_ENA_DLY" }, + { "LINEOUTR_DCS", NULL, "LINEOUTR_ENA_DLY" }, + + { "HPL_ENA_OUTP", NULL, "HPL_DCS" }, + { "HPR_ENA_OUTP", NULL, "HPR_DCS" }, + { "LINEOUTL_ENA_OUTP", NULL, "LINEOUTL_DCS" }, + { "LINEOUTR_ENA_OUTP", NULL, "LINEOUTR_DCS" }, + + { "HPL_RMV_SHORT", NULL, "HPL_ENA_OUTP" }, + { "HPR_RMV_SHORT", NULL, "HPR_ENA_OUTP" }, + { "LINEOUTL_RMV_SHORT", NULL, "LINEOUTL_ENA_OUTP" }, + { "LINEOUTR_RMV_SHORT", NULL, "LINEOUTR_ENA_OUTP" }, + + { "HPOUTL", NULL, "HPL_RMV_SHORT" }, + { "HPOUTR", NULL, "HPR_RMV_SHORT" }, + { "LINEOUTL", NULL, "LINEOUTL_RMV_SHORT" }, + { "LINEOUTR", NULL, "LINEOUTR_RMV_SHORT" }, { "LOP", NULL, "Left Speaker PGA" }, { "LON", NULL, "Left Speaker PGA" }, @@ -1019,29 +1093,71 @@ static int wm8903_add_widgets(struct snd_soc_codec *codec) static int wm8903_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - u16 reg; - switch (level) { case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: - reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0); - reg &= ~(WM8903_VMID_RES_MASK); - reg |= WM8903_VMID_RES_50K; - snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg); + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_RES_MASK, + WM8903_VMID_RES_50K); break; case SND_SOC_BIAS_STANDBY: if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { - snd_soc_write(codec, WM8903_CLOCK_RATES_2, - WM8903_CLK_SYS_ENA); - - /* Change DC servo dither level in startup sequence */ - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, 0x11); - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_1, 0x1257); - snd_soc_write(codec, WM8903_WRITE_SEQUENCER_2, 0x2); - - wm8903_run_sequence(codec, 0); - wm8903_sync_reg_cache(codec, codec->reg_cache); + snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0, + WM8903_POBCTRL | WM8903_ISEL_MASK | + WM8903_STARTUP_BIAS_ENA | + WM8903_BIAS_ENA, + WM8903_POBCTRL | + (2 << WM8903_ISEL_SHIFT) | + WM8903_STARTUP_BIAS_ENA); + + snd_soc_update_bits(codec, + WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0, + WM8903_SPK_DISCHARGE, + WM8903_SPK_DISCHARGE); + + msleep(33); + + snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5, + WM8903_SPKL_ENA | WM8903_SPKR_ENA, + WM8903_SPKL_ENA | WM8903_SPKR_ENA); + + snd_soc_update_bits(codec, + WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0, + WM8903_SPK_DISCHARGE, 0); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_TIE_ENA | + WM8903_BUFIO_ENA | + WM8903_VMID_IO_ENA | + WM8903_VMID_SOFT_MASK | + WM8903_VMID_RES_MASK | + WM8903_VMID_BUF_ENA, + WM8903_VMID_TIE_ENA | + WM8903_BUFIO_ENA | + WM8903_VMID_IO_ENA | + (2 << WM8903_VMID_SOFT_SHIFT) | + WM8903_VMID_RES_250K | + WM8903_VMID_BUF_ENA); + + msleep(129); + + snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5, + WM8903_SPKL_ENA | WM8903_SPKR_ENA, + 0); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_SOFT_MASK, 0); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_RES_MASK, + WM8903_VMID_RES_50K); + + snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0, + WM8903_BIAS_ENA | WM8903_POBCTRL, + WM8903_BIAS_ENA); /* By default no bypass paths are enabled so * enable Class W support. @@ -1054,17 +1170,32 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec, WM8903_CP_DYN_V); } - reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0); - reg &= ~(WM8903_VMID_RES_MASK); - reg |= WM8903_VMID_RES_250K; - snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg); + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_RES_MASK, + WM8903_VMID_RES_250K); break; case SND_SOC_BIAS_OFF: - wm8903_run_sequence(codec, 32); - reg = snd_soc_read(codec, WM8903_CLOCK_RATES_2); - reg &= ~WM8903_CLK_SYS_ENA; - snd_soc_write(codec, WM8903_CLOCK_RATES_2, reg); + snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0, + WM8903_BIAS_ENA, 0); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_SOFT_MASK, + 2 << WM8903_VMID_SOFT_SHIFT); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_BUF_ENA, 0); + + msleep(290); + + snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0, + WM8903_VMID_TIE_ENA | WM8903_BUFIO_ENA | + WM8903_VMID_IO_ENA | WM8903_VMID_RES_MASK | + WM8903_VMID_SOFT_MASK | + WM8903_VMID_BUF_ENA, 0); + + snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0, + WM8903_STARTUP_BIAS_ENA, 0); break; } @@ -1489,7 +1620,7 @@ int wm8903_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, WM8903_MICDET_EINT | WM8903_MICSHRT_EINT, irq_mask); - if (det && shrt) { + if (det || shrt) { /* Enable mic detection, this may not have been set through * platform data (eg, if the defaults are OK). */ snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0, @@ -1517,8 +1648,7 @@ static irqreturn_t wm8903_irq(int irq, void *data) int_val = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1) & mask; if (int_val & WM8903_WSEQ_BUSY_EINT) { - dev_dbg(codec->dev, "Write sequencer done\n"); - complete(&wm8903->wseq); + dev_warn(codec->dev, "Write sequencer done\n"); } /* @@ -1765,7 +1895,6 @@ static int wm8903_probe(struct snd_soc_codec *codec) u16 val; wm8903->codec = codec; - init_completion(&wm8903->wseq); ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C); if (ret != 0) { @@ -1781,19 +1910,33 @@ static int wm8903_probe(struct snd_soc_codec *codec) } val = snd_soc_read(codec, WM8903_REVISION_NUMBER); - dev_info(codec->dev, "WM8903 revision %d\n", - val & WM8903_CHIP_REV_MASK); + dev_info(codec->dev, "WM8903 revision %c\n", + (val & WM8903_CHIP_REV_MASK) + 'A'); wm8903_reset(codec); /* Set up GPIOs and microphone detection */ if (pdata) { + bool mic_gpio = false; + for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) { if (pdata->gpio_cfg[i] == WM8903_GPIO_NO_CONFIG) continue; snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i, pdata->gpio_cfg[i] & 0xffff); + + val = (pdata->gpio_cfg[i] & WM8903_GP1_FN_MASK) + >> WM8903_GP1_FN_SHIFT; + + switch (val) { + case WM8903_GPn_FN_MICBIAS_CURRENT_DETECT: + case WM8903_GPn_FN_MICBIAS_SHORT_DETECT: + mic_gpio = true; + break; + default: + break; + } } snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0, @@ -1804,6 +1947,14 @@ static int wm8903_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0, WM8903_WSEQ_ENA, WM8903_WSEQ_ENA); + /* If microphone detection is enabled by pdata but + * detected via IRQ then interrupts can be lost before + * the machine driver has set up microphone detection + * IRQs as the IRQs are clear on read. The detection + * will be enabled when the machine driver configures. + */ + WARN_ON(!mic_gpio && (pdata->micdet_cfg & WM8903_MICDET_ENA)); + wm8903->mic_delay = pdata->micdet_delay; } @@ -1863,9 +2014,9 @@ static int wm8903_probe(struct snd_soc_codec *codec) snd_soc_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val); /* Enable DAC soft mute by default */ - val = snd_soc_read(codec, WM8903_DAC_DIGITAL_1); - val |= WM8903_DAC_MUTEMODE; - snd_soc_write(codec, WM8903_DAC_DIGITAL_1, val); + snd_soc_update_bits(codec, WM8903_DAC_DIGITAL_1, + WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE, + WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE); snd_soc_add_controls(codec, wm8903_snd_controls, ARRAY_SIZE(wm8903_snd_controls)); @@ -1894,6 +2045,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8903 = { .reg_word_size = sizeof(u16), .reg_cache_default = wm8903_reg_defaults, .volatile_register = wm8903_volatile_register, + .seq_notifier = wm8903_seq_notifier, }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) @@ -1932,7 +2084,7 @@ MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id); static struct i2c_driver wm8903_i2c_driver = { .driver = { - .name = "wm8903-codec", + .name = "wm8903", .owner = THIS_MODULE, }, .probe = wm8903_i2c_probe, diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h index e8490f3edd0..db949311c0f 100644 --- a/sound/soc/codecs/wm8903.h +++ b/sound/soc/codecs/wm8903.h @@ -75,6 +75,14 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec, #define WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0 0x41 #define WM8903_DC_SERVO_0 0x43 #define WM8903_DC_SERVO_2 0x45 +#define WM8903_DC_SERVO_4 0x47 +#define WM8903_DC_SERVO_5 0x48 +#define WM8903_DC_SERVO_6 0x49 +#define WM8903_DC_SERVO_7 0x4A +#define WM8903_DC_SERVO_READBACK_1 0x51 +#define WM8903_DC_SERVO_READBACK_2 0x52 +#define WM8903_DC_SERVO_READBACK_3 0x53 +#define WM8903_DC_SERVO_READBACK_4 0x54 #define WM8903_ANALOGUE_HP_0 0x5A #define WM8903_ANALOGUE_LINEOUT_0 0x5E #define WM8903_CHARGE_PUMP_0 0x62 @@ -165,7 +173,7 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec, #define WM8903_VMID_RES_50K 2 #define WM8903_VMID_RES_250K 3 -#define WM8903_VMID_RES_5K 4 +#define WM8903_VMID_RES_5K 6 /* * R8 (0x08) - Analogue DAC 0 diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index 30fb48ec279..85e3e630e76 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -93,6 +93,7 @@ static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0); static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0); static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1); +static const DECLARE_TLV_DB_SCALE(limiter_tlv, 0, 100, 0); static const struct snd_kcontrol_new wm8978_snd_controls[] = { @@ -144,19 +145,19 @@ static const struct snd_kcontrol_new wm8978_snd_controls[] = { SOC_SINGLE("DAC Playback Limiter Threshold", WM8978_DAC_LIMITER_2, 4, 7, 0), - SOC_SINGLE("DAC Playback Limiter Boost", - WM8978_DAC_LIMITER_2, 0, 15, 0), + SOC_SINGLE_TLV("DAC Playback Limiter Volume", + WM8978_DAC_LIMITER_2, 0, 12, 0, limiter_tlv), SOC_ENUM("ALC Enable Switch", alc1), SOC_SINGLE("ALC Capture Min Gain", WM8978_ALC_CONTROL_1, 0, 7, 0), SOC_SINGLE("ALC Capture Max Gain", WM8978_ALC_CONTROL_1, 3, 7, 0), - SOC_SINGLE("ALC Capture Hold", WM8978_ALC_CONTROL_2, 4, 7, 0), + SOC_SINGLE("ALC Capture Hold", WM8978_ALC_CONTROL_2, 4, 10, 0), SOC_SINGLE("ALC Capture Target", WM8978_ALC_CONTROL_2, 0, 15, 0), SOC_ENUM("ALC Capture Mode", alc3), - SOC_SINGLE("ALC Capture Decay", WM8978_ALC_CONTROL_3, 4, 15, 0), - SOC_SINGLE("ALC Capture Attack", WM8978_ALC_CONTROL_3, 0, 15, 0), + SOC_SINGLE("ALC Capture Decay", WM8978_ALC_CONTROL_3, 4, 10, 0), + SOC_SINGLE("ALC Capture Attack", WM8978_ALC_CONTROL_3, 0, 10, 0), SOC_SINGLE("ALC Capture Noise Gate Switch", WM8978_NOISE_GATE, 3, 1, 0), SOC_SINGLE("ALC Capture Noise Gate Threshold", @@ -211,8 +212,10 @@ static const struct snd_kcontrol_new wm8978_snd_controls[] = { WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL, 6, 1, 1), /* DAC / ADC oversampling */ - SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL, 8, 1, 0), - SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL, 8, 1, 0), + SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL, + 5, 1, 0), + SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL, + 5, 1, 0), }; /* Mixer #1: Output (OUT1, OUT2) Mixer: mix AUX, Input mixer output and DAC */ diff --git a/sound/soc/codecs/wm8994-tables.c b/sound/soc/codecs/wm8994-tables.c index 68e9b024dd4..a87adbd05ee 100644 --- a/sound/soc/codecs/wm8994-tables.c +++ b/sound/soc/codecs/wm8994-tables.c @@ -62,8 +62,8 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = { { 0x00FF, 0x00FF }, /* R58 - MICBIAS */ { 0x000F, 0x000F }, /* R59 - LDO 1 */ { 0x0007, 0x0007 }, /* R60 - LDO 2 */ - { 0x0000, 0x0000 }, /* R61 */ - { 0x0000, 0x0000 }, /* R62 */ + { 0xFFFF, 0xFFFF }, /* R61 */ + { 0xFFFF, 0xFFFF }, /* R62 */ { 0x0000, 0x0000 }, /* R63 */ { 0x0000, 0x0000 }, /* R64 */ { 0x0000, 0x0000 }, /* R65 */ @@ -209,9 +209,9 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = { { 0x0000, 0x0000 }, /* R205 */ { 0x0000, 0x0000 }, /* R206 */ { 0x0000, 0x0000 }, /* R207 */ - { 0x0000, 0x0000 }, /* R208 */ - { 0x0000, 0x0000 }, /* R209 */ - { 0x0000, 0x0000 }, /* R210 */ + { 0xFFFF, 0xFFFF }, /* R208 */ + { 0xFFFF, 0xFFFF }, /* R209 */ + { 0xFFFF, 0xFFFF }, /* R210 */ { 0x0000, 0x0000 }, /* R211 */ { 0x0000, 0x0000 }, /* R212 */ { 0x0000, 0x0000 }, /* R213 */ @@ -1573,7 +1573,7 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = { { 0x03C3, 0x03C3 }, /* R1569 - Sidetone */ }; -const __devinitdata u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = { +const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = { 0x8994, /* R0 - Software Reset */ 0x0000, /* R1 - Power Management (1) */ 0x6000, /* R2 - Power Management (2) */ diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 0ca81d3c64e..3dc64c8b6a5 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -102,11 +102,16 @@ struct wm8994_priv { wm8958_micdet_cb jack_cb; void *jack_cb_data; - bool jack_is_mic; - bool jack_is_video; + int micdet_irq; int revision; struct wm8994_pdata *pdata; + + unsigned int aif1clk_enable:1; + unsigned int aif2clk_enable:1; + + unsigned int aif1clk_disable:1; + unsigned int aif2clk_disable:1; }; static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg) @@ -523,7 +528,7 @@ static int wm8994_get_retune_mobile_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct wm8994_priv *wm8994 =snd_soc_codec_get_drvdata(codec); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); int block = wm8994_get_retune_mobile_block(kcontrol->id.name); ucontrol->value.enumerated.item[0] = wm8994->retune_mobile_cfg[block]; @@ -1004,6 +1009,117 @@ static void wm8994_update_class_w(struct snd_soc_codec *codec) } } +static int late_enable_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wm8994->aif1clk_enable) { + snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1, + WM8994_AIF1CLK_ENA_MASK, + WM8994_AIF1CLK_ENA); + wm8994->aif1clk_enable = 0; + } + if (wm8994->aif2clk_enable) { + snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1, + WM8994_AIF2CLK_ENA_MASK, + WM8994_AIF2CLK_ENA); + wm8994->aif2clk_enable = 0; + } + break; + } + + return 0; +} + +static int late_disable_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + if (wm8994->aif1clk_disable) { + snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1, + WM8994_AIF1CLK_ENA_MASK, 0); + wm8994->aif1clk_disable = 0; + } + if (wm8994->aif2clk_disable) { + snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1, + WM8994_AIF2CLK_ENA_MASK, 0); + wm8994->aif2clk_disable = 0; + } + break; + } + + return 0; +} + +static int aif1clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wm8994->aif1clk_enable = 1; + break; + case SND_SOC_DAPM_POST_PMD: + wm8994->aif1clk_disable = 1; + break; + } + + return 0; +} + +static int aif2clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wm8994->aif2clk_enable = 1; + break; + case SND_SOC_DAPM_POST_PMD: + wm8994->aif2clk_disable = 1; + break; + } + + return 0; +} + +static int adc_mux_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + late_enable_ev(w, kcontrol, event); + return 0; +} + +static int micbias_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + late_enable_ev(w, kcontrol, event); + return 0; +} + +static int dac_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + unsigned int mask = 1 << w->shift; + + snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5, + mask, mask); + return 0; +} + static const char *hp_mux_text[] = { "Mixer", "DAC", @@ -1272,11 +1388,68 @@ static const struct soc_enum aif2dacr_src_enum = static const struct snd_kcontrol_new aif2dacr_src_mux = SOC_DAPM_ENUM("AIF2DACR Mux", aif2dacr_src_enum); +static const struct snd_soc_dapm_widget wm8994_lateclk_revd_widgets[] = { +SND_SOC_DAPM_SUPPLY("AIF1CLK", SND_SOC_NOPM, 0, 0, aif1clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF2CLK", SND_SOC_NOPM, 0, 0, aif2clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_PGA_E("Late DAC1L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("Late DAC1R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("Late DAC2L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("Late DAC2R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), + +SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev) +}; + +static const struct snd_soc_dapm_widget wm8994_lateclk_widgets[] = { +SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0) +}; + +static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = { +SND_SOC_DAPM_DAC_E("DAC2L", NULL, SND_SOC_NOPM, 3, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_DAC_E("DAC2R", NULL, SND_SOC_NOPM, 2, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_DAC_E("DAC1L", NULL, SND_SOC_NOPM, 1, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_DAC_E("DAC1R", NULL, SND_SOC_NOPM, 0, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +}; + +static const struct snd_soc_dapm_widget wm8994_dac_widgets[] = { +SND_SOC_DAPM_DAC("DAC2L", NULL, WM8994_POWER_MANAGEMENT_5, 3, 0), +SND_SOC_DAPM_DAC("DAC2R", NULL, WM8994_POWER_MANAGEMENT_5, 2, 0), +SND_SOC_DAPM_DAC("DAC1L", NULL, WM8994_POWER_MANAGEMENT_5, 1, 0), +SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 0, 0), +}; + +static const struct snd_soc_dapm_widget wm8994_adc_revd_widgets[] = { +SND_SOC_DAPM_MUX_E("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux, + adc_mux_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_MUX_E("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux, + adc_mux_ev, SND_SOC_DAPM_PRE_PMU), +}; + +static const struct snd_soc_dapm_widget wm8994_adc_widgets[] = { +SND_SOC_DAPM_MUX("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux), +SND_SOC_DAPM_MUX("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux), +}; + static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = { SND_SOC_DAPM_INPUT("DMIC1DAT"), SND_SOC_DAPM_INPUT("DMIC2DAT"), SND_SOC_DAPM_INPUT("Clock"), +SND_SOC_DAPM_MICBIAS("MICBIAS", WM8994_MICBIAS, 2, 0), +SND_SOC_DAPM_SUPPLY_S("MICBIAS Supply", 1, SND_SOC_NOPM, 0, 0, micbias_ev, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), @@ -1284,12 +1457,9 @@ SND_SOC_DAPM_SUPPLY("DSP1CLK", WM8994_CLOCKING_1, 3, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("DSP2CLK", WM8994_CLOCKING_1, 2, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("DSPINTCLK", WM8994_CLOCKING_1, 1, 0, NULL, 0), -SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, NULL, 0), -SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0), - -SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", "AIF1 Capture", +SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", NULL, 0, WM8994_POWER_MANAGEMENT_4, 9, 0), -SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", "AIF1 Capture", +SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", NULL, 0, WM8994_POWER_MANAGEMENT_4, 8, 0), SND_SOC_DAPM_AIF_IN_E("AIF1DAC1L", NULL, 0, WM8994_POWER_MANAGEMENT_5, 9, 0, wm8958_aif_ev, @@ -1298,9 +1468,9 @@ SND_SOC_DAPM_AIF_IN_E("AIF1DAC1R", NULL, 0, WM8994_POWER_MANAGEMENT_5, 8, 0, wm8958_aif_ev, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), -SND_SOC_DAPM_AIF_OUT("AIF1ADC2L", "AIF1 Capture", +SND_SOC_DAPM_AIF_OUT("AIF1ADC2L", NULL, 0, WM8994_POWER_MANAGEMENT_4, 11, 0), -SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", "AIF1 Capture", +SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", NULL, 0, WM8994_POWER_MANAGEMENT_4, 10, 0), SND_SOC_DAPM_AIF_IN_E("AIF1DAC2L", NULL, 0, WM8994_POWER_MANAGEMENT_5, 11, 0, wm8958_aif_ev, @@ -1345,6 +1515,7 @@ SND_SOC_DAPM_AIF_IN_E("AIF2DACR", NULL, 0, SND_SOC_DAPM_AIF_IN("AIF1DACDAT", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_IN("AIF2DACDAT", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIF1ADCDAT", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("AIF2ADCDAT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_MUX("AIF1DAC Mux", SND_SOC_NOPM, 0, 0, &aif1dac_mux), @@ -1368,14 +1539,6 @@ SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0), SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0), SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0), -SND_SOC_DAPM_MUX("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux), -SND_SOC_DAPM_MUX("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux), - -SND_SOC_DAPM_DAC("DAC2L", NULL, WM8994_POWER_MANAGEMENT_5, 3, 0), -SND_SOC_DAPM_DAC("DAC2R", NULL, WM8994_POWER_MANAGEMENT_5, 2, 0), -SND_SOC_DAPM_DAC("DAC1L", NULL, WM8994_POWER_MANAGEMENT_5, 1, 0), -SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 0, 0), - SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux), SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux), @@ -1515,14 +1678,12 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF2ADC Mux", "AIF3DACDAT", "AIF3ADCDAT" }, /* DAC1 inputs */ - { "DAC1L", NULL, "DAC1L Mixer" }, { "DAC1L Mixer", "AIF2 Switch", "AIF2DACL" }, { "DAC1L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, { "DAC1L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" }, { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" }, - { "DAC1R", NULL, "DAC1R Mixer" }, { "DAC1R Mixer", "AIF2 Switch", "AIF2DACR" }, { "DAC1R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, { "DAC1R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, @@ -1531,7 +1692,6 @@ static const struct snd_soc_dapm_route intercon[] = { /* DAC2/AIF2 outputs */ { "AIF2ADCL", NULL, "AIF2DAC2L Mixer" }, - { "DAC2L", NULL, "AIF2DAC2L Mixer" }, { "AIF2DAC2L Mixer", "AIF2 Switch", "AIF2DACL" }, { "AIF2DAC2L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, { "AIF2DAC2L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, @@ -1539,13 +1699,17 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF2DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" }, { "AIF2ADCR", NULL, "AIF2DAC2R Mixer" }, - { "DAC2R", NULL, "AIF2DAC2R Mixer" }, { "AIF2DAC2R Mixer", "AIF2 Switch", "AIF2DACR" }, { "AIF2DAC2R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, { "AIF2DAC2R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, { "AIF2DAC2R Mixer", "Left Sidetone Switch", "Left Sidetone" }, { "AIF2DAC2R Mixer", "Right Sidetone Switch", "Right Sidetone" }, + { "AIF1ADCDAT", NULL, "AIF1ADC1L" }, + { "AIF1ADCDAT", NULL, "AIF1ADC1R" }, + { "AIF1ADCDAT", NULL, "AIF1ADC2L" }, + { "AIF1ADCDAT", NULL, "AIF1ADC2R" }, + { "AIF2ADCDAT", NULL, "AIF2ADC Mux" }, /* AIF3 output */ @@ -1578,6 +1742,33 @@ static const struct snd_soc_dapm_route intercon[] = { { "Right Headphone Mux", "DAC", "DAC1R" }, }; +static const struct snd_soc_dapm_route wm8994_lateclk_revd_intercon[] = { + { "DAC1L", NULL, "Late DAC1L Enable PGA" }, + { "Late DAC1L Enable PGA", NULL, "DAC1L Mixer" }, + { "DAC1R", NULL, "Late DAC1R Enable PGA" }, + { "Late DAC1R Enable PGA", NULL, "DAC1R Mixer" }, + { "DAC2L", NULL, "Late DAC2L Enable PGA" }, + { "Late DAC2L Enable PGA", NULL, "AIF2DAC2L Mixer" }, + { "DAC2R", NULL, "Late DAC2R Enable PGA" }, + { "Late DAC2R Enable PGA", NULL, "AIF2DAC2R Mixer" } +}; + +static const struct snd_soc_dapm_route wm8994_lateclk_intercon[] = { + { "DAC1L", NULL, "DAC1L Mixer" }, + { "DAC1R", NULL, "DAC1R Mixer" }, + { "DAC2L", NULL, "AIF2DAC2L Mixer" }, + { "DAC2R", NULL, "AIF2DAC2R Mixer" }, +}; + +static const struct snd_soc_dapm_route wm8994_revd_intercon[] = { + { "AIF1DACDAT", NULL, "AIF2DACDAT" }, + { "AIF2DACDAT", NULL, "AIF1DACDAT" }, + { "AIF1ADCDAT", NULL, "AIF2ADCDAT" }, + { "AIF2ADCDAT", NULL, "AIF1ADCDAT" }, + { "MICBIAS", NULL, "CLK_SYS" }, + { "MICBIAS", NULL, "MICBIAS Supply" }, +}; + static const struct snd_soc_dapm_route wm8994_intercon[] = { { "AIF2DACL", NULL, "AIF2DAC Mux" }, { "AIF2DACR", NULL, "AIF2DAC Mux" }, @@ -2501,6 +2692,22 @@ static int wm8994_resume(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); int i, ret; + unsigned int val, mask; + + if (wm8994->revision < 4) { + /* force a HW read */ + val = wm8994_reg_read(codec->control_data, + WM8994_POWER_MANAGEMENT_5); + + /* modify the cache only */ + codec->cache_only = 1; + mask = WM8994_DAC1R_ENA | WM8994_DAC1L_ENA | + WM8994_DAC2R_ENA | WM8994_DAC2L_ENA; + val &= mask; + snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5, + mask, val); + codec->cache_only = 0; + } /* Restore the registers */ ret = snd_soc_cache_sync(codec); @@ -2688,6 +2895,13 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) else snd_soc_add_controls(wm8994->codec, wm8994_eq_controls, ARRAY_SIZE(wm8994_eq_controls)); + + for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) { + if (pdata->micbias[i]) { + snd_soc_write(codec, WM8958_MICBIAS1 + i, + pdata->micbias[i] & 0xffff); + } + } } /** @@ -2798,47 +3012,18 @@ static void wm8958_default_micdet(u16 status, void *data) int report = 0; /* If nothing present then clear our statuses */ - if (!(status & WM8958_MICD_STS)) { - wm8994->jack_is_video = false; - wm8994->jack_is_mic = false; + if (!(status & WM8958_MICD_STS)) goto done; - } - - /* Assume anything over 475 ohms is a microphone and remember - * that we've seen one (since buttons override it) */ - if (status & 0x600) - wm8994->jack_is_mic = true; - if (wm8994->jack_is_mic) - report |= SND_JACK_MICROPHONE; - /* Video has an impedence of approximately 75 ohms; assume - * this isn't used as a button and remember it since buttons - * override it. */ - if (status & 0x40) - wm8994->jack_is_video = true; - if (wm8994->jack_is_video) - report |= SND_JACK_VIDEOOUT; + report = SND_JACK_MICROPHONE; /* Everything else is buttons; just assign slots */ - if (status & 0x4) + if (status & 0x1c0) report |= SND_JACK_BTN_0; - if (status & 0x8) - report |= SND_JACK_BTN_1; - if (status & 0x10) - report |= SND_JACK_BTN_2; - if (status & 0x20) - report |= SND_JACK_BTN_3; - if (status & 0x80) - report |= SND_JACK_BTN_4; - if (status & 0x100) - report |= SND_JACK_BTN_5; done: - snd_soc_jack_report(wm8994->micdet[0].jack, - SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | - SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5 | - SND_JACK_MICROPHONE | SND_JACK_VIDEOOUT, - report); + snd_soc_jack_report(wm8994->micdet[0].jack, report, + SND_JACK_BTN_0 | SND_JACK_MICROPHONE); } /** @@ -2937,6 +3122,12 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->pdata = dev_get_platdata(codec->dev->parent); wm8994->codec = codec; + if (wm8994->pdata && wm8994->pdata->micdet_irq) + wm8994->micdet_irq = wm8994->pdata->micdet_irq; + else if (wm8994->pdata && wm8994->pdata->irq_base) + wm8994->micdet_irq = wm8994->pdata->irq_base + + WM8994_IRQ_MIC1_DET; + pm_runtime_enable(codec->dev); pm_runtime_resume(codec->dev); @@ -2985,14 +3176,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) switch (control->type) { case WM8994: - ret = wm8994_request_irq(codec->control_data, - WM8994_IRQ_MIC1_DET, - wm8994_mic_irq, "Mic 1 detect", - wm8994); - if (ret != 0) - dev_warn(codec->dev, - "Failed to request Mic1 detect IRQ: %d\n", - ret); + if (wm8994->micdet_irq) { + ret = request_threaded_irq(wm8994->micdet_irq, NULL, + wm8994_mic_irq, + IRQF_TRIGGER_RISING, + "Mic1 detect", + wm8994); + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic1 detect IRQ: %d\n", + ret); + } ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, @@ -3023,15 +3217,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) break; case WM8958: - ret = wm8994_request_irq(codec->control_data, - WM8994_IRQ_MIC1_DET, - wm8958_mic_irq, "Mic detect", - wm8994); - if (ret != 0) - dev_warn(codec->dev, - "Failed to request Mic detect IRQ: %d\n", - ret); - break; + if (wm8994->micdet_irq) { + ret = request_threaded_irq(wm8994->micdet_irq, NULL, + wm8958_mic_irq, + IRQF_TRIGGER_RISING, + "Mic detect", + wm8994); + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic detect IRQ: %d\n", + ret); + } } /* Remember if AIFnLRCLK is configured as a GPIO. This should be @@ -3112,10 +3308,31 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) case WM8994: snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets, ARRAY_SIZE(wm8994_specific_dapm_widgets)); + if (wm8994->revision < 4) { + snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets, + ARRAY_SIZE(wm8994_lateclk_revd_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_adc_revd_widgets, + ARRAY_SIZE(wm8994_adc_revd_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_dac_revd_widgets, + ARRAY_SIZE(wm8994_dac_revd_widgets)); + } else { + snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets, + ARRAY_SIZE(wm8994_lateclk_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets, + ARRAY_SIZE(wm8994_adc_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets, + ARRAY_SIZE(wm8994_dac_widgets)); + } break; case WM8958: snd_soc_add_controls(codec, wm8958_snd_controls, ARRAY_SIZE(wm8958_snd_controls)); + snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets, + ARRAY_SIZE(wm8994_lateclk_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets, + ARRAY_SIZE(wm8994_adc_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets, + ARRAY_SIZE(wm8994_dac_widgets)); snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets, ARRAY_SIZE(wm8958_dapm_widgets)); break; @@ -3129,8 +3346,20 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) case WM8994: snd_soc_dapm_add_routes(dapm, wm8994_intercon, ARRAY_SIZE(wm8994_intercon)); + + if (wm8994->revision < 4) { + snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon, + ARRAY_SIZE(wm8994_revd_intercon)); + snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon, + ARRAY_SIZE(wm8994_lateclk_revd_intercon)); + } else { + snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon, + ARRAY_SIZE(wm8994_lateclk_intercon)); + } break; case WM8958: + snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon, + ARRAY_SIZE(wm8994_lateclk_intercon)); snd_soc_dapm_add_routes(dapm, wm8958_intercon, ARRAY_SIZE(wm8958_intercon)); break; @@ -3142,7 +3371,8 @@ err_irq: wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994); - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, wm8994); + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); err: kfree(wm8994); return ret; @@ -3159,8 +3389,8 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) switch (control->type) { case WM8994: - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, - wm8994); + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994); wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, @@ -3170,8 +3400,8 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) break; case WM8958: - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, - wm8994); + if (wm8994->micdet_irq) + free_irq(wm8994->micdet_irq, wm8994); break; } kfree(wm8994->retune_mobile_texts); diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index 0c355bfc88f..999b8851226 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -43,6 +43,6 @@ struct wm8994_access_mask { }; extern const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE]; -extern const __devinitdata u16 wm8994_reg_defaults[WM8994_CACHE_SIZE]; +extern const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE]; #endif diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 5c224dd917d..55cdf298202 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -15,6 +15,7 @@ #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/device.h> #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> @@ -166,7 +167,7 @@ struct wm9081_priv { int fll_fref; int fll_fout; int tdm_width; - struct wm9081_retune_mobile_config *retune; + struct wm9081_pdata pdata; }; static int wm9081_volatile_register(struct snd_soc_codec *codec, unsigned int reg) @@ -388,27 +389,6 @@ SOC_DAPM_SINGLE("IN2 Switch", WM9081_ANALOGUE_MIXER, 2, 1, 0), SOC_DAPM_SINGLE("Playback Switch", WM9081_ANALOGUE_MIXER, 4, 1, 0), }; -static int speaker_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = w->codec; - unsigned int reg = snd_soc_read(codec, WM9081_POWER_MANAGEMENT); - - switch (event) { - case SND_SOC_DAPM_POST_PMU: - reg |= WM9081_SPK_ENA; - break; - - case SND_SOC_DAPM_PRE_PMD: - reg &= ~WM9081_SPK_ENA; - break; - } - - snd_soc_write(codec, WM9081_POWER_MANAGEMENT, reg); - - return 0; -} - struct _fll_div { u16 fll_fratio; u16 fll_outdiv; @@ -746,9 +726,8 @@ SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0, SND_SOC_DAPM_PGA("LINEOUT PGA", WM9081_POWER_MANAGEMENT, 4, 0, NULL, 0), -SND_SOC_DAPM_PGA_E("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0, - speaker_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), +SND_SOC_DAPM_PGA("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0), +SND_SOC_DAPM_PGA("Speaker", WM9081_POWER_MANAGEMENT, 1, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("LINEOUT"), SND_SOC_DAPM_OUTPUT("SPKN"), @@ -761,7 +740,7 @@ SND_SOC_DAPM_SUPPLY("TOCLK", WM9081_CLOCK_CONTROL_3, 2, 0, NULL, 0), }; -static const struct snd_soc_dapm_route audio_paths[] = { +static const struct snd_soc_dapm_route wm9081_audio_paths[] = { { "DAC", NULL, "CLK_SYS" }, { "DAC", NULL, "CLK_DSP" }, @@ -779,8 +758,10 @@ static const struct snd_soc_dapm_route audio_paths[] = { { "Speaker PGA", NULL, "TOCLK" }, { "Speaker PGA", NULL, "CLK_SYS" }, - { "SPKN", NULL, "Speaker PGA" }, - { "SPKP", NULL, "Speaker PGA" }, + { "Speaker", NULL, "Speaker PGA" }, + + { "SPKN", NULL, "Speaker" }, + { "SPKP", NULL, "Speaker" }, }; static int wm9081_set_bias_level(struct snd_soc_codec *codec, @@ -1081,21 +1062,22 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream, aif4 |= wm9081->bclk / wm9081->fs; /* Apply a ReTune Mobile configuration if it's in use */ - if (wm9081->retune) { - struct wm9081_retune_mobile_config *retune = wm9081->retune; + if (wm9081->pdata.num_retune_configs) { + struct wm9081_pdata *pdata = &wm9081->pdata; struct wm9081_retune_mobile_setting *s; int eq1; best = 0; - best_val = abs(retune->configs[0].rate - wm9081->fs); - for (i = 0; i < retune->num_configs; i++) { - cur_val = abs(retune->configs[i].rate - wm9081->fs); + best_val = abs(pdata->retune_configs[0].rate - wm9081->fs); + for (i = 0; i < pdata->num_retune_configs; i++) { + cur_val = abs(pdata->retune_configs[i].rate - + wm9081->fs); if (cur_val < best_val) { best_val = cur_val; best = i; } } - s = &retune->configs[best]; + s = &pdata->retune_configs[best]; dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n", s->name, s->rate); @@ -1138,10 +1120,9 @@ static int wm9081_digital_mute(struct snd_soc_dai *codec_dai, int mute) return 0; } -static int wm9081_set_sysclk(struct snd_soc_dai *codec_dai, +static int wm9081_set_sysclk(struct snd_soc_codec *codec, int clk_id, unsigned int freq, int dir) { - struct snd_soc_codec *codec = codec_dai->codec; struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec); switch (clk_id) { @@ -1206,7 +1187,6 @@ static int wm9081_set_tdm_slot(struct snd_soc_dai *dai, static struct snd_soc_dai_ops wm9081_dai_ops = { .hw_params = wm9081_hw_params, - .set_sysclk = wm9081_set_sysclk, .set_fmt = wm9081_set_dai_fmt, .digital_mute = wm9081_digital_mute, .set_tdm_slot = wm9081_set_tdm_slot, @@ -1230,7 +1210,6 @@ static struct snd_soc_dai_driver wm9081_dai = { static int wm9081_probe(struct snd_soc_codec *codec) { struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec); - struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; u16 reg; @@ -1254,6 +1233,14 @@ static int wm9081_probe(struct snd_soc_codec *codec) return ret; } + reg = 0; + if (wm9081->pdata.irq_high) + reg |= WM9081_IRQ_POL; + if (!wm9081->pdata.irq_cmos) + reg |= WM9081_IRQ_OP_CTRL; + snd_soc_update_bits(codec, WM9081_INTERRUPT_CONTROL, + WM9081_IRQ_POL | WM9081_IRQ_OP_CTRL, reg); + wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Enable zero cross by default */ @@ -1265,17 +1252,13 @@ static int wm9081_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, wm9081_snd_controls, ARRAY_SIZE(wm9081_snd_controls)); - if (!wm9081->retune) { + if (!wm9081->pdata.num_retune_configs) { dev_dbg(codec->dev, "No ReTune Mobile data, using normal EQ\n"); snd_soc_add_controls(codec, wm9081_eq_controls, ARRAY_SIZE(wm9081_eq_controls)); } - snd_soc_dapm_new_controls(dapm, wm9081_dapm_widgets, - ARRAY_SIZE(wm9081_dapm_widgets)); - snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); - return ret; } @@ -1319,11 +1302,19 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9081 = { .remove = wm9081_remove, .suspend = wm9081_suspend, .resume = wm9081_resume, + + .set_sysclk = wm9081_set_sysclk, .set_bias_level = wm9081_set_bias_level, + .reg_cache_size = ARRAY_SIZE(wm9081_reg_defaults), .reg_word_size = sizeof(u16), .reg_cache_default = wm9081_reg_defaults, .volatile_register = wm9081_volatile_register, + + .dapm_widgets = wm9081_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm9081_dapm_widgets), + .dapm_routes = wm9081_audio_paths, + .num_dapm_routes = ARRAY_SIZE(wm9081_audio_paths), }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) @@ -1341,6 +1332,10 @@ static __devinit int wm9081_i2c_probe(struct i2c_client *i2c, wm9081->control_type = SND_SOC_I2C; wm9081->control_data = i2c; + if (dev_get_platdata(&i2c->dev)) + memcpy(&wm9081->pdata, dev_get_platdata(&i2c->dev), + sizeof(wm9081->pdata)); + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm9081, &wm9081_dai, 1); if (ret < 0) @@ -1363,7 +1358,7 @@ MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id); static struct i2c_driver wm9081_i2c_driver = { .driver = { - .name = "wm9081-codec", + .name = "wm9081", .owner = THIS_MODULE, }, .probe = wm9081_i2c_probe, diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 613df5db0b3..7b6b3c18e29 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -82,7 +82,8 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op) } while (reg & op && count < 400); if (reg & op) - dev_err(codec->dev, "Timed out waiting for DC Servo\n"); + dev_err(codec->dev, "Timed out waiting for DC Servo %x\n", + op); } /* @@ -674,6 +675,9 @@ SND_SOC_DAPM_OUTPUT("LINEOUT2N"), }; static const struct snd_soc_dapm_route analogue_routes[] = { + { "MICBIAS1", NULL, "CLK_SYS" }, + { "MICBIAS2", NULL, "CLK_SYS" }, + { "IN1L PGA", "IN1LP Switch", "IN1LP" }, { "IN1L PGA", "IN1LN Switch", "IN1LN" }, diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index b36f0b39b09..fe7984221eb 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -218,7 +218,19 @@ static struct snd_soc_dai_link dm6467_evm_dai[] = { .ops = &evm_spdif_ops, }, }; -static struct snd_soc_dai_link da8xx_evm_dai = { + +static struct snd_soc_dai_link da830_evm_dai = { + .name = "TLV320AIC3X", + .stream_name = "AIC3X", + .cpu_dai_name = "davinci-mcasp.1", + .codec_dai_name = "tlv320aic3x-hifi", + .codec_name = "tlv320aic3x-codec.1-0018", + .platform_name = "davinci-pcm-audio", + .init = evm_aic3x_init, + .ops = &evm_ops, +}; + +static struct snd_soc_dai_link da850_evm_dai = { .name = "TLV320AIC3X", .stream_name = "AIC3X", .cpu_dai_name= "davinci-mcasp.0", @@ -259,13 +271,13 @@ static struct snd_soc_card dm6467_snd_soc_card_evm = { static struct snd_soc_card da830_snd_soc_card = { .name = "DA830/OMAP-L137 EVM", - .dai_link = &da8xx_evm_dai, + .dai_link = &da830_evm_dai, .num_links = 1, }; static struct snd_soc_card da850_snd_soc_card = { .name = "DA850/OMAP-L138 EVM", - .dai_link = &da8xx_evm_dai, + .dai_link = &da850_evm_dai, .num_links = 1, }; diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 9e0e565e6ed..d0d60b8a54d 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -658,7 +658,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) return -ENODEV; } - ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1, + ioarea = request_mem_region(mem->start, resource_size(mem), pdev->name); if (!ioarea) { dev_err(&pdev->dev, "McBSP region already claimed\n"); @@ -694,20 +694,25 @@ static int davinci_i2s_probe(struct platform_device *pdev) } clk_enable(dev->clk); - dev->base = (void __iomem *)IO_ADDRESS(mem->start); + dev->base = ioremap(mem->start, resource_size(mem)); + if (!dev->base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_release_clk; + } dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr = - (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG); + (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG); dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr = - (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG); + (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG); /* first TX, then RX */ res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENXIO; - goto err_free_mem; + goto err_iounmap; } dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = res->start; @@ -715,7 +720,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENXIO; - goto err_free_mem; + goto err_iounmap; } dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start; dev->dev = &pdev->dev; @@ -724,14 +729,19 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = snd_soc_register_dai(&pdev->dev, &davinci_i2s_dai); if (ret != 0) - goto err_free_mem; + goto err_iounmap; return 0; +err_iounmap: + iounmap(dev->base); +err_release_clk: + clk_disable(dev->clk); + clk_put(dev->clk); err_free_mem: kfree(dev); err_release_region: - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); return ret; } @@ -747,7 +757,7 @@ static int davinci_i2s_remove(struct platform_device *pdev) dev->clk = NULL; kfree(dev); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); return 0; } diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index fb55d2c5d70..a5af834c8ef 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -868,7 +868,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) } ioarea = request_mem_region(mem->start, - (mem->end - mem->start) + 1, pdev->name); + resource_size(mem), pdev->name); if (!ioarea) { dev_err(&pdev->dev, "Audio region already claimed\n"); ret = -EBUSY; @@ -885,7 +885,13 @@ static int davinci_mcasp_probe(struct platform_device *pdev) clk_enable(dev->clk); dev->clk_active = 1; - dev->base = (void __iomem *)IO_ADDRESS(mem->start); + dev->base = ioremap(mem->start, resource_size(mem)); + if (!dev->base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_release_clk; + } + dev->op_mode = pdata->op_mode; dev->tdm_slots = pdata->tdm_slots; dev->num_serializer = pdata->num_serializer; @@ -899,14 +905,14 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q; dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset + - io_v2p(dev->base)); + mem->start); /* first TX, then RX */ res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENODEV; - goto err_release_region; + goto err_iounmap; } dma_data->channel = res->start; @@ -915,13 +921,13 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dma_data->asp_chan_q = pdata->asp_chan_q; dma_data->ram_chan_q = pdata->ram_chan_q; dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset + - io_v2p(dev->base)); + mem->start); res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENODEV; - goto err_release_region; + goto err_iounmap; } dma_data->channel = res->start; @@ -929,11 +935,16 @@ static int davinci_mcasp_probe(struct platform_device *pdev) ret = snd_soc_register_dai(&pdev->dev, &davinci_mcasp_dai[pdata->op_mode]); if (ret != 0) - goto err_release_region; + goto err_iounmap; return 0; +err_iounmap: + iounmap(dev->base); +err_release_clk: + clk_disable(dev->clk); + clk_put(dev->clk); err_release_region: - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); err_release_data: kfree(dev); @@ -951,7 +962,7 @@ static int davinci_mcasp_remove(struct platform_device *pdev) dev->clk = NULL; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mem->start, (mem->end - mem->start) + 1); + release_mem_region(mem->start, resource_size(mem)); kfree(dev); diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c index 9d2afccc3a2..13e05a302a9 100644 --- a/sound/soc/davinci/davinci-vcif.c +++ b/sound/soc/davinci/davinci-vcif.c @@ -205,7 +205,7 @@ static struct snd_soc_dai_driver davinci_vcif_dai = { static int davinci_vcif_probe(struct platform_device *pdev) { - struct davinci_vc *davinci_vc = platform_get_drvdata(pdev); + struct davinci_vc *davinci_vc = mfd_get_data(pdev); struct davinci_vcif_dev *davinci_vcif_dev; int ret; diff --git a/sound/soc/ep93xx/edb93xx.c b/sound/soc/ep93xx/edb93xx.c index b270085227f..d3aa15119d2 100644 --- a/sound/soc/ep93xx/edb93xx.c +++ b/sound/soc/ep93xx/edb93xx.c @@ -41,17 +41,17 @@ static int edb93xx_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int err; + unsigned int mclk_rate; unsigned int rate = params_rate(params); + /* - * We set LRCLK equal to `rate' and SCLK = LRCLK * 64, - * because our sample size is 32 bit * 2 channels. - * I2S standard permits us to transmit more bits than - * the codec uses. - * MCLK = SCLK * 4 is the best recommended value, - * but we have to fall back to ratio 2 for higher - * sample rates. + * According to CS4271 datasheet we use MCLK/LRCK=256 for + * rates below 50kHz and 128 for higher sample rates */ - unsigned int mclk_rate = rate * 64 * ((rate <= 48000) ? 4 : 2); + if (rate < 50000) + mclk_rate = rate * 64 * 4; + else + mclk_rate = rate * 64 * 2; err = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | diff --git a/sound/soc/ep93xx/ep93xx-ac97.c b/sound/soc/ep93xx/ep93xx-ac97.c index 68a0bae1208..104e95cda0a 100644 --- a/sound/soc/ep93xx/ep93xx-ac97.c +++ b/sound/soc/ep93xx/ep93xx-ac97.c @@ -253,7 +253,6 @@ static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream, struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai); unsigned v = 0; - switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: diff --git a/sound/soc/ep93xx/ep93xx-i2s.c b/sound/soc/ep93xx/ep93xx-i2s.c index fff579a1c13..042f4e93746 100644 --- a/sound/soc/ep93xx/ep93xx-i2s.c +++ b/sound/soc/ep93xx/ep93xx-i2s.c @@ -242,7 +242,7 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream, { struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); unsigned word_len, div, sdiv, lrdiv; - int found = 0, err; + int err; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: @@ -275,15 +275,14 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream, * the codec uses. */ div = clk_get_rate(info->mclk) / params_rate(params); - for (sdiv = 2; sdiv <= 4; sdiv += 2) - for (lrdiv = 64; lrdiv <= 128; lrdiv <<= 1) - if (sdiv * lrdiv == div) { - found = 1; - goto out; - } -out: - if (!found) - return -EINVAL; + sdiv = 4; + if (div > (256 + 512) / 2) { + lrdiv = 128; + } else { + lrdiv = 64; + if (div < (128 + 256) / 2) + sdiv = 2; + } err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv); if (err) @@ -314,10 +313,12 @@ static int ep93xx_i2s_suspend(struct snd_soc_dai *dai) struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); if (!dai->active) - return; + return 0; ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK); ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE); + + return 0; } static int ep93xx_i2s_resume(struct snd_soc_dai *dai) @@ -325,10 +326,12 @@ static int ep93xx_i2s_resume(struct snd_soc_dai *dai) struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai); if (!dai->active) - return; + return 0; ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK); ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE); + + return 0; } #else #define ep93xx_i2s_suspend NULL @@ -352,13 +355,13 @@ static struct snd_soc_dai_driver ep93xx_i2s_dai = { .playback = { .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = EP93XX_I2S_FORMATS, }, .capture = { .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = EP93XX_I2S_FORMATS, }, .ops = &ep93xx_i2s_dai_ops, diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c index 06670776f64..a456e491155 100644 --- a/sound/soc/ep93xx/ep93xx-pcm.c +++ b/sound/soc/ep93xx/ep93xx-pcm.c @@ -35,9 +35,9 @@ static const struct snd_pcm_hardware ep93xx_pcm_hardware = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER), - .rates = SNDRV_PCM_RATE_8000_96000, + .rates = SNDRV_PCM_RATE_8000_192000, .rate_min = SNDRV_PCM_RATE_8000, - .rate_max = SNDRV_PCM_RATE_96000, + .rate_max = SNDRV_PCM_RATE_192000, .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index 4cf98c03af2..15dac0f20cd 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -896,8 +896,7 @@ static struct snd_pcm_ops fsl_dma_ops = { .pointer = fsl_dma_pointer, }; -static int __devinit fsl_soc_dma_probe(struct platform_device *pdev, - const struct of_device_id *match) +static int __devinit fsl_soc_dma_probe(struct platform_device *pdev) { struct dma_object *dma; struct device_node *np = pdev->dev.of_node; @@ -979,7 +978,7 @@ static const struct of_device_id fsl_soc_dma_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_soc_dma_ids); -static struct of_platform_driver fsl_soc_dma_driver = { +static struct platform_driver fsl_soc_dma_driver = { .driver = { .name = "fsl-pcm-audio", .owner = THIS_MODULE, @@ -993,12 +992,12 @@ static int __init fsl_soc_dma_init(void) { pr_info("Freescale Elo DMA ASoC PCM Driver\n"); - return of_register_platform_driver(&fsl_soc_dma_driver); + return platform_driver_register(&fsl_soc_dma_driver); } static void __exit fsl_soc_dma_exit(void) { - of_unregister_platform_driver(&fsl_soc_dma_driver); + platform_driver_unregister(&fsl_soc_dma_driver); } module_init(fsl_soc_dma_init); diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 4cc167a7aeb..313e0ccedd5 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -624,8 +624,7 @@ static void make_lowercase(char *s) } } -static int __devinit fsl_ssi_probe(struct platform_device *pdev, - const struct of_device_id *match) +static int __devinit fsl_ssi_probe(struct platform_device *pdev) { struct fsl_ssi_private *ssi_private; int ret = 0; @@ -774,7 +773,7 @@ static const struct of_device_id fsl_ssi_ids[] = { }; MODULE_DEVICE_TABLE(of, fsl_ssi_ids); -static struct of_platform_driver fsl_ssi_driver = { +static struct platform_driver fsl_ssi_driver = { .driver = { .name = "fsl-ssi-dai", .owner = THIS_MODULE, @@ -788,12 +787,12 @@ static int __init fsl_ssi_init(void) { printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n"); - return of_register_platform_driver(&fsl_ssi_driver); + return platform_driver_register(&fsl_ssi_driver); } static void __exit fsl_ssi_exit(void) { - of_unregister_platform_driver(&fsl_ssi_driver); + platform_driver_unregister(&fsl_ssi_driver); } module_init(fsl_ssi_init); diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c index f92dca07cd3..fff695ccdd3 100644 --- a/sound/soc/fsl/mpc5200_dma.c +++ b/sound/soc/fsl/mpc5200_dma.c @@ -368,8 +368,7 @@ static struct snd_soc_platform_driver mpc5200_audio_dma_platform = { .pcm_free = &psc_dma_free, }; -static int mpc5200_hpcd_probe(struct of_device *op, - const struct of_device_id *match) +static int mpc5200_hpcd_probe(struct of_device *op) { phys_addr_t fifo; struct psc_dma *psc_dma; @@ -511,32 +510,31 @@ static int mpc5200_hpcd_remove(struct of_device *op) } static struct of_device_id mpc5200_hpcd_match[] = { - { - .compatible = "fsl,mpc5200-pcm", - }, + { .compatible = "fsl,mpc5200-pcm", }, {} }; MODULE_DEVICE_TABLE(of, mpc5200_hpcd_match); -static struct of_platform_driver mpc5200_hpcd_of_driver = { - .owner = THIS_MODULE, - .name = "mpc5200-pcm-audio", - .match_table = mpc5200_hpcd_match, +static struct platform_driver mpc5200_hpcd_of_driver = { .probe = mpc5200_hpcd_probe, .remove = mpc5200_hpcd_remove, + .dev = { + .owner = THIS_MODULE, + .name = "mpc5200-pcm-audio", + .of_match_table = mpc5200_hpcd_match, + } }; static int __init mpc5200_hpcd_init(void) { - return of_register_platform_driver(&mpc5200_hpcd_of_driver); + return platform_driver_register(&mpc5200_hpcd_of_driver); } +module_init(mpc5200_hpcd_init); static void __exit mpc5200_hpcd_exit(void) { - of_unregister_platform_driver(&mpc5200_hpcd_of_driver); + platform_driver_unregister(&mpc5200_hpcd_of_driver); } - -module_init(mpc5200_hpcd_init); module_exit(mpc5200_hpcd_exit); MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 40acc8e2b1c..ad36b095bb7 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -272,8 +272,7 @@ static struct snd_soc_dai_driver psc_ac97_dai[] = { * - Probe/remove operations * - OF device match table */ -static int __devinit psc_ac97_of_probe(struct platform_device *op, - const struct of_device_id *match) +static int __devinit psc_ac97_of_probe(struct platform_device *op) { int rc; struct snd_ac97 ac97; @@ -316,7 +315,7 @@ static struct of_device_id psc_ac97_match[] __devinitdata = { }; MODULE_DEVICE_TABLE(of, psc_ac97_match); -static struct of_platform_driver psc_ac97_driver = { +static struct platform_driver psc_ac97_driver = { .probe = psc_ac97_of_probe, .remove = __devexit_p(psc_ac97_of_remove), .driver = { @@ -332,13 +331,13 @@ static struct of_platform_driver psc_ac97_driver = { */ static int __init psc_ac97_init(void) { - return of_register_platform_driver(&psc_ac97_driver); + return platform_driver_register(&psc_ac97_driver); } module_init(psc_ac97_init); static void __exit psc_ac97_exit(void) { - of_unregister_platform_driver(&psc_ac97_driver); + platform_driver_unregister(&psc_ac97_driver); } module_exit(psc_ac97_exit); diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 9018fa5bf0d..87cf2a5c2b2 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -150,8 +150,7 @@ static struct snd_soc_dai_driver psc_i2s_dai[] = {{ * - Probe/remove operations * - OF device match table */ -static int __devinit psc_i2s_of_probe(struct platform_device *op, - const struct of_device_id *match) +static int __devinit psc_i2s_of_probe(struct platform_device *op) { int rc; struct psc_dma *psc_dma; @@ -213,7 +212,7 @@ static struct of_device_id psc_i2s_match[] __devinitdata = { }; MODULE_DEVICE_TABLE(of, psc_i2s_match); -static struct of_platform_driver psc_i2s_driver = { +static struct platform_driver psc_i2s_driver = { .probe = psc_i2s_of_probe, .remove = __devexit_p(psc_i2s_of_remove), .driver = { @@ -229,13 +228,13 @@ static struct of_platform_driver psc_i2s_driver = { */ static int __init psc_i2s_init(void) { - return of_register_platform_driver(&psc_i2s_driver); + return platform_driver_register(&psc_i2s_driver); } module_init(psc_i2s_init); static void __exit psc_i2s_exit(void) { - of_unregister_platform_driver(&psc_i2s_driver); + platform_driver_unregister(&psc_i2s_driver); } module_exit(psc_i2s_exit); diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig index 642270a635e..d8f130d39dd 100644 --- a/sound/soc/imx/Kconfig +++ b/sound/soc/imx/Kconfig @@ -30,6 +30,16 @@ config SND_MXC_SOC_WM1133_EV1 Enable support for audio on the i.MX31ADS with the WM1133-EV1 PMIC board with WM8835x fitted. +config SND_SOC_MX27VIS_AIC32X4 + tristate "SoC audio support for Visstrim M10 boards" + depends on MACH_IMX27_VISSTRIM_M10 + select SND_SOC_TVL320AIC32X4 + select SND_MXC_SOC_SSI + select SND_MXC_SOC_MX2 + help + Say Y if you want to add support for SoC audio on Visstrim SM10 + board with TLV320AIC32X4 codec. + config SND_SOC_PHYCORE_AC97 tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards" depends on MACH_PCM043 || MACH_PCA100 @@ -44,7 +54,8 @@ config SND_SOC_EUKREA_TLV320 tristate "Eukrea TLV320" depends on MACH_EUKREA_MBIMX27_BASEBOARD \ || MACH_EUKREA_MBIMXSD25_BASEBOARD \ - || MACH_EUKREA_MBIMXSD35_BASEBOARD + || MACH_EUKREA_MBIMXSD35_BASEBOARD \ + || MACH_EUKREA_MBIMXSD51_BASEBOARD select SND_SOC_TLV320AIC23 select SND_MXC_SOC_SSI select SND_MXC_SOC_FIQ diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile index b67fc02a4ec..d6d609ba7e2 100644 --- a/sound/soc/imx/Makefile +++ b/sound/soc/imx/Makefile @@ -10,8 +10,10 @@ obj-$(CONFIG_SND_MXC_SOC_MX2) += snd-soc-imx-mx2.o # i.MX Machine Support snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o snd-soc-phycore-ac97-objs := phycore-ac97.o +snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o snd-soc-wm1133-ev1-objs := wm1133-ev1.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o +obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o diff --git a/sound/soc/imx/eukrea-tlv320.c b/sound/soc/imx/eukrea-tlv320.c index e20c9e1457c..75fb4b83548 100644 --- a/sound/soc/imx/eukrea-tlv320.c +++ b/sound/soc/imx/eukrea-tlv320.c @@ -79,7 +79,7 @@ static struct snd_soc_dai_link eukrea_tlv320_dai = { .name = "tlv320aic23", .stream_name = "TLV320AIC23", .codec_dai_name = "tlv320aic23-hifi", - .platform_name = "imx-pcm-audio.0", + .platform_name = "imx-fiq-pcm-audio.0", .codec_name = "tlv320aic23-codec.0-001a", .cpu_dai_name = "imx-ssi.0", .ops = &eukrea_tlv320_snd_ops, @@ -98,7 +98,8 @@ static int __init eukrea_tlv320_init(void) int ret; if (!machine_is_eukrea_cpuimx27() && !machine_is_eukrea_cpuimx25sd() - && !machine_is_eukrea_cpuimx35sd()) + && !machine_is_eukrea_cpuimx35sd() + && !machine_is_eukrea_cpuimx51sd()) /* return happy. We might run on a totally different machine */ return 0; diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c index 30894ea7f33..bc92ec62000 100644 --- a/sound/soc/imx/imx-ssi.c +++ b/sound/soc/imx/imx-ssi.c @@ -108,7 +108,7 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) break; case SND_SOC_DAIFMT_DSP_B: /* data on rising edge of bclk, frame high with data */ - strcr |= SSI_STCR_TFSL; + strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0; break; case SND_SOC_DAIFMT_DSP_A: /* data on rising edge of bclk, frame high 1clk before data */ @@ -656,6 +656,9 @@ static int imx_ssi_probe(struct platform_device *pdev) ssi->dma_params_rx.dma_addr = res->start + SSI_SRX0; ssi->dma_params_tx.dma_addr = res->start + SSI_STX0; + ssi->dma_params_tx.burstsize = 4; + ssi->dma_params_rx.burstsize = 4; + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0"); if (res) ssi->dma_params_tx.dma = res->start; diff --git a/sound/soc/imx/mx27vis-aic32x4.c b/sound/soc/imx/mx27vis-aic32x4.c new file mode 100644 index 00000000000..054110b91d4 --- /dev/null +++ b/sound/soc/imx/mx27vis-aic32x4.c @@ -0,0 +1,137 @@ +/* + * mx27vis-aic32x4.c + * + * Copyright 2011 Vista Silicon S.L. + * + * Author: Javier Martin <javier.martin@vista-silicon.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. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <asm/mach-types.h> +#include <mach/audmux.h> + +#include "../codecs/tlv320aic32x4.h" +#include "imx-ssi.h" + +static int mx27vis_aic32x4_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 *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret; + u32 dai_format; + + dai_format = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + + /* set codec DAI configuration */ + snd_soc_dai_set_fmt(codec_dai, dai_format); + + /* set cpu DAI configuration */ + snd_soc_dai_set_fmt(cpu_dai, dai_format); + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, + 25000000, SND_SOC_CLOCK_OUT); + if (ret) { + pr_err("%s: failed setting codec sysclk\n", __func__); + return ret; + } + + ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, + SND_SOC_CLOCK_IN); + if (ret) { + pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n"); + return ret; + } + + return 0; +} + +static struct snd_soc_ops mx27vis_aic32x4_snd_ops = { + .hw_params = mx27vis_aic32x4_hw_params, +}; + +static struct snd_soc_dai_link mx27vis_aic32x4_dai = { + .name = "tlv320aic32x4", + .stream_name = "TLV320AIC32X4", + .codec_dai_name = "tlv320aic32x4-hifi", + .platform_name = "imx-pcm-audio.0", + .codec_name = "tlv320aic32x4.0-0018", + .cpu_dai_name = "imx-ssi.0", + .ops = &mx27vis_aic32x4_snd_ops, +}; + +static struct snd_soc_card mx27vis_aic32x4 = { + .name = "visstrim_m10-audio", + .dai_link = &mx27vis_aic32x4_dai, + .num_links = 1, +}; + +static struct platform_device *mx27vis_aic32x4_snd_device; + +static int __init mx27vis_aic32x4_init(void) +{ + int ret; + + mx27vis_aic32x4_snd_device = platform_device_alloc("soc-audio", -1); + if (!mx27vis_aic32x4_snd_device) + return -ENOMEM; + + platform_set_drvdata(mx27vis_aic32x4_snd_device, &mx27vis_aic32x4); + ret = platform_device_add(mx27vis_aic32x4_snd_device); + + if (ret) { + printk(KERN_ERR "ASoC: Platform device allocation failed\n"); + platform_device_put(mx27vis_aic32x4_snd_device); + } + + /* Connect SSI0 as clock slave to SSI1 external pins */ + mxc_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, + MXC_AUDMUX_V1_PCR_SYN | + MXC_AUDMUX_V1_PCR_TFSDIR | + MXC_AUDMUX_V1_PCR_TCLKDIR | + MXC_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) | + MXC_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) + ); + mxc_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1, + MXC_AUDMUX_V1_PCR_SYN | + MXC_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) + ); + + return ret; +} + +static void __exit mx27vis_aic32x4_exit(void) +{ + platform_device_unregister(mx27vis_aic32x4_snd_device); +} + +module_init(mx27vis_aic32x4_init); +module_exit(mx27vis_aic32x4_exit); + +MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>"); +MODULE_DESCRIPTION("ALSA SoC AIC32X4 mx27 visstrim"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/mid-x86/Kconfig b/sound/soc/mid-x86/Kconfig index 1ad75383635..29350428f1c 100644 --- a/sound/soc/mid-x86/Kconfig +++ b/sound/soc/mid-x86/Kconfig @@ -1,6 +1,7 @@ config SND_MFLD_MACHINE tristate "SOC Machine Audio driver for Intel Medfield MID platform" depends on INTEL_SCU_IPC + depends on SND_INTEL_SST select SND_SOC_SN95031 select SND_SST_PLATFORM help @@ -11,4 +12,3 @@ config SND_MFLD_MACHINE config SND_SST_PLATFORM tristate - depends on SND_INTEL_SST diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/mid-x86/mfld_machine.c index 7925851a5de..429aa1be2cf 100644 --- a/sound/soc/mid-x86/mfld_machine.c +++ b/sound/soc/mid-x86/mfld_machine.c @@ -27,18 +27,59 @@ #include <linux/init.h> #include <linux/device.h> #include <linux/slab.h> +#include <linux/io.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include <sound/jack.h> #include "../codecs/sn95031.h" #define MID_MONO 1 #define MID_STEREO 2 #define MID_MAX_CAP 5 +#define MFLD_JACK_INSERT 0x04 + +enum soc_mic_bias_zones { + MFLD_MV_START = 0, + /* mic bias volutage range for Headphones*/ + MFLD_MV_HP = 400, + /* mic bias volutage range for American Headset*/ + MFLD_MV_AM_HS = 650, + /* mic bias volutage range for Headset*/ + MFLD_MV_HS = 2000, + MFLD_MV_UNDEFINED, +}; static unsigned int hs_switch; static unsigned int lo_dac; +struct mfld_mc_private { + struct platform_device *socdev; + void __iomem *int_base; + struct snd_soc_codec *codec; + u8 interrupt_status; +}; + +struct snd_soc_jack mfld_jack; + +/*Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin mfld_jack_pins[] = { + { + .pin = "Headphones", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "AMIC1", + .mask = SND_JACK_MICROPHONE, + }, +}; + +/* jack detection voltage zones */ +static struct snd_soc_jack_zone mfld_zones[] = { + {MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE}, + {MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET}, +}; + /* sound card controls */ static const char *headset_switch_text[] = {"Earpiece", "Headset"}; @@ -67,13 +108,11 @@ static int headset_set_switch(struct snd_kcontrol *kcontrol, if (ucontrol->value.integer.value[0]) { pr_debug("hs_set HS path\n"); - snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTL"); - snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTR"); + snd_soc_dapm_enable_pin(&codec->dapm, "Headphones"); snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); } else { pr_debug("hs_set EP path\n"); - snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTL"); - snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTR"); + snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); } snd_soc_dapm_sync(&codec->dapm); @@ -91,12 +130,10 @@ static void lo_enable_out_pins(struct snd_soc_codec *codec) snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT"); snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT"); if (hs_switch) { - snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTL"); - snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTR"); + snd_soc_dapm_enable_pin(&codec->dapm, "Headphones"); snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); } else { - snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTL"); - snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTR"); + snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT"); } } @@ -130,8 +167,7 @@ static int lo_set_switch(struct snd_kcontrol *kcontrol, case 1: pr_debug("set hs path\n"); - snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTL"); - snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTR"); + snd_soc_dapm_disable_pin(&codec->dapm, "Headphones"); snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT"); snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22); break; @@ -162,12 +198,45 @@ static const struct snd_kcontrol_new mfld_snd_controls[] = { lo_get_switch, lo_set_switch), }; +static const struct snd_soc_dapm_widget mfld_widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("Mic", NULL), +}; + +static const struct snd_soc_dapm_route mfld_map[] = { + {"Headphones", NULL, "HPOUTR"}, + {"Headphones", NULL, "HPOUTL"}, + {"Mic", NULL, "AMIC1"}, +}; + +static void mfld_jack_check(unsigned int intr_status) +{ + struct mfld_jack_data jack_data; + + jack_data.mfld_jack = &mfld_jack; + jack_data.intr_id = intr_status; + + sn95031_jack_detection(&jack_data); + /* TODO: add american headset detection post gpiolib support */ +} + static int mfld_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_codec *codec = runtime->codec; struct snd_soc_dapm_context *dapm = &codec->dapm; int ret_val; + /* Add jack sense widgets */ + snd_soc_dapm_new_controls(dapm, mfld_widgets, ARRAY_SIZE(mfld_widgets)); + + /* Set up the map */ + snd_soc_dapm_add_routes(dapm, mfld_map, ARRAY_SIZE(mfld_map)); + + /* always connected */ + snd_soc_dapm_enable_pin(dapm, "Headphones"); + snd_soc_dapm_enable_pin(dapm, "Mic"); + snd_soc_dapm_sync(dapm); + ret_val = snd_soc_add_controls(codec, mfld_snd_controls, ARRAY_SIZE(mfld_snd_controls)); if (ret_val) { @@ -175,8 +244,7 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime) return ret_val; } /* default is earpiece pin, userspace sets it explcitly */ - snd_soc_dapm_disable_pin(dapm, "HPOUTL"); - snd_soc_dapm_disable_pin(dapm, "HPOUTR"); + snd_soc_dapm_disable_pin(dapm, "Headphones"); /* default is lineout NC, userspace sets it explcitly */ snd_soc_dapm_disable_pin(dapm, "LINEOUTL"); snd_soc_dapm_disable_pin(dapm, "LINEOUTR"); @@ -185,7 +253,35 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime) /* we dont use linein in this so set to NC */ snd_soc_dapm_disable_pin(dapm, "LINEINL"); snd_soc_dapm_disable_pin(dapm, "LINEINR"); - return snd_soc_dapm_sync(dapm); + snd_soc_dapm_sync(dapm); + + /* Headset and button jack detection */ + ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1, &mfld_jack); + if (ret_val) { + pr_err("jack creation failed\n"); + return ret_val; + } + + ret_val = snd_soc_jack_add_pins(&mfld_jack, + ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins); + if (ret_val) { + pr_err("adding jack pins failed\n"); + return ret_val; + } + ret_val = snd_soc_jack_add_zones(&mfld_jack, + ARRAY_SIZE(mfld_zones), mfld_zones); + if (ret_val) { + pr_err("adding jack zones failed\n"); + return ret_val; + } + + /* we want to check if anything is inserted at boot, + * so send a fake event to codec and it will read adc + * to find if anything is there or not */ + mfld_jack_check(MFLD_JACK_INSERT); + return ret_val; } struct snd_soc_dai_link mfld_msic_dailink[] = { @@ -234,37 +330,94 @@ static struct snd_soc_card snd_soc_card_mfld = { .num_links = ARRAY_SIZE(mfld_msic_dailink), }; +static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev) +{ + struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev; + + memcpy_fromio(&mc_private->interrupt_status, + ((void *)(mc_private->int_base)), + sizeof(u8)); + return IRQ_WAKE_THREAD; +} + +static irqreturn_t snd_mfld_jack_detection(int irq, void *data) +{ + struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data; + + if (mfld_jack.codec == NULL) + return IRQ_HANDLED; + mfld_jack_check(mc_drv_ctx->interrupt_status); + + return IRQ_HANDLED; +} + static int __devinit snd_mfld_mc_probe(struct platform_device *pdev) { - struct platform_device *socdev; - int ret_val = 0; + int ret_val = 0, irq; + struct mfld_mc_private *mc_drv_ctx; + struct resource *irq_mem; pr_debug("snd_mfld_mc_probe called\n"); - socdev = platform_device_alloc("soc-audio", -1); - if (!socdev) { - pr_err("soc-audio device allocation failed\n"); + /* retrive the irq number */ + irq = platform_get_irq(pdev, 0); + + /* audio interrupt base of SRAM location where + * interrupts are stored by System FW */ + mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC); + if (!mc_drv_ctx) { + pr_err("allocation failed\n"); return -ENOMEM; } - platform_set_drvdata(socdev, &snd_soc_card_mfld); - ret_val = platform_device_add(socdev); + + irq_mem = platform_get_resource_byname( + pdev, IORESOURCE_MEM, "IRQ_BASE"); + if (!irq_mem) { + pr_err("no mem resource given\n"); + ret_val = -ENODEV; + goto unalloc; + } + mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start, + resource_size(irq_mem)); + if (!mc_drv_ctx->int_base) { + pr_err("Mapping of cache failed\n"); + ret_val = -ENOMEM; + goto unalloc; + } + /* register for interrupt */ + ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler, + snd_mfld_jack_detection, + IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx); if (ret_val) { - pr_err("Unable to add soc-audio device, err %d\n", ret_val); - platform_device_put(socdev); + pr_err("cannot register IRQ\n"); + goto unalloc; } - - platform_set_drvdata(pdev, socdev); - + /* register the soc card */ + snd_soc_card_mfld.dev = &pdev->dev; + ret_val = snd_soc_register_card(&snd_soc_card_mfld); + if (ret_val) { + pr_debug("snd_soc_register_card failed %d\n", ret_val); + goto freeirq; + } + platform_set_drvdata(pdev, mc_drv_ctx); pr_debug("successfully exited probe\n"); return ret_val; + +freeirq: + free_irq(irq, mc_drv_ctx); +unalloc: + kfree(mc_drv_ctx); + return ret_val; } static int __devexit snd_mfld_mc_remove(struct platform_device *pdev) { - struct platform_device *socdev = platform_get_drvdata(pdev); - pr_debug("snd_mfld_mc_remove called\n"); + struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev); - platform_device_unregister(socdev); + pr_debug("snd_mfld_mc_remove called\n"); + free_irq(platform_get_irq(pdev, 0), mc_drv_ctx); + snd_soc_unregister_card(&snd_soc_card_mfld); + kfree(mc_drv_ctx); platform_set_drvdata(pdev, NULL); return 0; } diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c index 96e6e9c9c5f..ee2c22475a7 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/mid-x86/sst_platform.c @@ -365,6 +365,14 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer return stream->stream_info.buffer_ptr; } +static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); + + return 0; +} static struct snd_pcm_ops sst_platform_ops = { .open = sst_platform_open, @@ -373,6 +381,7 @@ static struct snd_pcm_ops sst_platform_ops = { .prepare = sst_platform_pcm_prepare, .trigger = sst_platform_pcm_trigger, .pointer = sst_platform_pcm_pointer, + .hw_params = sst_platform_pcm_hw_params, }; static void sst_pcm_free(struct snd_pcm *pcm) diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c index 161750443eb..73dde4a1adc 100644 --- a/sound/soc/omap/am3517evm.c +++ b/sound/soc/omap/am3517evm.c @@ -139,7 +139,7 @@ static struct snd_soc_dai_link am3517evm_dai = { .cpu_dai_name ="omap-mcbsp-dai.0", .codec_dai_name = "tlv320aic23-hifi", .platform_name = "omap-pcm-audio", - .codec_name = "tlv320aic23-codec", + .codec_name = "tlv320aic23-codec.2-001a", .init = am3517evm_aic23_init, .ops = &am3517evm_ops, }; diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index d203f4da18a..2175f09e57b 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -69,110 +69,6 @@ static struct omap_mcbsp_data mcbsp_data[NUM_LINKS]; */ static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2]; -#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) -static const int omap1_dma_reqs[][2] = { - { OMAP_DMA_MCBSP1_TX, OMAP_DMA_MCBSP1_RX }, - { OMAP_DMA_MCBSP2_TX, OMAP_DMA_MCBSP2_RX }, - { OMAP_DMA_MCBSP3_TX, OMAP_DMA_MCBSP3_RX }, -}; -static const unsigned long omap1_mcbsp_port[][2] = { - { OMAP1510_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1, - OMAP1510_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 }, - { OMAP1510_MCBSP2_BASE + OMAP_MCBSP_REG_DXR1, - OMAP1510_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 }, - { OMAP1510_MCBSP3_BASE + OMAP_MCBSP_REG_DXR1, - OMAP1510_MCBSP3_BASE + OMAP_MCBSP_REG_DRR1 }, -}; -#else -static const int omap1_dma_reqs[][2] = {}; -static const unsigned long omap1_mcbsp_port[][2] = {}; -#endif - -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) -static const int omap24xx_dma_reqs[][2] = { - { OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX }, - { OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX }, -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) - { OMAP24XX_DMA_MCBSP3_TX, OMAP24XX_DMA_MCBSP3_RX }, - { OMAP24XX_DMA_MCBSP4_TX, OMAP24XX_DMA_MCBSP4_RX }, - { OMAP24XX_DMA_MCBSP5_TX, OMAP24XX_DMA_MCBSP5_RX }, -#endif -}; -#else -static const int omap24xx_dma_reqs[][2] = {}; -#endif - -#if defined(CONFIG_ARCH_OMAP4) -static const int omap44xx_dma_reqs[][2] = { - { OMAP44XX_DMA_MCBSP1_TX, OMAP44XX_DMA_MCBSP1_RX }, - { OMAP44XX_DMA_MCBSP2_TX, OMAP44XX_DMA_MCBSP2_RX }, - { OMAP44XX_DMA_MCBSP3_TX, OMAP44XX_DMA_MCBSP3_RX }, - { OMAP44XX_DMA_MCBSP4_TX, OMAP44XX_DMA_MCBSP4_RX }, -}; -#else -static const int omap44xx_dma_reqs[][2] = {}; -#endif - -#if defined(CONFIG_ARCH_OMAP2420) -static const unsigned long omap2420_mcbsp_port[][2] = { - { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1, - OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 }, - { OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR1, - OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 }, -}; -#else -static const unsigned long omap2420_mcbsp_port[][2] = {}; -#endif - -#if defined(CONFIG_ARCH_OMAP2430) -static const unsigned long omap2430_mcbsp_port[][2] = { - { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, - OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR, - OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DXR, - OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DXR, - OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DXR, - OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DRR }, -}; -#else -static const unsigned long omap2430_mcbsp_port[][2] = {}; -#endif - -#if defined(CONFIG_ARCH_OMAP3) -static const unsigned long omap34xx_mcbsp_port[][2] = { - { OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, - OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR, - OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DXR, - OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DXR, - OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DXR, - OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DRR }, -}; -#else -static const unsigned long omap34xx_mcbsp_port[][2] = {}; -#endif - -#if defined(CONFIG_ARCH_OMAP4) -static const unsigned long omap44xx_mcbsp_port[][2] = { - { OMAP44XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, - OMAP44XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP44XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR, - OMAP44XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP44XX_MCBSP3_BASE + OMAP_MCBSP_REG_DXR, - OMAP44XX_MCBSP3_BASE + OMAP_MCBSP_REG_DRR }, - { OMAP44XX_MCBSP4_BASE + OMAP_MCBSP_REG_DXR, - OMAP44XX_MCBSP4_BASE + OMAP_MCBSP_REG_DRR }, -}; -#else -static const unsigned long omap44xx_mcbsp_port[][2] = {}; -#endif - static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -346,24 +242,10 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, unsigned int format, div, framesize, master; dma_data = &omap_mcbsp_dai_dma_params[cpu_dai->id][substream->stream]; - if (cpu_class_is_omap1()) { - dma = omap1_dma_reqs[bus_id][substream->stream]; - port = omap1_mcbsp_port[bus_id][substream->stream]; - } else if (cpu_is_omap2420()) { - dma = omap24xx_dma_reqs[bus_id][substream->stream]; - port = omap2420_mcbsp_port[bus_id][substream->stream]; - } else if (cpu_is_omap2430()) { - dma = omap24xx_dma_reqs[bus_id][substream->stream]; - port = omap2430_mcbsp_port[bus_id][substream->stream]; - } else if (cpu_is_omap343x()) { - dma = omap24xx_dma_reqs[bus_id][substream->stream]; - port = omap34xx_mcbsp_port[bus_id][substream->stream]; - } else if (cpu_is_omap44xx()) { - dma = omap44xx_dma_reqs[bus_id][substream->stream]; - port = omap44xx_mcbsp_port[bus_id][substream->stream]; - } else { - return -ENODEV; - } + + dma = omap_mcbsp_dma_ch_params(bus_id, substream->stream); + port = omap_mcbsp_dma_reg_params(bus_id, substream->stream); + switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: dma_data->data_type = OMAP_DMA_DATA_TYPE_S16; diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h index 110c106611d..37dc7211ed3 100644 --- a/sound/soc/omap/omap-mcbsp.h +++ b/sound/soc/omap/omap-mcbsp.h @@ -43,7 +43,7 @@ enum omap_mcbsp_div { OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */ }; -#if defined(CONFIG_ARCH_OMAP2420) +#if defined(CONFIG_SOC_OMAP2420) #define NUM_LINKS 2 #endif #if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) @@ -54,7 +54,7 @@ enum omap_mcbsp_div { #undef NUM_LINKS #define NUM_LINKS 4 #endif -#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3) +#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_SOC_OMAP2430) #undef NUM_LINKS #define NUM_LINKS 5 #endif diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c index 28333e7d9c5..dc65650a6fa 100644 --- a/sound/soc/pxa/e740_wm9705.c +++ b/sound/soc/pxa/e740_wm9705.c @@ -117,7 +117,7 @@ static struct snd_soc_dai_link e740_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9705-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9705-codec", @@ -126,7 +126,7 @@ static struct snd_soc_dai_link e740_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name = "wm9705-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9705-codec", diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c index 01bf31675c5..51897fcd911 100644 --- a/sound/soc/pxa/e750_wm9705.c +++ b/sound/soc/pxa/e750_wm9705.c @@ -99,7 +99,7 @@ static struct snd_soc_dai_link e750_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9705-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9705-codec", @@ -109,7 +109,7 @@ static struct snd_soc_dai_link e750_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name ="wm9705-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9705-codec", diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c index c6a37c6ef23..053ed208e59 100644 --- a/sound/soc/pxa/e800_wm9712.c +++ b/sound/soc/pxa/e800_wm9712.c @@ -89,7 +89,7 @@ static struct snd_soc_dai_link e800_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9712-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", @@ -98,7 +98,7 @@ static struct snd_soc_dai_link e800_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name ="wm9712-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c index fc22e6eefc9..b13a4252812 100644 --- a/sound/soc/pxa/em-x270.c +++ b/sound/soc/pxa/em-x270.c @@ -37,7 +37,7 @@ static struct snd_soc_dai_link em_x270_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9712-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", @@ -45,7 +45,7 @@ static struct snd_soc_dai_link em_x270_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name ="wm9712-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index 0d70fc8c12b..38ca6759907 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -162,7 +162,7 @@ static struct snd_soc_dai_link mioa701_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9713-hifi", .codec_name = "wm9713-codec", .init = mioa701_wm9713_init, @@ -172,7 +172,7 @@ static struct snd_soc_dai_link mioa701_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name ="wm9713-aux", .codec_name = "wm9713-codec", .platform_name = "pxa-pcm-audio", diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index 857db96d4a4..504e4004f00 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -132,7 +132,7 @@ static struct snd_soc_dai_link palm27x_dai[] = { { .name = "AC97 HiFi", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9712-hifi", .codec_name = "wm9712-codec", .platform_name = "pxa-pcm-audio", @@ -141,7 +141,7 @@ static struct snd_soc_dai_link palm27x_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name = "wm9712-aux", .codec_name = "wm9712-codec", .platform_name = "pxa-pcm-audio", diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c index db1dd560a58..2afabaf5949 100644 --- a/sound/soc/pxa/raumfeld.c +++ b/sound/soc/pxa/raumfeld.c @@ -229,19 +229,19 @@ static struct snd_soc_dai_link raumfeld_dai[] = { { .name = "ak4104", .stream_name = "Playback", - .cpu_dai_name = "pxa-ssp-dai.1", - .codec_dai_name = "ak4104-hifi", - .platform_name = "pxa-pcm-audio", + .cpu_dai_name = "pxa-ssp-dai.1", + .codec_dai_name = "ak4104-hifi", + .platform_name = "pxa-pcm-audio", .ops = &raumfeld_ak4104_ops, - .codec_name = "ak4104-codec.0", + .codec_name = "ak4104-codec.0", }, { .name = "CS4270", .stream_name = "CS4270", - .cpu_dai_name = "pxa-ssp-dai.0", - .platform_name = "pxa-pcm-audio", - .codec_dai_name = "cs4270-hifi", - .codec_name = "cs4270-codec.0-0048", + .cpu_dai_name = "pxa-ssp-dai.0", + .platform_name = "pxa-pcm-audio", + .codec_dai_name = "cs4270-hifi", + .codec_name = "cs4270-codec.0-0048", .ops = &raumfeld_cs4270_ops, },}; diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index 489139a31cf..9a235136695 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -219,7 +219,7 @@ static struct snd_soc_dai_link tosa_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9712-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", @@ -229,7 +229,7 @@ static struct snd_soc_dai_link tosa_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name = "wm9712-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c index 3ceaef68e01..d69d9fc3223 100644 --- a/sound/soc/pxa/z2.c +++ b/sound/soc/pxa/z2.c @@ -95,6 +95,11 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE, }, + { + .pin = "Ext Spk", + .mask = SND_JACK_HEADPHONE, + .invert = 1 + }, }; /* Headset jack detection gpios */ @@ -147,7 +152,7 @@ static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_disable_pin(dapm, "LINPUT3"); snd_soc_dapm_disable_pin(dapm, "RINPUT3"); snd_soc_dapm_disable_pin(dapm, "OUT3"); - snd_soc_dapm_disable_pin(dapm, "MONO"); + snd_soc_dapm_disable_pin(dapm, "MONO1"); /* Add z2 specific widgets */ snd_soc_dapm_new_controls(dapm, wm8750_dapm_widgets, diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index c5858296b48..ac577263b3e 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -166,7 +166,7 @@ static struct snd_soc_dai_link zylonite_dai[] = { .stream_name = "AC97 HiFi", .codec_name = "wm9713-codec", .platform_name = "pxa-pcm-audio", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_name = "wm9713-hifi", .init = zylonite_wm9713_init, }, @@ -175,7 +175,7 @@ static struct snd_soc_dai_link zylonite_dai[] = { .stream_name = "AC97 Aux", .codec_name = "wm9713-codec", .platform_name = "pxa-pcm-audio", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_name = "wm9713-aux", }, { diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index a6a6b5fa2f2..a3fdfb63146 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -1,6 +1,6 @@ config SND_SOC_SAMSUNG tristate "ASoC support for Samsung" - depends on ARCH_S3C2410 || ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_S5P64X0 || ARCH_S5P6442 || ARCH_S5PV310 + depends on ARCH_S3C2410 || ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_S5P64X0 || ARCH_S5P6442 || ARCH_EXYNOS4 select S3C64XX_DMA if ARCH_S3C64XX select S3C2410_DMA if ARCH_S3C2410 help @@ -35,23 +35,16 @@ config SND_SAMSUNG_I2S tristate config SND_SOC_SAMSUNG_NEO1973_WM8753 - tristate "SoC I2S Audio support for NEO1973 - WM8753" - depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA01 + tristate "Audio support for Openmoko Neo1973 Smartphones (GTA01/GTA02)" + depends on SND_SOC_SAMSUNG && (MACH_NEO1973_GTA01 || MACH_NEO1973_GTA02) select SND_S3C24XX_I2S select SND_SOC_WM8753 + select SND_SOC_LM4857 if MACH_NEO1973_GTA01 + select SND_SOC_DFBMCS320 help - Say Y if you want to add support for SoC audio on smdk2440 - with the WM8753. + Say Y here to enable audio support for the Openmoko Neo1973 + Smartphones. -config SND_SOC_SAMSUNG_NEO1973_GTA02_WM8753 - tristate "Audio support for the Openmoko Neo FreeRunner (GTA02)" - depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA02 - select SND_S3C24XX_I2S - select SND_SOC_WM8753 - help - This driver provides audio support for the Openmoko Neo FreeRunner - smartphone. - config SND_SOC_SAMSUNG_JIVE_WM8750 tristate "SoC I2S Audio support for Jive" depends on SND_SOC_SAMSUNG && MACH_JIVE diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 705d4e8a672..294dec05c26 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -20,7 +20,6 @@ obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o # S3C24XX Machine Support snd-soc-jive-wm8750-objs := jive_wm8750.o snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o -snd-soc-neo1973-gta02-wm8753-objs := neo1973_gta02_wm8753.o snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o @@ -38,7 +37,6 @@ snd-soc-smdk-spdif-objs := smdk_spdif.o obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o -obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_GTA02_WM8753) += snd-soc-neo1973-gta02-wm8753.o obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o obj-$(CONFIG_SND_SOC_SAMSUNG_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o obj-$(CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index 9bce1df1f0d..5cb3b880f0d 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -310,7 +310,7 @@ dma_pointer(struct snd_pcm_substream *substream) /* we seem to be getting the odd error from the pcm library due * to out-of-bounds pointers. this is maybe due to the dma engine * not having loaded the new values for the channel before being - * callled... (todo - fix ) + * called... (todo - fix ) */ if (res >= snd_pcm_lib_buffer_bytes(substream)) { diff --git a/sound/soc/samsung/lm4857.h b/sound/soc/samsung/lm4857.h deleted file mode 100644 index 0cf5b7011d6..00000000000 --- a/sound/soc/samsung/lm4857.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * lm4857.h -- ALSA Soc Audio Layer - * - * Copyright 2007 Wolfson Microelectronics PLC. - * Author: Graeme Gregory - * graeme.gregory@wolfsonmicro.com or linux@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. - * - * Revision history - * 18th Jun 2007 Initial version. - */ - -#ifndef LM4857_H_ -#define LM4857_H_ - -/* The register offsets in the cache array */ -#define LM4857_MVOL 0 -#define LM4857_LVOL 1 -#define LM4857_RVOL 2 -#define LM4857_CTRL 3 - -/* the shifts required to set these bits */ -#define LM4857_3D 5 -#define LM4857_WAKEUP 5 -#define LM4857_EPGAIN 4 - -#endif /*LM4857_H_*/ - diff --git a/sound/soc/samsung/neo1973_gta02_wm8753.c b/sound/soc/samsung/neo1973_gta02_wm8753.c deleted file mode 100644 index 95ebf812b14..00000000000 --- a/sound/soc/samsung/neo1973_gta02_wm8753.c +++ /dev/null @@ -1,494 +0,0 @@ -/* - * neo1973_gta02_wm8753.c -- SoC audio for Openmoko Freerunner(GTA02) - * - * Copyright 2007 Openmoko Inc - * Author: Graeme Gregory <graeme@openmoko.org> - * Copyright 2007 Wolfson Microelectronics PLC. - * Author: Graeme Gregory <linux@wolfsonmicro.com> - * Copyright 2009 Wolfson Microelectronics - * - * 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/gpio.h> - -#include <sound/soc.h> - -#include <asm/mach-types.h> -#include <plat/regs-iis.h> -#include <mach/gta02.h> - -#include "../codecs/wm8753.h" -#include "s3c24xx-i2s.h" - -static struct snd_soc_card neo1973_gta02; - -static int neo1973_gta02_hifi_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 *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - unsigned int pll_out = 0, bclk = 0; - int ret = 0; - unsigned long iis_clkrate; - - iis_clkrate = s3c24xx_i2s_get_clockrate(); - - switch (params_rate(params)) { - case 8000: - case 16000: - pll_out = 12288000; - break; - case 48000: - bclk = WM8753_BCLK_DIV_4; - pll_out = 12288000; - break; - case 96000: - bclk = WM8753_BCLK_DIV_2; - pll_out = 12288000; - break; - case 11025: - bclk = WM8753_BCLK_DIV_16; - pll_out = 11289600; - break; - case 22050: - bclk = WM8753_BCLK_DIV_8; - pll_out = 11289600; - break; - case 44100: - bclk = WM8753_BCLK_DIV_4; - pll_out = 11289600; - break; - case 88200: - bclk = WM8753_BCLK_DIV_2; - pll_out = 11289600; - break; - } - - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, - SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - /* set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out, - SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* set MCLK division for sample rate */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, - S3C2410_IISMOD_32FS); - if (ret < 0) - return ret; - - /* set codec BCLK division for sample rate */ - ret = snd_soc_dai_set_clkdiv(codec_dai, - WM8753_BCLKDIV, bclk); - if (ret < 0) - return ret; - - /* set prescaler division for sample rate */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, - S3C24XX_PRESCALE(4, 4)); - if (ret < 0) - return ret; - - /* codec PLL input is PCLK/4 */ - ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, - iis_clkrate / 4, pll_out); - if (ret < 0) - return ret; - - return 0; -} - -static int neo1973_gta02_hifi_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - /* disable the PLL */ - return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0); -} - -/* - * Neo1973 WM8753 HiFi DAI opserations. - */ -static struct snd_soc_ops neo1973_gta02_hifi_ops = { - .hw_params = neo1973_gta02_hifi_hw_params, - .hw_free = neo1973_gta02_hifi_hw_free, -}; - -static int neo1973_gta02_voice_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 *codec_dai = rtd->codec_dai; - unsigned int pcmdiv = 0; - int ret = 0; - unsigned long iis_clkrate; - - iis_clkrate = s3c24xx_i2s_get_clockrate(); - - if (params_rate(params) != 8000) - return -EINVAL; - if (params_channels(params) != 1) - return -EINVAL; - - pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */ - - /* todo: gg check mode (DSP_B) against CSR datasheet */ - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, - 12288000, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* set codec PCM division for sample rate */ - ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, - pcmdiv); - if (ret < 0) - return ret; - - /* configure and enable PLL for 12.288MHz output */ - ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, - iis_clkrate / 4, 12288000); - if (ret < 0) - return ret; - - return 0; -} - -static int neo1973_gta02_voice_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - /* disable the PLL */ - return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0); -} - -static struct snd_soc_ops neo1973_gta02_voice_ops = { - .hw_params = neo1973_gta02_voice_hw_params, - .hw_free = neo1973_gta02_voice_hw_free, -}; - -#define LM4853_AMP 1 -#define LM4853_SPK 2 - -static u8 lm4853_state; - -/* This has no effect, it exists only to maintain compatibility with - * existing ALSA state files. - */ -static int lm4853_set_state(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int val = ucontrol->value.integer.value[0]; - - if (val) - lm4853_state |= LM4853_AMP; - else - lm4853_state &= ~LM4853_AMP; - - return 0; -} - -static int lm4853_get_state(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = lm4853_state & LM4853_AMP; - - return 0; -} - -static int lm4853_set_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int val = ucontrol->value.integer.value[0]; - - if (val) { - lm4853_state |= LM4853_SPK; - gpio_set_value(GTA02_GPIO_HP_IN, 0); - } else { - lm4853_state &= ~LM4853_SPK; - gpio_set_value(GTA02_GPIO_HP_IN, 1); - } - - return 0; -} - -static int lm4853_get_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = (lm4853_state & LM4853_SPK) >> 1; - - return 0; -} - -static int lm4853_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, - int event) -{ - gpio_set_value(GTA02_GPIO_AMP_SHUT, SND_SOC_DAPM_EVENT_OFF(event)); - - return 0; -} - -static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = { - SND_SOC_DAPM_SPK("Stereo Out", lm4853_event), - SND_SOC_DAPM_LINE("GSM Line Out", NULL), - SND_SOC_DAPM_LINE("GSM Line In", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Handset Mic", NULL), - SND_SOC_DAPM_SPK("Handset Spk", NULL), -}; - - -/* example machine audio_mapnections */ -static const struct snd_soc_dapm_route audio_map[] = { - - /* Connections to the lm4853 amp */ - {"Stereo Out", NULL, "LOUT1"}, - {"Stereo Out", NULL, "ROUT1"}, - - /* Connections to the GSM Module */ - {"GSM Line Out", NULL, "MONO1"}, - {"GSM Line Out", NULL, "MONO2"}, - {"RXP", NULL, "GSM Line In"}, - {"RXN", NULL, "GSM Line In"}, - - /* Connections to Headset */ - {"MIC1", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Headset Mic"}, - - /* Call Mic */ - {"MIC2", NULL, "Mic Bias"}, - {"MIC2N", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Handset Mic"}, - - /* Call Speaker */ - {"Handset Spk", NULL, "LOUT2"}, - {"Handset Spk", NULL, "ROUT2"}, - - /* Connect the ALC pins */ - {"ACIN", NULL, "ACOP"}, -}; - -static const struct snd_kcontrol_new wm8753_neo1973_gta02_controls[] = { - SOC_DAPM_PIN_SWITCH("Stereo Out"), - SOC_DAPM_PIN_SWITCH("GSM Line Out"), - SOC_DAPM_PIN_SWITCH("GSM Line In"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), - SOC_DAPM_PIN_SWITCH("Handset Mic"), - SOC_DAPM_PIN_SWITCH("Handset Spk"), - - /* This has no effect, it exists only to maintain compatibility with - * existing ALSA state files. - */ - SOC_SINGLE_EXT("Amp State Switch", 6, 0, 1, 0, - lm4853_get_state, - lm4853_set_state), - SOC_SINGLE_EXT("Amp Spk Switch", 7, 0, 1, 0, - lm4853_get_spk, - lm4853_set_spk), -}; - -/* - * This is an example machine initialisation for a wm8753 connected to a - * neo1973 GTA02. - */ -static int neo1973_gta02_wm8753_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - int err; - - /* set up NC codec pins */ - snd_soc_dapm_nc_pin(dapm, "OUT3"); - snd_soc_dapm_nc_pin(dapm, "OUT4"); - snd_soc_dapm_nc_pin(dapm, "LINE1"); - snd_soc_dapm_nc_pin(dapm, "LINE2"); - - /* Add neo1973 gta02 specific widgets */ - snd_soc_dapm_new_controls(dapm, wm8753_dapm_widgets, - ARRAY_SIZE(wm8753_dapm_widgets)); - - /* add neo1973 gta02 specific controls */ - err = snd_soc_add_controls(codec, wm8753_neo1973_gta02_controls, - ARRAY_SIZE(wm8753_neo1973_gta02_controls)); - - if (err < 0) - return err; - - /* set up neo1973 gta02 specific audio path audio_map */ - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - - /* set endpoints to default off mode */ - snd_soc_dapm_disable_pin(dapm, "Stereo Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Handset Mic"); - snd_soc_dapm_disable_pin(dapm, "Handset Spk"); - - /* allow audio paths from the GSM modem to run during suspend */ - snd_soc_dapm_ignore_suspend(dapm, "Stereo Out"); - snd_soc_dapm_ignore_suspend(dapm, "GSM Line Out"); - snd_soc_dapm_ignore_suspend(dapm, "GSM Line In"); - snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); - snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); - snd_soc_dapm_ignore_suspend(dapm, "Handset Spk"); - - snd_soc_dapm_sync(dapm); - - return 0; -} - -/* - * BT Codec DAI - */ -static struct snd_soc_dai_driver bt_dai = { - .name = "bluetooth-dai", - .playback = { - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .capture = { - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, -}; - -static struct snd_soc_dai_link neo1973_gta02_dai[] = { -{ /* Hifi Playback - for similatious use with voice below */ - .name = "WM8753", - .stream_name = "WM8753 HiFi", - .cpu_dai_name = "s3c24xx-iis", - .codec_dai_name = "wm8753-hifi", - .init = neo1973_gta02_wm8753_init, - .platform_name = "samsung-audio", - .codec_name = "wm8753-codec.0-001a", - .ops = &neo1973_gta02_hifi_ops, -}, -{ /* Voice via BT */ - .name = "Bluetooth", - .stream_name = "Voice", - .cpu_dai_name = "bluetooth-dai", - .codec_dai_name = "wm8753-voice", - .ops = &neo1973_gta02_voice_ops, - .codec_name = "wm8753-codec.0-001a", - .platform_name = "samsung-audio", -}, -}; - -static struct snd_soc_card neo1973_gta02 = { - .name = "neo1973-gta02", - .dai_link = neo1973_gta02_dai, - .num_links = ARRAY_SIZE(neo1973_gta02_dai), -}; - -static struct platform_device *neo1973_gta02_snd_device; - -static int __init neo1973_gta02_init(void) -{ - int ret; - - if (!machine_is_neo1973_gta02()) { - printk(KERN_INFO - "Only GTA02 is supported by this ASoC driver\n"); - return -ENODEV; - } - - neo1973_gta02_snd_device = platform_device_alloc("soc-audio", -1); - if (!neo1973_gta02_snd_device) - return -ENOMEM; - - /* register bluetooth DAI here */ - ret = snd_soc_register_dai(&neo1973_gta02_snd_device->dev, &bt_dai); - if (ret) - goto err_put_device; - - platform_set_drvdata(neo1973_gta02_snd_device, &neo1973_gta02); - ret = platform_device_add(neo1973_gta02_snd_device); - - if (ret) - goto err_unregister_dai; - - /* Initialise GPIOs used by amp */ - ret = gpio_request(GTA02_GPIO_HP_IN, "GTA02_HP_IN"); - if (ret) { - pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_HP_IN); - goto err_del_device; - } - - ret = gpio_direction_output(GTA02_GPIO_HP_IN, 1); - if (ret) { - pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_HP_IN); - goto err_free_gpio_hp_in; - } - - ret = gpio_request(GTA02_GPIO_AMP_SHUT, "GTA02_AMP_SHUT"); - if (ret) { - pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_AMP_SHUT); - goto err_free_gpio_hp_in; - } - - ret = gpio_direction_output(GTA02_GPIO_AMP_SHUT, 1); - if (ret) { - pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_AMP_SHUT); - goto err_free_gpio_amp_shut; - } - - return 0; - -err_free_gpio_amp_shut: - gpio_free(GTA02_GPIO_AMP_SHUT); -err_free_gpio_hp_in: - gpio_free(GTA02_GPIO_HP_IN); -err_del_device: - platform_device_del(neo1973_gta02_snd_device); -err_unregister_dai: - snd_soc_unregister_dai(&neo1973_gta02_snd_device->dev); -err_put_device: - platform_device_put(neo1973_gta02_snd_device); - return ret; -} -module_init(neo1973_gta02_init); - -static void __exit neo1973_gta02_exit(void) -{ - snd_soc_unregister_dai(&neo1973_gta02_snd_device->dev); - platform_device_unregister(neo1973_gta02_snd_device); - gpio_free(GTA02_GPIO_HP_IN); - gpio_free(GTA02_GPIO_AMP_SHUT); -} -module_exit(neo1973_gta02_exit); - -/* Module information */ -MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org"); -MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 GTA02"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c index d3cd6888a81..78bfdb3f5d7 100644 --- a/sound/soc/samsung/neo1973_wm8753.c +++ b/sound/soc/samsung/neo1973_wm8753.c @@ -1,56 +1,32 @@ /* - * neo1973_wm8753.c -- SoC audio for Neo1973 + * neo1973_wm8753.c -- SoC audio for Openmoko Neo1973 and Freerunner devices * + * Copyright 2007 Openmoko Inc + * Author: Graeme Gregory <graeme@openmoko.org> * Copyright 2007 Wolfson Microelectronics PLC. * Author: Graeme Gregory * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com + * Copyright 2009 Wolfson Microelectronics * * 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/module.h> -#include <linux/moduleparam.h> -#include <linux/timer.h> -#include <linux/interrupt.h> #include <linux/platform_device.h> -#include <linux/i2c.h> -#include <sound/core.h> -#include <sound/pcm.h> +#include <linux/gpio.h> + #include <sound/soc.h> -#include <sound/tlv.h> #include <asm/mach-types.h> -#include <mach/regs-clock.h> -#include <mach/regs-gpio.h> -#include <mach/hardware.h> -#include <linux/io.h> -#include <mach/spi-gpio.h> - #include <plat/regs-iis.h> +#include <mach/gta02.h> #include "../codecs/wm8753.h" -#include "lm4857.h" -#include "dma.h" #include "s3c24xx-i2s.h" -/* define the scenarios */ -#define NEO_AUDIO_OFF 0 -#define NEO_GSM_CALL_AUDIO_HANDSET 1 -#define NEO_GSM_CALL_AUDIO_HEADSET 2 -#define NEO_GSM_CALL_AUDIO_BLUETOOTH 3 -#define NEO_STEREO_TO_SPEAKERS 4 -#define NEO_STEREO_TO_HEADPHONES 5 -#define NEO_CAPTURE_HANDSET 6 -#define NEO_CAPTURE_HEADSET 7 -#define NEO_CAPTURE_BLUETOOTH 8 - -static struct snd_soc_card neo1973; -static struct i2c_client *i2c; - static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -61,8 +37,6 @@ static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream, int ret = 0; unsigned long iis_clkrate; - pr_debug("Entered %s\n", __func__); - iis_clkrate = s3c24xx_i2s_get_clockrate(); switch (params_rate(params)) { @@ -147,8 +121,6 @@ static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; - pr_debug("Entered %s\n", __func__); - /* disable the PLL */ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0); } @@ -170,8 +142,6 @@ static int neo1973_voice_hw_params(struct snd_pcm_substream *substream, int ret = 0; unsigned long iis_clkrate; - pr_debug("Entered %s\n", __func__); - iis_clkrate = s3c24xx_i2s_get_clockrate(); if (params_rate(params) != 8000) @@ -213,8 +183,6 @@ static int neo1973_voice_hw_free(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; - pr_debug("Entered %s\n", __func__); - /* disable the PLL */ return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0); } @@ -224,335 +192,232 @@ static struct snd_soc_ops neo1973_voice_ops = { .hw_free = neo1973_voice_hw_free, }; -static int neo1973_scenario; - -static int neo1973_get_scenario(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.integer.value[0] = neo1973_scenario; - return 0; -} - -static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario) -{ - struct snd_soc_dapm_context *dapm = &codec->dapm; - - pr_debug("Entered %s\n", __func__); - - switch (neo1973_scenario) { - case NEO_AUDIO_OFF: - snd_soc_dapm_disable_pin(dapm, "Audio Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - break; - case NEO_GSM_CALL_AUDIO_HANDSET: - snd_soc_dapm_enable_pin(dapm, "Audio Out"); - snd_soc_dapm_enable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_enable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_enable_pin(dapm, "Call Mic"); - break; - case NEO_GSM_CALL_AUDIO_HEADSET: - snd_soc_dapm_enable_pin(dapm, "Audio Out"); - snd_soc_dapm_enable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_enable_pin(dapm, "GSM Line In"); - snd_soc_dapm_enable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - break; - case NEO_GSM_CALL_AUDIO_BLUETOOTH: - snd_soc_dapm_disable_pin(dapm, "Audio Out"); - snd_soc_dapm_enable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_enable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - break; - case NEO_STEREO_TO_SPEAKERS: - snd_soc_dapm_enable_pin(dapm, "Audio Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - break; - case NEO_STEREO_TO_HEADPHONES: - snd_soc_dapm_enable_pin(dapm, "Audio Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - break; - case NEO_CAPTURE_HANDSET: - snd_soc_dapm_disable_pin(dapm, "Audio Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_enable_pin(dapm, "Call Mic"); - break; - case NEO_CAPTURE_HEADSET: - snd_soc_dapm_disable_pin(dapm, "Audio Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_enable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - break; - case NEO_CAPTURE_BLUETOOTH: - snd_soc_dapm_disable_pin(dapm, "Audio Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - break; - default: - snd_soc_dapm_disable_pin(dapm, "Audio Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); - snd_soc_dapm_disable_pin(dapm, "GSM Line In"); - snd_soc_dapm_disable_pin(dapm, "Headset Mic"); - snd_soc_dapm_disable_pin(dapm, "Call Mic"); - } +/* Shared routes and controls */ - snd_soc_dapm_sync(dapm); +static const struct snd_soc_dapm_widget neo1973_wm8753_dapm_widgets[] = { + SND_SOC_DAPM_LINE("GSM Line Out", NULL), + SND_SOC_DAPM_LINE("GSM Line In", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Handset Mic", NULL), +}; - return 0; -} +static const struct snd_soc_dapm_route neo1973_wm8753_routes[] = { + /* Connections to the GSM Module */ + {"GSM Line Out", NULL, "MONO1"}, + {"GSM Line Out", NULL, "MONO2"}, + {"RXP", NULL, "GSM Line In"}, + {"RXN", NULL, "GSM Line In"}, -static int neo1973_set_scenario(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + /* Connections to Headset */ + {"MIC1", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Headset Mic"}, - pr_debug("Entered %s\n", __func__); + /* Call Mic */ + {"MIC2", NULL, "Mic Bias"}, + {"MIC2N", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Handset Mic"}, - if (neo1973_scenario == ucontrol->value.integer.value[0]) - return 0; + /* Connect the ALC pins */ + {"ACIN", NULL, "ACOP"}, +}; - neo1973_scenario = ucontrol->value.integer.value[0]; - set_scenario_endpoints(codec, neo1973_scenario); - return 1; -} +static const struct snd_kcontrol_new neo1973_wm8753_controls[] = { + SOC_DAPM_PIN_SWITCH("GSM Line Out"), + SOC_DAPM_PIN_SWITCH("GSM Line In"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Handset Mic"), +}; -static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0}; +/* GTA02 specific routes and controlls */ -static void lm4857_write_regs(void) -{ - pr_debug("Entered %s\n", __func__); +#ifdef CONFIG_MACH_NEO1973_GTA02 - if (i2c_master_send(i2c, lm4857_regs, 4) != 4) - printk(KERN_ERR "lm4857: i2c write failed\n"); -} +static int gta02_speaker_enabled; -static int lm4857_get_reg(struct snd_kcontrol *kcontrol, +static int lm4853_set_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int reg = mc->reg; - int shift = mc->shift; - int mask = mc->max; + gta02_speaker_enabled = ucontrol->value.integer.value[0]; - pr_debug("Entered %s\n", __func__); + gpio_set_value(GTA02_GPIO_HP_IN, !gta02_speaker_enabled); - ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask; return 0; } -static int lm4857_set_reg(struct snd_kcontrol *kcontrol, +static int lm4853_get_spk(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int reg = mc->reg; - int shift = mc->shift; - int mask = mc->max; - - if (((lm4857_regs[reg] >> shift) & mask) == - ucontrol->value.integer.value[0]) - return 0; - - lm4857_regs[reg] &= ~(mask << shift); - lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift; - lm4857_write_regs(); - return 1; + ucontrol->value.integer.value[0] = gta02_speaker_enabled; + return 0; } -static int lm4857_get_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int lm4853_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) { - u8 value = lm4857_regs[LM4857_CTRL] & 0x0F; - - pr_debug("Entered %s\n", __func__); - - if (value) - value -= 5; + gpio_set_value(GTA02_GPIO_AMP_SHUT, SND_SOC_DAPM_EVENT_OFF(event)); - ucontrol->value.integer.value[0] = value; return 0; } -static int lm4857_set_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - u8 value = ucontrol->value.integer.value[0]; - - pr_debug("Entered %s\n", __func__); - - if (value) - value += 5; - - if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value) - return 0; - - lm4857_regs[LM4857_CTRL] &= 0xF0; - lm4857_regs[LM4857_CTRL] |= value; - lm4857_write_regs(); - return 1; -} +static const struct snd_soc_dapm_route neo1973_gta02_routes[] = { + /* Connections to the amp */ + {"Stereo Out", NULL, "LOUT1"}, + {"Stereo Out", NULL, "ROUT1"}, -static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = { - SND_SOC_DAPM_LINE("Audio Out", NULL), - SND_SOC_DAPM_LINE("GSM Line Out", NULL), - SND_SOC_DAPM_LINE("GSM Line In", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Call Mic", NULL), + /* Call Speaker */ + {"Handset Spk", NULL, "LOUT2"}, + {"Handset Spk", NULL, "ROUT2"}, }; +static const struct snd_kcontrol_new neo1973_gta02_wm8753_controls[] = { + SOC_DAPM_PIN_SWITCH("Handset Spk"), + SOC_DAPM_PIN_SWITCH("Stereo Out"), -static const struct snd_soc_dapm_route dapm_routes[] = { - - /* Connections to the lm4857 amp */ - {"Audio Out", NULL, "LOUT1"}, - {"Audio Out", NULL, "ROUT1"}, - - /* Connections to the GSM Module */ - {"GSM Line Out", NULL, "MONO1"}, - {"GSM Line Out", NULL, "MONO2"}, - {"RXP", NULL, "GSM Line In"}, - {"RXN", NULL, "GSM Line In"}, + SOC_SINGLE_BOOL_EXT("Amp Spk Switch", 0, + lm4853_get_spk, + lm4853_set_spk), +}; - /* Connections to Headset */ - {"MIC1", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Headset Mic"}, +static const struct snd_soc_dapm_widget neo1973_gta02_wm8753_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Handset Spk", NULL), + SND_SOC_DAPM_SPK("Stereo Out", lm4853_event), +}; - /* Call Mic */ - {"MIC2", NULL, "Mic Bias"}, - {"MIC2N", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Call Mic"}, +static int neo1973_gta02_wm8753_init(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = &codec->dapm; + int ret; - /* Connect the ALC pins */ - {"ACIN", NULL, "ACOP"}, -}; + ret = snd_soc_dapm_new_controls(dapm, neo1973_gta02_wm8753_dapm_widgets, + ARRAY_SIZE(neo1973_gta02_wm8753_dapm_widgets)); + if (ret) + return ret; -static const char *lm4857_mode[] = { - "Off", - "Call Speaker", - "Stereo Speakers", - "Stereo Speakers + Headphones", - "Headphones" -}; + ret = snd_soc_dapm_add_routes(dapm, neo1973_gta02_routes, + ARRAY_SIZE(neo1973_gta02_routes)); + if (ret) + return ret; -static const struct soc_enum lm4857_mode_enum[] = { - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode), -}; + ret = snd_soc_add_controls(codec, neo1973_gta02_wm8753_controls, + ARRAY_SIZE(neo1973_gta02_wm8753_controls)); + if (ret) + return ret; -static const char *neo_scenarios[] = { - "Off", - "GSM Handset", - "GSM Headset", - "GSM Bluetooth", - "Speakers", - "Headphones", - "Capture Handset", - "Capture Headset", - "Capture Bluetooth" -}; + snd_soc_dapm_disable_pin(dapm, "Stereo Out"); + snd_soc_dapm_disable_pin(dapm, "Handset Spk"); + snd_soc_dapm_ignore_suspend(dapm, "Stereo Out"); + snd_soc_dapm_ignore_suspend(dapm, "Handset Spk"); -static const struct soc_enum neo_scenario_enum[] = { - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios), neo_scenarios), -}; + return 0; +} -static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0); -static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0); - -static const struct snd_kcontrol_new wm8753_neo1973_controls[] = { - SOC_SINGLE_EXT_TLV("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0, - lm4857_get_reg, lm4857_set_reg, stereo_tlv), - SOC_SINGLE_EXT_TLV("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0, - lm4857_get_reg, lm4857_set_reg, stereo_tlv), - SOC_SINGLE_EXT_TLV("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0, - lm4857_get_reg, lm4857_set_reg, mono_tlv), - SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0], - lm4857_get_mode, lm4857_set_mode), - SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0], - neo1973_get_scenario, neo1973_set_scenario), - SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0, - lm4857_get_reg, lm4857_set_reg), - SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0, - lm4857_get_reg, lm4857_set_reg), - SOC_SINGLE_EXT("Amp Fast Wakeup Playback Switch", LM4857_CTRL, 5, 1, 0, - lm4857_get_reg, lm4857_set_reg), - SOC_SINGLE_EXT("Amp Earpiece 6dB Playback Switch", LM4857_CTRL, 4, 1, 0, - lm4857_get_reg, lm4857_set_reg), -}; +#else +static int neo1973_gta02_wm8753_init(struct snd_soc_code *codec) { return 0; } +#endif -/* - * This is an example machine initialisation for a wm8753 connected to a - * neo1973 II. It is missing logic to detect hp/mic insertions and logic - * to re-route the audio in such an event. - */ static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dapm_context *dapm = &codec->dapm; - int err; - - pr_debug("Entered %s\n", __func__); + int ret; /* set up NC codec pins */ - snd_soc_dapm_nc_pin(dapm, "LOUT2"); - snd_soc_dapm_nc_pin(dapm, "ROUT2"); + if (machine_is_neo1973_gta01()) { + snd_soc_dapm_nc_pin(dapm, "LOUT2"); + snd_soc_dapm_nc_pin(dapm, "ROUT2"); + } snd_soc_dapm_nc_pin(dapm, "OUT3"); snd_soc_dapm_nc_pin(dapm, "OUT4"); snd_soc_dapm_nc_pin(dapm, "LINE1"); snd_soc_dapm_nc_pin(dapm, "LINE2"); /* Add neo1973 specific widgets */ - snd_soc_dapm_new_controls(dapm, wm8753_dapm_widgets, - ARRAY_SIZE(wm8753_dapm_widgets)); - - /* set endpoints to default mode */ - set_scenario_endpoints(codec, NEO_AUDIO_OFF); + ret = snd_soc_dapm_new_controls(dapm, neo1973_wm8753_dapm_widgets, + ARRAY_SIZE(neo1973_wm8753_dapm_widgets)); + if (ret) + return ret; /* add neo1973 specific controls */ - err = snd_soc_add_controls(codec, wm8753_neo1973_controls, - ARRAY_SIZE(8753_neo1973_controls)); - if (err < 0) - return err; + ret = snd_soc_add_controls(codec, neo1973_wm8753_controls, + ARRAY_SIZE(neo1973_wm8753_controls)); + if (ret) + return ret; /* set up neo1973 specific audio routes */ - err = snd_soc_dapm_add_routes(dapm, dapm_routes, - ARRAY_SIZE(dapm_routes)); + ret = snd_soc_dapm_add_routes(dapm, neo1973_wm8753_routes, + ARRAY_SIZE(neo1973_wm8753_routes)); + if (ret) + return ret; + + /* set endpoints to default off mode */ + snd_soc_dapm_disable_pin(dapm, "GSM Line Out"); + snd_soc_dapm_disable_pin(dapm, "GSM Line In"); + snd_soc_dapm_disable_pin(dapm, "Headset Mic"); + snd_soc_dapm_disable_pin(dapm, "Handset Mic"); + + /* allow audio paths from the GSM modem to run during suspend */ + snd_soc_dapm_ignore_suspend(dapm, "GSM Line Out"); + snd_soc_dapm_ignore_suspend(dapm, "GSM Line In"); + snd_soc_dapm_ignore_suspend(dapm, "Headset Mic"); + snd_soc_dapm_ignore_suspend(dapm, "Handset Mic"); + + if (machine_is_neo1973_gta02()) { + ret = neo1973_gta02_wm8753_init(codec); + if (ret) + return ret; + } snd_soc_dapm_sync(dapm); + return 0; } -/* - * BT Codec DAI - */ -static struct snd_soc_dai bt_dai = { - .name = "bluetooth-dai", - .playback = { - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .capture = { - .channels_min = 1, - .channels_max = 1, - .rates = SNDRV_PCM_RATE_8000, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, +/* GTA01 specific controlls */ + +#ifdef CONFIG_MACH_NEO1973_GTA01 + +static const struct snd_soc_dapm_route neo1973_lm4857_routes[] = { + {"Amp IN", NULL, "ROUT1"}, + {"Amp IN", NULL, "LOUT1"}, + + {"Handset Spk", NULL, "Amp EP"}, + {"Stereo Out", NULL, "Amp LS"}, + {"Headphone", NULL, "Amp HP"}, +}; + +static const struct snd_soc_dapm_widget neo1973_lm4857_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Handset Spk", NULL), + SND_SOC_DAPM_SPK("Stereo Out", NULL), + SND_SOC_DAPM_HP("Headphone", NULL), }; +static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm) +{ + int ret; + + ret = snd_soc_dapm_new_controls(dapm, neo1973_lm4857_dapm_widgets, + ARRAY_SIZE(neo1973_lm4857_dapm_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, neo1973_lm4857_routes, + ARRAY_SIZE(neo1973_lm4857_routes)); + if (ret) + return ret; + + snd_soc_dapm_ignore_suspend(dapm, "Stereo Out"); + snd_soc_dapm_ignore_suspend(dapm, "Handset Spk"); + snd_soc_dapm_ignore_suspend(dapm, "Headphone"); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +#else +static int neo1973_lm4857_init(struct snd_soc_dapm_context *dapm) { return 0; }; +#endif + static struct snd_soc_dai_link neo1973_dai[] = { { /* Hifi Playback - for similatious use with voice below */ .name = "WM8753", @@ -568,90 +433,49 @@ static struct snd_soc_dai_link neo1973_dai[] = { .name = "Bluetooth", .stream_name = "Voice", .platform_name = "samsung-audio", - .cpu_dai_name = "bluetooth-dai", + .cpu_dai_name = "dfbmcs320-pcm", .codec_dai_name = "wm8753-voice", .codec_name = "wm8753-codec.0-001a", .ops = &neo1973_voice_ops, }, }; -static struct snd_soc_card neo1973 = { - .name = "neo1973", - .dai_link = neo1973_dai, - .num_links = ARRAY_SIZE(neo1973_dai), +static struct snd_soc_aux_dev neo1973_aux_devs[] = { + { + .name = "dfbmcs320", + .codec_name = "dfbmcs320.0", + }, + { + .name = "lm4857", + .codec_name = "lm4857.0-007c", + .init = neo1973_lm4857_init, + }, }; -static int lm4857_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - pr_debug("Entered %s\n", __func__); - - i2c = client; - - lm4857_write_regs(); - return 0; -} - -static int lm4857_i2c_remove(struct i2c_client *client) -{ - pr_debug("Entered %s\n", __func__); - - i2c = NULL; - - return 0; -} - -static u8 lm4857_state; - -static int lm4857_suspend(struct i2c_client *dev, pm_message_t state) -{ - pr_debug("Entered %s\n", __func__); - - dev_dbg(&dev->dev, "lm4857_suspend\n"); - lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf; - if (lm4857_state) { - lm4857_regs[LM4857_CTRL] &= 0xf0; - lm4857_write_regs(); - } - return 0; -} - -static int lm4857_resume(struct i2c_client *dev) -{ - pr_debug("Entered %s\n", __func__); - - if (lm4857_state) { - lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f); - lm4857_write_regs(); - } - return 0; -} - -static void lm4857_shutdown(struct i2c_client *dev) -{ - pr_debug("Entered %s\n", __func__); - - dev_dbg(&dev->dev, "lm4857_shutdown\n"); - lm4857_regs[LM4857_CTRL] &= 0xf0; - lm4857_write_regs(); -} +static struct snd_soc_codec_conf neo1973_codec_conf[] = { + { + .dev_name = "lm4857.0-007c", + .name_prefix = "Amp", + }, +}; -static const struct i2c_device_id lm4857_i2c_id[] = { - { "neo1973_lm4857", 0 }, - { } +#ifdef CONFIG_MACH_NEO1973_GTA02 +static const struct gpio neo1973_gta02_gpios[] = { + { GTA02_GPIO_HP_IN, GPIOF_OUT_INIT_HIGH, "GTA02_HP_IN" }, + { GTA02_GPIO_AMP_SHUT, GPIOF_OUT_INIT_HIGH, "GTA02_AMP_SHUT" }, }; +#else +static const struct gpio neo1973_gta02_gpios[] = {}; +#endif -static struct i2c_driver lm4857_i2c_driver = { - .driver = { - .name = "LM4857 I2C Amp", - .owner = THIS_MODULE, - }, - .suspend = lm4857_suspend, - .resume = lm4857_resume, - .shutdown = lm4857_shutdown, - .probe = lm4857_i2c_probe, - .remove = lm4857_i2c_remove, - .id_table = lm4857_i2c_id, +static struct snd_soc_card neo1973 = { + .name = "neo1973", + .dai_link = neo1973_dai, + .num_links = ARRAY_SIZE(neo1973_dai), + .aux_dev = neo1973_aux_devs, + .num_aux_devs = ARRAY_SIZE(neo1973_aux_devs), + .codec_conf = neo1973_codec_conf, + .num_configs = ARRAY_SIZE(neo1973_codec_conf), }; static struct platform_device *neo1973_snd_device; @@ -660,46 +484,56 @@ static int __init neo1973_init(void) { int ret; - pr_debug("Entered %s\n", __func__); - - if (!machine_is_neo1973_gta01()) { - printk(KERN_INFO - "Only GTA01 hardware supported by ASoC driver\n"); + if (!machine_is_neo1973_gta01() && !machine_is_neo1973_gta02()) return -ENODEV; + + if (machine_is_neo1973_gta02()) { + neo1973.name = "neo1973gta02"; + neo1973.num_aux_devs = 1; + + ret = gpio_request_array(neo1973_gta02_gpios, + ARRAY_SIZE(neo1973_gta02_gpios)); + if (ret) + return ret; } neo1973_snd_device = platform_device_alloc("soc-audio", -1); - if (!neo1973_snd_device) - return -ENOMEM; + if (!neo1973_snd_device) { + ret = -ENOMEM; + goto err_gpio_free; + } platform_set_drvdata(neo1973_snd_device, &neo1973); ret = platform_device_add(neo1973_snd_device); - if (ret) { - platform_device_put(neo1973_snd_device); - return ret; - } - - ret = i2c_add_driver(&lm4857_i2c_driver); + if (ret) + goto err_put_device; - if (ret != 0) - platform_device_unregister(neo1973_snd_device); + return 0; +err_put_device: + platform_device_put(neo1973_snd_device); +err_gpio_free: + if (machine_is_neo1973_gta02()) { + gpio_free_array(neo1973_gta02_gpios, + ARRAY_SIZE(neo1973_gta02_gpios)); + } return ret; } +module_init(neo1973_init); static void __exit neo1973_exit(void) { - pr_debug("Entered %s\n", __func__); - - i2c_del_driver(&lm4857_i2c_driver); platform_device_unregister(neo1973_snd_device); -} -module_init(neo1973_init); + if (machine_is_neo1973_gta02()) { + gpio_free_array(neo1973_gta02_gpios, + ARRAY_SIZE(neo1973_gta02_gpios)); + } +} module_exit(neo1973_exit); /* Module information */ MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org"); -MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973"); +MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 and Frerunner"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c index 3cb70075107..dc9d551f678 100644 --- a/sound/soc/samsung/s3c24xx_uda134x.c +++ b/sound/soc/samsung/s3c24xx_uda134x.c @@ -219,7 +219,7 @@ static struct snd_soc_ops s3c24xx_uda134x_ops = { static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = { .name = "UDA134X", .stream_name = "UDA134X", - .codec_name = "uda134x-hifi", + .codec_name = "uda134x-codec", .codec_dai_name = "uda134x-hifi", .cpu_dai_name = "s3c24xx-iis", .ops = &s3c24xx_uda134x_ops, @@ -314,6 +314,7 @@ static int s3c24xx_uda134x_probe(struct platform_device *pdev) platform_set_drvdata(s3c24xx_uda134x_snd_device, &snd_soc_s3c24xx_uda134x); + platform_device_add_data(s3c24xx_uda134x_snd_device, &s3c24xx_uda134x, sizeof(s3c24xx_uda134x)); ret = platform_device_add(s3c24xx_uda134x_snd_device); if (ret) { printk(KERN_ERR "S3C24XX_UDA134X SoC Audio: Unable to add\n"); diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index db66dc44add..5d76da43b14 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -1609,24 +1609,23 @@ int snd_soc_cache_sync(struct snd_soc_codec *codec) return 0; } + if (!codec->cache_ops || !codec->cache_ops->sync) + return -EINVAL; + if (codec->cache_ops->name) name = codec->cache_ops->name; else name = "unknown"; - if (codec->cache_ops && codec->cache_ops->sync) { - if (codec->cache_ops->name) - dev_dbg(codec->dev, "Syncing %s cache for %s codec\n", - codec->cache_ops->name, codec->name); - trace_snd_soc_cache_sync(codec, name, "start"); - ret = codec->cache_ops->sync(codec); - if (!ret) - codec->cache_sync = 0; - trace_snd_soc_cache_sync(codec, name, "end"); - return ret; - } - - return -EINVAL; + if (codec->cache_ops->name) + dev_dbg(codec->dev, "Syncing %s cache for %s codec\n", + codec->cache_ops->name, codec->name); + trace_snd_soc_cache_sync(codec, name, "start"); + ret = codec->cache_ops->sync(codec); + if (!ret) + codec->cache_sync = 0; + trace_snd_soc_cache_sync(codec, name, "end"); + return ret; } EXPORT_SYMBOL_GPL(snd_soc_cache_sync); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 205cbd7b149..4dda58926bc 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -87,15 +87,56 @@ static int min_bytes_needed(unsigned long val) return c; } +/* fill buf which is 'len' bytes with a formatted + * string of the form 'reg: value\n' */ +static int format_register_str(struct snd_soc_codec *codec, + unsigned int reg, char *buf, size_t len) +{ + int wordsize = codec->driver->reg_word_size * 2; + int regsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; + int ret; + char tmpbuf[len + 1]; + char regbuf[regsize + 1]; + + /* since tmpbuf is allocated on the stack, warn the callers if they + * try to abuse this function */ + WARN_ON(len > 63); + + /* +2 for ': ' and + 1 for '\n' */ + if (wordsize + regsize + 2 + 1 != len) + return -EINVAL; + + ret = snd_soc_read(codec , reg); + if (ret < 0) { + memset(regbuf, 'X', regsize); + regbuf[regsize] = '\0'; + } else { + snprintf(regbuf, regsize + 1, "%.*x", regsize, ret); + } + + /* prepare the buffer */ + snprintf(tmpbuf, len + 1, "%.*x: %s\n", wordsize, reg, regbuf); + /* copy it back to the caller without the '\0' */ + memcpy(buf, tmpbuf, len); + + return 0; +} + /* codec register dump */ -static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) +static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf, + size_t count, loff_t pos) { - int ret, i, step = 1, count = 0; + int i, step = 1; int wordsize, regsize; + int len; + size_t total = 0; + loff_t p = 0; wordsize = codec->driver->reg_word_size * 2; regsize = min_bytes_needed(codec->driver->reg_cache_size) * 2; + len = wordsize + regsize + 2 + 1; + if (!codec->driver->reg_cache_size) return 0; @@ -105,51 +146,34 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) for (i = 0; i < codec->driver->reg_cache_size; i += step) { if (codec->readable_register && !codec->readable_register(codec, i)) continue; - - count += sprintf(buf + count, "%.*x: ", regsize, i); - if (count >= PAGE_SIZE - 1) - break; - if (codec->driver->display_register) { count += codec->driver->display_register(codec, buf + count, PAGE_SIZE - count, i); } else { - /* If the read fails it's almost certainly due to - * the register being volatile and the device being - * powered off. - */ - ret = snd_soc_read(codec, i); - if (ret >= 0) - count += snprintf(buf + count, - PAGE_SIZE - count, - "%.*x", wordsize, ret); - else - count += snprintf(buf + count, - PAGE_SIZE - count, - "<no data: %d>", ret); + /* only support larger than PAGE_SIZE bytes debugfs + * entries for the default case */ + if (p >= pos) { + if (total + len >= count - 1) + break; + format_register_str(codec, i, buf + total, len); + total += len; + } + p += len; } - - if (count >= PAGE_SIZE - 1) - break; - - count += snprintf(buf + count, PAGE_SIZE - count, "\n"); - if (count >= PAGE_SIZE - 1) - break; } - /* Truncate count; min() would cause a warning */ - if (count >= PAGE_SIZE) - count = PAGE_SIZE - 1; + total = min(total, count - 1); - return count; + return total; } + static ssize_t codec_reg_show(struct device *dev, struct device_attribute *attr, char *buf) { struct snd_soc_pcm_runtime *rtd = container_of(dev, struct snd_soc_pcm_runtime, dev); - return soc_codec_reg_show(rtd->codec, buf); + return soc_codec_reg_show(rtd->codec, buf, PAGE_SIZE, 0); } static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL); @@ -188,16 +212,28 @@ static int codec_reg_open_file(struct inode *inode, struct file *file) } static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { ssize_t ret; struct snd_soc_codec *codec = file->private_data; - char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + char *buf; + + if (*ppos < 0 || !count) + return -EINVAL; + + buf = kmalloc(count, GFP_KERNEL); if (!buf) return -ENOMEM; - ret = soc_codec_reg_show(codec, buf); - if (ret >= 0) - ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + + ret = soc_codec_reg_show(codec, buf, count, *ppos); + if (ret >= 0) { + if (copy_to_user(user_buf, buf, ret)) { + kfree(buf); + return -EFAULT; + } + *ppos += ret; + } + kfree(buf); return ret; } @@ -223,8 +259,6 @@ static ssize_t codec_reg_write_file(struct file *file, while (*start == ' ') start++; reg = simple_strtoul(start, &start, 16); - if ((reg >= codec->driver->reg_cache_size) || (reg % step)) - return -EINVAL; while (*start == ' ') start++; if (strict_strtoul(start, 16, &value)) @@ -464,20 +498,30 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret; - if (codec_dai->driver->symmetric_rates || cpu_dai->driver->symmetric_rates || - rtd->dai_link->symmetric_rates) { - dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", - rtd->rate); + if (!codec_dai->driver->symmetric_rates && + !cpu_dai->driver->symmetric_rates && + !rtd->dai_link->symmetric_rates) + return 0; - ret = snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_RATE, - rtd->rate, - rtd->rate); - if (ret < 0) { - dev_err(&rtd->dev, - "Unable to apply rate symmetry constraint: %d\n", ret); - return ret; - } + /* This can happen if multiple streams are starting simultaneously - + * the second can need to get its constraints before the first has + * picked a rate. Complain and allow the application to carry on. + */ + if (!rtd->rate) { + dev_warn(&rtd->dev, + "Not enforcing symmetric_rates due to race\n"); + return 0; + } + + dev_dbg(&rtd->dev, "Symmetry forces %dHz rate\n", rtd->rate); + + ret = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_RATE, + rtd->rate, rtd->rate); + if (ret < 0) { + dev_err(&rtd->dev, + "Unable to apply rate symmetry constraint: %d\n", ret); + return ret; } return 0; @@ -1428,6 +1472,7 @@ static int soc_probe_codec(struct snd_soc_card *card, struct snd_soc_codec *codec) { int ret = 0; + const struct snd_soc_codec_driver *driver = codec->driver; codec->card = card; codec->dapm.card = card; @@ -1436,8 +1481,8 @@ static int soc_probe_codec(struct snd_soc_card *card, if (!try_module_get(codec->dev->driver->owner)) return -ENODEV; - if (codec->driver->probe) { - ret = codec->driver->probe(codec); + if (driver->probe) { + ret = driver->probe(codec); if (ret < 0) { dev_err(codec->dev, "asoc: failed to probe CODEC %s: %d\n", @@ -1446,6 +1491,13 @@ static int soc_probe_codec(struct snd_soc_card *card, } } + if (driver->dapm_widgets) + snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets, + driver->num_dapm_widgets); + if (driver->dapm_routes) + snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, + driver->num_dapm_routes); + soc_init_codec_debugfs(codec); /* mark codec as probed and add to card codec list */ @@ -1482,6 +1534,7 @@ static int soc_post_component_init(struct snd_soc_card *card, rtd = &card->rtd_aux[num]; name = aux_dev->name; } + rtd->card = card; /* machine controls, routes and widgets are not prefixed */ temp = codec->name_prefix; @@ -1503,7 +1556,6 @@ static int soc_post_component_init(struct snd_soc_card *card, /* register the rtd device */ rtd->codec = codec; - rtd->card = card; rtd->dev.parent = card->dev; rtd->dev.release = rtd_release; rtd->dev.init_name = name; @@ -1801,7 +1853,12 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } card->snd_card->dev = card->dev; -#ifdef CONFIG_PM + card->dapm.bias_level = SND_SOC_BIAS_OFF; + card->dapm.dev = card->dev; + card->dapm.card = card; + list_add(&card->dapm.list, &card->dapm_list); + +#ifdef CONFIG_PM_SLEEP /* deferred resume work */ INIT_WORK(&card->deferred_resume_work, soc_resume_deferred); #endif @@ -1831,11 +1888,37 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } } + if (card->dapm_widgets) + snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, + card->num_dapm_widgets); + if (card->dapm_routes) + snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, + card->num_dapm_routes); + +#ifdef CONFIG_DEBUG_FS + card->dapm.debugfs_dapm = debugfs_create_dir("dapm", + card->debugfs_card_root); + if (!card->dapm.debugfs_dapm) + printk(KERN_WARNING + "Failed to create card DAPM debugfs directory\n"); + + snd_soc_dapm_debugfs_init(&card->dapm); +#endif + snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), "%s", card->name); snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), "%s", card->name); + if (card->late_probe) { + ret = card->late_probe(card); + if (ret < 0) { + dev_err(card->dev, "%s late_probe() failed: %d\n", + card->name, ret); + goto probe_aux_dev_err; + } + } + ret = snd_card_register(card->snd_card); if (ret < 0) { printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name); @@ -2259,22 +2342,45 @@ EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hwparams); * @_template: control template * @data: control private data * @long_name: control long name + * @prefix: control name prefix * * Create a new mixer control from a template control. * * Returns 0 for success, else error. */ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, - void *data, char *long_name) + void *data, char *long_name, + const char *prefix) { struct snd_kcontrol_new template; + struct snd_kcontrol *kcontrol; + char *name = NULL; + int name_len; memcpy(&template, _template, sizeof(template)); - if (long_name) - template.name = long_name; template.index = 0; - return snd_ctl_new1(&template, data); + if (!long_name) + long_name = template.name; + + if (prefix) { + name_len = strlen(long_name) + strlen(prefix) + 2; + name = kmalloc(name_len, GFP_ATOMIC); + if (!name) + return NULL; + + snprintf(name, name_len, "%s %s", prefix, long_name); + + template.name = name; + } else { + template.name = long_name; + } + + kcontrol = snd_ctl_new1(&template, data); + + kfree(name); + + return kcontrol; } EXPORT_SYMBOL_GPL(snd_soc_cnew); @@ -2293,22 +2399,16 @@ int snd_soc_add_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, int num_controls) { struct snd_card *card = codec->card->snd_card; - char prefixed_name[44], *name; int err, i; for (i = 0; i < num_controls; i++) { const struct snd_kcontrol_new *control = &controls[i]; - if (codec->name_prefix) { - snprintf(prefixed_name, sizeof(prefixed_name), "%s %s", - codec->name_prefix, control->name); - name = prefixed_name; - } else { - name = control->name; - } - err = snd_ctl_add(card, snd_soc_cnew(control, codec, name)); + err = snd_ctl_add(card, snd_soc_cnew(control, codec, + control->name, + codec->name_prefix)); if (err < 0) { dev_err(codec->dev, "%s: Failed to add %s: %d\n", - codec->name, name, err); + codec->name, control->name, err); return err; } } @@ -2989,12 +3089,34 @@ int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, { if (dai->driver && dai->driver->ops->set_sysclk) return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir); + else if (dai->codec && dai->codec->driver->set_sysclk) + return dai->codec->driver->set_sysclk(dai->codec, clk_id, + freq, dir); else return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); /** + * snd_soc_codec_set_sysclk - configure CODEC system or master clock. + * @codec: CODEC + * @clk_id: DAI specific clock ID + * @freq: new clock frequency in Hz + * @dir: new clock direction - input/output. + * + * Configures the CODEC master (MCLK) or system (SYSCLK) clocking. + */ +int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id, + unsigned int freq, int dir) +{ + if (codec->driver->set_sysclk) + return codec->driver->set_sysclk(codec, clk_id, freq, dir); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk); + +/** * snd_soc_dai_set_clkdiv - configure DAI clock dividers. * @dai: DAI * @div_id: DAI specific clock divider ID @@ -3030,11 +3152,35 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source, if (dai->driver && dai->driver->ops->set_pll) return dai->driver->ops->set_pll(dai, pll_id, source, freq_in, freq_out); + else if (dai->codec && dai->codec->driver->set_pll) + return dai->codec->driver->set_pll(dai->codec, pll_id, source, + freq_in, freq_out); else return -EINVAL; } EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); +/* + * snd_soc_codec_set_pll - configure codec PLL. + * @codec: CODEC + * @pll_id: DAI specific PLL ID + * @source: DAI specific source for the PLL + * @freq_in: PLL input clock frequency in Hz + * @freq_out: requested PLL output clock frequency in Hz + * + * Configures and enables PLL to generate output clock based on input clock. + */ +int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + if (codec->driver->set_pll) + return codec->driver->set_pll(codec, pll_id, source, + freq_in, freq_out); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll); + /** * snd_soc_dai_set_fmt - configure DAI hardware audio format. * @dai: DAI diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d0342aab2c1..81c4052c127 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -32,6 +32,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> +#include <linux/async.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/bitops.h> @@ -125,17 +126,17 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( /** * snd_soc_dapm_set_bias_level - set the bias level for the system - * @card: audio device + * @dapm: DAPM context * @level: level to configure * * Configure the bias (power) levels for the SoC audio device. * * Returns 0 for success else error. */ -static int snd_soc_dapm_set_bias_level(struct snd_soc_card *card, - struct snd_soc_dapm_context *dapm, +static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level) { + struct snd_soc_card *card = dapm->card; int ret = 0; switch (level) { @@ -365,9 +366,20 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, struct snd_soc_dapm_widget *w) { int i, ret = 0; - size_t name_len; + size_t name_len, prefix_len; struct snd_soc_dapm_path *path; - struct snd_card *card = dapm->codec->card->snd_card; + struct snd_card *card = dapm->card->snd_card; + const char *prefix; + + if (dapm->codec) + prefix = dapm->codec->name_prefix; + else + prefix = NULL; + + if (prefix) + prefix_len = strlen(prefix) + 1; + else + prefix_len = 0; /* add kcontrol */ for (i = 0; i < w->num_kcontrols; i++) { @@ -396,8 +408,15 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, switch (w->id) { default: + /* The control will get a prefix from + * the control creation process but + * we're also using the same prefix + * for widgets so cut the prefix off + * the front of the widget name. + */ snprintf(path->long_name, name_len, "%s %s", - w->name, w->kcontrols[i].name); + w->name + prefix_len, + w->kcontrols[i].name); break; case snd_soc_dapm_mixer_named_ctl: snprintf(path->long_name, name_len, "%s", @@ -408,7 +427,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_context *dapm, path->long_name[name_len - 1] = '\0'; path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w, - path->long_name); + path->long_name, prefix); ret = snd_ctl_add(card, path->kcontrol); if (ret < 0) { dev_err(dapm->dev, @@ -429,7 +448,9 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_path *path = NULL; struct snd_kcontrol *kcontrol; - struct snd_card *card = dapm->codec->card->snd_card; + struct snd_card *card = dapm->card->snd_card; + const char *prefix; + size_t prefix_len; int ret = 0; if (!w->num_kcontrols) { @@ -437,7 +458,22 @@ static int dapm_new_mux(struct snd_soc_dapm_context *dapm, return -EINVAL; } - kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name); + if (dapm->codec) + prefix = dapm->codec->name_prefix; + else + prefix = NULL; + + if (prefix) + prefix_len = strlen(prefix) + 1; + else + prefix_len = 0; + + /* The control will get a prefix from the control creation + * process but we're also using the same prefix for widgets so + * cut the prefix off the front of the widget name. + */ + kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name + prefix_len, + prefix); ret = snd_ctl_add(card, kcontrol); if (ret < 0) @@ -479,7 +515,7 @@ static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm) */ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) { - int level = snd_power_get_state(widget->dapm->codec->card->snd_card); + int level = snd_power_get_state(widget->dapm->card->snd_card); switch (level) { case SNDRV_CTL_POWER_D3hot: @@ -712,7 +748,15 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) !path->connected(path->source, path->sink)) continue; - if (path->sink && path->sink->power_check && + if (!path->sink) + continue; + + if (path->sink->force) { + power = 1; + break; + } + + if (path->sink->power_check && path->sink->power_check(path->sink)) { power = 1; break; @@ -963,7 +1007,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, } if (!list_empty(&pending)) - dapm_seq_run_coalesced(dapm, &pending); + dapm_seq_run_coalesced(cur_dapm, &pending); if (cur_dapm && cur_dapm->seq_notifier) { for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) @@ -1006,7 +1050,62 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm) } } +/* Async callback run prior to DAPM sequences - brings to _PREPARE if + * they're changing state. + */ +static void dapm_pre_sequence_async(void *data, async_cookie_t cookie) +{ + struct snd_soc_dapm_context *d = data; + int ret; + if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); + if (ret != 0) + dev_err(d->dev, + "Failed to turn on bias: %d\n", ret); + } + + /* If we're changing to all on or all off then prepare */ + if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) || + (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE); + if (ret != 0) + dev_err(d->dev, + "Failed to prepare bias: %d\n", ret); + } +} + +/* Async callback run prior to DAPM sequences - brings to their final + * state. + */ +static void dapm_post_sequence_async(void *data, async_cookie_t cookie) +{ + struct snd_soc_dapm_context *d = data; + int ret; + + /* If we just powered the last thing off drop to standby bias */ + if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); + if (ret != 0) + dev_err(d->dev, "Failed to apply standby bias: %d\n", + ret); + } + + /* If we're in standby and can support bias off then do that */ + if (d->bias_level == SND_SOC_BIAS_STANDBY && d->idle_bias_off) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF); + if (ret != 0) + dev_err(d->dev, "Failed to turn off bias: %d\n", ret); + } + + /* If we just powered up then move to active bias */ + if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) { + ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON); + if (ret != 0) + dev_err(d->dev, "Failed to apply active bias: %d\n", + ret); + } +} /* * Scan each dapm widget for complete audio path. @@ -1019,12 +1118,12 @@ static void dapm_widget_update(struct snd_soc_dapm_context *dapm) */ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) { - struct snd_soc_card *card = dapm->codec->card; + struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; struct snd_soc_dapm_context *d; LIST_HEAD(up_list); LIST_HEAD(down_list); - int ret = 0; + LIST_HEAD(async_domain); int power; trace_snd_soc_dapm_start(card); @@ -1102,25 +1201,11 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) } } - list_for_each_entry(d, &dapm->card->dapm_list, list) { - if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) { - ret = snd_soc_dapm_set_bias_level(card, d, - SND_SOC_BIAS_STANDBY); - if (ret != 0) - dev_err(d->dev, - "Failed to turn on bias: %d\n", ret); - } - - /* If we're changing to all on or all off then prepare */ - if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) || - (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) { - ret = snd_soc_dapm_set_bias_level(card, d, - SND_SOC_BIAS_PREPARE); - if (ret != 0) - dev_err(d->dev, - "Failed to prepare bias: %d\n", ret); - } - } + /* Run all the bias changes in parallel */ + list_for_each_entry(d, &dapm->card->dapm_list, list) + async_schedule_domain(dapm_pre_sequence_async, d, + &async_domain); + async_synchronize_full_domain(&async_domain); /* Power down widgets first; try to avoid amplifying pops. */ dapm_seq_run(dapm, &down_list, event, false); @@ -1130,37 +1215,11 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) /* Now power up. */ dapm_seq_run(dapm, &up_list, event, true); - list_for_each_entry(d, &dapm->card->dapm_list, list) { - /* If we just powered the last thing off drop to standby bias */ - if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) { - ret = snd_soc_dapm_set_bias_level(card, d, - SND_SOC_BIAS_STANDBY); - if (ret != 0) - dev_err(d->dev, - "Failed to apply standby bias: %d\n", - ret); - } - - /* If we're in standby and can support bias off then do that */ - if (d->bias_level == SND_SOC_BIAS_STANDBY && - d->idle_bias_off) { - ret = snd_soc_dapm_set_bias_level(card, d, - SND_SOC_BIAS_OFF); - if (ret != 0) - dev_err(d->dev, - "Failed to turn off bias: %d\n", ret); - } - - /* If we just powered up then move to active bias */ - if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) { - ret = snd_soc_dapm_set_bias_level(card, d, - SND_SOC_BIAS_ON); - if (ret != 0) - dev_err(d->dev, - "Failed to apply active bias: %d\n", - ret); - } - } + /* Run all the bias changes in parallel */ + list_for_each_entry(d, &dapm->card->dapm_list, list) + async_schedule_domain(dapm_post_sequence_async, d, + &async_domain); + async_synchronize_full_domain(&async_domain); pop_dbg(dapm->dev, card->pop_time, "DAPM sequencing finished, waiting %dms\n", card->pop_time); @@ -1218,7 +1277,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (p->connect) ret += snprintf(buf + ret, PAGE_SIZE - ret, - " in %s %s\n", + " in \"%s\" \"%s\"\n", p->name ? p->name : "static", p->source->name); } @@ -1228,7 +1287,7 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (p->connect) ret += snprintf(buf + ret, PAGE_SIZE - ret, - " out %s %s\n", + " out \"%s\" \"%s\"\n", p->name ? p->name : "static", p->sink->name); } @@ -1493,7 +1552,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, char prefixed_source[80]; int ret = 0; - if (dapm->codec->name_prefix) { + if (dapm->codec && dapm->codec->name_prefix) { snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", dapm->codec->name_prefix, route->sink); sink = prefixed_sink; @@ -1664,6 +1723,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_widget *w; + unsigned int val; list_for_each_entry(w, &dapm->card->widgets, list) { @@ -1712,6 +1772,18 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) case snd_soc_dapm_post: break; } + + /* Read the initial power state from the device */ + if (w->reg >= 0) { + val = snd_soc_read(w->codec, w->reg); + val &= 1 << w->shift; + if (w->invert) + val = !val; + + if (val) + w->power = 1; + } + w->new = 1; } @@ -2130,14 +2202,14 @@ int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, return -ENOMEM; name_len = strlen(widget->name) + 1; - if (dapm->codec->name_prefix) + if (dapm->codec && dapm->codec->name_prefix) name_len += 1 + strlen(dapm->codec->name_prefix); w->name = kmalloc(name_len, GFP_KERNEL); if (w->name == NULL) { kfree(w); return -ENOMEM; } - if (dapm->codec->name_prefix) + if (dapm->codec && dapm->codec->name_prefix) snprintf(w->name, name_len, "%s %s", dapm->codec->name_prefix, widget->name); else @@ -2242,7 +2314,6 @@ int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, mutex_unlock(&codec->mutex); return 0; } -EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); /** * snd_soc_dapm_enable_pin - enable pin. @@ -2419,9 +2490,9 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm) * standby. */ if (powerdown) { - snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_PREPARE); + snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_PREPARE); dapm_seq_run(dapm, &down_list, 0, false); - snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_STANDBY); + snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY); } } @@ -2434,7 +2505,7 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card) list_for_each_entry(codec, &card->codec_dev_list, list) { soc_dapm_shutdown_codec(&codec->dapm); - snd_soc_dapm_set_bias_level(card, &codec->dapm, SND_SOC_BIAS_OFF); + snd_soc_dapm_set_bias_level(&codec->dapm, SND_SOC_BIAS_OFF); } } diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index ac5a5bc7375..fcab80b36a3 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -37,6 +37,7 @@ int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, { jack->codec = codec; INIT_LIST_HEAD(&jack->pins); + INIT_LIST_HEAD(&jack->jack_zones); BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); return snd_jack_new(codec->card->snd_card, id, type, &jack->jack); @@ -100,7 +101,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) } /* Report before the DAPM sync to help users updating micbias status */ - blocking_notifier_call_chain(&jack->notifier, status, NULL); + blocking_notifier_call_chain(&jack->notifier, status, jack); snd_soc_dapm_sync(dapm); @@ -112,6 +113,51 @@ out: EXPORT_SYMBOL_GPL(snd_soc_jack_report); /** + * snd_soc_jack_add_zones - Associate voltage zones with jack + * + * @jack: ASoC jack + * @count: Number of zones + * @zone: Array of zones + * + * After this function has been called the zones specified in the + * array will be associated with the jack. + */ +int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count, + struct snd_soc_jack_zone *zones) +{ + int i; + + for (i = 0; i < count; i++) { + INIT_LIST_HEAD(&zones[i].list); + list_add(&(zones[i].list), &jack->jack_zones); + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_jack_add_zones); + +/** + * snd_soc_jack_get_type - Based on the mic bias value, this function returns + * the type of jack from the zones delcared in the jack type + * + * @micbias_voltage: mic bias voltage at adc channel when jack is plugged in + * + * Based on the mic bias value passed, this function helps identify + * the type of jack from the already delcared jack zones + */ +int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage) +{ + struct snd_soc_jack_zone *zone; + + list_for_each_entry(zone, &jack->jack_zones, list) { + if (micbias_voltage >= zone->min_mv && + micbias_voltage < zone->max_mv) + return zone->jack_type; + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_jack_get_type); + +/** * snd_soc_jack_add_pins - Associate DAPM pins with an ASoC jack * * @jack: ASoC jack @@ -194,7 +240,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) int enable; int report; - enable = gpio_get_value(gpio->gpio); + enable = gpio_get_value_cansleep(gpio->gpio); if (gpio->invert) enable = !enable; @@ -284,6 +330,14 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, if (ret) goto err; + if (gpios[i].wake) { + ret = set_irq_wake(gpio_to_irq(gpios[i].gpio), 1); + if (ret != 0) + printk(KERN_ERR + "Failed to mark GPIO %d as wake source: %d\n", + gpios[i].gpio, ret); + } + #ifdef CONFIG_GPIO_SYSFS /* Expose GPIO value over sysfs for diagnostic purposes */ gpio_export(gpios[i].gpio, false); diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index dfd2ab9d975..fd183d3ab4f 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -2,13 +2,14 @@ snd-soc-tegra-das-objs := tegra_das.o snd-soc-tegra-pcm-objs := tegra_pcm.o snd-soc-tegra-i2s-objs := tegra_i2s.o +snd-soc-tegra-utils-objs += tegra_asoc_utils.o +obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-utils.o obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-das.o obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-pcm.o obj-$(CONFIG_SND_TEGRA_SOC_I2S) += snd-soc-tegra-i2s.o # Tegra machine Support snd-soc-tegra-harmony-objs := harmony.o -snd-soc-tegra-harmony-objs += tegra_asoc_utils.o obj-$(CONFIG_SND_TEGRA_SOC_HARMONY) += snd-soc-tegra-harmony.o diff --git a/sound/soc/tegra/harmony.c b/sound/soc/tegra/harmony.c index 11e2cb82566..8585957477e 100644 --- a/sound/soc/tegra/harmony.c +++ b/sound/soc/tegra/harmony.c @@ -38,10 +38,13 @@ #include <mach/harmony_audio.h> #include <sound/core.h> +#include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> +#include "../codecs/wm8903.h" + #include "tegra_das.h" #include "tegra_i2s.h" #include "tegra_pcm.h" @@ -49,10 +52,14 @@ #define DRV_NAME "tegra-snd-harmony" +#define GPIO_SPKR_EN BIT(0) +#define GPIO_INT_MIC_EN BIT(1) +#define GPIO_EXT_MIC_EN BIT(2) + struct tegra_harmony { struct tegra_asoc_utils_data util_data; struct harmony_audio_platform_data *pdata; - int gpio_spkr_en_requested; + int gpio_requested; }; static int harmony_asoc_hw_params(struct snd_pcm_substream *substream, @@ -123,6 +130,33 @@ static struct snd_soc_ops harmony_asoc_ops = { .hw_params = harmony_asoc_hw_params, }; +static struct snd_soc_jack harmony_hp_jack; + +static struct snd_soc_jack_pin harmony_hp_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, +}; + +static struct snd_soc_jack_gpio harmony_hp_jack_gpios[] = { + { + .name = "headphone detect", + .report = SND_JACK_HEADPHONE, + .debounce_time = 150, + .invert = 1, + } +}; + +static struct snd_soc_jack harmony_mic_jack; + +static struct snd_soc_jack_pin harmony_mic_jack_pins[] = { + { + .pin = "Mic Jack", + .mask = SND_JACK_MICROPHONE, + }, +}; + static int harmony_event_int_spk(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { @@ -154,6 +188,10 @@ static const struct snd_soc_dapm_route harmony_audio_map[] = { {"IN1L", NULL, "Mic Bias"}, }; +static const struct snd_kcontrol_new harmony_controls[] = { + SOC_DAPM_PIN_SWITCH("Int Spk"), +}; + static int harmony_asoc_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; @@ -168,19 +206,65 @@ static int harmony_asoc_init(struct snd_soc_pcm_runtime *rtd) dev_err(card->dev, "cannot get spkr_en gpio\n"); return ret; } - harmony->gpio_spkr_en_requested = 1; + harmony->gpio_requested |= GPIO_SPKR_EN; gpio_direction_output(pdata->gpio_spkr_en, 0); + ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en"); + if (ret) { + dev_err(card->dev, "cannot get int_mic_en gpio\n"); + return ret; + } + harmony->gpio_requested |= GPIO_INT_MIC_EN; + + /* Disable int mic; enable signal is active-high */ + gpio_direction_output(pdata->gpio_int_mic_en, 0); + + ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en"); + if (ret) { + dev_err(card->dev, "cannot get ext_mic_en gpio\n"); + return ret; + } + harmony->gpio_requested |= GPIO_EXT_MIC_EN; + + /* Enable ext mic; enable signal is active-low */ + gpio_direction_output(pdata->gpio_ext_mic_en, 0); + + ret = snd_soc_add_controls(codec, harmony_controls, + ARRAY_SIZE(harmony_controls)); + if (ret < 0) + return ret; + snd_soc_dapm_new_controls(dapm, harmony_dapm_widgets, ARRAY_SIZE(harmony_dapm_widgets)); snd_soc_dapm_add_routes(dapm, harmony_audio_map, ARRAY_SIZE(harmony_audio_map)); - snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); - snd_soc_dapm_enable_pin(dapm, "Int Spk"); - snd_soc_dapm_enable_pin(dapm, "Mic Jack"); + harmony_hp_jack_gpios[0].gpio = pdata->gpio_hp_det; + snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, + &harmony_hp_jack); + snd_soc_jack_add_pins(&harmony_hp_jack, + ARRAY_SIZE(harmony_hp_jack_pins), + harmony_hp_jack_pins); + snd_soc_jack_add_gpios(&harmony_hp_jack, + ARRAY_SIZE(harmony_hp_jack_gpios), + harmony_hp_jack_gpios); + + snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE, + &harmony_mic_jack); + snd_soc_jack_add_pins(&harmony_mic_jack, + ARRAY_SIZE(harmony_mic_jack_pins), + harmony_mic_jack_pins); + wm8903_mic_detect(codec, &harmony_mic_jack, SND_JACK_MICROPHONE, 0); + + snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + + snd_soc_dapm_nc_pin(dapm, "IN3L"); + snd_soc_dapm_nc_pin(dapm, "IN3R"); + snd_soc_dapm_nc_pin(dapm, "LINEOUTL"); + snd_soc_dapm_nc_pin(dapm, "LINEOUTR"); + snd_soc_dapm_sync(dapm); return 0; @@ -189,7 +273,7 @@ static int harmony_asoc_init(struct snd_soc_pcm_runtime *rtd) static struct snd_soc_dai_link harmony_wm8903_dai = { .name = "WM8903", .stream_name = "WM8903 PCM", - .codec_name = "wm8903-codec.0-001a", + .codec_name = "wm8903.0-001a", .platform_name = "tegra-pcm-audio", .cpu_dai_name = "tegra-i2s.0", .codec_dai_name = "wm8903-hifi", @@ -270,7 +354,11 @@ static int __devexit tegra_snd_harmony_remove(struct platform_device *pdev) tegra_asoc_utils_fini(&harmony->util_data); - if (harmony->gpio_spkr_en_requested) + if (harmony->gpio_requested & GPIO_EXT_MIC_EN) + gpio_free(pdata->gpio_ext_mic_en); + if (harmony->gpio_requested & GPIO_INT_MIC_EN) + gpio_free(pdata->gpio_int_mic_en); + if (harmony->gpio_requested & GPIO_SPKR_EN) gpio_free(pdata->gpio_spkr_en); kfree(harmony); @@ -302,3 +390,4 @@ module_exit(snd_tegra_harmony_exit); MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); MODULE_DESCRIPTION("Harmony machine ASoC driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c index cb4fc13c7d2..52f0a3f9ce4 100644 --- a/sound/soc/tegra/tegra_asoc_utils.c +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -101,6 +101,7 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, return 0; } +EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate); int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, struct device *dev) @@ -139,6 +140,7 @@ err_put_pll_a: err: return ret; } +EXPORT_SYMBOL_GPL(tegra_asoc_utils_init); void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data) { @@ -146,4 +148,8 @@ void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data) clk_put(data->clk_pll_a_out0); clk_put(data->clk_pll_a); } +EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini); +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); +MODULE_DESCRIPTION("Tegra ASoC utility code"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/tegra/tegra_das.c b/sound/soc/tegra/tegra_das.c index 01eb9c9301d..9f24ef73f2c 100644 --- a/sound/soc/tegra/tegra_das.c +++ b/sound/soc/tegra/tegra_das.c @@ -262,3 +262,4 @@ module_exit(tegra_das_modexit); MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); MODULE_DESCRIPTION("Tegra DAS driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c index 870ee361f75..4f5e2c90b02 100644 --- a/sound/soc/tegra/tegra_i2s.c +++ b/sound/soc/tegra/tegra_i2s.c @@ -500,3 +500,4 @@ module_exit(snd_tegra_i2s_exit); MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); MODULE_DESCRIPTION("Tegra I2S ASoC driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c index 663ea9fa0ca..3c271f95358 100644 --- a/sound/soc/tegra/tegra_pcm.c +++ b/sound/soc/tegra/tegra_pcm.c @@ -39,6 +39,8 @@ #include "tegra_pcm.h" +#define DRV_NAME "tegra-pcm-audio" + static const struct snd_pcm_hardware tegra_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | @@ -159,8 +161,8 @@ static int tegra_pcm_open(struct snd_pcm_substream *substream) prtd->dma_req[1].dev = prtd; prtd->dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); - if (IS_ERR(prtd->dma_chan)) { - ret = PTR_ERR(prtd->dma_chan); + if (prtd->dma_chan == NULL) { + ret = -ENOMEM; goto err; } @@ -377,7 +379,7 @@ static int __devexit tegra_pcm_platform_remove(struct platform_device *pdev) static struct platform_driver tegra_pcm_driver = { .driver = { - .name = "tegra-pcm-audio", + .name = DRV_NAME, .owner = THIS_MODULE, }, .probe = tegra_pcm_platform_probe, @@ -399,3 +401,4 @@ module_exit(snd_tegra_pcm_exit); MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); MODULE_DESCRIPTION("Tegra PCM ASoC driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); |