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

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

Version: ~ [ linux-5.2 ] ~ [ linux-5.1.16 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.57 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.132 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.184 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.184 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.69 ] ~ [ 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.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  * File:         sound/soc/codecs/ssm2602.c
  3  * Author:       Cliff Cai <Cliff.Cai@analog.com>
  4  *
  5  * Created:      Tue June 06 2008
  6  * Description:  Driver for ssm2602 sound chip
  7  *
  8  * Modified:
  9  *               Copyright 2008 Analog Devices Inc.
 10  *
 11  * Bugs:         Enter bugs at http://blackfin.uclinux.org/
 12  *
 13  * This program is free software; you can redistribute it and/or modify
 14  * it under the terms of the GNU General Public License as published by
 15  * the Free Software Foundation; either version 2 of the License, or
 16  * (at your option) any later version.
 17  *
 18  * This program is distributed in the hope that it will be useful,
 19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 21  * GNU General Public License for more details.
 22  *
 23  * You should have received a copy of the GNU General Public License
 24  * along with this program; if not, see the file COPYING, or write
 25  * to the Free Software Foundation, Inc.,
 26  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 27  */
 28 
 29 #include <linux/module.h>
 30 #include <linux/regmap.h>
 31 #include <linux/slab.h>
 32 
 33 #include <sound/pcm.h>
 34 #include <sound/pcm_params.h>
 35 #include <sound/soc.h>
 36 #include <sound/tlv.h>
 37 
 38 #include "ssm2602.h"
 39 
 40 /* codec private data */
 41 struct ssm2602_priv {
 42         unsigned int sysclk;
 43         const struct snd_pcm_hw_constraint_list *sysclk_constraints;
 44 
 45         struct regmap *regmap;
 46 
 47         enum ssm2602_type type;
 48         unsigned int clk_out_pwr;
 49 };
 50 
 51 /*
 52  * ssm2602 register cache
 53  * We can't read the ssm2602 register space when we are
 54  * using 2 wire for device control, so we cache them instead.
 55  * There is no point in caching the reset register
 56  */
 57 static const struct reg_default ssm2602_reg[SSM2602_CACHEREGNUM] = {
 58         { .reg = 0x00, .def = 0x0097 },
 59         { .reg = 0x01, .def = 0x0097 },
 60         { .reg = 0x02, .def = 0x0079 },
 61         { .reg = 0x03, .def = 0x0079 },
 62         { .reg = 0x04, .def = 0x000a },
 63         { .reg = 0x05, .def = 0x0008 },
 64         { .reg = 0x06, .def = 0x009f },
 65         { .reg = 0x07, .def = 0x000a },
 66         { .reg = 0x08, .def = 0x0000 },
 67         { .reg = 0x09, .def = 0x0000 }
 68 };
 69 
 70 
 71 /*Appending several "None"s just for OSS mixer use*/
 72 static const char *ssm2602_input_select[] = {
 73         "Line", "Mic",
 74 };
 75 
 76 static const char *ssm2602_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
 77 
 78 static const struct soc_enum ssm2602_enum[] = {
 79         SOC_ENUM_SINGLE(SSM2602_APANA, 2, ARRAY_SIZE(ssm2602_input_select),
 80                         ssm2602_input_select),
 81         SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, ARRAY_SIZE(ssm2602_deemph),
 82                         ssm2602_deemph),
 83 };
 84 
 85 static const unsigned int ssm260x_outmix_tlv[] = {
 86         TLV_DB_RANGE_HEAD(2),
 87         0, 47, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 0),
 88         48, 127, TLV_DB_SCALE_ITEM(-7400, 100, 0),
 89 };
 90 
 91 static const DECLARE_TLV_DB_SCALE(ssm260x_inpga_tlv, -3450, 150, 0);
 92 static const DECLARE_TLV_DB_SCALE(ssm260x_sidetone_tlv, -1500, 300, 0);
 93 
 94 static const struct snd_kcontrol_new ssm260x_snd_controls[] = {
 95 SOC_DOUBLE_R_TLV("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 45, 0,
 96         ssm260x_inpga_tlv),
 97 SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1),
 98 
 99 SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1),
