123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- /* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- */
- #include <linux/gpio.h>
- #include <linux/of_gpio.h>
- #include <linux/platform_device.h>
- #include <linux/slab.h>
- #include <linux/of_device.h>
- #include <sound/control.h>
- #include <sound/core.h>
- #include <sound/soc.h>
- #include <asoc/msm-cdc-pinctrl.h>
- #include <dsp/spf-core.h>
- #include <dsp/msm_audio_ion.h>
- #include <sound/info.h>
- #include "msm_common.h"
- struct snd_card_pdata {
- struct kobject snd_card_kobj;
- int card_status;
- }*snd_card_pdata;
- #define to_asoc_mach_common_pdata(kobj) \
- container_of((kobj), struct msm_common_pdata, aud_dev_kobj)
- #define DEVICE_ENABLE 1
- #define DEVICE_DISABLE 0
- #define ARRAY_SZ 21
- #define BUF_SZ 32
- #define DIR_SZ 10
- static struct attribute device_state_attr = {
- .name = "state",
- .mode = 0660,
- };
- static struct attribute card_state_attr = {
- .name = "card_state",
- .mode = 0660,
- };
- #define MAX_PORT 20
- #define CODEC_CHMAP "Channel Map"
- enum backend_id {
- SLIM = 1,
- CODEC_DMA,
- };
- struct chmap_pdata {
- int id;
- struct snd_soc_dai *dai;
- };
- #define MAX_USR_INPUT 10
- static ssize_t aud_dev_sysfs_store(struct kobject *kobj,
- struct attribute *attr,
- const char *buf, size_t count)
- {
- ssize_t ret = -EINVAL;
- struct msm_common_pdata *pdata = to_asoc_mach_common_pdata(kobj);
- uint32_t pcm_id, state = 0;
- if (count > MAX_USR_INPUT) {
- pr_err("%s: invalid string written", __func__);
- goto done;
- }
- sscanf(buf, "%d %d", &pcm_id, &state);
- if ((pcm_id > pdata->num_aud_devs) || (pcm_id < 0)) {
- pr_err("%s: invalid pcm id %d \n", __func__, pcm_id);
- goto done;
- }
- if ((state > DEVICE_ENABLE) || (state < DEVICE_DISABLE)) {
- pr_err("%s: invalid state %d \n", __func__, state);
- goto done;
- }
- pr_debug("%s: pcm_id %d state %d \n", __func__, pcm_id, state);
- pdata->aud_dev_state[pcm_id] = state;
- if ( state == DEVICE_ENABLE && (pdata->dsp_sessions_closed != 0))
- pdata->dsp_sessions_closed = 0;
- ret = count;
- done:
- return ret;
- }
- static const struct sysfs_ops aud_dev_sysfs_ops = {
- .store = aud_dev_sysfs_store,
- };
- static struct kobj_type aud_dev_ktype = {
- .sysfs_ops = &aud_dev_sysfs_ops,
- };
- static int aud_dev_sysfs_init(struct msm_common_pdata *pdata)
- {
- int ret = 0;
- char dir[10] = "aud_dev";
- ret = kobject_init_and_add(&pdata->aud_dev_kobj, &aud_dev_ktype,
- kernel_kobj, dir);
- if (ret < 0) {
- pr_err("%s: Failed to add kobject %s, err = %d\n",
- __func__, dir, ret);
- goto done;
- }
- ret = sysfs_create_file(&pdata->aud_dev_kobj, &device_state_attr);
- if (ret < 0) {
- pr_err("%s: Failed to add wdsp_boot sysfs entry to %s\n",
- __func__, dir);
- goto fail_create_file;
- }
- return ret;
- fail_create_file:
- kobject_put(&pdata->aud_dev_kobj);
- done:
- return ret;
- }
- int snd_card_notify_user(int card_status)
- {
- snd_card_pdata->card_status = card_status;
- sysfs_notify(&snd_card_pdata->snd_card_kobj, NULL, "card_state");
- return 0;
- }
- static ssize_t snd_card_sysfs_show(struct kobject *kobj,
- struct attribute *attr, char *buf)
- {
- return snprintf(buf, BUF_SZ, "%d", snd_card_pdata->card_status);
- }
- static ssize_t snd_card_sysfs_store(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
- {
- sscanf(buf, "%d", &snd_card_pdata->card_status);
- sysfs_notify(&snd_card_pdata->snd_card_kobj, NULL, "card_state");
- return 0;
- }
- static const struct sysfs_ops snd_card_sysfs_ops = {
- .show = snd_card_sysfs_show,
- .store = snd_card_sysfs_store,
- };
- static struct kobj_type snd_card_ktype = {
- .sysfs_ops = &snd_card_sysfs_ops,
- };
- int snd_card_sysfs_init(void)
- {
- int ret = 0;
- char dir[DIR_SZ] = "snd_card";
- snd_card_pdata = kcalloc(1, sizeof(struct snd_card_pdata), GFP_KERNEL);
- ret = kobject_init_and_add(&snd_card_pdata->snd_card_kobj, &snd_card_ktype,
- kernel_kobj, dir);
- if (ret < 0) {
- pr_err("%s: Failed to add kobject %s, err = %d\n",
- __func__, dir, ret);
- goto done;
- }
- ret = sysfs_create_file(&snd_card_pdata->snd_card_kobj, &card_state_attr);
- if (ret < 0) {
- pr_err("%s: Failed to add snd_card sysfs entry to %s\n",
- __func__, dir);
- goto fail_create_file;
- }
- return ret;
- fail_create_file:
- kobject_put(&snd_card_pdata->snd_card_kobj);
- done:
- return ret;
- }
- static void check_userspace_service_state(struct snd_soc_pcm_runtime *rtd,
- struct msm_common_pdata *pdata)
- {
- dev_info(rtd->card->dev,"%s: pcm_id %d state %d\n", __func__,
- rtd->num, pdata->aud_dev_state[rtd->num]);
- if (pdata->aud_dev_state[rtd->num] == DEVICE_ENABLE) {
- dev_info(rtd->card->dev, "%s userspace service crashed\n",
- __func__);
- if (pdata->dsp_sessions_closed == 0) {
- /*Issue close all graph cmd to DSP*/
- spf_core_apm_close_all();
- /*unmap all dma mapped buffers*/
- msm_audio_ion_crash_handler();
- pdata->dsp_sessions_closed = 1;
- }
- /*Reset the state as sysfs node wont be triggred*/
- pdata->aud_dev_state[rtd->num] = 0;
- }
- }
- static int get_intf_index(const char *stream_name)
- {
- if (strnstr(stream_name, "PRIMARY", strlen(stream_name)))
- return PRI_MI2S_TDM_AUXPCM;
- else if (strnstr(stream_name, "SECONDARY", strlen(stream_name)))
- return SEC_MI2S_TDM_AUXPCM;
- else if (strnstr(stream_name, "TERTIARY", strlen(stream_name)))
- return TER_MI2S_TDM_AUXPCM;
- else if (strnstr(stream_name, "QUATERNARY", strlen(stream_name)))
- return QUAT_MI2S_TDM_AUXPCM;
- else if (strnstr(stream_name, "QUINARY", strlen(stream_name)))
- return QUIN_MI2S_TDM_AUXPCM;
- else if (strnstr(stream_name, "SENARY", strlen(stream_name)))
- return SEN_MI2S_TDM_AUXPCM;
- else {
- pr_debug("%s: stream name %s does not match\n", __func__, stream_name);
- return -EINVAL;
- }
- }
- int msm_common_snd_startup(struct snd_pcm_substream *substream)
- {
- int ret = 0;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_card *card = rtd->card;
- struct msm_common_pdata *pdata = msm_common_get_pdata(card);
- const char *stream_name = rtd->dai_link->stream_name;
- int index = get_intf_index(stream_name);
- dev_dbg(rtd->card->dev,
- "%s: substream = %s stream = %d\n",
- __func__, substream->name, substream->stream);
- if (!pdata) {
- dev_err(rtd->card->dev, "%s: pdata is NULL\n", __func__);
- return -EINVAL;
- }
- if (index >= 0) {
- mutex_lock(&pdata->lock[index]);
- if (pdata->mi2s_gpio_p[index]) {
- if (atomic_read(&(pdata->mi2s_gpio_ref_cnt[index])) == 0) {
- ret = msm_cdc_pinctrl_select_active_state(
- pdata->mi2s_gpio_p[index]);
- if (ret) {
- pr_err("%s:pinctrl set actve fail with %d\n",
- __func__, ret);
- goto done;
- }
- }
- atomic_inc(&(pdata->mi2s_gpio_ref_cnt[index]));
- }
- done:
- mutex_unlock(&pdata->lock[index]);
- }
- return ret;
- }
- void msm_common_snd_shutdown(struct snd_pcm_substream *substream)
- {
- int ret;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_card *card = rtd->card;
- struct msm_common_pdata *pdata = msm_common_get_pdata(card);
- const char *stream_name = rtd->dai_link->stream_name;
- int index = get_intf_index(stream_name);
- pr_debug("%s(): substream = %s stream = %d\n", __func__,
- substream->name, substream->stream);
- if (!pdata) {
- dev_err(card->dev, "%s: pdata is NULL\n", __func__);
- return;
- }
- check_userspace_service_state(rtd, pdata);
- if (index >= 0) {
- mutex_lock(&pdata->lock[index]);
- if (pdata->mi2s_gpio_p[index]) {
- atomic_dec(&pdata->mi2s_gpio_ref_cnt[index]);
- if (atomic_read(&pdata->mi2s_gpio_ref_cnt[index]) == 0) {
- ret = msm_cdc_pinctrl_select_active_state(
- pdata->mi2s_gpio_p[index]);
- if (ret)
- dev_err(card->dev,
- "%s: pinctrl set actv fail %d\n",
- __func__, ret);
- }
- }
- mutex_unlock(&pdata->lock[index]);
- }
- }
- int msm_common_snd_init(struct platform_device *pdev, struct snd_soc_card *card)
- {
- struct msm_common_pdata *common_pdata = NULL;
- int count;
- common_pdata = kcalloc(1, sizeof(struct msm_common_pdata), GFP_KERNEL);
- if (!common_pdata)
- return -ENOMEM;
- for (count = 0; count < MI2S_TDM_AUXPCM_MAX; count++) {
- mutex_init(&common_pdata->lock[count]);
- atomic_set(&common_pdata->mi2s_gpio_ref_cnt[count], 0);
- }
- common_pdata->mi2s_gpio_p[PRI_MI2S_TDM_AUXPCM] = of_parse_phandle(pdev->dev.of_node,
- "qcom,pri-mi2s-gpios", 0);
- common_pdata->mi2s_gpio_p[SEC_MI2S_TDM_AUXPCM] = of_parse_phandle(pdev->dev.of_node,
- "qcom,sec-mi2s-gpios", 0);
- common_pdata->mi2s_gpio_p[TER_MI2S_TDM_AUXPCM] = of_parse_phandle(pdev->dev.of_node,
- "qcom,tert-mi2s-gpios", 0);
- common_pdata->mi2s_gpio_p[QUAT_MI2S_TDM_AUXPCM] = of_parse_phandle(pdev->dev.of_node,
- "qcom,quat-mi2s-gpios", 0);
- common_pdata->mi2s_gpio_p[QUIN_MI2S_TDM_AUXPCM] = of_parse_phandle(pdev->dev.of_node,
- "qcom,quin-mi2s-gpios", 0);
- common_pdata->mi2s_gpio_p[SEN_MI2S_TDM_AUXPCM] = of_parse_phandle(pdev->dev.of_node,
- "qcom,sen-mi2s-gpios", 0);
- common_pdata->aud_dev_state = devm_kcalloc(&pdev->dev, card->num_links,
- sizeof(uint8_t), GFP_KERNEL);
- dev_info(&pdev->dev, "num_links %d \n", card->num_links);
- common_pdata->num_aud_devs = card->num_links;
- aud_dev_sysfs_init(common_pdata);
- msm_common_set_pdata(card, common_pdata);
- return 0;
- };
- void msm_common_snd_deinit(struct msm_common_pdata *common_pdata)
- {
- int count;
- if (!common_pdata)
- return;
- for (count = 0; count < MI2S_TDM_AUXPCM_MAX; count++) {
- mutex_destroy(&common_pdata->lock[count]);
- }
- }
- int msm_channel_map_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
- {
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- uinfo->count = sizeof(uint32_t) * MAX_PORT;
- return 0;
- }
- int msm_channel_map_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- struct chmap_pdata *kctl_pdata =
- (struct chmap_pdata *)kcontrol->private_data;
- struct snd_soc_dai *codec_dai = NULL;
- int backend_id = 0;
- uint32_t rx_ch[MAX_PORT], tx_ch[MAX_PORT];
- uint32_t rx_ch_cnt = 0, tx_ch_cnt = 0;
- uint32_t *chmap_data = NULL;
- int ret = 0, len = 0;
- if (kctl_pdata == NULL) {
- pr_debug("%s: chmap_pdata is not initialized\n", __func__);
- return -EINVAL;
- }
- codec_dai = kctl_pdata->dai;
- backend_id = kctl_pdata->id;
- ret = snd_soc_dai_get_channel_map(codec_dai,
- &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
- if (ret || (tx_ch_cnt == 0 && rx_ch_cnt == 0)) {
- pr_err("%s: get channel map failed for %d\n",
- __func__, backend_id);
- return ret;
- }
- switch (backend_id) {
- case SLIM: {
- uint32_t *chmap;
- uint32_t ch_cnt, i;
- if (rx_ch_cnt) {
- chmap = rx_ch;
- ch_cnt = rx_ch_cnt;
- } else {
- chmap = tx_ch;
- ch_cnt = tx_ch_cnt;
- }
- len = sizeof(uint32_t) * (ch_cnt + 1);
- chmap_data = kzalloc(len, GFP_KERNEL);
- if (!chmap_data)
- return -ENOMEM;
- chmap_data[0] = ch_cnt;
- for (i = 0; i < ch_cnt; i++)
- chmap_data[i+1] = chmap[i];
- memcpy(ucontrol->value.bytes.data, chmap_data, len);
- break;
- }
- case CODEC_DMA: {
- chmap_data = kzalloc(sizeof(uint32_t) * 2, GFP_KERNEL);
- if (!chmap_data)
- return -ENOMEM;
- if (rx_ch_cnt) {
- chmap_data[0] = rx_ch_cnt;
- chmap_data[1] = rx_ch[0];
- } else {
- chmap_data[0] = tx_ch_cnt;
- chmap_data[1] = tx_ch[0];
- }
- memcpy(ucontrol->value.bytes.data, chmap_data,
- sizeof(uint32_t) * 2);
- break;
- }
- default:
- pr_err("%s, Invalid backend %d\n", __func__, backend_id);
- ret = -EINVAL;
- break;
- }
- kfree(chmap_data);
- return ret;
- }
- void msm_common_get_backend_name(const char *stream_name, char **backend_name)
- {
- char arg[ARRAY_SZ] = {0};
- char value[61] = {0};
- sscanf(stream_name, "%20[^-]-%60s", arg, value);
- *backend_name = kzalloc(ARRAY_SZ, GFP_KERNEL);
- if (!(*backend_name))
- return;
- strlcpy(*backend_name, arg, ARRAY_SZ);
- }
- int msm_common_dai_link_init(struct snd_soc_pcm_runtime *rtd)
- {
- struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- struct snd_soc_component *component = NULL;
- struct snd_soc_dai_link *dai_link = rtd->dai_link;
- struct device *dev = rtd->card->dev;
- int ret = 0;
- const char *mixer_ctl_name = CODEC_CHMAP;
- char *mixer_str = NULL;
- char *backend_name = NULL;
- uint32_t ctl_len = 0;
- struct chmap_pdata *pdata;
- struct snd_kcontrol *kctl;
- struct snd_kcontrol_new msm_common_channel_map[1] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "?",
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = msm_channel_map_info,
- .get = msm_channel_map_get,
- .private_value = 0,
- }
- };
- if (!codec_dai) {
- pr_err("%s: failed to get codec dai", __func__);
- return -EINVAL;
- }
- component = codec_dai->component;
- msm_common_get_backend_name(dai_link->stream_name, &backend_name);
- if (!backend_name) {
- pr_err("%s: failed to get backend name", __func__);
- return -EINVAL;
- }
- pdata = devm_kzalloc(dev, sizeof(struct chmap_pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
- if ((!strncmp(backend_name, "SLIM", strlen("SLIM"))) ||
- (!strncmp(backend_name, "CODEC_DMA", strlen("CODEC_DMA")))) {
- ctl_len = strlen(dai_link->stream_name) + 1 +
- strlen(mixer_ctl_name) + 1;
- mixer_str = kzalloc(ctl_len, GFP_KERNEL);
- if (!mixer_str)
- return -ENOMEM;
- snprintf(mixer_str, ctl_len, "%s %s", dai_link->stream_name,
- mixer_ctl_name);
- msm_common_channel_map[0].name = mixer_str;
- msm_common_channel_map[0].private_value = 0;
- pr_debug("Registering new mixer ctl %s\n", mixer_str);
- ret = snd_soc_add_component_controls(component,
- msm_common_channel_map,
- ARRAY_SIZE(msm_common_channel_map));
- kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
- if (!kctl) {
- pr_err("failed to get kctl %s\n", mixer_str);
- ret = -EINVAL;
- goto free_mixer_str;
- }
- if (!strncmp(backend_name, "SLIM", strlen("SLIM")))
- pdata->id = SLIM;
- else
- pdata->id = CODEC_DMA;
- pdata->dai = codec_dai;
- kctl->private_data = pdata;
- free_mixer_str:
- kfree(backend_name);
- kfree(mixer_str);
- }
- return ret;
- }
|