示例#1
0
static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
{
	u16 retry_cnt = 0;

retry:
	if (try_warm && soc_ac97_ops.warm_reset) {
		soc_ac97_ops.warm_reset(codec->ac97);
		if (ac97_read(codec, AC97_RESET) == 0x0090)
			return 1;
	}

	soc_ac97_ops.reset(codec->ac97);
	/* Set bit 16slot in register 74h, then every slot will has only 16
	 * bits. This command is sent out in 20bit mode, in which case the
	 * first nibble of data is eaten by the addr. (Tag is always 16 bit)*/
	ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900);

	if (ac97_read(codec, AC97_RESET)  != 0x0090)
		goto err;
	return 0;

err:
	while (retry_cnt++ < 10)
		goto retry;

	printk(KERN_ERR "AD1980 AC97 reset failed\n");
	return -EIO;
}
static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
{
	u16 retry_cnt = 0;

retry:
	if (try_warm && soc_ac97_ops.warm_reset) {
		soc_ac97_ops.warm_reset(codec->ac97);
		if (ac97_read(codec, AC97_RESET) == 0x0090)
			return 1;
	}

	soc_ac97_ops.reset(codec->ac97);
	ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900);

	if (ac97_read(codec, AC97_RESET)  != 0x0090)
		goto err;
	return 0;

err:
	while (retry_cnt++ < 10)
		goto retry;

	printk(KERN_ERR "AD1980 AC97 reset failed\n");
	return -EIO;
}
static int ad1980_soc_probe(struct snd_soc_codec *codec)
{
	int ret;
	u16 vendor_id2;
	u16 ext_status;

	printk(KERN_INFO "AD1980 SoC Audio Codec\n");

	ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
	if (ret < 0) {
		printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
		return ret;
	}

	ret = ad1980_reset(codec, 0);
	if (ret < 0) {
		printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
		goto reset_err;
	}

	
	if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144) {
		ret = -ENODEV;
		goto reset_err;
	}

	vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);

	if (vendor_id2 != 0x5370) {
		if (vendor_id2 != 0x5374) {
			ret = -ENODEV;
			goto reset_err;
		} else {
			printk(KERN_WARNING "ad1980: "
				"Found AD1981 - only 2/2 IN/OUT Channels "
				"supported\n");
		}
	}

	
	ac97_write(codec, AC97_MASTER, 0x0000);
	ac97_write(codec, AC97_PCM, 0x0000);
	ac97_write(codec, AC97_REC_GAIN, 0x0000);
	ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
	ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);

	
	ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
	ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);

	snd_soc_add_codec_controls(codec, ad1980_snd_ac97_controls,
				ARRAY_SIZE(ad1980_snd_ac97_controls));

	return 0;

reset_err:
	snd_soc_free_ac97_codec(codec);
	return ret;
}
示例#4
0
static int ad1980_soc_probe(struct snd_soc_codec *codec)
{
	int ret;
	u16 vendor_id2;
	u16 ext_status;

;

	ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
	if (ret < 0) {
;
		return ret;
	}

	ret = ad1980_reset(codec, 0);
	if (ret < 0) {
;
		goto reset_err;
	}

	/* Read out vendor ID to make sure it is ad1980 */
	if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144)
		goto reset_err;

	vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);

	if (vendor_id2 != 0x5370) {
		if (vendor_id2 != 0x5374)
			goto reset_err;
		else
//			printk(KERN_WARNING "ad1980: "
//				"Found AD1981 - only 2/2 IN/OUT Channels "
;
	}

	/* unmute captures and playbacks volume */
	ac97_write(codec, AC97_MASTER, 0x0000);
	ac97_write(codec, AC97_PCM, 0x0000);
	ac97_write(codec, AC97_REC_GAIN, 0x0000);
	ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
	ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);

	/*power on LFE/CENTER/Surround DACs*/
	ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
	ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);

	snd_soc_add_controls(codec, ad1980_snd_ac97_controls,
				ARRAY_SIZE(ad1980_snd_ac97_controls));

	return 0;

