~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/codecs/ad193x.c

Version: ~ [ linux-5.15-rc1 ] ~ [ linux-5.14.5 ] ~ [ linux-5.13.18 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.66 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.147 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.206 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.246 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.282 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.283 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * AD193X Audio Codec driver supporting AD1936/7/8/9
  3  *
  4  * Copyright 2010 Analog Devices Inc.
  5  *
  6  * Licensed under the GPL-2 or later.
  7  */
  8 
  9 #include <linux/module.h>
 10 #include <linux/kernel.h>
 11 #include <linux/device.h>
 12 #include <linux/regmap.h>
 13 #include <linux/slab.h>
 14 #include <sound/core.h>
 15 #include <sound/pcm.h>
 16 #include <sound/pcm_params.h>
 17 #include <sound/initval.h>
 18 #include <sound/soc.h>
 19 #include <sound/tlv.h>
 20 
 21 #include "ad193x.h"
 22 
 23 /* codec private data */
 24 struct ad193x_priv {
 25         struct regmap *regmap;
 26         enum ad193x_type type;
 27         int sysclk;
 28 };
 29 
 30 /*
 31  * AD193X volume/mute/de-emphasis etc. controls
 32  */
 33 static const char * const ad193x_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"};
 34 
 35 static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1,
 36                             ad193x_deemp);
 37 
 38 static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0);
 39 
 40 static const struct snd_kcontrol_new ad193x_snd_controls[] = {
 41         /* DAC volume control */
 42         SOC_DOUBLE_R_TLV("DAC1 Volume", AD193X_DAC_L1_VOL,
 43                         AD193X_DAC_R1_VOL, 0, 0xFF, 1, adau193x_tlv),
 44         SOC_DOUBLE_R_TLV("DAC2 Volume", AD193X_DAC_L2_VOL,
 45                         AD193X_DAC_R2_VOL, 0, 0xFF, 1, adau193x_tlv),
 46         SOC_DOUBLE_R_TLV("DAC3 Volume", AD193X_DAC_L3_VOL,
 47                         AD193X_DAC_R3_VOL, 0, 0xFF, 1, adau193x_tlv),
 48         SOC_DOUBLE_R_TLV("DAC4 Volume", AD193X_DAC_L4_VOL,
 49                         AD193X_DAC_R4_VOL, 0, 0xFF, 1, adau193x_tlv),
 50 
 51         /* DAC switch control */
 52         SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE,
 53                 AD193X_DACR1_MUTE, 1, 1),
 54         SOC_DOUBLE("DAC2 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL2_MUTE,
 55                 AD193X_DACR2_MUTE, 1, 1),
 56         SOC_DOUBLE("DAC3 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL3_MUTE,
 57                 AD193X_DACR3_MUTE, 1, 1),
 58         SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE,
 59                 AD193X_DACR4_MUTE, 1, 1),
 60 
 61         /* DAC de-emphasis */
 62         SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum),
 63 };
 64 
 65 static const struct snd_kcontrol_new ad193x_adc_snd_controls[] = {
 66         /* ADC switch control */
 67         SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE,
 68                 AD193X_ADCR1_MUTE, 1, 1),
 69         SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE,
 70                 AD193X_ADCR2_MUTE, 1, 1),
 71 
 72         /* ADC high-pass filter */
 73         SOC_SINGLE("ADC High Pass Filter Switch", AD193X_ADC_CTRL0,
 74                         AD193X_ADC_HIGHPASS_FILTER, 1, 0),
 75 };
 76 
 77 static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
 78         SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
 79         SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0),
 80         SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0),
 81         SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0),
 82         SND_SOC_DAPM_VMID("VMID"),
 83         SND_SOC_DAPM_OUTPUT("DAC1OUT"),
 84         SND_SOC_DAPM_OUTPUT("DAC2OUT"),
 85         SND_SOC_DAPM_OUTPUT("DAC3OUT"),
 86         SND_SOC_DAPM_OUTPUT("DAC4OUT"),
 87 };
 88 
 89 static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = {
 90         SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
 91         SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
 92         SND_SOC_DAPM_INPUT("ADC1IN"),
 93         SND_SOC_DAPM_INPUT("ADC2IN"),
 94 };
 95 
 96 static const struct snd_soc_dapm_route audio_paths[] = {
 97         { "DAC", NULL, "SYSCLK" },
 98         { "DAC Output", NULL, "DAC" },
 99         { "DAC Output", NULL, "VMID" },
100         { "DAC1OUT", NULL, "DAC Output" },
101         { "DAC2OUT", NULL, "DAC Output" },
102         { "DAC3OUT", NULL, "DAC Output" },
103         { "DAC4OUT", NULL, "DAC Output" },
104         { "SYSCLK", NULL, "PLL_PWR" },
105 };
106 
107 static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = {
108         { "ADC", NULL, "SYSCLK" },
109         { "ADC", NULL, "ADC_PWR" },
110         { "ADC", NULL, "ADC1IN" },
111         { "ADC", NULL, "ADC2IN" },
112 };
113 
114 static inline bool ad193x_has_adc(const struct ad193x_priv *ad193x)
115 {
116         switch (ad193x->type) {
117         case AD1933:
118         case AD1934:
119                 return false;
120         default:
121                 break;
122         }
123 
124         return true;
125 }
126 
127 /*
128  * DAI ops entries
129  */
130 
131 static int ad193x_mute(struct snd_soc_dai *dai, int mute)
132 {
133         struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(dai->codec);
134 
135         if (mute)
136                 regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
137                                     AD193X_DAC_MASTER_MUTE,
138                                     AD193X_DAC_MASTER_MUTE);
139         else
140                 regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
141                                     AD193X_DAC_MASTER_MUTE, 0);
142 
143         return 0;
144 }
145 
146 static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
147                                unsigned int rx_mask, int slots, int width)
148 {
149         struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(dai->codec);
150         unsigned int channels;
151 
152         switch (slots) {
153         case 2:
154                 channels = AD193X_2_CHANNELS;
155                 break;
156         case 4:
157                 channels = AD193X_4_CHANNELS;
158                 break;
159         case 8:
160                 channels = AD193X_8_CHANNELS;
161                 break;
162         case 16:
163                 channels = AD193X_16_CHANNELS;
164                 break;
165         default:
166                 return -EINVAL;
167         }
168 
169         regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
170                 AD193X_DAC_CHAN_MASK, channels << AD193X_DAC_CHAN_SHFT);
171         if (ad193x_has_adc(ad193x))
172                 regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
173                                    AD193X_ADC_CHAN_MASK,
174                                    channels << AD193X_ADC_CHAN_SHFT);
175 
176         return 0;
177 }
178 
179 static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
180                 unsigned int fmt)
181 {
182         struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec_dai->codec);
183         unsigned int adc_serfmt = 0;
184         unsigned int adc_fmt = 0;
185         unsigned int dac_fmt = 0;
186 
187         /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S
188          * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A)
189          */
190         switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
191         case SND_SOC_DAIFMT_I2S:
192                 adc_serfmt |= AD193X_ADC_SERFMT_TDM;
193                 break;
194         case SND_SOC_DAIFMT_DSP_A:
195                 adc_serfmt |= AD193X_ADC_SERFMT_AUX;
196                 break;
197         default:
198                 if (ad193x_has_adc(ad193x))
199                         return -EINVAL;
200                 break;
201         }
202 
203         switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
204         case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
205                 break;
206         case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */
207                 adc_fmt |= AD193X_ADC_LEFT_HIGH;
208                 dac_fmt |= AD193X_DAC_LEFT_HIGH;
209                 break;
210         case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */
211                 adc_fmt |= AD193X_ADC_BCLK_INV;
212                 dac_fmt |= AD193X_DAC_BCLK_INV;
213                 break;
214         case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */
215                 adc_fmt |= AD193X_ADC_LEFT_HIGH;
216                 adc_fmt |= AD193X_ADC_BCLK_INV;
217                 dac_fmt |= AD193X_DAC_LEFT_HIGH;
218                 dac_fmt |= AD193X_DAC_BCLK_INV;
219                 break;
220         default:
221                 return -EINVAL;
222         }
223 
224         switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
225         case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
226                 adc_fmt |= AD193X_ADC_LCR_MASTER;
227                 adc_fmt |= AD193X_ADC_BCLK_MASTER;
228                 dac_fmt |= AD193X_DAC_LCR_MASTER;
229                 dac_fmt |= AD193X_DAC_BCLK_MASTER;
230                 break;
231         case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */
232                 adc_fmt |= AD193X_ADC_LCR_MASTER;
233                 dac_fmt |= AD193X_DAC_LCR_MASTER;
234                 break;
235         case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
236                 adc_fmt |= AD193X_ADC_BCLK_MASTER;
237                 dac_fmt |= AD193X_DAC_BCLK_MASTER;
238                 break;
239         case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
240                 break;
241         default:
242                 return -EINVAL;
243         }
244 
245         if (ad193x_has_adc(ad193x)) {
246                 regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
247                                    AD193X_ADC_SERFMT_MASK, adc_serfmt);
248                 regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
249                                    AD193X_ADC_FMT_MASK, adc_fmt);
250         }
251         regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
252                 AD193X_DAC_FMT_MASK, dac_fmt);
253 
254         return 0;
255 }
256 
257 static int ad193x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
258                 int clk_id, unsigned int freq, int dir)
259 {
260         struct snd_soc_codec *codec = codec_dai->codec;
261         struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
262         switch (freq) {
263         case 12288000:
264         case 18432000:
265         case 24576000:
266         case 36864000:
267                 ad193x->sysclk = freq;
268                 return 0;
269         }
270         return -EINVAL;
271 }
272 
273 static int ad193x_hw_params(struct snd_pcm_substream *substream,
274                 struct snd_pcm_hw_params *params,
275                 struct snd_soc_dai *dai)
276 {
277         int word_len = 0, master_rate = 0;
278         struct snd_soc_codec *codec = dai->codec;
279         struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
280 
281         /* bit size */
282         switch (params_width(params)) {
283         case 16:
284                 word_len = 3;
285                 break;
286         case 20:
287                 word_len = 1;
288                 break;
289         case 24:
290         case 32:
291                 word_len = 0;
292                 break;
293         }
294 
295         switch (ad193x->sysclk) {
296         case 12288000:
297                 master_rate = AD193X_PLL_INPUT_256;
298                 break;
299         case 18432000:
300                 master_rate = AD193X_PLL_INPUT_384;
301                 break;
302         case 24576000:
303                 master_rate = AD193X_PLL_INPUT_512;
304                 break;
305         case 36864000:
306                 master_rate = AD193X_PLL_INPUT_768;
307                 break;
308         }
309 
310         regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL0,
311                             AD193X_PLL_INPUT_MASK, master_rate);
312 
313         regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
314                             AD193X_DAC_WORD_LEN_MASK,
315                             word_len << AD193X_DAC_WORD_LEN_SHFT);
316 
317         if (ad193x_has_adc(ad193x))
318                 regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
319                                    AD193X_ADC_WORD_LEN_MASK, word_len);
320 
321         return 0;
322 }
323 
324 static const struct snd_soc_dai_ops ad193x_dai_ops = {
325         .hw_params = ad193x_hw_params,
326         .digital_mute = ad193x_mute,
327         .set_tdm_slot = ad193x_set_tdm_slot,
328         .set_sysclk     = ad193x_set_dai_sysclk,
329         .set_fmt = ad193x_set_dai_fmt,
330 };
331 
332 /* codec DAI instance */
333 static struct snd_soc_dai_driver ad193x_dai = {
334         .name = "ad193x-hifi",
335         .playback = {
336                 .stream_name = "Playback",
337                 .channels_min = 2,
338                 .channels_max = 8,
339                 .rates = SNDRV_PCM_RATE_48000,
340                 .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
341                         SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
342         },
343         .capture = {
344                 .stream_name = "Capture",
345                 .channels_min = 2,
346                 .channels_max = 4,
347                 .rates = SNDRV_PCM_RATE_48000,
348                 .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
349                         SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
350         },
351         .ops = &ad193x_dai_ops,
352 };
353 
354 static int ad193x_codec_probe(struct snd_soc_codec *codec)
355 {
356         struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
357         struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
358         int num, ret;
359 
360         /* default setting for ad193x */
361 
362         /* unmute dac channels */
363         regmap_write(ad193x->regmap, AD193X_DAC_CHNL_MUTE, 0x0);
364         /* de-emphasis: 48kHz, powedown dac */
365         regmap_write(ad193x->regmap, AD193X_DAC_CTRL2, 0x1A);
366         /* dac in tdm mode */
367         regmap_write(ad193x->regmap, AD193X_DAC_CTRL0, 0x40);
368 
369         /* adc only */
370         if (ad193x_has_adc(ad193x)) {
371                 /* high-pass filter enable */
372                 regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3);
373                 /* sata delay=1, adc aux mode */
374                 regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43);
375         }
376 
377         /* pll input: mclki/xi */
378         regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */
379         regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL1, 0x04);
380 
381         /* adc only */
382         if (ad193x_has_adc(ad193x)) {
383                 /* add adc controls */
384                 num = ARRAY_SIZE(ad193x_adc_snd_controls);
385                 ret = snd_soc_add_codec_controls(codec,
386                                                  ad193x_adc_snd_controls,
387                                                  num);
388                 if (ret)
389                         return ret;
390 
391                 /* add adc widgets */
392                 num = ARRAY_SIZE(ad193x_adc_widgets);
393                 ret = snd_soc_dapm_new_controls(dapm,
394                                                 ad193x_adc_widgets,
395                                                 num);
396                 if (ret)
397                         return ret;
398 
399                 /* add adc routes */
400                 num = ARRAY_SIZE(ad193x_adc_audio_paths);
401                 ret = snd_soc_dapm_add_routes(dapm,
402                                               ad193x_adc_audio_paths,
403                                               num);
404                 if (ret)
405                         return ret;
406         }
407 
408         return 0;
409 }
410 
411 static const struct snd_soc_codec_driver soc_codec_dev_ad193x = {
412         .probe = ad193x_codec_probe,
413         .component_driver = {
414                 .controls               = ad193x_snd_controls,
415                 .num_controls           = ARRAY_SIZE(ad193x_snd_controls),
416                 .dapm_widgets           = ad193x_dapm_widgets,
417                 .num_dapm_widgets       = ARRAY_SIZE(ad193x_dapm_widgets),
418                 .dapm_routes            = audio_paths,
419                 .num_dapm_routes        = ARRAY_SIZE(audio_paths),
420         },
421 };
422 
423 const struct regmap_config ad193x_regmap_config = {
424         .max_register = AD193X_NUM_REGS - 1,
425 };
426 EXPORT_SYMBOL_GPL(ad193x_regmap_config);
427 
428 int ad193x_probe(struct device *dev, struct regmap *regmap,
429                  enum ad193x_type type)
430 {
431         struct ad193x_priv *ad193x;
432 
433         if (IS_ERR(regmap))
434                 return PTR_ERR(regmap);
435 
436         ad193x = devm_kzalloc(dev, sizeof(*ad193x), GFP_KERNEL);
437         if (ad193x == NULL)
438                 return -ENOMEM;
439 
440         ad193x->regmap = regmap;
441         ad193x->type = type;
442 
443         dev_set_drvdata(dev, ad193x);
444 
445         return snd_soc_register_codec(dev, &soc_codec_dev_ad193x,
446                 &ad193x_dai, 1);
447 }
448 EXPORT_SYMBOL_GPL(ad193x_probe);
449 
450 MODULE_DESCRIPTION("ASoC ad193x driver");
451 MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
452 MODULE_LICENSE("GPL");
453 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp