[xburst] Add 2.6.37 support
[openwrt.git] / target / linux / xburst / patches-2.6.35 / 060-jzcodec.patch
1 From 382d2274cfd8fc22064a33681e45668cfb6f91ad Mon Sep 17 00:00:00 2001
2 From: Lars-Peter Clausen <lars@metafoo.de>
3 Date: Sun, 1 Aug 2010 21:14:09 +0200
4 Subject: [PATCH] ASoC: Add JZ4740 codec driver
5
6 This patch adds support for the JZ4740 internal codec.
7
8 Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
9 Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
10 Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
11 ---
12  sound/soc/codecs/Kconfig  |    4 +
13  sound/soc/codecs/Makefile |    2 +
14  sound/soc/codecs/jz4740.c |  511 +++++++++++++++++++++++++++++++++++++++++++++
15  sound/soc/codecs/jz4740.h |   20 ++
16  4 files changed, 537 insertions(+), 0 deletions(-)
17  create mode 100644 sound/soc/codecs/jz4740.c
18  create mode 100644 sound/soc/codecs/jz4740.h
19
20 --- a/sound/soc/codecs/Kconfig
21 +++ b/sound/soc/codecs/Kconfig
22 @@ -23,6 +23,7 @@ config SND_SOC_ALL_CODECS
23         select SND_SOC_AK4671 if I2C
24         select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
25         select SND_SOC_CS4270 if I2C
26 +       select SND_SOC_JZ4740 if SOC_JZ4740
27         select SND_SOC_MAX9877 if I2C
28         select SND_SOC_DA7210 if I2C
29         select SND_SOC_PCM3008
30 @@ -138,6 +139,9 @@ config SND_SOC_CS4270_VD33_ERRATA
31  config SND_SOC_CX20442
32         tristate
33  
34 +config SND_SOC_JZ4740_CODEC
35 +       tristate
36 +
37  config SND_SOC_L3
38         tristate
39  
40 --- a/sound/soc/codecs/Makefile
41 +++ b/sound/soc/codecs/Makefile
42 @@ -56,6 +56,7 @@ snd-soc-wm9705-objs := wm9705.o
43  snd-soc-wm9712-objs := wm9712.o
44  snd-soc-wm9713-objs := wm9713.o
45  snd-soc-wm-hubs-objs := wm_hubs.o
46 +snd-soc-jz4740-codec-objs := jz4740.o
47  
48  # Amp
49  snd-soc-max9877-objs := max9877.o
50 @@ -78,6 +79,7 @@ obj-$(CONFIG_SND_SOC_CS4270)  += snd-soc-
51  obj-$(CONFIG_SND_SOC_CX20442)  += snd-soc-cx20442.o
52  obj-$(CONFIG_SND_SOC_DA7210)   += snd-soc-da7210.o
53  obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
54 +obj-$(CONFIG_SND_SOC_JZ4740_CODEC)     += snd-soc-jz4740-codec.o
55  obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
56  obj-$(CONFIG_SND_SOC_SPDIF)    += snd-soc-spdif.o
57  obj-$(CONFIG_SND_SOC_SSM2602)  += snd-soc-ssm2602.o
58 --- /dev/null
59 +++ b/sound/soc/codecs/jz4740.c
60 @@ -0,0 +1,511 @@
61 +/*
62 + * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
63 + *
64 + * This program is free software; you can redistribute it and/or modify
65 + * it under the terms of the GNU General Public License version 2 as
66 + * published by the Free Software Foundation.
67 + *
68 + *  You should have received a copy of the  GNU General Public License along
69 + *  with this program; if not, write  to the Free Software Foundation, Inc.,
70 + *  675 Mass Ave, Cambridge, MA 02139, USA.
71 + *
72 + */
73 +
74 +#include <linux/kernel.h>
75 +#include <linux/module.h>
76 +#include <linux/platform_device.h>
77 +#include <linux/slab.h>
78 +
79 +#include <linux/delay.h>
80 +
81 +#include <sound/core.h>
82 +#include <sound/pcm.h>
83 +#include <sound/pcm_params.h>
84 +#include <sound/initval.h>
85 +#include <sound/soc-dapm.h>
86 +#include <sound/soc.h>
87 +
88 +#define JZ4740_REG_CODEC_1 0x0
89 +#define JZ4740_REG_CODEC_2 0x1
90 +
91 +#define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
92 +#define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
93 +#define JZ4740_CODEC_1_SW1_ENABLE BIT(27)
94 +#define JZ4740_CODEC_1_ADC_ENABLE BIT(26)
95 +#define JZ4740_CODEC_1_SW2_ENABLE BIT(25)
96 +#define JZ4740_CODEC_1_DAC_ENABLE BIT(24)
97 +#define JZ4740_CODEC_1_VREF_DISABLE BIT(20)
98 +#define JZ4740_CODEC_1_VREF_AMP_DISABLE BIT(19)
99 +#define JZ4740_CODEC_1_VREF_PULLDOWN BIT(18)
100 +#define JZ4740_CODEC_1_VREF_LOW_CURRENT BIT(17)
101 +#define JZ4740_CODEC_1_VREF_HIGH_CURRENT BIT(16)
102 +#define JZ4740_CODEC_1_HEADPHONE_DISABLE BIT(14)
103 +#define JZ4740_CODEC_1_HEADPHONE_AMP_CHANGE_ANY BIT(13)
104 +#define JZ4740_CODEC_1_HEADPHONE_CHARGE BIT(12)
105 +#define JZ4740_CODEC_1_HEADPHONE_PULLDOWN (BIT(11) | BIT(10))
106 +#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M BIT(9)
107 +#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN BIT(8)
108 +#define JZ4740_CODEC_1_SUSPEND BIT(1)
109 +#define JZ4740_CODEC_1_RESET BIT(0)
110 +
111 +#define JZ4740_CODEC_1_LINE_ENABLE_OFFSET 29
112 +#define JZ4740_CODEC_1_MIC_ENABLE_OFFSET 28
113 +#define JZ4740_CODEC_1_SW1_ENABLE_OFFSET 27
114 +#define JZ4740_CODEC_1_ADC_ENABLE_OFFSET 26
115 +#define JZ4740_CODEC_1_SW2_ENABLE_OFFSET 25
116 +#define JZ4740_CODEC_1_DAC_ENABLE_OFFSET 24
117 +#define JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET 14
118 +#define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET 8
119 +
120 +#define JZ4740_CODEC_2_INPUT_VOLUME_MASK               0x1f0000
121 +#define JZ4740_CODEC_2_SAMPLE_RATE_MASK                        0x000f00
122 +#define JZ4740_CODEC_2_MIC_BOOST_GAIN_MASK             0x000030
123 +#define JZ4740_CODEC_2_HEADPHONE_VOLUME_MASK   0x000003
124 +
125 +#define JZ4740_CODEC_2_INPUT_VOLUME_OFFSET             16
126 +#define JZ4740_CODEC_2_SAMPLE_RATE_OFFSET               8
127 +#define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET    4
128 +#define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET  0
129 +
130 +static const uint32_t jz4740_codec_regs[] = {
131 +       0x021b2302, 0x00170803,
132 +};
133 +
134 +struct jz4740_codec {
135 +       void __iomem *base;
136 +       struct resource *mem;
137 +
138 +       uint32_t reg_cache[2];
139 +       struct snd_soc_codec codec;
140 +};
141 +
142 +static inline struct jz4740_codec *codec_to_jz4740(struct snd_soc_codec *codec)
143 +{
144 +       return container_of(codec, struct jz4740_codec, codec);
145 +}
146 +
147 +static unsigned int jz4740_codec_read(struct snd_soc_codec *codec,
148 +       unsigned int reg)
149 +{
150 +       struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
151 +       return readl(jz4740_codec->base + (reg << 2));
152 +}
153 +
154 +static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg,
155 +       unsigned int val)
156 +{
157 +       struct jz4740_codec *jz4740_codec = codec_to_jz4740(codec);
158 +
159 +       jz4740_codec->reg_cache[reg] = val;
160 +       writel(val, jz4740_codec->base + (reg << 2));
161 +
162 +       return 0;
163 +}
164 +
165 +static const struct snd_kcontrol_new jz4740_codec_controls[] = {
166 +       SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2,
167 +                       JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0),
168 +       SOC_SINGLE("Master Capture Volume", JZ4740_REG_CODEC_2,
169 +                       JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0),
170 +       SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
171 +                       JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
172 +       SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2,
173 +                       JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0),
174 +};
175 +
176 +static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
177 +       SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1,
178 +                       JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0),
179 +       SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1,
180 +                       JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0),
181 +};
182 +
183 +static const struct snd_kcontrol_new jz4740_codec_input_controls[] = {
184 +       SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1,
185 +                       JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0),
186 +       SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1,
187 +                       JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0),
188 +};
189 +
190 +static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = {
191 +       SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1,
192 +                       JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0),
193 +       SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1,
194 +                       JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0),
195 +
196 +       SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1,
197 +                       JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET, 1,
198 +                       jz4740_codec_output_controls,
199 +                       ARRAY_SIZE(jz4740_codec_output_controls)),
200 +
201 +       SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
202 +                       jz4740_codec_input_controls,
203 +                       ARRAY_SIZE(jz4740_codec_input_controls)),
204 +       SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
205 +
206 +       SND_SOC_DAPM_OUTPUT("LOUT"),
207 +       SND_SOC_DAPM_OUTPUT("ROUT"),
208 +
209 +       SND_SOC_DAPM_INPUT("MIC"),
210 +       SND_SOC_DAPM_INPUT("LIN"),
211 +       SND_SOC_DAPM_INPUT("RIN"),
212 +};
213 +
214 +static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
215 +       {"Line Input", NULL, "LIN"},
216 +       {"Line Input", NULL, "RIN"},
217 +
218 +       {"Input Mixer", "Line Capture Switch", "Line Input"},
219 +       {"Input Mixer", "Mic Capture Switch", "MIC"},
220 +
221 +       {"ADC", NULL, "Input Mixer"},
222 +
223 +       {"Output Mixer", "Bypass Switch", "Input Mixer"},
224 +       {"Output Mixer", "DAC Switch", "DAC"},
225 +
226 +       {"LOUT", NULL, "Output Mixer"},
227 +       {"ROUT", NULL, "Output Mixer"},
228 +};
229 +
230 +static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
231 +       struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
232 +{
233 +       uint32_t val;
234 +       struct snd_soc_pcm_runtime *rtd = substream->private_data;
235 +       struct snd_soc_device *socdev = rtd->socdev;
236 +       struct snd_soc_codec *codec = socdev->card->codec;
237 +
238 +       switch (params_rate(params)) {
239 +       case 8000:
240 +               val = 0;
241 +               break;
242 +       case 11025:
243 +               val = 1;
244 +               break;
245 +       case 12000:
246 +               val = 2;
247 +               break;
248 +       case 16000:
249 +               val = 3;
250 +               break;
251 +       case 22050:
252 +               val = 4;
253 +               break;
254 +       case 24000:
255 +               val = 5;
256 +               break;
257 +       case 32000:
258 +               val = 6;
259 +               break;
260 +       case 44100:
261 +               val = 7;
262 +               break;
263 +       case 48000:
264 +               val = 8;
265 +               break;
266 +       default:
267 +               return -EINVAL;
268 +       }
269 +
270 +       val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;
271 +
272 +       snd_soc_update_bits(codec, JZ4740_REG_CODEC_2,
273 +                               JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);
274 +
275 +       return 0;
276 +}
277 +
278 +static struct snd_soc_dai_ops jz4740_codec_dai_ops = {
279 +       .hw_params = jz4740_codec_hw_params,
280 +};
281 +
282 +struct snd_soc_dai jz4740_codec_dai = {
283 +       .name = "jz4740",
284 +       .playback = {
285 +               .stream_name = "Playback",
286 +               .channels_min = 2,
287 +               .channels_max = 2,
288 +               .rates = SNDRV_PCM_RATE_8000_48000,
289 +               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
290 +       },
291 +       .capture = {
292 +               .stream_name = "Capture",
293 +               .channels_min = 2,
294 +               .channels_max = 2,
295 +               .rates = SNDRV_PCM_RATE_8000_48000,
296 +               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
297 +       },
298 +       .ops = &jz4740_codec_dai_ops,
299 +       .symmetric_rates = 1,
300 +};
301 +EXPORT_SYMBOL_GPL(jz4740_codec_dai);
302 +
303 +static void jz4740_codec_wakeup(struct snd_soc_codec *codec)
304 +{
305 +       int i;
306 +       uint32_t *cache = codec->reg_cache;
307 +
308 +       snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
309 +               JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
310 +       udelay(2);
311 +
312 +       snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
313 +               JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
314 +
315 +       for (i = 0; i < ARRAY_SIZE(jz4740_codec_regs); ++i)
316 +               jz4740_codec_write(codec, i, cache[i]);
317 +}
318 +
319 +static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
320 +       enum snd_soc_bias_level level)
321 +{
322 +       unsigned int mask;
323 +       unsigned int value;
324 +
325 +       switch (level) {
326 +       case SND_SOC_BIAS_ON:
327 +               break;
328 +       case SND_SOC_BIAS_PREPARE:
329 +               mask = JZ4740_CODEC_1_VREF_DISABLE |
330 +                               JZ4740_CODEC_1_VREF_AMP_DISABLE |
331 +                               JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
332 +               value = 0;
333 +
334 +               snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
335 +               break;
336 +       case SND_SOC_BIAS_STANDBY:
337 +               /* The only way to clear the suspend flag is to reset the codec */
338 +               if (codec->bias_level == SND_SOC_BIAS_OFF)
339 +                       jz4740_codec_wakeup(codec);
340 +
341 +               mask = JZ4740_CODEC_1_VREF_DISABLE |
342 +                       JZ4740_CODEC_1_VREF_AMP_DISABLE |
343 +                       JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
344 +               value = JZ4740_CODEC_1_VREF_DISABLE |
345 +                       JZ4740_CODEC_1_VREF_AMP_DISABLE |
346 +                       JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
347 +
348 +               snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
349 +               break;
350 +       case SND_SOC_BIAS_OFF:
351 +               mask = JZ4740_CODEC_1_SUSPEND;
352 +               value = JZ4740_CODEC_1_SUSPEND;
353 +
354 +               snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
355 +               break;
356 +       default:
357 +               break;
358 +       }
359 +
360 +       codec->bias_level = level;
361 +
362 +       return 0;
363 +}
364 +
365 +static struct snd_soc_codec *jz4740_codec_codec;
366 +
367 +static int jz4740_codec_dev_probe(struct platform_device *pdev)
368 +{
369 +       int ret;
370 +       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
371 +       struct snd_soc_codec *codec = jz4740_codec_codec;
372 +
373 +       BUG_ON(!codec);
374 +
375 +       socdev->card->codec = codec;
376 +
377 +       ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
378 +       if (ret) {
379 +               dev_err(&pdev->dev, "Failed to create pcms: %d\n", ret);
380 +               return ret;
381 +       }
382 +
383 +       snd_soc_add_controls(codec, jz4740_codec_controls,
384 +               ARRAY_SIZE(jz4740_codec_controls));
385 +
386 +       snd_soc_dapm_new_controls(codec, jz4740_codec_dapm_widgets,
387 +               ARRAY_SIZE(jz4740_codec_dapm_widgets));
388 +
389 +       snd_soc_dapm_add_routes(codec, jz4740_codec_dapm_routes,
390 +               ARRAY_SIZE(jz4740_codec_dapm_routes));
391 +
392 +       snd_soc_dapm_new_widgets(codec);
393 +
394 +       return 0;
395 +}
396 +
397 +static int jz4740_codec_dev_remove(struct platform_device *pdev)
398 +{
399 +       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
400 +
401 +       snd_soc_free_pcms(socdev);
402 +       snd_soc_dapm_free(socdev);
403 +
404 +       return 0;
405 +}
406 +
407 +#ifdef CONFIG_PM_SLEEP
408 +
409 +static int jz4740_codec_suspend(struct platform_device *pdev, pm_message_t state)
410 +{
411 +       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
412 +       struct snd_soc_codec *codec = socdev->card->codec;
413 +
414 +       return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
415 +}
416 +
417 +static int jz4740_codec_resume(struct platform_device *pdev)
418 +{
419 +       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
420 +       struct snd_soc_codec *codec = socdev->card->codec;
421 +
422 +       return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
423 +}
424 +
425 +#else
426 +#define jz4740_codec_suspend NULL
427 +#define jz4740_codec_resume NULL
428 +#endif
429 +
430 +struct snd_soc_codec_device soc_codec_dev_jz4740_codec = {
431 +       .probe = jz4740_codec_dev_probe,
432 +       .remove = jz4740_codec_dev_remove,
433 +       .suspend = jz4740_codec_suspend,
434 +       .resume = jz4740_codec_resume,
435 +};
436 +EXPORT_SYMBOL_GPL(soc_codec_dev_jz4740_codec);
437 +
438 +static int __devinit jz4740_codec_probe(struct platform_device *pdev)
439 +{
440 +       int ret;
441 +       struct jz4740_codec *jz4740_codec;
442 +       struct snd_soc_codec *codec;
443 +       struct resource *mem;
444 +
445 +       jz4740_codec = kzalloc(sizeof(*jz4740_codec), GFP_KERNEL);
446 +       if (!jz4740_codec)
447 +               return -ENOMEM;
448 +
449 +       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
450 +       if (!mem) {
451 +               dev_err(&pdev->dev, "Failed to get mmio memory resource\n");
452 +               ret = -ENOENT;
453 +               goto err_free_codec;
454 +       }
455 +
456 +       mem = request_mem_region(mem->start, resource_size(mem), pdev->name);
457 +       if (!mem) {
458 +               dev_err(&pdev->dev, "Failed to request mmio memory region\n");
459 +               ret = -EBUSY;
460 +               goto err_free_codec;
461 +       }
462 +
463 +       jz4740_codec->base = ioremap(mem->start, resource_size(mem));
464 +       if (!jz4740_codec->base) {
465 +               dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
466 +               ret = -EBUSY;
467 +               goto err_release_mem_region;
468 +       }
469 +       jz4740_codec->mem = mem;
470 +
471 +       jz4740_codec_dai.dev = &pdev->dev;
472 +
473 +       codec = &jz4740_codec->codec;
474 +
475 +       codec->dev              = &pdev->dev;
476 +       codec->name             = "jz4740";
477 +       codec->owner            = THIS_MODULE;
478 +
479 +       codec->read             = jz4740_codec_read;
480 +       codec->write            = jz4740_codec_write;
481 +       codec->set_bias_level   = jz4740_codec_set_bias_level;
482 +       codec->bias_level       = SND_SOC_BIAS_OFF;
483 +
484 +       codec->dai              = &jz4740_codec_dai;
485 +       codec->num_dai          = 1;
486 +
487 +       codec->reg_cache        = jz4740_codec->reg_cache;
488 +       codec->reg_cache_size   = 2;
489 +       memcpy(codec->reg_cache, jz4740_codec_regs, sizeof(jz4740_codec_regs));
490 +
491 +       mutex_init(&codec->mutex);
492 +       INIT_LIST_HEAD(&codec->dapm_widgets);
493 +       INIT_LIST_HEAD(&codec->dapm_paths);
494 +
495 +       jz4740_codec_codec = codec;
496 +
497 +       snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
498 +                       JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
499 +
500 +       platform_set_drvdata(pdev, jz4740_codec);
501 +
502 +       ret = snd_soc_register_codec(codec);
503 +       if (ret) {
504 +               dev_err(&pdev->dev, "Failed to register codec\n");
505 +               goto err_iounmap;
506 +       }
507 +
508 +       ret = snd_soc_register_dai(&jz4740_codec_dai);
509 +       if (ret) {
510 +               dev_err(&pdev->dev, "Failed to register codec dai\n");
511 +               goto err_unregister_codec;
512 +       }
513 +
514 +       jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
515 +
516 +       return 0;
517 +
518 +err_unregister_codec:
519 +       snd_soc_unregister_codec(codec);
520 +err_iounmap:
521 +       iounmap(jz4740_codec->base);
522 +err_release_mem_region:
523 +       release_mem_region(mem->start, resource_size(mem));
524 +err_free_codec:
525 +       kfree(jz4740_codec);
526 +
527 +       return ret;
528 +}
529 +
530 +static int __devexit jz4740_codec_remove(struct platform_device *pdev)
531 +{
532 +       struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev);
533 +       struct resource *mem = jz4740_codec->mem;
534 +
535 +       snd_soc_unregister_dai(&jz4740_codec_dai);
536 +       snd_soc_unregister_codec(&jz4740_codec->codec);
537 +
538 +       iounmap(jz4740_codec->base);
539 +       release_mem_region(mem->start, resource_size(mem));
540 +
541 +       platform_set_drvdata(pdev, NULL);
542 +       kfree(jz4740_codec);
543 +
544 +       return 0;
545 +}
546 +
547 +static struct platform_driver jz4740_codec_driver = {
548 +       .probe = jz4740_codec_probe,
549 +       .remove = __devexit_p(jz4740_codec_remove),
550 +       .driver = {
551 +               .name = "jz4740-codec",
552 +               .owner = THIS_MODULE,
553 +       },
554 +};
555 +
556 +static int __init jz4740_codec_init(void)
557 +{
558 +       return platform_driver_register(&jz4740_codec_driver);
559 +}
560 +module_init(jz4740_codec_init);
561 +
562 +static void __exit jz4740_codec_exit(void)
563 +{
564 +       platform_driver_unregister(&jz4740_codec_driver);
565 +}
566 +module_exit(jz4740_codec_exit);
567 +
568 +MODULE_DESCRIPTION("JZ4740 SoC internal codec driver");
569 +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
570 +MODULE_LICENSE("GPL v2");
571 +MODULE_ALIAS("platform:jz4740-codec");
572 --- /dev/null
573 +++ b/sound/soc/codecs/jz4740.h
574 @@ -0,0 +1,20 @@
575 +/*
576 + * Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de>
577 + *
578 + * This program is free software; you can redistribute it and/or modify
579 + * it under the terms of the GNU General Public License version 2 as
580 + * published by the Free Software Foundation.
581 + *
582 + *  You should have received a copy of the  GNU General Public License along
583 + *  with this program; if not, write  to the Free Software Foundation, Inc.,
584 + *  675 Mass Ave, Cambridge, MA 02139, USA.
585 + *
586 + */
587 +
588 +#ifndef __SND_SOC_CODECS_JZ4740_CODEC_H__
589 +#define __SND_SOC_CODECS_JZ4740_CODEC_H__
590 +
591 +extern struct snd_soc_dai jz4740_codec_dai;
592 +extern struct snd_soc_codec_device soc_codec_dev_jz4740_codec;
593 +
594 +#endif