aboutsummaryrefslogblamecommitdiffstats
path: root/drivers/staging/intel_sst/intelmid_ctrl.c
blob: 19ec474b362a75d496d9dbbb0e6642d034faf603 (plain) (tree)

























                                                                              


                                           

                          




                                 















                                        




                                                     




                                                



















                                                         
                                        

                                       

                                       











































































                                                                              








                                                                         







































                                                                                  


                                          

                           





                                                        


                                                                             
                                                 
                                                                          




                                                                                








                                                                          
                                                 


                                                                    





                                                                          



















                                                                             
                                                     




















                                                        




                                                         









                                                           

                                                   























                                                                             
                                      

























































                                                                             
                                               






















                                                        





                                                         








                                                              

                                                 




















                                                                             
                                                   


































































                                                                              
                                        




                                              
                                                        
                                                       





                                                                 


                                                                  

                                       


















                                                                               



















                                                                             
                                                  
 
                                                     
























                                                                          

                                                                            
                                           
                            
                                           

                                                                          




                                                                         




                                                                           






                                                                 
















































































































































                                                                                 
























































                                                                              








                                                                




























                                                                








                                                                



































                                                                   

  
/*
 *  intelmid_ctrl.c - Intel Sound card driver for MID
 *
 *  Copyright (C) 2008-10 Intel Corp
 *  Authors:	Harsha Priya <priya.harsha@intel.com>
 *		Vinod Koul <vinod.koul@intel.com>
 *		Dharageswari R <dharageswari.r@intel.com>
 *		KP Jeeja <jeeja.kp@intel.com>
 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  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 version 2 of the License.
 *
 *  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.,
 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *  ALSA driver handling mixer controls for Intel MAD chipset
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <sound/core.h>
#include <sound/control.h>
#include "intel_sst.h"
#include "intel_sst_ioctl.h"
#include "intelmid_snd_control.h"
#include "intelmid.h"

#define HW_CH_BASE 4


#define HW_CH_0	"Hw1"
#define HW_CH_1	"Hw2"
#define HW_CH_2	"Hw3"
#define HW_CH_3	"Hw4"

static char *router_dmics[] = {	"DMIC1",
				"DMIC2",
				"DMIC3",
				"DMIC4",
				"DMIC5",
				"DMIC6"
				};

static char *out_names_mrst[] = {"Headphones",
				"Internal speakers"};
static char *in_names_mrst[] = {"AMIC",
				"DMIC",
				"HS_MIC"};
static char *line_out_names_mfld[] = {"Headset",
				"IHF    ",
				"Vibra1 ",
				"Vibra2 ",
				"NONE   "};
static char *out_names_mfld[] = {"Headset ",
				"EarPiece  "};
static char *in_names_mfld[] = {"AMIC",
				"DMIC"};

struct snd_control_val intelmad_ctrl_val[MAX_VENDORS] = {
	{
		.playback_vol_max = 63,
		.playback_vol_min = 0,
		.capture_vol_max = 63,
		.capture_vol_min = 0,
	},
	{
		.playback_vol_max = 0,
		.playback_vol_min = -31,
		.capture_vol_max = 0,
		.capture_vol_min = -20,
	},
	{
		.playback_vol_max = 0,
		.playback_vol_min = -31,
		.capture_vol_max = 0,
		.capture_vol_min = -31,
		.master_vol_max = 0,
		.master_vol_min = -126,
	},
};

/* control path functionalities */

static inline int snd_intelmad_volume_info(struct snd_ctl_elem_info *uinfo,
					int control_type, int max, int min)
{
	WARN_ON(!uinfo);

	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
	uinfo->count = control_type;
	uinfo->value.integer.min = min;
	uinfo->value.integer.max = max;
	return 0;
}

/**
* snd_intelmad_mute_info - provides information about the mute controls
*
* @kcontrol:	pointer to the control
* @uinfo:	pointer to the structure where the control's info need
*		to be filled
*
* This function is called when a mixer application requests for control's info
*/
static int snd_intelmad_mute_info(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_info *uinfo)
{
	WARN_ON(!uinfo);
	WARN_ON(!kcontrol);

	/* set up the mute as a boolean mono control with min-max values */
	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
	uinfo->count = MONO_CNTL;
	uinfo->value.integer.min = MIN_MUTE;
	uinfo->value.integer.max = MAX_MUTE;
	return 0;
}

