173 lines
4.8 KiB
Diff
173 lines
4.8 KiB
Diff
From: Ben Hutchings <ben@decadent.org.uk>
|
|
Subject: cs46xx: Use request_firmware() for old DSP code
|
|
|
|
Based on work by Kalle Olavi Niemitalo <kon@iki.fi>.
|
|
|
|
Tested by Antonio Ospite <ospite@studenti.unina.it>.
|
|
Unfortunately we cannot currently distribute the firmware.
|
|
|
|
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
|
|
index 17e03b9..124b3a0 100644
|
|
--- a/sound/pci/Kconfig
|
|
+++ b/sound/pci/Kconfig
|
|
@@ -229,7 +229,7 @@ config SND_CS4281
|
|
|
|
config SND_CS46XX
|
|
tristate "Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x"
|
|
- depends on BROKEN
|
|
+ select FW_LOADER
|
|
select SND_RAWMIDI
|
|
select SND_AC97_CODEC
|
|
help
|
|
@@ -241,6 +241,7 @@ config SND_CS46XX
|
|
|
|
config SND_CS46XX_NEW_DSP
|
|
bool "Cirrus Logic (Sound Fusion) New DSP support"
|
|
+ depends on BROKEN
|
|
depends on SND_CS46XX
|
|
default y
|
|
help
|
|
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
|
|
index 1be96ea..b12b930 100644
|
|
--- a/sound/pci/cs46xx/cs46xx_lib.c
|
|
+++ b/sound/pci/cs46xx/cs46xx_lib.c
|
|
@@ -53,6 +53,7 @@
|
|
#include <linux/slab.h>
|
|
#include <linux/gameport.h>
|
|
#include <linux/mutex.h>
|
|
+#include <linux/firmware.h>
|
|
|
|
|
|
#include <sound/core.h>
|
|
@@ -308,7 +309,7 @@ static void snd_cs46xx_ac97_write(struct snd_ac97 *ac97,
|
|
*/
|
|
|
|
int snd_cs46xx_download(struct snd_cs46xx *chip,
|
|
- u32 *src,
|
|
+ const __le32 *src,
|
|
unsigned long offset,
|
|
unsigned long len)
|
|
{
|
|
@@ -321,9 +322,9 @@ int snd_cs46xx_download(struct snd_cs46xx *chip,
|
|
dst = chip->region.idx[bank+1].remap_addr + offset;
|
|
len /= sizeof(u32);
|
|
|
|
- /* writel already converts 32-bit value to right endianess */
|
|
while (len-- > 0) {
|
|
- writel(*src++, dst);
|
|
+ __raw_writel((__force u32)*src++, dst);
|
|
+ mmiowb();
|
|
dst += sizeof(u32);
|
|
}
|
|
return 0;
|
|
@@ -360,23 +361,77 @@ int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip,
|
|
|
|
#else /* old DSP image */
|
|
|
|
-#include "cs46xx_image.h"
|
|
+struct cs46xx_old_image {
|
|
+ __le32 size[BA1_MEMORY_COUNT];
|
|
+ __le32 data[0];
|
|
+};
|
|
|
|
-int snd_cs46xx_download_image(struct snd_cs46xx *chip)
|
|
+static int snd_cs46xx_check_image_size(const struct firmware *firmware)
|
|
{
|
|
- int idx, err;
|
|
- unsigned long offset = 0;
|
|
+ const struct cs46xx_old_image *image =
|
|
+ (const struct cs46xx_old_image *)firmware->data;
|
|
+ size_t offset = sizeof(*image);
|
|
+ int idx;
|
|
+
|
|
+ if (firmware->size < offset) {
|
|
+ snd_printk(KERN_ERR "cs46xx: firmware too small\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
|
|
for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) {
|
|
- if ((err = snd_cs46xx_download(chip,
|
|
- &BA1Struct.map[offset],
|
|
- BA1Struct.memory[idx].offset,
|
|
- BA1Struct.memory[idx].size)) < 0)
|
|
- return err;
|
|
- offset += BA1Struct.memory[idx].size >> 2;
|
|
- }
|
|
+ size_t size = le32_to_cpu(image->size[idx]);
|
|
+
|
|
+ if (size % sizeof(u32)) {
|
|
+ snd_printk(KERN_ERR "cs46xx: firmware hunk misaligned\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if (size > BA1_DWORD_SIZE * sizeof(u32)) {
|
|
+ snd_printk(KERN_ERR "cs46xx: firmware hunk out of range\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ offset += size;
|
|
+ }
|
|
+
|
|
+ if (firmware->size != offset) {
|
|
+ snd_printk(KERN_ERR "cs46xx: firmware size mismatch\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
return 0;
|
|
}
|
|
+
|
|
+static int snd_cs46xx_download_image(struct snd_cs46xx *chip)
|
|
+{
|
|
+ int idx, err;
|
|
+ const struct firmware *firmware = NULL;
|
|
+ const struct cs46xx_old_image *image;
|
|
+ const __le32 *data;
|
|
+
|
|
+ err = request_firmware(&firmware, "cs46xx/cs46xx-old.fw",
|
|
+ &chip->pci->dev);
|
|
+ if (err < 0) {
|
|
+ snd_printk(KERN_ERR "cs46xx: no firmware\n");
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ err = snd_cs46xx_check_image_size(firmware);
|
|
+ if (err < 0)
|
|
+ goto end;
|
|
+ image = (const struct cs46xx_old_image *)firmware->data;
|
|
+ data = image->data;
|
|
+
|
|
+ for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) {
|
|
+ size_t size = le32_to_cpu(image->size[idx]);
|
|
+
|
|
+ err = snd_cs46xx_download(chip, data, idx << 16, size);
|
|
+ if (err < 0)
|
|
+ goto end;
|
|
+ data += size / sizeof(u32);
|
|
+ }
|
|
+end:
|
|
+ release_firmware(firmware);
|
|
+ return err;
|
|
+}
|
|
#endif /* CONFIG_SND_CS46XX_NEW_DSP */
|
|
|
|
/*
|
|
@@ -3874,3 +3929,5 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
|
|
*rchip = chip;
|
|
return 0;
|
|
}
|
|
+
|
|
+MODULE_FIRMWARE("cs46xx/cs46xx-old.fw");
|
|
diff --git a/sound/pci/cs46xx/cs46xx_lib.h b/sound/pci/cs46xx/cs46xx_lib.h
|
|
index 4eb55aa..85babb5 100644
|
|
--- a/sound/pci/cs46xx/cs46xx_lib.h
|
|
+++ b/sound/pci/cs46xx/cs46xx_lib.h
|
|
@@ -103,8 +103,8 @@ int cs46xx_dsp_proc_done (struct snd_cs46xx *chip);
|
|
#define cs46xx_dsp_proc_done(chip)
|
|
#endif
|
|
int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip);
|
|
-int snd_cs46xx_download (struct snd_cs46xx *chip, u32 *src, unsigned long offset,
|
|
- unsigned long len);
|
|
+int snd_cs46xx_download(struct snd_cs46xx *chip, const __le32 *src, unsigned long offset,
|
|
+ unsigned long len);
|
|
int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip, unsigned long offset, unsigned long len);
|
|
int cs46xx_dsp_enable_spdif_out (struct snd_cs46xx *chip);
|
|
int cs46xx_dsp_enable_spdif_hw (struct snd_cs46xx *chip);
|