diff options
author | Andreas Dannenberg <dannenberg@ti.com> | 2017-12-05 15:52:56 -0500 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2017-12-06 08:06:04 -0500 |
commit | 993a3450712b2a723689b6b6b1a7fe6fe053708e (patch) | |
tree | 4ee3c3a1481503b1d2f2b2396f8c9c15933056c2 | |
parent | 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323 (diff) |
ASoC: pcm186x: Add initial PCM1862/63/64/65 universal ADC driver
This is an initial version of the PCM186x codec driver supporting both
2-channel and 4-channel device variants. Not all device features are
supported yet such as master/slave mode PLL configuration for which the
codec driver currently relies on the PCM186x built-in clock
auto-detection feature or the connection of digital microphones.
However here is what's here and what should work:
- Support for SPI and I2C low-level interfaces
- Regmap support and basic register definitions
- Input Mixer and Mux selection
- I2C, LJ, and TDM DAI format support
Signed-off-by: Andreas Dannenberg <dannenberg@ti.com>
Signed-off-by: Michael Stecklein <m-stecklein@ti.com>
Signed-off-by: Andrew F. Davis <afd@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | sound/soc/codecs/Kconfig | 17 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 6 | ||||
-rw-r--r-- | sound/soc/codecs/pcm186x-i2c.c | 69 | ||||
-rw-r--r-- | sound/soc/codecs/pcm186x-spi.c | 69 | ||||
-rw-r--r-- | sound/soc/codecs/pcm186x.c | 719 | ||||
-rw-r--r-- | sound/soc/codecs/pcm186x.h | 220 |
6 files changed, 1100 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a42ddbc93f3d..dda8c01170b3 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -109,6 +109,8 @@ config SND_SOC_ALL_CODECS | |||
109 | select SND_SOC_PCM1681 if I2C | 109 | select SND_SOC_PCM1681 if I2C |
110 | select SND_SOC_PCM179X_I2C if I2C | 110 | select SND_SOC_PCM179X_I2C if I2C |
111 | select SND_SOC_PCM179X_SPI if SPI_MASTER | 111 | select SND_SOC_PCM179X_SPI if SPI_MASTER |
112 | select SND_SOC_PCM186X_I2C if I2C | ||
113 | select SND_SOC_PCM186X_SPI if SPI_MASTER | ||
112 | select SND_SOC_PCM3008 | 114 | select SND_SOC_PCM3008 |
113 | select SND_SOC_PCM3168A_I2C if I2C | 115 | select SND_SOC_PCM3168A_I2C if I2C |
114 | select SND_SOC_PCM3168A_SPI if SPI_MASTER | 116 | select SND_SOC_PCM3168A_SPI if SPI_MASTER |
@@ -661,6 +663,21 @@ config SND_SOC_PCM179X_SPI | |||
661 | Enable support for Texas Instruments PCM179x CODEC. | 663 | Enable support for Texas Instruments PCM179x CODEC. |
662 | Select this if your PCM179x is connected via an SPI bus. | 664 | Select this if your PCM179x is connected via an SPI bus. |
663 | 665 | ||
666 | config SND_SOC_PCM186X | ||
667 | tristate | ||
668 | |||
669 | config SND_SOC_PCM186X_I2C | ||
670 | tristate "Texas Instruments PCM186x CODECs - I2C" | ||
671 | depends on I2C | ||
672 | select SND_SOC_PCM186X | ||
673 | select REGMAP_I2C | ||
674 | |||
675 | config SND_SOC_PCM186X_SPI | ||
676 | tristate "Texas Instruments PCM186x CODECs - SPI" | ||
677 | depends on SPI_MASTER | ||
678 | select SND_SOC_PCM186X | ||
679 | select REGMAP_SPI | ||
680 | |||
664 | config SND_SOC_PCM3008 | 681 | config SND_SOC_PCM3008 |
665 | tristate | 682 | tristate |
666 | 683 | ||
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 0001069ce2a7..146e48a60098 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -105,6 +105,9 @@ snd-soc-pcm1681-objs := pcm1681.o | |||
105 | snd-soc-pcm179x-codec-objs := pcm179x.o | 105 | snd-soc-pcm179x-codec-objs := pcm179x.o |
106 | snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o | 106 | snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o |
107 | snd-soc-pcm179x-spi-objs := pcm179x-spi.o | 107 | snd-soc-pcm179x-spi-objs := pcm179x-spi.o |
108 | snd-soc-pcm186x-objs := pcm186x.o | ||
109 | snd-soc-pcm186x-i2c-objs := pcm186x-i2c.o | ||
110 | snd-soc-pcm186x-spi-objs := pcm186x-spi.o | ||
108 | snd-soc-pcm3008-objs := pcm3008.o | 111 | snd-soc-pcm3008-objs := pcm3008.o |
109 | snd-soc-pcm3168a-objs := pcm3168a.o | 112 | snd-soc-pcm3168a-objs := pcm3168a.o |
110 | snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o | 113 | snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o |
@@ -345,6 +348,9 @@ obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o | |||
345 | obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o | 348 | obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o |
346 | obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o | 349 | obj-$(CONFIG_SND_SOC_PCM179X_I2C) += snd-soc-pcm179x-i2c.o |
347 | obj-$(CONFIG_SND_SOC_PCM179X_SPI) += snd-soc-pcm179x-spi.o | 350 | obj-$(CONFIG_SND_SOC_PCM179X_SPI) += snd-soc-pcm179x-spi.o |
351 | obj-$(CONFIG_SND_SOC_PCM186X) += snd-soc-pcm186x.o | ||
352 | obj-$(CONFIG_SND_SOC_PCM186X_I2C) += snd-soc-pcm186x-i2c.o | ||
353 | obj-$(CONFIG_SND_SOC_PCM186X_SPI) += snd-soc-pcm186x-spi.o | ||
348 | obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o | 354 | obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o |
349 | obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o | 355 | obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o |
350 | obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o | 356 | obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o |
diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c new file mode 100644 index 000000000000..543621232d60 --- /dev/null +++ b/sound/soc/codecs/pcm186x-i2c.c | |||
@@ -0,0 +1,69 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Texas Instruments PCM186x Universal Audio ADC - I2C | ||
4 | * | ||
5 | * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com | ||
6 | * Andreas Dannenberg <dannenberg@ti.com> | ||
7 | * Andrew F. Davis <afd@ti.com> | ||
8 | */ | ||
9 | |||
10 | #include <linux/init.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/i2c.h> | ||
13 | |||
14 | #include "pcm186x.h" | ||
15 | |||
16 | static const struct of_device_id pcm186x_of_match[] = { | ||
17 | { .compatible = "ti,pcm1862", .data = (void *)PCM1862 }, | ||
18 | { .compatible = "ti,pcm1863", .data = (void *)PCM1863 }, | ||
19 | { .compatible = "ti,pcm1864", .data = (void *)PCM1864 }, | ||
20 | { .compatible = "ti,pcm1865", .data = (void *)PCM1865 }, | ||
21 | { } | ||
22 | }; | ||
23 | MODULE_DEVICE_TABLE(of, pcm186x_of_match); | ||
24 | |||
25 | static int pcm186x_i2c_probe(struct i2c_client *i2c, | ||
26 | const struct i2c_device_id *id) | ||
27 | { | ||
28 | const enum pcm186x_type type = (enum pcm186x_type)id->driver_data; | ||
29 | int irq = i2c->irq; | ||
30 | struct regmap *regmap; | ||
31 | |||
32 | regmap = devm_regmap_init_i2c(i2c, &pcm186x_regmap); | ||
33 | if (IS_ERR(regmap)) | ||
34 | return PTR_ERR(regmap); | ||
35 | |||
36 | return pcm186x_probe(&i2c->dev, type, irq, regmap); | ||
37 | } | ||
38 | |||
39 | static int pcm186x_i2c_remove(struct i2c_client *i2c) | ||
40 | { | ||
41 | pcm186x_remove(&i2c->dev); | ||
42 | |||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | static const struct i2c_device_id pcm186x_i2c_id[] = { | ||
47 | { "pcm1862", PCM1862 }, | ||
48 | { "pcm1863", PCM1863 }, | ||
49 | { "pcm1864", PCM1864 }, | ||
50 | { "pcm1865", PCM1865 }, | ||
51 | { } | ||
52 | }; | ||
53 | MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id); | ||
54 | |||
55 | static struct i2c_driver pcm186x_i2c_driver = { | ||
56 | .probe = pcm186x_i2c_probe, | ||
57 | .remove = pcm186x_i2c_remove, | ||
58 | .id_table = pcm186x_i2c_id, | ||
59 | .driver = { | ||
60 | .name = "pcm186x", | ||
61 | .of_match_table = pcm186x_of_match, | ||
62 | }, | ||
63 | }; | ||
64 | module_i2c_driver(pcm186x_i2c_driver); | ||
65 | |||
66 | MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>"); | ||
67 | MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); | ||
68 | MODULE_DESCRIPTION("PCM186x Universal Audio ADC I2C Interface Driver"); | ||
69 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/codecs/pcm186x-spi.c b/sound/soc/codecs/pcm186x-spi.c new file mode 100644 index 000000000000..2366f8e4d4d4 --- /dev/null +++ b/sound/soc/codecs/pcm186x-spi.c | |||
@@ -0,0 +1,69 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Texas Instruments PCM186x Universal Audio ADC - SPI | ||
4 | * | ||
5 | * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com | ||
6 | * Andreas Dannenberg <dannenberg@ti.com> | ||
7 | * Andrew F. Davis <afd@ti.com> | ||
8 | */ | ||
9 | |||
10 | #include <linux/init.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/spi/spi.h> | ||
13 | |||
14 | #include "pcm186x.h" | ||
15 | |||
16 | static const struct of_device_id pcm186x_of_match[] = { | ||
17 | { .compatible = "ti,pcm1862", .data = (void *)PCM1862 }, | ||
18 | { .compatible = "ti,pcm1863", .data = (void *)PCM1863 }, | ||
19 | { .compatible = "ti,pcm1864", .data = (void *)PCM1864 }, | ||
20 | { .compatible = "ti,pcm1865", .data = (void *)PCM1865 }, | ||
21 | { } | ||
22 | }; | ||
23 | MODULE_DEVICE_TABLE(of, pcm186x_of_match); | ||
24 | |||
25 | static int pcm186x_spi_probe(struct spi_device *spi) | ||
26 | { | ||
27 | const enum pcm186x_type type = | ||
28 | (enum pcm186x_type)spi_get_device_id(spi)->driver_data; | ||
29 | int irq = spi->irq; | ||
30 | struct regmap *regmap; | ||
31 | |||
32 | regmap = devm_regmap_init_spi(spi, &pcm186x_regmap); | ||
33 | if (IS_ERR(regmap)) | ||
34 | return PTR_ERR(regmap); | ||
35 | |||
36 | return pcm186x_probe(&spi->dev, type, irq, regmap); | ||
37 | } | ||
38 | |||
39 | static int pcm186x_spi_remove(struct spi_device *spi) | ||
40 | { | ||
41 | pcm186x_remove(&spi->dev); | ||
42 | |||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | static const struct spi_device_id pcm186x_spi_id[] = { | ||
47 | { "pcm1862", PCM1862 }, | ||
48 | { "pcm1863", PCM1863 }, | ||
49 | { "pcm1864", PCM1864 }, | ||
50 | { "pcm1865", PCM1865 }, | ||
51 | { } | ||
52 | }; | ||
53 | MODULE_DEVICE_TABLE(spi, pcm186x_spi_id); | ||
54 | |||
55 | static struct spi_driver pcm186x_spi_driver = { | ||
56 | .probe = pcm186x_spi_probe, | ||
57 | .remove = pcm186x_spi_remove, | ||
58 | .id_table = pcm186x_spi_id, | ||
59 | .driver = { | ||
60 | .name = "pcm186x", | ||
61 | .of_match_table = pcm186x_of_match, | ||
62 | }, | ||
63 | }; | ||
64 | module_spi_driver(pcm186x_spi_driver); | ||
65 | |||
66 | MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>"); | ||
67 | MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); | ||
68 | MODULE_DESCRIPTION("PCM186x Universal Audio ADC SPI Interface Driver"); | ||
69 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c new file mode 100644 index 000000000000..f7aa56e20169 --- /dev/null +++ b/sound/soc/codecs/pcm186x.c | |||
@@ -0,0 +1,719 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Texas Instruments PCM186x Universal Audio ADC | ||
4 | * | ||
5 | * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com | ||
6 | * Andreas Dannenberg <dannenberg@ti.com> | ||
7 | * Andrew F. Davis <afd@ti.com> | ||
8 | */ | ||
9 | |||
10 | #include <linux/module.h> | ||
11 | #include <linux/moduleparam.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/pm.h> | ||
15 | #include <linux/pm_runtime.h> | ||
16 | #include <linux/regulator/consumer.h> | ||
17 | #include <linux/regmap.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <sound/core.h> | ||
20 | #include <sound/pcm.h> | ||
21 | #include <sound/pcm_params.h> | ||
22 | #include <sound/soc.h> | ||
23 | #include <sound/jack.h> | ||
24 | #include <sound/initval.h> | ||
25 | #include <sound/tlv.h> | ||
26 | |||
27 | #include "pcm186x.h" | ||
28 | |||
29 | static const char * const pcm186x_supply_names[] = { | ||
30 | "avdd", /* Analog power supply. Connect to 3.3-V supply. */ | ||
31 | "dvdd", /* Digital power supply. Connect to 3.3-V supply. */ | ||
32 | "iovdd", /* I/O power supply. Connect to 3.3-V or 1.8-V. */ | ||
33 | }; | ||
34 | #define PCM186x_NUM_SUPPLIES ARRAY_SIZE(pcm186x_supply_names) | ||
35 | |||
36 | struct pcm186x_priv { | ||
37 | struct regmap *regmap; | ||
38 | struct regulator_bulk_data supplies[PCM186x_NUM_SUPPLIES]; | ||
39 | unsigned int sysclk; | ||
40 | unsigned int tdm_offset; | ||
41 | bool is_tdm_mode; | ||
42 | bool is_master_mode; | ||
43 | }; | ||
44 | |||
45 | static const DECLARE_TLV_DB_SCALE(pcm186x_pga_tlv, -1200, 4000, 50); | ||
46 | |||
47 | static const struct snd_kcontrol_new pcm1863_snd_controls[] = { | ||
48 | SOC_DOUBLE_R_S_TLV("ADC Capture Volume", PCM186X_PGA_VAL_CH1_L, | ||
49 | PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0, | ||
50 | pcm186x_pga_tlv), | ||
51 | }; | ||
52 | |||
53 | static const struct snd_kcontrol_new pcm1865_snd_controls[] = { | ||
54 | SOC_DOUBLE_R_S_TLV("ADC1 Capture Volume", PCM186X_PGA_VAL_CH1_L, | ||
55 | PCM186X_PGA_VAL_CH1_R, 0, -24, 80, 7, 0, | ||
56 | pcm186x_pga_tlv), | ||
57 | SOC_DOUBLE_R_S_TLV("ADC2 Capture Volume", PCM186X_PGA_VAL_CH2_L, | ||
58 | PCM186X_PGA_VAL_CH2_R, 0, -24, 80, 7, 0, | ||
59 | pcm186x_pga_tlv), | ||
60 | }; | ||
61 | |||
62 | const unsigned int pcm186x_adc_input_channel_sel_value[] = { | ||
63 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, | ||
64 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, | ||
65 | 0x10, 0x20, 0x30 | ||
66 | }; | ||
67 | |||
68 | static const char * const pcm186x_adcl_input_channel_sel_text[] = { | ||
69 | "No Select", | ||
70 | "VINL1[SE]", /* Default for ADC1L */ | ||
71 | "VINL2[SE]", /* Default for ADC2L */ | ||
72 | "VINL2[SE] + VINL1[SE]", | ||
73 | "VINL3[SE]", | ||
74 | "VINL3[SE] + VINL1[SE]", | ||
75 | "VINL3[SE] + VINL2[SE]", | ||
76 | "VINL3[SE] + VINL2[SE] + VINL1[SE]", | ||
77 | "VINL4[SE]", | ||
78 | "VINL4[SE] + VINL1[SE]", | ||
79 | "VINL4[SE] + VINL2[SE]", | ||
80 | "VINL4[SE] + VINL2[SE] + VINL1[SE]", | ||
81 | "VINL4[SE] + VINL3[SE]", | ||
82 | "VINL4[SE] + VINL3[SE] + VINL1[SE]", | ||
83 | "VINL4[SE] + VINL3[SE] + VINL2[SE]", | ||
84 | "VINL4[SE] + VINL3[SE] + VINL2[SE] + VINL1[SE]", | ||
85 | "{VIN1P, VIN1M}[DIFF]", | ||
86 | "{VIN4P, VIN4M}[DIFF]", | ||
87 | "{VIN1P, VIN1M}[DIFF] + {VIN4P, VIN4M}[DIFF]" | ||
88 | }; | ||
89 | |||
90 | static const char * const pcm186x_adcr_input_channel_sel_text[] = { | ||
91 | "No Select", | ||
92 | "VINR1[SE]", /* Default for ADC1R */ | ||
93 | "VINR2[SE]", /* Default for ADC2R */ | ||
94 | "VINR2[SE] + VINR1[SE]", | ||
95 | "VINR3[SE]", | ||
96 | "VINR3[SE] + VINR1[SE]", | ||
97 | "VINR3[SE] + VINR2[SE]", | ||
98 | "VINR3[SE] + VINR2[SE] + VINR1[SE]", | ||
99 | "VINR4[SE]", | ||
100 | "VINR4[SE] + VINR1[SE]", | ||
101 | "VINR4[SE] + VINR2[SE]", | ||
102 | "VINR4[SE] + VINR2[SE] + VINR1[SE]", | ||
103 | "VINR4[SE] + VINR3[SE]", | ||
104 | "VINR4[SE] + VINR3[SE] + VINR1[SE]", | ||
105 | "VINR4[SE] + VINR3[SE] + VINR2[SE]", | ||
106 | "VINR4[SE] + VINR3[SE] + VINR2[SE] + VINR1[SE]", | ||
107 | "{VIN2P, VIN2M}[DIFF]", | ||
108 | "{VIN3P, VIN3M}[DIFF]", | ||
109 | "{VIN2P, VIN2M}[DIFF] + {VIN3P, VIN3M}[DIFF]" | ||
110 | }; | ||
111 | |||
112 | static const struct soc_enum pcm186x_adc_input_channel_sel[] = { | ||
113 | SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_L, 0, | ||
114 | PCM186X_ADC_INPUT_SEL_MASK, | ||
115 | ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text), | ||
116 | pcm186x_adcl_input_channel_sel_text, | ||
117 | pcm186x_adc_input_channel_sel_value), | ||
118 | SOC_VALUE_ENUM_SINGLE(PCM186X_ADC1_INPUT_SEL_R, 0, | ||
119 | PCM186X_ADC_INPUT_SEL_MASK, | ||
120 | ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text), | ||
121 | pcm186x_adcr_input_channel_sel_text, | ||
122 | pcm186x_adc_input_channel_sel_value), | ||
123 | SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_L, 0, | ||
124 | PCM186X_ADC_INPUT_SEL_MASK, | ||
125 | ARRAY_SIZE(pcm186x_adcl_input_channel_sel_text), | ||
126 | pcm186x_adcl_input_channel_sel_text, | ||
127 | pcm186x_adc_input_channel_sel_value), | ||
128 | SOC_VALUE_ENUM_SINGLE(PCM186X_ADC2_INPUT_SEL_R, 0, | ||
129 | PCM186X_ADC_INPUT_SEL_MASK, | ||
130 | ARRAY_SIZE(pcm186x_adcr_input_channel_sel_text), | ||
131 | pcm186x_adcr_input_channel_sel_text, | ||
132 | pcm186x_adc_input_channel_sel_value), | ||
133 | }; | ||
134 | |||
135 | static const struct snd_kcontrol_new pcm186x_adc_mux_controls[] = { | ||
136 | SOC_DAPM_ENUM("ADC1 Left Input", pcm186x_adc_input_channel_sel[0]), | ||
137 | SOC_DAPM_ENUM("ADC1 Right Input", pcm186x_adc_input_channel_sel[1]), | ||
138 | SOC_DAPM_ENUM("ADC2 Left Input", pcm186x_adc_input_channel_sel[2]), | ||
139 | SOC_DAPM_ENUM("ADC2 Right Input", pcm186x_adc_input_channel_sel[3]), | ||
140 | }; | ||
141 | |||
142 | static const struct snd_soc_dapm_widget pcm1863_dapm_widgets[] = { | ||
143 | SND_SOC_DAPM_INPUT("VINL1"), | ||
144 | SND_SOC_DAPM_INPUT("VINR1"), | ||
145 | SND_SOC_DAPM_INPUT("VINL2"), | ||
146 | SND_SOC_DAPM_INPUT("VINR2"), | ||
147 | SND_SOC_DAPM_INPUT("VINL3"), | ||
148 | SND_SOC_DAPM_INPUT("VINR3"), | ||
149 | SND_SOC_DAPM_INPUT("VINL4"), | ||
150 | SND_SOC_DAPM_INPUT("VINR4"), | ||
151 | |||
152 | SND_SOC_DAPM_MUX("ADC Left Capture Source", SND_SOC_NOPM, 0, 0, | ||
153 | &pcm186x_adc_mux_controls[0]), | ||
154 | SND_SOC_DAPM_MUX("ADC Right Capture Source", SND_SOC_NOPM, 0, 0, | ||
155 | &pcm186x_adc_mux_controls[1]), | ||
156 | |||
157 | /* | ||
158 | * Put the codec into SLEEP mode when not in use, allowing the | ||
159 | * Energysense mechanism to operate. | ||
160 | */ | ||
161 | SND_SOC_DAPM_ADC("ADC", "HiFi Capture", PCM186X_POWER_CTRL, 1, 0), | ||
162 | }; | ||
163 | |||
164 | static const struct snd_soc_dapm_widget pcm1865_dapm_widgets[] = { | ||
165 | SND_SOC_DAPM_INPUT("VINL1"), | ||
166 | SND_SOC_DAPM_INPUT("VINR1"), | ||
167 | SND_SOC_DAPM_INPUT("VINL2"), | ||
168 | SND_SOC_DAPM_INPUT("VINR2"), | ||
169 | SND_SOC_DAPM_INPUT("VINL3"), | ||
170 | SND_SOC_DAPM_INPUT("VINR3"), | ||
171 | SND_SOC_DAPM_INPUT("VINL4"), | ||
172 | SND_SOC_DAPM_INPUT("VINR4"), | ||
173 | |||
174 | SND_SOC_DAPM_MUX("ADC1 Left Capture Source", SND_SOC_NOPM, 0, 0, | ||
175 | &pcm186x_adc_mux_controls[0]), | ||
176 | SND_SOC_DAPM_MUX("ADC1 Right Capture Source", SND_SOC_NOPM, 0, 0, | ||
177 | &pcm186x_adc_mux_controls[1]), | ||
178 | SND_SOC_DAPM_MUX("ADC2 Left Capture Source", SND_SOC_NOPM, 0, 0, | ||
179 | &pcm186x_adc_mux_controls[2]), | ||
180 | SND_SOC_DAPM_MUX("ADC2 Right Capture Source", SND_SOC_NOPM, 0, 0, | ||
181 | &pcm186x_adc_mux_controls[3]), | ||
182 | |||
183 | /* | ||
184 | * Put the codec into SLEEP mode when not in use, allowing the | ||
185 | * Energysense mechanism to operate. | ||
186 | */ | ||
187 | SND_SOC_DAPM_ADC("ADC1", "HiFi Capture 1", PCM186X_POWER_CTRL, 1, 0), | ||
188 | SND_SOC_DAPM_ADC("ADC2", "HiFi Capture 2", PCM186X_POWER_CTRL, 1, 0), | ||
189 | }; | ||
190 | |||
191 | static const struct snd_soc_dapm_route pcm1863_dapm_routes[] = { | ||
192 | { "ADC Left Capture Source", NULL, "VINL1" }, | ||
193 | { "ADC Left Capture Source", NULL, "VINR1" }, | ||
194 | { "ADC Left Capture Source", NULL, "VINL2" }, | ||
195 | { "ADC Left Capture Source", NULL, "VINR2" }, | ||
196 | { "ADC Left Capture Source", NULL, "VINL3" }, | ||
197 | { "ADC Left Capture Source", NULL, "VINR3" }, | ||
198 | { "ADC Left Capture Source", NULL, "VINL4" }, | ||
199 | { "ADC Left Capture Source", NULL, "VINR4" }, | ||
200 | |||
201 | { "ADC", NULL, "ADC Left Capture Source" }, | ||
202 | |||
203 | { "ADC Right Capture Source", NULL, "VINL1" }, | ||
204 | { "ADC Right Capture Source", NULL, "VINR1" }, | ||
205 | { "ADC Right Capture Source", NULL, "VINL2" }, | ||
206 | { "ADC Right Capture Source", NULL, "VINR2" }, | ||
207 | { "ADC Right Capture Source", NULL, "VINL3" }, | ||
208 | { "ADC Right Capture Source", NULL, "VINR3" }, | ||
209 | { "ADC Right Capture Source", NULL, "VINL4" }, | ||
210 | { "ADC Right Capture Source", NULL, "VINR4" }, | ||
211 | |||
212 | { "ADC", NULL, "ADC Right Capture Source" }, | ||
213 | }; | ||
214 | |||
215 | static const struct snd_soc_dapm_route pcm1865_dapm_routes[] = { | ||
216 | { "ADC1 Left Capture Source", NULL, "VINL1" }, | ||
217 | { "ADC1 Left Capture Source", NULL, "VINR1" }, | ||
218 | { "ADC1 Left Capture Source", NULL, "VINL2" }, | ||
219 | { "ADC1 Left Capture Source", NULL, "VINR2" }, | ||
220 | { "ADC1 Left Capture Source", NULL, "VINL3" }, | ||
221 | { "ADC1 Left Capture Source", NULL, "VINR3" }, | ||
222 | { "ADC1 Left Capture Source", NULL, "VINL4" }, | ||
223 | { "ADC1 Left Capture Source", NULL, "VINR4" }, | ||
224 | |||
225 | { "ADC1", NULL, "ADC1 Left Capture Source" }, | ||
226 | |||
227 | { "ADC1 Right Capture Source", NULL, "VINL1" }, | ||
228 | { "ADC1 Right Capture Source", NULL, "VINR1" }, | ||
229 | { "ADC1 Right Capture Source", NULL, "VINL2" }, | ||
230 | { "ADC1 Right Capture Source", NULL, "VINR2" }, | ||
231 | { "ADC1 Right Capture Source", NULL, "VINL3" }, | ||
232 | { "ADC1 Right Capture Source", NULL, "VINR3" }, | ||
233 | { "ADC1 Right Capture Source", NULL, "VINL4" }, | ||
234 | { "ADC1 Right Capture Source", NULL, "VINR4" }, | ||
235 | |||
236 | { "ADC1", NULL, "ADC1 Right Capture Source" }, | ||
237 | |||
238 | { "ADC2 Left Capture Source", NULL, "VINL1" }, | ||
239 | { "ADC2 Left Capture Source", NULL, "VINR1" }, | ||
240 | { "ADC2 Left Capture Source", NULL, "VINL2" }, | ||
241 | { "ADC2 Left Capture Source", NULL, "VINR2" }, | ||
242 | { "ADC2 Left Capture Source", NULL, "VINL3" }, | ||
243 | { "ADC2 Left Capture Source", NULL, "VINR3" }, | ||
244 | { "ADC2 Left Capture Source", NULL, "VINL4" }, | ||
245 | { "ADC2 Left Capture Source", NULL, "VINR4" }, | ||
246 | |||
247 | { "ADC2", NULL, "ADC2 Left Capture Source" }, | ||
248 | |||
249 | { "ADC2 Right Capture Source", NULL, "VINL1" }, | ||
250 | { "ADC2 Right Capture Source", NULL, "VINR1" }, | ||
251 | { "ADC2 Right Capture Source", NULL, "VINL2" }, | ||
252 | { "ADC2 Right Capture Source", NULL, "VINR2" }, | ||
253 | { "ADC2 Right Capture Source", NULL, "VINL3" }, | ||
254 | { "ADC2 Right Capture Source", NULL, "VINR3" }, | ||
255 | { "ADC2 Right Capture Source", NULL, "VINL4" }, | ||
256 | { "ADC2 Right Capture Source", NULL, "VINR4" }, | ||
257 | |||
258 | { "ADC2", NULL, "ADC2 Right Capture Source" }, | ||
259 | }; | ||
260 | |||
261 | static int pcm186x_hw_params(struct snd_pcm_substream *substream, | ||
262 | struct snd_pcm_hw_params *params, | ||
263 | struct snd_soc_dai *dai) | ||
264 | { | ||
265 | struct snd_soc_codec *codec = dai->codec; | ||
266 | |||
267 | struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
268 | unsigned int rate = params_rate(params); | ||
269 | unsigned int format = params_format(params); | ||
270 | unsigned int width = params_width(params); | ||
271 | unsigned int channels = params_channels(params); | ||
272 | unsigned int div_lrck; | ||
273 | unsigned int div_bck; | ||
274 | u8 tdm_tx_sel = 0; | ||
275 | u8 pcm_cfg = 0; | ||
276 | |||
277 | dev_dbg(codec->dev, "%s() rate=%u format=0x%x width=%u channels=%u\n", | ||
278 | __func__, rate, format, width, channels); | ||
279 | |||
280 | switch (width) { | ||
281 | case 16: | ||
282 | pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_16 << | ||
283 | PCM186X_PCM_CFG_RX_WLEN_SHIFT | | ||
284 | PCM186X_PCM_CFG_TX_WLEN_16 << | ||
285 | PCM186X_PCM_CFG_TX_WLEN_SHIFT; | ||
286 | break; | ||
287 | case 20: | ||
288 | pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_20 << | ||
289 | PCM186X_PCM_CFG_RX_WLEN_SHIFT | | ||
290 | PCM186X_PCM_CFG_TX_WLEN_20 << | ||
291 | PCM186X_PCM_CFG_TX_WLEN_SHIFT; | ||
292 | break; | ||
293 | case 24: | ||
294 | pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_24 << | ||
295 | PCM186X_PCM_CFG_RX_WLEN_SHIFT | | ||
296 | PCM186X_PCM_CFG_TX_WLEN_24 << | ||
297 | PCM186X_PCM_CFG_TX_WLEN_SHIFT; | ||
298 | break; | ||
299 | case 32: | ||
300 | pcm_cfg = PCM186X_PCM_CFG_RX_WLEN_32 << | ||
301 | PCM186X_PCM_CFG_RX_WLEN_SHIFT | | ||
302 | PCM186X_PCM_CFG_TX_WLEN_32 << | ||
303 | PCM186X_PCM_CFG_TX_WLEN_SHIFT; | ||
304 | break; | ||
305 | default: | ||
306 | return -EINVAL; | ||
307 | } | ||
308 | |||
309 | snd_soc_update_bits(codec, PCM186X_PCM_CFG, | ||
310 | PCM186X_PCM_CFG_RX_WLEN_MASK | | ||
311 | PCM186X_PCM_CFG_TX_WLEN_MASK, | ||
312 | pcm_cfg); | ||
313 | |||
314 | div_lrck = width * channels; | ||
315 | |||
316 | if (priv->is_tdm_mode) { | ||
317 | /* Select TDM transmission data */ | ||
318 | switch (channels) { | ||
319 | case 2: | ||
320 | tdm_tx_sel = PCM186X_TDM_TX_SEL_2CH; | ||
321 | break; | ||
322 | case 4: | ||
323 | tdm_tx_sel = PCM186X_TDM_TX_SEL_4CH; | ||
324 | break; | ||
325 | case 6: | ||
326 | tdm_tx_sel = PCM186X_TDM_TX_SEL_6CH; | ||
327 | break; | ||
328 | default: | ||
329 | return -EINVAL; | ||
330 | } | ||
331 | |||
332 | snd_soc_update_bits(codec, PCM186X_TDM_TX_SEL, | ||
333 | PCM186X_TDM_TX_SEL_MASK, tdm_tx_sel); | ||
334 | |||
335 | /* In DSP/TDM mode, the LRCLK divider must be 256 */ | ||
336 | div_lrck = 256; | ||
337 | |||
338 | /* Configure 1/256 duty cycle for LRCK */ | ||
339 | snd_soc_update_bits(codec, PCM186X_PCM_CFG, | ||
340 | PCM186X_PCM_CFG_TDM_LRCK_MODE, | ||
341 | PCM186X_PCM_CFG_TDM_LRCK_MODE); | ||
342 | } | ||
343 | |||
344 | /* Only configure clock dividers in master mode. */ | ||
345 | if (priv->is_master_mode) { | ||
346 | div_bck = priv->sysclk / (div_lrck * rate); | ||
347 | |||
348 | dev_dbg(codec->dev, | ||
349 | "%s() master_clk=%u div_bck=%u div_lrck=%u\n", | ||
350 | __func__, priv->sysclk, div_bck, div_lrck); | ||
351 | |||
352 | snd_soc_write(codec, PCM186X_BCK_DIV, div_bck - 1); | ||
353 | snd_soc_write(codec, PCM186X_LRK_DIV, div_lrck - 1); | ||
354 | } | ||
355 | |||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format) | ||
360 | { | ||
361 | struct snd_soc_codec *codec = dai->codec; | ||
362 | struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
363 | u8 clk_ctrl = 0; | ||
364 | u8 pcm_cfg = 0; | ||
365 | |||
366 | dev_dbg(codec->dev, "%s() format=0x%x\n", __func__, format); | ||
367 | |||
368 | /* set master/slave audio interface */ | ||
369 | switch (format & SND_SOC_DAIFMT_MASTER_MASK) { | ||
370 | case SND_SOC_DAIFMT_CBM_CFM: | ||
371 | if (!priv->sysclk) { | ||
372 | dev_err(codec->dev, "operating in master mode requires sysclock to be configured\n"); | ||
373 | return -EINVAL; | ||
374 | } | ||
375 | clk_ctrl |= PCM186X_CLK_CTRL_MST_MODE; | ||
376 | priv->is_master_mode = true; | ||
377 | break; | ||
378 | case SND_SOC_DAIFMT_CBS_CFS: | ||
379 | priv->is_master_mode = false; | ||
380 | break; | ||
381 | default: | ||
382 | dev_err(codec->dev, "Invalid DAI master/slave interface\n"); | ||
383 | return -EINVAL; | ||
384 | } | ||
385 | |||
386 | /* set interface polarity */ | ||
387 | switch (format & SND_SOC_DAIFMT_INV_MASK) { | ||
388 | case SND_SOC_DAIFMT_NB_NF: | ||
389 | break; | ||
390 | default: | ||
391 | dev_err(codec->dev, "Inverted DAI clocks not supported\n"); | ||
392 | return -EINVAL; | ||
393 | } | ||
394 | |||
395 | /* set interface format */ | ||
396 | switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
397 | case SND_SOC_DAIFMT_I2S: | ||
398 | pcm_cfg = PCM186X_PCM_CFG_FMT_I2S; | ||
399 | break; | ||
400 | case SND_SOC_DAIFMT_LEFT_J: | ||
401 | pcm_cfg = PCM186X_PCM_CFG_FMT_LEFTJ; | ||
402 | break; | ||
403 | case SND_SOC_DAIFMT_DSP_A: | ||
404 | priv->tdm_offset += 1; | ||
405 | /* Fall through... DSP_A uses the same basic config as DSP_B | ||
406 | * except we need to shift the TDM output by one BCK cycle | ||
407 | */ | ||
408 | case SND_SOC_DAIFMT_DSP_B: | ||
409 | priv->is_tdm_mode = true; | ||
410 | pcm_cfg = PCM186X_PCM_CFG_FMT_TDM; | ||
411 | break; | ||
412 | default: | ||
413 | dev_err(codec->dev, "Invalid DAI format\n"); | ||
414 | return -EINVAL; | ||
415 | } | ||
416 | |||
417 | snd_soc_update_bits(codec, PCM186X_CLK_CTRL, | ||
418 | PCM186X_CLK_CTRL_MST_MODE, clk_ctrl); | ||
419 | |||
420 | snd_soc_write(codec, PCM186X_TDM_TX_OFFSET, priv->tdm_offset); | ||
421 | |||
422 | snd_soc_update_bits(codec, PCM186X_PCM_CFG, | ||
423 | PCM186X_PCM_CFG_FMT_MASK, pcm_cfg); | ||
424 | |||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | static int pcm186x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, | ||
429 | unsigned int rx_mask, int slots, int slot_width) | ||
430 | { | ||
431 | struct snd_soc_codec *codec = dai->codec; | ||
432 | struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
433 | unsigned int first_slot, last_slot, tdm_offset; | ||
434 | |||
435 | dev_dbg(codec->dev, | ||
436 | "%s() tx_mask=0x%x rx_mask=0x%x slots=%d slot_width=%d\n", | ||
437 | __func__, tx_mask, rx_mask, slots, slot_width); | ||
438 | |||
439 | if (!tx_mask) { | ||
440 | dev_err(codec->dev, "tdm tx mask must not be 0\n"); | ||
441 | return -EINVAL; | ||
442 | } | ||
443 | |||
444 | first_slot = __ffs(tx_mask); | ||
445 | last_slot = __fls(tx_mask); | ||
446 | |||
447 | if (last_slot - first_slot != hweight32(tx_mask) - 1) { | ||
448 | dev_err(codec->dev, "tdm tx mask must be contiguous\n"); | ||
449 | return -EINVAL; | ||
450 | } | ||
451 | |||
452 | tdm_offset = first_slot * slot_width; | ||
453 | |||
454 | if (tdm_offset > 255) { | ||
455 | dev_err(codec->dev, "tdm tx slot selection out of bounds\n"); | ||
456 | return -EINVAL; | ||
457 | } | ||
458 | |||
459 | priv->tdm_offset = tdm_offset; | ||
460 | |||
461 | return 0; | ||
462 | } | ||
463 | |||
464 | static int pcm186x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, | ||
465 | unsigned int freq, int dir) | ||
466 | { | ||
467 | struct snd_soc_codec *codec = dai->codec; | ||
468 | struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
469 | |||
470 | dev_dbg(codec->dev, "%s() clk_id=%d freq=%u dir=%d\n", | ||
471 | __func__, clk_id, freq, dir); | ||
472 | |||
473 | priv->sysclk = freq; | ||
474 | |||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | const struct snd_soc_dai_ops pcm186x_dai_ops = { | ||
479 | .set_sysclk = pcm186x_set_dai_sysclk, | ||
480 | .set_tdm_slot = pcm186x_set_tdm_slot, | ||
481 | .set_fmt = pcm186x_set_fmt, | ||
482 | .hw_params = pcm186x_hw_params, | ||
483 | }; | ||
484 | |||
485 | static struct snd_soc_dai_driver pcm1863_dai = { | ||
486 | .name = "pcm1863-aif", | ||
487 | .capture = { | ||
488 | .stream_name = "Capture", | ||
489 | .channels_min = 1, | ||
490 | .channels_max = 2, | ||
491 | .rates = PCM186X_RATES, | ||
492 | .formats = PCM186X_FORMATS, | ||
493 | }, | ||
494 | .ops = &pcm186x_dai_ops, | ||
495 | }; | ||
496 | |||
497 | static struct snd_soc_dai_driver pcm1865_dai = { | ||
498 | .name = "pcm1865-aif", | ||
499 | .capture = { | ||
500 | .stream_name = "Capture", | ||
501 | .channels_min = 1, | ||
502 | .channels_max = 4, | ||
503 | .rates = PCM186X_RATES, | ||
504 | .formats = PCM186X_FORMATS, | ||
505 | }, | ||
506 | .ops = &pcm186x_dai_ops, | ||
507 | }; | ||
508 | |||
509 | static int pcm186x_power_on(struct snd_soc_codec *codec) | ||
510 | { | ||
511 | struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
512 | int ret = 0; | ||
513 | |||
514 | ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), | ||
515 | priv->supplies); | ||
516 | if (ret) | ||
517 | return ret; | ||
518 | |||
519 | regcache_cache_only(priv->regmap, false); | ||
520 | ret = regcache_sync(priv->regmap); | ||
521 | if (ret) { | ||
522 | dev_err(codec->dev, "Failed to restore cache\n"); | ||
523 | regcache_cache_only(priv->regmap, true); | ||
524 | regulator_bulk_disable(ARRAY_SIZE(priv->supplies), | ||
525 | priv->supplies); | ||
526 | return ret; | ||
527 | } | ||
528 | |||
529 | snd_soc_update_bits(codec, PCM186X_POWER_CTRL, | ||
530 | PCM186X_PWR_CTRL_PWRDN, 0); | ||
531 | |||
532 | return 0; | ||
533 | } | ||
534 | |||
535 | static int pcm186x_power_off(struct snd_soc_codec *codec) | ||
536 | { | ||
537 | struct pcm186x_priv *priv = snd_soc_codec_get_drvdata(codec); | ||
538 | int ret; | ||
539 | |||
540 | snd_soc_update_bits(codec, PCM186X_POWER_CTRL, | ||
541 | PCM186X_PWR_CTRL_PWRDN, PCM186X_PWR_CTRL_PWRDN); | ||
542 | |||
543 | regcache_cache_only(priv->regmap, true); | ||
544 | |||
545 | ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies), | ||
546 | priv->supplies); | ||
547 | if (ret) | ||
548 | return ret; | ||
549 | |||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | static int pcm186x_set_bias_level(struct snd_soc_codec *codec, | ||
554 | enum snd_soc_bias_level level) | ||
555 | { | ||
556 | dev_dbg(codec->dev, "## %s: %d -> %d\n", __func__, | ||
557 | snd_soc_codec_get_bias_level(codec), level); | ||
558 | |||
559 | switch (level) { | ||
560 | case SND_SOC_BIAS_ON: | ||
561 | break; | ||
562 | case SND_SOC_BIAS_PREPARE: | ||
563 | break; | ||
564 | case SND_SOC_BIAS_STANDBY: | ||
565 | if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) | ||
566 | pcm186x_power_on(codec); | ||
567 | break; | ||
568 | case SND_SOC_BIAS_OFF: | ||
569 | pcm186x_power_off(codec); | ||
570 | break; | ||
571 | } | ||
572 | |||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | static struct snd_soc_codec_driver soc_codec_dev_pcm1863 = { | ||
577 | .set_bias_level = pcm186x_set_bias_level, | ||
578 | |||
579 | .component_driver = { | ||
580 | .controls = pcm1863_snd_controls, | ||
581 | .num_controls = ARRAY_SIZE(pcm1863_snd_controls), | ||
582 | .dapm_widgets = pcm1863_dapm_widgets, | ||
583 | .num_dapm_widgets = ARRAY_SIZE(pcm1863_dapm_widgets), | ||
584 | .dapm_routes = pcm1863_dapm_routes, | ||
585 | .num_dapm_routes = ARRAY_SIZE(pcm1863_dapm_routes), | ||
586 | }, | ||
587 | }; | ||
588 | |||
589 | static struct snd_soc_codec_driver soc_codec_dev_pcm1865 = { | ||
590 | .set_bias_level = pcm186x_set_bias_level, | ||
591 | .suspend_bias_off = true, | ||
592 | |||
593 | .component_driver = { | ||
594 | .controls = pcm1865_snd_controls, | ||
595 | .num_controls = ARRAY_SIZE(pcm1865_snd_controls), | ||
596 | .dapm_widgets = pcm1865_dapm_widgets, | ||
597 | .num_dapm_widgets = ARRAY_SIZE(pcm1865_dapm_widgets), | ||
598 | .dapm_routes = pcm1865_dapm_routes, | ||
599 | .num_dapm_routes = ARRAY_SIZE(pcm1865_dapm_routes), | ||
600 | }, | ||
601 | }; | ||
602 | |||
603 | static bool pcm186x_volatile(struct device *dev, unsigned int reg) | ||
604 | { | ||
605 | switch (reg) { | ||
606 | case PCM186X_PAGE: | ||
607 | case PCM186X_DEVICE_STATUS: | ||
608 | case PCM186X_FSAMPLE_STATUS: | ||
609 | case PCM186X_DIV_STATUS: | ||
610 | case PCM186X_CLK_STATUS: | ||
611 | case PCM186X_SUPPLY_STATUS: | ||
612 | case PCM186X_MMAP_STAT_CTRL: | ||
613 | case PCM186X_MMAP_ADDRESS: | ||
614 | return true; | ||
615 | } | ||
616 | |||
617 | return false; | ||
618 | } | ||
619 | |||
620 | static const struct regmap_range_cfg pcm186x_range = { | ||
621 | .name = "Pages", | ||
622 | .range_max = PCM186X_MAX_REGISTER, | ||
623 | .selector_reg = PCM186X_PAGE, | ||
624 | .selector_mask = 0xff, | ||
625 | .window_len = PCM186X_PAGE_LEN, | ||
626 | }; | ||
627 | |||
628 | const struct regmap_config pcm186x_regmap = { | ||
629 | .reg_bits = 8, | ||
630 | .val_bits = 8, | ||
631 | |||
632 | .volatile_reg = pcm186x_volatile, | ||
633 | |||
634 | .ranges = &pcm186x_range, | ||
635 | .num_ranges = 1, | ||
636 | |||
637 | .max_register = PCM186X_MAX_REGISTER, | ||
638 | |||
639 | .cache_type = REGCACHE_RBTREE, | ||
640 | }; | ||
641 | EXPORT_SYMBOL_GPL(pcm186x_regmap); | ||
642 | |||
643 | int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq, | ||
644 | struct regmap *regmap) | ||
645 | { | ||
646 | struct pcm186x_priv *priv; | ||
647 | int i, ret; | ||
648 | |||
649 | priv = devm_kzalloc(dev, sizeof(struct pcm186x_priv), GFP_KERNEL); | ||
650 | if (!priv) | ||
651 | return -ENOMEM; | ||
652 | |||
653 | dev_set_drvdata(dev, priv); | ||
654 | priv->regmap = regmap; | ||
655 | |||
656 | for (i = 0; i < ARRAY_SIZE(priv->supplies); i++) | ||
657 | priv->supplies[i].supply = pcm186x_supply_names[i]; | ||
658 | |||
659 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies), | ||
660 | priv->supplies); | ||
661 | if (ret) { | ||
662 | dev_err(dev, "failed to request supplies: %d\n", ret); | ||
663 | return ret; | ||
664 | } | ||
665 | |||
666 | ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), | ||
667 | priv->supplies); | ||
668 | if (ret) { | ||
669 | dev_err(dev, "failed enable supplies: %d\n", ret); | ||
670 | return ret; | ||
671 | } | ||
672 | |||
673 | /* Reset device registers for a consistent power-on like state */ | ||
674 | ret = regmap_write(regmap, PCM186X_PAGE, PCM186X_RESET); | ||
675 | if (ret) { | ||
676 | dev_err(dev, "failed to write device: %d\n", ret); | ||
677 | return ret; | ||
678 | } | ||
679 | |||
680 | ret = regulator_bulk_disable(ARRAY_SIZE(priv->supplies), | ||
681 | priv->supplies); | ||
682 | if (ret) { | ||
683 | dev_err(dev, "failed disable supplies: %d\n", ret); | ||
684 | return ret; | ||
685 | } | ||
686 | |||
687 | switch (type) { | ||
688 | case PCM1865: | ||
689 | case PCM1864: | ||
690 | ret = snd_soc_register_codec(dev, &soc_codec_dev_pcm1865, | ||
691 | &pcm1865_dai, 1); | ||
692 | break; | ||
693 | case PCM1863: | ||
694 | case PCM1862: | ||
695 | default: | ||
696 | ret = snd_soc_register_codec(dev, &soc_codec_dev_pcm1863, | ||
697 | &pcm1863_dai, 1); | ||
698 | } | ||
699 | if (ret) { | ||
700 | dev_err(dev, "failed to register CODEC: %d\n", ret); | ||
701 | return ret; | ||
702 | } | ||
703 | |||
704 | return 0; | ||
705 | } | ||
706 | EXPORT_SYMBOL_GPL(pcm186x_probe); | ||
707 | |||
708 | int pcm186x_remove(struct device *dev) | ||
709 | { | ||
710 | snd_soc_unregister_codec(dev); | ||
711 | |||
712 | return 0; | ||
713 | } | ||
714 | EXPORT_SYMBOL_GPL(pcm186x_remove); | ||
715 | |||
716 | MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>"); | ||
717 | MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); | ||
718 | MODULE_DESCRIPTION("PCM186x Universal Audio ADC driver"); | ||
719 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/codecs/pcm186x.h b/sound/soc/codecs/pcm186x.h new file mode 100644 index 000000000000..b630111bb3c4 --- /dev/null +++ b/sound/soc/codecs/pcm186x.h | |||
@@ -0,0 +1,220 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Texas Instruments PCM186x Universal Audio ADC | ||
4 | * | ||
5 | * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com | ||
6 | * Andreas Dannenberg <dannenberg@ti.com> | ||
7 | * Andrew F. Davis <afd@ti.com> | ||
8 | */ | ||
9 | |||
10 | #ifndef _PCM186X_H_ | ||
11 | #define _PCM186X_H_ | ||
12 | |||
13 | #include <linux/pm.h> | ||
14 | #include <linux/regmap.h> | ||
15 | |||
16 | enum pcm186x_type { | ||
17 | PCM1862, | ||
18 | PCM1863, | ||
19 | PCM1864, | ||
20 | PCM1865, | ||
21 | }; | ||
22 | |||
23 | #define PCM186X_RATES SNDRV_PCM_RATE_8000_192000 | ||
24 | #define PCM186X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ | ||
25 | SNDRV_PCM_FMTBIT_S20_3LE |\ | ||
26 | SNDRV_PCM_FMTBIT_S24_LE | \ | ||
27 | SNDRV_PCM_FMTBIT_S32_LE) | ||
28 | |||
29 | #define PCM186X_PAGE_LEN 0x0100 | ||
30 | #define PCM186X_PAGE_BASE(n) (PCM186X_PAGE_LEN * n) | ||
31 | |||
32 | /* The page selection register address is the same on all pages */ | ||
33 | #define PCM186X_PAGE 0 | ||
34 | |||
35 | /* Register Definitions - Page 0 */ | ||
36 | #define PCM186X_PGA_VAL_CH1_L (PCM186X_PAGE_BASE(0) + 1) | ||
37 | #define PCM186X_PGA_VAL_CH1_R (PCM186X_PAGE_BASE(0) + 2) | ||
38 | #define PCM186X_PGA_VAL_CH2_L (PCM186X_PAGE_BASE(0) + 3) | ||
39 | #define PCM186X_PGA_VAL_CH2_R (PCM186X_PAGE_BASE(0) + 4) | ||
40 | #define PCM186X_PGA_CTRL (PCM186X_PAGE_BASE(0) + 5) | ||
41 | #define PCM186X_ADC1_INPUT_SEL_L (PCM186X_PAGE_BASE(0) + 6) | ||
42 | #define PCM186X_ADC1_INPUT_SEL_R (PCM186X_PAGE_BASE(0) + 7) | ||
43 | #define PCM186X_ADC2_INPUT_SEL_L (PCM186X_PAGE_BASE(0) + 8) | ||
44 | #define PCM186X_ADC2_INPUT_SEL_R (PCM186X_PAGE_BASE(0) + 9) | ||
45 | #define PCM186X_AUXADC_INPUT_SEL (PCM186X_PAGE_BASE(0) + 10) | ||
46 | #define PCM186X_PCM_CFG (PCM186X_PAGE_BASE(0) + 11) | ||
47 | #define PCM186X_TDM_TX_SEL (PCM186X_PAGE_BASE(0) + 12) | ||
48 | #define PCM186X_TDM_TX_OFFSET (PCM186X_PAGE_BASE(0) + 13) | ||
49 | #define PCM186X_TDM_RX_OFFSET (PCM186X_PAGE_BASE(0) + 14) | ||
50 | #define PCM186X_DPGA_VAL_CH1_L (PCM186X_PAGE_BASE(0) + 15) | ||
51 | #define PCM186X_GPIO1_0_CTRL (PCM186X_PAGE_BASE(0) + 16) | ||
52 | #define PCM186X_GPIO3_2_CTRL (PCM186X_PAGE_BASE(0) + 17) | ||
53 | #define PCM186X_GPIO1_0_DIR_CTRL (PCM186X_PAGE_BASE(0) + 18) | ||
54 | #define PCM186X_GPIO3_2_DIR_CTRL (PCM186X_PAGE_BASE(0) + 19) | ||
55 | #define PCM186X_GPIO_IN_OUT (PCM186X_PAGE_BASE(0) + 20) | ||
56 | #define PCM186X_GPIO_PULL_CTRL (PCM186X_PAGE_BASE(0) + 21) | ||
57 | #define PCM186X_DPGA_VAL_CH1_R (PCM186X_PAGE_BASE(0) + 22) | ||
58 | #define PCM186X_DPGA_VAL_CH2_L (PCM186X_PAGE_BASE(0) + 23) | ||
59 | #define PCM186X_DPGA_VAL_CH2_R (PCM186X_PAGE_BASE(0) + 24) | ||
60 | #define PCM186X_DPGA_GAIN_CTRL (PCM186X_PAGE_BASE(0) + 25) | ||
61 | #define PCM186X_DPGA_MIC_CTRL (PCM186X_PAGE_BASE(0) + 26) | ||
62 | #define PCM186X_DIN_RESAMP_CTRL (PCM186X_PAGE_BASE(0) + 27) | ||
63 | #define PCM186X_CLK_CTRL (PCM186X_PAGE_BASE(0) + 32) | ||
64 | #define PCM186X_DSP1_CLK_DIV (PCM186X_PAGE_BASE(0) + 33) | ||
65 | #define PCM186X_DSP2_CLK_DIV (PCM186X_PAGE_BASE(0) + 34) | ||
66 | #define PCM186X_ADC_CLK_DIV (PCM186X_PAGE_BASE(0) + 35) | ||
67 | #define PCM186X_PLL_SCK_DIV (PCM186X_PAGE_BASE(0) + 37) | ||
68 | #define PCM186X_BCK_DIV (PCM186X_PAGE_BASE(0) + 38) | ||
69 | #define PCM186X_LRK_DIV (PCM186X_PAGE_BASE(0) + 39) | ||
70 | #define PCM186X_PLL_CTRL (PCM186X_PAGE_BASE(0) + 40) | ||
71 | #define PCM186X_PLL_P_DIV (PCM186X_PAGE_BASE(0) + 41) | ||
72 | #define PCM186X_PLL_R_DIV (PCM186X_PAGE_BASE(0) + 42) | ||
73 | #define PCM186X_PLL_J_DIV (PCM186X_PAGE_BASE(0) + 43) | ||
74 | #define PCM186X_PLL_D_DIV_LSB (PCM186X_PAGE_BASE(0) + 44) | ||
75 | #define PCM186X_PLL_D_DIV_MSB (PCM186X_PAGE_BASE(0) + 45) | ||
76 | #define PCM186X_SIGDET_MODE (PCM186X_PAGE_BASE(0) + 48) | ||
77 | #define PCM186X_SIGDET_MASK (PCM186X_PAGE_BASE(0) + 49) | ||
78 | #define PCM186X_SIGDET_STAT (PCM186X_PAGE_BASE(0) + 50) | ||
79 | #define PCM186X_SIGDET_LOSS_TIME (PCM186X_PAGE_BASE(0) + 52) | ||
80 | #define PCM186X_SIGDET_SCAN_TIME (PCM186X_PAGE_BASE(0) + 53) | ||
81 | #define PCM186X_SIGDET_INT_INTVL (PCM186X_PAGE_BASE(0) + 54) | ||
82 | #define PCM186X_SIGDET_DC_REF_CH1_L (PCM186X_PAGE_BASE(0) + 64) | ||
83 | #define PCM186X_SIGDET_DC_DIFF_CH1_L (PCM186X_PAGE_BASE(0) + 65) | ||
84 | #define PCM186X_SIGDET_DC_LEV_CH1_L (PCM186X_PAGE_BASE(0) + 66) | ||
85 | #define PCM186X_SIGDET_DC_REF_CH1_R (PCM186X_PAGE_BASE(0) + 67) | ||
86 | #define PCM186X_SIGDET_DC_DIFF_CH1_R (PCM186X_PAGE_BASE(0) + 68) | ||
87 | #define PCM186X_SIGDET_DC_LEV_CH1_R (PCM186X_PAGE_BASE(0) + 69) | ||
88 | #define PCM186X_SIGDET_DC_REF_CH2_L (PCM186X_PAGE_BASE(0) + 70) | ||
89 | #define PCM186X_SIGDET_DC_DIFF_CH2_L (PCM186X_PAGE_BASE(0) + 71) | ||
90 | #define PCM186X_SIGDET_DC_LEV_CH2_L (PCM186X_PAGE_BASE(0) + 72) | ||
91 | #define PCM186X_SIGDET_DC_REF_CH2_R (PCM186X_PAGE_BASE(0) + 73) | ||
92 | #define PCM186X_SIGDET_DC_DIFF_CH2_R (PCM186X_PAGE_BASE(0) + 74) | ||
93 | #define PCM186X_SIGDET_DC_LEV_CH2_R (PCM186X_PAGE_BASE(0) + 75) | ||
94 | #define PCM186X_SIGDET_DC_REF_CH3_L (PCM186X_PAGE_BASE(0) + 76) | ||
95 | #define PCM186X_SIGDET_DC_DIFF_CH3_L (PCM186X_PAGE_BASE(0) + 77) | ||
96 | #define PCM186X_SIGDET_DC_LEV_CH3_L (PCM186X_PAGE_BASE(0) + 78) | ||
97 | #define PCM186X_SIGDET_DC_REF_CH3_R (PCM186X_PAGE_BASE(0) + 79) | ||
98 | #define PCM186X_SIGDET_DC_DIFF_CH3_R (PCM186X_PAGE_BASE(0) + 80) | ||
99 | #define PCM186X_SIGDET_DC_LEV_CH3_R (PCM186X_PAGE_BASE(0) + 81) | ||
100 | #define PCM186X_SIGDET_DC_REF_CH4_L (PCM186X_PAGE_BASE(0) + 82) | ||
101 | #define PCM186X_SIGDET_DC_DIFF_CH4_L (PCM186X_PAGE_BASE(0) + 83) | ||
102 | #define PCM186X_SIGDET_DC_LEV_CH4_L (PCM186X_PAGE_BASE(0) + 84) | ||
103 | #define PCM186X_SIGDET_DC_REF_CH4_R (PCM186X_PAGE_BASE(0) + 85) | ||
104 | #define PCM186X_SIGDET_DC_DIFF_CH4_R (PCM186X_PAGE_BASE(0) + 86) | ||
105 | #define PCM186X_SIGDET_DC_LEV_CH4_R (PCM186X_PAGE_BASE(0) + 87) | ||
106 | #define PCM186X_AUXADC_DATA_CTRL (PCM186X_PAGE_BASE(0) + 88) | ||
107 | #define PCM186X_AUXADC_DATA_LSB (PCM186X_PAGE_BASE(0) + 89) | ||
108 | #define PCM186X_AUXADC_DATA_MSB (PCM186X_PAGE_BASE(0) + 90) | ||
109 | #define PCM186X_INT_ENABLE (PCM186X_PAGE_BASE(0) + 96) | ||
110 | #define PCM186X_INT_FLAG (PCM186X_PAGE_BASE(0) + 97) | ||
111 | #define PCM186X_INT_POL_WIDTH (PCM186X_PAGE_BASE(0) + 98) | ||
112 | #define PCM186X_POWER_CTRL (PCM186X_PAGE_BASE(0) + 112) | ||
113 | #define PCM186X_FILTER_MUTE_CTRL (PCM186X_PAGE_BASE(0) + 113) | ||
114 | #define PCM186X_DEVICE_STATUS (PCM186X_PAGE_BASE(0) + 114) | ||
115 | #define PCM186X_FSAMPLE_STATUS (PCM186X_PAGE_BASE(0) + 115) | ||
116 | #define PCM186X_DIV_STATUS (PCM186X_PAGE_BASE(0) + 116) | ||
117 | #define PCM186X_CLK_STATUS (PCM186X_PAGE_BASE(0) + 117) | ||
118 | #define PCM186X_SUPPLY_STATUS (PCM186X_PAGE_BASE(0) + 120) | ||
119 | |||
120 | /* Register Definitions - Page 1 */ | ||
121 | #define PCM186X_MMAP_STAT_CTRL (PCM186X_PAGE_BASE(1) + 1) | ||
122 | #define PCM186X_MMAP_ADDRESS (PCM186X_PAGE_BASE(1) + 2) | ||
123 | #define PCM186X_MEM_WDATA0 (PCM186X_PAGE_BASE(1) + 4) | ||
124 | #define PCM186X_MEM_WDATA1 (PCM186X_PAGE_BASE(1) + 5) | ||
125 | #define PCM186X_MEM_WDATA2 (PCM186X_PAGE_BASE(1) + 6) | ||
126 | #define PCM186X_MEM_WDATA3 (PCM186X_PAGE_BASE(1) + 7) | ||
127 | #define PCM186X_MEM_RDATA0 (PCM186X_PAGE_BASE(1) + 8) | ||
128 | #define PCM186X_MEM_RDATA1 (PCM186X_PAGE_BASE(1) + 9) | ||
129 | #define PCM186X_MEM_RDATA2 (PCM186X_PAGE_BASE(1) + 10) | ||
130 | #define PCM186X_MEM_RDATA3 (PCM186X_PAGE_BASE(1) + 11) | ||
131 | |||
132 | /* Register Definitions - Page 3 */ | ||
133 | #define PCM186X_OSC_PWR_DOWN_CTRL (PCM186X_PAGE_BASE(3) + 18) | ||
134 | #define PCM186X_MIC_BIAS_CTRL (PCM186X_PAGE_BASE(3) + 21) | ||
135 | |||
136 | /* Register Definitions - Page 253 */ | ||
137 | #define PCM186X_CURR_TRIM_CTRL (PCM186X_PAGE_BASE(253) + 20) | ||
138 | |||
139 | #define PCM186X_MAX_REGISTER PCM186X_CURR_TRIM_CTRL | ||
140 | |||
141 | /* PCM186X_PAGE */ | ||
142 | #define PCM186X_RESET 0xff | ||
143 | |||
144 | /* PCM186X_ADCX_INPUT_SEL_X */ | ||
145 | #define PCM186X_ADC_INPUT_SEL_POL BIT(7) | ||
146 | #define PCM186X_ADC_INPUT_SEL_MASK GENMASK(5, 0) | ||
147 | |||
148 | /* PCM186X_PCM_CFG */ | ||
149 | #define PCM186X_PCM_CFG_RX_WLEN_MASK GENMASK(7, 6) | ||
150 | #define PCM186X_PCM_CFG_RX_WLEN_SHIFT 6 | ||
151 | #define PCM186X_PCM_CFG_RX_WLEN_32 0x00 | ||
152 | #define PCM186X_PCM_CFG_RX_WLEN_24 0x01 | ||
153 | #define PCM186X_PCM_CFG_RX_WLEN_20 0x02 | ||
154 | #define PCM186X_PCM_CFG_RX_WLEN_16 0x03 | ||
155 | #define PCM186X_PCM_CFG_TDM_LRCK_MODE BIT(4) | ||
156 | #define PCM186X_PCM_CFG_TX_WLEN_MASK GENMASK(3, 2) | ||
157 | #define PCM186X_PCM_CFG_TX_WLEN_SHIFT 2 | ||
158 | #define PCM186X_PCM_CFG_TX_WLEN_32 0x00 | ||
159 | #define PCM186X_PCM_CFG_TX_WLEN_24 0x01 | ||
160 | #define PCM186X_PCM_CFG_TX_WLEN_20 0x02 | ||
161 | #define PCM186X_PCM_CFG_TX_WLEN_16 0x03 | ||
162 | #define PCM186X_PCM_CFG_FMT_MASK GENMASK(1, 0) | ||
163 | #define PCM186X_PCM_CFG_FMT_SHIFT 0 | ||
164 | #define PCM186X_PCM_CFG_FMT_I2S 0x00 | ||
165 | #define PCM186X_PCM_CFG_FMT_LEFTJ 0x01 | ||
166 | #define PCM186X_PCM_CFG_FMT_RIGHTJ 0x02 | ||
167 | #define PCM186X_PCM_CFG_FMT_TDM 0x03 | ||
168 | |||
169 | /* PCM186X_TDM_TX_SEL */ | ||
170 | #define PCM186X_TDM_TX_SEL_2CH 0x00 | ||
171 | #define PCM186X_TDM_TX_SEL_4CH 0x01 | ||
172 | #define PCM186X_TDM_TX_SEL_6CH 0x02 | ||
173 | #define PCM186X_TDM_TX_SEL_MASK 0x03 | ||
174 | |||
175 | /* PCM186X_CLK_CTRL */ | ||
176 | #define PCM186X_CLK_CTRL_SCK_XI_SEL1 BIT(7) | ||
177 | #define PCM186X_CLK_CTRL_SCK_XI_SEL0 BIT(6) | ||
178 | #define PCM186X_CLK_CTRL_SCK_SRC_PLL BIT(5) | ||
179 | #define PCM186X_CLK_CTRL_MST_MODE BIT(4) | ||
180 | #define PCM186X_CLK_CTRL_ADC_SRC_PLL BIT(3) | ||
181 | #define PCM186X_CLK_CTRL_DSP2_SRC_PLL BIT(2) | ||
182 | #define PCM186X_CLK_CTRL_DSP1_SRC_PLL BIT(1) | ||
183 | #define PCM186X_CLK_CTRL_CLKDET_EN BIT(0) | ||
184 | |||
185 | /* PCM186X_PLL_CTRL */ | ||
186 | #define PCM186X_PLL_CTRL_LOCK BIT(4) | ||
187 | #define PCM186X_PLL_CTRL_REF_SEL BIT(1) | ||
188 | #define PCM186X_PLL_CTRL_EN BIT(0) | ||
189 | |||
190 | /* PCM186X_POWER_CTRL */ | ||
191 | #define PCM186X_PWR_CTRL_PWRDN BIT(2) | ||
192 | #define PCM186X_PWR_CTRL_SLEEP BIT(1) | ||
193 | #define PCM186X_PWR_CTRL_STBY BIT(0) | ||
194 | |||
195 | /* PCM186X_CLK_STATUS */ | ||
196 | #define PCM186X_CLK_STATUS_LRCKHLT BIT(6) | ||
197 | #define PCM186X_CLK_STATUS_BCKHLT BIT(5) | ||
198 | #define PCM186X_CLK_STATUS_SCKHLT BIT(4) | ||
199 | #define PCM186X_CLK_STATUS_LRCKERR BIT(2) | ||
200 | #define PCM186X_CLK_STATUS_BCKERR BIT(1) | ||
201 | #define PCM186X_CLK_STATUS_SCKERR BIT(0) | ||
202 | |||
203 | /* PCM186X_SUPPLY_STATUS */ | ||
204 | #define PCM186X_SUPPLY_STATUS_DVDD BIT(2) | ||
205 | #define PCM186X_SUPPLY_STATUS_AVDD BIT(1) | ||
206 | #define PCM186X_SUPPLY_STATUS_LDO BIT(0) | ||
207 | |||
208 | /* PCM186X_MMAP_STAT_CTRL */ | ||
209 | #define PCM186X_MMAP_STAT_DONE BIT(4) | ||
210 | #define PCM186X_MMAP_STAT_BUSY BIT(2) | ||
211 | #define PCM186X_MMAP_STAT_R_REQ BIT(1) | ||
212 | #define PCM186X_MMAP_STAT_W_REQ BIT(0) | ||
213 | |||
214 | extern const struct regmap_config pcm186x_regmap; | ||
215 | |||
216 | int pcm186x_probe(struct device *dev, enum pcm186x_type type, int irq, | ||
217 | struct regmap *regmap); | ||
218 | int pcm186x_remove(struct device *dev); | ||
219 | |||
220 | #endif /* _PCM186X_H_ */ | ||