/**
* snd_intelmad_capture_volume_info - provides info about the volume control
*
* @kcontrol:	pointer to the control
* @uinfo:	pointer to the structure where the control's info need
*		to be filled
*
* This function is called when a mixer application requests for control's info
*/
static int snd_intelmad_capture_volume_info(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_info *uinfo)
{
	snd_intelmad_volume_info(uinfo, MONO_CNTL,
		intelmad_ctrl_val[sst_card_vendor_id].capture_vol_max,
		intelmad_ctrl_val[sst_card_vendor_id].capture_vol_min);
	return 0;
}

/**
* snd_intelmad_playback_volume_info - provides info about the volume control
*
* @kcontrol:	pointer to the control
* @uinfo:	pointer to the structure where the control's info need
*		to be filled
*
* This function is called when a mixer application requests for control's info
*/
static int snd_intelmad_playback_volume_info(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_info *uinfo)
{
	snd_intelmad_volume_info(uinfo, STEREO_CNTL,
		intelmad_ctrl_val[sst_card_vendor_id].playback_vol_max,
		intelmad_ctrl_val[sst_card_vendor_id].playback_vol_min);
	return 0;
}

static int snd_intelmad_master_volume_info(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_info *uinfo)
{
	snd_intelmad_volume_info(uinfo, STEREO_CNTL,
		intelmad_ctrl_val[sst_card_vendor_id].master_vol_max,
		intelmad_ctrl_val[sst_card_vendor_id].master_vol_min);
	return 0;
}

/**
* snd_intelmad_device_info_mrst - provides information about the devices available
*
* @kcontrol:	pointer to the control
* @uinfo:	pointer to the structure where the devices's info need
*		to be filled
*
* This function is called when a mixer application requests for device's info
*/
static int snd_intelmad_device_info_mrst(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_info *uinfo)
{

	WARN_ON(!kcontrol);
	WARN_ON(!uinfo);

	/* setup device select as drop down controls with different values */
	if (kcontrol->id.numid == OUTPUT_SEL)
		uinfo->value.enumerated.items = ARRAY_SIZE(out_names_mrst);
	else
		uinfo->value.enumerated.items = ARRAY_SIZE(in_names_mrst);
	uinfo->count = MONO_CNTL;
	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;

	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
		uinfo->value.enumerated.item = 1;
	if (kcontrol->id.numid == OUTPUT_SEL)
		strncpy(uinfo->value.enumerated.name,
			out_names_mrst[uinfo->value.enumerated.item],
			sizeof(uinfo->value.enumerated.name)-1);
	else
		strncpy(uinfo->value.enumerated.name,
			in_names_mrst[uinfo->value.enumerated.item],
			sizeof(uinfo->value.enumerated.name)-1);
	return 0;
}

static int snd_intelmad_device_info_mfld(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_info *uinfo)
{
	struct snd_pmic_ops *scard_ops;
	struct snd_intelmad *intelmaddata;

	WARN_ON(!kcontrol);
	WARN_ON(!uinfo);

	intelmaddata = kcontrol->private_data;

	WARN_ON(!intelmaddata->sstdrv_ops);

	scard_ops = intelmaddata->sstdrv_ops->scard_ops;
	/* setup device select as drop down controls with different values */
	if (kcontrol->id.numid == OUTPUT_SEL)
		uinfo->value.enumerated.items = ARRAY_SIZE(out_names_mfld);
	else if (kcontrol->id.numid == INPUT_SEL)
		uinfo->value.enumerated.items = ARRAY_SIZE(in_names_mfld);
	else if (kcontrol->id.numid == LINEOUT_SEL_MFLD) {
		uinfo->value.enumerated.items = ARRAY_SIZE(line_out_names_mfld);
		scard_ops->line_out_names_cnt = uinfo->value.enumerated.items;
	} else
		return -EINVAL;
	uinfo->count = MONO_CNTL;
	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;

	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
		uinfo->value.enumerated.item = 1;
	if (kcontrol->id.numid == OUTPUT_SEL)
		strncpy(uinfo->value.enumerated.name,
			out_names_mfld[uinfo->value.enumerated.item],
			sizeof(uinfo->value.enumerated.name)-1);
	else if (kcontrol->id.numid == INPUT_SEL)
		strncpy(uinfo->value.enumerated.name,
			in_names_mfld[uinfo->value.enumerated.item],
			sizeof(uinfo->value.enumerated.name)-1);
	else if (kcontrol->id.numid == LINEOUT_SEL_MFLD)
		strncpy(uinfo->value.enumerated.name,
			line_out_names_mfld[uinfo->value.enumerated.item],
			sizeof(uinfo->value.enumerated.name)-1);
	else
		return -EINVAL;
	return 0;
}

