diff options
author | Takashi Iwai <tiwai@suse.de> | 2010-12-04 08:53:35 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2010-12-04 08:53:35 +0100 |
commit | 792e5f1aa488252509bd354035a9ff1354fe4a25 (patch) | |
tree | 7ffba4793835e3e825883539a5d6a38f391593d0 /sound | |
parent | b70e9def40dea91c0d8fbef8960b2ed7ee88a231 (diff) | |
parent | 28216bf48bbe4314a7a02b7c9822dbb26aca530c (diff) |
Merge branch 'topic/asoc' into for-next
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/88pm860x-codec.c | 12 | ||||
-rw-r--r-- | sound/soc/codecs/ak4535.c | 10 | ||||
-rw-r--r-- | sound/soc/codecs/alc5623.c | 10 | ||||
-rw-r--r-- | sound/soc/codecs/wm8580.c | 8 | ||||
-rw-r--r-- | sound/soc/codecs/wm8731.c | 86 | ||||
-rw-r--r-- | sound/soc/codecs/wm8994.c | 129 | ||||
-rw-r--r-- | sound/soc/sh/fsi-ak4642.c | 140 | ||||
-rw-r--r-- | sound/soc/sh/fsi.c | 207 | ||||
-rw-r--r-- | sound/soc/soc-cache.c | 58 | ||||
-rw-r--r-- | sound/soc/soc-core.c | 275 |
10 files changed, 714 insertions, 221 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/wm8580.c b/sound/soc/codecs/wm8580.c index e2a927684b48..cb7765f7687d 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -507,13 +507,13 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream, } /* 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 94e0cf3a66a1..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; } diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index bfd4cf4fde5f..b37e95c4e7c7 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; @@ -543,8 +548,9 @@ static const struct soc_enum aif2dacr_src = 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 +593,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 +667,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) { @@ -1205,10 +1257,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 +1268,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 +1677,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 +1859,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 */ @@ -2539,6 +2610,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/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..19a2f99be60c 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 @@ -131,8 +142,6 @@ struct fsi_priv { struct fsi_stream playback; struct fsi_stream capture; - - u32 mst_ctrl; }; struct fsi_core { @@ -141,6 +150,8 @@ struct fsi_core { u32 int_st; u32 iemsk; u32 imsk; + u32 a_mclk; + u32 b_mclk; }; struct fsi_master { @@ -180,62 +191,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; - } +#define fsi_reg_write(p, r, d)\ + __fsi_reg_write((u32)(p->base + REG_##r), d) - __fsi_reg_write((u32)(fsi->base + reg), data); -} +#define fsi_reg_read(p, r)\ + __fsi_reg_read((u32)(p->base + REG_##r)) -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_mask_set(p, r, m, d)\ + __fsi_reg_mask_set((u32)(p->base + REG_##r), m, d) - return __fsi_reg_read((u32)(fsi->base + reg)); -} - -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 +214,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 +340,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 +440,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 +449,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 +467,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 +478,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 +514,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 +547,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 +578,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 +668,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 +719,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 +737,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 +768,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 +801,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); @@ -1165,12 +1140,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 +1230,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..ff2bc8beddaa 100644 --- a/sound/soc/soc-cache.c +++ b/sound/soc/soc-cache.c @@ -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; } @@ -1348,7 +1348,7 @@ static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec) 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); @@ -1550,11 +1562,11 @@ 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; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c8d8dde7f4dd..78a01014079a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -525,7 +525,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 +874,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 +898,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 +986,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 +1005,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; @@ -1064,8 +1065,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 +1106,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 +1132,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. @@ -1401,11 +1401,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; @@ -1603,9 +1603,163 @@ 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_pcm_runtime *rtd = &card->rtd_aux[num]; + struct snd_soc_codec *codec; + const char *temp; + 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; + + 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"); + 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); + + /* now that all clients have probed, initialise the DAI link */ + if (aux_dev->init) { + /* machine controls, routes and widgets are not prefixed */ + temp = codec->name_prefix; + codec->name_prefix = NULL; + ret = aux_dev->init(&codec->dapm); + if (ret < 0) { + dev_err(codec->dev, + "asoc: failed to init %s\n", aux_dev->name); + 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 = aux_dev->name; + ret = device_register(&rtd->dev); + if (ret < 0) { + dev_err(codec->dev, + "asoc: failed to register aux 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\n"); + + /* 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\n"); + +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; + int err; + + /* unregister the rtd device */ + if (rtd->dev_registered) { + device_unregister(&rtd->dev); + rtd->dev_registered = 0; + } + + /* remove the CODEC */ + if (codec && codec->probed) { + if (codec->driver->remove) { + err = codec->driver->remove(codec); + if (err < 0) + dev_err(codec->dev, + "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); + } +} + +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; + dev_dbg(codec->dev, "Cache compress_type for %s is %d\n", + codec->name, codec->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 +1779,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 +1844,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), @@ -1683,6 +1879,10 @@ static void snd_soc_instantiate_card(struct snd_soc_card *card) 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); @@ -1744,6 +1944,10 @@ static int soc_remove(struct platform_device *pdev) run_delayed_work(&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); @@ -1937,7 +2141,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 +2154,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 +3150,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]; @@ -3285,9 +3491,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 +3512,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 +3531,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 +3555,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 +3566,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 +3603,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); } |