aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/da7210.c
diff options
context:
space:
mode:
authorKuninori Morimoto <morimoto.kuninori@renesas.com>2009-12-13 23:21:56 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-12-16 12:31:04 -0500
commit98615454f66175e923f239ab1d1bd85cd618363e (patch)
tree39643e632d173e8b47b66ed7cc2d399adfc1c6c0 /sound/soc/codecs/da7210.c
parent7c4e6492205b677a5786b85bcf72ce7c8f4adf15 (diff)
ASoC: Add DA7210 codec device support for ALSA
This original driver was created by Dialog Semiconductor, and cleanuped by Kuninori Morimoto. Special thanks to David Chen. This became very simple ASoC codec driver, and it is tested by EcoVec24 board. Signed-off-by: David Chen <Dajun.chen@diasemi.com> Signed-off-by: Kuninori Morimoto <morimoto.kuninori@renesas.com> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs/da7210.c')
-rw-r--r--sound/soc/codecs/da7210.c586
1 files changed, 586 insertions, 0 deletions
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
new file mode 100644
index 000000000000..14f5f344b1d5
--- /dev/null
+++ b/sound/soc/codecs/da7210.c
@@ -0,0 +1,586 @@
1/*
2 * DA7210 ALSA Soc codec driver
3 *
4 * Copyright (c) 2009 Dialog Semiconductor
5 * Written by David Chen <Dajun.chen@diasemi.com>
6 *
7 * Copyright (C) 2009 Renesas Solutions Corp.
8 * Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com>
9 *
10 * Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version.
16 */
17
18#include <linux/module.h>
19#include <linux/moduleparam.h>
20#include <linux/kernel.h>
21#include <linux/init.h>
22#include <linux/delay.h>
23#include <linux/pm.h>
24#include <linux/i2c.h>
25#include <linux/platform_device.h>
26#include <sound/core.h>
27#include <sound/pcm.h>
28#include <sound/pcm_params.h>
29#include <sound/soc.h>
30#include <sound/soc-dapm.h>
31#include <sound/tlv.h>
32#include <sound/initval.h>
33#include <asm/div64.h>
34
35#include "da7210.h"
36
37/* DA7210 register space */
38#define DA7210_STATUS 0x02
39#define DA7210_STARTUP1 0x03
40#define DA7210_MIC_L 0x07
41#define DA7210_MIC_R 0x08
42#define DA7210_INMIX_L 0x0D
43#define DA7210_INMIX_R 0x0E
44#define DA7210_ADC_HPF 0x0F
45#define DA7210_ADC 0x10
46#define DA7210_DAC_HPF 0x14
47#define DA7210_DAC_L 0x15
48#define DA7210_DAC_R 0x16
49#define DA7210_DAC_SEL 0x17
50#define DA7210_OUTMIX_L 0x1C
51#define DA7210_OUTMIX_R 0x1D
52#define DA7210_HP_L_VOL 0x21
53#define DA7210_HP_R_VOL 0x22
54#define DA7210_HP_CFG 0x23
55#define DA7210_DAI_SRC_SEL 0x25
56#define DA7210_DAI_CFG1 0x26
57#define DA7210_DAI_CFG3 0x28
58#define DA7210_PLL_DIV3 0x2B
59#define DA7210_PLL 0x2C
60
61/* STARTUP1 bit fields */
62#define DA7210_SC_MST_EN (1 << 0)
63
64/* MIC_L bit fields */
65#define DA7210_MICBIAS_EN (1 << 6)
66#define DA7210_MIC_L_EN (1 << 7)
67
68/* MIC_R bit fields */
69#define DA7210_MIC_R_EN (1 << 7)
70
71/* INMIX_L bit fields */
72#define DA7210_IN_L_EN (1 << 7)
73
74/* INMIX_R bit fields */
75#define DA7210_IN_R_EN (1 << 7)
76
77/* ADC_HPF bit fields */
78#define DA7210_ADC_VOICE_EN (1 << 7)
79
80/* ADC bit fields */
81#define DA7210_ADC_L_EN (1 << 3)
82#define DA7210_ADC_R_EN (1 << 7)
83
84/* DAC_SEL bit fields */
85#define DA7210_DAC_L_SRC_DAI_L (4 << 0)
86#define DA7210_DAC_L_EN (1 << 3)
87#define DA7210_DAC_R_SRC_DAI_R (5 << 4)
88#define DA7210_DAC_R_EN (1 << 7)
89
90/* OUTMIX_L bit fields */
91#define DA7210_OUT_L_EN (1 << 7)
92
93/* OUTMIX_R bit fields */
94#define DA7210_OUT_R_EN (1 << 7)
95
96/* HP_CFG bit fields */
97#define DA7210_HP_2CAP_MODE (1 << 1)
98#define DA7210_HP_SENSE_EN (1 << 2)
99#define DA7210_HP_L_EN (1 << 3)
100#define DA7210_HP_MODE (1 << 6)
101#define DA7210_HP_R_EN (1 << 7)
102
103/* DAI_SRC_SEL bit fields */
104#define DA7210_DAI_OUT_L_SRC (6 << 0)
105#define DA7210_DAI_OUT_R_SRC (7 << 4)
106
107/* DAI_CFG1 bit fields */
108#define DA7210_DAI_WORD_S16_LE (0 << 0)
109#define DA7210_DAI_WORD_S24_LE (2 << 0)
110#define DA7210_DAI_FLEN_64BIT (1 << 2)
111#define DA7210_DAI_MODE_MASTER (1 << 7)
112
113/* DAI_CFG3 bit fields */
114#define DA7210_DAI_FORMAT_I2SMODE (0 << 0)
115#define DA7210_DAI_OE (1 << 3)
116#define DA7210_DAI_EN (1 << 7)
117
118/*PLL_DIV3 bit fields */
119#define DA7210_MCLK_RANGE_10_20_MHZ (1 << 4)
120#define DA7210_PLL_BYP (1 << 6)
121
122/* PLL bit fields */
123#define DA7210_PLL_FS_48000 (11 << 0)
124
125#define DA7210_VERSION "0.0.1"
126
127/* Codec private data */
128struct da7210_priv {
129 struct snd_soc_codec codec;
130};
131
132static struct snd_soc_codec *da7210_codec;
133
134/*
135 * Register cache
136 */
137static const u8 da7210_reg[] = {
138 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R0 - R7 */
139 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, /* R8 - RF */
140 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x10, 0x54, /* R10 - R17 */
141 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R18 - R1F */
142 0x00, 0x00, 0x00, 0x02, 0x00, 0x76, 0x00, 0x00, /* R20 - R27 */
143 0x04, 0x00, 0x00, 0x30, 0x2A, 0x00, 0x40, 0x00, /* R28 - R2F */
144 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, /* R30 - R37 */
145 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, /* R38 - R3F */
146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R40 - R4F */
147 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R48 - R4F */
148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R50 - R57 */
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R58 - R5F */
150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R60 - R67 */
151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R68 - R6F */
152 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* R70 - R77 */
153 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x54, 0x00, /* R78 - R7F */
154 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, /* R80 - R87 */
155 0x00, /* R88 */
156};
157
158/*
159 * Read da7210 register cache
160 */
161static inline u32 da7210_read_reg_cache(struct snd_soc_codec *codec, u32 reg)
162{
163 u8 *cache = codec->reg_cache;
164 BUG_ON(reg > ARRAY_SIZE(da7210_reg));
165 return cache[reg];
166}
167
168/*
169 * Write to the da7210 register space
170 */
171static int da7210_write(struct snd_soc_codec *codec, u32 reg, u32 value)
172{
173 u8 *cache = codec->reg_cache;
174 u8 data[2];
175
176 BUG_ON(codec->volatile_register);
177
178 data[0] = reg & 0xff;
179 data[1] = value & 0xff;
180
181 if (reg >= codec->reg_cache_size)
182 return -EIO;
183
184 if (2 != codec->hw_write(codec->control_data, data, 2))
185 return -EIO;
186
187 cache[reg] = value;
188 return 0;
189}
190
191/*
192 * Read from the da7210 register space.
193 */
194static inline u32 da7210_read(struct snd_soc_codec *codec, u32 reg)
195{
196 if (DA7210_STATUS == reg)
197 return i2c_smbus_read_byte_data(codec->control_data, reg);
198
199 return da7210_read_reg_cache(codec, reg);
200}
201
202static int da7210_startup(struct snd_pcm_substream *substream,
203 struct snd_soc_dai *dai)
204{
205 int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
206 struct snd_soc_codec *codec = dai->codec;
207
208 if (is_play) {
209 /* PlayBack Volume 40 */
210 snd_soc_update_bits(codec, DA7210_HP_L_VOL, 0x3F, 40);
211 snd_soc_update_bits(codec, DA7210_HP_R_VOL, 0x3F, 40);
212
213 /* Enable Out */
214 snd_soc_update_bits(codec, DA7210_OUTMIX_L, 0x1F, 0x10);
215 snd_soc_update_bits(codec, DA7210_OUTMIX_R, 0x1F, 0x10);
216
217 } else {
218 /* Volume 7 */
219 snd_soc_update_bits(codec, DA7210_MIC_L, 0x7, 0x7);
220 snd_soc_update_bits(codec, DA7210_MIC_R, 0x7, 0x7);
221
222 /* Enable Mic */
223 snd_soc_update_bits(codec, DA7210_INMIX_L, 0x1F, 0x1);
224 snd_soc_update_bits(codec, DA7210_INMIX_R, 0x1F, 0x1);
225 }
226
227 return 0;
228}
229
230/*
231 * Set PCM DAI word length.
232 */
233static int da7210_hw_params(struct snd_pcm_substream *substream,
234 struct snd_pcm_hw_params *params,
235 struct snd_soc_dai *dai)
236{
237 struct snd_soc_pcm_runtime *rtd = substream->private_data;
238 struct snd_soc_device *socdev = rtd->socdev;
239 struct snd_soc_codec *codec = socdev->card->codec;
240 u32 dai_cfg1;
241 u32 reg, mask;
242
243 /* set DAI source to Left and Right ADC */
244 da7210_write(codec, DA7210_DAI_SRC_SEL,
245 DA7210_DAI_OUT_R_SRC | DA7210_DAI_OUT_L_SRC);
246
247 /* Enable DAI */
248 da7210_write(codec, DA7210_DAI_CFG3, DA7210_DAI_OE | DA7210_DAI_EN);
249
250 dai_cfg1 = 0xFC & da7210_read(codec, DA7210_DAI_CFG1);
251
252 switch (params_format(params)) {
253 case SNDRV_PCM_FORMAT_S16_LE:
254 dai_cfg1 |= DA7210_DAI_WORD_S16_LE;
255 break;
256 case SNDRV_PCM_FORMAT_S24_LE:
257 dai_cfg1 |= DA7210_DAI_WORD_S24_LE;
258 break;
259 default:
260 return -EINVAL;
261 }
262
263 da7210_write(codec, DA7210_DAI_CFG1, dai_cfg1);
264
265 /* FIXME
266 *
267 * It support 48K only now
268 */
269 switch (params_rate(params)) {
270 case 48000:
271 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
272 reg = DA7210_DAC_HPF;
273 mask = DA7210_DAC_VOICE_EN;
274 } else {
275 reg = DA7210_ADC_HPF;
276 mask = DA7210_ADC_VOICE_EN;
277 }
278 break;
279 default:
280 return -EINVAL;
281 }
282
283 snd_soc_update_bits(codec, reg, mask, 0);
284
285 return 0;
286}
287
288/*
289 * Set DAI mode and Format
290 */
291static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
292{
293 struct snd_soc_codec *codec = codec_dai->codec;
294 u32 dai_cfg1;
295 u32 dai_cfg3;
296
297 dai_cfg1 = 0x7f & da7210_read(codec, DA7210_DAI_CFG1);
298 dai_cfg3 = 0xfc & da7210_read(codec, DA7210_DAI_CFG3);
299
300 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
301 case SND_SOC_DAIFMT_CBM_CFM:
302 dai_cfg1 |= DA7210_DAI_MODE_MASTER;
303 break;
304 default:
305 return -EINVAL;
306 }
307
308 /* FIXME
309 *
310 * It support I2S only now
311 */
312 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
313 case SND_SOC_DAIFMT_I2S:
314 dai_cfg3 |= DA7210_DAI_FORMAT_I2SMODE;
315 break;
316 default:
317 return -EINVAL;
318 }
319
320 /* FIXME
321 *
322 * It support 64bit data transmission only now
323 */
324 dai_cfg1 |= DA7210_DAI_FLEN_64BIT;
325
326 da7210_write(codec, DA7210_DAI_CFG1, dai_cfg1);
327 da7210_write(codec, DA7210_DAI_CFG3, dai_cfg3);
328
329 return 0;
330}
331
332#define DA7210_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
333
334/* DAI operations */
335static struct snd_soc_dai_ops da7210_dai_ops = {
336 .startup = da7210_startup,
337 .hw_params = da7210_hw_params,
338 .set_fmt = da7210_set_dai_fmt,
339};
340
341struct snd_soc_dai da7210_dai = {
342 .name = "DA7210 IIS",
343 .id = 0,
344 /* playback capabilities */
345 .playback = {
346 .stream_name = "Playback",
347 .channels_min = 1,
348 .channels_max = 2,
349 .rates = SNDRV_PCM_RATE_8000_96000,
350 .formats = DA7210_FORMATS,
351 },
352 /* capture capabilities */
353 .capture = {
354 .stream_name = "Capture",
355 .channels_min = 1,
356 .channels_max = 2,
357 .rates = SNDRV_PCM_RATE_8000_96000,
358 .formats = DA7210_FORMATS,
359 },
360 .ops = &da7210_dai_ops,
361};
362EXPORT_SYMBOL_GPL(da7210_dai);
363
364/*
365 * Initialize the DA7210 driver
366 * register the mixer and dsp interfaces with the kernel
367 */
368static int da7210_init(struct da7210_priv *da7210)
369{
370 struct snd_soc_codec *codec = &da7210->codec;
371 int ret = 0;
372
373 if (da7210_codec) {
374 dev_err(codec->dev, "Another da7210 is registered\n");
375 return -EINVAL;
376 }
377
378 mutex_init(&codec->mutex);
379 INIT_LIST_HEAD(&codec->dapm_widgets);
380 INIT_LIST_HEAD(&codec->dapm_paths);
381
382 codec->private_data = da7210;
383 codec->name = "DA7210";
384 codec->owner = THIS_MODULE;
385 codec->read = da7210_read;
386 codec->write = da7210_write;
387 codec->dai = &da7210_dai;
388 codec->num_dai = 1;
389 codec->hw_write = (hw_write_t)i2c_master_send;
390 codec->reg_cache_size = ARRAY_SIZE(da7210_reg);
391 codec->reg_cache = kmemdup(da7210_reg,
392 sizeof(da7210_reg), GFP_KERNEL);
393
394 if (!codec->reg_cache)
395 return -ENOMEM;
396
397 da7210_dai.dev = codec->dev;
398 da7210_codec = codec;
399
400 ret = snd_soc_register_codec(codec);
401 if (ret) {
402 dev_err(codec->dev, "Failed to register CODEC: %d\n", ret);
403 goto init_err;
404 }
405
406 ret = snd_soc_register_dai(&da7210_dai);
407 if (ret) {
408 dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
409 goto init_err;
410 }
411
412 /* FIXME
413 *
414 * This driver use fixed value here
415 */
416
417 /*
418 * ADC settings
419 */
420
421 /* Enable Left & Right MIC PGA and Mic Bias */
422 da7210_write(codec, DA7210_MIC_L, DA7210_MIC_L_EN | DA7210_MICBIAS_EN);
423 da7210_write(codec, DA7210_MIC_R, DA7210_MIC_R_EN);
424
425 /* Enable Left and Right input PGA */
426 da7210_write(codec, DA7210_INMIX_L, DA7210_IN_L_EN);
427 da7210_write(codec, DA7210_INMIX_R, DA7210_IN_R_EN);
428
429 /* Enable Left and Right ADC */
430 da7210_write(codec, DA7210_ADC, DA7210_ADC_L_EN | DA7210_ADC_R_EN);
431
432 /*
433 * DAC settings
434 */
435
436 /* Enable Left and Right DAC */
437 da7210_write(codec, DA7210_DAC_SEL,
438 DA7210_DAC_L_SRC_DAI_L | DA7210_DAC_L_EN |
439 DA7210_DAC_R_SRC_DAI_R | DA7210_DAC_R_EN);
440
441 /* Enable Left and Right out PGA */
442 da7210_write(codec, DA7210_OUTMIX_L, DA7210_OUT_L_EN);
443 da7210_write(codec, DA7210_OUTMIX_R, DA7210_OUT_R_EN);
444
445 /* Enable Left and Right HeadPhone PGA */
446 da7210_write(codec, DA7210_HP_CFG,
447 DA7210_HP_2CAP_MODE | DA7210_HP_SENSE_EN |
448 DA7210_HP_L_EN | DA7210_HP_MODE | DA7210_HP_R_EN);
449
450 /* Diable PLL and bypass it */
451 da7210_write(codec, DA7210_PLL, DA7210_PLL_FS_48000);
452
453 /* Bypass PLL and set MCLK freq rang to 10-20MHz */
454 da7210_write(codec, DA7210_PLL_DIV3,
455 DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP);
456
457 /* Activate all enabled subsystem */
458 da7210_write(codec, DA7210_STARTUP1, DA7210_SC_MST_EN);
459
460 return ret;
461
462init_err:
463 kfree(codec->reg_cache);
464 codec->reg_cache = NULL;
465
466 return ret;
467
468}
469
470#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
471static int da7210_i2c_probe(struct i2c_client *i2c,
472 const struct i2c_device_id *id)
473{
474 struct da7210_priv *da7210;
475 struct snd_soc_codec *codec;
476 int ret;
477
478 da7210 = kzalloc(sizeof(struct da7210_priv), GFP_KERNEL);
479 if (!da7210)
480 return -ENOMEM;
481
482 codec = &da7210->codec;
483 codec->dev = &i2c->dev;
484
485 i2c_set_clientdata(i2c, da7210);
486 codec->control_data = i2c;
487
488 ret = da7210_init(da7210);
489 if (ret < 0)
490 pr_err("Failed to initialise da7210 audio codec\n");
491
492 return ret;
493}
494
495static int da7210_i2c_remove(struct i2c_client *client)
496{
497 struct da7210_priv *da7210 = i2c_get_clientdata(client);
498
499 snd_soc_unregister_dai(&da7210_dai);
500 kfree(da7210->codec.reg_cache);
501 kfree(da7210);
502 da7210_codec = NULL;
503
504 return 0;
505}
506
507static const struct i2c_device_id da7210_i2c_id[] = {
508 { "da7210", 0 },
509 { }
510};
511MODULE_DEVICE_TABLE(i2c, da7210_i2c_id);
512
513/* I2C codec control layer */
514static struct i2c_driver da7210_i2c_driver = {
515 .driver = {
516 .name = "DA7210 I2C Codec",
517 .owner = THIS_MODULE,
518 },
519 .probe = da7210_i2c_probe,
520 .remove = __devexit_p(da7210_i2c_remove),
521 .id_table = da7210_i2c_id,
522};
523#endif
524
525static int da7210_probe(struct platform_device *pdev)
526{
527 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
528 struct snd_soc_codec *codec;
529 int ret;
530
531 if (!da7210_codec) {
532 dev_err(&pdev->dev, "Codec device not registered\n");
533 return -ENODEV;
534 }
535
536 socdev->card->codec = da7210_codec;
537 codec = da7210_codec;
538
539 /* Register pcms */
540 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
541 if (ret < 0)
542 goto pcm_err;
543
544 dev_info(&pdev->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
545
546pcm_err:
547 return ret;
548}
549
550static int da7210_remove(struct platform_device *pdev)
551{
552 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
553
554 snd_soc_free_pcms(socdev);
555 snd_soc_dapm_free(socdev);
556
557 return 0;
558}
559
560struct snd_soc_codec_device soc_codec_dev_da7210 = {
561 .probe = da7210_probe,
562 .remove = da7210_remove,
563};
564EXPORT_SYMBOL_GPL(soc_codec_dev_da7210);
565
566static int __init da7210_modinit(void)
567{
568 int ret = 0;
569#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
570 ret = i2c_add_driver(&da7210_i2c_driver);
571#endif
572 return ret;
573}
574module_init(da7210_modinit);
575
576static void __exit da7210_exit(void)
577{
578#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
579 i2c_del_driver(&da7210_i2c_driver);
580#endif
581}
582module_exit(da7210_exit);
583
584MODULE_DESCRIPTION("ASoC DA7210 driver");
585MODULE_AUTHOR("David Chen, Kuninori Morimoto");
586MODULE_LICENSE("GPL");