/**
* snd_intelmad_volume_get - gets the current volume for the control
*
* @kcontrol:	pointer to the control
* @uval:	pointer to the structure where the control's info need
*		to be filled
*
* This function is called when .get function of a control is invoked from app
*/
static int snd_intelmad_volume_get(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *uval)
{
	int ret_val = 0, cntl_list[2] = {0,};
	int value = 0;
	struct snd_intelmad *intelmaddata;
	struct snd_pmic_ops *scard_ops;

	pr_debug("snd_intelmad_volume_get called\n");

	WARN_ON(!uval);
	WARN_ON(!kcontrol);

	intelmaddata = kcontrol->private_data;

	WARN_ON(!intelmaddata->sstdrv_ops);

	scard_ops = intelmaddata->sstdrv_ops->scard_ops;

	WARN_ON(!scard_ops);

	switch (kcontrol->id.numid) {
	case PLAYBACK_VOL:
		cntl_list[0] = PMIC_SND_RIGHT_PB_VOL;
		cntl_list[1] = PMIC_SND_LEFT_PB_VOL;
		break;

	case CAPTURE_VOL:
		cntl_list[0] = PMIC_SND_CAPTURE_VOL;
		break;

	case MASTER_VOL:
		cntl_list[0] = PMIC_SND_RIGHT_MASTER_VOL;
		cntl_list[1] = PMIC_SND_LEFT_MASTER_VOL;
		break;
	default:
		return -EINVAL;
	}

	ret_val = scard_ops->get_vol(cntl_list[0], &value);
	uval->value.integer.value[0] = value;

	if (ret_val)
		return ret_val;

	if (kcontrol->id.numid == PLAYBACK_VOL ||
		kcontrol->id.numid == MASTER_VOL) {
		ret_val = scard_ops->get_vol(cntl_list[1], &value);
		uval->value.integer.value[1] = value;
	}
	return ret_val;
}

/**
* snd_intelmad_mute_get - gets the current mute status for the control
*
* @kcontrol:	pointer to the control
* @uval:	pointer to the structure where the control's info need
*		to be filled
*
* This function is called when .get function of a control is invoked from app
*/
static int snd_intelmad_mute_get(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *uval)
{

	int cntl_list = 0, ret_val = 0;
	u8 value = 0;
	struct snd_intelmad *intelmaddata;
	struct snd_pmic_ops *scard_ops;

	pr_debug("Mute_get called\n");

	WARN_ON(!uval);
	WARN_ON(!kcontrol);

	intelmaddata = kcontrol->private_data;

	WARN_ON(!intelmaddata->sstdrv_ops);

	scard_ops = intelmaddata->sstdrv_ops->scard_ops;

	WARN_ON(!scard_ops);

	switch (kcontrol->id.numid) {
	case PLAYBACK_MUTE:
		if (intelmaddata->output_sel == STEREO_HEADPHONE)
			cntl_list = PMIC_SND_LEFT_HP_MUTE;
		else if ((intelmaddata->output_sel == INTERNAL_SPKR) ||
			(intelmaddata->output_sel == MONO_EARPIECE))
			cntl_list = PMIC_SND_LEFT_SPEAKER_MUTE;
		break;

	case CAPTURE_MUTE:
		if (intelmaddata->input_sel == DMIC)
			cntl_list = PMIC_SND_DMIC_MUTE;
		else if (intelmaddata->input_sel == AMIC)
			cntl_list = PMIC_SND_AMIC_MUTE;
		else if (intelmaddata->input_sel == HS_MIC)
			cntl_list = PMIC_SND_HP_MIC_MUTE;
		break;
	case MASTER_MUTE:
		uval->value.integer.value[0] = intelmaddata->master_mute;
		return 0;
	default:
		return -EINVAL;
	}

	ret_val = scard_ops->get_mute(cntl_list, &value);
	uval->value.integer.value[0] = value;
	return ret_val;
}

