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

TOMOYO Linux Cross Reference
Linux/sound/soc/sunxi/sun8i-codec.c

Version: ~ [ linux-5.8-rc5 ] ~ [ linux-5.7.8 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.51 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.132 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.188 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.230 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.230 ] ~ [ 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.85 ] ~ [ 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-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  * This driver supports the digital controls for the internal codec
  3  * found in Allwinner's A33 SoCs.
  4  *
  5  * (C) Copyright 2010-2016
  6  * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
  7  * huangxin <huangxin@Reuuimllatech.com>
  8  * Mylène Josserand <mylene.josserand@free-electrons.com>
  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 as published by
 12  * the Free Software Foundation; either version 2 of the License, or
 13  * (at your option) any later version.
 14  *
 15  * This program is distributed in the hope that it will be useful,
 16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 18  * GNU General Public License for more details.
 19  */
 20 
 21 #include <linux/module.h>
 22 #include <linux/delay.h>
 23 #include <linux/clk.h>
 24 #include <linux/io.h>
 25 #include <linux/pm_runtime.h>
 26 #include <linux/regmap.h>
 27 
 28 #include <sound/pcm_params.h>
 29 #include <sound/soc.h>
 30 #include <sound/soc-dapm.h>
 31 
 32 #define SUN8I_SYSCLK_CTL                                0x00c
 33 #define SUN8I_SYSCLK_CTL_AIF1CLK_ENA                    11
 34 #define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL                9
 35 #define SUN8I_SYSCLK_CTL_AIF1CLK_SRC                    8
 36 #define SUN8I_SYSCLK_CTL_SYSCLK_ENA                     3
 37 #define SUN8I_SYSCLK_CTL_SYSCLK_SRC                     0
 38 #define SUN8I_MOD_CLK_ENA                               0x010
 39 #define SUN8I_MOD_CLK_ENA_AIF1                          15
 40 #define SUN8I_MOD_CLK_ENA_ADC                           3
 41 #define SUN8I_MOD_CLK_ENA_DAC                           2
 42 #define SUN8I_MOD_RST_CTL                               0x014
 43 #define SUN8I_MOD_RST_CTL_AIF1                          15
 44 #define SUN8I_MOD_RST_CTL_ADC                           3
 45 #define SUN8I_MOD_RST_CTL_DAC                           2
 46 #define SUN8I_SYS_SR_CTRL                               0x018
 47 #define SUN8I_SYS_SR_CTRL_AIF1_FS                       12
 48 #define SUN8I_SYS_SR_CTRL_AIF2_FS                       8
 49 #define SUN8I_AIF1CLK_CTRL                              0x040
 50 #define SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD                15
 51 #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV                14
 52 #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV                13
 53 #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV                9
 54 #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV                6
 55 #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_16             (1 << 6)
 56 #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ                4
 57 #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16             (1 << 4)
 58 #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT                2
 59 #define SUN8I_AIF1_ADCDAT_CTRL                          0x044
 60 #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0L_ENA            15
 61 #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0R_ENA            14
 62 #define SUN8I_AIF1_DACDAT_CTRL                          0x048
 63 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA            15
 64 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA            14
 65 #define SUN8I_AIF1_MXR_SRC                              0x04c
 66 #define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF1DA0L        15
 67 #define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACL        14
 68 #define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_ADCL            13
 69 #define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACR        12
 70 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R        11
 71 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR        10
 72 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR            9
 73 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL        8
 74 #define SUN8I_ADC_DIG_CTRL                              0x100
 75 #define SUN8I_ADC_DIG_CTRL_ENDA                 15
 76 #define SUN8I_ADC_DIG_CTRL_ADOUT_DTS                    2
 77 #define SUN8I_ADC_DIG_CTRL_ADOUT_DLY                    1
 78 #define SUN8I_DAC_DIG_CTRL                              0x120
 79 #define SUN8I_DAC_DIG_CTRL_ENDA                 15
 80 #define SUN8I_DAC_MXR_SRC                               0x130
 81 #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L 15
 82 #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L 14
 83 #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL 13
 84 #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL             12
 85 #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R 11
 86 #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R 10
 87 #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR 9
 88 #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR             8
 89 
 90 #define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK          GENMASK(15, 12)
 91 #define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK          GENMASK(11, 8)
 92 #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK   GENMASK(5, 4)
 93 #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK   GENMASK(8, 6)
 94 #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK   GENMASK(12, 9)
 95 
 96 struct sun8i_codec {
 97         struct device   *dev;
 98         struct regmap   *regmap;
 99         struct clk      *clk_module;
100         struct clk      *clk_bus;
101 };
102 
103 static int sun8i_codec_runtime_resume(struct device *dev)
104 {
105         struct sun8i_codec *scodec = dev_get_drvdata(dev);
106         int ret;
107 
108         ret = clk_prepare_enable(scodec->clk_module);
109         if (ret) {
110                 dev_err(dev, "Failed to enable the module clock\n");
111                 return ret;
112         }
113 
114         ret = clk_prepare_enable(scodec->clk_bus);
115         if (ret) {
116                 dev_err(dev, "Failed to enable the bus clock\n");
117                 goto err_disable_modclk;
118         }
119 
120         regcache_cache_only(scodec->regmap, false);
121 
122         ret = regcache_sync(scodec->regmap);
123         if (ret) {
124                 dev_err(dev, "Failed to sync regmap cache\n");
125                 goto err_disable_clk;
126         }
127 
128         return 0;
129 
130 err_disable_clk:
131         clk_disable_unprepare(scodec->clk_bus);
132 
133 err_disable_modclk:
134         clk_disable_unprepare(scodec->clk_module);
135 
136         return ret;
137 }
138 
139 static int sun8i_codec_runtime_suspend(struct device *dev)
140 {
141         struct sun8i_codec *scodec = dev_get_drvdata(dev);
142 
143         regcache_cache_only(scodec->regmap, true);
144         regcache_mark_dirty(scodec->regmap);
145 
146         clk_disable_unprepare(scodec->clk_module);
147         clk_disable_unprepare(scodec->clk_bus);
148 
149         return 0;
150 }
151 
152 static int sun8i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
153 {
154         unsigned int rate = params_rate(params);
155 
156         switch (rate) {
157         case 8000:
158         case 7350:
159                 return 0x0;
160         case 11025:
161                 return 0x1;
162         case 12000:
163                 return 0x2;
164         case 16000:
165                 return 0x3;
166         case 22050:
167                 return 0x4;
168         case 24000:
169                 return 0x5;
170         case 32000:
171                 return 0x6;
172         case 44100:
173                 return 0x7;
174         case 48000:
175                 return 0x8;
176         case 96000:
177                 return 0x9;
178         case 192000:
179                 return 0xa;
180         default:
181                 return -EINVAL;
182         }
183 }
184 
185 static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
186 {
187         struct sun8i_codec *scodec = snd_soc_component_get_drvdata(dai->component);
188         u32 value;
189 
190         /* clock masters */
191         switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
192         case SND_SOC_DAIFMT_CBS_CFS: /* Codec slave, DAI master */
193                 value = 0x1;
194                 break;
195         case SND_SOC_DAIFMT_CBM_CFM: /* Codec Master, DAI slave */
196                 value = 0x0;
197                 break;
198         default:
199                 return -EINVAL;
200         }
201         regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
202                            BIT(SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD),
203                            value << SUN8I_AIF1CLK_CTRL_AIF1_MSTR_MOD);
204 
205         /* clock inversion */
206         switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
207         case SND_SOC_DAIFMT_NB_NF: /* Normal */
208                 value = 0x0;
209                 break;
210         case SND_SOC_DAIFMT_IB_IF: /* Inversion */
211                 value = 0x1;
212                 break;
213         default:
214                 return -EINVAL;
215         }
216         regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
217                            BIT(SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV),
218                            value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV);
219 
220         /*
221          * It appears that the DAI and the codec don't share the same
222          * polarity for the LRCK signal when they mean 'normal' and
223          * 'inverted' in the datasheet.
224          *
225          * Since the DAI here is our regular i2s driver that have been
226          * tested with way more codecs than just this one, it means
227          * that the codec probably gets it backward, and we have to
228          * invert the value here.
229          */
230         regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
231                            BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV),
232                            !value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV);
233 
234         /* DAI format */
235         switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
236         case SND_SOC_DAIFMT_I2S:
237                 value = 0x0;
238                 break;
239         case SND_SOC_DAIFMT_LEFT_J:
240                 value = 0x1;
241                 break;
242         case SND_SOC_DAIFMT_RIGHT_J:
243                 value = 0x2;
244                 break;
245         case SND_SOC_DAIFMT_DSP_A:
246         case SND_SOC_DAIFMT_DSP_B:
247                 value = 0x3;
248                 break;
249         default:
250                 return -EINVAL;
251         }
252         regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
253                            BIT(SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT),
254                            value << SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT);
255 
256         return 0;
257 }
258 
259 struct sun8i_codec_clk_div {
260         u8      div;
261         u8      val;
262 };
263 
264 static const struct sun8i_codec_clk_div sun8i_codec_bclk_div[] = {
265         { .div = 1,     .val = 0 },
266         { .div = 2,     .val = 1 },
267         { .div = 4,     .val = 2 },
268         { .div = 6,     .val = 3 },
269         { .div = 8,     .val = 4 },
270         { .div = 12,    .val = 5 },
271         { .div = 16,    .val = 6 },
272         { .div = 24,    .val = 7 },
273         { .div = 32,    .val = 8 },
274         { .div = 48,    .val = 9 },
275         { .div = 64,    .val = 10 },
276         { .div = 96,    .val = 11 },
277         { .div = 128,   .val = 12 },
278         { .div = 192,   .val = 13 },
279 };
280 
281 static u8 sun8i_codec_get_bclk_div(struct sun8i_codec *scodec,
282                                    unsigned int rate,
283                                    unsigned int word_size)
284 {
285         unsigned long clk_rate = clk_get_rate(scodec->clk_module);
286         unsigned int div = clk_rate / rate / word_size / 2;
287         unsigned int best_val = 0, best_diff = ~0;
288         int i;
289 
290         for (i = 0; i < ARRAY_SIZE(sun8i_codec_bclk_div); i++) {
291                 const struct sun8i_codec_clk_div *bdiv = &sun8i_codec_bclk_div[i];
292                 unsigned int diff = abs(bdiv->div - div);
293 
294                 if (diff < best_diff) {
295                         best_diff = diff;
296                         best_val = bdiv->val;
297                 }
298         }
299 
300         return best_val;
301 }
302 
303 static int sun8i_codec_hw_params(struct snd_pcm_substream *substream,
304                                  struct snd_pcm_hw_params *params,
305                                  struct snd_soc_dai *dai)
306 {
307         struct sun8i_codec *scodec = snd_soc_component_get_drvdata(dai->component);
308         int sample_rate;
309         u8 bclk_div;
310 
311         /*
312          * The CPU DAI handles only a sample of 16 bits. Configure the
313          * codec to handle this type of sample resolution.
314          */
315         regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
316                            SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK,
317                            SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16);
318 
319         bclk_div = sun8i_codec_get_bclk_div(scodec, params_rate(params), 16);
320         regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
321                            SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK,
322                            bclk_div << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV);
323 
324         regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL,
325                            SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK,
326                            SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_16);
327 
328         sample_rate = sun8i_codec_get_hw_rate(params);
329         if (sample_rate < 0)
330                 return sample_rate;
331 
332         regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
333                            SUN8I_SYS_SR_CTRL_AIF1_FS_MASK,
334                            sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS);
335         regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL,
336                            SUN8I_SYS_SR_CTRL_AIF2_FS_MASK,
337                            sample_rate << SUN8I_SYS_SR_CTRL_AIF2_FS);
338 
339         return 0;
340 }
341 
342 static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = {
343         SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital DAC Playback Switch",
344                         SUN8I_DAC_MXR_SRC,
345                         SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L,
346                         SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R, 1, 0),
347         SOC_DAPM_DOUBLE("AIF1 Slot 1 Digital DAC Playback Switch",
348                         SUN8I_DAC_MXR_SRC,
349                         SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L,
350                         SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R, 1, 0),
351         SOC_DAPM_DOUBLE("AIF2 Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC,
352                         SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL,
353                         SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR, 1, 0),
354         SOC_DAPM_DOUBLE("ADC Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC,
355                         SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL,
356                         SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0),
357 };
358 
359 static const struct snd_kcontrol_new sun8i_input_mixer_controls[] = {
360         SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch",
361                         SUN8I_AIF1_MXR_SRC,
362                         SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF1DA0L,
363                         SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0),
364         SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC,
365                         SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACL,
366                         SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0),
367         SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch",
368                         SUN8I_AIF1_MXR_SRC,
369                         SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_ADCL,
370                         SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0),
371         SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch",
372                         SUN8I_AIF1_MXR_SRC,
373                         SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACR,
374                         SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0),
375 };
376 
377 static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
378         /* Digital parts of the DACs and ADC */
379         SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA,
380                             0, NULL, 0),
381         SND_SOC_DAPM_SUPPLY("ADC", SUN8I_ADC_DIG_CTRL, SUN8I_ADC_DIG_CTRL_ENDA,
382                             0, NULL, 0),
383 
384         /* Analog DAC AIF */
385         SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left", "Playback", 0,
386                             SUN8I_AIF1_DACDAT_CTRL,
387                             SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0),
388         SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Right", "Playback", 0,
389                             SUN8I_AIF1_DACDAT_CTRL,
390                             SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
391 
392         /* Analog ADC AIF */
393         SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left ADC", "Capture", 0,
394                             SUN8I_AIF1_ADCDAT_CTRL,
395                             SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0L_ENA, 0),
396         SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Right ADC", "Capture", 0,
397                             SUN8I_AIF1_ADCDAT_CTRL,
398                             SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0R_ENA, 0),
399 
400         /* DAC and ADC Mixers */
401         SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
402                         sun8i_dac_mixer_controls),
403         SOC_MIXER_ARRAY("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
404                         sun8i_dac_mixer_controls),
405         SOC_MIXER_ARRAY("Left Digital ADC Mixer", SND_SOC_NOPM, 0, 0,
406                         sun8i_input_mixer_controls),
407         SOC_MIXER_ARRAY("Right Digital ADC Mixer", SND_SOC_NOPM, 0, 0,
408                         sun8i_input_mixer_controls),
409 
410         /* Clocks */
411         SND_SOC_DAPM_SUPPLY("MODCLK AFI1", SUN8I_MOD_CLK_ENA,
412                             SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0),
413         SND_SOC_DAPM_SUPPLY("MODCLK DAC", SUN8I_MOD_CLK_ENA,
414                             SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0),
415         SND_SOC_DAPM_SUPPLY("MODCLK ADC", SUN8I_MOD_CLK_ENA,
416                             SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0),
417         SND_SOC_DAPM_SUPPLY("AIF1", SUN8I_SYSCLK_CTL,
418                             SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0),
419         SND_SOC_DAPM_SUPPLY("SYSCLK", SUN8I_SYSCLK_CTL,
420                             SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0),
421 
422         SND_SOC_DAPM_SUPPLY("AIF1 PLL", SUN8I_SYSCLK_CTL,
423                             SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL, 0, NULL, 0),
424         /* Inversion as 0=AIF1, 1=AIF2 */
425         SND_SOC_DAPM_SUPPLY("SYSCLK AIF1", SUN8I_SYSCLK_CTL,
426                             SUN8I_SYSCLK_CTL_SYSCLK_SRC, 1, NULL, 0),
427 
428         /* Module reset */
429         SND_SOC_DAPM_SUPPLY("RST AIF1", SUN8I_MOD_RST_CTL,
430                             SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0),
431         SND_SOC_DAPM_SUPPLY("RST DAC", SUN8I_MOD_RST_CTL,
432                             SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0),
433         SND_SOC_DAPM_SUPPLY("RST ADC", SUN8I_MOD_RST_CTL,
434                             SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0),
435 
436         SND_SOC_DAPM_MIC("Headset Mic", NULL),
437         SND_SOC_DAPM_MIC("Mic", NULL),
438 
439 };
440 
441 static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
442         /* Clock Routes */
443         { "AIF1", NULL, "SYSCLK AIF1" },
444         { "AIF1 PLL", NULL, "AIF1" },
445         { "RST AIF1", NULL, "AIF1 PLL" },
446         { "MODCLK AFI1", NULL, "RST AIF1" },
447         { "DAC", NULL, "MODCLK AFI1" },
448         { "ADC", NULL, "MODCLK AFI1" },
449 
450         { "RST DAC", NULL, "SYSCLK" },
451         { "MODCLK DAC", NULL, "RST DAC" },
452         { "DAC", NULL, "MODCLK DAC" },
453 
454         { "RST ADC", NULL, "SYSCLK" },
455         { "MODCLK ADC", NULL, "RST ADC" },
456         { "ADC", NULL, "MODCLK ADC" },
457 
458         /* DAC Routes */
459         { "AIF1 Slot 0 Right", NULL, "DAC" },
460         { "AIF1 Slot 0 Left", NULL, "DAC" },
461 
462         /* DAC Mixer Routes */
463         { "Left Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch",
464           "AIF1 Slot 0 Left"},
465         { "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch",
466           "AIF1 Slot 0 Right"},
467 
468         /* ADC routes */
469         { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch",
470           "AIF1 Slot 0 Left ADC" },
471         { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch",
472           "AIF1 Slot 0 Right ADC" },
473 };
474 
475 static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
476         .hw_params = sun8i_codec_hw_params,
477         .set_fmt = sun8i_set_fmt,
478 };
479 
480 static struct snd_soc_dai_driver sun8i_codec_dai = {
481         .name = "sun8i",
482         /* playback capabilities */
483         .playback = {
484                 .stream_name = "Playback",
485                 .channels_min = 1,
486                 .channels_max = 2,
487                 .rates = SNDRV_PCM_RATE_8000_192000,
488                 .formats = SNDRV_PCM_FMTBIT_S16_LE,
489         },
490         /* capture capabilities */
491         .capture = {
492                 .stream_name = "Capture",
493                 .channels_min = 1,
494                 .channels_max = 2,
495                 .rates = SNDRV_PCM_RATE_8000_192000,
496                 .formats = SNDRV_PCM_FMTBIT_S16_LE,
497                 .sig_bits = 24,
498         },
499         /* pcm operations */
500         .ops = &sun8i_codec_dai_ops,
501 };
502 
503 static const struct snd_soc_component_driver sun8i_soc_component = {
504         .dapm_widgets           = sun8i_codec_dapm_widgets,
505         .num_dapm_widgets       = ARRAY_SIZE(sun8i_codec_dapm_widgets),
506         .dapm_routes            = sun8i_codec_dapm_routes,
507         .num_dapm_routes        = ARRAY_SIZE(sun8i_codec_dapm_routes),
508         .idle_bias_on           = 1,
509         .use_pmdown_time        = 1,
510         .endianness             = 1,
511         .non_legacy_dai_naming  = 1,
512 };
513 
514 static const struct regmap_config sun8i_codec_regmap_config = {
515         .reg_bits       = 32,
516         .reg_stride     = 4,
517         .val_bits       = 32,
518         .max_register   = SUN8I_DAC_MXR_SRC,
519 
520         .cache_type     = REGCACHE_FLAT,
521 };
522 
523 static int sun8i_codec_probe(struct platform_device *pdev)
524 {
525         struct resource *res_base;
526         struct sun8i_codec *scodec;
527         void __iomem *base;
528         int ret;
529 
530         scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
531         if (!scodec)
532                 return -ENOMEM;
533 
534         scodec->dev = &pdev->dev;
535 
536         scodec->clk_module = devm_clk_get(&pdev->dev, "mod");
537         if (IS_ERR(scodec->clk_module)) {
538                 dev_err(&pdev->dev, "Failed to get the module clock\n");
539                 return PTR_ERR(scodec->clk_module);
540         }
541 
542         scodec->clk_bus = devm_clk_get(&pdev->dev, "bus");
543         if (IS_ERR(scodec->clk_bus)) {
544                 dev_err(&pdev->dev, "Failed to get the bus clock\n");
545                 return PTR_ERR(scodec->clk_bus);
546         }
547 
548         res_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
549         base = devm_ioremap_resource(&pdev->dev, res_base);
550         if (IS_ERR(base)) {
551                 dev_err(&pdev->dev, "Failed to map the registers\n");
552                 return PTR_ERR(base);
553         }
554 
555         scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
556                                                &sun8i_codec_regmap_config);
557         if (IS_ERR(scodec->regmap)) {
558                 dev_err(&pdev->dev, "Failed to create our regmap\n");
559                 return PTR_ERR(scodec->regmap);
560         }
561 
562         platform_set_drvdata(pdev, scodec);
563 
564         pm_runtime_enable(&pdev->dev);
565         if (!pm_runtime_enabled(&pdev->dev)) {
566                 ret = sun8i_codec_runtime_resume(&pdev->dev);
567                 if (ret)
568                         goto err_pm_disable;
569         }
570 
571         ret = devm_snd_soc_register_component(&pdev->dev, &sun8i_soc_component,
572                                      &sun8i_codec_dai, 1);
573         if (ret) {
574                 dev_err(&pdev->dev, "Failed to register codec\n");
575                 goto err_suspend;
576         }
577 
578         return ret;
579 
580 err_suspend:
581         if (!pm_runtime_status_suspended(&pdev->dev))
582                 sun8i_codec_runtime_suspend(&pdev->dev);
583 
584 err_pm_disable:
585         pm_runtime_disable(&pdev->dev);
586 
587         return ret;
588 }
589 
590 static int sun8i_codec_remove(struct platform_device *pdev)
591 {
592         struct snd_soc_card *card = platform_get_drvdata(pdev);
593         struct sun8i_codec *scodec = snd_soc_card_get_drvdata(card);
594 
595         pm_runtime_disable(&pdev->dev);
596         if (!pm_runtime_status_suspended(&pdev->dev))
597                 sun8i_codec_runtime_suspend(&pdev->dev);
598 
599         clk_disable_unprepare(scodec->clk_module);
600         clk_disable_unprepare(scodec->clk_bus);
601 
602         return 0;
603 }
604 
605 static const struct of_device_id sun8i_codec_of_match[] = {
606         { .compatible = "allwinner,sun8i-a33-codec" },
607         {}
608 };
609 MODULE_DEVICE_TABLE(of, sun8i_codec_of_match);
610 
611 static const struct dev_pm_ops sun8i_codec_pm_ops = {
612         SET_RUNTIME_PM_OPS(sun8i_codec_runtime_suspend,
613                            sun8i_codec_runtime_resume, NULL)
614 };
615 
616 static struct platform_driver sun8i_codec_driver = {
617         .driver = {
618                 .name = "sun8i-codec",
619                 .of_match_table = sun8i_codec_of_match,
620                 .pm = &sun8i_codec_pm_ops,
621         },
622         .probe = sun8i_codec_probe,
623         .remove = sun8i_codec_remove,
624 };
625 module_platform_driver(sun8i_codec_driver);
626 
627 MODULE_DESCRIPTION("Allwinner A33 (sun8i) codec driver");
628 MODULE_AUTHOR("Mylène Josserand <mylene.josserand@free-electrons.com>");
629 MODULE_LICENSE("GPL");
630 MODULE_ALIAS("platform:sun8i-codec");
631 

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