Skip to content

Commit 028f5a5

Browse files
flatmaxbroonie
authored andcommitted
ASoC: wm8580: Add the wm8581 codec to the driver
This patch adds support for the wm8581 codec to the wm8580 driver. The wm8581 codec hardware adds a fourth DAC and otherwise is compatible with the wm8580 codec. of_device_id data is used to allow the driver to select the suitable DAC count specified in the device tree codec selection. The wm8580_driver_data struct is used to store the number of DACs. The snd_soc_dai_driver no longer lists the channels_max for the playback substream. This variable is set during the i2c probe from the of_device_id supplied wm8580_driver_data struct. With knowledge of the number of DACs in use, the DAC4 controls, widgets and routes are added as required for DAC4. The device tree documentation for the wm8580 is altered to list the wm8581 codec support, as is the Kconfig file. Signed-off-by: Matt Flax <[email protected]> Reviewed-by: Charles Keepax <[email protected]> Signed-off-by: Mark Brown <[email protected]>
1 parent 1001354 commit 028f5a5

File tree

3 files changed

+92
-12
lines changed

3 files changed

+92
-12
lines changed

Documentation/devicetree/bindings/sound/wm8580.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
WM8580 audio CODEC
1+
WM8580 and WM8581 audio CODEC
22

33
This device supports I2C only.
44

55
Required properties:
66

7-
- compatible : "wlf,wm8580"
7+
- compatible : "wlf,wm8580", "wlf,wm8581"
88

99
- reg : the I2C address of the device.
1010

sound/soc/codecs/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -914,7 +914,7 @@ config SND_SOC_WM8523
914914
depends on I2C
915915

916916
config SND_SOC_WM8580
917-
tristate "Wolfson Microelectronics WM8523 CODEC"
917+
tristate "Wolfson Microelectronics WM8580 and WM8581 CODECs"
918918
depends on I2C
919919

920920
config SND_SOC_WM8711

sound/soc/codecs/wm8580.c

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* wm8580.c -- WM8580 ALSA Soc Audio driver
2+
* wm8580.c -- WM8580 and WM8581 ALSA Soc Audio driver
33
*
44
* Copyright 2008-12 Wolfson Microelectronics PLC.
55
*
@@ -12,6 +12,9 @@
1212
* The WM8580 is a multichannel codec with S/PDIF support, featuring six
1313
* DAC channels and two ADC channels.
1414
*
15+
* The WM8581 is a multichannel codec with S/PDIF support, featuring eight
16+
* DAC channels and two ADC channels.
17+
*
1518
* Currently only the primary audio interface is supported - S/PDIF and
1619
* the secondary audio interfaces are not.
1720
*/
@@ -65,6 +68,8 @@
6568
#define WM8580_DIGITAL_ATTENUATION_DACR2 0x17
6669
#define WM8580_DIGITAL_ATTENUATION_DACL3 0x18
6770
#define WM8580_DIGITAL_ATTENUATION_DACR3 0x19
71+
#define WM8581_DIGITAL_ATTENUATION_DACL4 0x1A
72+
#define WM8581_DIGITAL_ATTENUATION_DACR4 0x1B
6873
#define WM8580_MASTER_DIGITAL_ATTENUATION 0x1C
6974
#define WM8580_ADC_CONTROL1 0x1D
7075
#define WM8580_SPDTXCHAN0 0x1E
@@ -236,12 +241,17 @@ static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = {
236241
"PVDD",
237242
};
238243

244+
struct wm8580_driver_data {
245+
int num_dacs;
246+
};
247+
239248
/* codec private data */
240249
struct wm8580_priv {
241250
struct regmap *regmap;
242251
struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES];
243252
struct pll_state a;
244253
struct pll_state b;
254+
const struct wm8580_driver_data *drvdata;
245255
int sysclk[2];
246256
};
247257

@@ -306,6 +316,19 @@ SOC_DOUBLE("Capture Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 1),
306316
SOC_SINGLE("Capture High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0),
307317
};
308318

319+
static const struct snd_kcontrol_new wm8581_snd_controls[] = {
320+
SOC_DOUBLE_R_EXT_TLV("DAC4 Playback Volume",
321+
WM8581_DIGITAL_ATTENUATION_DACL4,
322+
WM8581_DIGITAL_ATTENUATION_DACR4,
323+
0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv),
324+
325+
SOC_SINGLE("DAC4 Deemphasis Switch", WM8580_DAC_CONTROL3, 3, 1, 0),
326+
327+
SOC_DOUBLE("DAC4 Invert Switch", WM8580_DAC_CONTROL4, 8, 7, 1, 0),
328+
329+
SOC_SINGLE("DAC4 Switch", WM8580_DAC_CONTROL5, 3, 1, 1),
330+
};
331+
309332
static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = {
310333
SND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1),
311334
SND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1),
@@ -324,6 +347,13 @@ SND_SOC_DAPM_INPUT("AINL"),
324347
SND_SOC_DAPM_INPUT("AINR"),
325348
};
326349