/**
* snd_intelmad_volume_set - sets the volume control's info
*
* @kcontrol:	pointer to the control
* @uval:	pointer to the structure where the control's info is
*		available to be set
*
* This function is called when .set function of a control is invoked from app
*/
static int snd_intelmad_volume_set(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *uval)
{

	int ret_val, cntl_list[2] = {0,};
	struct snd_intelmad *intelmaddata;
	struct snd_pmic_ops *scard_ops;

	pr_debug("volume set called:%ld %ld\n",
			uval->value.integer.value[0],
			uval->value.integer.value[1]);

	WARN_ON(!uval);
	WARN_ON(!kcontrol);

	intelmaddata = kcontrol->private_data;

	WARN_ON(!intelmaddata->sstdrv_ops);

	scard_ops = intelmaddata->sstdrv_ops->scard_ops;

	WARN_ON(!scard_ops);

	switch (kcontrol->id.numid) {
	case PLAYBACK_VOL:
		cntl_list[0] = PMIC_SND_LEFT_PB_VOL;
		cntl_list[1] = PMIC_SND_RIGHT_PB_VOL;
		break;

	case CAPTURE_VOL:
		cntl_list[0] = PMIC_SND_CAPTURE_VOL;
		break;

	case MASTER_VOL:
		cntl_list[0] = PMIC_SND_LEFT_MASTER_VOL;
		cntl_list[1] = PMIC_SND_RIGHT_MASTER_VOL;
		break;

	default:
		return -EINVAL;
	}

	ret_val = scard_ops->set_vol(cntl_list[0],
				uval->value.integer.value[0]);
	if (ret_val)
		return ret_val;

	if (kcontrol->id.numid == PLAYBACK_VOL ||
		kcontrol->id.numid == MASTER_VOL)
		ret_val = scard_ops->set_vol(cntl_list[1],
				uval->value.integer.value[1]);
	return ret_val;
}

/**
* snd_intelmad_mute_set - sets the mute control's info
*
* @kcontrol:	pointer to the control
* @uval:	pointer to the structure where the control's info is
*		available to be set
*
* This function is called when .set function of a control is invoked from app
*/
static int snd_intelmad_mute_set(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *uval)
{
	int cntl_list[2] = {0,}, ret_val;
	struct snd_intelmad *intelmaddata;
	struct snd_pmic_ops *scard_ops;

	pr_debug("snd_intelmad_mute_set called\n");

	WARN_ON(!uval);
	WARN_ON(!kcontrol);

	intelmaddata = kcontrol->private_data;

	WARN_ON(!intelmaddata->sstdrv_ops);

	scard_ops = intelmaddata->sstdrv_ops->scard_ops;

	WARN_ON(!scard_ops);

	kcontrol->private_value = uval->value.integer.value[0];

	switch (kcontrol->id.numid) {
	case PLAYBACK_MUTE:
		if (intelmaddata->output_sel == STEREO_HEADPHONE) {
			cntl_list[0] = PMIC_SND_LEFT_HP_MUTE;
			cntl_list[1] = PMIC_SND_RIGHT_HP_MUTE;
		} else if ((intelmaddata->output_sel == INTERNAL_SPKR) ||
				(intelmaddata->output_sel == MONO_EARPIECE)) {
			cntl_list[0] = PMIC_SND_LEFT_SPEAKER_MUTE;
			cntl_list[1] = PMIC_SND_RIGHT_SPEAKER_MUTE;
		}
		break;

	case CAPTURE_MUTE:/*based on sel device mute the i/p dev*/
		if (intelmaddata->input_sel == DMIC)
			cntl_list[0] = PMIC_SND_DMIC_MUTE;
		else if (intelmaddata->input_sel == AMIC)
			cntl_list[0] = PMIC_SND_AMIC_MUTE;
		else if (intelmaddata->input_sel == HS_MIC)
			cntl_list[0] = PMIC_SND_HP_MIC_MUTE;
		break;
	case MASTER_MUTE:
		cntl_list[0] = PMIC_SND_MUTE_ALL;
		intelmaddata->master_mute = uval->value.integer.value[0];
		break;
	default:
		return -EINVAL;
	}

	ret_val = scard_ops->set_mute(cntl_list[0],
				uval->value.integer.value[0]);
	if (ret_val)
		return ret_val;

	if (kcontrol->id.numid == PLAYBACK_MUTE)
		ret_val = scard_ops->set_mute(cntl_list[1],
					uval->value.integer.value[0]);
	return ret_val;
}

