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

📄 ac97_codec.c

📁 h内核
💻 C
📖 第 1 页 / 共 5 页
字号:
	snd_ac97_write(ac97, AC97_GPIO_POLARITY, 0xffff & ~(AC97_GPIO_LINE1_OH));	snd_ac97_write(ac97, AC97_GPIO_STICKY, 0xffff);	snd_ac97_write(ac97, AC97_GPIO_WAKEUP, 0x0);	snd_ac97_write(ac97, AC97_MISC_AFE, 0x0);	return 0;}static int snd_ac97_test_rate(ac97_t *ac97, int reg, int shadow_reg, int rate){	unsigned short val;	unsigned int tmp;	tmp = ((unsigned int)rate * ac97->bus->clock) / 48000;	snd_ac97_write_cache(ac97, reg, tmp & 0xffff);	if (shadow_reg)		snd_ac97_write_cache(ac97, shadow_reg, tmp & 0xffff);	val = snd_ac97_read(ac97, reg);	return val == (tmp & 0xffff);}static void snd_ac97_determine_rates(ac97_t *ac97, int reg, int shadow_reg, unsigned int *r_result){	unsigned int result = 0;	if (ac97->bus->no_vra) {		*r_result = SNDRV_PCM_RATE_48000;		if ((ac97->flags & AC97_DOUBLE_RATE) &&		    reg == AC97_PCM_FRONT_DAC_RATE)			*r_result |= SNDRV_PCM_RATE_96000;		return;	}	if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE)		snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,				     AC97_EA_DRA, 0);	/* test a non-standard rate */	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11000))		result |= SNDRV_PCM_RATE_CONTINUOUS;	/* let's try to obtain standard rates */	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 8000))		result |= SNDRV_PCM_RATE_8000;	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11025))		result |= SNDRV_PCM_RATE_11025;	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 16000))		result |= SNDRV_PCM_RATE_16000;	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 22050))		result |= SNDRV_PCM_RATE_22050;	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 32000))		result |= SNDRV_PCM_RATE_32000;	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 44100))		result |= SNDRV_PCM_RATE_44100;	if (snd_ac97_test_rate(ac97, reg, shadow_reg, 48000))		result |= SNDRV_PCM_RATE_48000;	if ((ac97->flags & AC97_DOUBLE_RATE) &&	    reg == AC97_PCM_FRONT_DAC_RATE) {		/* test standard double rates */		snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,				     AC97_EA_DRA, AC97_EA_DRA);		if (snd_ac97_test_rate(ac97, reg, shadow_reg, 64000 / 2))			result |= SNDRV_PCM_RATE_64000;		if (snd_ac97_test_rate(ac97, reg, shadow_reg, 88200 / 2))			result |= SNDRV_PCM_RATE_88200;		if (snd_ac97_test_rate(ac97, reg, shadow_reg, 96000 / 2))			result |= SNDRV_PCM_RATE_96000;		/* some codecs don't support variable double rates */		if (!snd_ac97_test_rate(ac97, reg, shadow_reg, 76100 / 2))			result &= ~SNDRV_PCM_RATE_CONTINUOUS;		snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,				     AC97_EA_DRA, 0);	}	*r_result = result;}/* check AC97_SPDIF register to accept which sample rates */static unsigned int snd_ac97_determine_spdif_rates(ac97_t *ac97){	unsigned int result = 0;	int i;	static unsigned short ctl_bits[] = {		AC97_SC_SPSR_44K, AC97_SC_SPSR_32K, AC97_SC_SPSR_48K	};	static unsigned int rate_bits[] = {		SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_32000, SNDRV_PCM_RATE_48000	};	for (i = 0; i < (int)ARRAY_SIZE(ctl_bits); i++) {		snd_ac97_update_bits(ac97, AC97_SPDIF, AC97_SC_SPSR_MASK, ctl_bits[i]);		if ((snd_ac97_read(ac97, AC97_SPDIF) & AC97_SC_SPSR_MASK) == ctl_bits[i])			result |= rate_bits[i];	}	return result;}void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name, int modem){	const ac97_codec_id_t *pid;	sprintf(name, "0x%x %c%c%c", id,		printable(id >> 24),		printable(id >> 16),		printable(id >> 8));	for (pid = snd_ac97_codec_id_vendors; pid->id; pid++)		if (pid->id == (id & pid->mask)) {			strcpy(name, pid->name);			if (ac97) {				if (!modem && pid->patch)					pid->patch(ac97);				else if (modem && pid->mpatch)					pid->mpatch(ac97);			} 			goto __vendor_ok;		}	return;      __vendor_ok:	for (pid = snd_ac97_codec_ids; pid->id; pid++)		if (pid->id == (id & pid->mask)) {			strcat(name, " ");			strcat(name, pid->name);			if (pid->mask != 0xffffffff)				sprintf(name + strlen(name), " rev %d", id & ~pid->mask);			if (ac97) {				if (!modem && pid->patch)					pid->patch(ac97);				else if (modem && pid->mpatch)					pid->mpatch(ac97);			}			return;		}	sprintf(name + strlen(name), " id %x", id & 0xff);}/** * snd_ac97_get_short_name - retrieve codec name * @ac97: the codec instance * * Returns the short identifying name of the codec. */const char *snd_ac97_get_short_name(ac97_t *ac97){	const ac97_codec_id_t *pid;	for (pid = snd_ac97_codec_ids; pid->id; pid++)		if (pid->id == (ac97->id & pid->mask))			return pid->name;	return "unknown codec";}/* wait for a while until registers are accessible after RESET * return 0 if ok, negative not ready */static int ac97_reset_wait(ac97_t *ac97, int timeout, int with_modem){	unsigned long end_time;	unsigned short val;	end_time = jiffies + timeout;	do {				/* use preliminary reads to settle the communication */		snd_ac97_read(ac97, AC97_RESET);		snd_ac97_read(ac97, AC97_VENDOR_ID1);		snd_ac97_read(ac97, AC97_VENDOR_ID2);		/* modem? */		if (with_modem) {			val = snd_ac97_read(ac97, AC97_EXTENDED_MID);			if (val != 0xffff && (val & 1) != 0)				return 0;		}		if (ac97->scaps & AC97_SCAP_DETECT_BY_VENDOR) {			/* probably only Xbox issue - all registers are read as zero */			val = snd_ac97_read(ac97, AC97_VENDOR_ID1);			if (val != 0 && val != 0xffff)				return 0;		} else {			/* because the PCM or MASTER volume registers can be modified,			 * the REC_GAIN register is used for tests			 */			/* test if we can write to the record gain volume register */			snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05);			if ((snd_ac97_read(ac97, AC97_REC_GAIN) & 0x7fff) == 0x0a05)				return 0;		}		set_current_state(TASK_UNINTERRUPTIBLE);		schedule_timeout(1);	} while (time_after_eq(end_time, jiffies));	return -ENODEV;}/** * snd_ac97_bus - create an AC97 bus component * @card: the card instance * @num: the bus number * @ops: the bus callbacks table * @private_data: private data pointer for the new instance * @rbus: the pointer to store the new AC97 bus instance. * * Creates an AC97 bus component.  An ac97_bus_t instance is newly * allocated and initialized. * * The ops table must include valid callbacks (at least read and * write).  The other callbacks, wait and reset, are not mandatory. *  * The clock is set to 48000.  If another clock is needed, set * (*rbus)->clock manually. * * The AC97 bus instance is registered as a low-level device, so you don't * have to release it manually. * * Returns zero if successful, or a negative error code on failure. */int snd_ac97_bus(snd_card_t *card, int num, ac97_bus_ops_t *ops,		 void *private_data, ac97_bus_t **rbus){	int err;	ac97_bus_t *bus;	static snd_device_ops_t dev_ops = {		.dev_free =	snd_ac97_bus_dev_free,	};	snd_assert(card != NULL, return -EINVAL);	snd_assert(rbus != NULL, return -EINVAL);	bus = kcalloc(1, sizeof(*bus), GFP_KERNEL);	if (bus == NULL)		return -ENOMEM;	bus->card = card;	bus->num = num;	bus->ops = ops;	bus->private_data = private_data;	bus->clock = 48000;	spin_lock_init(&bus->bus_lock);	snd_ac97_bus_proc_init(bus);	if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops)) < 0) {		snd_ac97_bus_free(bus);		return err;	}	*rbus = bus;	return 0;}/* build_ops to do nothing */static struct snd_ac97_build_ops null_build_ops;/** * snd_ac97_mixer - create an Codec97 component * @bus: the AC97 bus which codec is attached to * @template: the template of ac97, including index, callbacks and *         the private data. * @rac97: the pointer to store the new ac97 instance. * * Creates an Codec97 component.  An ac97_t instance is newly * allocated and initialized from the template.  The codec * is then initialized by the standard procedure. * * The template must include the codec number (num) and address (addr), * and the private data (private_data). *  * The ac97 instance is registered as a low-level device, so you don't * have to release it manually. * * Returns zero if successful, or a negative error code on failure. */int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97){	int err;	ac97_t *ac97;	snd_card_t *card;	char name[64];	unsigned long end_time;	unsigned int reg;	static snd_device_ops_t ops = {		.dev_free =	snd_ac97_dev_free,	};	snd_assert(rac97 != NULL, return -EINVAL);	*rac97 = NULL;	snd_assert(bus != NULL && template != NULL, return -EINVAL);	snd_assert(template->num < 4 && bus->codec[template->num] == NULL, return -EINVAL);	snd_assert(bus->shared_type <= AC97_SHARED_TYPES, return -EINVAL);	if (bus->shared_type) {		/* already shared? */		down(&shared_codec_mutex);		ac97 = shared_codec[bus->shared_type-1][template->num];		if (ac97) {			if ((ac97_is_audio(ac97) && (template->scaps & AC97_SCAP_SKIP_AUDIO)) ||			    (ac97_is_modem(ac97) && (template->scaps & AC97_SCAP_SKIP_MODEM))) {				up(&shared_codec_mutex);				return -EACCES; /* skip this */			}		}		up(&shared_codec_mutex);	}	card = bus->card;	ac97 = kcalloc(1, sizeof(*ac97), GFP_KERNEL);	if (ac97 == NULL)		return -ENOMEM;	ac97->private_data = template->private_data;	ac97->private_free = template->private_free;	ac97->bus = bus;	ac97->pci = template->pci;	ac97->num = template->num;	ac97->addr = template->addr;	ac97->scaps = template->scaps;	ac97->limited_regs = template->limited_regs;	memcpy(ac97->reg_accessed, template->reg_accessed, sizeof(ac97->reg_accessed));	bus->codec[ac97->num] = ac97;	init_MUTEX(&ac97->reg_mutex);	init_MUTEX(&ac97->page_mutex);	if (ac97->pci) {		pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_VENDOR_ID, &ac97->subsystem_vendor);		pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_ID, &ac97->subsystem_device);	}	if (bus->ops->reset) {		bus->ops->reset(ac97);		goto __access_ok;	}	snd_ac97_write(ac97, AC97_RESET, 0);	/* reset to defaults */	if (bus->ops->wait)		bus->ops->wait(ac97);	else {		udelay(50);		if (ac97->scaps & AC97_SCAP_SKIP_AUDIO)			err = ac97_reset_wait(ac97, HZ/2, 1);		else {			err = ac97_reset_wait(ac97, HZ/2, 0);			if (err < 0)				err = ac97_reset_wait(ac97, HZ/2, 1);		}		if (err < 0) {			snd_printk(KERN_WARNING "AC'97 %d does not respond - RESET\n", ac97->num);			/* proceed anyway - it's often non-critical */		}	}      __access_ok:	ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16;	ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);	if (! (ac97->scaps & AC97_SCAP_DETECT_BY_VENDOR) &&	    (ac97->id == 0x00000000 || ac97->id == 0xffffffff)) {		snd_printk(KERN_ERR "AC'97 %d access is not valid [0x%x], removing mixer.\n", ac97->num, ac97->id);		snd_ac97_free(ac97);		return -EIO;	}		/* test for AC'97 */	if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO) && !(ac97->scaps & AC97_SCAP_AUDIO)) {		/* test if we can write to the record gain volume register */		snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a06);		if (((err = snd_ac97_read(ac97, AC97_REC_GAIN)) & 0x7fff) == 0x0a06)			ac97->scaps |= AC97_SCAP_AUDIO;	}	if (ac97->scaps & AC97_SCAP_AUDIO) {		ac97->caps = snd_ac97_read(ac97, AC97_RESET);		ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID);		if (ac97->ext_id == 0xffff)	/* invalid combination */			ac97->ext_id = 0;	}	/* test for MC'97 */	if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM) && !(ac97->scaps & AC97_SCAP_MODEM)) {		ac97->ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID);		if (ac97->ext_mid == 0xffff)	/* invalid combination */			ac97->ext_mid = 0;		if (ac97->ext_mid & 1)			ac97->scaps |= AC97_SCAP_MODEM;	}	if (!ac97_is_audio(ac97) && !ac97_is_modem(ac97)) {		if (!(ac97->scaps & (AC97_SCAP_SKIP_AUDIO|AC97_SCAP_SKIP_MODEM)))			snd_printk(KERN_ERR "AC'97 %d access error (not audio or modem codec)\n", ac97->num);		snd_ac97_free(ac97);		return -EACCES;	}	if (bus->ops->reset) // FIXME: always skipping?		goto __ready_ok;	/* FIXME: add powerdown control */	if (ac97_is_audio(ac97)) {		/* nothing should be in powerdown mode */		snd_ac97_write_cache(ac97, AC97_POWERDOWN, 0);		snd_ac97_write_cache(ac97, AC97_RESET, 0);		/* reset to defaults */		udelay(100);		/* nothing should be in powerdown mode */		snd_ac97_write_cache(ac97, AC97

⌨️ 快捷键说明

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