Root/target/linux/xburst/patches-2.6.36/010-n516-sound.patch

1From db1d0ca28d3c3d379be2203e2a5aff2685963e7a Mon Sep 17 00:00:00 2001
2From: Lars-Peter Clausen <lars@metafoo.de>
3Date: Sun, 1 Aug 2010 21:34:54 +0200
4Subject: [PATCH] Add N516 sound SoC board driver
5
6---
7 sound/soc/jz4740/Kconfig | 8 ++
8 sound/soc/jz4740/Makefile | 2 +
9 sound/soc/jz4740/n516.c | 303 +++++++++++++++++++++++++++++++++++++++++++++
10 3 files changed, 313 insertions(+), 0 deletions(-)
11 create mode 100644 sound/soc/jz4740/n516.c
12
13--- a/sound/soc/jz4740/Kconfig
14+++ b/sound/soc/jz4740/Kconfig
15@@ -21,3 +21,11 @@ config SND_JZ4740_SOC_QI_LB60
16     help
17       Say Y if you want to add support for ASoC audio on the Qi LB60 board
18       a.k.a Qi Ben NanoNote.
19+
20+config SND_JZ4740_SOC_N516
21+ tristate "SoC Audio support for Hanvon N516 eBook reader"
22+ depends on SND_JZ4740_SOC && JZ4740_N516
23+ select SND_JZ4740_SOC_I2S
24+ select SND_SOC_JZCODEC
25+ help
26+ Say Y if you want to enable support for SoC audio on the Hanvon N516.
27--- a/sound/soc/jz4740/Makefile
28+++ b/sound/soc/jz4740/Makefile
29@@ -9,5 +9,7 @@ obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-
30 
31 # Jz4740 Machine Support
32 snd-soc-qi-lb60-objs := qi_lb60.o
33+snd-soc-n516-objs := n516.o
34 
35 obj-$(CONFIG_SND_JZ4740_SOC_QI_LB60) += snd-soc-qi-lb60.o
36+obj-$(CONFIG_SND_JZ4740_SOC_N516) += snd-soc-n516.o
37--- /dev/null
38+++ b/sound/soc/jz4740/n516.c
39@@ -0,0 +1,303 @@
40+/*
41+ * Copyright (C) 2009, Yauhen Kharuzhy <jekhor@gmail.com>
42+ * OpenInkpot project
43+ * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
44+ *
45+ * This program is free software; you can redistribute it and/or modify
46+ * it under the terms of the GNU General Public License version 2 as
47+ * published by the Free Software Foundation.
48+ *
49+ * You should have received a copy of the GNU General Public License along
50+ * with this program; if not, write to the Free Software Foundation, Inc.,
51+ * 675 Mass Ave, Cambridge, MA 02139, USA.
52+ *
53+ */
54+
55+#include <linux/module.h>
56+#include <linux/interrupt.h>
57+#include <linux/platform_device.h>
58+#include <sound/core.h>
59+#include <sound/pcm.h>
60+#include <sound/soc.h>
61+#include <sound/soc-dapm.h>
62+#include <sound/jack.h>
63+#include <linux/gpio.h>
64+#include <linux/workqueue.h>
65+
66+#include "../codecs/jzcodec.h"
67+#include "jz4740-pcm.h"
68+#include "jz4740-i2s.h"
69+
70+#include <asm/mach-jz4740/board-n516.h>
71+
72+enum {
73+ N516_SPEAKER_AUTO = 0,
74+ N516_SPEAKER_OFF = 1,
75+ N516_SPEAKER_ON = 2,
76+};
77+
78+static int n516_speaker_mode;
79+static struct snd_soc_codec *n516_codec;
80+static struct work_struct n516_headphone_work;
81+
82+static void n516_ext_control(void)
83+{
84+ if (!n516_codec)
85+ return;
86+
87+ switch (n516_speaker_mode) {
88+ case N516_SPEAKER_ON:
89+ snd_soc_dapm_enable_pin(n516_codec, "Speaker");
90+ break;
91+ case N516_SPEAKER_OFF:
92+ snd_soc_dapm_disable_pin(n516_codec, "Speaker");
93+ break;
94+ case N516_SPEAKER_AUTO:
95+ if (snd_soc_dapm_get_pin_status(n516_codec, "Headphone"))
96+ snd_soc_dapm_disable_pin(n516_codec, "Speaker");
97+ else
98+ snd_soc_dapm_enable_pin(n516_codec, "Speaker");
99+ break;
100+ default:
101+ break;
102+ }
103+
104+ /* signal a DAPM event */
105+ snd_soc_dapm_sync(n516_codec);
106+}
107+
108+static int n516_speaker_event(struct snd_soc_dapm_widget *widget,
109+ struct snd_kcontrol *ctrl, int event)
110+{
111+ int on = !SND_SOC_DAPM_EVENT_OFF(event);
112+
113+ gpio_set_value(GPIO_SPEAKER_ENABLE, on);
114+
115+ return 0;
116+}
117+
118+static void n516_headphone_event_work(struct work_struct *work)
119+{
120+ n516_ext_control();
121+}
122+
123+static int n516_headphone_event(struct snd_soc_dapm_widget *widget,
124+ struct snd_kcontrol *ctrl, int event)
125+{
126+ /* We can't call soc_dapm_sync from a event handler */
127+ if (event & (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD))
128+ schedule_work(&n516_headphone_work);
129+ return 0;
130+}
131+
132+static const struct snd_soc_dapm_widget n516_widgets[] = {
133+ SND_SOC_DAPM_SPK("Speaker", n516_speaker_event),
134+ SND_SOC_DAPM_HP("Headphone", n516_headphone_event),
135+ SND_SOC_DAPM_MIC("Mic", NULL),
136+};
137+
138+static const struct snd_soc_dapm_route n516_routes[] = {
139+ {"Mic", NULL, "MIC"},
140+ {"Speaker", NULL, "LOUT"},
141+ {"Speaker", NULL, "ROUT"},
142+ {"Headphone", NULL, "LOUT"},
143+ {"Headphone", NULL, "ROUT"},
144+};
145+
146+static const char *n516_speaker_modes[] = {"Auto", "Off", "On"};
147+static const struct soc_enum n516_speaker_mode_enum =
148+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(n516_speaker_modes), n516_speaker_modes);
149+
150+static int n516_get_speaker_mode(struct snd_kcontrol *kcontrol,
151+ struct snd_ctl_elem_value *ucontrol)
152+{
153+ ucontrol->value.integer.value[0] = n516_speaker_mode;
154+ return 0;
155+}
156+
157+static int n516_set_speaker_mode(struct snd_kcontrol *kcontrol,
158+ struct snd_ctl_elem_value *ucontrol)
159+{
160+ if (n516_speaker_mode == ucontrol->value.integer.value[0])
161+ return 0;
162+
163+ n516_speaker_mode = ucontrol->value.integer.value[0];
164+ n516_ext_control();
165+ return 1;
166+}
167+
168+static const struct snd_kcontrol_new n516_controls[] = {
169+ SOC_ENUM_EXT("Speaker Function", n516_speaker_mode_enum,
170+ n516_get_speaker_mode, n516_set_speaker_mode),
171+};
172+
173+#define N516_DAIFMT (SND_SOC_DAIFMT_I2S | \
174+ SND_SOC_DAIFMT_NB_NF | \
175+ SND_SOC_DAIFMT_CBM_CFM)
176+
177+static int n516_codec_init(struct snd_soc_codec *codec)
178+{
179+ int ret;
180+ struct snd_soc_dai *cpu_dai = codec->socdev->card->dai_link->cpu_dai;
181+ struct snd_soc_dai *codec_dai = codec->socdev->card->dai_link->codec_dai;
182+
183+ n516_codec = codec;
184+
185+ snd_soc_dapm_nc_pin(codec, "LIN");
186+ snd_soc_dapm_nc_pin(codec, "RIN");
187+
188+ ret = snd_soc_dai_set_fmt(codec_dai, N516_DAIFMT);
189+ if (ret < 0) {
190+ dev_err(codec->dev, "Failed to set codec dai format: %d\n", ret);
191+ return ret;
192+ }
193+
194+ ret = snd_soc_dai_set_fmt(cpu_dai, N516_DAIFMT);
195+ if (ret < 0) {
196+ dev_err(codec->dev, "Failed to set cpu dai format: %d\n", ret);
197+ return ret;
198+ }
199+
200+ ret = snd_soc_dai_set_sysclk(codec_dai, JZCODEC_SYSCLK, 111,
201+ SND_SOC_CLOCK_IN);
202+ if (ret < 0) {
203+ dev_err(codec->dev, "Failed to set codec dai sysclk: %d\n", ret);
204+ return ret;
205+ }
206+
207+ ret = snd_soc_add_controls(codec, n516_controls,
208+ ARRAY_SIZE(n516_controls));
209+ if (ret) {
210+ dev_err(codec->dev, "Failed to add controls: %d\n", ret);
211+ return ret;
212+ }
213+
214+
215+ ret = snd_soc_dapm_new_controls(codec, n516_widgets,
216+ ARRAY_SIZE(n516_widgets));
217+ if (ret) {
218+ dev_err(codec->dev, "Failed to add dapm controls: %d\n", ret);
219+ return ret;
220+ }
221+
222+ ret = snd_soc_dapm_add_routes(codec, n516_routes, ARRAY_SIZE(n516_routes));
223+ if (ret) {
224+ dev_err(codec->dev, "Failed to add dapm routes: %d\n", ret);
225+ return ret;
226+ }
227+
228+ snd_soc_dapm_sync(codec);
229+
230+ return 0;
231+}
232+
233+static struct snd_soc_dai_link n516_dai = {
234+ .name = "jz-codec",
235+ .stream_name = "JZCODEC",
236+ .cpu_dai = &jz4740_i2s_dai,
237+ .codec_dai = &jz_codec_dai,
238+ .init = n516_codec_init,
239+};
240+
241+static struct snd_soc_card n516_card = {
242+ .name = "N516",
243+ .dai_link = &n516_dai,
244+ .num_links = 1,
245+ .platform = &jz4740_soc_platform,
246+};
247+
248+static struct snd_soc_device n516_snd_devdata = {
249+ .card = &n516_card,
250+ .codec_dev = &soc_codec_dev_jzcodec,
251+};
252+
253+static struct platform_device *n516_snd_device;
254+
255+static struct snd_soc_jack n516_hp_jack;
256+
257+static struct snd_soc_jack_pin n516_hp_pin = {
258+ .pin = "Headphone",
259+ .mask = SND_JACK_HEADPHONE,
260+};
261+
262+static struct snd_soc_jack_gpio n516_hp_gpio = {
263+ .gpio = GPIO_HPHONE_DETECT,
264+ .name = "Headphone detect",
265+ .report = SND_JACK_HEADPHONE,
266+ .debounce_time = 100,
267+};
268+
269+static int __init n516_add_headphone_jack(void)
270+{
271+ int ret;
272+
273+ ret = snd_soc_jack_new(&n516_card, "Headphone jack",
274+ SND_JACK_HEADPHONE, &n516_hp_jack);
275+ if (ret)
276+ return ret;
277+
278+ ret = snd_soc_jack_add_pins(&n516_hp_jack, 1, &n516_hp_pin);
279+ if (ret)
280+ return ret;
281+
282+ ret = snd_soc_jack_add_gpios(&n516_hp_jack, 1, &n516_hp_gpio);
283+
284+ return ret;
285+}
286+
287+static int __init n516_init(void)
288+{
289+ int ret;
290+
291+ n516_snd_device = platform_device_alloc("soc-audio", -1);
292+
293+ if (!n516_snd_device)
294+ return -ENOMEM;
295+
296+ ret = gpio_request(GPIO_SPEAKER_ENABLE, "Speaker enable");
297+ if (ret) {
298+ pr_err("n516 snd: Failed to request SPEAKER_ENABLE GPIO(%d): %d\n",
299+ GPIO_SPEAKER_ENABLE, ret);
300+ goto err_device_put;
301+ }
302+
303+ gpio_direction_output(GPIO_SPEAKER_ENABLE, 0);
304+ INIT_WORK(&n516_headphone_work, n516_headphone_event_work);
305+
306+ platform_set_drvdata(n516_snd_device, &n516_snd_devdata);
307+ n516_snd_devdata.dev = &n516_snd_device->dev;
308+ ret = platform_device_add(n516_snd_device);
309+ if (ret) {
310+ pr_err("n516 snd: Failed to add snd soc device: %d\n", ret);
311+ goto err_unset_pdata;
312+ }
313+
314+ ret = n516_add_headphone_jack();
315+ /* We can live without it, so just print a warning */
316+ if (ret)
317+ pr_warning("n516 snd: Failed to initalise headphone jack: %d\n", ret);
318+
319+ return 0;
320+
321+err_unset_pdata:
322+ platform_set_drvdata(n516_snd_device, NULL);
323+/*err_gpio_free_speaker:*/
324+ gpio_free(GPIO_SPEAKER_ENABLE);
325+err_device_put:
326+ platform_device_put(n516_snd_device);
327+
328+ return ret;
329+}
330+module_init(n516_init);
331+
332+static void __exit n516_exit(void)
333+{
334+ snd_soc_jack_free_gpios(&n516_hp_jack, 1, &n516_hp_gpio);
335+ gpio_free(GPIO_SPEAKER_ENABLE);
336+ platform_device_unregister(n516_snd_device);
337+}
338+module_exit(n516_exit);
339+
340+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
341+MODULE_DESCRIPTION("ALSA SoC N516 Audio support");
342+MODULE_LICENSE("GPL v2");
343

Archive Download this file



interactive