/**
* snd_intelmad_device_get - get the device select control's info
*
* @kcontrol:	pointer to the control
* @uval:	pointer to the structure where the control's info is
*		to be filled
*
* This function is called when .get function of a control is invoked from app
*/
static int snd_intelmad_device_get(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *uval)
{
	struct snd_intelmad *intelmaddata;
	struct snd_pmic_ops *scard_ops;
	pr_debug("device_get called\n");

	WARN_ON(!uval);
	WARN_ON(!kcontrol);

	intelmaddata = kcontrol->private_data;
	scard_ops = intelmaddata->sstdrv_ops->scard_ops;
	if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) {
		if (kcontrol->id.numid == OUTPUT_SEL)
			uval->value.enumerated.item[0] =
					scard_ops->output_dev_id;
		else if (kcontrol->id.numid == INPUT_SEL)
			uval->value.enumerated.item[0] =
					scard_ops->input_dev_id;
		else if (kcontrol->id.numid == LINEOUT_SEL_MFLD)
			uval->value.enumerated.item[0] =
					scard_ops->lineout_dev_id;
		else
			return -EINVAL;
	} else if (intelmaddata->cpu_id == CPU_CHIP_LINCROFT) {
		if (kcontrol->id.numid == OUTPUT_SEL)
			/* There is a mismatch here.
			 * ALSA expects 1 for internal speaker.
			 * But internally, we may give 2 for internal speaker.
			 */
			if (scard_ops->output_dev_id == MONO_EARPIECE ||
			    scard_ops->output_dev_id == INTERNAL_SPKR)
				uval->value.enumerated.item[0] = MONO_EARPIECE;
			else if (scard_ops->output_dev_id == STEREO_HEADPHONE)
				uval->value.enumerated.item[0] =
					STEREO_HEADPHONE;
			else
				return -EINVAL;
		else if (kcontrol->id.numid == INPUT_SEL)
			uval->value.enumerated.item[0] =
					scard_ops->input_dev_id;
		else
			return -EINVAL;
	} else
	uval->value.enumerated.item[0] = kcontrol->private_value;
	return 0;
}

/**
* snd_intelmad_device_set - set the device select control's info
*
* @kcontrol:	pointer to the control
* @uval:	pointer to the structure where the control's info is
*		available to be set
*
* This function is called when .set function of a control is invoked from app
*/
static int snd_intelmad_device_set(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *uval)
{
	struct snd_intelmad *intelmaddata;
	struct snd_pmic_ops *scard_ops;
	int ret_val = 0, vendor, status;
	struct intel_sst_pcm_control *pcm_control;

	pr_debug("snd_intelmad_device_set called\n");

	WARN_ON(!uval);
	WARN_ON(!kcontrol);
	status = -1;

	intelmaddata = kcontrol->private_data;

	WARN_ON(!intelmaddata->sstdrv_ops);

	scard_ops = intelmaddata->sstdrv_ops->scard_ops;

	WARN_ON(!scard_ops);

	/* store value with driver */
	kcontrol->private_value = uval->value.enumerated.item[0];

	switch (kcontrol->id.numid) {
	case OUTPUT_SEL:
		ret_val = scard_ops->set_output_dev(
				uval->value.enumerated.item[0]);
		intelmaddata->output_sel = uval->value.enumerated.item[0];
		break;
	case INPUT_SEL:
		vendor = intelmaddata->sstdrv_ops->vendor_id;
		if ((vendor == SND_MX) || (vendor == SND_FS)) {
			pcm_control = intelmaddata->sstdrv_ops->pcm_control;
			if (uval->value.enumerated.item[0] == HS_MIC)
				status = 1;
			else
				status = 0;
			pcm_control->device_control(
					SST_ENABLE_RX_TIME_SLOT, &status);
		}
		ret_val = scard_ops->set_input_dev(
				uval->value.enumerated.item[0]);
		intelmaddata->input_sel = uval->value.enumerated.item[0];
		break;
	case LINEOUT_SEL_MFLD:
		ret_val = scard_ops->set_lineout_dev(
					uval->value.enumerated.item[0]);
		intelmaddata->lineout_sel = uval->value.enumerated.item[0];
		break;
	default:
		return -EINVAL;
	}
	kcontrol->private_value = uval->value.enumerated.item[0];
	return ret_val;
}

