diff options
-rw-r--r-- | Documentation/devicetree/bindings/sound/wm8580.txt | 4 | ||||
-rw-r--r-- | sound/soc/codecs/Kconfig | 2 | ||||
-rw-r--r-- | sound/soc/codecs/wm8580.c | 98 |
3 files changed, 92 insertions, 12 deletions
diff --git a/Documentation/devicetree/bindings/sound/wm8580.txt b/Documentation/devicetree/bindings/sound/wm8580.txt index 7d9821f348da..78fce9b14954 100644 --- a/Documentation/devicetree/bindings/sound/wm8580.txt +++ b/Documentation/devicetree/bindings/sound/wm8580.txt @@ -1,10 +1,10 @@ -WM8580 audio CODEC +WM8580 and WM8581 audio CODEC This device supports I2C only. Required properties: - - compatible : "wlf,wm8580" + - compatible : "wlf,wm8580", "wlf,wm8581" - reg : the I2C address of the device. diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c67667bb970f..199bec88d4ae 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -914,7 +914,7 @@ config SND_SOC_WM8523 depends on I2C config SND_SOC_WM8580 - tristate "Wolfson Microelectronics WM8523 CODEC" + tristate "Wolfson Microelectronics WM8580 and WM8581 CODECs" depends on I2C config SND_SOC_WM8711 diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index faa7287a5253..aecd3c99e604 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -1,5 +1,5 @@ /* - * wm8580.c -- WM8580 ALSA Soc Audio driver + * wm8580.c -- WM8580 and WM8581 ALSA Soc Audio driver * * Copyright 2008-12 Wolfson Microelectronics PLC. * @@ -12,6 +12,9 @@ * The WM8580 is a multichannel codec with S/PDIF support, featuring six * DAC channels and two ADC channels. * + * The WM8581 is a multichannel codec with S/PDIF support, featuring eight + * DAC channels and two ADC channels. + * * Currently only the primary audio interface is supported - S/PDIF and * the secondary audio interfaces are not. */ @@ -65,6 +68,8 @@ #define WM8580_DIGITAL_ATTENUATION_DACR2 0x17 #define WM8580_DIGITAL_ATTENUATION_DACL3 0x18 #define WM8580_DIGITAL_ATTENUATION_DACR3 0x19 +#define WM8581_DIGITAL_ATTENUATION_DACL4 0x1A +#define WM8581_DIGITAL_ATTENUATION_DACR4 0x1B #define WM8580_MASTER_DIGITAL_ATTENUATION 0x1C #define WM8580_ADC_CONTROL1 0x1D #define WM8580_SPDTXCHAN0 0x1E @@ -236,12 +241,17 @@ static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = { "PVDD", }; +struct wm8580_driver_data { + int num_dacs; +}; + /* codec private data */ struct wm8580_priv { struct regmap *regmap; struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES]; struct pll_state a; struct pll_state b; + const struct wm8580_driver_data *drvdata; int sysclk[2]; }; @@ -306,6 +316,19 @@ SOC_DOUBLE("Capture Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 1), SOC_SINGLE("Capture High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0), }; +static const struct snd_kcontrol_new wm8581_snd_controls[] = { +SOC_DOUBLE_R_EXT_TLV("DAC4 Playback Volume", + WM8581_DIGITAL_ATTENUATION_DACL4, + WM8581_DIGITAL_ATTENUATION_DACR4, + 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv), + +SOC_SINGLE("DAC4 Deemphasis Switch", WM8580_DAC_CONTROL3, 3, 1, 0), + +SOC_DOUBLE("DAC4 Invert Switch", WM8580_DAC_CONTROL4, 8, 7, 1, 0), + +SOC_SINGLE("DAC4 Switch", WM8580_DAC_CONTROL5, 3, 1, 1), +}; + static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = { SND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1), SND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1), @@ -324,6 +347,13 @@ SND_SOC_DAPM_INPUT("AINL"), SND_SOC_DAPM_INPUT("AINR"), }; +static const struct snd_soc_dapm_widget wm8581_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC4", "Playback", WM8580_PWRDN1, 5, 1), + +SND_SOC_DAPM_OUTPUT("VOUT4L"), +SND_SOC_DAPM_OUTPUT("VOUT4R"), +}; + static const struct snd_soc_dapm_route wm8580_dapm_routes[] = { { "VOUT1L", NULL, "DAC1" }, { "VOUT1R", NULL, "DAC1" }, @@ -338,6 +368,11 @@ static const struct snd_soc_dapm_route wm8580_dapm_routes[] = { { "ADC", NULL, "AINR" }, }; +static const struct snd_soc_dapm_route wm8581_dapm_routes[] = { + { "VOUT4L", NULL, "DAC4" }, + { "VOUT4R", NULL, "DAC4" }, +}; + /* PLL divisors */ struct _pll_div { u32 prescale:1; @@ -815,10 +850,21 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec, return 0; } +static int wm8580_playback_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); + + return snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, 1, wm8580->drvdata->num_dacs * 2); +} + #define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops wm8580_dai_ops_playback = { + .startup = wm8580_playback_startup, .set_sysclk = wm8580_set_sysclk, .hw_params = wm8580_paif_hw_params, .set_fmt = wm8580_set_paif_dai_fmt, @@ -842,7 +888,6 @@ static struct snd_soc_dai_driver wm8580_dai[] = { .playback = { .stream_name = "Playback", .channels_min = 1, - .channels_max = 6, .rates = SNDRV_PCM_RATE_8000_192000, .formats = WM8580_FORMATS, }, @@ -865,8 +910,22 @@ static struct snd_soc_dai_driver wm8580_dai[] = { static int wm8580_probe(struct snd_soc_codec *codec) { struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); int ret = 0; + switch (wm8580->drvdata->num_dacs) { + case 4: + snd_soc_add_codec_controls(codec, wm8581_snd_controls, + ARRAY_SIZE(wm8581_snd_controls)); + snd_soc_dapm_new_controls(dapm, wm8581_dapm_widgets, + ARRAY_SIZE(wm8581_dapm_widgets)); + snd_soc_dapm_add_routes(dapm, wm8581_dapm_routes, + ARRAY_SIZE(wm8581_dapm_routes)); + break; + default: + break; + } + ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); if (ret != 0) { @@ -914,12 +973,6 @@ static const struct snd_soc_codec_driver soc_codec_dev_wm8580 = { }, }; -static const struct of_device_id wm8580_of_match[] = { - { .compatible = "wlf,wm8580" }, - { }, -}; -MODULE_DEVICE_TABLE(of, wm8580_of_match); - static const struct regmap_config wm8580_regmap = { .reg_bits = 7, .val_bits = 9, @@ -932,10 +985,26 @@ static const struct regmap_config wm8580_regmap = { .volatile_reg = wm8580_volatile, }; +const struct wm8580_driver_data wm8580_data = { + .num_dacs = 3, +}; + +const struct wm8580_driver_data wm8581_data = { + .num_dacs = 4, +}; + +static const struct of_device_id wm8580_of_match[] = { + { .compatible = "wlf,wm8580", .data = &wm8580_data }, + { .compatible = "wlf,wm8581", .data = &wm8581_data }, + { }, +}; +MODULE_DEVICE_TABLE(of, wm8580_of_match); + #if IS_ENABLED(CONFIG_I2C) static int wm8580_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + const struct of_device_id *of_id; struct wm8580_priv *wm8580; int ret, i; @@ -960,6 +1029,15 @@ static int wm8580_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, wm8580); + of_id = of_match_device(wm8580_of_match, &i2c->dev); + if (of_id) + wm8580->drvdata = of_id->data; + + if (!wm8580->drvdata) { + dev_err(&i2c->dev, "failed to find driver data\n"); + return -EINVAL; + } + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8580, wm8580_dai, ARRAY_SIZE(wm8580_dai)); @@ -973,7 +1051,8 @@ static int wm8580_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id wm8580_i2c_id[] = { - { "wm8580", 0 }, + { "wm8580", (kernel_ulong_t)&wm8580_data }, + { "wm8581", (kernel_ulong_t)&wm8581_data }, { } }; MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id); @@ -1014,4 +1093,5 @@ module_exit(wm8580_exit); MODULE_DESCRIPTION("ASoC WM8580 driver"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_AUTHOR("Matt Flax <flatmax@flatmax.org>"); MODULE_LICENSE("GPL"); |