aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/cmipci.c
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2007-09-17 03:41:36 -0400
committerJaroslav Kysela <perex@perex.cz>2007-10-16 10:50:36 -0400
commitc36fd8c3cd682fa9bbe5b2cb4b99e16625a37c94 (patch)
tree5fd2b7fbf3c29e915370dc504d7853c5a88487e4 /sound/pci/cmipci.c
parentebe9e289d870816b5d5dd6992a253f9a87f26cba (diff)
[ALSA] cmipci: fix distortion on rear channels
When playing multichannel data, the rear channels can get distorted if the last sample of the last played stereo stream was not zero. To avoid this, add a hack to play a few silence samples after the stream is stopped. Signed-off-by: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound/pci/cmipci.c')
-rw-r--r--sound/pci/cmipci.c68
1 files changed, 67 insertions, 1 deletions
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 085a36751ac0..6832649879ce 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -434,6 +434,7 @@ struct cmipci_pcm {
434 u8 running; /* dac/adc running? */ 434 u8 running; /* dac/adc running? */
435 u8 fmt; /* format bits */ 435 u8 fmt; /* format bits */
436 u8 is_dac; 436 u8 is_dac;
437 u8 needs_silencing;
437 unsigned int dma_size; /* in frames */ 438 unsigned int dma_size; /* in frames */
438 unsigned int shift; 439 unsigned int shift;
439 unsigned int ch; /* channel (0/1) */ 440 unsigned int ch; /* channel (0/1) */
@@ -903,6 +904,7 @@ static int snd_cmipci_pcm_trigger(struct cmipci *cm, struct cmipci_pcm *rec,
903 cm->ctrl &= ~chen; 904 cm->ctrl &= ~chen;
904 snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset); 905 snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset);
905 snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~reset); 906 snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~reset);
907 rec->needs_silencing = rec->is_dac;
906 break; 908 break;
907 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 909 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
908 case SNDRV_PCM_TRIGGER_SUSPEND: 910 case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -1304,11 +1306,75 @@ static int snd_cmipci_playback_spdif_prepare(struct snd_pcm_substream *substream
1304 return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream); 1306 return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream);
1305} 1307}
1306 1308
1309/*
1310 * Apparently, the samples last played on channel A stay in some buffer, even
1311 * after the channel is reset, and get added to the data for the rear DACs when
1312 * playing a multichannel stream on channel B. This is likely to generate
1313 * wraparounds and thus distortions.
1314 * To avoid this, we play at least one zero sample after the actual stream has
1315 * stopped.
1316 */
1317static void snd_cmipci_silence_hack(struct cmipci *cm, struct cmipci_pcm *rec)
1318{
1319 struct snd_pcm_runtime *runtime = rec->substream->runtime;
1320 unsigned int reg, val;
1321
1322 if (rec->needs_silencing && runtime && runtime->dma_area) {
1323 /* set up a small silence buffer */
1324 memset(runtime->dma_area, 0, PAGE_SIZE);
1325 reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2;
1326 val = ((PAGE_SIZE / 4) - 1) | (((PAGE_SIZE / 4) / 2 - 1) << 16);
1327 snd_cmipci_write(cm, reg, val);
1328
1329 /* configure for 16 bits, 2 channels, 8 kHz */
1330 if (runtime->channels > 2)
1331 set_dac_channels(cm, rec, 2);
1332 spin_lock_irq(&cm->reg_lock);
1333 val = snd_cmipci_read(cm, CM_REG_FUNCTRL1);
1334 val &= ~(CM_ASFC_MASK << (rec->ch * 3));
1335 val |= (4 << CM_ASFC_SHIFT) << (rec->ch * 3);
1336 snd_cmipci_write(cm, CM_REG_FUNCTRL1, val);
1337 val = snd_cmipci_read(cm, CM_REG_CHFORMAT);
1338 val &= ~(CM_CH0FMT_MASK << (rec->ch * 2));
1339 val |= (3 << CM_CH0FMT_SHIFT) << (rec->ch * 2);
1340 if (cm->chip_version == 68) {
1341 val &= ~(CM_CH0_SRATE_88K << (rec->ch * 2));
1342 val &= ~(CM_CH0_SRATE_96K << (rec->ch * 2));
1343 }
1344 snd_cmipci_write(cm, CM_REG_CHFORMAT, val);
1345
1346 /* start stream (we don't need interrupts) */
1347 cm->ctrl |= CM_CHEN0 << rec->ch;
1348 snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);
1349 spin_unlock_irq(&cm->reg_lock);
1350
1351 msleep(1);
1352
1353 /* stop and reset stream */
1354 spin_lock_irq(&cm->reg_lock);
1355 cm->ctrl &= ~(CM_CHEN0 << rec->ch);
1356 val = CM_RST_CH0 << rec->ch;
1357 snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | val);
1358 snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~val);
1359 spin_unlock_irq(&cm->reg_lock);
1360
1361 rec->needs_silencing = 0;
1362 }
1363}
1364
1307static int snd_cmipci_playback_hw_free(struct snd_pcm_substream *substream) 1365static int snd_cmipci_playback_hw_free(struct snd_pcm_substream *substream)
1308{ 1366{
1309 struct cmipci *cm = snd_pcm_substream_chip(substream); 1367 struct cmipci *cm = snd_pcm_substream_chip(substream);
1310 setup_spdif_playback(cm, substream, 0, 0); 1368 setup_spdif_playback(cm, substream, 0, 0);
1311 restore_mixer_state(cm); 1369 restore_mixer_state(cm);
1370 snd_cmipci_silence_hack(cm, &cm->channel[0]);
1371 return snd_cmipci_hw_free(substream);
1372}
1373
1374static int snd_cmipci_playback2_hw_free(struct snd_pcm_substream *substream)
1375{
1376 struct cmipci *cm = snd_pcm_substream_chip(substream);
1377 snd_cmipci_silence_hack(cm, &cm->channel[1]);
1312 return snd_cmipci_hw_free(substream); 1378 return snd_cmipci_hw_free(substream);
1313} 1379}
1314 1380
@@ -1736,7 +1802,7 @@ static struct snd_pcm_ops snd_cmipci_playback2_ops = {
1736 .close = snd_cmipci_playback2_close, 1802 .close = snd_cmipci_playback2_close,
1737 .ioctl = snd_pcm_lib_ioctl, 1803 .ioctl = snd_pcm_lib_ioctl,
1738 .hw_params = snd_cmipci_playback2_hw_params, 1804 .hw_params = snd_cmipci_playback2_hw_params,
1739 .hw_free = snd_cmipci_hw_free, 1805 .hw_free = snd_cmipci_playback2_hw_free,
1740 .prepare = snd_cmipci_capture_prepare, /* channel B */ 1806 .prepare = snd_cmipci_capture_prepare, /* channel B */
1741 .trigger = snd_cmipci_capture_trigger, /* channel B */ 1807 .trigger = snd_cmipci_capture_trigger, /* channel B */
1742 .pointer = snd_cmipci_capture_pointer, /* channel B */ 1808 .pointer = snd_cmipci_capture_pointer, /* channel B */