reset_err:
	snd_soc_free_ac97_codec(codec);
	return ret;
}
示例#5
0
文件: ac97audio.c 项目: PyroOS/Pyro
/** Sets the sampling rate.
 * \par Description:
 * Sets the sampling rate.
 * \param nCodec - Codec.
 * \param nReg - Register.
 * \param nRate - Sampling rate.
 * \author	Arno Klenke
 *****************************************************************************/
status_t ac97_set_rate( AC97AudioDriver_s* psDriver, int nCodec, uint8 nReg, uint nRate )
{
	/* Check double-rate rates */
	printk("Set rate %i\n", nRate );
	if( ( psDriver->nExtID[nCodec] & AC97_EI_DRA ) && nReg == AC97_PCM_FRONT_DAC_RATE )
	{
		uint16 nVal = ac97_read( psDriver, nCodec, AC97_EXTENDED_STATUS );
		nVal &= ~AC97_EA_DRA;
		if( nRate > 48000 )
			nVal |= AC97_EA_DRA;
		ac97_write( psDriver, nCodec, AC97_EXTENDED_STATUS, nVal );
	}
	
	if( nReg == AC97_SPDIF_CONTROL )
	{
		/* TODO: Check if this is right */
		uint16 nVal = AC97_SC_COPY;
		switch( nRate )
		{
			case 44100:
				nVal |= AC97_SC_SPSR_44K;
			break;
			case 48000:
				nVal |= AC97_SC_SPSR_48K;
			break;
			case 32000:
				nVal |= AC97_SC_SPSR_32K;
			break;
			default:
				ac97_enable_spdif( psDriver, nCodec, false );
				return( -EINVAL );
			break;
		}
		
		/* Disable SPDIF */
		ac97_enable_spdif( psDriver, nCodec, false );
		/* Write value */
		ac97_write( psDriver, nCodec, nReg, nVal );
		/* Enable spdif */
		ac97_enable_spdif( psDriver, nCodec, true );
		printk( "SPDIF programmed\n" );
		return( 0 );
	}
	
	ac97_write( psDriver, nCodec, nReg, nRate );
	uint16 nNewRate = ac97_read( psDriver, nCodec, nReg );
	
	if( nRate != nNewRate )
	{
		printk( "AC97SetRate(): Wanted %i, Got %i\n", (int)nRate, (int)nNewRate );
		return( -EINVAL );
	}
	
	return( 0 );
}
示例#6
0
文件: ac97audio.c 项目: PyroOS/Pyro
/** Test the resolution of one volume control.
 * \par Description:
 * Test the resolution of one volume control.
 * \param nCodec - Codec.
 * \param nReg - Register.
 * \internal
 * \author	Arno Klenke
 *****************************************************************************/
static int ac97_test_resolution( AC97AudioDriver_s* psDriver, int nCodec, uint8 nReg )
{
	int nRes = 0;
	if( ac97_write( psDriver, nCodec, nReg, 0x0008 ) == 0 && ac97_read( psDriver, nCodec, nReg ) == 0x0008 )
		nRes = 4;
	if( ac97_write( psDriver, nCodec, nReg, 0x0010 ) == 0 && ac97_read( psDriver, nCodec, nReg ) == 0x0010 )
		nRes = 5;
	if( ac97_write( psDriver, nCodec, nReg, 0x0020 ) == 0 && ac97_read( psDriver, nCodec, nReg ) == 0x0020 )
		nRes = 6;
	//printk( "Register %x resolution %i\n", (uint)nReg, nRes );
	return( nRes );
}
示例#7
0
文件: ac97audio.c 项目: PyroOS/Pyro
/** Handle ac97 driver ioctl.
 * \par Description:
 * Handle ac97 driver ioctl.
 * \param psDriver - AC97 driver structure.
 * \param nCommand - Command.
 * \param pArgs - Args.
 * \param bFromKernel - Ioctl from kernel.
 * \author	Arno Klenke
 *****************************************************************************/
