diff options
author | Andreas Mohr <andi@lisas.de> | 2010-12-27 15:17:35 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-01-02 05:11:38 -0500 |
commit | 689c69120ea3c8db069e11a7065ceffee90d0460 (patch) | |
tree | 8900047e2c2612df60d763de78e9b430ee96b59e /sound/pci/azt3328.c | |
parent | da237f35a8a503fb8893fb3b9d0622a991bcebef (diff) |
ALSA: azt3328: improve snd_azf3328_codec_setdmaa()
- add some WARN_ONCE
- add multi-I/O helper (and use helper struct)
- fix off-by-1 DMA length bug
- better variable naming
Signed-off-by: Andreas Mohr <andi@lisas.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/azt3328.c')
-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 76ff5fdb4838..6117595fc075 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c | |||
@@ -175,6 +175,7 @@ | |||
175 | 175 | ||
176 | #include <asm/io.h> | 176 | #include <asm/io.h> |
177 | #include <linux/init.h> | 177 | #include <linux/init.h> |
178 | #include <linux/bug.h> /* WARN_ONCE */ | ||
178 | #include <linux/pci.h> | 179 | #include <linux/pci.h> |
179 | #include <linux/delay.h> | 180 | #include <linux/delay.h> |
180 | #include <linux/slab.h> | 181 | #include <linux/slab.h> |
@@ -421,6 +422,21 @@ snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec, | |||
421 | outl(value, codec->io_base + reg); | 422 | outl(value, codec->io_base + reg); |
422 | } | 423 | } |
423 | 424 | ||
425 | static inline void | ||
426 | snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec, | ||
427 | unsigned reg, const void *buffer, int count | ||
428 | ) | ||
429 | { | ||
430 | unsigned long addr = codec->io_base + reg; | ||
431 | if (count) { | ||
432 | const u32 *buf = buffer; | ||
433 | do { | ||
434 | outl(*buf++, addr); | ||
435 | addr += 4; | ||
436 | } while (--count); | ||
437 | } | ||
438 | } | ||
439 | |||
424 | static inline u32 | 440 | static inline u32 |
425 | snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg) | 441 | snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg) |
426 | { | 442 | { |
@@ -1124,34 +1140,54 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip, | |||
1124 | static void | 1140 | static void |
1125 | snd_azf3328_codec_setdmaa(struct snd_azf3328_codec_data *codec, | 1141 | snd_azf3328_codec_setdmaa(struct snd_azf3328_codec_data *codec, |
1126 | unsigned long addr, | 1142 | unsigned long addr, |
1127 | unsigned int count, | 1143 | unsigned int period_bytes, |
1128 | unsigned int size | 1144 | unsigned int buffer_bytes |
1129 | ) | 1145 | ) |
1130 | { | 1146 | { |
1131 | snd_azf3328_dbgcallenter(); | 1147 | snd_azf3328_dbgcallenter(); |
1148 | WARN_ONCE(period_bytes & 1, "odd period length!?\n"); | ||
1149 | WARN_ONCE(buffer_bytes != 2 * period_bytes, | ||
1150 | "missed our input expectations! %u vs. %u\n", | ||
1151 | buffer_bytes, period_bytes); | ||
1132 | if (!codec->running) { | 1152 | if (!codec->running) { |
1133 | /* AZF3328 uses a two buffer pointer DMA transfer approach */ | 1153 | /* AZF3328 uses a two buffer pointer DMA transfer approach */ |
1134 | 1154 | ||
1135 | unsigned long flags, addr_area2; | 1155 | unsigned long flags; |
1136 | 1156 | ||
1137 | /* width 32bit (prevent overflow): */ | 1157 | /* width 32bit (prevent overflow): */ |
1138 | u32 count_areas, lengths; | 1158 | u32 area_length; |
1159 | struct codec_setup_io { | ||
1160 | u32 dma_start_1; | ||
1161 | u32 dma_start_2; | ||
1162 | u32 dma_lengths; | ||
1163 | } __attribute__((packed)) setup_io; | ||
1139 | 1164 | ||
1140 | count_areas = size/2; | 1165 | area_length = buffer_bytes/2; |
1141 | addr_area2 = addr+count_areas; | ||
1142 | snd_azf3328_dbgcodec("setdma: buffers %08lx[%u] / %08lx[%u]\n", | ||
1143 | addr, count_areas, addr_area2, count_areas); | ||
1144 | 1166 | ||
1145 | count_areas--; /* max. index */ | 1167 | setup_io.dma_start_1 = addr; |
1168 | setup_io.dma_start_2 = addr+area_length; | ||
1169 | |||
1170 | snd_azf3328_dbgcodec( | ||
1171 | "setdma: buffers %08x[%u] / %08x[%u], %u, %u\n", | ||
1172 | setup_io.dma_start_1, area_length, | ||
1173 | setup_io.dma_start_2, area_length, | ||
1174 | period_bytes, buffer_bytes); | ||
1175 | |||
1176 | /* Hmm, are we really supposed to decrement this by 1?? | ||
1177 | Most definitely certainly not: configuring full length does | ||
1178 | work properly (i.e. likely better), and BTW we | ||
1179 | violated possibly differing frame sizes with this... | ||
1180 | |||
1181 | area_length--; |* max. index *| | ||
1182 | */ | ||
1146 | 1183 | ||
1147 | /* build combined I/O buffer length word */ | 1184 | /* build combined I/O buffer length word */ |
1148 | lengths = (count_areas << 16) | (count_areas); | 1185 | setup_io.dma_lengths = (area_length << 16) | (area_length); |
1186 | |||
1149 | spin_lock_irqsave(codec->lock, flags); | 1187 | spin_lock_irqsave(codec->lock, flags); |
1150 | snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_1, addr); | 1188 | snd_azf3328_codec_outl_multi( |
1151 | snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_2, | 1189 | codec, IDX_IO_CODEC_DMA_START_1, &setup_io, 3 |
1152 | addr_area2); | 1190 | ); |
1153 | snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_LENGTHS, | ||
1154 | lengths); | ||
1155 | spin_unlock_irqrestore(codec->lock, flags); | 1191 | spin_unlock_irqrestore(codec->lock, flags); |
1156 | } | 1192 | } |
1157 | snd_azf3328_dbgcallleave(); | 1193 | snd_azf3328_dbgcallleave(); |