1 | /* |
2 | * ASoC codec driver for spear platform |
3 | * |
4 | * sound/soc/codecs/sta529.c -- spear ALSA Soc codec driver |
5 | * |
6 | * Copyright (C) 2012 ST Microelectronics |
7 | * Rajeev Kumar <rajeevkumar.linux@gmail.com> |
8 | * |
9 | * This file is licensed under the terms of the GNU General Public |
10 | * License version 2. This program is licensed "as is" without any |
11 | * warranty of any kind, whether express or implied. |
12 | */ |
13 | |
14 | #include <linux/clk.h> |
15 | #include <linux/init.h> |
16 | #include <linux/i2c.h> |
17 | #include <linux/io.h> |
18 | #include <linux/module.h> |
19 | #include <linux/moduleparam.h> |
20 | #include <linux/pm.h> |
21 | #include <linux/regmap.h> |
22 | #include <linux/slab.h> |
23 | |
24 | #include <sound/core.h> |
25 | #include <sound/initval.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/tlv.h> |
31 | |
32 | /* STA529 Register offsets */ |
33 | #define STA529_FFXCFG0 0x00 |
34 | #define STA529_FFXCFG1 0x01 |
35 | #define STA529_MVOL 0x02 |
36 | #define STA529_LVOL 0x03 |
37 | #define STA529_RVOL 0x04 |
38 | #define STA529_TTF0 0x05 |
39 | #define STA529_TTF1 0x06 |
40 | #define STA529_TTP0 0x07 |
41 | #define STA529_TTP1 0x08 |
42 | #define STA529_S2PCFG0 0x0A |
43 | #define STA529_S2PCFG1 0x0B |
44 | #define STA529_P2SCFG0 0x0C |
45 | #define STA529_P2SCFG1 0x0D |
46 | #define STA529_PLLCFG0 0x14 |
47 | #define STA529_PLLCFG1 0x15 |
48 | #define STA529_PLLCFG2 0x16 |
49 | #define STA529_PLLCFG3 0x17 |
50 | #define STA529_PLLPFE 0x18 |
51 | #define STA529_PLLST 0x19 |
52 | #define STA529_ADCCFG 0x1E /*mic_select*/ |
53 | #define STA529_CKOCFG 0x1F |
54 | #define STA529_MISC 0x20 |
55 | #define STA529_PADST0 0x21 |
56 | #define STA529_PADST1 0x22 |
57 | #define STA529_FFXST 0x23 |
58 | #define STA529_PWMIN1 0x2D |
59 | #define STA529_PWMIN2 0x2E |
60 | #define STA529_POWST 0x32 |
61 | |
62 | #define STA529_MAX_REGISTER 0x32 |
63 | |
64 | #define STA529_RATES (SNDRV_PCM_RATE_8000 | \ |
65 | SNDRV_PCM_RATE_11025 | \ |
66 | SNDRV_PCM_RATE_16000 | \ |
67 | SNDRV_PCM_RATE_22050 | \ |
68 | SNDRV_PCM_RATE_32000 | \ |
69 | SNDRV_PCM_RATE_44100 | \ |
70 | SNDRV_PCM_RATE_48000) |
71 | |
72 | #define STA529_FORMAT (SNDRV_PCM_FMTBIT_S16_LE | \ |
73 | SNDRV_PCM_FMTBIT_S24_LE | \ |
74 | SNDRV_PCM_FMTBIT_S32_LE) |
75 | #define S2PC_VALUE 0x98 |
76 | #define CLOCK_OUT 0x60 |
77 | #define DATA_FORMAT_MSK 0x0E |
78 | #define LEFT_J_DATA_FORMAT 0x00 |
79 | #define I2S_DATA_FORMAT 0x02 |
80 | #define RIGHT_J_DATA_FORMAT 0x04 |
81 | #define CODEC_MUTE_VAL 0x80 |
82 | |
83 | #define POWER_CNTLMSAK 0x40 |
84 | #define POWER_STDBY 0x40 |
85 | #define FFX_MASK 0x80 |
86 | #define FFX_OFF 0x80 |
87 | #define POWER_UP 0x00 |
88 | #define FFX_CLK_ENB 0x01 |
89 | #define FFX_CLK_DIS 0x00 |
90 | #define FFX_CLK_MSK 0x01 |
91 | #define PLAY_FREQ_RANGE_MSK 0x70 |
92 | #define CAP_FREQ_RANGE_MSK 0x0C |
93 | #define PDATA_LEN_MSK 0xC0 |
94 | #define BCLK_TO_FS_MSK 0x30 |
95 | #define AUDIO_MUTE_MSK 0x80 |
96 | |
97 | static const struct reg_default sta529_reg_defaults[] = { |
98 | { 0, 0x35 }, /* R0 - FFX Configuration reg 0 */ |
99 | { 1, 0xc8 }, /* R1 - FFX Configuration reg 1 */ |
100 | { 2, 0x50 }, /* R2 - Master Volume */ |
101 | { 3, 0x00 }, /* R3 - Left Volume */ |
102 | { 4, 0x00 }, /* R4 - Right Volume */ |
103 | { 10, 0xb2 }, /* R10 - S2P Config Reg 0 */ |
104 | { 11, 0x41 }, /* R11 - S2P Config Reg 1 */ |
105 | { 12, 0x92 }, /* R12 - P2S Config Reg 0 */ |
106 | { 13, 0x41 }, /* R13 - P2S Config Reg 1 */ |
107 | { 30, 0xd2 }, /* R30 - ADC Config Reg */ |
108 | { 31, 0x40 }, /* R31 - clock Out Reg */ |
109 | { 32, 0x21 }, /* R32 - Misc Register */ |
110 | }; |
111 | |
112 | struct sta529 { |
113 | struct regmap *regmap; |
114 | }; |
115 | |
116 | static bool sta529_readable(struct device *dev, unsigned int reg) |
117 | { |
118 | switch (reg) { |
119 | |
120 | case STA529_FFXCFG0: |
121 | case STA529_FFXCFG1: |
122 | case STA529_MVOL: |
123 | case STA529_LVOL: |
124 | case STA529_RVOL: |
125 | case STA529_S2PCFG0: |
126 | case STA529_S2PCFG1: |
127 | case STA529_P2SCFG0: |
128 | case STA529_P2SCFG1: |
129 | case STA529_ADCCFG: |
130 | case STA529_CKOCFG: |
131 | case STA529_MISC: |
132 | return true; |
133 | default: |
134 | return false; |
135 | } |
136 | } |
137 | |
138 | |
139 | static const char *pwm_mode_text[] = { "Binary" , "Headphone" , "Ternary" , |
140 | "Phase-shift" }; |
141 | |
142 | static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -9150, 50, 0); |
143 | static const DECLARE_TLV_DB_SCALE(master_vol_tlv, -12750, 50, 0); |
144 | static SOC_ENUM_SINGLE_DECL(pwm_src, STA529_FFXCFG1, 4, pwm_mode_text); |
145 | |
146 | static const struct snd_kcontrol_new sta529_snd_controls[] = { |
147 | SOC_DOUBLE_R_TLV("Digital Playback Volume" , STA529_LVOL, STA529_RVOL, 0, |
148 | 127, 0, out_gain_tlv), |
149 | SOC_SINGLE_TLV("Master Playback Volume" , STA529_MVOL, 0, 127, 1, |
150 | master_vol_tlv), |
151 | SOC_ENUM("PWM Select" , pwm_src), |
152 | }; |
153 | |
154 | static int sta529_set_bias_level(struct snd_soc_component *component, enum |
155 | snd_soc_bias_level level) |
156 | { |
157 | struct sta529 *sta529 = snd_soc_component_get_drvdata(c: component); |
158 | |
159 | switch (level) { |
160 | case SND_SOC_BIAS_ON: |
161 | case SND_SOC_BIAS_PREPARE: |
162 | snd_soc_component_update_bits(component, STA529_FFXCFG0, POWER_CNTLMSAK, |
163 | POWER_UP); |
164 | snd_soc_component_update_bits(component, STA529_MISC, FFX_CLK_MSK, |
165 | FFX_CLK_ENB); |
166 | break; |
167 | case SND_SOC_BIAS_STANDBY: |
168 | if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) |
169 | regcache_sync(map: sta529->regmap); |
170 | snd_soc_component_update_bits(component, STA529_FFXCFG0, |
171 | POWER_CNTLMSAK, POWER_STDBY); |
172 | /* Making FFX output to zero */ |
173 | snd_soc_component_update_bits(component, STA529_FFXCFG0, FFX_MASK, |
174 | FFX_OFF); |
175 | snd_soc_component_update_bits(component, STA529_MISC, FFX_CLK_MSK, |
176 | FFX_CLK_DIS); |
177 | break; |
178 | case SND_SOC_BIAS_OFF: |
179 | break; |
180 | } |
181 | |
182 | return 0; |
183 | |
184 | } |
185 | |
186 | static int sta529_hw_params(struct snd_pcm_substream *substream, |
187 | struct snd_pcm_hw_params *params, |
188 | struct snd_soc_dai *dai) |
189 | { |
190 | struct snd_soc_component *component = dai->component; |
191 | int pdata, play_freq_val, record_freq_val; |
192 | int bclk_to_fs_ratio; |
193 | |
194 | switch (params_width(p: params)) { |
195 | case 16: |
196 | pdata = 1; |
197 | bclk_to_fs_ratio = 0; |
198 | break; |
199 | case 24: |
200 | pdata = 2; |
201 | bclk_to_fs_ratio = 1; |
202 | break; |
203 | case 32: |
204 | pdata = 3; |
205 | bclk_to_fs_ratio = 2; |
206 | break; |
207 | default: |
208 | dev_err(component->dev, "Unsupported format\n" ); |
209 | return -EINVAL; |
210 | } |
211 | |
212 | switch (params_rate(p: params)) { |
213 | case 8000: |
214 | case 11025: |
215 | play_freq_val = 0; |
216 | record_freq_val = 2; |
217 | break; |
218 | case 16000: |
219 | case 22050: |
220 | play_freq_val = 1; |
221 | record_freq_val = 0; |
222 | break; |
223 | |
224 | case 32000: |
225 | case 44100: |
226 | case 48000: |
227 | play_freq_val = 2; |
228 | record_freq_val = 0; |
229 | break; |
230 | default: |
231 | dev_err(component->dev, "Unsupported rate\n" ); |
232 | return -EINVAL; |
233 | } |
234 | |
235 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
236 | snd_soc_component_update_bits(component, STA529_S2PCFG1, PDATA_LEN_MSK, |
237 | val: pdata << 6); |
238 | snd_soc_component_update_bits(component, STA529_S2PCFG1, BCLK_TO_FS_MSK, |
239 | val: bclk_to_fs_ratio << 4); |
240 | snd_soc_component_update_bits(component, STA529_MISC, PLAY_FREQ_RANGE_MSK, |
241 | val: play_freq_val << 4); |
242 | } else { |
243 | snd_soc_component_update_bits(component, STA529_P2SCFG1, PDATA_LEN_MSK, |
244 | val: pdata << 6); |
245 | snd_soc_component_update_bits(component, STA529_P2SCFG1, BCLK_TO_FS_MSK, |
246 | val: bclk_to_fs_ratio << 4); |
247 | snd_soc_component_update_bits(component, STA529_MISC, CAP_FREQ_RANGE_MSK, |
248 | val: record_freq_val << 2); |
249 | } |
250 | |
251 | return 0; |
252 | } |
253 | |
254 | static int sta529_mute(struct snd_soc_dai *dai, int mute, int direction) |
255 | { |
256 | u8 val = 0; |
257 | |
258 | if (mute) |
259 | val |= CODEC_MUTE_VAL; |
260 | |
261 | snd_soc_component_update_bits(component: dai->component, STA529_FFXCFG0, AUDIO_MUTE_MSK, val); |
262 | |
263 | return 0; |
264 | } |
265 | |
266 | static int sta529_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt) |
267 | { |
268 | struct snd_soc_component *component = codec_dai->component; |
269 | u8 mode = 0; |
270 | |
271 | /* interface format */ |
272 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
273 | case SND_SOC_DAIFMT_LEFT_J: |
274 | mode = LEFT_J_DATA_FORMAT; |
275 | break; |
276 | case SND_SOC_DAIFMT_I2S: |
277 | mode = I2S_DATA_FORMAT; |
278 | break; |
279 | case SND_SOC_DAIFMT_RIGHT_J: |
280 | mode = RIGHT_J_DATA_FORMAT; |
281 | break; |
282 | default: |
283 | return -EINVAL; |
284 | } |
285 | |
286 | snd_soc_component_update_bits(component, STA529_S2PCFG0, DATA_FORMAT_MSK, val: mode); |
287 | |
288 | return 0; |
289 | } |
290 | |
291 | static const struct snd_soc_dai_ops sta529_dai_ops = { |
292 | .hw_params = sta529_hw_params, |
293 | .set_fmt = sta529_set_dai_fmt, |
294 | .mute_stream = sta529_mute, |
295 | .no_capture_mute = 1, |
296 | }; |
297 | |
298 | static struct snd_soc_dai_driver sta529_dai = { |
299 | .name = "sta529-audio" , |
300 | .playback = { |
301 | .stream_name = "Playback" , |
302 | .channels_min = 2, |
303 | .channels_max = 2, |
304 | .rates = STA529_RATES, |
305 | .formats = STA529_FORMAT, |
306 | }, |
307 | .capture = { |
308 | .stream_name = "Capture" , |
309 | .channels_min = 2, |
310 | .channels_max = 2, |
311 | .rates = STA529_RATES, |
312 | .formats = STA529_FORMAT, |
313 | }, |
314 | .ops = &sta529_dai_ops, |
315 | }; |
316 | |
317 | static const struct snd_soc_component_driver sta529_component_driver = { |
318 | .set_bias_level = sta529_set_bias_level, |
319 | .controls = sta529_snd_controls, |
320 | .num_controls = ARRAY_SIZE(sta529_snd_controls), |
321 | .suspend_bias_off = 1, |
322 | .idle_bias_on = 1, |
323 | .use_pmdown_time = 1, |
324 | .endianness = 1, |
325 | }; |
326 | |
327 | static const struct regmap_config sta529_regmap = { |
328 | .reg_bits = 8, |
329 | .val_bits = 8, |
330 | |
331 | .max_register = STA529_MAX_REGISTER, |
332 | .readable_reg = sta529_readable, |
333 | |
334 | .cache_type = REGCACHE_MAPLE, |
335 | .reg_defaults = sta529_reg_defaults, |
336 | .num_reg_defaults = ARRAY_SIZE(sta529_reg_defaults), |
337 | }; |
338 | |
339 | static int sta529_i2c_probe(struct i2c_client *i2c) |
340 | { |
341 | struct sta529 *sta529; |
342 | int ret; |
343 | |
344 | sta529 = devm_kzalloc(dev: &i2c->dev, size: sizeof(struct sta529), GFP_KERNEL); |
345 | if (!sta529) |
346 | return -ENOMEM; |
347 | |
348 | sta529->regmap = devm_regmap_init_i2c(i2c, &sta529_regmap); |
349 | if (IS_ERR(ptr: sta529->regmap)) { |
350 | ret = PTR_ERR(ptr: sta529->regmap); |
351 | dev_err(&i2c->dev, "Failed to allocate regmap: %d\n" , ret); |
352 | return ret; |
353 | } |
354 | |
355 | i2c_set_clientdata(client: i2c, data: sta529); |
356 | |
357 | ret = devm_snd_soc_register_component(dev: &i2c->dev, |
358 | component_driver: &sta529_component_driver, dai_drv: &sta529_dai, num_dai: 1); |
359 | if (ret != 0) |
360 | dev_err(&i2c->dev, "Failed to register CODEC: %d\n" , ret); |
361 | |
362 | return ret; |
363 | } |
364 | |
365 | static const struct i2c_device_id sta529_i2c_id[] = { |
366 | { "sta529" , 0 }, |
367 | { } |
368 | }; |
369 | MODULE_DEVICE_TABLE(i2c, sta529_i2c_id); |
370 | |
371 | static const struct of_device_id sta529_of_match[] = { |
372 | { .compatible = "st,sta529" , }, |
373 | { } |
374 | }; |
375 | MODULE_DEVICE_TABLE(of, sta529_of_match); |
376 | |
377 | static struct i2c_driver sta529_i2c_driver = { |
378 | .driver = { |
379 | .name = "sta529" , |
380 | .of_match_table = sta529_of_match, |
381 | }, |
382 | .probe = sta529_i2c_probe, |
383 | .id_table = sta529_i2c_id, |
384 | }; |
385 | |
386 | module_i2c_driver(sta529_i2c_driver); |
387 | |
388 | MODULE_DESCRIPTION("ASoC STA529 codec driver" ); |
389 | MODULE_AUTHOR("Rajeev Kumar <rajeevkumar.linux@gmail.com>" ); |
390 | MODULE_LICENSE("GPL" ); |
391 | |