diff options
Diffstat (limited to 'sound/soc')
33 files changed, 1336 insertions, 491 deletions
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index 7e4d88007d4f..08e15dee9182 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -1358,7 +1358,7 @@ static int pm860x_probe(struct snd_soc_codec *codec) pm860x->name[i], pm860x); if (ret < 0) { dev_err(codec->dev, "Failed to request IRQ!\n"); - goto out_irq; + goto out; } } @@ -1369,7 +1369,7 @@ static int pm860x_probe(struct snd_soc_codec *codec) if (ret < 0) { dev_err(codec->dev, "Failed to fill register cache: %d\n", ret); - goto out_codec; + goto out; } snd_soc_add_controls(codec, pm860x_snd_controls, @@ -1379,12 +1379,10 @@ static int pm860x_probe(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); return 0; -out_codec: - i = 3; -out_irq: - for (; i >= 0; i--) +out: + while (--i >= 0) free_irq(pm860x->irq[i], pm860x); - return -EINVAL; + return ret; } static int pm860x_remove(struct snd_soc_codec *codec) diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 73675458076d..8b38739c88f8 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -366,9 +366,9 @@ static int ak4535_set_dai_fmt(struct snd_soc_dai *codec_dai, static int ak4535_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; - u16 mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf; + u16 mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC); if (!mute) - ak4535_write(codec, AK4535_DAC, mute_reg); + ak4535_write(codec, AK4535_DAC, mute_reg & ~0x20); else ak4535_write(codec, AK4535_DAC, mute_reg | 0x20); return 0; @@ -381,11 +381,11 @@ static int ak4535_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: - mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf; - ak4535_write(codec, AK4535_DAC, mute_reg); + mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC); + ak4535_write(codec, AK4535_DAC, mute_reg & ~0x20); break; case SND_SOC_BIAS_PREPARE: - mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf; + mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC); ak4535_write(codec, AK4535_DAC, mute_reg | 0x20); break; case SND_SOC_BIAS_STANDBY: diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index 9783e7e2eb93..4f377c9e868d 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -925,7 +925,6 @@ static int alc5623_probe(struct snd_soc_codec *codec) } switch (alc5623->id) { - default: case 0x21: snd_soc_add_controls(codec, rt5621_vol_snd_controls, ARRAY_SIZE(rt5621_vol_snd_controls)); @@ -938,6 +937,8 @@ static int alc5623_probe(struct snd_soc_codec *codec) snd_soc_add_controls(codec, alc5623_vol_snd_controls, ARRAY_SIZE(alc5623_vol_snd_controls)); break; + default: + return -EINVAL; } snd_soc_add_controls(codec, alc5623_snd_controls, @@ -950,7 +951,6 @@ static int alc5623_probe(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); switch (alc5623->id) { - default: case 0x21: case 0x22: snd_soc_dapm_new_controls(dapm, alc5623_dapm_amp_widgets, @@ -962,6 +962,8 @@ static int alc5623_probe(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(dapm, intercon_spk, ARRAY_SIZE(intercon_spk)); break; + default: + return -EINVAL; } return ret; @@ -1039,10 +1041,12 @@ static int alc5623_i2c_probe(struct i2c_client *client, case 0x22: alc5623_dai.name = "alc5622-hifi"; break; - default: case 0x23: alc5623_dai.name = "alc5623-hifi"; break; + default: + kfree(alc5623); + return -EINVAL; } i2c_set_clientdata(client, alc5623); diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index dc6912e9b667..07ba7e3f6a8c 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1627,7 +1627,6 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec) { struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec); struct wm8350 *wm8350 = dev_get_platdata(codec->dev); - int ret; wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, WM8350_JDL_ENA | WM8350_JDR_ENA); @@ -1642,15 +1641,9 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec) priv->hpr.jack = NULL; priv->mic.jack = NULL; - /* cancel any work waiting to be queued. */ - ret = cancel_delayed_work(&codec->dapm.delayed_work); - /* if there was any work waiting then we run it now and * wait for its completion */ - if (ret) { - schedule_delayed_work(&codec->dapm.delayed_work, 0); - flush_scheduled_work(); - } + flush_delayed_work_sync(&codec->dapm.delayed_work); wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF); diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index e2a927684b48..0ebdecfd27a9 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -160,7 +160,7 @@ static const u16 wm8580_reg[] = { 0x0121, 0x017e, 0x007d, 0x0014, /*R3*/ 0x0121, 0x017e, 0x007d, 0x0194, /*R7*/ - 0x001c, 0x0002, 0x0002, 0x00c2, /*R11*/ + 0x0010, 0x0002, 0x0002, 0x00c2, /*R11*/ 0x0182, 0x0082, 0x000a, 0x0024, /*R15*/ 0x0009, 0x0000, 0x00ff, 0x0000, /*R19*/ 0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R23*/ @@ -491,29 +491,29 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream, paifa |= 0x8; break; case SNDRV_PCM_FORMAT_S20_3LE: - paifa |= 0x10; + paifa |= 0x0; paifb |= WM8580_AIF_LENGTH_20; break; case SNDRV_PCM_FORMAT_S24_LE: - paifa |= 0x10; + paifa |= 0x0; paifb |= WM8580_AIF_LENGTH_24; break; case SNDRV_PCM_FORMAT_S32_LE: - paifa |= 0x10; - paifb |= WM8580_AIF_LENGTH_24; + paifa |= 0x0; + paifb |= WM8580_AIF_LENGTH_32; break; default: return -EINVAL; } /* Look up the SYSCLK ratio; accept only exact matches */ - ratio = wm8580->sysclk[dai->id] / params_rate(params); + ratio = wm8580->sysclk[dai->driver->id] / params_rate(params); for (i = 0; i < ARRAY_SIZE(wm8580_sysclk_ratios); i++) if (ratio == wm8580_sysclk_ratios[i]) break; if (i == ARRAY_SIZE(wm8580_sysclk_ratios)) { dev_err(codec->dev, "Invalid clock ratio %d/%d\n", - wm8580->sysclk[dai->id], params_rate(params)); + wm8580->sysclk[dai->driver->id], params_rate(params)); return -EINVAL; } paifa |= i; @@ -716,7 +716,7 @@ static int wm8580_set_sysclk(struct snd_soc_dai *dai, int clk_id, switch (clk_id) { case WM8580_CLKSRC_ADCMCLK: - if (dai->id != WM8580_DAI_PAIFTX) + if (dai->driver->id != WM8580_DAI_PAIFTX) return -EINVAL; sel = 0 << sel_shift; break; @@ -735,7 +735,7 @@ static int wm8580_set_sysclk(struct snd_soc_dai *dai, int clk_id, } /* We really should validate PLL settings but not yet */ - wm8580->sysclk[dai->id] = freq; + wm8580->sysclk[dai->driver->id] = freq; return snd_soc_update_bits(codec, WM8580_CLKSEL, sel_mask, sel); } diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index a1041450d9bb..71122dc36826 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -46,6 +46,8 @@ struct wm8731_priv { u16 reg_cache[WM8731_CACHEREGNUM]; unsigned int sysclk; int sysclk_type; + int playback_fs; + bool deemph; }; @@ -64,16 +66,79 @@ static const u16 wm8731_reg[WM8731_CACHEREGNUM] = { #define wm8731_reset(c) snd_soc_write(c, WM8731_RESET, 0) static const char *wm8731_input_select[] = {"Line In", "Mic"}; -static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; -static const struct soc_enum wm8731_enum[] = { - SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select), - SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph), -}; +static const struct soc_enum wm8731_insel_enum = + SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select); + +static int wm8731_deemph[] = { 0, 32000, 44100, 48000 }; + +static int wm8731_set_deemph(struct snd_soc_codec *codec) +{ + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); + int val, i, best; + + /* If we're using deemphasis select the nearest available sample + * rate. + */ + if (wm8731->deemph) { + best = 1; + for (i = 2; i < ARRAY_SIZE(wm8731_deemph); i++) { + if (abs(wm8731_deemph[i] - wm8731->playback_fs) < + abs(wm8731_deemph[best] - wm8731->playback_fs)) + best = i; + } + + val = best << 1; + } else { + best = 0; + val = 0; + } + + dev_dbg(codec->dev, "Set deemphasis %d (%dHz)\n", + best, wm8731_deemph[best]); + + return snd_soc_update_bits(codec, WM8731_APDIGI, 0x6, val); +} + +static int wm8731_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = wm8731->deemph; + + return 0; +} + +static int wm8731_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec); + int deemph = ucontrol->value.enumerated.item[0]; + int ret = 0; + + if (deemph > 1) + return -EINVAL; + + mutex_lock(&codec->mutex); + if (wm8731->deemph != deemph) { + wm8731->deemph = deemph; + + wm8731_set_deemph(codec); + + ret = 1; + } + mutex_unlock(&codec->mutex); + + return ret; +} static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0); static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0); static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); +static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 2000, 0); static const struct snd_kcontrol_new wm8731_snd_controls[] = { @@ -86,7 +151,7 @@ SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0, in_tlv), SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1), -SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0), +SOC_SINGLE_TLV("Mic Boost Volume", WM8731_APANA, 0, 1, 0, mic_tlv), SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1), SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1, @@ -95,7 +160,8 @@ SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1, SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1), SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0), -SOC_ENUM("Playback De-emphasis", wm8731_enum[1]), +SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, + wm8731_get_deemph, wm8731_put_deemph), }; /* Output Mixer */ @@ -107,7 +173,7 @@ SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0), /* Input mux */ static const struct snd_kcontrol_new wm8731_input_mux_controls = -SOC_DAPM_ENUM("Input Select", wm8731_enum[0]); +SOC_DAPM_ENUM("Input Select", wm8731_insel_enum); static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("OSC", WM8731_PWR, 5, 1, NULL, 0), @@ -239,6 +305,8 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream, u16 srate = (coeff_div[i].sr << 2) | (coeff_div[i].bosr << 1) | coeff_div[i].usb; + wm8731->playback_fs = params_rate(params); + snd_soc_write(codec, WM8731_SRATE, srate); /* bit size */ @@ -253,6 +321,8 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream, break; } + wm8731_set_deemph(codec); + snd_soc_write(codec, WM8731_IFACE, iface); return 0; } @@ -526,7 +596,7 @@ static int wm8731_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8731_RINVOL, 0x100, 0); /* Disable bypass path by default */ - snd_soc_update_bits(codec, WM8731_APANA, 0x4, 0); + snd_soc_update_bits(codec, WM8731_APANA, 0x8, 0); snd_soc_add_controls(codec, wm8731_snd_controls, ARRAY_SIZE(wm8731_snd_controls)); diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 57bf9468d39a..73507e71cb79 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1528,25 +1528,6 @@ static int wm8753_resume(struct snd_soc_codec *codec) return 0; } -/* - * This function forces any delayed work to be queued and run. - */ -static int run_delayed_work(struct delayed_work *dwork) -{ - int ret; - - /* cancel any work waiting to be queued. */ - ret = cancel_delayed_work(dwork); - - /* if there was any work waiting then we run it now and - * wait for it's completion */ - if (ret) { - schedule_delayed_work(dwork, 0); - flush_scheduled_work(); - } - return ret; -} - static int wm8753_probe(struct snd_soc_codec *codec) { struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); @@ -1606,7 +1587,7 @@ static int wm8753_probe(struct snd_soc_codec *codec) /* power down chip */ static int wm8753_remove(struct snd_soc_codec *codec) { - run_delayed_work(&codec->dapm.delayed_work); + flush_delayed_work_sync(&codec->dapm.delayed_work); wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 620793e51666..d015745a886b 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -218,10 +218,11 @@ struct wm8903_priv { int sysclk; int irq; - /* Reference counts */ + int fs; + int deemph; + + /* Reference count */ int class_w_users; - int playback_active; - int capture_active; struct completion wseq; @@ -230,9 +231,6 @@ struct wm8903_priv { int mic_short; int mic_last_report; int mic_delay; - - struct snd_pcm_substream *master_substream; - struct snd_pcm_substream *slave_substream; }; static int wm8903_volatile_register(unsigned int reg) @@ -462,6 +460,72 @@ static int wm8903_class_w_put(struct snd_kcontrol *kcontrol, .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } +static int wm8903_deemph[] = { 0, 32000, 44100, 48000 }; + +static int wm8903_set_deemph(struct snd_soc_codec *codec) +{ + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + int val, i, best; + + /* If we're using deemphasis select the nearest available sample + * rate. + */ + if (wm8903->deemph) { + best = 1; + for (i = 2; i < ARRAY_SIZE(wm8903_deemph); i++) { + if (abs(wm8903_deemph[i] - wm8903->fs) < + abs(wm8903_deemph[best] - wm8903->fs)) + best = i; + } + + val = best << WM8903_DEEMPH_SHIFT; + } else { + best = 0; + val = 0; + } + + dev_dbg(codec->dev, "Set deemphasis %d (%dHz)\n", + best, wm8903_deemph[best]); + + return snd_soc_update_bits(codec, WM8903_DAC_DIGITAL_1, + WM8903_DEEMPH_MASK, val); +} + +static int wm8903_get_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = wm8903->deemph; + + return 0; +} + +static int wm8903_put_deemph(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); + int deemph = ucontrol->value.enumerated.item[0]; + int ret = 0; + + if (deemph > 1) + return -EINVAL; + + mutex_lock(&codec->mutex); + if (wm8903->deemph != deemph) { + wm8903->deemph = deemph; + + wm8903_set_deemph(codec); + + ret = 1; + } + mutex_unlock(&codec->mutex); + + return ret; +} + /* ALSA can only do steps of .01dB */ static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); @@ -474,6 +538,23 @@ static const DECLARE_TLV_DB_SCALE(drc_tlv_min, 0, 600, 0); static const DECLARE_TLV_DB_SCALE(drc_tlv_max, 1200, 600, 0); static const DECLARE_TLV_DB_SCALE(drc_tlv_startup, -300, 50, 0); +static const char *hpf_mode_text[] = { + "Hi-fi", "Voice 1", "Voice 2", "Voice 3" +}; + +static const struct soc_enum hpf_mode = + SOC_ENUM_SINGLE(WM8903_ADC_DIGITAL_0, 5, 4, hpf_mode_text); + +static const char *osr_text[] = { + "Low power", "High performance" +}; + +static const struct soc_enum adc_osr = + SOC_ENUM_SINGLE(WM8903_ANALOGUE_ADC_0, 0, 2, osr_text); + +static const struct soc_enum dac_osr = + SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 0, 2, osr_text); + static const char *drc_slope_text[] = { "1", "1/2", "1/4", "1/8", "1/16", "0" }; @@ -536,13 +617,6 @@ static const char *mute_mode_text[] = { static const struct soc_enum mute_mode = SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 9, 2, mute_mode_text); -static const char *dac_deemphasis_text[] = { - "Disabled", "32kHz", "44.1kHz", "48kHz" -}; - -static const struct soc_enum dac_deemphasis = - SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 1, 4, dac_deemphasis_text); - static const char *companding_text[] = { "ulaw", "alaw" }; @@ -612,6 +686,9 @@ SOC_SINGLE("Right Input PGA Common Mode Switch", WM8903_ANALOGUE_RIGHT_INPUT_1, 6, 1, 0), /* ADCs */ +SOC_ENUM("ADC OSR", adc_osr), +SOC_SINGLE("HPF Switch", WM8903_ADC_DIGITAL_0, 4, 1, 0), +SOC_ENUM("HPF Mode", hpf_mode), SOC_SINGLE("DRC Switch", WM8903_DRC_0, 15, 1, 0), SOC_ENUM("DRC Compressor Slope R0", drc_slope_r0), SOC_ENUM("DRC Compressor Slope R1", drc_slope_r1), @@ -641,14 +718,16 @@ SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8903_DAC_DIGITAL_0, 4, 8, 12, 0, digital_sidetone_tlv), /* DAC */ +SOC_ENUM("DAC OSR", dac_osr), SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8903_DAC_DIGITAL_VOLUME_LEFT, WM8903_DAC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv), SOC_ENUM("DAC Soft Mute Rate", soft_mute), SOC_ENUM("DAC Mute Mode", mute_mode), SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0), -SOC_ENUM("DAC De-emphasis", dac_deemphasis), SOC_ENUM("DAC Companding Mode", dac_companding), SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0), +SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0, + wm8903_get_deemph, wm8903_put_deemph), /* Headphones */ SOC_DOUBLE_R("Headphone Switch", @@ -1222,58 +1301,6 @@ static struct { { 0, 0 }, }; -static int wm8903_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - struct snd_pcm_runtime *master_runtime; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - wm8903->playback_active++; - else - wm8903->capture_active++; - - /* The DAI has shared clocks so if we already have a playback or - * capture going then constrain this substream to match it. - */ - if (wm8903->master_substream) { - master_runtime = wm8903->master_substream->runtime; - - dev_dbg(codec->dev, "Constraining to %d bits\n", - master_runtime->sample_bits); - - snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - master_runtime->sample_bits, - master_runtime->sample_bits); - - wm8903->slave_substream = substream; - } else - wm8903->master_substream = substream; - - return 0; -} - -static void wm8903_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; - struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - wm8903->playback_active--; - else - wm8903->capture_active--; - - if (wm8903->master_substream == substream) - wm8903->master_substream = wm8903->slave_substream; - - wm8903->slave_substream = NULL; -} - static int wm8903_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -1298,11 +1325,6 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, u16 clock1 = snd_soc_read(codec, WM8903_CLOCK_RATES_1); u16 dac_digital1 = snd_soc_read(codec, WM8903_DAC_DIGITAL_1); - if (substream == wm8903->slave_substream) { - dev_dbg(codec->dev, "Ignoring hw_params for slave substream\n"); - return 0; - } - /* Enable sloping stopband filter for low sample rates */ if (fs <= 24000) dac_digital1 |= WM8903_DAC_SB_FILT; @@ -1320,19 +1342,6 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, } } - /* Constraints should stop us hitting this but let's make sure */ - if (wm8903->capture_active) - switch (sample_rates[dsp_config].rate) { - case 88200: - case 96000: - dev_err(codec->dev, "%dHz unsupported by ADC\n", - fs); - return -EINVAL; - - default: - break; - } - dev_dbg(codec->dev, "DSP fs = %dHz\n", sample_rates[dsp_config].rate); clock1 &= ~WM8903_SAMPLE_RATE_MASK; clock1 |= sample_rates[dsp_config].value; @@ -1428,6 +1437,9 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, aif2 |= bclk_divs[bclk_div].div; aif3 |= bclk / fs; + wm8903->fs = params_rate(params); + wm8903_set_deemph(codec); + snd_soc_write(codec, WM8903_CLOCK_RATES_0, clock0); snd_soc_write(codec, WM8903_CLOCK_RATES_1, clock1); snd_soc_write(codec, WM8903_AUDIO_INTERFACE_1, aif1); @@ -1571,8 +1583,6 @@ static irqreturn_t wm8903_irq(int irq, void *data) SNDRV_PCM_FMTBIT_S24_LE) static struct snd_soc_dai_ops wm8903_dai_ops = { - .startup = wm8903_startup, - .shutdown = wm8903_shutdown, .hw_params = wm8903_hw_params, .digital_mute = wm8903_digital_mute, .set_fmt = wm8903_set_dai_fmt, diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index d0024666eaf9..8ba142abd8e6 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -817,7 +817,8 @@ static int wm8904_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec); - return wm8904->deemph; + ucontrol->value.enumerated.item[0] = wm8904->deemph; + return 0; } static int wm8904_put_deemph(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 6200beb082b2..ca0265f008d2 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -379,7 +379,8 @@ static int wm8955_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec); - return wm8955->deemph; + ucontrol->value.enumerated.item[0] = wm8955->deemph; + return 0; } static int wm8955_put_deemph(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 0bcf36ee29bc..2c5712dce1d5 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -137,7 +137,8 @@ static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - return wm8960->deemph; + ucontrol->value.enumerated.item[0] = wm8960->deemph; + return 0; } static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index cf4b2722648a..f0c9d2691842 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3339,7 +3339,7 @@ static irqreturn_t wm8962_irq(int irq, void *data) int mask; int active; - mask = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2); + mask = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2_MASK); active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2); active &= ~mask; diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 77b1d441c3d0..15f34a26debf 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1030,6 +1030,12 @@ static int wm8993_set_bias_level(struct snd_soc_codec *codec, WM8993_VMID_SEL_MASK | WM8993_BIAS_ENA, 0); + snd_soc_update_bits(codec, WM8993_ANTIPOP2, + WM8993_STARTUP_BIAS_ENA | + WM8993_VMID_BUF_ENA | + WM8993_VMID_RAMP_MASK | + WM8993_BIAS_SRC, 0); + #ifdef CONFIG_REGULATOR /* Post 2.6.34 we will be able to get a callback when * the regulators are disabled which we can use but diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index bfd4cf4fde5f..af104acd75f8 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -92,6 +92,11 @@ struct wm8994_priv { int retune_mobile_cfg[WM8994_NUM_EQ]; struct soc_enum retune_mobile_enum; + /* Platform dependant MBC configuration */ + int mbc_cfg; + const char **mbc_texts; + struct soc_enum mbc_enum; + struct wm8994_micdet micdet[2]; wm8958_micdet_cb jack_cb; @@ -305,6 +310,19 @@ static const char *sidetone_hpf_text[] = { static const struct soc_enum sidetone_hpf = SOC_ENUM_SINGLE(WM8994_SIDETONE, 7, 7, sidetone_hpf_text); +static const char *adc_hpf_text[] = { + "HiFi", "Voice 1", "Voice 2", "Voice 3" +}; + +static const struct soc_enum aif1adc1_hpf = + SOC_ENUM_SINGLE(WM8994_AIF1_ADC1_FILTERS, 13, 4, adc_hpf_text); + +static const struct soc_enum aif1adc2_hpf = + SOC_ENUM_SINGLE(WM8994_AIF1_ADC2_FILTERS, 13, 4, adc_hpf_text); + +static const struct soc_enum aif2adc_hpf = + SOC_ENUM_SINGLE(WM8994_AIF2_ADC_FILTERS, 13, 4, adc_hpf_text); + static const DECLARE_TLV_DB_SCALE(aif_tlv, 0, 600, 0); static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); static const DECLARE_TLV_DB_SCALE(st_tlv, -3600, 300, 0); @@ -540,11 +558,22 @@ static const struct soc_enum aif2dacl_src = static const struct soc_enum aif2dacr_src = SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, 14, 2, aif_chan_src_text); +static const char *osr_text[] = { + "Low Power", "High Performance", +}; + +static const struct soc_enum dac_osr = + SOC_ENUM_SINGLE(WM8994_OVERSAMPLING, 0, 2, osr_text); + +static const struct soc_enum adc_osr = + SOC_ENUM_SINGLE(WM8994_OVERSAMPLING, 1, 2, osr_text); + static void wm8958_mbc_apply(struct snd_soc_codec *codec, int mbc, int start) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994_pdata *pdata = wm8994->pdata; int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5); - int ena, reg, aif; + int ena, reg, aif, i; switch (mbc) { case 0: @@ -587,7 +616,20 @@ static void wm8958_mbc_apply(struct snd_soc_codec *codec, int mbc, int start) snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM, WM8958_DSP2_ENA, WM8958_DSP2_ENA); - /* TODO: Apply any user specified MBC settings */ + /* If we've got user supplied MBC settings use them */ + if (pdata && pdata->num_mbc_cfgs) { + struct wm8958_mbc_cfg *cfg + = &pdata->mbc_cfgs[wm8994->mbc_cfg]; + + for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++) + snd_soc_write(codec, i + WM8958_MBC_BAND_1_K_1, + cfg->coeff_regs[i]); + + for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++) + snd_soc_write(codec, + i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1, + cfg->cutoff_regs[i]); + } /* Run the DSP */ snd_soc_write(codec, WM8958_DSP2_EXECCONTROL, @@ -648,6 +690,39 @@ static int wm8958_aif_ev(struct snd_soc_dapm_widget *w, return 0; } +static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994_pdata *pdata = wm8994->pdata; + int value = ucontrol->value.integer.value[0]; + int reg; + + /* Don't allow on the fly reconfiguration */ + reg = snd_soc_read(codec, WM8994_CLOCKING_1); + if (reg < 0 || reg & WM8958_DSP2CLK_ENA) + return -EBUSY; + + if (value >= pdata->num_mbc_cfgs) + return -EINVAL; + + wm8994->mbc_cfg = value; + + return 0; +} + +static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg; + + return 0; +} + static int wm8958_mbc_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -715,13 +790,13 @@ SOC_DOUBLE_R_TLV("AIF2ADC Volume", WM8994_AIF2_ADC_LEFT_VOLUME, SOC_ENUM("AIF1ADCL Source", aif1adcl_src), SOC_ENUM("AIF1ADCR Source", aif1adcr_src), -SOC_ENUM("AIF2ADCL Source", aif1adcl_src), -SOC_ENUM("AIF2ADCR Source", aif1adcr_src), +SOC_ENUM("AIF2ADCL Source", aif2adcl_src), +SOC_ENUM("AIF2ADCR Source", aif2adcr_src), SOC_ENUM("AIF1DACL Source", aif1dacl_src), SOC_ENUM("AIF1DACR Source", aif1dacr_src), -SOC_ENUM("AIF2DACL Source", aif1dacl_src), -SOC_ENUM("AIF2DACR Source", aif1dacr_src), +SOC_ENUM("AIF2DACL Source", aif2dacl_src), +SOC_ENUM("AIF2DACR Source", aif2dacr_src), SOC_DOUBLE_R_TLV("AIF1DAC1 Volume", WM8994_AIF1_DAC1_LEFT_VOLUME, WM8994_AIF1_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv), @@ -760,6 +835,18 @@ SOC_SINGLE_TLV("DAC2 Left Sidetone Volume", WM8994_DAC2_MIXER_VOLUMES, SOC_ENUM("Sidetone HPF Mux", sidetone_hpf), SOC_SINGLE("Sidetone HPF Switch", WM8994_SIDETONE, 6, 1, 0), +SOC_ENUM("AIF1ADC1 HPF Mode", aif1adc1_hpf), +SOC_DOUBLE("AIF1ADC1 HPF Switch", WM8994_AIF1_ADC1_FILTERS, 12, 11, 1, 0), + +SOC_ENUM("AIF1ADC2 HPF Mode", aif1adc2_hpf), +SOC_DOUBLE("AIF1ADC2 HPF Switch", WM8994_AIF1_ADC2_FILTERS, 12, 11, 1, 0), + +SOC_ENUM("AIF2ADC HPF Mode", aif2adc_hpf), +SOC_DOUBLE("AIF2ADC HPF Switch", WM8994_AIF2_ADC_FILTERS, 12, 11, 1, 0), + +SOC_ENUM("ADC OSR", adc_osr), +SOC_ENUM("DAC OSR", dac_osr), + SOC_DOUBLE_R_TLV("DAC1 Volume", WM8994_DAC1_LEFT_VOLUME, WM8994_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv), SOC_DOUBLE_R("DAC1 Switch", WM8994_DAC1_LEFT_VOLUME, @@ -1205,10 +1292,10 @@ SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", "AIF1 Capture", 0, WM8994_POWER_MANAGEMENT_4, 8, 0), SND_SOC_DAPM_AIF_IN_E("AIF1DAC1L", NULL, 0, WM8994_POWER_MANAGEMENT_5, 9, 0, wm8958_aif_ev, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_AIF_IN_E("AIF1DAC1R", NULL, 0, WM8994_POWER_MANAGEMENT_5, 8, 0, wm8958_aif_ev, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_AIF_OUT("AIF1ADC2L", "AIF1 Capture", 0, WM8994_POWER_MANAGEMENT_4, 11, 0), @@ -1216,10 +1303,10 @@ SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", "AIF1 Capture", 0, WM8994_POWER_MANAGEMENT_4, 10, 0), SND_SOC_DAPM_AIF_IN_E("AIF1DAC2L", NULL, 0, WM8994_POWER_MANAGEMENT_5, 11, 0, wm8958_aif_ev, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_AIF_IN_E("AIF1DAC2R", NULL, 0, WM8994_POWER_MANAGEMENT_5, 10, 0, wm8958_aif_ev, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER("AIF1ADC1L Mixer", SND_SOC_NOPM, 0, 0, aif1adc1l_mix, ARRAY_SIZE(aif1adc1l_mix)), @@ -1625,6 +1712,7 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, /* Allow no source specification when stopping */ if (freq_out) return -EINVAL; + src = wm8994->fll[id].src; break; case WM8994_FLL_SRC_MCLK1: case WM8994_FLL_SRC_MCLK2: @@ -1806,15 +1894,33 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { pm_runtime_get_sync(codec->dev); - /* Tweak DC servo and DSP configuration for - * improved performance. */ - if (control->type == WM8994 && wm8994->revision < 4) { - /* Tweak DC servo and DSP configuration for - * improved performance. */ - snd_soc_write(codec, 0x102, 0x3); - snd_soc_write(codec, 0x56, 0x3); - snd_soc_write(codec, 0x817, 0); - snd_soc_write(codec, 0x102, 0); + switch (control->type) { + case WM8994: + if (wm8994->revision < 4) { + /* Tweak DC servo and DSP + * configuration for improved + * performance. */ + snd_soc_write(codec, 0x102, 0x3); + snd_soc_write(codec, 0x56, 0x3); + snd_soc_write(codec, 0x817, 0); + snd_soc_write(codec, 0x102, 0); + } + break; + + case WM8958: + if (wm8994->revision == 0) { + /* Optimise performance for rev A */ + snd_soc_write(codec, 0x102, 0x3); + snd_soc_write(codec, 0xcb, 0x81); + snd_soc_write(codec, 0x817, 0); + snd_soc_write(codec, 0x102, 0); + + snd_soc_update_bits(codec, + WM8958_CHARGE_PUMP_2, + WM8958_CP_DISCH, + WM8958_CP_DISCH); + } + break; } /* Discharge LINEOUT1 & 2 */ @@ -2028,10 +2134,12 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, struct wm8994 *control = codec->control_data; struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); int aif1_reg; + int aif2_reg; int bclk_reg; int lrclk_reg; int rate_reg; int aif1 = 0; + int aif2 = 0; int bclk = 0; int lrclk = 0; int rate_val = 0; @@ -2042,6 +2150,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, switch (dai->id) { case 1: aif1_reg = WM8994_AIF1_CONTROL_1; + aif2_reg = WM8994_AIF1_CONTROL_2; bclk_reg = WM8994_AIF1_BCLK; rate_reg = WM8994_AIF1_RATE; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || @@ -2054,6 +2163,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, break; case 2: aif1_reg = WM8994_AIF2_CONTROL_1; + aif2_reg = WM8994_AIF2_CONTROL_2; bclk_reg = WM8994_AIF2_BCLK; rate_reg = WM8994_AIF2_RATE; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || @@ -2109,6 +2219,10 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, dev_dbg(dai->dev, "AIF%dCLK is %dHz, target BCLK %dHz\n", dai->id, wm8994->aifclk[id], bclk_rate); + if (params_channels(params) == 1 && + (snd_soc_read(codec, aif1_reg) & 0x18) == 0x18) + aif2 |= WM8994_AIF1_MONO; + if (wm8994->aifclk[id] == 0) { dev_err(dai->dev, "AIF%dCLK not configured\n", dai->id); return -EINVAL; @@ -2152,6 +2266,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, lrclk, bclk_rate / lrclk); snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1); + snd_soc_update_bits(codec, aif2_reg, WM8994_AIF1_MONO, aif2); snd_soc_update_bits(codec, bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk); snd_soc_update_bits(codec, lrclk_reg, WM8994_AIF1DAC_RATE_MASK, lrclk); @@ -2307,14 +2422,14 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .id = 1, .playback = { .stream_name = "AIF1 Playback", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, }, .capture = { .stream_name = "AIF1 Capture", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, @@ -2326,14 +2441,14 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .id = 2, .playback = { .stream_name = "AIF2 Playback", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, }, .capture = { .stream_name = "AIF2 Capture", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, @@ -2345,14 +2460,14 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .id = 3, .playback = { .stream_name = "AIF3 Playback", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, }, .capture = { .stream_name = "AIF3 Capture", - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, @@ -2539,6 +2654,34 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) dev_dbg(codec->dev, "%d ReTune Mobile configurations\n", pdata->num_retune_mobile_cfgs); + if (pdata->num_mbc_cfgs) { + struct snd_kcontrol_new control[] = { + SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum, + wm8958_get_mbc_enum, wm8958_put_mbc_enum), + }; + + /* We need an array of texts for the enum API */ + wm8994->mbc_texts = kmalloc(sizeof(char *) + * pdata->num_mbc_cfgs, GFP_KERNEL); + if (!wm8994->mbc_texts) { + dev_err(wm8994->codec->dev, + "Failed to allocate %d MBC config texts\n", + pdata->num_mbc_cfgs); + return; + } + + for (i = 0; i < pdata->num_mbc_cfgs; i++) + wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name; + + wm8994->mbc_enum.max = pdata->num_mbc_cfgs; + wm8994->mbc_enum.texts = wm8994->mbc_texts; + + ret = snd_soc_add_controls(wm8994->codec, control, 1); + if (ret != 0) + dev_err(wm8994->codec->dev, + "Failed to add MBC mode controls: %d\n", ret); + } + if (pdata->num_retune_mobile_cfgs) wm8994_handle_retune_mobile_pdata(wm8994); else diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index c7060775e88b..e5055b28c638 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -590,6 +590,10 @@ static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id, reg5 |= fll_div.fll_clk_ref_div << WM9081_FLL_CLK_REF_DIV_SHIFT; snd_soc_write(codec, WM9081_FLL_CONTROL_5, reg5); + /* Set gain to the recommended value */ + snd_soc_update_bits(codec, WM9081_FLL_CONTROL_4, + WM9081_FLL_GAIN_MASK, 0); + /* Enable the FLL */ snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1 | WM9081_FLL_ENA); diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index b24ba9fa7ef7..c466982eed23 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -323,7 +323,7 @@ SOC_DOUBLE_R("Speaker Switch", SOC_DOUBLE_R("Speaker ZC Switch", WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT, 7, 1, 0), -SOC_DOUBLE_TLV("Speaker Boost Volume", WM8993_SPKOUT_BOOST, 0, 3, 7, 0, +SOC_DOUBLE_TLV("Speaker Boost Volume", WM8993_SPKOUT_BOOST, 3, 0, 7, 0, spkboost_tlv), SOC_ENUM("Speaker Reference", speaker_ref), SOC_ENUM("Speaker Mode", speaker_mode), diff --git a/sound/soc/ep93xx/ep93xx-i2s.c b/sound/soc/ep93xx/ep93xx-i2s.c index 4f4873359613..9ac93f6b4f85 100644 --- a/sound/soc/ep93xx/ep93xx-i2s.c +++ b/sound/soc/ep93xx/ep93xx-i2s.c @@ -352,13 +352,13 @@ static struct snd_soc_dai_driver ep93xx_i2s_dai = { .playback = { .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, + .rates = SNDRV_PCM_RATE_8000_96000, .formats = EP93XX_I2S_FORMATS, }, .capture = { .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, + .rates = SNDRV_PCM_RATE_8000_96000, .formats = EP93XX_I2S_FORMATS, }, .ops = &ep93xx_i2s_dai_ops, diff --git a/sound/soc/ep93xx/ep93xx-pcm.c b/sound/soc/ep93xx/ep93xx-pcm.c index 2f121ddbe4bb..06670776f649 100644 --- a/sound/soc/ep93xx/ep93xx-pcm.c +++ b/sound/soc/ep93xx/ep93xx-pcm.c @@ -35,9 +35,9 @@ static const struct snd_pcm_hardware ep93xx_pcm_hardware = { SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER), - .rates = SNDRV_PCM_RATE_8000_48000, + .rates = SNDRV_PCM_RATE_8000_96000, .rate_min = SNDRV_PCM_RATE_8000, - .rate_max = SNDRV_PCM_RATE_48000, + .rate_max = SNDRV_PCM_RATE_96000, .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | diff --git a/sound/soc/nuc900/nuc900-pcm.c b/sound/soc/nuc900/nuc900-pcm.c index 2245f8b8edc1..8263f56dc665 100644 --- a/sound/soc/nuc900/nuc900-pcm.c +++ b/sound/soc/nuc900/nuc900-pcm.c @@ -50,12 +50,12 @@ static int nuc900_dma_hw_params(struct snd_pcm_substream *substream, unsigned long flags; int ret = 0; - spin_lock_irqsave(&nuc900_audio->lock, flags); - ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); if (ret < 0) return ret; + spin_lock_irqsave(&nuc900_audio->lock, flags); + nuc900_audio->substream = substream; nuc900_audio->dma_addr[substream->stream] = runtime->dma_addr; nuc900_audio->buffersize[substream->stream] = @@ -169,6 +169,7 @@ static int nuc900_dma_prepare(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct nuc900_audio *nuc900_audio = runtime->private_data; unsigned long flags, val; + int ret = 0; spin_lock_irqsave(&nuc900_audio->lock, flags); @@ -197,10 +198,10 @@ static int nuc900_dma_prepare(struct snd_pcm_substream *substream) AUDIO_WRITE(nuc900_audio->mmio + ACTL_RESET, val); break; default: - return -EINVAL; + ret = -EINVAL; } spin_unlock_irqrestore(&nuc900_audio->lock, flags); - return 0; + return ret; } static int nuc900_dma_trigger(struct snd_pcm_substream *substream, int cmd) diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index d542ea2ff6be..a088db6d5091 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -12,8 +12,8 @@ config SND_OMAP_SOC_MCPDM config SND_OMAP_SOC_N810 tristate "SoC Audio support for Nokia N810" depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C + depends on OMAP_MUX select SND_OMAP_SOC_MCBSP - select OMAP_MUX select SND_SOC_TLV320AIC3X help Say Y if you want to add support for SoC audio on Nokia N810. diff --git a/sound/soc/s6000/s6000-i2s.c b/sound/soc/s6000/s6000-i2s.c index 8778faa174a6..3052f64b2403 100644 --- a/sound/soc/s6000/s6000-i2s.c +++ b/sound/soc/s6000/s6000-i2s.c @@ -434,7 +434,7 @@ static struct snd_soc_dai_driver s6000_i2s_dai = { .rate_max = 1562500, }, .ops = &s6000_i2s_dai_ops, -} +}; static int __devinit s6000_i2s_probe(struct platform_device *pdev) { diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c index 271fd222bf19..ab3ccaec72d2 100644 --- a/sound/soc/s6000/s6000-pcm.c +++ b/sound/soc/s6000/s6000-pcm.c @@ -473,7 +473,7 @@ static int s6000_pcm_new(struct snd_card *card, } res = request_irq(params->irq, s6000_pcm_irq, IRQF_SHARED, - s6000_soc_platform.name, pcm); + "s6000-audio", pcm); if (res) { printk(KERN_ERR "s6000-pcm couldn't get IRQ\n"); return res; diff --git a/sound/soc/s6000/s6105-ipcam.c b/sound/soc/s6000/s6105-ipcam.c index 2e977e4d65e7..bf3f3f9165e4 100644 --- a/sound/soc/s6000/s6105-ipcam.c +++ b/sound/soc/s6000/s6105-ipcam.c @@ -168,7 +168,7 @@ static int s6105_aic3x_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_sync(dapm); - snd_ctl_add(codec->snd_card, snd_ctl_new1(&audio_out_mux, codec)); + snd_ctl_add(codec->card->snd_card, snd_ctl_new1(&audio_out_mux, codec)); return 0; } diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index eb45cf90d4e2..67cdad4ee535 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -116,6 +116,14 @@ config ASOC_SAMSUNG_SIMTEC_HERMES select SND_SOC_TLV320AIC3X select ASOC_SAMSUNG_SIMTEC +config ASOC_SAMSUNG_H1940_UDA1380 + tristate "Audio support for the HP iPAQ H1940" + depends on ASOC_SAMSUNG && ARCH_H1940 + select SND_S3C24XX_I2S + select SND_SOC_UDA1380 + help + This driver provides audio support for HP iPAQ h1940 PDA. + config ASOC_SAMSUNG_RX1950_UDA1380 tristate "Audio support for the HP iPAQ RX1950" depends on ASOC_SAMSUNG && MACH_RX1950 diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 0d24f95c8b1c..622e76eb9775 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -27,6 +27,7 @@ snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o +snd-soc-h1940-uda1380-objs := h1940_uda1380.o snd-soc-rx1950-uda1380-objs := rx1950_uda1380.o snd-soc-smdk-wm8580-objs := smdk_wm8580.o snd-soc-smdk-wm9713-objs := smdk_wm9713.o @@ -43,6 +44,7 @@ obj-$(CONFIG_ASOC_SAMSUNG_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o obj-$(CONFIG_ASOC_SAMSUNG_SIMTEC) += snd-soc-s3c24xx-simtec.o obj-$(CONFIG_ASOC_SAMSUNG_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o obj-$(CONFIG_ASOC_SAMSUNG_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o +obj-$(CONFIG_ASOC_SAMSUNG_H1940_UDA1380) += snd-soc-h1940-uda1380.o obj-$(CONFIG_ASOC_SAMSUNG_RX1950_UDA1380) += snd-soc-rx1950-uda1380.o obj-$(CONFIG_ASOC_SAMSUNG_SMDK_WM8580) += snd-soc-smdk-wm8580.o obj-$(CONFIG_ASOC_SAMSUNG_SMDK_WM9713) += snd-soc-smdk-wm9713.o diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c new file mode 100644 index 000000000000..c45f7ce14d61 --- /dev/null +++ b/sound/soc/samsung/h1940_uda1380.c @@ -0,0 +1,296 @@ +/* + * h1940-uda1380.c -- ALSA Soc Audio Layer + * + * Copyright (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org> + * Copyright (c) 2010 Vasily Khoruzhick <anarsoul@gmail.com> + * + * Based on version from Arnaud Patard <arnaud.patard@rtp-net.org> + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/gpio.h> + +#include <sound/soc.h> +#include <sound/uda1380.h> +#include <sound/jack.h> + +#include <plat/regs-iis.h> + +#include <mach/h1940-latch.h> + +#include <asm/mach-types.h> + +#include "dma.h" +#include "s3c24xx-i2s.h" +#include "../codecs/uda1380.h" + +static unsigned int rates[] = { + 11025, + 22050, + 44100, +}; + +static struct snd_pcm_hw_constraint_list hw_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static struct snd_soc_jack hp_jack; + +static struct snd_soc_jack_pin hp_jack_pins[] = { + { + .pin = "Headphone Jack", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Speaker", + .mask = SND_JACK_HEADPHONE, + .invert = 1, + }, +}; + +static struct snd_soc_jack_gpio hp_jack_gpios[] = { + { + .gpio = S3C2410_GPG(4), + .name = "hp-gpio", + .report = SND_JACK_HEADPHONE, + .invert = 1, + .debounce_time = 200, + }, +}; + +static int h1940_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.rate_min = hw_rates.list[0]; + runtime->hw.rate_max = hw_rates.list[hw_rates.count - 1]; + runtime->hw.rates = SNDRV_PCM_RATE_KNOT; + + return snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &hw_rates); +} + +static int h1940_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 *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int div; + int ret; + unsigned int rate = params_rate(params); + + switch (rate) { + case 11025: + case 22050: + case 44100: + div = s3c24xx_i2s_get_clockrate() / (384 * rate); + if (s3c24xx_i2s_get_clockrate() % (384 * rate) > (192 * rate)) + div++; + break; + default: + dev_err(&rtd->dev, "%s: rate %d is not supported\n", + __func__, rate); + return -EINVAL; + } + + /* set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) + return ret; + + /* select clock source */ + ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_PCLK, rate, + SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + /* set MCLK division for sample rate */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, + S3C2410_IISMOD_384FS); + if (ret < 0) + return ret; + + /* set BCLK division for sample rate */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK, + S3C2410_IISMOD_32FS); + if (ret < 0) + return ret; + + /* set prescaler division for sample rate */ + ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER, + S3C24XX_PRESCALE(div, div)); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops h1940_ops = { + .startup = h1940_startup, + .hw_params = h1940_hw_params, +}; + +static int h1940_spk_power(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + gpio_set_value(H1940_LATCH_AUDIO_POWER, 1); + else + gpio_set_value(H1940_LATCH_AUDIO_POWER, 0); + + return 0; +} + +/* h1940 machine dapm widgets */ +static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("Speaker", h1940_spk_power), +}; + +/* h1940 machine audio_map */ +static const struct snd_soc_dapm_route audio_map[] = { + /* headphone connected to VOUTLHP, VOUTRHP */ + {"Headphone Jack", NULL, "VOUTLHP"}, + {"Headphone Jack", NULL, "VOUTRHP"}, + + /* ext speaker connected to VOUTL, VOUTR */ + {"Speaker", NULL, "VOUTL"}, + {"Speaker", NULL, "VOUTR"}, + + /* mic is connected to VINM */ + {"VINM", NULL, "Mic Jack"}, +}; + +static struct platform_device *s3c24xx_snd_device; + +static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int err; + + /* Add h1940 specific widgets */ + err = snd_soc_dapm_new_controls(dapm, uda1380_dapm_widgets, + ARRAY_SIZE(uda1380_dapm_widgets)); + if (err) + return err; + + /* Set up h1940 specific audio path audio_mapnects */ + err = snd_soc_dapm_add_routes(dapm, audio_map, + ARRAY_SIZE(audio_map)); + if (err) + return err; + + snd_soc_dapm_enable_pin(dapm, "Headphone Jack"); + snd_soc_dapm_enable_pin(dapm, "Speaker"); + snd_soc_dapm_enable_pin(dapm, "Mic Jack"); + + snd_soc_dapm_sync(dapm); + + snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, + &hp_jack); + + snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins), + hp_jack_pins); + + snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios), + hp_jack_gpios); + + return 0; +} + +/* s3c24xx digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link h1940_uda1380_dai[] = { + { + .name = "uda1380", + .stream_name = "UDA1380 Duplex", + .cpu_dai_name = "s3c24xx-iis", + .codec_dai_name = "uda1380-hifi", + .init = h1940_uda1380_init, + .platform_name = "samsung-audio", + .codec_name = "uda1380-codec.0-001a", + .ops = &h1940_ops, + }, +}; + +static struct snd_soc_card h1940_asoc = { + .name = "h1940", + .dai_link = h1940_uda1380_dai, + .num_links = ARRAY_SIZE(h1940_uda1380_dai), +}; + +static int __init h1940_init(void) +{ + int ret; + + if (!machine_is_h1940()) + return -ENODEV; + + /* configure some gpios */ + ret = gpio_request(H1940_LATCH_AUDIO_POWER, "speaker-power"); + if (ret) + goto err_out; + + ret = gpio_direction_output(H1940_LATCH_AUDIO_POWER, 0); + if (ret) + goto err_gpio; + + s3c24xx_snd_device = platform_device_alloc("soc-audio", -1); + if (!s3c24xx_snd_device) { + ret = -ENOMEM; + goto err_gpio; + } + + platform_set_drvdata(s3c24xx_snd_device, &h1940_asoc); + ret = platform_device_add(s3c24xx_snd_device); + + if (ret) + goto err_plat; + + return 0; + +err_plat: + platform_device_put(s3c24xx_snd_device); +err_gpio: + gpio_free(H1940_LATCH_AUDIO_POWER); + +err_out: + return ret; +} + +static void __exit h1940_exit(void) +{ + platform_device_unregister(s3c24xx_snd_device); + snd_soc_jack_free_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios), + hp_jack_gpios); + gpio_free(H1940_LATCH_AUDIO_POWER); +} + +module_init(h1940_init); +module_exit(h1940_exit); + +/* Module information */ +MODULE_AUTHOR("Arnaud Patard, Vasily Khoruzhick"); +MODULE_DESCRIPTION("ALSA SoC H1940"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c index ca779ad1613c..cb2f4d04ab7e 100644 --- a/sound/soc/samsung/smdk_spdif.c +++ b/sound/soc/samsung/smdk_spdif.c @@ -28,7 +28,7 @@ static int set_audio_clock_heirachy(struct platform_device *pdev) { struct clk *fout_epll, *mout_epll, *sclk_audio0, *sclk_spdif; - int ret; + int ret = 0; fout_epll = clk_get(NULL, "fout_epll"); if (IS_ERR(fout_epll)) { @@ -152,8 +152,6 @@ static struct snd_soc_ops smdk_spdif_ops = { .hw_params = smdk_hw_params, }; -static struct snd_soc_card smdk; - static struct snd_soc_dai_link smdk_dai = { .name = "S/PDIF", .stream_name = "S/PDIF PCM Playback", diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c index d96602de71de..a14820ac9665 100644 --- a/sound/soc/sh/fsi-ak4642.c +++ b/sound/soc/sh/fsi-ak4642.c @@ -12,6 +12,14 @@ #include <linux/platform_device.h> #include <sound/sh_fsi.h> +struct fsi_ak4642_data { + const char *name; + const char *card; + const char *cpu_dai; + const char *codec; + const char *platform; +}; + static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *dai = rtd->codec_dai; @@ -27,37 +35,42 @@ static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd) } static struct snd_soc_dai_link fsi_dai_link = { - .name = "AK4642", - .stream_name = "AK4642", - .cpu_dai_name = "fsia-dai", /* fsi A */ .codec_dai_name = "ak4642-hifi", -#ifdef CONFIG_MACH_AP4EVB - .platform_name = "sh_fsi2", - .codec_name = "ak4642-codec.0-0013", -#else - .platform_name = "sh_fsi.0", - .codec_name = "ak4642-codec.0-0012", -#endif .init = fsi_ak4642_dai_init, - .ops = NULL, }; static struct snd_soc_card fsi_soc_card = { - .name = "FSI (AK4642)", .dai_link = &fsi_dai_link, .num_links = 1, }; static struct platform_device *fsi_snd_device; -static int __init fsi_ak4642_init(void) +static int fsi_ak4642_probe(struct platform_device *pdev) { int ret = -ENOMEM; + const struct platform_device_id *id_entry; + struct fsi_ak4642_data *pdata; + + id_entry = pdev->id_entry; + if (!id_entry) { + dev_err(&pdev->dev, "unknown fsi ak4642\n"); + return -ENODEV; + } + + pdata = (struct fsi_ak4642_data *)id_entry->driver_data; fsi_snd_device = platform_device_alloc("soc-audio", FSI_PORT_A); if (!fsi_snd_device) goto out; + fsi_dai_link.name = pdata->name; + fsi_dai_link.stream_name = pdata->name; + fsi_dai_link.cpu_dai_name = pdata->cpu_dai; + fsi_dai_link.platform_name = pdata->platform; + fsi_dai_link.codec_name = pdata->codec; + fsi_soc_card.name = pdata->card; + platform_set_drvdata(fsi_snd_device, &fsi_soc_card); ret = platform_device_add(fsi_snd_device); @@ -68,9 +81,108 @@ out: return ret; } -static void __exit fsi_ak4642_exit(void) +static int fsi_ak4642_remove(struct platform_device *pdev) { platform_device_unregister(fsi_snd_device); + return 0; +} + +static struct fsi_ak4642_data fsi_a_ak4642 = { + .name = "AK4642", + .card = "FSIA (AK4642)", + .cpu_dai = "fsia-dai", + .codec = "ak4642-codec.0-0012", + .platform = "sh_fsi.0", +}; + +static struct fsi_ak4642_data fsi_b_ak4642 = { + .name = "AK4642", + .card = "FSIB (AK4642)", + .cpu_dai = "fsib-dai", + .codec = "ak4642-codec.0-0012", + .platform = "sh_fsi.0", +}; + +static struct fsi_ak4642_data fsi_a_ak4643 = { + .name = "AK4643", + .card = "FSIA (AK4643)", + .cpu_dai = "fsia-dai", + .codec = "ak4642-codec.0-0013", + .platform = "sh_fsi.0", +}; + +static struct fsi_ak4642_data fsi_b_ak4643 = { + .name = "AK4643", + .card = "FSIB (AK4643)", + .cpu_dai = "fsib-dai", + .codec = "ak4642-codec.0-0013", + .platform = "sh_fsi.0", +}; + +static struct fsi_ak4642_data fsi2_a_ak4642 = { + .name = "AK4642", + .card = "FSI2A (AK4642)", + .cpu_dai = "fsia-dai", + .codec = "ak4642-codec.0-0012", + .platform = "sh_fsi2", +}; + +static struct fsi_ak4642_data fsi2_b_ak4642 = { + .name = "AK4642", + .card = "FSI2B (AK4642)", + .cpu_dai = "fsib-dai", + .codec = "ak4642-codec.0-0012", + .platform = "sh_fsi2", +}; + +static struct fsi_ak4642_data fsi2_a_ak4643 = { + .name = "AK4643", + .card = "FSI2A (AK4643)", + .cpu_dai = "fsia-dai", + .codec = "ak4642-codec.0-0013", + .platform = "sh_fsi2", +}; + +static struct fsi_ak4642_data fsi2_b_ak4643 = { + .name = "AK4643", + .card = "FSI2B (AK4643)", + .cpu_dai = "fsib-dai", + .codec = "ak4642-codec.0-0013", + .platform = "sh_fsi2", +}; + +static struct platform_device_id fsi_id_table[] = { + /* FSI */ + { "sh_fsi_a_ak4642", (kernel_ulong_t)&fsi_a_ak4642 }, + { "sh_fsi_b_ak4642", (kernel_ulong_t)&fsi_b_ak4642 }, + { "sh_fsi_a_ak4643", (kernel_ulong_t)&fsi_a_ak4643 }, + { "sh_fsi_b_ak4643", (kernel_ulong_t)&fsi_b_ak4643 }, + + /* FSI 2 */ + { "sh_fsi2_a_ak4642", (kernel_ulong_t)&fsi2_a_ak4642 }, + { "sh_fsi2_b_ak4642", (kernel_ulong_t)&fsi2_b_ak4642 }, + { "sh_fsi2_a_ak4643", (kernel_ulong_t)&fsi2_a_ak4643 }, + { "sh_fsi2_b_ak4643", (kernel_ulong_t)&fsi2_b_ak4643 }, + {}, +}; + +static struct platform_driver fsi_ak4642 = { + .driver = { + .name = "fsi-ak4642-audio", + }, + .probe = fsi_ak4642_probe, + .remove = fsi_ak4642_remove, + .id_table = fsi_id_table, +}; + +static int __init fsi_ak4642_init(void) +{ + return platform_driver_register(&fsi_ak4642); +} + +static void __exit fsi_ak4642_exit(void) +{ + platform_driver_unregister(&fsi_ak4642); } module_init(fsi_ak4642_init); diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 507e709f2807..97c5394aa7d7 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -19,20 +19,26 @@ #include <sound/soc.h> #include <sound/sh_fsi.h> -#define DO_FMT 0x0000 -#define DOFF_CTL 0x0004 -#define DOFF_ST 0x0008 -#define DI_FMT 0x000C -#define DIFF_CTL 0x0010 -#define DIFF_ST 0x0014 -#define CKG1 0x0018 -#define CKG2 0x001C -#define DIDT 0x0020 -#define DODT 0x0024 -#define MUTE_ST 0x0028 -#define OUT_SEL 0x0030 -#define REG_END OUT_SEL - +/* PortA/PortB register */ +#define REG_DO_FMT 0x0000 +#define REG_DOFF_CTL 0x0004 +#define REG_DOFF_ST 0x0008 +#define REG_DI_FMT 0x000C +#define REG_DIFF_CTL 0x0010 +#define REG_DIFF_ST 0x0014 +#define REG_CKG1 0x0018 +#define REG_CKG2 0x001C +#define REG_DIDT 0x0020 +#define REG_DODT 0x0024 +#define REG_MUTE_ST 0x0028 +#define REG_OUT_SEL 0x0030 + +/* master register */ +#define MST_CLK_RST 0x0210 +#define MST_SOFT_RST 0x0214 +#define MST_FIFO_SZ 0x0218 + +/* core register (depend on FSI version) */ #define A_MST_CTLR 0x0180 #define B_MST_CTLR 0x01A0 #define CPU_INT_ST 0x01F4 @@ -41,22 +47,23 @@ #define INT_ST 0x0200 #define IEMSK 0x0204 #define IMSK 0x0208 -#define MUTE 0x020C -#define CLK_RST 0x0210 -#define SOFT_RST 0x0214 -#define FIFO_SZ 0x0218 -#define MREG_START A_MST_CTLR -#define MREG_END FIFO_SZ /* DO_FMT */ /* DI_FMT */ +#define CR_BWS_24 (0x0 << 20) /* FSI2 */ +#define CR_BWS_16 (0x1 << 20) /* FSI2 */ +#define CR_BWS_20 (0x2 << 20) /* FSI2 */ + +#define CR_DTMD_PCM (0x0 << 8) /* FSI2 */ +#define CR_DTMD_SPDIF_PCM (0x1 << 8) /* FSI2 */ +#define CR_DTMD_SPDIF_STREAM (0x2 << 8) /* FSI2 */ + #define CR_MONO (0x0 << 4) #define CR_MONO_D (0x1 << 4) #define CR_PCM (0x2 << 4) #define CR_I2S (0x3 << 4) #define CR_TDM (0x4 << 4) #define CR_TDM_D (0x5 << 4) -#define CR_SPDIF 0x00100120 /* DOFF_CTL */ /* DIFF_CTL */ @@ -93,6 +100,10 @@ #define IR (1 << 4) /* Interrupt Reset */ #define FSISR (1 << 0) /* Software Reset */ +/* OUT_SEL (FSI2) */ +#define DMMD (1 << 4) /* SPDIF output timing 0: Biphase only */ + /* 1: Biphase and serial */ + /* FIFO_SZ */ #define FIFO_SZ_MASK 0x7 @@ -132,7 +143,7 @@ struct fsi_priv { struct fsi_stream playback; struct fsi_stream capture; - u32 mst_ctrl; + long rate; }; struct fsi_core { @@ -141,6 +152,8 @@ struct fsi_core { u32 int_st; u32 iemsk; u32 imsk; + u32 a_mclk; + u32 b_mclk; }; struct fsi_master { @@ -180,62 +193,22 @@ static void __fsi_reg_mask_set(u32 reg, u32 mask, u32 data) __fsi_reg_write(reg, val); } -static void fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data) -{ - if (reg > REG_END) { - pr_err("fsi: register access err (%s)\n", __func__); - return; - } - - __fsi_reg_write((u32)(fsi->base + reg), data); -} +#define fsi_reg_write(p, r, d)\ + __fsi_reg_write((u32)(p->base + REG_##r), d) -static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg) -{ - if (reg > REG_END) { - pr_err("fsi: register access err (%s)\n", __func__); - return 0; - } +#define fsi_reg_read(p, r)\ + __fsi_reg_read((u32)(p->base + REG_##r)) - return __fsi_reg_read((u32)(fsi->base + reg)); -} +#define fsi_reg_mask_set(p, r, m, d)\ + __fsi_reg_mask_set((u32)(p->base + REG_##r), m, d) -static void fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data) -{ - if (reg > REG_END) { - pr_err("fsi: register access err (%s)\n", __func__); - return; - } - - __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data); -} - -static void fsi_master_write(struct fsi_master *master, u32 reg, u32 data) -{ - unsigned long flags; - - if ((reg < MREG_START) || - (reg > MREG_END)) { - pr_err("fsi: register access err (%s)\n", __func__); - return; - } - - spin_lock_irqsave(&master->lock, flags); - __fsi_reg_write((u32)(master->base + reg), data); - spin_unlock_irqrestore(&master->lock, flags); -} - -static u32 fsi_master_read(struct fsi_master *master, u32 reg) +#define fsi_master_read(p, r) _fsi_master_read(p, MST_##r) +#define fsi_core_read(p, r) _fsi_master_read(p, p->core->r) +static u32 _fsi_master_read(struct fsi_master *master, u32 reg) { u32 ret; unsigned long flags; - if ((reg < MREG_START) || - (reg > MREG_END)) { - pr_err("fsi: register access err (%s)\n", __func__); - return 0; - } - spin_lock_irqsave(&master->lock, flags); ret = __fsi_reg_read((u32)(master->base + reg)); spin_unlock_irqrestore(&master->lock, flags); @@ -243,17 +216,13 @@ static u32 fsi_master_read(struct fsi_master *master, u32 reg) return ret; } -static void fsi_master_mask_set(struct fsi_master *master, +#define fsi_master_mask_set(p, r, m, d) _fsi_master_mask_set(p, MST_##r, m, d) +#define fsi_core_mask_set(p, r, m, d) _fsi_master_mask_set(p, p->core->r, m, d) +static void _fsi_master_mask_set(struct fsi_master *master, u32 reg, u32 mask, u32 data) { unsigned long flags; - if ((reg < MREG_START) || - (reg > MREG_END)) { - pr_err("fsi: register access err (%s)\n", __func__); - return; - } - spin_lock_irqsave(&master->lock, flags); __fsi_reg_mask_set((u32)(master->base + reg), mask, data); spin_unlock_irqrestore(&master->lock, flags); @@ -373,11 +342,13 @@ static void fsi_stream_pop(struct fsi_priv *fsi, int is_play) static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play) { u32 status; - u32 reg = is_play ? DOFF_ST : DIFF_ST; struct fsi_stream *io = fsi_get_stream(fsi, is_play); int data_num; - status = fsi_reg_read(fsi, reg); + status = is_play ? + fsi_reg_read(fsi, DOFF_ST) : + fsi_reg_read(fsi, DIFF_ST); + data_num = 0x1ff & (status >> 8); data_num *= io->chan_num; @@ -471,8 +442,8 @@ static void fsi_irq_enable(struct fsi_priv *fsi, int is_play) u32 data = AB_IO(1, fsi_get_port_shift(fsi, is_play)); struct fsi_master *master = fsi_get_master(fsi); - fsi_master_mask_set(master, master->core->imsk, data, data); - fsi_master_mask_set(master, master->core->iemsk, data, data); + fsi_core_mask_set(master, imsk, data, data); + fsi_core_mask_set(master, iemsk, data, data); } static void fsi_irq_disable(struct fsi_priv *fsi, int is_play) @@ -480,18 +451,13 @@ static void fsi_irq_disable(struct fsi_priv *fsi, int is_play) u32 data = AB_IO(1, fsi_get_port_shift(fsi, is_play)); struct fsi_master *master = fsi_get_master(fsi); - fsi_master_mask_set(master, master->core->imsk, data, 0); - fsi_master_mask_set(master, master->core->iemsk, data, 0); + fsi_core_mask_set(master, imsk, data, 0); + fsi_core_mask_set(master, iemsk, data, 0); } static u32 fsi_irq_get_status(struct fsi_master *master) { - return fsi_master_read(master, master->core->int_st); -} - -static void fsi_irq_clear_all_status(struct fsi_master *master) -{ - fsi_master_write(master, master->core->int_st, 0); + return fsi_core_read(master, int_st); } static void fsi_irq_clear_status(struct fsi_priv *fsi) @@ -503,7 +469,7 @@ static void fsi_irq_clear_status(struct fsi_priv *fsi) data |= AB_IO(1, fsi_get_port_shift(fsi, 1)); /* clear interrupt factor */ - fsi_master_mask_set(master, master->core->int_st, data, 0); + fsi_core_mask_set(master, int_st, data, 0); } /* @@ -514,17 +480,19 @@ static void fsi_irq_clear_status(struct fsi_priv *fsi) static void fsi_spdif_clk_ctrl(struct fsi_priv *fsi, int enable) { struct fsi_master *master = fsi_get_master(fsi); - u32 val = BP | SE; + u32 mask, val; if (master->core->ver < 2) { pr_err("fsi: register access err (%s)\n", __func__); return; } - if (enable) - fsi_master_mask_set(master, fsi->mst_ctrl, val, val); - else - fsi_master_mask_set(master, fsi->mst_ctrl, val, 0); + mask = BP | SE; + val = enable ? mask : 0; + + fsi_is_port_a(fsi) ? + fsi_core_mask_set(master, a_mclk, mask, val) : + fsi_core_mask_set(master, b_mclk, mask, val); } /* @@ -548,7 +516,7 @@ static void fsi_fifo_init(struct fsi_priv *fsi, { struct fsi_master *master = fsi_get_master(fsi); struct fsi_stream *io = fsi_get_stream(fsi, is_play); - u32 ctrl, shift, i; + u32 shift, i; /* get on-chip RAM capacity */ shift = fsi_master_read(master, FIFO_SZ); @@ -581,13 +549,17 @@ static void fsi_fifo_init(struct fsi_priv *fsi, dev_dbg(dai->dev, "%d channel %d store\n", io->chan_num, io->fifo_max_num); - ctrl = is_play ? DOFF_CTL : DIFF_CTL; - - /* set interrupt generation factor */ - fsi_reg_write(fsi, ctrl, IRQ_HALF); - - /* clear FIFO */ - fsi_reg_mask_set(fsi, ctrl, FIFO_CLR, FIFO_CLR); + /* + * set interrupt generation factor + * clear FIFO + */ + if (is_play) { + fsi_reg_write(fsi, DOFF_CTL, IRQ_HALF); + fsi_reg_mask_set(fsi, DOFF_CTL, FIFO_CLR, FIFO_CLR); + } else { + fsi_reg_write(fsi, DIFF_CTL, IRQ_HALF); + fsi_reg_mask_set(fsi, DIFF_CTL, FIFO_CLR, FIFO_CLR); + } } static void fsi_soft_all_reset(struct fsi_master *master) @@ -608,7 +580,6 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int startup, int stream) struct snd_pcm_substream *substream = NULL; int is_play = fsi_stream_is_play(stream); struct fsi_stream *io = fsi_get_stream(fsi, is_play); - u32 status_reg = is_play ? DOFF_ST : DIFF_ST; int data_residue_num; int data_num; int data_num_max; @@ -699,14 +670,19 @@ static int fsi_fifo_data_ctrl(struct fsi_priv *fsi, int startup, int stream) /* check fifo status */ if (!startup) { struct snd_soc_dai *dai = fsi_get_dai(substream); - u32 status = fsi_reg_read(fsi, status_reg); + u32 status = is_play ? + fsi_reg_read(fsi, DOFF_ST) : + fsi_reg_read(fsi, DIFF_ST); if (status & ERR_OVER) dev_err(dai->dev, "over run\n"); if (status & ERR_UNDER) dev_err(dai->dev, "under run\n"); } - fsi_reg_write(fsi, status_reg, 0); + + is_play ? + fsi_reg_write(fsi, DOFF_ST, 0) : + fsi_reg_write(fsi, DIFF_ST, 0); /* re-enable irq */ fsi_irq_enable(fsi, is_play); @@ -745,7 +721,8 @@ static irqreturn_t fsi_interrupt(int irq, void *data) if (int_st & AB_IO(1, BI_SHIFT)) fsi_data_pop(&master->fsib, 0); - fsi_irq_clear_all_status(master); + fsi_irq_clear_status(&master->fsia); + fsi_irq_clear_status(&master->fsib); return IRQ_HANDLED; } @@ -762,7 +739,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, struct fsi_stream *io; u32 flags = fsi_get_info_flags(fsi); u32 fmt; - u32 reg; u32 data; int is_play = fsi_is_play(substream); int is_master; @@ -794,7 +770,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, /* do fmt, di fmt */ data = 0; - reg = is_play ? DO_FMT : DI_FMT; fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags); switch (fmt) { case SH_FSI_FMT_MONO: @@ -828,16 +803,18 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, dev_err(dai->dev, "This FSI can not use SPDIF\n"); return -EINVAL; } - data = CR_SPDIF; + data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM; io->chan_num = 2; fsi_spdif_clk_ctrl(fsi, 1); - fsi_reg_mask_set(fsi, OUT_SEL, 0x0010, 0x0010); + fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD); break; default: dev_err(dai->dev, "unknown format.\n"); return -EINVAL; } - fsi_reg_write(fsi, reg, data); + is_play ? + fsi_reg_write(fsi, DO_FMT, data) : + fsi_reg_write(fsi, DI_FMT, data); /* irq clear */ fsi_irq_disable(fsi, is_play); @@ -854,10 +831,17 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream, { struct fsi_priv *fsi = fsi_get_priv(substream); int is_play = fsi_is_play(substream); + struct fsi_master *master = fsi_get_master(fsi); + int (*set_rate)(struct device *dev, int is_porta, int rate, int enable); fsi_irq_disable(fsi, is_play); fsi_clk_ctrl(fsi, 0); + set_rate = master->info->set_rate; + if (set_rate && fsi->rate) + set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0); + fsi->rate = 0; + pm_runtime_put_sync(dai->dev); } @@ -891,20 +875,20 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream, { struct fsi_priv *fsi = fsi_get_priv(substream); struct fsi_master *master = fsi_get_master(fsi); - int (*set_rate)(int is_porta, int rate) = master->info->set_rate; + int (*set_rate)(struct device *dev, int is_porta, int rate, int enable); int fsi_ver = master->core->ver; - int is_play = fsi_is_play(substream); + long rate = params_rate(params); int ret; - /* if slave mode, set_rate is not needed */ - if (!fsi_is_master_mode(fsi, is_play)) + set_rate = master->info->set_rate; + if (!set_rate) return 0; - /* it is error if no set_rate */ - if (!set_rate) - return -EIO; + ret = set_rate(dai->dev, fsi_is_port_a(fsi), rate, 1); + if (ret < 0) /* error */ + return ret; - ret = set_rate(fsi_is_port_a(fsi), params_rate(params)); + fsi->rate = rate; if (ret > 0) { u32 data = 0; @@ -1165,12 +1149,10 @@ static int fsi_probe(struct platform_device *pdev) /* FSI A setting */ master->fsia.base = master->base; master->fsia.master = master; - master->fsia.mst_ctrl = A_MST_CTLR; /* FSI B setting */ master->fsib.base = master->base + 0x40; master->fsib.master = master; - master->fsib.mst_ctrl = B_MST_CTLR; pm_runtime_enable(&pdev->dev); pm_runtime_resume(&pdev->dev); @@ -1257,6 +1239,8 @@ static struct fsi_core fsi2_core = { .int_st = CPU_INT_ST, .iemsk = CPU_IEMSK, .imsk = CPU_IMSK, + .a_mclk = A_MST_CTLR, + .b_mclk = B_MST_CTLR, }; static struct platform_device_id fsi_id_table[] = { diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c index 9b1ba33e6fe9..0e17b4050425 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -85,7 +85,7 @@ static int snd_soc_4_12_spi_write(void *control_data, const char *data, msg[1] = data[0]; spi_message_init(&m); - memset(&t, 0, (sizeof t)); + memset(&t, 0, sizeof t); t.tx_buf = &msg[0]; t.len = len; @@ -166,7 +166,7 @@ static int snd_soc_7_9_spi_write(void *control_data, const char *data, msg[1] = data[1]; spi_message_init(&m); - memset(&t, 0, (sizeof t)); + memset(&t, 0, sizeof t); t.tx_buf = &msg[0]; t.len = len; @@ -246,7 +246,7 @@ static int snd_soc_8_8_spi_write(void *control_data, const char *data, msg[1] = data[1]; spi_message_init(&m); - memset(&t, 0, (sizeof t)); + memset(&t, 0, sizeof t); t.tx_buf = &msg[0]; t.len = len; @@ -326,7 +326,7 @@ static int snd_soc_8_16_spi_write(void *control_data, const char *data, msg[2] = data[2]; spi_message_init(&m); - memset(&t, 0, (sizeof t)); + memset(&t, 0, sizeof t); t.tx_buf = &msg[0]; t.len = len; @@ -513,7 +513,7 @@ static int snd_soc_16_8_spi_write(void *control_data, const char *data, msg[2] = data[2]; spi_message_init(&m); - memset(&t, 0, (sizeof t)); + memset(&t, 0, sizeof t); t.tx_buf = &msg[0]; t.len = len; @@ -633,7 +633,7 @@ static int snd_soc_16_16_spi_write(void *control_data, const char *data, msg[3] = data[3]; spi_message_init(&m); - memset(&t, 0, (sizeof t)); + memset(&t, 0, sizeof t); t.tx_buf = &msg[0]; t.len = len; @@ -728,8 +728,8 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec, return -EINVAL; } - codec->driver->write = io_types[i].write; - codec->driver->read = io_types[i].read; + codec->write = io_types[i].write; + codec->read = io_types[i].read; switch (control) { case SND_SOC_CUSTOM: @@ -933,7 +933,7 @@ static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) rbtree_ctx = codec->reg_cache; rbtree_ctx->root = RB_ROOT; - if (!codec->driver->reg_cache_default) + if (!codec->reg_def_copy) return 0; /* @@ -951,7 +951,7 @@ static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec) struct snd_soc_rbtree_node *rbtree_node; \ \ ret = 0; \ - cache = codec->driver->reg_cache_default; \ + cache = codec->reg_def_copy; \ for (i = 0; i < codec->driver->reg_cache_size; ++i) { \ if (!cache[i]) \ continue; \ @@ -1078,7 +1078,7 @@ static int snd_soc_lzo_decompress_cache_block(struct snd_soc_codec *codec, static inline int snd_soc_lzo_get_blkindex(struct snd_soc_codec *codec, unsigned int reg) { - struct snd_soc_codec_driver *codec_drv; + const struct snd_soc_codec_driver *codec_drv; size_t reg_size; codec_drv = codec->driver; @@ -1090,7 +1090,7 @@ static inline int snd_soc_lzo_get_blkindex(struct snd_soc_codec *codec, static inline int snd_soc_lzo_get_blkpos(struct snd_soc_codec *codec, unsigned int reg) { - struct snd_soc_codec_driver *codec_drv; + const struct snd_soc_codec_driver *codec_drv; size_t reg_size; codec_drv = codec->driver; @@ -1101,7 +1101,7 @@ static inline int snd_soc_lzo_get_blkpos(struct snd_soc_codec *codec, static inline int snd_soc_lzo_get_blksize(struct snd_soc_codec *codec) { - struct snd_soc_codec_driver *codec_drv; + const struct snd_soc_codec_driver *codec_drv; size_t reg_size; codec_drv = codec->driver; @@ -1301,7 +1301,7 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) { struct snd_soc_lzo_ctx **lzo_blocks; size_t reg_size, bmp_size; - struct snd_soc_codec_driver *codec_drv; + const struct snd_soc_codec_driver *codec_drv; int ret, tofree, i, blksize, blkcount; const char *p, *end; unsigned long *sync_bmp; @@ -1316,13 +1316,13 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) * and remember to free it afterwards. */ tofree = 0; - if (!codec_drv->reg_cache_default) + if (!codec->reg_def_copy) tofree = 1; - if (!codec_drv->reg_cache_default) { - codec_drv->reg_cache_default = kzalloc(reg_size, + if (!codec->reg_def_copy) { + codec->reg_def_copy = kzalloc(reg_size, GFP_KERNEL); - if (!codec_drv->reg_cache_default) + if (!codec->reg_def_copy) return -ENOMEM; } @@ -1342,13 +1342,13 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) * that register. */ bmp_size = codec_drv->reg_cache_size; - sync_bmp = kmalloc(BITS_TO_LONGS(bmp_size) * sizeof (long), + sync_bmp = kmalloc(BITS_TO_LONGS(bmp_size) * sizeof(long), GFP_KERNEL); if (!sync_bmp) { ret = -ENOMEM; goto err; } - bitmap_zero(sync_bmp, reg_size); + bitmap_zero(sync_bmp, bmp_size); /* allocate the lzo blocks and initialize them */ for (i = 0; i < blkcount; ++i) { @@ -1368,8 +1368,8 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) } blksize = snd_soc_lzo_get_blksize(codec); - p = codec_drv->reg_cache_default; - end = codec_drv->reg_cache_default + reg_size; + p = codec->reg_def_copy; + end = codec->reg_def_copy + reg_size; /* compress the register map and fill the lzo blocks */ for (i = 0; i < blkcount; ++i, p += blksize) { lzo_blocks[i]->src = p; @@ -1385,14 +1385,18 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) lzo_blocks[i]->src_len; } - if (tofree) - kfree(codec_drv->reg_cache_default); + if (tofree) { + kfree(codec->reg_def_copy); + codec->reg_def_copy = NULL; + } return 0; err: snd_soc_cache_exit(codec); err_tofree: - if (tofree) - kfree(codec_drv->reg_cache_default); + if (tofree) { + kfree(codec->reg_def_copy); + codec->reg_def_copy = NULL; + } return ret; } @@ -1400,7 +1404,7 @@ static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec) { int i; int ret; - struct snd_soc_codec_driver *codec_drv; + const struct snd_soc_codec_driver *codec_drv; unsigned int val; codec_drv = codec->driver; @@ -1500,12 +1504,20 @@ static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec) static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) { - struct snd_soc_codec_driver *codec_drv; + const struct snd_soc_codec_driver *codec_drv; size_t reg_size; codec_drv = codec->driver; reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; + /* + * for flat compression, we don't need to keep a copy of the + * original defaults register cache as it will definitely not + * be marked as __devinitconst + */ + kfree(codec->reg_def_copy); + codec->reg_def_copy = NULL; + if (codec_drv->reg_cache_default) codec->reg_cache = kmemdup(codec_drv->reg_cache_default, reg_size, GFP_KERNEL); @@ -1521,6 +1533,7 @@ static int snd_soc_flat_cache_init(struct snd_soc_codec *codec) static const struct snd_soc_cache_ops cache_types[] = { { .id = SND_SOC_FLAT_COMPRESSION, + .name = "flat", .init = snd_soc_flat_cache_init, .exit = snd_soc_flat_cache_exit, .read = snd_soc_flat_cache_read, @@ -1529,6 +1542,7 @@ static const struct snd_soc_cache_ops cache_types[] = { }, { .id = SND_SOC_LZO_COMPRESSION, + .name = "LZO", .init = snd_soc_lzo_cache_init, .exit = snd_soc_lzo_cache_exit, .read = snd_soc_lzo_cache_read, @@ -1537,6 +1551,7 @@ static const struct snd_soc_cache_ops cache_types[] = { }, { .id = SND_SOC_RBTREE_COMPRESSION, + .name = "rbtree", .init = snd_soc_rbtree_cache_init, .exit = snd_soc_rbtree_cache_exit, .read = snd_soc_rbtree_cache_read, @@ -1550,19 +1565,23 @@ int snd_soc_cache_init(struct snd_soc_codec *codec) int i; for (i = 0; i < ARRAY_SIZE(cache_types); ++i) - if (cache_types[i].id == codec->driver->compress_type) + if (cache_types[i].id == codec->compress_type) break; if (i == ARRAY_SIZE(cache_types)) { dev_err(codec->dev, "Could not match compress type: %d\n", - codec->driver->compress_type); + codec->compress_type); return -EINVAL; } mutex_init(&codec->cache_rw_mutex); codec->cache_ops = &cache_types[i]; - if (codec->cache_ops->init) + if (codec->cache_ops->init) { + if (codec->cache_ops->name) + dev_dbg(codec->dev, "Initializing %s cache for %s codec\n", + codec->cache_ops->name, codec->name); return codec->cache_ops->init(codec); + } return -EINVAL; } @@ -1572,8 +1591,12 @@ int snd_soc_cache_init(struct snd_soc_codec *codec) */ int snd_soc_cache_exit(struct snd_soc_codec *codec) { - if (codec->cache_ops && codec->cache_ops->exit) + if (codec->cache_ops && codec->cache_ops->exit) { + if (codec->cache_ops->name) + dev_dbg(codec->dev, "Destroying %s cache for %s codec\n", + codec->cache_ops->name, codec->name); return codec->cache_ops->exit(codec); + } return -EINVAL; } @@ -1645,6 +1668,9 @@ int snd_soc_cache_sync(struct snd_soc_codec *codec) } if (codec->cache_ops && codec->cache_ops->sync) { + if (codec->cache_ops->name) + dev_dbg(codec->dev, "Syncing %s cache for %s codec\n", + codec->cache_ops->name, codec->name); ret = codec->cache_ops->sync(codec); if (!ret) codec->cache_sync = 0; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c8d8dde7f4dd..1dc4b11c1988 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -33,6 +33,7 @@ #include <linux/slab.h> #include <sound/ac97_codec.h> #include <sound/core.h> +#include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -69,25 +70,6 @@ static int pmdown_time = 5000; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); -/* - * This function forces any delayed work to be queued and run. - */ -static int run_delayed_work(struct delayed_work *dwork) -{ - int ret; - - /* cancel any work waiting to be queued. */ - ret = cancel_delayed_work(dwork); - - /* if there was any work waiting then we run it now and - * wait for it's completion */ - if (ret) { - schedule_delayed_work(dwork, 0); - flush_scheduled_work(); - } - return ret; -} - /* codec register dump */ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) { @@ -116,7 +98,7 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf) * the register being volatile and the device being * powered off. */ - ret = codec->driver->read(codec, i); + ret = snd_soc_read(codec, i); if (ret >= 0) count += snprintf(buf + count, PAGE_SIZE - count, @@ -227,7 +209,7 @@ static ssize_t codec_reg_write_file(struct file *file, start++; if (strict_strtoul(start, 16, &value)) return -EINVAL; - codec->driver->write(codec, reg, value); + snd_soc_write(codec, reg, value); return buf_size; } @@ -525,7 +507,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } - /* Check that the codec and cpu DAI's are compatible */ + /* Check that the codec and cpu DAIs are compatible */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { runtime->hw.rate_min = max(codec_dai_drv->playback.rate_min, @@ -874,7 +856,7 @@ codec_err: } /* - * Free's resources allocated by hw_params, can be called multiple times + * Frees resources allocated by hw_params, can be called multiple times */ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { @@ -898,7 +880,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) if (platform->driver->ops->hw_free) platform->driver->ops->hw_free(substream); - /* now free hw params for the DAI's */ + /* now free hw params for the DAIs */ if (codec_dai->driver->ops->hw_free) codec_dai->driver->ops->hw_free(substream, codec_dai); @@ -986,6 +968,7 @@ static int soc_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; int i; /* If the initialization of this soc device failed, there is no codec @@ -1004,7 +987,7 @@ static int soc_suspend(struct device *dev) /* we're going to block userspace touching us until resume completes */ snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D3hot); - /* mute any active DAC's */ + /* mute any active DACs */ for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dai *dai = card->rtd[i].codec_dai; struct snd_soc_dai_driver *drv = dai->driver; @@ -1044,7 +1027,7 @@ static int soc_suspend(struct device *dev) /* close any waiting streams and save state */ for (i = 0; i < card->num_rtd; i++) { - run_delayed_work(&card->rtd[i].delayed_work); + flush_delayed_work_sync(&card->rtd[i].delayed_work); card->rtd[i].codec->dapm.suspend_bias_level = card->rtd[i].codec->dapm.bias_level; } @@ -1064,8 +1047,7 @@ static int soc_suspend(struct device *dev) } /* suspend all CODECs */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_codec *codec = card->rtd[i].codec; + list_for_each_entry(codec, &card->codec_dev_list, card_list) { /* If there are paths active then the CODEC will be held with * bias _ON and should not be suspended. */ if (!codec->suspended && codec->driver->suspend) { @@ -1106,6 +1088,7 @@ static void soc_resume_deferred(struct work_struct *work) struct snd_soc_card *card = container_of(work, struct snd_soc_card, deferred_resume_work); struct platform_device *pdev = to_platform_device(card->dev); + struct snd_soc_codec *codec; int i; /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time, @@ -1131,8 +1114,7 @@ static void soc_resume_deferred(struct work_struct *work) cpu_dai->driver->resume(cpu_dai); } - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_codec *codec = card->rtd[i].codec; + list_for_each_entry(codec, &card->codec_dev_list, card_list) { /* If the CODEC was idle over suspend then it will have been * left with bias OFF or STANDBY and suspended so we must now * resume. Otherwise the suspend was suppressed. @@ -1327,6 +1309,27 @@ out: return 1; } +static void soc_remove_codec(struct snd_soc_codec *codec) +{ + int err; + + if (codec->driver->remove) { + err = codec->driver->remove(codec); + if (err < 0) + dev_err(codec->dev, + "asoc: failed to remove %s: %d\n", + codec->name, err); + } + + /* Make sure all DAPM widgets are freed */ + snd_soc_dapm_free(&codec->dapm); + + soc_cleanup_codec_debugfs(codec); + codec->probed = 0; + list_del(&codec->card_list); + module_put(codec->dev->driver->owner); +} + static void soc_remove_dai_link(struct snd_soc_card *card, int num) { struct snd_soc_pcm_runtime *rtd = &card->rtd[num]; @@ -1338,6 +1341,7 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num) /* unregister the rtd device */ if (rtd->dev_registered) { device_remove_file(&rtd->dev, &dev_attr_pmdown_time); + device_remove_file(&rtd->dev, &dev_attr_codec_reg); device_unregister(&rtd->dev); rtd->dev_registered = 0; } @@ -1366,22 +1370,8 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num) } /* remove the CODEC */ - if (codec && codec->probed) { - if (codec->driver->remove) { - err = codec->driver->remove(codec); - if (err < 0) - printk(KERN_ERR "asoc: failed to remove %s\n", codec->name); - } - - /* Make sure all DAPM widgets are freed */ - snd_soc_dapm_free(&codec->dapm); - - soc_cleanup_codec_debugfs(codec); - device_remove_file(&rtd->dev, &dev_attr_codec_reg); - codec->probed = 0; - list_del(&codec->card_list); - module_put(codec->dev->driver->owner); - } + if (codec && codec->probed) + soc_remove_codec(codec); /* remove the cpu_dai */ if (cpu_dai && cpu_dai->probed) { @@ -1401,11 +1391,11 @@ static void soc_set_name_prefix(struct snd_soc_card *card, { int i; - if (card->prefix_map == NULL) + if (card->codec_conf == NULL) return; - for (i = 0; i < card->num_prefixes; i++) { - struct snd_soc_prefix_map *map = &card->prefix_map[i]; + for (i = 0; i < card->num_configs; i++) { + struct snd_soc_codec_conf *map = &card->codec_conf[i]; if (map->dev_name && !strcmp(codec->name, map->dev_name)) { codec->name_prefix = map->name_prefix; break; @@ -1413,8 +1403,105 @@ static void soc_set_name_prefix(struct snd_soc_card *card, } } +static int soc_probe_codec(struct snd_soc_card *card, + struct snd_soc_codec *codec) +{ + int ret = 0; + + codec->card = card; + codec->dapm.card = card; + soc_set_name_prefix(card, codec); + + if (codec->driver->probe) { + ret = codec->driver->probe(codec); + if (ret < 0) { + dev_err(codec->dev, + "asoc: failed to probe CODEC %s: %d\n", + codec->name, ret); + return ret; + } + } + + soc_init_codec_debugfs(codec); + + /* mark codec as probed and add to card codec list */ + codec->probed = 1; + list_add(&codec->card_list, &card->codec_dev_list); + + return ret; +} + static void rtd_release(struct device *dev) {} +static int soc_post_component_init(struct snd_soc_card *card, + struct snd_soc_codec *codec, + int num, int dailess) +{ + struct snd_soc_dai_link *dai_link = NULL; + struct snd_soc_aux_dev *aux_dev = NULL; + struct snd_soc_pcm_runtime *rtd; + const char *temp, *name; + int ret = 0; + + if (!dailess) { + dai_link = &card->dai_link[num]; + rtd = &card->rtd[num]; + name = dai_link->name; + } else { + aux_dev = &card->aux_dev[num]; + rtd = &card->rtd_aux[num]; + name = aux_dev->name; + } + + /* machine controls, routes and widgets are not prefixed */ + temp = codec->name_prefix; + codec->name_prefix = NULL; + + /* do machine specific initialization */ + if (!dailess && dai_link->init) + ret = dai_link->init(rtd); + else if (dailess && aux_dev->init) + ret = aux_dev->init(&codec->dapm); + if (ret < 0) { + dev_err(card->dev, "asoc: failed to init %s: %d\n", name, ret); + return ret; + } + codec->name_prefix = temp; + + /* Make sure all DAPM widgets are instantiated */ + snd_soc_dapm_new_widgets(&codec->dapm); + snd_soc_dapm_sync(&codec->dapm); + + /* register the rtd device */ + rtd->codec = codec; + rtd->card = card; + rtd->dev.parent = card->dev; + rtd->dev.release = rtd_release; + rtd->dev.init_name = name; + ret = device_register(&rtd->dev); + if (ret < 0) { + dev_err(card->dev, + "asoc: failed to register runtime device: %d\n", ret); + return ret; + } + rtd->dev_registered = 1; + + /* add DAPM sysfs entries for this codec */ + ret = snd_soc_dapm_sys_add(&rtd->dev); + if (ret < 0) + dev_err(codec->dev, + "asoc: failed to add codec dapm sysfs entries: %d\n", + ret); + + /* add codec sysfs entries */ + ret = device_create_file(&rtd->dev, &dev_attr_codec_reg); + if (ret < 0) + dev_err(codec->dev, + "asoc: failed to add codec sysfs files: %d\n", ret); + + return 0; +} + static int soc_probe_dai_link(struct snd_soc_card *card, int num) { struct snd_soc_dai_link *dai_link = &card->dai_link[num]; @@ -1422,17 +1509,13 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) struct snd_soc_codec *codec = rtd->codec; struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai; - const char *temp; int ret; dev_dbg(card->dev, "probe %s dai link %d\n", card->name, num); /* config components */ codec_dai->codec = codec; - codec->card = card; cpu_dai->platform = platform; - rtd->card = card; - rtd->dev.parent = card->dev; codec_dai->card = card; cpu_dai->card = card; @@ -1456,22 +1539,9 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) /* probe the CODEC */ if (!codec->probed) { - codec->dapm.card = card; - soc_set_name_prefix(card, codec); - if (codec->driver->probe) { - ret = codec->driver->probe(codec); - if (ret < 0) { - printk(KERN_ERR "asoc: failed to probe CODEC %s\n", - codec->name); - return ret; - } - } - - soc_init_codec_debugfs(codec); - - /* mark codec as probed and add to card codec list */ - codec->probed = 1; - list_add(&codec->card_list, &card->codec_dev_list); + ret = soc_probe_codec(card, codec); + if (ret < 0) + return ret; } /* probe the platform */ @@ -1508,47 +1578,14 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) /* DAPM dai link stream work */ INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); - /* now that all clients have probed, initialise the DAI link */ - if (dai_link->init) { - /* machine controls, routes and widgets are not prefixed */ - temp = rtd->codec->name_prefix; - rtd->codec->name_prefix = NULL; - ret = dai_link->init(rtd); - if (ret < 0) { - printk(KERN_ERR "asoc: failed to init %s\n", dai_link->stream_name); - return ret; - } - rtd->codec->name_prefix = temp; - } - - /* Make sure all DAPM widgets are instantiated */ - snd_soc_dapm_new_widgets(&codec->dapm); - snd_soc_dapm_sync(&codec->dapm); - - /* register the rtd device */ - rtd->dev.release = rtd_release; - rtd->dev.init_name = dai_link->name; - ret = device_register(&rtd->dev); - if (ret < 0) { - printk(KERN_ERR "asoc: failed to register DAI runtime device %d\n", ret); + ret = soc_post_component_init(card, codec, num, 0); + if (ret) return ret; - } - rtd->dev_registered = 1; ret = device_create_file(&rtd->dev, &dev_attr_pmdown_time); if (ret < 0) printk(KERN_WARNING "asoc: failed to add pmdown_time sysfs\n"); - /* add DAPM sysfs entries for this codec */ - ret = snd_soc_dapm_sys_add(&rtd->dev); - if (ret < 0) - printk(KERN_WARNING "asoc: failed to add codec dapm sysfs entries\n"); - - /* add codec sysfs entries */ - ret = device_create_file(&rtd->dev, &dev_attr_codec_reg); - if (ret < 0) - printk(KERN_WARNING "asoc: failed to add codec sysfs files\n"); - /* create the pcm */ ret = soc_new_pcm(rtd, num); if (ret < 0) { @@ -1603,9 +1640,85 @@ static void soc_unregister_ac97_dai_link(struct snd_soc_codec *codec) } #endif +static int soc_probe_aux_dev(struct snd_soc_card *card, int num) +{ + struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num]; + struct snd_soc_codec *codec; + int ret = -ENODEV; + + /* find CODEC from registered CODECs*/ + list_for_each_entry(codec, &codec_list, list) { + if (!strcmp(codec->name, aux_dev->codec_name)) { + if (codec->probed) { + dev_err(codec->dev, + "asoc: codec already probed"); + ret = -EBUSY; + goto out; + } + goto found; + } + } + /* codec not found */ + dev_err(card->dev, "asoc: codec %s not found", aux_dev->codec_name); + goto out; + +found: + if (!try_module_get(codec->dev->driver->owner)) + return -ENODEV; + + ret = soc_probe_codec(card, codec); + if (ret < 0) + return ret; + + ret = soc_post_component_init(card, codec, num, 1); + +out: + return ret; +} + +static void soc_remove_aux_dev(struct snd_soc_card *card, int num) +{ + struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num]; + struct snd_soc_codec *codec = rtd->codec; + + /* unregister the rtd device */ + if (rtd->dev_registered) { + device_remove_file(&rtd->dev, &dev_attr_codec_reg); + device_unregister(&rtd->dev); + rtd->dev_registered = 0; + } + + if (codec && codec->probed) + soc_remove_codec(codec); +} + +static int snd_soc_init_codec_cache(struct snd_soc_codec *codec, + enum snd_soc_compress_type compress_type) +{ + int ret; + + if (codec->cache_init) + return 0; + + /* override the compress_type if necessary */ + if (compress_type && codec->compress_type != compress_type) + codec->compress_type = compress_type; + ret = snd_soc_cache_init(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to set cache compression type: %d\n", + ret); + return ret; + } + codec->cache_init = 1; + return 0; +} + static void snd_soc_instantiate_card(struct snd_soc_card *card) { struct platform_device *pdev = to_platform_device(card->dev); + struct snd_soc_codec *codec; + struct snd_soc_codec_conf *codec_conf; + enum snd_soc_compress_type compress_type; int ret, i; mutex_lock(&card->mutex); @@ -1625,6 +1738,39 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) return; } + /* initialize the register cache for each available codec */ + list_for_each_entry(codec, &codec_list, list) { + if (codec->cache_init) + continue; + /* check to see if we need to override the compress_type */ + for (i = 0; i < card->num_configs; ++i) { + codec_conf = &card->codec_conf[i]; + if (!strcmp(codec->name, codec_conf->dev_name)) { + compress_type = codec_conf->compress_type; + if (compress_type && compress_type + != codec->compress_type) + break; + } + } + if (i == card->num_configs) { + /* no need to override the compress_type so + * go ahead and do the standard thing */ + ret = snd_soc_init_codec_cache(codec, 0); + if (ret < 0) { + mutex_unlock(&card->mutex); + return; + } + continue; + } + /* override the compress_type with the one supplied in + * the machine driver */ + ret = snd_soc_init_codec_cache(codec, compress_type); + if (ret < 0) { + mutex_unlock(&card->mutex); + return; + } + } + /* card bind complete so register a sound card */ ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, card->owner, 0, &card->snd_card); @@ -1657,6 +1803,15 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) } } + for (i = 0; i < card->num_aux_devs; i++) { + ret = soc_probe_aux_dev(card, i); + if (ret < 0) { + pr_err("asoc: failed to add auxiliary devices %s: %d\n", + card->name, ret); + goto probe_aux_dev_err; + } + } + snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), "%s", card->name); snprintf(card->snd_card->longname, sizeof(card->snd_card->longname), @@ -1665,24 +1820,30 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) ret = snd_card_register(card->snd_card); if (ret < 0) { printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name); - goto probe_dai_err; + goto probe_aux_dev_err; } #ifdef CONFIG_SND_SOC_AC97_BUS /* register any AC97 codecs */ for (i = 0; i < card->num_rtd; i++) { - ret = soc_register_ac97_dai_link(&card->rtd[i]); - if (ret < 0) { - printk(KERN_ERR "asoc: failed to register AC97 %s\n", card->name); - goto probe_dai_err; - } + ret = soc_register_ac97_dai_link(&card->rtd[i]); + if (ret < 0) { + printk(KERN_ERR "asoc: failed to register AC97 %s\n", card->name); + while (--i >= 0) + soc_unregister_ac97_dai_link(card->rtd[i].codec); + goto probe_aux_dev_err; } + } #endif card->instantiated = 1; mutex_unlock(&card->mutex); return; +probe_aux_dev_err: + for (i = 0; i < card->num_aux_devs; i++) + soc_remove_aux_dev(card, i); + probe_dai_err: for (i = 0; i < card->num_links; i++) soc_remove_dai_link(card, i); @@ -1741,9 +1902,13 @@ static int soc_remove(struct platform_device *pdev) /* make sure any delayed work runs */ for (i = 0; i < card->num_rtd; i++) { struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; - run_delayed_work(&rtd->delayed_work); + flush_delayed_work_sync(&rtd->delayed_work); } + /* remove auxiliary devices */ + for (i = 0; i < card->num_aux_devs; i++) + soc_remove_aux_dev(card, i); + /* remove and free each DAI */ for (i = 0; i < card->num_rtd; i++) soc_remove_dai_link(card, i); @@ -1774,7 +1939,7 @@ static int soc_poweroff(struct device *dev) * now, we're shutting down so no imminent restart. */ for (i = 0; i < card->num_rtd; i++) { struct snd_soc_pcm_runtime *rtd = &card->rtd[i]; - run_delayed_work(&rtd->delayed_work); + flush_delayed_work_sync(&rtd->delayed_work); } snd_soc_dapm_shutdown(card); @@ -1937,7 +2102,7 @@ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg) { unsigned int ret; - ret = codec->driver->read(codec, reg); + ret = codec->read(codec, reg); dev_dbg(codec->dev, "read %x => %x\n", reg, ret); trace_snd_soc_reg_read(codec, reg, ret); @@ -1950,7 +2115,7 @@ unsigned int snd_soc_write(struct snd_soc_codec *codec, { dev_dbg(codec->dev, "write %x = %x\n", reg, val); trace_snd_soc_reg_write(codec, reg, val); - return codec->driver->write(codec, reg, val); + return codec->write(codec, reg, val); } EXPORT_SYMBOL_GPL(snd_soc_write); @@ -2946,10 +3111,12 @@ static int snd_soc_register_card(struct snd_soc_card *card) if (!card->name || !card->dev) return -EINVAL; - card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) * card->num_links, - GFP_KERNEL); + card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) * + (card->num_links + card->num_aux_devs), + GFP_KERNEL); if (card->rtd == NULL) return -ENOMEM; + card->rtd_aux = &card->rtd[card->num_links]; for (i = 0; i < card->num_links; i++) card->rtd[i].dai_link = &card->dai_link[i]; @@ -2991,7 +3158,7 @@ static int snd_soc_unregister_card(struct snd_soc_card *card) * Simplify DAI link configuration by removing ".-1" from device names * and sanitizing names. */ -static inline char *fmt_single_name(struct device *dev, int *id) +static char *fmt_single_name(struct device *dev, int *id) { char *found, name[NAME_SIZE]; int id1, id2; @@ -2999,7 +3166,7 @@ static inline char *fmt_single_name(struct device *dev, int *id) if (dev_name(dev) == NULL) return NULL; - strncpy(name, dev_name(dev), NAME_SIZE); + strlcpy(name, dev_name(dev), NAME_SIZE); /* are we a "%s.%d" name (platform and SPI components) */ found = strstr(name, dev->driver->name); @@ -3022,7 +3189,7 @@ static inline char *fmt_single_name(struct device *dev, int *id) /* sanitize component name for DAI link creation */ snprintf(tmp, NAME_SIZE, "%s.%s", dev->driver->name, name); - strncpy(name, tmp, NAME_SIZE); + strlcpy(name, tmp, NAME_SIZE); } else *id = 0; } @@ -3157,7 +3324,9 @@ int snd_soc_register_dais(struct device *dev, pr_debug("Registered DAI '%s'\n", dai->name); } + mutex_lock(&client_mutex); snd_soc_instantiate_cards(); + mutex_unlock(&client_mutex); return 0; err: @@ -3285,9 +3454,11 @@ static void fixup_codec_formats(struct snd_soc_pcm_stream *stream) * @codec: codec to register */ int snd_soc_register_codec(struct device *dev, - struct snd_soc_codec_driver *codec_drv, - struct snd_soc_dai_driver *dai_drv, int num_dai) + const struct snd_soc_codec_driver *codec_drv, + struct snd_soc_dai_driver *dai_drv, + int num_dai) { + size_t reg_size; struct snd_soc_codec *codec; int ret, i; @@ -3304,8 +3475,15 @@ int snd_soc_register_codec(struct device *dev, return -ENOMEM; } + if (codec_drv->compress_type) + codec->compress_type = codec_drv->compress_type; + else + codec->compress_type = SND_SOC_FLAT_COMPRESSION; + INIT_LIST_HEAD(&codec->dapm.widgets); INIT_LIST_HEAD(&codec->dapm.paths); + codec->write = codec_drv->write; + codec->read = codec_drv->read; codec->dapm.bias_level = SND_SOC_BIAS_OFF; codec->dapm.dev = dev; codec->dapm.codec = codec; @@ -3316,11 +3494,18 @@ int snd_soc_register_codec(struct device *dev, /* allocate CODEC register cache */ if (codec_drv->reg_cache_size && codec_drv->reg_word_size) { - ret = snd_soc_cache_init(codec); - if (ret < 0) { - dev_err(codec->dev, "Failed to set cache compression type: %d\n", - ret); - goto error_cache; + reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size; + /* it is necessary to make a copy of the default register cache + * because in the case of using a compression type that requires + * the default register cache to be marked as __devinitconst the + * kernel might have freed the array by the time we initialize + * the cache. + */ + codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default, + reg_size, GFP_KERNEL); + if (!codec->reg_def_copy) { + ret = -ENOMEM; + goto fail; } } @@ -3333,7 +3518,7 @@ int snd_soc_register_codec(struct device *dev, if (num_dai) { ret = snd_soc_register_dais(dev, dai_drv, num_dai); if (ret < 0) - goto error_dais; + goto fail; } mutex_lock(&client_mutex); @@ -3344,9 +3529,9 @@ int snd_soc_register_codec(struct device *dev, pr_debug("Registered codec '%s'\n", codec->name); return 0; -error_dais: - snd_soc_cache_exit(codec); -error_cache: +fail: + kfree(codec->reg_def_copy); + codec->reg_def_copy = NULL; kfree(codec->name); kfree(codec); return ret; @@ -3381,6 +3566,7 @@ found: pr_debug("Unregistered codec '%s'\n", codec->name); snd_soc_cache_exit(codec); + kfree(codec->reg_def_copy); kfree(codec->name); kfree(codec); } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6a29d59dabaf..3d310af28907 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -62,6 +62,7 @@ static int dapm_up_seq[] = { [snd_soc_dapm_mixer_named_ctl] = 7, [snd_soc_dapm_pga] = 8, [snd_soc_dapm_adc] = 9, + [snd_soc_dapm_out_drv] = 10, [snd_soc_dapm_hp] = 10, [snd_soc_dapm_spk] = 10, [snd_soc_dapm_post] = 11, @@ -72,6 +73,7 @@ static int dapm_down_seq[] = { [snd_soc_dapm_adc] = 1, [snd_soc_dapm_hp] = 2, [snd_soc_dapm_spk] = 2, + [snd_soc_dapm_out_drv] = 2, [snd_soc_dapm_pga] = 4, [snd_soc_dapm_mixer_named_ctl] = 5, [snd_soc_dapm_mixer] = 5, @@ -162,6 +164,10 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_card *card, else dapm->bias_level = level; } + if (ret == 0) { + if (card && card->set_bias_level_post) + ret = card->set_bias_level_post(card, level); + } trace_snd_soc_bias_level_done(card, level); @@ -231,6 +237,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, break; /* does not effect routing - always connected */ case snd_soc_dapm_pga: + case snd_soc_dapm_out_drv: case snd_soc_dapm_output: case snd_soc_dapm_adc: case snd_soc_dapm_input: @@ -977,6 +984,9 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) case SND_SOC_DAPM_STREAM_RESUME: sys_power = 1; break; + case SND_SOC_DAPM_STREAM_STOP: + sys_power = !!dapm->codec->active; + break; case SND_SOC_DAPM_STREAM_SUSPEND: sys_power = 0; break; @@ -1243,6 +1253,7 @@ static ssize_t dapm_widget_show(struct device *dev, case snd_soc_dapm_dac: case snd_soc_dapm_adc: case snd_soc_dapm_pga: + case snd_soc_dapm_out_drv: case snd_soc_dapm_mixer: case snd_soc_dapm_mixer_named_ctl: case snd_soc_dapm_supply: @@ -1421,6 +1432,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_adc: case snd_soc_dapm_dac: case snd_soc_dapm_pga: + case snd_soc_dapm_out_drv: case snd_soc_dapm_input: case snd_soc_dapm_output: case snd_soc_dapm_micbias: @@ -1538,6 +1550,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) w->power_check = dapm_dac_check_power; break; case snd_soc_dapm_pga: + case snd_soc_dapm_out_drv: w->power_check = dapm_generic_check_power; dapm_new_pga(dapm, w); break; diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 0e9b0710928b..ac5a5bc7375a 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -17,6 +17,7 @@ #include <linux/interrupt.h> #include <linux/workqueue.h> #include <linux/delay.h> +#include <trace/events/asoc.h> /** * snd_soc_jack_new - Create a new jack @@ -64,6 +65,8 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) int enable; int oldstatus; + trace_snd_soc_jack_report(jack, mask, status); + if (!jack) return; @@ -82,6 +85,8 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) if (mask && (jack->status == oldstatus)) goto out; + trace_snd_soc_jack_notify(jack, status); + list_for_each_entry(pin, &jack->pins, list) { enable = pin->mask & jack->status; @@ -210,6 +215,8 @@ static irqreturn_t gpio_handler(int irq, void *data) struct snd_soc_jack_gpio *gpio = data; struct device *dev = gpio->jack->codec->card->dev; + trace_snd_soc_jack_irq(gpio->name); + if (device_may_wakeup(dev)) pm_wakeup_event(dev, gpio->debounce_time + 50); |