diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/pcmcia/pdaudiocf |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/pcmcia/pdaudiocf')
-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 |
6 files changed, 1534 insertions, 0 deletions
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 | } | ||