2 * Driver for Digigram miXart soundcards
6 * Copyright (c) 2003 by Digigram <alsa@digigram.com>
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.
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.
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
23 #include <sound/driver.h>
24 #include <linux/interrupt.h>
26 #include <sound/core.h>
28 #include "mixart_mixer.h"
29 #include "mixart_core.h"
30 #include "mixart_hwdep.h"
33 /* miXart hwdep interface id string */
34 #define SND_MIXART_HWDEP_ID "miXart Loader"
36 static int mixart_hwdep_open(snd_hwdep_t
*hw
, struct file
*file
)
41 static int mixart_hwdep_release(snd_hwdep_t
*hw
, struct file
*file
)
47 * wait for a value on a peudo register, exit with a timeout
49 * @param mgr pointer to miXart manager structure
50 * @param offset unsigned pseudo_register base + offset of value
52 * @param timeout timeout in centisenconds
54 static int mixart_wait_nice_for_register_value(mixart_mgr_t
*mgr
, u32 offset
, int is_egal
, u32 value
, unsigned long timeout
)
56 unsigned long end_time
= jiffies
+ (timeout
* HZ
/ 100);
59 do { /* we may take too long time in this loop.
60 * so give controls back to kernel if needed.
64 read
= readl_be( MIXART_MEM( mgr
, offset
));
66 if(read
== value
) return 0;
68 else { /* wait for different value */
69 if(read
!= value
) return 0;
71 } while ( time_after_eq(end_time
, jiffies
) );
78 structures needed to upload elf code packets
80 typedef struct snd_mixart_elf32_ehdr snd_mixart_elf32_ehdr_t
;
82 struct snd_mixart_elf32_ehdr
{
99 typedef struct snd_mixart_elf32_phdr snd_mixart_elf32_phdr_t
;
101 struct snd_mixart_elf32_phdr
{
112 static int mixart_load_elf(mixart_mgr_t
*mgr
, snd_hwdep_dsp_image_t
*dsp
)
114 char elf32_magic_number
[4] = {0x7f,'E','L','F'};
115 snd_mixart_elf32_ehdr_t elf_header
;
118 if ( copy_from_user(&elf_header
, dsp
->image
, sizeof(snd_mixart_elf32_ehdr_t
)) )
122 if ( elf32_magic_number
[i
] != elf_header
.e_ident
[i
] )
125 if( elf_header
.e_phoff
!= 0 ) {
126 snd_mixart_elf32_phdr_t elf_programheader
;
128 for( i
=0; i
< be16_to_cpu(elf_header
.e_phnum
); i
++ ) {
129 u32 pos
= be32_to_cpu(elf_header
.e_phoff
) + (u32
)(i
* be16_to_cpu(elf_header
.e_phentsize
));
131 if( copy_from_user( &elf_programheader
, dsp
->image
+ pos
, sizeof(elf_programheader
) ) )
134 if(elf_programheader
.p_type
!= 0) {
135 if( elf_programheader
.p_filesz
!= 0 ) {
136 if(copy_from_user_toio( MIXART_MEM( mgr
, be32_to_cpu(elf_programheader
.p_vaddr
)),
137 dsp
->image
+ be32_to_cpu( elf_programheader
.p_offset
),
138 be32_to_cpu( elf_programheader
.p_filesz
)))
147 static int mixart_hwdep_dsp_status(snd_hwdep_t
*hw
, snd_hwdep_dsp_status_t
*info
)
149 mixart_mgr_t
*mgr
= hw
->private_data
;
151 strcpy(info
->id
, "miXart");
152 info
->num_dsps
= MIXART_HARDW_FILES_MAX_INDEX
;
154 if (mgr
->hwdep
->dsp_loaded
& (1 << MIXART_MOTHERBOARD_ELF_INDEX
))
155 info
->chip_ready
= 1;
157 info
->version
= MIXART_DRIVER_VERSION
;
162 * get basic information and init miXart
165 /* audio IDs for request to the board */
166 #define MIXART_FIRST_ANA_AUDIO_ID 0
167 #define MIXART_FIRST_DIG_AUDIO_ID 8
169 static int mixart_enum_connectors(mixart_mgr_t
*mgr
)
173 mixart_msg_t request
;
174 mixart_enum_connector_resp_t connector
;
175 mixart_audio_info_req_t audio_info_req
;
176 mixart_audio_info_resp_t audio_info
;
178 audio_info_req
.line_max_level
= MIXART_FLOAT_P_22_0_TO_HEX
;
179 audio_info_req
.micro_max_level
= MIXART_FLOAT_M_20_0_TO_HEX
;
180 audio_info_req
.cd_max_level
= MIXART_FLOAT____0_0_TO_HEX
;
182 request
.message_id
= MSG_SYSTEM_ENUM_PLAY_CONNECTOR
;
183 request
.uid
= (mixart_uid_t
){0,0}; /* board num = 0 */
187 err
= snd_mixart_send_msg(mgr
, &request
, sizeof(connector
), &connector
);
188 if((err
< 0) || (connector
.error_code
) || (connector
.uid_count
> MIXART_MAX_PHYS_CONNECTORS
)) {
189 snd_printk(KERN_ERR
"error MSG_SYSTEM_ENUM_PLAY_CONNECTOR\n");
193 for(k
=0; k
< connector
.uid_count
; k
++) {
196 if(k
< MIXART_FIRST_DIG_AUDIO_ID
) {
197 pipe
= &mgr
->chip
[k
/2]->pipe_out_ana
;
199 pipe
= &mgr
->chip
[(k
-MIXART_FIRST_DIG_AUDIO_ID
)/2]->pipe_out_dig
;
202 pipe
->uid_right_connector
= connector
.uid
[k
]; /* odd */
204 pipe
->uid_left_connector
= connector
.uid
[k
]; /* even */
207 /* snd_printk(KERN_DEBUG "playback connector[%d].object_id = %x\n", k, connector.uid[k].object_id); */
209 /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
210 request
.message_id
= MSG_CONNECTOR_GET_AUDIO_INFO
;
211 request
.uid
= connector
.uid
[k
];
212 request
.data
= &audio_info_req
;
213 request
.size
= sizeof(audio_info_req
);
215 err
= snd_mixart_send_msg(mgr
, &request
, sizeof(audio_info
), &audio_info
);
217 snd_printk(KERN_ERR
"error MSG_CONNECTOR_GET_AUDIO_INFO\n");
220 /*snd_printk(KERN_DEBUG "play analog_info.analog_level_present = %x\n", audio_info.info.analog_info.analog_level_present);*/
223 request
.message_id
= MSG_SYSTEM_ENUM_RECORD_CONNECTOR
;
224 request
.uid
= (mixart_uid_t
){0,0}; /* board num = 0 */
228 err
= snd_mixart_send_msg(mgr
, &request
, sizeof(connector
), &connector
);
229 if((err
< 0) || (connector
.error_code
) || (connector
.uid_count
> MIXART_MAX_PHYS_CONNECTORS
)) {
230 snd_printk(KERN_ERR
"error MSG_SYSTEM_ENUM_RECORD_CONNECTOR\n");
234 for(k
=0; k
< connector
.uid_count
; k
++) {
237 if(k
< MIXART_FIRST_DIG_AUDIO_ID
) {
238 pipe
= &mgr
->chip
[k
/2]->pipe_in_ana
;
240 pipe
= &mgr
->chip
[(k
-MIXART_FIRST_DIG_AUDIO_ID
)/2]->pipe_in_dig
;
243 pipe
->uid_right_connector
= connector
.uid
[k
]; /* odd */
245 pipe
->uid_left_connector
= connector
.uid
[k
]; /* even */
248 /* snd_printk(KERN_DEBUG "capture connector[%d].object_id = %x\n", k, connector.uid[k].object_id); */
250 /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
251 request
.message_id
= MSG_CONNECTOR_GET_AUDIO_INFO
;
252 request
.uid
= connector
.uid
[k
];
253 request
.data
= &audio_info_req
;
254 request
.size
= sizeof(audio_info_req
);
256 err
= snd_mixart_send_msg(mgr
, &request
, sizeof(audio_info
), &audio_info
);
258 snd_printk(KERN_ERR
"error MSG_CONNECTOR_GET_AUDIO_INFO\n");
261 /*snd_printk(KERN_DEBUG "rec analog_info.analog_level_present = %x\n", audio_info.info.analog_info.analog_level_present);*/
267 static int mixart_enum_physio(mixart_mgr_t
*mgr
)
271 mixart_msg_t request
;
272 mixart_uid_t get_console_mgr
;
273 mixart_return_uid_t console_mgr
;
274 mixart_uid_enumeration_t phys_io
;
276 /* get the uid for the console manager */
277 get_console_mgr
.object_id
= 0;
278 get_console_mgr
.desc
= MSG_CONSOLE_MANAGER
| 0; /* cardindex = 0 */
280 request
.message_id
= MSG_CONSOLE_GET_CLOCK_UID
;
281 request
.uid
= get_console_mgr
;
282 request
.data
= &get_console_mgr
;
283 request
.size
= sizeof(get_console_mgr
);
285 err
= snd_mixart_send_msg(mgr
, &request
, sizeof(console_mgr
), &console_mgr
);
287 if( (err
< 0) || (console_mgr
.error_code
!= 0) ) {
288 snd_printk(KERN_DEBUG
"error MSG_CONSOLE_GET_CLOCK_UID : err=%x\n", console_mgr
.error_code
);
292 /* used later for clock issues ! */
293 mgr
->uid_console_manager
= console_mgr
.uid
;
295 request
.message_id
= MSG_SYSTEM_ENUM_PHYSICAL_IO
;
296 request
.uid
= (mixart_uid_t
){0,0};
297 request
.data
= &console_mgr
.uid
;
298 request
.size
= sizeof(console_mgr
.uid
);
300 err
= snd_mixart_send_msg(mgr
, &request
, sizeof(phys_io
), &phys_io
);
301 if( (err
< 0) || ( phys_io
.error_code
!= 0 ) ) {
302 snd_printk(KERN_ERR
"error MSG_SYSTEM_ENUM_PHYSICAL_IO err(%x) error_code(%x)\n", err
, phys_io
.error_code
);
306 snd_assert(phys_io
.nb_uid
>= (MIXART_MAX_CARDS
* 2), return -EINVAL
); /* min 2 phys io per card (analog in + analog out) */
308 for(k
=0; k
<mgr
->num_cards
; k
++) {
309 mgr
->chip
[k
]->uid_in_analog_physio
= phys_io
.uid
[k
];
310 mgr
->chip
[k
]->uid_out_analog_physio
= phys_io
.uid
[phys_io
.nb_uid
/2 + k
];
317 static int mixart_first_init(mixart_mgr_t
*mgr
)
321 mixart_msg_t request
;
323 if((err
= mixart_enum_connectors(mgr
)) < 0) return err
;
325 if((err
= mixart_enum_physio(mgr
)) < 0) return err
;
327 /* send a synchro command to card (necessary to do this before first MSG_STREAM_START_STREAM_GRP_PACKET) */
328 /* though why not here */
329 request
.message_id
= MSG_SYSTEM_SEND_SYNCHRO_CMD
;
330 request
.uid
= (mixart_uid_t
){0,0};
333 /* this command has no data. response is a 32 bit status */
334 err
= snd_mixart_send_msg(mgr
, &request
, sizeof(k
), &k
);
335 if( (err
< 0) || (k
!= 0) ) {
336 snd_printk(KERN_ERR
"error MSG_SYSTEM_SEND_SYNCHRO_CMD\n");
337 return err
== 0 ? -EINVAL
: err
;
344 /* firmware base addresses (when hard coded) */
345 #define MIXART_MOTHERBOARD_XLX_BASE_ADDRESS 0x00600000
347 static int mixart_hwdep_dsp_load(snd_hwdep_t
*hw
, snd_hwdep_dsp_image_t
*dsp
)
349 mixart_mgr_t
* mgr
= hw
->private_data
;
351 u32 status_xilinx
, status_elf
, status_daught
;
354 /* read motherboard xilinx status */
355 status_xilinx
= readl_be( MIXART_MEM( mgr
,MIXART_PSEUDOREG_MXLX_STATUS_OFFSET
));
356 /* read elf status */
357 status_elf
= readl_be( MIXART_MEM( mgr
,MIXART_PSEUDOREG_ELF_STATUS_OFFSET
));
358 /* read daughterboard xilinx status */
359 status_daught
= readl_be( MIXART_MEM( mgr
,MIXART_PSEUDOREG_DXLX_STATUS_OFFSET
));
361 /* motherboard xilinx status 5 will say that the board is performing a reset */
362 if( status_xilinx
== 5 ) {
363 snd_printk( KERN_ERR
"miXart is resetting !\n");
364 return -EAGAIN
; /* try again later */
367 switch (dsp
->index
) {
368 case MIXART_MOTHERBOARD_XLX_INDEX
:
370 /* xilinx already loaded ? */
371 if( status_xilinx
== 4 ) {
372 snd_printk( KERN_DEBUG
"xilinx is already loaded !\n");
375 /* the status should be 0 == "idle" */
376 if( status_xilinx
!= 0 ) {
377 snd_printk( KERN_ERR
"xilinx load error ! status = %d\n", status_xilinx
);
378 return -EIO
; /* modprob -r may help ? */
381 /* check xilinx validity */
382 snd_assert(((u32
*)(dsp
->image
))[0]==0xFFFFFFFF, return -EINVAL
);
383 snd_assert(dsp
->length
% 4 == 0, return -EINVAL
);
385 /* set xilinx status to copying */
386 writel_be( 1, MIXART_MEM( mgr
, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET
));
388 /* setup xilinx base address */
389 writel_be( MIXART_MOTHERBOARD_XLX_BASE_ADDRESS
, MIXART_MEM( mgr
,MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET
));
390 /* setup code size for xilinx file */
391 writel_be( dsp
->length
, MIXART_MEM( mgr
, MIXART_PSEUDOREG_MXLX_SIZE_OFFSET
));
393 /* copy xilinx code */
394 if (copy_from_user_toio( MIXART_MEM( mgr
, MIXART_MOTHERBOARD_XLX_BASE_ADDRESS
), dsp
->image
, dsp
->length
))
397 /* set xilinx status to copy finished */
398 writel_be( 2, MIXART_MEM( mgr
, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET
));
400 /* return, because no further processing needed */
403 case MIXART_MOTHERBOARD_ELF_INDEX
:
405 if( status_elf
== 4 ) {
406 snd_printk( KERN_DEBUG
"elf file already loaded !\n");
410 /* the status should be 0 == "idle" */
411 if( status_elf
!= 0 ) {
412 snd_printk( KERN_ERR
"elf load error ! status = %d\n", status_elf
);
413 return -EIO
; /* modprob -r may help ? */
416 /* wait for xilinx status == 4 */
417 err
= mixart_wait_nice_for_register_value( mgr
, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET
, 1, 4, 500); /* 5sec */
419 snd_printk( KERN_ERR
"xilinx was not loaded or could not be started\n");
423 /* init some data on the card */
424 writel_be( 0, MIXART_MEM( mgr
, MIXART_PSEUDOREG_BOARDNUMBER
) ); /* set miXart boardnumber to 0 */
425 writel_be( 0, MIXART_MEM( mgr
, MIXART_FLOWTABLE_PTR
) ); /* reset pointer to flow table on miXart */
427 /* set elf status to copying */
428 writel_be( 1, MIXART_MEM( mgr
, MIXART_PSEUDOREG_ELF_STATUS_OFFSET
));
430 /* process the copying of the elf packets */
431 err
= mixart_load_elf( mgr
, dsp
);
432 if (err
< 0) return err
;
434 /* set elf status to copy finished */
435 writel_be( 2, MIXART_MEM( mgr
, MIXART_PSEUDOREG_ELF_STATUS_OFFSET
));
437 /* wait for elf status == 4 */
438 err
= mixart_wait_nice_for_register_value( mgr
, MIXART_PSEUDOREG_ELF_STATUS_OFFSET
, 1, 4, 300); /* 3sec */
440 snd_printk( KERN_ERR
"elf could not be started\n");
444 /* miXart waits at this point on the pointer to the flow table */
445 writel_be( (u32
)mgr
->flowinfo
.addr
, MIXART_MEM( mgr
, MIXART_FLOWTABLE_PTR
) ); /* give pointer of flow table to miXart */
447 return 0; /* return, another xilinx file has to be loaded before */
449 case MIXART_AESEBUBOARD_XLX_INDEX
:
452 /* elf and xilinx should be loaded */
453 if( (status_elf
!= 4) || (status_xilinx
!= 4) ) {
454 printk( KERN_ERR
"xilinx or elf not successfully loaded\n");
455 return -EIO
; /* modprob -r may help ? */
458 /* wait for daughter detection != 0 */
459 err
= mixart_wait_nice_for_register_value( mgr
, MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET
, 0, 0, 30); /* 300msec */
461 snd_printk( KERN_ERR
"error starting elf file\n");
465 /* the board type can now be retrieved */
466 mgr
->board_type
= (DAUGHTER_TYPE_MASK
& readl_be( MIXART_MEM( mgr
, MIXART_PSEUDOREG_DBRD_TYPE_OFFSET
)));
468 if (mgr
->board_type
== MIXART_DAUGHTER_TYPE_NONE
)
469 break; /* no daughter board; the file does not have to be loaded, continue after the switch */
471 /* only if aesebu daughter board presence (elf code must run) */
472 if (mgr
->board_type
!= MIXART_DAUGHTER_TYPE_AES
)
475 /* daughter should be idle */
476 if( status_daught
!= 0 ) {
477 printk( KERN_ERR
"daughter load error ! status = %d\n", status_daught
);
478 return -EIO
; /* modprob -r may help ? */
481 /* check daughterboard xilinx validity */
482 snd_assert(((u32
*)(dsp
->image
))[0]==0xFFFFFFFF, return -EINVAL
);
483 snd_assert(dsp
->length
% 4 == 0, return -EINVAL
);
485 /* inform mixart about the size of the file */
486 writel_be( dsp
->length
, MIXART_MEM( mgr
, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET
));
488 /* set daughterboard status to 1 */
489 writel_be( 1, MIXART_MEM( mgr
, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET
));
491 /* wait for status == 2 */
492 err
= mixart_wait_nice_for_register_value( mgr
, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET
, 1, 2, 30); /* 300msec */
494 snd_printk( KERN_ERR
"daughter board load error\n");
498 /* get the address where to write the file */
499 val
= readl_be( MIXART_MEM( mgr
, MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET
));
500 snd_assert(val
!= 0, return -EINVAL
);
502 /* copy daughterboard xilinx code */
503 if (copy_from_user_toio( MIXART_MEM( mgr
, val
), dsp
->image
, dsp
->length
))
506 /* set daughterboard status to 4 */
507 writel_be( 4, MIXART_MEM( mgr
, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET
));
509 /* continue with init */
511 } /* end of switch file index*/
513 /* wait for daughter status == 3 */
514 err
= mixart_wait_nice_for_register_value( mgr
, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET
, 1, 3, 300); /* 3sec */
516 snd_printk( KERN_ERR
"daughter board could not be initialised\n");
520 /* init mailbox (communication with embedded) */
521 snd_mixart_init_mailbox(mgr
);
523 /* first communication with embedded */
524 err
= mixart_first_init(mgr
);
526 snd_printk( KERN_ERR
"miXart could not be set up\n");
530 /* create devices and mixer in accordance with HW options*/
531 for (card_index
= 0; card_index
< mgr
->num_cards
; card_index
++) {
532 mixart_t
*chip
= mgr
->chip
[card_index
];
534 if ((err
= snd_mixart_create_pcm(chip
)) < 0)
537 if (card_index
== 0) {
538 if ((err
= snd_mixart_create_mixer(chip
->mgr
)) < 0)
542 if ((err
= snd_card_register(chip
->card
)) < 0)
546 snd_printdd("miXart firmware downloaded and successfully set up\n");
552 int snd_mixart_hwdep_new(mixart_mgr_t
*mgr
)
557 /* only create hwdep interface for first cardX (see "index" module parameter)*/
558 if ((err
= snd_hwdep_new(mgr
->chip
[0]->card
, SND_MIXART_HWDEP_ID
, 0, &hw
)) < 0)
561 hw
->iface
= SNDRV_HWDEP_IFACE_MIXART
;
562 hw
->private_data
= mgr
;
563 hw
->ops
.open
= mixart_hwdep_open
;
564 hw
->ops
.release
= mixart_hwdep_release
;
565 hw
->ops
.dsp_status
= mixart_hwdep_dsp_status
;
566 hw
->ops
.dsp_load
= mixart_hwdep_dsp_load
;
568 sprintf(hw
->name
, SND_MIXART_HWDEP_ID
);
570 mgr
->hwdep
->dsp_loaded
= 0;