diff options
-rw-r--r-- | sound/pci/azt3328.c | 66 |
1 files changed, 51 insertions, 15 deletions
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 76ff5fdb483..6117595fc07 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -175,6 +175,7 @@ #include <asm/io.h> #include <linux/init.h> +#include <linux/bug.h> /* WARN_ONCE */ #include <linux/pci.h> #include <linux/delay.h> #include <linux/slab.h> @@ -421,6 +422,21 @@ snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec, outl(value, codec->io_base + reg); } +static inline void +snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec, + unsigned reg, const void *buffer, int count +) +{ + unsigned long addr = codec->io_base + reg; + if (count) { + const u32 *buf = buffer; + do { + outl(*buf++, addr); + addr += 4; + } while (--count); + } +} + static inline u32 snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg) { @@ -1124,34 +1140,54 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip, static void snd_azf3328_codec_setdmaa(struct snd_azf3328_codec_data *codec, unsigned long addr, - unsigned int count, - unsigned int size + unsigned int period_bytes, + unsigned int buffer_bytes ) { snd_azf3328_dbgcallenter(); + WARN_ONCE(period_bytes & 1, "odd period length!?\n"); + WARN_ONCE(buffer_bytes != 2 * period_bytes, + "missed our input expectations! %u vs. %u\n", + buffer_bytes, period_bytes); if (!codec->running) { /* AZF3328 uses a two buffer pointer DMA transfer approach */ - unsigned long flags, addr_area2; + unsigned long flags; /* width 32bit (prevent overflow): */ - u32 count_areas, lengths; + u32 area_length; + struct codec_setup_io { + u32 dma_start_1; + u32 dma_start_2; + u32 dma_lengths; + } __attribute__((packed)) setup_io; - count_areas = size/2; - addr_area2 = addr+count_areas; - snd_azf3328_dbgcodec("setdma: buffers %08lx[%u] / %08lx[%u]\n", - addr, count_areas, addr_area2, count_areas); + area_length = buffer_bytes/2; - count_areas--; /* max. index */ + setup_io.dma_start_1 = addr; + setup_io.dma_start_2 = addr+area_length; + + snd_azf3328_dbgcodec( + "setdma: buffers %08x[%u] / %08x[%u], %u, %u\n", + setup_io.dma_start_1, area_length, + setup_io.dma_start_2, area_length, + period_bytes, buffer_bytes); + + /* Hmm, are we really supposed to decrement this by 1?? + Most definitely certainly not: configuring full length does + work properly (i.e. likely better), and BTW we + violated possibly differing frame sizes with this... + + area_length--; |* max. index *| + */ /* build combined I/O buffer length word */ - lengths = (count_areas << 16) | (count_areas); + setup_io.dma_lengths = (area_length << 16) | (area_length); + spin_lock_irqsave(codec->lock, flags); - snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_1, addr); - snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_2, - addr_area2); - snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_LENGTHS, - lengths); + snd_azf3328_codec_outl_multi( + codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3 + ); spin_unlock_irqrestore(codec->lock, flags); } snd_azf3328_dbgcallleave(); |