diff options
author | Paul Handrigan <Paul.Handrigan@cirrus.com> | 2014-06-23 18:29:53 -0400 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2014-06-24 11:16:13 -0400 |
commit | fb6f806967f6fe36fa40334e5551a5892d48f36f (patch) | |
tree | c97b30d89ae873c58c16453f4fc844a3eaa8f7fe | |
parent | 2b65df255c7e1f028f7b4f4d18fd41eecafad4bd (diff) |
ASoC: Add support for the CS4265 CODEC
This patch adds support for the Cirrus Logic CS4265 Stereo I2C CODEC.
Signed-off-by: Paul Handrigan <paul.handrigan@cirrus.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
-rw-r--r-- | sound/soc/codecs/Kconfig | 6 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/cs4265.c | 682 | ||||
-rw-r--r-- | sound/soc/codecs/cs4265.h | 64 |
4 files changed, 754 insertions, 0 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index cbfa1e18f651..3960a57124fa 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -47,6 +47,7 @@ config SND_SOC_ALL_CODECS | |||
47 | select SND_SOC_CS42L52 if I2C && INPUT | 47 | select SND_SOC_CS42L52 if I2C && INPUT |
48 | select SND_SOC_CS42L56 if I2C && INPUT | 48 | select SND_SOC_CS42L56 if I2C && INPUT |
49 | select SND_SOC_CS42L73 if I2C | 49 | select SND_SOC_CS42L73 if I2C |
50 | select SND_SOC_CS4265 if I2C | ||
50 | select SND_SOC_CS4270 if I2C | 51 | select SND_SOC_CS4270 if I2C |
51 | select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI | 52 | select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI |
52 | select SND_SOC_CS42XX8_I2C if I2C | 53 | select SND_SOC_CS42XX8_I2C if I2C |
@@ -338,6 +339,11 @@ config SND_SOC_CS42L73 | |||
338 | tristate "Cirrus Logic CS42L73 CODEC" | 339 | tristate "Cirrus Logic CS42L73 CODEC" |
339 | depends on I2C | 340 | depends on I2C |
340 | 341 | ||
342 | config SND_SOC_CS4265 | ||
343 | tristate "Cirrus Logic CS4265 CODEC" | ||
344 | depends on I2C | ||
345 | select REGMAP_I2C | ||
346 | |||
341 | # Cirrus Logic CS4270 Codec | 347 | # Cirrus Logic CS4270 Codec |
342 | config SND_SOC_CS4270 | 348 | config SND_SOC_CS4270 |
343 | tristate "Cirrus Logic CS4270 CODEC" | 349 | tristate "Cirrus Logic CS4270 CODEC" |
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index be3377b8d73f..2e62bad23ddc 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -37,6 +37,7 @@ snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o | |||
37 | snd-soc-cs42l52-objs := cs42l52.o | 37 | snd-soc-cs42l52-objs := cs42l52.o |
38 | snd-soc-cs42l56-objs := cs42l56.o | 38 | snd-soc-cs42l56-objs := cs42l56.o |
39 | snd-soc-cs42l73-objs := cs42l73.o | 39 | snd-soc-cs42l73-objs := cs42l73.o |
40 | snd-soc-cs4265-objs := cs4265.o | ||
40 | snd-soc-cs4270-objs := cs4270.o | 41 | snd-soc-cs4270-objs := cs4270.o |
41 | snd-soc-cs4271-objs := cs4271.o | 42 | snd-soc-cs4271-objs := cs4271.o |
42 | snd-soc-cs42xx8-objs := cs42xx8.o | 43 | snd-soc-cs42xx8-objs := cs42xx8.o |
@@ -202,6 +203,7 @@ obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o | |||
202 | obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o | 203 | obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o |
203 | obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o | 204 | obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o |
204 | obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o | 205 | obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o |
206 | obj-$(CONFIG_SND_SOC_CS4265) += snd-soc-cs4265.o | ||
205 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o | 207 | obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o |
206 | obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o | 208 | obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o |
207 | obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o | 209 | obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o |
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c new file mode 100644 index 000000000000..c9c04d2987c2 --- /dev/null +++ b/sound/soc/codecs/cs4265.c | |||
@@ -0,0 +1,682 @@ | |||
1 | /* | ||
2 | * cs4265.c -- CS4265 ALSA SoC audio driver | ||
3 | * | ||
4 | * Copyright 2014 Cirrus Logic, Inc. | ||
5 | * | ||
6 | * Author: Paul Handrigan <paul.handrigan@cirrus.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/moduleparam.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/gpio/consumer.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/i2c.h> | ||
21 | #include <linux/input.h> | ||
22 | #include <linux/regmap.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <sound/core.h> | ||
26 | #include <sound/pcm.h> | ||
27 | #include <sound/pcm_params.h> | ||
28 | #include <sound/soc.h> | ||
29 | #include <sound/soc-dapm.h> | ||
30 | #include <sound/initval.h> | ||
31 | #include <sound/tlv.h> | ||
32 | #include "cs4265.h" | ||
33 | |||
34 | struct cs4265_private { | ||
35 | struct device *dev; | ||
36 | struct regmap *regmap; | ||
37 | struct gpio_desc *reset_gpio; | ||
38 | u8 format; | ||
39 | u32 sysclk; | ||
40 | }; | ||
41 | |||
42 | static const struct reg_default cs4265_reg_defaults[] = { | ||
43 | { CS4265_PWRCTL, 0x0F }, | ||
44 | { CS4265_DAC_CTL, 0x08 }, | ||
45 | { CS4265_ADC_CTL, 0x00 }, | ||
46 | { CS4265_MCLK_FREQ, 0x00 }, | ||
47 | { CS4265_SIG_SEL, 0x40 }, | ||
48 | { CS4265_CHB_PGA_CTL, 0x00 }, | ||
49 | { CS4265_CHA_PGA_CTL, 0x00 }, | ||
50 | { CS4265_ADC_CTL2, 0x19 }, | ||
51 | { CS4265_DAC_CHA_VOL, 0x00 }, | ||
52 | { CS4265_DAC_CHB_VOL, 0x00 }, | ||
53 | { CS4265_DAC_CTL2, 0xC0 }, | ||
54 | { CS4265_SPDIF_CTL1, 0x00 }, | ||
55 | { CS4265_SPDIF_CTL2, 0x00 }, | ||
56 | { CS4265_INT_MASK, 0x00 }, | ||
57 | { CS4265_STATUS_MODE_MSB, 0x00 }, | ||
58 | { CS4265_STATUS_MODE_LSB, 0x00 }, | ||
59 | }; | ||
60 | |||
61 | static bool cs4265_readable_register(struct device *dev, unsigned int reg) | ||
62 | { | ||
63 | switch (reg) { | ||
64 | case CS4265_PWRCTL: | ||
65 | case CS4265_DAC_CTL: | ||
66 | case CS4265_ADC_CTL: | ||
67 | case CS4265_MCLK_FREQ: | ||
68 | case CS4265_SIG_SEL: | ||
69 | case CS4265_CHB_PGA_CTL: | ||
70 | case CS4265_CHA_PGA_CTL: | ||
71 | case CS4265_ADC_CTL2: | ||
72 | case CS4265_DAC_CHA_VOL: | ||
73 | case CS4265_DAC_CHB_VOL: | ||
74 | case CS4265_DAC_CTL2: | ||
75 | case CS4265_SPDIF_CTL1: | ||
76 | case CS4265_SPDIF_CTL2: | ||
77 | case CS4265_INT_MASK: | ||
78 | case CS4265_STATUS_MODE_MSB: | ||
79 | case CS4265_STATUS_MODE_LSB: | ||
80 | return true; | ||
81 | default: | ||
82 | return false; | ||
83 | } | ||
84 | } | ||
85 | |||
86 | static bool cs4265_volatile_register(struct device *dev, unsigned int reg) | ||
87 | { | ||
88 | switch (reg) { | ||
89 | case CS4265_INT_STATUS: | ||
90 | return 1; | ||
91 | default: | ||
92 | return 0; | ||
93 | } | ||
94 | } | ||
95 | |||
96 | static DECLARE_TLV_DB_SCALE(pga_tlv, -1200, 50, 0); | ||
97 | |||
98 | static DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 0); | ||
99 | |||
100 | static const char * const digital_input_mux_text[] = { | ||
101 | "SDIN1", "SDIN2" | ||
102 | }; | ||
103 | |||
104 | static SOC_ENUM_SINGLE_DECL(digital_input_mux_enum, CS4265_SIG_SEL, 7, | ||
105 | digital_input_mux_text); | ||
106 | |||
107 | static const struct snd_kcontrol_new digital_input_mux = | ||
108 | SOC_DAPM_ENUM("Digital Input Mux", digital_input_mux_enum); | ||
109 | |||
110 | static const char * const mic_linein_text[] = { | ||
111 | "MIC", "LINEIN" | ||
112 | }; | ||
113 | |||
114 | static SOC_ENUM_SINGLE_DECL(mic_linein_enum, CS4265_ADC_CTL2, 0, | ||
115 | mic_linein_text); | ||
116 | |||
117 | static const char * const cam_mode_text[] = { | ||
118 | "One Byte", "Two Byte" | ||
119 | }; | ||
120 | |||
121 | static SOC_ENUM_SINGLE_DECL(cam_mode_enum, CS4265_SPDIF_CTL1, 5, | ||
122 | cam_mode_text); | ||
123 | |||
124 | static const char * const cam_mono_stereo_text[] = { | ||
125 | "Stereo", "Mono" | ||
126 | }; | ||
127 | |||
128 | static SOC_ENUM_SINGLE_DECL(spdif_mono_stereo_enum, CS4265_SPDIF_CTL2, 2, | ||
129 | cam_mono_stereo_text); | ||
130 | |||
131 | static const char * const mono_select_text[] = { | ||
132 | "Channel A", "Channel B" | ||
133 | }; | ||
134 | |||
135 | static SOC_ENUM_SINGLE_DECL(spdif_mono_select_enum, CS4265_SPDIF_CTL2, 0, | ||
136 | mono_select_text); | ||
137 | |||
138 | static const struct snd_kcontrol_new mic_linein_mux = | ||
139 | SOC_DAPM_ENUM("ADC Input Capture Mux", mic_linein_enum); | ||
140 | |||
141 | static const struct snd_kcontrol_new loopback_ctl = | ||
142 | SOC_DAPM_SINGLE("Switch", CS4265_SIG_SEL, 1, 1, 0); | ||
143 | |||
144 | static const struct snd_kcontrol_new spdif_switch = | ||
145 | SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 0, 0); | ||
146 | |||
147 | static const struct snd_kcontrol_new dac_switch = | ||
148 | SOC_DAPM_SINGLE("Switch", CS4265_PWRCTL, 1, 1, 0); | ||
149 | |||
150 | static const struct snd_kcontrol_new cs4265_snd_controls[] = { | ||
151 | |||
152 | SOC_DOUBLE_R_SX_TLV("PGA Volume", CS4265_CHA_PGA_CTL, | ||
153 | CS4265_CHB_PGA_CTL, 0, 0x28, 0x30, pga_tlv), | ||
154 | SOC_DOUBLE_R_TLV("DAC Volume", CS4265_DAC_CHA_VOL, | ||
155 | CS4265_DAC_CHB_VOL, 0, 0xFF, 1, dac_tlv), | ||
156 | SOC_SINGLE("De-emp 44.1kHz Switch", CS4265_DAC_CTL, 1, | ||
157 | 1, 0), | ||
158 | SOC_SINGLE("DAC INV Switch", CS4265_DAC_CTL2, 5, | ||
159 | 1, 0), | ||
160 | SOC_SINGLE("DAC Zero Cross Switch", CS4265_DAC_CTL2, 6, | ||
161 | 1, 0), | ||
162 | SOC_SINGLE("DAC Soft Ramp Switch", CS4265_DAC_CTL2, 7, | ||
163 | 1, 0), | ||
164 | SOC_SINGLE("ADC HPF Switch", CS4265_ADC_CTL, 1, | ||
165 | 1, 0), | ||
166 | SOC_SINGLE("ADC Zero Cross Switch", CS4265_ADC_CTL2, 3, | ||
167 | 1, 1), | ||
168 | SOC_SINGLE("ADC Soft Ramp Switch", CS4265_ADC_CTL2, 7, | ||
169 | 1, 0), | ||
170 | SOC_SINGLE("E to F Buffer Disable Switch", CS4265_SPDIF_CTL1, | ||
171 | 6, 1, 0), | ||
172 | SOC_ENUM("C Data Access", cam_mode_enum), | ||
173 | SOC_SINGLE("Validity Bit Control Switch", CS4265_SPDIF_CTL2, | ||
174 | 3, 1, 0), | ||
175 | SOC_ENUM("SPDIF Mono/Stereo", spdif_mono_stereo_enum), | ||
176 | SOC_SINGLE("MMTLR Data Switch", 0, | ||
177 | 1, 1, 0), | ||
178 | SOC_ENUM("Mono Channel Select", spdif_mono_select_enum), | ||
179 | SND_SOC_BYTES("C Data Buffer", CS4265_C_DATA_BUFF, 24), | ||
180 | }; | ||
181 | |||
182 | static const struct snd_soc_dapm_widget cs4265_dapm_widgets[] = { | ||
183 | |||
184 | SND_SOC_DAPM_INPUT("LINEINL"), | ||
185 | SND_SOC_DAPM_INPUT("LINEINR"), | ||
186 | SND_SOC_DAPM_INPUT("MICL"), | ||
187 | SND_SOC_DAPM_INPUT("MICR"), | ||
188 | |||
189 | SND_SOC_DAPM_AIF_OUT("DOUT", NULL, 0, | ||
190 | SND_SOC_NOPM, 0, 0), | ||
191 | SND_SOC_DAPM_AIF_OUT("SPDIFOUT", NULL, 0, | ||
192 | SND_SOC_NOPM, 0, 0), | ||
193 | |||
194 | SND_SOC_DAPM_MUX("ADC Mux", SND_SOC_NOPM, 0, 0, &mic_linein_mux), | ||
195 | |||
196 | SND_SOC_DAPM_ADC("ADC", NULL, CS4265_PWRCTL, 2, 1), | ||
197 | SND_SOC_DAPM_PGA("Pre-amp MIC", CS4265_PWRCTL, 3, | ||
198 | 1, NULL, 0), | ||
199 | |||
200 | SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, | ||
201 | 0, 0, &digital_input_mux), | ||
202 | |||
203 | SND_SOC_DAPM_MIXER("SDIN1 Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
204 | SND_SOC_DAPM_MIXER("SDIN2 Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
205 | SND_SOC_DAPM_MIXER("SPDIF Transmitter", SND_SOC_NOPM, 0, 0, NULL, 0), | ||
206 | |||
207 | SND_SOC_DAPM_SWITCH("Loopback", SND_SOC_NOPM, 0, 0, | ||
208 | &loopback_ctl), | ||
209 | SND_SOC_DAPM_SWITCH("SPDIF", SND_SOC_NOPM, 0, 0, | ||
210 | &spdif_switch), | ||
211 | SND_SOC_DAPM_SWITCH("DAC", CS4265_PWRCTL, 1, 1, | ||
212 | &dac_switch), | ||
213 | |||
214 | SND_SOC_DAPM_AIF_IN("DIN1", NULL, 0, | ||
215 | SND_SOC_NOPM, 0, 0), | ||
216 | SND_SOC_DAPM_AIF_IN("DIN2", NULL, 0, | ||
217 | SND_SOC_NOPM, 0, 0), | ||
218 | SND_SOC_DAPM_AIF_IN("TXIN", NULL, 0, | ||
219 | CS4265_SPDIF_CTL2, 5, 1), | ||
220 | |||
221 | SND_SOC_DAPM_OUTPUT("LINEOUTL"), | ||
222 | SND_SOC_DAPM_OUTPUT("LINEOUTR"), | ||
223 | |||
224 | }; | ||
225 | |||
226 | static const struct snd_soc_dapm_route cs4265_audio_map[] = { | ||
227 | |||
228 | {"DIN1", NULL, "DAI1 Playback"}, | ||
229 | {"DIN2", NULL, "DAI2 Playback"}, | ||
230 | {"SDIN1 Input Mixer", NULL, "DIN1"}, | ||
231 | {"SDIN2 Input Mixer", NULL, "DIN2"}, | ||
232 | {"Input Mux", "SDIN1", "SDIN1 Input Mixer"}, | ||
233 | {"Input Mux", "SDIN2", "SDIN2 Input Mixer"}, | ||
234 | {"DAC", "Switch", "Input Mux"}, | ||
235 | {"SPDIF", "Switch", "Input Mux"}, | ||
236 | {"LINEOUTL", NULL, "DAC"}, | ||
237 | {"LINEOUTR", NULL, "DAC"}, | ||
238 | {"SPDIFOUT", NULL, "SPDIF"}, | ||
239 | |||
240 | {"ADC Mux", "LINEIN", "LINEINL"}, | ||
241 | {"ADC Mux", "LINEIN", "LINEINR"}, | ||
242 | {"ADC Mux", "MIC", "MICL"}, | ||
243 | {"ADC Mux", "MIC", "MICR"}, | ||
244 | {"ADC", NULL, "ADC Mux"}, | ||
245 | {"DOUT", NULL, "ADC"}, | ||
246 | {"DAI1 Capture", NULL, "DOUT"}, | ||
247 | {"DAI2 Capture", NULL, "DOUT"}, | ||
248 | |||
249 | /* Loopback */ | ||
250 | {"Loopback", "Switch", "ADC"}, | ||
251 | {"DAC", NULL, "Loopback"}, | ||
252 | }; | ||
253 | |||
254 | struct cs4265_clk_para { | ||
255 | u32 mclk; | ||
256 | u32 rate; | ||
257 | u8 fm_mode; /* values 1, 2, or 4 */ | ||
258 | u8 mclkdiv; | ||
259 | }; | ||
260 | |||
261 | static const struct cs4265_clk_para clk_map_table[] = { | ||
262 | /*32k*/ | ||
263 | {8192000, 32000, 0, 0}, | ||
264 | {12288000, 32000, 0, 1}, | ||
265 | {16384000, 32000, 0, 2}, | ||
266 | {24576000, 32000, 0, 3}, | ||
267 | {32768000, 32000, 0, 4}, | ||
268 | |||
269 | /*44.1k*/ | ||
270 | {11289600, 44100, 0, 0}, | ||
271 | {16934400, 44100, 0, 1}, | ||
272 | {22579200, 44100, 0, 2}, | ||
273 | {33868000, 44100, 0, 3}, | ||
274 | {45158400, 44100, 0, 4}, | ||
275 | |||
276 | /*48k*/ | ||
277 | {12288000, 48000, 0, 0}, | ||
278 | {18432000, 48000, 0, 1}, | ||
279 | {24576000, 48000, 0, 2}, | ||
280 | {36864000, 48000, 0, 3}, | ||
281 | {49152000, 48000, 0, 4}, | ||
282 | |||
283 | /*64k*/ | ||
284 | {8192000, 64000, 1, 0}, | ||
285 | {1228800, 64000, 1, 1}, | ||
286 | {1693440, 64000, 1, 2}, | ||
287 | {2457600, 64000, 1, 3}, | ||
288 | {3276800, 64000, 1, 4}, | ||
289 | |||
290 | /* 88.2k */ | ||
291 | {11289600, 88200, 1, 0}, | ||
292 | {16934400, 88200, 1, 1}, | ||
293 | {22579200, 88200, 1, 2}, | ||
294 | {33868000, 88200, 1, 3}, | ||
295 | {45158400, 88200, 1, 4}, | ||
296 | |||
297 | /* 96k */ | ||
298 | {12288000, 96000, 1, 0}, | ||
299 | {18432000, 96000, 1, 1}, | ||
300 | {24576000, 96000, 1, 2}, | ||
301 | {36864000, 96000, 1, 3}, | ||
302 | {49152000, 96000, 1, 4}, | ||
303 | |||
304 | /* 128k */ | ||
305 | {8192000, 128000, 2, 0}, | ||
306 | {12288000, 128000, 2, 1}, | ||
307 | {16934400, 128000, 2, 2}, | ||
308 | {24576000, 128000, 2, 3}, | ||
309 | {32768000, 128000, 2, 4}, | ||
310 | |||
311 | /* 176.4k */ | ||
312 | {11289600, 176400, 2, 0}, | ||
313 | {16934400, 176400, 2, 1}, | ||
314 | {22579200, 176400, 2, 2}, | ||
315 | {33868000, 176400, 2, 3}, | ||
316 | {49152000, 176400, 2, 4}, | ||
317 | |||
318 | /* 192k */ | ||
319 | {12288000, 192000, 2, 0}, | ||
320 | {18432000, 192000, 2, 1}, | ||
321 | {24576000, 192000, 2, 2}, | ||
322 | {36864000, 192000, 2, 3}, | ||
323 | {49152000, 192000, 2, 4}, | ||
324 | }; | ||
325 | |||
326 | static int cs4265_get_clk_index(int mclk, int rate) | ||
327 | { | ||
328 | int i; | ||
329 | |||
330 | for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) { | ||
331 | if (clk_map_table[i].rate == rate && | ||
332 | clk_map_table[i].mclk == mclk) | ||
333 | return i; | ||
334 | } | ||
335 | return -EINVAL; | ||
336 | } | ||
337 | |||
338 | static int cs4265_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id, | ||
339 | unsigned int freq, int dir) | ||
340 | { | ||
341 | struct snd_soc_codec *codec = codec_dai->codec; | ||
342 | struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec); | ||
343 | int i; | ||
344 | |||
345 | if (clk_id != 0) { | ||
346 | dev_err(codec->dev, "Invalid clk_id %d\n", clk_id); | ||
347 | return -EINVAL; | ||
348 | } | ||
349 | for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) { | ||
350 | if (clk_map_table[i].mclk == freq) { | ||
351 | cs4265->sysclk = freq; | ||
352 | return 0; | ||
353 | } | ||
354 | } | ||
355 | cs4265->sysclk = 0; | ||
356 | dev_err(codec->dev, "Invalid freq parameter %d\n", freq); | ||
357 | return -EINVAL; | ||
358 | } | ||
359 | |||
360 | static int cs4265_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) | ||
361 | { | ||
362 | struct snd_soc_codec *codec = codec_dai->codec; | ||
363 | struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec); | ||
364 | u8 iface = 0; | ||
365 | |||
366 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
367 | case SND_SOC_DAIFMT_CBM_CFM: | ||
368 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
369 | CS4265_ADC_MASTER, | ||
370 | CS4265_ADC_MASTER); | ||
371 | break; | ||
372 | case SND_SOC_DAIFMT_CBS_CFS: | ||
373 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
374 | CS4265_ADC_MASTER, | ||
375 | 0); | ||
376 | break; | ||
377 | default: | ||
378 | return -EINVAL; | ||
379 | } | ||
380 | |||
381 | /* interface format */ | ||
382 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
383 | case SND_SOC_DAIFMT_I2S: | ||
384 | iface |= SND_SOC_DAIFMT_I2S; | ||
385 | break; | ||
386 | case SND_SOC_DAIFMT_RIGHT_J: | ||
387 | iface |= SND_SOC_DAIFMT_RIGHT_J; | ||
388 | break; | ||
389 | case SND_SOC_DAIFMT_LEFT_J: | ||
390 | iface |= SND_SOC_DAIFMT_LEFT_J; | ||
391 | break; | ||
392 | default: | ||
393 | return -EINVAL; | ||
394 | } | ||
395 | |||
396 | cs4265->format = iface; | ||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | static int cs4265_digital_mute(struct snd_soc_dai *dai, int mute) | ||
401 | { | ||
402 | struct snd_soc_codec *codec = dai->codec; | ||
403 | |||
404 | if (mute) { | ||
405 | snd_soc_update_bits(codec, CS4265_DAC_CTL, | ||
406 | CS4265_DAC_CTL_MUTE, | ||
407 | CS4265_DAC_CTL_MUTE); | ||
408 | snd_soc_update_bits(codec, CS4265_SPDIF_CTL2, | ||
409 | CS4265_SPDIF_CTL2_MUTE, | ||
410 | CS4265_SPDIF_CTL2_MUTE); | ||
411 | } else { | ||
412 | snd_soc_update_bits(codec, CS4265_DAC_CTL, | ||
413 | CS4265_DAC_CTL_MUTE, | ||
414 | 0); | ||
415 | snd_soc_update_bits(codec, CS4265_SPDIF_CTL2, | ||
416 | CS4265_SPDIF_CTL2_MUTE, | ||
417 | 0); | ||
418 | } | ||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream, | ||
423 | struct snd_pcm_hw_params *params, | ||
424 | struct snd_soc_dai *dai) | ||
425 | { | ||
426 | struct snd_soc_codec *codec = dai->codec; | ||
427 | struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec); | ||
428 | int index; | ||
429 | |||
430 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && | ||
431 | ((cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK) | ||
432 | == SND_SOC_DAIFMT_RIGHT_J)) | ||
433 | return -EINVAL; | ||
434 | |||
435 | index = cs4265_get_clk_index(cs4265->sysclk, params_rate(params)); | ||
436 | if (index >= 0) { | ||
437 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
438 | CS4265_ADC_FM, clk_map_table[index].fm_mode); | ||
439 | snd_soc_update_bits(codec, CS4265_MCLK_FREQ, | ||
440 | CS4265_MCLK_FREQ_MASK, | ||
441 | clk_map_table[index].mclkdiv); | ||
442 | |||
443 | } else { | ||
444 | dev_err(codec->dev, "can't get correct mclk\n"); | ||
445 | return -EINVAL; | ||
446 | } | ||
447 | |||
448 | switch (cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
449 | case SND_SOC_DAIFMT_I2S: | ||
450 | snd_soc_update_bits(codec, CS4265_DAC_CTL, | ||
451 | CS4265_DAC_CTL_DIF, (1 << 4)); | ||
452 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
453 | CS4265_ADC_DIF, (1 << 4)); | ||
454 | snd_soc_update_bits(codec, CS4265_SPDIF_CTL2, | ||
455 | CS4265_SPDIF_CTL2_DIF, (1 << 6)); | ||
456 | break; | ||
457 | case SND_SOC_DAIFMT_RIGHT_J: | ||
458 | if (params_format(params) & SNDRV_PCM_FORMAT_S16_LE) { | ||
459 | snd_soc_update_bits(codec, CS4265_DAC_CTL, | ||
460 | CS4265_DAC_CTL_DIF, (1 << 5)); | ||
461 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
462 | CS4265_SPDIF_CTL2_DIF, (1 << 7)); | ||
463 | } else { | ||
464 | snd_soc_update_bits(codec, CS4265_DAC_CTL, | ||
465 | CS4265_DAC_CTL_DIF, (3 << 5)); | ||
466 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
467 | CS4265_SPDIF_CTL2_DIF, (1 << 7)); | ||
468 | } | ||
469 | break; | ||
470 | case SND_SOC_DAIFMT_LEFT_J: | ||
471 | snd_soc_update_bits(codec, CS4265_DAC_CTL, | ||
472 | CS4265_DAC_CTL_DIF, 0); | ||
473 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
474 | CS4265_ADC_DIF, 0); | ||
475 | snd_soc_update_bits(codec, CS4265_ADC_CTL, | ||
476 | CS4265_SPDIF_CTL2_DIF, (1 << 6)); | ||
477 | |||
478 | break; | ||
479 | default: | ||
480 | return -EINVAL; | ||
481 | } | ||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | static int cs4265_set_bias_level(struct snd_soc_codec *codec, | ||
486 | enum snd_soc_bias_level level) | ||
487 | { | ||
488 | switch (level) { | ||
489 | case SND_SOC_BIAS_ON: | ||
490 | break; | ||
491 | case SND_SOC_BIAS_PREPARE: | ||
492 | snd_soc_update_bits(codec, CS4265_PWRCTL, | ||
493 | CS4265_PWRCTL_PDN, 0); | ||
494 | break; | ||
495 | case SND_SOC_BIAS_STANDBY: | ||
496 | snd_soc_update_bits(codec, CS4265_PWRCTL, | ||
497 | CS4265_PWRCTL_PDN, | ||
498 | CS4265_PWRCTL_PDN); | ||
499 | break; | ||
500 | case SND_SOC_BIAS_OFF: | ||
501 | snd_soc_update_bits(codec, CS4265_PWRCTL, | ||
502 | CS4265_PWRCTL_PDN, | ||
503 | CS4265_PWRCTL_PDN); | ||
504 | break; | ||
505 | } | ||
506 | codec->dapm.bias_level = level; | ||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | #define CS4265_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ | ||
511 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ | ||
512 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \ | ||
513 | SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) | ||
514 | |||
515 | #define CS4265_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \ | ||
516 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE) | ||
517 | |||
518 | static const struct snd_soc_dai_ops cs4265_ops = { | ||
519 | .hw_params = cs4265_pcm_hw_params, | ||
520 | .digital_mute = cs4265_digital_mute, | ||
521 | .set_fmt = cs4265_set_fmt, | ||
522 | .set_sysclk = cs4265_set_sysclk, | ||
523 | }; | ||
524 | |||
525 | static struct snd_soc_dai_driver cs4265_dai[] = { | ||
526 | { | ||
527 | .name = "cs4265-dai1", | ||
528 | .playback = { | ||
529 | .stream_name = "DAI1 Playback", | ||
530 | .channels_min = 1, | ||
531 | .channels_max = 2, | ||
532 | .rates = CS4265_RATES, | ||
533 | .formats = CS4265_FORMATS, | ||
534 | }, | ||
535 | .capture = { | ||
536 | .stream_name = "DAI1 Capture", | ||
537 | .channels_min = 1, | ||
538 | .channels_max = 2, | ||
539 | .rates = CS4265_RATES, | ||
540 | .formats = CS4265_FORMATS, | ||
541 | }, | ||
542 | .ops = &cs4265_ops, | ||
543 | }, | ||
544 | { | ||
545 | .name = "cs4265-dai2", | ||
546 | .playback = { | ||
547 | .stream_name = "DAI2 Playback", | ||
548 | .channels_min = 1, | ||
549 | .channels_max = 2, | ||
550 | .rates = CS4265_RATES, | ||
551 | .formats = CS4265_FORMATS, | ||
552 | }, | ||
553 | .capture = { | ||
554 | .stream_name = "DAI2 Capture", | ||
555 | .channels_min = 1, | ||
556 | .channels_max = 2, | ||
557 | .rates = CS4265_RATES, | ||
558 | .formats = CS4265_FORMATS, | ||
559 | }, | ||
560 | .ops = &cs4265_ops, | ||
561 | }, | ||
562 | }; | ||
563 | |||
564 | static const struct snd_soc_codec_driver soc_codec_cs4265 = { | ||
565 | .set_bias_level = cs4265_set_bias_level, | ||
566 | |||
567 | .dapm_widgets = cs4265_dapm_widgets, | ||
568 | .num_dapm_widgets = ARRAY_SIZE(cs4265_dapm_widgets), | ||
569 | .dapm_routes = cs4265_audio_map, | ||
570 | .num_dapm_routes = ARRAY_SIZE(cs4265_audio_map), | ||
571 | |||
572 | .controls = cs4265_snd_controls, | ||
573 | .num_controls = ARRAY_SIZE(cs4265_snd_controls), | ||
574 | }; | ||
575 | |||
576 | static const struct regmap_config cs4265_regmap = { | ||
577 | .reg_bits = 8, | ||
578 | .val_bits = 8, | ||
579 | |||
580 | .max_register = CS4265_MAX_REGISTER, | ||
581 | .reg_defaults = cs4265_reg_defaults, | ||
582 | .num_reg_defaults = ARRAY_SIZE(cs4265_reg_defaults), | ||
583 | .readable_reg = cs4265_readable_register, | ||
584 | .volatile_reg = cs4265_volatile_register, | ||
585 | .cache_type = REGCACHE_RBTREE, | ||
586 | }; | ||
587 | |||
588 | static int cs4265_i2c_probe(struct i2c_client *i2c_client, | ||
589 | const struct i2c_device_id *id) | ||
590 | { | ||
591 | struct cs4265_private *cs4265; | ||
592 | int ret = 0; | ||
593 | unsigned int devid = 0; | ||
594 | unsigned int reg; | ||
595 | |||
596 | cs4265 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs4265_private), | ||
597 | GFP_KERNEL); | ||
598 | if (cs4265 == NULL) | ||
599 | return -ENOMEM; | ||
600 | cs4265->dev = &i2c_client->dev; | ||
601 | |||
602 | cs4265->regmap = devm_regmap_init_i2c(i2c_client, &cs4265_regmap); | ||
603 | if (IS_ERR(cs4265->regmap)) { | ||
604 | ret = PTR_ERR(cs4265->regmap); | ||
605 | dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret); | ||
606 | return ret; | ||
607 | } | ||
608 | |||
609 | cs4265->reset_gpio = devm_gpiod_get(&i2c_client->dev, | ||
610 | "reset-gpios"); | ||
611 | if (IS_ERR(cs4265->reset_gpio)) { | ||
612 | ret = PTR_ERR(cs4265->reset_gpio); | ||
613 | if (ret != -ENOENT && ret != -ENOSYS) | ||
614 | return ret; | ||
615 | |||
616 | cs4265->reset_gpio = NULL; | ||
617 | } else { | ||
618 | ret = gpiod_direction_output(cs4265->reset_gpio, 0); | ||
619 | if (ret) | ||
620 | return ret; | ||
621 | mdelay(1); | ||
622 | gpiod_set_value_cansleep(cs4265->reset_gpio, 1); | ||
623 | |||
624 | } | ||
625 | |||
626 | i2c_set_clientdata(i2c_client, cs4265); | ||
627 | |||
628 | ret = regmap_read(cs4265->regmap, CS4265_CHIP_ID, ®); | ||
629 | devid = reg & CS4265_CHIP_ID_MASK; | ||
630 | if (devid != CS4265_CHIP_ID_VAL) { | ||
631 | ret = -ENODEV; | ||
632 | dev_err(&i2c_client->dev, | ||
633 | "CS4265 Device ID (%X). Expected %X\n", | ||
634 | devid, CS4265_CHIP_ID); | ||
635 | return ret; | ||
636 | } | ||
637 | dev_info(&i2c_client->dev, | ||
638 | "CS4265 Version %x\n", | ||
639 | reg & CS4265_REV_ID_MASK); | ||
640 | |||
641 | regmap_write(cs4265->regmap, CS4265_PWRCTL, 0x0F); | ||
642 | |||
643 | ret = snd_soc_register_codec(&i2c_client->dev, | ||
644 | &soc_codec_cs4265, cs4265_dai, | ||
645 | ARRAY_SIZE(cs4265_dai)); | ||
646 | return ret; | ||
647 | } | ||
648 | |||
649 | static int cs4265_i2c_remove(struct i2c_client *client) | ||
650 | { | ||
651 | snd_soc_unregister_codec(&client->dev); | ||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | static const struct of_device_id cs4265_of_match[] = { | ||
656 | { .compatible = "cirrus,cs4265", }, | ||
657 | { } | ||
658 | }; | ||
659 | MODULE_DEVICE_TABLE(of, cs4265_of_match); | ||
660 | |||
661 | static const struct i2c_device_id cs4265_id[] = { | ||
662 | { "cs4265", 0 }, | ||
663 | { } | ||
664 | }; | ||
665 | MODULE_DEVICE_TABLE(i2c, cs4265_id); | ||
666 | |||
667 | static struct i2c_driver cs4265_i2c_driver = { | ||
668 | .driver = { | ||
669 | .name = "cs4265", | ||
670 | .owner = THIS_MODULE, | ||
671 | .of_match_table = cs4265_of_match, | ||
672 | }, | ||
673 | .id_table = cs4265_id, | ||
674 | .probe = cs4265_i2c_probe, | ||
675 | .remove = cs4265_i2c_remove, | ||
676 | }; | ||
677 | |||
678 | module_i2c_driver(cs4265_i2c_driver); | ||
679 | |||
680 | MODULE_DESCRIPTION("ASoC CS4265 driver"); | ||
681 | MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paul.handrigan@cirrus.com>"); | ||
682 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/cs4265.h b/sound/soc/codecs/cs4265.h new file mode 100644 index 000000000000..0a80a8dcec67 --- /dev/null +++ b/sound/soc/codecs/cs4265.h | |||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | * cs4265.h -- CS4265 ALSA SoC audio driver | ||
3 | * | ||
4 | * Copyright 2014 Cirrus Logic, Inc. | ||
5 | * | ||
6 | * Author: Paul Handrigan <paul.handrigan@cirrus.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #ifndef __CS4265_H__ | ||
15 | #define __CS4265_H__ | ||
16 | |||
17 | #define CS4265_CHIP_ID 0x1 | ||
18 | #define CS4265_CHIP_ID_VAL 0xD0 | ||
19 | #define CS4265_CHIP_ID_MASK 0xF0 | ||
20 | #define CS4265_REV_ID_MASK 0x0F | ||
21 | |||
22 | #define CS4265_PWRCTL 0x02 | ||
23 | #define CS4265_PWRCTL_PDN 1 | ||
24 | |||
25 | #define CS4265_DAC_CTL 0x3 | ||
26 | #define CS4265_DAC_CTL_MUTE (1 << 2) | ||
27 | #define CS4265_DAC_CTL_DIF (3 << 4) | ||
28 | |||
29 | #define CS4265_ADC_CTL 0x4 | ||
30 | #define CS4265_ADC_MASTER 1 | ||
31 | #define CS4265_ADC_DIF (1 << 4) | ||
32 | #define CS4265_ADC_FM (3 << 6) | ||
33 | |||
34 | #define CS4265_MCLK_FREQ 0x5 | ||
35 | #define CS4265_MCLK_FREQ_MASK (7 << 4) | ||
36 | |||
37 | #define CS4265_SIG_SEL 0x6 | ||
38 | #define CS4265_SIG_SEL_LOOP (1 << 1) | ||
39 | |||
40 | #define CS4265_CHB_PGA_CTL 0x7 | ||
41 | #define CS4265_CHA_PGA_CTL 0x8 | ||
42 | |||
43 | #define CS4265_ADC_CTL2 0x9 | ||
44 | |||
45 | #define CS4265_DAC_CHA_VOL 0xA | ||
46 | #define CS4265_DAC_CHB_VOL 0xB | ||
47 | |||
48 | #define CS4265_DAC_CTL2 0xC | ||
49 | |||
50 | #define CS4265_INT_STATUS 0xD | ||
51 | #define CS4265_INT_MASK 0xE | ||
52 | #define CS4265_STATUS_MODE_MSB 0xF | ||
53 | #define CS4265_STATUS_MODE_LSB 0x10 | ||
54 | |||
55 | #define CS4265_SPDIF_CTL1 0x11 | ||
56 | |||
57 | #define CS4265_SPDIF_CTL2 0x12 | ||
58 | #define CS4265_SPDIF_CTL2_MUTE (1 << 4) | ||
59 | #define CS4265_SPDIF_CTL2_DIF (3 << 6) | ||
60 | |||
61 | #define CS4265_C_DATA_BUFF 0x13 | ||
62 | #define CS4265_MAX_REGISTER 0x2A | ||
63 | |||
64 | #endif | ||