diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-11 13:20:50 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-11 13:20:50 -0800 |
commit | bae41e45b7400496b9bf0c70c6004419d9987819 (patch) | |
tree | cf22a65d119da1c414dbc79518857800fbe7a24b /sound/soc/codecs/max98090.c | |
parent | 7ef58b32f571bffb7763c6252ad7527562081f34 (diff) | |
parent | 6e1d7a51392f06899bd7b693f28ac60fa1e00032 (diff) | |
download | op-kernel-dev-bae41e45b7400496b9bf0c70c6004419d9987819.zip op-kernel-dev-bae41e45b7400496b9bf0c70c6004419d9987819.tar.gz |
Merge tag 'sound-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"This became a fairly large pull request. In addition to the usual
driver updates / fixes, there have been a high amount of cleanups in
ASoC area, as well as control API helpers and kernel documentations
fixes touching through the whole tree.
In the driver side, the biggest changes are the support for new Intel
SoC found on new x86 machines, and the updates of FireWire dice and
oxfw drivers.
Some remarkable items are below:
ALSA core:
- PCM mmap code cleanup, removal of arch-dependent codes
- PCM xrun injection support
- PCM hwptr tracepoint support
- Refactoring of snd_pcm_action(), simplification of PCM locking
- Robustified sequecner auto-load functionality
- New control API helpers and lots of cleanups along with them
- Lots of kerneldoc fixes and cleanups
USB-audio:
- The mixer resume code was largely rewritten, and the devices with
quirks are resumed properly.
- New hardware support: Focusrite Scarlett, Digidesign Mbox1,
Denon/Marantz DACs, Zoom R16/24
FireWire:
- DICE driver updates with better duplex and sync support, including
MIDI support
- New OXFW driver for Oxford Semiconductor FW970/971 chipset,
including the previous LaCie Speakers device. Fullduplex and MIDI
support included as well as DICE driver.
HD-audio:
- Refactoring the driver-caps quirk handling in snd-hda-intel
- More consistent control names representing the topology better
- Fixups: HP mute LED with ALC268 codec, Ideapad S210 built-in mic
fix, ASUS Z99He laptop EAPD
ASoC:
- Conversion of AC'97 drivers to use regmap, bringing us closer to
the removal of the ASoC level I/O code
- Clean up a lot of old drivers that were open coding things that
have subsequently been implemented in the core
- Some DAPM performance improvements
- Removal of the now seldom used CODEC mutex
- Lots of updates for the newer Intel SoC support, including support
for the DSP and some Cherrytrail and Braswell machine drivers
- Support for Samsung boards using rt5631 as the CODEC
- Removal of the obsolete AFEB9260 machine driver
- Driver support for the TI TS3A227E headset driver used in some
Chrombeooks
Others:
- ASIHPI driver update and cleanups
- Lots of dev_*() printk conversions
- Lots of trivial cleanups for the codes spotted by Coccinelle"
* tag 'sound-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (594 commits)
ALSA: pcxhr: NULL dereference on probe failure
ALSA: lola: NULL dereference on probe failure
ALSA: hda - Add "eapd" model string for AD1986A codec
ALSA: hda - Add EAPD fixup for ASUS Z99He laptop
ALSA: oxfw: Add hwdep interface
ALSA: oxfw: Add support for capture/playback MIDI messages
ALSA: oxfw: add support for capturing PCM samples
ALSA: oxfw: Add support AMDTP in-stream
ALSA: oxfw: Add support for Behringer/Mackie devices
ALSA: oxfw: Change the way to start stream
ALSA: oxfw: Add proc interface for debugging purpose
ALSA: oxfw: Change the way to make PCM rules/constraints
ALSA: oxfw: Add support for AV/C stream format command to get/set supported stream formation
ALSA: oxfw: Change the way to name card
ALSA: dice: Add support for MIDI capture/playback
ALSA: dice: Add support for capturing PCM samples
ALSA: dice: Support for non SYT-Match sampling clock source mode
ALSA: dice: Add support for duplex streams with synchronization
ALSA: dice: Change the way to start stream
ALSA: jack: Add dummy snd_jack_set_key() definition
...
Diffstat (limited to 'sound/soc/codecs/max98090.c')
-rw-r--r-- | sound/soc/codecs/max98090.c | 201 |
1 files changed, 160 insertions, 41 deletions
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 1229554..151f718 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -806,7 +806,7 @@ static const struct snd_kcontrol_new max98091_snd_controls[] = { static int max98090_micinput_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); unsigned int val = snd_soc_read(codec, w->reg); @@ -1311,6 +1311,10 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = { {"MIC1 Input", NULL, "MIC1"}, {"MIC2 Input", NULL, "MIC2"}, + {"DMICL", NULL, "DMICL_ENA"}, + {"DMICL", NULL, "DMICR_ENA"}, + {"DMICR", NULL, "DMICL_ENA"}, + {"DMICR", NULL, "DMICR_ENA"}, {"DMICL", NULL, "AHPF"}, {"DMICR", NULL, "AHPF"}, @@ -1368,8 +1372,6 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = { {"DMIC Mux", "ADC", "ADCR"}, {"DMIC Mux", "DMIC", "DMICL"}, {"DMIC Mux", "DMIC", "DMICR"}, - {"DMIC Mux", "DMIC", "DMICL_ENA"}, - {"DMIC Mux", "DMIC", "DMICR_ENA"}, {"LBENL Mux", "Normal", "DMIC Mux"}, {"LBENL Mux", "Loopback", "LTENL Mux"}, @@ -1395,8 +1397,8 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = { {"STENL Mux", "Sidetone Left", "DMICL"}, {"STENR Mux", "Sidetone Right", "ADCR"}, {"STENR Mux", "Sidetone Right", "DMICR"}, - {"DACL", "NULL", "STENL Mux"}, - {"DACR", "NULL", "STENL Mux"}, + {"DACL", NULL, "STENL Mux"}, + {"DACR", NULL, "STENR Mux"}, {"AIFINL", NULL, "SHDN"}, {"AIFINR", NULL, "SHDN"}, @@ -1826,27 +1828,155 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec, return 0; } -static const int comp_pclk_rates[] = { - 11289600, 12288000, 12000000, 13000000, 19200000 -}; - -static const int dmic_micclk[] = { - 2, 2, 2, 2, 4, 2 -}; +static const int dmic_divisors[] = { 2, 3, 4, 5, 6, 8 }; static const int comp_lrclk_rates[] = { 8000, 16000, 32000, 44100, 48000, 96000 }; -static const int dmic_comp[6][6] = { - {7, 8, 3, 3, 3, 3}, - {7, 8, 3, 3, 3, 3}, - {7, 8, 3, 3, 3, 3}, - {7, 8, 3, 1, 1, 1}, - {7, 8, 3, 1, 2, 2}, - {7, 8, 3, 3, 3, 3} +struct dmic_table { + int pclk; + struct { + int freq; + int comp[6]; /* One each for 8, 16, 32, 44.1, 48, and 96 kHz */ + } settings[6]; /* One for each dmic divisor. */ }; +static const struct dmic_table dmic_table[] = { /* One for each pclk freq. */ + { + .pclk = 11289600, + .settings = { + { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + }, + }, + { + .pclk = 12000000, + .settings = { + { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + } + }, + { + .pclk = 12288000, + .settings = { + { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } }, + } + }, + { + .pclk = 13000000, + .settings = { + { .freq = 2, .comp = { 7, 8, 1, 1, 1, 1 } }, + { .freq = 1, .comp = { 7, 8, 0, 0, 0, 0 } }, + { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } }, + { .freq = 0, .comp = { 7, 8, 4, 4, 5, 5 } }, + { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } }, + { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } }, + } + }, + { + .pclk = 19200000, + .settings = { + { .freq = 2, .comp = { 0, 0, 0, 0, 0, 0 } }, + { .freq = 1, .comp = { 7, 8, 1, 1, 1, 1 } }, + { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } }, + { .freq = 0, .comp = { 7, 8, 2, 2, 3, 3 } }, + { .freq = 0, .comp = { 7, 8, 1, 1, 2, 2 } }, + { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } }, + } + }, +}; + +static int max98090_find_divisor(int target_freq, int pclk) +{ + int current_diff = INT_MAX; + int test_diff = INT_MAX; + int divisor_index = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(dmic_divisors); i++) { + test_diff = abs(target_freq - (pclk / dmic_divisors[i])); + if (test_diff < current_diff) { + current_diff = test_diff; + divisor_index = i; + } + } + + return divisor_index; +} + +static int max98090_find_closest_pclk(int pclk) +{ + int m1; + int m2; + int i; + + for (i = 0; i < ARRAY_SIZE(dmic_table); i++) { + if (pclk == dmic_table[i].pclk) + return i; + if (pclk < dmic_table[i].pclk) { + if (i == 0) + return i; + m1 = pclk - dmic_table[i-1].pclk; + m2 = dmic_table[i].pclk - pclk; + if (m1 < m2) + return i - 1; + else + return i; + } + } + + return -EINVAL; +} + +static int max98090_configure_dmic(struct max98090_priv *max98090, + int target_dmic_clk, int pclk, int fs) +{ + int micclk_index; + int pclk_index; + int dmic_freq; + int dmic_comp; + int i; + + pclk_index = max98090_find_closest_pclk(pclk); + if (pclk_index < 0) + return pclk_index; + + micclk_index = max98090_find_divisor(target_dmic_clk, pclk); + + for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) { + if (fs <= (comp_lrclk_rates[i] + comp_lrclk_rates[i+1]) / 2) + break; + } + + dmic_freq = dmic_table[pclk_index].settings[micclk_index].freq; + dmic_comp = dmic_table[pclk_index].settings[micclk_index].comp[i]; + + regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_ENABLE, + M98090_MICCLK_MASK, + micclk_index << M98090_MICCLK_SHIFT); + + regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_CONFIG, + M98090_DMIC_COMP_MASK | M98090_DMIC_FREQ_MASK, + dmic_comp << M98090_DMIC_COMP_SHIFT | + dmic_freq << M98090_DMIC_FREQ_SHIFT); + + return 0; +} + static int max98090_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -1854,7 +1984,6 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_codec *codec = dai->codec; struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); struct max98090_cdata *cdata; - int i, j; cdata = &max98090->dai[0]; max98090->bclk = snd_soc_params_to_bclk(params); @@ -1893,27 +2022,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream, snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG, M98090_DHF_MASK, M98090_DHF_MASK); - /* Check for supported PCLK to LRCLK ratios */ - for (j = 0; j < ARRAY_SIZE(comp_pclk_rates); j++) { - if (comp_pclk_rates[j] == max98090->sysclk) { - break; - } - } - - for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) { - if (max98090->lrclk <= (comp_lrclk_rates[i] + - comp_lrclk_rates[i + 1]) / 2) { - break; - } - } - - snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_ENABLE, - M98090_MICCLK_MASK, - dmic_micclk[j] << M98090_MICCLK_SHIFT); - - snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_CONFIG, - M98090_DMIC_COMP_MASK, - dmic_comp[j][i] << M98090_DMIC_COMP_SHIFT); + max98090_configure_dmic(max98090, max98090->dmic_freq, max98090->pclk, + max98090->lrclk); return 0; } @@ -1944,12 +2054,15 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai, if ((freq >= 10000000) && (freq <= 20000000)) { snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, M98090_PSCLK_DIV1); + max98090->pclk = freq; } else if ((freq > 20000000) && (freq <= 40000000)) { snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, M98090_PSCLK_DIV2); + max98090->pclk = freq >> 1; } else if ((freq > 40000000) && (freq <= 60000000)) { snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, M98090_PSCLK_DIV4); + max98090->pclk = freq >> 2; } else { dev_err(codec->dev, "Invalid master clock frequency\n"); return -EINVAL; @@ -2324,6 +2437,7 @@ static int max98090_probe(struct snd_soc_codec *codec) /* Initialize private data */ max98090->sysclk = (unsigned)-1; + max98090->pclk = (unsigned)-1; max98090->master = false; cdata = &max98090->dai[0]; @@ -2463,6 +2577,11 @@ static int max98090_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, max98090); max98090->pdata = i2c->dev.platform_data; + ret = of_property_read_u32(i2c->dev.of_node, "maxim,dmic-freq", + &max98090->dmic_freq); + if (ret < 0) + max98090->dmic_freq = MAX98090_DEFAULT_DMIC_FREQ; + max98090->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap); if (IS_ERR(max98090->regmap)) { ret = PTR_ERR(max98090->regmap); |