status_t ac97_ioctl( AC97AudioDriver_s* psDriver, uint32 nCommand, void *pArgs, bool bFromKernel )
{
	switch( nCommand )
	{
		case AC97_GET_CODEC_INFO:
			memcpy_to_user( pArgs, psDriver, sizeof( AC97AudioDriver_s ) );
			break;
		case AC97_WAIT:
		{
			AC97RegOp_s sOp;
			memcpy_from_user( &sOp, pArgs, sizeof( AC97RegOp_s ) );
			psDriver->pfWait( psDriver->pDriverData, sOp.nCodec );
		}
		break;
		case AC97_READ:
		{
			AC97RegOp_s sOp;
			memcpy_from_user( &sOp, pArgs, sizeof( AC97RegOp_s ) );
			sOp.nVal = ac97_read( psDriver, sOp.nCodec, sOp.nReg );
			memcpy_to_user( pArgs, &sOp, sizeof( AC97RegOp_s ) );
		}
		break;
		case AC97_WRITE:
		{
			AC97RegOp_s sOp;
			memcpy_from_user( &sOp, pArgs, sizeof( AC97RegOp_s ) );
			ac97_write( psDriver, sOp.nCodec, sOp.nReg, sOp.nVal );
		}
		break;
		default:
			return( -EINVAL );
	}
	return( 0 );
}
示例#8
0
static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
				 struct snd_kcontrol *kcontrol, int event)
{
	struct snd_soc_codec *codec = w->codec;
	u16 status, rate;

	BUG_ON(event != SND_SOC_DAPM_PRE_PMD);

	/* Gracefully shut down the voice interface. */
	status = ac97_read(codec, AC97_EXTENDED_MID) | 0x1000;
	rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF;
	ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200);
	schedule_timeout_interruptible(msecs_to_jiffies(1));
	ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00);
	ac97_write(codec, AC97_EXTENDED_MID, status);

	return 0;
}
示例#9
0
/* We have to create a fake left and right HP mixers because
 * the codec only has a single control that is shared by both channels.
 * This makes it impossible to determine the audio path using the current
 * register map, thus we add a new (virtual) register to help determine the
 * audio route within the device.
 */
static int mixer_event(struct snd_soc_dapm_widget *w,
	struct snd_kcontrol *kcontrol, int event)
{
	u16 l, r, beep, tone, phone, rec, pcm, aux;

	l = ac97_read(w->codec, HPL_MIXER);
	r = ac97_read(w->codec, HPR_MIXER);
	beep = ac97_read(w->codec, AC97_PC_BEEP);
	tone = ac97_read(w->codec, AC97_MASTER_TONE);
	phone = ac97_read(w->codec, AC97_PHONE);
	rec = ac97_read(w->codec, AC97_REC_SEL);
	pcm = ac97_read(w->codec, AC97_PCM);
	aux = ac97_read(w->codec, AC97_AUX);

	if (event & SND_SOC_DAPM_PRE_REG)
		return 0;
	if ((l & 0x1) || (r & 0x1))
		ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
	else
		ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);

	if ((l & 0x2) || (r & 0x2))
		ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff);
	else
		ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000);

	if ((l & 0x4) || (r & 0x4))
		ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
	else
		ac97_write(w->codec, AC97_PHONE, phone | 0x8000);

	if ((l & 0x8) || (r & 0x8))
		ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff);
	else
		ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000);

	if ((l & 0x10) || (r & 0x10))
		ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
	else
		ac97_write(w->codec, AC97_PCM, pcm | 0x8000);

	if ((l & 0x20) || (r & 0x20))
		ac97_write(w->codec, AC97_AUX, aux & 0x7fff);
	else
		ac97_write(w->codec, AC97_AUX, aux | 0x8000);

	return 0;
}
示例#10
0
文件: ac97audio.c 项目: PyroOS/Pyro
/** Enables or disables the spdif output.
 * \par Description:
 * Enables or disables the spdif output.
 * \par Note:
 * Use the AC97SupportsSPDIF() method first.
 * \param nCodec - Codec.
 * \param bEnable - Enable or disable.
 * \author	Arno Klenke
 *****************************************************************************/
