1
0
Fork 0

Merge remote-tracking branches 'asoc/topic/tas5086', 'asoc/topic/tegra', 'asoc/topic/tlv320aic31xx', 'asoc/topic/tlv320dac33' and 'asoc/topic/topology' into asoc-next

hifive-unleashed-5.1
Mark Brown 2016-09-29 12:44:45 -07:00
12 changed files with 585 additions and 80 deletions

View File

@ -0,0 +1,42 @@
NVIDIA Tegra audio complex, with SGTL5000 CODEC
Required properties:
- compatible : "nvidia,tegra-audio-sgtl5000"
- 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 SGTL5000's pins (as documented in its binding), and the jacks
on the board:
* Headphone Jack
* Line In Jack
* Mic Jack
- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's
connected to the CODEC.
- nvidia,audio-codec : The phandle of the SGTL5000 audio codec.
Example:
sound {
compatible = "toradex,tegra-audio-sgtl5000-apalis_t30",
"nvidia,tegra-audio-sgtl5000";
nvidia,model = "Toradex Apalis T30";
nvidia,audio-routing =
"Headphone Jack", "HP_OUT",
"LINE_IN", "Line In Jack",
"MIC_IN", "Mic Jack";
nvidia,i2s-controller = <&tegra_i2s2>;
nvidia,audio-codec = <&sgtl5000>;
clocks = <&tegra_car TEGRA30_CLK_PLL_A>,
<&tegra_car TEGRA30_CLK_PLL_A_OUT0>,
<&tegra_car TEGRA30_CLK_EXTERN1>;
clock-names = "pll_a", "pll_a_out0", "mclk";
};

View File

@ -11,6 +11,7 @@ Required properties:
"ti,tlv320aic3110" - TLV320AIC3110 (stereo speaker amp, no MiniDSP)
"ti,tlv320aic3120" - TLV320AIC3120 (mono speaker amp, MiniDSP)
"ti,tlv320aic3111" - TLV320AIC3111 (stereo speaker amp, MiniDSP)
"ti,tlv320dac3100" - TLV320DAC3100 (no ADC, mono speaker amp, no MiniDSP)
- reg - <int> - I2C slave address
- HPVDD-supply, SPRVDD-supply, SPLVDD-supply, AVDD-supply, IOVDD-supply,
@ -37,9 +38,11 @@ CODEC output pins:
* MICBIAS
CODEC input pins:
* MIC1LP
* MIC1RP
* MIC1LM
* MIC1LP, devices with ADC
* MIC1RP, devices with ADC
* MIC1LM, devices with ADC
* AIN1, devices without ADC
* AIN2, devices without ADC
The pins can be used in referring sound node's audio-routing property.

View File

