aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/cs46xx/cs46xx_lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/cs46xx/cs46xx_lib.c')
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c265
1 files changed, 220 insertions, 45 deletions
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 1b66efd9b728..f18e5878f58b 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -54,7 +54,9 @@
54#include <linux/gameport.h> 54#include <linux/gameport.h>
55#include <linux/mutex.h> 55#include <linux/mutex.h>
56#include <linux/export.h> 56#include <linux/export.h>
57 57#include <linux/module.h>
58#include <linux/firmware.h>
59#include <linux/vmalloc.h>
58 60
59#include <sound/core.h> 61#include <sound/core.h>
60#include <sound/control.h> 62#include <sound/control.h>
@@ -330,13 +332,146 @@ int snd_cs46xx_download(struct snd_cs46xx *chip,
330 return 0; 332 return 0;
331} 333}
332 334
335static inline void memcpy_le32(void *dst, const void *src, unsigned int len)
336{
337#ifdef __LITTLE_ENDIAN
338 memcpy(dst, src, len);
339#else
340 u32 *_dst = dst;
341 const __le32 *_src = src;
342 len /= 4;
343 while (len-- > 0)
344 *_dst++ = le32_to_cpu(*_src++);
345#endif
346}
347
333#ifdef CONFIG_SND_CS46XX_NEW_DSP 348#ifdef CONFIG_SND_CS46XX_NEW_DSP
334 349
335#include "imgs/cwc4630.h" 350static const char *module_names[CS46XX_DSP_MODULES] = {
336#include "imgs/cwcasync.h" 351 "cwc4630", "cwcasync", "cwcsnoop", "cwcbinhack", "cwcdma"
337#include "imgs/cwcsnoop.h" 352};
338#include "imgs/cwcbinhack.h" 353
339#include "imgs/cwcdma.h" 354MODULE_FIRMWARE("cs46xx/cwc4630");
355MODULE_FIRMWARE("cs46xx/cwcasync");
356MODULE_FIRMWARE("cs46xx/cwcsnoop");
357MODULE_FIRMWARE("cs46xx/cwcbinhack");
358MODULE_FIRMWARE("cs46xx/cwcdma");
359
360static void free_module_desc(struct dsp_module_desc *module)
361{
362 if (!module)
363 return;
364 kfree(module->module_name);
365 kfree(module->symbol_table.symbols);
366 if (module->segments) {
367 int i;
368 for (i = 0; i < module->nsegments; i++)
369 kfree(module->segments[i].data);
370 kfree(module->segments);
371 }
372}
373
374/* firmware binary format:
375 * le32 nsymbols;
376 * struct {
377 * le32 address;
378 * char symbol_name[DSP_MAX_SYMBOL_NAME];
379 * le32 symbol_type;
380 * } symbols[nsymbols];
381 * le32 nsegments;
382 * struct {
383 * le32 segment_type;
384 * le32 offset;
385 * le32 size;
386 * le32 data[size];
387 * } segments[nsegments];
388 */
389
390static int load_firmware(struct snd_cs46xx *chip,
391 struct dsp_module_desc **module_ret,
392 const char *fw_name)
393{
394 int i, err;
395 unsigned int nums, fwlen, fwsize;
396 const __le32 *fwdat;
397 struct dsp_module_desc *module = NULL;
398 const struct firmware *fw;
399 char fw_path[32];
400
401 sprintf(fw_path, "cs46xx/%s", fw_name);
402 err = request_firmware(&fw, fw_path, &chip->pci->dev);
403 if (err < 0)
404 return err;
405 fwsize = fw->size / 4;
406 if (fwsize < 2) {
407 err = -EINVAL;
408 goto error;
409 }
410
411 err = -ENOMEM;
412 module = kzalloc(sizeof(*module), GFP_KERNEL);
413 if (!module)
414 goto error;
415 module->module_name = kstrdup(fw_name, GFP_KERNEL);
416 if (!module->module_name)
417 goto error;
418
419 fwlen = 0;
420 fwdat = (const __le32 *)fw->data;
421 nums = module->symbol_table.nsymbols = le32_to_cpu(fwdat[fwlen++]);
422 if (nums >= 40)
423 goto error_inval;
424 module->symbol_table.symbols =
425 kcalloc(nums, sizeof(struct dsp_symbol_entry), GFP_KERNEL);
426 if (!module->symbol_table.symbols)
427 goto error;
428 for (i = 0; i < nums; i++) {
429 struct dsp_symbol_entry *entry =
430 &module->symbol_table.symbols[i];
431 if (fwlen + 2 + DSP_MAX_SYMBOL_NAME / 4 > fwsize)
432 goto error_inval;
433 entry->address = le32_to_cpu(fwdat[fwlen++]);
434 memcpy(entry->symbol_name, &fwdat[fwlen], DSP_MAX_SYMBOL_NAME - 1);
435 fwlen += DSP_MAX_SYMBOL_NAME / 4;
436 entry->symbol_type = le32_to_cpu(fwdat[fwlen++]);
437 }
438
439 if (fwlen >= fwsize)
440 goto error_inval;
441 nums = module->nsegments = le32_to_cpu(fwdat[fwlen++]);
442 if (nums > 10)
443 goto error_inval;
444 module->segments =
445 kcalloc(nums, sizeof(struct dsp_segment_desc), GFP_KERNEL);
446 if (!module->segments)
447 goto error;
448 for (i = 0; i < nums; i++) {
449 struct dsp_segment_desc *entry = &module->segments[i];
450 if (fwlen + 3 > fwsize)
451 goto error_inval;
452 entry->segment_type = le32_to_cpu(fwdat[fwlen++]);
453 entry->offset = le32_to_cpu(fwdat[fwlen++]);
454 entry->size = le32_to_cpu(fwdat[fwlen++]);
455 if (fwlen + entry->size > fwsize)
456 goto error_inval;
457 entry->data = kmalloc(entry->size * 4, GFP_KERNEL);
458 if (!entry->data)
459 goto error;
460 memcpy_le32(entry->data, &fwdat[fwlen], entry->size * 4);
461 fwlen += entry->size;
462 }
463
464 *module_ret = module;
465 release_firmware(fw);
466 return 0;
467
468 error_inval:
469 err = -EINVAL;
470 error:
471 free_module_desc(module);
472 release_firmware(fw);
473 return err;
474}
340 475
341int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip, 476int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip,
342 unsigned long offset, 477 unsigned long offset,
@@ -361,20 +496,63 @@ int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip,
361 496
362#else /* old DSP image */ 497#else /* old DSP image */
363 498
364#include "cs46xx_image.h" 499struct ba1_struct {
500 struct {
501 u32 offset;
502 u32 size;
503 } memory[BA1_MEMORY_COUNT];
504 u32 map[BA1_DWORD_SIZE];
505};
506
507MODULE_FIRMWARE("cs46xx/ba1");
508
509static int load_firmware(struct snd_cs46xx *chip)
510{
511 const struct firmware *fw;
512 int i, size, err;
513
514 err = request_firmware(&fw, "cs46xx/ba1", &chip->pci->dev);
515 if (err < 0)
516 return err;
517 if (fw->size != sizeof(*chip->ba1)) {
518 err = -EINVAL;
519 goto error;
520 }
521
522 chip->ba1 = vmalloc(sizeof(*chip->ba1));
523 if (!chip->ba1) {
524 err = -ENOMEM;
525 goto error;
526 }
527
528 memcpy_le32(chip->ba1, fw->data, sizeof(*chip->ba1));
529
530 /* sanity check */
531 size = 0;
532 for (i = 0; i < BA1_MEMORY_COUNT; i++)
533 size += chip->ba1->memory[i].size;
534 if (size > BA1_DWORD_SIZE * 4)
535 err = -EINVAL;
536
537 error:
538 release_firmware(fw);
539 return err;
540}
365 541
366int snd_cs46xx_download_image(struct snd_cs46xx *chip) 542int snd_cs46xx_download_image(struct snd_cs46xx *chip)
367{ 543{
368 int idx, err; 544 int idx, err;
369 unsigned long offset = 0; 545 unsigned int offset = 0;
546 struct ba1_struct *ba1 = chip->ba1;
370 547
371 for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) { 548 for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) {
372 if ((err = snd_cs46xx_download(chip, 549 err = snd_cs46xx_download(chip,
373 &BA1Struct.map[offset], 550 &ba1->map[offset],
374 BA1Struct.memory[idx].offset, 551 ba1->memory[idx].offset,
375 BA1Struct.memory[idx].size)) < 0) 552 ba1->memory[idx].size);
553 if (err < 0)
376 return err; 554 return err;
377 offset += BA1Struct.memory[idx].size >> 2; 555 offset += ba1->memory[idx].size >> 2;
378 } 556 }
379 return 0; 557 return 0;
380} 558}
@@ -2798,6 +2976,10 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip)
2798 cs46xx_dsp_spos_destroy(chip); 2976 cs46xx_dsp_spos_destroy(chip);
2799 chip->dsp_spos_instance = NULL; 2977 chip->dsp_spos_instance = NULL;
2800 } 2978 }
2979 for (idx = 0; idx < CS46XX_DSP_MODULES; idx++)
2980 free_module_desc(chip->modules[idx]);
2981#else
2982 vfree(chip->ba1);
2801#endif 2983#endif
2802 2984
2803#ifdef CONFIG_PM_SLEEP 2985#ifdef CONFIG_PM_SLEEP
@@ -3067,6 +3249,11 @@ static void cs46xx_enable_stream_irqs(struct snd_cs46xx *chip)
3067int snd_cs46xx_start_dsp(struct snd_cs46xx *chip) 3249int snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
3068{ 3250{
3069 unsigned int tmp; 3251 unsigned int tmp;
3252#ifdef CONFIG_SND_CS46XX_NEW_DSP
3253 int i;
3254#endif
3255 int err;
3256
3070 /* 3257 /*
3071 * Reset the processor. 3258 * Reset the processor.
3072 */ 3259 */
@@ -3075,45 +3262,33 @@ int snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
3075 * Download the image to the processor. 3262 * Download the image to the processor.
3076 */ 3263 */
3077#ifdef CONFIG_SND_CS46XX_NEW_DSP 3264#ifdef CONFIG_SND_CS46XX_NEW_DSP
3078#if 0 3265 for (i = 0; i < CS46XX_DSP_MODULES; i++) {
3079 if (cs46xx_dsp_load_module(chip, &cwcemb80_module) < 0) { 3266 err = load_firmware(chip, &chip->modules[i], module_names[i]);
3080 snd_printk(KERN_ERR "image download error\n"); 3267 if (err < 0) {
3081 return -EIO; 3268 snd_printk(KERN_ERR "firmware load error [%s]\n",
3082 } 3269 module_names[i]);
3083#endif 3270 return err;
3084 3271 }
3085 if (cs46xx_dsp_load_module(chip, &cwc4630_module) < 0) { 3272 err = cs46xx_dsp_load_module(chip, chip->modules[i]);
3086 snd_printk(KERN_ERR "image download error [cwc4630]\n"); 3273 if (err < 0) {
3087 return -EIO; 3274 snd_printk(KERN_ERR "image download error [%s]\n",
3088 } 3275 module_names[i]);
3089 3276 return err;
3090 if (cs46xx_dsp_load_module(chip, &cwcasync_module) < 0) { 3277 }
3091 snd_printk(KERN_ERR "image download error [cwcasync]\n");
3092 return -EIO;
3093 }
3094
3095 if (cs46xx_dsp_load_module(chip, &cwcsnoop_module) < 0) {
3096 snd_printk(KERN_ERR "image download error [cwcsnoop]\n");
3097 return -EIO;
3098 }
3099
3100 if (cs46xx_dsp_load_module(chip, &cwcbinhack_module) < 0) {
3101 snd_printk(KERN_ERR "image download error [cwcbinhack]\n");
3102 return -EIO;
3103 }
3104
3105 if (cs46xx_dsp_load_module(chip, &cwcdma_module) < 0) {
3106 snd_printk(KERN_ERR "image download error [cwcdma]\n");
3107 return -EIO;
3108 } 3278 }
3109 3279
3110 if (cs46xx_dsp_scb_and_task_init(chip) < 0) 3280 if (cs46xx_dsp_scb_and_task_init(chip) < 0)
3111 return -EIO; 3281 return -EIO;
3112#else 3282#else
3283 err = load_firmware(chip);
3284 if (err < 0)
3285 return err;
3286
3113 /* old image */ 3287 /* old image */
3114 if (snd_cs46xx_download_image(chip) < 0) { 3288 err = snd_cs46xx_download_image(chip);
3289 if (err < 0) {
3115 snd_printk(KERN_ERR "image download error\n"); 3290 snd_printk(KERN_ERR "image download error\n");
3116 return -EIO; 3291 return err;
3117 } 3292 }
3118 3293
3119 /* 3294 /*