status_t ac97_enable_spdif( AC97AudioDriver_s* psDriver, int nCodec, bool bEnable )
{
	if( !ac97_supports_spdif( psDriver, nCodec ) )
		return( -EINVAL );
	
	uint16 nVal = ac97_read( psDriver, nCodec, AC97_EXTENDED_STATUS );
	nVal &= ~AC97_EA_SPDIF;
	if( bEnable )
		nVal |= AC97_EA_SPDIF;
	ac97_write( psDriver, nCodec, AC97_EXTENDED_STATUS, nVal );
	printk( "EXT %x\n", (uint)nVal );
	
	return( 0 );
}
示例#11
0
static int ad1980_soc_probe(struct snd_soc_codec *codec)
{
	int ret;
	u16 vendor_id2;
	u16 ext_status;

#ifdef CONFIG_DEBUG_PRINTK
	printk(KERN_INFO "AD1980 SoC Audio Codec\n");
#else
	;
#endif

	ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
	if (ret < 0) {
		printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
		return ret;
	}

	ret = ad1980_reset(codec, 0);
	if (ret < 0) {
		printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
		goto reset_err;
	}

	/* Read out vendor ID to make sure it is ad1980 */
	if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144)
		goto reset_err;

	vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);

	if (vendor_id2 != 0x5370) {
		if (vendor_id2 != 0x5374)
			goto reset_err;
		else
#ifdef CONFIG_DEBUG_PRINTK
			printk(KERN_WARNING "ad1980: "
				"Found AD1981 - only 2/2 IN/OUT Channels "
				"supported\n");
#else
			;
#endif
	}

	/* unmute captures and playbacks volume */
	ac97_write(codec, AC97_MASTER, 0x0000);
	ac97_write(codec, AC97_PCM, 0x0000);
	ac97_write(codec, AC97_REC_GAIN, 0x0000);
	ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
	ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);

	/*power on LFE/CENTER/Surround DACs*/
	ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
	ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);

	snd_soc_add_controls(codec, ad1980_snd_ac97_controls,
				ARRAY_SIZE(ad1980_snd_ac97_controls));

	return 0;

reset_err:
	snd_soc_free_ac97_codec(codec);
	return ret;
}
示例#12
0
static int ad1980_soc_probe(struct platform_device *pdev)
{
	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
	struct snd_soc_codec *codec;
	int ret = 0;
	u16 vendor_id2;
	u16 ext_status;

	printk(KERN_INFO "AD1980 SoC Audio Codec\n");

	socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
	if (socdev->codec == NULL)
		return -ENOMEM;
	codec = socdev->codec;
	mutex_init(&codec->mutex);

	codec->reg_cache =
		kzalloc(sizeof(u16) * ARRAY_SIZE(ad1980_reg), GFP_KERNEL);
	if (codec->reg_cache == NULL) {
		ret = -ENOMEM;
		goto cache_err;
	}
	memcpy(codec->reg_cache, ad1980_reg, sizeof(u16) * \
			ARRAY_SIZE(ad1980_reg));
	codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(ad1980_reg);
	codec->reg_cache_step = 2;
	codec->name = "AD1980";
	codec->owner = THIS_MODULE;
	codec->dai = &ad1980_dai;
	codec->num_dai = 1;
	codec->write = ac97_write;
	codec->read = ac97_read;
	INIT_LIST_HEAD(&codec->dapm_widgets);
	INIT_LIST_HEAD(&codec->dapm_paths);

	ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
	if (ret < 0) {
		printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
		goto codec_err;
	}

	/* register pcms */
	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
	if (ret < 0)
		goto pcm_err;


	ret = ad1980_reset(codec, 0);
	if (ret < 0) {
		printk(KERN_ERR "AC97 link error\n");
		goto reset_err;
	}

	/* Read out vendor ID to make sure it is ad1980 */
	if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144)
		goto reset_err;

	vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);

	if (vendor_id2 != 0x5370) {
		if (vendor_id2 != 0x5374)
			goto reset_err;
		else
			printk(KERN_WARNING "ad1980: "
				"Found AD1981 - only 2/2 IN/OUT Channels "
				"supported\n");
	}

	/* unmute captures and playbacks volume */
	ac97_write(codec, AC97_MASTER, 0x0000);
	ac97_write(codec, AC97_PCM, 0x0000);
	ac97_write(codec, AC97_REC_GAIN, 0x0000);
	ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
	ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);

	/*power on LFE/CENTER/Surround DACs*/
	ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
	ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);

	ad1980_add_controls(codec);
	ret = snd_soc_register_card(socdev);
	if (ret < 0) {
		printk(KERN_ERR "ad1980: failed to register card\n");
		goto reset_err;
	}

	return 0;

