From a37f1b8fdc912600c24f9d0d45d7046e50a031e4 Mon Sep 17 00:00:00 2001 From: Anatol Pomozov Date: Tue, 30 Dec 2014 11:12:35 -0800 Subject: ASoC: tegra: Add platform driver for rt5677 audio codec The driver supports NVIDIA Tegra Ryu board Sponsored: Google ChromeOS Signed-off-by: Anatol Pomozov Signed-off-by: Mark Brown --- sound/soc/tegra/Kconfig | 10 ++ sound/soc/tegra/Makefile | 2 + sound/soc/tegra/tegra_rt5677.c | 347 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 359 insertions(+) create mode 100644 sound/soc/tegra/tegra_rt5677.c (limited to 'sound/soc') diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 31198cf7f88d..a6768f832c6f 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -128,3 +128,13 @@ config SND_SOC_TEGRA_MAX98090 help Say Y or M here if you want to add support for SoC audio on Tegra boards using the MAX98090 codec, such as Venice2. + +config SND_SOC_TEGRA_RT5677 + tristate "SoC Audio support for Tegra boards using a RT5677 codec" + depends on SND_SOC_TEGRA && I2C && GPIOLIB + select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC + select SND_SOC_RT5677 + help + Say Y or M here if you want to add support for SoC audio on Tegra + boards using the RT5677 codec, such as Ryu. diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 5ae588cd96c4..9171655ad843 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o # Tegra machine Support snd-soc-tegra-rt5640-objs := tegra_rt5640.o +snd-soc-tegra-rt5677-objs := tegra_rt5677.o snd-soc-tegra-wm8753-objs := tegra_wm8753.o snd-soc-tegra-wm8903-objs := tegra_wm8903.o snd-soc-tegra-wm9712-objs := tegra_wm9712.o @@ -27,6 +28,7 @@ snd-soc-tegra-alc5632-objs := tegra_alc5632.o snd-soc-tegra-max98090-objs := tegra_max98090.o obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o +obj-$(CONFIG_SND_SOC_TEGRA_RT5677) += snd-soc-tegra-rt5677.o obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o diff --git a/sound/soc/tegra/tegra_rt5677.c b/sound/soc/tegra/tegra_rt5677.c new file mode 100644 index 000000000000..e4cf978a6e3a --- /dev/null +++ b/sound/soc/tegra/tegra_rt5677.c @@ -0,0 +1,347 @@ +/* +* tegra_rt5677.c - Tegra machine ASoC driver for boards using RT5677 codec. + * + * Copyright (c) 2014, The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Based on code copyright/by: + * + * Copyright (C) 2010-2012 - NVIDIA, Inc. + * Copyright (C) 2011 The AC100 Kernel Team + * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd. + * Copyright 2007 Wolfson Microelectronics PLC. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../codecs/rt5677.h" + +#include "tegra_asoc_utils.h" + +#define DRV_NAME "tegra-snd-rt5677" + +struct tegra_rt5677 { + struct tegra_asoc_utils_data util_data; + int gpio_hp_det; + int gpio_hp_en; + int gpio_mic_present; + int gpio_dmic_clk_en; +}; + +static int tegra_rt5677_asoc_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_card *card = rtd->card; + struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card); + int srate, mclk, err; + + srate = params_rate(params); + mclk = 256 * srate; + + err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk); + if (err < 0) { + dev_err(card->dev, "Can't configure clocks\n"); + return err; + } + + err = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_MCLK, mclk, + SND_SOC_CLOCK_IN); + if (err < 0) { + dev_err(card->dev, "codec_dai clock not set\n"); + return err; + } + + return 0; +} + +static int tegra_rt5677_event_hp(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card); + + if (!gpio_is_valid(machine->gpio_hp_en)) + return 0; + + gpio_set_value_cansleep(machine->gpio_hp_en, + SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static struct snd_soc_ops tegra_rt5677_ops = { + .hw_params = tegra_rt5677_asoc_hw_params, +}; + +static struct snd_soc_jack tegra_rt5677_hp_jack; + +static struct snd_soc_jack_pin tegra_rt5677_hp_jack_pins = { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, +}; +static struct snd_soc_jack_gpio tegra_rt5677_hp_jack_gpio = { + .name = "Headphone detection", + .report = SND_JACK_HEADPHONE, + .debounce_time = 150, +}; + +static struct snd_soc_jack tegra_rt5677_mic_jack; + +static struct snd_soc_jack_pin tegra_rt5677_mic_jack_pins = { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, +}; + +static struct snd_soc_jack_gpio tegra_rt5677_mic_jack_gpio = { + .name = "Headset Mic detection", + .report = SND_JACK_MICROPHONE, + .debounce_time = 150, + .invert = 1 +}; + +static const struct snd_soc_dapm_widget tegra_rt5677_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), + SND_SOC_DAPM_HP("Headphone", tegra_rt5677_event_hp), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Internal Mic 1", NULL), + SND_SOC_DAPM_MIC("Internal Mic 2", NULL), +}; + +static const struct snd_kcontrol_new tegra_rt5677_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Internal Mic 1"), + SOC_DAPM_PIN_SWITCH("Internal Mic 2"), +}; + +static int tegra_rt5677_asoc_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(rtd->card); + + snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, + &tegra_rt5677_hp_jack); + snd_soc_jack_add_pins(&tegra_rt5677_hp_jack, 1, + &tegra_rt5677_hp_jack_pins); + + if (gpio_is_valid(machine->gpio_hp_det)) { + tegra_rt5677_hp_jack_gpio.gpio = machine->gpio_hp_det; + snd_soc_jack_add_gpios(&tegra_rt5677_hp_jack, 1, + &tegra_rt5677_hp_jack_gpio); + } + + + snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE, + &tegra_rt5677_mic_jack); + snd_soc_jack_add_pins(&tegra_rt5677_mic_jack, 1, + &tegra_rt5677_mic_jack_pins); + + if (gpio_is_valid(machine->gpio_mic_present)) { + tegra_rt5677_mic_jack_gpio.gpio = machine->gpio_mic_present; + snd_soc_jack_add_gpios(&tegra_rt5677_mic_jack, 1, + &tegra_rt5677_mic_jack_gpio); + } + + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + + return 0; +} + +static int tegra_rt5677_card_remove(struct snd_soc_card *card) +{ + struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card); + + if (gpio_is_valid(machine->gpio_hp_det)) { + snd_soc_jack_free_gpios(&tegra_rt5677_hp_jack, 1, + &tegra_rt5677_hp_jack_gpio); + } + + if (gpio_is_valid(machine->gpio_mic_present)) { + snd_soc_jack_free_gpios(&tegra_rt5677_mic_jack, 1, + &tegra_rt5677_mic_jack_gpio); + } + + return 0; +} + +static struct snd_soc_dai_link tegra_rt5677_dai = { + .name = "RT5677", + .stream_name = "RT5677 PCM", + .codec_dai_name = "rt5677-aif1", + .init = tegra_rt5677_asoc_init, + .ops = &tegra_rt5677_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, +}; + +static struct snd_soc_card snd_soc_tegra_rt5677 = { + .name = "tegra-rt5677", + .owner = THIS_MODULE, + .remove = tegra_rt5677_card_remove, + .dai_link = &tegra_rt5677_dai, + .num_links = 1, + .controls = tegra_rt5677_controls, + .num_controls = ARRAY_SIZE(tegra_rt5677_controls), + .dapm_widgets = tegra_rt5677_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra_rt5677_dapm_widgets), + .fully_routed = true, +}; + +static int tegra_rt5677_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card = &snd_soc_tegra_rt5677; + struct tegra_rt5677 *machine; + int ret; + + machine = devm_kzalloc(&pdev->dev, + sizeof(struct tegra_rt5677), GFP_KERNEL); + if (!machine) + return -ENOMEM; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); + + machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0); + if (machine->gpio_hp_det == -EPROBE_DEFER) + return -EPROBE_DEFER; + + machine->gpio_mic_present = of_get_named_gpio(np, + "nvidia,mic-present-gpios", 0); + if (machine->gpio_mic_present == -EPROBE_DEFER) + return -EPROBE_DEFER; + + machine->gpio_hp_en = of_get_named_gpio(np, "nvidia,hp-en-gpios", 0); + if (machine->gpio_hp_en == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (gpio_is_valid(machine->gpio_hp_en)) { + ret = devm_gpio_request_one(&pdev->dev, machine->gpio_hp_en, + GPIOF_OUT_INIT_LOW, "hp_en"); + if (ret) { + dev_err(card->dev, "cannot get hp_en gpio\n"); + return ret; + } + } + + machine->gpio_dmic_clk_en = of_get_named_gpio(np, + "nvidia,dmic-clk-en-gpios", 0); + if (machine->gpio_dmic_clk_en == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (gpio_is_valid(machine->gpio_dmic_clk_en)) { + ret = devm_gpio_request_one(&pdev->dev, + machine->gpio_dmic_clk_en, + GPIOF_OUT_INIT_HIGH, "dmic_clk_en"); + if (ret) { + dev_err(card->dev, "cannot get dmic_clk_en gpio\n"); + return ret; + } + } + + ret = snd_soc_of_parse_card_name(card, "nvidia,model"); + if (ret) + goto err; + + ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); + if (ret) + goto err; + + tegra_rt5677_dai.codec_of_node = of_parse_phandle(np, + "nvidia,audio-codec", 0); + if (!tegra_rt5677_dai.codec_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,audio-codec' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + + tegra_rt5677_dai.cpu_of_node = of_parse_phandle(np, + "nvidia,i2s-controller", 0); + if (!tegra_rt5677_dai.cpu_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,i2s-controller' missing or invalid\n"); + ret = -EINVAL; + goto err; + } + tegra_rt5677_dai.platform_of_node = tegra_rt5677_dai.cpu_of_node; + + ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev); + if (ret) + goto err; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err_fini_utils; + } + + return 0; + +err_fini_utils: + tegra_asoc_utils_fini(&machine->util_data); +err: + return ret; +} + +static int tegra_rt5677_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(card); + + snd_soc_unregister_card(card); + + tegra_asoc_utils_fini(&machine->util_data); + + return 0; +} + +static const struct of_device_id tegra_rt5677_of_match[] = { + { .compatible = "nvidia,tegra-audio-rt5677", }, + {}, +}; + +static struct platform_driver tegra_rt5677_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = tegra_rt5677_of_match, + }, + .probe = tegra_rt5677_probe, + .remove = tegra_rt5677_remove, +}; +module_platform_driver(tegra_rt5677_driver); + +MODULE_AUTHOR("Anatol Pomozov "); +MODULE_DESCRIPTION("Tegra+RT5677 machine ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra_rt5677_of_match); -- cgit v1.2.3 From bec78c5f4ae228c4cbd432e97cadb8827fd8f1f9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 12 Jan 2015 10:27:17 +0100 Subject: ASoC: mc13783: Update set_tdm_slot() semantics The mc13783 driver uses inverted semantics for the tx_mask and rx_mask parameter of the set_tdm_slot() callback compared to rest of ASoC. This patch updates the driver's semantics to be consistent with the rest of ASoC, i.e. a set bit means a active slot and a cleared bit means a inactive slot. This will allow us to use the set_tdm_slot() API in a more generic way. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/codecs/mc13783.c | 10 +++++----- sound/soc/fsl/imx-mc13783.c | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index c1e441c2c8af..2ffb9a0570dc 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -328,16 +328,16 @@ static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai, } switch (rx_mask) { - case 0xfffffffc: + case 0x03: val |= SSI_NETWORK_DAC_RXSLOT_0_1; break; - case 0xfffffff3: + case 0x0c: val |= SSI_NETWORK_DAC_RXSLOT_2_3; break; - case 0xffffffcf: + case 0x30: val |= SSI_NETWORK_DAC_RXSLOT_4_5; break; - case 0xffffff3f: + case 0xc0: val |= SSI_NETWORK_DAC_RXSLOT_6_7; break; default: @@ -360,7 +360,7 @@ static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai, if (slots != 4) return -EINVAL; - if (tx_mask != 0xfffffffc) + if (tx_mask != 0x3) return -EINVAL; val |= (0x00 << 2); /* primary timeslot RX/TX(?) is 0 */ diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c index 6bf5bce01a92..9589452e995e 100644 --- a/sound/soc/fsl/imx-mc13783.c +++ b/sound/soc/fsl/imx-mc13783.c @@ -37,8 +37,7 @@ static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret; - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xfffffffc, 0xfffffffc, - 4, 16); + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 4, 16); if (ret) return ret; -- cgit v1.2.3 From d0077aaf2206f3c3524d71a9f38b408dca63852f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 12 Jan 2015 10:27:18 +0100 Subject: ASoC: fsl: Update set_tdm_slot() semantics The fsl-ssi and imx-ssi drivers use inverted semantics for the tx_mask and rx_mask parameter of the set_tdm_slot() callback compared to rest of ASoC. This patch updates the driver's semantics to be consistent with the rest of ASoC, i.e. a set bit means a active slot and a cleared bit means a inactive slot. This will allow us to use the set_tdm_slot() API in a more generic way. Signed-off-by: Lars-Peter Clausen Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/eukrea-tlv320.c | 2 +- sound/soc/fsl/fsl_ssi.c | 4 ++-- sound/soc/fsl/fsl_utils.c | 6 +++--- sound/soc/fsl/imx-mc13783.c | 2 +- sound/soc/fsl/imx-ssi.c | 4 ++-- sound/soc/fsl/wm1133-ev1.c | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c index 9ce70fc67b09..0d0203b34d8b 100644 --- a/sound/soc/fsl/eukrea-tlv320.c +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -69,7 +69,7 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream, return ret; } - snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0); + snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 0); ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, SND_SOC_CLOCK_IN); diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index a65f17d57ffb..8841e59a9869 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -992,8 +992,8 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN, CCSR_SSI_SCR_SSIEN); - regmap_write(regs, CCSR_SSI_STMSK, tx_mask); - regmap_write(regs, CCSR_SSI_SRMSK, rx_mask); + regmap_write(regs, CCSR_SSI_STMSK, ~tx_mask); + regmap_write(regs, CCSR_SSI_SRMSK, ~rx_mask); regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN, val); diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c index 2ac7755da876..5fd4463dbf05 100644 --- a/sound/soc/fsl/fsl_utils.c +++ b/sound/soc/fsl/fsl_utils.c @@ -94,7 +94,7 @@ EXPORT_SYMBOL(fsl_asoc_get_dma_channel); * @rx_mask: bitmask representing active RX slots. * * This function used to generate the TDM slot TX/RX mask. And the TX/RX - * mask will use a 0 bit for an active slot as default, and the default + * mask will use a 1 bit for an active slot as default, and the default * active bits are at the LSB of the mask value. */ int fsl_asoc_xlate_tdm_slot_mask(unsigned int slots, @@ -105,9 +105,9 @@ int fsl_asoc_xlate_tdm_slot_mask(unsigned int slots, return -EINVAL; if (tx_mask) - *tx_mask = ~((1 << slots) - 1); + *tx_mask = ((1 << slots) - 1); if (rx_mask) - *rx_mask = ~((1 << slots) - 1); + *rx_mask = ((1 << slots) - 1); return 0; } diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c index 9589452e995e..9e6493d4e7ff 100644 --- a/sound/soc/fsl/imx-mc13783.c +++ b/sound/soc/fsl/imx-mc13783.c @@ -45,7 +45,7 @@ static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream, if (ret) return ret; - ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x0, 0xfffffffc, 2, 16); + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 16); if (ret) return ret; diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index fa801e17c51e..6aeaac33871a 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -74,8 +74,8 @@ static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, sccr |= SSI_STCCR_DC(slots - 1); writel(sccr, ssi->base + SSI_SRCCR); - writel(tx_mask, ssi->base + SSI_STMSK); - writel(rx_mask, ssi->base + SSI_SRMSK); + writel(~tx_mask, ssi->base + SSI_STMSK); + writel(~rx_mask, ssi->base + SSI_SRMSK); return 0; } diff --git a/sound/soc/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c index 804749a6c61e..ca7b774b13ee 100644 --- a/sound/soc/fsl/wm1133-ev1.c +++ b/sound/soc/fsl/wm1133-ev1.c @@ -116,10 +116,10 @@ static int wm1133_ev1_hw_params(struct snd_pcm_substream *substream, /* TODO: The SSI driver should figure this out for us */ switch (channels) { case 2: - snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0); + snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 0); break; case 1: - snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffe, 0xffffffe, 1, 0); + snd_soc_dai_set_tdm_slot(cpu_dai, 0x1, 0x1, 1, 0); break; default: return -EINVAL; -- cgit v1.2.3 From bbcdb69dfcbd8842ee2a54265abd3e53cb3089e2 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 12 Jan 2015 10:27:19 +0100 Subject: ASoC: fsl: Remove fsl_asoc_xlate_tdm_slot_mask() Now that the fsl DAI drivers uses the same semantics as the rest of a ASoC the custom fsl_asoc_xlate_tdm_slot_mask() callback can be removed as it is identical to the generic one. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_utils.c | 27 --------------------------- sound/soc/fsl/fsl_utils.h | 3 --- sound/soc/fsl/imx-ssi.c | 1 - 3 files changed, 31 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c index 5fd4463dbf05..b9e42b503a37 100644 --- a/sound/soc/fsl/fsl_utils.c +++ b/sound/soc/fsl/fsl_utils.c @@ -86,33 +86,6 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np, } EXPORT_SYMBOL(fsl_asoc_get_dma_channel); -/** - * fsl_asoc_xlate_tdm_slot_mask - generate TDM slot TX/RX mask. - * - * @slots: Number of slots in use. - * @tx_mask: bitmask representing active TX slots. - * @rx_mask: bitmask representing active RX slots. - * - * This function used to generate the TDM slot TX/RX mask. And the TX/RX - * mask will use a 1 bit for an active slot as default, and the default - * active bits are at the LSB of the mask value. - */ -int fsl_asoc_xlate_tdm_slot_mask(unsigned int slots, - unsigned int *tx_mask, - unsigned int *rx_mask) -{ - if (!slots) - return -EINVAL; - - if (tx_mask) - *tx_mask = ((1 << slots) - 1); - if (rx_mask) - *rx_mask = ((1 << slots) - 1); - - return 0; -} -EXPORT_SYMBOL_GPL(fsl_asoc_xlate_tdm_slot_mask); - MODULE_AUTHOR("Timur Tabi "); MODULE_DESCRIPTION("Freescale ASoC utility code"); MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/fsl/fsl_utils.h b/sound/soc/fsl/fsl_utils.h index df535db40313..1687b66ef18e 100644 --- a/sound/soc/fsl/fsl_utils.h +++ b/sound/soc/fsl/fsl_utils.h @@ -22,7 +22,4 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np, const char *name, struct snd_soc_dai_link *dai, unsigned int *dma_channel_id, unsigned int *dma_id); -int fsl_asoc_xlate_tdm_slot_mask(unsigned int slots, - unsigned int *tx_mask, - unsigned int *rx_mask); #endif /* _FSL_UTILS_H */ diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c index 6aeaac33871a..461ce27b884f 100644 --- a/sound/soc/fsl/imx-ssi.c +++ b/sound/soc/fsl/imx-ssi.c @@ -340,7 +340,6 @@ static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { .set_fmt = imx_ssi_set_dai_fmt, .set_clkdiv = imx_ssi_set_dai_clkdiv, .set_sysclk = imx_ssi_set_dai_sysclk, - .xlate_tdm_slot_mask = fsl_asoc_xlate_tdm_slot_mask, .set_tdm_slot = imx_ssi_set_dai_tdm_slot, .trigger = imx_ssi_trigger, }; -- cgit v1.2.3 From e46c93669349072f5caca853f5618cfa01b86008 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 12 Jan 2015 10:27:20 +0100 Subject: ASoC: Update snd_soc_dai_set_tdm_slot() documentation There have been some conflicting interpretations of how snd_soc_dai_set_tdm_slot() is supposed to work. This patch updates the documentation to be more specific on the exact semantics to avoid such problems in the future. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 985052b3fbed..64e047dc7cdf 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2119,15 +2119,27 @@ static int snd_soc_xlate_tdm_slot_mask(unsigned int slots, } /** - * snd_soc_dai_set_tdm_slot - configure DAI TDM. - * @dai: DAI + * snd_soc_dai_set_tdm_slot() - Configures a DAI for TDM operation + * @dai: The DAI to configure * @tx_mask: bitmask representing active TX slots. * @rx_mask: bitmask representing active RX slots. * @slots: Number of slots in use. * @slot_width: Width in bits for each slot. * - * Configures a DAI for TDM operation. Both mask and slots are codec and DAI - * specific. + * This function configures the specified DAI for TDM operation. @slot contains + * the total number of slots of the TDM stream and @slot_with the width of each + * slot in bit clock cycles. @tx_mask and @rx_mask are bitmasks specifying the + * active slots of the TDM stream for the specified DAI, i.e. which slots the + * DAI should write to or read from. If a bit is set the corresponding slot is + * active, if a bit is cleared the corresponding slot is inactive. Bit 0 maps to + * the first slot, bit 1 to the second slot and so on. The first active slot + * maps to the first channel of the DAI, the second active slot to the second + * channel and so on. + * + * TDM mode can be disabled by passing 0 for @slots. In this case @tx_mask, + * @rx_mask and @slot_width will be ignored. + * + * Returns 0 on success, a negative error code otherwise. */ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) -- cgit v1.2.3 From f9911803e82a32c126c40dd6246ade2faf472cbc Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Tue, 13 Jan 2015 21:16:34 +0200 Subject: ASoC: simple-card: Enable and disable DAI clocks as needed Call clk_prepare_enable() and clk_disable_unprepare() for cpu dai clock and codec dai clock in dai statup and shutdown callbacks. This to make sure the related clock are enabled when the audio device is used. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/simple-card.txt | 5 ++++ include/sound/simple_card.h | 1 + sound/soc/generic/simple-card.c | 34 ++++++++++++++++++++++ 3 files changed, 40 insertions(+) (limited to 'sound/soc') diff --git a/Documentation/devicetree/bindings/sound/simple-card.txt b/Documentation/devicetree/bindings/sound/simple-card.txt index c3cba600bf11..73bf314f7240 100644 --- a/Documentation/devicetree/bindings/sound/simple-card.txt +++ b/Documentation/devicetree/bindings/sound/simple-card.txt @@ -75,6 +75,11 @@ Optional CPU/CODEC subnodes properties: it can be specified via "clocks" if system has clock node (= common clock), or "system-clock-frequency" (if system doens't support common clock) + If a clock is specified, it is + enabled with clk_prepare_enable() + in dai startup() and disabled with + clk_disable_unprepare() in dai + shutdown(). Example 1 - single DAI link: diff --git a/include/sound/simple_card.h b/include/sound/simple_card.h index 9b0ac77177b6..1255ddb1d3e2 100644 --- a/include/sound/simple_card.h +++ b/include/sound/simple_card.h @@ -20,6 +20,7 @@ struct asoc_simple_dai { unsigned int sysclk; int slots; int slot_width; + struct clk *clk; }; struct asoc_simple_card_info { diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index fb9240fdc9b7..cb3998d96cca 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -39,6 +39,37 @@ struct simple_card_data { #define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i) #define simple_priv_to_props(priv, i) ((priv)->dai_props + i) +static int asoc_simple_card_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_dai_props *dai_props = + &priv->dai_props[rtd - rtd->card->rtd]; + int ret; + + ret = clk_prepare_enable(dai_props->cpu_dai.clk); + if (ret) + return ret; + + ret = clk_prepare_enable(dai_props->codec_dai.clk); + if (ret) + clk_disable_unprepare(dai_props->cpu_dai.clk); + + return ret; +} + +static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_dai_props *dai_props = + &priv->dai_props[rtd - rtd->card->rtd]; + + clk_disable_unprepare(dai_props->cpu_dai.clk); + + clk_disable_unprepare(dai_props->codec_dai.clk); +} + static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -58,6 +89,8 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, } static struct snd_soc_ops asoc_simple_card_ops = { + .startup = asoc_simple_card_startup, + .shutdown = asoc_simple_card_shutdown, .hw_params = asoc_simple_card_hw_params, }; @@ -219,6 +252,7 @@ asoc_simple_card_sub_parse_of(struct device_node *np, } dai->sysclk = clk_get_rate(clk); + dai->clk = clk; } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) { dai->sysclk = val; } else { -- cgit v1.2.3 From a1be4cead9b9504aa6fc93b624975601cec8c188 Mon Sep 17 00:00:00 2001 From: Thomas Niederprüm Date: Thu, 22 Jan 2015 00:01:53 +0100 Subject: ASoC: sta32x: Convert to direct regmap API usage. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit use the regmap API directly rather than relying on the snd_soc_read/write functions as this seems to be in accordance with common practice. Signed-off-by: Thomas Niederprüm Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + sound/soc/codecs/sta32x.c | 271 ++++++++++++++++++++++++++-------------------- 2 files changed, 152 insertions(+), 120 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 8349f982a586..27e1e3b59649 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -581,6 +581,7 @@ config SND_SOC_SSM4567 config SND_SOC_STA32X tristate + select REGMAP_I2C config SND_SOC_STA350 tristate "STA350 speaker amplifier" diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 7e18200dd6a9..4517453b33b6 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -102,6 +102,35 @@ static const struct reg_default sta32x_regs[] = { { 0x2c, 0x0c }, }; +static const struct regmap_range sta32x_write_regs_range[] = { + regmap_reg_range(STA32X_CONFA, STA32X_AUTO2), + regmap_reg_range(STA32X_C1CFG, STA32X_FDRC2), +}; + +static const struct regmap_range sta32x_read_regs_range[] = { + regmap_reg_range(STA32X_CONFA, STA32X_AUTO2), + regmap_reg_range(STA32X_C1CFG, STA32X_FDRC2), +}; + +static const struct regmap_range sta32x_volatile_regs_range[] = { + regmap_reg_range(STA32X_CFADDR2, STA32X_CFUD), +}; + +static const struct regmap_access_table sta32x_write_regs = { + .yes_ranges = sta32x_write_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta32x_write_regs_range), +}; + +static const struct regmap_access_table sta32x_read_regs = { + .yes_ranges = sta32x_read_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta32x_read_regs_range), +}; + +static const struct regmap_access_table sta32x_volatile_regs = { + .yes_ranges = sta32x_volatile_regs_range, + .n_yes_ranges = ARRAY_SIZE(sta32x_volatile_regs_range), +}; + /* regulator power supply names */ static const char *sta32x_supply_names[] = { "Vdda", /* analog supply, 3.3VV */ @@ -122,6 +151,7 @@ struct sta32x_priv { u32 coef_shadow[STA32X_COEF_COUNT]; struct delayed_work watchdog_work; int shutdown; + struct mutex coeff_lock; }; static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12700, 50, 1); @@ -244,29 +274,42 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); int numcoef = kcontrol->private_value >> 16; int index = kcontrol->private_value & 0xffff; - unsigned int cfud; - int i; + unsigned int cfud, val; + int i, ret = 0; + + mutex_lock(&sta32x->coeff_lock); /* preserve reserved bits in STA32X_CFUD */ - cfud = snd_soc_read(codec, STA32X_CFUD) & 0xf0; - /* chip documentation does not say if the bits are self clearing, - * so do it explicitly */ - snd_soc_write(codec, STA32X_CFUD, cfud); + regmap_read(sta32x->regmap, STA32X_CFUD, &cfud); + cfud &= 0xf0; + /* + * chip documentation does not say if the bits are self clearing, + * so do it explicitly + */ + regmap_write(sta32x->regmap, STA32X_CFUD, cfud); - snd_soc_write(codec, STA32X_CFADDR2, index); - if (numcoef == 1) - snd_soc_write(codec, STA32X_CFUD, cfud | 0x04); - else if (numcoef == 5) - snd_soc_write(codec, STA32X_CFUD, cfud | 0x08); - else - return -EINVAL; - for (i = 0; i < 3 * numcoef; i++) - ucontrol->value.bytes.data[i] = - snd_soc_read(codec, STA32X_B1CF1 + i); + regmap_write(sta32x->regmap, STA32X_CFADDR2, index); + if (numcoef == 1) { + regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x04); + } else if (numcoef == 5) { + regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x08); + } else { + ret = -EINVAL; + goto exit_unlock; + } - return 0; + for (i = 0; i < 3 * numcoef; i++) { + regmap_read(sta32x->regmap, STA32X_B1CF1 + i, &val); + ucontrol->value.bytes.data[i] = val; + } + +exit_unlock: + mutex_unlock(&sta32x->coeff_lock); + + return ret; } static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol, @@ -280,24 +323,27 @@ static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol, int i; /* preserve reserved bits in STA32X_CFUD */ - cfud = snd_soc_read(codec, STA32X_CFUD) & 0xf0; - /* chip documentation does not say if the bits are self clearing, - * so do it explicitly */ - snd_soc_write(codec, STA32X_CFUD, cfud); + regmap_read(sta32x->regmap, STA32X_CFUD, &cfud); + cfud &= 0xf0; + /* + * chip documentation does not say if the bits are self clearing, + * so do it explicitly + */ + regmap_write(sta32x->regmap, STA32X_CFUD, cfud); - snd_soc_write(codec, STA32X_CFADDR2, index); + regmap_write(sta32x->regmap, STA32X_CFADDR2, index); for (i = 0; i < numcoef && (index + i < STA32X_COEF_COUNT); i++) sta32x->coef_shadow[index + i] = (ucontrol->value.bytes.data[3 * i] << 16) | (ucontrol->value.bytes.data[3 * i + 1] << 8) | (ucontrol->value.bytes.data[3 * i + 2]); for (i = 0; i < 3 * numcoef; i++) - snd_soc_write(codec, STA32X_B1CF1 + i, - ucontrol->value.bytes.data[i]); + regmap_write(sta32x->regmap, STA32X_B1CF1 + i, + ucontrol->value.bytes.data[i]); if (numcoef == 1) - snd_soc_write(codec, STA32X_CFUD, cfud | 0x01); + regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x01); else if (numcoef == 5) - snd_soc_write(codec, STA32X_CFUD, cfud | 0x02); + regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x02); else return -EINVAL; @@ -311,20 +357,23 @@ static int sta32x_sync_coef_shadow(struct snd_soc_codec *codec) int i; /* preserve reserved bits in STA32X_CFUD */ - cfud = snd_soc_read(codec, STA32X_CFUD) & 0xf0; + regmap_read(sta32x->regmap, STA32X_CFUD, &cfud); + cfud &= 0xf0; for (i = 0; i < STA32X_COEF_COUNT; i++) { - snd_soc_write(codec, STA32X_CFADDR2, i); - snd_soc_write(codec, STA32X_B1CF1, - (sta32x->coef_shadow[i] >> 16) & 0xff); - snd_soc_write(codec, STA32X_B1CF2, - (sta32x->coef_shadow[i] >> 8) & 0xff); - snd_soc_write(codec, STA32X_B1CF3, - (sta32x->coef_shadow[i]) & 0xff); - /* chip documentation does not say if the bits are - * self-clearing, so do it explicitly */ - snd_soc_write(codec, STA32X_CFUD, cfud); - snd_soc_write(codec, STA32X_CFUD, cfud | 0x01); + regmap_write(sta32x->regmap, STA32X_CFADDR2, i); + regmap_write(sta32x->regmap, STA32X_B1CF1, + (sta32x->coef_shadow[i] >> 16) & 0xff); + regmap_write(sta32x->regmap, STA32X_B1CF2, + (sta32x->coef_shadow[i] >> 8) & 0xff); + regmap_write(sta32x->regmap, STA32X_B1CF3, + (sta32x->coef_shadow[i]) & 0xff); + /* + * chip documentation does not say if the bits are + * self-clearing, so do it explicitly + */ + regmap_write(sta32x->regmap, STA32X_CFUD, cfud); + regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x01); } return 0; } @@ -336,11 +385,11 @@ static int sta32x_cache_sync(struct snd_soc_codec *codec) int rc; /* mute during register sync */ - mute = snd_soc_read(codec, STA32X_MMUTE); - snd_soc_write(codec, STA32X_MMUTE, mute | STA32X_MMUTE_MMUTE); + regmap_read(sta32x->regmap, STA32X_MMUTE, &mute); + regmap_write(sta32x->regmap, STA32X_MMUTE, mute | STA32X_MMUTE_MMUTE); sta32x_sync_coef_shadow(codec); rc = regcache_sync(sta32x->regmap); - snd_soc_write(codec, STA32X_MMUTE, mute); + regmap_write(sta32x->regmap, STA32X_MMUTE, mute); return rc; } @@ -599,10 +648,7 @@ static int sta32x_set_dai_fmt(struct snd_soc_dai *codec_dai, { struct snd_soc_codec *codec = codec_dai->codec; struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); - u8 confb = snd_soc_read(codec, STA32X_CONFB); - - pr_debug("\n"); - confb &= ~(STA32X_CONFB_C1IM | STA32X_CONFB_C2IM); + u8 confb = 0; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: @@ -632,8 +678,8 @@ static int sta32x_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - snd_soc_write(codec, STA32X_CONFB, confb); - return 0; + return regmap_update_bits(sta32x->regmap, STA32X_CONFB, + STA32X_CONFB_C1IM | STA32X_CONFB_C2IM, confb); } /** @@ -653,7 +699,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream, struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); unsigned int rate; int i, mcs = -1, ir = -1; - u8 confa, confb; + unsigned int confa, confb; rate = params_rate(params); pr_debug("rate: %u\n", rate); @@ -672,12 +718,10 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream, if (mcs < 0) return -EINVAL; - confa = snd_soc_read(codec, STA32X_CONFA); - confa &= ~(STA32X_CONFA_MCS_MASK | STA32X_CONFA_IR_MASK); - confa |= (ir << STA32X_CONFA_IR_SHIFT) | (mcs << STA32X_CONFA_MCS_SHIFT); + confa = (ir << STA32X_CONFA_IR_SHIFT) | + (mcs << STA32X_CONFA_MCS_SHIFT); + confb = 0; - confb = snd_soc_read(codec, STA32X_CONFB); - confb &= ~(STA32X_CONFB_SAI_MASK | STA32X_CONFB_SAIFB); switch (params_width(params)) { case 24: pr_debug("24bit\n"); @@ -746,8 +790,20 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - snd_soc_write(codec, STA32X_CONFA, confa); - snd_soc_write(codec, STA32X_CONFB, confb); + ret = regmap_update_bits(sta32x->regmap, STA32X_CONFA, + STA32X_CONFA_MCS_MASK | STA32X_CONFA_IR_MASK, + confa); + if (ret < 0) + return ret; + + ret = regmap_update_bits(sta32x->regmap, STA32X_CONFB, + STA32X_CONFB_SAI_MASK | STA32X_CONFB_SAIFB, + confb); + if (ret < 0) + return ret; + + return 0; +} return 0; } @@ -773,7 +829,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: /* Full power on */ - snd_soc_update_bits(codec, STA32X_CONFF, + regmap_update_bits(sta32x->regmap, STA32X_CONFF, STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, STA32X_CONFF_PWDN | STA32X_CONFF_EAPD); break; @@ -792,19 +848,17 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec, sta32x_watchdog_start(sta32x); } - /* Power up to mute */ - /* FIXME */ - snd_soc_update_bits(codec, STA32X_CONFF, - STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, - STA32X_CONFF_PWDN | STA32X_CONFF_EAPD); + /* Power down */ + regmap_update_bits(sta32x->regmap, STA32X_CONFF, + STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, + 0); break; case SND_SOC_BIAS_OFF: /* The chip runs through the power down sequence for us. */ - snd_soc_update_bits(codec, STA32X_CONFF, - STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, - STA32X_CONFF_PWDN); + regmap_update_bits(sta32x->regmap, STA32X_CONFF, + STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, 0); msleep(300); sta32x_watchdog_stop(sta32x); regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), @@ -836,11 +890,8 @@ static struct snd_soc_dai_driver sta32x_dai = { static int sta32x_probe(struct snd_soc_codec *codec) { struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); + struct sta32x_platform_data *pdata = sta32x->pdata; int i, ret = 0, thermal = 0; - - sta32x->codec = codec; - sta32x->pdata = dev_get_platdata(codec->dev); - ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); if (ret != 0) { @@ -848,50 +899,34 @@ static int sta32x_probe(struct snd_soc_codec *codec) return ret; } - /* Chip documentation explicitly requires that the reset values - * of reserved register bits are left untouched. - * Write the register default value to cache for reserved registers, - * so the write to the these registers are suppressed by the cache - * restore code when it skips writes of default registers. - */ - regcache_cache_only(sta32x->regmap, true); - snd_soc_write(codec, STA32X_CONFC, 0xc2); - snd_soc_write(codec, STA32X_CONFE, 0xc2); - snd_soc_write(codec, STA32X_CONFF, 0x5c); - snd_soc_write(codec, STA32X_MMUTE, 0x10); - snd_soc_write(codec, STA32X_AUTO1, 0x60); - snd_soc_write(codec, STA32X_AUTO3, 0x00); - snd_soc_write(codec, STA32X_C3CFG, 0x40); - regcache_cache_only(sta32x->regmap, false); - /* set thermal warning adjustment and recovery */ - if (!(sta32x->pdata->thermal_conf & STA32X_THERMAL_ADJUSTMENT_ENABLE)) + if (!pdata->thermal_warning_recovery) thermal |= STA32X_CONFA_TWAB; - if (!(sta32x->pdata->thermal_conf & STA32X_THERMAL_RECOVERY_ENABLE)) + if (!pdata->thermal_warning_adjustment) thermal |= STA32X_CONFA_TWRB; - snd_soc_update_bits(codec, STA32X_CONFA, - STA32X_CONFA_TWAB | STA32X_CONFA_TWRB, - thermal); + regmap_update_bits(sta32x->regmap, STA32X_CONFA, + STA32X_CONFA_TWAB | STA32X_CONFA_TWRB, + thermal); /* select output configuration */ - snd_soc_update_bits(codec, STA32X_CONFF, - STA32X_CONFF_OCFG_MASK, - sta32x->pdata->output_conf - << STA32X_CONFF_OCFG_SHIFT); + regmap_update_bits(sta32x->regmap, STA32X_CONFF, + STA32X_CONFF_OCFG_MASK, + pdata->output_conf + << STA32X_CONFF_OCFG_SHIFT); /* channel to output mapping */ - snd_soc_update_bits(codec, STA32X_C1CFG, - STA32X_CxCFG_OM_MASK, - sta32x->pdata->ch1_output_mapping - << STA32X_CxCFG_OM_SHIFT); - snd_soc_update_bits(codec, STA32X_C2CFG, - STA32X_CxCFG_OM_MASK, - sta32x->pdata->ch2_output_mapping - << STA32X_CxCFG_OM_SHIFT); - snd_soc_update_bits(codec, STA32X_C3CFG, - STA32X_CxCFG_OM_MASK, - sta32x->pdata->ch3_output_mapping - << STA32X_CxCFG_OM_SHIFT); + regmap_update_bits(sta32x->regmap, STA32X_C1CFG, + STA32X_CxCFG_OM_MASK, + pdata->ch1_output_mapping + << STA32X_CxCFG_OM_SHIFT); + regmap_update_bits(sta32x->regmap, STA32X_C2CFG, + STA32X_CxCFG_OM_MASK, + pdata->ch2_output_mapping + << STA32X_CxCFG_OM_SHIFT); + regmap_update_bits(sta32x->regmap, STA32X_C3CFG, + STA32X_CxCFG_OM_MASK, + pdata->ch3_output_mapping + << STA32X_CxCFG_OM_SHIFT); /* initialize coefficient shadow RAM with reset values */ for (i = 4; i <= 49; i += 5) @@ -924,16 +959,6 @@ static int sta32x_remove(struct snd_soc_codec *codec) return 0; } -static bool sta32x_reg_is_volatile(struct device *dev, unsigned int reg) -{ - switch (reg) { - case STA32X_CONFA ... STA32X_L2ATRT: - case STA32X_MPCC1 ... STA32X_FDRC2: - return 0; - } - return 1; -} - static const struct snd_soc_codec_driver sta32x_codec = { .probe = sta32x_probe, .remove = sta32x_remove, @@ -954,12 +979,16 @@ static const struct regmap_config sta32x_regmap = { .reg_defaults = sta32x_regs, .num_reg_defaults = ARRAY_SIZE(sta32x_regs), .cache_type = REGCACHE_RBTREE, - .volatile_reg = sta32x_reg_is_volatile, + .wr_table = &sta32x_write_regs, + .rd_table = &sta32x_read_regs, + .volatile_table = &sta32x_volatile_regs, +}; }; static int sta32x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct device *dev = &i2c->dev; struct sta32x_priv *sta32x; int ret, i; @@ -968,6 +997,8 @@ static int sta32x_i2c_probe(struct i2c_client *i2c, if (!sta32x) return -ENOMEM; + mutex_init(&sta32x->coeff_lock); + sta32x->pdata = dev_get_platdata(dev); /* regulators */ for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++) sta32x->supplies[i].supply = sta32x_supply_names[i]; @@ -982,15 +1013,15 @@ static int sta32x_i2c_probe(struct i2c_client *i2c, sta32x->regmap = devm_regmap_init_i2c(i2c, &sta32x_regmap); if (IS_ERR(sta32x->regmap)) { ret = PTR_ERR(sta32x->regmap); - dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); + dev_err(dev, "Failed to init regmap: %d\n", ret); return ret; } i2c_set_clientdata(i2c, sta32x); - ret = snd_soc_register_codec(&i2c->dev, &sta32x_codec, &sta32x_dai, 1); - if (ret != 0) - dev_err(&i2c->dev, "Failed to register codec (%d)\n", ret); + ret = snd_soc_register_codec(dev, &sta32x_codec, &sta32x_dai, 1); + if (ret < 0) + dev_err(dev, "Failed to register codec (%d)\n", ret); return ret; } -- cgit v1.2.3 From b66a29808e1fac7fc5c8174e3ec0f014bd418280 Mon Sep 17 00:00:00 2001 From: Thomas Niederprüm Date: Thu, 22 Jan 2015 00:01:54 +0100 Subject: ASoC: sta32x: make sta32x a gpio consumer for the reset GPIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reset GPIO on the STA32X Codecs is used to reset the Codec and clear all registers. Also taking it down puts the IC in power save mode, so we put the device in reset mode when we go to sleep. Signed-off-by: Thomas Niederprüm Signed-off-by: Mark Brown --- sound/soc/codecs/sta32x.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 4517453b33b6..ae9283781bb6 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -151,6 +152,7 @@ struct sta32x_priv { u32 coef_shadow[STA32X_COEF_COUNT]; struct delayed_work watchdog_work; int shutdown; + struct gpio_desc *gpiod_nreset; struct mutex coeff_lock; }; @@ -804,6 +806,16 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream, return 0; } + +static int sta32x_startup_sequence(struct sta32x_priv *sta32x) +{ + if (sta32x->gpiod_nreset) { + gpiod_set_value(sta32x->gpiod_nreset, 0); + mdelay(1); + gpiod_set_value(sta32x->gpiod_nreset, 1); + mdelay(1); + } + return 0; } @@ -844,6 +856,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec, return ret; } + sta32x_startup_sequence(sta32x); sta32x_cache_sync(codec); sta32x_watchdog_start(sta32x); } @@ -861,6 +874,10 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec, STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, 0); msleep(300); sta32x_watchdog_stop(sta32x); + + if (sta32x->gpiod_nreset) + gpiod_set_value(sta32x->gpiod_nreset, 0); + regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); break; @@ -899,6 +916,11 @@ static int sta32x_probe(struct snd_soc_codec *codec) return ret; } + ret = sta32x_startup_sequence(sta32x); + if (ret < 0) { + dev_err(codec->dev, "Failed to startup device\n"); + return ret; + } /* set thermal warning adjustment and recovery */ if (!pdata->thermal_warning_recovery) thermal |= STA32X_CONFA_TWAB; @@ -999,6 +1021,19 @@ static int sta32x_i2c_probe(struct i2c_client *i2c, mutex_init(&sta32x->coeff_lock); sta32x->pdata = dev_get_platdata(dev); + + /* GPIOs */ + sta32x->gpiod_nreset = devm_gpiod_get(dev, "reset"); + if (IS_ERR(sta32x->gpiod_nreset)) { + ret = PTR_ERR(sta32x->gpiod_nreset); + if (ret != -ENOENT && ret != -ENOSYS) + return ret; + + sta32x->gpiod_nreset = NULL; + } else { + gpiod_direction_output(sta32x->gpiod_nreset, 0); + } + /* regulators */ for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++) sta32x->supplies[i].supply = sta32x_supply_names[i]; -- cgit v1.2.3 From 88483f59d95f06e43dc9152afc81402df687bd27 Mon Sep 17 00:00:00 2001 From: Thomas Niederprüm Date: Thu, 22 Jan 2015 00:01:55 +0100 Subject: ASoC: sta32x: use DECLARE_TLV_DB_RANGE macro. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Niederprüm Signed-off-by: Mark Brown --- sound/soc/codecs/sta32x.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index ae9283781bb6..b808c65788fa 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -187,37 +187,32 @@ static const char *sta32x_limiter_release_rate[] = { "0.5116", "0.1370", "0.0744", "0.0499", "0.0360", "0.0299", "0.0264", "0.0208", "0.0198", "0.0172", "0.0147", "0.0137", "0.0134", "0.0117", "0.0110", "0.0104" }; - -static const unsigned int sta32x_limiter_ac_attack_tlv[] = { - TLV_DB_RANGE_HEAD(2), +static DECLARE_TLV_DB_RANGE(sta32x_limiter_ac_attack_tlv, 0, 7, TLV_DB_SCALE_ITEM(-1200, 200, 0), 8, 16, TLV_DB_SCALE_ITEM(300, 100, 0), -}; +); -static const unsigned int sta32x_limiter_ac_release_tlv[] = { - TLV_DB_RANGE_HEAD(5), +static DECLARE_TLV_DB_RANGE(sta32x_limiter_ac_release_tlv, 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), 1, 1, TLV_DB_SCALE_ITEM(-2900, 0, 0), 2, 2, TLV_DB_SCALE_ITEM(-2000, 0, 0), 3, 8, TLV_DB_SCALE_ITEM(-1400, 200, 0), 8, 16, TLV_DB_SCALE_ITEM(-700, 100, 0), -}; +); -static const unsigned int sta32x_limiter_drc_attack_tlv[] = { - TLV_DB_RANGE_HEAD(3), +static DECLARE_TLV_DB_RANGE(sta32x_limiter_drc_attack_tlv, 0, 7, TLV_DB_SCALE_ITEM(-3100, 200, 0), 8, 13, TLV_DB_SCALE_ITEM(-1600, 100, 0), 14, 16, TLV_DB_SCALE_ITEM(-1000, 300, 0), -}; +); -static const unsigned int sta32x_limiter_drc_release_tlv[] = { - TLV_DB_RANGE_HEAD(5), +static DECLARE_TLV_DB_RANGE(sta32x_limiter_drc_release_tlv, 0, 0, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0), 1, 2, TLV_DB_SCALE_ITEM(-3800, 200, 0), 3, 4, TLV_DB_SCALE_ITEM(-3300, 200, 0), 5, 12, TLV_DB_SCALE_ITEM(-3000, 200, 0), 13, 16, TLV_DB_SCALE_ITEM(-1500, 300, 0), -}; +); static SOC_ENUM_SINGLE_DECL(sta32x_drc_ac_enum, STA32X_CONFD, STA32X_CONFD_DRC_SHIFT, -- cgit v1.2.3 From 1c34c876c4abb219381dcb7096206f1a609f119b Mon Sep 17 00:00:00 2001 From: Thomas Niederprüm Date: Thu, 22 Jan 2015 00:01:57 +0100 Subject: ASoC: sta32x: move code to calculate mclk divider and extrapolation ratio to sta32x_hw_params() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Niederprüm Signed-off-by: Mark Brown --- sound/soc/codecs/sta32x.c | 87 +++++++++++++++++------------------------------ 1 file changed, 32 insertions(+), 55 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index b808c65788fa..ec2372498c92 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -554,17 +554,12 @@ static struct { }; /* MCLK to fs clock ratios */ -static struct { - int ratio; - int mcs; -} mclk_ratios[3][7] = { - { { 768, 0 }, { 512, 1 }, { 384, 2 }, { 256, 3 }, - { 128, 4 }, { 576, 5 }, { 0, 0 } }, - { { 384, 2 }, { 256, 3 }, { 192, 4 }, { 128, 5 }, {64, 0 }, { 0, 0 } }, - { { 384, 2 }, { 256, 3 }, { 192, 4 }, { 128, 5 }, {64, 0 }, { 0, 0 } }, +static int mcs_ratio_table[3][7] = { + { 768, 512, 384, 256, 128, 576, 0 }, + { 384, 256, 192, 128, 64, 0 }, + { 384, 256, 192, 128, 64, 0 }, }; - /** * sta32x_set_dai_sysclk - configure MCLK * @codec_dai: the codec DAI @@ -589,46 +584,10 @@ static int sta32x_set_dai_sysclk(struct snd_soc_dai *codec_dai, { struct snd_soc_codec *codec = codec_dai->codec; struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); - int i, j, ir, fs; - unsigned int rates = 0; - unsigned int rate_min = -1; - unsigned int rate_max = 0; - pr_debug("mclk=%u\n", freq); + dev_dbg(codec->dev, "mclk=%u\n", freq); sta32x->mclk = freq; - if (sta32x->mclk) { - for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) { - ir = interpolation_ratios[i].ir; - fs = interpolation_ratios[i].fs; - for (j = 0; mclk_ratios[ir][j].ratio; j++) { - if (mclk_ratios[ir][j].ratio * fs == freq) { - rates |= snd_pcm_rate_to_rate_bit(fs); - if (fs < rate_min) - rate_min = fs; - if (fs > rate_max) - rate_max = fs; - break; - } - } - } - /* FIXME: soc should support a rate list */ - rates &= ~SNDRV_PCM_RATE_KNOT; - - if (!rates) { - dev_err(codec->dev, "could not find a valid sample rate\n"); - return -EINVAL; - } - } else { - /* enable all possible rates */ - rates = STA32X_RATES; - rate_min = 32000; - rate_max = 192000; - } - - codec_dai->driver->playback.rates = rates; - codec_dai->driver->playback.rate_min = rate_min; - codec_dai->driver->playback.rate_max = rate_max; return 0; } @@ -694,26 +653,44 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); - unsigned int rate; - int i, mcs = -1, ir = -1; + int i, mcs = -EINVAL, ir = -EINVAL; unsigned int confa, confb; + unsigned int rate, ratio; + int ret; + + if (!sta32x->mclk) { + dev_err(codec->dev, + "sta32x->mclk is unset. Unable to determine ratio\n"); + return -EIO; + } rate = params_rate(params); - pr_debug("rate: %u\n", rate); - for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) + ratio = sta32x->mclk / rate; + dev_dbg(codec->dev, "rate: %u, ratio: %u\n", rate, ratio); + + for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++) { if (interpolation_ratios[i].fs == rate) { ir = interpolation_ratios[i].ir; break; } - if (ir < 0) + } + + if (ir < 0) { + dev_err(codec->dev, "Unsupported samplerate: %u\n", rate); return -EINVAL; - for (i = 0; mclk_ratios[ir][i].ratio; i++) - if (mclk_ratios[ir][i].ratio * rate == sta32x->mclk) { - mcs = mclk_ratios[ir][i].mcs; + } + + for (i = 0; i < 6; i++) { + if (mcs_ratio_table[ir][i] == ratio) { + mcs = i; break; } - if (mcs < 0) + } + + if (mcs < 0) { + dev_err(codec->dev, "Unresolvable ratio: %u\n", ratio); return -EINVAL; + } confa = (ir << STA32X_CONFA_IR_SHIFT) | (mcs << STA32X_CONFA_MCS_SHIFT); -- cgit v1.2.3 From f04b1e760a51120f358826d815d12c3f8ecdf1b4 Mon Sep 17 00:00:00 2001 From: Thomas Niederprüm Date: Thu, 22 Jan 2015 00:01:58 +0100 Subject: ASoC: sta32x: add device tree binding. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit make the sta32x driver usable with device tree configs. Code is heavily based on the sta350 driver. Signed-off-by: Thomas Niederprüm Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/st,sta32x.txt | 92 ++++++++++++++++++ include/sound/sta32x.h | 18 +++- sound/soc/codecs/sta32x.c | 108 ++++++++++++++++++++- 3 files changed, 211 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/st,sta32x.txt (limited to 'sound/soc') diff --git a/Documentation/devicetree/bindings/sound/st,sta32x.txt b/Documentation/devicetree/bindings/sound/st,sta32x.txt new file mode 100644 index 000000000000..255de3ae5b2f --- /dev/null +++ b/Documentation/devicetree/bindings/sound/st,sta32x.txt @@ -0,0 +1,92 @@ +STA32X audio CODEC + +The driver for this device only supports I2C. + +Required properties: + + - compatible: "st,sta32x" + - reg: the I2C address of the device for I2C + - reset-gpios: a GPIO spec for the reset pin. If specified, it will be + deasserted before communication to the codec starts. + + - power-down-gpios: a GPIO spec for the power down pin. If specified, + it will be deasserted before communication to the codec + starts. + + - Vdda-supply: regulator spec, providing 3.3V + - Vdd3-supply: regulator spec, providing 3.3V + - Vcc-supply: regulator spec, providing 5V - 26V + +Optional properties: + + - st,output-conf: number, Selects the output configuration: + 0: 2-channel (full-bridge) power, 2-channel data-out + 1: 2 (half-bridge). 1 (full-bridge) on-board power + 2: 2 Channel (Full-Bridge) Power, 1 Channel FFX + 3: 1 Channel Mono-Parallel + If parameter is missing, mode 0 will be enabled. + This property has to be specified as '/bits/ 8' value. + + - st,ch1-output-mapping: Channel 1 output mapping + - st,ch2-output-mapping: Channel 2 output mapping + - st,ch3-output-mapping: Channel 3 output mapping + 0: Channel 1 + 1: Channel 2 + 2: Channel 3 + If parameter is missing, channel 1 is chosen. + This properties have to be specified as '/bits/ 8' values. + + - st,thermal-warning-recover: + If present, thermal warning recovery is enabled. + + - st,thermal-warning-adjustment: + If present, thermal warning adjustment is enabled. + + - st,fault-detect-recovery: + If present, then fault recovery will be enabled. + + - st,drop-compensation-ns: number + Only required for "st,ffx-power-output-mode" == + "variable-drop-compensation". + Specifies the drop compensation in nanoseconds. + The value must be in the range of 0..300, and only + multiples of 20 are allowed. Default is 140ns. + + - st,max-power-use-mpcc: + If present, then MPCC bits are used for MPC coefficients, + otherwise standard MPC coefficients are used. + + - st,max-power-corr: + If present, power bridge correction for THD reduction near maximum + power output is enabled. + + - st,am-reduction-mode: + If present, FFX mode runs in AM reduction mode, otherwise normal + FFX mode is used. + + - st,odd-pwm-speed-mode: + If present, PWM speed mode run on odd speed mode (341.3 kHz) on all + channels. If not present, normal PWM spped mode (384 kHz) will be used. + + - st,invalid-input-detect-mute: + If present, automatic invalid input detect mute is enabled. + +Example: + +codec: sta32x@38 { + compatible = "st,sta32x"; + reg = <0x1c>; + reset-gpios = <&gpio1 19 0>; + power-down-gpios = <&gpio1 16 0>; + st,output-conf = /bits/ 8 <0x3>; // set output to 2-channel + // (full-bridge) power, + // 2-channel data-out + st,ch1-output-mapping = /bits/ 8 <0>; // set channel 1 output ch 1 + st,ch2-output-mapping = /bits/ 8 <0>; // set channel 2 output ch 1 + st,ch3-output-mapping = /bits/ 8 <0>; // set channel 3 output ch 1 + st,max-power-correction; // enables power bridge + // correction for THD reduction + // near maximum power output + st,invalid-input-detect-mute; // mute if no valid digital + // audio signal is provided. +}; diff --git a/include/sound/sta32x.h b/include/sound/sta32x.h index 8d93b0357a14..a894f7d17b1a 100644 --- a/include/sound/sta32x.h +++ b/include/sound/sta32x.h @@ -24,12 +24,20 @@ #define STA32X_THERMAL_RECOVERY_ENABLE 2 struct sta32x_platform_data { - int output_conf; - int ch1_output_mapping; - int ch2_output_mapping; - int ch3_output_mapping; - int thermal_conf; + u8 output_conf; + u8 ch1_output_mapping; + u8 ch2_output_mapping; + u8 ch3_output_mapping; int needs_esd_watchdog; + u8 drop_compensation_ns; + unsigned int thermal_warning_recovery:1; + unsigned int thermal_warning_adjustment:1; + unsigned int fault_detect_recovery:1; + unsigned int max_power_use_mpcc:1; + unsigned int max_power_correction:1; + unsigned int am_reduction_mode:1; + unsigned int odd_pwm_speed_mode:1; + unsigned int invalid_input_detect_mute:1; }; #endif /* __LINUX_SND__STA32X_H */ diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index ec2372498c92..669b67f8cee3 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -893,15 +895,49 @@ static int sta32x_probe(struct snd_soc_codec *codec) dev_err(codec->dev, "Failed to startup device\n"); return ret; } - /* set thermal warning adjustment and recovery */ + + /* CONFA */ if (!pdata->thermal_warning_recovery) thermal |= STA32X_CONFA_TWAB; if (!pdata->thermal_warning_adjustment) thermal |= STA32X_CONFA_TWRB; + if (!pdata->fault_detect_recovery) + thermal |= STA32X_CONFA_FDRB; regmap_update_bits(sta32x->regmap, STA32X_CONFA, - STA32X_CONFA_TWAB | STA32X_CONFA_TWRB, + STA32X_CONFA_TWAB | STA32X_CONFA_TWRB | + STA32X_CONFA_FDRB, thermal); + /* CONFC */ + regmap_update_bits(sta32x->regmap, STA32X_CONFC, + STA32X_CONFC_CSZ_MASK, + pdata->drop_compensation_ns + << STA32X_CONFC_CSZ_SHIFT); + + /* CONFE */ + regmap_update_bits(sta32x->regmap, STA32X_CONFE, + STA32X_CONFE_MPCV, + pdata->max_power_use_mpcc ? + STA32X_CONFE_MPCV : 0); + regmap_update_bits(sta32x->regmap, STA32X_CONFE, + STA32X_CONFE_MPC, + pdata->max_power_correction ? + STA32X_CONFE_MPC : 0); + regmap_update_bits(sta32x->regmap, STA32X_CONFE, + STA32X_CONFE_AME, + pdata->am_reduction_mode ? + STA32X_CONFE_AME : 0); + regmap_update_bits(sta32x->regmap, STA32X_CONFE, + STA32X_CONFE_PWMS, + pdata->odd_pwm_speed_mode ? + STA32X_CONFE_PWMS : 0); + + /* CONFF */ + regmap_update_bits(sta32x->regmap, STA32X_CONFF, + STA32X_CONFF_IDE, + pdata->invalid_input_detect_mute ? + STA32X_CONFF_IDE : 0); + /* select output configuration */ regmap_update_bits(sta32x->regmap, STA32X_CONFF, STA32X_CONFF_OCFG_MASK, @@ -977,7 +1013,66 @@ static const struct regmap_config sta32x_regmap = { .rd_table = &sta32x_read_regs, .volatile_table = &sta32x_volatile_regs, }; + +#ifdef CONFIG_OF +static const struct of_device_id st32x_dt_ids[] = { + { .compatible = "st,sta32x", }, + { } }; +MODULE_DEVICE_TABLE(of, st32x_dt_ids); + +static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x) +{ + struct device_node *np = dev->of_node; + struct sta32x_platform_data *pdata; + u16 tmp; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + of_property_read_u8(np, "st,output-conf", + &pdata->output_conf); + of_property_read_u8(np, "st,ch1-output-mapping", + &pdata->ch1_output_mapping); + of_property_read_u8(np, "st,ch2-output-mapping", + &pdata->ch2_output_mapping); + of_property_read_u8(np, "st,ch3-output-mapping", + &pdata->ch3_output_mapping); + + if (of_get_property(np, "st,thermal-warning-recovery", NULL)) + pdata->thermal_warning_recovery = 1; + if (of_get_property(np, "st,thermal-warning-adjustment", NULL)) + pdata->thermal_warning_adjustment = 1; + if (of_get_property(np, "st,needs_esd_watchdog", NULL)) + pdata->needs_esd_watchdog = 1; + + tmp = 140; + of_property_read_u16(np, "st,drop-compensation-ns", &tmp); + pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20; + + /* CONFE */ + if (of_get_property(np, "st,max-power-use-mpcc", NULL)) + pdata->max_power_use_mpcc = 1; + + if (of_get_property(np, "st,max-power-correction", NULL)) + pdata->max_power_correction = 1; + + if (of_get_property(np, "st,am-reduction-mode", NULL)) + pdata->am_reduction_mode = 1; + + if (of_get_property(np, "st,odd-pwm-speed-mode", NULL)) + pdata->odd_pwm_speed_mode = 1; + + /* CONFF */ + if (of_get_property(np, "st,invalid-input-detect-mute", NULL)) + pdata->invalid_input_detect_mute = 1; + + sta32x->pdata = pdata; + + return 0; +} +#endif static int sta32x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) @@ -994,6 +1089,14 @@ static int sta32x_i2c_probe(struct i2c_client *i2c, mutex_init(&sta32x->coeff_lock); sta32x->pdata = dev_get_platdata(dev); +#ifdef CONFIG_OF + if (dev->of_node) { + ret = sta32x_probe_dt(dev, sta32x); + if (ret < 0) + return ret; + } +#endif + /* GPIOs */ sta32x->gpiod_nreset = devm_gpiod_get(dev, "reset"); if (IS_ERR(sta32x->gpiod_nreset)) { @@ -1051,6 +1154,7 @@ static struct i2c_driver sta32x_i2c_driver = { .driver = { .name = "sta32x", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(st32x_dt_ids), }, .probe = sta32x_i2c_probe, .remove = sta32x_i2c_remove, -- cgit v1.2.3 From 30374d5dd3028cdc522ec7cc6593cb877deb0435 Mon Sep 17 00:00:00 2001 From: Thomas Niederprüm Date: Thu, 22 Jan 2015 00:01:59 +0100 Subject: ASoC: sta32x: use dev_dbg() for debug output. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Niederprüm Signed-off-by: Mark Brown --- sound/soc/codecs/sta32x.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 669b67f8cee3..e696efc6ec25 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -700,10 +700,10 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream, switch (params_width(params)) { case 24: - pr_debug("24bit\n"); + dev_dbg(codec->dev, "24bit\n"); /* fall through */ case 32: - pr_debug("24bit or 32bit\n"); + dev_dbg(codec->dev, "24bit or 32bit\n"); switch (sta32x->format) { case SND_SOC_DAIFMT_I2S: confb |= 0x0; @@ -718,7 +718,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream, break; case 20: - pr_debug("20bit\n"); + dev_dbg(codec->dev, "20bit\n"); switch (sta32x->format) { case SND_SOC_DAIFMT_I2S: confb |= 0x4; @@ -733,7 +733,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream, break; case 18: - pr_debug("18bit\n"); + dev_dbg(codec->dev, "18bit\n"); switch (sta32x->format) { case SND_SOC_DAIFMT_I2S: confb |= 0x8; @@ -748,7 +748,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream, break; case 16: - pr_debug("16bit\n"); + dev_dbg(codec->dev, "16bit\n"); switch (sta32x->format) { case SND_SOC_DAIFMT_I2S: confb |= 0x0; @@ -808,7 +808,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec, int ret; struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec); - pr_debug("level = %d\n", level); + dev_dbg(codec->dev, "level = %d\n", level); switch (level) { case SND_SOC_BIAS_ON: break; -- cgit v1.2.3 From 6fad62599982319a2631daa99906c6b45cacdaff Mon Sep 17 00:00:00 2001 From: Thomas Niederprüm Date: Thu, 22 Jan 2015 00:02:00 +0100 Subject: ASoC: sta32x: minor Kconfig update. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add description for the driver - Add dependency on the I2C module Signed-off-by: Thomas Niederprüm Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 27e1e3b59649..c63ee0e37188 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -580,7 +580,8 @@ config SND_SOC_SSM4567 depends on I2C config SND_SOC_STA32X - tristate + tristate "STA326, STA328 and STA329 speaker amplifier" + depends on I2C select REGMAP_I2C config SND_SOC_STA350 -- cgit v1.2.3 From 3c9390ad0fa642f42e437feae1c75bdd21e8e1bc Mon Sep 17 00:00:00 2001 From: Thomas Niederprüm Date: Thu, 22 Jan 2015 00:02:02 +0100 Subject: ASoC: sta32x: change dai name to be in line with the sta350 driver. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thomas Niederprüm Signed-off-by: Mark Brown --- sound/soc/codecs/sta32x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index e696efc6ec25..3a1343fa109b 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -867,7 +867,7 @@ static const struct snd_soc_dai_ops sta32x_dai_ops = { }; static struct snd_soc_dai_driver sta32x_dai = { - .name = "STA32X", + .name = "sta32x-hifi", .playback = { .stream_name = "Playback", .channels_min = 2, -- cgit v1.2.3 From 9503112d909cbbc2865a28c2586c436254169da8 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Mon, 2 Feb 2015 16:48:05 +0200 Subject: ASoC: tlv320aic3x: Add support for tlv320aic3104 Disables GPIO support and LINE2 input and renames Mic3 input to Mic2, if tlv320aic3104 mode is seleced. Devicetree binding document is updated accordingly. Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/tlv320aic3x.txt | 10 +- sound/soc/codecs/tlv320aic3x.c | 345 +++++++++++++++------ 2 files changed, 253 insertions(+), 102 deletions(-) (limited to 'sound/soc') diff --git a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt index 5e6040c2c2e9..47a213c411ce 100644 --- a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt +++ b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt @@ -9,6 +9,7 @@ Required properties: "ti,tlv320aic33" - TLV320AIC33 "ti,tlv320aic3007" - TLV320AIC3007 "ti,tlv320aic3106" - TLV320AIC3106 + "ti,tlv320aic3104" - TLV320AIC3104 - reg - - I2C slave address @@ -18,6 +19,7 @@ Optional properties: - gpio-reset - gpio pin number used for codec reset - ai3x-gpio-func - - AIC3X_GPIO1 & AIC3X_GPIO2 Functionality + - Not supported on tlv320aic3104 - ai3x-micbias-vg - MicBias Voltage required. 1 - MICBIAS output is powered to 2.0V, 2 - MICBIAS output is powered to 2.5V, @@ -36,7 +38,13 @@ CODEC output pins: * HPLCOM * HPRCOM -CODEC input pins: +CODEC input pins for TLV320AIC3104: + * MIC2L + * MIC2R + * LINE1L + * LINE1R + +CODEC input pins for other compatible codecs: * MIC3L * MIC3R * LINE1L diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index b7ebce054b4e..cb92cdba0324 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -87,6 +87,7 @@ struct aic3x_priv { #define AIC3X_MODEL_3X 0 #define AIC3X_MODEL_33 1 #define AIC3X_MODEL_3007 2 +#define AIC3X_MODEL_3104 3 u16 model; /* Selects the micbias voltage */ @@ -316,52 +317,37 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = { * only for swapped L-to-R and R-to-L routes. See below stereo controls * for direct L-to-L and R-to-R routes. */ - SOC_SINGLE_TLV("Left Line Mixer Line2R Bypass Volume", - LINE2R_2_LLOPM_VOL, 0, 118, 1, output_stage_tlv), SOC_SINGLE_TLV("Left Line Mixer PGAR Bypass Volume", PGAR_2_LLOPM_VOL, 0, 118, 1, output_stage_tlv), SOC_SINGLE_TLV("Left Line Mixer DACR1 Playback Volume", DACR1_2_LLOPM_VOL, 0, 118, 1, output_stage_tlv), - SOC_SINGLE_TLV("Right Line Mixer Line2L Bypass Volume", - LINE2L_2_RLOPM_VOL, 0, 118, 1, output_stage_tlv), SOC_SINGLE_TLV("Right Line Mixer PGAL Bypass Volume", PGAL_2_RLOPM_VOL, 0, 118, 1, output_stage_tlv), SOC_SINGLE_TLV("Right Line Mixer DACL1 Playback Volume", DACL1_2_RLOPM_VOL, 0, 118, 1, output_stage_tlv), - SOC_SINGLE_TLV("Left HP Mixer Line2R Bypass Volume", - LINE2R_2_HPLOUT_VOL, 0, 118, 1, output_stage_tlv), SOC_SINGLE_TLV("Left HP Mixer PGAR Bypass Volume", PGAR_2_HPLOUT_VOL, 0, 118, 1, output_stage_tlv), SOC_SINGLE_TLV("Left HP Mixer DACR1 Playback Volume", DACR1_2_HPLOUT_VOL, 0, 118, 1, output_stage_tlv), - SOC_SINGLE_TLV("Right HP Mixer Line2L Bypass Volume", - LINE2L_2_HPROUT_VOL, 0, 118, 1, output_stage_tlv), SOC_SINGLE_TLV("Right HP Mixer PGAL Bypass Volume", PGAL_2_HPROUT_VOL, 0, 118, 1, output_stage_tlv), SOC_SINGLE_TLV("Right HP Mixer DACL1 Playback Volume", DACL1_2_HPROUT_VOL, 0, 118, 1, output_stage_tlv), - SOC_SINGLE_TLV("Left HPCOM Mixer Line2R Bypass Volume", - LINE2R_2_HPLCOM_VOL, 0, 118, 1, output_stage_tlv), SOC_SINGLE_TLV("Left HPCOM Mixer PGAR Bypass Volume", PGAR_2_HPLCOM_VOL, 0, 118, 1, output_stage_tlv), SOC_SINGLE_TLV("Left HPCOM Mixer DACR1 Playback Volume", DACR1_2_HPLCOM_VOL, 0, 118, 1, output_stage_tlv), - SOC_SINGLE_TLV("Right HPCOM Mixer Line2L Bypass Volume", - LINE2L_2_HPRCOM_VOL, 0, 118, 1, output_stage_tlv), SOC_SINGLE_TLV("Right HPCOM Mixer PGAL Bypass Volume", PGAL_2_HPRCOM_VOL, 0, 118, 1, output_stage_tlv), SOC_SINGLE_TLV("Right HPCOM Mixer DACL1 Playback Volume", DACL1_2_HPRCOM_VOL, 0, 118, 1, output_stage_tlv), /* Stereo output controls for direct L-to-L and R-to-R routes */ - SOC_DOUBLE_R_TLV("Line Line2 Bypass Volume", - LINE2L_2_LLOPM_VOL, LINE2R_2_RLOPM_VOL, - 0, 118, 1, output_stage_tlv), SOC_DOUBLE_R_TLV("Line PGA Bypass Volume", PGAL_2_LLOPM_VOL, PGAR_2_RLOPM_VOL, 0, 118, 1, output_stage_tlv), @@ -369,9 +355,6 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = { DACL1_2_LLOPM_VOL, DACR1_2_RLOPM_VOL, 0, 118, 1, output_stage_tlv), - SOC_DOUBLE_R_TLV("HP Line2 Bypass Volume", - LINE2L_2_HPLOUT_VOL, LINE2R_2_HPROUT_VOL, - 0, 118, 1, output_stage_tlv), SOC_DOUBLE_R_TLV("HP PGA Bypass Volume", PGAL_2_HPLOUT_VOL, PGAR_2_HPROUT_VOL, 0, 118, 1, output_stage_tlv), @@ -379,9 +362,6 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = { DACL1_2_HPLOUT_VOL, DACR1_2_HPROUT_VOL, 0, 118, 1, output_stage_tlv), - SOC_DOUBLE_R_TLV("HPCOM Line2 Bypass Volume", - LINE2L_2_HPLCOM_VOL, LINE2R_2_HPRCOM_VOL, - 0, 118, 1, output_stage_tlv), SOC_DOUBLE_R_TLV("HPCOM PGA Bypass Volume", PGAL_2_HPLCOM_VOL, PGAR_2_HPRCOM_VOL, 0, 118, 1, output_stage_tlv), @@ -424,6 +404,45 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = { SOC_ENUM("Output Driver Ramp-up step", aic3x_rampup_step_enum), }; +/* For other than tlv320aic3104 */ +static const struct snd_kcontrol_new aic3x_extra_snd_controls[] = { + /* + * Output controls that map to output mixer switches. Note these are + * only for swapped L-to-R and R-to-L routes. See below stereo controls + * for direct L-to-L and R-to-R routes. + */ + SOC_SINGLE_TLV("Left Line Mixer Line2R Bypass Volume", + LINE2R_2_LLOPM_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Right Line Mixer Line2L Bypass Volume", + LINE2L_2_RLOPM_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Left HP Mixer Line2R Bypass Volume", + LINE2R_2_HPLOUT_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Right HP Mixer Line2L Bypass Volume", + LINE2L_2_HPROUT_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Left HPCOM Mixer Line2R Bypass Volume", + LINE2R_2_HPLCOM_VOL, 0, 118, 1, output_stage_tlv), + + SOC_SINGLE_TLV("Right HPCOM Mixer Line2L Bypass Volume", + LINE2L_2_HPRCOM_VOL, 0, 118, 1, output_stage_tlv), + + /* Stereo output controls for direct L-to-L and R-to-R routes */ + SOC_DOUBLE_R_TLV("Line Line2 Bypass Volume", + LINE2L_2_LLOPM_VOL, LINE2R_2_RLOPM_VOL, + 0, 118, 1, output_stage_tlv), + + SOC_DOUBLE_R_TLV("HP Line2 Bypass Volume", + LINE2L_2_HPLOUT_VOL, LINE2R_2_HPROUT_VOL, + 0, 118, 1, output_stage_tlv), + + SOC_DOUBLE_R_TLV("HPCOM Line2 Bypass Volume", + LINE2L_2_HPLCOM_VOL, LINE2R_2_HPRCOM_VOL, + 0, 118, 1, output_stage_tlv), +}; + static const struct snd_kcontrol_new aic3x_mono_controls[] = { SOC_DOUBLE_R_TLV("Mono Line2 Bypass Volume", LINE2L_2_MONOLOPM_VOL, LINE2R_2_MONOLOPM_VOL, @@ -464,22 +483,24 @@ SOC_DAPM_ENUM("Route", aic3x_right_hpcom_enum); /* Left Line Mixer */ static const struct snd_kcontrol_new aic3x_left_line_mixer_controls[] = { - SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_LLOPM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_LLOPM_VOL, 7, 1, 0), - SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_LLOPM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_LLOPM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_LLOPM_VOL, 7, 1, 0), + /* Not on tlv320aic3104 */ + SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_LLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_LLOPM_VOL, 7, 1, 0), }; /* Right Line Mixer */ static const struct snd_kcontrol_new aic3x_right_line_mixer_controls[] = { - SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_RLOPM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_RLOPM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_RLOPM_VOL, 7, 1, 0), - SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_RLOPM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_RLOPM_VOL, 7, 1, 0), + /* Not on tlv320aic3104 */ + SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_RLOPM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_RLOPM_VOL, 7, 1, 0), }; /* Mono Mixer */ @@ -494,42 +515,46 @@ static const struct snd_kcontrol_new aic3x_mono_mixer_controls[] = { /* Left HP Mixer */ static const struct snd_kcontrol_new aic3x_left_hp_mixer_controls[] = { - SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPLOUT_VOL, 7, 1, 0), SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_HPLOUT_VOL, 7, 1, 0), SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_HPLOUT_VOL, 7, 1, 0), - SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPLOUT_VOL, 7, 1, 0), SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_HPLOUT_VOL, 7, 1, 0), SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_HPLOUT_VOL, 7, 1, 0), + /* Not on tlv320aic3104 */ + SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPLOUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPLOUT_VOL, 7, 1, 0), }; /* Right HP Mixer */ static const struct snd_kcontrol_new aic3x_right_hp_mixer_controls[] = { - SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPROUT_VOL, 7, 1, 0), SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_HPROUT_VOL, 7, 1, 0), SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_HPROUT_VOL, 7, 1, 0), - SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPROUT_VOL, 7, 1, 0), SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_HPROUT_VOL, 7, 1, 0), SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_HPROUT_VOL, 7, 1, 0), + /* Not on tlv320aic3104 */ + SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPROUT_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPROUT_VOL, 7, 1, 0), }; /* Left HPCOM Mixer */ static const struct snd_kcontrol_new aic3x_left_hpcom_mixer_controls[] = { - SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_HPLCOM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_HPLCOM_VOL, 7, 1, 0), - SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPLCOM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_HPLCOM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_HPLCOM_VOL, 7, 1, 0), + /* Not on tlv320aic3104 */ + SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPLCOM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPLCOM_VOL, 7, 1, 0), }; /* Right HPCOM Mixer */ static const struct snd_kcontrol_new aic3x_right_hpcom_mixer_controls[] = { - SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPRCOM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("PGAL Bypass Switch", PGAL_2_HPRCOM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("DACL1 Switch", DACL1_2_HPRCOM_VOL, 7, 1, 0), - SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("PGAR Bypass Switch", PGAR_2_HPRCOM_VOL, 7, 1, 0), SOC_DAPM_SINGLE("DACR1 Switch", DACR1_2_HPRCOM_VOL, 7, 1, 0), + /* Not on tlv320aic3104 */ + SOC_DAPM_SINGLE("Line2L Bypass Switch", LINE2L_2_HPRCOM_VOL, 7, 1, 0), + SOC_DAPM_SINGLE("Line2R Bypass Switch", LINE2R_2_HPRCOM_VOL, 7, 1, 0), }; /* Left PGA Mixer */ @@ -550,6 +575,22 @@ static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = { SOC_DAPM_SINGLE_AIC3X("Mic3R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1), }; +/* Left PGA Mixer for tlv320aic3104 */ +static const struct snd_kcontrol_new aic3104_left_pga_mixer_controls[] = { + SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_LADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_LADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Mic2L Switch", MIC3LR_2_LADC_CTRL, 4, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Mic2R Switch", MIC3LR_2_LADC_CTRL, 0, 1, 1), +}; + +/* Right PGA Mixer for tlv320aic3104 */ +static const struct snd_kcontrol_new aic3104_right_pga_mixer_controls[] = { + SOC_DAPM_SINGLE_AIC3X("Line1R Switch", LINE1R_2_RADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Line1L Switch", LINE1L_2_RADC_CTRL, 3, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Mic2L Switch", MIC3LR_2_RADC_CTRL, 4, 1, 1), + SOC_DAPM_SINGLE_AIC3X("Mic2R Switch", MIC3LR_2_RADC_CTRL, 0, 1, 1), +}; + /* Left Line1 Mux */ static const struct snd_kcontrol_new aic3x_left_line1l_mux_controls = SOC_DAPM_ENUM("Route", aic3x_line1l_2_l_enum); @@ -593,26 +634,56 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { /* Inputs to Left ADC */ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", LINE1L_2_LADC_CTRL, 2, 0), - SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0, - &aic3x_left_pga_mixer_controls[0], - ARRAY_SIZE(aic3x_left_pga_mixer_controls)), SND_SOC_DAPM_MUX("Left Line1L Mux", SND_SOC_NOPM, 0, 0, &aic3x_left_line1l_mux_controls), SND_SOC_DAPM_MUX("Left Line1R Mux", SND_SOC_NOPM, 0, 0, &aic3x_left_line1r_mux_controls), - SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0, - &aic3x_left_line2_mux_controls), /* Inputs to Right ADC */ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", LINE1R_2_RADC_CTRL, 2, 0), - SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0, - &aic3x_right_pga_mixer_controls[0], - ARRAY_SIZE(aic3x_right_pga_mixer_controls)), SND_SOC_DAPM_MUX("Right Line1L Mux", SND_SOC_NOPM, 0, 0, &aic3x_right_line1l_mux_controls), SND_SOC_DAPM_MUX("Right Line1R Mux", SND_SOC_NOPM, 0, 0, &aic3x_right_line1r_mux_controls), + + /* Mic Bias */ + SND_SOC_DAPM_SUPPLY("Mic Bias", MICBIAS_CTRL, 6, 0, + mic_bias_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_OUTPUT("LLOUT"), + SND_SOC_DAPM_OUTPUT("RLOUT"), + SND_SOC_DAPM_OUTPUT("HPLOUT"), + SND_SOC_DAPM_OUTPUT("HPROUT"), + SND_SOC_DAPM_OUTPUT("HPLCOM"), + SND_SOC_DAPM_OUTPUT("HPRCOM"), + + SND_SOC_DAPM_INPUT("LINE1L"), + SND_SOC_DAPM_INPUT("LINE1R"), + + /* + * Virtual output pin to detection block inside codec. This can be + * used to keep codec bias on if gpio or detection features are needed. + * Force pin on or construct a path with an input jack and mic bias + * widgets. + */ + SND_SOC_DAPM_OUTPUT("Detection"), +}; + +/* For other than tlv320aic3104 */ +static const struct snd_soc_dapm_widget aic3x_extra_dapm_widgets[] = { + /* Inputs to Left ADC */ + SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_pga_mixer_controls[0], + ARRAY_SIZE(aic3x_left_pga_mixer_controls)), + SND_SOC_DAPM_MUX("Left Line2L Mux", SND_SOC_NOPM, 0, 0, + &aic3x_left_line2_mux_controls), + + /* Inputs to Right ADC */ + SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_pga_mixer_controls[0], + ARRAY_SIZE(aic3x_right_pga_mixer_controls)), SND_SOC_DAPM_MUX("Right Line2R Mux", SND_SOC_NOPM, 0, 0, &aic3x_right_line2_mux_controls), @@ -637,11 +708,6 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "DMic Rate 32", AIC3X_ASD_INTF_CTRLA, 0, 3, 3, 0), - /* Mic Bias */ - SND_SOC_DAPM_SUPPLY("Mic Bias", MICBIAS_CTRL, 6, 0, - mic_bias_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - /* Output mixers */ SND_SOC_DAPM_MIXER("Left Line Mixer", SND_SOC_NOPM, 0, 0, &aic3x_left_line_mixer_controls[0], @@ -662,27 +728,46 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { &aic3x_right_hpcom_mixer_controls[0], ARRAY_SIZE(aic3x_right_hpcom_mixer_controls)), - SND_SOC_DAPM_OUTPUT("LLOUT"), - SND_SOC_DAPM_OUTPUT("RLOUT"), - SND_SOC_DAPM_OUTPUT("HPLOUT"), - SND_SOC_DAPM_OUTPUT("HPROUT"), - SND_SOC_DAPM_OUTPUT("HPLCOM"), - SND_SOC_DAPM_OUTPUT("HPRCOM"), - SND_SOC_DAPM_INPUT("MIC3L"), SND_SOC_DAPM_INPUT("MIC3R"), - SND_SOC_DAPM_INPUT("LINE1L"), - SND_SOC_DAPM_INPUT("LINE1R"), SND_SOC_DAPM_INPUT("LINE2L"), SND_SOC_DAPM_INPUT("LINE2R"), +}; - /* - * Virtual output pin to detection block inside codec. This can be - * used to keep codec bias on if gpio or detection features are needed. - * Force pin on or construct a path with an input jack and mic bias - * widgets. - */ - SND_SOC_DAPM_OUTPUT("Detection"), +/* For tlv320aic3104 */ +static const struct snd_soc_dapm_widget aic3104_extra_dapm_widgets[] = { + /* Inputs to Left ADC */ + SND_SOC_DAPM_MIXER("Left PGA Mixer", SND_SOC_NOPM, 0, 0, + &aic3104_left_pga_mixer_controls[0], + ARRAY_SIZE(aic3104_left_pga_mixer_controls)), + + /* Inputs to Right ADC */ + SND_SOC_DAPM_MIXER("Right PGA Mixer", SND_SOC_NOPM, 0, 0, + &aic3104_right_pga_mixer_controls[0], + ARRAY_SIZE(aic3104_right_pga_mixer_controls)), + + /* Output mixers */ + SND_SOC_DAPM_MIXER("Left Line Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_line_mixer_controls[0], + ARRAY_SIZE(aic3x_left_line_mixer_controls) - 2), + SND_SOC_DAPM_MIXER("Right Line Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_line_mixer_controls[0], + ARRAY_SIZE(aic3x_right_line_mixer_controls) - 2), + SND_SOC_DAPM_MIXER("Left HP Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_hp_mixer_controls[0], + ARRAY_SIZE(aic3x_left_hp_mixer_controls) - 2), + SND_SOC_DAPM_MIXER("Right HP Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_hp_mixer_controls[0], + ARRAY_SIZE(aic3x_right_hp_mixer_controls) - 2), + SND_SOC_DAPM_MIXER("Left HPCOM Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_left_hpcom_mixer_controls[0], + ARRAY_SIZE(aic3x_left_hpcom_mixer_controls) - 2), + SND_SOC_DAPM_MIXER("Right HPCOM Mixer", SND_SOC_NOPM, 0, 0, + &aic3x_right_hpcom_mixer_controls[0], + ARRAY_SIZE(aic3x_right_hpcom_mixer_controls) - 2), + + SND_SOC_DAPM_INPUT("MIC2L"), + SND_SOC_DAPM_INPUT("MIC2R"), }; static const struct snd_soc_dapm_widget aic3x_dapm_mono_widgets[] = { @@ -712,17 +797,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"Left Line1R Mux", "single-ended", "LINE1R"}, {"Left Line1R Mux", "differential", "LINE1R"}, - {"Left Line2L Mux", "single-ended", "LINE2L"}, - {"Left Line2L Mux", "differential", "LINE2L"}, - {"Left PGA Mixer", "Line1L Switch", "Left Line1L Mux"}, {"Left PGA Mixer", "Line1R Switch", "Left Line1R Mux"}, - {"Left PGA Mixer", "Line2L Switch", "Left Line2L Mux"}, - {"Left PGA Mixer", "Mic3L Switch", "MIC3L"}, - {"Left PGA Mixer", "Mic3R Switch", "MIC3R"}, {"Left ADC", NULL, "Left PGA Mixer"}, - {"Left ADC", NULL, "GPIO1 dmic modclk"}, /* Right Input */ {"Right Line1R Mux", "single-ended", "LINE1R"}, @@ -730,25 +808,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"Right Line1L Mux", "single-ended", "LINE1L"}, {"Right Line1L Mux", "differential", "LINE1L"}, - {"Right Line2R Mux", "single-ended", "LINE2R"}, - {"Right Line2R Mux", "differential", "LINE2R"}, - {"Right PGA Mixer", "Line1L Switch", "Right Line1L Mux"}, {"Right PGA Mixer", "Line1R Switch", "Right Line1R Mux"}, - {"Right PGA Mixer", "Line2R Switch", "Right Line2R Mux"}, - {"Right PGA Mixer", "Mic3L Switch", "MIC3L"}, - {"Right PGA Mixer", "Mic3R Switch", "MIC3R"}, {"Right ADC", NULL, "Right PGA Mixer"}, - {"Right ADC", NULL, "GPIO1 dmic modclk"}, - - /* - * Logical path between digital mic enable and GPIO1 modulator clock - * output function - */ - {"GPIO1 dmic modclk", NULL, "DMic Rate 128"}, - {"GPIO1 dmic modclk", NULL, "DMic Rate 64"}, - {"GPIO1 dmic modclk", NULL, "DMic Rate 32"}, /* Left DAC Output */ {"Left DAC Mux", "DAC_L1", "Left DAC"}, @@ -761,10 +824,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"Right DAC Mux", "DAC_R3", "Right DAC"}, /* Left Line Output */ - {"Left Line Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, {"Left Line Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, {"Left Line Mixer", "DACL1 Switch", "Left DAC Mux"}, - {"Left Line Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, {"Left Line Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, {"Left Line Mixer", "DACR1 Switch", "Right DAC Mux"}, @@ -773,10 +834,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"LLOUT", NULL, "Left Line Out"}, /* Right Line Output */ - {"Right Line Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, {"Right Line Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, {"Right Line Mixer", "DACL1 Switch", "Left DAC Mux"}, - {"Right Line Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, {"Right Line Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, {"Right Line Mixer", "DACR1 Switch", "Right DAC Mux"}, @@ -785,10 +844,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"RLOUT", NULL, "Right Line Out"}, /* Left HP Output */ - {"Left HP Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, {"Left HP Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, {"Left HP Mixer", "DACL1 Switch", "Left DAC Mux"}, - {"Left HP Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, {"Left HP Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, {"Left HP Mixer", "DACR1 Switch", "Right DAC Mux"}, @@ -797,10 +854,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"HPLOUT", NULL, "Left HP Out"}, /* Right HP Output */ - {"Right HP Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, {"Right HP Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, {"Right HP Mixer", "DACL1 Switch", "Left DAC Mux"}, - {"Right HP Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, {"Right HP Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, {"Right HP Mixer", "DACR1 Switch", "Right DAC Mux"}, @@ -809,10 +864,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"HPROUT", NULL, "Right HP Out"}, /* Left HPCOM Output */ - {"Left HPCOM Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, {"Left HPCOM Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, {"Left HPCOM Mixer", "DACL1 Switch", "Left DAC Mux"}, - {"Left HPCOM Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, {"Left HPCOM Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, {"Left HPCOM Mixer", "DACR1 Switch", "Right DAC Mux"}, @@ -823,10 +876,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"HPLCOM", NULL, "Left HP Com"}, /* Right HPCOM Output */ - {"Right HPCOM Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, {"Right HPCOM Mixer", "PGAL Bypass Switch", "Left PGA Mixer"}, {"Right HPCOM Mixer", "DACL1 Switch", "Left DAC Mux"}, - {"Right HPCOM Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, {"Right HPCOM Mixer", "PGAR Bypass Switch", "Right PGA Mixer"}, {"Right HPCOM Mixer", "DACR1 Switch", "Right DAC Mux"}, @@ -839,6 +890,72 @@ static const struct snd_soc_dapm_route intercon[] = { {"HPRCOM", NULL, "Right HP Com"}, }; +/* For other than tlv320aic3104 */ +static const struct snd_soc_dapm_route intercon_extra[] = { + /* Left Input */ + {"Left Line2L Mux", "single-ended", "LINE2L"}, + {"Left Line2L Mux", "differential", "LINE2L"}, + + {"Left PGA Mixer", "Line2L Switch", "Left Line2L Mux"}, + {"Left PGA Mixer", "Mic3L Switch", "MIC3L"}, + {"Left PGA Mixer", "Mic3R Switch", "MIC3R"}, + + {"Left ADC", NULL, "GPIO1 dmic modclk"}, + + /* Right Input */ + {"Right Line2R Mux", "single-ended", "LINE2R"}, + {"Right Line2R Mux", "differential", "LINE2R"}, + + {"Right PGA Mixer", "Line2R Switch", "Right Line2R Mux"}, + {"Right PGA Mixer", "Mic3L Switch", "MIC3L"}, + {"Right PGA Mixer", "Mic3R Switch", "MIC3R"}, + + {"Right ADC", NULL, "GPIO1 dmic modclk"}, + + /* + * Logical path between digital mic enable and GPIO1 modulator clock + * output function + */ + {"GPIO1 dmic modclk", NULL, "DMic Rate 128"}, + {"GPIO1 dmic modclk", NULL, "DMic Rate 64"}, + {"GPIO1 dmic modclk", NULL, "DMic Rate 32"}, + + /* Left Line Output */ + {"Left Line Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, + {"Left Line Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, + + /* Right Line Output */ + {"Right Line Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, + {"Right Line Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, + + /* Left HP Output */ + {"Left HP Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, + {"Left HP Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, + + /* Right HP Output */ + {"Right HP Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, + {"Right HP Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, + + /* Left HPCOM Output */ + {"Left HPCOM Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, + {"Left HPCOM Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, + + /* Right HPCOM Output */ + {"Right HPCOM Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, + {"Right HPCOM Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, +}; + +/* For other than tlv320aic3104 */ +static const struct snd_soc_dapm_route intercon_extra_3104[] = { + /* Left Input */ + {"Left PGA Mixer", "Mic2L Switch", "MIC2L"}, + {"Left PGA Mixer", "Mic2R Switch", "MIC2R"}, + + /* Right Input */ + {"Right PGA Mixer", "Mic2L Switch", "MIC2L"}, + {"Right PGA Mixer", "Mic2R Switch", "MIC2R"}, +}; + static const struct snd_soc_dapm_route intercon_mono[] = { /* Mono Output */ {"Mono Mixer", "Line2L Bypass Switch", "Left Line2L Mux"}, @@ -867,17 +984,31 @@ static int aic3x_add_widgets(struct snd_soc_codec *codec) switch (aic3x->model) { case AIC3X_MODEL_3X: case AIC3X_MODEL_33: + snd_soc_dapm_new_controls(dapm, aic3x_extra_dapm_widgets, + ARRAY_SIZE(aic3x_extra_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon_extra, + ARRAY_SIZE(intercon_extra)); snd_soc_dapm_new_controls(dapm, aic3x_dapm_mono_widgets, ARRAY_SIZE(aic3x_dapm_mono_widgets)); snd_soc_dapm_add_routes(dapm, intercon_mono, ARRAY_SIZE(intercon_mono)); break; case AIC3X_MODEL_3007: + snd_soc_dapm_new_controls(dapm, aic3x_extra_dapm_widgets, + ARRAY_SIZE(aic3x_extra_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon_extra, + ARRAY_SIZE(intercon_extra)); snd_soc_dapm_new_controls(dapm, aic3007_dapm_widgets, ARRAY_SIZE(aic3007_dapm_widgets)); snd_soc_dapm_add_routes(dapm, intercon_3007, ARRAY_SIZE(intercon_3007)); break; + case AIC3X_MODEL_3104: + snd_soc_dapm_new_controls(dapm, aic3104_extra_dapm_widgets, + ARRAY_SIZE(aic3104_extra_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon_extra_3104, + ARRAY_SIZE(intercon_extra_3104)); + break; } return 0; @@ -1438,23 +1569,33 @@ static int aic3x_probe(struct snd_soc_codec *codec) aic3x_init(codec); if (aic3x->setup) { - /* setup GPIO functions */ - snd_soc_write(codec, AIC3X_GPIO1_REG, - (aic3x->setup->gpio_func[0] & 0xf) << 4); - snd_soc_write(codec, AIC3X_GPIO2_REG, - (aic3x->setup->gpio_func[1] & 0xf) << 4); + if (aic3x->model != AIC3X_MODEL_3104) { + /* setup GPIO functions */ + snd_soc_write(codec, AIC3X_GPIO1_REG, + (aic3x->setup->gpio_func[0] & 0xf) << 4); + snd_soc_write(codec, AIC3X_GPIO2_REG, + (aic3x->setup->gpio_func[1] & 0xf) << 4); + } else { + dev_warn(codec->dev, "GPIO functionality is not supported on tlv320aic3104\n"); + } } switch (aic3x->model) { case AIC3X_MODEL_3X: case AIC3X_MODEL_33: + snd_soc_add_codec_controls(codec, aic3x_extra_snd_controls, + ARRAY_SIZE(aic3x_extra_snd_controls)); snd_soc_add_codec_controls(codec, aic3x_mono_controls, ARRAY_SIZE(aic3x_mono_controls)); break; case AIC3X_MODEL_3007: + snd_soc_add_codec_controls(codec, aic3x_extra_snd_controls, + ARRAY_SIZE(aic3x_extra_snd_controls)); snd_soc_add_codec_controls(codec, &aic3x_classd_amp_gain_ctrl, 1); break; + case AIC3X_MODEL_3104: + break; } /* set mic bias voltage */ @@ -1522,6 +1663,7 @@ static const struct i2c_device_id aic3x_i2c_id[] = { { "tlv320aic33", AIC3X_MODEL_33 }, { "tlv320aic3007", AIC3X_MODEL_3007 }, { "tlv320aic3106", AIC3X_MODEL_3X }, + { "tlv320aic3104", AIC3X_MODEL_3104 }, { } }; MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); @@ -1673,6 +1815,7 @@ static const struct of_device_id tlv320aic3x_of_match[] = { { .compatible = "ti,tlv320aic33" }, { .compatible = "ti,tlv320aic3007" }, { .compatible = "ti,tlv320aic3106" }, + { .compatible = "ti,tlv320aic3104" }, {}, }; MODULE_DEVICE_TABLE(of, tlv320aic3x_of_match); -- cgit v1.2.3 From b8255930e0fbda841890ff6bb7154aa5fd62e143 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Wed, 4 Feb 2015 12:15:46 +0200 Subject: ASoC: tlv320aic3x: Fix bad comment before intercon_extra_3104 definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The intercon_extra_3104 is obviously for tlv320aic3104. Reported-by: Benoît Thébaudeau Signed-off-by: Jyri Sarha Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic3x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index cb92cdba0324..ed35e8f1f04c 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -945,7 +945,7 @@ static const struct snd_soc_dapm_route intercon_extra[] = { {"Right HPCOM Mixer", "Line2R Bypass Switch", "Right Line2R Mux"}, }; -/* For other than tlv320aic3104 */ +/* For tlv320aic3104 */ static const struct snd_soc_dapm_route intercon_extra_3104[] = { /* Left Input */ {"Left PGA Mixer", "Mic2L Switch", "MIC2L"}, -- cgit v1.2.3