trident.c

来自「linux 内核源代码」· C语言 代码 · 共 2,292 行 · 第 1/5 页

C
2,292
字号
		/* FIXME: figure out all this OSS fragment stuff */		bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt];		bufsize = PAGE_SIZE << dmabuf->buforder;		if (dmabuf->ossfragshift) {			if ((1000 << dmabuf->ossfragshift) < bytepersec)				dmabuf->fragshift = ld2(bytepersec / 1000);			else				dmabuf->fragshift = dmabuf->ossfragshift;		} else {			/* lets hand out reasonable big ass buffers by default */			dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT - 2);		}		dmabuf->numfrag = bufsize >> dmabuf->fragshift;		while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) {			dmabuf->fragshift--;			dmabuf->numfrag = bufsize >> dmabuf->fragshift;		}		dmabuf->fragsize = 1 << dmabuf->fragshift;		if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag)			dmabuf->numfrag = dmabuf->ossmaxfrags;		dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt];		dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift;		memset(dmabuf->rawbuf, (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80,		       dmabuf->dmasize);		spin_lock_irqsave(&s->card->lock, flags);		if (rec == DM_RECORD)			trident_rec_setup(s);		else /* DM_PLAYBACK */			trident_play_setup(s);		spin_unlock_irqrestore(&s->card->lock, flags);		/* set the ready flag for the dma buffer */		dmabuf->ready = 1;		pr_debug("trident: prog_dmabuf(%d), sample rate = %d, "			 "format = %d, numfrag = %d, fragsize = %d "			 "dmasize = %d\n", dmabuf->channel->num,			 dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,			 dmabuf->fragsize, dmabuf->dmasize);	}	unlock_set_fmt(state);	return 0;}static inline int prog_dmabuf_record(struct trident_state* state){	return prog_dmabuf(state, DM_RECORD);}static inline int prog_dmabuf_playback(struct trident_state* state){	return prog_dmabuf(state, DM_PLAYBACK);}/* we are doing quantum mechanics here, the buffer can only be empty, half or full filled i.e.   |------------|------------|   or   |xxxxxxxxxxxx|------------|   or   |xxxxxxxxxxxx|xxxxxxxxxxxx|   but we almost always get this   |xxxxxx------|------------|   or   |xxxxxxxxxxxx|xxxxx-------|   so we have to clear the tail space to "silence"   |xxxxxx000000|------------|   or   |xxxxxxxxxxxx|xxxxxx000000|*/static voidtrident_clear_tail(struct trident_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	unsigned swptr;	unsigned char silence = (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80;	unsigned int len;	unsigned long flags;	spin_lock_irqsave(&state->card->lock, flags);	swptr = dmabuf->swptr;	spin_unlock_irqrestore(&state->card->lock, flags);	if (swptr == 0 || swptr == dmabuf->dmasize / 2 ||	    swptr == dmabuf->dmasize)		return;	if (swptr < dmabuf->dmasize / 2)		len = dmabuf->dmasize / 2 - swptr;	else		len = dmabuf->dmasize - swptr;	memset(dmabuf->rawbuf + swptr, silence, len);	if (state->card->pci_id != PCI_DEVICE_ID_ALI_5451) {		spin_lock_irqsave(&state->card->lock, flags);		dmabuf->swptr += len;		dmabuf->count += len;		spin_unlock_irqrestore(&state->card->lock, flags);	}	/* restart the dma machine in case it is halted */	start_dac(state);}static intdrain_dac(struct trident_state *state, int nonblock){	DECLARE_WAITQUEUE(wait, current);	struct dmabuf *dmabuf = &state->dmabuf;	unsigned long flags;	unsigned long tmo;	int count;	unsigned long diff = 0;	if (dmabuf->mapped || !dmabuf->ready)		return 0;	add_wait_queue(&dmabuf->wait, &wait);	for (;;) {		/* It seems that we have to set the current state to TASK_INTERRUPTIBLE		   every time to make the process really go to sleep */		set_current_state(TASK_INTERRUPTIBLE);		spin_lock_irqsave(&state->card->lock, flags);		count = dmabuf->count;		spin_unlock_irqrestore(&state->card->lock, flags);		if (count <= 0)			break;		if (signal_pending(current))			break;		if (nonblock) {			remove_wait_queue(&dmabuf->wait, &wait);			set_current_state(TASK_RUNNING);			return -EBUSY;		}		/* No matter how much data is left in the buffer, we have to wait until		   CSO == ESO/2 or CSO == ESO when address engine interrupts */		if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451 ||		    state->card->pci_id == PCI_DEVICE_ID_INTERG_5050) {			diff = dmabuf->swptr - trident_get_dma_addr(state) + dmabuf->dmasize;			diff = diff % (dmabuf->dmasize);			tmo = (diff * HZ) / dmabuf->rate;		} else {			tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;		}		tmo >>= sample_shift[dmabuf->fmt];		if (!schedule_timeout(tmo ? tmo : 1) && tmo) {			break;		}	}	remove_wait_queue(&dmabuf->wait, &wait);	set_current_state(TASK_RUNNING);	if (signal_pending(current))		return -ERESTARTSYS;	return 0;}/* update buffer manangement pointers, especially, *//* dmabuf->count and dmabuf->hwptr */static voidtrident_update_ptr(struct trident_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	unsigned hwptr, swptr;	int clear_cnt = 0;	int diff;	unsigned char silence;	unsigned half_dmasize;	/* update hardware pointer */	hwptr = trident_get_dma_addr(state);	diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;	dmabuf->hwptr = hwptr;	dmabuf->total_bytes += diff;	/* error handling and process wake up for ADC */	if (dmabuf->enable == ADC_RUNNING) {		if (dmabuf->mapped) {			dmabuf->count -= diff;			if (dmabuf->count >= (signed) dmabuf->fragsize)				wake_up(&dmabuf->wait);		} else {			dmabuf->count += diff;			if (dmabuf->count < 0 ||			    dmabuf->count > dmabuf->dmasize) {				/* buffer underrun or buffer overrun, */				/* we have no way to recover it here, just */				/* stop the machine and let the process */				/* force hwptr and swptr to sync */				__stop_adc(state);				dmabuf->error++;			}			if (dmabuf->count < (signed) dmabuf->dmasize / 2)				wake_up(&dmabuf->wait);		}	}	/* error handling and process wake up for DAC */	if (dmabuf->enable == DAC_RUNNING) {		if (dmabuf->mapped) {			dmabuf->count += diff;			if (dmabuf->count >= (signed) dmabuf->fragsize)				wake_up(&dmabuf->wait);		} else {			dmabuf->count -= diff;			if (dmabuf->count < 0 ||			    dmabuf->count > dmabuf->dmasize) {				/* buffer underrun or buffer overrun, we have no way to recover				   it here, just stop the machine and let the process force hwptr				   and swptr to sync */				__stop_dac(state);				dmabuf->error++;			} else if (!dmabuf->endcleared) {				swptr = dmabuf->swptr;				silence = (dmabuf->fmt & TRIDENT_FMT_16BIT ? 0 : 0x80);				if (dmabuf->update_flag & ALI_ADDRESS_INT_UPDATE) {					/* We must clear end data of 1/2 dmabuf if needed.					   According to 1/2 algorithm of Address Engine Interrupt,					   check the validation of the data of half dmasize. */					half_dmasize = dmabuf->dmasize / 2;					if ((diff = hwptr - half_dmasize) < 0)						diff = hwptr;					if ((dmabuf->count + diff) < half_dmasize) {						//there is invalid data in the end of half buffer						if ((clear_cnt = half_dmasize - swptr) < 0)							clear_cnt += half_dmasize;						//clear the invalid data						memset(dmabuf->rawbuf + swptr, silence, clear_cnt);						if (state->chans_num == 6) {							clear_cnt = clear_cnt / 2;							swptr = swptr / 2;							memset(state->other_states[0]->dmabuf.rawbuf + swptr,							       silence, clear_cnt);							memset(state->other_states[1]->dmabuf.rawbuf + swptr,							       silence, clear_cnt);							memset(state->other_states[2]->dmabuf.rawbuf + swptr,							       silence, clear_cnt);							memset(state->other_states[3]->dmabuf.rawbuf + swptr,							       silence, clear_cnt);						}						dmabuf->endcleared = 1;					}				} else if (dmabuf->count < (signed) dmabuf->fragsize) {					clear_cnt = dmabuf->fragsize;					if ((swptr + clear_cnt) > dmabuf->dmasize)						clear_cnt = dmabuf->dmasize - swptr;					memset(dmabuf->rawbuf + swptr, silence, clear_cnt);					if (state->chans_num == 6) {						clear_cnt = clear_cnt / 2;						swptr = swptr / 2;						memset(state->other_states[0]->dmabuf.rawbuf + swptr,						       silence, clear_cnt);						memset(state->other_states[1]->dmabuf.rawbuf + swptr,						       silence, clear_cnt);						memset(state->other_states[2]->dmabuf.rawbuf + swptr,						       silence, clear_cnt);						memset(state->other_states[3]->dmabuf.rawbuf + swptr,						       silence, clear_cnt);					}					dmabuf->endcleared = 1;				}			}			/* trident_update_ptr is called by interrupt handler or by process via			   ioctl/poll, we only wake up the waiting process when we have more			   than 1/2 buffer free (always true for interrupt handler) */			if (dmabuf->count < (signed) dmabuf->dmasize / 2)				wake_up(&dmabuf->wait);		}	}	dmabuf->update_flag &= ~ALI_ADDRESS_INT_UPDATE;}static voidtrident_address_interrupt(struct trident_card *card){	int i;	struct trident_state *state;	unsigned int channel;	/* Update the pointers for all channels we are running. */	/* FIXME: should read interrupt status only once */	for (i = 0; i < NR_HW_CH; i++) {		channel = 63 - i;		if (trident_check_channel_interrupt(card, channel)) {			trident_ack_channel_interrupt(card, channel);			if ((state = card->states[i]) != NULL) {				trident_update_ptr(state);			} else {				printk(KERN_WARNING "trident: spurious channel "				       "irq %d.\n", channel);				trident_stop_voice(card, channel);				trident_disable_voice_irq(card, channel);			}		}	}}static voidali_hwvol_control(struct trident_card *card, int opt){	u16 dwTemp, volume[2], mute, diff, *pVol[2];	dwTemp = ali_ac97_read(card->ac97_codec[0], 0x02);	mute = dwTemp & 0x8000;	volume[0] = dwTemp & 0x001f;	volume[1] = (dwTemp & 0x1f00) >> 8;	if (volume[0] < volume[1]) {		pVol[0] = &volume[0];		pVol[1] = &volume[1];	} else {		pVol[1] = &volume[0];		pVol[0] = &volume[1];	}	diff = *(pVol[1]) - *(pVol[0]);	if (opt == 1) {		// MUTE		dwTemp ^= 0x8000;		ali_ac97_write(card->ac97_codec[0],			       0x02, dwTemp);	} else if (opt == 2) {	// Down		if (mute)			return;		if (*(pVol[1]) < 0x001f) {			(*pVol[1])++;			*(pVol[0]) = *(pVol[1]) - diff;		}		dwTemp &= 0xe0e0;		dwTemp |= (volume[0]) | (volume[1] << 8);		ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp);		card->ac97_codec[0]->mixer_state[0] = ((32 - volume[0]) * 25 / 8) |			(((32 - volume[1]) * 25 / 8) << 8);	} else if (opt == 4) {	// Up		if (mute)			return;		if (*(pVol[0]) > 0) {			(*pVol[0])--;			*(pVol[1]) = *(pVol[0]) + diff;		}		dwTemp &= 0xe0e0;		dwTemp |= (volume[0]) | (volume[1] << 8);		ali_ac97_write(card->ac97_codec[0], 0x02, dwTemp);		card->ac97_codec[0]->mixer_state[0] = ((32 - volume[0]) * 25 / 8) |			(((32 - volume[1]) * 25 / 8) << 8);	} else {		/* Nothing needs doing */	}}/* *	Re-enable reporting of vol change after 0.1 seconds */static voidali_timeout(unsigned long ptr){	struct trident_card *card = (struct trident_card *) ptr;	u16 temp = 0;	/* Enable GPIO IRQ (MISCINT bit 18h) */	temp = inw(TRID_REG(card, T4D_MISCINT + 2));	temp |= 0x0004;	outw(temp, TRID_REG(card, T4D_MISCINT + 2));}/* *	Set up the timer to clear the vol change notification */static voidali_set_timer(struct trident_card *card){	/* Add Timer Routine to Enable GPIO IRQ */	del_timer(&card->timer);	/* Never queue twice */	card->timer.function = ali_timeout;	card->timer.data = (unsigned long) card;	card->timer.expires = jiffies + HZ / 10;	add_timer(&card->timer);}/* *	Process a GPIO event */static voidali_queue_task(struct trident_card *card, int opt){	u16 temp;	/* Disable GPIO IRQ (MISCINT bit 18h) */	temp = inw(TRID_REG(card, T4D_MISCINT + 2));	temp &= (u16) (~0x0004);	outw(temp, TRID_REG(card, T4D_MISCINT + 2));	/* Adjust the volume */	ali_hwvol_control(card, opt);	/* Set the timer for 1/10th sec */	ali_set_timer(card);}static voidcyber_address_interrupt(struct trident_card *card){	int i, irq_status;	struct trident_state *state;	unsigned int channel;	/* Update the pointers for all channels we are running. */	/* FIXED: read interrupt status only once */	irq_status = inl(TRID_REG(card, T4D_AINT_A));	pr_debug("cyber_address_interrupt: irq_status 0x%X\n", irq_status);	for (i = 0; i < NR_HW_CH; i++) {		channel = 31 - i;		if (irq_status & (1 << channel)) {			/* clear bit by writing a 1, zeroes are ignored */			outl((1 << channel), TRID_REG(card, T4D_AINT_A));			pr_debug("cyber_interrupt: channel %d\n", channel);			if ((state = card->states[i]) != NULL) {				trident_update_ptr(state);			} else {				printk(KERN_WARNING "cyber5050: spurious "				       "channel irq %d.\n", channel);				trident_stop_voice(card, channel);				trident_disable_voice_irq(card, channel);			}		}	}}static irqreturn_ttrident_interrupt(int irq, void *dev_id){	struct trident_card *card = (struct trident_card *) dev_id;	u32 event;	u32 gpio;	spin_lock(&card->lock);	event = inl(TRID_REG(card, T4D_MISCINT));	pr_debug("trident: trident_interrupt called, MISCINT = 0x%08x\n",		 event);	if (event & ADDRESS_IRQ) {		card->address_interrupt(card);	}	if (card->pci_id == PCI_DEVICE_ID_ALI_5451) {		/* GPIO IRQ (H/W Volume Control) */		event = inl(TRID_REG(card, T4D_MISCINT));		if (event & (1 << 25)) {			gpio = inl(TRID_REG(card, ALI_GPIO));			if (!timer_pending(&card->timer))				ali_queue_task(card, gpio & 0x07);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?