diff options
Diffstat (limited to 'sound/soc/pxa')
-rw-r--r-- | sound/soc/pxa/Kconfig | 22 | ||||
-rw-r--r-- | sound/soc/pxa/Makefile | 6 | ||||
-rw-r--r-- | sound/soc/pxa/corgi.c | 12 | ||||
-rw-r--r-- | sound/soc/pxa/e800_wm9712.c | 8 | ||||
-rw-r--r-- | sound/soc/pxa/em-x270.c | 7 | ||||
-rw-r--r-- | sound/soc/pxa/palm27x.c | 269 | ||||
-rw-r--r-- | sound/soc/pxa/poodle.c | 6 | ||||
-rw-r--r-- | sound/soc/pxa/pxa-ssp.c | 931 | ||||
-rw-r--r-- | sound/soc/pxa/pxa-ssp.h | 47 | ||||
-rw-r--r-- | sound/soc/pxa/pxa2xx-ac97.c | 33 | ||||
-rw-r--r-- | sound/soc/pxa/pxa2xx-i2s.c | 35 | ||||
-rw-r--r-- | sound/soc/pxa/pxa2xx-pcm.c | 14 | ||||
-rw-r--r-- | sound/soc/pxa/spitz.c | 6 | ||||
-rw-r--r-- | sound/soc/pxa/tosa.c | 38 | ||||
-rw-r--r-- | sound/soc/pxa/zylonite.c | 219 |
15 files changed, 1596 insertions, 57 deletions
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index f8c1cdd940ac..f82e10699471 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig | |||
@@ -21,6 +21,9 @@ config SND_PXA2XX_SOC_AC97 | |||
21 | config SND_PXA2XX_SOC_I2S | 21 | config SND_PXA2XX_SOC_I2S |
22 | tristate | 22 | tristate |
23 | 23 | ||
24 | config SND_PXA_SOC_SSP | ||
25 | tristate | ||
26 | |||
24 | config SND_PXA2XX_SOC_CORGI | 27 | config SND_PXA2XX_SOC_CORGI |
25 | tristate "SoC Audio support for Sharp Zaurus SL-C7x0" | 28 | tristate "SoC Audio support for Sharp Zaurus SL-C7x0" |
26 | depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx | 29 | depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx |
@@ -75,3 +78,22 @@ config SND_PXA2XX_SOC_EM_X270 | |||
75 | help | 78 | help |
76 | Say Y if you want to add support for SoC audio on | 79 | Say Y if you want to add support for SoC audio on |
77 | CompuLab EM-x270. | 80 | CompuLab EM-x270. |
81 | |||
82 | config SND_PXA2XX_SOC_PALM27X | ||
83 | bool "SoC Audio support for Palm T|X, T5 and LifeDrive" | ||
84 | depends on SND_PXA2XX_SOC && (MACH_PALMLD || MACH_PALMTX || MACH_PALMT5) | ||
85 | select SND_PXA2XX_SOC_AC97 | ||
86 | select SND_SOC_WM9712 | ||
87 | help | ||
88 | Say Y if you want to add support for SoC audio on | ||
89 | Palm T|X, T5 or LifeDrive handheld computer. | ||
90 | |||
91 | config SND_SOC_ZYLONITE | ||
92 | tristate "SoC Audio support for Marvell Zylonite" | ||
93 | depends on SND_PXA2XX_SOC && MACH_ZYLONITE | ||
94 | select SND_PXA2XX_SOC_AC97 | ||
95 | select SND_PXA_SOC_SSP | ||
96 | select SND_SOC_WM9713 | ||
97 | help | ||
98 | Say Y if you want to add support for SoC audio on the | ||
99 | Marvell Zylonite reference platform. | ||
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index 5bc8edf9dca9..08a9f2797729 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile | |||
@@ -2,10 +2,12 @@ | |||
2 | snd-soc-pxa2xx-objs := pxa2xx-pcm.o | 2 | snd-soc-pxa2xx-objs := pxa2xx-pcm.o |
3 | snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o | 3 | snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o |
4 | snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o | 4 | snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o |
5 | snd-soc-pxa-ssp-objs := pxa-ssp.o | ||
5 | 6 | ||
6 | obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o | 7 | obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o |
7 | obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o | 8 | obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o |
8 | obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o | 9 | obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o |
10 | obj-$(CONFIG_SND_PXA_SOC_SSP) += snd-soc-pxa-ssp.o | ||
9 | 11 | ||
10 | # PXA Machine Support | 12 | # PXA Machine Support |
11 | snd-soc-corgi-objs := corgi.o | 13 | snd-soc-corgi-objs := corgi.o |
@@ -14,6 +16,8 @@ snd-soc-tosa-objs := tosa.o | |||
14 | snd-soc-e800-objs := e800_wm9712.o | 16 | snd-soc-e800-objs := e800_wm9712.o |
15 | snd-soc-spitz-objs := spitz.o | 17 | snd-soc-spitz-objs := spitz.o |
16 | snd-soc-em-x270-objs := em-x270.o | 18 | snd-soc-em-x270-objs := em-x270.o |
19 | snd-soc-palm27x-objs := palm27x.o | ||
20 | snd-soc-zylonite-objs := zylonite.o | ||
17 | 21 | ||
18 | obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o | 22 | obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o |
19 | obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o | 23 | obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o |
@@ -21,3 +25,5 @@ obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o | |||
21 | obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o | 25 | obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o |
22 | obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o | 26 | obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o |
23 | obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o | 27 | obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o |
28 | obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o | ||
29 | obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o | ||
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 2718eaf7895f..1ba25a559524 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c | |||
@@ -108,15 +108,11 @@ static int corgi_startup(struct snd_pcm_substream *substream) | |||
108 | } | 108 | } |
109 | 109 | ||
110 | /* we need to unmute the HP at shutdown as the mute burns power on corgi */ | 110 | /* we need to unmute the HP at shutdown as the mute burns power on corgi */ |
111 | static int corgi_shutdown(struct snd_pcm_substream *substream) | 111 | static void corgi_shutdown(struct snd_pcm_substream *substream) |
112 | { | 112 | { |
113 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
114 | struct snd_soc_codec *codec = rtd->socdev->codec; | ||
115 | |||
116 | /* set = unmute headphone */ | 113 | /* set = unmute headphone */ |
117 | gpio_set_value(CORGI_GPIO_MUTE_L, 1); | 114 | gpio_set_value(CORGI_GPIO_MUTE_L, 1); |
118 | gpio_set_value(CORGI_GPIO_MUTE_R, 1); | 115 | gpio_set_value(CORGI_GPIO_MUTE_R, 1); |
119 | return 0; | ||
120 | } | 116 | } |
121 | 117 | ||
122 | static int corgi_hw_params(struct snd_pcm_substream *substream, | 118 | static int corgi_hw_params(struct snd_pcm_substream *substream, |
@@ -314,8 +310,9 @@ static struct snd_soc_dai_link corgi_dai = { | |||
314 | }; | 310 | }; |
315 | 311 | ||
316 | /* corgi audio machine driver */ | 312 | /* corgi audio machine driver */ |
317 | static struct snd_soc_machine snd_soc_machine_corgi = { | 313 | static struct snd_soc_card snd_soc_corgi = { |
318 | .name = "Corgi", | 314 | .name = "Corgi", |
315 | .platform = &pxa2xx_soc_platform, | ||
319 | .dai_link = &corgi_dai, | 316 | .dai_link = &corgi_dai, |
320 | .num_links = 1, | 317 | .num_links = 1, |
321 | }; | 318 | }; |
@@ -328,8 +325,7 @@ static struct wm8731_setup_data corgi_wm8731_setup = { | |||
328 | 325 | ||
329 | /* corgi audio subsystem */ | 326 | /* corgi audio subsystem */ |
330 | static struct snd_soc_device corgi_snd_devdata = { | 327 | static struct snd_soc_device corgi_snd_devdata = { |
331 | .machine = &snd_soc_machine_corgi, | 328 | .card = &snd_soc_corgi, |
332 | .platform = &pxa2xx_soc_platform, | ||
333 | .codec_dev = &soc_codec_dev_wm8731, | 329 | .codec_dev = &soc_codec_dev_wm8731, |
334 | .codec_data = &corgi_wm8731_setup, | 330 | .codec_data = &corgi_wm8731_setup, |
335 | }; | 331 | }; |
diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c index 6781c5be242f..2e3386dfa0f0 100644 --- a/sound/soc/pxa/e800_wm9712.c +++ b/sound/soc/pxa/e800_wm9712.c | |||
@@ -29,7 +29,7 @@ | |||
29 | #include "pxa2xx-pcm.h" | 29 | #include "pxa2xx-pcm.h" |
30 | #include "pxa2xx-ac97.h" | 30 | #include "pxa2xx-ac97.h" |
31 | 31 | ||
32 | static struct snd_soc_machine e800; | 32 | static struct snd_soc_card e800; |
33 | 33 | ||
34 | static struct snd_soc_dai_link e800_dai[] = { | 34 | static struct snd_soc_dai_link e800_dai[] = { |
35 | { | 35 | { |
@@ -40,15 +40,15 @@ static struct snd_soc_dai_link e800_dai[] = { | |||
40 | }, | 40 | }, |
41 | }; | 41 | }; |
42 | 42 | ||
43 | static struct snd_soc_machine e800 = { | 43 | static struct snd_soc_card e800 = { |
44 | .name = "Toshiba e800", | 44 | .name = "Toshiba e800", |
45 | .platform = &pxa2xx_soc_platform, | ||
45 | .dai_link = e800_dai, | 46 | .dai_link = e800_dai, |
46 | .num_links = ARRAY_SIZE(e800_dai), | 47 | .num_links = ARRAY_SIZE(e800_dai), |
47 | }; | 48 | }; |
48 | 49 | ||
49 | static struct snd_soc_device e800_snd_devdata = { | 50 | static struct snd_soc_device e800_snd_devdata = { |
50 | .machine = &e800, | 51 | .card = &e800, |
51 | .platform = &pxa2xx_soc_platform, | ||
52 | .codec_dev = &soc_codec_dev_wm9712, | 52 | .codec_dev = &soc_codec_dev_wm9712, |
53 | }; | 53 | }; |
54 | 54 | ||
diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c index e6ff6929ab4b..fe4a729ea648 100644 --- a/sound/soc/pxa/em-x270.c +++ b/sound/soc/pxa/em-x270.c | |||
@@ -23,7 +23,6 @@ | |||
23 | #include <linux/moduleparam.h> | 23 | #include <linux/moduleparam.h> |
24 | #include <linux/device.h> | 24 | #include <linux/device.h> |
25 | 25 | ||
26 | #include <sound/driver.h> | ||
27 | #include <sound/core.h> | 26 | #include <sound/core.h> |
28 | #include <sound/pcm.h> | 27 | #include <sound/pcm.h> |
29 | #include <sound/soc.h> | 28 | #include <sound/soc.h> |
@@ -53,15 +52,15 @@ static struct snd_soc_dai_link em_x270_dai[] = { | |||
53 | }, | 52 | }, |
54 | }; | 53 | }; |
55 | 54 | ||
56 | static struct snd_soc_machine em_x270 = { | 55 | static struct snd_soc_card em_x270 = { |
57 | .name = "EM-X270", | 56 | .name = "EM-X270", |
57 | .platform = &pxa2xx_soc_platform, | ||
58 | .dai_link = em_x270_dai, | 58 | .dai_link = em_x270_dai, |
59 | .num_links = ARRAY_SIZE(em_x270_dai), | 59 | .num_links = ARRAY_SIZE(em_x270_dai), |
60 | }; | 60 | }; |
61 | 61 | ||
62 | static struct snd_soc_device em_x270_snd_devdata = { | 62 | static struct snd_soc_device em_x270_snd_devdata = { |
63 | .machine = &em_x270, | 63 | .card = &em_x270, |
64 | .platform = &pxa2xx_soc_platform, | ||
65 | .codec_dev = &soc_codec_dev_wm9712, | 64 | .codec_dev = &soc_codec_dev_wm9712, |
66 | }; | 65 | }; |
67 | 66 | ||
diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c new file mode 100644 index 000000000000..4a9cf3083af0 --- /dev/null +++ b/sound/soc/pxa/palm27x.c | |||
@@ -0,0 +1,269 @@ | |||
1 | /* | ||
2 | * linux/sound/soc/pxa/palm27x.c | ||
3 | * | ||
4 | * SoC Audio driver for Palm T|X, T5 and LifeDrive | ||
5 | * | ||
6 | * based on tosa.c | ||
7 | * | ||
8 | * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/moduleparam.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/gpio.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/irq.h> | ||
22 | |||
23 | #include <sound/core.h> | ||
24 | #include <sound/pcm.h> | ||
25 | #include <sound/soc.h> | ||
26 | #include <sound/soc-dapm.h> | ||
27 | |||
28 | #include <asm/mach-types.h> | ||
29 | #include <mach/audio.h> | ||
30 | #include <mach/palmasoc.h> | ||
31 | |||
32 | #include "../codecs/wm9712.h" | ||
33 | #include "pxa2xx-pcm.h" | ||
34 | #include "pxa2xx-ac97.h" | ||
35 | |||
36 | static int palm27x_jack_func = 1; | ||
37 | static int palm27x_spk_func = 1; | ||
38 | static int palm27x_ep_gpio = -1; | ||
39 | |||
40 | static void palm27x_ext_control(struct snd_soc_codec *codec) | ||
41 | { | ||
42 | if (!palm27x_spk_func) | ||
43 | snd_soc_dapm_enable_pin(codec, "Speaker"); | ||
44 | else | ||
45 | snd_soc_dapm_disable_pin(codec, "Speaker"); | ||
46 | |||
47 | if (!palm27x_jack_func) | ||
48 | snd_soc_dapm_enable_pin(codec, "Headphone Jack"); | ||
49 | else | ||
50 | snd_soc_dapm_disable_pin(codec, "Headphone Jack"); | ||
51 | |||
52 | snd_soc_dapm_sync(codec); | ||
53 | } | ||
54 | |||
55 | static int palm27x_startup(struct snd_pcm_substream *substream) | ||
56 | { | ||
57 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
58 | struct snd_soc_codec *codec = rtd->socdev->codec; | ||
59 | |||
60 | /* check the jack status at stream startup */ | ||
61 | palm27x_ext_control(codec); | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static struct snd_soc_ops palm27x_ops = { | ||
66 | .startup = palm27x_startup, | ||
67 | }; | ||
68 | |||
69 | static irqreturn_t palm27x_interrupt(int irq, void *v) | ||
70 | { | ||
71 | palm27x_spk_func = gpio_get_value(palm27x_ep_gpio); | ||
72 | palm27x_jack_func = !palm27x_spk_func; | ||
73 | return IRQ_HANDLED; | ||
74 | } | ||
75 | |||
76 | static int palm27x_get_jack(struct snd_kcontrol *kcontrol, | ||
77 | struct snd_ctl_elem_value *ucontrol) | ||
78 | { | ||
79 | ucontrol->value.integer.value[0] = palm27x_jack_func; | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | static int palm27x_set_jack(struct snd_kcontrol *kcontrol, | ||
84 | struct snd_ctl_elem_value *ucontrol) | ||
85 | { | ||
86 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
87 | |||
88 | if (palm27x_jack_func == ucontrol->value.integer.value[0]) | ||
89 | return 0; | ||
90 | |||
91 | palm27x_jack_func = ucontrol->value.integer.value[0]; | ||
92 | palm27x_ext_control(codec); | ||
93 | return 1; | ||
94 | } | ||
95 | |||
96 | static int palm27x_get_spk(struct snd_kcontrol *kcontrol, | ||
97 | struct snd_ctl_elem_value *ucontrol) | ||
98 | { | ||
99 | ucontrol->value.integer.value[0] = palm27x_spk_func; | ||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static int palm27x_set_spk(struct snd_kcontrol *kcontrol, | ||
104 | struct snd_ctl_elem_value *ucontrol) | ||
105 | { | ||
106 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | ||
107 | |||
108 | if (palm27x_spk_func == ucontrol->value.integer.value[0]) | ||
109 | return 0; | ||
110 | |||
111 | palm27x_spk_func = ucontrol->value.integer.value[0]; | ||
112 | palm27x_ext_control(codec); | ||
113 | return 1; | ||
114 | } | ||
115 | |||
116 | /* PalmTX machine dapm widgets */ | ||
117 | static const struct snd_soc_dapm_widget palm27x_dapm_widgets[] = { | ||
118 | SND_SOC_DAPM_HP("Headphone Jack", NULL), | ||
119 | SND_SOC_DAPM_SPK("Speaker", NULL), | ||
120 | }; | ||
121 | |||
122 | /* PalmTX audio map */ | ||
123 | static const struct snd_soc_dapm_route audio_map[] = { | ||
124 | /* headphone connected to HPOUTL, HPOUTR */ | ||
125 | {"Headphone Jack", NULL, "HPOUTL"}, | ||
126 | {"Headphone Jack", NULL, "HPOUTR"}, | ||
127 | |||
128 | /* ext speaker connected to ROUT2, LOUT2 */ | ||
129 | {"Speaker", NULL, "LOUT2"}, | ||
130 | {"Speaker", NULL, "ROUT2"}, | ||
131 | }; | ||
132 | |||
133 | static const char *jack_function[] = {"Headphone", "Off"}; | ||
134 | static const char *spk_function[] = {"On", "Off"}; | ||
135 | static const struct soc_enum palm27x_enum[] = { | ||
136 | SOC_ENUM_SINGLE_EXT(2, jack_function), | ||
137 | SOC_ENUM_SINGLE_EXT(2, spk_function), | ||
138 | }; | ||
139 | |||
140 | static const struct snd_kcontrol_new palm27x_controls[] = { | ||
141 | SOC_ENUM_EXT("Jack Function", palm27x_enum[0], palm27x_get_jack, | ||
142 | palm27x_set_jack), | ||
143 | SOC_ENUM_EXT("Speaker Function", palm27x_enum[1], palm27x_get_spk, | ||
144 | palm27x_set_spk), | ||
145 | }; | ||
146 | |||
147 | static int palm27x_ac97_init(struct snd_soc_codec *codec) | ||
148 | { | ||
149 | int i, err; | ||
150 | |||
151 | snd_soc_dapm_nc_pin(codec, "OUT3"); | ||
152 | snd_soc_dapm_nc_pin(codec, "MONOOUT"); | ||
153 | |||
154 | /* add palm27x specific controls */ | ||
155 | for (i = 0; i < ARRAY_SIZE(palm27x_controls); i++) { | ||
156 | err = snd_ctl_add(codec->card, | ||
157 | snd_soc_cnew(&palm27x_controls[i], | ||
158 | codec, NULL)); | ||
159 | if (err < 0) | ||
160 | return err; | ||
161 | } | ||
162 | |||
163 | /* add palm27x specific widgets */ | ||
164 | snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets, | ||
165 | ARRAY_SIZE(palm27x_dapm_widgets)); | ||
166 | |||
167 | /* set up palm27x specific audio path audio_map */ | ||
168 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
169 | |||
170 | snd_soc_dapm_sync(codec); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static struct snd_soc_dai_link palm27x_dai[] = { | ||
175 | { | ||
176 | .name = "AC97 HiFi", | ||
177 | .stream_name = "AC97 HiFi", | ||
178 | .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], | ||
179 | .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], | ||
180 | .init = palm27x_ac97_init, | ||
181 | .ops = &palm27x_ops, | ||
182 | }, | ||
183 | { | ||
184 | .name = "AC97 Aux", | ||
185 | .stream_name = "AC97 Aux", | ||
186 | .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], | ||
187 | .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], | ||
188 | .ops = &palm27x_ops, | ||
189 | }, | ||
190 | }; | ||
191 | |||
192 | static struct snd_soc_card palm27x_asoc = { | ||
193 | .name = "Palm/PXA27x", | ||
194 | .platform = &pxa2xx_soc_platform, | ||
195 | .dai_link = palm27x_dai, | ||
196 | .num_links = ARRAY_SIZE(palm27x_dai), | ||
197 | }; | ||
198 | |||
199 | static struct snd_soc_device palm27x_snd_devdata = { | ||
200 | .card = &palm27x_asoc, | ||
201 | .codec_dev = &soc_codec_dev_wm9712, | ||
202 | }; | ||
203 | |||
204 | static struct platform_device *palm27x_snd_device; | ||
205 | |||
206 | static int __init palm27x_asoc_init(void) | ||
207 | { | ||
208 | int ret; | ||
209 | |||
210 | if (!(machine_is_palmtx() || machine_is_palmt5() || | ||
211 | machine_is_palmld())) | ||
212 | return -ENODEV; | ||
213 | |||
214 | ret = gpio_request(palm27x_ep_gpio, "Headphone Jack"); | ||
215 | if (ret) | ||
216 | return ret; | ||
217 | ret = gpio_direction_input(palm27x_ep_gpio); | ||
218 | if (ret) | ||
219 | goto err_alloc; | ||
220 | |||
221 | if (request_irq(gpio_to_irq(palm27x_ep_gpio), palm27x_interrupt, | ||
222 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
223 | "Headphone jack", NULL)) | ||
224 | goto err_alloc; | ||
225 | |||
226 | palm27x_snd_device = platform_device_alloc("soc-audio", -1); | ||
227 | if (!palm27x_snd_device) { | ||
228 | ret = -ENOMEM; | ||
229 | goto err_dev; | ||
230 | } | ||
231 | |||
232 | platform_set_drvdata(palm27x_snd_device, &palm27x_snd_devdata); | ||
233 | palm27x_snd_devdata.dev = &palm27x_snd_device->dev; | ||
234 | ret = platform_device_add(palm27x_snd_device); | ||
235 | |||
236 | if (ret != 0) | ||
237 | goto put_device; | ||
238 | |||
239 | return 0; | ||
240 | |||
241 | put_device: | ||
242 | platform_device_put(palm27x_snd_device); | ||
243 | err_dev: | ||
244 | free_irq(gpio_to_irq(palm27x_ep_gpio), NULL); | ||
245 | err_alloc: | ||
246 | gpio_free(palm27x_ep_gpio); | ||
247 | |||
248 | return ret; | ||
249 | } | ||
250 | |||
251 | static void __exit palm27x_asoc_exit(void) | ||
252 | { | ||
253 | free_irq(gpio_to_irq(palm27x_ep_gpio), NULL); | ||
254 | gpio_free(palm27x_ep_gpio); | ||
255 | platform_device_unregister(palm27x_snd_device); | ||
256 | } | ||
257 | |||
258 | void __init palm27x_asoc_set_pdata(struct palm27x_asoc_info *data) | ||
259 | { | ||
260 | palm27x_ep_gpio = data->jack_gpio; | ||
261 | } | ||
262 | |||
263 | module_init(palm27x_asoc_init); | ||
264 | module_exit(palm27x_asoc_exit); | ||
265 | |||
266 | /* Module information */ | ||
267 | MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); | ||
268 | MODULE_DESCRIPTION("ALSA SoC Palm T|X, T5 and LifeDrive"); | ||
269 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 4d9930c52789..6e9827189fff 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c | |||
@@ -276,8 +276,9 @@ static struct snd_soc_dai_link poodle_dai = { | |||
276 | }; | 276 | }; |
277 | 277 | ||
278 | /* poodle audio machine driver */ | 278 | /* poodle audio machine driver */ |
279 | static struct snd_soc_machine snd_soc_machine_poodle = { | 279 | static struct snd_soc_card snd_soc_poodle = { |
280 | .name = "Poodle", | 280 | .name = "Poodle", |
281 | .platform = &pxa2xx_soc_platform, | ||
281 | .dai_link = &poodle_dai, | 282 | .dai_link = &poodle_dai, |
282 | .num_links = 1, | 283 | .num_links = 1, |
283 | }; | 284 | }; |
@@ -290,8 +291,7 @@ static struct wm8731_setup_data poodle_wm8731_setup = { | |||
290 | 291 | ||
291 | /* poodle audio subsystem */ | 292 | /* poodle audio subsystem */ |
292 | static struct snd_soc_device poodle_snd_devdata = { | 293 | static struct snd_soc_device poodle_snd_devdata = { |
293 | .machine = &snd_soc_machine_poodle, | 294 | .card = &snd_soc_poodle, |
294 | .platform = &pxa2xx_soc_platform, | ||
295 | .codec_dev = &soc_codec_dev_wm8731, | 295 | .codec_dev = &soc_codec_dev_wm8731, |
296 | .codec_data = &poodle_wm8731_setup, | 296 | .codec_data = &poodle_wm8731_setup, |
297 | }; | 297 | }; |
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c new file mode 100644 index 000000000000..73cb6b4c2f2d --- /dev/null +++ b/sound/soc/pxa/pxa-ssp.c | |||
@@ -0,0 +1,931 @@ | |||
1 | #define DEBUG | ||
2 | /* | ||
3 | * pxa-ssp.c -- ALSA Soc Audio Layer | ||
4 | * | ||
5 | * Copyright 2005,2008 Wolfson Microelectronics PLC. | ||
6 | * Author: Liam Girdwood | ||
7 | * Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | * | ||
14 | * TODO: | ||
15 | * o Test network mode for > 16bit sample size | ||
16 | */ | ||
17 | |||
18 | #include <linux/init.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/clk.h> | ||
22 | #include <linux/io.h> | ||
23 | |||
24 | #include <sound/core.h> | ||
25 | #include <sound/pcm.h> | ||
26 | #include <sound/initval.h> | ||
27 | #include <sound/pcm_params.h> | ||
28 | #include <sound/soc.h> | ||
29 | #include <sound/pxa2xx-lib.h> | ||
30 | |||
31 | #include <mach/hardware.h> | ||
32 | #include <mach/pxa-regs.h> | ||
33 | #include <mach/regs-ssp.h> | ||
34 | #include <mach/audio.h> | ||
35 | #include <mach/ssp.h> | ||
36 | |||
37 | #include "pxa2xx-pcm.h" | ||
38 | #include "pxa-ssp.h" | ||
39 | |||
40 | /* | ||
41 | * SSP audio private data | ||
42 | */ | ||
43 | struct ssp_priv { | ||
44 | struct ssp_dev dev; | ||
45 | unsigned int sysclk; | ||
46 | int dai_fmt; | ||
47 | #ifdef CONFIG_PM | ||
48 | struct ssp_state state; | ||
49 | #endif | ||
50 | }; | ||
51 | |||
52 | #define PXA2xx_SSP1_BASE 0x41000000 | ||
53 | #define PXA27x_SSP2_BASE 0x41700000 | ||
54 | #define PXA27x_SSP3_BASE 0x41900000 | ||
55 | #define PXA3xx_SSP4_BASE 0x41a00000 | ||
56 | |||
57 | static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_mono_out = { | ||
58 | .name = "SSP1 PCM Mono out", | ||
59 | .dev_addr = PXA2xx_SSP1_BASE + SSDR, | ||
60 | .drcmr = &DRCMR(14), | ||
61 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | | ||
62 | DCMD_BURST16 | DCMD_WIDTH2, | ||
63 | }; | ||
64 | |||
65 | static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_mono_in = { | ||
66 | .name = "SSP1 PCM Mono in", | ||
67 | .dev_addr = PXA2xx_SSP1_BASE + SSDR, | ||
68 | .drcmr = &DRCMR(13), | ||
69 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | ||
70 | DCMD_BURST16 | DCMD_WIDTH2, | ||
71 | }; | ||
72 | |||
73 | static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_stereo_out = { | ||
74 | .name = "SSP1 PCM Stereo out", | ||
75 | .dev_addr = PXA2xx_SSP1_BASE + SSDR, | ||
76 | .drcmr = &DRCMR(14), | ||
77 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | | ||
78 | DCMD_BURST16 | DCMD_WIDTH4, | ||
79 | }; | ||
80 | |||
81 | static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_stereo_in = { | ||
82 | .name = "SSP1 PCM Stereo in", | ||
83 | .dev_addr = PXA2xx_SSP1_BASE + SSDR, | ||
84 | .drcmr = &DRCMR(13), | ||
85 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | ||
86 | DCMD_BURST16 | DCMD_WIDTH4, | ||
87 | }; | ||
88 | |||
89 | static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_mono_out = { | ||
90 | .name = "SSP2 PCM Mono out", | ||
91 | .dev_addr = PXA27x_SSP2_BASE + SSDR, | ||
92 | .drcmr = &DRCMR(16), | ||
93 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | | ||
94 | DCMD_BURST16 | DCMD_WIDTH2, | ||
95 | }; | ||
96 | |||
97 | static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_mono_in = { | ||
98 | .name = "SSP2 PCM Mono in", | ||
99 | .dev_addr = PXA27x_SSP2_BASE + SSDR, | ||
100 | .drcmr = &DRCMR(15), | ||
101 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | ||
102 | DCMD_BURST16 | DCMD_WIDTH2, | ||
103 | }; | ||
104 | |||
105 | static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_stereo_out = { | ||
106 | .name = "SSP2 PCM Stereo out", | ||
107 | .dev_addr = PXA27x_SSP2_BASE + SSDR, | ||
108 | .drcmr = &DRCMR(16), | ||
109 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | | ||
110 | DCMD_BURST16 | DCMD_WIDTH4, | ||
111 | }; | ||
112 | |||
113 | static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_stereo_in = { | ||
114 | .name = "SSP2 PCM Stereo in", | ||
115 | .dev_addr = PXA27x_SSP2_BASE + SSDR, | ||
116 | .drcmr = &DRCMR(15), | ||
117 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | ||
118 | DCMD_BURST16 | DCMD_WIDTH4, | ||
119 | }; | ||
120 | |||
121 | static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_mono_out = { | ||
122 | .name = "SSP3 PCM Mono out", | ||
123 | .dev_addr = PXA27x_SSP3_BASE + SSDR, | ||
124 | .drcmr = &DRCMR(67), | ||
125 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | | ||
126 | DCMD_BURST16 | DCMD_WIDTH2, | ||
127 | }; | ||
128 | |||
129 | static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_mono_in = { | ||
130 | .name = "SSP3 PCM Mono in", | ||
131 | .dev_addr = PXA27x_SSP3_BASE + SSDR, | ||
132 | .drcmr = &DRCMR(66), | ||
133 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | ||
134 | DCMD_BURST16 | DCMD_WIDTH2, | ||
135 | }; | ||
136 | |||
137 | static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_stereo_out = { | ||
138 | .name = "SSP3 PCM Stereo out", | ||
139 | .dev_addr = PXA27x_SSP3_BASE + SSDR, | ||
140 | .drcmr = &DRCMR(67), | ||
141 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | | ||
142 | DCMD_BURST16 | DCMD_WIDTH4, | ||
143 | }; | ||
144 | |||
145 | static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_stereo_in = { | ||
146 | .name = "SSP3 PCM Stereo in", | ||
147 | .dev_addr = PXA27x_SSP3_BASE + SSDR, | ||
148 | .drcmr = &DRCMR(66), | ||
149 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | ||
150 | DCMD_BURST16 | DCMD_WIDTH4, | ||
151 | }; | ||
152 | |||
153 | static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_mono_out = { | ||
154 | .name = "SSP4 PCM Mono out", | ||
155 | .dev_addr = PXA3xx_SSP4_BASE + SSDR, | ||
156 | .drcmr = &DRCMR(67), | ||
157 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | | ||
158 | DCMD_BURST16 | DCMD_WIDTH2, | ||
159 | }; | ||
160 | |||
161 | static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_mono_in = { | ||
162 | .name = "SSP4 PCM Mono in", | ||
163 | .dev_addr = PXA3xx_SSP4_BASE + SSDR, | ||
164 | .drcmr = &DRCMR(66), | ||
165 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | ||
166 | DCMD_BURST16 | DCMD_WIDTH2, | ||
167 | }; | ||
168 | |||
169 | static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_stereo_out = { | ||
170 | .name = "SSP4 PCM Stereo out", | ||
171 | .dev_addr = PXA3xx_SSP4_BASE + SSDR, | ||
172 | .drcmr = &DRCMR(67), | ||
173 | .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | | ||
174 | DCMD_BURST16 | DCMD_WIDTH4, | ||
175 | }; | ||
176 | |||
177 | static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_stereo_in = { | ||
178 | .name = "SSP4 PCM Stereo in", | ||
179 | .dev_addr = PXA3xx_SSP4_BASE + SSDR, | ||
180 | .drcmr = &DRCMR(66), | ||
181 | .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | | ||
182 | DCMD_BURST16 | DCMD_WIDTH4, | ||
183 | }; | ||
184 | |||
185 | static void dump_registers(struct ssp_device *ssp) | ||
186 | { | ||
187 | dev_dbg(&ssp->pdev->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n", | ||
188 | ssp_read_reg(ssp, SSCR0), ssp_read_reg(ssp, SSCR1), | ||
189 | ssp_read_reg(ssp, SSTO)); | ||
190 | |||
191 | dev_dbg(&ssp->pdev->dev, "SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x\n", | ||
192 | ssp_read_reg(ssp, SSPSP), ssp_read_reg(ssp, SSSR), | ||
193 | ssp_read_reg(ssp, SSACD)); | ||
194 | } | ||
195 | |||
196 | static struct pxa2xx_pcm_dma_params *ssp_dma_params[4][4] = { | ||
197 | { | ||
198 | &pxa_ssp1_pcm_mono_out, &pxa_ssp1_pcm_mono_in, | ||
199 | &pxa_ssp1_pcm_stereo_out, &pxa_ssp1_pcm_stereo_in, | ||
200 | }, | ||
201 | { | ||
202 | &pxa_ssp2_pcm_mono_out, &pxa_ssp2_pcm_mono_in, | ||
203 | &pxa_ssp2_pcm_stereo_out, &pxa_ssp2_pcm_stereo_in, | ||
204 | }, | ||
205 | { | ||
206 | &pxa_ssp3_pcm_mono_out, &pxa_ssp3_pcm_mono_in, | ||
207 | &pxa_ssp3_pcm_stereo_out, &pxa_ssp3_pcm_stereo_in, | ||
208 | }, | ||
209 | { | ||
210 | &pxa_ssp4_pcm_mono_out, &pxa_ssp4_pcm_mono_in, | ||
211 | &pxa_ssp4_pcm_stereo_out, &pxa_ssp4_pcm_stereo_in, | ||
212 | }, | ||
213 | }; | ||
214 | |||
215 | static int pxa_ssp_startup(struct snd_pcm_substream *substream, | ||
216 | struct snd_soc_dai *dai) | ||
217 | { | ||
218 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
219 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
220 | struct ssp_priv *priv = cpu_dai->private_data; | ||
221 | int ret = 0; | ||
222 | |||
223 | if (!cpu_dai->active) { | ||
224 | ret = ssp_init(&priv->dev, cpu_dai->id + 1, SSP_NO_IRQ); | ||
225 | if (ret < 0) | ||
226 | return ret; | ||
227 | ssp_disable(&priv->dev); | ||
228 | } | ||
229 | return ret; | ||
230 | } | ||
231 | |||
232 | static void pxa_ssp_shutdown(struct snd_pcm_substream *substream, | ||
233 | struct snd_soc_dai *dai) | ||
234 | { | ||
235 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
236 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
237 | struct ssp_priv *priv = cpu_dai->private_data; | ||
238 | |||
239 | if (!cpu_dai->active) { | ||
240 | ssp_disable(&priv->dev); | ||
241 | ssp_exit(&priv->dev); | ||
242 | } | ||
243 | } | ||
244 | |||
245 | #ifdef CONFIG_PM | ||
246 | |||
247 | static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai) | ||
248 | { | ||
249 | struct ssp_priv *priv = cpu_dai->private_data; | ||
250 | |||
251 | if (!cpu_dai->active) | ||
252 | return 0; | ||
253 | |||
254 | ssp_save_state(&priv->dev, &priv->state); | ||
255 | clk_disable(priv->dev.ssp->clk); | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai) | ||
260 | { | ||
261 | struct ssp_priv *priv = cpu_dai->private_data; | ||
262 | |||
263 | if (!cpu_dai->active) | ||
264 | return 0; | ||
265 | |||
266 | clk_enable(priv->dev.ssp->clk); | ||
267 | ssp_restore_state(&priv->dev, &priv->state); | ||
268 | ssp_enable(&priv->dev); | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | #else | ||
274 | #define pxa_ssp_suspend NULL | ||
275 | #define pxa_ssp_resume NULL | ||
276 | #endif | ||
277 | |||
278 | /** | ||
279 | * ssp_set_clkdiv - set SSP clock divider | ||
280 | * @div: serial clock rate divider | ||
281 | */ | ||
282 | static void ssp_set_scr(struct ssp_dev *dev, u32 div) | ||
283 | { | ||
284 | struct ssp_device *ssp = dev->ssp; | ||
285 | u32 sscr0 = ssp_read_reg(dev->ssp, SSCR0) & ~SSCR0_SCR; | ||
286 | |||
287 | ssp_write_reg(ssp, SSCR0, (sscr0 | SSCR0_SerClkDiv(div))); | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * Set the SSP ports SYSCLK. | ||
292 | */ | ||
293 | static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | ||
294 | int clk_id, unsigned int freq, int dir) | ||
295 | { | ||
296 | struct ssp_priv *priv = cpu_dai->private_data; | ||
297 | struct ssp_device *ssp = priv->dev.ssp; | ||
298 | int val; | ||
299 | |||
300 | u32 sscr0 = ssp_read_reg(ssp, SSCR0) & | ||
301 | ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ADC); | ||
302 | |||
303 | dev_dbg(&ssp->pdev->dev, | ||
304 | "pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %d\n", | ||
305 | cpu_dai->id, clk_id, freq); | ||
306 | |||
307 | switch (clk_id) { | ||
308 | case PXA_SSP_CLK_NET_PLL: | ||
309 | sscr0 |= SSCR0_MOD; | ||
310 | break; | ||
311 | case PXA_SSP_CLK_PLL: | ||
312 | /* Internal PLL is fixed */ | ||
313 | if (cpu_is_pxa25x()) | ||
314 | priv->sysclk = 1843200; | ||
315 | else | ||
316 | priv->sysclk = 13000000; | ||
317 | break; | ||
318 | case PXA_SSP_CLK_EXT: | ||
319 | priv->sysclk = freq; | ||
320 | sscr0 |= SSCR0_ECS; | ||
321 | break; | ||
322 | case PXA_SSP_CLK_NET: | ||
323 | priv->sysclk = freq; | ||
324 | sscr0 |= SSCR0_NCS | SSCR0_MOD; | ||
325 | break; | ||
326 | case PXA_SSP_CLK_AUDIO: | ||
327 | priv->sysclk = 0; | ||
328 | ssp_set_scr(&priv->dev, 1); | ||
329 | sscr0 |= SSCR0_ADC; | ||
330 | break; | ||
331 | default: | ||
332 | return -ENODEV; | ||
333 | } | ||
334 | |||
335 | /* The SSP clock must be disabled when changing SSP clock mode | ||
336 | * on PXA2xx. On PXA3xx it must be enabled when doing so. */ | ||
337 | if (!cpu_is_pxa3xx()) | ||
338 | clk_disable(priv->dev.ssp->clk); | ||
339 | val = ssp_read_reg(ssp, SSCR0) | sscr0; | ||
340 | ssp_write_reg(ssp, SSCR0, val); | ||
341 | if (!cpu_is_pxa3xx()) | ||
342 | clk_enable(priv->dev.ssp->clk); | ||
343 | |||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | /* | ||
348 | * Set the SSP clock dividers. | ||
349 | */ | ||
350 | static int pxa_ssp_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, | ||
351 | int div_id, int div) | ||
352 | { | ||
353 | struct ssp_priv *priv = cpu_dai->private_data; | ||
354 | struct ssp_device *ssp = priv->dev.ssp; | ||
355 | int val; | ||
356 | |||
357 | switch (div_id) { | ||
358 | case PXA_SSP_AUDIO_DIV_ACDS: | ||
359 | val = (ssp_read_reg(ssp, SSACD) & ~0x7) | SSACD_ACDS(div); | ||
360 | ssp_write_reg(ssp, SSACD, val); | ||
361 | break; | ||
362 | case PXA_SSP_AUDIO_DIV_SCDB: | ||
363 | val = ssp_read_reg(ssp, SSACD); | ||
364 | val &= ~SSACD_SCDB; | ||
365 | #if defined(CONFIG_PXA3xx) | ||
366 | if (cpu_is_pxa3xx()) | ||
367 | val &= ~SSACD_SCDX8; | ||
368 | #endif | ||
369 | switch (div) { | ||
370 | case PXA_SSP_CLK_SCDB_1: | ||
371 | val |= SSACD_SCDB; | ||
372 | break; | ||
373 | case PXA_SSP_CLK_SCDB_4: | ||
374 | break; | ||
375 | #if defined(CONFIG_PXA3xx) | ||
376 | case PXA_SSP_CLK_SCDB_8: | ||
377 | if (cpu_is_pxa3xx()) | ||
378 | val |= SSACD_SCDX8; | ||
379 | else | ||
380 | return -EINVAL; | ||
381 | break; | ||
382 | #endif | ||
383 | default: | ||
384 | return -EINVAL; | ||
385 | } | ||
386 | ssp_write_reg(ssp, SSACD, val); | ||
387 | break; | ||
388 | case PXA_SSP_DIV_SCR: | ||
389 | ssp_set_scr(&priv->dev, div); | ||
390 | break; | ||
391 | default: | ||
392 | return -ENODEV; | ||
393 | } | ||
394 | |||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | /* | ||
399 | * Configure the PLL frequency pxa27x and (afaik - pxa320 only) | ||
400 | */ | ||
401 | static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, | ||
402 | int pll_id, unsigned int freq_in, unsigned int freq_out) | ||
403 | { | ||
404 | struct ssp_priv *priv = cpu_dai->private_data; | ||
405 | struct ssp_device *ssp = priv->dev.ssp; | ||
406 | u32 ssacd = ssp_read_reg(ssp, SSACD) & ~0x70; | ||
407 | |||
408 | #if defined(CONFIG_PXA3xx) | ||
409 | if (cpu_is_pxa3xx()) | ||
410 | ssp_write_reg(ssp, SSACDD, 0); | ||
411 | #endif | ||
412 | |||
413 | switch (freq_out) { | ||
414 | case 5622000: | ||
415 | break; | ||
416 | case 11345000: | ||
417 | ssacd |= (0x1 << 4); | ||
418 | break; | ||
419 | case 12235000: | ||
420 | ssacd |= (0x2 << 4); | ||
421 | break; | ||
422 | case 14857000: | ||
423 | ssacd |= (0x3 << 4); | ||
424 | break; | ||
425 | case 32842000: | ||
426 | ssacd |= (0x4 << 4); | ||
427 | break; | ||
428 | case 48000000: | ||
429 | ssacd |= (0x5 << 4); | ||
430 | break; | ||
431 | case 0: | ||
432 | /* Disable */ | ||
433 | break; | ||
434 | |||
435 | default: | ||
436 | #ifdef CONFIG_PXA3xx | ||
437 | /* PXA3xx has a clock ditherer which can be used to generate | ||
438 | * a wider range of frequencies - calculate a value for it. | ||
439 | */ | ||
440 | if (cpu_is_pxa3xx()) { | ||
441 | u32 val; | ||
442 | u64 tmp = 19968; | ||
443 | tmp *= 1000000; | ||
444 | do_div(tmp, freq_out); | ||
445 | val = tmp; | ||
446 | |||
447 | val = (val << 16) | 64;; | ||
448 | ssp_write_reg(ssp, SSACDD, val); | ||
449 | |||
450 | ssacd |= (0x6 << 4); | ||
451 | |||
452 | dev_dbg(&ssp->pdev->dev, | ||
453 | "Using SSACDD %x to supply %dHz\n", | ||
454 | val, freq_out); | ||
455 | break; | ||
456 | } | ||
457 | #endif | ||
458 | |||
459 | return -EINVAL; | ||
460 | } | ||
461 | |||
462 | ssp_write_reg(ssp, SSACD, ssacd); | ||
463 | |||
464 | return 0; | ||
465 | } | ||
466 | |||
467 | /* | ||
468 | * Set the active slots in TDM/Network mode | ||
469 | */ | ||
470 | static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, | ||
471 | unsigned int mask, int slots) | ||
472 | { | ||
473 | struct ssp_priv *priv = cpu_dai->private_data; | ||
474 | struct ssp_device *ssp = priv->dev.ssp; | ||
475 | u32 sscr0; | ||
476 | |||
477 | sscr0 = ssp_read_reg(ssp, SSCR0) & ~SSCR0_SlotsPerFrm(7); | ||
478 | |||
479 | /* set number of active slots */ | ||
480 | sscr0 |= SSCR0_SlotsPerFrm(slots); | ||
481 | ssp_write_reg(ssp, SSCR0, sscr0); | ||
482 | |||
483 | /* set active slot mask */ | ||
484 | ssp_write_reg(ssp, SSTSA, mask); | ||
485 | ssp_write_reg(ssp, SSRSA, mask); | ||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | /* | ||
490 | * Tristate the SSP DAI lines | ||
491 | */ | ||
492 | static int pxa_ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai, | ||
493 | int tristate) | ||
494 | { | ||
495 | struct ssp_priv *priv = cpu_dai->private_data; | ||
496 | struct ssp_device *ssp = priv->dev.ssp; | ||
497 | u32 sscr1; | ||
498 | |||
499 | sscr1 = ssp_read_reg(ssp, SSCR1); | ||
500 | if (tristate) | ||
501 | sscr1 &= ~SSCR1_TTE; | ||
502 | else | ||
503 | sscr1 |= SSCR1_TTE; | ||
504 | ssp_write_reg(ssp, SSCR1, sscr1); | ||
505 | |||
506 | return 0; | ||
507 | } | ||
508 | |||
509 | /* | ||
510 | * Set up the SSP DAI format. | ||
511 | * The SSP Port must be inactive before calling this function as the | ||
512 | * physical interface format is changed. | ||
513 | */ | ||
514 | static int pxa_ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, | ||
515 | unsigned int fmt) | ||
516 | { | ||
517 | struct ssp_priv *priv = cpu_dai->private_data; | ||
518 | struct ssp_device *ssp = priv->dev.ssp; | ||
519 | u32 sscr0; | ||
520 | u32 sscr1; | ||
521 | u32 sspsp; | ||
522 | |||
523 | /* reset port settings */ | ||
524 | sscr0 = ssp_read_reg(ssp, SSCR0) & | ||
525 | (SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ADC); | ||
526 | sscr1 = SSCR1_RxTresh(8) | SSCR1_TxTresh(7); | ||
527 | sspsp = 0; | ||
528 | |||
529 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
530 | case SND_SOC_DAIFMT_CBM_CFM: | ||
531 | sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR; | ||
532 | break; | ||
533 | case SND_SOC_DAIFMT_CBM_CFS: | ||
534 | sscr1 |= SSCR1_SCLKDIR; | ||
535 | break; | ||
536 | case SND_SOC_DAIFMT_CBS_CFS: | ||
537 | break; | ||
538 | default: | ||
539 | return -EINVAL; | ||
540 | } | ||
541 | |||
542 | ssp_write_reg(ssp, SSCR0, sscr0); | ||
543 | ssp_write_reg(ssp, SSCR1, sscr1); | ||
544 | ssp_write_reg(ssp, SSPSP, sspsp); | ||
545 | |||
546 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
547 | case SND_SOC_DAIFMT_I2S: | ||
548 | sscr0 |= SSCR0_MOD | SSCR0_PSP; | ||
549 | sscr1 |= SSCR1_RWOT | SSCR1_TRAIL; | ||
550 | |||
551 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
552 | case SND_SOC_DAIFMT_NB_NF: | ||
553 | sspsp |= SSPSP_FSRT; | ||
554 | break; | ||
555 | case SND_SOC_DAIFMT_NB_IF: | ||
556 | sspsp |= SSPSP_SFRMP | SSPSP_FSRT; | ||
557 | break; | ||
558 | case SND_SOC_DAIFMT_IB_IF: | ||
559 | sspsp |= SSPSP_SFRMP; | ||
560 | break; | ||
561 | default: | ||
562 | return -EINVAL; | ||
563 | } | ||
564 | break; | ||
565 | |||
566 | case SND_SOC_DAIFMT_DSP_A: | ||
567 | sspsp |= SSPSP_FSRT; | ||
568 | case SND_SOC_DAIFMT_DSP_B: | ||
569 | sscr0 |= SSCR0_MOD | SSCR0_PSP; | ||
570 | sscr1 |= SSCR1_TRAIL | SSCR1_RWOT; | ||
571 | |||
572 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
573 | case SND_SOC_DAIFMT_NB_NF: | ||
574 | sspsp |= SSPSP_SFRMP; | ||
575 | break; | ||
576 | case SND_SOC_DAIFMT_IB_IF: | ||
577 | break; | ||
578 | default: | ||
579 | return -EINVAL; | ||
580 | } | ||
581 | break; | ||
582 | |||
583 | default: | ||
584 | return -EINVAL; | ||
585 | } | ||
586 | |||
587 | ssp_write_reg(ssp, SSCR0, sscr0); | ||
588 | ssp_write_reg(ssp, SSCR1, sscr1); | ||
589 | ssp_write_reg(ssp, SSPSP, sspsp); | ||
590 | |||
591 | dump_registers(ssp); | ||
592 | |||
593 | /* Since we are configuring the timings for the format by hand | ||
594 | * we have to defer some things until hw_params() where we | ||
595 | * know parameters like the sample size. | ||
596 | */ | ||
597 | priv->dai_fmt = fmt; | ||
598 | |||
599 | return 0; | ||
600 | } | ||
601 | |||
602 | /* | ||
603 | * Set the SSP audio DMA parameters and sample size. | ||
604 | * Can be called multiple times by oss emulation. | ||
605 | */ | ||
606 | static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, | ||
607 | struct snd_pcm_hw_params *params, | ||
608 | struct snd_soc_dai *dai) | ||
609 | { | ||
610 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
611 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
612 | struct ssp_priv *priv = cpu_dai->private_data; | ||
613 | struct ssp_device *ssp = priv->dev.ssp; | ||
614 | int dma = 0, chn = params_channels(params); | ||
615 | u32 sscr0; | ||
616 | u32 sspsp; | ||
617 | int width = snd_pcm_format_physical_width(params_format(params)); | ||
618 | |||
619 | /* select correct DMA params */ | ||
620 | if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) | ||
621 | dma = 1; /* capture DMA offset is 1,3 */ | ||
622 | if (chn == 2) | ||
623 | dma += 2; /* stereo DMA offset is 2, mono is 0 */ | ||
624 | cpu_dai->dma_data = ssp_dma_params[cpu_dai->id][dma]; | ||
625 | |||
626 | dev_dbg(&ssp->pdev->dev, "pxa_ssp_hw_params: dma %d\n", dma); | ||
627 | |||
628 | /* we can only change the settings if the port is not in use */ | ||
629 | if (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) | ||
630 | return 0; | ||
631 | |||
632 | /* clear selected SSP bits */ | ||
633 | sscr0 = ssp_read_reg(ssp, SSCR0) & ~(SSCR0_DSS | SSCR0_EDSS); | ||
634 | ssp_write_reg(ssp, SSCR0, sscr0); | ||
635 | |||
636 | /* bit size */ | ||
637 | sscr0 = ssp_read_reg(ssp, SSCR0); | ||
638 | switch (params_format(params)) { | ||
639 | case SNDRV_PCM_FORMAT_S16_LE: | ||
640 | #ifdef CONFIG_PXA3xx | ||
641 | if (cpu_is_pxa3xx()) | ||
642 | sscr0 |= SSCR0_FPCKE; | ||
643 | #endif | ||
644 | sscr0 |= SSCR0_DataSize(16); | ||
645 | if (params_channels(params) > 1) | ||
646 | sscr0 |= SSCR0_EDSS; | ||
647 | break; | ||
648 | case SNDRV_PCM_FORMAT_S24_LE: | ||
649 | sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(8)); | ||
650 | /* we must be in network mode (2 slots) for 24 bit stereo */ | ||
651 | break; | ||
652 | case SNDRV_PCM_FORMAT_S32_LE: | ||
653 | sscr0 |= (SSCR0_EDSS | SSCR0_DataSize(16)); | ||
654 | /* we must be in network mode (2 slots) for 32 bit stereo */ | ||
655 | break; | ||
656 | } | ||
657 | ssp_write_reg(ssp, SSCR0, sscr0); | ||
658 | |||
659 | switch (priv->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
660 | case SND_SOC_DAIFMT_I2S: | ||
661 | /* Cleared when the DAI format is set */ | ||
662 | sspsp = ssp_read_reg(ssp, SSPSP) | SSPSP_SFRMWDTH(width); | ||
663 | ssp_write_reg(ssp, SSPSP, sspsp); | ||
664 | break; | ||
665 | default: | ||
666 | break; | ||
667 | } | ||
668 | |||
669 | /* We always use a network mode so we always require TDM slots | ||
670 | * - complain loudly and fail if they've not been set up yet. | ||
671 | */ | ||
672 | if (!(ssp_read_reg(ssp, SSTSA) & 0xf)) { | ||
673 | dev_err(&ssp->pdev->dev, "No TDM timeslot configured\n"); | ||
674 | return -EINVAL; | ||
675 | } | ||
676 | |||
677 | dump_registers(ssp); | ||
678 | |||
679 | return 0; | ||
680 | } | ||
681 | |||
682 | static int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd, | ||
683 | struct snd_soc_dai *dai) | ||
684 | { | ||
685 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
686 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
687 | int ret = 0; | ||
688 | struct ssp_priv *priv = cpu_dai->private_data; | ||
689 | struct ssp_device *ssp = priv->dev.ssp; | ||
690 | int val; | ||
691 | |||
692 | switch (cmd) { | ||
693 | case SNDRV_PCM_TRIGGER_RESUME: | ||
694 | ssp_enable(&priv->dev); | ||
695 | break; | ||
696 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
697 | val = ssp_read_reg(ssp, SSCR1); | ||
698 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
699 | val |= SSCR1_TSRE; | ||
700 | else | ||
701 | val |= SSCR1_RSRE; | ||
702 | ssp_write_reg(ssp, SSCR1, val); | ||
703 | val = ssp_read_reg(ssp, SSSR); | ||
704 | ssp_write_reg(ssp, SSSR, val); | ||
705 | break; | ||
706 | case SNDRV_PCM_TRIGGER_START: | ||
707 | val = ssp_read_reg(ssp, SSCR1); | ||
708 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
709 | val |= SSCR1_TSRE; | ||
710 | else | ||
711 | val |= SSCR1_RSRE; | ||
712 | ssp_write_reg(ssp, SSCR1, val); | ||
713 | ssp_enable(&priv->dev); | ||
714 | break; | ||
715 | case SNDRV_PCM_TRIGGER_STOP: | ||
716 | val = ssp_read_reg(ssp, SSCR1); | ||
717 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
718 | val &= ~SSCR1_TSRE; | ||
719 | else | ||
720 | val &= ~SSCR1_RSRE; | ||
721 | ssp_write_reg(ssp, SSCR1, val); | ||
722 | break; | ||
723 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
724 | ssp_disable(&priv->dev); | ||
725 | break; | ||
726 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
727 | val = ssp_read_reg(ssp, SSCR1); | ||
728 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
729 | val &= ~SSCR1_TSRE; | ||
730 | else | ||
731 | val &= ~SSCR1_RSRE; | ||
732 | ssp_write_reg(ssp, SSCR1, val); | ||
733 | break; | ||
734 | |||
735 | default: | ||
736 | ret = -EINVAL; | ||
737 | } | ||
738 | |||
739 | dump_registers(ssp); | ||
740 | |||
741 | return ret; | ||
742 | } | ||
743 | |||
744 | static int pxa_ssp_probe(struct platform_device *pdev, | ||
745 | struct snd_soc_dai *dai) | ||
746 | { | ||
747 | struct ssp_priv *priv; | ||
748 | int ret; | ||
749 | |||
750 | priv = kzalloc(sizeof(struct ssp_priv), GFP_KERNEL); | ||
751 | if (!priv) | ||
752 | return -ENOMEM; | ||
753 | |||
754 | priv->dev.ssp = ssp_request(dai->id, "SoC audio"); | ||
755 | if (priv->dev.ssp == NULL) { | ||
756 | ret = -ENODEV; | ||
757 | goto err_priv; | ||
758 | } | ||
759 | |||
760 | dai->private_data = priv; | ||
761 | |||
762 | return 0; | ||
763 | |||
764 | err_priv: | ||
765 | kfree(priv); | ||
766 | return ret; | ||
767 | } | ||
768 | |||
769 | static void pxa_ssp_remove(struct platform_device *pdev, | ||
770 | struct snd_soc_dai *dai) | ||
771 | { | ||
772 | struct ssp_priv *priv = dai->private_data; | ||
773 | ssp_free(priv->dev.ssp); | ||
774 | } | ||
775 | |||
776 | #define PXA_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ | ||
777 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ | ||
778 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ | ||
779 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) | ||
780 | |||
781 | #define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ | ||
782 | SNDRV_PCM_FMTBIT_S24_LE | \ | ||
783 | SNDRV_PCM_FMTBIT_S32_LE) | ||
784 | |||
785 | struct snd_soc_dai pxa_ssp_dai[] = { | ||
786 | { | ||
787 | .name = "pxa2xx-ssp1", | ||
788 | .id = 0, | ||
789 | .probe = pxa_ssp_probe, | ||
790 | .remove = pxa_ssp_remove, | ||
791 | .suspend = pxa_ssp_suspend, | ||
792 | .resume = pxa_ssp_resume, | ||
793 | .playback = { | ||
794 | .channels_min = 1, | ||
795 | .channels_max = 2, | ||
796 | .rates = PXA_SSP_RATES, | ||
797 | .formats = PXA_SSP_FORMATS, | ||
798 | }, | ||
799 | .capture = { | ||
800 | .channels_min = 1, | ||
801 | .channels_max = 2, | ||
802 | .rates = PXA_SSP_RATES, | ||
803 | .formats = PXA_SSP_FORMATS, | ||
804 | }, | ||
805 | .ops = { | ||
806 | .startup = pxa_ssp_startup, | ||
807 | .shutdown = pxa_ssp_shutdown, | ||
808 | .trigger = pxa_ssp_trigger, | ||
809 | .hw_params = pxa_ssp_hw_params, | ||
810 | .set_sysclk = pxa_ssp_set_dai_sysclk, | ||
811 | .set_clkdiv = pxa_ssp_set_dai_clkdiv, | ||
812 | .set_pll = pxa_ssp_set_dai_pll, | ||
813 | .set_fmt = pxa_ssp_set_dai_fmt, | ||
814 | .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, | ||
815 | .set_tristate = pxa_ssp_set_dai_tristate, | ||
816 | }, | ||
817 | }, | ||
818 | { .name = "pxa2xx-ssp2", | ||
819 | .id = 1, | ||
820 | .probe = pxa_ssp_probe, | ||
821 | .remove = pxa_ssp_remove, | ||
822 | .suspend = pxa_ssp_suspend, | ||
823 | .resume = pxa_ssp_resume, | ||
824 | .playback = { | ||
825 | .channels_min = 1, | ||
826 | .channels_max = 2, | ||
827 | .rates = PXA_SSP_RATES, | ||
828 | .formats = PXA_SSP_FORMATS, | ||
829 | }, | ||
830 | .capture = { | ||
831 | .channels_min = 1, | ||
832 | .channels_max = 2, | ||
833 | .rates = PXA_SSP_RATES, | ||
834 | .formats = PXA_SSP_FORMATS, | ||
835 | }, | ||
836 | .ops = { | ||
837 | .startup = pxa_ssp_startup, | ||
838 | .shutdown = pxa_ssp_shutdown, | ||
839 | .trigger = pxa_ssp_trigger, | ||
840 | .hw_params = pxa_ssp_hw_params, | ||
841 | .set_sysclk = pxa_ssp_set_dai_sysclk, | ||
842 | .set_clkdiv = pxa_ssp_set_dai_clkdiv, | ||
843 | .set_pll = pxa_ssp_set_dai_pll, | ||
844 | .set_fmt = pxa_ssp_set_dai_fmt, | ||
845 | .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, | ||
846 | .set_tristate = pxa_ssp_set_dai_tristate, | ||
847 | }, | ||
848 | }, | ||
849 | { | ||
850 | .name = "pxa2xx-ssp3", | ||
851 | .id = 2, | ||
852 | .probe = pxa_ssp_probe, | ||
853 | .remove = pxa_ssp_remove, | ||
854 | .suspend = pxa_ssp_suspend, | ||
855 | .resume = pxa_ssp_resume, | ||
856 | .playback = { | ||
857 | .channels_min = 1, | ||
858 | .channels_max = 2, | ||
859 | .rates = PXA_SSP_RATES, | ||
860 | .formats = PXA_SSP_FORMATS, | ||
861 | }, | ||
862 | .capture = { | ||
863 | .channels_min = 1, | ||
864 | .channels_max = 2, | ||
865 | .rates = PXA_SSP_RATES, | ||
866 | .formats = PXA_SSP_FORMATS, | ||
867 | }, | ||
868 | .ops = { | ||
869 | .startup = pxa_ssp_startup, | ||
870 | .shutdown = pxa_ssp_shutdown, | ||
871 | .trigger = pxa_ssp_trigger, | ||
872 | .hw_params = pxa_ssp_hw_params, | ||
873 | .set_sysclk = pxa_ssp_set_dai_sysclk, | ||
874 | .set_clkdiv = pxa_ssp_set_dai_clkdiv, | ||
875 | .set_pll = pxa_ssp_set_dai_pll, | ||
876 | .set_fmt = pxa_ssp_set_dai_fmt, | ||
877 | .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, | ||
878 | .set_tristate = pxa_ssp_set_dai_tristate, | ||
879 | }, | ||
880 | }, | ||
881 | { | ||
882 | .name = "pxa2xx-ssp4", | ||
883 | .id = 3, | ||
884 | .probe = pxa_ssp_probe, | ||
885 | .remove = pxa_ssp_remove, | ||
886 | .suspend = pxa_ssp_suspend, | ||
887 | .resume = pxa_ssp_resume, | ||
888 | .playback = { | ||
889 | .channels_min = 1, | ||
890 | .channels_max = 2, | ||
891 | .rates = PXA_SSP_RATES, | ||
892 | .formats = PXA_SSP_FORMATS, | ||
893 | }, | ||
894 | .capture = { | ||
895 | .channels_min = 1, | ||
896 | .channels_max = 2, | ||
897 | .rates = PXA_SSP_RATES, | ||
898 | .formats = PXA_SSP_FORMATS, | ||
899 | }, | ||
900 | .ops = { | ||
901 | .startup = pxa_ssp_startup, | ||
902 | .shutdown = pxa_ssp_shutdown, | ||
903 | .trigger = pxa_ssp_trigger, | ||
904 | .hw_params = pxa_ssp_hw_params, | ||
905 | .set_sysclk = pxa_ssp_set_dai_sysclk, | ||
906 | .set_clkdiv = pxa_ssp_set_dai_clkdiv, | ||
907 | .set_pll = pxa_ssp_set_dai_pll, | ||
908 | .set_fmt = pxa_ssp_set_dai_fmt, | ||
909 | .set_tdm_slot = pxa_ssp_set_dai_tdm_slot, | ||
910 | .set_tristate = pxa_ssp_set_dai_tristate, | ||
911 | }, | ||
912 | }, | ||
913 | }; | ||
914 | EXPORT_SYMBOL_GPL(pxa_ssp_dai); | ||
915 | |||
916 | static int __init pxa_ssp_init(void) | ||
917 | { | ||
918 | return snd_soc_register_dais(pxa_ssp_dai, ARRAY_SIZE(pxa_ssp_dai)); | ||
919 | } | ||
920 | module_init(pxa_ssp_init); | ||
921 | |||
922 | static void __exit pxa_ssp_exit(void) | ||
923 | { | ||
924 | snd_soc_unregister_dais(pxa_ssp_dai, ARRAY_SIZE(pxa_ssp_dai)); | ||
925 | } | ||
926 | module_exit(pxa_ssp_exit); | ||
927 | |||
928 | /* Module information */ | ||
929 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | ||
930 | MODULE_DESCRIPTION("PXA SSP/PCM SoC Interface"); | ||
931 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/pxa/pxa-ssp.h b/sound/soc/pxa/pxa-ssp.h new file mode 100644 index 000000000000..91deadd55675 --- /dev/null +++ b/sound/soc/pxa/pxa-ssp.h | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * ASoC PXA SSP port support | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef _PXA_SSP_H | ||
10 | #define _PXA_SSP_H | ||
11 | |||
12 | /* pxa DAI SSP IDs */ | ||
13 | #define PXA_DAI_SSP1 0 | ||
14 | #define PXA_DAI_SSP2 1 | ||
15 | #define PXA_DAI_SSP3 2 | ||
16 | #define PXA_DAI_SSP4 3 | ||
17 | |||
18 | /* SSP clock sources */ | ||
19 | #define PXA_SSP_CLK_PLL 0 | ||
20 | #define PXA_SSP_CLK_EXT 1 | ||
21 | #define PXA_SSP_CLK_NET 2 | ||
22 | #define PXA_SSP_CLK_AUDIO 3 | ||
23 | #define PXA_SSP_CLK_NET_PLL 4 | ||
24 | |||
25 | /* SSP audio dividers */ | ||
26 | #define PXA_SSP_AUDIO_DIV_ACDS 0 | ||
27 | #define PXA_SSP_AUDIO_DIV_SCDB 1 | ||
28 | #define PXA_SSP_DIV_SCR 2 | ||
29 | |||
30 | /* SSP ACDS audio dividers values */ | ||
31 | #define PXA_SSP_CLK_AUDIO_DIV_1 0 | ||
32 | #define PXA_SSP_CLK_AUDIO_DIV_2 1 | ||
33 | #define PXA_SSP_CLK_AUDIO_DIV_4 2 | ||
34 | #define PXA_SSP_CLK_AUDIO_DIV_8 3 | ||
35 | #define PXA_SSP_CLK_AUDIO_DIV_16 4 | ||
36 | #define PXA_SSP_CLK_AUDIO_DIV_32 5 | ||
37 | |||
38 | /* SSP divider bypass */ | ||
39 | #define PXA_SSP_CLK_SCDB_4 0 | ||
40 | #define PXA_SSP_CLK_SCDB_1 1 | ||
41 | #define PXA_SSP_CLK_SCDB_8 2 | ||
42 | |||
43 | #define PXA_SSP_PLL_OUT 0 | ||
44 | |||
45 | extern struct snd_soc_dai pxa_ssp_dai[4]; | ||
46 | |||
47 | #endif | ||
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index a7a3a9c5c6ff..780db6757ad2 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c | |||
@@ -87,14 +87,12 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = { | |||
87 | }; | 87 | }; |
88 | 88 | ||
89 | #ifdef CONFIG_PM | 89 | #ifdef CONFIG_PM |
90 | static int pxa2xx_ac97_suspend(struct platform_device *pdev, | 90 | static int pxa2xx_ac97_suspend(struct snd_soc_dai *dai) |
91 | struct snd_soc_dai *dai) | ||
92 | { | 91 | { |
93 | return pxa2xx_ac97_hw_suspend(); | 92 | return pxa2xx_ac97_hw_suspend(); |
94 | } | 93 | } |
95 | 94 | ||
96 | static int pxa2xx_ac97_resume(struct platform_device *pdev, | 95 | static int pxa2xx_ac97_resume(struct snd_soc_dai *dai) |
97 | struct snd_soc_dai *dai) | ||
98 | { | 96 | { |
99 | return pxa2xx_ac97_hw_resume(); | 97 | return pxa2xx_ac97_hw_resume(); |
100 | } | 98 | } |
@@ -117,7 +115,8 @@ static void pxa2xx_ac97_remove(struct platform_device *pdev, | |||
117 | } | 115 | } |
118 | 116 | ||
119 | static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, | 117 | static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, |
120 | struct snd_pcm_hw_params *params) | 118 | struct snd_pcm_hw_params *params, |
119 | struct snd_soc_dai *dai) | ||
121 | { | 120 | { |
122 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 121 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
123 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 122 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
@@ -131,7 +130,8 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, | |||
131 | } | 130 | } |
132 | 131 | ||
133 | static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, | 132 | static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, |
134 | struct snd_pcm_hw_params *params) | 133 | struct snd_pcm_hw_params *params, |
134 | struct snd_soc_dai *dai) | ||
135 | { | 135 | { |
136 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 136 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
137 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 137 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
@@ -145,7 +145,8 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, | |||
145 | } | 145 | } |
146 | 146 | ||
147 | static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, | 147 | static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream, |
148 | struct snd_pcm_hw_params *params) | 148 | struct snd_pcm_hw_params *params, |
149 | struct snd_soc_dai *dai) | ||
149 | { | 150 | { |
150 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 151 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
151 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 152 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
@@ -170,7 +171,7 @@ struct snd_soc_dai pxa_ac97_dai[] = { | |||
170 | { | 171 | { |
171 | .name = "pxa2xx-ac97", | 172 | .name = "pxa2xx-ac97", |
172 | .id = 0, | 173 | .id = 0, |
173 | .type = SND_SOC_DAI_AC97, | 174 | .ac97_control = 1, |
174 | .probe = pxa2xx_ac97_probe, | 175 | .probe = pxa2xx_ac97_probe, |
175 | .remove = pxa2xx_ac97_remove, | 176 | .remove = pxa2xx_ac97_remove, |
176 | .suspend = pxa2xx_ac97_suspend, | 177 | .suspend = pxa2xx_ac97_suspend, |
@@ -193,7 +194,7 @@ struct snd_soc_dai pxa_ac97_dai[] = { | |||
193 | { | 194 | { |
194 | .name = "pxa2xx-ac97-aux", | 195 | .name = "pxa2xx-ac97-aux", |
195 | .id = 1, | 196 | .id = 1, |
196 | .type = SND_SOC_DAI_AC97, | 197 | .ac97_control = 1, |
197 | .playback = { | 198 | .playback = { |
198 | .stream_name = "AC97 Aux Playback", | 199 | .stream_name = "AC97 Aux Playback", |
199 | .channels_min = 1, | 200 | .channels_min = 1, |
@@ -212,7 +213,7 @@ struct snd_soc_dai pxa_ac97_dai[] = { | |||
212 | { | 213 | { |
213 | .name = "pxa2xx-ac97-mic", | 214 | .name = "pxa2xx-ac97-mic", |
214 | .id = 2, | 215 | .id = 2, |
215 | .type = SND_SOC_DAI_AC97, | 216 | .ac97_control = 1, |
216 | .capture = { | 217 | .capture = { |
217 | .stream_name = "AC97 Mic Capture", | 218 | .stream_name = "AC97 Mic Capture", |
218 | .channels_min = 1, | 219 | .channels_min = 1, |
@@ -227,6 +228,18 @@ struct snd_soc_dai pxa_ac97_dai[] = { | |||
227 | EXPORT_SYMBOL_GPL(pxa_ac97_dai); | 228 | EXPORT_SYMBOL_GPL(pxa_ac97_dai); |
228 | EXPORT_SYMBOL_GPL(soc_ac97_ops); | 229 | EXPORT_SYMBOL_GPL(soc_ac97_ops); |
229 | 230 | ||
231 | static int __init pxa_ac97_init(void) | ||
232 | { | ||
233 | return snd_soc_register_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai)); | ||
234 | } | ||
235 | module_init(pxa_ac97_init); | ||
236 | |||
237 | static void __exit pxa_ac97_exit(void) | ||
238 | { | ||
239 | snd_soc_unregister_dais(pxa_ac97_dai, ARRAY_SIZE(pxa_ac97_dai)); | ||
240 | } | ||
241 | module_exit(pxa_ac97_exit); | ||
242 | |||
230 | MODULE_AUTHOR("Nicolas Pitre"); | 243 | MODULE_AUTHOR("Nicolas Pitre"); |
231 | MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); | 244 | MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); |
232 | MODULE_LICENSE("GPL"); | 245 | MODULE_LICENSE("GPL"); |
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index e758034db5c3..517991fb1099 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c | |||
@@ -121,7 +121,8 @@ static struct pxa2xx_gpio gpio_bus[] = { | |||
121 | }, | 121 | }, |
122 | }; | 122 | }; |
123 | 123 | ||
124 | static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream) | 124 | static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream, |
125 | struct snd_soc_dai *dai) | ||
125 | { | 126 | { |
126 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 127 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
127 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 128 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
@@ -187,7 +188,8 @@ static int pxa2xx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | |||
187 | } | 188 | } |
188 | 189 | ||
189 | static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, | 190 | static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, |
190 | struct snd_pcm_hw_params *params) | 191 | struct snd_pcm_hw_params *params, |
192 | struct snd_soc_dai *dai) | ||
191 | { | 193 | { |
192 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 194 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
193 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | 195 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; |
@@ -248,7 +250,8 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, | |||
248 | return 0; | 250 | return 0; |
249 | } | 251 | } |
250 | 252 | ||
251 | static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd) | 253 | static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, |
254 | struct snd_soc_dai *dai) | ||
252 | { | 255 | { |
253 | int ret = 0; | 256 | int ret = 0; |
254 | 257 | ||
@@ -269,7 +272,8 @@ static int pxa2xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd) | |||
269 | return ret; | 272 | return ret; |
270 | } | 273 | } |
271 | 274 | ||
272 | static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream) | 275 | static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream, |
276 | struct snd_soc_dai *dai) | ||
273 | { | 277 | { |
274 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 278 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
275 | SACR1 |= SACR1_DRPL; | 279 | SACR1 |= SACR1_DRPL; |
@@ -289,8 +293,7 @@ static void pxa2xx_i2s_shutdown(struct snd_pcm_substream *substream) | |||
289 | } | 293 | } |
290 | 294 | ||
291 | #ifdef CONFIG_PM | 295 | #ifdef CONFIG_PM |
292 | static int pxa2xx_i2s_suspend(struct platform_device *dev, | 296 | static int pxa2xx_i2s_suspend(struct snd_soc_dai *dai) |
293 | struct snd_soc_dai *dai) | ||
294 | { | 297 | { |
295 | if (!dai->active) | 298 | if (!dai->active) |
296 | return 0; | 299 | return 0; |
@@ -307,8 +310,7 @@ static int pxa2xx_i2s_suspend(struct platform_device *dev, | |||
307 | return 0; | 310 | return 0; |
308 | } | 311 | } |
309 | 312 | ||
310 | static int pxa2xx_i2s_resume(struct platform_device *pdev, | 313 | static int pxa2xx_i2s_resume(struct snd_soc_dai *dai) |
311 | struct snd_soc_dai *dai) | ||
312 | { | 314 | { |
313 | if (!dai->active) | 315 | if (!dai->active) |
314 | return 0; | 316 | return 0; |
@@ -336,7 +338,6 @@ static int pxa2xx_i2s_resume(struct platform_device *pdev, | |||
336 | struct snd_soc_dai pxa_i2s_dai = { | 338 | struct snd_soc_dai pxa_i2s_dai = { |
337 | .name = "pxa2xx-i2s", | 339 | .name = "pxa2xx-i2s", |
338 | .id = 0, | 340 | .id = 0, |
339 | .type = SND_SOC_DAI_I2S, | ||
340 | .suspend = pxa2xx_i2s_suspend, | 341 | .suspend = pxa2xx_i2s_suspend, |
341 | .resume = pxa2xx_i2s_resume, | 342 | .resume = pxa2xx_i2s_resume, |
342 | .playback = { | 343 | .playback = { |
@@ -353,8 +354,7 @@ struct snd_soc_dai pxa_i2s_dai = { | |||
353 | .startup = pxa2xx_i2s_startup, | 354 | .startup = pxa2xx_i2s_startup, |
354 | .shutdown = pxa2xx_i2s_shutdown, | 355 | .shutdown = pxa2xx_i2s_shutdown, |
355 | .trigger = pxa2xx_i2s_trigger, | 356 | .trigger = pxa2xx_i2s_trigger, |
356 | .hw_params = pxa2xx_i2s_hw_params,}, | 357 | .hw_params = pxa2xx_i2s_hw_params, |
357 | .dai_ops = { | ||
358 | .set_fmt = pxa2xx_i2s_set_dai_fmt, | 358 | .set_fmt = pxa2xx_i2s_set_dai_fmt, |
359 | .set_sysclk = pxa2xx_i2s_set_dai_sysclk, | 359 | .set_sysclk = pxa2xx_i2s_set_dai_sysclk, |
360 | }, | 360 | }, |
@@ -364,12 +364,23 @@ EXPORT_SYMBOL_GPL(pxa_i2s_dai); | |||
364 | 364 | ||
365 | static int pxa2xx_i2s_probe(struct platform_device *dev) | 365 | static int pxa2xx_i2s_probe(struct platform_device *dev) |
366 | { | 366 | { |
367 | int ret; | ||
368 | |||
367 | clk_i2s = clk_get(&dev->dev, "I2SCLK"); | 369 | clk_i2s = clk_get(&dev->dev, "I2SCLK"); |
368 | return IS_ERR(clk_i2s) ? PTR_ERR(clk_i2s) : 0; | 370 | if (IS_ERR(clk_i2s)) |
371 | return PTR_ERR(clk_i2s); | ||
372 | |||
373 | pxa_i2s_dai.dev = &dev->dev; | ||
374 | ret = snd_soc_register_dai(&pxa_i2s_dai); | ||
375 | if (ret != 0) | ||
376 | clk_put(clk_i2s); | ||
377 | |||
378 | return ret; | ||
369 | } | 379 | } |
370 | 380 | ||
371 | static int __devexit pxa2xx_i2s_remove(struct platform_device *dev) | 381 | static int __devexit pxa2xx_i2s_remove(struct platform_device *dev) |
372 | { | 382 | { |
383 | snd_soc_unregister_dai(&pxa_i2s_dai); | ||
373 | clk_put(clk_i2s); | 384 | clk_put(clk_i2s); |
374 | clk_i2s = ERR_PTR(-ENOENT); | 385 | clk_i2s = ERR_PTR(-ENOENT); |
375 | return 0; | 386 | return 0; |
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index afcd892cd2fa..c670d08e7c9e 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c | |||
@@ -69,7 +69,7 @@ static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) | |||
69 | return 0; | 69 | return 0; |
70 | } | 70 | } |
71 | 71 | ||
72 | struct snd_pcm_ops pxa2xx_pcm_ops = { | 72 | static struct snd_pcm_ops pxa2xx_pcm_ops = { |
73 | .open = __pxa2xx_pcm_open, | 73 | .open = __pxa2xx_pcm_open, |
74 | .close = __pxa2xx_pcm_close, | 74 | .close = __pxa2xx_pcm_close, |
75 | .ioctl = snd_pcm_lib_ioctl, | 75 | .ioctl = snd_pcm_lib_ioctl, |
@@ -118,6 +118,18 @@ struct snd_soc_platform pxa2xx_soc_platform = { | |||
118 | }; | 118 | }; |
119 | EXPORT_SYMBOL_GPL(pxa2xx_soc_platform); | 119 | EXPORT_SYMBOL_GPL(pxa2xx_soc_platform); |
120 | 120 | ||
121 | static int __init pxa2xx_soc_platform_init(void) | ||
122 | { | ||
123 | return snd_soc_register_platform(&pxa2xx_soc_platform); | ||
124 | } | ||
125 | module_init(pxa2xx_soc_platform_init); | ||
126 | |||
127 | static void __exit pxa2xx_soc_platform_exit(void) | ||
128 | { | ||
129 | snd_soc_unregister_platform(&pxa2xx_soc_platform); | ||
130 | } | ||
131 | module_exit(pxa2xx_soc_platform_exit); | ||
132 | |||
121 | MODULE_AUTHOR("Nicolas Pitre"); | 133 | MODULE_AUTHOR("Nicolas Pitre"); |
122 | MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module"); | 134 | MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module"); |
123 | MODULE_LICENSE("GPL"); | 135 | MODULE_LICENSE("GPL"); |
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index d307b6757e95..a3b9e6bdf979 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c | |||
@@ -319,8 +319,9 @@ static struct snd_soc_dai_link spitz_dai = { | |||
319 | }; | 319 | }; |
320 | 320 | ||
321 | /* spitz audio machine driver */ | 321 | /* spitz audio machine driver */ |
322 | static struct snd_soc_machine snd_soc_machine_spitz = { | 322 | static struct snd_soc_card snd_soc_spitz = { |
323 | .name = "Spitz", | 323 | .name = "Spitz", |
324 | .platform = &pxa2xx_soc_platform, | ||
324 | .dai_link = &spitz_dai, | 325 | .dai_link = &spitz_dai, |
325 | .num_links = 1, | 326 | .num_links = 1, |
326 | }; | 327 | }; |
@@ -333,8 +334,7 @@ static struct wm8750_setup_data spitz_wm8750_setup = { | |||
333 | 334 | ||
334 | /* spitz audio subsystem */ | 335 | /* spitz audio subsystem */ |
335 | static struct snd_soc_device spitz_snd_devdata = { | 336 | static struct snd_soc_device spitz_snd_devdata = { |
336 | .machine = &snd_soc_machine_spitz, | 337 | .card = &snd_soc_spitz, |
337 | .platform = &pxa2xx_soc_platform, | ||
338 | .codec_dev = &soc_codec_dev_wm8750, | 338 | .codec_dev = &soc_codec_dev_wm8750, |
339 | .codec_data = &spitz_wm8750_setup, | 339 | .codec_data = &spitz_wm8750_setup, |
340 | }; | 340 | }; |
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index afefe41b8c46..c77194f74c9b 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c | |||
@@ -38,7 +38,7 @@ | |||
38 | #include "pxa2xx-pcm.h" | 38 | #include "pxa2xx-pcm.h" |
39 | #include "pxa2xx-ac97.h" | 39 | #include "pxa2xx-ac97.h" |
40 | 40 | ||
41 | static struct snd_soc_machine tosa; | 41 | static struct snd_soc_card tosa; |
42 | 42 | ||
43 | #define TOSA_HP 0 | 43 | #define TOSA_HP 0 |
44 | #define TOSA_MIC_INT 1 | 44 | #define TOSA_MIC_INT 1 |
@@ -230,15 +230,37 @@ static struct snd_soc_dai_link tosa_dai[] = { | |||
230 | }, | 230 | }, |
231 | }; | 231 | }; |
232 | 232 | ||
233 | static struct snd_soc_machine tosa = { | 233 | static int tosa_probe(struct platform_device *dev) |
234 | { | ||
235 | int ret; | ||
236 | |||
237 | ret = gpio_request(TOSA_GPIO_L_MUTE, "Headphone Jack"); | ||
238 | if (ret) | ||
239 | return ret; | ||
240 | ret = gpio_direction_output(TOSA_GPIO_L_MUTE, 0); | ||
241 | if (ret) | ||
242 | gpio_free(TOSA_GPIO_L_MUTE); | ||
243 | |||
244 | return ret; | ||
245 | } | ||
246 | |||
247 | static int tosa_remove(struct platform_device *dev) | ||
248 | { | ||
249 | gpio_free(TOSA_GPIO_L_MUTE); | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static struct snd_soc_card tosa = { | ||
234 | .name = "Tosa", | 254 | .name = "Tosa", |
255 | .platform = &pxa2xx_soc_platform, | ||
235 | .dai_link = tosa_dai, | 256 | .dai_link = tosa_dai, |
236 | .num_links = ARRAY_SIZE(tosa_dai), | 257 | .num_links = ARRAY_SIZE(tosa_dai), |
258 | .probe = tosa_probe, | ||
259 | .remove = tosa_remove, | ||
237 | }; | 260 | }; |
238 | 261 | ||
239 | static struct snd_soc_device tosa_snd_devdata = { | 262 | static struct snd_soc_device tosa_snd_devdata = { |
240 | .machine = &tosa, | 263 | .card = &tosa, |
241 | .platform = &pxa2xx_soc_platform, | ||
242 | .codec_dev = &soc_codec_dev_wm9712, | 264 | .codec_dev = &soc_codec_dev_wm9712, |
243 | }; | 265 | }; |
244 | 266 | ||
@@ -251,11 +273,6 @@ static int __init tosa_init(void) | |||
251 | if (!machine_is_tosa()) | 273 | if (!machine_is_tosa()) |
252 | return -ENODEV; | 274 | return -ENODEV; |
253 | 275 | ||
254 | ret = gpio_request(TOSA_GPIO_L_MUTE, "Headphone Jack"); | ||
255 | if (ret) | ||
256 | return ret; | ||
257 | gpio_direction_output(TOSA_GPIO_L_MUTE, 0); | ||
258 | |||
259 | tosa_snd_device = platform_device_alloc("soc-audio", -1); | 276 | tosa_snd_device = platform_device_alloc("soc-audio", -1); |
260 | if (!tosa_snd_device) { | 277 | if (!tosa_snd_device) { |
261 | ret = -ENOMEM; | 278 | ret = -ENOMEM; |
@@ -272,15 +289,12 @@ static int __init tosa_init(void) | |||
272 | platform_device_put(tosa_snd_device); | 289 | platform_device_put(tosa_snd_device); |
273 | 290 | ||
274 | err_alloc: | 291 | err_alloc: |
275 | gpio_free(TOSA_GPIO_L_MUTE); | ||
276 | |||
277 | return ret; | 292 | return ret; |
278 | } | 293 | } |
279 | 294 | ||
280 | static void __exit tosa_exit(void) | 295 | static void __exit tosa_exit(void) |
281 | { | 296 | { |
282 | platform_device_unregister(tosa_snd_device); | 297 | platform_device_unregister(tosa_snd_device); |
283 | gpio_free(TOSA_GPIO_L_MUTE); | ||
284 | } | 298 | } |
285 | 299 | ||
286 | module_init(tosa_init); | 300 | module_init(tosa_init); |
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c new file mode 100644 index 000000000000..f8e9ecd589d3 --- /dev/null +++ b/sound/soc/pxa/zylonite.c | |||
@@ -0,0 +1,219 @@ | |||
1 | /* | ||
2 | * zylonite.c -- SoC audio for Zylonite | ||
3 | * | ||
4 | * Copyright 2008 Wolfson Microelectronics PLC. | ||
5 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License as | ||
9 | * published by the Free Software Foundation; either version 2 of the | ||
10 | * License, or (at your option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/moduleparam.h> | ||
16 | #include <linux/device.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <sound/core.h> | ||
19 | #include <sound/pcm.h> | ||
20 | #include <sound/pcm_params.h> | ||
21 | #include <sound/soc.h> | ||
22 | #include <sound/soc-dapm.h> | ||
23 | |||
24 | #include "../codecs/wm9713.h" | ||
25 | #include "pxa2xx-pcm.h" | ||
26 | #include "pxa2xx-ac97.h" | ||
27 | #include "pxa-ssp.h" | ||
28 | |||
29 | static struct snd_soc_card zylonite; | ||
30 | |||
31 | static const struct snd_soc_dapm_widget zylonite_dapm_widgets[] = { | ||
32 | SND_SOC_DAPM_HP("Headphone", NULL), | ||
33 | SND_SOC_DAPM_MIC("Headset Microphone", NULL), | ||
34 | SND_SOC_DAPM_MIC("Handset Microphone", NULL), | ||
35 | SND_SOC_DAPM_SPK("Multiactor", NULL), | ||
36 | SND_SOC_DAPM_SPK("Headset Earpiece", NULL), | ||
37 | }; | ||
38 | |||
39 | /* Currently supported audio map */ | ||
40 | static const struct snd_soc_dapm_route audio_map[] = { | ||
41 | |||
42 | /* Headphone output connected to HPL/HPR */ | ||
43 | { "Headphone", NULL, "HPL" }, | ||
44 | { "Headphone", NULL, "HPR" }, | ||
45 | |||
46 | /* On-board earpiece */ | ||
47 | { "Headset Earpiece", NULL, "OUT3" }, | ||
48 | |||
49 | /* Headphone mic */ | ||
50 | { "MIC2A", NULL, "Mic Bias" }, | ||
51 | { "Mic Bias", NULL, "Headset Microphone" }, | ||
52 | |||
53 | /* On-board mic */ | ||
54 | { "MIC1", NULL, "Mic Bias" }, | ||
55 | { "Mic Bias", NULL, "Handset Microphone" }, | ||
56 | |||
57 | /* Multiactor differentially connected over SPKL/SPKR */ | ||
58 | { "Multiactor", NULL, "SPKL" }, | ||
59 | { "Multiactor", NULL, "SPKR" }, | ||
60 | }; | ||
61 | |||
62 | static int zylonite_wm9713_init(struct snd_soc_codec *codec) | ||
63 | { | ||
64 | /* Currently we only support use of the AC97 clock here. If | ||
65 | * CLK_POUT is selected by SW15 then the clock API will need | ||
66 | * to be used to request and enable it here. | ||
67 | */ | ||
68 | |||
69 | snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets, | ||
70 | ARRAY_SIZE(zylonite_dapm_widgets)); | ||
71 | |||
72 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
73 | |||
74 | /* Static setup for now */ | ||
75 | snd_soc_dapm_enable_pin(codec, "Headphone"); | ||
76 | snd_soc_dapm_enable_pin(codec, "Headset Earpiece"); | ||
77 | |||
78 | snd_soc_dapm_sync(codec); | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static int zylonite_voice_hw_params(struct snd_pcm_substream *substream, | ||
83 | struct snd_pcm_hw_params *params) | ||
84 | { | ||
85 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
86 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
87 | struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; | ||
88 | unsigned int pll_out = 0; | ||
89 | unsigned int acds = 0; | ||
90 | unsigned int wm9713_div = 0; | ||
91 | int ret = 0; | ||
92 | |||
93 | switch (params_rate(params)) { | ||
94 | case 8000: | ||
95 | wm9713_div = 12; | ||
96 | pll_out = 2048000; | ||
97 | break; | ||
98 | case 16000: | ||
99 | wm9713_div = 6; | ||
100 | pll_out = 4096000; | ||
101 | break; | ||
102 | case 48000: | ||
103 | default: | ||
104 | wm9713_div = 2; | ||
105 | pll_out = 12288000; | ||
106 | acds = 1; | ||
107 | break; | ||
108 | } | ||
109 | |||
110 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | | ||
111 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | ||
112 | if (ret < 0) | ||
113 | return ret; | ||
114 | |||
115 | ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | | ||
116 | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); | ||
117 | if (ret < 0) | ||
118 | return ret; | ||
119 | |||
120 | ret = snd_soc_dai_set_tdm_slot(cpu_dai, | ||
121 | params_channels(params), | ||
122 | params_channels(params)); | ||
123 | if (ret < 0) | ||
124 | return ret; | ||
125 | |||
126 | ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, pll_out); | ||
127 | if (ret < 0) | ||
128 | return ret; | ||
129 | |||
130 | ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_AUDIO_DIV_ACDS, acds); | ||
131 | if (ret < 0) | ||
132 | return ret; | ||
133 | |||
134 | ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_AUDIO, 0, 1); | ||
135 | if (ret < 0) | ||
136 | return ret; | ||
137 | |||
138 | /* Note that if the PLL is in use the WM9713_PCMCLK_PLL_DIV needs | ||
139 | * to be set instead. | ||
140 | */ | ||
141 | ret = snd_soc_dai_set_clkdiv(codec_dai, WM9713_PCMCLK_DIV, | ||
142 | WM9713_PCMDIV(wm9713_div)); | ||
143 | if (ret < 0) | ||
144 | return ret; | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static struct snd_soc_ops zylonite_voice_ops = { | ||
150 | .hw_params = zylonite_voice_hw_params, | ||
151 | }; | ||
152 | |||
153 | static struct snd_soc_dai_link zylonite_dai[] = { | ||
154 | { | ||
155 | .name = "AC97", | ||
156 | .stream_name = "AC97 HiFi", | ||
157 | .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], | ||
158 | .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI], | ||
159 | .init = zylonite_wm9713_init, | ||
160 | }, | ||
161 | { | ||
162 | .name = "AC97 Aux", | ||
163 | .stream_name = "AC97 Aux", | ||
164 | .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], | ||
165 | .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX], | ||
166 | }, | ||
167 | { | ||
168 | .name = "WM9713 Voice", | ||
169 | .stream_name = "WM9713 Voice", | ||
170 | .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP3], | ||
171 | .codec_dai = &wm9713_dai[WM9713_DAI_PCM_VOICE], | ||
172 | .ops = &zylonite_voice_ops, | ||
173 | }, | ||
174 | }; | ||
175 | |||
176 | static struct snd_soc_card zylonite = { | ||
177 | .name = "Zylonite", | ||
178 | .platform = &pxa2xx_soc_platform, | ||
179 | .dai_link = zylonite_dai, | ||
180 | .num_links = ARRAY_SIZE(zylonite_dai), | ||
181 | }; | ||
182 | |||
183 | static struct snd_soc_device zylonite_snd_ac97_devdata = { | ||
184 | .card = &zylonite, | ||
185 | .codec_dev = &soc_codec_dev_wm9713, | ||
186 | }; | ||
187 | |||
188 | static struct platform_device *zylonite_snd_ac97_device; | ||
189 | |||
190 | static int __init zylonite_init(void) | ||
191 | { | ||
192 | int ret; | ||
193 | |||
194 | zylonite_snd_ac97_device = platform_device_alloc("soc-audio", -1); | ||
195 | if (!zylonite_snd_ac97_device) | ||
196 | return -ENOMEM; | ||
197 | |||
198 | platform_set_drvdata(zylonite_snd_ac97_device, | ||
199 | &zylonite_snd_ac97_devdata); | ||
200 | zylonite_snd_ac97_devdata.dev = &zylonite_snd_ac97_device->dev; | ||
201 | |||
202 | ret = platform_device_add(zylonite_snd_ac97_device); | ||
203 | if (ret != 0) | ||
204 | platform_device_put(zylonite_snd_ac97_device); | ||
205 | |||
206 | return ret; | ||
207 | } | ||
208 | |||
209 | static void __exit zylonite_exit(void) | ||
210 | { | ||
211 | platform_device_unregister(zylonite_snd_ac97_device); | ||
212 | } | ||
213 | |||
214 | module_init(zylonite_init); | ||
215 | module_exit(zylonite_exit); | ||
216 | |||
217 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | ||
218 | MODULE_DESCRIPTION("ALSA SoC WM9713 Zylonite"); | ||
219 | MODULE_LICENSE("GPL"); | ||