From 600e3c659bc10e1e6149be44ae6bb88b4f313d26 Mon Sep 17 00:00:00 2001 From: Bhalchandra Gajare Date: Fri, 28 Oct 2016 15:25:06 -0700 Subject: [PATCH] ASoC: wcd934x-dsp-cntl: Add misc device to control codec dsp The codec DSP needs to be enabled only when there is use case that utilizes the DSP. This way the codec DSP can be shutdown when it is not used. Change adds misc device node that the user space can use to trigger boot and shutdown of DSP. CRs-Fixed: 1085213 Change-Id: Ie8bb9ed903e46b0914b4ba2630efa864c751c29b Signed-off-by: Bhalchandra Gajare --- sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c | 125 +++++++++++++++++++- sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h | 4 + 2 files changed, 127 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c index 7e4cd6ce55a7..9898c1fc7471 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -665,6 +665,12 @@ static int wcd_cntl_do_boot(struct wcd_dsp_cntl *cntl) __func__); ret = -ETIMEDOUT; goto err_boot; + } else { + /* + * Re-initialize the return code to 0, as in success case, + * it will hold the remaining time for completion timeout + */ + ret = 0; } dev_dbg(codec->dev, "%s: WDSP booted in normal mode\n", __func__); @@ -877,6 +883,108 @@ static void wcd_cntl_debugfs_remove(struct wcd_dsp_cntl *cntl) debugfs_remove(cntl->entry); } +static int wcd_miscdev_release(struct inode *inode, struct file *filep) +{ + struct wcd_dsp_cntl *cntl = container_of(filep->private_data, + struct wcd_dsp_cntl, miscdev); + if (!cntl->m_dev || !cntl->m_ops || + !cntl->m_ops->vote_for_dsp) { + dev_err(cntl->codec->dev, + "%s: DSP not ready to boot\n", __func__); + return -EINVAL; + } + + /* Make sure the DSP users goes to zero upon closing dev node */ + while (cntl->boot_reqs > 0) { + cntl->m_ops->vote_for_dsp(cntl->m_dev, false); + cntl->boot_reqs--; + } + + return 0; +} + +static ssize_t wcd_miscdev_write(struct file *filep, const char __user *ubuf, + size_t count, loff_t *pos) +{ + struct wcd_dsp_cntl *cntl = container_of(filep->private_data, + struct wcd_dsp_cntl, miscdev); + char val[count]; + bool vote; + int ret = 0; + + if (count == 0 || count > 2) { + pr_err("%s: Invalid count = %zd\n", __func__, count); + ret = -EINVAL; + goto done; + } + + ret = copy_from_user(val, ubuf, count); + if (IS_ERR_VALUE(ret)) { + dev_err(cntl->codec->dev, + "%s: copy_from_user failed, err = %d\n", + __func__, ret); + ret = -EFAULT; + goto done; + } + + if (val[0] == '1') { + cntl->boot_reqs++; + vote = true; + } else if (val[0] == '0') { + if (cntl->boot_reqs == 0) { + dev_err(cntl->codec->dev, + "%s: WDSP already disabled\n", __func__); + ret = -EINVAL; + goto done; + } + cntl->boot_reqs--; + vote = false; + } else { + dev_err(cntl->codec->dev, "%s: Invalid value %s\n", + __func__, val); + ret = -EINVAL; + goto done; + } + + dev_dbg(cntl->codec->dev, + "%s: booted = %s, ref_cnt = %d, vote = %s\n", + __func__, cntl->is_wdsp_booted ? "true" : "false", + cntl->boot_reqs, vote ? "true" : "false"); + + if (cntl->m_dev && cntl->m_ops && + cntl->m_ops->vote_for_dsp) + ret = cntl->m_ops->vote_for_dsp(cntl->m_dev, vote); + else + ret = -EINVAL; +done: + if (ret) + return ret; + else + return count; +} + +static const struct file_operations wcd_miscdev_fops = { + .write = wcd_miscdev_write, + .release = wcd_miscdev_release, +}; + +static int wcd_cntl_miscdev_create(struct wcd_dsp_cntl *cntl) +{ + snprintf(cntl->miscdev_name, ARRAY_SIZE(cntl->miscdev_name), + "wcd_dsp%u_control", cntl->dsp_instance); + cntl->miscdev.minor = MISC_DYNAMIC_MINOR; + cntl->miscdev.name = cntl->miscdev_name; + cntl->miscdev.fops = &wcd_miscdev_fops; + cntl->miscdev.parent = cntl->codec->dev; + + return misc_register(&cntl->miscdev); +} + +static void wcd_cntl_miscdev_destroy(struct wcd_dsp_cntl *cntl) +{ + misc_deregister(&cntl->miscdev); +} + static int wcd_control_init(struct device *dev, void *priv_data) { struct wcd_dsp_cntl *cntl = priv_data; @@ -1009,13 +1117,20 @@ static int wcd_ctrl_component_bind(struct device *dev, goto done; } + ret = wcd_cntl_miscdev_create(cntl); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "%s: misc dev register failed, err = %d\n", + __func__, ret); + goto done; + } + snprintf(wcd_cntl_dir_name, WCD_CNTL_DIR_NAME_LEN_MAX, "%s%d", "wdsp", cntl->dsp_instance); ret = wcd_cntl_sysfs_init(wcd_cntl_dir_name, cntl); if (IS_ERR_VALUE(ret)) { dev_err(dev, "%s: sysfs_init failed, err = %d\n", __func__, ret); - goto done; + goto err_sysfs_init; } wcd_cntl_debugfs_init(wcd_cntl_dir_name, cntl); @@ -1029,7 +1144,7 @@ static int wcd_ctrl_component_bind(struct device *dev, /* Do not treat this as Fatal error */ dev_err(dev, "%s: Failed to create procfs entry %s\n", __func__, proc_name); - goto done; + goto err_sysfs_init; } cntl->ssr_entry.entry = entry; @@ -1048,6 +1163,10 @@ static int wcd_ctrl_component_bind(struct device *dev, } done: return ret; + +err_sysfs_init: + wcd_cntl_miscdev_destroy(cntl); + return ret; } static void wcd_ctrl_component_unbind(struct device *dev, @@ -1077,6 +1196,8 @@ static void wcd_ctrl_component_unbind(struct device *dev, /* Remove the debugfs entries */ wcd_cntl_debugfs_remove(cntl); + /* Remove the misc device */ + wcd_cntl_miscdev_destroy(cntl); } static const struct component_ops wcd_ctrl_component_ops = { diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h index 83c59ed7b676..e934638cc487 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h @@ -105,6 +105,10 @@ struct wcd_dsp_cntl { /* SSR related */ struct wdsp_ssr_entry ssr_entry; struct mutex ssr_mutex; + + /* Misc device related */ + char miscdev_name[256]; + struct miscdevice miscdev; }; void wcd_dsp_cntl_init(struct snd_soc_codec *codec,