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

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

Version: ~ [ linux-4.20-rc6 ] ~ [ linux-4.19.8 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.87 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.144 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.166 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.128 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.61 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.31.14 ] ~ [ linux-2.6.30.10 ] ~ [ linux-2.6.29.6 ] ~ [ linux-2.6.28.10 ] ~ [ linux-2.6.27.62 ] ~ [ 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  * cs42l51.c
  3  *
  4  * ASoC Driver for Cirrus Logic CS42L51 codecs
  5  *
  6  * Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com>
  7  *
  8  * Based on cs4270.c - Copyright (c) Freescale Semiconductor
  9  *
 10  * This program is free software; you can redistribute it and/or modify
 11  * it under the terms of the GNU General Public License version 2 as
 12  * published by the Free Software Foundation.
 13  *
 14  * This program is distributed in the hope that it will be useful,
 15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 17  * GNU General Public License for more details.
 18  *
 19  * For now:
 20  *  - Only I2C is support. Not SPI
 21  *  - master mode *NOT* supported
 22  */
 23 
 24 #include <linux/clk.h>
 25 #include <linux/module.h>
 26 #include <linux/slab.h>
 27 #include <sound/core.h>
 28 #include <sound/soc.h>
 29 #include <sound/tlv.h>
 30 #include <sound/initval.h>
 31 #include <sound/pcm_params.h>
 32 #include <sound/pcm.h>
 33 #include <linux/regmap.h>
 34 
 35 #include "cs42l51.h"
 36 
 37 enum master_slave_mode {
 38         MODE_SLAVE,
 39         MODE_SLAVE_AUTO,
 40         MODE_MASTER,
 41 };
 42 
 43 struct cs42l51_private {
 44         unsigned int mclk;
 45         struct clk *mclk_handle;
 46         unsigned int audio_mode;        /* The mode (I2S or left-justified) */
 47         enum master_slave_mode func;
 48 };
 49 
 50 #define CS42L51_FORMATS ( \
 51                 SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \
 52                 SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
 53                 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
 54                 SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE)
 55 
 56 static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
 57                         struct snd_ctl_elem_value *ucontrol)
 58 {
 59         struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 60         unsigned long value = snd_soc_component_read32(component, CS42L51_PCM_MIXER)&3;
 61 
 62         switch (value) {
 63         default:
 64         case 0:
 65                 ucontrol->value.enumerated.item[0] = 0;
 66                 break;
 67         /* same value : (L+R)/2 and (R+L)/2 */
 68         case 1:
 69         case 2:
 70                 ucontrol->value.enumerated.item[0] = 1;
 71                 break;
 72         case 3:
 73                 ucontrol->value.enumerated.item[0] = 2;
 74                 break;
 75         }
 76 
 77         return 0;
 78 }
 79 
 80 #define CHAN_MIX_NORMAL 0x00
 81 #define CHAN_MIX_BOTH   0x55
 82 #define CHAN_MIX_SWAP   0xFF
 83 
 84 static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
 85                         struct snd_ctl_elem_value *ucontrol)
 86 {
 87         struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 88         unsigned char val;
 89 
 90         switch (ucontrol->value.enumerated.item[0]) {
 91         default:
 92         case 0:
 93                 val = CHAN_MIX_NORMAL;
 94                 break;
 95         case 1:
 96                 val = CHAN_MIX_BOTH;
 97                 break;
 98         case 2:
 99                 val = CHAN_MIX_SWAP;
100                 break;
101         }
102 
103         snd_soc_component_write(component, CS42L51_PCM_MIXER, val);
104 
105         return 1;
106 }
107 
108 static const DECLARE_TLV_DB_SCALE(adc_pcm_tlv, -5150, 50, 0);
109 static const DECLARE_TLV_DB_SCALE(tone_tlv, -1050, 150, 0);
110 
111 static const DECLARE_TLV_DB_SCALE(aout_tlv, -10200, 50, 0);
112 
113 static const DECLARE_TLV_DB_SCALE(boost_tlv, 1600, 1600, 0);
114 static const char *chan_mix[] = {
115         "L R",
116         "L+R",
117         "R L",
118 };
119 
120 static SOC_ENUM_SINGLE_EXT_DECL(cs42l51_chan_mix, chan_mix);
121 
122 static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
123         SOC_DOUBLE_R_SX_TLV("PCM Playback Volume",
124                         CS42L51_PCMA_VOL, CS42L51_PCMB_VOL,
125                         0, 0x19, 0x7F, adc_pcm_tlv),
126         SOC_DOUBLE_R("PCM Playback Switch",
127                         CS42L51_PCMA_VOL, CS42L51_PCMB_VOL, 7, 1, 1),
128         SOC_DOUBLE_R_SX_TLV("Analog Playback Volume",
129                         CS42L51_AOUTA_VOL, CS42L51_AOUTB_VOL,
130                         0, 0x34, 0xE4, aout_tlv),
131         SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
132                         CS42L51_ADCA_VOL, CS42L51_ADCB_VOL,
133                         0, 0x19, 0x7F, adc_pcm_tlv),
134         SOC_DOUBLE_R("ADC Mixer Switch",
135                         CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1),
136         SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0),
137         SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0),
138         SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0),
139         SOC_SINGLE("Zero Cross Switch", CS42L51_DAC_CTL, 0, 0, 0),
140         SOC_DOUBLE_TLV("Mic Boost Volume",
141                         CS42L51_MIC_CTL, 0, 1, 1, 0, boost_tlv),
142         SOC_SINGLE_TLV("Bass Volume", CS42L51_TONE_CTL, 0, 0xf, 1, tone_tlv),
143         SOC_SINGLE_TLV("Treble Volume", CS42L51_TONE_CTL, 4, 0xf, 1, tone_tlv),
144         SOC_ENUM_EXT("PCM channel mixer",
145                         cs42l51_chan_mix,
146                         cs42l51_get_chan_mix, cs42l51_set_chan_mix),
147 };
148 
149 /*
150  * to power down, one must:
151  * 1.) Enable the PDN bit
152  * 2.) enable power-down for the select channels
153  * 3.) disable the PDN bit.
154  */
155 static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
156                 struct snd_kcontrol *kcontrol, int event)
157 {
158         struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
159 
160         switch (event) {
161         case SND_SOC_DAPM_PRE_PMD:
162                 snd_soc_component_update_bits(component, CS42L51_POWER_CTL1,
163                                     CS42L51_POWER_CTL1_PDN,
164                                     CS42L51_POWER_CTL1_PDN);
165                 break;
166         default:
167         case SND_SOC_DAPM_POST_PMD:
168                 snd_soc_component_update_bits(component, CS42L51_POWER_CTL1,
169                                     CS42L51_POWER_CTL1_PDN, 0);
170                 break;
171         }
172 
173         return 0;
174 }
175 
176 static const char *cs42l51_dac_names[] = {"Direct PCM",
177         "DSP PCM", "ADC"};
178 static SOC_ENUM_SINGLE_DECL(cs42l51_dac_mux_enum,
179                             CS42L51_DAC_CTL, 6, cs42l51_dac_names);
180 static const struct snd_kcontrol_new cs42l51_dac_mux_controls =
181         SOC_DAPM_ENUM("Route", cs42l51_dac_mux_enum);
182 
183 static const char *cs42l51_adcl_names[] = {"AIN1 Left", "AIN2 Left",
184         "MIC Left", "MIC+preamp Left"};
185 static SOC_ENUM_SINGLE_DECL(cs42l51_adcl_mux_enum,
186                             CS42L51_ADC_INPUT, 4, cs42l51_adcl_names);
187 static const struct snd_kcontrol_new cs42l51_adcl_mux_controls =
188         SOC_DAPM_ENUM("Route", cs42l51_adcl_mux_enum);
189 
190 static const char *cs42l51_adcr_names[] = {"AIN1 Right", "AIN2 Right",
191         "MIC Right", "MIC+preamp Right"};
192 static SOC_ENUM_SINGLE_DECL(cs42l51_adcr_mux_enum,
193                             CS42L51_ADC_INPUT, 6, cs42l51_adcr_names);
194 static const struct snd_kcontrol_new cs42l51_adcr_mux_controls =
195         SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum);
196 
197 static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
198         SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
199         SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0,
200                 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
201         SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0,
202                 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
203         SND_SOC_DAPM_ADC_E("Left ADC", "Left HiFi Capture",
204                 CS42L51_POWER_CTL1, 1, 1,
205                 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
206         SND_SOC_DAPM_ADC_E("Right ADC", "Right HiFi Capture",
207                 CS42L51_POWER_CTL1, 2, 1,
208                 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
209         SND_SOC_DAPM_DAC_E("Left DAC", "Left HiFi Playback",
210                 CS42L51_POWER_CTL1, 5, 1,
211                 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
212         SND_SOC_DAPM_DAC_E("Right DAC", "Right HiFi Playback",
213                 CS42L51_POWER_CTL1, 6, 1,
214                 cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
215 
216         /* analog/mic */
217         SND_SOC_DAPM_INPUT("AIN1L"),
218         SND_SOC_DAPM_INPUT("AIN1R"),
219         SND_SOC_DAPM_INPUT("AIN2L"),
220         SND_SOC_DAPM_INPUT("AIN2R"),
221         SND_SOC_DAPM_INPUT("MICL"),
222         SND_SOC_DAPM_INPUT("MICR"),
223 
224         SND_SOC_DAPM_MIXER("Mic Preamp Left",
225                 CS42L51_MIC_POWER_CTL, 2, 1, NULL, 0),
226         SND_SOC_DAPM_MIXER("Mic Preamp Right",
227                 CS42L51_MIC_POWER_CTL, 3, 1, NULL, 0),
228 
229         /* HP */
230         SND_SOC_DAPM_OUTPUT("HPL"),
231         SND_SOC_DAPM_OUTPUT("HPR"),
232 
233         /* mux */
234         SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0,
235                 &cs42l51_dac_mux_controls),
236         SND_SOC_DAPM_MUX("PGA-ADC Mux Left", SND_SOC_NOPM, 0, 0,
237                 &cs42l51_adcl_mux_controls),
238         SND_SOC_DAPM_MUX("PGA-ADC Mux Right", SND_SOC_NOPM, 0, 0,
239                 &cs42l51_adcr_mux_controls),
240 };
241 
242 static const struct snd_soc_dapm_widget cs42l51_dapm_mclk_widgets[] = {
243         SND_SOC_DAPM_CLOCK_SUPPLY("MCLK")
244 };
245 
246 static const struct snd_soc_dapm_route cs42l51_routes[] = {
247         {"HPL", NULL, "Left DAC"},
248         {"HPR", NULL, "Right DAC"},
249 
250         {"Left ADC", NULL, "Left PGA"},
251         {"Right ADC", NULL, "Right PGA"},
252 
253         {"Mic Preamp Left",  NULL,  "MICL"},
254         {"Mic Preamp Right", NULL,  "MICR"},
255 
256         {"PGA-ADC Mux Left",  "AIN1 Left",        "AIN1L" },
257         {"PGA-ADC Mux Left",  "AIN2 Left",        "AIN2L" },
258         {"PGA-ADC Mux Left",  "MIC Left",         "MICL"  },
259         {"PGA-ADC Mux Left",  "MIC+preamp Left",  "Mic Preamp Left" },
260         {"PGA-ADC Mux Right", "AIN1 Right",       "AIN1R" },
261         {"PGA-ADC Mux Right", "AIN2 Right",       "AIN2R" },
262         {"PGA-ADC Mux Right", "MIC Right",        "MICR" },
263         {"PGA-ADC Mux Right", "MIC+preamp Right", "Mic Preamp Right" },
264 
265         {"Left PGA", NULL, "PGA-ADC Mux Left"},
266         {"Right PGA", NULL, "PGA-ADC Mux Right"},
267 };
268 
269 static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
270                 unsigned int format)
271 {
272         struct snd_soc_component *component = codec_dai->component;
273         struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component);
274 
275         switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
276         case SND_SOC_DAIFMT_I2S:
277         case SND_SOC_DAIFMT_LEFT_J:
278         case SND_SOC_DAIFMT_RIGHT_J:
279                 cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
280                 break;
281         default:
282                 dev_err(component->dev, "invalid DAI format\n");
283                 return -EINVAL;
284         }
285 
286         switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
287         case SND_SOC_DAIFMT_CBM_CFM:
288                 cs42l51->func = MODE_MASTER;
289                 break;
290         case SND_SOC_DAIFMT_CBS_CFS:
291                 cs42l51->func = MODE_SLAVE_AUTO;
292                 break;
293         default:
294                 dev_err(component->dev, "Unknown master/slave configuration\n");
295                 return -EINVAL;
296         }
297 
298         return 0;
299 }
300 
301 struct cs42l51_ratios {
302         unsigned int ratio;
303         unsigned char speed_mode;
304         unsigned char mclk;
305 };
306 
307 static struct cs42l51_ratios slave_ratios[] = {
308         {  512, CS42L51_QSM_MODE, 0 }, {  768, CS42L51_QSM_MODE, 0 },
309         { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
310         { 2048, CS42L51_QSM_MODE, 0 }, { 3072, CS42L51_QSM_MODE, 0 },
311         {  256, CS42L51_HSM_MODE, 0 }, {  384, CS42L51_HSM_MODE, 0 },
312         {  512, CS42L51_HSM_MODE, 0 }, {  768, CS42L51_HSM_MODE, 0 },
313         { 1024, CS42L51_HSM_MODE, 0 }, { 1536, CS42L51_HSM_MODE, 0 },
314         {  128, CS42L51_SSM_MODE, 0 }, {  192, CS42L51_SSM_MODE, 0 },
315         {  256, CS42L51_SSM_MODE, 0 }, {  384, CS42L51_SSM_MODE, 0 },
316         {  512, CS42L51_SSM_MODE, 0 }, {  768, CS42L51_SSM_MODE, 0 },
317         {  128, CS42L51_DSM_MODE, 0 }, {  192, CS42L51_DSM_MODE, 0 },
318         {  256, CS42L51_DSM_MODE, 0 }, {  384, CS42L51_DSM_MODE, 0 },
319 };
320 
321 static struct cs42l51_ratios slave_auto_ratios[] = {
322         { 1024, CS42L51_QSM_MODE, 0 }, { 1536, CS42L51_QSM_MODE, 0 },
323         { 2048, CS42L51_QSM_MODE, 1 }, { 3072, CS42L51_QSM_MODE, 1 },
324         {  512, CS42L51_HSM_MODE, 0 }, {  768, CS42L51_HSM_MODE, 0 },
325         { 1024, CS42L51_HSM_MODE, 1 }, { 1536, CS42L51_HSM_MODE, 1 },
326         {  256, CS42L51_SSM_MODE, 0 }, {  384, CS42L51_SSM_MODE, 0 },
327         {  512, CS42L51_SSM_MODE, 1 }, {  768, CS42L51_SSM_MODE, 1 },
328         {  128, CS42L51_DSM_MODE, 0 }, {  192, CS42L51_DSM_MODE, 0 },
329         {  256, CS42L51_DSM_MODE, 1 }, {  384, CS42L51_DSM_MODE, 1 },
330 };
331 
332 static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai,
333                 int clk_id, unsigned int freq, int dir)
334 {
335         struct snd_soc_component *component = codec_dai->component;
336         struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component);
337 
338         cs42l51->mclk = freq;
339         return 0;
340 }
341 
342 static int cs42l51_hw_params(struct snd_pcm_substream *substream,
343                 struct snd_pcm_hw_params *params,
344                 struct snd_soc_dai *dai)
345 {
346         struct snd_soc_component *component = dai->component;
347         struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component);
348         int ret;
349         unsigned int i;
350         unsigned int rate;
351         unsigned int ratio;
352         struct cs42l51_ratios *ratios = NULL;
353         int nr_ratios = 0;
354         int intf_ctl, power_ctl, fmt;
355 
356         switch (cs42l51->func) {
357         case MODE_MASTER:
358                 return -EINVAL;
359         case MODE_SLAVE:
360                 ratios = slave_ratios;
361                 nr_ratios = ARRAY_SIZE(slave_ratios);
362                 break;
363         case MODE_SLAVE_AUTO:
364                 ratios = slave_auto_ratios;
365                 nr_ratios = ARRAY_SIZE(slave_auto_ratios);
366                 break;
367         }
368 
369         /* Figure out which MCLK/LRCK ratio to use */
370         rate = params_rate(params);     /* Sampling rate, in Hz */
371         ratio = cs42l51->mclk / rate;    /* MCLK/LRCK ratio */
372         for (i = 0; i < nr_ratios; i++) {
373                 if (ratios[i].ratio == ratio)
374                         break;
375         }
376 
377         if (i == nr_ratios) {
378                 /* We did not find a matching ratio */
379                 dev_err(component->dev, "could not find matching ratio\n");
380                 return -EINVAL;
381         }
382 
383         intf_ctl = snd_soc_component_read32(component, CS42L51_INTF_CTL);
384         power_ctl = snd_soc_component_read32(component, CS42L51_MIC_POWER_CTL);
385 
386         intf_ctl &= ~(CS42L51_INTF_CTL_MASTER | CS42L51_INTF_CTL_ADC_I2S
387                         | CS42L51_INTF_CTL_DAC_FORMAT(7));
388         power_ctl &= ~(CS42L51_MIC_POWER_CTL_SPEED(3)
389                         | CS42L51_MIC_POWER_CTL_MCLK_DIV2);
390 
391         switch (cs42l51->func) {
392         case MODE_MASTER:
393                 intf_ctl |= CS42L51_INTF_CTL_MASTER;
394                 power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
395                 break;
396         case MODE_SLAVE:
397                 power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
398                 break;
399         case MODE_SLAVE_AUTO:
400                 power_ctl |= CS42L51_MIC_POWER_CTL_AUTO;
401                 break;
402         }
403 
404         switch (cs42l51->audio_mode) {
405         case SND_SOC_DAIFMT_I2S:
406                 intf_ctl |= CS42L51_INTF_CTL_ADC_I2S;
407                 intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_I2S);
408                 break;
409         case SND_SOC_DAIFMT_LEFT_J:
410                 intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(CS42L51_DAC_DIF_LJ24);
411                 break;
412         case SND_SOC_DAIFMT_RIGHT_J:
413                 switch (params_width(params)) {
414                 case 16:
415                         fmt = CS42L51_DAC_DIF_RJ16;
416                         break;
417                 case 18:
418                         fmt = CS42L51_DAC_DIF_RJ18;
419                         break;
420                 case 20:
421                         fmt = CS42L51_DAC_DIF_RJ20;
422                         break;
423                 case 24:
424                         fmt = CS42L51_DAC_DIF_RJ24;
425                         break;
426                 default:
427                         dev_err(component->dev, "unknown format\n");
428                         return -EINVAL;
429                 }
430                 intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt);
431                 break;
432         default:
433                 dev_err(component->dev, "unknown format\n");
434                 return -EINVAL;
435         }
436 
437         if (ratios[i].mclk)
438                 power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2;
439 
440         ret = snd_soc_component_write(component, CS42L51_INTF_CTL, intf_ctl);
441         if (ret < 0)
442                 return ret;
443 
444         ret = snd_soc_component_write(component, CS42L51_MIC_POWER_CTL, power_ctl);
445         if (ret < 0)
446                 return ret;
447 
448         return 0;
449 }
450 
451 static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute)
452 {
453         struct snd_soc_component *component = dai->component;
454         int reg;
455         int mask = CS42L51_DAC_OUT_CTL_DACA_MUTE|CS42L51_DAC_OUT_CTL_DACB_MUTE;
456 
457         reg = snd_soc_component_read32(component, CS42L51_DAC_OUT_CTL);
458 
459         if (mute)
460                 reg |= mask;
461         else
462                 reg &= ~mask;
463 
464         return snd_soc_component_write(component, CS42L51_DAC_OUT_CTL, reg);
465 }
466 
467 static const struct snd_soc_dai_ops cs42l51_dai_ops = {
468         .hw_params      = cs42l51_hw_params,
469         .set_sysclk     = cs42l51_set_dai_sysclk,
470         .set_fmt        = cs42l51_set_dai_fmt,
471         .digital_mute   = cs42l51_dai_mute,
472 };
473 
474 static struct snd_soc_dai_driver cs42l51_dai = {
475         .name = "cs42l51-hifi",
476         .playback = {
477                 .stream_name = "Playback",
478                 .channels_min = 1,
479                 .channels_max = 2,
480                 .rates = SNDRV_PCM_RATE_8000_96000,
481                 .formats = CS42L51_FORMATS,
482         },
483         .capture = {
484                 .stream_name = "Capture",
485                 .channels_min = 1,
486                 .channels_max = 2,
487                 .rates = SNDRV_PCM_RATE_8000_96000,
488                 .formats = CS42L51_FORMATS,
489         },
490         .ops = &cs42l51_dai_ops,
491 };
492 
493 static int cs42l51_component_probe(struct snd_soc_component *component)
494 {
495         int ret, reg;
496         struct snd_soc_dapm_context *dapm;
497         struct cs42l51_private *cs42l51;
498 
499         cs42l51 = snd_soc_component_get_drvdata(component);
500         dapm = snd_soc_component_get_dapm(component);
501 
502         if (cs42l51->mclk_handle)
503                 snd_soc_dapm_new_controls(dapm, cs42l51_dapm_mclk_widgets, 1);
504 
505         /*
506          * DAC configuration
507          * - Use signal processor
508          * - auto mute
509          * - vol changes immediate
510          * - no de-emphasize
511          */
512         reg = CS42L51_DAC_CTL_DATA_SEL(1)
513                 | CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0);
514         ret = snd_soc_component_write(component, CS42L51_DAC_CTL, reg);
515         if (ret < 0)
516                 return ret;
517 
518         return 0;
519 }
520 
521 static const struct snd_soc_component_driver soc_component_device_cs42l51 = {
522         .probe                  = cs42l51_component_probe,
523         .controls               = cs42l51_snd_controls,
524         .num_controls           = ARRAY_SIZE(cs42l51_snd_controls),
525         .dapm_widgets           = cs42l51_dapm_widgets,
526         .num_dapm_widgets       = ARRAY_SIZE(cs42l51_dapm_widgets),
527         .dapm_routes            = cs42l51_routes,
528         .num_dapm_routes        = ARRAY_SIZE(cs42l51_routes),
529         .idle_bias_on           = 1,
530         .use_pmdown_time        = 1,
531         .endianness             = 1,
532         .non_legacy_dai_naming  = 1,
533 };
534 
535 const struct regmap_config cs42l51_regmap = {
536         .max_register = CS42L51_CHARGE_FREQ,
537         .cache_type = REGCACHE_RBTREE,
538 };
539 EXPORT_SYMBOL_GPL(cs42l51_regmap);
540 
541 int cs42l51_probe(struct device *dev, struct regmap *regmap)
542 {
543         struct cs42l51_private *cs42l51;
544         unsigned int val;
545         int ret;
546 
547         if (IS_ERR(regmap))
548                 return PTR_ERR(regmap);
549 
550         cs42l51 = devm_kzalloc(dev, sizeof(struct cs42l51_private),
551                                GFP_KERNEL);
552         if (!cs42l51)
553                 return -ENOMEM;
554 
555         dev_set_drvdata(dev, cs42l51);
556 
557         cs42l51->mclk_handle = devm_clk_get(dev, "MCLK");
558         if (IS_ERR(cs42l51->mclk_handle)) {
559                 if (PTR_ERR(cs42l51->mclk_handle) != -ENOENT)
560                         return PTR_ERR(cs42l51->mclk_handle);
561                 cs42l51->mclk_handle = NULL;
562         }
563 
564         /* Verify that we have a CS42L51 */
565         ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val);
566         if (ret < 0) {
567                 dev_err(dev, "failed to read I2C\n");
568                 goto error;
569         }
570 
571         if ((val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) &&
572             (val != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) {
573                 dev_err(dev, "Invalid chip id: %x\n", val);
574                 ret = -ENODEV;
575                 goto error;
576         }
577         dev_info(dev, "Cirrus Logic CS42L51, Revision: %02X\n",
578                  val & CS42L51_CHIP_REV_MASK);
579 
580         ret = devm_snd_soc_register_component(dev,
581                         &soc_component_device_cs42l51, &cs42l51_dai, 1);
582 error:
583         return ret;
584 }
585 EXPORT_SYMBOL_GPL(cs42l51_probe);
586 
587 const struct of_device_id cs42l51_of_match[] = {
588         { .compatible = "cirrus,cs42l51", },
589         { }
590 };
591 MODULE_DEVICE_TABLE(of, cs42l51_of_match);
592 EXPORT_SYMBOL_GPL(cs42l51_of_match);
593 
594 MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
595 MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
596 MODULE_LICENSE("GPL");
597 

~ [ 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