/*
 *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
 *  Universal routines for AK4531 codec
 *
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#define __SND_OSS_COMPAT__
#define SND_MAIN_OBJECT_FILE
#include "../../include/driver.h"
#include "../../include/mixer.h"
#include "../../include/ak4531_codec.h"

static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531);
static void snd_ak4531_proc_done(ak4531_t * ak4531);

/*
 *
 */
 
#if 0

static void snd_ak4531_dump(ak4531_t *ak4531)
{
	int idx;
	
	for (idx = 0; idx < 0x19; idx++)
		printk("ak4531 0x%x: 0x%x\n", idx, ak4531->regs[idx]);
}

#endif

/*
 *
 */
 
static int snd_ak4531_mute_stereo(ak4531_t *ak4531, int w_flag,
				  unsigned int *bitmap, int reg)
{
	unsigned long flags;
	int change = 0;
	int left, right;

	spin_lock_irqsave(&ak4531->reg_lock, flags);
	left = ak4531->regs[reg + 0] & 0x80 ? 0 : 1;
	right = ak4531->regs[reg + 1] & 0x80 ? 0 : 1;
	if (!w_flag) {
		snd_mixer_set_bit(bitmap, 0, left);
		snd_mixer_set_bit(bitmap, 1, right);
	} else {
		change = left != snd_mixer_get_bit(bitmap, 0) ||
		         right != snd_mixer_get_bit(bitmap, 1);
		ak4531->regs[reg + 0] &= 0x7f;
		if (!snd_mixer_get_bit(bitmap, 0))
			ak4531->regs[reg + 0] |= 0x80;
		ak4531->regs[reg + 1] &= 0x7f;
		if (!snd_mixer_get_bit(bitmap, 1))
			ak4531->regs[reg + 1] |= 0x80;
		ak4531->write(ak4531->private_data, reg + 0, ak4531->regs[reg + 0]);
		ak4531->write(ak4531->private_data, reg + 1, ak4531->regs[reg + 1]);
	}
	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
	return change;  
}                                                                                                                                                                                                                                                                                                            

#define AK4531_STEREO_MUTE(name, reg) \
	static int snd_ak4531_mute_##name(void *private_data, int w_flag, unsigned int *bitmap) \
	{ \
		return snd_ak4531_mute_stereo((ak4531_t *)private_data, w_flag, bitmap, reg); \
	}

AK4531_STEREO_MUTE(master, AK4531_LMASTER)
AK4531_STEREO_MUTE(voice, AK4531_LVOICE)
AK4531_STEREO_MUTE(fm, AK4531_LFM)
AK4531_STEREO_MUTE(cd, AK4531_LCD)
AK4531_STEREO_MUTE(line, AK4531_LLINE)
AK4531_STEREO_MUTE(aux, AK4531_LAUXA)

static int snd_ak4531_mute_mono(ak4531_t *ak4531, int w_flag, int *value,
				int reg)
{
	unsigned long flags;
	int change = 0;
	int val;

	spin_lock_irqsave(&ak4531->reg_lock, flags);
	val = ak4531->regs[reg] & 0x80 ? 0 : 1;
	if (!w_flag) {
		*value = val;
	} else {
		change = val != *value;
		ak4531->regs[reg] &= 0x7f;
		if (!*value)
			ak4531->regs[reg] |= 0x80;
		ak4531->write(ak4531->private_data, reg, ak4531->regs[reg]);
	}
	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
	return change;  
}                                                                                                                                                                                                                                                                                                            

#define AK4531_MONO_MUTE(name, reg) \
	static int snd_ak4531_mute_##name(void *private_data, int w_flag, int *value) \
	{ \
		return snd_ak4531_mute_mono((ak4531_t *)private_data, w_flag, value, reg); \
	}

AK4531_MONO_MUTE(mono1, AK4531_MONO1)
AK4531_MONO_MUTE(mono2, AK4531_MONO2)
AK4531_MONO_MUTE(mic, AK4531_MIC)
AK4531_MONO_MUTE(master_mono, AK4531_MONO_OUT)

static int snd_ak4531_volume_stereo(ak4531_t *ak4531, int w_flag, int *volume,
				    int reg)
{
	unsigned long flags;
	int change = 0, left, right;

	spin_lock_irqsave(&ak4531->reg_lock, flags);
	left = 31 - (ak4531->regs[reg + 0] & 31);
	right = 31 - (ak4531->regs[reg + 1] & 31);
	if (!w_flag) {
		volume[0] = left;
		volume[1] = right;
	} else {
		change = left != volume[0] || right != volume[1];
		ak4531->regs[reg + 0] &= ~31;
		ak4531->regs[reg + 0] |= 31 - volume[0];
		ak4531->regs[reg + 1] &= ~31;
		ak4531->regs[reg + 1] |= 31 - volume[1];
		ak4531->write(ak4531->private_data, reg + 0, ak4531->regs[reg + 0]);
		ak4531->write(ak4531->private_data, reg + 1, ak4531->regs[reg + 1]);
	}
	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
	return change;  
}                                                                                                                                                                                                                                                                                                            

#define AK4531_STEREO_VOLUME(name, reg) \
	static int snd_ak4531_volume_##name(void *private_data, int w_flag, int *volume) \
	{ \
		return snd_ak4531_volume_stereo((ak4531_t *)private_data, w_flag, volume, reg); \
	}

AK4531_STEREO_VOLUME(master, AK4531_LMASTER)
AK4531_STEREO_VOLUME(voice, AK4531_LVOICE)
AK4531_STEREO_VOLUME(fm, AK4531_LFM)
AK4531_STEREO_VOLUME(cd, AK4531_LCD)
AK4531_STEREO_VOLUME(line, AK4531_LLINE)
AK4531_STEREO_VOLUME(aux, AK4531_LAUXA)

static int snd_ak4531_volume_mono(ak4531_t *ak4531, int w_flag, int *volume,
				  int reg)
{
	unsigned long flags;
	int change = 0, val;

	spin_lock_irqsave(&ak4531->reg_lock, flags);
	val = 31 - (ak4531->regs[reg] & 31);
	if (!w_flag) {
		volume[0] = val;
	} else {
		change = val != volume[0];
		ak4531->regs[reg] &= ~31;
		ak4531->regs[reg] |= 31 - volume[0];
		ak4531->write(ak4531->private_data, reg, ak4531->regs[reg]);
	}
	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
	return change;  
}                                                                                                                                                                                                                                                                                                            

#define AK4531_MONO_VOLUME(name, reg) \
	static int snd_ak4531_volume_##name(void *private_data, int w_flag, int *volume) \
	{ \
		return snd_ak4531_volume_mono((ak4531_t *)private_data, w_flag, volume, reg); \
	}

AK4531_MONO_VOLUME(mono1, AK4531_MONO1)
AK4531_MONO_VOLUME(mono2, AK4531_MONO2)
AK4531_MONO_VOLUME(mic, AK4531_MIC)

static int snd_ak4531_mic_boost(void *private_data, int w_flag, int *volume)
{
	ak4531_t *ak4531 = (ak4531_t *)private_data;
	unsigned long flags;
	int change = 0, val;

	spin_lock_irqsave(&ak4531->reg_lock, flags);
	val = ak4531->regs[AK4531_MIC_GAIN] & 1;
	if (!w_flag) {
		volume[0] = val;
	} else {
		change = val != volume[0];
		ak4531->regs[AK4531_MIC_GAIN] &= ~1;
		ak4531->regs[AK4531_MIC_GAIN] |= volume[0] & 1;
		ak4531->write(ak4531->private_data, AK4531_MIC_GAIN, ak4531->regs[AK4531_MIC_GAIN]);
	}
	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
	return change;  
}                                                                                                                                                                                                                                                                                                            

static int snd_ak4531_volume_master_mono(void *private_data, int w_flag,
					 int *volume)
{
	ak4531_t *ak4531 = (ak4531_t *)private_data;
	unsigned long flags;
	int change = 0, val;

	spin_lock_irqsave(&ak4531->reg_lock, flags);
	val = 7 - (ak4531->regs[AK4531_MONO_OUT] & 7);
	if (!w_flag) {
		volume[0] = val;
	} else {
		change = val != volume[0];
		ak4531->regs[AK4531_MONO_OUT] &= ~7;
		ak4531->regs[AK4531_MONO_OUT] |= 7 - volume[0];
		ak4531->write(ak4531->private_data, AK4531_MONO_OUT, ak4531->regs[AK4531_MONO_OUT]);
	}
	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
	return change;  
}                                                                                                                                                                                                                                                                                                            