@ -83,7 +83,7 @@
#define SND_SOC_TPLG_NUM_TEXTS 16
/* ABI version */
#define SND_SOC_TPLG_ABI_VERSION 0x4
#define SND_SOC_TPLG_ABI_VERSION 0x5
/* Max size of TLV data */
#define SND_SOC_TPLG_TLV_SIZE 32
@ -105,7 +105,8 @@
#define SND_SOC_TPLG_TYPE_CODEC_LINK 9
#define SND_SOC_TPLG_TYPE_BACKEND_LINK 10
#define SND_SOC_TPLG_TYPE_PDATA 11
#define SND_SOC_TPLG_TYPE_MAX SND_SOC_TPLG_TYPE_PDATA
#define SND_SOC_TPLG_TYPE_BE_DAI 12
#define SND_SOC_TPLG_TYPE_MAX SND_SOC_TPLG_TYPE_BE_DAI
/* vendor block IDs - please add new vendor types to end */
#define SND_SOC_TPLG_TYPE_VENDOR_FW 1000
@ -124,6 +125,11 @@
#define SND_SOC_TPLG_TUPLE_TYPE_WORD 4
#define SND_SOC_TPLG_TUPLE_TYPE_SHORT 5
/* BE DAI flags */
#define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES (1 << 0)
#define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS (1 << 1)
#define SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS (1 << 2)
/*
* Block Header.
* This header precedes all object and object arrays below.
@ -251,6 +257,7 @@ struct snd_soc_tplg_stream_caps {
__le32 period_size_max; /* max period size bytes */
__le32 buffer_size_min; /* min buffer size bytes */
__le32 buffer_size_max; /* max buffer size bytes */
__le32 sig_bits; /* number of bits of content */
} __attribute__((packed));
/*
@ -285,6 +292,8 @@ struct snd_soc_tplg_manifest {
__le32 graph_elems; /* number of graph elements */
__le32 pcm_elems; /* number of PCM elements */
__le32 dai_link_elems; /* number of DAI link elements */
__le32 be_dai_elems; /* number of BE DAI elements */
__le32 reserved[20]; /* reserved for new ABI element types */
struct snd_soc_tplg_private priv;
} __attribute__((packed));
@ -450,4 +459,26 @@ struct snd_soc_tplg_link_config {
struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* supported configs playback and captrure */
__le32 num_streams; /* number of streams */
} __attribute__((packed));
/*
* Describes SW/FW specific features of BE DAI.
*
* File block representation for BE DAI :-
* +-----------------------------------+-----+
* | struct snd_soc_tplg_hdr | 1 |
* +-----------------------------------+-----+
* | struct snd_soc_tplg_be_dai | N |
* +-----------------------------------+-----+
*/
struct snd_soc_tplg_be_dai {
__le32 size; /* in bytes of this structure */
char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* name - used to match */
__le32 dai_id; /* unique ID - used to match */
__le32 playback; /* supports playback mode */
__le32 capture; /* supports capture mode */
struct snd_soc_tplg_stream_caps caps[2]; /* playback and capture for DAI */
__le32 flag_mask; /* bitmask of flags to configure */
__le32 flags; /* SND_SOC_TPLG_DAI_FLGBIT_* */
struct snd_soc_tplg_private priv;
} __attribute__((packed));
#endif

View File

@ -387,7 +387,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
val = index_in_array(tas5086_ratios, ARRAY_SIZE(tas5086_ratios),
priv->mclk / priv->rate);
if (val < 0) {
dev_err(codec->dev, "Inavlid MCLK / Fs ratio\n");
dev_err(codec->dev, "Invalid MCLK / Fs ratio\n");
return -EINVAL;
}

View File