static int snd_intelmad_device_dmic_get(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *uval)
{
	struct snd_intelmad *intelmaddata;
	struct snd_pmic_ops *scard_ops;

	WARN_ON(!uval);
	WARN_ON(!kcontrol);

	intelmaddata = kcontrol->private_data;
	scard_ops = intelmaddata->sstdrv_ops->scard_ops;

	if (scard_ops->input_dev_id != DMIC) {
		pr_debug("input dev = 0x%x\n", scard_ops->input_dev_id);
		return 0;
	}

	if (intelmaddata->cpu_id == CPU_CHIP_PENWELL)
		uval->value.enumerated.item[0] = kcontrol->private_value;
	else
		pr_debug(" CPU id = 0x%xis invalid.\n",
			intelmaddata->cpu_id);
	return 0;
}

void msic_set_bit(u8 index, unsigned int *available_dmics)
{
	*available_dmics |= (1 << index);
}

void msic_clear_bit(u8 index, unsigned int *available_dmics)
{
	*available_dmics &= ~(1 << index);
}

int msic_is_set_bit(u8 index, unsigned int *available_dmics)
{
	int ret_val;

	ret_val = (*available_dmics & (1 << index));
	return ret_val;
}

static int snd_intelmad_device_dmic_set(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *uval)
{
	struct snd_intelmad *intelmaddata;
	struct snd_pmic_ops *scard_ops;
	int i, dmic_index;
	unsigned int available_dmics;
	int jump_count;
	int max_dmics = ARRAY_SIZE(router_dmics);

	WARN_ON(!uval);
	WARN_ON(!kcontrol);

	intelmaddata = kcontrol->private_data;
	WARN_ON(!intelmaddata->sstdrv_ops);

	scard_ops = intelmaddata->sstdrv_ops->scard_ops;
	WARN_ON(!scard_ops);

	if (scard_ops->input_dev_id != DMIC) {
		pr_debug("input dev = 0x%x\n", scard_ops->input_dev_id);
		return 0;
	}

	available_dmics = scard_ops->available_dmics;

	if (kcontrol->private_value > uval->value.enumerated.item[0]) {
		pr_debug("jump count -1.\n");
		jump_count = -1;
	} else {
		pr_debug("jump count 1.\n");
		jump_count = 1;
	}

	dmic_index =  uval->value.enumerated.item[0];
	pr_debug("set function. dmic_index = %d, avl_dmic = 0x%x\n",
			 dmic_index, available_dmics);
	for (i = 0; i < max_dmics; i++) {
		pr_debug("set function. loop index = 0x%x.  dmic_index = 0x%x\n",
			 i, dmic_index);
		if (!msic_is_set_bit(dmic_index, &available_dmics)) {
			msic_clear_bit(kcontrol->private_value,
						&available_dmics);
			msic_set_bit(dmic_index, &available_dmics);
			kcontrol->private_value = dmic_index;
			scard_ops->available_dmics = available_dmics;
			scard_ops->hw_dmic_map[kcontrol->id.numid-HW_CH_BASE] =
				kcontrol->private_value;
			scard_ops->set_hw_dmic_route
				(kcontrol->id.numid-HW_CH_BASE);
			return 0;
		}

		dmic_index += jump_count;

		if (dmic_index > (max_dmics - 1) && jump_count == 1) {
			pr_debug("Resettingthe dmic index to 0.\n");
			dmic_index = 0;
		} else if (dmic_index == -1 && jump_count == -1) {
			pr_debug("Resetting the dmic index to 5.\n");
			dmic_index = max_dmics - 1;
		}
	}

	return -EINVAL;
}

