| /* |
| * 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 "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("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("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; |
| 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("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; |
| 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; |
| 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; |
| 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, |
| }, |
| }; |
| |