diff options
Diffstat (limited to 'drivers/staging/intel_sst/intelmid_ctrl.c')
-rw-r--r-- | drivers/staging/intel_sst/intelmid_ctrl.c | 629 |
1 files changed, 629 insertions, 0 deletions
diff --git a/drivers/staging/intel_sst/intelmid_ctrl.c b/drivers/staging/intel_sst/intelmid_ctrl.c new file mode 100644 index 0000000..03b4ece --- /dev/null +++ b/drivers/staging/intel_sst/intelmid_ctrl.c @@ -0,0 +1,629 @@ +/* + * 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 + */ +#include <sound/core.h> +#include <sound/control.h> +#include "jack.h" +#include "intel_sst.h" +#include "intel_sst_ioctl.h" +#include "intelmid_snd_control.h" +#include "intelmid.h" + +static char *out_names_mrst[] = {"Headphones", + "Internal speakers"}; +static char *in_names_mrst[] = {"AMIC", + "DMIC", + "HS_MIC"}; +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 = -126, + .capture_vol_max = 0, + .capture_vol_min = -31, + }, +}; + +/* 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; +} + +/** +* 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) +{ + 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_mfld); + else + uinfo->value.enumerated.items = ARRAY_SIZE(in_names_mfld); + 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 + strncpy(uinfo->value.enumerated.name, + in_names_mfld[uinfo->value.enumerated.item], + sizeof(uinfo->value.enumerated.name)-1); + 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("sst: 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; + 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) { + 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("sst: 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("sst: 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; + 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) + 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("sst: 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("sst: device_get called\n"); + + WARN_ON(!uval); + WARN_ON(!kcontrol); + + intelmaddata = kcontrol->private_data; + if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) { + scard_ops = intelmaddata->sstdrv_ops->scard_ops; + 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 + 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; + + pr_debug("sst: 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)) { + if (uval->value.enumerated.item[0] == HS_MIC) { + status = 1; + intelmaddata->sstdrv_ops-> + control_set(SST_ENABLE_RX_TIME_SLOT, &status); + } else { + status = 0; + intelmaddata->sstdrv_ops-> + control_set(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; + default: + return -EINVAL; + } + kcontrol->private_value = uval->value.enumerated.item[0]; + return ret_val; +} + +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 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, +}, +}; + |