@ -273,10 +273,20 @@ static const DECLARE_TLV_DB_SCALE(sp_vol_tlv, -6350, 50, 0);
/*
* controls to be exported to the user space
*/
static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
static const struct snd_kcontrol_new common31xx_snd_controls[] = {
SOC_DOUBLE_R_S_TLV("DAC Playback Volume", AIC31XX_LDACVOL,
AIC31XX_RDACVOL, 0, -127, 48, 7, 0, dac_vol_tlv),
SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN,
AIC31XX_HPRGAIN, 2, 1, 0),
SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN,
AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv),
SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,
AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),
};
static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
SOC_SINGLE_TLV("ADC Fine Capture Volume", AIC31XX_ADCFGA, 4, 4, 1,
adc_fgain_tlv),
@ -286,14 +296,6 @@ static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
SOC_SINGLE_TLV("Mic PGA Capture Volume", AIC31XX_MICPGA, 0,
119, 0, mic_pga_tlv),
SOC_DOUBLE_R("HP Driver Playback Switch", AIC31XX_HPLGAIN,
AIC31XX_HPRGAIN, 2, 1, 0),
SOC_DOUBLE_R_TLV("HP Driver Playback Volume", AIC31XX_HPLGAIN,
AIC31XX_HPRGAIN, 3, 0x09, 0, hp_drv_tlv),
SOC_DOUBLE_R_TLV("HP Analog Playback Volume", AIC31XX_LANALOGHPL,
AIC31XX_RANALOGHPR, 0, 0x7F, 1, hp_vol_tlv),
};
static const struct snd_kcontrol_new aic311x_snd_controls[] = {
@ -397,17 +399,28 @@ static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w,
return 0;
}
static const struct snd_kcontrol_new left_output_switches[] = {
static const struct snd_kcontrol_new aic31xx_left_output_switches[] = {
SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
SOC_DAPM_SINGLE("From MIC1LP", AIC31XX_DACMIXERROUTE, 5, 1, 0),
SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 4, 1, 0),
};
static const struct snd_kcontrol_new right_output_switches[] = {
static const struct snd_kcontrol_new aic31xx_right_output_switches[] = {
SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
SOC_DAPM_SINGLE("From MIC1RP", AIC31XX_DACMIXERROUTE, 1, 1, 0),
};
static const struct snd_kcontrol_new dac31xx_left_output_switches[] = {
SOC_DAPM_SINGLE("From Left DAC", AIC31XX_DACMIXERROUTE, 6, 1, 0),
SOC_DAPM_SINGLE("From AIN1", AIC31XX_DACMIXERROUTE, 5, 1, 0),
SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 4, 1, 0),
};
static const struct snd_kcontrol_new dac31xx_right_output_switches[] = {
SOC_DAPM_SINGLE("From Right DAC", AIC31XX_DACMIXERROUTE, 2, 1, 0),
SOC_DAPM_SINGLE("From AIN2", AIC31XX_DACMIXERROUTE, 1, 1, 0),
};
static const struct snd_kcontrol_new p_term_mic1lp =
SOC_DAPM_ENUM("MIC1LP P-Terminal", mic1lp_p_enum);
@ -457,7 +470,7 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w,
return 0;
}
static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
static const struct snd_soc_dapm_widget common31xx_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("DAC Left Input",
@ -473,14 +486,7 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
AIC31XX_DACSETUP, 6, 0, aic31xx_dapm_power_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* Output Mixers */
SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
left_output_switches,
ARRAY_SIZE(left_output_switches)),
SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
right_output_switches,
ARRAY_SIZE(right_output_switches)),
/* HP */
SND_SOC_DAPM_SWITCH("HP Left", SND_SOC_NOPM, 0, 0,
&aic31xx_dapm_hpl_switch),
SND_SOC_DAPM_SWITCH("HP Right", SND_SOC_NOPM, 0, 0,
@ -494,10 +500,34 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
NULL, 0, aic31xx_dapm_power_event,
SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
/* ADC */
SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0,
aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_POST_PMD),
/* Mic Bias */
SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
/* Outputs */
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
};
static const struct snd_soc_dapm_widget dac31xx_dapm_widgets[] = {
/* Inputs */
SND_SOC_DAPM_INPUT("AIN1"),
SND_SOC_DAPM_INPUT("AIN2"),
/* Output Mixers */
SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
dac31xx_left_output_switches,
ARRAY_SIZE(dac31xx_left_output_switches)),
SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
dac31xx_right_output_switches,
ARRAY_SIZE(dac31xx_right_output_switches)),
};
static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
/* Inputs */
SND_SOC_DAPM_INPUT("MIC1LP"),
SND_SOC_DAPM_INPUT("MIC1RP"),
SND_SOC_DAPM_INPUT("MIC1LM"),
/* Input Selection to MIC_PGA */
SND_SOC_DAPM_MUX("MIC1LP P-Terminal", SND_SOC_NOPM, 0, 0,
@ -507,24 +537,25 @@ static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
SND_SOC_DAPM_MUX("MIC1LM P-Terminal", SND_SOC_NOPM, 0, 0,
&p_term_mic1lm),
/* ADC */
SND_SOC_DAPM_ADC_E("ADC", "Capture", AIC31XX_ADCSETUP, 7, 0,
aic31xx_dapm_power_event, SND_SOC_DAPM_POST_PMU |
SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MUX("MIC1LM M-Terminal", SND_SOC_NOPM, 0, 0,
&m_term_mic1lm),
/* Enabling & Disabling MIC Gain Ctl */
SND_SOC_DAPM_PGA("MIC_GAIN_CTL", AIC31XX_MICPGA,
7, 1, NULL, 0),
/* Mic Bias */
SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, mic_bias_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
/* Outputs */
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
/* Inputs */
SND_SOC_DAPM_INPUT("MIC1LP"),
SND_SOC_DAPM_INPUT("MIC1RP"),
SND_SOC_DAPM_INPUT("MIC1LM"),
/* Output Mixers */
SND_SOC_DAPM_MIXER("Output Left", SND_SOC_NOPM, 0, 0,
aic31xx_left_output_switches,
ARRAY_SIZE(aic31xx_left_output_switches)),
SND_SOC_DAPM_MIXER("Output Right", SND_SOC_NOPM, 0, 0,
aic31xx_right_output_switches,
ARRAY_SIZE(aic31xx_right_output_switches)),
};
static const struct snd_soc_dapm_widget aic311x_dapm_widgets[] = {
@ -554,7 +585,7 @@ static const struct snd_soc_dapm_widget aic310x_dapm_widgets[] = {
};
static const struct snd_soc_dapm_route
aic31xx_audio_map[] = {
common31xx_audio_map[] = {
/* DAC Input Routing */
{"DAC Left Input", "Left Data", "DAC IN"},
{"DAC Left Input", "Right Data", "DAC IN"},
@ -565,6 +596,31 @@ aic31xx_audio_map[] = {
{"DAC Left", NULL, "DAC Left Input"},
{"DAC Right", NULL, "DAC Right Input"},
/* HPL path */
{"HP Left", "Switch", "Output Left"},
{"HPL Driver", NULL, "HP Left"},
{"HPL", NULL, "HPL Driver"},
/* HPR path */
{"HP Right", "Switch", "Output Right"},
{"HPR Driver", NULL, "HP Right"},
{"HPR", NULL, "HPR Driver"},
};
static const struct snd_soc_dapm_route
dac31xx_audio_map[] = {
/* Left Output */
{"Output Left", "From Left DAC", "DAC Left"},
{"Output Left", "From AIN1", "AIN1"},
{"Output Left", "From AIN2", "AIN2"},
/* Right Output */
{"Output Right", "From Right DAC", "DAC Right"},
{"Output Right", "From AIN2", "AIN2"},
};
static const struct snd_soc_dapm_route
aic31xx_audio_map[] = {
/* Mic input */
{"MIC1LP P-Terminal", "FFR 10 Ohm", "MIC1LP"},
{"MIC1LP P-Terminal", "FFR 20 Ohm", "MIC1LP"},
@ -595,16 +651,6 @@ aic31xx_audio_map[] = {
/* Right Output */
{"Output Right", "From Right DAC", "DAC Right"},
{"Output Right", "From MIC1RP", "MIC1RP"},
/* HPL path */
{"HP Left", "Switch", "Output Left"},
{"HPL Driver", NULL, "HP Left"},
{"HPL", NULL, "HPL Driver"},
/* HPR path */
{"HP Right", "Switch", "Output Right"},
{"HPR Driver", NULL, "HP Right"},
{"HPR", NULL, "HPR Driver"},
};
static const struct snd_soc_dapm_route
@ -633,6 +679,13 @@ static int aic31xx_add_controls(struct snd_soc_codec *codec)
int ret = 0;
struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
if (!(aic31xx->pdata.codec_type & DAC31XX_BIT))
ret = snd_soc_add_codec_controls(
codec, aic31xx_snd_controls,
ARRAY_SIZE(aic31xx_snd_controls));
if (ret)
return ret;
if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT)
ret = snd_soc_add_codec_controls(
codec, aic311x_snd_controls,
@ -651,6 +704,30 @@ static int aic31xx_add_widgets(struct snd_soc_codec *codec)
struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
int ret = 0;
if (aic31xx->pdata.codec_type & DAC31XX_BIT) {
ret = snd_soc_dapm_new_controls(
dapm, dac31xx_dapm_widgets,
ARRAY_SIZE(dac31xx_dapm_widgets));
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(dapm, dac31xx_audio_map,
ARRAY_SIZE(dac31xx_audio_map));
if (ret)
return ret;
} else {
ret = snd_soc_dapm_new_controls(
dapm, aic31xx_dapm_widgets,
ARRAY_SIZE(aic31xx_dapm_widgets));
if (ret)
return ret;
ret = snd_soc_dapm_add_routes(dapm, aic31xx_audio_map,
ARRAY_SIZE(aic31xx_audio_map));
if (ret)
return ret;
}
if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT) {
ret = snd_soc_dapm_new_controls(
dapm, aic311x_dapm_widgets,
@ -1115,12 +1192,12 @@ static struct snd_soc_codec_driver soc_codec_driver_aic31xx = {
.suspend_bias_off = true,
.component_driver = {
.controls = aic31xx_snd_controls,
.num_controls = ARRAY_SIZE(aic31xx_snd_controls),
.dapm_widgets = aic31xx_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(aic31xx_dapm_widgets),
.dapm_routes = aic31xx_audio_map,
.num_dapm_routes = ARRAY_SIZE(aic31xx_audio_map),
.controls = common31xx_snd_controls,
.num_controls = ARRAY_SIZE(common31xx_snd_controls),
.dapm_widgets = common31xx_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(common31xx_dapm_widgets),
.dapm_routes = common31xx_audio_map,
.num_dapm_routes = ARRAY_SIZE(common31xx_audio_map),
},
};
@ -1131,19 +1208,34 @@ static const struct snd_soc_dai_ops aic31xx_dai_ops = {
.digital_mute = aic31xx_dac_mute,
};
static struct snd_soc_dai_driver dac31xx_dai_driver[] = {
{
.name = "tlv32dac31xx-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = AIC31XX_RATES,
.formats = AIC31XX_FORMATS,
},
.ops = &aic31xx_dai_ops,
.symmetric_rates = 1,
}
};
static struct snd_soc_dai_driver aic31xx_dai_driver[] = {
{
.name = "tlv320aic31xx-hifi",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_min = 2,
.channels_max = 2,
.rates = AIC31XX_RATES,
.formats = AIC31XX_FORMATS,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_min = 2,
.channels_max = 2,
.rates = AIC31XX_RATES,
.formats = AIC31XX_FORMATS,
@ -1261,9 +1353,16 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
if (ret)
return ret;
return snd_soc_register_codec(&i2c->dev, &soc_codec_driver_aic31xx,
aic31xx_dai_driver,
ARRAY_SIZE(aic31xx_dai_driver));
if (aic31xx->pdata.codec_type & DAC31XX_BIT)
return snd_soc_register_codec(&i2c->dev,
&soc_codec_driver_aic31xx,
dac31xx_dai_driver,
ARRAY_SIZE(dac31xx_dai_driver));
else
return snd_soc_register_codec(&i2c->dev,
&soc_codec_driver_aic31xx,
aic31xx_dai_driver,
ARRAY_SIZE(aic31xx_dai_driver));
}
static int aic31xx_i2c_remove(struct i2c_client *i2c)
@ -1279,6 +1378,7 @@ static const struct i2c_device_id aic31xx_i2c_id[] = {
{ "tlv320aic3110", AIC3110 },
{ "tlv320aic3120", AIC3120 },
{ "tlv320aic3111", AIC3111 },
{ "tlv320dac3100", DAC3100 },
{ }
};
MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);

View File

@ -24,12 +24,14 @@
#define AIC31XX_STEREO_CLASS_D_BIT 0x1
#define AIC31XX_MINIDSP_BIT 0x2
#define DAC31XX_BIT 0x4
enum aic31xx_type {
AIC3100 = 0,
AIC3110 = AIC31XX_STEREO_CLASS_D_BIT,
AIC3120 = AIC31XX_MINIDSP_BIT,
AIC3111 = (AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT),
DAC3100 = DAC31XX_BIT,
};
struct aic31xx_pdata {

View File

@ -90,7 +90,6 @@ static const char *dac33_supply_names[DAC33_NUM_SUPPLIES] = {
struct tlv320dac33_priv {
struct mutex mutex;
struct workqueue_struct *dac33_wq;
struct work_struct work;
struct snd_soc_codec *codec;
struct regulator_bulk_data supplies[DAC33_NUM_SUPPLIES];
@ -771,7 +770,7 @@ static irqreturn_t dac33_interrupt_handler(int irq, void *dev)
/* Do not schedule the workqueue in Mode7 */
if (dac33->fifo_mode != DAC33_FIFO_MODE7)
queue_work(dac33->dac33_wq, &dac33->work);
schedule_work(&dac33->work);
return IRQ_HANDLED;
}
@ -1127,7 +1126,7 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (dac33->fifo_mode) {
dac33->state = DAC33_PREFILL;
queue_work(dac33->dac33_wq, &dac33->work);
schedule_work(&dac33->work);
}
break;
case SNDRV_PCM_TRIGGER_STOP:
@ -1135,7 +1134,7 @@ static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (dac33->fifo_mode) {
dac33->state = DAC33_FLUSH;
queue_work(dac33->dac33_wq, &dac33->work);
schedule_work(&dac33->work);
}
break;
default:
@ -1410,14 +1409,6 @@ static int dac33_soc_probe(struct snd_soc_codec *codec)
dac33->irq = -1;
}
if (dac33->irq != -1) {
/* Setup work queue */
dac33->dac33_wq =
create_singlethread_workqueue("tlv320dac33");
if (dac33->dac33_wq == NULL) {
free_irq(dac33->irq, codec);
return -ENOMEM;
}
INIT_WORK(&dac33->work, dac33_work);
}
}
@ -1437,7 +1428,7 @@ static int dac33_soc_remove(struct snd_soc_codec *codec)
if (dac33->irq >= 0) {
free_irq(dac33->irq, dac33->codec);
destroy_workqueue(dac33->dac33_wq);
flush_work(&dac33->work);
}
return 0;
}

View File

@ -48,9 +48,10 @@
#define SOC_TPLG_PASS_PCM_DAI 4
#define SOC_TPLG_PASS_GRAPH 5
#define SOC_TPLG_PASS_PINS 6
#define SOC_TPLG_PASS_BE_DAI 7
#define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST
#define SOC_TPLG_PASS_END SOC_TPLG_PASS_PINS
#define SOC_TPLG_PASS_END SOC_TPLG_PASS_BE_DAI
struct soc_tplg {
const struct firmware *fw;
@ -1475,6 +1476,7 @@ widget:
if (widget == NULL) {
dev_err(tplg->dev, "ASoC: failed to create widget %s controls\n",
w->name);
ret = -ENOMEM;
goto hdr_err;
}
@ -1554,6 +1556,25 @@ static void set_stream_info(struct snd_soc_pcm_stream *stream,
stream->rate_min = caps->rate_min;
stream->rate_max = caps->rate_max;
stream->formats = caps->formats;
stream->sig_bits = caps->sig_bits;
}
static void set_dai_flags(struct snd_soc_dai_driver *dai_drv,
unsigned int flag_mask, unsigned int flags)
{
if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES)
dai_drv->symmetric_rates =
flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES ? 1 : 0;
if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS)
dai_drv->symmetric_channels =
flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS ?
1 : 0;
if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS)
dai_drv->symmetric_samplebits =
flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS ?
1 : 0;
}
static int soc_tplg_dai_create(struct soc_tplg *tplg,
@ -1690,8 +1711,96 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
return 0;
}
/* *
* soc_tplg_be_dai_config - Find and configure an existing BE DAI.
* @tplg: topology context
* @be: topology BE DAI configs.
*
* The BE dai should already be registered by the platform driver. The
* platform driver should specify the BE DAI name and ID for matching.
*/
static int soc_tplg_be_dai_config(struct soc_tplg *tplg,
struct snd_soc_tplg_be_dai *be)
{
struct snd_soc_dai_link_component dai_component = {0};
struct snd_soc_dai *dai;
struct snd_soc_dai_driver *dai_drv;
struct snd_soc_pcm_stream *stream;
struct snd_soc_tplg_stream_caps *caps;
int ret;
dai_component.dai_name = be->dai_name;
dai = snd_soc_find_dai(&dai_component);
if (!dai) {
dev_err(tplg->dev, "ASoC: BE DAI %s not registered\n",
be->dai_name);
return -EINVAL;
}
if (be->dai_id != dai->id) {
dev_err(tplg->dev, "ASoC: BE DAI %s id mismatch\n",
be->dai_name);
return -EINVAL;
}
dai_drv = dai->driver;
if (!dai_drv)
return -EINVAL;
if (be->playback) {
stream = &dai_drv->playback;
caps = &be->caps[SND_SOC_TPLG_STREAM_PLAYBACK];
set_stream_info(stream, caps);
}
if (be->capture) {
stream = &dai_drv->capture;
caps = &be->caps[SND_SOC_TPLG_STREAM_CAPTURE];
set_stream_info(stream, caps);
}
if (be->flag_mask)
set_dai_flags(dai_drv, be->flag_mask, be->flags);
/* pass control to component driver for optional further init */
ret = soc_tplg_dai_load(tplg, dai_drv);
if (ret < 0) {
dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n");
return ret;
}
return 0;
}
static int soc_tplg_be_dai_elems_load(struct soc_tplg *tplg,
struct snd_soc_tplg_hdr *hdr)
{
struct snd_soc_tplg_be_dai *be;
int count = hdr->count;
int i;
if (tplg->pass != SOC_TPLG_PASS_BE_DAI)
return 0;
/* config the existing BE DAIs */
for (i = 0; i < count; i++) {
be = (struct snd_soc_tplg_be_dai *)tplg->pos;
if (be->size != sizeof(*be)) {
dev_err(tplg->dev, "ASoC: invalid BE DAI size\n");
return -EINVAL;
}
soc_tplg_be_dai_config(tplg, be);
tplg->pos += (sizeof(*be) + be->priv.size);
}
dev_dbg(tplg->dev, "ASoC: Configure %d BE DAIs\n", count);
return 0;
}
static int soc_tplg_manifest_load(struct soc_tplg *tplg,
struct snd_soc_tplg_hdr *hdr)
struct snd_soc_tplg_hdr *hdr)
{
struct snd_soc_tplg_manifest *manifest;
@ -1793,6 +1902,8 @@ static int soc_tplg_load_header(struct soc_tplg *tplg,
return soc_tplg_dapm_widget_elems_load(tplg, hdr);
case SND_SOC_TPLG_TYPE_PCM:
return soc_tplg_pcm_elems_load(tplg, hdr);
case SND_SOC_TPLG_TYPE_BE_DAI:
return soc_tplg_be_dai_elems_load(tplg, hdr);
case SND_SOC_TPLG_TYPE_MANIFEST:
return soc_tplg_manifest_load(tplg, hdr);
default:

View File

@ -138,3 +138,14 @@ config SND_SOC_TEGRA_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.
config SND_SOC_TEGRA_SGTL5000
tristate "SoC Audio support for Tegra boards using a SGTL5000 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_SGTL5000
help
Say Y or M here if you want to add support for SoC audio on Tegra
boards using the SGTL5000 codec, such as Apalis T30, Apalis TK1 or
Colibri T30.

View File

@ -26,6 +26,7 @@ snd-soc-tegra-wm9712-objs := tegra_wm9712.o
snd-soc-tegra-trimslice-objs := trimslice.o
snd-soc-tegra-alc5632-objs := tegra_alc5632.o
snd-soc-tegra-max98090-objs := tegra_max98090.o
snd-soc-tegra-sgtl5000-objs := tegra_sgtl5000.o
obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o
obj-$(CONFIG_SND_SOC_TEGRA_RT5677) += snd-soc-tegra-rt5677.o
@ -35,3 +36,4 @@ obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o
obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o
obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o
obj-$(CONFIG_SND_SOC_TEGRA_MAX98090) += snd-soc-tegra-max98090.o
obj-$(CONFIG_SND_SOC_TEGRA_SGTL5000) += snd-soc-tegra-sgtl5000.o

View File

@ -1,5 +1,5 @@
/*
* tegra_rt5640.c - Tegra machine ASoC driver for boards using WM8903 codec.
* tegra_rt5640.c - Tegra machine ASoC driver for boards using RT5640 codec.
*
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
*

View File

@ -0,0 +1,212 @@
/*
* tegra_sgtl5000.c - Tegra machine ASoC driver for boards using SGTL5000 codec
*
* Author: Marcel Ziswiler <marcel@ziswiler.com>
*
* 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.
* (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/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "../codecs/sgtl5000.h"
#include "tegra_asoc_utils.h"
#define DRV_NAME "tegra-snd-sgtl5000"
struct tegra_sgtl5000 {
struct tegra_asoc_utils_data util_data;
};
static int tegra_sgtl5000_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_sgtl5000 *machine = snd_soc_card_get_drvdata(card);
int srate, mclk;
int err;
srate = params_rate(params);
switch (srate) {
case 11025:
case 22050:
case 44100:
case 88200:
mclk = 11289600;
break;
default:
mclk = 12288000;
break;
}
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, SGTL5000_SYSCLK, mclk,
SND_SOC_CLOCK_IN);
if (err < 0) {
dev_err(card->dev, "codec_dai clock not set\n");
return err;
}
return 0;
}
static struct snd_soc_ops tegra_sgtl5000_ops = {
.hw_params = tegra_sgtl5000_hw_params,
};
static const struct snd_soc_dapm_widget tegra_sgtl5000_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_LINE("Line In Jack", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
};
static struct snd_soc_dai_link tegra_sgtl5000_dai = {
.name = "sgtl5000",
.stream_name = "HiFi",
.codec_dai_name = "sgtl5000",
.ops = &tegra_sgtl5000_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_sgtl5000 = {
.name = "tegra-sgtl5000",
.owner = THIS_MODULE,
.dai_link = &tegra_sgtl5000_dai,
.num_links = 1,
.dapm_widgets = tegra_sgtl5000_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(tegra_sgtl5000_dapm_widgets),
.fully_routed = true,
};
static int tegra_sgtl5000_driver_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct snd_soc_card *card = &snd_soc_tegra_sgtl5000;
struct tegra_sgtl5000 *machine;
int ret;
machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_sgtl5000),
GFP_KERNEL);
if (!machine) {
dev_err(&pdev->dev, "Can't allocate tegra_sgtl5000 struct\n");
return -ENOMEM;
}
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, machine);
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_sgtl5000_dai.codec_of_node = of_parse_phandle(np,
"nvidia,audio-codec", 0);
if (!tegra_sgtl5000_dai.codec_of_node) {
dev_err(&pdev->dev,
"Property 'nvidia,audio-codec' missing or invalid\n");
ret = -EINVAL;
goto err;
}
tegra_sgtl5000_dai.cpu_of_node = of_parse_phandle(np,
"nvidia,i2s-controller", 0);
if (!tegra_sgtl5000_dai.cpu_of_node) {
dev_err(&pdev->dev,
"Property 'nvidia,i2s-controller' missing/invalid\n");
ret = -EINVAL;
goto err;
}
tegra_sgtl5000_dai.platform_of_node = tegra_sgtl5000_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_sgtl5000_driver_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
struct tegra_sgtl5000 *machine = snd_soc_card_get_drvdata(card);
int ret;
ret = snd_soc_unregister_card(card);
tegra_asoc_utils_fini(&machine->util_data);
return ret;
}
static const struct of_device_id tegra_sgtl5000_of_match[] = {
{ .compatible = "nvidia,tegra-audio-sgtl5000", },
{ /* sentinel */ },
};
static struct platform_driver tegra_sgtl5000_driver = {
.driver = {
.name = DRV_NAME,
.pm = &snd_soc_pm_ops,
.of_match_table = tegra_sgtl5000_of_match,
},
.probe = tegra_sgtl5000_driver_probe,
.remove = tegra_sgtl5000_driver_remove,
};
module_platform_driver(tegra_sgtl5000_driver);
MODULE_AUTHOR("Marcel Ziswiler <marcel@ziswiler.com>");
MODULE_DESCRIPTION("Tegra SGTL5000 machine ASoC driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
MODULE_DEVICE_TABLE(of, tegra_sgtl5000_of_match);