diff options
Diffstat (limited to 'sound/pci/intel8x0.c')
| -rw-r--r-- | sound/pci/intel8x0.c | 105 | 
1 files changed, 78 insertions, 27 deletions
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 57648810eaf..173bebf9f51 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -355,6 +355,9 @@ struct ichdev {          unsigned int fragsize1;          unsigned int position;  	unsigned int pos_shift; +	unsigned int last_pos; +	unsigned long last_pos_jiffies; +	unsigned int jiffy_to_bytes;          int frags;          int lvi;          int lvi_frag; @@ -838,7 +841,10 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd  		ichdev->suspended = 0;  		/* fallthru */  	case SNDRV_PCM_TRIGGER_START: +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:  		val = ICH_IOCE | ICH_STARTBM; +		ichdev->last_pos = ichdev->position; +		ichdev->last_pos_jiffies = jiffies;  		break;  	case SNDRV_PCM_TRIGGER_SUSPEND:  		ichdev->suspended = 1; @@ -849,9 +855,6 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd  	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:  		val = ICH_IOCE;  		break; -	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -		val = ICH_IOCE | ICH_STARTBM; -		break;  	default:  		return -EINVAL;  	} @@ -1045,6 +1048,7 @@ static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream)  			ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;  	}  	snd_intel8x0_setup_periods(chip, ichdev); +	ichdev->jiffy_to_bytes = (runtime->rate * 4 * ichdev->pos_shift) / HZ;  	return 0;  } @@ -1053,7 +1057,7 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs  	struct intel8x0 *chip = snd_pcm_substream_chip(substream);  	struct ichdev *ichdev = get_ichdev(substream);  	size_t ptr1, ptr; -	int civ, timeout = 100; +	int civ, timeout = 10;  	unsigned int position;  	spin_lock(&chip->reg_lock); @@ -1069,9 +1073,19 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(struct snd_pcm_substream *subs  		    ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))  			break;  	} while (timeout--); -	ptr1 <<= ichdev->pos_shift; -	ptr = ichdev->fragsize1 - ptr1; -	ptr += position; +	if (ptr1 != 0) { +		ptr1 <<= ichdev->pos_shift; +		ptr = ichdev->fragsize1 - ptr1; +		ptr += position; +		ichdev->last_pos = ptr; +		ichdev->last_pos_jiffies = jiffies; +	} else { +		ptr1 = jiffies - ichdev->last_pos_jiffies; +		if (ptr1) +			ptr1 -= 1; +		ptr = ichdev->last_pos + ptr1 * ichdev->jiffy_to_bytes; +		ptr %= ichdev->size; +	}  	spin_unlock(&chip->reg_lock);  	if (ptr >= ichdev->size)  		return 0; @@ -1840,6 +1854,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {  	},  	{  		.subvendor = 0x1028, +		.subdevice = 0x016a, +		.name = "Dell Inspiron 8600",	/* STAC9750/51 */ +		.type = AC97_TUNE_HP_ONLY +	}, +	{ +		.subvendor = 0x1028,  		.subdevice = 0x0186,  		.name = "Dell Latitude D810", /* cf. Malone #41015 */  		.type = AC97_TUNE_HP_MUTE_LED @@ -1882,12 +1902,6 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {  	},  	{  		.subvendor = 0x103c, -		.subdevice = 0x0934, -		.name = "HP nx8220", -		.type = AC97_TUNE_MUTE_LED -	}, -	{ -		.subvendor = 0x103c,  		.subdevice = 0x129d,  		.name = "HP xw8000",  		.type = AC97_TUNE_HP_ONLY @@ -2661,12 +2675,14 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)  	struct snd_pcm_substream *subs;  	struct ichdev *ichdev;  	unsigned long port; -	unsigned long pos, t; -	struct timeval start_time, stop_time; +	unsigned long pos, pos1, t; +	int civ, timeout = 1000, attempt = 1; +	struct timespec start_time, stop_time;  	if (chip->ac97_bus->clock != 48000)  		return; /* specified in module option */ +      __again:  	subs = chip->pcm[0]->streams[0].substream;  	if (! subs || subs->dma_buffer.bytes < INTEL8X0_TESTBUF_SIZE) {  		snd_printk(KERN_WARNING "no playback buffer allocated - aborting measure ac97 clock\n"); @@ -2674,7 +2690,7 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)  	}  	ichdev = &chip->ichd[ICHD_PCMOUT];  	ichdev->physbuf = subs->dma_buffer.addr; -	ichdev->size = chip->ichd[ICHD_PCMOUT].fragsize = INTEL8X0_TESTBUF_SIZE; +	ichdev->size = ichdev->fragsize = INTEL8X0_TESTBUF_SIZE;  	ichdev->substream = NULL; /* don't process interrupts */  	/* set rate */ @@ -2693,16 +2709,31 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)  		iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);  		iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);  	} -	do_gettimeofday(&start_time); +	do_posix_clock_monotonic_gettime(&start_time);  	spin_unlock_irq(&chip->reg_lock);  	msleep(50);  	spin_lock_irq(&chip->reg_lock);  	/* check the position */ -	pos = ichdev->fragsize1; -	pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << ichdev->pos_shift; -	pos += ichdev->position; +	do { +		civ = igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV); +		pos1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb); +		if (pos1 == 0) { +			udelay(10); +			continue; +		} +		if (civ == igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV) && +		    pos1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb)) +			break; +	} while (timeout--); +	if (pos1 == 0) {	/* oops, this value is not reliable */ +		pos = 0; +	} else { +		pos = ichdev->fragsize1; +		pos -= pos1 << ichdev->pos_shift; +		pos += ichdev->position; +	}  	chip->in_measurement = 0; -	do_gettimeofday(&stop_time); +	do_posix_clock_monotonic_gettime(&stop_time);  	/* stop */  	if (chip->device_type == DEVICE_ALI) {  		iputdword(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 16)); @@ -2717,22 +2748,42 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)  	iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);  	spin_unlock_irq(&chip->reg_lock); +	if (pos == 0) { +		snd_printk(KERN_ERR "intel8x0: measure - unreliable DMA position..\n"); +	      __retry: +		if (attempt < 3) { +			msleep(300); +			attempt++; +			goto __again; +		} +		goto __end; +	} + +	pos /= 4;  	t = stop_time.tv_sec - start_time.tv_sec;  	t *= 1000000; -	t += stop_time.tv_usec - start_time.tv_usec; -	printk(KERN_INFO "%s: measured %lu usecs\n", __func__, t); +	t += (stop_time.tv_nsec - start_time.tv_nsec) / 1000; +	printk(KERN_INFO "%s: measured %lu usecs (%lu samples)\n", __func__, t, pos);  	if (t == 0) { -		snd_printk(KERN_ERR "?? calculation error..\n"); -		return; +		snd_printk(KERN_ERR "intel8x0: ?? calculation error..\n"); +		goto __retry;  	} -	pos = (pos / 4) * 1000; +	pos *= 1000;  	pos = (pos / t) * 1000 + ((pos % t) * 1000) / t; -	if (pos < 40000 || pos >= 60000)  +	if (pos < 40000 || pos >= 60000) {  		/* abnormal value. hw problem? */  		printk(KERN_INFO "intel8x0: measured clock %ld rejected\n", pos); +		goto __retry; +	} else if (pos > 40500 && pos < 41500) +		/* first exception - 41000Hz reference clock */ +		chip->ac97_bus->clock = 41000; +	else if (pos > 43600 && pos < 44600) +		/* second exception - 44100HZ reference clock */ +		chip->ac97_bus->clock = 44100;  	else if (pos < 47500 || pos > 48500)  		/* not 48000Hz, tuning the clock.. */  		chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos; +      __end:  	printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97_bus->clock);  	snd_ac97_update_power(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 0);  }  |