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/pci/mixart/mixart.c |
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/pci/mixart/mixart.c')
-rw-r--r-- | sound/pci/mixart/mixart.c | 1443 |
1 files changed, 1443 insertions, 0 deletions
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c new file mode 100644 index 000000000000..65bb0f47af2c --- /dev/null +++ b/sound/pci/mixart/mixart.c | |||
@@ -0,0 +1,1443 @@ | |||
1 | /* | ||
2 | * Driver for Digigram miXart soundcards | ||
3 | * | ||
4 | * main file with alsa callbacks | ||
5 | * | ||
6 | * Copyright (c) 2003 by Digigram <alsa@digigram.com> | ||
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 | |||
24 | #include <sound/driver.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/pci.h> | ||
28 | #include <linux/moduleparam.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/initval.h> | ||
31 | #include <sound/info.h> | ||
32 | #include <sound/control.h> | ||
33 | #include <sound/pcm.h> | ||
34 | #include <sound/pcm_params.h> | ||
35 | #include "mixart.h" | ||
36 | #include "mixart_hwdep.h" | ||
37 | #include "mixart_core.h" | ||
38 | #include "mixart_mixer.h" | ||
39 | |||
40 | #define CARD_NAME "miXart" | ||
41 | |||
42 | MODULE_AUTHOR("Digigram <alsa@digigram.com>"); | ||
43 | MODULE_DESCRIPTION("Digigram " CARD_NAME); | ||
44 | MODULE_LICENSE("GPL"); | ||
45 | MODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}"); | ||
46 | |||
47 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
48 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
49 | static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ | ||
50 | |||
51 | module_param_array(index, int, NULL, 0444); | ||
52 | MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard."); | ||
53 | module_param_array(id, charp, NULL, 0444); | ||
54 | MODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard."); | ||
55 | module_param_array(enable, bool, NULL, 0444); | ||
56 | MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard."); | ||
57 | |||
58 | /* | ||
59 | */ | ||
60 | |||
61 | static struct pci_device_id snd_mixart_ids[] = { | ||
62 | { 0x1057, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* MC8240 */ | ||
63 | { 0, } | ||
64 | }; | ||
65 | |||
66 | MODULE_DEVICE_TABLE(pci, snd_mixart_ids); | ||
67 | |||
68 | |||
69 | static int mixart_set_pipe_state(mixart_mgr_t *mgr, mixart_pipe_t* pipe, int start) | ||
70 | { | ||
71 | mixart_group_state_req_t group_state; | ||
72 | mixart_group_state_resp_t group_state_resp; | ||
73 | mixart_msg_t request; | ||
74 | int err; | ||
75 | u32 system_msg_uid; | ||
76 | |||
77 | switch(pipe->status) { | ||
78 | case PIPE_RUNNING: | ||
79 | case PIPE_CLOCK_SET: | ||
80 | if(start) return 0; /* already started */ | ||
81 | break; | ||
82 | case PIPE_STOPPED: | ||
83 | if(!start) return 0; /* already stopped */ | ||
84 | break; | ||
85 | default: | ||
86 | snd_printk(KERN_ERR "error mixart_set_pipe_state called with wrong pipe->status!\n"); | ||
87 | return -EINVAL; /* function called with wrong pipe status */ | ||
88 | } | ||
89 | |||
90 | system_msg_uid = 0x12345678; /* the event ! (take care: the MSB and two LSB's have to be 0) */ | ||
91 | |||
92 | /* wait on the last MSG_SYSTEM_SEND_SYNCHRO_CMD command to be really finished */ | ||
93 | |||
94 | request.message_id = MSG_SYSTEM_WAIT_SYNCHRO_CMD; | ||
95 | request.uid = (mixart_uid_t){0,0}; | ||
96 | request.data = &system_msg_uid; | ||
97 | request.size = sizeof(system_msg_uid); | ||
98 | |||
99 | err = snd_mixart_send_msg_wait_notif(mgr, &request, system_msg_uid); | ||
100 | if(err) { | ||
101 | snd_printk(KERN_ERR "error : MSG_SYSTEM_WAIT_SYNCHRO_CMD was not notified !\n"); | ||
102 | return err; | ||
103 | } | ||
104 | |||
105 | /* start or stop the pipe (1 pipe) */ | ||
106 | |||
107 | memset(&group_state, 0, sizeof(group_state)); | ||
108 | group_state.pipe_count = 1; | ||
109 | group_state.pipe_uid[0] = pipe->group_uid; | ||
110 | |||
111 | if(start) | ||
112 | request.message_id = MSG_STREAM_START_STREAM_GRP_PACKET; | ||
113 | else | ||
114 | request.message_id = MSG_STREAM_STOP_STREAM_GRP_PACKET; | ||
115 | |||
116 | request.uid = pipe->group_uid; /*(mixart_uid_t){0,0};*/ | ||
117 | request.data = &group_state; | ||
118 | request.size = sizeof(group_state); | ||
119 | |||
120 | err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp); | ||
121 | if (err < 0 || group_state_resp.txx_status != 0) { | ||
122 | snd_printk(KERN_ERR "error MSG_STREAM_ST***_STREAM_GRP_PACKET err=%x stat=%x !\n", err, group_state_resp.txx_status); | ||
123 | return -EINVAL; | ||
124 | } | ||
125 | |||
126 | if(start) { | ||
127 | u32 stat; | ||
128 | |||
129 | group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */ | ||
130 | |||
131 | err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp); | ||
132 | if (err < 0 || group_state_resp.txx_status != 0) { | ||
133 | snd_printk(KERN_ERR "error MSG_STREAM_START_STREAM_GRP_PACKET err=%x stat=%x !\n", err, group_state_resp.txx_status); | ||
134 | return -EINVAL; | ||
135 | } | ||
136 | |||
137 | /* in case of start send a synchro top */ | ||
138 | |||
139 | request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD; | ||
140 | request.uid = (mixart_uid_t){0,0}; | ||
141 | request.data = NULL; | ||
142 | request.size = 0; | ||
143 | |||
144 | err = snd_mixart_send_msg(mgr, &request, sizeof(stat), &stat); | ||
145 | if (err < 0 || stat != 0) { | ||
146 | snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD err=%x stat=%x !\n", err, stat); | ||
147 | return -EINVAL; | ||
148 | } | ||
149 | |||
150 | pipe->status = PIPE_RUNNING; | ||
151 | } | ||
152 | else /* !start */ | ||
153 | pipe->status = PIPE_STOPPED; | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | |||
159 | static int mixart_set_clock(mixart_mgr_t *mgr, mixart_pipe_t *pipe, unsigned int rate) | ||
160 | { | ||
161 | mixart_msg_t request; | ||
162 | mixart_clock_properties_t clock_properties; | ||
163 | mixart_clock_properties_resp_t clock_prop_resp; | ||
164 | int err; | ||
165 | |||
166 | switch(pipe->status) { | ||
167 | case PIPE_CLOCK_SET: | ||
168 | break; | ||
169 | case PIPE_RUNNING: | ||
170 | if(rate != 0) | ||
171 | break; | ||
172 | default: | ||
173 | if(rate == 0) | ||
174 | return 0; /* nothing to do */ | ||
175 | else { | ||
176 | snd_printk(KERN_ERR "error mixart_set_clock(%d) called with wrong pipe->status !\n", rate); | ||
177 | return -EINVAL; | ||
178 | } | ||
179 | } | ||
180 | |||
181 | memset(&clock_properties, 0, sizeof(clock_properties)); | ||
182 | clock_properties.clock_generic_type = (rate != 0) ? CGT_INTERNAL_CLOCK : CGT_NO_CLOCK; | ||
183 | clock_properties.clock_mode = CM_STANDALONE; | ||
184 | clock_properties.frequency = rate; | ||
185 | clock_properties.nb_callers = 1; /* only one entry in uid_caller ! */ | ||
186 | clock_properties.uid_caller[0] = pipe->group_uid; | ||
187 | |||
188 | snd_printdd("mixart_set_clock to %d kHz\n", rate); | ||
189 | |||
190 | request.message_id = MSG_CLOCK_SET_PROPERTIES; | ||
191 | request.uid = mgr->uid_console_manager; | ||
192 | request.data = &clock_properties; | ||
193 | request.size = sizeof(clock_properties); | ||
194 | |||
195 | err = snd_mixart_send_msg(mgr, &request, sizeof(clock_prop_resp), &clock_prop_resp); | ||
196 | if (err < 0 || clock_prop_resp.status != 0 || clock_prop_resp.clock_mode != CM_STANDALONE) { | ||
197 | snd_printk(KERN_ERR "error MSG_CLOCK_SET_PROPERTIES err=%x stat=%x mod=%x !\n", err, clock_prop_resp.status, clock_prop_resp.clock_mode); | ||
198 | return -EINVAL; | ||
199 | } | ||
200 | |||
201 | if(rate) pipe->status = PIPE_CLOCK_SET; | ||
202 | else pipe->status = PIPE_RUNNING; | ||
203 | |||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | |||
208 | /* | ||
209 | * Allocate or reference output pipe for analog IOs (pcmp0/1) | ||
210 | */ | ||
211 | mixart_pipe_t* snd_mixart_add_ref_pipe( mixart_t *chip, int pcm_number, int capture, int monitoring) | ||
212 | { | ||
213 | int stream_count; | ||
214 | mixart_pipe_t *pipe; | ||
215 | mixart_msg_t request; | ||
216 | |||
217 | if(capture) { | ||
218 | if (pcm_number == MIXART_PCM_ANALOG) { | ||
219 | pipe = &(chip->pipe_in_ana); /* analog inputs */ | ||
220 | } else { | ||
221 | pipe = &(chip->pipe_in_dig); /* digital inputs */ | ||
222 | } | ||
223 | request.message_id = MSG_STREAM_ADD_OUTPUT_GROUP; | ||
224 | stream_count = MIXART_CAPTURE_STREAMS; | ||
225 | } else { | ||
226 | if (pcm_number == MIXART_PCM_ANALOG) { | ||
227 | pipe = &(chip->pipe_out_ana); /* analog outputs */ | ||
228 | } else { | ||
229 | pipe = &(chip->pipe_out_dig); /* digital outputs */ | ||
230 | } | ||
231 | request.message_id = MSG_STREAM_ADD_INPUT_GROUP; | ||
232 | stream_count = MIXART_PLAYBACK_STREAMS; | ||
233 | } | ||
234 | |||
235 | /* a new stream is opened and there are already all streams in use */ | ||
236 | if( (monitoring == 0) && (pipe->references >= stream_count) ) { | ||
237 | return NULL; | ||
238 | } | ||
239 | |||
240 | /* pipe is not yet defined */ | ||
241 | if( pipe->status == PIPE_UNDEFINED ) { | ||
242 | int err, i; | ||
243 | struct { | ||
244 | mixart_streaming_group_req_t sgroup_req; | ||
245 | mixart_streaming_group_t sgroup_resp; | ||
246 | } *buf; | ||
247 | |||
248 | snd_printdd("add_ref_pipe audio chip(%d) pcm(%d)\n", chip->chip_idx, pcm_number); | ||
249 | |||
250 | buf = kmalloc(sizeof(*buf), GFP_KERNEL); | ||
251 | if (!buf) | ||
252 | return NULL; | ||
253 | |||
254 | request.uid = (mixart_uid_t){0,0}; /* should be StreamManagerUID, but zero is OK if there is only one ! */ | ||
255 | request.data = &buf->sgroup_req; | ||
256 | request.size = sizeof(buf->sgroup_req); | ||
257 | |||
258 | memset(&buf->sgroup_req, 0, sizeof(buf->sgroup_req)); | ||
259 | |||
260 | buf->sgroup_req.stream_count = stream_count; | ||
261 | buf->sgroup_req.channel_count = 2; | ||
262 | buf->sgroup_req.latency = 256; | ||
263 | buf->sgroup_req.connector = pipe->uid_left_connector; /* the left connector */ | ||
264 | |||
265 | for (i=0; i<stream_count; i++) { | ||
266 | int j; | ||
267 | struct mixart_flowinfo *flowinfo; | ||
268 | struct mixart_bufferinfo *bufferinfo; | ||
269 | |||
270 | /* we don't yet know the format, so config 16 bit pcm audio for instance */ | ||
271 | buf->sgroup_req.stream_info[i].size_max_byte_frame = 1024; | ||
272 | buf->sgroup_req.stream_info[i].size_max_sample_frame = 256; | ||
273 | buf->sgroup_req.stream_info[i].nb_bytes_max_per_sample = MIXART_FLOAT_P__4_0_TO_HEX; /* is 4.0f */ | ||
274 | |||
275 | /* find the right bufferinfo_array */ | ||
276 | j = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (pcm_number * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS)) + i; | ||
277 | if(capture) j += MIXART_PLAYBACK_STREAMS; /* in the array capture is behind playback */ | ||
278 | |||
279 | buf->sgroup_req.flow_entry[i] = j; | ||
280 | |||
281 | flowinfo = (struct mixart_flowinfo *)chip->mgr->flowinfo.area; | ||
282 | flowinfo[j].bufferinfo_array_phy_address = (u32)chip->mgr->bufferinfo.addr + (j * sizeof(mixart_bufferinfo_t)); | ||
283 | flowinfo[j].bufferinfo_count = 1; /* 1 will set the miXart to ring-buffer mode ! */ | ||
284 | |||
285 | bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area; | ||
286 | bufferinfo[j].buffer_address = 0; /* buffer is not yet allocated */ | ||
287 | bufferinfo[j].available_length = 0; /* buffer is not yet allocated */ | ||
288 | |||
289 | /* construct the identifier of the stream buffer received in the interrupts ! */ | ||
290 | bufferinfo[j].buffer_id = (chip->chip_idx << MIXART_NOTIFY_CARD_OFFSET) + (pcm_number << MIXART_NOTIFY_PCM_OFFSET ) + i; | ||
291 | if(capture) { | ||
292 | bufferinfo[j].buffer_id |= MIXART_NOTIFY_CAPT_MASK; | ||
293 | } | ||
294 | } | ||
295 | |||
296 | err = snd_mixart_send_msg(chip->mgr, &request, sizeof(buf->sgroup_resp), &buf->sgroup_resp); | ||
297 | if((err < 0) || (buf->sgroup_resp.status != 0)) { | ||
298 | snd_printk(KERN_ERR "error MSG_STREAM_ADD_**PUT_GROUP err=%x stat=%x !\n", err, buf->sgroup_resp.status); | ||
299 | kfree(buf); | ||
300 | return NULL; | ||
301 | } | ||
302 | |||
303 | pipe->group_uid = buf->sgroup_resp.group; /* id of the pipe, as returned by embedded */ | ||
304 | pipe->stream_count = buf->sgroup_resp.stream_count; | ||
305 | /* pipe->stream_uid[i] = buf->sgroup_resp.stream[i].stream_uid; */ | ||
306 | |||
307 | pipe->status = PIPE_STOPPED; | ||
308 | kfree(buf); | ||
309 | } | ||
310 | |||
311 | if(monitoring) pipe->monitoring = 1; | ||
312 | else pipe->references++; | ||
313 | |||
314 | return pipe; | ||
315 | } | ||
316 | |||
317 | |||
318 | int snd_mixart_kill_ref_pipe( mixart_mgr_t *mgr, mixart_pipe_t *pipe, int monitoring) | ||
319 | { | ||
320 | int err = 0; | ||
321 | |||
322 | if(pipe->status == PIPE_UNDEFINED) | ||
323 | return 0; | ||
324 | |||
325 | if(monitoring) | ||
326 | pipe->monitoring = 0; | ||
327 | else | ||
328 | pipe->references--; | ||
329 | |||
330 | if((pipe->references <= 0) && (pipe->monitoring == 0)) { | ||
331 | |||
332 | mixart_msg_t request; | ||
333 | mixart_delete_group_resp_t delete_resp; | ||
334 | |||
335 | /* release the clock */ | ||
336 | err = mixart_set_clock( mgr, pipe, 0); | ||
337 | if( err < 0 ) { | ||
338 | snd_printk(KERN_ERR "mixart_set_clock(0) return error!\n"); | ||
339 | } | ||
340 | |||
341 | /* stop the pipe */ | ||
342 | err = mixart_set_pipe_state(mgr, pipe, 0); | ||
343 | if( err < 0 ) { | ||
344 | snd_printk(KERN_ERR "error stopping pipe!\n"); | ||
345 | } | ||
346 | |||
347 | request.message_id = MSG_STREAM_DELETE_GROUP; | ||
348 | request.uid = (mixart_uid_t){0,0}; | ||
349 | request.data = &pipe->group_uid; /* the streaming group ! */ | ||
350 | request.size = sizeof(pipe->group_uid); | ||
351 | |||
352 | /* delete the pipe */ | ||
353 | err = snd_mixart_send_msg(mgr, &request, sizeof(delete_resp), &delete_resp); | ||
354 | if ((err < 0) || (delete_resp.status != 0)) { | ||
355 | snd_printk(KERN_ERR "error MSG_STREAM_DELETE_GROUP err(%x), status(%x)\n", err, delete_resp.status); | ||
356 | } | ||
357 | |||
358 | pipe->group_uid = (mixart_uid_t){0,0}; | ||
359 | pipe->stream_count = 0; | ||
360 | pipe->status = PIPE_UNDEFINED; | ||
361 | } | ||
362 | |||
363 | return err; | ||
364 | } | ||
365 | |||
366 | static int mixart_set_stream_state(mixart_stream_t *stream, int start) | ||
367 | { | ||
368 | mixart_t *chip; | ||
369 | mixart_stream_state_req_t stream_state_req; | ||
370 | mixart_msg_t request; | ||
371 | |||
372 | if(!stream->substream) | ||
373 | return -EINVAL; | ||
374 | |||
375 | memset(&stream_state_req, 0, sizeof(stream_state_req)); | ||
376 | stream_state_req.stream_count = 1; | ||
377 | stream_state_req.stream_info.stream_desc.uid_pipe = stream->pipe->group_uid; | ||
378 | stream_state_req.stream_info.stream_desc.stream_idx = stream->substream->number; | ||
379 | |||
380 | if (stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
381 | request.message_id = start ? MSG_STREAM_START_INPUT_STAGE_PACKET : MSG_STREAM_STOP_INPUT_STAGE_PACKET; | ||
382 | else | ||
383 | request.message_id = start ? MSG_STREAM_START_OUTPUT_STAGE_PACKET : MSG_STREAM_STOP_OUTPUT_STAGE_PACKET; | ||
384 | |||
385 | request.uid = (mixart_uid_t){0,0}; | ||
386 | request.data = &stream_state_req; | ||
387 | request.size = sizeof(stream_state_req); | ||
388 | |||
389 | stream->abs_period_elapsed = 0; /* reset stream pos */ | ||
390 | stream->buf_periods = 0; | ||
391 | stream->buf_period_frag = 0; | ||
392 | |||
393 | chip = snd_pcm_substream_chip(stream->substream); | ||
394 | |||
395 | return snd_mixart_send_msg_nonblock(chip->mgr, &request); | ||
396 | } | ||
397 | |||
398 | /* | ||
399 | * Trigger callback | ||
400 | */ | ||
401 | |||
402 | static int snd_mixart_trigger(snd_pcm_substream_t *subs, int cmd) | ||
403 | { | ||
404 | mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data; | ||
405 | |||
406 | switch (cmd) { | ||
407 | case SNDRV_PCM_TRIGGER_START: | ||
408 | |||
409 | snd_printdd("SNDRV_PCM_TRIGGER_START\n"); | ||
410 | |||
411 | /* START_STREAM */ | ||
412 | if( mixart_set_stream_state(stream, 1) ) | ||
413 | return -EINVAL; | ||
414 | |||
415 | stream->status = MIXART_STREAM_STATUS_RUNNING; | ||
416 | |||
417 | break; | ||
418 | case SNDRV_PCM_TRIGGER_STOP: | ||
419 | |||
420 | /* STOP_STREAM */ | ||
421 | if( mixart_set_stream_state(stream, 0) ) | ||
422 | return -EINVAL; | ||
423 | |||
424 | stream->status = MIXART_STREAM_STATUS_OPEN; | ||
425 | |||
426 | snd_printdd("SNDRV_PCM_TRIGGER_STOP\n"); | ||
427 | |||
428 | break; | ||
429 | |||
430 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
431 | /* TODO */ | ||
432 | stream->status = MIXART_STREAM_STATUS_PAUSE; | ||
433 | snd_printdd("SNDRV_PCM_PAUSE_PUSH\n"); | ||
434 | break; | ||
435 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
436 | /* TODO */ | ||
437 | stream->status = MIXART_STREAM_STATUS_RUNNING; | ||
438 | snd_printdd("SNDRV_PCM_PAUSE_RELEASE\n"); | ||
439 | break; | ||
440 | default: | ||
441 | return -EINVAL; | ||
442 | } | ||
443 | return 0; | ||
444 | } | ||
445 | |||
446 | static int mixart_sync_nonblock_events(mixart_mgr_t *mgr) | ||
447 | { | ||
448 | int timeout = HZ; | ||
449 | while (atomic_read(&mgr->msg_processed) > 0) { | ||
450 | if (! timeout--) { | ||
451 | snd_printk(KERN_ERR "mixart: cannot process nonblock events!\n"); | ||
452 | return -EBUSY; | ||
453 | } | ||
454 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
455 | schedule_timeout(1); | ||
456 | } | ||
457 | return 0; | ||
458 | } | ||
459 | |||
460 | /* | ||
461 | * prepare callback for all pcms | ||
462 | */ | ||
463 | static int snd_mixart_prepare(snd_pcm_substream_t *subs) | ||
464 | { | ||
465 | mixart_t *chip = snd_pcm_substream_chip(subs); | ||
466 | mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data; | ||
467 | |||
468 | /* TODO de façon non bloquante, réappliquer les hw_params (rate, bits, codec) */ | ||
469 | |||
470 | snd_printdd("snd_mixart_prepare\n"); | ||
471 | |||
472 | mixart_sync_nonblock_events(chip->mgr); | ||
473 | |||
474 | /* only the first stream can choose the sample rate */ | ||
475 | /* the further opened streams will be limited to its frequency (see open) */ | ||
476 | if(chip->mgr->ref_count_rate == 1) | ||
477 | chip->mgr->sample_rate = subs->runtime->rate; | ||
478 | |||
479 | /* set the clock only once (first stream) on the same pipe */ | ||
480 | if(stream->pipe->references == 1) { | ||
481 | if( mixart_set_clock(chip->mgr, stream->pipe, subs->runtime->rate) ) | ||
482 | return -EINVAL; | ||
483 | } | ||
484 | |||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | |||
489 | static int mixart_set_format(mixart_stream_t *stream, snd_pcm_format_t format) | ||
490 | { | ||
491 | int err; | ||
492 | mixart_t *chip; | ||
493 | mixart_msg_t request; | ||
494 | mixart_stream_param_desc_t stream_param; | ||
495 | mixart_return_uid_t resp; | ||
496 | |||
497 | chip = snd_pcm_substream_chip(stream->substream); | ||
498 | |||
499 | memset(&stream_param, 0, sizeof(stream_param)); | ||
500 | |||
501 | stream_param.coding_type = CT_LINEAR; | ||
502 | stream_param.number_of_channel = stream->channels; | ||
503 | |||
504 | stream_param.sampling_freq = chip->mgr->sample_rate; | ||
505 | if(stream_param.sampling_freq == 0) | ||
506 | stream_param.sampling_freq = 44100; /* if frequency not yet defined, use some default */ | ||
507 | |||
508 | switch(format){ | ||
509 | case SNDRV_PCM_FORMAT_U8: | ||
510 | stream_param.sample_type = ST_INTEGER_8; | ||
511 | stream_param.sample_size = 8; | ||
512 | break; | ||
513 | case SNDRV_PCM_FORMAT_S16_LE: | ||
514 | stream_param.sample_type = ST_INTEGER_16LE; | ||
515 | stream_param.sample_size = 16; | ||
516 | break; | ||
517 | case SNDRV_PCM_FORMAT_S16_BE: | ||
518 | stream_param.sample_type = ST_INTEGER_16BE; | ||
519 | stream_param.sample_size = 16; | ||
520 | break; | ||
521 | case SNDRV_PCM_FORMAT_S24_3LE: | ||
522 | stream_param.sample_type = ST_INTEGER_24LE; | ||
523 | stream_param.sample_size = 24; | ||
524 | break; | ||
525 | case SNDRV_PCM_FORMAT_S24_3BE: | ||
526 | stream_param.sample_type = ST_INTEGER_24BE; | ||
527 | stream_param.sample_size = 24; | ||
528 | break; | ||
529 | case SNDRV_PCM_FORMAT_FLOAT_LE: | ||
530 | stream_param.sample_type = ST_FLOATING_POINT_32LE; | ||
531 | stream_param.sample_size = 32; | ||
532 | break; | ||
533 | case SNDRV_PCM_FORMAT_FLOAT_BE: | ||
534 | stream_param.sample_type = ST_FLOATING_POINT_32BE; | ||
535 | stream_param.sample_size = 32; | ||
536 | break; | ||
537 | default: | ||
538 | snd_printk(KERN_ERR "error mixart_set_format() : unknown format\n"); | ||
539 | return -EINVAL; | ||
540 | } | ||
541 | |||
542 | snd_printdd("set SNDRV_PCM_FORMAT sample_type(%d) sample_size(%d) freq(%d) channels(%d)\n", | ||
543 | stream_param.sample_type, stream_param.sample_size, stream_param.sampling_freq, stream->channels); | ||
544 | |||
545 | /* TODO: what else to configure ? */ | ||
546 | /* stream_param.samples_per_frame = 2; */ | ||
547 | /* stream_param.bytes_per_frame = 4; */ | ||
548 | /* stream_param.bytes_per_sample = 2; */ | ||
549 | |||
550 | stream_param.pipe_count = 1; /* set to 1 */ | ||
551 | stream_param.stream_count = 1; /* set to 1 */ | ||
552 | stream_param.stream_desc[0].uid_pipe = stream->pipe->group_uid; | ||
553 | stream_param.stream_desc[0].stream_idx = stream->substream->number; | ||
554 | |||
555 | request.message_id = MSG_STREAM_SET_INPUT_STAGE_PARAM; | ||
556 | request.uid = (mixart_uid_t){0,0}; | ||
557 | request.data = &stream_param; | ||
558 | request.size = sizeof(stream_param); | ||
559 | |||
560 | err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp); | ||
561 | if((err < 0) || resp.error_code) { | ||
562 | snd_printk(KERN_ERR "MSG_STREAM_SET_INPUT_STAGE_PARAM err=%x; resp=%x\n", err, resp.error_code); | ||
563 | return -EINVAL; | ||
564 | } | ||
565 | return 0; | ||
566 | } | ||
567 | |||
568 | |||
569 | /* | ||
570 | * HW_PARAMS callback for all pcms | ||
571 | */ | ||
572 | static int snd_mixart_hw_params(snd_pcm_substream_t *subs, | ||
573 | snd_pcm_hw_params_t *hw) | ||
574 | { | ||
575 | mixart_t *chip = snd_pcm_substream_chip(subs); | ||
576 | mixart_mgr_t *mgr = chip->mgr; | ||
577 | mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data; | ||
578 | snd_pcm_format_t format; | ||
579 | int err; | ||
580 | int channels; | ||
581 | |||
582 | /* set up channels */ | ||
583 | channels = params_channels(hw); | ||
584 | |||
585 | /* set up format for the stream */ | ||
586 | format = params_format(hw); | ||
587 | |||
588 | down(&mgr->setup_mutex); | ||
589 | |||
590 | /* update the stream levels */ | ||
591 | if( stream->pcm_number <= MIXART_PCM_DIGITAL ) { | ||
592 | int is_aes = stream->pcm_number > MIXART_PCM_ANALOG; | ||
593 | if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK ) | ||
594 | mixart_update_playback_stream_level(chip, is_aes, subs->number); | ||
595 | else | ||
596 | mixart_update_capture_stream_level( chip, is_aes); | ||
597 | } | ||
598 | |||
599 | stream->channels = channels; | ||
600 | |||
601 | /* set the format to the board */ | ||
602 | err = mixart_set_format(stream, format); | ||
603 | if(err < 0) { | ||
604 | return err; | ||
605 | } | ||
606 | |||
607 | /* allocate buffer */ | ||
608 | err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw)); | ||
609 | |||
610 | if (err > 0) { | ||
611 | struct mixart_bufferinfo *bufferinfo; | ||
612 | int i = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (stream->pcm_number * (MIXART_PLAYBACK_STREAMS+MIXART_CAPTURE_STREAMS)) + subs->number; | ||
613 | if( subs->stream == SNDRV_PCM_STREAM_CAPTURE ) { | ||
614 | i += MIXART_PLAYBACK_STREAMS; /* in array capture is behind playback */ | ||
615 | } | ||
616 | |||
617 | bufferinfo = (struct mixart_bufferinfo *)chip->mgr->bufferinfo.area; | ||
618 | bufferinfo[i].buffer_address = subs->runtime->dma_addr; | ||
619 | bufferinfo[i].available_length = subs->runtime->dma_bytes; | ||
620 | /* bufferinfo[i].buffer_id is already defined */ | ||
621 | |||
622 | snd_printdd("snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n", i, | ||
623 | bufferinfo[i].buffer_address, | ||
624 | bufferinfo[i].available_length, | ||
625 | subs->number); | ||
626 | } | ||
627 | up(&mgr->setup_mutex); | ||
628 | |||
629 | return err; | ||
630 | } | ||
631 | |||
632 | static int snd_mixart_hw_free(snd_pcm_substream_t *subs) | ||
633 | { | ||
634 | mixart_t *chip = snd_pcm_substream_chip(subs); | ||
635 | snd_pcm_lib_free_pages(subs); | ||
636 | mixart_sync_nonblock_events(chip->mgr); | ||
637 | return 0; | ||
638 | } | ||
639 | |||
640 | |||
641 | |||
642 | /* | ||
643 | * TODO CONFIGURATION SPACE for all pcms, mono pcm must update channels_max | ||
644 | */ | ||
645 | static snd_pcm_hardware_t snd_mixart_analog_caps = | ||
646 | { | ||
647 | .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
648 | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | | ||
649 | SNDRV_PCM_INFO_PAUSE), | ||
650 | .formats = ( SNDRV_PCM_FMTBIT_U8 | | ||
651 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | | ||
652 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | | ||
653 | SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ), | ||
654 | .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, | ||
655 | .rate_min = 8000, | ||
656 | .rate_max = 48000, | ||
657 | .channels_min = 1, | ||
658 | .channels_max = 2, | ||
659 | .buffer_bytes_max = (32*1024), | ||
660 | .period_bytes_min = 256, /* 256 frames U8 mono*/ | ||
661 | .period_bytes_max = (16*1024), | ||
662 | .periods_min = 2, | ||
663 | .periods_max = (32*1024/256), | ||
664 | }; | ||
665 | |||
666 | static snd_pcm_hardware_t snd_mixart_digital_caps = | ||
667 | { | ||
668 | .info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | ||
669 | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | | ||
670 | SNDRV_PCM_INFO_PAUSE), | ||
671 | .formats = ( SNDRV_PCM_FMTBIT_U8 | | ||
672 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | | ||
673 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | | ||
674 | SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ), | ||
675 | .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, | ||
676 | .rate_min = 32000, | ||
677 | .rate_max = 48000, | ||
678 | .channels_min = 1, | ||
679 | .channels_max = 2, | ||
680 | .buffer_bytes_max = (32*1024), | ||
681 | .period_bytes_min = 256, /* 256 frames U8 mono*/ | ||
682 | .period_bytes_max = (16*1024), | ||
683 | .periods_min = 2, | ||
684 | .periods_max = (32*1024/256), | ||
685 | }; | ||
686 | |||
687 | |||
688 | static int snd_mixart_playback_open(snd_pcm_substream_t *subs) | ||
689 | { | ||
690 | mixart_t *chip = snd_pcm_substream_chip(subs); | ||
691 | mixart_mgr_t *mgr = chip->mgr; | ||
692 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
693 | snd_pcm_t *pcm = subs->pcm; | ||
694 | mixart_stream_t *stream; | ||
695 | mixart_pipe_t *pipe; | ||
696 | int err = 0; | ||
697 | int pcm_number; | ||
698 | |||
699 | down(&mgr->setup_mutex); | ||
700 | |||
701 | if ( pcm == chip->pcm ) { | ||
702 | pcm_number = MIXART_PCM_ANALOG; | ||
703 | runtime->hw = snd_mixart_analog_caps; | ||
704 | } else { | ||
705 | snd_assert ( pcm == chip->pcm_dig ); | ||
706 | pcm_number = MIXART_PCM_DIGITAL; | ||
707 | runtime->hw = snd_mixart_digital_caps; | ||
708 | } | ||
709 | snd_printdd("snd_mixart_playback_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number); | ||
710 | |||
711 | /* get stream info */ | ||
712 | stream = &(chip->playback_stream[pcm_number][subs->number]); | ||
713 | |||
714 | if (stream->status != MIXART_STREAM_STATUS_FREE){ | ||
715 | /* streams in use */ | ||
716 | snd_printk(KERN_ERR "snd_mixart_playback_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number); | ||
717 | err = -EBUSY; | ||
718 | goto _exit_open; | ||
719 | } | ||
720 | |||
721 | /* get pipe pointer (out pipe) */ | ||
722 | pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 0, 0); | ||
723 | |||
724 | if (pipe == NULL) { | ||
725 | err = -EINVAL; | ||
726 | goto _exit_open; | ||
727 | } | ||
728 | |||
729 | /* start the pipe if necessary */ | ||
730 | err = mixart_set_pipe_state(chip->mgr, pipe, 1); | ||
731 | if( err < 0 ) { | ||
732 | snd_printk(KERN_ERR "error starting pipe!\n"); | ||
733 | snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0); | ||
734 | err = -EINVAL; | ||
735 | goto _exit_open; | ||
736 | } | ||
737 | |||
738 | stream->pipe = pipe; | ||
739 | stream->pcm_number = pcm_number; | ||
740 | stream->status = MIXART_STREAM_STATUS_OPEN; | ||
741 | stream->substream = subs; | ||
742 | stream->channels = 0; /* not configured yet */ | ||
743 | |||
744 | runtime->private_data = stream; | ||
745 | |||
746 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); | ||
747 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64); | ||
748 | |||
749 | /* if a sample rate is already used, another stream cannot change */ | ||
750 | if(mgr->ref_count_rate++) { | ||
751 | if(mgr->sample_rate) { | ||
752 | runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate; | ||
753 | } | ||
754 | } | ||
755 | |||
756 | _exit_open: | ||
757 | up(&mgr->setup_mutex); | ||
758 | |||
759 | return err; | ||
760 | } | ||
761 | |||
762 | |||
763 | static int snd_mixart_capture_open(snd_pcm_substream_t *subs) | ||
764 | { | ||
765 | mixart_t *chip = snd_pcm_substream_chip(subs); | ||
766 | mixart_mgr_t *mgr = chip->mgr; | ||
767 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
768 | snd_pcm_t *pcm = subs->pcm; | ||
769 | mixart_stream_t *stream; | ||
770 | mixart_pipe_t *pipe; | ||
771 | int err = 0; | ||
772 | int pcm_number; | ||
773 | |||
774 | down(&mgr->setup_mutex); | ||
775 | |||
776 | if ( pcm == chip->pcm ) { | ||
777 | pcm_number = MIXART_PCM_ANALOG; | ||
778 | runtime->hw = snd_mixart_analog_caps; | ||
779 | } else { | ||
780 | snd_assert ( pcm == chip->pcm_dig ); | ||
781 | pcm_number = MIXART_PCM_DIGITAL; | ||
782 | runtime->hw = snd_mixart_digital_caps; | ||
783 | } | ||
784 | |||
785 | runtime->hw.channels_min = 2; /* for instance, no mono */ | ||
786 | |||
787 | snd_printdd("snd_mixart_capture_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number); | ||
788 | |||
789 | /* get stream info */ | ||
790 | stream = &(chip->capture_stream[pcm_number]); | ||
791 | |||
792 | if (stream->status != MIXART_STREAM_STATUS_FREE){ | ||
793 | /* streams in use */ | ||
794 | snd_printk(KERN_ERR "snd_mixart_capture_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number); | ||
795 | err = -EBUSY; | ||
796 | goto _exit_open; | ||
797 | } | ||
798 | |||
799 | /* get pipe pointer (in pipe) */ | ||
800 | pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 1, 0); | ||
801 | |||
802 | if (pipe == NULL) { | ||
803 | err = -EINVAL; | ||
804 | goto _exit_open; | ||
805 | } | ||
806 | |||
807 | /* start the pipe if necessary */ | ||
808 | err = mixart_set_pipe_state(chip->mgr, pipe, 1); | ||
809 | if( err < 0 ) { | ||
810 | snd_printk(KERN_ERR "error starting pipe!\n"); | ||
811 | snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0); | ||
812 | err = -EINVAL; | ||
813 | goto _exit_open; | ||
814 | } | ||
815 | |||
816 | stream->pipe = pipe; | ||
817 | stream->pcm_number = pcm_number; | ||
818 | stream->status = MIXART_STREAM_STATUS_OPEN; | ||
819 | stream->substream = subs; | ||
820 | stream->channels = 0; /* not configured yet */ | ||
821 | |||
822 | runtime->private_data = stream; | ||
823 | |||
824 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); | ||
825 | snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64); | ||
826 | |||
827 | /* if a sample rate is already used, another stream cannot change */ | ||
828 | if(mgr->ref_count_rate++) { | ||
829 | if(mgr->sample_rate) { | ||
830 | runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate; | ||
831 | } | ||
832 | } | ||
833 | |||
834 | _exit_open: | ||
835 | up(&mgr->setup_mutex); | ||
836 | |||
837 | return err; | ||
838 | } | ||
839 | |||
840 | |||
841 | |||
842 | static int snd_mixart_close(snd_pcm_substream_t *subs) | ||
843 | { | ||
844 | mixart_t *chip = snd_pcm_substream_chip(subs); | ||
845 | mixart_mgr_t *mgr = chip->mgr; | ||
846 | mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data; | ||
847 | |||
848 | down(&mgr->setup_mutex); | ||
849 | |||
850 | snd_printdd("snd_mixart_close C%d/P%d/Sub%d\n", chip->chip_idx, stream->pcm_number, subs->number); | ||
851 | |||
852 | /* sample rate released */ | ||
853 | if(--mgr->ref_count_rate == 0) { | ||
854 | mgr->sample_rate = 0; | ||
855 | } | ||
856 | |||
857 | /* delete pipe */ | ||
858 | if (snd_mixart_kill_ref_pipe(mgr, stream->pipe, 0 ) < 0) { | ||
859 | |||
860 | snd_printk(KERN_ERR "error snd_mixart_kill_ref_pipe C%dP%d\n", chip->chip_idx, stream->pcm_number); | ||
861 | } | ||
862 | |||
863 | stream->pipe = NULL; | ||
864 | stream->status = MIXART_STREAM_STATUS_FREE; | ||
865 | stream->substream = NULL; | ||
866 | |||
867 | up(&mgr->setup_mutex); | ||
868 | return 0; | ||
869 | } | ||
870 | |||
871 | |||
872 | static snd_pcm_uframes_t snd_mixart_stream_pointer(snd_pcm_substream_t * subs) | ||
873 | { | ||
874 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
875 | mixart_stream_t *stream = (mixart_stream_t*)runtime->private_data; | ||
876 | |||
877 | return (snd_pcm_uframes_t)((stream->buf_periods * runtime->period_size) + stream->buf_period_frag); | ||
878 | } | ||
879 | |||
880 | |||
881 | |||
882 | static snd_pcm_ops_t snd_mixart_playback_ops = { | ||
883 | .open = snd_mixart_playback_open, | ||
884 | .close = snd_mixart_close, | ||
885 | .ioctl = snd_pcm_lib_ioctl, | ||
886 | .prepare = snd_mixart_prepare, | ||
887 | .hw_params = snd_mixart_hw_params, | ||
888 | .hw_free = snd_mixart_hw_free, | ||
889 | .trigger = snd_mixart_trigger, | ||
890 | .pointer = snd_mixart_stream_pointer, | ||
891 | }; | ||
892 | |||
893 | static snd_pcm_ops_t snd_mixart_capture_ops = { | ||
894 | .open = snd_mixart_capture_open, | ||
895 | .close = snd_mixart_close, | ||
896 | .ioctl = snd_pcm_lib_ioctl, | ||
897 | .prepare = snd_mixart_prepare, | ||
898 | .hw_params = snd_mixart_hw_params, | ||
899 | .hw_free = snd_mixart_hw_free, | ||
900 | .trigger = snd_mixart_trigger, | ||
901 | .pointer = snd_mixart_stream_pointer, | ||
902 | }; | ||
903 | |||
904 | static void preallocate_buffers(mixart_t *chip, snd_pcm_t *pcm) | ||
905 | { | ||
906 | #if 0 | ||
907 | snd_pcm_substream_t *subs; | ||
908 | int stream; | ||
909 | |||
910 | for (stream = 0; stream < 2; stream++) { | ||
911 | int idx = 0; | ||
912 | for (subs = pcm->streams[stream].substream; subs; subs = subs->next, idx++) | ||
913 | /* set up the unique device id with the chip index */ | ||
914 | subs->dma_device.id = subs->pcm->device << 16 | | ||
915 | subs->stream << 8 | (subs->number + 1) | | ||
916 | (chip->chip_idx + 1) << 24; | ||
917 | } | ||
918 | #endif | ||
919 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, | ||
920 | snd_dma_pci_data(chip->mgr->pci), 32*1024, 32*1024); | ||
921 | } | ||
922 | |||
923 | /* | ||
924 | */ | ||
925 | static int snd_mixart_pcm_analog(mixart_t *chip) | ||
926 | { | ||
927 | int err; | ||
928 | snd_pcm_t *pcm; | ||
929 | char name[32]; | ||
930 | |||
931 | sprintf(name, "miXart analog %d", chip->chip_idx); | ||
932 | if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_ANALOG, | ||
933 | MIXART_PLAYBACK_STREAMS, | ||
934 | MIXART_CAPTURE_STREAMS, &pcm)) < 0) { | ||
935 | snd_printk(KERN_ERR "cannot create the analog pcm %d\n", chip->chip_idx); | ||
936 | return err; | ||
937 | } | ||
938 | |||
939 | pcm->private_data = chip; | ||
940 | |||
941 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops); | ||
942 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops); | ||
943 | |||
944 | pcm->info_flags = 0; | ||
945 | strcpy(pcm->name, name); | ||
946 | |||
947 | preallocate_buffers(chip, pcm); | ||
948 | |||
949 | chip->pcm = pcm; | ||
950 | return 0; | ||
951 | } | ||
952 | |||
953 | |||
954 | /* | ||
955 | */ | ||
956 | static int snd_mixart_pcm_digital(mixart_t *chip) | ||
957 | { | ||
958 | int err; | ||
959 | snd_pcm_t *pcm; | ||
960 | char name[32]; | ||
961 | |||
962 | sprintf(name, "miXart AES/EBU %d", chip->chip_idx); | ||
963 | if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_DIGITAL, | ||
964 | MIXART_PLAYBACK_STREAMS, | ||
965 | MIXART_CAPTURE_STREAMS, &pcm)) < 0) { | ||
966 | snd_printk(KERN_ERR "cannot create the digital pcm %d\n", chip->chip_idx); | ||
967 | return err; | ||
968 | } | ||
969 | |||
970 | pcm->private_data = chip; | ||
971 | |||
972 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops); | ||
973 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops); | ||
974 | |||
975 | pcm->info_flags = 0; | ||
976 | strcpy(pcm->name, name); | ||
977 | |||
978 | preallocate_buffers(chip, pcm); | ||
979 | |||
980 | chip->pcm_dig = pcm; | ||
981 | return 0; | ||
982 | } | ||
983 | |||
984 | static int snd_mixart_chip_free(mixart_t *chip) | ||
985 | { | ||
986 | kfree(chip); | ||
987 | return 0; | ||
988 | } | ||
989 | |||
990 | static int snd_mixart_chip_dev_free(snd_device_t *device) | ||
991 | { | ||
992 | mixart_t *chip = device->device_data; | ||
993 | return snd_mixart_chip_free(chip); | ||
994 | } | ||
995 | |||
996 | |||
997 | /* | ||
998 | */ | ||
999 | static int __devinit snd_mixart_create(mixart_mgr_t *mgr, snd_card_t *card, int idx) | ||
1000 | { | ||
1001 | int err; | ||
1002 | mixart_t *chip; | ||
1003 | static snd_device_ops_t ops = { | ||
1004 | .dev_free = snd_mixart_chip_dev_free, | ||
1005 | }; | ||
1006 | |||
1007 | mgr->chip[idx] = chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
1008 | if (! chip) { | ||
1009 | snd_printk(KERN_ERR "cannot allocate chip\n"); | ||
1010 | return -ENOMEM; | ||
1011 | } | ||
1012 | |||
1013 | chip->card = card; | ||
1014 | chip->chip_idx = idx; | ||
1015 | chip->mgr = mgr; | ||
1016 | |||
1017 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { | ||
1018 | snd_mixart_chip_free(chip); | ||
1019 | return err; | ||
1020 | } | ||
1021 | |||
1022 | snd_card_set_dev(card, &mgr->pci->dev); | ||
1023 | |||
1024 | return 0; | ||
1025 | } | ||
1026 | |||
1027 | int snd_mixart_create_pcm(mixart_t* chip) | ||
1028 | { | ||
1029 | int err; | ||
1030 | |||
1031 | err = snd_mixart_pcm_analog(chip); | ||
1032 | if (err < 0) | ||
1033 | return err; | ||
1034 | |||
1035 | if(chip->mgr->board_type == MIXART_DAUGHTER_TYPE_AES) { | ||
1036 | |||
1037 | err = snd_mixart_pcm_digital(chip); | ||
1038 | if (err < 0) | ||
1039 | return err; | ||
1040 | } | ||
1041 | return err; | ||
1042 | } | ||
1043 | |||
1044 | |||
1045 | /* | ||
1046 | * release all the cards assigned to a manager instance | ||
1047 | */ | ||
1048 | static int snd_mixart_free(mixart_mgr_t *mgr) | ||
1049 | { | ||
1050 | unsigned int i; | ||
1051 | |||
1052 | for (i = 0; i < mgr->num_cards; i++) { | ||
1053 | if (mgr->chip[i]) | ||
1054 | snd_card_free(mgr->chip[i]->card); | ||
1055 | } | ||
1056 | |||
1057 | /* stop mailbox */ | ||
1058 | snd_mixart_exit_mailbox(mgr); | ||
1059 | |||
1060 | /* release irq */ | ||
1061 | if (mgr->irq >= 0) | ||
1062 | free_irq(mgr->irq, (void *)mgr); | ||
1063 | |||
1064 | /* reset board if some firmware was loaded */ | ||
1065 | if(mgr->dsp_loaded) { | ||
1066 | snd_mixart_reset_board(mgr); | ||
1067 | snd_printdd("reset miXart !\n"); | ||
1068 | } | ||
1069 | |||
1070 | /* release the i/o ports */ | ||
1071 | for (i = 0; i < 2; i++) { | ||
1072 | if (mgr->mem[i].virt) | ||
1073 | iounmap(mgr->mem[i].virt); | ||
1074 | } | ||
1075 | pci_release_regions(mgr->pci); | ||
1076 | |||
1077 | /* free flowarray */ | ||
1078 | if(mgr->flowinfo.area) { | ||
1079 | snd_dma_free_pages(&mgr->flowinfo); | ||
1080 | mgr->flowinfo.area = NULL; | ||
1081 | } | ||
1082 | /* free bufferarray */ | ||
1083 | if(mgr->bufferinfo.area) { | ||
1084 | snd_dma_free_pages(&mgr->bufferinfo); | ||
1085 | mgr->bufferinfo.area = NULL; | ||
1086 | } | ||
1087 | |||
1088 | pci_disable_device(mgr->pci); | ||
1089 | kfree(mgr); | ||
1090 | return 0; | ||
1091 | } | ||
1092 | |||
1093 | /* | ||
1094 | * proc interface | ||
1095 | */ | ||
1096 | static long long snd_mixart_BA0_llseek(snd_info_entry_t *entry, | ||
1097 | void *private_file_data, | ||
1098 | struct file *file, | ||
1099 | long long offset, | ||
1100 | int orig) | ||
1101 | { | ||
1102 | offset = offset & ~3; /* 4 bytes aligned */ | ||
1103 | |||
1104 | switch(orig) { | ||
1105 | case 0: /* SEEK_SET */ | ||
1106 | file->f_pos = offset; | ||
1107 | break; | ||
1108 | case 1: /* SEEK_CUR */ | ||
1109 | file->f_pos += offset; | ||
1110 | break; | ||
1111 | case 2: /* SEEK_END, offset is negative */ | ||
1112 | file->f_pos = MIXART_BA0_SIZE + offset; | ||
1113 | break; | ||
1114 | default: | ||
1115 | return -EINVAL; | ||
1116 | } | ||
1117 | if(file->f_pos > MIXART_BA0_SIZE) | ||
1118 | file->f_pos = MIXART_BA0_SIZE; | ||
1119 | return file->f_pos; | ||
1120 | } | ||
1121 | |||
1122 | static long long snd_mixart_BA1_llseek(snd_info_entry_t *entry, | ||
1123 | void *private_file_data, | ||
1124 | struct file *file, | ||
1125 | long long offset, | ||
1126 | int orig) | ||
1127 | { | ||
1128 | offset = offset & ~3; /* 4 bytes aligned */ | ||
1129 | |||
1130 | switch(orig) { | ||
1131 | case 0: /* SEEK_SET */ | ||
1132 | file->f_pos = offset; | ||
1133 | break; | ||
1134 | case 1: /* SEEK_CUR */ | ||
1135 | file->f_pos += offset; | ||
1136 | break; | ||
1137 | case 2: /* SEEK_END, offset is negative */ | ||
1138 | file->f_pos = MIXART_BA1_SIZE + offset; | ||
1139 | break; | ||
1140 | default: | ||
1141 | return -EINVAL; | ||
1142 | } | ||
1143 | if(file->f_pos > MIXART_BA1_SIZE) | ||
1144 | file->f_pos = MIXART_BA1_SIZE; | ||
1145 | return file->f_pos; | ||
1146 | } | ||
1147 | |||
1148 | /* | ||
1149 | mixart_BA0 proc interface for BAR 0 - read callback | ||
1150 | */ | ||
1151 | static long snd_mixart_BA0_read(snd_info_entry_t *entry, void *file_private_data, | ||
1152 | struct file *file, char __user *buf, | ||
1153 | unsigned long count, unsigned long pos) | ||
1154 | { | ||
1155 | mixart_mgr_t *mgr = entry->private_data; | ||
1156 | |||
1157 | count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ | ||
1158 | if(count <= 0) | ||
1159 | return 0; | ||
1160 | if(pos + count > MIXART_BA0_SIZE) | ||
1161 | count = (long)(MIXART_BA0_SIZE - pos); | ||
1162 | if(copy_to_user_fromio(buf, MIXART_MEM( mgr, pos ), count)) | ||
1163 | return -EFAULT; | ||
1164 | return count; | ||
1165 | } | ||
1166 | |||
1167 | /* | ||
1168 | mixart_BA1 proc interface for BAR 1 - read callback | ||
1169 | */ | ||
1170 | static long snd_mixart_BA1_read(snd_info_entry_t *entry, void *file_private_data, | ||
1171 | struct file *file, char __user *buf, | ||
1172 | unsigned long count, unsigned long pos) | ||
1173 | { | ||
1174 | mixart_mgr_t *mgr = entry->private_data; | ||
1175 | |||
1176 | count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ | ||
1177 | if(count <= 0) | ||
1178 | return 0; | ||
1179 | if(pos + count > MIXART_BA1_SIZE) | ||
1180 | count = (long)(MIXART_BA1_SIZE - pos); | ||
1181 | if(copy_to_user_fromio(buf, MIXART_REG( mgr, pos ), count)) | ||
1182 | return -EFAULT; | ||
1183 | return count; | ||
1184 | } | ||
1185 | |||
1186 | static struct snd_info_entry_ops snd_mixart_proc_ops_BA0 = { | ||
1187 | .read = snd_mixart_BA0_read, | ||
1188 | .llseek = snd_mixart_BA0_llseek | ||
1189 | }; | ||
1190 | |||
1191 | static struct snd_info_entry_ops snd_mixart_proc_ops_BA1 = { | ||
1192 | .read = snd_mixart_BA1_read, | ||
1193 | .llseek = snd_mixart_BA1_llseek | ||
1194 | }; | ||
1195 | |||
1196 | |||
1197 | static void snd_mixart_proc_read(snd_info_entry_t *entry, | ||
1198 | snd_info_buffer_t * buffer) | ||
1199 | { | ||
1200 | mixart_t *chip = entry->private_data; | ||
1201 | u32 ref; | ||
1202 | |||
1203 | snd_iprintf(buffer, "Digigram miXart (alsa card %d)\n\n", chip->chip_idx); | ||
1204 | |||
1205 | /* stats available when embedded OS is running */ | ||
1206 | if (chip->mgr->dsp_loaded & ( 1 << MIXART_MOTHERBOARD_ELF_INDEX)) { | ||
1207 | snd_iprintf(buffer, "- hardware -\n"); | ||
1208 | switch (chip->mgr->board_type ) { | ||
1209 | case MIXART_DAUGHTER_TYPE_NONE : snd_iprintf(buffer, "\tmiXart8 (no daughter board)\n\n"); break; | ||
1210 | case MIXART_DAUGHTER_TYPE_AES : snd_iprintf(buffer, "\tmiXart8 AES/EBU\n\n"); break; | ||
1211 | case MIXART_DAUGHTER_TYPE_COBRANET : snd_iprintf(buffer, "\tmiXart8 Cobranet\n\n"); break; | ||
1212 | default: snd_iprintf(buffer, "\tUNKNOWN!\n\n"); break; | ||
1213 | } | ||
1214 | |||
1215 | snd_iprintf(buffer, "- system load -\n"); | ||
1216 | |||
1217 | /* get perf reference */ | ||
1218 | |||
1219 | ref = readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET)); | ||
1220 | |||
1221 | if (ref) { | ||
1222 | u32 mailbox = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET)) / ref; | ||
1223 | u32 streaming = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET)) / ref; | ||
1224 | u32 interr = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET)) / ref; | ||
1225 | |||
1226 | snd_iprintf(buffer, "\tstreaming : %d\n", streaming); | ||
1227 | snd_iprintf(buffer, "\tmailbox : %d\n", mailbox); | ||
1228 | snd_iprintf(buffer, "\tinterrups handling : %d\n\n", interr); | ||
1229 | } | ||
1230 | } /* endif elf loaded */ | ||
1231 | } | ||
1232 | |||
1233 | static void __devinit snd_mixart_proc_init(mixart_t *chip) | ||
1234 | { | ||
1235 | snd_info_entry_t *entry; | ||
1236 | |||
1237 | /* text interface to read perf and temp meters */ | ||
1238 | if (! snd_card_proc_new(chip->card, "board_info", &entry)) { | ||
1239 | entry->private_data = chip; | ||
1240 | entry->c.text.read_size = 1024; | ||
1241 | entry->c.text.read = snd_mixart_proc_read; | ||
1242 | } | ||
1243 | |||
1244 | if (! snd_card_proc_new(chip->card, "mixart_BA0", &entry)) { | ||
1245 | entry->content = SNDRV_INFO_CONTENT_DATA; | ||
1246 | entry->private_data = chip->mgr; | ||
1247 | entry->c.ops = &snd_mixart_proc_ops_BA0; | ||
1248 | entry->size = MIXART_BA0_SIZE; | ||
1249 | } | ||
1250 | if (! snd_card_proc_new(chip->card, "mixart_BA1", &entry)) { | ||
1251 | entry->content = SNDRV_INFO_CONTENT_DATA; | ||
1252 | entry->private_data = chip->mgr; | ||
1253 | entry->c.ops = &snd_mixart_proc_ops_BA1; | ||
1254 | entry->size = MIXART_BA1_SIZE; | ||
1255 | } | ||
1256 | } | ||
1257 | /* end of proc interface */ | ||
1258 | |||
1259 | |||
1260 | /* | ||
1261 | * probe function - creates the card manager | ||
1262 | */ | ||
1263 | static int __devinit snd_mixart_probe(struct pci_dev *pci, | ||
1264 | const struct pci_device_id *pci_id) | ||
1265 | { | ||
1266 | static int dev; | ||
1267 | mixart_mgr_t *mgr; | ||
1268 | unsigned int i; | ||
1269 | int err; | ||
1270 | size_t size; | ||
1271 | |||
1272 | /* | ||
1273 | */ | ||
1274 | if (dev >= SNDRV_CARDS) | ||
1275 | return -ENODEV; | ||
1276 | if (! enable[dev]) { | ||
1277 | dev++; | ||
1278 | return -ENOENT; | ||
1279 | } | ||
1280 | |||
1281 | /* enable PCI device */ | ||
1282 | if ((err = pci_enable_device(pci)) < 0) | ||
1283 | return err; | ||
1284 | pci_set_master(pci); | ||
1285 | |||
1286 | /* check if we can restrict PCI DMA transfers to 32 bits */ | ||
1287 | if (pci_set_dma_mask(pci, 0xffffffff) < 0) { | ||
1288 | snd_printk(KERN_ERR "architecture does not support 32bit PCI busmaster DMA\n"); | ||
1289 | pci_disable_device(pci); | ||
1290 | return -ENXIO; | ||
1291 | } | ||
1292 | |||
1293 | /* | ||
1294 | */ | ||
1295 | mgr = kcalloc(1, sizeof(*mgr), GFP_KERNEL); | ||
1296 | if (! mgr) { | ||
1297 | pci_disable_device(pci); | ||
1298 | return -ENOMEM; | ||
1299 | } | ||
1300 | |||
1301 | mgr->pci = pci; | ||
1302 | mgr->irq = -1; | ||
1303 | |||
1304 | /* resource assignment */ | ||
1305 | if ((err = pci_request_regions(pci, CARD_NAME)) < 0) { | ||
1306 | kfree(mgr); | ||
1307 | pci_disable_device(pci); | ||
1308 | return err; | ||
1309 | } | ||
1310 | for (i = 0; i < 2; i++) { | ||
1311 | mgr->mem[i].phys = pci_resource_start(pci, i); | ||
1312 | mgr->mem[i].virt = ioremap_nocache(mgr->mem[i].phys, | ||
1313 | pci_resource_len(pci, i)); | ||
1314 | } | ||
1315 | |||
1316 | if (request_irq(pci->irq, snd_mixart_interrupt, SA_INTERRUPT|SA_SHIRQ, CARD_NAME, (void *)mgr)) { | ||
1317 | snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); | ||
1318 | snd_mixart_free(mgr); | ||
1319 | return -EBUSY; | ||
1320 | } | ||
1321 | mgr->irq = pci->irq; | ||
1322 | |||
1323 | sprintf(mgr->shortname, "Digigram miXart"); | ||
1324 | sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, irq %i", mgr->shortname, mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq); | ||
1325 | |||
1326 | /* ISR spinlock */ | ||
1327 | spin_lock_init(&mgr->lock); | ||
1328 | |||
1329 | /* init mailbox */ | ||
1330 | mgr->msg_fifo_readptr = 0; | ||
1331 | mgr->msg_fifo_writeptr = 0; | ||
1332 | |||
1333 | spin_lock_init(&mgr->msg_lock); | ||
1334 | init_MUTEX(&mgr->msg_mutex); | ||
1335 | init_waitqueue_head(&mgr->msg_sleep); | ||
1336 | atomic_set(&mgr->msg_processed, 0); | ||
1337 | |||
1338 | /* init setup mutex*/ | ||
1339 | init_MUTEX(&mgr->setup_mutex); | ||
1340 | |||
1341 | /* init message taslket */ | ||
1342 | tasklet_init( &mgr->msg_taskq, snd_mixart_msg_tasklet, (unsigned long) mgr); | ||
1343 | |||
1344 | /* card assignment */ | ||
1345 | mgr->num_cards = MIXART_MAX_CARDS; /* 4 FIXME: configurable? */ | ||
1346 | for (i = 0; i < mgr->num_cards; i++) { | ||
1347 | snd_card_t *card; | ||
1348 | char tmpid[16]; | ||
1349 | int idx; | ||
1350 | |||
1351 | if (index[dev] < 0) | ||
1352 | idx = index[dev]; | ||
1353 | else | ||
1354 | idx = index[dev] + i; | ||
1355 | snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev] ? id[dev] : "MIXART", i); | ||
1356 | card = snd_card_new(idx, tmpid, THIS_MODULE, 0); | ||
1357 | |||
1358 | if (! card) { | ||
1359 | snd_printk(KERN_ERR "cannot allocate the card %d\n", i); | ||
1360 | snd_mixart_free(mgr); | ||
1361 | return -ENOMEM; | ||
1362 | } | ||
1363 | |||
1364 | strcpy(card->driver, CARD_NAME); | ||
1365 | sprintf(card->shortname, "%s [PCM #%d]", mgr->shortname, i); | ||
1366 | sprintf(card->longname, "%s [PCM #%d]", mgr->longname, i); | ||
1367 | |||
1368 | if ((err = snd_mixart_create(mgr, card, i)) < 0) { | ||
1369 | snd_mixart_free(mgr); | ||
1370 | return err; | ||
1371 | } | ||
1372 | |||
1373 | if(i==0) { | ||
1374 | /* init proc interface only for chip0 */ | ||
1375 | snd_mixart_proc_init(mgr->chip[i]); | ||
1376 | } | ||
1377 | |||
1378 | if ((err = snd_card_register(card)) < 0) { | ||
1379 | snd_mixart_free(mgr); | ||
1380 | return err; | ||
1381 | } | ||
1382 | } | ||
1383 | |||
1384 | /* init firmware status (mgr->dsp_loaded reset in hwdep_new) */ | ||
1385 | mgr->board_type = MIXART_DAUGHTER_TYPE_NONE; | ||
1386 | |||
1387 | /* create array of streaminfo */ | ||
1388 | size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_flowinfo_t)) ); | ||
1389 | if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), | ||
1390 | size, &mgr->flowinfo) < 0) { | ||
1391 | snd_mixart_free(mgr); | ||
1392 | return -ENOMEM; | ||
1393 | } | ||
1394 | /* init streaminfo_array */ | ||
1395 | memset(mgr->flowinfo.area, 0, size); | ||
1396 | |||
1397 | /* create array of bufferinfo */ | ||
1398 | size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_bufferinfo_t)) ); | ||
1399 | if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), | ||
1400 | size, &mgr->bufferinfo) < 0) { | ||
1401 | snd_mixart_free(mgr); | ||
1402 | return -ENOMEM; | ||
1403 | } | ||
1404 | /* init bufferinfo_array */ | ||
1405 | memset(mgr->bufferinfo.area, 0, size); | ||
1406 | |||
1407 | /* set up firmware */ | ||
1408 | err = snd_mixart_setup_firmware(mgr); | ||
1409 | if (err < 0) { | ||
1410 | snd_mixart_free(mgr); | ||
1411 | return err; | ||
1412 | } | ||
1413 | |||
1414 | pci_set_drvdata(pci, mgr); | ||
1415 | dev++; | ||
1416 | return 0; | ||
1417 | } | ||
1418 | |||
1419 | static void __devexit snd_mixart_remove(struct pci_dev *pci) | ||
1420 | { | ||
1421 | snd_mixart_free(pci_get_drvdata(pci)); | ||
1422 | pci_set_drvdata(pci, NULL); | ||
1423 | } | ||
1424 | |||
1425 | static struct pci_driver driver = { | ||
1426 | .name = "Digigram miXart", | ||
1427 | .id_table = snd_mixart_ids, | ||
1428 | .probe = snd_mixart_probe, | ||
1429 | .remove = __devexit_p(snd_mixart_remove), | ||
1430 | }; | ||
1431 | |||
1432 | static int __init alsa_card_mixart_init(void) | ||
1433 | { | ||
1434 | return pci_module_init(&driver); | ||
1435 | } | ||
1436 | |||
1437 | static void __exit alsa_card_mixart_exit(void) | ||
1438 | { | ||
1439 | pci_unregister_driver(&driver); | ||
1440 | } | ||
1441 | |||
1442 | module_init(alsa_card_mixart_init) | ||
1443 | module_exit(alsa_card_mixart_exit) | ||