static int snd_ak4531_stereo_out_sw(ak4531_t *ak4531, int w_flag,
				    unsigned int *bitmap, int reg, int bit)
{
	unsigned long flags;
	int change = 0;
	int left, right;

	bit = 1 << bit;
	spin_lock_irqsave(&ak4531->reg_lock, flags);
	left = ak4531->regs[reg] & (bit >> 0) ? 1 : 0;
	right = ak4531->regs[reg] & (bit >> 1) ? 1 : 0;
	if (!w_flag) {
		snd_mixer_set_bit(bitmap, 0, left);
		snd_mixer_set_bit(bitmap, 1, right);
	} else {
		change = left != snd_mixer_get_bit(bitmap, 0) ||
		         right != snd_mixer_get_bit(bitmap, 1);
		ak4531->regs[reg + 0] &= ~((bit >> 0) | (bit >> 1));
		if (snd_mixer_get_bit(bitmap, 0))
			ak4531->regs[reg] |= bit >> 0;
		if (snd_mixer_get_bit(bitmap, 1))
			ak4531->regs[reg] |= bit >> 1;
		ak4531->write(ak4531->private_data, reg, ak4531->regs[reg]);
	}
	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
	return change;  
}                                                                                                                                                                                                                                                                                                            

#define AK4531_STEREO_OUT_MUTE(name, reg, bit) \
	static int snd_ak4531_out_mute_##name(void *private_data, int w_flag, unsigned int *bitmap) \
	{ \
		return snd_ak4531_stereo_out_sw((ak4531_t *)private_data, w_flag, bitmap, reg, bit); \
	}

AK4531_STEREO_OUT_MUTE(voice, AK4531_OUT_SW2, 3)
AK4531_STEREO_OUT_MUTE(fm, AK4531_OUT_SW1, 6)
AK4531_STEREO_OUT_MUTE(cd, AK4531_OUT_SW1, 2)
AK4531_STEREO_OUT_MUTE(line, AK4531_OUT_SW1, 4)
AK4531_STEREO_OUT_MUTE(aux, AK4531_OUT_SW2, 5)

static int snd_ak4531_mono_out_sw(ak4531_t *ak4531, int w_flag, int *value,
				  int reg, int bit)
{
	unsigned long flags;
	int change = 0;
	int val;

	bit = 1 << bit;
	spin_lock_irqsave(&ak4531->reg_lock, flags);
	val = ak4531->regs[reg] & bit ? 1 : 0;
	if (!w_flag) {
		*value = val;
	} else {
		change = val != *value;
		ak4531->regs[reg] &= ~bit;
		if (*value)
			ak4531->regs[reg] |= bit;
		ak4531->write(ak4531->private_data, reg, ak4531->regs[reg]);
	}
	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
	return change;  
}                                                                                                                                                                                                                                                                                                            

#define AK4531_MONO_OUT_MUTE(name, reg, bit) \
	static int snd_ak4531_out_mute_##name(void *private_data, int w_flag, unsigned int *bitmap) \
	{ \
		return snd_ak4531_mono_out_sw((ak4531_t *)private_data, w_flag, bitmap, reg, bit); \
	}

AK4531_MONO_OUT_MUTE(mono1, AK4531_OUT_SW2, 0)
AK4531_MONO_OUT_MUTE(mono2, AK4531_OUT_SW2, 1)
AK4531_MONO_OUT_MUTE(mic, AK4531_OUT_SW1, 0)

static int snd_ak4531_input_route(ak4531_t *ak4531, int w_flag,
				  unsigned int *prsw,
				  int reg, int bit)
{
	unsigned long flags;
	int change = 0;
	int ll, rl, lr, rr;

	bit = 1 << bit;
	spin_lock_irqsave(&ak4531->reg_lock, flags);
	ll = ak4531->regs[reg + 0] & (bit >> 0) ? 1 : 0;
	rl = ak4531->regs[reg + 0] & (bit >> 1) ? 1 : 0;
	lr = ak4531->regs[reg + 1] & (bit >> 0) ? 1 : 0;
	rr = ak4531->regs[reg + 1] & (bit >> 1) ? 1 : 0;
	if (!w_flag) {
		snd_mixer_set_bit(prsw, 0, ll);
		snd_mixer_set_bit(prsw, 1, rl);
		snd_mixer_set_bit(prsw, 2, lr);
		snd_mixer_set_bit(prsw, 3, rr);
	} else {
		change = ll != snd_mixer_get_bit(prsw, 0) ||
		         rl != snd_mixer_get_bit(prsw, 1) ||
		         lr != snd_mixer_get_bit(prsw, 2) ||
		         rr != snd_mixer_get_bit(prsw, 3);
		ak4531->regs[reg + 0] &= ~((bit >> 0) | (bit >> 1));
		ak4531->regs[reg + 1] &= ~((bit >> 0) | (bit >> 1));
		if (snd_mixer_get_bit(prsw, 0))
			ak4531->regs[reg + 0] |= bit >> 0;
		if (snd_mixer_get_bit(prsw, 1))
			ak4531->regs[reg + 0] |= bit >> 1;
		if (snd_mixer_get_bit(prsw, 2))
			ak4531->regs[reg + 1] |= bit >> 0;
		if (snd_mixer_get_bit(prsw, 3))
			ak4531->regs[reg + 1] |= bit >> 1;
		ak4531->write(ak4531->private_data, reg + 0, ak4531->regs[reg + 0]);
		ak4531->write(ak4531->private_data, reg + 1, ak4531->regs[reg + 1]);
	}
	spin_unlock_irqrestore(&ak4531->reg_lock, flags);	
	return change;
}

#define AK4531_INPUT_ROUTE(name, reg, bit) \
	static int snd_ak4531_input_route_##name(void *private_data, int w_flag, unsigned int *prsw) \
	{ \
		return snd_ak4531_input_route((ak4531_t *)private_data, w_flag, prsw, reg, bit); \
	}

AK4531_INPUT_ROUTE(fm, AK4531_LIN_SW1, 6)
AK4531_INPUT_ROUTE(cd, AK4531_LIN_SW1, 2)
AK4531_INPUT_ROUTE(line, AK4531_LIN_SW1, 4)
AK4531_INPUT_ROUTE(aux, AK4531_LIN_SW2, 4)

static int snd_ak4531_stereo_in_sw(ak4531_t *ak4531, int w_flag,
				   unsigned int *bitmap, int reg, int bit)
{
	unsigned long flags;
	int change = 0;
	int left, right;

	bit = 1 << bit;
	spin_lock_irqsave(&ak4531->reg_lock, flags);
	left = ak4531->regs[reg + 0] & bit ? 1 : 0;
	right = ak4531->regs[reg + 1] & bit ? 1 : 0;
	if (!w_flag) {
		snd_mixer_set_bit(bitmap, 0, left);
		snd_mixer_set_bit(bitmap, 1, right);
	} else {
		change = left != snd_mixer_get_bit(bitmap, 0) ||
		         right != snd_mixer_get_bit(bitmap, 1);
		ak4531->regs[reg + 0] &= ~bit;
		ak4531->regs[reg + 1] &= ~bit;
		if (snd_mixer_get_bit(bitmap, 0))
			ak4531->regs[reg + 0] |= bit;
		if (snd_mixer_get_bit(bitmap, 1))
			ak4531->regs[reg + 1] |= bit;
		ak4531->write(ak4531->private_data, reg + 0, ak4531->regs[reg + 0]);
		ak4531->write(ak4531->private_data, reg + 1, ak4531->regs[reg + 1]);
	}
	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
	return change;  
}

#define AK4531_SW(name, reg, bit) \
	static int snd_ak4531_sw_##name(void *private_data, int w_flag, unsigned int *bitmap) \
	{ \
		return snd_ak4531_stereo_in_sw((ak4531_t *)private_data, w_flag, bitmap, reg, bit); \
	}

