diff options
author | Jaroslav Kysela <perex@perex.cz> | 2009-04-13 14:45:42 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2009-04-13 15:28:27 -0400 |
commit | 920e4ae31cb113328e617f4a0663fb17d7b09124 (patch) | |
tree | 1e58bfffcb3b244d056e26026a1a73a43bb5f385 /sound/pci | |
parent | bbf6ad1399e9516b0a95de3ad58ffbaed670e4cc (diff) |
[ALSA] intel8x0: an attempt to make ac97_clock measurement more reliable
- use monotonic posix clock to measure time
- try to avoid reading zero from PICB (position in current buffer) register
- show also measured samples
- when clock is near 41000 or 44100, use exactly these values
(they appears to be reference clocks for hardware manufacturers)
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound/pci')
-rw-r--r-- | sound/pci/intel8x0.c | 37 |
1 files changed, 28 insertions, 9 deletions
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 57648810eaf1..c86ff499460b 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c | |||
@@ -2661,8 +2661,9 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip) | |||
2661 | struct snd_pcm_substream *subs; | 2661 | struct snd_pcm_substream *subs; |
2662 | struct ichdev *ichdev; | 2662 | struct ichdev *ichdev; |
2663 | unsigned long port; | 2663 | unsigned long port; |
2664 | unsigned long pos, t; | 2664 | unsigned long pos, pos1, t; |
2665 | struct timeval start_time, stop_time; | 2665 | int civ, timeout = 1000; |
2666 | struct timespec start_time, stop_time; | ||
2666 | 2667 | ||
2667 | if (chip->ac97_bus->clock != 48000) | 2668 | if (chip->ac97_bus->clock != 48000) |
2668 | return; /* specified in module option */ | 2669 | return; /* specified in module option */ |
@@ -2693,16 +2694,27 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip) | |||
2693 | iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE); | 2694 | iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE); |
2694 | iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot); | 2695 | iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot); |
2695 | } | 2696 | } |
2696 | do_gettimeofday(&start_time); | 2697 | do_posix_clock_monotonic_gettime(&start_time); |
2697 | spin_unlock_irq(&chip->reg_lock); | 2698 | spin_unlock_irq(&chip->reg_lock); |
2698 | msleep(50); | 2699 | msleep(50); |
2699 | spin_lock_irq(&chip->reg_lock); | 2700 | spin_lock_irq(&chip->reg_lock); |
2700 | /* check the position */ | 2701 | /* check the position */ |
2702 | do { | ||
2703 | civ = igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV); | ||
2704 | pos1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb); | ||
2705 | if (pos1 == 0) { | ||
2706 | udelay(10); | ||
2707 | continue; | ||
2708 | } | ||
2709 | if (civ == igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV) && | ||
2710 | pos1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb)) | ||
2711 | break; | ||
2712 | } while (timeout--); | ||
2701 | pos = ichdev->fragsize1; | 2713 | pos = ichdev->fragsize1; |
2702 | pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << ichdev->pos_shift; | 2714 | pos -= pos1 << ichdev->pos_shift; |
2703 | pos += ichdev->position; | 2715 | pos += ichdev->position; |
2704 | chip->in_measurement = 0; | 2716 | chip->in_measurement = 0; |
2705 | do_gettimeofday(&stop_time); | 2717 | do_posix_clock_monotonic_gettime(&stop_time); |
2706 | /* stop */ | 2718 | /* stop */ |
2707 | if (chip->device_type == DEVICE_ALI) { | 2719 | if (chip->device_type == DEVICE_ALI) { |
2708 | iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 16)); | 2720 | iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 16)); |
@@ -2717,19 +2729,26 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip) | |||
2717 | iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS); | 2729 | iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS); |
2718 | spin_unlock_irq(&chip->reg_lock); | 2730 | spin_unlock_irq(&chip->reg_lock); |
2719 | 2731 | ||
2732 | pos /= 4; | ||
2720 | t = stop_time.tv_sec - start_time.tv_sec; | 2733 | t = stop_time.tv_sec - start_time.tv_sec; |
2721 | t *= 1000000; | 2734 | t *= 1000000; |
2722 | t += stop_time.tv_usec - start_time.tv_usec; | 2735 | t += (stop_time.tv_nsec - start_time.tv_nsec) / 1000; |
2723 | printk(KERN_INFO "%s: measured %lu usecs\n", __func__, t); | 2736 | printk(KERN_INFO "%s: measured %lu usecs (%lu samples)\n", __func__, t, pos); |
2724 | if (t == 0) { | 2737 | if (t == 0) { |
2725 | snd_printk(KERN_ERR "?? calculation error..\n"); | 2738 | snd_printk(KERN_ERR "intel8x0: ?? calculation error..\n"); |
2726 | return; | 2739 | return; |
2727 | } | 2740 | } |
2728 | pos = (pos / 4) * 1000; | 2741 | pos *= 1000; |
2729 | pos = (pos / t) * 1000 + ((pos % t) * 1000) / t; | 2742 | pos = (pos / t) * 1000 + ((pos % t) * 1000) / t; |
2730 | if (pos < 40000 || pos >= 60000) | 2743 | if (pos < 40000 || pos >= 60000) |
2731 | /* abnormal value. hw problem? */ | 2744 | /* abnormal value. hw problem? */ |
2732 | printk(KERN_INFO "intel8x0: measured clock %ld rejected\n", pos); | 2745 | printk(KERN_INFO "intel8x0: measured clock %ld rejected\n", pos); |
2746 | else if (pos > 40500 || pos < 41500) | ||
2747 | /* first exception - 41000Hz reference clock */ | ||
2748 | chip->ac97_bus->clock = 41000; | ||
2749 | else if (pos > 43600 || pos < 44600) | ||
2750 | /* second exception - 44100HZ reference clock */ | ||
2751 | chip->ac97_bus->clock = 44100; | ||
2733 | else if (pos < 47500 || pos > 48500) | 2752 | else if (pos < 47500 || pos > 48500) |
2734 | /* not 48000Hz, tuning the clock.. */ | 2753 | /* not 48000Hz, tuning the clock.. */ |
2735 | chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos; | 2754 | chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos; |