1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for Digigram miXart soundcards |
4 | * |
5 | * DSP firmware management |
6 | * |
7 | * Copyright (c) 2003 by Digigram <alsa@digigram.com> |
8 | */ |
9 | |
10 | #include <linux/interrupt.h> |
11 | #include <linux/pci.h> |
12 | #include <linux/firmware.h> |
13 | #include <linux/vmalloc.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/module.h> |
16 | #include <linux/io.h> |
17 | #include <sound/core.h> |
18 | #include "mixart.h" |
19 | #include "mixart_mixer.h" |
20 | #include "mixart_core.h" |
21 | #include "mixart_hwdep.h" |
22 | |
23 | |
24 | /** |
25 | * mixart_wait_nice_for_register_value - wait for a value on a peudo register, |
26 | * exit with a timeout |
27 | * |
28 | * @mgr: pointer to miXart manager structure |
29 | * @offset: unsigned pseudo_register base + offset of value |
30 | * @is_egal: wait for the equal value |
31 | * @value: value |
32 | * @timeout: timeout in centisenconds |
33 | */ |
34 | static int mixart_wait_nice_for_register_value(struct mixart_mgr *mgr, |
35 | u32 offset, int is_egal, |
36 | u32 value, unsigned long timeout) |
37 | { |
38 | unsigned long end_time = jiffies + (timeout * HZ / 100); |
39 | u32 read; |
40 | |
41 | do { /* we may take too long time in this loop. |
42 | * so give controls back to kernel if needed. |
43 | */ |
44 | cond_resched(); |
45 | |
46 | read = readl_be( MIXART_MEM( mgr, offset )); |
47 | if(is_egal) { |
48 | if(read == value) return 0; |
49 | } |
50 | else { /* wait for different value */ |
51 | if(read != value) return 0; |
52 | } |
53 | } while ( time_after_eq(end_time, jiffies) ); |
54 | |
55 | return -EBUSY; |
56 | } |
57 | |
58 | |
59 | /* |
60 | structures needed to upload elf code packets |
61 | */ |
62 | struct snd_mixart_elf32_ehdr { |
63 | u8 e_ident[16]; |
64 | __be16 e_type; |
65 | __be16 e_machine; |
66 | __be32 e_version; |
67 | __be32 e_entry; |
68 | __be32 e_phoff; |
69 | __be32 e_shoff; |
70 | __be32 e_flags; |
71 | __be16 e_ehsize; |
72 | __be16 e_phentsize; |
73 | __be16 e_phnum; |
74 | __be16 e_shentsize; |
75 | __be16 e_shnum; |
76 | __be16 e_shstrndx; |
77 | }; |
78 | |
79 | struct snd_mixart_elf32_phdr { |
80 | __be32 p_type; |
81 | __be32 p_offset; |
82 | __be32 p_vaddr; |
83 | __be32 p_paddr; |
84 | __be32 p_filesz; |
85 | __be32 p_memsz; |
86 | __be32 p_flags; |
87 | __be32 p_align; |
88 | }; |
89 | |
90 | static int mixart_load_elf(struct mixart_mgr *mgr, const struct firmware *dsp ) |
91 | { |
92 | char elf32_magic_number[4] = {0x7f,'E','L','F'}; |
93 | struct snd_mixart_elf32_ehdr *; |
94 | int i; |
95 | |
96 | elf_header = (struct snd_mixart_elf32_ehdr *)dsp->data; |
97 | for( i=0; i<4; i++ ) |
98 | if ( elf32_magic_number[i] != elf_header->e_ident[i] ) |
99 | return -EINVAL; |
100 | |
101 | if( elf_header->e_phoff != 0 ) { |
102 | struct snd_mixart_elf32_phdr ; |
103 | |
104 | for( i=0; i < be16_to_cpu(elf_header->e_phnum); i++ ) { |
105 | u32 pos = be32_to_cpu(elf_header->e_phoff) + (u32)(i * be16_to_cpu(elf_header->e_phentsize)); |
106 | |
107 | memcpy( &elf_programheader, dsp->data + pos, sizeof(elf_programheader) ); |
108 | |
109 | if(elf_programheader.p_type != 0) { |
110 | if( elf_programheader.p_filesz != 0 ) { |
111 | memcpy_toio( MIXART_MEM( mgr, be32_to_cpu(elf_programheader.p_vaddr)), |
112 | dsp->data + be32_to_cpu( elf_programheader.p_offset ), |
113 | be32_to_cpu( elf_programheader.p_filesz )); |
114 | } |
115 | } |
116 | } |
117 | } |
118 | return 0; |
119 | } |
120 | |
121 | /* |
122 | * get basic information and init miXart |
123 | */ |
124 | |
125 | /* audio IDs for request to the board */ |
126 | #define MIXART_FIRST_ANA_AUDIO_ID 0 |
127 | #define MIXART_FIRST_DIG_AUDIO_ID 8 |
128 | |
129 | static int mixart_enum_connectors(struct mixart_mgr *mgr) |
130 | { |
131 | u32 k; |
132 | int err; |
133 | struct mixart_msg request; |
134 | struct mixart_enum_connector_resp *connector; |
135 | struct mixart_audio_info_req *audio_info_req; |
136 | struct mixart_audio_info_resp *audio_info; |
137 | |
138 | connector = kmalloc(size: sizeof(*connector), GFP_KERNEL); |
139 | audio_info_req = kmalloc(size: sizeof(*audio_info_req), GFP_KERNEL); |
140 | audio_info = kmalloc(size: sizeof(*audio_info), GFP_KERNEL); |
141 | if (! connector || ! audio_info_req || ! audio_info) { |
142 | err = -ENOMEM; |
143 | goto __error; |
144 | } |
145 | |
146 | audio_info_req->line_max_level = MIXART_FLOAT_P_22_0_TO_HEX; |
147 | audio_info_req->micro_max_level = MIXART_FLOAT_M_20_0_TO_HEX; |
148 | audio_info_req->cd_max_level = MIXART_FLOAT____0_0_TO_HEX; |
149 | |
150 | request.message_id = MSG_SYSTEM_ENUM_PLAY_CONNECTOR; |
151 | request.uid = (struct mixart_uid){0,0}; /* board num = 0 */ |
152 | request.data = NULL; |
153 | request.size = 0; |
154 | |
155 | err = snd_mixart_send_msg(mgr, request: &request, max_resp_size: sizeof(*connector), resp_data: connector); |
156 | if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) { |
157 | dev_err(&mgr->pci->dev, |
158 | "error MSG_SYSTEM_ENUM_PLAY_CONNECTOR\n" ); |
159 | err = -EINVAL; |
160 | goto __error; |
161 | } |
162 | |
163 | for(k=0; k < connector->uid_count; k++) { |
164 | struct mixart_pipe *pipe; |
165 | |
166 | if(k < MIXART_FIRST_DIG_AUDIO_ID) { |
167 | pipe = &mgr->chip[k/2]->pipe_out_ana; |
168 | } else { |
169 | pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_out_dig; |
170 | } |
171 | if(k & 1) { |
172 | pipe->uid_right_connector = connector->uid[k]; /* odd */ |
173 | } else { |
174 | pipe->uid_left_connector = connector->uid[k]; /* even */ |
175 | } |
176 | |
177 | /* dev_dbg(&mgr->pci->dev, "playback connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */ |
178 | |
179 | /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */ |
180 | request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO; |
181 | request.uid = connector->uid[k]; |
182 | request.data = audio_info_req; |
183 | request.size = sizeof(*audio_info_req); |
184 | |
185 | err = snd_mixart_send_msg(mgr, request: &request, max_resp_size: sizeof(*audio_info), resp_data: audio_info); |
186 | if( err < 0 ) { |
187 | dev_err(&mgr->pci->dev, |
188 | "error MSG_CONNECTOR_GET_AUDIO_INFO\n" ); |
189 | goto __error; |
190 | } |
191 | /*dev_dbg(&mgr->pci->dev, "play analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/ |
192 | } |
193 | |
194 | request.message_id = MSG_SYSTEM_ENUM_RECORD_CONNECTOR; |
195 | request.uid = (struct mixart_uid){0,0}; /* board num = 0 */ |
196 | request.data = NULL; |
197 | request.size = 0; |
198 | |
199 | err = snd_mixart_send_msg(mgr, request: &request, max_resp_size: sizeof(*connector), resp_data: connector); |
200 | if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) { |
201 | dev_err(&mgr->pci->dev, |
202 | "error MSG_SYSTEM_ENUM_RECORD_CONNECTOR\n" ); |
203 | err = -EINVAL; |
204 | goto __error; |
205 | } |
206 | |
207 | for(k=0; k < connector->uid_count; k++) { |
208 | struct mixart_pipe *pipe; |
209 | |
210 | if(k < MIXART_FIRST_DIG_AUDIO_ID) { |
211 | pipe = &mgr->chip[k/2]->pipe_in_ana; |
212 | } else { |
213 | pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_in_dig; |
214 | } |
215 | if(k & 1) { |
216 | pipe->uid_right_connector = connector->uid[k]; /* odd */ |
217 | } else { |
218 | pipe->uid_left_connector = connector->uid[k]; /* even */ |
219 | } |
220 | |
221 | /* dev_dbg(&mgr->pci->dev, "capture connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */ |
222 | |
223 | /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */ |
224 | request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO; |
225 | request.uid = connector->uid[k]; |
226 | request.data = audio_info_req; |
227 | request.size = sizeof(*audio_info_req); |
228 | |
229 | err = snd_mixart_send_msg(mgr, request: &request, max_resp_size: sizeof(*audio_info), resp_data: audio_info); |
230 | if( err < 0 ) { |
231 | dev_err(&mgr->pci->dev, |
232 | "error MSG_CONNECTOR_GET_AUDIO_INFO\n" ); |
233 | goto __error; |
234 | } |
235 | /*dev_dbg(&mgr->pci->dev, "rec analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/ |
236 | } |
237 | err = 0; |
238 | |
239 | __error: |
240 | kfree(objp: connector); |
241 | kfree(objp: audio_info_req); |
242 | kfree(objp: audio_info); |
243 | |
244 | return err; |
245 | } |
246 | |
247 | static int mixart_enum_physio(struct mixart_mgr *mgr) |
248 | { |
249 | u32 k; |
250 | int err; |
251 | struct mixart_msg request; |
252 | struct mixart_uid get_console_mgr; |
253 | struct mixart_return_uid console_mgr; |
254 | struct mixart_uid_enumeration phys_io; |
255 | |
256 | /* get the uid for the console manager */ |
257 | get_console_mgr.object_id = 0; |
258 | get_console_mgr.desc = MSG_CONSOLE_MANAGER | 0; /* cardindex = 0 */ |
259 | |
260 | request.message_id = MSG_CONSOLE_GET_CLOCK_UID; |
261 | request.uid = get_console_mgr; |
262 | request.data = &get_console_mgr; |
263 | request.size = sizeof(get_console_mgr); |
264 | |
265 | err = snd_mixart_send_msg(mgr, request: &request, max_resp_size: sizeof(console_mgr), resp_data: &console_mgr); |
266 | |
267 | if( (err < 0) || (console_mgr.error_code != 0) ) { |
268 | dev_dbg(&mgr->pci->dev, |
269 | "error MSG_CONSOLE_GET_CLOCK_UID : err=%x\n" , |
270 | console_mgr.error_code); |
271 | return -EINVAL; |
272 | } |
273 | |
274 | /* used later for clock issues ! */ |
275 | mgr->uid_console_manager = console_mgr.uid; |
276 | |
277 | request.message_id = MSG_SYSTEM_ENUM_PHYSICAL_IO; |
278 | request.uid = (struct mixart_uid){0,0}; |
279 | request.data = &console_mgr.uid; |
280 | request.size = sizeof(console_mgr.uid); |
281 | |
282 | err = snd_mixart_send_msg(mgr, request: &request, max_resp_size: sizeof(phys_io), resp_data: &phys_io); |
283 | if( (err < 0) || ( phys_io.error_code != 0 ) ) { |
284 | dev_err(&mgr->pci->dev, |
285 | "error MSG_SYSTEM_ENUM_PHYSICAL_IO err(%x) error_code(%x)\n" , |
286 | err, phys_io.error_code); |
287 | return -EINVAL; |
288 | } |
289 | |
290 | /* min 2 phys io per card (analog in + analog out) */ |
291 | if (phys_io.nb_uid < MIXART_MAX_CARDS * 2) |
292 | return -EINVAL; |
293 | |
294 | for(k=0; k<mgr->num_cards; k++) { |
295 | mgr->chip[k]->uid_in_analog_physio = phys_io.uid[k]; |
296 | mgr->chip[k]->uid_out_analog_physio = phys_io.uid[phys_io.nb_uid/2 + k]; |
297 | } |
298 | |
299 | return 0; |
300 | } |
301 | |
302 | |
303 | static int mixart_first_init(struct mixart_mgr *mgr) |
304 | { |
305 | u32 k; |
306 | int err; |
307 | struct mixart_msg request; |
308 | |
309 | err = mixart_enum_connectors(mgr); |
310 | if (err < 0) |
311 | return err; |
312 | |
313 | err = mixart_enum_physio(mgr); |
314 | if (err < 0) |
315 | return err; |
316 | |
317 | /* send a synchro command to card (necessary to do this before first MSG_STREAM_START_STREAM_GRP_PACKET) */ |
318 | /* though why not here */ |
319 | request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD; |
320 | request.uid = (struct mixart_uid){0,0}; |
321 | request.data = NULL; |
322 | request.size = 0; |
323 | /* this command has no data. response is a 32 bit status */ |
324 | err = snd_mixart_send_msg(mgr, request: &request, max_resp_size: sizeof(k), resp_data: &k); |
325 | if( (err < 0) || (k != 0) ) { |
326 | dev_err(&mgr->pci->dev, "error MSG_SYSTEM_SEND_SYNCHRO_CMD\n" ); |
327 | return err == 0 ? -EINVAL : err; |
328 | } |
329 | |
330 | return 0; |
331 | } |
332 | |
333 | |
334 | /* firmware base addresses (when hard coded) */ |
335 | #define MIXART_MOTHERBOARD_XLX_BASE_ADDRESS 0x00600000 |
336 | |
337 | static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmware *dsp) |
338 | { |
339 | int err, card_index; |
340 | u32 status_xilinx, status_elf, status_daught; |
341 | u32 val; |
342 | |
343 | /* read motherboard xilinx status */ |
344 | status_xilinx = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_STATUS_OFFSET )); |
345 | /* read elf status */ |
346 | status_elf = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_ELF_STATUS_OFFSET )); |
347 | /* read daughterboard xilinx status */ |
348 | status_daught = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_DXLX_STATUS_OFFSET )); |
349 | |
350 | /* motherboard xilinx status 5 will say that the board is performing a reset */ |
351 | if (status_xilinx == 5) { |
352 | dev_err(&mgr->pci->dev, "miXart is resetting !\n" ); |
353 | return -EAGAIN; /* try again later */ |
354 | } |
355 | |
356 | switch (index) { |
357 | case MIXART_MOTHERBOARD_XLX_INDEX: |
358 | |
359 | /* xilinx already loaded ? */ |
360 | if (status_xilinx == 4) { |
361 | dev_dbg(&mgr->pci->dev, "xilinx is already loaded !\n" ); |
362 | return 0; |
363 | } |
364 | /* the status should be 0 == "idle" */ |
365 | if (status_xilinx != 0) { |
366 | dev_err(&mgr->pci->dev, |
367 | "xilinx load error ! status = %d\n" , |
368 | status_xilinx); |
369 | return -EIO; /* modprob -r may help ? */ |
370 | } |
371 | |
372 | /* check xilinx validity */ |
373 | if (((u32*)(dsp->data))[0] == 0xffffffff) |
374 | return -EINVAL; |
375 | if (dsp->size % 4) |
376 | return -EINVAL; |
377 | |
378 | /* set xilinx status to copying */ |
379 | writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET )); |
380 | |
381 | /* setup xilinx base address */ |
382 | writel_be( MIXART_MOTHERBOARD_XLX_BASE_ADDRESS, MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET )); |
383 | /* setup code size for xilinx file */ |
384 | writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_SIZE_OFFSET )); |
385 | |
386 | /* copy xilinx code */ |
387 | memcpy_toio( MIXART_MEM( mgr, MIXART_MOTHERBOARD_XLX_BASE_ADDRESS), dsp->data, dsp->size); |
388 | |
389 | /* set xilinx status to copy finished */ |
390 | writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET )); |
391 | |
392 | /* return, because no further processing needed */ |
393 | return 0; |
394 | |
395 | case MIXART_MOTHERBOARD_ELF_INDEX: |
396 | |
397 | if (status_elf == 4) { |
398 | dev_dbg(&mgr->pci->dev, "elf file already loaded !\n" ); |
399 | return 0; |
400 | } |
401 | |
402 | /* the status should be 0 == "idle" */ |
403 | if (status_elf != 0) { |
404 | dev_err(&mgr->pci->dev, |
405 | "elf load error ! status = %d\n" , |
406 | status_elf); |
407 | return -EIO; /* modprob -r may help ? */ |
408 | } |
409 | |
410 | /* wait for xilinx status == 4 */ |
411 | err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET, is_egal: 1, value: 4, timeout: 500); /* 5sec */ |
412 | if (err < 0) { |
413 | dev_err(&mgr->pci->dev, "xilinx was not loaded or " |
414 | "could not be started\n" ); |
415 | return err; |
416 | } |
417 | |
418 | /* init some data on the card */ |
419 | writel_be( 0, MIXART_MEM( mgr, MIXART_PSEUDOREG_BOARDNUMBER ) ); /* set miXart boardnumber to 0 */ |
420 | writel_be( 0, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* reset pointer to flow table on miXart */ |
421 | |
422 | /* set elf status to copying */ |
423 | writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET )); |
424 | |
425 | /* process the copying of the elf packets */ |
426 | err = mixart_load_elf( mgr, dsp ); |
427 | if (err < 0) return err; |
428 | |
429 | /* set elf status to copy finished */ |
430 | writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET )); |
431 | |
432 | /* wait for elf status == 4 */ |
433 | err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET, is_egal: 1, value: 4, timeout: 300); /* 3sec */ |
434 | if (err < 0) { |
435 | dev_err(&mgr->pci->dev, "elf could not be started\n" ); |
436 | return err; |
437 | } |
438 | |
439 | /* miXart waits at this point on the pointer to the flow table */ |
440 | writel_be( (u32)mgr->flowinfo.addr, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* give pointer of flow table to miXart */ |
441 | |
442 | return 0; /* return, another xilinx file has to be loaded before */ |
443 | |
444 | case MIXART_AESEBUBOARD_XLX_INDEX: |
445 | default: |
446 | |
447 | /* elf and xilinx should be loaded */ |
448 | if (status_elf != 4 || status_xilinx != 4) { |
449 | dev_err(&mgr->pci->dev, "xilinx or elf not " |
450 | "successfully loaded\n" ); |
451 | return -EIO; /* modprob -r may help ? */ |
452 | } |
453 | |
454 | /* wait for daughter detection != 0 */ |
455 | err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET, is_egal: 0, value: 0, timeout: 30); /* 300msec */ |
456 | if (err < 0) { |
457 | dev_err(&mgr->pci->dev, "error starting elf file\n" ); |
458 | return err; |
459 | } |
460 | |
461 | /* the board type can now be retrieved */ |
462 | mgr->board_type = (DAUGHTER_TYPE_MASK & readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DBRD_TYPE_OFFSET))); |
463 | |
464 | if (mgr->board_type == MIXART_DAUGHTER_TYPE_NONE) |
465 | break; /* no daughter board; the file does not have to be loaded, continue after the switch */ |
466 | |
467 | /* only if aesebu daughter board presence (elf code must run) */ |
468 | if (mgr->board_type != MIXART_DAUGHTER_TYPE_AES ) |
469 | return -EINVAL; |
470 | |
471 | /* daughter should be idle */ |
472 | if (status_daught != 0) { |
473 | dev_err(&mgr->pci->dev, |
474 | "daughter load error ! status = %d\n" , |
475 | status_daught); |
476 | return -EIO; /* modprob -r may help ? */ |
477 | } |
478 | |
479 | /* check daughterboard xilinx validity */ |
480 | if (((u32*)(dsp->data))[0] == 0xffffffff) |
481 | return -EINVAL; |
482 | if (dsp->size % 4) |
483 | return -EINVAL; |
484 | |
485 | /* inform mixart about the size of the file */ |
486 | writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET )); |
487 | |
488 | /* set daughterboard status to 1 */ |
489 | writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET )); |
490 | |
491 | /* wait for status == 2 */ |
492 | err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, is_egal: 1, value: 2, timeout: 30); /* 300msec */ |
493 | if (err < 0) { |
494 | dev_err(&mgr->pci->dev, "daughter board load error\n" ); |
495 | return err; |
496 | } |
497 | |
498 | /* get the address where to write the file */ |
499 | val = readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET )); |
500 | if (!val) |
501 | return -EINVAL; |
502 | |
503 | /* copy daughterboard xilinx code */ |
504 | memcpy_toio( MIXART_MEM( mgr, val), dsp->data, dsp->size); |
505 | |
506 | /* set daughterboard status to 4 */ |
507 | writel_be( 4, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET )); |
508 | |
509 | /* continue with init */ |
510 | break; |
511 | } /* end of switch file index*/ |
512 | |
513 | /* wait for daughter status == 3 */ |
514 | err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, is_egal: 1, value: 3, timeout: 300); /* 3sec */ |
515 | if (err < 0) { |
516 | dev_err(&mgr->pci->dev, |
517 | "daughter board could not be initialised\n" ); |
518 | return err; |
519 | } |
520 | |
521 | /* init mailbox (communication with embedded) */ |
522 | snd_mixart_init_mailbox(mgr); |
523 | |
524 | /* first communication with embedded */ |
525 | err = mixart_first_init(mgr); |
526 | if (err < 0) { |
527 | dev_err(&mgr->pci->dev, "miXart could not be set up\n" ); |
528 | return err; |
529 | } |
530 | |
531 | /* create devices and mixer in accordance with HW options*/ |
532 | for (card_index = 0; card_index < mgr->num_cards; card_index++) { |
533 | struct snd_mixart *chip = mgr->chip[card_index]; |
534 | |
535 | err = snd_mixart_create_pcm(chip); |
536 | if (err < 0) |
537 | return err; |
538 | |
539 | if (card_index == 0) { |
540 | err = snd_mixart_create_mixer(mgr: chip->mgr); |
541 | if (err < 0) |
542 | return err; |
543 | } |
544 | |
545 | err = snd_card_register(card: chip->card); |
546 | if (err < 0) |
547 | return err; |
548 | } |
549 | |
550 | dev_dbg(&mgr->pci->dev, |
551 | "miXart firmware downloaded and successfully set up\n" ); |
552 | |
553 | return 0; |
554 | } |
555 | |
556 | |
557 | int snd_mixart_setup_firmware(struct mixart_mgr *mgr) |
558 | { |
559 | static const char * const fw_files[3] = { |
560 | "miXart8.xlx" , "miXart8.elf" , "miXart8AES.xlx" |
561 | }; |
562 | char path[32]; |
563 | |
564 | const struct firmware *fw_entry; |
565 | int i, err; |
566 | |
567 | for (i = 0; i < 3; i++) { |
568 | sprintf(buf: path, fmt: "mixart/%s" , fw_files[i]); |
569 | if (request_firmware(fw: &fw_entry, name: path, device: &mgr->pci->dev)) { |
570 | dev_err(&mgr->pci->dev, |
571 | "miXart: can't load firmware %s\n" , path); |
572 | return -ENOENT; |
573 | } |
574 | /* fake hwdep dsp record */ |
575 | err = mixart_dsp_load(mgr, index: i, dsp: fw_entry); |
576 | release_firmware(fw: fw_entry); |
577 | if (err < 0) |
578 | return err; |
579 | mgr->dsp_loaded |= 1 << i; |
580 | } |
581 | return 0; |
582 | } |
583 | |
584 | MODULE_FIRMWARE("mixart/miXart8.xlx" ); |
585 | MODULE_FIRMWARE("mixart/miXart8.elf" ); |
586 | MODULE_FIRMWARE("mixart/miXart8AES.xlx" ); |
587 | |