AK4531_SW(voice, AK4531_LIN_SW2, 2)
AK4531_SW(mono1, AK4531_LIN_SW2, 0)
AK4531_SW(mono2, AK4531_LIN_SW2, 1)
AK4531_SW(mic, AK4531_LIN_SW1, 0)
AK4531_SW(tmono1, AK4531_LIN_SW2, 6)
AK4531_SW(tmono2, AK4531_LIN_SW2, 5)
AK4531_SW(tmic, AK4531_LIN_SW2, 7)

static int snd_ak4531_mixer_group_ctrl1(ak4531_t *ak4531,
					snd_kmixer_file_t * file,
					int w_flag,
					snd_mixer_group_t * ugroup,
					snd_mixer_volume1_control_t *volume1,
					snd_kmixer_element_t *volume1_element,
					int max,
					snd_mixer_sw1_control_t *sw1,
					snd_kmixer_element_t *sw1_element,
					snd_mixer_sw1_control_t *sw1_out,
					snd_kmixer_element_t *sw1_out_element,
					snd_mixer_sw1_control_t *sw1_in,
					snd_kmixer_element_t *sw1_in_element)
{
	int voices[2];
	unsigned int bitmap;
	int change = 0;

	if (!w_flag) {
		ugroup->caps = 0;
		ugroup->channels = SND_MIXER_CHN_MASK_STEREO;
		ugroup->mute = 0;
		ugroup->capture = 0;
		if (volume1) {
			ugroup->caps |= SND_MIXER_GRPCAP_VOLUME;
			volume1(ak4531, 0, voices);
			ugroup->volume.names.front_left = voices[0];
			ugroup->volume.names.front_right = voices[1];
			ugroup->min = 0;
			ugroup->max = max;
		}
		if (sw1_out) {
			ugroup->caps |= SND_MIXER_GRPCAP_MUTE;
			sw1_out(ak4531, 0, &bitmap);
			if (!snd_mixer_get_bit(&bitmap, 0))
				ugroup->mute |= SND_MIXER_CHN_MASK_FRONT_LEFT;
			if (!snd_mixer_get_bit(&bitmap, 1))
				ugroup->mute |= SND_MIXER_CHN_MASK_FRONT_RIGHT;
		}
		if (sw1_in) {
			ugroup->caps |= SND_MIXER_GRPCAP_CAPTURE;
			sw1_in(ak4531, 0, &bitmap);
			if (snd_mixer_get_bit(&bitmap, 0))
				ugroup->capture |= SND_MIXER_CHN_MASK_FRONT_LEFT;
			if (snd_mixer_get_bit(&bitmap, 1))
				ugroup->capture |= SND_MIXER_CHN_MASK_FRONT_RIGHT;
		}
		if (sw1) {
			ugroup->caps |= SND_MIXER_GRPCAP_MUTE;
			sw1(ak4531, 0, &bitmap);
			if (!snd_mixer_get_bit(&bitmap, 0)) {
				ugroup->mute |= SND_MIXER_CHN_MASK_FRONT_LEFT;
				ugroup->capture &= ~SND_MIXER_CHN_MASK_FRONT_LEFT;
			}
			if (!snd_mixer_get_bit(&bitmap, 1)) {
				ugroup->mute |= SND_MIXER_CHN_MASK_FRONT_RIGHT;
				ugroup->capture &= ~SND_MIXER_CHN_MASK_FRONT_RIGHT;
			}
		}
	} else {
		if (volume1) {
			voices[0] = ugroup->volume.names.front_left % (max+1);
			voices[1] = ugroup->volume.names.front_right % (max+1);
			if (volume1(ak4531, 1, voices) > 0) {
				snd_mixer_element_value_change(file, volume1_element, 0);
				change = 1;
			}
		}
		if (sw1) {
			bitmap = 0;
			if (!(ugroup->mute & SND_MIXER_CHN_MASK_FRONT_LEFT))
				snd_mixer_set_bit(&bitmap, 0, 1);
			if (!(ugroup->mute & SND_MIXER_CHN_MASK_FRONT_RIGHT))
				snd_mixer_set_bit(&bitmap, 1, 1);
			if (ugroup->capture & SND_MIXER_CHN_MASK_FRONT_LEFT)
				snd_mixer_set_bit(&bitmap, 0, 1);
			if (ugroup->capture & SND_MIXER_CHN_MASK_FRONT_RIGHT)
				snd_mixer_set_bit(&bitmap, 1, 1);
			if (sw1 && sw1(ak4531, 1, &bitmap) > 0) {
				snd_mixer_element_value_change(file, sw1_element, 0);
				change = 1;
			}
		}
		if (sw1_out) {
			bitmap = 0;
			if (!(ugroup->mute & SND_MIXER_CHN_MASK_FRONT_LEFT))
				snd_mixer_set_bit(&bitmap, 0, 1);
			if (!(ugroup->mute & SND_MIXER_CHN_MASK_FRONT_RIGHT))
				snd_mixer_set_bit(&bitmap, 1, 1);
			if (sw1_out && sw1_out(ak4531, 1, &bitmap) > 0) {
				snd_mixer_element_value_change(file, sw1_out_element, 0);
				change = 1;
			}
		}
		if (sw1_in) {
			bitmap = 0;
			if (ugroup->capture & SND_MIXER_CHN_MASK_FRONT_LEFT)
				snd_mixer_set_bit(&bitmap, 0, 1);
			if (ugroup->capture & SND_MIXER_CHN_MASK_FRONT_RIGHT)
				snd_mixer_set_bit(&bitmap, 1, 1);
			if (sw1_in(ak4531, 1, &bitmap) > 0) {
				snd_mixer_element_value_change(file, sw1_in_element, 0);
				change = 1;
			}
		}
	}
	return change;
}

static int snd_ak4531_mixer_group_ctrl2(ak4531_t *ak4531,
					snd_kmixer_file_t * file,
					int w_flag,
					snd_mixer_group_t * ugroup,
					snd_mixer_volume1_control_t *volume1,
					snd_kmixer_element_t *volume1_element,
					int max,
					snd_mixer_sw2_control_t *sw2,
					snd_kmixer_element_t *sw2_element,
					snd_mixer_sw2_control_t *sw2_out,
					snd_kmixer_element_t *sw2_out_element,
					snd_mixer_sw2_control_t *sw2_in,
					snd_kmixer_element_t *sw2_in_element)
{
	int voice, value, change = 0;

	if (!w_flag) {
		ugroup->caps = 0;
		ugroup->channels = SND_MIXER_CHN_MASK_MONO;
		ugroup->mute = 0;
		ugroup->capture = 0;
		if (volume1) {
			ugroup->caps |= SND_MIXER_GRPCAP_VOLUME;
			volume1(ak4531, 0, &voice);
			ugroup->volume.names.front_left = voice;
			ugroup->min = 0;
			ugroup->max = max;
		}
		if (sw2_out) {
			ugroup->caps |= SND_MIXER_GRPCAP_MUTE;
			sw2_out(ak4531, 0, &value);
			if (!value)
				ugroup->mute |= SND_MIXER_CHN_MASK_MONO;
		}
		if (sw2_in) {
			ugroup->caps |= SND_MIXER_GRPCAP_CAPTURE;
			sw2_in(ak4531, 0, &value);
			if (value)
				ugroup->capture |= SND_MIXER_CHN_MASK_MONO;
		}
		if (sw2) {
			ugroup->caps |= SND_MIXER_GRPCAP_MUTE;
			sw2(ak4531, 0, &value);
			if (!value) {
				ugroup->mute |= SND_MIXER_CHN_MASK_MONO;
				ugroup->capture &= ~SND_MIXER_CHN_MASK_MONO;
			}
			if (!value) {
				ugroup->mute |= SND_MIXER_CHN_MASK_MONO;
				ugroup->capture &= ~SND_MIXER_CHN_MASK_MONO;
			}
		}
	} else {
		if (volume1) {
			voice = ugroup->volume.names.front_left % (max+1);
			if (volume1(ak4531, 1, &voice) > 0) {
				snd_mixer_element_value_change(file, volume1_element, 0);
				change = 1;
			}
		}
		if (sw2) {
			value = 0;
			if (!(ugroup->mute & SND_MIXER_CHN_MASK_MONO))
				value = 1;
			if (ugroup->capture & SND_MIXER_CHN_MASK_MONO)
				value = 1;
			if (sw2 && sw2(ak4531, 1, &value) > 0) {
				snd_mixer_element_value_change(file, sw2_element, 0);
				change = 1;
			}
		}
		if (sw2_out) {
			value = 0;
			if (!(ugroup->mute & SND_MIXER_CHN_MASK_FRONT_LEFT))
				value = 1;
			if (sw2_out && sw2_out(ak4531, 1, &value) > 0) {
				snd_mixer_element_value_change(file, sw2_out_element, 0);
				change = 1;
			}
		}
		if (sw2_in) {
			value = 0;
			if (ugroup->capture & SND_MIXER_CHN_MASK_MONO)
				value = 1;
			if (sw2_in(ak4531, 1, &value) > 0) {
				snd_mixer_element_value_change(file, sw2_in_element, 0);
				change = 1;
			}
		}
	}
	return change;
}

