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 *
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 */
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 */
240249struct 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),
306316SOC_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+
309332static const struct snd_soc_dapm_widget wm8580_dapm_widgets [] = {
310333SND_SOC_DAPM_DAC ("DAC1" , "Playback" , WM8580_PWRDN1 , 2 , 1 ),
311334SND_SOC_DAPM_DAC ("DAC2" , "Playback" , WM8580_PWRDN1 , 3 , 1 ),
@@ -324,6 +347,13 @@ SND_SOC_DAPM_INPUT("AINL"),
324347SND_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+
327357static 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 */
342377struct _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
821866static 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[] = {
865910static 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-
923976static const struct regmap_config wm8580_regmap = {
924977 .reg_bits = 7 ,
925978 .val_bits = 9 ,
@@ -932,10 +985,25 @@ static const struct regmap_config wm8580_regmap = {
932985 .volatile_reg = wm8580_volatile ,
933986};
934987
935- #if IS_ENABLED (CONFIG_I2C )
988+ static const struct wm8580_driver_data wm8580_data = {
989+ .num_dacs = 3 ,
990+ };
991+
992+ static 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+
9361003static int wm8580_i2c_probe (struct i2c_client * i2c ,
9371004 const struct i2c_device_id * id )
9381005{
1006+ const struct of_device_id * of_id ;
9391007 struct wm8580_priv * wm8580 ;
9401008 int ret , i ;
9411009
@@ -960,6 +1028,15 @@ static int wm8580_i2c_probe(struct i2c_client *i2c,
9601028
9611029 i2c_set_clientdata (i2c , wm8580 );
9621030
1031+ of_id = of_match_device (wm8580_of_match , & i2c -> dev );
1032+ if (of_id )
1033+ wm8580 -> drvdata = of_id -> data ;
1034+
1035+ if (!wm8580 -> drvdata ) {
1036+ dev_err (& i2c -> dev , "failed to find driver data\n" );
1037+ return - EINVAL ;
1038+ }
1039+
9631040 ret = snd_soc_register_codec (& i2c -> dev ,
9641041 & soc_codec_dev_wm8580 , wm8580_dai , ARRAY_SIZE (wm8580_dai ));
9651042
@@ -973,7 +1050,8 @@ static int wm8580_i2c_remove(struct i2c_client *client)
9731050}
9741051
9751052static const struct i2c_device_id wm8580_i2c_id [] = {
976- { "wm8580" , 0 },
1053+ { "wm8580" , (kernel_ulong_t )& wm8580_data },
1054+ { "wm8581" , (kernel_ulong_t )& wm8581_data },
9771055 { }
9781056};
9791057MODULE_DEVICE_TABLE (i2c , wm8580_i2c_id );
@@ -987,31 +1065,10 @@ static struct i2c_driver wm8580_i2c_driver = {
9871065 .remove = wm8580_i2c_remove ,
9881066 .id_table = wm8580_i2c_id ,
9891067};
990- #endif
9911068
992- static int __init wm8580_modinit (void )
993- {
994- int ret = 0 ;
995-
996- #if IS_ENABLED (CONFIG_I2C )
997- ret = i2c_add_driver (& wm8580_i2c_driver );
998- if (ret != 0 ) {
999- pr_err ("Failed to register WM8580 I2C driver: %d\n" , ret );
1000- }
1001- #endif
1002-
1003- return ret ;
1004- }
1005- module_init (wm8580_modinit );
1006-
1007- static void __exit wm8580_exit (void )
1008- {
1009- #if IS_ENABLED (CONFIG_I2C )
1010- i2c_del_driver (& wm8580_i2c_driver );
1011- #endif
1012- }
1013- module_exit (wm8580_exit );
1069+ module_i2c_driver (wm8580_i2c_driver );
10141070
10151071MODULE_DESCRIPTION ("ASoC WM8580 driver" );
10161072MODULE_AUTHOR (
"Mark Brown <[email protected] >" );
1073+ MODULE_AUTHOR (
"Matt Flax <[email protected] >" );
10171074MODULE_LICENSE ("GPL" );
0 commit comments