From 5485b9ef3232681dd20e4a01fff608b35e7fd802 Mon Sep 17 00:00:00 2001 From: "KP, Jeeja" Date: Thu, 22 Mar 2012 21:47:57 +0530 Subject: [PATCH] audio: Exposed LPE Mixer controls for the HAL to select the Mixer required based on the sink device BZ: 26104 Added support for device pipeline configuration by exposing LPE Mixer elements as ALSA mixer controls. To add support in SOc for add platform controls, following alsa patchs are applied 1. 0001-ASoC-core-Add-API-call-to-register-platform-kcontrol 2. 0002-ASoC-core-Make-platform-probe-more-like-codec-probe Change-Id: I01b829294f82753496f06a5df05afec4c48384b7 Signed-off-by: KP, Jeeja Reviewed-on: http://android.intel.com:8080/40136 Reviewed-by: Koul, Vinod Tested-by: M, Arulselvan Reviewed-by: buildbot Tested-by: buildbot --- include/sound/intel_sst.h | 8 +- include/sound/intel_sst_ioctl.h | 13 +++ include/sound/soc.h | 7 +- sound/pci/sst/intel_sst.c | 5 ++ sound/pci/sst/intel_sst_common.h | 5 +- sound/pci/sst/intel_sst_drv_interface.c | 40 +++++++++ sound/pci/sst/intel_sst_fw_ipc.h | 6 ++ sound/pci/sst/intel_sst_ipc.c | 33 +++++++ sound/soc/mid-x86/sst_platform.c | 152 +++++++++++++++++++++++++++++++- sound/soc/soc-core.c | 85 ++++++++++++++---- 10 files changed, 333 insertions(+), 21 deletions(-) diff --git a/include/sound/intel_sst.h b/include/sound/intel_sst.h index c096d6c..3c48120 100644 --- a/include/sound/intel_sst.h +++ b/include/sound/intel_sst.h @@ -66,7 +66,8 @@ enum sst_controls { SST_SND_DEVICE_RESUME = 0x1012, SST_SND_DEVICE_RESUME_SYNC = 0x1013, SST_SET_RUNTIME_PARAMS = 0x1014, - SST_MAX_CONTROLS = 0x1014, + SST_SET_ALGO_PARAMS = 0x1015, + SST_MAX_CONTROLS = 0x1015, }; enum SND_CARDS { @@ -148,6 +149,11 @@ enum intel_sst_pll_mode { SST_PLL_MSIC = 0x20, }; +enum lpe_param_types_mixer { + SST_ALGO_PARAM_MIXER_STREAM_CFG = 0x801, +}; + + int register_sst_card(struct intel_sst_card_ops *card); void unregister_sst_card(struct intel_sst_card_ops *card); int intel_sst_set_pll(unsigned int enable, enum intel_sst_pll_mode mode); diff --git a/include/sound/intel_sst_ioctl.h b/include/sound/intel_sst_ioctl.h index eaea339..a63c2c4 100644 --- a/include/sound/intel_sst_ioctl.h +++ b/include/sound/intel_sst_ioctl.h @@ -93,6 +93,19 @@ enum snd_sst_audio_device_type { SND_SST_DEVICE_CAPTURE, }; +enum snd_sst_input_stream { + SST_INPUT_STREAM_PCM = 0x2, + SST_INPUT_STREAM_COMPRESS = 0x8, + SST_INPUT_STREAM_MIXED = 0xA, +}; + +enum snd_sst_stream_type { + SST_STREAM_DEVICE_HS = 32, + SST_STREAM_DEVICE_IHF = 33, + SST_STREAM_DEVICE_MIC0 = 34, + SST_STREAM_DEVICE_MIC1 = 35, +}; + /* Firmware Version info */ struct snd_sst_fw_version { __u8 build; /* build number*/ diff --git a/include/sound/soc.h b/include/sound/soc.h index d7c893c..0c6c621 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -200,7 +200,8 @@ struct soc_enum name = SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, \ ARRAY_SIZE(xtexts), xtexts, xvalues) #define SOC_VALUE_ENUM_SINGLE_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \ - SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues) + SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts,\ + xvalues) /* * Bias levels @@ -243,7 +244,7 @@ struct snd_soc_cache_ops; struct snd_soc_jack_gpio; #endif -typedef int (*hw_write_t)(void *,const char* ,int); +typedef int (*hw_write_t)(void *, const char*, int); extern struct snd_ac97_bus_ops soc_ac97_ops; @@ -350,6 +351,8 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, const char *prefix); int snd_soc_add_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, int num_controls); +int snd_soc_add_platform_controls(struct snd_soc_platform *platform, + const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/sst/intel_sst.c b/sound/pci/sst/intel_sst.c index 8d6a1f4..8d201ef 100644 --- a/sound/pci/sst/intel_sst.c +++ b/sound/pci/sst/intel_sst.c @@ -207,6 +207,7 @@ static int __devinit intel_sst_probe(struct pci_dev *pci, mutex_init(&sst_drv_ctx->stream_lock); mutex_init(&sst_drv_ctx->sst_lock); + mutex_init(&sst_drv_ctx->mixer_ctrl_lock); sst_drv_ctx->stream_cnt = 0; sst_drv_ctx->encoded_cnt = 0; @@ -276,6 +277,10 @@ static int __devinit intel_sst_probe(struct pci_dev *pci, sst_drv_ctx->mmap_len); } } + if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) { + sst_drv_ctx->device_input_mixer = SST_STREAM_DEVICE_IHF + | SST_INPUT_STREAM_PCM; + } /* Init the device */ ret = pci_enable_device(pci); diff --git a/sound/pci/sst/intel_sst_common.h b/sound/pci/sst/intel_sst_common.h index df4eef3..a7d05c5 100644 --- a/sound/pci/sst/intel_sst_common.h +++ b/sound/pci/sst/intel_sst_common.h @@ -470,6 +470,8 @@ struct intel_sst_drv { struct sst_dma dma; void *fw_in_mem; struct sst_runtime_param runtime_param; + unsigned int device_input_mixer; + struct mutex mixer_ctrl_lock; }; extern struct intel_sst_drv *sst_drv_ctx; @@ -558,7 +560,8 @@ void sst_clear_interrupt(void); int sst_download_fw(void); void free_stream_context(unsigned int str_id); void sst_clean_stream(struct stream_info *stream); -vibra_pwm_configure(unsigned int enable); +int vibra_pwm_configure(unsigned int enable); +int sst_send_algo_param(struct snd_ppp_params *algo_params); /* * sst_fill_header - inline to fill sst header diff --git a/sound/pci/sst/intel_sst_drv_interface.c b/sound/pci/sst/intel_sst_drv_interface.c index 4a6c06e..03a08be 100644 --- a/sound/pci/sst/intel_sst_drv_interface.c +++ b/sound/pci/sst/intel_sst_drv_interface.c @@ -162,6 +162,34 @@ void free_stream_context(unsigned int str_id) } } +void sst_send_lpe_mixer_algo_params() +{ + struct snd_ppp_params algo_param; + struct snd_ppp_mixer_params mixer_param; + unsigned int input_mixer, stream_device_id; + + mutex_lock(&sst_drv_ctx->mixer_ctrl_lock); + input_mixer = (sst_drv_ctx->device_input_mixer) + & SST_INPUT_STREAM_MIXED; + stream_device_id = sst_drv_ctx->device_input_mixer - input_mixer; + algo_param.algo_id = SST_CODEC_MIXER; + algo_param.str_id = stream_device_id; + algo_param.enable = 1; + algo_param.reserved = 0; + algo_param.size = sizeof(algo_param); + mixer_param.type = SST_ALGO_PARAM_MIXER_STREAM_CFG; + mixer_param.input_stream_bitmap = input_mixer; + mixer_param.size = sizeof(input_mixer); + algo_param.params = &mixer_param; + mutex_unlock(&sst_drv_ctx->mixer_ctrl_lock); + pr_err("setting pp param\n"); + pr_debug("Algo ID %d Str id %d Enable %d Size %d\n", + algo_param.algo_id, algo_param.str_id, + algo_param.enable, algo_param.size); + sst_send_algo_param(&algo_param); +} + + /* * sst_get_stream_allocated - this function gets a stream allocated with * the given params @@ -178,6 +206,10 @@ int sst_get_stream_allocated(struct snd_sst_params *str_param, int retval, str_id; struct stream_info *str_info; + if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) { + pr_debug("Sending LPE mixer algo Params\n"); + sst_send_lpe_mixer_algo_params(); + } retval = sst_alloc_stream((char *) &str_param->sparams, str_param->ops, str_param->codec, str_param->device_type); if (retval < 0) { @@ -628,6 +660,14 @@ static int sst_set_generic_params(enum sst_controls cmd, void *arg) ret_val = sst_copy_runtime_param(dst, src); break; } + case SST_SET_ALGO_PARAMS: { + unsigned int device_input_mixer = *((unsigned int *)arg); + pr_debug("LPE mixer algo param set %x\n", device_input_mixer); + mutex_lock(&sst_drv_ctx->mixer_ctrl_lock); + sst_drv_ctx->device_input_mixer = device_input_mixer; + mutex_unlock(&sst_drv_ctx->mixer_ctrl_lock); + break; + } default: pr_err("Invalid cmd request:%d\n", cmd); ret_val = -EINVAL; diff --git a/sound/pci/sst/intel_sst_fw_ipc.h b/sound/pci/sst/intel_sst_fw_ipc.h index 97d2330..b91be93 100644 --- a/sound/pci/sst/intel_sst_fw_ipc.h +++ b/sound/pci/sst/intel_sst_fw_ipc.h @@ -362,6 +362,12 @@ struct lib_slot_info { u32 dram_offset; /* starting offset of slot in DRAM */ } __attribute__ ((packed)); +struct snd_ppp_mixer_params { + __u32 type; /*Type of the parameter */ + __u32 size; + __u32 input_stream_bitmap; /*Input stream Bit Map*/ +} __attribute__ ((packed)); + struct snd_sst_lib_download { struct module_info lib_info; /* library info type, capabilities etc */ struct lib_slot_info slot_info; /* slot info to be downloaded */ diff --git a/sound/pci/sst/intel_sst_ipc.c b/sound/pci/sst/intel_sst_ipc.c index 893e74b..4ddad21 100644 --- a/sound/pci/sst/intel_sst_ipc.c +++ b/sound/pci/sst/intel_sst_ipc.c @@ -97,6 +97,39 @@ static int sst_send_runtime_param(struct snd_sst_runtime_params *params) params->addr, params->size); return sst_send_ipc_msg_nowait(&msg); } + +/* + * sst_send_algo_param - send LPE Mixer param to SST + * + * this function sends the algo parameter to sst dsp engine + */ +int sst_send_algo_param(struct snd_ppp_params *algo_params) +{ + u32 header_size = 0; + struct ipc_post *msg = NULL; + u32 ipc_msg_size = sizeof(u32) + sizeof(*algo_params) + - sizeof(algo_params->params) + algo_params->size; + u32 offset = 0; + + if (ipc_msg_size > SST_MAILBOX_SIZE) + return -ENOMEM; + if (sst_create_large_msg(&msg)) + return -ENOMEM; + sst_fill_header(&msg->header, + IPC_IA_ALG_PARAMS, 1, algo_params->str_id); + msg->header.part.data = sizeof(u32) + sizeof(*algo_params) + - sizeof(algo_params->params) + algo_params->size; + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + offset = sizeof(u32); + header_size = sizeof(*algo_params) - sizeof(algo_params->params); + memcpy(msg->mailbox_data + sizeof(u32), algo_params, + sizeof(*algo_params) - sizeof(algo_params->params)); + offset += header_size; + memcpy(msg->mailbox_data + offset , algo_params->params, + algo_params->size); + return sst_send_ipc_msg_nowait(&msg); +} + /** * sst_post_message - Posts message to SST * diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c index 49d1e96..ec908d8 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/mid-x86/sst_platform.c @@ -66,6 +66,33 @@ static struct snd_pcm_hardware sst_platform_pcm_hw = { .periods_max = SST_MAX_PERIODS, .fifo_size = SST_FIFO_SIZE, }; +#ifdef CONFIG_SND_CLV_MACHINE +static unsigned int lpe_mixer_input_ihf; +static unsigned int lpe_mixer_input_hs; + +int sst_set_mixer_param(unsigned int device_input_mixer) +{ + struct intel_sst_card_ops *sstdrv_ops; + int ret_val; + + /* allocate memory for SST API set */ + sstdrv_ops = kzalloc(sizeof(*sstdrv_ops), GFP_KERNEL); + if (!sstdrv_ops) + return -ENOMEM; + /* registering with SST driver to get access to SST APIs to use */ + ret_val = register_sst_card(sstdrv_ops); + if (ret_val) { + pr_err("sst: sst card registration failed\n"); + return -EIO; + } + + /*allocate memory for params*/ + ret_val = sstdrv_ops->pcm_control->set_generic_params( + SST_SET_ALGO_PARAMS, (void *)&device_input_mixer); + kfree(sstdrv_ops); + return ret_val; +} +#endif static int sst_platform_ihf_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, @@ -94,11 +121,99 @@ static int sst_platform_ihf_set_tdm_slot(struct snd_soc_dai *dai, kfree(sstdrv_ops); return ret_val; } - static const struct snd_soc_dai_ops sst_ihf_ops = { .set_tdm_slot = sst_platform_ihf_set_tdm_slot, }; +#ifdef CONFIG_SND_CLV_MACHINE +static int lpe_mixer_ihf_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = lpe_mixer_input_ihf; + return 0; +} + +static int lpe_mixer_ihf_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int device_input_mixer; + + switch (ucontrol->value.integer.value[0]) { + case 0: + pr_debug("input is PCM stream\n"); + device_input_mixer = SST_STREAM_DEVICE_IHF + | SST_INPUT_STREAM_PCM; + break; + case 1: + pr_debug("input is Comptress stream\n"); + device_input_mixer = SST_STREAM_DEVICE_IHF + | SST_INPUT_STREAM_COMPRESS; + break; + case 2: + pr_debug("input is mixed stream\n"); + device_input_mixer = SST_STREAM_DEVICE_IHF + | SST_INPUT_STREAM_MIXED; + break; + default: + pr_err("Invalid Input%d\n", ucontrol->value.integer.value[0]); + return -EINVAL; + } + lpe_mixer_input_ihf = ucontrol->value.integer.value[0]; + sst_set_mixer_param(device_input_mixer); + return 0; +} + +static int lpe_mixer_headset_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = lpe_mixer_input_hs; + return 0; +} + +static int lpe_mixer_headset_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mixer_input_stream; + + switch (ucontrol->value.integer.value[0]) { + case 0: + pr_debug("input is PCM stream\n"); + mixer_input_stream = SST_STREAM_DEVICE_HS + | SST_INPUT_STREAM_PCM; + break; + case 1: + pr_debug("input is Comptress stream\n"); + mixer_input_stream = SST_STREAM_DEVICE_HS + | SST_INPUT_STREAM_COMPRESS; + break; + case 2: + pr_debug("input is PCM stream\n"); + mixer_input_stream = SST_STREAM_DEVICE_HS + | SST_INPUT_STREAM_MIXED; + break; + default: + pr_err("Invalid Input%d\n", ucontrol->value.integer.value[0]); + return -EINVAL; + } + lpe_mixer_input_hs = ucontrol->value.integer.value[0]; + sst_set_mixer_param(mixer_input_stream); + return 0; +} +static const char *lpe_mixer_text[] = { + "PCM", "Compressed", "PCM and Compressed"}; + +static const struct soc_enum lpe_mixer_enum = + SOC_ENUM_SINGLE_EXT(3, lpe_mixer_text); + + +static const struct snd_kcontrol_new sst_controls[] = { + SOC_ENUM_EXT("LPE IHF mixer", lpe_mixer_enum, + lpe_mixer_ihf_get, lpe_mixer_ihf_set), + SOC_ENUM_EXT("LPE headset mixer", lpe_mixer_enum, + lpe_mixer_headset_get, lpe_mixer_headset_set), +}; +#endif + /* MFLD - MSIC */ static struct snd_soc_dai_driver sst_platform_dai[] = { { @@ -598,7 +713,42 @@ static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) } return retval; } +#ifdef CONFIG_SND_CLV_MACHINE +static int sst_soc_probe(struct snd_soc_platform *platform) +{ + pr_debug("%s called\n", __func__); + + lpe_mixer_input_ihf = 0; + lpe_mixer_input_hs = 0; + pr_debug("platform is %p\n", platform); + if (!platform) { + pr_err("platform ptr invalid"); + return -EINVAL; + } + if (!platform->card) { + pr_err("platform card ptr invalid"); + return -EINVAL; + } + if (!platform->card->snd_card) { + pr_err("platform card snd_card ptr invalid"); + return -EINVAL; + } + + return snd_soc_add_platform_controls(platform, sst_controls, + ARRAY_SIZE(sst_controls)); +} + +static int sst_soc_remove(struct snd_soc_platform *platform) +{ + pr_debug("%s called\n", __func__); + return 0; +} +#endif static struct snd_soc_platform_driver sst_soc_platform_drv = { +#ifdef CONFIG_SND_CLV_MACHINE + .probe = &sst_soc_probe, + .remove = &sst_soc_remove, +#endif .ops = &sst_platform_ops, .pcm_new = sst_pcm_new, .pcm_free = sst_pcm_free, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 3736248..25fb4bd 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -458,7 +458,7 @@ static int soc_ac97_dev_unregister(struct snd_soc_codec *codec) } /* stop no dev release warning */ -static void soc_ac97_device_release(struct device *dev){} +static void soc_ac97_device_release(struct device *dev) {} /* register ac97 codec to bus */ static int soc_ac97_dev_register(struct snd_soc_codec *codec) @@ -1531,6 +1531,39 @@ err_probe: return ret; } +static int soc_probe_platform(struct snd_soc_card *card, + struct snd_soc_platform *platform) +{ + int ret = 0; + const struct snd_soc_platform_driver *driver = platform->driver; + + platform->card = card; + + if (!try_module_get(platform->dev->driver->owner)) + return -ENODEV; + + if (driver->probe) { + ret = driver->probe(platform); + if (ret < 0) { + dev_err(platform->dev, + "asoc: failed to probe platform %s: %d\n", + platform->name, ret); + goto err_probe; + } + } + + /* mark platform as probed and add to card platform list */ + platform->probed = 1; + list_add(&platform->card_list, &card->platform_dev_list); + + return 0; + +err_probe: + module_put(platform->dev->driver->owner); + + return ret; +} + static void rtd_release(struct device *dev) {} static int soc_post_component_init(struct snd_soc_card *card, @@ -1649,21 +1682,10 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num) /* probe the platform */ if (!platform->probed) { - if (!try_module_get(platform->dev->driver->owner)) - return -ENODEV; + ret = soc_probe_platform(card, platform); + if (ret < 0) + return ret; - if (platform->driver->probe) { - ret = platform->driver->probe(platform); - if (ret < 0) { - printk(KERN_ERR "asoc: failed to probe platform %s\n", - platform->name); - module_put(platform->dev->driver->owner); - return ret; - } - } - /* mark platform as probed and add to card platform list */ - platform->probed = 1; - list_add(&platform->card_list, &card->platform_dev_list); } /* probe the CODEC DAI */ @@ -2121,7 +2143,7 @@ static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) if (codec_dai->driver->capture.channels_min) capture = 1; - dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num,new_name); + dev_dbg(rtd->card->dev, "registered pcm #%d %s\n", num, new_name); ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback, capture, &pcm); if (ret < 0) { @@ -2494,6 +2516,37 @@ int snd_soc_add_controls(struct snd_soc_codec *codec, EXPORT_SYMBOL_GPL(snd_soc_add_controls); /** + * snd_soc_add_platform_controls - add an array of controls to a platform. + * Convienience function to add a list of controls. + * + * @platform: platform to add controls to + * @controls: array of controls to add + * @num_controls: number of elements in the array + * + * Return 0 for success, else error. + */ +int snd_soc_add_platform_controls(struct snd_soc_platform *platform, + const struct snd_kcontrol_new *controls, int num_controls) +{ + struct snd_card *card = platform->card->snd_card; + int err, i; + + for (i = 0; i < num_controls; i++) { + const struct snd_kcontrol_new *control = &controls[i]; + err = snd_ctl_add(card, snd_soc_cnew(control, platform, + control->name, NULL)); + if (err < 0) { + dev_err(platform->dev, "Failed to add %s %d\n", + control->name, err); + return err; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls); + +/** * snd_soc_info_enum_double - enumerated double mixer info callback * @kcontrol: mixer control * @uinfo: control element information -- 2.7.4