snd-cs46xx: reenable using external firmware (closes: #464197)

svn path=/dists/trunk/linux-2.6/; revision=14124
This commit is contained in:
Ben Hutchings 2009-08-16 23:40:34 +00:00
parent d3c7acc3e2
commit 10f2508e9d
3 changed files with 172 additions and 0 deletions

2
debian/changelog vendored
View File

@ -45,6 +45,8 @@ linux-2.6 (2.6.31~rc6-1~experimental.1) UNRELEASED; urgency=low
* cxgb3: remove PHY firmware and use request_firmware() to load it
* Add firmware-linux-free package containing DFSG-free firmware
* av7110: include firmware source and binary
* snd-cs46xx: reenable using external firmware (closes: #464197,
but note that Debian cannot currently distribute the firmware)
[ Martin Michlmayr ]
* [armel/orion5x, armel/kirkwood] Set GPIO_SYSFS=y since these

View File

@ -0,0 +1,169 @@
From: Ben Hutchings <ben@decadent.org.uk>
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);

View File

@ -14,6 +14,7 @@
+ features/all/lib-crcitut-bit-reversed.patch
+ features/all/drivers-staging-rt28x0sta-request_firmware.patch
+ features/all/export-unionfs-symbols.patch
+ features/all/sound-pci-cs46xx-request_firmware.patch
+ bugfix/sparc/drivers_net-broken.patch
#+ bugfix/ia64/hardcode-arch-script-output.patch