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

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

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ 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.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 /*
  3  * ak4118.c  --  Asahi Kasei ALSA Soc Audio driver
  4  *
  5  * Copyright 2018 DEVIALET
  6  */
  7 
  8 #include <linux/i2c.h>
  9 #include <linux/gpio/consumer.h>
 10 #include <linux/module.h>
 11 #include <linux/of.h>
 12 #include <linux/regmap.h>
 13 #include <linux/slab.h>
 14 
 15 #include <sound/asoundef.h>
 16 #include <sound/core.h>
 17 #include <sound/initval.h>
 18 #include <sound/soc.h>
 19 
 20 #define AK4118_REG_CLK_PWR_CTL          0x00
 21 #define AK4118_REG_FORMAT_CTL           0x01
 22 #define AK4118_REG_IO_CTL0              0x02
 23 #define AK4118_REG_IO_CTL1              0x03
 24 #define AK4118_REG_INT0_MASK            0x04
 25 #define AK4118_REG_INT1_MASK            0x05
 26 #define AK4118_REG_RCV_STATUS0          0x06
 27 #define AK4118_REG_RCV_STATUS1          0x07
 28 #define AK4118_REG_RXCHAN_STATUS0       0x08
 29 #define AK4118_REG_RXCHAN_STATUS1       0x09
 30 #define AK4118_REG_RXCHAN_STATUS2       0x0a
 31 #define AK4118_REG_RXCHAN_STATUS3       0x0b
 32 #define AK4118_REG_RXCHAN_STATUS4       0x0c
 33 #define AK4118_REG_TXCHAN_STATUS0       0x0d
 34 #define AK4118_REG_TXCHAN_STATUS1       0x0e
 35 #define AK4118_REG_TXCHAN_STATUS2       0x0f
 36 #define AK4118_REG_TXCHAN_STATUS3       0x10
 37 #define AK4118_REG_TXCHAN_STATUS4       0x11
 38 #define AK4118_REG_BURST_PREAMB_PC0     0x12
 39 #define AK4118_REG_BURST_PREAMB_PC1     0x13
 40 #define AK4118_REG_BURST_PREAMB_PD0     0x14
 41 #define AK4118_REG_BURST_PREAMB_PD1     0x15
 42 #define AK4118_REG_QSUB_CTL             0x16
 43 #define AK4118_REG_QSUB_TRACK           0x17
 44 #define AK4118_REG_QSUB_INDEX           0x18
 45 #define AK4118_REG_QSUB_MIN             0x19
 46 #define AK4118_REG_QSUB_SEC             0x1a
 47 #define AK4118_REG_QSUB_FRAME           0x1b
 48 #define AK4118_REG_QSUB_ZERO            0x1c
 49 #define AK4118_REG_QSUB_ABS_MIN         0x1d
 50 #define AK4118_REG_QSUB_ABS_SEC         0x1e
 51 #define AK4118_REG_QSUB_ABS_FRAME       0x1f
 52 #define AK4118_REG_GPE                  0x20
 53 #define AK4118_REG_GPDR                 0x21
 54 #define AK4118_REG_GPSCR                0x22
 55 #define AK4118_REG_GPLR                 0x23
 56 #define AK4118_REG_DAT_MASK_DTS         0x24
 57 #define AK4118_REG_RX_DETECT            0x25
 58 #define AK4118_REG_STC_DAT_DETECT       0x26
 59 #define AK4118_REG_RXCHAN_STATUS5       0x27
 60 #define AK4118_REG_TXCHAN_STATUS5       0x28
 61 #define AK4118_REG_MAX                  0x29
 62 
 63 #define AK4118_REG_FORMAT_CTL_DIF0      (1 << 4)
 64 #define AK4118_REG_FORMAT_CTL_DIF1      (1 << 5)
 65 #define AK4118_REG_FORMAT_CTL_DIF2      (1 << 6)
 66 
 67 struct ak4118_priv {
 68         struct regmap *regmap;
 69         struct gpio_desc *reset;
 70         struct gpio_desc *irq;
 71         struct snd_soc_component *component;
 72 };
 73 
 74 static const struct reg_default ak4118_reg_defaults[] = {
 75         {AK4118_REG_CLK_PWR_CTL,        0x43},
 76         {AK4118_REG_FORMAT_CTL,         0x6a},
 77         {AK4118_REG_IO_CTL0,            0x88},
 78         {AK4118_REG_IO_CTL1,            0x48},
 79         {AK4118_REG_INT0_MASK,          0xee},
 80         {AK4118_REG_INT1_MASK,          0xb5},
 81         {AK4118_REG_RCV_STATUS0,        0x00},
 82         {AK4118_REG_RCV_STATUS1,        0x10},
 83         {AK4118_REG_TXCHAN_STATUS0,     0x00},
 84         {AK4118_REG_TXCHAN_STATUS1,     0x00},
 85         {AK4118_REG_TXCHAN_STATUS2,     0x00},
 86         {AK4118_REG_TXCHAN_STATUS3,     0x00},
 87         {AK4118_REG_TXCHAN_STATUS4,     0x00},
 88         {AK4118_REG_GPE,                0x77},
 89         {AK4118_REG_GPDR,               0x00},
 90         {AK4118_REG_GPSCR,              0x00},
 91         {AK4118_REG_GPLR,               0x00},
 92         {AK4118_REG_DAT_MASK_DTS,       0x3f},
 93         {AK4118_REG_RX_DETECT,          0x00},
 94         {AK4118_REG_STC_DAT_DETECT,     0x00},
 95         {AK4118_REG_TXCHAN_STATUS5,     0x00},
 96 };
 97 
 98 static const char * const ak4118_input_select_txt[] = {
 99         "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7",
100 };
101 static SOC_ENUM_SINGLE_DECL(ak4118_insel_enum, AK4118_REG_IO_CTL1, 0x0,
102                             ak4118_input_select_txt);
103 
104 static const struct snd_kcontrol_new ak4118_input_mux_controls =
105         SOC_DAPM_ENUM("Input Select", ak4118_insel_enum);
106 
107 static const char * const ak4118_iec958_fs_txt[] = {
108         "44100", "48000", "32000", "22050", "11025", "24000", "16000", "88200",
109         "8000", "96000", "64000", "176400", "192000",
110 };
111 
112 static const int ak4118_iec958_fs_val[] = {
113         0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xE,
114 };
115 
116 static SOC_VALUE_ENUM_SINGLE_DECL(ak4118_iec958_fs_enum, AK4118_REG_RCV_STATUS1,
117                                   0x4, 0x4, ak4118_iec958_fs_txt,
118                                   ak4118_iec958_fs_val);
119 
120 static struct snd_kcontrol_new ak4118_iec958_controls[] = {
121         SOC_SINGLE("IEC958 Parity Errors", AK4118_REG_RCV_STATUS0, 0, 1, 0),
122         SOC_SINGLE("IEC958 No Audio", AK4118_REG_RCV_STATUS0, 1, 1, 0),
123         SOC_SINGLE("IEC958 PLL Lock", AK4118_REG_RCV_STATUS0, 4, 1, 1),
124         SOC_SINGLE("IEC958 Non PCM", AK4118_REG_RCV_STATUS0, 6, 1, 0),
125         SOC_ENUM("IEC958 Sampling Freq", ak4118_iec958_fs_enum),
126 };
127 
128 static const struct snd_soc_dapm_widget ak4118_dapm_widgets[] = {
129         SND_SOC_DAPM_INPUT("INRX0"),
130         SND_SOC_DAPM_INPUT("INRX1"),
131         SND_SOC_DAPM_INPUT("INRX2"),
132         SND_SOC_DAPM_INPUT("INRX3"),
133         SND_SOC_DAPM_INPUT("INRX4"),
134         SND_SOC_DAPM_INPUT("INRX5"),
135         SND_SOC_DAPM_INPUT("INRX6"),
136         SND_SOC_DAPM_INPUT("INRX7"),
137         SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
138                          &ak4118_input_mux_controls),
139 };
140 
141 static const struct snd_soc_dapm_route ak4118_dapm_routes[] = {
142         {"Input Mux", "RX0", "INRX0"},
143         {"Input Mux", "RX1", "INRX1"},
144         {"Input Mux", "RX2", "INRX2"},
145         {"Input Mux", "RX3", "INRX3"},
146         {"Input Mux", "RX4", "INRX4"},
147         {"Input Mux", "RX5", "INRX5"},
148         {"Input Mux", "RX6", "INRX6"},
149         {"Input Mux", "RX7", "INRX7"},
150 };
151 
152 
153 static int ak4118_set_dai_fmt_provider(struct ak4118_priv *ak4118,
154                                        unsigned int format)
155 {
156         int dif;
157 
158         switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
159         case SND_SOC_DAIFMT_I2S:
160                 dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF2;
161                 break;
162         case SND_SOC_DAIFMT_RIGHT_J:
163                 dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF1;
164                 break;
165         case SND_SOC_DAIFMT_LEFT_J:
166                 dif = AK4118_REG_FORMAT_CTL_DIF2;
167                 break;
168         default:
169                 return -ENOTSUPP;
170         }
171 
172         return dif;
173 }
174 
175 static int ak4118_set_dai_fmt_consumer(struct ak4118_priv *ak4118,
176                                        unsigned int format)
177 {
178         int dif;
179 
180         switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
181         case SND_SOC_DAIFMT_I2S:
182                 dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF1 |
183                       AK4118_REG_FORMAT_CTL_DIF2;
184                 break;
185         case SND_SOC_DAIFMT_LEFT_J:
186                 dif = AK4118_REG_FORMAT_CTL_DIF1 | AK4118_REG_FORMAT_CTL_DIF2;
187                 break;
188         default:
189                 return -ENOTSUPP;
190         }
191 
192         return dif;
193 }
194 
195 static int ak4118_set_dai_fmt(struct snd_soc_dai *dai,
196                               unsigned int format)
197 {
198         struct snd_soc_component *component = dai->component;
199         struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component);
200         int dif;
201         int ret = 0;
202 
203         switch (format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
204         case SND_SOC_DAIFMT_CBP_CFP:
205                 dif = ak4118_set_dai_fmt_provider(ak4118, format);
206                 break;
207         case SND_SOC_DAIFMT_CBC_CFC:
208                 dif = ak4118_set_dai_fmt_consumer(ak4118, format);
209                 break;
210         default:
211                 ret = -ENOTSUPP;
212                 goto exit;
213         }
214 
215         /* format not supported */
216         if (dif < 0) {
217                 ret = dif;
218                 goto exit;
219         }
220 
221         ret = regmap_update_bits(ak4118->regmap, AK4118_REG_FORMAT_CTL,
222                                  AK4118_REG_FORMAT_CTL_DIF0 |
223                                  AK4118_REG_FORMAT_CTL_DIF1 |
224                                  AK4118_REG_FORMAT_CTL_DIF2, dif);
225         if (ret < 0)
226                 goto exit;
227 
228 exit:
229         return ret;
230 }
231 
232 static int ak4118_hw_params(struct snd_pcm_substream *substream,
233                             struct snd_pcm_hw_params *params,
234                             struct snd_soc_dai *dai)
235 {
236         return 0;
237 }
238 
239 static const struct snd_soc_dai_ops ak4118_dai_ops = {
240         .hw_params = ak4118_hw_params,
241         .set_fmt   = ak4118_set_dai_fmt,
242 };
243 
244 static struct snd_soc_dai_driver ak4118_dai = {
245         .name = "ak4118-hifi",
246         .capture = {
247                 .stream_name = "Capture",
248                 .channels_min = 2,
249                 .channels_max = 2,
250                 .rates = SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
251                          SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
252                          SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
253                          SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
254                 .formats = SNDRV_PCM_FMTBIT_S16_LE  |
255                            SNDRV_PCM_FMTBIT_S24_3LE |
256                            SNDRV_PCM_FMTBIT_S24_LE
257         },
258         .ops = &ak4118_dai_ops,
259 };
260 
261 static irqreturn_t ak4118_irq_handler(int irq, void *data)
262 {
263         struct ak4118_priv *ak4118 = data;
264         struct snd_soc_component *component = ak4118->component;
265         struct snd_kcontrol_new *kctl_new;
266         unsigned int i;
267 
268         if (!component)
269                 return IRQ_NONE;
270 
271         for (i = 0; i < ARRAY_SIZE(ak4118_iec958_controls); i++) {
272                 kctl_new = &ak4118_iec958_controls[i];
273 
274                 snd_soc_component_notify_control(component, kctl_new->name);
275         }
276 
277         return IRQ_HANDLED;
278 }
279 
280 static int ak4118_probe(struct snd_soc_component *component)
281 {
282         struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component);
283         int ret = 0;
284 
285         ak4118->component = component;
286 
287         /* release reset */
288         gpiod_set_value(ak4118->reset, 0);
289 
290         /* unmask all int1 sources */
291         ret = regmap_write(ak4118->regmap, AK4118_REG_INT1_MASK, 0x00);
292         if (ret < 0) {
293                 dev_err(component->dev,
294                         "failed to write regmap 0x%x 0x%x: %d\n",
295                         AK4118_REG_INT1_MASK, 0x00, ret);
296                 return ret;
297         }
298 
299         /* rx detect enable on all channels */
300         ret = regmap_write(ak4118->regmap, AK4118_REG_RX_DETECT, 0xff);
301         if (ret < 0) {
302                 dev_err(component->dev,
303                         "failed to write regmap 0x%x 0x%x: %d\n",
304                         AK4118_REG_RX_DETECT, 0xff, ret);
305                 return ret;
306         }
307 
308         ret = snd_soc_add_component_controls(component, ak4118_iec958_controls,
309                                          ARRAY_SIZE(ak4118_iec958_controls));
310         if (ret) {
311                 dev_err(component->dev,
312                         "failed to add component kcontrols: %d\n", ret);
313                 return ret;
314         }
315 
316         return 0;
317 }
318 
319 static void ak4118_remove(struct snd_soc_component *component)
320 {
321         struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component);
322 
323         /* hold reset */
324         gpiod_set_value(ak4118->reset, 1);
325 }
326 
327 static const struct snd_soc_component_driver soc_component_drv_ak4118 = {
328         .probe                  = ak4118_probe,
329         .remove                 = ak4118_remove,
330         .dapm_widgets           = ak4118_dapm_widgets,
331         .num_dapm_widgets       = ARRAY_SIZE(ak4118_dapm_widgets),
332         .dapm_routes            = ak4118_dapm_routes,
333         .num_dapm_routes        = ARRAY_SIZE(ak4118_dapm_routes),
334         .idle_bias_on           = 1,
335         .use_pmdown_time        = 1,
336         .endianness             = 1,
337 };
338 
339 static const struct regmap_config ak4118_regmap = {
340         .reg_bits = 8,
341         .val_bits = 8,
342 
343         .reg_defaults = ak4118_reg_defaults,
344         .num_reg_defaults = ARRAY_SIZE(ak4118_reg_defaults),
345 
346         .cache_type = REGCACHE_NONE,
347         .max_register = AK4118_REG_MAX - 1,
348 };
349 
350 static int ak4118_i2c_probe(struct i2c_client *i2c)
351 {
352         struct ak4118_priv *ak4118;
353         int ret;
354 
355         ak4118 = devm_kzalloc(&i2c->dev, sizeof(struct ak4118_priv),
356                               GFP_KERNEL);
357         if (ak4118 == NULL)
358                 return -ENOMEM;
359 
360         ak4118->regmap = devm_regmap_init_i2c(i2c, &ak4118_regmap);
361         if (IS_ERR(ak4118->regmap))
362                 return PTR_ERR(ak4118->regmap);
363 
364         i2c_set_clientdata(i2c, ak4118);
365 
366         ak4118->reset = devm_gpiod_get(&i2c->dev, "reset", GPIOD_OUT_HIGH);
367         if (IS_ERR(ak4118->reset))
368                 return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->reset),
369                                      "Failed to get reset\n");
370 
371         ak4118->irq = devm_gpiod_get(&i2c->dev, "irq", GPIOD_IN);
372         if (IS_ERR(ak4118->irq))
373                 return dev_err_probe(&i2c->dev, PTR_ERR(ak4118->irq),
374                                      "Failed to get IRQ\n");
375 
376         ret = devm_request_threaded_irq(&i2c->dev, gpiod_to_irq(ak4118->irq),
377                                         NULL, ak4118_irq_handler,
378                                         IRQF_TRIGGER_RISING | IRQF_ONESHOT,
379                                         "ak4118-irq", ak4118);
380         if (ret < 0) {
381                 dev_err(&i2c->dev, "Fail to request_irq: %d\n", ret);
382                 return ret;
383         }
384 
385         return devm_snd_soc_register_component(&i2c->dev,
386                                 &soc_component_drv_ak4118, &ak4118_dai, 1);
387 }
388 
389 #ifdef CONFIG_OF
390 static const struct of_device_id ak4118_of_match[] = {
391         { .compatible = "asahi-kasei,ak4118", },
392         {}
393 };
394 MODULE_DEVICE_TABLE(of, ak4118_of_match);
395 #endif
396 
397 static const struct i2c_device_id ak4118_id_table[] = {
398         { "ak4118" },
399         {}
400 };
401 MODULE_DEVICE_TABLE(i2c, ak4118_id_table);
402 
403 static struct i2c_driver ak4118_i2c_driver = {
404         .driver  = {
405                 .name = "ak4118",
406                 .of_match_table = of_match_ptr(ak4118_of_match),
407         },
408         .id_table = ak4118_id_table,
409         .probe = ak4118_i2c_probe,
410 };
411 
412 module_i2c_driver(ak4118_i2c_driver);
413 
414 MODULE_DESCRIPTION("Asahi Kasei AK4118 ALSA SoC driver");
415 MODULE_AUTHOR("Adrien Charruel <adrien.charruel@devialet.com>");
416 MODULE_LICENSE("GPL");
417 

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

kernel.org | git.kernel.org | LWN.net | Project Home | 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.

sflogo.php