static int snd_ak4531_mixer_group_ctrl3(ak4531_t *ak4531,
					snd_kmixer_file_t * file,
					int w_flag,
					snd_mixer_group_t * ugroup,
					snd_mixer_volume1_control_t *volume1,
					snd_kmixer_element_t *volume1_element,
					int max,
					snd_mixer_sw1_control_t *sw1,
					snd_kmixer_element_t *sw1_element,
					snd_mixer_sw1_control_t *sw1_out,
					snd_kmixer_element_t *sw1_out_element,
					snd_mixer_sw3_control_t *sw3_in,
					snd_kmixer_element_t *sw3_in_element)
{
	int voices[2];
	unsigned int bitmap;
	int change = 0;

	if (!w_flag) {
		ugroup->caps = 0;
		ugroup->channels = SND_MIXER_CHN_MASK_STEREO;
		ugroup->mute = 0;
		ugroup->capture = 0;
		if (volume1) {
			ugroup->caps |= SND_MIXER_GRPCAP_VOLUME;
			volume1(ak4531, 0, voices);
			ugroup->volume.names.front_left = voices[0];
			ugroup->volume.names.front_right = voices[1];
			ugroup->min = 0;
			ugroup->max = max;
		}
		if (sw1_out) {
			ugroup->caps |= SND_MIXER_GRPCAP_MUTE;
			sw1_out(ak4531, 0, &bitmap);
			if (!snd_mixer_get_bit(&bitmap, 0))
				ugroup->mute |= SND_MIXER_CHN_MASK_FRONT_LEFT;
			if (!snd_mixer_get_bit(&bitmap, 1))
				ugroup->mute |= SND_MIXER_CHN_MASK_FRONT_RIGHT;
		}
		if (sw3_in) {
			ugroup->caps |= SND_MIXER_GRPCAP_CAPTURE;
			sw3_in(ak4531, 0, &bitmap);
			if (snd_mixer_get_bit(&bitmap, 0))
				ugroup->capture |= SND_MIXER_CHN_MASK_FRONT_LEFT;
			if (snd_mixer_get_bit(&bitmap, 3))
				ugroup->capture |= SND_MIXER_CHN_MASK_FRONT_RIGHT;
		}
		if (sw1) {
			ugroup->caps |= SND_MIXER_GRPCAP_MUTE;
			sw1(ak4531, 0, &bitmap);
			if (!snd_mixer_get_bit(&bitmap, 0)) {
				ugroup->mute |= SND_MIXER_CHN_MASK_FRONT_LEFT;
				ugroup->capture &= ~SND_MIXER_CHN_MASK_FRONT_LEFT;
			}
			if (!snd_mixer_get_bit(&bitmap, 1)) {
				ugroup->mute |= SND_MIXER_CHN_MASK_FRONT_RIGHT;
				ugroup->capture &= ~SND_MIXER_CHN_MASK_FRONT_RIGHT;
			}
		}
	} else {
		if (volume1) {
			voices[0] = ugroup->volume.names.front_left % (max+1);
			voices[1] = ugroup->volume.names.front_right % (max+1);
			if (volume1(ak4531, 1, voices) > 0) {
				snd_mixer_element_value_change(file, volume1_element, 0);
				change = 1;
			}
		}
		if (sw1) {
			bitmap = 0;
			if (!(ugroup->mute & SND_MIXER_CHN_MASK_FRONT_LEFT))
				snd_mixer_set_bit(&bitmap, 0, 1);
			if (!(ugroup->mute & SND_MIXER_CHN_MASK_FRONT_RIGHT))
				snd_mixer_set_bit(&bitmap, 1, 1);
			if (ugroup->capture & SND_MIXER_CHN_MASK_FRONT_LEFT)
				snd_mixer_set_bit(&bitmap, 0, 1);
			if (ugroup->capture & SND_MIXER_CHN_MASK_FRONT_RIGHT)
				snd_mixer_set_bit(&bitmap, 1, 1);
			if (sw1 && sw1(ak4531, 1, &bitmap) > 0) {
				snd_mixer_element_value_change(file, sw1_element, 0);
				change = 1;
			}
		}
		if (sw1_out) {
			bitmap = 0;
			if (!(ugroup->mute & SND_MIXER_CHN_MASK_FRONT_LEFT))
				snd_mixer_set_bit(&bitmap, 0, 1);
			if (!(ugroup->mute & SND_MIXER_CHN_MASK_FRONT_RIGHT))
				snd_mixer_set_bit(&bitmap, 1, 1);
			if (sw1_out && sw1_out(ak4531, 1, &bitmap) > 0) {
				snd_mixer_element_value_change(file, sw1_out_element, 0);
				change = 1;
			}
		}
		if (sw3_in) {
			bitmap = 0;
			if (ugroup->capture & SND_MIXER_CHN_MASK_FRONT_LEFT)
				snd_mixer_set_bit(&bitmap, 0, 1);
			if (ugroup->capture & SND_MIXER_CHN_MASK_FRONT_RIGHT)
				snd_mixer_set_bit(&bitmap, 3, 1);
			if (sw3_in(ak4531, 1, &bitmap) > 0) {
				snd_mixer_element_value_change(file, sw3_in_element, 0);
				change = 1;
			}
		}
	}
	return change;
}

