Commit 1bf9f29a authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'asoc/topic/simple', 'asoc/topic/sta32x',...

Merge remote-tracking branches 'asoc/topic/simple', 'asoc/topic/sta32x', 'asoc/topic/tdm-slot', 'asoc/topic/tegra' and 'asoc/topic/tlv320aic3x' 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";
};
......@@ -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:
......
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.
};
......@@ -9,6 +9,7 @@ Required properties:
"ti,tlv320aic33" - TLV320AIC33
"ti,tlv320aic3007" - TLV320AIC3007
"ti,tlv320aic3106" - TLV320AIC3106
"ti,tlv320aic3104" - TLV320AIC3104
- reg - <int> - I2C slave address
......@@ -18,6 +19,7 @@ Optional properties:
- gpio-reset - gpio pin number used for codec reset
- ai3x-gpio-func - <array of 2 int> - 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
......
......@@ -20,6 +20,7 @@ struct asoc_simple_dai {
unsigned int sysclk;
int slots;
int slot_width;
struct clk *clk;
};
struct asoc_simple_card_info {
......
......@@ -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 */
......@@ -584,7 +584,9 @@ 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
tristate "STA350 speaker amplifier"
......
......@@ -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 */
......
This diff is collapsed.
This diff is collapsed.
......@@ -50,7 +50,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);
......
......@@ -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);
......
......@@ -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 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_DESCRIPTION("Freescale ASoC utility code");
MODULE_LICENSE("GPL v2");
......@@ -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 */
......@@ -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;
......@@ -46,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;
......
......@@ -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;
}
......@@ -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,
};
......
......@@ -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 */
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;
......
......@@ -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 {
......
......@@ -2140,15 +2140,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)
......
......@@ -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.
......@@ -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
......
/*
* 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