blob: fe929467f93c6b3ea68206ef9d6e270bdcbcd4eb [file] [log] [blame]
Graeme Gregory74930bb2007-05-14 11:03:52 +02001/*
2 * neo1973_wm8753.c -- SoC audio for Neo1973
3 *
4 * Copyright 2007 Wolfson Microelectronics PLC.
5 * Author: Graeme Gregory
6 * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
Graeme Gregory74930bb2007-05-14 11:03:52 +020013 */
14
15#include <linux/module.h>
16#include <linux/moduleparam.h>
17#include <linux/timer.h>
18#include <linux/interrupt.h>
19#include <linux/platform_device.h>
20#include <linux/i2c.h>
Graeme Gregory74930bb2007-05-14 11:03:52 +020021#include <sound/core.h>
22#include <sound/pcm.h>
23#include <sound/soc.h>
Mike Montoura2e31a52008-06-11 13:47:14 +010024#include <sound/tlv.h>
Graeme Gregory74930bb2007-05-14 11:03:52 +020025
Mark Brownfb2aa072008-10-08 13:02:20 +010026#include <asm/mach-types.h>
Russell Kinga09e64f2008-08-05 16:14:15 +010027#include <mach/regs-clock.h>
28#include <mach/regs-gpio.h>
29#include <mach/hardware.h>
Graeme Gregory8ba02ac2008-04-30 20:24:54 +020030#include <linux/io.h>
Russell Kinga09e64f2008-08-05 16:14:15 +010031#include <mach/spi-gpio.h>
Harald Welteaa9673c2007-12-19 15:37:49 +010032
Ben Dooks8150bc82009-03-04 00:49:26 +000033#include <plat/regs-iis.h>
Harald Welteaa9673c2007-12-19 15:37:49 +010034
Graeme Gregory74930bb2007-05-14 11:03:52 +020035#include "../codecs/wm8753.h"
36#include "lm4857.h"
Jassi Brar4b640cf2010-11-22 15:35:57 +090037#include "dma.h"
Graeme Gregory74930bb2007-05-14 11:03:52 +020038#include "s3c24xx-i2s.h"
39
Mark Brown87506542008-11-18 20:50:34 +000040static struct snd_soc_card neo1973;
Graeme Gregory74930bb2007-05-14 11:03:52 +020041static struct i2c_client *i2c;
42
43static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
44 struct snd_pcm_hw_params *params)
45{
46 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +000047 struct snd_soc_dai *codec_dai = rtd->codec_dai;
48 struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
Graeme Gregory74930bb2007-05-14 11:03:52 +020049 unsigned int pll_out = 0, bclk = 0;
50 int ret = 0;
51 unsigned long iis_clkrate;
52
Mark Brownee7d4762009-03-06 18:04:34 +000053 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +020054
Graeme Gregory74930bb2007-05-14 11:03:52 +020055 iis_clkrate = s3c24xx_i2s_get_clockrate();
56
57 switch (params_rate(params)) {
58 case 8000:
59 case 16000:
60 pll_out = 12288000;
61 break;
62 case 48000:
63 bclk = WM8753_BCLK_DIV_4;
64 pll_out = 12288000;
65 break;
66 case 96000:
67 bclk = WM8753_BCLK_DIV_2;
68 pll_out = 12288000;
69 break;
70 case 11025:
71 bclk = WM8753_BCLK_DIV_16;
72 pll_out = 11289600;
73 break;
74 case 22050:
75 bclk = WM8753_BCLK_DIV_8;
76 pll_out = 11289600;
77 break;
78 case 44100:
79 bclk = WM8753_BCLK_DIV_4;
80 pll_out = 11289600;
81 break;
82 case 88200:
83 bclk = WM8753_BCLK_DIV_2;
84 pll_out = 11289600;
85 break;
86 }
87
88 /* set codec DAI configuration */
Liam Girdwood64105cf2008-07-08 13:19:18 +010089 ret = snd_soc_dai_set_fmt(codec_dai,
Graeme Gregory74930bb2007-05-14 11:03:52 +020090 SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
91 SND_SOC_DAIFMT_CBM_CFM);
92 if (ret < 0)
93 return ret;
94
95 /* set cpu DAI configuration */
Liam Girdwood64105cf2008-07-08 13:19:18 +010096 ret = snd_soc_dai_set_fmt(cpu_dai,
Graeme Gregory74930bb2007-05-14 11:03:52 +020097 SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
98 SND_SOC_DAIFMT_CBM_CFM);
99 if (ret < 0)
100 return ret;
101
102 /* set the codec system clock for DAC and ADC */
Liam Girdwood64105cf2008-07-08 13:19:18 +0100103 ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,
Graeme Gregory74930bb2007-05-14 11:03:52 +0200104 SND_SOC_CLOCK_IN);
105 if (ret < 0)
106 return ret;
107
108 /* set MCLK division for sample rate */
Liam Girdwood64105cf2008-07-08 13:19:18 +0100109 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
Graeme Gregory8ba02ac2008-04-30 20:24:54 +0200110 S3C2410_IISMOD_32FS);
Graeme Gregory74930bb2007-05-14 11:03:52 +0200111 if (ret < 0)
112 return ret;
113
114 /* set codec BCLK division for sample rate */
Liam Girdwood64105cf2008-07-08 13:19:18 +0100115 ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
Graeme Gregory74930bb2007-05-14 11:03:52 +0200116 if (ret < 0)
117 return ret;
118
119 /* set prescaler division for sample rate */
Liam Girdwood64105cf2008-07-08 13:19:18 +0100120 ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
Graeme Gregory8ba02ac2008-04-30 20:24:54 +0200121 S3C24XX_PRESCALE(4, 4));
Graeme Gregory74930bb2007-05-14 11:03:52 +0200122 if (ret < 0)
123 return ret;
124
125 /* codec PLL input is PCLK/4 */
Mark Brown85488032009-09-05 18:52:16 +0100126 ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
Graeme Gregory74930bb2007-05-14 11:03:52 +0200127 iis_clkrate / 4, pll_out);
128 if (ret < 0)
129 return ret;
130
131 return 0;
132}
133
134static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
135{
136 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000137 struct snd_soc_dai *codec_dai = rtd->codec_dai;
Graeme Gregory74930bb2007-05-14 11:03:52 +0200138
Mark Brownee7d4762009-03-06 18:04:34 +0000139 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200140
Graeme Gregory74930bb2007-05-14 11:03:52 +0200141 /* disable the PLL */
Takashi Iwai140318a2009-10-01 08:40:32 +0200142 return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
Graeme Gregory74930bb2007-05-14 11:03:52 +0200143}
144
145/*
146 * Neo1973 WM8753 HiFi DAI opserations.
147 */
148static struct snd_soc_ops neo1973_hifi_ops = {
149 .hw_params = neo1973_hifi_hw_params,
150 .hw_free = neo1973_hifi_hw_free,
151};
152
153static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
154 struct snd_pcm_hw_params *params)
155{
156 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000157 struct snd_soc_dai *codec_dai = rtd->codec_dai;
Graeme Gregory74930bb2007-05-14 11:03:52 +0200158 unsigned int pcmdiv = 0;
159 int ret = 0;
160 unsigned long iis_clkrate;
161
Mark Brownee7d4762009-03-06 18:04:34 +0000162 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200163
Graeme Gregory74930bb2007-05-14 11:03:52 +0200164 iis_clkrate = s3c24xx_i2s_get_clockrate();
165
166 if (params_rate(params) != 8000)
167 return -EINVAL;
168 if (params_channels(params) != 1)
169 return -EINVAL;
170
171 pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
172
173 /* todo: gg check mode (DSP_B) against CSR datasheet */
174 /* set codec DAI configuration */
Liam Girdwood64105cf2008-07-08 13:19:18 +0100175 ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
Graeme Gregory74930bb2007-05-14 11:03:52 +0200176 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
177 if (ret < 0)
178 return ret;
179
180 /* set the codec system clock for DAC and ADC */
Liam Girdwood64105cf2008-07-08 13:19:18 +0100181 ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
Graeme Gregory74930bb2007-05-14 11:03:52 +0200182 SND_SOC_CLOCK_IN);
183 if (ret < 0)
184 return ret;
185
186 /* set codec PCM division for sample rate */
Liam Girdwood64105cf2008-07-08 13:19:18 +0100187 ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
Graeme Gregory74930bb2007-05-14 11:03:52 +0200188 if (ret < 0)
189 return ret;
190
Andrea Gelminifa2eb002010-10-16 15:19:20 +0200191 /* configure and enable PLL for 12.288MHz output */
Takashi Iwai140318a2009-10-01 08:40:32 +0200192 ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
Graeme Gregory74930bb2007-05-14 11:03:52 +0200193 iis_clkrate / 4, 12288000);
194 if (ret < 0)
195 return ret;
196
197 return 0;
198}
199
200static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
201{
202 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000203 struct snd_soc_dai *codec_dai = rtd->codec_dai;
Graeme Gregory74930bb2007-05-14 11:03:52 +0200204
Mark Brownee7d4762009-03-06 18:04:34 +0000205 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200206
Graeme Gregory74930bb2007-05-14 11:03:52 +0200207 /* disable the PLL */
Takashi Iwai140318a2009-10-01 08:40:32 +0200208 return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
Graeme Gregory74930bb2007-05-14 11:03:52 +0200209}
210
211static struct snd_soc_ops neo1973_voice_ops = {
212 .hw_params = neo1973_voice_hw_params,
213 .hw_free = neo1973_voice_hw_free,
214};
215
Graeme Gregory74930bb2007-05-14 11:03:52 +0200216static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0};
217
218static void lm4857_write_regs(void)
219{
Mark Brownee7d4762009-03-06 18:04:34 +0000220 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200221
Graeme Gregory74930bb2007-05-14 11:03:52 +0200222 if (i2c_master_send(i2c, lm4857_regs, 4) != 4)
223 printk(KERN_ERR "lm4857: i2c write failed\n");
224}
225
226static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
227 struct snd_ctl_elem_value *ucontrol)
228{
Mark Brown236e6722009-06-10 13:55:34 +0100229 struct soc_mixer_control *mc =
230 (struct soc_mixer_control *)kcontrol->private_value;
231 int reg = mc->reg;
232 int shift = mc->shift;
233 int mask = mc->max;
Graeme Gregory74930bb2007-05-14 11:03:52 +0200234
Mark Brownee7d4762009-03-06 18:04:34 +0000235 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200236
Graeme Gregory74930bb2007-05-14 11:03:52 +0200237 ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
238 return 0;
239}
240
241static int lm4857_set_reg(struct snd_kcontrol *kcontrol,
242 struct snd_ctl_elem_value *ucontrol)
243{
Mark Brown236e6722009-06-10 13:55:34 +0100244 struct soc_mixer_control *mc =
245 (struct soc_mixer_control *)kcontrol->private_value;
246 int reg = mc->reg;
247 int shift = mc->shift;
248 int mask = mc->max;
Graeme Gregory74930bb2007-05-14 11:03:52 +0200249
Graeme Gregory8ba02ac2008-04-30 20:24:54 +0200250 if (((lm4857_regs[reg] >> shift) & mask) ==
Graeme Gregory74930bb2007-05-14 11:03:52 +0200251 ucontrol->value.integer.value[0])
252 return 0;
253
Graeme Gregory8ba02ac2008-04-30 20:24:54 +0200254 lm4857_regs[reg] &= ~(mask << shift);
Graeme Gregory74930bb2007-05-14 11:03:52 +0200255 lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift;
256 lm4857_write_regs();
257 return 1;
258}
259
260static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
261 struct snd_ctl_elem_value *ucontrol)
262{
263 u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
264
Mark Brownee7d4762009-03-06 18:04:34 +0000265 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200266
Graeme Gregory74930bb2007-05-14 11:03:52 +0200267 if (value)
268 value -= 5;
269
270 ucontrol->value.integer.value[0] = value;
271 return 0;
272}
273
274static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
275 struct snd_ctl_elem_value *ucontrol)
276{
277 u8 value = ucontrol->value.integer.value[0];
278
Mark Brownee7d4762009-03-06 18:04:34 +0000279 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200280
Graeme Gregory74930bb2007-05-14 11:03:52 +0200281 if (value)
282 value += 5;
283
284 if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value)
285 return 0;
286
287 lm4857_regs[LM4857_CTRL] &= 0xF0;
288 lm4857_regs[LM4857_CTRL] |= value;
289 lm4857_write_regs();
290 return 1;
291}
292
293static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
294 SND_SOC_DAPM_LINE("Audio Out", NULL),
295 SND_SOC_DAPM_LINE("GSM Line Out", NULL),
296 SND_SOC_DAPM_LINE("GSM Line In", NULL),
297 SND_SOC_DAPM_MIC("Headset Mic", NULL),
298 SND_SOC_DAPM_MIC("Call Mic", NULL),
299};
300
301
Mark Brown8f3112d2008-05-13 14:58:03 +0200302static const struct snd_soc_dapm_route dapm_routes[] = {
Graeme Gregory74930bb2007-05-14 11:03:52 +0200303
304 /* Connections to the lm4857 amp */
305 {"Audio Out", NULL, "LOUT1"},
306 {"Audio Out", NULL, "ROUT1"},
307
308 /* Connections to the GSM Module */
309 {"GSM Line Out", NULL, "MONO1"},
310 {"GSM Line Out", NULL, "MONO2"},
311 {"RXP", NULL, "GSM Line In"},
312 {"RXN", NULL, "GSM Line In"},
313
314 /* Connections to Headset */
315 {"MIC1", NULL, "Mic Bias"},
316 {"Mic Bias", NULL, "Headset Mic"},
317
318 /* Call Mic */
319 {"MIC2", NULL, "Mic Bias"},
320 {"MIC2N", NULL, "Mic Bias"},
321 {"Mic Bias", NULL, "Call Mic"},
322
323 /* Connect the ALC pins */
324 {"ACIN", NULL, "ACOP"},
Graeme Gregory74930bb2007-05-14 11:03:52 +0200325};
326
327static const char *lm4857_mode[] = {
328 "Off",
329 "Call Speaker",
330 "Stereo Speakers",
331 "Stereo Speakers + Headphones",
332 "Headphones"
333};
334
335static const struct soc_enum lm4857_mode_enum[] = {
336 SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode),
337};
338
Mike Montoura2e31a52008-06-11 13:47:14 +0100339static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0);
340static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0);
341
Graeme Gregory74930bb2007-05-14 11:03:52 +0200342static const struct snd_kcontrol_new wm8753_neo1973_controls[] = {
Lars-Peter Clausen004c52e2011-03-07 08:04:54 +0100343 SOC_DAPM_PIN_SWITCH("Audio Out"),
344 SOC_DAPM_PIN_SWITCH("GSM Line Out"),
345 SOC_DAPM_PIN_SWITCH("GSM Line In"),
346 SOC_DAPM_PIN_SWITCH("Headset Mic"),
347 SOC_DAPM_PIN_SWITCH("Call Mic"),
348
Mike Montoura2e31a52008-06-11 13:47:14 +0100349 SOC_SINGLE_EXT_TLV("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
350 lm4857_get_reg, lm4857_set_reg, stereo_tlv),
351 SOC_SINGLE_EXT_TLV("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
352 lm4857_get_reg, lm4857_set_reg, stereo_tlv),
353 SOC_SINGLE_EXT_TLV("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
354 lm4857_get_reg, lm4857_set_reg, mono_tlv),
Graeme Gregory74930bb2007-05-14 11:03:52 +0200355 SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0],
356 lm4857_get_mode, lm4857_set_mode),
Graeme Gregory74930bb2007-05-14 11:03:52 +0200357 SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0,
358 lm4857_get_reg, lm4857_set_reg),
359 SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0,
360 lm4857_get_reg, lm4857_set_reg),
361 SOC_SINGLE_EXT("Amp Fast Wakeup Playback Switch", LM4857_CTRL, 5, 1, 0,
362 lm4857_get_reg, lm4857_set_reg),
363 SOC_SINGLE_EXT("Amp Earpiece 6dB Playback Switch", LM4857_CTRL, 4, 1, 0,
364 lm4857_get_reg, lm4857_set_reg),
365};
366
367/*
368 * This is an example machine initialisation for a wm8753 connected to a
369 * neo1973 II. It is missing logic to detect hp/mic insertions and logic
370 * to re-route the audio in such an event.
371 */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000372static int neo1973_wm8753_init(struct snd_soc_pcm_runtime *rtd)
Graeme Gregory74930bb2007-05-14 11:03:52 +0200373{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000374 struct snd_soc_codec *codec = rtd->codec;
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200375 struct snd_soc_dapm_context *dapm = &codec->dapm;
Philipp Zabeleb5f6d752009-03-12 11:07:54 +0100376 int err;
Graeme Gregory74930bb2007-05-14 11:03:52 +0200377
Mark Brownee7d4762009-03-06 18:04:34 +0000378 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200379
Graeme Gregory74930bb2007-05-14 11:03:52 +0200380 /* set up NC codec pins */
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200381 snd_soc_dapm_nc_pin(dapm, "LOUT2");
382 snd_soc_dapm_nc_pin(dapm, "ROUT2");
383 snd_soc_dapm_nc_pin(dapm, "OUT3");
384 snd_soc_dapm_nc_pin(dapm, "OUT4");
385 snd_soc_dapm_nc_pin(dapm, "LINE1");
386 snd_soc_dapm_nc_pin(dapm, "LINE2");
Graeme Gregory74930bb2007-05-14 11:03:52 +0200387
Graeme Gregory74930bb2007-05-14 11:03:52 +0200388 /* Add neo1973 specific widgets */
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200389 snd_soc_dapm_new_controls(dapm, wm8753_dapm_widgets,
Mark Brown8f3112d2008-05-13 14:58:03 +0200390 ARRAY_SIZE(wm8753_dapm_widgets));
Graeme Gregory74930bb2007-05-14 11:03:52 +0200391
Jonas Bonne8089942008-10-01 18:17:12 +0100392 /* set endpoints to default mode */
Lars-Peter Clausen004c52e2011-03-07 08:04:54 +0100393 snd_soc_dapm_disable_pin(dapm, "Audio Out");
394 snd_soc_dapm_disable_pin(dapm, "GSM Line Out");
395 snd_soc_dapm_disable_pin(dapm, "GSM Line In");
396 snd_soc_dapm_disable_pin(dapm, "Headset Mic");
397 snd_soc_dapm_disable_pin(dapm, "Call Mic");
Jonas Bonne8089942008-10-01 18:17:12 +0100398
Graeme Gregory74930bb2007-05-14 11:03:52 +0200399 /* add neo1973 specific controls */
Philipp Zabeleb5f6d752009-03-12 11:07:54 +0100400 err = snd_soc_add_controls(codec, wm8753_neo1973_controls,
401 ARRAY_SIZE(8753_neo1973_controls));
402 if (err < 0)
403 return err;
Graeme Gregory74930bb2007-05-14 11:03:52 +0200404
Mark Brown8f3112d2008-05-13 14:58:03 +0200405 /* set up neo1973 specific audio routes */
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200406 err = snd_soc_dapm_add_routes(dapm, dapm_routes,
Mark Brown8f3112d2008-05-13 14:58:03 +0200407 ARRAY_SIZE(dapm_routes));
Graeme Gregory74930bb2007-05-14 11:03:52 +0200408
Liam Girdwoodce6120c2010-11-05 15:53:46 +0200409 snd_soc_dapm_sync(dapm);
Graeme Gregory74930bb2007-05-14 11:03:52 +0200410 return 0;
411}
412
413/*
414 * BT Codec DAI
415 */
Liam Girdwood1992a6f2008-07-07 16:08:24 +0100416static struct snd_soc_dai bt_dai = {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000417 .name = "bluetooth-dai",
Graeme Gregory74930bb2007-05-14 11:03:52 +0200418 .playback = {
419 .channels_min = 1,
420 .channels_max = 1,
421 .rates = SNDRV_PCM_RATE_8000,
422 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
423 .capture = {
424 .channels_min = 1,
425 .channels_max = 1,
426 .rates = SNDRV_PCM_RATE_8000,
427 .formats = SNDRV_PCM_FMTBIT_S16_LE,},
428};
429
430static struct snd_soc_dai_link neo1973_dai[] = {
431{ /* Hifi Playback - for similatious use with voice below */
432 .name = "WM8753",
433 .stream_name = "WM8753 HiFi",
Jassi Brar58bb4072010-11-22 15:35:50 +0900434 .platform_name = "samsung-audio",
Lars-Peter Clausen518aa592011-01-24 22:12:42 +0100435 .cpu_dai_name = "s3c24xx-iis",
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000436 .codec_dai_name = "wm8753-hifi",
Lars-Peter Clausen81d7da52011-01-24 22:09:22 +0100437 .codec_name = "wm8753-codec.0-001a",
Graeme Gregory74930bb2007-05-14 11:03:52 +0200438 .init = neo1973_wm8753_init,
439 .ops = &neo1973_hifi_ops,
440},
441{ /* Voice via BT */
442 .name = "Bluetooth",
443 .stream_name = "Voice",
Jassi Brar58bb4072010-11-22 15:35:50 +0900444 .platform_name = "samsung-audio",
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000445 .cpu_dai_name = "bluetooth-dai",
446 .codec_dai_name = "wm8753-voice",
Lars-Peter Clausen81d7da52011-01-24 22:09:22 +0100447 .codec_name = "wm8753-codec.0-001a",
Graeme Gregory74930bb2007-05-14 11:03:52 +0200448 .ops = &neo1973_voice_ops,
449},
450};
451
Mark Brown87506542008-11-18 20:50:34 +0000452static struct snd_soc_card neo1973 = {
Graeme Gregory74930bb2007-05-14 11:03:52 +0200453 .name = "neo1973",
454 .dai_link = neo1973_dai,
455 .num_links = ARRAY_SIZE(neo1973_dai),
456};
457
Jean Delvareb6471302008-09-01 17:46:57 +0200458static int lm4857_i2c_probe(struct i2c_client *client,
459 const struct i2c_device_id *id)
Graeme Gregory74930bb2007-05-14 11:03:52 +0200460{
Mark Brownee7d4762009-03-06 18:04:34 +0000461 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200462
Jonas Bonnf9d1ab32008-10-01 21:47:19 +0200463 i2c = client;
464
Graeme Gregory74930bb2007-05-14 11:03:52 +0200465 lm4857_write_regs();
Graeme Gregory74930bb2007-05-14 11:03:52 +0200466 return 0;
467}
468
Jean Delvareb6471302008-09-01 17:46:57 +0200469static int lm4857_i2c_remove(struct i2c_client *client)
Graeme Gregory74930bb2007-05-14 11:03:52 +0200470{
Mark Brownee7d4762009-03-06 18:04:34 +0000471 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200472
Jonas Bonnf9d1ab32008-10-01 21:47:19 +0200473 i2c = NULL;
474
Jean Delvareb6471302008-09-01 17:46:57 +0200475 return 0;
Graeme Gregory74930bb2007-05-14 11:03:52 +0200476}
477
Graeme Gregoryfd403dc2008-04-30 20:26:45 +0200478static u8 lm4857_state;
479
480static int lm4857_suspend(struct i2c_client *dev, pm_message_t state)
481{
Mark Brownee7d4762009-03-06 18:04:34 +0000482 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200483
Graeme Gregoryfd403dc2008-04-30 20:26:45 +0200484 dev_dbg(&dev->dev, "lm4857_suspend\n");
485 lm4857_state = lm4857_regs[LM4857_CTRL] & 0xf;
486 if (lm4857_state) {
487 lm4857_regs[LM4857_CTRL] &= 0xf0;
488 lm4857_write_regs();
489 }
490 return 0;
491}
492
493static int lm4857_resume(struct i2c_client *dev)
494{
Mark Brownee7d4762009-03-06 18:04:34 +0000495 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200496
Graeme Gregoryfd403dc2008-04-30 20:26:45 +0200497 if (lm4857_state) {
498 lm4857_regs[LM4857_CTRL] |= (lm4857_state & 0x0f);
499 lm4857_write_regs();
500 }
501 return 0;
502}
503
504static void lm4857_shutdown(struct i2c_client *dev)
505{
Mark Brownee7d4762009-03-06 18:04:34 +0000506 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200507
Graeme Gregoryfd403dc2008-04-30 20:26:45 +0200508 dev_dbg(&dev->dev, "lm4857_shutdown\n");
509 lm4857_regs[LM4857_CTRL] &= 0xf0;
510 lm4857_write_regs();
511}
512
Jean Delvareb6471302008-09-01 17:46:57 +0200513static const struct i2c_device_id lm4857_i2c_id[] = {
Mark Browndf20cf92008-09-24 11:57:27 +0100514 { "neo1973_lm4857", 0 },
Jean Delvareb6471302008-09-01 17:46:57 +0200515 { }
516};
517
Graeme Gregory74930bb2007-05-14 11:03:52 +0200518static struct i2c_driver lm4857_i2c_driver = {
519 .driver = {
520 .name = "LM4857 I2C Amp",
521 .owner = THIS_MODULE,
522 },
Graeme Gregoryfd403dc2008-04-30 20:26:45 +0200523 .suspend = lm4857_suspend,
524 .resume = lm4857_resume,
525 .shutdown = lm4857_shutdown,
Jean Delvareb6471302008-09-01 17:46:57 +0200526 .probe = lm4857_i2c_probe,
527 .remove = lm4857_i2c_remove,
528 .id_table = lm4857_i2c_id,
Graeme Gregory74930bb2007-05-14 11:03:52 +0200529};
530
531static struct platform_device *neo1973_snd_device;
532
533static int __init neo1973_init(void)
534{
535 int ret;
536
Mark Brownee7d4762009-03-06 18:04:34 +0000537 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200538
Mark Brownfb2aa072008-10-08 13:02:20 +0100539 if (!machine_is_neo1973_gta01()) {
540 printk(KERN_INFO
541 "Only GTA01 hardware supported by ASoC driver\n");
542 return -ENODEV;
543 }
544
Graeme Gregory74930bb2007-05-14 11:03:52 +0200545 neo1973_snd_device = platform_device_alloc("soc-audio", -1);
546 if (!neo1973_snd_device)
547 return -ENOMEM;
548
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000549 platform_set_drvdata(neo1973_snd_device, &neo1973);
Graeme Gregory74930bb2007-05-14 11:03:52 +0200550 ret = platform_device_add(neo1973_snd_device);
551
Jean Delvared2802892008-09-01 17:44:05 +0200552 if (ret) {
Graeme Gregory74930bb2007-05-14 11:03:52 +0200553 platform_device_put(neo1973_snd_device);
Jean Delvared2802892008-09-01 17:44:05 +0200554 return ret;
555 }
Graeme Gregory74930bb2007-05-14 11:03:52 +0200556
Jonas Bonnf9d1ab32008-10-01 21:47:19 +0200557 ret = i2c_add_driver(&lm4857_i2c_driver);
558
Jean Delvareb6471302008-09-01 17:46:57 +0200559 if (ret != 0)
Jean Delvared2802892008-09-01 17:44:05 +0200560 platform_device_unregister(neo1973_snd_device);
Graeme Gregory74930bb2007-05-14 11:03:52 +0200561
562 return ret;
563}
564
565static void __exit neo1973_exit(void)
566{
Mark Brownee7d4762009-03-06 18:04:34 +0000567 pr_debug("Entered %s\n", __func__);
Tim Niemeyer1894c592008-05-05 14:16:12 +0200568
Tim Niemeyer6b9a9b32008-04-22 17:10:23 +0200569 i2c_del_driver(&lm4857_i2c_driver);
Graeme Gregory74930bb2007-05-14 11:03:52 +0200570 platform_device_unregister(neo1973_snd_device);
571}
572
573module_init(neo1973_init);
574module_exit(neo1973_exit);
575
576/* Module information */
Graeme Gregory443590e2008-04-30 20:25:23 +0200577MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org");
Graeme Gregory74930bb2007-05-14 11:03:52 +0200578MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973");
579MODULE_LICENSE("GPL");