Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
mixart_hwdep.c
Go to the documentation of this file.
1 /*
2  * Driver for Digigram miXart soundcards
3  *
4  * DSP firmware management
5  *
6  * Copyright (c) 2003 by Digigram <[email protected]>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  */
22 
23 #include <linux/interrupt.h>
24 #include <linux/pci.h>
25 #include <linux/firmware.h>
26 #include <linux/vmalloc.h>
27 #include <linux/slab.h>
28 #include <linux/module.h>
29 #include <asm/io.h>
30 #include <sound/core.h>
31 #include "mixart.h"
32 #include "mixart_mixer.h"
33 #include "mixart_core.h"
34 #include "mixart_hwdep.h"
35 
36 
45 static int mixart_wait_nice_for_register_value(struct mixart_mgr *mgr,
46  u32 offset, int is_egal,
47  u32 value, unsigned long timeout)
48 {
49  unsigned long end_time = jiffies + (timeout * HZ / 100);
50  u32 read;
51 
52  do { /* we may take too long time in this loop.
53  * so give controls back to kernel if needed.
54  */
55  cond_resched();
56 
57  read = readl_be( MIXART_MEM( mgr, offset ));
58  if(is_egal) {
59  if(read == value) return 0;
60  }
61  else { /* wait for different value */
62  if(read != value) return 0;
63  }
64  } while ( time_after_eq(end_time, jiffies) );
65 
66  return -EBUSY;
67 }
68 
69 
70 /*
71  structures needed to upload elf code packets
72  */
74  u8 e_ident[16];
88 };
89 
99 };
100 
101 static int mixart_load_elf(struct mixart_mgr *mgr, const struct firmware *dsp )
102 {
103  char elf32_magic_number[4] = {0x7f,'E','L','F'};
104  struct snd_mixart_elf32_ehdr *elf_header;
105  int i;
106 
107  elf_header = (struct snd_mixart_elf32_ehdr *)dsp->data;
108  for( i=0; i<4; i++ )
109  if ( elf32_magic_number[i] != elf_header->e_ident[i] )
110  return -EINVAL;
111 
112  if( elf_header->e_phoff != 0 ) {
113  struct snd_mixart_elf32_phdr elf_programheader;
114 
115  for( i=0; i < be16_to_cpu(elf_header->e_phnum); i++ ) {
116  u32 pos = be32_to_cpu(elf_header->e_phoff) + (u32)(i * be16_to_cpu(elf_header->e_phentsize));
117 
118  memcpy( &elf_programheader, dsp->data + pos, sizeof(elf_programheader) );
119 
120  if(elf_programheader.p_type != 0) {
121  if( elf_programheader.p_filesz != 0 ) {
122  memcpy_toio( MIXART_MEM( mgr, be32_to_cpu(elf_programheader.p_vaddr)),
123  dsp->data + be32_to_cpu( elf_programheader.p_offset ),
124  be32_to_cpu( elf_programheader.p_filesz ));
125  }
126  }
127  }
128  }
129  return 0;
130 }
131 
132 /*
133  * get basic information and init miXart
134  */
135 
136 /* audio IDs for request to the board */
137 #define MIXART_FIRST_ANA_AUDIO_ID 0
138 #define MIXART_FIRST_DIG_AUDIO_ID 8
139 
140 static int mixart_enum_connectors(struct mixart_mgr *mgr)
141 {
142  u32 k;
143  int err;
144  struct mixart_msg request;
146  struct mixart_audio_info_req *audio_info_req;
147  struct mixart_audio_info_resp *audio_info;
148 
149  connector = kmalloc(sizeof(*connector), GFP_KERNEL);
150  audio_info_req = kmalloc(sizeof(*audio_info_req), GFP_KERNEL);
151  audio_info = kmalloc(sizeof(*audio_info), GFP_KERNEL);
152  if (! connector || ! audio_info_req || ! audio_info) {
153  err = -ENOMEM;
154  goto __error;
155  }
156 
157  audio_info_req->line_max_level = MIXART_FLOAT_P_22_0_TO_HEX;
159  audio_info_req->cd_max_level = MIXART_FLOAT____0_0_TO_HEX;
160 
162  request.uid = (struct mixart_uid){0,0}; /* board num = 0 */
163  request.data = NULL;
164  request.size = 0;
165 
166  err = snd_mixart_send_msg(mgr, &request, sizeof(*connector), connector);
167  if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) {
168  snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_PLAY_CONNECTOR\n");
169  err = -EINVAL;
170  goto __error;
171  }
172 
173  for(k=0; k < connector->uid_count; k++) {
174  struct mixart_pipe *pipe;
175 
176  if(k < MIXART_FIRST_DIG_AUDIO_ID) {
177  pipe = &mgr->chip[k/2]->pipe_out_ana;
178  } else {
179  pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_out_dig;
180  }
181  if(k & 1) {
182  pipe->uid_right_connector = connector->uid[k]; /* odd */
183  } else {
184  pipe->uid_left_connector = connector->uid[k]; /* even */
185  }
186 
187  /* snd_printk(KERN_DEBUG "playback connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */
188 
189  /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
191  request.uid = connector->uid[k];
192  request.data = audio_info_req;
193  request.size = sizeof(*audio_info_req);
194 
195  err = snd_mixart_send_msg(mgr, &request, sizeof(*audio_info), audio_info);
196  if( err < 0 ) {
197  snd_printk(KERN_ERR "error MSG_CONNECTOR_GET_AUDIO_INFO\n");
198  goto __error;
199  }
200  /*snd_printk(KERN_DEBUG "play analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/
201  }
202 
204  request.uid = (struct mixart_uid){0,0}; /* board num = 0 */
205  request.data = NULL;
206  request.size = 0;
207 
208  err = snd_mixart_send_msg(mgr, &request, sizeof(*connector), connector);
209  if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) {
210  snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_RECORD_CONNECTOR\n");
211  err = -EINVAL;
212  goto __error;
213  }
214 
215  for(k=0; k < connector->uid_count; k++) {
216  struct mixart_pipe *pipe;
217 
218  if(k < MIXART_FIRST_DIG_AUDIO_ID) {
219  pipe = &mgr->chip[k/2]->pipe_in_ana;
220  } else {
221  pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_in_dig;
222  }
223  if(k & 1) {
224  pipe->uid_right_connector = connector->uid[k]; /* odd */
225  } else {
226  pipe->uid_left_connector = connector->uid[k]; /* even */
227  }
228 
229  /* snd_printk(KERN_DEBUG "capture connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */
230 
231  /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
233  request.uid = connector->uid[k];
234  request.data = audio_info_req;
235  request.size = sizeof(*audio_info_req);
236 
237  err = snd_mixart_send_msg(mgr, &request, sizeof(*audio_info), audio_info);
238  if( err < 0 ) {
239  snd_printk(KERN_ERR "error MSG_CONNECTOR_GET_AUDIO_INFO\n");
240  goto __error;
241  }
242  /*snd_printk(KERN_DEBUG "rec analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/
243  }
244  err = 0;
245 
246  __error:
247  kfree(connector);
248  kfree(audio_info_req);
249  kfree(audio_info);
250 
251  return err;
252 }
253 
254 static int mixart_enum_physio(struct mixart_mgr *mgr)
255 {
256  u32 k;
257  int err;
258  struct mixart_msg request;
259  struct mixart_uid get_console_mgr;
260  struct mixart_return_uid console_mgr;
261  struct mixart_uid_enumeration phys_io;
262 
263  /* get the uid for the console manager */
264  get_console_mgr.object_id = 0;
265  get_console_mgr.desc = MSG_CONSOLE_MANAGER | 0; /* cardindex = 0 */
266 
267  request.message_id = MSG_CONSOLE_GET_CLOCK_UID;
268  request.uid = get_console_mgr;
269  request.data = &get_console_mgr;
270  request.size = sizeof(get_console_mgr);
271 
272  err = snd_mixart_send_msg(mgr, &request, sizeof(console_mgr), &console_mgr);
273 
274  if( (err < 0) || (console_mgr.error_code != 0) ) {
275  snd_printk(KERN_DEBUG "error MSG_CONSOLE_GET_CLOCK_UID : err=%x\n", console_mgr.error_code);
276  return -EINVAL;
277  }
278 
279  /* used later for clock issues ! */
280  mgr->uid_console_manager = console_mgr.uid;
281 
283  request.uid = (struct mixart_uid){0,0};
284  request.data = &console_mgr.uid;
285  request.size = sizeof(console_mgr.uid);
286 
287  err = snd_mixart_send_msg(mgr, &request, sizeof(phys_io), &phys_io);
288  if( (err < 0) || ( phys_io.error_code != 0 ) ) {
289  snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_PHYSICAL_IO err(%x) error_code(%x)\n", err, phys_io.error_code );
290  return -EINVAL;
291  }
292 
293  /* min 2 phys io per card (analog in + analog out) */
294  if (phys_io.nb_uid < MIXART_MAX_CARDS * 2)
295  return -EINVAL;
296 
297  for(k=0; k<mgr->num_cards; k++) {
298  mgr->chip[k]->uid_in_analog_physio = phys_io.uid[k];
299  mgr->chip[k]->uid_out_analog_physio = phys_io.uid[phys_io.nb_uid/2 + k];
300  }
301 
302  return 0;
303 }
304 
305 
306 static int mixart_first_init(struct mixart_mgr *mgr)
307 {
308  u32 k;
309  int err;
310  struct mixart_msg request;
311 
312  if((err = mixart_enum_connectors(mgr)) < 0) return err;
313 
314  if((err = mixart_enum_physio(mgr)) < 0) return err;
315 
316  /* send a synchro command to card (necessary to do this before first MSG_STREAM_START_STREAM_GRP_PACKET) */
317  /* though why not here */
319  request.uid = (struct mixart_uid){0,0};
320  request.data = NULL;
321  request.size = 0;
322  /* this command has no data. response is a 32 bit status */
323  err = snd_mixart_send_msg(mgr, &request, sizeof(k), &k);
324  if( (err < 0) || (k != 0) ) {
325  snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD\n");
326  return err == 0 ? -EINVAL : err;
327  }
328 
329  return 0;
330 }
331 
332 
333 /* firmware base addresses (when hard coded) */
334 #define MIXART_MOTHERBOARD_XLX_BASE_ADDRESS 0x00600000
335 
336 static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmware *dsp)
337 {
338  int err, card_index;
339  u32 status_xilinx, status_elf, status_daught;
340  u32 val;
341 
342  /* read motherboard xilinx status */
344  /* read elf status */
346  /* read daughterboard xilinx status */
348 
349  /* motherboard xilinx status 5 will say that the board is performing a reset */
350  if (status_xilinx == 5) {
351  snd_printk(KERN_ERR "miXart is resetting !\n");
352  return -EAGAIN; /* try again later */
353  }
354 
355  switch (index) {
357 
358  /* xilinx already loaded ? */
359  if (status_xilinx == 4) {
360  snd_printk(KERN_DEBUG "xilinx is already loaded !\n");
361  return 0;
362  }
363  /* the status should be 0 == "idle" */
364  if (status_xilinx != 0) {
365  snd_printk(KERN_ERR "xilinx load error ! status = %d\n",
366  status_xilinx);
367  return -EIO; /* modprob -r may help ? */
368  }
369 
370  /* check xilinx validity */
371  if (((u32*)(dsp->data))[0] == 0xffffffff)
372  return -EINVAL;
373  if (dsp->size % 4)
374  return -EINVAL;
375 
376  /* set xilinx status to copying */
378 
379  /* setup xilinx base address */
381  /* setup code size for xilinx file */
383 
384  /* copy xilinx code */
386 
387  /* set xilinx status to copy finished */
389 
390  /* return, because no further processing needed */
391  return 0;
392 
394 
395  if (status_elf == 4) {
396  snd_printk(KERN_DEBUG "elf file already loaded !\n");
397  return 0;
398  }
399 
400  /* the status should be 0 == "idle" */
401  if (status_elf != 0) {
402  snd_printk(KERN_ERR "elf load error ! status = %d\n",
403  status_elf);
404  return -EIO; /* modprob -r may help ? */
405  }
406 
407  /* wait for xilinx status == 4 */
408  err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET, 1, 4, 500); /* 5sec */
409  if (err < 0) {
410  snd_printk(KERN_ERR "xilinx was not loaded or "
411  "could not be started\n");
412  return err;
413  }
414 
415  /* init some data on the card */
416  writel_be( 0, MIXART_MEM( mgr, MIXART_PSEUDOREG_BOARDNUMBER ) ); /* set miXart boardnumber to 0 */
417  writel_be( 0, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* reset pointer to flow table on miXart */
418 
419  /* set elf status to copying */
421 
422  /* process the copying of the elf packets */
423  err = mixart_load_elf( mgr, dsp );
424  if (err < 0) return err;
425 
426  /* set elf status to copy finished */
428 
429  /* wait for elf status == 4 */
430  err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET, 1, 4, 300); /* 3sec */
431  if (err < 0) {
432  snd_printk(KERN_ERR "elf could not be started\n");
433  return err;
434  }
435 
436  /* miXart waits at this point on the pointer to the flow table */
437  writel_be( (u32)mgr->flowinfo.addr, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* give pointer of flow table to miXart */
438 
439  return 0; /* return, another xilinx file has to be loaded before */
440 
442  default:
443 
444  /* elf and xilinx should be loaded */
445  if (status_elf != 4 || status_xilinx != 4) {
446  printk(KERN_ERR "xilinx or elf not "
447  "successfully loaded\n");
448  return -EIO; /* modprob -r may help ? */
449  }
450 
451  /* wait for daughter detection != 0 */
452  err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET, 0, 0, 30); /* 300msec */
453  if (err < 0) {
454  snd_printk(KERN_ERR "error starting elf file\n");
455  return err;
456  }
457 
458  /* the board type can now be retrieved */
460 
462  break; /* no daughter board; the file does not have to be loaded, continue after the switch */
463 
464  /* only if aesebu daughter board presence (elf code must run) */
466  return -EINVAL;
467 
468  /* daughter should be idle */
469  if (status_daught != 0) {
470  printk(KERN_ERR "daughter load error ! status = %d\n",
471  status_daught);
472  return -EIO; /* modprob -r may help ? */
473  }
474 
475  /* check daughterboard xilinx validity */
476  if (((u32*)(dsp->data))[0] == 0xffffffff)
477  return -EINVAL;
478  if (dsp->size % 4)
479  return -EINVAL;
480 
481  /* inform mixart about the size of the file */
483 
484  /* set daughterboard status to 1 */
486 
487  /* wait for status == 2 */
488  err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 2, 30); /* 300msec */
489  if (err < 0) {
490  snd_printk(KERN_ERR "daughter board load error\n");
491  return err;
492  }
493 
494  /* get the address where to write the file */
496  if (!val)
497  return -EINVAL;
498 
499  /* copy daughterboard xilinx code */
500  memcpy_toio( MIXART_MEM( mgr, val), dsp->data, dsp->size);
501 
502  /* set daughterboard status to 4 */
504 
505  /* continue with init */
506  break;
507  } /* end of switch file index*/
508 
509  /* wait for daughter status == 3 */
510  err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 3, 300); /* 3sec */
511  if (err < 0) {
513  "daughter board could not be initialised\n");
514  return err;
515  }
516 
517  /* init mailbox (communication with embedded) */
519 
520  /* first communication with embedded */
521  err = mixart_first_init(mgr);
522  if (err < 0) {
523  snd_printk(KERN_ERR "miXart could not be set up\n");
524  return err;
525  }
526 
527  /* create devices and mixer in accordance with HW options*/
528  for (card_index = 0; card_index < mgr->num_cards; card_index++) {
529  struct snd_mixart *chip = mgr->chip[card_index];
530 
531  if ((err = snd_mixart_create_pcm(chip)) < 0)
532  return err;
533 
534  if (card_index == 0) {
535  if ((err = snd_mixart_create_mixer(chip->mgr)) < 0)
536  return err;
537  }
538 
539  if ((err = snd_card_register(chip->card)) < 0)
540  return err;
541  }
542 
543  snd_printdd("miXart firmware downloaded and successfully set up\n");
544 
545  return 0;
546 }
547 
548 
549 #if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
550 #if !defined(CONFIG_USE_MIXARTLOADER) && !defined(CONFIG_SND_MIXART) /* built-in kernel */
551 #define SND_MIXART_FW_LOADER /* use the standard firmware loader */
552 #endif
553 #endif
554 
555 #ifdef SND_MIXART_FW_LOADER
556 
557 int snd_mixart_setup_firmware(struct mixart_mgr *mgr)
558 {
559  static char *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(path, "mixart/%s", fw_files[i]);
569  if (request_firmware(&fw_entry, path, &mgr->pci->dev)) {
570  snd_printk(KERN_ERR "miXart: can't load firmware %s\n", path);
571  return -ENOENT;
572  }
573  /* fake hwdep dsp record */
574  err = mixart_dsp_load(mgr, i, fw_entry);
575  release_firmware(fw_entry);
576  if (err < 0)
577  return err;
578  mgr->dsp_loaded |= 1 << i;
579  }
580  return 0;
581 }
582 
583 MODULE_FIRMWARE("mixart/miXart8.xlx");
584 MODULE_FIRMWARE("mixart/miXart8.elf");
585 MODULE_FIRMWARE("mixart/miXart8AES.xlx");
586 
587 #else /* old style firmware loading */
588 
589 /* miXart hwdep interface id string */
590 #define SND_MIXART_HWDEP_ID "miXart Loader"
591 
592 static int mixart_hwdep_dsp_status(struct snd_hwdep *hw,
593  struct snd_hwdep_dsp_status *info)
594 {
595  struct mixart_mgr *mgr = hw->private_data;
596 
597  strcpy(info->id, "miXart");
599 
600  if (mgr->dsp_loaded & (1 << MIXART_MOTHERBOARD_ELF_INDEX))
601  info->chip_ready = 1;
602 
604  return 0;
605 }
606 
607 static int mixart_hwdep_dsp_load(struct snd_hwdep *hw,
608  struct snd_hwdep_dsp_image *dsp)
609 {
610  struct mixart_mgr* mgr = hw->private_data;
611  struct firmware fw;
612  int err;
613 
614  fw.size = dsp->length;
615  fw.data = vmalloc(dsp->length);
616  if (! fw.data) {
617  snd_printk(KERN_ERR "miXart: cannot allocate image size %d\n",
618  (int)dsp->length);
619  return -ENOMEM;
620  }
621  if (copy_from_user((void *) fw.data, dsp->image, dsp->length)) {
622  vfree(fw.data);
623  return -EFAULT;
624  }
625  err = mixart_dsp_load(mgr, dsp->index, &fw);
626  vfree(fw.data);
627  if (err < 0)
628  return err;
629  mgr->dsp_loaded |= 1 << dsp->index;
630  return err;
631 }
632 
634 {
635  int err;
636  struct snd_hwdep *hw;
637 
638  /* only create hwdep interface for first cardX (see "index" module parameter)*/
639  if ((err = snd_hwdep_new(mgr->chip[0]->card, SND_MIXART_HWDEP_ID, 0, &hw)) < 0)
640  return err;
641 
643  hw->private_data = mgr;
644  hw->ops.dsp_status = mixart_hwdep_dsp_status;
645  hw->ops.dsp_load = mixart_hwdep_dsp_load;
646  hw->exclusive = 1;
648  mgr->dsp_loaded = 0;
649 
650  return snd_card_register(mgr->chip[0]->card);
651 }
652 
653 #endif /* SND_MIXART_FW_LOADER */