diff options
Diffstat (limited to 'sound/pci/cs46xx/cs46xx_lib.c')
-rw-r--r-- | sound/pci/cs46xx/cs46xx_lib.c | 265 |
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 | ||
335 | static 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" | 350 | static 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" | 354 | MODULE_FIRMWARE("cs46xx/cwc4630"); |
355 | MODULE_FIRMWARE("cs46xx/cwcasync"); | ||
356 | MODULE_FIRMWARE("cs46xx/cwcsnoop"); | ||
357 | MODULE_FIRMWARE("cs46xx/cwcbinhack"); | ||
358 | MODULE_FIRMWARE("cs46xx/cwcdma"); | ||
359 | |||
360 | static 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 | |||
390 | static 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 | ||
341 | int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip, | 476 | int 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" | 499 | struct ba1_struct { |
500 | struct { | ||
501 | u32 offset; | ||
502 | u32 size; | ||
503 | } memory[BA1_MEMORY_COUNT]; | ||
504 | u32 map[BA1_DWORD_SIZE]; | ||
505 | }; | ||
506 | |||
507 | MODULE_FIRMWARE("cs46xx/ba1"); | ||
508 | |||
509 | static 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 | ||
366 | int snd_cs46xx_download_image(struct snd_cs46xx *chip) | 542 | int 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) | |||
3067 | int snd_cs46xx_start_dsp(struct snd_cs46xx *chip) | 3249 | int 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 | /* |