Commit 801a5656 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'asoc/topic/tdm-slot', 'asoc/topic/tegra',...

Merge remote-tracking branches 'asoc/topic/tdm-slot', 'asoc/topic/tegra', 'asoc/topic/tlv320aic3x', 'asoc/topic/ts3a227e' and 'asoc/topic/ts3a277e' into asoc-next
NVIDIA Tegra audio complex, with RT5677 CODEC
Required properties:
- compatible : "nvidia,tegra-audio-rt5677"
- clocks : Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names : Must include the following entries:
- pll_a
- pll_a_out0
- mclk (The Tegra cdev1/extern1 clock, which feeds the CODEC's mclk)
- nvidia,model : The user-visible name of this sound complex.
- nvidia,audio-routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink,
the second being the connection's source. Valid names for sources and
sinks are the RT5677's pins (as documented in its binding), and the jacks
on the board:
* Headphone
* Speaker
* Headset Mic
* Internal Mic 1
* Internal Mic 2
- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's
connected to the CODEC.
- nvidia,audio-codec : The phandle of the RT5677 audio codec. This binding
assumes that AIF1 on the CODEC is connected to Tegra.
Optional properties:
- nvidia,hp-det-gpios : The GPIO that detects headphones are plugged in
- nvidia,hp-en-gpios : The GPIO that enables headphone amplifier
- nvidia,mic-present-gpios: The GPIO that mic jack is plugged in
- nvidia,dmic-clk-en-gpios : The GPIO that gates DMIC clock signal
Example:
sound {
compatible = "nvidia,tegra-audio-rt5677-ryu",
"nvidia,tegra-audio-rt5677";
nvidia,model = "NVIDIA Tegra Ryu";
nvidia,audio-routing =
"Headphone", "LOUT2",
"Headphone", "LOUT1",
"Headset Mic", "MICBIAS1",
"IN1P", "Headset Mic",
"IN1N", "Headset Mic",
"DMIC L1", "Internal Mic 1",
"DMIC R1", "Internal Mic 1",
"DMIC L2", "Internal Mic 2",
"DMIC R2", "Internal Mic 2",
"Speaker", "PDM1L",
"Speaker", "PDM1R";
nvidia,i2s-controller = <&tegra_i2s1>;
nvidia,audio-codec = <&rt5677>;
nvidia,hp-det-gpios = <&gpio TEGRA_GPIO(R, 7) GPIO_ACTIVE_HIGH>;
nvidia,mic-present-gpios = <&gpio TEGRA_GPIO(O, 5) GPIO_ACTIVE_LOW>;
nvidia,hp-en-gpios = <&rt5677 1 GPIO_ACTIVE_HIGH>;
nvidia,dmic-clk-en-gpios = <&rt5677 2 GPIO_ACTIVE_HIGH>;
clocks = <&tegra_car TEGRA124_CLK_PLL_A>,
<&tegra_car TEGRA124_CLK_PLL_A_OUT0>,
<&tegra_car TEGRA124_CLK_EXTERN1>;
clock-names = "pll_a", "pll_a_out0", "mclk";
};
...@@ -9,6 +9,7 @@ Required properties: ...@@ -9,6 +9,7 @@ Required properties:
"ti,tlv320aic33" - TLV320AIC33 "ti,tlv320aic33" - TLV320AIC33
"ti,tlv320aic3007" - TLV320AIC3007 "ti,tlv320aic3007" - TLV320AIC3007
"ti,tlv320aic3106" - TLV320AIC3106 "ti,tlv320aic3106" - TLV320AIC3106
"ti,tlv320aic3104" - TLV320AIC3104
- reg - <int> - I2C slave address - reg - <int> - I2C slave address
...@@ -18,6 +19,7 @@ Optional properties: ...@@ -18,6 +19,7 @@ Optional properties:
- gpio-reset - gpio pin number used for codec reset - gpio-reset - gpio pin number used for codec reset
- ai3x-gpio-func - <array of 2 int> - AIC3X_GPIO1 & AIC3X_GPIO2 Functionality - ai3x-gpio-func - <array of 2 int> - AIC3X_GPIO1 & AIC3X_GPIO2 Functionality
- Not supported on tlv320aic3104
- ai3x-micbias-vg - MicBias Voltage required. - ai3x-micbias-vg - MicBias Voltage required.
1 - MICBIAS output is powered to 2.0V, 1 - MICBIAS output is powered to 2.0V,
2 - MICBIAS output is powered to 2.5V, 2 - MICBIAS output is powered to 2.5V,
...@@ -36,7 +38,13 @@ CODEC output pins: ...@@ -36,7 +38,13 @@ CODEC output pins:
* HPLCOM * HPLCOM
* HPRCOM * HPRCOM
CODEC input pins: CODEC input pins for TLV320AIC3104:
* MIC2L
* MIC2R
* LINE1L
* LINE1R
CODEC input pins for other compatible codecs:
* MIC3L * MIC3L
* MIC3R * MIC3R
* LINE1L * LINE1L
......
...@@ -13,6 +13,11 @@ Required properties: ...@@ -13,6 +13,11 @@ Required properties:
- interrupt-parent: The parent interrupt controller - interrupt-parent: The parent interrupt controller
- interrupts: Interrupt number for /INT pin from the 227e - interrupts: Interrupt number for /INT pin from the 227e
Optional properies:
- ti,micbias: Intended MICBIAS voltage (datasheet section 9.6.7).
Select 0/1/2/3/4/5/6/7 to specify MACBIAS voltage
2.1V/2.2V/2.3V/2.4V/2.5V/2.6V/2.7V/2.8V
Default value is "1" (2.2V).
Examples: Examples:
......
...@@ -328,16 +328,16 @@ static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai, ...@@ -328,16 +328,16 @@ static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai,
} }
switch (rx_mask) { switch (rx_mask) {
case 0xfffffffc: case 0x03:
val |= SSI_NETWORK_DAC_RXSLOT_0_1; val |= SSI_NETWORK_DAC_RXSLOT_0_1;
break; break;
case 0xfffffff3: case 0x0c:
val |= SSI_NETWORK_DAC_RXSLOT_2_3; val |= SSI_NETWORK_DAC_RXSLOT_2_3;
break; break;
case 0xffffffcf: case 0x30:
val |= SSI_NETWORK_DAC_RXSLOT_4_5; val |= SSI_NETWORK_DAC_RXSLOT_4_5;
break; break;
case 0xffffff3f: case 0xc0:
val |= SSI_NETWORK_DAC_RXSLOT_6_7; val |= SSI_NETWORK_DAC_RXSLOT_6_7;
break; break;
default: default:
...@@ -360,7 +360,7 @@ static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai, ...@@ -360,7 +360,7 @@ static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai,
if (slots != 4) if (slots != 4)
return -EINVAL; return -EINVAL;
if (tx_mask != 0xfffffffc) if (tx_mask != 0x3)
return -EINVAL; return -EINVAL;
val |= (0x00 << 2); /* primary timeslot RX/TX(?) is 0 */ val |= (0x00 << 2); /* primary timeslot RX/TX(?) is 0 */
......
This diff is collapsed.
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
#include <sound/jack.h> #include <sound/jack.h>
#include <sound/soc.h> #include <sound/soc.h>
#include "ts3a227e.h"
struct ts3a227e { struct ts3a227e {
struct regmap *regmap; struct regmap *regmap;
struct snd_soc_jack *jack; struct snd_soc_jack *jack;
...@@ -79,6 +81,10 @@ static const int ts3a227e_buttons[] = { ...@@ -79,6 +81,10 @@ static const int ts3a227e_buttons[] = {
/* TS3A227E_REG_SETTING_2 0x05 */ /* TS3A227E_REG_SETTING_2 0x05 */
#define KP_ENABLE 0x04 #define KP_ENABLE 0x04
/* TS3A227E_REG_SETTING_3 0x06 */
#define MICBIAS_SETTING_SFT (3)
#define MICBIAS_SETTING_MASK (0x7 << MICBIAS_SETTING_SFT)
/* TS3A227E_REG_ACCESSORY_STATUS 0x0b */ /* TS3A227E_REG_ACCESSORY_STATUS 0x0b */
#define TYPE_3_POLE 0x01 #define TYPE_3_POLE 0x01
#define TYPE_4_POLE_OMTP 0x02 #define TYPE_4_POLE_OMTP 0x02
...@@ -221,9 +227,9 @@ int ts3a227e_enable_jack_detect(struct snd_soc_component *component, ...@@ -221,9 +227,9 @@ int ts3a227e_enable_jack_detect(struct snd_soc_component *component,
struct ts3a227e *ts3a227e = snd_soc_component_get_drvdata(component); struct ts3a227e *ts3a227e = snd_soc_component_get_drvdata(component);
snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA); snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA);
snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP); snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
ts3a227e->jack = jack; ts3a227e->jack = jack;
ts3a227e_jack_report(ts3a227e); ts3a227e_jack_report(ts3a227e);
...@@ -248,6 +254,21 @@ static const struct regmap_config ts3a227e_regmap_config = { ...@@ -248,6 +254,21 @@ static const struct regmap_config ts3a227e_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(ts3a227e_reg_defaults), .num_reg_defaults = ARRAY_SIZE(ts3a227e_reg_defaults),
}; };
static int ts3a227e_parse_dt(struct ts3a227e *ts3a227e, struct device_node *np)
{
u32 micbias;
int err;
err = of_property_read_u32(np, "ti,micbias", &micbias);
if (!err) {
regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_SETTING_3,
MICBIAS_SETTING_MASK,
(micbias & 0x07) << MICBIAS_SETTING_SFT);
}
return 0;
}
static int ts3a227e_i2c_probe(struct i2c_client *i2c, static int ts3a227e_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
...@@ -266,6 +287,14 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c, ...@@ -266,6 +287,14 @@ static int ts3a227e_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(ts3a227e->regmap)) if (IS_ERR(ts3a227e->regmap))
return PTR_ERR(ts3a227e->regmap); return PTR_ERR(ts3a227e->regmap);
if (dev->of_node) {
ret = ts3a227e_parse_dt(ts3a227e, dev->of_node);
if (ret) {
dev_err(dev, "Failed to parse device tree: %d\n", ret);
return ret;
}
}
ret = devm_request_threaded_irq(dev, i2c->irq, NULL, ts3a227e_interrupt, ret = devm_request_threaded_irq(dev, i2c->irq, NULL, ts3a227e_interrupt,
IRQF_TRIGGER_LOW | IRQF_ONESHOT, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"TS3A227E", ts3a227e); "TS3A227E", ts3a227e);
......
...@@ -50,7 +50,7 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream, ...@@ -50,7 +50,7 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream,
return ret; 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, ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
SND_SOC_CLOCK_IN); SND_SOC_CLOCK_IN);
......
...@@ -992,8 +992,8 @@ static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, ...@@ -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, regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN,
CCSR_SSI_SCR_SSIEN); CCSR_SSI_SCR_SSIEN);
regmap_write(regs, CCSR_SSI_STMSK, tx_mask); regmap_write(regs, CCSR_SSI_STMSK, ~tx_mask);
regmap_write(regs, CCSR_SSI_SRMSK, rx_mask); regmap_write(regs, CCSR_SSI_SRMSK, ~rx_mask);
regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN, val); regmap_update_bits(regs, CCSR_SSI_SCR, CCSR_SSI_SCR_SSIEN, val);
......
...@@ -86,33 +86,6 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np, ...@@ -86,33 +86,6 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np,
} }
EXPORT_SYMBOL(fsl_asoc_get_dma_channel); 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 0 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 <timur@freescale.com>"); MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
MODULE_DESCRIPTION("Freescale ASoC utility code"); MODULE_DESCRIPTION("Freescale ASoC utility code");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
...@@ -22,7 +22,4 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np, const char *name, ...@@ -22,7 +22,4 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np, const char *name,
struct snd_soc_dai_link *dai, struct snd_soc_dai_link *dai,
unsigned int *dma_channel_id, unsigned int *dma_channel_id,
unsigned int *dma_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 */ #endif /* _FSL_UTILS_H */
...@@ -37,8 +37,7 @@ static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream, ...@@ -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; struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret; int ret;
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xfffffffc, 0xfffffffc, ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 4, 16);
4, 16);
if (ret) if (ret)
return ret; return ret;
...@@ -46,7 +45,7 @@ static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream, ...@@ -46,7 +45,7 @@ static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream,
if (ret) if (ret)
return 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) if (ret)
return ret; return ret;
......
...@@ -74,8 +74,8 @@ static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, ...@@ -74,8 +74,8 @@ static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
sccr |= SSI_STCCR_DC(slots - 1); sccr |= SSI_STCCR_DC(slots - 1);
writel(sccr, ssi->base + SSI_SRCCR); writel(sccr, ssi->base + SSI_SRCCR);
writel(tx_mask, ssi->base + SSI_STMSK); writel(~tx_mask, ssi->base + SSI_STMSK);
writel(rx_mask, ssi->base + SSI_SRMSK); writel(~rx_mask, ssi->base + SSI_SRMSK);
return 0; return 0;
} }
...@@ -340,7 +340,6 @@ static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { ...@@ -340,7 +340,6 @@ static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = {
.set_fmt = imx_ssi_set_dai_fmt, .set_fmt = imx_ssi_set_dai_fmt,
.set_clkdiv = imx_ssi_set_dai_clkdiv, .set_clkdiv = imx_ssi_set_dai_clkdiv,
.set_sysclk = imx_ssi_set_dai_sysclk, .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, .set_tdm_slot = imx_ssi_set_dai_tdm_slot,
.trigger = imx_ssi_trigger, .trigger = imx_ssi_trigger,
}; };
......
...@@ -106,10 +106,10 @@ static int wm1133_ev1_hw_params(struct snd_pcm_substream *substream, ...@@ -106,10 +106,10 @@ static int wm1133_ev1_hw_params(struct snd_pcm_substream *substream,
/* TODO: The SSI driver should figure this out for us */ /* TODO: The SSI driver should figure this out for us */
switch (channels) { switch (channels) {
case 2: 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; break;
case 1: 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; break;
default: default:
return -EINVAL; return -EINVAL;
......
...@@ -2140,15 +2140,27 @@ static int snd_soc_xlate_tdm_slot_mask(unsigned int slots, ...@@ -2140,15 +2140,27 @@ static int snd_soc_xlate_tdm_slot_mask(unsigned int slots,
} }
/** /**
* snd_soc_dai_set_tdm_slot - configure DAI TDM. * snd_soc_dai_set_tdm_slot() - Configures a DAI for TDM operation
* @dai: DAI * @dai: The DAI to configure
* @tx_mask: bitmask representing active TX slots. * @tx_mask: bitmask representing active TX slots.
* @rx_mask: bitmask representing active RX slots. * @rx_mask: bitmask representing active RX slots.
* @slots: Number of slots in use. * @slots: Number of slots in use.
* @slot_width: Width in bits for each slot. * @slot_width: Width in bits for each slot.
* *
* Configures a DAI for TDM operation. Both mask and slots are codec and DAI * This function configures the specified DAI for TDM operation. @slot contains
* specific. * 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, 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) unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
......
...@@ -128,3 +128,13 @@ config SND_SOC_TEGRA_MAX98090 ...@@ -128,3 +128,13 @@ config SND_SOC_TEGRA_MAX98090
help help
Say Y or M here if you want to add support for SoC audio on Tegra Say Y or M here if you want to add support for SoC audio on Tegra
boards using the MAX98090 codec, such as Venice2. 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.
...@@ -19,6 +19,7 @@ obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o ...@@ -19,6 +19,7 @@ obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o
# Tegra machine Support # Tegra machine Support
snd-soc-tegra-rt5640-objs := tegra_rt5640.o 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-wm8753-objs := tegra_wm8753.o
snd-soc-tegra-wm8903-objs := tegra_wm8903.o snd-soc-tegra-wm8903-objs := tegra_wm8903.o
snd-soc-tegra-wm9712-objs := tegra_wm9712.o snd-soc-tegra-wm9712-objs := tegra_wm9712.o
...@@ -27,6 +28,7 @@ snd-soc-tegra-alc5632-objs := tegra_alc5632.o ...@@ -27,6 +28,7 @@ snd-soc-tegra-alc5632-objs := tegra_alc5632.o
snd-soc-tegra-max98090-objs := tegra_max98090.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_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_WM8753) += snd-soc-tegra-wm8753.o
obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o
......
/*
* 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 <http://www.gnu.org/licenses/>.
*
* Based on code copyright/by:
*
* Copyright (C) 2010-2012 - NVIDIA, Inc.
* Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.lauchpad.net>
* (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
* Copyright 2007 Wolfson Microelectronics PLC.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#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 <anatol@google.com>");
MODULE_DESCRIPTION("Tegra+RT5677 machine ASoC driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
MODULE_DEVICE_TABLE(of, tegra_rt5677_of_match);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment