diff options
author | Shengjiu Wang <shengjiu.wang@freescale.com> | 2017-02-15 11:28:36 +0800 |
---|---|---|
committer | Dong Aisheng <aisheng.dong@nxp.com> | 2019-11-25 15:52:48 +0800 |
commit | a019e7b4158a88f15b24e90e6d28cc615e558a9e (patch) | |
tree | c11210f7c27a1b108e1c45b1ae5565de66622519 /sound/soc/fsl/fsl_rpmsg_i2s.c | |
parent | 219d54332a09e8d8741c1e1982f5eae56099de85 (diff) |
MLK-13904-1: ASoC: fsl: add audio cpu dai driver base on rpmsg (part 1)
Add the cpu dai driver, as the rpmsg_send api can't be used in
atomic context, so using the workqueue instead of calling
rpmsg_send() directly.
The detail communication stack is defined in header file.
Signed-off-by: Shengjiu Wang <shengjiu.wang@freescale.com>
Acked-by: Robin Gong <yibin.gong@nxp.com>
[ Aisheng: split out imx-pcm.h changes ]
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
Diffstat (limited to 'sound/soc/fsl/fsl_rpmsg_i2s.c')
-rw-r--r-- | sound/soc/fsl/fsl_rpmsg_i2s.c | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/sound/soc/fsl/fsl_rpmsg_i2s.c b/sound/soc/fsl/fsl_rpmsg_i2s.c new file mode 100644 index 000000000000..2768c65155db --- /dev/null +++ b/sound/soc/fsl/fsl_rpmsg_i2s.c @@ -0,0 +1,251 @@ +/* + * Freescale ALSA SoC rpmsg i2s driver. + * + * Copyright 2017 NXP + * + * 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, either version 2 of the License, or(at your + * option) any later version. + * + */ +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/pm_runtime.h> +#include <linux/rpmsg.h> +#include <linux/slab.h> + +#include <sound/core.h> +#include <sound/dmaengine_pcm.h> +#include <sound/pcm_params.h> + +#include "fsl_rpmsg_i2s.h" +#include "imx-pcm.h" + +#define FSL_RPMSG_I2S_RATES SNDRV_PCM_RATE_8000_96000 +#define FSL_RPMSG_I2S_FORMATS SNDRV_PCM_FMTBIT_S16_LE + +static int i2s_send_message(struct i2s_rpmsg_s *msg, + struct i2s_info *info) +{ + int err; + + mutex_lock(&info->tx_lock); + if (!info->rpdev) { + dev_dbg(info->dev, "rpmsg channel not ready, m4 image ready?\n"); + return -EINVAL; + } + + dev_dbg(&info->rpdev->dev, "send cmd %d\n", msg->header.cmd); + + reinit_completion(&info->cmd_complete); + err = rpmsg_send(info->rpdev->ept, (void *)msg, + sizeof(struct i2s_rpmsg_s)); + if (err) { + dev_err(&info->rpdev->dev, "rpmsg_send failed: %d\n", err); + return err; + } + + /* wait response from rpmsg */ + err = wait_for_completion_timeout(&info->cmd_complete, + msecs_to_jiffies(RPMSG_TIMEOUT)); + if (!err) { + dev_err(&info->rpdev->dev, "rpmsg_send cmd %d timeout!\n", + msg->header.cmd); + return -ETIMEDOUT; + } + + dev_dbg(&info->rpdev->dev, "cmd:%d, resp %d\n", msg->header.cmd, + info->recv_msg.param.resp); + mutex_unlock(&info->tx_lock); + + return 0; +} + +static struct snd_soc_dai_driver fsl_rpmsg_i2s_dai = { + .playback = { + .stream_name = "CPU-Playback", + .channels_min = 1, + .channels_max = 2, + .rates = FSL_RPMSG_I2S_RATES, + .formats = FSL_RPMSG_I2S_FORMATS, + }, + .capture = { + .stream_name = "CPU-Capture", + .channels_min = 1, + .channels_max = 2, + .rates = FSL_RPMSG_I2S_RATES, + .formats = FSL_RPMSG_I2S_FORMATS, + }, + .symmetric_rates = 1, + .symmetric_channels = 1, + .symmetric_samplebits = 1, +}; + +static const struct snd_soc_component_driver fsl_component = { + .name = "fsl-rpmsg-i2s", +}; + +static const struct of_device_id fsl_rpmsg_i2s_ids[] = { + { .compatible = "fsl,imx7ulp-rpmsg-i2s"}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_rpmsg_i2s_ids); + +static void rpmsg_i2s_work(struct work_struct *work) +{ + struct work_of_rpmsg *work_of_rpmsg; + struct i2s_info *i2s_info; + + work_of_rpmsg = container_of(work, struct work_of_rpmsg, work); + i2s_info = work_of_rpmsg->i2s_info; + + i2s_send_message(&work_of_rpmsg->msg, i2s_info); +} + +static int fsl_rpmsg_i2s_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_rpmsg_i2s *rpmsg_i2s; + struct i2s_info *i2s_info; + int audioindex = 0; + int ret; + int i; + + rpmsg_i2s = devm_kzalloc(&pdev->dev, sizeof(struct fsl_rpmsg_i2s), + GFP_KERNEL); + if (!rpmsg_i2s) + return -ENOMEM; + + rpmsg_i2s->pdev = pdev; + i2s_info = &rpmsg_i2s->i2s_info; + + ret = of_property_read_u32(np, "fsl,audioindex", &audioindex); + if (ret) + audioindex = 0; + + /* Setup work queue */ + i2s_info->rpmsg_wq = create_singlethread_workqueue("rpmsg_i2s"); + if (i2s_info->rpmsg_wq == NULL) { + dev_err(&pdev->dev, "workqueue create failed\n"); + return -ENOMEM; + } + + i2s_info->send_message = i2s_send_message; + + for (i = 0; i < WORK_MAX_NUM; i++) { + INIT_WORK(&i2s_info->work_list[i].work, rpmsg_i2s_work); + i2s_info->work_list[i].i2s_info = i2s_info; + } + + for (i = 0; i < 2; i++) { + i2s_info->send_msg[i].header.cate = IMX_RPMSG_AUDIO; + i2s_info->send_msg[i].header.major = IMX_RMPSG_MAJOR; + i2s_info->send_msg[i].header.minor = IMX_RMPSG_MINOR; + i2s_info->send_msg[i].header.type = I2S_TYPE_A; + i2s_info->send_msg[i].param.audioindex = audioindex; + } + + mutex_init(&i2s_info->tx_lock); + + platform_set_drvdata(pdev, rpmsg_i2s); + pm_runtime_enable(&pdev->dev); + + ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component, + &fsl_rpmsg_i2s_dai, 1); + if (ret) + return ret; + + return imx_rpmsg_platform_register(&pdev->dev); +} + +static int fsl_rpmsg_i2s_remove(struct platform_device *pdev) +{ + struct fsl_rpmsg_i2s *rpmsg_i2s = platform_get_drvdata(pdev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + + if (i2s_info->rpmsg_wq) + destroy_workqueue(i2s_info->rpmsg_wq); + + return 0; +} + +#ifdef CONFIG_PM +static int fsl_rpmsg_i2s_runtime_resume(struct device *dev) +{ + return 0; +} + +static int fsl_rpmsg_i2s_runtime_suspend(struct device *dev) +{ + return 0; +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int fsl_rpmsg_i2s_suspend(struct device *dev) +{ + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg_tx; + struct i2s_rpmsg_s *rpmsg_rx; + + rpmsg_tx = &i2s_info->send_msg[SNDRV_PCM_STREAM_PLAYBACK]; + rpmsg_rx = &i2s_info->send_msg[SNDRV_PCM_STREAM_CAPTURE]; + + rpmsg_tx->header.cmd = I2S_TX_SUSPEND; + i2s_send_message(rpmsg_tx, i2s_info); + + rpmsg_tx->header.cmd = I2S_RX_SUSPEND; + i2s_send_message(rpmsg_rx, i2s_info); + + return 0; +} + +static int fsl_rpmsg_i2s_resume(struct device *dev) +{ + struct fsl_rpmsg_i2s *rpmsg_i2s = dev_get_drvdata(dev); + struct i2s_info *i2s_info = &rpmsg_i2s->i2s_info; + struct i2s_rpmsg_s *rpmsg_tx; + struct i2s_rpmsg_s *rpmsg_rx; + + rpmsg_tx = &i2s_info->send_msg[SNDRV_PCM_STREAM_PLAYBACK]; + rpmsg_rx = &i2s_info->send_msg[SNDRV_PCM_STREAM_CAPTURE]; + + rpmsg_tx->header.cmd = I2S_TX_RESUME; + i2s_send_message(rpmsg_tx, i2s_info); + + rpmsg_tx->header.cmd = I2S_RX_RESUME; + i2s_send_message(rpmsg_rx, i2s_info); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_rpmsg_i2s_pm_ops = { + SET_RUNTIME_PM_OPS(fsl_rpmsg_i2s_runtime_suspend, + fsl_rpmsg_i2s_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(fsl_rpmsg_i2s_suspend, fsl_rpmsg_i2s_resume) +}; + +static struct platform_driver fsl_rpmsg_i2s_driver = { + .probe = fsl_rpmsg_i2s_probe, + .remove = fsl_rpmsg_i2s_remove, + .driver = { + .name = "fsl-rpmsg-i2s", + .pm = &fsl_rpmsg_i2s_pm_ops, + .of_match_table = fsl_rpmsg_i2s_ids, + }, +}; + +module_platform_driver(fsl_rpmsg_i2s_driver); + +MODULE_DESCRIPTION("Freescale Soc rpmsg_i2s Interface"); +MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@freescale.com>"); +MODULE_ALIAS("platform:fsl-rpmsg_i2s"); +MODULE_LICENSE("GPL"); |