static int snd_ak4531_mixer_group_ctrl4(ak4531_t *ak4531,
					snd_kmixer_file_t * file,
					int w_flag,
					snd_mixer_group_t * ugroup,
					snd_mixer_volume1_control_t *volume1,
					snd_kmixer_element_t *volume1_element,
					int max,
					snd_mixer_sw2_control_t *sw2,
					snd_kmixer_element_t *sw2_element,
					snd_mixer_sw1_control_t *sw1_out,
					snd_kmixer_element_t *sw1_out_element,
					snd_mixer_sw1_control_t *sw1_in,
					snd_kmixer_element_t *sw1_in_element)
{
	int voice, value;
	unsigned int bitmap;
	int change = 0;

	if (!w_flag) {
		ugroup->caps = 0;
		ugroup->channels = SND_MIXER_CHN_MASK_STEREO;
		ugroup->mute = 0;
		ugroup->capture = 0;
		if (volume1) {
			ugroup->caps |= SND_MIXER_GRPCAP_VOLUME | SND_MIXER_GRPCAP_JOINTLY_VOLUME;
			volume1(ak4531, 0, &voice);
			ugroup->volume.names.front_left = voice;
			ugroup->volume.names.front_right = voice;
			ugroup->min = 0;
			ugroup->max = max;
		}
		if (sw1_out) {
			ugroup->caps |= SND_MIXER_GRPCAP_MUTE;
			sw1_out(ak4531, 0, &bitmap);
			if (!snd_mixer_get_bit(&bitmap, 0))
				ugroup->mute |= SND_MIXER_CHN_MASK_FRONT_LEFT;
			if (!snd_mixer_get_bit(&bitmap, 1))
				ugroup->mute |= SND_MIXER_CHN_MASK_FRONT_RIGHT;
		}
		if (sw1_in) {
			ugroup->caps |= SND_MIXER_GRPCAP_CAPTURE;
			sw1_in(ak4531, 0, &bitmap);
			if (snd_mixer_get_bit(&bitmap, 0))
				ugroup->capture |= SND_MIXER_CHN_MASK_FRONT_LEFT;
			if (snd_mixer_get_bit(&bitmap, 1))
				ugroup->capture |= SND_MIXER_CHN_MASK_FRONT_RIGHT;
		}
		if (sw2) {
			ugroup->caps |= SND_MIXER_GRPCAP_MUTE;
			sw2(ak4531, 0, &value);
			if (!value) {
				ugroup->mute |= SND_MIXER_CHN_MASK_FRONT_LEFT;
				ugroup->capture &= ~SND_MIXER_CHN_MASK_FRONT_LEFT;
			}
			if (!value) {
				ugroup->mute |= SND_MIXER_CHN_MASK_FRONT_RIGHT;
				ugroup->capture &= ~SND_MIXER_CHN_MASK_FRONT_RIGHT;
			}
		}
	} else {
		if (volume1) {
			voice = ugroup->volume.names.front_left % (max+1);
			if (volume1(ak4531, 1, &voice) > 0) {
				snd_mixer_element_value_change(file, volume1_element, 0);
				change = 1;
			}
		}
		if (sw2) {
			value = 0;
			if (!(ugroup->mute & SND_MIXER_CHN_MASK_STEREO))
				value = 1;
			if (ugroup->capture & SND_MIXER_CHN_MASK_STEREO)
				value = 1;
			if (sw2 && sw2(ak4531, 1, &value) > 0) {
				snd_mixer_element_value_change(file, sw2_element, 0);
				change = 1;
			}
		}
		if (sw1_out) {
			bitmap = 0;
			if (!(ugroup->mute & SND_MIXER_CHN_MASK_FRONT_LEFT))
				snd_mixer_set_bit(&bitmap, 0, 1);
			if (!(ugroup->mute & SND_MIXER_CHN_MASK_FRONT_RIGHT))
				snd_mixer_set_bit(&bitmap, 1, 1);
			if (sw1_out && sw1_out(ak4531, 1, &bitmap) > 0) {
				snd_mixer_element_value_change(file, sw1_out_element, 0);
				change = 1;
			}
		}
		if (sw1_in) {
			bitmap = 0;
			if (ugroup->capture & SND_MIXER_CHN_MASK_FRONT_LEFT)
				snd_mixer_set_bit(&bitmap, 0, 1);
			if (ugroup->capture & SND_MIXER_CHN_MASK_FRONT_RIGHT)
				snd_mixer_set_bit(&bitmap, 1, 1);
			if (sw1_in(ak4531, 1, &bitmap) > 0) {
				snd_mixer_element_value_change(file, sw1_in_element, 0);
				change = 1;
			}
		}
	}
	return change;
}

static int snd_ak4531_mixer_group_master(void *private_data,
					 snd_kmixer_file_t * file,
					 int w_flag,
					 snd_mixer_group_t * ugroup)
{
	ak4531_t *ak4531 = (ak4531_t *)private_data;

	return snd_ak4531_mixer_group_ctrl1(ak4531, file, w_flag, ugroup,
					    snd_ak4531_volume_master,
					    ak4531->me_vol_master,
					    31,
					    NULL,
					    NULL,
					    snd_ak4531_mute_master,
					    ak4531->me_sw_master,
					    NULL,
					    NULL);
}

static int snd_ak4531_mixer_group_master_mono(void *private_data,
					      snd_kmixer_file_t * file,
					      int w_flag,
					      snd_mixer_group_t * ugroup)
{
	ak4531_t *ak4531 = (ak4531_t *)private_data;

	return snd_ak4531_mixer_group_ctrl2(ak4531, file, w_flag, ugroup,
					    snd_ak4531_volume_master_mono,
					    ak4531->me_vol_master_mono,
					    31,
					    NULL,
					    NULL,
					    snd_ak4531_mute_master_mono,
					    ak4531->me_sw_master_mono,
					    NULL,
					    NULL);
}

static int snd_ak4531_mixer_group_pcm(void *private_data,
				      snd_kmixer_file_t * file,
				      int w_flag,
				      snd_mixer_group_t * ugroup)
{
	ak4531_t *ak4531 = (ak4531_t *)private_data;

	return snd_ak4531_mixer_group_ctrl1(ak4531, file, w_flag, ugroup,
					    snd_ak4531_volume_voice,
					    ak4531->me_vol_pcm,
					    31,
					    snd_ak4531_mute_voice,
					    ak4531->me_sw_pcm,
					    snd_ak4531_out_mute_voice,
					    ak4531->me_sw_pcm_out,
					    snd_ak4531_sw_voice,
					    ak4531->me_sw_pcm_in);
}

static int snd_ak4531_mixer_group_pcm1(void *private_data,
				       snd_kmixer_file_t * file,
				       int w_flag,
				       snd_mixer_group_t * ugroup)
{
	ak4531_t *ak4531 = (ak4531_t *)private_data;

	return snd_ak4531_mixer_group_ctrl3(ak4531, file, w_flag, ugroup,
					    snd_ak4531_volume_fm,
					    ak4531->me_vol_pcm1,
					    31,
					    snd_ak4531_mute_fm,
					    ak4531->me_sw_pcm1,
					    snd_ak4531_out_mute_fm,
					    ak4531->me_sw_pcm1_out,
					    snd_ak4531_input_route_fm,
					    ak4531->me_sw_pcm1_in);
}

static int snd_ak4531_mixer_group_cd(void *private_data,
				     snd_kmixer_file_t * file,
				     int w_flag,
				     snd_mixer_group_t * ugroup)
{
	ak4531_t *ak4531 = (ak4531_t *)private_data;

	return snd_ak4531_mixer_group_ctrl3(ak4531, file, w_flag, ugroup,
					    snd_ak4531_volume_cd,
					    ak4531->me_vol_cd,
					    31,
					    snd_ak4531_mute_cd,
					    ak4531->me_sw_cd,
					    snd_ak4531_out_mute_cd,
					    ak4531->me_sw_cd_out,
					    snd_ak4531_input_route_cd,
					    ak4531->me_sw_cd_in);
}

static int snd_ak4531_mixer_group_line(void *private_data,
				       snd_kmixer_file_t * file,
				       int w_flag,
				       snd_mixer_group_t * ugroup)
{
	ak4531_t *ak4531 = (ak4531_t *)private_data;

	return snd_ak4531_mixer_group_ctrl3(ak4531, file, w_flag, ugroup,
					    snd_ak4531_volume_line,
					    ak4531->me_vol_line,
					    31,
					    snd_ak4531_mute_line,
					    ak4531->me_sw_line,
					    snd_ak4531_out_mute_line,
					    ak4531->me_sw_line_out,
					    snd_ak4531_input_route_line,
					    ak4531->me_sw_line_in);
}

static int snd_ak4531_mixer_group_aux(void *private_data,
				      snd_kmixer_file_t * file,
				      int w_flag,
				      snd_mixer_group_t * ugroup)
{
	ak4531_t *ak4531 = (ak4531_t *)private_data;

	return snd_ak4531_mixer_group_ctrl3(ak4531, file, w_flag, ugroup,
					    snd_ak4531_volume_aux,
					    ak4531->me_vol_aux,
					    31,
					    snd_ak4531_mute_aux,
					    ak4531->me_sw_aux,
					    snd_ak4531_out_mute_aux,
					    ak4531->me_sw_aux_out,
					    snd_ak4531_input_route_aux,
					    ak4531->me_sw_aux_in);
}

static int snd_ak4531_mixer_group_mono(void *private_data,
				       snd_kmixer_file_t * file,
				       int w_flag,
				       snd_mixer_group_t * ugroup)
{
	ak4531_t *ak4531 = (ak4531_t *)private_data;

	return snd_ak4531_mixer_group_ctrl4(ak4531, file, w_flag, ugroup,
					    snd_ak4531_volume_mono1,
					    ak4531->me_vol_mono,
					    31,
					    snd_ak4531_mute_mono1,
					    ak4531->me_sw_mono,
					    snd_ak4531_out_mute_mono1,
					    ak4531->me_sw_mono_out,
					    snd_ak4531_sw_mono1,
					    ak4531->me_sw_mono_in);
}

