diff options
Diffstat (limited to 'sound/pcmcia/vx')
-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 |
7 files changed, 1453 insertions, 0 deletions
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 */ | ||