reset_err:
	snd_soc_free_pcms(socdev);

pcm_err:
	snd_soc_free_ac97_codec(codec);

codec_err:
	kfree(codec->reg_cache);

cache_err:
	kfree(socdev->codec);
	socdev->codec = NULL;
	return ret;
}
示例#13
0
文件: ac97audio.c 项目: PyroOS/Pyro
/** Initializes one codec
 * \par Description:
 * Initializes one codec
 * \param nCodec - Codec.
 * \internal
 * \author	Arno Klenke
 *****************************************************************************/
status_t ac97_initialize_codec( AC97AudioDriver_s* psDriver, int nCodec )
{
	AC97Mixer_s sMixer;
	
	printk( "Initialize AC97 codec #%i\n", nCodec );
	ac97_wait( psDriver, nCodec );
	
	psDriver->nID[nCodec] = ac97_read( psDriver, nCodec, AC97_VENDOR_ID1 ) << 16;
	psDriver->nID[nCodec] |= ac97_read( psDriver, nCodec, AC97_VENDOR_ID2 );
	
	printk( "AC97 codec id: 0x%x\n", psDriver->nID[nCodec] );
	
	/* Check if this is a modem codec */
	uint16 nExtM = ac97_read( psDriver, nCodec, AC97_EXTENDED_MODEM_ID );
	if( nExtM & 1 )
	{
		printk( "Found AC97 modem codec\n" );
		return( -ENODEV );
	}
	
	/* Read caps */
	psDriver->nBasicID[nCodec] = ac97_read( psDriver, nCodec, AC97_RESET );
	psDriver->nExtID[nCodec] = ac97_read( psDriver, nCodec, AC97_EXTENDED_ID );
	
	/* Powerup codec */
	ac97_write( psDriver, nCodec, AC97_POWER_CONTROL, 0 );
	ac97_write( psDriver, nCodec, AC97_RESET, 0 );
	snooze( 100 );
	ac97_write( psDriver, nCodec, AC97_POWER_CONTROL, 0 );
	ac97_write( psDriver, nCodec, AC97_GENERAL_PURPOSE, 0 );
	
	/* Show version */
	if( psDriver->nExtID[nCodec] & AC97_EI_REV_23 )
		printk( "AC97 2.2 codec\n" );
	else if( psDriver->nExtID[nCodec] & AC97_EI_REV_22 )
		printk( "AC97 2.2 codec\n" );
	else
		printk( "AC97 2.0 codec\n" );
	
	/* Check for VRA */
	uint16 nExtStatus = ac97_read( psDriver, nCodec, AC97_EXTENDED_STATUS );
	if( psDriver->nExtID[nCodec] & ( AC97_EI_VRA | AC97_EI_VRM ) )
	{
		printk( "Variable bitrates supported\n" );
		nExtStatus |= psDriver->nExtID[nCodec] & ( AC97_EI_VRA | AC97_EI_VRM );
	}
	
	/* Check for Surround DAC */
	if( psDriver->nExtID[nCodec] & AC97_EI_SDAC )
	{
		printk( "Found surround DAC\n" );
		nExtStatus |= AC97_EA_SDAC;
		nExtStatus &= ~AC97_EA_PRJ;
	}
	
	/* Check for Center/LFE DAC */
	if( ( psDriver->nExtID[nCodec] & ( AC97_EI_CDAC | AC97_EI_LDAC ) ) == ( AC97_EI_CDAC | AC97_EI_LDAC ) )
	{
		printk( "Found center/LFE DAC\n" );
		nExtStatus |= psDriver->nExtID[nCodec] & ( AC97_EI_CDAC | AC97_EI_LDAC );
		nExtStatus &= ~( AC97_EA_PRI| AC97_EA_PRK );
	}
	
	bool bSurroundPossible = false;
	
	/* Configure AC97 2.2 codecs to emulate AMAP behaviour */
	if( psDriver->nExtID[nCodec] & AC97_EI_REV_22 )
	{
		psDriver->nExtID[nCodec] &= ~AC97_EI_DACS_SLOT_MASK;
		switch( nCodec )
		{
			case 1:
			case 2:
				psDriver->nExtID[nCodec] |= ( 1 << AC97_EI_DACS_SLOT_SHIFT );
				break;
			case 3:
				psDriver->nExtID[nCodec]|= ( 2 << AC97_EI_DACS_SLOT_SHIFT );
				break;
		}
		ac97_write( psDriver, nCodec, AC97_EXTENDED_ID, psDriver->nExtID[nCodec] );
		bSurroundPossible = true;
	}
	/* Check for AMAP support */
	else if( psDriver->nExtID[nCodec] & AC97_EI_AMAP ) {
		printk( "Codec supports AMAP\n" );
		bSurroundPossible = true;
	}
	
	/* We would need codec specific code to configure this */
	if( nCodec != 0 && !bSurroundPossible )
	{
		printk( "Cannot use this codec\n" );
		return( -ENODEV );
	}
	
	psDriver->nChannels[nCodec] = 2;
	
	/* Add surround channels */
	if( bSurroundPossible )
	{
		if( psDriver->nExtID[nCodec] & AC97_EI_SDAC ) {
			psDriver->nMaxChannels += 2;
			psDriver->nChannels[nCodec] += 2;
		}
		if( ( psDriver->nExtID[nCodec] & ( AC97_EI_CDAC | AC97_EI_LDAC ) ) == ( AC97_EI_CDAC | AC97_EI_LDAC ) ) {
			psDriver->nMaxChannels += 2;
			psDriver->nChannels[nCodec] += 2;
		}
	} else {
		psDriver->nExtID[nCodec] &= ~( AC97_EI_SDAC | AC97_EI_CDAC | AC97_EI_LDAC );
	}
	
	/* Check for SPDIF */
	if( psDriver->nExtID[nCodec] & AC97_EI_SPDIF )
		printk( "SPDIF output supported\n" );
	
	psDriver->nExtStat[nCodec] = nExtStatus;
	ac97_write( psDriver, nCodec, AC97_EXTENDED_STATUS, nExtStatus );
	
	/* Master */
	psDriver->nNumMixer = 0;
	ac97_add_mixer( psDriver, sMixer, true, true, nCodec, AC97_MASTER_VOL_STEREO, ac97_test_resolution( psDriver, nCodec, AC97_MASTER_VOL_STEREO ) );
	ac97_write_stereo_mixer( psDriver, nCodec, AC97_MASTER_VOL_STEREO, 0x43, 0x43 );

	
	/* Headphone */
	if( psDriver->nBasicID[nCodec] & AC97_BC_HEADPHONE )
	{
		ac97_add_mixer( psDriver, sMixer, true, true, nCodec, AC97_HEADPHONE_VOL, ac97_test_resolution( psDriver, nCodec, AC97_HEADPHONE_VOL ) );
		ac97_write_stereo_mixer( psDriver, nCodec, AC97_HEADPHONE_VOL, 0x43, 0x43 );

	}
	
	/* Sourround */
	if( psDriver->nExtID[nCodec] & AC97_EI_SDAC )
	{
		ac97_add_mixer( psDriver, sMixer, true, true, nCodec, AC97_SURROUND_MASTER, ac97_test_resolution( psDriver, nCodec, AC97_SURROUND_MASTER ) );
		ac97_write_stereo_mixer( psDriver, nCodec, AC97_SURROUND_MASTER, 0x43, 0x43 );
	}
	
	/* Center/LFE */
	if( ( psDriver->nExtID[nCodec] & ( AC97_EI_CDAC | AC97_EI_LDAC ) ) == ( AC97_EI_CDAC | AC97_EI_LDAC ) )
	{
		ac97_add_mixer( psDriver, sMixer, true, true, nCodec, AC97_CENTER_LFE_MASTER, ac97_test_resolution( psDriver, nCodec, AC97_CENTER_LFE_MASTER ) );
		ac97_write_stereo_mixer( psDriver, nCodec, AC97_CENTER_LFE_MASTER, 0x43, 0x43 );
	}
	
	/* PCM */
	ac97_add_mixer( psDriver, sMixer, true, true, nCodec, AC97_PCMOUT_VOL, ac97_test_resolution( psDriver, nCodec, AC97_PCMOUT_VOL ) );
	ac97_write_stereo_mixer( psDriver, nCodec, AC97_PCMOUT_VOL, 0x43, 0x43 );
	
	/* LineIn */
	ac97_add_mixer( psDriver, sMixer, true, true, nCodec, AC97_LINEIN_VOL, ac97_test_resolution( psDriver, nCodec, AC97_LINEIN_VOL ) );
	ac97_write_stereo_mixer( psDriver, nCodec, AC97_LINEIN_VOL, 0x43, 0x43 );
	
	/* CD */
	ac97_add_mixer( psDriver, sMixer, true, true, nCodec, AC97_CD_VOL, ac97_test_resolution( psDriver, nCodec, AC97_CD_VOL ) );
	ac97_write_stereo_mixer( psDriver, nCodec, AC97_CD_VOL, 0x43, 0x43 );
	
	/* AUX */
	ac97_add_mixer( psDriver, sMixer, true, true, nCodec, AC97_AUX_VOL, ac97_test_resolution( psDriver, nCodec, AC97_AUX_VOL ) );
	ac97_write_stereo_mixer( psDriver, nCodec, AC97_AUX_VOL, 0x43, 0x43 );
	
	/* Mic */
	ac97_add_mixer( psDriver, sMixer, false, true, nCodec, AC97_MIC_VOL, ac97_test_resolution( psDriver, nCodec, AC97_MIC_VOL ) );
	ac97_write_mic_mixer( psDriver, nCodec, 0x00 );
	
	/* Record Gain */
	ac97_add_mixer( psDriver, sMixer, true, false, nCodec, AC97_RECORD_GAIN, ac97_test_resolution( psDriver, nCodec, AC97_RECORD_GAIN ) );
	ac97_write_stereo_mixer( psDriver, nCodec, AC97_RECORD_GAIN, 0x43, 0x43 );
	
	/* Record Gain Mic */
	ac97_add_mixer( psDriver, sMixer, false, false, nCodec, AC97_RECORD_GAIN_MIC, ac97_test_resolution( psDriver, nCodec, AC97_RECORD_GAIN_MIC ) );
	ac97_write_mono_mixer( psDriver, nCodec, AC97_RECORD_GAIN_MIC, 0x43 );

	
	return( 0 );
}