static int snd_ak4531_mixer_group_mono1(void *private_data,
				        snd_kmixer_file_t * file,
				        int w_flag,
				        snd_mixer_group_t * ugroup)
{
	ak4531_t *ak4531 = (ak4531_t *)private_data;

	return snd_ak4531_mixer_group_ctrl4(ak4531, file, w_flag, ugroup,
					    snd_ak4531_volume_mono2,
					    ak4531->me_vol_mono1,
					    31,
					    snd_ak4531_mute_mono2,
					    ak4531->me_sw_mono1,
					    snd_ak4531_out_mute_mono2,
					    ak4531->me_sw_mono1_out,
					    snd_ak4531_sw_mono2,
					    ak4531->me_sw_mono1_in);
}

static int snd_ak4531_mixer_group_mic(void *private_data,
				      snd_kmixer_file_t * file,
				      int w_flag,
				      snd_mixer_group_t * ugroup)
{
	ak4531_t *ak4531 = (ak4531_t *)private_data;

	return snd_ak4531_mixer_group_ctrl4(ak4531, file, w_flag, ugroup,
					    snd_ak4531_volume_mic,
					    ak4531->me_vol_mic,
					    31,
					    snd_ak4531_mute_mic,
					    ak4531->me_sw_mic,
					    snd_ak4531_out_mute_mic,
					    ak4531->me_sw_mic_out,
					    snd_ak4531_sw_mic,
					    ak4531->me_sw_mic_in);
}

static int snd_ak4531_get_mix_switch(snd_kmixer_t * mixer,
				     snd_kswitch_t * kswitch,
				     snd_switch_t * uswitch)
{
	ak4531_t *ak4531 = (ak4531_t *) mixer->private_data;

	uswitch->type = SND_SW_TYPE_BOOLEAN;
	uswitch->value.enable = ak4531->regs[AK4531_AD_IN] & 1;
	return 0;
}

static int snd_ak4531_set_mix_switch(snd_kmixer_t * mixer,
				     snd_kswitch_t * kswitch,
				     snd_switch_t * uswitch)
{
	unsigned long flags;
	ak4531_t *ak4531 = (ak4531_t *) mixer->private_data;

	if (uswitch->type != SND_SW_TYPE_BOOLEAN)
		return -EINVAL;
	spin_lock_irqsave(&ak4531->reg_lock, flags);
	ak4531->regs[AK4531_AD_IN] &= ~1;
	if (uswitch->value.enable)
		ak4531->regs[AK4531_AD_IN] |= 1;
	ak4531->write(ak4531->private_data, AK4531_AD_IN, ak4531->regs[AK4531_AD_IN]);
	spin_unlock_irqrestore(&ak4531->reg_lock, flags);
	return 0;
}

#define AK4531_SWITCHES \
		(sizeof(snd_ak4531_switches)/sizeof(snd_kswitch_t))

static snd_kswitch_t snd_ak4531_switches[] =
{
	{
		"AK4531 Recording Source",
		(snd_get_switch_t *)snd_ak4531_get_mix_switch,
		(snd_set_switch_t *)snd_ak4531_set_mix_switch,
		0,
		NULL,
		NULL
	},
};

static void snd_ak4531_mixer_free(void *data)
{
	ak4531_t *ak4531 = (ak4531_t *) data;

	if (ak4531) {
		snd_ak4531_proc_done(ak4531);
		if (ak4531->private_free)
			ak4531->private_free(ak4531);
	}
}