100 SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0),
101 
102 SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]),
103 };
104 
105 static const struct snd_kcontrol_new ssm2602_snd_controls[] = {
106 SOC_DOUBLE_R_TLV("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V,
107         0, 127, 0, ssm260x_outmix_tlv),
108 SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V,
109         7, 1, 0),
110 SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1,
111         ssm260x_sidetone_tlv),
112 
113 SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
114 SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
115 SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
116 };
117 
118 /* Output Mixer */
119 static const struct snd_kcontrol_new ssm260x_output_mixer_controls[] = {
120 SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0),
121 SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0),
122 SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0),
123 };
124 
125 /* Input mux */
126 static const struct snd_kcontrol_new ssm2602_input_mux_controls =
127 SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]);
128 
129 static const struct snd_soc_dapm_widget ssm260x_dapm_widgets[] = {
130 SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1),
131 SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1),
132 SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0),
133 
134 SND_SOC_DAPM_SUPPLY("Digital Core Power", SSM2602_ACTIVE, 0, 0, NULL, 0),
135 
136 SND_SOC_DAPM_OUTPUT("LOUT"),
137 SND_SOC_DAPM_OUTPUT("ROUT"),
138 SND_SOC_DAPM_INPUT("RLINEIN"),
139 SND_SOC_DAPM_INPUT("LLINEIN"),
140 };
141 
142 static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = {
143 SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
144         ssm260x_output_mixer_controls,
145         ARRAY_SIZE(ssm260x_output_mixer_controls)),
146 
147 SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls),
148 SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1),
149 
150 SND_SOC_DAPM_OUTPUT("LHPOUT"),
151 SND_SOC_DAPM_OUTPUT("RHPOUT"),
152 SND_SOC_DAPM_INPUT("MICIN"),
153 };
154 
155 static const struct snd_soc_dapm_widget ssm2604_dapm_widgets[] = {
156 SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
157         ssm260x_output_mixer_controls,
158         ARRAY_SIZE(ssm260x_output_mixer_controls) - 1), /* Last element is the mic */
159 };
160 
161 static const struct snd_soc_dapm_route ssm260x_routes[] = {
162         {"DAC", NULL, "Digital Core Power"},
163         {"ADC", NULL, "Digital Core Power"},
164 
165         {"Output Mixer", "Line Bypass Switch", "Line Input"},
166         {"Output Mixer", "HiFi Playback Switch", "DAC"},
167 
168         {"ROUT", NULL, "Output Mixer"},
169         {"LOUT", NULL, "Output Mixer"},
170 
171         {"Line Input", NULL, "LLINEIN"},
172         {"Line Input", NULL, "RLINEIN"},
173 };
174 
175 static const struct snd_soc_dapm_route ssm2602_routes[] = {
176         {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
177 
178         {"RHPOUT", NULL, "Output Mixer"},
179         {"LHPOUT", NULL, "Output Mixer"},
180 
181         {"Input Mux", "Line", "Line Input"},
182         {"Input Mux", "Mic", "Mic Bias"},
183         {"ADC", NULL, "Input Mux"},
184 
185         {"Mic Bias", NULL, "MICIN"},
186 };
187 
188 static const struct snd_soc_dapm_route ssm2604_routes[] = {
189         {"ADC", NULL, "Line Input"},
190 };
191 
192 static const unsigned int ssm2602_rates_12288000[] = {
193         8000, 16000, 32000, 48000, 96000,
194 };
195 
196 static const struct snd_pcm_hw_constraint_list ssm2602_constraints_12288000 = {
197         .list = ssm2602_rates_12288000,
198         .count = ARRAY_SIZE(ssm2602_rates_12288000),
199 };
200 
201 static const unsigned int ssm2602_rates_11289600[] = {
202         8000, 11025, 22050, 44100, 88200,
203 };
204 
205 static const struct snd_pcm_hw_constraint_list ssm2602_constraints_11289600 = {
206         .list = ssm2602_rates_11289600,
207         .count = ARRAY_SIZE(ssm2602_rates_11289600),
208 };
209 
210 struct ssm2602_coeff {
211         u32 mclk;
212         u32 rate;
213         u8 srate;
214 };
215 
216 #define SSM2602_COEFF_SRATE(sr, bosr, usb) (((sr) << 2) | ((bosr) << 1) | (usb))
217 
218 /* codec mclk clock coefficients */
219 static const struct ssm2602_coeff ssm2602_coeff_table[] = {
220         /* 48k */
221         {12288000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x0)},
222         {18432000, 48000, SSM2602_COEFF_SRATE(0x0, 0x1, 0x0)},
223         {12000000, 48000, SSM2602_COEFF_SRATE(0x0, 0x0, 0x1)},
224 
225         /* 32k */
226         {12288000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x0)},
227         {18432000, 32000, SSM2602_COEFF_SRATE(0x6, 0x1, 0x0)},
228         {12000000, 32000, SSM2602_COEFF_SRATE(0x6, 0x0, 0x1)},
229 
230         /* 16k */
231         {12288000, 16000, SSM2602_COEFF_SRATE(0x5, 0x0, 0x0)},
232         {18432000, 16000, SSM2602_COEFF_SRATE(0x5, 0x1, 0x0)},
233         {12000000, 16000, SSM2602_COEFF_SRATE(0xa, 0x0, 0x1)},
234 
235         /* 8k */
236         {12288000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x0)},
237         {18432000, 8000, SSM2602_COEFF_SRATE(0x3, 0x1, 0x0)},
238         {11289600, 8000, SSM2602_COEFF_SRATE(0xb, 0x0, 0x0)},
239         {16934400, 8000, SSM2602_COEFF_SRATE(0xb, 0x1, 0x0)},
240         {12000000, 8000, SSM2602_COEFF_SRATE(0x3, 0x0, 0x1)},
241 
242         /* 96k */
243         {12288000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x0)},
244         {18432000, 96000, SSM2602_COEFF_SRATE(0x7, 0x1, 0x0)},
245         {12000000, 96000, SSM2602_COEFF_SRATE(0x7, 0x0, 0x1)},
246 
247         /* 11.025k */
248         {11289600, 11025, SSM2602_COEFF_SRATE(0xc, 0x0, 0x0)},
249         {16934400, 11025, SSM2602_COEFF_SRATE(0xc, 0x1, 0x0)},
250         {12000000, 11025, SSM2602_COEFF_SRATE(0xc, 0x1, 0x1)},
251 
252         /* 22.05k */
253         {11289600, 22050, SSM2602_COEFF_SRATE(0xd, 0x0, 0x0)},
254         {16934400, 22050, SSM2602_COEFF_SRATE(0xd, 0x1, 0x0)},
255         {12000000, 22050, SSM2602_COEFF_SRATE(0xd, 0x1, 0x1)},
256 
257         /* 44.1k */
258         {11289600, 44100, SSM2602_COEFF_SRATE(0x8, 0x0, 0x0)},
259         {16934400, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x0)},
260         {12000000, 44100, SSM2602_COEFF_SRATE(0x8, 0x1, 0x1)},
261 
262         /* 88.2k */
263         {11289600, 88200, SSM2602_COEFF_SRATE(0xf, 0x0, 0x0)},
264         {16934400, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x0)},
265         {12000000, 88200, SSM2602_COEFF_SRATE(0xf, 0x1, 0x1)},
266 };
267 
268 static inline int ssm2602_get_coeff(int mclk, int rate)
269 {
270         int i;
271 
272         for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) {
273                 if (ssm2602_coeff_table[i].rate == rate &&
274                         ssm2602_coeff_table[i].mclk == mclk)
275                         return ssm2602_coeff_table[i].srate;
276         }
277         return -EINVAL;
278 }
279 
280 static int ssm2602_hw_params(struct snd_pcm_substream *substream,
281         struct snd_pcm_hw_params *params,
282         struct snd_soc_dai *dai)
283 {
284         struct snd_soc_codec *codec = dai->codec;
285         struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
286         int srate = ssm2602_get_coeff(ssm2602->sysclk, params_rate(params));
287         unsigned int iface;
288 
289         if (srate < 0)
290                 return srate;
291 
292         regmap_write(ssm2602->regmap, SSM2602_SRATE, srate);
293 
294         /* bit size */
295         switch (params_width(params)) {
296         case 16:
297                 iface = 0x0;
298                 break;
299         case 20:
300                 iface = 0x4;
301                 break;
302         case 24:
303                 iface = 0x8;
304                 break;
305         case 32:
306                 iface = 0xc;
307                 break;
308         default:
309                 return -EINVAL;
310         }
311         regmap_update_bits(ssm2602->regmap, SSM2602_IFACE,
312                 IFACE_AUDIO_DATA_LEN, iface);
313         return 0;
314 }
315 
316 static int ssm2602_startup(struct snd_pcm_substream *substream,
317                            struct snd_soc_dai *dai)
318 {
319         struct snd_soc_codec *codec = dai->codec;
320         struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
321 
322         if (ssm2602->sysclk_constraints) {
323                 snd_pcm_hw_constraint_list(substream->runtime, 0,
324                                    SNDRV_PCM_HW_PARAM_RATE,
325                                    ssm2602->sysclk_constraints);
326         }
327 
328         return 0;
329 }
330 
331 static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
332 {
333         struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(dai->codec);
334 
335         if (mute)
336                 regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI,
337                                     APDIGI_ENABLE_DAC_MUTE,
338                                     APDIGI_ENABLE_DAC_MUTE);
339         else
340                 regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI,
341                                     APDIGI_ENABLE_DAC_MUTE, 0);
342         return 0;
343 }
344 
345 static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
346                 int clk_id, unsigned int freq, int dir)
347 {
348         struct snd_soc_codec *codec = codec_dai->codec;
349         struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
350 
351         if (dir == SND_SOC_CLOCK_IN) {
352                 if (clk_id != SSM2602_SYSCLK)
353                         return -EINVAL;
354 
355                 switch (freq) {
356                 case 12288000:
357                 case 18432000:
358                         ssm2602->sysclk_constraints = &ssm2602_constraints_12288000;
359                         break;
360                 case 11289600:
361                 case 16934400:
362                         ssm2602->sysclk_constraints = &ssm2602_constraints_11289600;
363                         break;
364                 case 12000000:
365                         ssm2602->sysclk_constraints = NULL;
366                         break;
367                 default:
368                         return -EINVAL;
369                 }
370                 ssm2602->sysclk = freq;
371         } else {
372                 unsigned int mask;
373 
374                 switch (clk_id) {
375                 case SSM2602_CLK_CLKOUT:
376                         mask = PWR_CLK_OUT_PDN;
377                         break;
378                 case SSM2602_CLK_XTO:
379                         mask = PWR_OSC_PDN;
380                         break;
381                 default:
382                         return -EINVAL;
383                 }
384 
385                 if (freq == 0)
386                         ssm2602->clk_out_pwr |= mask;
387                 else
388                         ssm2602->clk_out_pwr &= ~mask;
389 
390                 regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
391                         PWR_CLK_OUT_PDN | PWR_OSC_PDN, ssm2602->clk_out_pwr);
392         }
393 
394         return 0;
395 }
396 
397 static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
398                 unsigned int fmt)
399 {
400         struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec_dai->codec);
401         unsigned int iface = 0;
402 
403         /* set master/slave audio interface */
404         switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
405         case SND_SOC_DAIFMT_CBM_CFM:
406                 iface |= 0x0040;
407                 break;
408         case SND_SOC_DAIFMT_CBS_CFS:
409                 break;
410         default:
411                 return -EINVAL;
412         }
413 
414         /* interface format */
415         switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
416         case SND_SOC_DAIFMT_I2S:
417                 iface |= 0x0002;
418                 break;
419         case SND_SOC_DAIFMT_RIGHT_J:
420                 break;
421         case SND_SOC_DAIFMT_LEFT_J:
422                 iface |= 0x0001;
423                 break;
424         case SND_SOC_DAIFMT_DSP_A:
425                 iface |= 0x0013;
426                 break;
427         case SND_SOC_DAIFMT_DSP_B:
428                 iface |= 0x0003;
429                 break;
430         default:
431                 return -EINVAL;
432         }
433 
434         /* clock inversion */
435         switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
436         case SND_SOC_DAIFMT_NB_NF:
437                 break;
438         case SND_SOC_DAIFMT_IB_IF:
439                 iface |= 0x0090;
440                 break;
441         case SND_SOC_DAIFMT_IB_NF:
442                 iface |= 0x0080;
443                 break;
444         case SND_SOC_DAIFMT_NB_IF:
445                 iface |= 0x0010;
446                 break;
447         default:
448                 return -EINVAL;
449         }
450 
451         /* set iface */
452         regmap_write(ssm2602->regmap, SSM2602_IFACE, iface);
453         return 0;
454 }
455 
456 static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
457                                  enum snd_soc_bias_level level)
458 {
459         struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
460 
461         switch (level) {
462         case SND_SOC_BIAS_ON:
463                 /* vref/mid on, osc and clkout on if enabled */
464                 regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
465                         PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN,
466                         ssm2602->clk_out_pwr);
467                 break;
468         case SND_SOC_BIAS_PREPARE:
469                 break;
470         case SND_SOC_BIAS_STANDBY:
471                 /* everything off except vref/vmid, */
472                 regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
473                         PWR_POWER_OFF | PWR_CLK_OUT_PDN | PWR_OSC_PDN,
474                         PWR_CLK_OUT_PDN | PWR_OSC_PDN);
475                 break;
476         case SND_SOC_BIAS_OFF:
477                 /* everything off */
478                 regmap_update_bits(ssm2602->regmap, SSM2602_PWR,
479                         PWR_POWER_OFF, PWR_POWER_OFF);
480                 break;
481 
482         }
483         codec->dapm.bias_level = level;
484         return 0;
485 }
486 
487 #define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
488                 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
489                 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
490                 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
491                 SNDRV_PCM_RATE_96000)
492 
493 #define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
494                 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
495 
496 static const struct snd_soc_dai_ops ssm2602_dai_ops = {
497         .startup        = ssm2602_startup,
498         .hw_params      = ssm2602_hw_params,
499         .digital_mute   = ssm2602_mute,
500         .set_sysclk     = ssm2602_set_dai_sysclk,
501         .set_fmt        = ssm2602_set_dai_fmt,
502 };
503 
504 static struct snd_soc_dai_driver ssm2602_dai = {
505         .name = "ssm2602-hifi",
506         .playback = {
507                 .stream_name = "Playback",
508                 .channels_min = 2,
509                 .channels_max = 2,
510                 .rates = SSM2602_RATES,
511                 .formats = SSM2602_FORMATS,},
512         .capture = {
513                 .stream_name = "Capture",
514                 .channels_min = 2,
515                 .channels_max = 2,
516                 .rates = SSM2602_RATES,
517                 .formats = SSM2602_FORMATS,},
518         .ops = &ssm2602_dai_ops,
519         .symmetric_rates = 1,
520         .symmetric_samplebits = 1,
521 };
522 
523 static int ssm2602_resume(struct snd_soc_codec *codec)
524 {
525         struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
526 
527         regcache_sync(ssm2602->regmap);
528 
529         return 0;
530 }
531 
532 static int ssm2602_codec_probe(struct snd_soc_codec *codec)
533 {
534         struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
535         struct snd_soc_dapm_context *dapm = &codec->dapm;
536         int ret;
537 
538         regmap_update_bits(ssm2602->regmap, SSM2602_LOUT1V,
539                             LOUT1V_LRHP_BOTH, LOUT1V_LRHP_BOTH);
540         regmap_update_bits(ssm2602->regmap, SSM2602_ROUT1V,
541                             ROUT1V_RLHP_BOTH, ROUT1V_RLHP_BOTH);
542 
543         ret = snd_soc_add_codec_controls(codec, ssm2602_snd_controls,
544                         ARRAY_SIZE(ssm2602_snd_controls));
545         if (ret)
546                 return ret;
547 
548         ret = snd_soc_dapm_new_controls(dapm, ssm2602_dapm_widgets,
549                         ARRAY_SIZE(ssm2602_dapm_widgets));
550         if (ret)
551                 return ret;
552 
553         return snd_soc_dapm_add_routes(dapm, ssm2602_routes,
554                         ARRAY_SIZE(ssm2602_routes));
555 }
556 
557 static int ssm2604_codec_probe(struct snd_soc_codec *codec)
558 {
559         struct snd_soc_dapm_context *dapm = &codec->dapm;
560         int ret;
561 
562         ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets,
563                         ARRAY_SIZE(ssm2604_dapm_widgets));
564         if (ret)
565                 return ret;
566 
567         return snd_soc_dapm_add_routes(dapm, ssm2604_routes,
568                         ARRAY_SIZE(ssm2604_routes));
569 }
570 
571 static int ssm260x_codec_probe(struct snd_soc_codec *codec)
572 {
573         struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
574         int ret;
575 
576         ret = regmap_write(ssm2602->regmap, SSM2602_RESET, 0);
577         if (ret < 0) {
578                 dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
579                 return ret;
580         }
581 
582         /* set the update bits */
583         regmap_update_bits(ssm2602->regmap, SSM2602_LINVOL,
584                             LINVOL_LRIN_BOTH, LINVOL_LRIN_BOTH);
585         regmap_update_bits(ssm2602->regmap, SSM2602_RINVOL,
586                             RINVOL_RLIN_BOTH, RINVOL_RLIN_BOTH);
587         /*select Line in as default input*/
588         regmap_write(ssm2602->regmap, SSM2602_APANA, APANA_SELECT_DAC |
589                         APANA_ENABLE_MIC_BOOST);
590 
591         switch (ssm2602->type) {
592         case SSM2602:
593                 ret = ssm2602_codec_probe(codec);
594                 break;
595         case SSM2604:
596                 ret = ssm2604_codec_probe(codec);
597                 break;
598         }
599 
600         return ret;
601 }
602 
603 static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
604         .probe =        ssm260x_codec_probe,
605         .resume =       ssm2602_resume,
606         .set_bias_level = ssm2602_set_bias_level,
607         .suspend_bias_off = true,
608 
609         .controls = ssm260x_snd_controls,
610         .num_controls = ARRAY_SIZE(ssm260x_snd_controls),
611         .dapm_widgets = ssm260x_dapm_widgets,
612         .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets),
613         .dapm_routes = ssm260x_routes,
614         .num_dapm_routes = ARRAY_SIZE(ssm260x_routes),
615 };
616 
617 static bool ssm2602_register_volatile(struct device *dev, unsigned int reg)
618 {
619         return reg == SSM2602_RESET;
620 }
621 
622 const struct regmap_config ssm2602_regmap_config = {
623         .val_bits = 9,
624         .reg_bits = 7,
625 
626         .max_register = SSM2602_RESET,
627         .volatile_reg = ssm2602_register_volatile,
628 
629         .cache_type = REGCACHE_RBTREE,
630         .reg_defaults = ssm2602_reg,
631         .num_reg_defaults = ARRAY_SIZE(ssm2602_reg),
632 };
633 EXPORT_SYMBOL_GPL(ssm2602_regmap_config);
634 
635 int ssm2602_probe(struct device *dev, enum ssm2602_type type,
636         struct regmap *regmap)
637 {
638         struct ssm2602_priv *ssm2602;
639 
640         if (IS_ERR(regmap))
641                 return PTR_ERR(regmap);
642 
643         ssm2602 = devm_kzalloc(dev, sizeof(*ssm2602), GFP_KERNEL);
644         if (ssm2602 == NULL)
645                 return -ENOMEM;
646 
647         dev_set_drvdata(dev, ssm2602);
648         ssm2602->type = type;
649         ssm2602->regmap = regmap;
650 
651         return snd_soc_register_codec(dev, &soc_codec_dev_ssm2602,
652                 &ssm2602_dai, 1);
653 }
654 EXPORT_SYMBOL_GPL(ssm2602_probe);
655 
656 MODULE_DESCRIPTION("ASoC SSM2602/SSM2603/SSM2604 driver");
657 MODULE_AUTHOR("Cliff Cai");
658 MODULE_LICENSE("GPL");
659 

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