diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-05-15 10:02:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-05-15 10:02:36 -0700 |
commit | 33e02dc69afbd8f1b85a51d74d72f139ba4ca623 (patch) | |
tree | 419637178f5dc6758703143d73eedf484d5b810e /sound/pci/emu10k1 | |
parent | d34672777da3ea919e8adb0670ab91ddadf7dea0 (diff) | |
parent | d731b1ed15052580b7b2f40559021012d280f1d9 (diff) |
Merge tag 'sound-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"This one became bigger than usual, not in the total size but rather
containing lots of small changes all over the places.
The majority of changes are about ASoC, especially SOF / Intel stuff,
and we see an interesting work for ASoC DAPM graph visualization,
while there are many other code cleanup and refactoring, too.
Core:
- A deadlock fix at device disconnection
- A new tool dapm-graph for visualising the DAPM state
ASoC:
- Large updates throughout the Intel audio drivers
- Fixes and clarifications for the DAPM documentation
- Cleanups of accessors for driver data, module labelling, and for
constification
- Modernsation and cleanup work in the Mediatek drivers
- Several fixes and features for the DaVinci I2S driver
- New drivers for several AMD and Intel platforms, Nuvoton NAU8325,
Rockchip RK3308 and Texas Instruments PCM6240
HD-audio:
- Cleanup for CONFIG_PM dependencies
- Cirrus HD-audio codec fixes and quirks
Others:
- Series of tree-wide fixes in Makefiles to use *-y
- Additions of missing module descriptions
- Scarlett2 USB mixer enhancements
- A series of legacy emu10k1 fixes and improvements"
* tag 'sound-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (603 commits)
ALSA: hda/realtek: Drop doubly quirk entry for 103c:8a2e
ALSA: hda/realtek - fixed headset Mic not show
ASoC: SOF: amd: Fix build error with built-in config
ALSA: scarlett2: Increase mixer range to +12dB
ALSA: scarlett2: Add S/PDIF source selection controls
ALSA: core: Remove superfluous CONFIG_PM
ALSA: Fix deadlocks with kctl removals at disconnection
ASoC: audio-graph-card2: call of_node_get() before of_get_next_child()
ASoC: SOF: amd: Correct spaces in Makefile
ASoC: rt715-sdca-sdw: Fix wrong complete waiting in rt715_dev_resume()
ASoC: Intel: sof_sdw_rt_amp: use dai parameter
ASoC: Intel: sof_sdw: add dai parameter to rtd_init callback
ASoC: Intel: sof_sdw: use .controls/.widgets to add controls/widgets
ASoC: Intel: sof_sdw: add controls and dapm widgets in codec_info
ASoC: Intel: sof_sdw: use generic name for controls/widgets
ASoC: Intel: sof_sdw_cs_amp: rename Speakers to Speaker
ASoC: Intel: maxim-common: change max98373 data to static
ASoC: Intel: sof_sdw: add max98373 dapm routes
ASoC: Intel: sof_rt5682: use max_98373_dai_link function
ASoC: Intel: sof_nau8825: use max_98373_dai_link function
...
Diffstat (limited to 'sound/pci/emu10k1')
-rw-r--r-- | sound/pci/emu10k1/Makefile | 6 | ||||
-rw-r--r-- | sound/pci/emu10k1/emu10k1_callback.c | 13 | ||||
-rw-r--r-- | sound/pci/emu10k1/emu10k1_main.c | 75 | ||||
-rw-r--r-- | sound/pci/emu10k1/emu10k1_patch.c | 209 | ||||
-rw-r--r-- | sound/pci/emu10k1/io.c | 53 | ||||
-rw-r--r-- | sound/pci/emu10k1/memory.c | 55 |
6 files changed, 195 insertions, 216 deletions
diff --git a/sound/pci/emu10k1/Makefile b/sound/pci/emu10k1/Makefile index 17d5527be319..1f325abcb3ef 100644 --- a/sound/pci/emu10k1/Makefile +++ b/sound/pci/emu10k1/Makefile @@ -4,12 +4,12 @@ # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz> # -snd-emu10k1-objs := emu10k1.o emu10k1_main.o \ +snd-emu10k1-y := emu10k1.o emu10k1_main.o \ irq.o memory.o voice.o emumpu401.o emupcm.o io.o \ emumixer.o emufx.o timer.o p16v.o snd-emu10k1-$(CONFIG_SND_PROC_FS) += emuproc.o -snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o -snd-emu10k1x-objs := emu10k1x.o +snd-emu10k1-synth-y := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o +snd-emu10k1x-y := emu10k1x.o # Toplevel Module Dependency obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c index 941bfbf812ed..ef26e4d3e2a3 100644 --- a/sound/pci/emu10k1/emu10k1_callback.c +++ b/sound/pci/emu10k1/emu10k1_callback.c @@ -255,7 +255,7 @@ lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw, /* check if sample is finished playing (non-looping only) */ if (bp != best + V_OFF && bp != best + V_FREE && (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { - val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch); + val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch) - 64 + 3; if (val >= vp->reg.loopstart) bp = best + V_OFF; } @@ -310,6 +310,7 @@ start_voice(struct snd_emux_voice *vp) { unsigned int temp; int ch; + bool w_16; u32 psst, dsl, map, ccca, vtarget; unsigned int addr, mapped_offset; struct snd_midi_channel *chan; @@ -321,6 +322,7 @@ start_voice(struct snd_emux_voice *vp) if (snd_BUG_ON(ch < 0)) return -EINVAL; chan = vp->chan; + w_16 = !(vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS); emem = (struct snd_emu10k1_memblk *)vp->block; if (emem == NULL) @@ -330,7 +332,7 @@ start_voice(struct snd_emux_voice *vp) /* dev_err(hw->card->devK, "emu: cannot map!\n"); */ return -ENOMEM; } - mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1; + mapped_offset = snd_emu10k1_memblk_offset(emem) >> w_16; vp->reg.start += mapped_offset; vp->reg.end += mapped_offset; vp->reg.loopstart += mapped_offset; @@ -362,7 +364,7 @@ start_voice(struct snd_emux_voice *vp) map = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0); - addr = vp->reg.start; + addr = vp->reg.start + 64 - 3; temp = vp->reg.parm.filterQ; ccca = (temp << 28) | addr; if (vp->apitch < 0xe400) @@ -371,7 +373,7 @@ start_voice(struct snd_emux_voice *vp) unsigned int shift = (vp->apitch - 0xe000) >> 10; ccca |= shift << 25; } - if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) + if (!w_16) ccca |= CCCA_8BITSELECT; vtarget = (unsigned int)vp->vtarget << 16; @@ -430,6 +432,9 @@ start_voice(struct snd_emux_voice *vp) /* Q & current address (Q 4bit value, MSB) */ CCCA, ccca, + /* cache */ + CCR, REG_VAL_PUT(CCR_CACHEINVALIDSIZE, 64), + /* reset volume */ VTFT, vtarget | vp->ftarget, CVCF, vtarget | CVCF_CURRENTFILTER_MASK, diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 8ccc0178360c..5b8a5ba825bd 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -652,52 +652,6 @@ static int snd_emu10k1_cardbus_init(struct snd_emu10k1 *emu) return 0; } -static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, - const struct firmware *fw_entry) -{ - int n, i; - u16 reg; - u8 value; - __always_unused u16 write_post; - - if (!fw_entry) - return -EIO; - - /* The FPGA is a Xilinx Spartan IIE XC2S50E */ - /* On E-MU 0404b it is a Xilinx Spartan III XC3S50 */ - /* GPIO7 -> FPGA PGMN - * GPIO6 -> FPGA CCLK - * GPIO5 -> FPGA DIN - * FPGA CONFIG OFF -> FPGA PGMN - */ - spin_lock_irq(&emu->emu_lock); - outw(0x00, emu->port + A_GPIO); /* Set PGMN low for 100uS. */ - write_post = inw(emu->port + A_GPIO); - udelay(100); - outw(0x80, emu->port + A_GPIO); /* Leave bit 7 set during netlist setup. */ - write_post = inw(emu->port + A_GPIO); - udelay(100); /* Allow FPGA memory to clean */ - for (n = 0; n < fw_entry->size; n++) { - value = fw_entry->data[n]; - for (i = 0; i < 8; i++) { - reg = 0x80; - if (value & 0x1) - reg = reg | 0x20; - value = value >> 1; - outw(reg, emu->port + A_GPIO); - write_post = inw(emu->port + A_GPIO); - outw(reg | 0x40, emu->port + A_GPIO); - write_post = inw(emu->port + A_GPIO); - } - } - /* After programming, set GPIO bit 4 high again. */ - outw(0x10, emu->port + A_GPIO); - write_post = inw(emu->port + A_GPIO); - spin_unlock_irq(&emu->emu_lock); - - return 0; -} - /* firmware file names, per model, init-fw and dock-fw (optional) */ static const char * const firmware_names[5][2] = { [EMU_MODEL_EMU1010] = { @@ -729,7 +683,8 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, int dock, return err; } - return snd_emu1010_load_firmware_entry(emu, *fw); + snd_emu1010_load_firmware_entry(emu, dock, *fw); + return 0; } static void snd_emu1010_load_dock_firmware(struct snd_emu10k1 *emu) @@ -744,9 +699,6 @@ static void snd_emu1010_load_dock_firmware(struct snd_emu10k1 *emu) msleep(200); dev_info(emu->card->dev, "emu1010: Loading Audio Dock Firmware\n"); - /* Return to Audio Dock programming mode */ - snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, - EMU_HANA_FPGA_CONFIG_AUDIODOCK); err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw); if (err < 0) return; @@ -864,28 +816,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu) snd_emu1010_fpga_lock(emu); - /* Disable 48Volt power to Audio Dock */ - snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0); - - /* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */ - snd_emu1010_fpga_read(emu, EMU_HANA_ID, ®); - dev_dbg(emu->card->dev, "reg1 = 0x%x\n", reg); - if ((reg & 0x3f) == 0x15) { - /* FPGA netlist already present so clear it */ - /* Return to programming mode */ - - snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_HANA); - } - snd_emu1010_fpga_read(emu, EMU_HANA_ID, ®); - dev_dbg(emu->card->dev, "reg2 = 0x%x\n", reg); - if ((reg & 0x3f) == 0x15) { - /* FPGA failed to return to programming mode */ - dev_info(emu->card->dev, - "emu1010: FPGA failed to return to programming mode\n"); - return -ENODEV; - } - dev_info(emu->card->dev, "emu1010: EMU_HANA_ID = 0x%x\n", reg); - + dev_info(emu->card->dev, "emu1010: Loading Hana Firmware\n"); err = snd_emu1010_load_firmware(emu, 0, &emu->firmware); if (err < 0) { dev_info(emu->card->dev, "emu1010: Loading Firmware failed\n"); diff --git a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c index 89890f24509f..dbfa89435ac2 100644 --- a/sound/pci/emu10k1/emu10k1_patch.c +++ b/sound/pci/emu10k1/emu10k1_patch.c @@ -16,7 +16,7 @@ #define BLANK_LOOP_START 4 #define BLANK_LOOP_END 8 #define BLANK_LOOP_SIZE 12 -#define BLANK_HEAD_SIZE 32 +#define BLANK_HEAD_SIZE 3 /* * allocate a sample block and copy data from userspace @@ -26,56 +26,76 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, struct snd_util_memhdr *hdr, const void __user *data, long count) { + u8 fill; + u32 xor; + int shift; int offset; int truesize, size, blocksize; - __maybe_unused int loopsize; - int loopend, sampleend; - unsigned int start_addr; + int loop_start, loop_end, loop_size, data_end, unroll; struct snd_emu10k1 *emu; emu = rec->hw; if (snd_BUG_ON(!sp || !hdr)) return -EINVAL; - if (sp->v.size == 0) { - dev_dbg(emu->card->dev, - "emu: rom font for sample %d\n", sp->v.sample); - return 0; + if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP | SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) { + /* should instead return -ENOTSUPP; but compatibility */ + printk(KERN_WARNING "Emu10k1 wavetable patch %d with unsupported loop feature\n", + sp->v.sample); } - /* recalculate address offset */ - sp->v.end -= sp->v.start; - sp->v.loopstart -= sp->v.start; - sp->v.loopend -= sp->v.start; - sp->v.start = 0; - - /* some samples have invalid data. the addresses are corrected in voice info */ - sampleend = sp->v.end; - if (sampleend > sp->v.size) - sampleend = sp->v.size; - loopend = sp->v.loopend; - if (loopend > sampleend) - loopend = sampleend; - - /* be sure loop points start < end */ - if (sp->v.loopstart >= sp->v.loopend) - swap(sp->v.loopstart, sp->v.loopend); + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) { + shift = 0; + fill = 0x80; + xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0 : 0x80808080; + } else { + shift = 1; + fill = 0; + xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0x80008000 : 0; + } /* compute true data size to be loaded */ truesize = sp->v.size + BLANK_HEAD_SIZE; - loopsize = 0; -#if 0 /* not supported */ - if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) - loopsize = sp->v.loopend - sp->v.loopstart; - truesize += loopsize; -#endif - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { truesize += BLANK_LOOP_SIZE; + /* if no blank loop is attached in the sample, add it */ + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { + sp->v.loopstart = sp->v.end + BLANK_LOOP_START; + sp->v.loopend = sp->v.end + BLANK_LOOP_END; + } + } + + loop_start = sp->v.loopstart; + loop_end = sp->v.loopend; + loop_size = loop_end - loop_start; + if (!loop_size) + return -EINVAL; + data_end = sp->v.end; + + /* recalculate offset */ + sp->v.start += BLANK_HEAD_SIZE; + sp->v.end += BLANK_HEAD_SIZE; + sp->v.loopstart += BLANK_HEAD_SIZE; + sp->v.loopend += BLANK_HEAD_SIZE; + + // Automatic pre-filling of the cache does not work in the presence + // of loops (*), and we don't want to fill it manually, as that is + // fiddly and slow. So we unroll the loop until the loop end is + // beyond the cache size. + // (*) Strictly speaking, a single iteration is supported (that's + // how it works when the playback engine runs), but handling this + // special case is not worth it. + unroll = 0; + while (sp->v.loopend < 64) { + truesize += loop_size; + sp->v.loopstart += loop_size; + sp->v.loopend += loop_size; + sp->v.end += loop_size; + unroll++; + } /* try to allocate a memory block */ - blocksize = truesize; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - blocksize *= 2; + blocksize = truesize << shift; sp->block = snd_emu10k1_synth_alloc(emu, blocksize); if (sp->block == NULL) { dev_dbg(emu->card->dev, @@ -88,110 +108,43 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, /* write blank samples at head */ offset = 0; - size = BLANK_HEAD_SIZE; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - size *= 2; - if (offset + size > blocksize) - return -EINVAL; - snd_emu10k1_synth_bzero(emu, sp->block, offset, size); - offset += size; - - /* copy start->loopend */ - size = loopend; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - size *= 2; - if (offset + size > blocksize) - return -EINVAL; - if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { - snd_emu10k1_synth_free(emu, sp->block); - sp->block = NULL; - return -EFAULT; - } + size = BLANK_HEAD_SIZE << shift; + snd_emu10k1_synth_memset(emu, sp->block, offset, size, fill); offset += size; - data += size; - -#if 0 /* not supported yet */ - /* handle reverse (or bidirectional) loop */ - if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) { - /* copy loop in reverse */ - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { - int woffset; - unsigned short *wblock = (unsigned short*)block; - woffset = offset / 2; - if (offset + loopsize * 2 > blocksize) - return -EINVAL; - for (i = 0; i < loopsize; i++) - wblock[woffset + i] = wblock[woffset - i -1]; - offset += loopsize * 2; - } else { - if (offset + loopsize > blocksize) - return -EINVAL; - for (i = 0; i < loopsize; i++) - block[offset + i] = block[offset - i -1]; - offset += loopsize; - } - /* modify loop pointers */ - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) { - sp->v.loopend += loopsize; - } else { - sp->v.loopstart += loopsize; - sp->v.loopend += loopsize; + /* copy provided samples */ + if (unroll && loop_end <= data_end) { + size = loop_end << shift; + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor)) + goto faulty; + offset += size; + + data += loop_start << shift; + while (--unroll > 0) { + size = loop_size << shift; + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor)) + goto faulty; + offset += size; } - /* add sample pointer */ - sp->v.end += loopsize; - } -#endif - /* loopend -> sample end */ - size = sp->v.size - loopend; - if (size < 0) - return -EINVAL; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - size *= 2; - if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { - snd_emu10k1_synth_free(emu, sp->block); - sp->block = NULL; - return -EFAULT; + size = (data_end - loop_start) << shift; + } else { + size = data_end << shift; } + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor)) + goto faulty; offset += size; /* clear rest of samples (if any) */ if (offset < blocksize) - snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset); - - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { - /* if no blank loop is attached in the sample, add it */ - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { - sp->v.loopstart = sp->v.end + BLANK_LOOP_START; - sp->v.loopend = sp->v.end + BLANK_LOOP_END; - } - } - -#if 0 /* not supported yet */ - if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) { - /* unsigned -> signed */ - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { - unsigned short *wblock = (unsigned short*)block; - for (i = 0; i < truesize; i++) - wblock[i] ^= 0x8000; - } else { - for (i = 0; i < truesize; i++) - block[i] ^= 0x80; - } - } -#endif - - /* recalculate offset */ - start_addr = BLANK_HEAD_SIZE * 2; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - start_addr >>= 1; - sp->v.start += start_addr; - sp->v.end += start_addr; - sp->v.loopstart += start_addr; - sp->v.loopend += start_addr; + snd_emu10k1_synth_memset(emu, sp->block, offset, blocksize - offset, fill); return 0; + +faulty: + snd_emu10k1_synth_free(emu, sp->block); + sp->block = NULL; + return -EFAULT; } /* diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index f4a1c2d4b078..b60ab5671e00 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -422,6 +422,59 @@ void snd_emu1010_update_clock(struct snd_emu10k1 *emu) snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, leds); } +void snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu, int dock, + const struct firmware *fw_entry) +{ + __always_unused u16 write_post; + + // On E-MU 1010 rev1 the FPGA is a Xilinx Spartan IIE XC2S50E. + // On E-MU 0404b it is a Xilinx Spartan III XC3S50. + // The wiring is as follows: + // GPO7 -> FPGA input & 1K resistor -> FPGA /PGMN <- FPGA output + // In normal operation, the active low reset line is held up by + // an FPGA output, while the GPO pin performs its duty as control + // register access strobe signal. Writing the respective bit to + // EMU_HANA_FPGA_CONFIG puts the FPGA output into high-Z mode, at + // which point the GPO pin can control the reset line through the + // resistor. + // GPO6 -> FPGA CCLK & FPGA input + // GPO5 -> FPGA DIN (dual function) + + // If the FPGA is already programmed, return it to programming mode + snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, + dock ? EMU_HANA_FPGA_CONFIG_AUDIODOCK : + EMU_HANA_FPGA_CONFIG_HANA); + + // Assert reset line for 100uS + outw(0x00, emu->port + A_GPIO); + write_post = inw(emu->port + A_GPIO); + udelay(100); + outw(0x80, emu->port + A_GPIO); + write_post = inw(emu->port + A_GPIO); + udelay(100); // Allow FPGA memory to clean + + // Upload the netlist. Keep reset line high! + for (int n = 0; n < fw_entry->size; n++) { + u8 value = fw_entry->data[n]; + for (int i = 0; i < 8; i++) { + u16 reg = 0x80; + if (value & 1) + reg |= 0x20; + value >>= 1; + outw(reg, emu->port + A_GPIO); + write_post = inw(emu->port + A_GPIO); + outw(reg | 0x40, emu->port + A_GPIO); + write_post = inw(emu->port + A_GPIO); + } + } + + // After programming, set GPIO bit 4 high again. + // This appears to be a config word that the rev1 Hana + // firmware reads; weird things happen without this. + outw(0x10, emu->port + A_GPIO); + write_post = inw(emu->port + A_GPIO); +} + void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb) { unsigned long flags; diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index 20b07117574b..d29711777161 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -565,15 +565,18 @@ static inline void *offset_ptr(struct snd_emu10k1 *emu, int page, int offset) } /* - * bzero(blk + offset, size) + * memset(blk + offset, value, size) */ -int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, - int offset, int size) +int snd_emu10k1_synth_memset(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, + int offset, int size, u8 value) { int page, nextofs, end_offset, temp, temp1; void *ptr; struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk; + if (snd_BUG_ON(offset + size > p->mem.size)) + return -EFAULT; + offset += blk->offset & (PAGE_SIZE - 1); end_offset = offset + size; page = get_aligned_page(offset); @@ -585,25 +588,55 @@ int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk temp = temp1; ptr = offset_ptr(emu, page + p->first_page, offset); if (ptr) - memset(ptr, 0, temp); + memset(ptr, value, temp); offset = nextofs; page++; } while (offset < end_offset); return 0; } -EXPORT_SYMBOL(snd_emu10k1_synth_bzero); +EXPORT_SYMBOL(snd_emu10k1_synth_memset); + +// Note that the value is assumed to be suitably repetitive. +static void xor_range(void *ptr, int size, u32 value) +{ + if ((long)ptr & 1) { + *(u8 *)ptr ^= (u8)value; + ptr++; + size--; + } + if (size > 1 && ((long)ptr & 2)) { + *(u16 *)ptr ^= (u16)value; + ptr += 2; + size -= 2; + } + while (size > 3) { + *(u32 *)ptr ^= value; + ptr += 4; + size -= 4; + } + if (size > 1) { + *(u16 *)ptr ^= (u16)value; + ptr += 2; + size -= 2; + } + if (size > 0) + *(u8 *)ptr ^= (u8)value; +} /* - * copy_from_user(blk + offset, data, size) + * copy_from_user(blk + offset, data, size) ^ xor */ int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_memblk *blk, - int offset, const char __user *data, int size) + int offset, const char __user *data, int size, u32 xor) { int page, nextofs, end_offset, temp, temp1; void *ptr; struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk; + if (snd_BUG_ON(offset + size > p->mem.size)) + return -EFAULT; + offset += blk->offset & (PAGE_SIZE - 1); end_offset = offset + size; page = get_aligned_page(offset); @@ -614,8 +647,12 @@ int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_me if (temp1 < temp) temp = temp1; ptr = offset_ptr(emu, page + p->first_page, offset); - if (ptr && copy_from_user(ptr, data, temp)) - return -EFAULT; + if (ptr) { + if (copy_from_user(ptr, data, temp)) + return -EFAULT; + if (xor) + xor_range(ptr, temp, xor); + } offset = nextofs; data += temp; page++; |