snd_kmixer_t *snd_ak4531_mixer(snd_card_t * card, ak4531_t * ak4531, int pcm_count, int *pcm_devs)
{
	int idx;
	snd_kmixer_t *mixer;
	snd_kswitch_t *kswitch;
	snd_kmixer_group_t *group;
	snd_kmixer_element_t *element;
	static struct snd_mixer_element_volume1_range master_range[2] = {
		{0, 31, -6200, 0},
		{0, 31, -6200, 0}
	};
	static struct snd_mixer_element_volume1_range master_mono_range[1] = {
		{0, 7, -2800, 0},
	};
	static struct snd_mixer_element_volume1_range stereo_range[2] = {
		{0, 31, -5000, 1200},
		{0, 31, -5000, 1200}
	};
	static struct snd_mixer_element_volume1_range mic_gain_range[1] = {
		{0, 1, 0, 3000},
	};
	static snd_mixer_voice_t stereo_voices[2] = {
		{SND_MIXER_VOICE_LEFT, 0},
		{SND_MIXER_VOICE_RIGHT, 0}
	};

	if (!card || !ak4531)
		return NULL;
	ak4531->reg_lock = SPIN_LOCK_UNLOCKED;
	mixer = snd_mixer_new(card, "AK4531");
	if (!mixer)
		return NULL;
	strcpy(mixer->name, "Asahi Kasei AK4531");
	for (idx = 0; idx < AK4531_SWITCHES; idx++)
		kswitch = snd_mixer_switch_new(mixer, &snd_ak4531_switches[idx], ak4531);
	mixer->private_data = ak4531;
	mixer->private_free = snd_ak4531_mixer_free;
	snd_ak4531_proc_init(card, ak4531);
	ak4531->write(ak4531->private_data, AK4531_RESET, 0x03);	/* no RST, PD */
	snd_delay(10);
	ak4531->write(ak4531->private_data, AK4531_CLOCK, 0x00);	/* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off LRCLK2 PLL */
	ak4531->write(ak4531->private_data, AK4531_AD_IN, ak4531->regs[AK4531_AD_IN] = 0);	/* recording source is mixer */
	ak4531->write(ak4531->private_data, AK4531_MIC_GAIN, ak4531->regs[AK4531_MIC_GAIN] = 1);

	/* build accumulators */
	if ((ak4531->me_in_accu = snd_mixer_lib_accu1(mixer, SND_MIXER_ELEMENT_INPUT_ACCU, 0, 0)) == NULL)
		goto __error;
	if ((ak4531->me_out_accu = snd_mixer_lib_accu1(mixer, SND_MIXER_ELEMENT_OUTPUT_ACCU, 0, 0)) == NULL)
		goto __error;
	if ((ak4531->me_mono_accu = snd_mixer_lib_accu1(mixer, SND_MIXER_ELEMENT_MONO_OUT_ACCU, 0, 0)) == NULL)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_out_accu, ak4531->me_mono_accu) < 0)
		goto __error;
	/* build master output */
	if ((group = snd_mixer_lib_group_ctrl(mixer, SND_MIXER_OUT_MASTER, 0, SND_MIXER_OSS_VOLUME, snd_ak4531_mixer_group_master, ak4531)) == NULL)
		goto __error;
	if ((ak4531->me_vol_master = snd_mixer_lib_volume1(mixer, "Master Volume", 0, 2, master_range, snd_ak4531_volume_master, ak4531)) == NULL)
		goto __error; 
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_vol_master) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_out_accu, ak4531->me_vol_master) < 0)
		goto __error;
	if ((ak4531->me_sw_master = snd_mixer_lib_sw1(mixer, "Master Switch", 0, 2, snd_ak4531_mute_master, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_master) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_vol_master, ak4531->me_sw_master) < 0)
		goto __error;
	if ((element = snd_mixer_lib_io_stereo(mixer, SND_MIXER_OUT_MASTER, 0, SND_MIXER_ETYPE_OUTPUT, 0)) == NULL)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_vol_master, element) < 0)
		goto __error;
	/* build mono output */
	if ((group = snd_mixer_lib_group_ctrl(mixer, SND_MIXER_OUT_MASTER_MONO, 0, SND_MIXER_OSS_PHONEOUT, snd_ak4531_mixer_group_master_mono, ak4531)) == NULL)
		goto __error;
	if ((ak4531->me_vol_master_mono = snd_mixer_lib_volume1(mixer, "Master Mono Volume", 0, 1, master_mono_range, snd_ak4531_volume_master_mono, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_vol_master_mono) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_mono_accu, ak4531->me_vol_master_mono) < 0)
		goto __error;
	if ((ak4531->me_sw_master_mono = snd_mixer_lib_sw2(mixer, "Master Mono Switch", 0, snd_ak4531_mute_master_mono, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_master_mono) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_vol_master_mono, ak4531->me_sw_master_mono) < 0)
		goto __error;
	if ((element = snd_mixer_lib_io_mono(mixer, SND_MIXER_OUT_MASTER_MONO, 0, SND_MIXER_ETYPE_OUTPUT, 0)) == NULL)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_master_mono, element) < 0)
		goto __error;
	/* build PCM input */
	if ((group = snd_mixer_lib_group_ctrl(mixer, SND_MIXER_IN_PCM, 0, SND_MIXER_OSS_PCM, snd_ak4531_mixer_group_pcm, ak4531)) == NULL)
		goto __error;
	if ((ak4531->me_playback = snd_mixer_lib_pcm(mixer, SND_MIXER_ELEMENT_PLAYBACK, 0, SND_MIXER_ETYPE_PLAYBACK, 1, pcm_devs)) == NULL)
		goto __error;
	if ((ak4531->me_vol_pcm = snd_mixer_lib_volume1(mixer, "PCM Volume", 0, 2, stereo_range, snd_ak4531_volume_voice, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_vol_pcm) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_playback, ak4531->me_vol_pcm) < 0)
		goto __error;
	if ((ak4531->me_sw_pcm = snd_mixer_lib_sw1(mixer, "PCM Switch", 0, 2, snd_ak4531_mute_voice, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_pcm) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_vol_pcm, ak4531->me_sw_pcm) < 0)
		goto __error;
	if ((ak4531->me_sw_pcm_out = snd_mixer_lib_sw1(mixer, "PCM Output Switch", 0, 2, snd_ak4531_out_mute_voice, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_pcm_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_pcm, ak4531->me_sw_pcm_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_pcm_out, ak4531->me_out_accu) < 0)
		goto __error;
	if ((ak4531->me_sw_pcm_in = snd_mixer_lib_sw1(mixer, "PCM Input Switch", 0, 2, snd_ak4531_sw_voice, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_pcm_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_pcm, ak4531->me_sw_pcm_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_pcm_in, ak4531->me_in_accu) < 0)
		goto __error;
	/* build FM (second PCM) input */
	if ((group = snd_mixer_lib_group_ctrl(mixer, SND_MIXER_IN_PCM, 1, SND_MIXER_OSS_LINE1, snd_ak4531_mixer_group_pcm1, ak4531)) == NULL)
		goto __error;
	if ((ak4531->me_playback1 = snd_mixer_lib_pcm(mixer, SND_MIXER_ELEMENT_PLAYBACK, 1, SND_MIXER_ETYPE_PLAYBACK, 1, pcm_devs + 1)) == NULL)
		goto __error;
	if ((ak4531->me_vol_pcm1 = snd_mixer_lib_volume1(mixer, "PCM Volume", 1, 2, stereo_range, snd_ak4531_volume_fm, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_vol_pcm1) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_playback1, ak4531->me_vol_pcm1) < 0)
		goto __error;
	if ((ak4531->me_sw_pcm1 = snd_mixer_lib_sw1(mixer, "PCM Switch", 1, 2, snd_ak4531_mute_fm, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_pcm1) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_vol_pcm1, ak4531->me_sw_pcm1) < 0)
		goto __error;
	if ((ak4531->me_sw_pcm1_out = snd_mixer_lib_sw1(mixer, "PCM Output Switch", 1, 2, snd_ak4531_out_mute_fm, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_pcm1_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_pcm1, ak4531->me_sw_pcm1_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_pcm1_out, ak4531->me_out_accu) < 0)
		goto __error;
	if ((ak4531->me_sw_pcm1_in = snd_mixer_lib_sw3(mixer, "PCM Input Switch", 1, SND_MIXER_SWITCH3_FULL_FEATURED, 2, stereo_voices, snd_ak4531_input_route_fm, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_pcm1_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_pcm1, ak4531->me_sw_pcm1_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_pcm1_in, ak4531->me_in_accu) < 0)
		goto __error;
	/* build CD input */
	if ((group = snd_mixer_lib_group_ctrl(mixer, SND_MIXER_IN_CD, 0, SND_MIXER_OSS_CD, snd_ak4531_mixer_group_cd, ak4531)) == NULL)
		goto __error;
	if ((element = snd_mixer_lib_io_stereo(mixer, SND_MIXER_IN_CD, 0, SND_MIXER_ETYPE_INPUT, 0)) == NULL)
		goto __error;
	if ((ak4531->me_vol_cd = snd_mixer_lib_volume1(mixer, "CD Volume", 0, 2, stereo_range, snd_ak4531_volume_cd, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_vol_cd) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, element, ak4531->me_vol_cd) < 0)
		goto __error;
	if ((ak4531->me_sw_cd = snd_mixer_lib_sw1(mixer, "CD Switch", 0, 2, snd_ak4531_mute_cd, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_cd) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_vol_cd, ak4531->me_sw_cd) < 0)
		goto __error;
	if ((ak4531->me_sw_cd_out = snd_mixer_lib_sw1(mixer, "CD Output Switch", 0, 2, snd_ak4531_out_mute_cd, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_cd_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_cd, ak4531->me_sw_cd_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_cd_out, ak4531->me_out_accu) < 0)
		goto __error;
	if ((ak4531->me_sw_cd_in = snd_mixer_lib_sw3(mixer, "CD Input Switch", 0, SND_MIXER_SWITCH3_FULL_FEATURED, 2, stereo_voices, snd_ak4531_input_route_cd, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_cd_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_cd, ak4531->me_sw_cd_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_cd_in, ak4531->me_in_accu) < 0)
		goto __error;
	/* build line input */
	if ((group = snd_mixer_lib_group_ctrl(mixer, SND_MIXER_IN_LINE, 0, SND_MIXER_OSS_LINE, snd_ak4531_mixer_group_line, ak4531)) == NULL)
		goto __error;
	if ((element = snd_mixer_lib_io_stereo(mixer, SND_MIXER_IN_LINE, 0, SND_MIXER_ETYPE_INPUT, 0)) == NULL)
		goto __error;
	if ((ak4531->me_vol_line = snd_mixer_lib_volume1(mixer, "Line Volume", 0, 2, stereo_range, snd_ak4531_volume_line, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_vol_line) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, element, ak4531->me_vol_line) < 0)
		goto __error;
	if ((ak4531->me_sw_line = snd_mixer_lib_sw1(mixer, "Line Switch", 0, 2, snd_ak4531_mute_line, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_line) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_vol_line, ak4531->me_sw_line) < 0)
		goto __error;
	if ((ak4531->me_sw_line_out = snd_mixer_lib_sw1(mixer, "Line Output Switch", 0, 2, snd_ak4531_out_mute_line, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_line_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_line, ak4531->me_sw_line_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_line_out, ak4531->me_out_accu) < 0)
		goto __error;
	if ((ak4531->me_sw_line_in = snd_mixer_lib_sw3(mixer, "Line Input Switch", 0, SND_MIXER_SWITCH3_FULL_FEATURED, 2, stereo_voices, snd_ak4531_input_route_line, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_line_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_line, ak4531->me_sw_line_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_line_in, ak4531->me_in_accu) < 0)
		goto __error;
	/* build AUX input */
	if ((group = snd_mixer_lib_group_ctrl(mixer, SND_MIXER_IN_AUX, 0, SND_MIXER_OSS_VIDEO, snd_ak4531_mixer_group_aux, ak4531)) == NULL)
		goto __error;
	if ((element = snd_mixer_lib_io_stereo(mixer, SND_MIXER_IN_AUX, 0, SND_MIXER_ETYPE_INPUT, 0)) == NULL)
		goto __error;
	if ((ak4531->me_vol_aux = snd_mixer_lib_volume1(mixer, "Aux Volume", 0, 2, stereo_range, snd_ak4531_volume_aux, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_vol_aux) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, element, ak4531->me_vol_aux) < 0)
		goto __error;
	if ((ak4531->me_sw_aux = snd_mixer_lib_sw1(mixer, "Aux Switch", 0, 2, snd_ak4531_mute_aux, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_aux) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_vol_aux, ak4531->me_sw_aux) < 0)
		goto __error;
	if ((ak4531->me_sw_aux_out = snd_mixer_lib_sw1(mixer, "Aux Output Switch", 0, 2, snd_ak4531_out_mute_aux, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_aux_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_aux, ak4531->me_sw_aux_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_aux_out, ak4531->me_out_accu) < 0)
		goto __error;
	if ((ak4531->me_sw_aux_in = snd_mixer_lib_sw3(mixer, "Aux Input Switch", 0, SND_MIXER_SWITCH3_FULL_FEATURED, 2, stereo_voices, snd_ak4531_input_route_aux, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_aux_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_aux, ak4531->me_sw_aux_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_aux_in, ak4531->me_in_accu) < 0)
		goto __error;
	/* build mono1 input */
	if ((group = snd_mixer_lib_group_ctrl(mixer, SND_MIXER_IN_MONO, 0, SND_MIXER_OSS_LINE2, snd_ak4531_mixer_group_mono, ak4531)) == NULL)
		goto __error;
	if ((element = snd_mixer_lib_io_mono(mixer, SND_MIXER_IN_MONO, 0, SND_MIXER_ETYPE_INPUT, 0)) == NULL)
		goto __error;

	if ((ak4531->me_sw_mono_bypass = snd_mixer_lib_sw1(mixer, "Mono Bypass Switch", 0, 2, snd_ak4531_sw_tmono1, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_mono_bypass) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, element, ak4531->me_sw_mono_bypass) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mono_bypass, ak4531->me_in_accu) < 0)
		goto __error;

	if ((ak4531->me_vol_mono = snd_mixer_lib_volume1(mixer, "Mono Volume", 0, 2, stereo_range, snd_ak4531_volume_mono1, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_vol_mono) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, element, ak4531->me_vol_mono) < 0)
		goto __error;
	if ((ak4531->me_sw_mono = snd_mixer_lib_sw2(mixer, "Mono Switch", 0, snd_ak4531_mute_mono1, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_mono) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_vol_mono, ak4531->me_sw_mono) < 0)
		goto __error;
	if ((ak4531->me_sw_mono_out = snd_mixer_lib_sw1(mixer, "Mono Output Switch", 0, 2, snd_ak4531_out_mute_mono1, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_mono_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mono, ak4531->me_sw_mono_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mono_out, ak4531->me_out_accu) < 0)
		goto __error;
	if ((ak4531->me_sw_mono_in = snd_mixer_lib_sw1(mixer, "Mono Input Switch", 0, 2, snd_ak4531_sw_mono1, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_mono_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mono, ak4531->me_sw_mono_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mono_in, ak4531->me_in_accu) < 0)
		goto __error;
	/* build mono2 input */
	if ((group = snd_mixer_lib_group_ctrl(mixer, SND_MIXER_IN_MONO, 1, SND_MIXER_OSS_LINE3, snd_ak4531_mixer_group_mono1, ak4531)) == NULL)
		goto __error;
	if ((element = snd_mixer_lib_io_mono(mixer, SND_MIXER_IN_MONO, 1, SND_MIXER_ETYPE_INPUT, 0)) == NULL)
		goto __error;

	if ((ak4531->me_sw_mono1_bypass = snd_mixer_lib_sw1(mixer, "Mono Bypass Switch", 1, 2, snd_ak4531_sw_tmono2, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_mono1_bypass) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, element, ak4531->me_sw_mono1_bypass) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mono1_bypass, ak4531->me_in_accu) < 0)
		goto __error;

	if ((ak4531->me_vol_mono1 = snd_mixer_lib_volume1(mixer, "Mono Volume", 1, 2, stereo_range, snd_ak4531_volume_mono2, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_vol_mono1) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, element, ak4531->me_vol_mono1) < 0)
		goto __error;
	if ((ak4531->me_sw_mono1 = snd_mixer_lib_sw2(mixer, "Mono Switch", 1, snd_ak4531_mute_mono2, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_mono1) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_vol_mono1, ak4531->me_sw_mono1) < 0)
		goto __error;
	if ((ak4531->me_sw_mono1_out = snd_mixer_lib_sw1(mixer, "Mono Output Switch", 1, 2, snd_ak4531_out_mute_mono2, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_mono1_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mono1, ak4531->me_sw_mono1_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mono1_out, ak4531->me_out_accu) < 0)
		goto __error;
	if ((ak4531->me_sw_mono1_in = snd_mixer_lib_sw1(mixer, "Mono Input Switch", 1, 2, snd_ak4531_sw_mono2, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_mono1_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mono1, ak4531->me_sw_mono1_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mono1_in, ak4531->me_in_accu) < 0)
		goto __error;
	/* build mic input */
	if ((element = snd_mixer_lib_io_mono(mixer, SND_MIXER_IN_MIC, 0, SND_MIXER_ETYPE_INPUT, 0)) == NULL)
		goto __error;
	if ((group = snd_mixer_lib_group_ctrl(mixer, SND_MIXER_IN_MIC, 0, SND_MIXER_OSS_MIC, snd_ak4531_mixer_group_mic, ak4531)) == NULL)
		goto __error;

	if ((ak4531->me_vol_mic_gain = snd_mixer_lib_volume1(mixer, "MIC Gain", 0, 1, mic_gain_range, snd_ak4531_mic_boost, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_vol_mic_gain) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, element, ak4531->me_vol_mic_gain) < 0)
		goto __error;

	if ((ak4531->me_sw_mic_bypass = snd_mixer_lib_sw1(mixer, "MIC Bypass Switch", 0, 2, snd_ak4531_sw_tmic, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_mic_bypass) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_vol_mic_gain, ak4531->me_sw_mic_bypass) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mic_bypass, ak4531->me_in_accu) < 0)
		goto __error;

	if ((ak4531->me_vol_mic = snd_mixer_lib_volume1(mixer, "MIC Volume", 0, 1, stereo_range, snd_ak4531_volume_mic, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_vol_mic) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_vol_mic_gain, ak4531->me_vol_mic) < 0)
		goto __error;
	if ((ak4531->me_sw_mic = snd_mixer_lib_sw2(mixer, "MIC Switch", 0, snd_ak4531_mute_mic, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_mic) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_vol_mic, ak4531->me_sw_mic) < 0)
		goto __error;
	if ((ak4531->me_sw_mic_out = snd_mixer_lib_sw1(mixer, "MIC Output Switch", 0, 2, snd_ak4531_out_mute_mic, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_mic_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mic, ak4531->me_sw_mic_out) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mic_out, ak4531->me_out_accu) < 0)
		goto __error;
	if ((ak4531->me_sw_mic_in = snd_mixer_lib_sw1(mixer, "MIC Input Switch", 0, 2, snd_ak4531_sw_mic, ak4531)) == NULL)
		goto __error;
	if (snd_mixer_group_element_add(mixer, group, ak4531->me_sw_mic_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mic, ak4531->me_sw_mic_in) < 0)
		goto __error;
	if (snd_mixer_element_route_add(mixer, ak4531->me_sw_mic_in, ak4531->me_in_accu) < 0)
		goto __error;

#if 0
	snd_ak4531_dump(ak4531);
#endif
	return mixer;

      __error:
      	snd_mixer_free(mixer);
      	return NULL;
}

/*

 */

static void snd_ak4531_proc_read(snd_info_buffer_t * buffer,
                                 void *private_data)
{
	ak4531_t *ak4531 = (ak4531_t *) private_data;

	snd_iprintf(buffer, "Asahi Kasei AK4531\n\n");
	snd_iprintf(buffer, "Recording source   : %s\n"
		    "MIC gain           : %s\n",
		    ak4531->regs[AK4531_AD_IN] & 1 ? "external" : "mixer",
		    ak4531->regs[AK4531_MIC_GAIN] & 1 ? "+30dB" : "+0dB");
}

static void snd_ak4531_proc_init(snd_card_t * card, ak4531_t * ak4531)
{
	snd_info_entry_t *entry;

	if ((entry = snd_info_create_entry(card, "ak4531")) != NULL) {
		entry->private_data = ak4531;
		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
		entry->t.text.read_size = 256;
		entry->t.text.read = snd_ak4531_proc_read;
		if (snd_info_register(entry) < 0) {
			snd_info_free_entry(entry);
			entry = NULL;
		}
	}
	ak4531->proc_entry = entry;
}

static void snd_ak4531_proc_done(ak4531_t * ak4531)
{
	if (ak4531->proc_entry) {
		snd_info_unregister(ak4531->proc_entry);
		ak4531->proc_entry = NULL;
	}
}

EXPORT_SYMBOL(snd_ak4531_mixer);

/*
 *  INIT part
 */

int init_module(void)
{
	return 0;
}

void cleanup_module(void)
{
}
