⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ymfpci.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
#if 0	if (file->f_flags & O_NONBLOCK) {		/*		 * XXX Our  mistake is to attach DMA buffer to state		 * rather than to some per-device structure.		 * Cannot skip waiting, can only make it shorter.		 */	}#endif	while (ypcm->running) {		spin_unlock_irqrestore(&unit->reg_lock, flags);		set_current_state(TASK_UNINTERRUPTIBLE);		schedule();		spin_lock_irqsave(&unit->reg_lock, flags);	}	spin_unlock_irqrestore(&unit->reg_lock, flags);	set_current_state(TASK_RUNNING);	remove_wait_queue(&state->dmabuf.wait, &waita);	/*	 * This function may take up to 4 seconds to reach this point	 * (32K circular buffer, 8000 Hz). User notices.	 */}/* *  Hardware start management */static void ymfpci_hw_start(ymfpci_t *codec){	unsigned long flags;	spin_lock_irqsave(&codec->reg_lock, flags);	if (codec->start_count++ == 0) {		ymfpci_writel(codec, YDSXGR_MODE, 3);		codec->active_bank = ymfpci_readl(codec, YDSXGR_CTRLSELECT) & 1;	}      	spin_unlock_irqrestore(&codec->reg_lock, flags);}static void ymfpci_hw_stop(ymfpci_t *codec){	unsigned long flags;	long timeout = 1000;	spin_lock_irqsave(&codec->reg_lock, flags);	if (--codec->start_count == 0) {		ymfpci_writel(codec, YDSXGR_MODE, 0);		while (timeout-- > 0) {			if ((ymfpci_readl(codec, YDSXGR_STATUS) & 2) == 0)				break;		}	}      	spin_unlock_irqrestore(&codec->reg_lock, flags);}/* *  Playback voice management */static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice){	ymfpci_voice_t *voice, *voice2;	int idx;		*rvoice = NULL;	for (idx = 0; idx < 64; idx += pair ? 2 : 1) {		voice = &codec->voices[idx];		voice2 = pair ? &codec->voices[idx+1] : NULL;		if (voice->use || (voice2 && voice2->use))			continue;		voice->use = 1;		if (voice2)			voice2->use = 1;		switch (type) {		case YMFPCI_PCM:			voice->pcm = 1;			if (voice2)				voice2->pcm = 1;			break;		case YMFPCI_SYNTH:			voice->synth = 1;			break;		case YMFPCI_MIDI:			voice->midi = 1;			break;		}		ymfpci_hw_start(codec);		if (voice2)			ymfpci_hw_start(codec);		*rvoice = voice;		return 0;	}	return -ENOMEM;}static int ymfpci_voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type,    int pair, ymfpci_voice_t **rvoice){	unsigned long flags;	int result;	spin_lock_irqsave(&codec->voice_lock, flags);	for (;;) {		result = voice_alloc(codec, type, pair, rvoice);		if (result == 0 || type != YMFPCI_PCM)			break;		/* TODO: synth/midi voice deallocation */		break;	}	spin_unlock_irqrestore(&codec->voice_lock, flags);		return result;		}static int ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice){	unsigned long flags;		ymfpci_hw_stop(codec);	spin_lock_irqsave(&codec->voice_lock, flags);	pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0;	pvoice->ypcm = NULL;	pvoice->interrupt = NULL;	spin_unlock_irqrestore(&codec->voice_lock, flags);	return 0;}/* *  PCM part */static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice){	ymfpci_pcm_t *ypcm;	int redzone;	int pos, delta, swptr;	int played, distance;	struct ymf_state *state;	struct ymf_dmabuf *dmabuf;	char silence;	if ((ypcm = voice->ypcm) == NULL) {		return;	}	if ((state = ypcm->state) == NULL) {		ypcm->running = 0;	// lock it		return;	}	dmabuf = &state->dmabuf;	spin_lock(&codec->reg_lock);	if (ypcm->running) {/* P3 */ /** printk("ymfpci: %d, intr bank %d count %d start 0x%x:%x\n",  voice->number, codec->active_bank, dmabuf->count,  voice->bank[0].start, voice->bank[1].start); **/		silence = (ymf_pcm_format_width(state->format.format) == 16) ?		    0 : 0x80;		/* We need actual left-hand-side redzone size here. */		redzone = ymf_calc_lend(state->format.rate);		redzone <<= (state->format.shift + 1);		swptr = dmabuf->swptr;		pos = voice->bank[codec->active_bank].start;		pos <<= state->format.shift;		if (pos < 0 || pos >= dmabuf->dmasize) {	/* ucode bug */			printk(KERN_ERR			    "ymfpci%d: %d: runaway: hwptr %d dmasize %d\n",			    codec->dev_audio, voice->number,			    dmabuf->hwptr, dmabuf->dmasize);			pos = 0;		}		if (pos < dmabuf->hwptr) {			delta = dmabuf->dmasize - dmabuf->hwptr;			memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta);			delta += pos;			memset(dmabuf->rawbuf, silence, pos);		} else {			delta = pos - dmabuf->hwptr;			memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta);		}		dmabuf->hwptr = pos;		if (dmabuf->count == 0) {			printk("ymfpci%d: %d: strain: hwptr %d\n",			    codec->dev_audio, voice->number, dmabuf->hwptr);			ymf_playback_trigger(codec, ypcm, 0);		}		if (swptr <= pos) {			distance = pos - swptr;		} else {			distance = dmabuf->dmasize - (swptr - pos);		}		if (distance < redzone) {			/*			 * hwptr inside redzone => DMA ran out of samples.			 */			if (delta < dmabuf->count) {				/*				 * Lost interrupt or other screwage.				 */				printk("ymfpci%d: %d: lost: delta %d"				    " hwptr %d swptr %d distance %d count %d\n",				    codec->dev_audio, voice->number, delta,				    dmabuf->hwptr, swptr, distance, dmabuf->count);			} else {				/*				 * Normal end of DMA.				 *///				printk("ymfpci%d: %d: done: delta %d"//				    " hwptr %d swptr %d distance %d count %d\n",//				    codec->dev_audio, voice->number, delta,//				    dmabuf->hwptr, swptr, distance, dmabuf->count);			}			played = dmabuf->count;			if (ypcm->running) {				ymf_playback_trigger(codec, ypcm, 0);			}		} else {			/*			 * hwptr is chipping away towards a remote swptr.			 * Calculate other distance and apply it to count.			 */			if (swptr >= pos) {				distance = swptr - pos;			} else {				distance = dmabuf->dmasize - (pos - swptr);			}			if (distance < dmabuf->count) {				played = dmabuf->count - distance;			} else {				played = 0;			}		}		dmabuf->total_bytes += played;		dmabuf->count -= played;		if (dmabuf->count < dmabuf->dmasize / 2) {			wake_up(&dmabuf->wait);		}	}	spin_unlock(&codec->reg_lock);}#if HAVE_RECORDstatic void ymfpci_pcm_capture_interrupt(snd_pcm_subchn_t *substream){	snd_pcm_runtime_t *runtime = substream->runtime;	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, );	ymfpci_t *codec = ypcm->codec;	u32 pos, delta;		spin_lock(&codec->reg_lock);	if (ypcm->running) {		pos = codec->bank_capture[ypcm->capture_bank_number][codec->active_bank]->start << ypcm->shift_offset;		if (pos < ypcm->last_pos)  // <--  dmabuf->hwptr			delta = pos + (ypcm->buffer_size - ypcm->last_pos);		else			delta = pos - ypcm->last_pos;		ypcm->frag_pos += delta;		ypcm->last_pos = pos;		while (ypcm->frag_pos >= ypcm->frag_size) {			ypcm->frag_pos -= ypcm->frag_size;			// printk("done - active_bank = 0x%x, start = 0x%x\n", codec->active_bank, voice->bank[codec->active_bank].start);			spin_unlock(&codec->reg_lock);			snd_pcm_transfer_done(substream);			spin_lock(&codec->reg_lock);		}	}	spin_unlock(&codec->reg_lock);}#endifstatic int ymf_playback_trigger(ymfpci_t *codec, ymfpci_pcm_t *ypcm, int cmd){	if (ypcm->voices[0] == NULL) {		return -EINVAL;	}	if (cmd != 0) {		codec->ctrl_playback[ypcm->voices[0]->number + 1] = virt_to_bus(ypcm->voices[0]->bank);		if (ypcm->voices[1] != NULL)			codec->ctrl_playback[ypcm->voices[1]->number + 1] = virt_to_bus(ypcm->voices[1]->bank);		ypcm->running = 1;	} else {		codec->ctrl_playback[ypcm->voices[0]->number + 1] = 0;		if (ypcm->voices[1] != NULL)			codec->ctrl_playback[ypcm->voices[1]->number + 1] = 0;		ypcm->running = 0;	}	return 0;}#if HAVE_RECORDstatic int ymfpci_capture_trigger(void *private_data,					      snd_pcm_subchn_t * substream,				      int cmd){	unsigned long flags;	ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO);	ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, -ENXIO);	int result = 0;	u32 tmp;	spin_lock_irqsave(&codec->reg_lock, flags);	if (cmd == SND_PCM_TRIGGER_GO) {		tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number);		ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp);		ypcm->running = 1;	} else if (cmd == SND_PCM_TRIGGER_STOP) {		tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number);		ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp);		ypcm->running = 0;	} else {		result = -EINVAL;	}	spin_unlock_irqrestore(&codec->reg_lock, flags);	return result;}#endifstatic int ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices){	int err;	if (ypcm->voices[1] != NULL && voices < 2) {		ymfpci_voice_free(ypcm->codec, ypcm->voices[1]);		ypcm->voices[1] = NULL;	}	if (voices == 1 && ypcm->voices[0] != NULL)		return 0;		/* already allocated */	if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL)		return 0;		/* already allocated */	if (voices > 1) {		if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) {			ymfpci_voice_free(ypcm->codec, ypcm->voices[0]);			ypcm->voices[0] = NULL;		}			}	err = ymfpci_voice_alloc(ypcm->codec, YMFPCI_PCM, voices > 1, &ypcm->voices[0]);	if (err < 0)		return err;	ypcm->voices[0]->ypcm = ypcm;	ypcm->voices[0]->interrupt = ymf_pcm_interrupt;	if (voices > 1) {		ypcm->voices[1] = &ypcm->codec->voices[ypcm->voices[0]->number + 1];		ypcm->voices[1]->ypcm = ypcm;	}	return 0;}static void ymf_pcm_init_voice(ymfpci_voice_t *voice, int stereo,    int rate, int w_16, unsigned long addr, unsigned int end, int spdif){	u32 format;	u32 delta = ymfpci_calc_delta(rate);	u32 lpfQ = ymfpci_calc_lpfQ(rate);	u32 lpfK = ymfpci_calc_lpfK(rate);	ymfpci_playback_bank_t *bank;	int nbank;	format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000);	if (stereo)		end >>= 1;	if (w_16)		end >>= 1;/* P3 */ // printk("ymf_pcm_init_voice: %d: Rate %d Format 0x%08x Delta 0x%x End 0x%x\n",//  voice->number, rate, format, delta, end);	for (nbank = 0; nbank < 2; nbank++) {		bank = &voice->bank[nbank];		bank->format = format;		bank->loop_default = 0;	/* 0-loops forever, otherwise count */		bank->base = addr;		bank->loop_start = 0;		bank->loop_end = end;		bank->loop_frac = 0;		bank->eg_gain_end = 0x40000000;		bank->lpfQ = lpfQ;		bank->status = 0;		bank->num_of_frames = 0;		bank->loop_count = 0;		bank->start = 0;		bank->start_frac = 0;		bank->delta =		bank->delta_end = delta;		bank->lpfK =		bank->lpfK_end = lpfK;		bank->eg_gain = 0x40000000;		bank->lpfD1 =		bank->lpfD2 = 0;		bank->left_gain = 		bank->right_gain =		bank->left_gain_end =		bank->right_gain_end =		bank->eff1_gain =		bank->eff2_gain =		bank->eff3_gain =		bank->eff1_gain_end =		bank->eff2_gain_end =		bank->eff3_gain_end = 0;		if (!stereo) {			if (!spdif) {				bank->left_gain = 				bank->right_gain =				bank->left_gain_end =				bank->right_gain_end = 0x40000000;			} else {				bank->eff2_gain =				bank->eff2_gain_end =				bank->eff3_gain =				bank->eff3_gain_end = 0x40000000;			}		} else {			if (!spdif) {				if ((voice->number & 1) == 0) {					bank->format |= 1;					bank->left_gain =					bank->left_gain_end = 0x40000000;				} else {					bank->right_gain =					bank->right_gain_end = 0x40000000;				}			} else {				if ((voice->number & 1) == 0) {					bank->format |= 1;					bank->eff2_gain =					bank->eff2_gain_end = 0x40000000;				} else {					bank->eff3_gain =					bank->eff3_gain_end = 0x40000000;				}			}		}	}}/* * XXX Use new cache coherent PCI DMA routines instead of virt_to_bus. */static int ymf_playback_prepare(ymfpci_t *codec, struct ymf_state *state){	ymfpci_pcm_t *ypcm = &state->ypcm;	int err, nvoice;	if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) {		/* Cannot be unless we leak voices in ymf_release! */		printk(KERN_ERR "ymfpci%d: cannot allocate voice!\n",		    codec->dev_audio);		return err;	}	for (nvoice = 0; nvoice < state->format.voices; nvoice++) {		ymf_pcm_init_voice(ypcm->voices[nvoice],		    state->format.voices == 2, state->format.rate,		    ymf_pcm_format_width(state->format.format) == 16,		    virt_to_bus(state->dmabuf.rawbuf), state->dmabuf.dmasize,

⌨️ 快捷键说明

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