ac97_codec.c

来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 1,915 行 · 第 1/5 页

C
1,915
字号
		if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_eapd, ac97))) < 0)			return err;	}	return 0;}static int snd_ac97_modem_build(snd_card_t * card, ac97_t * ac97){	/* TODO */	//printk("AC97_GPIO_CFG = %x\n",snd_ac97_read(ac97,AC97_GPIO_CFG));	snd_ac97_write(ac97, AC97_GPIO_CFG, 0xffff & ~(AC97_GPIO_LINE1_OH));	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;	/* 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;	*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);}/* 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;	end_time = jiffies + timeout;	do {		unsigned short ext_mid;				/* 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) {			ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID);			if (ext_mid != 0xffff && (ext_mid & 1) != 0)				return 0;		}		/* 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) == 0x8a05)			return 0;		set_current_state(TASK_UNINTERRUPTIBLE);		schedule_timeout(HZ/100);	} while (time_after_eq(end_time, jiffies));	return -ENODEV;}/** * snd_ac97_bus - create an AC97 bus component * @card: the card instance * @_bus: the template of AC97 bus, callbacks and *         the private data. * @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 from the template (_bus). * * The template must include the valid callbacks (at least read and * write), the bus number (num), and the private data (private_data). * The other callbacks, wait and reset, are not mandatory. *  * The clock is set to 48000.  If another clock is needed, set * bus->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, ac97_bus_t * _bus, ac97_bus_t ** rbus){	int err;	ac97_bus_t *bus;	static snd_device_ops_t ops = {		.dev_free =	snd_ac97_bus_dev_free,	};	snd_assert(card != NULL, return -EINVAL);	snd_assert(_bus != NULL && rbus != NULL, return -EINVAL);	bus = snd_magic_kmalloc(ac97_bus_t, 0, GFP_KERNEL);	if (bus == NULL)		return -ENOMEM;	*bus = *_bus;	bus->card = card;	if (bus->clock == 0)		bus->clock = 48000;	spin_lock_init(&bus->bus_lock);	snd_ac97_bus_proc_init(bus);	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, bus, &ops)) < 0) {		snd_ac97_bus_free(bus);		return err;	}	*rbus = bus;	return 0;}/** * snd_ac97_mixer - create an Codec97 component * @bus: the AC97 bus which codec is attached to * @_ac97: 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 (_ac97).  The codec * is then initialized by the standard procedure. * * The template must include the valid callbacks (at least read and * write), the codec number (num) and address (addr), and the private * data (private_data).  The other callbacks, wait and reset, are not * mandatory. *  * 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_t * _ac97, 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 && _ac97 != NULL, return -EINVAL);	snd_assert(_ac97->num < 4 && bus->codec[_ac97->num] == NULL, return -EINVAL);	card = bus->card;	ac97 = snd_magic_kmalloc(ac97_t, 0, GFP_KERNEL);	if (ac97 == NULL)		return -ENOMEM;	*ac97 = *_ac97;	ac97->bus = bus;	bus->codec[ac97->num] = ac97;	spin_lock_init(&ac97->reg_lock);	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->reset) {		bus->reset(ac97);		goto __access_ok;	}	snd_ac97_write(ac97, AC97_RESET, 0);	/* reset to defaults */	if (bus->wait)		bus->wait(ac97);	else {		udelay(50);		if (ac97_reset_wait(ac97, HZ/2, 0) < 0 &&		    ac97_reset_wait(ac97, HZ/2, 1) < 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->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;	}	/* AC97 audio codec chip revision detection. */	/* Currently only Realtek ALC650 detection implemented. */	switch(ac97->id & 0xfffffff0) {	case 0x414c4720:        /* ALC650 */		reg = snd_ac97_read(ac97, AC97_ALC650_REVISION);		if (((reg & 0x3f) >= 0) && ((reg & 0x3f) < 3))			ac97->id = 0x414c4720;          /* Old version */		else if (((reg & 0x3f) >= 3) && ((reg & 0x3f) < 0x10))			ac97->id = 0x414c4721;          /* D version */		else if ((reg&0x30) == 0x10)			ac97->id = 0x414c4722;          /* E version */		else if ((reg&0x30) == 0x20)			ac97->id = 0x414c4723;          /* F version */        }		/* 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)) == 0x8a06)			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->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_POWERDOWN, 0);		snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0);		end_time = jiffies + (HZ / 10);		do {			if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f)				goto __ready_ok;			set_current_state(TASK_UNINTERRUPTIBLE);			schedule_timeout(HZ/10);		} while (time_after_eq(end_time, jiffies));		snd_printk(KERN_ERR "AC'97 %d analog subsections not ready\n", ac97->num);	}	/* FIXME: add powerdown control */	if (ac97_is_modem(ac97)) {		unsigned char tmp;		/* nothing should be in powerdown mode */		/* note: it's important to set the rate at first */		tmp = AC97_MEA_GPIO;		if (ac97->ext_mid & AC97_MEI_LINE1) {			snd_ac97_write_cache(ac97, AC97_LINE1_RATE, 12000);			tmp |= AC97_MEA_ADC1 | AC97_MEA_DAC1;		}		if (ac97->ext_mid & AC97_MEI_LINE2) {			snd_ac97_write_cache(ac97, AC97_LINE2_RATE, 12000);			tmp |= AC97_MEA_ADC2 | AC97_MEA_DAC2;		}		if (ac97->ext_mid & AC97_MEI_HANDSET) {			snd_ac97_write_cache(ac97, AC97_HANDSET_RATE, 12000);			tmp |= AC97_MEA_HADC | AC97_MEA_HDAC;		}		snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xff00 & ~(tmp << 8));		udelay(100);		/* nothing should be in powerdown mode */		snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xff00 & ~(tmp << 8));		end_time = jiffies + (HZ / 10);		do {			if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp)				goto __ready_ok;			set_current_state(TASK_UNINTERRUPTIBLE);			schedule_timeout(HZ/10);		} while (time_after_eq(end_time, jiffies));		snd_printk(KERN_ERR "MC'97 %d converters and GPIO not ready (0x%x)\n", ac97->num, snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS));	}	      __ready_ok:	if (ac97_is_audio(ac97))		ac97->addr = (ac97->ext_id & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT;	else		ac97->addr = (ac97->ext_mid & AC97_MEI_ADDR_MASK) >> AC97_MEI_ADDR_SHIFT;	if (ac97->ext_id & 0x0189)	/* L/R, MIC, SDAC, LDAC VRA support */		snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, ac97->ext_id & 0x0189);	if (ac97->ext_id & AC97_EI_VRA) {	/* VRA support */		snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, 0, &ac97->rates[AC97_RATES_FRONT_DAC]);		snd_ac97_determine_rates(ac97, 

⌨️ 快捷键说明

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