summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorXinyu Chen <xinyu.chen@freescale.com>2012-04-09 13:34:16 +0800
committerXinyu Chen <xinyu.chen@freescale.com>2012-04-09 13:34:16 +0800
commit52c5341f1302a0b328e7dd5890c12729406256fc (patch)
tree687adeb27a395f512c2a0fa373bd173c437f9321 /sound
parentbee5f57f1e3e5174aa5390a330052e772afdc453 (diff)
parent3ec8c998827be5729ff98a0d7c097f7ad9ddbc6e (diff)
Merge remote branch 'fsl-linux-sdk/imx_3.0.15' into imx_3.0.15_android
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/wm8962.c404
-rw-r--r--sound/soc/imx/imx-cs42888.c131
-rw-r--r--sound/soc/imx/imx-esai.h41
-rw-r--r--sound/soc/imx/imx-pcm-dma-mx2.c211
-rw-r--r--sound/soc/imx/imx-pcm.h70
-rw-r--r--sound/soc/imx/imx-wm8962.c254
6 files changed, 926 insertions, 185 deletions
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 7b14746d9336..b8be06b3344a 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -63,6 +63,8 @@ struct wm8962_priv {
int fll_fref;
int fll_fout;
+ u16 dsp2_ena;
+
struct delayed_work mic_work;
struct snd_soc_jack *jack;
@@ -78,6 +80,8 @@ struct wm8962_priv {
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip;
#endif
+
+ int irq;
};
/* We can't use the same notifier block for more than one supply and
@@ -835,7 +839,7 @@ static const struct wm8962_reg_access {
[40] = { 0x00FF, 0x01FF, 0x0000 }, /* R40 - SPKOUTL volume */
[41] = { 0x00FF, 0x01FF, 0x0000 }, /* R41 - SPKOUTR volume */
- [47] = { 0x000F, 0x0000, 0x0000 }, /* R47 - Thermal Shutdown Status */
+ [47] = { 0x000F, 0x0000, 0xFFFF }, /* R47 - Thermal Shutdown Status*/
[48] = { 0x7EC7, 0x7E07, 0xFFFF }, /* R48 - Additional Control (4) */
[49] = { 0x00D3, 0x00D7, 0xFFFF }, /* R49 - Class D Control 1 */
[51] = { 0x0047, 0x0047, 0x0000 }, /* R51 - Class D Control 2 */
@@ -963,7 +967,7 @@ static const struct wm8962_reg_access {
[584] = { 0x002D, 0x002D, 0x0000 }, /* R584 - IRQ Debounce */
[586] = { 0xC000, 0xC000, 0x0000 }, /* R586 - MICINT Source Pol */
[768] = { 0x0001, 0x0001, 0x0000 }, /* R768 - DSP2 Power Management */
- [1037] = { 0x0000, 0x003F, 0x0000 }, /* R1037 - DSP2_ExecControl */
+ [1037] = { 0x0000, 0x003F, 0xFFFF }, /* R1037 - DSP2_ExecControl */
[4096] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4096 - Write Sequencer 0 */
[4097] = { 0x00FF, 0x00FF, 0x0000 }, /* R4097 - Write Sequencer 1 */
[4098] = { 0x070F, 0x070F, 0x0000 }, /* R4098 - Write Sequencer 2 */
@@ -1969,7 +1973,7 @@ static int wm8962_reset(struct snd_soc_codec *codec)
static const DECLARE_TLV_DB_SCALE(inpga_tlv, -2325, 75, 0);
static const DECLARE_TLV_DB_SCALE(mixin_tlv, -1500, 300, 0);
static const unsigned int mixinpga_tlv[] = {
- TLV_DB_RANGE_HEAD(7),
+ TLV_DB_RANGE_HEAD(5),
0, 1, TLV_DB_SCALE_ITEM(0, 600, 0),
2, 2, TLV_DB_SCALE_ITEM(1300, 1300, 0),
3, 4, TLV_DB_SCALE_ITEM(1800, 200, 0),
@@ -1984,10 +1988,127 @@ static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
static const DECLARE_TLV_DB_SCALE(hp_tlv, -700, 100, 0);
static const unsigned int classd_tlv[] = {
- TLV_DB_RANGE_HEAD(7),
+ TLV_DB_RANGE_HEAD(2),
0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
};
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+
+static int wm8962_dsp2_write_config(struct snd_soc_codec *codec)
+{
+ return 0;
+}
+
+static int wm8962_dsp2_set_enable(struct snd_soc_codec *codec, u16 val)
+{
+ u16 adcl = snd_soc_read(codec, WM8962_LEFT_ADC_VOLUME);
+ u16 adcr = snd_soc_read(codec, WM8962_RIGHT_ADC_VOLUME);
+ u16 dac = snd_soc_read(codec, WM8962_ADC_DAC_CONTROL_1);
+
+ /* Mute the ADCs and DACs */
+ snd_soc_write(codec, WM8962_LEFT_ADC_VOLUME, 0);
+ snd_soc_write(codec, WM8962_RIGHT_ADC_VOLUME, WM8962_ADC_VU);
+ snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1,
+ WM8962_DAC_MUTE, WM8962_DAC_MUTE);
+
+ snd_soc_write(codec, WM8962_SOUNDSTAGE_ENABLES_0, val);
+
+ /* Restore the ADCs and DACs */
+ snd_soc_write(codec, WM8962_LEFT_ADC_VOLUME, adcl);
+ snd_soc_write(codec, WM8962_RIGHT_ADC_VOLUME, adcr);
+ snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1,
+ WM8962_DAC_MUTE, dac);
+
+ return 0;
+}
+
+static int wm8962_dsp2_start(struct snd_soc_codec *codec)
+{
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+
+ wm8962_dsp2_write_config(codec);
+
+ snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_RUNR);
+
+ wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena);
+
+ return 0;
+}
+
+static int wm8962_dsp2_stop(struct snd_soc_codec *codec)
+{
+ wm8962_dsp2_set_enable(codec, 0);
+
+ snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_STOP);
+
+ return 0;
+}
+
+#define WM8962_DSP2_ENABLE(xname, xshift) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = wm8962_dsp2_ena_info, \
+ .get = wm8962_dsp2_ena_get, .put = wm8962_dsp2_ena_put, \
+ .private_value = xshift }
+
+static int wm8962_dsp2_ena_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+
+ return 0;
+}
+
+static int wm8962_dsp2_ena_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int shift = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = !!(wm8962->dsp2_ena & 1 << shift);
+
+ return 0;
+}
+
+static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int shift = kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ int old = wm8962->dsp2_ena;
+ int ret = 0;
+ int dsp2_running = snd_soc_read(codec, WM8962_DSP2_POWER_MANAGEMENT) &
+ WM8962_DSP2_ENA;
+
+ mutex_lock(&codec->mutex);
+
+ if (ucontrol->value.integer.value[0])
+ wm8962->dsp2_ena |= 1 << shift;
+ else
+ wm8962->dsp2_ena &= ~(1 << shift);
+
+ if (wm8962->dsp2_ena == old)
+ goto out;
+
+ ret = 1;
+
+ if (dsp2_running) {
+ if (wm8962->dsp2_ena)
+ wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena);
+ else
+ wm8962_dsp2_stop(codec);
+ }
+
+out:
+ mutex_unlock(&codec->mutex);
+
+ return ret;
+}
/* The VU bits for the headphones are in a different register to the mute
* bits and only take effect on the PGA if it is actually powered.
@@ -2054,6 +2175,14 @@ static const char *cap_hpf_mode_text[] = {
static const struct soc_enum cap_hpf_mode =
SOC_ENUM_SINGLE(WM8962_ADC_DAC_CONTROL_2, 10, 2, cap_hpf_mode_text);
+
+static const char *cap_lhpf_mode_text[] = {
+ "LPF", "HPF"
+};
+
+static const struct soc_enum cap_lhpf_mode =
+ SOC_ENUM_SINGLE(WM8962_LHPF1, 1, 2, cap_lhpf_mode_text);
+
static const struct snd_kcontrol_new wm8962_snd_controls[] = {
SOC_DOUBLE("Input Mixer Switch", WM8962_INPUT_MIXER_CONTROL_1, 3, 2, 1, 1),
@@ -2082,6 +2211,8 @@ SOC_DOUBLE_R("Capture ZC Switch", WM8962_LEFT_INPUT_VOLUME,
SOC_SINGLE("Capture HPF Switch", WM8962_ADC_DAC_CONTROL_1, 0, 1, 1),
SOC_ENUM("Capture HPF Mode", cap_hpf_mode),
SOC_SINGLE("Capture HPF Cutoff", WM8962_ADC_DAC_CONTROL_2, 7, 7, 0),
+SOC_SINGLE("Capture LHPF Switch", WM8962_LHPF1, 0, 1, 0),
+SOC_ENUM("Capture LHPF Mode", cap_lhpf_mode),
SOC_DOUBLE_R_TLV("Sidetone Volume", WM8962_DAC_DSP_MIXING_1,
WM8962_DAC_DSP_MIXING_2, 4, 12, 0, st_tlv),
@@ -2127,6 +2258,23 @@ SOC_SINGLE_TLV("HPMIXR MIXINR Volume", WM8962_HEADPHONE_MIXER_4,
SOC_SINGLE_TLV("Speaker Boost Volume", WM8962_CLASS_D_CONTROL_2, 0, 7, 0,
classd_tlv),
+
+SOC_SINGLE("EQ Switch", WM8962_EQ1, WM8962_EQ_ENA_SHIFT, 1, 0),
+SOC_DOUBLE_R_TLV("EQ1 Volume", WM8962_EQ2, WM8962_EQ22,
+ WM8962_EQL_B1_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ2 Volume", WM8962_EQ2, WM8962_EQ22,
+ WM8962_EQL_B2_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ3 Volume", WM8962_EQ2, WM8962_EQ22,
+ WM8962_EQL_B3_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23,
+ WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv),
+SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23,
+ WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv),
+
+WM8962_DSP2_ENABLE("VSS Switch", WM8962_VSS_ENA_SHIFT),
+WM8962_DSP2_ENABLE("HPF1 Switch", WM8962_HPF1_ENA_SHIFT),
+WM8962_DSP2_ENABLE("HPF2 Switch", WM8962_HPF2_ENA_SHIFT),
+WM8962_DSP2_ENABLE("HD Bass Switch", WM8962_HDBASS_ENA_SHIFT),
};
static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = {
@@ -2192,6 +2340,8 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+ unsigned long timeout;
int src;
int fll;
@@ -2211,9 +2361,20 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- if (fll)
+ if (fll) {
+ try_wait_for_completion(&wm8962->fll_lock);
+
snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
WM8962_FLL_ENA, WM8962_FLL_ENA);
+
+ timeout = msecs_to_jiffies(5);
+ timeout = wait_for_completion_timeout(&wm8962->fll_lock,
+ timeout);
+
+ if (wm8962->irq && timeout == 0)
+ dev_err(codec->dev,
+ "Timed out starting FLL\n");
+ }
break;
case SND_SOC_DAPM_POST_PMD:
@@ -2373,6 +2534,31 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
}
}
+static int dsp2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (wm8962->dsp2_ena)
+ wm8962_dsp2_start(codec);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ if (wm8962->dsp2_ena)
+ wm8962_dsp2_stop(codec);
+ break;
+
+ default:
+ BUG();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const char *st_text[] = { "None", "Right", "Left" };
static const struct soc_enum str_enum =
@@ -2487,7 +2673,7 @@ SND_SOC_DAPM_INPUT("IN4R"),
SND_SOC_DAPM_INPUT("Beep"),
SND_SOC_DAPM_INPUT("DMICDAT"),
-SND_SOC_DAPM_MICBIAS("MICBIAS", WM8962_PWR_MGMT_1, 1, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS", WM8962_PWR_MGMT_1, 1, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Class G", WM8962_CHARGE_PUMP_B, 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("SYSCLK", WM8962_CLOCKING2, 5, 0, sysclk_event,
@@ -2495,6 +2681,11 @@ SND_SOC_DAPM_SUPPLY("SYSCLK", WM8962_CLOCKING2, 5, 0, sysclk_event,
SND_SOC_DAPM_SUPPLY("Charge Pump", WM8962_CHARGE_PUMP_1, 0, 0, cp_event,
SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("TOCLK", WM8962_ADDITIONAL_CONTROL_1, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("TEMP_HP", WM8962_ADDITIONAL_CONTROL_4, 2, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("TEMP_SPK", WM8962_ADDITIONAL_CONTROL_4, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("DSP2", 1, WM8962_DSP2_POWER_MANAGEMENT,
+ WM8962_DSP2_ENA_SHIFT, 0, dsp2_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_MIXER("INPGAL", WM8962_LEFT_INPUT_PGA_CONTROL, 4, 0,
inpgal, ARRAY_SIZE(inpgal)),
@@ -2505,7 +2696,7 @@ SND_SOC_DAPM_MIXER("MIXINL", WM8962_PWR_MGMT_1, 5, 0,
SND_SOC_DAPM_MIXER("MIXINR", WM8962_PWR_MGMT_1, 4, 0,
mixinr, ARRAY_SIZE(mixinr)),
-SND_SOC_DAPM_AIF_IN("DMIC", NULL, 0, WM8962_PWR_MGMT_1, 10, 0),
+SND_SOC_DAPM_AIF_IN("DMIC_ENA", NULL, 0, WM8962_PWR_MGMT_1, 10, 0),
SND_SOC_DAPM_ADC("ADCL", "Capture", WM8962_PWR_MGMT_1, 3, 0),
SND_SOC_DAPM_ADC("ADCR", "Capture", WM8962_PWR_MGMT_1, 2, 0),
@@ -2584,17 +2775,19 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
{ "MICBIAS", NULL, "SYSCLK" },
- { "DMIC", NULL, "DMICDAT" },
+ { "DMIC_ENA", NULL, "DMICDAT" },
{ "ADCL", NULL, "SYSCLK" },
{ "ADCL", NULL, "TOCLK" },
{ "ADCL", NULL, "MIXINL" },
- { "ADCL", NULL, "DMIC" },
+ { "ADCL", NULL, "DMIC_ENA" },
+ { "ADCL", NULL, "DSP2" },
{ "ADCR", NULL, "SYSCLK" },
{ "ADCR", NULL, "TOCLK" },
{ "ADCR", NULL, "MIXINR" },
- { "ADCR", NULL, "DMIC" },
+ { "ADCR", NULL, "DMIC_ENA" },
+ { "ADCR", NULL, "DSP2" },
{ "STL", "Left", "ADCL" },
{ "STL", "Right", "ADCR" },
@@ -2606,11 +2799,13 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
{ "DACL", NULL, "TOCLK" },
{ "DACL", NULL, "Beep" },
{ "DACL", NULL, "STL" },
+ { "DACL", NULL, "DSP2" },
{ "DACR", NULL, "SYSCLK" },
{ "DACR", NULL, "TOCLK" },
{ "DACR", NULL, "Beep" },
{ "DACR", NULL, "STR" },
+ { "DACR", NULL, "DSP2" },
{ "HPMIXL", "IN4L Switch", "IN4L" },
{ "HPMIXL", "IN4R Switch", "IN4R" },
@@ -2646,6 +2841,9 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
{ "HPOUTL", NULL, "HPOUT" },
{ "HPOUTR", NULL, "HPOUT" },
+
+ { "HPOUTL", NULL, "TEMP_HP" },
+ { "HPOUTR", NULL, "TEMP_HP" },
};
static const struct snd_soc_dapm_route wm8962_spk_mono_intercon[] = {
@@ -2662,6 +2860,7 @@ static const struct snd_soc_dapm_route wm8962_spk_mono_intercon[] = {
{ "Speaker Output", NULL, "Speaker PGA" },
{ "Speaker Output", NULL, "SYSCLK" },
{ "Speaker Output", NULL, "TOCLK" },
+ { "Speaker Output", NULL, "TEMP_SPK" },
{ "SPKOUT", NULL, "Speaker Output" },
};
@@ -2690,10 +2889,12 @@ static const struct snd_soc_dapm_route wm8962_spk_stereo_intercon[] = {
{ "SPKOUTL Output", NULL, "SPKOUTL PGA" },
{ "SPKOUTL Output", NULL, "SYSCLK" },
{ "SPKOUTL Output", NULL, "TOCLK" },
+ { "SPKOUTL Output", NULL, "TEMP_SPK" },
{ "SPKOUTR Output", NULL, "SPKOUTR PGA" },
{ "SPKOUTR Output", NULL, "SYSCLK" },
{ "SPKOUTR Output", NULL, "TOCLK" },
+ { "SPKOUTR Output", NULL, "TEMP_SPK" },
{ "SPKOUTL", NULL, "SPKOUTL Output" },
{ "SPKOUTR", NULL, "SPKOUTR Output" },
@@ -2770,18 +2971,44 @@ static const int bclk_divs[] = {
1, -1, 2, 3, 4, -1, 6, 8, -1, 12, 16, 24, -1, 32, 32, 32
};
+static const int sysclk_rates[] = {
+ 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536,
+};
+
static void wm8962_configure_bclk(struct snd_soc_codec *codec)
{
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
int dspclk, i;
int clocking2 = 0;
+ int clocking4 = 0;
int aif2 = 0;
- if (!wm8962->bclk) {
- dev_dbg(codec->dev, "No BCLK rate configured\n");
+ if (!wm8962->sysclk_rate) {
+ dev_dbg(codec->dev, "No SYSCLK configured\n");
+ return;
+ }
+
+ if (!wm8962->bclk || !wm8962->lrclk) {
+ dev_dbg(codec->dev, "No audio clocks configured\n");
return;
}
+ for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) {
+ if (sysclk_rates[i] == wm8962->sysclk_rate / wm8962->lrclk) {
+ clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(sysclk_rates)) {
+ dev_err(codec->dev, "Unsupported sysclk ratio %d\n",
+ wm8962->sysclk_rate / wm8962->lrclk);
+ return;
+ }
+
+ snd_soc_update_bits(codec, WM8962_CLOCKING_4,
+ WM8962_SYSCLK_RATE_MASK, clocking4);
+
dspclk = snd_soc_read(codec, WM8962_CLOCKING1);
if (dspclk < 0) {
dev_err(codec->dev, "Failed to read DSPCLK: %d\n", dspclk);
@@ -2851,6 +3078,8 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
/* VMID 2*50k */
snd_soc_update_bits(codec, WM8962_PWR_MGMT_1,
WM8962_VMID_SEL_MASK, 0x80);
+
+ wm8962_configure_bclk(codec);
break;
case SND_SOC_BIAS_STANDBY:
@@ -2879,12 +3108,6 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
WM8962_BIAS_ENA | 0x180);
msleep(5);
-
- snd_soc_update_bits(codec, WM8962_CLOCKING2,
- WM8962_CLKREG_OVD,
- WM8962_CLKREG_OVD);
-
- wm8962_configure_bclk(codec);
}
/* VMID 2*250k */
@@ -2925,10 +3148,6 @@ static const struct {
{ 96000, 6 },
};
-static const int sysclk_rates[] = {
- 64, 128, 192, 256, 384, 512, 768, 1024, 1408, 1536,
-};
-
static int wm8962_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -2936,52 +3155,44 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->codec;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
- int rate = params_rate(params);
int i;
int aif0 = 0;
int adctl3 = 0;
- int clocking4 = 0;
+
+ if (codec->dapm.bias_level != SND_SOC_BIAS_OFF)
+ return 0;
wm8962->bclk = snd_soc_params_to_bclk(params);
+ if (params_channels(params) == 1)
+ wm8962->bclk *= 2;
+
wm8962->lrclk = params_rate(params);
for (i = 0; i < ARRAY_SIZE(sr_vals); i++) {
- if (sr_vals[i].rate == rate) {
+ if (sr_vals[i].rate == wm8962->lrclk) {
adctl3 |= sr_vals[i].reg;
break;
}
}
if (i == ARRAY_SIZE(sr_vals)) {
- dev_err(codec->dev, "Unsupported rate %dHz\n", rate);
+ dev_err(codec->dev, "Unsupported rate %dHz\n", wm8962->lrclk);
return -EINVAL;
}
- if (rate % 8000 == 0)
+ if (wm8962->lrclk % 8000 == 0)
adctl3 |= WM8962_SAMPLE_RATE_INT_MODE;
- for (i = 0; i < ARRAY_SIZE(sysclk_rates); i++) {
- if (sysclk_rates[i] == wm8962->sysclk_rate / rate) {
- clocking4 |= i << WM8962_SYSCLK_RATE_SHIFT;
- break;
- }
- }
- if (i == ARRAY_SIZE(sysclk_rates)) {
- dev_err(codec->dev, "Unsupported sysclk ratio %d\n",
- wm8962->sysclk_rate / rate);
- return -EINVAL;
- }
-
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
break;
case SNDRV_PCM_FORMAT_S20_3LE:
- aif0 |= 0x40;
+ aif0 |= 0x4;
break;
case SNDRV_PCM_FORMAT_S24_LE:
- aif0 |= 0x80;
+ aif0 |= 0x8;
break;
case SNDRV_PCM_FORMAT_S32_LE:
- aif0 |= 0xc0;
+ aif0 |= 0xc;
break;
default:
return -EINVAL;
@@ -2992,8 +3203,6 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_3,
WM8962_SAMPLE_RATE_INT_MODE |
WM8962_SAMPLE_RATE_MASK, adctl3);
- snd_soc_update_bits(codec, WM8962_CLOCKING_4,
- WM8962_SYSCLK_RATE_MASK, clocking4);
wm8962_configure_bclk(codec);
@@ -3262,22 +3471,39 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
snd_soc_write(codec, WM8962_FLL_CONTROL_7, fll_div.lambda);
snd_soc_write(codec, WM8962_FLL_CONTROL_8, fll_div.n);
+ try_wait_for_completion(&wm8962->fll_lock);
+
snd_soc_update_bits(codec, WM8962_FLL_CONTROL_1,
WM8962_FLL_FRAC | WM8962_FLL_REFCLK_SRC_MASK |
WM8962_FLL_ENA, fll1);
dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
- /* This should be a massive overestimate */
- timeout = msecs_to_jiffies(1);
+ ret = 0;
+
+ if (fll1 & WM8962_FLL_ENA) {
+ /* This should be a massive overestimate but go even
+ * higher if we'll error out
+ */
+ if (wm8962->irq)
+ timeout = msecs_to_jiffies(5);
+ else
+ timeout = msecs_to_jiffies(1);
+
+ timeout = wait_for_completion_timeout(&wm8962->fll_lock,
+ timeout);
- wait_for_completion_timeout(&wm8962->fll_lock, timeout);
+ if (timeout == 0 && wm8962->irq) {
+ dev_err(codec->dev, "FLL lock timed out");
+ ret = -ETIMEDOUT;
+ }
+ }
wm8962->fll_fref = Fref;
wm8962->fll_fout = Fout;
wm8962->fll_src = source;
- return 0;
+ return ret;
}
static int wm8962_mute(struct snd_soc_dai *dai, int mute)
@@ -3317,7 +3543,7 @@ static struct snd_soc_dai_driver wm8962_dai = {
},
.capture = {
.stream_name = "Capture",
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.rates = WM8962_RATES,
.formats = WM8962_FORMATS,
@@ -3362,12 +3588,19 @@ static irqreturn_t wm8962_irq(int irq, void *data)
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
int mask;
int active;
+ int reg;
mask = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2_MASK);
active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2);
active &= ~mask;
+ if (!active)
+ return IRQ_NONE;
+
+ /* Acknowledge the interrupts */
+ snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active);
+
if (active & WM8962_FLL_LOCK_EINT) {
dev_dbg(codec->dev, "FLL locked\n");
complete(&wm8962->fll_lock);
@@ -3376,9 +3609,21 @@ static irqreturn_t wm8962_irq(int irq, void *data)
if (active & WM8962_FIFOS_ERR_EINT)
dev_err(codec->dev, "FIFO error\n");
- if (active & WM8962_TEMP_SHUT_EINT)
+ if (active & WM8962_TEMP_SHUT_EINT) {
dev_crit(codec->dev, "Thermal shutdown\n");
+ reg = snd_soc_read(codec, WM8962_THERMAL_SHUTDOWN_STATUS);
+
+ if (reg & WM8962_TEMP_ERR_HP)
+ dev_crit(codec->dev, "Headphone thermal error\n");
+ if (reg & WM8962_TEMP_WARN_HP)
+ dev_crit(codec->dev, "Headphone thermal warning\n");
+ if (reg & WM8962_TEMP_ERR_SPK)
+ dev_crit(codec->dev, "Speaker thermal error\n");
+ if (reg & WM8962_TEMP_WARN_SPK)
+ dev_crit(codec->dev, "Speaker thermal warning\n");
+ }
+
if (active & (WM8962_MICSCD_EINT | WM8962_MICD_EINT)) {
dev_dbg(codec->dev, "Microphone event detected\n");
@@ -3392,9 +3637,6 @@ static irqreturn_t wm8962_irq(int irq, void *data)
msecs_to_jiffies(250));
}
- /* Acknowledge the interrupts */
- snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active);
-
return IRQ_HANDLED;
}
@@ -3434,34 +3676,17 @@ int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
snd_soc_jack_report(wm8962->jack, 0,
SND_JACK_MICROPHONE | SND_JACK_BTN_0);
- return 0;
-}
-EXPORT_SYMBOL_GPL(wm8962_mic_detect);
-
-#ifdef CONFIG_PM
-static int wm8962_resume(struct snd_soc_codec *codec)
-{
- u16 *reg_cache = codec->reg_cache;
- int i;
-
- /* Restore the registers */
- for (i = 1; i < codec->driver->reg_cache_size; i++) {
- switch (i) {
- case WM8962_SOFTWARE_RESET:
- continue;
- default:
- break;
- }
-
- if (reg_cache[i] != wm8962_reg[i])
- snd_soc_write(codec, i, reg_cache[i]);
+ if (jack) {
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK");
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "MICBIAS");
+ } else {
+ snd_soc_dapm_disable_pin(&codec->dapm, "SYSCLK");
+ snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS");
}
return 0;
}
-#else
-#define wm8962_resume NULL
-#endif
+EXPORT_SYMBOL_GPL(wm8962_mic_detect);
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
static int beep_rates[] = {
@@ -3738,8 +3963,6 @@ static int wm8962_probe(struct snd_soc_codec *codec)
int ret;
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
- struct i2c_client *i2c = container_of(codec->dev, struct i2c_client,
- dev);
u16 *reg_cache = codec->reg_cache;
int i, trigger, irq_pol;
bool dmicclk, dmicdat;
@@ -3829,6 +4052,10 @@ static int wm8962_probe(struct snd_soc_codec *codec)
*/
snd_soc_update_bits(codec, WM8962_CLOCKING2, WM8962_SYSCLK_ENA, 0);
+ /* Ensure we have soft control over all registers */
+ snd_soc_update_bits(codec, WM8962_CLOCKING2,
+ WM8962_CLKREG_OVD, WM8962_CLKREG_OVD);
+
/* Ensure that the oscillator and PLLs are disabled */
snd_soc_update_bits(codec, WM8962_PLL2,
WM8962_OSC_ENA | WM8962_PLL2_ENA | WM8962_PLL3_ENA,
@@ -3883,6 +4110,9 @@ static int wm8962_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME,
WM8962_HPOUT_VU, WM8962_HPOUT_VU);
+ /* Stereo control for EQ */
+ snd_soc_update_bits(codec, WM8962_EQ1, WM8962_EQ_SHARED_COEFF, 0);
+
wm8962_add_widgets(codec);
/* Save boards having to disable DMIC when not in use */
@@ -3911,7 +4141,7 @@ static int wm8962_probe(struct snd_soc_codec *codec)
wm8962_init_beep(codec);
wm8962_init_gpio(codec);
- if (i2c->irq) {
+ if (wm8962->irq) {
if (pdata && pdata->irq_active_low) {
trigger = IRQF_TRIGGER_LOW;
irq_pol = WM8962_IRQ_POL;
@@ -3923,12 +4153,13 @@ static int wm8962_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8962_INTERRUPT_CONTROL,
WM8962_IRQ_POL, irq_pol);
- ret = request_threaded_irq(i2c->irq, NULL, wm8962_irq,
+ ret = request_threaded_irq(wm8962->irq, NULL, wm8962_irq,
trigger | IRQF_ONESHOT,
"wm8962", codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to request IRQ %d: %d\n",
- i2c->irq, ret);
+ wm8962->irq, ret);
+ wm8962->irq = 0;
/* Non-fatal */
} else {
/* Enable some IRQs by default */
@@ -3953,12 +4184,10 @@ err:
static int wm8962_remove(struct snd_soc_codec *codec)
{
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *i2c = container_of(codec->dev, struct i2c_client,
- dev);
int i;
- if (i2c->irq)
- free_irq(i2c->irq, codec);
+ if (wm8962->irq)
+ free_irq(wm8962->irq, codec);
cancel_delayed_work_sync(&wm8962->mic_work);
@@ -3975,7 +4204,6 @@ static int wm8962_remove(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver soc_codec_dev_wm8962 = {
.probe = wm8962_probe,
.remove = wm8962_remove,
- .resume = wm8962_resume,
.set_bias_level = wm8962_set_bias_level,
.reg_cache_size = WM8962_MAX_REGISTER + 1,
.reg_word_size = sizeof(u16),
@@ -3998,6 +4226,8 @@ static __devinit int wm8962_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8962);
+ wm8962->irq = i2c->irq;
+
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_wm8962, &wm8962_dai, 1);
if (ret < 0)
diff --git a/sound/soc/imx/imx-cs42888.c b/sound/soc/imx/imx-cs42888.c
index dc46fa676386..c58af0c46f25 100644
--- a/sound/soc/imx/imx-cs42888.c
+++ b/sound/soc/imx/imx-cs42888.c
@@ -21,12 +21,14 @@
#include <linux/regulator/consumer.h>
#include <linux/fsl_devices.h>
#include <linux/gpio.h>
+#include <linux/mxc_asrc.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/soc-dai.h>
+#include <sound/pcm_params.h>
#include <mach/hardware.h>
#include <mach/clock.h>
@@ -34,6 +36,99 @@
#include "imx-esai.h"
#include "../codecs/cs42888.h"
+#include "imx-pcm.h"
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_IMX_HAVE_PLATFORM_IMX_ASRC)
+struct asrc_esai {
+ unsigned int cpu_dai_rates;
+ unsigned int codec_dai_rates;
+ enum asrc_pair_index asrc_index;
+ unsigned int output_sample_rate;
+};
+static struct asrc_esai asrc_esai_data;
+static bool asrc_support = 1;
+
+static int get_format_width(struct snd_pcm_hw_params *params)
+{
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ case SNDRV_PCM_FORMAT_U8:
+ return 8;
+ case SNDRV_PCM_FORMAT_U16:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_S16_BE:
+ return 16;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ case SNDRV_PCM_FORMAT_S20_3BE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ case SNDRV_PCM_FORMAT_S24_3BE:
+ case SNDRV_PCM_FORMAT_S24_BE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_U24_BE:
+ case SNDRV_PCM_FORMAT_U24_LE:
+ case SNDRV_PCM_FORMAT_U24_3BE:
+ case SNDRV_PCM_FORMAT_U24_3LE:
+ return 24;
+ case SNDRV_PCM_FORMAT_S32:
+ case SNDRV_PCM_FORMAT_U32:
+ return 32;
+ default:
+ pr_err("Format is not support!\r\n");
+ return -EINVAL;
+ }
+}
+
+static int config_asrc(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int rate = params_rate(params);
+ unsigned int channel = params_channels(params);
+ unsigned int wordwidth = get_format_width(params);
+ struct imx_pcm_runtime_data *pcm_data =
+ substream->runtime->private_data;
+ struct asrc_config config = {0};
+ int ret = 0;
+
+ if (rate <= 32000 || rate == asrc_esai_data.output_sample_rate)
+ return -EINVAL;
+
+ if (channel != 2)
+ return -EINVAL;
+
+ if (wordwidth != 24)
+ return -EINVAL;
+
+ ret = asrc_req_pair(channel, &asrc_esai_data.asrc_index);
+ if (ret < 0) {
+ pr_err("Fail to request asrc pair\n");
+ asrc_release_pair(asrc_esai_data.asrc_index);
+ asrc_finish_conv(asrc_esai_data.asrc_index);
+ return -EINVAL;
+ }
+
+ config.pair = asrc_esai_data.asrc_index;
+ config.channel_num = channel;
+ config.input_sample_rate = rate;
+ config.output_sample_rate = asrc_esai_data.output_sample_rate;
+ config.inclk = OUTCLK_ASRCK1_CLK;
+ config.word_width = wordwidth;
+ config.outclk = OUTCLK_ESAI_TX;
+
+ ret = asrc_config_pair(&config);
+ if (ret < 0) {
+ pr_err("Fail to config asrc\n");
+ asrc_release_pair(asrc_esai_data.asrc_index);
+ asrc_finish_conv(asrc_esai_data.asrc_index);
+ return ret;
+ }
+ pcm_data->asrc_index = asrc_esai_data.asrc_index;
+ pcm_data->asrc_enable = 1;
+
+ return 0;
+}
+#else
+static bool asrc_support;
+#endif
struct imx_priv_state {
int hw;
@@ -42,6 +137,7 @@ struct imx_priv_state {
static struct imx_priv_state hw_state;
unsigned int mclk_freq;
+
static int imx_3stack_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -51,6 +147,14 @@ static int imx_3stack_startup(struct snd_pcm_substream *substream)
hw_state.hw = 0;
}
+ if (asrc_support) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ asrc_esai_data.cpu_dai_rates =
+ cpu_dai->driver->playback.rates;
+ asrc_esai_data.codec_dai_rates =
+ codec_dai->driver->playback.rates;
+ }
+
return 0;
}
@@ -58,6 +162,24 @@ static void imx_3stack_shutdown(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+ if (asrc_support) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct imx_pcm_runtime_data *pcm_data =
+ substream->runtime->private_data;
+ if (pcm_data->asrc_enable) {
+ asrc_release_pair(asrc_esai_data.asrc_index);
+ asrc_finish_conv(asrc_esai_data.asrc_index);
+ }
+ pcm_data->asrc_enable = 0;
+ asrc_esai_data.asrc_index = -1;
+
+ codec_dai->driver->playback.rates =
+ asrc_esai_data.codec_dai_rates;
+ cpu_dai->driver->playback.rates =
+ asrc_esai_data.cpu_dai_rates;
+ }
+
if (!cpu_dai->active)
hw_state.hw = 0;
}
@@ -75,6 +197,12 @@ static int imx_3stack_surround_hw_params(struct snd_pcm_substream *substream,
if (hw_state.hw)
return 0;
hw_state.hw = 1;
+
+ if (asrc_support &&
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+ !config_asrc(substream, params)) {
+ rate = asrc_esai_data.output_sample_rate;
+ }
if (cpu_is_mx53() || machine_is_mx6q_sabreauto()) {
switch (rate) {
case 32000:
@@ -211,6 +339,9 @@ static int imx_3stack_cs42888_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
+ if (asrc_support)
+ asrc_esai_data.output_sample_rate = 44100;
+
snd_soc_dapm_new_controls(&codec->dapm, imx_3stack_dapm_widgets,
ARRAY_SIZE(imx_3stack_dapm_widgets));
diff --git a/sound/soc/imx/imx-esai.h b/sound/soc/imx/imx-esai.h
index 97cf1c44c004..d27cbd00ae96 100644
--- a/sound/soc/imx/imx-esai.h
+++ b/sound/soc/imx/imx-esai.h
@@ -1,7 +1,7 @@
/*
* imx-esai.h -- ESAI driver header file for Freescale IMX
*
- * Copyright 2008-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008-2012 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -19,25 +19,26 @@
#ifdef IMX_ESAI_DUMP
#define ESAI_DUMP() \
do {pr_info("dump @ %s\n", __func__); \
- pr_info("ecr %x\n", readl(esai->base + ESAI_ECR)); \
- pr_info("esr %x\n", readl(esai->base + ESAI_ESR)); \
- pr_info("tfcr %x\n", readl(esai->base + ESAI_TFCR)); \
- pr_info("tfsr %x\n", readl(esai->base + ESAI_TFSR)); \
- pr_info("rfcr %x\n", readl(esai->base + ESAI_RFCR)); \
- pr_info("rfsr %x\n", readl(esai->base + ESAI_RFSR)); \
- pr_info("tsr %x\n", readl(esai->base + ESAI_TSR)); \
- pr_info("saisr %x\n", readl(esai->base + ESAI_SAISR)); \
- pr_info("saicr %x\n", readl(esai->base + ESAI_SAICR)); \
- pr_info("tcr %x\n", readl(esai->base + ESAI_TCR)); \
- pr_info("tccr %x\n", readl(esai->base + ESAI_TCCR)); \
- pr_info("rcr %x\n", readl(esai->base + ESAI_RCR)); \
- pr_info("rccr %x\n", readl(esai->base + ESAI_RCCR)); \
- pr_info("tsma %x\n", readl(esai->base + ESAI_TSMA)); \
- pr_info("tsmb %x\n", readl(esai->base + ESAI_TSMB)); \
- pr_info("rsma %x\n", readl(esai->base + ESAI_RSMA)); \
- pr_info("rsmb %x\n", readl(esai->base + ESAI_RSMB)); \
- pr_info("prrc %x\n", readl(esai->base + ESAI_PRRC)); \
- pr_info("pcrc %x\n", readl(esai->base + ESAI_PCRC)); } while (0);
+ pr_info("ESAI_ECR 0x%08x\n", readl(esai->base + ESAI_ECR)); \
+ pr_info("ESAI_ESR 0x%08x\n", readl(esai->base + ESAI_ESR)); \
+ pr_info("ESAI_TFCR 0x%08x\n", readl(esai->base + ESAI_TFCR)); \
+ pr_info("ESAI_TFSR 0x%08x\n", readl(esai->base + ESAI_TFSR)); \
+ pr_info("ESAI_RFCR 0x%08x\n", readl(esai->base + ESAI_RFCR)); \
+ pr_info("ESAI_RFSR 0x%08x\n", readl(esai->base + ESAI_RFSR)); \
+ pr_info("ESAI_TSR 0x%08x\n", readl(esai->base + ESAI_TSR)); \
+ pr_info("ESAI_SAISR 0x%08x\n", readl(esai->base + ESAI_SAISR)); \
+ pr_info("ESAI_SAICR 0x%08x\n", readl(esai->base + ESAI_SAICR)); \
+ pr_info("ESAI_TCR 0x%08x\n", readl(esai->base + ESAI_TCR)); \
+ pr_info("ESAI_TCCR 0x%08x\n", readl(esai->base + ESAI_TCCR)); \
+ pr_info("ESAI_RCR 0x%08x\n", readl(esai->base + ESAI_RCR)); \
+ pr_info("ESAI_RCCR 0x%08x\n", readl(esai->base + ESAI_RCCR)); \
+ pr_info("ESAI_TSMA 0x%08x\n", readl(esai->base + ESAI_TSMA)); \
+ pr_info("ESAI_TSMB 0x%08x\n", readl(esai->base + ESAI_TSMB)); \
+ pr_info("ESAI_RSMA 0x%08x\n", readl(esai->base + ESAI_RSMA)); \
+ pr_info("ESAI_RSMB 0x%08x\n", readl(esai->base + ESAI_RSMB)); \
+ pr_info("ESAI_PRRC 0x%08x\n", readl(esai->base + ESAI_PRRC)); \
+ pr_info("ESAI_PCRC 0x%08x\n", readl(esai->base + ESAI_PCRC)); \
+ } while (0);
#else
#define ESAI_DUMP()
#endif
diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c
index 7ff31a894fa4..386bd26564a8 100644
--- a/sound/soc/imx/imx-pcm-dma-mx2.c
+++ b/sound/soc/imx/imx-pcm-dma-mx2.c
@@ -21,6 +21,8 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dmaengine.h>
+#include <linux/delay.h>
+#include <linux/mxc_asrc.h>
#include <sound/core.h>
#include <sound/initval.h>
@@ -31,19 +33,8 @@
#include <mach/dma.h>
#include "imx-ssi.h"
+#include "imx-pcm.h"
-struct imx_pcm_runtime_data {
- int period_bytes;
- int periods;
- int dma;
- unsigned long offset;
- unsigned long size;
- void *buf;
- int period_time;
- struct dma_async_tx_descriptor *desc;
- struct dma_chan *dma_chan;
- struct imx_dma_data dma_data;
-};
static void audio_dma_irq(void *data)
{
@@ -68,6 +59,106 @@ static bool filter(struct dma_chan *chan, void *param)
return true;
}
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_IMX_HAVE_PLATFORM_IMX_ASRC)
+static bool asrc_filter(struct dma_chan *chan, void *param)
+{
+ struct imx_pcm_runtime_data *iprtd = param;
+ if (!imx_dma_is_general_purpose(chan))
+ return false;
+ chan->private = &iprtd->asrc_dma_data;
+ return true;
+}
+static bool asrc_p2p_filter(struct dma_chan *chan, void *param)
+{
+ struct imx_pcm_runtime_data *iprtd = param;
+ if (!imx_dma_is_general_purpose(chan))
+ return false;
+ chan->private = &iprtd->asrc_p2p_dma_data;
+ return true;
+}
+static int imx_ssi_asrc_dma_alloc(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct imx_pcm_dma_params *dma_params;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+ struct dma_slave_config slave_config;
+
+ dma_cap_mask_t mask;
+ enum dma_slave_buswidth buswidth;
+ int ret;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ break;
+ default:
+ goto error;
+ }
+
+ dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ /*config m2p dma channel*/
+ iprtd->asrc_dma_data.peripheral_type = IMX_DMATYPE_ASRC;
+ iprtd->asrc_dma_data.priority = DMA_PRIO_HIGH;
+ iprtd->asrc_dma_data.dma_request =
+ asrc_get_dma_request(iprtd->asrc_index, 1);
+ iprtd->asrc_dma_chan = dma_request_channel(mask, asrc_filter, iprtd);
+
+ if (!iprtd->asrc_dma_chan)
+ goto error;
+
+ slave_config.direction = DMA_TO_DEVICE;
+ slave_config.dst_addr = asrc_get_per_addr(iprtd->asrc_index, 1);
+ slave_config.dst_addr_width = buswidth;
+ slave_config.dst_maxburst = dma_params->burstsize * buswidth;
+
+ ret = dmaengine_slave_config(iprtd->asrc_dma_chan, &slave_config);
+ if (ret)
+ goto error;
+ /*config p2p dma channel*/
+ iprtd->asrc_p2p_dma_data.peripheral_type = IMX_DMATYPE_ASRC;
+ iprtd->asrc_p2p_dma_data.priority = DMA_PRIO_HIGH;
+ iprtd->asrc_p2p_dma_data.dma_request =
+ asrc_get_dma_request(iprtd->asrc_index, 0);
+ iprtd->asrc_p2p_dma_data.dma_request_p2p = dma_params->dma;
+ iprtd->asrc_p2p_dma_chan =
+ dma_request_channel(mask, asrc_p2p_filter, iprtd);
+ if (!iprtd->asrc_p2p_dma_chan)
+ goto error;
+
+ slave_config.direction = DMA_DEV_TO_DEV;;
+ slave_config.src_addr = asrc_get_per_addr(iprtd->asrc_index, 0);
+ slave_config.src_addr_width = buswidth;
+ slave_config.src_maxburst = dma_params->burstsize * buswidth;
+ slave_config.dst_addr = dma_params->dma_addr;
+ slave_config.dst_addr_width = buswidth;
+ slave_config.dst_maxburst = dma_params->burstsize * buswidth;
+
+ ret = dmaengine_slave_config(iprtd->asrc_p2p_dma_chan, &slave_config);
+ if (ret)
+ goto error;
+
+ return 0;
+error:
+ if (iprtd->asrc_dma_chan) {
+ dma_release_channel(iprtd->asrc_dma_chan);
+ iprtd->asrc_dma_chan = NULL;
+ }
+ if (iprtd->asrc_p2p_dma_chan) {
+ dma_release_channel(iprtd->asrc_p2p_dma_chan);
+ iprtd->asrc_p2p_dma_chan = NULL;
+ }
+ return -EINVAL;
+}
+#endif
static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
@@ -137,17 +228,36 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
int ret;
dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
- ret = imx_ssi_dma_alloc(substream, params);
- if (ret)
- return ret;
- chan = iprtd->dma_chan;
+
+ if (iprtd->asrc_enable) {
+ ret = imx_ssi_asrc_dma_alloc(substream, params);
+ if (ret)
+ return ret;
+ chan = iprtd->asrc_p2p_dma_chan;
+ iprtd->asrc_p2p_desc =
+ chan->device->device_prep_dma_cyclic(chan, 0xffff,
+ 64,
+ 64,
+ DMA_DEV_TO_DEV);
+ if (!iprtd->asrc_p2p_desc) {
+ dev_err(&chan->dev->device,
+ "cannot prepare slave dma\n");
+ return -EINVAL;
+ }
+ chan = iprtd->asrc_dma_chan;
+ } else {
+ ret = imx_ssi_dma_alloc(substream, params);
+ if (ret)
+ return ret;
+ chan = iprtd->dma_chan;
+ }
iprtd->size = params_buffer_bytes(params);
iprtd->periods = params_periods(params);
iprtd->period_bytes = params_period_bytes(params);
iprtd->offset = 0;
iprtd->period_time = HZ / (params_rate(params) /
- params_period_size(params));
+ params_period_size(params));
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
@@ -155,19 +265,38 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
iprtd->buf = (unsigned int *)substream->dma_buffer.area;
- iprtd->desc = chan->device->device_prep_dma_cyclic(chan, dma_addr,
+ if (iprtd->asrc_enable) {
+ iprtd->asrc_desc =
+ chan->device->device_prep_dma_cyclic(chan, dma_addr,
+ iprtd->period_bytes * iprtd->periods,
+ iprtd->period_bytes,
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ if (!iprtd->asrc_desc) {
+ dev_err(&chan->dev->device,
+ "cannot prepare slave dma\n");
+ return -EINVAL;
+ }
+
+ iprtd->asrc_desc->callback = audio_dma_irq;
+ iprtd->asrc_desc->callback_param = substream;
+ } else {
+ iprtd->desc = chan->device->device_prep_dma_cyclic(
+ chan, dma_addr,
iprtd->period_bytes * iprtd->periods,
iprtd->period_bytes,
substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
DMA_TO_DEVICE : DMA_FROM_DEVICE);
- if (!iprtd->desc) {
- dev_err(&chan->dev->device, "cannot prepare slave dma\n");
- return -EINVAL;
+ if (!iprtd->desc) {
+ dev_err(&chan->dev->device,
+ "cannot prepare slave dma\n");
+ return -EINVAL;
+ }
+
+ iprtd->desc->callback = audio_dma_irq;
+ iprtd->desc->callback_param = substream;
}
- iprtd->desc->callback = audio_dma_irq;
- iprtd->desc->callback_param = substream;
-
return 0;
}
@@ -176,9 +305,21 @@ static int snd_imx_pcm_hw_free(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
- if (iprtd->dma_chan) {
- dma_release_channel(iprtd->dma_chan);
+ if (iprtd->asrc_enable) {
+ if (iprtd->asrc_dma_chan) {
+ dma_release_channel(iprtd->asrc_dma_chan);
+ iprtd->asrc_dma_chan = NULL;
+ }
+ if (iprtd->asrc_p2p_dma_chan) {
+ dma_release_channel(iprtd->asrc_p2p_dma_chan);
+ iprtd->asrc_p2p_dma_chan = NULL;
+ }
iprtd->dma_chan = NULL;
+ } else {
+ if (iprtd->dma_chan) {
+ dma_release_channel(iprtd->dma_chan);
+ iprtd->dma_chan = NULL;
+ }
}
return 0;
@@ -203,15 +344,25 @@ static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- dmaengine_submit(iprtd->desc);
-
+ if (iprtd->asrc_enable) {
+ dmaengine_submit(iprtd->asrc_p2p_desc);
+ dmaengine_submit(iprtd->asrc_desc);
+ asrc_start_conv(iprtd->asrc_index);
+ } else {
+ dmaengine_submit(iprtd->desc);
+ }
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- dmaengine_terminate_all(iprtd->dma_chan);
-
+ if (iprtd->asrc_enable) {
+ dmaengine_terminate_all(iprtd->asrc_dma_chan);
+ dmaengine_terminate_all(iprtd->asrc_p2p_dma_chan);
+ asrc_stop_conv(iprtd->asrc_index);
+ } else {
+ dmaengine_terminate_all(iprtd->dma_chan);
+ }
break;
default:
return -EINVAL;
diff --git a/sound/soc/imx/imx-pcm.h b/sound/soc/imx/imx-pcm.h
new file mode 100644
index 000000000000..be0d5ffbe5d1
--- /dev/null
+++ b/sound/soc/imx/imx-pcm.h
@@ -0,0 +1,70 @@
+/*
+ * MXC ALSA Soc Driver
+ *
+ * Copyright (C) 2012 Freescale Semiconductor, Inc.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _IMX_PCM_H
+#define _IMX_PCM_H
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dmaengine.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <mach/dma.h>
+
+#include "imx-ssi.h"
+
+struct imx_pcm_runtime_data {
+ int period_bytes;
+ int periods;
+ int dma;
+ unsigned long offset;
+ unsigned long size;
+ void *buf;
+ int period_time;
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *dma_chan;
+ struct imx_dma_data dma_data;
+
+#if defined(CONFIG_MXC_ASRC) || defined(CONFIG_IMX_HAVE_PLATFORM_IMX_ASRC)
+ int asrc_index;
+ int asrc_enable;
+ struct dma_async_tx_descriptor *asrc_desc;
+ struct dma_chan *asrc_dma_chan;
+ struct imx_dma_data asrc_dma_data;
+ struct dma_async_tx_descriptor *asrc_p2p_desc;
+ struct dma_chan *asrc_p2p_dma_chan;
+ struct imx_dma_data asrc_p2p_dma_data;
+#endif
+};
+#endif
diff --git a/sound/soc/imx/imx-wm8962.c b/sound/soc/imx/imx-wm8962.c
index 3154cbcd7dfc..2885d3a47889 100644
--- a/sound/soc/imx/imx-wm8962.c
+++ b/sound/soc/imx/imx-wm8962.c
@@ -37,6 +37,7 @@
#include <mach/dma.h>
#include <mach/clock.h>
#include <mach/audmux.h>
+#include <mach/gpio.h>
#include "imx-ssi.h"
#include "../codecs/wm8962.h"
@@ -45,31 +46,17 @@ struct imx_priv {
int sysclk; /*mclk from the outside*/
int codec_sysclk;
int dai_hifi;
+ int hp_irq;
+ int hp_status;
+ int amic_irq;
+ int amic_status;
struct platform_device *pdev;
};
-
+unsigned int sample_format = SNDRV_PCM_FMTBIT_S16_LE;
static struct imx_priv card_priv;
static struct snd_soc_card snd_soc_card_imx;
struct clk *wm8962_mclk;
-static struct snd_soc_jack hs_jack;
-
-/* Headphones jack detection DAPM pins */
-static struct snd_soc_jack_pin hs_jack_pins[] = {
- {
- .pin = "Headphone Jack",
- .mask = SND_JACK_HEADPHONE,
- },
-};
-
-/* Headphones jack detection gpios */
-static struct snd_soc_jack_gpio hs_jack_gpios[] = {
- [0] = {
- /* gpio is set on per-platform basis */
- .name = "hp-gpio",
- .report = SND_JACK_HEADPHONE,
- .debounce_time = 200,
- },
-};
+static struct snd_soc_codec *gcodec;
static int imx_hifi_startup(struct snd_pcm_substream *substream)
{
@@ -97,6 +84,7 @@ static int imx_hifi_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_codec *codec = rtd->codec;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct imx_priv *priv = &card_priv;
@@ -104,6 +92,10 @@ static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
unsigned int sample_rate = 44100;
int ret = 0;
u32 dai_format;
+ unsigned int pll_out;
+
+ if (codec->dapm.bias_level != SND_SOC_BIAS_OFF)
+ return 0;
dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM;
@@ -125,16 +117,22 @@ static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
return ret;
sample_rate = params_rate(params);
+ sample_format = params_format(params);
+
+ if (sample_format == SNDRV_PCM_FORMAT_S24_LE)
+ pll_out = sample_rate * 192;
+ else
+ pll_out = sample_rate * 256;
- ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL_INT,
+ ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL_MCLK,
WM8962_FLL_MCLK, priv->sysclk,
- sample_rate * 768);
+ pll_out);
if (ret < 0)
pr_err("Failed to start FLL: %d\n", ret);
ret = snd_soc_dai_set_sysclk(codec_dai,
WM8962_SYSCLK_FLL,
- sample_rate * 768,
+ pll_out,
SND_SOC_CLOCK_IN);
if (ret < 0) {
pr_err("Failed to set SYSCLK: %d\n", ret);
@@ -145,17 +143,15 @@ static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
}
static const struct snd_kcontrol_new controls[] = {
- SOC_DAPM_PIN_SWITCH("Main Speaker"),
- SOC_DAPM_PIN_SWITCH("DMIC"),
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
};
/* imx card dapm widgets */
static const struct snd_soc_dapm_widget imx_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
-
- SND_SOC_DAPM_MIC("AMIC", NULL),
-
SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_MIC("AMIC", NULL),
+ SND_SOC_DAPM_MIC("DMIC", NULL),
};
/* imx machine connections to the codec pins */
@@ -169,14 +165,150 @@ static const struct snd_soc_dapm_route audio_map[] = {
{ "MICBIAS", NULL, "AMIC" },
{ "IN3R", NULL, "MICBIAS" },
+
+ { "DMIC", NULL, "MICBIAS" },
+ { "DMICDAT", NULL, "DMIC" },
+
};
+static void headphone_detect_handler(struct work_struct *wor)
+{
+ struct imx_priv *priv = &card_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+ char *envp[3];
+ char *buf;
+
+ /*sysfs_notify(&pdev->dev.kobj, NULL, "headphone");*/
+ priv->hp_status = gpio_get_value(plat->hp_gpio);
+
+ /* setup a message for userspace headphone in */
+ buf = kmalloc(32, GFP_ATOMIC);
+ if (!buf) {
+ pr_err("%s kmalloc failed\n", __func__);
+ return;
+ }
+
+ if (priv->hp_status != plat->hp_active_low)
+ snprintf(buf, 32, "STATE=%d", 2);
+ else
+ snprintf(buf, 32, "STATE=%d", 0);
+
+ envp[0] = "NAME=headphone";
+ envp[1] = buf;
+ envp[2] = NULL;
+ kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp);
+ kfree(buf);
+
+ enable_irq(priv->hp_irq);
+
+ return;
+}
+
+static DECLARE_DELAYED_WORK(hp_event, headphone_detect_handler);
+
+static irqreturn_t imx_headphone_detect_handler(int irq, void *data)
+{
+ disable_irq_nosync(irq);
+ schedule_delayed_work(&hp_event, msecs_to_jiffies(200));
+ return IRQ_HANDLED;
+}
+
+static ssize_t show_headphone(struct device_driver *dev, char *buf)
+{
+ struct imx_priv *priv = &card_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+
+ /* determine whether hp is plugged in */
+ priv->hp_status = gpio_get_value(plat->hp_gpio);
+
+ if (priv->hp_status != plat->hp_active_low)
+ strcpy(buf, "headphone\n");
+ else
+ strcpy(buf, "speaker\n");
+
+ return strlen(buf);
+}
+
+static DRIVER_ATTR(headphone, S_IRUGO | S_IWUSR, show_headphone, NULL);
+
+static void amic_detect_handler(struct work_struct *work)
+{
+ struct imx_priv *priv = &card_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+ char *envp[3];
+ char *buf;
+
+ /* sysfs_notify(&pdev->dev.kobj, NULL, "amic"); */
+ priv->amic_status = gpio_get_value(plat->mic_gpio);
+
+ /* if amic is inserted, disable dmic */
+ if (priv->amic_status != plat->mic_active_low)
+ snd_soc_dapm_nc_pin(&gcodec->dapm, "DMIC");
+ else
+ snd_soc_dapm_enable_pin(&gcodec->dapm, "DMIC");
+
+ /* setup a message for userspace headphone in */
+ buf = kmalloc(32, GFP_ATOMIC);
+ if (!buf) {
+ pr_err("%s kmalloc failed\n", __func__);
+ return;
+ }
+
+ if (priv->amic_status == 0)
+ snprintf(buf, 32, "STATE=%d", 2);
+ else
+ snprintf(buf, 32, "STATE=%d", 0);
+
+ envp[0] = "NAME=amic";
+ envp[1] = buf;
+ envp[2] = NULL;
+ kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp);
+ kfree(buf);
+
+ enable_irq(priv->amic_irq);
+}
+
+static DECLARE_DELAYED_WORK(amic_event, amic_detect_handler);
+
+static irqreturn_t imx_amic_detect_handler(int irq, void *data)
+{
+ disable_irq_nosync(irq);
+ schedule_delayed_work(&amic_event, msecs_to_jiffies(200));
+ return IRQ_HANDLED;
+}
+
+static ssize_t show_amic(struct device_driver *dev, char *buf)
+{
+ struct imx_priv *priv = &card_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+
+ /* determine whether amic is plugged in */
+ priv->amic_status = gpio_get_value(plat->hp_gpio);
+
+ if (priv->amic_status != plat->mic_active_low)
+ strcpy(buf, "amic\n");
+ else
+ strcpy(buf, "dmic\n");
+
+ return strlen(buf);
+}
+
+static DRIVER_ATTR(amic, S_IRUGO | S_IWUSR, show_amic, NULL);
+
static int imx_wm8962_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
- int ret;
+ struct imx_priv *priv = &card_priv;
+ struct platform_device *pdev = priv->pdev;
+ struct mxc_audio_platform_data *plat = pdev->dev.platform_data;
+
+ gcodec = rtd->codec;
-/* Add imx specific widgets */
+ /* Add imx specific widgets */
snd_soc_dapm_new_controls(&codec->dapm, imx_dapm_widgets,
ARRAY_SIZE(imx_dapm_widgets));
@@ -188,24 +320,15 @@ static int imx_wm8962_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dapm_sync(&codec->dapm);
- if (hs_jack_gpios[0].gpio != -1) {
- /* Jack detection API stuff */
- ret = snd_soc_jack_new(codec, "Headphone Jack",
- SND_JACK_HEADPHONE, &hs_jack);
- if (ret)
- return ret;
+ if (plat->mic_gpio != -1) {
- ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
- hs_jack_pins);
- if (ret) {
- printk(KERN_ERR "failed to call snd_soc_jack_add_pins\n");
- return ret;
- }
+ priv->amic_status = gpio_get_value(plat->mic_gpio);
- ret = snd_soc_jack_add_gpios(&hs_jack,
- ARRAY_SIZE(hs_jack_gpios), hs_jack_gpios);
- if (ret)
- printk(KERN_WARNING "failed to call snd_soc_jack_add_gpios\n");
+ /* if amic is inserted, disable DMIC */
+ if (priv->amic_status != plat->mic_active_low)
+ snd_soc_dapm_nc_pin(&gcodec->dapm, "DMIC");
+ else
+ snd_soc_dapm_enable_pin(&gcodec->dapm, "DMIC");
}
return 0;
@@ -283,8 +406,43 @@ static int __devinit imx_wm8962_probe(struct platform_device *pdev)
}
priv->sysclk = plat->sysclk;
- hs_jack_gpios[0].gpio = plat->hp_gpio;
- hs_jack_gpios[0].invert = plat->hp_active_low;
+ priv->hp_irq = gpio_to_irq(plat->hp_gpio);
+ priv->amic_irq = gpio_to_irq(plat->mic_gpio);
+
+ if (plat->hp_gpio != -1) {
+ ret = request_irq(priv->hp_irq,
+ imx_headphone_detect_handler,
+ IRQ_TYPE_EDGE_BOTH, pdev->name, priv);
+
+ if (ret < 0) {
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = driver_create_file(pdev->dev.driver,
+ &driver_attr_headphone);
+ if (ret < 0) {
+ ret = -EINVAL;
+ return ret;
+ }
+ }
+
+ if (plat->mic_gpio != -1) {
+ ret = request_irq(priv->amic_irq,
+ imx_amic_detect_handler,
+ IRQ_TYPE_EDGE_BOTH, pdev->name, priv);
+
+ if (ret < 0) {
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = driver_create_file(pdev->dev.driver, &driver_attr_amic);
+ if (ret < 0) {
+ ret = -EINVAL;
+ return ret;
+ }
+ }
return ret;
}