350+
static const struct snd_soc_dapm_widget wm8581_dapm_widgets[] = {
351+
SND_SOC_DAPM_DAC("DAC4", "Playback", WM8580_PWRDN1, 5, 1),
352+
353+
SND_SOC_DAPM_OUTPUT("VOUT4L"),
354+
SND_SOC_DAPM_OUTPUT("VOUT4R"),
355+
};
356+
327357
static const struct snd_soc_dapm_route wm8580_dapm_routes[] = {
328358
{ "VOUT1L", NULL, "DAC1" },
329359
{ "VOUT1R", NULL, "DAC1" },
@@ -338,6 +368,11 @@ static const struct snd_soc_dapm_route wm8580_dapm_routes[] = {
338368
{ "ADC", NULL, "AINR" },
339369
};
340370

371+
static const struct snd_soc_dapm_route wm8581_dapm_routes[] = {
372+
{ "VOUT4L", NULL, "DAC4" },
373+
{ "VOUT4R", NULL, "DAC4" },
374+
};
375+
341376
/* PLL divisors */
342377
struct _pll_div {
343378
u32 prescale:1;
@@ -815,10 +850,21 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec,
815850
return 0;
816851
}
817852

853+
static int wm8580_playback_startup(struct snd_pcm_substream *substream,
854+
struct snd_soc_dai *dai)
855+
{
856+
struct snd_soc_codec *codec = dai->codec;
857+
struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
858+
859+
return snd_pcm_hw_constraint_minmax(substream->runtime,
860+
SNDRV_PCM_HW_PARAM_CHANNELS, 1, wm8580->drvdata->num_dacs * 2);
861+
}
862+
818863
#define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
819864
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
820865

821866
static const struct snd_soc_dai_ops wm8580_dai_ops_playback = {
867+
.startup = wm8580_playback_startup,
822868
.set_sysclk = wm8580_set_sysclk,
823869
.hw_params = wm8580_paif_hw_params,
824870
.set_fmt = wm8580_set_paif_dai_fmt,
@@ -842,7 +888,6 @@ static struct snd_soc_dai_driver wm8580_dai[] = {
842888
.playback = {
843889
.stream_name = "Playback",
844890
.channels_min = 1,
845-
.channels_max = 6,
846891
.rates = SNDRV_PCM_RATE_8000_192000,
847892
.formats = WM8580_FORMATS,
848893
},
@@ -865,8 +910,22 @@ static struct snd_soc_dai_driver wm8580_dai[] = {
865910
static int wm8580_probe(struct snd_soc_codec *codec)
866911
{
867912
struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
913+
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
868914
int ret = 0;
869915

916+
switch (wm8580->drvdata->num_dacs) {
917+
case 4:
918+
snd_soc_add_codec_controls(codec, wm8581_snd_controls,
919+
ARRAY_SIZE(wm8581_snd_controls));
920+
snd_soc_dapm_new_controls(dapm, wm8581_dapm_widgets,
921+
ARRAY_SIZE(wm8581_dapm_widgets));
922+
snd_soc_dapm_add_routes(dapm, wm8581_dapm_routes,
923+
ARRAY_SIZE(wm8581_dapm_routes));
924+
break;
925+
default:
926+
break;
927+
}
928+
870929
ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies),
871930
wm8580->supplies);
872931
if (ret != 0) {
@@ -914,12 +973,6 @@ static const struct snd_soc_codec_driver soc_codec_dev_wm8580 = {
914973
},
915974
};
916975

917-
static const struct of_device_id wm8580_of_match[] = {
918-
{ .compatible = "wlf,wm8580" },
919-
{ },
920-
};
921-
MODULE_DEVICE_TABLE(of, wm8580_of_match);
922-
923976
static const struct regmap_config wm8580_regmap = {
924977
.reg_bits = 7,
925978
.val_bits = 9,
@@ -932,10 +985,26 @@ static const struct regmap_config wm8580_regmap = {
932985
.volatile_reg = wm8580_volatile,
933986
};
934987

988+
const struct wm8580_driver_data wm8580_data = {
989+
.num_dacs = 3,
990+
};
991+
992+
const struct wm8580_driver_data wm8581_data = {
993+
.num_dacs = 4,
994+
};
995+
996+
static const struct of_device_id wm8580_of_match[] = {
997+
{ .compatible = "wlf,wm8580", .data = &wm8580_data },
998+
{ .compatible = "wlf,wm8581", .data = &wm8581_data },
999+
{ },
1000+
};
1001+
MODULE_DEVICE_TABLE(of, wm8580_of_match);
1002+
9351003
#if IS_ENABLED(CONFIG_I2C)
9361004
static int wm8580_i2c_probe(struct i2c_client *i2c,
9371005
const struct i2c_device_id *id)
9381006
{
1007+
const struct of_device_id *of_id;
9391008
struct wm8580_priv *wm8580;
9401009
int ret, i;
9411010

@@ -960,6 +1029,15 @@ static int wm8580_i2c_probe(struct i2c_client *i2c,
9601029

9611030
i2c_set_clientdata(i2c, wm8580);
9621031

1032+
of_id = of_match_device(wm8580_of_match, &i2c->dev);
1033+
if (of_id)
1034+
wm8580->drvdata = of_id->data;
1035+
1036+
if (!wm8580->drvdata) {
1037+
dev_err(&i2c->dev, "failed to find driver data\n");
1038+
return -EINVAL;
1039+
}
1040+
9631041
ret = snd_soc_register_codec(&i2c->dev,
9641042
&soc_codec_dev_wm8580, wm8580_dai, ARRAY_SIZE(wm8580_dai));
9651043

@@ -973,7 +1051,8 @@ static int wm8580_i2c_remove(struct i2c_client *client)
9731051
}
9741052

9751053
static const struct i2c_device_id wm8580_i2c_id[] = {
976-
{ "wm8580", 0 },
1054+
{ "wm8580", (kernel_ulong_t)&wm8580_data },
1055+
{ "wm8581", (kernel_ulong_t)&wm8581_data },
9771056
{ }
9781057
};
9791058
MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);
@@ -1014,4 +1093,5 @@ module_exit(wm8580_exit);
10141093

10151094
MODULE_DESCRIPTION("ASoC WM8580 driver");
10161095
MODULE_AUTHOR("Mark Brown <[email protected]>");
1096+
MODULE_AUTHOR("Matt Flax <[email protected]>");
10171097
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)