static int snd_intelmad_device_dmic_info_mfld(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_info *uinfo)
{
	struct snd_intelmad *intelmaddata;
	struct snd_pmic_ops *scard_ops;

	uinfo->count                  = MONO_CNTL;
	uinfo->type                   = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
	uinfo->value.enumerated.items = ARRAY_SIZE(router_dmics);

	intelmaddata = kcontrol->private_data;
	WARN_ON(!intelmaddata->sstdrv_ops);

	scard_ops = intelmaddata->sstdrv_ops->scard_ops;
	WARN_ON(!scard_ops);

	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
		uinfo->value.enumerated.item =
			uinfo->value.enumerated.items - 1;

	strncpy(uinfo->value.enumerated.name,
	router_dmics[uinfo->value.enumerated.item],
	sizeof(uinfo->value.enumerated.name)-1);


	msic_set_bit(kcontrol->private_value, &scard_ops->available_dmics);
	pr_debug("info function. avl_dmic = 0x%x",
		scard_ops->available_dmics);

	scard_ops->hw_dmic_map[kcontrol->id.numid-HW_CH_BASE] =
		kcontrol->private_value;

	return 0;
}

struct snd_kcontrol_new snd_intelmad_controls_mrst[MAX_CTRL] __devinitdata = {
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	"PCM Playback Source",
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_device_info_mrst,
	.get		=	snd_intelmad_device_get,
	.put		=	snd_intelmad_device_set,
	.private_value	=	0,
},
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	"PCM Capture Source",
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_device_info_mrst,
	.get		=	snd_intelmad_device_get,
	.put		=	snd_intelmad_device_set,
	.private_value	=	0,
},
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	"PCM Playback Volume",
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_playback_volume_info,
	.get		=	snd_intelmad_volume_get,
	.put		=	snd_intelmad_volume_set,
	.private_value	=	0,
},
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	"PCM Playback Switch",
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_mute_info,
	.get		=	snd_intelmad_mute_get,
	.put		=	snd_intelmad_mute_set,
	.private_value	=	0,
},
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	"PCM Capture Volume",
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_capture_volume_info,
	.get		=	snd_intelmad_volume_get,
	.put		=	snd_intelmad_volume_set,
	.private_value	=	0,
},
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	"PCM Capture Switch",
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_mute_info,
	.get		=	snd_intelmad_mute_get,
	.put		=	snd_intelmad_mute_set,
	.private_value	=	0,
},
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	"Master Playback Volume",
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_master_volume_info,
	.get		=	snd_intelmad_volume_get,
	.put		=	snd_intelmad_volume_set,
	.private_value	=	0,
},
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	"Master Playback Switch",
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_mute_info,
	.get		=	snd_intelmad_mute_get,
	.put		=	snd_intelmad_mute_set,
	.private_value	=	0,
},
};

struct snd_kcontrol_new
snd_intelmad_controls_mfld[MAX_CTRL_MFLD] __devinitdata = {
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	"PCM Playback Source",
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_device_info_mfld,
	.get		=	snd_intelmad_device_get,
	.put		=	snd_intelmad_device_set,
	.private_value	=	0,
},
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	"PCM Capture Source",
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_device_info_mfld,
	.get		=	snd_intelmad_device_get,
	.put		=	snd_intelmad_device_set,
	.private_value	=	0,
},
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	"Line out",
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_device_info_mfld,
	.get		=	snd_intelmad_device_get,
	.put		=	snd_intelmad_device_set,
	.private_value	=	0,
},
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	HW_CH_0,
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_device_dmic_info_mfld,
	.get		=	snd_intelmad_device_dmic_get,
	.put		=	snd_intelmad_device_dmic_set,
	.private_value	=	0
},
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	HW_CH_1,
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_device_dmic_info_mfld,
	.get		=	snd_intelmad_device_dmic_get,
	.put		=	snd_intelmad_device_dmic_set,
	.private_value	=	1
},
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	HW_CH_2,
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_device_dmic_info_mfld,
	.get		=	snd_intelmad_device_dmic_get,
	.put		=	snd_intelmad_device_dmic_set,
	.private_value	=	2
},
{
	.iface		=	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name		=	HW_CH_3,
	.access		=	SNDRV_CTL_ELEM_ACCESS_READWRITE,
	.info		=	snd_intelmad_device_dmic_info_mfld,
	.get		=	snd_intelmad_device_dmic_get,
	.put		=	snd_intelmad_device_dmic_set,
	.private_value	=	3
}
};