diff options
Diffstat (limited to 'sound/pcmcia')
-rw-r--r-- | sound/pcmcia/Kconfig | 39 | ||||
-rw-r--r-- | sound/pcmcia/Makefile | 6 | ||||
-rw-r--r-- | sound/pcmcia/pdaudiocf/Makefile | 8 | ||||
-rw-r--r-- | sound/pcmcia/pdaudiocf/pdaudiocf.c | 404 | ||||
-rw-r--r-- | sound/pcmcia/pdaudiocf/pdaudiocf.h | 145 | ||||
-rw-r--r-- | sound/pcmcia/pdaudiocf/pdaudiocf_core.c | 291 | ||||
-rw-r--r-- | sound/pcmcia/pdaudiocf/pdaudiocf_irq.c | 325 | ||||
-rw-r--r-- | sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c | 361 | ||||
-rw-r--r-- | sound/pcmcia/vx/Makefile | 11 | ||||
-rw-r--r-- | sound/pcmcia/vx/vx_entry.c | 384 | ||||
-rw-r--r-- | sound/pcmcia/vx/vxp440.c | 14 | ||||
-rw-r--r-- | sound/pcmcia/vx/vxp_mixer.c | 148 | ||||
-rw-r--r-- | sound/pcmcia/vx/vxp_ops.c | 614 | ||||
-rw-r--r-- | sound/pcmcia/vx/vxpocket.c | 164 | ||||
-rw-r--r-- | sound/pcmcia/vx/vxpocket.h | 118 |
15 files changed, 3032 insertions, 0 deletions
diff --git a/sound/pcmcia/Kconfig b/sound/pcmcia/Kconfig new file mode 100644 index 000000000000..3611e298834f --- /dev/null +++ b/sound/pcmcia/Kconfig | |||
@@ -0,0 +1,39 @@ | |||
1 | # ALSA PCMCIA drivers | ||
2 | |||
3 | menu "PCMCIA devices" | ||
4 | depends on SND!=n && PCMCIA | ||
5 | |||
6 | config SND_VXPOCKET | ||
7 | tristate "Digigram VXpocket" | ||
8 | depends on SND && PCMCIA && ISA | ||
9 | select SND_VX_LIB | ||
10 | help | ||
11 | Say Y here to include support for Digigram VXpocket | ||
12 | soundcards. | ||
13 | |||
14 | To compile this driver as a module, choose M here: the module | ||
15 | will be called snd-vxpocket. | ||
16 | |||
17 | config SND_VXP440 | ||
18 | tristate "Digigram VXpocket 440" | ||
19 | depends on SND && PCMCIA && ISA | ||
20 | select SND_VX_LIB | ||
21 | help | ||
22 | Say Y here to include support for Digigram VXpocket 440 | ||
23 | soundcards. | ||
24 | |||
25 | To compile this driver as a module, choose M here: the module | ||
26 | will be called snd-vxp440. | ||
27 | |||
28 | config SND_PDAUDIOCF | ||
29 | tristate "Sound Core PDAudioCF" | ||
30 | depends on SND && PCMCIA && ISA | ||
31 | select SND_PCM | ||
32 | help | ||
33 | Say Y here to include support for Sound Core PDAudioCF | ||
34 | soundcards. | ||
35 | |||
36 | To compile this driver as a module, choose M here: the module | ||
37 | will be called snd-pdaudiocf. | ||
38 | |||
39 | endmenu | ||
diff --git a/sound/pcmcia/Makefile b/sound/pcmcia/Makefile new file mode 100644 index 000000000000..b6656d48becd --- /dev/null +++ b/sound/pcmcia/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | obj-$(CONFIG_SND) += vx/ pdaudiocf/ | ||
diff --git a/sound/pcmcia/pdaudiocf/Makefile b/sound/pcmcia/pdaudiocf/Makefile new file mode 100644 index 000000000000..6e194f9b50e3 --- /dev/null +++ b/sound/pcmcia/pdaudiocf/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2004 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-pdaudiocf-objs := pdaudiocf.o pdaudiocf_core.o pdaudiocf_irq.o pdaudiocf_pcm.o | ||
7 | |||
8 | obj-$(CONFIG_SND_PDAUDIOCF) += snd-pdaudiocf.o | ||
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.c b/sound/pcmcia/pdaudiocf/pdaudiocf.c new file mode 100644 index 000000000000..f72c81cc9952 --- /dev/null +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.c | |||
@@ -0,0 +1,404 @@ | |||
1 | /* | ||
2 | * Driver for Sound Core PDAudioCF soundcard | ||
3 | * | ||
4 | * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <sound/core.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/moduleparam.h> | ||
25 | #include <pcmcia/version.h> | ||
26 | #include <pcmcia/ciscode.h> | ||
27 | #include <pcmcia/cisreg.h> | ||
28 | #include "pdaudiocf.h" | ||
29 | #include <sound/initval.h> | ||
30 | #include <linux/init.h> | ||
31 | |||
32 | /* | ||
33 | */ | ||
34 | |||
35 | #define CARD_NAME "PDAudio-CF" | ||
36 | |||
37 | MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>"); | ||
38 | MODULE_DESCRIPTION("Sound Core " CARD_NAME); | ||
39 | MODULE_LICENSE("GPL"); | ||
40 | MODULE_SUPPORTED_DEVICE("{{Sound Core," CARD_NAME "}}"); | ||
41 | |||
42 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
43 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
44 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ | ||
45 | |||
46 | module_param_array(index, int, NULL, 0444); | ||
47 | MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); | ||
48 | module_param_array(id, charp, NULL, 0444); | ||
49 | MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); | ||
50 | module_param_array(enable, bool, NULL, 0444); | ||
51 | MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); | ||
52 | |||
53 | /* | ||
54 | */ | ||
55 | |||
56 | static dev_info_t dev_info = "snd-pdaudiocf"; | ||
57 | static snd_card_t *card_list[SNDRV_CARDS]; | ||
58 | static dev_link_t *dev_list; | ||
59 | |||
60 | /* | ||
61 | * prototypes | ||
62 | */ | ||
63 | static void pdacf_config(dev_link_t *link); | ||
64 | static int pdacf_event(event_t event, int priority, event_callback_args_t *args); | ||
65 | static void snd_pdacf_detach(dev_link_t *link); | ||
66 | |||
67 | static void pdacf_release(dev_link_t *link) | ||
68 | { | ||
69 | if (link->state & DEV_CONFIG) { | ||
70 | /* release cs resources */ | ||
71 | pcmcia_release_configuration(link->handle); | ||
72 | pcmcia_release_io(link->handle, &link->io); | ||
73 | pcmcia_release_irq(link->handle, &link->irq); | ||
74 | link->state &= ~DEV_CONFIG; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * destructor | ||
80 | */ | ||
81 | static int snd_pdacf_free(pdacf_t *pdacf) | ||
82 | { | ||
83 | dev_link_t *link = &pdacf->link; | ||
84 | |||
85 | pdacf_release(link); | ||
86 | |||
87 | /* Break the link with Card Services */ | ||
88 | if (link->handle) | ||
89 | pcmcia_deregister_client(link->handle); | ||
90 | |||
91 | card_list[pdacf->index] = NULL; | ||
92 | pdacf->card = NULL; | ||
93 | |||
94 | kfree(pdacf); | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static int snd_pdacf_dev_free(snd_device_t *device) | ||
99 | { | ||
100 | pdacf_t *chip = device->device_data; | ||
101 | return snd_pdacf_free(chip); | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * snd_pdacf_attach - attach callback for cs | ||
106 | */ | ||
107 | static dev_link_t *snd_pdacf_attach(void) | ||
108 | { | ||
109 | client_reg_t client_reg; /* Register with cardmgr */ | ||
110 | dev_link_t *link; /* Info for cardmgr */ | ||
111 | int i, ret; | ||
112 | pdacf_t *pdacf; | ||
113 | snd_card_t *card; | ||
114 | static snd_device_ops_t ops = { | ||
115 | .dev_free = snd_pdacf_dev_free, | ||
116 | }; | ||
117 | |||
118 | snd_printdd(KERN_DEBUG "pdacf_attach called\n"); | ||
119 | /* find an empty slot from the card list */ | ||
120 | for (i = 0; i < SNDRV_CARDS; i++) { | ||
121 | if (! card_list[i]) | ||
122 | break; | ||
123 | } | ||
124 | if (i >= SNDRV_CARDS) { | ||
125 | snd_printk(KERN_ERR "pdacf: too many cards found\n"); | ||
126 | return NULL; | ||
127 | } | ||
128 | if (! enable[i]) | ||
129 | return NULL; /* disabled explicitly */ | ||
130 | |||
131 | /* ok, create a card instance */ | ||
132 | card = snd_card_new(index[i], id[i], THIS_MODULE, 0); | ||
133 | if (card == NULL) { | ||
134 | snd_printk(KERN_ERR "pdacf: cannot create a card instance\n"); | ||
135 | return NULL; | ||
136 | } | ||
137 | |||
138 | pdacf = snd_pdacf_create(card); | ||
139 | if (! pdacf) | ||
140 | return NULL; | ||
141 | |||
142 | if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, pdacf, &ops) < 0) { | ||
143 | kfree(pdacf); | ||
144 | snd_card_free(card); | ||
145 | return NULL; | ||
146 | } | ||
147 | |||
148 | pdacf->index = i; | ||
149 | card_list[i] = card; | ||
150 | |||
151 | link = &pdacf->link; | ||
152 | link->priv = pdacf; | ||
153 | |||
154 | link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; | ||
155 | link->io.NumPorts1 = 16; | ||
156 | |||
157 | link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT | IRQ_FORCED_PULSE; | ||
158 | // link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; | ||
159 | |||
160 | link->irq.IRQInfo1 = 0 /* | IRQ_LEVEL_ID */; | ||
161 | link->irq.Handler = pdacf_interrupt; | ||
162 | link->irq.Instance = pdacf; | ||
163 | link->conf.Attributes = CONF_ENABLE_IRQ; | ||
164 | link->conf.IntType = INT_MEMORY_AND_IO; | ||
165 | link->conf.ConfigIndex = 1; | ||
166 | link->conf.Present = PRESENT_OPTION; | ||
167 | |||
168 | /* Chain drivers */ | ||
169 | link->next = dev_list; | ||
170 | dev_list = link; | ||
171 | |||
172 | /* Register with Card Services */ | ||
173 | client_reg.dev_info = &dev_info; | ||
174 | client_reg.EventMask = | ||
175 | CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | ||
176 | #ifdef CONFIG_PM | ||
177 | | CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | ||
178 | | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME | ||
179 | #endif | ||
180 | ; | ||
181 | client_reg.event_handler = &pdacf_event; | ||
182 | client_reg.Version = 0x0210; | ||
183 | client_reg.event_callback_args.client_data = link; | ||
184 | |||
185 | ret = pcmcia_register_client(&link->handle, &client_reg); | ||
186 | if (ret != CS_SUCCESS) { | ||
187 | cs_error(link->handle, RegisterClient, ret); | ||
188 | snd_pdacf_detach(link); | ||
189 | return NULL; | ||
190 | } | ||
191 | |||
192 | return link; | ||
193 | } | ||
194 | |||
195 | |||
196 | /** | ||
197 | * snd_pdacf_assign_resources - initialize the hardware and card instance. | ||
198 | * @port: i/o port for the card | ||
199 | * @irq: irq number for the card | ||
200 | * | ||
201 | * this function assigns the specified port and irq, boot the card, | ||
202 | * create pcm and control instances, and initialize the rest hardware. | ||
203 | * | ||
204 | * returns 0 if successful, or a negative error code. | ||
205 | */ | ||
206 | static int snd_pdacf_assign_resources(pdacf_t *pdacf, int port, int irq) | ||
207 | { | ||
208 | int err; | ||
209 | snd_card_t *card = pdacf->card; | ||
210 | |||
211 | snd_printdd(KERN_DEBUG "pdacf assign resources: port = 0x%x, irq = %d\n", port, irq); | ||
212 | pdacf->port = port; | ||
213 | pdacf->irq = irq; | ||
214 | pdacf->chip_status |= PDAUDIOCF_STAT_IS_CONFIGURED; | ||
215 | |||
216 | err = snd_pdacf_ak4117_create(pdacf); | ||
217 | if (err < 0) | ||
218 | return err; | ||
219 | |||
220 | strcpy(card->driver, "PDAudio-CF"); | ||
221 | sprintf(card->shortname, "Core Sound %s", card->driver); | ||
222 | sprintf(card->longname, "%s at 0x%x, irq %i", | ||
223 | card->shortname, port, irq); | ||
224 | |||
225 | err = snd_pdacf_pcm_new(pdacf); | ||
226 | if (err < 0) | ||
227 | return err; | ||
228 | |||
229 | snd_card_set_pm_callback(card, snd_pdacf_suspend, snd_pdacf_resume, pdacf); | ||
230 | |||
231 | if ((err = snd_card_register(card)) < 0) | ||
232 | return err; | ||
233 | |||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | |||
238 | /* | ||
239 | * snd_pdacf_detach - detach callback for cs | ||
240 | */ | ||
241 | static void snd_pdacf_detach(dev_link_t *link) | ||
242 | { | ||
243 | pdacf_t *chip = link->priv; | ||
244 | |||
245 | snd_printdd(KERN_DEBUG "pdacf_detach called\n"); | ||
246 | /* Remove the interface data from the linked list */ | ||
247 | { | ||
248 | dev_link_t **linkp; | ||
249 | /* Locate device structure */ | ||
250 | for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) | ||
251 | if (*linkp == link) | ||
252 | break; | ||
253 | if (*linkp) | ||
254 | *linkp = link->next; | ||
255 | } | ||
256 | if (chip->chip_status & PDAUDIOCF_STAT_IS_CONFIGURED) | ||
257 | snd_pdacf_powerdown(chip); | ||
258 | chip->chip_status |= PDAUDIOCF_STAT_IS_STALE; /* to be sure */ | ||
259 | snd_card_disconnect(chip->card); | ||
260 | snd_card_free_in_thread(chip->card); | ||
261 | } | ||
262 | |||
263 | /* | ||
264 | * configuration callback | ||
265 | */ | ||
266 | |||
267 | #define CS_CHECK(fn, ret) \ | ||
268 | do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) | ||
269 | |||
270 | static void pdacf_config(dev_link_t *link) | ||
271 | { | ||
272 | client_handle_t handle = link->handle; | ||
273 | pdacf_t *pdacf = link->priv; | ||
274 | tuple_t tuple; | ||
275 | cisparse_t *parse = NULL; | ||
276 | config_info_t conf; | ||
277 | u_short buf[32]; | ||
278 | int last_fn, last_ret; | ||
279 | |||
280 | snd_printdd(KERN_DEBUG "pdacf_config called\n"); | ||
281 | parse = kmalloc(sizeof(*parse), GFP_KERNEL); | ||
282 | if (! parse) { | ||
283 | snd_printk(KERN_ERR "pdacf_config: cannot allocate\n"); | ||
284 | return; | ||
285 | } | ||
286 | tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; | ||
287 | tuple.Attributes = 0; | ||
288 | tuple.TupleData = (cisdata_t *)buf; | ||
289 | tuple.TupleDataMax = sizeof(buf); | ||
290 | tuple.TupleOffset = 0; | ||
291 | tuple.DesiredTuple = CISTPL_CONFIG; | ||
292 | CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); | ||
293 | CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); | ||
294 | CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, parse)); | ||
295 | link->conf.ConfigBase = parse->config.base; | ||
296 | link->conf.ConfigIndex = 0x5; | ||
297 | kfree(parse); | ||
298 | |||
299 | CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf)); | ||
300 | link->conf.Vcc = conf.Vcc; | ||
301 | |||
302 | /* Configure card */ | ||
303 | link->state |= DEV_CONFIG; | ||
304 | |||
305 | CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io)); | ||
306 | CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); | ||
307 | CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); | ||
308 | |||
309 | if (snd_pdacf_assign_resources(pdacf, link->io.BasePort1, link->irq.AssignedIRQ) < 0) | ||
310 | goto failed; | ||
311 | |||
312 | link->dev = &pdacf->node; | ||
313 | link->state &= ~DEV_CONFIG_PENDING; | ||
314 | return; | ||
315 | |||
316 | cs_failed: | ||
317 | cs_error(link->handle, last_fn, last_ret); | ||
318 | failed: | ||
319 | pcmcia_release_configuration(link->handle); | ||
320 | pcmcia_release_io(link->handle, &link->io); | ||
321 | pcmcia_release_irq(link->handle, &link->irq); | ||
322 | } | ||
323 | |||
324 | /* | ||
325 | * event callback | ||
326 | */ | ||
327 | static int pdacf_event(event_t event, int priority, event_callback_args_t *args) | ||
328 | { | ||
329 | dev_link_t *link = args->client_data; | ||
330 | pdacf_t *chip = link->priv; | ||
331 | |||
332 | switch (event) { | ||
333 | case CS_EVENT_CARD_REMOVAL: | ||
334 | snd_printdd(KERN_DEBUG "CARD_REMOVAL..\n"); | ||
335 | link->state &= ~DEV_PRESENT; | ||
336 | if (link->state & DEV_CONFIG) { | ||
337 | chip->chip_status |= PDAUDIOCF_STAT_IS_STALE; | ||
338 | } | ||
339 | break; | ||
340 | case CS_EVENT_CARD_INSERTION: | ||
341 | snd_printdd(KERN_DEBUG "CARD_INSERTION..\n"); | ||
342 | link->state |= DEV_PRESENT; | ||
343 | pdacf_config(link); | ||
344 | break; | ||
345 | #ifdef CONFIG_PM | ||
346 | case CS_EVENT_PM_SUSPEND: | ||
347 | snd_printdd(KERN_DEBUG "SUSPEND\n"); | ||
348 | link->state |= DEV_SUSPEND; | ||
349 | if (chip) { | ||
350 | snd_printdd(KERN_DEBUG "snd_pdacf_suspend calling\n"); | ||
351 | snd_pdacf_suspend(chip->card, PMSG_SUSPEND); | ||
352 | } | ||
353 | /* Fall through... */ | ||
354 | case CS_EVENT_RESET_PHYSICAL: | ||
355 | snd_printdd(KERN_DEBUG "RESET_PHYSICAL\n"); | ||
356 | if (link->state & DEV_CONFIG) | ||
357 | pcmcia_release_configuration(link->handle); | ||
358 | break; | ||
359 | case CS_EVENT_PM_RESUME: | ||
360 | snd_printdd(KERN_DEBUG "RESUME\n"); | ||
361 | link->state &= ~DEV_SUSPEND; | ||
362 | /* Fall through... */ | ||
363 | case CS_EVENT_CARD_RESET: | ||
364 | snd_printdd(KERN_DEBUG "CARD_RESET\n"); | ||
365 | if (DEV_OK(link)) { | ||
366 | snd_printdd(KERN_DEBUG "requestconfig...\n"); | ||
367 | pcmcia_request_configuration(link->handle, &link->conf); | ||
368 | if (chip) { | ||
369 | snd_printdd(KERN_DEBUG "calling snd_pdacf_resume\n"); | ||
370 | snd_pdacf_resume(chip->card); | ||
371 | } | ||
372 | } | ||
373 | snd_printdd(KERN_DEBUG "resume done!\n"); | ||
374 | break; | ||
375 | #endif | ||
376 | } | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | /* | ||
381 | * Module entry points | ||
382 | */ | ||
383 | static struct pcmcia_driver pdacf_cs_driver = { | ||
384 | .owner = THIS_MODULE, | ||
385 | .drv = { | ||
386 | .name = "snd-pdaudiocf", | ||
387 | }, | ||
388 | .attach = snd_pdacf_attach, | ||
389 | .detach = snd_pdacf_detach | ||
390 | }; | ||
391 | |||
392 | static int __init init_pdacf(void) | ||
393 | { | ||
394 | return pcmcia_register_driver(&pdacf_cs_driver); | ||
395 | } | ||
396 | |||
397 | static void __exit exit_pdacf(void) | ||
398 | { | ||
399 | pcmcia_unregister_driver(&pdacf_cs_driver); | ||
400 | BUG_ON(dev_list != NULL); | ||
401 | } | ||
402 | |||
403 | module_init(init_pdacf); | ||
404 | module_exit(exit_pdacf); | ||
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf.h b/sound/pcmcia/pdaudiocf/pdaudiocf.h new file mode 100644 index 000000000000..c7a9628256ee --- /dev/null +++ b/sound/pcmcia/pdaudiocf/pdaudiocf.h | |||
@@ -0,0 +1,145 @@ | |||
1 | /* | ||
2 | * Driver for Sound Cors PDAudioCF soundcard | ||
3 | * | ||
4 | * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #ifndef __PDAUDIOCF_H | ||
22 | #define __PDAUDIOCF_H | ||
23 | |||
24 | #include <sound/pcm.h> | ||
25 | #include <asm/io.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <pcmcia/cs_types.h> | ||
28 | #include <pcmcia/cs.h> | ||
29 | #include <pcmcia/cistpl.h> | ||
30 | #include <pcmcia/ds.h> | ||
31 | |||
32 | #include <sound/ak4117.h> | ||
33 | |||
34 | /* PDAUDIOCF registers */ | ||
35 | #define PDAUDIOCF_REG_MD 0x00 /* music data, R/O */ | ||
36 | #define PDAUDIOCF_REG_WDP 0x02 /* write data pointer / 2, R/O */ | ||
37 | #define PDAUDIOCF_REG_RDP 0x04 /* read data pointer / 2, R/O */ | ||
38 | #define PDAUDIOCF_REG_TCR 0x06 /* test control register W/O */ | ||
39 | #define PDAUDIOCF_REG_SCR 0x08 /* status and control, R/W (see bit description) */ | ||
40 | #define PDAUDIOCF_REG_ISR 0x0a /* interrupt status, R/O */ | ||
41 | #define PDAUDIOCF_REG_IER 0x0c /* interrupt enable, R/W */ | ||
42 | #define PDAUDIOCF_REG_AK_IFR 0x0e /* AK interface register, R/W */ | ||
43 | |||
44 | /* PDAUDIOCF_REG_TCR */ | ||
45 | #define PDAUDIOCF_ELIMAKMBIT (1<<0) /* simulate AKM music data */ | ||
46 | #define PDAUDIOCF_TESTDATASEL (1<<1) /* test data selection, 0 = 0x55, 1 = pseudo-random */ | ||
47 | |||
48 | /* PDAUDIOCF_REG_SCR */ | ||
49 | #define PDAUDIOCF_AK_SBP (1<<0) /* serial port busy flag */ | ||
50 | #define PDAUDIOCF_RST (1<<2) /* FPGA, AKM + SRAM buffer reset */ | ||
51 | #define PDAUDIOCF_PDN (1<<3) /* power down bit */ | ||
52 | #define PDAUDIOCF_CLKDIV0 (1<<4) /* choose 24.576Mhz clock divided by 1,2,3 or 4 */ | ||
53 | #define PDAUDIOCF_CLKDIV1 (1<<5) | ||
54 | #define PDAUDIOCF_RECORD (1<<6) /* start capturing to SRAM */ | ||
55 | #define PDAUDIOCF_AK_SDD (1<<7) /* music data detected */ | ||
56 | #define PDAUDIOCF_RED_LED_OFF (1<<8) /* red LED off override */ | ||
57 | #define PDAUDIOCF_BLUE_LED_OFF (1<<9) /* blue LED off override */ | ||
58 | #define PDAUDIOCF_DATAFMT0 (1<<10) /* data format bits: 00 = 16-bit, 01 = 18-bit */ | ||
59 | #define PDAUDIOCF_DATAFMT1 (1<<11) /* 10 = 20-bit, 11 = 24-bit, all right justified */ | ||
60 | #define PDAUDIOCF_FPGAREV(x) ((x>>12)&0x0f) /* FPGA revision */ | ||
61 | |||
62 | /* PDAUDIOCF_REG_ISR */ | ||
63 | #define PDAUDIOCF_IRQLVL (1<<0) /* Buffer level IRQ */ | ||
64 | #define PDAUDIOCF_IRQOVR (1<<1) /* Overrun IRQ */ | ||
65 | #define PDAUDIOCF_IRQAKM (1<<2) /* AKM IRQ */ | ||
66 | |||
67 | /* PDAUDIOCF_REG_IER */ | ||
68 | #define PDAUDIOCF_IRQLVLEN0 (1<<0) /* fill threshold levels; 00 = none, 01 = 1/8th of buffer */ | ||
69 | #define PDAUDIOCF_IRQLVLEN1 (1<<1) /* 10 = 1/4th of buffer, 11 = 1/2th of buffer */ | ||
70 | #define PDAUDIOCF_IRQOVREN (1<<2) /* enable overrun IRQ */ | ||
71 | #define PDAUDIOCF_IRQAKMEN (1<<3) /* enable AKM IRQ */ | ||
72 | #define PDAUDIOCF_BLUEDUTY0 (1<<8) /* blue LED duty cycle; 00 = 100%, 01 = 50% */ | ||
73 | #define PDAUDIOCF_BLUEDUTY1 (1<<9) /* 02 = 25%, 11 = 12% */ | ||
74 | #define PDAUDIOCF_REDDUTY0 (1<<10) /* red LED duty cycle; 00 = 100%, 01 = 50% */ | ||
75 | #define PDAUDIOCF_REDDUTY1 (1<<11) /* 02 = 25%, 11 = 12% */ | ||
76 | #define PDAUDIOCF_BLUESDD (1<<12) /* blue LED against SDD bit */ | ||
77 | #define PDAUDIOCF_BLUEMODULATE (1<<13) /* save power when 100% duty cycle selected */ | ||
78 | #define PDAUDIOCF_REDMODULATE (1<<14) /* save power when 100% duty cycle selected */ | ||
79 | #define PDAUDIOCF_HALFRATE (1<<15) /* slow both LED blinks by half (also spdif detect rate) */ | ||
80 | |||
81 | /* chip status */ | ||
82 | #define PDAUDIOCF_STAT_IS_STALE (1<<0) | ||
83 | #define PDAUDIOCF_STAT_IS_CONFIGURED (1<<1) | ||
84 | #define PDAUDIOCF_STAT_IS_SUSPENDED (1<<2) | ||
85 | |||
86 | typedef struct { | ||
87 | snd_card_t *card; | ||
88 | int index; | ||
89 | |||
90 | unsigned long port; | ||
91 | int irq; | ||
92 | |||
93 | spinlock_t reg_lock; | ||
94 | unsigned short regmap[8]; | ||
95 | unsigned short suspend_reg_scr; | ||
96 | struct tasklet_struct tq; | ||
97 | |||
98 | spinlock_t ak4117_lock; | ||
99 | ak4117_t *ak4117; | ||
100 | |||
101 | unsigned int chip_status; | ||
102 | |||
103 | snd_pcm_t *pcm; | ||
104 | snd_pcm_substream_t *pcm_substream; | ||
105 | unsigned int pcm_running: 1; | ||
106 | unsigned int pcm_channels; | ||
107 | unsigned int pcm_swab; | ||
108 | unsigned int pcm_little; | ||
109 | unsigned int pcm_frame; | ||
110 | unsigned int pcm_sample; | ||
111 | unsigned int pcm_xor; | ||
112 | unsigned int pcm_size; | ||
113 | unsigned int pcm_period; | ||
114 | unsigned int pcm_tdone; | ||
115 | unsigned int pcm_hwptr; | ||
116 | void *pcm_area; | ||
117 | |||
118 | /* pcmcia stuff */ | ||
119 | dev_link_t link; | ||
120 | dev_node_t node; | ||
121 | } pdacf_t; | ||
122 | |||
123 | static inline void pdacf_reg_write(pdacf_t *chip, unsigned char reg, unsigned short val) | ||
124 | { | ||
125 | outw(chip->regmap[reg>>1] = val, chip->port + reg); | ||
126 | } | ||
127 | |||
128 | static inline unsigned short pdacf_reg_read(pdacf_t *chip, unsigned char reg) | ||
129 | { | ||
130 | return inw(chip->port + reg); | ||
131 | } | ||
132 | |||
133 | pdacf_t *snd_pdacf_create(snd_card_t *card); | ||
134 | int snd_pdacf_ak4117_create(pdacf_t *pdacf); | ||
135 | void snd_pdacf_powerdown(pdacf_t *chip); | ||
136 | #ifdef CONFIG_PM | ||
137 | int snd_pdacf_suspend(snd_card_t *card, pm_message_t state); | ||
138 | int snd_pdacf_resume(snd_card_t *card); | ||
139 | #endif | ||
140 | int snd_pdacf_pcm_new(pdacf_t *chip); | ||
141 | irqreturn_t pdacf_interrupt(int irq, void *dev, struct pt_regs *regs); | ||
142 | void pdacf_tasklet(unsigned long private_data); | ||
143 | void pdacf_reinit(pdacf_t *chip, int resume); | ||
144 | |||
145 | #endif /* __PDAUDIOCF_H */ | ||
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c new file mode 100644 index 000000000000..a2132e3763dd --- /dev/null +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c | |||
@@ -0,0 +1,291 @@ | |||
1 | /* | ||
2 | * Driver for Sound Core PDAudioCF soundcard | ||
3 | * | ||
4 | * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <sound/core.h> | ||
24 | #include <sound/info.h> | ||
25 | #include "pdaudiocf.h" | ||
26 | #include <sound/initval.h> | ||
27 | |||
28 | /* | ||
29 | * | ||
30 | */ | ||
31 | static unsigned char pdacf_ak4117_read(void *private_data, unsigned char reg) | ||
32 | { | ||
33 | pdacf_t *chip = private_data; | ||
34 | unsigned long timeout; | ||
35 | unsigned long flags; | ||
36 | unsigned char res; | ||
37 | |||
38 | spin_lock_irqsave(&chip->ak4117_lock, flags); | ||
39 | timeout = 1000; | ||
40 | while (pdacf_reg_read(chip, PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { | ||
41 | udelay(5); | ||
42 | if (--timeout == 0) { | ||
43 | spin_unlock_irqrestore(&chip->ak4117_lock, flags); | ||
44 | snd_printk(KERN_ERR "AK4117 ready timeout (read)\n"); | ||
45 | return 0; | ||
46 | } | ||
47 | } | ||
48 | pdacf_reg_write(chip, PDAUDIOCF_REG_AK_IFR, (u16)reg << 8); | ||
49 | timeout = 1000; | ||
50 | while (pdacf_reg_read(chip, PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { | ||
51 | udelay(5); | ||
52 | if (--timeout == 0) { | ||
53 | spin_unlock_irqrestore(&chip->ak4117_lock, flags); | ||
54 | snd_printk(KERN_ERR "AK4117 read timeout (read2)\n"); | ||
55 | return 0; | ||
56 | } | ||
57 | } | ||
58 | res = (unsigned char)pdacf_reg_read(chip, PDAUDIOCF_REG_AK_IFR); | ||
59 | spin_unlock_irqrestore(&chip->ak4117_lock, flags); | ||
60 | return res; | ||
61 | } | ||
62 | |||
63 | static void pdacf_ak4117_write(void *private_data, unsigned char reg, unsigned char val) | ||
64 | { | ||
65 | pdacf_t *chip = private_data; | ||
66 | unsigned long timeout; | ||
67 | unsigned long flags; | ||
68 | |||
69 | spin_lock_irqsave(&chip->ak4117_lock, flags); | ||
70 | timeout = 1000; | ||
71 | while (inw(chip->port + PDAUDIOCF_REG_SCR) & PDAUDIOCF_AK_SBP) { | ||
72 | udelay(5); | ||
73 | if (--timeout == 0) { | ||
74 | spin_unlock_irqrestore(&chip->ak4117_lock, flags); | ||
75 | snd_printk(KERN_ERR "AK4117 ready timeout (write)\n"); | ||
76 | return; | ||
77 | } | ||
78 | } | ||
79 | outw((u16)reg << 8 | val | (1<<13), chip->port + PDAUDIOCF_REG_AK_IFR); | ||
80 | spin_unlock_irqrestore(&chip->ak4117_lock, flags); | ||
81 | } | ||
82 | |||
83 | #if 0 | ||
84 | void pdacf_dump(pdacf_t *chip) | ||
85 | { | ||
86 | printk("PDAUDIOCF DUMP (0x%lx):\n", chip->port); | ||
87 | printk("WPD : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_WDP)); | ||
88 | printk("RDP : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_RDP)); | ||
89 | printk("TCR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_TCR)); | ||
90 | printk("SCR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_SCR)); | ||
91 | printk("ISR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_ISR)); | ||
92 | printk("IER : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_IER)); | ||
93 | printk("AK_IFR : 0x%x\n", inw(chip->port + PDAUDIOCF_REG_AK_IFR)); | ||
94 | } | ||
95 | #endif | ||
96 | |||
97 | static int pdacf_reset(pdacf_t *chip, int powerdown) | ||
98 | { | ||
99 | u16 val; | ||
100 | |||
101 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); | ||
102 | val |= PDAUDIOCF_PDN; | ||
103 | val &= ~PDAUDIOCF_RECORD; /* for sure */ | ||
104 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); | ||
105 | udelay(5); | ||
106 | val |= PDAUDIOCF_RST; | ||
107 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); | ||
108 | udelay(200); | ||
109 | val &= ~PDAUDIOCF_RST; | ||
110 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); | ||
111 | udelay(5); | ||
112 | if (!powerdown) { | ||
113 | val &= ~PDAUDIOCF_PDN; | ||
114 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); | ||
115 | udelay(200); | ||
116 | } | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | void pdacf_reinit(pdacf_t *chip, int resume) | ||
121 | { | ||
122 | pdacf_reset(chip, 0); | ||
123 | if (resume) | ||
124 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, chip->suspend_reg_scr); | ||
125 | snd_ak4117_reinit(chip->ak4117); | ||
126 | pdacf_reg_write(chip, PDAUDIOCF_REG_TCR, chip->regmap[PDAUDIOCF_REG_TCR>>1]); | ||
127 | pdacf_reg_write(chip, PDAUDIOCF_REG_IER, chip->regmap[PDAUDIOCF_REG_IER>>1]); | ||
128 | } | ||
129 | |||
130 | static void pdacf_proc_read(snd_info_entry_t * entry, | ||
131 | snd_info_buffer_t * buffer) | ||
132 | { | ||
133 | pdacf_t *chip = entry->private_data; | ||
134 | u16 tmp; | ||
135 | |||
136 | snd_iprintf(buffer, "PDAudioCF\n\n"); | ||
137 | tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); | ||
138 | snd_iprintf(buffer, "FPGA revision : 0x%x\n", PDAUDIOCF_FPGAREV(tmp)); | ||
139 | |||
140 | } | ||
141 | |||
142 | static void pdacf_proc_init(pdacf_t *chip) | ||
143 | { | ||
144 | snd_info_entry_t *entry; | ||
145 | |||
146 | if (! snd_card_proc_new(chip->card, "pdaudiocf", &entry)) | ||
147 | snd_info_set_text_ops(entry, chip, 1024, pdacf_proc_read); | ||
148 | } | ||
149 | |||
150 | pdacf_t *snd_pdacf_create(snd_card_t *card) | ||
151 | { | ||
152 | pdacf_t *chip; | ||
153 | |||
154 | chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
155 | if (chip == NULL) | ||
156 | return NULL; | ||
157 | chip->card = card; | ||
158 | spin_lock_init(&chip->reg_lock); | ||
159 | spin_lock_init(&chip->ak4117_lock); | ||
160 | tasklet_init(&chip->tq, pdacf_tasklet, (unsigned long)chip); | ||
161 | card->private_data = chip; | ||
162 | |||
163 | pdacf_proc_init(chip); | ||
164 | return chip; | ||
165 | } | ||
166 | |||
167 | static void snd_pdacf_ak4117_change(ak4117_t *ak4117, unsigned char c0, unsigned char c1) | ||
168 | { | ||
169 | pdacf_t *chip = ak4117->change_callback_private; | ||
170 | unsigned long flags; | ||
171 | u16 val; | ||
172 | |||
173 | if (!(c0 & AK4117_UNLCK)) | ||
174 | return; | ||
175 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
176 | val = chip->regmap[PDAUDIOCF_REG_SCR>>1]; | ||
177 | if (ak4117->rcs0 & AK4117_UNLCK) | ||
178 | val |= PDAUDIOCF_BLUE_LED_OFF; | ||
179 | else | ||
180 | val &= ~PDAUDIOCF_BLUE_LED_OFF; | ||
181 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); | ||
182 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
183 | } | ||
184 | |||
185 | int snd_pdacf_ak4117_create(pdacf_t *chip) | ||
186 | { | ||
187 | int err; | ||
188 | u16 val; | ||
189 | /* design note: if we unmask PLL unlock, parity, valid, audio or auto bit interrupts */ | ||
190 | /* from AK4117 then INT1 pin from AK4117 will be high all time, because PCMCIA interrupts are */ | ||
191 | /* egde based and FPGA does logical OR for all interrupt sources, we cannot use these */ | ||
192 | /* high-rate sources */ | ||
193 | static unsigned char pgm[5] = { | ||
194 | AK4117_XTL_24_576M | AK4117_EXCT, /* AK4117_REG_PWRDN */ | ||
195 | AK4117_CM_PLL_XTAL | AK4117_PKCS_128fs | AK4117_XCKS_128fs, /* AK4117_REQ_CLOCK */ | ||
196 | AK4117_EFH_1024LRCLK | AK4117_DIF_24R | AK4117_IPS, /* AK4117_REG_IO */ | ||
197 | 0xff, /* AK4117_REG_INT0_MASK */ | ||
198 | AK4117_MAUTO | AK4117_MAUD | AK4117_MULK | AK4117_MPAR | AK4117_MV, /* AK4117_REG_INT1_MASK */ | ||
199 | }; | ||
200 | |||
201 | err = pdacf_reset(chip, 0); | ||
202 | if (err < 0) | ||
203 | return err; | ||
204 | err = snd_ak4117_create(chip->card, pdacf_ak4117_read, pdacf_ak4117_write, pgm, chip, &chip->ak4117); | ||
205 | if (err < 0) | ||
206 | return err; | ||
207 | |||
208 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_TCR); | ||
209 | #if 1 /* normal operation */ | ||
210 | val &= ~(PDAUDIOCF_ELIMAKMBIT|PDAUDIOCF_TESTDATASEL); | ||
211 | #else /* debug */ | ||
212 | val |= PDAUDIOCF_ELIMAKMBIT; | ||
213 | val &= ~PDAUDIOCF_TESTDATASEL; | ||
214 | #endif | ||
215 | pdacf_reg_write(chip, PDAUDIOCF_REG_TCR, val); | ||
216 | |||
217 | /* setup the FPGA to match AK4117 setup */ | ||
218 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); | ||
219 | val &= ~(PDAUDIOCF_CLKDIV0 | PDAUDIOCF_CLKDIV1); /* use 24.576Mhz clock */ | ||
220 | val &= ~(PDAUDIOCF_RED_LED_OFF|PDAUDIOCF_BLUE_LED_OFF); | ||
221 | val |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1; /* 24-bit data */ | ||
222 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); | ||
223 | |||
224 | /* setup LEDs and IRQ */ | ||
225 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_IER); | ||
226 | val &= ~(PDAUDIOCF_IRQLVLEN0 | PDAUDIOCF_IRQLVLEN1); | ||
227 | val &= ~(PDAUDIOCF_BLUEDUTY0 | PDAUDIOCF_REDDUTY0 | PDAUDIOCF_REDDUTY1); | ||
228 | val |= PDAUDIOCF_BLUEDUTY1 | PDAUDIOCF_HALFRATE; | ||
229 | val |= PDAUDIOCF_IRQOVREN | PDAUDIOCF_IRQAKMEN; | ||
230 | pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val); | ||
231 | |||
232 | chip->ak4117->change_callback_private = chip; | ||
233 | chip->ak4117->change_callback = snd_pdacf_ak4117_change; | ||
234 | |||
235 | /* update LED status */ | ||
236 | snd_pdacf_ak4117_change(chip->ak4117, AK4117_UNLCK, 0); | ||
237 | |||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | void snd_pdacf_powerdown(pdacf_t *chip) | ||
242 | { | ||
243 | u16 val; | ||
244 | |||
245 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); | ||
246 | chip->suspend_reg_scr = val; | ||
247 | val |= PDAUDIOCF_RED_LED_OFF | PDAUDIOCF_BLUE_LED_OFF; | ||
248 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, val); | ||
249 | /* disable interrupts, but use direct write to preserve old register value in chip->regmap */ | ||
250 | val = inw(chip->port + PDAUDIOCF_REG_IER); | ||
251 | val &= ~(PDAUDIOCF_IRQOVREN|PDAUDIOCF_IRQAKMEN|PDAUDIOCF_IRQLVLEN0|PDAUDIOCF_IRQLVLEN1); | ||
252 | outw(val, chip->port + PDAUDIOCF_REG_IER); | ||
253 | pdacf_reset(chip, 1); | ||
254 | } | ||
255 | |||
256 | #ifdef CONFIG_PM | ||
257 | |||
258 | int snd_pdacf_suspend(snd_card_t *card, pm_message_t state) | ||
259 | { | ||
260 | pdacf_t *chip = card->pm_private_data; | ||
261 | u16 val; | ||
262 | |||
263 | snd_pcm_suspend_all(chip->pcm); | ||
264 | /* disable interrupts, but use direct write to preserve old register value in chip->regmap */ | ||
265 | val = inw(chip->port + PDAUDIOCF_REG_IER); | ||
266 | val &= ~(PDAUDIOCF_IRQOVREN|PDAUDIOCF_IRQAKMEN|PDAUDIOCF_IRQLVLEN0|PDAUDIOCF_IRQLVLEN1); | ||
267 | outw(val, chip->port + PDAUDIOCF_REG_IER); | ||
268 | chip->chip_status |= PDAUDIOCF_STAT_IS_SUSPENDED; /* ignore interrupts from now */ | ||
269 | snd_pdacf_powerdown(chip); | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static inline int check_signal(pdacf_t *chip) | ||
274 | { | ||
275 | return (chip->ak4117->rcs0 & AK4117_UNLCK) == 0; | ||
276 | } | ||
277 | |||
278 | int snd_pdacf_resume(snd_card_t *card) | ||
279 | { | ||
280 | pdacf_t *chip = card->pm_private_data; | ||
281 | int timeout = 40; | ||
282 | |||
283 | pdacf_reinit(chip, 1); | ||
284 | /* wait for AK4117's PLL */ | ||
285 | while (timeout-- > 0 && | ||
286 | (snd_ak4117_external_rate(chip->ak4117) <= 0 || !check_signal(chip))) | ||
287 | mdelay(1); | ||
288 | chip->chip_status &= ~PDAUDIOCF_STAT_IS_SUSPENDED; | ||
289 | return 0; | ||
290 | } | ||
291 | #endif | ||
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c new file mode 100644 index 000000000000..255b63444b5d --- /dev/null +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c | |||
@@ -0,0 +1,325 @@ | |||
1 | /* | ||
2 | * Driver for Sound Core PDAudioCF soundcard | ||
3 | * | ||
4 | * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <sound/core.h> | ||
23 | #include "pdaudiocf.h" | ||
24 | #include <sound/initval.h> | ||
25 | |||
26 | /* | ||
27 | * | ||
28 | */ | ||
29 | irqreturn_t pdacf_interrupt(int irq, void *dev, struct pt_regs *regs) | ||
30 | { | ||
31 | pdacf_t *chip = dev; | ||
32 | unsigned short stat; | ||
33 | |||
34 | if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE| | ||
35 | PDAUDIOCF_STAT_IS_CONFIGURED| | ||
36 | PDAUDIOCF_STAT_IS_SUSPENDED)) != PDAUDIOCF_STAT_IS_CONFIGURED) | ||
37 | return IRQ_HANDLED; /* IRQ_NONE here? */ | ||
38 | |||
39 | stat = inw(chip->port + PDAUDIOCF_REG_ISR); | ||
40 | if (stat & (PDAUDIOCF_IRQLVL|PDAUDIOCF_IRQOVR)) { | ||
41 | if (stat & PDAUDIOCF_IRQOVR) /* should never happen */ | ||
42 | snd_printk(KERN_ERR "PDAUDIOCF SRAM buffer overrun detected!\n"); | ||
43 | if (chip->pcm_substream) | ||
44 | tasklet_hi_schedule(&chip->tq); | ||
45 | if (!(stat & PDAUDIOCF_IRQAKM)) | ||
46 | stat |= PDAUDIOCF_IRQAKM; /* check rate */ | ||
47 | } | ||
48 | if (regs != NULL) | ||
49 | snd_ak4117_check_rate_and_errors(chip->ak4117, 0); | ||
50 | return IRQ_HANDLED; | ||
51 | } | ||
52 | |||
53 | static inline void pdacf_transfer_mono16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) | ||
54 | { | ||
55 | while (size-- > 0) { | ||
56 | *dst++ = inw(rdp_port) ^ xor; | ||
57 | inw(rdp_port); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | static inline void pdacf_transfer_mono32(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) | ||
62 | { | ||
63 | register u16 val1, val2; | ||
64 | |||
65 | while (size-- > 0) { | ||
66 | val1 = inw(rdp_port); | ||
67 | val2 = inw(rdp_port); | ||
68 | inw(rdp_port); | ||
69 | *dst++ = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; | ||
70 | } | ||
71 | } | ||
72 | |||
73 | static inline void pdacf_transfer_stereo16(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) | ||
74 | { | ||
75 | while (size-- > 0) { | ||
76 | *dst++ = inw(rdp_port) ^ xor; | ||
77 | *dst++ = inw(rdp_port) ^ xor; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | static inline void pdacf_transfer_stereo32(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) | ||
82 | { | ||
83 | register u16 val1, val2, val3; | ||
84 | |||
85 | while (size-- > 0) { | ||
86 | val1 = inw(rdp_port); | ||
87 | val2 = inw(rdp_port); | ||
88 | val3 = inw(rdp_port); | ||
89 | *dst++ = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; | ||
90 | *dst++ = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | static inline void pdacf_transfer_mono16sw(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) | ||
95 | { | ||
96 | while (size-- > 0) { | ||
97 | *dst++ = swab16(inw(rdp_port) ^ xor); | ||
98 | inw(rdp_port); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | static inline void pdacf_transfer_mono32sw(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) | ||
103 | { | ||
104 | register u16 val1, val2; | ||
105 | |||
106 | while (size-- > 0) { | ||
107 | val1 = inw(rdp_port); | ||
108 | val2 = inw(rdp_port); | ||
109 | inw(rdp_port); | ||
110 | *dst++ = swab32((((val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | static inline void pdacf_transfer_stereo16sw(u16 *dst, u16 xor, unsigned int size, unsigned long rdp_port) | ||
115 | { | ||
116 | while (size-- > 0) { | ||
117 | *dst++ = swab16(inw(rdp_port) ^ xor); | ||
118 | *dst++ = swab16(inw(rdp_port) ^ xor); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | static inline void pdacf_transfer_stereo32sw(u32 *dst, u32 xor, unsigned int size, unsigned long rdp_port) | ||
123 | { | ||
124 | register u16 val1, val2, val3; | ||
125 | |||
126 | while (size-- > 0) { | ||
127 | val1 = inw(rdp_port); | ||
128 | val2 = inw(rdp_port); | ||
129 | val3 = inw(rdp_port); | ||
130 | *dst++ = swab32((((val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor); | ||
131 | *dst++ = swab32((((u32)val3 << 16) | (val2 & 0xff00)) ^ xor); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | static inline void pdacf_transfer_mono24le(u8 *dst, u16 xor, unsigned int size, unsigned long rdp_port) | ||
136 | { | ||
137 | register u16 val1, val2; | ||
138 | register u32 xval1; | ||
139 | |||
140 | while (size-- > 0) { | ||
141 | val1 = inw(rdp_port); | ||
142 | val2 = inw(rdp_port); | ||
143 | inw(rdp_port); | ||
144 | xval1 = (((val2 & 0xff) << 8) | (val1 << 16)) ^ xor; | ||
145 | *dst++ = (u8)(xval1 >> 8); | ||
146 | *dst++ = (u8)(xval1 >> 16); | ||
147 | *dst++ = (u8)(xval1 >> 24); | ||
148 | } | ||
149 | } | ||
150 | |||
151 | static inline void pdacf_transfer_mono24be(u8 *dst, u16 xor, unsigned int size, unsigned long rdp_port) | ||
152 | { | ||
153 | register u16 val1, val2; | ||
154 | register u32 xval1; | ||
155 | |||
156 | while (size-- > 0) { | ||
157 | val1 = inw(rdp_port); | ||
158 | val2 = inw(rdp_port); | ||
159 | inw(rdp_port); | ||
160 | xval1 = (((val2 & 0xff) << 8) | (val1 << 16)) ^ xor; | ||
161 | *dst++ = (u8)(xval1 >> 24); | ||
162 | *dst++ = (u8)(xval1 >> 16); | ||
163 | *dst++ = (u8)(xval1 >> 8); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | static inline void pdacf_transfer_stereo24le(u8 *dst, u32 xor, unsigned int size, unsigned long rdp_port) | ||
168 | { | ||
169 | register u16 val1, val2, val3; | ||
170 | register u32 xval1, xval2; | ||
171 | |||
172 | while (size-- > 0) { | ||
173 | val1 = inw(rdp_port); | ||
174 | val2 = inw(rdp_port); | ||
175 | val3 = inw(rdp_port); | ||
176 | xval1 = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; | ||
177 | xval2 = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor; | ||
178 | *dst++ = (u8)(xval1 >> 8); | ||
179 | *dst++ = (u8)(xval1 >> 16); | ||
180 | *dst++ = (u8)(xval1 >> 24); | ||
181 | *dst++ = (u8)(xval2 >> 8); | ||
182 | *dst++ = (u8)(xval2 >> 16); | ||
183 | *dst++ = (u8)(xval2 >> 24); | ||
184 | } | ||
185 | } | ||
186 | |||
187 | static inline void pdacf_transfer_stereo24be(u8 *dst, u32 xor, unsigned int size, unsigned long rdp_port) | ||
188 | { | ||
189 | register u16 val1, val2, val3; | ||
190 | register u32 xval1, xval2; | ||
191 | |||
192 | while (size-- > 0) { | ||
193 | val1 = inw(rdp_port); | ||
194 | val2 = inw(rdp_port); | ||
195 | val3 = inw(rdp_port); | ||
196 | xval1 = ((((u32)val2 & 0xff) << 24) | ((u32)val1 << 8)) ^ xor; | ||
197 | xval2 = (((u32)val3 << 16) | (val2 & 0xff00)) ^ xor; | ||
198 | *dst++ = (u8)(xval1 >> 24); | ||
199 | *dst++ = (u8)(xval1 >> 16); | ||
200 | *dst++ = (u8)(xval1 >> 8); | ||
201 | *dst++ = (u8)(xval2 >> 24); | ||
202 | *dst++ = (u8)(xval2 >> 16); | ||
203 | *dst++ = (u8)(xval2 >> 8); | ||
204 | } | ||
205 | } | ||
206 | |||
207 | static void pdacf_transfer(pdacf_t *chip, unsigned int size, unsigned int off) | ||
208 | { | ||
209 | unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD; | ||
210 | unsigned int xor = chip->pcm_xor; | ||
211 | |||
212 | if (chip->pcm_sample == 3) { | ||
213 | if (chip->pcm_little) { | ||
214 | if (chip->pcm_channels == 1) { | ||
215 | pdacf_transfer_mono24le((char *)chip->pcm_area + (off * 3), xor, size, rdp_port); | ||
216 | } else { | ||
217 | pdacf_transfer_stereo24le((char *)chip->pcm_area + (off * 6), xor, size, rdp_port); | ||
218 | } | ||
219 | } else { | ||
220 | if (chip->pcm_channels == 1) { | ||
221 | pdacf_transfer_mono24be((char *)chip->pcm_area + (off * 3), xor, size, rdp_port); | ||
222 | } else { | ||
223 | pdacf_transfer_stereo24be((char *)chip->pcm_area + (off * 6), xor, size, rdp_port); | ||
224 | } | ||
225 | } | ||
226 | return; | ||
227 | } | ||
228 | if (chip->pcm_swab == 0) { | ||
229 | if (chip->pcm_channels == 1) { | ||
230 | if (chip->pcm_frame == 2) { | ||
231 | pdacf_transfer_mono16((u16 *)chip->pcm_area + off, xor, size, rdp_port); | ||
232 | } else { | ||
233 | pdacf_transfer_mono32((u32 *)chip->pcm_area + off, xor, size, rdp_port); | ||
234 | } | ||
235 | } else { | ||
236 | if (chip->pcm_frame == 2) { | ||
237 | pdacf_transfer_stereo16((u16 *)chip->pcm_area + (off * 2), xor, size, rdp_port); | ||
238 | } else { | ||
239 | pdacf_transfer_stereo32((u32 *)chip->pcm_area + (off * 2), xor, size, rdp_port); | ||
240 | } | ||
241 | } | ||
242 | } else { | ||
243 | if (chip->pcm_channels == 1) { | ||
244 | if (chip->pcm_frame == 2) { | ||
245 | pdacf_transfer_mono16sw((u16 *)chip->pcm_area + off, xor, size, rdp_port); | ||
246 | } else { | ||
247 | pdacf_transfer_mono32sw((u32 *)chip->pcm_area + off, xor, size, rdp_port); | ||
248 | } | ||
249 | } else { | ||
250 | if (chip->pcm_frame == 2) { | ||
251 | pdacf_transfer_stereo16sw((u16 *)chip->pcm_area + (off * 2), xor, size, rdp_port); | ||
252 | } else { | ||
253 | pdacf_transfer_stereo32sw((u32 *)chip->pcm_area + (off * 2), xor, size, rdp_port); | ||
254 | } | ||
255 | } | ||
256 | } | ||
257 | } | ||
258 | |||
259 | void pdacf_tasklet(unsigned long private_data) | ||
260 | { | ||
261 | pdacf_t *chip = (pdacf_t *) private_data; | ||
262 | int size, off, cont, rdp, wdp; | ||
263 | |||
264 | if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|PDAUDIOCF_STAT_IS_CONFIGURED)) != PDAUDIOCF_STAT_IS_CONFIGURED) | ||
265 | return; | ||
266 | |||
267 | if (chip->pcm_substream == NULL || chip->pcm_substream->runtime == NULL || !snd_pcm_running(chip->pcm_substream)) | ||
268 | return; | ||
269 | |||
270 | rdp = inw(chip->port + PDAUDIOCF_REG_RDP); | ||
271 | wdp = inw(chip->port + PDAUDIOCF_REG_WDP); | ||
272 | // printk("TASKLET: rdp = %x, wdp = %x\n", rdp, wdp); | ||
273 | size = wdp - rdp; | ||
274 | if (size < 0) | ||
275 | size += 0x10000; | ||
276 | if (size == 0) | ||
277 | size = 0x10000; | ||
278 | size /= chip->pcm_frame; | ||
279 | if (size > 64) | ||
280 | size -= 32; | ||
281 | |||
282 | #if 0 | ||
283 | chip->pcm_hwptr += size; | ||
284 | chip->pcm_hwptr %= chip->pcm_size; | ||
285 | chip->pcm_tdone += size; | ||
286 | if (chip->pcm_frame == 2) { | ||
287 | unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD; | ||
288 | while (size-- > 0) { | ||
289 | inw(rdp_port); | ||
290 | inw(rdp_port); | ||
291 | } | ||
292 | } else { | ||
293 | unsigned long rdp_port = chip->port + PDAUDIOCF_REG_MD; | ||
294 | while (size-- > 0) { | ||
295 | inw(rdp_port); | ||
296 | inw(rdp_port); | ||
297 | inw(rdp_port); | ||
298 | } | ||
299 | } | ||
300 | #else | ||
301 | off = chip->pcm_hwptr + chip->pcm_tdone; | ||
302 | off %= chip->pcm_size; | ||
303 | chip->pcm_tdone += size; | ||
304 | while (size > 0) { | ||
305 | cont = chip->pcm_size - off; | ||
306 | if (cont > size) | ||
307 | cont = size; | ||
308 | pdacf_transfer(chip, cont, off); | ||
309 | off += cont; | ||
310 | off %= chip->pcm_size; | ||
311 | size -= cont; | ||
312 | } | ||
313 | #endif | ||
314 | spin_lock(&chip->reg_lock); | ||
315 | while (chip->pcm_tdone >= chip->pcm_period) { | ||
316 | chip->pcm_hwptr += chip->pcm_period; | ||
317 | chip->pcm_hwptr %= chip->pcm_size; | ||
318 | chip->pcm_tdone -= chip->pcm_period; | ||
319 | spin_unlock(&chip->reg_lock); | ||
320 | snd_pcm_period_elapsed(chip->pcm_substream); | ||
321 | spin_lock(&chip->reg_lock); | ||
322 | } | ||
323 | spin_unlock(&chip->reg_lock); | ||
324 | // printk("TASKLET: end\n"); | ||
325 | } | ||
diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c new file mode 100644 index 000000000000..0a954dc11b73 --- /dev/null +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c | |||
@@ -0,0 +1,361 @@ | |||
1 | /* | ||
2 | * Driver for Sound Core PDAudioCF soundcards | ||
3 | * | ||
4 | * PCM part | ||
5 | * | ||
6 | * Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/vmalloc.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/asoundef.h> | ||
29 | #include "pdaudiocf.h" | ||
30 | |||
31 | |||
32 | /* | ||
33 | * we use a vmalloc'ed (sg-)buffer | ||
34 | */ | ||
35 | |||
36 | /* get the physical page pointer on the given offset */ | ||
37 | static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, unsigned long offset) | ||
38 | { | ||
39 | void *pageptr = subs->runtime->dma_area + offset; | ||
40 | return vmalloc_to_page(pageptr); | ||
41 | } | ||
42 | |||
43 | /* | ||
44 | * hw_params callback | ||
45 | * NOTE: this may be called not only once per pcm open! | ||
46 | */ | ||
47 | static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size) | ||
48 | { | ||
49 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
50 | if (runtime->dma_area) { | ||
51 | if (runtime->dma_bytes >= size) | ||
52 | return 0; /* already enough large */ | ||
53 | vfree_nocheck(runtime->dma_area); | ||
54 | } | ||
55 | runtime->dma_area = vmalloc_nocheck(size); | ||
56 | if (! runtime->dma_area) | ||
57 | return -ENOMEM; | ||
58 | runtime->dma_bytes = size; | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | /* | ||
63 | * hw_free callback | ||
64 | * NOTE: this may be called not only once per pcm open! | ||
65 | */ | ||
66 | static int snd_pcm_free_vmalloc_buffer(snd_pcm_substream_t *subs) | ||
67 | { | ||
68 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
69 | if (runtime->dma_area) { | ||
70 | vfree_nocheck(runtime->dma_area); | ||
71 | runtime->dma_area = NULL; | ||
72 | } | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | /* | ||
77 | * clear the SRAM contents | ||
78 | */ | ||
79 | static int pdacf_pcm_clear_sram(pdacf_t *chip) | ||
80 | { | ||
81 | int max_loop = 64 * 1024; | ||
82 | |||
83 | while (inw(chip->port + PDAUDIOCF_REG_RDP) != inw(chip->port + PDAUDIOCF_REG_WDP)) { | ||
84 | if (max_loop-- < 0) | ||
85 | return -EIO; | ||
86 | inw(chip->port + PDAUDIOCF_REG_MD); | ||
87 | } | ||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | /* | ||
92 | * pdacf_pcm_trigger - trigger callback for capture | ||
93 | */ | ||
94 | static int pdacf_pcm_trigger(snd_pcm_substream_t *subs, int cmd) | ||
95 | { | ||
96 | pdacf_t *chip = snd_pcm_substream_chip(subs); | ||
97 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
98 | int inc, ret = 0, rate; | ||
99 | unsigned short mask, val, tmp; | ||
100 | |||
101 | if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) | ||
102 | return -EBUSY; | ||
103 | |||
104 | switch (cmd) { | ||
105 | case SNDRV_PCM_TRIGGER_START: | ||
106 | chip->pcm_hwptr = 0; | ||
107 | chip->pcm_tdone = 0; | ||
108 | /* fall thru */ | ||
109 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
110 | case SNDRV_PCM_TRIGGER_RESUME: | ||
111 | mask = 0; | ||
112 | val = PDAUDIOCF_RECORD; | ||
113 | inc = 1; | ||
114 | rate = snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_STAT|AK4117_CHECK_NO_RATE); | ||
115 | break; | ||
116 | case SNDRV_PCM_TRIGGER_STOP: | ||
117 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
118 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
119 | mask = PDAUDIOCF_RECORD; | ||
120 | val = 0; | ||
121 | inc = -1; | ||
122 | rate = 0; | ||
123 | break; | ||
124 | default: | ||
125 | return -EINVAL; | ||
126 | } | ||
127 | spin_lock(&chip->reg_lock); | ||
128 | chip->pcm_running += inc; | ||
129 | tmp = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); | ||
130 | if (chip->pcm_running) { | ||
131 | if ((chip->ak4117->rcs0 & AK4117_UNLCK) || runtime->rate != rate) { | ||
132 | chip->pcm_running -= inc; | ||
133 | ret = -EIO; | ||
134 | goto __end; | ||
135 | } | ||
136 | } | ||
137 | tmp &= ~mask; | ||
138 | tmp |= val; | ||
139 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, tmp); | ||
140 | __end: | ||
141 | spin_unlock(&chip->reg_lock); | ||
142 | snd_ak4117_check_rate_and_errors(chip->ak4117, AK4117_CHECK_NO_RATE); | ||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * pdacf_pcm_hw_params - hw_params callback for playback and capture | ||
148 | */ | ||
149 | static int pdacf_pcm_hw_params(snd_pcm_substream_t *subs, | ||
150 | snd_pcm_hw_params_t *hw_params) | ||
151 | { | ||
152 | return snd_pcm_alloc_vmalloc_buffer(subs, params_buffer_bytes(hw_params)); | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * pdacf_pcm_hw_free - hw_free callback for playback and capture | ||
157 | */ | ||
158 | static int pdacf_pcm_hw_free(snd_pcm_substream_t *subs) | ||
159 | { | ||
160 | return snd_pcm_free_vmalloc_buffer(subs); | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * pdacf_pcm_prepare - prepare callback for playback and capture | ||
165 | */ | ||
166 | static int pdacf_pcm_prepare(snd_pcm_substream_t *subs) | ||
167 | { | ||
168 | pdacf_t *chip = snd_pcm_substream_chip(subs); | ||
169 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
170 | u16 val, nval, aval; | ||
171 | |||
172 | if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) | ||
173 | return -EBUSY; | ||
174 | |||
175 | chip->pcm_channels = runtime->channels; | ||
176 | |||
177 | chip->pcm_little = snd_pcm_format_little_endian(runtime->format) > 0; | ||
178 | #ifdef SNDRV_LITTLE_ENDIAN | ||
179 | chip->pcm_swab = snd_pcm_format_big_endian(runtime->format) > 0; | ||
180 | #else | ||
181 | chip->pcm_swab = chip->pcm_little; | ||
182 | #endif | ||
183 | |||
184 | if (snd_pcm_format_unsigned(runtime->format)) | ||
185 | chip->pcm_xor = 0x80008000; | ||
186 | |||
187 | if (pdacf_pcm_clear_sram(chip) < 0) | ||
188 | return -EIO; | ||
189 | |||
190 | val = nval = pdacf_reg_read(chip, PDAUDIOCF_REG_SCR); | ||
191 | nval &= ~(PDAUDIOCF_DATAFMT0|PDAUDIOCF_DATAFMT1); | ||
192 | switch (runtime->format) { | ||
193 | case SNDRV_PCM_FORMAT_S16_LE: | ||
194 | case SNDRV_PCM_FORMAT_S16_BE: | ||
195 | break; | ||
196 | default: /* 24-bit */ | ||
197 | nval |= PDAUDIOCF_DATAFMT0 | PDAUDIOCF_DATAFMT1; | ||
198 | break; | ||
199 | } | ||
200 | aval = 0; | ||
201 | chip->pcm_sample = 4; | ||
202 | switch (runtime->format) { | ||
203 | case SNDRV_PCM_FORMAT_S16_LE: | ||
204 | case SNDRV_PCM_FORMAT_S16_BE: | ||
205 | aval = AK4117_DIF_16R; | ||
206 | chip->pcm_frame = 2; | ||
207 | chip->pcm_sample = 2; | ||
208 | break; | ||
209 | case SNDRV_PCM_FORMAT_S24_3LE: | ||
210 | case SNDRV_PCM_FORMAT_S24_3BE: | ||
211 | chip->pcm_sample = 3; | ||
212 | /* fall trough */ | ||
213 | default: /* 24-bit */ | ||
214 | aval = AK4117_DIF_24R; | ||
215 | chip->pcm_frame = 3; | ||
216 | chip->pcm_xor &= 0xffff0000; | ||
217 | break; | ||
218 | } | ||
219 | |||
220 | if (val != nval) { | ||
221 | snd_ak4117_reg_write(chip->ak4117, AK4117_REG_IO, AK4117_DIF2|AK4117_DIF1|AK4117_DIF0, aval); | ||
222 | pdacf_reg_write(chip, PDAUDIOCF_REG_SCR, nval); | ||
223 | } | ||
224 | |||
225 | val = pdacf_reg_read(chip, PDAUDIOCF_REG_IER); | ||
226 | val &= ~(PDAUDIOCF_IRQLVLEN1); | ||
227 | val |= PDAUDIOCF_IRQLVLEN0; | ||
228 | pdacf_reg_write(chip, PDAUDIOCF_REG_IER, val); | ||
229 | |||
230 | chip->pcm_size = runtime->buffer_size; | ||
231 | chip->pcm_period = runtime->period_size; | ||
232 | chip->pcm_area = runtime->dma_area; | ||
233 | |||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | |||
238 | /* | ||
239 | * capture hw information | ||
240 | */ | ||
241 | |||
242 | static snd_pcm_hardware_t pdacf_pcm_capture_hw = { | ||
243 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
244 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | | ||
245 | SNDRV_PCM_INFO_MMAP_VALID), | ||
246 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | | ||
247 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | | ||
248 | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE, | ||
249 | .rates = SNDRV_PCM_RATE_32000 | | ||
250 | SNDRV_PCM_RATE_44100 | | ||
251 | SNDRV_PCM_RATE_48000 | | ||
252 | SNDRV_PCM_RATE_88200 | | ||
253 | SNDRV_PCM_RATE_96000 | | ||
254 | SNDRV_PCM_RATE_176400 | | ||
255 | SNDRV_PCM_RATE_192000, | ||
256 | .rate_min = 32000, | ||
257 | .rate_max = 192000, | ||
258 | .channels_min = 1, | ||
259 | .channels_max = 2, | ||
260 | .buffer_bytes_max = (512*1024), | ||
261 | .period_bytes_min = 8*1024, | ||
262 | .period_bytes_max = (64*1024), | ||
263 | .periods_min = 2, | ||
264 | .periods_max = 128, | ||
265 | .fifo_size = 0, | ||
266 | }; | ||
267 | |||
268 | |||
269 | /* | ||
270 | * pdacf_pcm_capture_open - open callback for capture | ||
271 | */ | ||
272 | static int pdacf_pcm_capture_open(snd_pcm_substream_t *subs) | ||
273 | { | ||
274 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
275 | pdacf_t *chip = snd_pcm_substream_chip(subs); | ||
276 | |||
277 | if (chip->chip_status & PDAUDIOCF_STAT_IS_STALE) | ||
278 | return -EBUSY; | ||
279 | |||
280 | runtime->hw = pdacf_pcm_capture_hw; | ||
281 | runtime->private_data = chip; | ||
282 | chip->pcm_substream = subs; | ||
283 | |||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | /* | ||
288 | * pdacf_pcm_capture_close - close callback for capture | ||
289 | */ | ||
290 | static int pdacf_pcm_capture_close(snd_pcm_substream_t *subs) | ||
291 | { | ||
292 | pdacf_t *chip = snd_pcm_substream_chip(subs); | ||
293 | |||
294 | if (!chip) | ||
295 | return -EINVAL; | ||
296 | pdacf_reinit(chip, 0); | ||
297 | chip->pcm_substream = NULL; | ||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | |||
302 | /* | ||
303 | * pdacf_pcm_capture_pointer - pointer callback for capture | ||
304 | */ | ||
305 | static snd_pcm_uframes_t pdacf_pcm_capture_pointer(snd_pcm_substream_t *subs) | ||
306 | { | ||
307 | pdacf_t *chip = snd_pcm_substream_chip(subs); | ||
308 | return chip->pcm_hwptr; | ||
309 | } | ||
310 | |||
311 | /* | ||
312 | * operators for PCM capture | ||
313 | */ | ||
314 | static snd_pcm_ops_t pdacf_pcm_capture_ops = { | ||
315 | .open = pdacf_pcm_capture_open, | ||
316 | .close = pdacf_pcm_capture_close, | ||
317 | .ioctl = snd_pcm_lib_ioctl, | ||
318 | .hw_params = pdacf_pcm_hw_params, | ||
319 | .hw_free = pdacf_pcm_hw_free, | ||
320 | .prepare = pdacf_pcm_prepare, | ||
321 | .trigger = pdacf_pcm_trigger, | ||
322 | .pointer = pdacf_pcm_capture_pointer, | ||
323 | .page = snd_pcm_get_vmalloc_page, | ||
324 | }; | ||
325 | |||
326 | |||
327 | /* | ||
328 | * free callback for pcm | ||
329 | */ | ||
330 | static void snd_pdacf_pcm_free(snd_pcm_t *pcm) | ||
331 | { | ||
332 | pdacf_t *chip = pcm->private_data; | ||
333 | chip->pcm = NULL; | ||
334 | } | ||
335 | |||
336 | /* | ||
337 | * snd_pdacf_pcm_new - create and initialize a pcm | ||
338 | */ | ||
339 | int snd_pdacf_pcm_new(pdacf_t *chip) | ||
340 | { | ||
341 | snd_pcm_t *pcm; | ||
342 | int err; | ||
343 | |||
344 | err = snd_pcm_new(chip->card, "PDAudioCF", 0, 0, 1, &pcm); | ||
345 | if (err < 0) | ||
346 | return err; | ||
347 | |||
348 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pdacf_pcm_capture_ops); | ||
349 | |||
350 | pcm->private_data = chip; | ||
351 | pcm->private_free = snd_pdacf_pcm_free; | ||
352 | pcm->info_flags = 0; | ||
353 | strcpy(pcm->name, chip->card->shortname); | ||
354 | chip->pcm = pcm; | ||
355 | |||
356 | err = snd_ak4117_build(chip->ak4117, pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); | ||
357 | if (err < 0) | ||
358 | return err; | ||
359 | |||
360 | return 0; | ||
361 | } | ||
diff --git a/sound/pcmcia/vx/Makefile b/sound/pcmcia/vx/Makefile new file mode 100644 index 000000000000..f35dfa1af094 --- /dev/null +++ b/sound/pcmcia/vx/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-vx-cs-objs := vx_entry.o vxp_ops.o vxp_mixer.o | ||
7 | snd-vxpocket-objs := vxpocket.o | ||
8 | snd-vxp440-objs := vxp440.o | ||
9 | |||
10 | obj-$(CONFIG_SND_VXPOCKET) += snd-vxpocket.o snd-vx-cs.o | ||
11 | obj-$(CONFIG_SND_VXP440) += snd-vxp440.o snd-vx-cs.o | ||
diff --git a/sound/pcmcia/vx/vx_entry.c b/sound/pcmcia/vx/vx_entry.c new file mode 100644 index 000000000000..53d8172c52ae --- /dev/null +++ b/sound/pcmcia/vx/vx_entry.c | |||
@@ -0,0 +1,384 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VXpocket soundcards | ||
3 | * | ||
4 | * PCMCIA entry part | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <sound/core.h> | ||
25 | #include "vxpocket.h" | ||
26 | #include <pcmcia/ciscode.h> | ||
27 | #include <pcmcia/cisreg.h> | ||
28 | |||
29 | |||
30 | MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); | ||
31 | MODULE_DESCRIPTION("Common routines for Digigram PCMCIA VX drivers"); | ||
32 | MODULE_LICENSE("GPL"); | ||
33 | |||
34 | /* | ||
35 | * prototypes | ||
36 | */ | ||
37 | static void vxpocket_config(dev_link_t *link); | ||
38 | static int vxpocket_event(event_t event, int priority, event_callback_args_t *args); | ||
39 | |||
40 | |||
41 | static void vxpocket_release(dev_link_t *link) | ||
42 | { | ||
43 | if (link->state & DEV_CONFIG) { | ||
44 | /* release cs resources */ | ||
45 | pcmcia_release_configuration(link->handle); | ||
46 | pcmcia_release_io(link->handle, &link->io); | ||
47 | pcmcia_release_irq(link->handle, &link->irq); | ||
48 | link->state &= ~DEV_CONFIG; | ||
49 | } | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | * destructor | ||
54 | */ | ||
55 | static int snd_vxpocket_free(vx_core_t *chip) | ||
56 | { | ||
57 | struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; | ||
58 | struct snd_vxp_entry *hw; | ||
59 | dev_link_t *link = &vxp->link; | ||
60 | |||
61 | vxpocket_release(link); | ||
62 | |||
63 | /* Break the link with Card Services */ | ||
64 | if (link->handle) | ||
65 | pcmcia_deregister_client(link->handle); | ||
66 | |||
67 | hw = vxp->hw_entry; | ||
68 | if (hw) | ||
69 | hw->card_list[vxp->index] = NULL; | ||
70 | chip->card = NULL; | ||
71 | if (chip->dev) | ||
72 | kfree(chip->dev); | ||
73 | |||
74 | snd_vx_free_firmware(chip); | ||
75 | kfree(chip); | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static int snd_vxpocket_dev_free(snd_device_t *device) | ||
80 | { | ||
81 | vx_core_t *chip = device->device_data; | ||
82 | return snd_vxpocket_free(chip); | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | * snd_vxpocket_attach - attach callback for cs | ||
87 | * @hw: the hardware information | ||
88 | */ | ||
89 | dev_link_t *snd_vxpocket_attach(struct snd_vxp_entry *hw) | ||
90 | { | ||
91 | client_reg_t client_reg; /* Register with cardmgr */ | ||
92 | dev_link_t *link; /* Info for cardmgr */ | ||
93 | int i, ret; | ||
94 | vx_core_t *chip; | ||
95 | struct snd_vxpocket *vxp; | ||
96 | snd_card_t *card; | ||
97 | static snd_device_ops_t ops = { | ||
98 | .dev_free = snd_vxpocket_dev_free, | ||
99 | }; | ||
100 | |||
101 | snd_printdd(KERN_DEBUG "vxpocket_attach called\n"); | ||
102 | /* find an empty slot from the card list */ | ||
103 | for (i = 0; i < SNDRV_CARDS; i++) { | ||
104 | if (! hw->card_list[i]) | ||
105 | break; | ||
106 | } | ||
107 | if (i >= SNDRV_CARDS) { | ||
108 | snd_printk(KERN_ERR "vxpocket: too many cards found\n"); | ||
109 | return NULL; | ||
110 | } | ||
111 | if (! hw->enable_table[i]) | ||
112 | return NULL; /* disabled explicitly */ | ||
113 | |||
114 | /* ok, create a card instance */ | ||
115 | card = snd_card_new(hw->index_table[i], hw->id_table[i], THIS_MODULE, 0); | ||
116 | if (card == NULL) { | ||
117 | snd_printk(KERN_ERR "vxpocket: cannot create a card instance\n"); | ||
118 | return NULL; | ||
119 | } | ||
120 | |||
121 | chip = snd_vx_create(card, hw->hardware, hw->ops, | ||
122 | sizeof(struct snd_vxpocket) - sizeof(vx_core_t)); | ||
123 | if (! chip) | ||
124 | return NULL; | ||
125 | |||
126 | #ifdef SND_VX_FW_LOADER | ||
127 | /* fake a device here since pcmcia doesn't give a valid device... */ | ||
128 | chip->dev = kcalloc(1, sizeof(*chip->dev), GFP_KERNEL); | ||
129 | if (! chip->dev) { | ||
130 | snd_printk(KERN_ERR "vxp: can't malloc chip->dev\n"); | ||
131 | kfree(chip); | ||
132 | snd_card_free(card); | ||
133 | return NULL; | ||
134 | } | ||
135 | device_initialize(chip->dev); | ||
136 | sprintf(chip->dev->bus_id, "vxpocket%d", i); | ||
137 | #endif | ||
138 | |||
139 | if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops) < 0) { | ||
140 | kfree(chip); | ||
141 | snd_card_free(card); | ||
142 | return NULL; | ||
143 | } | ||
144 | |||
145 | vxp = (struct snd_vxpocket *)chip; | ||
146 | vxp->index = i; | ||
147 | vxp->hw_entry = hw; | ||
148 | chip->ibl.size = hw->ibl[i]; | ||
149 | hw->card_list[i] = chip; | ||
150 | |||
151 | link = &vxp->link; | ||
152 | link->priv = chip; | ||
153 | |||
154 | link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; | ||
155 | link->io.NumPorts1 = 16; | ||
156 | |||
157 | link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; | ||
158 | // link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; | ||
159 | |||
160 | link->irq.IRQInfo1 = IRQ_LEVEL_ID; | ||
161 | link->irq.Handler = &snd_vx_irq_handler; | ||
162 | link->irq.Instance = chip; | ||
163 | |||
164 | link->conf.Attributes = CONF_ENABLE_IRQ; | ||
165 | link->conf.Vcc = 50; | ||
166 | link->conf.IntType = INT_MEMORY_AND_IO; | ||
167 | link->conf.ConfigIndex = 1; | ||
168 | link->conf.Present = PRESENT_OPTION; | ||
169 | |||
170 | /* Register with Card Services */ | ||
171 | memset(&client_reg, 0, sizeof(client_reg)); | ||
172 | client_reg.dev_info = hw->dev_info; | ||
173 | client_reg.EventMask = | ||
174 | CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | ||
175 | #ifdef CONFIG_PM | ||
176 | | CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | ||
177 | | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME | ||
178 | #endif | ||
179 | ; | ||
180 | client_reg.event_handler = &vxpocket_event; | ||
181 | client_reg.Version = 0x0210; | ||
182 | client_reg.event_callback_args.client_data = link; | ||
183 | |||
184 | ret = pcmcia_register_client(&link->handle, &client_reg); | ||
185 | if (ret != CS_SUCCESS) { | ||
186 | cs_error(link->handle, RegisterClient, ret); | ||
187 | snd_card_free(card); | ||
188 | return NULL; | ||
189 | } | ||
190 | |||
191 | /* Chain drivers */ | ||
192 | link->next = hw->dev_list; | ||
193 | hw->dev_list = link; | ||
194 | |||
195 | /* snd_card_set_pm_callback(card, snd_vxpocket_suspend, snd_vxpocket_resume, chip); */ | ||
196 | |||
197 | return link; | ||
198 | } | ||
199 | |||
200 | |||
201 | /** | ||
202 | * snd_vxpocket_assign_resources - initialize the hardware and card instance. | ||
203 | * @port: i/o port for the card | ||
204 | * @irq: irq number for the card | ||
205 | * | ||
206 | * this function assigns the specified port and irq, boot the card, | ||
207 | * create pcm and control instances, and initialize the rest hardware. | ||
208 | * | ||
209 | * returns 0 if successful, or a negative error code. | ||
210 | */ | ||
211 | static int snd_vxpocket_assign_resources(vx_core_t *chip, int port, int irq) | ||
212 | { | ||
213 | int err; | ||
214 | snd_card_t *card = chip->card; | ||
215 | struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; | ||
216 | |||
217 | snd_printdd(KERN_DEBUG "vxpocket assign resources: port = 0x%x, irq = %d\n", port, irq); | ||
218 | vxp->port = port; | ||
219 | |||
220 | sprintf(card->shortname, "Digigram %s", card->driver); | ||
221 | sprintf(card->longname, "%s at 0x%x, irq %i", | ||
222 | card->shortname, port, irq); | ||
223 | |||
224 | chip->irq = irq; | ||
225 | |||
226 | if ((err = snd_vx_setup_firmware(chip)) < 0) | ||
227 | return err; | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | |||
233 | /* | ||
234 | * snd_vxpocket_detach - detach callback for cs | ||
235 | * @hw: the hardware information | ||
236 | */ | ||
237 | void snd_vxpocket_detach(struct snd_vxp_entry *hw, dev_link_t *link) | ||
238 | { | ||
239 | vx_core_t *chip; | ||
240 | |||
241 | if (! link) | ||
242 | return; | ||
243 | |||
244 | chip = link->priv; | ||
245 | |||
246 | snd_printdd(KERN_DEBUG "vxpocket_detach called\n"); | ||
247 | /* Remove the interface data from the linked list */ | ||
248 | if (hw) { | ||
249 | dev_link_t **linkp; | ||
250 | /* Locate device structure */ | ||
251 | for (linkp = &hw->dev_list; *linkp; linkp = &(*linkp)->next) | ||
252 | if (*linkp == link) { | ||
253 | *linkp = link->next; | ||
254 | break; | ||
255 | } | ||
256 | } | ||
257 | chip->chip_status |= VX_STAT_IS_STALE; /* to be sure */ | ||
258 | snd_card_disconnect(chip->card); | ||
259 | snd_card_free_in_thread(chip->card); | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * configuration callback | ||
264 | */ | ||
265 | |||
266 | #define CS_CHECK(fn, ret) \ | ||
267 | do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) | ||
268 | |||
269 | static void vxpocket_config(dev_link_t *link) | ||
270 | { | ||
271 | client_handle_t handle = link->handle; | ||
272 | vx_core_t *chip = link->priv; | ||
273 | struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; | ||
274 | tuple_t tuple; | ||
275 | cisparse_t *parse = NULL; | ||
276 | u_short buf[32]; | ||
277 | int last_fn, last_ret; | ||
278 | |||
279 | snd_printdd(KERN_DEBUG "vxpocket_config called\n"); | ||
280 | parse = kmalloc(sizeof(*parse), GFP_KERNEL); | ||
281 | if (! parse) { | ||
282 | snd_printk(KERN_ERR "vx: cannot allocate\n"); | ||
283 | return; | ||
284 | } | ||
285 | tuple.Attributes = 0; | ||
286 | tuple.TupleData = (cisdata_t *)buf; | ||
287 | tuple.TupleDataMax = sizeof(buf); | ||
288 | tuple.TupleOffset = 0; | ||
289 | tuple.DesiredTuple = CISTPL_CONFIG; | ||
290 | CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); | ||
291 | CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); | ||
292 | CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, parse)); | ||
293 | link->conf.ConfigBase = parse->config.base; | ||
294 | link->conf.Present = parse->config.rmask[0]; | ||
295 | |||
296 | /* Configure card */ | ||
297 | link->state |= DEV_CONFIG; | ||
298 | |||
299 | CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io)); | ||
300 | CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); | ||
301 | CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); | ||
302 | |||
303 | if (snd_vxpocket_assign_resources(chip, link->io.BasePort1, link->irq.AssignedIRQ) < 0) | ||
304 | goto failed; | ||
305 | |||
306 | link->dev = &vxp->node; | ||
307 | link->state &= ~DEV_CONFIG_PENDING; | ||
308 | kfree(parse); | ||
309 | return; | ||
310 | |||
311 | cs_failed: | ||
312 | cs_error(link->handle, last_fn, last_ret); | ||
313 | failed: | ||
314 | pcmcia_release_configuration(link->handle); | ||
315 | pcmcia_release_io(link->handle, &link->io); | ||
316 | pcmcia_release_irq(link->handle, &link->irq); | ||
317 | link->state &= ~DEV_CONFIG; | ||
318 | kfree(parse); | ||
319 | } | ||
320 | |||
321 | |||
322 | /* | ||
323 | * event callback | ||
324 | */ | ||
325 | static int vxpocket_event(event_t event, int priority, event_callback_args_t *args) | ||
326 | { | ||
327 | dev_link_t *link = args->client_data; | ||
328 | vx_core_t *chip = link->priv; | ||
329 | |||
330 | switch (event) { | ||
331 | case CS_EVENT_CARD_REMOVAL: | ||
332 | snd_printdd(KERN_DEBUG "CARD_REMOVAL..\n"); | ||
333 | link->state &= ~DEV_PRESENT; | ||
334 | if (link->state & DEV_CONFIG) { | ||
335 | chip->chip_status |= VX_STAT_IS_STALE; | ||
336 | } | ||
337 | break; | ||
338 | case CS_EVENT_CARD_INSERTION: | ||
339 | snd_printdd(KERN_DEBUG "CARD_INSERTION..\n"); | ||
340 | link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; | ||
341 | vxpocket_config(link); | ||
342 | break; | ||
343 | #ifdef CONFIG_PM | ||
344 | case CS_EVENT_PM_SUSPEND: | ||
345 | snd_printdd(KERN_DEBUG "SUSPEND\n"); | ||
346 | link->state |= DEV_SUSPEND; | ||
347 | if (chip && chip->card->pm_suspend) { | ||
348 | snd_printdd(KERN_DEBUG "snd_vx_suspend calling\n"); | ||
349 | chip->card->pm_suspend(chip->card, PMSG_SUSPEND); | ||
350 | } | ||
351 | /* Fall through... */ | ||
352 | case CS_EVENT_RESET_PHYSICAL: | ||
353 | snd_printdd(KERN_DEBUG "RESET_PHYSICAL\n"); | ||
354 | if (link->state & DEV_CONFIG) | ||
355 | pcmcia_release_configuration(link->handle); | ||
356 | break; | ||
357 | case CS_EVENT_PM_RESUME: | ||
358 | snd_printdd(KERN_DEBUG "RESUME\n"); | ||
359 | link->state &= ~DEV_SUSPEND; | ||
360 | /* Fall through... */ | ||
361 | case CS_EVENT_CARD_RESET: | ||
362 | snd_printdd(KERN_DEBUG "CARD_RESET\n"); | ||
363 | if (DEV_OK(link)) { | ||
364 | //struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; | ||
365 | snd_printdd(KERN_DEBUG "requestconfig...\n"); | ||
366 | pcmcia_request_configuration(link->handle, &link->conf); | ||
367 | if (chip && chip->card->pm_resume) { | ||
368 | snd_printdd(KERN_DEBUG "calling snd_vx_resume\n"); | ||
369 | chip->card->pm_resume(chip->card); | ||
370 | } | ||
371 | } | ||
372 | snd_printdd(KERN_DEBUG "resume done!\n"); | ||
373 | break; | ||
374 | #endif | ||
375 | } | ||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | /* | ||
380 | * exported stuffs | ||
381 | */ | ||
382 | EXPORT_SYMBOL(snd_vxpocket_ops); | ||
383 | EXPORT_SYMBOL(snd_vxpocket_attach); | ||
384 | EXPORT_SYMBOL(snd_vxpocket_detach); | ||
diff --git a/sound/pcmcia/vx/vxp440.c b/sound/pcmcia/vx/vxp440.c new file mode 100644 index 000000000000..59190a833001 --- /dev/null +++ b/sound/pcmcia/vx/vxp440.c | |||
@@ -0,0 +1,14 @@ | |||
1 | #define COMPILE_VXP440 | ||
2 | |||
3 | /* | ||
4 | add the following as /etc/pcmcia/vxp440.conf: | ||
5 | |||
6 | device "snd-vxp440" | ||
7 | class "audio" module "snd-vxp440" | ||
8 | |||
9 | card "Digigram VX-POCKET440" | ||
10 | manfid 0x01f1, 0x0100 | ||
11 | bind "snd-vxp440" | ||
12 | */ | ||
13 | |||
14 | #include "vxpocket.c" | ||
diff --git a/sound/pcmcia/vx/vxp_mixer.c b/sound/pcmcia/vx/vxp_mixer.c new file mode 100644 index 000000000000..aeaef3d81801 --- /dev/null +++ b/sound/pcmcia/vx/vxp_mixer.c | |||
@@ -0,0 +1,148 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VXpocket soundcards | ||
3 | * | ||
4 | * VX-pocket mixer | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/control.h> | ||
26 | #include "vxpocket.h" | ||
27 | |||
28 | #define MIC_LEVEL_MIN 0 | ||
29 | #define MIC_LEVEL_MAX 8 | ||
30 | |||
31 | /* | ||
32 | * mic level control (for VXPocket) | ||
33 | */ | ||
34 | static int vx_mic_level_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
35 | { | ||
36 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
37 | uinfo->count = 1; | ||
38 | uinfo->value.integer.min = 0; | ||
39 | uinfo->value.integer.max = MIC_LEVEL_MAX; | ||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static int vx_mic_level_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
44 | { | ||
45 | vx_core_t *_chip = snd_kcontrol_chip(kcontrol); | ||
46 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
47 | ucontrol->value.integer.value[0] = chip->mic_level; | ||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static int vx_mic_level_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
52 | { | ||
53 | vx_core_t *_chip = snd_kcontrol_chip(kcontrol); | ||
54 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
55 | down(&_chip->mixer_mutex); | ||
56 | if (chip->mic_level != ucontrol->value.integer.value[0]) { | ||
57 | vx_set_mic_level(_chip, ucontrol->value.integer.value[0]); | ||
58 | chip->mic_level = ucontrol->value.integer.value[0]; | ||
59 | up(&_chip->mixer_mutex); | ||
60 | return 1; | ||
61 | } | ||
62 | up(&_chip->mixer_mutex); | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static snd_kcontrol_new_t vx_control_mic_level = { | ||
67 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
68 | .name = "Mic Capture Volume", | ||
69 | .info = vx_mic_level_info, | ||
70 | .get = vx_mic_level_get, | ||
71 | .put = vx_mic_level_put, | ||
72 | }; | ||
73 | |||
74 | /* | ||
75 | * mic boost level control (for VXP440) | ||
76 | */ | ||
77 | static int vx_mic_boost_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
78 | { | ||
79 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
80 | uinfo->count = 1; | ||
81 | uinfo->value.integer.min = 0; | ||
82 | uinfo->value.integer.max = 1; | ||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | static int vx_mic_boost_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
87 | { | ||
88 | vx_core_t *_chip = snd_kcontrol_chip(kcontrol); | ||
89 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
90 | ucontrol->value.integer.value[0] = chip->mic_level; | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static int vx_mic_boost_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
95 | { | ||
96 | vx_core_t *_chip = snd_kcontrol_chip(kcontrol); | ||
97 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
98 | down(&_chip->mixer_mutex); | ||
99 | if (chip->mic_level != ucontrol->value.integer.value[0]) { | ||
100 | vx_set_mic_boost(_chip, ucontrol->value.integer.value[0]); | ||
101 | chip->mic_level = ucontrol->value.integer.value[0]; | ||
102 | up(&_chip->mixer_mutex); | ||
103 | return 1; | ||
104 | } | ||
105 | up(&_chip->mixer_mutex); | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static snd_kcontrol_new_t vx_control_mic_boost = { | ||
110 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
111 | .name = "Mic Boost", | ||
112 | .info = vx_mic_boost_info, | ||
113 | .get = vx_mic_boost_get, | ||
114 | .put = vx_mic_boost_put, | ||
115 | }; | ||
116 | |||
117 | |||
118 | int vxp_add_mic_controls(vx_core_t *_chip) | ||
119 | { | ||
120 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
121 | int err; | ||
122 | |||
123 | /* mute input levels */ | ||
124 | chip->mic_level = 0; | ||
125 | switch (_chip->type) { | ||
126 | case VX_TYPE_VXPOCKET: | ||
127 | vx_set_mic_level(_chip, 0); | ||
128 | break; | ||
129 | case VX_TYPE_VXP440: | ||
130 | vx_set_mic_boost(_chip, 0); | ||
131 | break; | ||
132 | } | ||
133 | |||
134 | /* mic level */ | ||
135 | switch (_chip->type) { | ||
136 | case VX_TYPE_VXPOCKET: | ||
137 | if ((err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_mic_level, chip))) < 0) | ||
138 | return err; | ||
139 | break; | ||
140 | case VX_TYPE_VXP440: | ||
141 | if ((err = snd_ctl_add(_chip->card, snd_ctl_new1(&vx_control_mic_boost, chip))) < 0) | ||
142 | return err; | ||
143 | break; | ||
144 | } | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c new file mode 100644 index 000000000000..ef6734271607 --- /dev/null +++ b/sound/pcmcia/vx/vxp_ops.c | |||
@@ -0,0 +1,614 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VXpocket soundcards | ||
3 | * | ||
4 | * lowlevel routines for VXpocket soundcards | ||
5 | * | ||
6 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <sound/driver.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/device.h> | ||
26 | #include <linux/firmware.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <asm/io.h> | ||
29 | #include "vxpocket.h" | ||
30 | |||
31 | |||
32 | static int vxp_reg_offset[VX_REG_MAX] = { | ||
33 | [VX_ICR] = 0x00, // ICR | ||
34 | [VX_CVR] = 0x01, // CVR | ||
35 | [VX_ISR] = 0x02, // ISR | ||
36 | [VX_IVR] = 0x03, // IVR | ||
37 | [VX_RXH] = 0x05, // RXH | ||
38 | [VX_RXM] = 0x06, // RXM | ||
39 | [VX_RXL] = 0x07, // RXL | ||
40 | [VX_DMA] = 0x04, // DMA | ||
41 | [VX_CDSP] = 0x08, // CDSP | ||
42 | [VX_LOFREQ] = 0x09, // LFREQ | ||
43 | [VX_HIFREQ] = 0x0a, // HFREQ | ||
44 | [VX_DATA] = 0x0b, // DATA | ||
45 | [VX_MICRO] = 0x0c, // MICRO | ||
46 | [VX_DIALOG] = 0x0d, // DIALOG | ||
47 | [VX_CSUER] = 0x0e, // CSUER | ||
48 | [VX_RUER] = 0x0f, // RUER | ||
49 | }; | ||
50 | |||
51 | |||
52 | inline static unsigned long vxp_reg_addr(vx_core_t *_chip, int reg) | ||
53 | { | ||
54 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
55 | return chip->port + vxp_reg_offset[reg]; | ||
56 | } | ||
57 | |||
58 | /* | ||
59 | * snd_vx_inb - read a byte from the register | ||
60 | * @offset: register offset | ||
61 | */ | ||
62 | static unsigned char vxp_inb(vx_core_t *chip, int offset) | ||
63 | { | ||
64 | return inb(vxp_reg_addr(chip, offset)); | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | * snd_vx_outb - write a byte on the register | ||
69 | * @offset: the register offset | ||
70 | * @val: the value to write | ||
71 | */ | ||
72 | static void vxp_outb(vx_core_t *chip, int offset, unsigned char val) | ||
73 | { | ||
74 | outb(val, vxp_reg_addr(chip, offset)); | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * redefine macros to call directly | ||
79 | */ | ||
80 | #undef vx_inb | ||
81 | #define vx_inb(chip,reg) vxp_inb((vx_core_t*)(chip), VX_##reg) | ||
82 | #undef vx_outb | ||
83 | #define vx_outb(chip,reg,val) vxp_outb((vx_core_t*)(chip), VX_##reg,val) | ||
84 | |||
85 | |||
86 | /* | ||
87 | * vx_check_magic - check the magic word on xilinx | ||
88 | * | ||
89 | * returns zero if a magic word is detected, or a negative error code. | ||
90 | */ | ||
91 | static int vx_check_magic(vx_core_t *chip) | ||
92 | { | ||
93 | unsigned long end_time = jiffies + HZ / 5; | ||
94 | int c; | ||
95 | do { | ||
96 | c = vx_inb(chip, CDSP); | ||
97 | if (c == CDSP_MAGIC) | ||
98 | return 0; | ||
99 | snd_vx_delay(chip, 10); | ||
100 | } while (time_after_eq(end_time, jiffies)); | ||
101 | snd_printk(KERN_ERR "cannot find xilinx magic word (%x)\n", c); | ||
102 | return -EIO; | ||
103 | } | ||
104 | |||
105 | |||
106 | /* | ||
107 | * vx_reset_dsp - reset the DSP | ||
108 | */ | ||
109 | |||
110 | #define XX_DSP_RESET_WAIT_TIME 2 /* ms */ | ||
111 | |||
112 | static void vxp_reset_dsp(vx_core_t *_chip) | ||
113 | { | ||
114 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
115 | |||
116 | /* set the reset dsp bit to 1 */ | ||
117 | vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_DSP_RESET_MASK); | ||
118 | vx_inb(chip, CDSP); | ||
119 | mdelay(XX_DSP_RESET_WAIT_TIME); | ||
120 | /* reset the bit */ | ||
121 | chip->regCDSP &= ~VXP_CDSP_DSP_RESET_MASK; | ||
122 | vx_outb(chip, CDSP, chip->regCDSP); | ||
123 | vx_inb(chip, CDSP); | ||
124 | mdelay(XX_DSP_RESET_WAIT_TIME); | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * reset codec bit | ||
129 | */ | ||
130 | static void vxp_reset_codec(vx_core_t *_chip) | ||
131 | { | ||
132 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
133 | |||
134 | /* Set the reset CODEC bit to 1. */ | ||
135 | vx_outb(chip, CDSP, chip->regCDSP | VXP_CDSP_CODEC_RESET_MASK); | ||
136 | vx_inb(chip, CDSP); | ||
137 | snd_vx_delay(_chip, 10); | ||
138 | /* Set the reset CODEC bit to 0. */ | ||
139 | chip->regCDSP &= ~VXP_CDSP_CODEC_RESET_MASK; | ||
140 | vx_outb(chip, CDSP, chip->regCDSP); | ||
141 | vx_inb(chip, CDSP); | ||
142 | snd_vx_delay(_chip, 1); | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * vx_load_xilinx_binary - load the xilinx binary image | ||
147 | * the binary image is the binary array converted from the bitstream file. | ||
148 | */ | ||
149 | static int vxp_load_xilinx_binary(vx_core_t *_chip, const struct firmware *fw) | ||
150 | { | ||
151 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
152 | unsigned int i; | ||
153 | int c; | ||
154 | int regCSUER, regRUER; | ||
155 | unsigned char *image; | ||
156 | unsigned char data; | ||
157 | |||
158 | /* Switch to programmation mode */ | ||
159 | chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK; | ||
160 | vx_outb(chip, DIALOG, chip->regDIALOG); | ||
161 | |||
162 | /* Save register CSUER and RUER */ | ||
163 | regCSUER = vx_inb(chip, CSUER); | ||
164 | regRUER = vx_inb(chip, RUER); | ||
165 | |||
166 | /* reset HF0 and HF1 */ | ||
167 | vx_outb(chip, ICR, 0); | ||
168 | |||
169 | /* Wait for answer HF2 equal to 1 */ | ||
170 | snd_printdd(KERN_DEBUG "check ISR_HF2\n"); | ||
171 | if (vx_check_isr(_chip, ISR_HF2, ISR_HF2, 20) < 0) | ||
172 | goto _error; | ||
173 | |||
174 | /* set HF1 for loading xilinx binary */ | ||
175 | vx_outb(chip, ICR, ICR_HF1); | ||
176 | image = fw->data; | ||
177 | for (i = 0; i < fw->size; i++, image++) { | ||
178 | data = *image; | ||
179 | if (vx_wait_isr_bit(_chip, ISR_TX_EMPTY) < 0) | ||
180 | goto _error; | ||
181 | vx_outb(chip, TXL, data); | ||
182 | /* wait for reading */ | ||
183 | if (vx_wait_for_rx_full(_chip) < 0) | ||
184 | goto _error; | ||
185 | c = vx_inb(chip, RXL); | ||
186 | if (c != (int)data) | ||
187 | snd_printk(KERN_ERR "vxpocket: load xilinx mismatch at %d: 0x%x != 0x%x\n", i, c, (int)data); | ||
188 | } | ||
189 | |||
190 | /* reset HF1 */ | ||
191 | vx_outb(chip, ICR, 0); | ||
192 | |||
193 | /* wait for HF3 */ | ||
194 | if (vx_check_isr(_chip, ISR_HF3, ISR_HF3, 20) < 0) | ||
195 | goto _error; | ||
196 | |||
197 | /* read the number of bytes received */ | ||
198 | if (vx_wait_for_rx_full(_chip) < 0) | ||
199 | goto _error; | ||
200 | |||
201 | c = (int)vx_inb(chip, RXH) << 16; | ||
202 | c |= (int)vx_inb(chip, RXM) << 8; | ||
203 | c |= vx_inb(chip, RXL); | ||
204 | |||
205 | snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%x\n", c, fw->size); | ||
206 | |||
207 | vx_outb(chip, ICR, ICR_HF0); | ||
208 | |||
209 | /* TEMPO 250ms : wait until Xilinx is downloaded */ | ||
210 | snd_vx_delay(_chip, 300); | ||
211 | |||
212 | /* test magical word */ | ||
213 | if (vx_check_magic(_chip) < 0) | ||
214 | goto _error; | ||
215 | |||
216 | /* Restore register 0x0E and 0x0F (thus replacing COR and FCSR) */ | ||
217 | vx_outb(chip, CSUER, regCSUER); | ||
218 | vx_outb(chip, RUER, regRUER); | ||
219 | |||
220 | /* Reset the Xilinx's signal enabling IO access */ | ||
221 | chip->regDIALOG |= VXP_DLG_XILINX_REPROG_MASK; | ||
222 | vx_outb(chip, DIALOG, chip->regDIALOG); | ||
223 | vx_inb(chip, DIALOG); | ||
224 | snd_vx_delay(_chip, 10); | ||
225 | chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK; | ||
226 | vx_outb(chip, DIALOG, chip->regDIALOG); | ||
227 | vx_inb(chip, DIALOG); | ||
228 | |||
229 | /* Reset of the Codec */ | ||
230 | vxp_reset_codec(_chip); | ||
231 | vx_reset_dsp(_chip); | ||
232 | |||
233 | return 0; | ||
234 | |||
235 | _error: | ||
236 | vx_outb(chip, CSUER, regCSUER); | ||
237 | vx_outb(chip, RUER, regRUER); | ||
238 | chip->regDIALOG &= ~VXP_DLG_XILINX_REPROG_MASK; | ||
239 | vx_outb(chip, DIALOG, chip->regDIALOG); | ||
240 | return -EIO; | ||
241 | } | ||
242 | |||
243 | |||
244 | /* | ||
245 | * vxp_load_dsp - load_dsp callback | ||
246 | */ | ||
247 | static int vxp_load_dsp(vx_core_t *vx, int index, const struct firmware *fw) | ||
248 | { | ||
249 | int err; | ||
250 | |||
251 | switch (index) { | ||
252 | case 0: | ||
253 | /* xilinx boot */ | ||
254 | if ((err = vx_check_magic(vx)) < 0) | ||
255 | return err; | ||
256 | if ((err = snd_vx_load_boot_image(vx, fw)) < 0) | ||
257 | return err; | ||
258 | return 0; | ||
259 | case 1: | ||
260 | /* xilinx image */ | ||
261 | return vxp_load_xilinx_binary(vx, fw); | ||
262 | case 2: | ||
263 | /* DSP boot */ | ||
264 | return snd_vx_dsp_boot(vx, fw); | ||
265 | case 3: | ||
266 | /* DSP image */ | ||
267 | return snd_vx_dsp_load(vx, fw); | ||
268 | default: | ||
269 | snd_BUG(); | ||
270 | return -EINVAL; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | |||
275 | /* | ||
276 | * vx_test_and_ack - test and acknowledge interrupt | ||
277 | * | ||
278 | * called from irq hander, too | ||
279 | * | ||
280 | * spinlock held! | ||
281 | */ | ||
282 | static int vxp_test_and_ack(vx_core_t *_chip) | ||
283 | { | ||
284 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
285 | |||
286 | /* not booted yet? */ | ||
287 | if (! (_chip->chip_status & VX_STAT_XILINX_LOADED)) | ||
288 | return -ENXIO; | ||
289 | |||
290 | if (! (vx_inb(chip, DIALOG) & VXP_DLG_MEMIRQ_MASK)) | ||
291 | return -EIO; | ||
292 | |||
293 | /* ok, interrupts generated, now ack it */ | ||
294 | /* set ACQUIT bit up and down */ | ||
295 | vx_outb(chip, DIALOG, chip->regDIALOG | VXP_DLG_ACK_MEMIRQ_MASK); | ||
296 | /* useless read just to spend some time and maintain | ||
297 | * the ACQUIT signal up for a while ( a bus cycle ) | ||
298 | */ | ||
299 | vx_inb(chip, DIALOG); | ||
300 | vx_outb(chip, DIALOG, chip->regDIALOG & ~VXP_DLG_ACK_MEMIRQ_MASK); | ||
301 | |||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | |||
306 | /* | ||
307 | * vx_validate_irq - enable/disable IRQ | ||
308 | */ | ||
309 | static void vxp_validate_irq(vx_core_t *_chip, int enable) | ||
310 | { | ||
311 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
312 | |||
313 | /* Set the interrupt enable bit to 1 in CDSP register */ | ||
314 | if (enable) | ||
315 | chip->regCDSP |= VXP_CDSP_VALID_IRQ_MASK; | ||
316 | else | ||
317 | chip->regCDSP &= ~VXP_CDSP_VALID_IRQ_MASK; | ||
318 | vx_outb(chip, CDSP, chip->regCDSP); | ||
319 | } | ||
320 | |||
321 | /* | ||
322 | * vx_setup_pseudo_dma - set up the pseudo dma read/write mode. | ||
323 | * @do_write: 0 = read, 1 = set up for DMA write | ||
324 | */ | ||
325 | static void vx_setup_pseudo_dma(vx_core_t *_chip, int do_write) | ||
326 | { | ||
327 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
328 | |||
329 | /* Interrupt mode and HREQ pin enabled for host transmit / receive data transfers */ | ||
330 | vx_outb(chip, ICR, do_write ? ICR_TREQ : ICR_RREQ); | ||
331 | /* Reset the pseudo-dma register */ | ||
332 | vx_inb(chip, ISR); | ||
333 | vx_outb(chip, ISR, 0); | ||
334 | |||
335 | /* Select DMA in read/write transfer mode and in 16-bit accesses */ | ||
336 | chip->regDIALOG |= VXP_DLG_DMA16_SEL_MASK; | ||
337 | chip->regDIALOG |= do_write ? VXP_DLG_DMAWRITE_SEL_MASK : VXP_DLG_DMAREAD_SEL_MASK; | ||
338 | vx_outb(chip, DIALOG, chip->regDIALOG); | ||
339 | |||
340 | } | ||
341 | |||
342 | /* | ||
343 | * vx_release_pseudo_dma - disable the pseudo-DMA mode | ||
344 | */ | ||
345 | static void vx_release_pseudo_dma(vx_core_t *_chip) | ||
346 | { | ||
347 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
348 | |||
349 | /* Disable DMA and 16-bit accesses */ | ||
350 | chip->regDIALOG &= ~(VXP_DLG_DMAWRITE_SEL_MASK| | ||
351 | VXP_DLG_DMAREAD_SEL_MASK| | ||
352 | VXP_DLG_DMA16_SEL_MASK); | ||
353 | vx_outb(chip, DIALOG, chip->regDIALOG); | ||
354 | /* HREQ pin disabled. */ | ||
355 | vx_outb(chip, ICR, 0); | ||
356 | } | ||
357 | |||
358 | /* | ||
359 | * vx_pseudo_dma_write - write bulk data on pseudo-DMA mode | ||
360 | * @count: data length to transfer in bytes | ||
361 | * | ||
362 | * data size must be aligned to 6 bytes to ensure the 24bit alignment on DSP. | ||
363 | * NB: call with a certain lock! | ||
364 | */ | ||
365 | static void vxp_dma_write(vx_core_t *chip, snd_pcm_runtime_t *runtime, | ||
366 | vx_pipe_t *pipe, int count) | ||
367 | { | ||
368 | long port = vxp_reg_addr(chip, VX_DMA); | ||
369 | int offset = pipe->hw_ptr; | ||
370 | unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); | ||
371 | |||
372 | vx_setup_pseudo_dma(chip, 1); | ||
373 | if (offset + count > pipe->buffer_bytes) { | ||
374 | int length = pipe->buffer_bytes - offset; | ||
375 | count -= length; | ||
376 | length >>= 1; /* in 16bit words */ | ||
377 | /* Transfer using pseudo-dma. */ | ||
378 | while (length-- > 0) { | ||
379 | outw(cpu_to_le16(*addr), port); | ||
380 | addr++; | ||
381 | } | ||
382 | addr = (unsigned short *)runtime->dma_area; | ||
383 | pipe->hw_ptr = 0; | ||
384 | } | ||
385 | pipe->hw_ptr += count; | ||
386 | count >>= 1; /* in 16bit words */ | ||
387 | /* Transfer using pseudo-dma. */ | ||
388 | while (count-- > 0) { | ||
389 | outw(cpu_to_le16(*addr), port); | ||
390 | addr++; | ||
391 | } | ||
392 | vx_release_pseudo_dma(chip); | ||
393 | } | ||
394 | |||
395 | |||
396 | /* | ||
397 | * vx_pseudo_dma_read - read bulk data on pseudo DMA mode | ||
398 | * @offset: buffer offset in bytes | ||
399 | * @count: data length to transfer in bytes | ||
400 | * | ||
401 | * the read length must be aligned to 6 bytes, as well as write. | ||
402 | * NB: call with a certain lock! | ||
403 | */ | ||
404 | static void vxp_dma_read(vx_core_t *chip, snd_pcm_runtime_t *runtime, | ||
405 | vx_pipe_t *pipe, int count) | ||
406 | { | ||
407 | struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; | ||
408 | long port = vxp_reg_addr(chip, VX_DMA); | ||
409 | int offset = pipe->hw_ptr; | ||
410 | unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); | ||
411 | |||
412 | snd_assert(count % 2 == 0, return); | ||
413 | vx_setup_pseudo_dma(chip, 0); | ||
414 | if (offset + count > pipe->buffer_bytes) { | ||
415 | int length = pipe->buffer_bytes - offset; | ||
416 | count -= length; | ||
417 | length >>= 1; /* in 16bit words */ | ||
418 | /* Transfer using pseudo-dma. */ | ||
419 | while (length-- > 0) | ||
420 | *addr++ = le16_to_cpu(inw(port)); | ||
421 | addr = (unsigned short *)runtime->dma_area; | ||
422 | pipe->hw_ptr = 0; | ||
423 | } | ||
424 | pipe->hw_ptr += count; | ||
425 | count >>= 1; /* in 16bit words */ | ||
426 | /* Transfer using pseudo-dma. */ | ||
427 | while (count-- > 1) | ||
428 | *addr++ = le16_to_cpu(inw(port)); | ||
429 | /* Disable DMA */ | ||
430 | pchip->regDIALOG &= ~VXP_DLG_DMAREAD_SEL_MASK; | ||
431 | vx_outb(chip, DIALOG, pchip->regDIALOG); | ||
432 | /* Read the last word (16 bits) */ | ||
433 | *addr = le16_to_cpu(inw(port)); | ||
434 | /* Disable 16-bit accesses */ | ||
435 | pchip->regDIALOG &= ~VXP_DLG_DMA16_SEL_MASK; | ||
436 | vx_outb(chip, DIALOG, pchip->regDIALOG); | ||
437 | /* HREQ pin disabled. */ | ||
438 | vx_outb(chip, ICR, 0); | ||
439 | } | ||
440 | |||
441 | |||
442 | /* | ||
443 | * write a codec data (24bit) | ||
444 | */ | ||
445 | static void vxp_write_codec_reg(vx_core_t *chip, int codec, unsigned int data) | ||
446 | { | ||
447 | int i; | ||
448 | |||
449 | /* Activate access to the corresponding codec register */ | ||
450 | if (! codec) | ||
451 | vx_inb(chip, LOFREQ); | ||
452 | else | ||
453 | vx_inb(chip, CODEC2); | ||
454 | |||
455 | /* We have to send 24 bits (3 x 8 bits). Start with most signif. Bit */ | ||
456 | for (i = 0; i < 24; i++, data <<= 1) | ||
457 | vx_outb(chip, DATA, ((data & 0x800000) ? VX_DATA_CODEC_MASK : 0)); | ||
458 | |||
459 | /* Terminate access to codec registers */ | ||
460 | vx_inb(chip, HIFREQ); | ||
461 | } | ||
462 | |||
463 | |||
464 | /* | ||
465 | * vx_set_mic_boost - set mic boost level (on vxp440 only) | ||
466 | * @boost: 0 = 20dB, 1 = +38dB | ||
467 | */ | ||
468 | void vx_set_mic_boost(vx_core_t *chip, int boost) | ||
469 | { | ||
470 | struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; | ||
471 | unsigned long flags; | ||
472 | |||
473 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
474 | return; | ||
475 | |||
476 | spin_lock_irqsave(&chip->lock, flags); | ||
477 | if (pchip->regCDSP & P24_CDSP_MICS_SEL_MASK) { | ||
478 | if (boost) { | ||
479 | /* boost: 38 dB */ | ||
480 | pchip->regCDSP &= ~P24_CDSP_MIC20_SEL_MASK; | ||
481 | pchip->regCDSP |= P24_CDSP_MIC38_SEL_MASK; | ||
482 | } else { | ||
483 | /* minimum value: 20 dB */ | ||
484 | pchip->regCDSP |= P24_CDSP_MIC20_SEL_MASK; | ||
485 | pchip->regCDSP &= ~P24_CDSP_MIC38_SEL_MASK; | ||
486 | } | ||
487 | vx_outb(chip, CDSP, pchip->regCDSP); | ||
488 | } | ||
489 | spin_unlock_irqrestore(&chip->lock, flags); | ||
490 | } | ||
491 | |||
492 | /* | ||
493 | * remap the linear value (0-8) to the actual value (0-15) | ||
494 | */ | ||
495 | static int vx_compute_mic_level(int level) | ||
496 | { | ||
497 | switch (level) { | ||
498 | case 5: level = 6 ; break; | ||
499 | case 6: level = 8 ; break; | ||
500 | case 7: level = 11; break; | ||
501 | case 8: level = 15; break; | ||
502 | default: break ; | ||
503 | } | ||
504 | return level; | ||
505 | } | ||
506 | |||
507 | /* | ||
508 | * vx_set_mic_level - set mic level (on vxpocket only) | ||
509 | * @level: the mic level = 0 - 8 (max) | ||
510 | */ | ||
511 | void vx_set_mic_level(vx_core_t *chip, int level) | ||
512 | { | ||
513 | struct snd_vxpocket *pchip = (struct snd_vxpocket *)chip; | ||
514 | unsigned long flags; | ||
515 | |||
516 | if (chip->chip_status & VX_STAT_IS_STALE) | ||
517 | return; | ||
518 | |||
519 | spin_lock_irqsave(&chip->lock, flags); | ||
520 | if (pchip->regCDSP & VXP_CDSP_MIC_SEL_MASK) { | ||
521 | level = vx_compute_mic_level(level); | ||
522 | vx_outb(chip, MICRO, level); | ||
523 | } | ||
524 | spin_unlock_irqrestore(&chip->lock, flags); | ||
525 | } | ||
526 | |||
527 | |||
528 | /* | ||
529 | * change the input audio source | ||
530 | */ | ||
531 | static void vxp_change_audio_source(vx_core_t *_chip, int src) | ||
532 | { | ||
533 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
534 | |||
535 | switch (src) { | ||
536 | case VX_AUDIO_SRC_DIGITAL: | ||
537 | chip->regCDSP |= VXP_CDSP_DATAIN_SEL_MASK; | ||
538 | vx_outb(chip, CDSP, chip->regCDSP); | ||
539 | break; | ||
540 | case VX_AUDIO_SRC_LINE: | ||
541 | chip->regCDSP &= ~VXP_CDSP_DATAIN_SEL_MASK; | ||
542 | if (_chip->type == VX_TYPE_VXP440) | ||
543 | chip->regCDSP &= ~P24_CDSP_MICS_SEL_MASK; | ||
544 | else | ||
545 | chip->regCDSP &= ~VXP_CDSP_MIC_SEL_MASK; | ||
546 | vx_outb(chip, CDSP, chip->regCDSP); | ||
547 | break; | ||
548 | case VX_AUDIO_SRC_MIC: | ||
549 | chip->regCDSP &= ~VXP_CDSP_DATAIN_SEL_MASK; | ||
550 | /* reset mic levels */ | ||
551 | if (_chip->type == VX_TYPE_VXP440) { | ||
552 | chip->regCDSP &= ~P24_CDSP_MICS_SEL_MASK; | ||
553 | if (chip->mic_level) | ||
554 | chip->regCDSP |= P24_CDSP_MIC38_SEL_MASK; | ||
555 | else | ||
556 | chip->regCDSP |= P24_CDSP_MIC20_SEL_MASK; | ||
557 | vx_outb(chip, CDSP, chip->regCDSP); | ||
558 | } else { | ||
559 | chip->regCDSP |= VXP_CDSP_MIC_SEL_MASK; | ||
560 | vx_outb(chip, CDSP, chip->regCDSP); | ||
561 | vx_outb(chip, MICRO, vx_compute_mic_level(chip->mic_level)); | ||
562 | } | ||
563 | break; | ||
564 | } | ||
565 | } | ||
566 | |||
567 | /* | ||
568 | * change the clock source | ||
569 | * source = INTERNAL_QUARTZ or UER_SYNC | ||
570 | */ | ||
571 | static void vxp_set_clock_source(vx_core_t *_chip, int source) | ||
572 | { | ||
573 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
574 | |||
575 | if (source == INTERNAL_QUARTZ) | ||
576 | chip->regCDSP &= ~VXP_CDSP_CLOCKIN_SEL_MASK; | ||
577 | else | ||
578 | chip->regCDSP |= VXP_CDSP_CLOCKIN_SEL_MASK; | ||
579 | vx_outb(chip, CDSP, chip->regCDSP); | ||
580 | } | ||
581 | |||
582 | |||
583 | /* | ||
584 | * reset the board | ||
585 | */ | ||
586 | static void vxp_reset_board(vx_core_t *_chip, int cold_reset) | ||
587 | { | ||
588 | struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; | ||
589 | |||
590 | chip->regCDSP = 0; | ||
591 | chip->regDIALOG = 0; | ||
592 | } | ||
593 | |||
594 | |||
595 | /* | ||
596 | * callbacks | ||
597 | */ | ||
598 | /* exported */ | ||
599 | struct snd_vx_ops snd_vxpocket_ops = { | ||
600 | .in8 = vxp_inb, | ||
601 | .out8 = vxp_outb, | ||
602 | .test_and_ack = vxp_test_and_ack, | ||
603 | .validate_irq = vxp_validate_irq, | ||
604 | .write_codec = vxp_write_codec_reg, | ||
605 | .reset_codec = vxp_reset_codec, | ||
606 | .change_audio_source = vxp_change_audio_source, | ||
607 | .set_clock_source = vxp_set_clock_source, | ||
608 | .load_dsp = vxp_load_dsp, | ||
609 | .add_controls = vxp_add_mic_controls, | ||
610 | .reset_dsp = vxp_reset_dsp, | ||
611 | .reset_board = vxp_reset_board, | ||
612 | .dma_write = vxp_dma_write, | ||
613 | .dma_read = vxp_dma_read, | ||
614 | }; | ||
diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c new file mode 100644 index 000000000000..fce2ad04fd8b --- /dev/null +++ b/sound/pcmcia/vx/vxpocket.c | |||
@@ -0,0 +1,164 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VXpocket V2/440 soundcards | ||
3 | * | ||
4 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | /* | ||
22 | please add the following as /etc/pcmcia/vxpocket.conf: | ||
23 | |||
24 | device "snd-vxpocket" | ||
25 | class "audio" module "snd-vxpocket" | ||
26 | |||
27 | card "Digigram VX-POCKET" | ||
28 | manfid 0x01f1, 0x0100 | ||
29 | bind "snd-vxpocket" | ||
30 | |||
31 | */ | ||
32 | |||
33 | #include <sound/driver.h> | ||
34 | #include <linux/init.h> | ||
35 | #include <linux/moduleparam.h> | ||
36 | #include <sound/core.h> | ||
37 | #include <pcmcia/version.h> | ||
38 | #include "vxpocket.h" | ||
39 | #include <sound/initval.h> | ||
40 | |||
41 | /* | ||
42 | */ | ||
43 | |||
44 | #ifdef COMPILE_VXP440 | ||
45 | #define CARD_NAME "VXPocket440" | ||
46 | #else | ||
47 | #define CARD_NAME "VXPocket" | ||
48 | #endif | ||
49 | |||
50 | MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); | ||
51 | MODULE_DESCRIPTION("Digigram " CARD_NAME); | ||
52 | MODULE_LICENSE("GPL"); | ||
53 | MODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}"); | ||
54 | |||
55 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
56 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
57 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ | ||
58 | static int ibl[SNDRV_CARDS]; | ||
59 | |||
60 | module_param_array(index, int, NULL, 0444); | ||
61 | MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); | ||
62 | module_param_array(id, charp, NULL, 0444); | ||
63 | MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); | ||
64 | module_param_array(enable, bool, NULL, 0444); | ||
65 | MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); | ||
66 | module_param_array(ibl, int, NULL, 0444); | ||
67 | MODULE_PARM_DESC(ibl, "Capture IBL size for " CARD_NAME " soundcard."); | ||
68 | |||
69 | |||
70 | /* | ||
71 | */ | ||
72 | |||
73 | #ifdef COMPILE_VXP440 | ||
74 | |||
75 | /* 1 DSP, 1 sync UER, 1 sync World Clock (NIY) */ | ||
76 | /* SMPTE (NIY) */ | ||
77 | /* 2 stereo analog input (line/micro) */ | ||
78 | /* 2 stereo analog output */ | ||
79 | /* Only output levels can be modified */ | ||
80 | /* UER, but only for the first two inputs and outputs. */ | ||
81 | |||
82 | #define NUM_CODECS 2 | ||
83 | #define CARD_TYPE VX_TYPE_VXP440 | ||
84 | #define DEV_INFO "snd-vxp440" | ||
85 | |||
86 | #else | ||
87 | |||
88 | /* 1 DSP, 1 sync UER */ | ||
89 | /* 1 programmable clock (NIY) */ | ||
90 | /* 1 stereo analog input (line/micro) */ | ||
91 | /* 1 stereo analog output */ | ||
92 | /* Only output levels can be modified */ | ||
93 | |||
94 | #define NUM_CODECS 1 | ||
95 | #define CARD_TYPE VX_TYPE_VXPOCKET | ||
96 | #define DEV_INFO "snd-vxpocket" | ||
97 | |||
98 | #endif | ||
99 | |||
100 | static dev_info_t dev_info = DEV_INFO; | ||
101 | |||
102 | static struct snd_vx_hardware vxp_hw = { | ||
103 | .name = CARD_NAME, | ||
104 | .type = CARD_TYPE, | ||
105 | |||
106 | /* hardware specs */ | ||
107 | .num_codecs = NUM_CODECS, | ||
108 | .num_ins = NUM_CODECS, | ||
109 | .num_outs = NUM_CODECS, | ||
110 | .output_level_max = VX_ANALOG_OUT_LEVEL_MAX, | ||
111 | }; | ||
112 | |||
113 | static struct snd_vxp_entry hw_entry = { | ||
114 | .dev_info = &dev_info, | ||
115 | |||
116 | /* module parameters */ | ||
117 | .index_table = index, | ||
118 | .id_table = id, | ||
119 | .enable_table = enable, | ||
120 | .ibl = ibl, | ||
121 | |||
122 | /* h/w config */ | ||
123 | .hardware = &vxp_hw, | ||
124 | .ops = &snd_vxpocket_ops, | ||
125 | }; | ||
126 | |||
127 | /* | ||
128 | */ | ||
129 | static dev_link_t *vxp_attach(void) | ||
130 | { | ||
131 | return snd_vxpocket_attach(&hw_entry); | ||
132 | } | ||
133 | |||
134 | static void vxp_detach(dev_link_t *link) | ||
135 | { | ||
136 | snd_vxpocket_detach(&hw_entry, link); | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * Module entry points | ||
141 | */ | ||
142 | |||
143 | static struct pcmcia_driver vxp_cs_driver = { | ||
144 | .owner = THIS_MODULE, | ||
145 | .drv = { | ||
146 | .name = DEV_INFO, | ||
147 | }, | ||
148 | .attach = vxp_attach, | ||
149 | .detach = vxp_detach | ||
150 | }; | ||
151 | |||
152 | static int __init init_vxpocket(void) | ||
153 | { | ||
154 | return pcmcia_register_driver(&vxp_cs_driver); | ||
155 | } | ||
156 | |||
157 | static void __exit exit_vxpocket(void) | ||
158 | { | ||
159 | pcmcia_unregister_driver(&vxp_cs_driver); | ||
160 | BUG_ON(hw_entry.dev_list != NULL); | ||
161 | } | ||
162 | |||
163 | module_init(init_vxpocket); | ||
164 | module_exit(exit_vxpocket); | ||
diff --git a/sound/pcmcia/vx/vxpocket.h b/sound/pcmcia/vx/vxpocket.h new file mode 100644 index 000000000000..4462c04a4e8f --- /dev/null +++ b/sound/pcmcia/vx/vxpocket.h | |||
@@ -0,0 +1,118 @@ | |||
1 | /* | ||
2 | * Driver for Digigram VXpocket soundcards | ||
3 | * | ||
4 | * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #ifndef __VXPOCKET_H | ||
22 | #define __VXPOCKET_H | ||
23 | |||
24 | #include <sound/vx_core.h> | ||
25 | |||
26 | #include <pcmcia/cs_types.h> | ||
27 | #include <pcmcia/cs.h> | ||
28 | #include <pcmcia/cistpl.h> | ||
29 | #include <pcmcia/ds.h> | ||
30 | |||
31 | struct snd_vxp_entry { | ||
32 | dev_info_t *dev_info; | ||
33 | |||
34 | /* module parameters */ | ||
35 | int *index_table; | ||
36 | char **id_table; | ||
37 | int *enable_table; | ||
38 | int *ibl; | ||
39 | |||
40 | /* h/w config */ | ||
41 | struct snd_vx_hardware *hardware; | ||
42 | struct snd_vx_ops *ops; | ||
43 | |||
44 | /* slots */ | ||
45 | vx_core_t *card_list[SNDRV_CARDS]; | ||
46 | dev_link_t *dev_list; /* Linked list of devices */ | ||
47 | }; | ||
48 | |||
49 | struct snd_vxpocket { | ||
50 | |||
51 | vx_core_t core; | ||
52 | |||
53 | unsigned long port; | ||
54 | |||
55 | int mic_level; /* analog mic level (or boost) */ | ||
56 | |||
57 | unsigned int regCDSP; /* current CDSP register */ | ||
58 | unsigned int regDIALOG; /* current DIALOG register */ | ||
59 | |||
60 | int index; | ||
61 | struct snd_vxp_entry *hw_entry; | ||
62 | |||
63 | /* pcmcia stuff */ | ||
64 | dev_link_t link; | ||
65 | dev_node_t node; | ||
66 | }; | ||
67 | |||
68 | extern struct snd_vx_ops snd_vxpocket_ops; | ||
69 | |||
70 | void vx_set_mic_boost(vx_core_t *chip, int boost); | ||
71 | void vx_set_mic_level(vx_core_t *chip, int level); | ||
72 | |||
73 | /* | ||
74 | * pcmcia stuff | ||
75 | */ | ||
76 | dev_link_t *snd_vxpocket_attach(struct snd_vxp_entry *hw); | ||
77 | void snd_vxpocket_detach(struct snd_vxp_entry *hw, dev_link_t *link); | ||
78 | |||
79 | int vxp_add_mic_controls(vx_core_t *chip); | ||
80 | |||
81 | /* Constants used to access the CDSP register (0x08). */ | ||
82 | #define CDSP_MAGIC 0xA7 /* magic value (for read) */ | ||
83 | /* for write */ | ||
84 | #define VXP_CDSP_CLOCKIN_SEL_MASK 0x80 /* 0 (internal), 1 (AES/EBU) */ | ||
85 | #define VXP_CDSP_DATAIN_SEL_MASK 0x40 /* 0 (analog), 1 (UER) */ | ||
86 | #define VXP_CDSP_SMPTE_SEL_MASK 0x20 | ||
87 | #define VXP_CDSP_RESERVED_MASK 0x10 | ||
88 | #define VXP_CDSP_MIC_SEL_MASK 0x08 | ||
89 | #define VXP_CDSP_VALID_IRQ_MASK 0x04 | ||
90 | #define VXP_CDSP_CODEC_RESET_MASK 0x02 | ||
91 | #define VXP_CDSP_DSP_RESET_MASK 0x01 | ||
92 | /* VXPOCKET 240/440 */ | ||
93 | #define P24_CDSP_MICS_SEL_MASK 0x18 | ||
94 | #define P24_CDSP_MIC20_SEL_MASK 0x10 | ||
95 | #define P24_CDSP_MIC38_SEL_MASK 0x08 | ||
96 | |||
97 | /* Constants used to access the MEMIRQ register (0x0C). */ | ||
98 | #define P44_MEMIRQ_MASTER_SLAVE_SEL_MASK 0x08 | ||
99 | #define P44_MEMIRQ_SYNCED_ALONE_SEL_MASK 0x04 | ||
100 | #define P44_MEMIRQ_WCLK_OUT_IN_SEL_MASK 0x02 /* Not used */ | ||
101 | #define P44_MEMIRQ_WCLK_UER_SEL_MASK 0x01 /* Not used */ | ||
102 | |||
103 | /* Micro levels (0x0C) */ | ||
104 | |||
105 | /* Constants used to access the DIALOG register (0x0D). */ | ||
106 | #define VXP_DLG_XILINX_REPROG_MASK 0x80 /* W */ | ||
107 | #define VXP_DLG_DATA_XICOR_MASK 0x80 /* R */ | ||
108 | #define VXP_DLG_RESERVED4_0_MASK 0x40 | ||
109 | #define VXP_DLG_RESERVED2_0_MASK 0x20 | ||
110 | #define VXP_DLG_RESERVED1_0_MASK 0x10 | ||
111 | #define VXP_DLG_DMAWRITE_SEL_MASK 0x08 /* W */ | ||
112 | #define VXP_DLG_DMAREAD_SEL_MASK 0x04 /* W */ | ||
113 | #define VXP_DLG_MEMIRQ_MASK 0x02 /* R */ | ||
114 | #define VXP_DLG_DMA16_SEL_MASK 0x02 /* W */ | ||
115 | #define VXP_DLG_ACK_MEMIRQ_MASK 0x01 /* R/W */ | ||
116 | |||
117 | |||
118 | #endif /* __VXPOCKET_H */ | ||