diff options
author | Tim Howe <tim.howe@cirrus.com> | 2015-07-16 15:51:40 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2015-07-16 16:56:02 -0400 |
commit | e40da86a37f64c73b810bc7a63d77c44dc61accb (patch) | |
tree | 38538af19d43396b9e996ada4b9f72fa274e0cff | |
parent | d770e558e21961ad6cfdf0ff7df0eb5d7d4f0754 (diff) |
ASoC: cs4349: Add support for Cirrus Logic CS4349
Signed-off-by: Tim Howe <tim.howe@cirrus.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | Documentation/devicetree/bindings/sound/cs4349.txt | 19 | ||||
-rw-r--r-- | sound/soc/codecs/Kconfig | 6 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/codecs/cs4349.c | 401 | ||||
-rw-r--r-- | sound/soc/codecs/cs4349.h | 146 |
5 files changed, 574 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/sound/cs4349.txt b/Documentation/devicetree/bindings/sound/cs4349.txt new file mode 100644 index 000000000000..54c117b59dba --- /dev/null +++ b/Documentation/devicetree/bindings/sound/cs4349.txt | |||
@@ -0,0 +1,19 @@ | |||
1 | CS4349 audio CODEC | ||
2 | |||
3 | Required properties: | ||
4 | |||
5 | - compatible : "cirrus,cs4349" | ||
6 | |||
7 | - reg : the I2C address of the device for I2C | ||
8 | |||
9 | Optional properties: | ||
10 | |||
11 | - reset-gpios : a GPIO spec for the reset pin. | ||
12 | |||
13 | Example: | ||
14 | |||
15 | codec: cs4349@48 { | ||
16 | compatible = "cirrus,cs4349"; | ||
17 | reg = <0x48>; | ||
18 | reset-gpios = <&gpio 54 0>; | ||
19 | }; | ||
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index efaafce8ba38..6a759d13ef22 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
@@ -53,6 +53,7 @@ config SND_SOC_ALL_CODECS | |||
53 | select SND_SOC_CS4271_I2C if I2C | 53 | select SND_SOC_CS4271_I2C if I2C |
54 | select SND_SOC_CS4271_SPI if SPI_MASTER | 54 | select SND_SOC_CS4271_SPI if SPI_MASTER |
55 | select SND_SOC_CS42XX8_I2C if I2C | 55 | select SND_SOC_CS42XX8_I2C if I2C |
56 | select SND_SOC_CS4349 if I2C | ||
56 | select SND_SOC_CX20442 if TTY | 57 | select SND_SOC_CX20442 if TTY |
57 | select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI | 58 | select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI |
58 | select SND_SOC_DA7213 if I2C | 59 | select SND_SOC_DA7213 if I2C |
@@ -403,6 +404,11 @@ config SND_SOC_CS42XX8_I2C | |||
403 | select SND_SOC_CS42XX8 | 404 | select SND_SOC_CS42XX8 |
404 | select REGMAP_I2C | 405 | select REGMAP_I2C |
405 | 406 | ||
407 | # Cirrus Logic CS4349 HiFi DAC | ||
408 | config SND_SOC_CS4349 | ||
409 | tristate "Cirrus Logic CS4349 CODEC" | ||
410 | depends on I2C | ||
411 | |||
406 | config SND_SOC_CX20442 | 412 | config SND_SOC_CX20442 |
407 | tristate | 413 | tristate |
408 | depends on TTY | 414 | depends on TTY |
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index cf160d972cb3..f19e8d29f89d 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
@@ -45,6 +45,7 @@ snd-soc-cs4271-i2c-objs := cs4271-i2c.o | |||
45 | snd-soc-cs4271-spi-objs := cs4271-spi.o | 45 | snd-soc-cs4271-spi-objs := cs4271-spi.o |
46 | snd-soc-cs42xx8-objs := cs42xx8.o | 46 | snd-soc-cs42xx8-objs := cs42xx8.o |
47 | snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o | 47 | snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o |
48 | snd-soc-cs4349-objs := cs4349.o | ||
48 | snd-soc-cx20442-objs := cx20442.o | 49 | snd-soc-cx20442-objs := cx20442.o |
49 | snd-soc-da7210-objs := da7210.o | 50 | snd-soc-da7210-objs := da7210.o |
50 | snd-soc-da7213-objs := da7213.o | 51 | snd-soc-da7213-objs := da7213.o |
@@ -232,6 +233,7 @@ obj-$(CONFIG_SND_SOC_CS4271_I2C) += snd-soc-cs4271-i2c.o | |||
232 | obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o | 233 | obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o |
233 | obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o | 234 | obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o |
234 | obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o | 235 | obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o |
236 | obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o | ||
235 | obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o | 237 | obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o |
236 | obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o | 238 | obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o |
237 | obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o | 239 | obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o |
diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c new file mode 100644 index 000000000000..a8df8a749607 --- /dev/null +++ b/sound/soc/codecs/cs4349.c | |||
@@ -0,0 +1,401 @@ | |||
1 | /* | ||
2 | * cs4349.c -- CS4349 ALSA Soc Audio driver | ||
3 | * | ||
4 | * Copyright 2015 Cirrus Logic, Inc. | ||
5 | * | ||
6 | * Authors: Tim Howe <Tim.Howe@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 | #include <linux/module.h> | ||
14 | #include <linux/moduleparam.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/delay.h> | ||
18 | #include <linux/gpio.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/pm.h> | ||
21 | #include <linux/i2c.h> | ||
22 | #include <linux/of_device.h> | ||
23 | #include <linux/regmap.h> | ||
24 | #include <linux/slab.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 "cs4349.h" | ||
33 | |||
34 | |||
35 | static const struct reg_default cs4349_reg_defaults[] = { | ||
36 | { 2, 0x00 }, /* r02 - Mode Control */ | ||
37 | { 3, 0x09 }, /* r03 - Volume, Mixing and Inversion Control */ | ||
38 | { 4, 0x81 }, /* r04 - Mute Control */ | ||
39 | { 5, 0x00 }, /* r05 - Channel A Volume Control */ | ||
40 | { 6, 0x00 }, /* r06 - Channel B Volume Control */ | ||
41 | { 7, 0xB1 }, /* r07 - Ramp and Filter Control */ | ||
42 | { 8, 0x1C }, /* r08 - Misc. Control */ | ||
43 | }; | ||
44 | |||
45 | /* Private data for the CS4349 */ | ||
46 | struct cs4349_private { | ||
47 | struct regmap *regmap; | ||
48 | struct cs4349_platform_data pdata; | ||
49 | struct gpio_desc *reset_gpio; | ||
50 | unsigned int mode; | ||
51 | int rate; | ||
52 | }; | ||
53 | |||
54 | static bool cs4349_readable_register(struct device *dev, unsigned int reg) | ||
55 | { | ||
56 | switch (reg) { | ||
57 | case CS4349_CHIPID: | ||
58 | case CS4349_MODE: | ||
59 | case CS4349_VMI: | ||
60 | case CS4349_MUTE: | ||
61 | case CS4349_VOLA: | ||
62 | case CS4349_VOLB: | ||
63 | case CS4349_RMPFLT: | ||
64 | case CS4349_MISC: | ||
65 | return true; | ||
66 | default: | ||
67 | return false; | ||
68 | } | ||
69 | } | ||
70 | |||
71 | static int cs4349_set_dai_fmt(struct snd_soc_dai *codec_dai, | ||
72 | unsigned int format) | ||
73 | { | ||
74 | struct snd_soc_codec *codec = codec_dai->codec; | ||
75 | struct cs4349_private *cs4349 = snd_soc_codec_get_drvdata(codec); | ||
76 | unsigned int fmt; | ||
77 | |||
78 | fmt = format & SND_SOC_DAIFMT_FORMAT_MASK; | ||
79 | |||
80 | switch (fmt) { | ||
81 | case SND_SOC_DAIFMT_I2S: | ||
82 | case SND_SOC_DAIFMT_LEFT_J: | ||
83 | case SND_SOC_DAIFMT_RIGHT_J: | ||
84 | cs4349->mode = format & SND_SOC_DAIFMT_FORMAT_MASK; | ||
85 | break; | ||
86 | default: | ||
87 | return -EINVAL; | ||
88 | } | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static int cs4349_pcm_hw_params(struct snd_pcm_substream *substream, | ||
94 | struct snd_pcm_hw_params *params, | ||
95 | struct snd_soc_dai *dai) | ||
96 | { | ||
97 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
98 | struct snd_soc_codec *codec = rtd->codec; | ||
99 | struct cs4349_private *cs4349 = snd_soc_codec_get_drvdata(codec); | ||
100 | int mode, fmt, ret; | ||
101 | |||
102 | mode = snd_soc_read(codec, CS4349_MODE); | ||
103 | cs4349->rate = params_rate(params); | ||
104 | |||
105 | switch (cs4349->mode) { | ||
106 | case SND_SOC_DAIFMT_I2S: | ||
107 | mode |= MODE_FORMAT(DIF_I2S); | ||
108 | break; | ||
109 | case SND_SOC_DAIFMT_LEFT_J: | ||
110 | mode |= MODE_FORMAT(DIF_LEFT_JST); | ||
111 | break; | ||
112 | case SND_SOC_DAIFMT_RIGHT_J: | ||
113 | switch (params_width(params)) { | ||
114 | case 16: | ||
115 | fmt = DIF_RGHT_JST16; | ||
116 | break; | ||
117 | case 24: | ||
118 | fmt = DIF_RGHT_JST24; | ||
119 | break; | ||
120 | default: | ||
121 | return -EINVAL; | ||
122 | } | ||
123 | mode |= MODE_FORMAT(fmt); | ||
124 | break; | ||
125 | default: | ||
126 | return -EINVAL; | ||
127 | } | ||
128 | |||
129 | ret = snd_soc_write(codec, CS4349_MODE, mode); | ||
130 | if (ret < 0) | ||
131 | return ret; | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static int cs4349_digital_mute(struct snd_soc_dai *dai, int mute) | ||
137 | { | ||
138 | struct snd_soc_codec *codec = dai->codec; | ||
139 | int reg; | ||
140 | |||
141 | reg = 0; | ||
142 | if (mute) | ||
143 | reg = MUTE_AB_MASK; | ||
144 | |||
145 | return snd_soc_update_bits(codec, CS4349_MUTE, MUTE_AB_MASK, reg); | ||
146 | } | ||
147 | |||
148 | static DECLARE_TLV_DB_SCALE(dig_tlv, -12750, 50, 0); | ||
149 | |||
150 | static const char * const chan_mix_texts[] = { | ||
151 | "Mute", "MuteA", "MuteA SwapB", "MuteA MonoB", "SwapA MuteB", | ||
152 | "BothR", "Swap", "SwapA MonoB", "MuteB", "Normal", "BothL", | ||
153 | "MonoB", "MonoA MuteB", "MonoA", "MonoA SwapB", "Mono", | ||
154 | /*Normal == Channel A = Left, Channel B = Right*/ | ||
155 | }; | ||
156 | |||
157 | static const char * const fm_texts[] = { | ||
158 | "Auto", "Single", "Double", "Quad", | ||
159 | }; | ||
160 | |||
161 | static const char * const deemph_texts[] = { | ||
162 | "None", "44.1k", "48k", "32k", | ||
163 | }; | ||
164 | |||
165 | static const char * const softr_zeroc_texts[] = { | ||
166 | "Immediate", "Zero Cross", "Soft Ramp", "SR on ZC", | ||
167 | }; | ||
168 | |||
169 | static int deemph_values[] = { | ||
170 | 0, 4, 8, 12, | ||
171 | }; | ||
172 | |||
173 | static int softr_zeroc_values[] = { | ||
174 | 0, 64, 128, 192, | ||
175 | }; | ||
176 | |||
177 | static const struct soc_enum chan_mix_enum = | ||
178 | SOC_ENUM_SINGLE(CS4349_VMI, 0, | ||
179 | ARRAY_SIZE(chan_mix_texts), | ||
180 | chan_mix_texts); | ||
181 | |||
182 | static const struct soc_enum fm_mode_enum = | ||
183 | SOC_ENUM_SINGLE(CS4349_MODE, 0, | ||
184 | ARRAY_SIZE(fm_texts), | ||
185 | fm_texts); | ||
186 | |||
187 | static SOC_VALUE_ENUM_SINGLE_DECL(deemph_enum, CS4349_MODE, 0, DEM_MASK, | ||
188 | deemph_texts, deemph_values); | ||
189 | |||
190 | static SOC_VALUE_ENUM_SINGLE_DECL(softr_zeroc_enum, CS4349_RMPFLT, 0, | ||
191 | SR_ZC_MASK, softr_zeroc_texts, | ||
192 | softr_zeroc_values); | ||
193 | |||
194 | static const struct snd_kcontrol_new cs4349_snd_controls[] = { | ||
195 | SOC_DOUBLE_R_TLV("Master Playback Volume", | ||
196 | CS4349_VOLA, CS4349_VOLB, 0, 0xFF, 1, dig_tlv), | ||
197 | SOC_ENUM("Functional Mode", fm_mode_enum), | ||
198 | SOC_ENUM("De-Emphasis Control", deemph_enum), | ||
199 | SOC_ENUM("Soft Ramp Zero Cross Control", softr_zeroc_enum), | ||
200 | SOC_ENUM("Channel Mixer", chan_mix_enum), | ||
201 | SOC_SINGLE("VolA = VolB Switch", CS4349_VMI, 7, 1, 0), | ||
202 | SOC_SINGLE("InvertA Switch", CS4349_VMI, 6, 1, 0), | ||
203 | SOC_SINGLE("InvertB Switch", CS4349_VMI, 5, 1, 0), | ||
204 | SOC_SINGLE("Auto-Mute Switch", CS4349_MUTE, 7, 1, 0), | ||
205 | SOC_SINGLE("MUTEC A = B Switch", CS4349_MUTE, 5, 1, 0), | ||
206 | SOC_SINGLE("Soft Ramp Up Switch", CS4349_RMPFLT, 5, 1, 0), | ||
207 | SOC_SINGLE("Soft Ramp Down Switch", CS4349_RMPFLT, 4, 1, 0), | ||
208 | SOC_SINGLE("Slow Roll Off Filter Switch", CS4349_RMPFLT, 2, 1, 0), | ||
209 | SOC_SINGLE("Freeze Switch", CS4349_MISC, 5, 1, 0), | ||
210 | SOC_SINGLE("Popguard Switch", CS4349_MISC, 4, 1, 0), | ||
211 | }; | ||
212 | |||
213 | static const struct snd_soc_dapm_widget cs4349_dapm_widgets[] = { | ||
214 | SND_SOC_DAPM_DAC("HiFi DAC", NULL, SND_SOC_NOPM, 0, 0), | ||
215 | |||
216 | SND_SOC_DAPM_OUTPUT("OutputA"), | ||
217 | SND_SOC_DAPM_OUTPUT("OutputB"), | ||
218 | }; | ||
219 | |||
220 | static const struct snd_soc_dapm_route cs4349_routes[] = { | ||
221 | {"DAC Playback", NULL, "OutputA"}, | ||
222 | {"DAC Playback", NULL, "OutputB"}, | ||
223 | |||
224 | {"OutputA", NULL, "HiFi DAC"}, | ||
225 | {"OutputB", NULL, "HiFi DAC"}, | ||
226 | }; | ||
227 | |||
228 | #define CS4349_PCM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ | ||
229 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ | ||
230 | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ | ||
231 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ | ||
232 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ | ||
233 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \ | ||
234 | SNDRV_PCM_FMTBIT_S32_LE) | ||
235 | |||
236 | #define CS4349_PCM_RATES SNDRV_PCM_RATE_8000_192000 | ||
237 | |||
238 | static const struct snd_soc_dai_ops cs4349_dai_ops = { | ||
239 | .hw_params = cs4349_pcm_hw_params, | ||
240 | .set_fmt = cs4349_set_dai_fmt, | ||
241 | .digital_mute = cs4349_digital_mute, | ||
242 | }; | ||
243 | |||
244 | static struct snd_soc_dai_driver cs4349_dai = { | ||
245 | .name = "cs4349_hifi", | ||
246 | .playback = { | ||
247 | .stream_name = "DAC Playback", | ||
248 | .channels_min = 1, | ||
249 | .channels_max = 2, | ||
250 | .rates = CS4349_PCM_RATES, | ||
251 | .formats = CS4349_PCM_FORMATS, | ||
252 | }, | ||
253 | .ops = &cs4349_dai_ops, | ||
254 | .symmetric_rates = 1, | ||
255 | }; | ||
256 | |||
257 | static struct snd_soc_codec_driver soc_codec_dev_cs4349 = { | ||
258 | .controls = cs4349_snd_controls, | ||
259 | .num_controls = ARRAY_SIZE(cs4349_snd_controls), | ||
260 | |||
261 | .dapm_widgets = cs4349_dapm_widgets, | ||
262 | .num_dapm_widgets = ARRAY_SIZE(cs4349_dapm_widgets), | ||
263 | .dapm_routes = cs4349_routes, | ||
264 | .num_dapm_routes = ARRAY_SIZE(cs4349_routes), | ||
265 | }; | ||
266 | |||
267 | static struct regmap_config cs4349_regmap = { | ||
268 | .reg_bits = 8, | ||
269 | .val_bits = 8, | ||
270 | |||
271 | .max_register = CS4349_NUMREGS, | ||
272 | .reg_defaults = cs4349_reg_defaults, | ||
273 | .num_reg_defaults = ARRAY_SIZE(cs4349_reg_defaults), | ||
274 | .readable_reg = cs4349_readable_register, | ||
275 | .cache_type = REGCACHE_RBTREE, | ||
276 | }; | ||
277 | |||
278 | static int cs4349_i2c_probe(struct i2c_client *client, | ||
279 | const struct i2c_device_id *id) | ||
280 | { | ||
281 | struct cs4349_private *cs4349; | ||
282 | struct cs4349_platform_data *pdata = dev_get_platdata(&client->dev); | ||
283 | int ret = 0; | ||
284 | |||
285 | cs4349 = devm_kzalloc(&client->dev, sizeof(*cs4349), GFP_KERNEL); | ||
286 | if (!cs4349) | ||
287 | return -ENOMEM; | ||
288 | |||
289 | cs4349->regmap = devm_regmap_init_i2c(client, &cs4349_regmap); | ||
290 | if (IS_ERR(cs4349->regmap)) { | ||
291 | ret = PTR_ERR(cs4349->regmap); | ||
292 | dev_err(&client->dev, "regmap_init() failed: %d\n", ret); | ||
293 | return ret; | ||
294 | } | ||
295 | |||
296 | if (pdata) | ||
297 | cs4349->pdata = *pdata; | ||
298 | |||
299 | /* Reset the Device */ | ||
300 | cs4349->reset_gpio = devm_gpiod_get_optional(&client->dev, | ||
301 | "reset", GPIOD_OUT_LOW); | ||
302 | if (IS_ERR(cs4349->reset_gpio)) | ||
303 | return PTR_ERR(cs4349->reset_gpio); | ||
304 | |||
305 | if (cs4349->reset_gpio) | ||
306 | gpiod_set_value_cansleep(cs4349->reset_gpio, 1); | ||
307 | |||
308 | i2c_set_clientdata(client, cs4349); | ||
309 | |||
310 | return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4349, | ||
311 | &cs4349_dai, 1); | ||
312 | } | ||
313 | |||
314 | static int cs4349_i2c_remove(struct i2c_client *client) | ||
315 | { | ||
316 | struct cs4349_private *cs4349 = i2c_get_clientdata(client); | ||
317 | |||
318 | snd_soc_unregister_codec(&client->dev); | ||
319 | |||
320 | /* Hold down reset */ | ||
321 | if (cs4349->reset_gpio) | ||
322 | gpiod_set_value_cansleep(cs4349->reset_gpio, 0); | ||
323 | |||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | #ifdef CONFIG_PM | ||
328 | static int cs4349_runtime_suspend(struct device *dev) | ||
329 | { | ||
330 | struct cs4349_private *cs4349 = dev_get_drvdata(dev); | ||
331 | struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); | ||
332 | int ret; | ||
333 | |||
334 | ret = snd_soc_update_bits(rtd->codec, CS4349_MISC, PWR_DWN, 1); | ||
335 | if (ret < 0) | ||
336 | return ret; | ||
337 | |||
338 | regcache_cache_only(cs4349->regmap, true); | ||
339 | |||
340 | /* Hold down reset */ | ||
341 | if (cs4349->reset_gpio) | ||
342 | gpiod_set_value_cansleep(cs4349->reset_gpio, 0); | ||
343 | |||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | static int cs4349_runtime_resume(struct device *dev) | ||
348 | { | ||
349 | struct cs4349_private *cs4349 = dev_get_drvdata(dev); | ||
350 | struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); | ||
351 | int ret; | ||
352 | |||
353 | ret = snd_soc_update_bits(rtd->codec, CS4349_MISC, PWR_DWN, 0); | ||
354 | if (ret < 0) | ||
355 | return ret; | ||
356 | |||
357 | if (cs4349->reset_gpio) | ||
358 | gpiod_set_value_cansleep(cs4349->reset_gpio, 1); | ||
359 | |||
360 | regcache_cache_only(cs4349->regmap, false); | ||
361 | regcache_sync(cs4349->regmap); | ||
362 | |||
363 | return 0; | ||
364 | } | ||
365 | #endif | ||
366 | |||
367 | static const struct dev_pm_ops cs4349_runtime_pm = { | ||
368 | SET_RUNTIME_PM_OPS(cs4349_runtime_suspend, cs4349_runtime_resume, | ||
369 | NULL) | ||
370 | }; | ||
371 | |||
372 | static const struct of_device_id cs4349_of_match[] = { | ||
373 | { .compatible = "cirrus,cs4349", }, | ||
374 | {}, | ||
375 | }; | ||
376 | |||
377 | MODULE_DEVICE_TABLE(of, cs4349_of_match); | ||
378 | |||
379 | static const struct i2c_device_id cs4349_i2c_id[] = { | ||
380 | {"cs4349", 0}, | ||
381 | {} | ||
382 | }; | ||
383 | |||
384 | MODULE_DEVICE_TABLE(i2c, cs4349_i2c_id); | ||
385 | |||
386 | static struct i2c_driver cs4349_i2c_driver = { | ||
387 | .driver = { | ||
388 | .name = "cs4349", | ||
389 | .owner = THIS_MODULE, | ||
390 | .of_match_table = cs4349_of_match, | ||
391 | }, | ||
392 | .id_table = cs4349_i2c_id, | ||
393 | .probe = cs4349_i2c_probe, | ||
394 | .remove = cs4349_i2c_remove, | ||
395 | }; | ||
396 | |||
397 | module_i2c_driver(cs4349_i2c_driver); | ||
398 | |||
399 | MODULE_AUTHOR("Tim Howe <tim.howe@cirrus.com>"); | ||
400 | MODULE_DESCRIPTION("Cirrus Logic CS4349 ALSA SoC Codec Driver"); | ||
401 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/codecs/cs4349.h b/sound/soc/codecs/cs4349.h new file mode 100644 index 000000000000..3884a8907d23 --- /dev/null +++ b/sound/soc/codecs/cs4349.h | |||
@@ -0,0 +1,146 @@ | |||
1 | /* | ||
2 | * ALSA SoC CS4349 codec driver | ||
3 | * | ||
4 | * Copyright 2015 Cirrus Logic, Inc. | ||
5 | * | ||
6 | * Author: Tim Howe <Tim.Howe@cirrus.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #ifndef __CS4349_H__ | ||
20 | #define __CS4349_H__ | ||
21 | |||
22 | struct cs4349_platform_data { | ||
23 | |||
24 | /* GPIO for Reset */ | ||
25 | unsigned int gpio_nreset; | ||
26 | |||
27 | }; | ||
28 | |||
29 | /* CS4349 registers addresses */ | ||
30 | #define CS4349_CHIPID 0x01 /* Device and Rev ID, Read Only */ | ||
31 | #define CS4349_MODE 0x02 /* Mode Control */ | ||
32 | #define CS4349_VMI 0x03 /* Volume, Mixing, Inversion Control */ | ||
33 | #define CS4349_MUTE 0x04 /* Mute Control */ | ||
34 | #define CS4349_VOLA 0x05 /* DAC Channel A Volume Control */ | ||
35 | #define CS4349_VOLB 0x06 /* DAC Channel B Volume Control */ | ||
36 | #define CS4349_RMPFLT 0x07 /* Ramp and Filter Control */ | ||
37 | #define CS4349_MISC 0x08 /* Power Down,Freeze Control,Pop Stop*/ | ||
38 | |||
39 | #define CS4349_FIRSTREG 0x01 | ||
40 | #define CS4349_LASTREG 0x08 | ||
41 | #define CS4349_NUMREGS (CS4349_LASTREG - CS4349_FIRSTREG + 1) | ||
42 | #define CS4349_I2C_INCR 0x80 | ||
43 | |||
44 | |||
45 | /* Device and Revision ID */ | ||
46 | #define CS4349_REVA 0xF0 /* Rev A */ | ||
47 | #define CS4349_REVB 0xF1 /* Rev B */ | ||
48 | #define CS4349_REVC2 0xFF /* Rev C2 */ | ||
49 | |||
50 | |||
51 | /* PDN_DONE Poll Maximum | ||
52 | * If soft ramp is set it will take much longer to power down | ||
53 | * the system. | ||
54 | */ | ||
55 | #define PDN_POLL_MAX 900 | ||
56 | |||
57 | |||
58 | /* Bitfield Definitions */ | ||
59 | |||
60 | /* CS4349_MODE */ | ||
61 | /* (Digital Interface Format, De-Emphasis Control, Functional Mode */ | ||
62 | #define DIF2 (1 << 6) | ||
63 | #define DIF1 (1 << 5) | ||
64 | #define DIF0 (1 << 4) | ||
65 | #define DEM1 (1 << 3) | ||
66 | #define DEM0 (1 << 2) | ||
67 | #define FM1 (1 << 1) | ||
68 | #define DIF_LEFT_JST 0x00 | ||
69 | #define DIF_I2S 0x01 | ||
70 | #define DIF_RGHT_JST16 0x02 | ||
71 | #define DIF_RGHT_JST24 0x03 | ||
72 | #define DIF_TDM0 0x04 | ||
73 | #define DIF_TDM1 0x05 | ||
74 | #define DIF_TDM2 0x06 | ||
75 | #define DIF_TDM3 0x07 | ||
76 | #define DIF_MASK 0x70 | ||
77 | #define MODE_FORMAT(x) (((x)&7)<<4) | ||
78 | #define DEM_MASK 0x0C | ||
79 | #define NO_DEM 0x00 | ||
80 | #define DEM_441 0x04 | ||
81 | #define DEM_48K 0x08 | ||
82 | #define DEM_32K 0x0C | ||
83 | #define FM_AUTO 0x00 | ||
84 | #define FM_SNGL 0x01 | ||
85 | #define FM_DBL 0x02 | ||
86 | #define FM_QUAD 0x03 | ||
87 | #define FM_SNGL_MIN 30000 | ||
88 | #define FM_SNGL_MAX 54000 | ||
89 | #define FM_DBL_MAX 108000 | ||
90 | #define FM_QUAD_MAX 216000 | ||
91 | #define FM_MASK 0x03 | ||
92 | |||
93 | /* CS4349_VMI (VMI = Volume, Mixing and Inversion Controls) */ | ||
94 | #define VOLBISA (1 << 7) | ||
95 | #define VOLAISB (1 << 7) | ||
96 | /* INVERT_A only available for Left Jstfd, Right Jstfd16 and Right Jstfd24 */ | ||
97 | #define INVERT_A (1 << 6) | ||
98 | /* INVERT_B only available for Left Jstfd, Right Jstfd16 and Right Jstfd24 */ | ||
99 | #define INVERT_B (1 << 5) | ||
100 | #define ATAPI3 (1 << 3) | ||
101 | #define ATAPI2 (1 << 2) | ||
102 | #define ATAPI1 (1 << 1) | ||
103 | #define ATAPI0 (1 << 0) | ||
104 | #define MUTEAB 0x00 | ||
105 | #define MUTEA_RIGHTB 0x01 | ||
106 | #define MUTEA_LEFTB 0x02 | ||
107 | #define MUTEA_SUMLRDIV2B 0x03 | ||
108 | #define RIGHTA_MUTEB 0x04 | ||
109 | #define RIGHTA_RIGHTB 0x05 | ||
110 | #define RIGHTA_LEFTB 0x06 | ||
111 | #define RIGHTA_SUMLRDIV2B 0x07 | ||
112 | #define LEFTA_MUTEB 0x08 | ||
113 | #define LEFTA_RIGHTB 0x09 /* Default */ | ||
114 | #define LEFTA_LEFTB 0x0A | ||
115 | #define LEFTA_SUMLRDIV2B 0x0B | ||
116 | #define SUMLRDIV2A_MUTEB 0x0C | ||
117 | #define SUMLRDIV2A_RIGHTB 0x0D | ||
118 | #define SUMLRDIV2A_LEFTB 0x0E | ||
119 | #define SUMLRDIV2_AB 0x0F | ||
120 | #define CHMIX_MASK 0x0F | ||
121 | |||
122 | /* CS4349_MUTE */ | ||
123 | #define AUTOMUTE (1 << 7) | ||
124 | #define MUTEC_AB (1 << 5) | ||
125 | #define MUTE_A (1 << 4) | ||
126 | #define MUTE_B (1 << 3) | ||
127 | #define MUTE_AB_MASK 0x18 | ||
128 | |||
129 | /* CS4349_RMPFLT (Ramp and Filter Control) */ | ||
130 | #define SCZ1 (1 << 7) | ||
131 | #define SCZ0 (1 << 6) | ||
132 | #define RMP_UP (1 << 5) | ||
133 | #define RMP_DN (1 << 4) | ||
134 | #define FILT_SEL (1 << 2) | ||
135 | #define IMMDT_CHNG 0x31 | ||
136 | #define ZEROCRSS 0x71 | ||
137 | #define SOFT_RMP 0xB1 | ||
138 | #define SFTRMP_ZEROCRSS 0xF1 | ||
139 | #define SR_ZC_MASK 0xC0 | ||
140 | |||
141 | /* CS4349_MISC */ | ||
142 | #define PWR_DWN (1 << 7) | ||
143 | #define FREEZE (1 << 5) | ||
144 | #define POPG_EN (1 << 4) | ||
145 | |||
146 | #endif /* __CS4349_H__ */ | ||