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

📄 hda_intel.c

📁 一个Linux下的软猫驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
static int azx_setup_controller(azx_t *chip, azx_dev_t *azx_dev){	unsigned char val;	int timeout;//printk(KERN_DEBUG"%s: %p tag=%d bufsize=%d fmt=0x%x\n", __FUNCTION__, azx_dev, (int)azx_dev->stream_tag, azx_dev->bufsize, (unsigned int)azx_dev->format_val);	/* make sure the run bit is zero for SD */	azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & ~SD_CTL_DMA_START);	/* reset stream */	azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) | SD_CTL_STREAM_RESET);	udelay(3);	timeout = 300;	while (!((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&	       --timeout)		;	val &= ~SD_CTL_STREAM_RESET;	azx_sd_writeb(azx_dev, SD_CTL, val);	udelay(3);	timeout = 300;	/* waiting for hardware to report that the stream is out of reset */	while (((val = azx_sd_readb(azx_dev, SD_CTL)) & SD_CTL_STREAM_RESET) &&	       --timeout)		;	/* program the stream_tag */	azx_sd_writel(azx_dev, SD_CTL,		      (azx_sd_readl(azx_dev, SD_CTL) & ~SD_CTL_STREAM_TAG_MASK) |		      (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT));	/* program the length of samples in cyclic buffer */	azx_sd_writel(azx_dev, SD_CBL, azx_dev->bufsize);	/* program the stream format */	/* this value needs to be the same as the one programmed */	azx_sd_writew(azx_dev, SD_FORMAT, azx_dev->format_val);	/* program the stream LVI (last valid index) of the BDL */	azx_sd_writew(azx_dev, SD_LVI, azx_dev->frags - 1);	/* program the BDL address */	/* lower BDL address */	azx_sd_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl_addr);	/* upper BDL address */	azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr));	/* enable the position buffer */	if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))		azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);	/* set the interrupt enable bits in the descriptor control register */	azx_sd_writel(azx_dev, SD_CTL, azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK);	return 0;}/* * Codec initialization */static int __devinit azx_codec_create(azx_t *chip, const char *model){	struct hda_bus_template bus_temp;	int c, codecs, err;	memset(&bus_temp, 0, sizeof(bus_temp));	bus_temp.private_data = chip;	bus_temp.modelname = model;	bus_temp.pci = chip->pci;	bus_temp.ops.command = azx_send_cmd;	bus_temp.ops.get_response = azx_get_response;	bus_temp.ops.get_wallclock = azx_get_wallclock;	bus_temp.ops.get_linkpos = azx_get_linkpos;	if ((err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus)) < 0)		return err;	codecs = 0;	for (c = 0; c < AZX_MAX_CODECS; c++) {		if (chip->codec_mask & (1 << c)) {			err = snd_hda_codec_new(chip->bus, c, NULL);			if (err < 0)				continue;			codecs++;		}	}	if (! codecs) {		snd_printk(KERN_ERR SFX "no codecs initialized\n");		return -ENXIO;	}	return 0;}/* * PCM support *//* assign a stream for the PCM */static inline azx_dev_t *azx_assign_device(azx_t *chip, int stream){	int dev, i, nums;	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {		dev = chip->playback_index_offset;		nums = chip->playback_streams;	} else {		dev = chip->capture_index_offset;		nums = chip->capture_streams;	}	for (i = 0; i < nums; i++, dev++)		if (! chip->azx_dev[dev].opened) {			chip->azx_dev[dev].opened = 1;			return &chip->azx_dev[dev];		}	return NULL;}/* release the assigned stream */static inline void azx_release_device(azx_dev_t *azx_dev){//printk(KERN_ERR"%s dev=%p\n", __FUNCTION__, azx_dev);	azx_dev->opened = 0;}static snd_pcm_hardware_t azx_pcm_hw = {	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |				 SNDRV_PCM_INFO_BLOCK_TRANSFER |				 SNDRV_PCM_INFO_MMAP_VALID |				 SNDRV_PCM_INFO_PAUSE /*|*/				 /*SNDRV_PCM_INFO_RESUME*/),	.formats =		SNDRV_PCM_FMTBIT_S16_LE,	.rates =		SNDRV_PCM_RATE_48000,	.rate_min =		48000,	.rate_max =		48000,	.channels_min =		2,	.channels_max =		2,	.buffer_bytes_max =	AZX_MAX_BUF_SIZE,	.period_bytes_min =	128,	.period_bytes_max =	AZX_MAX_BUF_SIZE / 2,	.periods_min =		2,	.periods_max =		AZX_MAX_FRAG,	.fifo_size =		0,};struct azx_pcm {	azx_t *chip;	struct hda_codec *codec;	struct hda_pcm_stream *hinfo[2];};static int azx_pcm_open(snd_pcm_substream_t *substream){	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];	azx_t *chip = apcm->chip;	azx_dev_t *azx_dev;	snd_pcm_runtime_t *runtime = substream->runtime;	unsigned long flags;	int err;	down(&chip->open_mutex);	azx_dev = azx_assign_device(chip, substream->stream);	if (azx_dev == NULL) {		up(&chip->open_mutex);		return -EBUSY;	}	runtime->hw = azx_pcm_hw;	runtime->hw.channels_min = hinfo->channels_min;	runtime->hw.channels_max = hinfo->channels_max;	runtime->hw.formats = hinfo->formats;	runtime->hw.rates = hinfo->rates;	snd_pcm_limit_hw_rates(runtime);	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);	if ((err = hinfo->ops.open(hinfo, apcm->codec, substream)) < 0) {		azx_release_device(azx_dev);		up(&chip->open_mutex);		return err;	}	spin_lock_irqsave(&chip->reg_lock, flags);	azx_dev->substream = substream;	azx_dev->running = 0;	spin_unlock_irqrestore(&chip->reg_lock, flags);	runtime->private_data = azx_dev;	up(&chip->open_mutex);	return 0;}static int azx_pcm_close(snd_pcm_substream_t *substream){	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];	azx_t *chip = apcm->chip;	azx_dev_t *azx_dev = get_azx_dev(substream);	unsigned long flags;//printk(KERN_DEBUG"%s: %p\n", __FUNCTION__, substream);	down(&chip->open_mutex);	spin_lock_irqsave(&chip->reg_lock, flags);	azx_dev->substream = NULL;	azx_dev->running = 0;	spin_unlock_irqrestore(&chip->reg_lock, flags);	azx_release_device(azx_dev);	hinfo->ops.close(hinfo, apcm->codec, substream);	up(&chip->open_mutex);	return 0;}static int azx_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params){	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static int azx_pcm_hw_free(snd_pcm_substream_t *substream){	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);	azx_dev_t *azx_dev = get_azx_dev(substream);	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];//printk(KERN_DEBUG"%s: %p\n", __FUNCTION__, substream);	/* reset BDL address */	azx_sd_writel(azx_dev, SD_BDLPL, 0);	azx_sd_writel(azx_dev, SD_BDLPU, 0);	azx_sd_writel(azx_dev, SD_CTL, 0);	hinfo->ops.cleanup(hinfo, apcm->codec, substream);	return snd_pcm_lib_free_pages(substream);}static int azx_pcm_prepare(snd_pcm_substream_t *substream){	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);	azx_t *chip = apcm->chip;	azx_dev_t *azx_dev = get_azx_dev(substream);	struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];	snd_pcm_runtime_t *runtime = substream->runtime;	azx_dev->bufsize = snd_pcm_lib_buffer_bytes(substream);	azx_dev->fragsize = snd_pcm_lib_period_bytes(substream);	azx_dev->frags = azx_dev->bufsize / azx_dev->fragsize;	azx_dev->format_val = snd_hda_calc_stream_format(runtime->rate,							 runtime->channels,							 runtime->format,							 hinfo->maxbps);	if (! azx_dev->format_val) {		snd_printk(KERN_ERR SFX "invalid format_val, rate=%d, ch=%d, format=%d\n",			   runtime->rate, runtime->channels, runtime->format);		return -EINVAL;	}	snd_printdd("azx_pcm_prepare: bufsize=0x%x, fragsize=0x%x, format=0x%x\n",		    azx_dev->bufsize, azx_dev->fragsize, azx_dev->format_val);	azx_setup_periods(azx_dev);	azx_setup_controller(chip, azx_dev);	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)		azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;	else		azx_dev->fifo_size = 0;	azx_dev->last_pos = 0;//	runtime->hw.fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE);	return hinfo->ops.prepare(hinfo, apcm->codec, azx_dev->stream_tag,				  azx_dev->format_val, substream);}static int azx_pcm_trigger(snd_pcm_substream_t *substream, int cmd){	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);	azx_dev_t *azx_dev = get_azx_dev(substream);	azx_t *chip = apcm->chip;	int err = 0;	spin_lock(&chip->reg_lock);	switch (cmd) {	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:	case SNDRV_PCM_TRIGGER_RESUME:	case SNDRV_PCM_TRIGGER_START:		azx_stream_start(chip, azx_dev);		azx_dev->running = 1;		break;	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:	case SNDRV_PCM_TRIGGER_SUSPEND:	case SNDRV_PCM_TRIGGER_STOP:		azx_stream_stop(chip, azx_dev);		azx_dev->running = 0;		break;	default:		err = -EINVAL;	}	spin_unlock(&chip->reg_lock);	if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH ||	    cmd == SNDRV_PCM_TRIGGER_SUSPEND ||	    cmd == SNDRV_PCM_TRIGGER_STOP) {		int timeout = 5000;		while (azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START && --timeout)			;	}	return err;}static snd_pcm_uframes_t azx_pcm_pointer(snd_pcm_substream_t *substream){	struct azx_pcm *apcm = snd_pcm_substream_chip(substream);	azx_t *chip = apcm->chip;	azx_dev_t *azx_dev = get_azx_dev(substream);	unsigned int pos;	if (chip->position_fix == POS_FIX_POSBUF) {		/* use the position buffer */		pos = *azx_dev->posbuf;	} else {		/* read LPIB */		pos = azx_sd_readl(azx_dev, SD_LPIB);		if (chip->position_fix == POS_FIX_FIFO)			pos += azx_dev->fifo_size;#if 0 /* disabled temprarily, auto-correction doesn't work well... */		else if (chip->position_fix == POS_FIX_AUTO && azx_dev->period_updating) {			/* check the validity of DMA position */			unsigned int diff = 0;			azx_dev->last_pos += azx_dev->fragsize;			if (azx_dev->last_pos > pos)				diff = azx_dev->last_pos - pos;			if (azx_dev->last_pos >= azx_dev->bufsize) {				if (pos < azx_dev->fragsize)					diff = 0;				azx_dev->last_pos = 0;			}			if (diff > 0 && diff <= azx_dev->fifo_size)				pos += azx_dev->fifo_size;			else {				snd_printdd(KERN_INFO "hda_intel: DMA position fix %d, switching to posbuf\n", diff);				chip->position_fix = POS_FIX_POSBUF;				pos = *azx_dev->posbuf;			}			azx_dev->period_updating = 0;		}#else		else if (chip->position_fix == POS_FIX_AUTO)			pos += azx_dev->fifo_size;#endif	}	if (pos >= azx_dev->bufsize)		pos = 0;	return bytes_to_frames(substream->runtime, pos);}static snd_pcm_ops_t azx_pcm_ops = {	.open = azx_pcm_open,	.close = azx_pcm_close,	.ioctl = snd_pcm_lib_ioctl,	.hw_params = azx_pcm_hw_params,	.hw_free = azx_pcm_hw_free,	.prepare = azx_pcm_prepare,	.trigger = azx_pcm_trigger,	.pointer = azx_pcm_pointer,};static void azx_pcm_free(snd_pcm_t *pcm){//printk(KERN_DEBUG"%s: %p\n", __FUNCTION__, pcm);	kfree(pcm->private_data);}static int __devinit create_codec_pcm(azx_t *chip, struct hda_codec *codec,				      struct hda_pcm *cpcm, int pcm_dev){	int err;	snd_pcm_t *pcm;	struct azx_pcm *apcm;	snd_assert(cpcm->stream[0].substreams || cpcm->stream[1].substreams, return -EINVAL);	snd_assert(cpcm->name, return -EINVAL);	err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,			  cpcm->stream[0].substreams, cpcm->stream[1].substreams,			  &pcm);	if (err < 0)		return err;	strcpy(pcm->name, cpcm->name);	apcm = kmalloc(sizeof(*apcm), GFP_KERNEL);	if (apcm == NULL)		return -ENOMEM;	apcm->chip = chip;	apcm->codec = codec;	apcm->hinfo[0] = &cpcm->stream[0];	apcm->hinfo[1] = &cpcm->stream[1];	pcm->private_data = apcm;	pcm->private_free = azx_pcm_free;	if (cpcm->stream[0].substreams)		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops);	if (cpcm->stream[1].substreams)		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,					      snd_dma_pci_data(chip->pci),					      1024 * 64, 1024 * 128);	cpcm->pcm = pcm;	chip->pcm[pcm_dev] = pcm;	chip->pcm_devs = pcm_dev + 1;	return 0;}static int __devinit azx_pcm_create(azx_t *chip){	struct list_head *p;	struct hda_codec *codec;	int c, err;	int pcm_dev;	if ((err = snd_hda_build_pcms(chip->bus)) < 0)		return err;	/* create audio PCMs */	pcm_dev = 0;	list_for_each(p, &chip->bus->codec_list) {		codec = list_entry(p, struct hda_codec, list);		for (c = 0; c < codec->num_pcms; c++) {			if (codec->pcm_info[c].is_modem)				continue; /* create later */			if (pcm_dev >= AZX_MAX_AUDIO_PCMS) {				snd_printk(KERN_ERR SFX "Too many audio PCMs\n");				return -EINVAL;			}			err = create_codec_pcm(chip, codec, &codec->pcm_info[c], pcm_dev);			if (err < 0)				return err;			pcm_dev++;		}	}

⌨️ 快捷键说明

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