diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-21 11:34:25 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-21 11:34:25 -0800 |
commit | 460dc1eecf37263c8e3b17685ef236f0d236facb (patch) | |
tree | 1d20e367cefccddb969b48afaab140b8125cea4e /sound/soc/codecs | |
parent | 024e4ec1856d57bb78c06ec903d29dcf716f5f47 (diff) | |
parent | b531f81b0d70ffbe8d70500512483227cc532608 (diff) |
Merge tag 'sound-3.9' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"The biggest change in this update is the unification of HD-audio codec
parsers. Now the HD-audio codec is parsed in a generic parser code
which is invoked by each HD-audio codec driver.
Some background information is found in David Henningsson's blog
entry:
http://voices.canonical.com/david.henningsson/2013/01/18/upcoming-changes-to-the-intel-hda-drivers/
Other than that, some random updates/fixes like USB-audio and a bunch
of small AoC updates as usual.
Highlights:
- Unification of HD-audio parser code (aka generic parser)
- Support of new Intel HD-audio controller, new IDT codecs
- Fixes for HD-audio HDMI audio hotplug
- Haswell HDMI audio fixup
- Support of Creative CA0132 DSP code
- A few fixes of HDSP driver
- USB-audio fix for Roland A-PRO, M-Audio FT C600
- Support PM for aloop driver (and fixes Oops)
- Compress API updates for gapless playback support
For ASoC part:
- Support for a wider range of hardware in the compressed stream code
- The ability to mute capture streams as well as playback streams
while inactive
- DT support for AK4642, FSI, Samsung I2S and WM8962
- AC'97 support for Tegra
- New driver for max98090, replacing the stub which was there
- A new driver from Dialog
Note that due to dependencies, DTification of DMA support for Samsung
platforms (used only by the and I2S driver and SPI) is merged here as
well."
Fix up trivial conflict in drivers/spi/spi-s3c64xx.c due to removed code
being changed.
* tag 'sound-3.9' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (453 commits)
ALSA: usb: Fix Processing Unit Descriptor parsers
ALSA: hda - hdmi: Notify userspace when ELD control changes
ALSA: hda - hdmi: Protect ELD buffer
ALSA: hda - hdmi: Refactor hdmi_eld into parsed_hdmi_eld
ALSA: hda - hdmi: Do not expose eld data when eld is invalid
ALSA: hda - hdmi: ELD shouldn't be valid after unplug
ALSA: hda - Fix the silent speaker output on Fujitsu S7020 laptop
ALSA: hda - add quirks for mute LED on two HP machines
ALSA: usb/quirks, fix out-of-bounds access
ASoC: codecs: Add da7213 codec
ALSA: au88x0 - Define channel map for au88x0
ALSA: compress: add support for gapless playback
ALSA: hda - Remove speaker clicks on CX20549
ALSA: hda - Disable runtime PM for Intel 5 Series/3400
ALSA: hda - Increase badness for missing multi-io
ASoC: arizona: Automatically manage input mutes
ALSA: hda - Fix broken workaround for HDMI/SPDIF conflicts
ALSA: hda/ca0132 - Add missing \n to debug prints
ALSA: hda/ca0132 - Fix type of INVALID_CHIP_ADDRESS
ALSA: hda - update documentation for no-primary-hp fixup
...
Diffstat (limited to 'sound/soc/codecs')
31 files changed, 7273 insertions, 787 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 3a847828932..133025fbb6d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -36,6 +36,7 @@ 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_DA7213 if I2C select SND_SOC_DA732X if I2C select SND_SOC_DA9055 if I2C select SND_SOC_DFBMCS320 @@ -98,7 +99,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8782 select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8900 if I2C - select SND_SOC_WM8903 if I2C + select SND_SOC_WM8903 if I2C && GENERIC_HARDIRQS select SND_SOC_WM8904 if I2C select SND_SOC_WM8940 if I2C select SND_SOC_WM8955 if I2C @@ -247,6 +248,9 @@ config SND_SOC_L3 config SND_SOC_DA7210 tristate +config SND_SOC_DA7213 + tristate + config SND_SOC_DA732X tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f6e8e36cceb..6a3b3c3b8b4 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -23,6 +23,7 @@ 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-da7213-objs := da7213.o snd-soc-da732x-objs := da732x.o snd-soc-da9055-objs := da9055.o snd-soc-dfbmcs320-objs := dfbmcs320.o @@ -147,6 +148,7 @@ 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_DA7213) += snd-soc-da7213.o obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o obj-$(CONFIG_SND_SOC_DFBMCS320) += snd-soc-dfbmcs320.o diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 1f0cdab0329..2d037870970 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -26,6 +26,7 @@ #include <linux/delay.h> #include <linux/i2c.h> #include <linux/slab.h> +#include <linux/of_device.h> #include <linux/module.h> #include <sound/soc.h> #include <sound/initval.h> @@ -513,12 +514,31 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4648 = { }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static struct of_device_id ak4642_of_match[]; static int ak4642_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct device_node *np = i2c->dev.of_node; + const struct snd_soc_codec_driver *driver; + + driver = NULL; + if (np) { + const struct of_device_id *of_id; + + of_id = of_match_device(ak4642_of_match, &i2c->dev); + if (of_id) + driver = of_id->data; + } else { + driver = (struct snd_soc_codec_driver *)id->driver_data; + } + + if (!driver) { + dev_err(&i2c->dev, "no driver\n"); + return -EINVAL; + } + return snd_soc_register_codec(&i2c->dev, - (struct snd_soc_codec_driver *)id->driver_data, - &ak4642_dai, 1); + driver, &ak4642_dai, 1); } static int ak4642_i2c_remove(struct i2c_client *client) @@ -527,6 +547,14 @@ static int ak4642_i2c_remove(struct i2c_client *client) return 0; } +static struct of_device_id ak4642_of_match[] = { + { .compatible = "asahi-kasei,ak4642", .data = &soc_codec_dev_ak4642}, + { .compatible = "asahi-kasei,ak4643", .data = &soc_codec_dev_ak4642}, + { .compatible = "asahi-kasei,ak4648", .data = &soc_codec_dev_ak4648}, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4642_of_match); + static const struct i2c_device_id ak4642_i2c_id[] = { { "ak4642", (kernel_ulong_t)&soc_codec_dev_ak4642 }, { "ak4643", (kernel_ulong_t)&soc_codec_dev_ak4642 }, @@ -539,6 +567,7 @@ static struct i2c_driver ak4642_i2c_driver = { .driver = { .name = "ak4642-codec", .owner = THIS_MODULE, + .of_match_table = ak4642_of_match, }, .probe = ak4642_i2c_probe, .remove = ak4642_i2c_remove, diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index ef62c435848..ac948a671ea 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -56,14 +56,14 @@ #define arizona_fll_warn(_fll, fmt, ...) \ dev_warn(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) #define arizona_fll_dbg(_fll, fmt, ...) \ - dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) + dev_dbg(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) #define arizona_aif_err(_dai, fmt, ...) \ dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) #define arizona_aif_warn(_dai, fmt, ...) \ dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) #define arizona_aif_dbg(_dai, fmt, ...) \ - dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) + dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "None", @@ -141,6 +141,30 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "ASRC1R", "ASRC2L", "ASRC2R", + "ISRC1INT1", + "ISRC1INT2", + "ISRC1INT3", + "ISRC1INT4", + "ISRC1DEC1", + "ISRC1DEC2", + "ISRC1DEC3", + "ISRC1DEC4", + "ISRC2INT1", + "ISRC2INT2", + "ISRC2INT3", + "ISRC2INT4", + "ISRC2DEC1", + "ISRC2DEC2", + "ISRC2DEC3", + "ISRC2DEC4", + "ISRC3INT1", + "ISRC3INT2", + "ISRC3INT3", + "ISRC3INT4", + "ISRC3DEC1", + "ISRC3DEC2", + "ISRC3DEC3", + "ISRC3DEC4", }; EXPORT_SYMBOL_GPL(arizona_mixer_texts); @@ -220,6 +244,30 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { 0x91, 0x92, 0x93, + 0xa0, /* ISRC1INT1 */ + 0xa1, + 0xa2, + 0xa3, + 0xa4, /* ISRC1DEC1 */ + 0xa5, + 0xa6, + 0xa7, + 0xa8, /* ISRC2DEC1 */ + 0xa9, + 0xaa, + 0xab, + 0xac, /* ISRC2INT1 */ + 0xad, + 0xae, + 0xaf, + 0xb0, /* ISRC3DEC1 */ + 0xb1, + 0xb2, + 0xb3, + 0xb4, /* ISRC3INT1 */ + 0xb5, + 0xb6, + 0xb7, }; EXPORT_SYMBOL_GPL(arizona_mixer_values); @@ -275,9 +323,35 @@ const struct soc_enum arizona_lhpf4_mode = arizona_lhpf_mode_text); EXPORT_SYMBOL_GPL(arizona_lhpf4_mode); +static const char *arizona_ng_hold_text[] = { + "30ms", "120ms", "250ms", "500ms", +}; + +const struct soc_enum arizona_ng_hold = + SOC_ENUM_SINGLE(ARIZONA_NOISE_GATE_CONTROL, ARIZONA_NGATE_HOLD_SHIFT, + 4, arizona_ng_hold_text); +EXPORT_SYMBOL_GPL(arizona_ng_hold); + int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { + unsigned int reg; + + if (w->shift % 2) + reg = ARIZONA_ADC_DIGITAL_VOLUME_1L + ((w->shift / 2) * 8); + else + reg = ARIZONA_ADC_DIGITAL_VOLUME_1R + ((w->shift / 2) * 8); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(w->codec, reg, ARIZONA_IN1L_MUTE, 0); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(w->codec, reg, ARIZONA_IN1L_MUTE, + ARIZONA_IN1L_MUTE); + break; + } + return 0; } EXPORT_SYMBOL_GPL(arizona_in_ev); @@ -417,6 +491,10 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, case 147456000: val |= 6 << ARIZONA_SYSCLK_FREQ_SHIFT; break; + case 0: + dev_dbg(arizona->dev, "%s cleared\n", name); + *clk = freq; + return 0; default: return -EINVAL; } @@ -635,6 +713,9 @@ static int arizona_startup(struct snd_pcm_substream *substream, return 0; } + if (base_rate == 0) + return 0; + if (base_rate % 8000) constraint = &arizona_44k1_constraint; else @@ -645,25 +726,81 @@ static int arizona_startup(struct snd_pcm_substream *substream, constraint); } +static int arizona_hw_params_rate(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + int base = dai->driver->base; + int i, sr_val; + + /* + * We will need to be more flexible than this in future, + * currently we use a single sample rate for SYSCLK. + */ + for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) + if (arizona_sr_vals[i] == params_rate(params)) + break; + if (i == ARRAY_SIZE(arizona_sr_vals)) { + arizona_aif_err(dai, "Unsupported sample rate %dHz\n", + params_rate(params)); + return -EINVAL; + } + sr_val = i; + + switch (dai_priv->clk) { + case ARIZONA_CLK_SYSCLK: + snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, + ARIZONA_SAMPLE_RATE_1_MASK, sr_val); + if (base) + snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_RATE_MASK, 0); + break; + case ARIZONA_CLK_ASYNCCLK: + snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1, + ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val); + if (base) + snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_RATE_MASK, + 8 << ARIZONA_AIF1_RATE_SHIFT); + break; + default: + arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); + return -EINVAL; + } + + return 0; +} + static int arizona_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 arizona_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + struct arizona *arizona = priv->arizona; int base = dai->driver->base; const int *rates; - int i; - int bclk, lrclk, wl, frame, sr_val; + int i, ret; + int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1]; + int bclk, lrclk, wl, frame, bclk_target; if (params_rate(params) % 8000) rates = &arizona_44k1_bclk_rates[0]; else rates = &arizona_48k_bclk_rates[0]; + bclk_target = snd_soc_params_to_bclk(params); + if (chan_limit && chan_limit < params_channels(params)) { + arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit); + bclk_target /= params_channels(params); + bclk_target *= chan_limit; + } + for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) { - if (rates[i] >= snd_soc_params_to_bclk(params) && + if (rates[i] >= bclk_target && rates[i] % params_rate(params) == 0) { bclk = i; break; @@ -675,16 +812,6 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) - if (arizona_sr_vals[i] == params_rate(params)) - break; - if (i == ARRAY_SIZE(arizona_sr_vals)) { - arizona_aif_err(dai, "Unsupported sample rate %dHz\n", - params_rate(params)); - return -EINVAL; - } - sr_val = i; - lrclk = rates[bclk] / params_rate(params); arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", @@ -693,28 +820,9 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, wl = snd_pcm_format_width(params_format(params)); frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl; - /* - * We will need to be more flexible than this in future, - * currently we use a single sample rate for SYSCLK. - */ - switch (dai_priv->clk) { - case ARIZONA_CLK_SYSCLK: - snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, - ARIZONA_SAMPLE_RATE_1_MASK, sr_val); - snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, - ARIZONA_AIF1_RATE_MASK, 0); - break; - case ARIZONA_CLK_ASYNCCLK: - snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1, - ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val); - snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, - ARIZONA_AIF1_RATE_MASK, - 8 << ARIZONA_AIF1_RATE_SHIFT); - break; - default: - arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); - return -EINVAL; - } + ret = arizona_hw_params_rate(substream, params, dai); + if (ret != 0) + return ret; snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); @@ -789,11 +897,27 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai, return snd_soc_dapm_sync(&codec->dapm); } +static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_codec *codec = dai->codec; + int base = dai->driver->base; + unsigned int reg; + + if (tristate) + reg = ARIZONA_AIF1_TRI; + else + reg = 0; + + return snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_TRI, reg); +} + const struct snd_soc_dai_ops arizona_dai_ops = { .startup = arizona_startup, .set_fmt = arizona_set_fmt, .hw_params = arizona_hw_params, .set_sysclk = arizona_dai_set_sysclk, + .set_tristate = arizona_set_tristate, }; EXPORT_SYMBOL_GPL(arizona_dai_ops); @@ -807,17 +931,6 @@ int arizona_init_dai(struct arizona_priv *priv, int id) } EXPORT_SYMBOL_GPL(arizona_init_dai); -static irqreturn_t arizona_fll_lock(int irq, void *data) -{ - struct arizona_fll *fll = data; - - arizona_fll_dbg(fll, "Lock status changed\n"); - - complete(&fll->lock); - - return IRQ_HANDLED; -} - static irqreturn_t arizona_fll_clock_ok(int irq, void *data) { struct arizona_fll *fll = data; @@ -910,7 +1023,7 @@ static int arizona_calc_fll(struct arizona_fll *fll, cfg->n = target / (ratio * Fref); - if (target % Fref) { + if (target % (ratio * Fref)) { gcd_fll = gcd(target, ratio * Fref); arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll); @@ -922,6 +1035,15 @@ static int arizona_calc_fll(struct arizona_fll *fll, cfg->lambda = 0; } + /* Round down to 16bit range with cost of accuracy lost. + * Denominator must be bigger than numerator so we only + * take care of it. + */ + while (cfg->lambda >= (1 << 16)) { + cfg->theta >>= 1; + cfg->lambda >>= 1; + } + arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", cfg->n, cfg->theta, cfg->lambda); arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", @@ -1057,7 +1179,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, { int ret; - init_completion(&fll->lock); init_completion(&fll->ok); fll->id = id; @@ -1068,13 +1189,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name), "FLL%d clock OK", id); - ret = arizona_request_irq(arizona, lock_irq, fll->lock_name, - arizona_fll_lock, fll); - if (ret != 0) { - dev_err(arizona->dev, "Failed to get FLL%d lock IRQ: %d\n", - id, ret); - } - ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name, arizona_fll_clock_ok, fll); if (ret != 0) { @@ -1089,6 +1203,40 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, } EXPORT_SYMBOL_GPL(arizona_init_fll); +/** + * arizona_set_output_mode - Set the mode of the specified output + * + * @codec: Device to configure + * @output: Output number + * @diff: True to set the output to differential mode + * + * Some systems use external analogue switches to connect more + * analogue devices to the CODEC than are supported by the device. In + * some systems this requires changing the switched output from single + * ended to differential mode dynamically at runtime, an operation + * supported using this function. + * + * Most systems have a single static configuration and should use + * platform data instead. + */ +int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff) +{ + unsigned int reg, val; + + if (output < 1 || output > 6) + return -EINVAL; + + reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8; + + if (diff) + val = ARIZONA_OUT1_MONO; + else + val = 0; + + return snd_soc_update_bits(codec, reg, ARIZONA_OUT1_MONO, val); +} +EXPORT_SYMBOL_GPL(arizona_set_output_mode); + MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 4deebeb0717..116372c91f5 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -66,7 +66,7 @@ struct arizona_priv { struct arizona_dai_priv dai[ARIZONA_MAX_DAI]; }; -#define ARIZONA_NUM_MIXER_INPUTS 75 +#define ARIZONA_NUM_MIXER_INPUTS 99 extern const unsigned int arizona_mixer_tlv[]; extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; @@ -176,6 +176,8 @@ extern const struct soc_enum arizona_lhpf2_mode; extern const struct soc_enum arizona_lhpf3_mode; extern const struct soc_enum arizona_lhpf4_mode; +extern const struct soc_enum arizona_ng_hold; + extern int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); @@ -195,7 +197,6 @@ struct arizona_fll { int id; unsigned int base; unsigned int vco_mult; - struct completion lock; struct completion ok; unsigned int fref; unsigned int fout; @@ -211,4 +212,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source, extern int arizona_init_dai(struct arizona_priv *priv, int dai); +int arizona_set_output_mode(struct snd_soc_codec *codec, int output, + bool diff); + #endif diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index ac8742a1f25..2415a4118db 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -167,6 +167,8 @@ struct cs4271_private { int gpio_nreset; /* GPIO that disable serial bus, if any */ int gpio_disable; + /* enable soft reset workaround */ + bool enable_soft_reset; }; /* @@ -325,6 +327,33 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream, int i, ret; unsigned int ratio, val; + if (cs4271->enable_soft_reset) { + /* + * Put the codec in soft reset and back again in case it's not + * currently streaming data. This way of bringing the codec in + * sync to the current clocks is not explicitly documented in + * the data sheet, but it seems to work fine, and in contrast + * to a read hardware reset, we don't have to sync back all + * registers every time. + */ + + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + !dai->capture_active) || + (substream->stream == SNDRV_PCM_STREAM_CAPTURE && + !dai->playback_active)) { + ret = snd_soc_update_bits(codec, CS4271_MODE2, + CS4271_MODE2_PDN, + CS4271_MODE2_PDN); + if (ret < 0) + return ret; + + ret = snd_soc_update_bits(codec, CS4271_MODE2, + CS4271_MODE2_PDN, 0); + if (ret < 0) + return ret; + } + } + cs4271->rate = params_rate(params); /* Configure DAC */ @@ -484,6 +513,10 @@ static int cs4271_probe(struct snd_soc_codec *codec) if (of_get_property(codec->dev->of_node, "cirrus,amutec-eq-bmutec", NULL)) amutec_eq_bmutec = true; + + if (of_get_property(codec->dev->of_node, + "cirrus,enable-soft-reset", NULL)) + cs4271->enable_soft_reset = true; } #endif @@ -492,6 +525,7 @@ static int cs4271_probe(struct snd_soc_codec *codec) gpio_nreset = cs4271plat->gpio_nreset; amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec; + cs4271->enable_soft_reset = cs4271plat->enable_soft_reset; } if (gpio_nreset >= 0) diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 9811a5478c8..0f6f481cec0 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1038,7 +1038,7 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec) struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); int ret; - cs42l52->beep = input_allocate_device(); + cs42l52->beep = devm_input_allocate_device(codec->dev); if (!cs42l52->beep) { dev_err(codec->dev, "Failed to allocate beep device\n"); return; @@ -1059,7 +1059,6 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec) ret = input_register_device(cs42l52->beep); if (ret != 0) { - input_free_device(cs42l52->beep); cs42l52->beep = NULL; dev_err(codec->dev, "Failed to register beep device\n"); } @@ -1076,7 +1075,6 @@ static void cs42l52_free_beep(struct snd_soc_codec *codec) struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); device_remove_file(codec->dev, &dev_attr_beep); - input_unregister_device(cs42l52->beep); cancel_work_sync(&cs42l52->beep_work); cs42l52->beep = NULL; diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c new file mode 100644 index 00000000000..41230ad1c3e --- /dev/null +++ b/sound/soc/codecs/da7213.c @@ -0,0 +1,1599 @@ +/* + * DA7213 ALSA SoC Codec Driver + * + * Copyright (c) 2013 Dialog Semiconductor + * + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> + * Based on DA9055 ALSA SoC codec driver. + * + * 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/delay.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include <sound/da7213.h> +#include "da7213.h" + + +/* Gain and Volume */ +static const unsigned int aux_vol_tlv[] = { + TLV_DB_RANGE_HEAD(2), + /* -54dB */ + 0x0, 0x11, TLV_DB_SCALE_ITEM(-5400, 0, 0), + /* -52.5dB to 15dB */ + 0x12, 0x3f, TLV_DB_SCALE_ITEM(-5250, 150, 0) +}; + +static const unsigned int digital_gain_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* -78dB to 12dB */ + 0x08, 0x7f, TLV_DB_SCALE_ITEM(-7800, 75, 0) +}; + +static const unsigned int alc_analog_gain_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0x0, 0x0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1), + /* 0dB to 36dB */ + 0x01, 0x07, TLV_DB_SCALE_ITEM(0, 600, 0) +}; + +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -600, 600, 0); +static const DECLARE_TLV_DB_SCALE(mixin_gain_tlv, -450, 150, 0); +static const DECLARE_TLV_DB_SCALE(eq_gain_tlv, -1050, 150, 0); +static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_SCALE(lineout_vol_tlv, -4800, 100, 0); +static const DECLARE_TLV_DB_SCALE(alc_threshold_tlv, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(alc_gain_tlv, 0, 600, 0); + +/* ADC and DAC voice mode (8kHz) high pass cutoff value */ +static const char * const da7213_voice_hpf_corner_txt[] = { + "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz" +}; + +static const struct soc_enum da7213_dac_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7213_DAC_FILTERS1, DA7213_VOICE_HPF_CORNER_SHIFT, + DA7213_VOICE_HPF_CORNER_MAX, + da7213_voice_hpf_corner_txt); + +static const struct soc_enum da7213_adc_voice_hpf_corner = + SOC_ENUM_SINGLE(DA7213_ADC_FILTERS1, DA7213_VOICE_HPF_CORNER_SHIFT, + DA7213_VOICE_HPF_CORNER_MAX, + da7213_voice_hpf_corner_txt); + +/* ADC and DAC high pass filter cutoff value */ +static const char * const da7213_audio_hpf_corner_txt[] = { + "Fs/24000", "Fs/12000", "Fs/6000", "Fs/3000" +}; + +static const struct soc_enum da7213_dac_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7213_DAC_FILTERS1, DA7213_AUDIO_HPF_CORNER_SHIFT, + DA7213_AUDIO_HPF_CORNER_MAX, + da7213_audio_hpf_corner_txt); + +static const struct soc_enum da7213_adc_audio_hpf_corner = + SOC_ENUM_SINGLE(DA7213_ADC_FILTERS1, DA7213_AUDIO_HPF_CORNER_SHIFT, + DA7213_AUDIO_HPF_CORNER_MAX, + da7213_audio_hpf_corner_txt); + +/* Gain ramping rate value */ +static const char * const da7213_gain_ramp_rate_txt[] = { + "nominal rate * 8", "nominal rate * 16", "nominal rate / 16", + "nominal rate / 32" +}; + +static const struct soc_enum da7213_gain_ramp_rate = + SOC_ENUM_SINGLE(DA7213_GAIN_RAMP_CTRL, DA7213_GAIN_RAMP_RATE_SHIFT, + DA7213_GAIN_RAMP_RATE_MAX, da7213_gain_ramp_rate_txt); + +/* DAC noise gate setup time value */ +static const char * const da7213_dac_ng_setup_time_txt[] = { + "256 samples", "512 samples", "1024 samples", "2048 samples" +}; + +static const struct soc_enum da7213_dac_ng_setup_time = + SOC_ENUM_SINGLE(DA7213_DAC_NG_SETUP_TIME, + DA7213_DAC_NG_SETUP_TIME_SHIFT, + DA7213_DAC_NG_SETUP_TIME_MAX, + da7213_dac_ng_setup_time_txt); + +/* DAC noise gate rampup rate value */ +static const char * const |