Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
  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
 67struct ak4118_priv {
 68	struct regmap *regmap;
 69	struct gpio_desc *reset;
 70	struct gpio_desc *irq;
 71	struct snd_soc_component *component;
 72};
 73
 74static 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
 98static const char * const ak4118_input_select_txt[] = {
 99	"RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7",
100};
101static SOC_ENUM_SINGLE_DECL(ak4118_insel_enum, AK4118_REG_IO_CTL1, 0x0,
102			    ak4118_input_select_txt);
103
104static const struct snd_kcontrol_new ak4118_input_mux_controls =
105	SOC_DAPM_ENUM("Input Select", ak4118_insel_enum);
106
107static const char * const ak4118_iec958_fs_txt[] = {
108	"44100", "48000", "32000", "22050", "11025", "24000", "16000", "88200",
109	"8000", "96000", "64000", "176400", "192000",
110};
111
112static const int ak4118_iec958_fs_val[] = {
113	0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xE,
114};
115
116static 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
120static 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
128static 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
141static 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
153static 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
175static 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
195static 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
228exit:
229	return ret;
230}
231
232static 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
239static const struct snd_soc_dai_ops ak4118_dai_ops = {
240	.hw_params = ak4118_hw_params,
241	.set_fmt   = ak4118_set_dai_fmt,
242};
243
244static 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
261static 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
280static 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
319static 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
327static 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
339static 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
350static 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
390static const struct of_device_id ak4118_of_match[] = {
391	{ .compatible = "asahi-kasei,ak4118", },
392	{}
393};
394MODULE_DEVICE_TABLE(of, ak4118_of_match);
395#endif
396
397static const struct i2c_device_id ak4118_id_table[] = {
398	{ "ak4118" },
399	{}
400};
401MODULE_DEVICE_TABLE(i2c, ak4118_id_table);
402
403static 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
412module_i2c_driver(ak4118_i2c_driver);
413
414MODULE_DESCRIPTION("Asahi Kasei AK4118 ALSA SoC driver");
415MODULE_AUTHOR("Adrien Charruel <adrien.charruel@devialet.com>");
416MODULE_LICENSE("GPL");